Bug 911213 - Implement new promise constructor, r=bz
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 11 Sep 2013 18:03:04 +0200
changeset 159483 07dafead9048be8c3be1e9a37211c0bf56c1d4b5
parent 159482 e0da7141fa0470a444d31b2900516465ec0b8ee6
child 159484 09861acec08eb272564b540bec59ead3f853f352
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs911213
milestone26.0a1
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
Bug 911213 - Implement new promise constructor, r=bz
b2g/components/MozKeyboard.js
dom/base/DOMRequestHelper.jsm
dom/base/test/test_domrequesthelper.xul
dom/promise/Promise.cpp
dom/promise/Promise.h
dom/promise/PromiseCallback.cpp
dom/promise/PromiseCallback.h
dom/promise/PromiseResolver.cpp
dom/promise/PromiseResolver.h
dom/promise/moz.build
dom/promise/tests/test_bug883683.html
dom/promise/tests/test_promise.html
dom/promise/tests/test_resolve.html
dom/tests/browser/test-console-api.html
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/Promise.webidl
--- a/b2g/components/MozKeyboard.js
+++ b/b2g/components/MozKeyboard.js
@@ -540,18 +540,18 @@ MozInputContext.prototype = {
   },
 
   get lang() {
     return this._context.lang;
   },
 
   getText: function ic_getText(offset, length) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage('Keyboard:GetText', {
         contextId: self._contextId,
         requestId: resolverId,
         offset: offset,
         length: length
       });
     });
   },
@@ -569,18 +569,18 @@ MozInputContext.prototype = {
   },
 
   get textAfterCursor() {
     return this._context.textAfterCursor;
   },
 
   setSelectionRange: function ic_setSelectionRange(start, length) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage("Keyboard:SetSelectionRange", {
         contextId: self._contextId,
         requestId: resolverId,
         selectionStart: start,
         selectionEnd: start + length
       });
     });
   },
@@ -598,64 +598,64 @@ MozInputContext.prototype = {
   },
 
   set onselectionchange(handler) {
     this.__DOM_IMPL__.setEventHandler("onselectionchange");
   },
 
   replaceSurroundingText: function ic_replaceSurrText(text, offset, length) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage('Keyboard:ReplaceSurroundingText', {
         contextId: self._contextId,
         requestId: resolverId,
         text: text,
         beforeLength: offset || 0,
         afterLength: length || 0
       });
     });
   },
 
   deleteSurroundingText: function ic_deleteSurrText(offset, length) {
     return this.replaceSurroundingText(null, offset, length);
   },
 
   sendKey: function ic_sendKey(keyCode, charCode, modifiers) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage('Keyboard:SendKey', {
         contextId: self._contextId,
         requestId: resolverId,
         keyCode: keyCode,
         charCode: charCode,
         modifiers: modifiers
       });
     });
   },
 
   setComposition: function ic_setComposition(text, cursor, clauses) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage('Keyboard:SetComposition', {
         contextId: self._contextId,
         requestId: resolverId,
         text: text,
         cursor: cursor || text.length,
         clauses: clauses || null
       });
     });
   },
 
   endComposition: function ic_endComposition(text) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage('Keyboard:EndComposition', {
         contextId: self._contextId,
         requestId: resolverId,
         text: text || ''
       });
     });
   }
 };
--- a/dom/base/DOMRequestHelper.jsm
+++ b/dom/base/DOMRequestHelper.jsm
@@ -216,14 +216,15 @@ DOMRequestIpcHelper.prototype = {
       if (this.getRequest(k) instanceof this._window.DOMRequest) {
         aCallback(k);
       }
     }, this);
   },
 
   forEachPromiseResolver: function(aCallback) {
     Object.keys(this._requests).forEach(function(k) {
-      if (this.getPromiseResolver(k) instanceof this._window.PromiseResolver) {
+      if ("resolve" in this.getPromiseResolver(k) &&
+          "reject" in this.getPromiseResolver(k)) {
         aCallback(k);
       }
     }, this);
   },
 }
