Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / events / EventListenerManager.cpp
blob72d4eb1b30764ea3a941d5d5c259a46021a2eab6
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::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"
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 uint32_t MutationBitForEventType(EventMessage aEventType) {
70 switch (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;
85 default:
86 break;
88 return 0;
91 class ListenerMapEntryComparator {
92 public:
93 explicit ListenerMapEntryComparator(nsAtom* aTarget)
94 : mAddressOfEventType(reinterpret_cast<uintptr_t>(aTarget)) {}
96 int operator()(
97 const EventListenerManager::EventListenerMapEntry& aEntry) const {
98 uintptr_t value = reinterpret_cast<uintptr_t>(aEntry.mTypeAtom.get());
99 if (mAddressOfEventType == value) {
100 return 0;
103 if (mAddressOfEventType < value) {
104 return -1;
107 return 1;
110 private:
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)
137 : mTarget(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) {
162 return;
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())) {
174 nsAutoCString name;
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));
182 } else {
183 name.Replace(entryPrefixLen, name.Length() - entryPrefixLen,
184 "(all)"_ns);
186 ImplCycleCollectionTraverse(aCallback, *entry.mListeners, name.get());
188 } else {
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,
199 unsigned aFlags) {
200 if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
201 nsAutoCString name;
202 name.AppendASCII(aName);
203 name.AppendLiteral(" listenerType=");
204 name.AppendInt(aField.mListenerType);
205 name.AppendLiteral(" ");
206 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(),
207 name.get(), aFlags);
208 } else {
209 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName,
210 aFlags);
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)
224 tmp->Disconnect();
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();
235 return window;
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
250 "Missing type");
251 MOZ_ASSERT_IF(
252 aEventMessage != eUnidentifiedEvent && !aAllEvents,
253 aTypeAtom == nsContentUtils::GetEventTypeFromMessage(aEventMessage));
255 if (!aListenerHolder || mClearingListeners) {
256 return;
259 if (aSignal && aSignal->Aborted()) {
260 return;
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
265 // to the listener.
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) {
276 return;
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;
299 } else {
300 listener->mListenerType = Listener::eNativeListener;
302 listener->mListener = std::move(aListenerHolder);
304 if (aSignal) {
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) {
337 case eAfterPaint:
338 mMayHavePaintEventListener = true;
339 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
340 window->SetHasPaintEventListeners();
342 break;
343 case eLegacyDOMActivate:
344 mMayHaveDOMActivateEventListener = true;
345 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
346 window->SetHasDOMActivateEventListeners();
348 break;
349 case eLegacySubtreeModified:
350 case eLegacyNodeInserted:
351 case eLegacyNodeRemoved:
352 case eLegacyNodeRemovedFromDocument:
353 case eLegacyNodeInsertedIntoDocument:
354 case eLegacyAttrModified:
355 case eLegacyCharacterDataModified:
356 #ifdef DEBUG
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 "
364 "documents");
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
380 // on this.
381 window->SetMutationListeners(
382 (resolvedEventMessage == eLegacySubtreeModified)
383 ? NS_EVENT_BITS_MUTATION_ALL
384 : MutationBitForEventType(resolvedEventMessage));
386 break;
387 case ePointerEnter:
388 case ePointerLeave:
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();
397 break;
398 case eGamepadButtonDown:
399 case eGamepadButtonUp:
400 case eGamepadAxisMove:
401 case eGamepadConnected:
402 case eGamepadDisconnected:
403 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
404 window->SetHasGamepadEventListener();
406 break;
407 case eDeviceOrientation:
408 case eDeviceOrientationAbsolute:
409 case eUserProximity:
410 case eDeviceLight:
411 case eDeviceMotion:
412 #if defined(MOZ_WIDGET_ANDROID)
413 case eOrientationChange:
414 #endif // #if defined(MOZ_WIDGET_ANDROID)
415 EnableDevice(aTypeAtom);
416 break;
417 case eTouchStart:
418 case eTouchEnd:
419 case eTouchMove:
420 case eTouchCancel:
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();
429 break;
430 case eMouseEnter:
431 case eMouseLeave:
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();
440 break;
441 case eEditorBeforeInput:
442 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
443 window->SetHasBeforeInputEventListenersForTelemetry();
445 break;
446 case eSelectionChange:
447 mMayHaveSelectionChangeEventListener = true;
448 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
449 window->SetHasSelectionChangeEventListeners();
451 break;
452 case eFormSelect:
453 mMayHaveFormSelectEventListener = true;
454 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
455 window->SetHasFormSelectEventListeners();
457 break;
458 case eScrollPortOverflow:
459 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
460 if (Document* doc = window->GetExtantDoc()) {
461 doc->SetUseCounter(eUseCounter_custom_onoverflow);
464 break;
465 case eScrollPortUnderflow:
466 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
467 if (Document* doc = window->GetExtantDoc()) {
468 doc->SetUseCounter(eUseCounter_custom_onunderflow);
471 break;
472 case eLegacyMouseLineOrPageScroll:
473 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
474 if (Document* doc = window->GetExtantDoc()) {
475 doc->SetUseCounter(eUseCounter_custom_ondommousescroll);
478 break;
479 case eLegacyMousePixelScroll:
480 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
481 if (Document* doc = window->GetExtantDoc()) {
482 doc->SetUseCounter(eUseCounter_custom_onmozmousepixelscroll);
485 break;
486 case eTransitionStart:
487 case eTransitionRun:
488 case eTransitionEnd:
489 case eTransitionCancel:
490 case eWebkitTransitionEnd:
491 mMayHaveTransitionEventListener = true;
492 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
493 window->SetHasTransitionEventListeners();
495 break;
496 case eFormCheckboxStateChange:
497 nsContentUtils::SetMayHaveFormCheckboxStateChangeListeners();
498 break;
499 case eFormRadioStateChange:
500 nsContentUtils::SetMayHaveFormRadioStateChangeListeners();
501 break;
502 default:
503 // XXX Use NS_ASSERTION here to print resolvedEventMessage since
504 // MOZ_ASSERT can take only string literal, not pointer to
505 // characters.
506 NS_ASSERTION(
507 resolvedEventMessage < eLegacyMutationEventFirst ||
508 resolvedEventMessage > eLegacyMutationEventLast,
509 nsPrintfCString("You added new mutation event, but it's not "
510 "handled above, resolvedEventMessage=%s",
511 ToChar(resolvedEventMessage))
512 .get());
513 NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerenter,
514 nsPrintfCString("resolvedEventMessage=%s",
515 ToChar(resolvedEventMessage))
516 .get());
517 NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerleave,
518 nsPrintfCString("resolvedEventMessage=%s",
519 ToChar(resolvedEventMessage))
520 .get());
521 NS_ASSERTION(
522 resolvedEventMessage < eGamepadEventFirst ||
523 resolvedEventMessage > eGamepadEventLast,
524 nsPrintfCString("You added new gamepad event, but it's not "
525 "handled above, resolvedEventMessage=%s",
526 ToChar(resolvedEventMessage))
527 .get());
528 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientation,
529 nsPrintfCString("resolvedEventMessage=%s",
530 ToChar(resolvedEventMessage))
531 .get());
532 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientationabsolute,
533 nsPrintfCString("resolvedEventMessage=%s",
534 ToChar(resolvedEventMessage))
535 .get());
536 NS_ASSERTION(aTypeAtom != nsGkAtoms::onuserproximity,
537 nsPrintfCString("resolvedEventMessage=%s",
538 ToChar(resolvedEventMessage))
539 .get());
540 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicelight,
541 nsPrintfCString("resolvedEventMessage=%s",
542 ToChar(resolvedEventMessage))
543 .get());
544 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicemotion,
545 nsPrintfCString("resolvedEventMessage=%s",
546 ToChar(resolvedEventMessage))
547 .get());
548 #if defined(MOZ_WIDGET_ANDROID)
549 NS_ASSERTION(aTypeAtom != nsGkAtoms::onorientationchange,
550 nsPrintfCString("resolvedEventMessage=%s",
551 ToChar(resolvedEventMessage))
552 .get());
553 #endif // #if defined(MOZ_WIDGET_ANDROID)
554 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchstart,
555 nsPrintfCString("resolvedEventMessage=%s",
556 ToChar(resolvedEventMessage))
557 .get());
558 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchend,
559 nsPrintfCString("resolvedEventMessage=%s",
560 ToChar(resolvedEventMessage))
561 .get());
562 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchmove,
563 nsPrintfCString("resolvedEventMessage=%s",
564 ToChar(resolvedEventMessage))
565 .get());
566 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchcancel,
567 nsPrintfCString("resolvedEventMessage=%s",
568 ToChar(resolvedEventMessage))
569 .get());
570 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseenter,
571 nsPrintfCString("resolvedEventMessage=%s",
572 ToChar(resolvedEventMessage))
573 .get());
574 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseleave,
575 nsPrintfCString("resolvedEventMessage=%s",
576 ToChar(resolvedEventMessage))
577 .get());
578 NS_ASSERTION(aTypeAtom != nsGkAtoms::onbeforeinput,
579 nsPrintfCString("resolvedEventMessage=%s",
580 ToChar(resolvedEventMessage))
581 .get());
582 NS_ASSERTION(aTypeAtom != nsGkAtoms::onselectionchange,
583 nsPrintfCString("resolvedEventMessage=%s",
584 ToChar(resolvedEventMessage))
585 .get());
586 NS_ASSERTION(aTypeAtom != nsGkAtoms::onselect,
587 nsPrintfCString("resolvedEventMessage=%s",
588 ToChar(resolvedEventMessage))
589 .get());
590 NS_ASSERTION(aTypeAtom != nsGkAtoms::onoverflow,
591 nsPrintfCString("resolvedEventMessage=%s",
592 ToChar(resolvedEventMessage))
593 .get());
594 NS_ASSERTION(aTypeAtom != nsGkAtoms::onunderflow,
595 nsPrintfCString("resolvedEventMessage=%s",
596 ToChar(resolvedEventMessage))
597 .get());
598 NS_ASSERTION(aTypeAtom != nsGkAtoms::onDOMMouseScroll,
599 nsPrintfCString("resolvedEventMessage=%s",
600 ToChar(resolvedEventMessage))
601 .get());
602 NS_ASSERTION(aTypeAtom != nsGkAtoms::onMozMousePixelScroll,
603 nsPrintfCString("resolvedEventMessage=%s",
604 ToChar(resolvedEventMessage))
605 .get());
606 break;
610 if (mIsMainThreadELM && !aFlags.mPassive && IsApzAwareEvent(aTypeAtom)) {
611 ProcessApzAwareEventListenerAdd();
614 if (mTarget) {
615 mTarget->EventListenerAdded(aTypeAtom);
618 if (mIsMainThreadELM && mTarget) {
619 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
620 aTypeAtom);
624 void EventListenerManager::ProcessApzAwareEventListenerAdd() {
625 Document* doc = nullptr;
627 // Mark the node as having apz aware listeners
628 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
629 node->SetMayBeApzAware();
630 doc = node->OwnerDoc();
633 // Schedule a paint so event regions on the layer tree gets updated
634 if (!doc) {
635 if (nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow()) {
636 doc = window->GetExtantDoc();
639 if (!doc) {
640 if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(mTarget)) {
641 if (nsPIDOMWindowInner* window = helper->GetOwner()) {
642 doc = window->GetExtantDoc();
647 if (doc && gfxPlatform::AsyncPanZoomEnabled()) {
648 PresShell* presShell = doc->GetPresShell();
649 if (presShell) {
650 nsIFrame* f = presShell->GetRootFrame();
651 if (f) {
652 f->SchedulePaint();
658 bool EventListenerManager::IsDeviceType(nsAtom* aTypeAtom) {
659 return aTypeAtom == nsGkAtoms::ondeviceorientation ||
660 aTypeAtom == nsGkAtoms::ondeviceorientationabsolute ||
661 aTypeAtom == nsGkAtoms::ondevicemotion ||
662 aTypeAtom == nsGkAtoms::ondevicelight
663 #if defined(MOZ_WIDGET_ANDROID)
664 || aTypeAtom == nsGkAtoms::onorientationchange
665 #endif
666 || aTypeAtom == nsGkAtoms::onuserproximity;
669 void EventListenerManager::EnableDevice(nsAtom* aTypeAtom) {
670 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
671 if (!window) {
672 return;
675 if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
676 #ifdef MOZ_WIDGET_ANDROID
677 // Falls back to SENSOR_ROTATION_VECTOR and SENSOR_ORIENTATION if
678 // unavailable on device.
679 window->EnableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
680 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
681 #else
682 window->EnableDeviceSensor(SENSOR_ORIENTATION);
683 #endif
684 return;
687 if (aTypeAtom == nsGkAtoms::ondeviceorientationabsolute) {
688 #ifdef MOZ_WIDGET_ANDROID
689 // Falls back to SENSOR_ORIENTATION if unavailable on device.
690 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
691 #else
692 window->EnableDeviceSensor(SENSOR_ORIENTATION);
693 #endif
694 return;
697 if (aTypeAtom == nsGkAtoms::onuserproximity) {
698 window->EnableDeviceSensor(SENSOR_PROXIMITY);
699 return;
702 if (aTypeAtom == nsGkAtoms::ondevicelight) {
703 window->EnableDeviceSensor(SENSOR_LIGHT);
704 return;
707 if (aTypeAtom == nsGkAtoms::ondevicemotion) {
708 window->EnableDeviceSensor(SENSOR_ACCELERATION);
709 window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
710 window->EnableDeviceSensor(SENSOR_GYROSCOPE);
711 return;
714 #if defined(MOZ_WIDGET_ANDROID)
715 if (aTypeAtom == nsGkAtoms::onorientationchange) {
716 window->EnableOrientationChangeListener();
717 return;
719 #endif
721 NS_WARNING("Enabling an unknown device sensor.");
724 void EventListenerManager::DisableDevice(nsAtom* aTypeAtom) {
725 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
726 if (!window) {
727 return;
730 if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
731 #ifdef MOZ_WIDGET_ANDROID
732 // Disable all potential fallback sensors.
733 window->DisableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
734 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
735 #endif
736 window->DisableDeviceSensor(SENSOR_ORIENTATION);
737 return;
740 if (aTypeAtom == nsGkAtoms::ondeviceorientationabsolute) {
741 #ifdef MOZ_WIDGET_ANDROID
742 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
743 #endif
744 window->DisableDeviceSensor(SENSOR_ORIENTATION);
745 return;
748 if (aTypeAtom == nsGkAtoms::ondevicemotion) {
749 window->DisableDeviceSensor(SENSOR_ACCELERATION);
750 window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
751 window->DisableDeviceSensor(SENSOR_GYROSCOPE);
752 return;
755 if (aTypeAtom == nsGkAtoms::onuserproximity) {
756 window->DisableDeviceSensor(SENSOR_PROXIMITY);
757 return;
760 if (aTypeAtom == nsGkAtoms::ondevicelight) {
761 window->DisableDeviceSensor(SENSOR_LIGHT);
762 return;
765 #if defined(MOZ_WIDGET_ANDROID)
766 if (aTypeAtom == nsGkAtoms::onorientationchange) {
767 window->DisableOrientationChangeListener();
768 return;
770 #endif
772 NS_WARNING("Disabling an unknown device sensor.");
775 void EventListenerManager::NotifyEventListenerRemoved(nsAtom* aUserType) {
776 // If the following code is changed, other callsites of EventListenerRemoved
777 // and NotifyAboutMainThreadListenerChange should be changed too.
778 ClearNoListenersForEvents();
779 mNoListenerForEventAtom = nullptr;
780 if (mTarget) {
781 mTarget->EventListenerRemoved(aUserType);
783 if (mIsMainThreadELM && mTarget) {
784 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
785 aUserType);
789 void EventListenerManager::RemoveEventListenerInternal(
790 EventListenerHolder aListenerHolder, nsAtom* aUserType,
791 const EventListenerFlags& aFlags, bool aAllEvents) {
792 if (!aListenerHolder || (!aUserType && !aAllEvents) || mClearingListeners) {
793 return;
796 Maybe<size_t> entryIndex = aAllEvents
797 ? mListenerMap.EntryIndexForAllEvents()
798 : mListenerMap.EntryIndexForType(aUserType);
799 if (!entryIndex) {
800 return;
803 ListenerArray& listenerArray = *mListenerMap.mEntries[*entryIndex].mListeners;
805 Maybe<uint32_t> listenerIndex = [&]() -> Maybe<uint32_t> {
806 uint32_t count = listenerArray.Length();
807 for (uint32_t i = 0; i < count; ++i) {
808 Listener* listener = &listenerArray.ElementAt(i);
809 if (listener->mListener == aListenerHolder &&
810 listener->mFlags.EqualsForRemoval(aFlags)) {
811 return Some(i);
814 return Nothing();
815 }();
817 if (!listenerIndex) {
818 return;
821 listenerArray.RemoveElementAt(*listenerIndex);
822 if (listenerArray.IsEmpty()) {
823 mListenerMap.mEntries.RemoveElementAt(*entryIndex);
826 RefPtr<EventListenerManager> kungFuDeathGrip(this);
827 if (!aAllEvents) {
828 NotifyEventListenerRemoved(aUserType);
829 if (IsDeviceType(aUserType)) {
830 DisableDevice(aUserType);
835 static bool IsDefaultPassiveWhenOnRoot(EventMessage aMessage) {
836 if (aMessage == eTouchStart || aMessage == eTouchMove || aMessage == eWheel ||
837 aMessage == eLegacyMouseLineOrPageScroll ||
838 aMessage == eLegacyMousePixelScroll) {
839 return true;
841 return false;
844 static bool IsRootEventTarget(EventTarget* aTarget) {
845 if (!aTarget) {
846 return false;
848 if (aTarget->IsInnerWindow()) {
849 return true;
851 const nsINode* node = nsINode::FromEventTarget(aTarget);
852 if (!node) {
853 return false;
855 Document* doc = node->OwnerDoc();
856 return node == doc || node == doc->GetRootElement() || node == doc->GetBody();
859 void EventListenerManager::MaybeMarkPassive(EventMessage aMessage,
860 EventListenerFlags& aFlags) {
861 if (!mIsMainThreadELM) {
862 return;
864 if (!IsDefaultPassiveWhenOnRoot(aMessage)) {
865 return;
867 if (!IsRootEventTarget(mTarget)) {
868 return;
870 aFlags.mPassive = true;
873 void EventListenerManager::AddEventListenerByType(
874 EventListenerHolder aListenerHolder, const nsAString& aType,
875 const EventListenerFlags& aFlags, const Optional<bool>& aPassive,
876 AbortSignal* aSignal) {
877 RefPtr<nsAtom> atom;
878 EventMessage message =
879 GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
881 EventListenerFlags flags = aFlags;
882 if (aPassive.WasPassed()) {
883 flags.mPassive = aPassive.Value();
884 } else {
885 MaybeMarkPassive(message, flags);
888 AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags,
889 false, false, aSignal);
892 void EventListenerManager::RemoveEventListenerByType(
893 EventListenerHolder aListenerHolder, const nsAString& aType,
894 const EventListenerFlags& aFlags) {
895 RefPtr<nsAtom> atom;
896 (void)GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
897 RemoveEventListenerInternal(std::move(aListenerHolder), atom, aFlags);
900 EventListenerManager::Listener* EventListenerManager::FindEventHandler(
901 nsAtom* aTypeAtom) {
902 // Run through the listeners for this type and see if a script
903 // listener is registered
904 RefPtr<ListenerArray> listeners = mListenerMap.GetListenersForType(aTypeAtom);
905 if (!listeners) {
906 return nullptr;
909 uint32_t count = listeners->Length();
910 for (uint32_t i = 0; i < count; ++i) {
911 Listener* listener = &listeners->ElementAt(i);
912 if (listener->mListenerIsHandler) {
913 return listener;
916 return nullptr;
919 EventListenerManager::Listener* EventListenerManager::SetEventHandlerInternal(
920 nsAtom* aName, const TypedEventHandler& aTypedHandler,
921 bool aPermitUntrustedEvents) {
922 MOZ_ASSERT(aName);
924 EventMessage eventMessage = GetEventMessage(aName);
925 Listener* listener = FindEventHandler(aName);
927 if (!listener) {
928 // If we didn't find a script listener or no listeners existed
929 // create and add a new one.
930 EventListenerFlags flags;
931 flags.mListenerIsJSListener = true;
932 MaybeMarkPassive(eventMessage, flags);
934 nsCOMPtr<JSEventHandler> jsEventHandler;
935 NS_NewJSEventHandler(mTarget, aName, aTypedHandler,
936 getter_AddRefs(jsEventHandler));
937 AddEventListenerInternal(EventListenerHolder(jsEventHandler), eventMessage,
938 aName, flags, true);
940 listener = FindEventHandler(aName);
941 } else {
942 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
943 MOZ_ASSERT(jsEventHandler,
944 "How can we have an event handler with no JSEventHandler?");
946 bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler;
947 // Possibly the same listener, but update still the context and scope.
948 jsEventHandler->SetHandler(aTypedHandler);
949 if (mTarget && !same) {
950 mTarget->EventListenerRemoved(aName);
951 mTarget->EventListenerAdded(aName);
953 if (mIsMainThreadELM && mTarget) {
954 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, aName);
958 // Set flag to indicate possible need for compilation later
959 listener->mHandlerIsString = !aTypedHandler.HasEventHandler();
960 if (aPermitUntrustedEvents) {
961 listener->mFlags.mAllowUntrustedEvents = true;
962 mMayHaveListenersForUntrustedEvents = true;
965 return listener;
968 nsresult EventListenerManager::SetEventHandler(nsAtom* aName,
969 const nsAString& aBody,
970 bool aDeferCompilation,
971 bool aPermitUntrustedEvents,
972 Element* aElement) {
973 auto removeEventHandler = MakeScopeExit([&] { RemoveEventHandler(aName); });
975 nsCOMPtr<Document> doc;
976 nsCOMPtr<nsIScriptGlobalObject> global =
977 GetScriptGlobalAndDocument(getter_AddRefs(doc));
979 if (!global) {
980 // This can happen; for example this document might have been
981 // loaded as data.
982 return NS_OK;
985 nsresult rv = NS_OK;
986 // return early preventing the event listener from being added
987 // 'doc' is fetched above
988 if (doc) {
989 // Don't allow adding an event listener if the document is sandboxed
990 // without 'allow-scripts'.
991 if (doc->HasScriptsBlockedBySandbox()) {
992 return NS_ERROR_DOM_SECURITY_ERR;
995 // Perform CSP check
996 nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
997 uint32_t lineNum = 0;
998 JS::ColumnNumberOneOrigin columnNum;
1000 JSContext* cx = nsContentUtils::GetCurrentJSContext();
1001 if (cx && !JS::DescribeScriptedCaller(cx, nullptr, &lineNum, &columnNum)) {
1002 JS_ClearPendingException(cx);
1005 if (csp) {
1006 bool allowsInlineScript = true;
1007 rv = csp->GetAllowsInline(
1008 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE,
1009 true, // aHasUnsafeHash
1010 u""_ns, // aNonce
1011 true, // aParserCreated (true because attribute event handler)
1012 aElement,
1013 nullptr, // nsICSPEventListener
1014 aBody, lineNum, columnNum.oneOriginValue(), &allowsInlineScript);
1015 NS_ENSURE_SUCCESS(rv, rv);
1017 // return early if CSP wants us to block inline scripts
1018 if (!allowsInlineScript) {
1019 return NS_OK;
1024 // This might be the first reference to this language in the global
1025 // We must init the language before we attempt to fetch its context.
1026 if (NS_FAILED(global->EnsureScriptEnvironment())) {
1027 NS_WARNING("Failed to setup script environment for this language");
1028 // but fall through and let the inevitable failure below handle it.
1031 nsIScriptContext* context = global->GetScriptContext();
1032 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
1033 NS_ENSURE_STATE(global->HasJSGlobal());
1035 removeEventHandler.release();
1037 Listener* listener = SetEventHandlerInternal(aName, TypedEventHandler(),
1038 aPermitUntrustedEvents);
1040 if (!aDeferCompilation) {
1041 return CompileEventHandlerInternal(listener, aName, &aBody, aElement);
1044 return NS_OK;
1047 void EventListenerManager::RemoveEventHandler(nsAtom* aName) {
1048 if (mClearingListeners) {
1049 return;
1052 Maybe<size_t> entryIndex = mListenerMap.EntryIndexForType(aName);
1053 if (!entryIndex) {
1054 return;
1057 ListenerArray& listenerArray = *mListenerMap.mEntries[*entryIndex].mListeners;
1059 Maybe<uint32_t> listenerIndex = [&]() -> Maybe<uint32_t> {
1060 uint32_t count = listenerArray.Length();
1061 for (uint32_t i = 0; i < count; ++i) {
1062 Listener* listener = &listenerArray.ElementAt(i);
1063 if (listener->mListenerIsHandler) {
1064 return Some(i);
1067 return Nothing();
1068 }();
1070 if (!listenerIndex) {
1071 return;
1074 listenerArray.RemoveElementAt(*listenerIndex);
1075 if (listenerArray.IsEmpty()) {
1076 mListenerMap.mEntries.RemoveElementAt(*entryIndex);
1079 RefPtr<EventListenerManager> kungFuDeathGrip(this);
1080 NotifyEventListenerRemoved(aName);
1081 if (IsDeviceType(aName)) {
1082 DisableDevice(aName);
1086 nsresult EventListenerManager::CompileEventHandlerInternal(
1087 Listener* aListener, nsAtom* aTypeAtom, const nsAString* aBody,
1088 Element* aElement) {
1089 MOZ_ASSERT(aListener->GetJSEventHandler());
1090 MOZ_ASSERT(aListener->mHandlerIsString,
1091 "Why are we compiling a non-string JS listener?");
1092 JSEventHandler* jsEventHandler = aListener->GetJSEventHandler();
1093 MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(),
1094 "What is there to compile?");
1096 nsresult result = NS_OK;
1097 nsCOMPtr<Document> doc;
1098 nsCOMPtr<nsIScriptGlobalObject> global =
1099 GetScriptGlobalAndDocument(getter_AddRefs(doc));
1100 NS_ENSURE_STATE(global);
1102 // Activate JSAPI, and make sure that exceptions are reported on the right
1103 // Window.
1104 AutoJSAPI jsapi;
1105 if (NS_WARN_IF(!jsapi.Init(global))) {
1106 return NS_ERROR_UNEXPECTED;
1108 JSContext* cx = jsapi.cx();
1110 nsAtom* attrName = aTypeAtom;
1112 // Flag us as not a string so we don't keep trying to compile strings which
1113 // can't be compiled.
1114 aListener->mHandlerIsString = false;
1116 // mTarget may not be an Element if it's a window and we're
1117 // getting an inline event listener forwarded from <html:body> or
1118 // <html:frameset> or <xul:window> or the like.
1119 // XXX I don't like that we have to reference content from
1120 // here. The alternative is to store the event handler string on
1121 // the JSEventHandler itself, and that still doesn't address
1122 // the arg names issue.
1123 RefPtr<Element> element = Element::FromEventTargetOrNull(mTarget);
1124 MOZ_ASSERT(element || aBody, "Where will we get our body?");
1125 nsAutoString handlerBody;
1126 const nsAString* body = aBody;
1127 if (!aBody) {
1128 if (aTypeAtom == nsGkAtoms::onSVGLoad) {
1129 attrName = nsGkAtoms::onload;
1130 } else if (aTypeAtom == nsGkAtoms::onSVGScroll) {
1131 attrName = nsGkAtoms::onscroll;
1132 } else if (aTypeAtom == nsGkAtoms::onbeginEvent) {
1133 attrName = nsGkAtoms::onbegin;
1134 } else if (aTypeAtom == nsGkAtoms::onrepeatEvent) {
1135 attrName = nsGkAtoms::onrepeat;
1136 } else if (aTypeAtom == nsGkAtoms::onendEvent) {
1137 attrName = nsGkAtoms::onend;
1138 } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationEnd) {
1139 attrName = nsGkAtoms::onwebkitanimationend;
1140 } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationIteration) {
1141 attrName = nsGkAtoms::onwebkitanimationiteration;
1142 } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationStart) {
1143 attrName = nsGkAtoms::onwebkitanimationstart;
1144 } else if (aTypeAtom == nsGkAtoms::onwebkitTransitionEnd) {
1145 attrName = nsGkAtoms::onwebkittransitionend;
1148 element->GetAttr(attrName, handlerBody);
1149 body = &handlerBody;
1150 aElement = element;
1152 aListener = nullptr;
1154 nsAutoCString url("-moz-evil:lying-event-listener"_ns);
1155 MOZ_ASSERT(body);
1156 MOZ_ASSERT(aElement);
1157 nsIURI* uri = aElement->OwnerDoc()->GetDocumentURI();
1158 if (uri) {
1159 uri->GetSpec(url);
1162 nsCOMPtr<nsPIDOMWindowInner> win =
1163 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget);
1164 uint32_t argCount;
1165 const char** argNames;
1166 nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), aTypeAtom, win,
1167 &argCount, &argNames);
1169 // Wrap the event target, so that we can use it as the scope for the event
1170 // handler. Note that mTarget is different from aElement in the <body> case,
1171 // where mTarget is a Window.
1173 // The wrapScope doesn't really matter here, because the target will create
1174 // its reflector in the proper scope, and then we'll enter that realm.
1175 JS::Rooted<JSObject*> wrapScope(cx, global->GetGlobalJSObject());
1176 JS::Rooted<JS::Value> v(cx);
1178 JSAutoRealm ar(cx, wrapScope);
1179 nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v,
1180 /* aAllowWrapping = */ false);
1181 if (NS_WARN_IF(NS_FAILED(rv))) {
1182 return rv;
1186 JS::Rooted<JSObject*> target(cx, &v.toObject());
1187 JSAutoRealm ar(cx, target);
1189 // Now that we've entered the realm we actually care about, create our
1190 // scope chain. Note that we start with |element|, not aElement, because
1191 // mTarget is different from aElement in the <body> case, where mTarget is a
1192 // Window, and in that case we do not want the scope chain to include the body
1193 // or the document.
1194 JS::RootedVector<JSObject*> scopeChain(cx);
1195 if (!nsJSUtils::GetScopeChainForElement(cx, element, &scopeChain)) {
1196 return NS_ERROR_OUT_OF_MEMORY;
1199 nsDependentAtomString str(attrName);
1200 // Most of our names are short enough that we don't even have to malloc
1201 // the JS string stuff, so don't worry about playing games with
1202 // refcounting XPCOM stringbuffers.
1203 JS::Rooted<JSString*> jsStr(
1204 cx, JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length()));
1205 NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY);
1207 // Get the reflector for |aElement|, so that we can pass to setElement.
1208 if (NS_WARN_IF(!GetOrCreateDOMReflector(cx, aElement, &v))) {
1209 return NS_ERROR_FAILURE;
1212 RefPtr<JS::loader::ScriptFetchOptions> fetchOptions =
1213 new JS::loader::ScriptFetchOptions(
1214 CORS_NONE, /* aNonce = */ u""_ns, RequestPriority::Auto,
1215 JS::loader::ParserMetadata::NotParserInserted,
1216 aElement->OwnerDoc()->NodePrincipal());
1218 RefPtr<JS::loader::EventScript> eventScript = new JS::loader::EventScript(
1219 aElement->OwnerDoc()->GetReferrerPolicy(), fetchOptions, uri);
1221 JS::CompileOptions options(cx);
1222 // Use line 0 to make the function body starts from line 1.
1223 options.setIntroductionType("eventHandler")
1224 .setFileAndLine(url.get(), 0)
1225 .setDeferDebugMetadata(true);
1227 JS::Rooted<JSObject*> handler(cx);
1228 result = nsJSUtils::CompileFunction(jsapi, scopeChain, options,
1229 nsAtomCString(aTypeAtom), argCount,
1230 argNames, *body, handler.address());
1231 NS_ENSURE_SUCCESS(result, result);
1232 NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
1234 JS::Rooted<JS::Value> privateValue(cx, JS::PrivateValue(eventScript));
1235 result = nsJSUtils::UpdateFunctionDebugMetadata(jsapi, handler, options,
1236 jsStr, privateValue);
1237 NS_ENSURE_SUCCESS(result, result);
1239 MOZ_ASSERT(js::IsObjectInContextCompartment(handler, cx));
1240 JS::Rooted<JSObject*> handlerGlobal(cx, JS::CurrentGlobalOrNull(cx));
1242 if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) {
1243 RefPtr<OnErrorEventHandlerNonNull> handlerCallback =
1244 new OnErrorEventHandlerNonNull(static_cast<JSContext*>(nullptr),
1245 handler, handlerGlobal,
1246 /* aIncumbentGlobal = */ nullptr);
1247 jsEventHandler->SetHandler(handlerCallback);
1248 } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) {
1249 RefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
1250 new OnBeforeUnloadEventHandlerNonNull(static_cast<JSContext*>(nullptr),
1251 handler, handlerGlobal,
1252 /* aIncumbentGlobal = */ nullptr);
1253 jsEventHandler->SetHandler(handlerCallback);
1254 } else {
1255 RefPtr<EventHandlerNonNull> handlerCallback = new EventHandlerNonNull(
1256 static_cast<JSContext*>(nullptr), handler, handlerGlobal,
1257 /* aIncumbentGlobal = */ nullptr);
1258 jsEventHandler->SetHandler(handlerCallback);
1261 return result;
1264 bool EventListenerManager::HandleEventSingleListener(
1265 Listener* aListener, nsAtom* aTypeAtom, WidgetEvent* aEvent,
1266 Event* aDOMEvent, EventTarget* aCurrentTarget, bool aItemInShadowTree) {
1267 if (!aEvent->mCurrentTarget) {
1268 aEvent->mCurrentTarget = aCurrentTarget->GetTargetForDOMEvent();
1269 if (!aEvent->mCurrentTarget) {
1270 return false;
1274 aEvent->mFlags.mInPassiveListener = aListener->mFlags.mPassive;
1276 nsCOMPtr<nsPIDOMWindowInner> innerWindow =
1277 WindowFromListener(aListener, aTypeAtom, aItemInShadowTree);
1278 mozilla::dom::Event* oldWindowEvent = nullptr;
1279 if (innerWindow) {
1280 oldWindowEvent = innerWindow->SetEvent(aDOMEvent);
1283 nsresult result = NS_OK;
1285 // strong ref
1286 EventListenerHolder listenerHolder(aListener->mListener.Clone());
1288 // If this is a script handler and we haven't yet
1289 // compiled the event handler itself
1290 if ((aListener->mListenerType == Listener::eJSEventListener) &&
1291 aListener->mHandlerIsString) {
1292 result =
1293 CompileEventHandlerInternal(aListener, aTypeAtom, nullptr, nullptr);
1294 aListener = nullptr;
1297 if (NS_SUCCEEDED(result)) {
1298 Maybe<EventCallbackDebuggerNotificationGuard> dbgGuard;
1299 if (dom::ChromeUtils::IsDevToolsOpened()) {
1300 dbgGuard.emplace(aCurrentTarget, aDOMEvent);
1302 nsAutoMicroTask mt;
1304 // Event::currentTarget is set in EventDispatcher.
1305 if (listenerHolder.HasWebIDLCallback()) {
1306 ErrorResult rv;
1307 listenerHolder.GetWebIDLCallback()->HandleEvent(aCurrentTarget,
1308 *aDOMEvent, rv);
1309 result = rv.StealNSResult();
1310 } else {
1311 // listenerHolder is holding a stack ref here.
1312 result = MOZ_KnownLive(listenerHolder.GetXPCOMCallback())
1313 ->HandleEvent(aDOMEvent);
1317 if (innerWindow) {
1318 Unused << innerWindow->SetEvent(oldWindowEvent);
1321 if (NS_FAILED(result)) {
1322 aEvent->mFlags.mExceptionWasRaised = true;
1324 aEvent->mFlags.mInPassiveListener = false;
1325 return !aEvent->mFlags.mImmediatePropagationStopped;
1328 /* static */ EventMessage EventListenerManager::GetLegacyEventMessage(
1329 EventMessage aEventMessage) {
1330 // webkit-prefixed legacy events:
1331 if (aEventMessage == eTransitionEnd) {
1332 return eWebkitTransitionEnd;
1334 if (aEventMessage == eAnimationStart) {
1335 return eWebkitAnimationStart;
1337 if (aEventMessage == eAnimationEnd) {
1338 return eWebkitAnimationEnd;
1340 if (aEventMessage == eAnimationIteration) {
1341 return eWebkitAnimationIteration;
1344 switch (aEventMessage) {
1345 case eFullscreenChange:
1346 return eMozFullscreenChange;
1347 case eFullscreenError:
1348 return eMozFullscreenError;
1349 default:
1350 return aEventMessage;
1354 EventMessage EventListenerManager::GetEventMessage(nsAtom* aEventName) const {
1355 if (mIsMainThreadELM) {
1356 return nsContentUtils::GetEventMessage(aEventName);
1359 // The nsContentUtils event message hashtables aren't threadsafe, so just fall
1360 // back to eUnidentifiedEvent.
1361 return eUnidentifiedEvent;
1364 EventMessage EventListenerManager::GetEventMessageAndAtomForListener(
1365 const nsAString& aType, nsAtom** aAtom) {
1366 if (mIsMainThreadELM) {
1367 return nsContentUtils::GetEventMessageAndAtomForListener(aType, aAtom);
1370 *aAtom = NS_Atomize(u"on"_ns + aType).take();
1371 return eUnidentifiedEvent;
1374 already_AddRefed<nsPIDOMWindowInner> EventListenerManager::WindowFromListener(
1375 Listener* aListener, nsAtom* aTypeAtom, bool aItemInShadowTree) {
1376 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
1377 if (!aItemInShadowTree) {
1378 if (aListener->mListener.HasWebIDLCallback()) {
1379 CallbackObject* callback = aListener->mListener.GetWebIDLCallback();
1380 nsIGlobalObject* global = nullptr;
1381 if (callback) {
1382 global = callback->IncumbentGlobalOrNull();
1384 if (global) {
1385 innerWindow = global->GetAsInnerWindow(); // Can be nullptr
1387 } else if (mTarget) {
1388 // This ensures `window.event` can be set properly for
1389 // nsWindowRoot to handle KeyPress event.
1390 if (aListener && aTypeAtom == nsGkAtoms::onkeypress &&
1391 mTarget->IsRootWindow()) {
1392 nsPIWindowRoot* root = mTarget->AsWindowRoot();
1393 if (nsPIDOMWindowOuter* outerWindow = root->GetWindow()) {
1394 innerWindow = outerWindow->GetCurrentInnerWindow();
1396 } else {
1397 // Can't get the global from
1398 // listener->mListener.GetXPCOMCallback().
1399 // In most cases, it would be the same as for
1400 // the target, so let's do that.
1401 if (nsIGlobalObject* global = mTarget->GetOwnerGlobal()) {
1402 innerWindow = global->GetAsInnerWindow();
1407 return innerWindow.forget();
1410 Maybe<size_t> EventListenerManager::EventListenerMap::EntryIndexForType(
1411 nsAtom* aTypeAtom) const {
1412 MOZ_ASSERT(aTypeAtom);
1414 size_t matchIndexOrInsertionPoint = 0;
1415 bool foundMatch = BinarySearchIf(mEntries, 0, mEntries.Length(),
1416 ListenerMapEntryComparator(aTypeAtom),
1417 &matchIndexOrInsertionPoint);
1418 return foundMatch ? Some(matchIndexOrInsertionPoint) : Nothing();
1421 Maybe<size_t> EventListenerManager::EventListenerMap::EntryIndexForAllEvents()
1422 const {
1423 // If we have an entry for "all events listeners", it'll be at the beginning
1424 // of the list and its type atom will be null.
1425 return !mEntries.IsEmpty() && mEntries[0].mTypeAtom == nullptr ? Some(0)
1426 : Nothing();
1429 RefPtr<EventListenerManager::ListenerArray>
1430 EventListenerManager::EventListenerMap::GetListenersForType(
1431 nsAtom* aTypeAtom) const {
1432 Maybe<size_t> index = EntryIndexForType(aTypeAtom);
1433 return index ? mEntries[*index].mListeners : nullptr;
1436 RefPtr<EventListenerManager::ListenerArray>
1437 EventListenerManager::EventListenerMap::GetListenersForAllEvents() const {
1438 Maybe<size_t> index = EntryIndexForAllEvents();
1439 return index ? mEntries[*index].mListeners : nullptr;
1442 RefPtr<EventListenerManager::ListenerArray>
1443 EventListenerManager::EventListenerMap::GetOrCreateListenersForType(
1444 nsAtom* aTypeAtom) {
1445 MOZ_ASSERT(aTypeAtom);
1446 size_t matchIndexOrInsertionPoint = 0;
1447 bool foundMatch = BinarySearchIf(mEntries, 0, mEntries.Length(),
1448 ListenerMapEntryComparator(aTypeAtom),
1449 &matchIndexOrInsertionPoint);
1450 if (foundMatch) {
1451 return mEntries[matchIndexOrInsertionPoint].mListeners;
1453 RefPtr<ListenerArray> listeners = MakeRefPtr<ListenerArray>();
1454 mEntries.InsertElementAt(matchIndexOrInsertionPoint,
1455 EventListenerMapEntry{aTypeAtom, listeners});
1457 return listeners;
1460 RefPtr<EventListenerManager::ListenerArray>
1461 EventListenerManager::EventListenerMap::GetOrCreateListenersForAllEvents() {
1462 RefPtr<ListenerArray> listeners = GetListenersForAllEvents();
1463 if (!listeners) {
1464 listeners = MakeRefPtr<ListenerArray>();
1465 mEntries.InsertElementAt(0, EventListenerMapEntry{nullptr, listeners});
1467 return listeners;
1470 void EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
1471 WidgetEvent* aEvent,
1472 Event** aDOMEvent,
1473 EventTarget* aCurrentTarget,
1474 nsEventStatus* aEventStatus,
1475 bool aItemInShadowTree) {
1476 MOZ_ASSERT_IF(aEvent->mMessage != eUnidentifiedEvent, mIsMainThreadELM);
1478 // Set the value of the internal PreventDefault flag properly based on
1479 // aEventStatus
1480 if (!aEvent->DefaultPrevented() &&
1481 *aEventStatus == nsEventStatus_eConsumeNoDefault) {
1482 // Assume that if only aEventStatus claims that the event has already been
1483 // consumed, the consumer is default event handler.
1484 aEvent->PreventDefault();
1487 if (aEvent->mFlags.mImmediatePropagationStopped) {
1488 return;
1491 Maybe<AutoHandlingUserInputStatePusher> userInputStatePusher;
1492 Maybe<AutoPopupStatePusher> popupStatePusher;
1493 if (mIsMainThreadELM) {
1494 userInputStatePusher.emplace(UserActivation::IsUserInteractionEvent(aEvent),
1495 aEvent);
1496 popupStatePusher.emplace(
1497 PopupBlocker::GetEventPopupControlState(aEvent, *aDOMEvent));
1500 EventMessage eventMessage = aEvent->mMessage;
1501 RefPtr<nsAtom> typeAtom =
1502 eventMessage == eUnidentifiedEvent
1503 ? aEvent->mSpecifiedEventType.get()
1504 : nsContentUtils::GetEventTypeFromMessage(eventMessage);
1505 if (!typeAtom) {
1506 // Some messages don't have a corresponding type atom, e.g.
1507 // eMouseEnterIntoWidget. These events can't have a listener, so we
1508 // can stop here.
1509 return;
1512 bool hasAnyListenerForEventType = false;
1514 // First, notify any "all events" listeners.
1515 if (RefPtr<ListenerArray> listenersForAllEvents =
1516 mListenerMap.GetListenersForAllEvents()) {
1517 HandleEventWithListenerArray(listenersForAllEvents, typeAtom, eventMessage,
1518 aPresContext, aEvent, aDOMEvent,
1519 aCurrentTarget, aItemInShadowTree);
1520 hasAnyListenerForEventType = true;
1523 // Now look for listeners for typeAtom, and call them if we have any.
1524 bool hasAnyListenerMatchingGroup = false;
1525 if (RefPtr<ListenerArray> listeners =
1526 mListenerMap.GetListenersForType(typeAtom)) {
1527 hasAnyListenerMatchingGroup = HandleEventWithListenerArray(
1528 listeners, typeAtom, eventMessage, aPresContext, aEvent, aDOMEvent,
1529 aCurrentTarget, aItemInShadowTree);
1530 hasAnyListenerForEventType = true;
1533 if (!hasAnyListenerMatchingGroup && aEvent->IsTrusted()) {
1534 // If we didn't find any matching listeners, and our event has a legacy
1535 // version, check the listeners for the legacy version.
1536 EventMessage legacyEventMessage = GetLegacyEventMessage(eventMessage);
1537 if (legacyEventMessage != eventMessage) {
1538 MOZ_ASSERT(
1539 GetLegacyEventMessage(legacyEventMessage) == legacyEventMessage,
1540 "Legacy event messages should not themselves have legacy versions");
1541 RefPtr<nsAtom> legacyTypeAtom =
1542 nsContentUtils::GetEventTypeFromMessage(legacyEventMessage);
1543 if (RefPtr<ListenerArray> legacyListeners =
1544 mListenerMap.GetListenersForType(legacyTypeAtom)) {
1545 HandleEventWithListenerArray(
1546 legacyListeners, legacyTypeAtom, legacyEventMessage, aPresContext,
1547 aEvent, aDOMEvent, aCurrentTarget, aItemInShadowTree);
1548 hasAnyListenerForEventType = true;
1553 aEvent->mCurrentTarget = nullptr;
1555 if (mIsMainThreadELM && !hasAnyListenerForEventType) {
1556 if (aEvent->mMessage != eUnidentifiedEvent) {
1557 mNoListenerForEvents[2] = mNoListenerForEvents[1];
1558 mNoListenerForEvents[1] = mNoListenerForEvents[0];
1559 mNoListenerForEvents[0] = aEvent->mMessage;
1560 } else {
1561 mNoListenerForEventAtom = aEvent->mSpecifiedEventType;
1565 if (aEvent->DefaultPrevented()) {
1566 *aEventStatus = nsEventStatus_eConsumeNoDefault;
1570 bool EventListenerManager::HandleEventWithListenerArray(
1571 ListenerArray* aListeners, nsAtom* aTypeAtom, EventMessage aEventMessage,
1572 nsPresContext* aPresContext, WidgetEvent* aEvent, Event** aDOMEvent,
1573 EventTarget* aCurrentTarget, bool aItemInShadowTree) {
1574 auto ensureDOMEvent = [&]() {
1575 if (!*aDOMEvent) {
1576 // Lazily create the DOM event.
1577 // This is tiny bit slow, but happens only once per event.
1578 // Similar code also in EventDispatcher.
1579 nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget;
1580 RefPtr<Event> event =
1581 EventDispatcher::CreateEvent(et, aPresContext, aEvent, u""_ns);
1582 event.forget(aDOMEvent);
1584 return *aDOMEvent != nullptr;
1587 Maybe<EventMessageAutoOverride> eventMessageAutoOverride;
1588 bool isOverridingEventMessage = aEvent->mMessage != aEventMessage;
1589 bool hasAnyListenerMatchingGroup = false;
1590 bool didReplaceOnceListener = false;
1592 for (Listener& listenerRef : aListeners->EndLimitedRange()) {
1593 Listener* listener = &listenerRef;
1594 if (listener->mListenerType == Listener::eNoListener) {
1595 // The listener is a placeholder value of a removed "once" listener.
1596 continue;
1598 if (!listener->mEnabled) {
1599 // The listener has been disabled, for example by devtools.
1600 continue;
1602 if (!listener->MatchesEventGroup(aEvent)) {
1603 continue;
1605 hasAnyListenerMatchingGroup = true;
1607 // Check that the phase is same in event and event listener. Also check
1608 // that the event is trusted or that the listener allows untrusted events.
1609 if (!listener->MatchesEventPhase(aEvent) ||
1610 !listener->AllowsEventTrustedness(aEvent)) {
1611 continue;
1614 Maybe<Listener> listenerHolder;
1615 if (listener->mFlags.mOnce) {
1616 // Move the listener to the stack before handling the event.
1617 // The order is important, otherwise the listener could be
1618 // called again inside the listener.
1619 listenerHolder.emplace(std::move(*listener));
1620 listener = listenerHolder.ptr();
1621 didReplaceOnceListener = true;
1623 if (ensureDOMEvent()) {
1624 if (isOverridingEventMessage && !eventMessageAutoOverride) {
1625 // Override the domEvent's event-message (its .type) until we
1626 // finish traversing listeners (when eventMessageAutoOverride
1627 // destructs).
1628 eventMessageAutoOverride.emplace(*aDOMEvent, aEventMessage);
1630 if (!HandleEventSingleListener(listener, aTypeAtom, aEvent, *aDOMEvent,
1631 aCurrentTarget, aItemInShadowTree)) {
1632 break;
1637 if (didReplaceOnceListener) {
1638 // If there are any once listeners replaced with a placeholder during the
1639 // loop above, we need to clean up them here. Note that this could clear
1640 // once listeners handled in some outer level as well, but that should not
1641 // affect the result.
1642 size_t oldLength = aListeners->Length();
1643 aListeners->NonObservingRemoveElementsBy([](const Listener& aListener) {
1644 return aListener.mListenerType == Listener::eNoListener;
1646 size_t newLength = aListeners->Length();
1647 if (newLength == 0) {
1648 // Remove the entry that has now become empty.
1649 mListenerMap.mEntries.RemoveElementsBy([](EventListenerMapEntry& entry) {
1650 return entry.mListeners->IsEmpty();
1653 if (newLength < oldLength) {
1654 // Call NotifyEventListenerRemoved once for every removed listener.
1655 size_t removedCount = oldLength - newLength;
1656 for (size_t i = 0; i < removedCount; i++) {
1657 NotifyEventListenerRemoved(aTypeAtom);
1659 if (IsDeviceType(aTypeAtom)) {
1660 // Call DisableDevice once for every removed listener.
1661 for (size_t i = 0; i < removedCount; i++) {
1662 DisableDevice(aTypeAtom);
1668 return hasAnyListenerMatchingGroup;
1671 void EventListenerManager::Disconnect() {
1672 mTarget = nullptr;
1673 RemoveAllListenersSilently();
1676 void EventListenerManager::AddEventListener(const nsAString& aType,
1677 EventListenerHolder aListenerHolder,
1678 bool aUseCapture,
1679 bool aWantsUntrusted) {
1680 EventListenerFlags flags;
1681 flags.mCapture = aUseCapture;
1682 flags.mAllowUntrustedEvents = aWantsUntrusted;
1683 return AddEventListenerByType(std::move(aListenerHolder), aType, flags);
1686 void EventListenerManager::AddEventListener(
1687 const nsAString& aType, EventListenerHolder aListenerHolder,
1688 const dom::AddEventListenerOptionsOrBoolean& aOptions,
1689 bool aWantsUntrusted) {
1690 EventListenerFlags flags;
1691 Optional<bool> passive;
1692 AbortSignal* signal = nullptr;
1693 if (aOptions.IsBoolean()) {
1694 flags.mCapture = aOptions.GetAsBoolean();
1695 } else {
1696 const auto& options = aOptions.GetAsAddEventListenerOptions();
1697 flags.mCapture = options.mCapture;
1698 flags.mInSystemGroup = options.mMozSystemGroup;
1699 flags.mOnce = options.mOnce;
1700 if (options.mPassive.WasPassed()) {
1701 passive.Construct(options.mPassive.Value());
1704 if (options.mSignal.WasPassed()) {
1705 signal = &options.mSignal.Value();
1709 flags.mAllowUntrustedEvents = aWantsUntrusted;
1710 return AddEventListenerByType(std::move(aListenerHolder), aType, flags,
1711 passive, signal);
1714 void EventListenerManager::RemoveEventListener(
1715 const nsAString& aType, EventListenerHolder aListenerHolder,
1716 bool aUseCapture) {
1717 EventListenerFlags flags;
1718 flags.mCapture = aUseCapture;
1719 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
1722 void EventListenerManager::RemoveEventListener(
1723 const nsAString& aType, EventListenerHolder aListenerHolder,
1724 const dom::EventListenerOptionsOrBoolean& aOptions) {
1725 EventListenerFlags flags;
1726 if (aOptions.IsBoolean()) {
1727 flags.mCapture = aOptions.GetAsBoolean();
1728 } else {
1729 const auto& options = aOptions.GetAsEventListenerOptions();
1730 flags.mCapture = options.mCapture;
1731 flags.mInSystemGroup = options.mMozSystemGroup;
1733 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
1736 void EventListenerManager::AddListenerForAllEvents(EventListener* aDOMListener,
1737 bool aUseCapture,
1738 bool aWantsUntrusted,
1739 bool aSystemEventGroup) {
1740 EventListenerFlags flags;
1741 flags.mCapture = aUseCapture;
1742 flags.mAllowUntrustedEvents = aWantsUntrusted;
1743 flags.mInSystemGroup = aSystemEventGroup;
1744 AddEventListenerInternal(EventListenerHolder(aDOMListener), eAllEvents,
1745 nullptr, flags, false, true);
1748 void EventListenerManager::RemoveListenerForAllEvents(
1749 EventListener* aDOMListener, bool aUseCapture, bool aSystemEventGroup) {
1750 EventListenerFlags flags;
1751 flags.mCapture = aUseCapture;
1752 flags.mInSystemGroup = aSystemEventGroup;
1753 RemoveEventListenerInternal(EventListenerHolder(aDOMListener), nullptr, flags,
1754 true);
1757 bool EventListenerManager::HasMutationListeners() {
1758 if (mMayHaveMutationListeners) {
1759 for (const auto& entry : mListenerMap.mEntries) {
1760 EventMessage message = GetEventMessage(entry.mTypeAtom);
1761 if (message >= eLegacyMutationEventFirst &&
1762 message <= eLegacyMutationEventLast) {
1763 return true;
1768 return false;
1771 uint32_t EventListenerManager::MutationListenerBits() {
1772 uint32_t bits = 0;
1773 if (mMayHaveMutationListeners) {
1774 for (const auto& entry : mListenerMap.mEntries) {
1775 EventMessage message = GetEventMessage(entry.mTypeAtom);
1776 if (message >= eLegacyMutationEventFirst &&
1777 message <= eLegacyMutationEventLast) {
1778 if (message == eLegacySubtreeModified) {
1779 return NS_EVENT_BITS_MUTATION_ALL;
1781 bits |= MutationBitForEventType(message);
1785 return bits;
1788 bool EventListenerManager::HasListenersFor(const nsAString& aEventName) const {
1789 RefPtr<nsAtom> atom = NS_Atomize(u"on"_ns + aEventName);
1790 return HasListenersFor(atom);
1793 bool EventListenerManager::HasListenersFor(nsAtom* aEventNameWithOn) const {
1794 return HasListenersForInternal(aEventNameWithOn, false);
1797 bool EventListenerManager::HasNonSystemGroupListenersFor(
1798 nsAtom* aEventNameWithOn) const {
1799 return HasListenersForInternal(aEventNameWithOn, true);
1802 bool EventListenerManager::HasListenersForInternal(
1803 nsAtom* aEventNameWithOn, bool aIgnoreSystemGroup) const {
1804 #ifdef DEBUG
1805 nsAutoString name;
1806 aEventNameWithOn->ToString(name);
1807 #endif
1808 NS_ASSERTION(StringBeginsWith(name, u"on"_ns),
1809 "Event name does not start with 'on'");
1810 RefPtr<ListenerArray> listeners =
1811 mListenerMap.GetListenersForType(aEventNameWithOn);
1812 if (!listeners) {
1813 return false;
1816 MOZ_ASSERT(!listeners->IsEmpty());
1818 if (!aIgnoreSystemGroup) {
1819 return true;
1822 // Check if any non-system-group listeners exist in `listeners`.
1823 for (const auto& listener : listeners->NonObservingRange()) {
1824 if (!listener.mFlags.mInSystemGroup) {
1825 return true;
1829 return false;
1832 bool EventListenerManager::HasListeners() const {
1833 return !mListenerMap.IsEmpty();
1836 nsresult EventListenerManager::GetListenerInfo(
1837 nsTArray<RefPtr<nsIEventListenerInfo>>& aList) {
1838 nsCOMPtr<EventTarget> target = mTarget;
1839 NS_ENSURE_STATE(target);
1840 aList.Clear();
1841 for (const auto& entry : mListenerMap.mEntries) {
1842 for (const Listener& listener : entry.mListeners->ForwardRange()) {
1843 // If this is a script handler and we haven't yet
1844 // compiled the event handler itself go ahead and compile it
1845 if (listener.mListenerType == Listener::eJSEventListener &&
1846 listener.mHandlerIsString) {
1847 CompileEventHandlerInternal(const_cast<Listener*>(&listener),
1848 entry.mTypeAtom, nullptr, nullptr);
1850 nsAutoString eventType;
1851 if (listener.mAllEvents) {
1852 eventType.SetIsVoid(true);
1853 } else if (listener.mListenerType == Listener::eNoListener) {
1854 continue;
1855 } else {
1856 eventType.Assign(Substring(nsDependentAtomString(entry.mTypeAtom), 2));
1859 JS::Rooted<JSObject*> callback(RootingCx());
1860 JS::Rooted<JSObject*> callbackGlobal(RootingCx());
1861 if (JSEventHandler* handler = listener.GetJSEventHandler()) {
1862 if (handler->GetTypedEventHandler().HasEventHandler()) {
1863 CallbackFunction* callbackFun = handler->GetTypedEventHandler().Ptr();
1864 callback = callbackFun->CallableOrNull();
1865 callbackGlobal = callbackFun->CallbackGlobalOrNull();
1866 if (!callback) {
1867 // This will be null for cross-compartment event listeners
1868 // which have been destroyed.
1869 continue;
1872 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1873 EventListener* listenerCallback =
1874 listener.mListener.GetWebIDLCallback();
1875 callback = listenerCallback->CallbackOrNull();
1876 callbackGlobal = listenerCallback->CallbackGlobalOrNull();
1877 if (!callback) {
1878 // This will be null for cross-compartment event listeners
1879 // which have been destroyed.
1880 continue;
1884 RefPtr<EventListenerInfo> info = new EventListenerInfo(
1885 this, eventType, callback, callbackGlobal, listener.mFlags.mCapture,
1886 listener.mFlags.mAllowUntrustedEvents, listener.mFlags.mInSystemGroup,
1887 listener.mListenerIsHandler);
1888 aList.AppendElement(info.forget());
1891 return NS_OK;
1894 EventListenerManager::Listener* EventListenerManager::GetListenerFor(
1895 nsAString& aType, JSObject* aListener, bool aCapturing,
1896 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler) {
1897 NS_ENSURE_TRUE(aListener, nullptr);
1899 RefPtr<ListenerArray> listeners = ([&]() -> RefPtr<ListenerArray> {
1900 if (aType.IsVoid()) {
1901 return mListenerMap.GetListenersForAllEvents();
1904 for (auto& mapEntry : mListenerMap.mEntries) {
1905 if (RefPtr<nsAtom> typeAtom = mapEntry.mTypeAtom) {
1906 if (Substring(nsDependentAtomString(typeAtom), 2).Equals(aType)) {
1907 return mapEntry.mListeners;
1912 return nullptr;
1913 })();
1915 if (!listeners) {
1916 return nullptr;
1919 for (Listener& listener : listeners->ForwardRange()) {
1920 if (listener.mListenerType == Listener::eNoListener) {
1921 continue;
1924 if (listener.mFlags.mCapture != aCapturing ||
1925 listener.mFlags.mAllowUntrustedEvents != aAllowsUntrusted ||
1926 listener.mFlags.mInSystemGroup != aInSystemEventGroup) {
1927 continue;
1930 if (aIsHandler) {
1931 if (JSEventHandler* handler = listener.GetJSEventHandler()) {
1932 if (handler->GetTypedEventHandler().HasEventHandler()) {
1933 if (handler->GetTypedEventHandler().Ptr()->CallableOrNull() ==
1934 aListener) {
1935 return &listener;
1939 } else if (listener.mListenerType == Listener::eWebIDLListener &&
1940 listener.mListener.GetWebIDLCallback()->CallbackOrNull() ==
1941 aListener) {
1942 return &listener;
1945 return nullptr;
1948 nsresult EventListenerManager::IsListenerEnabled(
1949 nsAString& aType, JSObject* aListener, bool aCapturing,
1950 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler,
1951 bool* aEnabled) {
1952 Listener* listener =
1953 GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted,
1954 aInSystemEventGroup, aIsHandler);
1955 NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE);
1956 *aEnabled = listener->mEnabled;
1957 return NS_OK;
1960 nsresult EventListenerManager::SetListenerEnabled(
1961 nsAString& aType, JSObject* aListener, bool aCapturing,
1962 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler,
1963 bool aEnabled) {
1964 Listener* listener =
1965 GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted,
1966 aInSystemEventGroup, aIsHandler);
1967 NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE);
1968 listener->mEnabled = aEnabled;
1969 if (aEnabled) {
1970 // We may have enabled some listener, clear the cache for which events
1971 // we don't have listeners.
1972 ClearNoListenersForEvents();
1973 mNoListenerForEventAtom = nullptr;
1975 return NS_OK;
1978 bool EventListenerManager::HasUnloadListeners() {
1979 return mListenerMap.GetListenersForType(nsGkAtoms::onunload) != nullptr;
1982 bool EventListenerManager::HasBeforeUnloadListeners() {
1983 return mListenerMap.GetListenersForType(nsGkAtoms::onbeforeunload) != nullptr;
1986 void EventListenerManager::SetEventHandler(nsAtom* aEventName,
1987 EventHandlerNonNull* aHandler) {
1988 if (!aHandler) {
1989 RemoveEventHandler(aEventName);
1990 return;
1993 // Untrusted events are always permitted for non-chrome script
1994 // handlers.
1995 SetEventHandlerInternal(
1996 aEventName, TypedEventHandler(aHandler),
1997 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
2000 void EventListenerManager::SetEventHandler(
2001 OnErrorEventHandlerNonNull* aHandler) {
2002 if (!aHandler) {
2003 RemoveEventHandler(nsGkAtoms::onerror);
2004 return;
2007 // Untrusted events are always permitted on workers and for non-chrome script
2008 // on the main thread.
2009 bool allowUntrusted = !mIsMainThreadELM || !nsContentUtils::IsCallerChrome();
2011 SetEventHandlerInternal(nsGkAtoms::onerror, TypedEventHandler(aHandler),
2012 allowUntrusted);
2015 void EventListenerManager::SetEventHandler(
2016 OnBeforeUnloadEventHandlerNonNull* aHandler) {
2017 if (!aHandler) {
2018 RemoveEventHandler(nsGkAtoms::onbeforeunload);
2019 return;
2022 // Untrusted events are always permitted for non-chrome script
2023 // handlers.
2024 SetEventHandlerInternal(
2025 nsGkAtoms::onbeforeunload, TypedEventHandler(aHandler),
2026 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
2029 const TypedEventHandler* EventListenerManager::GetTypedEventHandler(
2030 nsAtom* aEventName) {
2031 Listener* listener = FindEventHandler(aEventName);
2033 if (!listener) {
2034 return nullptr;
2037 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
2039 if (listener->mHandlerIsString) {
2040 CompileEventHandlerInternal(listener, aEventName, nullptr, nullptr);
2043 const TypedEventHandler& typedHandler =
2044 jsEventHandler->GetTypedEventHandler();
2045 return typedHandler.HasEventHandler() ? &typedHandler : nullptr;
2048 size_t EventListenerManager::SizeOfIncludingThis(
2049 MallocSizeOf aMallocSizeOf) const {
2050 return aMallocSizeOf(this) + mListenerMap.SizeOfExcludingThis(aMallocSizeOf);
2053 size_t EventListenerManager::EventListenerMap::SizeOfExcludingThis(
2054 MallocSizeOf aMallocSizeOf) const {
2055 size_t n = mEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
2056 for (const auto& entry : mEntries) {
2057 n += entry.SizeOfExcludingThis(aMallocSizeOf);
2059 return n;
2062 size_t EventListenerManager::EventListenerMapEntry::SizeOfExcludingThis(
2063 MallocSizeOf aMallocSizeOf) const {
2064 return mListeners->SizeOfIncludingThis(aMallocSizeOf);
2067 size_t EventListenerManager::ListenerArray::SizeOfIncludingThis(
2068 MallocSizeOf aMallocSizeOf) const {
2069 size_t n = aMallocSizeOf(this);
2070 n += ShallowSizeOfExcludingThis(aMallocSizeOf);
2071 for (const auto& listener : NonObservingRange()) {
2072 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
2073 if (jsEventHandler) {
2074 n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf);
2077 return n;
2080 uint32_t EventListenerManager::ListenerCount() const {
2081 uint32_t count = 0;
2082 for (const auto& entry : mListenerMap.mEntries) {
2083 count += entry.mListeners->Length();
2085 return count;
2088 void EventListenerManager::MarkForCC() {
2089 for (const auto& entry : mListenerMap.mEntries) {
2090 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2091 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
2092 if (jsEventHandler) {
2093 const TypedEventHandler& typedHandler =
2094 jsEventHandler->GetTypedEventHandler();
2095 if (typedHandler.HasEventHandler()) {
2096 typedHandler.Ptr()->MarkForCC();
2098 } else if (listener.mListenerType == Listener::eWebIDLListener) {
2099 listener.mListener.GetWebIDLCallback()->MarkForCC();
2103 if (mRefCnt.IsPurple()) {
2104 mRefCnt.RemovePurple();
2108 void EventListenerManager::TraceListeners(JSTracer* aTrc) {
2109 for (const auto& entry : mListenerMap.mEntries) {
2110 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2111 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
2112 if (jsEventHandler) {
2113 const TypedEventHandler& typedHandler =
2114 jsEventHandler->GetTypedEventHandler();
2115 if (typedHandler.HasEventHandler()) {
2116 mozilla::TraceScriptHolder(typedHandler.Ptr(), aTrc);
2118 } else if (listener.mListenerType == Listener::eWebIDLListener) {
2119 mozilla::TraceScriptHolder(listener.mListener.GetWebIDLCallback(),
2120 aTrc);
2122 // We might have eWrappedJSListener, but that is the legacy type for
2123 // JS implemented event listeners, and trickier to handle here.
2128 bool EventListenerManager::HasNonSystemGroupListenersForUntrustedKeyEvents() {
2129 for (const auto& entry : mListenerMap.mEntries) {
2130 if (entry.mTypeAtom != nsGkAtoms::onkeydown &&
2131 entry.mTypeAtom != nsGkAtoms::onkeypress &&
2132 entry.mTypeAtom != nsGkAtoms::onkeyup) {
2133 continue;
2135 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2136 if (!listener.mFlags.mInSystemGroup &&
2137 listener.mFlags.mAllowUntrustedEvents) {
2138 return true;
2142 return false;
2145 bool EventListenerManager::
2146 HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents() {
2147 for (const auto& entry : mListenerMap.mEntries) {
2148 if (entry.mTypeAtom != nsGkAtoms::onkeydown &&
2149 entry.mTypeAtom != nsGkAtoms::onkeypress &&
2150 entry.mTypeAtom != nsGkAtoms::onkeyup) {
2151 continue;
2153 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2154 if (!listener.mFlags.mPassive && !listener.mFlags.mInSystemGroup &&
2155 listener.mFlags.mAllowUntrustedEvents) {
2156 return true;
2160 return false;
2163 bool EventListenerManager::HasApzAwareListeners() {
2164 if (!mIsMainThreadELM) {
2165 return false;
2168 for (const auto& entry : mListenerMap.mEntries) {
2169 if (!IsApzAwareEvent(entry.mTypeAtom)) {
2170 continue;
2172 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2173 if (!listener.mFlags.mPassive) {
2174 return true;
2178 return false;
2181 static bool IsWheelEventType(nsAtom* aEvent) {
2182 if (aEvent == nsGkAtoms::onwheel || aEvent == nsGkAtoms::onDOMMouseScroll ||
2183 aEvent == nsGkAtoms::onmousewheel ||
2184 aEvent == nsGkAtoms::onMozMousePixelScroll) {
2185 return true;
2187 return false;
2190 bool EventListenerManager::IsApzAwareEvent(nsAtom* aEvent) {
2191 if (IsWheelEventType(aEvent)) {
2192 return true;
2194 // In theory we should schedule a repaint if the touch event pref changes,
2195 // because the event regions might be out of date. In practice that seems like
2196 // overkill because users generally shouldn't be flipping this pref, much
2197 // less expecting touch listeners on the page to immediately start preventing
2198 // scrolling without so much as a repaint. Tests that we write can work
2199 // around this constraint easily enough.
2200 if (aEvent == nsGkAtoms::ontouchstart || aEvent == nsGkAtoms::ontouchmove) {
2201 return TouchEvent::PrefEnabled(
2202 nsContentUtils::GetDocShellForEventTarget(mTarget));
2204 return false;
2207 bool EventListenerManager::HasNonPassiveWheelListener() {
2208 MOZ_ASSERT(NS_IsMainThread());
2209 for (const auto& entry : mListenerMap.mEntries) {
2210 if (!IsWheelEventType(entry.mTypeAtom)) {
2211 continue;
2213 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2214 if (!listener.mFlags.mPassive) {
2215 return true;
2219 return false;
2222 void EventListenerManager::RemoveAllListeners() {
2223 while (!mListenerMap.IsEmpty()) {
2224 size_t entryIndex = mListenerMap.mEntries.Length() - 1;
2225 EventListenerMapEntry& entry = mListenerMap.mEntries[entryIndex];
2226 RefPtr<nsAtom> type = entry.mTypeAtom;
2227 MOZ_ASSERT(!entry.mListeners->IsEmpty());
2228 size_t idx = entry.mListeners->Length() - 1;
2229 entry.mListeners->RemoveElementAt(idx);
2230 if (entry.mListeners->IsEmpty()) {
2231 mListenerMap.mEntries.RemoveElementAt(entryIndex);
2233 NotifyEventListenerRemoved(type);
2234 if (IsDeviceType(type)) {
2235 DisableDevice(type);
2240 already_AddRefed<nsIScriptGlobalObject>
2241 EventListenerManager::GetScriptGlobalAndDocument(Document** aDoc) {
2242 nsCOMPtr<Document> doc;
2243 nsCOMPtr<nsPIDOMWindowInner> win;
2244 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
2245 // Try to get context from doc
2246 doc = node->OwnerDoc();
2247 if (doc->IsLoadedAsData()) {
2248 return nullptr;
2251 win = do_QueryInterface(doc->GetScopeObject());
2252 } else if ((win = GetTargetAsInnerWindow())) {
2253 doc = win->GetExtantDoc();
2256 if (!win || !win->IsCurrentInnerWindow()) {
2257 return nullptr;
2260 doc.forget(aDoc);
2261 nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(win);
2262 return global.forget();
2265 EventListenerManager::ListenerSignalFollower::ListenerSignalFollower(
2266 EventListenerManager* aListenerManager,
2267 EventListenerManager::Listener* aListener, nsAtom* aTypeAtom)
2268 : dom::AbortFollower(),
2269 mListenerManager(aListenerManager),
2270 mListener(aListener->mListener.Clone()),
2271 mTypeAtom(aTypeAtom),
2272 mAllEvents(aListener->mAllEvents),
2273 mFlags(aListener->mFlags){};
2275 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower)
2277 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower)
2278 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower)
2280 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
2281 EventListenerManager::ListenerSignalFollower)
2282 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
2283 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2285 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
2286 EventListenerManager::ListenerSignalFollower)
2287 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
2288 tmp->mListenerManager = nullptr;
2289 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2291 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
2292 EventListenerManager::ListenerSignalFollower)
2293 NS_INTERFACE_MAP_ENTRY(nsISupports)
2294 NS_INTERFACE_MAP_END
2296 void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() {
2297 if (mListenerManager) {
2298 RefPtr<EventListenerManager> elm = mListenerManager;
2299 mListenerManager = nullptr;
2300 elm->RemoveEventListenerInternal(std::move(mListener), mTypeAtom, mFlags,
2301 mAllEvents);
2305 } // namespace mozilla