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