--- a/dom/base/test/test_domrequesthelper.xul
+++ b/dom/base/test/test_domrequesthelper.xul
@@ -25,42 +25,43 @@
       __proto__: DOMRequestIpcHelper.prototype
     };
 
     var dummy = new DummyHelperSubclass();
 
     function createPromise() {
       ok(Promise, "Promise object should exist");
 
-      var promise = dummy.createPromise(function(r) {
-        ok(r, "received PromiseResolver");
-        r.resolve(true);
+      var promise = dummy.createPromise(function(resolve, reject) {
+        resolve(true);
       });
       ok(promise instanceof Promise, "returned a Promise");
       promise.then(runTest);
     }
 
     function getResolver() {
       var id;
       var resolver;
-      var promise = dummy.createPromise(function(r) {
+      var promise = dummy.createPromise(function(resolve, reject) {
+        var r = { resolve: resolve, reject: reject };
         id = dummy.getPromiseResolverId(r);
         resolver = r;
         ok(typeof id === "string", "id should be string");
         r.resolve(true);
       }).then(function(unused) {
         var r = dummy.getPromiseResolver(id);
         ok(resolver === r, "get should succeed");
         runTest();
       });
     }
 
     function removeResolver() {
       var id;
-      var promise = dummy.createPromise(function(r) {
+      var promise = dummy.createPromise(function(resolve, reject) {
+        var r = { resolve: resolve, reject: reject };
         id = dummy.getPromiseResolverId(r);
         ok(typeof id === "string", "id should be string");
 
         var resolver = dummy.getPromiseResolver(id);
         ok(resolver === r, "resolver get should succeed");
 
         r.resolve(true);
       }).then(function(unused) {
@@ -69,17 +70,18 @@
         ok(resolver === undefined, "removeResolver: get should fail");
         runTest();
       });
     }
 
     function takeResolver() {
       var id;
       var resolver;
-      var promise = dummy.createPromise(function(r) {
+      var promise = dummy.createPromise(function(resolve, reject) {
+        var r = { resolve: resolve, reject: reject };
         id = dummy.getPromiseResolverId(r);
         resolver = r;
         ok(typeof id === "string", "id should be string");
 
         var gotR = dummy.getPromiseResolver(id);
         ok(gotR === r, "resolver get should succeed");
 
         r.resolve(true);
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -4,17 +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 "mozilla/dom/Promise.h"
 
 #include "jsfriendapi.h"
 #include "mozilla/dom/OwningNonNull.h"
 #include "mozilla/dom/PromiseBinding.h"
-#include "mozilla/dom/PromiseResolver.h"
 #include "mozilla/Preferences.h"
 #include "PromiseCallback.h"
 #include "nsContentUtils.h"
 #include "nsPIDOMWindow.h"
 #include "WorkerPrivate.h"
 #include "nsJSPrincipals.h"
 #include "nsJSUtils.h"
 #include "nsPIDOMWindow.h"
@@ -47,33 +46,83 @@ public:
     mPromise->RunTask();
     return NS_OK;
   }
 
 private:
   nsRefPtr<Promise> mPromise;
 };
 
+// This class processes the promise's callbacks with promise's result.
+class PromiseResolverTask MOZ_FINAL : public nsRunnable
+{
+public:
+  PromiseResolverTask(Promise* aPromise,
+                      JS::Handle<JS::Value> aValue,
+                      Promise::PromiseState aState)
+    : mPromise(aPromise)
+    , mValue(aValue)
+    , mState(aState)
+  {
+    MOZ_ASSERT(aPromise);
+    MOZ_ASSERT(mState != Promise::Pending);
+    MOZ_COUNT_CTOR(PromiseResolverTask);
+
+    JSContext* cx = nsContentUtils::GetSafeJSContext();
+
+    /* It's safe to use unsafeGet() here: the unsafeness comes from the
+     * possibility of updating the value of mJSObject without triggering the
+     * barriers.  However if the value will always be marked, post barriers
+     * unnecessary. */
+    JS_AddNamedValueRootRT(JS_GetRuntime(cx), mValue.unsafeGet(),
+                           "PromiseResolverTask.mValue");
+  }
+
+  ~PromiseResolverTask()
+  {
+    MOZ_COUNT_DTOR(PromiseResolverTask);
+
+    JSContext* cx = nsContentUtils::GetSafeJSContext();
+
+    /* It's safe to use unsafeGet() here: the unsafeness comes from the
+     * possibility of updating the value of mJSObject without triggering the
+     * barriers.  However if the value will always be marked, post barriers
+     * unnecessary. */
+    JS_RemoveValueRootRT(JS_GetRuntime(cx), mValue.unsafeGet());
+  }
+
+  NS_IMETHOD Run()
+  {
+    mPromise->RunResolveTask(
+      JS::Handle<JS::Value>::fromMarkedLocation(mValue.address()),
+      mState, Promise::SyncTask);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<Promise> mPromise;
+  JS::Heap<JS::Value> mValue;
+  Promise::PromiseState mState;
+};
+
 // Promise
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
   tmp->MaybeReportRejected();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolver)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectCallbacks);
   tmp->mResult = JS::UndefinedValue();
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolver)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
@@ -88,22 +137,21 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 Promise::Promise(nsPIDOMWindow* aWindow)
   : mWindow(aWindow)
   , mResult(JS::UndefinedValue())
   , mState(Pending)
   , mTaskPending(false)
   , mHadRejectCallback(false)
+  , mResolvePending(false)
 {
   MOZ_COUNT_CTOR(Promise);
   mozilla::HoldJSObjects(this);
   SetIsDOMBinding();
-
-  mResolver = new PromiseResolver(this);
 }
 
 Promise::~Promise()
 {
   MaybeReportRejected();
   mResult = JS::UndefinedValue();
   mozilla::DropJSObjects(this);
   MOZ_COUNT_DTOR(Promise);
@@ -147,40 +195,117 @@ EnterCompartment(Maybe<JSAutoCompartment
 {
   // FIXME Bug 878849
   if (aValue.WasPassed() && aValue.Value().isObject()) {
     JS::Rooted<JSObject*> rooted(aCx, &aValue.Value().toObject());
     aAc.construct(aCx, rooted);
   }
 }
 
+enum {
+  SLOT_PROMISE = 0,
+  SLOT_TASK
+};
+
+/* static */ bool
+Promise::JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp)
+{
+  JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
+
+  JS::Rooted<JS::Value> v(aCx,
+                          js::GetFunctionNativeReserved(&args.callee(),
+                                                        SLOT_PROMISE));
+  MOZ_ASSERT(v.isObject());
+
+  Promise* promise;
+  if (NS_FAILED(UNWRAP_OBJECT(Promise, aCx, &v.toObject(), promise))) {
+    return Throw(aCx, NS_ERROR_UNEXPECTED);
+  }
+
+  Optional<JS::Handle<JS::Value> > value(aCx);
+  if (aArgc) {
+    value.Value() = args[0];
+  }
+
+  v = js::GetFunctionNativeReserved(&args.callee(), SLOT_TASK);
+  PromiseCallback::Task task = static_cast<PromiseCallback::Task>(v.toInt32());
+
+  if (task == PromiseCallback::Resolve) {
+    promise->MaybeResolve(aCx, value);
+  } else {
+    promise->MaybeReject(aCx, value);
+  }
+
+  return true;
+}
+
+/* static */ JSObject*
+Promise::CreateFunction(JSContext* aCx, JSObject* aParent, Promise* aPromise,
+                        int32_t aTask)
+{
+  JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback,
+                                                 1 /* nargs */, 0 /* flags */,
+                                                 aParent, nullptr);
+  if (!func) {
+    return nullptr;
+  }
+
+  JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
+
+  JS::Rooted<JS::Value> promiseObj(aCx);
+  if (!dom::WrapNewBindingObject(aCx, obj, aPromise, &promiseObj)) {
+    return nullptr;
+  }
+
+  js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj);
+  js::SetFunctionNativeReserved(obj, SLOT_TASK, JS::Int32Value(aTask));
+
+  return obj;
+}
+
 /* static */ already_AddRefed<Promise>
 Promise::Constructor(const GlobalObject& aGlobal,
                      PromiseInit& aInit, ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.GetContext();
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<Promise> promise = new Promise(window);
 
-  aInit.Call(promise, *promise->mResolver, aRv,
+  JS::Rooted<JSObject*> resolveFunc(cx,
+                                    CreateFunction(cx, aGlobal.Get(), promise,
+                                                   PromiseCallback::Resolve));
+  if (!resolveFunc) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  JS::Rooted<JSObject*> rejectFunc(cx,
+                                   CreateFunction(cx, aGlobal.Get(), promise,
+                                                  PromiseCallback::Reject));
+  if (!rejectFunc) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  aInit.Call(promise, resolveFunc, rejectFunc, aRv,
              CallbackObject::eRethrowExceptions);
   aRv.WouldReportJSException();
 
   if (aRv.IsJSException()) {
     Optional<JS::Handle<JS::Value> > value(cx);
     aRv.StealJSException(cx, &value.Value());
 
     Maybe<JSAutoCompartment> ac;
     EnterCompartment(ac, cx, value);
-    promise->mResolver->Reject(cx, value);
+    promise->MaybeReject(cx, value);
   }
 
   return promise.forget();
 }
 
 /* static */ already_AddRefed<Promise>
 Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
                  JS::Handle<JS::Value> aValue, ErrorResult& aRv)
@@ -189,52 +314,52 @@ Promise::Resolve(const GlobalObject& aGl
   if (!window) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<Promise> promise = new Promise(window);
 
   Optional<JS::Handle<JS::Value> > value(aCx, aValue);
-  promise->mResolver->Resolve(aCx, value);
+  promise->MaybeResolve(aCx, value);
   return promise.forget();
 }
 
 /* static */ already_AddRefed<Promise>
 Promise::Reject(const GlobalObject& aGlobal, JSContext* aCx,
                 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<Promise> promise = new Promise(window);
 
   Optional<JS::Handle<JS::Value> > value(aCx, aValue);
-  promise->mResolver->Reject(aCx, value);
+  promise->MaybeReject(aCx, value);
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 Promise::Then(const Optional<OwningNonNull<AnyCallback> >& aResolveCallback,
               const Optional<OwningNonNull<AnyCallback> >& aRejectCallback)
 {
   nsRefPtr<Promise> promise = new Promise(GetParentObject());
 
   nsRefPtr<PromiseCallback> resolveCb =
-    PromiseCallback::Factory(promise->mResolver,
+    PromiseCallback::Factory(promise,
                              aResolveCallback.WasPassed()
                                ? &aResolveCallback.Value()
                                : nullptr,
                              PromiseCallback::Resolve);
 
   nsRefPtr<PromiseCallback> rejectCb =
-    PromiseCallback::Factory(promise->mResolver,
+    PromiseCallback::Factory(promise,
                              aRejectCallback.WasPassed()
                                ? &aRejectCallback.Value()
                                : nullptr,
                              PromiseCallback::Reject);
 
   AppendCallbacks(resolveCb, rejectCb);
 
   return promise.forget();
@@ -255,19 +380,19 @@ Promise::AppendCallbacks(PromiseCallback
     mResolveCallbacks.AppendElement(aResolveCallback);
   }
 
   if (aRejectCallback) {
     mHadRejectCallback = true;
     mRejectCallbacks.AppendElement(aRejectCallback);
   }
 
-  // If promise's state is resolved, queue a task to process promise's resolve
-  // callbacks with promise's result. If promise's state is rejected, queue a task
-  // to process promise's reject callbacks with promise's result.
+  // If promise's state is resolved, queue a task to process our resolve
+  // callbacks with promise's result. If promise's state is rejected, queue a
+  // task to process our reject callbacks with promise's result.
   if (mState != Pending && !mTaskPending) {
     nsRefPtr<PromiseTask> task = new PromiseTask(this);
     NS_DispatchToCurrentThread(task);
     mTaskPending = true;
   }
 }
 
 void
@@ -310,10 +435,96 @@ Promise::MaybeReportRejected()
   NS_DispatchToCurrentThread(
     new AsyncErrorReporter(JS_GetObjectRuntime(&mResult.toObject()),
                            report,
                            nullptr,
                            nsContentUtils::GetObjectPrincipal(&mResult.toObject()),
                            win));
 }
 
+void
+Promise::MaybeResolve(JSContext* aCx,
+                      const Optional<JS::Handle<JS::Value> >& aValue,
+                      PromiseTaskSync aAsynchronous)
+{
+  if (mResolvePending) {
+    return;
+  }
+
+  ResolveInternal(aCx, aValue, aAsynchronous);
+}
+
+void
+Promise::MaybeReject(JSContext* aCx,
+                     const Optional<JS::Handle<JS::Value> >& aValue,
+                     PromiseTaskSync aAsynchronous)
+{
+  if (mResolvePending) {
+    return;
+  }
+
+  RejectInternal(aCx, aValue, aAsynchronous);
+}
+
+void
+Promise::ResolveInternal(JSContext* aCx,
+                         const Optional<JS::Handle<JS::Value> >& aValue,
+                         PromiseTaskSync aAsynchronous)
+{
+  mResolvePending = true;
+
+  // TODO: Bug 879245 - Then-able objects
+  if (aValue.WasPassed() && aValue.Value().isObject()) {
+    JS::Rooted<JSObject*> valueObj(aCx, &aValue.Value().toObject());
+    Promise* nextPromise;
+    nsresult rv = UNWRAP_OBJECT(Promise, aCx, valueObj, nextPromise);
+
+    if (NS_SUCCEEDED(rv)) {
+      nsRefPtr<PromiseCallback> resolveCb = new ResolvePromiseCallback(this);
+      nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(this);
+      nextPromise->AppendCallbacks(resolveCb, rejectCb);
+      return;
+    }
+  }
+
+  // If the synchronous flag is set, process our resolve callbacks with
+  // value. Otherwise, the synchronous flag is unset, queue a task to process
+  // own resolve callbacks with value. Otherwise, the synchronous flag is
+  // unset, queue a task to process our resolve callbacks with value.
+  RunResolveTask(aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue,
+                 Resolved, aAsynchronous);
+}
+
+void
+Promise::RejectInternal(JSContext* aCx,
+                        const Optional<JS::Handle<JS::Value> >& aValue,
+                        PromiseTaskSync aAsynchronous)
+{
+  mResolvePending = true;
+
+  // If the synchronous flag is set, process our reject callbacks with
+  // value. Otherwise, the synchronous flag is unset, queue a task to process
+  // promise's reject callbacks with value.
+  RunResolveTask(aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue,
+                 Rejected, aAsynchronous);
+}
+
+void
+Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
+                        PromiseState aState,
+                        PromiseTaskSync aAsynchronous)
+{
+  // If the synchronous flag is unset, queue a task to process our
+  // accept callbacks with value.
+  if (aAsynchronous == AsyncTask) {
+    nsRefPtr<PromiseResolverTask> task =
+      new PromiseResolverTask(this, aValue, aState);
+    NS_DispatchToCurrentThread(task);
+    return;
+  }
+
+  SetResult(aValue);
+  SetState(aState);
+  RunTask();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -18,24 +18,25 @@
 #include "js/TypeDecls.h"
 
 namespace mozilla {
 namespace dom {
 
 class PromiseInit;
 class PromiseCallback;
 class AnyCallback;
-class PromiseResolver;
 
 class Promise MOZ_FINAL : public nsISupports,
                           public nsWrapperCache
 {
   friend class PromiseTask;
-  friend class PromiseResolver;
   friend class PromiseResolverTask;
+  friend class ResolvePromiseCallback;
+  friend class RejectPromiseCallback;
+  friend class WrapperPromiseCallback;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise)
 
   Promise(nsPIDOMWindow* aWindow);
   ~Promise();
 
@@ -74,16 +75,21 @@ public:
 
 private:
   enum PromiseState {
     Pending,
     Resolved,
     Rejected
   };
 
+  enum PromiseTaskSync {
+    SyncTask,
+    AsyncTask
+  };
+
   void SetState(PromiseState aState)
   {
     MOZ_ASSERT(mState == Pending);
     MOZ_ASSERT(aState != Pending);
     mState = aState;
   }
 
   void SetResult(JS::Handle<JS::Value> aValue)
@@ -92,32 +98,58 @@ private:
   }
 
   // This method processes promise's resolve/reject callbacks with promise's
   // result. It's executed when the resolver.resolve() or resolver.reject() is
   // called or when the promise already has a result and new callbacks are
   // appended by then(), catch() or done().
   void RunTask();
 
+  void RunResolveTask(JS::Handle<JS::Value> aValue,
+                      Promise::PromiseState aState,
+                      PromiseTaskSync aAsynchronous);
+
   void AppendCallbacks(PromiseCallback* aResolveCallback,
                        PromiseCallback* aRejectCallback);
 
   // If we have been rejected and our mResult is a JS exception,
   // report it to the error console.
   void MaybeReportRejected();
 
-  nsRefPtr<nsPIDOMWindow> mWindow;
+  void MaybeResolve(JSContext* aCx,
+                    const Optional<JS::Handle<JS::Value> >& aValue,
+                    PromiseTaskSync aSync = AsyncTask);
+  void MaybeReject(JSContext* aCx,
+                   const Optional<JS::Handle<JS::Value> >& aValue,
+                   PromiseTaskSync aSync = AsyncTask);
+
+  void ResolveInternal(JSContext* aCx,
+                       const Optional<JS::Handle<JS::Value> >& aValue,
+                       PromiseTaskSync aSync = AsyncTask);
 
-  nsRefPtr<PromiseResolver> mResolver;
+  void RejectInternal(JSContext* aCx,
+                      const Optional<JS::Handle<JS::Value> >& aValue,
+                      PromiseTaskSync aSync = AsyncTask);
+
+  // Static methods for the PromiseInit functions.
+  static bool
+  JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp);
+  static JSObject*
+  CreateFunction(JSContext* aCx, JSObject* aParent, Promise* aPromise,
+                int32_t aTask);
+
+  nsRefPtr<nsPIDOMWindow> mWindow;
 
   nsTArray<nsRefPtr<PromiseCallback> > mResolveCallbacks;
   nsTArray<nsRefPtr<PromiseCallback> > mRejectCallbacks;
 
   JS::Heap<JS::Value> mResult;
   PromiseState mState;
   bool mTaskPending;
   bool mHadRejectCallback;
+
+  bool mResolvePending;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Promise_h
--- a/dom/promise/PromiseCallback.cpp
+++ b/dom/promise/PromiseCallback.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/PromiseResolver.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseCallback)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseCallback)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseCallback)
@@ -46,100 +45,100 @@ EnterCompartment(Maybe<JSAutoCompartment
     aAc.construct(aCx, rooted);
   }
 }
 
 // ResolvePromiseCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(ResolvePromiseCallback,
                                      PromiseCallback,
-                                     mResolver)
+                                     mPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ResolvePromiseCallback)
 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
 
 NS_IMPL_ADDREF_INHERITED(ResolvePromiseCallback, PromiseCallback)
 NS_IMPL_RELEASE_INHERITED(ResolvePromiseCallback, PromiseCallback)
 
-ResolvePromiseCallback::ResolvePromiseCallback(PromiseResolver* aResolver)
-  : mResolver(aResolver)
+ResolvePromiseCallback::ResolvePromiseCallback(Promise* aPromise)
+  : mPromise(aPromise)
 {
-  MOZ_ASSERT(aResolver);
+  MOZ_ASSERT(aPromise);
   MOZ_COUNT_CTOR(ResolvePromiseCallback);
 }
 
 ResolvePromiseCallback::~ResolvePromiseCallback()
 {
   MOZ_COUNT_DTOR(ResolvePromiseCallback);
 }
 
 void
 ResolvePromiseCallback::Call(const Optional<JS::Handle<JS::Value> >& aValue)
 {
   // Run resolver's algorithm with value and the synchronous flag set.
   AutoJSContext cx;
   Maybe<JSAutoCompartment> ac;
   EnterCompartment(ac, cx, aValue);
 
-  mResolver->ResolveInternal(cx, aValue, PromiseResolver::SyncTask);
+  mPromise->ResolveInternal(cx, aValue, Promise::SyncTask);
 }
 
 // RejectPromiseCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(RejectPromiseCallback,
                                      PromiseCallback,
-                                     mResolver)
+                                     mPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(RejectPromiseCallback)
 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
 
 NS_IMPL_ADDREF_INHERITED(RejectPromiseCallback, PromiseCallback)
 NS_IMPL_RELEASE_INHERITED(RejectPromiseCallback, PromiseCallback)
 
-RejectPromiseCallback::RejectPromiseCallback(PromiseResolver* aResolver)
-  : mResolver(aResolver)
+RejectPromiseCallback::RejectPromiseCallback(Promise* aPromise)
+  : mPromise(aPromise)
 {
-  MOZ_ASSERT(aResolver);
+  MOZ_ASSERT(aPromise);
   MOZ_COUNT_CTOR(RejectPromiseCallback);
 }
 
 RejectPromiseCallback::~RejectPromiseCallback()
 {
   MOZ_COUNT_DTOR(RejectPromiseCallback);
 }
 
 void
 RejectPromiseCallback::Call(const Optional<JS::Handle<JS::Value> >& aValue)
 {
   // Run resolver's algorithm with value and the synchronous flag set.
   AutoJSContext cx;
   Maybe<JSAutoCompartment> ac;
   EnterCompartment(ac, cx, aValue);
 
-  mResolver->RejectInternal(cx, aValue, PromiseResolver::SyncTask);
+  mPromise->RejectInternal(cx, aValue, Promise::SyncTask);
 }
 
 // WrapperPromiseCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(WrapperPromiseCallback,
                                      PromiseCallback,
-                                     mNextResolver, mCallback)
+                                     mNextPromise, mCallback)
 
 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)
 
