LCOV - code coverage report
Current view: top level - dom/promise - Promise.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 48 418 11.5 %
Date: 2017-07-14 16:53:18 Functions: 12 79 15.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=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/. */
       6             : 
       7             : #include "mozilla/dom/Promise.h"
       8             : 
       9             : #include "js/Debug.h"
      10             : 
      11             : #include "mozilla/Atomics.h"
      12             : #include "mozilla/CycleCollectedJSContext.h"
      13             : #include "mozilla/OwningNonNull.h"
      14             : #include "mozilla/Preferences.h"
      15             : 
      16             : #include "mozilla/dom/BindingUtils.h"
      17             : #include "mozilla/dom/DOMError.h"
      18             : #include "mozilla/dom/DOMException.h"
      19             : #include "mozilla/dom/DOMExceptionBinding.h"
      20             : #include "mozilla/dom/MediaStreamError.h"
      21             : #include "mozilla/dom/PromiseBinding.h"
      22             : #include "mozilla/dom/ScriptSettings.h"
      23             : 
      24             : #include "jsfriendapi.h"
      25             : #include "js/StructuredClone.h"
      26             : #include "nsContentUtils.h"
      27             : #include "nsGlobalWindow.h"
      28             : #include "nsIScriptObjectPrincipal.h"
      29             : #include "nsJSEnvironment.h"
      30             : #include "nsJSPrincipals.h"
      31             : #include "nsJSUtils.h"
      32             : #include "nsPIDOMWindow.h"
      33             : #include "PromiseDebugging.h"
      34             : #include "PromiseNativeHandler.h"
      35             : #include "PromiseWorkerProxy.h"
      36             : #include "WorkerPrivate.h"
      37             : #include "WorkerRunnable.h"
      38             : #include "WrapperFactory.h"
      39             : #include "xpcpublic.h"
      40             : #ifdef MOZ_CRASHREPORTER
      41             : #include "nsExceptionHandler.h"
      42             : #endif
      43             : 
      44             : namespace mozilla {
      45             : namespace dom {
      46             : 
      47             : namespace {
      48             : // Generator used by Promise::GetID.
      49             : Atomic<uintptr_t> gIDGenerator(0);
      50             : } // namespace
      51             : 
      52             : using namespace workers;
      53             : 
      54             : // Promise
      55             : 
      56             : NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
      57             : 
      58           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
      59           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
      60           0 :   tmp->mPromiseObj = nullptr;
      61           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      62             : 
      63           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
      64           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
      65           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      66             : 
      67           6 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
      68           6 :   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPromiseObj);
      69           6 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
      70             : 
      71           4 : NS_IMPL_CYCLE_COLLECTING_ADDREF(Promise)
      72           6 : NS_IMPL_CYCLE_COLLECTING_RELEASE(Promise)
      73             : 
      74          30 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Promise)
      75           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
      76           0 :   NS_INTERFACE_MAP_ENTRY(Promise)
      77           0 : NS_INTERFACE_MAP_END
      78             : 
      79           2 : Promise::Promise(nsIGlobalObject* aGlobal)
      80             :   : mGlobal(aGlobal)
      81           2 :   , mPromiseObj(nullptr)
      82             : {
      83           2 :   MOZ_ASSERT(mGlobal);
      84             : 
      85           2 :   mozilla::HoldJSObjects(this);
      86           2 : }
      87             : 
      88           6 : Promise::~Promise()
      89             : {
      90           2 :   mozilla::DropJSObjects(this);
      91           6 : }
      92             : 
      93             : // static
      94             : already_AddRefed<Promise>
      95           2 : Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
      96             : {
      97           2 :   if (!aGlobal) {
      98           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
      99           0 :     return nullptr;
     100             :   }
     101           4 :   RefPtr<Promise> p = new Promise(aGlobal);
     102           2 :   p->CreateWrapper(nullptr, aRv);
     103           2 :   if (aRv.Failed()) {
     104           0 :     return nullptr;
     105             :   }
     106           2 :   return p.forget();
     107             : }
     108             : 
     109             : // static
     110             : already_AddRefed<Promise>
     111           0 : Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
     112             :                  JS::Handle<JS::Value> aValue, ErrorResult& aRv)
     113             : {
     114           0 :   JSAutoCompartment ac(aCx, aGlobal->GetGlobalJSObject());
     115             :   JS::Rooted<JSObject*> p(aCx,
     116           0 :                           JS::CallOriginalPromiseResolve(aCx, aValue));
     117           0 :   if (!p) {
     118           0 :     aRv.NoteJSContextException(aCx);
     119           0 :     return nullptr;
     120             :   }
     121             : 
     122           0 :   return CreateFromExisting(aGlobal, p);
     123             : }
     124             : 
     125             : // static
     126             : already_AddRefed<Promise>
     127           0 : Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
     128             :                 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
     129             : {
     130           0 :   JSAutoCompartment ac(aCx, aGlobal->GetGlobalJSObject());
     131             :   JS::Rooted<JSObject*> p(aCx,
     132           0 :                           JS::CallOriginalPromiseReject(aCx, aValue));
     133           0 :   if (!p) {
     134           0 :     aRv.NoteJSContextException(aCx);
     135           0 :     return nullptr;
     136             :   }
     137             : 
     138           0 :   return CreateFromExisting(aGlobal, p);
     139             : }
     140             : 
     141             : // static
     142             : already_AddRefed<Promise>
     143           0 : Promise::All(const GlobalObject& aGlobal,
     144             :              const nsTArray<RefPtr<Promise>>& aPromiseList, ErrorResult& aRv)
     145             : {
     146           0 :   nsCOMPtr<nsIGlobalObject> global;
     147           0 :   global = do_QueryInterface(aGlobal.GetAsSupports());
     148           0 :   if (!global) {
     149           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
     150           0 :     return nullptr;
     151             :   }
     152             : 
     153           0 :   JSContext* cx = aGlobal.Context();
     154             : 
     155           0 :   JS::AutoObjectVector promises(cx);
     156           0 :   if (!promises.reserve(aPromiseList.Length())) {
     157           0 :     aRv.NoteJSContextException(cx);
     158           0 :     return nullptr;
     159             :   }
     160             : 
     161           0 :   for (auto& promise : aPromiseList) {
     162           0 :     JS::Rooted<JSObject*> promiseObj(cx, promise->PromiseObj());
     163             :     // Just in case, make sure these are all in the context compartment.
     164           0 :     if (!JS_WrapObject(cx, &promiseObj)) {
     165           0 :       aRv.NoteJSContextException(cx);
     166           0 :       return nullptr;
     167             :     }
     168           0 :     promises.infallibleAppend(promiseObj);
     169             :   }
     170             : 
     171           0 :   JS::Rooted<JSObject*> result(cx, JS::GetWaitForAllPromise(cx, promises));
     172           0 :   if (!result) {
     173           0 :     aRv.NoteJSContextException(cx);
     174           0 :     return nullptr;
     175             :   }
     176             : 
     177           0 :   return CreateFromExisting(global, result);
     178             : }
     179             : 
     180             : void
     181           0 : Promise::Then(JSContext* aCx,
     182             :               // aCalleeGlobal may not be in the compartment of aCx, when called over
     183             :               // Xrays.
     184             :               JS::Handle<JSObject*> aCalleeGlobal,
     185             :               AnyCallback* aResolveCallback, AnyCallback* aRejectCallback,
     186             :               JS::MutableHandle<JS::Value> aRetval,
     187             :               ErrorResult& aRv)
     188             : {
     189           0 :   NS_ASSERT_OWNINGTHREAD(Promise);
     190             : 
     191             :   // Let's hope this does the right thing with Xrays...  Ensure everything is
     192             :   // just in the caller compartment; that ought to do the trick.  In theory we
     193             :   // should consider aCalleeGlobal, but in practice our only caller is
     194             :   // DOMRequest::Then, which is not working with a Promise subclass, so things
     195             :   // should be OK.
     196           0 :   JS::Rooted<JSObject*> promise(aCx, PromiseObj());
     197           0 :   if (!JS_WrapObject(aCx, &promise)) {
     198           0 :     aRv.NoteJSContextException(aCx);
     199           0 :     return;
     200             :   }
     201             : 
     202           0 :   JS::Rooted<JSObject*> resolveCallback(aCx);
     203           0 :   if (aResolveCallback) {
     204           0 :     resolveCallback = aResolveCallback->CallbackOrNull();
     205           0 :     if (!JS_WrapObject(aCx, &resolveCallback)) {
     206           0 :       aRv.NoteJSContextException(aCx);
     207           0 :       return;
     208             :     }
     209             :   }
     210             : 
     211           0 :   JS::Rooted<JSObject*> rejectCallback(aCx);
     212           0 :   if (aRejectCallback) {
     213           0 :     rejectCallback = aRejectCallback->CallbackOrNull();
     214           0 :     if (!JS_WrapObject(aCx, &rejectCallback)) {
     215           0 :       aRv.NoteJSContextException(aCx);
     216           0 :       return;
     217             :     }
     218             :   }
     219             : 
     220           0 :   JS::Rooted<JSObject*> retval(aCx);
     221           0 :   retval = JS::CallOriginalPromiseThen(aCx, promise, resolveCallback,
     222           0 :                                        rejectCallback);
     223           0 :   if (!retval) {
     224           0 :     aRv.NoteJSContextException(aCx);
     225           0 :     return;
     226             :   }
     227             : 
     228           0 :   aRetval.setObject(*retval);
     229             : }
     230             : 
     231             : void
     232           2 : Promise::CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv)
     233             : {
     234           4 :   AutoJSAPI jsapi;
     235           2 :   if (!jsapi.Init(mGlobal)) {
     236           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
     237           0 :     return;
     238             :   }
     239           2 :   JSContext* cx = jsapi.cx();
     240           2 :   mPromiseObj = JS::NewPromiseObject(cx, nullptr, aDesiredProto);
     241           2 :   if (!mPromiseObj) {
     242           0 :     JS_ClearPendingException(cx);
     243           0 :     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     244           0 :     return;
     245             :   }
     246             : }
     247             : 
     248             : void
     249           2 : Promise::MaybeResolve(JSContext* aCx,
     250             :                       JS::Handle<JS::Value> aValue)
     251             : {
     252           2 :   NS_ASSERT_OWNINGTHREAD(Promise);
     253             : 
     254           4 :   JS::Rooted<JSObject*> p(aCx, PromiseObj());
     255           2 :   if (!JS::ResolvePromise(aCx, p, aValue)) {
     256             :     // Now what?  There's nothing sane to do here.
     257           0 :     JS_ClearPendingException(aCx);
     258             :   }
     259           2 : }
     260             : 
     261             : void
     262           0 : Promise::MaybeReject(JSContext* aCx,
     263             :                      JS::Handle<JS::Value> aValue)
     264             : {
     265           0 :   NS_ASSERT_OWNINGTHREAD(Promise);
     266             : 
     267           0 :   JS::Rooted<JSObject*> p(aCx, PromiseObj());
     268           0 :   if (!JS::RejectPromise(aCx, p, aValue)) {
     269             :     // Now what?  There's nothing sane to do here.
     270           0 :     JS_ClearPendingException(aCx);
     271             :   }
     272           0 : }
     273             : 
     274             : #define SLOT_NATIVEHANDLER 0
     275             : #define SLOT_NATIVEHANDLER_TASK 1
     276             : 
     277             : enum class NativeHandlerTask : int32_t {
     278             :   Resolve,
     279             :   Reject
     280             : };
     281             : 
     282             : static bool
     283           0 : NativeHandlerCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
     284             : {
     285           0 :   JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
     286             : 
     287           0 :   JS::Value v = js::GetFunctionNativeReserved(&args.callee(),
     288           0 :                                               SLOT_NATIVEHANDLER);
     289           0 :   MOZ_ASSERT(v.isObject());
     290             : 
     291           0 :   JS::Rooted<JSObject*> obj(aCx, &v.toObject());
     292           0 :   PromiseNativeHandler* handler = nullptr;
     293           0 :   if (NS_FAILED(UNWRAP_OBJECT(PromiseNativeHandler, &obj, handler))) {
     294           0 :     return Throw(aCx, NS_ERROR_UNEXPECTED);
     295             :   }
     296             : 
     297           0 :   v = js::GetFunctionNativeReserved(&args.callee(), SLOT_NATIVEHANDLER_TASK);
     298           0 :   NativeHandlerTask task = static_cast<NativeHandlerTask>(v.toInt32());
     299             : 
     300           0 :   if (task == NativeHandlerTask::Resolve) {
     301           0 :     handler->ResolvedCallback(aCx, args.get(0));
     302             :   } else {
     303           0 :     MOZ_ASSERT(task == NativeHandlerTask::Reject);
     304           0 :     handler->RejectedCallback(aCx, args.get(0));
     305             :   }
     306             : 
     307           0 :   return true;
     308             : }
     309             : 
     310             : static JSObject*
     311           0 : CreateNativeHandlerFunction(JSContext* aCx, JS::Handle<JSObject*> aHolder,
     312             :                             NativeHandlerTask aTask)
     313             : {
     314           0 :   JSFunction* func = js::NewFunctionWithReserved(aCx, NativeHandlerCallback,
     315             :                                                  /* nargs = */ 1,
     316           0 :                                                  /* flags = */ 0, nullptr);
     317           0 :   if (!func) {
     318           0 :     return nullptr;
     319             :   }
     320             : 
     321           0 :   JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
     322             : 
     323           0 :   JS::ExposeObjectToActiveJS(aHolder);
     324           0 :   js::SetFunctionNativeReserved(obj, SLOT_NATIVEHANDLER,
     325           0 :                                 JS::ObjectValue(*aHolder));
     326           0 :   js::SetFunctionNativeReserved(obj, SLOT_NATIVEHANDLER_TASK,
     327           0 :                                 JS::Int32Value(static_cast<int32_t>(aTask)));
     328             : 
     329           0 :   return obj;
     330             : }
     331             : 
     332             : namespace {
     333             : 
     334             : class PromiseNativeHandlerShim final : public PromiseNativeHandler
     335             : {
     336             :   RefPtr<PromiseNativeHandler> mInner;
     337             : 
     338           0 :   ~PromiseNativeHandlerShim()
     339           0 :   {
     340           0 :   }
     341             : 
     342             : public:
     343           0 :   explicit PromiseNativeHandlerShim(PromiseNativeHandler* aInner)
     344           0 :     : mInner(aInner)
     345             :   {
     346           0 :     MOZ_ASSERT(mInner);
     347           0 :   }
     348             : 
     349             :   void
     350           0 :   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
     351             :   {
     352           0 :     mInner->ResolvedCallback(aCx, aValue);
     353           0 :     mInner = nullptr;
     354           0 :   }
     355             : 
     356             :   void
     357           0 :   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
     358             :   {
     359           0 :     mInner->RejectedCallback(aCx, aValue);
     360           0 :     mInner = nullptr;
     361           0 :   }
     362             : 
     363             :   bool
     364           0 :   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
     365             :              JS::MutableHandle<JSObject*> aWrapper)
     366             :   {
     367           0 :     return PromiseNativeHandlerBinding::Wrap(aCx, this, aGivenProto, aWrapper);
     368             :   }
     369             : 
     370             :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     371           0 :   NS_DECL_CYCLE_COLLECTION_CLASS(PromiseNativeHandlerShim)
     372             : };
     373             : 
     374           0 : NS_IMPL_CYCLE_COLLECTION(PromiseNativeHandlerShim, mInner)
     375             : 
     376           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseNativeHandlerShim)
     377           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseNativeHandlerShim)
     378             : 
     379           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseNativeHandlerShim)
     380           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     381           0 : NS_INTERFACE_MAP_END
     382             : 
     383             : } // anonymous namespace
     384             : 
     385             : void
     386           0 : Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
     387             : {
     388           0 :   NS_ASSERT_OWNINGTHREAD(Promise);
     389             : 
     390           0 :   AutoJSAPI jsapi;
     391           0 :   if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
     392             :     // Our API doesn't allow us to return a useful error.  Not like this should
     393             :     // happen anyway.
     394           0 :     return;
     395             :   }
     396             : 
     397             :   // The self-hosted promise js may keep the object we pass to it alive
     398             :   // for quite a while depending on when GC runs.  Therefore, pass a shim
     399             :   // object instead.  The shim will free its inner PromiseNativeHandler
     400             :   // after the promise has settled just like our previous c++ promises did.
     401             :   RefPtr<PromiseNativeHandlerShim> shim =
     402           0 :     new PromiseNativeHandlerShim(aRunnable);
     403             : 
     404           0 :   JSContext* cx = jsapi.cx();
     405           0 :   JS::Rooted<JSObject*> handlerWrapper(cx);
     406             :   // Note: PromiseNativeHandler is NOT wrappercached.  So we can't use
     407             :   // ToJSValue here, because it will try to do XPConnect wrapping on it, sadly.
     408           0 :   if (NS_WARN_IF(!shim->WrapObject(cx, nullptr, &handlerWrapper))) {
     409             :     // Again, no way to report errors.
     410           0 :     jsapi.ClearException();
     411           0 :     return;
     412             :   }
     413             : 
     414           0 :   JS::Rooted<JSObject*> resolveFunc(cx);
     415             :   resolveFunc =
     416           0 :     CreateNativeHandlerFunction(cx, handlerWrapper, NativeHandlerTask::Resolve);
     417           0 :   if (NS_WARN_IF(!resolveFunc)) {
     418           0 :     jsapi.ClearException();
     419           0 :     return;
     420             :   }
     421             : 
     422           0 :   JS::Rooted<JSObject*> rejectFunc(cx);
     423             :   rejectFunc =
     424           0 :     CreateNativeHandlerFunction(cx, handlerWrapper, NativeHandlerTask::Reject);
     425           0 :   if (NS_WARN_IF(!rejectFunc)) {
     426           0 :     jsapi.ClearException();
     427           0 :     return;
     428             :   }
     429             : 
     430           0 :   JS::Rooted<JSObject*> promiseObj(cx, PromiseObj());
     431           0 :   if (NS_WARN_IF(!JS::AddPromiseReactions(cx, promiseObj, resolveFunc,
     432             :                                           rejectFunc))) {
     433           0 :     jsapi.ClearException();
     434           0 :     return;
     435             :   }
     436             : }
     437             : 
     438             : void
     439           0 : Promise::HandleException(JSContext* aCx)
     440             : {
     441           0 :   JS::Rooted<JS::Value> exn(aCx);
     442           0 :   if (JS_GetPendingException(aCx, &exn)) {
     443           0 :     JS_ClearPendingException(aCx);
     444             :     // This is only called from MaybeSomething, so it's OK to MaybeReject here.
     445           0 :     MaybeReject(aCx, exn);
     446             :   }
     447           0 : }
     448             : 
     449             : // static
     450             : already_AddRefed<Promise>
     451           0 : Promise::CreateFromExisting(nsIGlobalObject* aGlobal,
     452             :                             JS::Handle<JSObject*> aPromiseObj)
     453             : {
     454           0 :   MOZ_ASSERT(js::GetObjectCompartment(aGlobal->GetGlobalJSObject()) ==
     455             :              js::GetObjectCompartment(aPromiseObj));
     456           0 :   RefPtr<Promise> p = new Promise(aGlobal);
     457           0 :   p->mPromiseObj = aPromiseObj;
     458           0 :   return p.forget();
     459             : }
     460             : 
     461             : 
     462             : void
     463           0 : Promise::MaybeResolveWithUndefined()
     464             : {
     465           0 :   NS_ASSERT_OWNINGTHREAD(Promise);
     466             : 
     467           0 :   MaybeResolve(JS::UndefinedHandleValue);
     468           0 : }
     469             : 
     470             : void
     471           0 : Promise::MaybeReject(const RefPtr<MediaStreamError>& aArg) {
     472           0 :   NS_ASSERT_OWNINGTHREAD(Promise);
     473             : 
     474           0 :   MaybeSomething(aArg, &Promise::MaybeReject);
     475           0 : }
     476             : 
     477             : void
     478           0 : Promise::MaybeRejectWithUndefined()
     479             : {
     480           0 :   NS_ASSERT_OWNINGTHREAD(Promise);
     481             : 
     482           0 :   MaybeSomething(JS::UndefinedHandleValue, &Promise::MaybeReject);
     483           0 : }
     484             : 
     485             : void
     486           0 : Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise)
     487             : {
     488           0 :   MOZ_ASSERT(!js::IsWrapper(aPromise));
     489             : 
     490           0 :   MOZ_ASSERT(JS::GetPromiseState(aPromise) == JS::PromiseState::Rejected);
     491             : 
     492           0 :   JS::Rooted<JS::Value> result(aCx, JS::GetPromiseResult(aPromise));
     493             : 
     494           0 :   js::ErrorReport report(aCx);
     495           0 :   if (!report.init(aCx, result, js::ErrorReport::NoSideEffects)) {
     496           0 :     JS_ClearPendingException(aCx);
     497           0 :     return;
     498             :   }
     499             : 
     500           0 :   RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
     501           0 :   bool isMainThread = MOZ_LIKELY(NS_IsMainThread());
     502           0 :   bool isChrome = isMainThread ? nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(aPromise))
     503           0 :                                : GetCurrentThreadWorkerPrivate()->IsChromeWorker();
     504           0 :   nsGlobalWindow* win = isMainThread ? xpc::WindowGlobalOrNull(aPromise) : nullptr;
     505           0 :   xpcReport->Init(report.report(), report.toStringResult().c_str(), isChrome,
     506           0 :                   win ? win->AsInner()->WindowID() : 0);
     507             : 
     508             :   // Now post an event to do the real reporting async
     509           0 :   NS_DispatchToMainThread(new AsyncErrorReporter(xpcReport));
     510             : }
     511             : 
     512             : bool
     513        1324 : Promise::PerformMicroTaskCheckpoint()
     514             : {
     515        1324 :   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
     516             : 
     517        1324 :   CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
     518             : 
     519             :   // On the main thread, we always use the main promise micro task queue.
     520             :   std::queue<nsCOMPtr<nsIRunnable>>& microtaskQueue =
     521        1324 :     context->GetPromiseMicroTaskQueue();
     522             : 
     523        1324 :   if (microtaskQueue.empty()) {
     524        1299 :     return false;
     525             :   }
     526             : 
     527          50 :   AutoSlowOperation aso;
     528             : 
     529         237 :   do {
     530         474 :     nsCOMPtr<nsIRunnable> runnable = microtaskQueue.front().forget();
     531         237 :     MOZ_ASSERT(runnable);
     532             : 
     533             :     // This function can re-enter, so we remove the element before calling.
     534         237 :     microtaskQueue.pop();
     535         237 :     nsresult rv = runnable->Run();
     536         237 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     537           0 :       return false;
     538             :     }
     539         237 :     aso.CheckForInterrupt();
     540         237 :     context->AfterProcessMicrotask();
     541         237 :   } while (!microtaskQueue.empty());
     542             : 
     543          25 :   return true;
     544             : }
     545             : 
     546             : void
     547           0 : Promise::PerformWorkerMicroTaskCheckpoint()
     548             : {
     549           0 :   MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
     550             : 
     551           0 :   CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
     552           0 :   if (!context) {
     553           0 :     return;
     554             :   }
     555             : 
     556             :   for (;;) {
     557             :     // For a normal microtask checkpoint, we try to use the debugger microtask
     558             :     // queue first. If the debugger queue is empty, we use the normal microtask
     559             :     // queue instead.
     560             :     std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
     561           0 :       &context->GetDebuggerPromiseMicroTaskQueue();
     562             : 
     563           0 :     if (microtaskQueue->empty()) {
     564           0 :       microtaskQueue = &context->GetPromiseMicroTaskQueue();
     565           0 :       if (microtaskQueue->empty()) {
     566           0 :         break;
     567             :       }
     568             :     }
     569             : 
     570           0 :     nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front().forget();
     571           0 :     MOZ_ASSERT(runnable);
     572             : 
     573             :     // This function can re-enter, so we remove the element before calling.
     574           0 :     microtaskQueue->pop();
     575           0 :     nsresult rv = runnable->Run();
     576           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     577           0 :       return;
     578             :     }
     579           0 :     context->AfterProcessMicrotask();
     580           0 :   }
     581             : }
     582             : 
     583             : void
     584           0 : Promise::PerformWorkerDebuggerMicroTaskCheckpoint()
     585             : {
     586           0 :   MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
     587             : 
     588           0 :   CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
     589           0 :   if (!context) {
     590           0 :     return;
     591             :   }
     592             : 
     593             :   for (;;) {
     594             :     // For a debugger microtask checkpoint, we always use the debugger microtask
     595             :     // queue.
     596             :     std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
     597           0 :       &context->GetDebuggerPromiseMicroTaskQueue();
     598             : 
     599           0 :     if (microtaskQueue->empty()) {
     600           0 :       break;
     601             :     }
     602             : 
     603           0 :     nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front().forget();
     604           0 :     MOZ_ASSERT(runnable);
     605             : 
     606             :     // This function can re-enter, so we remove the element before calling.
     607           0 :     microtaskQueue->pop();
     608           0 :     nsresult rv = runnable->Run();
     609           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     610           0 :       return;
     611             :     }
     612           0 :     context->AfterProcessMicrotask();
     613           0 :   }
     614             : }
     615             : 
     616             : JSObject*
     617           0 : Promise::GlobalJSObject() const
     618             : {
     619           0 :   return mGlobal->GetGlobalJSObject();
     620             : }
     621             : 
     622             : JSCompartment*
     623           0 : Promise::Compartment() const
     624             : {
     625           0 :   return js::GetObjectCompartment(GlobalJSObject());
     626             : }
     627             : 
     628             : // A WorkerRunnable to resolve/reject the Promise on the worker thread.
     629             : // Calling thread MUST hold PromiseWorkerProxy's mutex before creating this.
     630             : class PromiseWorkerProxyRunnable : public WorkerRunnable
     631             : {
     632             : public:
     633           0 :   PromiseWorkerProxyRunnable(PromiseWorkerProxy* aPromiseWorkerProxy,
     634             :                              PromiseWorkerProxy::RunCallbackFunc aFunc)
     635           0 :     : WorkerRunnable(aPromiseWorkerProxy->GetWorkerPrivate(),
     636             :                      WorkerThreadUnchangedBusyCount)
     637             :     , mPromiseWorkerProxy(aPromiseWorkerProxy)
     638           0 :     , mFunc(aFunc)
     639             :   {
     640           0 :     MOZ_ASSERT(NS_IsMainThread());
     641           0 :     MOZ_ASSERT(mPromiseWorkerProxy);
     642           0 :   }
     643             : 
     644             :   virtual bool
     645           0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     646             :   {
     647           0 :     MOZ_ASSERT(aWorkerPrivate);
     648           0 :     aWorkerPrivate->AssertIsOnWorkerThread();
     649           0 :     MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
     650             : 
     651           0 :     MOZ_ASSERT(mPromiseWorkerProxy);
     652           0 :     RefPtr<Promise> workerPromise = mPromiseWorkerProxy->WorkerPromise();
     653             : 
     654             :     // Here we convert the buffer to a JS::Value.
     655           0 :     JS::Rooted<JS::Value> value(aCx);
     656           0 :     if (!mPromiseWorkerProxy->Read(aCx, &value)) {
     657           0 :       JS_ClearPendingException(aCx);
     658           0 :       return false;
     659             :     }
     660             : 
     661           0 :     (workerPromise->*mFunc)(aCx, value);
     662             : 
     663             :     // Release the Promise because it has been resolved/rejected for sure.
     664           0 :     mPromiseWorkerProxy->CleanUp();
     665           0 :     return true;
     666             :   }
     667             : 
     668             : protected:
     669           0 :   ~PromiseWorkerProxyRunnable() {}
     670             : 
     671             : private:
     672             :   RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
     673             : 
     674             :   // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
     675             :   PromiseWorkerProxy::RunCallbackFunc mFunc;
     676             : };
     677             : 
     678           0 : class PromiseWorkerHolder final : public WorkerHolder
     679             : {
     680             :   // RawPointer because this proxy keeps alive the holder.
     681             :   PromiseWorkerProxy* mProxy;
     682             : 
     683             : public:
     684           0 :   explicit PromiseWorkerHolder(PromiseWorkerProxy* aProxy)
     685           0 :     : mProxy(aProxy)
     686             :   {
     687           0 :     MOZ_ASSERT(aProxy);
     688           0 :   }
     689             : 
     690             :   bool
     691           0 :   Notify(Status aStatus) override
     692             :   {
     693           0 :     if (aStatus >= Canceling) {
     694           0 :       mProxy->CleanUp();
     695             :     }
     696             : 
     697           0 :     return true;
     698             :   }
     699             : };
     700             : 
     701             : /* static */
     702             : already_AddRefed<PromiseWorkerProxy>
     703           0 : PromiseWorkerProxy::Create(WorkerPrivate* aWorkerPrivate,
     704             :                            Promise* aWorkerPromise,
     705             :                            const PromiseWorkerProxyStructuredCloneCallbacks* aCb)
     706             : {
     707           0 :   MOZ_ASSERT(aWorkerPrivate);
     708           0 :   aWorkerPrivate->AssertIsOnWorkerThread();
     709           0 :   MOZ_ASSERT(aWorkerPromise);
     710           0 :   MOZ_ASSERT_IF(aCb, !!aCb->Write && !!aCb->Read);
     711             : 
     712             :   RefPtr<PromiseWorkerProxy> proxy =
     713           0 :     new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise, aCb);
     714             : 
     715             :   // We do this to make sure the worker thread won't shut down before the
     716             :   // promise is resolved/rejected on the worker thread.
     717           0 :   if (!proxy->AddRefObject()) {
     718             :     // Probably the worker is terminating. We cannot complete the operation
     719             :     // and we have to release all the resources.
     720           0 :     proxy->CleanProperties();
     721           0 :     return nullptr;
     722             :   }
     723             : 
     724           0 :   return proxy.forget();
     725             : }
     726             : 
     727           0 : NS_IMPL_ISUPPORTS0(PromiseWorkerProxy)
     728             : 
     729           0 : PromiseWorkerProxy::PromiseWorkerProxy(WorkerPrivate* aWorkerPrivate,
     730             :                                        Promise* aWorkerPromise,
     731           0 :                                        const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks)
     732             :   : mWorkerPrivate(aWorkerPrivate)
     733             :   , mWorkerPromise(aWorkerPromise)
     734             :   , mCleanedUp(false)
     735             :   , mCallbacks(aCallbacks)
     736           0 :   , mCleanUpLock("cleanUpLock")
     737             : {
     738           0 : }
     739             : 
     740           0 : PromiseWorkerProxy::~PromiseWorkerProxy()
     741             : {
     742           0 :   MOZ_ASSERT(mCleanedUp);
     743           0 :   MOZ_ASSERT(!mWorkerHolder);
     744           0 :   MOZ_ASSERT(!mWorkerPromise);
     745           0 :   MOZ_ASSERT(!mWorkerPrivate);
     746           0 : }
     747             : 
     748             : void
     749           0 : PromiseWorkerProxy::CleanProperties()
     750             : {
     751             : #ifdef DEBUG
     752           0 :   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     753           0 :   MOZ_ASSERT(worker);
     754           0 :   worker->AssertIsOnWorkerThread();
     755             : #endif
     756             :   // Ok to do this unprotected from Create().
     757             :   // CleanUp() holds the lock before calling this.
     758           0 :   mCleanedUp = true;
     759           0 :   mWorkerPromise = nullptr;
     760           0 :   mWorkerPrivate = nullptr;
     761             : 
     762             :   // Clear the StructuredCloneHolderBase class.
     763           0 :   Clear();
     764           0 : }
     765             : 
     766             : bool
     767           0 : PromiseWorkerProxy::AddRefObject()
     768             : {
     769           0 :   MOZ_ASSERT(mWorkerPrivate);
     770           0 :   mWorkerPrivate->AssertIsOnWorkerThread();
     771             : 
     772           0 :   MOZ_ASSERT(!mWorkerHolder);
     773           0 :   mWorkerHolder.reset(new PromiseWorkerHolder(this));
     774           0 :   if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate, Canceling))) {
     775           0 :     mWorkerHolder = nullptr;
     776           0 :     return false;
     777             :   }
     778             : 
     779             :   // Maintain a reference so that we have a valid object to clean up when
     780             :   // removing the feature.
     781           0 :   AddRef();
     782           0 :   return true;
     783             : }
     784             : 
     785             : WorkerPrivate*
     786           0 : PromiseWorkerProxy::GetWorkerPrivate() const
     787             : {
     788             : #ifdef DEBUG
     789           0 :   if (NS_IsMainThread()) {
     790           0 :     mCleanUpLock.AssertCurrentThreadOwns();
     791             :   }
     792             : #endif
     793             :   // Safe to check this without a lock since we assert lock ownership on the
     794             :   // main thread above.
     795           0 :   MOZ_ASSERT(!mCleanedUp);
     796           0 :   MOZ_ASSERT(mWorkerHolder);
     797             : 
     798           0 :   return mWorkerPrivate;
     799             : }
     800             : 
     801             : Promise*
     802           0 : PromiseWorkerProxy::WorkerPromise() const
     803             : {
     804             : #ifdef DEBUG
     805           0 :   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     806           0 :   MOZ_ASSERT(worker);
     807           0 :   worker->AssertIsOnWorkerThread();
     808             : #endif
     809           0 :   MOZ_ASSERT(mWorkerPromise);
     810           0 :   return mWorkerPromise;
     811             : }
     812             : 
     813             : void
     814           0 : PromiseWorkerProxy::RunCallback(JSContext* aCx,
     815             :                                 JS::Handle<JS::Value> aValue,
     816             :                                 RunCallbackFunc aFunc)
     817             : {
     818           0 :   MOZ_ASSERT(NS_IsMainThread());
     819             : 
     820           0 :   MutexAutoLock lock(Lock());
     821             :   // If the worker thread's been cancelled we don't need to resolve the Promise.
     822           0 :   if (CleanedUp()) {
     823           0 :     return;
     824             :   }
     825             : 
     826             :   // The |aValue| is written into the StructuredCloneHolderBase.
     827           0 :   if (!Write(aCx, aValue)) {
     828           0 :     JS_ClearPendingException(aCx);
     829           0 :     MOZ_ASSERT(false, "cannot serialize the value with the StructuredCloneAlgorithm!");
     830             :   }
     831             : 
     832             :   RefPtr<PromiseWorkerProxyRunnable> runnable =
     833           0 :     new PromiseWorkerProxyRunnable(this, aFunc);
     834             : 
     835           0 :   runnable->Dispatch();
     836             : }
     837             : 
     838             : void
     839           0 : PromiseWorkerProxy::ResolvedCallback(JSContext* aCx,
     840             :                                      JS::Handle<JS::Value> aValue)
     841             : {
     842           0 :   RunCallback(aCx, aValue, &Promise::MaybeResolve);
     843           0 : }
     844             : 
     845             : void
     846           0 : PromiseWorkerProxy::RejectedCallback(JSContext* aCx,
     847             :                                      JS::Handle<JS::Value> aValue)
     848             : {
     849           0 :   RunCallback(aCx, aValue, &Promise::MaybeReject);
     850           0 : }
     851             : 
     852             : void
     853           0 : PromiseWorkerProxy::CleanUp()
     854             : {
     855             :   // Can't release Mutex while it is still locked, so scope the lock.
     856             :   {
     857           0 :     MutexAutoLock lock(Lock());
     858             : 
     859             :     // |mWorkerPrivate| is not safe to use anymore if we have already
     860             :     // cleaned up and RemoveWorkerHolder(), so we need to check |mCleanedUp|
     861             :     // first.
     862           0 :     if (CleanedUp()) {
     863           0 :       return;
     864             :     }
     865             : 
     866           0 :     MOZ_ASSERT(mWorkerPrivate);
     867           0 :     mWorkerPrivate->AssertIsOnWorkerThread();
     868             : 
     869             :     // Release the Promise and remove the PromiseWorkerProxy from the holders of
     870             :     // the worker thread since the Promise has been resolved/rejected or the
     871             :     // worker thread has been cancelled.
     872           0 :     MOZ_ASSERT(mWorkerHolder);
     873           0 :     mWorkerHolder = nullptr;
     874             : 
     875           0 :     CleanProperties();
     876             :   }
     877           0 :   Release();
     878             : }
     879             : 
     880             : JSObject*
     881           0 : PromiseWorkerProxy::CustomReadHandler(JSContext* aCx,
     882             :                                       JSStructuredCloneReader* aReader,
     883             :                                       uint32_t aTag,
     884             :                                       uint32_t aIndex)
     885             : {
     886           0 :   if (NS_WARN_IF(!mCallbacks)) {
     887           0 :     return nullptr;
     888             :   }
     889             : 
     890           0 :   return mCallbacks->Read(aCx, aReader, this, aTag, aIndex);
     891             : }
     892             : 
     893             : bool
     894           0 : PromiseWorkerProxy::CustomWriteHandler(JSContext* aCx,
     895             :                                        JSStructuredCloneWriter* aWriter,
     896             :                                        JS::Handle<JSObject*> aObj)
     897             : {
     898           0 :   if (NS_WARN_IF(!mCallbacks)) {
     899           0 :     return false;
     900             :   }
     901             : 
     902           0 :   return mCallbacks->Write(aCx, aWriter, this, aObj);
     903             : }
     904             : 
     905             : // Specializations of MaybeRejectBrokenly we actually support.
     906             : template<>
     907           0 : void Promise::MaybeRejectBrokenly(const RefPtr<DOMError>& aArg) {
     908           0 :   MaybeSomething(aArg, &Promise::MaybeReject);
     909           0 : }
     910             : template<>
     911           0 : void Promise::MaybeRejectBrokenly(const nsAString& aArg) {
     912           0 :   MaybeSomething(aArg, &Promise::MaybeReject);
     913           0 : }
     914             : 
     915             : Promise::PromiseState
     916           0 : Promise::State() const
     917             : {
     918           0 :   JS::Rooted<JSObject*> p(RootingCx(), PromiseObj());
     919           0 :   const JS::PromiseState state = JS::GetPromiseState(p);
     920             : 
     921           0 :   if (state == JS::PromiseState::Fulfilled) {
     922           0 :       return PromiseState::Resolved;
     923             :   }
     924             : 
     925           0 :   if (state == JS::PromiseState::Rejected) {
     926           0 :       return PromiseState::Rejected;
     927             :   }
     928             : 
     929           0 :   return PromiseState::Pending;
     930             : }
     931             : 
     932             : } // namespace dom
     933             : } // namespace mozilla

Generated by: LCOV version 1.13