/* -*- 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/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; class SerializedStackHolder; } // 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 aJSValue, dom::Sequence& 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& aArgs, ErrorResult& aRv); void Run(nsIGlobalObject* aGlobal, JSContext* aCx, const dom::Sequence& aArgs, ExtensionEventListener* aListener, ErrorResult& aRv); void Run(nsIGlobalObject* aGlobal, JSContext* aCx, const dom::Sequence& aArgs, JS::MutableHandle aRetVal, ErrorResult& aRv); void Run(nsIGlobalObject* aGlobal, JSContext* aCx, const dom::Sequence& aArgs, ExtensionEventListener* aListener, JS::MutableHandle aRetVal, ErrorResult& aRv); void Run(nsIGlobalObject* aGlobal, JSContext* aCx, const dom::Sequence& aArgs, const RefPtr& aPromiseRetval, ErrorResult& aRv); void Run(nsIGlobalObject* aGlobal, JSContext* aCx, JS::MutableHandle aRetVal, ErrorResult& aRv); void SetSerializedCallerStack( UniquePtr aCallerStack); protected: virtual ~ExtensionAPIRequestForwarder() = default; private: already_AddRefed CreateAPIRequest( nsIGlobalObject* aGlobal, JSContext* aCx, const dom::Sequence& aArgs, ExtensionEventListener* aListener, ErrorResult& aRv); APIRequestType mRequestType; ExtensionAPIRequestTarget mRequestTarget; Maybe> 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 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& 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& 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& aArgs, const RefPtr& aPromiseRetval, ErrorResult& aRv); bool MainThreadRun() override; void ReadResult(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv); Maybe GetResultType() { return mResultType; } protected: virtual bool ProcessHandlerResult(JSContext* aCx, JS::MutableHandle aRetval); already_AddRefed GetWebExtensionPolicy(); already_AddRefed CreateAPIRequest(JSContext* aCx); void SerializeCallerStack(JSContext* aCx); void DeserializeCallerStack(JSContext* aCx, JS::MutableHandle aRetval); void SerializeArgs(JSContext* aCx, const dom::Sequence& aArgs, ErrorResult& aRv); nsresult DeserializeArgs(JSContext* aCx, JS::MutableHandle aArgs); bool HandleAPIRequest(JSContext* aCx, JS::MutableHandle aRetval); Maybe mResultType; Maybe> mResultHolder; RefPtr mPromiseProxy; Maybe> mArgsHolder; Maybe> mStackHolder; Maybe mClientInfo; uint64_t mSWDescriptorId; // Only set for addListener/removeListener API requests. RefPtr 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 mClientInfo; public: RequestInitWorkerRunnable(dom::WorkerPrivate* aWorkerPrivate, Maybe& aSWClientInfo); bool MainThreadRun() override; }; class NotifyWorkerLoadedRunnable : public Runnable { uint64_t mSWDescriptorId; nsCOMPtr mSWBaseURI; public: explicit NotifyWorkerLoadedRunnable(const uint64_t aServiceWorkerDescriptorId, const nsCOMPtr& 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 mSWBaseURI; public: explicit NotifyWorkerDestroyedRunnable( const uint64_t aServiceWorkerDescriptorId, const nsCOMPtr& 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