FetchParent.cpp (14095B)
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/. */ 4 5 #include "FetchParent.h" 6 7 #include "FetchLog.h" 8 #include "FetchService.h" 9 #include "InternalRequest.h" 10 #include "InternalResponse.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" 17 18 using namespace mozilla::ipc; 19 20 namespace mozilla::dom { 21 22 NS_IMPL_ISUPPORTS(FetchParent::FetchParentCSPEventListener, nsICSPEventListener) 23 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())); 30 } 31 32 NS_IMETHODIMP FetchParent::FetchParentCSPEventListener::OnCSPViolationEvent( 33 const nsAString& aJSON) { 34 AssertIsOnMainThread(); 35 FETCH_LOG(("FetchParentCSPEventListener::OnCSPViolationEvent [%p]", this)); 36 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); 45 } 46 }); 47 48 MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r, nsIThread::DISPATCH_NORMAL)); 49 return NS_OK; 50 } 51 52 MOZ_RUNINIT nsTHashMap<nsIDHashKey, RefPtr<FetchParent>> 53 FetchParent::sActorTable; 54 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(); 61 } 62 return nullptr; 63 } 64 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; 73 } 74 entry.Insert(this); 75 return true; 76 })) { 77 FETCH_LOG(("FetchParent::FetchParent entry[%p] already exists", this)); 78 } 79 } 80 81 FetchParent::~FetchParent() { 82 FETCH_LOG(("FetchParent::~FetchParent [%p]", this)); 83 // MOZ_ASSERT(!mBackgroundEventTarget); 84 MOZ_ASSERT(mActorDestroyed && mIsDone); 85 mResponsePromises = nullptr; 86 } 87 88 IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) { 89 FETCH_LOG(("FetchParent::RecvFetchOp [%p]", this)); 90 AssertIsOnBackgroundThread(); 91 92 MOZ_ASSERT(!mIsDone); 93 if (mActorDestroyed) { 94 return IPC_OK(); 95 } 96 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())); 104 } 105 mCookieJarSettings = aArgs.cookieJarSettings(); 106 mNeedOnDataAvailable = aArgs.needOnDataAvailable(); 107 mHasCSPEventListener = aArgs.hasCSPEventListener(); 108 mIsThirdPartyContext = aArgs.isThirdPartyContext(); 109 mIsOn3PCBExceptionList = aArgs.isOn3PCBExceptionList(); 110 111 if (mHasCSPEventListener) { 112 mCSPEventListener = 113 MakeRefPtr<FetchParentCSPEventListener>(mID, mBackgroundEventTarget); 114 } 115 mAssociatedBrowsingContextID = aArgs.associatedBrowsingContextID(); 116 117 MOZ_ASSERT(!mPromise); 118 mPromise = new GenericPromise::Private(__func__); 119 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 (void)NS_WARN_IF( 133 !self->Send__delete__(self, NS_ERROR_DOM_ABORT_ERR)); 134 } 135 return; 136 } 137 self->mIsDone = true; 138 if (!self->mActorDestroyed && !self->mExtendForCSPEventListener) { 139 FETCH_LOG(("FetchParent::RecvFetchOp [%p] Send__delete__(NS_OK)", 140 self.get())); 141 (void)NS_WARN_IF(!self->Send__delete__(self, NS_OK)); 142 } 143 }, 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 (void)NS_WARN_IF(!self->Send__delete__(self, aErr)); 154 } 155 }); 156 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; 170 } 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 }))); 200 } 201 202 bool isResolved = self->mResponsePromises->IsResponseEndPromiseResolved(); 203 if (!isResolved && self->mIsWorkerFetch) { 204 // track only unresolved promises for worker fetch requests 205 // this is needed for clean-up of keepalive requests 206 self->mResponsePromises->GetResponseEndPromise() 207 ->Then( 208 GetMainThreadSerialEventTarget(), __func__, 209 [self](ResponseEndArgs&& aArgs) mutable { 210 AssertIsOnMainThread(); 211 MOZ_ASSERT(self->mPromise); 212 self->mPromise->Resolve(true, __func__); 213 self->mResponsePromises = nullptr; 214 }, 215 [self](CopyableErrorResult&& aErr) mutable { 216 AssertIsOnMainThread(); 217 MOZ_ASSERT(self->mPromise); 218 self->mPromise->Reject(aErr.StealNSResult(), __func__); 219 self->mResponsePromises = nullptr; 220 }) 221 ->Track(fetchService->GetResponseEndPromiseHolder( 222 self->mResponsePromises)); 223 } else { 224 self->mResponsePromises->GetResponseEndPromise()->Then( 225 GetMainThreadSerialEventTarget(), __func__, 226 [self](ResponseEndArgs&& aArgs) mutable { 227 AssertIsOnMainThread(); 228 MOZ_ASSERT(self->mPromise); 229 self->mPromise->Resolve(true, __func__); 230 self->mResponsePromises = nullptr; 231 }, 232 [self](CopyableErrorResult&& aErr) mutable { 233 AssertIsOnMainThread(); 234 MOZ_ASSERT(self->mPromise); 235 self->mPromise->Reject(aErr.StealNSResult(), __func__); 236 self->mResponsePromises = nullptr; 237 }); 238 } 239 }); 240 241 MOZ_ALWAYS_SUCCEEDS( 242 NS_DispatchToMainThread(r.forget(), nsIThread::DISPATCH_NORMAL)); 243 244 return IPC_OK(); 245 } 246 247 IPCResult FetchParent::RecvAbortFetchOp(bool aForceAbort) { 248 FETCH_LOG(("FetchParent::RecvAbortFetchOp [%p]", this)); 249 AssertIsOnBackgroundThread(); 250 251 if (mIsDone) { 252 FETCH_LOG(("FetchParent::RecvAbortFetchOp [%p], Already aborted", this)); 253 return IPC_OK(); 254 } 255 256 if (!aForceAbort && mRequest && mRequest->GetKeepalive()) { 257 // Keeping FetchParent/FetchChild alive for the main-thread keepalive fetch 258 // here is a temporary solution. The cancel logic should always be handled 259 // in FetchInstance::Cancel() once all main-thread fetch routing through 260 // PFetch. 261 if (!mIsWorkerFetch) { 262 FETCH_LOG(("Skip aborting fetch as the request is marked keepalive")); 263 return IPC_OK(); 264 } 265 } else { 266 mIsDone = true; 267 } 268 RefPtr<FetchParent> self = this; 269 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 270 __func__, [self, forceAbort = aForceAbort]() mutable { 271 FETCH_LOG(("FetchParent::RecvAbortFetchOp Runnable")); 272 AssertIsOnMainThread(); 273 if (self->mResponsePromises) { 274 RefPtr<FetchService> fetchService = FetchService::GetInstance(); 275 MOZ_ASSERT(fetchService); 276 fetchService->CancelFetch(std::move(self->mResponsePromises), 277 forceAbort); 278 } 279 }); 280 281 MOZ_ALWAYS_SUCCEEDS( 282 NS_DispatchToMainThread(r.forget(), nsIThread::DISPATCH_NORMAL)); 283 284 return IPC_OK(); 285 } 286 287 void FetchParent::OnResponseAvailableInternal( 288 SafeRefPtr<InternalResponse>&& aResponse) { 289 FETCH_LOG(("FetchParent::OnResponseAvailableInternal [%p]", this)); 290 AssertIsOnBackgroundThread(); 291 MOZ_ASSERT(aResponse); 292 MOZ_ASSERT(!mActorDestroyed); 293 294 if (mIsDone && aResponse->Type() != ResponseType::Error) { 295 FETCH_LOG( 296 ("FetchParent::OnResponseAvailableInternal [%p] " 297 "Fetch has already aborted", 298 this)); 299 return; 300 } 301 302 // To monitor the stream status between processes, response's body can not 303 // be serialized as RemoteLazyInputStream. Such that stream close can be 304 // propagated to FetchDriver in the parent process. 305 aResponse->SetSerializeAsLazy(false); 306 307 // CSP violation notification is asynchronous. Extending the FetchParent's 308 // life cycle for the notificaiton. 309 if (aResponse->Type() == ResponseType::Error && 310 aResponse->GetErrorCode() == NS_ERROR_CONTENT_BLOCKED && 311 mCSPEventListener) { 312 FETCH_LOG( 313 ("FetchParent::OnResponseAvailableInternal [%p] " 314 "NS_ERROR_CONTENT_BLOCKED", 315 this)); 316 mExtendForCSPEventListener = true; 317 } 318 319 (void)SendOnResponseAvailableInternal( 320 aResponse->ToParentToChildInternalResponse()); 321 } 322 323 void FetchParent::OnResponseEnd(const ResponseEndArgs& aArgs) { 324 FETCH_LOG(("FetchParent::OnResponseEnd [%p]", this)); 325 AssertIsOnBackgroundThread(); 326 MOZ_ASSERT(!mActorDestroyed); 327 328 if (mIsDone && aArgs.endReason() != FetchDriverObserver::eAborted) { 329 FETCH_LOG( 330 ("FetchParent::OnResponseEnd [%p] " 331 "Fetch has already aborted", 332 this)); 333 return; 334 } 335 336 (void)SendOnResponseEnd(aArgs); 337 } 338 339 void FetchParent::OnDataAvailable() { 340 FETCH_LOG(("FetchParent::OnDataAvailable [%p]", this)); 341 AssertIsOnBackgroundThread(); 342 MOZ_ASSERT(!mActorDestroyed); 343 344 (void)SendOnDataAvailable(); 345 } 346 347 void FetchParent::OnFlushConsoleReport( 348 const nsTArray<net::ConsoleReportCollected>& aReports) { 349 FETCH_LOG(("FetchParent::OnFlushConsoleReport [%p]", this)); 350 AssertIsOnBackgroundThread(); 351 MOZ_ASSERT(!mActorDestroyed); 352 353 (void)SendOnFlushConsoleReport(aReports); 354 } 355 356 void FetchParent::OnReportPerformanceTiming(const ResponseTiming&& aTiming) { 357 FETCH_LOG(("FetchParent::OnReportPerformanceTiming [%p]", this)); 358 AssertIsOnBackgroundThread(); 359 MOZ_ASSERT(!mActorDestroyed); 360 361 (void)SendOnReportPerformanceTiming(aTiming); 362 } 363 364 void FetchParent::OnNotifyNetworkMonitorAlternateStack(uint64_t aChannelID) { 365 FETCH_LOG(("FetchParent::OnNotifyNetworkMonitorAlternateStack [%p]", this)); 366 AssertIsOnBackgroundThread(); 367 MOZ_ASSERT(!mActorDestroyed); 368 369 (void)SendOnNotifyNetworkMonitorAlternateStack(aChannelID); 370 } 371 372 void FetchParent::ActorDestroy(ActorDestroyReason aReason) { 373 FETCH_LOG(("FetchParent::ActorDestroy [%p]", this)); 374 AssertIsOnBackgroundThread(); 375 mActorDestroyed = true; 376 auto entry = sActorTable.Lookup(mID); 377 if (entry) { 378 entry.Remove(); 379 FETCH_LOG(("FetchParent::ActorDestroy entry [%p] removed", this)); 380 } 381 // mRequest can be null when FetchParent has not yet received RecvFetchOp() 382 if (!mRequest) { 383 return; 384 } 385 386 // Abort the existing fetch. 387 // Actor can be destoried by shutdown when still fetching. 388 RecvAbortFetchOp(false); 389 390 // mBackgroundEventTarget = nullptr; 391 } 392 393 nsICSPEventListener* FetchParent::GetCSPEventListener() { 394 return mCSPEventListener; 395 } 396 397 void FetchParent::OnCSPViolationEvent(const nsAString& aJSON) { 398 FETCH_LOG(("FetchParent::OnCSPViolationEvent [%p]", this)); 399 AssertIsOnBackgroundThread(); 400 MOZ_ASSERT(mHasCSPEventListener); 401 MOZ_ASSERT(!mActorDestroyed); 402 403 (void)SendOnCSPViolationEvent(aJSON); 404 } 405 406 } // namespace mozilla::dom