Bug 783829 - Change from Proxy iterate to enumerate. r=efaust,bholley
authorTom Schuster <evilpies@gmail.com>
Wed, 17 Dec 2014 00:28:38 +0100
changeset 220069 2e24211fa51cfd027ddb208b324980fabe65c70a
parent 220068 c62a61e75fb162d1b8620ccf92e9049948a4dfe8
child 220070 b1346d8602279a8f43a6239bd93596c3520e942d
push id12753
push usercbook@mozilla.com
push dateWed, 17 Dec 2014 12:08:02 +0000
treeherderb2g-inbound@07e69caa9484 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust, bholley
bugs783829
milestone37.0a1
Bug 783829 - Change from Proxy iterate to enumerate. r=efaust,bholley
dom/base/nsGlobalWindow.cpp
dom/bindings/DOMJSProxyHandler.cpp
dom/bindings/DOMJSProxyHandler.h
js/ipc/WrapperOwner.cpp
js/ipc/WrapperOwner.h
js/src/jit-test/tests/basic/testBug771242.js
js/src/jsiter.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsproxy.h
js/src/jswrapper.h
js/src/proxy/BaseProxyHandler.cpp
js/src/proxy/CrossCompartmentWrapper.cpp
js/src/proxy/DeadObjectProxy.cpp
js/src/proxy/DeadObjectProxy.h
js/src/proxy/DirectProxyHandler.cpp
js/src/proxy/Proxy.cpp
js/src/proxy/Proxy.h
js/src/proxy/ScriptedDirectProxyHandler.cpp
js/src/proxy/ScriptedDirectProxyHandler.h
js/src/proxy/ScriptedIndirectProxyHandler.cpp
js/src/proxy/ScriptedIndirectProxyHandler.h
js/src/vm/ScopeObject.cpp
js/xpconnect/src/Sandbox.cpp
js/xpconnect/wrappers/FilteringWrapper.cpp
js/xpconnect/wrappers/FilteringWrapper.h
js/xpconnect/wrappers/WaiveXrayWrapper.cpp
js/xpconnect/wrappers/WaiveXrayWrapper.h
js/xpconnect/wrappers/XrayWrapper.cpp
js/xpconnect/wrappers/XrayWrapper.h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -616,16 +616,18 @@ public:
                               JS::MutableHandle<JSPropertyDescriptor> desc)
                               const MOZ_OVERRIDE;
   virtual bool ownPropertyKeys(JSContext *cx,
                                JS::Handle<JSObject*> proxy,
                                JS::AutoIdVector &props) const MOZ_OVERRIDE;
   virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
                        JS::Handle<jsid> id,
                        bool *bp) const MOZ_OVERRIDE;
+  virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
+                         JS::MutableHandle<JSObject*> vp) const MOZ_OVERRIDE;
   virtual bool preventExtensions(JSContext *cx,
                                  JS::Handle<JSObject*> proxy,
                                  bool *succeeded) const MOZ_OVERRIDE;
   virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
                             const MOZ_OVERRIDE;
   virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
   virtual bool get(JSContext *cx, JS::Handle<JSObject*> proxy,
@@ -645,19 +647,16 @@ public:
                                      JS::MutableHandle<JSPropertyDescriptor> desc)
                                      const MOZ_OVERRIDE;
   virtual bool hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
                       JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
   virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy,
                                             JS::AutoIdVector &props) const MOZ_OVERRIDE;
   virtual bool getEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy,
                                          JS::AutoIdVector &props) const MOZ_OVERRIDE;
-  virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
-                       unsigned flags,
-                       JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
   virtual const char *className(JSContext *cx,
                                 JS::Handle<JSObject*> wrapper) const MOZ_OVERRIDE;
 
   virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
 
   virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE {
     return false;
   }
@@ -939,22 +938,22 @@ nsOuterWindowProxy::getEnumerablePropert
   JS::AutoIdVector innerProps(cx);
   if (!js::Wrapper::getEnumerablePropertyKeys(cx, proxy, innerProps)) {
     return false;
   }
   return js::AppendUnique(cx, props, innerProps);
 }
 
 bool
-nsOuterWindowProxy::iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
-                            unsigned flags, JS::MutableHandle<JSObject*> objp) const
-{
-  // BaseProxyHandler::iterate seems to do what we want here: fall
-  // back on the property names returned from keys() and enumerate().
-  return js::BaseProxyHandler::iterate(cx, proxy, flags, objp);
+nsOuterWindowProxy::enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
+                              JS::MutableHandle<JSObject*> objp) const
+{
+  // BaseProxyHandler::enumerate seems to do what we want here: fall
+  // back on the property names returned from getEnumerablePropertyKeys()
+  return js::BaseProxyHandler::enumerate(cx, proxy, objp);
 }
 
 bool
 nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
                                       JS::Handle<JSObject*> proxy,
                                       JS::Handle<jsid> id,
                                       JS::MutableHandle<JS::Value> vp,
                                       bool& found) const
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -305,16 +305,23 @@ BaseDOMProxyHandler::getEnumerableProper
   if (!JS_GetPrototype(cx, proxy, &proto))  {
     return false;
   }
   return getOwnEnumerablePropertyKeys(cx, proxy, props) &&
          (!proto || js::GetPropertyKeys(cx, proto, 0, &props));
 }
 
 bool
