summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp')
-rw-r--r--toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp345
1 files changed, 345 insertions, 0 deletions
diff --git a/toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp b/toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp
new file mode 100644
index 0000000000..069c914dc4
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp
@@ -0,0 +1,345 @@
+/* -*- 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 "ExtensionBrowser.h"
+#include "ExtensionAPIRequestForwarder.h"
+
+#include "mozilla/dom/ExtensionBrowserBinding.h"
+#include "mozilla/dom/ExtensionPortBinding.h" // ExtensionPortDescriptor
+#include "mozilla/dom/WorkerScope.h" // GetWorkerPrivateFromContext
+#include "mozilla/extensions/ExtensionAlarms.h"
+#include "mozilla/extensions/ExtensionBrowserSettings.h"
+#include "mozilla/extensions/ExtensionDns.h"
+#include "mozilla/extensions/ExtensionMockAPI.h"
+#include "mozilla/extensions/ExtensionPort.h"
+#include "mozilla/extensions/ExtensionProxy.h"
+#include "mozilla/extensions/ExtensionRuntime.h"
+#include "mozilla/extensions/ExtensionScripting.h"
+#include "mozilla/extensions/ExtensionTest.h"
+#include "mozilla/extensions/WebExtensionPolicy.h"
+
+namespace mozilla {
+namespace extensions {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(ExtensionBrowser)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(ExtensionBrowser)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(ExtensionBrowser)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtensionBrowser)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ExtensionBrowser)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionAlarms)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionBrowserSettings)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionDns)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionMockAPI)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionProxy)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionRuntime)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionScripting)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionTest)
+ tmp->mLastError.setUndefined();
+ tmp->mPortsLookup.Clear();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ExtensionBrowser)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionAlarms)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionBrowserSettings)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionDns)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionMockAPI)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionRuntime)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionProxy)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionScripting)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionTest)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ExtensionBrowser)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLastError)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+ExtensionBrowser::ExtensionBrowser(nsIGlobalObject* aGlobal)
+ : mGlobal(aGlobal) {
+ MOZ_DIAGNOSTIC_ASSERT(mGlobal);
+}
+
+JSObject* ExtensionBrowser::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return dom::ExtensionBrowser_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+nsIGlobalObject* ExtensionBrowser::GetParentObject() const { return mGlobal; }
+
+bool ExtensionAPIAllowed(JSContext* aCx, JSObject* aGlobal) {
+#ifdef MOZ_WEBEXT_WEBIDL_ENABLED
+ // Only expose the Extension API bindings if:
+ // - the context is related to a worker where the Extension API are allowed
+ // (currently only the extension service worker declared in the extension
+ // manifest met this condition)
+ // - the global is an extension window or an extension content script sandbox
+ // TODO:
+ // - the support for the extension window is deferred to a followup.
+ // - support for the content script sandboxes is also deferred to follow-ups
+ // - lock native Extension API in an extension window or sandbox behind a
+ // separate pref.
+ MOZ_DIAGNOSTIC_ASSERT(
+ !NS_IsMainThread(),
+ "ExtensionAPI webidl bindings does not yet support main thread globals");
+
+ // Verify if the Extensions API should be allowed on a worker thread.
+ if (!StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup()) {
+ return false;
+ }
+
+ auto* workerPrivate = mozilla::dom::GetWorkerPrivateFromContext(aCx);
+ MOZ_ASSERT(workerPrivate);
+ MOZ_ASSERT(workerPrivate->IsServiceWorker());
+
+ return workerPrivate->ExtensionAPIAllowed();
+#else
+ // Always return false on build where MOZ_WEBEXT_WEBIDL_ENABLED is set to
+ // false (currently on all channels but nightly).
+ return false;
+#endif
+}
+
+void CreateAndDispatchInitWorkerContextRunnable() {
+ MOZ_ASSERT(dom::IsCurrentThreadRunningWorker());
+ // DO NOT pass this WorkerPrivate raw pointer to anything else but the
+ // RequestInitWorkerRunnable (which extends dom::WorkerMainThreadRunnable).
+ dom::WorkerPrivate* workerPrivate = dom::GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(workerPrivate);
+ MOZ_ASSERT(workerPrivate->ExtensionAPIAllowed());
+ MOZ_ASSERT(workerPrivate->IsServiceWorker());
+ workerPrivate->AssertIsOnWorkerThread();
+
+ auto* workerScope = workerPrivate->GlobalScope();
+ if (NS_WARN_IF(!workerScope)) {
+ return;
+ }
+
+ Maybe<dom::ClientInfo> clientInfo = workerScope->GetClientInfo();
+ if (NS_WARN_IF(clientInfo.isNothing())) {
+ return;
+ }
+
+ RefPtr<RequestInitWorkerRunnable> runnable =
+ new RequestInitWorkerRunnable(std::move(workerPrivate), clientInfo);
+ IgnoredErrorResult rv;
+ runnable->Dispatch(dom::WorkerStatus::Canceling, rv);
+ if (rv.Failed()) {
+ NS_WARNING("Failed to dispatch extensions::RequestInitWorkerRunnable");
+ }
+}
+
+already_AddRefed<Runnable> CreateWorkerLoadedRunnable(
+ const uint64_t aServiceWorkerDescriptorId,
+ const nsCOMPtr<nsIURI>& aWorkerBaseURI) {
+ RefPtr<NotifyWorkerLoadedRunnable> runnable = new NotifyWorkerLoadedRunnable(
+ aServiceWorkerDescriptorId, aWorkerBaseURI);
+ return runnable.forget();
+}
+
+already_AddRefed<Runnable> CreateWorkerDestroyedRunnable(
+ const uint64_t aServiceWorkerDescriptorId,
+ const nsCOMPtr<nsIURI>& aWorkerBaseURI) {
+ RefPtr<NotifyWorkerDestroyedRunnable> runnable =
+ new NotifyWorkerDestroyedRunnable(aServiceWorkerDescriptorId,
+ aWorkerBaseURI);
+ return runnable.forget();
+}
+
+void ExtensionBrowser::SetLastError(JS::Handle<JS::Value> aLastError) {
+ mLastError.set(aLastError);
+ mCheckedLastError = false;
+}
+
+void ExtensionBrowser::GetLastError(JS::MutableHandle<JS::Value> aRetVal) {
+ aRetVal.set(mLastError);
+ mCheckedLastError = true;
+}
+
+bool ExtensionBrowser::ClearLastError() {
+ bool shouldReport = !mCheckedLastError;
+ mLastError.setUndefined();
+ return shouldReport;
+}
+
+already_AddRefed<ExtensionPort> ExtensionBrowser::GetPort(
+ JS::Handle<JS::Value> aDescriptorValue, ErrorResult& aRv) {
+ // Get a port descriptor from the js value got from the API request
+ // handler.
+ UniquePtr<dom::ExtensionPortDescriptor> portDescriptor =
+ ExtensionPort::ToPortDescriptor(aDescriptorValue, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ auto portId = portDescriptor->mPortId;
+ auto maybePort = mPortsLookup.MaybeGet(portId);
+ if (maybePort.isSome() && maybePort.value().get()) {
+ RefPtr<ExtensionPort> existingPort = maybePort.value().get();
+ return existingPort.forget();
+ }
+
+ RefPtr<ExtensionPort> newPort =
+ ExtensionPort::Create(mGlobal, this, std::move(portDescriptor));
+ mPortsLookup.InsertOrUpdate(portId, newPort);
+ return newPort.forget();
+}
+
+void ExtensionBrowser::ForgetReleasedPort(const nsAString& aPortId) {
+ mPortsLookup.Remove(aPortId);
+}
+
+ExtensionAlarms* ExtensionBrowser::GetExtensionAlarms() {
+ if (!mExtensionAlarms) {
+ mExtensionAlarms = new ExtensionAlarms(mGlobal, this);
+ }
+
+ return mExtensionAlarms;
+}
+
+ExtensionBrowserSettings* ExtensionBrowser::GetExtensionBrowserSettings() {
+ if (!mExtensionBrowserSettings) {
+ mExtensionBrowserSettings = new ExtensionBrowserSettings(mGlobal, this);
+ }
+
+ return mExtensionBrowserSettings;
+}
+
+ExtensionDns* ExtensionBrowser::GetExtensionDns() {
+ if (!mExtensionDns) {
+ mExtensionDns = new ExtensionDns(mGlobal, this);
+ }
+
+ return mExtensionDns;
+}
+
+ExtensionMockAPI* ExtensionBrowser::GetExtensionMockAPI() {
+ if (!mExtensionMockAPI) {
+ mExtensionMockAPI = new ExtensionMockAPI(mGlobal, this);
+ }
+
+ return mExtensionMockAPI;
+}
+
+ExtensionProxy* ExtensionBrowser::GetExtensionProxy() {
+ if (!mExtensionProxy) {
+ mExtensionProxy = new ExtensionProxy(mGlobal, this);
+ }
+
+ return mExtensionProxy;
+}
+
+ExtensionRuntime* ExtensionBrowser::GetExtensionRuntime() {
+ if (!mExtensionRuntime) {
+ mExtensionRuntime = new ExtensionRuntime(mGlobal, this);
+ }
+
+ return mExtensionRuntime;
+}
+
+ExtensionScripting* ExtensionBrowser::GetExtensionScripting() {
+ if (!mExtensionScripting) {
+ mExtensionScripting = new ExtensionScripting(mGlobal, this);
+ }
+
+ return mExtensionScripting;
+}
+
+ExtensionTest* ExtensionBrowser::GetExtensionTest() {
+ if (!mExtensionTest) {
+ mExtensionTest = new ExtensionTest(mGlobal, this);
+ }
+
+ return mExtensionTest;
+}
+
+// static
+void ExtensionEventWakeupMap::ToMapKey(const nsAString& aAPINamespace,
+ const nsAString& aAPIName,
+ nsAString& aResultMapKey) {
+ aResultMapKey.Truncate();
+ aResultMapKey.AppendPrintf("%s.%s",
+ NS_ConvertUTF16toUTF8(aAPINamespace).get(),
+ NS_ConvertUTF16toUTF8(aAPIName).get());
+}
+
+nsresult ExtensionEventWakeupMap::IncrementListeners(
+ const nsAString& aAPINamespace, const nsAString& aAPIName) {
+ nsString key;
+ ToMapKey(aAPINamespace, aAPIName, key);
+ auto maybeCount = MaybeGet(key);
+ if (maybeCount.isSome()) {
+ InsertOrUpdate(key, maybeCount.value() + 1);
+ } else {
+ InsertOrUpdate(key, 1);
+ }
+
+ return NS_OK;
+}
+
+nsresult ExtensionEventWakeupMap::DecrementListeners(
+ const nsAString& aAPINamespace, const nsAString& aAPIName) {
+ nsString key;
+ ToMapKey(aAPINamespace, aAPIName, key);
+ auto maybeCount = MaybeGet(key);
+ if (maybeCount.isSome()) {
+ MOZ_ASSERT(maybeCount.value() >= 1, "Unexpected counter value set to zero");
+ uint64_t val = maybeCount.value() - 1;
+ if (val == 0) {
+ Remove(key);
+ } else {
+ InsertOrUpdate(key, val);
+ }
+ }
+
+ return NS_OK;
+}
+
+bool ExtensionEventWakeupMap::HasListener(const nsAString& aAPINamespace,
+ const nsAString& aAPIName) {
+ nsString key;
+ ToMapKey(aAPINamespace, aAPIName, key);
+ auto maybeCount = MaybeGet(key);
+ return (maybeCount.isSome() && maybeCount.value() > 0);
+}
+
+nsresult ExtensionBrowser::TrackWakeupEventListener(
+ JSContext* aCx, const nsString& aAPINamespace, const nsString& aAPIName) {
+ auto* workerPrivate = mozilla::dom::GetWorkerPrivateFromContext(aCx);
+ if (workerPrivate->WorkerScriptExecutedSuccessfully()) {
+ // Ignore if the worker script has already executed all its synchronous
+ // statements.
+ return NS_OK;
+ }
+ mExpectedEventWakeupMap.IncrementListeners(aAPINamespace, aAPIName);
+ return NS_OK;
+}
+
+nsresult ExtensionBrowser::UntrackWakeupEventListener(
+ JSContext* aCx, const nsString& aAPINamespace, const nsString& aAPIName) {
+ auto* workerPrivate = mozilla::dom::GetWorkerPrivateFromContext(aCx);
+ if (workerPrivate->WorkerScriptExecutedSuccessfully()) {
+ // Ignore if the worker script has already executed all its synchronous
+ return NS_OK;
+ }
+ mExpectedEventWakeupMap.DecrementListeners(aAPINamespace, aAPIName);
+ return NS_OK;
+}
+
+bool ExtensionBrowser::HasWakeupEventListener(const nsString& aAPINamespace,
+ const nsString& aAPIName) {
+ return mExpectedEventWakeupMap.HasListener(aAPINamespace, aAPIName);
+}
+
+} // namespace extensions
+} // namespace mozilla