Bug 1941046 - Part 4: Send a callback request for impression and clicks of MARS Top...
[gecko.git] / dom / events / EventListenerService.cpp
blob3398f0dd0258fbe956c233041440410d076dc57a
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 "EventListenerService.h"
8 #include "mozilla/BasicEvents.h"
9 #include "mozilla/EventDispatcher.h"
10 #include "mozilla/EventListenerManager.h"
11 #include "mozilla/HoldDropJSObjects.h"
12 #include "mozilla/JSEventHandler.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/EventListenerBinding.h"
16 #include "mozilla/dom/ScriptSettings.h"
17 #include "nsArrayUtils.h"
18 #include "nsCOMArray.h"
19 #include "nsINode.h"
20 #include "nsJSUtils.h"
21 #include "nsServiceManagerUtils.h"
22 #include "nsArray.h"
23 #include "nsThreadUtils.h"
25 namespace mozilla {
27 using namespace dom;
29 /******************************************************************************
30 * mozilla::EventListenerChange
31 ******************************************************************************/
33 NS_IMPL_ISUPPORTS(EventListenerChange, nsIEventListenerChange)
35 EventListenerChange::~EventListenerChange() = default;
37 EventListenerChange::EventListenerChange(EventTarget* aTarget)
38 : mTarget(aTarget) {}
40 void EventListenerChange::AddChangedListenerName(nsAtom* aEventName) {
41 mChangedListenerNames.AppendElement(aEventName);
44 NS_IMETHODIMP
45 EventListenerChange::GetTarget(EventTarget** aTarget) {
46 NS_ENSURE_ARG_POINTER(aTarget);
47 NS_ADDREF(*aTarget = mTarget);
48 return NS_OK;
51 NS_IMETHODIMP
52 EventListenerChange::GetCountOfEventListenerChangesAffectingAccessibility(
53 uint32_t* aCount) {
54 *aCount = 0;
56 size_t length = mChangedListenerNames.Length();
57 for (size_t i = 0; i < length; i++) {
58 RefPtr<nsAtom> listenerName = mChangedListenerNames[i];
60 // These are the event listener changes which may make an element
61 // accessible or inaccessible.
62 if (listenerName == nsGkAtoms::onclick ||
63 listenerName == nsGkAtoms::onmousedown ||
64 listenerName == nsGkAtoms::onmouseup) {
65 *aCount += 1;
69 return NS_OK;
72 /******************************************************************************
73 * mozilla::EventListenerInfo
74 ******************************************************************************/
76 EventListenerInfo::EventListenerInfo(
77 EventListenerManager* aListenerManager, const nsAString& aType,
78 JS::Handle<JSObject*> aScriptedListener,
79 JS::Handle<JSObject*> aScriptedListenerGlobal, bool aCapturing,
80 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler)
81 : mListenerManager(aListenerManager),
82 mType(aType),
83 mScriptedListener(aScriptedListener),
84 mScriptedListenerGlobal(aScriptedListenerGlobal),
85 mCapturing(aCapturing),
86 mAllowsUntrusted(aAllowsUntrusted),
87 mInSystemEventGroup(aInSystemEventGroup),
88 mIsHandler(aIsHandler) {
89 if (aScriptedListener) {
90 MOZ_ASSERT(JS_IsGlobalObject(aScriptedListenerGlobal));
91 js::AssertSameCompartment(aScriptedListener, aScriptedListenerGlobal);
94 HoldJSObjects(this);
97 EventListenerInfo::~EventListenerInfo() { DropJSObjects(this); }
99 NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(EventListenerInfo, (mListenerManager),
100 (mScriptedListener,
101 mScriptedListenerGlobal))
103 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventListenerInfo)
104 NS_INTERFACE_MAP_ENTRY(nsIEventListenerInfo)
105 NS_INTERFACE_MAP_ENTRY(nsISupports)
106 NS_INTERFACE_MAP_END
108 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerInfo)
109 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerInfo)
111 NS_IMETHODIMP
112 EventListenerInfo::GetType(nsAString& aType) {
113 aType = mType;
114 return NS_OK;
117 NS_IMETHODIMP
118 EventListenerInfo::GetCapturing(bool* aCapturing) {
119 *aCapturing = mCapturing;
120 return NS_OK;
123 NS_IMETHODIMP
124 EventListenerInfo::GetAllowsUntrusted(bool* aAllowsUntrusted) {
125 *aAllowsUntrusted = mAllowsUntrusted;
126 return NS_OK;
129 NS_IMETHODIMP
130 EventListenerInfo::GetInSystemEventGroup(bool* aInSystemEventGroup) {
131 *aInSystemEventGroup = mInSystemEventGroup;
132 return NS_OK;
135 NS_IMETHODIMP
136 EventListenerInfo::GetEnabled(bool* aEnabled) {
137 NS_ENSURE_STATE(mListenerManager);
138 return mListenerManager->IsListenerEnabled(
139 mType, mScriptedListener, mCapturing, mAllowsUntrusted,
140 mInSystemEventGroup, mIsHandler, aEnabled);
143 NS_IMETHODIMP
144 EventListenerInfo::SetEnabled(bool aEnabled) {
145 NS_ENSURE_STATE(mListenerManager);
146 return mListenerManager->SetListenerEnabled(
147 mType, mScriptedListener, mCapturing, mAllowsUntrusted,
148 mInSystemEventGroup, mIsHandler, aEnabled);
151 NS_IMETHODIMP
152 EventListenerInfo::GetListenerObject(JSContext* aCx,
153 JS::MutableHandle<JS::Value> aObject) {
154 Maybe<JSAutoRealm> ar;
155 GetJSVal(aCx, ar, aObject);
156 return NS_OK;
159 /******************************************************************************
160 * mozilla::EventListenerService
161 ******************************************************************************/
163 NS_IMPL_ISUPPORTS(EventListenerService, nsIEventListenerService)
165 bool EventListenerInfo::GetJSVal(JSContext* aCx, Maybe<JSAutoRealm>& aAr,
166 JS::MutableHandle<JS::Value> aJSVal) {
167 if (mScriptedListener) {
168 aJSVal.setObject(*mScriptedListener);
169 aAr.emplace(aCx, mScriptedListenerGlobal);
170 return true;
173 aJSVal.setNull();
174 return false;
177 NS_IMETHODIMP
178 EventListenerInfo::ToSource(nsAString& aResult) {
179 aResult.SetIsVoid(true);
181 AutoSafeJSContext cx;
182 Maybe<JSAutoRealm> ar;
183 JS::Rooted<JS::Value> v(cx);
184 if (GetJSVal(cx, ar, &v)) {
185 JSString* str = JS_ValueToSource(cx, v);
186 if (str) {
187 nsAutoJSString autoStr;
188 if (autoStr.init(cx, str)) {
189 aResult.Assign(autoStr);
193 return NS_OK;
196 EventListenerService* EventListenerService::sInstance = nullptr;
198 EventListenerService::EventListenerService() {
199 MOZ_ASSERT(!sInstance);
200 sInstance = this;
203 EventListenerService::~EventListenerService() {
204 MOZ_ASSERT(sInstance == this);
205 sInstance = nullptr;
208 NS_IMETHODIMP
209 EventListenerService::GetListenerInfoFor(
210 EventTarget* aEventTarget,
211 nsTArray<RefPtr<nsIEventListenerInfo>>& aOutArray) {
212 NS_ENSURE_ARG_POINTER(aEventTarget);
214 EventListenerManager* elm = aEventTarget->GetExistingListenerManager();
215 if (elm) {
216 elm->GetListenerInfo(aOutArray);
219 return NS_OK;
222 NS_IMETHODIMP
223 EventListenerService::HasListenersFor(EventTarget* aEventTarget,
224 const nsAString& aType, bool* aRetVal) {
225 NS_ENSURE_TRUE(aEventTarget, NS_ERROR_UNEXPECTED);
227 EventListenerManager* elm = aEventTarget->GetExistingListenerManager();
228 *aRetVal = elm && elm->HasListenersFor(aType);
229 return NS_OK;
232 static already_AddRefed<EventListener> ToEventListener(
233 JSContext* aCx, JS::Handle<JS::Value> aValue) {
234 if (NS_WARN_IF(!aValue.isObject())) {
235 return nullptr;
238 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
239 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
240 RefPtr<EventListener> listener =
241 new EventListener(aCx, obj, global, GetIncumbentGlobal());
242 return listener.forget();
245 NS_IMETHODIMP
246 EventListenerService::AddListenerForAllEvents(
247 EventTarget* aTarget, JS::Handle<JS::Value> aListener, bool aUseCapture,
248 bool aWantsUntrusted, bool aSystemEventGroup, JSContext* aCx) {
249 NS_ENSURE_STATE(aTarget);
251 RefPtr<EventListener> listener = ToEventListener(aCx, aListener);
252 if (!listener) {
253 return NS_ERROR_UNEXPECTED;
256 EventListenerManager* manager = aTarget->GetOrCreateListenerManager();
257 NS_ENSURE_STATE(manager);
258 manager->AddListenerForAllEvents(listener, aUseCapture, aWantsUntrusted,
259 aSystemEventGroup);
260 return NS_OK;
263 NS_IMETHODIMP
264 EventListenerService::RemoveListenerForAllEvents(
265 EventTarget* aTarget, JS::Handle<JS::Value> aListener, bool aUseCapture,
266 bool aSystemEventGroup, JSContext* aCx) {
267 NS_ENSURE_STATE(aTarget);
269 RefPtr<EventListener> listener = ToEventListener(aCx, aListener);
270 if (!listener) {
271 return NS_ERROR_UNEXPECTED;
274 EventListenerManager* manager = aTarget->GetExistingListenerManager();
275 if (manager) {
276 manager->RemoveListenerForAllEvents(listener, aUseCapture,
277 aSystemEventGroup);
279 return NS_OK;
282 NS_IMETHODIMP
283 EventListenerService::AddListenerChangeListener(
284 nsIListenerChangeListener* aListener) {
285 if (!mChangeListeners.Contains(aListener)) {
286 mChangeListeners.AppendElement(aListener);
288 return NS_OK;
291 NS_IMETHODIMP
292 EventListenerService::RemoveListenerChangeListener(
293 nsIListenerChangeListener* aListener) {
294 mChangeListeners.RemoveElement(aListener);
295 return NS_OK;
298 void EventListenerService::NotifyAboutMainThreadListenerChangeInternal(
299 dom::EventTarget* aTarget, nsAtom* aName) {
300 MOZ_ASSERT(NS_IsMainThread());
301 MOZ_ASSERT(aTarget);
302 if (mChangeListeners.IsEmpty()) {
303 return;
306 if (!mPendingListenerChanges) {
307 mPendingListenerChanges = nsArrayBase::Create();
308 nsCOMPtr<nsIRunnable> runnable =
309 NewRunnableMethod("EventListenerService::NotifyPendingChanges", this,
310 &EventListenerService::NotifyPendingChanges);
311 NS_DispatchToCurrentThread(runnable.forget());
314 RefPtr<EventListenerChange> changes =
315 mPendingListenerChangesSet.LookupOrInsertWith(aTarget, [&] {
316 auto c = MakeRefPtr<EventListenerChange>(aTarget);
317 mPendingListenerChanges->AppendElement(c);
318 return c;
320 changes->AddChangedListenerName(aName);
323 void EventListenerService::NotifyPendingChanges() {
324 nsCOMPtr<nsIMutableArray> changes;
325 mPendingListenerChanges.swap(changes);
326 mPendingListenerChangesSet.Clear();
328 for (nsCOMPtr<nsIListenerChangeListener> listener :
329 mChangeListeners.EndLimitedRange()) {
330 listener->ListenersChanged(changes);
334 } // namespace mozilla
336 nsresult NS_NewEventListenerService(nsIEventListenerService** aResult) {
337 *aResult = new mozilla::EventListenerService();
338 NS_ADDREF(*aResult);
339 return NS_OK;