Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / debugger / DebuggerNotificationObserver.cpp
blob94fa37251af6eb0cc8e70e327d0070c9eab03656
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DebuggerNotificationObserver.h"
9 #include "DebuggerNotification.h"
10 #include "nsIGlobalObject.h"
11 #include "WrapperFactory.h"
13 namespace mozilla::dom {
15 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DebuggerNotificationObserver,
16 mOwnerGlobal, mEventListenerCallbacks)
18 NS_IMPL_CYCLE_COLLECTING_ADDREF(DebuggerNotificationObserver)
19 NS_IMPL_CYCLE_COLLECTING_RELEASE(DebuggerNotificationObserver)
21 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DebuggerNotificationObserver)
22 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
23 NS_INTERFACE_MAP_END
25 /* static */ already_AddRefed<DebuggerNotificationObserver>
26 DebuggerNotificationObserver::Constructor(GlobalObject& aGlobal,
27 ErrorResult& aRv) {
28 nsCOMPtr<nsIGlobalObject> globalInterface(
29 do_QueryInterface(aGlobal.GetAsSupports()));
30 if (NS_WARN_IF(!globalInterface)) {
31 aRv.Throw(NS_ERROR_FAILURE);
32 return nullptr;
35 RefPtr<DebuggerNotificationObserver> observer(
36 new DebuggerNotificationObserver(globalInterface));
37 return observer.forget();
40 DebuggerNotificationObserver::DebuggerNotificationObserver(
41 nsIGlobalObject* aOwnerGlobal)
42 : mOwnerGlobal(aOwnerGlobal) {}
44 JSObject* DebuggerNotificationObserver::WrapObject(
45 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
46 return DebuggerNotificationObserver_Binding::Wrap(aCx, this, aGivenProto);
49 static already_AddRefed<DebuggerNotificationManager> GetManager(
50 JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal) {
51 // The debuggee global here is likely a debugger-compartment cross-compartment
52 // wrapper for the debuggee global object, so we need to unwrap it to get
53 // the real debuggee-compartment global object.
54 JS::Rooted<JSObject*> debuggeeGlobalRooted(
55 aCx, js::UncheckedUnwrap(aDebuggeeGlobal, false));
57 if (!debuggeeGlobalRooted) {
58 return nullptr;
61 nsCOMPtr<nsIGlobalObject> debuggeeGlobalObject(
62 xpc::NativeGlobal(debuggeeGlobalRooted));
63 if (!debuggeeGlobalObject) {
64 return nullptr;
67 RefPtr<DebuggerNotificationManager> manager(
68 debuggeeGlobalObject->GetOrCreateDebuggerNotificationManager());
69 return manager.forget();
72 bool DebuggerNotificationObserver::Connect(
73 JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal, ErrorResult& aRv) {
74 RefPtr<DebuggerNotificationManager> manager(GetManager(aCx, aDebuggeeGlobal));
76 if (!manager) {
77 aRv.Throw(NS_ERROR_FAILURE);
78 return false;
81 return manager->Attach(this);
84 bool DebuggerNotificationObserver::Disconnect(
85 JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal, ErrorResult& aRv) {
86 RefPtr<DebuggerNotificationManager> manager(GetManager(aCx, aDebuggeeGlobal));
88 if (!manager) {
89 aRv.Throw(NS_ERROR_FAILURE);
90 return false;
93 return manager->Detach(this);
96 bool DebuggerNotificationObserver::AddListener(
97 DebuggerNotificationCallback& aHandlerFn) {
98 const auto [begin, end] = mEventListenerCallbacks.NonObservingRange();
99 if (std::any_of(begin, end,
100 [&](const RefPtr<DebuggerNotificationCallback>& callback) {
101 return *callback == aHandlerFn;
102 })) {
103 return false;
106 RefPtr<DebuggerNotificationCallback> handlerFn(&aHandlerFn);
107 mEventListenerCallbacks.AppendElement(handlerFn);
108 return true;
111 bool DebuggerNotificationObserver::RemoveListener(
112 DebuggerNotificationCallback& aHandlerFn) {
113 for (nsTObserverArray<RefPtr<DebuggerNotificationCallback>>::ForwardIterator
114 iter(mEventListenerCallbacks);
115 iter.HasMore();) {
116 if (*iter.GetNext().get() == aHandlerFn) {
117 iter.Remove();
118 return true;
122 return false;
125 bool DebuggerNotificationObserver::HasListeners() {
126 return !mEventListenerCallbacks.IsEmpty();
129 void DebuggerNotificationObserver::NotifyListeners(
130 DebuggerNotification* aNotification) {
131 if (!HasListeners()) {
132 return;
135 // Since we want the notification objects to live in the same compartment
136 // as the observer, we create a new instance of the notification before
137 // an observer dispatches the event listeners.
138 RefPtr<DebuggerNotification> debuggerNotification(
139 aNotification->CloneInto(mOwnerGlobal));
141 for (RefPtr<DebuggerNotificationCallback> callback :
142 mEventListenerCallbacks.ForwardRange()) {
143 callback->Call(*debuggerNotification);
147 } // namespace mozilla::dom