Bug 1890793: Assert CallArgs::newTarget is not gray. r=spidermonkey-reviewers,sfink...
[gecko.git] / dom / events / EventDispatcher.cpp
blob61e5adf6f174b6441189967d2bfd83a616bb0572
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 #include "mozilla/Assertions.h"
8 #include "nsPresContext.h"
9 #include "nsContentUtils.h"
10 #include "nsDocShell.h"
11 #include "nsError.h"
12 #include <new>
13 #include "nsIContent.h"
14 #include "nsIContentInlines.h"
15 #include "mozilla/dom/Document.h"
16 #include "nsINode.h"
17 #include "nsIScriptObjectPrincipal.h"
18 #include "nsPIDOMWindow.h"
19 #include "nsRefreshDriver.h"
20 #include "AnimationEvent.h"
21 #include "BeforeUnloadEvent.h"
22 #include "ClipboardEvent.h"
23 #include "CommandEvent.h"
24 #include "CompositionEvent.h"
25 #include "DeviceMotionEvent.h"
26 #include "DragEvent.h"
27 #include "KeyboardEvent.h"
28 #include "mozilla/BasePrincipal.h"
29 #include "mozilla/ContentEvents.h"
30 #include "mozilla/dom/CloseEvent.h"
31 #include "mozilla/dom/CustomEvent.h"
32 #include "mozilla/dom/DeviceOrientationEvent.h"
33 #include "mozilla/dom/EventTarget.h"
34 #include "mozilla/dom/FocusEvent.h"
35 #include "mozilla/dom/HashChangeEvent.h"
36 #include "mozilla/dom/InputEvent.h"
37 #include "mozilla/dom/MessageEvent.h"
38 #include "mozilla/dom/MouseScrollEvent.h"
39 #include "mozilla/dom/MutationEvent.h"
40 #include "mozilla/dom/NotifyPaintEvent.h"
41 #include "mozilla/dom/PageTransitionEvent.h"
42 #include "mozilla/dom/PerformanceEventTiming.h"
43 #include "mozilla/dom/PerformanceMainThread.h"
44 #include "mozilla/dom/PointerEvent.h"
45 #include "mozilla/dom/RootedDictionary.h"
46 #include "mozilla/dom/ScrollAreaEvent.h"
47 #include "mozilla/dom/SimpleGestureEvent.h"
48 #include "mozilla/dom/ScriptSettings.h"
49 #include "mozilla/dom/StorageEvent.h"
50 #include "mozilla/dom/TextEvent.h"
51 #include "mozilla/dom/TimeEvent.h"
52 #include "mozilla/dom/TouchEvent.h"
53 #include "mozilla/dom/TransitionEvent.h"
54 #include "mozilla/dom/WheelEvent.h"
55 #include "mozilla/dom/WorkerPrivate.h"
56 #include "mozilla/dom/XULCommandEvent.h"
57 #include "mozilla/EventDispatcher.h"
58 #include "mozilla/EventListenerManager.h"
59 #include "mozilla/InternalMutationEvent.h"
60 #include "mozilla/ipc/MessageChannel.h"
61 #include "mozilla/MiscEvents.h"
62 #include "mozilla/MouseEvents.h"
63 #include "mozilla/ProfilerLabels.h"
64 #include "mozilla/ProfilerMarkers.h"
65 #include "mozilla/ScopeExit.h"
66 #include "mozilla/Telemetry.h"
67 #include "mozilla/TextEvents.h"
68 #include "mozilla/TouchEvents.h"
69 #include "mozilla/Unused.h"
71 namespace mozilla {
73 using namespace dom;
75 class ELMCreationDetector {
76 public:
77 ELMCreationDetector()
78 // We can do this optimization only in the main thread.
79 : mNonMainThread(!NS_IsMainThread()),
80 mInitialCount(mNonMainThread
81 ? 0
82 : EventListenerManager::sMainThreadCreatedCount) {}
84 bool MayHaveNewListenerManager() {
85 return mNonMainThread ||
86 mInitialCount != EventListenerManager::sMainThreadCreatedCount;
89 bool IsMainThread() { return !mNonMainThread; }
91 private:
92 bool mNonMainThread;
93 uint32_t mInitialCount;
96 static bool IsEventTargetChrome(EventTarget* aEventTarget,
97 Document** aDocument = nullptr) {
98 if (aDocument) {
99 *aDocument = nullptr;
102 Document* doc = nullptr;
103 if (nsINode* node = nsINode::FromEventTargetOrNull(aEventTarget)) {
104 doc = node->OwnerDoc();
105 } else if (nsPIDOMWindowInner* window =
106 nsPIDOMWindowInner::FromEventTargetOrNull(aEventTarget)) {
107 doc = window->GetExtantDoc();
110 // nsContentUtils::IsChromeDoc is null-safe.
111 bool isChrome = false;
112 if (doc) {
113 isChrome = nsContentUtils::IsChromeDoc(doc);
114 if (aDocument) {
115 nsCOMPtr<Document> retVal = doc;
116 retVal.swap(*aDocument);
118 } else if (nsCOMPtr<nsIScriptObjectPrincipal> sop =
119 do_QueryInterface(aEventTarget->GetOwnerGlobal())) {
120 isChrome = sop->GetPrincipal()->IsSystemPrincipal();
122 return isChrome;
125 // EventTargetChainItem represents a single item in the event target chain.
126 class EventTargetChainItem {
127 public:
128 explicit EventTargetChainItem(EventTarget* aTarget)
129 : mTarget(aTarget), mItemFlags(0) {
130 MOZ_COUNT_CTOR(EventTargetChainItem);
133 MOZ_COUNTED_DTOR(EventTargetChainItem)
135 static EventTargetChainItem* Create(nsTArray<EventTargetChainItem>& aChain,
136 EventTarget* aTarget,
137 EventTargetChainItem* aChild = nullptr) {
138 // The last item which can handle the event must be aChild.
139 MOZ_ASSERT(GetLastCanHandleEventTarget(aChain) == aChild);
140 MOZ_ASSERT(!aTarget || aTarget == aTarget->GetTargetForEventTargetChain());
141 EventTargetChainItem* etci = aChain.AppendElement(aTarget);
142 return etci;
145 static void DestroyLast(nsTArray<EventTargetChainItem>& aChain,
146 EventTargetChainItem* aItem) {
147 MOZ_ASSERT(&aChain.LastElement() == aItem);
148 aChain.RemoveLastElement();
151 static EventTargetChainItem* GetFirstCanHandleEventTarget(
152 nsTArray<EventTargetChainItem>& aChain) {
153 return &aChain[GetFirstCanHandleEventTargetIdx(aChain)];
156 static uint32_t GetFirstCanHandleEventTargetIdx(
157 nsTArray<EventTargetChainItem>& aChain) {
158 // aChain[i].PreHandleEventOnly() = true only when the target element wants
159 // PreHandleEvent and set mCanHandle=false. So we find the first element
160 // which can handle the event.
161 for (uint32_t i = 0; i < aChain.Length(); ++i) {
162 if (!aChain[i].PreHandleEventOnly()) {
163 return i;
166 MOZ_ASSERT(false);
167 return 0;
170 static EventTargetChainItem* GetLastCanHandleEventTarget(
171 nsTArray<EventTargetChainItem>& aChain) {
172 // Fine the last item which can handle the event.
173 for (int32_t i = aChain.Length() - 1; i >= 0; --i) {
174 if (!aChain[i].PreHandleEventOnly()) {
175 return &aChain[i];
178 return nullptr;
181 bool IsValid() const {
182 NS_WARNING_ASSERTION(!!(mTarget), "Event target is not valid!");
183 return !!(mTarget);
186 EventTarget* GetNewTarget() const { return mNewTarget; }
188 void SetNewTarget(EventTarget* aNewTarget) { mNewTarget = aNewTarget; }
190 EventTarget* GetRetargetedRelatedTarget() { return mRetargetedRelatedTarget; }
192 void SetRetargetedRelatedTarget(EventTarget* aTarget) {
193 mRetargetedRelatedTarget = aTarget;
196 void SetRetargetedTouchTarget(
197 Maybe<nsTArray<RefPtr<EventTarget>>>&& aTargets) {
198 mRetargetedTouchTargets = std::move(aTargets);
201 bool HasRetargetTouchTargets() const {
202 return mRetargetedTouchTargets.isSome() || mInitialTargetTouches.isSome();
205 void RetargetTouchTargets(WidgetTouchEvent* aTouchEvent, Event* aDOMEvent) {
206 MOZ_ASSERT(HasRetargetTouchTargets());
207 MOZ_ASSERT(aTouchEvent,
208 "mRetargetedTouchTargets should be empty when dispatching "
209 "non-touch events.");
211 if (mRetargetedTouchTargets.isSome()) {
212 WidgetTouchEvent::TouchArray& touches = aTouchEvent->mTouches;
213 MOZ_ASSERT(!touches.Length() ||
214 touches.Length() == mRetargetedTouchTargets->Length());
215 for (uint32_t i = 0; i < touches.Length(); ++i) {
216 touches[i]->mTarget = mRetargetedTouchTargets->ElementAt(i);
220 if (aDOMEvent) {
221 // The number of touch objects in targetTouches list may change depending
222 // on the retargeting.
223 TouchEvent* touchDOMEvent = static_cast<TouchEvent*>(aDOMEvent);
224 TouchList* targetTouches = touchDOMEvent->GetExistingTargetTouches();
225 if (targetTouches) {
226 targetTouches->Clear();
227 if (mInitialTargetTouches.isSome()) {
228 for (uint32_t i = 0; i < mInitialTargetTouches->Length(); ++i) {
229 Touch* touch = mInitialTargetTouches->ElementAt(i);
230 if (touch) {
231 touch->mTarget = touch->mOriginalTarget;
233 targetTouches->Append(touch);
240 void SetInitialTargetTouches(
241 Maybe<nsTArray<RefPtr<dom::Touch>>>&& aInitialTargetTouches) {
242 mInitialTargetTouches = std::move(aInitialTargetTouches);
245 void SetForceContentDispatch(bool aForce) {
246 mFlags.mForceContentDispatch = aForce;
249 bool ForceContentDispatch() const { return mFlags.mForceContentDispatch; }
251 void SetWantsWillHandleEvent(bool aWants) {
252 mFlags.mWantsWillHandleEvent = aWants;
255 bool WantsWillHandleEvent() const { return mFlags.mWantsWillHandleEvent; }
257 void SetWantsPreHandleEvent(bool aWants) {
258 mFlags.mWantsPreHandleEvent = aWants;
261 bool WantsPreHandleEvent() const { return mFlags.mWantsPreHandleEvent; }
263 void SetPreHandleEventOnly(bool aWants) {
264 mFlags.mPreHandleEventOnly = aWants;
267 bool PreHandleEventOnly() const { return mFlags.mPreHandleEventOnly; }
269 void SetRootOfClosedTree(bool aSet) { mFlags.mRootOfClosedTree = aSet; }
271 bool IsRootOfClosedTree() const { return mFlags.mRootOfClosedTree; }
273 void SetItemInShadowTree(bool aSet) { mFlags.mItemInShadowTree = aSet; }
275 bool IsItemInShadowTree() const { return mFlags.mItemInShadowTree; }
277 void SetIsSlotInClosedTree(bool aSet) { mFlags.mIsSlotInClosedTree = aSet; }
279 bool IsSlotInClosedTree() const { return mFlags.mIsSlotInClosedTree; }
281 void SetIsChromeHandler(bool aSet) { mFlags.mIsChromeHandler = aSet; }
283 bool IsChromeHandler() const { return mFlags.mIsChromeHandler; }
285 void SetMayHaveListenerManager(bool aMayHave) {
286 mFlags.mMayHaveManager = aMayHave;
289 bool MayHaveListenerManager() { return mFlags.mMayHaveManager; }
291 EventTarget* CurrentTarget() const { return mTarget; }
294 * Dispatches event through the event target chain.
295 * Handles capture, target and bubble phases both in default
296 * and system event group and calls also PostHandleEvent for each
297 * item in the chain.
299 MOZ_CAN_RUN_SCRIPT
300 static void HandleEventTargetChain(nsTArray<EventTargetChainItem>& aChain,
301 EventChainPostVisitor& aVisitor,
302 EventDispatchingCallback* aCallback,
303 ELMCreationDetector& aCd);
306 * Resets aVisitor object and calls GetEventTargetParent.
307 * Copies mItemFlags and mItemData to the current EventTargetChainItem.
309 void GetEventTargetParent(EventChainPreVisitor& aVisitor);
312 * Copies mItemFlags, mItemData to aVisitor,
313 * calls LegacyPreActivationBehavior and copies both members back
314 * to this EventTargetChainitem.
316 void LegacyPreActivationBehavior(EventChainVisitor& aVisitor);
319 * Copies mItemFlags and mItemData to aVisitor and calls ActivationBehavior.
321 MOZ_CAN_RUN_SCRIPT
322 void ActivationBehavior(EventChainPostVisitor& aVisitor);
325 * Copies mItemFlags and mItemData to aVisitor and
326 * calls LegacyCanceledActivationBehavior.
328 void LegacyCanceledActivationBehavior(EventChainPostVisitor& aVisitor);
331 * Copies mItemFlags and mItemData to aVisitor.
332 * Calls PreHandleEvent for those items which called SetWantsPreHandleEvent.
334 void PreHandleEvent(EventChainVisitor& aVisitor);
337 * If the current item in the event target chain has an event listener
338 * manager, this method calls EventListenerManager::HandleEvent().
340 void HandleEvent(EventChainPostVisitor& aVisitor, ELMCreationDetector& aCd) {
341 if (WantsWillHandleEvent()) {
342 mTarget->WillHandleEvent(aVisitor);
344 if (aVisitor.mEvent->PropagationStopped()) {
345 return;
347 if (aVisitor.mEvent->mFlags.mOnlySystemGroupDispatch &&
348 !aVisitor.mEvent->mFlags.mInSystemGroup) {
349 return;
351 if (aVisitor.mEvent->mFlags.mOnlySystemGroupDispatchInContent &&
352 !aVisitor.mEvent->mFlags.mInSystemGroup && !IsCurrentTargetChrome()) {
353 return;
355 if (!mManager) {
356 if (!MayHaveListenerManager() && !aCd.MayHaveNewListenerManager()) {
357 return;
359 mManager = mTarget->GetExistingListenerManager();
361 if (mManager) {
362 NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr,
363 "CurrentTarget should be null!");
365 mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent,
366 &aVisitor.mDOMEvent, CurrentTarget(),
367 &aVisitor.mEventStatus, IsItemInShadowTree());
368 NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr,
369 "CurrentTarget should be null!");
374 * Copies mItemFlags and mItemData to aVisitor and calls PostHandleEvent.
376 MOZ_CAN_RUN_SCRIPT void PostHandleEvent(EventChainPostVisitor& aVisitor);
378 private:
379 const nsCOMPtr<EventTarget> mTarget;
380 nsCOMPtr<EventTarget> mRetargetedRelatedTarget;
381 Maybe<nsTArray<RefPtr<EventTarget>>> mRetargetedTouchTargets;
382 Maybe<nsTArray<RefPtr<dom::Touch>>> mInitialTargetTouches;
384 class EventTargetChainFlags {
385 public:
386 explicit EventTargetChainFlags() { SetRawFlags(0); }
387 // Cached flags for each EventTargetChainItem which are set when calling
388 // GetEventTargetParent to create event target chain. They are used to
389 // manage or speedup event dispatching.
390 bool mForceContentDispatch : 1;
391 bool mWantsWillHandleEvent : 1;
392 bool mMayHaveManager : 1;
393 bool mChechedIfChrome : 1;
394 bool mIsChromeContent : 1;
395 bool mWantsPreHandleEvent : 1;
396 bool mPreHandleEventOnly : 1;
397 bool mRootOfClosedTree : 1;
398 bool mItemInShadowTree : 1;
399 bool mIsSlotInClosedTree : 1;
400 bool mIsChromeHandler : 1;
402 private:
403 using RawFlags = uint32_t;
404 void SetRawFlags(RawFlags aRawFlags) {
405 static_assert(
406 sizeof(EventTargetChainFlags) <= sizeof(RawFlags),
407 "EventTargetChainFlags must not be bigger than the RawFlags");
408 memcpy(this, &aRawFlags, sizeof(EventTargetChainFlags));
410 } mFlags;
412 uint16_t mItemFlags;
413 nsCOMPtr<nsISupports> mItemData;
414 // Event retargeting must happen whenever mNewTarget is non-null.
415 nsCOMPtr<EventTarget> mNewTarget;
416 // Cache mTarget's event listener manager.
417 RefPtr<EventListenerManager> mManager;
419 bool IsCurrentTargetChrome() {
420 if (!mFlags.mChechedIfChrome) {
421 mFlags.mChechedIfChrome = true;
422 if (IsEventTargetChrome(mTarget)) {
423 mFlags.mIsChromeContent = true;
426 return mFlags.mIsChromeContent;
430 void EventTargetChainItem::GetEventTargetParent(
431 EventChainPreVisitor& aVisitor) {
432 aVisitor.Reset();
433 mTarget->GetEventTargetParent(aVisitor);
434 SetForceContentDispatch(aVisitor.mForceContentDispatch);
435 SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent);
436 SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager);
437 SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
438 SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
439 SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
440 SetItemInShadowTree(aVisitor.mItemInShadowTree);
441 SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
442 SetRetargetedTouchTarget(std::move(aVisitor.mRetargetedTouchTargets));
443 mItemFlags = aVisitor.mItemFlags;
444 mItemData = aVisitor.mItemData;
447 void EventTargetChainItem::LegacyPreActivationBehavior(
448 EventChainVisitor& aVisitor) {
449 aVisitor.mItemFlags = mItemFlags;
450 aVisitor.mItemData = mItemData;
451 mTarget->LegacyPreActivationBehavior(aVisitor);
452 mItemFlags = aVisitor.mItemFlags;
453 mItemData = aVisitor.mItemData;
456 void EventTargetChainItem::PreHandleEvent(EventChainVisitor& aVisitor) {
457 if (!WantsPreHandleEvent()) {
458 return;
460 aVisitor.mItemFlags = mItemFlags;
461 aVisitor.mItemData = mItemData;
462 Unused << mTarget->PreHandleEvent(aVisitor);
463 MOZ_ASSERT(mItemFlags == aVisitor.mItemFlags);
464 MOZ_ASSERT(mItemData == aVisitor.mItemData);
467 void EventTargetChainItem::ActivationBehavior(EventChainPostVisitor& aVisitor) {
468 aVisitor.mItemFlags = mItemFlags;
469 aVisitor.mItemData = mItemData;
470 mTarget->ActivationBehavior(aVisitor);
471 MOZ_ASSERT(mItemFlags == aVisitor.mItemFlags);
472 MOZ_ASSERT(mItemData == aVisitor.mItemData);
475 void EventTargetChainItem::LegacyCanceledActivationBehavior(
476 EventChainPostVisitor& aVisitor) {
477 aVisitor.mItemFlags = mItemFlags;
478 aVisitor.mItemData = mItemData;
479 mTarget->LegacyCanceledActivationBehavior(aVisitor);
480 MOZ_ASSERT(mItemFlags == aVisitor.mItemFlags);
481 MOZ_ASSERT(mItemData == aVisitor.mItemData);
484 void EventTargetChainItem::PostHandleEvent(EventChainPostVisitor& aVisitor) {
485 aVisitor.mItemFlags = mItemFlags;
486 aVisitor.mItemData = mItemData;
487 mTarget->PostHandleEvent(aVisitor);
488 MOZ_ASSERT(mItemFlags == aVisitor.mItemFlags);
489 MOZ_ASSERT(mItemData == aVisitor.mItemData);
492 void EventTargetChainItem::HandleEventTargetChain(
493 nsTArray<EventTargetChainItem>& aChain, EventChainPostVisitor& aVisitor,
494 EventDispatchingCallback* aCallback, ELMCreationDetector& aCd) {
495 // Save the target so that it can be restored later.
496 nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->mTarget;
497 nsCOMPtr<EventTarget> firstRelatedTarget = aVisitor.mEvent->mRelatedTarget;
498 Maybe<AutoTArray<nsCOMPtr<EventTarget>, 10>> firstTouchTargets;
499 WidgetTouchEvent* touchEvent = nullptr;
500 if (aVisitor.mEvent->mClass == eTouchEventClass) {
501 touchEvent = aVisitor.mEvent->AsTouchEvent();
502 if (!aVisitor.mEvent->mFlags.mInSystemGroup) {
503 firstTouchTargets.emplace();
504 WidgetTouchEvent* touchEvent = aVisitor.mEvent->AsTouchEvent();
505 WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
506 for (uint32_t i = 0; i < touches.Length(); ++i) {
507 firstTouchTargets->AppendElement(touches[i]->mTarget);
512 uint32_t chainLength = aChain.Length();
513 EventTargetChainItem* chain = aChain.Elements();
514 uint32_t firstCanHandleEventTargetIdx =
515 EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain);
517 // Capture
518 aVisitor.mEvent->mFlags.mInCapturePhase = true;
519 aVisitor.mEvent->mFlags.mInBubblingPhase = false;
520 aVisitor.mEvent->mFlags.mInTargetPhase = false;
521 for (uint32_t i = chainLength - 1; i > firstCanHandleEventTargetIdx; --i) {
522 EventTargetChainItem& item = chain[i];
523 if (item.PreHandleEventOnly()) {
524 continue;
526 if ((!aVisitor.mEvent->mFlags.mNoContentDispatch ||
527 item.ForceContentDispatch()) &&
528 !aVisitor.mEvent->PropagationStopped()) {
529 item.HandleEvent(aVisitor, aCd);
532 if (item.GetNewTarget()) {
533 // item is at anonymous boundary. Need to retarget for the child items.
534 for (uint32_t j = i; j > 0; --j) {
535 uint32_t childIndex = j - 1;
536 EventTarget* newTarget = chain[childIndex].GetNewTarget();
537 if (newTarget) {
538 aVisitor.mEvent->mTarget = newTarget;
539 break;
544 // https://dom.spec.whatwg.org/#dispatching-events
545 // Step 14.2
546 // "Set event's relatedTarget to tuple's relatedTarget."
547 // Note, the initial retargeting was done already when creating
548 // event target chain, so we need to do this only after calling
549 // HandleEvent, not before, like in the specification.
550 if (item.GetRetargetedRelatedTarget()) {
551 bool found = false;
552 for (uint32_t j = i; j > 0; --j) {
553 uint32_t childIndex = j - 1;
554 EventTarget* relatedTarget =
555 chain[childIndex].GetRetargetedRelatedTarget();
556 if (relatedTarget) {
557 found = true;
558 aVisitor.mEvent->mRelatedTarget = relatedTarget;
559 break;
562 if (!found) {
563 aVisitor.mEvent->mRelatedTarget =
564 aVisitor.mEvent->mOriginalRelatedTarget;
568 if (item.HasRetargetTouchTargets()) {
569 bool found = false;
570 for (uint32_t j = i; j > 0; --j) {
571 uint32_t childIndex = j - 1;
572 if (chain[childIndex].HasRetargetTouchTargets()) {
573 found = true;
574 chain[childIndex].RetargetTouchTargets(touchEvent,
575 aVisitor.mDOMEvent);
576 break;
579 if (!found) {
580 WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
581 for (uint32_t i = 0; i < touches.Length(); ++i) {
582 touches[i]->mTarget = touches[i]->mOriginalTarget;
588 // Target
589 aVisitor.mEvent->mFlags.mInTargetPhase = true;
590 EventTargetChainItem& targetItem = chain[firstCanHandleEventTargetIdx];
591 // Need to explicitly retarget touch targets so that initial targets get set
592 // properly in case nothing else retargeted touches.
593 if (targetItem.HasRetargetTouchTargets()) {
594 targetItem.RetargetTouchTargets(touchEvent, aVisitor.mDOMEvent);
596 if (!aVisitor.mEvent->PropagationStopped() &&
597 (!aVisitor.mEvent->mFlags.mNoContentDispatch ||
598 targetItem.ForceContentDispatch())) {
599 targetItem.HandleEvent(aVisitor, aCd);
601 aVisitor.mEvent->mFlags.mInCapturePhase = false;
602 aVisitor.mEvent->mFlags.mInBubblingPhase = true;
603 if (!aVisitor.mEvent->PropagationStopped() &&
604 (!aVisitor.mEvent->mFlags.mNoContentDispatch ||
605 targetItem.ForceContentDispatch())) {
606 targetItem.HandleEvent(aVisitor, aCd);
609 if (aVisitor.mEvent->mFlags.mInSystemGroup) {
610 targetItem.PostHandleEvent(aVisitor);
612 aVisitor.mEvent->mFlags.mInTargetPhase = false;
614 // Bubble
615 for (uint32_t i = firstCanHandleEventTargetIdx + 1; i < chainLength; ++i) {
616 EventTargetChainItem& item = chain[i];
617 if (item.PreHandleEventOnly()) {
618 continue;
620 EventTarget* newTarget = item.GetNewTarget();
621 if (newTarget) {
622 // Item is at anonymous boundary. Need to retarget for the current item
623 // and for parent items.
624 aVisitor.mEvent->mTarget = newTarget;
627 // https://dom.spec.whatwg.org/#dispatching-events
628 // Step 15.2
629 // "Set event's relatedTarget to tuple's relatedTarget."
630 EventTarget* relatedTarget = item.GetRetargetedRelatedTarget();
631 if (relatedTarget) {
632 aVisitor.mEvent->mRelatedTarget = relatedTarget;
635 if (item.HasRetargetTouchTargets()) {
636 item.RetargetTouchTargets(touchEvent, aVisitor.mDOMEvent);
639 if (aVisitor.mEvent->mFlags.mBubbles || newTarget) {
640 if ((!aVisitor.mEvent->mFlags.mNoContentDispatch ||
641 item.ForceContentDispatch()) &&
642 !aVisitor.mEvent->PropagationStopped()) {
643 item.HandleEvent(aVisitor, aCd);
645 if (aVisitor.mEvent->mFlags.mInSystemGroup) {
646 item.PostHandleEvent(aVisitor);
650 aVisitor.mEvent->mFlags.mInBubblingPhase = false;
652 if (!aVisitor.mEvent->mFlags.mInSystemGroup &&
653 aVisitor.mEvent->IsAllowedToDispatchInSystemGroup()) {
654 // Dispatch to the system event group. Make sure to clear the
655 // STOP_DISPATCH flag since this resets for each event group.
656 aVisitor.mEvent->mFlags.mPropagationStopped = false;
657 aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false;
659 // Setting back the original target of the event.
660 aVisitor.mEvent->mTarget = aVisitor.mEvent->mOriginalTarget;
661 aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
662 if (firstTouchTargets) {
663 WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
664 for (uint32_t i = 0; i < touches.Length(); ++i) {
665 touches[i]->mTarget = touches[i]->mOriginalTarget;
669 // Special handling if PresShell (or some other caller)
670 // used a callback object.
671 if (aCallback) {
672 aCallback->HandleEvent(aVisitor);
675 // Retarget for system event group (which does the default handling too).
676 // Setting back the target which was used also for default event group.
677 aVisitor.mEvent->mTarget = firstTarget;
678 aVisitor.mEvent->mRelatedTarget = firstRelatedTarget;
679 if (firstTouchTargets) {
680 WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
681 for (uint32_t i = 0; i < firstTouchTargets->Length(); ++i) {
682 touches[i]->mTarget = firstTouchTargets->ElementAt(i);
686 aVisitor.mEvent->mFlags.mInSystemGroup = true;
687 HandleEventTargetChain(aChain, aVisitor, aCallback, aCd);
688 aVisitor.mEvent->mFlags.mInSystemGroup = false;
690 // After dispatch, clear all the propagation flags so that
691 // system group listeners don't affect to the event.
692 aVisitor.mEvent->mFlags.mPropagationStopped = false;
693 aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false;
697 // There are often 2 nested event dispatches ongoing at the same time, so
698 // have 2 separate caches.
699 static const uint32_t kCachedMainThreadChainSize = 128;
700 struct CachedChains {
701 nsTArray<EventTargetChainItem> mChain1;
702 nsTArray<EventTargetChainItem> mChain2;
704 static CachedChains* sCachedMainThreadChains = nullptr;
706 /* static */
707 void EventDispatcher::Shutdown() {
708 delete sCachedMainThreadChains;
709 sCachedMainThreadChains = nullptr;
712 EventTargetChainItem* EventTargetChainItemForChromeTarget(
713 nsTArray<EventTargetChainItem>& aChain, nsINode* aNode,
714 EventTargetChainItem* aChild = nullptr) {
715 if (!aNode->IsInComposedDoc()) {
716 return nullptr;
718 nsPIDOMWindowInner* win = aNode->OwnerDoc()->GetInnerWindow();
719 EventTarget* piTarget = win ? win->GetParentTarget() : nullptr;
720 NS_ENSURE_TRUE(piTarget, nullptr);
722 EventTargetChainItem* etci = EventTargetChainItem::Create(
723 aChain, piTarget->GetTargetForEventTargetChain(), aChild);
724 if (!etci->IsValid()) {
725 EventTargetChainItem::DestroyLast(aChain, etci);
726 return nullptr;
728 return etci;
731 /* static */ EventTargetChainItem* MayRetargetToChromeIfCanNotHandleEvent(
732 nsTArray<EventTargetChainItem>& aChain, EventChainPreVisitor& aPreVisitor,
733 EventTargetChainItem* aTargetEtci, EventTargetChainItem* aChildEtci,
734 nsINode* aContent) {
735 if (!aPreVisitor.mWantsPreHandleEvent) {
736 // Keep EventTargetChainItem if we need to call PreHandleEvent on it.
737 EventTargetChainItem::DestroyLast(aChain, aTargetEtci);
739 if (aPreVisitor.mAutomaticChromeDispatch && aContent) {
740 aPreVisitor.mRelatedTargetRetargetedInCurrentScope = false;
741 // Event target couldn't handle the event. Try to propagate to chrome.
742 EventTargetChainItem* chromeTargetEtci =
743 EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci);
744 if (chromeTargetEtci) {
745 // If we propagate to chrome, need to ensure we mark
746 // EventTargetChainItem to be chrome handler so that event.composedPath()
747 // can return the right value.
748 chromeTargetEtci->SetIsChromeHandler(true);
749 chromeTargetEtci->GetEventTargetParent(aPreVisitor);
750 return chromeTargetEtci;
753 return nullptr;
756 static bool ShouldClearTargets(WidgetEvent* aEvent) {
757 if (nsIContent* finalTarget =
758 nsIContent::FromEventTargetOrNull(aEvent->mTarget)) {
759 if (finalTarget->SubtreeRoot()->IsShadowRoot()) {
760 return true;
764 if (nsIContent* finalRelatedTarget =
765 nsIContent::FromEventTargetOrNull(aEvent->mRelatedTarget)) {
766 if (finalRelatedTarget->SubtreeRoot()->IsShadowRoot()) {
767 return true;
770 // XXXsmaug Check also all the touch objects.
772 return false;
775 static void DescribeEventTargetForProfilerMarker(const EventTarget* aTarget,
776 nsACString& aDescription) {
777 auto* node = aTarget->GetAsNode();
778 if (node) {
779 if (node->IsElement()) {
780 nsAutoString nodeDescription;
781 node->AsElement()->Describe(nodeDescription, true);
782 aDescription = NS_ConvertUTF16toUTF8(nodeDescription);
783 } else if (node->IsDocument()) {
784 aDescription.AssignLiteral("document");
785 } else if (node->IsText()) {
786 aDescription.AssignLiteral("text");
787 } else if (node->IsDocumentFragment()) {
788 aDescription.AssignLiteral("document fragment");
790 } else if (aTarget->IsInnerWindow() || aTarget->IsOuterWindow()) {
791 aDescription.AssignLiteral("window");
792 } else if (aTarget->IsRootWindow()) {
793 aDescription.AssignLiteral("root window");
794 } else {
795 // Probably something that inherits from DOMEventTargetHelper.
799 /* static */
800 nsresult EventDispatcher::Dispatch(EventTarget* aTarget,
801 nsPresContext* aPresContext,
802 WidgetEvent* aEvent, Event* aDOMEvent,
803 nsEventStatus* aEventStatus,
804 EventDispatchingCallback* aCallback,
805 nsTArray<EventTarget*>* aTargets) {
806 AUTO_PROFILER_LABEL_HOT("EventDispatcher::Dispatch", OTHER);
808 NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!");
809 NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched,
810 NS_ERROR_DOM_INVALID_STATE_ERR);
811 NS_ASSERTION(!aTargets || !aEvent->mMessage, "Wrong parameters!");
813 // If we're dispatching an already created DOMEvent object, make
814 // sure it is initialized!
815 // If aTargets is non-null, the event isn't going to be dispatched.
816 NS_ENSURE_TRUE(aEvent->mMessage || !aDOMEvent || aTargets,
817 NS_ERROR_DOM_INVALID_STATE_ERR);
819 // Events shall not be fired while we are in stable state to prevent anything
820 // visible from the scripts.
821 MOZ_ASSERT(!nsContentUtils::IsInStableOrMetaStableState());
822 NS_ENSURE_TRUE(!nsContentUtils::IsInStableOrMetaStableState(),
823 NS_ERROR_DOM_INVALID_STATE_ERR);
825 nsCOMPtr<EventTarget> target(aTarget);
827 RefPtr<PerformanceEventTiming> eventTimingEntry;
828 // Similar to PerformancePaintTiming, we don't need to
829 // expose them for printing documents
830 if (aPresContext && !aPresContext->IsPrintingOrPrintPreview()) {
831 eventTimingEntry =
832 PerformanceEventTiming::TryGenerateEventTiming(target, aEvent);
834 if (aEvent->IsTrusted() && aEvent->mMessage == eScroll) {
835 if (auto* perf = aPresContext->GetPerformanceMainThread()) {
836 if (!perf->HasDispatchedScrollEvent()) {
837 perf->SetHasDispatchedScrollEvent();
843 bool retargeted = false;
845 if (aEvent->mFlags.mRetargetToNonNativeAnonymous) {
846 nsIContent* content = nsIContent::FromEventTargetOrNull(target);
847 if (content && content->IsInNativeAnonymousSubtree()) {
848 nsCOMPtr<EventTarget> newTarget =
849 content->FindFirstNonChromeOnlyAccessContent();
850 NS_ENSURE_STATE(newTarget);
852 aEvent->mOriginalTarget = target;
853 target = newTarget;
854 retargeted = true;
858 if (aEvent->mFlags.mOnlyChromeDispatch) {
859 nsCOMPtr<Document> doc;
860 if (!IsEventTargetChrome(target, getter_AddRefs(doc)) && doc) {
861 nsPIDOMWindowInner* win = doc->GetInnerWindow();
862 // If we can't dispatch the event to chrome, do nothing.
863 EventTarget* piTarget = win ? win->GetParentTarget() : nullptr;
864 if (!piTarget) {
865 return NS_OK;
868 // Set the target to be the original dispatch target,
869 aEvent->mTarget = target;
870 // but use chrome event handler or BrowserChildMessageManager for event
871 // target chain.
872 target = piTarget;
873 } else if (NS_WARN_IF(!doc)) {
874 return NS_ERROR_UNEXPECTED;
878 #ifdef DEBUG
879 if (NS_IsMainThread() && aEvent->mMessage != eVoidEvent &&
880 !nsContentUtils::IsSafeToRunScript()) {
881 static const auto warn = [](bool aIsSystem) {
882 if (aIsSystem) {
883 NS_WARNING("Fix the caller!");
884 } else {
885 MOZ_CRASH("This is unsafe! Fix the caller!");
888 if (nsINode* node = nsINode::FromEventTargetOrNull(target)) {
889 // If this is a node, it's possible that this is some sort of DOM tree
890 // that is never accessed by script (for example an SVG image or XBL
891 // binding document or whatnot). We really only want to warn/assert here
892 // if there might be actual scripted listeners for this event, so restrict
893 // the warnings/asserts to the case when script can or once could touch
894 // this node's document.
895 Document* doc = node->OwnerDoc();
896 bool hasHadScriptHandlingObject;
897 nsIGlobalObject* global =
898 doc->GetScriptHandlingObject(hasHadScriptHandlingObject);
899 if (global || hasHadScriptHandlingObject) {
900 warn(nsContentUtils::IsChromeDoc(doc));
902 } else if (nsCOMPtr<nsIGlobalObject> global = target->GetOwnerGlobal()) {
903 warn(global->PrincipalOrNull()->IsSystemPrincipal());
907 if (aDOMEvent) {
908 WidgetEvent* innerEvent = aDOMEvent->WidgetEventPtr();
909 NS_ASSERTION(innerEvent == aEvent,
910 "The inner event of aDOMEvent is not the same as aEvent!");
912 #endif
914 nsresult rv = NS_OK;
915 bool externalDOMEvent = !!(aDOMEvent);
917 // If we have a PresContext, make sure it doesn't die before
918 // event dispatching is finished.
919 RefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
921 ELMCreationDetector cd;
922 nsTArray<EventTargetChainItem> chain;
923 if (cd.IsMainThread()) {
924 if (!sCachedMainThreadChains) {
925 sCachedMainThreadChains = new CachedChains();
928 if (sCachedMainThreadChains->mChain1.Capacity() ==
929 kCachedMainThreadChainSize) {
930 chain = std::move(sCachedMainThreadChains->mChain1);
931 } else if (sCachedMainThreadChains->mChain2.Capacity() ==
932 kCachedMainThreadChainSize) {
933 chain = std::move(sCachedMainThreadChains->mChain2);
934 } else {
935 chain.SetCapacity(kCachedMainThreadChainSize);
939 // Create the event target chain item for the event target.
940 EventTargetChainItem* targetEtci = EventTargetChainItem::Create(
941 chain, target->GetTargetForEventTargetChain());
942 MOZ_ASSERT(&chain[0] == targetEtci);
943 if (!targetEtci->IsValid()) {
944 EventTargetChainItem::DestroyLast(chain, targetEtci);
945 return NS_ERROR_FAILURE;
948 // Make sure that Event::target and Event::originalTarget
949 // point to the last item in the chain.
950 if (!aEvent->mTarget) {
951 // Note, CurrentTarget() points always to the object returned by
952 // GetTargetForEventTargetChain().
953 aEvent->mTarget = targetEtci->CurrentTarget();
954 } else {
955 // XXX But if the target is already set, use that. This is a hack
956 // for the 'load', 'beforeunload' and 'unload' events,
957 // which are dispatched to |window| but have document as their target.
959 // Make sure that the event target points to the right object.
960 aEvent->mTarget = aEvent->mTarget->GetTargetForEventTargetChain();
961 NS_ENSURE_STATE(aEvent->mTarget);
964 if (retargeted) {
965 aEvent->mOriginalTarget =
966 aEvent->mOriginalTarget->GetTargetForEventTargetChain();
967 NS_ENSURE_STATE(aEvent->mOriginalTarget);
968 } else {
969 aEvent->mOriginalTarget = aEvent->mTarget;
972 aEvent->mOriginalRelatedTarget = aEvent->mRelatedTarget;
974 bool clearTargets = false;
976 nsCOMPtr<nsIContent> content =
977 nsIContent::FromEventTargetOrNull(aEvent->mOriginalTarget);
978 bool isInAnon = content && content->IsInNativeAnonymousSubtree();
980 aEvent->mFlags.mIsBeingDispatched = true;
982 Maybe<uint32_t> activationTargetItemIndex;
984 // Create visitor object and start event dispatching.
985 // GetEventTargetParent for the original target.
986 nsEventStatus status = aDOMEvent && aDOMEvent->DefaultPrevented()
987 ? nsEventStatus_eConsumeNoDefault
988 : aEventStatus ? *aEventStatus
989 : nsEventStatus_eIgnore;
990 nsCOMPtr<EventTarget> targetForPreVisitor = aEvent->mTarget;
991 EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
992 isInAnon, targetForPreVisitor);
993 targetEtci->GetEventTargetParent(preVisitor);
995 if (preVisitor.mWantsActivationBehavior) {
996 MOZ_ASSERT(&chain[0] == targetEtci);
997 activationTargetItemIndex.emplace(0);
1000 if (!preVisitor.mCanHandle) {
1001 targetEtci = MayRetargetToChromeIfCanNotHandleEvent(
1002 chain, preVisitor, targetEtci, nullptr, content);
1004 if (!preVisitor.mCanHandle) {
1005 // The original target and chrome target (mAutomaticChromeDispatch=true)
1006 // can not handle the event but we still have to call their PreHandleEvent.
1007 for (uint32_t i = 0; i < chain.Length(); ++i) {
1008 chain[i].PreHandleEvent(preVisitor);
1011 clearTargets = ShouldClearTargets(aEvent);
1012 } else {
1013 // At least the original target can handle the event.
1014 // Setting the retarget to the |target| simplifies retargeting code.
1015 nsCOMPtr<EventTarget> t = aEvent->mTarget;
1016 targetEtci->SetNewTarget(t);
1017 // In order to not change the targetTouches array passed to TouchEvents
1018 // when dispatching events from JS, we need to store the initial Touch
1019 // objects on the list.
1020 if (aEvent->mClass == eTouchEventClass && aDOMEvent) {
1021 TouchEvent* touchEvent = static_cast<TouchEvent*>(aDOMEvent);
1022 TouchList* targetTouches = touchEvent->GetExistingTargetTouches();
1023 if (targetTouches) {
1024 Maybe<nsTArray<RefPtr<dom::Touch>>> initialTargetTouches;
1025 initialTargetTouches.emplace();
1026 for (uint32_t i = 0; i < targetTouches->Length(); ++i) {
1027 initialTargetTouches->AppendElement(targetTouches->Item(i));
1029 targetEtci->SetInitialTargetTouches(std::move(initialTargetTouches));
1030 targetTouches->Clear();
1033 EventTargetChainItem* topEtci = targetEtci;
1034 targetEtci = nullptr;
1035 while (preVisitor.GetParentTarget()) {
1036 EventTarget* parentTarget = preVisitor.GetParentTarget();
1037 EventTargetChainItem* parentEtci =
1038 EventTargetChainItem::Create(chain, parentTarget, topEtci);
1039 if (!parentEtci->IsValid()) {
1040 EventTargetChainItem::DestroyLast(chain, parentEtci);
1041 rv = NS_ERROR_FAILURE;
1042 break;
1045 parentEtci->SetIsSlotInClosedTree(preVisitor.mParentIsSlotInClosedTree);
1046 parentEtci->SetIsChromeHandler(preVisitor.mParentIsChromeHandler);
1048 // Item needs event retargetting.
1049 if (preVisitor.mEventTargetAtParent) {
1050 // Need to set the target of the event
1051 // so that also the next retargeting works.
1052 preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
1053 preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
1054 parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
1057 if (preVisitor.mRetargetedRelatedTarget) {
1058 preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget;
1061 parentEtci->GetEventTargetParent(preVisitor);
1063 if (preVisitor.mWantsActivationBehavior &&
1064 activationTargetItemIndex.isNothing() && aEvent->mFlags.mBubbles) {
1065 MOZ_ASSERT(&chain.LastElement() == parentEtci);
1066 activationTargetItemIndex.emplace(chain.Length() - 1);
1069 if (preVisitor.mCanHandle) {
1070 preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
1071 topEtci = parentEtci;
1072 } else {
1073 bool ignoreBecauseOfShadowDOM = preVisitor.mIgnoreBecauseOfShadowDOM;
1074 nsCOMPtr<nsINode> disabledTarget =
1075 nsINode::FromEventTargetOrNull(parentTarget);
1076 parentEtci = MayRetargetToChromeIfCanNotHandleEvent(
1077 chain, preVisitor, parentEtci, topEtci, disabledTarget);
1078 if (parentEtci && preVisitor.mCanHandle) {
1079 preVisitor.mTargetInKnownToBeHandledScope =
1080 preVisitor.mEvent->mTarget;
1081 EventTargetChainItem* item =
1082 EventTargetChainItem::GetFirstCanHandleEventTarget(chain);
1083 if (!ignoreBecauseOfShadowDOM) {
1084 // If we ignored the target because of Shadow DOM retargeting, we
1085 // shouldn't treat the target to be in the event path at all.
1086 item->SetNewTarget(parentTarget);
1088 topEtci = parentEtci;
1089 continue;
1091 break;
1095 if (activationTargetItemIndex) {
1096 chain[activationTargetItemIndex.value()].LegacyPreActivationBehavior(
1097 preVisitor);
1100 if (NS_SUCCEEDED(rv)) {
1101 if (aTargets) {
1102 aTargets->Clear();
1103 uint32_t numTargets = chain.Length();
1104 EventTarget** targets = aTargets->AppendElements(numTargets);
1105 for (uint32_t i = 0; i < numTargets; ++i) {
1106 targets[i] = chain[i].CurrentTarget()->GetTargetForDOMEvent();
1108 } else {
1109 // Event target chain is created. PreHandle the chain.
1110 for (uint32_t i = 0; i < chain.Length(); ++i) {
1111 chain[i].PreHandleEvent(preVisitor);
1114 RefPtr<nsRefreshDriver> refreshDriver;
1115 if (aEvent->IsTrusted() &&
1116 (aEvent->mMessage == eKeyPress ||
1117 aEvent->mMessage == eMouseClick) &&
1118 aPresContext && aPresContext->GetRootPresContext()) {
1119 refreshDriver = aPresContext->GetRootPresContext()->RefreshDriver();
1120 if (refreshDriver) {
1121 refreshDriver->EnterUserInputProcessing();
1124 auto cleanup = MakeScopeExit([&] {
1125 if (refreshDriver) {
1126 refreshDriver->ExitUserInputProcessing();
1130 clearTargets = ShouldClearTargets(aEvent);
1132 // Handle the chain.
1133 EventChainPostVisitor postVisitor(preVisitor);
1134 MOZ_RELEASE_ASSERT(!aEvent->mPath);
1135 aEvent->mPath = &chain;
1137 if (profiler_is_active()) {
1138 // Add a profiler label and a profiler marker for the actual
1139 // dispatch of the event.
1140 // This is a very hot code path, so we need to make sure not to
1141 // do this extra work when we're not profiling.
1142 if (!postVisitor.mDOMEvent) {
1143 // This is tiny bit slow, but happens only once per event.
1144 // Similar code also in EventListenerManager.
1145 RefPtr<Event> event = EventDispatcher::CreateEvent(
1146 aEvent->mOriginalTarget, aPresContext, aEvent, u""_ns);
1147 event.swap(postVisitor.mDOMEvent);
1149 nsAutoString typeStr;
1150 postVisitor.mDOMEvent->GetType(typeStr);
1151 AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
1152 "EventDispatcher::Dispatch", OTHER, typeStr);
1154 MarkerInnerWindowId innerWindowId;
1155 if (nsIGlobalObject* global = aEvent->mTarget->GetOwnerGlobal()) {
1156 if (nsPIDOMWindowInner* inner = global->GetAsInnerWindow()) {
1157 innerWindowId = MarkerInnerWindowId{inner->WindowID()};
1161 struct DOMEventMarker {
1162 static constexpr Span<const char> MarkerTypeName() {
1163 return MakeStringSpan("DOMEvent");
1165 static void StreamJSONMarkerData(
1166 baseprofiler::SpliceableJSONWriter& aWriter,
1167 const ProfilerString16View& aEventType,
1168 const nsCString& aTarget, const TimeStamp& aStartTime,
1169 const TimeStamp& aEventTimeStamp) {
1170 aWriter.StringProperty("eventType",
1171 NS_ConvertUTF16toUTF8(aEventType));
1172 if (!aTarget.IsEmpty()) {
1173 aWriter.StringProperty("target", aTarget);
1175 // This is the event processing latency, which is the time from
1176 // when the event was created, to when it was started to be
1177 // processed. Note that the computation of this latency is
1178 // deferred until serialization time, at the expense of some extra
1179 // memory.
1180 aWriter.DoubleProperty(
1181 "latency", (aStartTime - aEventTimeStamp).ToMilliseconds());
1183 static MarkerSchema MarkerTypeDisplay() {
1184 using MS = MarkerSchema;
1185 MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable,
1186 MS::Location::TimelineOverview};
1187 schema.SetChartLabel("{marker.data.eventType}");
1188 schema.SetTooltipLabel("{marker.data.eventType} - DOMEvent");
1189 schema.SetTableLabel(
1190 "{marker.data.eventType} - {marker.data.target}");
1191 schema.AddKeyLabelFormatSearchable("target", "Event Target",
1192 MS::Format::String,
1193 MS::Searchable::Searchable);
1194 schema.AddKeyLabelFormat("latency", "Latency",
1195 MS::Format::Duration);
1196 schema.AddKeyLabelFormatSearchable("eventType", "Event Type",
1197 MS::Format::String,
1198 MS::Searchable::Searchable);
1199 return schema;
1203 nsAutoCString target;
1204 DescribeEventTargetForProfilerMarker(aEvent->mTarget, target);
1206 auto startTime = TimeStamp::Now();
1207 profiler_add_marker("DOMEvent", geckoprofiler::category::DOM,
1208 {MarkerTiming::IntervalStart(startTime),
1209 MarkerInnerWindowId(innerWindowId)},
1210 DOMEventMarker{}, typeStr, target, startTime,
1211 aEvent->mTimeStamp);
1213 EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
1214 aCallback, cd);
1216 profiler_add_marker(
1217 "DOMEvent", geckoprofiler::category::DOM,
1218 {MarkerTiming::IntervalEnd(), std::move(innerWindowId)},
1219 DOMEventMarker{}, typeStr, target, startTime, aEvent->mTimeStamp);
1220 } else {
1221 EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
1222 aCallback, cd);
1224 aEvent->mPath = nullptr;
1226 if (aEvent->IsTrusted() &&
1227 (aEvent->mMessage == eKeyPress ||
1228 aEvent->mMessage == eMouseClick) &&
1229 aPresContext && aPresContext->GetRootPresContext()) {
1230 nsRefreshDriver* driver =
1231 aPresContext->GetRootPresContext()->RefreshDriver();
1232 if (driver && driver->HasPendingTick()) {
1233 switch (aEvent->mMessage) {
1234 case eKeyPress:
1235 driver->RegisterCompositionPayload(
1236 {layers::CompositionPayloadType::eKeyPress,
1237 aEvent->mTimeStamp});
1238 break;
1239 case eMouseClick: {
1240 if (aEvent->AsMouseEvent()->mInputSource ==
1241 MouseEvent_Binding::MOZ_SOURCE_MOUSE ||
1242 aEvent->AsMouseEvent()->mInputSource ==
1243 MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
1244 driver->RegisterCompositionPayload(
1245 {layers::CompositionPayloadType::eMouseUpFollowedByClick,
1246 aEvent->mTimeStamp});
1248 break;
1250 default:
1251 break;
1256 preVisitor.mEventStatus = postVisitor.mEventStatus;
1257 // If the DOM event was created during event flow.
1258 if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
1259 preVisitor.mDOMEvent = postVisitor.mDOMEvent;
1265 // Note, EventTargetChainItem objects are deleted when the chain goes out of
1266 // the scope.
1268 aEvent->mFlags.mIsBeingDispatched = false;
1269 aEvent->mFlags.mDispatchedAtLeastOnce = true;
1271 if (eventTimingEntry) {
1272 eventTimingEntry->FinalizeEventTiming(aEvent->mTarget);
1274 // https://dom.spec.whatwg.org/#concept-event-dispatch
1275 // step 10. If clearTargets, then:
1276 // 1. Set event's target to null.
1277 // 2. Set event's relatedTarget to null.
1278 // 3. Set event's touch target list to the empty list.
1279 if (clearTargets) {
1280 aEvent->mTarget = nullptr;
1281 aEvent->mOriginalTarget = nullptr;
1282 aEvent->mRelatedTarget = nullptr;
1283 aEvent->mOriginalRelatedTarget = nullptr;
1284 // XXXsmaug Check also all the touch objects.
1287 if (activationTargetItemIndex) {
1288 EventChainPostVisitor postVisitor(preVisitor);
1289 if (preVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
1290 chain[activationTargetItemIndex.value()].LegacyCanceledActivationBehavior(
1291 postVisitor);
1292 } else {
1293 chain[activationTargetItemIndex.value()].ActivationBehavior(postVisitor);
1295 preVisitor.mEventStatus = postVisitor.mEventStatus;
1296 // If the DOM event was created during event flow.
1297 if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
1298 preVisitor.mDOMEvent = postVisitor.mDOMEvent;
1302 if (!externalDOMEvent && preVisitor.mDOMEvent) {
1303 // A dom::Event was created while dispatching the event.
1304 // Duplicate private data if someone holds a pointer to it.
1305 nsrefcnt rc = 0;
1306 NS_RELEASE2(preVisitor.mDOMEvent, rc);
1307 if (preVisitor.mDOMEvent) {
1308 preVisitor.mDOMEvent->DuplicatePrivateData();
1312 if (aEventStatus) {
1313 *aEventStatus = preVisitor.mEventStatus;
1316 if (cd.IsMainThread() && chain.Capacity() == kCachedMainThreadChainSize &&
1317 sCachedMainThreadChains) {
1318 if (sCachedMainThreadChains->mChain1.Capacity() !=
1319 kCachedMainThreadChainSize) {
1320 chain.ClearAndRetainStorage();
1321 chain.SwapElements(sCachedMainThreadChains->mChain1);
1322 } else if (sCachedMainThreadChains->mChain2.Capacity() !=
1323 kCachedMainThreadChainSize) {
1324 chain.ClearAndRetainStorage();
1325 chain.SwapElements(sCachedMainThreadChains->mChain2);
1329 return rv;
1332 /* static */
1333 nsresult EventDispatcher::DispatchDOMEvent(EventTarget* aTarget,
1334 WidgetEvent* aEvent,
1335 Event* aDOMEvent,
1336 nsPresContext* aPresContext,
1337 nsEventStatus* aEventStatus) {
1338 if (aDOMEvent) {
1339 WidgetEvent* innerEvent = aDOMEvent->WidgetEventPtr();
1340 NS_ENSURE_TRUE(innerEvent, NS_ERROR_ILLEGAL_VALUE);
1342 // Don't modify the event if it's being dispatched right now.
1343 if (innerEvent->mFlags.mIsBeingDispatched) {
1344 return NS_ERROR_DOM_INVALID_STATE_ERR;
1347 bool dontResetTrusted = false;
1348 if (innerEvent->mFlags.mDispatchedAtLeastOnce) {
1349 innerEvent->mTarget = nullptr;
1350 innerEvent->mOriginalTarget = nullptr;
1351 } else {
1352 dontResetTrusted = aDOMEvent->IsTrusted();
1355 if (!dontResetTrusted) {
1356 // Check security state to determine if dispatcher is trusted
1357 bool trusted = NS_IsMainThread()
1358 ? nsContentUtils::LegacyIsCallerChromeOrNativeCode()
1359 : IsCurrentThreadRunningChromeWorker();
1360 aDOMEvent->SetTrusted(trusted);
1363 return EventDispatcher::Dispatch(aTarget, aPresContext, innerEvent,
1364 aDOMEvent, aEventStatus);
1365 } else if (aEvent) {
1366 return EventDispatcher::Dispatch(aTarget, aPresContext, aEvent, aDOMEvent,
1367 aEventStatus);
1369 return NS_ERROR_ILLEGAL_VALUE;
1372 /* static */ already_AddRefed<dom::Event> EventDispatcher::CreateEvent(
1373 EventTarget* aOwner, nsPresContext* aPresContext, WidgetEvent* aEvent,
1374 const nsAString& aEventType, CallerType aCallerType) {
1375 if (aEvent) {
1376 switch (aEvent->mClass) {
1377 case eMutationEventClass:
1378 return NS_NewDOMMutationEvent(aOwner, aPresContext,
1379 aEvent->AsMutationEvent());
1380 case eGUIEventClass:
1381 case eScrollPortEventClass:
1382 case eUIEventClass:
1383 return NS_NewDOMUIEvent(aOwner, aPresContext, aEvent->AsGUIEvent());
1384 case eScrollAreaEventClass:
1385 return NS_NewDOMScrollAreaEvent(aOwner, aPresContext,
1386 aEvent->AsScrollAreaEvent());
1387 case eKeyboardEventClass:
1388 return NS_NewDOMKeyboardEvent(aOwner, aPresContext,
1389 aEvent->AsKeyboardEvent());
1390 case eCompositionEventClass:
1391 return NS_NewDOMCompositionEvent(aOwner, aPresContext,
1392 aEvent->AsCompositionEvent());
1393 case eMouseEventClass:
1394 return NS_NewDOMMouseEvent(aOwner, aPresContext,
1395 aEvent->AsMouseEvent());
1396 case eFocusEventClass:
1397 return NS_NewDOMFocusEvent(aOwner, aPresContext,
1398 aEvent->AsFocusEvent());
1399 case eMouseScrollEventClass:
1400 return NS_NewDOMMouseScrollEvent(aOwner, aPresContext,
1401 aEvent->AsMouseScrollEvent());
1402 case eWheelEventClass:
1403 return NS_NewDOMWheelEvent(aOwner, aPresContext,
1404 aEvent->AsWheelEvent());
1405 case eEditorInputEventClass:
1406 return NS_NewDOMInputEvent(aOwner, aPresContext,
1407 aEvent->AsEditorInputEvent());
1408 case eLegacyTextEventClass:
1409 return NS_NewDOMTextEvent(aOwner, aPresContext,
1410 aEvent->AsLegacyTextEvent());
1411 case eDragEventClass:
1412 return NS_NewDOMDragEvent(aOwner, aPresContext, aEvent->AsDragEvent());
1413 case eClipboardEventClass:
1414 return NS_NewDOMClipboardEvent(aOwner, aPresContext,
1415 aEvent->AsClipboardEvent());
1416 case eSMILTimeEventClass:
1417 return NS_NewDOMTimeEvent(aOwner, aPresContext,
1418 aEvent->AsSMILTimeEvent());
1419 case eCommandEventClass:
1420 return NS_NewDOMCommandEvent(aOwner, aPresContext,
1421 aEvent->AsCommandEvent());
1422 case eSimpleGestureEventClass:
1423 return NS_NewDOMSimpleGestureEvent(aOwner, aPresContext,
1424 aEvent->AsSimpleGestureEvent());
1425 case ePointerEventClass:
1426 return NS_NewDOMPointerEvent(aOwner, aPresContext,
1427 aEvent->AsPointerEvent());
1428 case eTouchEventClass:
1429 return NS_NewDOMTouchEvent(aOwner, aPresContext,
1430 aEvent->AsTouchEvent());
1431 case eTransitionEventClass:
1432 return NS_NewDOMTransitionEvent(aOwner, aPresContext,
1433 aEvent->AsTransitionEvent());
1434 case eAnimationEventClass:
1435 return NS_NewDOMAnimationEvent(aOwner, aPresContext,
1436 aEvent->AsAnimationEvent());
1437 default:
1438 // For all other types of events, create a vanilla event object.
1439 return NS_NewDOMEvent(aOwner, aPresContext, aEvent);
1443 // And if we didn't get an event, check the type argument.
1445 if (aEventType.LowerCaseEqualsLiteral("mouseevent") ||
1446 aEventType.LowerCaseEqualsLiteral("mouseevents")) {
1447 return NS_NewDOMMouseEvent(aOwner, aPresContext, nullptr);
1449 if (aEventType.LowerCaseEqualsLiteral("dragevent")) {
1450 return NS_NewDOMDragEvent(aOwner, aPresContext, nullptr);
1452 if (aEventType.LowerCaseEqualsLiteral("keyboardevent")) {
1453 return NS_NewDOMKeyboardEvent(aOwner, aPresContext, nullptr);
1455 if (aEventType.LowerCaseEqualsLiteral("compositionevent")) {
1456 return NS_NewDOMCompositionEvent(aOwner, aPresContext, nullptr);
1458 if (aEventType.LowerCaseEqualsLiteral("textevent")) {
1459 if (!StaticPrefs::dom_events_textevent_enabled()) {
1460 return NS_NewDOMCompositionEvent(aOwner, aPresContext, nullptr);
1462 return NS_NewDOMTextEvent(aOwner, aPresContext, nullptr);
1464 if (aEventType.LowerCaseEqualsLiteral("mutationevent") ||
1465 aEventType.LowerCaseEqualsLiteral("mutationevents")) {
1466 return NS_NewDOMMutationEvent(aOwner, aPresContext, nullptr);
1468 if (aEventType.LowerCaseEqualsLiteral("deviceorientationevent")) {
1469 DeviceOrientationEventInit init;
1470 RefPtr<Event> event =
1471 DeviceOrientationEvent::Constructor(aOwner, u""_ns, init);
1472 event->MarkUninitialized();
1473 return event.forget();
1475 if (aEventType.LowerCaseEqualsLiteral("devicemotionevent")) {
1476 return NS_NewDOMDeviceMotionEvent(aOwner, aPresContext, nullptr);
1478 if (aEventType.LowerCaseEqualsLiteral("uievent") ||
1479 aEventType.LowerCaseEqualsLiteral("uievents")) {
1480 return NS_NewDOMUIEvent(aOwner, aPresContext, nullptr);
1482 if (aEventType.LowerCaseEqualsLiteral("event") ||
1483 aEventType.LowerCaseEqualsLiteral("events") ||
1484 aEventType.LowerCaseEqualsLiteral("htmlevents") ||
1485 aEventType.LowerCaseEqualsLiteral("svgevents")) {
1486 return NS_NewDOMEvent(aOwner, aPresContext, nullptr);
1488 if (aEventType.LowerCaseEqualsLiteral("messageevent")) {
1489 RefPtr<Event> event = new MessageEvent(aOwner, aPresContext, nullptr);
1490 return event.forget();
1492 if (aEventType.LowerCaseEqualsLiteral("beforeunloadevent")) {
1493 return NS_NewDOMBeforeUnloadEvent(aOwner, aPresContext, nullptr);
1495 if (aEventType.LowerCaseEqualsLiteral("touchevent") &&
1496 TouchEvent::LegacyAPIEnabled(
1497 nsContentUtils::GetDocShellForEventTarget(aOwner),
1498 aCallerType == CallerType::System)) {
1499 return NS_NewDOMTouchEvent(aOwner, aPresContext, nullptr);
1501 if (aEventType.LowerCaseEqualsLiteral("hashchangeevent")) {
1502 HashChangeEventInit init;
1503 RefPtr<Event> event = HashChangeEvent::Constructor(aOwner, u""_ns, init);
1504 event->MarkUninitialized();
1505 return event.forget();
1507 if (aEventType.LowerCaseEqualsLiteral("customevent")) {
1508 return NS_NewDOMCustomEvent(aOwner, aPresContext, nullptr);
1510 if (aEventType.LowerCaseEqualsLiteral("storageevent")) {
1511 RefPtr<Event> event =
1512 StorageEvent::Constructor(aOwner, u""_ns, StorageEventInit());
1513 event->MarkUninitialized();
1514 return event.forget();
1516 if (aEventType.LowerCaseEqualsLiteral("focusevent")) {
1517 RefPtr<Event> event = NS_NewDOMFocusEvent(aOwner, aPresContext, nullptr);
1518 event->MarkUninitialized();
1519 return event.forget();
1522 // Only allow these events for chrome
1523 if (aCallerType == CallerType::System) {
1524 if (aEventType.LowerCaseEqualsLiteral("simplegestureevent")) {
1525 return NS_NewDOMSimpleGestureEvent(aOwner, aPresContext, nullptr);
1527 if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") ||
1528 aEventType.LowerCaseEqualsLiteral("xulcommandevents")) {
1529 return NS_NewDOMXULCommandEvent(aOwner, aPresContext, nullptr);
1533 // NEW EVENT TYPES SHOULD NOT BE ADDED HERE; THEY SHOULD USE ONLY EVENT
1534 // CONSTRUCTORS
1536 return nullptr;
1539 struct CurrentTargetPathInfo {
1540 uint32_t mIndex;
1541 int32_t mHiddenSubtreeLevel;
1544 static CurrentTargetPathInfo TargetPathInfo(
1545 const nsTArray<EventTargetChainItem>& aEventPath,
1546 const EventTarget& aCurrentTarget) {
1547 int32_t currentTargetHiddenSubtreeLevel = 0;
1548 for (uint32_t index = aEventPath.Length(); index--;) {
1549 const EventTargetChainItem& item = aEventPath.ElementAt(index);
1550 if (item.PreHandleEventOnly()) {
1551 continue;
1554 if (item.IsRootOfClosedTree()) {
1555 currentTargetHiddenSubtreeLevel++;
1558 if (item.CurrentTarget() == &aCurrentTarget) {
1559 return {index, currentTargetHiddenSubtreeLevel};
1562 if (item.IsSlotInClosedTree()) {
1563 currentTargetHiddenSubtreeLevel--;
1566 MOZ_ASSERT_UNREACHABLE("No target found?");
1567 return {0, 0};
1570 // https://dom.spec.whatwg.org/#dom-event-composedpath
1571 void EventDispatcher::GetComposedPathFor(WidgetEvent* aEvent,
1572 nsTArray<RefPtr<EventTarget>>& aPath) {
1573 MOZ_ASSERT(aPath.IsEmpty());
1574 nsTArray<EventTargetChainItem>* path = aEvent->mPath;
1575 if (!path || path->IsEmpty() || !aEvent->mCurrentTarget) {
1576 return;
1579 EventTarget* currentTarget =
1580 aEvent->mCurrentTarget->GetTargetForEventTargetChain();
1581 if (!currentTarget) {
1582 return;
1585 CurrentTargetPathInfo currentTargetInfo =
1586 TargetPathInfo(*path, *currentTarget);
1589 int32_t maxHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1590 int32_t currentHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1591 for (uint32_t index = currentTargetInfo.mIndex; index--;) {
1592 EventTargetChainItem& item = path->ElementAt(index);
1593 if (item.PreHandleEventOnly()) {
1594 continue;
1597 if (item.IsRootOfClosedTree()) {
1598 currentHiddenLevel++;
1601 if (currentHiddenLevel <= maxHiddenLevel) {
1602 aPath.AppendElement(item.CurrentTarget()->GetTargetForDOMEvent());
1605 if (item.IsChromeHandler()) {
1606 break;
1609 if (item.IsSlotInClosedTree()) {
1610 currentHiddenLevel--;
1611 maxHiddenLevel = std::min(maxHiddenLevel, currentHiddenLevel);
1615 aPath.Reverse();
1618 aPath.AppendElement(currentTarget->GetTargetForDOMEvent());
1621 int32_t maxHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1622 int32_t currentHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1623 for (uint32_t index = currentTargetInfo.mIndex + 1; index < path->Length();
1624 ++index) {
1625 EventTargetChainItem& item = path->ElementAt(index);
1626 if (item.PreHandleEventOnly()) {
1627 continue;
1630 if (item.IsSlotInClosedTree()) {
1631 currentHiddenLevel++;
1634 if (item.IsChromeHandler()) {
1635 break;
1638 if (currentHiddenLevel <= maxHiddenLevel) {
1639 aPath.AppendElement(item.CurrentTarget()->GetTargetForDOMEvent());
1642 if (item.IsRootOfClosedTree()) {
1643 currentHiddenLevel--;
1644 maxHiddenLevel = std::min(maxHiddenLevel, currentHiddenLevel);
1650 void EventChainPreVisitor::IgnoreCurrentTargetBecauseOfShadowDOMRetargeting() {
1651 mCanHandle = false;
1652 mIgnoreBecauseOfShadowDOM = true;
1654 EventTarget* target = nullptr;
1656 auto getWindow = [this]() -> nsPIDOMWindowOuter* {
1657 nsINode* node = nsINode::FromEventTargetOrNull(this->mParentTarget);
1658 if (!node) {
1659 return nullptr;
1661 Document* doc = node->GetComposedDoc();
1662 if (!doc) {
1663 return nullptr;
1666 return doc->GetWindow();
1669 // The HTMLEditor is registered to nsWindowRoot, so we
1670 // want to dispatch events to it.
1671 if (nsCOMPtr<nsPIDOMWindowOuter> win = getWindow()) {
1672 target = win->GetParentTarget();
1674 SetParentTarget(target, false);
1676 mEventTargetAtParent = nullptr;
1679 } // namespace mozilla