DebuggerNotificationObserver.cpp (4746B)
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/. */ 6 7 #include "DebuggerNotificationObserver.h" 8 9 #include "DebuggerNotification.h" 10 #include "WrapperFactory.h" 11 #include "nsIGlobalObject.h" 12 13 namespace mozilla::dom { 14 15 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DebuggerNotificationObserver, 16 mOwnerGlobal, mEventListenerCallbacks) 17 18 NS_IMPL_CYCLE_COLLECTING_ADDREF(DebuggerNotificationObserver) 19 NS_IMPL_CYCLE_COLLECTING_RELEASE(DebuggerNotificationObserver) 20 21 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DebuggerNotificationObserver) 22 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 23 NS_INTERFACE_MAP_END 24 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; 33 } 34 35 RefPtr<DebuggerNotificationObserver> observer( 36 new DebuggerNotificationObserver(globalInterface)); 37 return observer.forget(); 38 } 39 40 DebuggerNotificationObserver::DebuggerNotificationObserver( 41 nsIGlobalObject* aOwnerGlobal) 42 : mOwnerGlobal(aOwnerGlobal) {} 43 44 JSObject* DebuggerNotificationObserver::WrapObject( 45 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 46 return DebuggerNotificationObserver_Binding::Wrap(aCx, this, aGivenProto); 47 } 48 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)); 56 57 if (!debuggeeGlobalRooted) { 58 return nullptr; 59 } 60 61 nsCOMPtr<nsIGlobalObject> debuggeeGlobalObject( 62 xpc::NativeGlobal(debuggeeGlobalRooted)); 63 if (!debuggeeGlobalObject) { 64 return nullptr; 65 } 66 67 RefPtr<DebuggerNotificationManager> manager( 68 debuggeeGlobalObject->GetOrCreateDebuggerNotificationManager()); 69 return manager.forget(); 70 } 71 72 bool DebuggerNotificationObserver::Connect( 73 JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal, ErrorResult& aRv) { 74 RefPtr<DebuggerNotificationManager> manager(GetManager(aCx, aDebuggeeGlobal)); 75 76 if (!manager) { 77 aRv.Throw(NS_ERROR_FAILURE); 78 return false; 79 } 80 81 return manager->Attach(this); 82 } 83 84 bool DebuggerNotificationObserver::Disconnect( 85 JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal, ErrorResult& aRv) { 86 RefPtr<DebuggerNotificationManager> manager(GetManager(aCx, aDebuggeeGlobal)); 87 88 if (!manager) { 89 aRv.Throw(NS_ERROR_FAILURE); 90 return false; 91 } 92 93 return manager->Detach(this); 94 } 95 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; 104 } 105 106 RefPtr<DebuggerNotificationCallback> handlerFn(&aHandlerFn); 107 mEventListenerCallbacks.AppendElement(handlerFn); 108 return true; 109 } 110 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; 119 } 120 } 121 122 return false; 123 } 124 125 bool DebuggerNotificationObserver::HasListeners() { 126 return !mEventListenerCallbacks.IsEmpty(); 127 } 128 129 void DebuggerNotificationObserver::NotifyListeners( 130 DebuggerNotification* aNotification) { 131 if (!HasListeners()) { 132 return; 133 } 134 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)); 140 141 for (RefPtr<DebuggerNotificationCallback> callback : 142 mEventListenerCallbacks.ForwardRange()) { 143 callback->Call(*debuggerNotification); 144 } 145 } 146 147 } // namespace mozilla::dom