Back out 13 changesets (bug 1170760) for Gu bustage in homescreen/test/unit/apps_test.js
authorPhil Ringnalda <philringnalda@gmail.com>
Wed, 25 Nov 2015 21:02:55 -0800
changeset 274231 08f527d6528249e686c4ef9fac002939e3dab59a
parent 274230 8fa4b88f205c4be1afc99654d4f4739415a51182
child 274232 1161dc3a8efdfe10fbe3c538b0ce08d1222fb5f4
push id68524
push userphilringnalda@gmail.com
push dateThu, 26 Nov 2015 05:03:14 +0000
treeherdermozilla-inbound@08f527d65282 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1170760
milestone45.0a1
backs out5d84599a88460d7ecb53b8ef5f7ccbdd5b463afd
6104fe33d5f5367b8413b0108008356918ee160c
1dfb229da01df4659e7f521f01d8c6d88d30f0d6
f380faddfdd8ee76862fbb284b70143f6ec3bfb2
541831dc6b57ceec9c6857fcd8d7462c9b8a6c69
6a5b7dfab8822228233b0a079b249ead32ecc70c
ee514a25692270b5df04c85326d14bd798e20bd8
3c2c1acc34ee4f971e1172171ef692a6b7ed1adb
dc2a7f5dc5d6f3b202c328853e3d86101d372bdc
b312a08fbab525073a1eac321825953c4d73fafd
cb6aba9b849770047f6a93e2b05f7137bda3dc43
39e4f5b1ba4000ce22871a6fd5aec5e731abe70b
7d79cce3630ab0f5bb7591d556b475fc09a7a74b
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Back out 13 changesets (bug 1170760) for Gu bustage in homescreen/test/unit/apps_test.js CLOSED TREE Backed out changeset 5d84599a8846 (bug 1170760) Backed out changeset 6104fe33d5f5 (bug 1170760) Backed out changeset 1dfb229da01d (bug 1170760) Backed out changeset f380faddfdd8 (bug 1170760) Backed out changeset 541831dc6b57 (bug 1170760) Backed out changeset 6a5b7dfab882 (bug 1170760) Backed out changeset ee514a256922 (bug 1170760) Backed out changeset 3c2c1acc34ee (bug 1170760) Backed out changeset dc2a7f5dc5d6 (bug 1170760) Backed out changeset b312a08fbab5 (bug 1170760) Backed out changeset cb6aba9b8497 (bug 1170760) Backed out changeset 39e4f5b1ba40 (bug 1170760) Backed out changeset 7d79cce3630a (bug 1170760)
browser/base/content/sync/aboutSyncTabs.js
dom/base/DOMRequest.cpp
dom/base/DOMRequest.h
dom/bindings/BindingUtils.cpp
dom/bindings/Bindings.conf
dom/bindings/Codegen.py
dom/bindings/Errors.msg
dom/bindings/parser/WebIDL.py
dom/bindings/test/TestBindingHeader.h
dom/bindings/test/TestCodeGen.webidl
dom/bindings/test/TestInterfaceJS.js
dom/promise/Promise.cpp
dom/promise/Promise.h
dom/promise/PromiseCallback.cpp
dom/promise/PromiseCallback.h
dom/promise/PromiseDebugging.cpp
dom/promise/PromiseDebugging.h
dom/promise/tests/chrome.ini
dom/promise/tests/file_promise_xrays.html
dom/promise/tests/mochitest.ini
dom/promise/tests/test_bug883683.html
dom/promise/tests/test_promise_xrays.html
dom/promise/tests/test_species_getter.html
dom/webidl/DOMRequest.webidl
dom/webidl/Promise.webidl
dom/webidl/PromiseDebugging.webidl
services/cloudsync/CloudSyncBookmarks.jsm
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/js/builtins/Promise-subclassing.html
toolkit/components/asyncshutdown/tests/xpcshell/test_AsyncShutdown.js
--- a/browser/base/content/sync/aboutSyncTabs.js
+++ b/browser/base/content/sync/aboutSyncTabs.js
@@ -268,17 +268,17 @@ var RemoteTabViewer = {
           };
           let tabEnt = this.createItem(tabAttrs);
           list.appendChild(tabEnt);
         }
       }
     }.bind(this);
 
     return CloudSync().tabs.getRemoteTabs()
-                           .then(updateTabList, Promise.reject.bind(Promise));
+                           .then(updateTabList, Promise.reject);
   },
 
   adjustContextMenu: function (event) {
     let mode = "all";
     switch (this._tabsList.selectedItems.length) {
       case 0:
         break;
       case 1:
--- a/dom/base/DOMRequest.cpp
+++ b/dom/base/DOMRequest.cpp
@@ -8,17 +8,16 @@
 
 #include "DOMError.h"
 #include "nsThreadUtils.h"
 #include "DOMCursor.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
-#include "jsfriendapi.h"
 
 using mozilla::dom::AnyCallback;
 using mozilla::dom::DOMError;
 using mozilla::dom::DOMRequest;
 using mozilla::dom::DOMRequestService;
 using mozilla::dom::DOMCursor;
 using mozilla::dom::Promise;
 using mozilla::dom::AutoJSAPI;
@@ -202,44 +201,39 @@ DOMRequest::FireEvent(const nsAString& a
 }
 
 void
 DOMRequest::RootResultVal()
 {
   mozilla::HoldJSObjects(this);
 }
 
-void
+already_AddRefed<Promise>
 DOMRequest::Then(JSContext* aCx, AnyCallback* aResolveCallback,
-                 AnyCallback* aRejectCallback,
-                 JS::MutableHandle<JS::Value> aRetval,
-                 mozilla::ErrorResult& aRv)
+                 AnyCallback* aRejectCallback, mozilla::ErrorResult& aRv)
 {
   if (!mPromise) {
     mPromise = Promise::Create(DOMEventTargetHelper::GetParentObject(), aRv);
     if (aRv.Failed()) {
-      return;
+      return nullptr;
     }
     if (mDone) {
       // Since we create mPromise lazily, it's possible that the DOMRequest object
       // has already fired its success/error event.  In that case we should
       // manually resolve/reject mPromise here.  mPromise will take care of
       // calling the callbacks on |promise| as needed.
       if (mError) {
         mPromise->MaybeRejectBrokenly(mError);
       } else {
         mPromise->MaybeResolve(mResult);
       }
     }
   }
 
-  // Just use the global of the Promise itself as the callee global.
-  JS::Rooted<JSObject*> global(aCx, mPromise->GetWrapper());
-  global = js::GetGlobalForObjectCrossCompartment(global);
-  mPromise->Then(aCx, global, aResolveCallback, aRejectCallback, aRetval, aRv);
+  return mPromise->Then(aCx, aResolveCallback, aRejectCallback, aRv);
 }
 
 NS_IMPL_ISUPPORTS(DOMRequestService, nsIDOMRequestService)
 
 NS_IMETHODIMP
 DOMRequestService::CreateRequest(nsIDOMWindow* aWindow,
                                  nsIDOMDOMRequest** aRequest)
 {
--- a/dom/base/DOMRequest.h
+++ b/dom/base/DOMRequest.h
@@ -69,21 +69,19 @@ public:
     NS_ASSERTION(mDone || !mError,
                  "Error should be null when pending");
     return mError;
   }
 
   IMPL_EVENT_HANDLER(success)
   IMPL_EVENT_HANDLER(error)
 
-  void
+  already_AddRefed<mozilla::dom::Promise>
   Then(JSContext* aCx, AnyCallback* aResolveCallback,
-       AnyCallback* aRejectCallback,
-       JS::MutableHandle<JS::Value> aRetval,
-       mozilla::ErrorResult& aRv);
+       AnyCallback* aRejectCallback, mozilla::ErrorResult& aRv);
 
   void FireSuccess(JS::Handle<JS::Value> aResult);
   void FireError(const nsAString& aError);
   void FireError(nsresult aError);
   void FireDetailedError(DOMError* aError);
 
   explicit DOMRequest(nsPIDOMWindow* aWindow);
   explicit DOMRequest(nsIGlobalObject* aGlobal);
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2756,27 +2756,18 @@ ConvertExceptionToPromise(JSContext* cx,
   if (!JS_GetPendingException(cx, &exn)) {
     // This is very important: if there is no pending exception here but we're
     // ending up in this code, that means the callee threw an uncatchable
     // exception.  Just propagate that out as-is.
     return false;
   }
 
   JS_ClearPendingException(cx);
-
-  nsCOMPtr<nsIGlobalObject> globalObj =
-    do_QueryInterface(global.GetAsSupports());
-  if (!globalObj) {
-    ErrorResult rv;
-    rv.Throw(NS_ERROR_UNEXPECTED);
-    return !rv.MaybeSetPendingException(cx);
-  }
-
   ErrorResult rv;
-  RefPtr<Promise> promise = Promise::Reject(globalObj, cx, exn, rv);
+  RefPtr<Promise> promise = Promise::Reject(global, exn, rv);
   if (rv.MaybeSetPendingException(cx)) {
     // We just give up.  We put the exception from the ErrorResult on
     // the JSContext just to make sure to not leak memory on the
     // ErrorResult, but now just put the original exception back.
     JS_SetPendingException(cx, exn);
     return false;
   }
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1944,23 +1944,17 @@ DOMInterfaces = {
         'headerFile': 'TestExampleProxyInterface-example.h',
         'register': False
         },
 
 'TestDeprecatedInterface' : {
         # Keep this in sync with TestExampleInterface
         'headerFile': 'TestBindingHeader.h',
         'register': False
-        },
-
-'TestInterfaceWithPromiseConstructorArg' : {
-        'headerFile': 'TestBindingHeader.h',
-        'register': False,
-        },
-
+        }
 }
 
 # These are temporary, until they've been converted to use new DOM bindings
 def addExternalIface(iface, nativeType=None, headerFile=None,
                      notflattened=False):
     if iface in DOMInterfaces:
         raise Exception('Interface declared both as WebIDL and External interface')
     domInterface = {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2974,31 +2974,16 @@ class CGCreateInterfaceObjectsMethod(CGA
                 }
                 """))
 
             unforgeableHolderSetup = CGList(
                 [createUnforgeableHolder, installUnforgeableHolder], "\n")
         else:
             unforgeableHolderSetup = None
 
-        if self.descriptor.name == "Promise":
-            speciesSetup = CGGeneric(fill(
-                """
-                JS::Rooted<JSObject*> promiseConstructor(aCx, *interfaceCache);
-                JS::Rooted<jsid> species(aCx,
-                  SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species)));
-                if (!JS_DefinePropertyById(aCx, promiseConstructor, species, JS::UndefinedHandleValue,
-                                           JSPROP_SHARED, Promise::PromiseSpecies, nullptr)) {
-                  $*{failureCode}
-                }
-                """,
-                failureCode=failureCode))
-        else:
-            speciesSetup = None
-
         if (self.descriptor.interface.isOnGlobalProtoChain() and
             needInterfacePrototypeObject):
             makeProtoPrototypeImmutable = CGGeneric(fill(
                 """
                 if (*${protoCache}) {
                   bool succeeded;
                   JS::Handle<JSObject*> prot = GetProtoObjectHandle(aCx, aGlobal);
                   if (!JS_SetImmutablePrototype(aCx, prot, &succeeded)) {
@@ -3014,17 +2999,17 @@ class CGCreateInterfaceObjectsMethod(CGA
                 protoCache=protoCache,
                 failureCode=failureCode))
         else:
             makeProtoPrototypeImmutable = None
 
         return CGList(
             [getParentProto, CGGeneric(getConstructorProto), initIds,
              prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup,
-             speciesSetup, makeProtoPrototypeImmutable],
+             makeProtoPrototypeImmutable],
             "\n").define()
 
 
 class CGGetPerInterfaceObject(CGAbstractMethod):
     """
     A method for getting a per-interface object (a prototype object or interface
     constructor object).
     """
@@ -5088,134 +5073,34 @@ def getJSToNativeConversionInfo(type, de
             else:
                 declType = "NonNull<" + typeName + ">"
 
         templateBody = ""
         if forceOwningType:
             templateBody += 'static_assert(IsRefcounted<%s>::value, "We can only store refcounted classes.");' % typeName
 
         if isPromise:
-            # Per spec, what we're supposed to do is take the original
-            # Promise.resolve and call it with the original Promise as this
-            # value to make a Promise out of whatever value we actually have
-            # here.  The question is which global we should use.  There are
-            # several cases to consider:
-            #
-            # 1) Normal call to API with a Promise argument.  This is a case the
-            #    spec covers, and we should be using the current Realm's
-            #    Promise.  That means the current compartment.
-            # 2) Call to API with a Promise argument over Xrays.  In practice,
-            #    this sort of thing seems to be used for giving an API
-            #    implementation a way to wait for conclusion of an asyc
-            #    operation, _not_ to expose the Promise to content code.  So we
-            #    probably want to allow callers to use such an API in a
-            #    "natural" way, by passing chrome-side promises; indeed, that
-            #    may be all that the caller has to represent their async
-            #    operation.  That means we really need to do the
-            #    Promise.resolve() in the caller (chrome) compartment: if we do
-            #    it in the content compartment, we will try to call .then() on
-            #    the chrome promise while in the content compartment, which will
-            #    throw and we'll just get a rejected Promise.  Note that this is
-            #    also the reason why a caller who has a chrome Promise
-            #    representing an async operation can't itself convert it to a
-            #    content-side Promise (at least not without some serious
-            #    gyrations).
-            # 3) Promise return value from a callback or callback interface.
-            #    This is in theory a case the spec covers but in practice it
-            #    really doesn't define behavior here because it doesn't define
-            #    what Realm we're in after the callback returns, which is when
-            #    the argument conversion happens.  We will use the current
-            #    compartment, which is the compartment of the callable (which
-            #    may itself be a cross-compartment wrapper itself), which makes
-            #    as much sense as anything else. In practice, such an API would
-            #    once again be providing a Promise to signal completion of an
-            #    operation, which would then not be exposed to anyone other than
-            #    our own implementation code.
-            # 4) Return value from a JS-implemented interface.  In this case we
-            #    have a problem.  Our current compartment is the compartment of
-            #    the JS implementation.  But if the JS implementation returned
-            #    a page-side Promise (which is a totally sane thing to do, and
-            #    in fact the right thing to do given that this return value is
-            #    going right to content script) then we don't want to
-            #    Promise.resolve with our current compartment Promise, because
-            #    that will wrap it up in a chrome-side Promise, which is
-            #    decidedly _not_ what's desired here.  So in that case we
-            #    should really unwrap the return value and use the global of
-            #    the result.  CheckedUnwrap should be good enough for that; if
-            #    it fails, then we're failing unwrap while in a
-            #    system-privileged compartment, so presumably we have a dead
-            #    object wrapper.  Just error out.  Do NOT fall back to using
-            #    the current compartment instead: that will return a
-            #    system-privileged rejected (because getting .then inside
-            #    resolve() failed) Promise to the caller, which they won't be
-            #    able to touch.  That's not helpful.  If we error out, on the
-            #    other hand, they will get a content-side rejected promise.
-            #    Same thing if the value returned is not even an object.
-            if isCallbackReturnValue == "JSImpl":
-                # Case 4 above.  Note that globalObj defaults to the current
-                # compartment global.  Note that we don't use $*{exceptionCode}
-                # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED)
-                # which we don't really want here.
-                assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n"
-                getPromiseGlobal = fill(
-                    """
-                    if (!$${val}.isObject()) {
-                      aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
-                      return nullptr;
-                    }
-                    JSObject* unwrappedVal = js::CheckedUnwrap(&$${val}.toObject());
-                    if (!unwrappedVal) {
-                      // A slight lie, but not much of one, for a dead object wrapper.
-                      aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
-                      return nullptr;
-                    }
-                    globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal);
-                    """,
-                    sourceDescription=sourceDescription)
-            else:
-                getPromiseGlobal = ""
-
             templateBody = fill(
                 """
-                { // Scope for our GlobalObject, ErrorResult, JSAutoCompartment,
-                  // etc.
-
-                  JS::Rooted<JSObject*> globalObj(cx, JS::CurrentGlobalOrNull(cx));
-                  $*{getPromiseGlobal}
-                  JSAutoCompartment ac(cx, globalObj);
-                  GlobalObject promiseGlobal(cx, globalObj);
+                { // Scope for our GlobalObject and ErrorResult
+
+                  // Might as well use CurrentGlobalOrNull here; that will at
+                  // least give us the same behavior as if the caller just called
+                  // Promise.resolve() themselves.
+                  GlobalObject promiseGlobal(cx, JS::CurrentGlobalOrNull(cx));
                   if (promiseGlobal.Failed()) {
                     $*{exceptionCode}
                   }
                   ErrorResult promiseRv;
-                  JS::Handle<JSObject*> promiseCtor =
-                    PromiseBinding::GetConstructorObjectHandle(cx, globalObj);
-                  if (!promiseCtor) {
-                    $*{exceptionCode}
-                  }
-                  JS::Rooted<JS::Value> resolveThisv(cx, JS::ObjectValue(*promiseCtor));
-                  JS::Rooted<JS::Value> resolveResult(cx);
-                  JS::Rooted<JS::Value> valueToResolve(cx, $${val});
-                  if (!JS_WrapValue(cx, &valueToResolve)) {
-                    $*{exceptionCode}
-                  }
-                  Promise::Resolve(promiseGlobal, resolveThisv, valueToResolve,
-                                   &resolveResult, promiseRv);
+                  $${declName} = Promise::Resolve(promiseGlobal, $${val}, promiseRv);
                   if (promiseRv.MaybeSetPendingException(cx)) {
                     $*{exceptionCode}
                   }
-                  nsresult unwrapRv = UNWRAP_OBJECT(Promise, &resolveResult.toObject(), $${declName});
-                  if (NS_FAILED(unwrapRv)) { // Quite odd
-                    promiseRv.Throw(unwrapRv);
-                    promiseRv.MaybeSetPendingException(cx);
-                    $*{exceptionCode}
-                  }
                 }
                 """,
-                getPromiseGlobal=getPromiseGlobal,
                 exceptionCode=exceptionCode)
         elif not descriptor.skipGen and not descriptor.interface.isConsequential() and not descriptor.interface.isExternal():
             if failureCode is not None:
                 templateBody += str(CastableObjectUnwrapper(
                     descriptor,
                     "&${val}.toObject()",
                     "${declName}",
                     failureCode))
@@ -7142,86 +7027,38 @@ class CGPerSignatureCall(CGThing):
         # needsCx decision on the types involved, just on our extended
         # attributes. Also, JSContext is not needed for the static case
         # since GlobalObject already contains the context.
         needsCx = needCx(returnType, arguments, self.extendedAttributes,
                          not descriptor.interface.isJSImplemented(), static)
         if needsCx:
             argsPre.append("cx")
 
-        # Hack for making Promise.prototype.then work well over Xrays.
-        if (not static and
-            (descriptor.name == "Promise" or
-             descriptor.name == "MozAbortablePromise") and
-            idlNode.isMethod() and
-            idlNode.identifier.name == "then"):
-            cgThings.append(CGGeneric(dedent(
-                """
-                JS::Rooted<JSObject*> calleeGlobal(cx, xpc::XrayAwareCalleeGlobal(&args.callee()));
-                """)))
-            argsPre.append("calleeGlobal")
-
         needsUnwrap = False
         argsPost = []
         if isConstructor:
+            needsUnwrap = True
+            needsUnwrappedVar = False
+            unwrappedVar = "obj"
             if descriptor.name == "Promise" or descriptor.name == "MozAbortablePromise":
                 # Hack for Promise for now: pass in our desired proto so the
                 # implementation can create the reflector with the right proto.
                 argsPost.append("desiredProto")
-                # Also, we do not want to enter the content compartment when the
-                # Promise constructor is called via Xrays, because we want to
-                # create our callback functions that we will hand to our caller
-                # in the Xray compartment.  The reason we want to do that is the
-                # following situation, over Xrays:
-                #
-                #   contentWindow.Promise.race([Promise.resolve(5)])
-                #
-                # Ideally this would work.  Internally, race() does a
-                # contentWindow.Promise.resolve() on everything in the array.
-                # Per spec, to support subclassing,
-                # contentWindow.Promise.resolve has to do:
-                #
-                #   var resolve, reject;
-                #   var p = new contentWindow.Promise(function(a, b) {
-                #    resolve = a;
-                #    reject = b;
-                #  });
-                #  resolve(arg);
-                #  return p;
-                #
-                # where "arg" is, in this case, the chrome-side return value of
-                # Promise.resolve(5).  But if the "resolve" function in that
-                # case were created in the content compartment, then calling it
-                # would wrap "arg" in an opaque wrapper, and that function tries
-                # to get .then off the argument, which would throw.  So we need
-                # to create the "resolve" function in the chrome compartment,
-                # and hence want to be running the entire Promise constructor
-                # (which creates that function) in the chrome compartment in
-                # this case. So don't set needsUnwrap here.
-            else:
-                needsUnwrap = True
-                needsUnwrappedVar = False
-                unwrappedVar = "obj"
         elif descriptor.interface.isJSImplemented():
             if not idlNode.isStatic():
                 needsUnwrap = True
                 needsUnwrappedVar = True
                 argsPost.append("js::GetObjectCompartment(unwrappedObj ? *unwrappedObj : obj)")
         elif needScopeObject(returnType, arguments, self.extendedAttributes,
                              descriptor.wrapperCache, True,
                              idlNode.getExtendedAttribute("StoreInSlot")):
             needsUnwrap = True
             needsUnwrappedVar = True
             argsPre.append("unwrappedObj ? *unwrappedObj : obj")
 
-        if static and not isConstructor and descriptor.name == "Promise":
-            # Hack for Promise for now: pass in the "this" value to
-            # Promise static methods.
-            argsPre.append("args.thisv()")
-
         if needsUnwrap and needsUnwrappedVar:
             # We cannot assign into obj because it's a Handle, not a
             # MutableHandle, so we need a separate Rooted.
             cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n"))
             unwrappedVar = "unwrappedObj.ref()"
 
         if idlNode.isMethod() and idlNode.isLegacycaller():
             # If we can have legacycaller with identifier, we can't
@@ -9156,16 +8993,36 @@ class CGStaticMethodJitinfo(CGGeneric):
             "  JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n"
             "  false, false, 0\n"
             "};\n" %
             (IDLToCIdentifier(method.identifier.name),
              CppKeywords.checkMethodName(
                  IDLToCIdentifier(method.identifier.name))))
 
 
