summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/webidl-api/ExtensionEventListener.h
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/extensions/webidl-api/ExtensionEventListener.h')
-rw-r--r--toolkit/components/extensions/webidl-api/ExtensionEventListener.h234
1 files changed, 234 insertions, 0 deletions
diff --git a/toolkit/components/extensions/webidl-api/ExtensionEventListener.h b/toolkit/components/extensions/webidl-api/ExtensionEventListener.h
new file mode 100644
index 0000000000..e986e4f58b
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/ExtensionEventListener.h
@@ -0,0 +1,234 @@
+/* -*- 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_ExtensionEventListener_h
+#define mozilla_extensions_ExtensionEventListener_h
+
+#include "js/Promise.h" // JS::IsPromiseObject
+#include "mozIExtensionAPIRequestHandling.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
+#include "mozilla/dom/StructuredCloneHolder.h"
+#include "mozilla/dom/WorkerRunnable.h"
+#include "mozilla/dom/WorkerPrivate.h"
+
+#include "ExtensionBrowser.h"
+
+class nsIGlobalObject;
+
+namespace mozilla {
+
+namespace dom {
+class Function;
+} // namespace dom
+
+namespace extensions {
+
+#define SLOT_SEND_RESPONSE_CALLBACK_INSTANCE 0
+
+// A class that represents a callback parameter passed to WebExtensions API
+// addListener / removeListener methods.
+//
+// Instances of this class are sent to the mozIExtensionAPIRequestHandler as
+// a property of the mozIExtensionAPIRequest.
+//
+// The mozIExtensionEventListener xpcom interface provides methods that allow
+// the mozIExtensionAPIRequestHandler running in the Main Thread to call the
+// underlying callback Function on its owning thread.
+class ExtensionEventListener final : public mozIExtensionEventListener {
+ public:
+ NS_DECL_MOZIEXTENSIONEVENTLISTENER
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ using CleanupCallback = std::function<void()>;
+ using ListenerCallOptions = mozIExtensionListenerCallOptions;
+ using APIObjectType = ListenerCallOptions::APIObjectType;
+ using CallbackType = ListenerCallOptions::CallbackType;
+
+ static already_AddRefed<ExtensionEventListener> Create(
+ nsIGlobalObject* aGlobal, ExtensionBrowser* aExtensionBrowser,
+ dom::Function* aCallback, CleanupCallback&& aCleanupCallback,
+ ErrorResult& aRv);
+
+ static bool IsPromise(JSContext* aCx, JS::Handle<JS::Value> aValue) {
+ if (!aValue.isObject()) {
+ return false;
+ }
+
+ JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
+ return JS::IsPromiseObject(obj);
+ }
+
+ dom::WorkerPrivate* GetWorkerPrivate() const;
+
+ RefPtr<dom::Function> GetCallback() const { return mCallback; }
+
+ nsCOMPtr<nsIGlobalObject> GetGlobalObject() const {
+ nsCOMPtr<nsIGlobalObject> global = do_QueryReferent(mGlobal);
+ return global;
+ }
+
+ ExtensionBrowser* GetExtensionBrowser() const { return mExtensionBrowser; }
+
+ void Cleanup() {
+ if (mWorkerRef) {
+ MutexAutoLock lock(mMutex);
+
+ mWorkerRef->Private()->AssertIsOnWorkerThread();
+ mWorkerRef = nullptr;
+ }
+
+ mGlobal = nullptr;
+ mCallback = nullptr;
+ mExtensionBrowser = nullptr;
+ }
+
+ private:
+ ExtensionEventListener(nsIGlobalObject* aGlobal,
+ ExtensionBrowser* aExtensionBrowser,
+ dom::Function* aCallback)
+ : mGlobal(do_GetWeakReference(aGlobal)),
+ mExtensionBrowser(aExtensionBrowser),
+ mCallback(aCallback),
+ mMutex("ExtensionEventListener::mMutex") {
+ MOZ_ASSERT(aGlobal);
+ MOZ_ASSERT(aExtensionBrowser);
+ MOZ_ASSERT(aCallback);
+ };
+
+ static UniquePtr<dom::StructuredCloneHolder> SerializeCallArguments(
+ const nsTArray<JS::Value>& aArgs, JSContext* aCx, ErrorResult& aRv);
+
+ ~ExtensionEventListener() { Cleanup(); };
+
+ // Accessed on the main and on the owning threads.
+ RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef;
+
+ // Accessed only on the owning thread.
+ nsWeakPtr mGlobal;
+ RefPtr<ExtensionBrowser> mExtensionBrowser;
+ RefPtr<dom::Function> mCallback;
+
+ // Used to make sure we are not going to release the
+ // instance on the worker thread, while we are in the
+ // process of forwarding a call from the main thread.
+ Mutex mMutex MOZ_UNANNOTATED;
+};
+
+// A WorkerRunnable subclass used to call an ExtensionEventListener
+// in the thread that owns the dom::Function wrapped by the
+// ExtensionEventListener class.
+class ExtensionListenerCallWorkerRunnable final : public dom::WorkerRunnable {
+ friend class ExtensionListenerCallPromiseResultHandler;
+
+ public:
+ using ListenerCallOptions = mozIExtensionListenerCallOptions;
+ using APIObjectType = ListenerCallOptions::APIObjectType;
+ using CallbackType = ListenerCallOptions::CallbackType;
+
+ ExtensionListenerCallWorkerRunnable(
+ const RefPtr<ExtensionEventListener>& aExtensionEventListener,
+ UniquePtr<dom::StructuredCloneHolder> aArgsHolder,
+ ListenerCallOptions* aCallOptions,
+ RefPtr<dom::Promise> aPromiseRetval = nullptr)
+ : WorkerRunnable(aExtensionEventListener->GetWorkerPrivate(),
+ "ExtensionListenerCallWorkerRunnable", WorkerThread),
+ mListener(aExtensionEventListener),
+ mArgsHolder(std::move(aArgsHolder)),
+ mPromiseResult(std::move(aPromiseRetval)),
+ mAPIObjectType(APIObjectType::NONE),
+ mCallbackArgType(CallbackType::CALLBACK_NONE) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aExtensionEventListener);
+
+ if (aCallOptions) {
+ aCallOptions->GetApiObjectType(&mAPIObjectType);
+ aCallOptions->GetApiObjectPrepended(&mAPIObjectPrepended);
+ aCallOptions->GetCallbackType(&mCallbackArgType);
+ }
+ }
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ bool WorkerRun(JSContext* aCx, dom::WorkerPrivate* aWorkerPrivate) override;
+
+ bool IsCallResultCancelled() { return mIsCallResultCancelled; }
+
+ private:
+ ~ExtensionListenerCallWorkerRunnable() {
+ NS_ReleaseOnMainThread("~ExtensionListenerCallWorkerRunnable",
+ mPromiseResult.forget());
+ ReleaseArgsHolder();
+ mListener = nullptr;
+ }
+
+ void ReleaseArgsHolder() {
+ if (NS_IsMainThread()) {
+ mArgsHolder = nullptr;
+ } else {
+ auto releaseArgsHolder = [argsHolder = std::move(mArgsHolder)]() {};
+ nsCOMPtr<nsIRunnable> runnable =
+ NS_NewRunnableFunction(__func__, std::move(releaseArgsHolder));
+ NS_DispatchToMainThread(runnable);
+ }
+ }
+
+ void DeserializeCallArguments(JSContext* aCx, dom::Sequence<JS::Value>& aArg,
+ ErrorResult& aRv);
+
+ RefPtr<ExtensionEventListener> mListener;
+ UniquePtr<dom::StructuredCloneHolder> mArgsHolder;
+ RefPtr<dom::Promise> mPromiseResult;
+ bool mIsCallResultCancelled = false;
+ // Call Options.
+ bool mAPIObjectPrepended;
+ APIObjectType mAPIObjectType;
+ CallbackType mCallbackArgType;
+};
+
+// A class attached to the promise that should be resolved once the extension
+// event listener call has been handled, responsible for serializing resolved
+// values or rejected errors on the listener's owning thread and sending them to
+// the extension event listener caller running on the main thread.
+class ExtensionListenerCallPromiseResultHandler
+ : public dom::PromiseNativeHandler {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ static void Create(
+ const RefPtr<dom::Promise>& aPromise,
+ const RefPtr<ExtensionListenerCallWorkerRunnable>& aWorkerRunnable,
+ dom::ThreadSafeWorkerRef* aWorkerRef);
+
+ // PromiseNativeHandler
+ void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override;
+ void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override;
+
+ enum class PromiseCallbackType { Resolve, Reject };
+
+ private:
+ ExtensionListenerCallPromiseResultHandler(
+ dom::ThreadSafeWorkerRef* aWorkerRef,
+ RefPtr<ExtensionListenerCallWorkerRunnable> aWorkerRunnable)
+ : mWorkerRef(aWorkerRef), mWorkerRunnable(std::move(aWorkerRunnable)) {}
+
+ ~ExtensionListenerCallPromiseResultHandler() = default;
+
+ void WorkerRunCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ PromiseCallbackType aCallbackType);
+
+ // Set and accessed only on the owning worker thread.
+ RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef;
+
+ // Reference to the runnable created on and owned by the main thread,
+ // accessed on the worker thread and released on the owning thread.
+ RefPtr<ExtensionListenerCallWorkerRunnable> mWorkerRunnable;
+};
+
+} // namespace extensions
+} // namespace mozilla
+
+#endif // mozilla_extensions_ExtensionEventListener_h