+BaseDOMProxyHandler::enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
+                               JS::MutableHandle<JSObject*> objp) const
+{
+  return BaseProxyHandler::enumerate(cx, proxy, objp);
+}
+
+bool
 DOMProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp) const
 {
   if (!hasOwn(cx, proxy, id, bp)) {
     return false;
   }
 
   if (*bp) {
     // We have the property ourselves; no need to worry about our prototype
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -57,16 +57,18 @@ public:
                                 JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
   virtual bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
                                JS::AutoIdVector &props) const MOZ_OVERRIDE;
 
   bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
                              JS::Handle<jsid> id,
                              JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
 
+  virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
+                         JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
 
   // We override getOwnEnumerablePropertyKeys() and implement it directly
   // instead of using the default implementation, which would call
   // ownPropertyKeys and then filter out the non-enumerable ones. This avoids
   // unnecessary work during enumeration.
   virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
                                             JS::AutoIdVector &props) const MOZ_OVERRIDE;
   bool getEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -89,16 +89,17 @@ class CPOWProxyHandler : public BaseProx
 
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const MOZ_OVERRIDE;
     virtual bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const MOZ_OVERRIDE;
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
                      HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
                      JS::HandleId id, bool strict, JS::MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
@@ -263,16 +264,24 @@ WrapperOwner::delete_(JSContext *cx, Han
         return ipcfail(cx);
 
     LOG_STACK();
 
     return ok(cx, status);
 }
 
 bool
+CPOWProxyHandler::enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const
+{
+    // Using a CPOW for the Iterator would slow down for .. in performance, instead
+    // call the base hook, that will use our implementation of getEnumerablePropertyKeys.
+    return BaseProxyHandler::enumerate(cx, proxy, objp);
+}
+
+bool
 CPOWProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
 {
     FORWARD(has, (cx, proxy, id, bp));
 }
 
 bool
 WrapperOwner::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
 {
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -52,17 +52,16 @@ class WrapperOwner : public virtual Java
     // SpiderMonkey extensions.
     bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                                JS::MutableHandle<JSPropertyDescriptor> desc);
     bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
     bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::HandleObject proxy,
                                       JS::AutoIdVector &props);
     bool getEnumerablePropertyKeys(JSContext *cx, JS::HandleObject proxy,
                                    JS::AutoIdVector &props);
-    // We use "iterate" provided by the base class here.
     bool hasInstance(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool *bp);
     bool objectClassIs(JSContext *cx, JS::HandleObject obj, js::ESClassValue classValue);
     const char* className(JSContext *cx, JS::HandleObject proxy);
     bool regexp_toShared(JSContext *cx, JS::HandleObject proxy, js::RegExpGuard *g);
 
     nsresult instanceOf(JSObject *obj, const nsID *id, bool *bp);
 
     bool toString(JSContext *cx, JS::HandleObject callee, JS::CallArgs &args);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testBug771242.js
+++ /dev/null
@@ -1,14 +0,0 @@
-a = function() {
-  b = newGlobal()
-};
-c = [0, 0]
-c.sort(a)
-function d() {
-  yield arguments[4]
-}
-b.iterate = d
-f = Proxy.create(b)
-e = Iterator(f, true)
-for (p in f) {
-  e.next()
-}
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -664,16 +664,26 @@ UpdateNativeIterator(NativeIterator *ni,
 bool
 js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleObject objp)
 {
     if (obj->is<PropertyIteratorObject>() || obj->is<LegacyGeneratorObject>()) {
         objp.set(obj);
         return true;
     }
 
+    // We should only call the enumerate trap for "for-in".
+    // Or when we call GetIterator from the Proxy [[Enumerate]] hook.
+    // In the future also for Reflect.enumerate.
+    // JSITER_ENUMERATE is just an optimization and the same
+    // as flags == 0 otherwise.
+    if (flags == 0 || flags == JSITER_ENUMERATE) {
+        if (obj->is<ProxyObject>())
+            return Proxy::enumerate(cx, obj, objp);
+    }
+
     Vector<Shape *, 8> shapes(cx);
     uint32_t key = 0;
     if (flags == JSITER_ENUMERATE) {
         /*
          * Check to see if this is the same as the most recent object which
          * was iterated over.  We don't explicitly check for shapeless
          * objects here, as they are not inserted into the cache and
          * will result in a miss.
@@ -742,19 +752,16 @@ js::GetIterator(JSContext *cx, HandleObj
                 if (shapes.length() == 2)
                     cx->runtime()->nativeIterCache.last = iterobj;
                 return true;
             }
         }
     }
 
   miss:
-    if (obj->is<ProxyObject>())
-        return Proxy::iterate(cx, obj, flags, objp);
-
     if (!GetCustomIterator(cx, obj, flags, objp))
         return false;
     if (objp)
         return true;
 
     AutoIdVector keys(cx);
     if (flags & JSITER_FOREACH) {
         MOZ_ASSERT(shapes.empty());
@@ -1257,50 +1264,83 @@ bool
 js::SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end)
 {
     return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
 }
 
 bool
 js::IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
 {
-    /* Fast path for native iterators */
+    // Fast path for native iterators.
     if (iterobj->is<PropertyIteratorObject>()) {
         NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
         bool done;
         if (!NativeIteratorNext(cx, ni, rval, &done))
             return false;
 
         if (done)
             rval.setMagic(JS_NO_ITER_VALUE);
         return true;
     }
 
-    /* We're reentering below and can call anything. */
+    // We're reentering below and can call anything.
     JS_CHECK_RECURSION(cx, return false);
 
-    /* Call the iterator object's .next method. */
+    // Call the iterator object's .next method.
     if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, rval))
         return false;
