diff options
Diffstat (limited to 'toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp')
-rw-r--r-- | toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp | 345 |
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 |