Backed out changeset f594e6f00208 (bug 1940883) for causing crashes in bug 1941164.
[gecko.git] / dom / fetch / FetchParent.cpp
blob8b82381abea924213d37dde3a0a2f5dbc4349886
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 "FetchService.h"
8 #include "InternalRequest.h"
9 #include "InternalResponse.h"
10 #include "mozilla/Unused.h"
11 #include "mozilla/dom/ClientInfo.h"
12 #include "mozilla/dom/FetchTypes.h"
13 #include "mozilla/dom/PerformanceTimingTypes.h"
14 #include "mozilla/dom/ServiceWorkerDescriptor.h"
15 #include "mozilla/ipc/BackgroundParent.h"
16 #include "nsThreadUtils.h"
18 using namespace mozilla::ipc;
20 namespace mozilla::dom {
22 NS_IMPL_ISUPPORTS(FetchParent::FetchParentCSPEventListener, nsICSPEventListener)
24 FetchParent::FetchParentCSPEventListener::FetchParentCSPEventListener(
25 const nsID& aActorID, nsCOMPtr<nsISerialEventTarget> aEventTarget)
26 : mActorID(aActorID), mEventTarget(aEventTarget) {
27 MOZ_ASSERT(mEventTarget);
28 FETCH_LOG(("FetchParentCSPEventListener [%p] actor ID: %s", this,
29 mActorID.ToString().get()));
32 NS_IMETHODIMP FetchParent::FetchParentCSPEventListener::OnCSPViolationEvent(
33 const nsAString& aJSON) {
34 AssertIsOnMainThread();
35 FETCH_LOG(("FetchParentCSPEventListener::OnCSPViolationEvent [%p]", this));
37 nsAutoString json(aJSON);
38 nsCOMPtr<nsIRunnable> r =
39 NS_NewRunnableFunction(__func__, [actorID = mActorID, json]() mutable {
40 FETCH_LOG(
41 ("FetchParentCSPEventListener::OnCSPViolationEvent, Runnale"));
42 RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
43 if (actor) {
44 actor->OnCSPViolationEvent(json);
46 });
48 MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r, nsIThread::DISPATCH_NORMAL));
49 return NS_OK;
52 MOZ_RUNINIT nsTHashMap<nsIDHashKey, RefPtr<FetchParent>>
53 FetchParent::sActorTable;
55 /*static*/
56 RefPtr<FetchParent> FetchParent::GetActorByID(const nsID& aID) {
57 AssertIsOnBackgroundThread();
58 auto entry = sActorTable.Lookup(aID);
59 if (entry) {
60 return entry.Data();
62 return nullptr;
65 FetchParent::FetchParent() : mID(nsID::GenerateUUID()) {
66 FETCH_LOG(("FetchParent::FetchParent [%p]", this));
67 AssertIsOnBackgroundThread();
68 mBackgroundEventTarget = GetCurrentSerialEventTarget();
69 MOZ_ASSERT(mBackgroundEventTarget);
70 if (!sActorTable.WithEntryHandle(mID, [&](auto&& entry) {
71 if (entry.HasEntry()) {
72 return false;
74 entry.Insert(this);
75 return true;
76 })) {
77 FETCH_LOG(("FetchParent::FetchParent entry[%p] already exists", this));
81 FetchParent::~FetchParent() {
82 FETCH_LOG(("FetchParent::~FetchParent [%p]", this));
83 // MOZ_ASSERT(!mBackgroundEventTarget);
84 MOZ_ASSERT(mActorDestroyed && mIsDone);
85 mResponsePromises = nullptr;
88 IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
89 FETCH_LOG(("FetchParent::RecvFetchOp [%p]", this));
90 AssertIsOnBackgroundThread();
92 MOZ_ASSERT(!mIsDone);
93 if (mActorDestroyed) {
94 return IPC_OK();
97 mRequest = MakeSafeRefPtr<InternalRequest>(std::move(aArgs.request()));
98 mIsWorkerFetch = aArgs.isWorkerRequest();
99 mPrincipalInfo = std::move(aArgs.principalInfo());
100 mWorkerScript = aArgs.workerScript();
101 mClientInfo = Some(ClientInfo(aArgs.clientInfo()));
102 if (aArgs.controller().isSome()) {
103 mController = Some(ServiceWorkerDescriptor(aArgs.controller().ref()));
105 mCookieJarSettings = aArgs.cookieJarSettings();
106 mNeedOnDataAvailable = aArgs.needOnDataAvailable();
107 mHasCSPEventListener = aArgs.hasCSPEventListener();
108 mIsThirdPartyContext = aArgs.isThirdPartyContext();
109 mIsOn3PCBExceptionList = aArgs.isOn3PCBExceptionList();
111 if (mHasCSPEventListener) {
112 mCSPEventListener =
113 MakeRefPtr<FetchParentCSPEventListener>(mID, mBackgroundEventTarget);
115 mAssociatedBrowsingContextID = aArgs.associatedBrowsingContextID();
117 MOZ_ASSERT(!mPromise);
118 mPromise = new GenericPromise::Private(__func__);
120 RefPtr<FetchParent> self = this;
121 mPromise->Then(
122 mBackgroundEventTarget, __func__,
123 [self](const bool&& result) mutable {
124 FETCH_LOG(
125 ("FetchParent::RecvFetchOp [%p] Success Callback", self.get()));
126 AssertIsOnBackgroundThread();
127 self->mPromise = nullptr;
128 if (self->mIsDone) {
129 FETCH_LOG(("FetchParent::RecvFetchOp [%p] Fetch has already aborted",
130 self.get()));
131 if (!self->mActorDestroyed) {
132 Unused << NS_WARN_IF(
133 !self->Send__delete__(self, NS_ERROR_DOM_ABORT_ERR));
135 return;
137 self->mIsDone = true;
138 if (!self->mActorDestroyed && !self->mExtendForCSPEventListener) {
139 FETCH_LOG(("FetchParent::RecvFetchOp [%p] Send__delete__(NS_OK)",
140 self.get()));
141 Unused << NS_WARN_IF(!self->Send__delete__(self, NS_OK));
144 [self](const nsresult&& aErr) mutable {
145 FETCH_LOG(
146 ("FetchParent::RecvFetchOp [%p] Failure Callback", self.get()));
147 AssertIsOnBackgroundThread();
148 self->mIsDone = true;
149 self->mPromise = nullptr;
150 if (!self->mActorDestroyed) {
151 FETCH_LOG(("FetchParent::RecvFetchOp [%p] Send__delete__(aErr)",
152 self.get()));
153 Unused << NS_WARN_IF(!self->Send__delete__(self, aErr));
157 RefPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() mutable {
158 FETCH_LOG(
159 ("FetchParent::RecvFetchOp [%p], Main Thread Runnable", self.get()));
160 AssertIsOnMainThread();
161 if (self->mIsDone) {
162 MOZ_ASSERT(!self->mResponsePromises);
163 MOZ_ASSERT(self->mPromise);
164 FETCH_LOG(
165 ("FetchParent::RecvFetchOp [%p], Main Thread Runnable, "
166 "already aborted",
167 self.get()));
168 self->mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
169 return;
171 RefPtr<FetchService> fetchService = FetchService::GetInstance();
172 MOZ_ASSERT(fetchService);
173 MOZ_ASSERT(self->mRequest);
174 MOZ_ASSERT(!self->mResponsePromises);
175 if (self->mIsWorkerFetch) {
176 self->mResponsePromises =
177 fetchService->Fetch(AsVariant(FetchService::WorkerFetchArgs(
178 {self->mRequest.clonePtr(), self->mPrincipalInfo,
179 self->mWorkerScript, self->mClientInfo, self->mController,
180 self->mCookieJarSettings, self->mNeedOnDataAvailable,
181 self->mCSPEventListener, self->mAssociatedBrowsingContextID,
182 self->mBackgroundEventTarget, self->mID,
183 self->mIsThirdPartyContext,
184 MozPromiseRequestHolder<FetchServiceResponseEndPromise>(),
185 self->mPromise, self->mIsOn3PCBExceptionList})));
186 } else {
187 MOZ_ASSERT(self->mRequest->GetKeepalive());
188 self->mResponsePromises =
189 fetchService->Fetch(AsVariant(FetchService::MainThreadFetchArgs({
190 self->mRequest.clonePtr(),
191 self->mPrincipalInfo,
192 self->mCookieJarSettings,
193 self->mNeedOnDataAvailable,
194 self->mCSPEventListener,
195 self->mAssociatedBrowsingContextID,
196 self->mBackgroundEventTarget,
197 self->mID,
198 self->mIsThirdPartyContext,
199 })));
202 bool isResolved =
203 self->mResponsePromises->GetResponseEndPromise()->IsResolved();
204 if (!isResolved && self->mIsWorkerFetch) {
205 // track only unresolved promises for worker fetch requests
206 // this is needed for clean-up of keepalive requests
207 self->mResponsePromises->GetResponseEndPromise()
208 ->Then(
209 GetMainThreadSerialEventTarget(), __func__,
210 [self](ResponseEndArgs&& aArgs) mutable {
211 AssertIsOnMainThread();
212 MOZ_ASSERT(self->mPromise);
213 self->mPromise->Resolve(true, __func__);
214 self->mResponsePromises = nullptr;
216 [self](CopyableErrorResult&& aErr) mutable {
217 AssertIsOnMainThread();
218 MOZ_ASSERT(self->mPromise);
219 self->mPromise->Reject(aErr.StealNSResult(), __func__);
220 self->mResponsePromises = nullptr;
222 ->Track(fetchService->GetResponseEndPromiseHolder(
223 self->mResponsePromises));
224 } else {
225 self->mResponsePromises->GetResponseEndPromise()->Then(
226 GetMainThreadSerialEventTarget(), __func__,
227 [self](ResponseEndArgs&& aArgs) mutable {
228 AssertIsOnMainThread();
229 MOZ_ASSERT(self->mPromise);
230 self->mPromise->Resolve(true, __func__);
231 self->mResponsePromises = nullptr;
233 [self](CopyableErrorResult&& aErr) mutable {
234 AssertIsOnMainThread();
235 MOZ_ASSERT(self->mPromise);
236 self->mPromise->Reject(aErr.StealNSResult(), __func__);
237 self->mResponsePromises = nullptr;
242 MOZ_ALWAYS_SUCCEEDS(
243 NS_DispatchToMainThread(r.forget(), nsIThread::DISPATCH_NORMAL));
245 return IPC_OK();
248 IPCResult FetchParent::RecvAbortFetchOp(bool aForceAbort) {
249 FETCH_LOG(("FetchParent::RecvAbortFetchOp [%p]", this));
250 AssertIsOnBackgroundThread();
252 if (mIsDone) {
253 FETCH_LOG(("FetchParent::RecvAbortFetchOp [%p], Already aborted", this));
254 return IPC_OK();
257 if (!aForceAbort && mRequest && mRequest->GetKeepalive()) {
258 // Keeping FetchParent/FetchChild alive for the main-thread keepalive fetch
259 // here is a temporary solution. The cancel logic should always be handled
260 // in FetchInstance::Cancel() once all main-thread fetch routing through
261 // PFetch.
262 if (!mIsWorkerFetch) {
263 FETCH_LOG(("Skip aborting fetch as the request is marked keepalive"));
264 return IPC_OK();
266 } else {
267 mIsDone = true;
269 RefPtr<FetchParent> self = this;
270 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
271 __func__, [self, forceAbort = aForceAbort]() mutable {
272 FETCH_LOG(("FetchParent::RecvAbortFetchOp Runnable"));
273 AssertIsOnMainThread();
274 if (self->mResponsePromises) {
275 RefPtr<FetchService> fetchService = FetchService::GetInstance();
276 MOZ_ASSERT(fetchService);
277 fetchService->CancelFetch(std::move(self->mResponsePromises),
278 forceAbort);
282 MOZ_ALWAYS_SUCCEEDS(
283 NS_DispatchToMainThread(r.forget(), nsIThread::DISPATCH_NORMAL));
285 return IPC_OK();
288 void FetchParent::OnResponseAvailableInternal(
289 SafeRefPtr<InternalResponse>&& aResponse) {
290 FETCH_LOG(("FetchParent::OnResponseAvailableInternal [%p]", this));
291 AssertIsOnBackgroundThread();
292 MOZ_ASSERT(aResponse);
293 MOZ_ASSERT(!mActorDestroyed);
295 if (mIsDone && aResponse->Type() != ResponseType::Error) {
296 FETCH_LOG(
297 ("FetchParent::OnResponseAvailableInternal [%p] "
298 "Fetch has already aborted",
299 this));
300 return;
303 // To monitor the stream status between processes, response's body can not
304 // be serialized as RemoteLazyInputStream. Such that stream close can be
305 // propagated to FetchDriver in the parent process.
306 aResponse->SetSerializeAsLazy(false);
308 // CSP violation notification is asynchronous. Extending the FetchParent's
309 // life cycle for the notificaiton.
310 if (aResponse->Type() == ResponseType::Error &&
311 aResponse->GetErrorCode() == NS_ERROR_CONTENT_BLOCKED &&
312 mCSPEventListener) {
313 FETCH_LOG(
314 ("FetchParent::OnResponseAvailableInternal [%p] "
315 "NS_ERROR_CONTENT_BLOCKED",
316 this));
317 mExtendForCSPEventListener = true;
320 Unused << SendOnResponseAvailableInternal(
321 aResponse->ToParentToChildInternalResponse());
324 void FetchParent::OnResponseEnd(const ResponseEndArgs& aArgs) {
325 FETCH_LOG(("FetchParent::OnResponseEnd [%p]", this));
326 AssertIsOnBackgroundThread();
327 MOZ_ASSERT(!mActorDestroyed);
329 if (mIsDone && aArgs.endReason() != FetchDriverObserver::eAborted) {
330 FETCH_LOG(
331 ("FetchParent::OnResponseEnd [%p] "
332 "Fetch has already aborted",
333 this));
334 return;
337 Unused << SendOnResponseEnd(aArgs);
340 void FetchParent::OnDataAvailable() {
341 FETCH_LOG(("FetchParent::OnDataAvailable [%p]", this));
342 AssertIsOnBackgroundThread();
343 MOZ_ASSERT(!mActorDestroyed);
345 Unused << SendOnDataAvailable();
348 void FetchParent::OnFlushConsoleReport(
349 const nsTArray<net::ConsoleReportCollected>& aReports) {
350 FETCH_LOG(("FetchParent::OnFlushConsoleReport [%p]", this));
351 AssertIsOnBackgroundThread();
352 MOZ_ASSERT(!mActorDestroyed);
354 Unused << SendOnFlushConsoleReport(aReports);
357 void FetchParent::OnReportPerformanceTiming(const ResponseTiming&& aTiming) {
358 FETCH_LOG(("FetchParent::OnReportPerformanceTiming [%p]", this));
359 AssertIsOnBackgroundThread();
360 MOZ_ASSERT(!mActorDestroyed);
362 Unused << SendOnReportPerformanceTiming(aTiming);
365 void FetchParent::OnNotifyNetworkMonitorAlternateStack(uint64_t aChannelID) {
366 FETCH_LOG(("FetchParent::OnNotifyNetworkMonitorAlternateStack [%p]", this));
367 AssertIsOnBackgroundThread();
368 MOZ_ASSERT(!mActorDestroyed);
370 Unused << SendOnNotifyNetworkMonitorAlternateStack(aChannelID);
373 void FetchParent::ActorDestroy(ActorDestroyReason aReason) {
374 FETCH_LOG(("FetchParent::ActorDestroy [%p]", this));
375 AssertIsOnBackgroundThread();
376 mActorDestroyed = true;
377 auto entry = sActorTable.Lookup(mID);
378 if (entry) {
379 entry.Remove();
380 FETCH_LOG(("FetchParent::ActorDestroy entry [%p] removed", this));
382 // mRequest can be null when FetchParent has not yet received RecvFetchOp()
383 if (!mRequest) {
384 return;
387 // Abort the existing fetch.
388 // Actor can be destoried by shutdown when still fetching.
389 RecvAbortFetchOp(false);
391 // mBackgroundEventTarget = nullptr;
394 nsICSPEventListener* FetchParent::GetCSPEventListener() {
395 return mCSPEventListener;
398 void FetchParent::OnCSPViolationEvent(const nsAString& aJSON) {
399 FETCH_LOG(("FetchParent::OnCSPViolationEvent [%p]", this));
400 AssertIsOnBackgroundThread();
401 MOZ_ASSERT(mHasCSPEventListener);
402 MOZ_ASSERT(!mActorDestroyed);
404 Unused << SendOnCSPViolationEvent(aJSON);
407 } // namespace mozilla::dom