+    // We try to support the old and new iterator protocol at the same time!
     if (!Invoke(cx, ObjectValue(*iterobj), rval, 0, nullptr, rval)) {
-        /* Check for StopIteration. */
+        // We still check for StopIterator
         if (!cx->isExceptionPending())
             return false;
         RootedValue exception(cx);
         if (!cx->getPendingException(&exception))
             return false;
         if (!JS_IsStopIteration(exception))
             return false;
 
         cx->clearPendingException();
         rval.setMagic(JS_NO_ITER_VALUE);
         return true;
     }
 
-    return true;
+    if (!rval.isObject()) {
+        // Old style generators might return primitive values
+        return true;
+    }
+
+    // If the object has both the done and value property, we assume
+    // it's using the new style protocol. Otherwise just return the object.
+    RootedObject result(cx, &rval.toObject());
+    bool found = false;
+    if (!JSObject::hasProperty(cx, result, cx->names().done, &found))
+        return false;
+    if (!found)
+        return true;
+    if (!JSObject::hasProperty(cx, result, cx->names().value, &found))
+        return false;
+    if (!found)
+        return true;
+
+    // At this point we hopefully have a new style iterator result
+
+    // 7.4.4 IteratorComplete
+    // Get iterResult.done
+    if (!JSObject::getProperty(cx, result, result, cx->names().done, rval))
+        return false;
+
+    bool done = ToBoolean(rval);
+    if (done) {
+         rval.setMagic(JS_NO_ITER_VALUE);
+         return true;
+     }
+
+    // 7.4.5 IteratorValue
+    return JSObject::getProperty(cx, result, result, cx->names().value, rval);
 }
 
 static bool
 stopiter_hasInstance(JSContext *cx, HandleObject obj, MutableHandleValue v, bool *bp)
 {
     *bp = JS_IsStopIteration(v);
     return true;
 }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -561,18 +561,21 @@ class JSObject : public js::gc::Cell
      */
     bool isCallable() const;
     bool isConstructor() const;
     JSNative callHook() const;
     JSNative constructHook() const;
 
     MOZ_ALWAYS_INLINE void finalize(js::FreeOp *fop);
 
-    static inline bool hasProperty(JSContext *cx, js::HandleObject obj,
-                                   js::HandleId id, bool *foundp);
+    static inline bool hasProperty(JSContext *cx, js::HandleObject obj, js::HandleId id,
+                                   bool *foundp);
+
+    static inline bool hasProperty(JSContext *cx, js::HandleObject obj, js::PropertyName *name,
+                                   bool *foundp);
 
   public:
     static bool reportReadOnly(js::ThreadSafeContext *cx, jsid id, unsigned report = JSREPORT_ERROR);
     bool reportNotConfigurable(js::ThreadSafeContext *cx, jsid id, unsigned report = JSREPORT_ERROR);
     bool reportNotExtensible(js::ThreadSafeContext *cx, unsigned report = JSREPORT_ERROR);
 
     /*
      * Get the property with the given id, then call it as a function with the
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -357,30 +357,36 @@ JSObject::setInitialSlotsMaybeNonNative(
 
 inline void
 JSObject::setInitialElementsMaybeNonNative(js::HeapSlot *elements)
 {
     static_cast<js::NativeObject *>(this)->elements_ = elements;
 }
 
 /* static */ inline bool
-JSObject::hasProperty(JSContext *cx, js::HandleObject obj,
-                      js::HandleId id, bool *foundp)
+JSObject::hasProperty(JSContext *cx, js::HandleObject obj, js::HandleId id, bool *foundp)
 {
     JS::RootedObject pobj(cx);
     js::RootedShape prop(cx);
     if (!lookupGeneric(cx, obj, id, &pobj, &prop)) {
         *foundp = false;  /* initialize to shut GCC up */
         return false;
     }
     *foundp = !!prop;
     return true;
 }
 
 /* static */ inline bool
+JSObject::hasProperty(JSContext *cx, js::HandleObject obj, js::PropertyName *name, bool *foundp)
+{
+    JS::RootedId id(cx, js::NameToId(name));
+    return hasProperty(cx, obj, id, foundp);
+}
+
+/* static */ inline bool
 JSObject::getElement(JSContext *cx, js::HandleObject obj, js::HandleObject receiver,
                      uint32_t index, js::MutableHandleValue vp)
 {
     js::ElementIdOp op = obj->getOps()->getElement;
     if (op)
         return op(cx, obj, receiver, index, vp);
 
     JS::RootedId id(cx);
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -250,16 +250,22 @@ class JS_FRIEND_API(BaseProxyHandler)
     /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const = 0;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const = 0;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const = 0;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const = 0;
+    /*
+     * Because [[Enumerate]] is one of the standard traps it should be overridden.
+     * However for convenience BaseProxyHandler includes a pure virtual implementation,
+     * that turns the properties returned by getEnumerablePropertyKeys into an Iterator object.
+     */
+    virtual bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const = 0;
 
     /*
      * These methods are standard, but the engine does not normally call them.
      * They're opt-in. See "Proxy prototype chains" above.
      *
      * getPrototypeOf() crashes if called. setPrototypeOf() throws a TypeError.
      */
     virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const;
@@ -302,18 +308,16 @@ class JS_FRIEND_API(BaseProxyHandler)
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const = 0;
     virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const;
     virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                               AutoIdVector &props) const;
     virtual bool getEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                            AutoIdVector &props) const = 0;
