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