Bug 1826136 [wpt PR 39338] - Update wpt metadata, a=testonly
[gecko.git] / dom / events / EventListenerManager.cpp
blobe82a1129487f6097cf6edeec92a8966121d1c032
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/loader/LoadedScript.h"
11 #include "mozilla/BasicEvents.h"
12 #include "mozilla/CycleCollectedJSRuntime.h"
13 #include "mozilla/DOMEventTargetHelper.h"
14 #include "mozilla/EventDispatcher.h"
15 #include "mozilla/EventListenerManager.h"
16 #include "mozilla/HalSensor.h"
17 #include "mozilla/InternalMutationEvent.h"
18 #include "mozilla/JSEventHandler.h"
19 #include "mozilla/Maybe.h"
20 #include "mozilla/MemoryReporting.h"
21 #include "mozilla/Preferences.h"
22 #include "mozilla/PresShell.h"
23 #include "mozilla/dom/AbortSignal.h"
24 #include "mozilla/dom/BindingUtils.h"
25 #include "mozilla/dom/EventCallbackDebuggerNotification.h"
26 #include "mozilla/dom/Element.h"
27 #include "mozilla/dom/Event.h"
28 #include "mozilla/dom/EventTargetBinding.h"
29 #include "mozilla/dom/PopupBlocker.h"
30 #include "mozilla/dom/ScriptLoader.h"
31 #include "mozilla/dom/ScriptSettings.h"
32 #include "mozilla/dom/TouchEvent.h"
33 #include "mozilla/dom/UserActivation.h"
34 #include "mozilla/ScopeExit.h"
35 #include "mozilla/StaticPrefs_dom.h"
36 #include "mozilla/TimeStamp.h"
37 #include "mozilla/dom/ChromeUtils.h"
39 #include "EventListenerService.h"
40 #include "nsCOMPtr.h"
41 #include "nsContentUtils.h"
42 #include "nsDOMCID.h"
43 #include "nsError.h"
44 #include "nsGenericHTMLElement.h"
45 #include "nsGkAtoms.h"
46 #include "nsIContent.h"
47 #include "nsIContentSecurityPolicy.h"
48 #include "mozilla/dom/Document.h"
49 #include "nsIScriptGlobalObject.h"
50 #include "nsISupports.h"
51 #include "nsJSUtils.h"
52 #include "nsNameSpaceManager.h"
53 #include "nsPIDOMWindow.h"
54 #include "nsPrintfCString.h"
55 #include "nsSandboxFlags.h"
56 #include "xpcpublic.h"
57 #include "nsIFrame.h"
58 #include "nsDisplayList.h"
60 namespace mozilla {
62 using namespace dom;
63 using namespace hal;
65 #define EVENT_TYPE_EQUALS(ls, message, userType, allEvents) \
66 ((ls->mEventMessage == message && \
67 (ls->mEventMessage != eUnidentifiedEvent || ls->mTypeAtom == userType)) || \
68 (allEvents && ls->mAllEvents))
70 static const uint32_t kAllMutationBits =
71 NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED |
72 NS_EVENT_BITS_MUTATION_NODEINSERTED | NS_EVENT_BITS_MUTATION_NODEREMOVED |
73 NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT |
74 NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT |
75 NS_EVENT_BITS_MUTATION_ATTRMODIFIED |
76 NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
78 static uint32_t MutationBitForEventType(EventMessage aEventType) {
79 switch (aEventType) {
80 case eLegacySubtreeModified:
81 return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED;
82 case eLegacyNodeInserted:
83 return NS_EVENT_BITS_MUTATION_NODEINSERTED;
84 case eLegacyNodeRemoved:
85 return NS_EVENT_BITS_MUTATION_NODEREMOVED;
86 case eLegacyNodeRemovedFromDocument:
87 return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT;
88 case eLegacyNodeInsertedIntoDocument:
89 return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT;
90 case eLegacyAttrModified:
91 return NS_EVENT_BITS_MUTATION_ATTRMODIFIED;
92 case eLegacyCharacterDataModified:
93 return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
94 default:
95 break;
97 return 0;
100 uint32_t EventListenerManager::sMainThreadCreatedCount = 0;
102 EventListenerManagerBase::EventListenerManagerBase()
103 : mNoListenerForEvent(eVoidEvent),
104 mMayHavePaintEventListener(false),
105 mMayHaveMutationListeners(false),
106 mMayHaveCapturingListeners(false),
107 mMayHaveSystemGroupListeners(false),
108 mMayHaveTouchEventListener(false),
109 mMayHaveMouseEnterLeaveEventListener(false),
110 mMayHavePointerEnterLeaveEventListener(false),
111 mMayHaveKeyEventListener(false),
112 mMayHaveInputOrCompositionEventListener(false),
113 mMayHaveSelectionChangeEventListener(false),
114 mMayHaveFormSelectEventListener(false),
115 mMayHaveTransitionEventListener(false),
116 mClearingListeners(false),
117 mIsMainThreadELM(NS_IsMainThread()),
118 mHasNonPrivilegedClickListeners(false),
119 mUnknownNonPrivilegedClickListeners(false) {
120 static_assert(sizeof(EventListenerManagerBase) == sizeof(uint32_t),
121 "Keep the size of EventListenerManagerBase size compact!");
124 EventListenerManager::EventListenerManager(EventTarget* aTarget)
125 : EventListenerManagerBase(), mTarget(aTarget) {
126 NS_ASSERTION(aTarget, "unexpected null pointer");
128 if (mIsMainThreadELM) {
129 ++sMainThreadCreatedCount;
133 EventListenerManager::~EventListenerManager() {
134 // If your code fails this assertion, a possible reason is that
135 // a class did not call our Disconnect() manually. Note that
136 // this class can have Disconnect called in one of two ways:
137 // if it is part of a cycle, then in Unlink() (such a cycle
138 // would be with one of the listeners, not mTarget which is weak).
139 // If not part of a cycle, then Disconnect must be called manually,
140 // typically from the destructor of the owner class (mTarget).
141 // XXX azakai: Is there any reason to not just call Disconnect
142 // from right here, if not previously called?
143 NS_ASSERTION(!mTarget, "didn't call Disconnect");
144 RemoveAllListenersSilently();
147 void EventListenerManager::RemoveAllListenersSilently() {
148 if (mClearingListeners) {
149 return;
151 mClearingListeners = true;
152 mListeners.Clear();
153 mClearingListeners = false;
156 inline void ImplCycleCollectionTraverse(
157 nsCycleCollectionTraversalCallback& aCallback,
158 EventListenerManager::Listener& aField, const char* aName,
159 unsigned aFlags) {
160 if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
161 nsAutoCString name;
162 name.AppendASCII(aName);
163 if (aField.mTypeAtom) {
164 name.AppendLiteral(" event=");
165 name.Append(nsAtomCString(aField.mTypeAtom));
166 name.AppendLiteral(" listenerType=");
167 name.AppendInt(aField.mListenerType);
168 name.AppendLiteral(" ");
170 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(),
171 name.get(), aFlags);
172 } else {
173 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName,
174 aFlags);
177 CycleCollectionNoteChild(aCallback, aField.mSignalFollower.get(),
178 "mSignalFollower", aFlags);
181 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager)
183 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager)
184 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
185 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
187 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager)
188 tmp->Disconnect();
189 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
191 nsPIDOMWindowInner* EventListenerManager::GetInnerWindowForTarget() {
192 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
193 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
194 // if that's the XBL document?
195 return node->OwnerDoc()->GetInnerWindow();
198 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
199 return window;
202 already_AddRefed<nsPIDOMWindowInner>
203 EventListenerManager::GetTargetAsInnerWindow() const {
204 nsCOMPtr<nsPIDOMWindowInner> window =
205 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget);
206 return window.forget();
209 void EventListenerManager::AddEventListenerInternal(
210 EventListenerHolder aListenerHolder, EventMessage aEventMessage,
211 nsAtom* aTypeAtom, const EventListenerFlags& aFlags, bool aHandler,
212 bool aAllEvents, AbortSignal* aSignal) {
213 MOZ_ASSERT((aEventMessage && aTypeAtom) || aAllEvents, // all-events listener
214 "Missing type");
216 if (!aListenerHolder || mClearingListeners) {
217 return;
220 if (aSignal && aSignal->Aborted()) {
221 return;
224 // Since there is no public API to call us with an EventListenerHolder, we
225 // know that there's an EventListenerHolder on the stack holding a strong ref
226 // to the listener.
228 Listener* listener;
229 uint32_t count = mListeners.Length();
230 for (uint32_t i = 0; i < count; i++) {
231 listener = &mListeners.ElementAt(i);
232 // mListener == aListenerHolder is the last one, since it can be a bit slow.
233 if (listener->mListenerIsHandler == aHandler &&
234 listener->mFlags.EqualsForAddition(aFlags) &&
235 EVENT_TYPE_EQUALS(listener, aEventMessage, aTypeAtom, aAllEvents) &&
236 listener->mListener == aListenerHolder) {
237 return;
241 mNoListenerForEvent = eVoidEvent;
242 mNoListenerForEventAtom = nullptr;
244 listener =
245 aAllEvents ? mListeners.InsertElementAt(0) : mListeners.AppendElement();
246 listener->mEventMessage = aEventMessage;
247 listener->mTypeAtom = aTypeAtom;
248 listener->mFlags = aFlags;
249 listener->mListenerIsHandler = aHandler;
250 listener->mHandlerIsString = false;
251 listener->mAllEvents = aAllEvents;
252 listener->mIsChrome =
253 mIsMainThreadELM && nsContentUtils::LegacyIsCallerChromeOrNativeCode();
255 // Detect the type of event listener.
256 if (aFlags.mListenerIsJSListener) {
257 MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback());
258 listener->mListenerType = Listener::eJSEventListener;
259 } else if (aListenerHolder.HasWebIDLCallback()) {
260 listener->mListenerType = Listener::eWebIDLListener;
261 } else {
262 listener->mListenerType = Listener::eNativeListener;
264 listener->mListener = std::move(aListenerHolder);
266 if (aSignal) {
267 listener->mSignalFollower = new ListenerSignalFollower(this, listener);
268 listener->mSignalFollower->Follow(aSignal);
271 if (aFlags.mInSystemGroup) {
272 mMayHaveSystemGroupListeners = true;
274 if (aFlags.mCapture) {
275 mMayHaveCapturingListeners = true;
278 // Events which are not supported in the running environment is mapped to
279 // eUnidentifiedEvent. Then, we need to consider the proper event message
280 // with comparing the atom.
282 EventMessage resolvedEventMessage = aEventMessage;
283 if (resolvedEventMessage == eUnidentifiedEvent && aTypeAtom->IsStatic()) {
284 // TouchEvents are registered only when
285 // nsContentUtils::InitializeTouchEventTable() is called.
286 if (aTypeAtom == nsGkAtoms::ontouchstart) {
287 resolvedEventMessage = eTouchStart;
288 } else if (aTypeAtom == nsGkAtoms::ontouchend) {
289 resolvedEventMessage = eTouchEnd;
290 } else if (aTypeAtom == nsGkAtoms::ontouchmove) {
291 resolvedEventMessage = eTouchMove;
292 } else if (aTypeAtom == nsGkAtoms::ontouchcancel) {
293 resolvedEventMessage = eTouchCancel;
297 switch (resolvedEventMessage) {
298 case eAfterPaint:
299 mMayHavePaintEventListener = true;
300 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
301 window->SetHasPaintEventListeners();
303 break;
304 case eLegacySubtreeModified:
305 case eLegacyNodeInserted:
306 case eLegacyNodeRemoved:
307 case eLegacyNodeRemovedFromDocument:
308 case eLegacyNodeInsertedIntoDocument:
309 case eLegacyAttrModified:
310 case eLegacyCharacterDataModified:
311 // For mutation listeners, we need to update the global bit on the DOM
312 // window. Otherwise we won't actually fire the mutation event.
313 mMayHaveMutationListeners = true;
314 // Go from our target to the nearest enclosing DOM window.
315 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
316 if (Document* doc = window->GetExtantDoc()) {
317 doc->WarnOnceAbout(DeprecatedOperations::eMutationEvent);
319 // If resolvedEventMessage is eLegacySubtreeModified, we need to
320 // listen all mutations. nsContentUtils::HasMutationListeners relies
321 // on this.
322 window->SetMutationListeners(
323 (resolvedEventMessage == eLegacySubtreeModified)
324 ? kAllMutationBits
325 : MutationBitForEventType(resolvedEventMessage));
327 break;
328 case ePointerEnter:
329 case ePointerLeave:
330 mMayHavePointerEnterLeaveEventListener = true;
331 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
332 NS_WARNING_ASSERTION(
333 !nsContentUtils::IsChromeDoc(window->GetExtantDoc()),
334 "Please do not use pointerenter/leave events in chrome. "
335 "They are slower than pointerover/out!");
336 window->SetHasPointerEnterLeaveEventListeners();
338 break;
339 case eGamepadButtonDown:
340 case eGamepadButtonUp:
341 case eGamepadAxisMove:
342 case eGamepadConnected:
343 case eGamepadDisconnected:
344 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
345 window->SetHasGamepadEventListener();
347 break;
348 case eDeviceOrientation:
349 case eDeviceOrientationAbsolute:
350 case eUserProximity:
351 case eDeviceLight:
352 case eDeviceMotion:
353 #if defined(MOZ_WIDGET_ANDROID)
354 case eOrientationChange:
355 #endif // #if defined(MOZ_WIDGET_ANDROID)
356 EnableDevice(resolvedEventMessage);
357 break;
358 case eTouchStart:
359 case eTouchEnd:
360 case eTouchMove:
361 case eTouchCancel:
362 mMayHaveTouchEventListener = true;
363 // we don't want touchevent listeners added by scrollbars to flip this
364 // flag so we ignore listeners created with system event flag
365 if (!aFlags.mInSystemGroup) {
366 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
367 window->SetHasTouchEventListeners();
370 break;
371 case eMouseEnter:
372 case eMouseLeave:
373 mMayHaveMouseEnterLeaveEventListener = true;
374 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
375 NS_WARNING_ASSERTION(
376 !nsContentUtils::IsChromeDoc(window->GetExtantDoc()),
377 "Please do not use mouseenter/leave events in chrome. "
378 "They are slower than mouseover/out!");
379 window->SetHasMouseEnterLeaveEventListeners();
381 break;
382 case eKeyDown:
383 case eKeyPress:
384 case eKeyUp:
385 if (!aFlags.mInSystemGroup) {
386 mMayHaveKeyEventListener = true;
388 break;
389 case eCompositionEnd:
390 case eCompositionStart:
391 case eCompositionUpdate:
392 case eEditorInput:
393 if (!aFlags.mInSystemGroup) {
394 mMayHaveInputOrCompositionEventListener = true;
396 break;
397 case eEditorBeforeInput:
398 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
399 window->SetHasBeforeInputEventListenersForTelemetry();
401 break;
402 case eSelectionChange:
403 mMayHaveSelectionChangeEventListener = true;
404 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
405 window->SetHasSelectionChangeEventListeners();
407 break;
408 case eFormSelect:
409 mMayHaveFormSelectEventListener = true;
410 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
411 window->SetHasFormSelectEventListeners();
413 break;
414 case eMarqueeStart:
415 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
416 if (Document* doc = window->GetExtantDoc()) {
417 doc->SetUseCounter(eUseCounter_custom_onstart);
420 break;
421 case eMarqueeBounce:
422 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
423 if (Document* doc = window->GetExtantDoc()) {
424 doc->SetUseCounter(eUseCounter_custom_onbounce);
427 break;
428 case eMarqueeFinish:
429 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
430 if (Document* doc = window->GetExtantDoc()) {
431 doc->SetUseCounter(eUseCounter_custom_onfinish);
434 break;
435 case eScrollPortOverflow:
436 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
437 if (Document* doc = window->GetExtantDoc()) {
438 doc->SetUseCounter(eUseCounter_custom_onoverflow);
441 break;
442 case eScrollPortUnderflow:
443 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
444 if (Document* doc = window->GetExtantDoc()) {
445 doc->SetUseCounter(eUseCounter_custom_onunderflow);
448 break;
449 case eLegacyMouseLineOrPageScroll:
450 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
451 if (Document* doc = window->GetExtantDoc()) {
452 doc->SetUseCounter(eUseCounter_custom_ondommousescroll);
455 break;
456 case eLegacyMousePixelScroll:
457 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
458 if (Document* doc = window->GetExtantDoc()) {
459 doc->SetUseCounter(eUseCounter_custom_onmozmousepixelscroll);
462 break;
463 case eTransitionStart:
464 case eTransitionRun:
465 case eTransitionEnd:
466 case eTransitionCancel:
467 case eWebkitTransitionEnd:
468 mMayHaveTransitionEventListener = true;
469 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
470 window->SetHasTransitionEventListeners();
472 break;
473 case eFormCheckboxStateChange:
474 nsContentUtils::SetMayHaveFormCheckboxStateChangeListeners();
475 break;
476 case eFormRadioStateChange:
477 nsContentUtils::SetMayHaveFormRadioStateChangeListeners();
478 break;
479 default:
480 // XXX Use NS_ASSERTION here to print resolvedEventMessage since
481 // MOZ_ASSERT can take only string literal, not pointer to
482 // characters.
483 NS_ASSERTION(
484 resolvedEventMessage < eLegacyMutationEventFirst ||
485 resolvedEventMessage > eLegacyMutationEventLast,
486 nsPrintfCString("You added new mutation event, but it's not "
487 "handled above, resolvedEventMessage=%s",
488 ToChar(resolvedEventMessage))
489 .get());
490 NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerenter,
491 nsPrintfCString("resolvedEventMessage=%s",
492 ToChar(resolvedEventMessage))
493 .get());
494 NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerleave,
495 nsPrintfCString("resolvedEventMessage=%s",
496 ToChar(resolvedEventMessage))
497 .get());
498 NS_ASSERTION(
499 resolvedEventMessage < eGamepadEventFirst ||
500 resolvedEventMessage > eGamepadEventLast,
501 nsPrintfCString("You added new gamepad event, but it's not "
502 "handled above, resolvedEventMessage=%s",
503 ToChar(resolvedEventMessage))
504 .get());
505 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientation,
506 nsPrintfCString("resolvedEventMessage=%s",
507 ToChar(resolvedEventMessage))
508 .get());
509 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientationabsolute,
510 nsPrintfCString("resolvedEventMessage=%s",
511 ToChar(resolvedEventMessage))
512 .get());
513 NS_ASSERTION(aTypeAtom != nsGkAtoms::onuserproximity,
514 nsPrintfCString("resolvedEventMessage=%s",
515 ToChar(resolvedEventMessage))
516 .get());
517 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicelight,
518 nsPrintfCString("resolvedEventMessage=%s",
519 ToChar(resolvedEventMessage))
520 .get());
521 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicemotion,
522 nsPrintfCString("resolvedEventMessage=%s",
523 ToChar(resolvedEventMessage))
524 .get());
525 #if defined(MOZ_WIDGET_ANDROID)
526 NS_ASSERTION(aTypeAtom != nsGkAtoms::onorientationchange,
527 nsPrintfCString("resolvedEventMessage=%s",
528 ToChar(resolvedEventMessage))
529 .get());
530 #endif // #if defined(MOZ_WIDGET_ANDROID)
531 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchstart,
532 nsPrintfCString("resolvedEventMessage=%s",
533 ToChar(resolvedEventMessage))
534 .get());
535 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchend,
536 nsPrintfCString("resolvedEventMessage=%s",
537 ToChar(resolvedEventMessage))
538 .get());
539 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchmove,
540 nsPrintfCString("resolvedEventMessage=%s",
541 ToChar(resolvedEventMessage))
542 .get());
543 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchcancel,
544 nsPrintfCString("resolvedEventMessage=%s",
545 ToChar(resolvedEventMessage))
546 .get());
547 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseenter,
548 nsPrintfCString("resolvedEventMessage=%s",
549 ToChar(resolvedEventMessage))
550 .get());
551 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseleave,
552 nsPrintfCString("resolvedEventMessage=%s",
553 ToChar(resolvedEventMessage))
554 .get());
555 NS_ASSERTION(aTypeAtom != nsGkAtoms::onkeydown,
556 nsPrintfCString("resolvedEventMessage=%s",
557 ToChar(resolvedEventMessage))
558 .get());
559 NS_ASSERTION(aTypeAtom != nsGkAtoms::onkeypress,
560 nsPrintfCString("resolvedEventMessage=%s",
561 ToChar(resolvedEventMessage))
562 .get());
563 NS_ASSERTION(aTypeAtom != nsGkAtoms::onkeyup,
564 nsPrintfCString("resolvedEventMessage=%s",
565 ToChar(resolvedEventMessage))
566 .get());
567 NS_ASSERTION(aTypeAtom != nsGkAtoms::oncompositionend,
568 nsPrintfCString("resolvedEventMessage=%s",
569 ToChar(resolvedEventMessage))
570 .get());
571 NS_ASSERTION(aTypeAtom != nsGkAtoms::oncompositionstart,
572 nsPrintfCString("resolvedEventMessage=%s",
573 ToChar(resolvedEventMessage))
574 .get());
575 NS_ASSERTION(aTypeAtom != nsGkAtoms::oncompositionupdate,
576 nsPrintfCString("resolvedEventMessage=%s",
577 ToChar(resolvedEventMessage))
578 .get());
579 NS_ASSERTION(aTypeAtom != nsGkAtoms::oninput,
580 nsPrintfCString("resolvedEventMessage=%s",
581 ToChar(resolvedEventMessage))
582 .get());
583 NS_ASSERTION(aTypeAtom != nsGkAtoms::onbeforeinput,
584 nsPrintfCString("resolvedEventMessage=%s",
585 ToChar(resolvedEventMessage))
586 .get());
587 NS_ASSERTION(aTypeAtom != nsGkAtoms::onselectionchange,
588 nsPrintfCString("resolvedEventMessage=%s",
589 ToChar(resolvedEventMessage))
590 .get());
591 NS_ASSERTION(aTypeAtom != nsGkAtoms::onselect,
592 nsPrintfCString("resolvedEventMessage=%s",
593 ToChar(resolvedEventMessage))
594 .get());
595 NS_ASSERTION(aTypeAtom != nsGkAtoms::onstart,
596 nsPrintfCString("resolvedEventMessage=%s",
597 ToChar(resolvedEventMessage))
598 .get());
599 NS_ASSERTION(aTypeAtom != nsGkAtoms::onbounce,
600 nsPrintfCString("resolvedEventMessage=%s",
601 ToChar(resolvedEventMessage))
602 .get());
603 NS_ASSERTION(aTypeAtom != nsGkAtoms::onfinish,
604 nsPrintfCString("resolvedEventMessage=%s",
605 ToChar(resolvedEventMessage))
606 .get());
607 NS_ASSERTION(aTypeAtom != nsGkAtoms::onoverflow,
608 nsPrintfCString("resolvedEventMessage=%s",
609 ToChar(resolvedEventMessage))
610 .get());
611 NS_ASSERTION(aTypeAtom != nsGkAtoms::onunderflow,
612 nsPrintfCString("resolvedEventMessage=%s",
613 ToChar(resolvedEventMessage))
614 .get());
615 NS_ASSERTION(aTypeAtom != nsGkAtoms::onDOMMouseScroll,
616 nsPrintfCString("resolvedEventMessage=%s",
617 ToChar(resolvedEventMessage))
618 .get());
619 NS_ASSERTION(aTypeAtom != nsGkAtoms::onMozMousePixelScroll,
620 nsPrintfCString("resolvedEventMessage=%s",
621 ToChar(resolvedEventMessage))
622 .get());
623 break;
627 if (IsApzAwareListener(listener)) {
628 ProcessApzAwareEventListenerAdd();
631 if (mTarget) {
632 mTarget->EventListenerAdded(aTypeAtom);
635 if (mIsMainThreadELM && mTarget) {
636 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
637 aTypeAtom);
640 if (!mHasNonPrivilegedClickListeners || mUnknownNonPrivilegedClickListeners) {
641 if (IsNonChromeClickListener(listener)) {
642 mHasNonPrivilegedClickListeners = true;
643 mUnknownNonPrivilegedClickListeners = false;
648 void EventListenerManager::ProcessApzAwareEventListenerAdd() {
649 Document* doc = nullptr;
651 // Mark the node as having apz aware listeners
652 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
653 node->SetMayBeApzAware();
654 doc = node->OwnerDoc();
657 // Schedule a paint so event regions on the layer tree gets updated
658 if (!doc) {
659 if (nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow()) {
660 doc = window->GetExtantDoc();
663 if (!doc) {
664 if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(mTarget)) {
665 if (nsPIDOMWindowInner* window = helper->GetOwner()) {
666 doc = window->GetExtantDoc();
671 if (doc && gfxPlatform::AsyncPanZoomEnabled()) {
672 PresShell* presShell = doc->GetPresShell();
673 if (presShell) {
674 nsIFrame* f = presShell->GetRootFrame();
675 if (f) {
676 f->SchedulePaint();
682 bool EventListenerManager::IsDeviceType(EventMessage aEventMessage) {
683 switch (aEventMessage) {
684 case eDeviceOrientation:
685 case eDeviceOrientationAbsolute:
686 case eDeviceMotion:
687 case eDeviceLight:
688 case eUserProximity:
689 #if defined(MOZ_WIDGET_ANDROID)
690 case eOrientationChange:
691 #endif
692 return true;
693 default:
694 break;
696 return false;
699 void EventListenerManager::EnableDevice(EventMessage aEventMessage) {
700 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
701 if (!window) {
702 return;
705 switch (aEventMessage) {
706 case eDeviceOrientation:
707 #ifdef MOZ_WIDGET_ANDROID
708 // Falls back to SENSOR_ROTATION_VECTOR and SENSOR_ORIENTATION if
709 // unavailable on device.
710 window->EnableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
711 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
712 #else
713 window->EnableDeviceSensor(SENSOR_ORIENTATION);
714 #endif
715 break;
716 case eDeviceOrientationAbsolute:
717 #ifdef MOZ_WIDGET_ANDROID
718 // Falls back to SENSOR_ORIENTATION if unavailable on device.
719 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
720 #else
721 window->EnableDeviceSensor(SENSOR_ORIENTATION);
722 #endif
723 break;
724 case eUserProximity:
725 window->EnableDeviceSensor(SENSOR_PROXIMITY);
726 break;
727 case eDeviceLight:
728 window->EnableDeviceSensor(SENSOR_LIGHT);
729 break;
730 case eDeviceMotion:
731 window->EnableDeviceSensor(SENSOR_ACCELERATION);
732 window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
733 window->EnableDeviceSensor(SENSOR_GYROSCOPE);
734 break;
735 #if defined(MOZ_WIDGET_ANDROID)
736 case eOrientationChange:
737 window->EnableOrientationChangeListener();
738 break;
739 #endif
740 default:
741 NS_WARNING("Enabling an unknown device sensor.");
742 break;
746 void EventListenerManager::DisableDevice(EventMessage aEventMessage) {
747 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
748 if (!window) {
749 return;
752 switch (aEventMessage) {
753 case eDeviceOrientation:
754 #ifdef MOZ_WIDGET_ANDROID
755 // Disable all potential fallback sensors.
756 window->DisableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
757 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
758 #endif
759 window->DisableDeviceSensor(SENSOR_ORIENTATION);
760 break;
761 case eDeviceOrientationAbsolute:
762 #ifdef MOZ_WIDGET_ANDROID
763 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
764 #endif
765 window->DisableDeviceSensor(SENSOR_ORIENTATION);
766 break;
767 case eDeviceMotion:
768 window->DisableDeviceSensor(SENSOR_ACCELERATION);
769 window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
770 window->DisableDeviceSensor(SENSOR_GYROSCOPE);
771 break;
772 case eUserProximity:
773 window->DisableDeviceSensor(SENSOR_PROXIMITY);
774 break;
775 case eDeviceLight:
776 window->DisableDeviceSensor(SENSOR_LIGHT);
777 break;
778 #if defined(MOZ_WIDGET_ANDROID)
779 case eOrientationChange:
780 window->DisableOrientationChangeListener();
781 break;
782 #endif
783 default:
784 NS_WARNING("Disabling an unknown device sensor.");
785 break;
789 void EventListenerManager::NotifyEventListenerRemoved(nsAtom* aUserType) {
790 // If the following code is changed, other callsites of EventListenerRemoved
791 // and NotifyAboutMainThreadListenerChange should be changed too.
792 mNoListenerForEvent = eVoidEvent;
793 mNoListenerForEventAtom = nullptr;
794 if (mTarget) {
795 mTarget->EventListenerRemoved(aUserType);
797 if (mIsMainThreadELM && mTarget) {
798 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
799 aUserType);
803 void EventListenerManager::RemoveEventListenerInternal(
804 EventListenerHolder aListenerHolder, EventMessage aEventMessage,
805 nsAtom* aUserType, const EventListenerFlags& aFlags, bool aAllEvents) {
806 if (!aListenerHolder || !aEventMessage || mClearingListeners) {
807 return;
810 Listener* listener;
812 uint32_t count = mListeners.Length();
813 bool deviceType = IsDeviceType(aEventMessage);
815 RefPtr<EventListenerManager> kungFuDeathGrip(this);
817 for (uint32_t i = 0; i < count; ++i) {
818 listener = &mListeners.ElementAt(i);
819 if (EVENT_TYPE_EQUALS(listener, aEventMessage, aUserType, aAllEvents)) {
820 if (listener->mListener == aListenerHolder &&
821 listener->mFlags.EqualsForRemoval(aFlags)) {
822 if (IsNonChromeClickListener(listener)) {
823 mUnknownNonPrivilegedClickListeners = true;
825 mListeners.RemoveElementAt(i);
826 NotifyEventListenerRemoved(aUserType);
827 if (!aAllEvents && deviceType) {
828 DisableDevice(aEventMessage);
830 return;
836 bool EventListenerManager::HasNonPrivilegedClickListeners() {
837 if (mUnknownNonPrivilegedClickListeners) {
838 Listener* listener;
840 mUnknownNonPrivilegedClickListeners = false;
841 for (uint32_t i = 0; i < mListeners.Length(); ++i) {
842 listener = &mListeners.ElementAt(i);
843 if (IsNonChromeClickListener(listener)) {
844 mHasNonPrivilegedClickListeners = true;
845 return mHasNonPrivilegedClickListeners;
848 mHasNonPrivilegedClickListeners = false;
850 return mHasNonPrivilegedClickListeners;
853 bool EventListenerManager::ListenerCanHandle(const Listener* aListener,
854 const WidgetEvent* aEvent,
855 EventMessage aEventMessage) const
858 MOZ_ASSERT(aEventMessage == aEvent->mMessage ||
859 aEventMessage == GetLegacyEventMessage(aEvent->mMessage),
860 "aEvent and aEventMessage should agree, modulo legacyness");
862 // The listener has been removed, it cannot handle anything.
863 if (aListener->mListenerType == Listener::eNoListener) {
864 return false;
867 // The listener has been disabled, for example by devtools.
868 if (!aListener->mEnabled) {
869 return false;
872 // This is slightly different from EVENT_TYPE_EQUALS in that it returns
873 // true even when aEvent->mMessage == eUnidentifiedEvent and
874 // aListener=>mEventMessage != eUnidentifiedEvent as long as the atoms are
875 // the same
876 if (MOZ_UNLIKELY(aListener->mAllEvents)) {
877 return true;
879 if (aEvent->mMessage == eUnidentifiedEvent) {
880 return aListener->mTypeAtom == aEvent->mSpecifiedEventType;
882 MOZ_ASSERT(mIsMainThreadELM);
883 return aListener->mEventMessage == aEventMessage;
886 static bool IsDefaultPassiveWhenOnRoot(EventMessage aMessage) {
887 if (aMessage == eTouchStart || aMessage == eTouchMove) {
888 return StaticPrefs::dom_event_default_to_passive_touch_listeners();
890 if (aMessage == eWheel || aMessage == eLegacyMouseLineOrPageScroll ||
891 aMessage == eLegacyMousePixelScroll) {
892 return StaticPrefs::dom_event_default_to_passive_wheel_listeners();
894 return false;
897 static bool IsRootEventTarget(EventTarget* aTarget) {
898 if (!aTarget) {
899 return false;
901 if (aTarget->IsInnerWindow()) {
902 return true;
904 const nsINode* node = nsINode::FromEventTarget(aTarget);
905 if (!node) {
906 return false;
908 Document* doc = node->OwnerDoc();
909 return node == doc || node == doc->GetRootElement() || node == doc->GetBody();
912 void EventListenerManager::MaybeMarkPassive(EventMessage aMessage,
913 EventListenerFlags& aFlags) {
914 if (!mIsMainThreadELM) {
915 return;
917 if (!IsDefaultPassiveWhenOnRoot(aMessage)) {
918 return;
920 if (!IsRootEventTarget(mTarget)) {
921 return;
923 aFlags.mPassive = true;
926 void EventListenerManager::AddEventListenerByType(
927 EventListenerHolder aListenerHolder, const nsAString& aType,
928 const EventListenerFlags& aFlags, const Optional<bool>& aPassive,
929 AbortSignal* aSignal) {
930 RefPtr<nsAtom> atom;
931 EventMessage message =
932 GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
934 EventListenerFlags flags = aFlags;
935 if (aPassive.WasPassed()) {
936 flags.mPassive = aPassive.Value();
937 } else {
938 MaybeMarkPassive(message, flags);
941 AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags,
942 false, false, aSignal);
945 void EventListenerManager::RemoveEventListenerByType(
946 EventListenerHolder aListenerHolder, const nsAString& aType,
947 const EventListenerFlags& aFlags) {
948 RefPtr<nsAtom> atom;
949 EventMessage message =
950 GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
951 RemoveEventListenerInternal(std::move(aListenerHolder), message, atom,
952 aFlags);
955 EventListenerManager::Listener* EventListenerManager::FindEventHandler(
956 EventMessage aEventMessage, nsAtom* aTypeAtom) {
957 // Run through the listeners for this type and see if a script
958 // listener is registered
959 Listener* listener;
960 uint32_t count = mListeners.Length();
961 for (uint32_t i = 0; i < count; ++i) {
962 listener = &mListeners.ElementAt(i);
963 if (listener->mListenerIsHandler &&
964 EVENT_TYPE_EQUALS(listener, aEventMessage, aTypeAtom, false)) {
965 return listener;
968 return nullptr;
971 EventListenerManager::Listener* EventListenerManager::SetEventHandlerInternal(
972 nsAtom* aName, const TypedEventHandler& aTypedHandler,
973 bool aPermitUntrustedEvents) {
974 MOZ_ASSERT(aName);
976 EventMessage eventMessage = GetEventMessage(aName);
977 Listener* listener = FindEventHandler(eventMessage, aName);
979 if (!listener) {
980 // If we didn't find a script listener or no listeners existed
981 // create and add a new one.
982 EventListenerFlags flags;
983 flags.mListenerIsJSListener = true;
984 MaybeMarkPassive(eventMessage, flags);
986 nsCOMPtr<JSEventHandler> jsEventHandler;
987 NS_NewJSEventHandler(mTarget, aName, aTypedHandler,
988 getter_AddRefs(jsEventHandler));
989 AddEventListenerInternal(EventListenerHolder(jsEventHandler), eventMessage,
990 aName, flags, true);
992 listener = FindEventHandler(eventMessage, aName);
993 } else {
994 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
995 MOZ_ASSERT(jsEventHandler,
996 "How can we have an event handler with no JSEventHandler?");
998 bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler;
999 // Possibly the same listener, but update still the context and scope.
1000 jsEventHandler->SetHandler(aTypedHandler);
1001 if (mTarget && !same) {
1002 mTarget->EventListenerRemoved(aName);
1003 mTarget->EventListenerAdded(aName);
1005 if (mIsMainThreadELM && mTarget) {
1006 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, aName);
1010 // Set flag to indicate possible need for compilation later
1011 listener->mHandlerIsString = !aTypedHandler.HasEventHandler();
1012 if (aPermitUntrustedEvents) {
1013 listener->mFlags.mAllowUntrustedEvents = true;
1016 return listener;
1019 nsresult EventListenerManager::SetEventHandler(nsAtom* aName,
1020 const nsAString& aBody,
1021 bool aDeferCompilation,
1022 bool aPermitUntrustedEvents,
1023 Element* aElement) {
1024 auto removeEventHandler = MakeScopeExit([&] { RemoveEventHandler(aName); });
1026 nsCOMPtr<Document> doc;
1027 nsCOMPtr<nsIScriptGlobalObject> global =
1028 GetScriptGlobalAndDocument(getter_AddRefs(doc));
1030 if (!global) {
1031 // This can happen; for example this document might have been
1032 // loaded as data.
1033 return NS_OK;
1036 nsresult rv = NS_OK;
1037 // return early preventing the event listener from being added
1038 // 'doc' is fetched above
1039 if (doc) {
1040 // Don't allow adding an event listener if the document is sandboxed
1041 // without 'allow-scripts'.
1042 if (doc->HasScriptsBlockedBySandbox()) {
1043 return NS_ERROR_DOM_SECURITY_ERR;
1046 // Perform CSP check
1047 nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
1048 unsigned lineNum = 0;
1049 unsigned columnNum = 0;
1051 JSContext* cx = nsContentUtils::GetCurrentJSContext();
1052 if (cx && !JS::DescribeScriptedCaller(cx, nullptr, &lineNum, &columnNum)) {
1053 JS_ClearPendingException(cx);
1056 if (csp) {
1057 bool allowsInlineScript = true;
1058 rv = csp->GetAllowsInline(
1059 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE,
1060 true, // aHasUnsafeHash
1061 u""_ns, // aNonce
1062 true, // aParserCreated (true because attribute event handler)
1063 aElement,
1064 nullptr, // nsICSPEventListener
1065 aBody, lineNum, columnNum, &allowsInlineScript);
1066 NS_ENSURE_SUCCESS(rv, rv);
1068 // return early if CSP wants us to block inline scripts
1069 if (!allowsInlineScript) {
1070 return NS_OK;
1075 // This might be the first reference to this language in the global
1076 // We must init the language before we attempt to fetch its context.
1077 if (NS_FAILED(global->EnsureScriptEnvironment())) {
1078 NS_WARNING("Failed to setup script environment for this language");
1079 // but fall through and let the inevitable failure below handle it.
1082 nsIScriptContext* context = global->GetScriptContext();
1083 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
1084 NS_ENSURE_STATE(global->HasJSGlobal());
1086 removeEventHandler.release();
1088 Listener* listener = SetEventHandlerInternal(aName, TypedEventHandler(),
1089 aPermitUntrustedEvents);
1091 if (!aDeferCompilation) {
1092 return CompileEventHandlerInternal(listener, &aBody, aElement);
1095 return NS_OK;
1098 void EventListenerManager::RemoveEventHandler(nsAtom* aName) {
1099 if (mClearingListeners) {
1100 return;
1103 EventMessage eventMessage = GetEventMessage(aName);
1104 Listener* listener = FindEventHandler(eventMessage, aName);
1106 if (listener) {
1107 if (IsNonChromeClickListener(listener)) {
1108 mUnknownNonPrivilegedClickListeners = true;
1110 mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0)));
1111 NotifyEventListenerRemoved(aName);
1112 if (IsDeviceType(eventMessage)) {
1113 DisableDevice(eventMessage);
1118 bool EventListenerManager::IsNonChromeClickListener(Listener* aListener) {
1119 return !aListener->mFlags.mInSystemGroup && !aListener->mIsChrome &&
1120 aListener->mEventMessage == eMouseClick &&
1121 (aListener->GetJSEventHandler() ||
1122 aListener->mListener.HasWebIDLCallback());
1125 nsresult EventListenerManager::CompileEventHandlerInternal(
1126 Listener* aListener, const nsAString* aBody, Element* aElement) {
1127 MOZ_ASSERT(aListener->GetJSEventHandler());
1128 MOZ_ASSERT(aListener->mHandlerIsString,
1129 "Why are we compiling a non-string JS listener?");
1130 JSEventHandler* jsEventHandler = aListener->GetJSEventHandler();
1131 MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(),
1132 "What is there to compile?");
1134 nsresult result = NS_OK;
1135 nsCOMPtr<Document> doc;
1136 nsCOMPtr<nsIScriptGlobalObject> global =
1137 GetScriptGlobalAndDocument(getter_AddRefs(doc));
1138 NS_ENSURE_STATE(global);
1140 // Activate JSAPI, and make sure that exceptions are reported on the right
1141 // Window.
1142 AutoJSAPI jsapi;
1143 if (NS_WARN_IF(!jsapi.Init(global))) {
1144 return NS_ERROR_UNEXPECTED;
1146 JSContext* cx = jsapi.cx();
1148 RefPtr<nsAtom> typeAtom = aListener->mTypeAtom;
1149 nsAtom* attrName = typeAtom;
1151 // Flag us as not a string so we don't keep trying to compile strings which
1152 // can't be compiled.
1153 aListener->mHandlerIsString = false;
1155 // mTarget may not be an Element if it's a window and we're
1156 // getting an inline event listener forwarded from <html:body> or
1157 // <html:frameset> or <xul:window> or the like.
1158 // XXX I don't like that we have to reference content from
1159 // here. The alternative is to store the event handler string on
1160 // the JSEventHandler itself, and that still doesn't address
1161 // the arg names issue.
1162 RefPtr<Element> element = Element::FromEventTargetOrNull(mTarget);
1163 MOZ_ASSERT(element || aBody, "Where will we get our body?");
1164 nsAutoString handlerBody;
1165 const nsAString* body = aBody;
1166 if (!aBody) {
1167 if (aListener->mTypeAtom == nsGkAtoms::onSVGLoad) {
1168 attrName = nsGkAtoms::onload;
1169 } else if (aListener->mTypeAtom == nsGkAtoms::onSVGScroll) {
1170 attrName = nsGkAtoms::onscroll;
1171 } else if (aListener->mTypeAtom == nsGkAtoms::onbeginEvent) {
1172 attrName = nsGkAtoms::onbegin;
1173 } else if (aListener->mTypeAtom == nsGkAtoms::onrepeatEvent) {
1174 attrName = nsGkAtoms::onrepeat;
1175 } else if (aListener->mTypeAtom == nsGkAtoms::onendEvent) {
1176 attrName = nsGkAtoms::onend;
1177 } else if (aListener->mTypeAtom == nsGkAtoms::onwebkitAnimationEnd) {
1178 attrName = nsGkAtoms::onwebkitanimationend;
1179 } else if (aListener->mTypeAtom == nsGkAtoms::onwebkitAnimationIteration) {
1180 attrName = nsGkAtoms::onwebkitanimationiteration;
1181 } else if (aListener->mTypeAtom == nsGkAtoms::onwebkitAnimationStart) {
1182 attrName = nsGkAtoms::onwebkitanimationstart;
1183 } else if (aListener->mTypeAtom == nsGkAtoms::onwebkitTransitionEnd) {
1184 attrName = nsGkAtoms::onwebkittransitionend;
1187 element->GetAttr(kNameSpaceID_None, attrName, handlerBody);
1188 body = &handlerBody;
1189 aElement = element;
1191 aListener = nullptr;
1193 nsAutoCString url("-moz-evil:lying-event-listener"_ns);
1194 MOZ_ASSERT(body);
1195 MOZ_ASSERT(aElement);
1196 nsIURI* uri = aElement->OwnerDoc()->GetDocumentURI();
1197 if (uri) {
1198 uri->GetSpec(url);
1201 nsCOMPtr<nsPIDOMWindowInner> win =
1202 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget);
1203 uint32_t argCount;
1204 const char** argNames;
1205 nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), typeAtom, win,
1206 &argCount, &argNames);
1208 // Wrap the event target, so that we can use it as the scope for the event
1209 // handler. Note that mTarget is different from aElement in the <body> case,
1210 // where mTarget is a Window.
1212 // The wrapScope doesn't really matter here, because the target will create
1213 // its reflector in the proper scope, and then we'll enter that realm.
1214 JS::Rooted<JSObject*> wrapScope(cx, global->GetGlobalJSObject());
1215 JS::Rooted<JS::Value> v(cx);
1217 JSAutoRealm ar(cx, wrapScope);
1218 nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v,
1219 /* aAllowWrapping = */ false);
1220 if (NS_WARN_IF(NS_FAILED(rv))) {
1221 return rv;
1225 JS::Rooted<JSObject*> target(cx, &v.toObject());
1226 JSAutoRealm ar(cx, target);
1228 // Now that we've entered the realm we actually care about, create our
1229 // scope chain. Note that we start with |element|, not aElement, because
1230 // mTarget is different from aElement in the <body> case, where mTarget is a
1231 // Window, and in that case we do not want the scope chain to include the body
1232 // or the document.
1233 JS::RootedVector<JSObject*> scopeChain(cx);
1234 if (!nsJSUtils::GetScopeChainForElement(cx, element, &scopeChain)) {
1235 return NS_ERROR_OUT_OF_MEMORY;
1238 nsDependentAtomString str(attrName);
1239 // Most of our names are short enough that we don't even have to malloc
1240 // the JS string stuff, so don't worry about playing games with
1241 // refcounting XPCOM stringbuffers.
1242 JS::Rooted<JSString*> jsStr(
1243 cx, JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length()));
1244 NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY);
1246 // Get the reflector for |aElement|, so that we can pass to setElement.
1247 if (NS_WARN_IF(!GetOrCreateDOMReflector(cx, aElement, &v))) {
1248 return NS_ERROR_FAILURE;
1251 RefPtr<JS::loader::ScriptFetchOptions> fetchOptions =
1252 new JS::loader::ScriptFetchOptions(
1253 CORS_NONE, aElement->OwnerDoc()->GetReferrerPolicy(),
1254 aElement->OwnerDoc()->NodePrincipal());
1256 RefPtr<JS::loader::EventScript> eventScript =
1257 new JS::loader::EventScript(fetchOptions, uri);
1259 JS::CompileOptions options(cx);
1260 // Use line 0 to make the function body starts from line 1.
1261 options.setIntroductionType("eventHandler")
1262 .setFileAndLine(url.get(), 0)
1263 .setDeferDebugMetadata(true);
1265 JS::Rooted<JSObject*> handler(cx);
1266 result = nsJSUtils::CompileFunction(jsapi, scopeChain, options,
1267 nsAtomCString(typeAtom), argCount,
1268 argNames, *body, handler.address());
1269 NS_ENSURE_SUCCESS(result, result);
1270 NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
1272 JS::Rooted<JS::Value> privateValue(cx, JS::PrivateValue(eventScript));
1273 result = nsJSUtils::UpdateFunctionDebugMetadata(jsapi, handler, options,
1274 jsStr, privateValue);
1275 NS_ENSURE_SUCCESS(result, result);
1277 MOZ_ASSERT(js::IsObjectInContextCompartment(handler, cx));
1278 JS::Rooted<JSObject*> handlerGlobal(cx, JS::CurrentGlobalOrNull(cx));
1280 if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) {
1281 RefPtr<OnErrorEventHandlerNonNull> handlerCallback =
1282 new OnErrorEventHandlerNonNull(static_cast<JSContext*>(nullptr),
1283 handler, handlerGlobal,
1284 /* aIncumbentGlobal = */ nullptr);
1285 jsEventHandler->SetHandler(handlerCallback);
1286 } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) {
1287 RefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
1288 new OnBeforeUnloadEventHandlerNonNull(static_cast<JSContext*>(nullptr),
1289 handler, handlerGlobal,
1290 /* aIncumbentGlobal = */ nullptr);
1291 jsEventHandler->SetHandler(handlerCallback);
1292 } else {
1293 RefPtr<EventHandlerNonNull> handlerCallback = new EventHandlerNonNull(
1294 static_cast<JSContext*>(nullptr), handler, handlerGlobal,
1295 /* aIncumbentGlobal = */ nullptr);
1296 jsEventHandler->SetHandler(handlerCallback);
1299 return result;
1302 nsresult EventListenerManager::HandleEventSubType(Listener* aListener,
1303 Event* aDOMEvent,
1304 EventTarget* aCurrentTarget) {
1305 nsresult result = NS_OK;
1306 // strong ref
1307 EventListenerHolder listenerHolder(aListener->mListener.Clone());
1309 // If this is a script handler and we haven't yet
1310 // compiled the event handler itself
1311 if ((aListener->mListenerType == Listener::eJSEventListener) &&
1312 aListener->mHandlerIsString) {
1313 result = CompileEventHandlerInternal(aListener, nullptr, nullptr);
1314 aListener = nullptr;
1317 if (NS_SUCCEEDED(result)) {
1318 Maybe<EventCallbackDebuggerNotificationGuard> dbgGuard;
1319 if (dom::ChromeUtils::IsDevToolsOpened()) {
1320 dbgGuard.emplace(aCurrentTarget, aDOMEvent);
1322 nsAutoMicroTask mt;
1324 // Event::currentTarget is set in EventDispatcher.
1325 if (listenerHolder.HasWebIDLCallback()) {
1326 ErrorResult rv;
1327 listenerHolder.GetWebIDLCallback()->HandleEvent(aCurrentTarget,
1328 *aDOMEvent, rv);
1329 result = rv.StealNSResult();
1330 } else {
1331 // listenerHolder is holding a stack ref here.
1332 result = MOZ_KnownLive(listenerHolder.GetXPCOMCallback())
1333 ->HandleEvent(aDOMEvent);
1337 return result;
1340 EventMessage EventListenerManager::GetLegacyEventMessage(
1341 EventMessage aEventMessage) const {
1342 // webkit-prefixed legacy events:
1343 if (aEventMessage == eTransitionEnd) {
1344 return eWebkitTransitionEnd;
1346 if (aEventMessage == eAnimationStart) {
1347 return eWebkitAnimationStart;
1349 if (aEventMessage == eAnimationEnd) {
1350 return eWebkitAnimationEnd;
1352 if (aEventMessage == eAnimationIteration) {
1353 return eWebkitAnimationIteration;
1356 switch (aEventMessage) {
1357 case eFullscreenChange:
1358 return eMozFullscreenChange;
1359 case eFullscreenError:
1360 return eMozFullscreenError;
1361 default:
1362 return aEventMessage;
1366 EventMessage EventListenerManager::GetEventMessage(nsAtom* aEventName) const {
1367 if (mIsMainThreadELM) {
1368 return nsContentUtils::GetEventMessage(aEventName);
1371 // The nsContentUtils event message hashtables aren't threadsafe, so just fall
1372 // back to eUnidentifiedEvent.
1373 return eUnidentifiedEvent;
1376 EventMessage EventListenerManager::GetEventMessageAndAtomForListener(
1377 const nsAString& aType, nsAtom** aAtom) {
1378 if (mIsMainThreadELM) {
1379 return nsContentUtils::GetEventMessageAndAtomForListener(aType, aAtom);
1382 *aAtom = NS_Atomize(u"on"_ns + aType).take();
1383 return eUnidentifiedEvent;
1386 already_AddRefed<nsPIDOMWindowInner> EventListenerManager::WindowFromListener(
1387 Listener* aListener, bool aItemInShadowTree) {
1388 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
1389 if (!aItemInShadowTree) {
1390 if (aListener->mListener.HasWebIDLCallback()) {
1391 CallbackObject* callback = aListener->mListener.GetWebIDLCallback();
1392 nsIGlobalObject* global = nullptr;
1393 if (callback) {
1394 global = callback->IncumbentGlobalOrNull();
1396 if (global) {
1397 innerWindow = global->AsInnerWindow(); // Can be nullptr
1399 } else {
1400 // Can't get the global from
1401 // listener->mListener.GetXPCOMCallback().
1402 // In most cases, it would be the same as for
1403 // the target, so let's do that.
1404 innerWindow = GetInnerWindowForTarget(); // Can be nullptr
1407 return innerWindow.forget();
1411 * Causes a check for event listeners and processing by them if they exist.
1412 * @param an event listener
1415 void EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
1416 WidgetEvent* aEvent,
1417 Event** aDOMEvent,
1418 EventTarget* aCurrentTarget,
1419 nsEventStatus* aEventStatus,
1420 bool aItemInShadowTree) {
1421 // Set the value of the internal PreventDefault flag properly based on
1422 // aEventStatus
1423 if (!aEvent->DefaultPrevented() &&
1424 *aEventStatus == nsEventStatus_eConsumeNoDefault) {
1425 // Assume that if only aEventStatus claims that the event has already been
1426 // consumed, the consumer is default event handler.
1427 aEvent->PreventDefault();
1430 Maybe<AutoHandlingUserInputStatePusher> userInputStatePusher;
1431 Maybe<AutoPopupStatePusher> popupStatePusher;
1432 if (mIsMainThreadELM) {
1433 userInputStatePusher.emplace(UserActivation::IsUserInteractionEvent(aEvent),
1434 aEvent);
1435 popupStatePusher.emplace(
1436 PopupBlocker::GetEventPopupControlState(aEvent, *aDOMEvent));
1439 bool hasListener = false;
1440 bool hasListenerForCurrentGroup = false;
1441 bool usingLegacyMessage = false;
1442 bool hasRemovedListener = false;
1443 EventMessage eventMessage = aEvent->mMessage;
1445 while (true) {
1446 Maybe<EventMessageAutoOverride> legacyAutoOverride;
1447 for (Listener& listenerRef : mListeners.EndLimitedRange()) {
1448 if (aEvent->mFlags.mImmediatePropagationStopped) {
1449 break;
1451 Listener* listener = &listenerRef;
1452 // Check that the phase is same in event and event listener.
1453 // Handle only trusted events, except when listener permits untrusted
1454 // events.
1455 if (ListenerCanHandle(listener, aEvent, eventMessage)) {
1456 hasListener = true;
1457 hasListenerForCurrentGroup =
1458 hasListenerForCurrentGroup ||
1459 listener->mFlags.mInSystemGroup == aEvent->mFlags.mInSystemGroup;
1460 if (listener->IsListening(aEvent) &&
1461 (aEvent->IsTrusted() || listener->mFlags.mAllowUntrustedEvents)) {
1462 if (!*aDOMEvent) {
1463 // This is tiny bit slow, but happens only once per event.
1464 // Similar code also in EventDispatcher.
1465 nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget;
1466 RefPtr<Event> event =
1467 EventDispatcher::CreateEvent(et, aPresContext, aEvent, u""_ns);
1468 event.forget(aDOMEvent);
1470 if (*aDOMEvent) {
1471 if (!aEvent->mCurrentTarget) {
1472 aEvent->mCurrentTarget = aCurrentTarget->GetTargetForDOMEvent();
1473 if (!aEvent->mCurrentTarget) {
1474 break;
1477 if (usingLegacyMessage && !legacyAutoOverride) {
1478 // Override the aDOMEvent's event-message (its .type) until we
1479 // finish traversing listeners (when legacyAutoOverride destructs)
1480 legacyAutoOverride.emplace(*aDOMEvent, eventMessage);
1483 aEvent->mFlags.mInPassiveListener = listener->mFlags.mPassive;
1484 Maybe<Listener> listenerHolder;
1485 if (listener->mFlags.mOnce) {
1486 // Move the listener to the stack before handling the event.
1487 // The order is important, otherwise the listener could be
1488 // called again inside the listener.
1489 listenerHolder.emplace(std::move(*listener));
1490 listener = listenerHolder.ptr();
1491 hasRemovedListener = true;
1494 nsCOMPtr<nsPIDOMWindowInner> innerWindow =
1495 WindowFromListener(listener, aItemInShadowTree);
1496 mozilla::dom::Event* oldWindowEvent = nullptr;
1497 if (innerWindow) {
1498 oldWindowEvent = innerWindow->SetEvent(*aDOMEvent);
1501 nsresult rv =
1502 HandleEventSubType(listener, *aDOMEvent, aCurrentTarget);
1504 if (innerWindow) {
1505 Unused << innerWindow->SetEvent(oldWindowEvent);
1508 if (NS_FAILED(rv)) {
1509 aEvent->mFlags.mExceptionWasRaised = true;
1511 aEvent->mFlags.mInPassiveListener = false;
1517 // If we didn't find any matching listeners, and our event has a legacy
1518 // version, we'll now switch to looking for that legacy version and we'll
1519 // recheck our listeners.
1520 if (hasListenerForCurrentGroup || usingLegacyMessage ||
1521 !aEvent->IsTrusted()) {
1522 // No need to recheck listeners, because we already found a match, we
1523 // already rechecked them, or it is not a trusted event.
1524 break;
1526 EventMessage legacyEventMessage = GetLegacyEventMessage(eventMessage);
1527 if (legacyEventMessage == eventMessage) {
1528 break; // There's no legacy version of our event; no need to recheck.
1530 MOZ_ASSERT(
1531 GetLegacyEventMessage(legacyEventMessage) == legacyEventMessage,
1532 "Legacy event messages should not themselves have legacy versions");
1534 // Recheck our listeners, using the legacy event message we just looked up:
1535 eventMessage = legacyEventMessage;
1536 usingLegacyMessage = true;
1539 aEvent->mCurrentTarget = nullptr;
1541 if (hasRemovedListener) {
1542 // If there are any once listeners replaced with a placeholder in
1543 // the loop above, we need to clean up them here. Note that, this
1544 // could clear once listeners handled in some outer level as well,
1545 // but that should not affect the result.
1546 mListeners.NonObservingRemoveElementsBy([](const Listener& aListener) {
1547 return aListener.mListenerType == Listener::eNoListener;
1549 NotifyEventListenerRemoved(aEvent->mSpecifiedEventType);
1550 if (IsDeviceType(aEvent->mMessage)) {
1551 // This is a device-type event, we need to check whether we can
1552 // disable device after removing the once listeners.
1553 const auto [begin, end] = mListeners.NonObservingRange();
1554 const bool hasAnyListener =
1555 std::any_of(begin, end, [aEvent](const Listener& listenerRef) {
1556 const Listener* listener = &listenerRef;
1557 return EVENT_TYPE_EQUALS(listener, aEvent->mMessage,
1558 aEvent->mSpecifiedEventType,
1559 /* all events */ false);
1562 if (!hasAnyListener) {
1563 DisableDevice(aEvent->mMessage);
1568 if (mIsMainThreadELM && !hasListener) {
1569 mNoListenerForEvent = aEvent->mMessage;
1570 mNoListenerForEventAtom = aEvent->mSpecifiedEventType;
1573 if (aEvent->DefaultPrevented()) {
1574 *aEventStatus = nsEventStatus_eConsumeNoDefault;
1578 void EventListenerManager::Disconnect() {
1579 mTarget = nullptr;
1580 RemoveAllListenersSilently();
1583 void EventListenerManager::AddEventListener(const nsAString& aType,
1584 EventListenerHolder aListenerHolder,
1585 bool aUseCapture,
1586 bool aWantsUntrusted) {
1587 EventListenerFlags flags;
1588 flags.mCapture = aUseCapture;
1589 flags.mAllowUntrustedEvents = aWantsUntrusted;
1590 return AddEventListenerByType(std::move(aListenerHolder), aType, flags);
1593 void EventListenerManager::AddEventListener(
1594 const nsAString& aType, EventListenerHolder aListenerHolder,
1595 const dom::AddEventListenerOptionsOrBoolean& aOptions,
1596 bool aWantsUntrusted) {
1597 EventListenerFlags flags;
1598 Optional<bool> passive;
1599 AbortSignal* signal = nullptr;
1600 if (aOptions.IsBoolean()) {
1601 flags.mCapture = aOptions.GetAsBoolean();
1602 } else {
1603 const auto& options = aOptions.GetAsAddEventListenerOptions();
1604 flags.mCapture = options.mCapture;
1605 flags.mInSystemGroup = options.mMozSystemGroup;
1606 flags.mOnce = options.mOnce;
1607 if (options.mPassive.WasPassed()) {
1608 passive.Construct(options.mPassive.Value());
1611 if (options.mSignal.WasPassed()) {
1612 signal = &options.mSignal.Value();
1616 flags.mAllowUntrustedEvents = aWantsUntrusted;
1617 return AddEventListenerByType(std::move(aListenerHolder), aType, flags,
1618 passive, signal);
1621 void EventListenerManager::RemoveEventListener(
1622 const nsAString& aType, EventListenerHolder aListenerHolder,
1623 bool aUseCapture) {
1624 EventListenerFlags flags;
1625 flags.mCapture = aUseCapture;
1626 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
1629 void EventListenerManager::RemoveEventListener(
1630 const nsAString& aType, EventListenerHolder aListenerHolder,
1631 const dom::EventListenerOptionsOrBoolean& aOptions) {
1632 EventListenerFlags flags;
1633 if (aOptions.IsBoolean()) {
1634 flags.mCapture = aOptions.GetAsBoolean();
1635 } else {
1636 const auto& options = aOptions.GetAsEventListenerOptions();
1637 flags.mCapture = options.mCapture;
1638 flags.mInSystemGroup = options.mMozSystemGroup;
1640 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
1643 void EventListenerManager::AddListenerForAllEvents(EventListener* aDOMListener,
1644 bool aUseCapture,
1645 bool aWantsUntrusted,
1646 bool aSystemEventGroup) {
1647 EventListenerFlags flags;
1648 flags.mCapture = aUseCapture;
1649 flags.mAllowUntrustedEvents = aWantsUntrusted;
1650 flags.mInSystemGroup = aSystemEventGroup;
1651 AddEventListenerInternal(EventListenerHolder(aDOMListener), eAllEvents,
1652 nullptr, flags, false, true);
1655 void EventListenerManager::RemoveListenerForAllEvents(
1656 EventListener* aDOMListener, bool aUseCapture, bool aSystemEventGroup) {
1657 EventListenerFlags flags;
1658 flags.mCapture = aUseCapture;
1659 flags.mInSystemGroup = aSystemEventGroup;
1660 RemoveEventListenerInternal(EventListenerHolder(aDOMListener), eAllEvents,
1661 nullptr, flags, true);
1664 bool EventListenerManager::HasMutationListeners() {
1665 if (mMayHaveMutationListeners) {
1666 uint32_t count = mListeners.Length();
1667 for (uint32_t i = 0; i < count; ++i) {
1668 Listener* listener = &mListeners.ElementAt(i);
1669 if (listener->mEventMessage >= eLegacyMutationEventFirst &&
1670 listener->mEventMessage <= eLegacyMutationEventLast) {
1671 return true;
1676 return false;
1679 uint32_t EventListenerManager::MutationListenerBits() {
1680 uint32_t bits = 0;
1681 if (mMayHaveMutationListeners) {
1682 uint32_t count = mListeners.Length();
1683 for (uint32_t i = 0; i < count; ++i) {
1684 Listener* listener = &mListeners.ElementAt(i);
1685 if (listener->mEventMessage >= eLegacyMutationEventFirst &&
1686 listener->mEventMessage <= eLegacyMutationEventLast) {
1687 if (listener->mEventMessage == eLegacySubtreeModified) {
1688 return kAllMutationBits;
1690 bits |= MutationBitForEventType(listener->mEventMessage);
1694 return bits;
1697 bool EventListenerManager::HasListenersFor(const nsAString& aEventName) const {
1698 RefPtr<nsAtom> atom = NS_Atomize(u"on"_ns + aEventName);
1699 return HasListenersFor(atom);
1702 bool EventListenerManager::HasListenersFor(nsAtom* aEventNameWithOn) const {
1703 return HasListenersForInternal(aEventNameWithOn, false);
1706 bool EventListenerManager::HasNonSystemGroupListenersFor(
1707 nsAtom* aEventNameWithOn) const {
1708 return HasListenersForInternal(aEventNameWithOn, true);
1711 bool EventListenerManager::HasListenersForInternal(
1712 nsAtom* aEventNameWithOn, bool aIgnoreSystemGroup) const {
1713 #ifdef DEBUG
1714 nsAutoString name;
1715 aEventNameWithOn->ToString(name);
1716 #endif
1717 NS_ASSERTION(StringBeginsWith(name, u"on"_ns),
1718 "Event name does not start with 'on'");
1719 uint32_t count = mListeners.Length();
1720 for (uint32_t i = 0; i < count; ++i) {
1721 const Listener* listener = &mListeners.ElementAt(i);
1722 if (listener->mTypeAtom == aEventNameWithOn) {
1723 if (aIgnoreSystemGroup && listener->mFlags.mInSystemGroup) {
1724 continue;
1726 return true;
1729 return false;
1732 bool EventListenerManager::HasListeners() const {
1733 return !mListeners.IsEmpty();
1736 nsresult EventListenerManager::GetListenerInfo(
1737 nsTArray<RefPtr<nsIEventListenerInfo>>& aList) {
1738 nsCOMPtr<EventTarget> target = mTarget;
1739 NS_ENSURE_STATE(target);
1740 aList.Clear();
1741 for (const Listener& listener : mListeners.ForwardRange()) {
1742 // If this is a script handler and we haven't yet
1743 // compiled the event handler itself go ahead and compile it
1744 if (listener.mListenerType == Listener::eJSEventListener &&
1745 listener.mHandlerIsString) {
1746 CompileEventHandlerInternal(const_cast<Listener*>(&listener), nullptr,
1747 nullptr);
1749 nsAutoString eventType;
1750 if (listener.mAllEvents) {
1751 eventType.SetIsVoid(true);
1752 } else if (listener.mListenerType == Listener::eNoListener) {
1753 continue;
1754 } else {
1755 eventType.Assign(Substring(nsDependentAtomString(listener.mTypeAtom), 2));
1758 JS::Rooted<JSObject*> callback(RootingCx());
1759 JS::Rooted<JSObject*> callbackGlobal(RootingCx());
1760 if (JSEventHandler* handler = listener.GetJSEventHandler()) {
1761 if (handler->GetTypedEventHandler().HasEventHandler()) {
1762 CallbackFunction* callbackFun = handler->GetTypedEventHandler().Ptr();
1763 callback = callbackFun->CallableOrNull();
1764 callbackGlobal = callbackFun->CallbackGlobalOrNull();
1765 if (!callback) {
1766 // This will be null for cross-compartment event listeners
1767 // which have been destroyed.
1768 continue;
1771 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1772 EventListener* listenerCallback = listener.mListener.GetWebIDLCallback();
1773 callback = listenerCallback->CallbackOrNull();
1774 callbackGlobal = listenerCallback->CallbackGlobalOrNull();
1775 if (!callback) {
1776 // This will be null for cross-compartment event listeners
1777 // which have been destroyed.
1778 continue;
1782 RefPtr<EventListenerInfo> info = new EventListenerInfo(
1783 this, eventType, callback, callbackGlobal, listener.mFlags.mCapture,
1784 listener.mFlags.mAllowUntrustedEvents, listener.mFlags.mInSystemGroup,
1785 listener.mListenerIsHandler);
1786 aList.AppendElement(info.forget());
1788 return NS_OK;
1791 EventListenerManager::Listener* EventListenerManager::GetListenerFor(
1792 nsAString& aType, JSObject* aListener, bool aCapturing,
1793 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler) {
1794 NS_ENSURE_TRUE(aListener, nullptr);
1796 for (Listener& listener : mListeners.ForwardRange()) {
1797 if ((aType.IsVoid() && !listener.mAllEvents) ||
1798 !Substring(nsDependentAtomString(listener.mTypeAtom), 2)
1799 .Equals(aType) ||
1800 listener.mListenerType == Listener::eNoListener) {
1801 continue;
1804 if (listener.mFlags.mCapture != aCapturing ||
1805 listener.mFlags.mAllowUntrustedEvents != aAllowsUntrusted ||
1806 listener.mFlags.mInSystemGroup != aInSystemEventGroup) {
1807 continue;
1810 if (aIsHandler) {
1811 if (JSEventHandler* handler = listener.GetJSEventHandler()) {
1812 if (handler->GetTypedEventHandler().HasEventHandler()) {
1813 if (handler->GetTypedEventHandler().Ptr()->CallableOrNull() ==
1814 aListener) {
1815 return &listener;
1819 } else if (listener.mListenerType == Listener::eWebIDLListener &&
1820 listener.mListener.GetWebIDLCallback()->CallbackOrNull() ==
1821 aListener) {
1822 return &listener;
1825 return nullptr;
1828 nsresult EventListenerManager::IsListenerEnabled(
1829 nsAString& aType, JSObject* aListener, bool aCapturing,
1830 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler,
1831 bool* aEnabled) {
1832 Listener* listener =
1833 GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted,
1834 aInSystemEventGroup, aIsHandler);
1835 NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE);
1836 *aEnabled = listener->mEnabled;
1837 return NS_OK;
1840 nsresult EventListenerManager::SetListenerEnabled(
1841 nsAString& aType, JSObject* aListener, bool aCapturing,
1842 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler,
1843 bool aEnabled) {
1844 Listener* listener =
1845 GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted,
1846 aInSystemEventGroup, aIsHandler);
1847 NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE);
1848 listener->mEnabled = aEnabled;
1849 if (aEnabled) {
1850 // We may have enabled some listener, clear the cache for which events
1851 // we don't have listeners.
1852 mNoListenerForEvent = eVoidEvent;
1853 mNoListenerForEventAtom = nullptr;
1855 return NS_OK;
1858 bool EventListenerManager::HasUnloadListeners() {
1859 uint32_t count = mListeners.Length();
1860 for (uint32_t i = 0; i < count; ++i) {
1861 Listener* listener = &mListeners.ElementAt(i);
1862 if (listener->mEventMessage == eUnload) {
1863 return true;
1866 return false;
1869 bool EventListenerManager::HasBeforeUnloadListeners() {
1870 uint32_t count = mListeners.Length();
1871 for (uint32_t i = 0; i < count; ++i) {
1872 Listener* listener = &mListeners.ElementAt(i);
1873 if (listener->mEventMessage == eBeforeUnload) {
1874 return true;
1877 return false;
1880 void EventListenerManager::SetEventHandler(nsAtom* aEventName,
1881 EventHandlerNonNull* aHandler) {
1882 if (!aHandler) {
1883 RemoveEventHandler(aEventName);
1884 return;
1887 // Untrusted events are always permitted for non-chrome script
1888 // handlers.
1889 SetEventHandlerInternal(
1890 aEventName, TypedEventHandler(aHandler),
1891 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
1894 void EventListenerManager::SetEventHandler(
1895 OnErrorEventHandlerNonNull* aHandler) {
1896 if (!aHandler) {
1897 RemoveEventHandler(nsGkAtoms::onerror);
1898 return;
1901 // Untrusted events are always permitted on workers and for non-chrome script
1902 // on the main thread.
1903 bool allowUntrusted = !mIsMainThreadELM || !nsContentUtils::IsCallerChrome();
1905 SetEventHandlerInternal(nsGkAtoms::onerror, TypedEventHandler(aHandler),
1906 allowUntrusted);
1909 void EventListenerManager::SetEventHandler(
1910 OnBeforeUnloadEventHandlerNonNull* aHandler) {
1911 if (!aHandler) {
1912 RemoveEventHandler(nsGkAtoms::onbeforeunload);
1913 return;
1916 // Untrusted events are always permitted for non-chrome script
1917 // handlers.
1918 SetEventHandlerInternal(
1919 nsGkAtoms::onbeforeunload, TypedEventHandler(aHandler),
1920 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
1923 const TypedEventHandler* EventListenerManager::GetTypedEventHandler(
1924 nsAtom* aEventName) {
1925 EventMessage eventMessage = GetEventMessage(aEventName);
1926 Listener* listener = FindEventHandler(eventMessage, aEventName);
1928 if (!listener) {
1929 return nullptr;
1932 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
1934 if (listener->mHandlerIsString) {
1935 CompileEventHandlerInternal(listener, nullptr, nullptr);
1938 const TypedEventHandler& typedHandler =
1939 jsEventHandler->GetTypedEventHandler();
1940 return typedHandler.HasEventHandler() ? &typedHandler : nullptr;
1943 size_t EventListenerManager::SizeOfIncludingThis(
1944 MallocSizeOf aMallocSizeOf) const {
1945 size_t n = aMallocSizeOf(this);
1946 n += mListeners.ShallowSizeOfExcludingThis(aMallocSizeOf);
1947 uint32_t count = mListeners.Length();
1948 for (uint32_t i = 0; i < count; ++i) {
1949 JSEventHandler* jsEventHandler =
1950 mListeners.ElementAt(i).GetJSEventHandler();
1951 if (jsEventHandler) {
1952 n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf);
1955 return n;
1958 void EventListenerManager::MarkForCC() {
1959 uint32_t count = mListeners.Length();
1960 for (uint32_t i = 0; i < count; ++i) {
1961 const Listener& listener = mListeners.ElementAt(i);
1962 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
1963 if (jsEventHandler) {
1964 const TypedEventHandler& typedHandler =
1965 jsEventHandler->GetTypedEventHandler();
1966 if (typedHandler.HasEventHandler()) {
1967 typedHandler.Ptr()->MarkForCC();
1969 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1970 listener.mListener.GetWebIDLCallback()->MarkForCC();
1973 if (mRefCnt.IsPurple()) {
1974 mRefCnt.RemovePurple();
1978 void EventListenerManager::TraceListeners(JSTracer* aTrc) {
1979 uint32_t count = mListeners.Length();
1980 for (uint32_t i = 0; i < count; ++i) {
1981 const Listener& listener = mListeners.ElementAt(i);
1982 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
1983 if (jsEventHandler) {
1984 const TypedEventHandler& typedHandler =
1985 jsEventHandler->GetTypedEventHandler();
1986 if (typedHandler.HasEventHandler()) {
1987 mozilla::TraceScriptHolder(typedHandler.Ptr(), aTrc);
1989 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1990 mozilla::TraceScriptHolder(listener.mListener.GetWebIDLCallback(), aTrc);
1992 // We might have eWrappedJSListener, but that is the legacy type for
1993 // JS implemented event listeners, and trickier to handle here.
1997 bool EventListenerManager::HasNonSystemGroupListenersForUntrustedKeyEvents() {
1998 uint32_t count = mListeners.Length();
1999 for (uint32_t i = 0; i < count; ++i) {
2000 Listener* listener = &mListeners.ElementAt(i);
2001 if (!listener->mFlags.mInSystemGroup &&
2002 listener->mFlags.mAllowUntrustedEvents &&
2003 (listener->mTypeAtom == nsGkAtoms::onkeydown ||
2004 listener->mTypeAtom == nsGkAtoms::onkeypress ||
2005 listener->mTypeAtom == nsGkAtoms::onkeyup)) {
2006 return true;
2009 return false;
2012 bool EventListenerManager::
2013 HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents() {
2014 uint32_t count = mListeners.Length();
2015 for (uint32_t i = 0; i < count; ++i) {
2016 Listener* listener = &mListeners.ElementAt(i);
2017 if (!listener->mFlags.mPassive && !listener->mFlags.mInSystemGroup &&
2018 listener->mFlags.mAllowUntrustedEvents &&
2019 (listener->mTypeAtom == nsGkAtoms::onkeydown ||
2020 listener->mTypeAtom == nsGkAtoms::onkeypress ||
2021 listener->mTypeAtom == nsGkAtoms::onkeyup)) {
2022 return true;
2025 return false;
2028 bool EventListenerManager::HasApzAwareListeners() {
2029 uint32_t count = mListeners.Length();
2030 for (uint32_t i = 0; i < count; ++i) {
2031 Listener* listener = &mListeners.ElementAt(i);
2032 if (IsApzAwareListener(listener)) {
2033 return true;
2036 return false;
2039 bool EventListenerManager::IsApzAwareListener(Listener* aListener) {
2040 return !aListener->mFlags.mPassive && mIsMainThreadELM &&
2041 IsApzAwareEvent(aListener->mTypeAtom);
2044 static bool IsWheelEventType(nsAtom* aEvent) {
2045 if (aEvent == nsGkAtoms::onwheel || aEvent == nsGkAtoms::onDOMMouseScroll ||
2046 aEvent == nsGkAtoms::onmousewheel ||
2047 aEvent == nsGkAtoms::onMozMousePixelScroll) {
2048 return true;
2050 return false;
2053 bool EventListenerManager::IsApzAwareEvent(nsAtom* aEvent) {
2054 if (IsWheelEventType(aEvent)) {
2055 return true;
2057 // In theory we should schedule a repaint if the touch event pref changes,
2058 // because the event regions might be out of date. In practice that seems like
2059 // overkill because users generally shouldn't be flipping this pref, much
2060 // less expecting touch listeners on the page to immediately start preventing
2061 // scrolling without so much as a repaint. Tests that we write can work
2062 // around this constraint easily enough.
2063 if (aEvent == nsGkAtoms::ontouchstart || aEvent == nsGkAtoms::ontouchmove) {
2064 return TouchEvent::PrefEnabled(
2065 nsContentUtils::GetDocShellForEventTarget(mTarget));
2067 return false;
2070 bool EventListenerManager::HasNonPassiveWheelListener() {
2071 MOZ_ASSERT(NS_IsMainThread());
2072 uint32_t count = mListeners.Length();
2073 for (uint32_t i = 0; i < count; ++i) {
2074 Listener* listener = &mListeners.ElementAt(i);
2075 if (!listener->mFlags.mPassive && IsWheelEventType(listener->mTypeAtom)) {
2076 return true;
2079 return false;
2082 void EventListenerManager::RemoveAllListeners() {
2083 while (!mListeners.IsEmpty()) {
2084 size_t idx = mListeners.Length() - 1;
2085 RefPtr<nsAtom> type = mListeners.ElementAt(idx).mTypeAtom;
2086 EventMessage message = mListeners.ElementAt(idx).mEventMessage;
2087 mListeners.RemoveElementAt(idx);
2088 NotifyEventListenerRemoved(type);
2089 if (IsDeviceType(message)) {
2090 DisableDevice(message);
2095 already_AddRefed<nsIScriptGlobalObject>
2096 EventListenerManager::GetScriptGlobalAndDocument(Document** aDoc) {
2097 nsCOMPtr<Document> doc;
2098 nsCOMPtr<nsPIDOMWindowInner> win;
2099 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
2100 // Try to get context from doc
2101 doc = node->OwnerDoc();
2102 if (doc->IsLoadedAsData()) {
2103 return nullptr;
2106 win = do_QueryInterface(doc->GetScopeObject());
2107 } else if ((win = GetTargetAsInnerWindow())) {
2108 doc = win->GetExtantDoc();
2111 if (!win || !win->IsCurrentInnerWindow()) {
2112 return nullptr;
2115 doc.forget(aDoc);
2116 nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(win);
2117 return global.forget();
2120 EventListenerManager::ListenerSignalFollower::ListenerSignalFollower(
2121 EventListenerManager* aListenerManager,
2122 EventListenerManager::Listener* aListener)
2123 : dom::AbortFollower(),
2124 mListenerManager(aListenerManager),
2125 mListener(aListener->mListener.Clone()),
2126 mTypeAtom(aListener->mTypeAtom),
2127 mEventMessage(aListener->mEventMessage),
2128 mAllEvents(aListener->mAllEvents),
2129 mFlags(aListener->mFlags){};
2131 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower)
2133 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower)
2134 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower)
2136 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
2137 EventListenerManager::ListenerSignalFollower)
2138 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
2139 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2141 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
2142 EventListenerManager::ListenerSignalFollower)
2143 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
2144 tmp->mListenerManager = nullptr;
2145 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2147 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
2148 EventListenerManager::ListenerSignalFollower)
2149 NS_INTERFACE_MAP_ENTRY(nsISupports)
2150 NS_INTERFACE_MAP_END
2152 void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() {
2153 if (mListenerManager) {
2154 RefPtr<EventListenerManager> elm = mListenerManager;
2155 mListenerManager = nullptr;
2156 elm->RemoveEventListenerInternal(std::move(mListener), mEventMessage,
2157 mTypeAtom, mFlags, mAllEvents);
2161 } // namespace mozilla