diff options
Diffstat (limited to 'dom/workers/WorkerScope.cpp')
-rw-r--r-- | dom/workers/WorkerScope.cpp | 1406 |
1 files changed, 1406 insertions, 0 deletions
diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp new file mode 100644 index 0000000000..159829f4a8 --- /dev/null +++ b/dom/workers/WorkerScope.cpp @@ -0,0 +1,1406 @@ +/* -*- 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/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 { + +static mozilla::LazyLogModule sWorkerScopeLog("WorkerScope"); + +#ifdef LOG +# undef LOG +#endif +#define LOG(args) MOZ_LOG(sWorkerScopeLog, LogLevel::Debug, args); + +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) + : mWorkerPrivate(aWorkerPrivate), + mClientSource(std::move(aClientSource)), + mSerialEventTarget(aWorkerPrivate->HybridEventTarget()) { + LOG(("WorkerGlobalScopeBase::WorkerGlobalScopeBase [%p]", this)); + 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 mWorkerPrivate->ShouldResistFingerprinting(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( + already_AddRefed<nsIRunnable>&& aRunnable) const { + return SerialEventTarget()->Dispatch(std::move(aRunnable), + NS_DISPATCH_NORMAL); +} + +nsISerialEventTarget* WorkerGlobalScopeBase::SerialEventTarget() 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() { + LOG(("WorkerGlobalScope::NoteTerminating [%p]", this)); + if (IsDying()) { + return; + } + + StartDying(); +} + +void WorkerGlobalScope::NoteShuttingDown() { + MOZ_ASSERT(IsDying()); + LOG(("WorkerGlobalScope::NoteShuttingDown [%p]", this)); + + if (mNavigator) { + mNavigator->Invalidate(); + mNavigator = nullptr; + } +} + +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::GetWebExposedOriginSerialization( + 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(this); + } + + 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(); +} + +// https://html.spec.whatwg.org/multipage/web-messaging.html#eligible-for-messaging +// * a WorkerGlobalScope object whose closing flag is false and whose worker +// is not a suspendable worker. +bool WorkerGlobalScope::IsEligibleForMessaging() { + return mIsEligibleForMessaging; +} +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) + : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource)), + NamedWorkerGlobalScopeMixin(aName) {} + +bool DedicatedWorkerGlobalScope::WrapGlobalObject( + JSContext* aCx, JS::MutableHandle<JSObject*> aReflector) { + AssertIsOnWorkerThread(); + MOZ_ASSERT(!mWorkerPrivate->IsSharedWorker()); + + JS::RealmOptions options; + mWorkerPrivate->CopyJSRealmOptions(options); + + xpc::SetPrefableRealmOptions(options); + + return DedicatedWorkerGlobalScope_Binding::Wrap( + aCx, this, this, options, + nsJSPrincipals::get(mWorkerPrivate->GetPrincipal()), 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) + : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource)), + 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()), 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) + : WorkerGlobalScope(std::move(aWorkerPrivate), std::move(aClientSource)), + 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()), 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()), 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 |