/* -*- 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 "WorkerScope.h" #include "jsapi.h" #include "mozilla/EventListenerManager.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/Console.h" #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h" #include "mozilla/dom/Fetch.h" #include "mozilla/dom/FunctionBinding.h" #include "mozilla/dom/ImageBitmap.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseWorkerProxy.h" #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h" #include "mozilla/dom/SharedWorkerGlobalScopeBinding.h" #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h" #include "mozilla/dom/WorkerGlobalScopeBinding.h" #include "mozilla/dom/cache/CacheStorage.h" #include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/Services.h" #include "nsServiceManagerUtils.h" #include "nsIDocument.h" #include "nsIServiceWorkerManager.h" #ifdef ANDROID #include #endif #include "Location.h" #include "Navigator.h" #include "Principal.h" #include "RuntimeService.h" #include "ScriptLoader.h" #include "WorkerPrivate.h" #include "WorkerRunnable.h" #include "Performance.h" #include "ServiceWorkerClients.h" #include "ServiceWorkerManager.h" #include "ServiceWorkerRegistration.h" #ifdef XP_WIN #undef PostMessage #endif using namespace mozilla; using namespace mozilla::dom; USING_WORKERS_NAMESPACE using mozilla::dom::cache::CacheStorage; using mozilla::dom::indexedDB::IDBFactory; using mozilla::ipc::PrincipalInfo; BEGIN_WORKERS_NAMESPACE WorkerGlobalScope::WorkerGlobalScope(WorkerPrivate* aWorkerPrivate) : mWindowInteractionsAllowed(0) , mWorkerPrivate(aWorkerPrivate) { mWorkerPrivate->AssertIsOnWorkerThread(); } WorkerGlobalScope::~WorkerGlobalScope() { mWorkerPrivate->AssertIsOnWorkerThread(); } NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerGlobalScope) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScope, DOMEventTargetHelper) tmp->mWorkerPrivate->AssertIsOnWorkerThread(); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage) tmp->TraverseHostObjectURIs(cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScope, DOMEventTargetHelper) tmp->mWorkerPrivate->AssertIsOnWorkerThread(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage) tmp->UnlinkHostObjectURIs(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerGlobalScope, DOMEventTargetHelper) tmp->mWorkerPrivate->AssertIsOnWorkerThread(); tmp->mWorkerPrivate->TraceTimeouts(aCallbacks, aClosure); NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_ADDREF_INHERITED(WorkerGlobalScope, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(WorkerGlobalScope, DOMEventTargetHelper) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerGlobalScope) NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) JSObject* WorkerGlobalScope::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { MOZ_CRASH("We should never get here!"); } Console* WorkerGlobalScope::GetConsole() { mWorkerPrivate->AssertIsOnWorkerThread(); if (!mConsole) { mConsole = new Console(nullptr); } return mConsole; } already_AddRefed WorkerGlobalScope::GetCaches(ErrorResult& aRv) { if (!mCacheStorage) { MOZ_ASSERT(mWorkerPrivate); mCacheStorage = CacheStorage::CreateOnWorker(cache::DEFAULT_NAMESPACE, this, mWorkerPrivate, aRv); } nsRefPtr ref = mCacheStorage; return ref.forget(); } already_AddRefed WorkerGlobalScope::Location() { mWorkerPrivate->AssertIsOnWorkerThread(); if (!mLocation) { WorkerPrivate::LocationInfo& info = mWorkerPrivate->GetLocationInfo(); mLocation = WorkerLocation::Create(info); MOZ_ASSERT(mLocation); } nsRefPtr location = mLocation; return location.forget(); } already_AddRefed WorkerGlobalScope::Navigator() { mWorkerPrivate->AssertIsOnWorkerThread(); if (!mNavigator) { mNavigator = WorkerNavigator::Create(mWorkerPrivate->OnLine()); MOZ_ASSERT(mNavigator); } nsRefPtr navigator = mNavigator; return navigator.forget(); } already_AddRefed WorkerGlobalScope::GetExistingNavigator() const { mWorkerPrivate->AssertIsOnWorkerThread(); nsRefPtr navigator = mNavigator; return navigator.forget(); } void WorkerGlobalScope::Close(JSContext* aCx, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); if (mWorkerPrivate->IsServiceWorker()) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); } else { mWorkerPrivate->CloseInternal(aCx); } } OnErrorEventHandlerNonNull* WorkerGlobalScope::GetOnerror() { mWorkerPrivate->AssertIsOnWorkerThread(); EventListenerManager* elm = GetExistingListenerManager(); return elm ? elm->GetOnErrorEventHandler() : nullptr; } void WorkerGlobalScope::SetOnerror(OnErrorEventHandlerNonNull* aHandler) { mWorkerPrivate->AssertIsOnWorkerThread(); EventListenerManager* elm = GetOrCreateListenerManager(); if (elm) { elm->SetEventHandler(aHandler); } } void WorkerGlobalScope::ImportScripts(JSContext* aCx, const Sequence& aScriptURLs, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); scriptloader::Load(aCx, mWorkerPrivate, aScriptURLs, WorkerScript, aRv); } int32_t WorkerGlobalScope::SetTimeout(JSContext* aCx, Function& aHandler, const int32_t aTimeout, const Sequence& aArguments, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); return mWorkerPrivate->SetTimeout(aCx, &aHandler, EmptyString(), aTimeout, aArguments, false, aRv); } int32_t WorkerGlobalScope::SetTimeout(JSContext* /* unused */, const nsAString& aHandler, const int32_t aTimeout, const Sequence& /* unused */, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); Sequence dummy; return mWorkerPrivate->SetTimeout(GetCurrentThreadJSContext(), nullptr, aHandler, aTimeout, dummy, false, aRv); } void WorkerGlobalScope::ClearTimeout(int32_t aHandle, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerPrivate->ClearTimeout(aHandle); } int32_t WorkerGlobalScope::SetInterval(JSContext* aCx, Function& aHandler, const Optional& aTimeout, const Sequence& aArguments, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); bool isInterval = aTimeout.WasPassed(); int32_t timeout = aTimeout.WasPassed() ? aTimeout.Value() : 0; return mWorkerPrivate->SetTimeout(aCx, &aHandler, EmptyString(), timeout, aArguments, isInterval, aRv); } int32_t WorkerGlobalScope::SetInterval(JSContext* /* unused */, const nsAString& aHandler, const Optional& aTimeout, const Sequence& /* unused */, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); Sequence dummy; bool isInterval = aTimeout.WasPassed(); int32_t timeout = aTimeout.WasPassed() ? aTimeout.Value() : 0; return mWorkerPrivate->SetTimeout(GetCurrentThreadJSContext(), nullptr, aHandler, timeout, dummy, isInterval, aRv); } void WorkerGlobalScope::ClearInterval(int32_t aHandle, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerPrivate->ClearTimeout(aHandle); } void WorkerGlobalScope::Atob(const nsAString& aAtob, nsAString& aOutput, ErrorResult& aRv) const { mWorkerPrivate->AssertIsOnWorkerThread(); aRv = nsContentUtils::Atob(aAtob, aOutput); } void WorkerGlobalScope::Btoa(const nsAString& aBtoa, nsAString& aOutput, ErrorResult& aRv) const { mWorkerPrivate->AssertIsOnWorkerThread(); aRv = nsContentUtils::Btoa(aBtoa, aOutput); } void WorkerGlobalScope::Dump(const Optional& aString) const { mWorkerPrivate->AssertIsOnWorkerThread(); if (!aString.WasPassed()) { return; } if (!mWorkerPrivate->DumpEnabled()) { return; } NS_ConvertUTF16toUTF8 str(aString.Value()); #ifdef ANDROID __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get()); #endif fputs(str.get(), stdout); fflush(stdout); } Performance* WorkerGlobalScope::GetPerformance() { mWorkerPrivate->AssertIsOnWorkerThread(); if (!mPerformance) { mPerformance = new Performance(mWorkerPrivate); } return mPerformance; } already_AddRefed WorkerGlobalScope::Fetch(const RequestOrUSVString& aInput, const RequestInit& aInit, ErrorResult& aRv) { return FetchRequest(this, aInput, aInit, aRv); } already_AddRefed WorkerGlobalScope::GetIndexedDB(ErrorResult& aErrorResult) { mWorkerPrivate->AssertIsOnWorkerThread(); nsRefPtr indexedDB = mIndexedDB; if (!indexedDB) { if (!mWorkerPrivate->IsIndexedDBAllowed()) { NS_WARNING("IndexedDB is not allowed in this worker!"); return nullptr; } JSContext* cx = mWorkerPrivate->GetJSContext(); MOZ_ASSERT(cx); JS::Rooted owningObject(cx, GetGlobalJSObject()); MOZ_ASSERT(owningObject); const PrincipalInfo& principalInfo = mWorkerPrivate->GetPrincipalInfo(); nsresult rv = IDBFactory::CreateForWorker(cx, owningObject, principalInfo, mWorkerPrivate->WindowID(), getter_AddRefs(indexedDB)); if (NS_WARN_IF(NS_FAILED(rv))) { aErrorResult = rv; return nullptr; } mIndexedDB = indexedDB; } return indexedDB.forget(); } already_AddRefed WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage, ErrorResult& aRv) { return ImageBitmap::Create(this, aImage, Nothing(), aRv); } already_AddRefed WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh, ErrorResult& aRv) { return ImageBitmap::Create(this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aRv); } DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate) : WorkerGlobalScope(aWorkerPrivate) { } bool DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx, JS::MutableHandle aReflector) { mWorkerPrivate->AssertIsOnWorkerThread(); MOZ_ASSERT(!mWorkerPrivate->IsSharedWorker()); JS::CompartmentOptions options; mWorkerPrivate->CopyJSCompartmentOptions(options); const bool usesSystemPrincipal = mWorkerPrivate->UsesSystemPrincipal(); // Note that xpc::ShouldDiscardSystemSource() and // xpc::ExtraWarningsForSystemJS() read prefs that are cached on the main // thread. This is benignly racey. const bool discardSource = (usesSystemPrincipal || mWorkerPrivate->IsInPrivilegedApp()) && xpc::ShouldDiscardSystemSource(); const bool extraWarnings = usesSystemPrincipal && xpc::ExtraWarningsForSystemJS(); options.setDiscardSource(discardSource) .extraWarningsOverride().set(extraWarnings); return DedicatedWorkerGlobalScopeBinding_workers::Wrap(aCx, this, this, options, GetWorkerPrincipal(), true, aReflector); } void DedicatedWorkerGlobalScope::PostMessage(JSContext* aCx, JS::Handle aMessage, const Optional>& aTransferable, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerPrivate->PostMessageToParent(aCx, aMessage, aTransferable, aRv); } SharedWorkerGlobalScope::SharedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsCString& aName) : WorkerGlobalScope(aWorkerPrivate), mName(aName) { } bool SharedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx, JS::MutableHandle aReflector) { mWorkerPrivate->AssertIsOnWorkerThread(); MOZ_ASSERT(mWorkerPrivate->IsSharedWorker()); JS::CompartmentOptions options; mWorkerPrivate->CopyJSCompartmentOptions(options); return SharedWorkerGlobalScopeBinding_workers::Wrap(aCx, this, this, options, GetWorkerPrincipal(), true, aReflector); } NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope, mClients, mRegistration) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerGlobalScope) NS_INTERFACE_MAP_END_INHERITING(WorkerGlobalScope) NS_IMPL_ADDREF_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) NS_IMPL_RELEASE_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope) : WorkerGlobalScope(aWorkerPrivate), mScope(NS_ConvertUTF8toUTF16(aScope)) { } ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope() { } bool ServiceWorkerGlobalScope::WrapGlobalObject(JSContext* aCx, JS::MutableHandle aReflector) { mWorkerPrivate->AssertIsOnWorkerThread(); MOZ_ASSERT(mWorkerPrivate->IsServiceWorker()); JS::CompartmentOptions options; mWorkerPrivate->CopyJSCompartmentOptions(options); return ServiceWorkerGlobalScopeBinding_workers::Wrap(aCx, this, this, options, GetWorkerPrincipal(), true, aReflector); } ServiceWorkerClients* ServiceWorkerGlobalScope::Clients() { if (!mClients) { mClients = new ServiceWorkerClients(this); } return mClients; } ServiceWorkerRegistrationWorkerThread* ServiceWorkerGlobalScope::Registration() { if (!mRegistration) { mRegistration = new ServiceWorkerRegistrationWorkerThread(mWorkerPrivate, mScope); } return mRegistration; } namespace { class SkipWaitingResultRunnable final : public WorkerRunnable { nsRefPtr mPromiseProxy; public: SkipWaitingResultRunnable(WorkerPrivate* aWorkerPrivate, PromiseWorkerProxy* aPromiseProxy) : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) , mPromiseProxy(aPromiseProxy) { AssertIsOnMainThread(); } virtual bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); Promise* promise = mPromiseProxy->GetWorkerPromise(); MOZ_ASSERT(promise); promise->MaybeResolve(JS::UndefinedHandleValue); // Release the reference on the worker thread. mPromiseProxy->CleanUp(aCx); return true; } }; class WorkerScopeSkipWaitingRunnable final : public nsRunnable { nsRefPtr mPromiseProxy; nsCString mScope; public: WorkerScopeSkipWaitingRunnable(PromiseWorkerProxy* aPromiseProxy, const nsCString& aScope) : mPromiseProxy(aPromiseProxy) , mScope(aScope) { MOZ_ASSERT(aPromiseProxy); } NS_IMETHODIMP Run() override { AssertIsOnMainThread(); nsRefPtr swm = ServiceWorkerManager::GetInstance(); MOZ_ASSERT(swm); MutexAutoLock lock(mPromiseProxy->GetCleanUpLock()); if (mPromiseProxy->IsClean()) { return NS_OK; } WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); MOZ_ASSERT(workerPrivate); swm->SetSkipWaitingFlag(workerPrivate->GetPrincipal(), mScope, workerPrivate->ServiceWorkerID()); nsRefPtr runnable = new SkipWaitingResultRunnable(workerPrivate, mPromiseProxy); AutoJSAPI jsapi; jsapi.Init(); JSContext* cx = jsapi.cx(); if (runnable->Dispatch(cx)) { return NS_OK; } // Dispatch to worker thread failed because the worker is shutting down. // Use a control runnable to release the runnable on the worker thread. nsRefPtr releaseRunnable = new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy); if (!releaseRunnable->Dispatch(cx)) { NS_RUNTIMEABORT("Failed to dispatch Claim control runnable."); } return NS_OK; } }; } // namespace already_AddRefed ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); MOZ_ASSERT(mWorkerPrivate->IsServiceWorker()); nsRefPtr promise = Promise::Create(this, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } nsRefPtr promiseProxy = PromiseWorkerProxy::Create(mWorkerPrivate, promise); if (!promiseProxy->GetWorkerPromise()) { // Don't dispatch if adding the worker feature failed. promise->MaybeResolve(JS::UndefinedHandleValue); return promise.forget(); } nsRefPtr runnable = new WorkerScopeSkipWaitingRunnable(promiseProxy, NS_ConvertUTF16toUTF8(mScope)); aRv = NS_DispatchToMainThread(runnable); if (NS_WARN_IF(aRv.Failed())) { promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); } return promise.forget(); } // static bool ServiceWorkerGlobalScope::InterceptionEnabled(JSContext* aCx, JSObject* aObj) { WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); return worker->InterceptionEnabled(); } WorkerDebuggerGlobalScope::WorkerDebuggerGlobalScope( WorkerPrivate* aWorkerPrivate) : mWorkerPrivate(aWorkerPrivate) { mWorkerPrivate->AssertIsOnWorkerThread(); } WorkerDebuggerGlobalScope::~WorkerDebuggerGlobalScope() { mWorkerPrivate->AssertIsOnWorkerThread(); } NS_IMPL_ADDREF_INHERITED(WorkerDebuggerGlobalScope, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(WorkerDebuggerGlobalScope, DOMEventTargetHelper) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerDebuggerGlobalScope) NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) bool WorkerDebuggerGlobalScope::WrapGlobalObject(JSContext* aCx, JS::MutableHandle aReflector) { mWorkerPrivate->AssertIsOnWorkerThread(); JS::CompartmentOptions options; mWorkerPrivate->CopyJSCompartmentOptions(options); return WorkerDebuggerGlobalScopeBinding::Wrap(aCx, this, this, options, GetWorkerPrincipal(), true, aReflector); } void WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx, JS::MutableHandle aGlobal) { aGlobal.set(mWorkerPrivate->GetOrCreateGlobalScope(aCx)->GetWrapper()); } class WorkerDebuggerSandboxPrivate : public nsIGlobalObject, public nsWrapperCache { public: explicit WorkerDebuggerSandboxPrivate(JSObject *global) { SetWrapper(global); } NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WorkerDebuggerSandboxPrivate, nsIGlobalObject) virtual JSObject *GetGlobalJSObject() override { return GetWrapper(); } virtual JSObject* WrapObject(JSContext* cx, JS::Handle aGivenProto) override { MOZ_CRASH("WorkerDebuggerSandboxPrivate doesn't use DOM bindings!"); } private: virtual ~WorkerDebuggerSandboxPrivate() { ClearWrapper(); } }; NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerDebuggerSandboxPrivate) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkerDebuggerSandboxPrivate) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER tmp->UnlinkHostObjectURIs(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkerDebuggerSandboxPrivate) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS tmp->TraverseHostObjectURIs(cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(WorkerDebuggerSandboxPrivate) NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerDebuggerSandboxPrivate) NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerDebuggerSandboxPrivate) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerDebuggerSandboxPrivate) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_INTERFACE_MAP_END static bool workerdebuggersandbox_enumerate(JSContext *cx, JS::Handle obj) { return JS_EnumerateStandardClasses(cx, obj); } static bool workerdebuggersandbox_resolve(JSContext *cx, JS::Handle obj, JS::Handle id, bool *resolvedp) { return JS_ResolveStandardClass(cx, obj, id, resolvedp); } static bool workerdebuggersandbox_convert(JSContext *cx, JS::Handle obj, JSType type, JS::MutableHandle vp) { if (type == JSTYPE_OBJECT) { vp.setObject(*obj); return true; } return JS::OrdinaryToPrimitive(cx, obj, type, vp); } static void workerdebuggersandbox_finalize(js::FreeOp *fop, JSObject *obj) { nsIGlobalObject *globalObject = static_cast(JS_GetPrivate(obj)); NS_RELEASE(globalObject); } static void workerdebuggersandbox_moved(JSObject *obj, const JSObject *old) { } const js::Class workerdebuggersandbox_class = { "workerdebuggersandbox", JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS, nullptr, nullptr, nullptr, nullptr, workerdebuggersandbox_enumerate, workerdebuggersandbox_resolve, nullptr, /* mayResolve */ workerdebuggersandbox_convert, workerdebuggersandbox_finalize, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook, JS_NULL_CLASS_SPEC, { nullptr, nullptr, false, nullptr, workerdebuggersandbox_moved }, JS_NULL_OBJECT_OPS }; void WorkerDebuggerGlobalScope::CreateSandbox(JSContext* aCx, const nsAString& aName, JS::Handle aPrototype, JS::MutableHandle aResult) { mWorkerPrivate->AssertIsOnWorkerThread(); JS::CompartmentOptions options; options.setInvisibleToDebugger(true); JS::Rooted sandbox(aCx, JS_NewGlobalObject(aCx, js::Jsvalify(&workerdebuggersandbox_class), nullptr, JS::DontFireOnNewGlobalHook, options)); if (!sandbox) { JS_ReportError(aCx, "Can't create sandbox!"); aResult.set(nullptr); return; } { JSAutoCompartment ac(aCx, sandbox); JS::Rooted prototype(aCx, aPrototype); if (!JS_WrapObject(aCx, &prototype)) { JS_ReportError(aCx, "Can't wrap sandbox prototype!"); aResult.set(nullptr); return; } if (!JS_SetPrototype(aCx, sandbox, prototype)) { JS_ReportError(aCx, "Can't set sandbox prototype!"); aResult.set(nullptr); return; } nsCOMPtr globalObject = new WorkerDebuggerSandboxPrivate(sandbox); // Pass on ownership of globalObject to |sandbox|. JS_SetPrivate(sandbox, globalObject.forget().take()); } JS_FireOnNewGlobalObject(aCx, sandbox); if (!JS_WrapObject(aCx, &sandbox)) { JS_ReportError(aCx, "Can't wrap sandbox!"); aResult.set(nullptr); return; } aResult.set(sandbox); } void WorkerDebuggerGlobalScope::LoadSubScript(JSContext* aCx, const nsAString& aURL, const Optional>& aSandbox, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); Maybe ac; if (aSandbox.WasPassed()) { JS::Rooted sandbox(aCx, js::CheckedUnwrap(aSandbox.Value())); if (!IsDebuggerSandbox(sandbox)) { aRv.Throw(NS_ERROR_INVALID_ARG); return; } ac.emplace(aCx, sandbox); } nsTArray urls; urls.AppendElement(aURL); scriptloader::Load(aCx, mWorkerPrivate, urls, DebuggerScript, aRv); } void WorkerDebuggerGlobalScope::EnterEventLoop() { mWorkerPrivate->EnterDebuggerEventLoop(); } void WorkerDebuggerGlobalScope::LeaveEventLoop() { mWorkerPrivate->LeaveDebuggerEventLoop(); } void WorkerDebuggerGlobalScope::PostMessage(const nsAString& aMessage) { mWorkerPrivate->PostMessageToDebugger(aMessage); } void WorkerDebuggerGlobalScope::SetImmediate(JSContext* aCx, Function& aHandler, ErrorResult& aRv) { mWorkerPrivate->SetDebuggerImmediate(aCx, aHandler, aRv); } void WorkerDebuggerGlobalScope::ReportError(JSContext* aCx, const nsAString& aMessage) { JS::AutoFilename afn; uint32_t lineno = 0; JS::DescribeScriptedCaller(aCx, &afn, &lineno); nsString filename(NS_ConvertUTF8toUTF16(afn.get())); mWorkerPrivate->ReportErrorToDebugger(filename, lineno, aMessage); } void WorkerDebuggerGlobalScope::Dump(JSContext* aCx, const Optional& aString) const { return mWorkerPrivate->GetOrCreateGlobalScope(aCx)->Dump(aString); } nsIGlobalObject* GetGlobalObjectForGlobal(JSObject* global) { nsIGlobalObject* globalObject = nullptr; UNWRAP_WORKER_OBJECT(WorkerGlobalScope, global, globalObject); if (!globalObject) { UNWRAP_OBJECT(WorkerDebuggerGlobalScope, global, globalObject); if (!globalObject) { MOZ_ASSERT(IsDebuggerSandbox(global)); globalObject = static_cast(JS_GetPrivate(global)); MOZ_ASSERT(globalObject); } } return globalObject; } bool IsWorkerGlobal(JSObject* object) { nsIGlobalObject* globalObject = nullptr; return NS_SUCCEEDED(UNWRAP_WORKER_OBJECT(WorkerGlobalScope, object, globalObject)) && !!globalObject; } bool IsDebuggerGlobal(JSObject* object) { nsIGlobalObject* globalObject = nullptr; return NS_SUCCEEDED(UNWRAP_OBJECT(WorkerDebuggerGlobalScope, object, globalObject)) && !!globalObject; } bool IsDebuggerSandbox(JSObject* object) { return js::GetObjectClass(object) == &workerdebuggersandbox_class; } bool GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp) { JS_ReportErrorNumber(aCx, js::GetErrorMessage, nullptr, JSMSG_GETTER_ONLY); return false; } END_WORKERS_NAMESPACE