-WrapperPromiseCallback::WrapperPromiseCallback(PromiseResolver* aNextResolver,
+WrapperPromiseCallback::WrapperPromiseCallback(Promise* aNextPromise,
                                                AnyCallback* aCallback)
-  : mNextResolver(aNextResolver)
+  : mNextPromise(aNextPromise)
   , mCallback(aCallback)
 {
-  MOZ_ASSERT(aNextResolver);
+  MOZ_ASSERT(aNextPromise);
   MOZ_COUNT_CTOR(WrapperPromiseCallback);
 }
 
 WrapperPromiseCallback::~WrapperPromiseCallback()
 {
   MOZ_COUNT_DTOR(WrapperPromiseCallback);
 }
 
@@ -150,36 +149,36 @@ WrapperPromiseCallback::Call(const Optio
   Maybe<JSAutoCompartment> ac;
   EnterCompartment(ac, cx, aValue);
 
   ErrorResult rv;
 
   // If invoking callback threw an exception, run resolver's reject with the
   // thrown exception as argument and the synchronous flag set.
   Optional<JS::Handle<JS::Value> > value(cx,
-    mCallback->Call(mNextResolver->GetParentObject(), aValue, rv,
+    mCallback->Call(mNextPromise->GetParentObject(), aValue, rv,
                     CallbackObject::eRethrowExceptions));
 
   rv.WouldReportJSException();
 
   if (rv.Failed() && rv.IsJSException()) {
     Optional<JS::Handle<JS::Value> > value(cx);
     rv.StealJSException(cx, &value.Value());
 
     Maybe<JSAutoCompartment> ac2;
     EnterCompartment(ac2, cx, value);
-    mNextResolver->RejectInternal(cx, value, PromiseResolver::SyncTask);
+    mNextPromise->RejectInternal(cx, value, Promise::SyncTask);
     return;
   }
 
   // Otherwise, run resolver's resolve with value and the synchronous flag
   // set.
   Maybe<JSAutoCompartment> ac2;
   EnterCompartment(ac2, cx, value);
-  mNextResolver->ResolveInternal(cx, value, PromiseResolver::SyncTask);
+  mNextPromise->ResolveInternal(cx, value, Promise::SyncTask);
 }
 
 // SimpleWrapperPromiseCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(SimpleWrapperPromiseCallback,
                                      PromiseCallback,
                                      mPromise, mCallback)
 
@@ -206,33 +205,33 @@ SimpleWrapperPromiseCallback::~SimpleWra
 void
 SimpleWrapperPromiseCallback::Call(const Optional<JS::Handle<JS::Value> >& aValue)
 {
   ErrorResult rv;
   mCallback->Call(mPromise, aValue, rv);
 }
 
 /* static */ PromiseCallback*
-PromiseCallback::Factory(PromiseResolver* aNextResolver,
-                         AnyCallback* aCallback, Task aTask)
+PromiseCallback::Factory(Promise* aNextPromise, AnyCallback* aCallback,
+                         Task aTask)
 {
-  MOZ_ASSERT(aNextResolver);
+  MOZ_ASSERT(aNextPromise);
 
   // If we have a callback and a next resolver, we have to exec the callback and
   // then propagate the return value to the next resolver->resolve().
   if (aCallback) {
-    return new WrapperPromiseCallback(aNextResolver, aCallback);
+    return new WrapperPromiseCallback(aNextPromise, aCallback);
   }
 
   if (aTask == Resolve) {
-    return new ResolvePromiseCallback(aNextResolver);
+    return new ResolvePromiseCallback(aNextPromise);
   }
 
   if (aTask == Reject) {
-    return new RejectPromiseCallback(aNextResolver);
+    return new RejectPromiseCallback(aNextPromise);
   }
 
   MOZ_ASSERT(false, "This should not happen");
   return nullptr;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/promise/PromiseCallback.h
+++ b/dom/promise/PromiseCallback.h
@@ -8,18 +8,16 @@
 #define mozilla_dom_PromiseCallback_h
 
 #include "mozilla/dom/Promise.h"
 #include "nsCycleCollectionParticipant.h"
 
 namespace mozilla {
 namespace dom {
 
-class PromiseResolver;
-
 // This is the base class for any PromiseCallback.
 // It's a logical step in the promise chain of callbacks.
 class PromiseCallback : public nsISupports
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(PromiseCallback)
 
@@ -30,38 +28,36 @@ public:
 
   enum Task {
     Resolve,
     Reject
   };
 
   // This factory returns a PromiseCallback object with refcount of 0.
   static PromiseCallback*
-  Factory(PromiseResolver* aNextResolver, AnyCallback* aCallback,
-          Task aTask);
+  Factory(Promise* aNextPromise, AnyCallback* aCallback, Task aTask);
 };
 
 // WrapperPromiseCallback execs a JS Callback with a value, and then the return
-// value is sent to the aNextResolver->resolve() or to aNextResolver->Reject()
-// if the JS Callback throws.
+// value is sent to the aNextPromise->resolveFunction() or to
+// aNextPromise->RejectFunction() if the JS Callback throws.
 class WrapperPromiseCallback MOZ_FINAL : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WrapperPromiseCallback,
                                            PromiseCallback)
 
   void Call(const Optional<JS::Handle<JS::Value> >& aValue) MOZ_OVERRIDE;
 
-  WrapperPromiseCallback(PromiseResolver* aNextResolver,
-                         AnyCallback* aCallback);
+  WrapperPromiseCallback(Promise* aNextPromise, AnyCallback* aCallback);
   ~WrapperPromiseCallback();
 
 private:
-  nsRefPtr<PromiseResolver> mNextResolver;
+  nsRefPtr<Promise> mNextPromise;
   nsRefPtr<AnyCallback> mCallback;
 };
 
 // SimpleWrapperPromiseCallback execs a JS Callback with a value.
 class SimpleWrapperPromiseCallback MOZ_FINAL : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -74,48 +70,48 @@ public:
                                AnyCallback* aCallback);
   ~SimpleWrapperPromiseCallback();
 
 private:
   nsRefPtr<Promise> mPromise;
   nsRefPtr<AnyCallback> mCallback;
 };
 