+class CGMethodIdentityTest(CGAbstractMethod):
+    """
+    A class to generate a method-identity test for a given IDL operation.
+    """
+    def __init__(self, descriptor, method):
+        self.method = method
+        name = "Is%sMethod" % MakeNativeName(method.identifier.name)
+        CGAbstractMethod.__init__(self, descriptor, name, 'bool',
+                                  [Argument('JS::Handle<JSObject*>', 'aObj')])
+
+    def definition_body(self):
+        return dedent(
+            """
+            MOZ_ASSERT(aObj);
+            return js::IsFunctionObject(aObj) &&
+                   js::FunctionObjectIsNative(aObj) &&
+                   FUNCTION_VALUE_TO_JITINFO(JS::ObjectValue(*aObj)) == &%s_methodinfo;
+            """ % IDLToCIdentifier(self.method.identifier.name))
+
+
 def getEnumValueName(value):
     # Some enum values can be empty strings.  Others might have weird
     # characters in them.  Deal with the former by returning "_empty",
     # deal with possible name collisions from that by throwing if the
     # enum value is actually "_empty", and throw on any value
     # containing non-ASCII chars for now. Replace all chars other than
     # [0-9A-Za-z_] with '_'.
     if re.match("[^\x20-\x7E]", value):
@@ -11787,16 +11644,18 @@ class CGDescriptor(CGThing):
                     elif descriptor.interface.hasInterfacePrototypeObject():
                         specializedMethod = CGSpecializedMethod(descriptor, m)
                         cgThings.append(specializedMethod)
                         if m.returnsPromise():
                             cgThings.append(CGMethodPromiseWrapper(descriptor, specializedMethod))
                         cgThings.append(CGMemberJITInfo(descriptor, m))
                         if props.isCrossOriginMethod:
                             crossOriginMethods.add(m.identifier.name)
+                        if m.getExtendedAttribute("MethodIdentityTestable"):
+                            cgThings.append(CGMethodIdentityTest(descriptor, m))
             # If we've hit the maplike/setlike member itself, go ahead and
             # generate its convenience functions.
             elif m.isMaplikeOrSetlike():
                 cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m))
             elif m.isAttr():
                 if m.stringifier:
                     raise TypeError("Stringifier attributes not supported yet. "
                                     "See bug 824857.\n"
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -78,16 +78,10 @@ MSG_DEF(MSG_INVALID_URL_SCHEME, 2, JSEXN
 MSG_DEF(MSG_RESPONSE_URL_IS_NULL, 0, JSEXN_TYPEERR, "Cannot set Response.finalURL when Response.url is null.")
 MSG_DEF(MSG_RESPONSE_HAS_VARY_STAR, 0, JSEXN_TYPEERR, "Invalid Response object with a 'Vary: *' header.")
 MSG_DEF(MSG_BAD_FORMDATA, 0, JSEXN_TYPEERR, "Could not parse content as FormData.")
 MSG_DEF(MSG_NO_ACTIVE_WORKER, 1, JSEXN_TYPEERR, "No active worker for scope {0}.")
 MSG_DEF(MSG_NOTIFICATION_PERMISSION_DENIED, 0, JSEXN_TYPEERR, "Permission to show Notification denied.")
 MSG_DEF(MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER, 0, JSEXN_TYPEERR, "Notification constructor cannot be used in ServiceWorkerGlobalScope. Use registration.showNotification() instead.")
 MSG_DEF(MSG_INVALID_SCOPE, 2, JSEXN_TYPEERR, "Invalid scope trying to resolve {0} with base URL {1}.")
 MSG_DEF(MSG_INVALID_KEYFRAME_OFFSETS, 0, JSEXN_TYPEERR, "Keyframes with specified offsets must be in order and all be in the range [0, 1].")
-MSG_DEF(MSG_ILLEGAL_PROMISE_CONSTRUCTOR, 0, JSEXN_TYPEERR, "Non-constructor value passed to NewPromiseCapability.")
-MSG_DEF(MSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY, 0, JSEXN_TYPEERR, "GetCapabilitiesExecutor function already invoked with non-undefined values.")
-MSG_DEF(MSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
-MSG_DEF(MSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
-MSG_DEF(MSG_PROMISE_ARG_NOT_ITERABLE, 1, JSEXN_TYPEERR, "{0} is not iterable")
-MSG_DEF(MSG_IS_NOT_PROMISE, 1, JSEXN_TYPEERR, "{0} is not a Promise")
 MSG_DEF(MSG_SW_INSTALL_ERROR, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} encountered an error during installation.")
 MSG_DEF(MSG_SW_SCRIPT_THREW, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} threw an exception during script evaluation.")
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -4800,16 +4800,17 @@ class IDLMethod(IDLInterfaceMember, IDLS
               identifier == "UnsafeInPrerendering" or
               identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "Func" or
               identifier == "AvailableIn" or
               identifier == "CheckAnyPermissions" or
               identifier == "CheckAllPermissions" or
               identifier == "BinaryName" or
+              identifier == "MethodIdentityTestable" or
               identifier == "StaticClassOverride"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on method" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -1366,24 +1366,12 @@ public:
   already_AddRefed<TestDeprecatedInterface>
     Constructor(const GlobalObject&, ErrorResult&);
 
   static void AlsoDeprecated(const GlobalObject&);
 
   virtual nsISupports* GetParentObject();
 };
 
-class TestInterfaceWithPromiseConstructorArg : public nsISupports, public nsWrapperCache
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  static
-  already_AddRefed<TestInterfaceWithPromiseConstructorArg>
-    Constructor(const GlobalObject&, Promise&, ErrorResult&);
-
-  virtual nsISupports* GetParentObject();
-};
-
 } // namespace dom
 } // namespace mozilla
 
 #endif /* TestBindingHeader_h */
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -50,17 +50,16 @@ callback interface TestCallbackInterface
   void passFloat32Array(Float32Array arg);
   void passFloat64Array(Float64Array arg);
   void passSequenceOfArrayBuffers(sequence<ArrayBuffer> arg);
   void passSequenceOfNullableArrayBuffers(sequence<ArrayBuffer?> arg);
   void passVariadicTypedArray(Float32Array... arg);
   void passVariadicNullableTypedArray(Float32Array?... arg);
   Uint8Array receiveUint8Array();
   attribute Uint8Array uint8ArrayAttr;
-  Promise<void> receivePromise();
 };
 
 callback interface TestSingleOperationCallbackInterface {
   TestInterface doSomething(short arg, sequence<double> anotherArg);
 };
 
 enum TestEnum {
   "1",
@@ -1030,19 +1029,16 @@ dictionary Dict : ParentDict {
 
   long dashed-name;
 
   required long requiredLong;
   required object requiredObject;
 
   CustomEventInit customEventInit;
   TestDictionaryTypedef dictionaryTypedef;
-
-  Promise<void> promise;
-  sequence<Promise<void>> promiseSequence;
 };
 
 dictionary ParentDict : GrandparentDict {
   long c = 5;
   TestInterface someInterface;
   TestInterface? someNullableInterface = null;
   TestExternalInterface someExternalInterface;
   any parentAny;
@@ -1160,12 +1156,8 @@ interface TestCppKeywordNamedMethodsInte
   long volatile();
 };
 
 [Deprecated="GetAttributeNode", Constructor()]
 interface TestDeprecatedInterface {
   static void alsoDeprecated();
 };
 
-
-[Constructor(Promise<void> promise)]
-interface TestInterfaceWithPromiseConstructorArg {
-};
--- a/dom/bindings/test/TestInterfaceJS.js
+++ b/dom/bindings/test/TestInterfaceJS.js
@@ -119,41 +119,43 @@ TestInterfaceJS.prototype = {
   testPromiseWithDOMExceptionThrowingThenFunction: function() {
     return this._win.Promise.resolve(5).then(() => {
       throw new this._win.DOMException("We are a third DOMException",
                                        "NetworkError");
     });
   },
 
   testPromiseWithThrowingChromeThenable: function() {
-    var thenable =  {
-      then: function() {
-        noSuchMethodExistsYo3()
-      }
-    };
+    // We need to produce a thing that has a "then" property in the page
+    // compartment, since we plan to call the page-provided resolve function.
+    var thenable = new this._win.Object();
+    Cu.waiveXrays(thenable).then = function() {
+      noSuchMethodExistsYo3()
+    }
     return new this._win.Promise(function(resolve) {
       resolve(thenable)
     });
   },
 
   testPromiseWithThrowingContentThenable: function(thenable) {
     // Waive Xrays on the thenable, because we're calling resolve() in the
     // chrome compartment, so that's the compartment the "then" property get
     // will happen in, and if we leave the Xray in place the function-valued
     // property won't return the function.
     return this._win.Promise.resolve(Cu.waiveXrays(thenable));
   },
 
   testPromiseWithDOMExceptionThrowingThenable: function() {
-    var thenable =  {
-      then: () => {
-        throw new this._win.DOMException("We are a fourth DOMException",
-                                         "TypeMismatchError");
-      }
-    };
+    // We need to produce a thing that has a "then" property in the page
+    // compartment, since we plan to call the page-provided resolve function.
+    var thenable = new this._win.Object();
+    Cu.waiveXrays(thenable).then = () => {
+      throw new this._win.DOMException("We are a fourth DOMException",
+                                       "TypeMismatchError");
+    }
     return new this._win.Promise(function(resolve) {
       resolve(thenable)
     });
   },
 
   get onsomething() {
     return this.__DOM_IMPL__.getEventHandler("onsomething");
   },
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -29,17 +29,16 @@
 #include "nsJSUtils.h"
 #include "nsPIDOMWindow.h"
 #include "PromiseCallback.h"
 #include "PromiseDebugging.h"
 #include "PromiseNativeHandler.h"
 #include "PromiseWorkerProxy.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
-#include "WrapperFactory.h"
 #include "xpcpublic.h"
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 namespace mozilla {
 namespace dom {
 
@@ -110,68 +109,68 @@ protected:
 
 private:
   RefPtr<Promise> mPromise;
   RefPtr<PromiseCallback> mCallback;
   JS::PersistentRooted<JS::Value> mValue;
   NS_DECL_OWNINGTHREAD;
 };
 
+enum {
+  SLOT_PROMISE = 0,
+  SLOT_DATA
+};
+
 /*
  * Utilities for thenable callbacks.
  *
  * A thenable is a { then: function(resolve, reject) { } }.
  * `then` is called with a resolve and reject callback pair.
  * Since only one of these should be called at most once (first call wins), the
  * two keep a reference to each other in SLOT_DATA. When either of them is
  * called, the references are cleared. Further calls are ignored.
  */
 namespace {
 void
 LinkThenableCallables(JSContext* aCx, JS::Handle<JSObject*> aResolveFunc,
                       JS::Handle<JSObject*> aRejectFunc)
 {
-  js::SetFunctionNativeReserved(aResolveFunc, Promise::SLOT_DATA,
+  js::SetFunctionNativeReserved(aResolveFunc, SLOT_DATA,
                                 JS::ObjectValue(*aRejectFunc));
-  js::SetFunctionNativeReserved(aRejectFunc, Promise::SLOT_DATA,
+  js::SetFunctionNativeReserved(aRejectFunc, SLOT_DATA,
                                 JS::ObjectValue(*aResolveFunc));
 }
 
 /*
  * Returns false if callback was already called before, otherwise breaks the
  * links and returns true.
  */
 bool
 MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle<JSObject*> aFunc)
 {
-  JS::Value otherFuncVal =
-    js::GetFunctionNativeReserved(aFunc, Promise::SLOT_DATA);
+  JS::Value otherFuncVal = js::GetFunctionNativeReserved(aFunc, SLOT_DATA);
 
   if (!otherFuncVal.isObject()) {
     return false;
   }
 
   JSObject* otherFuncObj = &otherFuncVal.toObject();
-  MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj,
-                                           Promise::SLOT_DATA).isObject());
+  MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, SLOT_DATA).isObject());
 
   // Break both references.
-  js::SetFunctionNativeReserved(aFunc, Promise::SLOT_DATA,
-                                JS::UndefinedValue());
-  js::SetFunctionNativeReserved(otherFuncObj, Promise::SLOT_DATA,
-                                JS::UndefinedValue());
+  js::SetFunctionNativeReserved(aFunc, SLOT_DATA, JS::UndefinedValue());
+  js::SetFunctionNativeReserved(otherFuncObj, SLOT_DATA, JS::UndefinedValue());
 
   return true;
 }
 
 Promise*
 GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc)
 {
-  JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc,
-                                                       Promise::SLOT_PROMISE);
+  JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, SLOT_PROMISE);
 
   MOZ_ASSERT(promiseVal.isObject());
 
   Promise* promise;
   UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise);
   return promise;
 }
 } // namespace
@@ -273,118 +272,58 @@ protected:
 
 private:
   RefPtr<Promise> mPromise;
   JS::PersistentRooted<JSObject*> mThenable;
   RefPtr<PromiseInit> mThen;
   NS_DECL_OWNINGTHREAD;
 };
 
-// A struct implementing
-// <http://www.ecma-international.org/ecma-262/6.0/#sec-promisecapability-records>.
-// While the spec holds on to these in some places, in practice those places
-// don't actually need everything from this struct, so we explicitly grab
-// members from it as needed in those situations.  That allows us to make this a
-// stack-only struct and keep the rooting simple.
-//
-// We also add an optimization for the (common) case when we discover that the
-// Promise constructor we're supposed to use is in fact the canonical Promise
-// constructor.  In that case we will just set mNativePromise in our
-// PromiseCapability and not set mPromise/mResolve/mReject; the correct
-// callbacks will be the standard Promise ones, and we don't really want to
-// synthesize JSFunctions for them in that situation.
-struct MOZ_STACK_CLASS Promise::PromiseCapability
+// Fast version of PromiseResolveThenableJob for use in the cases when we know we're
+// calling the canonical Promise.prototype.then on an actual DOM Promise.  In
+// that case we can just bypass the jumping into and out of JS and call
+// AppendCallbacks on that promise directly.
+class FastPromiseResolveThenableJob final : public nsRunnable
 {
-  explicit PromiseCapability(JSContext* aCx)
-    : mPromise(aCx)
-    , mResolve(aCx)
-    , mReject(aCx)
-  {}
+public:
+  FastPromiseResolveThenableJob(PromiseCallback* aResolveCallback,
+                                PromiseCallback* aRejectCallback,
+                                Promise* aNextPromise)
+    : mResolveCallback(aResolveCallback)
+    , mRejectCallback(aRejectCallback)
+    , mNextPromise(aNextPromise)
+  {
+    MOZ_ASSERT(aResolveCallback);
+    MOZ_ASSERT(aRejectCallback);
+    MOZ_ASSERT(aNextPromise);
+    MOZ_COUNT_CTOR(FastPromiseResolveThenableJob);
+  }
 
-  // Take an exception on aCx and try to convert it into a promise rejection.
-  // Note that this can result in a new exception being thrown on aCx, or an
-  // exception getting thrown on aRv.  On entry to this method, aRv is assumed
-  // to not be a failure.  This should only be called if NewPromiseCapability
-  // succeeded on this PromiseCapability.
-  void RejectWithException(JSContext* aCx, ErrorResult& aRv);
+  virtual
+  ~FastPromiseResolveThenableJob()
+  {
+    NS_ASSERT_OWNINGTHREAD(FastPromiseResolveThenableJob);
+    MOZ_COUNT_DTOR(FastPromiseResolveThenableJob);
+  }
 
-  // Return a JS::Value representing the promise.  This should only be called if
-  // NewPromiseCapability succeeded on this PromiseCapability.  It makes no
-  // guarantees about compartments (e.g. in the mNativePromise case it's in the
-  // compartment of the reflector, but in the mPromise case it might be in the
-  // compartment of some cross-compartment wrapper for a reflector).
-  JS::Value PromiseValue() const;
-
-  // All the JS::Value fields of this struct are actually objects, but for our
-  // purposes it's simpler to store them as JS::Value.
-
-  // [[Promise]].
-  JS::Rooted<JS::Value> mPromise;
-  // [[Resolve]].  Value in the context compartment.
-  JS::Rooted<JS::Value> mResolve;
-  // [[Reject]].  Value in the context compartment.
-  JS::Rooted<JS::Value> mReject;
-  // If mNativePromise is non-null, we should use it, not mPromise.
-  RefPtr<Promise> mNativePromise;
+protected:
+  NS_IMETHOD
+  Run() override
+  {
+    NS_ASSERT_OWNINGTHREAD(FastPromiseResolveThenableJob);
+    mNextPromise->AppendCallbacks(mResolveCallback, mRejectCallback);
+    return NS_OK;
+  }
 
 private:
-  // We don't want to allow creation of temporaries of this type, ever.
-  PromiseCapability(const PromiseCapability&) = delete;
-  PromiseCapability(PromiseCapability&&) = delete;
+  RefPtr<PromiseCallback> mResolveCallback;
+  RefPtr<PromiseCallback> mRejectCallback;
+  RefPtr<Promise> mNextPromise;
 };
 
