summaryrefslogtreecommitdiffstats
path: root/dom/workers/WorkerScope.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/WorkerScope.cpp')
-rw-r--r--dom/workers/WorkerScope.cpp1414
1 files changed, 1414 insertions, 0 deletions
diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp
new file mode 100644
index 0000000000..8a592e4065
--- /dev/null
+++ b/dom/workers/WorkerScope.cpp
@@ -0,0 +1,1414 @@
+/* -*- 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 <stdio.h>
+#include <new>
+#include <utility>
+#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 <android/log.h>
+#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<JS::Value> unused(cx);
+ JS::SourceText<char16_t> 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<ClientSource> 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<nsIGlobalObject*>(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<ClientInfo> WorkerGlobalScopeBase::GetClientInfo() const {
+ return Some(mClientSource->Info());
+}
+
+Maybe<ServiceWorkerDescriptor> WorkerGlobalScopeBase::GetController() const {
+ return mClientSource->GetController();
+}
+
+mozilla::Result<mozilla::ipc::PrincipalInfo, nsresult>
+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<nsIRunnable>&& 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<JS::Value> 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<Console> WorkerGlobalScopeBase::GetConsole(ErrorResult& aRv) {
+ AssertIsOnWorkerThread();
+
+ if (!mConsole) {
+ mConsole = Console::Create(mWorkerPrivate->GetJSContext(), nullptr, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ }
+
+ RefPtr<Console> 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<PerformanceWorker> pw =
+ static_cast<PerformanceWorker*>(mPerformance.get());
+ MOZ_ASSERT(pw);
+ pw->NoteShuttingDown();
+ }
+}
+
+Crypto* WorkerGlobalScope::GetCrypto(ErrorResult& aError) {
+ AssertIsOnWorkerThread();
+
+ if (!mCrypto) {
+ mCrypto = new Crypto(this);
+ }
+
+ return mCrypto;
+}
+
+already_AddRefed<CacheStorage> WorkerGlobalScope::GetCaches(ErrorResult& aRv) {
+ if (!mCacheStorage) {
+ mCacheStorage = CacheStorage::CreateOnWorker(cache::DEFAULT_NAMESPACE, this,
+ mWorkerPrivate, aRv);
+ }
+
+ RefPtr<CacheStorage> 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<WorkerLocation> WorkerGlobalScope::Location() {
+ AssertIsOnWorkerThread();
+
+ if (!mLocation) {
+ mLocation = WorkerLocation::Create(mWorkerPrivate->GetLocationInfo());
+ MOZ_ASSERT(mLocation);
+ }
+
+ RefPtr<WorkerLocation> location = mLocation;
+ return location.forget();
+}
+
+already_AddRefed<WorkerNavigator> WorkerGlobalScope::Navigator() {
+ AssertIsOnWorkerThread();
+
+ if (!mNavigator) {
+ mNavigator = WorkerNavigator::Create(mWorkerPrivate->OnLine());
+ MOZ_ASSERT(mNavigator);
+ }
+
+ RefPtr<WorkerNavigator> navigator = mNavigator;
+ return navigator.forget();
+}
+
+already_AddRefed<WorkerNavigator> WorkerGlobalScope::GetExistingNavigator()
+ const {
+ AssertIsOnWorkerThread();
+
+ RefPtr<WorkerNavigator> 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<nsString>& aScriptURLs,
+ ErrorResult& aRv) {
+ AssertIsOnWorkerThread();
+
+ UniquePtr<SerializedStackHolder> 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<JS::Value>& 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<JS::Value>& /* 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<JS::Value>& 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<JS::Value>& /* 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<JS::Value>& aArguments, bool aIsInterval, ErrorResult& aRv) {
+ AssertIsOnWorkerThread();
+
+ DebuggerNotificationDispatch(
+ this, aIsInterval ? DebuggerNotificationType::SetInterval
+ : DebuggerNotificationType::SetTimeout);
+
+ nsTArray<JS::Heap<JS::Value>> args;
+ if (!args.AppendElements(aArguments, fallible)) {
+ aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return 0;
+ }
+
+ RefPtr<TimeoutHandler> 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<TimeoutHandler> 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<nsAString>& 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<JSObject*> aFunctions, ErrorResult& aRv) {
+ JSObject* obj = js::GetTestingFunctions(aCx);
+ if (!obj) {
+ aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return;
+ }
+
+ aFunctions.set(obj);
+}
+
+already_AddRefed<Promise> WorkerGlobalScope::Fetch(
+ const RequestOrUSVString& aInput, const RequestInit& aInit,
+ CallerType aCallerType, ErrorResult& aRv) {
+ return FetchRequest(this, aInput, aInit, aCallerType, aRv);
+}
+
+already_AddRefed<IDBFactory> WorkerGlobalScope::GetIndexedDB(
+ JSContext* aCx, ErrorResult& aErrorResult) {
+ AssertIsOnWorkerThread();
+
+ RefPtr<IDBFactory> 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<Promise> WorkerGlobalScope::CreateImageBitmap(
+ const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions,
+ ErrorResult& aRv) {
+ return ImageBitmap::Create(this, aImage, Nothing(), aOptions, aRv);
+}
+
+already_AddRefed<Promise> 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<JS::Value> aValue,
+ const StructuredSerializeOptions& aOptions,
+ JS::MutableHandle<JS::Value> 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<EventCallbackDebuggerNotificationType>
+WorkerGlobalScope::GetDebuggerNotificationType() const {
+ return Some(EventCallbackDebuggerNotificationType::Global);
+}
+
+RefPtr<ServiceWorkerRegistration>
+WorkerGlobalScope::GetServiceWorkerRegistration(
+ const ServiceWorkerRegistrationDescriptor& aDescriptor) const {
+ AssertIsOnWorkerThread();
+ RefPtr<ServiceWorkerRegistration> ref;
+ ForEachGlobalTeardownObserver(
+ [&](GlobalTeardownObserver* aObserver, bool* aDoneOut) {
+ RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aObserver);
+ if (!swr || !swr->MatchesDescriptor(aDescriptor)) {
+ return;
+ }
+
+ ref = std::move(swr);
+ *aDoneOut = true;
+ });
+ return ref;
+}
+
+RefPtr<ServiceWorkerRegistration>
+WorkerGlobalScope::GetOrCreateServiceWorkerRegistration(
+ const ServiceWorkerRegistrationDescriptor& aDescriptor) {
+ AssertIsOnWorkerThread();
+ RefPtr<ServiceWorkerRegistration> 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<ClientSource> aClientSource,
+ const nsString& aName, bool aShouldResistFingerprinting)
+ : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource),
+ aShouldResistFingerprinting),
+ NamedWorkerGlobalScopeMixin(aName) {}
+
+bool DedicatedWorkerGlobalScope::WrapGlobalObject(
+ JSContext* aCx, JS::MutableHandle<JSObject*> 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<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable, ErrorResult& aRv) {
+ AssertIsOnWorkerThread();
+ mWorkerPrivate->PostMessageToParent(aCx, aMessage, aTransferable, aRv);
+}
+
+void DedicatedWorkerGlobalScope::PostMessage(
+ JSContext* aCx, JS::Handle<JS::Value> 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<VsyncWorkerChild>();
+
+ 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<FrameRequest> callbacks;
+ mFrameRequestManager.Take(callbacks);
+
+ RefPtr<DedicatedWorkerGlobalScope> 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<ClientSource> aClientSource,
+ const nsString& aName, bool aShouldResistFingerprinting)
+ : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource),
+ aShouldResistFingerprinting),
+ NamedWorkerGlobalScopeMixin(aName) {}
+
+bool SharedWorkerGlobalScope::WrapGlobalObject(
+ JSContext* aCx, JS::MutableHandle<JSObject*> 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<ClientSource> 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<JSObject*> 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<Clients> ServiceWorkerGlobalScope::GetClients() {
+ if (!mClients) {
+ mClients = new Clients(this);
+ }
+
+ RefPtr<Clients> 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<nsString>{},
+ nsIScriptError::warningFlag, mSourceSpec, u""_ns, mLine, mColumn);
+
+ return NS_OK;
+ }
+};
+
+} // anonymous namespace
+
+void ServiceWorkerGlobalScope::NoteFetchHandlerWasAdded() const {
+ if (mWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
+ RefPtr<Runnable> 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<Promise> ServiceWorkerGlobalScope::SkipWaiting(
+ ErrorResult& aRv) {
+ AssertIsOnWorkerThread();
+ MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
+
+ RefPtr<Promise> promise = Promise::Create(this, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ using MozPromiseType =
+ decltype(mWorkerPrivate->SetServiceWorkerSkipWaitingFlag())::element_type;
+ auto holder = MakeRefPtr<DOMMozPromiseRequestHolder<MozPromiseType>>(this);
+
+ mWorkerPrivate->SetServiceWorkerSkipWaitingFlag()
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [holder, promise](const MozPromiseType::ResolveOrRejectValue&) {
+ holder->Complete();
+ promise->MaybeResolveWithUndefined();
+ })
+ ->Track(*holder);
+
+ return promise.forget();
+}
+
+SafeRefPtr<extensions::ExtensionBrowser>
+ServiceWorkerGlobalScope::AcquireExtensionBrowser() {
+ if (!mExtensionBrowser) {
+ mExtensionBrowser = MakeSafeRefPtr<extensions::ExtensionBrowser>(this);
+ }
+
+ return mExtensionBrowser.clonePtr();
+}
+
+bool WorkerDebuggerGlobalScope::WrapGlobalObject(
+ JSContext* aCx, JS::MutableHandle<JSObject*> 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<JSObject*> 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<JSObject*> aPrototype,
+ JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv) {
+ AssertIsOnWorkerThread();
+
+ aResult.set(nullptr);
+
+ JS::Rooted<JS::Value> protoVal(aCx);
+ protoVal.setObjectOrNull(aPrototype);
+ JS::Rooted<JSObject*> 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<JS::Handle<JSObject*>>& aSandbox, ErrorResult& aRv) {
+ AssertIsOnWorkerThread();
+
+ Maybe<JSAutoRealm> ar;
+ if (aSandbox.WasPassed()) {
+ // We only care about worker debugger sandbox objects here, so
+ // CheckedUnwrapStatic is fine.
+ JS::Rooted<JSObject*> sandbox(aCx,
+ js::CheckedUnwrapStatic(aSandbox.Value()));
+ if (!sandbox || !IsWorkerDebuggerSandbox(sandbox)) {
+ aRv.Throw(NS_ERROR_INVALID_ARG);
+ return;
+ }
+
+ ar.emplace(aCx, sandbox);
+ }
+
+ nsTArray<nsString> 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<JS::Value>& aEvents, ErrorResult& aRv) {
+ WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx);
+ if (!scope) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ RefPtr<Console> 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> 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> console = scope->GetConsole(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ console->SetConsoleEventHandler(aHandler);
+}
+
+void WorkerDebuggerGlobalScope::Dump(JSContext* aCx,
+ const Optional<nsAString>& 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