-    virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
-                         MutableHandleObject objp) const;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) const;
     virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) const;
     virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const;
     virtual const char *className(JSContext *cx, HandleObject proxy) const;
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const;
     virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const;
     virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) const;
@@ -363,16 +367,18 @@ class JS_PUBLIC_API(DirectProxyHandler) 
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id,
                          bool *bp) const MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, HandleObject proxy,
+                           MutableHandleObject objp) const MOZ_OVERRIDE;
     virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
                                 MutableHandleObject protop) const MOZ_OVERRIDE;
     virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
                                 bool *bp) const MOZ_OVERRIDE;
     virtual bool setImmutablePrototype(JSContext *cx, HandleObject proxy,
                                        bool *succeeded) const MOZ_OVERRIDE;
     virtual bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const MOZ_OVERRIDE;
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
@@ -389,18 +395,16 @@ class JS_PUBLIC_API(DirectProxyHandler) 
     virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id,
                         bool *bp) const MOZ_OVERRIDE;
     virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                               AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool getEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                            AutoIdVector &props) const MOZ_OVERRIDE;
-    virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
-                         MutableHandleObject objp) const MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) const MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
                              bool *bp) const MOZ_OVERRIDE;
     virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
                                JSContext *cx) const MOZ_OVERRIDE;
     virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy,
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -116,20 +116,22 @@ class JS_FRIEND_API(CrossCompartmentWrap
     /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject wrapper,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, HandleObject wrapper, MutableHandleObject objp) const MOZ_OVERRIDE;
     virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
                                 MutableHandleObject protop) const MOZ_OVERRIDE;
     virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
                                 bool *bp) const MOZ_OVERRIDE;
+
     virtual bool setImmutablePrototype(JSContext *cx, HandleObject proxy,
                                        bool *succeeded) const MOZ_OVERRIDE;
     virtual bool preventExtensions(JSContext *cx, HandleObject wrapper, bool *succeeded) const MOZ_OVERRIDE;
     virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const MOZ_OVERRIDE;
     virtual bool has(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool get(JSContext *cx, HandleObject wrapper, HandleObject receiver,
                      HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver,
@@ -140,18 +142,16 @@ class JS_FRIEND_API(CrossCompartmentWrap
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject wrapper,
                                               AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool getEnumerablePropertyKeys(JSContext *cx, HandleObject wrapper,
                                            AutoIdVector &props) const MOZ_OVERRIDE;
-    virtual bool iterate(JSContext *cx, HandleObject wrapper, unsigned flags,
-                         MutableHandleObject objp) const MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) const MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v,
                              bool *bp) const MOZ_OVERRIDE;
     virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject wrapper,
                                    unsigned indent) const MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -225,29 +225,24 @@ BaseProxyHandler::getOwnEnumerableProper
 
     MOZ_ASSERT(i <= props.length());
     props.resize(i);
 
     return true;
 }
 
 bool
-BaseProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
-                          MutableHandleObject objp) const
+BaseProxyHandler::enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
 
     AutoIdVector props(cx);
-    if ((flags & JSITER_OWNONLY)
-        ? !getOwnEnumerablePropertyKeys(cx, proxy, props)
-        : !getEnumerablePropertyKeys(cx, proxy, props)) {
+    if (!getEnumerablePropertyKeys(cx, proxy, props))
         return false;
-    }
-
-    return EnumeratedIdVectorToIterator(cx, proxy, flags, props, objp);
+    return EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
 }
 
 bool
 BaseProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const
 {
     MOZ_CRASH("callable proxies should implement call trap");
 }
 
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -257,22 +257,22 @@ Reify(JSContext *cx, JSCompartment *orig
     close.clear();
     if (!CloseIterator(cx, iterObj))
         return false;
 
     return EnumeratedIdVectorToIterator(cx, obj, ni->flags, keys, objp);
 }
 
 bool
-CrossCompartmentWrapper::iterate(JSContext *cx, HandleObject wrapper, unsigned flags,
-                                 MutableHandleObject objp) const
+CrossCompartmentWrapper::enumerate(JSContext *cx, HandleObject wrapper,
+                                   MutableHandleObject objp) const
 {
     {
         AutoCompartment call(cx, wrappedObject(wrapper));
-        if (!Wrapper::iterate(cx, wrapper, flags, objp))
+        if (!Wrapper::enumerate(cx, wrapper, objp))
             return false;
     }
 
     if (CanReify(objp))
         return Reify(cx, cx->compartment(), objp);
     return cx->compartment()->wrap(cx, objp);
 }
 
--- a/js/src/proxy/DeadObjectProxy.cpp
+++ b/js/src/proxy/DeadObjectProxy.cpp
@@ -49,16 +49,23 @@ DeadObjectProxy::ownPropertyKeys(JSConte
 bool
 DeadObjectProxy::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
     return false;
 }
 
 bool
+DeadObjectProxy::enumerate(JSContext *cx, HandleObject wrapper, MutableHandleObject objp) const
+{
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
+    return false;
+}
+
+bool
 DeadObjectProxy::getEnumerablePropertyKeys(JSContext *cx, HandleObject wrapper,
                                            AutoIdVector &props) const
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
     return false;
 }
 
 bool
