Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / events / EventListenerManager.cpp
blobb7ffeb7b6a0aa647a07e55311a1f5b9f42bcedd6
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"
59 #include "nsPIWindowRoot.h"
61 namespace mozilla {
63 using namespace dom;
64 using namespace hal;
66 #define EVENT_TYPE_EQUALS(ls, message, userType, allEvents) \
67 ((ls->mEventMessage == message && \
68 (ls->mEventMessage != eUnidentifiedEvent || ls->mTypeAtom == userType)) || \
69 (allEvents && ls->mAllEvents))
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;
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;
98 return 0;
101 uint32_t EventListenerManager::sMainThreadCreatedCount = 0;
103 EventListenerManagerBase::EventListenerManagerBase()
104 : mMayHavePaintEventListener(false),
105 mMayHaveMutationListeners(false),
106 mMayHaveCapturingListeners(false),
107 mMayHaveSystemGroupListeners(false),
108 mMayHaveTouchEventListener(false),
109 mMayHaveMouseEnterLeaveEventListener(false),
110 mMayHavePointerEnterLeaveEventListener(false),
111 mMayHaveSelectionChangeEventListener(false),
112 mMayHaveFormSelectEventListener(false),
113 mMayHaveTransitionEventListener(false),
114 mClearingListeners(false),
115 mIsMainThreadELM(NS_IsMainThread()),
116 mMayHaveListenersForUntrustedEvents(false) {
117 ClearNoListenersForEvents();
118 static_assert(sizeof(EventListenerManagerBase) == sizeof(uint64_t),
119 "Keep the size of EventListenerManagerBase size compact!");
122 EventListenerManager::EventListenerManager(EventTarget* aTarget)
123 : EventListenerManagerBase(), mTarget(aTarget) {
124 NS_ASSERTION(aTarget, "unexpected null pointer");
126 if (mIsMainThreadELM) {
127 ++sMainThreadCreatedCount;
131 EventListenerManager::~EventListenerManager() {
132 // If your code fails this assertion, a possible reason is that
133 // a class did not call our Disconnect() manually. Note that
134 // this class can have Disconnect called in one of two ways:
135 // if it is part of a cycle, then in Unlink() (such a cycle
136 // would be with one of the listeners, not mTarget which is weak).
137 // If not part of a cycle, then Disconnect must be called manually,
138 // typically from the destructor of the owner class (mTarget).
139 // XXX azakai: Is there any reason to not just call Disconnect
140 // from right here, if not previously called?
141 NS_ASSERTION(!mTarget, "didn't call Disconnect");
142 RemoveAllListenersSilently();
145 void EventListenerManager::RemoveAllListenersSilently() {
146 if (mClearingListeners) {
147 return;
149 mClearingListeners = true;
150 mListeners.Clear();
151 mClearingListeners = false;
154 inline void ImplCycleCollectionTraverse(
155 nsCycleCollectionTraversalCallback& aCallback,
156 EventListenerManager::Listener& aField, const char* aName,
157 unsigned aFlags) {
158 if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
159 nsAutoCString name;
160 name.AppendASCII(aName);
161 if (aField.mTypeAtom) {
162 name.AppendLiteral(" event=");
163 name.Append(nsAtomCString(aField.mTypeAtom));
164 name.AppendLiteral(" listenerType=");
165 name.AppendInt(aField.mListenerType);
166 name.AppendLiteral(" ");
168 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(),
169 name.get(), aFlags);
170 } else {
171 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName,
172 aFlags);
175 CycleCollectionNoteChild(aCallback, aField.mSignalFollower.get(),
176 "mSignalFollower", aFlags);
179 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager)
181 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager)
182 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
183 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
185 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager)
186 tmp->Disconnect();
187 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
189 nsPIDOMWindowInner* EventListenerManager::GetInnerWindowForTarget() {
190 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
191 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
192 // if that's the XBL document?
193 return node->OwnerDoc()->GetInnerWindow();
196 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
197 return window;
200 already_AddRefed<nsPIDOMWindowInner>
201 EventListenerManager::GetTargetAsInnerWindow() const {
202 nsCOMPtr<nsPIDOMWindowInner> window =
203 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget);
204 return window.forget();
207 void EventListenerManager::AddEventListenerInternal(
208 EventListenerHolder aListenerHolder, EventMessage aEventMessage,
209 nsAtom* aTypeAtom, const EventListenerFlags& aFlags, bool aHandler,
210 bool aAllEvents, AbortSignal* aSignal) {
211 MOZ_ASSERT((aEventMessage && aTypeAtom) || aAllEvents, // all-events listener
212 "Missing type");
214 if (!aListenerHolder || mClearingListeners) {
215 return;
218 if (aSignal && aSignal->Aborted()) {
219 return;
222 // Since there is no public API to call us with an EventListenerHolder, we
223 // know that there's an EventListenerHolder on the stack holding a strong ref
224 // to the listener.
226 Listener* listener;
227 uint32_t count = mListeners.Length();
228 for (uint32_t i = 0; i < count; i++) {
229 listener = &mListeners.ElementAt(i);
230 // mListener == aListenerHolder is the last one, since it can be a bit slow.
231 if (listener->mListenerIsHandler == aHandler &&
232 listener->mFlags.EqualsForAddition(aFlags) &&
233 EVENT_TYPE_EQUALS(listener, aEventMessage, aTypeAtom, aAllEvents) &&
234 listener->mListener == aListenerHolder) {
235 return;
239 ClearNoListenersForEvents();
240 mNoListenerForEventAtom = nullptr;
242 listener =
243 aAllEvents ? mListeners.InsertElementAt(0) : mListeners.AppendElement();
244 listener->mEventMessage = aEventMessage;
245 listener->mTypeAtom = aTypeAtom;
246 listener->mFlags = aFlags;
247 listener->mListenerIsHandler = aHandler;
248 listener->mHandlerIsString = false;
249 listener->mAllEvents = aAllEvents;
251 if (listener->mFlags.mAllowUntrustedEvents) {
252 mMayHaveListenersForUntrustedEvents = true;
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 #ifdef DEBUG
312 MOZ_ASSERT(!aFlags.mInSystemGroup,
313 "Legacy mutation events shouldn't be handled by ourselves");
314 MOZ_ASSERT(listener->mListenerType != Listener::eNativeListener,
315 "Legacy mutation events shouldn't be handled in C++ code");
316 if (nsINode* targetNode = nsINode::FromEventTargetOrNull(mTarget)) {
317 MOZ_ASSERT(!nsContentUtils::IsChromeDoc(targetNode->OwnerDoc()),
318 "Legacy mutation events shouldn't be handled in chrome "
319 "documents");
320 MOZ_ASSERT(!targetNode->IsInNativeAnonymousSubtree(),
321 "Legacy mutation events shouldn't listen to mutations in "
322 "native anonymous subtrees");
324 #endif // #ifdef DEBUG
325 // For mutation listeners, we need to update the global bit on the DOM
326 // window. Otherwise we won't actually fire the mutation event.
327 mMayHaveMutationListeners = true;
328 // Go from our target to the nearest enclosing DOM window.
329 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
330 if (Document* doc = window->GetExtantDoc()) {
331 doc->WarnOnceAbout(DeprecatedOperations::eMutationEvent);
333 // If resolvedEventMessage is eLegacySubtreeModified, we need to
334 // listen all mutations. nsContentUtils::HasMutationListeners relies
335 // on this.
336 window->SetMutationListeners(
337 (resolvedEventMessage == eLegacySubtreeModified)
338 ? kAllMutationBits
339 : MutationBitForEventType(resolvedEventMessage));
341 break;
342 case ePointerEnter:
343 case ePointerLeave:
344 mMayHavePointerEnterLeaveEventListener = true;
345 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
346 NS_WARNING_ASSERTION(
347 !nsContentUtils::IsChromeDoc(window->GetExtantDoc()),
348 "Please do not use pointerenter/leave events in chrome. "
349 "They are slower than pointerover/out!");
350 window->SetHasPointerEnterLeaveEventListeners();
352 break;
353 case eGamepadButtonDown:
354 case eGamepadButtonUp:
355 case eGamepadAxisMove:
356 case eGamepadConnected:
357 case eGamepadDisconnected:
358 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
359 window->SetHasGamepadEventListener();
361 break;
362 case eDeviceOrientation:
363 case eDeviceOrientationAbsolute:
364 case eUserProximity:
365 case eDeviceLight:
366 case eDeviceMotion:
367 #if defined(MOZ_WIDGET_ANDROID)
368 case eOrientationChange:
369 #endif // #if defined(MOZ_WIDGET_ANDROID)
370 EnableDevice(resolvedEventMessage);
371 break;
372 case eTouchStart:
373 case eTouchEnd:
374 case eTouchMove:
375 case eTouchCancel:
376 mMayHaveTouchEventListener = true;
377 // we don't want touchevent listeners added by scrollbars to flip this
378 // flag so we ignore listeners created with system event flag
379 if (!aFlags.mInSystemGroup) {
380 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
381 window->SetHasTouchEventListeners();
384 break;
385 case eMouseEnter:
386 case eMouseLeave:
387 mMayHaveMouseEnterLeaveEventListener = true;
388 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
389 NS_WARNING_ASSERTION(
390 !nsContentUtils::IsChromeDoc(window->GetExtantDoc()),
391 "Please do not use mouseenter/leave events in chrome. "
392 "They are slower than mouseover/out!");
393 window->SetHasMouseEnterLeaveEventListeners();
395 break;
396 case eEditorBeforeInput:
397 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
398 window->SetHasBeforeInputEventListenersForTelemetry();
400 break;
401 case eSelectionChange:
402 mMayHaveSelectionChangeEventListener = true;
403 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
404 window->SetHasSelectionChangeEventListeners();
406 break;
407 case eFormSelect:
408 mMayHaveFormSelectEventListener = true;
409 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
410 window->SetHasFormSelectEventListeners();
412 break;
413 case eMarqueeStart:
414 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
415 if (Document* doc = window->GetExtantDoc()) {
416 doc->SetUseCounter(eUseCounter_custom_onstart);
419 break;
420 case eMarqueeBounce:
421 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
422 if (Document* doc = window->GetExtantDoc()) {
423 doc->SetUseCounter(eUseCounter_custom_onbounce);
426 break;
427 case eMarqueeFinish:
428 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
429 if (Document* doc = window->GetExtantDoc()) {
430 doc->SetUseCounter(eUseCounter_custom_onfinish);
433 break;
434 case eScrollPortOverflow:
435 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
436 if (Document* doc = window->GetExtantDoc()) {
437 doc->SetUseCounter(eUseCounter_custom_onoverflow);
440 break;
441 case eScrollPortUnderflow:
442 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
443 if (Document* doc = window->GetExtantDoc()) {
444 doc->SetUseCounter(eUseCounter_custom_onunderflow);
447 break;
448 case eLegacyMouseLineOrPageScroll:
449 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
450 if (Document* doc = window->GetExtantDoc()) {
451 doc->SetUseCounter(eUseCounter_custom_ondommousescroll);
454 break;
455 case eLegacyMousePixelScroll:
456 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
457 if (Document* doc = window->GetExtantDoc()) {
458 doc->SetUseCounter(eUseCounter_custom_onmozmousepixelscroll);
461 break;
462 case eTransitionStart:
463 case eTransitionRun:
464 case eTransitionEnd:
465 case eTransitionCancel:
466 case eWebkitTransitionEnd:
467 mMayHaveTransitionEventListener = true;
468 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
469 window->SetHasTransitionEventListeners();
471 break;
472 case eFormCheckboxStateChange:
473 nsContentUtils::SetMayHaveFormCheckboxStateChangeListeners();
474 break;
475 case eFormRadioStateChange:
476 nsContentUtils::SetMayHaveFormRadioStateChangeListeners();
477 break;
478 default:
479 // XXX Use NS_ASSERTION here to print resolvedEventMessage since
480 // MOZ_ASSERT can take only string literal, not pointer to
481 // characters.
482 NS_ASSERTION(
483 resolvedEventMessage < eLegacyMutationEventFirst ||
484 resolvedEventMessage > eLegacyMutationEventLast,
485 nsPrintfCString("You added new mutation event, but it's not "
486 "handled above, resolvedEventMessage=%s",
487 ToChar(resolvedEventMessage))
488 .get());
489 NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerenter,
490 nsPrintfCString("resolvedEventMessage=%s",
491 ToChar(resolvedEventMessage))
492 .get());
493 NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerleave,
494 nsPrintfCString("resolvedEventMessage=%s",
495 ToChar(resolvedEventMessage))
496 .get());
497 NS_ASSERTION(
498 resolvedEventMessage < eGamepadEventFirst ||
499 resolvedEventMessage > eGamepadEventLast,
500 nsPrintfCString("You added new gamepad event, but it's not "
501 "handled above, resolvedEventMessage=%s",
502 ToChar(resolvedEventMessage))
503 .get());
504 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientation,
505 nsPrintfCString("resolvedEventMessage=%s",
506 ToChar(resolvedEventMessage))
507 .get());
508 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientationabsolute,
509 nsPrintfCString("resolvedEventMessage=%s",
510 ToChar(resolvedEventMessage))
511 .get());
512 NS_ASSERTION(aTypeAtom != nsGkAtoms::onuserproximity,
513 nsPrintfCString("resolvedEventMessage=%s",
514 ToChar(resolvedEventMessage))
515 .get());
516 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicelight,
517 nsPrintfCString("resolvedEventMessage=%s",
518 ToChar(resolvedEventMessage))
519 .get());
520 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicemotion,
521 nsPrintfCString("resolvedEventMessage=%s",
522 ToChar(resolvedEventMessage))
523 .get());
524 #if defined(MOZ_WIDGET_ANDROID)
525 NS_ASSERTION(aTypeAtom != nsGkAtoms::onorientationchange,
526 nsPrintfCString("resolvedEventMessage=%s",
527 ToChar(resolvedEventMessage))
528 .get());
529 #endif // #if defined(MOZ_WIDGET_ANDROID)
530 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchstart,
531 nsPrintfCString("resolvedEventMessage=%s",
532 ToChar(resolvedEventMessage))
533 .get());
534 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchend,
535 nsPrintfCString("resolvedEventMessage=%s",
536 ToChar(resolvedEventMessage))
537 .get());
538 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchmove,
539 nsPrintfCString("resolvedEventMessage=%s",
540 ToChar(resolvedEventMessage))
541 .get());
542 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchcancel,
543 nsPrintfCString("resolvedEventMessage=%s",
544 ToChar(resolvedEventMessage))
545 .get());
546 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseenter,
547 nsPrintfCString("resolvedEventMessage=%s",
548 ToChar(resolvedEventMessage))
549 .get());
550 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseleave,
551 nsPrintfCString("resolvedEventMessage=%s",
552 ToChar(resolvedEventMessage))
553 .get());
554 NS_ASSERTION(aTypeAtom != nsGkAtoms::onbeforeinput,
555 nsPrintfCString("resolvedEventMessage=%s",
556 ToChar(resolvedEventMessage))
557 .get());
558 NS_ASSERTION(aTypeAtom != nsGkAtoms::onselectionchange,
559 nsPrintfCString("resolvedEventMessage=%s",
560 ToChar(resolvedEventMessage))
561 .get());
562 NS_ASSERTION(aTypeAtom != nsGkAtoms::onselect,
563 nsPrintfCString("resolvedEventMessage=%s",
564 ToChar(resolvedEventMessage))
565 .get());
566 NS_ASSERTION(aTypeAtom != nsGkAtoms::onstart,
567 nsPrintfCString("resolvedEventMessage=%s",
568 ToChar(resolvedEventMessage))
569 .get());
570 NS_ASSERTION(aTypeAtom != nsGkAtoms::onbounce,
571 nsPrintfCString("resolvedEventMessage=%s",
572 ToChar(resolvedEventMessage))
573 .get());
574 NS_ASSERTION(aTypeAtom != nsGkAtoms::onfinish,
575 nsPrintfCString("resolvedEventMessage=%s",
576 ToChar(resolvedEventMessage))
577 .get());
578 NS_ASSERTION(aTypeAtom != nsGkAtoms::onoverflow,
579 nsPrintfCString("resolvedEventMessage=%s",
580 ToChar(resolvedEventMessage))
581 .get());
582 NS_ASSERTION(aTypeAtom != nsGkAtoms::onunderflow,
583 nsPrintfCString("resolvedEventMessage=%s",
584 ToChar(resolvedEventMessage))
585 .get());
586 NS_ASSERTION(aTypeAtom != nsGkAtoms::onDOMMouseScroll,
587 nsPrintfCString("resolvedEventMessage=%s",
588 ToChar(resolvedEventMessage))
589 .get());
590 NS_ASSERTION(aTypeAtom != nsGkAtoms::onMozMousePixelScroll,
591 nsPrintfCString("resolvedEventMessage=%s",
592 ToChar(resolvedEventMessage))
593 .get());
594 break;
598 if (IsApzAwareListener(listener)) {
599 ProcessApzAwareEventListenerAdd();
602 if (mTarget) {
603 mTarget->EventListenerAdded(aTypeAtom);
606 if (mIsMainThreadELM && mTarget) {
607 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
608 aTypeAtom);
612 void EventListenerManager::ProcessApzAwareEventListenerAdd() {
613 Document* doc = nullptr;
615 // Mark the node as having apz aware listeners
616 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
617 node->SetMayBeApzAware();
618 doc = node->OwnerDoc();
621 // Schedule a paint so event regions on the layer tree gets updated
622 if (!doc) {
623 if (nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow()) {
624 doc = window->GetExtantDoc();
627 if (!doc) {
628 if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(mTarget)) {
629 if (nsPIDOMWindowInner* window = helper->GetOwner()) {
630 doc = window->GetExtantDoc();
635 if (doc && gfxPlatform::AsyncPanZoomEnabled()) {
636 PresShell* presShell = doc->GetPresShell();
637 if (presShell) {
638 nsIFrame* f = presShell->GetRootFrame();
639 if (f) {
640 f->SchedulePaint();
646 bool EventListenerManager::IsDeviceType(EventMessage aEventMessage) {
647 switch (aEventMessage) {
648 case eDeviceOrientation:
649 case eDeviceOrientationAbsolute:
650 case eDeviceMotion:
651 case eDeviceLight:
652 case eUserProximity:
653 #if defined(MOZ_WIDGET_ANDROID)
654 case eOrientationChange:
655 #endif
656 return true;
657 default:
658 break;
660 return false;
663 void EventListenerManager::EnableDevice(EventMessage aEventMessage) {
664 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
665 if (!window) {
666 return;
669 switch (aEventMessage) {
670 case eDeviceOrientation:
671 #ifdef MOZ_WIDGET_ANDROID
672 // Falls back to SENSOR_ROTATION_VECTOR and SENSOR_ORIENTATION if
673 // unavailable on device.
674 window->EnableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
675 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
676 #else
677 window->EnableDeviceSensor(SENSOR_ORIENTATION);
678 #endif
679 break;
680 case eDeviceOrientationAbsolute:
681 #ifdef MOZ_WIDGET_ANDROID
682 // Falls back to SENSOR_ORIENTATION if unavailable on device.
683 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
684 #else
685 window->EnableDeviceSensor(SENSOR_ORIENTATION);
686 #endif
687 break;
688 case eUserProximity:
689 window->EnableDeviceSensor(SENSOR_PROXIMITY);
690 break;
691 case eDeviceLight:
692 window->EnableDeviceSensor(SENSOR_LIGHT);
693 break;
694 case eDeviceMotion:
695 window->EnableDeviceSensor(SENSOR_ACCELERATION);
696 window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
697 window->EnableDeviceSensor(SENSOR_GYROSCOPE);
698 break;
699 #if defined(MOZ_WIDGET_ANDROID)
700 case eOrientationChange:
701 window->EnableOrientationChangeListener();
702 break;
703 #endif
704 default:
705 NS_WARNING("Enabling an unknown device sensor.");
706 break;
710 void EventListenerManager::DisableDevice(EventMessage aEventMessage) {
711 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
712 if (!window) {
713 return;
716 switch (aEventMessage) {
717 case eDeviceOrientation:
718 #ifdef MOZ_WIDGET_ANDROID
719 // Disable all potential fallback sensors.
720 window->DisableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
721 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
722 #endif
723 window->DisableDeviceSensor(SENSOR_ORIENTATION);
724 break;
725 case eDeviceOrientationAbsolute:
726 #ifdef MOZ_WIDGET_ANDROID
727 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
728 #endif
729 window->DisableDeviceSensor(SENSOR_ORIENTATION);
730 break;
731 case eDeviceMotion:
732 window->DisableDeviceSensor(SENSOR_ACCELERATION);
733 window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
734 window->DisableDeviceSensor(SENSOR_GYROSCOPE);
735 break;
736 case eUserProximity:
737 window->DisableDeviceSensor(SENSOR_PROXIMITY);
738 break;
739 case eDeviceLight:
740 window->DisableDeviceSensor(SENSOR_LIGHT);
741 break;
742 #if defined(MOZ_WIDGET_ANDROID)
743 case eOrientationChange:
744 window->DisableOrientationChangeListener();
745 break;
746 #endif
747 default:
748 NS_WARNING("Disabling an unknown device sensor.");
749 break;
753 void EventListenerManager::NotifyEventListenerRemoved(nsAtom* aUserType) {
754 // If the following code is changed, other callsites of EventListenerRemoved
755 // and NotifyAboutMainThreadListenerChange should be changed too.
756 ClearNoListenersForEvents();
757 mNoListenerForEventAtom = nullptr;
758 if (mTarget) {
759 mTarget->EventListenerRemoved(aUserType);
761 if (mIsMainThreadELM && mTarget) {
762 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
763 aUserType);
767 void EventListenerManager::RemoveEventListenerInternal(
768 EventListenerHolder aListenerHolder, EventMessage aEventMessage,
769 nsAtom* aUserType, const EventListenerFlags& aFlags, bool aAllEvents) {
770 if (!aListenerHolder || !aEventMessage || mClearingListeners) {
771 return;
774 Listener* listener;
776 uint32_t count = mListeners.Length();
777 bool deviceType = IsDeviceType(aEventMessage);
779 RefPtr<EventListenerManager> kungFuDeathGrip(this);
781 for (uint32_t i = 0; i < count; ++i) {
782 listener = &mListeners.ElementAt(i);
783 if (EVENT_TYPE_EQUALS(listener, aEventMessage, aUserType, aAllEvents)) {
784 if (listener->mListener == aListenerHolder &&
785 listener->mFlags.EqualsForRemoval(aFlags)) {
786 mListeners.RemoveElementAt(i);
787 NotifyEventListenerRemoved(aUserType);
788 if (!aAllEvents && deviceType) {
789 DisableDevice(aEventMessage);
791 return;
797 bool EventListenerManager::Listener::MatchesEventMessage(
798 const WidgetEvent* aEvent, EventMessage aEventMessage) const {
799 MOZ_ASSERT(aEventMessage == aEvent->mMessage ||
800 aEventMessage == EventListenerManager::GetLegacyEventMessage(
801 aEvent->mMessage),
802 "aEvent and aEventMessage should agree, modulo legacyness");
804 if (MOZ_UNLIKELY(mAllEvents)) {
805 return true;
808 // This is slightly different from EVENT_TYPE_EQUALS in that it returns
809 // true even when aEvent->mMessage == eUnidentifiedEvent and
810 // mEventMessage != eUnidentifiedEvent as long as the atoms are
811 // the same.
812 if (aEvent->mMessage == eUnidentifiedEvent) {
813 return mTypeAtom == aEvent->mSpecifiedEventType;
815 return mEventMessage == aEventMessage;
818 static bool IsDefaultPassiveWhenOnRoot(EventMessage aMessage) {
819 if (aMessage == eTouchStart || aMessage == eTouchMove) {
820 return true;
822 if (aMessage == eWheel || aMessage == eLegacyMouseLineOrPageScroll ||
823 aMessage == eLegacyMousePixelScroll) {
824 return StaticPrefs::dom_event_default_to_passive_wheel_listeners();
826 return false;
829 static bool IsRootEventTarget(EventTarget* aTarget) {
830 if (!aTarget) {
831 return false;
833 if (aTarget->IsInnerWindow()) {
834 return true;
836 const nsINode* node = nsINode::FromEventTarget(aTarget);
837 if (!node) {
838 return false;
840 Document* doc = node->OwnerDoc();
841 return node == doc || node == doc->GetRootElement() || node == doc->GetBody();
844 void EventListenerManager::MaybeMarkPassive(EventMessage aMessage,
845 EventListenerFlags& aFlags) {
846 if (!mIsMainThreadELM) {
847 return;
849 if (!IsDefaultPassiveWhenOnRoot(aMessage)) {
850 return;
852 if (!IsRootEventTarget(mTarget)) {
853 return;
855 aFlags.mPassive = true;
858 void EventListenerManager::AddEventListenerByType(
859 EventListenerHolder aListenerHolder, const nsAString& aType,
860 const EventListenerFlags& aFlags, const Optional<bool>& aPassive,
861 AbortSignal* aSignal) {
862 RefPtr<nsAtom> atom;
863 EventMessage message =
864 GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
866 EventListenerFlags flags = aFlags;
867 if (aPassive.WasPassed()) {
868 flags.mPassive = aPassive.Value();
869 } else {
870 MaybeMarkPassive(message, flags);
873 AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags,
874 false, false, aSignal);
877 void EventListenerManager::RemoveEventListenerByType(
878 EventListenerHolder aListenerHolder, const nsAString& aType,
879 const EventListenerFlags& aFlags) {
880 RefPtr<nsAtom> atom;
881 EventMessage message =
882 GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
883 RemoveEventListenerInternal(std::move(aListenerHolder), message, atom,
884 aFlags);
887 EventListenerManager::Listener* EventListenerManager::FindEventHandler(
888 EventMessage aEventMessage, nsAtom* aTypeAtom) {
889 // Run through the listeners for this type and see if a script
890 // listener is registered
891 Listener* listener;
892 uint32_t count = mListeners.Length();
893 for (uint32_t i = 0; i < count; ++i) {
894 listener = &mListeners.ElementAt(i);
895 if (listener->mListenerIsHandler &&
896 EVENT_TYPE_EQUALS(listener, aEventMessage, aTypeAtom, false)) {
897 return listener;
900 return nullptr;
903 EventListenerManager::Listener* EventListenerManager::SetEventHandlerInternal(
904 nsAtom* aName, const TypedEventHandler& aTypedHandler,
905 bool aPermitUntrustedEvents) {
906 MOZ_ASSERT(aName);
908 EventMessage eventMessage = GetEventMessage(aName);
909 Listener* listener = FindEventHandler(eventMessage, aName);
911 if (!listener) {
912 // If we didn't find a script listener or no listeners existed
913 // create and add a new one.
914 EventListenerFlags flags;
915 flags.mListenerIsJSListener = true;
916 MaybeMarkPassive(eventMessage, flags);
918 nsCOMPtr<JSEventHandler> jsEventHandler;
919 NS_NewJSEventHandler(mTarget, aName, aTypedHandler,
920 getter_AddRefs(jsEventHandler));
921 AddEventListenerInternal(EventListenerHolder(jsEventHandler), eventMessage,
922 aName, flags, true);
924 listener = FindEventHandler(eventMessage, aName);
925 } else {
926 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
927 MOZ_ASSERT(jsEventHandler,
928 "How can we have an event handler with no JSEventHandler?");
930 bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler;
931 // Possibly the same listener, but update still the context and scope.
932 jsEventHandler->SetHandler(aTypedHandler);
933 if (mTarget && !same) {
934 mTarget->EventListenerRemoved(aName);
935 mTarget->EventListenerAdded(aName);
937 if (mIsMainThreadELM && mTarget) {
938 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, aName);
942 // Set flag to indicate possible need for compilation later
943 listener->mHandlerIsString = !aTypedHandler.HasEventHandler();
944 if (aPermitUntrustedEvents) {
945 listener->mFlags.mAllowUntrustedEvents = true;
946 mMayHaveListenersForUntrustedEvents = true;
949 return listener;
952 nsresult EventListenerManager::SetEventHandler(nsAtom* aName,
953 const nsAString& aBody,
954 bool aDeferCompilation,
955 bool aPermitUntrustedEvents,
956 Element* aElement) {
957 auto removeEventHandler = MakeScopeExit([&] { RemoveEventHandler(aName); });
959 nsCOMPtr<Document> doc;
960 nsCOMPtr<nsIScriptGlobalObject> global =
961 GetScriptGlobalAndDocument(getter_AddRefs(doc));
963 if (!global) {
964 // This can happen; for example this document might have been
965 // loaded as data.
966 return NS_OK;
969 nsresult rv = NS_OK;
970 // return early preventing the event listener from being added
971 // 'doc' is fetched above
972 if (doc) {
973 // Don't allow adding an event listener if the document is sandboxed
974 // without 'allow-scripts'.
975 if (doc->HasScriptsBlockedBySandbox()) {
976 return NS_ERROR_DOM_SECURITY_ERR;
979 // Perform CSP check
980 nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
981 unsigned lineNum = 0;
982 unsigned columnNum = 0;
984 JSContext* cx = nsContentUtils::GetCurrentJSContext();
985 if (cx && !JS::DescribeScriptedCaller(cx, nullptr, &lineNum, &columnNum)) {
986 JS_ClearPendingException(cx);
989 if (csp) {
990 bool allowsInlineScript = true;
991 rv = csp->GetAllowsInline(
992 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE,
993 true, // aHasUnsafeHash
994 u""_ns, // aNonce
995 true, // aParserCreated (true because attribute event handler)
996 aElement,
997 nullptr, // nsICSPEventListener
998 aBody, lineNum, columnNum, &allowsInlineScript);
999 NS_ENSURE_SUCCESS(rv, rv);
1001 // return early if CSP wants us to block inline scripts
1002 if (!allowsInlineScript) {
1003 return NS_OK;
1008 // This might be the first reference to this language in the global
1009 // We must init the language before we attempt to fetch its context.
1010 if (NS_FAILED(global->EnsureScriptEnvironment())) {
1011 NS_WARNING("Failed to setup script environment for this language");
1012 // but fall through and let the inevitable failure below handle it.
1015 nsIScriptContext* context = global->GetScriptContext();
1016 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
1017 NS_ENSURE_STATE(global->HasJSGlobal());
1019 removeEventHandler.release();
1021 Listener* listener = SetEventHandlerInternal(aName, TypedEventHandler(),
1022 aPermitUntrustedEvents);
1024 if (!aDeferCompilation) {
1025 return CompileEventHandlerInternal(listener, &aBody, aElement);
1028 return NS_OK;
1031 void EventListenerManager::RemoveEventHandler(nsAtom* aName) {
1032 if (mClearingListeners) {
1033 return;
1036 EventMessage eventMessage = GetEventMessage(aName);
1037 Listener* listener = FindEventHandler(eventMessage, aName);
1039 if (listener) {
1040 mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0)));
1041 NotifyEventListenerRemoved(aName);
1042 if (IsDeviceType(eventMessage)) {
1043 DisableDevice(eventMessage);
1048 nsresult EventListenerManager::CompileEventHandlerInternal(
1049 Listener* aListener, const nsAString* aBody, Element* aElement) {
1050 MOZ_ASSERT(aListener->GetJSEventHandler());
1051 MOZ_ASSERT(aListener->mHandlerIsString,
1052 "Why are we compiling a non-string JS listener?");
1053 JSEventHandler* jsEventHandler = aListener->GetJSEventHandler();
1054 MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(),
1055 "What is there to compile?");
1057 nsresult result = NS_OK;
1058 nsCOMPtr<Document> doc;
1059 nsCOMPtr<nsIScriptGlobalObject> global =
1060 GetScriptGlobalAndDocument(getter_AddRefs(doc));
1061 NS_ENSURE_STATE(global);
1063 // Activate JSAPI, and make sure that exceptions are reported on the right
1064 // Window.
1065 AutoJSAPI jsapi;
1066 if (NS_WARN_IF(!jsapi.Init(global))) {
1067 return NS_ERROR_UNEXPECTED;
1069 JSContext* cx = jsapi.cx();
1071 RefPtr<nsAtom> typeAtom = aListener->mTypeAtom;
1072 nsAtom* attrName = typeAtom;
1074 // Flag us as not a string so we don't keep trying to compile strings which
1075 // can't be compiled.
1076 aListener->mHandlerIsString = false;
1078 // mTarget may not be an Element if it's a window and we're
1079 // getting an inline event listener forwarded from <html:body> or
1080 // <html:frameset> or <xul:window> or the like.
1081 // XXX I don't like that we have to reference content from
1082 // here. The alternative is to store the event handler string on
1083 // the JSEventHandler itself, and that still doesn't address
1084 // the arg names issue.
1085 RefPtr<Element> element = Element::FromEventTargetOrNull(mTarget);
1086 MOZ_ASSERT(element || aBody, "Where will we get our body?");
1087 nsAutoString handlerBody;
1088 const nsAString* body = aBody;
1089 if (!aBody) {
1090 if (aListener->mTypeAtom == nsGkAtoms::onSVGLoad) {
1091 attrName = nsGkAtoms::onload;
1092 } else if (aListener->mTypeAtom == nsGkAtoms::onSVGScroll) {
1093 attrName = nsGkAtoms::onscroll;
1094 } else if (aListener->mTypeAtom == nsGkAtoms::onbeginEvent) {
1095 attrName = nsGkAtoms::onbegin;
1096 } else if (aListener->mTypeAtom == nsGkAtoms::onrepeatEvent) {
1097 attrName = nsGkAtoms::onrepeat;
1098 } else if (aListener->mTypeAtom == nsGkAtoms::onendEvent) {
1099 attrName = nsGkAtoms::onend;
1100 } else if (aListener->mTypeAtom == nsGkAtoms::onwebkitAnimationEnd) {
1101 attrName = nsGkAtoms::onwebkitanimationend;
1102 } else if (aListener->mTypeAtom == nsGkAtoms::onwebkitAnimationIteration) {
1103 attrName = nsGkAtoms::onwebkitanimationiteration;
1104 } else if (aListener->mTypeAtom == nsGkAtoms::onwebkitAnimationStart) {
1105 attrName = nsGkAtoms::onwebkitanimationstart;
1106 } else if (aListener->mTypeAtom == nsGkAtoms::onwebkitTransitionEnd) {
1107 attrName = nsGkAtoms::onwebkittransitionend;
1110 element->GetAttr(attrName, handlerBody);
1111 body = &handlerBody;
1112 aElement = element;
1114 aListener = nullptr;
1116 nsAutoCString url("-moz-evil:lying-event-listener"_ns);
1117 MOZ_ASSERT(body);
1118 MOZ_ASSERT(aElement);
1119 nsIURI* uri = aElement->OwnerDoc()->GetDocumentURI();
1120 if (uri) {
1121 uri->GetSpec(url);
1124 nsCOMPtr<nsPIDOMWindowInner> win =
1125 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget);
1126 uint32_t argCount;
1127 const char** argNames;
1128 nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), typeAtom, win,
1129 &argCount, &argNames);
1131 // Wrap the event target, so that we can use it as the scope for the event
1132 // handler. Note that mTarget is different from aElement in the <body> case,
1133 // where mTarget is a Window.
1135 // The wrapScope doesn't really matter here, because the target will create
1136 // its reflector in the proper scope, and then we'll enter that realm.
1137 JS::Rooted<JSObject*> wrapScope(cx, global->GetGlobalJSObject());
1138 JS::Rooted<JS::Value> v(cx);
1140 JSAutoRealm ar(cx, wrapScope);
1141 nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v,
1142 /* aAllowWrapping = */ false);
1143 if (NS_WARN_IF(NS_FAILED(rv))) {
1144 return rv;
1148 JS::Rooted<JSObject*> target(cx, &v.toObject());
1149 JSAutoRealm ar(cx, target);
1151 // Now that we've entered the realm we actually care about, create our
1152 // scope chain. Note that we start with |element|, not aElement, because
1153 // mTarget is different from aElement in the <body> case, where mTarget is a
1154 // Window, and in that case we do not want the scope chain to include the body
1155 // or the document.
1156 JS::RootedVector<JSObject*> scopeChain(cx);
1157 if (!nsJSUtils::GetScopeChainForElement(cx, element, &scopeChain)) {
1158 return NS_ERROR_OUT_OF_MEMORY;
1161 nsDependentAtomString str(attrName);
1162 // Most of our names are short enough that we don't even have to malloc
1163 // the JS string stuff, so don't worry about playing games with
1164 // refcounting XPCOM stringbuffers.
1165 JS::Rooted<JSString*> jsStr(
1166 cx, JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length()));
1167 NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY);
1169 // Get the reflector for |aElement|, so that we can pass to setElement.
1170 if (NS_WARN_IF(!GetOrCreateDOMReflector(cx, aElement, &v))) {
1171 return NS_ERROR_FAILURE;
1174 RefPtr<JS::loader::ScriptFetchOptions> fetchOptions =
1175 new JS::loader::ScriptFetchOptions(
1176 CORS_NONE, aElement->OwnerDoc()->GetReferrerPolicy(),
1177 aElement->OwnerDoc()->NodePrincipal());
1179 RefPtr<JS::loader::EventScript> eventScript =
1180 new JS::loader::EventScript(fetchOptions, uri);
1182 JS::CompileOptions options(cx);
1183 // Use line 0 to make the function body starts from line 1.
1184 options.setIntroductionType("eventHandler")
1185 .setFileAndLine(url.get(), 0)
1186 .setDeferDebugMetadata(true);
1188 JS::Rooted<JSObject*> handler(cx);
1189 result = nsJSUtils::CompileFunction(jsapi, scopeChain, options,
1190 nsAtomCString(typeAtom), argCount,
1191 argNames, *body, handler.address());
1192 NS_ENSURE_SUCCESS(result, result);
1193 NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
1195 JS::Rooted<JS::Value> privateValue(cx, JS::PrivateValue(eventScript));
1196 result = nsJSUtils::UpdateFunctionDebugMetadata(jsapi, handler, options,
1197 jsStr, privateValue);
1198 NS_ENSURE_SUCCESS(result, result);
1200 MOZ_ASSERT(js::IsObjectInContextCompartment(handler, cx));
1201 JS::Rooted<JSObject*> handlerGlobal(cx, JS::CurrentGlobalOrNull(cx));
1203 if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) {
1204 RefPtr<OnErrorEventHandlerNonNull> handlerCallback =
1205 new OnErrorEventHandlerNonNull(static_cast<JSContext*>(nullptr),
1206 handler, handlerGlobal,
1207 /* aIncumbentGlobal = */ nullptr);
1208 jsEventHandler->SetHandler(handlerCallback);
1209 } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) {
1210 RefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
1211 new OnBeforeUnloadEventHandlerNonNull(static_cast<JSContext*>(nullptr),
1212 handler, handlerGlobal,
1213 /* aIncumbentGlobal = */ nullptr);
1214 jsEventHandler->SetHandler(handlerCallback);
1215 } else {
1216 RefPtr<EventHandlerNonNull> handlerCallback = new EventHandlerNonNull(
1217 static_cast<JSContext*>(nullptr), handler, handlerGlobal,
1218 /* aIncumbentGlobal = */ nullptr);
1219 jsEventHandler->SetHandler(handlerCallback);
1222 return result;
1225 nsresult EventListenerManager::HandleEventSubType(Listener* aListener,
1226 Event* aDOMEvent,
1227 EventTarget* aCurrentTarget) {
1228 nsresult result = NS_OK;
1229 // strong ref
1230 EventListenerHolder listenerHolder(aListener->mListener.Clone());
1232 // If this is a script handler and we haven't yet
1233 // compiled the event handler itself
1234 if ((aListener->mListenerType == Listener::eJSEventListener) &&
1235 aListener->mHandlerIsString) {
1236 result = CompileEventHandlerInternal(aListener, nullptr, nullptr);
1237 aListener = nullptr;
1240 if (NS_SUCCEEDED(result)) {
1241 Maybe<EventCallbackDebuggerNotificationGuard> dbgGuard;
1242 if (dom::ChromeUtils::IsDevToolsOpened()) {
1243 dbgGuard.emplace(aCurrentTarget, aDOMEvent);
1245 nsAutoMicroTask mt;
1247 // Event::currentTarget is set in EventDispatcher.
1248 if (listenerHolder.HasWebIDLCallback()) {
1249 ErrorResult rv;
1250 listenerHolder.GetWebIDLCallback()->HandleEvent(aCurrentTarget,
1251 *aDOMEvent, rv);
1252 result = rv.StealNSResult();
1253 } else {
1254 // listenerHolder is holding a stack ref here.
1255 result = MOZ_KnownLive(listenerHolder.GetXPCOMCallback())
1256 ->HandleEvent(aDOMEvent);
1260 return result;
1263 /* static */ EventMessage EventListenerManager::GetLegacyEventMessage(
1264 EventMessage aEventMessage) {
1265 // webkit-prefixed legacy events:
1266 if (aEventMessage == eTransitionEnd) {
1267 return eWebkitTransitionEnd;
1269 if (aEventMessage == eAnimationStart) {
1270 return eWebkitAnimationStart;
1272 if (aEventMessage == eAnimationEnd) {
1273 return eWebkitAnimationEnd;
1275 if (aEventMessage == eAnimationIteration) {
1276 return eWebkitAnimationIteration;
1279 switch (aEventMessage) {
1280 case eFullscreenChange:
1281 return eMozFullscreenChange;
1282 case eFullscreenError:
1283 return eMozFullscreenError;
1284 default:
1285 return aEventMessage;
1289 EventMessage EventListenerManager::GetEventMessage(nsAtom* aEventName) const {
1290 if (mIsMainThreadELM) {
1291 return nsContentUtils::GetEventMessage(aEventName);
1294 // The nsContentUtils event message hashtables aren't threadsafe, so just fall
1295 // back to eUnidentifiedEvent.
1296 return eUnidentifiedEvent;
1299 EventMessage EventListenerManager::GetEventMessageAndAtomForListener(
1300 const nsAString& aType, nsAtom** aAtom) {
1301 if (mIsMainThreadELM) {
1302 return nsContentUtils::GetEventMessageAndAtomForListener(aType, aAtom);
1305 *aAtom = NS_Atomize(u"on"_ns + aType).take();
1306 return eUnidentifiedEvent;
1309 already_AddRefed<nsPIDOMWindowInner> EventListenerManager::WindowFromListener(
1310 Listener* aListener, bool aItemInShadowTree) {
1311 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
1312 if (!aItemInShadowTree) {
1313 if (aListener->mListener.HasWebIDLCallback()) {
1314 CallbackObject* callback = aListener->mListener.GetWebIDLCallback();
1315 nsIGlobalObject* global = nullptr;
1316 if (callback) {
1317 global = callback->IncumbentGlobalOrNull();
1319 if (global) {
1320 innerWindow = global->AsInnerWindow(); // Can be nullptr
1322 } else {
1323 // This ensures `window.event` can be set properly for
1324 // nsWindowRoot to handle KeyPress event.
1325 if (aListener && aListener->mEventMessage == eKeyPress && mTarget &&
1326 mTarget->IsRootWindow()) {
1327 nsPIWindowRoot* root = mTarget->AsWindowRoot();
1328 if (nsPIDOMWindowOuter* outerWindow = root->GetWindow()) {
1329 innerWindow = outerWindow->GetCurrentInnerWindow();
1331 } else {
1332 // Can't get the global from
1333 // listener->mListener.GetXPCOMCallback().
1334 // In most cases, it would be the same as for
1335 // the target, so let's do that.
1336 innerWindow = GetInnerWindowForTarget(); // Can be nullptr
1340 return innerWindow.forget();
1344 * Causes a check for event listeners and processing by them if they exist.
1345 * @param an event listener
1348 void EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
1349 WidgetEvent* aEvent,
1350 Event** aDOMEvent,
1351 EventTarget* aCurrentTarget,
1352 nsEventStatus* aEventStatus,
1353 bool aItemInShadowTree) {
1354 MOZ_ASSERT_IF(aEvent->mMessage != eUnidentifiedEvent, mIsMainThreadELM);
1356 // Set the value of the internal PreventDefault flag properly based on
1357 // aEventStatus
1358 if (!aEvent->DefaultPrevented() &&
1359 *aEventStatus == nsEventStatus_eConsumeNoDefault) {
1360 // Assume that if only aEventStatus claims that the event has already been
1361 // consumed, the consumer is default event handler.
1362 aEvent->PreventDefault();
1365 Maybe<AutoHandlingUserInputStatePusher> userInputStatePusher;
1366 Maybe<AutoPopupStatePusher> popupStatePusher;
1367 if (mIsMainThreadELM) {
1368 userInputStatePusher.emplace(UserActivation::IsUserInteractionEvent(aEvent),
1369 aEvent);
1370 popupStatePusher.emplace(
1371 PopupBlocker::GetEventPopupControlState(aEvent, *aDOMEvent));
1374 bool hasListener = false;
1375 bool hasListenerForCurrentGroup = false;
1376 bool usingLegacyMessage = false;
1377 bool hasRemovedListener = false;
1378 EventMessage eventMessage = aEvent->mMessage;
1380 while (true) {
1381 Maybe<EventMessageAutoOverride> legacyAutoOverride;
1382 for (Listener& listenerRef : mListeners.EndLimitedRange()) {
1383 if (aEvent->mFlags.mImmediatePropagationStopped) {
1384 break;
1386 Listener* listener = &listenerRef;
1387 if (!listener->MatchesEventMessage(aEvent, eventMessage)) {
1388 continue;
1390 if (listener->mListenerType == Listener::eNoListener) {
1391 // The listener has been removed, it cannot handle anything.
1392 continue;
1394 if (!listener->mEnabled) {
1395 // The listener has been disabled, for example by devtools.
1396 continue;
1398 hasListener = true;
1399 if (!listener->MatchesEventGroup(aEvent)) {
1400 continue;
1402 hasListenerForCurrentGroup = true;
1404 // Check that the phase is same in event and event listener. Also check
1405 // that the event is trusted or that the listener allows untrusted events.
1406 if (!listener->MatchesEventPhase(aEvent) ||
1407 !listener->AllowsEventTrustedness(aEvent)) {
1408 continue;
1410 if (!*aDOMEvent) {
1411 // Lazily create the DOM event.
1412 // This is tiny bit slow, but happens only once per event.
1413 // Similar code also in EventDispatcher.
1414 nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget;
1415 RefPtr<Event> event =
1416 EventDispatcher::CreateEvent(et, aPresContext, aEvent, u""_ns);
1417 event.forget(aDOMEvent);
1419 if (!*aDOMEvent) {
1420 continue;
1422 if (!aEvent->mCurrentTarget) {
1423 aEvent->mCurrentTarget = aCurrentTarget->GetTargetForDOMEvent();
1424 if (!aEvent->mCurrentTarget) {
1425 break;
1428 if (usingLegacyMessage && !legacyAutoOverride) {
1429 // Override the aDOMEvent's event-message (its .type) until we
1430 // finish traversing listeners (when legacyAutoOverride destructs)
1431 legacyAutoOverride.emplace(*aDOMEvent, eventMessage);
1434 aEvent->mFlags.mInPassiveListener = listener->mFlags.mPassive;
1435 Maybe<Listener> listenerHolder;
1436 if (listener->mFlags.mOnce) {
1437 // Move the listener to the stack before handling the event.
1438 // The order is important, otherwise the listener could be
1439 // called again inside the listener.
1440 listenerHolder.emplace(std::move(*listener));
1441 listener = listenerHolder.ptr();
1442 hasRemovedListener = true;
1445 nsCOMPtr<nsPIDOMWindowInner> innerWindow =
1446 WindowFromListener(listener, aItemInShadowTree);
1447 mozilla::dom::Event* oldWindowEvent = nullptr;
1448 if (innerWindow) {
1449 oldWindowEvent = innerWindow->SetEvent(*aDOMEvent);
1452 nsresult rv = HandleEventSubType(listener, *aDOMEvent, aCurrentTarget);
1454 if (innerWindow) {
1455 Unused << innerWindow->SetEvent(oldWindowEvent);
1458 if (NS_FAILED(rv)) {
1459 aEvent->mFlags.mExceptionWasRaised = true;
1461 aEvent->mFlags.mInPassiveListener = false;
1464 // If we didn't find any matching listeners, and our event has a legacy
1465 // version, we'll now switch to looking for that legacy version and we'll
1466 // recheck our listeners.
1467 if (hasListenerForCurrentGroup || usingLegacyMessage ||
1468 !aEvent->IsTrusted()) {
1469 // No need to recheck listeners, because we already found a match, we
1470 // already rechecked them, or it is not a trusted event.
1471 break;
1473 EventMessage legacyEventMessage = GetLegacyEventMessage(eventMessage);
1474 if (legacyEventMessage == eventMessage) {
1475 break; // There's no legacy version of our event; no need to recheck.
1477 MOZ_ASSERT(
1478 GetLegacyEventMessage(legacyEventMessage) == legacyEventMessage,
1479 "Legacy event messages should not themselves have legacy versions");
1481 // Recheck our listeners, using the legacy event message we just looked up:
1482 eventMessage = legacyEventMessage;
1483 usingLegacyMessage = true;
1486 aEvent->mCurrentTarget = nullptr;
1488 if (hasRemovedListener) {
1489 // If there are any once listeners replaced with a placeholder in
1490 // the loop above, we need to clean up them here. Note that, this
1491 // could clear once listeners handled in some outer level as well,
1492 // but that should not affect the result.
1493 mListeners.NonObservingRemoveElementsBy([](const Listener& aListener) {
1494 return aListener.mListenerType == Listener::eNoListener;
1496 NotifyEventListenerRemoved(aEvent->mSpecifiedEventType);
1497 if (IsDeviceType(aEvent->mMessage)) {
1498 // This is a device-type event, we need to check whether we can
1499 // disable device after removing the once listeners.
1500 const auto [begin, end] = mListeners.NonObservingRange();
1501 const bool hasAnyListener =
1502 std::any_of(begin, end, [aEvent](const Listener& listenerRef) {
1503 const Listener* listener = &listenerRef;
1504 return EVENT_TYPE_EQUALS(listener, aEvent->mMessage,
1505 aEvent->mSpecifiedEventType,
1506 /* all events */ false);
1509 if (!hasAnyListener) {
1510 DisableDevice(aEvent->mMessage);
1515 if (mIsMainThreadELM && !hasListener) {
1516 if (aEvent->mMessage != eUnidentifiedEvent) {
1517 mNoListenerForEvents[2] = mNoListenerForEvents[1];
1518 mNoListenerForEvents[1] = mNoListenerForEvents[0];
1519 mNoListenerForEvents[0] = aEvent->mMessage;
1520 } else {
1521 mNoListenerForEventAtom = aEvent->mSpecifiedEventType;
1525 if (aEvent->DefaultPrevented()) {
1526 *aEventStatus = nsEventStatus_eConsumeNoDefault;
1530 void EventListenerManager::Disconnect() {
1531 mTarget = nullptr;
1532 RemoveAllListenersSilently();
1535 void EventListenerManager::AddEventListener(const nsAString& aType,
1536 EventListenerHolder aListenerHolder,
1537 bool aUseCapture,
1538 bool aWantsUntrusted) {
1539 EventListenerFlags flags;
1540 flags.mCapture = aUseCapture;
1541 flags.mAllowUntrustedEvents = aWantsUntrusted;
1542 return AddEventListenerByType(std::move(aListenerHolder), aType, flags);
1545 void EventListenerManager::AddEventListener(
1546 const nsAString& aType, EventListenerHolder aListenerHolder,
1547 const dom::AddEventListenerOptionsOrBoolean& aOptions,
1548 bool aWantsUntrusted) {
1549 EventListenerFlags flags;
1550 Optional<bool> passive;
1551 AbortSignal* signal = nullptr;
1552 if (aOptions.IsBoolean()) {
1553 flags.mCapture = aOptions.GetAsBoolean();
1554 } else {
1555 const auto& options = aOptions.GetAsAddEventListenerOptions();
1556 flags.mCapture = options.mCapture;
1557 flags.mInSystemGroup = options.mMozSystemGroup;
1558 flags.mOnce = options.mOnce;
1559 if (options.mPassive.WasPassed()) {
1560 passive.Construct(options.mPassive.Value());
1563 if (options.mSignal.WasPassed()) {
1564 signal = &options.mSignal.Value();
1568 flags.mAllowUntrustedEvents = aWantsUntrusted;
1569 return AddEventListenerByType(std::move(aListenerHolder), aType, flags,
1570 passive, signal);
1573 void EventListenerManager::RemoveEventListener(
1574 const nsAString& aType, EventListenerHolder aListenerHolder,
1575 bool aUseCapture) {
1576 EventListenerFlags flags;
1577 flags.mCapture = aUseCapture;
1578 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
1581 void EventListenerManager::RemoveEventListener(
1582 const nsAString& aType, EventListenerHolder aListenerHolder,
1583 const dom::EventListenerOptionsOrBoolean& aOptions) {
1584 EventListenerFlags flags;
1585 if (aOptions.IsBoolean()) {
1586 flags.mCapture = aOptions.GetAsBoolean();
1587 } else {
1588 const auto& options = aOptions.GetAsEventListenerOptions();
1589 flags.mCapture = options.mCapture;
1590 flags.mInSystemGroup = options.mMozSystemGroup;
1592 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
1595 void EventListenerManager::AddListenerForAllEvents(EventListener* aDOMListener,
1596 bool aUseCapture,
1597 bool aWantsUntrusted,
1598 bool aSystemEventGroup) {
1599 EventListenerFlags flags;
1600 flags.mCapture = aUseCapture;
1601 flags.mAllowUntrustedEvents = aWantsUntrusted;
1602 flags.mInSystemGroup = aSystemEventGroup;
1603 AddEventListenerInternal(EventListenerHolder(aDOMListener), eAllEvents,
1604 nullptr, flags, false, true);
1607 void EventListenerManager::RemoveListenerForAllEvents(
1608 EventListener* aDOMListener, bool aUseCapture, bool aSystemEventGroup) {
1609 EventListenerFlags flags;
1610 flags.mCapture = aUseCapture;
1611 flags.mInSystemGroup = aSystemEventGroup;
1612 RemoveEventListenerInternal(EventListenerHolder(aDOMListener), eAllEvents,
1613 nullptr, flags, true);
1616 bool EventListenerManager::HasMutationListeners() {
1617 if (mMayHaveMutationListeners) {
1618 uint32_t count = mListeners.Length();
1619 for (uint32_t i = 0; i < count; ++i) {
1620 Listener* listener = &mListeners.ElementAt(i);
1621 if (listener->mEventMessage >= eLegacyMutationEventFirst &&
1622 listener->mEventMessage <= eLegacyMutationEventLast) {
1623 return true;
1628 return false;
1631 uint32_t EventListenerManager::MutationListenerBits() {
1632 uint32_t bits = 0;
1633 if (mMayHaveMutationListeners) {
1634 uint32_t count = mListeners.Length();
1635 for (uint32_t i = 0; i < count; ++i) {
1636 Listener* listener = &mListeners.ElementAt(i);
1637 if (listener->mEventMessage >= eLegacyMutationEventFirst &&
1638 listener->mEventMessage <= eLegacyMutationEventLast) {
1639 if (listener->mEventMessage == eLegacySubtreeModified) {
1640 return kAllMutationBits;
1642 bits |= MutationBitForEventType(listener->mEventMessage);
1646 return bits;
1649 bool EventListenerManager::HasListenersFor(const nsAString& aEventName) const {
1650 RefPtr<nsAtom> atom = NS_Atomize(u"on"_ns + aEventName);
1651 return HasListenersFor(atom);
1654 bool EventListenerManager::HasListenersFor(nsAtom* aEventNameWithOn) const {
1655 return HasListenersForInternal(aEventNameWithOn, false);
1658 bool EventListenerManager::HasNonSystemGroupListenersFor(
1659 nsAtom* aEventNameWithOn) const {
1660 return HasListenersForInternal(aEventNameWithOn, true);
1663 bool EventListenerManager::HasListenersForInternal(
1664 nsAtom* aEventNameWithOn, bool aIgnoreSystemGroup) const {
1665 #ifdef DEBUG
1666 nsAutoString name;
1667 aEventNameWithOn->ToString(name);
1668 #endif
1669 NS_ASSERTION(StringBeginsWith(name, u"on"_ns),
1670 "Event name does not start with 'on'");
1671 uint32_t count = mListeners.Length();
1672 for (uint32_t i = 0; i < count; ++i) {
1673 const Listener* listener = &mListeners.ElementAt(i);
1674 if (listener->mTypeAtom == aEventNameWithOn) {
1675 if (aIgnoreSystemGroup && listener->mFlags.mInSystemGroup) {
1676 continue;
1678 return true;
1681 return false;
1684 bool EventListenerManager::HasListeners() const {
1685 return !mListeners.IsEmpty();
1688 nsresult EventListenerManager::GetListenerInfo(
1689 nsTArray<RefPtr<nsIEventListenerInfo>>& aList) {
1690 nsCOMPtr<EventTarget> target = mTarget;
1691 NS_ENSURE_STATE(target);
1692 aList.Clear();
1693 for (const Listener& listener : mListeners.ForwardRange()) {
1694 // If this is a script handler and we haven't yet
1695 // compiled the event handler itself go ahead and compile it
1696 if (listener.mListenerType == Listener::eJSEventListener &&
1697 listener.mHandlerIsString) {
1698 CompileEventHandlerInternal(const_cast<Listener*>(&listener), nullptr,
1699 nullptr);
1701 nsAutoString eventType;
1702 if (listener.mAllEvents) {
1703 eventType.SetIsVoid(true);
1704 } else if (listener.mListenerType == Listener::eNoListener) {
1705 continue;
1706 } else {
1707 eventType.Assign(Substring(nsDependentAtomString(listener.mTypeAtom), 2));
1710 JS::Rooted<JSObject*> callback(RootingCx());
1711 JS::Rooted<JSObject*> callbackGlobal(RootingCx());
1712 if (JSEventHandler* handler = listener.GetJSEventHandler()) {
1713 if (handler->GetTypedEventHandler().HasEventHandler()) {
1714 CallbackFunction* callbackFun = handler->GetTypedEventHandler().Ptr();
1715 callback = callbackFun->CallableOrNull();
1716 callbackGlobal = callbackFun->CallbackGlobalOrNull();
1717 if (!callback) {
1718 // This will be null for cross-compartment event listeners
1719 // which have been destroyed.
1720 continue;
1723 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1724 EventListener* listenerCallback = listener.mListener.GetWebIDLCallback();
1725 callback = listenerCallback->CallbackOrNull();
1726 callbackGlobal = listenerCallback->CallbackGlobalOrNull();
1727 if (!callback) {
1728 // This will be null for cross-compartment event listeners
1729 // which have been destroyed.
1730 continue;
1734 RefPtr<EventListenerInfo> info = new EventListenerInfo(
1735 this, eventType, callback, callbackGlobal, listener.mFlags.mCapture,
1736 listener.mFlags.mAllowUntrustedEvents, listener.mFlags.mInSystemGroup,
1737 listener.mListenerIsHandler);
1738 aList.AppendElement(info.forget());
1740 return NS_OK;
1743 EventListenerManager::Listener* EventListenerManager::GetListenerFor(
1744 nsAString& aType, JSObject* aListener, bool aCapturing,
1745 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler) {
1746 NS_ENSURE_TRUE(aListener, nullptr);
1748 for (Listener& listener : mListeners.ForwardRange()) {
1749 if ((aType.IsVoid() && !listener.mAllEvents) ||
1750 !Substring(nsDependentAtomString(listener.mTypeAtom), 2)
1751 .Equals(aType) ||
1752 listener.mListenerType == Listener::eNoListener) {
1753 continue;
1756 if (listener.mFlags.mCapture != aCapturing ||
1757 listener.mFlags.mAllowUntrustedEvents != aAllowsUntrusted ||
1758 listener.mFlags.mInSystemGroup != aInSystemEventGroup) {
1759 continue;
1762 if (aIsHandler) {
1763 if (JSEventHandler* handler = listener.GetJSEventHandler()) {
1764 if (handler->GetTypedEventHandler().HasEventHandler()) {
1765 if (handler->GetTypedEventHandler().Ptr()->CallableOrNull() ==
1766 aListener) {
1767 return &listener;
1771 } else if (listener.mListenerType == Listener::eWebIDLListener &&
1772 listener.mListener.GetWebIDLCallback()->CallbackOrNull() ==
1773 aListener) {
1774 return &listener;
1777 return nullptr;
1780 nsresult EventListenerManager::IsListenerEnabled(
1781 nsAString& aType, JSObject* aListener, bool aCapturing,
1782 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler,
1783 bool* aEnabled) {
1784 Listener* listener =
1785 GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted,
1786 aInSystemEventGroup, aIsHandler);
1787 NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE);
1788 *aEnabled = listener->mEnabled;
1789 return NS_OK;
1792 nsresult EventListenerManager::SetListenerEnabled(
1793 nsAString& aType, JSObject* aListener, bool aCapturing,
1794 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler,
1795 bool aEnabled) {
1796 Listener* listener =
1797 GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted,
1798 aInSystemEventGroup, aIsHandler);
1799 NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE);
1800 listener->mEnabled = aEnabled;
1801 if (aEnabled) {
1802 // We may have enabled some listener, clear the cache for which events
1803 // we don't have listeners.
1804 ClearNoListenersForEvents();
1805 mNoListenerForEventAtom = nullptr;
1807 return NS_OK;
1810 bool EventListenerManager::HasUnloadListeners() {
1811 uint32_t count = mListeners.Length();
1812 for (uint32_t i = 0; i < count; ++i) {
1813 Listener* listener = &mListeners.ElementAt(i);
1814 if (listener->mEventMessage == eUnload) {
1815 return true;
1818 return false;
1821 bool EventListenerManager::HasBeforeUnloadListeners() {
1822 uint32_t count = mListeners.Length();
1823 for (uint32_t i = 0; i < count; ++i) {
1824 Listener* listener = &mListeners.ElementAt(i);
1825 if (listener->mEventMessage == eBeforeUnload) {
1826 return true;
1829 return false;
1832 void EventListenerManager::SetEventHandler(nsAtom* aEventName,
1833 EventHandlerNonNull* aHandler) {
1834 if (!aHandler) {
1835 RemoveEventHandler(aEventName);
1836 return;
1839 // Untrusted events are always permitted for non-chrome script
1840 // handlers.
1841 SetEventHandlerInternal(
1842 aEventName, TypedEventHandler(aHandler),
1843 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
1846 void EventListenerManager::SetEventHandler(
1847 OnErrorEventHandlerNonNull* aHandler) {
1848 if (!aHandler) {
1849 RemoveEventHandler(nsGkAtoms::onerror);
1850 return;
1853 // Untrusted events are always permitted on workers and for non-chrome script
1854 // on the main thread.
1855 bool allowUntrusted = !mIsMainThreadELM || !nsContentUtils::IsCallerChrome();
1857 SetEventHandlerInternal(nsGkAtoms::onerror, TypedEventHandler(aHandler),
1858 allowUntrusted);
1861 void EventListenerManager::SetEventHandler(
1862 OnBeforeUnloadEventHandlerNonNull* aHandler) {
1863 if (!aHandler) {
1864 RemoveEventHandler(nsGkAtoms::onbeforeunload);
1865 return;
1868 // Untrusted events are always permitted for non-chrome script
1869 // handlers.
1870 SetEventHandlerInternal(
1871 nsGkAtoms::onbeforeunload, TypedEventHandler(aHandler),
1872 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
1875 const TypedEventHandler* EventListenerManager::GetTypedEventHandler(
1876 nsAtom* aEventName) {
1877 EventMessage eventMessage = GetEventMessage(aEventName);
1878 Listener* listener = FindEventHandler(eventMessage, aEventName);
1880 if (!listener) {
1881 return nullptr;
1884 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
1886 if (listener->mHandlerIsString) {
1887 CompileEventHandlerInternal(listener, nullptr, nullptr);
1890 const TypedEventHandler& typedHandler =
1891 jsEventHandler->GetTypedEventHandler();
1892 return typedHandler.HasEventHandler() ? &typedHandler : nullptr;
1895 size_t EventListenerManager::SizeOfIncludingThis(
1896 MallocSizeOf aMallocSizeOf) const {
1897 size_t n = aMallocSizeOf(this);
1898 n += mListeners.ShallowSizeOfExcludingThis(aMallocSizeOf);
1899 uint32_t count = mListeners.Length();
1900 for (uint32_t i = 0; i < count; ++i) {
1901 JSEventHandler* jsEventHandler =
1902 mListeners.ElementAt(i).GetJSEventHandler();
1903 if (jsEventHandler) {
1904 n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf);
1907 return n;
1910 void EventListenerManager::MarkForCC() {
1911 uint32_t count = mListeners.Length();
1912 for (uint32_t i = 0; i < count; ++i) {
1913 const Listener& listener = mListeners.ElementAt(i);
1914 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
1915 if (jsEventHandler) {
1916 const TypedEventHandler& typedHandler =
1917 jsEventHandler->GetTypedEventHandler();
1918 if (typedHandler.HasEventHandler()) {
1919 typedHandler.Ptr()->MarkForCC();
1921 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1922 listener.mListener.GetWebIDLCallback()->MarkForCC();
1925 if (mRefCnt.IsPurple()) {
1926 mRefCnt.RemovePurple();
1930 void EventListenerManager::TraceListeners(JSTracer* aTrc) {
1931 uint32_t count = mListeners.Length();
1932 for (uint32_t i = 0; i < count; ++i) {
1933 const Listener& listener = mListeners.ElementAt(i);
1934 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
1935 if (jsEventHandler) {
1936 const TypedEventHandler& typedHandler =
1937 jsEventHandler->GetTypedEventHandler();
1938 if (typedHandler.HasEventHandler()) {
1939 mozilla::TraceScriptHolder(typedHandler.Ptr(), aTrc);
1941 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1942 mozilla::TraceScriptHolder(listener.mListener.GetWebIDLCallback(), aTrc);
1944 // We might have eWrappedJSListener, but that is the legacy type for
1945 // JS implemented event listeners, and trickier to handle here.
1949 bool EventListenerManager::HasNonSystemGroupListenersForUntrustedKeyEvents() {
1950 uint32_t count = mListeners.Length();
1951 for (uint32_t i = 0; i < count; ++i) {
1952 Listener* listener = &mListeners.ElementAt(i);
1953 if (!listener->mFlags.mInSystemGroup &&
1954 listener->mFlags.mAllowUntrustedEvents &&
1955 (listener->mTypeAtom == nsGkAtoms::onkeydown ||
1956 listener->mTypeAtom == nsGkAtoms::onkeypress ||
1957 listener->mTypeAtom == nsGkAtoms::onkeyup)) {
1958 return true;
1961 return false;
1964 bool EventListenerManager::
1965 HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents() {
1966 uint32_t count = mListeners.Length();
1967 for (uint32_t i = 0; i < count; ++i) {
1968 Listener* listener = &mListeners.ElementAt(i);
1969 if (!listener->mFlags.mPassive && !listener->mFlags.mInSystemGroup &&
1970 listener->mFlags.mAllowUntrustedEvents &&
1971 (listener->mTypeAtom == nsGkAtoms::onkeydown ||
1972 listener->mTypeAtom == nsGkAtoms::onkeypress ||
1973 listener->mTypeAtom == nsGkAtoms::onkeyup)) {
1974 return true;
1977 return false;
1980 bool EventListenerManager::HasApzAwareListeners() {
1981 uint32_t count = mListeners.Length();
1982 for (uint32_t i = 0; i < count; ++i) {
1983 Listener* listener = &mListeners.ElementAt(i);
1984 if (IsApzAwareListener(listener)) {
1985 return true;
1988 return false;
1991 bool EventListenerManager::IsApzAwareListener(Listener* aListener) {
1992 return !aListener->mFlags.mPassive && mIsMainThreadELM &&
1993 IsApzAwareEvent(aListener->mTypeAtom);
1996 static bool IsWheelEventType(nsAtom* aEvent) {
1997 if (aEvent == nsGkAtoms::onwheel || aEvent == nsGkAtoms::onDOMMouseScroll ||
1998 aEvent == nsGkAtoms::onmousewheel ||
1999 aEvent == nsGkAtoms::onMozMousePixelScroll) {
2000 return true;
2002 return false;
2005 bool EventListenerManager::IsApzAwareEvent(nsAtom* aEvent) {
2006 if (IsWheelEventType(aEvent)) {
2007 return true;
2009 // In theory we should schedule a repaint if the touch event pref changes,
2010 // because the event regions might be out of date. In practice that seems like
2011 // overkill because users generally shouldn't be flipping this pref, much
2012 // less expecting touch listeners on the page to immediately start preventing
2013 // scrolling without so much as a repaint. Tests that we write can work
2014 // around this constraint easily enough.
2015 if (aEvent == nsGkAtoms::ontouchstart || aEvent == nsGkAtoms::ontouchmove) {
2016 return TouchEvent::PrefEnabled(
2017 nsContentUtils::GetDocShellForEventTarget(mTarget));
2019 return false;
2022 bool EventListenerManager::HasNonPassiveWheelListener() {
2023 MOZ_ASSERT(NS_IsMainThread());
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 && IsWheelEventType(listener->mTypeAtom)) {
2028 return true;
2031 return false;
2034 void EventListenerManager::RemoveAllListeners() {
2035 while (!mListeners.IsEmpty()) {
2036 size_t idx = mListeners.Length() - 1;
2037 RefPtr<nsAtom> type = mListeners.ElementAt(idx).mTypeAtom;
2038 EventMessage message = mListeners.ElementAt(idx).mEventMessage;
2039 mListeners.RemoveElementAt(idx);
2040 NotifyEventListenerRemoved(type);
2041 if (IsDeviceType(message)) {
2042 DisableDevice(message);
2047 already_AddRefed<nsIScriptGlobalObject>
2048 EventListenerManager::GetScriptGlobalAndDocument(Document** aDoc) {
2049 nsCOMPtr<Document> doc;
2050 nsCOMPtr<nsPIDOMWindowInner> win;
2051 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
2052 // Try to get context from doc
2053 doc = node->OwnerDoc();
2054 if (doc->IsLoadedAsData()) {
2055 return nullptr;
2058 win = do_QueryInterface(doc->GetScopeObject());
2059 } else if ((win = GetTargetAsInnerWindow())) {
2060 doc = win->GetExtantDoc();
2063 if (!win || !win->IsCurrentInnerWindow()) {
2064 return nullptr;
2067 doc.forget(aDoc);
2068 nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(win);
2069 return global.forget();
2072 EventListenerManager::ListenerSignalFollower::ListenerSignalFollower(
2073 EventListenerManager* aListenerManager,
2074 EventListenerManager::Listener* aListener)
2075 : dom::AbortFollower(),
2076 mListenerManager(aListenerManager),
2077 mListener(aListener->mListener.Clone()),
2078 mTypeAtom(aListener->mTypeAtom),
2079 mEventMessage(aListener->mEventMessage),
2080 mAllEvents(aListener->mAllEvents),
2081 mFlags(aListener->mFlags){};
2083 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower)
2085 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower)
2086 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower)
2088 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
2089 EventListenerManager::ListenerSignalFollower)
2090 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
2091 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2093 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
2094 EventListenerManager::ListenerSignalFollower)
2095 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
2096 tmp->mListenerManager = nullptr;
2097 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2099 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
2100 EventListenerManager::ListenerSignalFollower)
2101 NS_INTERFACE_MAP_ENTRY(nsISupports)
2102 NS_INTERFACE_MAP_END
2104 void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() {
2105 if (mListenerManager) {
2106 RefPtr<EventListenerManager> elm = mListenerManager;
2107 mListenerManager = nullptr;
2108 elm->RemoveEventListenerInternal(std::move(mListener), mEventMessage,
2109 mTypeAtom, mFlags, mAllEvents);
2113 } // namespace mozilla