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 "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 "nsMemory.h"
22 #include "nsServiceManagerUtils.h"
23 #include "nsArray.h"
24 #include "nsThreadUtils.h"
25
26 namespace mozilla {
27
28 using namespace dom;
29
30 /******************************************************************************
31 * mozilla::EventListenerChange
32 ******************************************************************************/
33
34 NS_IMPL_ISUPPORTS(EventListenerChange, nsIEventListenerChange)
35
36 EventListenerChange::~EventListenerChange() = default;
37
EventListenerChange(EventTarget * aTarget)38 EventListenerChange::EventListenerChange(EventTarget* aTarget)
39 : mTarget(aTarget) {}
40
AddChangedListenerName(nsAtom * aEventName)41 void EventListenerChange::AddChangedListenerName(nsAtom* aEventName) {
42 mChangedListenerNames.AppendElement(aEventName);
43 }
44
45 NS_IMETHODIMP
GetTarget(EventTarget ** aTarget)46 EventListenerChange::GetTarget(EventTarget** aTarget) {
47 NS_ENSURE_ARG_POINTER(aTarget);
48 NS_ADDREF(*aTarget = mTarget);
49 return NS_OK;
50 }
51
52 NS_IMETHODIMP
GetCountOfEventListenerChangesAffectingAccessibility(uint32_t * aCount)53 EventListenerChange::GetCountOfEventListenerChangesAffectingAccessibility(
54 uint32_t* aCount) {
55 *aCount = 0;
56
57 size_t length = mChangedListenerNames.Length();
58 for (size_t i = 0; i < length; i++) {
59 RefPtr<nsAtom> listenerName = mChangedListenerNames[i];
60
61 // These are the event listener changes which may make an element
62 // accessible or inaccessible.
63 if (listenerName == nsGkAtoms::onclick ||
64 listenerName == nsGkAtoms::onmousedown ||
65 listenerName == nsGkAtoms::onmouseup) {
66 *aCount += 1;
67 }
68 }
69
70 return NS_OK;
71 }
72
73 /******************************************************************************
74 * mozilla::EventListenerInfo
75 ******************************************************************************/
76
EventListenerInfo(EventListenerManager * aListenerManager,const nsAString & aType,JS::Handle<JSObject * > aScriptedListener,JS::Handle<JSObject * > aScriptedListenerGlobal,bool aCapturing,bool aAllowsUntrusted,bool aInSystemEventGroup,bool aIsHandler)77 EventListenerInfo::EventListenerInfo(
78 EventListenerManager* aListenerManager, const nsAString& aType,
79 JS::Handle<JSObject*> aScriptedListener,
80 JS::Handle<JSObject*> aScriptedListenerGlobal, bool aCapturing,
81 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler)
82 : mListenerManager(aListenerManager),
83 mType(aType),
84 mScriptedListener(aScriptedListener),
85 mScriptedListenerGlobal(aScriptedListenerGlobal),
86 mCapturing(aCapturing),
87 mAllowsUntrusted(aAllowsUntrusted),
88 mInSystemEventGroup(aInSystemEventGroup),
89 mIsHandler(aIsHandler) {
90 if (aScriptedListener) {
91 MOZ_ASSERT(JS_IsGlobalObject(aScriptedListenerGlobal));
92 js::AssertSameCompartment(aScriptedListener, aScriptedListenerGlobal);
93 }
94
95 HoldJSObjects(this);
96 }
97
~EventListenerInfo()98 EventListenerInfo::~EventListenerInfo() { DropJSObjects(this); }
99
100 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerInfo)
101
102 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerInfo)
103 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
104 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
105
106 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerInfo)
107 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
108 tmp->mScriptedListener = nullptr;
109 tmp->mScriptedListenerGlobal = nullptr;
110 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
111
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(EventListenerInfo)112 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(EventListenerInfo)
113 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptedListener)
114 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptedListenerGlobal)
115 NS_IMPL_CYCLE_COLLECTION_TRACE_END
116
117 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventListenerInfo)
118 NS_INTERFACE_MAP_ENTRY(nsIEventListenerInfo)
119 NS_INTERFACE_MAP_ENTRY(nsISupports)
120 NS_INTERFACE_MAP_END
121
122 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerInfo)
123 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerInfo)
124
125 NS_IMETHODIMP
126 EventListenerInfo::GetType(nsAString& aType) {
127 aType = mType;
128 return NS_OK;
129 }
130
131 NS_IMETHODIMP
GetCapturing(bool * aCapturing)132 EventListenerInfo::GetCapturing(bool* aCapturing) {
133 *aCapturing = mCapturing;
134 return NS_OK;
135 }
136
137 NS_IMETHODIMP
GetAllowsUntrusted(bool * aAllowsUntrusted)138 EventListenerInfo::GetAllowsUntrusted(bool* aAllowsUntrusted) {
139 *aAllowsUntrusted = mAllowsUntrusted;
140 return NS_OK;
141 }
142
143 NS_IMETHODIMP
GetInSystemEventGroup(bool * aInSystemEventGroup)144 EventListenerInfo::GetInSystemEventGroup(bool* aInSystemEventGroup) {
145 *aInSystemEventGroup = mInSystemEventGroup;
146 return NS_OK;
147 }
148
149 NS_IMETHODIMP
GetEnabled(bool * aEnabled)150 EventListenerInfo::GetEnabled(bool* aEnabled) {
151 NS_ENSURE_STATE(mListenerManager);
152 return mListenerManager->IsListenerEnabled(
153 mType, mScriptedListener, mCapturing, mAllowsUntrusted,
154 mInSystemEventGroup, mIsHandler, aEnabled);
155 }
156
157 NS_IMETHODIMP
SetEnabled(bool aEnabled)158 EventListenerInfo::SetEnabled(bool aEnabled) {
159 NS_ENSURE_STATE(mListenerManager);
160 return mListenerManager->SetListenerEnabled(
161 mType, mScriptedListener, mCapturing, mAllowsUntrusted,
162 mInSystemEventGroup, mIsHandler, aEnabled);
163 }
164
165 NS_IMETHODIMP
GetListenerObject(JSContext * aCx,JS::MutableHandle<JS::Value> aObject)166 EventListenerInfo::GetListenerObject(JSContext* aCx,
167 JS::MutableHandle<JS::Value> aObject) {
168 Maybe<JSAutoRealm> ar;
169 GetJSVal(aCx, ar, aObject);
170 return NS_OK;
171 }
172
173 /******************************************************************************
174 * mozilla::EventListenerService
175 ******************************************************************************/
176
NS_IMPL_ISUPPORTS(EventListenerService,nsIEventListenerService)177 NS_IMPL_ISUPPORTS(EventListenerService, nsIEventListenerService)
178
179 bool EventListenerInfo::GetJSVal(JSContext* aCx, Maybe<JSAutoRealm>& aAr,
180 JS::MutableHandle<JS::Value> aJSVal) {
181 if (mScriptedListener) {
182 aJSVal.setObject(*mScriptedListener);
183 aAr.emplace(aCx, mScriptedListenerGlobal);
184 return true;
185 }
186
187 aJSVal.setNull();
188 return false;
189 }
190
191 NS_IMETHODIMP
ToSource(nsAString & aResult)192 EventListenerInfo::ToSource(nsAString& aResult) {
193 aResult.SetIsVoid(true);
194
195 AutoSafeJSContext cx;
196 Maybe<JSAutoRealm> ar;
197 JS::Rooted<JS::Value> v(cx);
198 if (GetJSVal(cx, ar, &v)) {
199 JSString* str = JS_ValueToSource(cx, v);
200 if (str) {
201 nsAutoJSString autoStr;
202 if (autoStr.init(cx, str)) {
203 aResult.Assign(autoStr);
204 }
205 }
206 }
207 return NS_OK;
208 }
209
210 EventListenerService* EventListenerService::sInstance = nullptr;
211
EventListenerService()212 EventListenerService::EventListenerService() {
213 MOZ_ASSERT(!sInstance);
214 sInstance = this;
215 }
216
~EventListenerService()217 EventListenerService::~EventListenerService() {
218 MOZ_ASSERT(sInstance == this);
219 sInstance = nullptr;
220 }
221
222 NS_IMETHODIMP
GetListenerInfoFor(EventTarget * aEventTarget,nsTArray<RefPtr<nsIEventListenerInfo>> & aOutArray)223 EventListenerService::GetListenerInfoFor(
224 EventTarget* aEventTarget,
225 nsTArray<RefPtr<nsIEventListenerInfo>>& aOutArray) {
226 NS_ENSURE_ARG_POINTER(aEventTarget);
227
228 EventListenerManager* elm = aEventTarget->GetExistingListenerManager();
229 if (elm) {
230 elm->GetListenerInfo(aOutArray);
231 }
232
233 return NS_OK;
234 }
235
236 NS_IMETHODIMP
GetEventTargetChainFor(EventTarget * aEventTarget,bool aComposed,nsTArray<RefPtr<EventTarget>> & aOutArray)237 EventListenerService::GetEventTargetChainFor(
238 EventTarget* aEventTarget, bool aComposed,
239 nsTArray<RefPtr<EventTarget>>& aOutArray) {
240 NS_ENSURE_ARG(aEventTarget);
241 WidgetEvent event(true, eVoidEvent);
242 event.SetComposed(aComposed);
243 nsTArray<EventTarget*> targets;
244 nsresult rv = EventDispatcher::Dispatch(aEventTarget, nullptr, &event,
245 nullptr, nullptr, nullptr, &targets);
246 NS_ENSURE_SUCCESS(rv, rv);
247 aOutArray.AppendElements(targets);
248 return NS_OK;
249 }
250
251 NS_IMETHODIMP
HasListenersFor(EventTarget * aEventTarget,const nsAString & aType,bool * aRetVal)252 EventListenerService::HasListenersFor(EventTarget* aEventTarget,
253 const nsAString& aType, bool* aRetVal) {
254 NS_ENSURE_TRUE(aEventTarget, NS_ERROR_UNEXPECTED);
255
256 EventListenerManager* elm = aEventTarget->GetExistingListenerManager();
257 *aRetVal = elm && elm->HasListenersFor(aType);
258 return NS_OK;
259 }
260
ToEventListener(JSContext * aCx,JS::Handle<JS::Value> aValue)261 static already_AddRefed<EventListener> ToEventListener(
262 JSContext* aCx, JS::Handle<JS::Value> aValue) {
263 if (NS_WARN_IF(!aValue.isObject())) {
264 return nullptr;
265 }
266
267 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
268 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
269 RefPtr<EventListener> listener =
270 new EventListener(aCx, obj, global, GetIncumbentGlobal());
271 return listener.forget();
272 }
273
274 NS_IMETHODIMP
AddSystemEventListener(EventTarget * aTarget,const nsAString & aType,JS::Handle<JS::Value> aListener,bool aUseCapture,JSContext * aCx)275 EventListenerService::AddSystemEventListener(EventTarget* aTarget,
276 const nsAString& aType,
277 JS::Handle<JS::Value> aListener,
278 bool aUseCapture, JSContext* aCx) {
279 MOZ_ASSERT(aTarget, "Missing target");
280
281 NS_ENSURE_TRUE(aTarget, NS_ERROR_UNEXPECTED);
282
283 RefPtr<EventListener> listener = ToEventListener(aCx, aListener);
284 if (!listener) {
285 return NS_ERROR_UNEXPECTED;
286 }
287
288 EventListenerManager* manager = aTarget->GetOrCreateListenerManager();
289 NS_ENSURE_STATE(manager);
290
291 EventListenerFlags flags = aUseCapture ? TrustedEventsAtSystemGroupCapture()
292 : TrustedEventsAtSystemGroupBubble();
293 manager->AddEventListenerByType(listener, aType, flags);
294 return NS_OK;
295 }
296
297 NS_IMETHODIMP
RemoveSystemEventListener(EventTarget * aTarget,const nsAString & aType,JS::Handle<JS::Value> aListener,bool aUseCapture,JSContext * aCx)298 EventListenerService::RemoveSystemEventListener(EventTarget* aTarget,
299 const nsAString& aType,
300 JS::Handle<JS::Value> aListener,
301 bool aUseCapture,
302 JSContext* aCx) {
303 MOZ_ASSERT(aTarget, "Missing target");
304
305 NS_ENSURE_TRUE(aTarget, NS_ERROR_UNEXPECTED);
306
307 RefPtr<EventListener> listener = ToEventListener(aCx, aListener);
308 if (!listener) {
309 return NS_ERROR_UNEXPECTED;
310 }
311
312 EventListenerManager* manager = aTarget->GetExistingListenerManager();
313 if (manager) {
314 EventListenerFlags flags = aUseCapture ? TrustedEventsAtSystemGroupCapture()
315 : TrustedEventsAtSystemGroupBubble();
316 manager->RemoveEventListenerByType(listener, aType, flags);
317 }
318
319 return NS_OK;
320 }
321
322 NS_IMETHODIMP
AddListenerForAllEvents(EventTarget * aTarget,JS::Handle<JS::Value> aListener,bool aUseCapture,bool aWantsUntrusted,bool aSystemEventGroup,JSContext * aCx)323 EventListenerService::AddListenerForAllEvents(
324 EventTarget* aTarget, JS::Handle<JS::Value> aListener, bool aUseCapture,
325 bool aWantsUntrusted, bool aSystemEventGroup, JSContext* aCx) {
326 NS_ENSURE_STATE(aTarget);
327
328 RefPtr<EventListener> listener = ToEventListener(aCx, aListener);
329 if (!listener) {
330 return NS_ERROR_UNEXPECTED;
331 }
332
333 EventListenerManager* manager = aTarget->GetOrCreateListenerManager();
334 NS_ENSURE_STATE(manager);
335 manager->AddListenerForAllEvents(listener, aUseCapture, aWantsUntrusted,
336 aSystemEventGroup);
337 return NS_OK;
338 }
339
340 NS_IMETHODIMP
RemoveListenerForAllEvents(EventTarget * aTarget,JS::Handle<JS::Value> aListener,bool aUseCapture,bool aSystemEventGroup,JSContext * aCx)341 EventListenerService::RemoveListenerForAllEvents(
342 EventTarget* aTarget, JS::Handle<JS::Value> aListener, bool aUseCapture,
343 bool aSystemEventGroup, JSContext* aCx) {
344 NS_ENSURE_STATE(aTarget);
345
346 RefPtr<EventListener> listener = ToEventListener(aCx, aListener);
347 if (!listener) {
348 return NS_ERROR_UNEXPECTED;
349 }
350
351 EventListenerManager* manager = aTarget->GetExistingListenerManager();
352 if (manager) {
353 manager->RemoveListenerForAllEvents(listener, aUseCapture,
354 aSystemEventGroup);
355 }
356 return NS_OK;
357 }
358
359 NS_IMETHODIMP
AddListenerChangeListener(nsIListenerChangeListener * aListener)360 EventListenerService::AddListenerChangeListener(
361 nsIListenerChangeListener* aListener) {
362 if (!mChangeListeners.Contains(aListener)) {
363 mChangeListeners.AppendElement(aListener);
364 }
365 return NS_OK;
366 };
367
368 NS_IMETHODIMP
RemoveListenerChangeListener(nsIListenerChangeListener * aListener)369 EventListenerService::RemoveListenerChangeListener(
370 nsIListenerChangeListener* aListener) {
371 mChangeListeners.RemoveElement(aListener);
372 return NS_OK;
373 };
374
NotifyAboutMainThreadListenerChangeInternal(dom::EventTarget * aTarget,nsAtom * aName)375 void EventListenerService::NotifyAboutMainThreadListenerChangeInternal(
376 dom::EventTarget* aTarget, nsAtom* aName) {
377 MOZ_ASSERT(NS_IsMainThread());
378 MOZ_ASSERT(aTarget);
379 if (mChangeListeners.IsEmpty()) {
380 return;
381 }
382
383 if (!mPendingListenerChanges) {
384 mPendingListenerChanges = nsArrayBase::Create();
385 nsCOMPtr<nsIRunnable> runnable =
386 NewRunnableMethod("EventListenerService::NotifyPendingChanges", this,
387 &EventListenerService::NotifyPendingChanges);
388 if (nsCOMPtr<nsIGlobalObject> global = aTarget->GetOwnerGlobal()) {
389 global->Dispatch(TaskCategory::Other, runnable.forget());
390 } else if (nsINode* node = nsINode::FromEventTarget(aTarget)) {
391 node->OwnerDoc()->Dispatch(TaskCategory::Other, runnable.forget());
392 } else {
393 NS_DispatchToCurrentThread(runnable);
394 }
395 }
396
397 RefPtr<EventListenerChange> changes =
398 mPendingListenerChangesSet.LookupOrInsertWith(aTarget, [&] {
399 auto c = MakeRefPtr<EventListenerChange>(aTarget);
400 mPendingListenerChanges->AppendElement(c);
401 return c;
402 });
403 changes->AddChangedListenerName(aName);
404 }
405
NotifyPendingChanges()406 void EventListenerService::NotifyPendingChanges() {
407 nsCOMPtr<nsIMutableArray> changes;
408 mPendingListenerChanges.swap(changes);
409 mPendingListenerChangesSet.Clear();
410
411 for (nsCOMPtr<nsIListenerChangeListener> listener :
412 mChangeListeners.EndLimitedRange()) {
413 listener->ListenersChanged(changes);
414 }
415 }
416
417 } // namespace mozilla
418
NS_NewEventListenerService(nsIEventListenerService ** aResult)419 nsresult NS_NewEventListenerService(nsIEventListenerService** aResult) {
420 *aResult = new mozilla::EventListenerService();
421 NS_ADDREF(*aResult);
422 return NS_OK;
423 }
424