WorkerScope.cpp (52770B)
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 "mozilla/dom/WorkerScope.h" 8 9 #include <stdio.h> 10 11 #include <new> 12 #include <utility> 13 14 #include "Crypto.h" 15 #include "GeckoProfiler.h" 16 #include "MainThreadUtils.h" 17 #include "ScriptLoader.h" 18 #include "js/CompilationAndEvaluation.h" 19 #include "js/CompileOptions.h" 20 #include "js/RealmOptions.h" 21 #include "js/RootingAPI.h" 22 #include "js/SourceText.h" 23 #include "js/Value.h" 24 #include "js/Wrapper.h" 25 #include "jsapi.h" 26 #include "jsfriendapi.h" 27 #include "mozilla/AlreadyAddRefed.h" 28 #include "mozilla/BaseProfilerMarkersPrerequisites.h" 29 #include "mozilla/CycleCollectedJSContext.h" 30 #include "mozilla/ErrorResult.h" 31 #include "mozilla/EventListenerManager.h" 32 #include "mozilla/Logging.h" 33 #include "mozilla/Maybe.h" 34 #include "mozilla/MozPromise.h" 35 #include "mozilla/Mutex.h" 36 #include "mozilla/RefPtr.h" 37 #include "mozilla/Result.h" 38 #include "mozilla/StaticAnalysisFunctions.h" 39 #include "mozilla/StorageAccess.h" 40 #include "mozilla/UniquePtr.h" 41 #include "mozilla/dom/AutoEntryScript.h" 42 #include "mozilla/dom/BindingDeclarations.h" 43 #include "mozilla/dom/BindingUtils.h" 44 #include "mozilla/dom/BlobURLProtocolHandler.h" 45 #include "mozilla/dom/CSPEvalChecker.h" 46 #include "mozilla/dom/CallbackDebuggerNotification.h" 47 #include "mozilla/dom/ClientSource.h" 48 #include "mozilla/dom/Clients.h" 49 #include "mozilla/dom/Console.h" 50 #include "mozilla/dom/CookieStore.h" 51 #include "mozilla/dom/DOMMozPromiseRequestHolder.h" 52 #include "mozilla/dom/DOMString.h" 53 #include "mozilla/dom/DebuggerNotification.h" 54 #include "mozilla/dom/DebuggerNotificationBinding.h" 55 #include "mozilla/dom/DebuggerNotificationManager.h" 56 #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h" 57 #include "mozilla/dom/Fetch.h" 58 #include "mozilla/dom/FontFaceSet.h" 59 #include "mozilla/dom/IDBFactory.h" 60 #include "mozilla/dom/ImageBitmap.h" 61 #include "mozilla/dom/ImageBitmapSource.h" 62 #include "mozilla/dom/MessagePortBinding.h" 63 #include "mozilla/dom/Performance.h" 64 #include "mozilla/dom/Promise.h" 65 #include "mozilla/dom/PromiseWorkerProxy.h" 66 #include "mozilla/dom/ScriptSettings.h" 67 #include "mozilla/dom/SerializedStackHolder.h" 68 #include "mozilla/dom/ServiceWorker.h" 69 #include "mozilla/dom/ServiceWorkerDescriptor.h" 70 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h" 71 #include "mozilla/dom/ServiceWorkerManager.h" 72 #include "mozilla/dom/ServiceWorkerRegistration.h" 73 #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h" 74 #include "mozilla/dom/ServiceWorkerUtils.h" 75 #include "mozilla/dom/SharedWorkerGlobalScopeBinding.h" 76 #include "mozilla/dom/SimpleGlobalObject.h" 77 #include "mozilla/dom/TestUtils.h" 78 #include "mozilla/dom/TimeoutHandler.h" 79 #include "mozilla/dom/TimeoutManager.h" 80 #include "mozilla/dom/TrustedTypeUtils.h" 81 #include "mozilla/dom/TrustedTypesConstants.h" 82 #include "mozilla/dom/VsyncWorkerChild.h" 83 #include "mozilla/dom/WebTaskSchedulerWorker.h" 84 #include "mozilla/dom/WindowOrWorkerGlobalScopeBinding.h" 85 #include "mozilla/dom/WorkerCommon.h" 86 #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h" 87 #include "mozilla/dom/WorkerDocumentListener.h" 88 #include "mozilla/dom/WorkerGlobalScopeBinding.h" 89 #include "mozilla/dom/WorkerLocation.h" 90 #include "mozilla/dom/WorkerNavigator.h" 91 #include "mozilla/dom/WorkerPrivate.h" 92 #include "mozilla/dom/WorkerRunnable.h" 93 #include "mozilla/dom/cache/CacheStorage.h" 94 #include "mozilla/dom/cache/Types.h" 95 #include "mozilla/extensions/ExtensionBrowser.h" 96 #include "mozilla/fallible.h" 97 #include "mozilla/gfx/Rect.h" 98 #include "mozilla/ipc/BackgroundChild.h" 99 #include "mozilla/ipc/PBackgroundChild.h" 100 #include "mozilla/ipc/PBackgroundSharedTypes.h" 101 #include "nsAtom.h" 102 #include "nsCOMPtr.h" 103 #include "nsContentUtils.h" 104 #include "nsDebug.h" 105 #include "nsGkAtoms.h" 106 #include "nsIEventTarget.h" 107 #include "nsIGlobalObject.h" 108 #include "nsIScriptError.h" 109 #include "nsISerialEventTarget.h" 110 #include "nsIWeakReference.h" 111 #include "nsJSUtils.h" 112 #include "nsLiteralString.h" 113 #include "nsQueryObject.h" 114 #include "nsRFPService.h" 115 #include "nsReadableUtils.h" 116 #include "nsString.h" 117 #include "nsTArray.h" 118 #include "nsTLiteralString.h" 119 #include "nsThreadUtils.h" 120 #include "nsWeakReference.h" 121 #include "nsWrapperCacheInlines.h" 122 #include "nscore.h" 123 #include "xpcpublic.h" 124 125 #ifdef ANDROID 126 # include <android/log.h> 127 #endif 128 129 #ifdef XP_WIN 130 # undef PostMessage 131 #endif 132 133 using mozilla::dom::cache::CacheStorage; 134 using mozilla::dom::workerinternals::NamedWorkerGlobalScopeMixin; 135 using mozilla::ipc::BackgroundChild; 136 using mozilla::ipc::PBackgroundChild; 137 using mozilla::ipc::PrincipalInfo; 138 139 namespace mozilla::dom { 140 141 static mozilla::LazyLogModule sWorkerScopeLog("WorkerScope"); 142 143 #ifdef LOG 144 # undef LOG 145 #endif 146 #define LOG(args) MOZ_LOG(sWorkerScopeLog, LogLevel::Debug, args); 147 148 class WorkerScriptTimeoutHandler final : public ScriptTimeoutHandler { 149 public: 150 NS_DECL_ISUPPORTS_INHERITED 151 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WorkerScriptTimeoutHandler, 152 ScriptTimeoutHandler) 153 154 WorkerScriptTimeoutHandler(JSContext* aCx, nsIGlobalObject* aGlobal, 155 const nsAString& aExpression) 156 : ScriptTimeoutHandler(aCx, aGlobal, aExpression) {} 157 158 MOZ_CAN_RUN_SCRIPT virtual bool Call(const char* aExecutionReason) override; 159 160 private: 161 virtual ~WorkerScriptTimeoutHandler() = default; 162 }; 163 164 NS_IMPL_CYCLE_COLLECTION_INHERITED(WorkerScriptTimeoutHandler, 165 ScriptTimeoutHandler) 166 167 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerScriptTimeoutHandler) 168 NS_INTERFACE_MAP_END_INHERITING(ScriptTimeoutHandler) 169 170 NS_IMPL_ADDREF_INHERITED(WorkerScriptTimeoutHandler, ScriptTimeoutHandler) 171 NS_IMPL_RELEASE_INHERITED(WorkerScriptTimeoutHandler, ScriptTimeoutHandler) 172 173 bool WorkerScriptTimeoutHandler::Call(const char* aExecutionReason) { 174 nsAutoMicroTask mt; 175 AutoEntryScript aes(mGlobal, aExecutionReason, false); 176 177 JSContext* cx = aes.cx(); 178 JS::CompileOptions options(cx); 179 options.setFileAndLine(mCaller.FileName().get(), mCaller.mLine) 180 .setNoScriptRval(true); 181 options.setIntroductionType("domTimer"); 182 183 JS::Rooted<JS::Value> unused(cx); 184 JS::SourceText<char16_t> srcBuf; 185 if (!srcBuf.init(cx, mExpr.BeginReading(), mExpr.Length(), 186 JS::SourceOwnership::Borrowed) || 187 !JS::Evaluate(cx, options, srcBuf, &unused)) { 188 if (!JS_IsExceptionPending(cx)) { 189 return false; 190 } 191 } 192 193 return true; 194 }; 195 196 namespace workerinternals { 197 void NamedWorkerGlobalScopeMixin::GetName(DOMString& aName) const { 198 aName.AsAString() = mName; 199 } 200 static const char* GetTimeoutReasonString(Timeout* aTimeout) { 201 switch (aTimeout->mReason) { 202 case Timeout::Reason::eTimeoutOrInterval: 203 if (aTimeout->mIsInterval) { 204 return "setInterval handler"; 205 } 206 return "setTimeout handler"; 207 case Timeout::Reason::eIdleCallbackTimeout: 208 return "setIdleCallback handler (timed out)"; 209 case Timeout::Reason::eAbortSignalTimeout: 210 return "AbortSignal timeout"; 211 case Timeout::Reason::eDelayedWebTaskTimeout: 212 return "delayedWebTaskCallback handler (timed out)"; 213 case Timeout::Reason::eJSTimeout: 214 return "JS timeout"; 215 } 216 MOZ_CRASH("Unexpected enum value"); 217 return ""; 218 } 219 } // namespace workerinternals 220 221 NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerGlobalScopeBase) 222 223 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScopeBase, 224 DOMEventTargetHelper) 225 tmp->AssertIsOnWorkerThread(); 226 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole) 227 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mModuleLoader) 228 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSerialEventTarget) 229 tmp->TraverseObjectsInGlobal(cb); 230 // If we already exited WorkerThreadPrimaryRunnable, we will find it 231 // nullptr and there is nothing left to do here on the WorkerPrivate, 232 // in particular the timeouts have already been canceled and unlinked. 233 if (tmp->mWorkerPrivate) { 234 tmp->mWorkerPrivate->TraverseTimeouts(cb); 235 } 236 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 237 238 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScopeBase, 239 DOMEventTargetHelper) 240 tmp->AssertIsOnWorkerThread(); 241 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole) 242 NS_IMPL_CYCLE_COLLECTION_UNLINK(mModuleLoader) 243 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSerialEventTarget) 244 tmp->UnlinkObjectsInGlobal(); 245 // If we already exited WorkerThreadPrimaryRunnable, we will find it 246 // nullptr and there is nothing left to do here on the WorkerPrivate, 247 // in particular the timeouts have already been canceled and unlinked. 248 if (tmp->mWorkerPrivate) { 249 tmp->mWorkerPrivate->UnlinkTimeouts(); 250 } 251 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE 252 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 253 254 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerGlobalScopeBase, 255 DOMEventTargetHelper) 256 tmp->AssertIsOnWorkerThread(); 257 NS_IMPL_CYCLE_COLLECTION_TRACE_END 258 259 NS_IMPL_ADDREF_INHERITED(WorkerGlobalScopeBase, DOMEventTargetHelper) 260 NS_IMPL_RELEASE_INHERITED(WorkerGlobalScopeBase, DOMEventTargetHelper) 261 262 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerGlobalScopeBase) 263 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) 264 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 265 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 266 267 WorkerGlobalScopeBase::WorkerGlobalScopeBase( 268 WorkerPrivate* aWorkerPrivate, UniquePtr<ClientSource> aClientSource) 269 : mWorkerPrivate(aWorkerPrivate), 270 mClientSource(std::move(aClientSource)), 271 mSerialEventTarget(aWorkerPrivate->HybridEventTarget()) { 272 mTimeoutManager = MakeUnique<dom::TimeoutManager>( 273 *this, /* not used on workers */ 0, mSerialEventTarget, 274 mWorkerPrivate->IsChromeWorker()); 275 LOG(("WorkerGlobalScopeBase::WorkerGlobalScopeBase [%p]", this)); 276 MOZ_ASSERT(mWorkerPrivate); 277 #ifdef DEBUG 278 mWorkerPrivate->AssertIsOnWorkerThread(); 279 mWorkerThreadUsedOnlyForAssert = PR_GetCurrentThread(); 280 #endif 281 MOZ_ASSERT(mClientSource); 282 283 MOZ_DIAGNOSTIC_ASSERT( 284 mSerialEventTarget, 285 "There should be an event target when a worker global is created."); 286 287 // In workers, each DETH must have an owner. Because the global scope doesn't 288 // have one, let's set it as owner of itself. 289 BindToOwner(static_cast<nsIGlobalObject*>(this)); 290 } 291 292 WorkerGlobalScopeBase::~WorkerGlobalScopeBase() = default; 293 294 JSObject* WorkerGlobalScopeBase::GetGlobalJSObject() { 295 AssertIsOnWorkerThread(); 296 return GetWrapper(); 297 } 298 299 bool WorkerGlobalScopeBase::RunTimeoutHandler(mozilla::dom::Timeout* aTimeout) { 300 // this is almost a copy of nsGlobalWindowInner::RunTimeoutHandler 301 302 // Hold on to the timeout in case mExpr or mFunObj releases its 303 // doc. 304 // XXXbz Our caller guarantees it'll hold on to the timeout (because 305 // we're MOZ_CAN_RUN_SCRIPT), so we can probably stop doing that... 306 RefPtr<Timeout> timeout = aTimeout; 307 Timeout* last_running_timeout = mTimeoutManager->BeginRunningTimeout(timeout); 308 timeout->mRunning = true; 309 310 uint32_t nestingLevel = mTimeoutManager->GetNestingLevelForWorker(); 311 mTimeoutManager->SetNestingLevelForWorker(timeout->mNestingLevel); 312 313 const char* reason = workerinternals::GetTimeoutReasonString(timeout); 314 315 bool abortIntervalHandler; 316 { 317 RefPtr<TimeoutHandler> handler(timeout->mScriptHandler); 318 319 CallbackDebuggerNotificationGuard guard( 320 this, timeout->mIsInterval 321 ? DebuggerNotificationType::SetIntervalCallback 322 : DebuggerNotificationType::SetTimeoutCallback); 323 abortIntervalHandler = !handler->Call(reason); 324 } 325 326 // If we received an uncatchable exception, do not schedule the timeout again. 327 // This allows the slow script dialog to break easy DoS attacks like 328 // setInterval(function() { while(1); }, 100); 329 if (abortIntervalHandler) { 330 // If it wasn't an interval timer to begin with, this does nothing. If it 331 // was, we'll treat it as a timeout that we just ran and discard it when 332 // we return. 333 timeout->mIsInterval = false; 334 } 335 336 // We ignore any failures from calling EvaluateString() on the context or 337 // Call() on a Function here since we're in a loop 338 // where we're likely to be running timeouts whose OS timers 339 // didn't fire in time and we don't want to not fire those timers 340 // now just because execution of one timer failed. We can't 341 // propagate the error to anyone who cares about it from this 342 // point anyway, and the script context should have already reported 343 // the script error in the usual way - so we just drop it. 344 345 mTimeoutManager->SetNestingLevelForWorker(nestingLevel); 346 347 mTimeoutManager->EndRunningTimeout(last_running_timeout); 348 timeout->mRunning = false; 349 350 return timeout->mCleared; 351 } 352 353 JSObject* WorkerGlobalScopeBase::GetGlobalJSObjectPreserveColor() const { 354 AssertIsOnWorkerThread(); 355 return GetWrapperPreserveColor(); 356 } 357 358 bool WorkerGlobalScopeBase::IsSharedMemoryAllowed() const { 359 AssertIsOnWorkerThread(); 360 return mWorkerPrivate->IsSharedMemoryAllowed(); 361 } 362 363 bool WorkerGlobalScopeBase::ShouldResistFingerprinting( 364 RFPTarget aTarget) const { 365 AssertIsOnWorkerThread(); 366 return mWorkerPrivate->ShouldResistFingerprinting(aTarget); 367 } 368 369 OriginTrials WorkerGlobalScopeBase::Trials() const { 370 AssertIsOnWorkerThread(); 371 return mWorkerPrivate->Trials(); 372 } 373 374 StorageAccess WorkerGlobalScopeBase::GetStorageAccess() { 375 AssertIsOnWorkerThread(); 376 return mWorkerPrivate->StorageAccess(); 377 } 378 379 nsICookieJarSettings* WorkerGlobalScopeBase::GetCookieJarSettings() { 380 AssertIsOnWorkerThread(); 381 return mWorkerPrivate->CookieJarSettings(); 382 } 383 384 nsIURI* WorkerGlobalScopeBase::GetBaseURI() const { 385 return mWorkerPrivate->GetBaseURI(); 386 } 387 388 Maybe<ClientInfo> WorkerGlobalScopeBase::GetClientInfo() const { 389 return Some(mClientSource->Info()); 390 } 391 392 Maybe<ClientState> WorkerGlobalScopeBase::GetClientState() const { 393 Result<ClientState, ErrorResult> res = mClientSource->SnapshotState(); 394 if (res.isOk()) { 395 return Some(res.unwrap()); 396 } 397 398 res.unwrapErr().SuppressException(); 399 return Nothing(); 400 } 401 402 Maybe<ServiceWorkerDescriptor> WorkerGlobalScopeBase::GetController() const { 403 return mClientSource->GetController(); 404 } 405 406 mozilla::Result<mozilla::ipc::PrincipalInfo, nsresult> 407 WorkerGlobalScopeBase::GetStorageKey() { 408 AssertIsOnWorkerThread(); 409 410 const mozilla::ipc::PrincipalInfo& principalInfo = 411 mWorkerPrivate->GetEffectiveStoragePrincipalInfo(); 412 413 // Block expanded and null principals, let content and system through. 414 if (principalInfo.type() != 415 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo && 416 principalInfo.type() != 417 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) { 418 return Err(NS_ERROR_DOM_SECURITY_ERR); 419 } 420 421 return principalInfo; 422 } 423 424 void WorkerGlobalScopeBase::Control( 425 const ServiceWorkerDescriptor& aServiceWorker) { 426 AssertIsOnWorkerThread(); 427 MOZ_DIAGNOSTIC_ASSERT(!mWorkerPrivate->IsChromeWorker()); 428 MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate->Kind() != WorkerKindService); 429 430 if (IsBlobURI(mWorkerPrivate->GetBaseURI())) { 431 // Blob URL workers can only become controlled by inheriting from 432 // their parent. Make sure to note this properly. 433 mClientSource->InheritController(aServiceWorker); 434 } else { 435 // Otherwise this is a normal interception and we simply record the 436 // controller locally. 437 mClientSource->SetController(aServiceWorker); 438 } 439 } 440 441 nsresult WorkerGlobalScopeBase::Dispatch( 442 already_AddRefed<nsIRunnable>&& aRunnable) const { 443 return SerialEventTarget()->Dispatch(std::move(aRunnable), 444 NS_DISPATCH_NORMAL); 445 } 446 447 nsISerialEventTarget* WorkerGlobalScopeBase::SerialEventTarget() const { 448 AssertIsOnWorkerThread(); 449 return mSerialEventTarget; 450 } 451 452 // See also AutoJSAPI::ReportException 453 void WorkerGlobalScopeBase::ReportError(JSContext* aCx, 454 JS::Handle<JS::Value> aError, 455 CallerType, ErrorResult& aRv) { 456 JS::ErrorReportBuilder jsReport(aCx); 457 JS::ExceptionStack exnStack(aCx, aError, nullptr); 458 if (!jsReport.init(aCx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) { 459 return aRv.NoteJSContextException(aCx); 460 } 461 462 // Before invoking ReportError, put the exception back on the context, 463 // because it may want to put it in its error events and has no other way 464 // to get hold of it. After we invoke ReportError, clear the exception on 465 // cx(), just in case ReportError didn't. 466 JS::SetPendingExceptionStack(aCx, exnStack); 467 mWorkerPrivate->ReportError(aCx, jsReport.toStringResult(), 468 jsReport.report()); 469 JS_ClearPendingException(aCx); 470 } 471 472 void WorkerGlobalScopeBase::Atob(const nsAString& aAtob, nsAString& aOut, 473 ErrorResult& aRv) const { 474 AssertIsOnWorkerThread(); 475 aRv = nsContentUtils::Atob(aAtob, aOut); 476 } 477 478 void WorkerGlobalScopeBase::Btoa(const nsAString& aBtoa, nsAString& aOut, 479 ErrorResult& aRv) const { 480 AssertIsOnWorkerThread(); 481 aRv = nsContentUtils::Btoa(aBtoa, aOut); 482 } 483 484 already_AddRefed<Console> WorkerGlobalScopeBase::GetConsole(ErrorResult& aRv) { 485 AssertIsOnWorkerThread(); 486 487 if (!mConsole) { 488 mConsole = Console::Create(mWorkerPrivate->GetJSContext(), nullptr, aRv); 489 if (NS_WARN_IF(aRv.Failed())) { 490 return nullptr; 491 } 492 } 493 494 RefPtr<Console> console = mConsole; 495 return console.forget(); 496 } 497 498 uint64_t WorkerGlobalScopeBase::WindowID() const { 499 return mWorkerPrivate->WindowID(); 500 } 501 502 NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerGlobalScope) 503 504 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScope, 505 WorkerGlobalScopeBase) 506 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) 507 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) 508 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler) 509 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskSchedulingState) 510 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrustedTypePolicyFactory) 511 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) 512 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) 513 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet) 514 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB) 515 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage) 516 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager) 517 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 518 519 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScope, 520 WorkerGlobalScopeBase) 521 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto) 522 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) 523 if (tmp->mWebTaskScheduler) { 524 tmp->mWebTaskScheduler->Disconnect(); 525 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler) 526 } 527 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskSchedulingState) 528 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrustedTypePolicyFactory) 529 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) 530 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) 531 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet) 532 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB) 533 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage) 534 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager) 535 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 536 537 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(WorkerGlobalScope, 538 WorkerGlobalScopeBase) 539 540 WorkerGlobalScope::~WorkerGlobalScope() = default; 541 542 void WorkerGlobalScope::NoteTerminating() { 543 LOG(("WorkerGlobalScope::NoteTerminating [%p]", this)); 544 if (IsDying()) { 545 return; 546 } 547 548 StartDying(); 549 } 550 551 void WorkerGlobalScope::NoteShuttingDown() { 552 MOZ_ASSERT(IsDying()); 553 LOG(("WorkerGlobalScope::NoteShuttingDown [%p]", this)); 554 555 if (mNavigator) { 556 mNavigator->Invalidate(); 557 mNavigator = nullptr; 558 } 559 } 560 561 Crypto* WorkerGlobalScope::GetCrypto(ErrorResult& aError) { 562 AssertIsOnWorkerThread(); 563 564 if (!mCrypto) { 565 mCrypto = new Crypto(this); 566 } 567 568 return mCrypto; 569 } 570 571 already_AddRefed<CacheStorage> WorkerGlobalScope::GetCaches(ErrorResult& aRv) { 572 if (!mCacheStorage) { 573 mCacheStorage = CacheStorage::CreateOnWorker(cache::DEFAULT_NAMESPACE, this, 574 mWorkerPrivate, aRv); 575 mWorkerPrivate->NotifyStorageKeyUsed(); 576 } 577 578 RefPtr<CacheStorage> ref = mCacheStorage; 579 return ref.forget(); 580 } 581 582 bool WorkerGlobalScope::IsSecureContext() const { 583 bool globalSecure = JS::GetIsSecureContext( 584 js::GetNonCCWObjectRealm(GetWrapperPreserveColor())); 585 MOZ_ASSERT(globalSecure == mWorkerPrivate->IsSecureContext()); 586 return globalSecure; 587 } 588 589 already_AddRefed<WorkerLocation> WorkerGlobalScope::Location() { 590 AssertIsOnWorkerThread(); 591 592 if (!mLocation) { 593 mLocation = WorkerLocation::Create(mWorkerPrivate->GetLocationInfo()); 594 MOZ_ASSERT(mLocation); 595 } 596 597 RefPtr<WorkerLocation> location = mLocation; 598 return location.forget(); 599 } 600 601 already_AddRefed<WorkerNavigator> WorkerGlobalScope::Navigator() { 602 AssertIsOnWorkerThread(); 603 604 if (!mNavigator) { 605 bool onLine = mWorkerPrivate->OnLine(); 606 if (mWorkerPrivate->ShouldResistFingerprinting( 607 RFPTarget::NetworkConnection)) { 608 onLine = true; 609 } 610 mNavigator = WorkerNavigator::Create(onLine); 611 MOZ_ASSERT(mNavigator); 612 } 613 614 RefPtr<WorkerNavigator> navigator = mNavigator; 615 return navigator.forget(); 616 } 617 618 already_AddRefed<WorkerNavigator> WorkerGlobalScope::GetExistingNavigator() 619 const { 620 AssertIsOnWorkerThread(); 621 622 RefPtr<WorkerNavigator> navigator = mNavigator; 623 return navigator.forget(); 624 } 625 626 FontFaceSet* WorkerGlobalScope::GetFonts(ErrorResult& aRv) { 627 AssertIsOnWorkerThread(); 628 629 if (!mFontFaceSet) { 630 mFontFaceSet = FontFaceSet::CreateForWorker(this, mWorkerPrivate); 631 if (MOZ_UNLIKELY(!mFontFaceSet)) { 632 aRv.ThrowInvalidStateError("Couldn't acquire worker reference"); 633 return nullptr; 634 } 635 } 636 637 return mFontFaceSet; 638 } 639 640 OnErrorEventHandlerNonNull* WorkerGlobalScope::GetOnerror() { 641 AssertIsOnWorkerThread(); 642 643 EventListenerManager* elm = GetExistingListenerManager(); 644 return elm ? elm->GetOnErrorEventHandler() : nullptr; 645 } 646 647 void WorkerGlobalScope::SetOnerror(OnErrorEventHandlerNonNull* aHandler) { 648 AssertIsOnWorkerThread(); 649 650 EventListenerManager* elm = GetOrCreateListenerManager(); 651 if (elm) { 652 elm->SetEventHandler(aHandler); 653 } 654 } 655 656 void WorkerGlobalScope::ImportScripts( 657 JSContext* aCx, const Sequence<OwningTrustedScriptURLOrString>& aScriptURLs, 658 nsIPrincipal* aSubjectPrincipal, ErrorResult& aRv) { 659 AssertIsOnWorkerThread(); 660 661 UniquePtr<SerializedStackHolder> stack; 662 if (mWorkerPrivate->IsWatchedByDevTools()) { 663 stack = GetCurrentStackForNetMonitor(aCx); 664 } 665 666 { 667 nsTArray<nsString> scriptURLs; 668 nsCOMPtr<nsIGlobalObject> pinnedGlobal = this; 669 for (const auto& scriptURL : aScriptURLs) { 670 constexpr nsLiteralString sink = u"WorkerGlobalScope importScripts"_ns; 671 Maybe<nsAutoString> compliantStringHolder; 672 const nsAString* compliantString = 673 TrustedTypeUtils::GetTrustedTypesCompliantString( 674 scriptURL, sink, kTrustedTypesOnlySinkGroup, *pinnedGlobal, 675 aSubjectPrincipal, compliantStringHolder, aRv); 676 if (aRv.Failed()) { 677 return; 678 } 679 scriptURLs.AppendElement(*compliantString); 680 } 681 AUTO_PROFILER_MARKER_TEXT( 682 "ImportScripts", JS, MarkerStack::Capture(), 683 profiler_thread_is_being_profiled_for_markers() 684 ? StringJoin(","_ns, scriptURLs, 685 [](nsACString& dest, const auto& scriptUrl) { 686 AppendUTF16toUTF8( 687 Substring( 688 scriptUrl, 0, 689 std::min(size_t(128), scriptUrl.Length())), 690 dest); 691 }) 692 : nsAutoCString{}); 693 workerinternals::Load(mWorkerPrivate, std::move(stack), scriptURLs, 694 WorkerScript, aRv); 695 } 696 } 697 698 int32_t WorkerGlobalScope::SetTimeout( 699 JSContext* aCx, const FunctionOrTrustedScriptOrString& aHandler, 700 const int32_t aTimeout, const Sequence<JS::Value>& aArguments, 701 nsIPrincipal* aSubjectPrincipal, ErrorResult& aRv) { 702 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, aArguments, false, 703 aSubjectPrincipal, aRv); 704 } 705 706 void WorkerGlobalScope::ClearTimeout(int32_t aHandle) { 707 AssertIsOnWorkerThread(); 708 709 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout); 710 711 mWorkerPrivate->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval); 712 } 713 714 int32_t WorkerGlobalScope::SetInterval( 715 JSContext* aCx, const FunctionOrTrustedScriptOrString& aHandler, 716 const int32_t aTimeout, const Sequence<JS::Value>& aArguments, 717 nsIPrincipal* aSubjectPrincipal, ErrorResult& aRv) { 718 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, aArguments, true, 719 aSubjectPrincipal, aRv); 720 } 721 722 void WorkerGlobalScope::ClearInterval(int32_t aHandle) { 723 AssertIsOnWorkerThread(); 724 725 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval); 726 727 mWorkerPrivate->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval); 728 } 729 730 int32_t WorkerGlobalScope::SetTimeoutOrInterval( 731 JSContext* aCx, const FunctionOrTrustedScriptOrString& aHandler, 732 const int32_t aTimeout, const Sequence<JS::Value>& aArguments, 733 bool aIsInterval, nsIPrincipal* aSubjectPrincipal, ErrorResult& aRv) { 734 AssertIsOnWorkerThread(); 735 736 DebuggerNotificationDispatch( 737 this, aIsInterval ? DebuggerNotificationType::SetInterval 738 : DebuggerNotificationType::SetTimeout); 739 740 if (aHandler.IsFunction()) { 741 nsTArray<JS::Heap<JS::Value>> args; 742 if (!args.AppendElements(aArguments, fallible)) { 743 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 744 return 0; 745 } 746 RefPtr<TimeoutHandler> handler = new CallbackTimeoutHandler( 747 aCx, this, &aHandler.GetAsFunction(), std::move(args)); 748 return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, aIsInterval, 749 Timeout::Reason::eTimeoutOrInterval, aRv); 750 } 751 752 constexpr nsLiteralString sinkSetTimeout = u"WorkerGlobalScope setTimeout"_ns; 753 constexpr nsLiteralString sinkSetInterval = 754 u"WorkerGlobalScope setInterval"_ns; 755 Maybe<nsAutoString> compliantStringHolder; 756 nsCOMPtr<nsIGlobalObject> pinnedGlobal = this; 757 const nsAString* compliantString = 758 TrustedTypeUtils::GetTrustedTypesCompliantString( 759 aHandler, aIsInterval ? sinkSetInterval : sinkSetTimeout, 760 kTrustedTypesOnlySinkGroup, *pinnedGlobal, aSubjectPrincipal, 761 compliantStringHolder, aRv); 762 if (aRv.Failed()) { 763 return 0; 764 } 765 766 bool allowEval = false; 767 aRv = CSPEvalChecker::CheckForWorker(aCx, mWorkerPrivate, *compliantString, 768 &allowEval); 769 if (NS_WARN_IF(aRv.Failed()) || !allowEval) { 770 return 0; 771 } 772 773 RefPtr<TimeoutHandler> handler = 774 new WorkerScriptTimeoutHandler(aCx, this, *compliantString); 775 776 return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, aIsInterval, 777 Timeout::Reason::eTimeoutOrInterval, aRv); 778 } 779 780 bool WorkerGlobalScope::HasScheduledNormalOrHighPriorityWebTasks() const { 781 if (!mWebTaskScheduler) { 782 return false; 783 } 784 return mWebTaskScheduler->HasScheduledNormalOrHighPriorityWebTasks(); 785 } 786 787 void WorkerGlobalScope::GetOrigin(nsAString& aOrigin) const { 788 AssertIsOnWorkerThread(); 789 nsContentUtils::GetWebExposedOriginSerialization( 790 mWorkerPrivate->GetPrincipal(), aOrigin); 791 } 792 793 bool WorkerGlobalScope::CrossOriginIsolated() const { 794 return mWorkerPrivate->CrossOriginIsolated(); 795 } 796 797 void WorkerGlobalScope::Dump(const Optional<nsAString>& aString) const { 798 AssertIsOnWorkerThread(); 799 800 if (!aString.WasPassed()) { 801 return; 802 } 803 804 if (!nsJSUtils::DumpEnabled()) { 805 return; 806 } 807 808 NS_ConvertUTF16toUTF8 str(aString.Value()); 809 810 MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, 811 ("[Worker.Dump] %s", str.get())); 812 #ifdef ANDROID 813 __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get()); 814 #endif 815 fputs(str.get(), stdout); 816 fflush(stdout); 817 } 818 819 Performance* WorkerGlobalScope::GetPerformance() { 820 AssertIsOnWorkerThread(); 821 822 if (!mPerformance) { 823 mPerformance = Performance::CreateForWorker(this); 824 } 825 826 return mPerformance; 827 } 828 829 bool WorkerGlobalScope::IsInAutomation(JSContext* aCx, JSObject* /* unused */) { 830 return GetWorkerPrivateFromContext(aCx)->IsInAutomation(); 831 } 832 833 void WorkerGlobalScope::GetJSTestingFunctions( 834 JSContext* aCx, JS::MutableHandle<JSObject*> aFunctions, ErrorResult& aRv) { 835 JSObject* obj = js::GetTestingFunctions(aCx); 836 if (!obj) { 837 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 838 return; 839 } 840 841 aFunctions.set(obj); 842 } 843 844 already_AddRefed<Promise> WorkerGlobalScope::Fetch( 845 const RequestOrUTF8String& aInput, const RequestInit& aInit, 846 CallerType aCallerType, ErrorResult& aRv) { 847 return FetchRequest(this, aInput, aInit, aCallerType, aRv); 848 } 849 850 already_AddRefed<IDBFactory> WorkerGlobalScope::GetIndexedDB( 851 JSContext* aCx, ErrorResult& aErrorResult) { 852 AssertIsOnWorkerThread(); 853 854 RefPtr<IDBFactory> indexedDB = mIndexedDB; 855 856 if (!indexedDB) { 857 StorageAccess access = mWorkerPrivate->StorageAccess(); 858 859 bool allowed = true; 860 if (access == StorageAccess::eDeny) { 861 NS_WARNING("IndexedDB is not allowed in this worker!"); 862 allowed = false; 863 } 864 865 if (ShouldPartitionStorage(access) && 866 !StoragePartitioningEnabled(access, 867 mWorkerPrivate->CookieJarSettings())) { 868 NS_WARNING("IndexedDB is not allowed in this worker!"); 869 allowed = false; 870 } 871 872 auto windowID = mWorkerPrivate->WindowID(); 873 874 auto principalInfoPtr = 875 allowed ? MakeUnique<PrincipalInfo>( 876 mWorkerPrivate->GetEffectiveStoragePrincipalInfo()) 877 : nullptr; 878 auto res = IDBFactory::CreateForWorker(this, std::move(principalInfoPtr), 879 windowID); 880 881 if (NS_WARN_IF(res.isErr())) { 882 aErrorResult = res.unwrapErr(); 883 return nullptr; 884 } 885 886 indexedDB = res.unwrap(); 887 mIndexedDB = indexedDB; 888 } 889 890 mWorkerPrivate->NotifyStorageKeyUsed(); 891 892 return indexedDB.forget(); 893 } 894 895 WebTaskScheduler* WorkerGlobalScope::Scheduler() { 896 mWorkerPrivate->AssertIsOnWorkerThread(); 897 898 if (!mWebTaskScheduler) { 899 mWebTaskScheduler = WebTaskScheduler::CreateForWorker(mWorkerPrivate); 900 } 901 902 MOZ_ASSERT(mWebTaskScheduler); 903 return mWebTaskScheduler; 904 } 905 906 WebTaskScheduler* WorkerGlobalScope::GetExistingScheduler() const { 907 return mWebTaskScheduler; 908 } 909 910 inline void WorkerGlobalScope::SetWebTaskSchedulingState( 911 WebTaskSchedulingState* aState) { 912 mWebTaskSchedulingState = aState; 913 } 914 915 already_AddRefed<Promise> WorkerGlobalScope::CreateImageBitmap( 916 const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions, 917 ErrorResult& aRv) { 918 return ImageBitmap::Create(this, aImage, Nothing(), aOptions, aRv); 919 } 920 921 already_AddRefed<Promise> WorkerGlobalScope::CreateImageBitmap( 922 const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw, 923 int32_t aSh, const ImageBitmapOptions& aOptions, ErrorResult& aRv) { 924 return ImageBitmap::Create( 925 this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aOptions, aRv); 926 } 927 928 // https://html.spec.whatwg.org/#structured-cloning 929 void WorkerGlobalScope::StructuredClone( 930 JSContext* aCx, JS::Handle<JS::Value> aValue, 931 const StructuredSerializeOptions& aOptions, 932 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) { 933 nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRetval, aError); 934 } 935 936 mozilla::dom::DebuggerNotificationManager* 937 WorkerGlobalScope::GetOrCreateDebuggerNotificationManager() { 938 if (!mDebuggerNotificationManager) { 939 mDebuggerNotificationManager = new DebuggerNotificationManager(this); 940 } 941 942 return mDebuggerNotificationManager; 943 } 944 945 mozilla::dom::DebuggerNotificationManager* 946 WorkerGlobalScope::GetExistingDebuggerNotificationManager() { 947 return mDebuggerNotificationManager; 948 } 949 950 Maybe<EventCallbackDebuggerNotificationType> 951 WorkerGlobalScope::GetDebuggerNotificationType() const { 952 return Some(EventCallbackDebuggerNotificationType::Global); 953 } 954 955 already_AddRefed<ServiceWorkerContainer> 956 WorkerGlobalScope::GetServiceWorkerContainer() { 957 return RefPtr(Navigator())->ServiceWorker(); 958 } 959 960 RefPtr<ServiceWorker> WorkerGlobalScope::GetOrCreateServiceWorker( 961 const ServiceWorkerDescriptor& aDescriptor) { 962 RefPtr<ServiceWorker> ref; 963 ForEachGlobalTeardownObserver( 964 [&](GlobalTeardownObserver* aObserver, bool* aDoneOut) { 965 RefPtr<ServiceWorker> sw = do_QueryObject(aObserver); 966 if (!sw || !sw->Descriptor().Matches(aDescriptor)) { 967 return; 968 } 969 970 ref = std::move(sw); 971 *aDoneOut = true; 972 }); 973 974 if (!ref) { 975 ref = ServiceWorker::Create(this, aDescriptor); 976 } 977 978 return ref; 979 } 980 981 RefPtr<ServiceWorkerRegistration> 982 WorkerGlobalScope::GetServiceWorkerRegistration( 983 const ServiceWorkerRegistrationDescriptor& aDescriptor) const { 984 AssertIsOnWorkerThread(); 985 RefPtr<ServiceWorkerRegistration> ref; 986 ForEachGlobalTeardownObserver( 987 [&](GlobalTeardownObserver* aObserver, bool* aDoneOut) { 988 RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aObserver); 989 if (!swr || !swr->MatchesDescriptor(aDescriptor)) { 990 return; 991 } 992 993 ref = std::move(swr); 994 *aDoneOut = true; 995 }); 996 return ref; 997 } 998 999 RefPtr<ServiceWorkerRegistration> 1000 WorkerGlobalScope::GetOrCreateServiceWorkerRegistration( 1001 const ServiceWorkerRegistrationDescriptor& aDescriptor) { 1002 AssertIsOnWorkerThread(); 1003 RefPtr<ServiceWorkerRegistration> ref = 1004 GetServiceWorkerRegistration(aDescriptor); 1005 if (!ref) { 1006 ref = ServiceWorkerRegistration::CreateForWorker(mWorkerPrivate, this, 1007 aDescriptor); 1008 } 1009 return ref; 1010 } 1011 1012 mozilla::dom::StorageManager* WorkerGlobalScope::GetStorageManager() { 1013 return RefPtr(Navigator())->Storage(); 1014 } 1015 1016 // https://html.spec.whatwg.org/multipage/web-messaging.html#eligible-for-messaging 1017 // * a WorkerGlobalScope object whose closing flag is false and whose worker 1018 // is not a suspendable worker. 1019 bool WorkerGlobalScope::IsEligibleForMessaging() { 1020 return mIsEligibleForMessaging; 1021 } 1022 1023 void WorkerGlobalScope::ReportToConsole( 1024 uint32_t aErrorFlags, const nsCString& aCategory, 1025 nsContentUtils::PropertiesFile aFile, const nsCString& aMessageName, 1026 const nsTArray<nsString>& aParams, 1027 const mozilla::SourceLocation& aLocation) { 1028 WorkerPrivate::ReportErrorToConsole(aErrorFlags, aCategory, aFile, 1029 aMessageName, aParams, aLocation); 1030 } 1031 1032 void WorkerGlobalScope::StorageAccessPermissionGranted() { 1033 // Reset the IndexedDB factory. 1034 mIndexedDB = nullptr; 1035 1036 // Reset DOM Cache 1037 mCacheStorage = nullptr; 1038 } 1039 1040 TrustedTypePolicyFactory* WorkerGlobalScope::TrustedTypes() { 1041 AssertIsOnWorkerThread(); 1042 if (!mTrustedTypePolicyFactory) { 1043 mTrustedTypePolicyFactory = MakeRefPtr<TrustedTypePolicyFactory>(this); 1044 } 1045 1046 return mTrustedTypePolicyFactory; 1047 } 1048 1049 bool WorkerGlobalScope::WindowInteractionAllowed() const { 1050 AssertIsOnWorkerThread(); 1051 return mWindowInteractionsAllowed > 0; 1052 } 1053 1054 void WorkerGlobalScope::AllowWindowInteraction() { 1055 AssertIsOnWorkerThread(); 1056 mWindowInteractionsAllowed++; 1057 } 1058 1059 void WorkerGlobalScope::ConsumeWindowInteraction() { 1060 AssertIsOnWorkerThread(); 1061 MOZ_ASSERT(mWindowInteractionsAllowed); 1062 mWindowInteractionsAllowed--; 1063 } 1064 1065 NS_IMPL_CYCLE_COLLECTION_INHERITED(DedicatedWorkerGlobalScope, 1066 WorkerGlobalScope, mFrameRequestManager) 1067 1068 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DedicatedWorkerGlobalScope, 1069 WorkerGlobalScope) 1070 NS_IMPL_CYCLE_COLLECTION_TRACE_END 1071 1072 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(DedicatedWorkerGlobalScope, 1073 WorkerGlobalScope) 1074 1075 DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope( 1076 WorkerPrivate* aWorkerPrivate, UniquePtr<ClientSource> aClientSource, 1077 const nsString& aName) 1078 : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource)), 1079 NamedWorkerGlobalScopeMixin(aName) {} 1080 1081 bool DedicatedWorkerGlobalScope::WrapGlobalObject( 1082 JSContext* aCx, JS::MutableHandle<JSObject*> aReflector) { 1083 AssertIsOnWorkerThread(); 1084 MOZ_ASSERT(!mWorkerPrivate->IsSharedWorker()); 1085 1086 JS::RealmOptions options; 1087 mWorkerPrivate->CopyJSRealmOptions(options); 1088 1089 xpc::SetPrefableRealmOptions(options); 1090 1091 return DedicatedWorkerGlobalScope_Binding::Wrap( 1092 aCx, this, this, options, 1093 nsJSPrincipals::get(mWorkerPrivate->GetPrincipal()), aReflector); 1094 } 1095 1096 void DedicatedWorkerGlobalScope::PostMessage( 1097 JSContext* aCx, JS::Handle<JS::Value> aMessage, 1098 const Sequence<JSObject*>& aTransferable, ErrorResult& aRv) { 1099 AssertIsOnWorkerThread(); 1100 mWorkerPrivate->PostMessageToParent(aCx, aMessage, aTransferable, aRv); 1101 } 1102 1103 void DedicatedWorkerGlobalScope::PostMessage( 1104 JSContext* aCx, JS::Handle<JS::Value> aMessage, 1105 const StructuredSerializeOptions& aOptions, ErrorResult& aRv) { 1106 AssertIsOnWorkerThread(); 1107 mWorkerPrivate->PostMessageToParent(aCx, aMessage, aOptions.mTransfer, aRv); 1108 } 1109 1110 void DedicatedWorkerGlobalScope::Close() { 1111 AssertIsOnWorkerThread(); 1112 mWorkerPrivate->CloseInternal(); 1113 } 1114 1115 uint32_t DedicatedWorkerGlobalScope::RequestAnimationFrame( 1116 FrameRequestCallback& aCallback, ErrorResult& aError) { 1117 AssertIsOnWorkerThread(); 1118 1119 DebuggerNotificationDispatch(this, 1120 DebuggerNotificationType::RequestAnimationFrame); 1121 1122 // Ensure the worker is associated with a window. 1123 if (mWorkerPrivate->WindowID() == UINT64_MAX) { 1124 aError.ThrowNotSupportedError("Worker has no associated owner Window"); 1125 return 0; 1126 } 1127 1128 if (!mVsyncChild) { 1129 PBackgroundChild* bgChild = BackgroundChild::GetOrCreateForCurrentThread(); 1130 mVsyncChild = MakeRefPtr<VsyncWorkerChild>(); 1131 1132 if (!bgChild || !mVsyncChild->Initialize(mWorkerPrivate) || 1133 !bgChild->SendPVsyncConstructor(mVsyncChild)) { 1134 mVsyncChild->Destroy(); 1135 mVsyncChild = nullptr; 1136 aError.ThrowNotSupportedError( 1137 "Worker failed to register for vsync to drive event loop"); 1138 return 0; 1139 } 1140 } 1141 1142 if (!mDocListener) { 1143 mDocListener = WorkerDocumentListener::Create(mWorkerPrivate); 1144 if (!mDocListener) { 1145 aError.ThrowNotSupportedError( 1146 "Worker failed to register for document visibility events"); 1147 return 0; 1148 } 1149 } 1150 1151 uint32_t handle = 0; 1152 aError = mFrameRequestManager.Schedule(aCallback, &handle); 1153 if (!aError.Failed() && mDocumentVisible) { 1154 mVsyncChild->TryObserve(); 1155 } 1156 return handle; 1157 } 1158 1159 void DedicatedWorkerGlobalScope::CancelAnimationFrame(uint32_t aHandle, 1160 ErrorResult& aError) { 1161 AssertIsOnWorkerThread(); 1162 1163 DebuggerNotificationDispatch(this, 1164 DebuggerNotificationType::CancelAnimationFrame); 1165 1166 // Ensure the worker is associated with a window. 1167 if (mWorkerPrivate->WindowID() == UINT64_MAX) { 1168 aError.ThrowNotSupportedError("Worker has no associated owner Window"); 1169 return; 1170 } 1171 1172 mFrameRequestManager.Cancel(aHandle); 1173 if (mVsyncChild && mFrameRequestManager.IsEmpty()) { 1174 mVsyncChild->TryUnobserve(); 1175 } 1176 } 1177 1178 void DedicatedWorkerGlobalScope::OnDocumentVisible(bool aVisible) { 1179 AssertIsOnWorkerThread(); 1180 1181 mDocumentVisible = aVisible; 1182 1183 // We only change state immediately when we become visible. If we become 1184 // hidden, then we wait for the next vsync tick to apply that. 1185 if (aVisible && !mFrameRequestManager.IsEmpty()) { 1186 mVsyncChild->TryObserve(); 1187 } 1188 } 1189 1190 void DedicatedWorkerGlobalScope::OnVsync(const VsyncEvent& aVsync) { 1191 AssertIsOnWorkerThread(); 1192 1193 if (mFrameRequestManager.IsEmpty() || !mDocumentVisible) { 1194 // If we ever receive a vsync event, and there are still no callbacks to 1195 // process, or we remain hidden, we should disable observing them. By 1196 // waiting an extra tick, we ensure we minimize extra IPC for content that 1197 // does not call requestFrameAnimation directly during the callback, or 1198 // that is rapidly toggling between hidden and visible. 1199 mVsyncChild->TryUnobserve(); 1200 return; 1201 } 1202 1203 RefPtr<DedicatedWorkerGlobalScope> scope(this); 1204 CallbackDebuggerNotificationGuard guard( 1205 scope, DebuggerNotificationType::RequestAnimationFrameCallback); 1206 1207 // This is similar to what we do in nsRefreshDriver::RunFrameRequestCallbacks 1208 // and Performance::TimeStampToDOMHighResForRendering in order to have the 1209 // same behaviour for requestAnimationFrame on both the main and worker 1210 // threads. 1211 DOMHighResTimeStamp timeStamp = 0; 1212 if (!aVsync.mTime.IsNull()) { 1213 timeStamp = mWorkerPrivate->TimeStampToDOMHighRes(aVsync.mTime); 1214 // 0 is an inappropriate mixin for this this area; however CSS Animations 1215 // needs to have it's Time Reduction Logic refactored, so it's currently 1216 // only clamping for RFP mode. RFP mode gives a much lower time precision, 1217 // so we accept the security leak here for now. 1218 timeStamp = nsRFPService::ReduceTimePrecisionAsMSecsRFPOnly( 1219 timeStamp, 0, this->GetRTPCallerType()); 1220 } 1221 1222 FrameRequestManager::FiringCallbacks callbacks(mFrameRequestManager); 1223 1224 for (auto& callback : callbacks.mList) { 1225 if (callback.mCancelled) { 1226 continue; 1227 } 1228 1229 // MOZ_KnownLive is OK, because the stack array `callbacks` keeps the 1230 // callback alive and the mCallback strong reference can't be mutated by 1231 // the call. 1232 LogFrameRequestCallback::Run run(callback.mCallback); 1233 MOZ_KnownLive(callback.mCallback)->Call(timeStamp); 1234 } 1235 } 1236 1237 SharedWorkerGlobalScope::SharedWorkerGlobalScope( 1238 WorkerPrivate* aWorkerPrivate, UniquePtr<ClientSource> aClientSource, 1239 const nsString& aName) 1240 : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource)), 1241 NamedWorkerGlobalScopeMixin(aName) {} 1242 1243 bool SharedWorkerGlobalScope::WrapGlobalObject( 1244 JSContext* aCx, JS::MutableHandle<JSObject*> aReflector) { 1245 AssertIsOnWorkerThread(); 1246 MOZ_ASSERT(mWorkerPrivate->IsSharedWorker()); 1247 1248 JS::RealmOptions options; 1249 mWorkerPrivate->CopyJSRealmOptions(options); 1250 1251 return SharedWorkerGlobalScope_Binding::Wrap( 1252 aCx, this, this, options, 1253 nsJSPrincipals::get(mWorkerPrivate->GetPrincipal()), aReflector); 1254 } 1255 1256 void SharedWorkerGlobalScope::Close() { 1257 AssertIsOnWorkerThread(); 1258 mWorkerPrivate->CloseInternal(); 1259 } 1260 1261 NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope, 1262 mClients, mExtensionBrowser, mRegistration, 1263 mCookieStore) 1264 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerGlobalScope) 1265 NS_INTERFACE_MAP_END_INHERITING(WorkerGlobalScope) 1266 1267 NS_IMPL_ADDREF_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) 1268 NS_IMPL_RELEASE_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) 1269 1270 ServiceWorkerGlobalScope::ServiceWorkerGlobalScope( 1271 WorkerPrivate* aWorkerPrivate, UniquePtr<ClientSource> aClientSource, 1272 const ServiceWorkerRegistrationDescriptor& aRegistrationDescriptor) 1273 : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource)), 1274 mScope(NS_ConvertUTF8toUTF16(aRegistrationDescriptor.Scope())) 1275 1276 // Eagerly create the registration because we will need to receive 1277 // updates about the state of the registration. We can't wait until 1278 // first access to start receiving these. 1279 , 1280 mRegistration( 1281 GetOrCreateServiceWorkerRegistration(aRegistrationDescriptor)) {} 1282 1283 ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope() = default; 1284 1285 bool ServiceWorkerGlobalScope::WrapGlobalObject( 1286 JSContext* aCx, JS::MutableHandle<JSObject*> aReflector) { 1287 AssertIsOnWorkerThread(); 1288 MOZ_ASSERT(mWorkerPrivate->IsServiceWorker()); 1289 1290 JS::RealmOptions options; 1291 mWorkerPrivate->CopyJSRealmOptions(options); 1292 1293 return ServiceWorkerGlobalScope_Binding::Wrap( 1294 aCx, this, this, options, 1295 nsJSPrincipals::get(mWorkerPrivate->GetPrincipal()), aReflector); 1296 } 1297 1298 already_AddRefed<Clients> ServiceWorkerGlobalScope::GetClients() { 1299 if (!mClients) { 1300 mClients = new Clients(this); 1301 } 1302 1303 RefPtr<Clients> ref = mClients; 1304 return ref.forget(); 1305 } 1306 1307 ServiceWorkerRegistration* ServiceWorkerGlobalScope::Registration() { 1308 return mRegistration; 1309 } 1310 1311 EventHandlerNonNull* ServiceWorkerGlobalScope::GetOnfetch() { 1312 AssertIsOnWorkerThread(); 1313 1314 return GetEventHandler(nsGkAtoms::onfetch); 1315 } 1316 1317 namespace { 1318 1319 class ReportFetchListenerWarningRunnable final : public Runnable { 1320 const nsCString mScope; 1321 mozilla::JSCallingLocation mCaller; 1322 1323 public: 1324 explicit ReportFetchListenerWarningRunnable(const nsString& aScope) 1325 : mozilla::Runnable("ReportFetchListenerWarningRunnable"), 1326 mScope(NS_ConvertUTF16toUTF8(aScope)) { 1327 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1328 MOZ_ASSERT(workerPrivate); 1329 JSContext* cx = workerPrivate->GetJSContext(); 1330 MOZ_ASSERT(cx); 1331 1332 mCaller = JSCallingLocation::Get(cx); 1333 } 1334 1335 NS_IMETHOD 1336 Run() override { 1337 AssertIsOnMainThread(); 1338 1339 ServiceWorkerManager::LocalizeAndReportToAllClients( 1340 mScope, "ServiceWorkerNoFetchHandler", nsTArray<nsString>{}, 1341 nsIScriptError::warningFlag, mCaller.FileName(), u""_ns, mCaller.mLine, 1342 mCaller.mColumn); 1343 1344 return NS_OK; 1345 } 1346 }; 1347 1348 } // anonymous namespace 1349 1350 void ServiceWorkerGlobalScope::NoteFetchHandlerWasAdded() const { 1351 if (mWorkerPrivate->WorkerScriptExecutedSuccessfully()) { 1352 RefPtr<Runnable> r = new ReportFetchListenerWarningRunnable(mScope); 1353 mWorkerPrivate->DispatchToMainThreadForMessaging(r.forget()); 1354 } 1355 mWorkerPrivate->SetFetchHandlerWasAdded(); 1356 } 1357 1358 void ServiceWorkerGlobalScope::SetOnfetch( 1359 mozilla::dom::EventHandlerNonNull* aCallback) { 1360 AssertIsOnWorkerThread(); 1361 1362 if (aCallback) { 1363 NoteFetchHandlerWasAdded(); 1364 } 1365 SetEventHandler(nsGkAtoms::onfetch, aCallback); 1366 } 1367 1368 void ServiceWorkerGlobalScope::EventListenerAdded(nsAtom* aType) { 1369 AssertIsOnWorkerThread(); 1370 1371 if (aType == nsGkAtoms::onfetch) { 1372 NoteFetchHandlerWasAdded(); 1373 } 1374 } 1375 1376 already_AddRefed<Promise> ServiceWorkerGlobalScope::SkipWaiting( 1377 ErrorResult& aRv) { 1378 AssertIsOnWorkerThread(); 1379 MOZ_ASSERT(mWorkerPrivate->IsServiceWorker()); 1380 1381 RefPtr<Promise> promise = Promise::Create(this, aRv); 1382 if (NS_WARN_IF(aRv.Failed())) { 1383 return nullptr; 1384 } 1385 1386 using MozPromiseType = 1387 decltype(mWorkerPrivate->SetServiceWorkerSkipWaitingFlag())::element_type; 1388 auto holder = MakeRefPtr<DOMMozPromiseRequestHolder<MozPromiseType>>(this); 1389 1390 mWorkerPrivate->SetServiceWorkerSkipWaitingFlag() 1391 ->Then(GetCurrentSerialEventTarget(), __func__, 1392 [holder, promise](const MozPromiseType::ResolveOrRejectValue&) { 1393 holder->Complete(); 1394 promise->MaybeResolveWithUndefined(); 1395 }) 1396 ->Track(*holder); 1397 1398 return promise.forget(); 1399 } 1400 1401 SafeRefPtr<extensions::ExtensionBrowser> 1402 ServiceWorkerGlobalScope::AcquireExtensionBrowser() { 1403 if (!mExtensionBrowser) { 1404 mExtensionBrowser = MakeSafeRefPtr<extensions::ExtensionBrowser>(this); 1405 } 1406 1407 return mExtensionBrowser.clonePtr(); 1408 } 1409 1410 already_AddRefed<CookieStore> ServiceWorkerGlobalScope::CookieStore() { 1411 if (!mCookieStore) { 1412 mCookieStore = CookieStore::Create(this); 1413 } 1414 1415 return do_AddRef(mCookieStore); 1416 } 1417 1418 bool WorkerDebuggerGlobalScope::WrapGlobalObject( 1419 JSContext* aCx, JS::MutableHandle<JSObject*> aReflector) { 1420 AssertIsOnWorkerThread(); 1421 1422 JS::RealmOptions options; 1423 mWorkerPrivate->CopyJSRealmOptions(options); 1424 1425 return WorkerDebuggerGlobalScope_Binding::Wrap( 1426 aCx, this, this, options, 1427 nsJSPrincipals::get(mWorkerPrivate->GetPrincipal()), aReflector); 1428 } 1429 1430 void WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx, 1431 JS::MutableHandle<JSObject*> aGlobal, 1432 ErrorResult& aRv) { 1433 WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); 1434 if (!scope) { 1435 aRv.Throw(NS_ERROR_FAILURE); 1436 return; 1437 } 1438 1439 aGlobal.set(scope->GetWrapper()); 1440 } 1441 1442 void WorkerDebuggerGlobalScope::CreateSandbox( 1443 JSContext* aCx, const nsAString& aName, JS::Handle<JSObject*> aPrototype, 1444 JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv) { 1445 AssertIsOnWorkerThread(); 1446 1447 aResult.set(nullptr); 1448 1449 JS::Rooted<JS::Value> protoVal(aCx); 1450 protoVal.setObjectOrNull(aPrototype); 1451 JS::Rooted<JSObject*> sandbox( 1452 aCx, 1453 SimpleGlobalObject::Create( 1454 SimpleGlobalObject::GlobalType::WorkerDebuggerSandbox, protoVal)); 1455 1456 if (!sandbox) { 1457 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 1458 return; 1459 } 1460 1461 if (!JS_WrapObject(aCx, &sandbox)) { 1462 aRv.NoteJSContextException(aCx); 1463 return; 1464 } 1465 1466 aResult.set(sandbox); 1467 } 1468 1469 void WorkerDebuggerGlobalScope::LoadSubScript( 1470 JSContext* aCx, const nsAString& aURL, 1471 const Optional<JS::Handle<JSObject*>>& aSandbox, ErrorResult& aRv) { 1472 AssertIsOnWorkerThread(); 1473 1474 Maybe<JSAutoRealm> ar; 1475 if (aSandbox.WasPassed()) { 1476 // We only care about worker debugger sandbox objects here, so 1477 // CheckedUnwrapStatic is fine. 1478 JS::Rooted<JSObject*> sandbox(aCx, 1479 js::CheckedUnwrapStatic(aSandbox.Value())); 1480 if (!sandbox || !IsWorkerDebuggerSandbox(sandbox)) { 1481 aRv.Throw(NS_ERROR_INVALID_ARG); 1482 return; 1483 } 1484 1485 ar.emplace(aCx, sandbox); 1486 } 1487 1488 nsTArray<nsString> urls; 1489 urls.AppendElement(aURL); 1490 workerinternals::Load(mWorkerPrivate, nullptr, urls, DebuggerScript, aRv); 1491 } 1492 1493 void WorkerDebuggerGlobalScope::EnterEventLoop() { 1494 // We're on the worker thread here, and WorkerPrivate's refcounting is 1495 // non-threadsafe: you can only do it on the parent thread. What that 1496 // means in practice is that we're relying on it being kept alive while 1497 // we run. Hopefully. 1498 MOZ_KnownLive(mWorkerPrivate)->EnterDebuggerEventLoop(); 1499 } 1500 1501 void WorkerDebuggerGlobalScope::LeaveEventLoop() { 1502 mWorkerPrivate->LeaveDebuggerEventLoop(); 1503 } 1504 1505 void WorkerDebuggerGlobalScope::PostMessage(const nsAString& aMessage) { 1506 mWorkerPrivate->PostMessageToDebugger(aMessage); 1507 } 1508 1509 void WorkerDebuggerGlobalScope::SetImmediate(Function& aHandler, 1510 ErrorResult& aRv) { 1511 mWorkerPrivate->SetDebuggerImmediate(aHandler, aRv); 1512 } 1513 1514 void WorkerDebuggerGlobalScope::ReportError(JSContext* aCx, 1515 const nsAString& aMessage) { 1516 auto caller = JSCallingLocation::Get(aCx); 1517 mWorkerPrivate->ReportErrorToDebugger(caller.FileName(), caller.mLine, 1518 aMessage); 1519 } 1520 1521 void WorkerDebuggerGlobalScope::RetrieveConsoleEvents( 1522 JSContext* aCx, nsTArray<JS::Value>& aEvents, ErrorResult& aRv) { 1523 WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); 1524 if (!scope) { 1525 aRv.Throw(NS_ERROR_FAILURE); 1526 return; 1527 } 1528 1529 RefPtr<Console> console = scope->GetConsole(aRv); 1530 if (NS_WARN_IF(aRv.Failed())) { 1531 return; 1532 } 1533 1534 console->RetrieveConsoleEvents(aCx, aEvents, aRv); 1535 } 1536 1537 void WorkerDebuggerGlobalScope::ClearConsoleEvents(JSContext* aCx, 1538 ErrorResult& aRv) { 1539 WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); 1540 if (!scope) { 1541 aRv.Throw(NS_ERROR_FAILURE); 1542 return; 1543 } 1544 1545 RefPtr<Console> console = scope->GetConsoleIfExists(); 1546 if (console) { 1547 console->ClearStorage(); 1548 } 1549 } 1550 1551 void WorkerDebuggerGlobalScope::SetConsoleEventHandler(JSContext* aCx, 1552 AnyCallback* aHandler, 1553 ErrorResult& aRv) { 1554 WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); 1555 if (!scope) { 1556 aRv.Throw(NS_ERROR_FAILURE); 1557 return; 1558 } 1559 1560 RefPtr<Console> console = scope->GetConsole(aRv); 1561 if (NS_WARN_IF(aRv.Failed())) { 1562 return; 1563 } 1564 1565 console->SetConsoleEventHandler(aHandler); 1566 } 1567 1568 void WorkerDebuggerGlobalScope::Dump(JSContext* aCx, 1569 const Optional<nsAString>& aString) const { 1570 WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); 1571 if (scope) { 1572 scope->Dump(aString); 1573 } 1574 } 1575 1576 bool IsWorkerGlobal(JSObject* object) { 1577 return IS_INSTANCE_OF(WorkerGlobalScope, object); 1578 } 1579 1580 bool IsWorkerDebuggerGlobal(JSObject* object) { 1581 return IS_INSTANCE_OF(WorkerDebuggerGlobalScope, object); 1582 } 1583 1584 bool IsWorkerDebuggerSandbox(JSObject* object) { 1585 return SimpleGlobalObject::SimpleGlobalType(object) == 1586 SimpleGlobalObject::GlobalType::WorkerDebuggerSandbox; 1587 } 1588 1589 } // namespace mozilla::dom