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