-// ResolvePromiseCallback calls aResolver->Resolve() with the value received by
-// Call().
+// ResolvePromiseCallback calls aPromise->ResolveFunction() with the value
+// received by Call().
 class ResolvePromiseCallback MOZ_FINAL : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ResolvePromiseCallback,
                                            PromiseCallback)
 
   void Call(const Optional<JS::Handle<JS::Value> >& aValue) MOZ_OVERRIDE;
 
-  ResolvePromiseCallback(PromiseResolver* aResolver);
+  ResolvePromiseCallback(Promise* aPromise);
   ~ResolvePromiseCallback();
 
 private:
-  nsRefPtr<PromiseResolver> mResolver;
+  nsRefPtr<Promise> mPromise;
 };
 
-// RejectPromiseCallback calls aResolver->Reject() with the value received by
-// Call().
+// RejectPromiseCallback calls aPromise->RejectFunction() with the value
+// received by Call().
 class RejectPromiseCallback MOZ_FINAL : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(RejectPromiseCallback,
                                            PromiseCallback)
 
   void Call(const Optional<JS::Handle<JS::Value> >& aValue) MOZ_OVERRIDE;
 
-  RejectPromiseCallback(PromiseResolver* aResolver);
+  RejectPromiseCallback(Promise* aPromise);
   ~RejectPromiseCallback();
 
 private:
