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::ColumnNumberOneOrigin
11 #include "js/loader/LoadedScript.h"
12 #include "js/loader/ScriptFetchOptions.h"
13 #include "mozilla/BasicEvents.h"
14 #include "mozilla/BinarySearch.h"
15 #include "mozilla/CycleCollectedJSRuntime.h"
16 #include "mozilla/DOMEventTargetHelper.h"
17 #include "mozilla/EventDispatcher.h"
18 #include "mozilla/EventListenerManager.h"
19 #include "mozilla/HalSensor.h"
20 #include "mozilla/InternalMutationEvent.h"
21 #include "mozilla/JSEventHandler.h"
22 #include "mozilla/Maybe.h"
23 #include "mozilla/MemoryReporting.h"
24 #include "mozilla/Preferences.h"
25 #include "mozilla/PresShell.h"
26 #include "mozilla/dom/AbortSignal.h"
27 #include "mozilla/dom/BindingUtils.h"
28 #include "mozilla/dom/EventCallbackDebuggerNotification.h"
29 #include "mozilla/dom/Element.h"
30 #include "mozilla/dom/Event.h"
31 #include "mozilla/dom/EventTargetBinding.h"
32 #include "mozilla/dom/PopupBlocker.h"
33 #include "mozilla/dom/RequestBinding.h"
34 #include "mozilla/dom/ScriptLoader.h"
35 #include "mozilla/dom/ScriptSettings.h"
36 #include "mozilla/dom/TouchEvent.h"
37 #include "mozilla/dom/UserActivation.h"
38 #include "mozilla/ScopeExit.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 uint32_t MutationBitForEventType(EventMessage aEventType
) {
71 case eLegacySubtreeModified
:
72 return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED
;
73 case eLegacyNodeInserted
:
74 return NS_EVENT_BITS_MUTATION_NODEINSERTED
;
75 case eLegacyNodeRemoved
:
76 return NS_EVENT_BITS_MUTATION_NODEREMOVED
;
77 case eLegacyNodeRemovedFromDocument
:
78 return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT
;
79 case eLegacyNodeInsertedIntoDocument
:
80 return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT
;
81 case eLegacyAttrModified
:
82 return NS_EVENT_BITS_MUTATION_ATTRMODIFIED
;
83 case eLegacyCharacterDataModified
:
84 return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED
;
91 class ListenerMapEntryComparator
{
93 explicit ListenerMapEntryComparator(nsAtom
* aTarget
)
94 : mAddressOfEventType(reinterpret_cast<uintptr_t>(aTarget
)) {}
97 const EventListenerManager::EventListenerMapEntry
& aEntry
) const {
98 uintptr_t value
= reinterpret_cast<uintptr_t>(aEntry
.mTypeAtom
.get());
99 if (mAddressOfEventType
== value
) {
103 if (mAddressOfEventType
< value
) {
111 const uintptr_t mAddressOfEventType
; // the address of the atom, can be 0
114 uint32_t EventListenerManager::sMainThreadCreatedCount
= 0;
116 EventListenerManagerBase::EventListenerManagerBase()
117 : mMayHaveDOMActivateEventListener(false),
118 mMayHavePaintEventListener(false),
119 mMayHaveMutationListeners(false),
120 mMayHaveCapturingListeners(false),
121 mMayHaveSystemGroupListeners(false),
122 mMayHaveTouchEventListener(false),
123 mMayHaveMouseEnterLeaveEventListener(false),
124 mMayHavePointerEnterLeaveEventListener(false),
125 mMayHaveSelectionChangeEventListener(false),
126 mMayHaveFormSelectEventListener(false),
127 mMayHaveTransitionEventListener(false),
128 mClearingListeners(false),
129 mIsMainThreadELM(NS_IsMainThread()),
130 mMayHaveListenersForUntrustedEvents(false) {
131 ClearNoListenersForEvents();
132 static_assert(sizeof(EventListenerManagerBase
) == sizeof(uint64_t),
133 "Keep the size of EventListenerManagerBase size compact!");
136 EventListenerManager::EventListenerManager(EventTarget
* aTarget
)
138 NS_ASSERTION(aTarget
, "unexpected null pointer");
140 if (mIsMainThreadELM
) {
141 mRefCnt
.SetIsOnMainThread();
142 ++sMainThreadCreatedCount
;
146 EventListenerManager::~EventListenerManager() {
147 // If your code fails this assertion, a possible reason is that
148 // a class did not call our Disconnect() manually. Note that
149 // this class can have Disconnect called in one of two ways:
150 // if it is part of a cycle, then in Unlink() (such a cycle
151 // would be with one of the listeners, not mTarget which is weak).
152 // If not part of a cycle, then Disconnect must be called manually,
153 // typically from the destructor of the owner class (mTarget).
154 // XXX azakai: Is there any reason to not just call Disconnect
155 // from right here, if not previously called?
156 NS_ASSERTION(!mTarget
, "didn't call Disconnect");
157 RemoveAllListenersSilently();
160 void EventListenerManager::RemoveAllListenersSilently() {
161 if (mClearingListeners
) {
164 mClearingListeners
= true;
165 mListenerMap
.Clear();
166 mClearingListeners
= false;
169 inline void ImplCycleCollectionTraverse(
170 nsCycleCollectionTraversalCallback
& aCallback
,
171 EventListenerManager::EventListenerMap
& aField
, const char* aName
,
172 uint32_t aFlags
= 0) {
173 if (MOZ_UNLIKELY(aCallback
.WantDebugInfo())) {
175 name
.AppendASCII(aName
);
176 name
.AppendLiteral(" mEntries[i] event=");
177 size_t entryPrefixLen
= name
.Length();
178 for (const auto& entry
: aField
.mEntries
) {
179 if (entry
.mTypeAtom
) {
180 name
.Replace(entryPrefixLen
, name
.Length() - entryPrefixLen
,
181 nsAtomCString(entry
.mTypeAtom
));
183 name
.Replace(entryPrefixLen
, name
.Length() - entryPrefixLen
,
186 ImplCycleCollectionTraverse(aCallback
, *entry
.mListeners
, name
.get());
189 for (const auto& entry
: aField
.mEntries
) {
190 ImplCycleCollectionTraverse(aCallback
, *entry
.mListeners
,
191 ".mEntries[i].mListeners");
196 inline void ImplCycleCollectionTraverse(
197 nsCycleCollectionTraversalCallback
& aCallback
,
198 EventListenerManager::Listener
& aField
, const char* aName
,
200 if (MOZ_UNLIKELY(aCallback
.WantDebugInfo())) {
202 name
.AppendASCII(aName
);
203 name
.AppendLiteral(" listenerType=");
204 name
.AppendInt(aField
.mListenerType
);
205 name
.AppendLiteral(" ");
206 CycleCollectionNoteChild(aCallback
, aField
.mListener
.GetISupports(),
209 CycleCollectionNoteChild(aCallback
, aField
.mListener
.GetISupports(), aName
,
213 CycleCollectionNoteChild(aCallback
, aField
.mSignalFollower
.get(),
214 "mSignalFollower", aFlags
);
217 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager
)
219 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager
)
220 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerMap
);
221 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
223 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager
)
225 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
227 nsPIDOMWindowInner
* EventListenerManager::GetInnerWindowForTarget() {
228 if (nsINode
* node
= nsINode::FromEventTargetOrNull(mTarget
)) {
229 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
230 // if that's the XBL document?
231 return node
->OwnerDoc()->GetInnerWindow();
234 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow();
238 already_AddRefed
<nsPIDOMWindowInner
>
239 EventListenerManager::GetTargetAsInnerWindow() const {
240 nsCOMPtr
<nsPIDOMWindowInner
> window
=
241 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget
);
242 return window
.forget();
245 void EventListenerManager::AddEventListenerInternal(
246 EventListenerHolder aListenerHolder
, EventMessage aEventMessage
,
247 nsAtom
* aTypeAtom
, const EventListenerFlags
& aFlags
, bool aHandler
,
248 bool aAllEvents
, AbortSignal
* aSignal
) {
249 MOZ_ASSERT((aEventMessage
&& aTypeAtom
) || aAllEvents
, // all-events listener
252 aEventMessage
!= eUnidentifiedEvent
&& !aAllEvents
,
253 aTypeAtom
== nsContentUtils::GetEventTypeFromMessage(aEventMessage
));
255 if (!aListenerHolder
|| mClearingListeners
) {
259 if (aSignal
&& aSignal
->Aborted()) {
263 // Since there is no public API to call us with an EventListenerHolder, we
264 // know that there's an EventListenerHolder on the stack holding a strong ref
267 RefPtr
<ListenerArray
> listeners
=
268 aAllEvents
? mListenerMap
.GetOrCreateListenersForAllEvents()
269 : mListenerMap
.GetOrCreateListenersForType(aTypeAtom
);
271 for (const Listener
& listener
: listeners
->NonObservingRange()) {
272 // mListener == aListenerHolder is the last one, since it can be a bit slow.
273 if (listener
.mListenerIsHandler
== aHandler
&&
274 listener
.mFlags
.EqualsForAddition(aFlags
) &&
275 listener
.mListener
== aListenerHolder
) {
280 ClearNoListenersForEvents();
281 mNoListenerForEventAtom
= nullptr;
283 Listener
* listener
= listeners
->AppendElement();
284 listener
->mFlags
= aFlags
;
285 listener
->mListenerIsHandler
= aHandler
;
286 listener
->mHandlerIsString
= false;
287 listener
->mAllEvents
= aAllEvents
;
289 if (listener
->mFlags
.mAllowUntrustedEvents
) {
290 mMayHaveListenersForUntrustedEvents
= true;
293 // Detect the type of event listener.
294 if (aFlags
.mListenerIsJSListener
) {
295 MOZ_ASSERT(!aListenerHolder
.HasWebIDLCallback());
296 listener
->mListenerType
= Listener::eJSEventListener
;
297 } else if (aListenerHolder
.HasWebIDLCallback()) {
298 listener
->mListenerType
= Listener::eWebIDLListener
;
300 listener
->mListenerType
= Listener::eNativeListener
;
302 listener
->mListener
= std::move(aListenerHolder
);
305 listener
->mSignalFollower
=
306 new ListenerSignalFollower(this, listener
, aTypeAtom
);
307 listener
->mSignalFollower
->Follow(aSignal
);
310 if (aFlags
.mInSystemGroup
) {
311 mMayHaveSystemGroupListeners
= true;
313 if (aFlags
.mCapture
) {
314 mMayHaveCapturingListeners
= true;
317 // Events which are not supported in the running environment is mapped to
318 // eUnidentifiedEvent. Then, we need to consider the proper event message
319 // with comparing the atom.
321 EventMessage resolvedEventMessage
= aEventMessage
;
322 if (resolvedEventMessage
== eUnidentifiedEvent
&& aTypeAtom
->IsStatic()) {
323 // TouchEvents are registered only when
324 // nsContentUtils::InitializeTouchEventTable() is called.
325 if (aTypeAtom
== nsGkAtoms::ontouchstart
) {
326 resolvedEventMessage
= eTouchStart
;
327 } else if (aTypeAtom
== nsGkAtoms::ontouchend
) {
328 resolvedEventMessage
= eTouchEnd
;
329 } else if (aTypeAtom
== nsGkAtoms::ontouchmove
) {
330 resolvedEventMessage
= eTouchMove
;
331 } else if (aTypeAtom
== nsGkAtoms::ontouchcancel
) {
332 resolvedEventMessage
= eTouchCancel
;
336 switch (resolvedEventMessage
) {
338 mMayHavePaintEventListener
= true;
339 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
340 window
->SetHasPaintEventListeners();
343 case eLegacyDOMActivate
:
344 mMayHaveDOMActivateEventListener
= true;
345 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
346 window
->SetHasDOMActivateEventListeners();
349 case eLegacySubtreeModified
:
350 case eLegacyNodeInserted
:
351 case eLegacyNodeRemoved
:
352 case eLegacyNodeRemovedFromDocument
:
353 case eLegacyNodeInsertedIntoDocument
:
354 case eLegacyAttrModified
:
355 case eLegacyCharacterDataModified
:
357 MOZ_ASSERT(!aFlags
.mInSystemGroup
,
358 "Legacy mutation events shouldn't be handled by ourselves");
359 MOZ_ASSERT(listener
->mListenerType
!= Listener::eNativeListener
,
360 "Legacy mutation events shouldn't be handled in C++ code");
361 if (nsINode
* targetNode
= nsINode::FromEventTargetOrNull(mTarget
)) {
362 MOZ_ASSERT(!nsContentUtils::IsChromeDoc(targetNode
->OwnerDoc()),
363 "Legacy mutation events shouldn't be handled in chrome "
365 MOZ_ASSERT(!targetNode
->IsInNativeAnonymousSubtree(),
366 "Legacy mutation events shouldn't listen to mutations in "
367 "native anonymous subtrees");
369 #endif // #ifdef DEBUG
370 // For mutation listeners, we need to update the global bit on the DOM
371 // window. Otherwise we won't actually fire the mutation event.
372 mMayHaveMutationListeners
= true;
373 // Go from our target to the nearest enclosing DOM window.
374 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
375 if (Document
* doc
= window
->GetExtantDoc()) {
376 doc
->WarnOnceAbout(DeprecatedOperations::eMutationEvent
);
378 // If resolvedEventMessage is eLegacySubtreeModified, we need to
379 // listen all mutations. nsContentUtils::HasMutationListeners relies
381 window
->SetMutationListeners(
382 (resolvedEventMessage
== eLegacySubtreeModified
)
383 ? NS_EVENT_BITS_MUTATION_ALL
384 : MutationBitForEventType(resolvedEventMessage
));
389 mMayHavePointerEnterLeaveEventListener
= true;
390 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
391 NS_WARNING_ASSERTION(
392 !nsContentUtils::IsChromeDoc(window
->GetExtantDoc()),
393 "Please do not use pointerenter/leave events in chrome. "
394 "They are slower than pointerover/out!");
395 window
->SetHasPointerEnterLeaveEventListeners();
398 case eGamepadButtonDown
:
399 case eGamepadButtonUp
:
400 case eGamepadAxisMove
:
401 case eGamepadConnected
:
402 case eGamepadDisconnected
:
403 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
404 window
->SetHasGamepadEventListener();
407 case eDeviceOrientation
:
408 case eDeviceOrientationAbsolute
:
412 #if defined(MOZ_WIDGET_ANDROID)
413 case eOrientationChange
:
414 #endif // #if defined(MOZ_WIDGET_ANDROID)
415 EnableDevice(aTypeAtom
);
421 mMayHaveTouchEventListener
= true;
422 // we don't want touchevent listeners added by scrollbars to flip this
423 // flag so we ignore listeners created with system event flag
424 if (!aFlags
.mInSystemGroup
) {
425 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
426 window
->SetHasTouchEventListeners();
432 mMayHaveMouseEnterLeaveEventListener
= true;
433 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
434 NS_WARNING_ASSERTION(
435 !nsContentUtils::IsChromeDoc(window
->GetExtantDoc()),
436 "Please do not use mouseenter/leave events in chrome. "
437 "They are slower than mouseover/out!");
438 window
->SetHasMouseEnterLeaveEventListeners();
441 case eEditorBeforeInput
:
442 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
443 window
->SetHasBeforeInputEventListenersForTelemetry();
446 case eSelectionChange
:
447 mMayHaveSelectionChangeEventListener
= true;
448 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
449 window
->SetHasSelectionChangeEventListeners();
453 mMayHaveFormSelectEventListener
= true;
454 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
455 window
->SetHasFormSelectEventListeners();
458 case eScrollPortOverflow
:
459 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
460 if (Document
* doc
= window
->GetExtantDoc()) {
461 doc
->SetUseCounter(eUseCounter_custom_onoverflow
);
465 case eScrollPortUnderflow
:
466 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
467 if (Document
* doc
= window
->GetExtantDoc()) {
468 doc
->SetUseCounter(eUseCounter_custom_onunderflow
);
472 case eLegacyMouseLineOrPageScroll
:
473 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
474 if (Document
* doc
= window
->GetExtantDoc()) {
475 doc
->SetUseCounter(eUseCounter_custom_ondommousescroll
);
479 case eLegacyMousePixelScroll
:
480 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
481 if (Document
* doc
= window
->GetExtantDoc()) {
482 doc
->SetUseCounter(eUseCounter_custom_onmozmousepixelscroll
);
486 case eTransitionStart
:
489 case eTransitionCancel
:
490 case eWebkitTransitionEnd
:
491 mMayHaveTransitionEventListener
= true;
492 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
493 window
->SetHasTransitionEventListeners();
496 case eFormCheckboxStateChange
:
497 nsContentUtils::SetMayHaveFormCheckboxStateChangeListeners();
499 case eFormRadioStateChange
:
500 nsContentUtils::SetMayHaveFormRadioStateChangeListeners();
503 // XXX Use NS_ASSERTION here to print resolvedEventMessage since
504 // MOZ_ASSERT can take only string literal, not pointer to
507 resolvedEventMessage
< eLegacyMutationEventFirst
||
508 resolvedEventMessage
> eLegacyMutationEventLast
,
509 nsPrintfCString("You added new mutation event, but it's not "
510 "handled above, resolvedEventMessage=%s",
511 ToChar(resolvedEventMessage
))
513 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onpointerenter
,
514 nsPrintfCString("resolvedEventMessage=%s",
515 ToChar(resolvedEventMessage
))
517 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onpointerleave
,
518 nsPrintfCString("resolvedEventMessage=%s",
519 ToChar(resolvedEventMessage
))
522 resolvedEventMessage
< eGamepadEventFirst
||
523 resolvedEventMessage
> eGamepadEventLast
,
524 nsPrintfCString("You added new gamepad event, but it's not "
525 "handled above, resolvedEventMessage=%s",
526 ToChar(resolvedEventMessage
))
528 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondeviceorientation
,
529 nsPrintfCString("resolvedEventMessage=%s",
530 ToChar(resolvedEventMessage
))
532 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondeviceorientationabsolute
,
533 nsPrintfCString("resolvedEventMessage=%s",
534 ToChar(resolvedEventMessage
))
536 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onuserproximity
,
537 nsPrintfCString("resolvedEventMessage=%s",
538 ToChar(resolvedEventMessage
))
540 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondevicelight
,
541 nsPrintfCString("resolvedEventMessage=%s",
542 ToChar(resolvedEventMessage
))
544 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondevicemotion
,
545 nsPrintfCString("resolvedEventMessage=%s",
546 ToChar(resolvedEventMessage
))
548 #if defined(MOZ_WIDGET_ANDROID)
549 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onorientationchange
,
550 nsPrintfCString("resolvedEventMessage=%s",
551 ToChar(resolvedEventMessage
))
553 #endif // #if defined(MOZ_WIDGET_ANDROID)
554 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchstart
,
555 nsPrintfCString("resolvedEventMessage=%s",
556 ToChar(resolvedEventMessage
))
558 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchend
,
559 nsPrintfCString("resolvedEventMessage=%s",
560 ToChar(resolvedEventMessage
))
562 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchmove
,
563 nsPrintfCString("resolvedEventMessage=%s",
564 ToChar(resolvedEventMessage
))
566 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchcancel
,
567 nsPrintfCString("resolvedEventMessage=%s",
568 ToChar(resolvedEventMessage
))
570 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onmouseenter
,
571 nsPrintfCString("resolvedEventMessage=%s",
572 ToChar(resolvedEventMessage
))
574 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onmouseleave
,
575 nsPrintfCString("resolvedEventMessage=%s",
576 ToChar(resolvedEventMessage
))
578 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onbeforeinput
,
579 nsPrintfCString("resolvedEventMessage=%s",
580 ToChar(resolvedEventMessage
))
582 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onselectionchange
,
583 nsPrintfCString("resolvedEventMessage=%s",
584 ToChar(resolvedEventMessage
))
586 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onselect
,
587 nsPrintfCString("resolvedEventMessage=%s",
588 ToChar(resolvedEventMessage
))
590 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onoverflow
,
591 nsPrintfCString("resolvedEventMessage=%s",
592 ToChar(resolvedEventMessage
))
594 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onunderflow
,
595 nsPrintfCString("resolvedEventMessage=%s",
596 ToChar(resolvedEventMessage
))
598 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onDOMMouseScroll
,
599 nsPrintfCString("resolvedEventMessage=%s",
600 ToChar(resolvedEventMessage
))
602 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onMozMousePixelScroll
,
603 nsPrintfCString("resolvedEventMessage=%s",
604 ToChar(resolvedEventMessage
))
610 if (mIsMainThreadELM
&& !aFlags
.mPassive
&& IsApzAwareEvent(aTypeAtom
)) {
611 ProcessApzAwareEventListenerAdd();
615 mTarget
->EventListenerAdded(aTypeAtom
);
618 if (mIsMainThreadELM
&& mTarget
) {
619 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
,
624 void EventListenerManager::ProcessApzAwareEventListenerAdd() {
625 Document
* doc
= nullptr;
627 // Mark the node as having apz aware listeners
628 if (nsINode
* node
= nsINode::FromEventTargetOrNull(mTarget
)) {
629 node
->SetMayBeApzAware();
630 doc
= node
->OwnerDoc();
633 // Schedule a paint so event regions on the layer tree gets updated
635 if (nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow()) {
636 doc
= window
->GetExtantDoc();
640 if (nsCOMPtr
<DOMEventTargetHelper
> helper
= do_QueryInterface(mTarget
)) {
641 if (nsPIDOMWindowInner
* window
= helper
->GetOwner()) {
642 doc
= window
->GetExtantDoc();
647 if (doc
&& gfxPlatform::AsyncPanZoomEnabled()) {
648 PresShell
* presShell
= doc
->GetPresShell();
650 nsIFrame
* f
= presShell
->GetRootFrame();
658 bool EventListenerManager::IsDeviceType(nsAtom
* aTypeAtom
) {
659 return aTypeAtom
== nsGkAtoms::ondeviceorientation
||
660 aTypeAtom
== nsGkAtoms::ondeviceorientationabsolute
||
661 aTypeAtom
== nsGkAtoms::ondevicemotion
||
662 aTypeAtom
== nsGkAtoms::ondevicelight
663 #if defined(MOZ_WIDGET_ANDROID)
664 || aTypeAtom
== nsGkAtoms::onorientationchange
666 || aTypeAtom
== nsGkAtoms::onuserproximity
;
669 void EventListenerManager::EnableDevice(nsAtom
* aTypeAtom
) {
670 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow();
675 if (aTypeAtom
== nsGkAtoms::ondeviceorientation
) {
676 #ifdef MOZ_WIDGET_ANDROID
677 // Falls back to SENSOR_ROTATION_VECTOR and SENSOR_ORIENTATION if
678 // unavailable on device.
679 window
->EnableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR
);
680 window
->EnableDeviceSensor(SENSOR_ROTATION_VECTOR
);
682 window
->EnableDeviceSensor(SENSOR_ORIENTATION
);
687 if (aTypeAtom
== nsGkAtoms::ondeviceorientationabsolute
) {
688 #ifdef MOZ_WIDGET_ANDROID
689 // Falls back to SENSOR_ORIENTATION if unavailable on device.
690 window
->EnableDeviceSensor(SENSOR_ROTATION_VECTOR
);
692 window
->EnableDeviceSensor(SENSOR_ORIENTATION
);
697 if (aTypeAtom
== nsGkAtoms::onuserproximity
) {
698 window
->EnableDeviceSensor(SENSOR_PROXIMITY
);
702 if (aTypeAtom
== nsGkAtoms::ondevicelight
) {
703 window
->EnableDeviceSensor(SENSOR_LIGHT
);
707 if (aTypeAtom
== nsGkAtoms::ondevicemotion
) {
708 window
->EnableDeviceSensor(SENSOR_ACCELERATION
);
709 window
->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION
);
710 window
->EnableDeviceSensor(SENSOR_GYROSCOPE
);
714 #if defined(MOZ_WIDGET_ANDROID)
715 if (aTypeAtom
== nsGkAtoms::onorientationchange
) {
716 window
->EnableOrientationChangeListener();
721 NS_WARNING("Enabling an unknown device sensor.");
724 void EventListenerManager::DisableDevice(nsAtom
* aTypeAtom
) {
725 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow();
730 if (aTypeAtom
== nsGkAtoms::ondeviceorientation
) {
731 #ifdef MOZ_WIDGET_ANDROID
732 // Disable all potential fallback sensors.
733 window
->DisableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR
);
734 window
->DisableDeviceSensor(SENSOR_ROTATION_VECTOR
);
736 window
->DisableDeviceSensor(SENSOR_ORIENTATION
);
740 if (aTypeAtom
== nsGkAtoms::ondeviceorientationabsolute
) {
741 #ifdef MOZ_WIDGET_ANDROID
742 window
->DisableDeviceSensor(SENSOR_ROTATION_VECTOR
);
744 window
->DisableDeviceSensor(SENSOR_ORIENTATION
);
748 if (aTypeAtom
== nsGkAtoms::ondevicemotion
) {
749 window
->DisableDeviceSensor(SENSOR_ACCELERATION
);
750 window
->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION
);
751 window
->DisableDeviceSensor(SENSOR_GYROSCOPE
);
755 if (aTypeAtom
== nsGkAtoms::onuserproximity
) {
756 window
->DisableDeviceSensor(SENSOR_PROXIMITY
);
760 if (aTypeAtom
== nsGkAtoms::ondevicelight
) {
761 window
->DisableDeviceSensor(SENSOR_LIGHT
);
765 #if defined(MOZ_WIDGET_ANDROID)
766 if (aTypeAtom
== nsGkAtoms::onorientationchange
) {
767 window
->DisableOrientationChangeListener();
772 NS_WARNING("Disabling an unknown device sensor.");
775 void EventListenerManager::NotifyEventListenerRemoved(nsAtom
* aUserType
) {
776 // If the following code is changed, other callsites of EventListenerRemoved
777 // and NotifyAboutMainThreadListenerChange should be changed too.
778 ClearNoListenersForEvents();
779 mNoListenerForEventAtom
= nullptr;
781 mTarget
->EventListenerRemoved(aUserType
);
783 if (mIsMainThreadELM
&& mTarget
) {
784 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
,
789 void EventListenerManager::RemoveEventListenerInternal(
790 EventListenerHolder aListenerHolder
, nsAtom
* aUserType
,
791 const EventListenerFlags
& aFlags
, bool aAllEvents
) {
792 if (!aListenerHolder
|| (!aUserType
&& !aAllEvents
) || mClearingListeners
) {
796 Maybe
<size_t> entryIndex
= aAllEvents
797 ? mListenerMap
.EntryIndexForAllEvents()
798 : mListenerMap
.EntryIndexForType(aUserType
);
803 ListenerArray
& listenerArray
= *mListenerMap
.mEntries
[*entryIndex
].mListeners
;
805 Maybe
<uint32_t> listenerIndex
= [&]() -> Maybe
<uint32_t> {
806 uint32_t count
= listenerArray
.Length();
807 for (uint32_t i
= 0; i
< count
; ++i
) {
808 Listener
* listener
= &listenerArray
.ElementAt(i
);
809 if (listener
->mListener
== aListenerHolder
&&
810 listener
->mFlags
.EqualsForRemoval(aFlags
)) {
817 if (!listenerIndex
) {
821 listenerArray
.RemoveElementAt(*listenerIndex
);
822 if (listenerArray
.IsEmpty()) {
823 mListenerMap
.mEntries
.RemoveElementAt(*entryIndex
);
826 RefPtr
<EventListenerManager
> kungFuDeathGrip(this);
828 NotifyEventListenerRemoved(aUserType
);
829 if (IsDeviceType(aUserType
)) {
830 DisableDevice(aUserType
);
835 static bool IsDefaultPassiveWhenOnRoot(EventMessage aMessage
) {
836 if (aMessage
== eTouchStart
|| aMessage
== eTouchMove
|| aMessage
== eWheel
||
837 aMessage
== eLegacyMouseLineOrPageScroll
||
838 aMessage
== eLegacyMousePixelScroll
) {
844 static bool IsRootEventTarget(EventTarget
* aTarget
) {
848 if (aTarget
->IsInnerWindow()) {
851 const nsINode
* node
= nsINode::FromEventTarget(aTarget
);
855 Document
* doc
= node
->OwnerDoc();
856 return node
== doc
|| node
== doc
->GetRootElement() || node
== doc
->GetBody();
859 void EventListenerManager::MaybeMarkPassive(EventMessage aMessage
,
860 EventListenerFlags
& aFlags
) {
861 if (!mIsMainThreadELM
) {
864 if (!IsDefaultPassiveWhenOnRoot(aMessage
)) {
867 if (!IsRootEventTarget(mTarget
)) {
870 aFlags
.mPassive
= true;
873 void EventListenerManager::AddEventListenerByType(
874 EventListenerHolder aListenerHolder
, const nsAString
& aType
,
875 const EventListenerFlags
& aFlags
, const Optional
<bool>& aPassive
,
876 AbortSignal
* aSignal
) {
878 EventMessage message
=
879 GetEventMessageAndAtomForListener(aType
, getter_AddRefs(atom
));
881 EventListenerFlags flags
= aFlags
;
882 if (aPassive
.WasPassed()) {
883 flags
.mPassive
= aPassive
.Value();
885 MaybeMarkPassive(message
, flags
);
888 AddEventListenerInternal(std::move(aListenerHolder
), message
, atom
, flags
,
889 false, false, aSignal
);
892 void EventListenerManager::RemoveEventListenerByType(
893 EventListenerHolder aListenerHolder
, const nsAString
& aType
,
894 const EventListenerFlags
& aFlags
) {
896 (void)GetEventMessageAndAtomForListener(aType
, getter_AddRefs(atom
));
897 RemoveEventListenerInternal(std::move(aListenerHolder
), atom
, aFlags
);
900 EventListenerManager::Listener
* EventListenerManager::FindEventHandler(
902 // Run through the listeners for this type and see if a script
903 // listener is registered
904 RefPtr
<ListenerArray
> listeners
= mListenerMap
.GetListenersForType(aTypeAtom
);
909 uint32_t count
= listeners
->Length();
910 for (uint32_t i
= 0; i
< count
; ++i
) {
911 Listener
* listener
= &listeners
->ElementAt(i
);
912 if (listener
->mListenerIsHandler
) {
919 EventListenerManager::Listener
* EventListenerManager::SetEventHandlerInternal(
920 nsAtom
* aName
, const TypedEventHandler
& aTypedHandler
,
921 bool aPermitUntrustedEvents
) {
924 EventMessage eventMessage
= GetEventMessage(aName
);
925 Listener
* listener
= FindEventHandler(aName
);
928 // If we didn't find a script listener or no listeners existed
929 // create and add a new one.
930 EventListenerFlags flags
;
931 flags
.mListenerIsJSListener
= true;
932 MaybeMarkPassive(eventMessage
, flags
);
934 nsCOMPtr
<JSEventHandler
> jsEventHandler
;
935 NS_NewJSEventHandler(mTarget
, aName
, aTypedHandler
,
936 getter_AddRefs(jsEventHandler
));
937 AddEventListenerInternal(EventListenerHolder(jsEventHandler
), eventMessage
,
940 listener
= FindEventHandler(aName
);
942 JSEventHandler
* jsEventHandler
= listener
->GetJSEventHandler();
943 MOZ_ASSERT(jsEventHandler
,
944 "How can we have an event handler with no JSEventHandler?");
946 bool same
= jsEventHandler
->GetTypedEventHandler() == aTypedHandler
;
947 // Possibly the same listener, but update still the context and scope.
948 jsEventHandler
->SetHandler(aTypedHandler
);
949 if (mTarget
&& !same
) {
950 mTarget
->EventListenerRemoved(aName
);
951 mTarget
->EventListenerAdded(aName
);
953 if (mIsMainThreadELM
&& mTarget
) {
954 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
, aName
);
958 // Set flag to indicate possible need for compilation later
959 listener
->mHandlerIsString
= !aTypedHandler
.HasEventHandler();
960 if (aPermitUntrustedEvents
) {
961 listener
->mFlags
.mAllowUntrustedEvents
= true;
962 mMayHaveListenersForUntrustedEvents
= true;
968 nsresult
EventListenerManager::SetEventHandler(nsAtom
* aName
,
969 const nsAString
& aBody
,
970 bool aDeferCompilation
,
971 bool aPermitUntrustedEvents
,
973 auto removeEventHandler
= MakeScopeExit([&] { RemoveEventHandler(aName
); });
975 nsCOMPtr
<Document
> doc
;
976 nsCOMPtr
<nsIScriptGlobalObject
> global
=
977 GetScriptGlobalAndDocument(getter_AddRefs(doc
));
980 // This can happen; for example this document might have been
986 // return early preventing the event listener from being added
987 // 'doc' is fetched above
989 // Don't allow adding an event listener if the document is sandboxed
990 // without 'allow-scripts'.
991 if (doc
->HasScriptsBlockedBySandbox()) {
992 return NS_ERROR_DOM_SECURITY_ERR
;
996 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= doc
->GetCsp();
997 uint32_t lineNum
= 0;
998 JS::ColumnNumberOneOrigin columnNum
;
1000 JSContext
* cx
= nsContentUtils::GetCurrentJSContext();
1001 if (cx
&& !JS::DescribeScriptedCaller(cx
, nullptr, &lineNum
, &columnNum
)) {
1002 JS_ClearPendingException(cx
);
1006 bool allowsInlineScript
= true;
1007 rv
= csp
->GetAllowsInline(
1008 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
,
1009 true, // aHasUnsafeHash
1011 true, // aParserCreated (true because attribute event handler)
1013 nullptr, // nsICSPEventListener
1014 aBody
, lineNum
, columnNum
.oneOriginValue(), &allowsInlineScript
);
1015 NS_ENSURE_SUCCESS(rv
, rv
);
1017 // return early if CSP wants us to block inline scripts
1018 if (!allowsInlineScript
) {
1024 // This might be the first reference to this language in the global
1025 // We must init the language before we attempt to fetch its context.
1026 if (NS_FAILED(global
->EnsureScriptEnvironment())) {
1027 NS_WARNING("Failed to setup script environment for this language");
1028 // but fall through and let the inevitable failure below handle it.
1031 nsIScriptContext
* context
= global
->GetScriptContext();
1032 NS_ENSURE_TRUE(context
, NS_ERROR_FAILURE
);
1033 NS_ENSURE_STATE(global
->HasJSGlobal());
1035 removeEventHandler
.release();
1037 Listener
* listener
= SetEventHandlerInternal(aName
, TypedEventHandler(),
1038 aPermitUntrustedEvents
);
1040 if (!aDeferCompilation
) {
1041 return CompileEventHandlerInternal(listener
, aName
, &aBody
, aElement
);
1047 void EventListenerManager::RemoveEventHandler(nsAtom
* aName
) {
1048 if (mClearingListeners
) {
1052 Maybe
<size_t> entryIndex
= mListenerMap
.EntryIndexForType(aName
);
1057 ListenerArray
& listenerArray
= *mListenerMap
.mEntries
[*entryIndex
].mListeners
;
1059 Maybe
<uint32_t> listenerIndex
= [&]() -> Maybe
<uint32_t> {
1060 uint32_t count
= listenerArray
.Length();
1061 for (uint32_t i
= 0; i
< count
; ++i
) {
1062 Listener
* listener
= &listenerArray
.ElementAt(i
);
1063 if (listener
->mListenerIsHandler
) {
1070 if (!listenerIndex
) {
1074 listenerArray
.RemoveElementAt(*listenerIndex
);
1075 if (listenerArray
.IsEmpty()) {
1076 mListenerMap
.mEntries
.RemoveElementAt(*entryIndex
);
1079 RefPtr
<EventListenerManager
> kungFuDeathGrip(this);
1080 NotifyEventListenerRemoved(aName
);
1081 if (IsDeviceType(aName
)) {
1082 DisableDevice(aName
);
1086 nsresult
EventListenerManager::CompileEventHandlerInternal(
1087 Listener
* aListener
, nsAtom
* aTypeAtom
, const nsAString
* aBody
,
1088 Element
* aElement
) {
1089 MOZ_ASSERT(aListener
->GetJSEventHandler());
1090 MOZ_ASSERT(aListener
->mHandlerIsString
,
1091 "Why are we compiling a non-string JS listener?");
1092 JSEventHandler
* jsEventHandler
= aListener
->GetJSEventHandler();
1093 MOZ_ASSERT(!jsEventHandler
->GetTypedEventHandler().HasEventHandler(),
1094 "What is there to compile?");
1096 nsresult result
= NS_OK
;
1097 nsCOMPtr
<Document
> doc
;
1098 nsCOMPtr
<nsIScriptGlobalObject
> global
=
1099 GetScriptGlobalAndDocument(getter_AddRefs(doc
));
1100 NS_ENSURE_STATE(global
);
1102 // Activate JSAPI, and make sure that exceptions are reported on the right
1105 if (NS_WARN_IF(!jsapi
.Init(global
))) {
1106 return NS_ERROR_UNEXPECTED
;
1108 JSContext
* cx
= jsapi
.cx();
1110 nsAtom
* attrName
= aTypeAtom
;
1112 // Flag us as not a string so we don't keep trying to compile strings which
1113 // can't be compiled.
1114 aListener
->mHandlerIsString
= false;
1116 // mTarget may not be an Element if it's a window and we're
1117 // getting an inline event listener forwarded from <html:body> or
1118 // <html:frameset> or <xul:window> or the like.
1119 // XXX I don't like that we have to reference content from
1120 // here. The alternative is to store the event handler string on
1121 // the JSEventHandler itself, and that still doesn't address
1122 // the arg names issue.
1123 RefPtr
<Element
> element
= Element::FromEventTargetOrNull(mTarget
);
1124 MOZ_ASSERT(element
|| aBody
, "Where will we get our body?");
1125 nsAutoString handlerBody
;
1126 const nsAString
* body
= aBody
;
1128 if (aTypeAtom
== nsGkAtoms::onSVGLoad
) {
1129 attrName
= nsGkAtoms::onload
;
1130 } else if (aTypeAtom
== nsGkAtoms::onSVGScroll
) {
1131 attrName
= nsGkAtoms::onscroll
;
1132 } else if (aTypeAtom
== nsGkAtoms::onbeginEvent
) {
1133 attrName
= nsGkAtoms::onbegin
;
1134 } else if (aTypeAtom
== nsGkAtoms::onrepeatEvent
) {
1135 attrName
= nsGkAtoms::onrepeat
;
1136 } else if (aTypeAtom
== nsGkAtoms::onendEvent
) {
1137 attrName
= nsGkAtoms::onend
;
1138 } else if (aTypeAtom
== nsGkAtoms::onwebkitAnimationEnd
) {
1139 attrName
= nsGkAtoms::onwebkitanimationend
;
1140 } else if (aTypeAtom
== nsGkAtoms::onwebkitAnimationIteration
) {
1141 attrName
= nsGkAtoms::onwebkitanimationiteration
;
1142 } else if (aTypeAtom
== nsGkAtoms::onwebkitAnimationStart
) {
1143 attrName
= nsGkAtoms::onwebkitanimationstart
;
1144 } else if (aTypeAtom
== nsGkAtoms::onwebkitTransitionEnd
) {
1145 attrName
= nsGkAtoms::onwebkittransitionend
;
1148 element
->GetAttr(attrName
, handlerBody
);
1149 body
= &handlerBody
;
1152 aListener
= nullptr;
1154 nsAutoCString
url("-moz-evil:lying-event-listener"_ns
);
1156 MOZ_ASSERT(aElement
);
1157 nsIURI
* uri
= aElement
->OwnerDoc()->GetDocumentURI();
1162 nsCOMPtr
<nsPIDOMWindowInner
> win
=
1163 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget
);
1165 const char** argNames
;
1166 nsContentUtils::GetEventArgNames(aElement
->GetNameSpaceID(), aTypeAtom
, win
,
1167 &argCount
, &argNames
);
1169 // Wrap the event target, so that we can use it as the scope for the event
1170 // handler. Note that mTarget is different from aElement in the <body> case,
1171 // where mTarget is a Window.
1173 // The wrapScope doesn't really matter here, because the target will create
1174 // its reflector in the proper scope, and then we'll enter that realm.
1175 JS::Rooted
<JSObject
*> wrapScope(cx
, global
->GetGlobalJSObject());
1176 JS::Rooted
<JS::Value
> v(cx
);
1178 JSAutoRealm
ar(cx
, wrapScope
);
1179 nsresult rv
= nsContentUtils::WrapNative(cx
, mTarget
, &v
,
1180 /* aAllowWrapping = */ false);
1181 if (NS_WARN_IF(NS_FAILED(rv
))) {
1186 JS::Rooted
<JSObject
*> target(cx
, &v
.toObject());
1187 JSAutoRealm
ar(cx
, target
);
1189 // Now that we've entered the realm we actually care about, create our
1190 // scope chain. Note that we start with |element|, not aElement, because
1191 // mTarget is different from aElement in the <body> case, where mTarget is a
1192 // Window, and in that case we do not want the scope chain to include the body
1194 JS::RootedVector
<JSObject
*> scopeChain(cx
);
1195 if (!nsJSUtils::GetScopeChainForElement(cx
, element
, &scopeChain
)) {
1196 return NS_ERROR_OUT_OF_MEMORY
;
1199 nsDependentAtomString
str(attrName
);
1200 // Most of our names are short enough that we don't even have to malloc
1201 // the JS string stuff, so don't worry about playing games with
1202 // refcounting XPCOM stringbuffers.
1203 JS::Rooted
<JSString
*> jsStr(
1204 cx
, JS_NewUCStringCopyN(cx
, str
.BeginReading(), str
.Length()));
1205 NS_ENSURE_TRUE(jsStr
, NS_ERROR_OUT_OF_MEMORY
);
1207 // Get the reflector for |aElement|, so that we can pass to setElement.
1208 if (NS_WARN_IF(!GetOrCreateDOMReflector(cx
, aElement
, &v
))) {
1209 return NS_ERROR_FAILURE
;
1212 RefPtr
<JS::loader::ScriptFetchOptions
> fetchOptions
=
1213 new JS::loader::ScriptFetchOptions(
1214 CORS_NONE
, /* aNonce = */ u
""_ns
, RequestPriority::Auto
,
1215 JS::loader::ParserMetadata::NotParserInserted
,
1216 aElement
->OwnerDoc()->NodePrincipal());
1218 RefPtr
<JS::loader::EventScript
> eventScript
= new JS::loader::EventScript(
1219 aElement
->OwnerDoc()->GetReferrerPolicy(), fetchOptions
, uri
);
1221 JS::CompileOptions
options(cx
);
1222 // Use line 0 to make the function body starts from line 1.
1223 options
.setIntroductionType("eventHandler")
1224 .setFileAndLine(url
.get(), 0)
1225 .setDeferDebugMetadata(true);
1227 JS::Rooted
<JSObject
*> handler(cx
);
1228 result
= nsJSUtils::CompileFunction(jsapi
, scopeChain
, options
,
1229 nsAtomCString(aTypeAtom
), argCount
,
1230 argNames
, *body
, handler
.address());
1231 NS_ENSURE_SUCCESS(result
, result
);
1232 NS_ENSURE_TRUE(handler
, NS_ERROR_FAILURE
);
1234 JS::Rooted
<JS::Value
> privateValue(cx
, JS::PrivateValue(eventScript
));
1235 result
= nsJSUtils::UpdateFunctionDebugMetadata(jsapi
, handler
, options
,
1236 jsStr
, privateValue
);
1237 NS_ENSURE_SUCCESS(result
, result
);
1239 MOZ_ASSERT(js::IsObjectInContextCompartment(handler
, cx
));
1240 JS::Rooted
<JSObject
*> handlerGlobal(cx
, JS::CurrentGlobalOrNull(cx
));
1242 if (jsEventHandler
->EventName() == nsGkAtoms::onerror
&& win
) {
1243 RefPtr
<OnErrorEventHandlerNonNull
> handlerCallback
=
1244 new OnErrorEventHandlerNonNull(static_cast<JSContext
*>(nullptr),
1245 handler
, handlerGlobal
,
1246 /* aIncumbentGlobal = */ nullptr);
1247 jsEventHandler
->SetHandler(handlerCallback
);
1248 } else if (jsEventHandler
->EventName() == nsGkAtoms::onbeforeunload
&& win
) {
1249 RefPtr
<OnBeforeUnloadEventHandlerNonNull
> handlerCallback
=
1250 new OnBeforeUnloadEventHandlerNonNull(static_cast<JSContext
*>(nullptr),
1251 handler
, handlerGlobal
,
1252 /* aIncumbentGlobal = */ nullptr);
1253 jsEventHandler
->SetHandler(handlerCallback
);
1255 RefPtr
<EventHandlerNonNull
> handlerCallback
= new EventHandlerNonNull(
1256 static_cast<JSContext
*>(nullptr), handler
, handlerGlobal
,
1257 /* aIncumbentGlobal = */ nullptr);
1258 jsEventHandler
->SetHandler(handlerCallback
);
1264 bool EventListenerManager::HandleEventSingleListener(
1265 Listener
* aListener
, nsAtom
* aTypeAtom
, WidgetEvent
* aEvent
,
1266 Event
* aDOMEvent
, EventTarget
* aCurrentTarget
, bool aItemInShadowTree
) {
1267 if (!aEvent
->mCurrentTarget
) {
1268 aEvent
->mCurrentTarget
= aCurrentTarget
->GetTargetForDOMEvent();
1269 if (!aEvent
->mCurrentTarget
) {
1274 aEvent
->mFlags
.mInPassiveListener
= aListener
->mFlags
.mPassive
;
1276 nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
=
1277 WindowFromListener(aListener
, aTypeAtom
, aItemInShadowTree
);
1278 mozilla::dom::Event
* oldWindowEvent
= nullptr;
1280 oldWindowEvent
= innerWindow
->SetEvent(aDOMEvent
);
1283 nsresult result
= NS_OK
;
1286 EventListenerHolder
listenerHolder(aListener
->mListener
.Clone());
1288 // If this is a script handler and we haven't yet
1289 // compiled the event handler itself
1290 if ((aListener
->mListenerType
== Listener::eJSEventListener
) &&
1291 aListener
->mHandlerIsString
) {
1293 CompileEventHandlerInternal(aListener
, aTypeAtom
, nullptr, nullptr);
1294 aListener
= nullptr;
1297 if (NS_SUCCEEDED(result
)) {
1298 Maybe
<EventCallbackDebuggerNotificationGuard
> dbgGuard
;
1299 if (dom::ChromeUtils::IsDevToolsOpened()) {
1300 dbgGuard
.emplace(aCurrentTarget
, aDOMEvent
);
1304 // Event::currentTarget is set in EventDispatcher.
1305 if (listenerHolder
.HasWebIDLCallback()) {
1307 listenerHolder
.GetWebIDLCallback()->HandleEvent(aCurrentTarget
,
1309 result
= rv
.StealNSResult();
1311 // listenerHolder is holding a stack ref here.
1312 result
= MOZ_KnownLive(listenerHolder
.GetXPCOMCallback())
1313 ->HandleEvent(aDOMEvent
);
1318 Unused
<< innerWindow
->SetEvent(oldWindowEvent
);
1321 if (NS_FAILED(result
)) {
1322 aEvent
->mFlags
.mExceptionWasRaised
= true;
1324 aEvent
->mFlags
.mInPassiveListener
= false;
1325 return !aEvent
->mFlags
.mImmediatePropagationStopped
;
1328 /* static */ EventMessage
EventListenerManager::GetLegacyEventMessage(
1329 EventMessage aEventMessage
) {
1330 // webkit-prefixed legacy events:
1331 if (aEventMessage
== eTransitionEnd
) {
1332 return eWebkitTransitionEnd
;
1334 if (aEventMessage
== eAnimationStart
) {
1335 return eWebkitAnimationStart
;
1337 if (aEventMessage
== eAnimationEnd
) {
1338 return eWebkitAnimationEnd
;
1340 if (aEventMessage
== eAnimationIteration
) {
1341 return eWebkitAnimationIteration
;
1344 switch (aEventMessage
) {
1345 case eFullscreenChange
:
1346 return eMozFullscreenChange
;
1347 case eFullscreenError
:
1348 return eMozFullscreenError
;
1350 return aEventMessage
;
1354 EventMessage
EventListenerManager::GetEventMessage(nsAtom
* aEventName
) const {
1355 if (mIsMainThreadELM
) {
1356 return nsContentUtils::GetEventMessage(aEventName
);
1359 // The nsContentUtils event message hashtables aren't threadsafe, so just fall
1360 // back to eUnidentifiedEvent.
1361 return eUnidentifiedEvent
;
1364 EventMessage
EventListenerManager::GetEventMessageAndAtomForListener(
1365 const nsAString
& aType
, nsAtom
** aAtom
) {
1366 if (mIsMainThreadELM
) {
1367 return nsContentUtils::GetEventMessageAndAtomForListener(aType
, aAtom
);
1370 *aAtom
= NS_Atomize(u
"on"_ns
+ aType
).take();
1371 return eUnidentifiedEvent
;
1374 already_AddRefed
<nsPIDOMWindowInner
> EventListenerManager::WindowFromListener(
1375 Listener
* aListener
, nsAtom
* aTypeAtom
, bool aItemInShadowTree
) {
1376 nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
;
1377 if (!aItemInShadowTree
) {
1378 if (aListener
->mListener
.HasWebIDLCallback()) {
1379 CallbackObject
* callback
= aListener
->mListener
.GetWebIDLCallback();
1380 nsIGlobalObject
* global
= nullptr;
1382 global
= callback
->IncumbentGlobalOrNull();
1385 innerWindow
= global
->GetAsInnerWindow(); // Can be nullptr
1387 } else if (mTarget
) {
1388 // This ensures `window.event` can be set properly for
1389 // nsWindowRoot to handle KeyPress event.
1390 if (aListener
&& aTypeAtom
== nsGkAtoms::onkeypress
&&
1391 mTarget
->IsRootWindow()) {
1392 nsPIWindowRoot
* root
= mTarget
->AsWindowRoot();
1393 if (nsPIDOMWindowOuter
* outerWindow
= root
->GetWindow()) {
1394 innerWindow
= outerWindow
->GetCurrentInnerWindow();
1397 // Can't get the global from
1398 // listener->mListener.GetXPCOMCallback().
1399 // In most cases, it would be the same as for
1400 // the target, so let's do that.
1401 if (nsIGlobalObject
* global
= mTarget
->GetOwnerGlobal()) {
1402 innerWindow
= global
->GetAsInnerWindow();
1407 return innerWindow
.forget();
1410 Maybe
<size_t> EventListenerManager::EventListenerMap::EntryIndexForType(
1411 nsAtom
* aTypeAtom
) const {
1412 MOZ_ASSERT(aTypeAtom
);
1414 size_t matchIndexOrInsertionPoint
= 0;
1415 bool foundMatch
= BinarySearchIf(mEntries
, 0, mEntries
.Length(),
1416 ListenerMapEntryComparator(aTypeAtom
),
1417 &matchIndexOrInsertionPoint
);
1418 return foundMatch
? Some(matchIndexOrInsertionPoint
) : Nothing();
1421 Maybe
<size_t> EventListenerManager::EventListenerMap::EntryIndexForAllEvents()
1423 // If we have an entry for "all events listeners", it'll be at the beginning
1424 // of the list and its type atom will be null.
1425 return !mEntries
.IsEmpty() && mEntries
[0].mTypeAtom
== nullptr ? Some(0)
1429 RefPtr
<EventListenerManager::ListenerArray
>
1430 EventListenerManager::EventListenerMap::GetListenersForType(
1431 nsAtom
* aTypeAtom
) const {
1432 Maybe
<size_t> index
= EntryIndexForType(aTypeAtom
);
1433 return index
? mEntries
[*index
].mListeners
: nullptr;
1436 RefPtr
<EventListenerManager::ListenerArray
>
1437 EventListenerManager::EventListenerMap::GetListenersForAllEvents() const {
1438 Maybe
<size_t> index
= EntryIndexForAllEvents();
1439 return index
? mEntries
[*index
].mListeners
: nullptr;
1442 RefPtr
<EventListenerManager::ListenerArray
>
1443 EventListenerManager::EventListenerMap::GetOrCreateListenersForType(
1444 nsAtom
* aTypeAtom
) {
1445 MOZ_ASSERT(aTypeAtom
);
1446 size_t matchIndexOrInsertionPoint
= 0;
1447 bool foundMatch
= BinarySearchIf(mEntries
, 0, mEntries
.Length(),
1448 ListenerMapEntryComparator(aTypeAtom
),
1449 &matchIndexOrInsertionPoint
);
1451 return mEntries
[matchIndexOrInsertionPoint
].mListeners
;
1453 RefPtr
<ListenerArray
> listeners
= MakeRefPtr
<ListenerArray
>();
1454 mEntries
.InsertElementAt(matchIndexOrInsertionPoint
,
1455 EventListenerMapEntry
{aTypeAtom
, listeners
});
1460 RefPtr
<EventListenerManager::ListenerArray
>
1461 EventListenerManager::EventListenerMap::GetOrCreateListenersForAllEvents() {
1462 RefPtr
<ListenerArray
> listeners
= GetListenersForAllEvents();
1464 listeners
= MakeRefPtr
<ListenerArray
>();
1465 mEntries
.InsertElementAt(0, EventListenerMapEntry
{nullptr, listeners
});
1470 void EventListenerManager::HandleEventInternal(nsPresContext
* aPresContext
,
1471 WidgetEvent
* aEvent
,
1473 EventTarget
* aCurrentTarget
,
1474 nsEventStatus
* aEventStatus
,
1475 bool aItemInShadowTree
) {
1476 MOZ_ASSERT_IF(aEvent
->mMessage
!= eUnidentifiedEvent
, mIsMainThreadELM
);
1478 // Set the value of the internal PreventDefault flag properly based on
1480 if (!aEvent
->DefaultPrevented() &&
1481 *aEventStatus
== nsEventStatus_eConsumeNoDefault
) {
1482 // Assume that if only aEventStatus claims that the event has already been
1483 // consumed, the consumer is default event handler.
1484 aEvent
->PreventDefault();
1487 if (aEvent
->mFlags
.mImmediatePropagationStopped
) {
1491 Maybe
<AutoHandlingUserInputStatePusher
> userInputStatePusher
;
1492 Maybe
<AutoPopupStatePusher
> popupStatePusher
;
1493 if (mIsMainThreadELM
) {
1494 userInputStatePusher
.emplace(UserActivation::IsUserInteractionEvent(aEvent
),
1496 popupStatePusher
.emplace(
1497 PopupBlocker::GetEventPopupControlState(aEvent
, *aDOMEvent
));
1500 EventMessage eventMessage
= aEvent
->mMessage
;
1501 RefPtr
<nsAtom
> typeAtom
=
1502 eventMessage
== eUnidentifiedEvent
1503 ? aEvent
->mSpecifiedEventType
.get()
1504 : nsContentUtils::GetEventTypeFromMessage(eventMessage
);
1506 // Some messages don't have a corresponding type atom, e.g.
1507 // eMouseEnterIntoWidget. These events can't have a listener, so we
1512 bool hasAnyListenerForEventType
= false;
1514 // First, notify any "all events" listeners.
1515 if (RefPtr
<ListenerArray
> listenersForAllEvents
=
1516 mListenerMap
.GetListenersForAllEvents()) {
1517 HandleEventWithListenerArray(listenersForAllEvents
, typeAtom
, eventMessage
,
1518 aPresContext
, aEvent
, aDOMEvent
,
1519 aCurrentTarget
, aItemInShadowTree
);
1520 hasAnyListenerForEventType
= true;
1523 // Now look for listeners for typeAtom, and call them if we have any.
1524 bool hasAnyListenerMatchingGroup
= false;
1525 if (RefPtr
<ListenerArray
> listeners
=
1526 mListenerMap
.GetListenersForType(typeAtom
)) {
1527 hasAnyListenerMatchingGroup
= HandleEventWithListenerArray(
1528 listeners
, typeAtom
, eventMessage
, aPresContext
, aEvent
, aDOMEvent
,
1529 aCurrentTarget
, aItemInShadowTree
);
1530 hasAnyListenerForEventType
= true;
1533 if (!hasAnyListenerMatchingGroup
&& aEvent
->IsTrusted()) {
1534 // If we didn't find any matching listeners, and our event has a legacy
1535 // version, check the listeners for the legacy version.
1536 EventMessage legacyEventMessage
= GetLegacyEventMessage(eventMessage
);
1537 if (legacyEventMessage
!= eventMessage
) {
1539 GetLegacyEventMessage(legacyEventMessage
) == legacyEventMessage
,
1540 "Legacy event messages should not themselves have legacy versions");
1541 RefPtr
<nsAtom
> legacyTypeAtom
=
1542 nsContentUtils::GetEventTypeFromMessage(legacyEventMessage
);
1543 if (RefPtr
<ListenerArray
> legacyListeners
=
1544 mListenerMap
.GetListenersForType(legacyTypeAtom
)) {
1545 HandleEventWithListenerArray(
1546 legacyListeners
, legacyTypeAtom
, legacyEventMessage
, aPresContext
,
1547 aEvent
, aDOMEvent
, aCurrentTarget
, aItemInShadowTree
);
1548 hasAnyListenerForEventType
= true;
1553 aEvent
->mCurrentTarget
= nullptr;
1555 if (mIsMainThreadELM
&& !hasAnyListenerForEventType
) {
1556 if (aEvent
->mMessage
!= eUnidentifiedEvent
) {
1557 mNoListenerForEvents
[2] = mNoListenerForEvents
[1];
1558 mNoListenerForEvents
[1] = mNoListenerForEvents
[0];
1559 mNoListenerForEvents
[0] = aEvent
->mMessage
;
1561 mNoListenerForEventAtom
= aEvent
->mSpecifiedEventType
;
1565 if (aEvent
->DefaultPrevented()) {
1566 *aEventStatus
= nsEventStatus_eConsumeNoDefault
;
1570 bool EventListenerManager::HandleEventWithListenerArray(
1571 ListenerArray
* aListeners
, nsAtom
* aTypeAtom
, EventMessage aEventMessage
,
1572 nsPresContext
* aPresContext
, WidgetEvent
* aEvent
, Event
** aDOMEvent
,
1573 EventTarget
* aCurrentTarget
, bool aItemInShadowTree
) {
1574 auto ensureDOMEvent
= [&]() {
1576 // Lazily create the DOM event.
1577 // This is tiny bit slow, but happens only once per event.
1578 // Similar code also in EventDispatcher.
1579 nsCOMPtr
<EventTarget
> et
= aEvent
->mOriginalTarget
;
1580 RefPtr
<Event
> event
=
1581 EventDispatcher::CreateEvent(et
, aPresContext
, aEvent
, u
""_ns
);
1582 event
.forget(aDOMEvent
);
1584 return *aDOMEvent
!= nullptr;
1587 Maybe
<EventMessageAutoOverride
> eventMessageAutoOverride
;
1588 bool isOverridingEventMessage
= aEvent
->mMessage
!= aEventMessage
;
1589 bool hasAnyListenerMatchingGroup
= false;
1590 bool didReplaceOnceListener
= false;
1592 for (Listener
& listenerRef
: aListeners
->EndLimitedRange()) {
1593 Listener
* listener
= &listenerRef
;
1594 if (listener
->mListenerType
== Listener::eNoListener
) {
1595 // The listener is a placeholder value of a removed "once" listener.
1598 if (!listener
->mEnabled
) {
1599 // The listener has been disabled, for example by devtools.
1602 if (!listener
->MatchesEventGroup(aEvent
)) {
1605 hasAnyListenerMatchingGroup
= true;
1607 // Check that the phase is same in event and event listener. Also check
1608 // that the event is trusted or that the listener allows untrusted events.
1609 if (!listener
->MatchesEventPhase(aEvent
) ||
1610 !listener
->AllowsEventTrustedness(aEvent
)) {
1614 Maybe
<Listener
> listenerHolder
;
1615 if (listener
->mFlags
.mOnce
) {
1616 // Move the listener to the stack before handling the event.
1617 // The order is important, otherwise the listener could be
1618 // called again inside the listener.
1619 listenerHolder
.emplace(std::move(*listener
));
1620 listener
= listenerHolder
.ptr();
1621 didReplaceOnceListener
= true;
1623 if (ensureDOMEvent()) {
1624 if (isOverridingEventMessage
&& !eventMessageAutoOverride
) {
1625 // Override the domEvent's event-message (its .type) until we
1626 // finish traversing listeners (when eventMessageAutoOverride
1628 eventMessageAutoOverride
.emplace(*aDOMEvent
, aEventMessage
);
1630 if (!HandleEventSingleListener(listener
, aTypeAtom
, aEvent
, *aDOMEvent
,
1631 aCurrentTarget
, aItemInShadowTree
)) {
1637 if (didReplaceOnceListener
) {
1638 // If there are any once listeners replaced with a placeholder during the
1639 // loop above, we need to clean up them here. Note that this could clear
1640 // once listeners handled in some outer level as well, but that should not
1641 // affect the result.
1642 size_t oldLength
= aListeners
->Length();
1643 aListeners
->NonObservingRemoveElementsBy([](const Listener
& aListener
) {
1644 return aListener
.mListenerType
== Listener::eNoListener
;
1646 size_t newLength
= aListeners
->Length();
1647 if (newLength
== 0) {
1648 // Remove the entry that has now become empty.
1649 mListenerMap
.mEntries
.RemoveElementsBy([](EventListenerMapEntry
& entry
) {
1650 return entry
.mListeners
->IsEmpty();
1653 if (newLength
< oldLength
) {
1654 // Call NotifyEventListenerRemoved once for every removed listener.
1655 size_t removedCount
= oldLength
- newLength
;
1656 for (size_t i
= 0; i
< removedCount
; i
++) {
1657 NotifyEventListenerRemoved(aTypeAtom
);
1659 if (IsDeviceType(aTypeAtom
)) {
1660 // Call DisableDevice once for every removed listener.
1661 for (size_t i
= 0; i
< removedCount
; i
++) {
1662 DisableDevice(aTypeAtom
);
1668 return hasAnyListenerMatchingGroup
;
1671 void EventListenerManager::Disconnect() {
1673 RemoveAllListenersSilently();
1676 void EventListenerManager::AddEventListener(const nsAString
& aType
,
1677 EventListenerHolder aListenerHolder
,
1679 bool aWantsUntrusted
) {
1680 EventListenerFlags flags
;
1681 flags
.mCapture
= aUseCapture
;
1682 flags
.mAllowUntrustedEvents
= aWantsUntrusted
;
1683 return AddEventListenerByType(std::move(aListenerHolder
), aType
, flags
);
1686 void EventListenerManager::AddEventListener(
1687 const nsAString
& aType
, EventListenerHolder aListenerHolder
,
1688 const dom::AddEventListenerOptionsOrBoolean
& aOptions
,
1689 bool aWantsUntrusted
) {
1690 EventListenerFlags flags
;
1691 Optional
<bool> passive
;
1692 AbortSignal
* signal
= nullptr;
1693 if (aOptions
.IsBoolean()) {
1694 flags
.mCapture
= aOptions
.GetAsBoolean();
1696 const auto& options
= aOptions
.GetAsAddEventListenerOptions();
1697 flags
.mCapture
= options
.mCapture
;
1698 flags
.mInSystemGroup
= options
.mMozSystemGroup
;
1699 flags
.mOnce
= options
.mOnce
;
1700 if (options
.mPassive
.WasPassed()) {
1701 passive
.Construct(options
.mPassive
.Value());
1704 if (options
.mSignal
.WasPassed()) {
1705 signal
= &options
.mSignal
.Value();
1709 flags
.mAllowUntrustedEvents
= aWantsUntrusted
;
1710 return AddEventListenerByType(std::move(aListenerHolder
), aType
, flags
,
1714 void EventListenerManager::RemoveEventListener(
1715 const nsAString
& aType
, EventListenerHolder aListenerHolder
,
1717 EventListenerFlags flags
;
1718 flags
.mCapture
= aUseCapture
;
1719 RemoveEventListenerByType(std::move(aListenerHolder
), aType
, flags
);
1722 void EventListenerManager::RemoveEventListener(
1723 const nsAString
& aType
, EventListenerHolder aListenerHolder
,
1724 const dom::EventListenerOptionsOrBoolean
& aOptions
) {
1725 EventListenerFlags flags
;
1726 if (aOptions
.IsBoolean()) {
1727 flags
.mCapture
= aOptions
.GetAsBoolean();
1729 const auto& options
= aOptions
.GetAsEventListenerOptions();
1730 flags
.mCapture
= options
.mCapture
;
1731 flags
.mInSystemGroup
= options
.mMozSystemGroup
;
1733 RemoveEventListenerByType(std::move(aListenerHolder
), aType
, flags
);
1736 void EventListenerManager::AddListenerForAllEvents(EventListener
* aDOMListener
,
1738 bool aWantsUntrusted
,
1739 bool aSystemEventGroup
) {
1740 EventListenerFlags flags
;
1741 flags
.mCapture
= aUseCapture
;
1742 flags
.mAllowUntrustedEvents
= aWantsUntrusted
;
1743 flags
.mInSystemGroup
= aSystemEventGroup
;
1744 AddEventListenerInternal(EventListenerHolder(aDOMListener
), eAllEvents
,
1745 nullptr, flags
, false, true);
1748 void EventListenerManager::RemoveListenerForAllEvents(
1749 EventListener
* aDOMListener
, bool aUseCapture
, bool aSystemEventGroup
) {
1750 EventListenerFlags flags
;
1751 flags
.mCapture
= aUseCapture
;
1752 flags
.mInSystemGroup
= aSystemEventGroup
;
1753 RemoveEventListenerInternal(EventListenerHolder(aDOMListener
), nullptr, flags
,
1757 bool EventListenerManager::HasMutationListeners() {
1758 if (mMayHaveMutationListeners
) {
1759 for (const auto& entry
: mListenerMap
.mEntries
) {
1760 EventMessage message
= GetEventMessage(entry
.mTypeAtom
);
1761 if (message
>= eLegacyMutationEventFirst
&&
1762 message
<= eLegacyMutationEventLast
) {
1771 uint32_t EventListenerManager::MutationListenerBits() {
1773 if (mMayHaveMutationListeners
) {
1774 for (const auto& entry
: mListenerMap
.mEntries
) {
1775 EventMessage message
= GetEventMessage(entry
.mTypeAtom
);
1776 if (message
>= eLegacyMutationEventFirst
&&
1777 message
<= eLegacyMutationEventLast
) {
1778 if (message
== eLegacySubtreeModified
) {
1779 return NS_EVENT_BITS_MUTATION_ALL
;
1781 bits
|= MutationBitForEventType(message
);
1788 bool EventListenerManager::HasListenersFor(const nsAString
& aEventName
) const {
1789 RefPtr
<nsAtom
> atom
= NS_Atomize(u
"on"_ns
+ aEventName
);
1790 return HasListenersFor(atom
);
1793 bool EventListenerManager::HasListenersFor(nsAtom
* aEventNameWithOn
) const {
1794 return HasListenersForInternal(aEventNameWithOn
, false);
1797 bool EventListenerManager::HasNonSystemGroupListenersFor(
1798 nsAtom
* aEventNameWithOn
) const {
1799 return HasListenersForInternal(aEventNameWithOn
, true);
1802 bool EventListenerManager::HasListenersForInternal(
1803 nsAtom
* aEventNameWithOn
, bool aIgnoreSystemGroup
) const {
1806 aEventNameWithOn
->ToString(name
);
1808 NS_ASSERTION(StringBeginsWith(name
, u
"on"_ns
),
1809 "Event name does not start with 'on'");
1810 RefPtr
<ListenerArray
> listeners
=
1811 mListenerMap
.GetListenersForType(aEventNameWithOn
);
1816 MOZ_ASSERT(!listeners
->IsEmpty());
1818 if (!aIgnoreSystemGroup
) {
1822 // Check if any non-system-group listeners exist in `listeners`.
1823 for (const auto& listener
: listeners
->NonObservingRange()) {
1824 if (!listener
.mFlags
.mInSystemGroup
) {
1832 bool EventListenerManager::HasListeners() const {
1833 return !mListenerMap
.IsEmpty();
1836 nsresult
EventListenerManager::GetListenerInfo(
1837 nsTArray
<RefPtr
<nsIEventListenerInfo
>>& aList
) {
1838 nsCOMPtr
<EventTarget
> target
= mTarget
;
1839 NS_ENSURE_STATE(target
);
1841 for (const auto& entry
: mListenerMap
.mEntries
) {
1842 for (const Listener
& listener
: entry
.mListeners
->ForwardRange()) {
1843 // If this is a script handler and we haven't yet
1844 // compiled the event handler itself go ahead and compile it
1845 if (listener
.mListenerType
== Listener::eJSEventListener
&&
1846 listener
.mHandlerIsString
) {
1847 CompileEventHandlerInternal(const_cast<Listener
*>(&listener
),
1848 entry
.mTypeAtom
, nullptr, nullptr);
1850 nsAutoString eventType
;
1851 if (listener
.mAllEvents
) {
1852 eventType
.SetIsVoid(true);
1853 } else if (listener
.mListenerType
== Listener::eNoListener
) {
1856 eventType
.Assign(Substring(nsDependentAtomString(entry
.mTypeAtom
), 2));
1859 JS::Rooted
<JSObject
*> callback(RootingCx());
1860 JS::Rooted
<JSObject
*> callbackGlobal(RootingCx());
1861 if (JSEventHandler
* handler
= listener
.GetJSEventHandler()) {
1862 if (handler
->GetTypedEventHandler().HasEventHandler()) {
1863 CallbackFunction
* callbackFun
= handler
->GetTypedEventHandler().Ptr();
1864 callback
= callbackFun
->CallableOrNull();
1865 callbackGlobal
= callbackFun
->CallbackGlobalOrNull();
1867 // This will be null for cross-compartment event listeners
1868 // which have been destroyed.
1872 } else if (listener
.mListenerType
== Listener::eWebIDLListener
) {
1873 EventListener
* listenerCallback
=
1874 listener
.mListener
.GetWebIDLCallback();
1875 callback
= listenerCallback
->CallbackOrNull();
1876 callbackGlobal
= listenerCallback
->CallbackGlobalOrNull();
1878 // This will be null for cross-compartment event listeners
1879 // which have been destroyed.
1884 RefPtr
<EventListenerInfo
> info
= new EventListenerInfo(
1885 this, eventType
, callback
, callbackGlobal
, listener
.mFlags
.mCapture
,
1886 listener
.mFlags
.mAllowUntrustedEvents
, listener
.mFlags
.mInSystemGroup
,
1887 listener
.mListenerIsHandler
);
1888 aList
.AppendElement(info
.forget());
1894 EventListenerManager::Listener
* EventListenerManager::GetListenerFor(
1895 nsAString
& aType
, JSObject
* aListener
, bool aCapturing
,
1896 bool aAllowsUntrusted
, bool aInSystemEventGroup
, bool aIsHandler
) {
1897 NS_ENSURE_TRUE(aListener
, nullptr);
1899 RefPtr
<ListenerArray
> listeners
= ([&]() -> RefPtr
<ListenerArray
> {
1900 if (aType
.IsVoid()) {
1901 return mListenerMap
.GetListenersForAllEvents();
1904 for (auto& mapEntry
: mListenerMap
.mEntries
) {
1905 if (RefPtr
<nsAtom
> typeAtom
= mapEntry
.mTypeAtom
) {
1906 if (Substring(nsDependentAtomString(typeAtom
), 2).Equals(aType
)) {
1907 return mapEntry
.mListeners
;
1919 for (Listener
& listener
: listeners
->ForwardRange()) {
1920 if (listener
.mListenerType
== Listener::eNoListener
) {
1924 if (listener
.mFlags
.mCapture
!= aCapturing
||
1925 listener
.mFlags
.mAllowUntrustedEvents
!= aAllowsUntrusted
||
1926 listener
.mFlags
.mInSystemGroup
!= aInSystemEventGroup
) {
1931 if (JSEventHandler
* handler
= listener
.GetJSEventHandler()) {
1932 if (handler
->GetTypedEventHandler().HasEventHandler()) {
1933 if (handler
->GetTypedEventHandler().Ptr()->CallableOrNull() ==
1939 } else if (listener
.mListenerType
== Listener::eWebIDLListener
&&
1940 listener
.mListener
.GetWebIDLCallback()->CallbackOrNull() ==
1948 nsresult
EventListenerManager::IsListenerEnabled(
1949 nsAString
& aType
, JSObject
* aListener
, bool aCapturing
,
1950 bool aAllowsUntrusted
, bool aInSystemEventGroup
, bool aIsHandler
,
1952 Listener
* listener
=
1953 GetListenerFor(aType
, aListener
, aCapturing
, aAllowsUntrusted
,
1954 aInSystemEventGroup
, aIsHandler
);
1955 NS_ENSURE_TRUE(listener
, NS_ERROR_NOT_AVAILABLE
);
1956 *aEnabled
= listener
->mEnabled
;
1960 nsresult
EventListenerManager::SetListenerEnabled(
1961 nsAString
& aType
, JSObject
* aListener
, bool aCapturing
,
1962 bool aAllowsUntrusted
, bool aInSystemEventGroup
, bool aIsHandler
,
1964 Listener
* listener
=
1965 GetListenerFor(aType
, aListener
, aCapturing
, aAllowsUntrusted
,
1966 aInSystemEventGroup
, aIsHandler
);
1967 NS_ENSURE_TRUE(listener
, NS_ERROR_NOT_AVAILABLE
);
1968 listener
->mEnabled
= aEnabled
;
1970 // We may have enabled some listener, clear the cache for which events
1971 // we don't have listeners.
1972 ClearNoListenersForEvents();
1973 mNoListenerForEventAtom
= nullptr;
1978 bool EventListenerManager::HasUnloadListeners() {
1979 return mListenerMap
.GetListenersForType(nsGkAtoms::onunload
) != nullptr;
1982 bool EventListenerManager::HasBeforeUnloadListeners() {
1983 return mListenerMap
.GetListenersForType(nsGkAtoms::onbeforeunload
) != nullptr;
1986 void EventListenerManager::SetEventHandler(nsAtom
* aEventName
,
1987 EventHandlerNonNull
* aHandler
) {
1989 RemoveEventHandler(aEventName
);
1993 // Untrusted events are always permitted for non-chrome script
1995 SetEventHandlerInternal(
1996 aEventName
, TypedEventHandler(aHandler
),
1997 !mIsMainThreadELM
|| !nsContentUtils::IsCallerChrome());
2000 void EventListenerManager::SetEventHandler(
2001 OnErrorEventHandlerNonNull
* aHandler
) {
2003 RemoveEventHandler(nsGkAtoms::onerror
);
2007 // Untrusted events are always permitted on workers and for non-chrome script
2008 // on the main thread.
2009 bool allowUntrusted
= !mIsMainThreadELM
|| !nsContentUtils::IsCallerChrome();
2011 SetEventHandlerInternal(nsGkAtoms::onerror
, TypedEventHandler(aHandler
),
2015 void EventListenerManager::SetEventHandler(
2016 OnBeforeUnloadEventHandlerNonNull
* aHandler
) {
2018 RemoveEventHandler(nsGkAtoms::onbeforeunload
);
2022 // Untrusted events are always permitted for non-chrome script
2024 SetEventHandlerInternal(
2025 nsGkAtoms::onbeforeunload
, TypedEventHandler(aHandler
),
2026 !mIsMainThreadELM
|| !nsContentUtils::IsCallerChrome());
2029 const TypedEventHandler
* EventListenerManager::GetTypedEventHandler(
2030 nsAtom
* aEventName
) {
2031 Listener
* listener
= FindEventHandler(aEventName
);
2037 JSEventHandler
* jsEventHandler
= listener
->GetJSEventHandler();
2039 if (listener
->mHandlerIsString
) {
2040 CompileEventHandlerInternal(listener
, aEventName
, nullptr, nullptr);
2043 const TypedEventHandler
& typedHandler
=
2044 jsEventHandler
->GetTypedEventHandler();
2045 return typedHandler
.HasEventHandler() ? &typedHandler
: nullptr;
2048 size_t EventListenerManager::SizeOfIncludingThis(
2049 MallocSizeOf aMallocSizeOf
) const {
2050 return aMallocSizeOf(this) + mListenerMap
.SizeOfExcludingThis(aMallocSizeOf
);
2053 size_t EventListenerManager::EventListenerMap::SizeOfExcludingThis(
2054 MallocSizeOf aMallocSizeOf
) const {
2055 size_t n
= mEntries
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
2056 for (const auto& entry
: mEntries
) {
2057 n
+= entry
.SizeOfExcludingThis(aMallocSizeOf
);
2062 size_t EventListenerManager::EventListenerMapEntry::SizeOfExcludingThis(
2063 MallocSizeOf aMallocSizeOf
) const {
2064 return mListeners
->SizeOfIncludingThis(aMallocSizeOf
);
2067 size_t EventListenerManager::ListenerArray::SizeOfIncludingThis(
2068 MallocSizeOf aMallocSizeOf
) const {
2069 size_t n
= aMallocSizeOf(this);
2070 n
+= ShallowSizeOfExcludingThis(aMallocSizeOf
);
2071 for (const auto& listener
: NonObservingRange()) {
2072 JSEventHandler
* jsEventHandler
= listener
.GetJSEventHandler();
2073 if (jsEventHandler
) {
2074 n
+= jsEventHandler
->SizeOfIncludingThis(aMallocSizeOf
);
2080 uint32_t EventListenerManager::ListenerCount() const {
2082 for (const auto& entry
: mListenerMap
.mEntries
) {
2083 count
+= entry
.mListeners
->Length();
2088 void EventListenerManager::MarkForCC() {
2089 for (const auto& entry
: mListenerMap
.mEntries
) {
2090 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2091 JSEventHandler
* jsEventHandler
= listener
.GetJSEventHandler();
2092 if (jsEventHandler
) {
2093 const TypedEventHandler
& typedHandler
=
2094 jsEventHandler
->GetTypedEventHandler();
2095 if (typedHandler
.HasEventHandler()) {
2096 typedHandler
.Ptr()->MarkForCC();
2098 } else if (listener
.mListenerType
== Listener::eWebIDLListener
) {
2099 listener
.mListener
.GetWebIDLCallback()->MarkForCC();
2103 if (mRefCnt
.IsPurple()) {
2104 mRefCnt
.RemovePurple();
2108 void EventListenerManager::TraceListeners(JSTracer
* aTrc
) {
2109 for (const auto& entry
: mListenerMap
.mEntries
) {
2110 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2111 JSEventHandler
* jsEventHandler
= listener
.GetJSEventHandler();
2112 if (jsEventHandler
) {
2113 const TypedEventHandler
& typedHandler
=
2114 jsEventHandler
->GetTypedEventHandler();
2115 if (typedHandler
.HasEventHandler()) {
2116 mozilla::TraceScriptHolder(typedHandler
.Ptr(), aTrc
);
2118 } else if (listener
.mListenerType
== Listener::eWebIDLListener
) {
2119 mozilla::TraceScriptHolder(listener
.mListener
.GetWebIDLCallback(),
2122 // We might have eWrappedJSListener, but that is the legacy type for
2123 // JS implemented event listeners, and trickier to handle here.
2128 bool EventListenerManager::HasNonSystemGroupListenersForUntrustedKeyEvents() {
2129 for (const auto& entry
: mListenerMap
.mEntries
) {
2130 if (entry
.mTypeAtom
!= nsGkAtoms::onkeydown
&&
2131 entry
.mTypeAtom
!= nsGkAtoms::onkeypress
&&
2132 entry
.mTypeAtom
!= nsGkAtoms::onkeyup
) {
2135 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2136 if (!listener
.mFlags
.mInSystemGroup
&&
2137 listener
.mFlags
.mAllowUntrustedEvents
) {
2145 bool EventListenerManager::
2146 HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents() {
2147 for (const auto& entry
: mListenerMap
.mEntries
) {
2148 if (entry
.mTypeAtom
!= nsGkAtoms::onkeydown
&&
2149 entry
.mTypeAtom
!= nsGkAtoms::onkeypress
&&
2150 entry
.mTypeAtom
!= nsGkAtoms::onkeyup
) {
2153 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2154 if (!listener
.mFlags
.mPassive
&& !listener
.mFlags
.mInSystemGroup
&&
2155 listener
.mFlags
.mAllowUntrustedEvents
) {
2163 bool EventListenerManager::HasApzAwareListeners() {
2164 if (!mIsMainThreadELM
) {
2168 for (const auto& entry
: mListenerMap
.mEntries
) {
2169 if (!IsApzAwareEvent(entry
.mTypeAtom
)) {
2172 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2173 if (!listener
.mFlags
.mPassive
) {
2181 static bool IsWheelEventType(nsAtom
* aEvent
) {
2182 if (aEvent
== nsGkAtoms::onwheel
|| aEvent
== nsGkAtoms::onDOMMouseScroll
||
2183 aEvent
== nsGkAtoms::onmousewheel
||
2184 aEvent
== nsGkAtoms::onMozMousePixelScroll
) {
2190 bool EventListenerManager::IsApzAwareEvent(nsAtom
* aEvent
) {
2191 if (IsWheelEventType(aEvent
)) {
2194 // In theory we should schedule a repaint if the touch event pref changes,
2195 // because the event regions might be out of date. In practice that seems like
2196 // overkill because users generally shouldn't be flipping this pref, much
2197 // less expecting touch listeners on the page to immediately start preventing
2198 // scrolling without so much as a repaint. Tests that we write can work
2199 // around this constraint easily enough.
2200 if (aEvent
== nsGkAtoms::ontouchstart
|| aEvent
== nsGkAtoms::ontouchmove
) {
2201 return TouchEvent::PrefEnabled(
2202 nsContentUtils::GetDocShellForEventTarget(mTarget
));
2207 bool EventListenerManager::HasNonPassiveWheelListener() {
2208 MOZ_ASSERT(NS_IsMainThread());
2209 for (const auto& entry
: mListenerMap
.mEntries
) {
2210 if (!IsWheelEventType(entry
.mTypeAtom
)) {
2213 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2214 if (!listener
.mFlags
.mPassive
) {
2222 void EventListenerManager::RemoveAllListeners() {
2223 while (!mListenerMap
.IsEmpty()) {
2224 size_t entryIndex
= mListenerMap
.mEntries
.Length() - 1;
2225 EventListenerMapEntry
& entry
= mListenerMap
.mEntries
[entryIndex
];
2226 RefPtr
<nsAtom
> type
= entry
.mTypeAtom
;
2227 MOZ_ASSERT(!entry
.mListeners
->IsEmpty());
2228 size_t idx
= entry
.mListeners
->Length() - 1;
2229 entry
.mListeners
->RemoveElementAt(idx
);
2230 if (entry
.mListeners
->IsEmpty()) {
2231 mListenerMap
.mEntries
.RemoveElementAt(entryIndex
);
2233 NotifyEventListenerRemoved(type
);
2234 if (IsDeviceType(type
)) {
2235 DisableDevice(type
);
2240 already_AddRefed
<nsIScriptGlobalObject
>
2241 EventListenerManager::GetScriptGlobalAndDocument(Document
** aDoc
) {
2242 nsCOMPtr
<Document
> doc
;
2243 nsCOMPtr
<nsPIDOMWindowInner
> win
;
2244 if (nsINode
* node
= nsINode::FromEventTargetOrNull(mTarget
)) {
2245 // Try to get context from doc
2246 doc
= node
->OwnerDoc();
2247 if (doc
->IsLoadedAsData()) {
2251 win
= do_QueryInterface(doc
->GetScopeObject());
2252 } else if ((win
= GetTargetAsInnerWindow())) {
2253 doc
= win
->GetExtantDoc();
2256 if (!win
|| !win
->IsCurrentInnerWindow()) {
2261 nsCOMPtr
<nsIScriptGlobalObject
> global
= do_QueryInterface(win
);
2262 return global
.forget();
2265 EventListenerManager::ListenerSignalFollower::ListenerSignalFollower(
2266 EventListenerManager
* aListenerManager
,
2267 EventListenerManager::Listener
* aListener
, nsAtom
* aTypeAtom
)
2268 : dom::AbortFollower(),
2269 mListenerManager(aListenerManager
),
2270 mListener(aListener
->mListener
.Clone()),
2271 mTypeAtom(aTypeAtom
),
2272 mAllEvents(aListener
->mAllEvents
),
2273 mFlags(aListener
->mFlags
){};
2275 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower
)
2277 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower
)
2278 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower
)
2280 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
2281 EventListenerManager::ListenerSignalFollower
)
2282 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener
)
2283 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2285 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
2286 EventListenerManager::ListenerSignalFollower
)
2287 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener
)
2288 tmp
->mListenerManager
= nullptr;
2289 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2291 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
2292 EventListenerManager::ListenerSignalFollower
)
2293 NS_INTERFACE_MAP_ENTRY(nsISupports
)
2294 NS_INTERFACE_MAP_END
2296 void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() {
2297 if (mListenerManager
) {
2298 RefPtr
<EventListenerManager
> elm
= mListenerManager
;
2299 mListenerManager
= nullptr;
2300 elm
->RemoveEventListenerInternal(std::move(mListener
), mTypeAtom
, mFlags
,
2305 } // namespace mozilla