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 }