Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / events / EventListenerService.cpp
blob4336476aa9599b1f63bb186a1d943b403eafc3c5
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::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);
234 return NS_OK;
237 NS_IMETHODIMP
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);
244 return NS_OK;
247 static already_AddRefed<EventListener> ToEventListener(
248 JSContext* aCx, JS::Handle<JS::Value> aValue) {
249 if (NS_WARN_IF(!aValue.isObject())) {
250 return nullptr;
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();
260 NS_IMETHODIMP
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);
270 if (!listener) {
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);
280 return NS_OK;
283 NS_IMETHODIMP
284 EventListenerService::RemoveSystemEventListener(EventTarget* aTarget,
285 const nsAString& aType,
286 JS::Handle<JS::Value> aListener,
287 bool aUseCapture,
288 JSContext* aCx) {
289 MOZ_ASSERT(aTarget, "Missing target");
291 NS_ENSURE_TRUE(aTarget, NS_ERROR_UNEXPECTED);
293 RefPtr<EventListener> listener = ToEventListener(aCx, aListener);
294 if (!listener) {
295 return NS_ERROR_UNEXPECTED;
298 EventListenerManager* manager = aTarget->GetExistingListenerManager();
299 if (manager) {
300 EventListenerFlags flags = aUseCapture ? TrustedEventsAtSystemGroupCapture()
301 : TrustedEventsAtSystemGroupBubble();
302 manager->RemoveEventListenerByType(listener, aType, flags);
305 return NS_OK;
308 NS_IMETHODIMP
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);
315 if (!listener) {
316 return NS_ERROR_UNEXPECTED;
319 EventListenerManager* manager = aTarget->GetOrCreateListenerManager();
320 NS_ENSURE_STATE(manager);
321 manager->AddListenerForAllEvents(listener, aUseCapture, aWantsUntrusted,
322 aSystemEventGroup);
323 return NS_OK;
326 NS_IMETHODIMP
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);
333 if (!listener) {
334 return NS_ERROR_UNEXPECTED;
337 EventListenerManager* manager = aTarget->GetExistingListenerManager();
338 if (manager) {
339 manager->RemoveListenerForAllEvents(listener, aUseCapture,
340 aSystemEventGroup);
342 return NS_OK;
345 NS_IMETHODIMP
346 EventListenerService::AddListenerChangeListener(
347 nsIListenerChangeListener* aListener) {
348 if (!mChangeListeners.Contains(aListener)) {
349 mChangeListeners.AppendElement(aListener);
351 return NS_OK;
354 NS_IMETHODIMP
355 EventListenerService::RemoveListenerChangeListener(
356 nsIListenerChangeListener* aListener) {
357 mChangeListeners.RemoveElement(aListener);
358 return NS_OK;
361 void EventListenerService::NotifyAboutMainThreadListenerChangeInternal(
362 dom::EventTarget* aTarget, nsAtom* aName) {
363 MOZ_ASSERT(NS_IsMainThread());
364 MOZ_ASSERT(aTarget);
365 if (mChangeListeners.IsEmpty()) {
366 return;
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());
378 } else {
379 NS_DispatchToCurrentThread(runnable);
383 RefPtr<EventListenerChange> changes =
384 mPendingListenerChangesSet.LookupOrInsertWith(aTarget, [&] {
385 auto c = MakeRefPtr<EventListenerChange>(aTarget);
386 mPendingListenerChanges->AppendElement(c);
387 return 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();
407 NS_ADDREF(*aResult);
408 return NS_OK;