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::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
);
232 static already_AddRefed
<EventListener
> ToEventListener(
233 JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) {
234 if (NS_WARN_IF(!aValue
.isObject())) {
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();
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
);
253 return NS_ERROR_UNEXPECTED
;
256 EventListenerManager
* manager
= aTarget
->GetOrCreateListenerManager();
257 NS_ENSURE_STATE(manager
);
258 manager
->AddListenerForAllEvents(listener
, aUseCapture
, aWantsUntrusted
,
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
);
271 return NS_ERROR_UNEXPECTED
;
274 EventListenerManager
* manager
= aTarget
->GetExistingListenerManager();
276 manager
->RemoveListenerForAllEvents(listener
, aUseCapture
,
283 EventListenerService::AddListenerChangeListener(
284 nsIListenerChangeListener
* aListener
) {
285 if (!mChangeListeners
.Contains(aListener
)) {
286 mChangeListeners
.AppendElement(aListener
);
292 EventListenerService::RemoveListenerChangeListener(
293 nsIListenerChangeListener
* aListener
) {
294 mChangeListeners
.RemoveElement(aListener
);
298 void EventListenerService::NotifyAboutMainThreadListenerChangeInternal(
299 dom::EventTarget
* aTarget
, nsAtom
* aName
) {
300 MOZ_ASSERT(NS_IsMainThread());
302 if (mChangeListeners
.IsEmpty()) {
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
);
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();