--- a/js/src/proxy/DeadObjectProxy.h
+++ b/js/src/proxy/DeadObjectProxy.h
@@ -21,16 +21,17 @@ class DeadObjectProxy : public BaseProxy
     /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject wrapper,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, HandleObject wrapper, MutableHandleObject objp) const MOZ_OVERRIDE;
     virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
                                 MutableHandleObject protop) const MOZ_OVERRIDE;
     virtual bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const MOZ_OVERRIDE;
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
 
     /* SpiderMonkey extensions. */
--- a/js/src/proxy/DirectProxyHandler.cpp
+++ b/js/src/proxy/DirectProxyHandler.cpp
@@ -58,16 +58,25 @@ bool
 DirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
 {
     assertEnteredPolicy(cx, proxy, id, SET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return JSObject::deleteGeneric(cx, target, id, bp);
 }
 
 bool
+DirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const
+{
+    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
+    MOZ_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
+    RootedObject target(cx, proxy->as<ProxyObject>().target());
+    return GetIterator(cx, target, 0, objp);
+}
+
+bool
 DirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
     RootedValue target(cx, proxy->as<ProxyObject>().private_());
     return Invoke(cx, args.thisv(), target, args.length(), args.array(), args.rval());
 }
 
 bool
@@ -239,23 +248,13 @@ DirectProxyHandler::getEnumerablePropert
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
     MOZ_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return GetPropertyKeys(cx, target, 0, &props);
 }
 
 bool
-DirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
-                            MutableHandleObject objp) const
-{
-    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
-    MOZ_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
-    RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return GetIterator(cx, target, flags, objp);
-}
-
-bool
 DirectProxyHandler::isCallable(JSObject *obj) const
 {
     JSObject * target = obj->as<ProxyObject>().target();
     return target->isCallable();
 }
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -361,40 +361,37 @@ Proxy::getEnumerablePropertyKeys(JSConte
         return false;
     AutoIdVector protoProps(cx);
     INVOKE_ON_PROTOTYPE(cx, handler, proxy,
                         GetPropertyKeys(cx, proto, 0, &protoProps) &&
                         AppendUnique(cx, props, protoProps));
 }
 
 bool
-Proxy::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleObject objp)
+Proxy::enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp)
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     objp.set(nullptr); // default result if we refuse to perform this action
     if (!handler->hasPrototype()) {
         AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
                                BaseProxyHandler::ENUMERATE, true);
         // If the policy denies access but wants us to return true, we need
         // to hand a valid (empty) iterator object to the caller.
         if (!policy.allowed()) {
             return policy.returnValue() &&
-                   NewEmptyPropertyIterator(cx, flags, objp);
+                   NewEmptyPropertyIterator(cx, 0, objp);
         }
-        return handler->iterate(cx, proxy, flags, objp);
+        return handler->enumerate(cx, proxy, objp);
     }
     AutoIdVector props(cx);
     // The other Proxy::foo methods do the prototype-aware work for us here.
-    if ((flags & JSITER_OWNONLY)
-        ? !Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props)
-        : !Proxy::getEnumerablePropertyKeys(cx, proxy, props)) {
+    if (!Proxy::getEnumerablePropertyKeys(cx, proxy, props))
         return false;
-    }
-    return EnumeratedIdVectorToIterator(cx, proxy, flags, props, objp);
+    return EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
 }
 
 bool
 Proxy::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
 
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -29,16 +29,17 @@ class Proxy
     static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                          MutableHandle<JSPropertyDescriptor> desc);
     static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                          MutableHandleValue vp);
     static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                MutableHandle<JSPropertyDescriptor> desc);
     static bool ownPropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props);
     static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
+    static bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp);
     static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible);
     static bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded);
     static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
     static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp);
     static bool setImmutablePrototype(JSContext *cx, HandleObject proxy, bool *succeeded);
     static bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                     MutableHandleValue vp);
@@ -51,17 +52,16 @@ class Proxy
     static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                       MutableHandle<JSPropertyDescriptor> desc);
     static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                       MutableHandleValue vp);
     static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                              AutoIdVector &props);
     static bool getEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props);
-    static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleObject objp);
     static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
     static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
     static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
     static const char *className(JSContext *cx, HandleObject proxy);
     static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
     static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
     static bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp);
     static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -744,64 +744,92 @@ ScriptedDirectProxyHandler::delete_(JSCo
         return true;
     }
 
     // step 11
     *bp = false;
     return true;
 }
 
-// ES6 (22 May, 2014) 9.5.11 Proxy.[[Enumerate]]
+// ES6 (14 October, 2014) 9.5.11 Proxy.[[Enumerate]]
 bool
-ScriptedDirectProxyHandler::getEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const
+ScriptedDirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy,
+                                      MutableHandleObject objp) const
 {
     // step 1
     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
 
     // step 2
     if (!handler) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
         return false;
     }
 
-    // step 3
+    // step 3: unnecessary assert
+    // step 4
     RootedObject target(cx, proxy->as<ProxyObject>().target());
 
-    // step 4-5
+    // step 5-6
     RootedValue trap(cx);
     if (!JSObject::getProperty(cx, handler, handler, cx->names().enumerate, &trap))
         return false;
 
-    // step 6
+    // step 7
     if (trap.isUndefined())
-        return DirectProxyHandler::getEnumerablePropertyKeys(cx, proxy, props);
+        return DirectProxyHandler::enumerate(cx, proxy, objp);
 
