/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/WorkerScope.h" #include #include #include #include "Crypto.h" #include "GeckoProfiler.h" #include "MainThreadUtils.h" #include "ScriptLoader.h" #include "js/CompilationAndEvaluation.h" #include "js/CompileOptions.h" #include "js/RealmOptions.h" #include "js/RootingAPI.h" #include "js/SourceText.h" #include "js/Value.h" #include "js/Wrapper.h" #include "jsapi.h" #include "jsfriendapi.h" #include "mozilla/AlreadyAddRefed.h" #include "mozilla/BaseProfilerMarkersPrerequisites.h" #include "mozilla/CycleCollectedJSContext.h" #include "mozilla/ErrorResult.h" #include "mozilla/EventListenerManager.h" #include "mozilla/Logging.h" #include "mozilla/Maybe.h" #include "mozilla/MozPromise.h" #include "mozilla/Mutex.h" #include "mozilla/NotNull.h" #include "mozilla/RefPtr.h" #include "mozilla/Result.h" #include "mozilla/StaticAnalysisFunctions.h" #include "mozilla/StorageAccess.h" #include "mozilla/TaskCategory.h" #include "mozilla/UniquePtr.h" #include "mozilla/Unused.h" #include "mozilla/dom/AutoEntryScript.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BlobURLProtocolHandler.h" #include "mozilla/dom/CSPEvalChecker.h" #include "mozilla/dom/CallbackDebuggerNotification.h" #include "mozilla/dom/ClientSource.h" #include "mozilla/dom/Clients.h" #include "mozilla/dom/Console.h" #include "mozilla/dom/DOMMozPromiseRequestHolder.h" #include "mozilla/dom/DebuggerNotification.h" #include "mozilla/dom/DebuggerNotificationBinding.h" #include "mozilla/dom/DebuggerNotificationManager.h" #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h" #include "mozilla/dom/DOMString.h" #include "mozilla/dom/Fetch.h" #include "mozilla/dom/FontFaceSet.h" #include "mozilla/dom/IDBFactory.h" #include "mozilla/dom/ImageBitmap.h" #include "mozilla/dom/ImageBitmapSource.h" #include "mozilla/dom/MessagePortBinding.h" #include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/ipc/PBackgroundSharedTypes.h" #include "mozilla/dom/Performance.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseWorkerProxy.h" #include "mozilla/dom/WebTaskSchedulerWorker.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/SerializedStackHolder.h" #include "mozilla/dom/ServiceWorkerDescriptor.h" #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h" #include "mozilla/dom/ServiceWorkerManager.h" #include "mozilla/dom/ServiceWorkerRegistration.h" #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h" #include "mozilla/dom/ServiceWorkerUtils.h" #include "mozilla/dom/SharedWorkerGlobalScopeBinding.h" #include "mozilla/dom/SimpleGlobalObject.h" #include "mozilla/dom/TimeoutHandler.h" #include "mozilla/dom/TestUtils.h" #include "mozilla/dom/WorkerCommon.h" #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h" #include "mozilla/dom/WorkerGlobalScopeBinding.h" #include "mozilla/dom/WorkerLocation.h" #include "mozilla/dom/WorkerNavigator.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerRunnable.h" #include "mozilla/dom/WorkerDocumentListener.h" #include "mozilla/dom/VsyncWorkerChild.h" #include "mozilla/dom/cache/CacheStorage.h" #include "mozilla/dom/cache/Types.h" #include "mozilla/extensions/ExtensionBrowser.h" #include "mozilla/fallible.h" #include "mozilla/gfx/Rect.h" #include "nsAtom.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsDebug.h" #include "nsGkAtoms.h" #include "nsIEventTarget.h" #include "nsIGlobalObject.h" #include "nsIScriptError.h" #include "nsISerialEventTarget.h" #include "nsIWeakReference.h" #include "nsJSUtils.h" #include "nsLiteralString.h" #include "nsQueryObject.h" #include "nsReadableUtils.h" #include "nsRFPService.h" #include "nsString.h" #include "nsTArray.h" #include "nsTLiteralString.h" #include "nsThreadUtils.h" #include "nsWeakReference.h" #include "nsWrapperCacheInlines.h" #include "nscore.h" #include "xpcpublic.h" #ifdef ANDROID # include #endif #ifdef XP_WIN # undef PostMessage #endif using mozilla::dom::cache::CacheStorage; using mozilla::dom::workerinternals::NamedWorkerGlobalScopeMixin; using mozilla::ipc::BackgroundChild; using mozilla::ipc::PBackgroundChild; using mozilla::ipc::PrincipalInfo; namespace mozilla::dom { class WorkerScriptTimeoutHandler final : public ScriptTimeoutHandler { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WorkerScriptTimeoutHandler, ScriptTimeoutHandler) WorkerScriptTimeoutHandler(JSContext* aCx, nsIGlobalObject* aGlobal, const nsAString& aExpression) : ScriptTimeoutHandler(aCx, aGlobal, aExpression) {} MOZ_CAN_RUN_SCRIPT virtual bool Call(const char* aExecutionReason) override; private: virtual ~WorkerScriptTimeoutHandler() = default; }; NS_IMPL_CYCLE_COLLECTION_INHERITED(WorkerScriptTimeoutHandler, ScriptTimeoutHandler) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerScriptTimeoutHandler) NS_INTERFACE_MAP_END_INHERITING(ScriptTimeoutHandler) NS_IMPL_ADDREF_INHERITED(WorkerScriptTimeoutHandler, ScriptTimeoutHandler) NS_IMPL_RELEASE_INHERITED(WorkerScriptTimeoutHandler, ScriptTimeoutHandler) bool WorkerScriptTimeoutHandler::Call(const char* aExecutionReason) { nsAutoMicroTask mt; AutoEntryScript aes(mGlobal, aExecutionReason, false); JSContext* cx = aes.cx(); JS::CompileOptions options(cx); options.setFileAndLine(mFileName.get(), mLineNo).setNoScriptRval(true); options.setIntroductionType("domTimer"); JS::Rooted unused(cx); JS::SourceText srcBuf; if (!srcBuf.init(cx, mExpr.BeginReading(), mExpr.Length(), JS::SourceOwnership::Borrowed) || !JS::Evaluate(cx, options, srcBuf, &unused)) { if (!JS_IsExceptionPending(cx)) { return false; } } return true; }; namespace workerinternals { void NamedWorkerGlobalScopeMixin::GetName(DOMString& aName) const { aName.AsAString() = mName; } } // namespace workerinternals NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerGlobalScopeBase) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScopeBase, DOMEventTargetHelper) tmp->AssertIsOnWorkerThread(); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mModuleLoader) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSerialEventTarget) tmp->TraverseObjectsInGlobal(cb); // If we already exited WorkerThreadPrimaryRunnable, we will find it // nullptr and there is nothing left to do here on the WorkerPrivate, // in particular the timeouts have already been canceled and unlinked. if (tmp->mWorkerPrivate) { tmp->mWorkerPrivate->TraverseTimeouts(cb); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScopeBase, DOMEventTargetHelper) tmp->AssertIsOnWorkerThread(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole) NS_IMPL_CYCLE_COLLECTION_UNLINK(mModuleLoader) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSerialEventTarget) tmp->UnlinkObjectsInGlobal(); // If we already exited WorkerThreadPrimaryRunnable, we will find it // nullptr and there is nothing left to do here on the WorkerPrivate, // in particular the timeouts have already been canceled and unlinked. if (tmp->mWorkerPrivate) { tmp->mWorkerPrivate->UnlinkTimeouts(); } NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerGlobalScopeBase, DOMEventTargetHelper) tmp->AssertIsOnWorkerThread(); NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_ADDREF_INHERITED(WorkerGlobalScopeBase, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(WorkerGlobalScopeBase, DOMEventTargetHelper) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerGlobalScopeBase) NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) WorkerGlobalScopeBase::WorkerGlobalScopeBase( WorkerPrivate* aWorkerPrivate, UniquePtr aClientSource, bool aShouldResistFingerprinting) : mWorkerPrivate(aWorkerPrivate), mClientSource(std::move(aClientSource)), mSerialEventTarget(aWorkerPrivate->HybridEventTarget()), mShouldResistFingerprinting(aShouldResistFingerprinting) { MOZ_ASSERT(mWorkerPrivate); #ifdef DEBUG mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerThreadUsedOnlyForAssert = PR_GetCurrentThread(); #endif MOZ_ASSERT(mClientSource); MOZ_DIAGNOSTIC_ASSERT( mSerialEventTarget, "There should be an event target when a worker global is created."); // In workers, each DETH must have an owner. Because the global scope doesn't // have one, let's set it as owner of itself. BindToOwner(static_cast(this)); } WorkerGlobalScopeBase::~WorkerGlobalScopeBase() = default; JSObject* WorkerGlobalScopeBase::GetGlobalJSObject() { AssertIsOnWorkerThread(); return GetWrapper(); } JSObject* WorkerGlobalScopeBase::GetGlobalJSObjectPreserveColor() const { AssertIsOnWorkerThread(); return GetWrapperPreserveColor(); } bool WorkerGlobalScopeBase::IsSharedMemoryAllowed() const { AssertIsOnWorkerThread(); return mWorkerPrivate->IsSharedMemoryAllowed(); } bool WorkerGlobalScopeBase::ShouldResistFingerprinting( RFPTarget aTarget) const { AssertIsOnWorkerThread(); return mShouldResistFingerprinting && nsRFPService::IsRFPEnabledFor(aTarget); } OriginTrials WorkerGlobalScopeBase::Trials() const { AssertIsOnWorkerThread(); return mWorkerPrivate->Trials(); } StorageAccess WorkerGlobalScopeBase::GetStorageAccess() { AssertIsOnWorkerThread(); return mWorkerPrivate->StorageAccess(); } Maybe WorkerGlobalScopeBase::GetClientInfo() const { return Some(mClientSource->Info()); } Maybe WorkerGlobalScopeBase::GetController() const { return mClientSource->GetController(); } mozilla::Result WorkerGlobalScopeBase::GetStorageKey() { AssertIsOnWorkerThread(); const mozilla::ipc::PrincipalInfo& principalInfo = mWorkerPrivate->GetEffectiveStoragePrincipalInfo(); // Block expanded and null principals, let content and system through. if (principalInfo.type() != mozilla::ipc::PrincipalInfo::TContentPrincipalInfo && principalInfo.type() != mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) { return Err(NS_ERROR_DOM_SECURITY_ERR); } return principalInfo; } void WorkerGlobalScopeBase::Control( const ServiceWorkerDescriptor& aServiceWorker) { AssertIsOnWorkerThread(); MOZ_DIAGNOSTIC_ASSERT(!mWorkerPrivate->IsChromeWorker()); MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate->Kind() != WorkerKindService); if (IsBlobURI(mWorkerPrivate->GetBaseURI())) { // Blob URL workers can only become controlled by inheriting from // their parent. Make sure to note this properly. mClientSource->InheritController(aServiceWorker); } else { // Otherwise this is a normal interception and we simply record the // controller locally. mClientSource->SetController(aServiceWorker); } } nsresult WorkerGlobalScopeBase::Dispatch( TaskCategory aCategory, already_AddRefed&& aRunnable) { return EventTargetFor(aCategory)->Dispatch(std::move(aRunnable), NS_DISPATCH_NORMAL); } nsISerialEventTarget* WorkerGlobalScopeBase::EventTargetFor( TaskCategory) const { AssertIsOnWorkerThread(); return mSerialEventTarget; } // See also AutoJSAPI::ReportException void WorkerGlobalScopeBase::ReportError(JSContext* aCx, JS::Handle aError, CallerType, ErrorResult& aRv) { JS::ErrorReportBuilder jsReport(aCx); JS::ExceptionStack exnStack(aCx, aError, nullptr); if (!jsReport.init(aCx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) { return aRv.NoteJSContextException(aCx); } // Before invoking ReportError, put the exception back on the context, // because it may want to put it in its error events and has no other way // to get hold of it. After we invoke ReportError, clear the exception on // cx(), just in case ReportError didn't. JS::SetPendingExceptionStack(aCx, exnStack); mWorkerPrivate->ReportError(aCx, jsReport.toStringResult(), jsReport.report()); JS_ClearPendingException(aCx); } void WorkerGlobalScopeBase::Atob(const nsAString& aAtob, nsAString& aOut, ErrorResult& aRv) const { AssertIsOnWorkerThread(); aRv = nsContentUtils::Atob(aAtob, aOut); } void WorkerGlobalScopeBase::Btoa(const nsAString& aBtoa, nsAString& aOut, ErrorResult& aRv) const { AssertIsOnWorkerThread(); aRv = nsContentUtils::Btoa(aBtoa, aOut); } already_AddRefed WorkerGlobalScopeBase::GetConsole(ErrorResult& aRv) { AssertIsOnWorkerThread(); if (!mConsole) { mConsole = Console::Create(mWorkerPrivate->GetJSContext(), nullptr, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } } RefPtr console = mConsole; return console.forget(); } uint64_t WorkerGlobalScopeBase::WindowID() const { return mWorkerPrivate->WindowID(); } NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerGlobalScope) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScope, WorkerGlobalScopeBase) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScope, WorkerGlobalScopeBase) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) if (tmp->mWebTaskScheduler) { tmp->mWebTaskScheduler->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler) } NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(WorkerGlobalScope, WorkerGlobalScopeBase) WorkerGlobalScope::~WorkerGlobalScope() = default; void WorkerGlobalScope::NoteTerminating() { if (IsDying()) { return; } StartDying(); } void WorkerGlobalScope::NoteShuttingDown() { MOZ_ASSERT(IsDying()); if (mNavigator) { mNavigator->Invalidate(); mNavigator = nullptr; } if (mPerformance) { RefPtr pw = static_cast(mPerformance.get()); MOZ_ASSERT(pw); pw->NoteShuttingDown(); } } Crypto* WorkerGlobalScope::GetCrypto(ErrorResult& aError) { AssertIsOnWorkerThread(); if (!mCrypto) { mCrypto = new Crypto(this); } return mCrypto; } already_AddRefed WorkerGlobalScope::GetCaches(ErrorResult& aRv) { if (!mCacheStorage) { mCacheStorage = CacheStorage::CreateOnWorker(cache::DEFAULT_NAMESPACE, this, mWorkerPrivate, aRv); } RefPtr ref = mCacheStorage; return ref.forget(); } bool WorkerGlobalScope::IsSecureContext() const { bool globalSecure = JS::GetIsSecureContext( js::GetNonCCWObjectRealm(GetWrapperPreserveColor())); MOZ_ASSERT(globalSecure == mWorkerPrivate->IsSecureContext()); return globalSecure; } already_AddRefed WorkerGlobalScope::Location() { AssertIsOnWorkerThread(); if (!mLocation) { mLocation = WorkerLocation::Create(mWorkerPrivate->GetLocationInfo()); MOZ_ASSERT(mLocation); } RefPtr location = mLocation; return location.forget(); } already_AddRefed WorkerGlobalScope::Navigator() { AssertIsOnWorkerThread(); if (!mNavigator) { mNavigator = WorkerNavigator::Create(mWorkerPrivate->OnLine()); MOZ_ASSERT(mNavigator); } RefPtr navigator = mNavigator; return navigator.forget(); } already_AddRefed WorkerGlobalScope::GetExistingNavigator() const { AssertIsOnWorkerThread(); RefPtr navigator = mNavigator; return navigator.forget(); } FontFaceSet* WorkerGlobalScope::GetFonts(ErrorResult& aRv) { AssertIsOnWorkerThread(); if (!mFontFaceSet) { mFontFaceSet = FontFaceSet::CreateForWorker(this, mWorkerPrivate); if (MOZ_UNLIKELY(!mFontFaceSet)) { aRv.ThrowInvalidStateError("Couldn't acquire worker reference"); return nullptr; } } return mFontFaceSet; } OnErrorEventHandlerNonNull* WorkerGlobalScope::GetOnerror() { AssertIsOnWorkerThread(); EventListenerManager* elm = GetExistingListenerManager(); return elm ? elm->GetOnErrorEventHandler() : nullptr; } void WorkerGlobalScope::SetOnerror(OnErrorEventHandlerNonNull* aHandler) { AssertIsOnWorkerThread(); EventListenerManager* elm = GetOrCreateListenerManager(); if (elm) { elm->SetEventHandler(aHandler); } } void WorkerGlobalScope::ImportScripts(JSContext* aCx, const Sequence& aScriptURLs, ErrorResult& aRv) { AssertIsOnWorkerThread(); UniquePtr stack; if (mWorkerPrivate->IsWatchedByDevTools()) { stack = GetCurrentStackForNetMonitor(aCx); } { AUTO_PROFILER_MARKER_TEXT( "ImportScripts", JS, MarkerStack::Capture(), profiler_thread_is_being_profiled_for_markers() ? StringJoin(","_ns, aScriptURLs, [](nsACString& dest, const auto& scriptUrl) { AppendUTF16toUTF8( Substring( scriptUrl, 0, std::min(size_t(128), scriptUrl.Length())), dest); }) : nsAutoCString{}); workerinternals::Load(mWorkerPrivate, std::move(stack), aScriptURLs, WorkerScript, aRv); } } int32_t WorkerGlobalScope::SetTimeout(JSContext* aCx, Function& aHandler, const int32_t aTimeout, const Sequence& aArguments, ErrorResult& aRv) { return SetTimeoutOrInterval(aCx, aHandler, aTimeout, aArguments, false, aRv); } int32_t WorkerGlobalScope::SetTimeout(JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout, const Sequence& /* unused */, ErrorResult& aRv) { return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aRv); } void WorkerGlobalScope::ClearTimeout(int32_t aHandle) { AssertIsOnWorkerThread(); DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout); mWorkerPrivate->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval); } int32_t WorkerGlobalScope::SetInterval(JSContext* aCx, Function& aHandler, const int32_t aTimeout, const Sequence& aArguments, ErrorResult& aRv) { return SetTimeoutOrInterval(aCx, aHandler, aTimeout, aArguments, true, aRv); } int32_t WorkerGlobalScope::SetInterval(JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout, const Sequence& /* unused */, ErrorResult& aRv) { return SetTimeoutOrInterval(aCx, aHandler, aTimeout, true, aRv); } void WorkerGlobalScope::ClearInterval(int32_t aHandle) { AssertIsOnWorkerThread(); DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval); mWorkerPrivate->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval); } int32_t WorkerGlobalScope::SetTimeoutOrInterval( JSContext* aCx, Function& aHandler, const int32_t aTimeout, const Sequence& aArguments, bool aIsInterval, ErrorResult& aRv) { AssertIsOnWorkerThread(); DebuggerNotificationDispatch( this, aIsInterval ? DebuggerNotificationType::SetInterval : DebuggerNotificationType::SetTimeout); nsTArray> args; if (!args.AppendElements(aArguments, fallible)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return 0; } RefPtr handler = new CallbackTimeoutHandler(aCx, this, &aHandler, std::move(args)); return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, aIsInterval, Timeout::Reason::eTimeoutOrInterval, aRv); } int32_t WorkerGlobalScope::SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout, bool aIsInterval, ErrorResult& aRv) { AssertIsOnWorkerThread(); DebuggerNotificationDispatch( this, aIsInterval ? DebuggerNotificationType::SetInterval : DebuggerNotificationType::SetTimeout); bool allowEval = false; aRv = CSPEvalChecker::CheckForWorker(aCx, mWorkerPrivate, aHandler, &allowEval); if (NS_WARN_IF(aRv.Failed()) || !allowEval) { return 0; } RefPtr handler = new WorkerScriptTimeoutHandler(aCx, this, aHandler); return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, aIsInterval, Timeout::Reason::eTimeoutOrInterval, aRv); } void WorkerGlobalScope::GetOrigin(nsAString& aOrigin) const { AssertIsOnWorkerThread(); nsContentUtils::GetUTFOrigin(mWorkerPrivate->GetPrincipal(), aOrigin); } bool WorkerGlobalScope::CrossOriginIsolated() const { return mWorkerPrivate->CrossOriginIsolated(); } void WorkerGlobalScope::Dump(const Optional& aString) const { AssertIsOnWorkerThread(); if (!aString.WasPassed()) { return; } if (!nsJSUtils::DumpEnabled()) { return; } NS_ConvertUTF16toUTF8 str(aString.Value()); MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, ("[Worker.Dump] %s", str.get())); #ifdef ANDROID __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get()); #endif fputs(str.get(), stdout); fflush(stdout); } Performance* WorkerGlobalScope::GetPerformance() { AssertIsOnWorkerThread(); if (!mPerformance) { mPerformance = Performance::CreateForWorker(mWorkerPrivate); } return mPerformance; } bool WorkerGlobalScope::IsInAutomation(JSContext* aCx, JSObject* /* unused */) { return GetWorkerPrivateFromContext(aCx)->IsInAutomation(); } void WorkerGlobalScope::GetJSTestingFunctions( JSContext* aCx, JS::MutableHandle aFunctions, ErrorResult& aRv) { JSObject* obj = js::GetTestingFunctions(aCx); if (!obj) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } aFunctions.set(obj); } already_AddRefed WorkerGlobalScope::Fetch( const RequestOrUSVString& aInput, const RequestInit& aInit, CallerType aCallerType, ErrorResult& aRv) { return FetchRequest(this, aInput, aInit, aCallerType, aRv); } already_AddRefed WorkerGlobalScope::GetIndexedDB( JSContext* aCx, ErrorResult& aErrorResult) { AssertIsOnWorkerThread(); RefPtr indexedDB = mIndexedDB; if (!indexedDB) { StorageAccess access = mWorkerPrivate->StorageAccess(); if (access == StorageAccess::eDeny) { NS_WARNING("IndexedDB is not allowed in this worker!"); aErrorResult = NS_ERROR_DOM_SECURITY_ERR; return nullptr; } if (ShouldPartitionStorage(access) && !StoragePartitioningEnabled(access, mWorkerPrivate->CookieJarSettings())) { NS_WARNING("IndexedDB is not allowed in this worker!"); aErrorResult = NS_ERROR_DOM_SECURITY_ERR; return nullptr; } const PrincipalInfo& principalInfo = mWorkerPrivate->GetEffectiveStoragePrincipalInfo(); auto res = IDBFactory::CreateForWorker(this, principalInfo, mWorkerPrivate->WindowID()); if (NS_WARN_IF(res.isErr())) { aErrorResult = res.unwrapErr(); return nullptr; } indexedDB = res.unwrap(); mIndexedDB = indexedDB; } return indexedDB.forget(); } WebTaskScheduler* WorkerGlobalScope::Scheduler() { mWorkerPrivate->AssertIsOnWorkerThread(); if (!mWebTaskScheduler) { mWebTaskScheduler = WebTaskScheduler::CreateForWorker(mWorkerPrivate); } MOZ_ASSERT(mWebTaskScheduler); return mWebTaskScheduler; } WebTaskScheduler* WorkerGlobalScope::GetExistingScheduler() const { return mWebTaskScheduler; } already_AddRefed WorkerGlobalScope::CreateImageBitmap( const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions, ErrorResult& aRv) { return ImageBitmap::Create(this, aImage, Nothing(), aOptions, aRv); } already_AddRefed WorkerGlobalScope::CreateImageBitmap( const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh, const ImageBitmapOptions& aOptions, ErrorResult& aRv) { return ImageBitmap::Create( this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aOptions, aRv); } // https://html.spec.whatwg.org/#structured-cloning void WorkerGlobalScope::StructuredClone( JSContext* aCx, JS::Handle aValue, const StructuredSerializeOptions& aOptions, JS::MutableHandle aRetval, ErrorResult& aError) { nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRetval, aError); } mozilla::dom::DebuggerNotificationManager* WorkerGlobalScope::GetOrCreateDebuggerNotificationManager() { if (!mDebuggerNotificationManager) { mDebuggerNotificationManager = new DebuggerNotificationManager(this); } return mDebuggerNotificationManager; } mozilla::dom::DebuggerNotificationManager* WorkerGlobalScope::GetExistingDebuggerNotificationManager() { return mDebuggerNotificationManager; } Maybe WorkerGlobalScope::GetDebuggerNotificationType() const { return Some(EventCallbackDebuggerNotificationType::Global); } RefPtr WorkerGlobalScope::GetServiceWorkerRegistration( const ServiceWorkerRegistrationDescriptor& aDescriptor) const { AssertIsOnWorkerThread(); RefPtr ref; ForEachGlobalTeardownObserver( [&](GlobalTeardownObserver* aObserver, bool* aDoneOut) { RefPtr swr = do_QueryObject(aObserver); if (!swr || !swr->MatchesDescriptor(aDescriptor)) { return; } ref = std::move(swr); *aDoneOut = true; }); return ref; } RefPtr WorkerGlobalScope::GetOrCreateServiceWorkerRegistration( const ServiceWorkerRegistrationDescriptor& aDescriptor) { AssertIsOnWorkerThread(); RefPtr ref = GetServiceWorkerRegistration(aDescriptor); if (!ref) { ref = ServiceWorkerRegistration::CreateForWorker(mWorkerPrivate, this, aDescriptor); } return ref; } mozilla::dom::StorageManager* WorkerGlobalScope::GetStorageManager() { return RefPtr(Navigator())->Storage(); } void WorkerGlobalScope::StorageAccessPermissionGranted() { // Reset the IndexedDB factory. mIndexedDB = nullptr; // Reset DOM Cache mCacheStorage = nullptr; } bool WorkerGlobalScope::WindowInteractionAllowed() const { AssertIsOnWorkerThread(); return mWindowInteractionsAllowed > 0; } void WorkerGlobalScope::AllowWindowInteraction() { AssertIsOnWorkerThread(); mWindowInteractionsAllowed++; } void WorkerGlobalScope::ConsumeWindowInteraction() { AssertIsOnWorkerThread(); MOZ_ASSERT(mWindowInteractionsAllowed); mWindowInteractionsAllowed--; } NS_IMPL_CYCLE_COLLECTION_INHERITED(DedicatedWorkerGlobalScope, WorkerGlobalScope, mFrameRequestManager) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DedicatedWorkerGlobalScope, WorkerGlobalScope) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(DedicatedWorkerGlobalScope, WorkerGlobalScope) DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope( WorkerPrivate* aWorkerPrivate, UniquePtr aClientSource, const nsString& aName, bool aShouldResistFingerprinting) : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource), aShouldResistFingerprinting), NamedWorkerGlobalScopeMixin(aName) {} bool DedicatedWorkerGlobalScope::WrapGlobalObject( JSContext* aCx, JS::MutableHandle aReflector) { AssertIsOnWorkerThread(); MOZ_ASSERT(!mWorkerPrivate->IsSharedWorker()); JS::RealmOptions options; mWorkerPrivate->CopyJSRealmOptions(options); const bool usesSystemPrincipal = mWorkerPrivate->UsesSystemPrincipal(); // Note that xpc::ShouldDiscardSystemSource() reads a prefs that is cached // on the main thread. This is benignly racey. const bool discardSource = usesSystemPrincipal && xpc::ShouldDiscardSystemSource(); JS::RealmBehaviors& behaviors = options.behaviors(); behaviors.setDiscardSource(discardSource); xpc::SetPrefableRealmOptions(options); return DedicatedWorkerGlobalScope_Binding::Wrap( aCx, this, this, options, nsJSPrincipals::get(mWorkerPrivate->GetPrincipal()), true, aReflector); } void DedicatedWorkerGlobalScope::PostMessage( JSContext* aCx, JS::Handle aMessage, const Sequence& aTransferable, ErrorResult& aRv) { AssertIsOnWorkerThread(); mWorkerPrivate->PostMessageToParent(aCx, aMessage, aTransferable, aRv); } void DedicatedWorkerGlobalScope::PostMessage( JSContext* aCx, JS::Handle aMessage, const StructuredSerializeOptions& aOptions, ErrorResult& aRv) { AssertIsOnWorkerThread(); mWorkerPrivate->PostMessageToParent(aCx, aMessage, aOptions.mTransfer, aRv); } void DedicatedWorkerGlobalScope::Close() { AssertIsOnWorkerThread(); mWorkerPrivate->CloseInternal(); } int32_t DedicatedWorkerGlobalScope::RequestAnimationFrame( FrameRequestCallback& aCallback, ErrorResult& aError) { AssertIsOnWorkerThread(); DebuggerNotificationDispatch(this, DebuggerNotificationType::RequestAnimationFrame); // Ensure the worker is associated with a window. if (mWorkerPrivate->WindowID() == UINT64_MAX) { aError.ThrowNotSupportedError("Worker has no associated owner Window"); return 0; } if (!mVsyncChild) { PBackgroundChild* bgChild = BackgroundChild::GetOrCreateForCurrentThread(); mVsyncChild = MakeRefPtr(); if (!bgChild || !mVsyncChild->Initialize(mWorkerPrivate) || !bgChild->SendPVsyncConstructor(mVsyncChild)) { mVsyncChild->Destroy(); mVsyncChild = nullptr; aError.ThrowNotSupportedError( "Worker failed to register for vsync to drive event loop"); return 0; } } if (!mDocListener) { mDocListener = WorkerDocumentListener::Create(mWorkerPrivate); if (!mDocListener) { aError.ThrowNotSupportedError( "Worker failed to register for document visibility events"); return 0; } } int32_t handle = 0; aError = mFrameRequestManager.Schedule(aCallback, &handle); if (!aError.Failed() && mDocumentVisible) { mVsyncChild->TryObserve(); } return handle; } void DedicatedWorkerGlobalScope::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError) { AssertIsOnWorkerThread(); DebuggerNotificationDispatch(this, DebuggerNotificationType::CancelAnimationFrame); // Ensure the worker is associated with a window. if (mWorkerPrivate->WindowID() == UINT64_MAX) { aError.ThrowNotSupportedError("Worker has no associated owner Window"); return; } mFrameRequestManager.Cancel(aHandle); if (mVsyncChild && mFrameRequestManager.IsEmpty()) { mVsyncChild->TryUnobserve(); } } void DedicatedWorkerGlobalScope::OnDocumentVisible(bool aVisible) { AssertIsOnWorkerThread(); mDocumentVisible = aVisible; // We only change state immediately when we become visible. If we become // hidden, then we wait for the next vsync tick to apply that. if (aVisible && !mFrameRequestManager.IsEmpty()) { mVsyncChild->TryObserve(); } } void DedicatedWorkerGlobalScope::OnVsync(const VsyncEvent& aVsync) { AssertIsOnWorkerThread(); if (mFrameRequestManager.IsEmpty() || !mDocumentVisible) { // If we ever receive a vsync event, and there are still no callbacks to // process, or we remain hidden, we should disable observing them. By // waiting an extra tick, we ensure we minimize extra IPC for content that // does not call requestFrameAnimation directly during the callback, or // that is rapidly toggling between hidden and visible. mVsyncChild->TryUnobserve(); return; } nsTArray callbacks; mFrameRequestManager.Take(callbacks); RefPtr scope(this); CallbackDebuggerNotificationGuard guard( scope, DebuggerNotificationType::RequestAnimationFrameCallback); // This is similar to what we do in nsRefreshDriver::RunFrameRequestCallbacks // and Performance::TimeStampToDOMHighResForRendering in order to have the // same behaviour for requestAnimationFrame on both the main and worker // threads. DOMHighResTimeStamp timeStamp = 0; if (!aVsync.mTime.IsNull()) { timeStamp = mWorkerPrivate->TimeStampToDOMHighRes(aVsync.mTime); // 0 is an inappropriate mixin for this this area; however CSS Animations // needs to have it's Time Reduction Logic refactored, so it's currently // only clamping for RFP mode. RFP mode gives a much lower time precision, // so we accept the security leak here for now. timeStamp = nsRFPService::ReduceTimePrecisionAsMSecsRFPOnly( timeStamp, 0, this->GetRTPCallerType()); } for (auto& callback : callbacks) { if (mFrameRequestManager.IsCanceled(callback.mHandle)) { continue; } // MOZ_KnownLive is OK, because the stack array `callbacks` keeps the // callback alive and the mCallback strong reference can't be mutated by // the call. LogFrameRequestCallback::Run run(callback.mCallback); MOZ_KnownLive(callback.mCallback)->Call(timeStamp); } } SharedWorkerGlobalScope::SharedWorkerGlobalScope( WorkerPrivate* aWorkerPrivate, UniquePtr aClientSource, const nsString& aName, bool aShouldResistFingerprinting) : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource), aShouldResistFingerprinting), NamedWorkerGlobalScopeMixin(aName) {} bool SharedWorkerGlobalScope::WrapGlobalObject( JSContext* aCx, JS::MutableHandle aReflector) { AssertIsOnWorkerThread(); MOZ_ASSERT(mWorkerPrivate->IsSharedWorker()); JS::RealmOptions options; mWorkerPrivate->CopyJSRealmOptions(options); return SharedWorkerGlobalScope_Binding::Wrap( aCx, this, this, options, nsJSPrincipals::get(mWorkerPrivate->GetPrincipal()), true, aReflector); } void SharedWorkerGlobalScope::Close() { AssertIsOnWorkerThread(); mWorkerPrivate->CloseInternal(); } NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope, mClients, mExtensionBrowser, mRegistration) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerGlobalScope) NS_INTERFACE_MAP_END_INHERITING(WorkerGlobalScope) NS_IMPL_ADDREF_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) NS_IMPL_RELEASE_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) ServiceWorkerGlobalScope::ServiceWorkerGlobalScope( WorkerPrivate* aWorkerPrivate, UniquePtr aClientSource, const ServiceWorkerRegistrationDescriptor& aRegistrationDescriptor, bool aShouldResistFingerprinting) : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource), aShouldResistFingerprinting), mScope(NS_ConvertUTF8toUTF16(aRegistrationDescriptor.Scope())) // Eagerly create the registration because we will need to receive // updates about the state of the registration. We can't wait until // first access to start receiving these. , mRegistration( GetOrCreateServiceWorkerRegistration(aRegistrationDescriptor)) {} ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope() = default; bool ServiceWorkerGlobalScope::WrapGlobalObject( JSContext* aCx, JS::MutableHandle aReflector) { AssertIsOnWorkerThread(); MOZ_ASSERT(mWorkerPrivate->IsServiceWorker()); JS::RealmOptions options; mWorkerPrivate->CopyJSRealmOptions(options); return ServiceWorkerGlobalScope_Binding::Wrap( aCx, this, this, options, nsJSPrincipals::get(mWorkerPrivate->GetPrincipal()), true, aReflector); } already_AddRefed ServiceWorkerGlobalScope::GetClients() { if (!mClients) { mClients = new Clients(this); } RefPtr ref = mClients; return ref.forget(); } ServiceWorkerRegistration* ServiceWorkerGlobalScope::Registration() { return mRegistration; } EventHandlerNonNull* ServiceWorkerGlobalScope::GetOnfetch() { AssertIsOnWorkerThread(); return GetEventHandler(nsGkAtoms::onfetch); } namespace { class ReportFetchListenerWarningRunnable final : public Runnable { const nsCString mScope; nsString mSourceSpec; uint32_t mLine; uint32_t mColumn; public: explicit ReportFetchListenerWarningRunnable(const nsString& aScope) : mozilla::Runnable("ReportFetchListenerWarningRunnable"), mScope(NS_ConvertUTF16toUTF8(aScope)) { WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(workerPrivate); JSContext* cx = workerPrivate->GetJSContext(); MOZ_ASSERT(cx); nsJSUtils::GetCallingLocation(cx, mSourceSpec, &mLine, &mColumn); } NS_IMETHOD Run() override { AssertIsOnMainThread(); ServiceWorkerManager::LocalizeAndReportToAllClients( mScope, "ServiceWorkerNoFetchHandler", nsTArray{}, nsIScriptError::warningFlag, mSourceSpec, u""_ns, mLine, mColumn); return NS_OK; } }; } // anonymous namespace void ServiceWorkerGlobalScope::NoteFetchHandlerWasAdded() const { if (mWorkerPrivate->WorkerScriptExecutedSuccessfully()) { RefPtr r = new ReportFetchListenerWarningRunnable(mScope); mWorkerPrivate->DispatchToMainThreadForMessaging(r.forget()); } mWorkerPrivate->SetFetchHandlerWasAdded(); } void ServiceWorkerGlobalScope::SetOnfetch( mozilla::dom::EventHandlerNonNull* aCallback) { AssertIsOnWorkerThread(); if (aCallback) { NoteFetchHandlerWasAdded(); } SetEventHandler(nsGkAtoms::onfetch, aCallback); } void ServiceWorkerGlobalScope::EventListenerAdded(nsAtom* aType) { AssertIsOnWorkerThread(); if (aType == nsGkAtoms::onfetch) { NoteFetchHandlerWasAdded(); } } already_AddRefed ServiceWorkerGlobalScope::SkipWaiting( ErrorResult& aRv) { AssertIsOnWorkerThread(); MOZ_ASSERT(mWorkerPrivate->IsServiceWorker()); RefPtr promise = Promise::Create(this, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } using MozPromiseType = decltype(mWorkerPrivate->SetServiceWorkerSkipWaitingFlag())::element_type; auto holder = MakeRefPtr>(this); mWorkerPrivate->SetServiceWorkerSkipWaitingFlag() ->Then(GetCurrentSerialEventTarget(), __func__, [holder, promise](const MozPromiseType::ResolveOrRejectValue&) { holder->Complete(); promise->MaybeResolveWithUndefined(); }) ->Track(*holder); return promise.forget(); } SafeRefPtr ServiceWorkerGlobalScope::AcquireExtensionBrowser() { if (!mExtensionBrowser) { mExtensionBrowser = MakeSafeRefPtr(this); } return mExtensionBrowser.clonePtr(); } bool WorkerDebuggerGlobalScope::WrapGlobalObject( JSContext* aCx, JS::MutableHandle aReflector) { AssertIsOnWorkerThread(); JS::RealmOptions options; mWorkerPrivate->CopyJSRealmOptions(options); return WorkerDebuggerGlobalScope_Binding::Wrap( aCx, this, this, options, nsJSPrincipals::get(mWorkerPrivate->GetPrincipal()), true, aReflector); } void WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx, JS::MutableHandle aGlobal, ErrorResult& aRv) { WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); if (!scope) { aRv.Throw(NS_ERROR_FAILURE); return; } aGlobal.set(scope->GetWrapper()); } void WorkerDebuggerGlobalScope::CreateSandbox( JSContext* aCx, const nsAString& aName, JS::Handle aPrototype, JS::MutableHandle aResult, ErrorResult& aRv) { AssertIsOnWorkerThread(); aResult.set(nullptr); JS::Rooted protoVal(aCx); protoVal.setObjectOrNull(aPrototype); JS::Rooted sandbox( aCx, SimpleGlobalObject::Create( SimpleGlobalObject::GlobalType::WorkerDebuggerSandbox, protoVal)); if (!sandbox) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } if (!JS_WrapObject(aCx, &sandbox)) { aRv.NoteJSContextException(aCx); return; } aResult.set(sandbox); } void WorkerDebuggerGlobalScope::LoadSubScript( JSContext* aCx, const nsAString& aURL, const Optional>& aSandbox, ErrorResult& aRv) { AssertIsOnWorkerThread(); Maybe ar; if (aSandbox.WasPassed()) { // We only care about worker debugger sandbox objects here, so // CheckedUnwrapStatic is fine. JS::Rooted sandbox(aCx, js::CheckedUnwrapStatic(aSandbox.Value())); if (!sandbox || !IsWorkerDebuggerSandbox(sandbox)) { aRv.Throw(NS_ERROR_INVALID_ARG); return; } ar.emplace(aCx, sandbox); } nsTArray urls; urls.AppendElement(aURL); workerinternals::Load(mWorkerPrivate, nullptr, urls, DebuggerScript, aRv); } void WorkerDebuggerGlobalScope::EnterEventLoop() { // We're on the worker thread here, and WorkerPrivate's refcounting is // non-threadsafe: you can only do it on the parent thread. What that // means in practice is that we're relying on it being kept alive while // we run. Hopefully. MOZ_KnownLive(mWorkerPrivate)->EnterDebuggerEventLoop(); } void WorkerDebuggerGlobalScope::LeaveEventLoop() { mWorkerPrivate->LeaveDebuggerEventLoop(); } void WorkerDebuggerGlobalScope::PostMessage(const nsAString& aMessage) { mWorkerPrivate->PostMessageToDebugger(aMessage); } void WorkerDebuggerGlobalScope::SetImmediate(Function& aHandler, ErrorResult& aRv) { mWorkerPrivate->SetDebuggerImmediate(aHandler, aRv); } void WorkerDebuggerGlobalScope::ReportError(JSContext* aCx, const nsAString& aMessage) { JS::AutoFilename chars; uint32_t lineno = 0; JS::DescribeScriptedCaller(aCx, &chars, &lineno); nsString filename(NS_ConvertUTF8toUTF16(chars.get())); mWorkerPrivate->ReportErrorToDebugger(filename, lineno, aMessage); } void WorkerDebuggerGlobalScope::RetrieveConsoleEvents( JSContext* aCx, nsTArray& aEvents, ErrorResult& aRv) { WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); if (!scope) { aRv.Throw(NS_ERROR_FAILURE); return; } RefPtr console = scope->GetConsole(aRv); if (NS_WARN_IF(aRv.Failed())) { return; } console->RetrieveConsoleEvents(aCx, aEvents, aRv); } void WorkerDebuggerGlobalScope::ClearConsoleEvents(JSContext* aCx, ErrorResult& aRv) { WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); if (!scope) { aRv.Throw(NS_ERROR_FAILURE); return; } RefPtr console = scope->GetConsoleIfExists(); if (console) { console->ClearStorage(); } } void WorkerDebuggerGlobalScope::SetConsoleEventHandler(JSContext* aCx, AnyCallback* aHandler, ErrorResult& aRv) { WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); if (!scope) { aRv.Throw(NS_ERROR_FAILURE); return; } RefPtr console = scope->GetConsole(aRv); if (NS_WARN_IF(aRv.Failed())) { return; } console->SetConsoleEventHandler(aHandler); } void WorkerDebuggerGlobalScope::Dump(JSContext* aCx, const Optional& aString) const { WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); if (scope) { scope->Dump(aString); } } bool IsWorkerGlobal(JSObject* object) { return IS_INSTANCE_OF(WorkerGlobalScope, object); } bool IsWorkerDebuggerGlobal(JSObject* object) { return IS_INSTANCE_OF(WorkerDebuggerGlobalScope, object); } bool IsWorkerDebuggerSandbox(JSObject* object) { return SimpleGlobalObject::SimpleGlobalType(object) == SimpleGlobalObject::GlobalType::WorkerDebuggerSandbox; } } // namespace mozilla::dom