Bug 1572460 - Refactor `selection` out of the `InspectorFront`. r=yulia
[gecko.git] / dom / events / EventListenerManager.cpp
bloba1900824c9b40c5507b421639b5543255b902ef9
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 // Microsoft's API Name hackery sucks
8 #undef CreateEvent
10 #include "mozilla/BasicEvents.h"
11 #include "mozilla/CycleCollectedJSRuntime.h"
12 #include "mozilla/DOMEventTargetHelper.h"
13 #include "mozilla/EventDispatcher.h"
14 #include "mozilla/EventListenerManager.h"
15 #include "mozilla/HalSensor.h"
16 #include "mozilla/InternalMutationEvent.h"
17 #include "mozilla/JSEventHandler.h"
18 #include "mozilla/Maybe.h"
19 #include "mozilla/MemoryReporting.h"
20 #include "mozilla/Preferences.h"
21 #include "mozilla/PresShell.h"
22 #include "mozilla/StaticPrefs_full_screen_api.h"
23 #include "mozilla/dom/BindingUtils.h"
24 #include "mozilla/dom/EventCallbackDebuggerNotification.h"
25 #include "mozilla/dom/Element.h"
26 #include "mozilla/dom/Event.h"
27 #include "mozilla/dom/EventTargetBinding.h"
28 #include "mozilla/dom/LoadedScript.h"
29 #include "mozilla/dom/PopupBlocker.h"
30 #include "mozilla/dom/ScriptLoader.h"
31 #include "mozilla/dom/ScriptSettings.h"
32 #include "mozilla/dom/TouchEvent.h"
33 #include "mozilla/TimelineConsumers.h"
34 #include "mozilla/EventTimelineMarker.h"
35 #include "mozilla/TimeStamp.h"
37 #include "EventListenerService.h"
38 #include "nsCOMPtr.h"
39 #include "nsContentUtils.h"
40 #include "nsDOMCID.h"
41 #include "nsError.h"
42 #include "nsGkAtoms.h"
43 #include "nsIContent.h"
44 #include "nsIContentSecurityPolicy.h"
45 #include "mozilla/dom/Document.h"
46 #include "nsIDOMEventListener.h"
47 #include "nsIScriptGlobalObject.h"
48 #include "nsISupports.h"
49 #include "nsISupportsPrimitives.h"
50 #include "nsIXPConnect.h"
51 #include "nsJSUtils.h"
52 #include "nsNameSpaceManager.h"
53 #include "nsPIDOMWindow.h"
54 #include "nsSandboxFlags.h"
55 #include "xpcpublic.h"
56 #include "nsIFrame.h"
57 #include "nsDisplayList.h"
59 namespace mozilla {
61 using namespace dom;
62 using namespace hal;
64 #define EVENT_TYPE_EQUALS(ls, message, userType, allEvents) \
65 ((ls->mEventMessage == message && \
66 (ls->mEventMessage != eUnidentifiedEvent || ls->mTypeAtom == userType)) || \
67 (allEvents && ls->mAllEvents))
69 static const uint32_t kAllMutationBits =
70 NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED |
71 NS_EVENT_BITS_MUTATION_NODEINSERTED | NS_EVENT_BITS_MUTATION_NODEREMOVED |
72 NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT |
73 NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT |
74 NS_EVENT_BITS_MUTATION_ATTRMODIFIED |
75 NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
77 static uint32_t MutationBitForEventType(EventMessage aEventType) {
78 switch (aEventType) {
79 case eLegacySubtreeModified:
80 return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED;
81 case eLegacyNodeInserted:
82 return NS_EVENT_BITS_MUTATION_NODEINSERTED;
83 case eLegacyNodeRemoved:
84 return NS_EVENT_BITS_MUTATION_NODEREMOVED;
85 case eLegacyNodeRemovedFromDocument:
86 return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT;
87 case eLegacyNodeInsertedIntoDocument:
88 return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT;
89 case eLegacyAttrModified:
90 return NS_EVENT_BITS_MUTATION_ATTRMODIFIED;
91 case eLegacyCharacterDataModified:
92 return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
93 default:
94 break;
96 return 0;
99 uint32_t EventListenerManager::sMainThreadCreatedCount = 0;
101 EventListenerManagerBase::EventListenerManagerBase()
102 : mNoListenerForEvent(eVoidEvent),
103 mMayHavePaintEventListener(false),
104 mMayHaveMutationListeners(false),
105 mMayHaveCapturingListeners(false),
106 mMayHaveSystemGroupListeners(false),
107 mMayHaveTouchEventListener(false),
108 mMayHaveMouseEnterLeaveEventListener(false),
109 mMayHavePointerEnterLeaveEventListener(false),
110 mMayHaveKeyEventListener(false),
111 mMayHaveInputOrCompositionEventListener(false),
112 mMayHaveSelectionChangeEventListener(false),
113 mClearingListeners(false),
114 mIsMainThreadELM(NS_IsMainThread()) {
115 static_assert(sizeof(EventListenerManagerBase) == sizeof(uint32_t),
116 "Keep the size of EventListenerManagerBase size compact!");
119 EventListenerManager::EventListenerManager(EventTarget* aTarget)
120 : EventListenerManagerBase(), mTarget(aTarget) {
121 NS_ASSERTION(aTarget, "unexpected null pointer");
123 if (mIsMainThreadELM) {
124 ++sMainThreadCreatedCount;
128 EventListenerManager::~EventListenerManager() {
129 // If your code fails this assertion, a possible reason is that
130 // a class did not call our Disconnect() manually. Note that
131 // this class can have Disconnect called in one of two ways:
132 // if it is part of a cycle, then in Unlink() (such a cycle
133 // would be with one of the listeners, not mTarget which is weak).
134 // If not part of a cycle, then Disconnect must be called manually,
135 // typically from the destructor of the owner class (mTarget).
136 // XXX azakai: Is there any reason to not just call Disconnect
137 // from right here, if not previously called?
138 NS_ASSERTION(!mTarget, "didn't call Disconnect");
139 RemoveAllListenersSilently();
142 void EventListenerManager::RemoveAllListenersSilently() {
143 if (mClearingListeners) {
144 return;
146 mClearingListeners = true;
147 mListeners.Clear();
148 mClearingListeners = false;
151 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(EventListenerManager, AddRef)
152 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(EventListenerManager, Release)
154 inline void ImplCycleCollectionTraverse(
155 nsCycleCollectionTraversalCallback& aCallback,
156 EventListenerManager::Listener& aField, const char* aName,
157 unsigned aFlags) {
158 if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
159 nsAutoCString name;
160 name.AppendASCII(aName);
161 if (aField.mTypeAtom) {
162 name.AppendLiteral(" event=");
163 name.Append(nsAtomCString(aField.mTypeAtom));
164 name.AppendLiteral(" listenerType=");
165 name.AppendInt(aField.mListenerType);
166 name.AppendLiteral(" ");
168 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(),
169 name.get(), aFlags);
170 } else {
171 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName,
172 aFlags);
176 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager)
178 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager)
179 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
180 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
182 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager)
183 tmp->Disconnect();
184 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
186 nsPIDOMWindowInner* EventListenerManager::GetInnerWindowForTarget() {
187 nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
188 if (node) {
189 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
190 // if that's the XBL document?
191 return node->OwnerDoc()->GetInnerWindow();
194 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
195 return window;
198 already_AddRefed<nsPIDOMWindowInner>
199 EventListenerManager::GetTargetAsInnerWindow() const {
200 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mTarget);
201 return window.forget();
204 void EventListenerManager::AddEventListenerInternal(
205 EventListenerHolder aListenerHolder, EventMessage aEventMessage,
206 nsAtom* aTypeAtom, const EventListenerFlags& aFlags, bool aHandler,
207 bool aAllEvents) {
208 MOZ_ASSERT((aEventMessage && aTypeAtom) || aAllEvents, // all-events listener
209 "Missing type");
211 if (!aListenerHolder || mClearingListeners) {
212 return;
215 // Since there is no public API to call us with an EventListenerHolder, we
216 // know that there's an EventListenerHolder on the stack holding a strong ref
217 // to the listener.
219 Listener* listener;
220 uint32_t count = mListeners.Length();
221 for (uint32_t i = 0; i < count; i++) {
222 listener = &mListeners.ElementAt(i);
223 // mListener == aListenerHolder is the last one, since it can be a bit slow.
224 if (listener->mListenerIsHandler == aHandler &&
225 listener->mFlags.EqualsForAddition(aFlags) &&
226 EVENT_TYPE_EQUALS(listener, aEventMessage, aTypeAtom, aAllEvents) &&
227 listener->mListener == aListenerHolder) {
228 return;
232 mNoListenerForEvent = eVoidEvent;
233 mNoListenerForEventAtom = nullptr;
235 listener =
236 aAllEvents ? mListeners.InsertElementAt(0) : mListeners.AppendElement();
237 listener->mEventMessage = aEventMessage;
238 listener->mTypeAtom = aTypeAtom;
239 listener->mFlags = aFlags;
240 listener->mListenerIsHandler = aHandler;
241 listener->mHandlerIsString = false;
242 listener->mAllEvents = aAllEvents;
243 listener->mIsChrome =
244 mIsMainThreadELM && nsContentUtils::LegacyIsCallerChromeOrNativeCode();
246 // Detect the type of event listener.
247 if (aFlags.mListenerIsJSListener) {
248 MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback());
249 listener->mListenerType = Listener::eJSEventListener;
250 } else if (aListenerHolder.HasWebIDLCallback()) {
251 listener->mListenerType = Listener::eWebIDLListener;
252 } else {
253 listener->mListenerType = Listener::eNativeListener;
255 listener->mListener = std::move(aListenerHolder);
257 if (aFlags.mInSystemGroup) {
258 mMayHaveSystemGroupListeners = true;
260 if (aFlags.mCapture) {
261 mMayHaveCapturingListeners = true;
264 if (aEventMessage == eAfterPaint) {
265 mMayHavePaintEventListener = true;
266 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
267 window->SetHasPaintEventListeners();
269 } else if (aEventMessage >= eLegacyMutationEventFirst &&
270 aEventMessage <= eLegacyMutationEventLast) {
271 // For mutation listeners, we need to update the global bit on the DOM
272 // window. Otherwise we won't actually fire the mutation event.
273 mMayHaveMutationListeners = true;
274 // Go from our target to the nearest enclosing DOM window.
275 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
276 nsCOMPtr<Document> doc = window->GetExtantDoc();
277 if (doc) {
278 doc->WarnOnceAbout(Document::eMutationEvent);
280 // If aEventMessage is eLegacySubtreeModified, we need to listen all
281 // mutations. nsContentUtils::HasMutationListeners relies on this.
282 window->SetMutationListeners(
283 (aEventMessage == eLegacySubtreeModified)
284 ? kAllMutationBits
285 : MutationBitForEventType(aEventMessage));
287 } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
288 EnableDevice(eDeviceOrientation);
289 } else if (aTypeAtom == nsGkAtoms::onabsolutedeviceorientation) {
290 EnableDevice(eAbsoluteDeviceOrientation);
291 } else if (aTypeAtom == nsGkAtoms::ondeviceproximity ||
292 aTypeAtom == nsGkAtoms::onuserproximity) {
293 EnableDevice(eDeviceProximity);
294 } else if (aTypeAtom == nsGkAtoms::ondevicelight) {
295 EnableDevice(eDeviceLight);
296 } else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
297 EnableDevice(eDeviceMotion);
298 #if defined(MOZ_WIDGET_ANDROID)
299 } else if (aTypeAtom == nsGkAtoms::onorientationchange) {
300 EnableDevice(eOrientationChange);
301 #endif
302 } else if (aTypeAtom == nsGkAtoms::ontouchstart ||
303 aTypeAtom == nsGkAtoms::ontouchend ||
304 aTypeAtom == nsGkAtoms::ontouchmove ||
305 aTypeAtom == nsGkAtoms::ontouchcancel) {
306 mMayHaveTouchEventListener = true;
307 nsPIDOMWindowInner* window = GetInnerWindowForTarget();
308 // we don't want touchevent listeners added by scrollbars to flip this flag
309 // so we ignore listeners created with system event flag
310 if (window && !aFlags.mInSystemGroup) {
311 window->SetHasTouchEventListeners();
313 } else if (aEventMessage >= ePointerEventFirst &&
314 aEventMessage <= ePointerEventLast) {
315 nsPIDOMWindowInner* window = GetInnerWindowForTarget();
316 if (aTypeAtom == nsGkAtoms::onpointerenter ||
317 aTypeAtom == nsGkAtoms::onpointerleave) {
318 mMayHavePointerEnterLeaveEventListener = true;
319 if (window) {
320 #ifdef DEBUG
321 nsCOMPtr<Document> d = window->GetExtantDoc();
322 NS_WARNING_ASSERTION(
323 !nsContentUtils::IsChromeDoc(d),
324 "Please do not use pointerenter/leave events in chrome. "
325 "They are slower than pointerover/out!");
326 #endif
327 window->SetHasPointerEnterLeaveEventListeners();
330 } else if (aTypeAtom == nsGkAtoms::onmouseenter ||
331 aTypeAtom == nsGkAtoms::onmouseleave) {
332 mMayHaveMouseEnterLeaveEventListener = true;
333 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
334 #ifdef DEBUG
335 nsCOMPtr<Document> d = window->GetExtantDoc();
336 NS_WARNING_ASSERTION(
337 !nsContentUtils::IsChromeDoc(d),
338 "Please do not use mouseenter/leave events in chrome. "
339 "They are slower than mouseover/out!");
340 #endif
341 window->SetHasMouseEnterLeaveEventListeners();
343 } else if (aEventMessage >= eGamepadEventFirst &&
344 aEventMessage <= eGamepadEventLast) {
345 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
346 window->SetHasGamepadEventListener();
348 } else if (aTypeAtom == nsGkAtoms::onkeydown ||
349 aTypeAtom == nsGkAtoms::onkeypress ||
350 aTypeAtom == nsGkAtoms::onkeyup) {
351 if (!aFlags.mInSystemGroup) {
352 mMayHaveKeyEventListener = true;
354 } else if (aTypeAtom == nsGkAtoms::oncompositionend ||
355 aTypeAtom == nsGkAtoms::oncompositionstart ||
356 aTypeAtom == nsGkAtoms::oncompositionupdate ||
357 aTypeAtom == nsGkAtoms::oninput) {
358 if (!aFlags.mInSystemGroup) {
359 mMayHaveInputOrCompositionEventListener = true;
361 } else if (aEventMessage == eSelectionChange) {
362 mMayHaveSelectionChangeEventListener = true;
363 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
364 window->SetHasSelectionChangeEventListeners();
366 } else if (aTypeAtom == nsGkAtoms::onstart) {
367 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
368 nsCOMPtr<Document> doc = window->GetExtantDoc();
369 if (doc) {
370 doc->SetDocumentAndPageUseCounter(eUseCounter_custom_onstart);
373 } else if (aTypeAtom == nsGkAtoms::onbounce) {
374 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
375 nsCOMPtr<Document> doc = window->GetExtantDoc();
376 if (doc) {
377 doc->SetDocumentAndPageUseCounter(eUseCounter_custom_onbounce);
380 } else if (aTypeAtom == nsGkAtoms::onfinish) {
381 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
382 nsCOMPtr<Document> doc = window->GetExtantDoc();
383 if (doc) {
384 doc->SetDocumentAndPageUseCounter(eUseCounter_custom_onfinish);
387 } else if (aTypeAtom == nsGkAtoms::ontext) {
388 // Ignore event listeners in the system group since editor needs to
389 // listen "text" events in the system group.
390 if (!aFlags.mInSystemGroup) {
391 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
392 window->SetHasTextEventListenerInDefaultGroup();
397 if (IsApzAwareListener(listener)) {
398 ProcessApzAwareEventListenerAdd();
401 if (mTarget) {
402 mTarget->EventListenerAdded(aTypeAtom);
405 if (mIsMainThreadELM && mTarget) {
406 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
407 aTypeAtom);
411 void EventListenerManager::ProcessApzAwareEventListenerAdd() {
412 // Mark the node as having apz aware listeners
413 nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
414 if (node) {
415 node->SetMayBeApzAware();
418 // Schedule a paint so event regions on the layer tree gets updated
419 Document* doc = nullptr;
420 if (node) {
421 doc = node->OwnerDoc();
423 if (!doc) {
424 if (nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow()) {
425 doc = window->GetExtantDoc();
428 if (!doc) {
429 if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(mTarget)) {
430 if (nsPIDOMWindowInner* window = helper->GetOwner()) {
431 doc = window->GetExtantDoc();
436 if (doc && gfxPlatform::AsyncPanZoomEnabled()) {
437 PresShell* presShell = doc->GetPresShell();
438 if (presShell) {
439 nsIFrame* f = presShell->GetRootFrame();
440 if (f) {
441 f->SchedulePaint();
447 bool EventListenerManager::IsDeviceType(EventMessage aEventMessage) {
448 switch (aEventMessage) {
449 case eDeviceOrientation:
450 case eAbsoluteDeviceOrientation:
451 case eDeviceMotion:
452 case eDeviceLight:
453 case eDeviceProximity:
454 case eUserProximity:
455 #if defined(MOZ_WIDGET_ANDROID)
456 case eOrientationChange:
457 #endif
458 return true;
459 default:
460 break;
462 return false;
465 void EventListenerManager::EnableDevice(EventMessage aEventMessage) {
466 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
467 if (!window) {
468 return;
471 switch (aEventMessage) {
472 case eDeviceOrientation:
473 #ifdef MOZ_WIDGET_ANDROID
474 // Falls back to SENSOR_ROTATION_VECTOR and SENSOR_ORIENTATION if
475 // unavailable on device.
476 window->EnableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
477 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
478 #else
479 window->EnableDeviceSensor(SENSOR_ORIENTATION);
480 #endif
481 break;
482 case eAbsoluteDeviceOrientation:
483 #ifdef MOZ_WIDGET_ANDROID
484 // Falls back to SENSOR_ORIENTATION if unavailable on device.
485 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
486 #else
487 window->EnableDeviceSensor(SENSOR_ORIENTATION);
488 #endif
489 break;
490 case eDeviceProximity:
491 case eUserProximity:
492 window->EnableDeviceSensor(SENSOR_PROXIMITY);
493 break;
494 case eDeviceLight:
495 window->EnableDeviceSensor(SENSOR_LIGHT);
496 break;
497 case eDeviceMotion:
498 window->EnableDeviceSensor(SENSOR_ACCELERATION);
499 window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
500 window->EnableDeviceSensor(SENSOR_GYROSCOPE);
501 break;
502 #if defined(MOZ_WIDGET_ANDROID)
503 case eOrientationChange:
504 window->EnableOrientationChangeListener();
505 break;
506 #endif
507 default:
508 NS_WARNING("Enabling an unknown device sensor.");
509 break;
513 void EventListenerManager::DisableDevice(EventMessage aEventMessage) {
514 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
515 if (!window) {
516 return;
519 switch (aEventMessage) {
520 case eDeviceOrientation:
521 #ifdef MOZ_WIDGET_ANDROID
522 // Disable all potential fallback sensors.
523 window->DisableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
524 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
525 #endif
526 window->DisableDeviceSensor(SENSOR_ORIENTATION);
527 break;
528 case eAbsoluteDeviceOrientation:
529 #ifdef MOZ_WIDGET_ANDROID
530 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
531 #endif
532 window->DisableDeviceSensor(SENSOR_ORIENTATION);
533 break;
534 case eDeviceMotion:
535 window->DisableDeviceSensor(SENSOR_ACCELERATION);
536 window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
537 window->DisableDeviceSensor(SENSOR_GYROSCOPE);
538 break;
539 case eDeviceProximity:
540 case eUserProximity:
541 window->DisableDeviceSensor(SENSOR_PROXIMITY);
542 break;
543 case eDeviceLight:
544 window->DisableDeviceSensor(SENSOR_LIGHT);
545 break;
546 #if defined(MOZ_WIDGET_ANDROID)
547 case eOrientationChange:
548 window->DisableOrientationChangeListener();
549 break;
550 #endif
551 default:
552 NS_WARNING("Disabling an unknown device sensor.");
553 break;
557 void EventListenerManager::NotifyEventListenerRemoved(nsAtom* aUserType) {
558 // If the following code is changed, other callsites of EventListenerRemoved
559 // and NotifyAboutMainThreadListenerChange should be changed too.
560 mNoListenerForEvent = eVoidEvent;
561 mNoListenerForEventAtom = nullptr;
562 if (mTarget) {
563 mTarget->EventListenerRemoved(aUserType);
565 if (mIsMainThreadELM && mTarget) {
566 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
567 aUserType);
571 void EventListenerManager::RemoveEventListenerInternal(
572 EventListenerHolder aListenerHolder, EventMessage aEventMessage,
573 nsAtom* aUserType, const EventListenerFlags& aFlags, bool aAllEvents) {
574 if (!aListenerHolder || !aEventMessage || mClearingListeners) {
575 return;
578 Listener* listener;
580 uint32_t count = mListeners.Length();
581 bool deviceType = IsDeviceType(aEventMessage);
583 RefPtr<EventListenerManager> kungFuDeathGrip(this);
585 for (uint32_t i = 0; i < count; ++i) {
586 listener = &mListeners.ElementAt(i);
587 if (EVENT_TYPE_EQUALS(listener, aEventMessage, aUserType, aAllEvents)) {
588 if (listener->mListener == aListenerHolder &&
589 listener->mFlags.EqualsForRemoval(aFlags)) {
590 mListeners.RemoveElementAt(i);
591 NotifyEventListenerRemoved(aUserType);
592 if (!aAllEvents && deviceType) {
593 DisableDevice(aEventMessage);
595 return;
601 bool EventListenerManager::ListenerCanHandle(const Listener* aListener,
602 const WidgetEvent* aEvent,
603 EventMessage aEventMessage) const
606 MOZ_ASSERT(aEventMessage == aEvent->mMessage ||
607 aEventMessage == GetLegacyEventMessage(aEvent->mMessage),
608 "aEvent and aEventMessage should agree, modulo legacyness");
610 // The listener has been removed, it cannot handle anything.
611 if (aListener->mListenerType == Listener::eNoListener) {
612 return false;
614 // This is slightly different from EVENT_TYPE_EQUALS in that it returns
615 // true even when aEvent->mMessage == eUnidentifiedEvent and
616 // aListener=>mEventMessage != eUnidentifiedEvent as long as the atoms are
617 // the same
618 if (MOZ_UNLIKELY(aListener->mAllEvents)) {
619 return true;
621 if (aEvent->mMessage == eUnidentifiedEvent) {
622 return aListener->mTypeAtom == aEvent->mSpecifiedEventType;
624 if (MOZ_UNLIKELY(!StaticPrefs::full_screen_api_unprefix_enabled() &&
625 aEvent->IsTrusted() &&
626 (aEventMessage == eFullscreenChange ||
627 aEventMessage == eFullscreenError))) {
628 // If unprefixed Fullscreen API is not enabled, don't dispatch it
629 // to the content.
630 if (!aEvent->mFlags.mInSystemGroup && !aListener->mIsChrome) {
631 return false;
634 MOZ_ASSERT(mIsMainThreadELM);
635 return aListener->mEventMessage == aEventMessage;
638 static bool DefaultToPassiveTouchListeners() {
639 static bool sDefaultToPassiveTouchListeners = false;
640 static bool sIsPrefCached = false;
642 if (!sIsPrefCached) {
643 sIsPrefCached = true;
644 Preferences::AddBoolVarCache(
645 &sDefaultToPassiveTouchListeners,
646 "dom.event.default_to_passive_touch_listeners");
649 return sDefaultToPassiveTouchListeners;
652 void EventListenerManager::AddEventListenerByType(
653 EventListenerHolder aListenerHolder, const nsAString& aType,
654 const EventListenerFlags& aFlags, const Optional<bool>& aPassive) {
655 RefPtr<nsAtom> atom;
656 EventMessage message =
657 GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
659 EventListenerFlags flags = aFlags;
660 if (aPassive.WasPassed()) {
661 flags.mPassive = aPassive.Value();
662 } else if ((message == eTouchStart || message == eTouchMove) &&
663 mIsMainThreadELM && DefaultToPassiveTouchListeners()) {
664 nsCOMPtr<nsINode> node;
665 nsCOMPtr<nsPIDOMWindowInner> win;
666 if ((win = GetTargetAsInnerWindow()) ||
667 ((node = do_QueryInterface(mTarget)) &&
668 (node == node->OwnerDoc() ||
669 node == node->OwnerDoc()->GetRootElement() ||
670 node == node->OwnerDoc()->GetBody()))) {
671 flags.mPassive = true;
675 AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags);
678 void EventListenerManager::RemoveEventListenerByType(
679 EventListenerHolder aListenerHolder, const nsAString& aType,
680 const EventListenerFlags& aFlags) {
681 RefPtr<nsAtom> atom;
682 EventMessage message =
683 GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
684 RemoveEventListenerInternal(std::move(aListenerHolder), message, atom,
685 aFlags);
688 EventListenerManager::Listener* EventListenerManager::FindEventHandler(
689 EventMessage aEventMessage, nsAtom* aTypeAtom) {
690 // Run through the listeners for this type and see if a script
691 // listener is registered
692 Listener* listener;
693 uint32_t count = mListeners.Length();
694 for (uint32_t i = 0; i < count; ++i) {
695 listener = &mListeners.ElementAt(i);
696 if (listener->mListenerIsHandler &&
697 EVENT_TYPE_EQUALS(listener, aEventMessage, aTypeAtom, false)) {
698 return listener;
701 return nullptr;
704 EventListenerManager::Listener* EventListenerManager::SetEventHandlerInternal(
705 nsAtom* aName, const TypedEventHandler& aTypedHandler,
706 bool aPermitUntrustedEvents) {
707 MOZ_ASSERT(aName);
709 EventMessage eventMessage = GetEventMessage(aName);
710 Listener* listener = FindEventHandler(eventMessage, aName);
712 if (!listener) {
713 // If we didn't find a script listener or no listeners existed
714 // create and add a new one.
715 EventListenerFlags flags;
716 flags.mListenerIsJSListener = true;
718 nsCOMPtr<JSEventHandler> jsEventHandler;
719 NS_NewJSEventHandler(mTarget, aName, aTypedHandler,
720 getter_AddRefs(jsEventHandler));
721 AddEventListenerInternal(EventListenerHolder(jsEventHandler), eventMessage,
722 aName, flags, true);
724 listener = FindEventHandler(eventMessage, aName);
725 } else {
726 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
727 MOZ_ASSERT(jsEventHandler,
728 "How can we have an event handler with no JSEventHandler?");
730 bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler;
731 // Possibly the same listener, but update still the context and scope.
732 jsEventHandler->SetHandler(aTypedHandler);
733 if (mTarget && !same) {
734 mTarget->EventListenerRemoved(aName);
735 mTarget->EventListenerAdded(aName);
737 if (mIsMainThreadELM && mTarget) {
738 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, aName);
742 // Set flag to indicate possible need for compilation later
743 listener->mHandlerIsString = !aTypedHandler.HasEventHandler();
744 if (aPermitUntrustedEvents) {
745 listener->mFlags.mAllowUntrustedEvents = true;
748 return listener;
751 nsresult EventListenerManager::SetEventHandler(nsAtom* aName,
752 const nsAString& aBody,
753 bool aDeferCompilation,
754 bool aPermitUntrustedEvents,
755 Element* aElement) {
756 nsCOMPtr<Document> doc;
757 nsCOMPtr<nsIScriptGlobalObject> global =
758 GetScriptGlobalAndDocument(getter_AddRefs(doc));
760 if (!global) {
761 // This can happen; for example this document might have been
762 // loaded as data.
763 return NS_OK;
766 nsresult rv = NS_OK;
767 // return early preventing the event listener from being added
768 // 'doc' is fetched above
769 if (doc) {
770 // Don't allow adding an event listener if the document is sandboxed
771 // without 'allow-scripts'.
772 if (doc->HasScriptsBlockedBySandbox()) {
773 return NS_ERROR_DOM_SECURITY_ERR;
776 // Perform CSP check
777 nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
778 unsigned lineNum = 0;
779 unsigned columnNum = 0;
781 JSContext* cx = nsContentUtils::GetCurrentJSContext();
782 if (cx && !JS::DescribeScriptedCaller(cx, nullptr, &lineNum, &columnNum)) {
783 JS_ClearPendingException(cx);
786 if (csp) {
787 bool allowsInlineScript = true;
788 rv = csp->GetAllowsInline(
789 nsIContentPolicy::TYPE_SCRIPT,
790 EmptyString(), // aNonce
791 true, // aParserCreated (true because attribute event handler)
792 aElement,
793 nullptr, // nsICSPEventListener
794 aBody, lineNum, columnNum, &allowsInlineScript);
795 NS_ENSURE_SUCCESS(rv, rv);
797 // return early if CSP wants us to block inline scripts
798 if (!allowsInlineScript) {
799 return NS_OK;
804 // This might be the first reference to this language in the global
805 // We must init the language before we attempt to fetch its context.
806 if (NS_FAILED(global->EnsureScriptEnvironment())) {
807 NS_WARNING("Failed to setup script environment for this language");
808 // but fall through and let the inevitable failure below handle it.
811 nsIScriptContext* context = global->GetScriptContext();
812 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
813 NS_ENSURE_STATE(global->HasJSGlobal());
815 Listener* listener = SetEventHandlerInternal(aName, TypedEventHandler(),
816 aPermitUntrustedEvents);
818 if (!aDeferCompilation) {
819 return CompileEventHandlerInternal(listener, &aBody, aElement);
822 return NS_OK;
825 void EventListenerManager::RemoveEventHandler(nsAtom* aName) {
826 if (mClearingListeners) {
827 return;
830 EventMessage eventMessage = GetEventMessage(aName);
831 Listener* listener = FindEventHandler(eventMessage, aName);
833 if (listener) {
834 mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0)));
835 NotifyEventListenerRemoved(aName);
836 if (IsDeviceType(eventMessage)) {
837 DisableDevice(eventMessage);
842 nsresult EventListenerManager::CompileEventHandlerInternal(
843 Listener* aListener, const nsAString* aBody, Element* aElement) {
844 MOZ_ASSERT(aListener->GetJSEventHandler());
845 MOZ_ASSERT(aListener->mHandlerIsString,
846 "Why are we compiling a non-string JS listener?");
847 JSEventHandler* jsEventHandler = aListener->GetJSEventHandler();
848 MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(),
849 "What is there to compile?");
851 nsresult result = NS_OK;
852 nsCOMPtr<Document> doc;
853 nsCOMPtr<nsIScriptGlobalObject> global =
854 GetScriptGlobalAndDocument(getter_AddRefs(doc));
855 NS_ENSURE_STATE(global);
857 // Activate JSAPI, and make sure that exceptions are reported on the right
858 // Window.
859 AutoJSAPI jsapi;
860 if (NS_WARN_IF(!jsapi.Init(global))) {
861 return NS_ERROR_UNEXPECTED;
863 JSContext* cx = jsapi.cx();
865 RefPtr<nsAtom> typeAtom = aListener->mTypeAtom;
866 nsAtom* attrName = typeAtom;
868 // Flag us as not a string so we don't keep trying to compile strings which
869 // can't be compiled.
870 aListener->mHandlerIsString = false;
872 // mTarget may not be an Element if it's a window and we're
873 // getting an inline event listener forwarded from <html:body> or
874 // <html:frameset> or <xul:window> or the like.
875 // XXX I don't like that we have to reference content from
876 // here. The alternative is to store the event handler string on
877 // the JSEventHandler itself, and that still doesn't address
878 // the arg names issue.
879 nsCOMPtr<Element> element = do_QueryInterface(mTarget);
880 MOZ_ASSERT(element || aBody, "Where will we get our body?");
881 nsAutoString handlerBody;
882 const nsAString* body = aBody;
883 if (!aBody) {
884 if (aListener->mTypeAtom == nsGkAtoms::onSVGLoad) {
885 attrName = nsGkAtoms::onload;
886 } else if (aListener->mTypeAtom == nsGkAtoms::onSVGUnload) {
887 attrName = nsGkAtoms::onunload;
888 } else if (aListener->mTypeAtom == nsGkAtoms::onSVGResize) {
889 attrName = nsGkAtoms::onresize;
890 } else if (aListener->mTypeAtom == nsGkAtoms::onSVGScroll) {
891 attrName = nsGkAtoms::onscroll;
892 } else if (aListener->mTypeAtom == nsGkAtoms::onSVGZoom) {
893 attrName = nsGkAtoms::onzoom;
894 } else if (aListener->mTypeAtom == nsGkAtoms::onbeginEvent) {
895 attrName = nsGkAtoms::onbegin;
896 } else if (aListener->mTypeAtom == nsGkAtoms::onrepeatEvent) {
897 attrName = nsGkAtoms::onrepeat;
898 } else if (aListener->mTypeAtom == nsGkAtoms::onendEvent) {
899 attrName = nsGkAtoms::onend;
902 element->GetAttr(kNameSpaceID_None, attrName, handlerBody);
903 body = &handlerBody;
904 aElement = element;
906 aListener = nullptr;
908 nsAutoCString url(NS_LITERAL_CSTRING("-moz-evil:lying-event-listener"));
909 MOZ_ASSERT(body);
910 MOZ_ASSERT(aElement);
911 nsIURI* uri = aElement->OwnerDoc()->GetDocumentURI();
912 if (uri) {
913 uri->GetSpec(url);
916 nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(mTarget);
917 uint32_t argCount;
918 const char** argNames;
919 nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), typeAtom, win,
920 &argCount, &argNames);
922 // Wrap the event target, so that we can use it as the scope for the event
923 // handler. Note that mTarget is different from aElement in the <body> case,
924 // where mTarget is a Window.
926 // The wrapScope doesn't really matter here, because the target will create
927 // its reflector in the proper scope, and then we'll enter that realm.
928 JS::Rooted<JSObject*> wrapScope(cx, global->GetGlobalJSObject());
929 JS::Rooted<JS::Value> v(cx);
931 JSAutoRealm ar(cx, wrapScope);
932 nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v,
933 /* aAllowWrapping = */ false);
934 if (NS_WARN_IF(NS_FAILED(rv))) {
935 return rv;
939 JS::Rooted<JSObject*> target(cx, &v.toObject());
940 JSAutoRealm ar(cx, target);
942 // Now that we've entered the realm we actually care about, create our
943 // scope chain. Note that we start with |element|, not aElement, because
944 // mTarget is different from aElement in the <body> case, where mTarget is a
945 // Window, and in that case we do not want the scope chain to include the body
946 // or the document.
947 JS::RootedVector<JSObject*> scopeChain(cx);
948 if (!nsJSUtils::GetScopeChainForElement(cx, element, &scopeChain)) {
949 return NS_ERROR_OUT_OF_MEMORY;
952 nsDependentAtomString str(attrName);
953 // Most of our names are short enough that we don't even have to malloc
954 // the JS string stuff, so don't worry about playing games with
955 // refcounting XPCOM stringbuffers.
956 JS::Rooted<JSString*> jsStr(
957 cx, JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length()));
958 NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY);
960 // Get the reflector for |aElement|, so that we can pass to setElement.
961 if (NS_WARN_IF(!GetOrCreateDOMReflector(cx, aElement, &v))) {
962 return NS_ERROR_FAILURE;
964 JS::CompileOptions options(cx);
965 // Use line 0 to make the function body starts from line 1.
966 options.setIntroductionType("eventHandler")
967 .setFileAndLine(url.get(), 0)
968 .setElement(&v.toObject())
969 .setElementAttributeName(jsStr);
971 JS::Rooted<JSObject*> handler(cx);
972 result = nsJSUtils::CompileFunction(jsapi, scopeChain, options,
973 nsAtomCString(typeAtom), argCount,
974 argNames, *body, handler.address());
975 NS_ENSURE_SUCCESS(result, result);
976 NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
978 MOZ_ASSERT(js::IsObjectInContextCompartment(handler, cx));
979 JS::Rooted<JSObject*> handlerGlobal(cx, JS::CurrentGlobalOrNull(cx));
981 if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) {
982 RefPtr<OnErrorEventHandlerNonNull> handlerCallback =
983 new OnErrorEventHandlerNonNull(static_cast<JSContext*>(nullptr),
984 handler, handlerGlobal,
985 /* aIncumbentGlobal = */ nullptr);
986 jsEventHandler->SetHandler(handlerCallback);
987 } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) {
988 RefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
989 new OnBeforeUnloadEventHandlerNonNull(static_cast<JSContext*>(nullptr),
990 handler, handlerGlobal,
991 /* aIncumbentGlobal = */ nullptr);
992 jsEventHandler->SetHandler(handlerCallback);
993 } else {
994 RefPtr<EventHandlerNonNull> handlerCallback = new EventHandlerNonNull(
995 static_cast<JSContext*>(nullptr), handler, handlerGlobal,
996 /* aIncumbentGlobal = */ nullptr);
997 jsEventHandler->SetHandler(handlerCallback);
1000 return result;
1003 nsresult EventListenerManager::HandleEventSubType(Listener* aListener,
1004 Event* aDOMEvent,
1005 EventTarget* aCurrentTarget) {
1006 nsresult result = NS_OK;
1007 // strong ref
1008 EventListenerHolder listenerHolder(aListener->mListener.Clone());
1010 // If this is a script handler and we haven't yet
1011 // compiled the event handler itself
1012 if ((aListener->mListenerType == Listener::eJSEventListener) &&
1013 aListener->mHandlerIsString) {
1014 result = CompileEventHandlerInternal(aListener, nullptr, nullptr);
1015 aListener = nullptr;
1018 if (NS_SUCCEEDED(result)) {
1019 EventCallbackDebuggerNotificationGuard dbgGuard(aCurrentTarget, aDOMEvent);
1020 nsAutoMicroTask mt;
1022 // Event::currentTarget is set in EventDispatcher.
1023 if (listenerHolder.HasWebIDLCallback()) {
1024 ErrorResult rv;
1025 listenerHolder.GetWebIDLCallback()->HandleEvent(aCurrentTarget,
1026 *aDOMEvent, rv);
1027 result = rv.StealNSResult();
1028 } else {
1029 // listenerHolder is holding a stack ref here.
1030 result = MOZ_KnownLive(listenerHolder.GetXPCOMCallback())
1031 ->HandleEvent(aDOMEvent);
1035 return result;
1038 EventMessage EventListenerManager::GetLegacyEventMessage(
1039 EventMessage aEventMessage) const {
1040 // webkit-prefixed legacy events:
1041 if (aEventMessage == eTransitionEnd) {
1042 return eWebkitTransitionEnd;
1044 if (aEventMessage == eAnimationStart) {
1045 return eWebkitAnimationStart;
1047 if (aEventMessage == eAnimationEnd) {
1048 return eWebkitAnimationEnd;
1050 if (aEventMessage == eAnimationIteration) {
1051 return eWebkitAnimationIteration;
1054 switch (aEventMessage) {
1055 case eFullscreenChange:
1056 return eMozFullscreenChange;
1057 case eFullscreenError:
1058 return eMozFullscreenError;
1059 default:
1060 return aEventMessage;
1064 EventMessage EventListenerManager::GetEventMessage(nsAtom* aEventName) const {
1065 if (mIsMainThreadELM) {
1066 return nsContentUtils::GetEventMessage(aEventName);
1069 // The nsContentUtils event message hashtables aren't threadsafe, so just fall
1070 // back to eUnidentifiedEvent.
1071 return eUnidentifiedEvent;
1074 EventMessage EventListenerManager::GetEventMessageAndAtomForListener(
1075 const nsAString& aType, nsAtom** aAtom) {
1076 if (mIsMainThreadELM) {
1077 return nsContentUtils::GetEventMessageAndAtomForListener(aType, aAtom);
1080 *aAtom = NS_Atomize(NS_LITERAL_STRING("on") + aType).take();
1081 return eUnidentifiedEvent;
1084 already_AddRefed<nsPIDOMWindowInner> EventListenerManager::WindowFromListener(
1085 Listener* aListener, bool aItemInShadowTree) {
1086 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
1087 if (!aItemInShadowTree) {
1088 if (aListener->mListener.HasWebIDLCallback()) {
1089 CallbackObject* callback = aListener->mListener.GetWebIDLCallback();
1090 nsIGlobalObject* global = nullptr;
1091 if (callback) {
1092 global = callback->IncumbentGlobalOrNull();
1094 if (global) {
1095 innerWindow = global->AsInnerWindow(); // Can be nullptr
1097 } else {
1098 // Can't get the global from
1099 // listener->mListener.GetXPCOMCallback().
1100 // In most cases, it would be the same as for
1101 // the target, so let's do that.
1102 innerWindow = GetInnerWindowForTarget(); // Can be nullptr
1105 return innerWindow.forget();
1109 * Causes a check for event listeners and processing by them if they exist.
1110 * @param an event listener
1113 void EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
1114 WidgetEvent* aEvent,
1115 Event** aDOMEvent,
1116 EventTarget* aCurrentTarget,
1117 nsEventStatus* aEventStatus,
1118 bool aItemInShadowTree) {
1119 // Set the value of the internal PreventDefault flag properly based on
1120 // aEventStatus
1121 if (!aEvent->DefaultPrevented() &&
1122 *aEventStatus == nsEventStatus_eConsumeNoDefault) {
1123 // Assume that if only aEventStatus claims that the event has already been
1124 // consumed, the consumer is default event handler.
1125 aEvent->PreventDefault();
1128 Maybe<AutoHandlingUserInputStatePusher> userInputStatePusher;
1129 Maybe<AutoPopupStatePusher> popupStatePusher;
1130 if (mIsMainThreadELM) {
1131 userInputStatePusher.emplace(
1132 EventStateManager::IsUserInteractionEvent(aEvent), aEvent);
1133 popupStatePusher.emplace(
1134 PopupBlocker::GetEventPopupControlState(aEvent, *aDOMEvent));
1137 bool hasListener = false;
1138 bool hasListenerForCurrentGroup = false;
1139 bool usingLegacyMessage = false;
1140 bool hasRemovedListener = false;
1141 EventMessage eventMessage = aEvent->mMessage;
1143 while (true) {
1144 nsAutoTObserverArray<Listener, 2>::EndLimitedIterator iter(mListeners);
1145 Maybe<EventMessageAutoOverride> legacyAutoOverride;
1146 while (iter.HasMore()) {
1147 if (aEvent->mFlags.mImmediatePropagationStopped) {
1148 break;
1150 Listener* listener = &iter.GetNext();
1151 // Check that the phase is same in event and event listener.
1152 // Handle only trusted events, except when listener permits untrusted
1153 // events.
1154 if (ListenerCanHandle(listener, aEvent, eventMessage)) {
1155 hasListener = true;
1156 hasListenerForCurrentGroup =
1157 hasListenerForCurrentGroup ||
1158 listener->mFlags.mInSystemGroup == aEvent->mFlags.mInSystemGroup;
1159 if (listener->IsListening(aEvent) &&
1160 (aEvent->IsTrusted() || listener->mFlags.mAllowUntrustedEvents)) {
1161 if (!*aDOMEvent) {
1162 // This is tiny bit slow, but happens only once per event.
1163 // Similar code also in EventDispatcher.
1164 nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget;
1165 RefPtr<Event> event = EventDispatcher::CreateEvent(
1166 et, aPresContext, aEvent, EmptyString());
1167 event.forget(aDOMEvent);
1169 if (*aDOMEvent) {
1170 if (!aEvent->mCurrentTarget) {
1171 aEvent->mCurrentTarget = aCurrentTarget->GetTargetForDOMEvent();
1172 if (!aEvent->mCurrentTarget) {
1173 break;
1176 if (usingLegacyMessage && !legacyAutoOverride) {
1177 // Override the aDOMEvent's event-message (its .type) until we
1178 // finish traversing listeners (when legacyAutoOverride destructs)
1179 legacyAutoOverride.emplace(*aDOMEvent, eventMessage);
1182 // Maybe add a marker to the docshell's timeline, but only
1183 // bother with all the logic if some docshell is recording.
1184 nsCOMPtr<nsIDocShell> docShell;
1185 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
1186 bool needsEndEventMarker = false;
1188 if (mIsMainThreadELM &&
1189 listener->mListenerType != Listener::eNativeListener) {
1190 docShell = nsContentUtils::GetDocShellForEventTarget(mTarget);
1191 if (docShell) {
1192 if (timelines && timelines->HasConsumer(docShell)) {
1193 needsEndEventMarker = true;
1194 nsAutoString typeStr;
1195 (*aDOMEvent)->GetType(typeStr);
1196 uint16_t phase = (*aDOMEvent)->EventPhase();
1197 timelines->AddMarkerForDocShell(
1198 docShell, MakeUnique<EventTimelineMarker>(
1199 typeStr, phase, MarkerTracingType::START));
1204 aEvent->mFlags.mInPassiveListener = listener->mFlags.mPassive;
1205 Maybe<Listener> listenerHolder;
1206 if (listener->mFlags.mOnce) {
1207 // Move the listener to the stack before handling the event.
1208 // The order is important, otherwise the listener could be
1209 // called again inside the listener.
1210 listenerHolder.emplace(std::move(*listener));
1211 listener = listenerHolder.ptr();
1212 hasRemovedListener = true;
1215 nsCOMPtr<nsPIDOMWindowInner> innerWindow =
1216 WindowFromListener(listener, aItemInShadowTree);
1217 mozilla::dom::Event* oldWindowEvent = nullptr;
1218 if (innerWindow) {
1219 oldWindowEvent = innerWindow->SetEvent(*aDOMEvent);
1222 nsresult rv =
1223 HandleEventSubType(listener, *aDOMEvent, aCurrentTarget);
1225 if (innerWindow) {
1226 Unused << innerWindow->SetEvent(oldWindowEvent);
1229 if (NS_FAILED(rv)) {
1230 aEvent->mFlags.mExceptionWasRaised = true;
1232 aEvent->mFlags.mInPassiveListener = false;
1234 if (needsEndEventMarker) {
1235 timelines->AddMarkerForDocShell(docShell, "DOMEvent",
1236 MarkerTracingType::END);
1243 // If we didn't find any matching listeners, and our event has a legacy
1244 // version, we'll now switch to looking for that legacy version and we'll
1245 // recheck our listeners.
1246 if (hasListenerForCurrentGroup || usingLegacyMessage ||
1247 !aEvent->IsTrusted()) {
1248 // No need to recheck listeners, because we already found a match, we
1249 // already rechecked them, or it is not a trusted event.
1250 break;
1252 EventMessage legacyEventMessage = GetLegacyEventMessage(eventMessage);
1253 if (legacyEventMessage == eventMessage) {
1254 break; // There's no legacy version of our event; no need to recheck.
1256 MOZ_ASSERT(
1257 GetLegacyEventMessage(legacyEventMessage) == legacyEventMessage,
1258 "Legacy event messages should not themselves have legacy versions");
1260 // Recheck our listeners, using the legacy event message we just looked up:
1261 eventMessage = legacyEventMessage;
1262 usingLegacyMessage = true;
1265 aEvent->mCurrentTarget = nullptr;
1267 if (hasRemovedListener) {
1268 // If there are any once listeners replaced with a placeholder in
1269 // the loop above, we need to clean up them here. Note that, this
1270 // could clear once listeners handled in some outer level as well,
1271 // but that should not affect the result.
1272 mListeners.RemoveElementsBy([](const Listener& aListener) {
1273 return aListener.mListenerType == Listener::eNoListener;
1275 NotifyEventListenerRemoved(aEvent->mSpecifiedEventType);
1276 if (IsDeviceType(aEvent->mMessage)) {
1277 // This is a device-type event, we need to check whether we can
1278 // disable device after removing the once listeners.
1279 bool hasAnyListener = false;
1280 nsAutoTObserverArray<Listener, 2>::ForwardIterator iter(mListeners);
1281 while (iter.HasMore()) {
1282 Listener* listener = &iter.GetNext();
1283 if (EVENT_TYPE_EQUALS(listener, aEvent->mMessage,
1284 aEvent->mSpecifiedEventType,
1285 /* all events */ false)) {
1286 hasAnyListener = true;
1287 break;
1290 if (!hasAnyListener) {
1291 DisableDevice(aEvent->mMessage);
1296 if (mIsMainThreadELM && !hasListener) {
1297 mNoListenerForEvent = aEvent->mMessage;
1298 mNoListenerForEventAtom = aEvent->mSpecifiedEventType;
1301 if (aEvent->DefaultPrevented()) {
1302 *aEventStatus = nsEventStatus_eConsumeNoDefault;
1306 void EventListenerManager::Disconnect() {
1307 mTarget = nullptr;
1308 RemoveAllListenersSilently();
1311 void EventListenerManager::AddEventListener(const nsAString& aType,
1312 EventListenerHolder aListenerHolder,
1313 bool aUseCapture,
1314 bool aWantsUntrusted) {
1315 EventListenerFlags flags;
1316 flags.mCapture = aUseCapture;
1317 flags.mAllowUntrustedEvents = aWantsUntrusted;
1318 return AddEventListenerByType(std::move(aListenerHolder), aType, flags);
1321 void EventListenerManager::AddEventListener(
1322 const nsAString& aType, EventListenerHolder aListenerHolder,
1323 const dom::AddEventListenerOptionsOrBoolean& aOptions,
1324 bool aWantsUntrusted) {
1325 EventListenerFlags flags;
1326 Optional<bool> passive;
1327 if (aOptions.IsBoolean()) {
1328 flags.mCapture = aOptions.GetAsBoolean();
1329 } else {
1330 const auto& options = aOptions.GetAsAddEventListenerOptions();
1331 flags.mCapture = options.mCapture;
1332 flags.mInSystemGroup = options.mMozSystemGroup;
1333 flags.mOnce = options.mOnce;
1334 if (options.mPassive.WasPassed()) {
1335 passive.Construct(options.mPassive.Value());
1338 flags.mAllowUntrustedEvents = aWantsUntrusted;
1339 return AddEventListenerByType(std::move(aListenerHolder), aType, flags,
1340 passive);
1343 void EventListenerManager::RemoveEventListener(
1344 const nsAString& aType, EventListenerHolder aListenerHolder,
1345 bool aUseCapture) {
1346 EventListenerFlags flags;
1347 flags.mCapture = aUseCapture;
1348 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
1351 void EventListenerManager::RemoveEventListener(
1352 const nsAString& aType, EventListenerHolder aListenerHolder,
1353 const dom::EventListenerOptionsOrBoolean& aOptions) {
1354 EventListenerFlags flags;
1355 if (aOptions.IsBoolean()) {
1356 flags.mCapture = aOptions.GetAsBoolean();
1357 } else {
1358 const auto& options = aOptions.GetAsEventListenerOptions();
1359 flags.mCapture = options.mCapture;
1360 flags.mInSystemGroup = options.mMozSystemGroup;
1362 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
1365 void EventListenerManager::AddListenerForAllEvents(EventListener* aDOMListener,
1366 bool aUseCapture,
1367 bool aWantsUntrusted,
1368 bool aSystemEventGroup) {
1369 EventListenerFlags flags;
1370 flags.mCapture = aUseCapture;
1371 flags.mAllowUntrustedEvents = aWantsUntrusted;
1372 flags.mInSystemGroup = aSystemEventGroup;
1373 AddEventListenerInternal(EventListenerHolder(aDOMListener), eAllEvents,
1374 nullptr, flags, false, true);
1377 void EventListenerManager::RemoveListenerForAllEvents(
1378 EventListener* aDOMListener, bool aUseCapture, bool aSystemEventGroup) {
1379 EventListenerFlags flags;
1380 flags.mCapture = aUseCapture;
1381 flags.mInSystemGroup = aSystemEventGroup;
1382 RemoveEventListenerInternal(EventListenerHolder(aDOMListener), eAllEvents,
1383 nullptr, flags, true);
1386 bool EventListenerManager::HasMutationListeners() {
1387 if (mMayHaveMutationListeners) {
1388 uint32_t count = mListeners.Length();
1389 for (uint32_t i = 0; i < count; ++i) {
1390 Listener* listener = &mListeners.ElementAt(i);
1391 if (listener->mEventMessage >= eLegacyMutationEventFirst &&
1392 listener->mEventMessage <= eLegacyMutationEventLast) {
1393 return true;
1398 return false;
1401 uint32_t EventListenerManager::MutationListenerBits() {
1402 uint32_t bits = 0;
1403 if (mMayHaveMutationListeners) {
1404 uint32_t count = mListeners.Length();
1405 for (uint32_t i = 0; i < count; ++i) {
1406 Listener* listener = &mListeners.ElementAt(i);
1407 if (listener->mEventMessage >= eLegacyMutationEventFirst &&
1408 listener->mEventMessage <= eLegacyMutationEventLast) {
1409 if (listener->mEventMessage == eLegacySubtreeModified) {
1410 return kAllMutationBits;
1412 bits |= MutationBitForEventType(listener->mEventMessage);
1416 return bits;
1419 bool EventListenerManager::HasListenersFor(const nsAString& aEventName) const {
1420 RefPtr<nsAtom> atom = NS_Atomize(NS_LITERAL_STRING("on") + aEventName);
1421 return HasListenersFor(atom);
1424 bool EventListenerManager::HasListenersFor(nsAtom* aEventNameWithOn) const {
1425 #ifdef DEBUG
1426 nsAutoString name;
1427 aEventNameWithOn->ToString(name);
1428 #endif
1429 NS_ASSERTION(StringBeginsWith(name, NS_LITERAL_STRING("on")),
1430 "Event name does not start with 'on'");
1431 uint32_t count = mListeners.Length();
1432 for (uint32_t i = 0; i < count; ++i) {
1433 const Listener* listener = &mListeners.ElementAt(i);
1434 if (listener->mTypeAtom == aEventNameWithOn) {
1435 return true;
1438 return false;
1441 bool EventListenerManager::HasListeners() const {
1442 return !mListeners.IsEmpty();
1445 nsresult EventListenerManager::GetListenerInfo(
1446 nsTArray<RefPtr<nsIEventListenerInfo>>& aList) {
1447 nsCOMPtr<EventTarget> target = mTarget;
1448 NS_ENSURE_STATE(target);
1449 aList.Clear();
1450 nsAutoTObserverArray<Listener, 2>::ForwardIterator iter(mListeners);
1451 while (iter.HasMore()) {
1452 const Listener& listener = iter.GetNext();
1453 // If this is a script handler and we haven't yet
1454 // compiled the event handler itself go ahead and compile it
1455 if (listener.mListenerType == Listener::eJSEventListener &&
1456 listener.mHandlerIsString) {
1457 CompileEventHandlerInternal(const_cast<Listener*>(&listener), nullptr,
1458 nullptr);
1460 nsAutoString eventType;
1461 if (listener.mAllEvents) {
1462 eventType.SetIsVoid(true);
1463 } else if (listener.mListenerType == Listener::eNoListener) {
1464 continue;
1465 } else {
1466 eventType.Assign(Substring(nsDependentAtomString(listener.mTypeAtom), 2));
1469 JS::Rooted<JSObject*> callback(RootingCx());
1470 JS::Rooted<JSObject*> callbackGlobal(RootingCx());
1471 if (JSEventHandler* handler = listener.GetJSEventHandler()) {
1472 if (handler->GetTypedEventHandler().HasEventHandler()) {
1473 CallbackFunction* callbackFun = handler->GetTypedEventHandler().Ptr();
1474 callback = callbackFun->CallableOrNull();
1475 callbackGlobal = callbackFun->CallbackGlobalOrNull();
1476 if (!callback) {
1477 // This will be null for cross-compartment event listeners
1478 // which have been destroyed.
1479 continue;
1482 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1483 EventListener* listenerCallback = listener.mListener.GetWebIDLCallback();
1484 callback = listenerCallback->CallbackOrNull();
1485 callbackGlobal = listenerCallback->CallbackGlobalOrNull();
1486 if (!callback) {
1487 // This will be null for cross-compartment event listeners
1488 // which have been destroyed.
1489 continue;
1493 RefPtr<EventListenerInfo> info = new EventListenerInfo(
1494 eventType, callback, callbackGlobal, listener.mFlags.mCapture,
1495 listener.mFlags.mAllowUntrustedEvents, listener.mFlags.mInSystemGroup);
1496 aList.AppendElement(info.forget());
1498 return NS_OK;
1501 bool EventListenerManager::HasUnloadListeners() {
1502 uint32_t count = mListeners.Length();
1503 for (uint32_t i = 0; i < count; ++i) {
1504 Listener* listener = &mListeners.ElementAt(i);
1505 if (listener->mEventMessage == eUnload ||
1506 listener->mEventMessage == eBeforeUnload) {
1507 return true;
1510 return false;
1513 void EventListenerManager::SetEventHandler(nsAtom* aEventName,
1514 EventHandlerNonNull* aHandler) {
1515 if (!aHandler) {
1516 RemoveEventHandler(aEventName);
1517 return;
1520 // Untrusted events are always permitted for non-chrome script
1521 // handlers.
1522 SetEventHandlerInternal(
1523 aEventName, TypedEventHandler(aHandler),
1524 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
1527 void EventListenerManager::SetEventHandler(
1528 OnErrorEventHandlerNonNull* aHandler) {
1529 if (!aHandler) {
1530 RemoveEventHandler(nsGkAtoms::onerror);
1531 return;
1534 // Untrusted events are always permitted on workers and for non-chrome script
1535 // on the main thread.
1536 bool allowUntrusted = !mIsMainThreadELM || !nsContentUtils::IsCallerChrome();
1538 SetEventHandlerInternal(nsGkAtoms::onerror, TypedEventHandler(aHandler),
1539 allowUntrusted);
1542 void EventListenerManager::SetEventHandler(
1543 OnBeforeUnloadEventHandlerNonNull* aHandler) {
1544 if (!aHandler) {
1545 RemoveEventHandler(nsGkAtoms::onbeforeunload);
1546 return;
1549 // Untrusted events are always permitted for non-chrome script
1550 // handlers.
1551 SetEventHandlerInternal(
1552 nsGkAtoms::onbeforeunload, TypedEventHandler(aHandler),
1553 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
1556 const TypedEventHandler* EventListenerManager::GetTypedEventHandler(
1557 nsAtom* aEventName) {
1558 EventMessage eventMessage = GetEventMessage(aEventName);
1559 Listener* listener = FindEventHandler(eventMessage, aEventName);
1561 if (!listener) {
1562 return nullptr;
1565 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
1567 if (listener->mHandlerIsString) {
1568 CompileEventHandlerInternal(listener, nullptr, nullptr);
1571 const TypedEventHandler& typedHandler =
1572 jsEventHandler->GetTypedEventHandler();
1573 return typedHandler.HasEventHandler() ? &typedHandler : nullptr;
1576 size_t EventListenerManager::SizeOfIncludingThis(
1577 MallocSizeOf aMallocSizeOf) const {
1578 size_t n = aMallocSizeOf(this);
1579 n += mListeners.ShallowSizeOfExcludingThis(aMallocSizeOf);
1580 uint32_t count = mListeners.Length();
1581 for (uint32_t i = 0; i < count; ++i) {
1582 JSEventHandler* jsEventHandler =
1583 mListeners.ElementAt(i).GetJSEventHandler();
1584 if (jsEventHandler) {
1585 n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf);
1588 return n;
1591 void EventListenerManager::MarkForCC() {
1592 uint32_t count = mListeners.Length();
1593 for (uint32_t i = 0; i < count; ++i) {
1594 const Listener& listener = mListeners.ElementAt(i);
1595 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
1596 if (jsEventHandler) {
1597 const TypedEventHandler& typedHandler =
1598 jsEventHandler->GetTypedEventHandler();
1599 if (typedHandler.HasEventHandler()) {
1600 typedHandler.Ptr()->MarkForCC();
1602 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1603 listener.mListener.GetWebIDLCallback()->MarkForCC();
1606 if (mRefCnt.IsPurple()) {
1607 mRefCnt.RemovePurple();
1611 void EventListenerManager::TraceListeners(JSTracer* aTrc) {
1612 uint32_t count = mListeners.Length();
1613 for (uint32_t i = 0; i < count; ++i) {
1614 const Listener& listener = mListeners.ElementAt(i);
1615 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
1616 if (jsEventHandler) {
1617 const TypedEventHandler& typedHandler =
1618 jsEventHandler->GetTypedEventHandler();
1619 if (typedHandler.HasEventHandler()) {
1620 mozilla::TraceScriptHolder(typedHandler.Ptr(), aTrc);
1622 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1623 mozilla::TraceScriptHolder(listener.mListener.GetWebIDLCallback(), aTrc);
1625 // We might have eWrappedJSListener, but that is the legacy type for
1626 // JS implemented event listeners, and trickier to handle here.
1630 bool EventListenerManager::HasNonSystemGroupListenersForUntrustedKeyEvents() {
1631 uint32_t count = mListeners.Length();
1632 for (uint32_t i = 0; i < count; ++i) {
1633 Listener* listener = &mListeners.ElementAt(i);
1634 if (!listener->mFlags.mInSystemGroup &&
1635 listener->mFlags.mAllowUntrustedEvents &&
1636 (listener->mTypeAtom == nsGkAtoms::onkeydown ||
1637 listener->mTypeAtom == nsGkAtoms::onkeypress ||
1638 listener->mTypeAtom == nsGkAtoms::onkeyup)) {
1639 return true;
1642 return false;
1645 bool EventListenerManager::
1646 HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents() {
1647 uint32_t count = mListeners.Length();
1648 for (uint32_t i = 0; i < count; ++i) {
1649 Listener* listener = &mListeners.ElementAt(i);
1650 if (!listener->mFlags.mPassive && !listener->mFlags.mInSystemGroup &&
1651 listener->mFlags.mAllowUntrustedEvents &&
1652 (listener->mTypeAtom == nsGkAtoms::onkeydown ||
1653 listener->mTypeAtom == nsGkAtoms::onkeypress ||
1654 listener->mTypeAtom == nsGkAtoms::onkeyup)) {
1655 return true;
1658 return false;
1661 bool EventListenerManager::HasApzAwareListeners() {
1662 uint32_t count = mListeners.Length();
1663 for (uint32_t i = 0; i < count; ++i) {
1664 Listener* listener = &mListeners.ElementAt(i);
1665 if (IsApzAwareListener(listener)) {
1666 return true;
1669 return false;
1672 bool EventListenerManager::IsApzAwareListener(Listener* aListener) {
1673 return !aListener->mFlags.mPassive && IsApzAwareEvent(aListener->mTypeAtom);
1676 bool EventListenerManager::IsApzAwareEvent(nsAtom* aEvent) {
1677 if (aEvent == nsGkAtoms::onwheel || aEvent == nsGkAtoms::onDOMMouseScroll ||
1678 aEvent == nsGkAtoms::onmousewheel ||
1679 aEvent == nsGkAtoms::onMozMousePixelScroll) {
1680 return true;
1682 // In theory we should schedule a repaint if the touch event pref changes,
1683 // because the event regions might be out of date. In practice that seems like
1684 // overkill because users generally shouldn't be flipping this pref, much
1685 // less expecting touch listeners on the page to immediately start preventing
1686 // scrolling without so much as a repaint. Tests that we write can work
1687 // around this constraint easily enough.
1688 if (aEvent == nsGkAtoms::ontouchstart || aEvent == nsGkAtoms::ontouchmove) {
1689 return TouchEvent::PrefEnabled(
1690 nsContentUtils::GetDocShellForEventTarget(mTarget));
1692 return false;
1695 void EventListenerManager::RemoveAllListeners() {
1696 while (!mListeners.IsEmpty()) {
1697 size_t idx = mListeners.Length() - 1;
1698 RefPtr<nsAtom> type = mListeners.ElementAt(idx).mTypeAtom;
1699 EventMessage message = mListeners.ElementAt(idx).mEventMessage;
1700 mListeners.RemoveElementAt(idx);
1701 NotifyEventListenerRemoved(type);
1702 if (IsDeviceType(message)) {
1703 DisableDevice(message);
1708 already_AddRefed<nsIScriptGlobalObject>
1709 EventListenerManager::GetScriptGlobalAndDocument(Document** aDoc) {
1710 nsCOMPtr<nsINode> node(do_QueryInterface(mTarget));
1711 nsCOMPtr<Document> doc;
1712 nsCOMPtr<nsIScriptGlobalObject> global;
1713 if (node) {
1714 // Try to get context from doc
1715 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
1716 // if that's the XBL document?
1717 doc = node->OwnerDoc();
1718 if (doc->IsLoadedAsData()) {
1719 return nullptr;
1722 // We want to allow compiling an event handler even in an unloaded
1723 // document, so use GetScopeObject here, not GetScriptHandlingObject.
1724 global = do_QueryInterface(doc->GetScopeObject());
1725 } else {
1726 if (nsCOMPtr<nsPIDOMWindowInner> win = GetTargetAsInnerWindow()) {
1727 doc = win->GetExtantDoc();
1728 global = do_QueryInterface(win);
1729 } else {
1730 global = do_QueryInterface(mTarget);
1734 doc.forget(aDoc);
1735 return global.forget();
1738 } // namespace mozilla