-void
-Promise::PromiseCapability::RejectWithException(JSContext* aCx,
-                                                ErrorResult& aRv)
-{
-  // This method basically implements
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-ifabruptrejectpromise
-  // or at least the parts of it that happen if we have an abrupt completion.
-
-  MOZ_ASSERT(!aRv.Failed());
-  MOZ_ASSERT(mNativePromise || !mPromise.isUndefined(),
-             "NewPromiseCapability didn't succeed");
-
-  JS::Rooted<JS::Value> exn(aCx);
-  if (!JS_GetPendingException(aCx, &exn)) {
-    // This is an uncatchable exception, so can't be converted into a rejection.
-    // Just rethrow that on aRv.
-    aRv.ThrowUncatchableException();
-    return;
-  }
-
-  JS_ClearPendingException(aCx);
-
-  // If we have a native promise, just reject it without trying to call out into
-  // JS.
-  if (mNativePromise) {
-    mNativePromise->MaybeRejectInternal(aCx, exn);
-    return;
-  }
-
-  JS::Rooted<JS::Value> ignored(aCx);
-  if (!JS::Call(aCx, JS::UndefinedHandleValue, mReject, JS::HandleValueArray(exn),
-                &ignored)) {
-    aRv.NoteJSContextException();
-  }
-}
-
-JS::Value
-Promise::PromiseCapability::PromiseValue() const
-{
-  MOZ_ASSERT(mNativePromise || !mPromise.isUndefined(),
-             "NewPromiseCapability didn't succeed");
-
-  if (mNativePromise) {
-    return JS::ObjectValue(*mNativePromise->GetWrapper());
-  }
-
-  return mPromise;
-}
-
 // Promise
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
 #if defined(DOM_PROMISE_DEPRECATED_REPORTING)
   tmp->MaybeReportRejectedOnce();
 #else
@@ -653,18 +592,16 @@ Promise::JSCallbackThenableRejecter(JSCo
                                     unsigned aArgc, JS::Value* aVp)
 {
   return ThenableResolverCommon(aCx, PromiseCallback::Reject, aArgc, aVp);
 }
 
 /* static */ JSObject*
 Promise::CreateFunction(JSContext* aCx, Promise* aPromise, int32_t aTask)
 {
-  // If this function ever changes, make sure to update
-  // WrapperPromiseCallback::GetDependentPromise.
   JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback,
                                                  1 /* nargs */, 0 /* flags */,
                                                  nullptr);
   if (!func) {
     return nullptr;
   }
 
   JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
@@ -754,602 +691,133 @@ Promise::CallInitFunction(const GlobalOb
     return;
   }
 
   aInit.Call(resolveFunc, rejectFunc, aRv, "promise initializer",
              CallbackObject::eRethrowExceptions, Compartment());
   aRv.WouldReportJSException();
 
   if (aRv.Failed()) {
-    // There are two possibilities here.  Either we've got a rethrown exception,
-    // or we reported that already and synthesized a generic NS_ERROR_FAILURE on
-    // the ErrorResult.  In the former case, it doesn't much matter how we get
-    // the exception JS::Value from the ErrorResult to us, since we'll just end
-    // up wrapping it into the right compartment as needed if we hand it to
-    // someone.  But in the latter case we have to ensure that the new exception
-    // object we create is created in our reflector compartment, not in our
-    // current compartment, because in the case when we're a Promise constructor
-    // called over Xrays creating it in the current compartment would mean
-    // rejecting with a value that can't be accessed by code that can call
-    // then() on this Promise.
+    // We want the same behavior as this JS implementation:
+    //
+    // function Promise(arg) { try { arg(a, b); } catch (e) { this.reject(e); }}
     //
-    // Luckily, MaybeReject(aRv) does exactly what we want here: it enters our
-    // reflector compartment before trying to produce a JS::Value from the
-    // ErrorResult.
-    MaybeReject(aRv);
+    // In particular, that means not using MaybeReject(aRv) here, since that
+    // would create the exception object in our reflector compartment, while we
+    // want to create it in whatever the current compartment on cx is.
+    JS::Rooted<JS::Value> value(cx);
+    DebugOnly<bool> conversionResult = ToJSValue(cx, aRv, &value);
+    MOZ_ASSERT(conversionResult);
+    MaybeRejectInternal(cx, value);
   }
 }
 
-#define GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT 0
-#define GET_CAPABILITIES_EXECUTOR_REJECT_SLOT 1
-
-namespace {
-bool
-GetCapabilitiesExecutor(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+/* static */ already_AddRefed<Promise>
+Promise::Resolve(const GlobalObject& aGlobal,
+                 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
 {
-  // Implements
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-getcapabilitiesexecutor-functions
-  // except we store the [[Resolve]] and [[Reject]] in our own internal slots,
-  // not in a PromiseCapability.  The PromiseCapability will then read them from
-  // us.
-  JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
-
-  // Step 1 is an assert.
-
-  // Step 2 doesn't need to be done, because it's just giving a name to the
-  // PromiseCapability record which is supposed to be stored in an internal
-  // slot.  But we don't store that at all, per the comment above; we just
-  // directly store its [[Resolve]] and [[Reject]] members.
-
-  // Steps 3 and 4.
-  if (!js::GetFunctionNativeReserved(&args.callee(),
-                                     GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT).isUndefined() ||
-      !js::GetFunctionNativeReserved(&args.callee(),
-                                     GET_CAPABILITIES_EXECUTOR_REJECT_SLOT).isUndefined()) {
-    ErrorResult rv;
-    rv.ThrowTypeError<MSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY>();
-    return !rv.MaybeSetPendingException(aCx);
-  }
-
-  // Step 5.
-  js::SetFunctionNativeReserved(&args.callee(),
-                                GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT,
-                                args.get(0));
-
-  // Step 6.
-  js::SetFunctionNativeReserved(&args.callee(),
-                                GET_CAPABILITIES_EXECUTOR_REJECT_SLOT,
-                                args.get(1));
-
-  // Step 7.
-  args.rval().setUndefined();
-  return true;
-}
-} // anonymous namespace
-
-/* static */ void
-Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal,
-                              JS::Handle<JS::Value> aConstructor,
-                              bool aForceCallbackCreation,
-                              PromiseCapability& aCapability,
-                              ErrorResult& aRv)
-{
-  // Implements
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-newpromisecapability
-
-  if (!aConstructor.isObject() ||
-      !JS::IsConstructor(&aConstructor.toObject())) {
-    aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
-    return;
-  }
+  // If a Promise was passed, just return it.
+  if (aValue.isObject()) {
+    JS::Rooted<JSObject*> valueObj(aGlobal.Context(), &aValue.toObject());
+    Promise* nextPromise;
+    nsresult rv = UNWRAP_OBJECT(Promise, valueObj, nextPromise);
 
-  // Step 2 is a note.
-  // Step 3 is already done because we got the PromiseCapability passed in.
-
-  // Optimization: Check whether constructor is in fact the canonical
-  // Promise constructor for aGlobal.
-  JS::Rooted<JSObject*> global(aCx, aGlobal->GetGlobalJSObject());
-  {
-    // Scope for the JSAutoCompartment, since we need to enter the compartment
-    // of global to get constructors from it.  Save the compartment we used to
-    // be in, though; we'll need it later.
-    JS::Rooted<JSObject*> callerGlobal(aCx, JS::CurrentGlobalOrNull(aCx));
-    JSAutoCompartment ac(aCx, global);
-
-    // Now wrap aConstructor into the compartment of aGlobal, so comparing it to
-    // the canonical Promise for that compartment actually makes sense.
-    JS::Rooted<JS::Value> constructorValue(aCx, aConstructor);
-    if (!MaybeWrapObjectValue(aCx, &constructorValue)) {
-      aRv.NoteJSContextException();
-      return;
-    }
-
-    JSObject* defaultCtor = PromiseBinding::GetConstructorObject(aCx, global);
-    if (!defaultCtor) {
-      aRv.NoteJSContextException();
-      return;
-    }
-    if (defaultCtor == &constructorValue.toObject()) {
-      // This is the canonical Promise constructor.
-      aCapability.mNativePromise = Promise::Create(aGlobal, aRv);
-      if (aForceCallbackCreation) {
-        // We have to be a bit careful here.  We want to create these functions
-        // in the compartment in which they would be created if we actually
-        // invoked the constructor via JS::Construct below.  That means our
-        // callerGlobal compartment if aConstructor is an Xray and the reflector
-        // compartment of the promise we're creating otherwise.  But note that
-        // our callerGlobal compartment is precisely the reflector compartment
-        // unless the call was done over Xrays, because the reflector
-        // compartment comes from xpc::XrayAwareCalleeGlobal.  So we really just
-        // want to create these functions in the callerGlobal compartment.
-        MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(&aConstructor.toObject()) ||
-                   callerGlobal == global);
-        JSAutoCompartment ac2(aCx, callerGlobal);
-
-        JSObject* resolveFuncObj =
-          CreateFunction(aCx, aCapability.mNativePromise,
-                         PromiseCallback::Resolve);
-        if (!resolveFuncObj) {
-          aRv.NoteJSContextException();
-          return;
-        }
-        aCapability.mResolve.setObject(*resolveFuncObj);
-
-        JSObject* rejectFuncObj =
-          CreateFunction(aCx, aCapability.mNativePromise,
-                         PromiseCallback::Reject);
-        if (!rejectFuncObj) {
-          aRv.NoteJSContextException();
-          return;
-        }
-        aCapability.mReject.setObject(*rejectFuncObj);
-      }
-      return;
+    if (NS_SUCCEEDED(rv)) {
+      RefPtr<Promise> addRefed = nextPromise;
+      return addRefed.forget();
     }
   }
 
-  // Step 4.
-  // We can create our get-capabilities function in the calling compartment.  It
-  // will work just as if we did |new promiseConstructor(function(a,b){}).
-  // Notably, if we're called over Xrays that's all fine, because we will end up
-  // creating the callbacks in the caller compartment in that case.
-  JSFunction* getCapabilitiesFunc =
-    js::NewFunctionWithReserved(aCx, GetCapabilitiesExecutor,
-                                2 /* nargs */,
-                                0 /* flags */,
-                                nullptr);
-  if (!getCapabilitiesFunc) {
-    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return;
-  }
-
-  JS::Rooted<JSObject*> getCapabilitiesObj(aCx);
-  getCapabilitiesObj = JS_GetFunctionObject(getCapabilitiesFunc);
-
-  // Step 5 doesn't need to be done, since we're not actually storing a
-  // PromiseCapability in the executor; see the comments in
-  // GetCapabilitiesExecutor above.
-
-  // Step 6 and step 7.
-  JS::Rooted<JS::Value> getCapabilities(aCx,
-                                        JS::ObjectValue(*getCapabilitiesObj));
-  JS::Rooted<JS::Value> promiseVal(aCx);
-  if (!JS::Construct(aCx, aConstructor,
-                     JS::HandleValueArray(getCapabilities),
-                     &promiseVal)) {
-    aRv.NoteJSContextException();
-    return;
-  }
-
-  // Step 8 plus copying over the value to the PromiseCapability.
-  JS::Rooted<JS::Value> v(aCx);
-  v = js::GetFunctionNativeReserved(getCapabilitiesObj,
-                                    GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT);
-  if (!v.isObject() || !JS::IsCallable(&v.toObject())) {
-    aRv.ThrowTypeError<MSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE>();
-    return;
-  }
-  aCapability.mResolve = v;
-
-  // Step 9 plus copying over the value to the PromiseCapability.
-  v = js::GetFunctionNativeReserved(getCapabilitiesObj,
-                                    GET_CAPABILITIES_EXECUTOR_REJECT_SLOT);
-  if (!v.isObject() || !JS::IsCallable(&v.toObject())) {
-    aRv.ThrowTypeError<MSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE>();
-    return;
-  }
-  aCapability.mReject = v;
-
-  // Step 10.
-  aCapability.mPromise = promiseVal;
-
-  // Step 11 doesn't need anything, since the PromiseCapability was passed in.
-}
-
-/* static */ void
-Promise::Resolve(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
-                 JS::Handle<JS::Value> aValue,
-                 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv)
-{
-  // Implementation of
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.resolve
-
-  JSContext* cx = aGlobal.Context();
-
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(aGlobal.GetAsSupports());
   if (!global) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
-    return;
-  }
-
-  // Steps 1 and 2.
-  if (!aThisv.isObject()) {
-    aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
-    return;
+    return nullptr;
   }
 
-  // Step 3.  If a Promise was passed and matches our constructor, just return it.
-  if (aValue.isObject()) {
-    JS::Rooted<JSObject*> valueObj(cx, &aValue.toObject());
-    Promise* nextPromise;
-    nsresult rv = UNWRAP_OBJECT(Promise, valueObj, nextPromise);
-
-    if (NS_SUCCEEDED(rv)) {
-      JS::Rooted<JS::Value> constructor(cx);
-      if (!JS_GetProperty(cx, valueObj, "constructor", &constructor)) {
-        aRv.NoteJSContextException();
-        return;
-      }
-
-      // Cheat instead of calling JS_SameValue, since we know one's an object.
-      if (aThisv == constructor) {
-        aRetval.setObject(*valueObj);
-        return;
-      }
-    }
+  RefPtr<Promise> p = Resolve(global, aGlobal.Context(), aValue, aRv);
+  if (p) {
+    p->mFullfillmentStack = p->mAllocationStack;
   }
-
-  // Step 4.
-  PromiseCapability capability(cx);
-  NewPromiseCapability(cx, global, aThisv, false, capability, aRv);
-  // Step 5.
-  if (aRv.Failed()) {
-    return;
-  }
-
-  // Step 6.
-  Promise* p = capability.mNativePromise;
-  if (p) {
-    p->MaybeResolveInternal(cx, aValue);
-    p->mFullfillmentStack = p->mAllocationStack;
-  } else {
-    JS::Rooted<JS::Value> value(cx, aValue);
-    JS::Rooted<JS::Value> ignored(cx);
-    if (!JS::Call(cx, JS::UndefinedHandleValue /* thisVal */,
-                  capability.mResolve, JS::HandleValueArray(value),
-                  &ignored)) {
-      // Step 7.
-      aRv.NoteJSContextException();
-      return;
-    }
-  }
-
-  // Step 8.
-  aRetval.set(capability.PromiseValue());
+  return p.forget();
 }
 
 /* static */ already_AddRefed<Promise>
 Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
                  JS::Handle<JS::Value> aValue, ErrorResult& aRv)
 {
   RefPtr<Promise> promise = Create(aGlobal, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   promise->MaybeResolveInternal(aCx, aValue);
   return promise.forget();
 }
 
-/* static */ void
-Promise::Reject(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
-                JS::Handle<JS::Value> aValue,
-                JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv)
+/* static */ already_AddRefed<Promise>
+Promise::Reject(const GlobalObject& aGlobal,
+                JS::Handle<JS::Value> aValue, ErrorResult& aRv)
 {
-  // Implementation of
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.reject
-
-  JSContext* cx = aGlobal.Context();
-
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(aGlobal.GetAsSupports());
   if (!global) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
-    return;
-  }
-
-  // Steps 1 and 2.
-  if (!aThisv.isObject()) {
-    aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
-    return;
-  }
-
-  // Step 3.
-  PromiseCapability capability(cx);
-  NewPromiseCapability(cx, global, aThisv, false, capability, aRv);
-  // Step 4.
-  if (aRv.Failed()) {
-    return;
+    return nullptr;
   }
 
-  // Step 5.
-  Promise* p = capability.mNativePromise;
+  RefPtr<Promise> p = Reject(global, aGlobal.Context(), aValue, aRv);
   if (p) {
-    p->MaybeRejectInternal(cx, aValue);
     p->mRejectionStack = p->mAllocationStack;
-  } else {
-    JS::Rooted<JS::Value> value(cx, aValue);
-    JS::Rooted<JS::Value> ignored(cx);
-    if (!JS::Call(cx, JS::UndefinedHandleValue /* thisVal */,
-                  capability.mReject, JS::HandleValueArray(value),
-                  &ignored)) {
-      // Step 6.
-      aRv.NoteJSContextException();
-      return;
-    }
   }
-
-  // Step 7.
-  aRetval.set(capability.PromiseValue());
+  return p.forget();
 }
 
 /* static */ already_AddRefed<Promise>
 Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
                 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
 {
   RefPtr<Promise> promise = Create(aGlobal, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   promise->MaybeRejectInternal(aCx, aValue);
   return promise.forget();
 }
 
-namespace {
-void
-SpeciesConstructor(JSContext* aCx,
-                   JS::Handle<JSObject*> promise,
-                   JS::Handle<JS::Value> defaultCtor,
-                   JS::MutableHandle<JS::Value> ctor,
-                   ErrorResult& aRv)
+already_AddRefed<Promise>
+Promise::Then(JSContext* aCx, AnyCallback* aResolveCallback,
+              AnyCallback* aRejectCallback, ErrorResult& aRv)
 {
-  // Implements
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-speciesconstructor
-
-  // Step 1.
-  MOZ_ASSERT(promise);
-
-  // Step 2.
-  JS::Rooted<JS::Value> constructorVal(aCx);
-  if (!JS_GetProperty(aCx, promise, "constructor", &constructorVal)) {
-    // Step 3.
-    aRv.NoteJSContextException();
-    return;
-  }
-
-  // Step 4.
-  if (constructorVal.isUndefined()) {
-    ctor.set(defaultCtor);
-    return;
-  }
-
-  // Step 5.
-  if (!constructorVal.isObject()) {
-    aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
-    return;
-  }
-
-  // Step 6.
-  JS::Rooted<jsid> species(aCx,
-    SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species)));
-  JS::Rooted<JS::Value> speciesVal(aCx);
-  JS::Rooted<JSObject*> constructorObj(aCx, &constructorVal.toObject());
-  if (!JS_GetPropertyById(aCx, constructorObj, species, &speciesVal)) {
-    // Step 7.
-    aRv.NoteJSContextException();
-    return;
-  }
-
-  // Step 8.
-  if (speciesVal.isNullOrUndefined()) {
-    ctor.set(defaultCtor);
-    return;
-  }
-
-  // Step 9.
-  if (speciesVal.isObject() && JS::IsConstructor(&speciesVal.toObject())) {
-    ctor.set(speciesVal);
-    return;
-  }
-
-  // Step 10.
-  aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
-}
-} // anonymous namespace
-
-void
-Promise::Then(JSContext* aCx, JS::Handle<JSObject*> aCalleeGlobal,
-              AnyCallback* aResolveCallback, AnyCallback* aRejectCallback,
-              JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv)
-{
-  // Implements
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.then
-
-  // Step 1.
-  JS::Rooted<JS::Value> promiseVal(aCx, JS::ObjectValue(*GetWrapper()));
-  if (!MaybeWrapObjectValue(aCx, &promiseVal)) {
-    aRv.NoteJSContextException();
-    return;
-  }
-  JS::Rooted<JSObject*> promiseObj(aCx, &promiseVal.toObject());
-  MOZ_ASSERT(promiseObj);
-
-  // Step 2 was done by the bindings.
-
-  // Step 3.  We want to use aCalleeGlobal here because it will do the
-  // right thing for us via Xrays (where we won't find @@species on
-  // our promise constructor for now).
-  JS::Rooted<JSObject*> calleeGlobal(aCx, aCalleeGlobal);
-  JS::Rooted<JS::Value> defaultCtorVal(aCx);
-  { // Scope for JSAutoCompartment
-    JSAutoCompartment ac(aCx, aCalleeGlobal);
-    JSObject* defaultCtor =
-      PromiseBinding::GetConstructorObject(aCx, calleeGlobal);
-    if (!defaultCtor) {
-      aRv.NoteJSContextException();
-      return;
-    }
-    defaultCtorVal.setObject(*defaultCtor);
-  }
-  if (!MaybeWrapObjectValue(aCx, &defaultCtorVal)) {
-    aRv.NoteJSContextException();
-    return;
-  }
-
-  JS::Rooted<JS::Value> constructor(aCx);
-  SpeciesConstructor(aCx, promiseObj, defaultCtorVal, &constructor, aRv);
+  RefPtr<Promise> promise = Create(GetParentObject(), aRv);
   if (aRv.Failed()) {
-    // Step 4.
-    return;
+    return nullptr;
   }
 
-  // Step 5.
-  GlobalObject globalObj(aCx, GetWrapper());
-  if (globalObj.Failed()) {
-    aRv.NoteJSContextException();
-    return;
-  }
-  nsCOMPtr<nsIGlobalObject> globalObject =
-    do_QueryInterface(globalObj.GetAsSupports());
-  if (!globalObject) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return;
-  }
-  PromiseCapability capability(aCx);
-  NewPromiseCapability(aCx, globalObject, constructor, false, capability, aRv);
-  if (aRv.Failed()) {
-    // Step 6.
-    return;
-  }
+  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
 
