Merge mozilla-central to autoland. a=merge CLOSED TREE
[gecko.git] / dom / events / EventListenerManager.cpp
blob269ccd9de770a000a5ab711f91e22cabd732b845
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Microsoft's API Name hackery sucks
8 #undef CreateEvent
10 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
11 #include "js/loader/LoadedScript.h"
12 #include "js/loader/ScriptFetchOptions.h"
13 #include "mozilla/BasicEvents.h"
14 #include "mozilla/BinarySearch.h"
15 #include "mozilla/CycleCollectedJSRuntime.h"
16 #include "mozilla/DOMEventTargetHelper.h"
17 #include "mozilla/EventDispatcher.h"
18 #include "mozilla/EventListenerManager.h"
19 #include "mozilla/HalSensor.h"
20 #include "mozilla/InternalMutationEvent.h"
21 #include "mozilla/JSEventHandler.h"
22 #include "mozilla/Maybe.h"
23 #include "mozilla/MemoryReporting.h"
24 #include "mozilla/Preferences.h"
25 #include "mozilla/PresShell.h"
26 #include "mozilla/dom/AbortSignal.h"
27 #include "mozilla/dom/BindingUtils.h"
28 #include "mozilla/dom/EventCallbackDebuggerNotification.h"
29 #include "mozilla/dom/Element.h"
30 #include "mozilla/dom/Event.h"
31 #include "mozilla/dom/EventTargetBinding.h"
32 #include "mozilla/dom/PopupBlocker.h"
33 #include "mozilla/dom/RequestBinding.h"
34 #include "mozilla/dom/ScriptLoader.h"
35 #include "mozilla/dom/ScriptSettings.h"
36 #include "mozilla/dom/TouchEvent.h"
37 #include "mozilla/dom/UserActivation.h"
38 #include "mozilla/ScopeExit.h"
39 #include "mozilla/TimeStamp.h"
40 #include "mozilla/dom/ChromeUtils.h"
42 #include "EventListenerService.h"
43 #include "nsCOMPtr.h"
44 #include "nsContentUtils.h"
45 #include "nsDOMCID.h"
46 #include "nsError.h"
47 #include "nsGenericHTMLElement.h"
48 #include "nsGkAtoms.h"
49 #include "nsIContent.h"
50 #include "nsIContentSecurityPolicy.h"
51 #include "mozilla/dom/Document.h"
52 #include "nsIScriptGlobalObject.h"
53 #include "nsISupports.h"
54 #include "nsJSUtils.h"
55 #include "nsNameSpaceManager.h"
56 #include "nsPIDOMWindow.h"
57 #include "nsPrintfCString.h"
58 #include "nsSandboxFlags.h"
59 #include "xpcpublic.h"
60 #include "nsIFrame.h"
61 #include "nsDisplayList.h"
62 #include "nsPIWindowRoot.h"
64 namespace mozilla {
66 using namespace dom;
67 using namespace hal;
69 static uint32_t MutationBitForEventType(EventMessage aEventType) {
70 switch (aEventType) {
71 case eLegacySubtreeModified:
72 return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED;
73 case eLegacyNodeInserted:
74 return NS_EVENT_BITS_MUTATION_NODEINSERTED;
75 case eLegacyNodeRemoved:
76 return NS_EVENT_BITS_MUTATION_NODEREMOVED;
77 case eLegacyNodeRemovedFromDocument:
78 return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT;
79 case eLegacyNodeInsertedIntoDocument:
80 return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT;
81 case eLegacyAttrModified:
82 return NS_EVENT_BITS_MUTATION_ATTRMODIFIED;
83 case eLegacyCharacterDataModified:
84 return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
85 default:
86 break;
88 return 0;
91 class ListenerMapEntryComparator {
92 public:
93 explicit ListenerMapEntryComparator(nsAtom* aTarget)
94 : mAddressOfEventType(reinterpret_cast<uintptr_t>(aTarget)) {}
96 int operator()(
97 const EventListenerManager::EventListenerMapEntry& aEntry) const {
98 uintptr_t value = reinterpret_cast<uintptr_t>(aEntry.mTypeAtom.get());
99 if (mAddressOfEventType == value) {
100 return 0;
103 if (mAddressOfEventType < value) {
104 return -1;
107 return 1;
110 private:
111 const uintptr_t mAddressOfEventType; // the address of the atom, can be 0
114 uint32_t EventListenerManager::sMainThreadCreatedCount = 0;
116 EventListenerManagerBase::EventListenerManagerBase()
117 : mMayHaveDOMActivateEventListener(false),
118 mMayHavePaintEventListener(false),
119 mMayHaveMutationListeners(false),
120 mMayHaveCapturingListeners(false),
121 mMayHaveSystemGroupListeners(false),
122 mMayHaveTouchEventListener(false),
123 mMayHaveMouseEnterLeaveEventListener(false),
124 mMayHavePointerEnterLeaveEventListener(false),
125 mMayHaveSelectionChangeEventListener(false),
126 mMayHaveFormSelectEventListener(false),
127 mMayHaveTransitionEventListener(false),
128 mClearingListeners(false),
129 mIsMainThreadELM(NS_IsMainThread()),
130 mMayHaveListenersForUntrustedEvents(false) {
131 ClearNoListenersForEvents();
132 static_assert(sizeof(EventListenerManagerBase) == sizeof(uint64_t),
133 "Keep the size of EventListenerManagerBase size compact!");
136 EventListenerManager::EventListenerManager(EventTarget* aTarget)
137 : mTarget(aTarget) {
138 NS_ASSERTION(aTarget, "unexpected null pointer");
140 if (mIsMainThreadELM) {
141 mRefCnt.SetIsOnMainThread();
142 ++sMainThreadCreatedCount;
146 EventListenerManager::~EventListenerManager() {
147 // If your code fails this assertion, a possible reason is that
148 // a class did not call our Disconnect() manually. Note that
149 // this class can have Disconnect called in one of two ways:
150 // if it is part of a cycle, then in Unlink() (such a cycle
151 // would be with one of the listeners, not mTarget which is weak).
152 // If not part of a cycle, then Disconnect must be called manually,
153 // typically from the destructor of the owner class (mTarget).
154 // XXX azakai: Is there any reason to not just call Disconnect
155 // from right here, if not previously called?
156 NS_ASSERTION(!mTarget, "didn't call Disconnect");
157 RemoveAllListenersSilently();
160 void EventListenerManager::RemoveAllListenersSilently() {
161 if (mClearingListeners) {
162 return;
164 mClearingListeners = true;
165 mListenerMap.Clear();
166 mClearingListeners = false;
169 inline void ImplCycleCollectionTraverse(
170 nsCycleCollectionTraversalCallback& aCallback,
171 EventListenerManager::EventListenerMap& aField, const char* aName,
172 uint32_t aFlags = 0) {
173 if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
174 nsAutoCString name;
175 name.AppendASCII(aName);
176 name.AppendLiteral(" mEntries[i] event=");
177 size_t entryPrefixLen = name.Length();
178 for (const auto& entry : aField.mEntries) {
179 if (entry.mTypeAtom) {
180 name.Replace(entryPrefixLen, name.Length() - entryPrefixLen,
181 nsAtomCString(entry.mTypeAtom));
182 } else {
183 name.Replace(entryPrefixLen, name.Length() - entryPrefixLen,
184 "(all)"_ns);
186 ImplCycleCollectionTraverse(aCallback, *entry.mListeners, name.get());
188 } else {
189 for (const auto& entry : aField.mEntries) {
190 ImplCycleCollectionTraverse(aCallback, *entry.mListeners,
191 ".mEntries[i].mListeners");
196 inline void ImplCycleCollectionTraverse(
197 nsCycleCollectionTraversalCallback& aCallback,
198 EventListenerManager::Listener& aField, const char* aName,
199 unsigned aFlags) {
200 if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
201 nsAutoCString name;
202 name.AppendASCII(aName);
203 name.AppendLiteral(" listenerType=");
204 name.AppendInt(aField.mListenerType);
205 name.AppendLiteral(" ");
206 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(),
207 name.get(), aFlags);
208 } else {
209 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName,
210 aFlags);
213 CycleCollectionNoteChild(aCallback, aField.mSignalFollower.get(),
214 "mSignalFollower", aFlags);
217 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager)
219 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager)
220 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerMap);
221 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
223 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager)
224 tmp->Disconnect();
225 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
227 nsPIDOMWindowInner* EventListenerManager::GetInnerWindowForTarget() {
228 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
229 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
230 // if that's the XBL document?
231 return node->OwnerDoc()->GetInnerWindow();
234 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
235 return window;
238 already_AddRefed<nsPIDOMWindowInner>
239 EventListenerManager::GetTargetAsInnerWindow() const {
240 nsCOMPtr<nsPIDOMWindowInner> window =
241 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget);
242 return window.forget();
245 void EventListenerManager::AddEventListenerInternal(
246 EventListenerHolder aListenerHolder, EventMessage aEventMessage,
247 nsAtom* aTypeAtom, const EventListenerFlags& aFlags, bool aHandler,
248 bool aAllEvents, AbortSignal* aSignal) {
249 MOZ_ASSERT((aEventMessage && aTypeAtom) || aAllEvents, // all-events listener
250 "Missing type");
251 MOZ_ASSERT_IF(
252 aEventMessage != eUnidentifiedEvent && !aAllEvents,
253 aTypeAtom == nsContentUtils::GetEventTypeFromMessage(aEventMessage));
255 if (!aListenerHolder || mClearingListeners) {
256 return;
259 if (aSignal && aSignal->Aborted()) {
260 return;
263 // Since there is no public API to call us with an EventListenerHolder, we
264 // know that there's an EventListenerHolder on the stack holding a strong ref
265 // to the listener.
267 RefPtr<ListenerArray> listeners =
268 aAllEvents ? mListenerMap.GetOrCreateListenersForAllEvents()
269 : mListenerMap.GetOrCreateListenersForType(aTypeAtom);
271 for (const Listener& listener : listeners->NonObservingRange()) {
272 // mListener == aListenerHolder is the last one, since it can be a bit slow.
273 if (listener.mListenerIsHandler == aHandler &&
274 listener.mFlags.EqualsForAddition(aFlags) &&
275 listener.mListener == aListenerHolder) {
276 return;
280 ClearNoListenersForEvents();
281 mNoListenerForEventAtom = nullptr;
283 Listener* listener = listeners->AppendElement();
284 listener->mFlags = aFlags;
285 listener->mListenerIsHandler = aHandler;
286 listener->mHandlerIsString = false;
287 listener->mAllEvents = aAllEvents;
289 if (listener->mFlags.mAllowUntrustedEvents) {
290 mMayHaveListenersForUntrustedEvents = true;
293 // Detect the type of event listener.
294 if (aFlags.mListenerIsJSListener) {
295 MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback());
296 listener->mListenerType = Listener::eJSEventListener;
297 } else if (aListenerHolder.HasWebIDLCallback()) {
298 listener->mListenerType = Listener::eWebIDLListener;
299 } else {
300 listener->mListenerType = Listener::eNativeListener;
302 listener->mListener = std::move(aListenerHolder);
304 if (aSignal) {
305 listener->mSignalFollower =
306 new ListenerSignalFollower(this, listener, aTypeAtom);
307 listener->mSignalFollower->Follow(aSignal);
310 if (aFlags.mInSystemGroup) {
311 mMayHaveSystemGroupListeners = true;
313 if (aFlags.mCapture) {
314 mMayHaveCapturingListeners = true;
317 // Events which are not supported in the running environment is mapped to
318 // eUnidentifiedEvent. Then, we need to consider the proper event message
319 // with comparing the atom.
321 EventMessage resolvedEventMessage = aEventMessage;
322 if (resolvedEventMessage == eUnidentifiedEvent && aTypeAtom->IsStatic()) {
323 // TouchEvents are registered only when
324 // nsContentUtils::InitializeTouchEventTable() is called.
325 if (aTypeAtom == nsGkAtoms::ontouchstart) {
326 resolvedEventMessage = eTouchStart;
327 } else if (aTypeAtom == nsGkAtoms::ontouchend) {
328 resolvedEventMessage = eTouchEnd;
329 } else if (aTypeAtom == nsGkAtoms::ontouchmove) {
330 resolvedEventMessage = eTouchMove;
331 } else if (aTypeAtom == nsGkAtoms::ontouchcancel) {
332 resolvedEventMessage = eTouchCancel;
336 switch (resolvedEventMessage) {
337 case eAfterPaint:
338 mMayHavePaintEventListener = true;
339 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
340 window->SetHasPaintEventListeners();
342 break;
343 case eLegacyDOMActivate:
344 mMayHaveDOMActivateEventListener = true;
345 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
346 window->SetHasDOMActivateEventListeners();
348 break;
349 case eLegacySubtreeModified:
350 case eLegacyNodeInserted:
351 case eLegacyNodeRemoved:
352 case eLegacyNodeRemovedFromDocument:
353 case eLegacyNodeInsertedIntoDocument:
354 case eLegacyAttrModified:
355 case eLegacyCharacterDataModified:
356 #ifdef DEBUG
357 MOZ_ASSERT(!aFlags.mInSystemGroup,
358 "Legacy mutation events shouldn't be handled by ourselves");
359 MOZ_ASSERT(listener->mListenerType != Listener::eNativeListener,
360 "Legacy mutation events shouldn't be handled in C++ code");
361 if (nsINode* targetNode = nsINode::FromEventTargetOrNull(mTarget)) {
362 MOZ_ASSERT(!nsContentUtils::IsChromeDoc(targetNode->OwnerDoc()),
363 "Legacy mutation events shouldn't be handled in chrome "
364 "documents");
365 MOZ_ASSERT(!targetNode->IsInNativeAnonymousSubtree(),
366 "Legacy mutation events shouldn't listen to mutations in "
367 "native anonymous subtrees");
369 #endif // #ifdef DEBUG
370 // For mutation listeners, we need to update the global bit on the DOM
371 // window. Otherwise we won't actually fire the mutation event.
372 mMayHaveMutationListeners = true;
373 // Go from our target to the nearest enclosing DOM window.
374 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
375 if (Document* doc = window->GetExtantDoc()) {
376 doc->WarnOnceAbout(DeprecatedOperations::eMutationEvent);
378 // If resolvedEventMessage is eLegacySubtreeModified, we need to
379 // listen all mutations. nsContentUtils::HasMutationListeners relies
380 // on this.
381 window->SetMutationListeners(
382 (resolvedEventMessage == eLegacySubtreeModified)
383 ? NS_EVENT_BITS_MUTATION_ALL
384 : MutationBitForEventType(resolvedEventMessage));
386 break;
387 case ePointerEnter:
388 case ePointerLeave:
389 mMayHavePointerEnterLeaveEventListener = true;
390 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
391 NS_WARNING_ASSERTION(
392 !nsContentUtils::IsChromeDoc(window->GetExtantDoc()),
393 "Please do not use pointerenter/leave events in chrome. "
394 "They are slower than pointerover/out!");
395 window->SetHasPointerEnterLeaveEventListeners();
397 break;
398 case eGamepadButtonDown:
399 case eGamepadButtonUp:
400 case eGamepadAxisMove:
401 case eGamepadConnected:
402 case eGamepadDisconnected:
403 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
404 window->SetHasGamepadEventListener();
406 break;
407 case eDeviceOrientation:
408 case eDeviceOrientationAbsolute:
409 case eUserProximity:
410 case eDeviceLight:
411 case eDeviceMotion:
412 #if defined(MOZ_WIDGET_ANDROID)
413 case eOrientationChange:
414 #endif // #if defined(MOZ_WIDGET_ANDROID)
415 EnableDevice(aTypeAtom);
416 break;
417 case eTouchStart:
418 case eTouchEnd:
419 case eTouchMove:
420 case eTouchCancel:
421 mMayHaveTouchEventListener = true;
422 // we don't want touchevent listeners added by scrollbars to flip this
423 // flag so we ignore listeners created with system event flag
424 if (!aFlags.mInSystemGroup) {
425 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
426 window->SetHasTouchEventListeners();
429 break;
430 case eMouseEnter:
431 case eMouseLeave:
432 mMayHaveMouseEnterLeaveEventListener = true;
433 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
434 NS_WARNING_ASSERTION(
435 !nsContentUtils::IsChromeDoc(window->GetExtantDoc()),
436 "Please do not use mouseenter/leave events in chrome. "
437 "They are slower than mouseover/out!");
438 window->SetHasMouseEnterLeaveEventListeners();
440 break;
441 case eEditorBeforeInput:
442 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
443 window->SetHasBeforeInputEventListenersForTelemetry();
445 break;
446 case eSelectionChange:
447 mMayHaveSelectionChangeEventListener = true;
448 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
449 window->SetHasSelectionChangeEventListeners();
451 break;
452 case eFormSelect:
453 mMayHaveFormSelectEventListener = true;
454 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
455 window->SetHasFormSelectEventListeners();
457 break;
458 case eMarqueeStart:
459 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
460 if (Document* doc = window->GetExtantDoc()) {
461 doc->SetUseCounter(eUseCounter_custom_onstart);
464 break;
465 case eMarqueeBounce:
466 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
467 if (Document* doc = window->GetExtantDoc()) {
468 doc->SetUseCounter(eUseCounter_custom_onbounce);
471 break;
472 case eMarqueeFinish:
473 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
474 if (Document* doc = window->GetExtantDoc()) {
475 doc->SetUseCounter(eUseCounter_custom_onfinish);
478 break;
479 case eScrollPortOverflow:
480 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
481 if (Document* doc = window->GetExtantDoc()) {
482 doc->SetUseCounter(eUseCounter_custom_onoverflow);
485 break;
486 case eScrollPortUnderflow:
487 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
488 if (Document* doc = window->GetExtantDoc()) {
489 doc->SetUseCounter(eUseCounter_custom_onunderflow);
492 break;
493 case eLegacyMouseLineOrPageScroll:
494 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
495 if (Document* doc = window->GetExtantDoc()) {
496 doc->SetUseCounter(eUseCounter_custom_ondommousescroll);
499 break;
500 case eLegacyMousePixelScroll:
501 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
502 if (Document* doc = window->GetExtantDoc()) {
503 doc->SetUseCounter(eUseCounter_custom_onmozmousepixelscroll);
506 break;
507 case eTransitionStart:
508 case eTransitionRun:
509 case eTransitionEnd:
510 case eTransitionCancel:
511 case eWebkitTransitionEnd:
512 mMayHaveTransitionEventListener = true;
513 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
514 window->SetHasTransitionEventListeners();
516 break;
517 case eFormCheckboxStateChange:
518 nsContentUtils::SetMayHaveFormCheckboxStateChangeListeners();
519 break;
520 case eFormRadioStateChange:
521 nsContentUtils::SetMayHaveFormRadioStateChangeListeners();
522 break;
523 default:
524 // XXX Use NS_ASSERTION here to print resolvedEventMessage since
525 // MOZ_ASSERT can take only string literal, not pointer to
526 // characters.
527 NS_ASSERTION(
528 resolvedEventMessage < eLegacyMutationEventFirst ||
529 resolvedEventMessage > eLegacyMutationEventLast,
530 nsPrintfCString("You added new mutation event, but it's not "
531 "handled above, resolvedEventMessage=%s",
532 ToChar(resolvedEventMessage))
533 .get());
534 NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerenter,
535 nsPrintfCString("resolvedEventMessage=%s",
536 ToChar(resolvedEventMessage))
537 .get());
538 NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerleave,
539 nsPrintfCString("resolvedEventMessage=%s",
540 ToChar(resolvedEventMessage))
541 .get());
542 NS_ASSERTION(
543 resolvedEventMessage < eGamepadEventFirst ||
544 resolvedEventMessage > eGamepadEventLast,
545 nsPrintfCString("You added new gamepad event, but it's not "
546 "handled above, resolvedEventMessage=%s",
547 ToChar(resolvedEventMessage))
548 .get());
549 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientation,
550 nsPrintfCString("resolvedEventMessage=%s",
551 ToChar(resolvedEventMessage))
552 .get());
553 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientationabsolute,
554 nsPrintfCString("resolvedEventMessage=%s",
555 ToChar(resolvedEventMessage))
556 .get());
557 NS_ASSERTION(aTypeAtom != nsGkAtoms::onuserproximity,
558 nsPrintfCString("resolvedEventMessage=%s",
559 ToChar(resolvedEventMessage))
560 .get());
561 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicelight,
562 nsPrintfCString("resolvedEventMessage=%s",
563 ToChar(resolvedEventMessage))
564 .get());
565 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicemotion,
566 nsPrintfCString("resolvedEventMessage=%s",
567 ToChar(resolvedEventMessage))
568 .get());
569 #if defined(MOZ_WIDGET_ANDROID)
570 NS_ASSERTION(aTypeAtom != nsGkAtoms::onorientationchange,
571 nsPrintfCString("resolvedEventMessage=%s",
572 ToChar(resolvedEventMessage))
573 .get());
574 #endif // #if defined(MOZ_WIDGET_ANDROID)
575 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchstart,
576 nsPrintfCString("resolvedEventMessage=%s",
577 ToChar(resolvedEventMessage))
578 .get());
579 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchend,
580 nsPrintfCString("resolvedEventMessage=%s",
581 ToChar(resolvedEventMessage))
582 .get());
583 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchmove,
584 nsPrintfCString("resolvedEventMessage=%s",
585 ToChar(resolvedEventMessage))
586 .get());
587 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchcancel,
588 nsPrintfCString("resolvedEventMessage=%s",
589 ToChar(resolvedEventMessage))
590 .get());
591 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseenter,
592 nsPrintfCString("resolvedEventMessage=%s",
593 ToChar(resolvedEventMessage))
594 .get());
595 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseleave,
596 nsPrintfCString("resolvedEventMessage=%s",
597 ToChar(resolvedEventMessage))
598 .get());
599 NS_ASSERTION(aTypeAtom != nsGkAtoms::onbeforeinput,
600 nsPrintfCString("resolvedEventMessage=%s",
601 ToChar(resolvedEventMessage))
602 .get());
603 NS_ASSERTION(aTypeAtom != nsGkAtoms::onselectionchange,
604 nsPrintfCString("resolvedEventMessage=%s",
605 ToChar(resolvedEventMessage))
606 .get());
607 NS_ASSERTION(aTypeAtom != nsGkAtoms::onselect,
608 nsPrintfCString("resolvedEventMessage=%s",
609 ToChar(resolvedEventMessage))
610 .get());
611 NS_ASSERTION(aTypeAtom != nsGkAtoms::onstart,
612 nsPrintfCString("resolvedEventMessage=%s",
613 ToChar(resolvedEventMessage))
614 .get());
615 NS_ASSERTION(aTypeAtom != nsGkAtoms::onbounce,
616 nsPrintfCString("resolvedEventMessage=%s",
617 ToChar(resolvedEventMessage))
618 .get());
619 NS_ASSERTION(aTypeAtom != nsGkAtoms::onfinish,
620 nsPrintfCString("resolvedEventMessage=%s",
621 ToChar(resolvedEventMessage))
622 .get());
623 NS_ASSERTION(aTypeAtom != nsGkAtoms::onoverflow,
624 nsPrintfCString("resolvedEventMessage=%s",
625 ToChar(resolvedEventMessage))
626 .get());
627 NS_ASSERTION(aTypeAtom != nsGkAtoms::onunderflow,
628 nsPrintfCString("resolvedEventMessage=%s",
629 ToChar(resolvedEventMessage))
630 .get());
631 NS_ASSERTION(aTypeAtom != nsGkAtoms::onDOMMouseScroll,
632 nsPrintfCString("resolvedEventMessage=%s",
633 ToChar(resolvedEventMessage))
634 .get());
635 NS_ASSERTION(aTypeAtom != nsGkAtoms::onMozMousePixelScroll,
636 nsPrintfCString("resolvedEventMessage=%s",
637 ToChar(resolvedEventMessage))
638 .get());
639 break;
643 if (mIsMainThreadELM && !aFlags.mPassive && IsApzAwareEvent(aTypeAtom)) {
644 ProcessApzAwareEventListenerAdd();
647 if (mTarget) {
648 mTarget->EventListenerAdded(aTypeAtom);
651 if (mIsMainThreadELM && mTarget) {
652 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
653 aTypeAtom);
657 void EventListenerManager::ProcessApzAwareEventListenerAdd() {
658 Document* doc = nullptr;
660 // Mark the node as having apz aware listeners
661 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
662 node->SetMayBeApzAware();
663 doc = node->OwnerDoc();
666 // Schedule a paint so event regions on the layer tree gets updated
667 if (!doc) {
668 if (nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow()) {
669 doc = window->GetExtantDoc();
672 if (!doc) {
673 if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(mTarget)) {
674 if (nsPIDOMWindowInner* window = helper->GetOwner()) {
675 doc = window->GetExtantDoc();
680 if (doc && gfxPlatform::AsyncPanZoomEnabled()) {
681 PresShell* presShell = doc->GetPresShell();
682 if (presShell) {
683 nsIFrame* f = presShell->GetRootFrame();
684 if (f) {
685 f->SchedulePaint();
691 bool EventListenerManager::IsDeviceType(nsAtom* aTypeAtom) {
692 return aTypeAtom == nsGkAtoms::ondeviceorientation ||
693 aTypeAtom == nsGkAtoms::ondeviceorientationabsolute ||
694 aTypeAtom == nsGkAtoms::ondevicemotion ||
695 aTypeAtom == nsGkAtoms::ondevicelight
696 #if defined(MOZ_WIDGET_ANDROID)
697 || aTypeAtom == nsGkAtoms::onorientationchange
698 #endif
699 || aTypeAtom == nsGkAtoms::onuserproximity;
702 void EventListenerManager::EnableDevice(nsAtom* aTypeAtom) {
703 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
704 if (!window) {
705 return;
708 if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
709 #ifdef MOZ_WIDGET_ANDROID
710 // Falls back to SENSOR_ROTATION_VECTOR and SENSOR_ORIENTATION if
711 // unavailable on device.
712 window->EnableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
713 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
714 #else
715 window->EnableDeviceSensor(SENSOR_ORIENTATION);
716 #endif
717 return;
720 if (aTypeAtom == nsGkAtoms::ondeviceorientationabsolute) {
721 #ifdef MOZ_WIDGET_ANDROID
722 // Falls back to SENSOR_ORIENTATION if unavailable on device.
723 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
724 #else
725 window->EnableDeviceSensor(SENSOR_ORIENTATION);
726 #endif
727 return;
730 if (aTypeAtom == nsGkAtoms::onuserproximity) {
731 window->EnableDeviceSensor(SENSOR_PROXIMITY);
732 return;
735 if (aTypeAtom == nsGkAtoms::ondevicelight) {
736 window->EnableDeviceSensor(SENSOR_LIGHT);
737 return;
740 if (aTypeAtom == nsGkAtoms::ondevicemotion) {
741 window->EnableDeviceSensor(SENSOR_ACCELERATION);
742 window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
743 window->EnableDeviceSensor(SENSOR_GYROSCOPE);
744 return;
747 #if defined(MOZ_WIDGET_ANDROID)
748 if (aTypeAtom == nsGkAtoms::onorientationchange) {
749 window->EnableOrientationChangeListener();
750 return;
752 #endif
754 NS_WARNING("Enabling an unknown device sensor.");
757 void EventListenerManager::DisableDevice(nsAtom* aTypeAtom) {
758 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
759 if (!window) {
760 return;
763 if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
764 #ifdef MOZ_WIDGET_ANDROID
765 // Disable all potential fallback sensors.
766 window->DisableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
767 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
768 #endif
769 window->DisableDeviceSensor(SENSOR_ORIENTATION);
770 return;
773 if (aTypeAtom == nsGkAtoms::ondeviceorientationabsolute) {
774 #ifdef MOZ_WIDGET_ANDROID
775 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
776 #endif
777 window->DisableDeviceSensor(SENSOR_ORIENTATION);
778 return;
781 if (aTypeAtom == nsGkAtoms::ondevicemotion) {
782 window->DisableDeviceSensor(SENSOR_ACCELERATION);
783 window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
784 window->DisableDeviceSensor(SENSOR_GYROSCOPE);
785 return;
788 if (aTypeAtom == nsGkAtoms::onuserproximity) {
789 window->DisableDeviceSensor(SENSOR_PROXIMITY);
790 return;
793 if (aTypeAtom == nsGkAtoms::ondevicelight) {
794 window->DisableDeviceSensor(SENSOR_LIGHT);
795 return;
798 #if defined(MOZ_WIDGET_ANDROID)
799 if (aTypeAtom == nsGkAtoms::onorientationchange) {
800 window->DisableOrientationChangeListener();
801 return;
803 #endif
805 NS_WARNING("Disabling an unknown device sensor.");
808 void EventListenerManager::NotifyEventListenerRemoved(nsAtom* aUserType) {
809 // If the following code is changed, other callsites of EventListenerRemoved
810 // and NotifyAboutMainThreadListenerChange should be changed too.
811 ClearNoListenersForEvents();
812 mNoListenerForEventAtom = nullptr;
813 if (mTarget) {
814 mTarget->EventListenerRemoved(aUserType);
816 if (mIsMainThreadELM && mTarget) {
817 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
818 aUserType);
822 void EventListenerManager::RemoveEventListenerInternal(
823 EventListenerHolder aListenerHolder, nsAtom* aUserType,
824 const EventListenerFlags& aFlags, bool aAllEvents) {
825 if (!aListenerHolder || (!aUserType && !aAllEvents) || mClearingListeners) {
826 return;
829 Maybe<size_t> entryIndex = aAllEvents
830 ? mListenerMap.EntryIndexForAllEvents()
831 : mListenerMap.EntryIndexForType(aUserType);
832 if (!entryIndex) {
833 return;
836 ListenerArray& listenerArray = *mListenerMap.mEntries[*entryIndex].mListeners;
838 Maybe<uint32_t> listenerIndex = [&]() -> Maybe<uint32_t> {
839 uint32_t count = listenerArray.Length();
840 for (uint32_t i = 0; i < count; ++i) {
841 Listener* listener = &listenerArray.ElementAt(i);
842 if (listener->mListener == aListenerHolder &&
843 listener->mFlags.EqualsForRemoval(aFlags)) {
844 return Some(i);
847 return Nothing();
848 }();
850 if (!listenerIndex) {
851 return;
854 listenerArray.RemoveElementAt(*listenerIndex);
855 if (listenerArray.IsEmpty()) {
856 mListenerMap.mEntries.RemoveElementAt(*entryIndex);
859 RefPtr<EventListenerManager> kungFuDeathGrip(this);
860 if (!aAllEvents) {
861 NotifyEventListenerRemoved(aUserType);
862 if (IsDeviceType(aUserType)) {
863 DisableDevice(aUserType);
868 static bool IsDefaultPassiveWhenOnRoot(EventMessage aMessage) {
869 if (aMessage == eTouchStart || aMessage == eTouchMove || aMessage == eWheel ||
870 aMessage == eLegacyMouseLineOrPageScroll ||
871 aMessage == eLegacyMousePixelScroll) {
872 return true;
874 return false;
877 static bool IsRootEventTarget(EventTarget* aTarget) {
878 if (!aTarget) {
879 return false;
881 if (aTarget->IsInnerWindow()) {
882 return true;
884 const nsINode* node = nsINode::FromEventTarget(aTarget);
885 if (!node) {
886 return false;
888 Document* doc = node->OwnerDoc();
889 return node == doc || node == doc->GetRootElement() || node == doc->GetBody();
892 void EventListenerManager::MaybeMarkPassive(EventMessage aMessage,
893 EventListenerFlags& aFlags) {
894 if (!mIsMainThreadELM) {
895 return;
897 if (!IsDefaultPassiveWhenOnRoot(aMessage)) {
898 return;
900 if (!IsRootEventTarget(mTarget)) {
901 return;
903 aFlags.mPassive = true;
906 void EventListenerManager::AddEventListenerByType(
907 EventListenerHolder aListenerHolder, const nsAString& aType,
908 const EventListenerFlags& aFlags, const Optional<bool>& aPassive,
909 AbortSignal* aSignal) {
910 RefPtr<nsAtom> atom;
911 EventMessage message =
912 GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
914 EventListenerFlags flags = aFlags;
915 if (aPassive.WasPassed()) {
916 flags.mPassive = aPassive.Value();
917 } else {
918 MaybeMarkPassive(message, flags);
921 AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags,
922 false, false, aSignal);
925 void EventListenerManager::RemoveEventListenerByType(
926 EventListenerHolder aListenerHolder, const nsAString& aType,
927 const EventListenerFlags& aFlags) {
928 RefPtr<nsAtom> atom;
929 (void)GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
930 RemoveEventListenerInternal(std::move(aListenerHolder), atom, aFlags);
933 EventListenerManager::Listener* EventListenerManager::FindEventHandler(
934 nsAtom* aTypeAtom) {
935 // Run through the listeners for this type and see if a script
936 // listener is registered
937 RefPtr<ListenerArray> listeners = mListenerMap.GetListenersForType(aTypeAtom);
938 if (!listeners) {
939 return nullptr;
942 uint32_t count = listeners->Length();
943 for (uint32_t i = 0; i < count; ++i) {
944 Listener* listener = &listeners->ElementAt(i);
945 if (listener->mListenerIsHandler) {
946 return listener;
949 return nullptr;
952 EventListenerManager::Listener* EventListenerManager::SetEventHandlerInternal(
953 nsAtom* aName, const TypedEventHandler& aTypedHandler,
954 bool aPermitUntrustedEvents) {
955 MOZ_ASSERT(aName);
957 EventMessage eventMessage = GetEventMessage(aName);
958 Listener* listener = FindEventHandler(aName);
960 if (!listener) {
961 // If we didn't find a script listener or no listeners existed
962 // create and add a new one.
963 EventListenerFlags flags;
964 flags.mListenerIsJSListener = true;
965 MaybeMarkPassive(eventMessage, flags);
967 nsCOMPtr<JSEventHandler> jsEventHandler;
968 NS_NewJSEventHandler(mTarget, aName, aTypedHandler,
969 getter_AddRefs(jsEventHandler));
970 AddEventListenerInternal(EventListenerHolder(jsEventHandler), eventMessage,
971 aName, flags, true);
973 listener = FindEventHandler(aName);
974 } else {
975 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
976 MOZ_ASSERT(jsEventHandler,
977 "How can we have an event handler with no JSEventHandler?");
979 bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler;
980 // Possibly the same listener, but update still the context and scope.
981 jsEventHandler->SetHandler(aTypedHandler);
982 if (mTarget && !same) {
983 mTarget->EventListenerRemoved(aName);
984 mTarget->EventListenerAdded(aName);
986 if (mIsMainThreadELM && mTarget) {
987 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, aName);
991 // Set flag to indicate possible need for compilation later
992 listener->mHandlerIsString = !aTypedHandler.HasEventHandler();
993 if (aPermitUntrustedEvents) {
994 listener->mFlags.mAllowUntrustedEvents = true;
995 mMayHaveListenersForUntrustedEvents = true;
998 return listener;
1001 nsresult EventListenerManager::SetEventHandler(nsAtom* aName,
1002 const nsAString& aBody,
1003 bool aDeferCompilation,
1004 bool aPermitUntrustedEvents,
1005 Element* aElement) {
1006 auto removeEventHandler = MakeScopeExit([&] { RemoveEventHandler(aName); });
1008 nsCOMPtr<Document> doc;
1009 nsCOMPtr<nsIScriptGlobalObject> global =
1010 GetScriptGlobalAndDocument(getter_AddRefs(doc));
1012 if (!global) {
1013 // This can happen; for example this document might have been
1014 // loaded as data.
1015 return NS_OK;
1018 nsresult rv = NS_OK;
1019 // return early preventing the event listener from being added
1020 // 'doc' is fetched above
1021 if (doc) {
1022 // Don't allow adding an event listener if the document is sandboxed
1023 // without 'allow-scripts'.
1024 if (doc->HasScriptsBlockedBySandbox()) {
1025 return NS_ERROR_DOM_SECURITY_ERR;
1028 // Perform CSP check
1029 nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
1030 uint32_t lineNum = 0;
1031 JS::ColumnNumberOneOrigin columnNum;
1033 JSContext* cx = nsContentUtils::GetCurrentJSContext();
1034 if (cx && !JS::DescribeScriptedCaller(cx, nullptr, &lineNum, &columnNum)) {
1035 JS_ClearPendingException(cx);
1038 if (csp) {
1039 bool allowsInlineScript = true;
1040 rv = csp->GetAllowsInline(
1041 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE,
1042 true, // aHasUnsafeHash
1043 u""_ns, // aNonce
1044 true, // aParserCreated (true because attribute event handler)
1045 aElement,
1046 nullptr, // nsICSPEventListener
1047 aBody, lineNum, columnNum.oneOriginValue(), &allowsInlineScript);
1048 NS_ENSURE_SUCCESS(rv, rv);
1050 // return early if CSP wants us to block inline scripts
1051 if (!allowsInlineScript) {
1052 return NS_OK;
1057 // This might be the first reference to this language in the global
1058 // We must init the language before we attempt to fetch its context.
1059 if (NS_FAILED(global->EnsureScriptEnvironment())) {
1060 NS_WARNING("Failed to setup script environment for this language");
1061 // but fall through and let the inevitable failure below handle it.
1064 nsIScriptContext* context = global->GetScriptContext();
1065 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
1066 NS_ENSURE_STATE(global->HasJSGlobal());
1068 removeEventHandler.release();
1070 Listener* listener = SetEventHandlerInternal(aName, TypedEventHandler(),
1071 aPermitUntrustedEvents);
1073 if (!aDeferCompilation) {
1074 return CompileEventHandlerInternal(listener, aName, &aBody, aElement);
1077 return NS_OK;
1080 void EventListenerManager::RemoveEventHandler(nsAtom* aName) {
1081 if (mClearingListeners) {
1082 return;
1085 Maybe<size_t> entryIndex = mListenerMap.EntryIndexForType(aName);
1086 if (!entryIndex) {
1087 return;
1090 ListenerArray& listenerArray = *mListenerMap.mEntries[*entryIndex].mListeners;
1092 Maybe<uint32_t> listenerIndex = [&]() -> Maybe<uint32_t> {
1093 uint32_t count = listenerArray.Length();
1094 for (uint32_t i = 0; i < count; ++i) {
1095 Listener* listener = &listenerArray.ElementAt(i);
1096 if (listener->mListenerIsHandler) {
1097 return Some(i);
1100 return Nothing();
1101 }();
1103 if (!listenerIndex) {
1104 return;
1107 listenerArray.RemoveElementAt(*listenerIndex);
1108 if (listenerArray.IsEmpty()) {
1109 mListenerMap.mEntries.RemoveElementAt(*entryIndex);
1112 RefPtr<EventListenerManager> kungFuDeathGrip(this);
1113 NotifyEventListenerRemoved(aName);
1114 if (IsDeviceType(aName)) {
1115 DisableDevice(aName);
1119 nsresult EventListenerManager::CompileEventHandlerInternal(
1120 Listener* aListener, nsAtom* aTypeAtom, const nsAString* aBody,
1121 Element* aElement) {
1122 MOZ_ASSERT(aListener->GetJSEventHandler());
1123 MOZ_ASSERT(aListener->mHandlerIsString,
1124 "Why are we compiling a non-string JS listener?");
1125 JSEventHandler* jsEventHandler = aListener->GetJSEventHandler();
1126 MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(),
1127 "What is there to compile?");
1129 nsresult result = NS_OK;
1130 nsCOMPtr<Document> doc;
1131 nsCOMPtr<nsIScriptGlobalObject> global =
1132 GetScriptGlobalAndDocument(getter_AddRefs(doc));
1133 NS_ENSURE_STATE(global);
1135 // Activate JSAPI, and make sure that exceptions are reported on the right
1136 // Window.
1137 AutoJSAPI jsapi;
1138 if (NS_WARN_IF(!jsapi.Init(global))) {
1139 return NS_ERROR_UNEXPECTED;
1141 JSContext* cx = jsapi.cx();
1143 nsAtom* attrName = aTypeAtom;
1145 // Flag us as not a string so we don't keep trying to compile strings which
1146 // can't be compiled.
1147 aListener->mHandlerIsString = false;
1149 // mTarget may not be an Element if it's a window and we're
1150 // getting an inline event listener forwarded from <html:body> or
1151 // <html:frameset> or <xul:window> or the like.
1152 // XXX I don't like that we have to reference content from
1153 // here. The alternative is to store the event handler string on
1154 // the JSEventHandler itself, and that still doesn't address
1155 // the arg names issue.
1156 RefPtr<Element> element = Element::FromEventTargetOrNull(mTarget);
1157 MOZ_ASSERT(element || aBody, "Where will we get our body?");
1158 nsAutoString handlerBody;
1159 const nsAString* body = aBody;
1160 if (!aBody) {
1161 if (aTypeAtom == nsGkAtoms::onSVGLoad) {
1162 attrName = nsGkAtoms::onload;
1163 } else if (aTypeAtom == nsGkAtoms::onSVGScroll) {
1164 attrName = nsGkAtoms::onscroll;
1165 } else if (aTypeAtom == nsGkAtoms::onbeginEvent) {
1166 attrName = nsGkAtoms::onbegin;
1167 } else if (aTypeAtom == nsGkAtoms::onrepeatEvent) {
1168 attrName = nsGkAtoms::onrepeat;
1169 } else if (aTypeAtom == nsGkAtoms::onendEvent) {
1170 attrName = nsGkAtoms::onend;
1171 } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationEnd) {
1172 attrName = nsGkAtoms::onwebkitanimationend;
1173 } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationIteration) {
1174 attrName = nsGkAtoms::onwebkitanimationiteration;
1175 } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationStart) {
1176 attrName = nsGkAtoms::onwebkitanimationstart;
1177 } else if (aTypeAtom == nsGkAtoms::onwebkitTransitionEnd) {
1178 attrName = nsGkAtoms::onwebkittransitionend;
1181 element->GetAttr(attrName, handlerBody);
1182 body = &handlerBody;
1183 aElement = element;
1185 aListener = nullptr;
1187 nsAutoCString url("-moz-evil:lying-event-listener"_ns);
1188 MOZ_ASSERT(body);
1189 MOZ_ASSERT(aElement);
1190 nsIURI* uri = aElement->OwnerDoc()->GetDocumentURI();
1191 if (uri) {
1192 uri->GetSpec(url);
1195 nsCOMPtr<nsPIDOMWindowInner> win =
1196 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget);
1197 uint32_t argCount;
1198 const char** argNames;
1199 nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), aTypeAtom, win,
1200 &argCount, &argNames);
1202 // Wrap the event target, so that we can use it as the scope for the event
1203 // handler. Note that mTarget is different from aElement in the <body> case,
1204 // where mTarget is a Window.
1206 // The wrapScope doesn't really matter here, because the target will create
1207 // its reflector in the proper scope, and then we'll enter that realm.
1208 JS::Rooted<JSObject*> wrapScope(cx, global->GetGlobalJSObject());
1209 JS::Rooted<JS::Value> v(cx);
1211 JSAutoRealm ar(cx, wrapScope);
1212 nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v,
1213 /* aAllowWrapping = */ false);
1214 if (NS_WARN_IF(NS_FAILED(rv))) {
1215 return rv;
1219 JS::Rooted<JSObject*> target(cx, &v.toObject());
1220 JSAutoRealm ar(cx, target);
1222 // Now that we've entered the realm we actually care about, create our
1223 // scope chain. Note that we start with |element|, not aElement, because
1224 // mTarget is different from aElement in the <body> case, where mTarget is a
1225 // Window, and in that case we do not want the scope chain to include the body
1226 // or the document.
1227 JS::RootedVector<JSObject*> scopeChain(cx);
1228 if (!nsJSUtils::GetScopeChainForElement(cx, element, &scopeChain)) {
1229 return NS_ERROR_OUT_OF_MEMORY;
1232 nsDependentAtomString str(attrName);
1233 // Most of our names are short enough that we don't even have to malloc
1234 // the JS string stuff, so don't worry about playing games with
1235 // refcounting XPCOM stringbuffers.
1236 JS::Rooted<JSString*> jsStr(
1237 cx, JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length()));
1238 NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY);
1240 // Get the reflector for |aElement|, so that we can pass to setElement.
1241 if (NS_WARN_IF(!GetOrCreateDOMReflector(cx, aElement, &v))) {
1242 return NS_ERROR_FAILURE;
1245 RefPtr<JS::loader::ScriptFetchOptions> fetchOptions =
1246 new JS::loader::ScriptFetchOptions(
1247 CORS_NONE, /* aNonce = */ u""_ns, RequestPriority::Auto,
1248 JS::loader::ParserMetadata::NotParserInserted,
1249 aElement->OwnerDoc()->NodePrincipal());
1251 RefPtr<JS::loader::EventScript> eventScript = new JS::loader::EventScript(
1252 aElement->OwnerDoc()->GetReferrerPolicy(), fetchOptions, uri);
1254 JS::CompileOptions options(cx);
1255 // Use line 0 to make the function body starts from line 1.
1256 options.setIntroductionType("eventHandler")
1257 .setFileAndLine(url.get(), 0)
1258 .setDeferDebugMetadata(true);
1260 JS::Rooted<JSObject*> handler(cx);
1261 result = nsJSUtils::CompileFunction(jsapi, scopeChain, options,
1262 nsAtomCString(aTypeAtom), argCount,
1263 argNames, *body, handler.address());
1264 NS_ENSURE_SUCCESS(result, result);
1265 NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
1267 JS::Rooted<JS::Value> privateValue(cx, JS::PrivateValue(eventScript));
1268 result = nsJSUtils::UpdateFunctionDebugMetadata(jsapi, handler, options,
1269 jsStr, privateValue);
1270 NS_ENSURE_SUCCESS(result, result);
1272 MOZ_ASSERT(js::IsObjectInContextCompartment(handler, cx));
1273 JS::Rooted<JSObject*> handlerGlobal(cx, JS::CurrentGlobalOrNull(cx));
1275 if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) {
1276 RefPtr<OnErrorEventHandlerNonNull> handlerCallback =
1277 new OnErrorEventHandlerNonNull(static_cast<JSContext*>(nullptr),
1278 handler, handlerGlobal,
1279 /* aIncumbentGlobal = */ nullptr);
1280 jsEventHandler->SetHandler(handlerCallback);
1281 } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) {
1282 RefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
1283 new OnBeforeUnloadEventHandlerNonNull(static_cast<JSContext*>(nullptr),
1284 handler, handlerGlobal,
1285 /* aIncumbentGlobal = */ nullptr);
1286 jsEventHandler->SetHandler(handlerCallback);
1287 } else {
1288 RefPtr<EventHandlerNonNull> handlerCallback = new EventHandlerNonNull(
1289 static_cast<JSContext*>(nullptr), handler, handlerGlobal,
1290 /* aIncumbentGlobal = */ nullptr);
1291 jsEventHandler->SetHandler(handlerCallback);
1294 return result;
1297 bool EventListenerManager::HandleEventSingleListener(
1298 Listener* aListener, nsAtom* aTypeAtom, WidgetEvent* aEvent,
1299 Event* aDOMEvent, EventTarget* aCurrentTarget, bool aItemInShadowTree) {
1300 if (!aEvent->mCurrentTarget) {
1301 aEvent->mCurrentTarget = aCurrentTarget->GetTargetForDOMEvent();
1302 if (!aEvent->mCurrentTarget) {
1303 return false;
1307 aEvent->mFlags.mInPassiveListener = aListener->mFlags.mPassive;
1309 nsCOMPtr<nsPIDOMWindowInner> innerWindow =
1310 WindowFromListener(aListener, aTypeAtom, aItemInShadowTree);
1311 mozilla::dom::Event* oldWindowEvent = nullptr;
1312 if (innerWindow) {
1313 oldWindowEvent = innerWindow->SetEvent(aDOMEvent);
1316 nsresult result = NS_OK;
1318 // strong ref
1319 EventListenerHolder listenerHolder(aListener->mListener.Clone());
1321 // If this is a script handler and we haven't yet
1322 // compiled the event handler itself
1323 if ((aListener->mListenerType == Listener::eJSEventListener) &&
1324 aListener->mHandlerIsString) {
1325 result =
1326 CompileEventHandlerInternal(aListener, aTypeAtom, nullptr, nullptr);
1327 aListener = nullptr;
1330 if (NS_SUCCEEDED(result)) {
1331 Maybe<EventCallbackDebuggerNotificationGuard> dbgGuard;
1332 if (dom::ChromeUtils::IsDevToolsOpened()) {
1333 dbgGuard.emplace(aCurrentTarget, aDOMEvent);
1335 nsAutoMicroTask mt;
1337 // Event::currentTarget is set in EventDispatcher.
1338 if (listenerHolder.HasWebIDLCallback()) {
1339 ErrorResult rv;
1340 listenerHolder.GetWebIDLCallback()->HandleEvent(aCurrentTarget,
1341 *aDOMEvent, rv);
1342 result = rv.StealNSResult();
1343 } else {
1344 // listenerHolder is holding a stack ref here.
1345 result = MOZ_KnownLive(listenerHolder.GetXPCOMCallback())
1346 ->HandleEvent(aDOMEvent);
1350 if (innerWindow) {
1351 Unused << innerWindow->SetEvent(oldWindowEvent);
1354 if (NS_FAILED(result)) {
1355 aEvent->mFlags.mExceptionWasRaised = true;
1357 aEvent->mFlags.mInPassiveListener = false;
1358 return !aEvent->mFlags.mImmediatePropagationStopped;
1361 /* static */ EventMessage EventListenerManager::GetLegacyEventMessage(
1362 EventMessage aEventMessage) {
1363 // webkit-prefixed legacy events:
1364 if (aEventMessage == eTransitionEnd) {
1365 return eWebkitTransitionEnd;
1367 if (aEventMessage == eAnimationStart) {
1368 return eWebkitAnimationStart;
1370 if (aEventMessage == eAnimationEnd) {
1371 return eWebkitAnimationEnd;
1373 if (aEventMessage == eAnimationIteration) {
1374 return eWebkitAnimationIteration;
1377 switch (aEventMessage) {
1378 case eFullscreenChange:
1379 return eMozFullscreenChange;
1380 case eFullscreenError:
1381 return eMozFullscreenError;
1382 default:
1383 return aEventMessage;
1387 EventMessage EventListenerManager::GetEventMessage(nsAtom* aEventName) const {
1388 if (mIsMainThreadELM) {
1389 return nsContentUtils::GetEventMessage(aEventName);
1392 // The nsContentUtils event message hashtables aren't threadsafe, so just fall
1393 // back to eUnidentifiedEvent.
1394 return eUnidentifiedEvent;
1397 EventMessage EventListenerManager::GetEventMessageAndAtomForListener(
1398 const nsAString& aType, nsAtom** aAtom) {
1399 if (mIsMainThreadELM) {
1400 return nsContentUtils::GetEventMessageAndAtomForListener(aType, aAtom);
1403 *aAtom = NS_Atomize(u"on"_ns + aType).take();
1404 return eUnidentifiedEvent;
1407 already_AddRefed<nsPIDOMWindowInner> EventListenerManager::WindowFromListener(
1408 Listener* aListener, nsAtom* aTypeAtom, bool aItemInShadowTree) {
1409 nsCOMPtr<nsPIDOMWindowInner> innerWindow;
1410 if (!aItemInShadowTree) {
1411 if (aListener->mListener.HasWebIDLCallback()) {
1412 CallbackObject* callback = aListener->mListener.GetWebIDLCallback();
1413 nsIGlobalObject* global = nullptr;
1414 if (callback) {
1415 global = callback->IncumbentGlobalOrNull();
1417 if (global) {
1418 innerWindow = global->GetAsInnerWindow(); // Can be nullptr
1420 } else {
1421 // This ensures `window.event` can be set properly for
1422 // nsWindowRoot to handle KeyPress event.
1423 if (aListener && aTypeAtom == nsGkAtoms::onkeypress && mTarget &&
1424 mTarget->IsRootWindow()) {
1425 nsPIWindowRoot* root = mTarget->AsWindowRoot();
1426 if (nsPIDOMWindowOuter* outerWindow = root->GetWindow()) {
1427 innerWindow = outerWindow->GetCurrentInnerWindow();
1429 } else {
1430 // Can't get the global from
1431 // listener->mListener.GetXPCOMCallback().
1432 // In most cases, it would be the same as for
1433 // the target, so let's do that.
1434 innerWindow = GetInnerWindowForTarget(); // Can be nullptr
1438 return innerWindow.forget();
1441 Maybe<size_t> EventListenerManager::EventListenerMap::EntryIndexForType(
1442 nsAtom* aTypeAtom) const {
1443 MOZ_ASSERT(aTypeAtom);
1445 size_t matchIndexOrInsertionPoint = 0;
1446 bool foundMatch = BinarySearchIf(mEntries, 0, mEntries.Length(),
1447 ListenerMapEntryComparator(aTypeAtom),
1448 &matchIndexOrInsertionPoint);
1449 return foundMatch ? Some(matchIndexOrInsertionPoint) : Nothing();
1452 Maybe<size_t> EventListenerManager::EventListenerMap::EntryIndexForAllEvents()
1453 const {
1454 // If we have an entry for "all events listeners", it'll be at the beginning
1455 // of the list and its type atom will be null.
1456 return !mEntries.IsEmpty() && mEntries[0].mTypeAtom == nullptr ? Some(0)
1457 : Nothing();
1460 RefPtr<EventListenerManager::ListenerArray>
1461 EventListenerManager::EventListenerMap::GetListenersForType(
1462 nsAtom* aTypeAtom) const {
1463 Maybe<size_t> index = EntryIndexForType(aTypeAtom);
1464 return index ? mEntries[*index].mListeners : nullptr;
1467 RefPtr<EventListenerManager::ListenerArray>
1468 EventListenerManager::EventListenerMap::GetListenersForAllEvents() const {
1469 Maybe<size_t> index = EntryIndexForAllEvents();
1470 return index ? mEntries[*index].mListeners : nullptr;
1473 RefPtr<EventListenerManager::ListenerArray>
1474 EventListenerManager::EventListenerMap::GetOrCreateListenersForType(
1475 nsAtom* aTypeAtom) {
1476 MOZ_ASSERT(aTypeAtom);
1477 size_t matchIndexOrInsertionPoint = 0;
1478 bool foundMatch = BinarySearchIf(mEntries, 0, mEntries.Length(),
1479 ListenerMapEntryComparator(aTypeAtom),
1480 &matchIndexOrInsertionPoint);
1481 if (foundMatch) {
1482 return mEntries[matchIndexOrInsertionPoint].mListeners;
1484 RefPtr<ListenerArray> listeners = MakeRefPtr<ListenerArray>();
1485 mEntries.InsertElementAt(matchIndexOrInsertionPoint,
1486 EventListenerMapEntry{aTypeAtom, listeners});
1488 return listeners;
1491 RefPtr<EventListenerManager::ListenerArray>
1492 EventListenerManager::EventListenerMap::GetOrCreateListenersForAllEvents() {
1493 RefPtr<ListenerArray> listeners = GetListenersForAllEvents();
1494 if (!listeners) {
1495 listeners = MakeRefPtr<ListenerArray>();
1496 mEntries.InsertElementAt(0, EventListenerMapEntry{nullptr, listeners});
1498 return listeners;
1501 void EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
1502 WidgetEvent* aEvent,
1503 Event** aDOMEvent,
1504 EventTarget* aCurrentTarget,
1505 nsEventStatus* aEventStatus,
1506 bool aItemInShadowTree) {
1507 MOZ_ASSERT_IF(aEvent->mMessage != eUnidentifiedEvent, mIsMainThreadELM);
1509 // Set the value of the internal PreventDefault flag properly based on
1510 // aEventStatus
1511 if (!aEvent->DefaultPrevented() &&
1512 *aEventStatus == nsEventStatus_eConsumeNoDefault) {
1513 // Assume that if only aEventStatus claims that the event has already been
1514 // consumed, the consumer is default event handler.
1515 aEvent->PreventDefault();
1518 if (aEvent->mFlags.mImmediatePropagationStopped) {
1519 return;
1522 Maybe<AutoHandlingUserInputStatePusher> userInputStatePusher;
1523 Maybe<AutoPopupStatePusher> popupStatePusher;
1524 if (mIsMainThreadELM) {
1525 userInputStatePusher.emplace(UserActivation::IsUserInteractionEvent(aEvent),
1526 aEvent);
1527 popupStatePusher.emplace(
1528 PopupBlocker::GetEventPopupControlState(aEvent, *aDOMEvent));
1531 EventMessage eventMessage = aEvent->mMessage;
1532 RefPtr<nsAtom> typeAtom =
1533 eventMessage == eUnidentifiedEvent
1534 ? aEvent->mSpecifiedEventType.get()
1535 : nsContentUtils::GetEventTypeFromMessage(eventMessage);
1536 if (!typeAtom) {
1537 // Some messages don't have a corresponding type atom, e.g.
1538 // eMouseEnterIntoWidget. These events can't have a listener, so we
1539 // can stop here.
1540 return;
1543 bool hasAnyListenerForEventType = false;
1545 // First, notify any "all events" listeners.
1546 if (RefPtr<ListenerArray> listenersForAllEvents =
1547 mListenerMap.GetListenersForAllEvents()) {
1548 HandleEventWithListenerArray(listenersForAllEvents, typeAtom, eventMessage,
1549 aPresContext, aEvent, aDOMEvent,
1550 aCurrentTarget, aItemInShadowTree);
1551 hasAnyListenerForEventType = true;
1554 // Now look for listeners for typeAtom, and call them if we have any.
1555 bool hasAnyListenerMatchingGroup = false;
1556 if (RefPtr<ListenerArray> listeners =
1557 mListenerMap.GetListenersForType(typeAtom)) {
1558 hasAnyListenerMatchingGroup = HandleEventWithListenerArray(
1559 listeners, typeAtom, eventMessage, aPresContext, aEvent, aDOMEvent,
1560 aCurrentTarget, aItemInShadowTree);
1561 hasAnyListenerForEventType = true;
1564 if (!hasAnyListenerMatchingGroup && aEvent->IsTrusted()) {
1565 // If we didn't find any matching listeners, and our event has a legacy
1566 // version, check the listeners for the legacy version.
1567 EventMessage legacyEventMessage = GetLegacyEventMessage(eventMessage);
1568 if (legacyEventMessage != eventMessage) {
1569 MOZ_ASSERT(
1570 GetLegacyEventMessage(legacyEventMessage) == legacyEventMessage,
1571 "Legacy event messages should not themselves have legacy versions");
1572 RefPtr<nsAtom> legacyTypeAtom =
1573 nsContentUtils::GetEventTypeFromMessage(legacyEventMessage);
1574 if (RefPtr<ListenerArray> legacyListeners =
1575 mListenerMap.GetListenersForType(legacyTypeAtom)) {
1576 HandleEventWithListenerArray(
1577 legacyListeners, legacyTypeAtom, legacyEventMessage, aPresContext,
1578 aEvent, aDOMEvent, aCurrentTarget, aItemInShadowTree);
1579 hasAnyListenerForEventType = true;
1584 aEvent->mCurrentTarget = nullptr;
1586 if (mIsMainThreadELM && !hasAnyListenerForEventType) {
1587 if (aEvent->mMessage != eUnidentifiedEvent) {
1588 mNoListenerForEvents[2] = mNoListenerForEvents[1];
1589 mNoListenerForEvents[1] = mNoListenerForEvents[0];
1590 mNoListenerForEvents[0] = aEvent->mMessage;
1591 } else {
1592 mNoListenerForEventAtom = aEvent->mSpecifiedEventType;
1596 if (aEvent->DefaultPrevented()) {
1597 *aEventStatus = nsEventStatus_eConsumeNoDefault;
1601 bool EventListenerManager::HandleEventWithListenerArray(
1602 ListenerArray* aListeners, nsAtom* aTypeAtom, EventMessage aEventMessage,
1603 nsPresContext* aPresContext, WidgetEvent* aEvent, Event** aDOMEvent,
1604 EventTarget* aCurrentTarget, bool aItemInShadowTree) {
1605 auto ensureDOMEvent = [&]() {
1606 if (!*aDOMEvent) {
1607 // Lazily create the DOM event.
1608 // This is tiny bit slow, but happens only once per event.
1609 // Similar code also in EventDispatcher.
1610 nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget;
1611 RefPtr<Event> event =
1612 EventDispatcher::CreateEvent(et, aPresContext, aEvent, u""_ns);
1613 event.forget(aDOMEvent);
1615 return *aDOMEvent != nullptr;
1618 Maybe<EventMessageAutoOverride> eventMessageAutoOverride;
1619 bool isOverridingEventMessage = aEvent->mMessage != aEventMessage;
1620 bool hasAnyListenerMatchingGroup = false;
1621 bool didReplaceOnceListener = false;
1623 for (Listener& listenerRef : aListeners->EndLimitedRange()) {
1624 Listener* listener = &listenerRef;
1625 if (listener->mListenerType == Listener::eNoListener) {
1626 // The listener is a placeholder value of a removed "once" listener.
1627 continue;
1629 if (!listener->mEnabled) {
1630 // The listener has been disabled, for example by devtools.
1631 continue;
1633 if (!listener->MatchesEventGroup(aEvent)) {
1634 continue;
1636 hasAnyListenerMatchingGroup = true;
1638 // Check that the phase is same in event and event listener. Also check
1639 // that the event is trusted or that the listener allows untrusted events.
1640 if (!listener->MatchesEventPhase(aEvent) ||
1641 !listener->AllowsEventTrustedness(aEvent)) {
1642 continue;
1645 Maybe<Listener> listenerHolder;
1646 if (listener->mFlags.mOnce) {
1647 // Move the listener to the stack before handling the event.
1648 // The order is important, otherwise the listener could be
1649 // called again inside the listener.
1650 listenerHolder.emplace(std::move(*listener));
1651 listener = listenerHolder.ptr();
1652 didReplaceOnceListener = true;
1654 if (ensureDOMEvent()) {
1655 if (isOverridingEventMessage && !eventMessageAutoOverride) {
1656 // Override the domEvent's event-message (its .type) until we
1657 // finish traversing listeners (when eventMessageAutoOverride
1658 // destructs).
1659 eventMessageAutoOverride.emplace(*aDOMEvent, aEventMessage);
1661 if (!HandleEventSingleListener(listener, aTypeAtom, aEvent, *aDOMEvent,
1662 aCurrentTarget, aItemInShadowTree)) {
1663 break;
1668 if (didReplaceOnceListener) {
1669 // If there are any once listeners replaced with a placeholder during the
1670 // loop above, we need to clean up them here. Note that this could clear
1671 // once listeners handled in some outer level as well, but that should not
1672 // affect the result.
1673 size_t oldLength = aListeners->Length();
1674 aListeners->NonObservingRemoveElementsBy([](const Listener& aListener) {
1675 return aListener.mListenerType == Listener::eNoListener;
1677 size_t newLength = aListeners->Length();
1678 if (newLength == 0) {
1679 // Remove the entry that has now become empty.
1680 mListenerMap.mEntries.RemoveElementsBy([](EventListenerMapEntry& entry) {
1681 return entry.mListeners->IsEmpty();
1684 if (newLength < oldLength) {
1685 // Call NotifyEventListenerRemoved once for every removed listener.
1686 size_t removedCount = oldLength - newLength;
1687 for (size_t i = 0; i < removedCount; i++) {
1688 NotifyEventListenerRemoved(aTypeAtom);
1690 if (IsDeviceType(aTypeAtom)) {
1691 // Call DisableDevice once for every removed listener.
1692 for (size_t i = 0; i < removedCount; i++) {
1693 DisableDevice(aTypeAtom);
1699 return hasAnyListenerMatchingGroup;
1702 void EventListenerManager::Disconnect() {
1703 mTarget = nullptr;
1704 RemoveAllListenersSilently();
1707 void EventListenerManager::AddEventListener(const nsAString& aType,
1708 EventListenerHolder aListenerHolder,
1709 bool aUseCapture,
1710 bool aWantsUntrusted) {
1711 EventListenerFlags flags;
1712 flags.mCapture = aUseCapture;
1713 flags.mAllowUntrustedEvents = aWantsUntrusted;
1714 return AddEventListenerByType(std::move(aListenerHolder), aType, flags);
1717 void EventListenerManager::AddEventListener(
1718 const nsAString& aType, EventListenerHolder aListenerHolder,
1719 const dom::AddEventListenerOptionsOrBoolean& aOptions,
1720 bool aWantsUntrusted) {
1721 EventListenerFlags flags;
1722 Optional<bool> passive;
1723 AbortSignal* signal = nullptr;
1724 if (aOptions.IsBoolean()) {
1725 flags.mCapture = aOptions.GetAsBoolean();
1726 } else {
1727 const auto& options = aOptions.GetAsAddEventListenerOptions();
1728 flags.mCapture = options.mCapture;
1729 flags.mInSystemGroup = options.mMozSystemGroup;
1730 flags.mOnce = options.mOnce;
1731 if (options.mPassive.WasPassed()) {
1732 passive.Construct(options.mPassive.Value());
1735 if (options.mSignal.WasPassed()) {
1736 signal = &options.mSignal.Value();
1740 flags.mAllowUntrustedEvents = aWantsUntrusted;
1741 return AddEventListenerByType(std::move(aListenerHolder), aType, flags,
1742 passive, signal);
1745 void EventListenerManager::RemoveEventListener(
1746 const nsAString& aType, EventListenerHolder aListenerHolder,
1747 bool aUseCapture) {
1748 EventListenerFlags flags;
1749 flags.mCapture = aUseCapture;
1750 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
1753 void EventListenerManager::RemoveEventListener(
1754 const nsAString& aType, EventListenerHolder aListenerHolder,
1755 const dom::EventListenerOptionsOrBoolean& aOptions) {
1756 EventListenerFlags flags;
1757 if (aOptions.IsBoolean()) {
1758 flags.mCapture = aOptions.GetAsBoolean();
1759 } else {
1760 const auto& options = aOptions.GetAsEventListenerOptions();
1761 flags.mCapture = options.mCapture;
1762 flags.mInSystemGroup = options.mMozSystemGroup;
1764 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
1767 void EventListenerManager::AddListenerForAllEvents(EventListener* aDOMListener,
1768 bool aUseCapture,
1769 bool aWantsUntrusted,
1770 bool aSystemEventGroup) {
1771 EventListenerFlags flags;
1772 flags.mCapture = aUseCapture;
1773 flags.mAllowUntrustedEvents = aWantsUntrusted;
1774 flags.mInSystemGroup = aSystemEventGroup;
1775 AddEventListenerInternal(EventListenerHolder(aDOMListener), eAllEvents,
1776 nullptr, flags, false, true);
1779 void EventListenerManager::RemoveListenerForAllEvents(
1780 EventListener* aDOMListener, bool aUseCapture, bool aSystemEventGroup) {
1781 EventListenerFlags flags;
1782 flags.mCapture = aUseCapture;
1783 flags.mInSystemGroup = aSystemEventGroup;
1784 RemoveEventListenerInternal(EventListenerHolder(aDOMListener), nullptr, flags,
1785 true);
1788 bool EventListenerManager::HasMutationListeners() {
1789 if (mMayHaveMutationListeners) {
1790 for (const auto& entry : mListenerMap.mEntries) {
1791 EventMessage message = GetEventMessage(entry.mTypeAtom);
1792 if (message >= eLegacyMutationEventFirst &&
1793 message <= eLegacyMutationEventLast) {
1794 return true;
1799 return false;
1802 uint32_t EventListenerManager::MutationListenerBits() {
1803 uint32_t bits = 0;
1804 if (mMayHaveMutationListeners) {
1805 for (const auto& entry : mListenerMap.mEntries) {
1806 EventMessage message = GetEventMessage(entry.mTypeAtom);
1807 if (message >= eLegacyMutationEventFirst &&
1808 message <= eLegacyMutationEventLast) {
1809 if (message == eLegacySubtreeModified) {
1810 return NS_EVENT_BITS_MUTATION_ALL;
1812 bits |= MutationBitForEventType(message);
1816 return bits;
1819 bool EventListenerManager::HasListenersFor(const nsAString& aEventName) const {
1820 RefPtr<nsAtom> atom = NS_Atomize(u"on"_ns + aEventName);
1821 return HasListenersFor(atom);
1824 bool EventListenerManager::HasListenersFor(nsAtom* aEventNameWithOn) const {
1825 return HasListenersForInternal(aEventNameWithOn, false);
1828 bool EventListenerManager::HasNonSystemGroupListenersFor(
1829 nsAtom* aEventNameWithOn) const {
1830 return HasListenersForInternal(aEventNameWithOn, true);
1833 bool EventListenerManager::HasListenersForInternal(
1834 nsAtom* aEventNameWithOn, bool aIgnoreSystemGroup) const {
1835 #ifdef DEBUG
1836 nsAutoString name;
1837 aEventNameWithOn->ToString(name);
1838 #endif
1839 NS_ASSERTION(StringBeginsWith(name, u"on"_ns),
1840 "Event name does not start with 'on'");
1841 RefPtr<ListenerArray> listeners =
1842 mListenerMap.GetListenersForType(aEventNameWithOn);
1843 if (!listeners) {
1844 return false;
1847 MOZ_ASSERT(!listeners->IsEmpty());
1849 if (!aIgnoreSystemGroup) {
1850 return true;
1853 // Check if any non-system-group listeners exist in `listeners`.
1854 for (const auto& listener : listeners->NonObservingRange()) {
1855 if (!listener.mFlags.mInSystemGroup) {
1856 return true;
1860 return false;
1863 bool EventListenerManager::HasListeners() const {
1864 return !mListenerMap.IsEmpty();
1867 nsresult EventListenerManager::GetListenerInfo(
1868 nsTArray<RefPtr<nsIEventListenerInfo>>& aList) {
1869 nsCOMPtr<EventTarget> target = mTarget;
1870 NS_ENSURE_STATE(target);
1871 aList.Clear();
1872 for (const auto& entry : mListenerMap.mEntries) {
1873 for (const Listener& listener : entry.mListeners->ForwardRange()) {
1874 // If this is a script handler and we haven't yet
1875 // compiled the event handler itself go ahead and compile it
1876 if (listener.mListenerType == Listener::eJSEventListener &&
1877 listener.mHandlerIsString) {
1878 CompileEventHandlerInternal(const_cast<Listener*>(&listener),
1879 entry.mTypeAtom, nullptr, nullptr);
1881 nsAutoString eventType;
1882 if (listener.mAllEvents) {
1883 eventType.SetIsVoid(true);
1884 } else if (listener.mListenerType == Listener::eNoListener) {
1885 continue;
1886 } else {
1887 eventType.Assign(Substring(nsDependentAtomString(entry.mTypeAtom), 2));
1890 JS::Rooted<JSObject*> callback(RootingCx());
1891 JS::Rooted<JSObject*> callbackGlobal(RootingCx());
1892 if (JSEventHandler* handler = listener.GetJSEventHandler()) {
1893 if (handler->GetTypedEventHandler().HasEventHandler()) {
1894 CallbackFunction* callbackFun = handler->GetTypedEventHandler().Ptr();
1895 callback = callbackFun->CallableOrNull();
1896 callbackGlobal = callbackFun->CallbackGlobalOrNull();
1897 if (!callback) {
1898 // This will be null for cross-compartment event listeners
1899 // which have been destroyed.
1900 continue;
1903 } else if (listener.mListenerType == Listener::eWebIDLListener) {
1904 EventListener* listenerCallback =
1905 listener.mListener.GetWebIDLCallback();
1906 callback = listenerCallback->CallbackOrNull();
1907 callbackGlobal = listenerCallback->CallbackGlobalOrNull();
1908 if (!callback) {
1909 // This will be null for cross-compartment event listeners
1910 // which have been destroyed.
1911 continue;
1915 RefPtr<EventListenerInfo> info = new EventListenerInfo(
1916 this, eventType, callback, callbackGlobal, listener.mFlags.mCapture,
1917 listener.mFlags.mAllowUntrustedEvents, listener.mFlags.mInSystemGroup,
1918 listener.mListenerIsHandler);
1919 aList.AppendElement(info.forget());
1922 return NS_OK;
1925 EventListenerManager::Listener* EventListenerManager::GetListenerFor(
1926 nsAString& aType, JSObject* aListener, bool aCapturing,
1927 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler) {
1928 NS_ENSURE_TRUE(aListener, nullptr);
1930 RefPtr<ListenerArray> listeners = ([&]() -> RefPtr<ListenerArray> {
1931 if (aType.IsVoid()) {
1932 return mListenerMap.GetListenersForAllEvents();
1935 for (auto& mapEntry : mListenerMap.mEntries) {
1936 if (RefPtr<nsAtom> typeAtom = mapEntry.mTypeAtom) {
1937 if (Substring(nsDependentAtomString(typeAtom), 2).Equals(aType)) {
1938 return mapEntry.mListeners;
1943 return nullptr;
1944 })();
1946 if (!listeners) {
1947 return nullptr;
1950 for (Listener& listener : listeners->ForwardRange()) {
1951 if (listener.mListenerType == Listener::eNoListener) {
1952 continue;
1955 if (listener.mFlags.mCapture != aCapturing ||
1956 listener.mFlags.mAllowUntrustedEvents != aAllowsUntrusted ||
1957 listener.mFlags.mInSystemGroup != aInSystemEventGroup) {
1958 continue;
1961 if (aIsHandler) {
1962 if (JSEventHandler* handler = listener.GetJSEventHandler()) {
1963 if (handler->GetTypedEventHandler().HasEventHandler()) {
1964 if (handler->GetTypedEventHandler().Ptr()->CallableOrNull() ==
1965 aListener) {
1966 return &listener;
1970 } else if (listener.mListenerType == Listener::eWebIDLListener &&
1971 listener.mListener.GetWebIDLCallback()->CallbackOrNull() ==
1972 aListener) {
1973 return &listener;
1976 return nullptr;
1979 nsresult EventListenerManager::IsListenerEnabled(
1980 nsAString& aType, JSObject* aListener, bool aCapturing,
1981 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler,
1982 bool* aEnabled) {
1983 Listener* listener =
1984 GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted,
1985 aInSystemEventGroup, aIsHandler);
1986 NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE);
1987 *aEnabled = listener->mEnabled;
1988 return NS_OK;
1991 nsresult EventListenerManager::SetListenerEnabled(
1992 nsAString& aType, JSObject* aListener, bool aCapturing,
1993 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler,
1994 bool aEnabled) {
1995 Listener* listener =
1996 GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted,
1997 aInSystemEventGroup, aIsHandler);
1998 NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE);
1999 listener->mEnabled = aEnabled;
2000 if (aEnabled) {
2001 // We may have enabled some listener, clear the cache for which events
2002 // we don't have listeners.
2003 ClearNoListenersForEvents();
2004 mNoListenerForEventAtom = nullptr;
2006 return NS_OK;
2009 bool EventListenerManager::HasUnloadListeners() {
2010 return mListenerMap.GetListenersForType(nsGkAtoms::onunload) != nullptr;
2013 bool EventListenerManager::HasBeforeUnloadListeners() {
2014 return mListenerMap.GetListenersForType(nsGkAtoms::onbeforeunload) != nullptr;
2017 void EventListenerManager::SetEventHandler(nsAtom* aEventName,
2018 EventHandlerNonNull* aHandler) {
2019 if (!aHandler) {
2020 RemoveEventHandler(aEventName);
2021 return;
2024 // Untrusted events are always permitted for non-chrome script
2025 // handlers.
2026 SetEventHandlerInternal(
2027 aEventName, TypedEventHandler(aHandler),
2028 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
2031 void EventListenerManager::SetEventHandler(
2032 OnErrorEventHandlerNonNull* aHandler) {
2033 if (!aHandler) {
2034 RemoveEventHandler(nsGkAtoms::onerror);
2035 return;
2038 // Untrusted events are always permitted on workers and for non-chrome script
2039 // on the main thread.
2040 bool allowUntrusted = !mIsMainThreadELM || !nsContentUtils::IsCallerChrome();
2042 SetEventHandlerInternal(nsGkAtoms::onerror, TypedEventHandler(aHandler),
2043 allowUntrusted);
2046 void EventListenerManager::SetEventHandler(
2047 OnBeforeUnloadEventHandlerNonNull* aHandler) {
2048 if (!aHandler) {
2049 RemoveEventHandler(nsGkAtoms::onbeforeunload);
2050 return;
2053 // Untrusted events are always permitted for non-chrome script
2054 // handlers.
2055 SetEventHandlerInternal(
2056 nsGkAtoms::onbeforeunload, TypedEventHandler(aHandler),
2057 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
2060 const TypedEventHandler* EventListenerManager::GetTypedEventHandler(
2061 nsAtom* aEventName) {
2062 Listener* listener = FindEventHandler(aEventName);
2064 if (!listener) {
2065 return nullptr;
2068 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
2070 if (listener->mHandlerIsString) {
2071 CompileEventHandlerInternal(listener, aEventName, nullptr, nullptr);
2074 const TypedEventHandler& typedHandler =
2075 jsEventHandler->GetTypedEventHandler();
2076 return typedHandler.HasEventHandler() ? &typedHandler : nullptr;
2079 size_t EventListenerManager::SizeOfIncludingThis(
2080 MallocSizeOf aMallocSizeOf) const {
2081 return aMallocSizeOf(this) + mListenerMap.SizeOfExcludingThis(aMallocSizeOf);
2084 size_t EventListenerManager::EventListenerMap::SizeOfExcludingThis(
2085 MallocSizeOf aMallocSizeOf) const {
2086 size_t n = mEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
2087 for (const auto& entry : mEntries) {
2088 n += entry.SizeOfExcludingThis(aMallocSizeOf);
2090 return n;
2093 size_t EventListenerManager::EventListenerMapEntry::SizeOfExcludingThis(
2094 MallocSizeOf aMallocSizeOf) const {
2095 return mListeners->SizeOfIncludingThis(aMallocSizeOf);
2098 size_t EventListenerManager::ListenerArray::SizeOfIncludingThis(
2099 MallocSizeOf aMallocSizeOf) const {
2100 size_t n = aMallocSizeOf(this);
2101 n += ShallowSizeOfExcludingThis(aMallocSizeOf);
2102 for (const auto& listener : NonObservingRange()) {
2103 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
2104 if (jsEventHandler) {
2105 n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf);
2108 return n;
2111 uint32_t EventListenerManager::ListenerCount() const {
2112 uint32_t count = 0;
2113 for (const auto& entry : mListenerMap.mEntries) {
2114 count += entry.mListeners->Length();
2116 return count;
2119 void EventListenerManager::MarkForCC() {
2120 for (const auto& entry : mListenerMap.mEntries) {
2121 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2122 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
2123 if (jsEventHandler) {
2124 const TypedEventHandler& typedHandler =
2125 jsEventHandler->GetTypedEventHandler();
2126 if (typedHandler.HasEventHandler()) {
2127 typedHandler.Ptr()->MarkForCC();
2129 } else if (listener.mListenerType == Listener::eWebIDLListener) {
2130 listener.mListener.GetWebIDLCallback()->MarkForCC();
2134 if (mRefCnt.IsPurple()) {
2135 mRefCnt.RemovePurple();
2139 void EventListenerManager::TraceListeners(JSTracer* aTrc) {
2140 for (const auto& entry : mListenerMap.mEntries) {
2141 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2142 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
2143 if (jsEventHandler) {
2144 const TypedEventHandler& typedHandler =
2145 jsEventHandler->GetTypedEventHandler();
2146 if (typedHandler.HasEventHandler()) {
2147 mozilla::TraceScriptHolder(typedHandler.Ptr(), aTrc);
2149 } else if (listener.mListenerType == Listener::eWebIDLListener) {
2150 mozilla::TraceScriptHolder(listener.mListener.GetWebIDLCallback(),
2151 aTrc);
2153 // We might have eWrappedJSListener, but that is the legacy type for
2154 // JS implemented event listeners, and trickier to handle here.
2159 bool EventListenerManager::HasNonSystemGroupListenersForUntrustedKeyEvents() {
2160 for (const auto& entry : mListenerMap.mEntries) {
2161 if (entry.mTypeAtom != nsGkAtoms::onkeydown &&
2162 entry.mTypeAtom != nsGkAtoms::onkeypress &&
2163 entry.mTypeAtom != nsGkAtoms::onkeyup) {
2164 continue;
2166 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2167 if (!listener.mFlags.mInSystemGroup &&
2168 listener.mFlags.mAllowUntrustedEvents) {
2169 return true;
2173 return false;
2176 bool EventListenerManager::
2177 HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents() {
2178 for (const auto& entry : mListenerMap.mEntries) {
2179 if (entry.mTypeAtom != nsGkAtoms::onkeydown &&
2180 entry.mTypeAtom != nsGkAtoms::onkeypress &&
2181 entry.mTypeAtom != nsGkAtoms::onkeyup) {
2182 continue;
2184 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2185 if (!listener.mFlags.mPassive && !listener.mFlags.mInSystemGroup &&
2186 listener.mFlags.mAllowUntrustedEvents) {
2187 return true;
2191 return false;
2194 bool EventListenerManager::HasApzAwareListeners() {
2195 if (!mIsMainThreadELM) {
2196 return false;
2199 for (const auto& entry : mListenerMap.mEntries) {
2200 if (!IsApzAwareEvent(entry.mTypeAtom)) {
2201 continue;
2203 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2204 if (!listener.mFlags.mPassive) {
2205 return true;
2209 return false;
2212 static bool IsWheelEventType(nsAtom* aEvent) {
2213 if (aEvent == nsGkAtoms::onwheel || aEvent == nsGkAtoms::onDOMMouseScroll ||
2214 aEvent == nsGkAtoms::onmousewheel ||
2215 aEvent == nsGkAtoms::onMozMousePixelScroll) {
2216 return true;
2218 return false;
2221 bool EventListenerManager::IsApzAwareEvent(nsAtom* aEvent) {
2222 if (IsWheelEventType(aEvent)) {
2223 return true;
2225 // In theory we should schedule a repaint if the touch event pref changes,
2226 // because the event regions might be out of date. In practice that seems like
2227 // overkill because users generally shouldn't be flipping this pref, much
2228 // less expecting touch listeners on the page to immediately start preventing
2229 // scrolling without so much as a repaint. Tests that we write can work
2230 // around this constraint easily enough.
2231 if (aEvent == nsGkAtoms::ontouchstart || aEvent == nsGkAtoms::ontouchmove) {
2232 return TouchEvent::PrefEnabled(
2233 nsContentUtils::GetDocShellForEventTarget(mTarget));
2235 return false;
2238 bool EventListenerManager::HasNonPassiveWheelListener() {
2239 MOZ_ASSERT(NS_IsMainThread());
2240 for (const auto& entry : mListenerMap.mEntries) {
2241 if (!IsWheelEventType(entry.mTypeAtom)) {
2242 continue;
2244 for (const auto& listener : entry.mListeners->NonObservingRange()) {
2245 if (!listener.mFlags.mPassive) {
2246 return true;
2250 return false;
2253 void EventListenerManager::RemoveAllListeners() {
2254 while (!mListenerMap.IsEmpty()) {
2255 size_t entryIndex = mListenerMap.mEntries.Length() - 1;
2256 EventListenerMapEntry& entry = mListenerMap.mEntries[entryIndex];
2257 RefPtr<nsAtom> type = entry.mTypeAtom;
2258 MOZ_ASSERT(!entry.mListeners->IsEmpty());
2259 size_t idx = entry.mListeners->Length() - 1;
2260 entry.mListeners->RemoveElementAt(idx);
2261 if (entry.mListeners->IsEmpty()) {
2262 mListenerMap.mEntries.RemoveElementAt(entryIndex);
2264 NotifyEventListenerRemoved(type);
2265 if (IsDeviceType(type)) {
2266 DisableDevice(type);
2271 already_AddRefed<nsIScriptGlobalObject>
2272 EventListenerManager::GetScriptGlobalAndDocument(Document** aDoc) {
2273 nsCOMPtr<Document> doc;
2274 nsCOMPtr<nsPIDOMWindowInner> win;
2275 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
2276 // Try to get context from doc
2277 doc = node->OwnerDoc();
2278 if (doc->IsLoadedAsData()) {
2279 return nullptr;
2282 win = do_QueryInterface(doc->GetScopeObject());
2283 } else if ((win = GetTargetAsInnerWindow())) {
2284 doc = win->GetExtantDoc();
2287 if (!win || !win->IsCurrentInnerWindow()) {
2288 return nullptr;
2291 doc.forget(aDoc);
2292 nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(win);
2293 return global.forget();
2296 EventListenerManager::ListenerSignalFollower::ListenerSignalFollower(
2297 EventListenerManager* aListenerManager,
2298 EventListenerManager::Listener* aListener, nsAtom* aTypeAtom)
2299 : dom::AbortFollower(),
2300 mListenerManager(aListenerManager),
2301 mListener(aListener->mListener.Clone()),
2302 mTypeAtom(aTypeAtom),
2303 mAllEvents(aListener->mAllEvents),
2304 mFlags(aListener->mFlags){};
2306 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower)
2308 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower)
2309 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower)
2311 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
2312 EventListenerManager::ListenerSignalFollower)
2313 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
2314 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2316 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
2317 EventListenerManager::ListenerSignalFollower)
2318 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
2319 tmp->mListenerManager = nullptr;
2320 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2322 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
2323 EventListenerManager::ListenerSignalFollower)
2324 NS_INTERFACE_MAP_ENTRY(nsISupports)
2325 NS_INTERFACE_MAP_END
2327 void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() {
2328 if (mListenerManager) {
2329 RefPtr<EventListenerManager> elm = mListenerManager;
2330 mListenerManager = nullptr;
2331 elm->RemoveEventListenerInternal(std::move(mListener), mTypeAtom, mFlags,
2332 mAllEvents);
2336 } // namespace mozilla