FetchChild.cpp (16601B)
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 "FetchChild.h" 6 7 #include "FetchLog.h" 8 #include "FetchObserver.h" 9 #include "FetchUtil.h" 10 #include "InternalResponse.h" 11 #include "Request.h" 12 #include "Response.h" 13 #include "mozilla/ConsoleReportCollector.h" 14 #include "mozilla/SchedulerGroup.h" 15 #include "mozilla/dom/PerformanceStorage.h" 16 #include "mozilla/dom/PerformanceTiming.h" 17 #include "mozilla/dom/Promise.h" 18 #include "mozilla/dom/RemoteWorkerChild.h" 19 #include "mozilla/dom/SecurityPolicyViolationEventBinding.h" 20 #include "mozilla/dom/WorkerChannelInfo.h" 21 #include "mozilla/dom/WorkerPrivate.h" 22 #include "mozilla/dom/WorkerRef.h" 23 #include "mozilla/dom/WorkerScope.h" 24 #include "nsIAsyncInputStream.h" 25 #include "nsIGlobalObject.h" 26 #include "nsIObserverService.h" 27 #include "nsIRunnable.h" 28 #include "nsIURI.h" 29 #include "nsNetUtil.h" 30 #include "nsThreadUtils.h" 31 32 namespace mozilla::dom { 33 34 NS_IMPL_ISUPPORTS0(FetchChild) 35 36 mozilla::ipc::IPCResult FetchChild::Recv__delete__(const nsresult&& aResult) { 37 FETCH_LOG(("FetchChild::Recv__delete__ [%p]", this)); 38 if (mIsShutdown) { 39 return IPC_OK(); 40 } 41 // Shutdown has not been called, so mWorkerRef->Private() should be still 42 // alive. 43 if (mWorkerRef) { 44 MOZ_ASSERT(mWorkerRef->Private()); 45 mWorkerRef->Private()->AssertIsOnWorkerThread(); 46 } else { 47 MOZ_ASSERT(mIsKeepAliveRequest); 48 } 49 50 if (mPromise->State() == Promise::PromiseState::Pending) { 51 if (NS_FAILED(aResult)) { 52 mPromise->MaybeReject(aResult); 53 if (mFetchObserver) { 54 mFetchObserver->SetState(FetchState::Errored); 55 } 56 } else { 57 mPromise->MaybeResolve(aResult); 58 if (mFetchObserver) { 59 mFetchObserver->SetState(FetchState::Complete); 60 } 61 } 62 } 63 return IPC_OK(); 64 } 65 66 mozilla::ipc::IPCResult FetchChild::RecvOnResponseAvailableInternal( 67 ParentToChildInternalResponse&& aResponse) { 68 FETCH_LOG(("FetchChild::RecvOnResponseAvailableInternal [%p]", this)); 69 if (mIsShutdown) { 70 return IPC_OK(); 71 } 72 // Shutdown has not been called, so mWorkerRef->Private() should be still 73 // alive. 74 if (mWorkerRef) { 75 MOZ_ASSERT(mWorkerRef->Private()); 76 mWorkerRef->Private()->AssertIsOnWorkerThread(); 77 } 78 79 SafeRefPtr<InternalResponse> internalResponse = 80 InternalResponse::FromIPC(aResponse); 81 IgnoredErrorResult result; 82 internalResponse->Headers()->SetGuard(HeadersGuardEnum::Immutable, result); 83 MOZ_ASSERT(internalResponse); 84 85 if (internalResponse->Type() != ResponseType::Error) { 86 if (internalResponse->Type() == ResponseType::Opaque) { 87 internalResponse->GeneratePaddingInfo(); 88 } 89 90 if (mFetchObserver) { 91 mFetchObserver->SetState(FetchState::Complete); 92 } 93 94 // mFetchObserver->SetState runs JS and a blocking JS function can run 95 // queued runnables, including ActorDestroy that nullifies mPromise. 96 if (!mPromise) { 97 return IPC_OK(); 98 } 99 nsCOMPtr<nsIGlobalObject> global; 100 global = mPromise->GetGlobalObject(); 101 RefPtr<Response> response = 102 new Response(global, internalResponse.clonePtr(), mSignalImpl); 103 mPromise->MaybeResolve(response); 104 105 return IPC_OK(); 106 } 107 108 FETCH_LOG( 109 ("FetchChild::RecvOnResponseAvailableInternal [%p] response type is " 110 "Error(0x%x)", 111 this, static_cast<int32_t>(internalResponse->GetErrorCode()))); 112 if (mFetchObserver) { 113 mFetchObserver->SetState(FetchState::Errored); 114 } 115 116 // mFetchObserver->SetState runs JS and a blocking JS function can run queued 117 // runnables, including ActorDestroy that nullifies mPromise. 118 if (!mPromise) { 119 return IPC_OK(); 120 } 121 mPromise->MaybeRejectWithTypeError<MSG_FETCH_FAILED>(); 122 return IPC_OK(); 123 } 124 125 mozilla::ipc::IPCResult FetchChild::RecvOnResponseEnd(ResponseEndArgs&& aArgs) { 126 FETCH_LOG(("FetchChild::RecvOnResponseEnd [%p]", this)); 127 if (mIsShutdown) { 128 return IPC_OK(); 129 } 130 // Shutdown has not been called, so mWorkerRef->Private() should be still 131 // alive. 132 if (mWorkerRef) { 133 MOZ_ASSERT(mWorkerRef->Private()); 134 mWorkerRef->Private()->AssertIsOnWorkerThread(); 135 } 136 137 if (aArgs.endReason() == FetchDriverObserver::eAborted) { 138 FETCH_LOG( 139 ("FetchChild::RecvOnResponseEnd [%p] endReason is eAborted", this)); 140 if (mFetchObserver) { 141 mFetchObserver->SetState(FetchState::Errored); 142 } 143 144 // mFetchObserver->SetState runs JS and a blocking JS function can run 145 // queued runnables, including ActorDestroy that nullifies mPromise. 146 if (!mPromise) { 147 return IPC_OK(); 148 } 149 mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); 150 } 151 152 Unfollow(); 153 return IPC_OK(); 154 } 155 156 mozilla::ipc::IPCResult FetchChild::RecvOnDataAvailable() { 157 FETCH_LOG(("FetchChild::RecvOnDataAvailable [%p]", this)); 158 if (mIsShutdown) { 159 return IPC_OK(); 160 } 161 // Shutdown has not been called, so mWorkerRef->Private() should be still 162 // alive. 163 if (mWorkerRef) { 164 MOZ_ASSERT(mWorkerRef->Private()); 165 mWorkerRef->Private()->AssertIsOnWorkerThread(); 166 } 167 168 if (mFetchObserver && mFetchObserver->State() == FetchState::Requesting) { 169 mFetchObserver->SetState(FetchState::Responding); 170 } 171 return IPC_OK(); 172 } 173 174 mozilla::ipc::IPCResult FetchChild::RecvOnFlushConsoleReport( 175 nsTArray<net::ConsoleReportCollected>&& aReports) { 176 FETCH_LOG(("FetchChild::RecvOnFlushConsoleReport [%p]", this)); 177 if (mIsShutdown) { 178 return IPC_OK(); 179 } 180 MOZ_ASSERT(mReporter); 181 182 if (NS_IsMainThread()) { 183 MOZ_ASSERT(mIsKeepAliveRequest); 184 // extract doc object to flush the console report 185 for (const auto& report : aReports) { 186 mReporter->AddConsoleReport( 187 report.errorFlags(), report.category(), 188 static_cast<nsContentUtils::PropertiesFile>(report.propertiesFile()), 189 report.sourceFileURI(), report.lineNumber(), report.columnNumber(), 190 report.messageName(), report.stringParams()); 191 } 192 193 MOZ_ASSERT(mPromise); 194 nsCOMPtr<nsPIDOMWindowInner> window = 195 do_QueryInterface(mPromise->GetGlobalObject()); 196 if (window) { 197 Document* doc = window->GetExtantDoc(); 198 mReporter->FlushConsoleReports(doc); 199 } else { 200 mReporter->FlushReportsToConsole(0); 201 } 202 return IPC_OK(); 203 } 204 // Shutdown has not been called, so mWorkerRef->Private() should be still 205 // alive. 206 if (mWorkerRef) { 207 MOZ_ASSERT(mWorkerRef->Private()); 208 mWorkerRef->Private()->AssertIsOnWorkerThread(); 209 } 210 211 RefPtr<ThreadSafeWorkerRef> workerRef = mWorkerRef; 212 nsCOMPtr<nsIConsoleReportCollector> reporter = mReporter; 213 214 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 215 __func__, [reports = std::move(aReports), reporter = std::move(reporter), 216 workerRef = std::move(workerRef)]() mutable { 217 for (const auto& report : reports) { 218 reporter->AddConsoleReport( 219 report.errorFlags(), report.category(), 220 static_cast<nsContentUtils::PropertiesFile>( 221 report.propertiesFile()), 222 report.sourceFileURI(), report.lineNumber(), 223 report.columnNumber(), report.messageName(), 224 report.stringParams()); 225 } 226 227 if (workerRef->Private()->IsServiceWorker()) { 228 reporter->FlushReportsToConsoleForServiceWorkerScope( 229 workerRef->Private()->ServiceWorkerScope()); 230 } 231 232 if (workerRef->Private()->IsSharedWorker()) { 233 workerRef->Private() 234 ->GetRemoteWorkerController() 235 ->FlushReportsOnMainThread(reporter); 236 } 237 238 reporter->FlushConsoleReports(workerRef->Private()->GetLoadGroup()); 239 }); 240 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 241 242 return IPC_OK(); 243 } 244 245 RefPtr<FetchChild> FetchChild::CreateForWorker( 246 WorkerPrivate* aWorkerPrivate, RefPtr<Promise> aPromise, 247 RefPtr<AbortSignalImpl> aSignalImpl, RefPtr<FetchObserver> aObserver) { 248 MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate); 249 aWorkerPrivate->AssertIsOnWorkerThread(); 250 FETCH_LOG(("FetchChild::CreateForWorker [%p]", aWorkerPrivate)); 251 252 RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>( 253 std::move(aPromise), std::move(aSignalImpl), std::move(aObserver)); 254 255 RefPtr<StrongWorkerRef> workerRef = 256 StrongWorkerRef::Create(aWorkerPrivate, "FetchChild", [actor]() { 257 FETCH_LOG(("StrongWorkerRef callback")); 258 actor->Shutdown(); 259 }); 260 if (NS_WARN_IF(!workerRef)) { 261 return nullptr; 262 } 263 264 actor->mWorkerRef = new ThreadSafeWorkerRef(workerRef); 265 if (NS_WARN_IF(!actor->mWorkerRef)) { 266 return nullptr; 267 } 268 return actor; 269 } 270 271 RefPtr<FetchChild> FetchChild::CreateForMainThread( 272 RefPtr<Promise> aPromise, RefPtr<AbortSignalImpl> aSignalImpl, 273 RefPtr<FetchObserver> aObserver) { 274 RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>( 275 std::move(aPromise), std::move(aSignalImpl), std::move(aObserver)); 276 FETCH_LOG(("FetchChild::CreateForMainThread actor[%p]", actor.get())); 277 278 return actor; 279 } 280 281 mozilla::ipc::IPCResult FetchChild::RecvOnCSPViolationEvent( 282 const nsAString& aJSON) { 283 FETCH_LOG(("FetchChild::RecvOnCSPViolationEvent [%p] aJSON: %s\n", this, 284 NS_ConvertUTF16toUTF8(aJSON).BeginReading())); 285 286 nsString JSON(aJSON); 287 288 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [JSON]() mutable { 289 SecurityPolicyViolationEventInit violationEventInit; 290 if (NS_WARN_IF(!violationEventInit.Init(JSON))) { 291 return; 292 } 293 294 nsCOMPtr<nsIURI> uri; 295 nsresult rv = 296 NS_NewURI(getter_AddRefs(uri), violationEventInit.mBlockedURI); 297 if (NS_WARN_IF(NS_FAILED(rv))) { 298 return; 299 } 300 301 nsCOMPtr<nsIObserverService> observerService = 302 mozilla::services::GetObserverService(); 303 if (!observerService) { 304 return; 305 } 306 307 rv = observerService->NotifyObservers( 308 uri, CSP_VIOLATION_TOPIC, violationEventInit.mViolatedDirective.get()); 309 if (NS_WARN_IF(NS_FAILED(rv))) { 310 return; 311 } 312 }); 313 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 314 315 if (mCSPEventListener) { 316 (void)NS_WARN_IF(NS_FAILED(mCSPEventListener->OnCSPViolationEvent(aJSON))); 317 } 318 return IPC_OK(); 319 } 320 321 mozilla::ipc::IPCResult FetchChild::RecvOnReportPerformanceTiming( 322 ResponseTiming&& aTiming) { 323 FETCH_LOG(("FetchChild::RecvOnReportPerformanceTiming [%p]", this)); 324 if (mIsShutdown) { 325 return IPC_OK(); 326 } 327 // Shutdown has not been called, so mWorkerRef->Private() should be still 328 // alive. 329 if (mWorkerRef) { 330 MOZ_ASSERT(mWorkerRef->Private()); 331 mWorkerRef->Private()->AssertIsOnWorkerThread(); 332 333 RefPtr<PerformanceStorage> performanceStorage = 334 mWorkerRef->Private()->GetPerformanceStorage(); 335 if (performanceStorage) { 336 performanceStorage->AddEntry( 337 aTiming.entryName(), aTiming.initiatorType(), 338 MakeUnique<PerformanceTimingData>(aTiming.timingData())); 339 } 340 } else if (mIsKeepAliveRequest) { 341 MOZ_ASSERT(mPromise->GetGlobalObject()); 342 auto* innerWindow = mPromise->GetGlobalObject()->GetAsInnerWindow(); 343 if (innerWindow) { 344 mozilla::dom::Performance* performance = innerWindow->GetPerformance(); 345 if (performance) { 346 performance->AsPerformanceStorage()->AddEntry( 347 aTiming.entryName(), aTiming.initiatorType(), 348 MakeUnique<PerformanceTimingData>(aTiming.timingData())); 349 } 350 } 351 } 352 return IPC_OK(); 353 } 354 355 mozilla::ipc::IPCResult FetchChild::RecvOnNotifyNetworkMonitorAlternateStack( 356 uint64_t aChannelID) { 357 FETCH_LOG( 358 ("FetchChild::RecvOnNotifyNetworkMonitorAlternateStack [%p]", this)); 359 if (mIsShutdown) { 360 return IPC_OK(); 361 } 362 // Shutdown has not been called, so mWorkerRef->Private() should be still 363 // alive. 364 if (mWorkerRef) { 365 MOZ_ASSERT(mWorkerRef->Private()); 366 mWorkerRef->Private()->AssertIsOnWorkerThread(); 367 368 if (!mOriginStack) { 369 return IPC_OK(); 370 } 371 372 if (!mWorkerChannelInfo) { 373 mWorkerChannelInfo = MakeRefPtr<WorkerChannelInfo>( 374 aChannelID, mWorkerRef->Private()->AssociatedBrowsingContextID()); 375 } 376 377 // Unfortunately, SerializedStackHolder can only be read on the main thread. 378 // However, it doesn't block the fetch execution. 379 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 380 __func__, [channel = mWorkerChannelInfo, 381 stack = std::move(mOriginStack)]() mutable { 382 NotifyNetworkMonitorAlternateStack(channel, std::move(stack)); 383 }); 384 385 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 386 } else { 387 // Handle main-thread fetch requests 388 if (!mOriginStack) { 389 return IPC_OK(); 390 } 391 392 if (!mWorkerChannelInfo) { 393 // Get browsing context from the promise's global object 394 uint64_t browsingContextID = 0; 395 if (mPromise && mPromise->GetGlobalObject()) { 396 if (auto* innerWindow = 397 mPromise->GetGlobalObject()->GetAsInnerWindow()) { 398 if (auto* browsingContext = innerWindow->GetBrowsingContext()) { 399 browsingContextID = browsingContext->Id(); 400 } 401 } 402 } 403 if (browsingContextID == 0) { 404 FETCH_LOG( 405 ("FetchChild::RecvOnNotifyNetworkMonitorAlternateStack: unable to " 406 "get browsingContextID for main-thread fetch, channelID=%" PRIu64, 407 aChannelID)); 408 } 409 mWorkerChannelInfo = 410 MakeRefPtr<WorkerChannelInfo>(aChannelID, browsingContextID); 411 } 412 413 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 414 __func__, [channel = mWorkerChannelInfo, 415 stack = std::move(mOriginStack)]() mutable { 416 NotifyNetworkMonitorAlternateStack(channel, std::move(stack)); 417 }); 418 419 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 420 } 421 422 return IPC_OK(); 423 } 424 425 void FetchChild::SetCSPEventListener(nsICSPEventListener* aListener) { 426 MOZ_ASSERT(aListener && !mCSPEventListener); 427 mCSPEventListener = aListener; 428 } 429 430 FetchChild::FetchChild(RefPtr<Promise>&& aPromise, 431 RefPtr<AbortSignalImpl>&& aSignalImpl, 432 RefPtr<FetchObserver>&& aObserver) 433 : mPromise(std::move(aPromise)), 434 mSignalImpl(std::move(aSignalImpl)), 435 mFetchObserver(std::move(aObserver)), 436 mReporter(new ConsoleReportCollector()) { 437 FETCH_LOG(("FetchChild::FetchChild [%p]", this)); 438 } 439 440 void FetchChild::RunAbortAlgorithm() { 441 FETCH_LOG(("FetchChild::RunAbortAlgorithm [%p]", this)); 442 if (mIsShutdown) { 443 return; 444 } 445 if (mWorkerRef || mIsKeepAliveRequest) { 446 (void)SendAbortFetchOp(true); 447 } 448 } 449 450 void FetchChild::DoFetchOp(const FetchOpArgs& aArgs) { 451 FETCH_LOG(("FetchChild::DoFetchOp [%p]", this)); 452 // we need to store this for keepalive request 453 // as we need to update the load group during actor termination 454 mIsKeepAliveRequest = aArgs.request().keepalive(); 455 if (mIsKeepAliveRequest) { 456 mKeepaliveRequestSize = 457 aArgs.request().bodySize() > 0 ? aArgs.request().bodySize() : 0; 458 } 459 if (mSignalImpl) { 460 if (mSignalImpl->Aborted()) { 461 (void)SendAbortFetchOp(true); 462 return; 463 } 464 Follow(mSignalImpl); 465 } 466 (void)SendFetchOp(aArgs); 467 } 468 469 void FetchChild::Shutdown() { 470 // This is invoked for worker fetch requests only. 471 // We need to modify this to be invoked for main-thread fetch requests as 472 // well. Typically during global teardown. See Bug 1901082 473 474 FETCH_LOG(("FetchChild::Shutdown [%p]", this)); 475 if (mIsShutdown) { 476 return; 477 } 478 mIsShutdown.Flip(); 479 480 // If mWorkerRef is nullptr here, that means Recv__delete__() must be called 481 if (!mWorkerRef) { 482 return; 483 } 484 mPromise = nullptr; 485 mFetchObserver = nullptr; 486 Unfollow(); 487 mSignalImpl = nullptr; 488 mCSPEventListener = nullptr; 489 SendAbortFetchOp(false); 490 491 mWorkerRef = nullptr; 492 } 493 494 void FetchChild::ActorDestroy(ActorDestroyReason aReason) { 495 FETCH_LOG(("FetchChild::ActorDestroy [%p]", this)); 496 // for keepalive request decrement the pending keepalive count 497 if (mIsKeepAliveRequest) { 498 // For workers we do not have limit per load group rather we have limit 499 // per request 500 if (NS_IsMainThread()) { 501 MOZ_ASSERT(mPromise->GetGlobalObject()); 502 nsCOMPtr<nsILoadGroup> loadGroup = 503 FetchUtil::GetLoadGroupFromGlobal(mPromise->GetGlobalObject()); 504 if (loadGroup) { 505 FetchUtil::DecrementPendingKeepaliveRequestSize(loadGroup, 506 mKeepaliveRequestSize); 507 } 508 } 509 } 510 mPromise = nullptr; 511 mFetchObserver = nullptr; 512 mSignalImpl = nullptr; 513 mCSPEventListener = nullptr; 514 mWorkerRef = nullptr; 515 } 516 517 } // namespace mozilla::dom