summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.h
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.h')
-rw-r--r--toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.h258
1 files changed, 258 insertions, 0 deletions
diff --git a/toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.h b/toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.h
new file mode 100644
index 0000000000..a74c3e7c45
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.h
@@ -0,0 +1,258 @@
+/* -*- 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/. */
+
+#ifndef mozilla_extensions_ExtensionAPIRequestForwarder_h
+#define mozilla_extensions_ExtensionAPIRequestForwarder_h
+
+#include "ExtensionAPIRequest.h"
+
+#include "mozilla/dom/PromiseWorkerProxy.h"
+#include "mozilla/dom/RootedDictionary.h"
+#include "mozilla/dom/SerializedStackHolder.h"
+#include "mozilla/dom/StructuredCloneHolder.h"
+#include "mozilla/dom/WorkerRunnable.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/ToJSValue.h"
+
+namespace mozilla {
+namespace dom {
+class ClientInfoAndState;
+class Function;
+} // namespace dom
+namespace extensions {
+
+class ExtensionAPIRequestForwarder;
+
+// A class used to forward an API request (a method call, add/remote listener or
+// a property getter) originated from a WebExtensions global (a window, a
+// content script sandbox or a service worker) to the JS privileged API request
+// handler available on the main thread (mozIExtensionAPIRequestHandler).
+//
+// Instances of this class are meant to be short-living, and destroyed when the
+// caller function is exiting.
+class ExtensionAPIRequestForwarder {
+ friend class ExtensionAPIRequest;
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExtensionAPIRequestForwarder)
+
+ public:
+ using APIRequestType = mozIExtensionAPIRequest::RequestType;
+ using APIResultType = mozIExtensionAPIRequestResult::ResultType;
+
+ static nsresult JSArrayToSequence(JSContext* aCx,
+ JS::Handle<JS::Value> aJSValue,
+ dom::Sequence<JS::Value>& aResult);
+
+ static void ThrowUnexpectedError(JSContext* aCx, ErrorResult& aRv);
+
+ static mozIExtensionAPIRequestHandler& APIRequestHandler();
+
+ ExtensionAPIRequestForwarder(const APIRequestType aRequestType,
+ const nsAString& aApiNamespace,
+ const nsAString& aApiMethod,
+ const nsAString& aApiObjectType = u""_ns,
+ const nsAString& aApiObjectId = u""_ns);
+
+ mozIExtensionAPIRequest::RequestType GetRequestType() const {
+ return mRequestType;
+ }
+
+ const ExtensionAPIRequestTarget* GetRequestTarget() {
+ return &mRequestTarget;
+ }
+
+ void Run(nsIGlobalObject* aGlobal, JSContext* aCx,
+ const dom::Sequence<JS::Value>& aArgs, ErrorResult& aRv);
+
+ void Run(nsIGlobalObject* aGlobal, JSContext* aCx,
+ const dom::Sequence<JS::Value>& aArgs,
+ ExtensionEventListener* aListener, ErrorResult& aRv);
+
+ void Run(nsIGlobalObject* aGlobal, JSContext* aCx,
+ const dom::Sequence<JS::Value>& aArgs,
+ JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv);
+
+ void Run(nsIGlobalObject* aGlobal, JSContext* aCx,
+ const dom::Sequence<JS::Value>& aArgs,
+ ExtensionEventListener* aListener,
+ JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv);
+
+ void Run(nsIGlobalObject* aGlobal, JSContext* aCx,
+ const dom::Sequence<JS::Value>& aArgs,
+ const RefPtr<dom::Promise>& aPromiseRetval, ErrorResult& aRv);
+
+ void Run(nsIGlobalObject* aGlobal, JSContext* aCx,
+ JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv);
+
+ void SetSerializedCallerStack(
+ UniquePtr<dom::SerializedStackHolder> aCallerStack);
+
+ protected:
+ virtual ~ExtensionAPIRequestForwarder() = default;
+
+ private:
+ already_AddRefed<ExtensionAPIRequest> CreateAPIRequest(
+ nsIGlobalObject* aGlobal, JSContext* aCx,
+ const dom::Sequence<JS::Value>& aArgs, ExtensionEventListener* aListener,
+ ErrorResult& aRv);
+
+ APIRequestType mRequestType;
+ ExtensionAPIRequestTarget mRequestTarget;
+ Maybe<UniquePtr<dom::SerializedStackHolder>> mStackHolder;
+};
+
+/*
+ * This runnable is used internally by ExtensionAPIRequestForwader class
+ * to call the JS privileged code that handle the API requests originated
+ * from the WebIDL bindings instantiated in a worker thread.
+ *
+ * The runnable is meant to block the worker thread until we get a result
+ * from the JS privileged code that handles the API request.
+ *
+ * For async API calls we still need to block the worker thread until
+ * we get a promise (which we link to the worker thread promise and
+ * at that point we unblock the worker thread), because the JS privileged
+ * code handling the API request may need to throw some errors synchonously
+ * (e.g. in case of additional validations based on the API schema definition
+ * for the parameter, like strings that has to pass additional validation
+ * or normalizations).
+ */
+class RequestWorkerRunnable : public dom::WorkerMainThreadRunnable {
+ public:
+ using APIRequestType = mozIExtensionAPIRequest::RequestType;
+ using APIResultType = mozIExtensionAPIRequestResult::ResultType;
+
+ RequestWorkerRunnable(dom::WorkerPrivate* aWorkerPrivate,
+ ExtensionAPIRequestForwarder* aOuterAPIRequest);
+
+ void SetSerializedCallerStack(
+ UniquePtr<dom::SerializedStackHolder> aCallerStack);
+
+ /**
+ * Init a request runnable for AddListener and RemoveListener API requests
+ * (which do have an event callback callback and do not expect any return
+ * value).
+ */
+ void Init(nsIGlobalObject* aGlobal, JSContext* aCx,
+ const dom::Sequence<JS::Value>& aArgs,
+ ExtensionEventListener* aListener, ErrorResult& aRv);
+
+ /**
+ * Init a request runnable for CallFunctionNoReturn API requests (which do
+ * do not expect any return value).
+ */
+ void Init(nsIGlobalObject* aGlobal, JSContext* aCx,
+ const dom::Sequence<JS::Value>& aArgs, ErrorResult& aRv) {
+ Init(aGlobal, aCx, aArgs, nullptr, aRv);
+ }
+
+ /**
+ * Init a request runnable for CallAsyncFunction API requests (which do
+ * expect a promise as return value).
+ */
+ void Init(nsIGlobalObject* aGlobal, JSContext* aCx,
+ const dom::Sequence<JS::Value>& aArgs,
+ const RefPtr<dom::Promise>& aPromiseRetval, ErrorResult& aRv);
+
+ bool MainThreadRun() override;
+
+ void ReadResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+ ErrorResult& aRv);
+
+ Maybe<mozIExtensionAPIRequestResult::ResultType> GetResultType() {
+ return mResultType;
+ }
+
+ protected:
+ virtual bool ProcessHandlerResult(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aRetval);
+
+ already_AddRefed<WebExtensionPolicy> GetWebExtensionPolicy();
+ already_AddRefed<ExtensionAPIRequest> CreateAPIRequest(JSContext* aCx);
+
+ void SerializeCallerStack(JSContext* aCx);
+ void DeserializeCallerStack(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aRetval);
+ void SerializeArgs(JSContext* aCx, const dom::Sequence<JS::Value>& aArgs,
+ ErrorResult& aRv);
+ nsresult DeserializeArgs(JSContext* aCx, JS::MutableHandle<JS::Value> aArgs);
+
+ bool HandleAPIRequest(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
+
+ Maybe<mozIExtensionAPIRequestResult::ResultType> mResultType;
+ Maybe<UniquePtr<dom::StructuredCloneHolder>> mResultHolder;
+ RefPtr<dom::PromiseWorkerProxy> mPromiseProxy;
+ Maybe<UniquePtr<dom::StructuredCloneHolder>> mArgsHolder;
+ Maybe<UniquePtr<dom::SerializedStackHolder>> mStackHolder;
+ Maybe<dom::ClientInfo> mClientInfo;
+ uint64_t mSWDescriptorId;
+
+ // Only set for addListener/removeListener API requests.
+ RefPtr<ExtensionEventListener> mEventListener;
+
+ // The outer request object is kept alive by the caller for the
+ // entire life of the inner worker runnable.
+ ExtensionAPIRequestForwarder* mOuterRequest;
+};
+
+class RequestInitWorkerRunnable : public dom::WorkerMainThreadRunnable {
+ Maybe<dom::ClientInfo> mClientInfo;
+
+ public:
+ RequestInitWorkerRunnable(dom::WorkerPrivate* aWorkerPrivate,
+ Maybe<dom::ClientInfo>& aSWClientInfo);
+ bool MainThreadRun() override;
+};
+
+class NotifyWorkerLoadedRunnable : public Runnable {
+ uint64_t mSWDescriptorId;
+ nsCOMPtr<nsIURI> mSWBaseURI;
+
+ public:
+ explicit NotifyWorkerLoadedRunnable(const uint64_t aServiceWorkerDescriptorId,
+ const nsCOMPtr<nsIURI>& aWorkerBaseURI)
+ : Runnable("extensions::NotifyWorkerLoadedRunnable"),
+ mSWDescriptorId(aServiceWorkerDescriptorId),
+ mSWBaseURI(aWorkerBaseURI) {
+ MOZ_ASSERT(mSWDescriptorId > 0);
+ MOZ_ASSERT(mSWBaseURI);
+ }
+
+ NS_IMETHOD Run() override;
+
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(NotifyWorkerLoadedRunnable, Runnable)
+
+ private:
+ ~NotifyWorkerLoadedRunnable() = default;
+};
+
+class NotifyWorkerDestroyedRunnable : public Runnable {
+ uint64_t mSWDescriptorId;
+ nsCOMPtr<nsIURI> mSWBaseURI;
+
+ public:
+ explicit NotifyWorkerDestroyedRunnable(
+ const uint64_t aServiceWorkerDescriptorId,
+ const nsCOMPtr<nsIURI>& aWorkerBaseURI)
+ : Runnable("extensions::NotifyWorkerDestroyedRunnable"),
+ mSWDescriptorId(aServiceWorkerDescriptorId),
+ mSWBaseURI(aWorkerBaseURI) {
+ MOZ_ASSERT(mSWDescriptorId > 0);
+ MOZ_ASSERT(mSWBaseURI);
+ }
+
+ NS_IMETHOD Run() override;
+
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(NotifyWorkerDestroyedRunnable, Runnable)
+
+ private:
+ ~NotifyWorkerDestroyedRunnable() = default;
+};
+
+} // namespace extensions
+} // namespace mozilla
+
+#endif // mozilla_extensions_ExtensionAPIRequestForwarder_h