-  // Now step 7: start
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromisethen
-
-  // Step 1 and step 2 are just assertions.
-
-  // Step 3 and step 4 are kinda handled for us already; we use null
-  // to represent "Identity" and "Thrower".
-
-  // Steps 5 and 6.  These branch based on whether we know we have a
-  // vanilla Promise or not.
-  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
-  if (capability.mNativePromise) {
-    Promise* promise = capability.mNativePromise;
-
-    RefPtr<PromiseCallback> resolveCb =
-      PromiseCallback::Factory(promise, global, aResolveCallback,
-                               PromiseCallback::Resolve);
-
-    RefPtr<PromiseCallback> rejectCb =
-      PromiseCallback::Factory(promise, global, aRejectCallback,
-                               PromiseCallback::Reject);
+  RefPtr<PromiseCallback> resolveCb =
+    PromiseCallback::Factory(promise, global, aResolveCallback,
+                             PromiseCallback::Resolve);
 
-    AppendCallbacks(resolveCb, rejectCb);
-  } else {
-    JS::Rooted<JSObject*> resolveObj(aCx, &capability.mResolve.toObject());
-    RefPtr<AnyCallback> resolveFunc =
-      new AnyCallback(aCx, resolveObj, GetIncumbentGlobal());
-
-    JS::Rooted<JSObject*> rejectObj(aCx, &capability.mReject.toObject());
-    RefPtr<AnyCallback> rejectFunc =
-      new AnyCallback(aCx, rejectObj, GetIncumbentGlobal());
-
-    if (!capability.mPromise.isObject()) {
-      aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
-      return;
-    }
-    JS::Rooted<JSObject*> newPromiseObj(aCx, &capability.mPromise.toObject());
-    // We want to store the reflector itself.
-    newPromiseObj = js::CheckedUnwrap(newPromiseObj);
-    if (!newPromiseObj) {
-      // Just throw something.
-      aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
-      return;
-    }
+  RefPtr<PromiseCallback> rejectCb =
+    PromiseCallback::Factory(promise, global, aRejectCallback,
+                             PromiseCallback::Reject);
 
-    RefPtr<PromiseCallback> resolveCb;
-    if (aResolveCallback) {
-      resolveCb = new WrapperPromiseCallback(global, aResolveCallback,
-                                             newPromiseObj,
-                                             resolveFunc, rejectFunc);
-    } else {
-      resolveCb = new InvokePromiseFuncCallback(global, newPromiseObj,
-                                                resolveFunc);
-    }
+  AppendCallbacks(resolveCb, rejectCb);
 
-    RefPtr<PromiseCallback> rejectCb;
-    if (aRejectCallback) {
-      rejectCb = new WrapperPromiseCallback(global, aRejectCallback,
-                                            newPromiseObj,
-                                            resolveFunc, rejectFunc);
-    } else {
-      rejectCb = new InvokePromiseFuncCallback(global, newPromiseObj,
-                                               rejectFunc);
-    }
-
-    AppendCallbacks(resolveCb, rejectCb);
-  }
-
-  aRetval.set(capability.PromiseValue());
+  return promise.forget();
 }
 
-void
-Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback,
-               JS::MutableHandle<JS::Value> aRetval,
-               ErrorResult& aRv)
+already_AddRefed<Promise>
+Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback, ErrorResult& aRv)
 {
-  // Implements
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.catch
-
-  // We can't call Promise::Then directly, because someone might have
-  // overridden Promise.prototype.then.
-  JS::Rooted<JS::Value> promiseVal(aCx, JS::ObjectValue(*GetWrapper()));
-  if (!MaybeWrapObjectValue(aCx, &promiseVal)) {
-    aRv.NoteJSContextException();
-    return;
-  }
-  JS::Rooted<JSObject*> promiseObj(aCx, &promiseVal.toObject());
-  MOZ_ASSERT(promiseObj);
-  JS::AutoValueArray<2> callbacks(aCx);
-  callbacks[0].setUndefined();
-  if (aRejectCallback) {
-    callbacks[1].setObject(*aRejectCallback->Callable());
-    // It could be in any compartment, so put it in ours.
-    if (!MaybeWrapObjectValue(aCx, callbacks[1])) {
-      aRv.NoteJSContextException();
-      return;
-    }
-  } else {
-    callbacks[1].setNull();
-  }
-  if (!JS_CallFunctionName(aCx, promiseObj, "then", callbacks, aRetval)) {
-    aRv.NoteJSContextException();
-  }
+  RefPtr<AnyCallback> resolveCb;
+  return Then(aCx, resolveCb, aRejectCallback, aRv);
 }
 
 /**
  * The CountdownHolder class encapsulates Promise.all countdown functions and
  * the countdown holder parts of the Promises spec. It maintains the result
  * array and AllResolveElementFunctions use SetValue() to set the array indices.
  */
 class CountdownHolder final : public nsISupports
@@ -1481,329 +949,34 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(AllResol
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AllResolveElementFunction)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AllResolveElementFunction)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION(AllResolveElementFunction, mCountdownHolder)
 
-static const JSClass PromiseAllDataHolderClass = {
-  "PromiseAllDataHolder", JSCLASS_HAS_RESERVED_SLOTS(3)
-};
-
-// Slot indices for objects of class PromiseAllDataHolderClass.
-#define DATA_HOLDER_REMAINING_ELEMENTS_SLOT 0
-#define DATA_HOLDER_VALUES_ARRAY_SLOT 1
-#define DATA_HOLDER_RESOLVE_FUNCTION_SLOT 2
-
-// Slot indices for PromiseAllResolveElement.
-// The RESOLVE_ELEMENT_INDEX_SLOT stores our index unless we've already been
-// called.  Then it stores INT32_MIN (which is never a valid index value).
-#define RESOLVE_ELEMENT_INDEX_SLOT 0
-// The RESOLVE_ELEMENT_DATA_HOLDER_SLOT slot stores an object of class
-// PromiseAllDataHolderClass.
-#define RESOLVE_ELEMENT_DATA_HOLDER_SLOT 1
-
-static bool
-PromiseAllResolveElement(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+/* static */ already_AddRefed<Promise>
+Promise::All(const GlobalObject& aGlobal,
+             const Sequence<JS::Value>& aIterable, ErrorResult& aRv)
 {
-  // Implements
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.all-resolve-element-functions
-  //
-  // See the big comment about compartments in Promise::All "Substep 4" that
-  // explains what compartments the various stuff here lives in.
-  JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
-
-  // Step 1.
-  int32_t index =
-    js::GetFunctionNativeReserved(&args.callee(),
-                                  RESOLVE_ELEMENT_INDEX_SLOT).toInt32();
-  // Step 2.
-  if (index == INT32_MIN) {
-    args.rval().setUndefined();
-    return true;
-  }
-
-  // Step 3.
-  js::SetFunctionNativeReserved(&args.callee(),
-                                RESOLVE_ELEMENT_INDEX_SLOT,
-                                JS::Int32Value(INT32_MIN));
-
-  // Step 4 already done.
-
-  // Step 5.
-  JS::Rooted<JSObject*> dataHolder(aCx,
-    &js::GetFunctionNativeReserved(&args.callee(),
-                                   RESOLVE_ELEMENT_DATA_HOLDER_SLOT).toObject());
-
-  JS::Rooted<JS::Value> values(aCx,
-    js::GetReservedSlot(dataHolder, DATA_HOLDER_VALUES_ARRAY_SLOT));
-
-  // Step 6, effectively.
-  JS::Rooted<JS::Value> resolveFunc(aCx,
-    js::GetReservedSlot(dataHolder, DATA_HOLDER_RESOLVE_FUNCTION_SLOT));
-
-  // Step 7.
-  int32_t remainingElements =
-    js::GetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32();
-
-  // Step 8.
-  JS::Rooted<JSObject*> valuesObj(aCx, &values.toObject());
-  if (!JS_DefineElement(aCx, valuesObj, index, args.get(0), JSPROP_ENUMERATE)) {
-    return false;
-  }
-
-  // Step 9.
-  remainingElements -= 1;
-  js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT,
-                      JS::Int32Value(remainingElements));
-
-  // Step 10.
-  if (remainingElements == 0) {
-    return JS::Call(aCx, JS::UndefinedHandleValue, resolveFunc,
-                    JS::HandleValueArray(values), args.rval());
-  }
-
-  // Step 11.
-  args.rval().setUndefined();
-  return true;
-}
-
-
-/* static */ void
-Promise::All(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
-             JS::Handle<JS::Value> aIterable,
-             JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv)
-{
-  // Implements http://www.ecma-international.org/ecma-262/6.0/#sec-promise.all
-  nsCOMPtr<nsIGlobalObject> global =
-    do_QueryInterface(aGlobal.GetAsSupports());
-  if (!global) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return;
-  }
-
   JSContext* cx = aGlobal.Context();
 
-  // Steps 1-5: nothing to do.  Note that the @@species bits got removed in
-  // https://github.com/tc39/ecma262/pull/211
-
-  // Step 6.
-  PromiseCapability capability(cx);
-  NewPromiseCapability(cx, global, aThisv, true, capability, aRv);
-  // Step 7.
-  if (aRv.Failed()) {
-    return;
-  }
-
-  MOZ_ASSERT(aThisv.isObject(), "How did NewPromiseCapability succeed?");
-  JS::Rooted<JSObject*> constructorObj(cx, &aThisv.toObject());
+  nsTArray<RefPtr<Promise>> promiseList;
 
-  // After this point we have a useful promise value in "capability", so just go
-  // ahead and put it in our retval now.  Every single return path below would
-  // want to do that anyway.
-  aRetval.set(capability.PromiseValue());
-  if (!MaybeWrapValue(cx, aRetval)) {
-    aRv.NoteJSContextException();
-    return;
-  }
+  for (uint32_t i = 0; i < aIterable.Length(); ++i) {
+    JS::Rooted<JS::Value> value(cx, aIterable.ElementAt(i));
+    RefPtr<Promise> nextPromise = Promise::Resolve(aGlobal, value, aRv);
 
-  // The arguments we're going to be passing to "then" on each loop iteration.
-  // The second one we know already; the first one will be created on each
-  // iteration of the loop.
-  JS::AutoValueArray<2> callbackFunctions(cx);
-  callbackFunctions[1].set(capability.mReject);
+    MOZ_ASSERT(!aRv.Failed());
 
-  // Steps 8 and 9.
-  JS::ForOfIterator iter(cx);
-  if (!iter.init(aIterable, JS::ForOfIterator::AllowNonIterable)) {
-    capability.RejectWithException(cx, aRv);
-    return;
-  }
-
-  if (!iter.valueIsIterable()) {
-    ThrowErrorMessage(cx, MSG_PROMISE_ARG_NOT_ITERABLE,
-                      "Argument of Promise.all");
-    capability.RejectWithException(cx, aRv);
-    return;
+    promiseList.AppendElement(Move(nextPromise));
   }
 
-  // Step 10 doesn't need to be done, because ForOfIterator handles it
-  // for us.
-
-  // Now we jump over to
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromiseall
-  // and do its steps.
-
-  // Substep 4. Create our data holder that holds all the things shared across
-  // every step of the iterator.  In particular, this holds the
-  // remainingElementsCount (as an integer reserved slot), the array of values,
-  // and the resolve function from our PromiseCapability.
-  //
-  // We have to be very careful about which compartments we create things in
-  // here.  In particular, we have to maintain the invariant that anything
-  // stored in a reserved slot is same-compartment with the object whose
-  // reserved slot it's in.  But we want to create the values array in the
-  // Promise reflector compartment, because that array can get exposed to code
-  // that has access to the Promise reflector (in particular code from that
-  // compartment), and that should work, even if the Promise reflector
-  // compartment is less-privileged than our caller compartment.
-  //
-  // So the plan is as follows: Create the values array in the promise reflector
-  // compartment.  Create the PromiseAllResolveElement function and the data
-  // holder in our current compartment.  Store a cross-compartment wrapper to
-  // the values array in the holder.  This should be OK because the only things
-  // we hand the PromiseAllResolveElement function to are the "then" calls we do
-  // and in the case when the reflector compartment is not the current
-  // compartment those are happening over Xrays anyway, which means they get the
-  // canonical "then" function and content can't see our
-  // PromiseAllResolveElement.
-  JS::Rooted<JSObject*> dataHolder(cx);
-  dataHolder = JS_NewObjectWithGivenProto(cx, &PromiseAllDataHolderClass,
-                                          nullptr);
-  if (!dataHolder) {
-    capability.RejectWithException(cx, aRv);
-    return;
-  }
-
-  JS::Rooted<JSObject*> reflectorGlobal(cx, global->GetGlobalJSObject());
-  JS::Rooted<JSObject*> valuesArray(cx);
-  { // Scope for JSAutoCompartment.
-    JSAutoCompartment ac(cx, reflectorGlobal);
-    valuesArray = JS_NewArrayObject(cx, 0);
-  }
-  if (!valuesArray) {
-    // It's important that we've exited the JSAutoCompartment by now, before
-    // calling RejectWithException and possibly invoking capability.mReject.
-    capability.RejectWithException(cx, aRv);
-    return;
-  }
-
-  // The values array as a value we can pass to a function in our current
-  // compartment, or store in the holder's reserved slot.
-  JS::Rooted<JS::Value> valuesArrayVal(cx, JS::ObjectValue(*valuesArray));
-  if (!MaybeWrapObjectValue(cx, &valuesArrayVal)) {
-    capability.RejectWithException(cx, aRv);
-    return;
-  }
-
-  js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT,
-                      JS::Int32Value(1));
-  js::SetReservedSlot(dataHolder, DATA_HOLDER_VALUES_ARRAY_SLOT,
-                      valuesArrayVal);
-  js::SetReservedSlot(dataHolder, DATA_HOLDER_RESOLVE_FUNCTION_SLOT,
-                      capability.mResolve);
-
-  // Substep 5.
-  CheckedInt32 index = 0;
-
-  // Substep 6.
-  JS::Rooted<JS::Value> nextValue(cx);
-  while (true) {
-    bool done;
-    // Steps a, b, c, e, f, g.
-    if (!iter.next(&nextValue, &done)) {
-      capability.RejectWithException(cx, aRv);
-      return;
-    }
-
-    // Step d.
-    if (done) {
-      int32_t remainingCount =
-        js::GetReservedSlot(dataHolder,
-                            DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32();
-      remainingCount -= 1;
-      if (remainingCount == 0) {
-        JS::Rooted<JS::Value> ignored(cx);
-        if (!JS::Call(cx, JS::UndefinedHandleValue, capability.mResolve,
-                      JS::HandleValueArray(valuesArrayVal), &ignored)) {
-          capability.RejectWithException(cx, aRv);
-        }
-        return;
-      }
-      js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT,
-                          JS::Int32Value(remainingCount));
-      // We're all set for now!
-      return;
-    }
-
-    // Step h.
-    { // Scope for the JSAutoCompartment we need to work with valuesArray.  We
-      // mostly do this for performance; we could go ahead and do the define via
-      // a cross-compartment proxy instead...
-      JSAutoCompartment ac(cx, valuesArray);
-      if (!JS_DefineElement(cx, valuesArray, index.value(),
-                            JS::UndefinedHandleValue, JSPROP_ENUMERATE)) {
-        // Have to go back into the caller compartment before we try to touch
-        // capability.mReject.  Luckily, capability.mReject is guaranteed to be
-        // an object in the right compartment here.
-        JSAutoCompartment ac2(cx, &capability.mReject.toObject());
-        capability.RejectWithException(cx, aRv);
-        return;
-      }
-    }
-
-    // Step i.  Sadly, we can't take a shortcut here even if
-    // capability.mNativePromise exists, because someone could have overridden
-    // "resolve" on the canonical Promise constructor.
-    JS::Rooted<JS::Value> nextPromise(cx);
-    if (!JS_CallFunctionName(cx, constructorObj, "resolve",
-                             JS::HandleValueArray(nextValue),
-                             &nextPromise)) {
-      // Step j.
-      capability.RejectWithException(cx, aRv);
-      return;
-    }
-
-    // Step k.
-    JS::Rooted<JSObject*> resolveElement(cx);
-    JSFunction* resolveFunc =
-      js::NewFunctionWithReserved(cx, PromiseAllResolveElement,
-                                  1 /* nargs */, 0 /* flags */, nullptr);
-    if (!resolveFunc) {
-      capability.RejectWithException(cx, aRv);
-      return;
-    }
-
-    resolveElement = JS_GetFunctionObject(resolveFunc);
-    // Steps l-p.
-    js::SetFunctionNativeReserved(resolveElement,
-                                  RESOLVE_ELEMENT_INDEX_SLOT,
-                                  JS::Int32Value(index.value()));
-    js::SetFunctionNativeReserved(resolveElement,
-                                  RESOLVE_ELEMENT_DATA_HOLDER_SLOT,
-                                  JS::ObjectValue(*dataHolder));
-
-    // Step q.
-    int32_t remainingElements =
-      js::GetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32();
-    js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT,
-                        JS::Int32Value(remainingElements + 1));
-
-    // Step r.  And now we don't know whether nextPromise has an overridden
-    // "then" method, so no shortcuts here either.
-    callbackFunctions[0].setObject(*resolveElement);
-    JS::Rooted<JSObject*> nextPromiseObj(cx);
-    JS::Rooted<JS::Value> ignored(cx);
-    if (!JS_ValueToObject(cx, nextPromise, &nextPromiseObj) ||
-        !JS_CallFunctionName(cx, nextPromiseObj, "then", callbackFunctions,
-                             &ignored)) {
-      // Step s.
-      capability.RejectWithException(cx, aRv);
-    }
-
-    // Step t.
-    index += 1;
-    if (!index.isValid()) {
-      // Let's just claim OOM.
-      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      capability.RejectWithException(cx, aRv);
-    }
-  }
+  return Promise::All(aGlobal, promiseList, aRv);
 }
 
 /* static */ already_AddRefed<Promise>
 Promise::All(const GlobalObject& aGlobal,
              const nsTArray<RefPtr<Promise>>& aPromiseList, ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(aGlobal.GetAsSupports());
@@ -1851,125 +1024,57 @@ Promise::All(const GlobalObject& aGlobal
     // Every promise gets its own resolve callback, which will set the right
     // index in the array to the resolution value.
     aPromiseList[i]->AppendCallbacks(resolveCb, rejectCb);
   }
 
   return promise.forget();
 }
 
