dom/promise/Promise.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/promise/Promise.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1122 @@
     1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/dom/Promise.h"
    1.11 +
    1.12 +#include "jsfriendapi.h"
    1.13 +#include "mozilla/dom/OwningNonNull.h"
    1.14 +#include "mozilla/dom/PromiseBinding.h"
    1.15 +#include "mozilla/CycleCollectedJSRuntime.h"
    1.16 +#include "mozilla/Preferences.h"
    1.17 +#include "PromiseCallback.h"
    1.18 +#include "PromiseNativeHandler.h"
    1.19 +#include "nsContentUtils.h"
    1.20 +#include "WorkerPrivate.h"
    1.21 +#include "WorkerRunnable.h"
    1.22 +#include "nsJSPrincipals.h"
    1.23 +#include "nsJSUtils.h"
    1.24 +#include "nsPIDOMWindow.h"
    1.25 +#include "nsJSEnvironment.h"
    1.26 +
    1.27 +namespace mozilla {
    1.28 +namespace dom {
    1.29 +
    1.30 +using namespace workers;
    1.31 +
    1.32 +NS_IMPL_ISUPPORTS0(PromiseNativeHandler)
    1.33 +
    1.34 +// PromiseTask
    1.35 +
    1.36 +// This class processes the promise's callbacks with promise's result.
    1.37 +class PromiseTask MOZ_FINAL : public nsRunnable
    1.38 +{
    1.39 +public:
    1.40 +  PromiseTask(Promise* aPromise)
    1.41 +    : mPromise(aPromise)
    1.42 +  {
    1.43 +    MOZ_ASSERT(aPromise);
    1.44 +    MOZ_COUNT_CTOR(PromiseTask);
    1.45 +  }
    1.46 +
    1.47 +  ~PromiseTask()
    1.48 +  {
    1.49 +    MOZ_COUNT_DTOR(PromiseTask);
    1.50 +  }
    1.51 +
    1.52 +  NS_IMETHOD Run()
    1.53 +  {
    1.54 +    mPromise->mTaskPending = false;
    1.55 +    mPromise->RunTask();
    1.56 +    return NS_OK;
    1.57 +  }
    1.58 +
    1.59 +private:
    1.60 +  nsRefPtr<Promise> mPromise;
    1.61 +};
    1.62 +
    1.63 +class WorkerPromiseTask MOZ_FINAL : public WorkerSameThreadRunnable
    1.64 +{
    1.65 +public:
    1.66 +  WorkerPromiseTask(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
    1.67 +    : WorkerSameThreadRunnable(aWorkerPrivate)
    1.68 +    , mPromise(aPromise)
    1.69 +  {
    1.70 +    MOZ_ASSERT(aPromise);
    1.71 +    MOZ_COUNT_CTOR(WorkerPromiseTask);
    1.72 +  }
    1.73 +
    1.74 +  ~WorkerPromiseTask()
    1.75 +  {
    1.76 +    MOZ_COUNT_DTOR(WorkerPromiseTask);
    1.77 +  }
    1.78 +
    1.79 +  bool
    1.80 +  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1.81 +  {
    1.82 +    mPromise->mTaskPending = false;
    1.83 +    mPromise->RunTask();
    1.84 +    return true;
    1.85 +  }
    1.86 +
    1.87 +private:
    1.88 +  nsRefPtr<Promise> mPromise;
    1.89 +};
    1.90 +
    1.91 +class PromiseResolverMixin
    1.92 +{
    1.93 +public:
    1.94 +  PromiseResolverMixin(Promise* aPromise,
    1.95 +                       JS::Handle<JS::Value> aValue,
    1.96 +                       Promise::PromiseState aState)
    1.97 +    : mPromise(aPromise)
    1.98 +    , mValue(CycleCollectedJSRuntime::Get()->Runtime(), aValue)
    1.99 +    , mState(aState)
   1.100 +  {
   1.101 +    MOZ_ASSERT(aPromise);
   1.102 +    MOZ_ASSERT(mState != Promise::Pending);
   1.103 +    MOZ_COUNT_CTOR(PromiseResolverMixin);
   1.104 +  }
   1.105 +
   1.106 +  virtual ~PromiseResolverMixin()
   1.107 +  {
   1.108 +    NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
   1.109 +    MOZ_COUNT_DTOR(PromiseResolverMixin);
   1.110 +  }
   1.111 +
   1.112 +protected:
   1.113 +  void
   1.114 +  RunInternal()
   1.115 +  {
   1.116 +    NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
   1.117 +    mPromise->RunResolveTask(
   1.118 +      JS::Handle<JS::Value>::fromMarkedLocation(mValue.address()),
   1.119 +      mState, Promise::SyncTask);
   1.120 +  }
   1.121 +
   1.122 +private:
   1.123 +  nsRefPtr<Promise> mPromise;
   1.124 +  JS::PersistentRooted<JS::Value> mValue;
   1.125 +  Promise::PromiseState mState;
   1.126 +  NS_DECL_OWNINGTHREAD;
   1.127 +};
   1.128 +
   1.129 +// This class processes the promise's callbacks with promise's result.
   1.130 +class PromiseResolverTask MOZ_FINAL : public nsRunnable,
   1.131 +                                      public PromiseResolverMixin
   1.132 +{
   1.133 +public:
   1.134 +  PromiseResolverTask(Promise* aPromise,
   1.135 +                      JS::Handle<JS::Value> aValue,
   1.136 +                      Promise::PromiseState aState)
   1.137 +    : PromiseResolverMixin(aPromise, aValue, aState)
   1.138 +  {}
   1.139 +
   1.140 +  ~PromiseResolverTask()
   1.141 +  {}
   1.142 +
   1.143 +  NS_IMETHOD Run()
   1.144 +  {
   1.145 +    RunInternal();
   1.146 +    return NS_OK;
   1.147 +  }
   1.148 +};
   1.149 +
   1.150 +class WorkerPromiseResolverTask MOZ_FINAL : public WorkerSameThreadRunnable,
   1.151 +                                            public PromiseResolverMixin
   1.152 +{
   1.153 +public:
   1.154 +  WorkerPromiseResolverTask(WorkerPrivate* aWorkerPrivate,
   1.155 +                            Promise* aPromise,
   1.156 +                            JS::Handle<JS::Value> aValue,
   1.157 +                            Promise::PromiseState aState)
   1.158 +    : WorkerSameThreadRunnable(aWorkerPrivate),
   1.159 +      PromiseResolverMixin(aPromise, aValue, aState)
   1.160 +  {}
   1.161 +
   1.162 +  ~WorkerPromiseResolverTask()
   1.163 +  {}
   1.164 +
   1.165 +  bool
   1.166 +  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   1.167 +  {
   1.168 +    RunInternal();
   1.169 +    return true;
   1.170 +  }
   1.171 +};
   1.172 +
   1.173 +// Promise
   1.174 +
   1.175 +NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
   1.176 +
   1.177 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
   1.178 +  tmp->MaybeReportRejectedOnce();
   1.179 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
   1.180 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks)
   1.181 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectCallbacks)
   1.182 +  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   1.183 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1.184 +
   1.185 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
   1.186 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
   1.187 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks)
   1.188 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks)
   1.189 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   1.190 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.191 +
   1.192 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
   1.193 +  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
   1.194 +  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   1.195 +NS_IMPL_CYCLE_COLLECTION_TRACE_END
   1.196 +
   1.197 +NS_IMPL_CYCLE_COLLECTING_ADDREF(Promise)
   1.198 +NS_IMPL_CYCLE_COLLECTING_RELEASE(Promise)
   1.199 +
   1.200 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Promise)
   1.201 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   1.202 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.203 +NS_INTERFACE_MAP_END
   1.204 +
   1.205 +Promise::Promise(nsIGlobalObject* aGlobal)
   1.206 +  : mGlobal(aGlobal)
   1.207 +  , mResult(JS::UndefinedValue())
   1.208 +  , mState(Pending)
   1.209 +  , mTaskPending(false)
   1.210 +  , mHadRejectCallback(false)
   1.211 +  , mResolvePending(false)
   1.212 +{
   1.213 +  MOZ_ASSERT(mGlobal);
   1.214 +
   1.215 +  mozilla::HoldJSObjects(this);
   1.216 +  SetIsDOMBinding();
   1.217 +}
   1.218 +
   1.219 +Promise::~Promise()
   1.220 +{
   1.221 +  MaybeReportRejectedOnce();
   1.222 +  mozilla::DropJSObjects(this);
   1.223 +}
   1.224 +
   1.225 +JSObject*
   1.226 +Promise::WrapObject(JSContext* aCx)
   1.227 +{
   1.228 +  return PromiseBinding::Wrap(aCx, this);
   1.229 +}
   1.230 +
   1.231 +JSObject*
   1.232 +Promise::GetOrCreateWrapper(JSContext* aCx)
   1.233 +{
   1.234 +  if (JSObject* wrapper = GetWrapper()) {
   1.235 +    return wrapper;
   1.236 +  }
   1.237 +
   1.238 +  nsIGlobalObject* global = GetParentObject();
   1.239 +  MOZ_ASSERT(global);
   1.240 +
   1.241 +  JS::Rooted<JSObject*> scope(aCx, global->GetGlobalJSObject());
   1.242 +  if (!scope) {
   1.243 +    JS_ReportError(aCx, "can't get scope");
   1.244 +    return nullptr;
   1.245 +  }
   1.246 +
   1.247 +  JSAutoCompartment ac(aCx, scope);
   1.248 +
   1.249 +  JS::Rooted<JS::Value> val(aCx);
   1.250 +  if (!WrapNewBindingObject(aCx, this, &val)) {
   1.251 +    MOZ_ASSERT(JS_IsExceptionPending(aCx));
   1.252 +    return nullptr;
   1.253 +  }
   1.254 +
   1.255 +  return GetWrapper();
   1.256 +}
   1.257 +
   1.258 +void
   1.259 +Promise::MaybeResolve(JSContext* aCx,
   1.260 +                      JS::Handle<JS::Value> aValue)
   1.261 +{
   1.262 +  MaybeResolveInternal(aCx, aValue);
   1.263 +}
   1.264 +
   1.265 +void
   1.266 +Promise::MaybeReject(JSContext* aCx,
   1.267 +                     JS::Handle<JS::Value> aValue)
   1.268 +{
   1.269 +  MaybeRejectInternal(aCx, aValue);
   1.270 +}
   1.271 +
   1.272 +enum {
   1.273 +  SLOT_PROMISE = 0,
   1.274 +  SLOT_DATA
   1.275 +};
   1.276 +
   1.277 +/* static */ bool
   1.278 +Promise::JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   1.279 +{
   1.280 +  JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
   1.281 +
   1.282 +  JS::Rooted<JS::Value> v(aCx,
   1.283 +                          js::GetFunctionNativeReserved(&args.callee(),
   1.284 +                                                        SLOT_PROMISE));
   1.285 +  MOZ_ASSERT(v.isObject());
   1.286 +
   1.287 +  Promise* promise;
   1.288 +  if (NS_FAILED(UNWRAP_OBJECT(Promise, &v.toObject(), promise))) {
   1.289 +    return Throw(aCx, NS_ERROR_UNEXPECTED);
   1.290 +  }
   1.291 +
   1.292 +  v = js::GetFunctionNativeReserved(&args.callee(), SLOT_DATA);
   1.293 +  PromiseCallback::Task task = static_cast<PromiseCallback::Task>(v.toInt32());
   1.294 +
   1.295 +  if (task == PromiseCallback::Resolve) {
   1.296 +    promise->MaybeResolveInternal(aCx, args.get(0));
   1.297 +  } else {
   1.298 +    promise->MaybeRejectInternal(aCx, args.get(0));
   1.299 +  }
   1.300 +
   1.301 +  return true;
   1.302 +}
   1.303 +
   1.304 +/*
   1.305 + * Utilities for thenable callbacks.
   1.306 + *
   1.307 + * A thenable is a { then: function(resolve, reject) { } }.
   1.308 + * `then` is called with a resolve and reject callback pair.
   1.309 + * Since only one of these should be called at most once (first call wins), the
   1.310 + * two keep a reference to each other in SLOT_DATA. When either of them is
   1.311 + * called, the references are cleared. Further calls are ignored.
   1.312 + */
   1.313 +namespace {
   1.314 +void
   1.315 +LinkThenableCallables(JSContext* aCx, JS::Handle<JSObject*> aResolveFunc,
   1.316 +                      JS::Handle<JSObject*> aRejectFunc)
   1.317 +{
   1.318 +  js::SetFunctionNativeReserved(aResolveFunc, SLOT_DATA,
   1.319 +                                JS::ObjectValue(*aRejectFunc));
   1.320 +  js::SetFunctionNativeReserved(aRejectFunc, SLOT_DATA,
   1.321 +                                JS::ObjectValue(*aResolveFunc));
   1.322 +}
   1.323 +
   1.324 +/*
   1.325 + * Returns false if callback was already called before, otherwise breaks the
   1.326 + * links and returns true.
   1.327 + */
   1.328 +bool
   1.329 +MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle<JSObject*> aFunc)
   1.330 +{
   1.331 +  JS::Value otherFuncVal = js::GetFunctionNativeReserved(aFunc, SLOT_DATA);
   1.332 +
   1.333 +  if (!otherFuncVal.isObject()) {
   1.334 +    return false;
   1.335 +  }
   1.336 +
   1.337 +  JSObject* otherFuncObj = &otherFuncVal.toObject();
   1.338 +  MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, SLOT_DATA).isObject());
   1.339 +
   1.340 +  // Break both references.
   1.341 +  js::SetFunctionNativeReserved(aFunc, SLOT_DATA, JS::UndefinedValue());
   1.342 +  js::SetFunctionNativeReserved(otherFuncObj, SLOT_DATA, JS::UndefinedValue());
   1.343 +
   1.344 +  return true;
   1.345 +}
   1.346 +
   1.347 +Promise*
   1.348 +GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc)
   1.349 +{
   1.350 +  JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, SLOT_PROMISE);
   1.351 +
   1.352 +  MOZ_ASSERT(promiseVal.isObject());
   1.353 +
   1.354 +  Promise* promise;
   1.355 +  UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise);
   1.356 +  return promise;
   1.357 +}
   1.358 +};
   1.359 +
   1.360 +/*
   1.361 + * Common bits of (JSCallbackThenableResolver/JSCallbackThenableRejecter).
   1.362 + * Resolves/rejects the Promise if it is ok to do so, based on whether either of
   1.363 + * the callbacks have been called before or not.
   1.364 + */
   1.365 +/* static */ bool
   1.366 +Promise::ThenableResolverCommon(JSContext* aCx, uint32_t aTask,
   1.367 +                                unsigned aArgc, JS::Value* aVp)
   1.368 +{
   1.369 +  JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
   1.370 +  JS::Rooted<JSObject*> thisFunc(aCx, &args.callee());
   1.371 +  if (!MarkAsCalledIfNotCalledBefore(aCx, thisFunc)) {
   1.372 +    // A function from this pair has been called before.
   1.373 +    return true;
   1.374 +  }
   1.375 +
   1.376 +  Promise* promise = GetPromise(aCx, thisFunc);
   1.377 +  MOZ_ASSERT(promise);
   1.378 +
   1.379 +  if (aTask == PromiseCallback::Resolve) {
   1.380 +    promise->ResolveInternal(aCx, args.get(0), SyncTask);
   1.381 +  } else {
   1.382 +    promise->RejectInternal(aCx, args.get(0), SyncTask);
   1.383 +  }
   1.384 +  return true;
   1.385 +}
   1.386 +
   1.387 +/* static */ bool
   1.388 +Promise::JSCallbackThenableResolver(JSContext* aCx,
   1.389 +                                    unsigned aArgc, JS::Value* aVp)
   1.390 +{
   1.391 +  return ThenableResolverCommon(aCx, PromiseCallback::Resolve, aArgc, aVp);
   1.392 +}
   1.393 +
   1.394 +/* static */ bool
   1.395 +Promise::JSCallbackThenableRejecter(JSContext* aCx,
   1.396 +                                    unsigned aArgc, JS::Value* aVp)
   1.397 +{
   1.398 +  return ThenableResolverCommon(aCx, PromiseCallback::Reject, aArgc, aVp);
   1.399 +}
   1.400 +
   1.401 +/* static */ JSObject*
   1.402 +Promise::CreateFunction(JSContext* aCx, JSObject* aParent, Promise* aPromise,
   1.403 +                        int32_t aTask)
   1.404 +{
   1.405 +  JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback,
   1.406 +                                                 1 /* nargs */, 0 /* flags */,
   1.407 +                                                 aParent, nullptr);
   1.408 +  if (!func) {
   1.409 +    return nullptr;
   1.410 +  }
   1.411 +
   1.412 +  JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
   1.413 +
   1.414 +  JS::Rooted<JS::Value> promiseObj(aCx);
   1.415 +  if (!dom::WrapNewBindingObject(aCx, aPromise, &promiseObj)) {
   1.416 +    return nullptr;
   1.417 +  }
   1.418 +
   1.419 +  js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj);
   1.420 +  js::SetFunctionNativeReserved(obj, SLOT_DATA, JS::Int32Value(aTask));
   1.421 +
   1.422 +  return obj;
   1.423 +}
   1.424 +
   1.425 +/* static */ JSObject*
   1.426 +Promise::CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask)
   1.427 +{
   1.428 +  JSNative whichFunc =
   1.429 +    aTask == PromiseCallback::Resolve ? JSCallbackThenableResolver :
   1.430 +                                        JSCallbackThenableRejecter ;
   1.431 +
   1.432 +  JSFunction* func = js::NewFunctionWithReserved(aCx, whichFunc,
   1.433 +                                                 1 /* nargs */, 0 /* flags */,
   1.434 +                                                 nullptr, nullptr);
   1.435 +  if (!func) {
   1.436 +    return nullptr;
   1.437 +  }
   1.438 +
   1.439 +  JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
   1.440 +
   1.441 +  JS::Rooted<JS::Value> promiseObj(aCx);
   1.442 +  if (!dom::WrapNewBindingObject(aCx, aPromise, &promiseObj)) {
   1.443 +    return nullptr;
   1.444 +  }
   1.445 +
   1.446 +  js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj);
   1.447 +
   1.448 +  return obj;
   1.449 +}
   1.450 +
   1.451 +/* static */ already_AddRefed<Promise>
   1.452 +Promise::Constructor(const GlobalObject& aGlobal,
   1.453 +                     PromiseInit& aInit, ErrorResult& aRv)
   1.454 +{
   1.455 +  JSContext* cx = aGlobal.GetContext();
   1.456 +
   1.457 +  nsCOMPtr<nsIGlobalObject> global;
   1.458 +  global = do_QueryInterface(aGlobal.GetAsSupports());
   1.459 +  if (!global) {
   1.460 +    aRv.Throw(NS_ERROR_UNEXPECTED);
   1.461 +    return nullptr;
   1.462 +  }
   1.463 +
   1.464 +  nsRefPtr<Promise> promise = new Promise(global);
   1.465 +
   1.466 +  JS::Rooted<JSObject*> resolveFunc(cx,
   1.467 +                                    CreateFunction(cx, aGlobal.Get(), promise,
   1.468 +                                                   PromiseCallback::Resolve));
   1.469 +  if (!resolveFunc) {
   1.470 +    aRv.Throw(NS_ERROR_UNEXPECTED);
   1.471 +    return nullptr;
   1.472 +  }
   1.473 +
   1.474 +  JS::Rooted<JSObject*> rejectFunc(cx,
   1.475 +                                   CreateFunction(cx, aGlobal.Get(), promise,
   1.476 +                                                  PromiseCallback::Reject));
   1.477 +  if (!rejectFunc) {
   1.478 +    aRv.Throw(NS_ERROR_UNEXPECTED);
   1.479 +    return nullptr;
   1.480 +  }
   1.481 +
   1.482 +  aInit.Call(resolveFunc, rejectFunc, aRv, CallbackObject::eRethrowExceptions);
   1.483 +  aRv.WouldReportJSException();
   1.484 +
   1.485 +  if (aRv.IsJSException()) {
   1.486 +    JS::Rooted<JS::Value> value(cx);
   1.487 +    aRv.StealJSException(cx, &value);
   1.488 +
   1.489 +    // we want the same behavior as this JS implementation:
   1.490 +    // function Promise(arg) { try { arg(a, b); } catch (e) { this.reject(e); }}
   1.491 +    if (!JS_WrapValue(cx, &value)) {
   1.492 +      aRv.Throw(NS_ERROR_UNEXPECTED);
   1.493 +      return nullptr;
   1.494 +    }
   1.495 +
   1.496 +    promise->MaybeRejectInternal(cx, value);
   1.497 +  }
   1.498 +
   1.499 +  return promise.forget();
   1.500 +}
   1.501 +
   1.502 +/* static */ already_AddRefed<Promise>
   1.503 +Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
   1.504 +                 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
   1.505 +{
   1.506 +  // If a Promise was passed, just return it.
   1.507 +  if (aValue.isObject()) {
   1.508 +    JS::Rooted<JSObject*> valueObj(aCx, &aValue.toObject());
   1.509 +    Promise* nextPromise;
   1.510 +    nsresult rv = UNWRAP_OBJECT(Promise, valueObj, nextPromise);
   1.511 +
   1.512 +    if (NS_SUCCEEDED(rv)) {
   1.513 +      nsRefPtr<Promise> addRefed = nextPromise;
   1.514 +      return addRefed.forget();
   1.515 +    }
   1.516 +  }
   1.517 +
   1.518 +  nsCOMPtr<nsIGlobalObject> global =
   1.519 +    do_QueryInterface(aGlobal.GetAsSupports());
   1.520 +  if (!global) {
   1.521 +    aRv.Throw(NS_ERROR_UNEXPECTED);
   1.522 +    return nullptr;
   1.523 +  }
   1.524 +
   1.525 +  return Resolve(global, aCx, aValue, aRv);
   1.526 +}
   1.527 +
   1.528 +/* static */ already_AddRefed<Promise>
   1.529 +Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
   1.530 +                 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
   1.531 +{
   1.532 +  nsRefPtr<Promise> promise = new Promise(aGlobal);
   1.533 +
   1.534 +  promise->MaybeResolveInternal(aCx, aValue);
   1.535 +  return promise.forget();
   1.536 +}
   1.537 +
   1.538 +/* static */ already_AddRefed<Promise>
   1.539 +Promise::Reject(const GlobalObject& aGlobal, JSContext* aCx,
   1.540 +                JS::Handle<JS::Value> aValue, ErrorResult& aRv)
   1.541 +{
   1.542 +  nsCOMPtr<nsIGlobalObject> global =
   1.543 +    do_QueryInterface(aGlobal.GetAsSupports());
   1.544 +  if (!global) {
   1.545 +    aRv.Throw(NS_ERROR_UNEXPECTED);
   1.546 +    return nullptr;
   1.547 +  }
   1.548 +
   1.549 +  return Reject(global, aCx, aValue, aRv);
   1.550 +}
   1.551 +
   1.552 +/* static */ already_AddRefed<Promise>
   1.553 +Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
   1.554 +                JS::Handle<JS::Value> aValue, ErrorResult& aRv)
   1.555 +{
   1.556 +  nsRefPtr<Promise> promise = new Promise(aGlobal);
   1.557 +
   1.558 +  promise->MaybeRejectInternal(aCx, aValue);
   1.559 +  return promise.forget();
   1.560 +}
   1.561 +
   1.562 +already_AddRefed<Promise>
   1.563 +Promise::Then(JSContext* aCx, AnyCallback* aResolveCallback,
   1.564 +              AnyCallback* aRejectCallback)
   1.565 +{
   1.566 +  nsRefPtr<Promise> promise = new Promise(GetParentObject());
   1.567 +
   1.568 +  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
   1.569 +
   1.570 +  nsRefPtr<PromiseCallback> resolveCb =
   1.571 +    PromiseCallback::Factory(promise, global, aResolveCallback,
   1.572 +                             PromiseCallback::Resolve);
   1.573 +
   1.574 +  nsRefPtr<PromiseCallback> rejectCb =
   1.575 +    PromiseCallback::Factory(promise, global, aRejectCallback,
   1.576 +                             PromiseCallback::Reject);
   1.577 +
   1.578 +  AppendCallbacks(resolveCb, rejectCb);
   1.579 +
   1.580 +  return promise.forget();
   1.581 +}
   1.582 +
   1.583 +already_AddRefed<Promise>
   1.584 +Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback)
   1.585 +{
   1.586 +  nsRefPtr<AnyCallback> resolveCb;
   1.587 +  return Then(aCx, resolveCb, aRejectCallback);
   1.588 +}
   1.589 +
   1.590 +/**
   1.591 + * The CountdownHolder class encapsulates Promise.all countdown functions and
   1.592 + * the countdown holder parts of the Promises spec. It maintains the result
   1.593 + * array and AllResolveHandlers use SetValue() to set the array indices.
   1.594 + */
   1.595 +class CountdownHolder MOZ_FINAL : public nsISupports
   1.596 +{
   1.597 +public:
   1.598 +  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   1.599 +  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CountdownHolder)
   1.600 +
   1.601 +  CountdownHolder(const GlobalObject& aGlobal, Promise* aPromise,
   1.602 +                  uint32_t aCountdown)
   1.603 +    : mPromise(aPromise), mCountdown(aCountdown)
   1.604 +  {
   1.605 +    MOZ_ASSERT(aCountdown != 0);
   1.606 +    JSContext* cx = aGlobal.GetContext();
   1.607 +
   1.608 +    // The only time aGlobal.GetContext() and aGlobal.Get() are not
   1.609 +    // same-compartment is when we're called via Xrays, and in that situation we
   1.610 +    // in fact want to create the array in the callee compartment
   1.611 +
   1.612 +    JSAutoCompartment ac(cx, aGlobal.Get());
   1.613 +    mValues = JS_NewArrayObject(cx, aCountdown);
   1.614 +    mozilla::HoldJSObjects(this);
   1.615 +  }
   1.616 +
   1.617 +  ~CountdownHolder()
   1.618 +  {
   1.619 +    mozilla::DropJSObjects(this);
   1.620 +  }
   1.621 +
   1.622 +  void SetValue(uint32_t index, const JS::Handle<JS::Value> aValue)
   1.623 +  {
   1.624 +    MOZ_ASSERT(mCountdown > 0);
   1.625 +
   1.626 +    ThreadsafeAutoSafeJSContext cx;
   1.627 +    JSAutoCompartment ac(cx, mValues);
   1.628 +    {
   1.629 +
   1.630 +      AutoDontReportUncaught silenceReporting(cx);
   1.631 +      JS::Rooted<JS::Value> value(cx, aValue);
   1.632 +      if (!JS_WrapValue(cx, &value) ||
   1.633 +          !JS_DefineElement(cx, mValues, index, value, nullptr, nullptr,
   1.634 +                            JSPROP_ENUMERATE)) {
   1.635 +        MOZ_ASSERT(JS_IsExceptionPending(cx));
   1.636 +        JS::Rooted<JS::Value> exn(cx);
   1.637 +        JS_GetPendingException(cx, &exn);
   1.638 +
   1.639 +        mPromise->MaybeReject(cx, exn);
   1.640 +      }
   1.641 +    }
   1.642 +
   1.643 +    --mCountdown;
   1.644 +    if (mCountdown == 0) {
   1.645 +      JS::Rooted<JS::Value> result(cx, JS::ObjectValue(*mValues));
   1.646 +      mPromise->MaybeResolve(cx, result);
   1.647 +    }
   1.648 +  }
   1.649 +
   1.650 +private:
   1.651 +  nsRefPtr<Promise> mPromise;
   1.652 +  uint32_t mCountdown;
   1.653 +  JS::Heap<JSObject*> mValues;
   1.654 +};
   1.655 +
   1.656 +NS_IMPL_CYCLE_COLLECTING_ADDREF(CountdownHolder)
   1.657 +NS_IMPL_CYCLE_COLLECTING_RELEASE(CountdownHolder)
   1.658 +NS_IMPL_CYCLE_COLLECTION_CLASS(CountdownHolder)
   1.659 +
   1.660 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CountdownHolder)
   1.661 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.662 +NS_INTERFACE_MAP_END
   1.663 +
   1.664 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CountdownHolder)
   1.665 +  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mValues)
   1.666 +NS_IMPL_CYCLE_COLLECTION_TRACE_END
   1.667 +
   1.668 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CountdownHolder)
   1.669 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   1.670 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
   1.671 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.672 +
   1.673 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CountdownHolder)
   1.674 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
   1.675 +  tmp->mValues = nullptr;
   1.676 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1.677 +
   1.678 +/**
   1.679 + * An AllResolveHandler is the per-promise part of the Promise.all() algorithm.
   1.680 + * Every Promise in the handler is handed an instance of this as a resolution
   1.681 + * handler and it sets the relevant index in the CountdownHolder.
   1.682 + */
   1.683 +class AllResolveHandler MOZ_FINAL : public PromiseNativeHandler
   1.684 +{
   1.685 +public:
   1.686 +  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   1.687 +  NS_DECL_CYCLE_COLLECTION_CLASS(AllResolveHandler)
   1.688 +
   1.689 +  AllResolveHandler(CountdownHolder* aHolder, uint32_t aIndex)
   1.690 +    : mCountdownHolder(aHolder), mIndex(aIndex)
   1.691 +  {
   1.692 +    MOZ_ASSERT(aHolder);
   1.693 +  }
   1.694 +
   1.695 +  ~AllResolveHandler()
   1.696 +  {
   1.697 +  }
   1.698 +
   1.699 +  void
   1.700 +  ResolvedCallback(JS::Handle<JS::Value> aValue)
   1.701 +  {
   1.702 +    mCountdownHolder->SetValue(mIndex, aValue);
   1.703 +  }
   1.704 +
   1.705 +  void
   1.706 +  RejectedCallback(JS::Handle<JS::Value> aValue)
   1.707 +  {
   1.708 +    // Should never be attached to Promise as a reject handler.
   1.709 +    MOZ_ASSERT(false, "AllResolveHandler should never be attached to a Promise's reject handler!");
   1.710 +  }
   1.711 +
   1.712 +private:
   1.713 +  nsRefPtr<CountdownHolder> mCountdownHolder;
   1.714 +  uint32_t mIndex;
   1.715 +};
   1.716 +
   1.717 +NS_IMPL_CYCLE_COLLECTING_ADDREF(AllResolveHandler)
   1.718 +NS_IMPL_CYCLE_COLLECTING_RELEASE(AllResolveHandler)
   1.719 +
   1.720 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AllResolveHandler)
   1.721 +NS_INTERFACE_MAP_END_INHERITING(PromiseNativeHandler)
   1.722 +
   1.723 +NS_IMPL_CYCLE_COLLECTION(AllResolveHandler, mCountdownHolder)
   1.724 +
   1.725 +/* static */ already_AddRefed<Promise>
   1.726 +Promise::All(const GlobalObject& aGlobal, JSContext* aCx,
   1.727 +             const Sequence<JS::Value>& aIterable, ErrorResult& aRv)
   1.728 +{
   1.729 +  nsCOMPtr<nsIGlobalObject> global =
   1.730 +    do_QueryInterface(aGlobal.GetAsSupports());
   1.731 +  if (!global) {
   1.732 +    aRv.Throw(NS_ERROR_UNEXPECTED);
   1.733 +    return nullptr;
   1.734 +  }
   1.735 +
   1.736 +  if (aIterable.Length() == 0) {
   1.737 +    JS::Rooted<JSObject*> empty(aCx, JS_NewArrayObject(aCx, 0));
   1.738 +    if (!empty) {
   1.739 +      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1.740 +      return nullptr;
   1.741 +    }
   1.742 +    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*empty));
   1.743 +    return Promise::Resolve(aGlobal, aCx, value, aRv);
   1.744 +  }
   1.745 +
   1.746 +  nsRefPtr<Promise> promise = new Promise(global);
   1.747 +  nsRefPtr<CountdownHolder> holder =
   1.748 +    new CountdownHolder(aGlobal, promise, aIterable.Length());
   1.749 +
   1.750 +  JS::Rooted<JSObject*> obj(aCx, JS::CurrentGlobalOrNull(aCx));
   1.751 +  if (!obj) {
   1.752 +    aRv.Throw(NS_ERROR_UNEXPECTED);
   1.753 +    return nullptr;
   1.754 +  }
   1.755 +
   1.756 +  nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(promise, obj);
   1.757 +
   1.758 +  for (uint32_t i = 0; i < aIterable.Length(); ++i) {
   1.759 +    JS::Rooted<JS::Value> value(aCx, aIterable.ElementAt(i));
   1.760 +    nsRefPtr<Promise> nextPromise = Promise::Resolve(aGlobal, aCx, value, aRv);
   1.761 +
   1.762 +    MOZ_ASSERT(!aRv.Failed());
   1.763 +
   1.764 +    nsRefPtr<PromiseNativeHandler> resolveHandler =
   1.765 +      new AllResolveHandler(holder, i);
   1.766 +
   1.767 +    nsRefPtr<PromiseCallback> resolveCb =
   1.768 +      new NativePromiseCallback(resolveHandler, Resolved);
   1.769 +    // Every promise gets its own resolve callback, which will set the right
   1.770 +    // index in the array to the resolution value.
   1.771 +    nextPromise->AppendCallbacks(resolveCb, rejectCb);
   1.772 +  }
   1.773 +
   1.774 +  return promise.forget();
   1.775 +}
   1.776 +
   1.777 +/* static */ already_AddRefed<Promise>
   1.778 +Promise::Race(const GlobalObject& aGlobal, JSContext* aCx,
   1.779 +              const Sequence<JS::Value>& aIterable, ErrorResult& aRv)
   1.780 +{
   1.781 +  nsCOMPtr<nsIGlobalObject> global =
   1.782 +    do_QueryInterface(aGlobal.GetAsSupports());
   1.783 +  if (!global) {
   1.784 +    aRv.Throw(NS_ERROR_UNEXPECTED);
   1.785 +    return nullptr;
   1.786 +  }
   1.787 +
   1.788 +  JS::Rooted<JSObject*> obj(aCx, JS::CurrentGlobalOrNull(aCx));
   1.789 +  if (!obj) {
   1.790 +    aRv.Throw(NS_ERROR_UNEXPECTED);
   1.791 +    return nullptr;
   1.792 +  }
   1.793 +
   1.794 +  nsRefPtr<Promise> promise = new Promise(global);
   1.795 +
   1.796 +  nsRefPtr<PromiseCallback> resolveCb =
   1.797 +    new ResolvePromiseCallback(promise, obj);
   1.798 +
   1.799 +  nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(promise, obj);
   1.800 +
   1.801 +  for (uint32_t i = 0; i < aIterable.Length(); ++i) {
   1.802 +    JS::Rooted<JS::Value> value(aCx, aIterable.ElementAt(i));
   1.803 +    nsRefPtr<Promise> nextPromise = Promise::Resolve(aGlobal, aCx, value, aRv);
   1.804 +    // According to spec, Resolve can throw, but our implementation never does.
   1.805 +    // Well it does when window isn't passed on the main thread, but that is an
   1.806 +    // implementation detail which should never be reached since we are checking
   1.807 +    // for window above. Remove this when subclassing is supported.
   1.808 +    MOZ_ASSERT(!aRv.Failed());
   1.809 +    nextPromise->AppendCallbacks(resolveCb, rejectCb);
   1.810 +  }
   1.811 +
   1.812 +  return promise.forget();
   1.813 +}
   1.814 +
   1.815 +void
   1.816 +Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
   1.817 +{
   1.818 +  nsRefPtr<PromiseCallback> resolveCb =
   1.819 +    new NativePromiseCallback(aRunnable, Resolved);
   1.820 +
   1.821 +  nsRefPtr<PromiseCallback> rejectCb =
   1.822 +    new NativePromiseCallback(aRunnable, Rejected);
   1.823 +
   1.824 +  AppendCallbacks(resolveCb, rejectCb);
   1.825 +}
   1.826 +
   1.827 +void
   1.828 +Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
   1.829 +                         PromiseCallback* aRejectCallback)
   1.830 +{
   1.831 +  if (aResolveCallback) {
   1.832 +    mResolveCallbacks.AppendElement(aResolveCallback);
   1.833 +  }
   1.834 +
   1.835 +  if (aRejectCallback) {
   1.836 +    mHadRejectCallback = true;
   1.837 +    mRejectCallbacks.AppendElement(aRejectCallback);
   1.838 +
   1.839 +    // Now that there is a callback, we don't need to report anymore.
   1.840 +    RemoveFeature();
   1.841 +  }
   1.842 +
   1.843 +  // If promise's state is resolved, queue a task to process our resolve
   1.844 +  // callbacks with promise's result. If promise's state is rejected, queue a
   1.845 +  // task to process our reject callbacks with promise's result.
   1.846 +  if (mState != Pending && !mTaskPending) {
   1.847 +    if (MOZ_LIKELY(NS_IsMainThread())) {
   1.848 +      nsRefPtr<PromiseTask> task = new PromiseTask(this);
   1.849 +      NS_DispatchToCurrentThread(task);
   1.850 +    } else {
   1.851 +      WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   1.852 +      MOZ_ASSERT(worker);
   1.853 +      nsRefPtr<WorkerPromiseTask> task = new WorkerPromiseTask(worker, this);
   1.854 +      task->Dispatch(worker->GetJSContext());
   1.855 +    }
   1.856 +    mTaskPending = true;
   1.857 +  }
   1.858 +}
   1.859 +
   1.860 +void
   1.861 +Promise::RunTask()
   1.862 +{
   1.863 +  MOZ_ASSERT(mState != Pending);
   1.864 +
   1.865 +  nsTArray<nsRefPtr<PromiseCallback>> callbacks;
   1.866 +  callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
   1.867 +                                            : mRejectCallbacks);
   1.868 +  mResolveCallbacks.Clear();
   1.869 +  mRejectCallbacks.Clear();
   1.870 +
   1.871 +  ThreadsafeAutoJSContext cx; // Just for rooting.
   1.872 +  JS::Rooted<JS::Value> value(cx, mResult);
   1.873 +
   1.874 +  for (uint32_t i = 0; i < callbacks.Length(); ++i) {
   1.875 +    callbacks[i]->Call(value);
   1.876 +  }
   1.877 +}
   1.878 +
   1.879 +void
   1.880 +Promise::MaybeReportRejected()
   1.881 +{
   1.882 +  if (mState != Rejected || mHadRejectCallback || mResult.isUndefined()) {
   1.883 +    return;
   1.884 +  }
   1.885 +
   1.886 +  if (!mResult.isObject()) {
   1.887 +    return;
   1.888 +  }
   1.889 +  ThreadsafeAutoJSContext cx;
   1.890 +  JS::Rooted<JSObject*> obj(cx, &mResult.toObject());
   1.891 +  JSAutoCompartment ac(cx, obj);
   1.892 +  JSErrorReport* report = JS_ErrorFromException(cx, obj);
   1.893 +  if (!report) {
   1.894 +    return;
   1.895 +  }
   1.896 +
   1.897 +  // Remains null in case of worker.
   1.898 +  nsCOMPtr<nsPIDOMWindow> win;
   1.899 +  bool isChromeError = false;
   1.900 +
   1.901 +  if (MOZ_LIKELY(NS_IsMainThread())) {
   1.902 +    win =
   1.903 +      do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(obj));
   1.904 +    nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(obj);
   1.905 +    isChromeError = nsContentUtils::IsSystemPrincipal(principal);
   1.906 +  } else {
   1.907 +    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   1.908 +    MOZ_ASSERT(worker);
   1.909 +    isChromeError = worker->IsChromeWorker();
   1.910 +  }
   1.911 +
   1.912 +  // Now post an event to do the real reporting async
   1.913 +  // Since Promises preserve their wrapper, it is essential to nsRefPtr<> the
   1.914 +  // AsyncErrorReporter, otherwise if the call to DispatchToMainThread fails, it
   1.915 +  // will leak. See Bug 958684.
   1.916 +  nsRefPtr<AsyncErrorReporter> r =
   1.917 +    new AsyncErrorReporter(JS_GetObjectRuntime(obj),
   1.918 +                           report,
   1.919 +                           nullptr,
   1.920 +                           isChromeError,
   1.921 +                           win);
   1.922 +  NS_DispatchToMainThread(r);
   1.923 +}
   1.924 +
   1.925 +void
   1.926 +Promise::MaybeResolveInternal(JSContext* aCx,
   1.927 +                              JS::Handle<JS::Value> aValue,
   1.928 +                              PromiseTaskSync aAsynchronous)
   1.929 +{
   1.930 +  if (mResolvePending) {
   1.931 +    return;
   1.932 +  }
   1.933 +
   1.934 +  ResolveInternal(aCx, aValue, aAsynchronous);
   1.935 +}
   1.936 +
   1.937 +void
   1.938 +Promise::MaybeRejectInternal(JSContext* aCx,
   1.939 +                             JS::Handle<JS::Value> aValue,
   1.940 +                             PromiseTaskSync aAsynchronous)
   1.941 +{
   1.942 +  if (mResolvePending) {
   1.943 +    return;
   1.944 +  }
   1.945 +
   1.946 +  RejectInternal(aCx, aValue, aAsynchronous);
   1.947 +}
   1.948 +
   1.949 +void
   1.950 +Promise::HandleException(JSContext* aCx)
   1.951 +{
   1.952 +  JS::Rooted<JS::Value> exn(aCx);
   1.953 +  if (JS_GetPendingException(aCx, &exn)) {
   1.954 +    JS_ClearPendingException(aCx);
   1.955 +    RejectInternal(aCx, exn, SyncTask);
   1.956 +  }
   1.957 +}
   1.958 +
   1.959 +void
   1.960 +Promise::ResolveInternal(JSContext* aCx,
   1.961 +                         JS::Handle<JS::Value> aValue,
   1.962 +                         PromiseTaskSync aAsynchronous)
   1.963 +{
   1.964 +  mResolvePending = true;
   1.965 +
   1.966 +  if (aValue.isObject()) {
   1.967 +    AutoDontReportUncaught silenceReporting(aCx);
   1.968 +    JS::Rooted<JSObject*> valueObj(aCx, &aValue.toObject());
   1.969 +
   1.970 +    // Thenables.
   1.971 +    JS::Rooted<JS::Value> then(aCx);
   1.972 +    if (!JS_GetProperty(aCx, valueObj, "then", &then)) {
   1.973 +      HandleException(aCx);
   1.974 +      return;
   1.975 +    }
   1.976 +
   1.977 +    if (then.isObject() && JS_ObjectIsCallable(aCx, &then.toObject())) {
   1.978 +      JS::Rooted<JSObject*> resolveFunc(aCx,
   1.979 +        CreateThenableFunction(aCx, this, PromiseCallback::Resolve));
   1.980 +
   1.981 +      if (!resolveFunc) {
   1.982 +        HandleException(aCx);
   1.983 +        return;
   1.984 +      }
   1.985 +
   1.986 +      JS::Rooted<JSObject*> rejectFunc(aCx,
   1.987 +        CreateThenableFunction(aCx, this, PromiseCallback::Reject));
   1.988 +      if (!rejectFunc) {
   1.989 +        HandleException(aCx);
   1.990 +        return;
   1.991 +      }
   1.992 +
   1.993 +      LinkThenableCallables(aCx, resolveFunc, rejectFunc);
   1.994 +
   1.995 +      JS::Rooted<JSObject*> thenObj(aCx, &then.toObject());
   1.996 +      nsRefPtr<PromiseInit> thenCallback =
   1.997 +        new PromiseInit(thenObj, mozilla::dom::GetIncumbentGlobal());
   1.998 +
   1.999 +      ErrorResult rv;
  1.1000 +      thenCallback->Call(valueObj, resolveFunc, rejectFunc,
  1.1001 +                         rv, CallbackObject::eRethrowExceptions);
  1.1002 +      rv.WouldReportJSException();
  1.1003 +
  1.1004 +      if (rv.IsJSException()) {
  1.1005 +        JS::Rooted<JS::Value> exn(aCx);
  1.1006 +        rv.StealJSException(aCx, &exn);
  1.1007 +
  1.1008 +        bool couldMarkAsCalled = MarkAsCalledIfNotCalledBefore(aCx, resolveFunc);
  1.1009 +
  1.1010 +        // If we could mark as called, neither of the callbacks had been called
  1.1011 +        // when the exception was thrown. So we can reject the Promise.
  1.1012 +        if (couldMarkAsCalled) {
  1.1013 +          bool ok = JS_WrapValue(aCx, &exn);
  1.1014 +          MOZ_ASSERT(ok);
  1.1015 +          if (!ok) {
  1.1016 +            NS_WARNING("Failed to wrap value into the right compartment.");
  1.1017 +          }
  1.1018 +
  1.1019 +          RejectInternal(aCx, exn, Promise::SyncTask);
  1.1020 +        }
  1.1021 +        // At least one of resolveFunc or rejectFunc have been called, so ignore
  1.1022 +        // the exception. FIXME(nsm): This should be reported to the error
  1.1023 +        // console though, for debugging.
  1.1024 +      }
  1.1025 +
  1.1026 +      return;
  1.1027 +    }
  1.1028 +  }
  1.1029 +
  1.1030 +  // If the synchronous flag is set, process our resolve callbacks with
  1.1031 +  // value. Otherwise, the synchronous flag is unset, queue a task to process
  1.1032 +  // own resolve callbacks with value. Otherwise, the synchronous flag is
  1.1033 +  // unset, queue a task to process our resolve callbacks with value.
  1.1034 +  RunResolveTask(aValue, Resolved, aAsynchronous);
  1.1035 +}
  1.1036 +
  1.1037 +void
  1.1038 +Promise::RejectInternal(JSContext* aCx,
  1.1039 +                        JS::Handle<JS::Value> aValue,
  1.1040 +                        PromiseTaskSync aAsynchronous)
  1.1041 +{
  1.1042 +  mResolvePending = true;
  1.1043 +
  1.1044 +  // If the synchronous flag is set, process our reject callbacks with
  1.1045 +  // value. Otherwise, the synchronous flag is unset, queue a task to process
  1.1046 +  // promise's reject callbacks with value.
  1.1047 +  RunResolveTask(aValue, Rejected, aAsynchronous);
  1.1048 +}
  1.1049 +
  1.1050 +void
  1.1051 +Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
  1.1052 +                        PromiseState aState,
  1.1053 +                        PromiseTaskSync aAsynchronous)
  1.1054 +{
  1.1055 +  // If the synchronous flag is unset, queue a task to process our
  1.1056 +  // accept callbacks with value.
  1.1057 +  if (aAsynchronous == AsyncTask) {
  1.1058 +    if (MOZ_LIKELY(NS_IsMainThread())) {
  1.1059 +      nsRefPtr<PromiseResolverTask> task =
  1.1060 +        new PromiseResolverTask(this, aValue, aState);
  1.1061 +      NS_DispatchToCurrentThread(task);
  1.1062 +    } else {
  1.1063 +      WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
  1.1064 +      MOZ_ASSERT(worker);
  1.1065 +      nsRefPtr<WorkerPromiseResolverTask> task =
  1.1066 +        new WorkerPromiseResolverTask(worker, this, aValue, aState);
  1.1067 +      task->Dispatch(worker->GetJSContext());
  1.1068 +    }
  1.1069 +    return;
  1.1070 +  }
  1.1071 +
  1.1072 +  // Promise.all() or Promise.race() implementations will repeatedly call
  1.1073 +  // Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState
  1.1074 +  // from asserting.
  1.1075 +  if (mState != Pending) {
  1.1076 +    return;
  1.1077 +  }
  1.1078 +
  1.1079 +  SetResult(aValue);
  1.1080 +  SetState(aState);
  1.1081 +
  1.1082 +  // If the Promise was rejected, and there is no reject handler already setup,
  1.1083 +  // watch for thread shutdown.
  1.1084 +  if (aState == PromiseState::Rejected &&
  1.1085 +      !mHadRejectCallback &&
  1.1086 +      !NS_IsMainThread()) {
  1.1087 +    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
  1.1088 +    MOZ_ASSERT(worker);
  1.1089 +    worker->AssertIsOnWorkerThread();
  1.1090 +
  1.1091 +    mFeature = new PromiseReportRejectFeature(this);
  1.1092 +    if (NS_WARN_IF(!worker->AddFeature(worker->GetJSContext(), mFeature))) {
  1.1093 +      // To avoid a false RemoveFeature().
  1.1094 +      mFeature = nullptr;
  1.1095 +      // Worker is shutting down, report rejection immediately since it is
  1.1096 +      // unlikely that reject callbacks will be added after this point.
  1.1097 +      MaybeReportRejectedOnce();
  1.1098 +    }
  1.1099 +  }
  1.1100 +
  1.1101 +  RunTask();
  1.1102 +}
  1.1103 +
  1.1104 +void
  1.1105 +Promise::RemoveFeature()
  1.1106 +{
  1.1107 +  if (mFeature) {
  1.1108 +    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
  1.1109 +    MOZ_ASSERT(worker);
  1.1110 +    worker->RemoveFeature(worker->GetJSContext(), mFeature);
  1.1111 +    mFeature = nullptr;
  1.1112 +  }
  1.1113 +}
  1.1114 +
  1.1115 +bool
  1.1116 +PromiseReportRejectFeature::Notify(JSContext* aCx, workers::Status aStatus)
  1.1117 +{
  1.1118 +  MOZ_ASSERT(aStatus > workers::Running);
  1.1119 +  mPromise->MaybeReportRejectedOnce();
  1.1120 +  // After this point, `this` has been deleted by RemoveFeature!
  1.1121 +  return true;
  1.1122 +}
  1.1123 +
  1.1124 +} // namespace dom
  1.1125 +} // namespace mozilla

mercurial