diff options
Diffstat (limited to 'toolkit/components/extensions/webidl-api/ExtensionEventManager.cpp')
-rw-r--r-- | toolkit/components/extensions/webidl-api/ExtensionEventManager.cpp | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/toolkit/components/extensions/webidl-api/ExtensionEventManager.cpp b/toolkit/components/extensions/webidl-api/ExtensionEventManager.cpp new file mode 100644 index 0000000000..2a5f9e0978 --- /dev/null +++ b/toolkit/components/extensions/webidl-api/ExtensionEventManager.cpp @@ -0,0 +1,168 @@ +/* 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 "ExtensionEventManager.h" + +#include "ExtensionAPIAddRemoveListener.h" + +#include "mozilla/dom/ExtensionEventManagerBinding.h" +#include "nsIGlobalObject.h" +#include "ExtensionEventListener.h" + +namespace mozilla { +namespace extensions { + +NS_IMPL_CYCLE_COLLECTION_CLASS(ExtensionEventManager); +NS_IMPL_CYCLE_COLLECTING_ADDREF(ExtensionEventManager); +NS_IMPL_CYCLE_COLLECTING_RELEASE(ExtensionEventManager) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ExtensionEventManager) + tmp->mListeners.clear(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionBrowser) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ExtensionEventManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionBrowser) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ExtensionEventManager) + for (auto iter = tmp->mListeners.iter(); !iter.done(); iter.next()) { + aCallbacks.Trace(&iter.get().mutableKey(), "mListeners key", aClosure); + } + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtensionEventManager) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +ExtensionEventManager::ExtensionEventManager( + nsIGlobalObject* aGlobal, ExtensionBrowser* aExtensionBrowser, + const nsAString& aNamespace, const nsAString& aEventName, + const nsAString& aObjectType, const nsAString& aObjectId) + : mGlobal(aGlobal), + mExtensionBrowser(aExtensionBrowser), + mAPINamespace(aNamespace), + mEventName(aEventName), + mAPIObjectType(aObjectType), + mAPIObjectId(aObjectId) { + MOZ_DIAGNOSTIC_ASSERT(mGlobal); + MOZ_DIAGNOSTIC_ASSERT(mExtensionBrowser); + + RefPtr<ExtensionEventManager> self = this; + mozilla::HoldJSObjects(this); +} + +ExtensionEventManager::~ExtensionEventManager() { + ReleaseListeners(); + mozilla::DropJSObjects(this); +}; + +void ExtensionEventManager::ReleaseListeners() { + if (mListeners.empty()) { + return; + } + + for (auto iter = mListeners.iter(); !iter.done(); iter.next()) { + iter.get().value()->Cleanup(); + } + + mListeners.clear(); +} + +JSObject* ExtensionEventManager::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return dom::ExtensionEventManager_Binding::Wrap(aCx, this, aGivenProto); +} + +nsIGlobalObject* ExtensionEventManager::GetParentObject() const { + return mGlobal; +} + +void ExtensionEventManager::AddListener( + JSContext* aCx, dom::Function& aCallback, + const dom::Optional<JS::Handle<JSObject*>>& aOptions, ErrorResult& aRv) { + JS::Rooted<JSObject*> cb(aCx, aCallback.CallbackOrNull()); + if (cb == nullptr) { + ThrowUnexpectedError(aCx, aRv); + return; + } + + RefPtr<ExtensionEventManager> self = this; + + IgnoredErrorResult rv; + RefPtr<ExtensionEventListener> wrappedCb = ExtensionEventListener::Create( + mGlobal, mExtensionBrowser, &aCallback, + [self = std::move(self)]() { self->ReleaseListeners(); }, rv); + + if (NS_WARN_IF(rv.Failed())) { + ThrowUnexpectedError(aCx, aRv); + return; + } + + RefPtr<ExtensionEventListener> storedWrapper = wrappedCb; + if (!mListeners.put(cb, std::move(storedWrapper))) { + ThrowUnexpectedError(aCx, aRv); + return; + } + + auto request = SendAddListener(mEventName); + request->Run(mGlobal, aCx, {}, wrappedCb, aRv); + + if (!aRv.Failed() && mAPIObjectType.IsEmpty()) { + mExtensionBrowser->TrackWakeupEventListener(aCx, mAPINamespace, mEventName); + } +} + +void ExtensionEventManager::RemoveListener(dom::Function& aCallback, + ErrorResult& aRv) { + dom::AutoJSAPI jsapi; + if (NS_WARN_IF(!jsapi.Init(mGlobal))) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + JSContext* cx = jsapi.cx(); + JS::Rooted<JSObject*> cb(cx, aCallback.CallbackOrNull()); + const auto& ptr = mListeners.lookup(cb); + + // Return earlier if the listener wasn't found + if (!ptr) { + return; + } + + RefPtr<ExtensionEventListener> wrappedCb = ptr->value(); + auto request = SendRemoveListener(mEventName); + request->Run(mGlobal, cx, {}, wrappedCb, aRv); + + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + if (mAPIObjectType.IsEmpty()) { + mExtensionBrowser->UntrackWakeupEventListener(cx, mAPINamespace, + mEventName); + } + + mListeners.remove(cb); + + wrappedCb->Cleanup(); +} + +bool ExtensionEventManager::HasListener(dom::Function& aCallback, + ErrorResult& aRv) const { + return mListeners.has(aCallback.CallbackOrNull()); +} + +bool ExtensionEventManager::HasListeners(ErrorResult& aRv) const { + return !mListeners.empty(); +} + +} // namespace extensions +} // namespace mozilla |