-/* static */ void
-Promise::Race(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
-              JS::Handle<JS::Value> aIterable, JS::MutableHandle<JS::Value> aRetval,
-              ErrorResult& aRv)
+/* static */ already_AddRefed<Promise>
+Promise::Race(const GlobalObject& aGlobal,
+              const Sequence<JS::Value>& aIterable, ErrorResult& aRv)
 {
-  // Implements http://www.ecma-international.org/ecma-262/6.0/#sec-promise.race
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(aGlobal.GetAsSupports());
   if (!global) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
-    return;
+    return nullptr;
   }
 
   JSContext* cx = aGlobal.Context();
 
-  // Steps 1-5: nothing to do.  Note that the @@species bits got removed in
-  // https://github.com/tc39/ecma262/pull/211
-  PromiseCapability capability(cx);
-
-  // Step 6.
-  NewPromiseCapability(cx, global, aThisv, true, capability, aRv);
-  // Step 7.
-  if (aRv.Failed()) {
-    return;
+  JS::Rooted<JSObject*> obj(cx, JS::CurrentGlobalOrNull(cx));
+  if (!obj) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
   }
 
-  MOZ_ASSERT(aThisv.isObject(), "How did NewPromiseCapability succeed?");
-  JS::Rooted<JSObject*> constructorObj(cx, &aThisv.toObject());
-
-  // After this point we have a useful promise value in "capability", so just go
-  // ahead and put it in our retval now.  Every single return path below would
-  // want to do that anyway.
-  aRetval.set(capability.PromiseValue());
-  if (!MaybeWrapValue(cx, aRetval)) {
-    aRv.NoteJSContextException();
-    return;
-  }
-
-  // The arguments we're going to be passing to "then" on each loop iteration.
-  JS::AutoValueArray<2> callbackFunctions(cx);
-  callbackFunctions[0].set(capability.mResolve);
-  callbackFunctions[1].set(capability.mReject);
-
-  // Steps 8 and 9.
-  JS::ForOfIterator iter(cx);
-  if (!iter.init(aIterable, JS::ForOfIterator::AllowNonIterable)) {
-    capability.RejectWithException(cx, aRv);
-    return;
-  }
-
-  if (!iter.valueIsIterable()) {
-    ThrowErrorMessage(cx, MSG_PROMISE_ARG_NOT_ITERABLE,
-                      "Argument of Promise.race");
-    capability.RejectWithException(cx, aRv);
-    return;
+  RefPtr<Promise> promise = Create(global, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
   }
 
-  // Step 10 doesn't need to be done, because ForOfIterator handles it
-  // for us.
+  RefPtr<PromiseCallback> resolveCb =
+    new ResolvePromiseCallback(promise, obj);
 
-  // Now we jump over to
-  // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromiserace
-  // and do its steps.
-  JS::Rooted<JS::Value> nextValue(cx);
-  while (true) {
-    bool done;
-    // Steps a, b, c, e, f, g.
-    if (!iter.next(&nextValue, &done)) {
-      capability.RejectWithException(cx, aRv);
-      return;
-    }
-
-    // Step d.
-    if (done) {
-      // We're all set!
-      return;
-    }
+  RefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(promise, obj);
 
-    // Step h.  Sadly, we can't take a shortcut here even if
-    // capability.mNativePromise exists, because someone could have overridden
-    // "resolve" on the canonical Promise constructor.
-    JS::Rooted<JS::Value> nextPromise(cx);
-    if (!JS_CallFunctionName(cx, constructorObj, "resolve",
-                             JS::HandleValueArray(nextValue), &nextPromise)) {
-      // Step i.
-      capability.RejectWithException(cx, aRv);
-      return;
-    }
+  for (uint32_t i = 0; i < aIterable.Length(); ++i) {
+    JS::Rooted<JS::Value> value(cx, aIterable.ElementAt(i));
+    RefPtr<Promise> nextPromise = Promise::Resolve(aGlobal, value, aRv);
+    // According to spec, Resolve can throw, but our implementation never does.
+    // Well it does when window isn't passed on the main thread, but that is an
+    // implementation detail which should never be reached since we are checking
+    // for window above. Remove this when subclassing is supported.
+    MOZ_ASSERT(!aRv.Failed());
+    nextPromise->AppendCallbacks(resolveCb, rejectCb);
+  }
 
-    // Step j.  And now we don't know whether nextPromise has an overridden
-    // "then" method, so no shortcuts here either.
-    JS::Rooted<JSObject*> nextPromiseObj(cx);
-    JS::Rooted<JS::Value> ignored(cx);
-    if (!JS_ValueToObject(cx, nextPromise, &nextPromiseObj) ||
-        !JS_CallFunctionName(cx, nextPromiseObj, "then", callbackFunctions,
-                             &ignored)) {
-      // Step k.
-      capability.RejectWithException(cx, aRv);
-    }
-  }
-}
-
-/* static */
-bool
-Promise::PromiseSpecies(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-{
-  JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
-  args.rval().set(args.thisv());
-  return true;
+  return promise.forget();
 }
 
 void
 Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
 {
   RefPtr<PromiseCallback> resolveCb =
     new NativePromiseCallback(aRunnable, Resolved);
 
@@ -2172,35 +1277,45 @@ Promise::ResolveInternal(JSContext* aCx,
       HandleException(aCx);
       return;
     }
 
     if (then.isObject() && JS::IsCallable(&then.toObject())) {
       // This is the then() function of the thenable aValueObj.
       JS::Rooted<JSObject*> thenObj(aCx, &then.toObject());
 
-      // We used to have a fast path here for the case when the following
-      // requirements held:
+      // Add a fast path for the case when we're resolved with an actual
+      // Promise.  This has two requirements:
       //
       // 1) valueObj is a Promise.
       // 2) thenObj is a JSFunction backed by our actual Promise::Then
       //    implementation.
       //
-      // But now that we're doing subclassing in Promise.prototype.then we would
-      // also need the following requirements:
-      //
-      // 3) Getting valueObj.constructor has no side-effects.
-      // 4) Getting valueObj.constructor[@@species] has no side-effects.
-      // 5) valueObj.constructor[@@species] is a function and calling it has no
-      //    side-effects (e.g. it's the canonical Promise constructor) and it
-      //    provides some callback functions to call as arguments to its
-      //    argument.
-      //
-      // Ensuring that stuff while not inside SpiderMonkey is painful, so let's
-      // drop the fast path for now.
+      // If those requirements are satisfied, then we know exactly what
+      // thenObj.call(valueObj) will do, so we can optimize a bit and avoid ever
+      // entering JS for this stuff.
+      Promise* nextPromise;
+      if (PromiseBinding::IsThenMethod(thenObj) &&
+          NS_SUCCEEDED(UNWRAP_OBJECT(Promise, valueObj, nextPromise))) {
+        // If we were taking the codepath that involves PromiseResolveThenableJob and
+        // PromiseInit below, then eventually, in PromiseResolveThenableJob::Run, we
+        // would create some JSFunctions in the compartment of
+        // this->GetWrapper() and pass them to the PromiseInit. So by the time
+        // we'd see the resolution value it would be wrapped into the
+        // compartment of this->GetWrapper().  The global of that compartment is
+        // this->GetGlobalJSObject(), so use that as the global for
+        // ResolvePromiseCallback/RejectPromiseCallback.
+        JS::Rooted<JSObject*> glob(aCx, GlobalJSObject());
+        RefPtr<PromiseCallback> resolveCb = new ResolvePromiseCallback(this, glob);
+        RefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(this, glob);
+        RefPtr<FastPromiseResolveThenableJob> task =
+          new FastPromiseResolveThenableJob(resolveCb, rejectCb, nextPromise);
+        DispatchToMicroTask(task);
+        return;
+      }
 
       RefPtr<PromiseInit> thenCallback =
         new PromiseInit(nullptr, thenObj, mozilla::dom::GetIncumbentGlobal());
       RefPtr<PromiseResolveThenableJob> task =
         new PromiseResolveThenableJob(this, valueObj, thenCallback);
       DispatchToMicroTask(task);
       return;
     }
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -158,114 +158,81 @@ public:
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<Promise>
   Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
               ErrorResult& aRv, JS::Handle<JSObject*> aDesiredProto);
 
-  static void
-  Resolve(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
-          JS::Handle<JS::Value> aValue,
-          JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv);
+  static already_AddRefed<Promise>
+  Resolve(const GlobalObject& aGlobal,
+          JS::Handle<JS::Value> aValue, ErrorResult& aRv);
 
   static already_AddRefed<Promise>
   Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
           JS::Handle<JS::Value> aValue, ErrorResult& aRv);
 
-  static void
-  Reject(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
-         JS::Handle<JS::Value> aValue,
-         JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv);
+  static already_AddRefed<Promise>
+  Reject(const GlobalObject& aGlobal,
+         JS::Handle<JS::Value> aValue, ErrorResult& aRv);
 
   static already_AddRefed<Promise>
   Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
          JS::Handle<JS::Value> aValue, ErrorResult& aRv);
 
-  void
-  Then(JSContext* aCx,
-       // aCalleeGlobal may not be in the compartment of aCx, when called over
-       // Xrays.
-       JS::Handle<JSObject*> aCalleeGlobal,
-       AnyCallback* aResolveCallback, AnyCallback* aRejectCallback,
-       JS::MutableHandle<JS::Value> aRetval,
-       ErrorResult& aRv);
+  already_AddRefed<Promise>
+  Then(JSContext* aCx, AnyCallback* aResolveCallback,
+       AnyCallback* aRejectCallback, ErrorResult& aRv);
 
-  void
-  Catch(JSContext* aCx,
-        AnyCallback* aRejectCallback,
-        JS::MutableHandle<JS::Value> aRetval,
-        ErrorResult& aRv);
+  already_AddRefed<Promise>
+  Catch(JSContext* aCx, AnyCallback* aRejectCallback, ErrorResult& aRv);
 
-  static void
-  All(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
-      JS::Handle<JS::Value> aIterable, JS::MutableHandle<JS::Value> aRetval,
-      ErrorResult& aRv);
+  static already_AddRefed<Promise>
+  All(const GlobalObject& aGlobal,
+      const Sequence<JS::Value>& aIterable, ErrorResult& aRv);
 
   static already_AddRefed<Promise>
   All(const GlobalObject& aGlobal,
       const nsTArray<RefPtr<Promise>>& aPromiseList, ErrorResult& aRv);
 
-  static void
-  Race(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv,
-       JS::Handle<JS::Value> aIterable, JS::MutableHandle<JS::Value> aRetval,
-       ErrorResult& aRv);
-
-  static bool
-  PromiseSpecies(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
+  static already_AddRefed<Promise>
+  Race(const GlobalObject& aGlobal,
+       const Sequence<JS::Value>& aIterable, ErrorResult& aRv);
 
   void AppendNativeHandler(PromiseNativeHandler* aRunnable);
 
   JSObject* GlobalJSObject() const;
 
   JSCompartment* Compartment() const;
 
   // Return a unique-to-the-process identifier for this Promise.
   uint64_t GetID();
 
   // Queue an async microtask to current main or worker thread.
   static void
   DispatchToMicroTask(nsIRunnable* aRunnable);
 
-  enum JSCallbackSlots {
-    SLOT_PROMISE = 0,
-    SLOT_DATA
-  };
-
 protected:
-  struct PromiseCapability;
-
   // Do NOT call this unless you're Promise::Create.  I wish we could enforce
   // that from inside this class too, somehow.
   explicit Promise(nsIGlobalObject* aGlobal);
 
   virtual ~Promise();
 
   // Do JS-wrapping after Promise creation.  Passing null for aDesiredProto will
   // use the default prototype for the sort of Promise we have.
   void CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv);
 
   // Create the JS resolving functions of resolve() and reject(). And provide
   // references to the two functions by calling PromiseInit passed from Promise
   // constructor.
   void CallInitFunction(const GlobalObject& aGlobal, PromiseInit& aInit,
                         ErrorResult& aRv);
 
-  // The NewPromiseCapability function from
-  // <http://www.ecma-international.org/ecma-262/6.0/#sec-newpromisecapability>.
-  // Errors are communicated via aRv.  If aForceCallbackCreation is
-  // true, then this function will ensure that aCapability has a
-  // useful mResolve/mReject even if mNativePromise is non-null.
-  static void NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal,
-                                   JS::Handle<JS::Value> aConstructor,
-                                   bool aForceCallbackCreation,
-                                   PromiseCapability& aCapability,
-                                   ErrorResult& aRv);
-
   bool IsPending()
   {
     return mResolvePending;
   }
 
   void GetDependentPromises(nsTArray<RefPtr<Promise>>& aPromises);
 
   bool IsLastInChain() const
--- a/dom/promise/PromiseCallback.cpp
+++ b/dom/promise/PromiseCallback.cpp
@@ -4,18 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PromiseCallback.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 
 #include "jsapi.h"
-#include "jsfriendapi.h"
-#include "jswrapper.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseCallback)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseCallback)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseCallback)
@@ -151,123 +149,35 @@ RejectPromiseCallback::Call(JSContext* a
     return NS_ERROR_FAILURE;
   }
 
 
   mPromise->RejectInternal(aCx, value);
   return NS_OK;
 }
 
-// InvokePromiseFuncCallback
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(InvokePromiseFuncCallback)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(InvokePromiseFuncCallback,
-                                                PromiseCallback)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromiseFunc)
-  tmp->mGlobal = nullptr;
-  tmp->mNextPromiseObj = nullptr;
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(InvokePromiseFuncCallback,
-                                                  PromiseCallback)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromiseFunc)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(InvokePromiseFuncCallback)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNextPromiseObj)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(InvokePromiseFuncCallback)
-NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
-
-NS_IMPL_ADDREF_INHERITED(InvokePromiseFuncCallback, PromiseCallback)
-NS_IMPL_RELEASE_INHERITED(InvokePromiseFuncCallback, PromiseCallback)
-
-InvokePromiseFuncCallback::InvokePromiseFuncCallback(JS::Handle<JSObject*> aGlobal,
-                                                     JS::Handle<JSObject*> aNextPromiseObj,
-                                                     AnyCallback* aPromiseFunc)
-  : mGlobal(aGlobal)
-  , mNextPromiseObj(aNextPromiseObj)
-  , mPromiseFunc(aPromiseFunc)
-{
-  MOZ_ASSERT(aGlobal);
-  MOZ_ASSERT(aNextPromiseObj);
-  MOZ_ASSERT(aPromiseFunc);
-  HoldJSObjects(this);
-}
-
-InvokePromiseFuncCallback::~InvokePromiseFuncCallback()
-{
-  DropJSObjects(this);
-}
-
-nsresult
-InvokePromiseFuncCallback::Call(JSContext* aCx,
-                                JS::Handle<JS::Value> aValue)
-{
-  // Run resolver's algorithm with value and the synchronous flag set.
-
-  JS::ExposeObjectToActiveJS(mGlobal);
-  JS::ExposeValueToActiveJS(aValue);
-
-  JSAutoCompartment ac(aCx, mGlobal);
-  JS::Rooted<JS::Value> value(aCx, aValue);
-  if (!JS_WrapValue(aCx, &value)) {
-    NS_WARNING("Failed to wrap value into the right compartment.");
-    return NS_ERROR_FAILURE;
-  }
-
-  ErrorResult rv;
-  JS::Rooted<JS::Value> ignored(aCx);
-  mPromiseFunc->Call(value, &ignored, rv);
-  // Useful exceptions already got reported.
-  rv.SuppressException();
-  return NS_OK;
-}
-
-Promise*
-InvokePromiseFuncCallback::GetDependentPromise()
-{
-  Promise* promise;
-  if (NS_SUCCEEDED(UNWRAP_OBJECT(Promise, mNextPromiseObj, promise))) {
-    return promise;
-  }
-
-  // Oh, well.
-  return nullptr;
-}
-
 // WrapperPromiseCallback
 NS_IMPL_CYCLE_COLLECTION_CLASS(WrapperPromiseCallback)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WrapperPromiseCallback,
                                                 PromiseCallback)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextPromise)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveFunc)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectFunc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
   tmp->mGlobal = nullptr;
