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
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"
39 #include "nsContentUtils.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"
57 #include "nsDisplayList.h"
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
) {
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
;
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
) {
146 mClearingListeners
= true;
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
,
158 if (MOZ_UNLIKELY(aCallback
.WantDebugInfo())) {
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(),
171 CycleCollectionNoteChild(aCallback
, aField
.mListener
.GetISupports(), aName
,
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
)
184 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
186 nsPIDOMWindowInner
* EventListenerManager::GetInnerWindowForTarget() {
187 nsCOMPtr
<nsINode
> node
= do_QueryInterface(mTarget
);
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();
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
,
208 MOZ_ASSERT((aEventMessage
&& aTypeAtom
) || aAllEvents
, // all-events listener
211 if (!aListenerHolder
|| mClearingListeners
) {
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
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
) {
232 mNoListenerForEvent
= eVoidEvent
;
233 mNoListenerForEventAtom
= nullptr;
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
;
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();
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
)
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
);
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;
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!");
327 window
->SetHasPointerEnterLeaveEventListeners();
330 } else if (aTypeAtom
== nsGkAtoms::onmouseenter
||
331 aTypeAtom
== nsGkAtoms::onmouseleave
) {
332 mMayHaveMouseEnterLeaveEventListener
= true;
333 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
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!");
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();
370 doc
->SetDocumentAndPageUseCounter(eUseCounter_custom_onstart
);
373 } else if (aTypeAtom
== nsGkAtoms::onbounce
) {
374 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
375 nsCOMPtr
<Document
> doc
= window
->GetExtantDoc();
377 doc
->SetDocumentAndPageUseCounter(eUseCounter_custom_onbounce
);
380 } else if (aTypeAtom
== nsGkAtoms::onfinish
) {
381 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
382 nsCOMPtr
<Document
> doc
= window
->GetExtantDoc();
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();
402 mTarget
->EventListenerAdded(aTypeAtom
);
405 if (mIsMainThreadELM
&& mTarget
) {
406 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
,
411 void EventListenerManager::ProcessApzAwareEventListenerAdd() {
412 // Mark the node as having apz aware listeners
413 nsCOMPtr
<nsINode
> node
= do_QueryInterface(mTarget
);
415 node
->SetMayBeApzAware();
418 // Schedule a paint so event regions on the layer tree gets updated
419 Document
* doc
= nullptr;
421 doc
= node
->OwnerDoc();
424 if (nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow()) {
425 doc
= window
->GetExtantDoc();
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();
439 nsIFrame
* f
= presShell
->GetRootFrame();
447 bool EventListenerManager::IsDeviceType(EventMessage aEventMessage
) {
448 switch (aEventMessage
) {
449 case eDeviceOrientation
:
450 case eAbsoluteDeviceOrientation
:
453 case eDeviceProximity
:
455 #if defined(MOZ_WIDGET_ANDROID)
456 case eOrientationChange
:
465 void EventListenerManager::EnableDevice(EventMessage aEventMessage
) {
466 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow();
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
);
479 window
->EnableDeviceSensor(SENSOR_ORIENTATION
);
482 case eAbsoluteDeviceOrientation
:
483 #ifdef MOZ_WIDGET_ANDROID
484 // Falls back to SENSOR_ORIENTATION if unavailable on device.
485 window
->EnableDeviceSensor(SENSOR_ROTATION_VECTOR
);
487 window
->EnableDeviceSensor(SENSOR_ORIENTATION
);
490 case eDeviceProximity
:
492 window
->EnableDeviceSensor(SENSOR_PROXIMITY
);
495 window
->EnableDeviceSensor(SENSOR_LIGHT
);
498 window
->EnableDeviceSensor(SENSOR_ACCELERATION
);
499 window
->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION
);
500 window
->EnableDeviceSensor(SENSOR_GYROSCOPE
);
502 #if defined(MOZ_WIDGET_ANDROID)
503 case eOrientationChange
:
504 window
->EnableOrientationChangeListener();
508 NS_WARNING("Enabling an unknown device sensor.");
513 void EventListenerManager::DisableDevice(EventMessage aEventMessage
) {
514 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow();
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
);
526 window
->DisableDeviceSensor(SENSOR_ORIENTATION
);
528 case eAbsoluteDeviceOrientation
:
529 #ifdef MOZ_WIDGET_ANDROID
530 window
->DisableDeviceSensor(SENSOR_ROTATION_VECTOR
);
532 window
->DisableDeviceSensor(SENSOR_ORIENTATION
);
535 window
->DisableDeviceSensor(SENSOR_ACCELERATION
);
536 window
->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION
);
537 window
->DisableDeviceSensor(SENSOR_GYROSCOPE
);
539 case eDeviceProximity
:
541 window
->DisableDeviceSensor(SENSOR_PROXIMITY
);
544 window
->DisableDeviceSensor(SENSOR_LIGHT
);
546 #if defined(MOZ_WIDGET_ANDROID)
547 case eOrientationChange
:
548 window
->DisableOrientationChangeListener();
552 NS_WARNING("Disabling an unknown device sensor.");
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;
563 mTarget
->EventListenerRemoved(aUserType
);
565 if (mIsMainThreadELM
&& mTarget
) {
566 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
,
571 void EventListenerManager::RemoveEventListenerInternal(
572 EventListenerHolder aListenerHolder
, EventMessage aEventMessage
,
573 nsAtom
* aUserType
, const EventListenerFlags
& aFlags
, bool aAllEvents
) {
574 if (!aListenerHolder
|| !aEventMessage
|| mClearingListeners
) {
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
);
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
) {
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
618 if (MOZ_UNLIKELY(aListener
->mAllEvents
)) {
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
630 if (!aEvent
->mFlags
.mInSystemGroup
&& !aListener
->mIsChrome
) {
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
) {
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
) {
682 EventMessage message
=
683 GetEventMessageAndAtomForListener(aType
, getter_AddRefs(atom
));
684 RemoveEventListenerInternal(std::move(aListenerHolder
), message
, atom
,
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
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)) {
704 EventListenerManager::Listener
* EventListenerManager::SetEventHandlerInternal(
705 nsAtom
* aName
, const TypedEventHandler
& aTypedHandler
,
706 bool aPermitUntrustedEvents
) {
709 EventMessage eventMessage
= GetEventMessage(aName
);
710 Listener
* listener
= FindEventHandler(eventMessage
, aName
);
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
,
724 listener
= FindEventHandler(eventMessage
, aName
);
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;
751 nsresult
EventListenerManager::SetEventHandler(nsAtom
* aName
,
752 const nsAString
& aBody
,
753 bool aDeferCompilation
,
754 bool aPermitUntrustedEvents
,
756 nsCOMPtr
<Document
> doc
;
757 nsCOMPtr
<nsIScriptGlobalObject
> global
=
758 GetScriptGlobalAndDocument(getter_AddRefs(doc
));
761 // This can happen; for example this document might have been
767 // return early preventing the event listener from being added
768 // 'doc' is fetched above
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
;
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
);
787 bool allowsInlineScript
= true;
788 rv
= csp
->GetAllowsInline(
789 nsIContentPolicy::TYPE_SCRIPT
,
790 EmptyString(), // aNonce
791 true, // aParserCreated (true because attribute event handler)
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
) {
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
);
825 void EventListenerManager::RemoveEventHandler(nsAtom
* aName
) {
826 if (mClearingListeners
) {
830 EventMessage eventMessage
= GetEventMessage(aName
);
831 Listener
* listener
= FindEventHandler(eventMessage
, aName
);
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
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
;
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
);
908 nsAutoCString
url(NS_LITERAL_CSTRING("-moz-evil:lying-event-listener"));
910 MOZ_ASSERT(aElement
);
911 nsIURI
* uri
= aElement
->OwnerDoc()->GetDocumentURI();
916 nsCOMPtr
<nsPIDOMWindowInner
> win
= do_QueryInterface(mTarget
);
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
))) {
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
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
);
994 RefPtr
<EventHandlerNonNull
> handlerCallback
= new EventHandlerNonNull(
995 static_cast<JSContext
*>(nullptr), handler
, handlerGlobal
,
996 /* aIncumbentGlobal = */ nullptr);
997 jsEventHandler
->SetHandler(handlerCallback
);
1003 nsresult
EventListenerManager::HandleEventSubType(Listener
* aListener
,
1005 EventTarget
* aCurrentTarget
) {
1006 nsresult result
= NS_OK
;
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
);
1022 // Event::currentTarget is set in EventDispatcher.
1023 if (listenerHolder
.HasWebIDLCallback()) {
1025 listenerHolder
.GetWebIDLCallback()->HandleEvent(aCurrentTarget
,
1027 result
= rv
.StealNSResult();
1029 // listenerHolder is holding a stack ref here.
1030 result
= MOZ_KnownLive(listenerHolder
.GetXPCOMCallback())
1031 ->HandleEvent(aDOMEvent
);
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
;
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;
1092 global
= callback
->IncumbentGlobalOrNull();
1095 innerWindow
= global
->AsInnerWindow(); // Can be nullptr
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
,
1116 EventTarget
* aCurrentTarget
,
1117 nsEventStatus
* aEventStatus
,
1118 bool aItemInShadowTree
) {
1119 // Set the value of the internal PreventDefault flag properly based on
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
;
1144 nsAutoTObserverArray
<Listener
, 2>::EndLimitedIterator
iter(mListeners
);
1145 Maybe
<EventMessageAutoOverride
> legacyAutoOverride
;
1146 while (iter
.HasMore()) {
1147 if (aEvent
->mFlags
.mImmediatePropagationStopped
) {
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
1154 if (ListenerCanHandle(listener
, aEvent
, eventMessage
)) {
1156 hasListenerForCurrentGroup
=
1157 hasListenerForCurrentGroup
||
1158 listener
->mFlags
.mInSystemGroup
== aEvent
->mFlags
.mInSystemGroup
;
1159 if (listener
->IsListening(aEvent
) &&
1160 (aEvent
->IsTrusted() || listener
->mFlags
.mAllowUntrustedEvents
)) {
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
);
1170 if (!aEvent
->mCurrentTarget
) {
1171 aEvent
->mCurrentTarget
= aCurrentTarget
->GetTargetForDOMEvent();
1172 if (!aEvent
->mCurrentTarget
) {
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
);
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;
1219 oldWindowEvent
= innerWindow
->SetEvent(*aDOMEvent
);
1223 HandleEventSubType(listener
, *aDOMEvent
, aCurrentTarget
);
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.
1252 EventMessage legacyEventMessage
= GetLegacyEventMessage(eventMessage
);
1253 if (legacyEventMessage
== eventMessage
) {
1254 break; // There's no legacy version of our event; no need to recheck.
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;
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() {
1308 RemoveAllListenersSilently();
1311 void EventListenerManager::AddEventListener(const nsAString
& aType
,
1312 EventListenerHolder aListenerHolder
,
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();
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
,
1343 void EventListenerManager::RemoveEventListener(
1344 const nsAString
& aType
, EventListenerHolder aListenerHolder
,
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();
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
,
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
) {
1401 uint32_t EventListenerManager::MutationListenerBits() {
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
);
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 {
1427 aEventNameWithOn
->ToString(name
);
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
) {
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
);
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,
1460 nsAutoString eventType
;
1461 if (listener
.mAllEvents
) {
1462 eventType
.SetIsVoid(true);
1463 } else if (listener
.mListenerType
== Listener::eNoListener
) {
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();
1477 // This will be null for cross-compartment event listeners
1478 // which have been destroyed.
1482 } else if (listener
.mListenerType
== Listener::eWebIDLListener
) {
1483 EventListener
* listenerCallback
= listener
.mListener
.GetWebIDLCallback();
1484 callback
= listenerCallback
->CallbackOrNull();
1485 callbackGlobal
= listenerCallback
->CallbackGlobalOrNull();
1487 // This will be null for cross-compartment event listeners
1488 // which have been destroyed.
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());
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
) {
1513 void EventListenerManager::SetEventHandler(nsAtom
* aEventName
,
1514 EventHandlerNonNull
* aHandler
) {
1516 RemoveEventHandler(aEventName
);
1520 // Untrusted events are always permitted for non-chrome script
1522 SetEventHandlerInternal(
1523 aEventName
, TypedEventHandler(aHandler
),
1524 !mIsMainThreadELM
|| !nsContentUtils::IsCallerChrome());
1527 void EventListenerManager::SetEventHandler(
1528 OnErrorEventHandlerNonNull
* aHandler
) {
1530 RemoveEventHandler(nsGkAtoms::onerror
);
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
),
1542 void EventListenerManager::SetEventHandler(
1543 OnBeforeUnloadEventHandlerNonNull
* aHandler
) {
1545 RemoveEventHandler(nsGkAtoms::onbeforeunload
);
1549 // Untrusted events are always permitted for non-chrome script
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
);
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
);
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
)) {
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
)) {
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
)) {
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
) {
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
));
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
;
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()) {
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());
1726 if (nsCOMPtr
<nsPIDOMWindowInner
> win
= GetTargetAsInnerWindow()) {
1727 doc
= win
->GetExtantDoc();
1728 global
= do_QueryInterface(win
);
1730 global
= do_QueryInterface(mTarget
);
1735 return global
.forget();
1738 } // namespace mozilla