WorkerPrivate.cpp (225608B)
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 "WorkerPrivate.h" 8 9 #include <utility> 10 11 #include "MessageEventRunnable.h" 12 #include "RuntimeService.h" 13 #include "ScriptLoader.h" 14 #include "WorkerCSPEventListener.h" 15 #include "WorkerDebugger.h" 16 #include "WorkerDebuggerManager.h" 17 #include "WorkerError.h" 18 #include "WorkerEventTarget.h" 19 #include "WorkerNavigator.h" 20 #include "WorkerRef.h" 21 #include "WorkerRunnable.h" 22 #include "WorkerThread.h" 23 #include "js/CallAndConstruct.h" // JS_CallFunctionValue 24 #include "js/CompilationAndEvaluation.h" 25 #include "js/ContextOptions.h" 26 #include "js/Exception.h" 27 #include "js/LocaleSensitive.h" 28 #include "js/MemoryMetrics.h" 29 #include "js/SourceText.h" 30 #include "js/friend/ErrorMessages.h" // JSMSG_OUT_OF_MEMORY 31 #include "js/friend/MicroTask.h" 32 #include "mozilla/AntiTrackingUtils.h" 33 #include "mozilla/BasePrincipal.h" 34 #include "mozilla/CycleCollectedJSContext.h" 35 #include "mozilla/ExtensionPolicyService.h" 36 #include "mozilla/Mutex.h" 37 #include "mozilla/ProfilerLabels.h" 38 #include "mozilla/ProfilerMarkers.h" 39 #include "mozilla/Result.h" 40 #include "mozilla/ScopeExit.h" 41 #include "mozilla/StaticPrefs_browser.h" 42 #include "mozilla/StaticPrefs_dom.h" 43 #include "mozilla/StaticPrefs_javascript.h" 44 #include "mozilla/StorageAccess.h" 45 #include "mozilla/StoragePrincipalHelper.h" 46 #include "mozilla/ThreadEventQueue.h" 47 #include "mozilla/ThreadSafety.h" 48 #include "mozilla/ThrottledEventQueue.h" 49 #include "mozilla/dom/BrowsingContextGroup.h" 50 #include "mozilla/dom/CallbackDebuggerNotification.h" 51 #include "mozilla/dom/ClientManager.h" 52 #include "mozilla/dom/ClientState.h" 53 #include "mozilla/dom/Console.h" 54 #include "mozilla/dom/ContentChild.h" 55 #include "mozilla/dom/DOMTypes.h" 56 #include "mozilla/dom/DocGroup.h" 57 #include "mozilla/dom/Document.h" 58 #include "mozilla/dom/DocumentInlines.h" 59 #include "mozilla/dom/Event.h" 60 #include "mozilla/dom/Exceptions.h" 61 #include "mozilla/dom/FunctionBinding.h" 62 #include "mozilla/dom/IndexedDatabaseManager.h" 63 #include "mozilla/dom/JSExecutionManager.h" 64 #include "mozilla/dom/MessageEvent.h" 65 #include "mozilla/dom/MessageEventBinding.h" 66 #include "mozilla/dom/MessagePort.h" 67 #include "mozilla/dom/MessagePortBinding.h" 68 #include "mozilla/dom/PRemoteWorkerDebuggerParent.h" 69 #include "mozilla/dom/Performance.h" 70 #include "mozilla/dom/PerformanceStorageWorker.h" 71 #include "mozilla/dom/PolicyContainer.h" 72 #include "mozilla/dom/PromiseDebugging.h" 73 #include "mozilla/dom/ReferrerInfo.h" 74 #include "mozilla/dom/RemoteWorkerChild.h" 75 #include "mozilla/dom/RemoteWorkerDebuggerChild.h" 76 #include "mozilla/dom/RemoteWorkerNonLifeCycleOpControllerChild.h" 77 #include "mozilla/dom/RemoteWorkerService.h" 78 #include "mozilla/dom/RootedDictionary.h" 79 #include "mozilla/dom/ServiceWorkerEvents.h" 80 #include "mozilla/dom/ServiceWorkerManager.h" 81 #include "mozilla/dom/SimpleGlobalObject.h" 82 #include "mozilla/dom/TimeoutHandler.h" 83 #include "mozilla/dom/TimeoutManager.h" 84 #include "mozilla/dom/UseCounterMetrics.h" 85 #include "mozilla/dom/WebTaskScheduler.h" 86 #include "mozilla/dom/WindowContext.h" 87 #include "mozilla/dom/WorkerBinding.h" 88 #include "mozilla/dom/WorkerScope.h" 89 #include "mozilla/dom/WorkerStatus.h" 90 #include "mozilla/dom/nsCSPContext.h" 91 #include "mozilla/dom/nsCSPUtils.h" 92 #include "mozilla/extensions/ExtensionBrowser.h" // extensions::Create{AndDispatchInitWorkerContext,WorkerLoaded,WorkerDestroyed}Runnable 93 #include "mozilla/extensions/WebExtensionPolicy.h" 94 #include "mozilla/glean/DomUseCounterMetrics.h" 95 #include "mozilla/ipc/BackgroundChild.h" 96 #include "mozilla/ipc/PBackgroundChild.h" 97 #include "mozilla/net/CookieJarSettings.h" 98 #include "nsContentSecurityManager.h" 99 #include "nsCycleCollector.h" 100 #include "nsGlobalWindowInner.h" 101 #include "nsIDUtils.h" 102 #include "nsIEventTarget.h" 103 #include "nsIFile.h" 104 #include "nsIMemoryReporter.h" 105 #include "nsIPermissionManager.h" 106 #include "nsIProtocolHandler.h" 107 #include "nsIScriptError.h" 108 #include "nsIURI.h" 109 #include "nsIURL.h" 110 #include "nsIUUIDGenerator.h" 111 #include "nsNetUtil.h" 112 #include "nsPresContext.h" 113 #include "nsPrintfCString.h" 114 #include "nsProxyRelease.h" 115 #include "nsQueryObject.h" 116 #include "nsRFPService.h" 117 #include "nsSandboxFlags.h" 118 #include "nsThreadManager.h" 119 #include "nsThreadUtils.h" 120 #include "nsUTF8Utils.h" 121 122 #ifdef XP_WIN 123 # undef PostMessage 124 #endif 125 126 // JS_MaybeGC will run once every second during normal execution. 127 #define PERIODIC_GC_TIMER_DELAY_SEC 1 128 129 // A shrinking GC will run five seconds after the last event is processed. 130 #define IDLE_GC_TIMER_DELAY_SEC 5 131 132 // Arbitrary short grace period for the currently running task to finish. 133 // There isn't an advantage for us to immediately interrupt JS in the middle of 134 // execution that might yield soon, especially when there is so much async 135 // variability in the data flow prior to us deciding to trigger the interrupt. 136 #define DEBUGGER_RUNNABLE_INTERRUPT_AFTER_MS 250 137 138 static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate"); 139 static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts"); 140 static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection"); 141 142 mozilla::LogModule* WorkerLog() { return sWorkerPrivateLog; } 143 144 mozilla::LogModule* TimeoutsLog() { return sWorkerTimeoutsLog; } 145 146 #ifdef LOG 147 # undef LOG 148 #endif 149 #ifdef LOGV 150 # undef LOGV 151 #endif 152 #define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args); 153 #define LOGV(args) MOZ_LOG(sWorkerPrivateLog, LogLevel::Verbose, args); 154 155 namespace mozilla { 156 157 using namespace ipc; 158 159 namespace dom { 160 161 using namespace workerinternals; 162 163 MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf) 164 165 namespace { 166 167 #ifdef DEBUG 168 169 const nsIID kDEBUGWorkerEventTargetIID = { 170 0xccaba3fa, 171 0x5be2, 172 0x4de2, 173 {0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb}}; 174 175 #endif 176 177 template <class T> 178 class UniquePtrComparator { 179 using A = UniquePtr<T>; 180 using B = T*; 181 182 public: 183 bool Equals(const A& a, const A& b) const { 184 return (a && b) ? (*a == *b) : (!a && !b); 185 } 186 bool LessThan(const A& a, const A& b) const { 187 return (a && b) ? (*a < *b) : !!b; 188 } 189 }; 190 191 template <class T> 192 inline UniquePtrComparator<T> GetUniquePtrComparator( 193 const nsTArray<UniquePtr<T>>&) { 194 return UniquePtrComparator<T>(); 195 } 196 197 // This class is used to wrap any runnables that the worker receives via the 198 // nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or 199 // from the worker's EventTarget). 200 class ExternalRunnableWrapper final : public WorkerThreadRunnable { 201 nsCOMPtr<nsIRunnable> mWrappedRunnable; 202 203 public: 204 ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate, 205 nsIRunnable* aWrappedRunnable) 206 : WorkerThreadRunnable("ExternalRunnableWrapper"), 207 mWrappedRunnable(aWrappedRunnable) { 208 MOZ_ASSERT(aWorkerPrivate); 209 MOZ_ASSERT(aWrappedRunnable); 210 } 211 212 NS_INLINE_DECL_REFCOUNTING_INHERITED(ExternalRunnableWrapper, 213 WorkerThreadRunnable) 214 215 private: 216 ~ExternalRunnableWrapper() = default; 217 218 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { 219 // Silence bad assertions. 220 return true; 221 } 222 223 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, 224 bool aDispatchResult) override { 225 // Silence bad assertions. 226 } 227 228 virtual bool WorkerRun(JSContext* aCx, 229 WorkerPrivate* aWorkerPrivate) override { 230 nsresult rv = mWrappedRunnable->Run(); 231 mWrappedRunnable = nullptr; 232 if (NS_FAILED(rv)) { 233 if (!JS_IsExceptionPending(aCx)) { 234 Throw(aCx, rv); 235 } 236 return false; 237 } 238 return true; 239 } 240 241 nsresult Cancel() override { 242 nsCOMPtr<nsIDiscardableRunnable> doomed = 243 do_QueryInterface(mWrappedRunnable); 244 if (doomed) { 245 doomed->OnDiscard(); 246 } 247 mWrappedRunnable = nullptr; 248 return NS_OK; 249 } 250 251 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY 252 NS_IMETHOD GetName(nsACString& aName) override { 253 aName.AssignLiteral("ExternalRunnableWrapper("); 254 if (nsCOMPtr<nsINamed> named = do_QueryInterface(mWrappedRunnable)) { 255 nsAutoCString containedName; 256 named->GetName(containedName); 257 aName.Append(containedName); 258 } else { 259 aName.AppendLiteral("?"); 260 } 261 aName.AppendLiteral(")"); 262 return NS_OK; 263 } 264 #endif 265 }; 266 267 struct WindowAction { 268 nsPIDOMWindowInner* mWindow; 269 bool mDefaultAction; 270 271 MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow) 272 : mWindow(aWindow), mDefaultAction(true) {} 273 274 bool operator==(const WindowAction& aOther) const { 275 return mWindow == aOther.mWindow; 276 } 277 }; 278 279 class WorkerFinishedRunnable final : public WorkerControlRunnable { 280 WorkerPrivate* mFinishedWorker; 281 282 public: 283 WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate, 284 WorkerPrivate* aFinishedWorker) 285 : WorkerControlRunnable("WorkerFinishedRunnable"), 286 mFinishedWorker(aFinishedWorker) { 287 aFinishedWorker->IncreaseWorkerFinishedRunnableCount(); 288 } 289 290 private: 291 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { 292 // Silence bad assertions. 293 return true; 294 } 295 296 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, 297 bool aDispatchResult) override { 298 // Silence bad assertions. 299 } 300 301 virtual bool WorkerRun(JSContext* aCx, 302 WorkerPrivate* aWorkerPrivate) override { 303 // This may block on the main thread. 304 AutoYieldJSThreadExecution yield; 305 306 mFinishedWorker->DecreaseWorkerFinishedRunnableCount(); 307 308 if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) { 309 NS_WARNING("Failed to dispatch, going to leak!"); 310 } 311 312 RuntimeService* runtime = RuntimeService::GetService(); 313 NS_ASSERTION(runtime, "This should never be null!"); 314 315 mFinishedWorker->DisableDebugger(); 316 317 runtime->UnregisterWorker(*mFinishedWorker); 318 319 mFinishedWorker->ClearSelfAndParentEventTargetRef(); 320 return true; 321 } 322 }; 323 324 class TopLevelWorkerFinishedRunnable final : public Runnable { 325 WorkerPrivate* mFinishedWorker; 326 327 public: 328 explicit TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker) 329 : mozilla::Runnable("TopLevelWorkerFinishedRunnable"), 330 mFinishedWorker(aFinishedWorker) { 331 aFinishedWorker->AssertIsOnWorkerThread(); 332 aFinishedWorker->IncreaseTopLevelWorkerFinishedRunnableCount(); 333 } 334 335 NS_INLINE_DECL_REFCOUNTING_INHERITED(TopLevelWorkerFinishedRunnable, Runnable) 336 337 private: 338 ~TopLevelWorkerFinishedRunnable() = default; 339 340 NS_IMETHOD 341 Run() override { 342 AssertIsOnMainThread(); 343 344 mFinishedWorker->DecreaseTopLevelWorkerFinishedRunnableCount(); 345 346 RuntimeService* runtime = RuntimeService::GetService(); 347 MOZ_ASSERT(runtime); 348 349 mFinishedWorker->DisableDebugger(); 350 351 runtime->UnregisterWorker(*mFinishedWorker); 352 353 if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) { 354 NS_WARNING("Failed to dispatch, going to leak!"); 355 } 356 357 mFinishedWorker->ClearSelfAndParentEventTargetRef(); 358 return NS_OK; 359 } 360 }; 361 362 class CompileScriptRunnable final : public WorkerDebuggeeRunnable { 363 nsString mScriptURL; 364 const mozilla::Encoding* mDocumentEncoding; 365 UniquePtr<SerializedStackHolder> mOriginStack; 366 367 public: 368 explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate, 369 UniquePtr<SerializedStackHolder> aOriginStack, 370 const nsAString& aScriptURL, 371 const mozilla::Encoding* aDocumentEncoding) 372 : WorkerDebuggeeRunnable("CompileScriptRunnable"), 373 mScriptURL(aScriptURL), 374 mDocumentEncoding(aDocumentEncoding), 375 mOriginStack(aOriginStack.release()) {} 376 377 private: 378 // We can't implement PreRun effectively, because at the point when that would 379 // run we have not yet done our load so don't know things like our final 380 // principal and whatnot. 381 382 virtual bool WorkerRun(JSContext* aCx, 383 WorkerPrivate* aWorkerPrivate) override { 384 aWorkerPrivate->AssertIsOnWorkerThread(); 385 386 WorkerGlobalScope* globalScope = 387 aWorkerPrivate->GetOrCreateGlobalScope(aCx); 388 if (NS_WARN_IF(!globalScope)) { 389 return false; 390 } 391 392 if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) { 393 return false; 394 } 395 396 ErrorResult rv; 397 workerinternals::LoadMainScript(aWorkerPrivate, std::move(mOriginStack), 398 mScriptURL, WorkerScript, rv, 399 mDocumentEncoding); 400 401 if (aWorkerPrivate->ExtensionAPIAllowed()) { 402 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 403 RefPtr<Runnable> extWorkerRunnable = 404 extensions::CreateWorkerLoadedRunnable( 405 aWorkerPrivate->ServiceWorkerID(), aWorkerPrivate->GetBaseURI()); 406 // Dispatch as a low priority runnable. 407 if (NS_FAILED(aWorkerPrivate->DispatchToMainThreadForMessaging( 408 extWorkerRunnable.forget()))) { 409 NS_WARNING( 410 "Failed to dispatch runnable to notify extensions worker loaded"); 411 } 412 } 413 414 rv.WouldReportJSException(); 415 // Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still 416 // return false and don't SetWorkerScriptExecutedSuccessfully() in that 417 // case, but don't throw anything on aCx. The idea is to not dispatch error 418 // events if our load is canceled with that error code. 419 if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) { 420 rv.SuppressException(); 421 return false; 422 } 423 424 // Make sure to propagate exceptions from rv onto aCx, so that they will get 425 // reported after we return. We want to propagate just JS exceptions, 426 // because all the other errors are handled when the script is loaded. 427 // See: https://dom.spec.whatwg.org/#concept-event-fire 428 if (rv.Failed() && !rv.IsJSException()) { 429 WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent( 430 aWorkerPrivate); 431 rv.SuppressException(); 432 return false; 433 } 434 435 // This is a little dumb, but aCx is in the null realm here because we 436 // set it up that way in our Run(), since we had not created the global at 437 // that point yet. So we need to enter the realm of our global, 438 // because setting a pending exception on aCx involves wrapping into its 439 // current compartment. Luckily we have a global now. 440 JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject()); 441 if (rv.MaybeSetPendingException(aCx)) { 442 // In the event of an uncaught exception, the worker should still keep 443 // running (return true) but should not be marked as having executed 444 // successfully (which will cause ServiceWorker installation to fail). 445 // In previous error handling cases in this method, we return false (to 446 // trigger CloseInternal) because the global is not in an operable 447 // state at all. 448 // 449 // For ServiceWorkers, this would correspond to the "Run Service Worker" 450 // algorithm returning an "abrupt completion" and _not_ failure. 451 // 452 // For DedicatedWorkers and SharedWorkers, this would correspond to the 453 // "run a worker" algorithm disregarding the return value of "run the 454 // classic script"/"run the module script" in step 24: 455 // 456 // "If script is a classic script, then run the classic script script. 457 // Otherwise, it is a module script; run the module script script." 458 return true; 459 } 460 461 aWorkerPrivate->SetWorkerScriptExecutedSuccessfully(); 462 return true; 463 } 464 465 void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 466 bool aRunResult) override { 467 if (!aRunResult) { 468 aWorkerPrivate->CloseInternal(); 469 } 470 WorkerThreadRunnable::PostRun(aCx, aWorkerPrivate, aRunResult); 471 } 472 }; 473 474 class NotifyRunnable final : public WorkerControlRunnable { 475 WorkerStatus mStatus; 476 477 public: 478 NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus) 479 : WorkerControlRunnable("NotifyRunnable"), mStatus(aStatus) { 480 MOZ_ASSERT(aStatus == Closing || aStatus == Canceling || 481 aStatus == Killing); 482 } 483 484 private: 485 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { 486 aWorkerPrivate->AssertIsOnParentThread(); 487 return true; 488 } 489 490 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, 491 bool aDispatchResult) override { 492 aWorkerPrivate->AssertIsOnParentThread(); 493 } 494 495 virtual bool WorkerRun(JSContext* aCx, 496 WorkerPrivate* aWorkerPrivate) override { 497 return aWorkerPrivate->NotifyInternal(mStatus); 498 } 499 500 virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 501 bool aRunResult) override {} 502 }; 503 504 class FreezeRunnable final : public WorkerControlRunnable { 505 public: 506 explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate) 507 : WorkerControlRunnable("FreezeRunnable") {} 508 509 private: 510 virtual bool WorkerRun(JSContext* aCx, 511 WorkerPrivate* aWorkerPrivate) override { 512 return aWorkerPrivate->FreezeInternal(); 513 } 514 }; 515 516 class ThawRunnable final : public WorkerControlRunnable { 517 public: 518 explicit ThawRunnable(WorkerPrivate* aWorkerPrivate) 519 : WorkerControlRunnable("ThawRunnable") {} 520 521 private: 522 virtual bool WorkerRun(JSContext* aCx, 523 WorkerPrivate* aWorkerPrivate) override { 524 return aWorkerPrivate->ThawInternal(); 525 } 526 }; 527 528 class ChangeBackgroundStateRunnable final : public WorkerControlRunnable { 529 public: 530 ChangeBackgroundStateRunnable() = delete; 531 explicit ChangeBackgroundStateRunnable(WorkerPrivate* aWorkerPrivate) = 532 delete; 533 ChangeBackgroundStateRunnable(WorkerPrivate* aWorkerPrivate, 534 bool aIsBackground) 535 : WorkerControlRunnable("ChangeBackgroundStateRunnable"), 536 mIsBackground(aIsBackground) {} 537 538 private: 539 bool mIsBackground = false; 540 virtual bool WorkerRun(JSContext* aCx, 541 WorkerPrivate* aWorkerPrivate) override { 542 return aWorkerPrivate->ChangeBackgroundStateInternal(mIsBackground); 543 } 544 }; 545 546 class ChangePlaybackStateRunnable final : public WorkerControlRunnable { 547 public: 548 ChangePlaybackStateRunnable() = delete; 549 explicit ChangePlaybackStateRunnable(WorkerPrivate* aWorkerPrivate) = delete; 550 ChangePlaybackStateRunnable(WorkerPrivate* aWorkerPrivate, 551 bool aIsPlayingAudio) 552 : WorkerControlRunnable("ChangePlaybackStateRunnable"), 553 mIsPlayingAudio(aIsPlayingAudio) {} 554 555 private: 556 virtual bool WorkerRun(JSContext* aCx, 557 WorkerPrivate* aWorkerPrivate) override { 558 return aWorkerPrivate->ChangePlaybackStateInternal(mIsPlayingAudio); 559 } 560 bool mIsPlayingAudio = false; 561 }; 562 563 class ChangeActivePeerConnectionsRunnable final : public WorkerControlRunnable { 564 public: 565 ChangeActivePeerConnectionsRunnable() = delete; 566 explicit ChangeActivePeerConnectionsRunnable(WorkerPrivate* aWorkerPrivate) = 567 delete; 568 ChangeActivePeerConnectionsRunnable(WorkerPrivate* aWorkerPrivate, 569 bool aHasPeerConnections) 570 : WorkerControlRunnable("ChangeActivePeerConnectionsRunnable"), 571 mConnections(aHasPeerConnections) {} 572 573 private: 574 virtual bool WorkerRun(JSContext* aCx, 575 WorkerPrivate* aWorkerPrivate) override { 576 return aWorkerPrivate->ChangePeerConnectionsInternal(mConnections); 577 } 578 bool mConnections = false; 579 }; 580 581 class PropagateStorageAccessPermissionGrantedRunnable final 582 : public WorkerControlRunnable { 583 public: 584 explicit PropagateStorageAccessPermissionGrantedRunnable( 585 WorkerPrivate* aWorkerPrivate) 586 : WorkerControlRunnable( 587 "PropagateStorageAccessPermissionGrantedRunnable") {} 588 589 private: 590 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 591 aWorkerPrivate->PropagateStorageAccessPermissionGrantedInternal(); 592 return true; 593 } 594 }; 595 596 class ReportErrorToConsoleRunnable final : public WorkerParentThreadRunnable { 597 public: 598 // aWorkerPrivate is the worker thread we're on (or the main thread, if null) 599 static void Report(WorkerPrivate* aWorkerPrivate, uint32_t aErrorFlags, 600 const nsCString& aCategory, 601 nsContentUtils::PropertiesFile aFile, 602 const nsCString& aMessageName, 603 const nsTArray<nsString>& aParams, 604 const mozilla::SourceLocation& aLocation) { 605 if (aWorkerPrivate) { 606 aWorkerPrivate->AssertIsOnWorkerThread(); 607 } else { 608 AssertIsOnMainThread(); 609 } 610 611 // Now fire a runnable to do the same on the parent's thread if we can. 612 if (aWorkerPrivate) { 613 RefPtr<ReportErrorToConsoleRunnable> runnable = 614 new ReportErrorToConsoleRunnable(aWorkerPrivate, aErrorFlags, 615 aCategory, aFile, aMessageName, 616 aParams, aLocation); 617 runnable->Dispatch(aWorkerPrivate); 618 return; 619 } 620 621 // Log a warning to the console. 622 nsContentUtils::ReportToConsole(aErrorFlags, aCategory, nullptr, aFile, 623 aMessageName.get(), aParams, aLocation); 624 } 625 626 private: 627 ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate, 628 uint32_t aErrorFlags, const nsCString& aCategory, 629 nsContentUtils::PropertiesFile aFile, 630 const nsCString& aMessageName, 631 const nsTArray<nsString>& aParams, 632 const mozilla::SourceLocation& aLocation) 633 : WorkerParentThreadRunnable("ReportErrorToConsoleRunnable"), 634 mErrorFlags(aErrorFlags), 635 mCategory(aCategory), 636 mFile(aFile), 637 mMessageName(aMessageName), 638 mParams(aParams.Clone()), 639 mLocation(aLocation) {} 640 641 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, 642 bool aDispatchResult) override { 643 aWorkerPrivate->AssertIsOnWorkerThread(); 644 645 // Dispatch may fail if the worker was canceled, no need to report that as 646 // an error, so don't call base class PostDispatch. 647 } 648 649 virtual bool WorkerRun(JSContext* aCx, 650 WorkerPrivate* aWorkerPrivate) override { 651 WorkerPrivate* parent = aWorkerPrivate->GetParent(); 652 MOZ_ASSERT_IF(!parent, NS_IsMainThread()); 653 Report(parent, mErrorFlags, mCategory, mFile, mMessageName, mParams, 654 mLocation); 655 return true; 656 } 657 658 const uint32_t mErrorFlags; 659 const nsCString mCategory; 660 const nsContentUtils::PropertiesFile mFile; 661 const nsCString mMessageName; 662 const nsTArray<nsString> mParams; 663 const mozilla::SourceLocation mLocation; 664 }; 665 666 class DebuggerImmediateRunnable final : public WorkerThreadRunnable { 667 RefPtr<dom::Function> mHandler; 668 669 public: 670 explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate, 671 dom::Function& aHandler) 672 : WorkerThreadRunnable("DebuggerImmediateRunnable"), 673 mHandler(&aHandler) {} 674 675 private: 676 virtual bool IsDebuggerRunnable() const override { return true; } 677 678 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { 679 // Silence bad assertions. 680 return true; 681 } 682 683 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, 684 bool aDispatchResult) override { 685 // Silence bad assertions. 686 } 687 688 // Make as MOZ_CAN_RUN_SCRIPT_BOUNDARY for calling mHandler->Call(); 689 // Since WorkerRunnable::WorkerRun has not to be MOZ_CAN_RUN_SCRIPT, but 690 // DebuggerImmediateRunnable is a special case that must to call the function 691 // defined in the debugger script. 692 MOZ_CAN_RUN_SCRIPT_BOUNDARY 693 virtual bool WorkerRun(JSContext* aCx, 694 WorkerPrivate* aWorkerPrivate) override { 695 JS::Rooted<JS::Value> rval(aCx); 696 IgnoredErrorResult rv; 697 MOZ_KnownLive(mHandler)->Call({}, &rval, rv); 698 699 return !rv.Failed(); 700 } 701 }; 702 703 // GetJSContext() is safe on the worker thread 704 void PeriodicGCTimerCallback(nsITimer* aTimer, 705 void* aClosure) MOZ_NO_THREAD_SAFETY_ANALYSIS { 706 auto* workerPrivate = static_cast<WorkerPrivate*>(aClosure); 707 MOZ_DIAGNOSTIC_ASSERT(workerPrivate); 708 workerPrivate->AssertIsOnWorkerThread(); 709 workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(), 710 false /* shrinking */, 711 false /* collect children */); 712 LOG(WorkerLog(), ("Worker %p run periodic GC\n", workerPrivate)); 713 } 714 715 void IdleGCTimerCallback(nsITimer* aTimer, 716 void* aClosure) MOZ_NO_THREAD_SAFETY_ANALYSIS { 717 auto* workerPrivate = static_cast<WorkerPrivate*>(aClosure); 718 MOZ_DIAGNOSTIC_ASSERT(workerPrivate); 719 workerPrivate->AssertIsOnWorkerThread(); 720 workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(), 721 true /* shrinking */, 722 false /* collect children */); 723 LOG(WorkerLog(), ("Worker %p run idle GC\n", workerPrivate)); 724 725 // After running idle GC we can cancel the current timers. 726 workerPrivate->CancelGCTimers(); 727 } 728 729 class UpdateContextOptionsRunnable final : public WorkerControlRunnable { 730 JS::ContextOptions mContextOptions; 731 732 public: 733 UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate, 734 const JS::ContextOptions& aContextOptions) 735 : WorkerControlRunnable("UpdateContextOptionsRunnable"), 736 mContextOptions(aContextOptions) {} 737 738 private: 739 virtual bool WorkerRun(JSContext* aCx, 740 WorkerPrivate* aWorkerPrivate) override { 741 aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions); 742 return true; 743 } 744 }; 745 746 class UpdateLanguagesRunnable final : public WorkerThreadRunnable { 747 nsTArray<nsString> mLanguages; 748 749 public: 750 UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate, 751 const nsTArray<nsString>& aLanguages) 752 : WorkerThreadRunnable("UpdateLanguagesRunnable"), 753 mLanguages(aLanguages.Clone()) {} 754 755 virtual bool WorkerRun(JSContext* aCx, 756 WorkerPrivate* aWorkerPrivate) override { 757 aWorkerPrivate->UpdateLanguagesInternal(mLanguages); 758 return true; 759 } 760 }; 761 762 class UpdateJSWorkerMemoryParameterRunnable final 763 : public WorkerControlRunnable { 764 Maybe<uint32_t> mValue; 765 JSGCParamKey mKey; 766 767 public: 768 UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate, 769 JSGCParamKey aKey, 770 Maybe<uint32_t> aValue) 771 : WorkerControlRunnable("UpdateJSWorkerMemoryParameterRunnable"), 772 mValue(aValue), 773 mKey(aKey) {} 774 775 private: 776 virtual bool WorkerRun(JSContext* aCx, 777 WorkerPrivate* aWorkerPrivate) override { 778 aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue); 779 return true; 780 } 781 }; 782 783 #ifdef JS_GC_ZEAL 784 class UpdateGCZealRunnable final : public WorkerControlRunnable { 785 uint8_t mGCZeal; 786 uint32_t mFrequency; 787 788 public: 789 UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate, uint8_t aGCZeal, 790 uint32_t aFrequency) 791 : WorkerControlRunnable("UpdateGCZealRunnable"), 792 mGCZeal(aGCZeal), 793 mFrequency(aFrequency) {} 794 795 private: 796 virtual bool WorkerRun(JSContext* aCx, 797 WorkerPrivate* aWorkerPrivate) override { 798 aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency); 799 return true; 800 } 801 }; 802 #endif 803 804 class SetLowMemoryStateRunnable final : public WorkerControlRunnable { 805 bool mState; 806 807 public: 808 SetLowMemoryStateRunnable(WorkerPrivate* aWorkerPrivate, bool aState) 809 : WorkerControlRunnable("SetLowMemoryStateRunnable"), mState(aState) {} 810 811 private: 812 virtual bool WorkerRun(JSContext* aCx, 813 WorkerPrivate* aWorkerPrivate) override { 814 aWorkerPrivate->SetLowMemoryStateInternal(aCx, mState); 815 return true; 816 } 817 }; 818 819 class GarbageCollectRunnable final : public WorkerControlRunnable { 820 bool mShrinking; 821 bool mCollectChildren; 822 823 public: 824 GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking, 825 bool aCollectChildren) 826 : WorkerControlRunnable("GarbageCollectRunnable"), 827 mShrinking(aShrinking), 828 mCollectChildren(aCollectChildren) {} 829 830 private: 831 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { 832 // Silence bad assertions, this can be dispatched from either the main 833 // thread or the timer thread.. 834 return true; 835 } 836 837 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, 838 bool aDispatchResult) override { 839 // Silence bad assertions, this can be dispatched from either the main 840 // thread or the timer thread.. 841 } 842 843 virtual bool WorkerRun(JSContext* aCx, 844 WorkerPrivate* aWorkerPrivate) override { 845 aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren); 846 if (mShrinking) { 847 // Either we've run the idle GC or explicit GC call from the parent, 848 // we can cancel the current timers. 849 aWorkerPrivate->CancelGCTimers(); 850 } 851 return true; 852 } 853 }; 854 855 class CycleCollectRunnable final : public WorkerControlRunnable { 856 bool mCollectChildren; 857 858 public: 859 CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren) 860 : WorkerControlRunnable("CycleCollectRunnable"), 861 mCollectChildren(aCollectChildren) {} 862 863 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 864 aWorkerPrivate->CycleCollectInternal(mCollectChildren); 865 return true; 866 } 867 }; 868 869 class OfflineStatusChangeRunnable final : public WorkerThreadRunnable { 870 public: 871 OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline) 872 : WorkerThreadRunnable("OfflineStatusChangeRunnable"), 873 mIsOffline(aIsOffline) {} 874 875 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 876 aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline); 877 return true; 878 } 879 880 private: 881 bool mIsOffline; 882 }; 883 884 class MemoryPressureRunnable final : public WorkerControlRunnable { 885 public: 886 explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate) 887 : WorkerControlRunnable("MemoryPressureRunnable") {} 888 889 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 890 aWorkerPrivate->MemoryPressureInternal(); 891 return true; 892 } 893 }; 894 895 class DisableRemoteDebuggerRunnable final : public WorkerControlRunnable { 896 public: 897 explicit DisableRemoteDebuggerRunnable(WorkerPrivate* aWorkerPrivate) 898 : WorkerControlRunnable("DisableRemoteDebuggerRunnable") {} 899 900 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 901 aWorkerPrivate->DisableRemoteDebuggerOnWorkerThread(); 902 return true; 903 } 904 }; 905 906 #ifdef DEBUG 907 static bool StartsWithExplicit(nsACString& s) { 908 return StringBeginsWith(s, "explicit/"_ns); 909 } 910 #endif 911 912 PRThread* PRThreadFromThread(nsIThread* aThread) { 913 MOZ_ASSERT(aThread); 914 915 PRThread* result; 916 MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result)); 917 MOZ_ASSERT(result); 918 919 return result; 920 } 921 922 // A runnable to cancel the worker from the parent thread when self.close() is 923 // called. This runnable is executed on the parent process in order to cancel 924 // the current runnable. It uses a normal WorkerDebuggeeRunnable in order to be 925 // sure that all the pending WorkerDebuggeeRunnables are executed before this. 926 class CancelingOnParentRunnable final : public WorkerParentDebuggeeRunnable { 927 public: 928 explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate) 929 : WorkerParentDebuggeeRunnable("CancelingOnParentRunnable") {} 930 931 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 932 aWorkerPrivate->Cancel(); 933 return true; 934 } 935 }; 936 937 // A runnable to cancel the worker from the parent process. 938 class CancelingWithTimeoutOnParentRunnable final 939 : public WorkerParentControlRunnable { 940 public: 941 explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate* aWorkerPrivate) 942 : WorkerParentControlRunnable("CancelingWithTimeoutOnParentRunnable") {} 943 944 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 945 aWorkerPrivate->AssertIsOnParentThread(); 946 aWorkerPrivate->StartCancelingTimer(); 947 return true; 948 } 949 }; 950 951 class CancelingTimerCallback final : public nsITimerCallback { 952 public: 953 NS_DECL_ISUPPORTS 954 955 explicit CancelingTimerCallback(WorkerPrivate* aWorkerPrivate) 956 : mWorkerPrivate(aWorkerPrivate) {} 957 958 NS_IMETHOD 959 Notify(nsITimer* aTimer) override { 960 mWorkerPrivate->AssertIsOnParentThread(); 961 mWorkerPrivate->Cancel(); 962 return NS_OK; 963 } 964 965 private: 966 ~CancelingTimerCallback() = default; 967 968 // Raw pointer here is OK because the timer is canceled during the shutdown 969 // steps. 970 WorkerPrivate* mWorkerPrivate; 971 }; 972 973 NS_IMPL_ISUPPORTS(CancelingTimerCallback, nsITimerCallback) 974 975 // This runnable starts the canceling of a worker after a self.close(). 976 class CancelingRunnable final : public Runnable { 977 public: 978 CancelingRunnable() : Runnable("CancelingRunnable") {} 979 980 NS_IMETHOD 981 Run() override { 982 LOG(WorkerLog(), ("CancelingRunnable::Run [%p]", this)); 983 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 984 MOZ_ASSERT(workerPrivate); 985 workerPrivate->AssertIsOnWorkerThread(); 986 987 // Now we can cancel the this worker from the parent process. 988 RefPtr<CancelingOnParentRunnable> r = 989 new CancelingOnParentRunnable(workerPrivate); 990 r->Dispatch(workerPrivate); 991 992 return NS_OK; 993 } 994 }; 995 996 } /* anonymous namespace */ 997 998 nsString ComputeWorkerPrivateId() { 999 nsID uuid = nsID::GenerateUUID(); 1000 return NSID_TrimBracketsUTF16(uuid); 1001 } 1002 1003 class WorkerPrivate::EventTarget final : public nsISerialEventTarget { 1004 // This mutex protects mWorkerPrivate and must be acquired *before* the 1005 // WorkerPrivate's mutex whenever they must both be held. 1006 mozilla::Mutex mMutex; 1007 WorkerPrivate* mWorkerPrivate MOZ_GUARDED_BY(mMutex); 1008 nsCOMPtr<nsIEventTarget> mNestedEventTarget MOZ_GUARDED_BY(mMutex); 1009 bool mDisabled MOZ_GUARDED_BY(mMutex); 1010 bool mShutdown MOZ_GUARDED_BY(mMutex); 1011 1012 public: 1013 EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget) 1014 : mMutex("WorkerPrivate::EventTarget::mMutex"), 1015 mWorkerPrivate(aWorkerPrivate), 1016 mNestedEventTarget(aNestedEventTarget), 1017 mDisabled(false), 1018 mShutdown(false) { 1019 MOZ_ASSERT(aWorkerPrivate); 1020 MOZ_ASSERT(aNestedEventTarget); 1021 } 1022 1023 void Disable() { 1024 { 1025 MutexAutoLock lock(mMutex); 1026 1027 // Note, Disable() can be called more than once safely. 1028 mDisabled = true; 1029 } 1030 } 1031 1032 void Shutdown() { 1033 nsCOMPtr<nsIEventTarget> nestedEventTarget; 1034 { 1035 MutexAutoLock lock(mMutex); 1036 1037 mWorkerPrivate = nullptr; 1038 mNestedEventTarget.swap(nestedEventTarget); 1039 MOZ_ASSERT(mDisabled); 1040 mShutdown = true; 1041 } 1042 } 1043 1044 RefPtr<nsIEventTarget> GetNestedEventTarget() { 1045 RefPtr<nsIEventTarget> nestedEventTarget = nullptr; 1046 { 1047 MutexAutoLock lock(mMutex); 1048 if (mWorkerPrivate) { 1049 mWorkerPrivate->AssertIsOnWorkerThread(); 1050 nestedEventTarget = mNestedEventTarget.get(); 1051 } 1052 } 1053 return nestedEventTarget; 1054 } 1055 1056 NS_DECL_THREADSAFE_ISUPPORTS 1057 NS_DECL_NSIEVENTTARGET_FULL 1058 1059 private: 1060 ~EventTarget() = default; 1061 }; 1062 1063 class WorkerJSContextStats final : public JS::RuntimeStats { 1064 const nsCString mRtPath; 1065 1066 public: 1067 explicit WorkerJSContextStats(const nsACString& aRtPath) 1068 : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) {} 1069 1070 ~WorkerJSContextStats() { 1071 for (JS::ZoneStats& stats : zoneStatsVector) { 1072 delete static_cast<xpc::ZoneStatsExtras*>(stats.extra); 1073 } 1074 1075 for (JS::RealmStats& stats : realmStatsVector) { 1076 delete static_cast<xpc::RealmStatsExtras*>(stats.extra); 1077 } 1078 } 1079 1080 const nsCString& Path() const { return mRtPath; } 1081 1082 virtual void initExtraZoneStats(JS::Zone* aZone, JS::ZoneStats* aZoneStats, 1083 const JS::AutoRequireNoGC& nogc) override { 1084 MOZ_ASSERT(!aZoneStats->extra); 1085 1086 // ReportJSRuntimeExplicitTreeStats expects that 1087 // aZoneStats->extra is a xpc::ZoneStatsExtras pointer. 1088 xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras; 1089 extras->pathPrefix = mRtPath; 1090 extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)aZone); 1091 1092 MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix)); 1093 1094 aZoneStats->extra = extras; 1095 } 1096 1097 virtual void initExtraRealmStats(JS::Realm* aRealm, 1098 JS::RealmStats* aRealmStats, 1099 const JS::AutoRequireNoGC& nogc) override { 1100 MOZ_ASSERT(!aRealmStats->extra); 1101 1102 // ReportJSRuntimeExplicitTreeStats expects that 1103 // aRealmStats->extra is a xpc::RealmStatsExtras pointer. 1104 xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras; 1105 1106 // This is the |jsPathPrefix|. Each worker has exactly one realm. 1107 extras->jsPathPrefix.Assign(mRtPath); 1108 extras->jsPathPrefix += 1109 nsPrintfCString("zone(0x%p)/", (void*)js::GetRealmZone(aRealm)); 1110 extras->jsPathPrefix += "realm(web-worker)/"_ns; 1111 1112 // This should never be used when reporting with workers (hence the "?!"). 1113 extras->domPathPrefix.AssignLiteral("explicit/workers/?!/"); 1114 1115 MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix)); 1116 MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix)); 1117 1118 extras->location = nullptr; 1119 1120 aRealmStats->extra = extras; 1121 } 1122 }; 1123 1124 class WorkerPrivate::MemoryReporter final : public nsIMemoryReporter { 1125 NS_DECL_THREADSAFE_ISUPPORTS 1126 1127 friend class WorkerPrivate; 1128 1129 SharedMutex mMutex; 1130 WorkerPrivate* mWorkerPrivate; 1131 1132 public: 1133 explicit MemoryReporter(WorkerPrivate* aWorkerPrivate) 1134 : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate) { 1135 aWorkerPrivate->AssertIsOnWorkerThread(); 1136 } 1137 1138 NS_IMETHOD 1139 CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, 1140 bool aAnonymize) override; 1141 1142 private: 1143 class FinishCollectRunnable; 1144 1145 class CollectReportsRunnable final : public MainThreadWorkerControlRunnable { 1146 RefPtr<FinishCollectRunnable> mFinishCollectRunnable; 1147 const bool mAnonymize; 1148 1149 public: 1150 CollectReportsRunnable(WorkerPrivate* aWorkerPrivate, 1151 nsIHandleReportCallback* aHandleReport, 1152 nsISupports* aHandlerData, bool aAnonymize, 1153 const nsACString& aPath); 1154 1155 private: 1156 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; 1157 1158 ~CollectReportsRunnable() { 1159 if (NS_IsMainThread()) { 1160 mFinishCollectRunnable->Run(); 1161 return; 1162 } 1163 1164 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1165 MOZ_ASSERT(workerPrivate); 1166 MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThreadForMessaging( 1167 mFinishCollectRunnable.forget())); 1168 } 1169 }; 1170 1171 class FinishCollectRunnable final : public Runnable { 1172 nsCOMPtr<nsIHandleReportCallback> mHandleReport; 1173 nsCOMPtr<nsISupports> mHandlerData; 1174 size_t mPerformanceUserEntries; 1175 size_t mPerformanceResourceEntries; 1176 const bool mAnonymize; 1177 bool mSuccess; 1178 1179 public: 1180 WorkerJSContextStats mCxStats; 1181 1182 explicit FinishCollectRunnable(nsIHandleReportCallback* aHandleReport, 1183 nsISupports* aHandlerData, bool aAnonymize, 1184 const nsACString& aPath); 1185 1186 NS_IMETHOD Run() override; 1187 1188 void SetPerformanceSizes(size_t userEntries, size_t resourceEntries) { 1189 mPerformanceUserEntries = userEntries; 1190 mPerformanceResourceEntries = resourceEntries; 1191 } 1192 1193 void SetSuccess(bool success) { mSuccess = success; } 1194 1195 FinishCollectRunnable(const FinishCollectRunnable&) = delete; 1196 FinishCollectRunnable& operator=(const FinishCollectRunnable&) = delete; 1197 FinishCollectRunnable& operator=(const FinishCollectRunnable&&) = delete; 1198 1199 private: 1200 ~FinishCollectRunnable() { 1201 // mHandleReport and mHandlerData are released on the main thread. 1202 AssertIsOnMainThread(); 1203 } 1204 }; 1205 1206 ~MemoryReporter() = default; 1207 1208 void Disable() { 1209 // Called from WorkerPrivate::DisableMemoryReporter. 1210 mMutex.AssertCurrentThreadOwns(); 1211 1212 NS_ASSERTION(mWorkerPrivate, "Disabled more than once!"); 1213 mWorkerPrivate = nullptr; 1214 } 1215 }; 1216 1217 NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter) 1218 1219 NS_IMETHODIMP 1220 WorkerPrivate::MemoryReporter::CollectReports( 1221 nsIHandleReportCallback* aHandleReport, nsISupports* aData, 1222 bool aAnonymize) { 1223 AssertIsOnMainThread(); 1224 1225 RefPtr<CollectReportsRunnable> runnable; 1226 1227 { 1228 MutexAutoLock lock(mMutex); 1229 1230 if (!mWorkerPrivate) { 1231 // This will effectively report 0 memory. 1232 nsCOMPtr<nsIMemoryReporterManager> manager = 1233 do_GetService("@mozilla.org/memory-reporter-manager;1"); 1234 if (manager) { 1235 manager->EndReport(); 1236 } 1237 return NS_OK; 1238 } 1239 1240 nsAutoCString path; 1241 path.AppendLiteral("explicit/workers/workers("); 1242 if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) { 1243 path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>"); 1244 } else { 1245 nsAutoCString escapedDomain(mWorkerPrivate->Domain()); 1246 if (escapedDomain.IsEmpty()) { 1247 escapedDomain += "chrome"; 1248 } else { 1249 escapedDomain.ReplaceChar('/', '\\'); 1250 } 1251 path.Append(escapedDomain); 1252 path.AppendLiteral(")/worker("); 1253 NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL()); 1254 escapedURL.ReplaceChar('/', '\\'); 1255 path.Append(escapedURL); 1256 } 1257 path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate)); 1258 1259 runnable = new CollectReportsRunnable(mWorkerPrivate, aHandleReport, aData, 1260 aAnonymize, path); 1261 } 1262 1263 if (!runnable->Dispatch(mWorkerPrivate)) { 1264 return NS_ERROR_UNEXPECTED; 1265 } 1266 1267 return NS_OK; 1268 } 1269 1270 WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable( 1271 WorkerPrivate* aWorkerPrivate, nsIHandleReportCallback* aHandleReport, 1272 nsISupports* aHandlerData, bool aAnonymize, const nsACString& aPath) 1273 : MainThreadWorkerControlRunnable("CollectReportsRunnable"), 1274 mFinishCollectRunnable(new FinishCollectRunnable( 1275 aHandleReport, aHandlerData, aAnonymize, aPath)), 1276 mAnonymize(aAnonymize) {} 1277 1278 bool WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun( 1279 JSContext* aCx, WorkerPrivate* aWorkerPrivate) { 1280 aWorkerPrivate->AssertIsOnWorkerThread(); 1281 1282 RefPtr<WorkerGlobalScope> scope = aWorkerPrivate->GlobalScope(); 1283 RefPtr<Performance> performance = 1284 scope ? scope->GetPerformanceIfExists() : nullptr; 1285 if (performance) { 1286 size_t userEntries = performance->SizeOfUserEntries(JsWorkerMallocSizeOf); 1287 size_t resourceEntries = 1288 performance->SizeOfResourceEntries(JsWorkerMallocSizeOf); 1289 mFinishCollectRunnable->SetPerformanceSizes(userEntries, resourceEntries); 1290 } 1291 1292 mFinishCollectRunnable->SetSuccess(aWorkerPrivate->CollectRuntimeStats( 1293 &mFinishCollectRunnable->mCxStats, mAnonymize)); 1294 1295 return true; 1296 } 1297 1298 WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable( 1299 nsIHandleReportCallback* aHandleReport, nsISupports* aHandlerData, 1300 bool aAnonymize, const nsACString& aPath) 1301 : mozilla::Runnable( 1302 "dom::WorkerPrivate::MemoryReporter::FinishCollectRunnable"), 1303 mHandleReport(aHandleReport), 1304 mHandlerData(aHandlerData), 1305 mPerformanceUserEntries(0), 1306 mPerformanceResourceEntries(0), 1307 mAnonymize(aAnonymize), 1308 mSuccess(false), 1309 mCxStats(aPath) {} 1310 1311 NS_IMETHODIMP 1312 WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run() { 1313 AssertIsOnMainThread(); 1314 1315 nsCOMPtr<nsIMemoryReporterManager> manager = 1316 do_GetService("@mozilla.org/memory-reporter-manager;1"); 1317 1318 if (!manager) return NS_OK; 1319 1320 if (mSuccess) { 1321 xpc::ReportJSRuntimeExplicitTreeStats( 1322 mCxStats, mCxStats.Path(), mHandleReport, mHandlerData, mAnonymize); 1323 1324 if (mPerformanceUserEntries) { 1325 nsCString path = mCxStats.Path(); 1326 path.AppendLiteral("dom/performance/user-entries"); 1327 mHandleReport->Callback(""_ns, path, nsIMemoryReporter::KIND_HEAP, 1328 nsIMemoryReporter::UNITS_BYTES, 1329 static_cast<int64_t>(mPerformanceUserEntries), 1330 "Memory used for performance user entries."_ns, 1331 mHandlerData); 1332 } 1333 1334 if (mPerformanceResourceEntries) { 1335 nsCString path = mCxStats.Path(); 1336 path.AppendLiteral("dom/performance/resource-entries"); 1337 mHandleReport->Callback( 1338 ""_ns, path, nsIMemoryReporter::KIND_HEAP, 1339 nsIMemoryReporter::UNITS_BYTES, 1340 static_cast<int64_t>(mPerformanceResourceEntries), 1341 "Memory used for performance resource entries."_ns, mHandlerData); 1342 } 1343 } 1344 1345 manager->EndReport(); 1346 1347 return NS_OK; 1348 } 1349 1350 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget) 1351 : mEventTarget(aEventTarget), 1352 mResult(NS_ERROR_FAILURE), 1353 mCompleted(false) 1354 #ifdef DEBUG 1355 , 1356 mHasRun(false) 1357 #endif 1358 { 1359 } 1360 1361 Document* WorkerPrivate::GetDocument() const { 1362 AssertIsOnMainThread(); 1363 if (nsPIDOMWindowInner* window = GetAncestorWindow()) { 1364 return window->GetExtantDoc(); 1365 } 1366 // couldn't query a document, give up and return nullptr 1367 return nullptr; 1368 } 1369 1370 nsPIDOMWindowInner* WorkerPrivate::GetAncestorWindow() const { 1371 AssertIsOnMainThread(); 1372 1373 // We should query the window from the top level worker in case of a nested 1374 // worker, as only the top level one can have a window. 1375 WorkerPrivate* top = GetTopLevelWorker(); 1376 return top->GetWindow(); 1377 } 1378 1379 class EvictFromBFCacheRunnable final : public WorkerProxyToMainThreadRunnable { 1380 public: 1381 void RunOnMainThread(WorkerPrivate* aWorkerPrivate) override { 1382 MOZ_ASSERT(aWorkerPrivate); 1383 AssertIsOnMainThread(); 1384 if (nsCOMPtr<nsPIDOMWindowInner> win = 1385 aWorkerPrivate->GetAncestorWindow()) { 1386 win->RemoveFromBFCacheSync(); 1387 } 1388 } 1389 1390 void RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override { 1391 MOZ_ASSERT(aWorkerPrivate); 1392 aWorkerPrivate->AssertIsOnWorkerThread(); 1393 } 1394 }; 1395 1396 void WorkerPrivate::EvictFromBFCache() { 1397 AssertIsOnWorkerThread(); 1398 RefPtr<EvictFromBFCacheRunnable> runnable = new EvictFromBFCacheRunnable(); 1399 runnable->Dispatch(this); 1400 } 1401 1402 nsresult WorkerPrivate::SetCsp(nsIContentSecurityPolicy* aCSP) { 1403 AssertIsOnMainThread(); 1404 if (!aCSP) { 1405 return NS_OK; 1406 } 1407 aCSP->EnsureEventTarget(mMainThreadEventTarget); 1408 1409 mLoadInfo.mCSP = aCSP; 1410 auto ctx = WorkerCSPContext::CreateFromCSP(aCSP); 1411 if (NS_WARN_IF(ctx.isErr())) { 1412 return ctx.unwrapErr(); 1413 } 1414 mLoadInfo.mCSPContext = ctx.unwrap(); 1415 return NS_OK; 1416 } 1417 1418 nsresult WorkerPrivate::SetCSPFromHeaderValues( 1419 const nsACString& aCSPHeaderValue, 1420 const nsACString& aCSPReportOnlyHeaderValue) { 1421 AssertIsOnMainThread(); 1422 MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP); 1423 1424 NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue); 1425 NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue); 1426 1427 nsresult rv; 1428 nsCOMPtr<nsIContentSecurityPolicy> csp = new nsCSPContext(); 1429 1430 // First, we try to query the URI from the Principal, but 1431 // in case selfURI remains empty (e.g in case the Principal 1432 // is a SystemPrincipal) then we fall back and use the 1433 // base URI as selfURI for CSP. 1434 nsCOMPtr<nsIURI> selfURI; 1435 // Its not recommended to use the BasePrincipal to get the URI 1436 // but in this case we need to make an exception 1437 auto* basePrin = BasePrincipal::Cast(mLoadInfo.mPrincipal); 1438 if (basePrin) { 1439 basePrin->GetURI(getter_AddRefs(selfURI)); 1440 } 1441 if (!selfURI) { 1442 selfURI = mLoadInfo.mBaseURI; 1443 } 1444 MOZ_ASSERT(selfURI, "need a self URI for CSP"); 1445 1446 rv = csp->SetRequestContextWithPrincipal(mLoadInfo.mPrincipal, selfURI, ""_ns, 1447 0); 1448 NS_ENSURE_SUCCESS(rv, rv); 1449 1450 csp->EnsureEventTarget(mMainThreadEventTarget); 1451 1452 // If there's a CSP header, apply it. 1453 if (!cspHeaderValue.IsEmpty()) { 1454 rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false); 1455 NS_ENSURE_SUCCESS(rv, rv); 1456 } 1457 // If there's a report-only CSP header, apply it. 1458 if (!cspROHeaderValue.IsEmpty()) { 1459 rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true); 1460 NS_ENSURE_SUCCESS(rv, rv); 1461 } 1462 1463 RefPtr<extensions::WebExtensionPolicy> addonPolicy; 1464 1465 if (basePrin) { 1466 addonPolicy = basePrin->AddonPolicy(); 1467 } 1468 1469 // For extension workers there aren't any csp header values, 1470 // instead it will inherit the Extension CSP. 1471 if (addonPolicy) { 1472 csp->AppendPolicy(addonPolicy->BaseCSP(), false, false); 1473 csp->AppendPolicy(addonPolicy->ExtensionPageCSP(), false, false); 1474 } 1475 1476 mLoadInfo.mCSP = csp; 1477 1478 auto ctx = WorkerCSPContext::CreateFromCSP(csp); 1479 if (NS_WARN_IF(ctx.isErr())) { 1480 return ctx.unwrapErr(); 1481 } 1482 mLoadInfo.mCSPContext = ctx.unwrap(); 1483 return NS_OK; 1484 } 1485 1486 bool WorkerPrivate::IsFrozenForWorkerThread() const { 1487 auto data = mWorkerThreadAccessible.Access(); 1488 return data->mFrozen; 1489 } 1490 1491 bool WorkerPrivate::IsFrozen() const { 1492 AssertIsOnParentThread(); 1493 return mParentFrozen; 1494 } 1495 1496 void WorkerPrivate::StoreCSPOnClient() { 1497 auto data = mWorkerThreadAccessible.Access(); 1498 MOZ_ASSERT(data->mScope); 1499 if (mLoadInfo.mCSPContext) { 1500 mozilla::ipc::PolicyContainerArgs policyContainerArgs; 1501 policyContainerArgs.csp() = Some(mLoadInfo.mCSPContext->CSPInfo()); 1502 data->mScope->MutableClientSourceRef().SetPolicyContainerArgs( 1503 policyContainerArgs); 1504 } 1505 } 1506 1507 void WorkerPrivate::UpdateReferrerInfoFromHeader( 1508 const nsACString& aReferrerPolicyHeaderValue) { 1509 NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue); 1510 1511 if (headerValue.IsEmpty()) { 1512 return; 1513 } 1514 1515 ReferrerPolicy policy = 1516 ReferrerInfo::ReferrerPolicyFromHeaderString(headerValue); 1517 if (policy == ReferrerPolicy::_empty) { 1518 return; 1519 } 1520 1521 nsCOMPtr<nsIReferrerInfo> referrerInfo = 1522 static_cast<ReferrerInfo*>(GetReferrerInfo())->CloneWithNewPolicy(policy); 1523 SetReferrerInfo(referrerInfo); 1524 } 1525 1526 void WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb) { 1527 AssertIsOnParentThread(); 1528 1529 // The WorkerPrivate::mParentEventTargetRef has a reference to the exposed 1530 // Worker object, which is really held by the worker thread. We traverse this 1531 // reference if and only if all main thread event queues are empty, no 1532 // shutdown tasks, no StrongWorkerRefs, no child workers, no timeouts, no 1533 // blocking background actors, and we have not released the main thread 1534 // reference. We do not unlink it. This allows the CC to break cycles 1535 // involving the Worker and begin shutting it down (which does happen in 1536 // unlink) but ensures that the WorkerPrivate won't be deleted before we're 1537 // done shutting down the thread. 1538 if (IsEligibleForCC() && !mMainThreadObjectsForgotten) { 1539 nsCycleCollectionTraversalCallback& cb = aCb; 1540 WorkerPrivate* tmp = this; 1541 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef); 1542 } 1543 } 1544 1545 nsresult WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable, 1546 nsIEventTarget* aSyncLoopTarget) { 1547 // May be called on any thread! 1548 RefPtr<WorkerRunnable> runnable(aRunnable); 1549 1550 LOGV(("WorkerPrivate::Dispatch [%p] runnable %p", this, runnable.get())); 1551 if (!aSyncLoopTarget) { 1552 // Dispatch control runnable 1553 if (runnable->IsControlRunnable()) { 1554 return DispatchControlRunnable(runnable.forget()); 1555 } 1556 1557 // Dispatch debugger runnable 1558 if (runnable->IsDebuggerRunnable()) { 1559 return DispatchDebuggerRunnable(runnable.forget()); 1560 } 1561 } 1562 MutexAutoLock lock(mMutex); 1563 return DispatchLockHeld(runnable.forget(), aSyncLoopTarget, lock); 1564 } 1565 1566 nsresult WorkerPrivate::DispatchToParent( 1567 already_AddRefed<WorkerRunnable> aRunnable) { 1568 RefPtr<WorkerRunnable> runnable(aRunnable); 1569 1570 LOGV(("WorkerPrivate::DispatchToParent [%p] runnable %p", this, 1571 runnable.get())); 1572 1573 WorkerPrivate* parent = GetParent(); 1574 // Dispatch to parent worker 1575 if (parent) { 1576 if (runnable->IsControlRunnable()) { 1577 return parent->DispatchControlRunnable(runnable.forget()); 1578 } 1579 return parent->Dispatch(runnable.forget()); 1580 } 1581 1582 // Dispatch to main thread 1583 if (runnable->IsDebuggeeRunnable()) { 1584 RefPtr<WorkerParentDebuggeeRunnable> debuggeeRunnable = 1585 runnable.forget().downcast<WorkerParentDebuggeeRunnable>(); 1586 return DispatchDebuggeeToMainThread(debuggeeRunnable.forget(), 1587 NS_DISPATCH_FALLIBLE); 1588 } 1589 return DispatchToMainThread(runnable.forget(), NS_DISPATCH_FALLIBLE); 1590 } 1591 1592 nsresult WorkerPrivate::DispatchLockHeld( 1593 already_AddRefed<WorkerRunnable> aRunnable, nsIEventTarget* aSyncLoopTarget, 1594 const MutexAutoLock& aProofOfLock) { 1595 // May be called on any thread! 1596 RefPtr<WorkerRunnable> runnable(aRunnable); 1597 LOGV(("WorkerPrivate::DispatchLockHeld [%p] runnable: %p", this, 1598 runnable.get())); 1599 1600 MOZ_ASSERT_IF(aSyncLoopTarget, mThread); 1601 1602 // Dispatch normal worker runnable 1603 if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Canceling)) { 1604 NS_WARNING( 1605 "A runnable was posted to a worker that is already shutting " 1606 "down!"); 1607 return NS_ERROR_UNEXPECTED; 1608 } 1609 1610 // Postpone the debuggee runnable dispatching while remote debugger 1611 // registration 1612 if (runnable->IsDebuggeeRunnable() && !mDebuggerReady && 1613 !mRemoteDebuggerReady && 1614 (!mRemoteDebuggerRegistered && XRE_IsParentProcess())) { 1615 MOZ_RELEASE_ASSERT(!aSyncLoopTarget); 1616 mDelayedDebuggeeRunnables.AppendElement(runnable); 1617 return NS_OK; 1618 } 1619 1620 if (!mThread) { 1621 if (ParentStatus() == Pending || mStatus == Pending) { 1622 LOGV( 1623 ("WorkerPrivate::DispatchLockHeld [%p] runnable %p is queued in " 1624 "mPreStartRunnables", 1625 this, runnable.get())); 1626 RefPtr<WorkerThreadRunnable> workerThreadRunnable = 1627 static_cast<WorkerThreadRunnable*>(runnable.get()); 1628 PROFILER_MARKER("WorkerPrivate::DispatchLockHeld", OTHER, 1629 {MarkerStack::MaybeCapture( 1630 profiler_feature_active(ProfilerFeature::Flows))}, 1631 FlowMarker, Flow::FromPointer(runnable.get())); 1632 mPreStartRunnables.AppendElement(workerThreadRunnable); 1633 return NS_OK; 1634 } 1635 1636 NS_WARNING( 1637 "Using a worker event target after the thread has already" 1638 "been released!"); 1639 return NS_ERROR_UNEXPECTED; 1640 } 1641 1642 nsresult rv; 1643 if (aSyncLoopTarget) { 1644 LOGV( 1645 ("WorkerPrivate::DispatchLockHeld [%p] runnable %p dispatch to a " 1646 "SyncLoop(%p)", 1647 this, runnable.get(), aSyncLoopTarget)); 1648 rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_FALLIBLE); 1649 } else { 1650 // If mStatus is Pending, the WorkerPrivate initialization still can fail. 1651 // Append this WorkerThreadRunnable to WorkerPrivate::mPreStartRunnables, 1652 // such that this WorkerThreadRunnable can get the correct value of 1653 // mCleanPreStartDispatching in WorkerPrivate::RunLoopNeverRan(). 1654 if (mStatus == Pending) { 1655 LOGV( 1656 ("WorkerPrivate::DispatchLockHeld [%p] runnable %p is append in " 1657 "mPreStartRunnables", 1658 this, runnable.get())); 1659 RefPtr<WorkerThreadRunnable> workerThreadRunnable = 1660 static_cast<WorkerThreadRunnable*>(runnable.get()); 1661 PROFILER_MARKER("WorkerPrivate::DispatchLockHeld", OTHER, 1662 {MarkerStack::MaybeCapture( 1663 profiler_feature_active(ProfilerFeature::Flows))}, 1664 FlowMarker, Flow::FromPointer(runnable.get())); 1665 mPreStartRunnables.AppendElement(workerThreadRunnable); 1666 } 1667 1668 // WorkerDebuggeeRunnables don't need any special treatment here. True, 1669 // they should not be delivered to a frozen worker. But frozen workers 1670 // aren't drawing from the thread's main event queue anyway, only from 1671 // mControlQueue. 1672 LOGV( 1673 ("WorkerPrivate::DispatchLockHeld [%p] runnable %p dispatch to the " 1674 "main event queue", 1675 this, runnable.get())); 1676 rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget()); 1677 } 1678 1679 if (NS_WARN_IF(NS_FAILED(rv))) { 1680 LOGV(("WorkerPrivate::Dispatch Failed [%p]", this)); 1681 return rv; 1682 } 1683 1684 mCondVar.Notify(); 1685 return NS_OK; 1686 } 1687 1688 void WorkerPrivate::EnableDebugger() { 1689 AssertIsOnParentThread(); 1690 1691 if (NS_FAILED(RegisterWorkerDebugger(this))) { 1692 NS_WARNING("Failed to register worker debugger!"); 1693 return; 1694 } 1695 } 1696 1697 void WorkerPrivate::DisableDebugger() { 1698 AssertIsOnParentThread(); 1699 1700 // RegisterDebuggerMainThreadRunnable might be dispatched but not executed. 1701 // Wait for its execution before unregistraion. 1702 if (!NS_IsMainThread()) { 1703 WaitForIsDebuggerRegistered(true); 1704 } 1705 1706 if (NS_FAILED(UnregisterWorkerDebugger(this))) { 1707 NS_WARNING("Failed to unregister worker debugger!"); 1708 } 1709 } 1710 1711 void WorkerPrivate::BindRemoteWorkerDebuggerChild() { 1712 AssertIsOnWorkerThread(); 1713 MOZ_ASSERT_DEBUG_OR_FUZZING(!mRemoteDebugger); 1714 1715 if (XRE_IsParentProcess()) { 1716 return; 1717 } 1718 1719 RefPtr<RemoteWorkerDebuggerChild> debugger = 1720 MakeRefPtr<RemoteWorkerDebuggerChild>(this); 1721 mDebuggerChildEp.Bind(debugger); 1722 { 1723 MutexAutoLock lock(mMutex); 1724 MOZ_ASSERT_DEBUG_OR_FUZZING(!mRemoteDebugger); 1725 mRemoteDebugger = std::move(debugger); 1726 mDebuggerBindingCondVar.Notify(); 1727 } 1728 } 1729 1730 void WorkerPrivate::CreateRemoteDebuggerEndpoints() { 1731 AssertIsOnParentThread(); 1732 1733 if (XRE_IsParentProcess()) { 1734 return; 1735 } 1736 1737 MutexAutoLock lock(mMutex); 1738 MOZ_ASSERT_DEBUG_OR_FUZZING(!mRemoteDebugger && 1739 !mDebuggerParentEp.IsValid() && 1740 !mDebuggerChildEp.IsValid()); 1741 1742 (void)NS_WARN_IF(NS_FAILED(PRemoteWorkerDebugger::CreateEndpoints( 1743 &mDebuggerParentEp, &mDebuggerChildEp))); 1744 } 1745 1746 void WorkerPrivate::SetIsRemoteDebuggerRegistered(const bool& aRegistered) { 1747 AssertIsOnWorkerThread(); 1748 1749 if (XRE_IsParentProcess()) { 1750 return; 1751 } 1752 1753 if (aRegistered) { 1754 MutexAutoLock lock(mMutex); 1755 MOZ_ASSERT(mRemoteDebuggerRegistered != aRegistered); 1756 1757 mRemoteDebuggerRegistered = aRegistered; 1758 bool debuggerRegistered = mDebuggerRegistered && mRemoteDebuggerRegistered; 1759 if (mRemoteDebuggerReady && mDebuggerReady && debuggerRegistered) { 1760 LOGV( 1761 ("WorkerPrivate::SetIsRemoteDebuggerRegistered [%p] dispatching " 1762 "the delayed debuggee runnables", 1763 this)); 1764 // Dispatch all the delayed runnables without releasing the lock, to 1765 // ensure that the order in which debuggee runnables execute is the same 1766 // as the order in which they were originally dispatched. 1767 auto pending = std::move(mDelayedDebuggeeRunnables); 1768 for (uint32_t i = 0; i < pending.Length(); i++) { 1769 RefPtr<WorkerRunnable> runnable = std::move(pending[i]); 1770 (void)NS_WARN_IF( 1771 NS_FAILED(DispatchLockHeld(runnable.forget(), nullptr, lock))); 1772 } 1773 MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty()); 1774 } 1775 mDebuggerBindingCondVar.Notify(); 1776 return; 1777 } 1778 1779 RefPtr<RemoteWorkerDebuggerChild> unregisteredDebugger; 1780 { 1781 MutexAutoLock lock(mMutex); 1782 // Can not call RemoteWorkerDebuggerChild::Close() with lock. It causes 1783 // deadlock between mMutex and MessageChannel::mMonitor. 1784 unregisteredDebugger = std::move(mRemoteDebugger); 1785 // Force to set as unregistered, mRemoteDebuggerRegistered could be false 1786 // here since Worker quickly shutdown or initialization fails in 1787 // WorkerThreadPrimaryRunnable::Run(). 1788 mRemoteDebuggerRegistered = aRegistered; 1789 } 1790 if (unregisteredDebugger) { 1791 unregisteredDebugger->Close(); 1792 unregisteredDebugger = nullptr; 1793 } 1794 { 1795 MutexAutoLock lock(mMutex); 1796 mDebuggerBindingCondVar.Notify(); 1797 } 1798 } 1799 1800 void WorkerPrivate::SetIsRemoteDebuggerReady(const bool& aReady) { 1801 AssertIsOnWorkerThread(); 1802 MutexAutoLock lock(mMutex); 1803 1804 if (XRE_IsParentProcess()) { 1805 return; 1806 } 1807 1808 if (mRemoteDebuggerReady == aReady) { 1809 return; 1810 } 1811 1812 bool debuggerRegistered = mDebuggerRegistered && mRemoteDebuggerRegistered; 1813 1814 if (!aReady && debuggerRegistered) { 1815 // The debugger can only be marked as not ready during registration. 1816 return; 1817 } 1818 1819 mRemoteDebuggerReady = aReady; 1820 1821 if (mRemoteDebuggerReady && mDebuggerReady && debuggerRegistered) { 1822 LOGV( 1823 ("WorkerPrivate::SetIsRemoteDebuggerReady [%p] dispatching " 1824 "the delayed debuggee runnables", 1825 this)); 1826 // Dispatch all the delayed runnables without releasing the lock, to ensure 1827 // that the order in which debuggee runnables execute is the same as the 1828 // order in which they were originally dispatched. 1829 auto pending = std::move(mDelayedDebuggeeRunnables); 1830 for (uint32_t i = 0; i < pending.Length(); i++) { 1831 RefPtr<WorkerRunnable> runnable = std::move(pending[i]); 1832 (void)NS_WARN_IF( 1833 NS_FAILED(DispatchLockHeld(runnable.forget(), nullptr, lock))); 1834 } 1835 MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty()); 1836 } 1837 } 1838 1839 void WorkerPrivate::SetIsQueued(const bool& aQueued) { 1840 AssertIsOnParentThread(); 1841 mIsQueued = aQueued; 1842 } 1843 1844 bool WorkerPrivate::IsQueued() const { 1845 AssertIsOnParentThread(); 1846 return mIsQueued; 1847 } 1848 1849 void WorkerPrivate::EnableRemoteDebugger() { 1850 AssertIsOnParentThread(); 1851 1852 // XXX Skip for ChromeWorker now, this should be removed after Devtool codes 1853 // adapt to RemoteWorkerDebugger mechanism. 1854 if (XRE_IsParentProcess()) { 1855 return; 1856 } 1857 1858 // Wait for RemoteWorkerDebuggerChild binding done in the worker thread. 1859 mozilla::ipc::Endpoint<PRemoteWorkerDebuggerParent> parentEp; 1860 { 1861 MutexAutoLock lock(mMutex); 1862 if (!mRemoteDebugger) { 1863 mDebuggerBindingCondVar.Wait(); 1864 } 1865 // If Worker Thread never run the event loop, i.e. JSContext initilaization 1866 // fails, directly return for the cases. Because mRemoteDebugger is only 1867 // created after initialization successfully, but mDebuggerBindingCondVar 1868 // can get notified if the initialization fails. 1869 if (!mRemoteDebugger) { 1870 return; 1871 } 1872 parentEp = std::move(mDebuggerParentEp); 1873 } 1874 1875 // Call IPC for RemoteWorkerDebuggerParent binding and registration. 1876 RemoteWorkerDebuggerInfo info( 1877 mIsChromeWorker, mWorkerKind, mScriptURL, WindowID(), 1878 WrapNotNull(GetPrincipal()), IsServiceWorker() ? ServiceWorkerID() : 0, 1879 Id(), mWorkerName, 1880 GetParent() ? nsAutoString(GetParent()->Id()) : EmptyString()); 1881 1882 MOZ_ASSERT_DEBUG_OR_FUZZING(parentEp.IsValid()); 1883 RemoteWorkerService::RegisterRemoteDebugger(std::move(info), 1884 std::move(parentEp)); 1885 // Wait for register done 1886 { 1887 MutexAutoLock lock(mMutex); 1888 if (!mRemoteDebuggerRegistered) { 1889 mDebuggerBindingCondVar.Wait(); 1890 } 1891 // Warning the case if the Worker shutdown before remote debugger 1892 // registration down. 1893 (void)NS_WARN_IF(!mRemoteDebuggerRegistered); 1894 } 1895 } 1896 1897 void WorkerPrivate::DisableRemoteDebugger() { 1898 AssertIsOnParentThread(); 1899 1900 if (XRE_IsParentProcess()) { 1901 return; 1902 } 1903 1904 RefPtr<DisableRemoteDebuggerRunnable> r = 1905 new DisableRemoteDebuggerRunnable(this); 1906 1907 if (r->Dispatch(this)) { 1908 MutexAutoLock lock(mMutex); 1909 if (mRemoteDebuggerRegistered) { 1910 mDebuggerBindingCondVar.Wait(); 1911 } 1912 } 1913 } 1914 1915 void WorkerPrivate::DisableRemoteDebuggerOnWorkerThread( 1916 const bool& aForShutdown) { 1917 AssertIsOnWorkerThread(); 1918 1919 if (XRE_IsParentProcess()) { 1920 return; 1921 } 1922 RefPtr<RemoteWorkerDebuggerChild> remoteDebugger; 1923 { 1924 MutexAutoLock lock(mMutex); 1925 remoteDebugger = mRemoteDebugger; 1926 } 1927 if (remoteDebugger) { 1928 remoteDebugger->SendUnregister(); 1929 } 1930 1931 // Now notify the parent thread if it is blocked by waiting 1932 // RemoteWorkerDebugger registration or unregsiteration. 1933 if (aForShutdown) { 1934 SetIsRemoteDebuggerRegistered(false); 1935 } 1936 } 1937 1938 nsresult WorkerPrivate::DispatchControlRunnable( 1939 already_AddRefed<WorkerRunnable> aWorkerRunnable) { 1940 // May be called on any thread! 1941 RefPtr<WorkerRunnable> runnable(aWorkerRunnable); 1942 MOZ_ASSERT_DEBUG_OR_FUZZING(runnable && runnable->IsControlRunnable()); 1943 1944 LOG(WorkerLog(), ("WorkerPrivate::DispatchControlRunnable [%p] runnable %p", 1945 this, runnable.get())); 1946 1947 JSContext* cx = nullptr; 1948 { 1949 MutexAutoLock lock(mMutex); 1950 1951 if (mStatus == Dead) { 1952 return NS_ERROR_UNEXPECTED; 1953 } 1954 1955 // Unfortunately we can not distinguish if we are on WorkerThread or not. 1956 // mThread is set in WorkerPrivate::SetWorkerPrivateInWorkerThread(), but 1957 // ControlRunnable can be dispatching before setting mThread. 1958 MOZ_ASSERT(mDispatchingControlRunnables < UINT32_MAX); 1959 mDispatchingControlRunnables++; 1960 1961 // Transfer ownership to the control queue. 1962 mControlQueue.Push(runnable.forget().take()); 1963 cx = mJSContext; 1964 MOZ_ASSERT_IF(cx, mThread); 1965 } 1966 1967 if (cx) { 1968 JS_RequestInterruptCallback(cx); 1969 } 1970 1971 { 1972 MutexAutoLock lock(mMutex); 1973 if (!--mDispatchingControlRunnables) { 1974 mCondVar.Notify(); 1975 } 1976 } 1977 1978 return NS_OK; 1979 } 1980 1981 void DebuggerInterruptTimerCallback(nsITimer* aTimer, void* aClosure) 1982 MOZ_NO_THREAD_SAFETY_ANALYSIS { 1983 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1984 MOZ_DIAGNOSTIC_ASSERT(workerPrivate); 1985 workerPrivate->DebuggerInterruptRequest(); 1986 } 1987 1988 nsresult WorkerPrivate::DispatchDebuggerRunnable( 1989 already_AddRefed<WorkerRunnable> aDebuggerRunnable) { 1990 // May be called on any thread! 1991 1992 RefPtr<WorkerRunnable> runnable(aDebuggerRunnable); 1993 1994 MOZ_ASSERT(runnable); 1995 1996 MutexAutoLock lock(mMutex); 1997 if (!mDebuggerInterruptTimer) { 1998 // There is no timer, so we need to create one. For locking discipline 1999 // purposes we can't manipulate the timer while our mutex is held so 2000 // drop the mutex while we build and configure the timer. Only this 2001 // function here on the main thread will create a timer, so we're not 2002 // racing anyone to create or assign the timer. 2003 nsCOMPtr<nsITimer> timer; 2004 { 2005 MutexAutoUnlock unlock(mMutex); 2006 timer = NS_NewTimer(); 2007 MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(mWorkerControlEventTarget)); 2008 2009 // Whenever an event is scheduled on the WorkerControlEventTarget an 2010 // interrupt is automatically requested which causes us to yield JS 2011 // execution and the next JS execution in the queue to execute. This 2012 // allows for simple code reuse of the existing interrupt callback code 2013 // used for control events. 2014 MOZ_ALWAYS_SUCCEEDS(timer->InitWithNamedFuncCallback( 2015 DebuggerInterruptTimerCallback, nullptr, 2016 DEBUGGER_RUNNABLE_INTERRUPT_AFTER_MS, nsITimer::TYPE_ONE_SHOT, 2017 "dom:DebuggerInterruptTimer"_ns)); 2018 } 2019 2020 // okay, we have our mutex back now, put the timer in place. 2021 mDebuggerInterruptTimer.swap(timer); 2022 } 2023 2024 if (mStatus == Dead) { 2025 NS_WARNING( 2026 "A debugger runnable was posted to a worker that is already " 2027 "shutting down!"); 2028 return NS_ERROR_UNEXPECTED; 2029 } 2030 2031 // Transfer ownership to the debugger queue. 2032 mDebuggerQueue.Push(runnable.forget().take()); 2033 2034 mCondVar.Notify(); 2035 2036 return NS_OK; 2037 } 2038 2039 void WorkerPrivate::DebuggerInterruptRequest() { 2040 AssertIsOnWorkerThread(); 2041 2042 auto data = mWorkerThreadAccessible.Access(); 2043 data->mDebuggerInterruptRequested = true; 2044 } 2045 2046 already_AddRefed<WorkerRunnable> WorkerPrivate::MaybeWrapAsWorkerRunnable( 2047 already_AddRefed<nsIRunnable> aRunnable) { 2048 // May be called on any thread! 2049 2050 nsCOMPtr<nsIRunnable> runnable(aRunnable); 2051 MOZ_ASSERT(runnable); 2052 2053 LOGV(("WorkerPrivate::MaybeWrapAsWorkerRunnable [%p] runnable: %p", this, 2054 runnable.get())); 2055 2056 RefPtr<WorkerRunnable> workerRunnable = 2057 WorkerRunnable::FromRunnable(runnable); 2058 if (workerRunnable) { 2059 return workerRunnable.forget(); 2060 } 2061 2062 workerRunnable = new ExternalRunnableWrapper(this, runnable); 2063 return workerRunnable.forget(); 2064 } 2065 2066 bool WorkerPrivate::Start() { 2067 // May be called on any thread! 2068 LOG(WorkerLog(), ("WorkerPrivate::Start [%p]", this)); 2069 { 2070 MutexAutoLock lock(mMutex); 2071 NS_ASSERTION(mParentStatus != Running, "How can this be?!"); 2072 2073 if (mParentStatus == Pending) { 2074 mParentStatus = Running; 2075 return true; 2076 } 2077 } 2078 2079 return false; 2080 } 2081 2082 // aCx is null when called from the finalizer 2083 bool WorkerPrivate::Notify(WorkerStatus aStatus) { 2084 AssertIsOnParentThread(); 2085 // This method is only called for Canceling or later. 2086 MOZ_DIAGNOSTIC_ASSERT(aStatus >= Canceling); 2087 2088 bool pending; 2089 { 2090 MutexAutoLock lock(mMutex); 2091 2092 if (mParentStatus >= aStatus) { 2093 return true; 2094 } 2095 2096 pending = mParentStatus == Pending; 2097 mParentStatus = aStatus; 2098 } 2099 2100 if (mCancellationCallback) { 2101 mCancellationCallback(!pending); 2102 mCancellationCallback = nullptr; 2103 } 2104 2105 mParentRef->DropWorkerPrivate(); 2106 2107 if (pending) { 2108 #ifdef DEBUG 2109 { 2110 // Fake a thread here just so that our assertions don't go off for no 2111 // reason. 2112 nsIThread* currentThread = NS_GetCurrentThread(); 2113 MOZ_ASSERT(currentThread); 2114 2115 MOZ_ASSERT(!mPRThread); 2116 mPRThread = PRThreadFromThread(currentThread); 2117 MOZ_ASSERT(mPRThread); 2118 } 2119 #endif 2120 2121 // Worker never got a chance to run, go ahead and delete it. 2122 ScheduleDeletion(WorkerPrivate::WorkerNeverRan); 2123 return true; 2124 } 2125 2126 // No Canceling timeout is needed. 2127 if (mCancelingTimer) { 2128 mCancelingTimer->Cancel(); 2129 mCancelingTimer = nullptr; 2130 } 2131 2132 // The NotifyRunnable kicks off a series of events that need the 2133 // CancelingOnParentRunnable to be executed always. 2134 // Note that we already advanced mParentStatus above and we check that 2135 // status in all other (asynchronous) call sites of SetIsPaused. 2136 if (!mParent) { 2137 MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(false)); 2138 } 2139 2140 RefPtr<NotifyRunnable> runnable = new NotifyRunnable(this, aStatus); 2141 return runnable->Dispatch(this); 2142 } 2143 2144 bool WorkerPrivate::Freeze(const nsPIDOMWindowInner* aWindow) { 2145 AssertIsOnParentThread(); 2146 2147 mParentFrozen = true; 2148 2149 bool isCanceling = false; 2150 { 2151 MutexAutoLock lock(mMutex); 2152 2153 isCanceling = mParentStatus >= Canceling; 2154 } 2155 2156 // WorkerDebuggeeRunnables sent from a worker to content must not be 2157 // delivered while the worker is frozen. 2158 // 2159 // Since a top-level worker and all its children share the same 2160 // mMainThreadDebuggeeEventTarget, it's sufficient to do this only in the 2161 // top-level worker. 2162 if (aWindow) { 2163 // This is called from WorkerPrivate construction, and We may not have 2164 // allocated mMainThreadDebuggeeEventTarget yet. 2165 if (mMainThreadDebuggeeEventTarget) { 2166 // Pausing a ThrottledEventQueue is infallible. 2167 MOZ_ALWAYS_SUCCEEDS( 2168 mMainThreadDebuggeeEventTarget->SetIsPaused(!isCanceling)); 2169 } 2170 } 2171 2172 if (isCanceling) { 2173 return true; 2174 } 2175 2176 // DisableRemoteDebugger(); 2177 2178 DisableDebugger(); 2179 2180 RefPtr<FreezeRunnable> runnable = new FreezeRunnable(this); 2181 return runnable->Dispatch(this); 2182 } 2183 2184 bool WorkerPrivate::Thaw(const nsPIDOMWindowInner* aWindow) { 2185 AssertIsOnParentThread(); 2186 MOZ_ASSERT(mParentFrozen); 2187 2188 mParentFrozen = false; 2189 2190 { 2191 bool isCanceling = false; 2192 2193 { 2194 MutexAutoLock lock(mMutex); 2195 2196 isCanceling = mParentStatus >= Canceling; 2197 } 2198 2199 // Delivery of WorkerDebuggeeRunnables to the window may resume. 2200 // 2201 // Since a top-level worker and all its children share the same 2202 // mMainThreadDebuggeeEventTarget, it's sufficient to do this only in the 2203 // top-level worker. 2204 if (aWindow) { 2205 // Since the worker is no longer frozen, only a paused parent window 2206 // should require the queue to remain paused. 2207 // 2208 // This can only fail if the ThrottledEventQueue cannot dispatch its 2209 // executor to the main thread, in which case the main thread was never 2210 // going to draw runnables from it anyway, so the failure doesn't matter. 2211 (void)mMainThreadDebuggeeEventTarget->SetIsPaused( 2212 IsParentWindowPaused() && !isCanceling); 2213 } 2214 2215 if (isCanceling) { 2216 return true; 2217 } 2218 } 2219 2220 // Create remote debugger endpoints here for child binding in ThawRunnable; 2221 // CreateRemoteDebuggerEndpoints(); 2222 RefPtr<ThawRunnable> runnable = new ThawRunnable(this); 2223 bool rv = runnable->Dispatch(this); 2224 // EnableRemoteDebugger(); 2225 2226 EnableDebugger(); 2227 2228 return rv; 2229 } 2230 2231 void WorkerPrivate::ParentWindowPaused() { 2232 AssertIsOnMainThread(); 2233 MOZ_ASSERT(!mParentWindowPaused); 2234 mParentWindowPaused = true; 2235 2236 // This is called from WorkerPrivate construction, and we may not have 2237 // allocated mMainThreadDebuggeeEventTarget yet. 2238 if (mMainThreadDebuggeeEventTarget) { 2239 bool isCanceling = false; 2240 2241 { 2242 MutexAutoLock lock(mMutex); 2243 2244 isCanceling = mParentStatus >= Canceling; 2245 } 2246 2247 // If we are already canceling we might wait for CancelingOnParentRunnable 2248 // to be executed, so do not pause. 2249 MOZ_ALWAYS_SUCCEEDS( 2250 mMainThreadDebuggeeEventTarget->SetIsPaused(!isCanceling)); 2251 } 2252 } 2253 2254 void WorkerPrivate::ParentWindowResumed() { 2255 AssertIsOnMainThread(); 2256 2257 MOZ_ASSERT(mParentWindowPaused); 2258 mParentWindowPaused = false; 2259 2260 bool isCanceling = false; 2261 { 2262 MutexAutoLock lock(mMutex); 2263 2264 isCanceling = mParentStatus >= Canceling; 2265 } 2266 2267 // Since the window is no longer paused, the queue should only remain paused 2268 // if the worker is frozen. 2269 // 2270 // This can only fail if the ThrottledEventQueue cannot dispatch its executor 2271 // to the main thread, in which case the main thread was never going to draw 2272 // runnables from it anyway, so the failure doesn't matter. 2273 (void)mMainThreadDebuggeeEventTarget->SetIsPaused(IsFrozen() && !isCanceling); 2274 } 2275 2276 void WorkerPrivate::PropagateStorageAccessPermissionGranted() { 2277 AssertIsOnParentThread(); 2278 2279 { 2280 MutexAutoLock lock(mMutex); 2281 2282 if (mParentStatus >= Canceling) { 2283 return; 2284 } 2285 } 2286 2287 RefPtr<PropagateStorageAccessPermissionGrantedRunnable> runnable = 2288 new PropagateStorageAccessPermissionGrantedRunnable(this); 2289 (void)NS_WARN_IF(!runnable->Dispatch(this)); 2290 } 2291 2292 void WorkerPrivate::NotifyStorageKeyUsed() { 2293 AssertIsOnWorkerThread(); 2294 2295 // Only notify once per global. 2296 if (hasNotifiedStorageKeyUsed) { 2297 return; 2298 } 2299 hasNotifiedStorageKeyUsed = true; 2300 2301 // Notify about storage access on the main thread. 2302 RefPtr<StrongWorkerRef> strongRef = 2303 StrongWorkerRef::Create(this, "WorkerPrivate::NotifyStorageKeyUsed"); 2304 if (!strongRef) { 2305 return; 2306 } 2307 RefPtr<ThreadSafeWorkerRef> ref = new ThreadSafeWorkerRef(strongRef); 2308 DispatchToMainThread(NS_NewRunnableFunction( 2309 "WorkerPrivate::NotifyStorageKeyUsed", [ref = std::move(ref)] { 2310 nsGlobalWindowInner* window = 2311 nsGlobalWindowInner::Cast(ref->Private()->GetAncestorWindow()); 2312 if (window) { 2313 window->MaybeNotifyStorageKeyUsed(); 2314 } 2315 })); 2316 } 2317 2318 bool WorkerPrivate::Close() { 2319 mMutex.AssertCurrentThreadOwns(); 2320 if (mParentStatus < Closing) { 2321 mParentStatus = Closing; 2322 } 2323 2324 return true; 2325 } 2326 2327 bool WorkerPrivate::ProxyReleaseMainThreadObjects() { 2328 AssertIsOnParentThread(); 2329 MOZ_ASSERT(!mMainThreadObjectsForgotten); 2330 2331 nsCOMPtr<nsILoadGroup> loadGroupToCancel; 2332 // If we're not overriden, then do nothing here. Let the load group get 2333 // handled in ForgetMainThreadObjects(). 2334 if (mLoadInfo.mInterfaceRequestor) { 2335 mLoadInfo.mLoadGroup.swap(loadGroupToCancel); 2336 } 2337 2338 bool result = mLoadInfo.ProxyReleaseMainThreadObjects( 2339 this, std::move(loadGroupToCancel)); 2340 2341 mMainThreadObjectsForgotten = true; 2342 2343 return result; 2344 } 2345 2346 void WorkerPrivate::UpdateContextOptions( 2347 const JS::ContextOptions& aContextOptions) { 2348 AssertIsOnParentThread(); 2349 2350 { 2351 MutexAutoLock lock(mMutex); 2352 mJSSettings.contextOptions = aContextOptions; 2353 } 2354 2355 RefPtr<UpdateContextOptionsRunnable> runnable = 2356 new UpdateContextOptionsRunnable(this, aContextOptions); 2357 if (!runnable->Dispatch(this)) { 2358 NS_WARNING("Failed to update worker context options!"); 2359 } 2360 } 2361 2362 void WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages) { 2363 AssertIsOnParentThread(); 2364 2365 RefPtr<UpdateLanguagesRunnable> runnable = 2366 new UpdateLanguagesRunnable(this, aLanguages); 2367 if (!runnable->Dispatch(this)) { 2368 NS_WARNING("Failed to update worker languages!"); 2369 } 2370 } 2371 2372 void WorkerPrivate::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey, 2373 Maybe<uint32_t> aValue) { 2374 AssertIsOnParentThread(); 2375 2376 bool changed = false; 2377 2378 { 2379 MutexAutoLock lock(mMutex); 2380 changed = mJSSettings.ApplyGCSetting(aKey, aValue); 2381 } 2382 2383 if (changed) { 2384 RefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable = 2385 new UpdateJSWorkerMemoryParameterRunnable(this, aKey, aValue); 2386 if (!runnable->Dispatch(this)) { 2387 NS_WARNING("Failed to update memory parameter!"); 2388 } 2389 } 2390 } 2391 2392 #ifdef JS_GC_ZEAL 2393 void WorkerPrivate::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency) { 2394 AssertIsOnParentThread(); 2395 2396 { 2397 MutexAutoLock lock(mMutex); 2398 mJSSettings.gcZeal = aGCZeal; 2399 mJSSettings.gcZealFrequency = aFrequency; 2400 } 2401 2402 RefPtr<UpdateGCZealRunnable> runnable = 2403 new UpdateGCZealRunnable(this, aGCZeal, aFrequency); 2404 if (!runnable->Dispatch(this)) { 2405 NS_WARNING("Failed to update worker gczeal!"); 2406 } 2407 } 2408 #endif 2409 2410 void WorkerPrivate::SetLowMemoryState(bool aState) { 2411 AssertIsOnParentThread(); 2412 2413 RefPtr<SetLowMemoryStateRunnable> runnable = 2414 new SetLowMemoryStateRunnable(this, aState); 2415 if (!runnable->Dispatch(this)) { 2416 NS_WARNING("Failed to set low memory state!"); 2417 } 2418 } 2419 2420 void WorkerPrivate::GarbageCollect(bool aShrinking) { 2421 AssertIsOnParentThread(); 2422 2423 RefPtr<GarbageCollectRunnable> runnable = new GarbageCollectRunnable( 2424 this, aShrinking, /* aCollectChildren = */ true); 2425 if (!runnable->Dispatch(this)) { 2426 NS_WARNING("Failed to GC worker!"); 2427 } 2428 } 2429 2430 void WorkerPrivate::CycleCollect() { 2431 AssertIsOnParentThread(); 2432 2433 RefPtr<CycleCollectRunnable> runnable = 2434 new CycleCollectRunnable(this, /* aCollectChildren = */ true); 2435 if (!runnable->Dispatch(this)) { 2436 NS_WARNING("Failed to CC worker!"); 2437 } 2438 } 2439 2440 void WorkerPrivate::OfflineStatusChangeEvent(bool aIsOffline) { 2441 AssertIsOnParentThread(); 2442 2443 RefPtr<OfflineStatusChangeRunnable> runnable = 2444 new OfflineStatusChangeRunnable(this, aIsOffline); 2445 if (!runnable->Dispatch(this)) { 2446 NS_WARNING("Failed to dispatch offline status change event!"); 2447 } 2448 } 2449 2450 void WorkerPrivate::OfflineStatusChangeEventInternal(bool aIsOffline) { 2451 auto data = mWorkerThreadAccessible.Access(); 2452 2453 // The worker is already in this state. No need to dispatch an event. 2454 if (data->mOnLine == !aIsOffline) { 2455 return; 2456 } 2457 2458 if (ShouldResistFingerprinting(RFPTarget::NetworkConnection)) { 2459 // We always report the worker as online if resistFingerprinting is 2460 // enabled, regardless of the actual network status. 2461 return; 2462 } 2463 2464 for (uint32_t index = 0; index < data->mChildWorkers.Length(); ++index) { 2465 data->mChildWorkers[index]->OfflineStatusChangeEvent(aIsOffline); 2466 } 2467 2468 data->mOnLine = !aIsOffline; 2469 WorkerGlobalScope* globalScope = GlobalScope(); 2470 RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator(); 2471 if (nav) { 2472 nav->SetOnLine(data->mOnLine); 2473 } 2474 2475 nsString eventType; 2476 if (aIsOffline) { 2477 eventType.AssignLiteral("offline"); 2478 } else { 2479 eventType.AssignLiteral("online"); 2480 } 2481 2482 RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr); 2483 2484 event->InitEvent(eventType, false, false); 2485 event->SetTrusted(true); 2486 2487 globalScope->DispatchEvent(*event); 2488 } 2489 2490 void WorkerPrivate::MemoryPressure() { 2491 AssertIsOnParentThread(); 2492 2493 RefPtr<MemoryPressureRunnable> runnable = new MemoryPressureRunnable(this); 2494 (void)NS_WARN_IF(!runnable->Dispatch(this)); 2495 } 2496 2497 RefPtr<WorkerPrivate::JSMemoryUsagePromise> WorkerPrivate::GetJSMemoryUsage() { 2498 AssertIsOnMainThread(); 2499 2500 { 2501 MutexAutoLock lock(mMutex); 2502 // If we have started shutting down the worker, do not dispatch a runnable 2503 // to measure its memory. 2504 if (ParentStatus() > Running) { 2505 return nullptr; 2506 } 2507 } 2508 2509 return InvokeAsync(ControlEventTarget(), __func__, []() { 2510 WorkerPrivate* wp = GetCurrentThreadWorkerPrivate(); 2511 MOZ_ASSERT(wp); 2512 wp->AssertIsOnWorkerThread(); 2513 MutexAutoLock lock(wp->mMutex); 2514 return JSMemoryUsagePromise::CreateAndResolve( 2515 js::GetGCHeapUsage(wp->mJSContext), __func__); 2516 }); 2517 } 2518 2519 void WorkerPrivate::WorkerScriptLoaded() { 2520 AssertIsOnMainThread(); 2521 2522 if (IsSharedWorker() || IsServiceWorker()) { 2523 // No longer need to hold references to the window or document we came from. 2524 mLoadInfo.mWindow = nullptr; 2525 mLoadInfo.mScriptContext = nullptr; 2526 } 2527 } 2528 2529 void WorkerPrivate::SetBaseURI(nsIURI* aBaseURI) { 2530 AssertIsOnMainThread(); 2531 2532 if (!mLoadInfo.mBaseURI) { 2533 NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!"); 2534 mLoadInfo.mResolvedScriptURI = aBaseURI; 2535 } 2536 2537 mLoadInfo.mBaseURI = aBaseURI; 2538 2539 if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) { 2540 mLocationInfo.mHref.Truncate(); 2541 } 2542 2543 mLocationInfo.mHostname.Truncate(); 2544 nsContentUtils::GetHostOrIPv6WithBrackets(aBaseURI, mLocationInfo.mHostname); 2545 2546 nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI)); 2547 if (!url || NS_FAILED(url->GetFilePath(mLocationInfo.mPathname))) { 2548 mLocationInfo.mPathname.Truncate(); 2549 } 2550 2551 nsCString temp; 2552 2553 if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) { 2554 mLocationInfo.mSearch.Assign('?'); 2555 mLocationInfo.mSearch.Append(temp); 2556 } 2557 2558 if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) { 2559 if (mLocationInfo.mHash.IsEmpty()) { 2560 mLocationInfo.mHash.Assign('#'); 2561 mLocationInfo.mHash.Append(temp); 2562 } 2563 } 2564 2565 if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) { 2566 mLocationInfo.mProtocol.Append(':'); 2567 } else { 2568 mLocationInfo.mProtocol.Truncate(); 2569 } 2570 2571 int32_t port; 2572 if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) { 2573 mLocationInfo.mPort.AppendInt(port); 2574 2575 nsAutoCString host(mLocationInfo.mHostname); 2576 host.Append(':'); 2577 host.Append(mLocationInfo.mPort); 2578 2579 mLocationInfo.mHost.Assign(host); 2580 } else { 2581 mLocationInfo.mHost.Assign(mLocationInfo.mHostname); 2582 } 2583 2584 nsContentUtils::GetWebExposedOriginSerialization(aBaseURI, 2585 mLocationInfo.mOrigin); 2586 } 2587 2588 nsresult WorkerPrivate::SetPrincipalsAndCSPOnMainThread( 2589 nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal, 2590 nsILoadGroup* aLoadGroup, nsIContentSecurityPolicy* aCsp) { 2591 return mLoadInfo.SetPrincipalsAndCSPOnMainThread( 2592 aPrincipal, aPartitionedPrincipal, aLoadGroup, aCsp); 2593 } 2594 2595 nsresult WorkerPrivate::SetPrincipalsAndCSPFromChannel(nsIChannel* aChannel) { 2596 return mLoadInfo.SetPrincipalsAndCSPFromChannel(aChannel); 2597 } 2598 2599 bool WorkerPrivate::FinalChannelPrincipalIsValid(nsIChannel* aChannel) { 2600 return mLoadInfo.FinalChannelPrincipalIsValid(aChannel); 2601 } 2602 2603 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 2604 bool WorkerPrivate::PrincipalURIMatchesScriptURL() { 2605 return mLoadInfo.PrincipalURIMatchesScriptURL(); 2606 } 2607 #endif 2608 2609 void WorkerPrivate::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup) { 2610 AssertIsOnMainThread(); 2611 2612 // The load group should have been overriden at init time. 2613 mLoadInfo.mInterfaceRequestor->MaybeAddBrowserChild(aBaseLoadGroup); 2614 } 2615 2616 void WorkerPrivate::UpdateIsOnContentBlockingAllowList( 2617 bool aOnContentBlockingAllowList) { 2618 AssertIsOnWorkerThread(); 2619 MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker()); 2620 2621 RefPtr<StrongWorkerRef> strongRef = StrongWorkerRef::Create( 2622 this, "WorkerPrivate::UpdateIsOnContentBlockingAllowList"); 2623 if (!strongRef) { 2624 return; 2625 } 2626 RefPtr<ThreadSafeWorkerRef> ref = new ThreadSafeWorkerRef(strongRef); 2627 DispatchToMainThread(NS_NewRunnableFunction( 2628 "WorkerPrivate::UpdateIsOnContentBlockingAllowList", 2629 [ref = std::move(ref), aOnContentBlockingAllowList] { 2630 ref->Private() 2631 ->mLoadInfo.mCookieJarSettingsArgs.isOnContentBlockingAllowList() = 2632 aOnContentBlockingAllowList; 2633 2634 nsCOMPtr<nsICookieJarSettings> workerCookieJarSettings; 2635 net::CookieJarSettings::Deserialize( 2636 ref->Private()->mLoadInfo.mCookieJarSettingsArgs, 2637 getter_AddRefs(workerCookieJarSettings)); 2638 bool shouldResistFingerprinting = 2639 nsContentUtils::ShouldResistFingerprinting_dangerous( 2640 ref->Private()->mLoadInfo.mPrincipal, 2641 "Service Workers exist outside a Document or Channel; as a " 2642 "property of the domain (and origin attributes). We don't have " 2643 "a " 2644 "CookieJarSettings to perform the *nested check*, but we can " 2645 "rely " 2646 "on the FPI/dFPI partition key check. The WorkerPrivate's " 2647 "ShouldResistFingerprinting function for the ServiceWorker " 2648 "depends " 2649 "on this boolean and will also consider an explicit RFPTarget.", 2650 RFPTarget::IsAlwaysEnabledForPrecompute) && 2651 !nsContentUtils::ETPSaysShouldNotResistFingerprinting( 2652 workerCookieJarSettings, false); 2653 2654 ref->Private() 2655 ->mLoadInfo.mCookieJarSettingsArgs.shouldResistFingerprinting() = 2656 shouldResistFingerprinting; 2657 ref->Private()->mLoadInfo.mShouldResistFingerprinting = 2658 shouldResistFingerprinting; 2659 })); 2660 2661 /* From: 2662 https://searchfox.org/mozilla-central/rev/964b8aa226c68bbf83c9ffc38984804734bb0de2/js/public/RealmOptions.h#316-318 2663 > RealmCreationOptions specify fundamental realm characteristics that must 2664 be specified when the realm is created, that can't be changed after the 2665 realm is created. 2666 */ 2667 /* 2668 nsCString locale; 2669 if (aEnabled) { 2670 locale = nsRFPService::GetSpoofedJSLocale(); 2671 } 2672 2673 MutexAutoLock lock(mMutex); 2674 mJSSettings.chromeRealmOptions.creationOptions().setForceUTC(aEnabled); 2675 mJSSettings.chromeRealmOptions.creationOptions().setAlwaysUseFdlibm(aEnabled); 2676 if (aEnabled) { 2677 mJSSettings.chromeRealmOptions.creationOptions().setLocaleCopyZ( 2678 locale.get()); 2679 } 2680 2681 mJSSettings.contentRealmOptions.creationOptions().setForceUTC(aEnabled); 2682 mJSSettings.contentRealmOptions.creationOptions().setAlwaysUseFdlibm( 2683 aEnabled); 2684 if (aEnabled) { 2685 mJSSettings.contentRealmOptions.creationOptions().setLocaleCopyZ( 2686 locale.get()); 2687 } 2688 */ 2689 } 2690 2691 bool WorkerPrivate::IsOnParentThread() const { 2692 if (GetParent()) { 2693 return GetParent()->IsOnWorkerThread(); 2694 } 2695 return NS_IsMainThread(); 2696 } 2697 2698 #ifdef DEBUG 2699 2700 void WorkerPrivate::AssertIsOnParentThread() const { 2701 if (GetParent()) { 2702 GetParent()->AssertIsOnWorkerThread(); 2703 } else { 2704 AssertIsOnMainThread(); 2705 } 2706 } 2707 2708 void WorkerPrivate::AssertInnerWindowIsCorrect() const { 2709 AssertIsOnParentThread(); 2710 2711 // Only care about top level workers from windows. 2712 if (mParent || !mLoadInfo.mWindow) { 2713 return; 2714 } 2715 2716 AssertIsOnMainThread(); 2717 2718 nsPIDOMWindowOuter* outer = mLoadInfo.mWindow->GetOuterWindow(); 2719 NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow, 2720 "Inner window no longer correct!"); 2721 } 2722 2723 #endif 2724 2725 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 2726 bool WorkerPrivate::PrincipalIsValid() const { 2727 return mLoadInfo.PrincipalIsValid(); 2728 } 2729 #endif 2730 2731 WorkerPrivate::WorkerThreadAccessible::WorkerThreadAccessible( 2732 WorkerPrivate* const aParent) 2733 : mNumWorkerRefsPreventingShutdownStart(0), 2734 mDebuggerEventLoopLevel(0), 2735 mNonblockingCCBackgroundActorCount(0), 2736 mErrorHandlerRecursionCount(0), 2737 mFrozen(false), 2738 mDebuggerInterruptRequested(false), 2739 mPeriodicGCTimerRunning(false), 2740 mIdleGCTimerRunning(false), 2741 mOnLine(aParent ? aParent->OnLine() : !NS_IsOffline()), 2742 mJSThreadExecutionGranted(false), 2743 mCCCollectedAnything(false) {} 2744 2745 namespace { 2746 2747 bool IsNewWorkerSecureContext(const WorkerPrivate* const aParent, 2748 const WorkerKind aWorkerKind, 2749 const WorkerLoadInfo& aLoadInfo) { 2750 if (aParent) { 2751 return aParent->IsSecureContext(); 2752 } 2753 2754 // Our secure context state depends on the kind of worker we have. 2755 2756 if (aLoadInfo.mPrincipal && aLoadInfo.mPrincipal->IsSystemPrincipal()) { 2757 return true; 2758 } 2759 2760 if (aWorkerKind == WorkerKindService) { 2761 return true; 2762 } 2763 2764 if (aLoadInfo.mSecureContext != WorkerLoadInfo::eNotSet) { 2765 return aLoadInfo.mSecureContext == WorkerLoadInfo::eSecureContext; 2766 } 2767 2768 MOZ_ASSERT_UNREACHABLE( 2769 "non-chrome worker that is not a service worker " 2770 "that has no parent and no associated window"); 2771 2772 return false; 2773 } 2774 2775 } // namespace 2776 2777 WorkerPrivate::WorkerPrivate( 2778 WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker, 2779 WorkerKind aWorkerKind, RequestCredentials aRequestCredentials, 2780 enum WorkerType aWorkerType, const nsAString& aWorkerName, 2781 const nsACString& aServiceWorkerScope, WorkerLoadInfo& aLoadInfo, 2782 nsString&& aId, const nsID& aAgentClusterId, 2783 const nsILoadInfo::CrossOriginOpenerPolicy aAgentClusterOpenerPolicy, 2784 CancellationCallback&& aCancellationCallback, 2785 TerminationCallback&& aTerminationCallback, 2786 mozilla::ipc::Endpoint<PRemoteWorkerNonLifeCycleOpControllerChild>&& 2787 aChildEp) 2788 : mMutex("WorkerPrivate Mutex"), 2789 mCondVar(mMutex, "WorkerPrivate CondVar"), 2790 mParent(aParent), 2791 mScriptURL(aScriptURL), 2792 mWorkerName(aWorkerName), 2793 mCredentialsMode(aRequestCredentials), 2794 mWorkerType(aWorkerType), // If the worker runs as a script or a module 2795 mWorkerKind(aWorkerKind), 2796 mCancellationCallback(std::move(aCancellationCallback)), 2797 mTerminationCallback(std::move(aTerminationCallback)), 2798 mLoadInfo(std::move(aLoadInfo)), 2799 mDebugger(nullptr), 2800 mDispatchingControlRunnables(0), 2801 mJSContext(nullptr), 2802 mPRThread(nullptr), 2803 mWorkerControlEventTarget(new WorkerEventTarget( 2804 this, WorkerEventTarget::Behavior::ControlOnly)), 2805 mWorkerHybridEventTarget( 2806 new WorkerEventTarget(this, WorkerEventTarget::Behavior::Hybrid)), 2807 mChildEp(std::move(aChildEp)), 2808 mRemoteDebuggerRegistered(false), 2809 mRemoteDebuggerReady(true), 2810 mIsQueued(false), 2811 mDebuggerBindingCondVar(mMutex, 2812 "WorkerPrivate RemoteDebuggerBindingCondVar"), 2813 mWorkerDebuggerEventTarget(new WorkerEventTarget( 2814 this, WorkerEventTarget::Behavior::DebuggerOnly)), 2815 mParentStatus(Pending), 2816 mStatus(Pending), 2817 mCreationTimeStamp(TimeStamp::Now()), 2818 mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC), 2819 mReportedUseCounters(false), 2820 mAgentClusterId(aAgentClusterId), 2821 mWorkerThreadAccessible(aParent), 2822 mPostSyncLoopOperations(0), 2823 mParentWindowPaused(false), 2824 mWorkerScriptExecutedSuccessfully(false), 2825 mFetchHandlerWasAdded(false), 2826 mMainThreadObjectsForgotten(false), 2827 mIsChromeWorker(aIsChromeWorker), 2828 mParentFrozen(false), 2829 mIsSecureContext( 2830 IsNewWorkerSecureContext(mParent, mWorkerKind, mLoadInfo)), 2831 mDebuggerRegistered(false), 2832 mIsInBackground(false), 2833 mDebuggerReady(true), 2834 mExtensionAPIAllowed(false), 2835 mIsInAutomation(false), 2836 mId(std::move(aId)), 2837 mAgentClusterOpenerPolicy(aAgentClusterOpenerPolicy), 2838 mIsPrivilegedAddonGlobal(false), 2839 mTopLevelWorkerFinishedRunnableCount(0), 2840 mWorkerFinishedRunnableCount(0), 2841 mFontVisibility(ComputeFontVisibility()) { 2842 LOG(WorkerLog(), ("WorkerPrivate::WorkerPrivate [%p]", this)); 2843 MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread()); 2844 2845 if (aParent) { 2846 aParent->AssertIsOnWorkerThread(); 2847 2848 // Note that this copies our parent's secure context state into mJSSettings. 2849 aParent->CopyJSSettings(mJSSettings); 2850 2851 MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext); 2852 2853 mIsInAutomation = aParent->IsInAutomation(); 2854 2855 MOZ_ASSERT(IsDedicatedWorker()); 2856 2857 if (aParent->mParentFrozen) { 2858 Freeze(nullptr); 2859 } 2860 2861 if (aParent->IsRunningInBackground()) { 2862 mIsInBackground = true; 2863 } 2864 if (aParent->IsPlayingAudio()) { 2865 mIsPlayingAudio = true; 2866 } 2867 2868 if (aParent->HasActivePeerConnections()) { 2869 mHasActivePeerConnections = true; 2870 } 2871 2872 mIsPrivilegedAddonGlobal = aParent->mIsPrivilegedAddonGlobal; 2873 } else { 2874 AssertIsOnMainThread(); 2875 2876 RuntimeService::GetDefaultJSSettings(mJSSettings); 2877 2878 { 2879 JS::RealmOptions& chromeRealmOptions = mJSSettings.chromeRealmOptions; 2880 JS::RealmOptions& contentRealmOptions = mJSSettings.contentRealmOptions; 2881 2882 xpc::InitGlobalObjectOptions( 2883 chromeRealmOptions, UsesSystemPrincipal(), mIsSecureContext, 2884 ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC), 2885 ShouldResistFingerprinting(RFPTarget::JSMathFdlibm), 2886 ShouldResistFingerprinting(RFPTarget::JSLocale), ""_ns, u""_ns); 2887 xpc::InitGlobalObjectOptions( 2888 contentRealmOptions, UsesSystemPrincipal(), mIsSecureContext, 2889 ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC), 2890 ShouldResistFingerprinting(RFPTarget::JSMathFdlibm), 2891 ShouldResistFingerprinting(RFPTarget::JSLocale), ""_ns, u""_ns); 2892 2893 // Check if it's a privileged addon executing in order to allow access 2894 // to SharedArrayBuffer 2895 if (mLoadInfo.mPrincipal) { 2896 if (auto* policy = 2897 BasePrincipal::Cast(mLoadInfo.mPrincipal)->AddonPolicy()) { 2898 if (policy->IsPrivileged() && 2899 ExtensionPolicyService::GetSingleton().IsExtensionProcess()) { 2900 // Privileged extensions are allowed to use SharedArrayBuffer in 2901 // their extension process, but never in content scripts in 2902 // content processes. 2903 mIsPrivilegedAddonGlobal = true; 2904 } 2905 2906 if (StaticPrefs:: 2907 extensions_backgroundServiceWorker_enabled_AtStartup() && 2908 mWorkerKind == WorkerKindService && 2909 policy->IsManifestBackgroundWorker(mScriptURL)) { 2910 // Only allows ExtensionAPI for extension service workers 2911 // that are declared in the extension manifest json as 2912 // the background service worker. 2913 mExtensionAPIAllowed = true; 2914 } 2915 } 2916 } 2917 2918 // The SharedArrayBuffer global constructor property should not be present 2919 // in a fresh global object when shared memory objects aren't allowed 2920 // (because COOP/COEP support isn't enabled, or because COOP/COEP don't 2921 // act to isolate this worker to a separate process). 2922 const bool defineSharedArrayBufferConstructor = IsSharedMemoryAllowed(); 2923 chromeRealmOptions.creationOptions() 2924 .setDefineSharedArrayBufferConstructor( 2925 defineSharedArrayBufferConstructor); 2926 contentRealmOptions.creationOptions() 2927 .setDefineSharedArrayBufferConstructor( 2928 defineSharedArrayBufferConstructor); 2929 } 2930 2931 mIsInAutomation = xpc::IsInAutomation(); 2932 2933 // Our parent can get suspended after it initiates the async creation 2934 // of a new worker thread. In this case suspend the new worker as well. 2935 if (mLoadInfo.mWindow && 2936 nsGlobalWindowInner::Cast(mLoadInfo.mWindow)->IsSuspended()) { 2937 ParentWindowPaused(); 2938 } 2939 2940 if (mLoadInfo.mWindow && 2941 nsGlobalWindowInner::Cast(mLoadInfo.mWindow)->IsFrozen()) { 2942 Freeze(mLoadInfo.mWindow); 2943 } 2944 2945 if (mLoadInfo.mWindow && mLoadInfo.mWindow->GetOuterWindow() && 2946 mLoadInfo.mWindow->GetOuterWindow()->IsBackground()) { 2947 mIsInBackground = true; 2948 } 2949 2950 if (mLoadInfo.mWindow && 2951 nsGlobalWindowInner::Cast(mLoadInfo.mWindow)->IsPlayingAudio()) { 2952 SetIsPlayingAudio(true); 2953 } 2954 2955 if (mLoadInfo.mWindow && nsGlobalWindowInner::Cast(mLoadInfo.mWindow) 2956 ->HasActivePeerConnections()) { 2957 SetActivePeerConnections(true); 2958 } 2959 } 2960 2961 nsCOMPtr<nsISerialEventTarget> target; 2962 2963 // A child worker just inherits the parent workers ThrottledEventQueue 2964 // and main thread target for now. This is mainly due to the restriction 2965 // that ThrottledEventQueue can only be created on the main thread at the 2966 // moment. 2967 if (aParent) { 2968 mMainThreadEventTargetForMessaging = 2969 aParent->mMainThreadEventTargetForMessaging; 2970 mMainThreadEventTarget = aParent->mMainThreadEventTarget; 2971 mMainThreadDebuggeeEventTarget = aParent->mMainThreadDebuggeeEventTarget; 2972 return; 2973 } 2974 2975 MOZ_ASSERT(NS_IsMainThread()); 2976 target = GetWindow() 2977 ? GetWindow()->GetBrowsingContextGroup()->GetWorkerEventQueue() 2978 : nullptr; 2979 2980 if (!target) { 2981 target = GetMainThreadSerialEventTarget(); 2982 MOZ_DIAGNOSTIC_ASSERT(target); 2983 } 2984 2985 // Throttle events to the main thread using a ThrottledEventQueue specific to 2986 // this tree of worker threads. 2987 mMainThreadEventTargetForMessaging = 2988 ThrottledEventQueue::Create(target, "Worker queue for messaging"); 2989 mMainThreadEventTarget = ThrottledEventQueue::Create( 2990 GetMainThreadSerialEventTarget(), "Worker queue", 2991 nsIRunnablePriority::PRIORITY_MEDIUMHIGH); 2992 mMainThreadDebuggeeEventTarget = 2993 ThrottledEventQueue::Create(target, "Worker debuggee queue"); 2994 if (IsParentWindowPaused() || IsFrozen()) { 2995 MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(true)); 2996 } 2997 } 2998 2999 WorkerPrivate::~WorkerPrivate() { 3000 MOZ_DIAGNOSTIC_ASSERT(mTopLevelWorkerFinishedRunnableCount == 0); 3001 MOZ_DIAGNOSTIC_ASSERT(mWorkerFinishedRunnableCount == 0); 3002 MOZ_DIAGNOSTIC_ASSERT(mPendingJSAsyncTasks.empty()); 3003 3004 mWorkerDebuggerEventTarget->ForgetWorkerPrivate(this); 3005 3006 mWorkerControlEventTarget->ForgetWorkerPrivate(this); 3007 3008 // We force the hybrid event target to forget the thread when we 3009 // enter the Killing state, but we do it again here to be safe. 3010 // Its possible that we may be created and destroyed without progressing 3011 // to Killing via some obscure code path. 3012 mWorkerHybridEventTarget->ForgetWorkerPrivate(this); 3013 } 3014 3015 WorkerPrivate::AgentClusterIdAndCoop 3016 WorkerPrivate::ComputeAgentClusterIdAndCoop(WorkerPrivate* aParent, 3017 WorkerKind aWorkerKind, 3018 WorkerLoadInfo* aLoadInfo, 3019 bool aIsChromeWorker) { 3020 nsILoadInfo::CrossOriginOpenerPolicy agentClusterCoop = 3021 nsILoadInfo::OPENER_POLICY_UNSAFE_NONE; 3022 3023 if (aParent) { 3024 MOZ_ASSERT(aWorkerKind == WorkerKind::WorkerKindDedicated); 3025 3026 return {aParent->AgentClusterId(), aParent->mAgentClusterOpenerPolicy}; 3027 } 3028 3029 AssertIsOnMainThread(); 3030 3031 if (aWorkerKind == WorkerKind::WorkerKindService || 3032 aWorkerKind == WorkerKind::WorkerKindShared) { 3033 return {aLoadInfo->mAgentClusterId, agentClusterCoop}; 3034 } 3035 3036 if (aLoadInfo->mWindow) { 3037 Document* doc = aLoadInfo->mWindow->GetExtantDoc(); 3038 MOZ_DIAGNOSTIC_ASSERT(doc); 3039 RefPtr<DocGroup> docGroup = doc->GetDocGroup(); 3040 3041 nsID agentClusterId = 3042 docGroup ? docGroup->AgentClusterId() : nsID::GenerateUUID(); 3043 3044 BrowsingContext* bc = aLoadInfo->mWindow->GetBrowsingContext(); 3045 MOZ_DIAGNOSTIC_ASSERT(bc); 3046 return {agentClusterId, bc->Top()->GetOpenerPolicy()}; 3047 } 3048 3049 // Chrome workers share an AgentCluster with the XPC module global. This 3050 // allows things like shared memory and WASM modules to be transferred between 3051 // chrome workers and system JS. 3052 // Also set COOP+COEP flags to allow access to shared memory. 3053 if (aIsChromeWorker) { 3054 if (nsIGlobalObject* systemGlobal = 3055 xpc::NativeGlobal(xpc::PrivilegedJunkScope())) { 3056 nsID agentClusterId = systemGlobal->GetAgentClusterId().valueOrFrom( 3057 [] { return nsID::GenerateUUID(); }); 3058 return { 3059 agentClusterId, 3060 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP}; 3061 } 3062 } 3063 3064 // If the window object was failed to be set into the WorkerLoadInfo, we 3065 // make the worker into another agent cluster group instead of failures. 3066 return {nsID::GenerateUUID(), agentClusterCoop}; 3067 } 3068 3069 // static 3070 already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor( 3071 JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker, 3072 WorkerKind aWorkerKind, RequestCredentials aRequestCredentials, 3073 enum WorkerType aWorkerType, const nsAString& aWorkerName, 3074 const nsACString& aServiceWorkerScope, WorkerLoadInfo* aLoadInfo, 3075 ErrorResult& aRv, nsString aId, 3076 CancellationCallback&& aCancellationCallback, 3077 TerminationCallback&& aTerminationCallback, 3078 mozilla::ipc::Endpoint<PRemoteWorkerNonLifeCycleOpControllerChild>&& 3079 aChildEp) { 3080 WorkerPrivate* parent = 3081 NS_IsMainThread() ? nullptr : GetCurrentThreadWorkerPrivate(); 3082 3083 // If this is a sub-worker, we need to keep the parent worker alive until this 3084 // one is registered. 3085 RefPtr<StrongWorkerRef> workerRef; 3086 if (parent) { 3087 parent->AssertIsOnWorkerThread(); 3088 3089 workerRef = StrongWorkerRef::Create(parent, "WorkerPrivate::Constructor"); 3090 if (NS_WARN_IF(!workerRef)) { 3091 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 3092 return nullptr; 3093 } 3094 } else { 3095 AssertIsOnMainThread(); 3096 } 3097 3098 Maybe<WorkerLoadInfo> stackLoadInfo; 3099 if (!aLoadInfo) { 3100 stackLoadInfo.emplace(); 3101 3102 nsresult rv = GetLoadInfo( 3103 aCx, nullptr, parent, aScriptURL, aWorkerType, aRequestCredentials, 3104 aIsChromeWorker, InheritLoadGroup, aWorkerKind, stackLoadInfo.ptr()); 3105 aRv.MightThrowJSException(); 3106 if (NS_FAILED(rv)) { 3107 workerinternals::ReportLoadError(aRv, rv, aScriptURL); 3108 return nullptr; 3109 } 3110 3111 aLoadInfo = stackLoadInfo.ptr(); 3112 } 3113 3114 // NB: This has to be done before creating the WorkerPrivate, because it will 3115 // attempt to use static variables that are initialized in the RuntimeService 3116 // constructor. 3117 RuntimeService* runtimeService; 3118 3119 if (!parent) { 3120 runtimeService = RuntimeService::GetOrCreateService(); 3121 if (!runtimeService) { 3122 aRv.Throw(NS_ERROR_FAILURE); 3123 return nullptr; 3124 } 3125 } else { 3126 runtimeService = RuntimeService::GetService(); 3127 } 3128 3129 MOZ_ASSERT(runtimeService); 3130 3131 // Don't create a worker with the shutting down RuntimeService. 3132 if (runtimeService->IsShuttingDown()) { 3133 aRv.Throw(NS_ERROR_UNEXPECTED); 3134 return nullptr; 3135 } 3136 3137 AgentClusterIdAndCoop idAndCoop = ComputeAgentClusterIdAndCoop( 3138 parent, aWorkerKind, aLoadInfo, aIsChromeWorker); 3139 3140 RefPtr<WorkerPrivate> worker = new WorkerPrivate( 3141 parent, aScriptURL, aIsChromeWorker, aWorkerKind, aRequestCredentials, 3142 aWorkerType, aWorkerName, aServiceWorkerScope, *aLoadInfo, std::move(aId), 3143 idAndCoop.mId, idAndCoop.mCoop, std::move(aCancellationCallback), 3144 std::move(aTerminationCallback), std::move(aChildEp)); 3145 3146 // Gecko contexts always have an explicitly-set default locale (set by 3147 // XPJSRuntime::Initialize for the main thread, set by 3148 // WorkerThreadPrimaryRunnable::Run for workers just before running worker 3149 // code), so this is never SpiderMonkey's builtin default locale. 3150 JS::UniqueChars defaultLocale = JS_GetDefaultLocale(aCx); 3151 if (NS_WARN_IF(!defaultLocale)) { 3152 aRv.Throw(NS_ERROR_UNEXPECTED); 3153 return nullptr; 3154 } 3155 3156 worker->mDefaultLocale = std::move(defaultLocale); 3157 3158 // Create remote debugger endpoint here for child binding in 3159 // WorkerThreadPrimaryRunnable 3160 // worker->CreateRemoteDebuggerEndpoints(); 3161 3162 if (!runtimeService->RegisterWorker(*worker)) { 3163 aRv.Throw(NS_ERROR_UNEXPECTED); 3164 return nullptr; 3165 } 3166 3167 // From this point on (worker thread has been started) we 3168 // must keep ourself alive. We can now only be cleared by 3169 // ClearSelfAndParentEventTargetRef(). 3170 worker->mSelfRef = worker; 3171 worker->mParentRef = MakeRefPtr<WorkerParentRef>(worker); 3172 3173 // Enable remote worker debugger when the worker is really scheduled. 3174 /* 3175 if (!worker->mIsQueued) { 3176 worker->EnableRemoteDebugger(); 3177 } 3178 */ 3179 3180 worker->EnableDebugger(); 3181 3182 MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid()); 3183 3184 UniquePtr<SerializedStackHolder> stack; 3185 if (worker->IsWatchedByDevTools()) { 3186 stack = GetCurrentStackForNetMonitor(aCx); 3187 } 3188 3189 // This should be non-null for dedicated workers and null for Shared and 3190 // Service workers. All Encoding values are static and will live as long 3191 // as the process and the convention is to therefore use raw pointers. 3192 const mozilla::Encoding* aDocumentEncoding = 3193 NS_IsMainThread() && !worker->GetParent() && worker->GetDocument() 3194 ? worker->GetDocument()->GetDocumentCharacterSet().get() 3195 : nullptr; 3196 3197 RefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable( 3198 worker, std::move(stack), aScriptURL, aDocumentEncoding); 3199 if (!compiler->Dispatch(worker)) { 3200 aRv.Throw(NS_ERROR_UNEXPECTED); 3201 return nullptr; 3202 } 3203 3204 return worker.forget(); 3205 } 3206 3207 // Mark worker private as running in the background tab 3208 // for further throttling 3209 void WorkerPrivate::SetIsRunningInBackground() { 3210 AssertIsOnParentThread(); 3211 3212 RefPtr<ChangeBackgroundStateRunnable> runnable = 3213 new ChangeBackgroundStateRunnable(this, true); 3214 runnable->Dispatch(this); 3215 3216 LOG(WorkerLog(), ("SetIsRunningInBackground [%p]", this)); 3217 } 3218 3219 void WorkerPrivate::SetIsRunningInForeground() { 3220 AssertIsOnParentThread(); 3221 3222 RefPtr<ChangeBackgroundStateRunnable> runnable = 3223 new ChangeBackgroundStateRunnable(this, false); 3224 runnable->Dispatch(this); 3225 3226 LOG(WorkerLog(), ("SetIsRunningInForeground [%p]", this)); 3227 } 3228 3229 void WorkerPrivate::SetIsPlayingAudio(bool aIsPlayingAudio) { 3230 AssertIsOnParentThread(); 3231 3232 RefPtr<ChangePlaybackStateRunnable> runnable = 3233 new ChangePlaybackStateRunnable(this, aIsPlayingAudio); 3234 runnable->Dispatch(this); 3235 3236 AUTO_PROFILER_MARKER_UNTYPED("WorkerPrivate::SetIsPlayingAudio", DOM, {}); 3237 LOG(WorkerLog(), ("SetIsPlayingAudio [%p]", this)); 3238 } 3239 3240 void WorkerPrivate::SetActivePeerConnections(bool aHasPeerConnections) { 3241 AssertIsOnParentThread(); 3242 3243 RefPtr<ChangeActivePeerConnectionsRunnable> runnable = 3244 new ChangeActivePeerConnectionsRunnable(this, aHasPeerConnections); 3245 runnable->Dispatch(this); 3246 3247 AUTO_PROFILER_MARKER_UNTYPED("WorkerPrivate::SetActivePeerConnections", DOM, 3248 {}); 3249 LOG(WorkerLog(), ("SetActivePeerConnections [%p]", this)); 3250 } 3251 3252 nsresult WorkerPrivate::SetIsDebuggerReady(bool aReady) { 3253 AssertIsOnMainThread(); 3254 MutexAutoLock lock(mMutex); 3255 3256 if (mDebuggerReady == aReady) { 3257 return NS_OK; 3258 } 3259 3260 if (!aReady && mDebuggerRegistered) { 3261 // The debugger can only be marked as not ready during registration. 3262 return NS_ERROR_FAILURE; 3263 } 3264 3265 mDebuggerReady = aReady; 3266 3267 bool debuggerRegistered = mDebuggerRegistered && (mRemoteDebuggerRegistered || 3268 XRE_IsParentProcess()); 3269 3270 if (aReady && debuggerRegistered) { 3271 // Dispatch all the delayed runnables without releasing the lock, to ensure 3272 // that the order in which debuggee runnables execute is the same as the 3273 // order in which they were originally dispatched. 3274 auto pending = std::move(mDelayedDebuggeeRunnables); 3275 for (uint32_t i = 0; i < pending.Length(); i++) { 3276 RefPtr<WorkerRunnable> runnable = std::move(pending[i]); 3277 nsresult rv = DispatchLockHeld(runnable.forget(), nullptr, lock); 3278 NS_ENSURE_SUCCESS(rv, rv); 3279 } 3280 MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty()); 3281 } 3282 3283 return NS_OK; 3284 } 3285 3286 // static 3287 nsresult WorkerPrivate::GetLoadInfo( 3288 JSContext* aCx, nsPIDOMWindowInner* aWindow, WorkerPrivate* aParent, 3289 const nsAString& aScriptURL, const enum WorkerType& aWorkerType, 3290 const RequestCredentials& aCredentials, bool aIsChromeWorker, 3291 LoadGroupBehavior aLoadGroupBehavior, WorkerKind aWorkerKind, 3292 WorkerLoadInfo* aLoadInfo) { 3293 using namespace mozilla::dom::workerinternals; 3294 3295 MOZ_ASSERT(aCx); 3296 MOZ_ASSERT_IF(NS_IsMainThread(), 3297 aCx == nsContentUtils::GetCurrentJSContext()); 3298 3299 if (aWindow) { 3300 AssertIsOnMainThread(); 3301 } 3302 3303 WorkerLoadInfo loadInfo; 3304 nsresult rv; 3305 3306 if (aParent) { 3307 aParent->AssertIsOnWorkerThread(); 3308 3309 // If the parent is going away give up now. 3310 WorkerStatus parentStatus; 3311 { 3312 MutexAutoLock lock(aParent->mMutex); 3313 parentStatus = aParent->mStatus; 3314 } 3315 3316 if (parentStatus > Running) { 3317 return NS_ERROR_FAILURE; 3318 } 3319 3320 // Passing a pointer to our stack loadInfo is safe here because this 3321 // method uses a sync runnable to get the channel from the main thread. 3322 rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL, aWorkerType, 3323 aCredentials, loadInfo); 3324 if (NS_FAILED(rv)) { 3325 MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent)); 3326 return rv; 3327 } 3328 3329 // Now that we've spun the loop there's no guarantee that our parent is 3330 // still alive. We may have received control messages initiating shutdown. 3331 { 3332 MutexAutoLock lock(aParent->mMutex); 3333 parentStatus = aParent->mStatus; 3334 } 3335 3336 if (parentStatus > Running) { 3337 MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent)); 3338 return NS_ERROR_FAILURE; 3339 } 3340 3341 loadInfo.mTrials = aParent->Trials(); 3342 loadInfo.mDomain = aParent->Domain(); 3343 loadInfo.mFromWindow = aParent->IsFromWindow(); 3344 loadInfo.mWindowID = aParent->WindowID(); 3345 loadInfo.mAssociatedBrowsingContextID = 3346 aParent->AssociatedBrowsingContextID(); 3347 loadInfo.mStorageAccess = aParent->StorageAccess(); 3348 loadInfo.mUseRegularPrincipal = aParent->UseRegularPrincipal(); 3349 loadInfo.mUsingStorageAccess = aParent->UsingStorageAccess(); 3350 loadInfo.mCookieJarSettings = aParent->CookieJarSettings(); 3351 if (loadInfo.mCookieJarSettings) { 3352 loadInfo.mCookieJarSettingsArgs = aParent->CookieJarSettingsArgs(); 3353 } 3354 loadInfo.mOriginAttributes = aParent->GetOriginAttributes(); 3355 loadInfo.mServiceWorkersTestingInWindow = 3356 aParent->ServiceWorkersTestingInWindow(); 3357 loadInfo.mIsThirdPartyContext = aParent->IsThirdPartyContext(); 3358 loadInfo.mShouldResistFingerprinting = aParent->ShouldResistFingerprinting( 3359 RFPTarget::IsAlwaysEnabledForPrecompute); 3360 loadInfo.mOverriddenFingerprintingSettings = 3361 aParent->GetOverriddenFingerprintingSettings(); 3362 loadInfo.mParentController = aParent->GlobalScope()->GetController(); 3363 loadInfo.mWatchedByDevTools = aParent->IsWatchedByDevTools(); 3364 loadInfo.mIsOn3PCBExceptionList = aParent->IsOn3PCBExceptionList(); 3365 } else { 3366 AssertIsOnMainThread(); 3367 3368 // Make sure that the IndexedDatabaseManager is set up 3369 IndexedDatabaseManager* idm = IndexedDatabaseManager::GetOrCreate(); 3370 if (idm) { 3371 (void)NS_WARN_IF(NS_FAILED(idm->EnsureLocale())); 3372 } else { 3373 NS_WARNING("Failed to get IndexedDatabaseManager!"); 3374 } 3375 3376 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); 3377 MOZ_ASSERT(ssm); 3378 3379 bool isChrome = nsContentUtils::IsSystemCaller(aCx); 3380 3381 // First check to make sure the caller has permission to make a privileged 3382 // worker if they called the ChromeWorker/ChromeSharedWorker constructor. 3383 if (aIsChromeWorker && !isChrome) { 3384 return NS_ERROR_DOM_SECURITY_ERR; 3385 } 3386 3387 // Chrome callers (whether creating a ChromeWorker or Worker) always get the 3388 // system principal here as they're allowed to load anything. The script 3389 // loader will refuse to run any script that does not also have the system 3390 // principal. 3391 if (isChrome) { 3392 rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mLoadingPrincipal)); 3393 NS_ENSURE_SUCCESS(rv, rv); 3394 } 3395 3396 // See if we're being called from a window. 3397 nsCOMPtr<nsPIDOMWindowInner> globalWindow = aWindow; 3398 if (!globalWindow) { 3399 globalWindow = xpc::CurrentWindowOrNull(aCx); 3400 } 3401 3402 nsCOMPtr<Document> document; 3403 Maybe<ClientInfo> clientInfo; 3404 3405 if (globalWindow) { 3406 // Only use the current inner window, and only use it if the caller can 3407 // access it. 3408 if (nsPIDOMWindowOuter* outerWindow = globalWindow->GetOuterWindow()) { 3409 loadInfo.mWindow = outerWindow->GetCurrentInnerWindow(); 3410 } 3411 3412 loadInfo.mTrials = 3413 OriginTrials::FromWindow(nsGlobalWindowInner::Cast(loadInfo.mWindow)); 3414 3415 BrowsingContext* browsingContext = globalWindow->GetBrowsingContext(); 3416 3417 // TODO: fix this for SharedWorkers with multiple documents (bug 3418 // 1177935) 3419 loadInfo.mServiceWorkersTestingInWindow = 3420 browsingContext && 3421 browsingContext->Top()->ServiceWorkersTestingEnabled(); 3422 3423 if (!loadInfo.mWindow || 3424 (globalWindow != loadInfo.mWindow && 3425 !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) { 3426 return NS_ERROR_DOM_SECURITY_ERR; 3427 } 3428 3429 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow); 3430 MOZ_ASSERT(sgo); 3431 3432 loadInfo.mScriptContext = sgo->GetContext(); 3433 NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE); 3434 3435 // If we're called from a window then we can dig out the principal and URI 3436 // from the document. 3437 document = loadInfo.mWindow->GetExtantDoc(); 3438 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE); 3439 3440 loadInfo.mBaseURI = document->GetDocBaseURI(); 3441 loadInfo.mLoadGroup = document->GetDocumentLoadGroup(); 3442 NS_ENSURE_TRUE(loadInfo.mLoadGroup, NS_ERROR_FAILURE); 3443 3444 clientInfo = globalWindow->GetClientInfo(); 3445 3446 // Use the document's NodePrincipal as loading principal if we're not 3447 // being called from chrome. 3448 if (!loadInfo.mLoadingPrincipal) { 3449 loadInfo.mLoadingPrincipal = document->NodePrincipal(); 3450 NS_ENSURE_TRUE(loadInfo.mLoadingPrincipal, NS_ERROR_FAILURE); 3451 3452 // We use the document's base domain to limit the number of workers 3453 // each domain can create. For sandboxed documents, we use the domain 3454 // of their first non-sandboxed document, walking up until we find 3455 // one. If we can't find one, we fall back to using the GUID of the 3456 // null principal as the base domain. 3457 if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) { 3458 nsCOMPtr<Document> tmpDoc = document; 3459 do { 3460 tmpDoc = tmpDoc->GetInProcessParentDocument(); 3461 } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN); 3462 3463 if (tmpDoc) { 3464 // There was an unsandboxed ancestor, yay! 3465 nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal(); 3466 rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain); 3467 NS_ENSURE_SUCCESS(rv, rv); 3468 } else { 3469 // No unsandboxed ancestor, use our GUID. 3470 rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain); 3471 NS_ENSURE_SUCCESS(rv, rv); 3472 } 3473 } else { 3474 // Document creating the worker is not sandboxed. 3475 rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain); 3476 NS_ENSURE_SUCCESS(rv, rv); 3477 } 3478 } 3479 3480 NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup, 3481 loadInfo.mLoadingPrincipal), 3482 NS_ERROR_FAILURE); 3483 3484 nsCOMPtr<nsIPermissionManager> permMgr = 3485 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); 3486 NS_ENSURE_SUCCESS(rv, rv); 3487 3488 uint32_t perm; 3489 rv = permMgr->TestPermissionFromPrincipal(loadInfo.mLoadingPrincipal, 3490 "systemXHR"_ns, &perm); 3491 NS_ENSURE_SUCCESS(rv, rv); 3492 3493 loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION; 3494 3495 loadInfo.mWatchedByDevTools = 3496 browsingContext && browsingContext->WatchedByDevTools(); 3497 3498 loadInfo.mReferrerInfo = 3499 ReferrerInfo::CreateForFetch(loadInfo.mLoadingPrincipal, document); 3500 loadInfo.mFromWindow = true; 3501 loadInfo.mWindowID = globalWindow->WindowID(); 3502 loadInfo.mAssociatedBrowsingContextID = 3503 globalWindow->GetBrowsingContext()->Id(); 3504 loadInfo.mStorageAccess = StorageAllowedForWindow(globalWindow); 3505 loadInfo.mUseRegularPrincipal = document->UseRegularPrincipal(); 3506 loadInfo.mUsingStorageAccess = document->UsingStorageAccess(); 3507 loadInfo.mShouldResistFingerprinting = 3508 document->ShouldResistFingerprinting( 3509 RFPTarget::IsAlwaysEnabledForPrecompute); 3510 loadInfo.mOverriddenFingerprintingSettings = 3511 document->GetOverriddenFingerprintingSettings(); 3512 loadInfo.mIsOn3PCBExceptionList = document->IsOn3PCBExceptionList(); 3513 3514 // This is an hack to deny the storage-access-permission for workers of 3515 // sub-iframes. 3516 if (loadInfo.mUsingStorageAccess && 3517 StorageAllowedForDocument(document) != StorageAccess::eAllow) { 3518 loadInfo.mUsingStorageAccess = false; 3519 } 3520 loadInfo.mIsThirdPartyContext = 3521 AntiTrackingUtils::IsThirdPartyWindow(globalWindow, nullptr); 3522 loadInfo.mCookieJarSettings = document->CookieJarSettings(); 3523 if (loadInfo.mCookieJarSettings) { 3524 auto* cookieJarSettings = 3525 net::CookieJarSettings::Cast(loadInfo.mCookieJarSettings); 3526 cookieJarSettings->Serialize(loadInfo.mCookieJarSettingsArgs); 3527 } 3528 StoragePrincipalHelper::GetRegularPrincipalOriginAttributes( 3529 document, loadInfo.mOriginAttributes); 3530 loadInfo.mParentController = globalWindow->GetController(); 3531 loadInfo.mSecureContext = loadInfo.mWindow->IsSecureContext() 3532 ? WorkerLoadInfo::eSecureContext 3533 : WorkerLoadInfo::eInsecureContext; 3534 } else { 3535 // Not a window 3536 MOZ_ASSERT(isChrome); 3537 3538 // We're being created outside of a window. Need to figure out the script 3539 // that is creating us in order for us to use relative URIs later on. 3540 JS::AutoFilename fileName; 3541 if (JS::DescribeScriptedCaller(&fileName, aCx)) { 3542 // In most cases, fileName is URI. In a few other cases 3543 // (e.g. xpcshell), fileName is a file path. Ideally, we would 3544 // prefer testing whether fileName parses as an URI and fallback 3545 // to file path in case of error, but Windows file paths have 3546 // the interesting property that they can be parsed as bogus 3547 // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C", 3548 // hostname "Windows", path "Tmp"), which defeats this algorithm. 3549 // Therefore, we adopt the opposite convention. 3550 nsCOMPtr<nsIFile> scriptFile; 3551 rv = NS_NewNativeLocalFile(nsDependentCString(fileName.get()), 3552 getter_AddRefs(scriptFile)); 3553 if (NS_SUCCEEDED(rv)) { 3554 rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI), scriptFile); 3555 } 3556 if (NS_FAILED(rv)) { 3557 // As expected, fileName is not a path, so proceed with 3558 // a uri. 3559 rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI), fileName.get()); 3560 } 3561 if (NS_FAILED(rv)) { 3562 return rv; 3563 } 3564 } 3565 loadInfo.mXHRParamsAllowed = true; 3566 loadInfo.mFromWindow = false; 3567 loadInfo.mWindowID = UINT64_MAX; 3568 loadInfo.mStorageAccess = StorageAccess::eAllow; 3569 loadInfo.mUseRegularPrincipal = true; 3570 loadInfo.mUsingStorageAccess = false; 3571 loadInfo.mCookieJarSettings = 3572 mozilla::net::CookieJarSettings::Create(loadInfo.mLoadingPrincipal); 3573 loadInfo.mShouldResistFingerprinting = 3574 nsContentUtils::ShouldResistFingerprinting_dangerous( 3575 loadInfo.mLoadingPrincipal, 3576 "Unusual situation - we have no document or CookieJarSettings", 3577 RFPTarget::IsAlwaysEnabledForPrecompute); 3578 MOZ_ASSERT(loadInfo.mCookieJarSettings); 3579 auto* cookieJarSettings = 3580 net::CookieJarSettings::Cast(loadInfo.mCookieJarSettings); 3581 cookieJarSettings->Serialize(loadInfo.mCookieJarSettingsArgs); 3582 3583 loadInfo.mOriginAttributes = OriginAttributes(); 3584 loadInfo.mIsThirdPartyContext = false; 3585 loadInfo.mIsOn3PCBExceptionList = false; 3586 } 3587 3588 MOZ_ASSERT(loadInfo.mLoadingPrincipal); 3589 MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty()); 3590 3591 if (!loadInfo.mLoadGroup || aLoadGroupBehavior == OverrideLoadGroup) { 3592 OverrideLoadInfoLoadGroup(loadInfo, loadInfo.mLoadingPrincipal); 3593 } 3594 MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup, 3595 loadInfo.mLoadingPrincipal)); 3596 3597 // Top level workers' main script use the document charset for the script 3598 // uri encoding. 3599 nsCOMPtr<nsIURI> url; 3600 rv = nsContentUtils::NewURIWithDocumentCharset( 3601 getter_AddRefs(url), aScriptURL, document, loadInfo.mBaseURI); 3602 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR); 3603 3604 rv = ChannelFromScriptURLMainThread( 3605 loadInfo.mLoadingPrincipal, document, loadInfo.mLoadGroup, url, 3606 aWorkerType, aCredentials, clientInfo, ContentPolicyType(aWorkerKind), 3607 loadInfo.mCookieJarSettings, loadInfo.mReferrerInfo, 3608 getter_AddRefs(loadInfo.mChannel)); 3609 NS_ENSURE_SUCCESS(rv, rv); 3610 3611 rv = NS_GetFinalChannelURI(loadInfo.mChannel, 3612 getter_AddRefs(loadInfo.mResolvedScriptURI)); 3613 NS_ENSURE_SUCCESS(rv, rv); 3614 3615 // We need the correct hasStoragePermission flag for the channel here since 3616 // we will do a content blocking check later when we set the storage 3617 // principal for the worker. The channel here won't be opened when we do the 3618 // check later, so the hasStoragePermission flag is incorrect. To address 3619 // this, We copy the hasStoragePermission flag from the document if there is 3620 // a window. The worker is created as the same origin of the window. So, the 3621 // worker is supposed to have the same storage permission as the window as 3622 // well as the hasStoragePermission flag. 3623 nsCOMPtr<nsILoadInfo> channelLoadInfo = loadInfo.mChannel->LoadInfo(); 3624 rv = channelLoadInfo->SetStoragePermission( 3625 loadInfo.mUsingStorageAccess ? nsILoadInfo::HasStoragePermission 3626 : nsILoadInfo::NoStoragePermission); 3627 NS_ENSURE_SUCCESS(rv, rv); 3628 3629 rv = loadInfo.SetPrincipalsAndCSPFromChannel(loadInfo.mChannel); 3630 NS_ENSURE_SUCCESS(rv, rv); 3631 } 3632 3633 MOZ_DIAGNOSTIC_ASSERT(loadInfo.mLoadingPrincipal); 3634 MOZ_DIAGNOSTIC_ASSERT(loadInfo.PrincipalIsValid()); 3635 3636 *aLoadInfo = std::move(loadInfo); 3637 return NS_OK; 3638 } 3639 3640 // static 3641 void WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo, 3642 nsIPrincipal* aPrincipal) { 3643 MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor); 3644 MOZ_ASSERT(aLoadInfo.mLoadingPrincipal == aPrincipal); 3645 3646 aLoadInfo.mInterfaceRequestor = 3647 new WorkerLoadInfo::InterfaceRequestor(aPrincipal, aLoadInfo.mLoadGroup); 3648 aLoadInfo.mInterfaceRequestor->MaybeAddBrowserChild(aLoadInfo.mLoadGroup); 3649 3650 // NOTE: this defaults the load context to: 3651 // - private browsing = false 3652 // - content = true 3653 // - use remote tabs = false 3654 nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID); 3655 3656 nsresult rv = 3657 loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor); 3658 MOZ_ALWAYS_SUCCEEDS(rv); 3659 3660 aLoadInfo.mLoadGroup = std::move(loadGroup); 3661 3662 MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup, aPrincipal)); 3663 } 3664 3665 void WorkerPrivate::RunLoopNeverRan() { 3666 LOG(WorkerLog(), ("WorkerPrivate::RunLoopNeverRan [%p]", this)); 3667 // RunLoopNeverRan is only called in WorkerThreadPrimaryRunnable::Run() to 3668 // handle cases 3669 // 1. Fail to get BackgroundChild for the worker thread or 3670 // 2. Fail to initialize the worker's JS context 3671 // However, mPreStartRunnables had already dispatched in 3672 // WorkerThread::SetWorkerPrivateInWorkerThread() where beforing above jobs 3673 // start. So we need to clean up these dispatched runnables for the worker 3674 // thread. 3675 3676 auto data = mWorkerThreadAccessible.Access(); 3677 RefPtr<WorkerThread> thread; 3678 { 3679 MutexAutoLock lock(mMutex); 3680 3681 if (!mPreStartRunnables.IsEmpty()) { 3682 for (const RefPtr<WorkerThreadRunnable>& runnable : mPreStartRunnables) { 3683 runnable->mCleanPreStartDispatching = true; 3684 PROFILER_MARKER("WorkerPrivate::RunLoopNeverRan", OTHER, {}, 3685 TerminatingFlowMarker, 3686 Flow::FromPointer(runnable.get())); 3687 } 3688 mPreStartRunnables.Clear(); 3689 } 3690 3691 // Switch State to Dead 3692 mStatus = Dead; 3693 thread = mThread; 3694 } 3695 3696 // Clear the dispatched mPreStartRunnables. 3697 if (thread && NS_HasPendingEvents(thread)) { 3698 NS_ProcessPendingEvents(nullptr); 3699 } 3700 3701 // After mStatus is set to Dead there can be no more 3702 // WorkerControlRunnables so no need to lock here. 3703 if (!mControlQueue.IsEmpty()) { 3704 WorkerRunnable* runnable = nullptr; 3705 while (mControlQueue.Pop(runnable)) { 3706 runnable->Release(); 3707 } 3708 } 3709 3710 // There should be no StrongWorkerRefs, child Workers, and Timeouts, but 3711 // WeakWorkerRefs could. WorkerThreadPrimaryRunnable could have created a 3712 // PerformanceStorageWorker which holds a WeakWorkerRef. 3713 // Notify WeakWorkerRefs with Dead status. 3714 NotifyWorkerRefs(Dead); 3715 } 3716 3717 void WorkerPrivate::UnrootGlobalScopes() { 3718 LOG(WorkerLog(), ("WorkerPrivate::UnrootGlobalScopes [%p]", this)); 3719 auto data = mWorkerThreadAccessible.Access(); 3720 3721 RefPtr<WorkerDebuggerGlobalScope> debugScope = data->mDebuggerScope.forget(); 3722 if (debugScope) { 3723 MOZ_ASSERT(debugScope->mWorkerPrivate == this); 3724 } 3725 RefPtr<WorkerGlobalScope> scope = data->mScope.forget(); 3726 if (scope) { 3727 MOZ_ASSERT(scope->mWorkerPrivate == this); 3728 } 3729 } 3730 3731 void WorkerPrivate::DoRunLoop(JSContext* aCx) { 3732 LOG(WorkerLog(), ("WorkerPrivate::DoRunLoop [%p]", this)); 3733 auto data = mWorkerThreadAccessible.Access(); 3734 MOZ_RELEASE_ASSERT(!GetExecutionManager()); 3735 3736 RefPtr<WorkerThread> thread; 3737 { 3738 MutexAutoLock lock(mMutex); 3739 mJSContext = aCx; 3740 // mThread is set before we enter, and is never changed during DoRunLoop. 3741 // copy to local so we don't trigger mutex analysis 3742 MOZ_ASSERT(mThread); 3743 thread = mThread; 3744 3745 MOZ_ASSERT(mStatus == Pending); 3746 mStatus = Running; 3747 3748 // Now, start to run the event loop, mPreStartRunnables can be cleared, 3749 // since when get here, Worker initialization has done successfully. 3750 mPreStartRunnables.Clear(); 3751 } 3752 3753 // Create IPC between the content process worker thread and the parent 3754 // process background thread for non-life cycle related operations of 3755 // SharedWorker/ServiceWorker 3756 if (mChildEp.IsValid()) { 3757 mRemoteWorkerNonLifeCycleOpController = 3758 RemoteWorkerNonLifeCycleOpControllerChild::Create(); 3759 MOZ_ASSERT_DEBUG_OR_FUZZING(mRemoteWorkerNonLifeCycleOpController); 3760 mChildEp.Bind(mRemoteWorkerNonLifeCycleOpController); 3761 } 3762 3763 // Now that we've done that, we can go ahead and set up our AutoJSAPI. We 3764 // can't before this point, because it can't find the right JSContext before 3765 // then, since it gets it from our mJSContext. 3766 AutoJSAPI jsapi; 3767 jsapi.Init(); 3768 MOZ_ASSERT(jsapi.cx() == aCx); 3769 3770 EnableMemoryReporter(); 3771 3772 InitializeGCTimers(); 3773 3774 bool checkFinalGCCC = 3775 StaticPrefs::dom_workers_GCCC_on_potentially_last_event(); 3776 3777 bool debuggerRunnablesPending = false; 3778 bool normalRunnablesPending = false; 3779 auto noRunnablesPendingAndKeepAlive = 3780 [&debuggerRunnablesPending, &normalRunnablesPending, &thread, this]() 3781 MOZ_REQUIRES(mMutex) { 3782 // We want to keep both pending flags always updated while looping. 3783 debuggerRunnablesPending = !mDebuggerQueue.IsEmpty(); 3784 normalRunnablesPending = NS_HasPendingEvents(thread); 3785 3786 bool anyRunnablesPending = !mControlQueue.IsEmpty() || 3787 debuggerRunnablesPending || 3788 normalRunnablesPending; 3789 bool keepWorkerAlive = mStatus == Running || HasActiveWorkerRefs(); 3790 3791 return (!anyRunnablesPending && keepWorkerAlive); 3792 }; 3793 3794 for (;;) { 3795 WorkerStatus currentStatus; 3796 3797 if (checkFinalGCCC) { 3798 // If we get here after the last event ran but someone holds a WorkerRef 3799 // and there is no other logic to release that WorkerRef than lazily 3800 // through GC/CC, we might block forever on the next WaitForWorkerEvents. 3801 // Every object holding a WorkerRef should really have a straight, 3802 // deterministic line from the WorkerRef's callback being invoked to the 3803 // WorkerRef being released which is supported by strong-references that 3804 // can't form a cycle. 3805 bool mayNeedFinalGCCC = false; 3806 { 3807 MutexAutoLock lock(mMutex); 3808 3809 currentStatus = mStatus; 3810 mayNeedFinalGCCC = 3811 (mStatus >= Canceling && HasActiveWorkerRefs() && 3812 !debuggerRunnablesPending && !normalRunnablesPending && 3813 data->mPerformedShutdownAfterLastContentTaskExecuted); 3814 } 3815 if (mayNeedFinalGCCC) { 3816 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 3817 // WorkerRef::ReleaseWorker will check this flag via 3818 // AssertIsNotPotentiallyLastGCCCRunning 3819 data->mIsPotentiallyLastGCCCRunning = true; 3820 #endif 3821 // GarbageCollectInternal will trigger both GC and CC 3822 GarbageCollectInternal(aCx, true /* aShrinking */, 3823 true /* aCollectChildren */); 3824 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 3825 data->mIsPotentiallyLastGCCCRunning = false; 3826 #endif 3827 } 3828 } 3829 3830 { 3831 MutexAutoLock lock(mMutex); 3832 if (checkFinalGCCC && currentStatus != mStatus) { 3833 // Something moved our status while we were supposed to check for a 3834 // potentially needed GC/CC. Just check again. 3835 continue; 3836 } 3837 3838 // Wait for a runnable to arrive that we can execute, or for it to be okay 3839 // to shutdown this worker once all holders have been removed. 3840 // Holders may be removed from inside normal runnables, but we don't 3841 // check for that after processing normal runnables, so we need to let 3842 // control flow to the shutdown logic without blocking. 3843 while (noRunnablesPendingAndKeepAlive()) { 3844 // We pop out to this loop when there are no pending events. 3845 // If we don't reset these, we may not re-enter ProcessNextEvent() 3846 // until we have events to process, and it may seem like we have 3847 // an event running for a very long time. 3848 thread->SetRunningEventDelay(TimeDuration(), TimeStamp()); 3849 3850 mWorkerLoopIsIdle = true; 3851 3852 WaitForWorkerEvents(); 3853 3854 mWorkerLoopIsIdle = false; 3855 } 3856 3857 auto result = ProcessAllControlRunnablesLocked(); 3858 if (result != ProcessAllControlRunnablesResult::Nothing) { 3859 // Update all saved runnable flags for side effect for the 3860 // loop check about transitioning to Killing below. 3861 (void)noRunnablesPendingAndKeepAlive(); 3862 } 3863 3864 currentStatus = mStatus; 3865 } 3866 3867 // Status transitions to Closing/Canceling and there are no SyncLoops, 3868 // set global start dying, disconnect EventTargetObjects and 3869 // WebTaskScheduler. 3870 // The Worker might switch to the "Killing" immediately then directly exits 3871 // DoRunLoop(). Before exiting the DoRunLoop(), explicitly disconnecting the 3872 // WorkerGlobalScope's EventTargetObject here would help to fail runnable 3873 // dispatching when the Worker is in the status changing. 3874 if (currentStatus >= Closing && 3875 !data->mPerformedShutdownAfterLastContentTaskExecuted) { 3876 data->mPerformedShutdownAfterLastContentTaskExecuted.Flip(); 3877 if (data->mScope) { 3878 data->mScope->NoteTerminating(); 3879 data->mScope->DisconnectGlobalTeardownObservers(); 3880 if (WebTaskScheduler* scheduler = 3881 data->mScope->GetExistingScheduler()) { 3882 scheduler->Disconnect(); 3883 } 3884 } 3885 } 3886 3887 // Transition from Canceling to Killing and exit this loop when: 3888 // * All (non-weak) WorkerRefs have been released. 3889 // * There are no runnables pending. This is intended to let same-thread 3890 // dispatches as part of cleanup be able to run to completion, but any 3891 // logic that still wants async things to happen should be holding a 3892 // StrongWorkerRef. 3893 if (currentStatus != Running && !HasActiveWorkerRefs() && 3894 !normalRunnablesPending && !debuggerRunnablesPending) { 3895 // Now we are ready to kill the worker thread. 3896 if (currentStatus == Canceling) { 3897 NotifyInternal(Killing); 3898 3899 #ifdef DEBUG 3900 { 3901 MutexAutoLock lock(mMutex); 3902 currentStatus = mStatus; 3903 } 3904 MOZ_ASSERT(currentStatus == Killing); 3905 #else 3906 currentStatus = Killing; 3907 #endif 3908 } 3909 3910 // If we're supposed to die then we should exit the loop. 3911 if (currentStatus == Killing) { 3912 // We are about to destroy worker, report all use counters. 3913 ReportUseCounters(); 3914 3915 // Flush uncaught rejections immediately, without 3916 // waiting for a next tick. 3917 PromiseDebugging::FlushUncaughtRejections(); 3918 3919 // DisableRemoteDebuggerOnWorkerThread(true /*aForShutdown*/); 3920 3921 ShutdownGCTimers(); 3922 3923 DisableMemoryReporter(); 3924 3925 // Move the timer out with the mutex held but only drop the ref 3926 // when the mutex is not held. 3927 nsCOMPtr<nsITimer> timer; 3928 { 3929 MutexAutoLock lock(mMutex); 3930 mStatus = Dead; 3931 // Wait for the dispatching ControlRunnables complete. 3932 while (mDispatchingControlRunnables) { 3933 mCondVar.Wait(); 3934 } 3935 mJSContext = nullptr; 3936 mDebuggerInterruptTimer.swap(timer); 3937 } 3938 timer = nullptr; 3939 3940 // After mStatus is set to Dead there can be no more 3941 // WorkerControlRunnables so no need to lock here. 3942 if (!mControlQueue.IsEmpty()) { 3943 LOG(WorkerLog(), 3944 ("WorkerPrivate::DoRunLoop [%p] dropping control runnables in " 3945 "Dead status", 3946 this)); 3947 WorkerRunnable* runnable = nullptr; 3948 while (mControlQueue.Pop(runnable)) { 3949 runnable->Cancel(); 3950 runnable->Release(); 3951 } 3952 } 3953 3954 // We do not need the timeouts any more, they have been canceled 3955 // by NotifyInternal(Killing) above if they were active. 3956 UnlinkTimeouts(); 3957 3958 return; 3959 } 3960 } 3961 3962 if (debuggerRunnablesPending || normalRunnablesPending) { 3963 // Start the periodic GC timer if it is not already running. 3964 SetGCTimerMode(PeriodicTimer); 3965 } 3966 3967 if (debuggerRunnablesPending) { 3968 ProcessSingleDebuggerRunnable(); 3969 3970 { 3971 MutexAutoLock lock(mMutex); 3972 debuggerRunnablesPending = !mDebuggerQueue.IsEmpty(); 3973 } 3974 3975 if (debuggerRunnablesPending) { 3976 WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope(); 3977 // If the worker was canceled before ever creating its content global 3978 // then mCancelBeforeWorkerScopeConstructed could have been flipped and 3979 // all of the WorkerDebuggerRunnables canceled, so the debugger global 3980 // would never have been created. 3981 if (globalScope) { 3982 // Now *might* be a good time to GC. Let the JS engine make the 3983 // decision. 3984 JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject()); 3985 JS_MaybeGC(aCx); 3986 } 3987 } 3988 } else if (normalRunnablesPending) { 3989 // Process a single runnable from the main queue. 3990 NS_ProcessNextEvent(thread, false); 3991 3992 normalRunnablesPending = NS_HasPendingEvents(thread); 3993 if (normalRunnablesPending && GlobalScope()) { 3994 // Now *might* be a good time to GC. Let the JS engine make the 3995 // decision. 3996 JSAutoRealm ar(aCx, GlobalScope()->GetGlobalJSObject()); 3997 JS_MaybeGC(aCx); 3998 } 3999 } 4000 4001 // Checking the background actors if needed, any runnable execution could 4002 // release background actors which blocks GC/CC on 4003 // WorkerPrivate::mParentEventTargetRef. 4004 if (currentStatus < Canceling) { 4005 UpdateCCFlag(CCFlag::CheckBackgroundActors); 4006 } 4007 4008 if (!debuggerRunnablesPending && !normalRunnablesPending) { 4009 // Both the debugger event queue and the normal event queue has been 4010 // exhausted, cancel the periodic GC timer and schedule the idle GC timer. 4011 SetGCTimerMode(IdleTimer); 4012 } 4013 4014 // If the worker thread is spamming the main thread faster than it can 4015 // process the work, then pause the worker thread until the main thread 4016 // catches up. 4017 size_t queuedEvents = mMainThreadEventTargetForMessaging->Length() + 4018 mMainThreadDebuggeeEventTarget->Length(); 4019 if (queuedEvents > 5000) { 4020 // Note, postMessage uses mMainThreadDebuggeeEventTarget! 4021 mMainThreadDebuggeeEventTarget->AwaitIdle(); 4022 } 4023 } 4024 4025 MOZ_CRASH("Shouldn't get here!"); 4026 } 4027 4028 namespace { 4029 /** 4030 * If there is a current CycleCollectedJSContext, return its recursion depth, 4031 * otherwise return 1. 4032 * 4033 * In the edge case where a worker is starting up so late that PBackground is 4034 * already shutting down, the cycle collected context will never be created, 4035 * but we will need to drain the event loop in ClearMainEventQueue. This will 4036 * result in a normal NS_ProcessPendingEvents invocation which will call 4037 * WorkerPrivate::OnProcessNextEvent and WorkerPrivate::AfterProcessNextEvent 4038 * which want to handle the need to process control runnables and perform a 4039 * sanity check assertion, respectively. 4040 * 4041 * We claim a depth of 1 when there's no CCJS because this most corresponds to 4042 * reality, but this doesn't meant that other code might want to drain various 4043 * runnable queues as part of this cleanup. 4044 */ 4045 uint32_t GetEffectiveEventLoopRecursionDepth() { 4046 auto* ccjs = CycleCollectedJSContext::Get(); 4047 if (ccjs) { 4048 return ccjs->RecursionDepth(); 4049 } 4050 4051 return 1; 4052 } 4053 4054 } // namespace 4055 4056 void WorkerPrivate::OnProcessNextEvent() { 4057 AssertIsOnWorkerThread(); 4058 4059 uint32_t recursionDepth = GetEffectiveEventLoopRecursionDepth(); 4060 MOZ_ASSERT(recursionDepth); 4061 4062 // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop. 4063 // However, it's possible that non-worker C++ could spin its own nested event 4064 // loop, and in that case we must ensure that we continue to process control 4065 // runnables here. 4066 if (recursionDepth > 1 && mSyncLoopStack.Length() < recursionDepth - 1) { 4067 (void)ProcessAllControlRunnables(); 4068 // There's no running JS, and no state to revalidate, so we can ignore the 4069 // return value. 4070 } 4071 } 4072 4073 void WorkerPrivate::AfterProcessNextEvent() { 4074 AssertIsOnWorkerThread(); 4075 MOZ_ASSERT(GetEffectiveEventLoopRecursionDepth()); 4076 } 4077 4078 nsISerialEventTarget* WorkerPrivate::MainThreadEventTargetForMessaging() { 4079 return mMainThreadEventTargetForMessaging; 4080 } 4081 4082 nsresult WorkerPrivate::DispatchToMainThreadForMessaging( 4083 nsIRunnable* aRunnable, nsIEventTarget::DispatchFlags aFlags) { 4084 nsCOMPtr<nsIRunnable> r = aRunnable; 4085 return DispatchToMainThreadForMessaging(r.forget(), aFlags); 4086 } 4087 4088 nsresult WorkerPrivate::DispatchToMainThreadForMessaging( 4089 already_AddRefed<nsIRunnable> aRunnable, 4090 nsIEventTarget::DispatchFlags aFlags) { 4091 return mMainThreadEventTargetForMessaging->Dispatch(std::move(aRunnable), 4092 aFlags); 4093 } 4094 4095 nsISerialEventTarget* WorkerPrivate::MainThreadEventTarget() { 4096 return mMainThreadEventTarget; 4097 } 4098 4099 nsresult WorkerPrivate::DispatchToMainThread( 4100 nsIRunnable* aRunnable, nsIEventTarget::DispatchFlags aFlags) { 4101 nsCOMPtr<nsIRunnable> r = aRunnable; 4102 return DispatchToMainThread(r.forget(), aFlags); 4103 } 4104 4105 nsresult WorkerPrivate::DispatchToMainThread( 4106 already_AddRefed<nsIRunnable> aRunnable, 4107 nsIEventTarget::DispatchFlags aFlags) { 4108 return mMainThreadEventTarget->Dispatch(std::move(aRunnable), aFlags); 4109 } 4110 4111 nsresult WorkerPrivate::DispatchDebuggeeToMainThread( 4112 already_AddRefed<WorkerRunnable> aRunnable, 4113 nsIEventTarget::DispatchFlags aFlags) { 4114 RefPtr<WorkerRunnable> debuggeeRunnable = std::move(aRunnable); 4115 MOZ_ASSERT_DEBUG_OR_FUZZING(debuggeeRunnable->IsDebuggeeRunnable()); 4116 return mMainThreadDebuggeeEventTarget->Dispatch(debuggeeRunnable.forget(), 4117 aFlags); 4118 } 4119 4120 nsISerialEventTarget* WorkerPrivate::ControlEventTarget() { 4121 return mWorkerControlEventTarget; 4122 } 4123 4124 nsISerialEventTarget* WorkerPrivate::HybridEventTarget() { 4125 return mWorkerHybridEventTarget; 4126 } 4127 4128 ClientType WorkerPrivate::GetClientType() const { 4129 switch (Kind()) { 4130 case WorkerKindDedicated: 4131 return ClientType::Worker; 4132 case WorkerKindShared: 4133 return ClientType::Sharedworker; 4134 case WorkerKindService: 4135 return ClientType::Serviceworker; 4136 default: 4137 MOZ_CRASH("unknown worker type!"); 4138 } 4139 } 4140 4141 UniquePtr<ClientSource> WorkerPrivate::CreateClientSource() { 4142 auto data = mWorkerThreadAccessible.Access(); 4143 MOZ_ASSERT(!data->mScope, "Client should be created before the global"); 4144 4145 UniquePtr<ClientSource> clientSource; 4146 if (IsServiceWorker()) { 4147 clientSource = ClientManager::CreateSourceFromInfo( 4148 GetSourceInfo(), mWorkerHybridEventTarget); 4149 } else { 4150 clientSource = ClientManager::CreateSource( 4151 GetClientType(), mWorkerHybridEventTarget, 4152 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker( 4153 this) 4154 ? GetPartitionedPrincipalInfo() 4155 : GetPrincipalInfo()); 4156 } 4157 MOZ_DIAGNOSTIC_ASSERT(clientSource); 4158 4159 clientSource->SetAgentClusterId(mAgentClusterId); 4160 4161 if (data->mFrozen) { 4162 clientSource->Freeze(); 4163 } 4164 4165 // Shortly after the client is reserved we will try loading the main script 4166 // for the worker. This may get intercepted by the ServiceWorkerManager 4167 // which will then try to create a ClientHandle. Its actually possible for 4168 // the main thread to create this ClientHandle before our IPC message creating 4169 // the ClientSource completes. To avoid this race we synchronously ping our 4170 // parent Client actor here. This ensure the worker ClientSource is created 4171 // in the parent before the main thread might try reaching it with a 4172 // ClientHandle. 4173 // 4174 // An alternative solution would have been to handle the out-of-order 4175 // operations on the parent side. We could have created a small window where 4176 // we allow ClientHandle objects to exist without a ClientSource. We would 4177 // then time out these handles if they stayed orphaned for too long. This 4178 // approach would be much more complex, but also avoid this extra bit of 4179 // latency when starting workers. 4180 // 4181 // Note, we only have to do this for workers that can be controlled by a 4182 // service worker. So avoid the sync overhead here if we are starting a 4183 // service worker or a chrome worker. 4184 if (Kind() != WorkerKindService && !IsChromeWorker()) { 4185 clientSource->WorkerSyncPing(this); 4186 } 4187 4188 return clientSource; 4189 } 4190 4191 bool WorkerPrivate::EnsureCSPEventListener() { 4192 if (!mCSPEventListener) { 4193 mCSPEventListener = WorkerCSPEventListener::Create(this); 4194 if (NS_WARN_IF(!mCSPEventListener)) { 4195 return false; 4196 } 4197 } 4198 return true; 4199 } 4200 4201 nsICSPEventListener* WorkerPrivate::CSPEventListener() const { 4202 MOZ_ASSERT(mCSPEventListener); 4203 return mCSPEventListener; 4204 } 4205 4206 void WorkerPrivate::EnsurePerformanceStorage() { 4207 AssertIsOnWorkerThread(); 4208 4209 if (!mPerformanceStorage) { 4210 mPerformanceStorage = PerformanceStorageWorker::Create(this); 4211 } 4212 } 4213 4214 bool WorkerPrivate::GetExecutionGranted() const { 4215 auto data = mWorkerThreadAccessible.Access(); 4216 return data->mJSThreadExecutionGranted; 4217 } 4218 4219 void WorkerPrivate::SetExecutionGranted(bool aGranted) { 4220 auto data = mWorkerThreadAccessible.Access(); 4221 data->mJSThreadExecutionGranted = aGranted; 4222 } 4223 4224 void WorkerPrivate::ScheduleTimeSliceExpiration(uint32_t aDelay) { 4225 auto data = mWorkerThreadAccessible.Access(); 4226 4227 if (!data->mTSTimer) { 4228 data->mTSTimer = NS_NewTimer(); 4229 MOZ_ALWAYS_SUCCEEDS(data->mTSTimer->SetTarget(mWorkerControlEventTarget)); 4230 } 4231 4232 // Whenever an event is scheduled on the WorkerControlEventTarget an 4233 // interrupt is automatically requested which causes us to yield JS execution 4234 // and the next JS execution in the queue to execute. 4235 // This allows for simple code reuse of the existing interrupt callback code 4236 // used for control events. 4237 MOZ_ALWAYS_SUCCEEDS(data->mTSTimer->InitWithNamedFuncCallback( 4238 [](nsITimer* Timer, void* aClosure) { return; }, nullptr, aDelay, 4239 nsITimer::TYPE_ONE_SHOT, "TimeSliceExpirationTimer"_ns)); 4240 } 4241 4242 void WorkerPrivate::CancelTimeSliceExpiration() { 4243 auto data = mWorkerThreadAccessible.Access(); 4244 MOZ_ALWAYS_SUCCEEDS(data->mTSTimer->Cancel()); 4245 } 4246 4247 JSExecutionManager* WorkerPrivate::GetExecutionManager() const { 4248 auto data = mWorkerThreadAccessible.Access(); 4249 return data->mExecutionManager.get(); 4250 } 4251 4252 void WorkerPrivate::SetExecutionManager(JSExecutionManager* aManager) { 4253 auto data = mWorkerThreadAccessible.Access(); 4254 data->mExecutionManager = aManager; 4255 } 4256 4257 void WorkerPrivate::ExecutionReady() { 4258 auto data = mWorkerThreadAccessible.Access(); 4259 { 4260 MutexAutoLock lock(mMutex); 4261 if (mStatus >= Canceling) { 4262 return; 4263 } 4264 } 4265 4266 data->mScope->MutableClientSourceRef().WorkerExecutionReady(this); 4267 4268 if (ExtensionAPIAllowed()) { 4269 extensions::CreateAndDispatchInitWorkerContextRunnable(); 4270 } 4271 } 4272 4273 void WorkerPrivate::InitializeGCTimers() { 4274 auto data = mWorkerThreadAccessible.Access(); 4275 4276 // We need timers for GC. The basic plan is to run a non-shrinking GC 4277 // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running. 4278 // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to 4279 // run a shrinking GC. 4280 data->mPeriodicGCTimer = NS_NewTimer(); 4281 data->mIdleGCTimer = NS_NewTimer(); 4282 4283 data->mPeriodicGCTimerRunning = false; 4284 data->mIdleGCTimerRunning = false; 4285 } 4286 4287 void WorkerPrivate::SetGCTimerMode(GCTimerMode aMode) { 4288 auto data = mWorkerThreadAccessible.Access(); 4289 4290 if (!data->mPeriodicGCTimer || !data->mIdleGCTimer) { 4291 // GC timers have been cleared already. 4292 return; 4293 } 4294 4295 if (aMode == NoTimer) { 4296 MOZ_ALWAYS_SUCCEEDS(data->mPeriodicGCTimer->Cancel()); 4297 data->mPeriodicGCTimerRunning = false; 4298 MOZ_ALWAYS_SUCCEEDS(data->mIdleGCTimer->Cancel()); 4299 data->mIdleGCTimerRunning = false; 4300 return; 4301 } 4302 4303 WorkerStatus status; 4304 { 4305 MutexAutoLock lock(mMutex); 4306 status = mStatus; 4307 } 4308 4309 if (status >= Killing) { 4310 ShutdownGCTimers(); 4311 return; 4312 } 4313 4314 // If the idle timer is running, don't cancel it when the periodic timer 4315 // is scheduled since we do want shrinking GC to be called occasionally. 4316 if (aMode == PeriodicTimer && data->mPeriodicGCTimerRunning) { 4317 return; 4318 } 4319 4320 if (aMode == IdleTimer) { 4321 if (!data->mPeriodicGCTimerRunning) { 4322 // Since running idle GC cancels both GC timers, after that we want 4323 // first at least periodic GC timer getting activated, since that tells 4324 // us that there have been some non-control tasks to process. Otherwise 4325 // idle GC timer would keep running all the time. 4326 return; 4327 } 4328 4329 // Cancel the periodic timer now, since the event loop is (in the common 4330 // case) empty now. 4331 MOZ_ALWAYS_SUCCEEDS(data->mPeriodicGCTimer->Cancel()); 4332 data->mPeriodicGCTimerRunning = false; 4333 4334 if (data->mIdleGCTimerRunning) { 4335 return; 4336 } 4337 } 4338 4339 MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer); 4340 4341 uint32_t delay = 0; 4342 int16_t type = nsITimer::TYPE_ONE_SHOT; 4343 nsTimerCallbackFunc callback = nullptr; 4344 nsCString name; 4345 nsITimer* timer = nullptr; 4346 4347 if (aMode == PeriodicTimer) { 4348 delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000; 4349 type = nsITimer::TYPE_REPEATING_SLACK; 4350 callback = PeriodicGCTimerCallback; 4351 name.AssignLiteral("dom::PeriodicGCTimerCallback"); 4352 timer = data->mPeriodicGCTimer; 4353 data->mPeriodicGCTimerRunning = true; 4354 LOG(WorkerLog(), ("Worker %p scheduled periodic GC timer\n", this)); 4355 } else { 4356 delay = IDLE_GC_TIMER_DELAY_SEC * 1000; 4357 type = nsITimer::TYPE_ONE_SHOT; 4358 callback = IdleGCTimerCallback; 4359 name.AssignLiteral("dom::IdleGCTimerCallback"); 4360 timer = data->mIdleGCTimer; 4361 data->mIdleGCTimerRunning = true; 4362 LOG(WorkerLog(), ("Worker %p scheduled idle GC timer\n", this)); 4363 } 4364 4365 MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(mWorkerControlEventTarget)); 4366 MOZ_ALWAYS_SUCCEEDS( 4367 timer->InitWithNamedFuncCallback(callback, this, delay, type, name)); 4368 } 4369 4370 void WorkerPrivate::ShutdownGCTimers() { 4371 auto data = mWorkerThreadAccessible.Access(); 4372 4373 MOZ_ASSERT(!data->mPeriodicGCTimer == !data->mIdleGCTimer); 4374 4375 if (!data->mPeriodicGCTimer && !data->mIdleGCTimer) { 4376 return; 4377 } 4378 4379 // Always make sure the timers are canceled. 4380 MOZ_ALWAYS_SUCCEEDS(data->mPeriodicGCTimer->Cancel()); 4381 MOZ_ALWAYS_SUCCEEDS(data->mIdleGCTimer->Cancel()); 4382 4383 LOG(WorkerLog(), ("Worker %p killed the GC timers\n", this)); 4384 4385 data->mPeriodicGCTimer = nullptr; 4386 data->mIdleGCTimer = nullptr; 4387 data->mPeriodicGCTimerRunning = false; 4388 data->mIdleGCTimerRunning = false; 4389 } 4390 4391 bool WorkerPrivate::InterruptCallback(JSContext* aCx) { 4392 auto data = mWorkerThreadAccessible.Access(); 4393 4394 AutoYieldJSThreadExecution yield; 4395 4396 // If we are here it's because a WorkerControlRunnable has been dispatched. 4397 // The runnable could be processed here or it could have already been 4398 // processed by a sync event loop. 4399 // The most important thing this method must do, is to decide if the JS 4400 // execution should continue or not. If the runnable returns an error or if 4401 // the worker status is >= Canceling, we should stop the JS execution. 4402 4403 MOZ_ASSERT(!JS_IsExceptionPending(aCx)); 4404 4405 bool mayContinue = true; 4406 bool scheduledIdleGC = false; 4407 4408 for (;;) { 4409 // Run all control events now. 4410 auto result = ProcessAllControlRunnables(); 4411 if (result == ProcessAllControlRunnablesResult::Abort) { 4412 mayContinue = false; 4413 } 4414 4415 bool mayFreeze = data->mFrozen; 4416 4417 { 4418 MutexAutoLock lock(mMutex); 4419 4420 if (mayFreeze) { 4421 mayFreeze = mStatus <= Running; 4422 } 4423 4424 if (mStatus >= Canceling) { 4425 mayContinue = false; 4426 } 4427 } 4428 4429 if (!mayContinue || !mayFreeze) { 4430 break; 4431 } 4432 4433 // Cancel the periodic GC timer here before freezing. The idle GC timer 4434 // will clean everything up once it runs. 4435 if (!scheduledIdleGC) { 4436 SetGCTimerMode(IdleTimer); 4437 scheduledIdleGC = true; 4438 } 4439 4440 while ((mayContinue = MayContinueRunning())) { 4441 MutexAutoLock lock(mMutex); 4442 if (!mControlQueue.IsEmpty()) { 4443 break; 4444 } 4445 4446 WaitForWorkerEvents(); 4447 } 4448 } 4449 4450 if (!mayContinue) { 4451 // We want only uncatchable exceptions here. 4452 NS_ASSERTION(!JS_IsExceptionPending(aCx), 4453 "Should not have an exception set here!"); 4454 return false; 4455 } 4456 4457 // Make sure the periodic timer gets turned back on here. 4458 SetGCTimerMode(PeriodicTimer); 4459 4460 if (data->mDebuggerInterruptRequested) { 4461 bool debuggerRunnablesPending = false; 4462 { 4463 MutexAutoLock lock(mMutex); 4464 debuggerRunnablesPending = !mDebuggerQueue.IsEmpty(); 4465 } 4466 if (debuggerRunnablesPending) { 4467 // Prevents interrupting the debugger's own logic unless it has called 4468 // back into content 4469 WorkerGlobalScope* globalScope = GlobalScope(); 4470 if (globalScope) { 4471 JSObject* global = JS::CurrentGlobalOrNull(aCx); 4472 if (global && global == globalScope->GetGlobalJSObject()) { 4473 while (debuggerRunnablesPending) { 4474 ProcessSingleDebuggerRunnable(); 4475 { 4476 MutexAutoLock lock(mMutex); 4477 debuggerRunnablesPending = !mDebuggerQueue.IsEmpty(); 4478 } 4479 } 4480 } 4481 } 4482 } 4483 data->mDebuggerInterruptRequested = false; 4484 } 4485 4486 return true; 4487 } 4488 4489 void WorkerPrivate::CloseInternal() { 4490 AssertIsOnWorkerThread(); 4491 NotifyInternal(Closing); 4492 } 4493 4494 bool WorkerPrivate::IsOnCurrentThread() { 4495 // May be called on any thread! 4496 4497 MOZ_ASSERT(mPRThread); 4498 return PR_GetCurrentThread() == mPRThread; 4499 } 4500 4501 void WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) { 4502 AssertIsOnWorkerThread(); 4503 { 4504 // mWorkerThreadAccessible's accessor must be destructed before 4505 // the scheduled Runnable gets to run. 4506 auto data = mWorkerThreadAccessible.Access(); 4507 MOZ_ASSERT(data->mChildWorkers.IsEmpty()); 4508 4509 MOZ_RELEASE_ASSERT(!data->mDeletionScheduled); 4510 data->mDeletionScheduled.Flip(); 4511 } 4512 MOZ_ASSERT(mSyncLoopStack.IsEmpty()); 4513 MOZ_ASSERT(mPostSyncLoopOperations == 0); 4514 4515 // If Worker is never ran, clear the mPreStartRunnables. To let the resource 4516 // hold by the pre-submmited runnables. 4517 if (WorkerNeverRan == aRanOrNot) { 4518 ClearPreStartRunnables(); 4519 } 4520 4521 #ifdef DEBUG 4522 if (WorkerRan == aRanOrNot) { 4523 nsIThread* currentThread = NS_GetCurrentThread(); 4524 MOZ_ASSERT(currentThread); 4525 // On the worker thread WorkerRunnable will refuse to run if not nested 4526 // on top of a WorkerThreadPrimaryRunnable. 4527 (void)NS_WARN_IF(NS_HasPendingEvents(currentThread)); 4528 } 4529 #endif 4530 4531 // Force to set mRemoteDebuggerRegistered as false and notify if the Worker is 4532 // waiting for the registration done. 4533 SetIsRemoteDebuggerRegistered(false); 4534 4535 if (WorkerPrivate* parent = GetParent()) { 4536 RefPtr<WorkerFinishedRunnable> runnable = 4537 new WorkerFinishedRunnable(parent, this); 4538 if (!runnable->Dispatch(parent)) { 4539 NS_WARNING("Failed to dispatch runnable!"); 4540 } 4541 } else { 4542 if (ExtensionAPIAllowed()) { 4543 MOZ_ASSERT(IsServiceWorker()); 4544 RefPtr<Runnable> extWorkerRunnable = 4545 extensions::CreateWorkerDestroyedRunnable(ServiceWorkerID(), 4546 GetBaseURI()); 4547 // Dispatch as a low priority runnable. 4548 if (NS_FAILED( 4549 DispatchToMainThreadForMessaging(extWorkerRunnable.forget()))) { 4550 NS_WARNING( 4551 "Failed to dispatch runnable to notify extensions worker " 4552 "destroyed"); 4553 } 4554 } 4555 4556 // Note, this uses the lower priority DispatchToMainThreadForMessaging for 4557 // dispatching TopLevelWorkerFinishedRunnable to the main thread so that 4558 // other relevant runnables are guaranteed to run before it. 4559 RefPtr<TopLevelWorkerFinishedRunnable> runnable = 4560 new TopLevelWorkerFinishedRunnable(this); 4561 if (NS_FAILED(DispatchToMainThreadForMessaging(runnable.forget()))) { 4562 NS_WARNING("Failed to dispatch runnable!"); 4563 } 4564 4565 // NOTE: Calling any WorkerPrivate methods (or accessing member data) after 4566 // this point is unsafe (the TopLevelWorkerFinishedRunnable just dispatched 4567 // may be able to call ClearSelfAndParentEventTargetRef on this 4568 // WorkerPrivate instance and by the time we get here the WorkerPrivate 4569 // instance destructor may have been already called). 4570 } 4571 } 4572 4573 bool WorkerPrivate::CollectRuntimeStats( 4574 JS::RuntimeStats* aRtStats, bool aAnonymize) MOZ_NO_THREAD_SAFETY_ANALYSIS { 4575 // We don't have a lock to access mJSContext, but it's safe to access on this 4576 // thread. 4577 AssertIsOnWorkerThread(); 4578 NS_ASSERTION(aRtStats, "Null RuntimeStats!"); 4579 // We don't really own it, but it's safe to access on this thread 4580 NS_ASSERTION(mJSContext, "This must never be null!"); 4581 4582 return JS::CollectRuntimeStats(mJSContext, aRtStats, nullptr, aAnonymize); 4583 } 4584 4585 void WorkerPrivate::EnableMemoryReporter() { 4586 auto data = mWorkerThreadAccessible.Access(); 4587 MOZ_ASSERT(!data->mMemoryReporter); 4588 4589 // No need to lock here since the main thread can't race until we've 4590 // successfully registered the reporter. 4591 data->mMemoryReporter = new MemoryReporter(this); 4592 4593 if (NS_FAILED(RegisterWeakAsyncMemoryReporter(data->mMemoryReporter))) { 4594 NS_WARNING("Failed to register memory reporter!"); 4595 // No need to lock here since a failed registration means our memory 4596 // reporter can't start running. Just clean up. 4597 data->mMemoryReporter = nullptr; 4598 } 4599 } 4600 4601 void WorkerPrivate::DisableMemoryReporter() { 4602 auto data = mWorkerThreadAccessible.Access(); 4603 4604 RefPtr<MemoryReporter> memoryReporter; 4605 { 4606 // Mutex protectes MemoryReporter::mWorkerPrivate which is cleared by 4607 // MemoryReporter::Disable() below. 4608 MutexAutoLock lock(mMutex); 4609 4610 // There is nothing to do here if the memory reporter was never successfully 4611 // registered. 4612 if (!data->mMemoryReporter) { 4613 return; 4614 } 4615 4616 // We don't need this set any longer. Swap it out so that we can unregister 4617 // below. 4618 data->mMemoryReporter.swap(memoryReporter); 4619 4620 // Next disable the memory reporter so that the main thread stops trying to 4621 // signal us. 4622 memoryReporter->Disable(); 4623 } 4624 4625 // Finally unregister the memory reporter. 4626 if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) { 4627 NS_WARNING("Failed to unregister memory reporter!"); 4628 } 4629 } 4630 4631 void WorkerPrivate::WaitForWorkerEvents() { 4632 AUTO_PROFILER_LABEL("WorkerPrivate::WaitForWorkerEvents", IDLE); 4633 4634 AssertIsOnWorkerThread(); 4635 mMutex.AssertCurrentThreadOwns(); 4636 4637 // Wait for a worker event. 4638 mCondVar.Wait(); 4639 } 4640 4641 WorkerPrivate::ProcessAllControlRunnablesResult 4642 WorkerPrivate::ProcessAllControlRunnablesLocked() { 4643 AssertIsOnWorkerThread(); 4644 mMutex.AssertCurrentThreadOwns(); 4645 4646 AutoYieldJSThreadExecution yield; 4647 4648 auto result = ProcessAllControlRunnablesResult::Nothing; 4649 4650 for (;;) { 4651 WorkerRunnable* event; 4652 if (!mControlQueue.Pop(event)) { 4653 break; 4654 } 4655 4656 MutexAutoUnlock unlock(mMutex); 4657 4658 { 4659 MOZ_ASSERT(event); 4660 AUTO_PROFILE_FOLLOWING_RUNNABLE(event); 4661 if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) { 4662 result = ProcessAllControlRunnablesResult::Abort; 4663 } 4664 } 4665 4666 if (result == ProcessAllControlRunnablesResult::Nothing) { 4667 // We ran at least one thing. 4668 result = ProcessAllControlRunnablesResult::MayContinue; 4669 } 4670 event->Release(); 4671 } 4672 4673 return result; 4674 } 4675 4676 void WorkerPrivate::ShutdownModuleLoader() { 4677 AssertIsOnWorkerThread(); 4678 4679 WorkerGlobalScope* globalScope = GlobalScope(); 4680 if (globalScope) { 4681 if (globalScope->GetModuleLoader(nullptr)) { 4682 globalScope->GetModuleLoader(nullptr)->Shutdown(); 4683 } 4684 } 4685 WorkerDebuggerGlobalScope* debugGlobalScope = DebuggerGlobalScope(); 4686 if (debugGlobalScope) { 4687 if (debugGlobalScope->GetModuleLoader(nullptr)) { 4688 debugGlobalScope->GetModuleLoader(nullptr)->Shutdown(); 4689 } 4690 } 4691 } 4692 4693 void WorkerPrivate::ClearPreStartRunnables() { 4694 nsTArray<RefPtr<WorkerThreadRunnable>> prestart; 4695 { 4696 MutexAutoLock lock(mMutex); 4697 mPreStartRunnables.SwapElements(prestart); 4698 } 4699 for (uint32_t count = prestart.Length(), index = 0; index < count; index++) { 4700 LOG(WorkerLog(), ("WorkerPrivate::ClearPreStartRunnable [%p]", this)); 4701 RefPtr<WorkerRunnable> runnable = std::move(prestart[index]); 4702 runnable->Cancel(); 4703 } 4704 } 4705 4706 void WorkerPrivate::ProcessSingleDebuggerRunnable() { 4707 WorkerRunnable* runnable = nullptr; 4708 4709 // Move the timer out with the mutex held but only drop the ref 4710 // when the mutex is not held. 4711 nsCOMPtr<nsITimer> timer; 4712 { 4713 MutexAutoLock lock(mMutex); 4714 4715 mDebuggerQueue.Pop(runnable); 4716 4717 mDebuggerInterruptTimer.swap(timer); 4718 } 4719 timer = nullptr; 4720 4721 { 4722 MOZ_ASSERT(runnable); 4723 AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable); 4724 static_cast<nsIRunnable*>(runnable)->Run(); 4725 } 4726 runnable->Release(); 4727 4728 CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); 4729 ccjs->PerformDebuggerMicroTaskCheckpoint(); 4730 } 4731 4732 void WorkerPrivate::ClearDebuggerEventQueue() { 4733 bool debuggerRunnablesPending = false; 4734 { 4735 MutexAutoLock lock(mMutex); 4736 debuggerRunnablesPending = !mDebuggerQueue.IsEmpty(); 4737 } 4738 while (debuggerRunnablesPending) { 4739 WorkerRunnable* runnable = nullptr; 4740 { 4741 MutexAutoLock lock(mMutex); 4742 mDebuggerQueue.Pop(runnable); 4743 debuggerRunnablesPending = !mDebuggerQueue.IsEmpty(); 4744 } 4745 // It should be ok to simply release the runnable, without running it. 4746 runnable->Release(); 4747 4748 // Move the timer out with the mutex held but only drop the ref 4749 // when the mutex is not held. 4750 nsCOMPtr<nsITimer> timer; 4751 { 4752 MutexAutoLock lock(mMutex); 4753 mDebuggerInterruptTimer.swap(timer); 4754 } 4755 timer = nullptr; 4756 } 4757 } 4758 4759 bool WorkerPrivate::FreezeInternal() { 4760 auto data = mWorkerThreadAccessible.Access(); 4761 NS_ASSERTION(!data->mFrozen, "Already frozen!"); 4762 4763 AutoYieldJSThreadExecution yield; 4764 4765 // The worker can freeze even if it failed to run (and doesn't have a global). 4766 if (data->mScope) { 4767 data->mScope->MutableClientSourceRef().Freeze(); 4768 } 4769 4770 data->mFrozen = true; 4771 4772 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 4773 data->mChildWorkers[index]->Freeze(nullptr); 4774 } 4775 auto* timeoutManager = 4776 data->mScope ? data->mScope->GetTimeoutManager() : nullptr; 4777 if (timeoutManager) { 4778 timeoutManager->Suspend(); 4779 timeoutManager->Freeze(); 4780 } 4781 4782 return true; 4783 } 4784 4785 bool WorkerPrivate::HasActiveWorkerRefs() { 4786 auto data = mWorkerThreadAccessible.Access(); 4787 auto* timeoutManager = 4788 data->mScope ? data->mScope->GetTimeoutManager() : nullptr; 4789 return !data->mChildWorkers.IsEmpty() || 4790 (timeoutManager && timeoutManager->HasTimeouts()) || 4791 !data->mWorkerRefs.IsEmpty(); 4792 } 4793 4794 bool WorkerPrivate::ThawInternal() { 4795 auto data = mWorkerThreadAccessible.Access(); 4796 NS_ASSERTION(data->mFrozen, "Not yet frozen!"); 4797 4798 // BindRemoteWorkerDebuggerChild(); 4799 4800 data->mFrozen = false; 4801 4802 auto* timeoutManager = 4803 data->mScope ? data->mScope->GetTimeoutManager() : nullptr; 4804 if (timeoutManager) { 4805 timeoutManager->Thaw(); 4806 timeoutManager->Resume(); 4807 } 4808 4809 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 4810 data->mChildWorkers[index]->Thaw(nullptr); 4811 } 4812 4813 // The worker can thaw even if it failed to run (and doesn't have a global). 4814 if (data->mScope) { 4815 data->mScope->MutableClientSourceRef().Thaw(); 4816 } 4817 4818 return true; 4819 } 4820 4821 bool WorkerPrivate::ChangeBackgroundStateInternal(bool aIsBackground) { 4822 AssertIsOnWorkerThread(); 4823 mIsInBackground = aIsBackground; 4824 auto data = mWorkerThreadAccessible.Access(); 4825 if (StaticPrefs::dom_workers_throttling_enabled_AtStartup()) { 4826 auto* timeoutManager = 4827 data->mScope ? data->mScope->GetTimeoutManager() : nullptr; 4828 if (timeoutManager) { 4829 timeoutManager->UpdateBackgroundState(); 4830 } 4831 } 4832 4833 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 4834 if (aIsBackground) { 4835 data->mChildWorkers[index]->SetIsRunningInBackground(); 4836 } else { 4837 data->mChildWorkers[index]->SetIsRunningInForeground(); 4838 } 4839 } 4840 return true; 4841 } 4842 4843 bool WorkerPrivate::ChangePlaybackStateInternal(bool aIsPlayingAudio) { 4844 AssertIsOnWorkerThread(); 4845 mIsPlayingAudio = aIsPlayingAudio; 4846 auto data = mWorkerThreadAccessible.Access(); 4847 4848 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 4849 data->mChildWorkers[index]->SetIsPlayingAudio(aIsPlayingAudio); 4850 } 4851 return true; 4852 } 4853 4854 bool WorkerPrivate::ChangePeerConnectionsInternal(bool aHasPeerConnections) { 4855 AssertIsOnWorkerThread(); 4856 mHasActivePeerConnections = aHasPeerConnections; 4857 auto data = mWorkerThreadAccessible.Access(); 4858 4859 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 4860 data->mChildWorkers[index]->SetActivePeerConnections(aHasPeerConnections); 4861 } 4862 return true; 4863 } 4864 4865 void WorkerPrivate::PropagateStorageAccessPermissionGrantedInternal() { 4866 auto data = mWorkerThreadAccessible.Access(); 4867 4868 mLoadInfo.mUseRegularPrincipal = true; 4869 mLoadInfo.mUsingStorageAccess = true; 4870 4871 WorkerGlobalScope* globalScope = GlobalScope(); 4872 if (globalScope) { 4873 globalScope->StorageAccessPermissionGranted(); 4874 } 4875 4876 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 4877 data->mChildWorkers[index]->PropagateStorageAccessPermissionGranted(); 4878 } 4879 } 4880 4881 void WorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback& cb) { 4882 auto data = mWorkerThreadAccessible.Access(); 4883 auto* timeoutManager = 4884 data->mScope ? data->mScope->GetTimeoutManager() : nullptr; 4885 if (timeoutManager) { 4886 timeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) { 4887 cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout)); 4888 }); 4889 } 4890 } 4891 4892 void WorkerPrivate::UnlinkTimeouts() { 4893 auto data = mWorkerThreadAccessible.Access(); 4894 auto* timeoutManager = 4895 data->mScope ? data->mScope->GetTimeoutManager() : nullptr; 4896 if (timeoutManager) { 4897 timeoutManager->ClearAllTimeouts(); 4898 if (!timeoutManager->HasTimeouts()) { 4899 UpdateCCFlag(CCFlag::EligibleForTimeout); 4900 } 4901 } 4902 } 4903 4904 bool WorkerPrivate::AddChildWorker(WorkerPrivate& aChildWorker) { 4905 auto data = mWorkerThreadAccessible.Access(); 4906 4907 #ifdef DEBUG 4908 { 4909 WorkerStatus currentStatus; 4910 { 4911 MutexAutoLock lock(mMutex); 4912 currentStatus = mStatus; 4913 } 4914 4915 MOZ_ASSERT(currentStatus == Running); 4916 } 4917 #endif 4918 4919 NS_ASSERTION(!data->mChildWorkers.Contains(&aChildWorker), 4920 "Already know about this one!"); 4921 data->mChildWorkers.AppendElement(&aChildWorker); 4922 4923 if (data->mChildWorkers.Length() == 1) { 4924 UpdateCCFlag(CCFlag::IneligibleForChildWorker); 4925 } 4926 4927 return true; 4928 } 4929 4930 void WorkerPrivate::RemoveChildWorker(WorkerPrivate& aChildWorker) { 4931 auto data = mWorkerThreadAccessible.Access(); 4932 4933 NS_ASSERTION(data->mChildWorkers.Contains(&aChildWorker), 4934 "Didn't know about this one!"); 4935 data->mChildWorkers.RemoveElement(&aChildWorker); 4936 4937 if (data->mChildWorkers.IsEmpty()) { 4938 UpdateCCFlag(CCFlag::EligibleForChildWorker); 4939 } 4940 } 4941 4942 bool WorkerPrivate::AddWorkerRef(WorkerRef* aWorkerRef, 4943 WorkerStatus aFailStatus) { 4944 MOZ_ASSERT(aWorkerRef); 4945 auto data = mWorkerThreadAccessible.Access(); 4946 4947 { 4948 MutexAutoLock lock(mMutex); 4949 4950 LOG(WorkerLog(), 4951 ("WorkerPrivate::AddWorkerRef [%p] mStatus: %u, aFailStatus: (%u)", 4952 this, static_cast<uint8_t>(mStatus), 4953 static_cast<uint8_t>(aFailStatus))); 4954 4955 if (mStatus >= aFailStatus) { 4956 return false; 4957 } 4958 4959 // We shouldn't create strong references to workers before their main loop 4960 // begins running. Strong references must be disposed of on the worker 4961 // thread, so strong references from other threads use a control runnable 4962 // for that purpose. If the worker fails to reach the main loop stage then 4963 // no control runnables get run and it would be impossible to get rid of the 4964 // reference properly. 4965 MOZ_DIAGNOSTIC_ASSERT_IF(aWorkerRef->IsPreventingShutdown(), 4966 mStatus >= WorkerStatus::Running); 4967 } 4968 4969 MOZ_ASSERT(!data->mWorkerRefs.Contains(aWorkerRef), 4970 "Already know about this one!"); 4971 4972 if (aWorkerRef->IsPreventingShutdown()) { 4973 data->mNumWorkerRefsPreventingShutdownStart += 1; 4974 if (data->mNumWorkerRefsPreventingShutdownStart == 1) { 4975 UpdateCCFlag(CCFlag::IneligibleForWorkerRef); 4976 } 4977 } 4978 4979 data->mWorkerRefs.AppendElement(aWorkerRef); 4980 return true; 4981 } 4982 4983 void WorkerPrivate::RemoveWorkerRef(WorkerRef* aWorkerRef) { 4984 MOZ_ASSERT(aWorkerRef); 4985 LOG(WorkerLog(), 4986 ("WorkerPrivate::RemoveWorkerRef [%p] aWorkerRef: %p", this, aWorkerRef)); 4987 auto data = mWorkerThreadAccessible.Access(); 4988 4989 MOZ_ASSERT(data->mWorkerRefs.Contains(aWorkerRef), 4990 "Didn't know about this one!"); 4991 data->mWorkerRefs.RemoveElement(aWorkerRef); 4992 4993 if (aWorkerRef->IsPreventingShutdown()) { 4994 data->mNumWorkerRefsPreventingShutdownStart -= 1; 4995 if (!data->mNumWorkerRefsPreventingShutdownStart) { 4996 UpdateCCFlag(CCFlag::EligibleForWorkerRef); 4997 } 4998 } 4999 } 5000 5001 void WorkerPrivate::NotifyWorkerRefs(WorkerStatus aStatus) { 5002 auto data = mWorkerThreadAccessible.Access(); 5003 5004 NS_ASSERTION(aStatus > Closing, "Bad status!"); 5005 5006 LOG(WorkerLog(), ("WorkerPrivate::NotifyWorkerRefs [%p] aStatus: %u", this, 5007 static_cast<uint8_t>(aStatus))); 5008 5009 for (auto* workerRef : data->mWorkerRefs.ForwardRange()) { 5010 LOG(WorkerLog(), ("WorkerPrivate::NotifyWorkerRefs [%p] WorkerRefs(%s %p)", 5011 this, workerRef->mName, workerRef)); 5012 workerRef->Notify(); 5013 } 5014 5015 AutoTArray<CheckedUnsafePtr<WorkerPrivate>, 10> children; 5016 children.AppendElements(data->mChildWorkers); 5017 5018 for (uint32_t index = 0; index < children.Length(); index++) { 5019 if (!children[index]->Notify(aStatus)) { 5020 NS_WARNING("Failed to notify child worker!"); 5021 } 5022 } 5023 } 5024 5025 nsresult WorkerPrivate::RegisterShutdownTask(nsITargetShutdownTask* aTask) { 5026 NS_ENSURE_ARG(aTask); 5027 5028 MutexAutoLock lock(mMutex); 5029 // If we've already started running shutdown tasks, don't allow registering 5030 // new ones. 5031 if (mShutdownTasksRun) { 5032 return NS_ERROR_UNEXPECTED; 5033 } 5034 return mShutdownTasks.AddTask(aTask); 5035 } 5036 5037 nsresult WorkerPrivate::UnregisterShutdownTask(nsITargetShutdownTask* aTask) { 5038 NS_ENSURE_ARG(aTask); 5039 5040 MutexAutoLock lock(mMutex); 5041 return mShutdownTasks.RemoveTask(aTask); 5042 } 5043 5044 void WorkerPrivate::JSAsyncTaskStarted(JS::Dispatchable* aDispatchable) { 5045 RefPtr<StrongWorkerRef> ref = StrongWorkerRef::Create(this, "JSAsyncTask"); 5046 MOZ_ASSERT_DEBUG_OR_FUZZING(ref); 5047 if (NS_WARN_IF(!ref)) { 5048 return; 5049 } 5050 MOZ_ALWAYS_TRUE(mPendingJSAsyncTasks.putNew(aDispatchable, std::move(ref))); 5051 } 5052 5053 void WorkerPrivate::JSAsyncTaskFinished(JS::Dispatchable* aDispatchable) { 5054 mPendingJSAsyncTasks.remove(aDispatchable); 5055 } 5056 5057 void WorkerPrivate::RunShutdownTasks() { 5058 TargetShutdownTaskSet::TasksArray shutdownTasks; 5059 5060 { 5061 MutexAutoLock lock(mMutex); 5062 mShutdownTasksRun = true; 5063 shutdownTasks = mShutdownTasks.Extract(); 5064 } 5065 5066 for (const auto& task : shutdownTasks) { 5067 task->TargetShutdown(); 5068 } 5069 mWorkerHybridEventTarget->ForgetWorkerPrivate(this); 5070 } 5071 5072 RefPtr<WorkerParentRef> WorkerPrivate::GetWorkerParentRef() const { 5073 RefPtr<WorkerParentRef> ref(mParentRef); 5074 return ref; 5075 } 5076 5077 void WorkerPrivate::AdjustNonblockingCCBackgroundActorCount(int32_t aCount) { 5078 AssertIsOnWorkerThread(); 5079 auto data = mWorkerThreadAccessible.Access(); 5080 LOGV(("WorkerPrivate::AdjustNonblockingCCBackgroundActors [%p] (%d/%u)", this, 5081 aCount, data->mNonblockingCCBackgroundActorCount)); 5082 5083 #ifdef DEBUG 5084 if (aCount < 0) { 5085 MOZ_ASSERT(data->mNonblockingCCBackgroundActorCount >= 5086 (uint32_t)abs(aCount)); 5087 } 5088 #endif 5089 5090 data->mNonblockingCCBackgroundActorCount += aCount; 5091 } 5092 5093 void WorkerPrivate::UpdateCCFlag(const CCFlag aFlag) { 5094 AssertIsOnWorkerThread(); 5095 5096 auto data = mWorkerThreadAccessible.Access(); 5097 5098 #ifdef DEBUG 5099 switch (aFlag) { 5100 case CCFlag::EligibleForWorkerRef: { 5101 MOZ_ASSERT(!data->mNumWorkerRefsPreventingShutdownStart); 5102 break; 5103 } 5104 case CCFlag::IneligibleForWorkerRef: { 5105 MOZ_ASSERT(data->mNumWorkerRefsPreventingShutdownStart); 5106 break; 5107 } 5108 case CCFlag::EligibleForChildWorker: { 5109 MOZ_ASSERT(data->mChildWorkers.IsEmpty()); 5110 break; 5111 } 5112 case CCFlag::IneligibleForChildWorker: { 5113 MOZ_ASSERT(!data->mChildWorkers.IsEmpty()); 5114 break; 5115 } 5116 case CCFlag::EligibleForTimeout: { 5117 auto* timeoutManager = 5118 data->mScope ? data->mScope->GetTimeoutManager() : nullptr; 5119 MOZ_ASSERT(timeoutManager && !timeoutManager->HasTimeouts()); 5120 break; 5121 } 5122 case CCFlag::IneligibleForTimeout: { 5123 auto* timeoutManager = 5124 data->mScope ? data->mScope->GetTimeoutManager() : nullptr; 5125 MOZ_ASSERT(!timeoutManager || timeoutManager->HasTimeouts()); 5126 break; 5127 } 5128 case CCFlag::CheckBackgroundActors: { 5129 break; 5130 } 5131 } 5132 #endif 5133 5134 { 5135 MutexAutoLock lock(mMutex); 5136 if (mStatus > Canceling) { 5137 mCCFlagSaysEligible = true; 5138 return; 5139 } 5140 } 5141 auto HasBackgroundActors = [nonblockingActorCount = 5142 data->mNonblockingCCBackgroundActorCount]() { 5143 RefPtr<PBackgroundChild> backgroundChild = 5144 BackgroundChild::GetForCurrentThread(); 5145 MOZ_ASSERT(backgroundChild); 5146 auto totalCount = backgroundChild->AllManagedActorsCount(); 5147 LOGV(("WorkerPrivate::UpdateCCFlag HasBackgroundActors: %s(%u/%u)", 5148 totalCount > nonblockingActorCount ? "true" : "false", totalCount, 5149 nonblockingActorCount)); 5150 5151 return totalCount > nonblockingActorCount; 5152 }; 5153 5154 auto* timeoutManager = 5155 data->mScope ? data->mScope->GetTimeoutManager() : nullptr; 5156 5157 bool noTimeouts{true}; 5158 if (timeoutManager) { 5159 noTimeouts = !timeoutManager->HasTimeouts(); 5160 } 5161 5162 bool eligibleForCC = data->mChildWorkers.IsEmpty() && noTimeouts && 5163 !data->mNumWorkerRefsPreventingShutdownStart; 5164 5165 // Only checking BackgroundActors when no strong WorkerRef, ChildWorker, and 5166 // Timeout since the checking is expensive. 5167 if (eligibleForCC) { 5168 eligibleForCC = !HasBackgroundActors(); 5169 } 5170 5171 { 5172 MutexAutoLock lock(mMutex); 5173 mCCFlagSaysEligible = eligibleForCC; 5174 } 5175 } 5176 5177 bool WorkerPrivate::IsEligibleForCC() { 5178 LOGV(("WorkerPrivate::IsEligibleForCC [%p]", this)); 5179 MutexAutoLock lock(mMutex); 5180 if (mStatus > Canceling) { 5181 return true; 5182 } 5183 5184 bool hasShutdownTasks = !mShutdownTasks.IsEmpty(); 5185 bool hasPendingEvents = false; 5186 if (mThread) { 5187 hasPendingEvents = 5188 NS_SUCCEEDED(mThread->HasPendingEvents(&hasPendingEvents)) && 5189 hasPendingEvents; 5190 } 5191 5192 LOGV(("mMainThreadEventTarget: %s", 5193 mMainThreadEventTarget->IsEmpty() ? "empty" : "non-empty")); 5194 LOGV(("mMainThreadEventTargetForMessaging: %s", 5195 mMainThreadEventTargetForMessaging->IsEmpty() ? "empty" : "non-empty")); 5196 LOGV(("mMainThreadDebuggerEventTarget: %s", 5197 mMainThreadDebuggeeEventTarget->IsEmpty() ? "empty" : "non-empty")); 5198 LOGV(("mCCFlagSaysEligible: %s", mCCFlagSaysEligible ? "true" : "false")); 5199 LOGV(("hasShutdownTasks: %s", hasShutdownTasks ? "true" : "false")); 5200 LOGV(("hasPendingEvents: %s", hasPendingEvents ? "true" : "false")); 5201 5202 return mMainThreadEventTarget->IsEmpty() && 5203 mMainThreadEventTargetForMessaging->IsEmpty() && 5204 mMainThreadDebuggeeEventTarget->IsEmpty() && mCCFlagSaysEligible && 5205 !hasShutdownTasks && !hasPendingEvents && mWorkerLoopIsIdle; 5206 } 5207 5208 void WorkerPrivate::CancelAllTimeouts() { 5209 auto data = mWorkerThreadAccessible.Access(); 5210 5211 auto* timeoutManager = 5212 data->mScope ? data->mScope->GetTimeoutManager() : nullptr; 5213 if (timeoutManager) { 5214 timeoutManager->ClearAllTimeouts(); 5215 if (!timeoutManager->HasTimeouts()) { 5216 UpdateCCFlag(CCFlag::EligibleForTimeout); 5217 } 5218 } 5219 5220 LOG(TimeoutsLog(), ("Worker %p CancelAllTimeouts.\n", this)); 5221 } 5222 5223 already_AddRefed<nsISerialEventTarget> WorkerPrivate::CreateNewSyncLoop( 5224 WorkerStatus aFailStatus) { 5225 AssertIsOnWorkerThread(); 5226 MOZ_ASSERT( 5227 aFailStatus >= Canceling, 5228 "Sync loops can be created when the worker is in Running/Closing state!"); 5229 5230 LOG(WorkerLog(), ("WorkerPrivate::CreateNewSyncLoop [%p] failstatus: %u", 5231 this, static_cast<uint8_t>(aFailStatus))); 5232 5233 ThreadEventQueue* queue = nullptr; 5234 { 5235 MutexAutoLock lock(mMutex); 5236 5237 if (mStatus >= aFailStatus) { 5238 return nullptr; 5239 } 5240 queue = static_cast<ThreadEventQueue*>(mThread->EventQueue()); 5241 } 5242 5243 nsCOMPtr<nsISerialEventTarget> nestedEventTarget = queue->PushEventQueue(); 5244 MOZ_ASSERT(nestedEventTarget); 5245 5246 RefPtr<EventTarget> workerEventTarget = 5247 new EventTarget(this, nestedEventTarget); 5248 5249 { 5250 // Modifications must be protected by mMutex in DEBUG builds, see comment 5251 // about mSyncLoopStack in WorkerPrivate.h. 5252 #ifdef DEBUG 5253 MutexAutoLock lock(mMutex); 5254 #endif 5255 5256 mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget)); 5257 } 5258 5259 return workerEventTarget.forget(); 5260 } 5261 5262 nsresult WorkerPrivate::RunCurrentSyncLoop() { 5263 AssertIsOnWorkerThread(); 5264 LOG(WorkerLog(), ("WorkerPrivate::RunCurrentSyncLoop [%p]", this)); 5265 RefPtr<WorkerThread> thread; 5266 JSContext* cx = GetJSContext(); 5267 MOZ_ASSERT(cx); 5268 // mThread is set before we enter, and is never changed during 5269 // RunCurrentSyncLoop. 5270 { 5271 MutexAutoLock lock(mMutex); 5272 // Copy to local so we don't trigger mutex analysis lower down 5273 // mThread is set before we enter, and is never changed during 5274 // RunCurrentSyncLoop copy to local so we don't trigger mutex analysis 5275 thread = mThread; 5276 } 5277 5278 AutoPushEventLoopGlobal eventLoopGlobal(this, cx); 5279 5280 // This should not change between now and the time we finish running this sync 5281 // loop. 5282 uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1; 5283 5284 SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex].get(); 5285 5286 AutoYieldJSThreadExecution yield; 5287 5288 MOZ_ASSERT(loopInfo); 5289 MOZ_ASSERT(!loopInfo->mHasRun); 5290 MOZ_ASSERT(!loopInfo->mCompleted); 5291 5292 #ifdef DEBUG 5293 loopInfo->mHasRun = true; 5294 #endif 5295 5296 { 5297 while (!loopInfo->mCompleted) { 5298 bool normalRunnablesPending = false; 5299 5300 // Don't block with the periodic GC timer running. 5301 if (!NS_HasPendingEvents(thread)) { 5302 SetGCTimerMode(IdleTimer); 5303 } 5304 5305 // Wait for something to do. 5306 { 5307 MutexAutoLock lock(mMutex); 5308 5309 for (;;) { 5310 while (mControlQueue.IsEmpty() && !normalRunnablesPending && 5311 !(normalRunnablesPending = NS_HasPendingEvents(thread))) { 5312 WaitForWorkerEvents(); 5313 } 5314 5315 auto result = ProcessAllControlRunnablesLocked(); 5316 if (result != ProcessAllControlRunnablesResult::Nothing) { 5317 // The state of the world may have changed. Recheck it if we need to 5318 // continue. 5319 normalRunnablesPending = 5320 result == ProcessAllControlRunnablesResult::MayContinue && 5321 NS_HasPendingEvents(thread); 5322 5323 // NB: If we processed a NotifyRunnable, we might have run 5324 // non-control runnables, one of which may have shut down the 5325 // sync loop. 5326 if (loopInfo->mCompleted) { 5327 break; 5328 } 5329 } 5330 5331 // If we *didn't* run any control runnables, this should be unchanged. 5332 MOZ_ASSERT(!loopInfo->mCompleted); 5333 5334 if (normalRunnablesPending) { 5335 break; 5336 } 5337 } 5338 } 5339 5340 if (normalRunnablesPending) { 5341 // Make sure the periodic timer is running before we continue. 5342 SetGCTimerMode(PeriodicTimer); 5343 5344 MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread, false)); 5345 5346 // Now *might* be a good time to GC. Let the JS engine make the 5347 // decision. 5348 if (GetCurrentEventLoopGlobal()) { 5349 // If GetCurrentEventLoopGlobal() is non-null, our JSContext is in a 5350 // Realm, so it's safe to try to GC. 5351 MOZ_ASSERT(JS::CurrentGlobalOrNull(cx)); 5352 JS_MaybeGC(cx); 5353 } 5354 } 5355 } 5356 } 5357 5358 // Make sure that the stack didn't change underneath us. 5359 MOZ_ASSERT(mSyncLoopStack[currentLoopIndex].get() == loopInfo); 5360 5361 return DestroySyncLoop(currentLoopIndex); 5362 } 5363 5364 nsresult WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex) { 5365 MOZ_ASSERT(!mSyncLoopStack.IsEmpty()); 5366 MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex); 5367 5368 LOG(WorkerLog(), 5369 ("WorkerPrivate::DestroySyncLoop [%p] aLoopIndex: %u", this, aLoopIndex)); 5370 5371 AutoYieldJSThreadExecution yield; 5372 5373 // We're about to delete the loop, stash its event target and result. 5374 const auto& loopInfo = mSyncLoopStack[aLoopIndex]; 5375 5376 nsresult result = loopInfo->mResult; 5377 5378 { 5379 RefPtr<nsIEventTarget> nestedEventTarget( 5380 loopInfo->mEventTarget->GetNestedEventTarget()); 5381 MOZ_ASSERT(nestedEventTarget); 5382 5383 loopInfo->mEventTarget->Shutdown(); 5384 5385 { 5386 MutexAutoLock lock(mMutex); 5387 static_cast<ThreadEventQueue*>(mThread->EventQueue()) 5388 ->PopEventQueue(nestedEventTarget); 5389 } 5390 } 5391 5392 // Are we making a 1 -> 0 transition here? 5393 if (mSyncLoopStack.Length() == 1) { 5394 if ((mPostSyncLoopOperations & eDispatchCancelingRunnable)) { 5395 LOG(WorkerLog(), 5396 ("WorkerPrivate::DestroySyncLoop [%p] Dispatching CancelingRunnables", 5397 this)); 5398 DispatchCancelingRunnable(); 5399 } 5400 5401 mPostSyncLoopOperations = 0; 5402 } 5403 5404 { 5405 // Modifications must be protected by mMutex in DEBUG builds, see comment 5406 // about mSyncLoopStack in WorkerPrivate.h. 5407 #ifdef DEBUG 5408 MutexAutoLock lock(mMutex); 5409 #endif 5410 5411 // This will delete |loopInfo|! 5412 mSyncLoopStack.RemoveElementAt(aLoopIndex); 5413 } 5414 5415 return result; 5416 } 5417 5418 void WorkerPrivate::DispatchCancelingRunnable() { 5419 // Here we use a normal runnable to know when the current JS chunk of code 5420 // is finished. We cannot use a WorkerRunnable because they are not 5421 // accepted any more by the worker, and we do not want to use a 5422 // WorkerControlRunnable because they are immediately executed. 5423 5424 LOG(WorkerLog(), ("WorkerPrivate::DispatchCancelingRunnable [%p]", this)); 5425 RefPtr<CancelingRunnable> r = new CancelingRunnable(); 5426 { 5427 MutexAutoLock lock(mMutex); 5428 mThread->nsThread::Dispatch(r.forget(), NS_DISPATCH_NORMAL); 5429 } 5430 5431 // At the same time, we want to be sure that we interrupt infinite loops. 5432 // The following runnable starts a timer that cancel the worker, from the 5433 // parent thread, after CANCELING_TIMEOUT millseconds. 5434 LOG(WorkerLog(), ("WorkerPrivate::DispatchCancelingRunnable [%p] Setup a " 5435 "timeout canceling", 5436 this)); 5437 RefPtr<CancelingWithTimeoutOnParentRunnable> rr = 5438 new CancelingWithTimeoutOnParentRunnable(this); 5439 rr->Dispatch(this); 5440 } 5441 5442 void WorkerPrivate::ReportUseCounters() { 5443 AssertIsOnWorkerThread(); 5444 5445 if (mReportedUseCounters) { 5446 return; 5447 } 5448 mReportedUseCounters = true; 5449 5450 if (IsChromeWorker()) { 5451 return; 5452 } 5453 5454 const size_t kind = Kind(); 5455 switch (kind) { 5456 case WorkerKindDedicated: 5457 glean::use_counter::dedicated_workers_destroyed.Add(); 5458 break; 5459 case WorkerKindShared: 5460 glean::use_counter::shared_workers_destroyed.Add(); 5461 break; 5462 case WorkerKindService: 5463 glean::use_counter::service_workers_destroyed.Add(); 5464 break; 5465 default: 5466 MOZ_ASSERT(false, "Unknown worker kind"); 5467 return; 5468 } 5469 5470 Maybe<nsCString> workerPathForLogging; 5471 const bool dumpCounters = StaticPrefs::dom_use_counters_dump_worker(); 5472 if (dumpCounters) { 5473 nsAutoCString path(Domain()); 5474 path.AppendLiteral("("); 5475 NS_ConvertUTF16toUTF8 script(ScriptURL()); 5476 path.Append(script); 5477 path.AppendPrintf(", 0x%p)", this); 5478 workerPathForLogging.emplace(std::move(path)); 5479 } 5480 5481 const size_t count = static_cast<size_t>(UseCounterWorker::Count); 5482 5483 const auto workerKind = Kind(); 5484 for (size_t c = 0; c < count; ++c) { 5485 if (!GetUseCounter(static_cast<UseCounterWorker>(c))) { 5486 continue; 5487 } 5488 const char* metricName = 5489 IncrementWorkerUseCounter(static_cast<UseCounterWorker>(c), workerKind); 5490 if (dumpCounters) { 5491 printf_stderr("USE_COUNTER_WORKER: %s - %s\n", metricName, 5492 workerPathForLogging->get()); 5493 } 5494 } 5495 } 5496 5497 void WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, 5498 nsresult aResult) { 5499 AssertValidSyncLoop(aSyncLoopTarget); 5500 5501 if (!MaybeStopSyncLoop(aSyncLoopTarget, aResult)) { 5502 // TODO: I wonder if we should really ever crash here given the assert. 5503 MOZ_CRASH("Unknown sync loop!"); 5504 } 5505 } 5506 5507 bool WorkerPrivate::MaybeStopSyncLoop(nsIEventTarget* aSyncLoopTarget, 5508 nsresult aResult) { 5509 AssertIsOnWorkerThread(); 5510 5511 for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) { 5512 const auto& loopInfo = mSyncLoopStack[index - 1]; 5513 MOZ_ASSERT(loopInfo); 5514 MOZ_ASSERT(loopInfo->mEventTarget); 5515 5516 if (loopInfo->mEventTarget == aSyncLoopTarget) { 5517 // Can't assert |loop->mHasRun| here because dispatch failures can cause 5518 // us to bail out early. 5519 MOZ_ASSERT(!loopInfo->mCompleted); 5520 5521 loopInfo->mResult = aResult; 5522 loopInfo->mCompleted = true; 5523 5524 loopInfo->mEventTarget->Disable(); 5525 5526 return true; 5527 } 5528 5529 MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget)); 5530 } 5531 5532 return false; 5533 } 5534 5535 #ifdef DEBUG 5536 void WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget) { 5537 MOZ_ASSERT(aSyncLoopTarget); 5538 5539 EventTarget* workerTarget; 5540 nsresult rv = aSyncLoopTarget->QueryInterface( 5541 kDEBUGWorkerEventTargetIID, reinterpret_cast<void**>(&workerTarget)); 5542 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5543 MOZ_ASSERT(workerTarget); 5544 5545 bool valid = false; 5546 5547 { 5548 MutexAutoLock lock(mMutex); 5549 5550 for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) { 5551 const auto& loopInfo = mSyncLoopStack[index]; 5552 MOZ_ASSERT(loopInfo); 5553 MOZ_ASSERT(loopInfo->mEventTarget); 5554 5555 if (loopInfo->mEventTarget == aSyncLoopTarget) { 5556 valid = true; 5557 break; 5558 } 5559 5560 MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget)); 5561 } 5562 } 5563 5564 MOZ_ASSERT(valid); 5565 } 5566 #endif 5567 5568 void WorkerPrivate::PostMessageToParent( 5569 JSContext* aCx, JS::Handle<JS::Value> aMessage, 5570 const Sequence<JSObject*>& aTransferable, ErrorResult& aRv) { 5571 LOG(WorkerLog(), ("WorkerPrivate::PostMessageToParent [%p]", this)); 5572 AssertIsOnWorkerThread(); 5573 MOZ_DIAGNOSTIC_ASSERT(IsDedicatedWorker()); 5574 5575 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue()); 5576 5577 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable, 5578 &transferable); 5579 if (NS_WARN_IF(aRv.Failed())) { 5580 return; 5581 } 5582 5583 RefPtr<MessageEventToParentRunnable> runnable = 5584 new MessageEventToParentRunnable(this); 5585 5586 JS::CloneDataPolicy clonePolicy; 5587 5588 // Parent and dedicated workers are always part of the same cluster. 5589 clonePolicy.allowIntraClusterClonableSharedObjects(); 5590 5591 if (IsSharedMemoryAllowed()) { 5592 clonePolicy.allowSharedMemoryObjects(); 5593 } 5594 5595 runnable->Write(aCx, aMessage, transferable, clonePolicy, aRv); 5596 5597 if (NS_WARN_IF(aRv.Failed())) { 5598 return; 5599 } 5600 5601 if (!runnable->Dispatch(this)) { 5602 aRv = NS_ERROR_FAILURE; 5603 } 5604 } 5605 5606 void WorkerPrivate::EnterDebuggerEventLoop() { 5607 auto data = mWorkerThreadAccessible.Access(); 5608 5609 JSContext* cx = GetJSContext(); 5610 MOZ_ASSERT(cx); 5611 5612 AutoPushEventLoopGlobal eventLoopGlobal(this, cx); 5613 AutoYieldJSThreadExecution yield; 5614 5615 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get(); 5616 5617 uint32_t currentEventLoopLevel = ++data->mDebuggerEventLoopLevel; 5618 5619 while (currentEventLoopLevel <= data->mDebuggerEventLoopLevel) { 5620 bool debuggerRunnablesPending = false; 5621 5622 { 5623 MutexAutoLock lock(mMutex); 5624 5625 debuggerRunnablesPending = !mDebuggerQueue.IsEmpty(); 5626 } 5627 5628 // Don't block with the periodic GC timer running. 5629 if (!debuggerRunnablesPending) { 5630 SetGCTimerMode(IdleTimer); 5631 } 5632 5633 // Wait for something to do 5634 { 5635 MutexAutoLock lock(mMutex); 5636 5637 if (StaticPrefs::javascript_options_use_js_microtask_queue()) { 5638 // When JS microtask queue is enabled, check for debugger microtasks 5639 // directly from the JS engine 5640 while (mControlQueue.IsEmpty() && 5641 !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) && 5642 !JS::HasDebuggerMicroTasks(cx)) { 5643 WaitForWorkerEvents(); 5644 } 5645 } else { 5646 // Legacy path: check the debugger microtask queue in 5647 // CycleCollectedJSContext 5648 std::deque<RefPtr<MicroTaskRunnable>>& debuggerMtQueue = 5649 ccjscx->GetDebuggerMicroTaskQueue(); 5650 while (mControlQueue.IsEmpty() && 5651 !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) && 5652 debuggerMtQueue.empty()) { 5653 WaitForWorkerEvents(); 5654 } 5655 } 5656 5657 ProcessAllControlRunnablesLocked(); 5658 5659 // XXXkhuey should we abort JS on the stack here if we got Abort above? 5660 } 5661 ccjscx->PerformDebuggerMicroTaskCheckpoint(); 5662 if (debuggerRunnablesPending) { 5663 // Start the periodic GC timer if it is not already running. 5664 SetGCTimerMode(PeriodicTimer); 5665 5666 ProcessSingleDebuggerRunnable(); 5667 5668 // Now *might* be a good time to GC. Let the JS engine make the decision. 5669 if (GetCurrentEventLoopGlobal()) { 5670 // If GetCurrentEventLoopGlobal() is non-null, our JSContext is in a 5671 // Realm, so it's safe to try to GC. 5672 MOZ_ASSERT(JS::CurrentGlobalOrNull(cx)); 5673 JS_MaybeGC(cx); 5674 } 5675 } 5676 } 5677 } 5678 5679 void WorkerPrivate::LeaveDebuggerEventLoop() { 5680 auto data = mWorkerThreadAccessible.Access(); 5681 5682 // TODO: Why lock the mutex if we're accessing data accessible to one thread 5683 // only? 5684 MutexAutoLock lock(mMutex); 5685 5686 if (data->mDebuggerEventLoopLevel > 0) { 5687 --data->mDebuggerEventLoopLevel; 5688 } 5689 } 5690 5691 void WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage) { 5692 AssertIsOnWorkerThread(); 5693 5694 mDebugger->PostMessageToDebugger(aMessage); 5695 RefPtr<RemoteWorkerDebuggerChild> remoteDebugger; 5696 { 5697 MutexAutoLock lock(mMutex); 5698 if (!mRemoteDebugger) { 5699 return; 5700 } 5701 remoteDebugger = mRemoteDebugger; 5702 } 5703 MOZ_ASSERT_DEBUG_OR_FUZZING(remoteDebugger); 5704 (void)remoteDebugger->SendPostMessageToDebugger(nsAutoString(aMessage)); 5705 } 5706 5707 void WorkerPrivate::SetDebuggerImmediate(dom::Function& aHandler, 5708 ErrorResult& aRv) { 5709 AssertIsOnWorkerThread(); 5710 5711 RefPtr<DebuggerImmediateRunnable> runnable = 5712 new DebuggerImmediateRunnable(this, aHandler); 5713 if (!runnable->Dispatch(this)) { 5714 aRv.Throw(NS_ERROR_FAILURE); 5715 } 5716 } 5717 5718 void WorkerPrivate::ReportErrorToDebugger(const nsACString& aFilename, 5719 uint32_t aLineno, 5720 const nsAString& aMessage) { 5721 AssertIsOnWorkerThread(); 5722 mDebugger->ReportErrorToDebugger(aFilename, aLineno, aMessage); 5723 RefPtr<RemoteWorkerDebuggerChild> remoteDebugger; 5724 { 5725 MutexAutoLock lock(mMutex); 5726 if (!mRemoteDebugger) { 5727 return; 5728 } 5729 remoteDebugger = mRemoteDebugger; 5730 } 5731 MOZ_ASSERT_DEBUG_OR_FUZZING(remoteDebugger); 5732 (void)remoteDebugger->SendReportErrorToDebugger(RemoteWorkerDebuggerErrorInfo( 5733 nsAutoCString(aFilename), aLineno, nsAutoString(aMessage))); 5734 } 5735 5736 void WorkerPrivate::UpdateWindowIDToDebugger(const uint64_t& aWindowID, 5737 const bool& aIsAdd) { 5738 AssertIsOnWorkerThread(); 5739 // only need to update the remote debugger since local debugger grab the 5740 // windowIDs information from RemoteWorkerChild directly. 5741 5742 RefPtr<RemoteWorkerDebuggerChild> remoteDebugger; 5743 { 5744 MutexAutoLock lock(mMutex); 5745 if (!mRemoteDebugger) { 5746 return; 5747 } 5748 remoteDebugger = mRemoteDebugger; 5749 } 5750 MOZ_ASSERT_DEBUG_OR_FUZZING(remoteDebugger); 5751 if (aIsAdd) { 5752 (void)remoteDebugger->SendAddWindowID(aWindowID); 5753 } else { 5754 (void)remoteDebugger->SendRemoveWindowID(aWindowID); 5755 } 5756 } 5757 5758 bool WorkerPrivate::NotifyInternal(WorkerStatus aStatus) { 5759 auto data = mWorkerThreadAccessible.Access(); 5760 5761 // Yield execution while notifying out-of-module WorkerRefs and cancelling 5762 // runnables. 5763 AutoYieldJSThreadExecution yield; 5764 5765 NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!"); 5766 5767 RefPtr<EventTarget> eventTarget; 5768 5769 // Save the old status and set the new status. 5770 { 5771 MutexAutoLock lock(mMutex); 5772 5773 LOG(WorkerLog(), 5774 ("WorkerPrivate::NotifyInternal [%p] mStatus: %u, aStatus: %u", this, 5775 static_cast<uint8_t>(mStatus), static_cast<uint8_t>(aStatus))); 5776 5777 if (mStatus >= aStatus) { 5778 return true; 5779 } 5780 5781 MOZ_ASSERT_IF(aStatus == Killing, 5782 mStatus == Canceling && mParentStatus == Canceling); 5783 5784 mStatus = aStatus; 5785 5786 // Mark parent status as closing immediately to avoid new events being 5787 // dispatched after we clear the queue below. 5788 if (aStatus == Closing) { 5789 Close(); 5790 } 5791 5792 // Synchronize the mParentStatus with mStatus, such that event dispatching 5793 // will fail in proper after WorkerPrivate gets into Killing status. 5794 if (aStatus >= Killing) { 5795 mParentStatus = aStatus; 5796 } 5797 } 5798 5799 // Status transistion to "Canceling"/"Killing", mark the scope as dying when 5800 // "Canceling," or shutdown the StorageManager when "Killing." 5801 if (aStatus >= Canceling) { 5802 if (data->mScope) { 5803 if (aStatus == Canceling) { 5804 data->mScope->NoteTerminating(); 5805 } else { 5806 data->mScope->NoteShuttingDown(); 5807 } 5808 } 5809 } 5810 5811 if (aStatus >= Closing) { 5812 CancelAllTimeouts(); 5813 5814 JSContext* cx = GetJSContext(); 5815 if (cx) { 5816 // This will invoke the JS async task finished callback for cancellable 5817 // JS tasks, which will invoke JSAsyncTaskFinished and remove from 5818 // mPendingJSAsyncTasks. 5819 // 5820 // There may still be outstanding JS tasks for things that couldn't be 5821 // cancelled. These must either finish normally, or be blocked on 5822 // through a call to JS::ShutdownAsyncTasks. Cycle collector shutdown 5823 // will call this during worker shutdown. 5824 JS::CancelAsyncTasks(cx); 5825 } 5826 } 5827 5828 if (aStatus == Closing && GlobalScope()) { 5829 GlobalScope()->SetIsNotEligibleForMessaging(); 5830 } 5831 5832 // Let all our holders know the new status. 5833 if (aStatus == Canceling) { 5834 NotifyWorkerRefs(aStatus); 5835 } 5836 5837 if (aStatus == Canceling && mRemoteWorkerNonLifeCycleOpController) { 5838 mRemoteWorkerNonLifeCycleOpController->TransistionStateToCanceled(); 5839 } 5840 5841 if (aStatus == Killing && mRemoteWorkerNonLifeCycleOpController) { 5842 mRemoteWorkerNonLifeCycleOpController->TransistionStateToKilled(); 5843 mRemoteWorkerNonLifeCycleOpController = nullptr; 5844 } 5845 5846 // If the worker script never ran, or failed to compile, we don't need to do 5847 // anything else. 5848 WorkerGlobalScope* global = GlobalScope(); 5849 if (!global) { 5850 if (aStatus == Canceling) { 5851 MOZ_ASSERT(!data->mCancelBeforeWorkerScopeConstructed); 5852 data->mCancelBeforeWorkerScopeConstructed.Flip(); 5853 } 5854 return true; 5855 } 5856 5857 // Don't abort the script now, but we dispatch a runnable to do it when the 5858 // current JS frame is executed. 5859 if (aStatus == Closing) { 5860 if (!mSyncLoopStack.IsEmpty()) { 5861 LOG(WorkerLog(), ("WorkerPrivate::NotifyInternal [%p] request to " 5862 "dispatch canceling runnables...", 5863 this)); 5864 mPostSyncLoopOperations |= eDispatchCancelingRunnable; 5865 } else { 5866 DispatchCancelingRunnable(); 5867 } 5868 return true; 5869 } 5870 5871 MOZ_ASSERT(aStatus == Canceling || aStatus == Killing); 5872 5873 LOG(WorkerLog(), ("WorkerPrivate::NotifyInternal [%p] abort script", this)); 5874 5875 // Always abort the script. 5876 return false; 5877 } 5878 5879 void WorkerPrivate::ReportError(JSContext* aCx, 5880 JS::ConstUTF8CharsZ aToStringResult, 5881 JSErrorReport* aReport) { 5882 auto data = mWorkerThreadAccessible.Access(); 5883 5884 if (!MayContinueRunning() || data->mErrorHandlerRecursionCount == 2) { 5885 return; 5886 } 5887 5888 NS_ASSERTION(data->mErrorHandlerRecursionCount == 0 || 5889 data->mErrorHandlerRecursionCount == 1, 5890 "Bad recursion logic!"); 5891 5892 UniquePtr<WorkerErrorReport> report = MakeUnique<WorkerErrorReport>(); 5893 if (aReport) { 5894 report->AssignErrorReport(aReport); 5895 } 5896 5897 JS::ExceptionStack exnStack(aCx); 5898 // NOTE: This function is used both for errors and warnings, and warnings 5899 // can be reported while there's a pending exception. 5900 // Warnings are always reported with non-null JSErrorReport. 5901 if (!aReport || !aReport->isWarning()) { 5902 MOZ_ASSERT(JS_IsExceptionPending(aCx)); 5903 if (!JS::StealPendingExceptionStack(aCx, &exnStack)) { 5904 JS_ClearPendingException(aCx); 5905 return; 5906 } 5907 5908 JS::Rooted<JSObject*> stack(aCx), stackGlobal(aCx); 5909 xpc::FindExceptionStackForConsoleReport( 5910 nullptr, exnStack.exception(), exnStack.stack(), &stack, &stackGlobal); 5911 5912 if (stack) { 5913 JSAutoRealm ar(aCx, stackGlobal); 5914 report->SerializeWorkerStack(aCx, this, stack); 5915 } 5916 } 5917 5918 if (report->mMessage.IsEmpty() && aToStringResult) { 5919 nsDependentCString toStringResult(aToStringResult.c_str()); 5920 if (!AppendUTF8toUTF16(toStringResult, report->mMessage, 5921 mozilla::fallible)) { 5922 // Try again, with only a 1 KB string. Do this infallibly this time. 5923 // If the user doesn't have 1 KB to spare we're done anyways. 5924 size_t index = std::min<size_t>(1024, toStringResult.Length()); 5925 5926 // Drop the last code point that may be cropped. 5927 index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index); 5928 5929 nsDependentCString truncatedToStringResult(aToStringResult.c_str(), 5930 index); 5931 AppendUTF8toUTF16(truncatedToStringResult, report->mMessage); 5932 } 5933 } 5934 5935 data->mErrorHandlerRecursionCount++; 5936 5937 // Don't want to run the scope's error handler if this is a recursive error or 5938 // if we ran out of memory. 5939 bool fireAtScope = data->mErrorHandlerRecursionCount == 1 && 5940 report->mErrorNumber != JSMSG_OUT_OF_MEMORY && 5941 JS::CurrentGlobalOrNull(aCx); 5942 5943 WorkerErrorReport::ReportError(aCx, this, fireAtScope, nullptr, 5944 std::move(report), 0, exnStack.exception()); 5945 5946 data->mErrorHandlerRecursionCount--; 5947 } 5948 5949 // static 5950 void WorkerPrivate::ReportErrorToConsole( 5951 uint32_t aErrorFlags, const nsCString& aCategory, 5952 nsContentUtils::PropertiesFile aFile, const nsCString& aMessageName, 5953 const nsTArray<nsString>& aParams, 5954 const mozilla::SourceLocation& aLocation) { 5955 WorkerPrivate* wp = nullptr; 5956 if (!NS_IsMainThread()) { 5957 wp = GetCurrentThreadWorkerPrivate(); 5958 } 5959 5960 ReportErrorToConsoleRunnable::Report(wp, aErrorFlags, aCategory, aFile, 5961 aMessageName, aParams, aLocation); 5962 } 5963 5964 int32_t WorkerPrivate::SetTimeout(JSContext* aCx, TimeoutHandler* aHandler, 5965 int32_t aTimeout, bool aIsInterval, 5966 Timeout::Reason aReason, ErrorResult& aRv) { 5967 auto data = mWorkerThreadAccessible.Access(); 5968 MOZ_ASSERT(aHandler); 5969 5970 WorkerGlobalScope* globalScope = GlobalScope(); 5971 MOZ_DIAGNOSTIC_ASSERT(globalScope); 5972 auto* timeoutManager = globalScope->GetTimeoutManager(); 5973 MOZ_DIAGNOSTIC_ASSERT(timeoutManager); 5974 int32_t timerId = -1; 5975 WorkerStatus status; 5976 { 5977 MutexAutoLock lock(mMutex); 5978 status = mStatus; 5979 } 5980 // If the worker is trying to call setTimeout/setInterval and the 5981 // worker itself which has initiated the close process. 5982 if (status >= Closing) { 5983 return timeoutManager->GetTimeoutId(aReason); 5984 } 5985 bool hadTimeouts = timeoutManager->HasTimeouts(); 5986 nsresult rv = timeoutManager->SetTimeout(aHandler, aTimeout, aIsInterval, 5987 aReason, &timerId); 5988 if (NS_FAILED(rv)) { 5989 aRv.Throw(NS_ERROR_FAILURE); 5990 return timerId; 5991 } 5992 if (!hadTimeouts) { 5993 UpdateCCFlag(CCFlag::IneligibleForTimeout); 5994 } 5995 return timerId; 5996 } 5997 5998 void WorkerPrivate::ClearTimeout(int32_t aId, Timeout::Reason aReason) { 5999 MOZ_ASSERT(aReason == Timeout::Reason::eTimeoutOrInterval, 6000 "This timeout reason doesn't support cancellation."); 6001 WorkerGlobalScope* globalScope = GlobalScope(); 6002 MOZ_DIAGNOSTIC_ASSERT(globalScope); 6003 auto* timeoutManager = globalScope->GetTimeoutManager(); 6004 MOZ_DIAGNOSTIC_ASSERT(timeoutManager); 6005 timeoutManager->ClearTimeout(aId, aReason); 6006 if (!timeoutManager->HasTimeouts()) { 6007 UpdateCCFlag(CCFlag::EligibleForTimeout); 6008 } 6009 } 6010 6011 void WorkerPrivate::StartCancelingTimer() { 6012 AssertIsOnParentThread(); 6013 6014 // return if mCancelingTimer has already existed. 6015 if (mCancelingTimer) { 6016 return; 6017 } 6018 6019 auto errorCleanup = MakeScopeExit([&] { mCancelingTimer = nullptr; }); 6020 6021 if (WorkerPrivate* parent = GetParent()) { 6022 mCancelingTimer = NS_NewTimer(parent->ControlEventTarget()); 6023 } else { 6024 mCancelingTimer = NS_NewTimer(); 6025 } 6026 6027 if (NS_WARN_IF(!mCancelingTimer)) { 6028 return; 6029 } 6030 6031 // This is not needed if we are already in an advanced shutdown state. 6032 { 6033 MutexAutoLock lock(mMutex); 6034 if (ParentStatus() >= Canceling) { 6035 return; 6036 } 6037 } 6038 6039 uint32_t cancelingTimeoutMillis = 6040 StaticPrefs::dom_worker_canceling_timeoutMilliseconds(); 6041 6042 RefPtr<CancelingTimerCallback> callback = new CancelingTimerCallback(this); 6043 nsresult rv = mCancelingTimer->InitWithCallback( 6044 callback, cancelingTimeoutMillis, nsITimer::TYPE_ONE_SHOT); 6045 if (NS_WARN_IF(NS_FAILED(rv))) { 6046 return; 6047 } 6048 6049 errorCleanup.release(); 6050 } 6051 6052 void WorkerPrivate::UpdateContextOptionsInternal( 6053 JSContext* aCx, const JS::ContextOptions& aContextOptions) { 6054 auto data = mWorkerThreadAccessible.Access(); 6055 6056 JS::ContextOptionsRef(aCx) = aContextOptions; 6057 6058 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 6059 data->mChildWorkers[index]->UpdateContextOptions(aContextOptions); 6060 } 6061 } 6062 6063 void WorkerPrivate::UpdateLanguagesInternal( 6064 const nsTArray<nsString>& aLanguages) { 6065 WorkerGlobalScope* globalScope = GlobalScope(); 6066 RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator(); 6067 if (nav) { 6068 nav->SetLanguages(aLanguages); 6069 } 6070 6071 auto data = mWorkerThreadAccessible.Access(); 6072 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 6073 data->mChildWorkers[index]->UpdateLanguages(aLanguages); 6074 } 6075 6076 RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr); 6077 6078 event->InitEvent(u"languagechange"_ns, false, false); 6079 event->SetTrusted(true); 6080 6081 globalScope->DispatchEvent(*event); 6082 } 6083 6084 void WorkerPrivate::UpdateJSWorkerMemoryParameterInternal( 6085 JSContext* aCx, JSGCParamKey aKey, Maybe<uint32_t> aValue) { 6086 auto data = mWorkerThreadAccessible.Access(); 6087 6088 if (aValue) { 6089 JS_SetGCParameter(aCx, aKey, *aValue); 6090 } else { 6091 JS_ResetGCParameter(aCx, aKey); 6092 } 6093 6094 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 6095 data->mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aKey, aValue); 6096 } 6097 } 6098 6099 #ifdef JS_GC_ZEAL 6100 void WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal, 6101 uint32_t aFrequency) { 6102 auto data = mWorkerThreadAccessible.Access(); 6103 6104 JS::SetGCZeal(aCx, aGCZeal, aFrequency); 6105 6106 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 6107 data->mChildWorkers[index]->UpdateGCZeal(aGCZeal, aFrequency); 6108 } 6109 } 6110 #endif 6111 6112 void WorkerPrivate::SetLowMemoryStateInternal(JSContext* aCx, bool aState) { 6113 auto data = mWorkerThreadAccessible.Access(); 6114 6115 JS::SetLowMemoryState(aCx, aState); 6116 6117 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 6118 data->mChildWorkers[index]->SetLowMemoryState(aState); 6119 } 6120 } 6121 6122 void WorkerPrivate::SetCCCollectedAnything(bool collectedAnything) { 6123 mWorkerThreadAccessible.Access()->mCCCollectedAnything = collectedAnything; 6124 } 6125 6126 uint32_t WorkerPrivate::GetCurrentTimerNestingLevel() const { 6127 auto data = mWorkerThreadAccessible.Access(); 6128 return data->mScope 6129 ? data->mScope->GetTimeoutManager()->GetNestingLevelForWorker() 6130 : 0; 6131 } 6132 6133 bool WorkerPrivate::isLastCCCollectedAnything() { 6134 return mWorkerThreadAccessible.Access()->mCCCollectedAnything; 6135 } 6136 6137 void WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking, 6138 bool aCollectChildren) { 6139 // Perform GC followed by CC (the CC is triggered by 6140 // WorkerJSRuntime::CustomGCCallback at the end of the collection). 6141 6142 auto data = mWorkerThreadAccessible.Access(); 6143 6144 if (!GlobalScope()) { 6145 // We haven't compiled anything yet. Just bail out. 6146 return; 6147 } 6148 6149 if (aShrinking || aCollectChildren) { 6150 JS::PrepareForFullGC(aCx); 6151 6152 if (aShrinking && mSyncLoopStack.IsEmpty()) { 6153 JS::NonIncrementalGC(aCx, JS::GCOptions::Shrink, 6154 JS::GCReason::DOM_WORKER); 6155 6156 // Check whether the CC collected anything and if so GC again. This is 6157 // necessary to collect all garbage. 6158 if (data->mCCCollectedAnything) { 6159 JS::NonIncrementalGC(aCx, JS::GCOptions::Normal, 6160 JS::GCReason::DOM_WORKER); 6161 } 6162 6163 if (!aCollectChildren) { 6164 LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this)); 6165 } 6166 } else { 6167 JS::NonIncrementalGC(aCx, JS::GCOptions::Normal, 6168 JS::GCReason::DOM_WORKER); 6169 LOG(WorkerLog(), ("Worker %p collected garbage\n", this)); 6170 } 6171 } else { 6172 JS_MaybeGC(aCx); 6173 LOG(WorkerLog(), ("Worker %p collected periodic garbage\n", this)); 6174 } 6175 6176 if (aCollectChildren) { 6177 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 6178 data->mChildWorkers[index]->GarbageCollect(aShrinking); 6179 } 6180 } 6181 } 6182 6183 void WorkerPrivate::CycleCollectInternal(bool aCollectChildren) { 6184 auto data = mWorkerThreadAccessible.Access(); 6185 6186 nsCycleCollector_collect(CCReason::WORKER, nullptr); 6187 6188 if (aCollectChildren) { 6189 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 6190 data->mChildWorkers[index]->CycleCollect(); 6191 } 6192 } 6193 } 6194 6195 void WorkerPrivate::MemoryPressureInternal() { 6196 auto data = mWorkerThreadAccessible.Access(); 6197 6198 if (data->mScope) { 6199 RefPtr<Console> console = data->mScope->GetConsoleIfExists(); 6200 if (console) { 6201 console->ClearStorage(); 6202 } 6203 6204 RefPtr<Performance> performance = data->mScope->GetPerformanceIfExists(); 6205 if (performance) { 6206 performance->MemoryPressure(); 6207 } 6208 6209 data->mScope->RemoveReportRecords(); 6210 } 6211 6212 if (data->mDebuggerScope) { 6213 RefPtr<Console> console = data->mDebuggerScope->GetConsoleIfExists(); 6214 if (console) { 6215 console->ClearStorage(); 6216 } 6217 } 6218 6219 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) { 6220 data->mChildWorkers[index]->MemoryPressure(); 6221 } 6222 } 6223 6224 void WorkerPrivate::SetThread(WorkerThread* aThread) { 6225 if (aThread) { 6226 #ifdef DEBUG 6227 { 6228 bool isOnCurrentThread; 6229 MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread))); 6230 MOZ_ASSERT(!isOnCurrentThread); 6231 } 6232 #endif 6233 6234 MOZ_ASSERT(!mPRThread); 6235 mPRThread = PRThreadFromThread(aThread); 6236 MOZ_ASSERT(mPRThread); 6237 6238 mWorkerThreadAccessible.Transfer(mPRThread); 6239 } else { 6240 MOZ_ASSERT(mPRThread); 6241 } 6242 } 6243 6244 void WorkerPrivate::SetWorkerPrivateInWorkerThread( 6245 WorkerThread* const aThread) { 6246 LOG(WorkerLog(), 6247 ("WorkerPrivate::SetWorkerPrivateInWorkerThread [%p]", this)); 6248 MutexAutoLock lock(mMutex); 6249 6250 MOZ_ASSERT(!mThread); 6251 MOZ_ASSERT(mStatus == Pending); 6252 6253 mThread = aThread; 6254 mThread->SetWorker(WorkerThreadFriendKey{}, this); 6255 6256 if (!mPreStartRunnables.IsEmpty()) { 6257 for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) { 6258 MOZ_ALWAYS_SUCCEEDS(mThread->DispatchAnyThread( 6259 WorkerThreadFriendKey{}, mPreStartRunnables[index])); 6260 } 6261 // Don't clear mPreStartRunnables here, it will be cleared in the beginning 6262 // of WorkerPrivate::DoRunLoop() or when in WorkerPrivate::RunLoopNeverRan() 6263 } 6264 } 6265 6266 void WorkerPrivate::ResetWorkerPrivateInWorkerThread() { 6267 LOG(WorkerLog(), 6268 ("WorkerPrivate::ResetWorkerPrivateInWorkerThread [%p]", this)); 6269 RefPtr<WorkerThread> doomedThread; 6270 6271 // Release the mutex before doomedThread. 6272 MutexAutoLock lock(mMutex); 6273 MOZ_ASSERT(mStatus == Dead); 6274 6275 MOZ_ASSERT(mThread); 6276 6277 mThread->ClearEventQueueAndWorker(WorkerThreadFriendKey{}); 6278 mThread.swap(doomedThread); 6279 } 6280 6281 void WorkerPrivate::BeginCTypesCall() { 6282 AssertIsOnWorkerThread(); 6283 auto data = mWorkerThreadAccessible.Access(); 6284 6285 // Don't try to GC while we're blocked in a ctypes call. 6286 SetGCTimerMode(NoTimer); 6287 6288 data->mYieldJSThreadExecution.EmplaceBack(); 6289 } 6290 6291 void WorkerPrivate::EndCTypesCall() { 6292 AssertIsOnWorkerThread(); 6293 auto data = mWorkerThreadAccessible.Access(); 6294 6295 data->mYieldJSThreadExecution.RemoveLastElement(); 6296 6297 // Make sure the periodic timer is running before we start running JS again. 6298 SetGCTimerMode(PeriodicTimer); 6299 } 6300 6301 void WorkerPrivate::BeginCTypesCallback() { 6302 AssertIsOnWorkerThread(); 6303 6304 // Make sure the periodic timer is running before we start running JS again. 6305 SetGCTimerMode(PeriodicTimer); 6306 6307 // Re-requesting execution is not needed since the JSRuntime code calling 6308 // this will do an AutoEntryScript. 6309 } 6310 6311 void WorkerPrivate::EndCTypesCallback() { 6312 AssertIsOnWorkerThread(); 6313 6314 // Don't try to GC while we're blocked in a ctypes call. 6315 SetGCTimerMode(NoTimer); 6316 } 6317 6318 bool WorkerPrivate::ConnectMessagePort(JSContext* aCx, 6319 UniqueMessagePortId& aIdentifier) { 6320 AssertIsOnWorkerThread(); 6321 6322 WorkerGlobalScope* globalScope = GlobalScope(); 6323 6324 JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper()); 6325 MOZ_ASSERT(jsGlobal); 6326 6327 // This UniqueMessagePortId is used to create a new port, still connected 6328 // with the other one, but in the worker thread. 6329 ErrorResult rv; 6330 RefPtr<MessagePort> port = MessagePort::Create(globalScope, aIdentifier, rv); 6331 if (NS_WARN_IF(rv.Failed())) { 6332 rv.SuppressException(); 6333 return false; 6334 } 6335 6336 GlobalObject globalObject(aCx, jsGlobal); 6337 if (globalObject.Failed()) { 6338 return false; 6339 } 6340 6341 RootedDictionary<MessageEventInit> init(aCx); 6342 init.mData = JS_GetEmptyStringValue(aCx); 6343 init.mBubbles = false; 6344 init.mCancelable = false; 6345 init.mSource.SetValue().SetAsMessagePort() = port; 6346 if (!init.mPorts.AppendElement(port.forget(), fallible)) { 6347 return false; 6348 } 6349 6350 RefPtr<MessageEvent> event = 6351 MessageEvent::Constructor(globalObject, u"connect"_ns, init); 6352 6353 event->SetTrusted(true); 6354 6355 globalScope->DispatchEvent(*event); 6356 6357 return true; 6358 } 6359 6360 WorkerGlobalScope* WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx) { 6361 auto data = mWorkerThreadAccessible.Access(); 6362 6363 if (data->mScope) { 6364 return data->mScope; 6365 } 6366 6367 if (IsSharedWorker()) { 6368 data->mScope = 6369 new SharedWorkerGlobalScope(this, CreateClientSource(), WorkerName()); 6370 } else if (IsServiceWorker()) { 6371 data->mScope = new ServiceWorkerGlobalScope( 6372 this, CreateClientSource(), GetServiceWorkerRegistrationDescriptor()); 6373 } else { 6374 data->mScope = new DedicatedWorkerGlobalScope(this, CreateClientSource(), 6375 WorkerName()); 6376 } 6377 6378 JS::Rooted<JSObject*> global(aCx); 6379 NS_ENSURE_TRUE(data->mScope->WrapGlobalObject(aCx, &global), nullptr); 6380 6381 JSAutoRealm ar(aCx, global); 6382 6383 if (!RegisterBindings(aCx, global)) { 6384 data->mScope = nullptr; 6385 return nullptr; 6386 } 6387 6388 // Worker has already in "Canceling", let the WorkerGlobalScope start dying. 6389 if (data->mCancelBeforeWorkerScopeConstructed) { 6390 data->mScope->NoteTerminating(); 6391 data->mScope->DisconnectGlobalTeardownObservers(); 6392 } 6393 6394 JS_FireOnNewGlobalObject(aCx, global); 6395 6396 return data->mScope; 6397 } 6398 6399 WorkerDebuggerGlobalScope* WorkerPrivate::CreateDebuggerGlobalScope( 6400 JSContext* aCx) { 6401 auto data = mWorkerThreadAccessible.Access(); 6402 MOZ_ASSERT(!data->mDebuggerScope); 6403 6404 // The debugger global gets a dummy client, not the "real" client used by the 6405 // debugee worker. 6406 auto clientSource = ClientManager::CreateSource( 6407 GetClientType(), HybridEventTarget(), NullPrincipalInfo()); 6408 6409 data->mDebuggerScope = 6410 new WorkerDebuggerGlobalScope(this, std::move(clientSource)); 6411 6412 JS::Rooted<JSObject*> global(aCx); 6413 NS_ENSURE_TRUE(data->mDebuggerScope->WrapGlobalObject(aCx, &global), nullptr); 6414 6415 JSAutoRealm ar(aCx, global); 6416 6417 if (!RegisterDebuggerBindings(aCx, global)) { 6418 data->mDebuggerScope = nullptr; 6419 return nullptr; 6420 } 6421 6422 JS_FireOnNewGlobalObject(aCx, global); 6423 6424 return data->mDebuggerScope; 6425 } 6426 6427 bool WorkerPrivate::IsOnWorkerThread() const { 6428 // We can't use mThread because it must be protected by mMutex and sometimes 6429 // this method is called when mMutex is already locked. This method should 6430 // always work. 6431 MOZ_ASSERT(mPRThread, 6432 "AssertIsOnWorkerThread() called before a thread was assigned!"); 6433 6434 return mPRThread == PR_GetCurrentThread(); 6435 } 6436 6437 #ifdef DEBUG 6438 void WorkerPrivate::AssertIsOnWorkerThread() const { 6439 MOZ_ASSERT(IsOnWorkerThread()); 6440 } 6441 #endif // DEBUG 6442 6443 void WorkerPrivate::DumpCrashInformation(nsACString& aString) { 6444 auto data = mWorkerThreadAccessible.Access(); 6445 6446 aString.Append("IsChromeWorker("); 6447 if (IsChromeWorker()) { 6448 aString.Append(NS_ConvertUTF16toUTF8(ScriptURL())); 6449 } else { 6450 aString.Append("false"); 6451 } 6452 aString.Append(")"); 6453 for (const auto* workerRef : data->mWorkerRefs.NonObservingRange()) { 6454 if (workerRef->IsPreventingShutdown()) { 6455 aString.Append("|"); 6456 aString.Append(workerRef->Name()); 6457 const nsCString status = GET_WORKERREF_DEBUG_STATUS(workerRef); 6458 if (!status.IsEmpty()) { 6459 aString.Append("["); 6460 aString.Append(status); 6461 aString.Append("]"); 6462 } 6463 } 6464 } 6465 } 6466 6467 PerformanceStorage* WorkerPrivate::GetPerformanceStorage() { 6468 MOZ_ASSERT(mPerformanceStorage); 6469 return mPerformanceStorage; 6470 } 6471 6472 bool WorkerPrivate::ShouldResistFingerprinting(RFPTarget aTarget) const { 6473 return mLoadInfo.mShouldResistFingerprinting && 6474 nsRFPService::IsRFPEnabledFor( 6475 mLoadInfo.mOriginAttributes.IsPrivateBrowsing(), aTarget, 6476 mLoadInfo.mOverriddenFingerprintingSettings); 6477 } 6478 6479 void WorkerPrivate::SetRemoteWorkerController(RemoteWorkerChild* aController) { 6480 AssertIsOnMainThread(); 6481 MOZ_ASSERT(aController); 6482 MOZ_ASSERT(!mRemoteWorkerController); 6483 6484 mRemoteWorkerController = aController; 6485 } 6486 6487 RemoteWorkerChild* WorkerPrivate::GetRemoteWorkerController() { 6488 AssertIsOnMainThread(); 6489 MOZ_ASSERT(mRemoteWorkerController); 6490 return mRemoteWorkerController; 6491 } 6492 6493 RefPtr<GenericPromise> WorkerPrivate::SetServiceWorkerSkipWaitingFlag() { 6494 AssertIsOnWorkerThread(); 6495 MOZ_ASSERT(IsServiceWorker()); 6496 6497 RefPtr<RemoteWorkerChild> rwc = mRemoteWorkerController; 6498 6499 if (!rwc) { 6500 return GenericPromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR, __func__); 6501 } 6502 6503 RefPtr<GenericPromise> promise = 6504 rwc->MaybeSendSetServiceWorkerSkipWaitingFlag(); 6505 6506 return promise; 6507 } 6508 6509 const nsString& WorkerPrivate::Id() { 6510 if (mId.IsEmpty()) { 6511 mId = ComputeWorkerPrivateId(); 6512 } 6513 6514 MOZ_ASSERT(!mId.IsEmpty()); 6515 6516 return mId; 6517 } 6518 6519 bool WorkerPrivate::IsSharedMemoryAllowed() const { 6520 if (StaticPrefs:: 6521 dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) { 6522 return true; 6523 } 6524 6525 // Allow privileged addons to access shared memory. 6526 if (mIsPrivilegedAddonGlobal) { 6527 return true; 6528 } 6529 6530 return CrossOriginIsolated(); 6531 } 6532 6533 bool WorkerPrivate::CrossOriginIsolated() const { 6534 if (!StaticPrefs:: 6535 dom_postMessage_sharedArrayBuffer_withCOOP_COEP_AtStartup()) { 6536 return false; 6537 } 6538 6539 return mAgentClusterOpenerPolicy == 6540 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP; 6541 } 6542 6543 nsILoadInfo::CrossOriginEmbedderPolicy WorkerPrivate::GetEmbedderPolicy() 6544 const { 6545 if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) { 6546 return nsILoadInfo::EMBEDDER_POLICY_NULL; 6547 } 6548 6549 return mEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL); 6550 } 6551 6552 Result<Ok, nsresult> WorkerPrivate::SetEmbedderPolicy( 6553 nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) { 6554 MOZ_ASSERT(NS_IsMainThread()); 6555 MOZ_ASSERT(mEmbedderPolicy.isNothing()); 6556 6557 if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) { 6558 return Ok(); 6559 } 6560 6561 // https://html.spec.whatwg.org/multipage/browsers.html#check-a-global-object's-embedder-policy 6562 // If ownerPolicy's value is not compatible with cross-origin isolation or 6563 // policy's value is compatible with cross-origin isolation, then return true. 6564 EnsureOwnerEmbedderPolicy(); 6565 nsILoadInfo::CrossOriginEmbedderPolicy ownerPolicy = 6566 mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL); 6567 if (nsContentSecurityManager::IsCompatibleWithCrossOriginIsolation( 6568 ownerPolicy) && 6569 !nsContentSecurityManager::IsCompatibleWithCrossOriginIsolation( 6570 aPolicy)) { 6571 return Err(NS_ERROR_BLOCKED_BY_POLICY); 6572 } 6573 6574 mEmbedderPolicy.emplace(aPolicy); 6575 6576 return Ok(); 6577 } 6578 6579 void WorkerPrivate::InheritOwnerEmbedderPolicyOrNull(nsIRequest* aRequest) { 6580 MOZ_ASSERT(NS_IsMainThread()); 6581 MOZ_ASSERT(aRequest); 6582 6583 EnsureOwnerEmbedderPolicy(); 6584 6585 if (mOwnerEmbedderPolicy.isSome()) { 6586 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); 6587 MOZ_ASSERT(channel); 6588 6589 nsCOMPtr<nsIURI> scriptURI; 6590 MOZ_ALWAYS_SUCCEEDS(channel->GetURI(getter_AddRefs(scriptURI))); 6591 6592 bool isLocalScriptURI = false; 6593 MOZ_ALWAYS_SUCCEEDS(NS_URIChainHasFlags( 6594 scriptURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, 6595 &isLocalScriptURI)); 6596 6597 MOZ_RELEASE_ASSERT(isLocalScriptURI); 6598 } 6599 6600 mEmbedderPolicy.emplace( 6601 mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL)); 6602 } 6603 6604 bool WorkerPrivate::MatchEmbedderPolicy( 6605 nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) const { 6606 MOZ_ASSERT(NS_IsMainThread()); 6607 6608 if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) { 6609 return true; 6610 } 6611 6612 return mEmbedderPolicy.value() == aPolicy; 6613 } 6614 6615 nsILoadInfo::CrossOriginEmbedderPolicy WorkerPrivate::GetOwnerEmbedderPolicy() 6616 const { 6617 if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) { 6618 return nsILoadInfo::EMBEDDER_POLICY_NULL; 6619 } 6620 6621 return mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL); 6622 } 6623 6624 void WorkerPrivate::EnsureOwnerEmbedderPolicy() { 6625 MOZ_ASSERT(NS_IsMainThread()); 6626 MOZ_ASSERT(mOwnerEmbedderPolicy.isNothing()); 6627 6628 if (GetParent()) { 6629 mOwnerEmbedderPolicy.emplace(GetParent()->GetEmbedderPolicy()); 6630 } else if (GetWindow() && GetWindow()->GetWindowContext()) { 6631 mOwnerEmbedderPolicy.emplace( 6632 GetWindow()->GetWindowContext()->GetEmbedderPolicy()); 6633 } 6634 } 6635 6636 nsIPrincipal* WorkerPrivate::GetEffectiveStoragePrincipal() const { 6637 AssertIsOnWorkerThread(); 6638 6639 if (mLoadInfo.mUseRegularPrincipal) { 6640 return mLoadInfo.mPrincipal; 6641 } 6642 6643 return mLoadInfo.mPartitionedPrincipal; 6644 } 6645 6646 const mozilla::ipc::PrincipalInfo& 6647 WorkerPrivate::GetEffectiveStoragePrincipalInfo() const { 6648 AssertIsOnWorkerThread(); 6649 6650 if (mLoadInfo.mUseRegularPrincipal) { 6651 return *mLoadInfo.mPrincipalInfo; 6652 } 6653 6654 return *mLoadInfo.mPartitionedPrincipalInfo; 6655 } 6656 6657 NS_IMPL_ADDREF(WorkerPrivate::EventTarget) 6658 NS_IMPL_RELEASE(WorkerPrivate::EventTarget) 6659 6660 NS_INTERFACE_MAP_BEGIN(WorkerPrivate::EventTarget) 6661 NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget) 6662 NS_INTERFACE_MAP_ENTRY(nsIEventTarget) 6663 NS_INTERFACE_MAP_ENTRY(nsISupports) 6664 #ifdef DEBUG 6665 // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its 6666 // result. 6667 if (aIID.Equals(kDEBUGWorkerEventTargetIID)) { 6668 *aInstancePtr = this; 6669 return NS_OK; 6670 } else 6671 #endif 6672 NS_INTERFACE_MAP_END 6673 6674 NS_IMETHODIMP 6675 WorkerPrivate::EventTarget::DispatchFromScript(nsIRunnable* aRunnable, 6676 DispatchFlags aFlags) { 6677 return Dispatch(do_AddRef(aRunnable), aFlags); 6678 } 6679 6680 NS_IMETHODIMP 6681 WorkerPrivate::EventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, 6682 DispatchFlags aFlags) { 6683 // May be called on any thread! 6684 6685 // NOTE: This nsIEventTarget implementation never leaks aRunnable, even if 6686 // NS_DISPATCH_FALLIBLE is not set. 6687 nsCOMPtr<nsIRunnable> event(aRunnable); 6688 6689 RefPtr<WorkerRunnable> workerRunnable; 6690 6691 MutexAutoLock lock(mMutex); 6692 6693 if (mDisabled) { 6694 NS_WARNING( 6695 "A runnable was posted to a worker that is already shutting " 6696 "down!"); 6697 return NS_ERROR_UNEXPECTED; 6698 } 6699 6700 MOZ_ASSERT(mWorkerPrivate); 6701 MOZ_ASSERT(mNestedEventTarget); 6702 6703 if (event) { 6704 workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(event.forget()); 6705 } 6706 6707 nsresult rv = 6708 mWorkerPrivate->Dispatch(workerRunnable.forget(), mNestedEventTarget); 6709 if (NS_WARN_IF(NS_FAILED(rv))) { 6710 return rv; 6711 } 6712 6713 return NS_OK; 6714 } 6715 6716 NS_IMETHODIMP 6717 WorkerPrivate::EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, 6718 uint32_t) 6719 6720 { 6721 return NS_ERROR_NOT_IMPLEMENTED; 6722 } 6723 6724 NS_IMETHODIMP 6725 WorkerPrivate::EventTarget::RegisterShutdownTask(nsITargetShutdownTask* aTask) { 6726 return NS_ERROR_NOT_IMPLEMENTED; 6727 } 6728 6729 NS_IMETHODIMP 6730 WorkerPrivate::EventTarget::UnregisterShutdownTask( 6731 nsITargetShutdownTask* aTask) { 6732 return NS_ERROR_NOT_IMPLEMENTED; 6733 } 6734 6735 NS_IMETHODIMP 6736 WorkerPrivate::EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) { 6737 // May be called on any thread! 6738 6739 MOZ_ASSERT(aIsOnCurrentThread); 6740 6741 MutexAutoLock lock(mMutex); 6742 6743 if (mShutdown) { 6744 NS_WARNING( 6745 "A worker's event target was used after the worker has shutdown!"); 6746 return NS_ERROR_UNEXPECTED; 6747 } 6748 6749 MOZ_ASSERT(mNestedEventTarget); 6750 6751 *aIsOnCurrentThread = mNestedEventTarget->IsOnCurrentThread(); 6752 return NS_OK; 6753 } 6754 6755 NS_IMETHODIMP_(bool) 6756 WorkerPrivate::EventTarget::IsOnCurrentThreadInfallible() { 6757 // May be called on any thread! 6758 6759 MutexAutoLock lock(mMutex); 6760 6761 if (mShutdown) { 6762 NS_WARNING( 6763 "A worker's event target was used after the worker has shutdown!"); 6764 return false; 6765 } 6766 6767 MOZ_ASSERT(mNestedEventTarget); 6768 6769 return mNestedEventTarget->IsOnCurrentThread(); 6770 } 6771 6772 WorkerPrivate::AutoPushEventLoopGlobal::AutoPushEventLoopGlobal( 6773 WorkerPrivate* aWorkerPrivate, JSContext* aCx) { 6774 auto data = aWorkerPrivate->mWorkerThreadAccessible.Access(); 6775 mOldEventLoopGlobal = std::move(data->mCurrentEventLoopGlobal); 6776 if (JSObject* global = JS::CurrentGlobalOrNull(aCx)) { 6777 data->mCurrentEventLoopGlobal = xpc::NativeGlobal(global); 6778 } 6779 #ifdef DEBUG 6780 mNewEventLoopGlobal = data->mCurrentEventLoopGlobal; 6781 #endif 6782 } 6783 6784 WorkerPrivate::AutoPushEventLoopGlobal::~AutoPushEventLoopGlobal() { 6785 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 6786 // We are popping out the event loop global, WorkerPrivate is supposed to be 6787 // alive and in a valid status(Running or Canceling) 6788 MOZ_ASSERT(workerPrivate); 6789 auto data = workerPrivate->mWorkerThreadAccessible.Access(); 6790 #ifdef DEBUG 6791 // Saved event loop global should be matched. 6792 MOZ_ASSERT(data->mCurrentEventLoopGlobal == mNewEventLoopGlobal); 6793 mNewEventLoopGlobal = nullptr; 6794 #endif 6795 data->mCurrentEventLoopGlobal = std::move(mOldEventLoopGlobal); 6796 } 6797 6798 // FontVisibilityProvider implementation 6799 FontVisibility WorkerPrivate::GetFontVisibility() const { 6800 return mFontVisibility; 6801 } 6802 6803 void WorkerPrivate::ReportBlockedFontFamily(const nsCString& aMsg) const { 6804 MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Info, ("%s", aMsg.get())); 6805 nsContentUtils::ReportToConsoleNonLocalized(NS_ConvertUTF8toUTF16(aMsg), 6806 nsIScriptError::warningFlag, 6807 "Security"_ns, GetDocument()); 6808 } 6809 6810 bool WorkerPrivate::IsChrome() const { return IsChromeWorker(); } 6811 6812 bool WorkerPrivate::IsPrivateBrowsing() const { 6813 return mLoadInfo.mOriginAttributes.IsPrivateBrowsing(); 6814 } 6815 6816 nsICookieJarSettings* WorkerPrivate::GetCookieJarSettings() const { 6817 return CookieJarSettings(); 6818 } 6819 6820 Maybe<FontVisibility> WorkerPrivate::MaybeInheritFontVisibility() const { 6821 if (mParent) { 6822 // If we have a parent, we inherit the parent's font visibility. 6823 return Some(mParent->GetFontVisibility()); 6824 } 6825 6826 dom::Document* doc = GetDocument(); 6827 if (!doc) { 6828 return Nothing(); 6829 } 6830 6831 nsPresContext* presContext = doc->GetPresContext(); 6832 NS_ENSURE_TRUE(presContext, Nothing()); 6833 6834 return Some(presContext->GetFontVisibility()); 6835 } 6836 6837 void WorkerPrivate::UserFontSetUpdated(gfxUserFontEntry*) {} 6838 6839 // ----------------------------------------------------------------------------- 6840 // AutoSyncLoopHolder 6841 6842 AutoSyncLoopHolder::AutoSyncLoopHolder(WorkerPrivate* aWorkerPrivate, 6843 WorkerStatus aFailStatus, 6844 const char* const aName) 6845 : mTarget(aWorkerPrivate->CreateNewSyncLoop(aFailStatus)), 6846 mIndex(aWorkerPrivate->mSyncLoopStack.Length() - 1) { 6847 aWorkerPrivate->AssertIsOnWorkerThread(); 6848 LOGV( 6849 ("AutoSyncLoopHolder::AutoSyncLoopHolder [%p] creator: %s", this, aName)); 6850 if (aFailStatus < Canceling) { 6851 mWorkerRef = StrongWorkerRef::Create(aWorkerPrivate, aName, [aName]() { 6852 // Do nothing with the shutdown callback here since we need to wait for 6853 // the underlying SyncLoop to complete by itself. 6854 LOGV( 6855 ("AutoSyncLoopHolder::AutoSyncLoopHolder Worker starts to shutdown " 6856 "with a AutoSyncLoopHolder(%s).", 6857 aName)); 6858 }); 6859 } else { 6860 LOGV( 6861 ("AutoSyncLoopHolder::AutoSyncLoopHolder [%p] Create " 6862 "AutoSyncLoopHolder(%s) while Worker is shutting down", 6863 this, aName)); 6864 mWorkerRef = StrongWorkerRef::CreateForcibly(aWorkerPrivate, aName); 6865 } 6866 // mWorkerRef can be nullptr here. 6867 } 6868 6869 AutoSyncLoopHolder::~AutoSyncLoopHolder() { 6870 if (mWorkerRef && mTarget) { 6871 mWorkerRef->Private()->AssertIsOnWorkerThread(); 6872 mWorkerRef->Private()->StopSyncLoop(mTarget, NS_ERROR_FAILURE); 6873 mWorkerRef->Private()->DestroySyncLoop(mIndex); 6874 } 6875 } 6876 6877 nsresult AutoSyncLoopHolder::Run() { 6878 if (mWorkerRef) { 6879 WorkerPrivate* workerPrivate = mWorkerRef->Private(); 6880 MOZ_ASSERT(workerPrivate); 6881 6882 workerPrivate->AssertIsOnWorkerThread(); 6883 6884 nsresult rv = workerPrivate->RunCurrentSyncLoop(); 6885 6886 // The sync loop is done, sync loop has already destroyed in the end of 6887 // WorkerPrivate::RunCurrentSyncLoop(). So, release mWorkerRef here to 6888 // avoid destroying sync loop again in the ~AutoSyncLoopHolder(); 6889 mWorkerRef = nullptr; 6890 6891 return rv; 6892 } 6893 return NS_OK; 6894 } 6895 6896 nsISerialEventTarget* AutoSyncLoopHolder::GetSerialEventTarget() const { 6897 // This can be null if CreateNewSyncLoop() fails. 6898 return mTarget; 6899 } 6900 6901 // ----------------------------------------------------------------------------- 6902 // WorkerParentRef 6903 WorkerParentRef::WorkerParentRef(RefPtr<WorkerPrivate>& aWorkerPrivate) 6904 : mWorkerPrivate(aWorkerPrivate) { 6905 LOGV(("WorkerParentRef::WorkerParentRef [%p] aWorkerPrivate %p", this, 6906 aWorkerPrivate.get())); 6907 MOZ_ASSERT(mWorkerPrivate); 6908 mWorkerPrivate->AssertIsOnParentThread(); 6909 } 6910 6911 const RefPtr<WorkerPrivate>& WorkerParentRef::Private() const { 6912 if (mWorkerPrivate) { 6913 mWorkerPrivate->AssertIsOnParentThread(); 6914 } 6915 return mWorkerPrivate; 6916 } 6917 6918 void WorkerParentRef::DropWorkerPrivate() { 6919 LOGV(("WorkerParentRef::DropWorkerPrivate [%p]", this)); 6920 if (mWorkerPrivate) { 6921 mWorkerPrivate->AssertIsOnParentThread(); 6922 mWorkerPrivate = nullptr; 6923 } 6924 } 6925 6926 WorkerParentRef::~WorkerParentRef() = default; 6927 6928 } // namespace dom 6929 } // namespace mozilla