-  tmp->mNextPromiseObj = nullptr;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WrapperPromiseCallback,
                                                   PromiseCallback)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextPromise)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveFunc)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectFunc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WrapperPromiseCallback)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNextPromiseObj)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WrapperPromiseCallback)
 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
 
 NS_IMPL_ADDREF_INHERITED(WrapperPromiseCallback, PromiseCallback)
 NS_IMPL_RELEASE_INHERITED(WrapperPromiseCallback, PromiseCallback)
 
@@ -278,34 +188,16 @@ WrapperPromiseCallback::WrapperPromiseCa
   , mGlobal(aGlobal)
   , mCallback(aCallback)
 {
   MOZ_ASSERT(aNextPromise);
   MOZ_ASSERT(aGlobal);
   HoldJSObjects(this);
 }
 
-WrapperPromiseCallback::WrapperPromiseCallback(JS::Handle<JSObject*> aGlobal,
-                                               AnyCallback* aCallback,
-                                               JS::Handle<JSObject*> aNextPromiseObj,
-                                               AnyCallback* aResolveFunc,
-                                               AnyCallback* aRejectFunc)
-  : mNextPromiseObj(aNextPromiseObj)
-  , mResolveFunc(aResolveFunc)
-  , mRejectFunc(aRejectFunc)
-  , mGlobal(aGlobal)
-  , mCallback(aCallback)
-{
-  MOZ_ASSERT(mNextPromiseObj);
-  MOZ_ASSERT(aResolveFunc);
-  MOZ_ASSERT(aRejectFunc);
-  MOZ_ASSERT(aGlobal);
-  HoldJSObjects(this);
-}
-
 WrapperPromiseCallback::~WrapperPromiseCallback()
 {
   DropJSObjects(this);
 }
 
 nsresult
 WrapperPromiseCallback::Call(JSContext* aCx,
                              JS::Handle<JS::Value> aValue)
@@ -319,73 +211,46 @@ WrapperPromiseCallback::Call(JSContext* 
     NS_WARNING("Failed to wrap value into the right compartment.");
     return NS_ERROR_FAILURE;
   }
 
   ErrorResult rv;
 
   // PromiseReactionTask step 6
   JS::Rooted<JS::Value> retValue(aCx);
-  JSCompartment* compartment;
-  if (mNextPromise) {
-    compartment = mNextPromise->Compartment();
-  } else {
-    MOZ_ASSERT(mNextPromiseObj);
-    compartment = js::GetObjectCompartment(mNextPromiseObj);
-  }
   mCallback->Call(value, &retValue, rv, "promise callback",
                   CallbackObject::eRethrowExceptions,
-                  compartment);
+                  mNextPromise->Compartment());
 
   rv.WouldReportJSException();
 
   // PromiseReactionTask step 7
   if (rv.Failed()) {
     JS::Rooted<JS::Value> value(aCx);
     { // Scope for JSAutoCompartment
       // Convert the ErrorResult to a JS exception object that we can reject
       // ourselves with.  This will be exactly the exception that would get
       // thrown from a binding method whose ErrorResult ended up with whatever
-      // is on "rv" right now.  Do this in the promise reflector compartment.
-      Maybe<JSAutoCompartment> ac;
-      if (mNextPromise) {
-        ac.emplace(aCx, mNextPromise->GlobalJSObject());
-      } else {
-        ac.emplace(aCx, mNextPromiseObj);
-      }
+      // is on "rv" right now.
+      JSAutoCompartment ac(aCx, mNextPromise->GlobalJSObject());
       DebugOnly<bool> conversionResult = ToJSValue(aCx, rv, &value);
       MOZ_ASSERT(conversionResult);
     }
 
-    if (mNextPromise) {
-      mNextPromise->RejectInternal(aCx, value);
-    } else {
-      JS::Rooted<JS::Value> ignored(aCx);
-      ErrorResult rejectRv;
-      mRejectFunc->Call(value, &ignored, rejectRv);
-      // This reported any JS exceptions; we just have a pointless exception on
-      // there now.
-      rejectRv.SuppressException();
-    }
+    mNextPromise->RejectInternal(aCx, value);
     return NS_OK;
   }
 
   // If the return value is the same as the promise itself, throw TypeError.
   if (retValue.isObject()) {
     JS::Rooted<JSObject*> valueObj(aCx, &retValue.toObject());
-    valueObj = js::CheckedUnwrap(valueObj);
-    JS::Rooted<JSObject*> nextPromiseObj(aCx);
-    if (mNextPromise) {
-      nextPromiseObj = mNextPromise->GetWrapper();
-    } else {
-      MOZ_ASSERT(mNextPromiseObj);
-      nextPromiseObj = mNextPromiseObj;
-    }
-    // XXXbz shouldn't this check be over in ResolveInternal anyway?
-    if (valueObj == nextPromiseObj) {
+    Promise* returnedPromise;
+    nsresult r = UNWRAP_OBJECT(Promise, valueObj, returnedPromise);
+
+    if (NS_SUCCEEDED(r) && returnedPromise == mNextPromise) {
       const char* fileName = nullptr;
       uint32_t lineNumber = 0;
 
       // Try to get some information about the callback to report a sane error,
       // but don't try too hard (only deals with scripted functions).
       JS::Rooted<JSObject*> unwrapped(aCx,
         js::CheckedUnwrap(mCallback->Callback()));
 
@@ -424,86 +289,31 @@ WrapperPromiseCallback::Call(JSContext* 
       JS::Rooted<JS::Value> typeError(aCx);
       if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, fn, lineNumber, 0,
                            nullptr, message, &typeError)) {
         // Out of memory. Promise will stay unresolved.
         JS_ClearPendingException(aCx);
         return NS_ERROR_OUT_OF_MEMORY;
       }
 
-      if (mNextPromise) {
-        mNextPromise->RejectInternal(aCx, typeError);
-      } else {
-        JS::Rooted<JS::Value> ignored(aCx);
-        ErrorResult rejectRv;
-        mRejectFunc->Call(typeError, &ignored, rejectRv);
-        // This reported any JS exceptions; we just have a pointless exception
-        // on there now.
-        rejectRv.SuppressException();
-      }
+      mNextPromise->RejectInternal(aCx, typeError);
       return NS_OK;
     }
   }
 
   // Otherwise, run resolver's resolve with value.
   if (!JS_WrapValue(aCx, &retValue)) {
     NS_WARNING("Failed to wrap value into the right compartment.");
     return NS_ERROR_FAILURE;
   }
 
-  if (mNextPromise) {
-    mNextPromise->ResolveInternal(aCx, retValue);
-  } else {
-    JS::Rooted<JS::Value> ignored(aCx);
-    ErrorResult resolveRv;
-    mResolveFunc->Call(retValue, &ignored, resolveRv);
-    // This reported any JS exceptions; we just have a pointless exception
-    // on there now.
-    resolveRv.SuppressException();
-  }
-    
+  mNextPromise->ResolveInternal(aCx, retValue);
   return NS_OK;
 }
 
-Promise*
-WrapperPromiseCallback::GetDependentPromise()
-{
-  // Per spec, various algorithms like all() and race() are actually implemented
-  // in terms of calling then() but passing it the resolve/reject functions that
-  // are passed as arguments to function passed to the Promise constructor.
-  // That will cause the promise in question to hold on to a
-  // WrapperPromiseCallback, but the dependent promise should really be the one
-  // whose constructor those functions came from, not the about-to-be-ignored
-  // return value of "then".  So try to determine whether we're in that case and
-  // if so go ahead and dig the dependent promise out of the function we have.
-  JSObject* callable = mCallback->Callable();
-  // Unwrap it, in case it's a cross-compartment wrapper.  Our caller here is
-  // system, so it's really ok to just go and unwrap.
-  callable = js::UncheckedUnwrap(callable);
-  if (JS_IsNativeFunction(callable, Promise::JSCallback)) {
-    JS::Value promiseVal =
-      js::GetFunctionNativeReserved(callable, Promise::SLOT_PROMISE);
-    Promise* promise;
-    UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise);
-    return promise;
-  }
-
-  if (mNextPromise) {
-    return mNextPromise;
-  }
-
-  Promise* promise;
-  if (NS_SUCCEEDED(UNWRAP_OBJECT(Promise, mNextPromiseObj, promise))) {
-    return promise;
-  }
-
-  // Oh, well.
-  return nullptr;
-}
-
 // NativePromiseCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(NativePromiseCallback,
                                    PromiseCallback, mHandler)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(NativePromiseCallback)
 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
 
--- a/dom/promise/PromiseCallback.h
+++ b/dom/promise/PromiseCallback.h
@@ -40,57 +40,40 @@ public:
 
   // This factory returns a PromiseCallback object with refcount of 0.
   static PromiseCallback*
   Factory(Promise* aNextPromise, JS::Handle<JSObject*> aObject,
           AnyCallback* aCallback, Task aTask);
 };
 
 // WrapperPromiseCallback execs a JS Callback with a value, and then the return
-// value is sent to either:
-// a) If aNextPromise is non-null, the aNextPromise->ResolveFunction() or to
-//    aNextPromise->RejectFunction() if the JS Callback throws.
-// or
-// b) If aNextPromise is null, in which case aResolveFunc and aRejectFunc must
-//    be non-null, then to aResolveFunc, unless aCallback threw, in which case
-//    aRejectFunc.
+// value is sent to the aNextPromise->ResolveFunction() or to
+// aNextPromise->RejectFunction() if the JS Callback throws.
 class WrapperPromiseCallback final : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WrapperPromiseCallback,
                                                          PromiseCallback)
 
   nsresult Call(JSContext* aCx,
                 JS::Handle<JS::Value> aValue) override;
 
-  Promise* GetDependentPromise() override;
+  Promise* GetDependentPromise() override
+  {
+    return mNextPromise;
+  }
 
-  // Constructor for when we know we have a vanilla Promise.
   WrapperPromiseCallback(Promise* aNextPromise, JS::Handle<JSObject*> aGlobal,
                          AnyCallback* aCallback);
 
-  // Constructor for when all we have to work with are resolve/reject functions.
-  WrapperPromiseCallback(JS::Handle<JSObject*> aGlobal,
-                         AnyCallback* aCallback,
-                         JS::Handle<JSObject*> mNextPromiseObj,
-                         AnyCallback* aResolveFunc,
-                         AnyCallback* aRejectFunc);
-
 private:
   ~WrapperPromiseCallback();
 
-  // Either mNextPromise is non-null or all three of mNextPromiseObj,
-  // mResolveFund and mRejectFunc must are non-null.
   RefPtr<Promise> mNextPromise;
-  // mNextPromiseObj is the reflector itself; it may not be in the
-  // same compartment as anything else we have.
-  JS::Heap<JSObject*> mNextPromiseObj;
-  RefPtr<AnyCallback> mResolveFunc;
-  RefPtr<AnyCallback> mRejectFunc;
   JS::Heap<JSObject*> mGlobal;
   RefPtr<AnyCallback> mCallback;
 };
 
 // ResolvePromiseCallback calls aPromise->ResolveFunction() with the value
 // received by Call().
 class ResolvePromiseCallback final : public PromiseCallback
 {
@@ -137,42 +120,16 @@ public:
 
 private:
   ~RejectPromiseCallback();
 
   RefPtr<Promise> mPromise;
   JS::Heap<JSObject*> mGlobal;
 };
 
-// InvokePromiseFuncCallback calls the given function with the value
-// received by Call().
-class InvokePromiseFuncCallback final : public PromiseCallback
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(InvokePromiseFuncCallback,
-                                                         PromiseCallback)
-
-  nsresult Call(JSContext* aCx,
-                JS::Handle<JS::Value> aValue) override;
-
-  Promise* GetDependentPromise() override;
-
-  InvokePromiseFuncCallback(JS::Handle<JSObject*> aGlobal,
-                            JS::Handle<JSObject*> aNextPromiseObj,
-                            AnyCallback* aPromiseFunc);
-
-private:
-  ~InvokePromiseFuncCallback();
-
-  JS::Heap<JSObject*> mGlobal;
-  JS::Heap<JSObject*> mNextPromiseObj;
-  RefPtr<AnyCallback> mPromiseFunc;
-};
-
 // NativePromiseCallback wraps a PromiseNativeHandler.
 class NativePromiseCallback final : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(NativePromiseCallback,
                                            PromiseCallback)
 
--- a/dom/promise/PromiseDebugging.cpp
+++ b/dom/promise/PromiseDebugging.cpp
@@ -9,17 +9,16 @@
 
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/TimeStamp.h"
 
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/PromiseDebugging.h"
 #include "mozilla/dom/PromiseDebuggingBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 class FlushRejections: public nsCancelableRunnable
 {
@@ -61,49 +60,33 @@ private:
   // `true` if an instance of `FlushRejections` is currently dispatched
   // and has not been executed yet.
   static ThreadLocal<bool> sDispatched;
 };
 
 /* static */ ThreadLocal<bool>
 FlushRejections::sDispatched;
 
-static Promise*
-UnwrapPromise(JS::Handle<JSObject*> aPromise, ErrorResult& aRv)
+/* static */ void
+PromiseDebugging::GetState(GlobalObject&, Promise& aPromise,
+                           PromiseDebuggingStateHolder& aState)
 {
-  Promise* promise;
-  if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Promise, aPromise, promise)))) {
-    aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING("Argument"));
-    return nullptr;
-  }
-  return promise;
-}
-
-/* static */ void
-PromiseDebugging::GetState(GlobalObject&, JS::Handle<JSObject*> aPromise,
-                           PromiseDebuggingStateHolder& aState,
-                           ErrorResult& aRv)
-{
-  Promise* promise = UnwrapPromise(aPromise, aRv);
-  if (aRv.Failed()) {
-    return;
-  }
-  switch (promise->mState) {
+  switch (aPromise.mState) {
   case Promise::Pending:
     aState.mState = PromiseDebuggingState::Pending;
     break;
   case Promise::Resolved:
     aState.mState = PromiseDebuggingState::Fulfilled;
-    JS::ExposeValueToActiveJS(promise->mResult);
-    aState.mValue = promise->mResult;
+    JS::ExposeValueToActiveJS(aPromise.mResult);
+    aState.mValue = aPromise.mResult;
     break;
   case Promise::Rejected:
     aState.mState = PromiseDebuggingState::Rejected;
-    JS::ExposeValueToActiveJS(promise->mResult);
-    aState.mReason = promise->mResult;
+    JS::ExposeValueToActiveJS(aPromise.mResult);
+    aState.mReason = aPromise.mResult;
     break;
   }
 }
 
 /*static */ nsString
 PromiseDebugging::sIDPrefix;
 
 /* static */ void
@@ -130,89 +113,59 @@ PromiseDebugging::Shutdown()
 /* static */ void
 PromiseDebugging::FlushUncaughtRejections()
 {
   MOZ_ASSERT(!NS_IsMainThread());
   FlushRejections::FlushSync();
 }
 
 /* static */ void
-PromiseDebugging::GetAllocationStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
-                                     JS::MutableHandle<JSObject*> aStack,
-                                     ErrorResult& aRv)
+PromiseDebugging::GetAllocationStack(GlobalObject&, Promise& aPromise,
+                                     JS::MutableHandle<JSObject*> aStack)
 {
-  Promise* promise = UnwrapPromise(aPromise, aRv);
-  if (aRv.Failed()) {
-    return;
-  }
-  aStack.set(promise->mAllocationStack);
+  aStack.set(aPromise.mAllocationStack);
 }
 
 /* static */ void
-PromiseDebugging::GetRejectionStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
-                                    JS::MutableHandle<JSObject*> aStack,
-                                    ErrorResult& aRv)
+PromiseDebugging::GetRejectionStack(GlobalObject&, Promise& aPromise,
+                                    JS::MutableHandle<JSObject*> aStack)
 {
-  Promise* promise = UnwrapPromise(aPromise, aRv);
-  if (aRv.Failed()) {
-    return;
-  }
-  aStack.set(promise->mRejectionStack);
+  aStack.set(aPromise.mRejectionStack);
 }
 
 /* static */ void
-PromiseDebugging::GetFullfillmentStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
-                                       JS::MutableHandle<JSObject*> aStack,
-                                       ErrorResult& aRv)
+PromiseDebugging::GetFullfillmentStack(GlobalObject&, Promise& aPromise,
+                                       JS::MutableHandle<JSObject*> aStack)
 {
-  Promise* promise = UnwrapPromise(aPromise, aRv);
-  if (aRv.Failed()) {
-    return;
-  }
-  aStack.set(promise->mFullfillmentStack);
+  aStack.set(aPromise.mFullfillmentStack);
 }
 
 /* static */ void
-PromiseDebugging::GetDependentPromises(GlobalObject&, JS::Handle<JSObject*> aPromise,
-                                       nsTArray<RefPtr<Promise>>& aPromises,
-                                       ErrorResult& aRv)
+PromiseDebugging::GetDependentPromises(GlobalObject&, Promise& aPromise,
+                                       nsTArray<RefPtr<Promise>>& aPromises)
 {
-  Promise* promise = UnwrapPromise(aPromise, aRv);
-  if (aRv.Failed()) {
-    return;
-  }
-  promise->GetDependentPromises(aPromises);
+  aPromise.GetDependentPromises(aPromises);
 }
 
 /* static */ double
-PromiseDebugging::GetPromiseLifetime(GlobalObject&,
-                                     JS::Handle<JSObject*> aPromise,
-                                     ErrorResult& aRv)
+PromiseDebugging::GetPromiseLifetime(GlobalObject&, Promise& aPromise)
 {
-  Promise* promise = UnwrapPromise(aPromise, aRv);
-  if (aRv.Failed()) {
-    return 0;
-  }
-  return (TimeStamp::Now() - promise->mCreationTimestamp).ToMilliseconds();
+  return (TimeStamp::Now() - aPromise.mCreationTimestamp).ToMilliseconds();
 }
 
 /* static */ double
