WorkletFetchHandler.cpp (21641B)
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 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 #include "WorkletFetchHandler.h" 7 8 #include "js/ContextOptions.h" 9 #include "js/loader/ModuleLoadRequest.h" 10 #include "mozilla/CycleCollectedJSContext.h" 11 #include "mozilla/ScopeExit.h" 12 #include "mozilla/StaticPrefs_javascript.h" 13 #include "mozilla/TaskQueue.h" 14 #include "mozilla/dom/Document.h" 15 #include "mozilla/dom/Fetch.h" 16 #include "mozilla/dom/Request.h" 17 #include "mozilla/dom/RequestBinding.h" 18 #include "mozilla/dom/Response.h" 19 #include "mozilla/dom/RootedDictionary.h" 20 #include "mozilla/dom/ScriptLoadHandler.h" // ScriptDecoder 21 #include "mozilla/dom/ScriptLoader.h" 22 #include "mozilla/dom/Worklet.h" 23 #include "mozilla/dom/WorkletBinding.h" 24 #include "mozilla/dom/WorkletGlobalScope.h" 25 #include "mozilla/dom/WorkletImpl.h" 26 #include "mozilla/dom/WorkletThread.h" 27 #include "mozilla/dom/worklet/WorkletModuleLoader.h" 28 #include "nsIInputStreamPump.h" 29 #include "nsIThreadRetargetableRequest.h" 30 #include "xpcpublic.h" 31 32 using JS::loader::ModuleLoadRequest; 33 using JS::loader::ParserMetadata; 34 using JS::loader::ScriptFetchOptions; 35 using mozilla::dom::loader::WorkletModuleLoader; 36 37 namespace mozilla::dom { 38 39 // A Runnable to call ModuleLoadRequest::StartModuleLoad on a worklet thread. 40 class StartModuleLoadRunnable final : public Runnable { 41 public: 42 StartModuleLoadRunnable( 43 WorkletImpl* aWorkletImpl, 44 const nsMainThreadPtrHandle<WorkletFetchHandler>& aHandlerRef, 45 nsCOMPtr<nsIURI> aURI, nsIURI* aReferrer, 46 const nsTArray<nsString>& aLocalizedStrs) 47 : Runnable("Worklet::StartModuleLoadRunnable"), 48 mWorkletImpl(aWorkletImpl), 49 mHandlerRef(aHandlerRef), 50 mURI(std::move(aURI)), 51 mReferrer(aReferrer), 52 mLocalizedStrs(aLocalizedStrs), 53 mParentRuntime( 54 JS_GetParentRuntime(CycleCollectedJSContext::Get()->Context())) { 55 MOZ_ASSERT(NS_IsMainThread()); 56 MOZ_ASSERT(mParentRuntime); 57 xpc::SetPrefableContextOptions(mContextOptions); 58 } 59 60 ~StartModuleLoadRunnable() = default; 61 62 NS_IMETHOD Run() override; 63 64 private: 65 NS_IMETHOD RunOnWorkletThread(); 66 67 RefPtr<WorkletImpl> mWorkletImpl; 68 nsMainThreadPtrHandle<WorkletFetchHandler> mHandlerRef; 69 nsCOMPtr<nsIURI> mURI; 70 nsCOMPtr<nsIURI> mReferrer; 71 const nsTArray<nsString>& mLocalizedStrs; 72 JSRuntime* mParentRuntime; 73 JS::ContextOptions mContextOptions; 74 }; 75 76 NS_IMETHODIMP 77 StartModuleLoadRunnable::Run() { 78 // WorkletThread::IsOnWorkletThread() cannot be used here because it depends 79 // on a WorkletJSContext having been created for this thread. That does not 80 // happen until the global scope is created the first time 81 // RunOnWorkletThread() is called. 82 MOZ_ASSERT(!NS_IsMainThread()); 83 return RunOnWorkletThread(); 84 } 85 86 NS_IMETHODIMP StartModuleLoadRunnable::RunOnWorkletThread() { 87 // This can be called on a GraphRunner thread or a DOM Worklet thread. 88 WorkletThread::EnsureCycleCollectedJSContext(mParentRuntime, mContextOptions); 89 90 WorkletGlobalScope* globalScope = mWorkletImpl->GetGlobalScope(); 91 if (!globalScope) { 92 return NS_ERROR_DOM_UNKNOWN_ERR; 93 } 94 95 // To fetch a worklet/module worker script graph: 96 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-worklet/module-worker-script-graph 97 // Step 1. Let options be a script fetch options whose cryptographic nonce is 98 // the empty string, integrity metadata is the empty string, parser metadata 99 // is "not-parser-inserted", credentials mode is credentials mode, referrer 100 // policy is the empty string, and fetch priority is "auto". 101 RefPtr<ScriptFetchOptions> fetchOptions = new ScriptFetchOptions( 102 CORSMode::CORS_NONE, /* aNonce = */ u""_ns, RequestPriority::Auto, 103 ParserMetadata::NotParserInserted, 104 /*triggeringPrincipal*/ nullptr); 105 106 WorkletModuleLoader* moduleLoader = 107 static_cast<WorkletModuleLoader*>(globalScope->GetModuleLoader()); 108 MOZ_ASSERT(moduleLoader); 109 110 if (!moduleLoader->HasSetLocalizedStrings()) { 111 moduleLoader->SetLocalizedStrings(&mLocalizedStrs); 112 } 113 114 RefPtr<WorkletLoadContext> loadContext = new WorkletLoadContext(mHandlerRef); 115 116 // Part of Step 2. This sets the Top-level flag to true 117 RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest( 118 JS::ModuleType::JavaScript, SRIMetadata(), mReferrer, loadContext, 119 ModuleLoadRequest::Kind::TopLevel, moduleLoader, nullptr); 120 121 request->mURL = mURI->GetSpecOrDefault(); 122 request->NoCacheEntryFound(ReferrerPolicy::_empty, fetchOptions, mURI); 123 124 return request->StartModuleLoad(); 125 } 126 127 StartFetchRunnable::StartFetchRunnable( 128 const nsMainThreadPtrHandle<WorkletFetchHandler>& aHandlerRef, nsIURI* aURI, 129 nsIURI* aReferrer) 130 : Runnable("Worklet::StartFetchRunnable"), 131 mHandlerRef(aHandlerRef), 132 mURI(aURI), 133 mReferrer(aReferrer) { 134 MOZ_ASSERT(!NS_IsMainThread()); 135 } 136 137 NS_IMETHODIMP 138 StartFetchRunnable::Run() { 139 MOZ_ASSERT(NS_IsMainThread()); 140 141 nsCOMPtr<nsIGlobalObject> global = 142 do_QueryInterface(mHandlerRef->mWorklet->GetParentObject()); 143 MOZ_ASSERT(global); 144 145 AutoJSAPI jsapi; 146 if (NS_WARN_IF(!jsapi.Init(global))) { 147 return NS_ERROR_FAILURE; 148 } 149 150 JSContext* cx = jsapi.cx(); 151 nsresult rv = mHandlerRef->StartFetch(cx, mURI, mReferrer); 152 if (NS_FAILED(rv)) { 153 mHandlerRef->HandleFetchFailed(mURI); 154 return NS_ERROR_FAILURE; 155 } 156 157 return NS_OK; 158 } 159 160 // A Runnable to call ModuleLoadRequest::OnFetchComplete on a worklet thread. 161 class FetchCompleteRunnable final : public Runnable { 162 public: 163 FetchCompleteRunnable(WorkletImpl* aWorkletImpl, nsIURI* aURI, 164 nsresult aResult, 165 #ifdef NIGHTLY_BUILD 166 bool aHasWasmMimeTypeEssence, 167 #endif 168 UniquePtr<uint8_t[]> aScriptBuffer = nullptr, 169 size_t aScriptLength = 0) 170 : Runnable("Worklet::FetchCompleteRunnable"), 171 mWorkletImpl(aWorkletImpl), 172 mURI(aURI), 173 mResult(aResult), 174 #ifdef NIGHTLY_BUILD 175 mHasWasmMimeTypeEssence(aHasWasmMimeTypeEssence), 176 #endif 177 mScriptBuffer(std::move(aScriptBuffer)), 178 mScriptLength(aScriptLength) { 179 MOZ_ASSERT(NS_IsMainThread()); 180 } 181 182 ~FetchCompleteRunnable() = default; 183 184 NS_IMETHOD Run() override; 185 186 private: 187 NS_IMETHOD RunOnWorkletThread(); 188 189 RefPtr<WorkletImpl> mWorkletImpl; 190 nsCOMPtr<nsIURI> mURI; 191 nsresult mResult; 192 #ifdef NIGHTLY_BUILD 193 bool mHasWasmMimeTypeEssence; 194 #endif 195 UniquePtr<uint8_t[]> mScriptBuffer; 196 size_t mScriptLength; 197 }; 198 199 NS_IMETHODIMP 200 FetchCompleteRunnable::Run() { 201 MOZ_ASSERT(WorkletThread::IsOnWorkletThread()); 202 return RunOnWorkletThread(); 203 } 204 205 NS_IMETHODIMP FetchCompleteRunnable::RunOnWorkletThread() { 206 WorkletGlobalScope* globalScope = mWorkletImpl->GetGlobalScope(); 207 if (!globalScope) { 208 return NS_ERROR_DOM_UNKNOWN_ERR; 209 } 210 211 WorkletModuleLoader* moduleLoader = 212 static_cast<WorkletModuleLoader*>(globalScope->GetModuleLoader()); 213 MOZ_ASSERT(moduleLoader); 214 MOZ_ASSERT(mURI); 215 ModuleLoadRequest* request = moduleLoader->GetRequest(mURI); 216 MOZ_ASSERT(request); 217 218 // Set the Source type to "text" for decoding. 219 request->SetTextSource(request->mLoadContext.get()); 220 221 nsresult rv; 222 if (mScriptBuffer) { 223 UniquePtr<ScriptDecoder> decoder = MakeUnique<ScriptDecoder>( 224 UTF_8_ENCODING, ScriptDecoder::BOMHandling::Remove); 225 rv = decoder->DecodeRawData(request, mScriptBuffer.get(), mScriptLength, 226 true); 227 NS_ENSURE_SUCCESS(rv, rv); 228 } 229 230 #ifdef NIGHTLY_BUILD 231 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script 232 // Extract the content-type. If its essence is wasm, we'll attempt to 233 // compile this module as a wasm module. (Steps 13.2, 13.6) 234 if (mHasWasmMimeTypeEssence) { 235 request->SetHasWasmMimeTypeEssence(); 236 } 237 #endif 238 239 request->SetBaseURL(mURI); 240 request->OnFetchComplete(mResult); 241 moduleLoader->RemoveRequest(mURI); 242 return NS_OK; 243 } 244 245 ////////////////////////////////////////////////////////////// 246 // WorkletFetchHandler 247 ////////////////////////////////////////////////////////////// 248 NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkletFetchHandler) 249 NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkletFetchHandler) 250 251 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkletFetchHandler) 252 NS_INTERFACE_MAP_ENTRY(nsISupports) 253 NS_INTERFACE_MAP_END 254 255 NS_IMPL_CYCLE_COLLECTION_CLASS(WorkletFetchHandler) 256 257 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkletFetchHandler) 258 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWorklet, mPromises) 259 tmp->mErrorToRethrow.setUndefined(); 260 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 261 262 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkletFetchHandler) 263 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWorklet, mPromises) 264 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 265 266 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WorkletFetchHandler) 267 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow) 268 NS_IMPL_CYCLE_COLLECTION_TRACE_END 269 270 // static 271 already_AddRefed<Promise> WorkletFetchHandler::AddModule( 272 Worklet* aWorklet, JSContext* aCx, const nsAString& aModuleURL, 273 const WorkletOptions& aOptions, ErrorResult& aRv) { 274 MOZ_ASSERT(aWorklet); 275 MOZ_ASSERT(NS_IsMainThread()); 276 277 aWorklet->Impl()->OnAddModuleStarted(); 278 279 auto promiseSettledGuard = 280 MakeScopeExit([&] { aWorklet->Impl()->OnAddModulePromiseSettled(); }); 281 282 nsCOMPtr<nsIGlobalObject> global = 283 do_QueryInterface(aWorklet->GetParentObject()); 284 MOZ_ASSERT(global); 285 286 RefPtr<Promise> promise = Promise::Create(global, aRv); 287 if (NS_WARN_IF(aRv.Failed())) { 288 return nullptr; 289 } 290 291 nsCOMPtr<nsPIDOMWindowInner> window = aWorklet->GetParentObject(); 292 MOZ_ASSERT(window); 293 294 nsCOMPtr<Document> doc; 295 doc = window->GetExtantDoc(); 296 if (!doc) { 297 promise->MaybeReject(NS_ERROR_FAILURE); 298 return promise.forget(); 299 } 300 301 nsCOMPtr<nsIURI> resolvedURI; 302 nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI), aModuleURL, nullptr, 303 doc->GetBaseURI()); 304 if (NS_WARN_IF(NS_FAILED(rv))) { 305 // https://html.spec.whatwg.org/multipage/worklets.html#dom-worklet-addmodule 306 // Step 3. If this fails, then return a promise rejected with a 307 // "SyntaxError" DOMException. 308 rv = NS_ERROR_DOM_SYNTAX_ERR; 309 310 promise->MaybeReject(rv); 311 return promise.forget(); 312 } 313 314 nsAutoCString spec; 315 rv = resolvedURI->GetSpec(spec); 316 if (NS_WARN_IF(NS_FAILED(rv))) { 317 rv = NS_ERROR_DOM_SYNTAX_ERR; 318 319 promise->MaybeReject(rv); 320 return promise.forget(); 321 } 322 323 // Maybe we already have an handler for this URI 324 { 325 WorkletFetchHandler* handler = aWorklet->GetImportFetchHandler(spec); 326 if (handler) { 327 handler->AddPromise(aCx, promise); 328 return promise.forget(); 329 } 330 } 331 332 RefPtr<WorkletFetchHandler> handler = 333 new WorkletFetchHandler(aWorklet, promise, aOptions.mCredentials); 334 335 nsMainThreadPtrHandle<WorkletFetchHandler> handlerRef{ 336 new nsMainThreadPtrHolder<WorkletFetchHandler>("FetchHandler", handler)}; 337 338 // Determine request's Referrer 339 // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer 340 // Step 3. Switch on request’s referrer: 341 // "client" 342 // Step 1.4. Let referrerSource be document’s URL. 343 nsIURI* referrer = doc->GetDocumentURIAsReferrer(); 344 nsCOMPtr<nsIRunnable> runnable = new StartModuleLoadRunnable( 345 aWorklet->mImpl, handlerRef, std::move(resolvedURI), referrer, 346 aWorklet->GetLocalizedStrings()); 347 348 if (NS_FAILED(aWorklet->mImpl->SendControlMessage(runnable.forget()))) { 349 return nullptr; 350 } 351 352 promiseSettledGuard.release(); 353 354 aWorklet->AddImportFetchHandler(spec, handler); 355 return promise.forget(); 356 } 357 358 WorkletFetchHandler::WorkletFetchHandler(Worklet* aWorklet, Promise* aPromise, 359 RequestCredentials aCredentials) 360 : mWorklet(aWorklet), mStatus(ePending), mCredentials(aCredentials) { 361 MOZ_ASSERT(aWorklet); 362 MOZ_ASSERT(aPromise); 363 MOZ_ASSERT(NS_IsMainThread()); 364 365 mPromises.AppendElement(aPromise); 366 } 367 368 WorkletFetchHandler::~WorkletFetchHandler() { mozilla::DropJSObjects(this); } 369 370 void WorkletFetchHandler::ExecutionFailed() { 371 MOZ_ASSERT(NS_IsMainThread()); 372 RejectPromises(NS_ERROR_DOM_ABORT_ERR); 373 } 374 375 void WorkletFetchHandler::ExecutionFailed(JS::Handle<JS::Value> aError) { 376 MOZ_ASSERT(NS_IsMainThread()); 377 RejectPromises(aError); 378 } 379 380 void WorkletFetchHandler::ExecutionSucceeded() { 381 MOZ_ASSERT(NS_IsMainThread()); 382 ResolvePromises(); 383 } 384 385 void WorkletFetchHandler::AddPromise(JSContext* aCx, Promise* aPromise) { 386 MOZ_ASSERT(aPromise); 387 MOZ_ASSERT(NS_IsMainThread()); 388 389 switch (mStatus) { 390 case ePending: 391 mPromises.AppendElement(aPromise); 392 return; 393 394 case eRejected: 395 if (mHasError) { 396 JS::Rooted<JS::Value> error(aCx, mErrorToRethrow); 397 aPromise->MaybeReject(error); 398 } else { 399 aPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); 400 } 401 return; 402 403 case eResolved: 404 aPromise->MaybeResolveWithUndefined(); 405 return; 406 } 407 } 408 409 void WorkletFetchHandler::RejectPromises(nsresult aResult) { 410 MOZ_ASSERT(mStatus == ePending); 411 MOZ_ASSERT(NS_FAILED(aResult)); 412 MOZ_ASSERT(NS_IsMainThread()); 413 414 mWorklet->Impl()->OnAddModulePromiseSettled(); 415 416 for (uint32_t i = 0; i < mPromises.Length(); ++i) { 417 mPromises[i]->MaybeReject(aResult); 418 } 419 mPromises.Clear(); 420 421 mStatus = eRejected; 422 mWorklet = nullptr; 423 } 424 425 void WorkletFetchHandler::RejectPromises(JS::Handle<JS::Value> aValue) { 426 MOZ_ASSERT(mStatus == ePending); 427 MOZ_ASSERT(NS_IsMainThread()); 428 429 mWorklet->Impl()->OnAddModulePromiseSettled(); 430 431 for (uint32_t i = 0; i < mPromises.Length(); ++i) { 432 mPromises[i]->MaybeReject(aValue); 433 } 434 mPromises.Clear(); 435 436 mHasError = true; 437 mErrorToRethrow = aValue; 438 439 mozilla::HoldJSObjects(this); 440 441 mStatus = eRejected; 442 mWorklet = nullptr; 443 } 444 445 void WorkletFetchHandler::ResolvePromises() { 446 MOZ_ASSERT(mStatus == ePending); 447 MOZ_ASSERT(NS_IsMainThread()); 448 449 mWorklet->Impl()->OnAddModulePromiseSettled(); 450 451 for (uint32_t i = 0; i < mPromises.Length(); ++i) { 452 mPromises[i]->MaybeResolveWithUndefined(); 453 } 454 mPromises.Clear(); 455 456 mStatus = eResolved; 457 mWorklet = nullptr; 458 } 459 460 nsresult WorkletFetchHandler::StartFetch(JSContext* aCx, nsIURI* aURI, 461 nsIURI* aReferrer) { 462 nsAutoCString spec; 463 nsresult res = aURI->GetSpec(spec); 464 if (NS_WARN_IF(NS_FAILED(res))) { 465 return NS_ERROR_FAILURE; 466 } 467 468 RequestOrUTF8String requestInput; 469 requestInput.SetAsUTF8String().ShareOrDependUpon(spec); 470 471 RootedDictionary<RequestInit> requestInit(aCx); 472 requestInit.mCredentials.Construct(mCredentials); 473 474 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script 475 // Step 8. mode is "cors" 476 requestInit.mMode.Construct(RequestMode::Cors); 477 478 if (aReferrer) { 479 res = aReferrer->GetSpec(requestInit.mReferrer.Construct()); 480 if (NS_WARN_IF(NS_FAILED(res))) { 481 return NS_ERROR_FAILURE; 482 } 483 } 484 485 nsCOMPtr<nsIGlobalObject> global = 486 do_QueryInterface(mWorklet->GetParentObject()); 487 MOZ_ASSERT(global); 488 489 // Note: added to infer a default credentials mode in the Request setup, 490 // but we always pass an explicit credentials value in requestInit, so 491 // this has no effect right now. Bug 1887862 covers fixing worklets to behave 492 // the same as "normal" fetch calls. 493 nsIPrincipal* p = global->PrincipalOrNull(); 494 CallerType callerType = (p && p->IsSystemPrincipal() ? CallerType::System 495 : CallerType::NonSystem); 496 IgnoredErrorResult rv; 497 SafeRefPtr<Request> request = Request::Constructor( 498 global, aCx, requestInput, requestInit, callerType, rv); 499 if (rv.Failed()) { 500 return NS_ERROR_FAILURE; 501 } 502 503 request->OverrideContentPolicyType(mWorklet->Impl()->ContentPolicyType()); 504 505 RequestOrUTF8String finalRequestInput; 506 finalRequestInput.SetAsRequest() = request.unsafeGetRawPtr(); 507 508 RefPtr<Promise> fetchPromise = FetchRequest( 509 global, finalRequestInput, requestInit, CallerType::System, rv); 510 if (NS_WARN_IF(rv.Failed())) { 511 return NS_ERROR_FAILURE; 512 } 513 514 RefPtr<WorkletScriptHandler> scriptHandler = 515 new WorkletScriptHandler(mWorklet, aURI); 516 fetchPromise->AppendNativeHandler(scriptHandler); 517 return NS_OK; 518 } 519 520 void WorkletFetchHandler::HandleFetchFailed(nsIURI* aURI) { 521 nsCOMPtr<nsIRunnable> runnable = 522 new FetchCompleteRunnable(mWorklet->mImpl, aURI, NS_ERROR_FAILURE, 523 #ifdef NIGHTLY_BUILD 524 false, 525 #endif 526 nullptr, 0); 527 528 if (NS_WARN_IF( 529 NS_FAILED(mWorklet->mImpl->SendControlMessage(runnable.forget())))) { 530 NS_WARNING("Failed to dispatch FetchCompleteRunnable to a worklet thread."); 531 } 532 } 533 534 ////////////////////////////////////////////////////////////// 535 // WorkletScriptHandler 536 ////////////////////////////////////////////////////////////// 537 NS_IMPL_ISUPPORTS(WorkletScriptHandler, nsIStreamLoaderObserver) 538 539 WorkletScriptHandler::WorkletScriptHandler(Worklet* aWorklet, nsIURI* aURI) 540 : mWorklet(aWorklet), mURI(aURI) {} 541 542 void WorkletScriptHandler::ResolvedCallback(JSContext* aCx, 543 JS::Handle<JS::Value> aValue, 544 ErrorResult& aRv) { 545 MOZ_ASSERT(NS_IsMainThread()); 546 547 if (!aValue.isObject()) { 548 HandleFailure(NS_ERROR_FAILURE); 549 return; 550 } 551 552 RefPtr<Response> response; 553 nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response); 554 if (NS_WARN_IF(NS_FAILED(rv))) { 555 HandleFailure(NS_ERROR_FAILURE); 556 return; 557 } 558 559 // https://html.spec.whatwg.org/multipage/worklets.html#dom-worklet-addmodule 560 // Step 6.4.1. If script is null, then: 561 // Step 1.1.2. Reject promise with an "AbortError" DOMException. 562 if (!response->Ok()) { 563 HandleFailure(NS_ERROR_DOM_ABORT_ERR); 564 return; 565 } 566 567 #ifdef NIGHTLY_BUILD 568 nsAutoCString contentType; 569 ErrorResult result; 570 if (response->GetInternalHeaders()) { 571 response->GetInternalHeaders()->Get("Content-Type"_ns, contentType, result); 572 if (!result.Failed()) { 573 mHasWasmMimeTypeEssence = nsContentUtils::HasWasmMimeTypeEssence( 574 NS_ConvertUTF8toUTF16(contentType)); 575 } 576 } 577 #endif 578 579 nsCOMPtr<nsIInputStream> inputStream; 580 response->GetBody(getter_AddRefs(inputStream)); 581 if (!inputStream) { 582 HandleFailure(NS_ERROR_DOM_NETWORK_ERR); 583 return; 584 } 585 586 nsCOMPtr<nsIInputStreamPump> pump; 587 rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream.forget()); 588 if (NS_WARN_IF(NS_FAILED(rv))) { 589 HandleFailure(rv); 590 return; 591 } 592 593 nsCOMPtr<nsIStreamLoader> loader; 594 rv = NS_NewStreamLoader(getter_AddRefs(loader), this); 595 if (NS_WARN_IF(NS_FAILED(rv))) { 596 HandleFailure(rv); 597 return; 598 } 599 600 rv = pump->AsyncRead(loader); 601 if (NS_WARN_IF(NS_FAILED(rv))) { 602 HandleFailure(rv); 603 return; 604 } 605 606 nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(pump); 607 if (rr) { 608 nsCOMPtr<nsIEventTarget> sts = 609 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); 610 RefPtr<TaskQueue> queue = TaskQueue::Create( 611 sts.forget(), "WorkletScriptHandler STS Delivery Queue"); 612 rv = rr->RetargetDeliveryTo(queue); 613 if (NS_FAILED(rv)) { 614 NS_WARNING("Failed to dispatch the nsIInputStreamPump to a IO thread."); 615 } 616 } 617 } 618 619 NS_IMETHODIMP WorkletScriptHandler::OnStreamComplete(nsIStreamLoader* aLoader, 620 nsISupports* aContext, 621 nsresult aStatus, 622 uint32_t aStringLen, 623 const uint8_t* aString) { 624 MOZ_ASSERT(NS_IsMainThread()); 625 626 if (NS_FAILED(aStatus)) { 627 HandleFailure(aStatus); 628 return NS_OK; 629 } 630 631 // Copy the buffer and decode it on worklet thread, as we can only access 632 // ModuleLoadRequest on worklet thread. 633 UniquePtr<uint8_t[]> scriptTextBuf = MakeUnique<uint8_t[]>(aStringLen); 634 memcpy(scriptTextBuf.get(), aString, aStringLen); 635 636 nsCOMPtr<nsIRunnable> runnable = 637 new FetchCompleteRunnable(mWorklet->mImpl, mURI, NS_OK, 638 #ifdef NIGHTLY_BUILD 639 mHasWasmMimeTypeEssence, 640 #endif 641 std::move(scriptTextBuf), aStringLen); 642 643 if (NS_FAILED(mWorklet->mImpl->SendControlMessage(runnable.forget()))) { 644 HandleFailure(NS_ERROR_FAILURE); 645 } 646 647 return NS_OK; 648 } 649 650 void WorkletScriptHandler::RejectedCallback(JSContext* aCx, 651 JS::Handle<JS::Value> aValue, 652 ErrorResult& aRv) { 653 MOZ_ASSERT(NS_IsMainThread()); 654 655 // https://html.spec.whatwg.org/multipage/worklets.html#dom-worklet-addmodule 656 // Step 6.4.1. If script is null, then: 657 // Step 1.1.2. Reject promise with an "AbortError" DOMException. 658 HandleFailure(NS_ERROR_DOM_ABORT_ERR); 659 } 660 661 void WorkletScriptHandler::HandleFailure(nsresult aResult) { 662 DispatchFetchCompleteToWorklet(aResult); 663 } 664 665 void WorkletScriptHandler::DispatchFetchCompleteToWorklet(nsresult aRv) { 666 nsCOMPtr<nsIRunnable> runnable = 667 new FetchCompleteRunnable(mWorklet->mImpl, mURI, aRv, 668 #ifdef NIGHTLY_BUILD 669 mHasWasmMimeTypeEssence, 670 #endif 671 nullptr, 0); 672 673 if (NS_WARN_IF( 674 NS_FAILED(mWorklet->mImpl->SendControlMessage(runnable.forget())))) { 675 NS_WARNING("Failed to dispatch FetchCompleteRunnable to a worklet thread."); 676 } 677 } 678 679 } // namespace mozilla::dom