-  nsRefPtr<PromiseResolver> mResolver;
+  nsRefPtr<Promise> mPromise;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PromiseCallback_h
deleted file mode 100644
--- a/dom/promise/PromiseResolver.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * 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 "mozilla/dom/PromiseResolver.h"
-#include "mozilla/dom/PromiseBinding.h"
-#include "mozilla/dom/Promise.h"
-#include "nsThreadUtils.h"
-#include "PromiseCallback.h"
-
-namespace mozilla {
-namespace dom {
-
-// PromiseResolverTask
-
-// This class processes the promise's callbacks with promise's result.
-class PromiseResolverTask MOZ_FINAL : public nsRunnable
-{
-public:
-  PromiseResolverTask(PromiseResolver* aResolver,
-                      const JS::Handle<JS::Value> aValue,
-                      Promise::PromiseState aState)
-    : mResolver(aResolver)
-    , mValue(aValue)
-    , mState(aState)
-  {
-    MOZ_ASSERT(aResolver);
-    MOZ_ASSERT(mState != Promise::Pending);
-    MOZ_COUNT_CTOR(PromiseResolverTask);
-
-    JSContext* cx = nsContentUtils::GetSafeJSContext();
-    JS_AddNamedValueRootRT(JS_GetRuntime(cx), &mValue,
-                           "PromiseResolverTask.mValue");
-  }
-
-  ~PromiseResolverTask()
-  {
-    MOZ_COUNT_DTOR(PromiseResolverTask);
-
-    JSContext* cx = nsContentUtils::GetSafeJSContext();
-    JS_RemoveValueRootRT(JS_GetRuntime(cx), &mValue);
-  }
-
-  NS_IMETHOD Run()
-  {
-    mResolver->RunTask(JS::Handle<JS::Value>::fromMarkedLocation(&mValue),
-                       mState, PromiseResolver::SyncTask);
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<PromiseResolver> mResolver;
-  JS::Value mValue;
-  Promise::PromiseState mState;
-};
-
-// PromiseResolver
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(PromiseResolver, mPromise)
-
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PromiseResolver, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PromiseResolver, Release)
-
-PromiseResolver::PromiseResolver(Promise* aPromise)
-  : mPromise(aPromise)
-  , mResolvePending(false)
-{
-  MOZ_COUNT_CTOR(PromiseResolver);
-  SetIsDOMBinding();
-}
-
-PromiseResolver::~PromiseResolver()
-{
-  MOZ_COUNT_DTOR(PromiseResolver);
-}
-
-JSObject*
-PromiseResolver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
-{
-  return PromiseResolverBinding::Wrap(aCx, aScope, this);
-}
-
-void
-PromiseResolver::Resolve(JSContext* aCx,
-                         const Optional<JS::Handle<JS::Value> >& aValue,
-                         PromiseTaskSync aAsynchronous)
-{
-  if (mResolvePending) {
-    return;
-  }
-
-  ResolveInternal(aCx, aValue, aAsynchronous);
-}
-
-void
-PromiseResolver::ResolveInternal(JSContext* aCx,
-                                 const Optional<JS::Handle<JS::Value> >& aValue,
-                                 PromiseTaskSync aAsynchronous)
-{
-  mResolvePending = true;
-
-  // TODO: Bug 879245 - Then-able objects
-  if (aValue.WasPassed() && aValue.Value().isObject()) {
-    JS::Rooted<JSObject*> valueObj(aCx, &aValue.Value().toObject());
-    Promise* nextPromise;
-    nsresult rv = UNWRAP_OBJECT(Promise, aCx, valueObj, nextPromise);
-
-    if (NS_SUCCEEDED(rv)) {
-      nsRefPtr<PromiseCallback> resolveCb = new ResolvePromiseCallback(this);
-      nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(this);
-      nextPromise->AppendCallbacks(resolveCb, rejectCb);
-      return;
-    }
-  }
-
-  // If the synchronous flag is set, process promise's resolve callbacks with
-  // value. Otherwise, the synchronous flag is unset, queue a task to process
-  // promise's resolve callbacks with value. Otherwise, the synchronous flag is
-  // unset, queue a task to process promise's resolve callbacks with value.
-  RunTask(aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue,
-          Promise::Resolved, aAsynchronous);
-}
-
-void
-PromiseResolver::Reject(JSContext* aCx,
-                        const Optional<JS::Handle<JS::Value> >& aValue,
-                        PromiseTaskSync aAsynchronous)
-{
-  if (mResolvePending) {
-    return;
-  }
-
-  RejectInternal(aCx, aValue, aAsynchronous);
-}
-
-void
-PromiseResolver::RejectInternal(JSContext* aCx,
-                                const Optional<JS::Handle<JS::Value> >& aValue,
-                                PromiseTaskSync aAsynchronous)
-{
-  mResolvePending = true;
-
-  // If the synchronous flag is set, process promise's reject callbacks with
-  // value. Otherwise, the synchronous flag is unset, queue a task to process
-  // promise's reject callbacks with value.
-  RunTask(aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue,
-          Promise::Rejected, aAsynchronous);
-}
-
-void
-PromiseResolver::RunTask(JS::Handle<JS::Value> aValue,
-                         Promise::PromiseState aState,
-                         PromiseTaskSync aAsynchronous)
-{
-  // If the synchronous flag is unset, queue a task to process promise's
-  // accept callbacks with value.
-  if (aAsynchronous == AsyncTask) {
-    nsRefPtr<PromiseResolverTask> task =
-      new PromiseResolverTask(this, aValue, aState);
-    NS_DispatchToCurrentThread(task);
-    return;
-  }
-
-  mPromise->SetResult(aValue);
-  mPromise->SetState(aState);
-  mPromise->RunTask();
-  mPromise = nullptr;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/promise/PromiseResolver.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * 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/. */
-
-#ifndef mozilla_dom_PromiseResolver_h
-#define mozilla_dom_PromiseResolver_h
-
-#include "mozilla/dom/Promise.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/dom/BindingDeclarations.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsWrapperCache.h"
-#include "js/TypeDecls.h"
-
-namespace mozilla {
-namespace dom {
-
-class PromiseResolver MOZ_FINAL : public nsWrapperCache
-{
-  friend class PromiseResolverTask;
-  friend class WrapperPromiseCallback;
-  friend class ResolvePromiseCallback;
-  friend class RejectPromiseCallback;
-
-private:
-  enum PromiseTaskSync {
-    SyncTask,
-    AsyncTask
-  };
-
-public:
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PromiseResolver)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PromiseResolver)
-
-  PromiseResolver(Promise* aPromise);
-  virtual ~PromiseResolver();
-
-  Promise* GetParentObject() const
-  {
-    return mPromise;
-  }
-
-  virtual JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
-
-  void Resolve(JSContext* aCx, const Optional<JS::Handle<JS::Value> >& aValue,
-               PromiseTaskSync aSync = AsyncTask);
-
-  void Reject(JSContext* aCx, const Optional<JS::Handle<JS::Value> >& aValue,
-              PromiseTaskSync aSync = AsyncTask);
-
-private:
-  void ResolveInternal(JSContext* aCx,
-                       const Optional<JS::Handle<JS::Value> >& aValue,
-                       PromiseTaskSync aSync = AsyncTask);
-
-  void RejectInternal(JSContext* aCx,
-                      const Optional<JS::Handle<JS::Value> >& aValue,
-                      PromiseTaskSync aSync = AsyncTask);
-
-  void RunTask(JS::Handle<JS::Value> aValue,
-               Promise::PromiseState aState, PromiseTaskSync aSync);
-
-  nsRefPtr<Promise> mPromise;
-
-  bool mResolvePending;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_PromiseResolver_h
--- a/dom/promise/moz.build
+++ b/dom/promise/moz.build
@@ -7,22 +7,20 @@
 TEST_DIRS += ['tests']
 
 XPIDL_MODULE = 'dom_promise'
 
 MODULE = 'dom'
 
 EXPORTS.mozilla.dom += [
     'Promise.h',
-    'PromiseResolver.h',
 ]
 
 CPP_SOURCES += [
     'Promise.cpp',
-    'PromiseResolver.cpp',
     'PromiseCallback.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 LIBXUL_LIBRARY = True
 
 LIBRARY_NAME = 'dompromise_s'
--- a/dom/promise/tests/test_bug883683.html
+++ b/dom/promise/tests/test_bug883683.html
@@ -18,20 +18,20 @@
 
 function runTest() {
   [{}, {}, {}, {}, {}].reduce(Promise.reject);
   ok(true, "No leaks with reject?");
 
   [{}, {}, {}, {}, {}].reduce(Promise.resolve);
   ok(true, "No leaks with resolve?");
 
-  [{}, {}, {}, {}, {}].reduce(function(a, b, c, d) { return new Promise(function(r) { throw a; }); });
+  [{}, {}, {}, {}, {}].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(r) { }); });
+  [{}, {}, {}, {}, {}].reduce(function(a, b, c, d) { return new Promise(function(r1, r2) { }); });
   ok(true, "No leaks with empty promise?");
 
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, runTest);
 // -->
--- a/dom/promise/tests/test_promise.html
+++ b/dom/promise/tests/test_promise.html
@@ -14,96 +14,95 @@
 
 </div>
 <pre id="test">
 <script type="application/javascript"><!--
 
 function promiseResolve() {
   ok(Promise, "Promise object should exist");
 
-  var promise = new Promise(function(resolver) {
-    ok(resolver, "PromiseResolver exists");
-    ok("reject" in resolver, "PromiseResolver.reject exists");
-    ok("resolve" in resolver, "PromiseResolver.resolve exists");
+  var promise = new Promise(function(resolve, reject) {
+    ok(resolve, "Promise.resolve exists");
+    ok(reject, "Promise.reject exists");
 
-    resolver.resolve(42);
+    resolve(42);
   }).then(function(what) {
     ok(true, "Then - resolveCb has been called");
     is(what, 42, "ResolveCb received 42");
     runTest();
   }, function() {
     ok(false, "Then - rejectCb has been called");
     runTest();
   });
 }
 
 function promiseReject() {
-  var promise = new Promise(function(resolver) {
-    resolver.reject(42);
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
   }).then(function(what) {
     ok(false, "Then - resolveCb has been called");
     runTest();
   }, function(what) {
     ok(true, "Then - rejectCb has been called");
     is(what, 42, "RejectCb received 42");
     runTest();
   });
 }
 
 function promiseException() {
-  var promise = new Promise(function(resolver) {
+  var promise = new Promise(function(resolve, reject) {
     throw 42;
   }).then(function(what) {
     ok(false, "Then - resolveCb has been called");
     runTest();
   }, function(what) {
     ok(true, "Then - rejectCb has been called");
     is(what, 42, "RejectCb received 42");
     runTest();
   });
 }
 
 function promiseGC() {
-  var resolver;
-  var promise = new Promise(function(r) {
-    resolver = r;
+  var resolve;
+  var promise = new Promise(function(r1, r2) {
+    resolve = r1;
   }).then(function(what) {
     ok(true, "Then - promise is still alive");
     runTest();
   });
 
   promise = null;
 
   SpecialPowers.gc();
   SpecialPowers.forceGC();
   SpecialPowers.forceCC();
 
-  resolver.resolve(42);
+  resolve(42);
 }
 
 function promiseAsync() {
   var global = "foo";
-  var f = new Promise(function(r) {
+  var f = new Promise(function(r1, r2) {
     is(global, "foo", "Global should be foo");
-    r.resolve(42);
+    r1(42);
     is(global, "foo", "Global should still be foo");
     setTimeout(function() {
       is(global, "bar", "Global should still be bar!");
       runTest();
     }, 0);
   }).then(function() {
     global = "bar";
   });
   is(global, "foo", "Global should still be foo (2)");
 }
 
 function promiseDoubleThen() {
   var steps = 0;
-  var promise = new Promise(function(resolver) {
-    resolver.resolve(42);
+  var promise = new Promise(function(r1, r2) {
+    r1(42);
   });
 
   promise.then(function(what) {
     ok(true, "Then.resolve has been called");
     is(what, 42, "Value == 42");
     steps++;
   }, function(what) {
     ok(false, "Then.reject has been called");
@@ -115,32 +114,32 @@ function promiseDoubleThen() {
     is(what, 42, "Value == 42");
     runTest();
   }, function(what) {
     ok(false, "Then.reject has been called");
   });
 }
 
 function promiseThenException() {
-  var promise = new Promise(function(resolver) {
-    resolver.resolve(42);
+  var promise = new Promise(function(resolve, reject) {
+    resolve(42);
   });
 
   promise.then(function(what) {
     ok(true, "Then.resolve has been called");
     throw "booh";
   }).catch(function(e) {
     ok(true, "window.onerror has been called!");
     runTest();
   });
 }
 
 function promiseThenCatchThen() {
-  var promise = new Promise(function(resolver) {
-    resolver.resolve(42);
+  var promise = new Promise(function(resolve, reject) {
+    resolve(42);
   });
 
   var promise2 = promise.then(function(what) {
     ok(true, "Then.resolve has been called");
     is(what, 42, "Value == 42");
     return what + 1;
   }, function(what) {
     ok(false, "Then.reject has been called");
@@ -161,18 +160,18 @@ function promiseThenCatchThen() {
     is(what, 44, "Value == 44");
     runTest();
   }, function(what) {
     ok(false, "Then.reject has been called");
   });
 }
 
 function promiseRejectThenCatchThen() {
-  var promise = new Promise(function(resolver) {
-    resolver.reject(42);
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
   });
 
   var promise2 = promise.then(function(what) {
     ok(false, "Then.resolve has been called");
   }, function(what) {
     ok(true, "Then.reject has been called");
     is(what, 42, "Value == 42");
     return what + 1;
@@ -189,18 +188,18 @@ function promiseRejectThenCatchThen() {
   }).then(function(what) {
     ok(true, "Then.resolve has been called");
     is(what, 44, "Value == 44");
     runTest();
   });
 }
 
 function promiseRejectThenCatchThen2() {
-  var promise = new Promise(function(resolver) {
-    resolver.reject(42);
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
   });
 
   promise.then(function(what) {
     ok(true, "Then.resolve has been called");
     is(what, 42, "Value == 42");
     return what+1;
   }).catch(function(what) {
     is(what, 42, "Value == 42");
@@ -209,18 +208,18 @@ function promiseRejectThenCatchThen2() {
   }).then(function(what) {
     ok(true, "Then.resolve has been called");
     is(what, 43, "Value == 43");
     runTest();
   });
 }
 
 function promiseRejectThenCatchExceptionThen() {
-  var promise = new Promise(function(resolver) {
-    resolver.reject(42);
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
   });
 
   promise.then(function(what) {
     ok(false, "Then.resolve has been called");
   }, function(what) {
     ok(true, "Then.reject has been called");
     is(what, 42, "Value == 42");
     throw(what + 1);
@@ -232,18 +231,18 @@ function promiseRejectThenCatchException
     ok(true, "Then.resolve has been called");
     is(what, 44, "Value == 44");
     runTest();
   });
 }
 
 function promiseThenCatchOrderingResolve() {
   var global = 0;
-  var f = new Promise(function(r) {
-    r.resolve(42);
+  var f = new Promise(function(r1, r2) {
+    r1(42);
   });
 
   f.then(function() {
     f.then(function() {
       global++;
     });
     f.catch(function() {
       global++;
@@ -255,18 +254,18 @@ function promiseThenCatchOrderingResolve
       is(global, 2, "Many steps... should return 2");
       runTest();
     }, 0);
   });
 }
 
 function promiseThenCatchOrderingReject() {
   var global = 0;
-  var f = new Promise(function(r) {
-    r.reject(42);
+  var f = new Promise(function(r1, r2) {
+    r2(42);
   })
 
   f.then(function() {}, function() {
     f.then(function() {
       global++;
     });
     f.catch(function() {
       global++;
@@ -277,61 +276,61 @@ function promiseThenCatchOrderingReject(
     setTimeout(function() {
       is(global, 2, "Many steps... should return 2");
       runTest();
     }, 0);
   });
 }
 
 function promiseNestedPromise() {
-  new Promise(function(resolver) {
-    resolver.resolve(new Promise(function(r) {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(resolve, reject) {
       ok(true, "Nested promise is executed");
-      r.resolve(42);
+      resolve(42);
     }));
   }).then(function(value) {
     is(value, 42, "Nested promise is executed and then == 42");
     runTest();
   });
 }
 
 function promiseNestedNestedPromise() {
-  new Promise(function(resolver) {
-    resolver.resolve(new Promise(function(r) {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(resolve, reject) {
       ok(true, "Nested promise is executed");
-      r.resolve(42);
+      resolve(42);
     }).then(function(what) { return what+1; }));
   }).then(function(value) {
     is(value, 43, "Nested promise is executed and then == 43");
     runTest();
   });
 }
 
 function promiseWrongNestedPromise() {
-  new Promise(function(resolver) {
-    resolver.resolve(new Promise(function(r) {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(r, r2) {
       ok(true, "Nested promise is executed");
-      r.resolve(42);
+      r(42);
     }));
-    resolver.reject(42);
+    reject(42);
   }).then(function(value) {
     is(value, 42, "Nested promise is executed and then == 42");
     runTest();
   }, function(value) {
      ok(false, "This is wrong");
   });
 }
 
 function promiseLoop() {
-  new Promise(function(resolver) {
-    resolver.resolve(new Promise(function(r) {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(r1, r2) {
       ok(true, "Nested promise is executed");
-      r.resolve(new Promise(function(r) {
+      r1(new Promise(function(r1, r2) {
         ok(true, "Nested nested promise is executed");
-        r.resolve(42);
+        r1(42);
       }));
     }));
   }).then(function(value) {
     is(value, 42, "Nested nested promise is executed and then == 42");
     runTest();
   }, function(value) {
      ok(false, "This is wrong");
   });
@@ -351,19 +350,19 @@ function promiseResolve() {
     is(what, 42, "Value == 42");
     runTest();
   }, function() {
     ok(false, "This should not be called");
   });
 }
 
 function promiseResolveNestedPromise() {
-  var promise = Promise.resolve(new Promise(function(r) {
+  var promise = Promise.resolve(new Promise(function(r, r2) {
     ok(true, "Nested promise is executed");
-    r.resolve(42);
+    r(42);
   }, function() {
     ok(false, "This should not be called");
   })).then(function(what) {
     is(what, 42, "Value == 42");
     runTest();
   }, function() {
     ok(false, "This should not be called");
   });
--- a/dom/promise/tests/test_resolve.html
+++ b/dom/promise/tests/test_resolve.html
@@ -39,23 +39,23 @@ function cbError() {
 function runTest() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
 
   var test = tests.pop();
 
-  new Promise(function(resolver) {
-    resolver.resolve(test);
+  new Promise(function(resolve, reject) {
+    resolve(test);
   }).then(function(what) {
     ok(test === what, "What is: " + what);
   }, cbError).then(function() {
-    new Promise(function(resolver) {
-      resolver.reject(test)
+    new Promise(function(resolve, reject) {
+      reject(test)
     }).then(cbError, function(what) {
       ok(test === what, "What is: " + what);
     }).then(runTest, cbError);
   });
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, runTest);
--- a/dom/tests/browser/test-console-api.html
+++ b/dom/tests/browser/test-console-api.html
@@ -44,17 +44,17 @@
       function testGroups() {
         console.groupCollapsed("a", "group");
         console.group("b", "group");
         console.groupEnd("b", "group");
       }
 
       function nativeCallback() {
         SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, function() {
-          new Promise(r => r.resolve(42)).then(console.log);
+          new Promise(function(resolve, reject) { resolve(42); }).then(console.log);
         });
       }
     </script>
   </head>
   <body>
     <h1>Console API Test Page</h1>
     <button onclick="test();">Log stuff</button>
     <button id="test-trace" onclick="foobar585956a('omg');">Test trace</button>
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -378,17 +378,16 @@ var interfaceNamesInGlobalScope =
     "PhoneNumberService",
     "Plugin",
     "PluginArray",
     "PopStateEvent",
     "PopupBlockedEvent",
     "ProcessingInstruction",
     "ProgressEvent",
     {name: "Promise", b2g: false, release: false},
-    {name: "PromiseResolver", b2g: false, release: false},
     "PropertyNodeList",
     "Range",
     "RecordErrorEvent",
     "Rect",
     "RGBColor",
     "RTCDataChannelEvent",
     "RTCPeerConnectionIceEvent",
     "Screen",
--- a/dom/webidl/Promise.webidl
+++ b/dom/webidl/Promise.webidl
@@ -2,24 +2,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/.
  *
  * The origin of this IDL file is
  * http://dom.spec.whatwg.org/#promises
  */
 
-[Func="mozilla::dom::Promise::EnabledForScope"]
-interface PromiseResolver {
-  // TODO bug 875289 - void fulfill(optional any value);
-  void resolve(optional any value);
-  void reject(optional any value);
-};
-
-callback PromiseInit = void (PromiseResolver resolver);
+// TODO We use object instead Function.  There is an open issue on WebIDL to
+// have different types for "platform-provided function" and "user-provided
+// function"; for now, we just use "object".
+callback PromiseInit = void (object resolve, object reject);
 callback AnyCallback = any (optional any value);
 
 [Func="mozilla::dom::Promise::EnabledForScope", Constructor(PromiseInit init)]
 interface Promise {
   // TODO bug 875289 - static Promise fulfill(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