-PromiseDebugging::GetTimeToSettle(GlobalObject&, JS::Handle<JSObject*> aPromise,
+PromiseDebugging::GetTimeToSettle(GlobalObject&, Promise& aPromise,
                                   ErrorResult& aRv)
 {
-  Promise* promise = UnwrapPromise(aPromise, aRv);
-  if (aRv.Failed()) {
-    return 0;
-  }
-  if (promise->mState == Promise::Pending) {
+  if (aPromise.mState == Promise::Pending) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return 0;
   }
-  return (promise->mSettlementTimestamp -
-          promise->mCreationTimestamp).ToMilliseconds();
+  return (aPromise.mSettlementTimestamp -
+          aPromise.mCreationTimestamp).ToMilliseconds();
 }
 
 /* static */ void
 PromiseDebugging::AddUncaughtRejectionObserver(GlobalObject&,
                                                UncaughtRejectionObserver& aObserver)
 {
   CycleCollectedJSRuntime* storage = CycleCollectedJSRuntime::Get();
   nsTArray<nsCOMPtr<nsISupports>>& observers = storage->mUncaughtRejectionObservers;
@@ -246,25 +199,20 @@ PromiseDebugging::AddUncaughtRejection(P
 PromiseDebugging::AddConsumedRejection(Promise& aPromise)
 {
   CycleCollectedJSRuntime::Get()->mConsumedRejections.AppendElement(&aPromise);
   FlushRejections::DispatchNeeded();
 }
 
 /* static */ void
 PromiseDebugging::GetPromiseID(GlobalObject&,
-                               JS::Handle<JSObject*> aPromise,
-                               nsString& aID,
-                               ErrorResult& aRv)
+                               Promise& aPromise,
+                               nsString& aID)
 {
-  Promise* promise = UnwrapPromise(aPromise, aRv);
-  if (aRv.Failed()) {
-    return;
-  }
-  uint64_t promiseID = promise->GetID();
+  uint64_t promiseID = aPromise.GetID();
   aID = sIDPrefix;
   aID.AppendInt(promiseID);
 }
 
 /* static */ void
 PromiseDebugging::FlushUncaughtRejectionsInternal()
 {
   CycleCollectedJSRuntime* storage = CycleCollectedJSRuntime::Get();
--- a/dom/promise/PromiseDebugging.h
+++ b/dom/promise/PromiseDebugging.h
@@ -27,42 +27,32 @@ class UncaughtRejectionObserver;
 class FlushRejections;
 
 class PromiseDebugging
 {
 public:
   static void Init();
   static void Shutdown();
 
-  static void GetState(GlobalObject&, JS::Handle<JSObject*> aPromise,
-                       PromiseDebuggingStateHolder& aState,
-                       ErrorResult& aRv);
+  static void GetState(GlobalObject&, Promise& aPromise,
+                       PromiseDebuggingStateHolder& aState);
 
-  static void GetAllocationStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
-                                 JS::MutableHandle<JSObject*> aStack,
-                                 ErrorResult& aRv);
-  static void GetRejectionStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
-                                JS::MutableHandle<JSObject*> aStack,
-                                ErrorResult& aRv);
-  static void GetFullfillmentStack(GlobalObject&,
-                                   JS::Handle<JSObject*> aPromise,
-                                   JS::MutableHandle<JSObject*> aStack,
-                                   ErrorResult& aRv);
-  static void GetDependentPromises(GlobalObject&,
-                                   JS::Handle<JSObject*> aPromise,
-                                   nsTArray<RefPtr<Promise>>& aPromises,
-                                   ErrorResult& aRv);
-  static double GetPromiseLifetime(GlobalObject&,
-                                   JS::Handle<JSObject*> aPromise,
-                                   ErrorResult& aRv);
-  static double GetTimeToSettle(GlobalObject&, JS::Handle<JSObject*> aPromise,
+  static void GetAllocationStack(GlobalObject&, Promise& aPromise,
+                                 JS::MutableHandle<JSObject*> aStack);
+  static void GetRejectionStack(GlobalObject&, Promise& aPromise,
+                                JS::MutableHandle<JSObject*> aStack);
+  static void GetFullfillmentStack(GlobalObject&, Promise& aPromise,
+                                   JS::MutableHandle<JSObject*> aStack);
+  static void GetDependentPromises(GlobalObject&, Promise& aPromise,
+                                   nsTArray<RefPtr<Promise>>& aPromises);
+  static double GetPromiseLifetime(GlobalObject&, Promise& aPromise);
+  static double GetTimeToSettle(GlobalObject&, Promise& aPromise,
                                 ErrorResult& aRv);
 
-  static void GetPromiseID(GlobalObject&, JS::Handle<JSObject*>, nsString&,
-                           ErrorResult&);
+  static void GetPromiseID(GlobalObject&, Promise&, nsString&);
 
   // Mechanism for watching uncaught instances of Promise.
   static void AddUncaughtRejectionObserver(GlobalObject&,
                                            UncaughtRejectionObserver& aObserver);
   static bool RemoveUncaughtRejectionObserver(GlobalObject&,
                                               UncaughtRejectionObserver& aObserver);
 
   // Mark a Promise as having been left uncaught at script completion.
--- a/dom/promise/tests/chrome.ini
+++ b/dom/promise/tests/chrome.ini
@@ -1,8 +1,7 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g'
 
 [test_dependentPromises.html]
 [test_on_new_promise.html]
 [test_on_promise_settled.html]
 [test_on_promise_settled_duplicates.html]
-[test_promise_xrays.html]
deleted file mode 100644
--- a/dom/promise/tests/file_promise_xrays.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <script>
-    function vendGetter(name) {
-      return function() { throw "Getting " + String(name) };
-    }
-    function vendSetter(name) {
-      return function() { throw "Setting " + String(name) };
-    }
-    var setupThrew = false;
-    try {
-      // Neuter everything we can think of on Promise.
-      for (var obj of [Promise, Promise.prototype]) {
-        propNames = Object.getOwnPropertyNames(obj);
-        propNames = propNames.concat(Object.getOwnPropertySymbols(obj));
-        for (var propName of propNames) {
-          if (propName == "prototype" && obj == Promise) {
-            // It's not configurable
-            continue;
-          }
-          Object.defineProperty(obj, propName,
-            { get: vendGetter(propName), set: vendSetter(propName) });
-        }
-      }
-    } catch (e) {
-      // Something went wrong.  Save that info so the test can check for it.
-      setupThrew = e;
-    }
-  </script>
-</html>
--- a/dom/promise/tests/mochitest.ini
+++ b/dom/promise/tests/mochitest.ini
@@ -1,17 +1,13 @@
 [DEFAULT]
-# Support files for chrome tests that we want to load over HTTP need
-# to go in here, not chrome.ini, apparently.
-support-files = file_promise_xrays.html
 
 [test_abortable_promise.html]
 [test_bug883683.html]
 [test_promise.html]
 [test_promise_utils.html]
 [test_resolve.html]
 [test_resolver_return_value.html]
 [test_thenable_vs_promise_ordering.html]
 [test_promise_and_timeout_ordering.html]
 support-files = file_promise_and_timeout_ordering.js
 [test_promise_and_timeout_ordering_workers.html]
 support-files = file_promise_and_timeout_ordering.js
-[test_species_getter.html]
--- a/dom/promise/tests/test_bug883683.html
+++ b/dom/promise/tests/test_bug883683.html
@@ -12,20 +12,20 @@
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript"><!--
 
 function runTest() {
-  [{}, {}, {}, {}, {}].reduce(Promise.reject.bind(Promise));
+  [{}, {}, {}, {}, {}].reduce(Promise.reject);
   ok(true, "No leaks with reject?");
 
-  [{}, {}, {}, {}, {}].reduce(Promise.resolve.bind(Promise));
+  [{}, {}, {}, {}, {}].reduce(Promise.resolve);
   ok(true, "No leaks with resolve?");
 
   [{}, {}, {}, {}, {}].reduce(function(a, b, c, d) { return new Promise(function(r1, r2) { throw a; }); });
   ok(true, "No leaks with exception?");
 
   [{}, {}, {}, {}, {}].reduce(function(a, b, c, d) { return new Promise(function(r1, r2) { }); });
   ok(true, "No leaks with empty promise?");
 
deleted file mode 100644
--- a/dom/promise/tests/test_promise_xrays.html
+++ /dev/null
@@ -1,301 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1170760
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 1170760</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1170760">Mozilla Bug 1170760</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-<iframe id="t" src="http://example.org/tests/dom/promise/tests/file_promise_xrays.html"></iframe>
-</div>
-
-<pre id="test">
-<script type="application/javascript">
-
-var win = $("t").contentWindow;
-
-/** Test for Bug 1170760 **/
-SimpleTest.waitForExplicitFinish();
-
-function testLoadComplete() {
-  is(win.location.href, $("t").src, "Should have loaded the right thing");
-  nextTest();
-}
-
-function testHaveXray() {
-  is(typeof win.Promise.race, "function", "Should see a race() function");
-  var exception;
-  try {
-    win.Promise.wrappedJSObject.race;
-  } catch (e) {
-    exception = e;
-  }
-  is(exception, "Getting race", "Should have thrown the right exception");
-  is(win.wrappedJSObject.setupThrew, false, "Setup should not have thrown");
-  nextTest();
-}
-
-function testRace1() {
-  var p = win.Promise.race(new win.Array(1, 2));
-  p.then(
-    function(arg) {
-      ok(arg == 1 || arg == 2,
-         "Should get the right value when racing content-side array");
-    },
-    function(e) {
-      ok(false, "testRace1 threw exception: " + e);
-    }
-  ).then(nextTest);
-}
-
-function testRace2() {
-  var p = win.Promise.race(
-    new Array(win.Promise.resolve(1), win.Promise.resolve(2)));
-  p.then(
-    function(arg) {
-      ok(arg == 1 || arg == 2,
-         "Should get the right value when racing content-side array of explicit Promises");
-    },
-    function(e) {
-      ok(false, "testRace2 threw exception: " + e);
-    }
-  ).then(nextTest);
-}
-
-function testRace3() {
-  // This works with a chrome-side array because we do the iteration
-  // while still in the Xray compartment.
-  var p = win.Promise.race([1, 2]);
-  p.then(
-    function(arg) {
-      ok(arg == 1 || arg == 2,
-         "Should get the right value when racing chrome-side array");
-    },
-    function(e) {
-      ok(false, "testRace3 threw exception: " + e);
-    }
-  ).then(nextTest);
-}
-
-function testRace4() {
-  // This works with both content-side and chrome-side Promises because we want
-  // it to and go to some lengths to make it work.
-  var p = win.Promise.race([Promise.resolve(1), win.Promise.resolve(2)]);
-  p.then(
-    function(arg) {
-      ok(arg == 1 || arg == 2,
-         "Should get the right value when racing chrome-side promises");
-    },
-    function(e) {
-      ok(false, "testRace4 threw exception: " + e);
-    }
-  ).then(nextTest);
-}
-
-function testAll1() {
-  var p = win.Promise.all(new win.Array(1, 2));
-  p.then(
-    function(arg) {
-      ok(arg instanceof win.Array, "Should get an Array from Promise.all (1)");
-      is(arg[0], 1, "First entry of Promise.all return value should be correct (1)");
-      is(arg[1], 2, "Second entry of Promise.all return value should be correct (1)");
-    },
-    function(e) {
-      ok(false, "testAll1 threw exception: " + e);
-    }
-  ).then(nextTest);
-}
-
-function testAll2() {
-  var p = win.Promise.all(
-    new Array(win.Promise.resolve(1), win.Promise.resolve(2)));
-  p.then(
-    function(arg) {
-      ok(arg instanceof win.Array, "Should get an Array from Promise.all (2)");
-      is(arg[0], 1, "First entry of Promise.all return value should be correct (2)");
-      is(arg[1], 2, "Second entry of Promise.all return value should be correct (2)");
-    },
-    function(e) {
-      ok(false, "testAll2 threw exception: " + e);
-    }
-  ).then(nextTest);
-}
-
-function testAll3() {
-  // This works with a chrome-side array because we do the iteration
-  // while still in the Xray compartment.
-  var p = win.Promise.all([1, 2]);
-  p.then(
-    function(arg) {
-      ok(arg instanceof win.Array, "Should get an Array from Promise.all (3)");
-      is(arg[0], 1, "First entry of Promise.all return value should be correct (3)");
-      is(arg[1], 2, "Second entry of Promise.all return value should be correct (3)");
-    },
-    function(e) {
-      ok(false, "testAll3 threw exception: " + e);
-    }
-  ).then(nextTest);
-}
-
-function testAll4() {
-  // This works with both content-side and chrome-side Promises because we want
-  // it to and go to some lengths to make it work.
-  var p = win.Promise.all([Promise.resolve(1), win.Promise.resolve(2)]);
-  p.then(
-    function(arg) {
-      ok(arg instanceof win.Array, "Should get an Array from Promise.all (4)");
-      is(arg[0], 1, "First entry of Promise.all return value should be correct (4)");
-      is(arg[1], 2, "Second entry of Promise.all return value should be correct (4)");
-    },
-    function(e) {
-      ok(false, "testAll4 threw exception: " + e);
-    }
-  ).then(nextTest);
-}
-
-function testAll5() {
-  var p = win.Promise.all(new win.Array());
-  p.then(
-    function(arg) {
-      ok(arg instanceof win.Array, "Should get an Array from Promise.all (5)");
-    },
-    function(e) {
-      ok(false, "testAll5 threw exception: " + e);
-    }
-  ).then(nextTest);
-}
-
-function testResolve1() {
-  var p = win.Promise.resolve(5);
-  ok(p instanceof win.Promise, "Promise.resolve should return a promise");
-  p.then(
-    function(arg) {
-      is(arg, 5, "Should get correct Promise.resolve value");
-    },
-    function(e) {
-      ok(false, "testAll5 threw exception: " + e);
-    }
-  ).then(nextTest);
-}
-
-function testResolve2() {
-  var p = win.Promise.resolve(5);
-  var q = win.Promise.resolve(p);
-  is(q, p, "Promise.resolve should just pass through Promise values");
-  nextTest();
-}
-
-function testResolve3() {
-  var p = win.Promise.resolve(Promise.resolve(5));
-  p.then(
-    function(arg) {
-      is(arg, 5, "Promise.resolve with chrome Promise should work");
-    },
-    function(e) {
-      ok(false, "Promise.resolve with chrome Promise should not fail");
-    }
-  ).then(nextTest);
-}
-
-function testReject1() {
-  var p = win.Promise.reject(5);
-  ok(p instanceof win.Promise, "Promise.reject should return a promise");
-  p.then(
-    function(arg) {
-      ok(false, "Promise should be rejected");
-    },
-    function(e) {
-      is(e, 5, "Should get correct Promise.reject value");
-    }
-  ).then(nextTest);
-}
-
-function testThen1() {
-  var p = win.Promise.resolve(5);
-  var q = p.then((x) => x*x);
-  ok(q instanceof win.Promise,
-     "Promise.then should return a promise from the right global");
-  q.then(
-    function(arg) {
-      is(arg, 25, "Promise.then should work");
-    },
-    function(e) {
-      ok(false, "Promise.then should not fail");
-    }
-  ).then(nextTest);
-}
-
-function testThen2() {
-  var p = win.Promise.resolve(5);
-  var q = p.then((x) => Promise.resolve(x*x));
-  ok(q instanceof win.Promise,
-     "Promise.then should return a promise from the right global");
-  q.then(
-    function(arg) {
-      is(arg, 25, "Promise.then resolved with chrome promise should work");
-    },
-    function(e) {
-      ok(false, "Promise.then resolved with chrome promise should not fail");
-    }
-  ).then(nextTest);
-}
-
-function testCatch1() {
-  var p = win.Promise.reject(5);
-  ok(p instanceof win.Promise, "Promise.resolve should return a promise");
-  var q = p.catch((x) => x*x);
-  ok(q instanceof win.Promise,
-     "Promise.catch should return a promise from the right global");
-  q.then(
-    function(arg) {
-      is(arg, 25, "Promise.catch should work");
-    },
-    function(e) {
-      ok(false, "Promise.catch should not fail");
-    }
-  ).then(nextTest);
-}
-
-var tests = [
-  testLoadComplete,
-  testHaveXray,
-  testRace1,
-  testRace2,
-  testRace3,
-  testRace4,
-  testAll1,
-  testAll2,
-  testAll3,
-  testAll4,
-  testAll5,
-  testResolve1,
-  testResolve2,
-  testResolve3,
-  testReject1,
-  testThen1,
-  testThen2,
-  testCatch1,
-];
-
-function nextTest() {
-  if (tests.length == 0) {
-    SimpleTest.finish();
-    return;
-  }
-  tests.shift()();
-}
-
-addLoadEvent(nextTest);
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/promise/tests/test_species_getter.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Test for ...</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<script>
-test(function() {
-  var desc = Object.getOwnPropertyDescriptor(Promise, Symbol.species);
-  assert_not_equals(desc, undefined, "Should have a property");
-
-  assert_equals(desc.configurable, true, "Property should be configurable");
-  assert_equals(desc.enumerable, false, "Property should not be enumerable");
-  assert_equals(desc.set, undefined, "Should not have a setter");
-  var getter = desc.get;
-
-  var things = [undefined, null, 5, "xyz", Promise, Object];
-  for (var thing of things) {
-    assert_equals(getter.call(thing), thing,
-                  "Getter should return its this value");
-  }
-
-}, "Promise should have an @@species getter that works per spec");
-</script>
--- a/dom/webidl/DOMRequest.webidl
+++ b/dom/webidl/DOMRequest.webidl
@@ -15,15 +15,14 @@ interface DOMRequestShared {
   attribute EventHandler onsuccess;
   attribute EventHandler onerror;
 };
 
 [Exposed=(Window,Worker)]
 interface DOMRequest : EventTarget {
   // The [TreatNonCallableAsNull] annotation is required since then() should do
   // nothing instead of throwing errors when non-callable arguments are passed.
-  // See documentation for Promise.then to see why we return "any".
-  [NewObject, Throws]
-  any then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null,
-           [TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
+  [NewObject]
+  Promise<any> then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null,
+                    [TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
 };
 
 DOMRequest implements DOMRequestShared;
--- a/dom/webidl/Promise.webidl
+++ b/dom/webidl/Promise.webidl
@@ -15,44 +15,33 @@ callback PromiseInit = void (object reso
 [TreatNonCallableAsNull]
 callback AnyCallback = any (any value);
 
 // REMOVE THE RELEVANT ENTRY FROM test_interfaces.html WHEN THIS IS IMPLEMENTED IN JS.
 [Constructor(PromiseInit init),
  Exposed=(Window,Worker,System)]
 // Need to escape "Promise" so it's treated as an identifier.
 interface _Promise {
-  // Have to use "any" (or "object", but "any" is simpler) as the type to
-  // support the subclassing behavior, since nothing actually requires the
-  // return value of PromiseSubclass.resolve/reject to be a Promise object.
-  [NewObject, Throws]
-  static any resolve(optional any value);
-  [NewObject, Throws]
-  static any reject(optional any value);
+  // Disable the static methods when the interface object is supposed to be
+  // disabled, just in case some code decides to walk over to .constructor from
+  // the proto of a promise object or someone screws up and manages to create a
+  // Promise object in this scope without having resolved the interface object
+  // first.
+  [NewObject]
+  static Promise<any> resolve(optional any value);
+  [NewObject]
+  static Promise<void> reject(optional any value);
 
   // The [TreatNonCallableAsNull] annotation is required since then() should do
   // nothing instead of throwing errors when non-callable arguments are passed.
-  // Have to use "any" (or "object", but "any" is simpler) as the type to
-  // support the subclassing behavior, since nothing actually requires the
-  // return value of PromiseSubclass.then/catch to be a Promise object.
-  [NewObject, Throws]
-  any then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null,
-           [TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
+  [NewObject, MethodIdentityTestable]
+  Promise<any> then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null,
+                    [TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
 
-  [NewObject, Throws]
-  any catch([TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
+  [NewObject]
+  Promise<any> catch([TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
 
-  // Have to use "any" (or "object", but "any" is simpler) as the type to
-  // support the subclassing behavior, since nothing actually requires the
-  // return value of PromiseSubclass.all to be a Promise object.  As a result,
-  // we also have to do our argument conversion manually, because we want to
-  // convert its exceptions into rejections.
-  [NewObject, Throws]
-  static any all(optional any iterable);
+  [NewObject]
+  static Promise<any> all(sequence<any> iterable);
 
-  // Have to use "any" (or "object", but "any" is simpler) as the type to
-  // support the subclassing behavior, since nothing actually requires the
-  // return value of PromiseSubclass.race to be a Promise object.  As a result,
-  // we also have to do our argument conversion manually, because we want to
-  // convert its exceptions into rejections.
-  [NewObject, Throws]
-  static any race(optional any iterable);
+  [NewObject]
+  static Promise<any> race(sequence<any> iterable);
 };
--- a/dom/webidl/PromiseDebugging.webidl
+++ b/dom/webidl/PromiseDebugging.webidl
@@ -47,60 +47,43 @@ callback interface UncaughtRejectionObse
    * @param p A Promise that was previously left in uncaught state is
    * now caught, i.e. it is not the last in its chain anymore.
    */
   void onConsumed(Promise<any> p);
 };
 
 [ChromeOnly, Exposed=(Window,System)]
 interface PromiseDebugging {
-  /**
-   * The various functions on this interface all expect to take promises but
-   * don't want the WebIDL behavior of assimilating random passed-in objects
-   * into promises.  They also want to treat Promise subclass instances as
-   * promises instead of wrapping them in a vanilla Promise, which is what the
-   * IDL spec says to do.  So we list all our arguments as "object" instead of
-   * "Promise" and check for them being a Promise internally.
-   */
-
-  /**
-   * Get the current state of the given promise.
-   */
-  [Throws]
-  static PromiseDebuggingStateHolder getState(object p);
+  static PromiseDebuggingStateHolder getState(Promise<any> p);
 
   /**
    * Return the stack to the promise's allocation point.  This can
    * return null if the promise was not created from script.
    */
-  [Throws]
-  static object? getAllocationStack(object p);
+  static object? getAllocationStack(Promise<any> p);
 
   /**
    * Return the stack to the promise's rejection point, if the
    * rejection happened from script.  This can return null if the
    * promise has not been rejected or was not rejected from script.
    */
-  [Throws]
-  static object? getRejectionStack(object p);
+  static object? getRejectionStack(Promise<any> p);
 
   /**
    * Return the stack to the promise's fulfillment point, if the
    * fulfillment happened from script.  This can return null if the
    * promise has not been fulfilled or was not fulfilled from script.
    */
-  [Throws]
-  static object? getFullfillmentStack(object p);
+  static object? getFullfillmentStack(Promise<any> p);
 
   /**
    * Return an identifier for a promise. This identifier is guaranteed
    * to be unique to this instance of Firefox.
    */
-  [Throws]
-  static DOMString getPromiseID(object p);
+  static DOMString getPromiseID(Promise<any> p);
 
   /**
    * Get the promises directly depending on a given promise.  These are:
    *
    * 1) Return values of then() calls on the promise
    * 2) Return values of Promise.all() if the given promise was passed in as one
    *    of the arguments.
    * 3) Return values of Promise.race() if the given promise was passed in as
@@ -108,32 +91,30 @@ interface PromiseDebugging {
    *
    * Once a promise is settled, it will generally notify its dependent promises
    * and forget about them, so this is most useful on unsettled promises.
    *
    * Note that this function only returns the promises that directly depend on
    * p.  It does not recursively return promises that depend on promises that
    * depend on p.
    */
-  [Throws]
-  static sequence<Promise<any>> getDependentPromises(object p);
+  static sequence<Promise<any>> getDependentPromises(Promise<any> p);
 
   /**
    * Get the number of milliseconds elapsed since the given promise was created.
    */
-  [Throws]
-  static DOMHighResTimeStamp getPromiseLifetime(object p);
+  static DOMHighResTimeStamp getPromiseLifetime(Promise<any> p);
 
   /*
    * Get the number of milliseconds elapsed between the promise being created
    * and being settled.  Throws NS_ERROR_UNEXPECTED if the promise has not
    * settled.
    */
   [Throws]
-  static DOMHighResTimeStamp getTimeToSettle(object p);
+  static DOMHighResTimeStamp getTimeToSettle(Promise<any> p);
 
   /**
    * Watching uncaught rejections on the current thread.
    *
    * Adding an observer twice will cause it to be notified twice
    * of events.
    */
   static void addUncaughtRejectionObserver(UncaughtRejectionObserver o);
--- a/services/cloudsync/CloudSyncBookmarks.jsm
+++ b/services/cloudsync/CloudSyncBookmarks.jsm
@@ -289,33 +289,33 @@ var RootFolder = function (rootId, rootN
       results = Array.prototype.concat.apply([], results);
       let promises = [];
       results.map(function (result) {
         let promise = PlacesWrapper.localIdToGuid(result.parent).then(
           function (guidResult) {
             result.parent = guidResult;
             return Promise.resolve(result);
           },
-          Promise.reject.bind(Promise)
+          Promise.reject
         );
         promises.push(promise);
       });
       return Promise.all(promises);
     }
 
     function getAnnos(results) {
       results = Array.prototype.concat.apply([], results);
       let promises = [];
       results.map(function (result) {
         let promise = PlacesWrapper.getItemAnnotationsForLocalId(result.id).then(
           function (annos) {
             result.annos = annos;
             return Promise.resolve(result);
           },
-          Promise.reject.bind(Promise)
+          Promise.reject
         );
         promises.push(promise);
       });
       return Promise.all(promises);
     }
 
     let promises = [
       getFolders(folders),
@@ -347,17 +347,17 @@ var RootFolder = function (rootId, rootN
     function getParentGuids(results) {
       let promises = [];
       results.map(function (result) {
         let promise = PlacesWrapper.localIdToGuid(result.parent).then(
           function (guidResult) {
             result.parent = guidResult;
             return Promise.resolve(result);
           },
-          Promise.reject.bind(Promise)
+          Promise.reject
         );
         promises.push(promise);
       });
       return Promise.all(promises);
     }
 
     PlacesWrapper.getItemsByGuid(guids, types)
                  .then(getParentGuids)
@@ -554,17 +554,17 @@ var RootFolder = function (rootId, rootN
         }
       }
 
       for (let item of items) {
         if (!item || 'object' !== typeof(item)) {
           continue;
         }
 
-        let promise = exists(item).then(handleSortedItem, Promise.reject.bind(Promise));
+        let promise = exists(item).then(handleSortedItem, Promise.reject);
         promises.push(promise);
       }
 
       return Promise.all(promises);
     }
 
     let processNewFolders = function () {
       let newFolderGuids = Object.keys(newFolders);
@@ -583,28 +583,28 @@ var RootFolder = function (rootId, rootN
       let promises = [];
       for (let guid of newFolderRoots) {
         let root = newFolders[guid];
         let promise = Promise.resolve();
         promise = promise.then(
           function () {
             return _createItem(root);
           },
-          Promise.reject.bind(Promise)
+          Promise.reject
         );
         let items = [].concat(root._children);
 
         while (items.length) {
           let item = newFolders[items.shift()];
           items = items.concat(item._children);
           promise = promise.then(
             function () {
               return _createItem(item);
             },
-            Promise.reject.bind(Promise)
+            Promise.reject
           );
         }
         promises.push(promise);
       }
 
       return Promise.all(promises);
     }
 
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -29914,26 +29914,17 @@
       },
       {
         "path": "webdriver/windows/window_manipulation.py"
       }
     ]
   },
   "local_changes": {
     "deleted": [],
-    "items": {
-      "testharness": {
-        "js/builtins/Promise-subclassing.html": [
-          {
-            "path": "js/builtins/Promise-subclassing.html",
-            "url": "/js/builtins/Promise-subclassing.html"
-          }
-        ]
-      }
-    },
+    "items": {},
     "reftest_nodes": {}
   },
   "reftest_nodes": {
     "2dcontext/building-paths/canvas_complexshapes_arcto_001.htm": [
       {
         "path": "2dcontext/building-paths/canvas_complexshapes_arcto_001.htm",
         "references": [
           [
deleted file mode 100644
--- a/testing/web-platform/tests/js/builtins/Promise-subclassing.html
+++ /dev/null
@@ -1,265 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title></title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script>
-
-var theLog = [];
-var speciesGets = 0;
-var speciesCalls = 0;
-var constructorCalls = 0;
-var constructorGets = 0;
-var resolveCalls = 0;
-var rejectCalls = 0;
-var thenCalls = 0;
-var catchCalls = 0;
-var allCalls = 0;
-var raceCalls = 0;
-var nextCalls = 0;
-
-function takeLog() {
-  var oldLog = theLog;
-  theLog = [];
-  speciesGets = speciesCalls = constructorCalls = resolveCalls =
-    rejectCalls = thenCalls = catchCalls = allCalls = raceCalls =
-    nextCalls = constructorGets = 0;
-  return oldLog;
-}
-
-function clearLog() {
-  takeLog();
-}
-
-function log(str) {
-  theLog.push(str);
-}
-
-class LoggingPromise extends Promise {
-  constructor(func) {
-    super(func);
-    Object.defineProperty(this, "constructor",
-                          {
-                            get: function() {
-                              ++constructorGets;
-                              log(`Constructor get ${constructorGets}`);
-                              return Object.getPrototypeOf(this).constructor;
-                            }
-                          });
-    ++constructorCalls;
-    log(`Constructor ${constructorCalls}`);
-  }
-
-  static get [Symbol.species]() {
-    ++speciesGets;
-    log(`Species get ${speciesGets}`);
-    return LoggingSpecies;
-  }
-
-  static resolve(val) {
-    ++resolveCalls;
-    log(`Resolve ${resolveCalls}`);
-    return super.resolve(val);
-  }
-
-  static reject(val) {
-    ++rejectCalls;
-    log(`Reject ${rejectCalls}`);
-    return super.reject(val);
-  }
-
-  then(resolve, reject) {
-    ++thenCalls;
-    log(`Then ${thenCalls}`);
-    return super.then(resolve, reject);
-  }
-
-  catch(handler) {
-    ++catchCalls;
-    log(`Catch ${catchCalls}`);
-    return super.catch(handler);
-  }
-
-  static all(val) {
-    ++allCalls;
-    log(`All ${allCalls}`);
-    return super.all(val);
-  }
-
-  static race(val) {
-    ++raceCalls;
-    log(`Race ${raceCalls}`);
-    return super.race(val);
-  }
-}
-
-class LoggingIterable {
-  constructor(array) {
-    this.iter = array[Symbol.iterator]();
-  }
-
-  get [Symbol.iterator]() { return () => this }
-
-  next() {
-    ++nextCalls;
-    log(`Next ${nextCalls}`);
-    return this.iter.next();
-  }
-}
-
-class LoggingSpecies extends LoggingPromise {
-  constructor(func) {
-    ++speciesCalls;
-    log(`Species call ${speciesCalls}`);
-    super(func)
-  }
-}
-
-class SpeciesLessPromise extends LoggingPromise {
-  static get [Symbol.species]() {
-    return undefined;
-  }
-}
-
-promise_test(function testBasicConstructor() {
-  var p = new LoggingPromise((resolve) => resolve(5));
-  var log = takeLog();
-  assert_array_equals(log, ["Constructor 1"]);
-  assert_true(p instanceof LoggingPromise);
-  return p.then(function(arg) {
-    assert_equals(arg, 5);
-  });
-}, "Basic constructor behavior");
-
-promise_test(function testPromiseRace() {
-  clearLog();
-  var p = LoggingPromise.race(new LoggingIterable([1, 2]));
-  var log = takeLog();
-  assert_array_equals(log, ["Race 1", "Constructor 1",
-                            "Next 1", "Resolve 1", "Constructor 2",
-                            "Then 1", "Constructor get 1", "Species get 1", "Species call 1", "Constructor 3",
-                            "Next 2", "Resolve 2", "Constructor 4",
-                            "Then 2", "Constructor get 2", "Species get 2", "Species call 2", "Constructor 5",
-                            "Next 3"]);
-  assert_true(p instanceof LoggingPromise);
-  return p.then(function(arg) {
-    assert_true(arg == 1 || arg == 2);
-  });
-}, "Promise.race behavior");
-
-promise_test(function testPromiseRaceNoSpecies() {
-  clearLog();
-  var p = SpeciesLessPromise.race(new LoggingIterable([1, 2]));
-  var log = takeLog();
-  assert_array_equals(log, ["Race 1", "Constructor 1",
-                            "Next 1", "Resolve 1", "Constructor 2",
-                            "Then 1", "Constructor get 1",
-                            "Next 2", "Resolve 2", "Constructor 3",
-                            "Then 2", "Constructor get 2",
-                            "Next 3"]);
-  assert_true(p instanceof SpeciesLessPromise);
-  return p.then(function(arg) {
-    assert_true(arg == 1 || arg == 2);
-  });
-}, "Promise.race without species behavior");
-
-promise_test(function testPromiseAll() {
-  clearLog();
-  var p = LoggingPromise.all(new LoggingIterable([1, 2]));
-  var log = takeLog();
-  assert_array_equals(log, ["All 1", "Constructor 1",
-                            "Next 1", "Resolve 1", "Constructor 2",
-                            "Then 1", "Constructor get 1", "Species get 1", "Species call 1", "Constructor 3",
-                            "Next 2", "Resolve 2", "Constructor 4",
-                            "Then 2", "Constructor get 2", "Species get 2", "Species call 2", "Constructor 5",
-                            "Next 3"]);
-  assert_true(p instanceof LoggingPromise);
-  return p.then(function(arg) {
-    assert_array_equals(arg, [1, 2]);
-  });
-}, "Promise.all behavior");
-
-promise_test(function testPromiseResolve() {
-  clearLog();
-  var p = LoggingPromise.resolve(5);
-  var log = takeLog();
-  assert_array_equals(log, ["Resolve 1", "Constructor 1"]);
-  var q = LoggingPromise.resolve(p);
-  assert_equals(p, q,
-                "Promise.resolve with same constructor should preserve identity");
-  log = takeLog();
-  assert_array_equals(log, ["Resolve 1", "Constructor get 1"]);
-
-  var r = Promise.resolve(p);
-  log = takeLog();
-  assert_array_equals(log, ["Constructor get 1"]);
-  assert_not_equals(p, r,
-                    "Promise.resolve with different constructor should " +
-                    "create a new Promise instance (1)")
-  var s = Promise.resolve(6);
-  var u = LoggingPromise.resolve(s);
-  log = takeLog();
-  assert_array_equals(log, ["Resolve 1", "Constructor 1"]);
-  assert_not_equals(s, u,
-                    "Promise.resolve with different constructor should " +
-                    "create a new Promise instance (2)")
-
-  Object.defineProperty(s, "constructor", { value: LoggingPromise });
-  var v = LoggingPromise.resolve(s);
-  log = takeLog();
-  assert_array_equals(log, ["Resolve 1"]);
-  assert_equals(v, s, "Faking the .constructor should work");
-  assert_false(v instanceof LoggingPromise);
-
-  var results = Promise.all([p, q, r, s, u, v]);
-  return results.then(function(arg) {
-    assert_array_equals(arg, [5, 5, 5, 6, 6, 6]);
-  });
-}, "Promise.resolve behavior");
-
-promise_test(function testPromiseReject() {
-  clearLog();
-  var p = LoggingPromise.reject(5);
-  var log = takeLog();
-  assert_array_equals(log, ["Reject 1", "Constructor 1"]);
-
-  return p.catch(function(arg) {
-    assert_equals(arg, 5);
-  });
-}, "Promise.reject behavior");
-
-promise_test(function testPromiseThen() {
-  clearLog();
-  var p = LoggingPromise.resolve(5);
-  var log = takeLog();
-  assert_array_equals(log, ["Resolve 1", "Constructor 1"]);
-
-  var q = p.then((x) => x*x);
-  log = takeLog();
-  assert_array_equals(log, ["Then 1", "Constructor get 1", "Species get 1",
-                            "Species call 1", "Constructor 1"]);
-  assert_true(q instanceof LoggingPromise);
-
-  return q.then(function(arg) {
-    assert_equals(arg, 25);
-  });
-}, "Promise.then behavior");
-
-promise_test(function testPromiseCatch() {
-  clearLog();
-  var p = LoggingPromise.reject(5);
-  var log = takeLog();
-  assert_array_equals(log, ["Reject 1", "Constructor 1"]);
-
-  var q = p.catch((x) => x*x);
-  log = takeLog();
-  assert_array_equals(log, ["Catch 1", "Then 1", "Constructor get 1",
-                            "Species get 1", "Species call 1", "Constructor 1"]);
-  assert_true(q instanceof LoggingPromise);
-
-  return q.then(function(arg) {
-    assert_equals(arg, 25);
-  });
-}, "Promise.catch behavior");
-
-</script>
--- a/toolkit/components/asyncshutdown/tests/xpcshell/test_AsyncShutdown.js
+++ b/toolkit/components/asyncshutdown/tests/xpcshell/test_AsyncShutdown.js
@@ -143,17 +143,17 @@ add_task(function* test_phase_removeBloc
     }
     do_print("Waiting (should lift very quickly)");
     yield lock.wait();
     do_remove_blocker(lock, blockers[0], false);
 
 
     do_print("Attempt to remove a blocker after wait");
     lock = makeLock(kind);
-    blocker = Promise.resolve.bind(Promise);
+    blocker = Promise.resolve;
     yield lock.wait();
     do_remove_blocker(lock, blocker, false);
 
     do_print("Attempt to remove non-registered blocker after wait()");
     do_remove_blocker(lock, "foo", false);
     do_remove_blocker(lock, null, false);
   }