-    // step 7-8
+    // step 8-9
     Value argv[] = {
         ObjectOrNullValue(target)
     };
     RootedValue trapResult(cx);
     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
         return false;
 
-    // step 9
+    // step 10
     if (trapResult.isPrimitive()) {
         JSAutoByteString bytes;
         if (!AtomToPrintableString(cx, cx->names().enumerate, &bytes))
             return false;
         RootedValue v(cx, ObjectOrNullValue(proxy));
         js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_SEARCH_STACK,
                              v, js::NullPtr(), bytes.ptr());
         return false;
     }
 
-    // step 10
-    // FIXME: the trap should return an iterator object, see bug 783826. Since this isn't very
-    // useful for us internally, we convery to an id vector.
-    return ArrayToIdVector(cx, proxy, target, trapResult, props, 0, cx->names().enumerate);
+    // step 11
+    objp.set(&trapResult.toObject());
+    return true;
+}
+
+// Non-standard, try to convert iterator result to ids
+bool
+ScriptedDirectProxyHandler::getEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const
+{
+    RootedObject iterator(cx);
+    if (!enumerate(cx, proxy, &iterator))
+        return false;
+
+    do {
+        RootedValue rval(cx);
+        if (!IteratorMore(cx, iterator, &rval))
+            return false;
+
+        if (rval.isMagic(JS_NO_ITER_VALUE))
+            break;
+
+        RootedId id(cx);
+        if (!ValueToId<CanGC>(cx, rval, &id))
+            return false;
+
+        if (!props.append(id))
+            return false;
+    } while (true);
+
+    return true;
 }
 
 // ES6 (22 May, 2014) 9.5.7 Proxy.[[HasProperty]](P)
 bool
 ScriptedDirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
 {
     // step 2
     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
@@ -1003,23 +1031,16 @@ ScriptedDirectProxyHandler::set(JSContex
         }
     }
 
     // step 11, 15
     vp.set(BooleanValue(success));
     return true;
 }
 
-bool
-ScriptedDirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
-                                    MutableHandleObject objp) const
-{
-    // FIXME: Provide a proper implementation for this trap, see bug 787004
-    return DirectProxyHandler::iterate(cx, proxy, flags, objp);
-}
 
 // ES6 (22 May, 2014) 9.5.13 Proxy.[[Call]]
 bool
 ScriptedDirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const
 {
     // step 1
     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
 
--- a/js/src/proxy/ScriptedDirectProxyHandler.h
+++ b/js/src/proxy/ScriptedDirectProxyHandler.h
@@ -21,16 +21,17 @@ class ScriptedDirectProxyHandler : publi
     /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const MOZ_OVERRIDE;
 
     /* These two are standard internal methods but aren't implemented to spec yet. */
     virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
                                 MutableHandleObject protop) const MOZ_OVERRIDE;
     virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
                                 bool *bp) const MOZ_OVERRIDE;
     /* Non-standard, but needed to handle revoked proxies. */
     virtual bool setImmutablePrototype(JSContext *cx, HandleObject proxy,
@@ -59,18 +60,16 @@ class ScriptedDirectProxyHandler : publi
     // filter. [[GetOwnProperty]] could potentially change the enumerability of
     // the target's properties.
     virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                               AutoIdVector &props) const MOZ_OVERRIDE {
         return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
     }
     virtual bool getEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                            AutoIdVector &props) const MOZ_OVERRIDE;
-    virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
-                         MutableHandleObject objp) const MOZ_OVERRIDE;
 
     virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE;
     virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE {
         // For now we maintain the broken behavior that a scripted proxy is constructable if it's
         // callable. See bug 929467.
         return isCallable(obj);
     }
     virtual bool isScripted() const MOZ_OVERRIDE { return true; }
--- a/js/src/proxy/ScriptedIndirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedIndirectProxyHandler.cpp
@@ -222,16 +222,37 @@ ScriptedIndirectProxyHandler::delete_(JS
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue fval(cx), value(cx);
     return GetFundamentalTrap(cx, handler, cx->names().delete_, &fval) &&
            Trap1(cx, handler, fval, id, &value) &&
            ValueToBool(value, bp);
 }
 
 bool
+ScriptedIndirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy,
+                                        MutableHandleObject objp) const
+{
+    // The hook that is called "enumerate" in the spec, used to be "iterate"
+    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
+    RootedValue value(cx);
+    if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value))
+        return false;
+    if (!IsCallable(value))
+        return BaseProxyHandler::enumerate(cx, proxy, objp);
+
+    RootedValue rval(cx);
+    if (!Trap(cx, handler, value, 0, nullptr, &rval))
+        return false;
+    if (!ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, rval))
+        return false;
+    objp.set(&rval.toObject());
+    return true;
+}
+
+bool
 ScriptedIndirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue fval(cx), value(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().has, &fval))
         return false;
     if (!IsCallable(fval))
         return BaseProxyHandler::has(cx, proxy, id, bp);
@@ -332,36 +353,16 @@ ScriptedIndirectProxyHandler::getEnumera
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue fval(cx), value(cx);
     return GetFundamentalTrap(cx, handler, cx->names().enumerate, &fval) &&
            Trap(cx, handler, fval, 0, nullptr, &value) &&
            ArrayToIdVector(cx, value, props);
 }
 
 bool
-ScriptedIndirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
-                                      MutableHandleObject objp) const
-{
-    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
-    RootedValue value(cx);
-    if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value))
-        return false;
-    if (!IsCallable(value))
-        return BaseProxyHandler::iterate(cx, proxy, flags, objp);
-
-    RootedValue rval(cx);
-    if (!Trap(cx, handler, value, 0, nullptr, &rval))
-        return false;
-    if (!ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, rval))
-        return false;
-    objp.set(&rval.toObject());
-    return true;
-}
-
-bool
 ScriptedIndirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                                          CallArgs args) const
 {
     return BaseProxyHandler::nativeCall(cx, test, impl, args);
 }
 
 JSString *
 ScriptedIndirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const
--- a/js/src/proxy/ScriptedIndirectProxyHandler.h
+++ b/js/src/proxy/ScriptedIndirectProxyHandler.h
@@ -22,34 +22,34 @@ class ScriptedIndirectProxyHandler : pub
     /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, HandleObject proxy,
+                           MutableHandleObject objp) const MOZ_OVERRIDE;
     virtual bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const MOZ_OVERRIDE;
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                      MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                      bool strict, MutableHandleValue vp) const MOZ_OVERRIDE;
 
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                               AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool getEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                            AutoIdVector &props) const MOZ_OVERRIDE;
-    virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
-                         MutableHandleObject objp) const MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) const MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE;
     virtual bool isScripted() const MOZ_OVERRIDE { return true; }
 
     static const char family;
     static const ScriptedIndirectProxyHandler singleton;
 
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1775,16 +1775,21 @@ class DebugScopeProxy : public BaseProxy
         return getScopePropertyNames(cx, proxy, props, JSITER_OWNONLY);
     }
 
     bool getEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE
     {
         return getScopePropertyNames(cx, proxy, props, 0);
     }
 
+    bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const MOZ_OVERRIDE
+    {
+        return BaseProxyHandler::enumerate(cx, proxy, objp);
+    }
+
     bool has(JSContext *cx, HandleObject proxy, HandleId id_, bool *bp) const MOZ_OVERRIDE
     {
         RootedId id(cx, id_);
         ScopeObject &scopeObj = proxy->as<DebugScopeObject>().scope();
 
         if (isArguments(cx, id) && isFunctionScope(scopeObj)) {
             *bp = true;
             return true;
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -732,20 +732,20 @@ bool
 xpc::SandboxProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx,
                                                        JS::Handle<JSObject*> proxy,
                                                        AutoIdVector &props) const
 {
     return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
 }
 
 bool
-xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
-                                  unsigned flags, JS::MutableHandle<JSObject*> objp) const
+xpc::SandboxProxyHandler::enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
+                                    JS::MutableHandle<JSObject*> objp) const
 {
-    return BaseProxyHandler::iterate(cx, proxy, flags, objp);
+    return BaseProxyHandler::enumerate(cx, proxy, objp);
 }
 
 bool
 xpc::GlobalProperties::Parse(JSContext *cx, JS::HandleObject obj)
 {
     MOZ_ASSERT(JS_IsArrayObject(cx, obj));
 
     uint32_t length;
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -119,25 +119,25 @@ FilteringWrapper<Base, Policy>::getEnume
 {
     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
     return Base::getEnumerablePropertyKeys(cx, wrapper, props) &&
            Filter<Policy>(cx, wrapper, props);
 }
 
 template <typename Base, typename Policy>
 bool
-FilteringWrapper<Base, Policy>::iterate(JSContext *cx, HandleObject wrapper,
-                                        unsigned flags, MutableHandleObject objp) const
+FilteringWrapper<Base, Policy>::enumerate(JSContext *cx, HandleObject wrapper,
+                                          MutableHandleObject objp) const
 {
     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
-    // We refuse to trigger the iterator hook across chrome wrappers because
+    // We refuse to trigger the enumerate hook across chrome wrappers because
     // we don't know how to censor custom iterator objects. Instead we trigger
-    // the default proxy iterate trap, which will ask enumerate() for the list
-    // of (censored) ids.
-    return js::BaseProxyHandler::iterate(cx, wrapper, flags, objp);
+    // the default proxy enumerate trap, which will ask getEnumerablePropertyKeys()
+    // for the list of (censored) ids.
+    return js::BaseProxyHandler::enumerate(cx, wrapper, objp);
 }
 
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::call(JSContext *cx, JS::Handle<JSObject*> wrapper,
                                     const JS::CallArgs &args) const
 {
     if (!Policy::checkCall(cx, wrapper, args))
--- a/js/xpconnect/wrappers/FilteringWrapper.h
+++ b/js/xpconnect/wrappers/FilteringWrapper.h
@@ -36,18 +36,18 @@ class FilteringWrapper : public Base {
 
     virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle<JSObject*> wrapper,
                                        JS::Handle<jsid> id,
                                        JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> wrapper,
                                               JS::AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool getEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> wrapper,
                                            JS::AutoIdVector &props) const MOZ_OVERRIDE;
-    virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> wrapper, unsigned flags,
-                         JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> wrapper,
+                           JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
 
     virtual bool call(JSContext *cx, JS::Handle<JSObject*> wrapper,
                       const JS::CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, JS::Handle<JSObject*> wrapper,
                            const JS::CallArgs &args) const MOZ_OVERRIDE;
 
     virtual bool nativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl,
                             JS::CallArgs args) const MOZ_OVERRIDE;
--- a/js/xpconnect/wrappers/WaiveXrayWrapper.cpp
+++ b/js/xpconnect/wrappers/WaiveXrayWrapper.cpp
@@ -54,21 +54,21 @@ WaiveXrayWrapper::get(JSContext *cx, Han
                       HandleObject receiver, HandleId id,
                       MutableHandleValue vp) const
 {
     return CrossCompartmentWrapper::get(cx, wrapper, receiver, id, vp) &&
            WrapperFactory::WaiveXrayAndWrap(cx, vp);
 }
 
 bool
-WaiveXrayWrapper::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
-                          MutableHandleObject objp) const
+WaiveXrayWrapper::enumerate(JSContext *cx, HandleObject proxy,
+                            MutableHandleObject objp) const
 {
-    return CrossCompartmentWrapper::iterate(cx, proxy, flags, objp) &&
-           (!objp || WrapperFactory::WaiveXrayAndWrap(cx, objp));
+    return CrossCompartmentWrapper::enumerate(cx, proxy, objp) &&
+           WrapperFactory::WaiveXrayAndWrap(cx, objp);
 }
 
 bool
 WaiveXrayWrapper::call(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args) const
 {
     return CrossCompartmentWrapper::call(cx, wrapper, args) &&
            WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
 }
