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