/* -*- 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 "DebuggerNotificationObserver.h" #include "DebuggerNotification.h" #include "nsIGlobalObject.h" #include "WrapperFactory.h" namespace mozilla::dom { NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DebuggerNotificationObserver, mOwnerGlobal, mEventListenerCallbacks) NS_IMPL_CYCLE_COLLECTING_ADDREF(DebuggerNotificationObserver) NS_IMPL_CYCLE_COLLECTING_RELEASE(DebuggerNotificationObserver) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DebuggerNotificationObserver) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_END /* static */ already_AddRefed DebuggerNotificationObserver::Constructor(GlobalObject& aGlobal, ErrorResult& aRv) { nsCOMPtr globalInterface( do_QueryInterface(aGlobal.GetAsSupports())); if (NS_WARN_IF(!globalInterface)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } RefPtr observer( new DebuggerNotificationObserver(globalInterface)); return observer.forget(); } DebuggerNotificationObserver::DebuggerNotificationObserver( nsIGlobalObject* aOwnerGlobal) : mEventListenerCallbacks(), mOwnerGlobal(aOwnerGlobal) {} JSObject* DebuggerNotificationObserver::WrapObject( JSContext* aCx, JS::Handle aGivenProto) { return DebuggerNotificationObserver_Binding::Wrap(aCx, this, aGivenProto); } static already_AddRefed GetManager( JSContext* aCx, JS::Handle aDebuggeeGlobal) { // The debuggee global here is likely a debugger-compartment cross-compartment // wrapper for the debuggee global object, so we need to unwrap it to get // the real debuggee-compartment global object. JS::Rooted debuggeeGlobalRooted( aCx, js::UncheckedUnwrap(aDebuggeeGlobal, false)); if (!debuggeeGlobalRooted) { return nullptr; } nsCOMPtr debuggeeGlobalObject( xpc::NativeGlobal(debuggeeGlobalRooted)); if (!debuggeeGlobalObject) { return nullptr; } RefPtr manager( debuggeeGlobalObject->GetOrCreateDebuggerNotificationManager()); return manager.forget(); } bool DebuggerNotificationObserver::Connect( JSContext* aCx, JS::Handle aDebuggeeGlobal, ErrorResult& aRv) { RefPtr manager(GetManager(aCx, aDebuggeeGlobal)); if (!manager) { aRv.Throw(NS_ERROR_FAILURE); return false; } return manager->Attach(this); } bool DebuggerNotificationObserver::Disconnect( JSContext* aCx, JS::Handle aDebuggeeGlobal, ErrorResult& aRv) { RefPtr manager(GetManager(aCx, aDebuggeeGlobal)); if (!manager) { aRv.Throw(NS_ERROR_FAILURE); return false; } return manager->Detach(this); } bool DebuggerNotificationObserver::AddListener( DebuggerNotificationCallback& aHandlerFn) { const auto [begin, end] = mEventListenerCallbacks.NonObservingRange(); if (std::any_of(begin, end, [&](const RefPtr& callback) { return *callback == aHandlerFn; })) { return false; } RefPtr handlerFn(&aHandlerFn); mEventListenerCallbacks.AppendElement(handlerFn); return true; } bool DebuggerNotificationObserver::RemoveListener( DebuggerNotificationCallback& aHandlerFn) { for (nsTObserverArray>::ForwardIterator iter(mEventListenerCallbacks); iter.HasMore();) { if (*iter.GetNext().get() == aHandlerFn) { iter.Remove(); return true; } } return false; } bool DebuggerNotificationObserver::HasListeners() { return !mEventListenerCallbacks.IsEmpty(); } void DebuggerNotificationObserver::NotifyListeners( DebuggerNotification* aNotification) { if (!HasListeners()) { return; } // Since we want the notification objects to live in the same compartment // as the observer, we create a new instance of the notification before // an observer dispatches the event listeners. RefPtr debuggerNotification( aNotification->CloneInto(mOwnerGlobal)); for (RefPtr callback : mEventListenerCallbacks.ForwardRange()) { callback->Call(*debuggerNotification); } } } // namespace mozilla::dom