--- a/js/xpconnect/wrappers/WaiveXrayWrapper.h
+++ b/js/xpconnect/wrappers/WaiveXrayWrapper.h
@@ -24,18 +24,18 @@ class WaiveXrayWrapper : public js::Cros
                                 JS::MutableHandle<JSObject*> protop) const MOZ_OVERRIDE;
     virtual bool get(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> receiver,
                      JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, JS::Handle<JSObject*> wrapper,
                       const JS::CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, JS::Handle<JSObject*> wrapper,
                            const JS::CallArgs &args) const MOZ_OVERRIDE;
 
-    virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> proxy, unsigned flags,
-                         JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
+                           JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, JS::IsAcceptableThis test,
                             JS::NativeImpl impl, JS::CallArgs args) const MOZ_OVERRIDE;
     virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle<JSObject*> wrapper,
                                        JS::Handle<jsid> id,
                                        JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
 
     static const WaiveXrayWrapper singleton;
 };
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -2115,21 +2115,21 @@ bool
 XrayWrapper<Base, Traits>::getEnumerablePropertyKeys(JSContext *cx, HandleObject wrapper,
                                                      AutoIdVector &props) const
 {
     return getPropertyKeys(cx, wrapper, 0, props);
 }
 
 template <typename Base, typename Traits>
 bool
-XrayWrapper<Base, Traits>::iterate(JSContext *cx, HandleObject wrapper,
-                                   unsigned flags, MutableHandleObject objp) const
+XrayWrapper<Base, Traits>::enumerate(JSContext *cx, HandleObject wrapper,
+                                     MutableHandleObject objp) const
 {
     // Skip our Base if it isn't already ProxyHandler.
-    return js::BaseProxyHandler::iterate(cx, wrapper, flags, objp);
+    return js::BaseProxyHandler::enumerate(cx, wrapper, objp);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::call(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args) const
 {
     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL);
     // Hard cast the singleton since SecurityWrapper doesn't have one.
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -408,16 +408,18 @@ class XrayWrapper : public Base {
     virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
                                           JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
                                 JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, JS::Handle<JSObject*> wrapper,
                                  JS::AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> wrapper,
                          JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> wrapper,
+                           JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
     virtual bool getPrototypeOf(JSContext *cx, JS::HandleObject wrapper,
                                 JS::MutableHandleObject protop) const MOZ_OVERRIDE;
     virtual bool setPrototypeOf(JSContext *cx, JS::HandleObject wrapper,
                                 JS::HandleObject proto, bool *bp) const MOZ_OVERRIDE;
     virtual bool setImmutablePrototype(JSContext *cx, JS::HandleObject wrapper,
                                        bool *succeeded) const MOZ_OVERRIDE;
     virtual bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> wrapper, bool *succeeded) const MOZ_OVERRIDE;
     virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> wrapper, bool *extensible) const MOZ_OVERRIDE;
@@ -436,18 +438,16 @@ class XrayWrapper : public Base {
     virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
                                        JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool hasOwn(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
                         bool *bp) const MOZ_OVERRIDE;
     virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> wrapper,
                                               JS::AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool getEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> wrapper,
                                            JS::AutoIdVector &props) const MOZ_OVERRIDE;
-    virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> wrapper, unsigned flags,
-                         JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
 
     virtual const char *className(JSContext *cx, JS::HandleObject proxy) const MOZ_OVERRIDE;
     virtual bool defaultValue(JSContext *cx, JS::HandleObject wrapper,
                               JSType hint, JS::MutableHandleValue vp)
                               const MOZ_OVERRIDE;
 
     static const XrayWrapper singleton;
 
@@ -507,18 +507,18 @@ public:
 
     virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle<JSObject*> proxy,
                                        JS::Handle<jsid> id,
                                        JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                         bool *bp) const MOZ_OVERRIDE;
     virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy,
                                               JS::AutoIdVector &props) const MOZ_OVERRIDE;
-    virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> proxy, unsigned flags,
-                         JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
+                           JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
 };
 
 extern const SandboxProxyHandler sandboxProxyHandler;
 
 // A proxy handler that lets us wrap callables and invoke them with
 // the correct this object, while forwarding all other operations down
 // to them directly.
 class SandboxCallableProxyHandler : public js::Wrapper {