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"
20 #include "nsJSUtils.h"
21 #include "nsServiceManagerUtils.h"
23 #include "nsThreadUtils.h"
29 /******************************************************************************
30 * mozilla::EventListenerChange
31 ******************************************************************************/
33 NS_IMPL_ISUPPORTS(EventListenerChange
, nsIEventListenerChange
)
35 EventListenerChange::~EventListenerChange() = default;
37 EventListenerChange::EventListenerChange(EventTarget
* aTarget
)
40 void EventListenerChange::AddChangedListenerName(nsAtom
* aEventName
) {
41 mChangedListenerNames
.AppendElement(aEventName
);
45 EventListenerChange::GetTarget(EventTarget
** aTarget
) {
46 NS_ENSURE_ARG_POINTER(aTarget
);
47 NS_ADDREF(*aTarget
= mTarget
);
52 EventListenerChange::GetCountOfEventListenerChangesAffectingAccessibility(
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
) {
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
),
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
);
97 EventListenerInfo::~EventListenerInfo() { DropJSObjects(this); }
99 NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(EventListenerInfo
, (mListenerManager
),
101 mScriptedListenerGlobal
))
103 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventListenerInfo
)
104 NS_INTERFACE_MAP_ENTRY(nsIEventListenerInfo
)
105 NS_INTERFACE_MAP_ENTRY(nsISupports
)
108 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerInfo
)
109 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerInfo
)
112 EventListenerInfo::GetType(nsAString
& aType
) {
118 EventListenerInfo::GetCapturing(bool* aCapturing
) {
119 *aCapturing
= mCapturing
;
124 EventListenerInfo::GetAllowsUntrusted(bool* aAllowsUntrusted
) {
125 *aAllowsUntrusted
= mAllowsUntrusted
;
130 EventListenerInfo::GetInSystemEventGroup(bool* aInSystemEventGroup
) {
131 *aInSystemEventGroup
= mInSystemEventGroup
;
136 EventListenerInfo::GetEnabled(bool* aEnabled
) {
137 NS_ENSURE_STATE(mListenerManager
);
138 return mListenerManager
->IsListenerEnabled(
139 mType
, mScriptedListener
, mCapturing
, mAllowsUntrusted
,
140 mInSystemEventGroup
, mIsHandler
, aEnabled
);
144 EventListenerInfo::SetEnabled(bool aEnabled
) {
145 NS_ENSURE_STATE(mListenerManager
);
146 return mListenerManager
->SetListenerEnabled(
147 mType
, mScriptedListener
, mCapturing
, mAllowsUntrusted
,
148 mInSystemEventGroup
, mIsHandler
, aEnabled
);
152 EventListenerInfo::GetListenerObject(JSContext
* aCx
,
153 JS::MutableHandle
<JS::Value
> aObject
) {
154 Maybe
<JSAutoRealm
> ar
;
155 GetJSVal(aCx
, ar
, aObject
);
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
);
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
);
187 nsAutoJSString autoStr
;
188 if (autoStr
.init(cx
, str
)) {
189 aResult
.Assign(autoStr
);
196 EventListenerService
* EventListenerService::sInstance
= nullptr;
198 EventListenerService::EventListenerService() {
199 MOZ_ASSERT(!sInstance
);
203 EventListenerService::~EventListenerService() {
204 MOZ_ASSERT(sInstance
== this);
209 EventListenerService::GetListenerInfoFor(
210 EventTarget
* aEventTarget
,
211 nsTArray
<RefPtr
<nsIEventListenerInfo
>>& aOutArray
) {
212 NS_ENSURE_ARG_POINTER(aEventTarget
);
214 EventListenerManager
* elm
= aEventTarget
->GetExistingListenerManager();
216 elm
->GetListenerInfo(aOutArray
);
223 EventListenerService::GetEventTargetChainFor(
224 EventTarget
* aEventTarget
, bool aComposed
,
225 nsTArray
<RefPtr
<EventTarget
>>& aOutArray
) {
226 NS_ENSURE_ARG(aEventTarget
);
227 WidgetEvent
event(true, eVoidEvent
);
228 event
.SetComposed(aComposed
);
229 nsTArray
<EventTarget
*> targets
;
230 nsresult rv
= EventDispatcher::Dispatch(aEventTarget
, nullptr, &event
,
231 nullptr, nullptr, nullptr, &targets
);
232 NS_ENSURE_SUCCESS(rv
, rv
);
233 aOutArray
.AppendElements(targets
);
238 EventListenerService::HasListenersFor(EventTarget
* aEventTarget
,
239 const nsAString
& aType
, bool* aRetVal
) {
240 NS_ENSURE_TRUE(aEventTarget
, NS_ERROR_UNEXPECTED
);
242 EventListenerManager
* elm
= aEventTarget
->GetExistingListenerManager();
243 *aRetVal
= elm
&& elm
->HasListenersFor(aType
);
247 static already_AddRefed
<EventListener
> ToEventListener(
248 JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) {
249 if (NS_WARN_IF(!aValue
.isObject())) {
253 JS::Rooted
<JSObject
*> obj(aCx
, &aValue
.toObject());
254 JS::Rooted
<JSObject
*> global(aCx
, JS::CurrentGlobalOrNull(aCx
));
255 RefPtr
<EventListener
> listener
=
256 new EventListener(aCx
, obj
, global
, GetIncumbentGlobal());
257 return listener
.forget();
261 EventListenerService::AddSystemEventListener(EventTarget
* aTarget
,
262 const nsAString
& aType
,
263 JS::Handle
<JS::Value
> aListener
,
264 bool aUseCapture
, JSContext
* aCx
) {
265 MOZ_ASSERT(aTarget
, "Missing target");
267 NS_ENSURE_TRUE(aTarget
, NS_ERROR_UNEXPECTED
);
269 RefPtr
<EventListener
> listener
= ToEventListener(aCx
, aListener
);
271 return NS_ERROR_UNEXPECTED
;
274 EventListenerManager
* manager
= aTarget
->GetOrCreateListenerManager();
275 NS_ENSURE_STATE(manager
);
277 EventListenerFlags flags
= aUseCapture
? TrustedEventsAtSystemGroupCapture()
278 : TrustedEventsAtSystemGroupBubble();
279 manager
->AddEventListenerByType(listener
, aType
, flags
);
284 EventListenerService::RemoveSystemEventListener(EventTarget
* aTarget
,
285 const nsAString
& aType
,
286 JS::Handle
<JS::Value
> aListener
,
289 MOZ_ASSERT(aTarget
, "Missing target");
291 NS_ENSURE_TRUE(aTarget
, NS_ERROR_UNEXPECTED
);
293 RefPtr
<EventListener
> listener
= ToEventListener(aCx
, aListener
);
295 return NS_ERROR_UNEXPECTED
;
298 EventListenerManager
* manager
= aTarget
->GetExistingListenerManager();
300 EventListenerFlags flags
= aUseCapture
? TrustedEventsAtSystemGroupCapture()
301 : TrustedEventsAtSystemGroupBubble();
302 manager
->RemoveEventListenerByType(listener
, aType
, flags
);
309 EventListenerService::AddListenerForAllEvents(
310 EventTarget
* aTarget
, JS::Handle
<JS::Value
> aListener
, bool aUseCapture
,
311 bool aWantsUntrusted
, bool aSystemEventGroup
, JSContext
* aCx
) {
312 NS_ENSURE_STATE(aTarget
);
314 RefPtr
<EventListener
> listener
= ToEventListener(aCx
, aListener
);
316 return NS_ERROR_UNEXPECTED
;
319 EventListenerManager
* manager
= aTarget
->GetOrCreateListenerManager();
320 NS_ENSURE_STATE(manager
);
321 manager
->AddListenerForAllEvents(listener
, aUseCapture
, aWantsUntrusted
,
327 EventListenerService::RemoveListenerForAllEvents(
328 EventTarget
* aTarget
, JS::Handle
<JS::Value
> aListener
, bool aUseCapture
,
329 bool aSystemEventGroup
, JSContext
* aCx
) {
330 NS_ENSURE_STATE(aTarget
);
332 RefPtr
<EventListener
> listener
= ToEventListener(aCx
, aListener
);
334 return NS_ERROR_UNEXPECTED
;
337 EventListenerManager
* manager
= aTarget
->GetExistingListenerManager();
339 manager
->RemoveListenerForAllEvents(listener
, aUseCapture
,
346 EventListenerService::AddListenerChangeListener(
347 nsIListenerChangeListener
* aListener
) {
348 if (!mChangeListeners
.Contains(aListener
)) {
349 mChangeListeners
.AppendElement(aListener
);
355 EventListenerService::RemoveListenerChangeListener(
356 nsIListenerChangeListener
* aListener
) {
357 mChangeListeners
.RemoveElement(aListener
);
361 void EventListenerService::NotifyAboutMainThreadListenerChangeInternal(
362 dom::EventTarget
* aTarget
, nsAtom
* aName
) {
363 MOZ_ASSERT(NS_IsMainThread());
365 if (mChangeListeners
.IsEmpty()) {
369 if (!mPendingListenerChanges
) {
370 mPendingListenerChanges
= nsArrayBase::Create();
371 nsCOMPtr
<nsIRunnable
> runnable
=
372 NewRunnableMethod("EventListenerService::NotifyPendingChanges", this,
373 &EventListenerService::NotifyPendingChanges
);
374 if (nsCOMPtr
<nsIGlobalObject
> global
= aTarget
->GetOwnerGlobal()) {
375 global
->Dispatch(TaskCategory::Other
, runnable
.forget());
376 } else if (nsINode
* node
= nsINode::FromEventTarget(aTarget
)) {
377 node
->OwnerDoc()->Dispatch(TaskCategory::Other
, runnable
.forget());
379 NS_DispatchToCurrentThread(runnable
);
383 RefPtr
<EventListenerChange
> changes
=
384 mPendingListenerChangesSet
.LookupOrInsertWith(aTarget
, [&] {
385 auto c
= MakeRefPtr
<EventListenerChange
>(aTarget
);
386 mPendingListenerChanges
->AppendElement(c
);
389 changes
->AddChangedListenerName(aName
);
392 void EventListenerService::NotifyPendingChanges() {
393 nsCOMPtr
<nsIMutableArray
> changes
;
394 mPendingListenerChanges
.swap(changes
);
395 mPendingListenerChangesSet
.Clear();
397 for (nsCOMPtr
<nsIListenerChangeListener
> listener
:
398 mChangeListeners
.EndLimitedRange()) {
399 listener
->ListenersChanged(changes
);
403 } // namespace mozilla
405 nsresult
NS_NewEventListenerService(nsIEventListenerService
** aResult
) {
406 *aResult
= new mozilla::EventListenerService();