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 "js/ColumnNumber.h" // JS::ColumnNumberZeroOrigin
11 #include "js/loader/LoadedScript.h"
12 #include "mozilla/BasicEvents.h"
13 #include "mozilla/BinarySearch.h"
14 #include "mozilla/CycleCollectedJSRuntime.h"
15 #include "mozilla/DOMEventTargetHelper.h"
16 #include "mozilla/EventDispatcher.h"
17 #include "mozilla/EventListenerManager.h"
18 #include "mozilla/HalSensor.h"
19 #include "mozilla/InternalMutationEvent.h"
20 #include "mozilla/JSEventHandler.h"
21 #include "mozilla/Maybe.h"
22 #include "mozilla/MemoryReporting.h"
23 #include "mozilla/Preferences.h"
24 #include "mozilla/PresShell.h"
25 #include "mozilla/dom/AbortSignal.h"
26 #include "mozilla/dom/BindingUtils.h"
27 #include "mozilla/dom/EventCallbackDebuggerNotification.h"
28 #include "mozilla/dom/Element.h"
29 #include "mozilla/dom/Event.h"
30 #include "mozilla/dom/EventTargetBinding.h"
31 #include "mozilla/dom/PopupBlocker.h"
32 #include "mozilla/dom/RequestBinding.h"
33 #include "mozilla/dom/ScriptLoader.h"
34 #include "mozilla/dom/ScriptSettings.h"
35 #include "mozilla/dom/TouchEvent.h"
36 #include "mozilla/dom/UserActivation.h"
37 #include "mozilla/ScopeExit.h"
38 #include "mozilla/StaticPrefs_dom.h"
39 #include "mozilla/TimeStamp.h"
40 #include "mozilla/dom/ChromeUtils.h"
42 #include "EventListenerService.h"
44 #include "nsContentUtils.h"
47 #include "nsGenericHTMLElement.h"
48 #include "nsGkAtoms.h"
49 #include "nsIContent.h"
50 #include "nsIContentSecurityPolicy.h"
51 #include "mozilla/dom/Document.h"
52 #include "nsIScriptGlobalObject.h"
53 #include "nsISupports.h"
54 #include "nsJSUtils.h"
55 #include "nsNameSpaceManager.h"
56 #include "nsPIDOMWindow.h"
57 #include "nsPrintfCString.h"
58 #include "nsSandboxFlags.h"
59 #include "xpcpublic.h"
61 #include "nsDisplayList.h"
62 #include "nsPIWindowRoot.h"
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 class ListenerMapEntryComparator
{
101 explicit ListenerMapEntryComparator(nsAtom
* aTarget
)
102 : mAddressOfEventType(reinterpret_cast<uintptr_t>(aTarget
)) {}
105 const EventListenerManager::EventListenerMapEntry
& aEntry
) const {
106 uintptr_t value
= reinterpret_cast<uintptr_t>(aEntry
.mTypeAtom
.get());
107 if (mAddressOfEventType
== value
) {
111 if (mAddressOfEventType
< value
) {
119 const uintptr_t mAddressOfEventType
; // the address of the atom, can be 0
122 uint32_t EventListenerManager::sMainThreadCreatedCount
= 0;
124 EventListenerManagerBase::EventListenerManagerBase()
125 : mMayHaveDOMActivateEventListener(false),
126 mMayHavePaintEventListener(false),
127 mMayHaveMutationListeners(false),
128 mMayHaveCapturingListeners(false),
129 mMayHaveSystemGroupListeners(false),
130 mMayHaveTouchEventListener(false),
131 mMayHaveMouseEnterLeaveEventListener(false),
132 mMayHavePointerEnterLeaveEventListener(false),
133 mMayHaveSelectionChangeEventListener(false),
134 mMayHaveFormSelectEventListener(false),
135 mMayHaveTransitionEventListener(false),
136 mClearingListeners(false),
137 mIsMainThreadELM(NS_IsMainThread()),
138 mMayHaveListenersForUntrustedEvents(false) {
139 ClearNoListenersForEvents();
140 static_assert(sizeof(EventListenerManagerBase
) == sizeof(uint64_t),
141 "Keep the size of EventListenerManagerBase size compact!");
144 EventListenerManager::EventListenerManager(EventTarget
* aTarget
)
145 : EventListenerManagerBase(), mTarget(aTarget
) {
146 NS_ASSERTION(aTarget
, "unexpected null pointer");
148 if (mIsMainThreadELM
) {
149 ++sMainThreadCreatedCount
;
153 EventListenerManager::~EventListenerManager() {
154 // If your code fails this assertion, a possible reason is that
155 // a class did not call our Disconnect() manually. Note that
156 // this class can have Disconnect called in one of two ways:
157 // if it is part of a cycle, then in Unlink() (such a cycle
158 // would be with one of the listeners, not mTarget which is weak).
159 // If not part of a cycle, then Disconnect must be called manually,
160 // typically from the destructor of the owner class (mTarget).
161 // XXX azakai: Is there any reason to not just call Disconnect
162 // from right here, if not previously called?
163 NS_ASSERTION(!mTarget
, "didn't call Disconnect");
164 RemoveAllListenersSilently();
167 void EventListenerManager::RemoveAllListenersSilently() {
168 if (mClearingListeners
) {
171 mClearingListeners
= true;
172 mListenerMap
.Clear();
173 mClearingListeners
= false;
176 inline void ImplCycleCollectionTraverse(
177 nsCycleCollectionTraversalCallback
& aCallback
,
178 EventListenerManager::EventListenerMap
& aField
, const char* aName
,
179 uint32_t aFlags
= 0) {
180 if (MOZ_UNLIKELY(aCallback
.WantDebugInfo())) {
182 name
.AppendASCII(aName
);
183 name
.AppendLiteral(" mEntries[i] event=");
184 size_t entryPrefixLen
= name
.Length();
185 for (const auto& entry
: aField
.mEntries
) {
186 if (entry
.mTypeAtom
) {
187 name
.Replace(entryPrefixLen
, name
.Length() - entryPrefixLen
,
188 nsAtomCString(entry
.mTypeAtom
));
190 name
.Replace(entryPrefixLen
, name
.Length() - entryPrefixLen
,
193 ImplCycleCollectionTraverse(aCallback
, *entry
.mListeners
, name
.get());
196 for (const auto& entry
: aField
.mEntries
) {
197 ImplCycleCollectionTraverse(aCallback
, *entry
.mListeners
,
198 ".mEntries[i].mListeners");
203 inline void ImplCycleCollectionTraverse(
204 nsCycleCollectionTraversalCallback
& aCallback
,
205 EventListenerManager::Listener
& aField
, const char* aName
,
207 if (MOZ_UNLIKELY(aCallback
.WantDebugInfo())) {
209 name
.AppendASCII(aName
);
210 name
.AppendLiteral(" listenerType=");
211 name
.AppendInt(aField
.mListenerType
);
212 name
.AppendLiteral(" ");
213 CycleCollectionNoteChild(aCallback
, aField
.mListener
.GetISupports(),
216 CycleCollectionNoteChild(aCallback
, aField
.mListener
.GetISupports(), aName
,
220 CycleCollectionNoteChild(aCallback
, aField
.mSignalFollower
.get(),
221 "mSignalFollower", aFlags
);
224 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager
)
226 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager
)
227 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerMap
);
228 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
230 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager
)
232 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
234 nsPIDOMWindowInner
* EventListenerManager::GetInnerWindowForTarget() {
235 if (nsINode
* node
= nsINode::FromEventTargetOrNull(mTarget
)) {
236 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
237 // if that's the XBL document?
238 return node
->OwnerDoc()->GetInnerWindow();
241 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow();
245 already_AddRefed
<nsPIDOMWindowInner
>
246 EventListenerManager::GetTargetAsInnerWindow() const {
247 nsCOMPtr
<nsPIDOMWindowInner
> window
=
248 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget
);
249 return window
.forget();
252 void EventListenerManager::AddEventListenerInternal(
253 EventListenerHolder aListenerHolder
, EventMessage aEventMessage
,
254 nsAtom
* aTypeAtom
, const EventListenerFlags
& aFlags
, bool aHandler
,
255 bool aAllEvents
, AbortSignal
* aSignal
) {
256 MOZ_ASSERT((aEventMessage
&& aTypeAtom
) || aAllEvents
, // all-events listener
259 aEventMessage
!= eUnidentifiedEvent
&& !aAllEvents
,
260 aTypeAtom
== nsContentUtils::GetEventTypeFromMessage(aEventMessage
));
262 if (!aListenerHolder
|| mClearingListeners
) {
266 if (aSignal
&& aSignal
->Aborted()) {
270 // Since there is no public API to call us with an EventListenerHolder, we
271 // know that there's an EventListenerHolder on the stack holding a strong ref
274 RefPtr
<ListenerArray
> listeners
=
275 aAllEvents
? mListenerMap
.GetOrCreateListenersForAllEvents()
276 : mListenerMap
.GetOrCreateListenersForType(aTypeAtom
);
278 for (const Listener
& listener
: listeners
->NonObservingRange()) {
279 // mListener == aListenerHolder is the last one, since it can be a bit slow.
280 if (listener
.mListenerIsHandler
== aHandler
&&
281 listener
.mFlags
.EqualsForAddition(aFlags
) &&
282 listener
.mListener
== aListenerHolder
) {
287 ClearNoListenersForEvents();
288 mNoListenerForEventAtom
= nullptr;
290 Listener
* listener
= listeners
->AppendElement();
291 listener
->mFlags
= aFlags
;
292 listener
->mListenerIsHandler
= aHandler
;
293 listener
->mHandlerIsString
= false;
294 listener
->mAllEvents
= aAllEvents
;
296 if (listener
->mFlags
.mAllowUntrustedEvents
) {
297 mMayHaveListenersForUntrustedEvents
= true;
300 // Detect the type of event listener.
301 if (aFlags
.mListenerIsJSListener
) {
302 MOZ_ASSERT(!aListenerHolder
.HasWebIDLCallback());
303 listener
->mListenerType
= Listener::eJSEventListener
;
304 } else if (aListenerHolder
.HasWebIDLCallback()) {
305 listener
->mListenerType
= Listener::eWebIDLListener
;
307 listener
->mListenerType
= Listener::eNativeListener
;
309 listener
->mListener
= std::move(aListenerHolder
);
312 listener
->mSignalFollower
=
313 new ListenerSignalFollower(this, listener
, aTypeAtom
);
314 listener
->mSignalFollower
->Follow(aSignal
);
317 if (aFlags
.mInSystemGroup
) {
318 mMayHaveSystemGroupListeners
= true;
320 if (aFlags
.mCapture
) {
321 mMayHaveCapturingListeners
= true;
324 // Events which are not supported in the running environment is mapped to
325 // eUnidentifiedEvent. Then, we need to consider the proper event message
326 // with comparing the atom.
328 EventMessage resolvedEventMessage
= aEventMessage
;
329 if (resolvedEventMessage
== eUnidentifiedEvent
&& aTypeAtom
->IsStatic()) {
330 // TouchEvents are registered only when
331 // nsContentUtils::InitializeTouchEventTable() is called.
332 if (aTypeAtom
== nsGkAtoms::ontouchstart
) {
333 resolvedEventMessage
= eTouchStart
;
334 } else if (aTypeAtom
== nsGkAtoms::ontouchend
) {
335 resolvedEventMessage
= eTouchEnd
;
336 } else if (aTypeAtom
== nsGkAtoms::ontouchmove
) {
337 resolvedEventMessage
= eTouchMove
;
338 } else if (aTypeAtom
== nsGkAtoms::ontouchcancel
) {
339 resolvedEventMessage
= eTouchCancel
;
343 switch (resolvedEventMessage
) {
345 mMayHavePaintEventListener
= true;
346 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
347 window
->SetHasPaintEventListeners();
350 case eLegacyDOMActivate
:
351 mMayHaveDOMActivateEventListener
= true;
352 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
353 window
->SetHasDOMActivateEventListeners();
356 case eLegacySubtreeModified
:
357 case eLegacyNodeInserted
:
358 case eLegacyNodeRemoved
:
359 case eLegacyNodeRemovedFromDocument
:
360 case eLegacyNodeInsertedIntoDocument
:
361 case eLegacyAttrModified
:
362 case eLegacyCharacterDataModified
:
364 MOZ_ASSERT(!aFlags
.mInSystemGroup
,
365 "Legacy mutation events shouldn't be handled by ourselves");
366 MOZ_ASSERT(listener
->mListenerType
!= Listener::eNativeListener
,
367 "Legacy mutation events shouldn't be handled in C++ code");
368 if (nsINode
* targetNode
= nsINode::FromEventTargetOrNull(mTarget
)) {
369 MOZ_ASSERT(!nsContentUtils::IsChromeDoc(targetNode
->OwnerDoc()),
370 "Legacy mutation events shouldn't be handled in chrome "
372 MOZ_ASSERT(!targetNode
->IsInNativeAnonymousSubtree(),
373 "Legacy mutation events shouldn't listen to mutations in "
374 "native anonymous subtrees");
376 #endif // #ifdef DEBUG
377 // For mutation listeners, we need to update the global bit on the DOM
378 // window. Otherwise we won't actually fire the mutation event.
379 mMayHaveMutationListeners
= true;
380 // Go from our target to the nearest enclosing DOM window.
381 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
382 if (Document
* doc
= window
->GetExtantDoc()) {
383 doc
->WarnOnceAbout(DeprecatedOperations::eMutationEvent
);
385 // If resolvedEventMessage is eLegacySubtreeModified, we need to
386 // listen all mutations. nsContentUtils::HasMutationListeners relies
388 window
->SetMutationListeners(
389 (resolvedEventMessage
== eLegacySubtreeModified
)
391 : MutationBitForEventType(resolvedEventMessage
));
396 mMayHavePointerEnterLeaveEventListener
= true;
397 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
398 NS_WARNING_ASSERTION(
399 !nsContentUtils::IsChromeDoc(window
->GetExtantDoc()),
400 "Please do not use pointerenter/leave events in chrome. "
401 "They are slower than pointerover/out!");
402 window
->SetHasPointerEnterLeaveEventListeners();
405 case eGamepadButtonDown
:
406 case eGamepadButtonUp
:
407 case eGamepadAxisMove
:
408 case eGamepadConnected
:
409 case eGamepadDisconnected
:
410 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
411 window
->SetHasGamepadEventListener();
414 case eDeviceOrientation
:
415 case eDeviceOrientationAbsolute
:
419 #if defined(MOZ_WIDGET_ANDROID)
420 case eOrientationChange
:
421 #endif // #if defined(MOZ_WIDGET_ANDROID)
422 EnableDevice(aTypeAtom
);
428 mMayHaveTouchEventListener
= true;
429 // we don't want touchevent listeners added by scrollbars to flip this
430 // flag so we ignore listeners created with system event flag
431 if (!aFlags
.mInSystemGroup
) {
432 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
433 window
->SetHasTouchEventListeners();
439 mMayHaveMouseEnterLeaveEventListener
= true;
440 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
441 NS_WARNING_ASSERTION(
442 !nsContentUtils::IsChromeDoc(window
->GetExtantDoc()),
443 "Please do not use mouseenter/leave events in chrome. "
444 "They are slower than mouseover/out!");
445 window
->SetHasMouseEnterLeaveEventListeners();
448 case eEditorBeforeInput
:
449 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
450 window
->SetHasBeforeInputEventListenersForTelemetry();
453 case eSelectionChange
:
454 mMayHaveSelectionChangeEventListener
= true;
455 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
456 window
->SetHasSelectionChangeEventListeners();
460 mMayHaveFormSelectEventListener
= true;
461 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
462 window
->SetHasFormSelectEventListeners();
466 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
467 if (Document
* doc
= window
->GetExtantDoc()) {
468 doc
->SetUseCounter(eUseCounter_custom_onstart
);
473 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
474 if (Document
* doc
= window
->GetExtantDoc()) {
475 doc
->SetUseCounter(eUseCounter_custom_onbounce
);
480 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
481 if (Document
* doc
= window
->GetExtantDoc()) {
482 doc
->SetUseCounter(eUseCounter_custom_onfinish
);
486 case eScrollPortOverflow
:
487 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
488 if (Document
* doc
= window
->GetExtantDoc()) {
489 doc
->SetUseCounter(eUseCounter_custom_onoverflow
);
493 case eScrollPortUnderflow
:
494 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
495 if (Document
* doc
= window
->GetExtantDoc()) {
496 doc
->SetUseCounter(eUseCounter_custom_onunderflow
);
500 case eLegacyMouseLineOrPageScroll
:
501 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
502 if (Document
* doc
= window
->GetExtantDoc()) {
503 doc
->SetUseCounter(eUseCounter_custom_ondommousescroll
);
507 case eLegacyMousePixelScroll
:
508 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
509 if (Document
* doc
= window
->GetExtantDoc()) {
510 doc
->SetUseCounter(eUseCounter_custom_onmozmousepixelscroll
);
514 case eTransitionStart
:
517 case eTransitionCancel
:
518 case eWebkitTransitionEnd
:
519 mMayHaveTransitionEventListener
= true;
520 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
521 window
->SetHasTransitionEventListeners();
524 case eFormCheckboxStateChange
:
525 nsContentUtils::SetMayHaveFormCheckboxStateChangeListeners();
527 case eFormRadioStateChange
:
528 nsContentUtils::SetMayHaveFormRadioStateChangeListeners();
531 // XXX Use NS_ASSERTION here to print resolvedEventMessage since
532 // MOZ_ASSERT can take only string literal, not pointer to
535 resolvedEventMessage
< eLegacyMutationEventFirst
||
536 resolvedEventMessage
> eLegacyMutationEventLast
,
537 nsPrintfCString("You added new mutation event, but it's not "
538 "handled above, resolvedEventMessage=%s",
539 ToChar(resolvedEventMessage
))
541 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onpointerenter
,
542 nsPrintfCString("resolvedEventMessage=%s",
543 ToChar(resolvedEventMessage
))
545 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onpointerleave
,
546 nsPrintfCString("resolvedEventMessage=%s",
547 ToChar(resolvedEventMessage
))
550 resolvedEventMessage
< eGamepadEventFirst
||
551 resolvedEventMessage
> eGamepadEventLast
,
552 nsPrintfCString("You added new gamepad event, but it's not "
553 "handled above, resolvedEventMessage=%s",
554 ToChar(resolvedEventMessage
))
556 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondeviceorientation
,
557 nsPrintfCString("resolvedEventMessage=%s",
558 ToChar(resolvedEventMessage
))
560 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondeviceorientationabsolute
,
561 nsPrintfCString("resolvedEventMessage=%s",
562 ToChar(resolvedEventMessage
))
564 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onuserproximity
,
565 nsPrintfCString("resolvedEventMessage=%s",
566 ToChar(resolvedEventMessage
))
568 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondevicelight
,
569 nsPrintfCString("resolvedEventMessage=%s",
570 ToChar(resolvedEventMessage
))
572 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondevicemotion
,
573 nsPrintfCString("resolvedEventMessage=%s",
574 ToChar(resolvedEventMessage
))
576 #if defined(MOZ_WIDGET_ANDROID)
577 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onorientationchange
,
578 nsPrintfCString("resolvedEventMessage=%s",
579 ToChar(resolvedEventMessage
))
581 #endif // #if defined(MOZ_WIDGET_ANDROID)
582 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchstart
,
583 nsPrintfCString("resolvedEventMessage=%s",
584 ToChar(resolvedEventMessage
))
586 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchend
,
587 nsPrintfCString("resolvedEventMessage=%s",
588 ToChar(resolvedEventMessage
))
590 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchmove
,
591 nsPrintfCString("resolvedEventMessage=%s",
592 ToChar(resolvedEventMessage
))
594 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchcancel
,
595 nsPrintfCString("resolvedEventMessage=%s",
596 ToChar(resolvedEventMessage
))
598 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onmouseenter
,
599 nsPrintfCString("resolvedEventMessage=%s",
600 ToChar(resolvedEventMessage
))
602 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onmouseleave
,
603 nsPrintfCString("resolvedEventMessage=%s",
604 ToChar(resolvedEventMessage
))
606 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onbeforeinput
,
607 nsPrintfCString("resolvedEventMessage=%s",
608 ToChar(resolvedEventMessage
))
610 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onselectionchange
,
611 nsPrintfCString("resolvedEventMessage=%s",
612 ToChar(resolvedEventMessage
))
614 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onselect
,
615 nsPrintfCString("resolvedEventMessage=%s",
616 ToChar(resolvedEventMessage
))
618 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onstart
,
619 nsPrintfCString("resolvedEventMessage=%s",
620 ToChar(resolvedEventMessage
))
622 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onbounce
,
623 nsPrintfCString("resolvedEventMessage=%s",
624 ToChar(resolvedEventMessage
))
626 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onfinish
,
627 nsPrintfCString("resolvedEventMessage=%s",
628 ToChar(resolvedEventMessage
))
630 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onoverflow
,
631 nsPrintfCString("resolvedEventMessage=%s",
632 ToChar(resolvedEventMessage
))
634 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onunderflow
,
635 nsPrintfCString("resolvedEventMessage=%s",
636 ToChar(resolvedEventMessage
))
638 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onDOMMouseScroll
,
639 nsPrintfCString("resolvedEventMessage=%s",
640 ToChar(resolvedEventMessage
))
642 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onMozMousePixelScroll
,
643 nsPrintfCString("resolvedEventMessage=%s",
644 ToChar(resolvedEventMessage
))
650 if (mIsMainThreadELM
&& !aFlags
.mPassive
&& IsApzAwareEvent(aTypeAtom
)) {
651 ProcessApzAwareEventListenerAdd();
655 mTarget
->EventListenerAdded(aTypeAtom
);
658 if (mIsMainThreadELM
&& mTarget
) {
659 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
,
664 void EventListenerManager::ProcessApzAwareEventListenerAdd() {
665 Document
* doc
= nullptr;
667 // Mark the node as having apz aware listeners
668 if (nsINode
* node
= nsINode::FromEventTargetOrNull(mTarget
)) {
669 node
->SetMayBeApzAware();
670 doc
= node
->OwnerDoc();
673 // Schedule a paint so event regions on the layer tree gets updated
675 if (nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow()) {
676 doc
= window
->GetExtantDoc();
680 if (nsCOMPtr
<DOMEventTargetHelper
> helper
= do_QueryInterface(mTarget
)) {
681 if (nsPIDOMWindowInner
* window
= helper
->GetOwner()) {
682 doc
= window
->GetExtantDoc();
687 if (doc
&& gfxPlatform::AsyncPanZoomEnabled()) {
688 PresShell
* presShell
= doc
->GetPresShell();
690 nsIFrame
* f
= presShell
->GetRootFrame();
698 bool EventListenerManager::IsDeviceType(nsAtom
* aTypeAtom
) {
699 return aTypeAtom
== nsGkAtoms::ondeviceorientation
||
700 aTypeAtom
== nsGkAtoms::ondeviceorientationabsolute
||
701 aTypeAtom
== nsGkAtoms::ondevicemotion
||
702 aTypeAtom
== nsGkAtoms::ondevicelight
703 #if defined(MOZ_WIDGET_ANDROID)
704 || aTypeAtom
== nsGkAtoms::onorientationchange
706 || aTypeAtom
== nsGkAtoms::onuserproximity
;
709 void EventListenerManager::EnableDevice(nsAtom
* aTypeAtom
) {
710 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow();
715 if (aTypeAtom
== nsGkAtoms::ondeviceorientation
) {
716 #ifdef MOZ_WIDGET_ANDROID
717 // Falls back to SENSOR_ROTATION_VECTOR and SENSOR_ORIENTATION if
718 // unavailable on device.
719 window
->EnableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR
);
720 window
->EnableDeviceSensor(SENSOR_ROTATION_VECTOR
);
722 window
->EnableDeviceSensor(SENSOR_ORIENTATION
);
727 if (aTypeAtom
== nsGkAtoms::ondeviceorientationabsolute
) {
728 #ifdef MOZ_WIDGET_ANDROID
729 // Falls back to SENSOR_ORIENTATION if unavailable on device.
730 window
->EnableDeviceSensor(SENSOR_ROTATION_VECTOR
);
732 window
->EnableDeviceSensor(SENSOR_ORIENTATION
);
737 if (aTypeAtom
== nsGkAtoms::onuserproximity
) {
738 window
->EnableDeviceSensor(SENSOR_PROXIMITY
);
742 if (aTypeAtom
== nsGkAtoms::ondevicelight
) {
743 window
->EnableDeviceSensor(SENSOR_LIGHT
);
747 if (aTypeAtom
== nsGkAtoms::ondevicemotion
) {
748 window
->EnableDeviceSensor(SENSOR_ACCELERATION
);
749 window
->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION
);
750 window
->EnableDeviceSensor(SENSOR_GYROSCOPE
);
754 #if defined(MOZ_WIDGET_ANDROID)
755 if (aTypeAtom
== nsGkAtoms::onorientationchange
) {
756 window
->EnableOrientationChangeListener();
761 NS_WARNING("Enabling an unknown device sensor.");
764 void EventListenerManager::DisableDevice(nsAtom
* aTypeAtom
) {
765 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow();
770 if (aTypeAtom
== nsGkAtoms::ondeviceorientation
) {
771 #ifdef MOZ_WIDGET_ANDROID
772 // Disable all potential fallback sensors.
773 window
->DisableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR
);
774 window
->DisableDeviceSensor(SENSOR_ROTATION_VECTOR
);
776 window
->DisableDeviceSensor(SENSOR_ORIENTATION
);
780 if (aTypeAtom
== nsGkAtoms::ondeviceorientationabsolute
) {
781 #ifdef MOZ_WIDGET_ANDROID
782 window
->DisableDeviceSensor(SENSOR_ROTATION_VECTOR
);
784 window
->DisableDeviceSensor(SENSOR_ORIENTATION
);
788 if (aTypeAtom
== nsGkAtoms::ondevicemotion
) {
789 window
->DisableDeviceSensor(SENSOR_ACCELERATION
);
790 window
->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION
);
791 window
->DisableDeviceSensor(SENSOR_GYROSCOPE
);
795 if (aTypeAtom
== nsGkAtoms::onuserproximity
) {
796 window
->DisableDeviceSensor(SENSOR_PROXIMITY
);
800 if (aTypeAtom
== nsGkAtoms::ondevicelight
) {
801 window
->DisableDeviceSensor(SENSOR_LIGHT
);
805 #if defined(MOZ_WIDGET_ANDROID)
806 if (aTypeAtom
== nsGkAtoms::onorientationchange
) {
807 window
->DisableOrientationChangeListener();
812 NS_WARNING("Disabling an unknown device sensor.");
815 void EventListenerManager::NotifyEventListenerRemoved(nsAtom
* aUserType
) {
816 // If the following code is changed, other callsites of EventListenerRemoved
817 // and NotifyAboutMainThreadListenerChange should be changed too.
818 ClearNoListenersForEvents();
819 mNoListenerForEventAtom
= nullptr;
821 mTarget
->EventListenerRemoved(aUserType
);
823 if (mIsMainThreadELM
&& mTarget
) {
824 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
,
829 void EventListenerManager::RemoveEventListenerInternal(
830 EventListenerHolder aListenerHolder
, nsAtom
* aUserType
,
831 const EventListenerFlags
& aFlags
, bool aAllEvents
) {
832 if (!aListenerHolder
|| (!aUserType
&& !aAllEvents
) || mClearingListeners
) {
836 Maybe
<size_t> entryIndex
= aAllEvents
837 ? mListenerMap
.EntryIndexForAllEvents()
838 : mListenerMap
.EntryIndexForType(aUserType
);
843 ListenerArray
& listenerArray
= *mListenerMap
.mEntries
[*entryIndex
].mListeners
;
845 Maybe
<uint32_t> listenerIndex
= [&]() -> Maybe
<uint32_t> {
846 uint32_t count
= listenerArray
.Length();
847 for (uint32_t i
= 0; i
< count
; ++i
) {
848 Listener
* listener
= &listenerArray
.ElementAt(i
);
849 if (listener
->mListener
== aListenerHolder
&&
850 listener
->mFlags
.EqualsForRemoval(aFlags
)) {
857 if (!listenerIndex
) {
861 listenerArray
.RemoveElementAt(*listenerIndex
);
862 if (listenerArray
.IsEmpty()) {
863 mListenerMap
.mEntries
.RemoveElementAt(*entryIndex
);
866 RefPtr
<EventListenerManager
> kungFuDeathGrip(this);
868 NotifyEventListenerRemoved(aUserType
);
869 if (IsDeviceType(aUserType
)) {
870 DisableDevice(aUserType
);
875 static bool IsDefaultPassiveWhenOnRoot(EventMessage aMessage
) {
876 if (aMessage
== eTouchStart
|| aMessage
== eTouchMove
) {
879 if (aMessage
== eWheel
|| aMessage
== eLegacyMouseLineOrPageScroll
||
880 aMessage
== eLegacyMousePixelScroll
) {
881 return StaticPrefs::dom_event_default_to_passive_wheel_listeners();
886 static bool IsRootEventTarget(EventTarget
* aTarget
) {
890 if (aTarget
->IsInnerWindow()) {
893 const nsINode
* node
= nsINode::FromEventTarget(aTarget
);
897 Document
* doc
= node
->OwnerDoc();
898 return node
== doc
|| node
== doc
->GetRootElement() || node
== doc
->GetBody();
901 void EventListenerManager::MaybeMarkPassive(EventMessage aMessage
,
902 EventListenerFlags
& aFlags
) {
903 if (!mIsMainThreadELM
) {
906 if (!IsDefaultPassiveWhenOnRoot(aMessage
)) {
909 if (!IsRootEventTarget(mTarget
)) {
912 aFlags
.mPassive
= true;
915 void EventListenerManager::AddEventListenerByType(
916 EventListenerHolder aListenerHolder
, const nsAString
& aType
,
917 const EventListenerFlags
& aFlags
, const Optional
<bool>& aPassive
,
918 AbortSignal
* aSignal
) {
920 EventMessage message
=
921 GetEventMessageAndAtomForListener(aType
, getter_AddRefs(atom
));
923 EventListenerFlags flags
= aFlags
;
924 if (aPassive
.WasPassed()) {
925 flags
.mPassive
= aPassive
.Value();
927 MaybeMarkPassive(message
, flags
);
930 AddEventListenerInternal(std::move(aListenerHolder
), message
, atom
, flags
,
931 false, false, aSignal
);
934 void EventListenerManager::RemoveEventListenerByType(
935 EventListenerHolder aListenerHolder
, const nsAString
& aType
,
936 const EventListenerFlags
& aFlags
) {
938 (void)GetEventMessageAndAtomForListener(aType
, getter_AddRefs(atom
));
939 RemoveEventListenerInternal(std::move(aListenerHolder
), atom
, aFlags
);
942 EventListenerManager::Listener
* EventListenerManager::FindEventHandler(
944 // Run through the listeners for this type and see if a script
945 // listener is registered
946 RefPtr
<ListenerArray
> listeners
= mListenerMap
.GetListenersForType(aTypeAtom
);
951 uint32_t count
= listeners
->Length();
952 for (uint32_t i
= 0; i
< count
; ++i
) {
953 Listener
* listener
= &listeners
->ElementAt(i
);
954 if (listener
->mListenerIsHandler
) {
961 EventListenerManager::Listener
* EventListenerManager::SetEventHandlerInternal(
962 nsAtom
* aName
, const TypedEventHandler
& aTypedHandler
,
963 bool aPermitUntrustedEvents
) {
966 EventMessage eventMessage
= GetEventMessage(aName
);
967 Listener
* listener
= FindEventHandler(aName
);
970 // If we didn't find a script listener or no listeners existed
971 // create and add a new one.
972 EventListenerFlags flags
;
973 flags
.mListenerIsJSListener
= true;
974 MaybeMarkPassive(eventMessage
, flags
);
976 nsCOMPtr
<JSEventHandler
> jsEventHandler
;
977 NS_NewJSEventHandler(mTarget
, aName
, aTypedHandler
,
978 getter_AddRefs(jsEventHandler
));
979 AddEventListenerInternal(EventListenerHolder(jsEventHandler
), eventMessage
,
982 listener
= FindEventHandler(aName
);
984 JSEventHandler
* jsEventHandler
= listener
->GetJSEventHandler();
985 MOZ_ASSERT(jsEventHandler
,
986 "How can we have an event handler with no JSEventHandler?");
988 bool same
= jsEventHandler
->GetTypedEventHandler() == aTypedHandler
;
989 // Possibly the same listener, but update still the context and scope.
990 jsEventHandler
->SetHandler(aTypedHandler
);
991 if (mTarget
&& !same
) {
992 mTarget
->EventListenerRemoved(aName
);
993 mTarget
->EventListenerAdded(aName
);
995 if (mIsMainThreadELM
&& mTarget
) {
996 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
, aName
);
1000 // Set flag to indicate possible need for compilation later
1001 listener
->mHandlerIsString
= !aTypedHandler
.HasEventHandler();
1002 if (aPermitUntrustedEvents
) {
1003 listener
->mFlags
.mAllowUntrustedEvents
= true;
1004 mMayHaveListenersForUntrustedEvents
= true;
1010 nsresult
EventListenerManager::SetEventHandler(nsAtom
* aName
,
1011 const nsAString
& aBody
,
1012 bool aDeferCompilation
,
1013 bool aPermitUntrustedEvents
,
1014 Element
* aElement
) {
1015 auto removeEventHandler
= MakeScopeExit([&] { RemoveEventHandler(aName
); });
1017 nsCOMPtr
<Document
> doc
;
1018 nsCOMPtr
<nsIScriptGlobalObject
> global
=
1019 GetScriptGlobalAndDocument(getter_AddRefs(doc
));
1022 // This can happen; for example this document might have been
1027 nsresult rv
= NS_OK
;
1028 // return early preventing the event listener from being added
1029 // 'doc' is fetched above
1031 // Don't allow adding an event listener if the document is sandboxed
1032 // without 'allow-scripts'.
1033 if (doc
->HasScriptsBlockedBySandbox()) {
1034 return NS_ERROR_DOM_SECURITY_ERR
;
1037 // Perform CSP check
1038 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= doc
->GetCsp();
1039 uint32_t lineNum
= 0;
1040 JS::ColumnNumberZeroOrigin columnNum
;
1042 JSContext
* cx
= nsContentUtils::GetCurrentJSContext();
1043 if (cx
&& !JS::DescribeScriptedCaller(cx
, nullptr, &lineNum
, &columnNum
)) {
1044 JS_ClearPendingException(cx
);
1048 bool allowsInlineScript
= true;
1049 rv
= csp
->GetAllowsInline(
1050 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
,
1051 true, // aHasUnsafeHash
1053 true, // aParserCreated (true because attribute event handler)
1055 nullptr, // nsICSPEventListener
1056 aBody
, lineNum
, columnNum
.zeroOriginValue(), &allowsInlineScript
);
1057 NS_ENSURE_SUCCESS(rv
, rv
);
1059 // return early if CSP wants us to block inline scripts
1060 if (!allowsInlineScript
) {
1066 // This might be the first reference to this language in the global
1067 // We must init the language before we attempt to fetch its context.
1068 if (NS_FAILED(global
->EnsureScriptEnvironment())) {
1069 NS_WARNING("Failed to setup script environment for this language");
1070 // but fall through and let the inevitable failure below handle it.
1073 nsIScriptContext
* context
= global
->GetScriptContext();
1074 NS_ENSURE_TRUE(context
, NS_ERROR_FAILURE
);
1075 NS_ENSURE_STATE(global
->HasJSGlobal());
1077 removeEventHandler
.release();
1079 Listener
* listener
= SetEventHandlerInternal(aName
, TypedEventHandler(),
1080 aPermitUntrustedEvents
);
1082 if (!aDeferCompilation
) {
1083 return CompileEventHandlerInternal(listener
, aName
, &aBody
, aElement
);
1089 void EventListenerManager::RemoveEventHandler(nsAtom
* aName
) {
1090 if (mClearingListeners
) {
1094 Maybe
<size_t> entryIndex
= mListenerMap
.EntryIndexForType(aName
);
1099 ListenerArray
& listenerArray
= *mListenerMap
.mEntries
[*entryIndex
].mListeners
;
1101 Maybe
<uint32_t> listenerIndex
= [&]() -> Maybe
<uint32_t> {
1102 uint32_t count
= listenerArray
.Length();
1103 for (uint32_t i
= 0; i
< count
; ++i
) {
1104 Listener
* listener
= &listenerArray
.ElementAt(i
);
1105 if (listener
->mListenerIsHandler
) {
1112 if (!listenerIndex
) {
1116 listenerArray
.RemoveElementAt(*listenerIndex
);
1117 if (listenerArray
.IsEmpty()) {
1118 mListenerMap
.mEntries
.RemoveElementAt(*entryIndex
);
1121 RefPtr
<EventListenerManager
> kungFuDeathGrip(this);
1122 NotifyEventListenerRemoved(aName
);
1123 if (IsDeviceType(aName
)) {
1124 DisableDevice(aName
);
1128 nsresult
EventListenerManager::CompileEventHandlerInternal(
1129 Listener
* aListener
, nsAtom
* aTypeAtom
, const nsAString
* aBody
,
1130 Element
* aElement
) {
1131 MOZ_ASSERT(aListener
->GetJSEventHandler());
1132 MOZ_ASSERT(aListener
->mHandlerIsString
,
1133 "Why are we compiling a non-string JS listener?");
1134 JSEventHandler
* jsEventHandler
= aListener
->GetJSEventHandler();
1135 MOZ_ASSERT(!jsEventHandler
->GetTypedEventHandler().HasEventHandler(),
1136 "What is there to compile?");
1138 nsresult result
= NS_OK
;
1139 nsCOMPtr
<Document
> doc
;
1140 nsCOMPtr
<nsIScriptGlobalObject
> global
=
1141 GetScriptGlobalAndDocument(getter_AddRefs(doc
));
1142 NS_ENSURE_STATE(global
);
1144 // Activate JSAPI, and make sure that exceptions are reported on the right
1147 if (NS_WARN_IF(!jsapi
.Init(global
))) {
1148 return NS_ERROR_UNEXPECTED
;
1150 JSContext
* cx
= jsapi
.cx();
1152 nsAtom
* attrName
= aTypeAtom
;
1154 // Flag us as not a string so we don't keep trying to compile strings which
1155 // can't be compiled.
1156 aListener
->mHandlerIsString
= false;
1158 // mTarget may not be an Element if it's a window and we're
1159 // getting an inline event listener forwarded from <html:body> or
1160 // <html:frameset> or <xul:window> or the like.
1161 // XXX I don't like that we have to reference content from
1162 // here. The alternative is to store the event handler string on
1163 // the JSEventHandler itself, and that still doesn't address
1164 // the arg names issue.
1165 RefPtr
<Element
> element
= Element::FromEventTargetOrNull(mTarget
);
1166 MOZ_ASSERT(element
|| aBody
, "Where will we get our body?");
1167 nsAutoString handlerBody
;
1168 const nsAString
* body
= aBody
;
1170 if (aTypeAtom
== nsGkAtoms::onSVGLoad
) {
1171 attrName
= nsGkAtoms::onload
;
1172 } else if (aTypeAtom
== nsGkAtoms::onSVGScroll
) {
1173 attrName
= nsGkAtoms::onscroll
;
1174 } else if (aTypeAtom
== nsGkAtoms::onbeginEvent
) {
1175 attrName
= nsGkAtoms::onbegin
;
1176 } else if (aTypeAtom
== nsGkAtoms::onrepeatEvent
) {
1177 attrName
= nsGkAtoms::onrepeat
;
1178 } else if (aTypeAtom
== nsGkAtoms::onendEvent
) {
1179 attrName
= nsGkAtoms::onend
;
1180 } else if (aTypeAtom
== nsGkAtoms::onwebkitAnimationEnd
) {
1181 attrName
= nsGkAtoms::onwebkitanimationend
;
1182 } else if (aTypeAtom
== nsGkAtoms::onwebkitAnimationIteration
) {
1183 attrName
= nsGkAtoms::onwebkitanimationiteration
;
1184 } else if (aTypeAtom
== nsGkAtoms::onwebkitAnimationStart
) {
1185 attrName
= nsGkAtoms::onwebkitanimationstart
;
1186 } else if (aTypeAtom
== nsGkAtoms::onwebkitTransitionEnd
) {
1187 attrName
= nsGkAtoms::onwebkittransitionend
;
1190 element
->GetAttr(attrName
, handlerBody
);
1191 body
= &handlerBody
;
1194 aListener
= nullptr;
1196 nsAutoCString
url("-moz-evil:lying-event-listener"_ns
);
1198 MOZ_ASSERT(aElement
);
1199 nsIURI
* uri
= aElement
->OwnerDoc()->GetDocumentURI();
1204 nsCOMPtr
<nsPIDOMWindowInner
> win
=
1205 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget
);
1207 const char** argNames
;
1208 nsContentUtils::GetEventArgNames(aElement
->GetNameSpaceID(), aTypeAtom
, win
,
1209 &argCount
, &argNames
);
1211 // Wrap the event target, so that we can use it as the scope for the event
1212 // handler. Note that mTarget is different from aElement in the <body> case,
1213 // where mTarget is a Window.
1215 // The wrapScope doesn't really matter here, because the target will create
1216 // its reflector in the proper scope, and then we'll enter that realm.
1217 JS::Rooted
<JSObject
*> wrapScope(cx
, global
->GetGlobalJSObject());
1218 JS::Rooted
<JS::Value
> v(cx
);
1220 JSAutoRealm
ar(cx
, wrapScope
);
1221 nsresult rv
= nsContentUtils::WrapNative(cx
, mTarget
, &v
,
1222 /* aAllowWrapping = */ false);
1223 if (NS_WARN_IF(NS_FAILED(rv
))) {
1228 JS::Rooted
<JSObject
*> target(cx
, &v
.toObject());
1229 JSAutoRealm
ar(cx
, target
);
1231 // Now that we've entered the realm we actually care about, create our
1232 // scope chain. Note that we start with |element|, not aElement, because
1233 // mTarget is different from aElement in the <body> case, where mTarget is a
1234 // Window, and in that case we do not want the scope chain to include the body
1236 JS::RootedVector
<JSObject
*> scopeChain(cx
);
1237 if (!nsJSUtils::GetScopeChainForElement(cx
, element
, &scopeChain
)) {
1238 return NS_ERROR_OUT_OF_MEMORY
;
1241 nsDependentAtomString
str(attrName
);
1242 // Most of our names are short enough that we don't even have to malloc
1243 // the JS string stuff, so don't worry about playing games with
1244 // refcounting XPCOM stringbuffers.
1245 JS::Rooted
<JSString
*> jsStr(
1246 cx
, JS_NewUCStringCopyN(cx
, str
.BeginReading(), str
.Length()));
1247 NS_ENSURE_TRUE(jsStr
, NS_ERROR_OUT_OF_MEMORY
);
1249 // Get the reflector for |aElement|, so that we can pass to setElement.
1250 if (NS_WARN_IF(!GetOrCreateDOMReflector(cx
, aElement
, &v
))) {
1251 return NS_ERROR_FAILURE
;
1254 RefPtr
<JS::loader::ScriptFetchOptions
> fetchOptions
=
1255 new JS::loader::ScriptFetchOptions(
1256 CORS_NONE
, aElement
->OwnerDoc()->GetReferrerPolicy(),
1257 /* aNonce = */ u
""_ns
, RequestPriority::Auto
,
1258 JS::loader::ParserMetadata::NotParserInserted
,
1259 aElement
->OwnerDoc()->NodePrincipal());
1261 RefPtr
<JS::loader::EventScript
> eventScript
=
1262 new JS::loader::EventScript(fetchOptions
, uri
);
1264 JS::CompileOptions
options(cx
);
1265 // Use line 0 to make the function body starts from line 1.
1266 options
.setIntroductionType("eventHandler")
1267 .setFileAndLine(url
.get(), 0)
1268 .setDeferDebugMetadata(true);
1270 JS::Rooted
<JSObject
*> handler(cx
);
1271 result
= nsJSUtils::CompileFunction(jsapi
, scopeChain
, options
,
1272 nsAtomCString(aTypeAtom
), argCount
,
1273 argNames
, *body
, handler
.address());
1274 NS_ENSURE_SUCCESS(result
, result
);
1275 NS_ENSURE_TRUE(handler
, NS_ERROR_FAILURE
);
1277 JS::Rooted
<JS::Value
> privateValue(cx
, JS::PrivateValue(eventScript
));
1278 result
= nsJSUtils::UpdateFunctionDebugMetadata(jsapi
, handler
, options
,
1279 jsStr
, privateValue
);
1280 NS_ENSURE_SUCCESS(result
, result
);
1282 MOZ_ASSERT(js::IsObjectInContextCompartment(handler
, cx
));
1283 JS::Rooted
<JSObject
*> handlerGlobal(cx
, JS::CurrentGlobalOrNull(cx
));
1285 if (jsEventHandler
->EventName() == nsGkAtoms::onerror
&& win
) {
1286 RefPtr
<OnErrorEventHandlerNonNull
> handlerCallback
=
1287 new OnErrorEventHandlerNonNull(static_cast<JSContext
*>(nullptr),
1288 handler
, handlerGlobal
,
1289 /* aIncumbentGlobal = */ nullptr);
1290 jsEventHandler
->SetHandler(handlerCallback
);
1291 } else if (jsEventHandler
->EventName() == nsGkAtoms::onbeforeunload
&& win
) {
1292 RefPtr
<OnBeforeUnloadEventHandlerNonNull
> handlerCallback
=
1293 new OnBeforeUnloadEventHandlerNonNull(static_cast<JSContext
*>(nullptr),
1294 handler
, handlerGlobal
,
1295 /* aIncumbentGlobal = */ nullptr);
1296 jsEventHandler
->SetHandler(handlerCallback
);
1298 RefPtr
<EventHandlerNonNull
> handlerCallback
= new EventHandlerNonNull(
1299 static_cast<JSContext
*>(nullptr), handler
, handlerGlobal
,
1300 /* aIncumbentGlobal = */ nullptr);
1301 jsEventHandler
->SetHandler(handlerCallback
);
1307 bool EventListenerManager::HandleEventSingleListener(
1308 Listener
* aListener
, nsAtom
* aTypeAtom
, WidgetEvent
* aEvent
,
1309 Event
* aDOMEvent
, EventTarget
* aCurrentTarget
, bool aItemInShadowTree
) {
1310 if (!aEvent
->mCurrentTarget
) {
1311 aEvent
->mCurrentTarget
= aCurrentTarget
->GetTargetForDOMEvent();
1312 if (!aEvent
->mCurrentTarget
) {
1317 aEvent
->mFlags
.mInPassiveListener
= aListener
->mFlags
.mPassive
;
1319 nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
=
1320 WindowFromListener(aListener
, aTypeAtom
, aItemInShadowTree
);
1321 mozilla::dom::Event
* oldWindowEvent
= nullptr;
1323 oldWindowEvent
= innerWindow
->SetEvent(aDOMEvent
);
1326 nsresult result
= NS_OK
;
1329 EventListenerHolder
listenerHolder(aListener
->mListener
.Clone());
1331 // If this is a script handler and we haven't yet
1332 // compiled the event handler itself
1333 if ((aListener
->mListenerType
== Listener::eJSEventListener
) &&
1334 aListener
->mHandlerIsString
) {
1336 CompileEventHandlerInternal(aListener
, aTypeAtom
, nullptr, nullptr);
1337 aListener
= nullptr;
1340 if (NS_SUCCEEDED(result
)) {
1341 Maybe
<EventCallbackDebuggerNotificationGuard
> dbgGuard
;
1342 if (dom::ChromeUtils::IsDevToolsOpened()) {
1343 dbgGuard
.emplace(aCurrentTarget
, aDOMEvent
);
1347 // Event::currentTarget is set in EventDispatcher.
1348 if (listenerHolder
.HasWebIDLCallback()) {
1350 listenerHolder
.GetWebIDLCallback()->HandleEvent(aCurrentTarget
,
1352 result
= rv
.StealNSResult();
1354 // listenerHolder is holding a stack ref here.
1355 result
= MOZ_KnownLive(listenerHolder
.GetXPCOMCallback())
1356 ->HandleEvent(aDOMEvent
);
1361 Unused
<< innerWindow
->SetEvent(oldWindowEvent
);
1364 if (NS_FAILED(result
)) {
1365 aEvent
->mFlags
.mExceptionWasRaised
= true;
1367 aEvent
->mFlags
.mInPassiveListener
= false;
1368 return !aEvent
->mFlags
.mImmediatePropagationStopped
;
1371 /* static */ EventMessage
EventListenerManager::GetLegacyEventMessage(
1372 EventMessage aEventMessage
) {
1373 // webkit-prefixed legacy events:
1374 if (aEventMessage
== eTransitionEnd
) {
1375 return eWebkitTransitionEnd
;
1377 if (aEventMessage
== eAnimationStart
) {
1378 return eWebkitAnimationStart
;
1380 if (aEventMessage
== eAnimationEnd
) {
1381 return eWebkitAnimationEnd
;
1383 if (aEventMessage
== eAnimationIteration
) {
1384 return eWebkitAnimationIteration
;
1387 switch (aEventMessage
) {
1388 case eFullscreenChange
:
1389 return eMozFullscreenChange
;
1390 case eFullscreenError
:
1391 return eMozFullscreenError
;
1393 return aEventMessage
;
1397 EventMessage
EventListenerManager::GetEventMessage(nsAtom
* aEventName
) const {
1398 if (mIsMainThreadELM
) {
1399 return nsContentUtils::GetEventMessage(aEventName
);
1402 // The nsContentUtils event message hashtables aren't threadsafe, so just fall
1403 // back to eUnidentifiedEvent.
1404 return eUnidentifiedEvent
;
1407 EventMessage
EventListenerManager::GetEventMessageAndAtomForListener(
1408 const nsAString
& aType
, nsAtom
** aAtom
) {
1409 if (mIsMainThreadELM
) {
1410 return nsContentUtils::GetEventMessageAndAtomForListener(aType
, aAtom
);
1413 *aAtom
= NS_Atomize(u
"on"_ns
+ aType
).take();
1414 return eUnidentifiedEvent
;
1417 already_AddRefed
<nsPIDOMWindowInner
> EventListenerManager::WindowFromListener(
1418 Listener
* aListener
, nsAtom
* aTypeAtom
, bool aItemInShadowTree
) {
1419 nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
;
1420 if (!aItemInShadowTree
) {
1421 if (aListener
->mListener
.HasWebIDLCallback()) {
1422 CallbackObject
* callback
= aListener
->mListener
.GetWebIDLCallback();
1423 nsIGlobalObject
* global
= nullptr;
1425 global
= callback
->IncumbentGlobalOrNull();
1428 innerWindow
= global
->AsInnerWindow(); // Can be nullptr
1431 // This ensures `window.event` can be set properly for
1432 // nsWindowRoot to handle KeyPress event.
1433 if (aListener
&& aTypeAtom
== nsGkAtoms::onkeypress
&& mTarget
&&
1434 mTarget
->IsRootWindow()) {
1435 nsPIWindowRoot
* root
= mTarget
->AsWindowRoot();
1436 if (nsPIDOMWindowOuter
* outerWindow
= root
->GetWindow()) {
1437 innerWindow
= outerWindow
->GetCurrentInnerWindow();
1440 // Can't get the global from
1441 // listener->mListener.GetXPCOMCallback().
1442 // In most cases, it would be the same as for
1443 // the target, so let's do that.
1444 innerWindow
= GetInnerWindowForTarget(); // Can be nullptr
1448 return innerWindow
.forget();
1451 Maybe
<size_t> EventListenerManager::EventListenerMap::EntryIndexForType(
1452 nsAtom
* aTypeAtom
) const {
1453 MOZ_ASSERT(aTypeAtom
);
1455 size_t matchIndexOrInsertionPoint
= 0;
1456 bool foundMatch
= BinarySearchIf(mEntries
, 0, mEntries
.Length(),
1457 ListenerMapEntryComparator(aTypeAtom
),
1458 &matchIndexOrInsertionPoint
);
1459 return foundMatch
? Some(matchIndexOrInsertionPoint
) : Nothing();
1462 Maybe
<size_t> EventListenerManager::EventListenerMap::EntryIndexForAllEvents()
1464 // If we have an entry for "all events listeners", it'll be at the beginning
1465 // of the list and its type atom will be null.
1466 return !mEntries
.IsEmpty() && mEntries
[0].mTypeAtom
== nullptr ? Some(0)
1470 RefPtr
<EventListenerManager::ListenerArray
>
1471 EventListenerManager::EventListenerMap::GetListenersForType(
1472 nsAtom
* aTypeAtom
) const {
1473 Maybe
<size_t> index
= EntryIndexForType(aTypeAtom
);
1474 return index
? mEntries
[*index
].mListeners
: nullptr;
1477 RefPtr
<EventListenerManager::ListenerArray
>
1478 EventListenerManager::EventListenerMap::GetListenersForAllEvents() const {
1479 Maybe
<size_t> index
= EntryIndexForAllEvents();
1480 return index
? mEntries
[*index
].mListeners
: nullptr;
1483 RefPtr
<EventListenerManager::ListenerArray
>
1484 EventListenerManager::EventListenerMap::GetOrCreateListenersForType(
1485 nsAtom
* aTypeAtom
) {
1486 MOZ_ASSERT(aTypeAtom
);
1487 size_t matchIndexOrInsertionPoint
= 0;
1488 bool foundMatch
= BinarySearchIf(mEntries
, 0, mEntries
.Length(),
1489 ListenerMapEntryComparator(aTypeAtom
),
1490 &matchIndexOrInsertionPoint
);
1492 return mEntries
[matchIndexOrInsertionPoint
].mListeners
;
1494 RefPtr
<ListenerArray
> listeners
= MakeRefPtr
<ListenerArray
>();
1495 mEntries
.InsertElementAt(matchIndexOrInsertionPoint
,
1496 EventListenerMapEntry
{aTypeAtom
, listeners
});
1501 RefPtr
<EventListenerManager::ListenerArray
>
1502 EventListenerManager::EventListenerMap::GetOrCreateListenersForAllEvents() {
1503 RefPtr
<ListenerArray
> listeners
= GetListenersForAllEvents();
1505 listeners
= MakeRefPtr
<ListenerArray
>();
1506 mEntries
.InsertElementAt(0, EventListenerMapEntry
{nullptr, listeners
});
1511 void EventListenerManager::HandleEventInternal(nsPresContext
* aPresContext
,
1512 WidgetEvent
* aEvent
,
1514 EventTarget
* aCurrentTarget
,
1515 nsEventStatus
* aEventStatus
,
1516 bool aItemInShadowTree
) {
1517 MOZ_ASSERT_IF(aEvent
->mMessage
!= eUnidentifiedEvent
, mIsMainThreadELM
);
1519 // Set the value of the internal PreventDefault flag properly based on
1521 if (!aEvent
->DefaultPrevented() &&
1522 *aEventStatus
== nsEventStatus_eConsumeNoDefault
) {
1523 // Assume that if only aEventStatus claims that the event has already been
1524 // consumed, the consumer is default event handler.
1525 aEvent
->PreventDefault();
1528 if (aEvent
->mFlags
.mImmediatePropagationStopped
) {
1532 Maybe
<AutoHandlingUserInputStatePusher
> userInputStatePusher
;
1533 Maybe
<AutoPopupStatePusher
> popupStatePusher
;
1534 if (mIsMainThreadELM
) {
1535 userInputStatePusher
.emplace(UserActivation::IsUserInteractionEvent(aEvent
),
1537 popupStatePusher
.emplace(
1538 PopupBlocker::GetEventPopupControlState(aEvent
, *aDOMEvent
));
1541 EventMessage eventMessage
= aEvent
->mMessage
;
1542 RefPtr
<nsAtom
> typeAtom
=
1543 eventMessage
== eUnidentifiedEvent
1544 ? aEvent
->mSpecifiedEventType
.get()
1545 : nsContentUtils::GetEventTypeFromMessage(eventMessage
);
1547 // Some messages don't have a corresponding type atom, e.g.
1548 // eMouseEnterIntoWidget. These events can't have a listener, so we
1553 bool hasAnyListenerForEventType
= false;
1555 // First, notify any "all events" listeners.
1556 if (RefPtr
<ListenerArray
> listenersForAllEvents
=
1557 mListenerMap
.GetListenersForAllEvents()) {
1558 HandleEventWithListenerArray(listenersForAllEvents
, typeAtom
, eventMessage
,
1559 aPresContext
, aEvent
, aDOMEvent
,
1560 aCurrentTarget
, aItemInShadowTree
);
1561 hasAnyListenerForEventType
= true;
1564 // Now look for listeners for typeAtom, and call them if we have any.
1565 bool hasAnyListenerMatchingGroup
= false;
1566 if (RefPtr
<ListenerArray
> listeners
=
1567 mListenerMap
.GetListenersForType(typeAtom
)) {
1568 hasAnyListenerMatchingGroup
= HandleEventWithListenerArray(
1569 listeners
, typeAtom
, eventMessage
, aPresContext
, aEvent
, aDOMEvent
,
1570 aCurrentTarget
, aItemInShadowTree
);
1571 hasAnyListenerForEventType
= true;
1574 if (!hasAnyListenerMatchingGroup
&& aEvent
->IsTrusted()) {
1575 // If we didn't find any matching listeners, and our event has a legacy
1576 // version, check the listeners for the legacy version.
1577 EventMessage legacyEventMessage
= GetLegacyEventMessage(eventMessage
);
1578 if (legacyEventMessage
!= eventMessage
) {
1580 GetLegacyEventMessage(legacyEventMessage
) == legacyEventMessage
,
1581 "Legacy event messages should not themselves have legacy versions");
1582 RefPtr
<nsAtom
> legacyTypeAtom
=
1583 nsContentUtils::GetEventTypeFromMessage(legacyEventMessage
);
1584 if (RefPtr
<ListenerArray
> legacyListeners
=
1585 mListenerMap
.GetListenersForType(legacyTypeAtom
)) {
1586 HandleEventWithListenerArray(
1587 legacyListeners
, legacyTypeAtom
, legacyEventMessage
, aPresContext
,
1588 aEvent
, aDOMEvent
, aCurrentTarget
, aItemInShadowTree
);
1589 hasAnyListenerForEventType
= true;
1594 aEvent
->mCurrentTarget
= nullptr;
1596 if (mIsMainThreadELM
&& !hasAnyListenerForEventType
) {
1597 if (aEvent
->mMessage
!= eUnidentifiedEvent
) {
1598 mNoListenerForEvents
[2] = mNoListenerForEvents
[1];
1599 mNoListenerForEvents
[1] = mNoListenerForEvents
[0];
1600 mNoListenerForEvents
[0] = aEvent
->mMessage
;
1602 mNoListenerForEventAtom
= aEvent
->mSpecifiedEventType
;
1606 if (aEvent
->DefaultPrevented()) {
1607 *aEventStatus
= nsEventStatus_eConsumeNoDefault
;
1611 bool EventListenerManager::HandleEventWithListenerArray(
1612 ListenerArray
* aListeners
, nsAtom
* aTypeAtom
, EventMessage aEventMessage
,
1613 nsPresContext
* aPresContext
, WidgetEvent
* aEvent
, Event
** aDOMEvent
,
1614 EventTarget
* aCurrentTarget
, bool aItemInShadowTree
) {
1615 auto ensureDOMEvent
= [&]() {
1617 // Lazily create the DOM event.
1618 // This is tiny bit slow, but happens only once per event.
1619 // Similar code also in EventDispatcher.
1620 nsCOMPtr
<EventTarget
> et
= aEvent
->mOriginalTarget
;
1621 RefPtr
<Event
> event
=
1622 EventDispatcher::CreateEvent(et
, aPresContext
, aEvent
, u
""_ns
);
1623 event
.forget(aDOMEvent
);
1625 return *aDOMEvent
!= nullptr;
1628 Maybe
<EventMessageAutoOverride
> eventMessageAutoOverride
;
1629 bool isOverridingEventMessage
= aEvent
->mMessage
!= aEventMessage
;
1630 bool hasAnyListenerMatchingGroup
= false;
1631 bool didReplaceOnceListener
= false;
1633 for (Listener
& listenerRef
: aListeners
->EndLimitedRange()) {
1634 Listener
* listener
= &listenerRef
;
1635 if (listener
->mListenerType
== Listener::eNoListener
) {
1636 // The listener is a placeholder value of a removed "once" listener.
1639 if (!listener
->mEnabled
) {
1640 // The listener has been disabled, for example by devtools.
1643 if (!listener
->MatchesEventGroup(aEvent
)) {
1646 hasAnyListenerMatchingGroup
= true;
1648 // Check that the phase is same in event and event listener. Also check
1649 // that the event is trusted or that the listener allows untrusted events.
1650 if (!listener
->MatchesEventPhase(aEvent
) ||
1651 !listener
->AllowsEventTrustedness(aEvent
)) {
1655 Maybe
<Listener
> listenerHolder
;
1656 if (listener
->mFlags
.mOnce
) {
1657 // Move the listener to the stack before handling the event.
1658 // The order is important, otherwise the listener could be
1659 // called again inside the listener.
1660 listenerHolder
.emplace(std::move(*listener
));
1661 listener
= listenerHolder
.ptr();
1662 didReplaceOnceListener
= true;
1664 if (ensureDOMEvent()) {
1665 if (isOverridingEventMessage
&& !eventMessageAutoOverride
) {
1666 // Override the domEvent's event-message (its .type) until we
1667 // finish traversing listeners (when eventMessageAutoOverride
1669 eventMessageAutoOverride
.emplace(*aDOMEvent
, aEventMessage
);
1671 if (!HandleEventSingleListener(listener
, aTypeAtom
, aEvent
, *aDOMEvent
,
1672 aCurrentTarget
, aItemInShadowTree
)) {
1678 if (didReplaceOnceListener
) {
1679 // If there are any once listeners replaced with a placeholder during the
1680 // loop above, we need to clean up them here. Note that this could clear
1681 // once listeners handled in some outer level as well, but that should not
1682 // affect the result.
1683 size_t oldLength
= aListeners
->Length();
1684 aListeners
->NonObservingRemoveElementsBy([](const Listener
& aListener
) {
1685 return aListener
.mListenerType
== Listener::eNoListener
;
1687 size_t newLength
= aListeners
->Length();
1688 if (newLength
== 0) {
1689 // Remove the entry that has now become empty.
1690 mListenerMap
.mEntries
.RemoveElementsBy([](EventListenerMapEntry
& entry
) {
1691 return entry
.mListeners
->IsEmpty();
1694 if (newLength
< oldLength
) {
1695 // Call NotifyEventListenerRemoved once for every removed listener.
1696 size_t removedCount
= oldLength
- newLength
;
1697 for (size_t i
= 0; i
< removedCount
; i
++) {
1698 NotifyEventListenerRemoved(aTypeAtom
);
1700 if (IsDeviceType(aTypeAtom
)) {
1701 // Call DisableDevice once for every removed listener.
1702 for (size_t i
= 0; i
< removedCount
; i
++) {
1703 DisableDevice(aTypeAtom
);
1709 return hasAnyListenerMatchingGroup
;
1712 void EventListenerManager::Disconnect() {
1714 RemoveAllListenersSilently();
1717 void EventListenerManager::AddEventListener(const nsAString
& aType
,
1718 EventListenerHolder aListenerHolder
,
1720 bool aWantsUntrusted
) {
1721 EventListenerFlags flags
;
1722 flags
.mCapture
= aUseCapture
;
1723 flags
.mAllowUntrustedEvents
= aWantsUntrusted
;
1724 return AddEventListenerByType(std::move(aListenerHolder
), aType
, flags
);
1727 void EventListenerManager::AddEventListener(
1728 const nsAString
& aType
, EventListenerHolder aListenerHolder
,
1729 const dom::AddEventListenerOptionsOrBoolean
& aOptions
,
1730 bool aWantsUntrusted
) {
1731 EventListenerFlags flags
;
1732 Optional
<bool> passive
;
1733 AbortSignal
* signal
= nullptr;
1734 if (aOptions
.IsBoolean()) {
1735 flags
.mCapture
= aOptions
.GetAsBoolean();
1737 const auto& options
= aOptions
.GetAsAddEventListenerOptions();
1738 flags
.mCapture
= options
.mCapture
;
1739 flags
.mInSystemGroup
= options
.mMozSystemGroup
;
1740 flags
.mOnce
= options
.mOnce
;
1741 if (options
.mPassive
.WasPassed()) {
1742 passive
.Construct(options
.mPassive
.Value());
1745 if (options
.mSignal
.WasPassed()) {
1746 signal
= &options
.mSignal
.Value();
1750 flags
.mAllowUntrustedEvents
= aWantsUntrusted
;
1751 return AddEventListenerByType(std::move(aListenerHolder
), aType
, flags
,
1755 void EventListenerManager::RemoveEventListener(
1756 const nsAString
& aType
, EventListenerHolder aListenerHolder
,
1758 EventListenerFlags flags
;
1759 flags
.mCapture
= aUseCapture
;
1760 RemoveEventListenerByType(std::move(aListenerHolder
), aType
, flags
);
1763 void EventListenerManager::RemoveEventListener(
1764 const nsAString
& aType
, EventListenerHolder aListenerHolder
,
1765 const dom::EventListenerOptionsOrBoolean
& aOptions
) {
1766 EventListenerFlags flags
;
1767 if (aOptions
.IsBoolean()) {
1768 flags
.mCapture
= aOptions
.GetAsBoolean();
1770 const auto& options
= aOptions
.GetAsEventListenerOptions();
1771 flags
.mCapture
= options
.mCapture
;
1772 flags
.mInSystemGroup
= options
.mMozSystemGroup
;
1774 RemoveEventListenerByType(std::move(aListenerHolder
), aType
, flags
);
1777 void EventListenerManager::AddListenerForAllEvents(EventListener
* aDOMListener
,
1779 bool aWantsUntrusted
,
1780 bool aSystemEventGroup
) {
1781 EventListenerFlags flags
;
1782 flags
.mCapture
= aUseCapture
;
1783 flags
.mAllowUntrustedEvents
= aWantsUntrusted
;
1784 flags
.mInSystemGroup
= aSystemEventGroup
;
1785 AddEventListenerInternal(EventListenerHolder(aDOMListener
), eAllEvents
,
1786 nullptr, flags
, false, true);
1789 void EventListenerManager::RemoveListenerForAllEvents(
1790 EventListener
* aDOMListener
, bool aUseCapture
, bool aSystemEventGroup
) {
1791 EventListenerFlags flags
;
1792 flags
.mCapture
= aUseCapture
;
1793 flags
.mInSystemGroup
= aSystemEventGroup
;
1794 RemoveEventListenerInternal(EventListenerHolder(aDOMListener
), nullptr, flags
,
1798 bool EventListenerManager::HasMutationListeners() {
1799 if (mMayHaveMutationListeners
) {
1800 for (const auto& entry
: mListenerMap
.mEntries
) {
1801 EventMessage message
= GetEventMessage(entry
.mTypeAtom
);
1802 if (message
>= eLegacyMutationEventFirst
&&
1803 message
<= eLegacyMutationEventLast
) {
1812 uint32_t EventListenerManager::MutationListenerBits() {
1814 if (mMayHaveMutationListeners
) {
1815 for (const auto& entry
: mListenerMap
.mEntries
) {
1816 EventMessage message
= GetEventMessage(entry
.mTypeAtom
);
1817 if (message
>= eLegacyMutationEventFirst
&&
1818 message
<= eLegacyMutationEventLast
) {
1819 if (message
== eLegacySubtreeModified
) {
1820 return kAllMutationBits
;
1822 bits
|= MutationBitForEventType(message
);
1829 bool EventListenerManager::HasListenersFor(const nsAString
& aEventName
) const {
1830 RefPtr
<nsAtom
> atom
= NS_Atomize(u
"on"_ns
+ aEventName
);
1831 return HasListenersFor(atom
);
1834 bool EventListenerManager::HasListenersFor(nsAtom
* aEventNameWithOn
) const {
1835 return HasListenersForInternal(aEventNameWithOn
, false);
1838 bool EventListenerManager::HasNonSystemGroupListenersFor(
1839 nsAtom
* aEventNameWithOn
) const {
1840 return HasListenersForInternal(aEventNameWithOn
, true);
1843 bool EventListenerManager::HasListenersForInternal(
1844 nsAtom
* aEventNameWithOn
, bool aIgnoreSystemGroup
) const {
1847 aEventNameWithOn
->ToString(name
);
1849 NS_ASSERTION(StringBeginsWith(name
, u
"on"_ns
),
1850 "Event name does not start with 'on'");
1851 RefPtr
<ListenerArray
> listeners
=
1852 mListenerMap
.GetListenersForType(aEventNameWithOn
);
1857 MOZ_ASSERT(!listeners
->IsEmpty());
1859 if (!aIgnoreSystemGroup
) {
1863 // Check if any non-system-group listeners exist in `listeners`.
1864 for (const auto& listener
: listeners
->NonObservingRange()) {
1865 if (!listener
.mFlags
.mInSystemGroup
) {
1873 bool EventListenerManager::HasListeners() const {
1874 return !mListenerMap
.IsEmpty();
1877 nsresult
EventListenerManager::GetListenerInfo(
1878 nsTArray
<RefPtr
<nsIEventListenerInfo
>>& aList
) {
1879 nsCOMPtr
<EventTarget
> target
= mTarget
;
1880 NS_ENSURE_STATE(target
);
1882 for (const auto& entry
: mListenerMap
.mEntries
) {
1883 for (const Listener
& listener
: entry
.mListeners
->ForwardRange()) {
1884 // If this is a script handler and we haven't yet
1885 // compiled the event handler itself go ahead and compile it
1886 if (listener
.mListenerType
== Listener::eJSEventListener
&&
1887 listener
.mHandlerIsString
) {
1888 CompileEventHandlerInternal(const_cast<Listener
*>(&listener
),
1889 entry
.mTypeAtom
, nullptr, nullptr);
1891 nsAutoString eventType
;
1892 if (listener
.mAllEvents
) {
1893 eventType
.SetIsVoid(true);
1894 } else if (listener
.mListenerType
== Listener::eNoListener
) {
1897 eventType
.Assign(Substring(nsDependentAtomString(entry
.mTypeAtom
), 2));
1900 JS::Rooted
<JSObject
*> callback(RootingCx());
1901 JS::Rooted
<JSObject
*> callbackGlobal(RootingCx());
1902 if (JSEventHandler
* handler
= listener
.GetJSEventHandler()) {
1903 if (handler
->GetTypedEventHandler().HasEventHandler()) {
1904 CallbackFunction
* callbackFun
= handler
->GetTypedEventHandler().Ptr();
1905 callback
= callbackFun
->CallableOrNull();
1906 callbackGlobal
= callbackFun
->CallbackGlobalOrNull();
1908 // This will be null for cross-compartment event listeners
1909 // which have been destroyed.
1913 } else if (listener
.mListenerType
== Listener::eWebIDLListener
) {
1914 EventListener
* listenerCallback
=
1915 listener
.mListener
.GetWebIDLCallback();
1916 callback
= listenerCallback
->CallbackOrNull();
1917 callbackGlobal
= listenerCallback
->CallbackGlobalOrNull();
1919 // This will be null for cross-compartment event listeners
1920 // which have been destroyed.
1925 RefPtr
<EventListenerInfo
> info
= new EventListenerInfo(
1926 this, eventType
, callback
, callbackGlobal
, listener
.mFlags
.mCapture
,
1927 listener
.mFlags
.mAllowUntrustedEvents
, listener
.mFlags
.mInSystemGroup
,
1928 listener
.mListenerIsHandler
);
1929 aList
.AppendElement(info
.forget());
1935 EventListenerManager::Listener
* EventListenerManager::GetListenerFor(
1936 nsAString
& aType
, JSObject
* aListener
, bool aCapturing
,
1937 bool aAllowsUntrusted
, bool aInSystemEventGroup
, bool aIsHandler
) {
1938 NS_ENSURE_TRUE(aListener
, nullptr);
1940 RefPtr
<ListenerArray
> listeners
= ([&]() -> RefPtr
<ListenerArray
> {
1941 if (aType
.IsVoid()) {
1942 return mListenerMap
.GetListenersForAllEvents();
1945 for (auto& mapEntry
: mListenerMap
.mEntries
) {
1946 if (RefPtr
<nsAtom
> typeAtom
= mapEntry
.mTypeAtom
) {
1947 if (Substring(nsDependentAtomString(typeAtom
), 2).Equals(aType
)) {
1948 return mapEntry
.mListeners
;
1960 for (Listener
& listener
: listeners
->ForwardRange()) {
1961 if (listener
.mListenerType
== Listener::eNoListener
) {
1965 if (listener
.mFlags
.mCapture
!= aCapturing
||
1966 listener
.mFlags
.mAllowUntrustedEvents
!= aAllowsUntrusted
||
1967 listener
.mFlags
.mInSystemGroup
!= aInSystemEventGroup
) {
1972 if (JSEventHandler
* handler
= listener
.GetJSEventHandler()) {
1973 if (handler
->GetTypedEventHandler().HasEventHandler()) {
1974 if (handler
->GetTypedEventHandler().Ptr()->CallableOrNull() ==
1980 } else if (listener
.mListenerType
== Listener::eWebIDLListener
&&
1981 listener
.mListener
.GetWebIDLCallback()->CallbackOrNull() ==
1989 nsresult
EventListenerManager::IsListenerEnabled(
1990 nsAString
& aType
, JSObject
* aListener
, bool aCapturing
,
1991 bool aAllowsUntrusted
, bool aInSystemEventGroup
, bool aIsHandler
,
1993 Listener
* listener
=
1994 GetListenerFor(aType
, aListener
, aCapturing
, aAllowsUntrusted
,
1995 aInSystemEventGroup
, aIsHandler
);
1996 NS_ENSURE_TRUE(listener
, NS_ERROR_NOT_AVAILABLE
);
1997 *aEnabled
= listener
->mEnabled
;
2001 nsresult
EventListenerManager::SetListenerEnabled(
2002 nsAString
& aType
, JSObject
* aListener
, bool aCapturing
,
2003 bool aAllowsUntrusted
, bool aInSystemEventGroup
, bool aIsHandler
,
2005 Listener
* listener
=
2006 GetListenerFor(aType
, aListener
, aCapturing
, aAllowsUntrusted
,
2007 aInSystemEventGroup
, aIsHandler
);
2008 NS_ENSURE_TRUE(listener
, NS_ERROR_NOT_AVAILABLE
);
2009 listener
->mEnabled
= aEnabled
;
2011 // We may have enabled some listener, clear the cache for which events
2012 // we don't have listeners.
2013 ClearNoListenersForEvents();
2014 mNoListenerForEventAtom
= nullptr;
2019 bool EventListenerManager::HasUnloadListeners() {
2020 return mListenerMap
.GetListenersForType(nsGkAtoms::onunload
) != nullptr;
2023 bool EventListenerManager::HasBeforeUnloadListeners() {
2024 return mListenerMap
.GetListenersForType(nsGkAtoms::onbeforeunload
) != nullptr;
2027 void EventListenerManager::SetEventHandler(nsAtom
* aEventName
,
2028 EventHandlerNonNull
* aHandler
) {
2030 RemoveEventHandler(aEventName
);
2034 // Untrusted events are always permitted for non-chrome script
2036 SetEventHandlerInternal(
2037 aEventName
, TypedEventHandler(aHandler
),
2038 !mIsMainThreadELM
|| !nsContentUtils::IsCallerChrome());
2041 void EventListenerManager::SetEventHandler(
2042 OnErrorEventHandlerNonNull
* aHandler
) {
2044 RemoveEventHandler(nsGkAtoms::onerror
);
2048 // Untrusted events are always permitted on workers and for non-chrome script
2049 // on the main thread.
2050 bool allowUntrusted
= !mIsMainThreadELM
|| !nsContentUtils::IsCallerChrome();
2052 SetEventHandlerInternal(nsGkAtoms::onerror
, TypedEventHandler(aHandler
),
2056 void EventListenerManager::SetEventHandler(
2057 OnBeforeUnloadEventHandlerNonNull
* aHandler
) {
2059 RemoveEventHandler(nsGkAtoms::onbeforeunload
);
2063 // Untrusted events are always permitted for non-chrome script
2065 SetEventHandlerInternal(
2066 nsGkAtoms::onbeforeunload
, TypedEventHandler(aHandler
),
2067 !mIsMainThreadELM
|| !nsContentUtils::IsCallerChrome());
2070 const TypedEventHandler
* EventListenerManager::GetTypedEventHandler(
2071 nsAtom
* aEventName
) {
2072 Listener
* listener
= FindEventHandler(aEventName
);
2078 JSEventHandler
* jsEventHandler
= listener
->GetJSEventHandler();
2080 if (listener
->mHandlerIsString
) {
2081 CompileEventHandlerInternal(listener
, aEventName
, nullptr, nullptr);
2084 const TypedEventHandler
& typedHandler
=
2085 jsEventHandler
->GetTypedEventHandler();
2086 return typedHandler
.HasEventHandler() ? &typedHandler
: nullptr;
2089 size_t EventListenerManager::SizeOfIncludingThis(
2090 MallocSizeOf aMallocSizeOf
) const {
2091 return aMallocSizeOf(this) + mListenerMap
.SizeOfExcludingThis(aMallocSizeOf
);
2094 size_t EventListenerManager::EventListenerMap::SizeOfExcludingThis(
2095 MallocSizeOf aMallocSizeOf
) const {
2096 size_t n
= mEntries
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
2097 for (const auto& entry
: mEntries
) {
2098 n
+= entry
.SizeOfExcludingThis(aMallocSizeOf
);
2103 size_t EventListenerManager::EventListenerMapEntry::SizeOfExcludingThis(
2104 MallocSizeOf aMallocSizeOf
) const {
2105 return mListeners
->SizeOfIncludingThis(aMallocSizeOf
);
2108 size_t EventListenerManager::ListenerArray::SizeOfIncludingThis(
2109 MallocSizeOf aMallocSizeOf
) const {
2110 size_t n
= aMallocSizeOf(this);
2111 n
+= ShallowSizeOfExcludingThis(aMallocSizeOf
);
2112 for (const auto& listener
: NonObservingRange()) {
2113 JSEventHandler
* jsEventHandler
= listener
.GetJSEventHandler();
2114 if (jsEventHandler
) {
2115 n
+= jsEventHandler
->SizeOfIncludingThis(aMallocSizeOf
);
2121 uint32_t EventListenerManager::ListenerCount() const {
2123 for (const auto& entry
: mListenerMap
.mEntries
) {
2124 count
+= entry
.mListeners
->Length();
2129 void EventListenerManager::MarkForCC() {
2130 for (const auto& entry
: mListenerMap
.mEntries
) {
2131 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2132 JSEventHandler
* jsEventHandler
= listener
.GetJSEventHandler();
2133 if (jsEventHandler
) {
2134 const TypedEventHandler
& typedHandler
=
2135 jsEventHandler
->GetTypedEventHandler();
2136 if (typedHandler
.HasEventHandler()) {
2137 typedHandler
.Ptr()->MarkForCC();
2139 } else if (listener
.mListenerType
== Listener::eWebIDLListener
) {
2140 listener
.mListener
.GetWebIDLCallback()->MarkForCC();
2144 if (mRefCnt
.IsPurple()) {
2145 mRefCnt
.RemovePurple();
2149 void EventListenerManager::TraceListeners(JSTracer
* aTrc
) {
2150 for (const auto& entry
: mListenerMap
.mEntries
) {
2151 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2152 JSEventHandler
* jsEventHandler
= listener
.GetJSEventHandler();
2153 if (jsEventHandler
) {
2154 const TypedEventHandler
& typedHandler
=
2155 jsEventHandler
->GetTypedEventHandler();
2156 if (typedHandler
.HasEventHandler()) {
2157 mozilla::TraceScriptHolder(typedHandler
.Ptr(), aTrc
);
2159 } else if (listener
.mListenerType
== Listener::eWebIDLListener
) {
2160 mozilla::TraceScriptHolder(listener
.mListener
.GetWebIDLCallback(),
2163 // We might have eWrappedJSListener, but that is the legacy type for
2164 // JS implemented event listeners, and trickier to handle here.
2169 bool EventListenerManager::HasNonSystemGroupListenersForUntrustedKeyEvents() {
2170 for (const auto& entry
: mListenerMap
.mEntries
) {
2171 if (entry
.mTypeAtom
!= nsGkAtoms::onkeydown
&&
2172 entry
.mTypeAtom
!= nsGkAtoms::onkeypress
&&
2173 entry
.mTypeAtom
!= nsGkAtoms::onkeyup
) {
2176 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2177 if (!listener
.mFlags
.mInSystemGroup
&&
2178 listener
.mFlags
.mAllowUntrustedEvents
) {
2186 bool EventListenerManager::
2187 HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents() {
2188 for (const auto& entry
: mListenerMap
.mEntries
) {
2189 if (entry
.mTypeAtom
!= nsGkAtoms::onkeydown
&&
2190 entry
.mTypeAtom
!= nsGkAtoms::onkeypress
&&
2191 entry
.mTypeAtom
!= nsGkAtoms::onkeyup
) {
2194 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2195 if (!listener
.mFlags
.mPassive
&& !listener
.mFlags
.mInSystemGroup
&&
2196 listener
.mFlags
.mAllowUntrustedEvents
) {
2204 bool EventListenerManager::HasApzAwareListeners() {
2205 if (!mIsMainThreadELM
) {
2209 for (const auto& entry
: mListenerMap
.mEntries
) {
2210 if (!IsApzAwareEvent(entry
.mTypeAtom
)) {
2213 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2214 if (!listener
.mFlags
.mPassive
) {
2222 static bool IsWheelEventType(nsAtom
* aEvent
) {
2223 if (aEvent
== nsGkAtoms::onwheel
|| aEvent
== nsGkAtoms::onDOMMouseScroll
||
2224 aEvent
== nsGkAtoms::onmousewheel
||
2225 aEvent
== nsGkAtoms::onMozMousePixelScroll
) {
2231 bool EventListenerManager::IsApzAwareEvent(nsAtom
* aEvent
) {
2232 if (IsWheelEventType(aEvent
)) {
2235 // In theory we should schedule a repaint if the touch event pref changes,
2236 // because the event regions might be out of date. In practice that seems like
2237 // overkill because users generally shouldn't be flipping this pref, much
2238 // less expecting touch listeners on the page to immediately start preventing
2239 // scrolling without so much as a repaint. Tests that we write can work
2240 // around this constraint easily enough.
2241 if (aEvent
== nsGkAtoms::ontouchstart
|| aEvent
== nsGkAtoms::ontouchmove
) {
2242 return TouchEvent::PrefEnabled(
2243 nsContentUtils::GetDocShellForEventTarget(mTarget
));
2248 bool EventListenerManager::HasNonPassiveWheelListener() {
2249 MOZ_ASSERT(NS_IsMainThread());
2250 for (const auto& entry
: mListenerMap
.mEntries
) {
2251 if (!IsWheelEventType(entry
.mTypeAtom
)) {
2254 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2255 if (!listener
.mFlags
.mPassive
) {
2263 void EventListenerManager::RemoveAllListeners() {
2264 while (!mListenerMap
.IsEmpty()) {
2265 size_t entryIndex
= mListenerMap
.mEntries
.Length() - 1;
2266 EventListenerMapEntry
& entry
= mListenerMap
.mEntries
[entryIndex
];
2267 RefPtr
<nsAtom
> type
= entry
.mTypeAtom
;
2268 MOZ_ASSERT(!entry
.mListeners
->IsEmpty());
2269 size_t idx
= entry
.mListeners
->Length() - 1;
2270 entry
.mListeners
->RemoveElementAt(idx
);
2271 if (entry
.mListeners
->IsEmpty()) {
2272 mListenerMap
.mEntries
.RemoveElementAt(entryIndex
);
2274 NotifyEventListenerRemoved(type
);
2275 if (IsDeviceType(type
)) {
2276 DisableDevice(type
);
2281 already_AddRefed
<nsIScriptGlobalObject
>
2282 EventListenerManager::GetScriptGlobalAndDocument(Document
** aDoc
) {
2283 nsCOMPtr
<Document
> doc
;
2284 nsCOMPtr
<nsPIDOMWindowInner
> win
;
2285 if (nsINode
* node
= nsINode::FromEventTargetOrNull(mTarget
)) {
2286 // Try to get context from doc
2287 doc
= node
->OwnerDoc();
2288 if (doc
->IsLoadedAsData()) {
2292 win
= do_QueryInterface(doc
->GetScopeObject());
2293 } else if ((win
= GetTargetAsInnerWindow())) {
2294 doc
= win
->GetExtantDoc();
2297 if (!win
|| !win
->IsCurrentInnerWindow()) {
2302 nsCOMPtr
<nsIScriptGlobalObject
> global
= do_QueryInterface(win
);
2303 return global
.forget();
2306 EventListenerManager::ListenerSignalFollower::ListenerSignalFollower(
2307 EventListenerManager
* aListenerManager
,
2308 EventListenerManager::Listener
* aListener
, nsAtom
* aTypeAtom
)
2309 : dom::AbortFollower(),
2310 mListenerManager(aListenerManager
),
2311 mListener(aListener
->mListener
.Clone()),
2312 mTypeAtom(aTypeAtom
),
2313 mAllEvents(aListener
->mAllEvents
),
2314 mFlags(aListener
->mFlags
){};
2316 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower
)
2318 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower
)
2319 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower
)
2321 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
2322 EventListenerManager::ListenerSignalFollower
)
2323 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener
)
2324 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2326 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
2327 EventListenerManager::ListenerSignalFollower
)
2328 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener
)
2329 tmp
->mListenerManager
= nullptr;
2330 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2332 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
2333 EventListenerManager::ListenerSignalFollower
)
2334 NS_INTERFACE_MAP_ENTRY(nsISupports
)
2335 NS_INTERFACE_MAP_END
2337 void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() {
2338 if (mListenerManager
) {
2339 RefPtr
<EventListenerManager
> elm
= mListenerManager
;
2340 mListenerManager
= nullptr;
2341 elm
->RemoveEventListenerInternal(std::move(mListener
), mTypeAtom
, mFlags
,
2346 } // namespace mozilla