forked from mirrors/gecko-dev
Backed out changeset 09168636f92e (bug 1890782) Backed out changeset 45c9c902f35f (bug 1889444) Backed out changeset 7cd441010547 (bug 1889444) Backed out changeset 692d3fb54e2c (bug 1890546) Backed out changeset 4c476414499a (bug 1843308) Backed out changeset fc70ef415bfe (bug 1888504) Backed out changeset e400fe8e13ac (bug 1888500) Backed out changeset 336738f93085 (bug 1888500) Backed out changeset 40fdfaf3cc32 (bug 1848406) Backed out changeset 6a3ec1f62811 (bug 1848406) Backed out changeset 5ea32ea95f62 (bug 1848406) Backed out changeset df982722bc0a (bug 1848406) Backed out changeset 6808ec37fa93 (bug 1848406) Backed out changeset 36b8e78cc27e (bug 1848406) Backed out changeset d63358e762de (bug 1848406)
1416 lines
46 KiB
C++
1416 lines
46 KiB
C++
/* -*- 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(mTrustedTypePolicyFactory)
|
|
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(mTrustedTypePolicyFactory)
|
|
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 RequestOrUTF8String& 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;
|
|
}
|
|
|
|
TrustedTypePolicyFactory* WorkerGlobalScope::TrustedTypes() {
|
|
if (!mTrustedTypePolicyFactory) {
|
|
mTrustedTypePolicyFactory = MakeRefPtr<TrustedTypePolicyFactory>(this);
|
|
}
|
|
|
|
return mTrustedTypePolicyFactory;
|
|
}
|
|
|
|
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
|