tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Promise.cpp (39879B)


      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 "PromiseDebugging.h"
     10 #include "PromiseNativeHandler.h"
     11 #include "PromiseWorkerProxy.h"
     12 #include "WrapperFactory.h"
     13 #include "js/Debug.h"
     14 #include "js/Exception.h"  // JS::ExceptionStack
     15 #include "js/Object.h"     // JS::GetCompartment
     16 #include "js/StructuredClone.h"
     17 #include "jsfriendapi.h"
     18 #include "mozilla/BasePrincipal.h"
     19 #include "mozilla/CycleCollectedJSContext.h"
     20 #include "mozilla/HoldDropJSObjects.h"
     21 #include "mozilla/OwningNonNull.h"
     22 #include "mozilla/Preferences.h"
     23 #include "mozilla/dom/AutoEntryScript.h"
     24 #include "mozilla/dom/BindingUtils.h"
     25 #include "mozilla/dom/DOMException.h"
     26 #include "mozilla/dom/DOMExceptionBinding.h"
     27 #include "mozilla/dom/Exceptions.h"
     28 #include "mozilla/dom/MediaStreamError.h"
     29 #include "mozilla/dom/Promise-inl.h"
     30 #include "mozilla/dom/PromiseBinding.h"
     31 #include "mozilla/dom/ScriptSettings.h"
     32 #include "mozilla/dom/UserActivation.h"
     33 #include "mozilla/dom/WorkerPrivate.h"
     34 #include "mozilla/dom/WorkerRef.h"
     35 #include "mozilla/dom/WorkerRunnable.h"
     36 #include "mozilla/dom/WorkletGlobalScope.h"
     37 #include "mozilla/dom/WorkletImpl.h"
     38 #include "mozilla/webgpu/PipelineError.h"
     39 #include "nsContentUtils.h"
     40 #include "nsCycleCollectionParticipant.h"
     41 #include "nsDebug.h"
     42 #include "nsGlobalWindowInner.h"
     43 #include "nsIScriptObjectPrincipal.h"
     44 #include "nsISupportsImpl.h"
     45 #include "nsJSEnvironment.h"
     46 #include "nsJSPrincipals.h"
     47 #include "nsJSUtils.h"
     48 #include "nsPIDOMWindow.h"
     49 #include "xpcprivate.h"
     50 #include "xpcpublic.h"
     51 
     52 namespace mozilla::dom {
     53 
     54 // Promise
     55 
     56 NS_IMPL_CYCLE_COLLECTION_SINGLE_ZONE_SCRIPT_HOLDER_CLASS(Promise)
     57 
     58 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
     59  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
     60  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
     61  tmp->mPromiseObj = nullptr;
     62 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     63 
     64 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
     65  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
     66 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     67 
     68 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
     69  // If you add new JS member variables, you may need to stop using
     70  // NS_IMPL_CYCLE_COLLECTION_SINGLE_ZONE_SCRIPT_HOLDER_CLASS.
     71  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPromiseObj);
     72 NS_IMPL_CYCLE_COLLECTION_TRACE_END
     73 
     74 Promise::Promise(nsIGlobalObject* aGlobal)
     75    : mGlobal(aGlobal), mPromiseObj(nullptr) {
     76  MOZ_ASSERT(mGlobal);
     77 
     78  mozilla::HoldJSObjectsWithKey(this);
     79 }
     80 
     81 Promise::~Promise() { mozilla::DropJSObjectsWithKey(this); }
     82 
     83 // static
     84 already_AddRefed<Promise> Promise::Create(
     85    nsIGlobalObject* aGlobal, ErrorResult& aRv,
     86    PropagateUserInteraction aPropagateUserInteraction) {
     87  if (!aGlobal) {
     88    aRv.Throw(NS_ERROR_UNEXPECTED);
     89    return nullptr;
     90  }
     91  RefPtr<Promise> p = new Promise(aGlobal);
     92  p->CreateWrapper(aRv, aPropagateUserInteraction);
     93  if (aRv.Failed()) {
     94    return nullptr;
     95  }
     96  return p.forget();
     97 }
     98 
     99 // static
    100 already_AddRefed<Promise> Promise::CreateInfallible(
    101    nsIGlobalObject* aGlobal,
    102    PropagateUserInteraction aPropagateUserInteraction) {
    103  MOZ_ASSERT(aGlobal);
    104  RefPtr<Promise> p = new Promise(aGlobal);
    105  IgnoredErrorResult rv;
    106  p->CreateWrapper(rv, aPropagateUserInteraction);
    107  if (rv.Failed()) {
    108    if (rv.ErrorCodeIs(NS_ERROR_OUT_OF_MEMORY)) {
    109      NS_ABORT_OOM(0);  // (0 meaning unknown size)
    110    }
    111    if (rv.ErrorCodeIs(NS_ERROR_NOT_INITIALIZED)) {
    112      MOZ_CRASH("Failed to create promise wrapper for unknown non-OOM reason");
    113    }
    114  }
    115 
    116  // We may have failed to init the wrapper here, because nsIGlobalObject had
    117  // null GlobalJSObject. In that case we consider the JS realm is dead, which
    118  // means:
    119  // 1. This promise can't be settled.
    120  // 2. Nothing can subscribe this promise anymore from that realm.
    121  // Such condition makes this promise a no-op object.
    122  (void)NS_WARN_IF(!p->PromiseObj());
    123 
    124  return p.forget();
    125 }
    126 
    127 bool Promise::MaybePropagateUserInputEventHandling() {
    128  MOZ_ASSERT(mPromiseObj,
    129             "Should be called only if the wrapper is successfully created");
    130  JS::PromiseUserInputEventHandlingState state =
    131      UserActivation::IsHandlingUserInput()
    132          ? JS::PromiseUserInputEventHandlingState::HadUserInteractionAtCreation
    133          : JS::PromiseUserInputEventHandlingState::
    134                DidntHaveUserInteractionAtCreation;
    135  JS::Rooted<JSObject*> p(RootingCx(), mPromiseObj);
    136  return JS::SetPromiseUserInputEventHandlingState(p, state);
    137 }
    138 
    139 // static
    140 already_AddRefed<Promise> Promise::Resolve(
    141    nsIGlobalObject* aGlobal, JSContext* aCx, JS::Handle<JS::Value> aValue,
    142    ErrorResult& aRv, PropagateUserInteraction aPropagateUserInteraction) {
    143  JSAutoRealm ar(aCx, aGlobal->GetGlobalJSObject());
    144  JS::Rooted<JSObject*> p(aCx, JS::CallOriginalPromiseResolve(aCx, aValue));
    145  if (!p) {
    146    aRv.NoteJSContextException(aCx);
    147    return nullptr;
    148  }
    149 
    150  return CreateFromExisting(aGlobal, p, aPropagateUserInteraction);
    151 }
    152 
    153 // static
    154 already_AddRefed<Promise> Promise::Reject(nsIGlobalObject* aGlobal,
    155                                          JSContext* aCx,
    156                                          JS::Handle<JS::Value> aValue,
    157                                          ErrorResult& aRv) {
    158  JSAutoRealm ar(aCx, aGlobal->GetGlobalJSObject());
    159  JS::Rooted<JSObject*> p(aCx, JS::CallOriginalPromiseReject(aCx, aValue));
    160  if (!p) {
    161    aRv.NoteJSContextException(aCx);
    162    return nullptr;
    163  }
    164 
    165  // This promise will never be resolved, so we pass
    166  // eDontPropagateUserInteraction for aPropagateUserInteraction
    167  // unconditionally.
    168  return CreateFromExisting(aGlobal, p, eDontPropagateUserInteraction);
    169 }
    170 
    171 // static
    172 already_AddRefed<Promise> Promise::All(
    173    JSContext* aCx, const nsTArray<RefPtr<Promise>>& aPromiseList,
    174    ErrorResult& aRv, PropagateUserInteraction aPropagateUserInteraction) {
    175  JS::Rooted<JSObject*> globalObj(aCx, JS::CurrentGlobalOrNull(aCx));
    176  if (!globalObj) {
    177    aRv.Throw(NS_ERROR_UNEXPECTED);
    178    return nullptr;
    179  }
    180 
    181  nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(globalObj);
    182  if (!global) {
    183    aRv.Throw(NS_ERROR_UNEXPECTED);
    184    return nullptr;
    185  }
    186 
    187  JS::RootedVector<JSObject*> promises(aCx);
    188  if (!promises.reserve(aPromiseList.Length())) {
    189    aRv.NoteJSContextException(aCx);
    190    return nullptr;
    191  }
    192 
    193  for (const auto& promise : aPromiseList) {
    194    JS::Rooted<JSObject*> promiseObj(aCx, promise->PromiseObj());
    195    if (!promiseObj) {
    196      // No-op object will never settle, so we return a no-op Promise here,
    197      // which is equivalent of returning the existing no-op one.
    198      return do_AddRef(promise);
    199    }
    200    // Just in case, make sure these are all in the context compartment.
    201    if (!JS_WrapObject(aCx, &promiseObj)) {
    202      aRv.NoteJSContextException(aCx);
    203      return nullptr;
    204    }
    205    promises.infallibleAppend(promiseObj);
    206  }
    207 
    208  JS::Rooted<JSObject*> result(aCx, JS::GetWaitForAllPromise(aCx, promises));
    209  if (!result) {
    210    aRv.NoteJSContextException(aCx);
    211    return nullptr;
    212  }
    213 
    214  return CreateFromExisting(global, result, aPropagateUserInteraction);
    215 }
    216 
    217 struct WaitForAllEmptyTask : public MicroTaskRunnable {
    218  WaitForAllEmptyTask(
    219      nsIGlobalObject* aGlobal,
    220      const std::function<void(const Span<JS::Heap<JS::Value>>&)>& aCallback)
    221      : mGlobal(aGlobal), mCallback(aCallback) {}
    222 
    223 private:
    224  virtual void Run(AutoSlowOperation&) override { mCallback({}); }
    225 
    226  virtual bool Suppressed() override { return mGlobal->IsInSyncOperation(); }
    227 
    228  nsCOMPtr<nsIGlobalObject> mGlobal;
    229  const std::function<void(const Span<JS::Heap<JS::Value>>&)> mCallback;
    230 };
    231 
    232 // Initializing WaitForAllResults also performs step 1 and step 2 of
    233 // #wait-for-all.
    234 struct WaitForAllResults {
    235  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WaitForAllResults)
    236  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WaitForAllResults)
    237 
    238  explicit WaitForAllResults(size_t aSize) : mResult(aSize) {
    239    HoldJSObjects(this);
    240 
    241    mResult.EnsureLengthAtLeast(aSize);
    242  }
    243 
    244  // Step 1
    245  size_t mFullfilledCount = 0;
    246 
    247  // Step 2
    248  bool mRejected = false;
    249 
    250  nsTArray<JS::Heap<JS::Value>> mResult;
    251 
    252 private:
    253  ~WaitForAllResults() { DropJSObjects(this); };
    254 };
    255 
    256 NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(WaitForAllResults, (), (mResult))
    257 
    258 // https://webidl.spec.whatwg.org/#wait-for-all
    259 /* static */
    260 void Promise::WaitForAll(nsIGlobalObject* aGlobal,
    261                         const Span<RefPtr<Promise>>& aPromises,
    262                         SuccessSteps aSuccessSteps, FailureSteps aFailureSteps,
    263                         nsISupports* aCycleCollectedArg) {
    264  // Step 1 and step 2 are in WaitForAllResults.
    265 
    266  // Step 3
    267  const auto& rejectionHandlerSteps =
    268      [aFailureSteps](JSContext* aCx, JS::Handle<JS::Value> aArg,
    269                      ErrorResult& aRv,
    270                      const RefPtr<WaitForAllResults>& aResult,
    271                      const nsCOMPtr<nsISupports>& aCycleCollectedArg) {
    272        // Step 3.1
    273        if (aResult->mRejected) {
    274          return nullptr;
    275        }
    276        // Step 3.2
    277        aResult->mRejected = true;
    278        // Step 3.3
    279        aFailureSteps(aArg);
    280        return nullptr;
    281      };
    282  // Step 5
    283  const size_t total = aPromises.size();
    284  // Step 6
    285  if (!total) {
    286    CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
    287    if (context) {
    288      RefPtr<MicroTaskRunnable> microTask =
    289          new WaitForAllEmptyTask(aGlobal, aSuccessSteps);
    290      // Step 6.1
    291      context->DispatchToMicroTask(microTask.forget());
    292    }
    293    // Step 6.2
    294    return;
    295  }
    296  // Step 7
    297  size_t index = 0;
    298  // Step 8
    299  // Since we'll be passing an nsTArray to several invocations to
    300  // fulfillmentHandlerSteps we wrap it into a cycle collecting and tracing
    301  // object.
    302  RefPtr result = MakeAndAddRef<WaitForAllResults>(total);
    303  nsCOMPtr arg = aCycleCollectedArg;
    304  // Step 9
    305  for (const auto& promise : aPromises) {
    306    // Step 9.1 and step 9.2
    307    const auto& fulfillmentHandlerSteps =
    308        [aSuccessSteps, promiseIndex = index](
    309            JSContext* aCx, JS::Handle<JS::Value> aArg, ErrorResult& aRv,
    310            const RefPtr<WaitForAllResults>& aResult,
    311            const nsCOMPtr<nsISupports>& aCycleCollectedArg)
    312        -> already_AddRefed<Promise> {
    313      // Step 9.2.1
    314      aResult->mResult[promiseIndex].set(aArg.get());
    315      // Step 9.2.2
    316      aResult->mFullfilledCount++;
    317      // Step 9.2.3.
    318      // aResult->mResult.Length() is by definition equals to total.
    319      if (aResult->mFullfilledCount == aResult->mResult.Length()) {
    320        aSuccessSteps(aResult->mResult);
    321      }
    322      return nullptr;
    323    };
    324    // Step 9.4 (and actually also step 4 and step 9.3)
    325    Result resultPromise = promise->ThenCatchWithCycleCollectedArgs(
    326        fulfillmentHandlerSteps, rejectionHandlerSteps, result, arg);
    327 
    328    // https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-performpromisethen
    329    // Step 12
    330    // Promise;:ThenCatchWithCycleCollectedArgs is fairly similar to, but not
    331    // exactly the same as PerformPromiseThen, in particular the step that marks
    332    // the promise as handled is missing. It's performed here to not change the
    333    // existing behavior of ThenCatchWithCycleCollectedArgs.
    334    if (resultPromise.isOk()) {
    335      (void)resultPromise.unwrap()->SetAnyPromiseIsHandled();
    336    }
    337 
    338    // Step 9.5
    339    index++;
    340  }
    341 }
    342 
    343 static void SettlePromise(Promise* aSettlingPromise, Promise* aCallbackPromise,
    344                          ErrorResult& aRv) {
    345  if (!aSettlingPromise) {
    346    return;
    347  }
    348  if (aRv.IsUncatchableException()) {
    349    return;
    350  }
    351  if (aRv.Failed()) {
    352    aSettlingPromise->MaybeReject(std::move(aRv));
    353    return;
    354  }
    355  if (aCallbackPromise) {
    356    aSettlingPromise->MaybeResolve(aCallbackPromise);
    357  } else {
    358    aSettlingPromise->MaybeResolveWithUndefined();
    359  }
    360 }
    361 
    362 void PromiseNativeThenHandlerBase::ResolvedCallback(
    363    JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
    364  if (!HasResolvedCallback()) {
    365    mPromise->MaybeResolve(aValue);
    366    return;
    367  }
    368  RefPtr<Promise> promise = CallResolveCallback(aCx, aValue, aRv);
    369  SettlePromise(mPromise, promise, aRv);
    370 }
    371 
    372 void PromiseNativeThenHandlerBase::RejectedCallback(
    373    JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
    374  if (!HasRejectedCallback()) {
    375    mPromise->MaybeReject(aValue);
    376    return;
    377  }
    378  RefPtr<Promise> promise = CallRejectCallback(aCx, aValue, aRv);
    379  SettlePromise(mPromise, promise, aRv);
    380 }
    381 
    382 NS_IMPL_CYCLE_COLLECTION_CLASS(PromiseNativeThenHandlerBase)
    383 
    384 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PromiseNativeThenHandlerBase)
    385  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
    386  tmp->Traverse(cb);
    387 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    388 
    389 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PromiseNativeThenHandlerBase)
    390  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
    391  tmp->Unlink();
    392 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    393 
    394 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseNativeThenHandlerBase)
    395  NS_INTERFACE_MAP_ENTRY(nsISupports)
    396 NS_INTERFACE_MAP_END
    397 
    398 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(PromiseNativeThenHandlerBase)
    399  tmp->Trace(aCallbacks, aClosure);
    400 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    401 
    402 NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseNativeThenHandlerBase)
    403 NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseNativeThenHandlerBase)
    404 
    405 Result<RefPtr<Promise>, nsresult> Promise::ThenWithoutCycleCollection(
    406    const std::function<already_AddRefed<Promise>(
    407        JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv)>&
    408        aCallback) {
    409  return ThenWithCycleCollectedArgs(aCallback);
    410 }
    411 
    412 void Promise::CreateWrapper(
    413    ErrorResult& aRv, PropagateUserInteraction aPropagateUserInteraction) {
    414  AutoJSAPI jsapi;
    415  if (!jsapi.Init(mGlobal)) {
    416    aRv.Throw(NS_ERROR_UNEXPECTED);
    417    return;
    418  }
    419  JSContext* cx = jsapi.cx();
    420  mPromiseObj = JS::NewPromiseObject(cx, nullptr);
    421  if (!mPromiseObj) {
    422    nsresult error = JS_IsThrowingOutOfMemory(cx) ? NS_ERROR_OUT_OF_MEMORY
    423                                                  : NS_ERROR_NOT_INITIALIZED;
    424    JS_ClearPendingException(cx);
    425    aRv.Throw(error);
    426    return;
    427  }
    428  if (aPropagateUserInteraction == ePropagateUserInteraction) {
    429    (void)MaybePropagateUserInputEventHandling();
    430  }
    431 }
    432 
    433 void Promise::MaybeResolve(JSContext* aCx, JS::Handle<JS::Value> aValue) {
    434  NS_ASSERT_OWNINGTHREAD(Promise);
    435 
    436  JS::Rooted<JSObject*> p(aCx, PromiseObj());
    437  if (!p || !JS::ResolvePromise(aCx, p, aValue)) {
    438    // Now what?  There's nothing sane to do here.
    439    JS_ClearPendingException(aCx);
    440  }
    441 }
    442 
    443 void Promise::MaybeReject(JSContext* aCx, JS::Handle<JS::Value> aValue) {
    444  NS_ASSERT_OWNINGTHREAD(Promise);
    445 
    446  JS::Rooted<JSObject*> p(aCx, PromiseObj());
    447  if (!p || !JS::RejectPromise(aCx, p, aValue)) {
    448    // Now what?  There's nothing sane to do here.
    449    JS_ClearPendingException(aCx);
    450  }
    451 }
    452 
    453 #define SLOT_NATIVEHANDLER 0
    454 #define SLOT_NATIVEHANDLER_TASK 1
    455 
    456 enum class NativeHandlerTask : int32_t { Resolve, Reject };
    457 
    458 MOZ_CAN_RUN_SCRIPT
    459 static bool NativeHandlerCallback(JSContext* aCx, unsigned aArgc,
    460                                  JS::Value* aVp) {
    461  JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
    462 
    463  JS::Value v =
    464      js::GetFunctionNativeReserved(&args.callee(), SLOT_NATIVEHANDLER);
    465  MOZ_ASSERT(v.isObject());
    466 
    467  JS::Rooted<JSObject*> obj(aCx, &v.toObject());
    468  PromiseNativeHandler* handler = nullptr;
    469  if (NS_FAILED(UNWRAP_OBJECT(PromiseNativeHandler, &obj, handler))) {
    470    return Throw(aCx, NS_ERROR_UNEXPECTED);
    471  }
    472 
    473  v = js::GetFunctionNativeReserved(&args.callee(), SLOT_NATIVEHANDLER_TASK);
    474  NativeHandlerTask task = static_cast<NativeHandlerTask>(v.toInt32());
    475 
    476  ErrorResult rv;
    477  if (task == NativeHandlerTask::Resolve) {
    478    // handler is kept alive by "obj" on the stack.
    479    MOZ_KnownLive(handler)->ResolvedCallback(aCx, args.get(0), rv);
    480  } else {
    481    MOZ_ASSERT(task == NativeHandlerTask::Reject);
    482    // handler is kept alive by "obj" on the stack.
    483    MOZ_KnownLive(handler)->RejectedCallback(aCx, args.get(0), rv);
    484  }
    485 
    486  return !rv.MaybeSetPendingException(aCx);
    487 }
    488 
    489 static JSObject* CreateNativeHandlerFunction(JSContext* aCx,
    490                                             JS::Handle<JSObject*> aHolder,
    491                                             NativeHandlerTask aTask) {
    492  JSFunction* func = js::NewFunctionWithReserved(aCx, NativeHandlerCallback,
    493                                                 /* nargs = */ 1,
    494                                                 /* flags = */ 0, nullptr);
    495  if (!func) {
    496    return nullptr;
    497  }
    498 
    499  JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
    500 
    501  JS::AssertObjectIsNotGray(aHolder);
    502  js::SetFunctionNativeReserved(obj, SLOT_NATIVEHANDLER,
    503                                JS::ObjectValue(*aHolder));
    504  js::SetFunctionNativeReserved(obj, SLOT_NATIVEHANDLER_TASK,
    505                                JS::Int32Value(static_cast<int32_t>(aTask)));
    506 
    507  return obj;
    508 }
    509 
    510 namespace {
    511 
    512 class PromiseNativeHandlerShim final : public PromiseNativeHandler {
    513  RefPtr<PromiseNativeHandler> mInner;
    514 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    515  enum InnerState {
    516    NotCleared,
    517    ClearedFromResolve,
    518    ClearedFromReject,
    519    ClearedFromCC,
    520  };
    521  InnerState mState = NotCleared;
    522 #endif
    523 
    524  ~PromiseNativeHandlerShim() = default;
    525 
    526 public:
    527  explicit PromiseNativeHandlerShim(PromiseNativeHandler* aInner)
    528      : mInner(aInner) {
    529    MOZ_DIAGNOSTIC_ASSERT(mInner);
    530  }
    531 
    532  MOZ_CAN_RUN_SCRIPT
    533  void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
    534                        ErrorResult& aRv) override {
    535 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    536    MOZ_DIAGNOSTIC_ASSERT(mState != ClearedFromResolve);
    537    MOZ_DIAGNOSTIC_ASSERT(mState != ClearedFromReject);
    538    MOZ_DIAGNOSTIC_ASSERT(mState != ClearedFromCC);
    539 #else
    540    if (!mInner) {
    541      return;
    542    }
    543 #endif
    544    RefPtr<PromiseNativeHandler> inner = std::move(mInner);
    545    inner->ResolvedCallback(aCx, aValue, aRv);
    546    MOZ_ASSERT(!mInner);
    547 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    548    mState = ClearedFromResolve;
    549 #endif
    550  }
    551 
    552  MOZ_CAN_RUN_SCRIPT
    553  void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
    554                        ErrorResult& aRv) override {
    555 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    556    MOZ_DIAGNOSTIC_ASSERT(mState != ClearedFromResolve);
    557    MOZ_DIAGNOSTIC_ASSERT(mState != ClearedFromReject);
    558    MOZ_DIAGNOSTIC_ASSERT(mState != ClearedFromCC);
    559 #else
    560    if (!mInner) {
    561      return;
    562    }
    563 #endif
    564    RefPtr<PromiseNativeHandler> inner = std::move(mInner);
    565    inner->RejectedCallback(aCx, aValue, aRv);
    566    MOZ_ASSERT(!mInner);
    567 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    568    mState = ClearedFromReject;
    569 #endif
    570  }
    571 
    572  bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
    573                  JS::MutableHandle<JSObject*> aWrapper) {
    574    return PromiseNativeHandler_Binding::Wrap(aCx, this, aGivenProto, aWrapper);
    575  }
    576 
    577  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    578  NS_DECL_CYCLE_COLLECTION_CLASS(PromiseNativeHandlerShim)
    579 };
    580 
    581 NS_IMPL_CYCLE_COLLECTION_CLASS(PromiseNativeHandlerShim)
    582 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PromiseNativeHandlerShim)
    583  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInner)
    584 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    585  tmp->mState = ClearedFromCC;
    586 #endif
    587 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    588 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PromiseNativeHandlerShim)
    589  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInner)
    590 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    591 
    592 NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseNativeHandlerShim)
    593 NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseNativeHandlerShim)
    594 
    595 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseNativeHandlerShim)
    596  NS_INTERFACE_MAP_ENTRY(nsISupports)
    597 NS_INTERFACE_MAP_END
    598 
    599 }  // anonymous namespace
    600 
    601 void Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable) {
    602  NS_ASSERT_OWNINGTHREAD(Promise);
    603 
    604  AutoJSAPI jsapi;
    605  if (NS_WARN_IF(!mPromiseObj || !jsapi.Init(mGlobal))) {
    606    // Our API doesn't allow us to return a useful error.  Not like this should
    607    // happen anyway.
    608    return;
    609  }
    610 
    611  // The self-hosted promise js may keep the object we pass to it alive
    612  // for quite a while depending on when GC runs.  Therefore, pass a shim
    613  // object instead.  The shim will free its inner PromiseNativeHandler
    614  // after the promise has settled just like our previous c++ promises did.
    615  RefPtr<PromiseNativeHandlerShim> shim =
    616      new PromiseNativeHandlerShim(aRunnable);
    617 
    618  JSContext* cx = jsapi.cx();
    619  JS::Rooted<JSObject*> handlerWrapper(cx);
    620  // Note: PromiseNativeHandler is NOT wrappercached.  So we can't use
    621  // ToJSValue here, because it will try to do XPConnect wrapping on it, sadly.
    622  if (NS_WARN_IF(!shim->WrapObject(cx, nullptr, &handlerWrapper))) {
    623    // Again, no way to report errors.
    624    jsapi.ClearException();
    625    return;
    626  }
    627 
    628  JS::Rooted<JSObject*> resolveFunc(cx);
    629  resolveFunc = CreateNativeHandlerFunction(cx, handlerWrapper,
    630                                            NativeHandlerTask::Resolve);
    631  if (NS_WARN_IF(!resolveFunc)) {
    632    jsapi.ClearException();
    633    return;
    634  }
    635 
    636  JS::Rooted<JSObject*> rejectFunc(cx);
    637  rejectFunc = CreateNativeHandlerFunction(cx, handlerWrapper,
    638                                           NativeHandlerTask::Reject);
    639  if (NS_WARN_IF(!rejectFunc)) {
    640    jsapi.ClearException();
    641    return;
    642  }
    643 
    644  JS::Rooted<JSObject*> promiseObj(cx, PromiseObj());
    645  if (NS_WARN_IF(
    646          !JS::AddPromiseReactions(cx, promiseObj, resolveFunc, rejectFunc))) {
    647    jsapi.ClearException();
    648    return;
    649  }
    650 }
    651 
    652 void Promise::HandleException(JSContext* aCx) {
    653  JS::Rooted<JS::Value> exn(aCx);
    654  if (JS_GetPendingException(aCx, &exn)) {
    655    JS_ClearPendingException(aCx);
    656    // Always reject even if this was called in *Resolve.
    657    MaybeReject(aCx, exn);
    658  }
    659 }
    660 
    661 // static
    662 already_AddRefed<Promise> Promise::RejectWithExceptionFromContext(
    663    nsIGlobalObject* aGlobal, JSContext* aCx, ErrorResult& aError) {
    664  JS::Rooted<JS::Value> exn(aCx);
    665  if (!JS_GetPendingException(aCx, &exn)) {
    666    // This is very important: if there is no pending exception here but we're
    667    // ending up in this code, that means the callee threw an uncatchable
    668    // exception.  Just propagate that out as-is.
    669    aError.ThrowUncatchableException();
    670    return nullptr;
    671  }
    672 
    673  JSAutoRealm ar(aCx, aGlobal->GetGlobalJSObject());
    674  if (!JS_WrapValue(aCx, &exn)) {
    675    // We just give up.
    676    aError.StealExceptionFromJSContext(aCx);
    677    return nullptr;
    678  }
    679 
    680  JS_ClearPendingException(aCx);
    681 
    682  IgnoredErrorResult error;
    683  RefPtr<Promise> promise = Promise::Reject(aGlobal, aCx, exn, error);
    684  if (!promise) {
    685    // We just give up, let's store the exception in the ErrorResult.
    686    aError.ThrowJSException(aCx, exn);
    687    return nullptr;
    688  }
    689 
    690  return promise.forget();
    691 }
    692 
    693 // static
    694 already_AddRefed<Promise> Promise::CreateFromExisting(
    695    nsIGlobalObject* aGlobal, JS::Handle<JSObject*> aPromiseObj,
    696    PropagateUserInteraction aPropagateUserInteraction) {
    697  MOZ_ASSERT(JS::GetCompartment(aGlobal->GetGlobalJSObjectPreserveColor()) ==
    698             JS::GetCompartment(aPromiseObj));
    699  RefPtr<Promise> p = new Promise(aGlobal);
    700  p->mPromiseObj = aPromiseObj;
    701  if (aPropagateUserInteraction == ePropagateUserInteraction &&
    702      !p->MaybePropagateUserInputEventHandling()) {
    703    return nullptr;
    704  }
    705  return p.forget();
    706 }
    707 
    708 void Promise::MaybeResolveWithUndefined() {
    709  NS_ASSERT_OWNINGTHREAD(Promise);
    710 
    711  MaybeResolve(JS::UndefinedHandleValue);
    712 }
    713 
    714 void Promise::MaybeReject(const RefPtr<MediaStreamError>& aArg) {
    715  NS_ASSERT_OWNINGTHREAD(Promise);
    716 
    717  MaybeSomething(aArg, &Promise::MaybeReject);
    718 }
    719 
    720 void Promise::MaybeReject(const RefPtr<webgpu::PipelineError>& aArg) {
    721  NS_ASSERT_OWNINGTHREAD(Promise);
    722 
    723  MaybeSomething(aArg, &Promise::MaybeReject);
    724 }
    725 
    726 void Promise::MaybeRejectWithUndefined() {
    727  NS_ASSERT_OWNINGTHREAD(Promise);
    728 
    729  MaybeSomething(JS::UndefinedHandleValue, &Promise::MaybeReject);
    730 }
    731 
    732 void Promise::ReportRejectedPromise(JSContext* aCx,
    733                                    JS::Handle<JSObject*> aPromise) {
    734  MOZ_ASSERT(!js::IsWrapper(aPromise));
    735 
    736  MOZ_ASSERT(JS::GetPromiseState(aPromise) == JS::PromiseState::Rejected);
    737 
    738  bool isChrome = false;
    739  uint64_t innerWindowID = 0;
    740  nsGlobalWindowInner* winForDispatch = nullptr;
    741  if (MOZ_LIKELY(NS_IsMainThread())) {
    742    isChrome = nsContentUtils::ObjectPrincipal(aPromise)->IsSystemPrincipal();
    743 
    744    if (nsGlobalWindowInner* win = xpc::WindowGlobalOrNull(aPromise)) {
    745      winForDispatch = win;
    746      innerWindowID = win->WindowID();
    747    } else if (nsGlobalWindowInner* win = xpc::SandboxWindowOrNull(
    748                   JS::GetNonCCWObjectGlobal(aPromise), aCx)) {
    749      // Don't dispatch rejections from the sandbox to the associated DOM
    750      // window.
    751      innerWindowID = win->WindowID();
    752    }
    753  } else if (const WorkerPrivate* wp = GetCurrentThreadWorkerPrivate()) {
    754    isChrome = wp->UsesSystemPrincipal();
    755    innerWindowID = wp->WindowID();
    756  } else if (nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(aPromise)) {
    757    if (nsCOMPtr<WorkletGlobalScope> workletGlobal =
    758            do_QueryInterface(global)) {
    759      WorkletImpl* impl = workletGlobal->Impl();
    760      isChrome = impl->PrincipalInfo().type() ==
    761                 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo;
    762      innerWindowID = impl->LoadInfo().InnerWindowID();
    763    }
    764  }
    765 
    766  JS::Rooted<JS::Value> result(aCx, JS::GetPromiseResult(aPromise));
    767  // resolutionSite can be null if async stacks are disabled.
    768  JS::Rooted<JSObject*> resolutionSite(aCx,
    769                                       JS::GetPromiseResolutionSite(aPromise));
    770 
    771  // We're inspecting the rejection value only to report it to the console, and
    772  // we do so without side-effects, so we can safely unwrap it without regard to
    773  // the privileges of the Promise object that holds it. If we don't unwrap
    774  // before trying to create the error report, we wind up reporting any
    775  // cross-origin objects as "uncaught exception: Object".
    776  RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
    777  {
    778    Maybe<JSAutoRealm> ar;
    779    JS::Rooted<JS::Value> unwrapped(aCx, result);
    780    if (unwrapped.isObject()) {
    781      unwrapped.setObject(*js::UncheckedUnwrap(&unwrapped.toObject()));
    782      ar.emplace(aCx, &unwrapped.toObject());
    783    }
    784 
    785    JS::ErrorReportBuilder report(aCx);
    786    RefPtr<Exception> exn;
    787    if (unwrapped.isObject() &&
    788        (NS_SUCCEEDED(UNWRAP_OBJECT(DOMException, &unwrapped, exn)) ||
    789         NS_SUCCEEDED(UNWRAP_OBJECT(Exception, &unwrapped, exn)))) {
    790      xpcReport->Init(aCx, exn, isChrome, innerWindowID);
    791    } else {
    792      // Use the resolution site as the exception stack
    793      JS::ExceptionStack exnStack(aCx, unwrapped, resolutionSite);
    794      if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) {
    795        JS_ClearPendingException(aCx);
    796        return;
    797      }
    798 
    799      xpcReport->Init(report.report(), report.toStringResult().c_str(),
    800                      isChrome, innerWindowID);
    801    }
    802  }
    803 
    804  // Used to initialize the similarly named nsISciptError attribute.
    805  xpcReport->mIsPromiseRejection = true;
    806 
    807  // Now post an event to do the real reporting async
    808  RefPtr<AsyncErrorReporter> event = new AsyncErrorReporter(xpcReport);
    809  if (winForDispatch) {
    810    if (!winForDispatch->IsDying()) {
    811      // Exceptions from a dying window will cause the window to leak.
    812      event->SetException(aCx, result);
    813      if (resolutionSite) {
    814        event->SerializeStack(aCx, resolutionSite);
    815      }
    816    }
    817    winForDispatch->Dispatch(event.forget());
    818  } else {
    819    NS_DispatchToMainThread(event);
    820  }
    821 }
    822 
    823 void Promise::MaybeResolveWithClone(JSContext* aCx,
    824                                    JS::Handle<JS::Value> aValue) {
    825  JS::Rooted<JSObject*> sourceScope(aCx, JS::CurrentGlobalOrNull(aCx));
    826  AutoEntryScript aes(GetParentObject(), "Promise resolution");
    827  JSContext* cx = aes.cx();
    828  JS::Rooted<JS::Value> value(cx, aValue);
    829 
    830  xpc::StackScopedCloneOptions options;
    831  options.wrapReflectors = true;
    832  if (!StackScopedClone(cx, options, sourceScope, &value)) {
    833    HandleException(cx);
    834    return;
    835  }
    836  MaybeResolve(aCx, value);
    837 }
    838 
    839 void Promise::MaybeRejectWithClone(JSContext* aCx,
    840                                   JS::Handle<JS::Value> aValue) {
    841  JS::Rooted<JSObject*> sourceScope(aCx, JS::CurrentGlobalOrNull(aCx));
    842  AutoEntryScript aes(GetParentObject(), "Promise rejection");
    843  JSContext* cx = aes.cx();
    844  JS::Rooted<JS::Value> value(cx, aValue);
    845 
    846  xpc::StackScopedCloneOptions options;
    847  options.wrapReflectors = true;
    848  if (!StackScopedClone(cx, options, sourceScope, &value)) {
    849    HandleException(cx);
    850    return;
    851  }
    852  MaybeReject(aCx, value);
    853 }
    854 
    855 // A WorkerRunnable to resolve/reject the Promise on the worker thread.
    856 // Calling thread MUST hold PromiseWorkerProxy's mutex before creating this.
    857 class PromiseWorkerProxyRunnable final : public WorkerThreadRunnable {
    858 public:
    859  PromiseWorkerProxyRunnable(PromiseWorkerProxy* aPromiseWorkerProxy,
    860                             PromiseWorkerProxy::RunCallbackFunc aFunc)
    861      : WorkerThreadRunnable("PromiseWorkerProxyRunnable"),
    862        mPromiseWorkerProxy(aPromiseWorkerProxy),
    863        mFunc(aFunc) {
    864    MOZ_ASSERT(NS_IsMainThread());
    865    MOZ_ASSERT(mPromiseWorkerProxy);
    866  }
    867 
    868  virtual bool WorkerRun(JSContext* aCx,
    869                         WorkerPrivate* aWorkerPrivate) override {
    870    MOZ_ASSERT(aWorkerPrivate);
    871    aWorkerPrivate->AssertIsOnWorkerThread();
    872 
    873    MOZ_ASSERT(mPromiseWorkerProxy);
    874    RefPtr<Promise> workerPromise = mPromiseWorkerProxy->GetWorkerPromise();
    875    // Once Worker had already started shutdown, workerPromise would be nullptr
    876    if (!workerPromise) {
    877      return true;
    878    }
    879 
    880    // Here we convert the buffer to a JS::Value.
    881    JS::Rooted<JS::Value> value(aCx);
    882    if (!mPromiseWorkerProxy->Read(aCx, &value)) {
    883      JS_ClearPendingException(aCx);
    884      return false;
    885    }
    886 
    887    (workerPromise->*mFunc)(aCx, value);
    888 
    889    // Release the Promise because it has been resolved/rejected for sure.
    890    mPromiseWorkerProxy->CleanUp();
    891    return true;
    892  }
    893 
    894 protected:
    895  ~PromiseWorkerProxyRunnable() = default;
    896 
    897 private:
    898  RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
    899 
    900  // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
    901  PromiseWorkerProxy::RunCallbackFunc mFunc;
    902 };
    903 
    904 /* static */
    905 already_AddRefed<PromiseWorkerProxy> PromiseWorkerProxy::Create(
    906    WorkerPrivate* aWorkerPrivate, Promise* aWorkerPromise,
    907    const PromiseWorkerProxyStructuredCloneCallbacks* aCb) {
    908  MOZ_ASSERT(aWorkerPrivate);
    909  aWorkerPrivate->AssertIsOnWorkerThread();
    910  MOZ_ASSERT(aWorkerPromise);
    911  MOZ_ASSERT_IF(aCb, !!aCb->Write && !!aCb->Read);
    912 
    913  RefPtr<PromiseWorkerProxy> proxy =
    914      new PromiseWorkerProxy(aWorkerPromise, aCb);
    915 
    916  // Maintain a reference so that we have a valid object to clean up when
    917  // removing the feature.
    918  proxy.get()->AddRef();
    919 
    920  // We do this to make sure the worker thread won't shut down before the
    921  // promise is resolved/rejected on the worker thread.
    922  RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
    923      aWorkerPrivate, "PromiseWorkerProxy", [proxy]() { proxy->CleanUp(); });
    924 
    925  if (NS_WARN_IF(!workerRef)) {
    926    // Probably the worker is terminating. We cannot complete the operation
    927    // and we have to release all the resources.  CleanUp releases the extra
    928    // ref, too
    929    proxy->CleanUp();
    930    return nullptr;
    931  }
    932 
    933  proxy->mWorkerRef = new ThreadSafeWorkerRef(workerRef);
    934 
    935  return proxy.forget();
    936 }
    937 
    938 NS_IMPL_ISUPPORTS0(PromiseWorkerProxy)
    939 
    940 PromiseWorkerProxy::PromiseWorkerProxy(
    941    Promise* aWorkerPromise,
    942    const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks)
    943    : mWorkerPromise(aWorkerPromise),
    944      mCleanedUp(false),
    945      mCallbacks(aCallbacks),
    946      mCleanUpLock("cleanUpLock") {}
    947 
    948 PromiseWorkerProxy::~PromiseWorkerProxy() {
    949  MOZ_ASSERT(mCleanedUp);
    950  MOZ_ASSERT(!mWorkerPromise);
    951  MOZ_ASSERT(!mWorkerRef);
    952 }
    953 
    954 WorkerPrivate* PromiseWorkerProxy::GetWorkerPrivate() const {
    955 #ifdef DEBUG
    956  if (NS_IsMainThread()) {
    957    mCleanUpLock.AssertCurrentThreadOwns();
    958  }
    959 #endif
    960  // Safe to check this without a lock since we assert lock ownership on the
    961  // main thread above.
    962  MOZ_ASSERT(!mCleanedUp);
    963  MOZ_ASSERT(mWorkerRef);
    964 
    965  return mWorkerRef->Private();
    966 }
    967 
    968 Promise* PromiseWorkerProxy::GetWorkerPromise() const {
    969  MOZ_ASSERT(IsCurrentThreadRunningWorker());
    970  return mWorkerPromise;
    971 }
    972 
    973 void PromiseWorkerProxy::RunCallback(JSContext* aCx,
    974                                     JS::Handle<JS::Value> aValue,
    975                                     RunCallbackFunc aFunc) {
    976  MOZ_ASSERT(NS_IsMainThread());
    977 
    978  MutexAutoLock lock(Lock());
    979  // If the worker thread's been cancelled we don't need to resolve the Promise.
    980  if (CleanedUp()) {
    981    return;
    982  }
    983 
    984  // The |aValue| is written into the StructuredCloneHolderBase.
    985  if (!Write(aCx, aValue)) {
    986    JS_ClearPendingException(aCx);
    987    MOZ_ASSERT(false,
    988               "cannot serialize the value with the StructuredCloneAlgorithm!");
    989  }
    990 
    991  RefPtr<PromiseWorkerProxyRunnable> runnable =
    992      new PromiseWorkerProxyRunnable(this, aFunc);
    993 
    994  runnable->Dispatch(GetWorkerPrivate());
    995 }
    996 
    997 void PromiseWorkerProxy::ResolvedCallback(JSContext* aCx,
    998                                          JS::Handle<JS::Value> aValue,
    999                                          ErrorResult& aRv) {
   1000  RunCallback(aCx, aValue, &Promise::MaybeResolve);
   1001 }
   1002 
   1003 void PromiseWorkerProxy::RejectedCallback(JSContext* aCx,
   1004                                          JS::Handle<JS::Value> aValue,
   1005                                          ErrorResult& aRv) {
   1006  RunCallback(aCx, aValue, &Promise::MaybeReject);
   1007 }
   1008 
   1009 void PromiseWorkerProxy::CleanUp() {
   1010  // Can't release Mutex while it is still locked, so scope the lock.
   1011  {
   1012    MutexAutoLock lock(Lock());
   1013 
   1014    if (CleanedUp()) {
   1015      return;
   1016    }
   1017 
   1018    // We can be called if we failed to get a WorkerRef
   1019    if (mWorkerRef) {
   1020      mWorkerRef->Private()->AssertIsOnWorkerThread();
   1021    }
   1022 
   1023    // Release the Promise and remove the PromiseWorkerProxy from the holders of
   1024    // the worker thread since the Promise has been resolved/rejected or the
   1025    // worker thread has been cancelled.
   1026    mCleanedUp = true;
   1027    mWorkerPromise = nullptr;
   1028    mWorkerRef = nullptr;
   1029 
   1030    // Clear the StructuredCloneHolderBase class.
   1031    Clear();
   1032  }
   1033  Release();
   1034 }
   1035 
   1036 JSObject* PromiseWorkerProxy::CustomReadHandler(
   1037    JSContext* aCx, JSStructuredCloneReader* aReader,
   1038    const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag,
   1039    uint32_t aIndex) {
   1040  if (NS_WARN_IF(!mCallbacks)) {
   1041    return nullptr;
   1042  }
   1043 
   1044  return mCallbacks->Read(aCx, aReader, this, aTag, aIndex);
   1045 }
   1046 
   1047 bool PromiseWorkerProxy::CustomWriteHandler(JSContext* aCx,
   1048                                            JSStructuredCloneWriter* aWriter,
   1049                                            JS::Handle<JSObject*> aObj,
   1050                                            bool* aSameProcessScopeRequired) {
   1051  if (NS_WARN_IF(!mCallbacks)) {
   1052    return false;
   1053  }
   1054 
   1055  return mCallbacks->Write(aCx, aWriter, this, aObj);
   1056 }
   1057 
   1058 // Specializations of MaybeRejectBrokenly we actually support.
   1059 template <>
   1060 void Promise::MaybeRejectBrokenly(const RefPtr<DOMException>& aArg) {
   1061  MaybeSomething(aArg, &Promise::MaybeReject);
   1062 }
   1063 template <>
   1064 void Promise::MaybeRejectBrokenly(const nsAString& aArg) {
   1065  MaybeSomething(aArg, &Promise::MaybeReject);
   1066 }
   1067 
   1068 Promise::PromiseState Promise::State() const {
   1069  JS::Rooted<JSObject*> p(RootingCx(), PromiseObj());
   1070  const JS::PromiseState state = JS::GetPromiseState(p);
   1071 
   1072  if (state == JS::PromiseState::Fulfilled) {
   1073    return PromiseState::Resolved;
   1074  }
   1075 
   1076  if (state == JS::PromiseState::Rejected) {
   1077    return PromiseState::Rejected;
   1078  }
   1079 
   1080  return PromiseState::Pending;
   1081 }
   1082 
   1083 bool Promise::SetSettledPromiseIsHandled() {
   1084  if (!mPromiseObj) {
   1085    // Do nothing as it's a no-op promise
   1086    return false;
   1087  }
   1088  AutoAllowLegacyScriptExecution exemption;
   1089  AutoEntryScript aes(mGlobal, "Set settled promise handled");
   1090  JSContext* cx = aes.cx();
   1091  JS::Rooted<JSObject*> promiseObj(cx, mPromiseObj);
   1092  return JS::SetSettledPromiseIsHandled(cx, promiseObj);
   1093 }
   1094 
   1095 bool Promise::SetAnyPromiseIsHandled() {
   1096  if (!mPromiseObj) {
   1097    // Do nothing as it's a no-op promise
   1098    return false;
   1099  }
   1100  AutoAllowLegacyScriptExecution exemption;
   1101  AutoEntryScript aes(mGlobal, "Set any promise handled");
   1102  JSContext* cx = aes.cx();
   1103  JS::Rooted<JSObject*> promiseObj(cx, mPromiseObj);
   1104  return JS::SetAnyPromiseIsHandled(cx, promiseObj);
   1105 }
   1106 
   1107 /* static */
   1108 already_AddRefed<Promise> Promise::CreateResolvedWithUndefined(
   1109    nsIGlobalObject* global, ErrorResult& aRv) {
   1110  RefPtr<Promise> returnPromise = Promise::Create(global, aRv);
   1111  if (aRv.Failed()) {
   1112    return nullptr;
   1113  }
   1114  returnPromise->MaybeResolveWithUndefined();
   1115  return returnPromise.forget();
   1116 }
   1117 
   1118 already_AddRefed<Promise> Promise::CreateRejected(
   1119    nsIGlobalObject* aGlobal, JS::Handle<JS::Value> aRejectionError,
   1120    ErrorResult& aRv) {
   1121  RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
   1122  if (aRv.Failed()) {
   1123    return nullptr;
   1124  }
   1125  promise->MaybeReject(aRejectionError);
   1126  return promise.forget();
   1127 }
   1128 
   1129 already_AddRefed<Promise> Promise::CreateRejectedWithTypeError(
   1130    nsIGlobalObject* aGlobal, const nsACString& aMessage, ErrorResult& aRv) {
   1131  RefPtr<Promise> returnPromise = Promise::Create(aGlobal, aRv);
   1132  if (aRv.Failed()) {
   1133    return nullptr;
   1134  }
   1135  returnPromise->MaybeRejectWithTypeError(aMessage);
   1136  return returnPromise.forget();
   1137 }
   1138 
   1139 already_AddRefed<Promise> Promise::CreateRejectedWithErrorResult(
   1140    nsIGlobalObject* aGlobal, ErrorResult& aRejectionError) {
   1141  RefPtr<Promise> returnPromise = Promise::Create(aGlobal, IgnoreErrors());
   1142  if (!returnPromise) {
   1143    return nullptr;
   1144  }
   1145  returnPromise->MaybeReject(std::move(aRejectionError));
   1146  return returnPromise.forget();
   1147 }
   1148 
   1149 nsresult Promise::TryExtractNSResultFromRejectionValue(
   1150    JS::Handle<JS::Value> aValue) {
   1151  if (aValue.isInt32()) {
   1152    return nsresult(aValue.toInt32());
   1153  }
   1154 
   1155  if (aValue.isObject()) {
   1156    RefPtr<DOMException> domException;
   1157    UNWRAP_OBJECT(DOMException, aValue, domException);
   1158    if (domException) {
   1159      return domException->GetResult();
   1160    }
   1161  }
   1162 
   1163  return NS_ERROR_DOM_NOT_NUMBER_ERR;
   1164 }
   1165 
   1166 }  // namespace mozilla::dom
   1167 
   1168 extern "C" {
   1169 
   1170 // These functions are used in the implementation of ffi bindings for
   1171 // dom::Promise from Rust.
   1172 
   1173 void DomPromise_AddRef(mozilla::dom::Promise* aPromise) {
   1174  MOZ_ASSERT(aPromise);
   1175  aPromise->AddRef();
   1176 }
   1177 
   1178 void DomPromise_Release(mozilla::dom::Promise* aPromise) {
   1179  MOZ_ASSERT(aPromise);
   1180  aPromise->Release();
   1181 }
   1182 
   1183 #define DOM_PROMISE_FUNC_WITH_VARIANT(name, func)                         \
   1184  void name(mozilla::dom::Promise* aPromise, nsIVariant* aVariant) {      \
   1185    MOZ_ASSERT(aPromise);                                                 \
   1186    MOZ_ASSERT(aVariant);                                                 \
   1187    mozilla::dom::AutoEntryScript aes(aPromise->GetGlobalObject(),        \
   1188                                      "Promise resolution or rejection"); \
   1189    JSContext* cx = aes.cx();                                             \
   1190                                                                          \
   1191    JS::Rooted<JS::Value> val(cx);                                        \
   1192    nsresult rv = NS_OK;                                                  \
   1193    if (!XPCVariant::VariantDataToJS(cx, aVariant, &rv, &val)) {          \
   1194      aPromise->MaybeRejectWithTypeError(                                 \
   1195          "Failed to convert nsIVariant to JS");                          \
   1196      return;                                                             \
   1197    }                                                                     \
   1198    aPromise->func(val);                                                  \
   1199  }
   1200 
   1201 DOM_PROMISE_FUNC_WITH_VARIANT(DomPromise_RejectWithVariant, MaybeReject)
   1202 DOM_PROMISE_FUNC_WITH_VARIANT(DomPromise_ResolveWithVariant, MaybeResolve)
   1203 
   1204 #undef DOM_PROMISE_FUNC_WITH_VARIANT
   1205 }