Fetch.cpp (60786B)
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 7 #include "Fetch.h" 8 9 #include "BodyExtractor.h" 10 #include "FetchChild.h" 11 #include "FetchObserver.h" 12 #include "FetchUtil.h" 13 #include "InternalRequest.h" 14 #include "InternalResponse.h" 15 #include "ThirdPartyUtil.h" 16 #include "js/RootingAPI.h" 17 #include "js/Value.h" 18 #include "mozilla/CycleCollectedJSContext.h" 19 #include "mozilla/ErrorResult.h" 20 #include "mozilla/StaticPrefs_dom.h" 21 #include "mozilla/dom/BindingDeclarations.h" 22 #include "mozilla/dom/BodyConsumer.h" 23 #include "mozilla/dom/DOMException.h" 24 #include "mozilla/dom/Document.h" 25 #include "mozilla/dom/Exceptions.h" 26 #include "mozilla/dom/FetchDriver.h" 27 #include "mozilla/dom/File.h" 28 #include "mozilla/dom/FormData.h" 29 #include "mozilla/dom/Headers.h" 30 #include "mozilla/dom/MimeType.h" 31 #include "mozilla/dom/Promise.h" 32 #include "mozilla/dom/PromiseWorkerProxy.h" 33 #include "mozilla/dom/ReadableStreamDefaultReader.h" 34 #include "mozilla/dom/RemoteWorkerChild.h" 35 #include "mozilla/dom/Request.h" 36 #include "mozilla/dom/Response.h" 37 #include "mozilla/dom/ScriptSettings.h" 38 #include "mozilla/dom/URLSearchParams.h" 39 #include "mozilla/dom/WorkerCommon.h" 40 #include "mozilla/dom/WorkerRef.h" 41 #include "mozilla/dom/WorkerRunnable.h" 42 #include "mozilla/dom/WorkerScope.h" 43 #include "mozilla/ipc/BackgroundChild.h" 44 #include "mozilla/ipc/IPCStreamUtils.h" 45 #include "mozilla/ipc/PBackgroundChild.h" 46 #include "mozilla/ipc/PBackgroundSharedTypes.h" 47 #include "mozilla/net/CookieJarSettings.h" 48 #include "nsDOMString.h" 49 #include "nsIClassifiedChannel.h" 50 #include "nsIGlobalObject.h" 51 #include "nsJSUtils.h" 52 #include "nsNetUtil.h" 53 #include "nsProxyRelease.h" 54 #include "nsReadableUtils.h" 55 #include "nsStreamUtils.h" 56 #include "nsStringStream.h" 57 58 namespace mozilla::dom { 59 60 namespace { 61 62 // Step 17.2.1.2 and 17.2.2 of 63 // https://fetch.spec.whatwg.org/#concept-http-network-fetch 64 // If stream is readable, then error stream with ... 65 void AbortStream(JSContext* aCx, ReadableStream* aReadableStream, 66 ErrorResult& aRv, JS::Handle<JS::Value> aReasonDetails) { 67 if (aReadableStream->State() != ReadableStream::ReaderState::Readable) { 68 return; 69 } 70 71 JS::Rooted<JS::Value> value(aCx, aReasonDetails); 72 73 if (aReasonDetails.isUndefined()) { 74 RefPtr<DOMException> e = DOMException::Create(NS_ERROR_DOM_ABORT_ERR); 75 if (!GetOrCreateDOMReflector(aCx, e, &value)) { 76 return; 77 } 78 } 79 80 aReadableStream->ErrorNative(aCx, value, aRv); 81 } 82 83 } // namespace 84 85 class AbortSignalMainThread final : public AbortSignalImpl { 86 public: 87 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 88 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbortSignalMainThread) 89 90 explicit AbortSignalMainThread(SignalAborted aAborted) 91 : AbortSignalImpl(aAborted, JS::UndefinedHandleValue) { 92 mozilla::HoldJSObjects(this); 93 } 94 95 private: 96 ~AbortSignalMainThread() { mozilla::DropJSObjects(this); }; 97 }; 98 99 NS_IMPL_CYCLE_COLLECTION_CLASS(AbortSignalMainThread) 100 101 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbortSignalMainThread) 102 AbortSignalImpl::Unlink(static_cast<AbortSignalImpl*>(tmp)); 103 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 104 105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbortSignalMainThread) 106 AbortSignalImpl::Traverse(static_cast<AbortSignalImpl*>(tmp), cb); 107 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 108 109 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AbortSignalMainThread) 110 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReason) 111 NS_IMPL_CYCLE_COLLECTION_TRACE_END 112 113 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbortSignalMainThread) 114 NS_INTERFACE_MAP_ENTRY(nsISupports) 115 NS_INTERFACE_MAP_END 116 117 NS_IMPL_CYCLE_COLLECTING_ADDREF(AbortSignalMainThread) 118 NS_IMPL_CYCLE_COLLECTING_RELEASE(AbortSignalMainThread) 119 120 class AbortSignalProxy; 121 122 // This runnable propagates changes from the AbortSignalImpl on workers to the 123 // AbortSignalImpl on main-thread. 124 class AbortSignalProxyRunnable final : public Runnable { 125 RefPtr<AbortSignalProxy> mProxy; 126 127 public: 128 explicit AbortSignalProxyRunnable(AbortSignalProxy* aProxy) 129 : Runnable("dom::AbortSignalProxyRunnable"), mProxy(aProxy) {} 130 131 NS_IMETHOD Run() override; 132 }; 133 134 // This class orchestrates the proxying of AbortSignal operations between the 135 // main thread and a worker thread. 136 class AbortSignalProxy final : public AbortFollower { 137 // This is created and released on the main-thread. 138 RefPtr<AbortSignalImpl> mSignalImplMainThread; 139 140 // The main-thread event target for runnable dispatching. 141 nsCOMPtr<nsIEventTarget> mMainThreadEventTarget; 142 143 // This value is used only when creating mSignalImplMainThread on the main 144 // thread, to create it in already-aborted state if necessary. It does *not* 145 // reflect the instantaneous is-aborted status of the worker thread's 146 // AbortSignal. 147 const SignalAborted mAborted; 148 149 public: 150 NS_DECL_THREADSAFE_ISUPPORTS 151 152 AbortSignalProxy(AbortSignalImpl* aSignalImpl, 153 nsIEventTarget* aMainThreadEventTarget) 154 : mMainThreadEventTarget(aMainThreadEventTarget), 155 mAborted(aSignalImpl->Aborted() ? SignalAborted::Yes 156 : SignalAborted::No) { 157 MOZ_ASSERT(!NS_IsMainThread()); 158 MOZ_ASSERT(mMainThreadEventTarget); 159 Follow(aSignalImpl); 160 } 161 162 // AbortFollower 163 void RunAbortAlgorithm() override; 164 165 AbortSignalImpl* GetOrCreateSignalImplForMainThread() { 166 MOZ_ASSERT(NS_IsMainThread()); 167 if (!mSignalImplMainThread) { 168 mSignalImplMainThread = new AbortSignalMainThread(mAborted); 169 } 170 return mSignalImplMainThread; 171 } 172 173 AbortSignalImpl* GetSignalImplForTargetThread() { 174 MOZ_ASSERT(!NS_IsMainThread()); 175 return Signal(); 176 } 177 178 nsIEventTarget* MainThreadEventTarget() { return mMainThreadEventTarget; } 179 180 void Shutdown() { 181 MOZ_ASSERT(!NS_IsMainThread()); 182 Unfollow(); 183 } 184 185 private: 186 ~AbortSignalProxy() { 187 NS_ProxyRelease("AbortSignalProxy::mSignalImplMainThread", 188 mMainThreadEventTarget, mSignalImplMainThread.forget()); 189 } 190 }; 191 192 NS_IMPL_ISUPPORTS0(AbortSignalProxy) 193 194 NS_IMETHODIMP AbortSignalProxyRunnable::Run() { 195 MOZ_ASSERT(NS_IsMainThread()); 196 AbortSignalImpl* signalImpl = mProxy->GetOrCreateSignalImplForMainThread(); 197 signalImpl->SignalAbort(JS::UndefinedHandleValue); 198 return NS_OK; 199 } 200 201 void AbortSignalProxy::RunAbortAlgorithm() { 202 MOZ_ASSERT(!NS_IsMainThread()); 203 RefPtr<AbortSignalProxyRunnable> runnable = 204 new AbortSignalProxyRunnable(this); 205 MainThreadEventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); 206 } 207 208 class WorkerFetchResolver final : public FetchDriverObserver { 209 // Thread-safe: 210 RefPtr<PromiseWorkerProxy> mPromiseProxy; 211 RefPtr<AbortSignalProxy> mSignalProxy; 212 213 // Touched only on the worker thread. 214 RefPtr<FetchObserver> mFetchObserver; 215 RefPtr<WeakWorkerRef> mWorkerRef; 216 bool mIsShutdown; 217 218 Atomic<bool> mNeedOnDataAvailable; 219 220 public: 221 // Returns null if worker is shutting down. 222 static already_AddRefed<WorkerFetchResolver> Create( 223 WorkerPrivate* aWorkerPrivate, Promise* aPromise, 224 AbortSignalImpl* aSignalImpl, FetchObserver* aObserver) { 225 MOZ_ASSERT(aWorkerPrivate); 226 aWorkerPrivate->AssertIsOnWorkerThread(); 227 RefPtr<PromiseWorkerProxy> proxy = 228 PromiseWorkerProxy::Create(aWorkerPrivate, aPromise); 229 if (!proxy) { 230 return nullptr; 231 } 232 233 RefPtr<AbortSignalProxy> signalProxy; 234 if (aSignalImpl) { 235 signalProxy = new AbortSignalProxy( 236 aSignalImpl, aWorkerPrivate->MainThreadEventTarget()); 237 } 238 239 RefPtr<WorkerFetchResolver> r = 240 new WorkerFetchResolver(proxy, signalProxy, aObserver); 241 242 RefPtr<WeakWorkerRef> workerRef = WeakWorkerRef::Create( 243 aWorkerPrivate, [r]() { r->Shutdown(r->mWorkerRef->GetPrivate()); }); 244 if (NS_WARN_IF(!workerRef)) { 245 return nullptr; 246 } 247 248 r->mWorkerRef = std::move(workerRef); 249 250 return r.forget(); 251 } 252 253 AbortSignalImpl* GetAbortSignalForMainThread() { 254 MOZ_ASSERT(NS_IsMainThread()); 255 256 if (!mSignalProxy) { 257 return nullptr; 258 } 259 260 return mSignalProxy->GetOrCreateSignalImplForMainThread(); 261 } 262 263 AbortSignalImpl* GetAbortSignalForTargetThread() { 264 mPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread(); 265 266 if (!mSignalProxy) { 267 return nullptr; 268 } 269 270 return mSignalProxy->GetSignalImplForTargetThread(); 271 } 272 273 PromiseWorkerProxy* PromiseProxy() const { 274 MOZ_ASSERT(NS_IsMainThread()); 275 return mPromiseProxy; 276 } 277 278 Promise* WorkerPromise(WorkerPrivate* aWorkerPrivate) const { 279 MOZ_ASSERT(aWorkerPrivate); 280 aWorkerPrivate->AssertIsOnWorkerThread(); 281 MOZ_ASSERT(!mIsShutdown); 282 283 return mPromiseProxy->GetWorkerPromise(); 284 } 285 286 FetchObserver* GetFetchObserver(WorkerPrivate* aWorkerPrivate) const { 287 MOZ_ASSERT(aWorkerPrivate); 288 aWorkerPrivate->AssertIsOnWorkerThread(); 289 290 return mFetchObserver; 291 } 292 293 void OnResponseAvailableInternal( 294 SafeRefPtr<InternalResponse> aResponse) override; 295 296 void OnResponseEnd(FetchDriverObserver::EndReason aReason, 297 JS::Handle<JS::Value> aReasonDetails) override; 298 299 bool NeedOnDataAvailable() override; 300 301 void OnDataAvailable() override; 302 303 void Shutdown(WorkerPrivate* aWorkerPrivate) { 304 MOZ_ASSERT(aWorkerPrivate); 305 aWorkerPrivate->AssertIsOnWorkerThread(); 306 307 mIsShutdown = true; 308 mPromiseProxy->CleanUp(); 309 310 mNeedOnDataAvailable = false; 311 mFetchObserver = nullptr; 312 313 if (mSignalProxy) { 314 mSignalProxy->Shutdown(); 315 } 316 317 mWorkerRef = nullptr; 318 } 319 320 bool IsShutdown(WorkerPrivate* aWorkerPrivate) const { 321 MOZ_ASSERT(aWorkerPrivate); 322 aWorkerPrivate->AssertIsOnWorkerThread(); 323 return mIsShutdown; 324 } 325 326 private: 327 WorkerFetchResolver(PromiseWorkerProxy* aProxy, 328 AbortSignalProxy* aSignalProxy, FetchObserver* aObserver) 329 : mPromiseProxy(aProxy), 330 mSignalProxy(aSignalProxy), 331 mFetchObserver(aObserver), 332 mIsShutdown(false), 333 mNeedOnDataAvailable(!!aObserver) { 334 MOZ_ASSERT(!NS_IsMainThread()); 335 MOZ_ASSERT(mPromiseProxy); 336 } 337 338 ~WorkerFetchResolver() = default; 339 340 virtual void FlushConsoleReport() override; 341 }; 342 343 void FetchDriverObserver::OnResponseAvailable( 344 SafeRefPtr<InternalResponse> aResponse) { 345 MOZ_ASSERT(!mGotResponseAvailable); 346 mGotResponseAvailable = true; 347 OnResponseAvailableInternal(std::move(aResponse)); 348 } 349 350 class MainThreadFetchResolver final : public FetchDriverObserver { 351 RefPtr<Promise> mPromise; 352 RefPtr<Response> mResponse; 353 RefPtr<FetchObserver> mFetchObserver; 354 RefPtr<AbortSignalImpl> mSignalImpl; 355 const bool mMozErrors; 356 357 nsCOMPtr<nsILoadGroup> mLoadGroup; 358 359 NS_DECL_OWNINGTHREAD 360 public: 361 MainThreadFetchResolver(Promise* aPromise, FetchObserver* aObserver, 362 AbortSignalImpl* aSignalImpl, bool aMozErrors) 363 : mPromise(aPromise), 364 mFetchObserver(aObserver), 365 mSignalImpl(aSignalImpl), 366 mMozErrors(aMozErrors) {} 367 368 void OnResponseAvailableInternal( 369 SafeRefPtr<InternalResponse> aResponse) override; 370 371 void SetLoadGroup(nsILoadGroup* aLoadGroup) { mLoadGroup = aLoadGroup; } 372 373 void OnResponseEnd(FetchDriverObserver::EndReason aReason, 374 JS::Handle<JS::Value> aReasonDetails) override { 375 if (aReason == eAborted) { 376 if (!aReasonDetails.isUndefined()) { 377 mPromise->MaybeReject(aReasonDetails); 378 } else { 379 mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); 380 } 381 } 382 383 mFetchObserver = nullptr; 384 385 FlushConsoleReport(); 386 } 387 388 bool NeedOnDataAvailable() override; 389 390 void OnDataAvailable() override; 391 392 private: 393 ~MainThreadFetchResolver(); 394 395 void FlushConsoleReport() override { 396 mReporter->FlushConsoleReports(mLoadGroup); 397 } 398 }; 399 400 class MainThreadFetchRunnable : public Runnable { 401 RefPtr<WorkerFetchResolver> mResolver; 402 const ClientInfo mClientInfo; 403 const Maybe<ServiceWorkerDescriptor> mController; 404 nsCOMPtr<nsICSPEventListener> mCSPEventListener; 405 SafeRefPtr<InternalRequest> mRequest; 406 UniquePtr<SerializedStackHolder> mOriginStack; 407 408 public: 409 MainThreadFetchRunnable(WorkerFetchResolver* aResolver, 410 const ClientInfo& aClientInfo, 411 const Maybe<ServiceWorkerDescriptor>& aController, 412 nsICSPEventListener* aCSPEventListener, 413 SafeRefPtr<InternalRequest> aRequest, 414 UniquePtr<SerializedStackHolder>&& aOriginStack) 415 : Runnable("dom::MainThreadFetchRunnable"), 416 mResolver(aResolver), 417 mClientInfo(aClientInfo), 418 mController(aController), 419 mCSPEventListener(aCSPEventListener), 420 mRequest(std::move(aRequest)), 421 mOriginStack(std::move(aOriginStack)) { 422 MOZ_ASSERT(mResolver); 423 } 424 425 NS_IMETHOD 426 Run() override { 427 AssertIsOnMainThread(); 428 RefPtr<FetchDriver> fetch; 429 RefPtr<PromiseWorkerProxy> proxy = mResolver->PromiseProxy(); 430 431 { 432 // Acquire the proxy mutex while getting data from the WorkerPrivate... 433 MutexAutoLock lock(proxy->Lock()); 434 if (proxy->CleanedUp()) { 435 NS_WARNING("Aborting Fetch because worker already shut down"); 436 return NS_OK; 437 } 438 439 WorkerPrivate* workerPrivate = proxy->GetWorkerPrivate(); 440 MOZ_ASSERT(workerPrivate); 441 nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal(); 442 MOZ_ASSERT(principal); 443 nsCOMPtr<nsILoadGroup> loadGroup = workerPrivate->GetLoadGroup(); 444 MOZ_ASSERT(loadGroup); 445 // We don't track if a worker is spawned from a tracking script for now, 446 // so pass false as the last argument to FetchDriver(). 447 fetch = new FetchDriver(mRequest.clonePtr(), principal, loadGroup, 448 workerPrivate->MainThreadEventTarget(), 449 workerPrivate->CookieJarSettings(), 450 workerPrivate->GetPerformanceStorage(), 451 net::ClassificationFlags({0, 0})); 452 nsAutoCString spec; 453 if (proxy->GetWorkerPrivate()->GetBaseURI()) { 454 proxy->GetWorkerPrivate()->GetBaseURI()->GetAsciiSpec(spec); 455 } 456 fetch->SetWorkerScript(spec); 457 458 fetch->SetClientInfo(mClientInfo); 459 fetch->SetController(mController); 460 fetch->SetCSPEventListener(mCSPEventListener); 461 } 462 463 fetch->SetOriginStack(std::move(mOriginStack)); 464 465 RefPtr<AbortSignalImpl> signalImpl = 466 mResolver->GetAbortSignalForMainThread(); 467 468 // ...but release it before calling Fetch, because mResolver's callback can 469 // be called synchronously and they want the mutex, too. 470 return fetch->Fetch(signalImpl, mResolver); 471 } 472 }; 473 474 already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal, 475 const RequestOrUTF8String& aInput, 476 const RequestInit& aInit, 477 CallerType aCallerType, 478 ErrorResult& aRv) { 479 RefPtr<Promise> p = Promise::Create(aGlobal, aRv); 480 if (NS_WARN_IF(aRv.Failed())) { 481 return nullptr; 482 } 483 484 MOZ_ASSERT(aGlobal); 485 486 // Double check that we have chrome privileges if the Request's content 487 // policy type has been overridden. 488 MOZ_ASSERT_IF(aInput.IsRequest() && 489 aInput.GetAsRequest().IsContentPolicyTypeOverridden(), 490 aCallerType == CallerType::System); 491 492 AutoJSAPI jsapi; 493 if (!jsapi.Init(aGlobal)) { 494 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 495 return nullptr; 496 } 497 498 JSContext* cx = jsapi.cx(); 499 JS::Rooted<JSObject*> jsGlobal(cx, aGlobal->GetGlobalJSObject()); 500 GlobalObject global(cx, jsGlobal); 501 502 SafeRefPtr<Request> request = 503 Request::Constructor(global, aInput, aInit, aRv); 504 if (aRv.Failed()) { 505 return nullptr; 506 } 507 508 SafeRefPtr<InternalRequest> internalRequest = request->GetInternalRequest(); 509 510 // Restore information of InterceptedHttpChannel if they are passed with the 511 // Request. Since Request::Constructor would not copy these members. 512 if (aInput.IsRequest()) { 513 RefPtr<Request> inputReq = &aInput.GetAsRequest(); 514 SafeRefPtr<InternalRequest> inputInReq = inputReq->GetInternalRequest(); 515 if (inputInReq->GetInterceptionTriggeringPrincipalInfo()) { 516 internalRequest->SetInterceptionContentPolicyType( 517 inputInReq->InterceptionContentPolicyType()); 518 internalRequest->SetInterceptionTriggeringPrincipalInfo( 519 MakeUnique<mozilla::ipc::PrincipalInfo>( 520 *(inputInReq->GetInterceptionTriggeringPrincipalInfo().get()))); 521 if (!inputInReq->InterceptionRedirectChain().IsEmpty()) { 522 internalRequest->SetInterceptionRedirectChain( 523 inputInReq->InterceptionRedirectChain()); 524 } 525 internalRequest->SetInterceptionFromThirdParty( 526 inputInReq->InterceptionFromThirdParty()); 527 } 528 } 529 530 RefPtr<AbortSignalImpl> signalImpl = request->GetSignalImpl(); 531 532 if (signalImpl && signalImpl->Aborted()) { 533 // Already aborted signal rejects immediately. 534 JS::Rooted<JS::Value> reason(cx, signalImpl->RawReason()); 535 if (reason.get().isUndefined()) { 536 aRv.Throw(NS_ERROR_DOM_ABORT_ERR); 537 return nullptr; 538 } 539 540 p->MaybeReject(reason); 541 return p.forget(); 542 } 543 544 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx); 545 if (realm && JS::GetDebuggerObservesWasm(realm)) { 546 internalRequest->SetSkipWasmCaching(); 547 } 548 549 RefPtr<FetchObserver> observer; 550 if (aInit.mObserve.WasPassed()) { 551 observer = new FetchObserver(aGlobal, signalImpl); 552 aInit.mObserve.Value().HandleEvent(*observer); 553 } 554 555 if (NS_IsMainThread() && !internalRequest->GetKeepalive()) { 556 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal); 557 nsCOMPtr<Document> doc; 558 nsCOMPtr<nsILoadGroup> loadGroup; 559 nsCOMPtr<nsICookieJarSettings> cookieJarSettings; 560 nsIPrincipal* principal; 561 net::ClassificationFlags trackingFlags = {0, 0}; 562 if (window) { 563 doc = window->GetExtantDoc(); 564 if (!doc) { 565 aRv.Throw(NS_ERROR_FAILURE); 566 return nullptr; 567 } 568 principal = doc->NodePrincipal(); 569 loadGroup = doc->GetDocumentLoadGroup(); 570 cookieJarSettings = doc->CookieJarSettings(); 571 572 trackingFlags = doc->GetScriptTrackingFlags(); 573 } else { 574 principal = aGlobal->PrincipalOrNull(); 575 if (NS_WARN_IF(!principal)) { 576 aRv.Throw(NS_ERROR_FAILURE); 577 return nullptr; 578 } 579 580 cookieJarSettings = mozilla::net::CookieJarSettings::Create(principal); 581 } 582 583 if (!loadGroup) { 584 nsresult rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), principal); 585 if (NS_WARN_IF(NS_FAILED(rv))) { 586 aRv.Throw(rv); 587 return nullptr; 588 } 589 } 590 591 RefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver( 592 p, observer, signalImpl, request->MozErrors()); 593 RefPtr<FetchDriver> fetch = 594 new FetchDriver(std::move(internalRequest), principal, loadGroup, 595 aGlobal->SerialEventTarget(), cookieJarSettings, 596 nullptr, // PerformanceStorage 597 trackingFlags); 598 fetch->SetDocument(doc); 599 resolver->SetLoadGroup(loadGroup); 600 aRv = fetch->Fetch(signalImpl, resolver); 601 if (NS_WARN_IF(aRv.Failed())) { 602 return nullptr; 603 } 604 } else if (NS_IsMainThread() && internalRequest->GetKeepalive()) { 605 // keepalive is set to true, route the request through PFetch 606 // We plan to route all main-thread fetch request through PFetch. 607 // See Bug 1897129. 608 609 uint64_t bodyLength = 610 internalRequest->BodyLength() > 0 ? internalRequest->BodyLength() : 0; 611 612 nsCOMPtr<nsILoadGroup> loadGroup = 613 FetchUtil::GetLoadGroupFromGlobal(aGlobal); 614 615 if (loadGroup && !FetchUtil::IncrementPendingKeepaliveRequestSize( 616 loadGroup, bodyLength)) { 617 p->MaybeRejectWithTypeError<MSG_FETCH_FAILED>(); 618 return p.forget(); 619 }; 620 621 if (!loadGroup) { 622 // if there is no load group for this request ensure that the request 623 // size does not exceed FETCH_KEEPALIVE_MAX_SIZE 624 if (bodyLength > FETCH_KEEPALIVE_MAX_SIZE) { 625 p->MaybeRejectWithTypeError<MSG_FETCH_FAILED>(); 626 return p.forget(); 627 } 628 } 629 630 RefPtr<FetchChild> actor = 631 FetchChild::CreateForMainThread(p, signalImpl, observer); 632 if (!actor) { 633 NS_WARNING("Could not start keepalive request."); 634 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 635 return nullptr; 636 } 637 638 Maybe<ClientInfo> clientInfo(aGlobal->GetClientInfo()); 639 if (clientInfo.isNothing()) { 640 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 641 return nullptr; 642 } 643 644 auto* backgroundChild = 645 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 646 (void)NS_WARN_IF(!backgroundChild->SendPFetchConstructor(actor)); 647 648 FetchOpArgs ipcArgs; 649 650 ipcArgs.request() = IPCInternalRequest(); 651 internalRequest->ToIPCInternalRequest(&(ipcArgs.request()), 652 backgroundChild); 653 654 ipcArgs.clientInfo() = clientInfo.ref().ToIPC(); 655 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal); 656 nsCOMPtr<Document> doc; 657 nsCOMPtr<nsICookieJarSettings> cookieJarSettings; 658 nsIPrincipal* principal; 659 // we don't check if we this request is invoked from a tracking script 660 // we might add this capability in future. 661 // See Bug 1892406 662 if (window) { 663 doc = window->GetExtantDoc(); 664 if (!doc) { 665 aRv.Throw(NS_ERROR_FAILURE); 666 return nullptr; 667 } 668 principal = doc->NodePrincipal(); 669 cookieJarSettings = doc->CookieJarSettings(); 670 // fetch the thirdparty context from the document 671 672 ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance(); 673 if (!thirdPartyUtil) { 674 return nullptr; 675 } 676 if (thirdPartyUtil) { 677 bool thirdParty = false; 678 (void)thirdPartyUtil->IsThirdPartyWindow(window->GetOuterWindow(), 679 nullptr, &thirdParty); 680 ipcArgs.isThirdPartyContext() = thirdParty; 681 } 682 } else { 683 principal = aGlobal->PrincipalOrNull(); 684 if (NS_WARN_IF(!principal)) { 685 aRv.Throw(NS_ERROR_FAILURE); 686 return nullptr; 687 } 688 cookieJarSettings = mozilla::net::CookieJarSettings::Create(principal); 689 } 690 691 if (cookieJarSettings) { 692 net::CookieJarSettingsArgs csArgs; 693 net::CookieJarSettings::Cast(cookieJarSettings)->Serialize(csArgs); 694 ipcArgs.cookieJarSettings() = Some(csArgs); 695 } 696 697 nsresult rv = PrincipalToPrincipalInfo(principal, &ipcArgs.principalInfo()); 698 NS_ENSURE_SUCCESS(rv, nullptr); 699 700 ipcArgs.hasCSPEventListener() = false; 701 ipcArgs.isWorkerRequest() = false; 702 703 if (window && window->GetBrowsingContext()) { 704 ipcArgs.associatedBrowsingContextID() = 705 window->GetBrowsingContext()->Id(); 706 } 707 708 UniquePtr<SerializedStackHolder> stack = GetCurrentStackForNetMonitor(cx); 709 if (stack) { 710 actor->SetOriginStack(std::move(stack)); 711 } 712 713 actor->DoFetchOp(ipcArgs); 714 715 return p.forget(); 716 } else { 717 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); 718 MOZ_ASSERT(worker); 719 720 if (worker->IsServiceWorker()) { 721 internalRequest->SetSkipServiceWorker(); 722 } 723 724 // PFetch gives no benefit for the fetch in the parent process. 725 // Dispatch fetch to the parent process main thread directly for that case. 726 // For child process, dispatch fetch op to the parent. 727 if (StaticPrefs::dom_workers_pFetch_enabled() && !XRE_IsParentProcess()) { 728 if (internalRequest->GetKeepalive()) { 729 uint64_t bodyLength = internalRequest->BodyLength() > 0 730 ? internalRequest->BodyLength() 731 : 0; 732 733 // We differ from the fetch spec and main thread fetch here. 734 // We do not limit the keepalive size per loadgroup(but instead per 735 // request). This is due to the fact that loadgroup is not accessible on 736 // the worker thread and we dont want to introduce async to introduce 737 // this check. 738 if (bodyLength > FETCH_KEEPALIVE_MAX_SIZE) { 739 p->MaybeRejectWithTypeError<MSG_FETCH_FAILED>(); 740 return p.forget(); 741 } 742 } 743 744 RefPtr<FetchChild> actor = 745 FetchChild::CreateForWorker(worker, p, signalImpl, observer); 746 if (!actor) { 747 NS_WARNING("Could not keep the worker alive."); 748 aRv.Throw(NS_ERROR_DOM_ABORT_ERR); 749 return nullptr; 750 } 751 752 Maybe<ClientInfo> clientInfo(worker->GlobalScope()->GetClientInfo()); 753 if (clientInfo.isNothing()) { 754 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 755 return nullptr; 756 } 757 758 auto* backgroundChild = 759 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 760 (void)NS_WARN_IF(!backgroundChild->SendPFetchConstructor(actor)); 761 762 FetchOpArgs ipcArgs; 763 ipcArgs.request() = IPCInternalRequest(); 764 internalRequest->ToIPCInternalRequest(&(ipcArgs.request()), 765 backgroundChild); 766 767 ipcArgs.principalInfo() = worker->GetPrincipalInfo(); 768 ipcArgs.clientInfo() = clientInfo.ref().ToIPC(); 769 if (worker->GetBaseURI()) { 770 worker->GetBaseURI()->GetAsciiSpec(ipcArgs.workerScript()); 771 } 772 if (worker->GlobalScope()->GetController().isSome()) { 773 ipcArgs.controller() = 774 Some(worker->GlobalScope()->GetController().ref().ToIPC()); 775 } 776 if (worker->CookieJarSettings()) { 777 ipcArgs.cookieJarSettings() = Some(worker->CookieJarSettingsArgs()); 778 } 779 if (worker->CSPEventListener()) { 780 ipcArgs.hasCSPEventListener() = true; 781 actor->SetCSPEventListener(worker->CSPEventListener()); 782 } else { 783 ipcArgs.hasCSPEventListener() = false; 784 } 785 786 ipcArgs.associatedBrowsingContextID() = 787 worker->AssociatedBrowsingContextID(); 788 789 if (worker->IsWatchedByDevTools()) { 790 UniquePtr<SerializedStackHolder> stack; 791 stack = GetCurrentStackForNetMonitor(cx); 792 actor->SetOriginStack(std::move(stack)); 793 } 794 795 ipcArgs.isThirdPartyContext() = worker->IsThirdPartyContext(); 796 797 ipcArgs.isOn3PCBExceptionList() = worker->IsOn3PCBExceptionList(); 798 799 ipcArgs.isWorkerRequest() = true; 800 801 actor->DoFetchOp(ipcArgs); 802 803 return p.forget(); 804 } 805 // Dispatch worker fetch to the main thread 806 // We do not check if keepalive flag is set for ChromeWorkers 807 // See Bug 1898664 808 RefPtr<WorkerFetchResolver> resolver = 809 WorkerFetchResolver::Create(worker, p, signalImpl, observer); 810 if (!resolver) { 811 NS_WARNING("Could not keep the worker alive."); 812 aRv.Throw(NS_ERROR_DOM_ABORT_ERR); 813 return nullptr; 814 } 815 816 Maybe<ClientInfo> clientInfo(worker->GlobalScope()->GetClientInfo()); 817 if (clientInfo.isNothing()) { 818 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 819 return nullptr; 820 } 821 822 UniquePtr<SerializedStackHolder> stack; 823 if (worker->IsWatchedByDevTools()) { 824 stack = GetCurrentStackForNetMonitor(cx); 825 } 826 827 RefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable( 828 resolver, clientInfo.ref(), worker->GlobalScope()->GetController(), 829 worker->CSPEventListener(), std::move(internalRequest), 830 std::move(stack)); 831 worker->DispatchToMainThread(run.forget()); 832 } 833 834 return p.forget(); 835 } 836 837 class ResolveFetchPromise : public Runnable { 838 public: 839 ResolveFetchPromise(Promise* aPromise, Response* aResponse) 840 : Runnable("ResolveFetchPromise"), 841 mPromise(aPromise), 842 mResponse(aResponse) {} 843 844 NS_IMETHOD Run() override { 845 mPromise->MaybeResolve(mResponse); 846 return NS_OK; 847 } 848 RefPtr<Promise> mPromise; 849 RefPtr<Response> mResponse; 850 }; 851 852 void MainThreadFetchResolver::OnResponseAvailableInternal( 853 SafeRefPtr<InternalResponse> aResponse) { 854 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); 855 AssertIsOnMainThread(); 856 857 if (aResponse->Type() != ResponseType::Error) { 858 nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject(); 859 nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(go); 860 861 // Notify the document when a fetch completes successfully. This is 862 // used by the password manager as a hint to observe DOM mutations. 863 // Call this prior to setting state to Complete so we can set up the 864 // observer before mutations occurs. 865 Document* doc = inner ? inner->GetExtantDoc() : nullptr; 866 if (doc) { 867 doc->NotifyFetchOrXHRSuccess(); 868 } 869 870 if (mFetchObserver) { 871 mFetchObserver->SetState(FetchState::Complete); 872 } 873 874 mResponse = new Response(go, std::move(aResponse), mSignalImpl); 875 // response headers received from the network should be immutable 876 // all response header settings must be done before this point 877 // see Bug 1574174 878 ErrorResult result; 879 mResponse->Headers_()->SetGuard(HeadersGuardEnum::Immutable, result); 880 MOZ_ASSERT(!result.Failed()); 881 882 BrowsingContext* bc = inner ? inner->GetBrowsingContext() : nullptr; 883 bc = bc ? bc->Top() : nullptr; 884 if (bc && bc->IsLoading()) { 885 bc->AddDeprioritizedLoadRunner( 886 new ResolveFetchPromise(mPromise, mResponse)); 887 } else { 888 mPromise->MaybeResolve(mResponse); 889 } 890 } else { 891 if (mFetchObserver) { 892 mFetchObserver->SetState(FetchState::Errored); 893 } 894 895 if (mMozErrors) { 896 mPromise->MaybeReject(aResponse->GetErrorCode()); 897 return; 898 } 899 900 mPromise->MaybeRejectWithTypeError<MSG_FETCH_FAILED>(); 901 } 902 } 903 904 bool MainThreadFetchResolver::NeedOnDataAvailable() { 905 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); 906 return !!mFetchObserver; 907 } 908 909 void MainThreadFetchResolver::OnDataAvailable() { 910 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); 911 AssertIsOnMainThread(); 912 913 if (!mFetchObserver) { 914 return; 915 } 916 917 if (mFetchObserver->State() == FetchState::Requesting) { 918 mFetchObserver->SetState(FetchState::Responding); 919 } 920 } 921 922 MainThreadFetchResolver::~MainThreadFetchResolver() { 923 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); 924 } 925 926 class WorkerFetchResponseRunnable final : public MainThreadWorkerRunnable { 927 RefPtr<WorkerFetchResolver> mResolver; 928 // Passed from main thread to worker thread after being initialized. 929 SafeRefPtr<InternalResponse> mInternalResponse; 930 931 public: 932 WorkerFetchResponseRunnable(WorkerPrivate* aWorkerPrivate, 933 WorkerFetchResolver* aResolver, 934 SafeRefPtr<InternalResponse> aResponse) 935 : MainThreadWorkerRunnable("WorkerFetchResponseRunnable"), 936 mResolver(aResolver), 937 mInternalResponse(std::move(aResponse)) { 938 MOZ_ASSERT(mResolver); 939 } 940 941 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 942 MOZ_ASSERT(aWorkerPrivate); 943 aWorkerPrivate->AssertIsOnWorkerThread(); 944 if (mResolver->IsShutdown(aWorkerPrivate)) { 945 return true; 946 } 947 948 RefPtr<Promise> promise = mResolver->WorkerPromise(aWorkerPrivate); 949 // Once Worker had already started shutdown, workerPromise would be nullptr 950 if (!promise) { 951 return true; 952 } 953 RefPtr<FetchObserver> fetchObserver = 954 mResolver->GetFetchObserver(aWorkerPrivate); 955 956 if (mInternalResponse->Type() != ResponseType::Error) { 957 if (fetchObserver) { 958 fetchObserver->SetState(FetchState::Complete); 959 } 960 961 RefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope(); 962 RefPtr<Response> response = 963 new Response(global, mInternalResponse.clonePtr(), 964 mResolver->GetAbortSignalForTargetThread()); 965 966 // response headers received from the network should be immutable, 967 // all response header settings must be done before this point 968 // see Bug 1574174 969 ErrorResult result; 970 response->Headers_()->SetGuard(HeadersGuardEnum::Immutable, result); 971 MOZ_ASSERT(!result.Failed()); 972 973 promise->MaybeResolve(response); 974 } else { 975 if (fetchObserver) { 976 fetchObserver->SetState(FetchState::Errored); 977 } 978 979 promise->MaybeRejectWithTypeError<MSG_FETCH_FAILED>(); 980 } 981 return true; 982 } 983 }; 984 985 class WorkerDataAvailableRunnable final : public MainThreadWorkerRunnable { 986 RefPtr<WorkerFetchResolver> mResolver; 987 988 public: 989 WorkerDataAvailableRunnable(WorkerPrivate* aWorkerPrivate, 990 WorkerFetchResolver* aResolver) 991 : MainThreadWorkerRunnable("WorkerDataAvailableRunnable"), 992 mResolver(aResolver) {} 993 994 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 995 MOZ_ASSERT(aWorkerPrivate); 996 aWorkerPrivate->AssertIsOnWorkerThread(); 997 998 RefPtr<FetchObserver> fetchObserver = 999 mResolver->GetFetchObserver(aWorkerPrivate); 1000 1001 if (fetchObserver && fetchObserver->State() == FetchState::Requesting) { 1002 fetchObserver->SetState(FetchState::Responding); 1003 } 1004 1005 return true; 1006 } 1007 }; 1008 1009 class WorkerFetchResponseEndBase { 1010 protected: 1011 RefPtr<WorkerFetchResolver> mResolver; 1012 1013 public: 1014 explicit WorkerFetchResponseEndBase(WorkerFetchResolver* aResolver) 1015 : mResolver(aResolver) { 1016 MOZ_ASSERT(aResolver); 1017 } 1018 1019 void WorkerRunInternal(WorkerPrivate* aWorkerPrivate) { 1020 mResolver->Shutdown(aWorkerPrivate); 1021 } 1022 }; 1023 1024 class WorkerFetchResponseEndRunnable final : public MainThreadWorkerRunnable, 1025 public WorkerFetchResponseEndBase { 1026 FetchDriverObserver::EndReason mReason; 1027 1028 public: 1029 WorkerFetchResponseEndRunnable(WorkerPrivate* aWorkerPrivate, 1030 WorkerFetchResolver* aResolver, 1031 FetchDriverObserver::EndReason aReason) 1032 : MainThreadWorkerRunnable("WorkerFetchResponseEndRunnable"), 1033 WorkerFetchResponseEndBase(aResolver), 1034 mReason(aReason) {} 1035 1036 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 1037 if (mResolver->IsShutdown(aWorkerPrivate)) { 1038 return true; 1039 } 1040 1041 if (mReason == FetchDriverObserver::eAborted) { 1042 mResolver->WorkerPromise(aWorkerPrivate) 1043 ->MaybeReject(NS_ERROR_DOM_ABORT_ERR); 1044 } 1045 1046 WorkerRunInternal(aWorkerPrivate); 1047 return true; 1048 } 1049 1050 nsresult Cancel() override { return Run(); } 1051 }; 1052 1053 class WorkerFetchResponseEndControlRunnable final 1054 : public MainThreadWorkerControlRunnable, 1055 public WorkerFetchResponseEndBase { 1056 public: 1057 WorkerFetchResponseEndControlRunnable(WorkerPrivate* aWorkerPrivate, 1058 WorkerFetchResolver* aResolver) 1059 : MainThreadWorkerControlRunnable( 1060 "WorkerFetchResponseEndControlRunnable"), 1061 WorkerFetchResponseEndBase(aResolver) {} 1062 1063 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 1064 WorkerRunInternal(aWorkerPrivate); 1065 return true; 1066 } 1067 1068 // Control runnable cancel already calls Run(). 1069 }; 1070 1071 void WorkerFetchResolver::OnResponseAvailableInternal( 1072 SafeRefPtr<InternalResponse> aResponse) { 1073 AssertIsOnMainThread(); 1074 1075 MutexAutoLock lock(mPromiseProxy->Lock()); 1076 if (mPromiseProxy->CleanedUp()) { 1077 return; 1078 } 1079 1080 RefPtr<WorkerFetchResponseRunnable> r = new WorkerFetchResponseRunnable( 1081 mPromiseProxy->GetWorkerPrivate(), this, std::move(aResponse)); 1082 1083 if (!r->Dispatch(mPromiseProxy->GetWorkerPrivate())) { 1084 NS_WARNING("Could not dispatch fetch response"); 1085 } 1086 } 1087 1088 bool WorkerFetchResolver::NeedOnDataAvailable() { 1089 AssertIsOnMainThread(); 1090 return mNeedOnDataAvailable; 1091 } 1092 1093 void WorkerFetchResolver::OnDataAvailable() { 1094 AssertIsOnMainThread(); 1095 1096 MutexAutoLock lock(mPromiseProxy->Lock()); 1097 if (mPromiseProxy->CleanedUp()) { 1098 return; 1099 } 1100 1101 RefPtr<WorkerDataAvailableRunnable> r = 1102 new WorkerDataAvailableRunnable(mPromiseProxy->GetWorkerPrivate(), this); 1103 (void)r->Dispatch(mPromiseProxy->GetWorkerPrivate()); 1104 } 1105 1106 void WorkerFetchResolver::OnResponseEnd(FetchDriverObserver::EndReason aReason, 1107 JS::Handle<JS::Value> aReasonDetails) { 1108 AssertIsOnMainThread(); 1109 MutexAutoLock lock(mPromiseProxy->Lock()); 1110 if (mPromiseProxy->CleanedUp()) { 1111 return; 1112 } 1113 1114 FlushConsoleReport(); 1115 1116 (void)aReasonDetails; 1117 1118 RefPtr<WorkerFetchResponseEndRunnable> r = new WorkerFetchResponseEndRunnable( 1119 mPromiseProxy->GetWorkerPrivate(), this, aReason); 1120 1121 if (!r->Dispatch(mPromiseProxy->GetWorkerPrivate())) { 1122 RefPtr<WorkerFetchResponseEndControlRunnable> cr = 1123 new WorkerFetchResponseEndControlRunnable( 1124 mPromiseProxy->GetWorkerPrivate(), this); 1125 // This can fail if the worker thread is canceled or killed causing 1126 // the PromiseWorkerProxy to give up its WorkerRef immediately, 1127 // allowing the worker thread to become Dead. 1128 if (!cr->Dispatch(mPromiseProxy->GetWorkerPrivate())) { 1129 NS_WARNING("Failed to dispatch WorkerFetchResponseEndControlRunnable"); 1130 } 1131 } 1132 } 1133 1134 void WorkerFetchResolver::FlushConsoleReport() { 1135 AssertIsOnMainThread(); 1136 MOZ_ASSERT(mPromiseProxy); 1137 1138 if (!mReporter) { 1139 return; 1140 } 1141 1142 WorkerPrivate* worker = mPromiseProxy->GetWorkerPrivate(); 1143 if (!worker) { 1144 mReporter->FlushReportsToConsole(0); 1145 return; 1146 } 1147 1148 if (worker->IsServiceWorker()) { 1149 // Flush to service worker 1150 mReporter->FlushReportsToConsoleForServiceWorkerScope( 1151 worker->ServiceWorkerScope()); 1152 return; 1153 } 1154 1155 if (worker->IsSharedWorker()) { 1156 // Flush to shared worker 1157 worker->GetRemoteWorkerController()->FlushReportsOnMainThread(mReporter); 1158 return; 1159 } 1160 1161 // Flush to dedicated worker 1162 mReporter->FlushConsoleReports(worker->GetLoadGroup()); 1163 } 1164 1165 nsresult ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit, 1166 nsIInputStream** aStream, 1167 nsCString& aContentTypeWithCharset, 1168 uint64_t& aContentLength) { 1169 MOZ_ASSERT(aStream); 1170 nsAutoCString charset; 1171 aContentTypeWithCharset.SetIsVoid(true); 1172 1173 if (aBodyInit.IsArrayBuffer()) { 1174 BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer()); 1175 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1176 charset); 1177 } 1178 1179 if (aBodyInit.IsArrayBufferView()) { 1180 BodyExtractor<const ArrayBufferView> body( 1181 &aBodyInit.GetAsArrayBufferView()); 1182 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1183 charset); 1184 } 1185 1186 if (aBodyInit.IsBlob()) { 1187 Blob& blob = aBodyInit.GetAsBlob(); 1188 BodyExtractor<const Blob> body(&blob); 1189 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1190 charset); 1191 } 1192 1193 if (aBodyInit.IsFormData()) { 1194 FormData& formData = aBodyInit.GetAsFormData(); 1195 BodyExtractor<const FormData> body(&formData); 1196 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1197 charset); 1198 } 1199 1200 if (aBodyInit.IsUSVString()) { 1201 BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString()); 1202 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1203 charset); 1204 } 1205 1206 if (aBodyInit.IsURLSearchParams()) { 1207 URLSearchParams& usp = aBodyInit.GetAsURLSearchParams(); 1208 BodyExtractor<const URLSearchParams> body(&usp); 1209 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1210 charset); 1211 } 1212 1213 MOZ_ASSERT_UNREACHABLE("Should never reach here"); 1214 return NS_ERROR_FAILURE; 1215 } 1216 1217 nsresult ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit, 1218 nsIInputStream** aStream, 1219 nsCString& aContentTypeWithCharset, 1220 uint64_t& aContentLength) { 1221 MOZ_ASSERT(aStream); 1222 MOZ_ASSERT(!*aStream); 1223 1224 nsAutoCString charset; 1225 aContentTypeWithCharset.SetIsVoid(true); 1226 1227 if (aBodyInit.IsArrayBuffer()) { 1228 BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer()); 1229 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1230 charset); 1231 } 1232 1233 if (aBodyInit.IsArrayBufferView()) { 1234 BodyExtractor<const ArrayBufferView> body( 1235 &aBodyInit.GetAsArrayBufferView()); 1236 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1237 charset); 1238 } 1239 1240 if (aBodyInit.IsBlob()) { 1241 BodyExtractor<const Blob> body(&aBodyInit.GetAsBlob()); 1242 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1243 charset); 1244 } 1245 1246 if (aBodyInit.IsFormData()) { 1247 BodyExtractor<const FormData> body(&aBodyInit.GetAsFormData()); 1248 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1249 charset); 1250 } 1251 1252 if (aBodyInit.IsUSVString()) { 1253 BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString()); 1254 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1255 charset); 1256 } 1257 1258 if (aBodyInit.IsURLSearchParams()) { 1259 BodyExtractor<const URLSearchParams> body( 1260 &aBodyInit.GetAsURLSearchParams()); 1261 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1262 charset); 1263 } 1264 1265 MOZ_ASSERT_UNREACHABLE("Should never reach here"); 1266 return NS_ERROR_FAILURE; 1267 } 1268 1269 nsresult ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit, 1270 nsIInputStream** aStream, 1271 nsCString& aContentTypeWithCharset, 1272 uint64_t& aContentLength) { 1273 MOZ_ASSERT(aStream); 1274 MOZ_ASSERT(!*aStream); 1275 1276 // ReadableStreams should be handled by 1277 // BodyExtractorReadableStream::GetAsStream. 1278 MOZ_ASSERT(!aBodyInit.IsReadableStream()); 1279 1280 nsAutoCString charset; 1281 aContentTypeWithCharset.SetIsVoid(true); 1282 1283 if (aBodyInit.IsArrayBuffer()) { 1284 BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer()); 1285 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1286 charset); 1287 } 1288 1289 if (aBodyInit.IsArrayBufferView()) { 1290 BodyExtractor<const ArrayBufferView> body( 1291 &aBodyInit.GetAsArrayBufferView()); 1292 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1293 charset); 1294 } 1295 1296 if (aBodyInit.IsBlob()) { 1297 BodyExtractor<const Blob> body(&aBodyInit.GetAsBlob()); 1298 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1299 charset); 1300 } 1301 1302 if (aBodyInit.IsFormData()) { 1303 BodyExtractor<const FormData> body(&aBodyInit.GetAsFormData()); 1304 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1305 charset); 1306 } 1307 1308 if (aBodyInit.IsUSVString()) { 1309 BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString()); 1310 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1311 charset); 1312 } 1313 1314 if (aBodyInit.IsURLSearchParams()) { 1315 BodyExtractor<const URLSearchParams> body( 1316 &aBodyInit.GetAsURLSearchParams()); 1317 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset, 1318 charset); 1319 } 1320 1321 MOZ_ASSERT_UNREACHABLE("Should never reach here"); 1322 return NS_ERROR_FAILURE; 1323 } 1324 1325 NS_IMPL_CYCLE_COLLECTION(FetchBodyBase, mReadableStreamBody) 1326 1327 NS_IMPL_CYCLE_COLLECTING_ADDREF(FetchBodyBase) 1328 NS_IMPL_CYCLE_COLLECTING_RELEASE(FetchBodyBase) 1329 1330 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchBodyBase) 1331 NS_INTERFACE_MAP_ENTRY(nsISupports) 1332 NS_INTERFACE_MAP_END 1333 1334 template <class Derived> 1335 FetchBody<Derived>::FetchBody(nsIGlobalObject* aOwner) 1336 : mOwner(aOwner), mBodyUsed(false) { 1337 MOZ_ASSERT(aOwner); 1338 1339 if (!NS_IsMainThread()) { 1340 WorkerPrivate* wp = GetCurrentThreadWorkerPrivate(); 1341 MOZ_ASSERT(wp); 1342 mMainThreadEventTarget = wp->MainThreadEventTarget(); 1343 } else { 1344 mMainThreadEventTarget = GetMainThreadSerialEventTarget(); 1345 } 1346 1347 MOZ_ASSERT(mMainThreadEventTarget); 1348 } 1349 1350 template FetchBody<Request>::FetchBody(nsIGlobalObject* aOwner); 1351 1352 template FetchBody<Response>::FetchBody(nsIGlobalObject* aOwner); 1353 1354 template <class Derived> 1355 FetchBody<Derived>::~FetchBody() { 1356 Unfollow(); 1357 } 1358 1359 template FetchBody<Request>::~FetchBody(); 1360 1361 template FetchBody<Response>::~FetchBody(); 1362 1363 template <class Derived> 1364 bool FetchBody<Derived>::BodyUsed() const { 1365 if (mBodyUsed) { 1366 return true; 1367 } 1368 1369 // If this stream is disturbed, return true. 1370 if (mReadableStreamBody) { 1371 return mReadableStreamBody->Disturbed(); 1372 } 1373 1374 return false; 1375 } 1376 1377 template bool FetchBody<Request>::BodyUsed() const; 1378 1379 template bool FetchBody<Response>::BodyUsed() const; 1380 1381 template <class Derived> 1382 void FetchBody<Derived>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv) { 1383 MOZ_ASSERT(aCx); 1384 MOZ_ASSERT(mOwner->SerialEventTarget()->IsOnCurrentThread()); 1385 1386 MOZ_DIAGNOSTIC_ASSERT(!BodyUsed(), "Consuming already used body?"); 1387 if (BodyUsed()) { 1388 return; 1389 } 1390 1391 mBodyUsed = true; 1392 1393 // If we already have a ReadableStreamBody and it has been created by DOM, we 1394 // have to lock it now because it can have been shared with other objects. 1395 if (mReadableStreamBody) { 1396 if (mFetchStreamReader) { 1397 // Having FetchStreamReader means there's no nsIInputStream underlying it 1398 MOZ_ASSERT(!mReadableStreamBody->MaybeGetInputStreamIfUnread()); 1399 mFetchStreamReader->StartConsuming(aCx, mReadableStreamBody, aRv); 1400 return; 1401 } 1402 // We should have nsIInputStream at this point as long as it's still 1403 // readable 1404 MOZ_ASSERT_IF( 1405 mReadableStreamBody->State() == ReadableStream::ReaderState::Readable, 1406 mReadableStreamBody->MaybeGetInputStreamIfUnread()); 1407 LockStream(aCx, mReadableStreamBody, aRv); 1408 } 1409 } 1410 1411 template void FetchBody<Request>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv); 1412 1413 template void FetchBody<Response>::SetBodyUsed(JSContext* aCx, 1414 ErrorResult& aRv); 1415 1416 template <class Derived> 1417 already_AddRefed<Promise> FetchBody<Derived>::ConsumeBody( 1418 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv) { 1419 aRv.MightThrowJSException(); 1420 1421 RefPtr<AbortSignalImpl> signalImpl = 1422 DerivedClass()->GetSignalImplToConsumeBody(); 1423 1424 if (signalImpl && signalImpl->Aborted()) { 1425 JS::Rooted<JS::Value> abortReason(aCx, signalImpl->RawReason()); 1426 1427 if (abortReason.get().isUndefined()) { 1428 aRv.Throw(NS_ERROR_DOM_ABORT_ERR); 1429 return nullptr; 1430 } 1431 1432 nsCOMPtr<nsIGlobalObject> go = DerivedClass()->GetParentObject(); 1433 1434 RefPtr<Promise> promise = Promise::Create(go, aRv); 1435 promise->MaybeReject(abortReason); 1436 return promise.forget(); 1437 } 1438 1439 if (BodyUsed()) { 1440 aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>(); 1441 return nullptr; 1442 } 1443 1444 nsAutoCString mimeType; 1445 nsAutoCString mixedCaseMimeType; 1446 DerivedClass()->GetMimeType(mimeType, mixedCaseMimeType); 1447 1448 // Null bodies are a special-case in the fetch spec. The Body mix-in can only 1449 // be "disturbed" or "locked" if its associated "body" is non-null. 1450 // Additionally, the Body min-in's "consume body" algorithm explicitly creates 1451 // a fresh empty ReadableStream object in step 2. This means that `bodyUsed` 1452 // will never return true for a null body. 1453 // 1454 // To this end, we create a fresh (empty) body every time a request is made 1455 // and consume its body here, without marking this FetchBody consumed via 1456 // SetBodyUsed. 1457 nsCOMPtr<nsIInputStream> bodyStream; 1458 DerivedClass()->GetBody(getter_AddRefs(bodyStream)); 1459 if (!bodyStream) { 1460 RefPtr<EmptyBody> emptyBody = 1461 EmptyBody::Create(DerivedClass()->GetParentObject(), 1462 DerivedClass()->GetPrincipalInfo().get(), signalImpl, 1463 mimeType, mixedCaseMimeType, aRv); 1464 if (NS_WARN_IF(aRv.Failed())) { 1465 return nullptr; 1466 } 1467 1468 return emptyBody->ConsumeBody(aCx, aType, aRv); 1469 } 1470 1471 SetBodyUsed(aCx, aRv); 1472 if (NS_WARN_IF(aRv.Failed())) { 1473 return nullptr; 1474 } 1475 1476 nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject(); 1477 1478 MutableBlobStorage::MutableBlobStorageType blobStorageType = 1479 MutableBlobStorage::eOnlyInMemory; 1480 const mozilla::UniquePtr<mozilla::ipc::PrincipalInfo>& principalInfo = 1481 DerivedClass()->GetPrincipalInfo(); 1482 // We support temporary file for blobs only if the principal is known and 1483 // it's system or content not in private Browsing. 1484 if (principalInfo && 1485 (principalInfo->type() == 1486 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo || 1487 (principalInfo->type() == 1488 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo && 1489 !principalInfo->get_ContentPrincipalInfo() 1490 .attrs() 1491 .IsPrivateBrowsing()))) { 1492 blobStorageType = MutableBlobStorage::eCouldBeInTemporaryFile; 1493 } 1494 1495 RefPtr<Promise> promise = BodyConsumer::Create( 1496 global, mMainThreadEventTarget, bodyStream, signalImpl, aType, 1497 BodyBlobURISpec(), BodyLocalPath(), mimeType, mixedCaseMimeType, 1498 blobStorageType, aRv); 1499 if (NS_WARN_IF(aRv.Failed())) { 1500 return nullptr; 1501 } 1502 1503 return promise.forget(); 1504 } 1505 1506 template already_AddRefed<Promise> FetchBody<Request>::ConsumeBody( 1507 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv); 1508 1509 template already_AddRefed<Promise> FetchBody<Response>::ConsumeBody( 1510 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv); 1511 1512 template already_AddRefed<Promise> FetchBody<EmptyBody>::ConsumeBody( 1513 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv); 1514 1515 template <class Derived> 1516 void FetchBody<Derived>::GetMimeType(nsACString& aMimeType, 1517 nsACString& aMixedCaseMimeType) { 1518 // Implements "extract a MIME type" from 1519 // https://fetch.spec.whatwg.org/#concept-header-extract-mime-type 1520 MOZ_ASSERT(DerivedClass()->GetInternalHeaders()); 1521 1522 ErrorResult result; 1523 nsAutoCString contentTypeValue; 1524 DerivedClass()->GetInternalHeaders()->Get("Content-Type"_ns, contentTypeValue, 1525 result); 1526 MOZ_ALWAYS_TRUE(!result.Failed()); 1527 1528 if (contentTypeValue.IsVoid()) { 1529 return; 1530 } 1531 1532 nsTArray<nsTDependentSubstring<char>> values = 1533 CMimeType::SplitMimetype(contentTypeValue); 1534 1535 nsAutoCString charset; 1536 nsAutoCString essence; 1537 RefPtr<CMimeType> mimeType; 1538 1539 for (const auto& value : values) { 1540 RefPtr<CMimeType> temporaryMimeType = CMimeType::Parse(value); 1541 1542 if (!temporaryMimeType) { 1543 continue; 1544 } 1545 1546 nsAutoCString temporaryEssence; 1547 temporaryMimeType->GetEssence(temporaryEssence); 1548 1549 if (temporaryEssence.EqualsLiteral("*/*")) { 1550 continue; 1551 } 1552 1553 mimeType = temporaryMimeType; 1554 1555 if (!temporaryEssence.Equals(essence)) { 1556 charset.Truncate(); 1557 mimeType->GetParameterValue("charset"_ns, charset, false, false); 1558 1559 essence = temporaryEssence; 1560 } else { 1561 nsAutoCString newCharset; 1562 if (!mimeType->GetParameterValue("charset"_ns, newCharset, false, 1563 false) && 1564 !charset.IsEmpty()) { 1565 mimeType->SetParameterValue("charset"_ns, charset); 1566 } else if (!newCharset.IsEmpty()) { 1567 charset = newCharset; 1568 } 1569 } 1570 } 1571 1572 if (mimeType) { 1573 mimeType->Serialize(aMixedCaseMimeType); 1574 aMimeType = aMixedCaseMimeType; 1575 ToLowerCase(aMimeType); 1576 } 1577 } 1578 1579 template void FetchBody<Request>::GetMimeType(nsACString& aMimeType, 1580 nsACString& aMixedCaseMimeType); 1581 template void FetchBody<Response>::GetMimeType(nsACString& aMimeType, 1582 nsACString& aMixedCaseMimeType); 1583 1584 template <class Derived> 1585 const nsACString& FetchBody<Derived>::BodyBlobURISpec() const { 1586 return DerivedClass()->BodyBlobURISpec(); 1587 } 1588 1589 template const nsACString& FetchBody<Request>::BodyBlobURISpec() const; 1590 1591 template const nsACString& FetchBody<Response>::BodyBlobURISpec() const; 1592 1593 template const nsACString& FetchBody<EmptyBody>::BodyBlobURISpec() const; 1594 1595 template <class Derived> 1596 const nsAString& FetchBody<Derived>::BodyLocalPath() const { 1597 return DerivedClass()->BodyLocalPath(); 1598 } 1599 1600 template const nsAString& FetchBody<Request>::BodyLocalPath() const; 1601 1602 template const nsAString& FetchBody<Response>::BodyLocalPath() const; 1603 1604 template const nsAString& FetchBody<EmptyBody>::BodyLocalPath() const; 1605 1606 template <class Derived> 1607 void FetchBody<Derived>::SetReadableStreamBody(JSContext* aCx, 1608 ReadableStream* aBody) { 1609 MOZ_ASSERT(!mReadableStreamBody); 1610 MOZ_ASSERT(aBody); 1611 mReadableStreamBody = aBody; 1612 1613 RefPtr<AbortSignalImpl> signalImpl = DerivedClass()->GetSignalImpl(); 1614 if (!signalImpl) { 1615 return; 1616 } 1617 1618 bool aborted = signalImpl->Aborted(); 1619 if (aborted) { 1620 IgnoredErrorResult result; 1621 JS::Rooted<JS::Value> abortReason(aCx, signalImpl->RawReason()); 1622 AbortStream(aCx, mReadableStreamBody, result, abortReason); 1623 if (NS_WARN_IF(result.Failed())) { 1624 return; 1625 } 1626 } else if (!IsFollowing()) { 1627 Follow(signalImpl); 1628 } 1629 } 1630 1631 template void FetchBody<Request>::SetReadableStreamBody(JSContext* aCx, 1632 ReadableStream* aBody); 1633 1634 template void FetchBody<Response>::SetReadableStreamBody(JSContext* aCx, 1635 ReadableStream* aBody); 1636 1637 template <class Derived> 1638 already_AddRefed<ReadableStream> FetchBody<Derived>::GetBody(JSContext* aCx, 1639 ErrorResult& aRv) { 1640 if (mReadableStreamBody) { 1641 return do_AddRef(mReadableStreamBody); 1642 } 1643 1644 nsCOMPtr<nsIInputStream> inputStream; 1645 DerivedClass()->GetBody(getter_AddRefs(inputStream)); 1646 1647 if (!inputStream) { 1648 return nullptr; 1649 } 1650 1651 // The spec immediately creates ReadableStream on Response/Request constructor 1652 // via https://fetch.spec.whatwg.org/#concept-bodyinit-extract, but Gecko 1653 // creates nsIInputStream there instead and creates ReadableStream only when 1654 // .body is accessed. Thus we only follow step 4 of it here. 1655 // 1656 // Step 4: Otherwise, set stream to a new ReadableStream object, and set up 1657 // stream with byte reading support. 1658 auto algorithms = 1659 MakeRefPtr<NonAsyncInputToReadableStreamAlgorithms>(*inputStream); 1660 RefPtr<ReadableStream> body = ReadableStream::CreateByteNative( 1661 aCx, DerivedClass()->GetParentObject(), *algorithms, Nothing(), aRv); 1662 if (aRv.Failed()) { 1663 return nullptr; 1664 } 1665 mReadableStreamBody = body; 1666 1667 // If the body has been already consumed, we lock the stream. 1668 if (BodyUsed()) { 1669 LockStream(aCx, body, aRv); 1670 if (NS_WARN_IF(aRv.Failed())) { 1671 return nullptr; 1672 } 1673 } 1674 1675 RefPtr<AbortSignalImpl> signalImpl = DerivedClass()->GetSignalImpl(); 1676 if (signalImpl) { 1677 if (signalImpl->Aborted()) { 1678 JS::Rooted<JS::Value> abortReason(aCx, signalImpl->RawReason()); 1679 AbortStream(aCx, body, aRv, abortReason); 1680 if (NS_WARN_IF(aRv.Failed())) { 1681 return nullptr; 1682 } 1683 } else if (!IsFollowing()) { 1684 Follow(signalImpl); 1685 } 1686 } 1687 1688 return body.forget(); 1689 } 1690 1691 template already_AddRefed<ReadableStream> FetchBody<Request>::GetBody( 1692 JSContext* aCx, ErrorResult& aRv); 1693 1694 template already_AddRefed<ReadableStream> FetchBody<Response>::GetBody( 1695 JSContext* aCx, ErrorResult& aRv); 1696 1697 template <class Derived> 1698 void FetchBody<Derived>::LockStream(JSContext* aCx, ReadableStream* aStream, 1699 ErrorResult& aRv) { 1700 // This is native stream, creating a reader will not execute any JS code. 1701 RefPtr<ReadableStreamDefaultReader> reader = aStream->GetReader(aRv); 1702 if (aRv.Failed()) { 1703 return; 1704 } 1705 } 1706 1707 template void FetchBody<Request>::LockStream(JSContext* aCx, 1708 ReadableStream* aStream, 1709 ErrorResult& aRv); 1710 1711 template void FetchBody<Response>::LockStream(JSContext* aCx, 1712 ReadableStream* aStream, 1713 ErrorResult& aRv); 1714 1715 template <class Derived> 1716 void FetchBody<Derived>::MaybeTeeReadableStreamBody( 1717 JSContext* aCx, ReadableStream** aBodyOut, 1718 FetchStreamReader** aStreamReader, nsIInputStream** aInputStream, 1719 ErrorResult& aRv) { 1720 MOZ_DIAGNOSTIC_ASSERT(aStreamReader); 1721 MOZ_DIAGNOSTIC_ASSERT(aInputStream); 1722 MOZ_DIAGNOSTIC_ASSERT(!BodyUsed()); 1723 1724 *aBodyOut = nullptr; 1725 *aStreamReader = nullptr; 1726 *aInputStream = nullptr; 1727 1728 if (!mReadableStreamBody) { 1729 return; 1730 } 1731 1732 // If this is a ReadableStream with an native source, this has been 1733 // generated by a Fetch. In this case, Fetch will be able to recreate it 1734 // again when GetBody() is called. 1735 if (mReadableStreamBody->MaybeGetInputStreamIfUnread()) { 1736 *aBodyOut = nullptr; 1737 return; 1738 } 1739 1740 nsTArray<RefPtr<ReadableStream>> branches; 1741 MOZ_KnownLive(mReadableStreamBody)->Tee(aCx, branches, aRv); 1742 if (aRv.Failed()) { 1743 return; 1744 } 1745 1746 mReadableStreamBody = branches[0]; 1747 branches[1].forget(aBodyOut); 1748 1749 aRv = FetchStreamReader::Create(aCx, mOwner, aStreamReader, aInputStream); 1750 if (NS_WARN_IF(aRv.Failed())) { 1751 return; 1752 } 1753 } 1754 1755 template void FetchBody<Request>::MaybeTeeReadableStreamBody( 1756 JSContext* aCx, ReadableStream** aBodyOut, 1757 FetchStreamReader** aStreamReader, nsIInputStream** aInputStream, 1758 ErrorResult& aRv); 1759 1760 template void FetchBody<Response>::MaybeTeeReadableStreamBody( 1761 JSContext* aCx, ReadableStream** aBodyOut, 1762 FetchStreamReader** aStreamReader, nsIInputStream** aInputStream, 1763 ErrorResult& aRv); 1764 1765 template <class Derived> 1766 void FetchBody<Derived>::RunAbortAlgorithm() { 1767 if (!mReadableStreamBody) { 1768 return; 1769 } 1770 1771 AutoJSAPI jsapi; 1772 if (!jsapi.Init(mOwner)) { 1773 return; 1774 } 1775 1776 JSContext* cx = jsapi.cx(); 1777 1778 RefPtr<ReadableStream> body(mReadableStreamBody); 1779 IgnoredErrorResult result; 1780 1781 JS::Rooted<JS::Value> abortReason(cx); 1782 1783 AbortSignalImpl* signalImpl = Signal(); 1784 if (signalImpl) { 1785 abortReason.set(signalImpl->RawReason()); 1786 } 1787 1788 AbortStream(cx, body, result, abortReason); 1789 } 1790 1791 template void FetchBody<Request>::RunAbortAlgorithm(); 1792 1793 template void FetchBody<Response>::RunAbortAlgorithm(); 1794 1795 NS_IMPL_ADDREF_INHERITED(EmptyBody, FetchBody<EmptyBody>) 1796 NS_IMPL_RELEASE_INHERITED(EmptyBody, FetchBody<EmptyBody>) 1797 1798 NS_IMPL_CYCLE_COLLECTION_CLASS(EmptyBody) 1799 1800 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EmptyBody, FetchBody<EmptyBody>) 1801 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) 1802 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAbortSignalImpl) 1803 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchStreamReader) 1804 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1805 1806 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EmptyBody, 1807 FetchBody<EmptyBody>) 1808 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) 1809 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAbortSignalImpl) 1810 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchStreamReader) 1811 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1812 1813 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(EmptyBody, FetchBody<EmptyBody>) 1814 NS_IMPL_CYCLE_COLLECTION_TRACE_END 1815 1816 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EmptyBody) 1817 NS_INTERFACE_MAP_END_INHERITING(FetchBody<EmptyBody>) 1818 1819 EmptyBody::EmptyBody(nsIGlobalObject* aGlobal, 1820 mozilla::ipc::PrincipalInfo* aPrincipalInfo, 1821 AbortSignalImpl* aAbortSignalImpl, 1822 const nsACString& aMimeType, 1823 const nsACString& aMixedCaseMimeType, 1824 already_AddRefed<nsIInputStream> aBodyStream) 1825 : FetchBody<EmptyBody>(aGlobal), 1826 mAbortSignalImpl(aAbortSignalImpl), 1827 mMimeType(aMimeType), 1828 mMixedCaseMimeType(aMixedCaseMimeType), 1829 mBodyStream(std::move(aBodyStream)) { 1830 if (aPrincipalInfo) { 1831 mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*aPrincipalInfo); 1832 } 1833 } 1834 1835 EmptyBody::~EmptyBody() = default; 1836 1837 /* static */ 1838 already_AddRefed<EmptyBody> EmptyBody::Create( 1839 nsIGlobalObject* aGlobal, mozilla::ipc::PrincipalInfo* aPrincipalInfo, 1840 AbortSignalImpl* aAbortSignalImpl, const nsACString& aMimeType, 1841 const nsACString& aMixedCaseMimeType, ErrorResult& aRv) { 1842 nsCOMPtr<nsIInputStream> bodyStream; 1843 aRv = NS_NewCStringInputStream(getter_AddRefs(bodyStream), ""_ns); 1844 if (NS_WARN_IF(aRv.Failed())) { 1845 return nullptr; 1846 } 1847 1848 RefPtr<EmptyBody> emptyBody = 1849 new EmptyBody(aGlobal, aPrincipalInfo, aAbortSignalImpl, aMimeType, 1850 aMixedCaseMimeType, bodyStream.forget()); 1851 return emptyBody.forget(); 1852 } 1853 1854 void EmptyBody::GetBody(nsIInputStream** aStream, int64_t* aBodyLength) { 1855 MOZ_ASSERT(aStream); 1856 1857 if (aBodyLength) { 1858 *aBodyLength = 0; 1859 } 1860 1861 nsCOMPtr<nsIInputStream> bodyStream = mBodyStream; 1862 bodyStream.forget(aStream); 1863 } 1864 1865 } // namespace mozilla::dom