Bumping manifests a=b2g-bump
[gecko.git] / dom / promise / Promise.cpp
bloba7cd78520d10247b9da8afbe10a94265a0447604
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/Promise.h"
9 #include "jsfriendapi.h"
10 #include "mozilla/dom/BindingUtils.h"
11 #include "mozilla/dom/DOMError.h"
12 #include "mozilla/dom/OwningNonNull.h"
13 #include "mozilla/dom/PromiseBinding.h"
14 #include "mozilla/dom/ScriptSettings.h"
15 #include "mozilla/CycleCollectedJSRuntime.h"
16 #include "mozilla/Preferences.h"
17 #include "PromiseCallback.h"
18 #include "PromiseNativeHandler.h"
19 #include "PromiseWorkerProxy.h"
20 #include "nsContentUtils.h"
21 #include "WorkerPrivate.h"
22 #include "WorkerRunnable.h"
23 #include "nsJSPrincipals.h"
24 #include "nsJSUtils.h"
25 #include "nsPIDOMWindow.h"
26 #include "nsJSEnvironment.h"
27 #include "nsIScriptObjectPrincipal.h"
28 #include "xpcpublic.h"
29 #include "nsGlobalWindow.h"
31 namespace mozilla {
32 namespace dom {
34 using namespace workers;
36 NS_IMPL_ISUPPORTS0(PromiseNativeHandler)
38 // PromiseTask
40 // This class processes the promise's callbacks with promise's result.
41 class PromiseTask MOZ_FINAL : public nsRunnable
43 public:
44 PromiseTask(Promise* aPromise)
45 : mPromise(aPromise)
47 MOZ_ASSERT(aPromise);
48 MOZ_COUNT_CTOR(PromiseTask);
51 protected:
52 ~PromiseTask()
54 NS_ASSERT_OWNINGTHREAD(PromiseTask);
55 MOZ_COUNT_DTOR(PromiseTask);
58 public:
59 NS_IMETHOD
60 Run() MOZ_OVERRIDE
62 NS_ASSERT_OWNINGTHREAD(PromiseTask);
63 mPromise->mTaskPending = false;
64 mPromise->RunTask();
65 return NS_OK;
68 private:
69 nsRefPtr<Promise> mPromise;
70 NS_DECL_OWNINGTHREAD
73 // This class processes the promise's callbacks with promise's result.
74 class PromiseResolverTask MOZ_FINAL : public nsRunnable
76 public:
77 PromiseResolverTask(Promise* aPromise,
78 JS::Handle<JS::Value> aValue,
79 Promise::PromiseState aState)
80 : mPromise(aPromise)
81 , mValue(CycleCollectedJSRuntime::Get()->Runtime(), aValue)
82 , mState(aState)
84 MOZ_ASSERT(aPromise);
85 MOZ_ASSERT(mState != Promise::Pending);
86 MOZ_COUNT_CTOR(PromiseResolverTask);
89 virtual
90 ~PromiseResolverTask()
92 NS_ASSERT_OWNINGTHREAD(PromiseResolverTask);
93 MOZ_COUNT_DTOR(PromiseResolverTask);
96 NS_IMETHOD
97 Run() MOZ_OVERRIDE
99 NS_ASSERT_OWNINGTHREAD(PromiseResolverTask);
100 mPromise->RunResolveTask(
101 JS::Handle<JS::Value>::fromMarkedLocation(mValue.address()),
102 mState, Promise::SyncTask);
103 return NS_OK;
106 private:
107 nsRefPtr<Promise> mPromise;
108 JS::PersistentRooted<JS::Value> mValue;
109 Promise::PromiseState mState;
110 NS_DECL_OWNINGTHREAD;
113 enum {
114 SLOT_PROMISE = 0,
115 SLOT_DATA
119 * Utilities for thenable callbacks.
121 * A thenable is a { then: function(resolve, reject) { } }.
122 * `then` is called with a resolve and reject callback pair.
123 * Since only one of these should be called at most once (first call wins), the
124 * two keep a reference to each other in SLOT_DATA. When either of them is
125 * called, the references are cleared. Further calls are ignored.
127 namespace {
128 void
129 LinkThenableCallables(JSContext* aCx, JS::Handle<JSObject*> aResolveFunc,
130 JS::Handle<JSObject*> aRejectFunc)
132 js::SetFunctionNativeReserved(aResolveFunc, SLOT_DATA,
133 JS::ObjectValue(*aRejectFunc));
134 js::SetFunctionNativeReserved(aRejectFunc, SLOT_DATA,
135 JS::ObjectValue(*aResolveFunc));
139 * Returns false if callback was already called before, otherwise breaks the
140 * links and returns true.
142 bool
143 MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle<JSObject*> aFunc)
145 JS::Value otherFuncVal = js::GetFunctionNativeReserved(aFunc, SLOT_DATA);
147 if (!otherFuncVal.isObject()) {
148 return false;
151 JSObject* otherFuncObj = &otherFuncVal.toObject();
152 MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, SLOT_DATA).isObject());
154 // Break both references.
155 js::SetFunctionNativeReserved(aFunc, SLOT_DATA, JS::UndefinedValue());
156 js::SetFunctionNativeReserved(otherFuncObj, SLOT_DATA, JS::UndefinedValue());
158 return true;
161 Promise*
162 GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc)
164 JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, SLOT_PROMISE);
166 MOZ_ASSERT(promiseVal.isObject());
168 Promise* promise;
169 UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise);
170 return promise;
174 // Main thread runnable to resolve thenables.
175 // Equivalent to the specification's ResolvePromiseViaThenableTask.
176 class ThenableResolverTask MOZ_FINAL : public nsRunnable
178 public:
179 ThenableResolverTask(Promise* aPromise,
180 JS::Handle<JSObject*> aThenable,
181 PromiseInit* aThen)
182 : mPromise(aPromise)
183 , mThenable(CycleCollectedJSRuntime::Get()->Runtime(), aThenable)
184 , mThen(aThen)
186 MOZ_ASSERT(aPromise);
187 MOZ_COUNT_CTOR(ThenableResolverTask);
190 virtual
191 ~ThenableResolverTask()
193 NS_ASSERT_OWNINGTHREAD(ThenableResolverTask);
194 MOZ_COUNT_DTOR(ThenableResolverTask);
197 protected:
198 NS_IMETHOD
199 Run() MOZ_OVERRIDE
201 NS_ASSERT_OWNINGTHREAD(ThenableResolverTask);
202 ThreadsafeAutoJSContext cx;
203 JS::Rooted<JSObject*> wrapper(cx, mPromise->GetWrapper());
204 MOZ_ASSERT(wrapper); // It was preserved!
205 if (!wrapper) {
206 return NS_OK;
208 JSAutoCompartment ac(cx, wrapper);
210 JS::Rooted<JSObject*> resolveFunc(cx,
211 mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Resolve));
213 if (!resolveFunc) {
214 mPromise->HandleException(cx);
215 return NS_OK;
218 JS::Rooted<JSObject*> rejectFunc(cx,
219 mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Reject));
220 if (!rejectFunc) {
221 mPromise->HandleException(cx);
222 return NS_OK;
225 LinkThenableCallables(cx, resolveFunc, rejectFunc);
227 ErrorResult rv;
229 JS::Rooted<JSObject*> rootedThenable(cx, mThenable);
231 mThen->Call(rootedThenable, resolveFunc, rejectFunc, rv,
232 CallbackObject::eRethrowExceptions);
234 rv.WouldReportJSException();
235 if (rv.IsJSException()) {
236 JS::Rooted<JS::Value> exn(cx);
237 rv.StealJSException(cx, &exn);
239 bool couldMarkAsCalled = MarkAsCalledIfNotCalledBefore(cx, resolveFunc);
241 // If we could mark as called, neither of the callbacks had been called
242 // when the exception was thrown. So we can reject the Promise.
243 if (couldMarkAsCalled) {
244 bool ok = JS_WrapValue(cx, &exn);
245 MOZ_ASSERT(ok);
246 if (!ok) {
247 NS_WARNING("Failed to wrap value into the right compartment.");
250 mPromise->RejectInternal(cx, exn, Promise::SyncTask);
252 // At least one of resolveFunc or rejectFunc have been called, so ignore
253 // the exception. FIXME(nsm): This should be reported to the error
254 // console though, for debugging.
257 return NS_OK;
260 private:
261 nsRefPtr<Promise> mPromise;
262 JS::PersistentRooted<JSObject*> mThenable;
263 nsRefPtr<PromiseInit> mThen;
264 NS_DECL_OWNINGTHREAD;
267 // Promise
269 NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
271 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
272 tmp->MaybeReportRejectedOnce();
273 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
274 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks)
275 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectCallbacks)
276 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
277 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
279 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
280 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
281 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks)
282 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks)
283 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
284 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
286 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
287 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
288 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
289 NS_IMPL_CYCLE_COLLECTION_TRACE_END
291 NS_IMPL_CYCLE_COLLECTING_ADDREF(Promise)
292 NS_IMPL_CYCLE_COLLECTING_RELEASE(Promise)
294 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Promise)
295 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
296 NS_INTERFACE_MAP_ENTRY(nsISupports)
297 NS_INTERFACE_MAP_END
299 Promise::Promise(nsIGlobalObject* aGlobal)
300 : mGlobal(aGlobal)
301 , mResult(JS::UndefinedValue())
302 , mState(Pending)
303 , mTaskPending(false)
304 , mHadRejectCallback(false)
305 , mResolvePending(false)
307 MOZ_ASSERT(mGlobal);
309 mozilla::HoldJSObjects(this);
310 SetIsDOMBinding();
313 Promise::~Promise()
315 MaybeReportRejectedOnce();
316 mozilla::DropJSObjects(this);
319 JSObject*
320 Promise::WrapObject(JSContext* aCx)
322 return PromiseBinding::Wrap(aCx, this);
325 already_AddRefed<Promise>
326 Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
328 AutoJSAPI jsapi;
329 if (!jsapi.Init(aGlobal)) {
330 aRv.Throw(NS_ERROR_UNEXPECTED);
331 return nullptr;
333 JSContext* cx = jsapi.cx();
335 nsRefPtr<Promise> p = new Promise(aGlobal);
337 JS::Rooted<JS::Value> ignored(cx);
338 if (!WrapNewBindingObject(cx, p, &ignored)) {
339 JS_ClearPendingException(cx);
340 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
341 return nullptr;
344 // Need the .get() bit here to get template deduction working right
345 dom::PreserveWrapper(p.get());
347 return p.forget();
350 void
351 Promise::MaybeResolve(JSContext* aCx,
352 JS::Handle<JS::Value> aValue)
354 MaybeResolveInternal(aCx, aValue);
357 void
358 Promise::MaybeReject(JSContext* aCx,
359 JS::Handle<JS::Value> aValue)
361 MaybeRejectInternal(aCx, aValue);
364 /* static */ bool
365 Promise::JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
367 JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
369 JS::Rooted<JS::Value> v(aCx,
370 js::GetFunctionNativeReserved(&args.callee(),
371 SLOT_PROMISE));
372 MOZ_ASSERT(v.isObject());
374 Promise* promise;
375 if (NS_FAILED(UNWRAP_OBJECT(Promise, &v.toObject(), promise))) {
376 return Throw(aCx, NS_ERROR_UNEXPECTED);
379 v = js::GetFunctionNativeReserved(&args.callee(), SLOT_DATA);
380 PromiseCallback::Task task = static_cast<PromiseCallback::Task>(v.toInt32());
382 if (task == PromiseCallback::Resolve) {
383 promise->MaybeResolveInternal(aCx, args.get(0));
384 } else {
385 promise->MaybeRejectInternal(aCx, args.get(0));
388 return true;
392 * Common bits of (JSCallbackThenableResolver/JSCallbackThenableRejecter).
393 * Resolves/rejects the Promise if it is ok to do so, based on whether either of
394 * the callbacks have been called before or not.
396 /* static */ bool
397 Promise::ThenableResolverCommon(JSContext* aCx, uint32_t aTask,
398 unsigned aArgc, JS::Value* aVp)
400 JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
401 JS::Rooted<JSObject*> thisFunc(aCx, &args.callee());
402 if (!MarkAsCalledIfNotCalledBefore(aCx, thisFunc)) {
403 // A function from this pair has been called before.
404 return true;
407 Promise* promise = GetPromise(aCx, thisFunc);
408 MOZ_ASSERT(promise);
410 if (aTask == PromiseCallback::Resolve) {
411 promise->ResolveInternal(aCx, args.get(0));
412 } else {
413 promise->RejectInternal(aCx, args.get(0));
415 return true;
418 /* static */ bool
419 Promise::JSCallbackThenableResolver(JSContext* aCx,
420 unsigned aArgc, JS::Value* aVp)
422 return ThenableResolverCommon(aCx, PromiseCallback::Resolve, aArgc, aVp);
425 /* static */ bool
426 Promise::JSCallbackThenableRejecter(JSContext* aCx,
427 unsigned aArgc, JS::Value* aVp)
429 return ThenableResolverCommon(aCx, PromiseCallback::Reject, aArgc, aVp);
432 /* static */ JSObject*
433 Promise::CreateFunction(JSContext* aCx, JSObject* aParent, Promise* aPromise,
434 int32_t aTask)
436 JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback,
437 1 /* nargs */, 0 /* flags */,
438 aParent, nullptr);
439 if (!func) {
440 return nullptr;
443 JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
445 JS::Rooted<JS::Value> promiseObj(aCx);
446 if (!dom::WrapNewBindingObject(aCx, aPromise, &promiseObj)) {
447 return nullptr;
450 js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj);
451 js::SetFunctionNativeReserved(obj, SLOT_DATA, JS::Int32Value(aTask));
453 return obj;
456 /* static */ JSObject*
457 Promise::CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask)
459 JSNative whichFunc =
460 aTask == PromiseCallback::Resolve ? JSCallbackThenableResolver :
461 JSCallbackThenableRejecter ;
463 JSFunction* func = js::NewFunctionWithReserved(aCx, whichFunc,
464 1 /* nargs */, 0 /* flags */,
465 nullptr, nullptr);
466 if (!func) {
467 return nullptr;
470 JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
472 JS::Rooted<JS::Value> promiseObj(aCx);
473 if (!dom::WrapNewBindingObject(aCx, aPromise, &promiseObj)) {
474 return nullptr;
477 js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj);
479 return obj;
482 /* static */ already_AddRefed<Promise>
483 Promise::Constructor(const GlobalObject& aGlobal,
484 PromiseInit& aInit, ErrorResult& aRv)
486 JSContext* cx = aGlobal.Context();
488 nsCOMPtr<nsIGlobalObject> global;
489 global = do_QueryInterface(aGlobal.GetAsSupports());
490 if (!global) {
491 aRv.Throw(NS_ERROR_UNEXPECTED);
492 return nullptr;
495 nsRefPtr<Promise> promise = Create(global, aRv);
496 if (aRv.Failed()) {
497 return nullptr;
500 JS::Rooted<JSObject*> resolveFunc(cx,
501 CreateFunction(cx, aGlobal.Get(), promise,
502 PromiseCallback::Resolve));
503 if (!resolveFunc) {
504 aRv.Throw(NS_ERROR_UNEXPECTED);
505 return nullptr;
508 JS::Rooted<JSObject*> rejectFunc(cx,
509 CreateFunction(cx, aGlobal.Get(), promise,
510 PromiseCallback::Reject));
511 if (!rejectFunc) {
512 aRv.Throw(NS_ERROR_UNEXPECTED);
513 return nullptr;
516 aInit.Call(resolveFunc, rejectFunc, aRv, CallbackObject::eRethrowExceptions);
517 aRv.WouldReportJSException();
519 if (aRv.IsJSException()) {
520 JS::Rooted<JS::Value> value(cx);
521 aRv.StealJSException(cx, &value);
523 // we want the same behavior as this JS implementation:
524 // function Promise(arg) { try { arg(a, b); } catch (e) { this.reject(e); }}
525 if (!JS_WrapValue(cx, &value)) {
526 aRv.Throw(NS_ERROR_UNEXPECTED);
527 return nullptr;
530 promise->MaybeRejectInternal(cx, value);
533 return promise.forget();
536 /* static */ already_AddRefed<Promise>
537 Promise::Resolve(const GlobalObject& aGlobal,
538 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
540 // If a Promise was passed, just return it.
541 if (aValue.isObject()) {
542 JS::Rooted<JSObject*> valueObj(aGlobal.Context(), &aValue.toObject());
543 Promise* nextPromise;
544 nsresult rv = UNWRAP_OBJECT(Promise, valueObj, nextPromise);
546 if (NS_SUCCEEDED(rv)) {
547 nsRefPtr<Promise> addRefed = nextPromise;
548 return addRefed.forget();
552 nsCOMPtr<nsIGlobalObject> global =
553 do_QueryInterface(aGlobal.GetAsSupports());
554 if (!global) {
555 aRv.Throw(NS_ERROR_UNEXPECTED);
556 return nullptr;
559 return Resolve(global, aGlobal.Context(), aValue, aRv);
562 /* static */ already_AddRefed<Promise>
563 Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
564 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
566 nsRefPtr<Promise> promise = Create(aGlobal, aRv);
567 if (aRv.Failed()) {
568 return nullptr;
571 promise->MaybeResolveInternal(aCx, aValue);
572 return promise.forget();
575 /* static */ already_AddRefed<Promise>
576 Promise::Reject(const GlobalObject& aGlobal,
577 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
579 nsCOMPtr<nsIGlobalObject> global =
580 do_QueryInterface(aGlobal.GetAsSupports());
581 if (!global) {
582 aRv.Throw(NS_ERROR_UNEXPECTED);
583 return nullptr;
586 return Reject(global, aGlobal.Context(), aValue, aRv);
589 /* static */ already_AddRefed<Promise>
590 Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
591 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
593 nsRefPtr<Promise> promise = Create(aGlobal, aRv);
594 if (aRv.Failed()) {
595 return nullptr;
598 promise->MaybeRejectInternal(aCx, aValue);
599 return promise.forget();
602 already_AddRefed<Promise>
603 Promise::Then(JSContext* aCx, AnyCallback* aResolveCallback,
604 AnyCallback* aRejectCallback, ErrorResult& aRv)
606 nsRefPtr<Promise> promise = Create(GetParentObject(), aRv);
607 if (aRv.Failed()) {
608 return nullptr;
611 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
613 nsRefPtr<PromiseCallback> resolveCb =
614 PromiseCallback::Factory(promise, global, aResolveCallback,
615 PromiseCallback::Resolve);
617 nsRefPtr<PromiseCallback> rejectCb =
618 PromiseCallback::Factory(promise, global, aRejectCallback,
619 PromiseCallback::Reject);
621 AppendCallbacks(resolveCb, rejectCb);
623 return promise.forget();
626 already_AddRefed<Promise>
627 Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback, ErrorResult& aRv)
629 nsRefPtr<AnyCallback> resolveCb;
630 return Then(aCx, resolveCb, aRejectCallback, aRv);
634 * The CountdownHolder class encapsulates Promise.all countdown functions and
635 * the countdown holder parts of the Promises spec. It maintains the result
636 * array and AllResolveHandlers use SetValue() to set the array indices.
638 class CountdownHolder MOZ_FINAL : public nsISupports
640 public:
641 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
642 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CountdownHolder)
644 CountdownHolder(const GlobalObject& aGlobal, Promise* aPromise,
645 uint32_t aCountdown)
646 : mPromise(aPromise), mCountdown(aCountdown)
648 MOZ_ASSERT(aCountdown != 0);
649 JSContext* cx = aGlobal.Context();
651 // The only time aGlobal.Context() and aGlobal.Get() are not
652 // same-compartment is when we're called via Xrays, and in that situation we
653 // in fact want to create the array in the callee compartment
655 JSAutoCompartment ac(cx, aGlobal.Get());
656 mValues = JS_NewArrayObject(cx, aCountdown);
657 mozilla::HoldJSObjects(this);
660 private:
661 ~CountdownHolder()
663 mozilla::DropJSObjects(this);
666 public:
667 void SetValue(uint32_t index, const JS::Handle<JS::Value> aValue)
669 MOZ_ASSERT(mCountdown > 0);
671 ThreadsafeAutoSafeJSContext cx;
672 JSAutoCompartment ac(cx, mValues);
675 AutoDontReportUncaught silenceReporting(cx);
676 JS::Rooted<JS::Value> value(cx, aValue);
677 JS::Rooted<JSObject*> values(cx, mValues);
678 if (!JS_WrapValue(cx, &value) ||
679 !JS_DefineElement(cx, values, index, value, JSPROP_ENUMERATE)) {
680 MOZ_ASSERT(JS_IsExceptionPending(cx));
681 JS::Rooted<JS::Value> exn(cx);
682 JS_GetPendingException(cx, &exn);
684 mPromise->MaybeReject(cx, exn);
688 --mCountdown;
689 if (mCountdown == 0) {
690 JS::Rooted<JS::Value> result(cx, JS::ObjectValue(*mValues));
691 mPromise->MaybeResolve(cx, result);
695 private:
696 nsRefPtr<Promise> mPromise;
697 uint32_t mCountdown;
698 JS::Heap<JSObject*> mValues;
701 NS_IMPL_CYCLE_COLLECTING_ADDREF(CountdownHolder)
702 NS_IMPL_CYCLE_COLLECTING_RELEASE(CountdownHolder)
703 NS_IMPL_CYCLE_COLLECTION_CLASS(CountdownHolder)
705 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CountdownHolder)
706 NS_INTERFACE_MAP_ENTRY(nsISupports)
707 NS_INTERFACE_MAP_END
709 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CountdownHolder)
710 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mValues)
711 NS_IMPL_CYCLE_COLLECTION_TRACE_END
713 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CountdownHolder)
714 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
715 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
716 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
718 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CountdownHolder)
719 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
720 tmp->mValues = nullptr;
721 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
724 * An AllResolveHandler is the per-promise part of the Promise.all() algorithm.
725 * Every Promise in the handler is handed an instance of this as a resolution
726 * handler and it sets the relevant index in the CountdownHolder.
728 class AllResolveHandler MOZ_FINAL : public PromiseNativeHandler
730 public:
731 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
732 NS_DECL_CYCLE_COLLECTION_CLASS(AllResolveHandler)
734 AllResolveHandler(CountdownHolder* aHolder, uint32_t aIndex)
735 : mCountdownHolder(aHolder), mIndex(aIndex)
737 MOZ_ASSERT(aHolder);
740 void
741 ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
743 mCountdownHolder->SetValue(mIndex, aValue);
746 void
747 RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
749 // Should never be attached to Promise as a reject handler.
750 MOZ_ASSERT(false, "AllResolveHandler should never be attached to a Promise's reject handler!");
753 private:
754 ~AllResolveHandler()
758 nsRefPtr<CountdownHolder> mCountdownHolder;
759 uint32_t mIndex;
762 NS_IMPL_CYCLE_COLLECTING_ADDREF(AllResolveHandler)
763 NS_IMPL_CYCLE_COLLECTING_RELEASE(AllResolveHandler)
765 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AllResolveHandler)
766 NS_INTERFACE_MAP_END_INHERITING(PromiseNativeHandler)
768 NS_IMPL_CYCLE_COLLECTION(AllResolveHandler, mCountdownHolder)
770 /* static */ already_AddRefed<Promise>
771 Promise::All(const GlobalObject& aGlobal,
772 const Sequence<JS::Value>& aIterable, ErrorResult& aRv)
774 nsCOMPtr<nsIGlobalObject> global =
775 do_QueryInterface(aGlobal.GetAsSupports());
776 if (!global) {
777 aRv.Throw(NS_ERROR_UNEXPECTED);
778 return nullptr;
781 JSContext* cx = aGlobal.Context();
783 if (aIterable.Length() == 0) {
784 JS::Rooted<JSObject*> empty(cx, JS_NewArrayObject(cx, 0));
785 if (!empty) {
786 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
787 return nullptr;
789 JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*empty));
790 return Promise::Resolve(aGlobal, value, aRv);
793 nsRefPtr<Promise> promise = Create(global, aRv);
794 if (aRv.Failed()) {
795 return nullptr;
797 nsRefPtr<CountdownHolder> holder =
798 new CountdownHolder(aGlobal, promise, aIterable.Length());
800 JS::Rooted<JSObject*> obj(cx, JS::CurrentGlobalOrNull(cx));
801 if (!obj) {
802 aRv.Throw(NS_ERROR_UNEXPECTED);
803 return nullptr;
806 nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(promise, obj);
808 for (uint32_t i = 0; i < aIterable.Length(); ++i) {
809 JS::Rooted<JS::Value> value(cx, aIterable.ElementAt(i));
810 nsRefPtr<Promise> nextPromise = Promise::Resolve(aGlobal, value, aRv);
812 MOZ_ASSERT(!aRv.Failed());
814 nsRefPtr<PromiseNativeHandler> resolveHandler =
815 new AllResolveHandler(holder, i);
817 nsRefPtr<PromiseCallback> resolveCb =
818 new NativePromiseCallback(resolveHandler, Resolved);
819 // Every promise gets its own resolve callback, which will set the right
820 // index in the array to the resolution value.
821 nextPromise->AppendCallbacks(resolveCb, rejectCb);
824 return promise.forget();
827 /* static */ already_AddRefed<Promise>
828 Promise::Race(const GlobalObject& aGlobal,
829 const Sequence<JS::Value>& aIterable, ErrorResult& aRv)
831 nsCOMPtr<nsIGlobalObject> global =
832 do_QueryInterface(aGlobal.GetAsSupports());
833 if (!global) {
834 aRv.Throw(NS_ERROR_UNEXPECTED);
835 return nullptr;
838 JSContext* cx = aGlobal.Context();
840 JS::Rooted<JSObject*> obj(cx, JS::CurrentGlobalOrNull(cx));
841 if (!obj) {
842 aRv.Throw(NS_ERROR_UNEXPECTED);
843 return nullptr;
846 nsRefPtr<Promise> promise = Create(global, aRv);
847 if (aRv.Failed()) {
848 return nullptr;
851 nsRefPtr<PromiseCallback> resolveCb =
852 new ResolvePromiseCallback(promise, obj);
854 nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(promise, obj);
856 for (uint32_t i = 0; i < aIterable.Length(); ++i) {
857 JS::Rooted<JS::Value> value(cx, aIterable.ElementAt(i));
858 nsRefPtr<Promise> nextPromise = Promise::Resolve(aGlobal, value, aRv);
859 // According to spec, Resolve can throw, but our implementation never does.
860 // Well it does when window isn't passed on the main thread, but that is an
861 // implementation detail which should never be reached since we are checking
862 // for window above. Remove this when subclassing is supported.
863 MOZ_ASSERT(!aRv.Failed());
864 nextPromise->AppendCallbacks(resolveCb, rejectCb);
867 return promise.forget();
870 void
871 Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
873 nsRefPtr<PromiseCallback> resolveCb =
874 new NativePromiseCallback(aRunnable, Resolved);
876 nsRefPtr<PromiseCallback> rejectCb =
877 new NativePromiseCallback(aRunnable, Rejected);
879 AppendCallbacks(resolveCb, rejectCb);
882 void
883 Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
884 PromiseCallback* aRejectCallback)
886 if (aResolveCallback) {
887 mResolveCallbacks.AppendElement(aResolveCallback);
890 if (aRejectCallback) {
891 mHadRejectCallback = true;
892 mRejectCallbacks.AppendElement(aRejectCallback);
894 // Now that there is a callback, we don't need to report anymore.
895 RemoveFeature();
898 // If promise's state is resolved, queue a task to process our resolve
899 // callbacks with promise's result. If promise's state is rejected, queue a
900 // task to process our reject callbacks with promise's result.
901 if (mState != Pending && !mTaskPending) {
902 nsRefPtr<PromiseTask> task = new PromiseTask(this);
903 DispatchToMainOrWorkerThread(task);
904 mTaskPending = true;
908 class WrappedWorkerRunnable MOZ_FINAL : public WorkerSameThreadRunnable
910 public:
911 WrappedWorkerRunnable(WorkerPrivate* aWorkerPrivate, nsIRunnable* aRunnable)
912 : WorkerSameThreadRunnable(aWorkerPrivate)
913 , mRunnable(aRunnable)
915 MOZ_ASSERT(aRunnable);
916 MOZ_COUNT_CTOR(WrappedWorkerRunnable);
919 bool
920 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
922 NS_ASSERT_OWNINGTHREAD(WrappedWorkerRunnable);
923 mRunnable->Run();
924 return true;
927 private:
928 virtual
929 ~WrappedWorkerRunnable()
931 MOZ_COUNT_DTOR(WrappedWorkerRunnable);
932 NS_ASSERT_OWNINGTHREAD(WrappedWorkerRunnable);
935 nsCOMPtr<nsIRunnable> mRunnable;
936 NS_DECL_OWNINGTHREAD
939 /* static */ void
940 Promise::DispatchToMainOrWorkerThread(nsIRunnable* aRunnable)
942 MOZ_ASSERT(aRunnable);
943 if (NS_IsMainThread()) {
944 NS_DispatchToCurrentThread(aRunnable);
945 return;
947 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
948 MOZ_ASSERT(worker);
949 nsRefPtr<WrappedWorkerRunnable> task = new WrappedWorkerRunnable(worker, aRunnable);
950 task->Dispatch(worker->GetJSContext());
953 void
954 Promise::RunTask()
956 MOZ_ASSERT(mState != Pending);
958 nsTArray<nsRefPtr<PromiseCallback>> callbacks;
959 callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
960 : mRejectCallbacks);
961 mResolveCallbacks.Clear();
962 mRejectCallbacks.Clear();
964 ThreadsafeAutoJSContext cx;
965 JS::Rooted<JS::Value> value(cx, mResult);
966 JS::Rooted<JSObject*> wrapper(cx, GetWrapper());
967 MOZ_ASSERT(wrapper); // We preserved it
969 JSAutoCompartment ac(cx, wrapper);
970 if (!MaybeWrapValue(cx, &value)) {
971 return;
974 for (uint32_t i = 0; i < callbacks.Length(); ++i) {
975 callbacks[i]->Call(cx, value);
979 void
980 Promise::MaybeReportRejected()
982 if (mState != Rejected || mHadRejectCallback || mResult.isUndefined()) {
983 return;
986 AutoJSAPI jsapi;
987 // We may not have a usable global by now (if it got unlinked
988 // already), so don't init with it.
989 jsapi.Init();
990 JSContext* cx = jsapi.cx();
991 JS::Rooted<JSObject*> obj(cx, GetWrapper());
992 MOZ_ASSERT(obj); // We preserve our wrapper, so should always have one here.
993 JS::Rooted<JS::Value> val(cx, mResult);
994 JS::ExposeValueToActiveJS(val);
996 JSAutoCompartment ac(cx, obj);
997 if (!JS_WrapValue(cx, &val)) {
998 JS_ClearPendingException(cx);
999 return;
1002 js::ErrorReport report(cx);
1003 if (!report.init(cx, val)) {
1004 JS_ClearPendingException(cx);
1005 return;
1008 // Remains null in case of worker.
1009 nsCOMPtr<nsPIDOMWindow> win;
1010 bool isChromeError = false;
1012 if (MOZ_LIKELY(NS_IsMainThread())) {
1013 nsIPrincipal* principal;
1014 win = xpc::WindowGlobalOrNull(obj);
1015 principal = nsContentUtils::ObjectPrincipal(obj);
1016 isChromeError = nsContentUtils::IsSystemPrincipal(principal);
1017 } else {
1018 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
1019 MOZ_ASSERT(worker);
1020 isChromeError = worker->IsChromeWorker();
1023 // Now post an event to do the real reporting async
1024 // Since Promises preserve their wrapper, it is essential to nsRefPtr<> the
1025 // AsyncErrorReporter, otherwise if the call to DispatchToMainThread fails, it
1026 // will leak. See Bug 958684.
1027 nsRefPtr<AsyncErrorReporter> r =
1028 new AsyncErrorReporter(CycleCollectedJSRuntime::Get()->Runtime(),
1029 report.report(),
1030 report.message(),
1031 isChromeError,
1032 win);
1033 NS_DispatchToMainThread(r);
1036 void
1037 Promise::MaybeResolveInternal(JSContext* aCx,
1038 JS::Handle<JS::Value> aValue,
1039 PromiseTaskSync aAsynchronous)
1041 if (mResolvePending) {
1042 return;
1045 ResolveInternal(aCx, aValue, aAsynchronous);
1048 void
1049 Promise::MaybeRejectInternal(JSContext* aCx,
1050 JS::Handle<JS::Value> aValue,
1051 PromiseTaskSync aAsynchronous)
1053 if (mResolvePending) {
1054 return;
1057 RejectInternal(aCx, aValue, aAsynchronous);
1060 void
1061 Promise::HandleException(JSContext* aCx)
1063 JS::Rooted<JS::Value> exn(aCx);
1064 if (JS_GetPendingException(aCx, &exn)) {
1065 JS_ClearPendingException(aCx);
1066 RejectInternal(aCx, exn, SyncTask);
1070 void
1071 Promise::ResolveInternal(JSContext* aCx,
1072 JS::Handle<JS::Value> aValue,
1073 PromiseTaskSync aAsynchronous)
1075 mResolvePending = true;
1077 if (aValue.isObject()) {
1078 AutoDontReportUncaught silenceReporting(aCx);
1079 JS::Rooted<JSObject*> valueObj(aCx, &aValue.toObject());
1081 // Thenables.
1082 JS::Rooted<JS::Value> then(aCx);
1083 if (!JS_GetProperty(aCx, valueObj, "then", &then)) {
1084 HandleException(aCx);
1085 return;
1088 if (then.isObject() && JS_ObjectIsCallable(aCx, &then.toObject())) {
1089 // This is the then() function of the thenable aValueObj.
1090 JS::Rooted<JSObject*> thenObj(aCx, &then.toObject());
1091 nsRefPtr<PromiseInit> thenCallback =
1092 new PromiseInit(thenObj, mozilla::dom::GetIncumbentGlobal());
1093 nsRefPtr<ThenableResolverTask> task =
1094 new ThenableResolverTask(this, valueObj, thenCallback);
1095 DispatchToMainOrWorkerThread(task);
1096 return;
1100 // If the synchronous flag is set, process our resolve callbacks with
1101 // value. Otherwise, the synchronous flag is unset, queue a task to process
1102 // own resolve callbacks with value. Otherwise, the synchronous flag is
1103 // unset, queue a task to process our resolve callbacks with value.
1104 RunResolveTask(aValue, Resolved, aAsynchronous);
1107 void
1108 Promise::RejectInternal(JSContext* aCx,
1109 JS::Handle<JS::Value> aValue,
1110 PromiseTaskSync aAsynchronous)
1112 mResolvePending = true;
1114 // If the synchronous flag is set, process our reject callbacks with
1115 // value. Otherwise, the synchronous flag is unset, queue a task to process
1116 // promise's reject callbacks with value.
1117 RunResolveTask(aValue, Rejected, aAsynchronous);
1120 void
1121 Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
1122 PromiseState aState,
1123 PromiseTaskSync aAsynchronous)
1125 // If the synchronous flag is unset, queue a task to process our
1126 // accept callbacks with value.
1127 if (aAsynchronous == AsyncTask) {
1128 nsRefPtr<PromiseResolverTask> task =
1129 new PromiseResolverTask(this, aValue, aState);
1130 DispatchToMainOrWorkerThread(task);
1131 return;
1134 // Promise.all() or Promise.race() implementations will repeatedly call
1135 // Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState
1136 // from asserting.
1137 if (mState != Pending) {
1138 return;
1141 SetResult(aValue);
1142 SetState(aState);
1144 // If the Promise was rejected, and there is no reject handler already setup,
1145 // watch for thread shutdown.
1146 if (aState == PromiseState::Rejected &&
1147 !mHadRejectCallback &&
1148 !NS_IsMainThread()) {
1149 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
1150 MOZ_ASSERT(worker);
1151 worker->AssertIsOnWorkerThread();
1153 mFeature = new PromiseReportRejectFeature(this);
1154 if (NS_WARN_IF(!worker->AddFeature(worker->GetJSContext(), mFeature))) {
1155 // To avoid a false RemoveFeature().
1156 mFeature = nullptr;
1157 // Worker is shutting down, report rejection immediately since it is
1158 // unlikely that reject callbacks will be added after this point.
1159 MaybeReportRejectedOnce();
1163 RunTask();
1166 void
1167 Promise::RemoveFeature()
1169 if (mFeature) {
1170 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
1171 MOZ_ASSERT(worker);
1172 worker->RemoveFeature(worker->GetJSContext(), mFeature);
1173 mFeature = nullptr;
1177 bool
1178 PromiseReportRejectFeature::Notify(JSContext* aCx, workers::Status aStatus)
1180 MOZ_ASSERT(aStatus > workers::Running);
1181 mPromise->MaybeReportRejectedOnce();
1182 // After this point, `this` has been deleted by RemoveFeature!
1183 return true;
1186 // A WorkerRunnable to resolve/reject the Promise on the worker thread.
1188 class PromiseWorkerProxyRunnable : public workers::WorkerRunnable
1190 public:
1191 PromiseWorkerProxyRunnable(PromiseWorkerProxy* aPromiseWorkerProxy,
1192 JSStructuredCloneCallbacks* aCallbacks,
1193 JSAutoStructuredCloneBuffer&& aBuffer,
1194 PromiseWorkerProxy::RunCallbackFunc aFunc)
1195 : WorkerRunnable(aPromiseWorkerProxy->GetWorkerPrivate(),
1196 WorkerThreadUnchangedBusyCount)
1197 , mPromiseWorkerProxy(aPromiseWorkerProxy)
1198 , mCallbacks(aCallbacks)
1199 , mBuffer(Move(aBuffer))
1200 , mFunc(aFunc)
1202 MOZ_ASSERT(NS_IsMainThread());
1203 MOZ_ASSERT(mPromiseWorkerProxy);
1206 virtual bool
1207 WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate)
1209 MOZ_ASSERT(aWorkerPrivate);
1210 aWorkerPrivate->AssertIsOnWorkerThread();
1211 MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
1213 MOZ_ASSERT(mPromiseWorkerProxy);
1214 nsRefPtr<Promise> workerPromise = mPromiseWorkerProxy->GetWorkerPromise();
1215 MOZ_ASSERT(workerPromise);
1217 // Here we convert the buffer to a JS::Value.
1218 JS::Rooted<JS::Value> value(aCx);
1219 if (!mBuffer.read(aCx, &value, mCallbacks, mPromiseWorkerProxy)) {
1220 JS_ClearPendingException(aCx);
1221 return false;
1224 // TODO Bug 975246 - nsRefPtr should support operator |nsRefPtr->*funcType|.
1225 (workerPromise.get()->*mFunc)(aCx,
1226 value,
1227 Promise::PromiseTaskSync::SyncTask);
1229 // Release the Promise because it has been resolved/rejected for sure.
1230 mPromiseWorkerProxy->CleanUp(aCx);
1231 return true;
1234 protected:
1235 ~PromiseWorkerProxyRunnable() {}
1237 private:
1238 nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
1239 JSStructuredCloneCallbacks* mCallbacks;
1240 JSAutoStructuredCloneBuffer mBuffer;
1242 // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
1243 PromiseWorkerProxy::RunCallbackFunc mFunc;
1246 PromiseWorkerProxy::PromiseWorkerProxy(WorkerPrivate* aWorkerPrivate,
1247 Promise* aWorkerPromise,
1248 JSStructuredCloneCallbacks* aCallbacks)
1249 : mWorkerPrivate(aWorkerPrivate)
1250 , mWorkerPromise(aWorkerPromise)
1251 , mCleanedUp(false)
1252 , mCallbacks(aCallbacks)
1253 , mCleanUpLock("cleanUpLock")
1255 MOZ_ASSERT(mWorkerPrivate);
1256 mWorkerPrivate->AssertIsOnWorkerThread();
1257 MOZ_ASSERT(mWorkerPromise);
1259 // We do this to make sure the worker thread won't shut down before the
1260 // promise is resolved/rejected on the worker thread.
1261 if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
1262 MOZ_ASSERT(false, "cannot add the worker feature!");
1263 return;
1267 PromiseWorkerProxy::~PromiseWorkerProxy()
1269 MOZ_ASSERT(mCleanedUp);
1270 MOZ_ASSERT(!mWorkerPromise);
1273 WorkerPrivate*
1274 PromiseWorkerProxy::GetWorkerPrivate() const
1276 // It's ok to race on |mCleanedUp|, because it will never cause us to fire
1277 // the assertion when we should not.
1278 MOZ_ASSERT(!mCleanedUp);
1280 return mWorkerPrivate;
1283 Promise*
1284 PromiseWorkerProxy::GetWorkerPromise() const
1286 return mWorkerPromise;
1289 void
1290 PromiseWorkerProxy::StoreISupports(nsISupports* aSupports)
1292 MOZ_ASSERT(NS_IsMainThread());
1294 nsMainThreadPtrHandle<nsISupports> supports(
1295 new nsMainThreadPtrHolder<nsISupports>(aSupports));
1296 mSupportsArray.AppendElement(supports);
1299 void
1300 PromiseWorkerProxy::RunCallback(JSContext* aCx,
1301 JS::Handle<JS::Value> aValue,
1302 RunCallbackFunc aFunc)
1304 MOZ_ASSERT(NS_IsMainThread());
1306 MutexAutoLock lock(mCleanUpLock);
1307 // If the worker thread's been cancelled we don't need to resolve the Promise.
1308 if (mCleanedUp) {
1309 return;
1312 // The |aValue| is written into the buffer. Note that we also pass |this|
1313 // into the structured-clone write in order to set its |mSupportsArray| to
1314 // keep objects alive until the structured-clone read/write is done.
1315 JSAutoStructuredCloneBuffer buffer;
1316 if (!buffer.write(aCx, aValue, mCallbacks, this)) {
1317 JS_ClearPendingException(aCx);
1318 MOZ_ASSERT(false, "cannot write the JSAutoStructuredCloneBuffer!");
1321 nsRefPtr<PromiseWorkerProxyRunnable> runnable =
1322 new PromiseWorkerProxyRunnable(this,
1323 mCallbacks,
1324 Move(buffer),
1325 aFunc);
1327 runnable->Dispatch(aCx);
1330 void
1331 PromiseWorkerProxy::ResolvedCallback(JSContext* aCx,
1332 JS::Handle<JS::Value> aValue)
1334 RunCallback(aCx, aValue, &Promise::ResolveInternal);
1337 void
1338 PromiseWorkerProxy::RejectedCallback(JSContext* aCx,
1339 JS::Handle<JS::Value> aValue)
1341 RunCallback(aCx, aValue, &Promise::RejectInternal);
1344 bool
1345 PromiseWorkerProxy::Notify(JSContext* aCx, Status aStatus)
1347 MOZ_ASSERT(mWorkerPrivate);
1348 mWorkerPrivate->AssertIsOnWorkerThread();
1349 MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
1351 if (aStatus >= Canceling) {
1352 CleanUp(aCx);
1355 return true;
1358 void
1359 PromiseWorkerProxy::CleanUp(JSContext* aCx)
1361 MutexAutoLock lock(mCleanUpLock);
1363 // |mWorkerPrivate| might not be safe to use anymore if we have already
1364 // cleaned up and RemoveFeature(), so we need to check |mCleanedUp| first.
1365 if (mCleanedUp) {
1366 return;
1369 MOZ_ASSERT(mWorkerPrivate);
1370 mWorkerPrivate->AssertIsOnWorkerThread();
1371 MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
1373 // Release the Promise and remove the PromiseWorkerProxy from the features of
1374 // the worker thread since the Promise has been resolved/rejected or the
1375 // worker thread has been cancelled.
1376 mWorkerPromise = nullptr;
1377 mWorkerPrivate->RemoveFeature(aCx, this);
1378 mCleanedUp = true;
1381 // Specializations of MaybeRejectBrokenly we actually support.
1382 template<>
1383 void Promise::MaybeRejectBrokenly(const nsRefPtr<DOMError>& aArg) {
1384 MaybeSomething(aArg, &Promise::MaybeReject);
1386 template<>
1387 void Promise::MaybeRejectBrokenly(const nsAString& aArg) {
1388 MaybeSomething(aArg, &Promise::MaybeReject);
1391 } // namespace dom
1392 } // namespace mozilla