Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / fetch / FetchService.cpp
blob77decbc56f510895b44a751c8f5aba0a31f00bf9
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "FetchLog.h"
6 #include "FetchParent.h"
7 #include "nsContentUtils.h"
8 #include "nsIContentSecurityPolicy.h"
9 #include "nsICookieJarSettings.h"
10 #include "nsILoadGroup.h"
11 #include "nsILoadInfo.h"
12 #include "nsIIOService.h"
13 #include "nsIObserverService.h"
14 #include "nsIPrincipal.h"
15 #include "nsIScriptSecurityManager.h"
16 #include "nsNetUtil.h"
17 #include "nsThreadUtils.h"
18 #include "nsXULAppAPI.h"
19 #include "mozilla/BasePrincipal.h"
20 #include "mozilla/ClearOnShutdown.h"
21 #include "mozilla/SchedulerGroup.h"
22 #include "mozilla/ScopeExit.h"
23 #include "mozilla/UniquePtr.h"
24 #include "mozilla/dom/ClientInfo.h"
25 #include "mozilla/dom/FetchService.h"
26 #include "mozilla/dom/InternalRequest.h"
27 #include "mozilla/dom/InternalResponse.h"
28 #include "mozilla/dom/PerformanceStorage.h"
29 #include "mozilla/dom/PerformanceTiming.h"
30 #include "mozilla/dom/ServiceWorkerDescriptor.h"
31 #include "mozilla/ipc/BackgroundUtils.h"
32 #include "mozilla/net/CookieJarSettings.h"
34 namespace mozilla::dom {
36 mozilla::LazyLogModule gFetchLog("Fetch");
38 // FetchServicePromises
40 FetchServicePromises::FetchServicePromises()
41 : mAvailablePromise(
42 MakeRefPtr<FetchServiceResponseAvailablePromise::Private>(__func__)),
43 mTimingPromise(
44 MakeRefPtr<FetchServiceResponseTimingPromise::Private>(__func__)),
45 mEndPromise(
46 MakeRefPtr<FetchServiceResponseEndPromise::Private>(__func__)) {
47 mAvailablePromise->UseSynchronousTaskDispatch(__func__);
48 mTimingPromise->UseSynchronousTaskDispatch(__func__);
49 mEndPromise->UseSynchronousTaskDispatch(__func__);
52 RefPtr<FetchServiceResponseAvailablePromise>
53 FetchServicePromises::GetResponseAvailablePromise() {
54 return mAvailablePromise;
57 RefPtr<FetchServiceResponseTimingPromise>
58 FetchServicePromises::GetResponseTimingPromise() {
59 return mTimingPromise;
62 RefPtr<FetchServiceResponseEndPromise>
63 FetchServicePromises::GetResponseEndPromise() {
64 return mEndPromise;
67 void FetchServicePromises::ResolveResponseAvailablePromise(
68 FetchServiceResponse&& aResponse, const char* aMethodName) {
69 if (mAvailablePromise) {
70 mAvailablePromise->Resolve(std::move(aResponse), aMethodName);
74 void FetchServicePromises::RejectResponseAvailablePromise(
75 const CopyableErrorResult&& aError, const char* aMethodName) {
76 if (mAvailablePromise) {
77 mAvailablePromise->Reject(aError, aMethodName);
81 void FetchServicePromises::ResolveResponseTimingPromise(
82 ResponseTiming&& aTiming, const char* aMethodName) {
83 if (mTimingPromise) {
84 mTimingPromise->Resolve(std::move(aTiming), aMethodName);
88 void FetchServicePromises::RejectResponseTimingPromise(
89 const CopyableErrorResult&& aError, const char* aMethodName) {
90 if (mTimingPromise) {
91 mTimingPromise->Reject(aError, aMethodName);
95 void FetchServicePromises::ResolveResponseEndPromise(ResponseEndArgs&& aArgs,
96 const char* aMethodName) {
97 if (mEndPromise) {
98 mEndPromise->Resolve(std::move(aArgs), aMethodName);
102 void FetchServicePromises::RejectResponseEndPromise(
103 const CopyableErrorResult&& aError, const char* aMethodName) {
104 if (mEndPromise) {
105 mEndPromise->Reject(aError, aMethodName);
109 // FetchInstance
111 nsresult FetchService::FetchInstance::Initialize(FetchArgs&& aArgs) {
112 MOZ_ASSERT(XRE_IsParentProcess());
113 MOZ_ASSERT(NS_IsMainThread());
114 MOZ_ASSERT(!aArgs.is<UnknownArgs>() && mArgs.is<UnknownArgs>());
116 mArgs = std::move(aArgs);
118 // Get needed information for FetchDriver from passed-in channel.
119 if (mArgs.is<NavigationPreloadArgs>()) {
120 mRequest = mArgs.as<NavigationPreloadArgs>().mRequest.clonePtr();
121 nsIChannel* channel = mArgs.as<NavigationPreloadArgs>().mChannel;
122 FETCH_LOG(("FetchInstance::Initialize [%p] request[%p], channel[%p]", this,
123 mRequest.unsafeGetRawPtr(), channel));
125 nsresult rv;
126 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
127 MOZ_ASSERT(loadInfo);
129 nsCOMPtr<nsIURI> channelURI;
130 rv = channel->GetURI(getter_AddRefs(channelURI));
131 if (NS_WARN_IF(NS_FAILED(rv))) {
132 return rv;
135 nsIScriptSecurityManager* securityManager =
136 nsContentUtils::GetSecurityManager();
137 if (securityManager) {
138 securityManager->GetChannelResultPrincipal(channel,
139 getter_AddRefs(mPrincipal));
142 if (!mPrincipal) {
143 return NS_ERROR_UNEXPECTED;
146 // Get loadGroup from channel
147 rv = channel->GetLoadGroup(getter_AddRefs(mLoadGroup));
148 if (NS_WARN_IF(NS_FAILED(rv))) {
149 return rv;
151 if (!mLoadGroup) {
152 rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), mPrincipal);
153 if (NS_WARN_IF(NS_FAILED(rv))) {
154 return rv;
158 // Get CookieJarSettings from channel
159 rv = loadInfo->GetCookieJarSettings(getter_AddRefs(mCookieJarSettings));
160 if (NS_WARN_IF(NS_FAILED(rv))) {
161 return rv;
164 // Get PerformanceStorage from channel
165 mPerformanceStorage = loadInfo->GetPerformanceStorage();
166 } else {
167 mIsWorkerFetch = true;
168 mRequest = mArgs.as<WorkerFetchArgs>().mRequest.clonePtr();
170 FETCH_LOG(("FetchInstance::Initialize [%p] request[%p]", this,
171 mRequest.unsafeGetRawPtr()));
173 auto principalOrErr =
174 PrincipalInfoToPrincipal(mArgs.as<WorkerFetchArgs>().mPrincipalInfo);
175 if (principalOrErr.isErr()) {
176 return principalOrErr.unwrapErr();
178 mPrincipal = principalOrErr.unwrap();
179 nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), mPrincipal);
180 if (NS_WARN_IF(NS_FAILED(rv))) {
181 return rv;
184 if (mArgs.as<WorkerFetchArgs>().mCookieJarSettings.isSome()) {
185 net::CookieJarSettings::Deserialize(
186 mArgs.as<WorkerFetchArgs>().mCookieJarSettings.ref(),
187 getter_AddRefs(mCookieJarSettings));
191 return NS_OK;
194 RefPtr<FetchServicePromises> FetchService::FetchInstance::Fetch() {
195 MOZ_ASSERT(XRE_IsParentProcess());
196 MOZ_ASSERT(NS_IsMainThread());
198 MOZ_ASSERT(mPrincipal);
199 MOZ_ASSERT(mLoadGroup);
201 nsAutoCString principalSpec;
202 MOZ_ALWAYS_SUCCEEDS(mPrincipal->GetAsciiSpec(principalSpec));
203 nsAutoCString requestURL;
204 mRequest->GetURL(requestURL);
205 FETCH_LOG(("FetchInstance::Fetch [%p], mRequest URL: %s mPrincipal: %s", this,
206 requestURL.BeginReading(), principalSpec.BeginReading()));
208 nsresult rv;
210 // Create a FetchDriver instance
211 mFetchDriver = MakeRefPtr<FetchDriver>(
212 mRequest.clonePtr(), // Fetch Request
213 mPrincipal, // Principal
214 mLoadGroup, // LoadGroup
215 GetMainThreadSerialEventTarget(), // MainThreadEventTarget
216 mCookieJarSettings, // CookieJarSettings
217 mPerformanceStorage, // PerformanceStorage
218 false // IsTrackingFetch
221 if (mIsWorkerFetch) {
222 auto& args = mArgs.as<WorkerFetchArgs>();
223 mFetchDriver->SetWorkerScript(args.mWorkerScript);
224 MOZ_ASSERT(args.mClientInfo.isSome());
225 mFetchDriver->SetClientInfo(args.mClientInfo.ref());
226 mFetchDriver->SetController(args.mController);
227 if (args.mCSPEventListener) {
228 mFetchDriver->SetCSPEventListener(args.mCSPEventListener);
230 mFetchDriver->SetAssociatedBrowsingContextID(
231 args.mAssociatedBrowsingContextID);
234 mFetchDriver->EnableNetworkInterceptControl();
236 mPromises = MakeRefPtr<FetchServicePromises>();
238 // Call FetchDriver::Fetch to start fetching.
239 // Pass AbortSignalImpl as nullptr since we no need support AbortSignalImpl
240 // with FetchService. AbortSignalImpl related information should be passed
241 // through PFetch or InterceptedHttpChannel, then call
242 // FetchService::CancelFetch() to abort the running fetch.
243 rv = mFetchDriver->Fetch(nullptr, this);
244 if (NS_WARN_IF(NS_FAILED(rv))) {
245 FETCH_LOG(
246 ("FetchInstance::Fetch FetchDriver::Fetch failed(0x%X)", (uint32_t)rv));
247 return FetchService::NetworkErrorResponse(rv, mArgs);
250 return mPromises;
253 void FetchService::FetchInstance::Cancel() {
254 MOZ_ASSERT(XRE_IsParentProcess());
255 MOZ_ASSERT(NS_IsMainThread());
257 FETCH_LOG(("FetchInstance::Cancel() [%p]", this));
259 // If mFetchDriver is not null here, FetchInstance::Fetch() has already
260 // started, let mFetchDriver::RunAbortAlgorithm() to call
261 // FetchInstance::OnResponseEnd() to resolve the pending promises.
262 // Otherwise, resolving the pending promises here.
263 if (mFetchDriver) {
264 mFetchDriver->RunAbortAlgorithm();
265 return;
268 MOZ_ASSERT(mPromises);
270 mPromises->ResolveResponseAvailablePromise(
271 InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR), __func__);
273 mPromises->ResolveResponseTimingPromise(ResponseTiming(), __func__);
275 mPromises->ResolveResponseEndPromise(
276 ResponseEndArgs(FetchDriverObserver::eAborted), __func__);
279 void FetchService::FetchInstance::OnResponseEnd(
280 FetchDriverObserver::EndReason aReason,
281 JS::Handle<JS::Value> aReasonDetails) {
282 FETCH_LOG(("FetchInstance::OnResponseEnd [%p] %s", this,
283 aReason == eAborted ? "eAborted" : "eNetworking"));
285 if (mIsWorkerFetch) {
286 FlushConsoleReport();
287 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
288 __func__, [endArgs = ResponseEndArgs(aReason),
289 actorID = mArgs.as<WorkerFetchArgs>().mActorID]() {
290 FETCH_LOG(("FetchInstance::OnResponseEnd, Runnable"));
291 RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
292 if (actor) {
293 actor->OnResponseEnd(std::move(endArgs));
296 MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
297 r, nsIThread::DISPATCH_NORMAL));
300 MOZ_ASSERT(mPromises);
302 if (aReason == eAborted) {
303 // If ResponseAvailablePromise has not resolved yet, resolved with
304 // NS_ERROR_DOM_ABORT_ERR response.
305 if (!mPromises->GetResponseAvailablePromise()->IsResolved()) {
306 mPromises->ResolveResponseAvailablePromise(
307 InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR), __func__);
310 // If ResponseTimingPromise has not resolved yet, resolved with empty
311 // ResponseTiming.
312 if (!mPromises->GetResponseTimingPromise()->IsResolved()) {
313 mPromises->ResolveResponseTimingPromise(ResponseTiming(), __func__);
315 // Resolve the ResponseEndPromise
316 mPromises->ResolveResponseEndPromise(ResponseEndArgs(aReason), __func__);
317 return;
320 MOZ_ASSERT(mPromises->GetResponseAvailablePromise()->IsResolved() &&
321 mPromises->GetResponseTimingPromise()->IsResolved());
323 // Resolve the ResponseEndPromise
324 mPromises->ResolveResponseEndPromise(ResponseEndArgs(aReason), __func__);
326 // Remove the FetchInstance from FetchInstanceTable
327 RefPtr<FetchService> fetchService = FetchService::GetInstance();
328 MOZ_ASSERT(fetchService);
329 auto entry = fetchService->mFetchInstanceTable.Lookup(mPromises);
330 if (entry) {
331 entry.Remove();
332 FETCH_LOG(
333 ("FetchInstance::OnResponseEnd entry of responsePromise[%p] is "
334 "removed",
335 mPromises.get()));
339 void FetchService::FetchInstance::OnResponseAvailableInternal(
340 SafeRefPtr<InternalResponse> aResponse) {
341 FETCH_LOG(("FetchInstance::OnResponseAvailableInternal [%p]", this));
342 mResponse = std::move(aResponse);
344 nsCOMPtr<nsIInputStream> body;
345 mResponse->GetUnfilteredBody(getter_AddRefs(body));
346 FETCH_LOG(
347 ("FetchInstance::OnResponseAvailableInternal [%p] response body: %p",
348 this, body.get()));
350 if (mIsWorkerFetch) {
351 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
352 __func__, [response = mResponse.clonePtr(),
353 actorID = mArgs.as<WorkerFetchArgs>().mActorID]() mutable {
354 FETCH_LOG(("FetchInstance::OnResponseAvailableInternal Runnable"));
355 RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
356 if (actor) {
357 actor->OnResponseAvailableInternal(std::move(response));
360 MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
361 r, nsIThread::DISPATCH_NORMAL));
364 MOZ_ASSERT(mPromises);
366 // Resolve the ResponseAvailablePromise
367 mPromises->ResolveResponseAvailablePromise(mResponse.clonePtr(), __func__);
370 bool FetchService::FetchInstance::NeedOnDataAvailable() {
371 if (mArgs.is<WorkerFetchArgs>()) {
372 return mArgs.as<WorkerFetchArgs>().mNeedOnDataAvailable;
374 return false;
377 void FetchService::FetchInstance::OnDataAvailable() {
378 FETCH_LOG(("FetchInstance::OnDataAvailable [%p]", this));
380 if (!NeedOnDataAvailable()) {
381 return;
384 if (mIsWorkerFetch) {
385 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
386 __func__, [actorID = mArgs.as<WorkerFetchArgs>().mActorID]() {
387 FETCH_LOG(("FetchInstance::OnDataAvailable, Runnable"));
388 RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
389 if (actor) {
390 actor->OnDataAvailable();
393 MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
394 r, nsIThread::DISPATCH_NORMAL));
398 void FetchService::FetchInstance::FlushConsoleReport() {
399 FETCH_LOG(("FetchInstance::FlushConsoleReport [%p]", this));
401 if (mIsWorkerFetch) {
402 if (!mReporter) {
403 return;
405 nsTArray<net::ConsoleReportCollected> reports;
406 mReporter->StealConsoleReports(reports);
407 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
408 __func__, [actorID = mArgs.as<WorkerFetchArgs>().mActorID,
409 consoleReports = std::move(reports)]() {
410 FETCH_LOG(("FetchInstance::FlushConsolReport, Runnable"));
411 RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
412 if (actor) {
413 actor->OnFlushConsoleReport(std::move(consoleReports));
416 MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
417 r, nsIThread::DISPATCH_NORMAL));
421 void FetchService::FetchInstance::OnReportPerformanceTiming() {
422 FETCH_LOG(("FetchInstance::OnReportPerformanceTiming [%p]", this));
423 MOZ_ASSERT(mFetchDriver);
424 MOZ_ASSERT(mPromises);
426 if (mPromises->GetResponseTimingPromise()->IsResolved()) {
427 return;
430 ResponseTiming timing;
431 UniquePtr<PerformanceTimingData> performanceTiming(
432 mFetchDriver->GetPerformanceTimingData(timing.initiatorType(),
433 timing.entryName()));
434 // FetchDriver has no corresponding performance timing when fetch() failed.
435 // Resolve the ResponseTimingPromise with empty timing.
436 if (!performanceTiming) {
437 mPromises->ResolveResponseTimingPromise(ResponseTiming(), __func__);
438 return;
440 timing.timingData() = performanceTiming->ToIPC();
441 // Force replace initiatorType for ServiceWorkerNavgationPreload.
442 if (!mIsWorkerFetch) {
443 timing.initiatorType() = u"navigation"_ns;
444 } else {
445 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
446 __func__,
447 [actorID = mArgs.as<WorkerFetchArgs>().mActorID, timing = timing]() {
448 FETCH_LOG(("FetchInstance::OnReportPerformanceTiming, Runnable"));
449 RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
450 if (actor) {
451 actor->OnReportPerformanceTiming(std::move(timing));
454 MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
455 r, nsIThread::DISPATCH_NORMAL));
458 mPromises->ResolveResponseTimingPromise(std::move(timing), __func__);
461 void FetchService::FetchInstance::OnNotifyNetworkMonitorAlternateStack(
462 uint64_t aChannelID) {
463 FETCH_LOG(("FetchInstance::OnNotifyNetworkMonitorAlternateStack [%p]", this));
464 MOZ_ASSERT(mFetchDriver);
465 MOZ_ASSERT(mPromises);
466 if (!mIsWorkerFetch) {
467 return;
470 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
471 __func__, [actorID = mArgs.as<WorkerFetchArgs>().mActorID,
472 channelID = aChannelID]() {
473 FETCH_LOG(
474 ("FetchInstance::NotifyNetworkMonitorAlternateStack, Runnable"));
475 RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
476 if (actor) {
477 actor->OnNotifyNetworkMonitorAlternateStack(channelID);
481 MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
482 r, nsIThread::DISPATCH_NORMAL));
485 // FetchService
487 NS_IMPL_ISUPPORTS(FetchService, nsIObserver)
489 StaticRefPtr<FetchService> gInstance;
491 /*static*/
492 already_AddRefed<FetchService> FetchService::GetInstance() {
493 MOZ_ASSERT(XRE_IsParentProcess());
494 MOZ_ASSERT(NS_IsMainThread());
496 if (!gInstance) {
497 gInstance = MakeRefPtr<FetchService>();
498 nsresult rv = gInstance->RegisterNetworkObserver();
499 if (NS_WARN_IF(NS_FAILED(rv))) {
500 gInstance = nullptr;
501 return nullptr;
503 ClearOnShutdown(&gInstance);
505 RefPtr<FetchService> service = gInstance;
506 return service.forget();
509 /*static*/
510 RefPtr<FetchServicePromises> FetchService::NetworkErrorResponse(
511 nsresult aRv, const FetchArgs& aArgs) {
512 if (aArgs.is<WorkerFetchArgs>()) {
513 const WorkerFetchArgs& args = aArgs.as<WorkerFetchArgs>();
514 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
515 __func__, [aRv, actorID = args.mActorID]() mutable {
516 FETCH_LOG(
517 ("FetchService::PropagateErrorResponse runnable aError: 0x%X",
518 (uint32_t)aRv));
519 RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
520 if (actor) {
521 actor->OnResponseAvailableInternal(
522 InternalResponse::NetworkError(aRv));
523 actor->OnResponseEnd(
524 ResponseEndArgs(FetchDriverObserver::eAborted));
527 MOZ_ALWAYS_SUCCEEDS(
528 args.mEventTarget->Dispatch(r, nsIThread::DISPATCH_NORMAL));
531 RefPtr<FetchServicePromises> promises = MakeRefPtr<FetchServicePromises>();
532 promises->ResolveResponseAvailablePromise(InternalResponse::NetworkError(aRv),
533 __func__);
534 promises->ResolveResponseTimingPromise(ResponseTiming(), __func__);
535 promises->ResolveResponseEndPromise(
536 ResponseEndArgs(FetchDriverObserver::eAborted), __func__);
537 return promises;
540 FetchService::FetchService() {
541 MOZ_ASSERT(XRE_IsParentProcess());
542 MOZ_ASSERT(NS_IsMainThread());
545 FetchService::~FetchService() {
546 MOZ_ALWAYS_SUCCEEDS(UnregisterNetworkObserver());
549 nsresult FetchService::RegisterNetworkObserver() {
550 AssertIsOnMainThread();
551 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
552 if (!observerService) {
553 return NS_ERROR_UNEXPECTED;
556 nsCOMPtr<nsIIOService> ioService = services::GetIOService();
557 if (!ioService) {
558 return NS_ERROR_UNEXPECTED;
561 nsresult rv = observerService->AddObserver(
562 this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false);
563 NS_ENSURE_SUCCESS(rv, rv);
565 rv = observerService->AddObserver(this, "xpcom-shutdown", false);
566 NS_ENSURE_SUCCESS(rv, rv);
568 rv = ioService->GetOffline(&mOffline);
569 NS_ENSURE_SUCCESS(rv, rv);
570 mObservingNetwork = true;
572 return NS_OK;
575 nsresult FetchService::UnregisterNetworkObserver() {
576 AssertIsOnMainThread();
577 nsresult rv;
578 if (mObservingNetwork) {
579 nsCOMPtr<nsIObserverService> observerService =
580 mozilla::services::GetObserverService();
581 if (observerService) {
582 rv = observerService->RemoveObserver(this,
583 NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
584 NS_ENSURE_SUCCESS(rv, rv);
585 rv = observerService->RemoveObserver(this, "xpcom-shutdown");
586 NS_ENSURE_SUCCESS(rv, rv);
588 mObservingNetwork = false;
590 return NS_OK;
593 NS_IMETHODIMP FetchService::Observe(nsISupports* aSubject, const char* aTopic,
594 const char16_t* aData) {
595 FETCH_LOG(("FetchService::Observe topic: %s", aTopic));
596 AssertIsOnMainThread();
597 MOZ_ASSERT(!strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) ||
598 !strcmp(aTopic, "xpcom-shutdown"));
600 if (!strcmp(aTopic, "xpcom-shutdown")) {
601 // Going to shutdown, unregister the network status observer to avoid
602 // receiving
603 nsresult rv = UnregisterNetworkObserver();
604 NS_ENSURE_SUCCESS(rv, rv);
605 return NS_OK;
608 if (nsDependentString(aData).EqualsLiteral(NS_IOSERVICE_ONLINE)) {
609 mOffline = false;
610 } else {
611 mOffline = true;
612 // Network is offline, cancel running fetchs.
613 for (auto it = mFetchInstanceTable.begin(), end = mFetchInstanceTable.end();
614 it != end; ++it) {
615 it->GetData()->Cancel();
617 mFetchInstanceTable.Clear();
619 return NS_OK;
622 RefPtr<FetchServicePromises> FetchService::Fetch(FetchArgs&& aArgs) {
623 MOZ_ASSERT(XRE_IsParentProcess());
624 MOZ_ASSERT(NS_IsMainThread());
626 FETCH_LOG(("FetchService::Fetch (%s)", aArgs.is<NavigationPreloadArgs>()
627 ? "NavigationPreload"
628 : "WorkerFetch"));
629 if (mOffline) {
630 FETCH_LOG(("FetchService::Fetch network offline"));
631 return NetworkErrorResponse(NS_ERROR_OFFLINE, aArgs);
634 // Create FetchInstance
635 RefPtr<FetchInstance> fetch = MakeRefPtr<FetchInstance>();
637 // Call FetchInstance::Initialize() to get needed information for FetchDriver
638 nsresult rv = fetch->Initialize(std::move(aArgs));
639 if (NS_WARN_IF(NS_FAILED(rv))) {
640 return NetworkErrorResponse(rv, fetch->Args());
643 // Call FetchInstance::Fetch() to start an asynchronous fetching.
644 RefPtr<FetchServicePromises> promises = fetch->Fetch();
645 MOZ_ASSERT(promises);
647 if (!promises->GetResponseAvailablePromise()->IsResolved()) {
648 // Insert the created FetchInstance into FetchInstanceTable.
649 if (!mFetchInstanceTable.WithEntryHandle(promises, [&](auto&& entry) {
650 if (entry.HasEntry()) {
651 return false;
653 entry.Insert(fetch);
654 return true;
655 })) {
656 FETCH_LOG(
657 ("FetchService::Fetch entry[%p] already exists", promises.get()));
658 return NetworkErrorResponse(NS_ERROR_UNEXPECTED, fetch->Args());
660 FETCH_LOG(("FetchService::Fetch entry[%p] of FetchInstance[%p] added",
661 promises.get(), fetch.get()));
663 return promises;
666 void FetchService::CancelFetch(const RefPtr<FetchServicePromises>&& aPromises) {
667 MOZ_ASSERT(XRE_IsParentProcess());
668 MOZ_ASSERT(NS_IsMainThread());
669 MOZ_ASSERT(aPromises);
670 FETCH_LOG(("FetchService::CancelFetch aPromises[%p]", aPromises.get()));
672 auto entry = mFetchInstanceTable.Lookup(aPromises);
673 if (entry) {
674 // Notice any modifications here before entry.Remove() probably should be
675 // reflected to Observe() offline case.
676 entry.Data()->Cancel();
677 entry.Remove();
678 FETCH_LOG(
679 ("FetchService::CancelFetch entry [%p] removed", aPromises.get()));
683 } // namespace mozilla::dom