summaryrefslogtreecommitdiffstats
path: root/dom/workers/WorkerScope.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/WorkerScope.cpp')
-rw-r--r--dom/workers/WorkerScope.cpp978
1 files changed, 978 insertions, 0 deletions
diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp
new file mode 100644
index 000000000..d9a987b62
--- /dev/null
+++ b/dom/workers/WorkerScope.cpp
@@ -0,0 +1,978 @@
+/* -*- 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/IDBFactory.h"
+#include "mozilla/dom/ImageBitmap.h"
+#include "mozilla/dom/ImageBitmapBinding.h"
+#include "mozilla/dom/Performance.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseWorkerProxy.h"
+#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
+#include "mozilla/dom/SharedWorkerGlobalScopeBinding.h"
+#include "mozilla/dom/SimpleGlobalObject.h"
+#include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
+#include "mozilla/dom/WorkerGlobalScopeBinding.h"
+#include "mozilla/dom/WorkerLocation.h"
+#include "mozilla/dom/WorkerNavigator.h"
+#include "mozilla/dom/cache/CacheStorage.h"
+#include "mozilla/Services.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsIDocument.h"
+#include "nsIServiceWorkerManager.h"
+#include "nsIScriptTimeoutHandler.h"
+
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
+#include "Crypto.h"
+#include "Principal.h"
+#include "RuntimeService.h"
+#include "ScriptLoader.h"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
+#include "ServiceWorkerClients.h"
+#include "ServiceWorkerManager.h"
+#include "ServiceWorkerRegistration.h"
+
+#ifdef XP_WIN
+#undef PostMessage
+#endif
+
+extern already_AddRefed<nsIScriptTimeoutHandler>
+NS_CreateJSTimeoutHandler(JSContext* aCx,
+ mozilla::dom::workers::WorkerPrivate* aWorkerPrivate,
+ mozilla::dom::Function& aFunction,
+ const mozilla::dom::Sequence<JS::Value>& aArguments,
+ mozilla::ErrorResult& aError);
+
+extern already_AddRefed<nsIScriptTimeoutHandler>
+NS_CreateJSTimeoutHandler(JSContext* aCx,
+ mozilla::dom::workers::WorkerPrivate* aWorkerPrivate,
+ const nsAString& aExpression);
+
+using namespace mozilla;
+using namespace mozilla::dom;
+USING_WORKERS_NAMESPACE
+
+using mozilla::dom::cache::CacheStorage;
+using mozilla::ipc::PrincipalInfo;
+
+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(mCrypto)
+ 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);
+ tmp->mWorkerPrivate->TraverseTimeouts(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(mCrypto)
+ 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();
+ tmp->mWorkerPrivate->UnlinkTimeouts();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerGlobalScope,
+ DOMEventTargetHelper)
+ tmp->mWorkerPrivate->AssertIsOnWorkerThread();
+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<JSObject*> aGivenProto)
+{
+ MOZ_CRASH("We should never get here!");
+}
+
+Console*
+WorkerGlobalScope::GetConsole(ErrorResult& aRv)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ if (!mConsole) {
+ mConsole = Console::Create(nullptr, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ }
+
+ return mConsole;
+}
+
+Crypto*
+WorkerGlobalScope::GetCrypto(ErrorResult& aError)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ if (!mCrypto) {
+ mCrypto = new Crypto();
+ mCrypto->Init(this);
+ }
+
+ return mCrypto;
+}
+
+already_AddRefed<CacheStorage>
+WorkerGlobalScope::GetCaches(ErrorResult& aRv)
+{
+ if (!mCacheStorage) {
+ MOZ_ASSERT(mWorkerPrivate);
+ 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::GetObjectCompartment(GetWrapperPreserveColor()));
+ MOZ_ASSERT(globalSecure == mWorkerPrivate->IsSecureContext());
+ return globalSecure;
+}
+
+already_AddRefed<WorkerLocation>
+WorkerGlobalScope::Location()
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ if (!mLocation) {
+ WorkerPrivate::LocationInfo& info = mWorkerPrivate->GetLocationInfo();
+
+ mLocation = WorkerLocation::Create(info);
+ MOZ_ASSERT(mLocation);
+ }
+
+ RefPtr<WorkerLocation> location = mLocation;
+ return location.forget();
+}
+
+already_AddRefed<WorkerNavigator>
+WorkerGlobalScope::Navigator()
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ if (!mNavigator) {
+ mNavigator = WorkerNavigator::Create(mWorkerPrivate->OnLine());
+ MOZ_ASSERT(mNavigator);
+ }
+
+ RefPtr<WorkerNavigator> navigator = mNavigator;
+ return navigator.forget();
+}
+
+already_AddRefed<WorkerNavigator>
+WorkerGlobalScope::GetExistingNavigator() const
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ RefPtr<WorkerNavigator> 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(const Sequence<nsString>& aScriptURLs,
+ ErrorResult& aRv)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ scriptloader::Load(mWorkerPrivate, aScriptURLs, WorkerScript, aRv);
+}
+
+int32_t
+WorkerGlobalScope::SetTimeout(JSContext* aCx,
+ Function& aHandler,
+ const int32_t aTimeout,
+ const Sequence<JS::Value>& aArguments,
+ ErrorResult& aRv)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ nsCOMPtr<nsIScriptTimeoutHandler> handler =
+ NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler, aArguments, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return 0;
+ }
+
+ return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, false, aRv);
+}
+
+int32_t
+WorkerGlobalScope::SetTimeout(JSContext* aCx,
+ const nsAString& aHandler,
+ const int32_t aTimeout,
+ const Sequence<JS::Value>& /* unused */,
+ ErrorResult& aRv)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ nsCOMPtr<nsIScriptTimeoutHandler> handler =
+ NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler);
+ return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, false, aRv);
+}
+
+void
+WorkerGlobalScope::ClearTimeout(int32_t aHandle)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ mWorkerPrivate->ClearTimeout(aHandle);
+}
+
+int32_t
+WorkerGlobalScope::SetInterval(JSContext* aCx,
+ Function& aHandler,
+ const Optional<int32_t>& aTimeout,
+ const Sequence<JS::Value>& aArguments,
+ ErrorResult& aRv)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ bool isInterval = aTimeout.WasPassed();
+ int32_t timeout = aTimeout.WasPassed() ? aTimeout.Value() : 0;
+
+ nsCOMPtr<nsIScriptTimeoutHandler> handler =
+ NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler, aArguments, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return 0;
+ }
+
+ return mWorkerPrivate->SetTimeout(aCx, handler, timeout, isInterval, aRv);
+}
+
+int32_t
+WorkerGlobalScope::SetInterval(JSContext* aCx,
+ const nsAString& aHandler,
+ const Optional<int32_t>& aTimeout,
+ const Sequence<JS::Value>& /* unused */,
+ ErrorResult& aRv)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ Sequence<JS::Value> dummy;
+
+ bool isInterval = aTimeout.WasPassed();
+ int32_t timeout = aTimeout.WasPassed() ? aTimeout.Value() : 0;
+
+ nsCOMPtr<nsIScriptTimeoutHandler> handler =
+ NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler);
+ return mWorkerPrivate->SetTimeout(aCx, handler, timeout, isInterval, aRv);
+}
+
+void
+WorkerGlobalScope::ClearInterval(int32_t aHandle)
+{
+ 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<nsAString>& aString) const
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ if (!aString.WasPassed()) {
+ return;
+ }
+
+#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
+ if (!mWorkerPrivate->DumpEnabled()) {
+ return;
+ }
+#endif
+
+ 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()
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ if (!mPerformance) {
+ mPerformance = Performance::CreateForWorker(mWorkerPrivate);
+ }
+
+ return mPerformance;
+}
+
+already_AddRefed<Promise>
+WorkerGlobalScope::Fetch(const RequestOrUSVString& aInput,
+ const RequestInit& aInit, ErrorResult& aRv)
+{
+ return FetchRequest(this, aInput, aInit, aRv);
+}
+
+already_AddRefed<IDBFactory>
+WorkerGlobalScope::GetIndexedDB(ErrorResult& aErrorResult)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ RefPtr<IDBFactory> indexedDB = mIndexedDB;
+
+ if (!indexedDB) {
+ if (!mWorkerPrivate->IsStorageAllowed()) {
+ NS_WARNING("IndexedDB is not allowed in this worker!");
+ aErrorResult = NS_ERROR_DOM_SECURITY_ERR;
+ return nullptr;
+ }
+
+ JSContext* cx = mWorkerPrivate->GetJSContext();
+ MOZ_ASSERT(cx);
+
+ JS::Rooted<JSObject*> 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<Promise>
+WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage,
+ ErrorResult& aRv)
+{
+ if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
+ aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+ return nullptr;
+ }
+
+ return ImageBitmap::Create(this, aImage, Nothing(), aRv);
+}
+
+already_AddRefed<Promise>
+WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage,
+ int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
+ ErrorResult& aRv)
+{
+ if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
+ aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+ return nullptr;
+ }
+
+ return ImageBitmap::Create(this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aRv);
+}
+
+already_AddRefed<mozilla::dom::Promise>
+WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage,
+ int32_t aOffset, int32_t aLength,
+ ImageBitmapFormat aFormat,
+ const Sequence<ChannelPixelLayout>& aLayout,
+ ErrorResult& aRv)
+{
+ JSContext* cx = GetCurrentThreadJSContext();
+ MOZ_ASSERT(cx);
+
+ if (!ImageBitmap::ExtensionsEnabled(cx, nullptr)) {
+ aRv.Throw(NS_ERROR_TYPE_ERR);
+ return nullptr;
+ }
+
+ if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
+ return ImageBitmap::Create(this, aImage, aOffset, aLength, aFormat, aLayout,
+ aRv);
+ } else {
+ aRv.Throw(NS_ERROR_TYPE_ERR);
+ return nullptr;
+ }
+}
+
+DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
+: WorkerGlobalScope(aWorkerPrivate)
+{
+}
+
+bool
+DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx,
+ JS::MutableHandle<JSObject*> 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 &&
+ xpc::ShouldDiscardSystemSource();
+ const bool extraWarnings = usesSystemPrincipal &&
+ xpc::ExtraWarningsForSystemJS();
+
+ JS::CompartmentBehaviors& behaviors = options.behaviors();
+ behaviors.setDiscardSource(discardSource)
+ .extraWarningsOverride().set(extraWarnings);
+
+ const bool sharedMemoryEnabled = xpc::SharedMemoryEnabled();
+
+ JS::CompartmentCreationOptions& creationOptions = options.creationOptions();
+ creationOptions.setSharedMemoryAndAtomicsEnabled(sharedMemoryEnabled);
+
+ return DedicatedWorkerGlobalScopeBinding::Wrap(aCx, this, this,
+ options,
+ GetWorkerPrincipal(),
+ true, aReflector);
+}
+
+void
+DedicatedWorkerGlobalScope::PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& 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<JSObject*> aReflector)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ MOZ_ASSERT(mWorkerPrivate->IsSharedWorker());
+
+ JS::CompartmentOptions options;
+ mWorkerPrivate->CopyJSCompartmentOptions(options);
+
+ return SharedWorkerGlobalScopeBinding::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<JSObject*> aReflector)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
+
+ JS::CompartmentOptions options;
+ mWorkerPrivate->CopyJSCompartmentOptions(options);
+
+ return ServiceWorkerGlobalScopeBinding::Wrap(aCx, this, this, options,
+ GetWorkerPrincipal(),
+ true, aReflector);
+}
+
+ServiceWorkerClients*
+ServiceWorkerGlobalScope::Clients()
+{
+ if (!mClients) {
+ mClients = new ServiceWorkerClients(this);
+ }
+
+ return mClients;
+}
+
+ServiceWorkerRegistration*
+ServiceWorkerGlobalScope::Registration()
+{
+ if (!mRegistration) {
+ mRegistration =
+ ServiceWorkerRegistration::CreateForWorker(mWorkerPrivate, mScope);
+ }
+
+ return mRegistration;
+}
+
+namespace {
+
+class SkipWaitingResultRunnable final : public WorkerRunnable
+{
+ RefPtr<PromiseWorkerProxy> mPromiseProxy;
+
+public:
+ SkipWaitingResultRunnable(WorkerPrivate* aWorkerPrivate,
+ PromiseWorkerProxy* aPromiseProxy)
+ : WorkerRunnable(aWorkerPrivate)
+ , mPromiseProxy(aPromiseProxy)
+ {
+ AssertIsOnMainThread();
+ }
+
+ virtual bool
+ WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+ {
+ MOZ_ASSERT(aWorkerPrivate);
+ aWorkerPrivate->AssertIsOnWorkerThread();
+
+ RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
+ promise->MaybeResolveWithUndefined();
+
+ // Release the reference on the worker thread.
+ mPromiseProxy->CleanUp();
+
+ return true;
+ }
+};
+
+class WorkerScopeSkipWaitingRunnable final : public Runnable
+{
+ RefPtr<PromiseWorkerProxy> mPromiseProxy;
+ nsCString mScope;
+
+public:
+ WorkerScopeSkipWaitingRunnable(PromiseWorkerProxy* aPromiseProxy,
+ const nsCString& aScope)
+ : mPromiseProxy(aPromiseProxy)
+ , mScope(aScope)
+ {
+ MOZ_ASSERT(aPromiseProxy);
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ AssertIsOnMainThread();
+
+ MutexAutoLock lock(mPromiseProxy->Lock());
+ if (mPromiseProxy->CleanedUp()) {
+ return NS_OK;
+ }
+
+ WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+ MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
+
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ if (swm) {
+ swm->SetSkipWaitingFlag(workerPrivate->GetPrincipal(), mScope,
+ workerPrivate->ServiceWorkerID());
+ }
+
+ RefPtr<SkipWaitingResultRunnable> runnable =
+ new SkipWaitingResultRunnable(workerPrivate, mPromiseProxy);
+
+ if (!runnable->Dispatch()) {
+ NS_WARNING("Failed to dispatch SkipWaitingResultRunnable to the worker.");
+ }
+ return NS_OK;
+ }
+};
+
+} // namespace
+
+already_AddRefed<Promise>
+ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
+
+ RefPtr<Promise> promise = Promise::Create(this, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ RefPtr<PromiseWorkerProxy> promiseProxy =
+ PromiseWorkerProxy::Create(mWorkerPrivate, promise);
+ if (!promiseProxy) {
+ promise->MaybeResolveWithUndefined();
+ return promise.forget();
+ }
+
+ RefPtr<WorkerScopeSkipWaitingRunnable> runnable =
+ new WorkerScopeSkipWaitingRunnable(promiseProxy,
+ NS_ConvertUTF16toUTF8(mScope));
+
+ MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
+ return promise.forget();
+}
+
+bool
+ServiceWorkerGlobalScope::OpenWindowEnabled(JSContext* aCx, JSObject* aObj)
+{
+ WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(worker);
+ worker->AssertIsOnWorkerThread();
+ return worker->OpenWindowEnabled();
+}
+
+WorkerDebuggerGlobalScope::WorkerDebuggerGlobalScope(
+ WorkerPrivate* aWorkerPrivate)
+: mWorkerPrivate(aWorkerPrivate)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+}
+
+WorkerDebuggerGlobalScope::~WorkerDebuggerGlobalScope()
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerDebuggerGlobalScope)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerDebuggerGlobalScope,
+ DOMEventTargetHelper)
+ tmp->mWorkerPrivate->AssertIsOnWorkerThread();
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerDebuggerGlobalScope,
+ DOMEventTargetHelper)
+ tmp->mWorkerPrivate->AssertIsOnWorkerThread();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerDebuggerGlobalScope,
+ DOMEventTargetHelper)
+ tmp->mWorkerPrivate->AssertIsOnWorkerThread();
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+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<JSObject*> 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<JSObject*> aGlobal,
+ ErrorResult& aRv)
+{
+ WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx);
+ if (!scope) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ }
+
+ aGlobal.set(scope->GetWrapper());
+}
+
+void
+WorkerDebuggerGlobalScope::CreateSandbox(JSContext* aCx, const nsAString& aName,
+ JS::Handle<JSObject*> aPrototype,
+ JS::MutableHandle<JSObject*> aResult,
+ ErrorResult& aRv)
+{
+ mWorkerPrivate->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)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ Maybe<JSAutoCompartment> ac;
+ if (aSandbox.WasPassed()) {
+ JS::Rooted<JSObject*> sandbox(aCx, js::CheckedUnwrap(aSandbox.Value()));
+ if (!IsDebuggerSandbox(sandbox)) {
+ aRv.Throw(NS_ERROR_INVALID_ARG);
+ return;
+ }
+
+ ac.emplace(aCx, sandbox);
+ }
+
+ nsTArray<nsString> urls;
+ urls.AppendElement(aURL);
+ scriptloader::Load(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(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::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);
+}
+
+Console*
+WorkerDebuggerGlobalScope::GetConsole(ErrorResult& aRv)
+{
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ // Debugger console has its own console object.
+ if (!mConsole) {
+ mConsole = Console::Create(nullptr, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ }
+
+ return mConsole;
+}
+
+void
+WorkerDebuggerGlobalScope::Dump(JSContext* aCx,
+ const Optional<nsAString>& aString) const
+{
+ WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx);
+ if (scope) {
+ scope->Dump(aString);
+ }
+}
+
+BEGIN_WORKERS_NAMESPACE
+
+bool
+IsWorkerGlobal(JSObject* object)
+{
+ return IS_INSTANCE_OF(WorkerGlobalScope, object);
+}
+
+bool
+IsDebuggerGlobal(JSObject* object)
+{
+ return IS_INSTANCE_OF(WorkerDebuggerGlobalScope, object);
+}
+
+bool
+IsDebuggerSandbox(JSObject* object)
+{
+ return SimpleGlobalObject::SimpleGlobalType(object) ==
+ SimpleGlobalObject::GlobalType::WorkerDebuggerSandbox;
+}
+
+bool
+GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+{
+ JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
+ return false;
+}
+
+END_WORKERS_NAMESPACE