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();
459 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
460 if (Document
* doc
= window
->GetExtantDoc()) {
461 doc
->SetUseCounter(eUseCounter_custom_onstart
);
466 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
467 if (Document
* doc
= window
->GetExtantDoc()) {
468 doc
->SetUseCounter(eUseCounter_custom_onbounce
);
473 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
474 if (Document
* doc
= window
->GetExtantDoc()) {
475 doc
->SetUseCounter(eUseCounter_custom_onfinish
);
479 case eScrollPortOverflow
:
480 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
481 if (Document
* doc
= window
->GetExtantDoc()) {
482 doc
->SetUseCounter(eUseCounter_custom_onoverflow
);
486 case eScrollPortUnderflow
:
487 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
488 if (Document
* doc
= window
->GetExtantDoc()) {
489 doc
->SetUseCounter(eUseCounter_custom_onunderflow
);
493 case eLegacyMouseLineOrPageScroll
:
494 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
495 if (Document
* doc
= window
->GetExtantDoc()) {
496 doc
->SetUseCounter(eUseCounter_custom_ondommousescroll
);
500 case eLegacyMousePixelScroll
:
501 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
502 if (Document
* doc
= window
->GetExtantDoc()) {
503 doc
->SetUseCounter(eUseCounter_custom_onmozmousepixelscroll
);
507 case eTransitionStart
:
510 case eTransitionCancel
:
511 case eWebkitTransitionEnd
:
512 mMayHaveTransitionEventListener
= true;
513 if (nsPIDOMWindowInner
* window
= GetInnerWindowForTarget()) {
514 window
->SetHasTransitionEventListeners();
517 case eFormCheckboxStateChange
:
518 nsContentUtils::SetMayHaveFormCheckboxStateChangeListeners();
520 case eFormRadioStateChange
:
521 nsContentUtils::SetMayHaveFormRadioStateChangeListeners();
524 // XXX Use NS_ASSERTION here to print resolvedEventMessage since
525 // MOZ_ASSERT can take only string literal, not pointer to
528 resolvedEventMessage
< eLegacyMutationEventFirst
||
529 resolvedEventMessage
> eLegacyMutationEventLast
,
530 nsPrintfCString("You added new mutation event, but it's not "
531 "handled above, resolvedEventMessage=%s",
532 ToChar(resolvedEventMessage
))
534 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onpointerenter
,
535 nsPrintfCString("resolvedEventMessage=%s",
536 ToChar(resolvedEventMessage
))
538 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onpointerleave
,
539 nsPrintfCString("resolvedEventMessage=%s",
540 ToChar(resolvedEventMessage
))
543 resolvedEventMessage
< eGamepadEventFirst
||
544 resolvedEventMessage
> eGamepadEventLast
,
545 nsPrintfCString("You added new gamepad event, but it's not "
546 "handled above, resolvedEventMessage=%s",
547 ToChar(resolvedEventMessage
))
549 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondeviceorientation
,
550 nsPrintfCString("resolvedEventMessage=%s",
551 ToChar(resolvedEventMessage
))
553 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondeviceorientationabsolute
,
554 nsPrintfCString("resolvedEventMessage=%s",
555 ToChar(resolvedEventMessage
))
557 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onuserproximity
,
558 nsPrintfCString("resolvedEventMessage=%s",
559 ToChar(resolvedEventMessage
))
561 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondevicelight
,
562 nsPrintfCString("resolvedEventMessage=%s",
563 ToChar(resolvedEventMessage
))
565 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ondevicemotion
,
566 nsPrintfCString("resolvedEventMessage=%s",
567 ToChar(resolvedEventMessage
))
569 #if defined(MOZ_WIDGET_ANDROID)
570 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onorientationchange
,
571 nsPrintfCString("resolvedEventMessage=%s",
572 ToChar(resolvedEventMessage
))
574 #endif // #if defined(MOZ_WIDGET_ANDROID)
575 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchstart
,
576 nsPrintfCString("resolvedEventMessage=%s",
577 ToChar(resolvedEventMessage
))
579 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchend
,
580 nsPrintfCString("resolvedEventMessage=%s",
581 ToChar(resolvedEventMessage
))
583 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchmove
,
584 nsPrintfCString("resolvedEventMessage=%s",
585 ToChar(resolvedEventMessage
))
587 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::ontouchcancel
,
588 nsPrintfCString("resolvedEventMessage=%s",
589 ToChar(resolvedEventMessage
))
591 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onmouseenter
,
592 nsPrintfCString("resolvedEventMessage=%s",
593 ToChar(resolvedEventMessage
))
595 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onmouseleave
,
596 nsPrintfCString("resolvedEventMessage=%s",
597 ToChar(resolvedEventMessage
))
599 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onbeforeinput
,
600 nsPrintfCString("resolvedEventMessage=%s",
601 ToChar(resolvedEventMessage
))
603 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onselectionchange
,
604 nsPrintfCString("resolvedEventMessage=%s",
605 ToChar(resolvedEventMessage
))
607 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onselect
,
608 nsPrintfCString("resolvedEventMessage=%s",
609 ToChar(resolvedEventMessage
))
611 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onstart
,
612 nsPrintfCString("resolvedEventMessage=%s",
613 ToChar(resolvedEventMessage
))
615 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onbounce
,
616 nsPrintfCString("resolvedEventMessage=%s",
617 ToChar(resolvedEventMessage
))
619 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onfinish
,
620 nsPrintfCString("resolvedEventMessage=%s",
621 ToChar(resolvedEventMessage
))
623 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onoverflow
,
624 nsPrintfCString("resolvedEventMessage=%s",
625 ToChar(resolvedEventMessage
))
627 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onunderflow
,
628 nsPrintfCString("resolvedEventMessage=%s",
629 ToChar(resolvedEventMessage
))
631 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onDOMMouseScroll
,
632 nsPrintfCString("resolvedEventMessage=%s",
633 ToChar(resolvedEventMessage
))
635 NS_ASSERTION(aTypeAtom
!= nsGkAtoms::onMozMousePixelScroll
,
636 nsPrintfCString("resolvedEventMessage=%s",
637 ToChar(resolvedEventMessage
))
643 if (mIsMainThreadELM
&& !aFlags
.mPassive
&& IsApzAwareEvent(aTypeAtom
)) {
644 ProcessApzAwareEventListenerAdd();
648 mTarget
->EventListenerAdded(aTypeAtom
);
651 if (mIsMainThreadELM
&& mTarget
) {
652 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
,
657 void EventListenerManager::ProcessApzAwareEventListenerAdd() {
658 Document
* doc
= nullptr;
660 // Mark the node as having apz aware listeners
661 if (nsINode
* node
= nsINode::FromEventTargetOrNull(mTarget
)) {
662 node
->SetMayBeApzAware();
663 doc
= node
->OwnerDoc();
666 // Schedule a paint so event regions on the layer tree gets updated
668 if (nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow()) {
669 doc
= window
->GetExtantDoc();
673 if (nsCOMPtr
<DOMEventTargetHelper
> helper
= do_QueryInterface(mTarget
)) {
674 if (nsPIDOMWindowInner
* window
= helper
->GetOwner()) {
675 doc
= window
->GetExtantDoc();
680 if (doc
&& gfxPlatform::AsyncPanZoomEnabled()) {
681 PresShell
* presShell
= doc
->GetPresShell();
683 nsIFrame
* f
= presShell
->GetRootFrame();
691 bool EventListenerManager::IsDeviceType(nsAtom
* aTypeAtom
) {
692 return aTypeAtom
== nsGkAtoms::ondeviceorientation
||
693 aTypeAtom
== nsGkAtoms::ondeviceorientationabsolute
||
694 aTypeAtom
== nsGkAtoms::ondevicemotion
||
695 aTypeAtom
== nsGkAtoms::ondevicelight
696 #if defined(MOZ_WIDGET_ANDROID)
697 || aTypeAtom
== nsGkAtoms::onorientationchange
699 || aTypeAtom
== nsGkAtoms::onuserproximity
;
702 void EventListenerManager::EnableDevice(nsAtom
* aTypeAtom
) {
703 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow();
708 if (aTypeAtom
== nsGkAtoms::ondeviceorientation
) {
709 #ifdef MOZ_WIDGET_ANDROID
710 // Falls back to SENSOR_ROTATION_VECTOR and SENSOR_ORIENTATION if
711 // unavailable on device.
712 window
->EnableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR
);
713 window
->EnableDeviceSensor(SENSOR_ROTATION_VECTOR
);
715 window
->EnableDeviceSensor(SENSOR_ORIENTATION
);
720 if (aTypeAtom
== nsGkAtoms::ondeviceorientationabsolute
) {
721 #ifdef MOZ_WIDGET_ANDROID
722 // Falls back to SENSOR_ORIENTATION if unavailable on device.
723 window
->EnableDeviceSensor(SENSOR_ROTATION_VECTOR
);
725 window
->EnableDeviceSensor(SENSOR_ORIENTATION
);
730 if (aTypeAtom
== nsGkAtoms::onuserproximity
) {
731 window
->EnableDeviceSensor(SENSOR_PROXIMITY
);
735 if (aTypeAtom
== nsGkAtoms::ondevicelight
) {
736 window
->EnableDeviceSensor(SENSOR_LIGHT
);
740 if (aTypeAtom
== nsGkAtoms::ondevicemotion
) {
741 window
->EnableDeviceSensor(SENSOR_ACCELERATION
);
742 window
->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION
);
743 window
->EnableDeviceSensor(SENSOR_GYROSCOPE
);
747 #if defined(MOZ_WIDGET_ANDROID)
748 if (aTypeAtom
== nsGkAtoms::onorientationchange
) {
749 window
->EnableOrientationChangeListener();
754 NS_WARNING("Enabling an unknown device sensor.");
757 void EventListenerManager::DisableDevice(nsAtom
* aTypeAtom
) {
758 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetTargetAsInnerWindow();
763 if (aTypeAtom
== nsGkAtoms::ondeviceorientation
) {
764 #ifdef MOZ_WIDGET_ANDROID
765 // Disable all potential fallback sensors.
766 window
->DisableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR
);
767 window
->DisableDeviceSensor(SENSOR_ROTATION_VECTOR
);
769 window
->DisableDeviceSensor(SENSOR_ORIENTATION
);
773 if (aTypeAtom
== nsGkAtoms::ondeviceorientationabsolute
) {
774 #ifdef MOZ_WIDGET_ANDROID
775 window
->DisableDeviceSensor(SENSOR_ROTATION_VECTOR
);
777 window
->DisableDeviceSensor(SENSOR_ORIENTATION
);
781 if (aTypeAtom
== nsGkAtoms::ondevicemotion
) {
782 window
->DisableDeviceSensor(SENSOR_ACCELERATION
);
783 window
->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION
);
784 window
->DisableDeviceSensor(SENSOR_GYROSCOPE
);
788 if (aTypeAtom
== nsGkAtoms::onuserproximity
) {
789 window
->DisableDeviceSensor(SENSOR_PROXIMITY
);
793 if (aTypeAtom
== nsGkAtoms::ondevicelight
) {
794 window
->DisableDeviceSensor(SENSOR_LIGHT
);
798 #if defined(MOZ_WIDGET_ANDROID)
799 if (aTypeAtom
== nsGkAtoms::onorientationchange
) {
800 window
->DisableOrientationChangeListener();
805 NS_WARNING("Disabling an unknown device sensor.");
808 void EventListenerManager::NotifyEventListenerRemoved(nsAtom
* aUserType
) {
809 // If the following code is changed, other callsites of EventListenerRemoved
810 // and NotifyAboutMainThreadListenerChange should be changed too.
811 ClearNoListenersForEvents();
812 mNoListenerForEventAtom
= nullptr;
814 mTarget
->EventListenerRemoved(aUserType
);
816 if (mIsMainThreadELM
&& mTarget
) {
817 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
,
822 void EventListenerManager::RemoveEventListenerInternal(
823 EventListenerHolder aListenerHolder
, nsAtom
* aUserType
,
824 const EventListenerFlags
& aFlags
, bool aAllEvents
) {
825 if (!aListenerHolder
|| (!aUserType
&& !aAllEvents
) || mClearingListeners
) {
829 Maybe
<size_t> entryIndex
= aAllEvents
830 ? mListenerMap
.EntryIndexForAllEvents()
831 : mListenerMap
.EntryIndexForType(aUserType
);
836 ListenerArray
& listenerArray
= *mListenerMap
.mEntries
[*entryIndex
].mListeners
;
838 Maybe
<uint32_t> listenerIndex
= [&]() -> Maybe
<uint32_t> {
839 uint32_t count
= listenerArray
.Length();
840 for (uint32_t i
= 0; i
< count
; ++i
) {
841 Listener
* listener
= &listenerArray
.ElementAt(i
);
842 if (listener
->mListener
== aListenerHolder
&&
843 listener
->mFlags
.EqualsForRemoval(aFlags
)) {
850 if (!listenerIndex
) {
854 listenerArray
.RemoveElementAt(*listenerIndex
);
855 if (listenerArray
.IsEmpty()) {
856 mListenerMap
.mEntries
.RemoveElementAt(*entryIndex
);
859 RefPtr
<EventListenerManager
> kungFuDeathGrip(this);
861 NotifyEventListenerRemoved(aUserType
);
862 if (IsDeviceType(aUserType
)) {
863 DisableDevice(aUserType
);
868 static bool IsDefaultPassiveWhenOnRoot(EventMessage aMessage
) {
869 if (aMessage
== eTouchStart
|| aMessage
== eTouchMove
|| aMessage
== eWheel
||
870 aMessage
== eLegacyMouseLineOrPageScroll
||
871 aMessage
== eLegacyMousePixelScroll
) {
877 static bool IsRootEventTarget(EventTarget
* aTarget
) {
881 if (aTarget
->IsInnerWindow()) {
884 const nsINode
* node
= nsINode::FromEventTarget(aTarget
);
888 Document
* doc
= node
->OwnerDoc();
889 return node
== doc
|| node
== doc
->GetRootElement() || node
== doc
->GetBody();
892 void EventListenerManager::MaybeMarkPassive(EventMessage aMessage
,
893 EventListenerFlags
& aFlags
) {
894 if (!mIsMainThreadELM
) {
897 if (!IsDefaultPassiveWhenOnRoot(aMessage
)) {
900 if (!IsRootEventTarget(mTarget
)) {
903 aFlags
.mPassive
= true;
906 void EventListenerManager::AddEventListenerByType(
907 EventListenerHolder aListenerHolder
, const nsAString
& aType
,
908 const EventListenerFlags
& aFlags
, const Optional
<bool>& aPassive
,
909 AbortSignal
* aSignal
) {
911 EventMessage message
=
912 GetEventMessageAndAtomForListener(aType
, getter_AddRefs(atom
));
914 EventListenerFlags flags
= aFlags
;
915 if (aPassive
.WasPassed()) {
916 flags
.mPassive
= aPassive
.Value();
918 MaybeMarkPassive(message
, flags
);
921 AddEventListenerInternal(std::move(aListenerHolder
), message
, atom
, flags
,
922 false, false, aSignal
);
925 void EventListenerManager::RemoveEventListenerByType(
926 EventListenerHolder aListenerHolder
, const nsAString
& aType
,
927 const EventListenerFlags
& aFlags
) {
929 (void)GetEventMessageAndAtomForListener(aType
, getter_AddRefs(atom
));
930 RemoveEventListenerInternal(std::move(aListenerHolder
), atom
, aFlags
);
933 EventListenerManager::Listener
* EventListenerManager::FindEventHandler(
935 // Run through the listeners for this type and see if a script
936 // listener is registered
937 RefPtr
<ListenerArray
> listeners
= mListenerMap
.GetListenersForType(aTypeAtom
);
942 uint32_t count
= listeners
->Length();
943 for (uint32_t i
= 0; i
< count
; ++i
) {
944 Listener
* listener
= &listeners
->ElementAt(i
);
945 if (listener
->mListenerIsHandler
) {
952 EventListenerManager::Listener
* EventListenerManager::SetEventHandlerInternal(
953 nsAtom
* aName
, const TypedEventHandler
& aTypedHandler
,
954 bool aPermitUntrustedEvents
) {
957 EventMessage eventMessage
= GetEventMessage(aName
);
958 Listener
* listener
= FindEventHandler(aName
);
961 // If we didn't find a script listener or no listeners existed
962 // create and add a new one.
963 EventListenerFlags flags
;
964 flags
.mListenerIsJSListener
= true;
965 MaybeMarkPassive(eventMessage
, flags
);
967 nsCOMPtr
<JSEventHandler
> jsEventHandler
;
968 NS_NewJSEventHandler(mTarget
, aName
, aTypedHandler
,
969 getter_AddRefs(jsEventHandler
));
970 AddEventListenerInternal(EventListenerHolder(jsEventHandler
), eventMessage
,
973 listener
= FindEventHandler(aName
);
975 JSEventHandler
* jsEventHandler
= listener
->GetJSEventHandler();
976 MOZ_ASSERT(jsEventHandler
,
977 "How can we have an event handler with no JSEventHandler?");
979 bool same
= jsEventHandler
->GetTypedEventHandler() == aTypedHandler
;
980 // Possibly the same listener, but update still the context and scope.
981 jsEventHandler
->SetHandler(aTypedHandler
);
982 if (mTarget
&& !same
) {
983 mTarget
->EventListenerRemoved(aName
);
984 mTarget
->EventListenerAdded(aName
);
986 if (mIsMainThreadELM
&& mTarget
) {
987 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget
, aName
);
991 // Set flag to indicate possible need for compilation later
992 listener
->mHandlerIsString
= !aTypedHandler
.HasEventHandler();
993 if (aPermitUntrustedEvents
) {
994 listener
->mFlags
.mAllowUntrustedEvents
= true;
995 mMayHaveListenersForUntrustedEvents
= true;
1001 nsresult
EventListenerManager::SetEventHandler(nsAtom
* aName
,
1002 const nsAString
& aBody
,
1003 bool aDeferCompilation
,
1004 bool aPermitUntrustedEvents
,
1005 Element
* aElement
) {
1006 auto removeEventHandler
= MakeScopeExit([&] { RemoveEventHandler(aName
); });
1008 nsCOMPtr
<Document
> doc
;
1009 nsCOMPtr
<nsIScriptGlobalObject
> global
=
1010 GetScriptGlobalAndDocument(getter_AddRefs(doc
));
1013 // This can happen; for example this document might have been
1018 nsresult rv
= NS_OK
;
1019 // return early preventing the event listener from being added
1020 // 'doc' is fetched above
1022 // Don't allow adding an event listener if the document is sandboxed
1023 // without 'allow-scripts'.
1024 if (doc
->HasScriptsBlockedBySandbox()) {
1025 return NS_ERROR_DOM_SECURITY_ERR
;
1028 // Perform CSP check
1029 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= doc
->GetCsp();
1030 uint32_t lineNum
= 0;
1031 JS::ColumnNumberOneOrigin columnNum
;
1033 JSContext
* cx
= nsContentUtils::GetCurrentJSContext();
1034 if (cx
&& !JS::DescribeScriptedCaller(cx
, nullptr, &lineNum
, &columnNum
)) {
1035 JS_ClearPendingException(cx
);
1039 bool allowsInlineScript
= true;
1040 rv
= csp
->GetAllowsInline(
1041 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
,
1042 true, // aHasUnsafeHash
1044 true, // aParserCreated (true because attribute event handler)
1046 nullptr, // nsICSPEventListener
1047 aBody
, lineNum
, columnNum
.oneOriginValue(), &allowsInlineScript
);
1048 NS_ENSURE_SUCCESS(rv
, rv
);
1050 // return early if CSP wants us to block inline scripts
1051 if (!allowsInlineScript
) {
1057 // This might be the first reference to this language in the global
1058 // We must init the language before we attempt to fetch its context.
1059 if (NS_FAILED(global
->EnsureScriptEnvironment())) {
1060 NS_WARNING("Failed to setup script environment for this language");
1061 // but fall through and let the inevitable failure below handle it.
1064 nsIScriptContext
* context
= global
->GetScriptContext();
1065 NS_ENSURE_TRUE(context
, NS_ERROR_FAILURE
);
1066 NS_ENSURE_STATE(global
->HasJSGlobal());
1068 removeEventHandler
.release();
1070 Listener
* listener
= SetEventHandlerInternal(aName
, TypedEventHandler(),
1071 aPermitUntrustedEvents
);
1073 if (!aDeferCompilation
) {
1074 return CompileEventHandlerInternal(listener
, aName
, &aBody
, aElement
);
1080 void EventListenerManager::RemoveEventHandler(nsAtom
* aName
) {
1081 if (mClearingListeners
) {
1085 Maybe
<size_t> entryIndex
= mListenerMap
.EntryIndexForType(aName
);
1090 ListenerArray
& listenerArray
= *mListenerMap
.mEntries
[*entryIndex
].mListeners
;
1092 Maybe
<uint32_t> listenerIndex
= [&]() -> Maybe
<uint32_t> {
1093 uint32_t count
= listenerArray
.Length();
1094 for (uint32_t i
= 0; i
< count
; ++i
) {
1095 Listener
* listener
= &listenerArray
.ElementAt(i
);
1096 if (listener
->mListenerIsHandler
) {
1103 if (!listenerIndex
) {
1107 listenerArray
.RemoveElementAt(*listenerIndex
);
1108 if (listenerArray
.IsEmpty()) {
1109 mListenerMap
.mEntries
.RemoveElementAt(*entryIndex
);
1112 RefPtr
<EventListenerManager
> kungFuDeathGrip(this);
1113 NotifyEventListenerRemoved(aName
);
1114 if (IsDeviceType(aName
)) {
1115 DisableDevice(aName
);
1119 nsresult
EventListenerManager::CompileEventHandlerInternal(
1120 Listener
* aListener
, nsAtom
* aTypeAtom
, const nsAString
* aBody
,
1121 Element
* aElement
) {
1122 MOZ_ASSERT(aListener
->GetJSEventHandler());
1123 MOZ_ASSERT(aListener
->mHandlerIsString
,
1124 "Why are we compiling a non-string JS listener?");
1125 JSEventHandler
* jsEventHandler
= aListener
->GetJSEventHandler();
1126 MOZ_ASSERT(!jsEventHandler
->GetTypedEventHandler().HasEventHandler(),
1127 "What is there to compile?");
1129 nsresult result
= NS_OK
;
1130 nsCOMPtr
<Document
> doc
;
1131 nsCOMPtr
<nsIScriptGlobalObject
> global
=
1132 GetScriptGlobalAndDocument(getter_AddRefs(doc
));
1133 NS_ENSURE_STATE(global
);
1135 // Activate JSAPI, and make sure that exceptions are reported on the right
1138 if (NS_WARN_IF(!jsapi
.Init(global
))) {
1139 return NS_ERROR_UNEXPECTED
;
1141 JSContext
* cx
= jsapi
.cx();
1143 nsAtom
* attrName
= aTypeAtom
;
1145 // Flag us as not a string so we don't keep trying to compile strings which
1146 // can't be compiled.
1147 aListener
->mHandlerIsString
= false;
1149 // mTarget may not be an Element if it's a window and we're
1150 // getting an inline event listener forwarded from <html:body> or
1151 // <html:frameset> or <xul:window> or the like.
1152 // XXX I don't like that we have to reference content from
1153 // here. The alternative is to store the event handler string on
1154 // the JSEventHandler itself, and that still doesn't address
1155 // the arg names issue.
1156 RefPtr
<Element
> element
= Element::FromEventTargetOrNull(mTarget
);
1157 MOZ_ASSERT(element
|| aBody
, "Where will we get our body?");
1158 nsAutoString handlerBody
;
1159 const nsAString
* body
= aBody
;
1161 if (aTypeAtom
== nsGkAtoms::onSVGLoad
) {
1162 attrName
= nsGkAtoms::onload
;
1163 } else if (aTypeAtom
== nsGkAtoms::onSVGScroll
) {
1164 attrName
= nsGkAtoms::onscroll
;
1165 } else if (aTypeAtom
== nsGkAtoms::onbeginEvent
) {
1166 attrName
= nsGkAtoms::onbegin
;
1167 } else if (aTypeAtom
== nsGkAtoms::onrepeatEvent
) {
1168 attrName
= nsGkAtoms::onrepeat
;
1169 } else if (aTypeAtom
== nsGkAtoms::onendEvent
) {
1170 attrName
= nsGkAtoms::onend
;
1171 } else if (aTypeAtom
== nsGkAtoms::onwebkitAnimationEnd
) {
1172 attrName
= nsGkAtoms::onwebkitanimationend
;
1173 } else if (aTypeAtom
== nsGkAtoms::onwebkitAnimationIteration
) {
1174 attrName
= nsGkAtoms::onwebkitanimationiteration
;
1175 } else if (aTypeAtom
== nsGkAtoms::onwebkitAnimationStart
) {
1176 attrName
= nsGkAtoms::onwebkitanimationstart
;
1177 } else if (aTypeAtom
== nsGkAtoms::onwebkitTransitionEnd
) {
1178 attrName
= nsGkAtoms::onwebkittransitionend
;
1181 element
->GetAttr(attrName
, handlerBody
);
1182 body
= &handlerBody
;
1185 aListener
= nullptr;
1187 nsAutoCString
url("-moz-evil:lying-event-listener"_ns
);
1189 MOZ_ASSERT(aElement
);
1190 nsIURI
* uri
= aElement
->OwnerDoc()->GetDocumentURI();
1195 nsCOMPtr
<nsPIDOMWindowInner
> win
=
1196 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget
);
1198 const char** argNames
;
1199 nsContentUtils::GetEventArgNames(aElement
->GetNameSpaceID(), aTypeAtom
, win
,
1200 &argCount
, &argNames
);
1202 // Wrap the event target, so that we can use it as the scope for the event
1203 // handler. Note that mTarget is different from aElement in the <body> case,
1204 // where mTarget is a Window.
1206 // The wrapScope doesn't really matter here, because the target will create
1207 // its reflector in the proper scope, and then we'll enter that realm.
1208 JS::Rooted
<JSObject
*> wrapScope(cx
, global
->GetGlobalJSObject());
1209 JS::Rooted
<JS::Value
> v(cx
);
1211 JSAutoRealm
ar(cx
, wrapScope
);
1212 nsresult rv
= nsContentUtils::WrapNative(cx
, mTarget
, &v
,
1213 /* aAllowWrapping = */ false);
1214 if (NS_WARN_IF(NS_FAILED(rv
))) {
1219 JS::Rooted
<JSObject
*> target(cx
, &v
.toObject());
1220 JSAutoRealm
ar(cx
, target
);
1222 // Now that we've entered the realm we actually care about, create our
1223 // scope chain. Note that we start with |element|, not aElement, because
1224 // mTarget is different from aElement in the <body> case, where mTarget is a
1225 // Window, and in that case we do not want the scope chain to include the body
1227 JS::RootedVector
<JSObject
*> scopeChain(cx
);
1228 if (!nsJSUtils::GetScopeChainForElement(cx
, element
, &scopeChain
)) {
1229 return NS_ERROR_OUT_OF_MEMORY
;
1232 nsDependentAtomString
str(attrName
);
1233 // Most of our names are short enough that we don't even have to malloc
1234 // the JS string stuff, so don't worry about playing games with
1235 // refcounting XPCOM stringbuffers.
1236 JS::Rooted
<JSString
*> jsStr(
1237 cx
, JS_NewUCStringCopyN(cx
, str
.BeginReading(), str
.Length()));
1238 NS_ENSURE_TRUE(jsStr
, NS_ERROR_OUT_OF_MEMORY
);
1240 // Get the reflector for |aElement|, so that we can pass to setElement.
1241 if (NS_WARN_IF(!GetOrCreateDOMReflector(cx
, aElement
, &v
))) {
1242 return NS_ERROR_FAILURE
;
1245 RefPtr
<JS::loader::ScriptFetchOptions
> fetchOptions
=
1246 new JS::loader::ScriptFetchOptions(
1247 CORS_NONE
, /* aNonce = */ u
""_ns
, RequestPriority::Auto
,
1248 JS::loader::ParserMetadata::NotParserInserted
,
1249 aElement
->OwnerDoc()->NodePrincipal());
1251 RefPtr
<JS::loader::EventScript
> eventScript
= new JS::loader::EventScript(
1252 aElement
->OwnerDoc()->GetReferrerPolicy(), fetchOptions
, uri
);
1254 JS::CompileOptions
options(cx
);
1255 // Use line 0 to make the function body starts from line 1.
1256 options
.setIntroductionType("eventHandler")
1257 .setFileAndLine(url
.get(), 0)
1258 .setDeferDebugMetadata(true);
1260 JS::Rooted
<JSObject
*> handler(cx
);
1261 result
= nsJSUtils::CompileFunction(jsapi
, scopeChain
, options
,
1262 nsAtomCString(aTypeAtom
), argCount
,
1263 argNames
, *body
, handler
.address());
1264 NS_ENSURE_SUCCESS(result
, result
);
1265 NS_ENSURE_TRUE(handler
, NS_ERROR_FAILURE
);
1267 JS::Rooted
<JS::Value
> privateValue(cx
, JS::PrivateValue(eventScript
));
1268 result
= nsJSUtils::UpdateFunctionDebugMetadata(jsapi
, handler
, options
,
1269 jsStr
, privateValue
);
1270 NS_ENSURE_SUCCESS(result
, result
);
1272 MOZ_ASSERT(js::IsObjectInContextCompartment(handler
, cx
));
1273 JS::Rooted
<JSObject
*> handlerGlobal(cx
, JS::CurrentGlobalOrNull(cx
));
1275 if (jsEventHandler
->EventName() == nsGkAtoms::onerror
&& win
) {
1276 RefPtr
<OnErrorEventHandlerNonNull
> handlerCallback
=
1277 new OnErrorEventHandlerNonNull(static_cast<JSContext
*>(nullptr),
1278 handler
, handlerGlobal
,
1279 /* aIncumbentGlobal = */ nullptr);
1280 jsEventHandler
->SetHandler(handlerCallback
);
1281 } else if (jsEventHandler
->EventName() == nsGkAtoms::onbeforeunload
&& win
) {
1282 RefPtr
<OnBeforeUnloadEventHandlerNonNull
> handlerCallback
=
1283 new OnBeforeUnloadEventHandlerNonNull(static_cast<JSContext
*>(nullptr),
1284 handler
, handlerGlobal
,
1285 /* aIncumbentGlobal = */ nullptr);
1286 jsEventHandler
->SetHandler(handlerCallback
);
1288 RefPtr
<EventHandlerNonNull
> handlerCallback
= new EventHandlerNonNull(
1289 static_cast<JSContext
*>(nullptr), handler
, handlerGlobal
,
1290 /* aIncumbentGlobal = */ nullptr);
1291 jsEventHandler
->SetHandler(handlerCallback
);
1297 bool EventListenerManager::HandleEventSingleListener(
1298 Listener
* aListener
, nsAtom
* aTypeAtom
, WidgetEvent
* aEvent
,
1299 Event
* aDOMEvent
, EventTarget
* aCurrentTarget
, bool aItemInShadowTree
) {
1300 if (!aEvent
->mCurrentTarget
) {
1301 aEvent
->mCurrentTarget
= aCurrentTarget
->GetTargetForDOMEvent();
1302 if (!aEvent
->mCurrentTarget
) {
1307 aEvent
->mFlags
.mInPassiveListener
= aListener
->mFlags
.mPassive
;
1309 nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
=
1310 WindowFromListener(aListener
, aTypeAtom
, aItemInShadowTree
);
1311 mozilla::dom::Event
* oldWindowEvent
= nullptr;
1313 oldWindowEvent
= innerWindow
->SetEvent(aDOMEvent
);
1316 nsresult result
= NS_OK
;
1319 EventListenerHolder
listenerHolder(aListener
->mListener
.Clone());
1321 // If this is a script handler and we haven't yet
1322 // compiled the event handler itself
1323 if ((aListener
->mListenerType
== Listener::eJSEventListener
) &&
1324 aListener
->mHandlerIsString
) {
1326 CompileEventHandlerInternal(aListener
, aTypeAtom
, nullptr, nullptr);
1327 aListener
= nullptr;
1330 if (NS_SUCCEEDED(result
)) {
1331 Maybe
<EventCallbackDebuggerNotificationGuard
> dbgGuard
;
1332 if (dom::ChromeUtils::IsDevToolsOpened()) {
1333 dbgGuard
.emplace(aCurrentTarget
, aDOMEvent
);
1337 // Event::currentTarget is set in EventDispatcher.
1338 if (listenerHolder
.HasWebIDLCallback()) {
1340 listenerHolder
.GetWebIDLCallback()->HandleEvent(aCurrentTarget
,
1342 result
= rv
.StealNSResult();
1344 // listenerHolder is holding a stack ref here.
1345 result
= MOZ_KnownLive(listenerHolder
.GetXPCOMCallback())
1346 ->HandleEvent(aDOMEvent
);
1351 Unused
<< innerWindow
->SetEvent(oldWindowEvent
);
1354 if (NS_FAILED(result
)) {
1355 aEvent
->mFlags
.mExceptionWasRaised
= true;
1357 aEvent
->mFlags
.mInPassiveListener
= false;
1358 return !aEvent
->mFlags
.mImmediatePropagationStopped
;
1361 /* static */ EventMessage
EventListenerManager::GetLegacyEventMessage(
1362 EventMessage aEventMessage
) {
1363 // webkit-prefixed legacy events:
1364 if (aEventMessage
== eTransitionEnd
) {
1365 return eWebkitTransitionEnd
;
1367 if (aEventMessage
== eAnimationStart
) {
1368 return eWebkitAnimationStart
;
1370 if (aEventMessage
== eAnimationEnd
) {
1371 return eWebkitAnimationEnd
;
1373 if (aEventMessage
== eAnimationIteration
) {
1374 return eWebkitAnimationIteration
;
1377 switch (aEventMessage
) {
1378 case eFullscreenChange
:
1379 return eMozFullscreenChange
;
1380 case eFullscreenError
:
1381 return eMozFullscreenError
;
1383 return aEventMessage
;
1387 EventMessage
EventListenerManager::GetEventMessage(nsAtom
* aEventName
) const {
1388 if (mIsMainThreadELM
) {
1389 return nsContentUtils::GetEventMessage(aEventName
);
1392 // The nsContentUtils event message hashtables aren't threadsafe, so just fall
1393 // back to eUnidentifiedEvent.
1394 return eUnidentifiedEvent
;
1397 EventMessage
EventListenerManager::GetEventMessageAndAtomForListener(
1398 const nsAString
& aType
, nsAtom
** aAtom
) {
1399 if (mIsMainThreadELM
) {
1400 return nsContentUtils::GetEventMessageAndAtomForListener(aType
, aAtom
);
1403 *aAtom
= NS_Atomize(u
"on"_ns
+ aType
).take();
1404 return eUnidentifiedEvent
;
1407 already_AddRefed
<nsPIDOMWindowInner
> EventListenerManager::WindowFromListener(
1408 Listener
* aListener
, nsAtom
* aTypeAtom
, bool aItemInShadowTree
) {
1409 nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
;
1410 if (!aItemInShadowTree
) {
1411 if (aListener
->mListener
.HasWebIDLCallback()) {
1412 CallbackObject
* callback
= aListener
->mListener
.GetWebIDLCallback();
1413 nsIGlobalObject
* global
= nullptr;
1415 global
= callback
->IncumbentGlobalOrNull();
1418 innerWindow
= global
->GetAsInnerWindow(); // Can be nullptr
1421 // This ensures `window.event` can be set properly for
1422 // nsWindowRoot to handle KeyPress event.
1423 if (aListener
&& aTypeAtom
== nsGkAtoms::onkeypress
&& mTarget
&&
1424 mTarget
->IsRootWindow()) {
1425 nsPIWindowRoot
* root
= mTarget
->AsWindowRoot();
1426 if (nsPIDOMWindowOuter
* outerWindow
= root
->GetWindow()) {
1427 innerWindow
= outerWindow
->GetCurrentInnerWindow();
1430 // Can't get the global from
1431 // listener->mListener.GetXPCOMCallback().
1432 // In most cases, it would be the same as for
1433 // the target, so let's do that.
1434 innerWindow
= GetInnerWindowForTarget(); // Can be nullptr
1438 return innerWindow
.forget();
1441 Maybe
<size_t> EventListenerManager::EventListenerMap::EntryIndexForType(
1442 nsAtom
* aTypeAtom
) const {
1443 MOZ_ASSERT(aTypeAtom
);
1445 size_t matchIndexOrInsertionPoint
= 0;
1446 bool foundMatch
= BinarySearchIf(mEntries
, 0, mEntries
.Length(),
1447 ListenerMapEntryComparator(aTypeAtom
),
1448 &matchIndexOrInsertionPoint
);
1449 return foundMatch
? Some(matchIndexOrInsertionPoint
) : Nothing();
1452 Maybe
<size_t> EventListenerManager::EventListenerMap::EntryIndexForAllEvents()
1454 // If we have an entry for "all events listeners", it'll be at the beginning
1455 // of the list and its type atom will be null.
1456 return !mEntries
.IsEmpty() && mEntries
[0].mTypeAtom
== nullptr ? Some(0)
1460 RefPtr
<EventListenerManager::ListenerArray
>
1461 EventListenerManager::EventListenerMap::GetListenersForType(
1462 nsAtom
* aTypeAtom
) const {
1463 Maybe
<size_t> index
= EntryIndexForType(aTypeAtom
);
1464 return index
? mEntries
[*index
].mListeners
: nullptr;
1467 RefPtr
<EventListenerManager::ListenerArray
>
1468 EventListenerManager::EventListenerMap::GetListenersForAllEvents() const {
1469 Maybe
<size_t> index
= EntryIndexForAllEvents();
1470 return index
? mEntries
[*index
].mListeners
: nullptr;
1473 RefPtr
<EventListenerManager::ListenerArray
>
1474 EventListenerManager::EventListenerMap::GetOrCreateListenersForType(
1475 nsAtom
* aTypeAtom
) {
1476 MOZ_ASSERT(aTypeAtom
);
1477 size_t matchIndexOrInsertionPoint
= 0;
1478 bool foundMatch
= BinarySearchIf(mEntries
, 0, mEntries
.Length(),
1479 ListenerMapEntryComparator(aTypeAtom
),
1480 &matchIndexOrInsertionPoint
);
1482 return mEntries
[matchIndexOrInsertionPoint
].mListeners
;
1484 RefPtr
<ListenerArray
> listeners
= MakeRefPtr
<ListenerArray
>();
1485 mEntries
.InsertElementAt(matchIndexOrInsertionPoint
,
1486 EventListenerMapEntry
{aTypeAtom
, listeners
});
1491 RefPtr
<EventListenerManager::ListenerArray
>
1492 EventListenerManager::EventListenerMap::GetOrCreateListenersForAllEvents() {
1493 RefPtr
<ListenerArray
> listeners
= GetListenersForAllEvents();
1495 listeners
= MakeRefPtr
<ListenerArray
>();
1496 mEntries
.InsertElementAt(0, EventListenerMapEntry
{nullptr, listeners
});
1501 void EventListenerManager::HandleEventInternal(nsPresContext
* aPresContext
,
1502 WidgetEvent
* aEvent
,
1504 EventTarget
* aCurrentTarget
,
1505 nsEventStatus
* aEventStatus
,
1506 bool aItemInShadowTree
) {
1507 MOZ_ASSERT_IF(aEvent
->mMessage
!= eUnidentifiedEvent
, mIsMainThreadELM
);
1509 // Set the value of the internal PreventDefault flag properly based on
1511 if (!aEvent
->DefaultPrevented() &&
1512 *aEventStatus
== nsEventStatus_eConsumeNoDefault
) {
1513 // Assume that if only aEventStatus claims that the event has already been
1514 // consumed, the consumer is default event handler.
1515 aEvent
->PreventDefault();
1518 if (aEvent
->mFlags
.mImmediatePropagationStopped
) {
1522 Maybe
<AutoHandlingUserInputStatePusher
> userInputStatePusher
;
1523 Maybe
<AutoPopupStatePusher
> popupStatePusher
;
1524 if (mIsMainThreadELM
) {
1525 userInputStatePusher
.emplace(UserActivation::IsUserInteractionEvent(aEvent
),
1527 popupStatePusher
.emplace(
1528 PopupBlocker::GetEventPopupControlState(aEvent
, *aDOMEvent
));
1531 EventMessage eventMessage
= aEvent
->mMessage
;
1532 RefPtr
<nsAtom
> typeAtom
=
1533 eventMessage
== eUnidentifiedEvent
1534 ? aEvent
->mSpecifiedEventType
.get()
1535 : nsContentUtils::GetEventTypeFromMessage(eventMessage
);
1537 // Some messages don't have a corresponding type atom, e.g.
1538 // eMouseEnterIntoWidget. These events can't have a listener, so we
1543 bool hasAnyListenerForEventType
= false;
1545 // First, notify any "all events" listeners.
1546 if (RefPtr
<ListenerArray
> listenersForAllEvents
=
1547 mListenerMap
.GetListenersForAllEvents()) {
1548 HandleEventWithListenerArray(listenersForAllEvents
, typeAtom
, eventMessage
,
1549 aPresContext
, aEvent
, aDOMEvent
,
1550 aCurrentTarget
, aItemInShadowTree
);
1551 hasAnyListenerForEventType
= true;
1554 // Now look for listeners for typeAtom, and call them if we have any.
1555 bool hasAnyListenerMatchingGroup
= false;
1556 if (RefPtr
<ListenerArray
> listeners
=
1557 mListenerMap
.GetListenersForType(typeAtom
)) {
1558 hasAnyListenerMatchingGroup
= HandleEventWithListenerArray(
1559 listeners
, typeAtom
, eventMessage
, aPresContext
, aEvent
, aDOMEvent
,
1560 aCurrentTarget
, aItemInShadowTree
);
1561 hasAnyListenerForEventType
= true;
1564 if (!hasAnyListenerMatchingGroup
&& aEvent
->IsTrusted()) {
1565 // If we didn't find any matching listeners, and our event has a legacy
1566 // version, check the listeners for the legacy version.
1567 EventMessage legacyEventMessage
= GetLegacyEventMessage(eventMessage
);
1568 if (legacyEventMessage
!= eventMessage
) {
1570 GetLegacyEventMessage(legacyEventMessage
) == legacyEventMessage
,
1571 "Legacy event messages should not themselves have legacy versions");
1572 RefPtr
<nsAtom
> legacyTypeAtom
=
1573 nsContentUtils::GetEventTypeFromMessage(legacyEventMessage
);
1574 if (RefPtr
<ListenerArray
> legacyListeners
=
1575 mListenerMap
.GetListenersForType(legacyTypeAtom
)) {
1576 HandleEventWithListenerArray(
1577 legacyListeners
, legacyTypeAtom
, legacyEventMessage
, aPresContext
,
1578 aEvent
, aDOMEvent
, aCurrentTarget
, aItemInShadowTree
);
1579 hasAnyListenerForEventType
= true;
1584 aEvent
->mCurrentTarget
= nullptr;
1586 if (mIsMainThreadELM
&& !hasAnyListenerForEventType
) {
1587 if (aEvent
->mMessage
!= eUnidentifiedEvent
) {
1588 mNoListenerForEvents
[2] = mNoListenerForEvents
[1];
1589 mNoListenerForEvents
[1] = mNoListenerForEvents
[0];
1590 mNoListenerForEvents
[0] = aEvent
->mMessage
;
1592 mNoListenerForEventAtom
= aEvent
->mSpecifiedEventType
;
1596 if (aEvent
->DefaultPrevented()) {
1597 *aEventStatus
= nsEventStatus_eConsumeNoDefault
;
1601 bool EventListenerManager::HandleEventWithListenerArray(
1602 ListenerArray
* aListeners
, nsAtom
* aTypeAtom
, EventMessage aEventMessage
,
1603 nsPresContext
* aPresContext
, WidgetEvent
* aEvent
, Event
** aDOMEvent
,
1604 EventTarget
* aCurrentTarget
, bool aItemInShadowTree
) {
1605 auto ensureDOMEvent
= [&]() {
1607 // Lazily create the DOM event.
1608 // This is tiny bit slow, but happens only once per event.
1609 // Similar code also in EventDispatcher.
1610 nsCOMPtr
<EventTarget
> et
= aEvent
->mOriginalTarget
;
1611 RefPtr
<Event
> event
=
1612 EventDispatcher::CreateEvent(et
, aPresContext
, aEvent
, u
""_ns
);
1613 event
.forget(aDOMEvent
);
1615 return *aDOMEvent
!= nullptr;
1618 Maybe
<EventMessageAutoOverride
> eventMessageAutoOverride
;
1619 bool isOverridingEventMessage
= aEvent
->mMessage
!= aEventMessage
;
1620 bool hasAnyListenerMatchingGroup
= false;
1621 bool didReplaceOnceListener
= false;
1623 for (Listener
& listenerRef
: aListeners
->EndLimitedRange()) {
1624 Listener
* listener
= &listenerRef
;
1625 if (listener
->mListenerType
== Listener::eNoListener
) {
1626 // The listener is a placeholder value of a removed "once" listener.
1629 if (!listener
->mEnabled
) {
1630 // The listener has been disabled, for example by devtools.
1633 if (!listener
->MatchesEventGroup(aEvent
)) {
1636 hasAnyListenerMatchingGroup
= true;
1638 // Check that the phase is same in event and event listener. Also check
1639 // that the event is trusted or that the listener allows untrusted events.
1640 if (!listener
->MatchesEventPhase(aEvent
) ||
1641 !listener
->AllowsEventTrustedness(aEvent
)) {
1645 Maybe
<Listener
> listenerHolder
;
1646 if (listener
->mFlags
.mOnce
) {
1647 // Move the listener to the stack before handling the event.
1648 // The order is important, otherwise the listener could be
1649 // called again inside the listener.
1650 listenerHolder
.emplace(std::move(*listener
));
1651 listener
= listenerHolder
.ptr();
1652 didReplaceOnceListener
= true;
1654 if (ensureDOMEvent()) {
1655 if (isOverridingEventMessage
&& !eventMessageAutoOverride
) {
1656 // Override the domEvent's event-message (its .type) until we
1657 // finish traversing listeners (when eventMessageAutoOverride
1659 eventMessageAutoOverride
.emplace(*aDOMEvent
, aEventMessage
);
1661 if (!HandleEventSingleListener(listener
, aTypeAtom
, aEvent
, *aDOMEvent
,
1662 aCurrentTarget
, aItemInShadowTree
)) {
1668 if (didReplaceOnceListener
) {
1669 // If there are any once listeners replaced with a placeholder during the
1670 // loop above, we need to clean up them here. Note that this could clear
1671 // once listeners handled in some outer level as well, but that should not
1672 // affect the result.
1673 size_t oldLength
= aListeners
->Length();
1674 aListeners
->NonObservingRemoveElementsBy([](const Listener
& aListener
) {
1675 return aListener
.mListenerType
== Listener::eNoListener
;
1677 size_t newLength
= aListeners
->Length();
1678 if (newLength
== 0) {
1679 // Remove the entry that has now become empty.
1680 mListenerMap
.mEntries
.RemoveElementsBy([](EventListenerMapEntry
& entry
) {
1681 return entry
.mListeners
->IsEmpty();
1684 if (newLength
< oldLength
) {
1685 // Call NotifyEventListenerRemoved once for every removed listener.
1686 size_t removedCount
= oldLength
- newLength
;
1687 for (size_t i
= 0; i
< removedCount
; i
++) {
1688 NotifyEventListenerRemoved(aTypeAtom
);
1690 if (IsDeviceType(aTypeAtom
)) {
1691 // Call DisableDevice once for every removed listener.
1692 for (size_t i
= 0; i
< removedCount
; i
++) {
1693 DisableDevice(aTypeAtom
);
1699 return hasAnyListenerMatchingGroup
;
1702 void EventListenerManager::Disconnect() {
1704 RemoveAllListenersSilently();
1707 void EventListenerManager::AddEventListener(const nsAString
& aType
,
1708 EventListenerHolder aListenerHolder
,
1710 bool aWantsUntrusted
) {
1711 EventListenerFlags flags
;
1712 flags
.mCapture
= aUseCapture
;
1713 flags
.mAllowUntrustedEvents
= aWantsUntrusted
;
1714 return AddEventListenerByType(std::move(aListenerHolder
), aType
, flags
);
1717 void EventListenerManager::AddEventListener(
1718 const nsAString
& aType
, EventListenerHolder aListenerHolder
,
1719 const dom::AddEventListenerOptionsOrBoolean
& aOptions
,
1720 bool aWantsUntrusted
) {
1721 EventListenerFlags flags
;
1722 Optional
<bool> passive
;
1723 AbortSignal
* signal
= nullptr;
1724 if (aOptions
.IsBoolean()) {
1725 flags
.mCapture
= aOptions
.GetAsBoolean();
1727 const auto& options
= aOptions
.GetAsAddEventListenerOptions();
1728 flags
.mCapture
= options
.mCapture
;
1729 flags
.mInSystemGroup
= options
.mMozSystemGroup
;
1730 flags
.mOnce
= options
.mOnce
;
1731 if (options
.mPassive
.WasPassed()) {
1732 passive
.Construct(options
.mPassive
.Value());
1735 if (options
.mSignal
.WasPassed()) {
1736 signal
= &options
.mSignal
.Value();
1740 flags
.mAllowUntrustedEvents
= aWantsUntrusted
;
1741 return AddEventListenerByType(std::move(aListenerHolder
), aType
, flags
,
1745 void EventListenerManager::RemoveEventListener(
1746 const nsAString
& aType
, EventListenerHolder aListenerHolder
,
1748 EventListenerFlags flags
;
1749 flags
.mCapture
= aUseCapture
;
1750 RemoveEventListenerByType(std::move(aListenerHolder
), aType
, flags
);
1753 void EventListenerManager::RemoveEventListener(
1754 const nsAString
& aType
, EventListenerHolder aListenerHolder
,
1755 const dom::EventListenerOptionsOrBoolean
& aOptions
) {
1756 EventListenerFlags flags
;
1757 if (aOptions
.IsBoolean()) {
1758 flags
.mCapture
= aOptions
.GetAsBoolean();
1760 const auto& options
= aOptions
.GetAsEventListenerOptions();
1761 flags
.mCapture
= options
.mCapture
;
1762 flags
.mInSystemGroup
= options
.mMozSystemGroup
;
1764 RemoveEventListenerByType(std::move(aListenerHolder
), aType
, flags
);
1767 void EventListenerManager::AddListenerForAllEvents(EventListener
* aDOMListener
,
1769 bool aWantsUntrusted
,
1770 bool aSystemEventGroup
) {
1771 EventListenerFlags flags
;
1772 flags
.mCapture
= aUseCapture
;
1773 flags
.mAllowUntrustedEvents
= aWantsUntrusted
;
1774 flags
.mInSystemGroup
= aSystemEventGroup
;
1775 AddEventListenerInternal(EventListenerHolder(aDOMListener
), eAllEvents
,
1776 nullptr, flags
, false, true);
1779 void EventListenerManager::RemoveListenerForAllEvents(
1780 EventListener
* aDOMListener
, bool aUseCapture
, bool aSystemEventGroup
) {
1781 EventListenerFlags flags
;
1782 flags
.mCapture
= aUseCapture
;
1783 flags
.mInSystemGroup
= aSystemEventGroup
;
1784 RemoveEventListenerInternal(EventListenerHolder(aDOMListener
), nullptr, flags
,
1788 bool EventListenerManager::HasMutationListeners() {
1789 if (mMayHaveMutationListeners
) {
1790 for (const auto& entry
: mListenerMap
.mEntries
) {
1791 EventMessage message
= GetEventMessage(entry
.mTypeAtom
);
1792 if (message
>= eLegacyMutationEventFirst
&&
1793 message
<= eLegacyMutationEventLast
) {
1802 uint32_t EventListenerManager::MutationListenerBits() {
1804 if (mMayHaveMutationListeners
) {
1805 for (const auto& entry
: mListenerMap
.mEntries
) {
1806 EventMessage message
= GetEventMessage(entry
.mTypeAtom
);
1807 if (message
>= eLegacyMutationEventFirst
&&
1808 message
<= eLegacyMutationEventLast
) {
1809 if (message
== eLegacySubtreeModified
) {
1810 return NS_EVENT_BITS_MUTATION_ALL
;
1812 bits
|= MutationBitForEventType(message
);
1819 bool EventListenerManager::HasListenersFor(const nsAString
& aEventName
) const {
1820 RefPtr
<nsAtom
> atom
= NS_Atomize(u
"on"_ns
+ aEventName
);
1821 return HasListenersFor(atom
);
1824 bool EventListenerManager::HasListenersFor(nsAtom
* aEventNameWithOn
) const {
1825 return HasListenersForInternal(aEventNameWithOn
, false);
1828 bool EventListenerManager::HasNonSystemGroupListenersFor(
1829 nsAtom
* aEventNameWithOn
) const {
1830 return HasListenersForInternal(aEventNameWithOn
, true);
1833 bool EventListenerManager::HasListenersForInternal(
1834 nsAtom
* aEventNameWithOn
, bool aIgnoreSystemGroup
) const {
1837 aEventNameWithOn
->ToString(name
);
1839 NS_ASSERTION(StringBeginsWith(name
, u
"on"_ns
),
1840 "Event name does not start with 'on'");
1841 RefPtr
<ListenerArray
> listeners
=
1842 mListenerMap
.GetListenersForType(aEventNameWithOn
);
1847 MOZ_ASSERT(!listeners
->IsEmpty());
1849 if (!aIgnoreSystemGroup
) {
1853 // Check if any non-system-group listeners exist in `listeners`.
1854 for (const auto& listener
: listeners
->NonObservingRange()) {
1855 if (!listener
.mFlags
.mInSystemGroup
) {
1863 bool EventListenerManager::HasListeners() const {
1864 return !mListenerMap
.IsEmpty();
1867 nsresult
EventListenerManager::GetListenerInfo(
1868 nsTArray
<RefPtr
<nsIEventListenerInfo
>>& aList
) {
1869 nsCOMPtr
<EventTarget
> target
= mTarget
;
1870 NS_ENSURE_STATE(target
);
1872 for (const auto& entry
: mListenerMap
.mEntries
) {
1873 for (const Listener
& listener
: entry
.mListeners
->ForwardRange()) {
1874 // If this is a script handler and we haven't yet
1875 // compiled the event handler itself go ahead and compile it
1876 if (listener
.mListenerType
== Listener::eJSEventListener
&&
1877 listener
.mHandlerIsString
) {
1878 CompileEventHandlerInternal(const_cast<Listener
*>(&listener
),
1879 entry
.mTypeAtom
, nullptr, nullptr);
1881 nsAutoString eventType
;
1882 if (listener
.mAllEvents
) {
1883 eventType
.SetIsVoid(true);
1884 } else if (listener
.mListenerType
== Listener::eNoListener
) {
1887 eventType
.Assign(Substring(nsDependentAtomString(entry
.mTypeAtom
), 2));
1890 JS::Rooted
<JSObject
*> callback(RootingCx());
1891 JS::Rooted
<JSObject
*> callbackGlobal(RootingCx());
1892 if (JSEventHandler
* handler
= listener
.GetJSEventHandler()) {
1893 if (handler
->GetTypedEventHandler().HasEventHandler()) {
1894 CallbackFunction
* callbackFun
= handler
->GetTypedEventHandler().Ptr();
1895 callback
= callbackFun
->CallableOrNull();
1896 callbackGlobal
= callbackFun
->CallbackGlobalOrNull();
1898 // This will be null for cross-compartment event listeners
1899 // which have been destroyed.
1903 } else if (listener
.mListenerType
== Listener::eWebIDLListener
) {
1904 EventListener
* listenerCallback
=
1905 listener
.mListener
.GetWebIDLCallback();
1906 callback
= listenerCallback
->CallbackOrNull();
1907 callbackGlobal
= listenerCallback
->CallbackGlobalOrNull();
1909 // This will be null for cross-compartment event listeners
1910 // which have been destroyed.
1915 RefPtr
<EventListenerInfo
> info
= new EventListenerInfo(
1916 this, eventType
, callback
, callbackGlobal
, listener
.mFlags
.mCapture
,
1917 listener
.mFlags
.mAllowUntrustedEvents
, listener
.mFlags
.mInSystemGroup
,
1918 listener
.mListenerIsHandler
);
1919 aList
.AppendElement(info
.forget());
1925 EventListenerManager::Listener
* EventListenerManager::GetListenerFor(
1926 nsAString
& aType
, JSObject
* aListener
, bool aCapturing
,
1927 bool aAllowsUntrusted
, bool aInSystemEventGroup
, bool aIsHandler
) {
1928 NS_ENSURE_TRUE(aListener
, nullptr);
1930 RefPtr
<ListenerArray
> listeners
= ([&]() -> RefPtr
<ListenerArray
> {
1931 if (aType
.IsVoid()) {
1932 return mListenerMap
.GetListenersForAllEvents();
1935 for (auto& mapEntry
: mListenerMap
.mEntries
) {
1936 if (RefPtr
<nsAtom
> typeAtom
= mapEntry
.mTypeAtom
) {
1937 if (Substring(nsDependentAtomString(typeAtom
), 2).Equals(aType
)) {
1938 return mapEntry
.mListeners
;
1950 for (Listener
& listener
: listeners
->ForwardRange()) {
1951 if (listener
.mListenerType
== Listener::eNoListener
) {
1955 if (listener
.mFlags
.mCapture
!= aCapturing
||
1956 listener
.mFlags
.mAllowUntrustedEvents
!= aAllowsUntrusted
||
1957 listener
.mFlags
.mInSystemGroup
!= aInSystemEventGroup
) {
1962 if (JSEventHandler
* handler
= listener
.GetJSEventHandler()) {
1963 if (handler
->GetTypedEventHandler().HasEventHandler()) {
1964 if (handler
->GetTypedEventHandler().Ptr()->CallableOrNull() ==
1970 } else if (listener
.mListenerType
== Listener::eWebIDLListener
&&
1971 listener
.mListener
.GetWebIDLCallback()->CallbackOrNull() ==
1979 nsresult
EventListenerManager::IsListenerEnabled(
1980 nsAString
& aType
, JSObject
* aListener
, bool aCapturing
,
1981 bool aAllowsUntrusted
, bool aInSystemEventGroup
, bool aIsHandler
,
1983 Listener
* listener
=
1984 GetListenerFor(aType
, aListener
, aCapturing
, aAllowsUntrusted
,
1985 aInSystemEventGroup
, aIsHandler
);
1986 NS_ENSURE_TRUE(listener
, NS_ERROR_NOT_AVAILABLE
);
1987 *aEnabled
= listener
->mEnabled
;
1991 nsresult
EventListenerManager::SetListenerEnabled(
1992 nsAString
& aType
, JSObject
* aListener
, bool aCapturing
,
1993 bool aAllowsUntrusted
, bool aInSystemEventGroup
, bool aIsHandler
,
1995 Listener
* listener
=
1996 GetListenerFor(aType
, aListener
, aCapturing
, aAllowsUntrusted
,
1997 aInSystemEventGroup
, aIsHandler
);
1998 NS_ENSURE_TRUE(listener
, NS_ERROR_NOT_AVAILABLE
);
1999 listener
->mEnabled
= aEnabled
;
2001 // We may have enabled some listener, clear the cache for which events
2002 // we don't have listeners.
2003 ClearNoListenersForEvents();
2004 mNoListenerForEventAtom
= nullptr;
2009 bool EventListenerManager::HasUnloadListeners() {
2010 return mListenerMap
.GetListenersForType(nsGkAtoms::onunload
) != nullptr;
2013 bool EventListenerManager::HasBeforeUnloadListeners() {
2014 return mListenerMap
.GetListenersForType(nsGkAtoms::onbeforeunload
) != nullptr;
2017 void EventListenerManager::SetEventHandler(nsAtom
* aEventName
,
2018 EventHandlerNonNull
* aHandler
) {
2020 RemoveEventHandler(aEventName
);
2024 // Untrusted events are always permitted for non-chrome script
2026 SetEventHandlerInternal(
2027 aEventName
, TypedEventHandler(aHandler
),
2028 !mIsMainThreadELM
|| !nsContentUtils::IsCallerChrome());
2031 void EventListenerManager::SetEventHandler(
2032 OnErrorEventHandlerNonNull
* aHandler
) {
2034 RemoveEventHandler(nsGkAtoms::onerror
);
2038 // Untrusted events are always permitted on workers and for non-chrome script
2039 // on the main thread.
2040 bool allowUntrusted
= !mIsMainThreadELM
|| !nsContentUtils::IsCallerChrome();
2042 SetEventHandlerInternal(nsGkAtoms::onerror
, TypedEventHandler(aHandler
),
2046 void EventListenerManager::SetEventHandler(
2047 OnBeforeUnloadEventHandlerNonNull
* aHandler
) {
2049 RemoveEventHandler(nsGkAtoms::onbeforeunload
);
2053 // Untrusted events are always permitted for non-chrome script
2055 SetEventHandlerInternal(
2056 nsGkAtoms::onbeforeunload
, TypedEventHandler(aHandler
),
2057 !mIsMainThreadELM
|| !nsContentUtils::IsCallerChrome());
2060 const TypedEventHandler
* EventListenerManager::GetTypedEventHandler(
2061 nsAtom
* aEventName
) {
2062 Listener
* listener
= FindEventHandler(aEventName
);
2068 JSEventHandler
* jsEventHandler
= listener
->GetJSEventHandler();
2070 if (listener
->mHandlerIsString
) {
2071 CompileEventHandlerInternal(listener
, aEventName
, nullptr, nullptr);
2074 const TypedEventHandler
& typedHandler
=
2075 jsEventHandler
->GetTypedEventHandler();
2076 return typedHandler
.HasEventHandler() ? &typedHandler
: nullptr;
2079 size_t EventListenerManager::SizeOfIncludingThis(
2080 MallocSizeOf aMallocSizeOf
) const {
2081 return aMallocSizeOf(this) + mListenerMap
.SizeOfExcludingThis(aMallocSizeOf
);
2084 size_t EventListenerManager::EventListenerMap::SizeOfExcludingThis(
2085 MallocSizeOf aMallocSizeOf
) const {
2086 size_t n
= mEntries
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
2087 for (const auto& entry
: mEntries
) {
2088 n
+= entry
.SizeOfExcludingThis(aMallocSizeOf
);
2093 size_t EventListenerManager::EventListenerMapEntry::SizeOfExcludingThis(
2094 MallocSizeOf aMallocSizeOf
) const {
2095 return mListeners
->SizeOfIncludingThis(aMallocSizeOf
);
2098 size_t EventListenerManager::ListenerArray::SizeOfIncludingThis(
2099 MallocSizeOf aMallocSizeOf
) const {
2100 size_t n
= aMallocSizeOf(this);
2101 n
+= ShallowSizeOfExcludingThis(aMallocSizeOf
);
2102 for (const auto& listener
: NonObservingRange()) {
2103 JSEventHandler
* jsEventHandler
= listener
.GetJSEventHandler();
2104 if (jsEventHandler
) {
2105 n
+= jsEventHandler
->SizeOfIncludingThis(aMallocSizeOf
);
2111 uint32_t EventListenerManager::ListenerCount() const {
2113 for (const auto& entry
: mListenerMap
.mEntries
) {
2114 count
+= entry
.mListeners
->Length();
2119 void EventListenerManager::MarkForCC() {
2120 for (const auto& entry
: mListenerMap
.mEntries
) {
2121 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2122 JSEventHandler
* jsEventHandler
= listener
.GetJSEventHandler();
2123 if (jsEventHandler
) {
2124 const TypedEventHandler
& typedHandler
=
2125 jsEventHandler
->GetTypedEventHandler();
2126 if (typedHandler
.HasEventHandler()) {
2127 typedHandler
.Ptr()->MarkForCC();
2129 } else if (listener
.mListenerType
== Listener::eWebIDLListener
) {
2130 listener
.mListener
.GetWebIDLCallback()->MarkForCC();
2134 if (mRefCnt
.IsPurple()) {
2135 mRefCnt
.RemovePurple();
2139 void EventListenerManager::TraceListeners(JSTracer
* aTrc
) {
2140 for (const auto& entry
: mListenerMap
.mEntries
) {
2141 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2142 JSEventHandler
* jsEventHandler
= listener
.GetJSEventHandler();
2143 if (jsEventHandler
) {
2144 const TypedEventHandler
& typedHandler
=
2145 jsEventHandler
->GetTypedEventHandler();
2146 if (typedHandler
.HasEventHandler()) {
2147 mozilla::TraceScriptHolder(typedHandler
.Ptr(), aTrc
);
2149 } else if (listener
.mListenerType
== Listener::eWebIDLListener
) {
2150 mozilla::TraceScriptHolder(listener
.mListener
.GetWebIDLCallback(),
2153 // We might have eWrappedJSListener, but that is the legacy type for
2154 // JS implemented event listeners, and trickier to handle here.
2159 bool EventListenerManager::HasNonSystemGroupListenersForUntrustedKeyEvents() {
2160 for (const auto& entry
: mListenerMap
.mEntries
) {
2161 if (entry
.mTypeAtom
!= nsGkAtoms::onkeydown
&&
2162 entry
.mTypeAtom
!= nsGkAtoms::onkeypress
&&
2163 entry
.mTypeAtom
!= nsGkAtoms::onkeyup
) {
2166 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2167 if (!listener
.mFlags
.mInSystemGroup
&&
2168 listener
.mFlags
.mAllowUntrustedEvents
) {
2176 bool EventListenerManager::
2177 HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents() {
2178 for (const auto& entry
: mListenerMap
.mEntries
) {
2179 if (entry
.mTypeAtom
!= nsGkAtoms::onkeydown
&&
2180 entry
.mTypeAtom
!= nsGkAtoms::onkeypress
&&
2181 entry
.mTypeAtom
!= nsGkAtoms::onkeyup
) {
2184 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2185 if (!listener
.mFlags
.mPassive
&& !listener
.mFlags
.mInSystemGroup
&&
2186 listener
.mFlags
.mAllowUntrustedEvents
) {
2194 bool EventListenerManager::HasApzAwareListeners() {
2195 if (!mIsMainThreadELM
) {
2199 for (const auto& entry
: mListenerMap
.mEntries
) {
2200 if (!IsApzAwareEvent(entry
.mTypeAtom
)) {
2203 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2204 if (!listener
.mFlags
.mPassive
) {
2212 static bool IsWheelEventType(nsAtom
* aEvent
) {
2213 if (aEvent
== nsGkAtoms::onwheel
|| aEvent
== nsGkAtoms::onDOMMouseScroll
||
2214 aEvent
== nsGkAtoms::onmousewheel
||
2215 aEvent
== nsGkAtoms::onMozMousePixelScroll
) {
2221 bool EventListenerManager::IsApzAwareEvent(nsAtom
* aEvent
) {
2222 if (IsWheelEventType(aEvent
)) {
2225 // In theory we should schedule a repaint if the touch event pref changes,
2226 // because the event regions might be out of date. In practice that seems like
2227 // overkill because users generally shouldn't be flipping this pref, much
2228 // less expecting touch listeners on the page to immediately start preventing
2229 // scrolling without so much as a repaint. Tests that we write can work
2230 // around this constraint easily enough.
2231 if (aEvent
== nsGkAtoms::ontouchstart
|| aEvent
== nsGkAtoms::ontouchmove
) {
2232 return TouchEvent::PrefEnabled(
2233 nsContentUtils::GetDocShellForEventTarget(mTarget
));
2238 bool EventListenerManager::HasNonPassiveWheelListener() {
2239 MOZ_ASSERT(NS_IsMainThread());
2240 for (const auto& entry
: mListenerMap
.mEntries
) {
2241 if (!IsWheelEventType(entry
.mTypeAtom
)) {
2244 for (const auto& listener
: entry
.mListeners
->NonObservingRange()) {
2245 if (!listener
.mFlags
.mPassive
) {
2253 void EventListenerManager::RemoveAllListeners() {
2254 while (!mListenerMap
.IsEmpty()) {
2255 size_t entryIndex
= mListenerMap
.mEntries
.Length() - 1;
2256 EventListenerMapEntry
& entry
= mListenerMap
.mEntries
[entryIndex
];
2257 RefPtr
<nsAtom
> type
= entry
.mTypeAtom
;
2258 MOZ_ASSERT(!entry
.mListeners
->IsEmpty());
2259 size_t idx
= entry
.mListeners
->Length() - 1;
2260 entry
.mListeners
->RemoveElementAt(idx
);
2261 if (entry
.mListeners
->IsEmpty()) {
2262 mListenerMap
.mEntries
.RemoveElementAt(entryIndex
);
2264 NotifyEventListenerRemoved(type
);
2265 if (IsDeviceType(type
)) {
2266 DisableDevice(type
);
2271 already_AddRefed
<nsIScriptGlobalObject
>
2272 EventListenerManager::GetScriptGlobalAndDocument(Document
** aDoc
) {
2273 nsCOMPtr
<Document
> doc
;
2274 nsCOMPtr
<nsPIDOMWindowInner
> win
;
2275 if (nsINode
* node
= nsINode::FromEventTargetOrNull(mTarget
)) {
2276 // Try to get context from doc
2277 doc
= node
->OwnerDoc();
2278 if (doc
->IsLoadedAsData()) {
2282 win
= do_QueryInterface(doc
->GetScopeObject());
2283 } else if ((win
= GetTargetAsInnerWindow())) {
2284 doc
= win
->GetExtantDoc();
2287 if (!win
|| !win
->IsCurrentInnerWindow()) {
2292 nsCOMPtr
<nsIScriptGlobalObject
> global
= do_QueryInterface(win
);
2293 return global
.forget();
2296 EventListenerManager::ListenerSignalFollower::ListenerSignalFollower(
2297 EventListenerManager
* aListenerManager
,
2298 EventListenerManager::Listener
* aListener
, nsAtom
* aTypeAtom
)
2299 : dom::AbortFollower(),
2300 mListenerManager(aListenerManager
),
2301 mListener(aListener
->mListener
.Clone()),
2302 mTypeAtom(aTypeAtom
),
2303 mAllEvents(aListener
->mAllEvents
),
2304 mFlags(aListener
->mFlags
){};
2306 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower
)
2308 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower
)
2309 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower
)
2311 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
2312 EventListenerManager::ListenerSignalFollower
)
2313 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener
)
2314 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2316 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
2317 EventListenerManager::ListenerSignalFollower
)
2318 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener
)
2319 tmp
->mListenerManager
= nullptr;
2320 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2322 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
2323 EventListenerManager::ListenerSignalFollower
)
2324 NS_INTERFACE_MAP_ENTRY(nsISupports
)
2325 NS_INTERFACE_MAP_END
2327 void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() {
2328 if (mListenerManager
) {
2329 RefPtr
<EventListenerManager
> elm
= mListenerManager
;
2330 mListenerManager
= nullptr;
2331 elm
->RemoveEventListenerInternal(std::move(mListener
), mTypeAtom
, mFlags
,
2336 } // namespace mozilla