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"
13 #include "nsIContent.h"
14 #include "nsIContentInlines.h"
15 #include "mozilla/dom/Document.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/TimeEvent.h"
51 #include "mozilla/dom/TouchEvent.h"
52 #include "mozilla/dom/TransitionEvent.h"
53 #include "mozilla/dom/WheelEvent.h"
54 #include "mozilla/dom/WorkerPrivate.h"
55 #include "mozilla/dom/XULCommandEvent.h"
56 #include "mozilla/EventDispatcher.h"
57 #include "mozilla/EventListenerManager.h"
58 #include "mozilla/InternalMutationEvent.h"
59 #include "mozilla/ipc/MessageChannel.h"
60 #include "mozilla/MiscEvents.h"
61 #include "mozilla/MouseEvents.h"
62 #include "mozilla/ProfilerLabels.h"
63 #include "mozilla/ProfilerMarkers.h"
64 #include "mozilla/ScopeExit.h"
65 #include "mozilla/Telemetry.h"
66 #include "mozilla/TextEvents.h"
67 #include "mozilla/TouchEvents.h"
68 #include "mozilla/Unused.h"
74 class ELMCreationDetector
{
77 // We can do this optimization only in the main thread.
78 : mNonMainThread(!NS_IsMainThread()),
79 mInitialCount(mNonMainThread
81 : EventListenerManager::sMainThreadCreatedCount
) {}
83 bool MayHaveNewListenerManager() {
84 return mNonMainThread
||
85 mInitialCount
!= EventListenerManager::sMainThreadCreatedCount
;
88 bool IsMainThread() { return !mNonMainThread
; }
92 uint32_t mInitialCount
;
95 static bool IsEventTargetChrome(EventTarget
* aEventTarget
,
96 Document
** aDocument
= nullptr) {
101 Document
* doc
= nullptr;
102 if (nsINode
* node
= nsINode::FromEventTargetOrNull(aEventTarget
)) {
103 doc
= node
->OwnerDoc();
104 } else if (nsPIDOMWindowInner
* window
=
105 nsPIDOMWindowInner::FromEventTargetOrNull(aEventTarget
)) {
106 doc
= window
->GetExtantDoc();
109 // nsContentUtils::IsChromeDoc is null-safe.
110 bool isChrome
= false;
112 isChrome
= nsContentUtils::IsChromeDoc(doc
);
114 nsCOMPtr
<Document
> retVal
= doc
;
115 retVal
.swap(*aDocument
);
117 } else if (nsCOMPtr
<nsIScriptObjectPrincipal
> sop
=
118 do_QueryInterface(aEventTarget
->GetOwnerGlobal())) {
119 isChrome
= sop
->GetPrincipal()->IsSystemPrincipal();
124 // EventTargetChainItem represents a single item in the event target chain.
125 class EventTargetChainItem
{
127 explicit EventTargetChainItem(EventTarget
* aTarget
)
128 : mTarget(aTarget
), mItemFlags(0) {
129 MOZ_COUNT_CTOR(EventTargetChainItem
);
132 MOZ_COUNTED_DTOR(EventTargetChainItem
)
134 static EventTargetChainItem
* Create(nsTArray
<EventTargetChainItem
>& aChain
,
135 EventTarget
* aTarget
,
136 EventTargetChainItem
* aChild
= nullptr) {
137 // The last item which can handle the event must be aChild.
138 MOZ_ASSERT(GetLastCanHandleEventTarget(aChain
) == aChild
);
139 MOZ_ASSERT(!aTarget
|| aTarget
== aTarget
->GetTargetForEventTargetChain());
140 EventTargetChainItem
* etci
= aChain
.AppendElement(aTarget
);
144 static void DestroyLast(nsTArray
<EventTargetChainItem
>& aChain
,
145 EventTargetChainItem
* aItem
) {
146 MOZ_ASSERT(&aChain
.LastElement() == aItem
);
147 aChain
.RemoveLastElement();
150 static EventTargetChainItem
* GetFirstCanHandleEventTarget(
151 nsTArray
<EventTargetChainItem
>& aChain
) {
152 return &aChain
[GetFirstCanHandleEventTargetIdx(aChain
)];
155 static uint32_t GetFirstCanHandleEventTargetIdx(
156 nsTArray
<EventTargetChainItem
>& aChain
) {
157 // aChain[i].PreHandleEventOnly() = true only when the target element wants
158 // PreHandleEvent and set mCanHandle=false. So we find the first element
159 // which can handle the event.
160 for (uint32_t i
= 0; i
< aChain
.Length(); ++i
) {
161 if (!aChain
[i
].PreHandleEventOnly()) {
169 static EventTargetChainItem
* GetLastCanHandleEventTarget(
170 nsTArray
<EventTargetChainItem
>& aChain
) {
171 // Fine the last item which can handle the event.
172 for (int32_t i
= aChain
.Length() - 1; i
>= 0; --i
) {
173 if (!aChain
[i
].PreHandleEventOnly()) {
180 bool IsValid() const {
181 NS_WARNING_ASSERTION(!!(mTarget
), "Event target is not valid!");
185 EventTarget
* GetNewTarget() const { return mNewTarget
; }
187 void SetNewTarget(EventTarget
* aNewTarget
) { mNewTarget
= aNewTarget
; }
189 EventTarget
* GetRetargetedRelatedTarget() { return mRetargetedRelatedTarget
; }
191 void SetRetargetedRelatedTarget(EventTarget
* aTarget
) {
192 mRetargetedRelatedTarget
= aTarget
;
195 void SetRetargetedTouchTarget(
196 Maybe
<nsTArray
<RefPtr
<EventTarget
>>>&& aTargets
) {
197 mRetargetedTouchTargets
= std::move(aTargets
);
200 bool HasRetargetTouchTargets() const {
201 return mRetargetedTouchTargets
.isSome() || mInitialTargetTouches
.isSome();
204 void RetargetTouchTargets(WidgetTouchEvent
* aTouchEvent
, Event
* aDOMEvent
) {
205 MOZ_ASSERT(HasRetargetTouchTargets());
206 MOZ_ASSERT(aTouchEvent
,
207 "mRetargetedTouchTargets should be empty when dispatching "
208 "non-touch events.");
210 if (mRetargetedTouchTargets
.isSome()) {
211 WidgetTouchEvent::TouchArray
& touches
= aTouchEvent
->mTouches
;
212 MOZ_ASSERT(!touches
.Length() ||
213 touches
.Length() == mRetargetedTouchTargets
->Length());
214 for (uint32_t i
= 0; i
< touches
.Length(); ++i
) {
215 touches
[i
]->mTarget
= mRetargetedTouchTargets
->ElementAt(i
);
220 // The number of touch objects in targetTouches list may change depending
221 // on the retargeting.
222 TouchEvent
* touchDOMEvent
= static_cast<TouchEvent
*>(aDOMEvent
);
223 TouchList
* targetTouches
= touchDOMEvent
->GetExistingTargetTouches();
225 targetTouches
->Clear();
226 if (mInitialTargetTouches
.isSome()) {
227 for (uint32_t i
= 0; i
< mInitialTargetTouches
->Length(); ++i
) {
228 Touch
* touch
= mInitialTargetTouches
->ElementAt(i
);
230 touch
->mTarget
= touch
->mOriginalTarget
;
232 targetTouches
->Append(touch
);
239 void SetInitialTargetTouches(
240 Maybe
<nsTArray
<RefPtr
<dom::Touch
>>>&& aInitialTargetTouches
) {
241 mInitialTargetTouches
= std::move(aInitialTargetTouches
);
244 void SetForceContentDispatch(bool aForce
) {
245 mFlags
.mForceContentDispatch
= aForce
;
248 bool ForceContentDispatch() const { return mFlags
.mForceContentDispatch
; }
250 void SetWantsWillHandleEvent(bool aWants
) {
251 mFlags
.mWantsWillHandleEvent
= aWants
;
254 bool WantsWillHandleEvent() const { return mFlags
.mWantsWillHandleEvent
; }
256 void SetWantsPreHandleEvent(bool aWants
) {
257 mFlags
.mWantsPreHandleEvent
= aWants
;
260 bool WantsPreHandleEvent() const { return mFlags
.mWantsPreHandleEvent
; }
262 void SetPreHandleEventOnly(bool aWants
) {
263 mFlags
.mPreHandleEventOnly
= aWants
;
266 bool PreHandleEventOnly() const { return mFlags
.mPreHandleEventOnly
; }
268 void SetRootOfClosedTree(bool aSet
) { mFlags
.mRootOfClosedTree
= aSet
; }
270 bool IsRootOfClosedTree() const { return mFlags
.mRootOfClosedTree
; }
272 void SetItemInShadowTree(bool aSet
) { mFlags
.mItemInShadowTree
= aSet
; }
274 bool IsItemInShadowTree() const { return mFlags
.mItemInShadowTree
; }
276 void SetIsSlotInClosedTree(bool aSet
) { mFlags
.mIsSlotInClosedTree
= aSet
; }
278 bool IsSlotInClosedTree() const { return mFlags
.mIsSlotInClosedTree
; }
280 void SetIsChromeHandler(bool aSet
) { mFlags
.mIsChromeHandler
= aSet
; }
282 bool IsChromeHandler() const { return mFlags
.mIsChromeHandler
; }
284 void SetMayHaveListenerManager(bool aMayHave
) {
285 mFlags
.mMayHaveManager
= aMayHave
;
288 bool MayHaveListenerManager() { return mFlags
.mMayHaveManager
; }
290 EventTarget
* CurrentTarget() const { return mTarget
; }
293 * Dispatches event through the event target chain.
294 * Handles capture, target and bubble phases both in default
295 * and system event group and calls also PostHandleEvent for each
299 static void HandleEventTargetChain(nsTArray
<EventTargetChainItem
>& aChain
,
300 EventChainPostVisitor
& aVisitor
,
301 EventDispatchingCallback
* aCallback
,
302 ELMCreationDetector
& aCd
);
305 * Resets aVisitor object and calls GetEventTargetParent.
306 * Copies mItemFlags and mItemData to the current EventTargetChainItem.
308 void GetEventTargetParent(EventChainPreVisitor
& aVisitor
);
311 * Copies mItemFlags, mItemData to aVisitor,
312 * calls LegacyPreActivationBehavior and copies both members back
313 * to this EventTargetChainitem.
315 void LegacyPreActivationBehavior(EventChainVisitor
& aVisitor
);
318 * Copies mItemFlags and mItemData to aVisitor and calls ActivationBehavior.
321 void ActivationBehavior(EventChainPostVisitor
& aVisitor
);
324 * Copies mItemFlags and mItemData to aVisitor and
325 * calls LegacyCanceledActivationBehavior.
327 void LegacyCanceledActivationBehavior(EventChainPostVisitor
& aVisitor
);
330 * Copies mItemFlags and mItemData to aVisitor.
331 * Calls PreHandleEvent for those items which called SetWantsPreHandleEvent.
333 void PreHandleEvent(EventChainVisitor
& aVisitor
);
336 * If the current item in the event target chain has an event listener
337 * manager, this method calls EventListenerManager::HandleEvent().
339 void HandleEvent(EventChainPostVisitor
& aVisitor
, ELMCreationDetector
& aCd
) {
340 if (WantsWillHandleEvent()) {
341 mTarget
->WillHandleEvent(aVisitor
);
343 if (aVisitor
.mEvent
->PropagationStopped()) {
346 if (aVisitor
.mEvent
->mFlags
.mOnlySystemGroupDispatch
&&
347 !aVisitor
.mEvent
->mFlags
.mInSystemGroup
) {
350 if (aVisitor
.mEvent
->mFlags
.mOnlySystemGroupDispatchInContent
&&
351 !aVisitor
.mEvent
->mFlags
.mInSystemGroup
&& !IsCurrentTargetChrome()) {
355 if (!MayHaveListenerManager() && !aCd
.MayHaveNewListenerManager()) {
358 mManager
= mTarget
->GetExistingListenerManager();
361 NS_ASSERTION(aVisitor
.mEvent
->mCurrentTarget
== nullptr,
362 "CurrentTarget should be null!");
364 mManager
->HandleEvent(aVisitor
.mPresContext
, aVisitor
.mEvent
,
365 &aVisitor
.mDOMEvent
, CurrentTarget(),
366 &aVisitor
.mEventStatus
, IsItemInShadowTree());
367 NS_ASSERTION(aVisitor
.mEvent
->mCurrentTarget
== nullptr,
368 "CurrentTarget should be null!");
373 * Copies mItemFlags and mItemData to aVisitor and calls PostHandleEvent.
375 MOZ_CAN_RUN_SCRIPT
void PostHandleEvent(EventChainPostVisitor
& aVisitor
);
378 const nsCOMPtr
<EventTarget
> mTarget
;
379 nsCOMPtr
<EventTarget
> mRetargetedRelatedTarget
;
380 Maybe
<nsTArray
<RefPtr
<EventTarget
>>> mRetargetedTouchTargets
;
381 Maybe
<nsTArray
<RefPtr
<dom::Touch
>>> mInitialTargetTouches
;
383 class EventTargetChainFlags
{
385 explicit EventTargetChainFlags() { SetRawFlags(0); }
386 // Cached flags for each EventTargetChainItem which are set when calling
387 // GetEventTargetParent to create event target chain. They are used to
388 // manage or speedup event dispatching.
389 bool mForceContentDispatch
: 1;
390 bool mWantsWillHandleEvent
: 1;
391 bool mMayHaveManager
: 1;
392 bool mChechedIfChrome
: 1;
393 bool mIsChromeContent
: 1;
394 bool mWantsPreHandleEvent
: 1;
395 bool mPreHandleEventOnly
: 1;
396 bool mRootOfClosedTree
: 1;
397 bool mItemInShadowTree
: 1;
398 bool mIsSlotInClosedTree
: 1;
399 bool mIsChromeHandler
: 1;
402 using RawFlags
= uint32_t;
403 void SetRawFlags(RawFlags aRawFlags
) {
405 sizeof(EventTargetChainFlags
) <= sizeof(RawFlags
),
406 "EventTargetChainFlags must not be bigger than the RawFlags");
407 memcpy(this, &aRawFlags
, sizeof(EventTargetChainFlags
));
412 nsCOMPtr
<nsISupports
> mItemData
;
413 // Event retargeting must happen whenever mNewTarget is non-null.
414 nsCOMPtr
<EventTarget
> mNewTarget
;
415 // Cache mTarget's event listener manager.
416 RefPtr
<EventListenerManager
> mManager
;
418 bool IsCurrentTargetChrome() {
419 if (!mFlags
.mChechedIfChrome
) {
420 mFlags
.mChechedIfChrome
= true;
421 if (IsEventTargetChrome(mTarget
)) {
422 mFlags
.mIsChromeContent
= true;
425 return mFlags
.mIsChromeContent
;
429 void EventTargetChainItem::GetEventTargetParent(
430 EventChainPreVisitor
& aVisitor
) {
432 mTarget
->GetEventTargetParent(aVisitor
);
433 SetForceContentDispatch(aVisitor
.mForceContentDispatch
);
434 SetWantsWillHandleEvent(aVisitor
.mWantsWillHandleEvent
);
435 SetMayHaveListenerManager(aVisitor
.mMayHaveListenerManager
);
436 SetWantsPreHandleEvent(aVisitor
.mWantsPreHandleEvent
);
437 SetPreHandleEventOnly(aVisitor
.mWantsPreHandleEvent
&& !aVisitor
.mCanHandle
);
438 SetRootOfClosedTree(aVisitor
.mRootOfClosedTree
);
439 SetItemInShadowTree(aVisitor
.mItemInShadowTree
);
440 SetRetargetedRelatedTarget(aVisitor
.mRetargetedRelatedTarget
);
441 SetRetargetedTouchTarget(std::move(aVisitor
.mRetargetedTouchTargets
));
442 mItemFlags
= aVisitor
.mItemFlags
;
443 mItemData
= aVisitor
.mItemData
;
446 void EventTargetChainItem::LegacyPreActivationBehavior(
447 EventChainVisitor
& aVisitor
) {
448 aVisitor
.mItemFlags
= mItemFlags
;
449 aVisitor
.mItemData
= mItemData
;
450 mTarget
->LegacyPreActivationBehavior(aVisitor
);
451 mItemFlags
= aVisitor
.mItemFlags
;
452 mItemData
= aVisitor
.mItemData
;
455 void EventTargetChainItem::PreHandleEvent(EventChainVisitor
& aVisitor
) {
456 if (!WantsPreHandleEvent()) {
459 aVisitor
.mItemFlags
= mItemFlags
;
460 aVisitor
.mItemData
= mItemData
;
461 Unused
<< mTarget
->PreHandleEvent(aVisitor
);
462 MOZ_ASSERT(mItemFlags
== aVisitor
.mItemFlags
);
463 MOZ_ASSERT(mItemData
== aVisitor
.mItemData
);
466 void EventTargetChainItem::ActivationBehavior(EventChainPostVisitor
& aVisitor
) {
467 aVisitor
.mItemFlags
= mItemFlags
;
468 aVisitor
.mItemData
= mItemData
;
469 mTarget
->ActivationBehavior(aVisitor
);
470 MOZ_ASSERT(mItemFlags
== aVisitor
.mItemFlags
);
471 MOZ_ASSERT(mItemData
== aVisitor
.mItemData
);
474 void EventTargetChainItem::LegacyCanceledActivationBehavior(
475 EventChainPostVisitor
& aVisitor
) {
476 aVisitor
.mItemFlags
= mItemFlags
;
477 aVisitor
.mItemData
= mItemData
;
478 mTarget
->LegacyCanceledActivationBehavior(aVisitor
);
479 MOZ_ASSERT(mItemFlags
== aVisitor
.mItemFlags
);
480 MOZ_ASSERT(mItemData
== aVisitor
.mItemData
);
483 void EventTargetChainItem::PostHandleEvent(EventChainPostVisitor
& aVisitor
) {
484 aVisitor
.mItemFlags
= mItemFlags
;
485 aVisitor
.mItemData
= mItemData
;
486 mTarget
->PostHandleEvent(aVisitor
);
487 MOZ_ASSERT(mItemFlags
== aVisitor
.mItemFlags
);
488 MOZ_ASSERT(mItemData
== aVisitor
.mItemData
);
491 void EventTargetChainItem::HandleEventTargetChain(
492 nsTArray
<EventTargetChainItem
>& aChain
, EventChainPostVisitor
& aVisitor
,
493 EventDispatchingCallback
* aCallback
, ELMCreationDetector
& aCd
) {
494 // Save the target so that it can be restored later.
495 nsCOMPtr
<EventTarget
> firstTarget
= aVisitor
.mEvent
->mTarget
;
496 nsCOMPtr
<EventTarget
> firstRelatedTarget
= aVisitor
.mEvent
->mRelatedTarget
;
497 Maybe
<AutoTArray
<nsCOMPtr
<EventTarget
>, 10>> firstTouchTargets
;
498 WidgetTouchEvent
* touchEvent
= nullptr;
499 if (aVisitor
.mEvent
->mClass
== eTouchEventClass
) {
500 touchEvent
= aVisitor
.mEvent
->AsTouchEvent();
501 if (!aVisitor
.mEvent
->mFlags
.mInSystemGroup
) {
502 firstTouchTargets
.emplace();
503 WidgetTouchEvent
* touchEvent
= aVisitor
.mEvent
->AsTouchEvent();
504 WidgetTouchEvent::TouchArray
& touches
= touchEvent
->mTouches
;
505 for (uint32_t i
= 0; i
< touches
.Length(); ++i
) {
506 firstTouchTargets
->AppendElement(touches
[i
]->mTarget
);
511 uint32_t chainLength
= aChain
.Length();
512 EventTargetChainItem
* chain
= aChain
.Elements();
513 uint32_t firstCanHandleEventTargetIdx
=
514 EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain
);
517 aVisitor
.mEvent
->mFlags
.mInCapturePhase
= true;
518 aVisitor
.mEvent
->mFlags
.mInBubblingPhase
= false;
519 aVisitor
.mEvent
->mFlags
.mInTargetPhase
= false;
520 for (uint32_t i
= chainLength
- 1; i
> firstCanHandleEventTargetIdx
; --i
) {
521 EventTargetChainItem
& item
= chain
[i
];
522 if (item
.PreHandleEventOnly()) {
525 if ((!aVisitor
.mEvent
->mFlags
.mNoContentDispatch
||
526 item
.ForceContentDispatch()) &&
527 !aVisitor
.mEvent
->PropagationStopped()) {
528 item
.HandleEvent(aVisitor
, aCd
);
531 if (item
.GetNewTarget()) {
532 // item is at anonymous boundary. Need to retarget for the child items.
533 for (uint32_t j
= i
; j
> 0; --j
) {
534 uint32_t childIndex
= j
- 1;
535 EventTarget
* newTarget
= chain
[childIndex
].GetNewTarget();
537 aVisitor
.mEvent
->mTarget
= newTarget
;
543 // https://dom.spec.whatwg.org/#dispatching-events
545 // "Set event's relatedTarget to tuple's relatedTarget."
546 // Note, the initial retargeting was done already when creating
547 // event target chain, so we need to do this only after calling
548 // HandleEvent, not before, like in the specification.
549 if (item
.GetRetargetedRelatedTarget()) {
551 for (uint32_t j
= i
; j
> 0; --j
) {
552 uint32_t childIndex
= j
- 1;
553 EventTarget
* relatedTarget
=
554 chain
[childIndex
].GetRetargetedRelatedTarget();
557 aVisitor
.mEvent
->mRelatedTarget
= relatedTarget
;
562 aVisitor
.mEvent
->mRelatedTarget
=
563 aVisitor
.mEvent
->mOriginalRelatedTarget
;
567 if (item
.HasRetargetTouchTargets()) {
569 for (uint32_t j
= i
; j
> 0; --j
) {
570 uint32_t childIndex
= j
- 1;
571 if (chain
[childIndex
].HasRetargetTouchTargets()) {
573 chain
[childIndex
].RetargetTouchTargets(touchEvent
,
579 WidgetTouchEvent::TouchArray
& touches
= touchEvent
->mTouches
;
580 for (uint32_t i
= 0; i
< touches
.Length(); ++i
) {
581 touches
[i
]->mTarget
= touches
[i
]->mOriginalTarget
;
588 const bool prefCorrectOrder
=
589 StaticPrefs::dom_events_phases_correctOrderOnTarget();
590 aVisitor
.mEvent
->mFlags
.mInTargetPhase
= true;
591 if (!prefCorrectOrder
) {
592 aVisitor
.mEvent
->mFlags
.mInBubblingPhase
= true;
594 EventTargetChainItem
& targetItem
= chain
[firstCanHandleEventTargetIdx
];
595 // Need to explicitly retarget touch targets so that initial targets get set
596 // properly in case nothing else retargeted touches.
597 if (targetItem
.HasRetargetTouchTargets()) {
598 targetItem
.RetargetTouchTargets(touchEvent
, aVisitor
.mDOMEvent
);
600 if (!aVisitor
.mEvent
->PropagationStopped() &&
601 (!aVisitor
.mEvent
->mFlags
.mNoContentDispatch
||
602 targetItem
.ForceContentDispatch())) {
603 targetItem
.HandleEvent(aVisitor
, aCd
);
605 if (prefCorrectOrder
) {
606 aVisitor
.mEvent
->mFlags
.mInCapturePhase
= false;
607 aVisitor
.mEvent
->mFlags
.mInBubblingPhase
= true;
608 if (!aVisitor
.mEvent
->PropagationStopped() &&
609 (!aVisitor
.mEvent
->mFlags
.mNoContentDispatch
||
610 targetItem
.ForceContentDispatch())) {
611 targetItem
.HandleEvent(aVisitor
, aCd
);
615 if (aVisitor
.mEvent
->mFlags
.mInSystemGroup
) {
616 targetItem
.PostHandleEvent(aVisitor
);
618 aVisitor
.mEvent
->mFlags
.mInTargetPhase
= false;
619 if (!prefCorrectOrder
) {
620 aVisitor
.mEvent
->mFlags
.mInCapturePhase
= false;
624 for (uint32_t i
= firstCanHandleEventTargetIdx
+ 1; i
< chainLength
; ++i
) {
625 EventTargetChainItem
& item
= chain
[i
];
626 if (item
.PreHandleEventOnly()) {
629 EventTarget
* newTarget
= item
.GetNewTarget();
631 // Item is at anonymous boundary. Need to retarget for the current item
632 // and for parent items.
633 aVisitor
.mEvent
->mTarget
= newTarget
;
636 // https://dom.spec.whatwg.org/#dispatching-events
638 // "Set event's relatedTarget to tuple's relatedTarget."
639 EventTarget
* relatedTarget
= item
.GetRetargetedRelatedTarget();
641 aVisitor
.mEvent
->mRelatedTarget
= relatedTarget
;
644 if (item
.HasRetargetTouchTargets()) {
645 item
.RetargetTouchTargets(touchEvent
, aVisitor
.mDOMEvent
);
648 if (aVisitor
.mEvent
->mFlags
.mBubbles
|| newTarget
) {
649 if ((!aVisitor
.mEvent
->mFlags
.mNoContentDispatch
||
650 item
.ForceContentDispatch()) &&
651 !aVisitor
.mEvent
->PropagationStopped()) {
652 item
.HandleEvent(aVisitor
, aCd
);
654 if (aVisitor
.mEvent
->mFlags
.mInSystemGroup
) {
655 item
.PostHandleEvent(aVisitor
);
659 aVisitor
.mEvent
->mFlags
.mInBubblingPhase
= false;
661 if (!aVisitor
.mEvent
->mFlags
.mInSystemGroup
&&
662 aVisitor
.mEvent
->IsAllowedToDispatchInSystemGroup()) {
663 // Dispatch to the system event group. Make sure to clear the
664 // STOP_DISPATCH flag since this resets for each event group.
665 aVisitor
.mEvent
->mFlags
.mPropagationStopped
= false;
666 aVisitor
.mEvent
->mFlags
.mImmediatePropagationStopped
= false;
668 // Setting back the original target of the event.
669 aVisitor
.mEvent
->mTarget
= aVisitor
.mEvent
->mOriginalTarget
;
670 aVisitor
.mEvent
->mRelatedTarget
= aVisitor
.mEvent
->mOriginalRelatedTarget
;
671 if (firstTouchTargets
) {
672 WidgetTouchEvent::TouchArray
& touches
= touchEvent
->mTouches
;
673 for (uint32_t i
= 0; i
< touches
.Length(); ++i
) {
674 touches
[i
]->mTarget
= touches
[i
]->mOriginalTarget
;
678 // Special handling if PresShell (or some other caller)
679 // used a callback object.
681 aCallback
->HandleEvent(aVisitor
);
684 // Retarget for system event group (which does the default handling too).
685 // Setting back the target which was used also for default event group.
686 aVisitor
.mEvent
->mTarget
= firstTarget
;
687 aVisitor
.mEvent
->mRelatedTarget
= firstRelatedTarget
;
688 if (firstTouchTargets
) {
689 WidgetTouchEvent::TouchArray
& touches
= touchEvent
->mTouches
;
690 for (uint32_t i
= 0; i
< firstTouchTargets
->Length(); ++i
) {
691 touches
[i
]->mTarget
= firstTouchTargets
->ElementAt(i
);
695 aVisitor
.mEvent
->mFlags
.mInSystemGroup
= true;
696 HandleEventTargetChain(aChain
, aVisitor
, aCallback
, aCd
);
697 aVisitor
.mEvent
->mFlags
.mInSystemGroup
= false;
699 // After dispatch, clear all the propagation flags so that
700 // system group listeners don't affect to the event.
701 aVisitor
.mEvent
->mFlags
.mPropagationStopped
= false;
702 aVisitor
.mEvent
->mFlags
.mImmediatePropagationStopped
= false;
706 // There are often 2 nested event dispatches ongoing at the same time, so
707 // have 2 separate caches.
708 static const uint32_t kCachedMainThreadChainSize
= 128;
709 struct CachedChains
{
710 nsTArray
<EventTargetChainItem
> mChain1
;
711 nsTArray
<EventTargetChainItem
> mChain2
;
713 static CachedChains
* sCachedMainThreadChains
= nullptr;
716 void EventDispatcher::Shutdown() {
717 delete sCachedMainThreadChains
;
718 sCachedMainThreadChains
= nullptr;
721 EventTargetChainItem
* EventTargetChainItemForChromeTarget(
722 nsTArray
<EventTargetChainItem
>& aChain
, nsINode
* aNode
,
723 EventTargetChainItem
* aChild
= nullptr) {
724 if (!aNode
->IsInComposedDoc()) {
727 nsPIDOMWindowInner
* win
= aNode
->OwnerDoc()->GetInnerWindow();
728 EventTarget
* piTarget
= win
? win
->GetParentTarget() : nullptr;
729 NS_ENSURE_TRUE(piTarget
, nullptr);
731 EventTargetChainItem
* etci
= EventTargetChainItem::Create(
732 aChain
, piTarget
->GetTargetForEventTargetChain(), aChild
);
733 if (!etci
->IsValid()) {
734 EventTargetChainItem::DestroyLast(aChain
, etci
);
740 /* static */ EventTargetChainItem
* MayRetargetToChromeIfCanNotHandleEvent(
741 nsTArray
<EventTargetChainItem
>& aChain
, EventChainPreVisitor
& aPreVisitor
,
742 EventTargetChainItem
* aTargetEtci
, EventTargetChainItem
* aChildEtci
,
744 if (!aPreVisitor
.mWantsPreHandleEvent
) {
745 // Keep EventTargetChainItem if we need to call PreHandleEvent on it.
746 EventTargetChainItem::DestroyLast(aChain
, aTargetEtci
);
748 if (aPreVisitor
.mAutomaticChromeDispatch
&& aContent
) {
749 aPreVisitor
.mRelatedTargetRetargetedInCurrentScope
= false;
750 // Event target couldn't handle the event. Try to propagate to chrome.
751 EventTargetChainItem
* chromeTargetEtci
=
752 EventTargetChainItemForChromeTarget(aChain
, aContent
, aChildEtci
);
753 if (chromeTargetEtci
) {
754 // If we propagate to chrome, need to ensure we mark
755 // EventTargetChainItem to be chrome handler so that event.composedPath()
756 // can return the right value.
757 chromeTargetEtci
->SetIsChromeHandler(true);
758 chromeTargetEtci
->GetEventTargetParent(aPreVisitor
);
759 return chromeTargetEtci
;
765 static bool ShouldClearTargets(WidgetEvent
* aEvent
) {
766 if (nsIContent
* finalTarget
=
767 nsIContent::FromEventTargetOrNull(aEvent
->mTarget
)) {
768 if (finalTarget
->SubtreeRoot()->IsShadowRoot()) {
773 if (nsIContent
* finalRelatedTarget
=
774 nsIContent::FromEventTargetOrNull(aEvent
->mRelatedTarget
)) {
775 if (finalRelatedTarget
->SubtreeRoot()->IsShadowRoot()) {
779 // XXXsmaug Check also all the touch objects.
784 static void DescribeEventTargetForProfilerMarker(const EventTarget
* aTarget
,
785 nsACString
& aDescription
) {
786 auto* node
= aTarget
->GetAsNode();
788 if (node
->IsElement()) {
789 nsAutoString nodeDescription
;
790 node
->AsElement()->Describe(nodeDescription
, true);
791 aDescription
= NS_ConvertUTF16toUTF8(nodeDescription
);
792 } else if (node
->IsDocument()) {
793 aDescription
.AssignLiteral("document");
794 } else if (node
->IsText()) {
795 aDescription
.AssignLiteral("text");
796 } else if (node
->IsDocumentFragment()) {
797 aDescription
.AssignLiteral("document fragment");
799 } else if (aTarget
->IsInnerWindow() || aTarget
->IsOuterWindow()) {
800 aDescription
.AssignLiteral("window");
801 } else if (aTarget
->IsRootWindow()) {
802 aDescription
.AssignLiteral("root window");
804 // Probably something that inherits from DOMEventTargetHelper.
809 nsresult
EventDispatcher::Dispatch(EventTarget
* aTarget
,
810 nsPresContext
* aPresContext
,
811 WidgetEvent
* aEvent
, Event
* aDOMEvent
,
812 nsEventStatus
* aEventStatus
,
813 EventDispatchingCallback
* aCallback
,
814 nsTArray
<EventTarget
*>* aTargets
) {
815 AUTO_PROFILER_LABEL_HOT("EventDispatcher::Dispatch", OTHER
);
817 NS_ASSERTION(aEvent
, "Trying to dispatch without WidgetEvent!");
818 NS_ENSURE_TRUE(!aEvent
->mFlags
.mIsBeingDispatched
,
819 NS_ERROR_DOM_INVALID_STATE_ERR
);
820 NS_ASSERTION(!aTargets
|| !aEvent
->mMessage
, "Wrong parameters!");
822 // If we're dispatching an already created DOMEvent object, make
823 // sure it is initialized!
824 // If aTargets is non-null, the event isn't going to be dispatched.
825 NS_ENSURE_TRUE(aEvent
->mMessage
|| !aDOMEvent
|| aTargets
,
826 NS_ERROR_DOM_INVALID_STATE_ERR
);
828 // Events shall not be fired while we are in stable state to prevent anything
829 // visible from the scripts.
830 MOZ_ASSERT(!nsContentUtils::IsInStableOrMetaStableState());
831 NS_ENSURE_TRUE(!nsContentUtils::IsInStableOrMetaStableState(),
832 NS_ERROR_DOM_INVALID_STATE_ERR
);
834 nsCOMPtr
<EventTarget
> target(aTarget
);
836 RefPtr
<PerformanceEventTiming
> eventTimingEntry
;
837 // Similar to PerformancePaintTiming, we don't need to
838 // expose them for printing documents
839 if (aPresContext
&& !aPresContext
->IsPrintingOrPrintPreview()) {
841 PerformanceEventTiming::TryGenerateEventTiming(target
, aEvent
);
843 if (aEvent
->IsTrusted() && aEvent
->mMessage
== eScroll
) {
844 if (auto* perf
= aPresContext
->GetPerformanceMainThread()) {
845 if (!perf
->HasDispatchedScrollEvent()) {
846 perf
->SetHasDispatchedScrollEvent();
852 bool retargeted
= false;
854 if (aEvent
->mFlags
.mRetargetToNonNativeAnonymous
) {
855 nsIContent
* content
= nsIContent::FromEventTargetOrNull(target
);
856 if (content
&& content
->IsInNativeAnonymousSubtree()) {
857 nsCOMPtr
<EventTarget
> newTarget
=
858 content
->FindFirstNonChromeOnlyAccessContent();
859 NS_ENSURE_STATE(newTarget
);
861 aEvent
->mOriginalTarget
= target
;
867 if (aEvent
->mFlags
.mOnlyChromeDispatch
) {
868 nsCOMPtr
<Document
> doc
;
869 if (!IsEventTargetChrome(target
, getter_AddRefs(doc
)) && doc
) {
870 nsPIDOMWindowInner
* win
= doc
->GetInnerWindow();
871 // If we can't dispatch the event to chrome, do nothing.
872 EventTarget
* piTarget
= win
? win
->GetParentTarget() : nullptr;
877 // Set the target to be the original dispatch target,
878 aEvent
->mTarget
= target
;
879 // but use chrome event handler or BrowserChildMessageManager for event
882 } else if (NS_WARN_IF(!doc
)) {
883 return NS_ERROR_UNEXPECTED
;
888 if (NS_IsMainThread() && aEvent
->mMessage
!= eVoidEvent
&&
889 !nsContentUtils::IsSafeToRunScript()) {
890 static const auto warn
= [](bool aIsSystem
) {
892 NS_WARNING("Fix the caller!");
894 MOZ_CRASH("This is unsafe! Fix the caller!");
897 if (nsINode
* node
= nsINode::FromEventTargetOrNull(target
)) {
898 // If this is a node, it's possible that this is some sort of DOM tree
899 // that is never accessed by script (for example an SVG image or XBL
900 // binding document or whatnot). We really only want to warn/assert here
901 // if there might be actual scripted listeners for this event, so restrict
902 // the warnings/asserts to the case when script can or once could touch
903 // this node's document.
904 Document
* doc
= node
->OwnerDoc();
905 bool hasHadScriptHandlingObject
;
906 nsIGlobalObject
* global
=
907 doc
->GetScriptHandlingObject(hasHadScriptHandlingObject
);
908 if (global
|| hasHadScriptHandlingObject
) {
909 warn(nsContentUtils::IsChromeDoc(doc
));
911 } else if (nsCOMPtr
<nsIGlobalObject
> global
= target
->GetOwnerGlobal()) {
912 warn(global
->PrincipalOrNull()->IsSystemPrincipal());
917 WidgetEvent
* innerEvent
= aDOMEvent
->WidgetEventPtr();
918 NS_ASSERTION(innerEvent
== aEvent
,
919 "The inner event of aDOMEvent is not the same as aEvent!");
924 bool externalDOMEvent
= !!(aDOMEvent
);
926 // If we have a PresContext, make sure it doesn't die before
927 // event dispatching is finished.
928 RefPtr
<nsPresContext
> kungFuDeathGrip(aPresContext
);
930 ELMCreationDetector cd
;
931 nsTArray
<EventTargetChainItem
> chain
;
932 if (cd
.IsMainThread()) {
933 if (!sCachedMainThreadChains
) {
934 sCachedMainThreadChains
= new CachedChains();
937 if (sCachedMainThreadChains
->mChain1
.Capacity() ==
938 kCachedMainThreadChainSize
) {
939 chain
= std::move(sCachedMainThreadChains
->mChain1
);
940 } else if (sCachedMainThreadChains
->mChain2
.Capacity() ==
941 kCachedMainThreadChainSize
) {
942 chain
= std::move(sCachedMainThreadChains
->mChain2
);
944 chain
.SetCapacity(kCachedMainThreadChainSize
);
948 // Create the event target chain item for the event target.
949 EventTargetChainItem
* targetEtci
= EventTargetChainItem::Create(
950 chain
, target
->GetTargetForEventTargetChain());
951 MOZ_ASSERT(&chain
[0] == targetEtci
);
952 if (!targetEtci
->IsValid()) {
953 EventTargetChainItem::DestroyLast(chain
, targetEtci
);
954 return NS_ERROR_FAILURE
;
957 // Make sure that Event::target and Event::originalTarget
958 // point to the last item in the chain.
959 if (!aEvent
->mTarget
) {
960 // Note, CurrentTarget() points always to the object returned by
961 // GetTargetForEventTargetChain().
962 aEvent
->mTarget
= targetEtci
->CurrentTarget();
964 // XXX But if the target is already set, use that. This is a hack
965 // for the 'load', 'beforeunload' and 'unload' events,
966 // which are dispatched to |window| but have document as their target.
968 // Make sure that the event target points to the right object.
969 aEvent
->mTarget
= aEvent
->mTarget
->GetTargetForEventTargetChain();
970 NS_ENSURE_STATE(aEvent
->mTarget
);
974 aEvent
->mOriginalTarget
=
975 aEvent
->mOriginalTarget
->GetTargetForEventTargetChain();
976 NS_ENSURE_STATE(aEvent
->mOriginalTarget
);
978 aEvent
->mOriginalTarget
= aEvent
->mTarget
;
981 aEvent
->mOriginalRelatedTarget
= aEvent
->mRelatedTarget
;
983 bool clearTargets
= false;
985 nsCOMPtr
<nsIContent
> content
=
986 nsIContent::FromEventTargetOrNull(aEvent
->mOriginalTarget
);
987 bool isInAnon
= content
&& content
->IsInNativeAnonymousSubtree();
989 aEvent
->mFlags
.mIsBeingDispatched
= true;
991 Maybe
<uint32_t> activationTargetItemIndex
;
993 // Create visitor object and start event dispatching.
994 // GetEventTargetParent for the original target.
995 nsEventStatus status
= aDOMEvent
&& aDOMEvent
->DefaultPrevented()
996 ? nsEventStatus_eConsumeNoDefault
997 : aEventStatus
? *aEventStatus
998 : nsEventStatus_eIgnore
;
999 nsCOMPtr
<EventTarget
> targetForPreVisitor
= aEvent
->mTarget
;
1000 EventChainPreVisitor
preVisitor(aPresContext
, aEvent
, aDOMEvent
, status
,
1001 isInAnon
, targetForPreVisitor
);
1002 targetEtci
->GetEventTargetParent(preVisitor
);
1004 if (preVisitor
.mWantsActivationBehavior
) {
1005 MOZ_ASSERT(&chain
[0] == targetEtci
);
1006 activationTargetItemIndex
.emplace(0);
1009 if (!preVisitor
.mCanHandle
) {
1010 targetEtci
= MayRetargetToChromeIfCanNotHandleEvent(
1011 chain
, preVisitor
, targetEtci
, nullptr, content
);
1013 if (!preVisitor
.mCanHandle
) {
1014 // The original target and chrome target (mAutomaticChromeDispatch=true)
1015 // can not handle the event but we still have to call their PreHandleEvent.
1016 for (uint32_t i
= 0; i
< chain
.Length(); ++i
) {
1017 chain
[i
].PreHandleEvent(preVisitor
);
1020 clearTargets
= ShouldClearTargets(aEvent
);
1022 // At least the original target can handle the event.
1023 // Setting the retarget to the |target| simplifies retargeting code.
1024 nsCOMPtr
<EventTarget
> t
= aEvent
->mTarget
;
1025 targetEtci
->SetNewTarget(t
);
1026 // In order to not change the targetTouches array passed to TouchEvents
1027 // when dispatching events from JS, we need to store the initial Touch
1028 // objects on the list.
1029 if (aEvent
->mClass
== eTouchEventClass
&& aDOMEvent
) {
1030 TouchEvent
* touchEvent
= static_cast<TouchEvent
*>(aDOMEvent
);
1031 TouchList
* targetTouches
= touchEvent
->GetExistingTargetTouches();
1032 if (targetTouches
) {
1033 Maybe
<nsTArray
<RefPtr
<dom::Touch
>>> initialTargetTouches
;
1034 initialTargetTouches
.emplace();
1035 for (uint32_t i
= 0; i
< targetTouches
->Length(); ++i
) {
1036 initialTargetTouches
->AppendElement(targetTouches
->Item(i
));
1038 targetEtci
->SetInitialTargetTouches(std::move(initialTargetTouches
));
1039 targetTouches
->Clear();
1042 EventTargetChainItem
* topEtci
= targetEtci
;
1043 targetEtci
= nullptr;
1044 while (preVisitor
.GetParentTarget()) {
1045 EventTarget
* parentTarget
= preVisitor
.GetParentTarget();
1046 EventTargetChainItem
* parentEtci
=
1047 EventTargetChainItem::Create(chain
, parentTarget
, topEtci
);
1048 if (!parentEtci
->IsValid()) {
1049 EventTargetChainItem::DestroyLast(chain
, parentEtci
);
1050 rv
= NS_ERROR_FAILURE
;
1054 parentEtci
->SetIsSlotInClosedTree(preVisitor
.mParentIsSlotInClosedTree
);
1055 parentEtci
->SetIsChromeHandler(preVisitor
.mParentIsChromeHandler
);
1057 // Item needs event retargetting.
1058 if (preVisitor
.mEventTargetAtParent
) {
1059 // Need to set the target of the event
1060 // so that also the next retargeting works.
1061 preVisitor
.mTargetInKnownToBeHandledScope
= preVisitor
.mEvent
->mTarget
;
1062 preVisitor
.mEvent
->mTarget
= preVisitor
.mEventTargetAtParent
;
1063 parentEtci
->SetNewTarget(preVisitor
.mEventTargetAtParent
);
1066 if (preVisitor
.mRetargetedRelatedTarget
) {
1067 preVisitor
.mEvent
->mRelatedTarget
= preVisitor
.mRetargetedRelatedTarget
;
1070 parentEtci
->GetEventTargetParent(preVisitor
);
1072 if (preVisitor
.mWantsActivationBehavior
&&
1073 activationTargetItemIndex
.isNothing() && aEvent
->mFlags
.mBubbles
) {
1074 MOZ_ASSERT(&chain
.LastElement() == parentEtci
);
1075 activationTargetItemIndex
.emplace(chain
.Length() - 1);
1078 if (preVisitor
.mCanHandle
) {
1079 preVisitor
.mTargetInKnownToBeHandledScope
= preVisitor
.mEvent
->mTarget
;
1080 topEtci
= parentEtci
;
1082 bool ignoreBecauseOfShadowDOM
= preVisitor
.mIgnoreBecauseOfShadowDOM
;
1083 nsCOMPtr
<nsINode
> disabledTarget
=
1084 nsINode::FromEventTargetOrNull(parentTarget
);
1085 parentEtci
= MayRetargetToChromeIfCanNotHandleEvent(
1086 chain
, preVisitor
, parentEtci
, topEtci
, disabledTarget
);
1087 if (parentEtci
&& preVisitor
.mCanHandle
) {
1088 preVisitor
.mTargetInKnownToBeHandledScope
=
1089 preVisitor
.mEvent
->mTarget
;
1090 EventTargetChainItem
* item
=
1091 EventTargetChainItem::GetFirstCanHandleEventTarget(chain
);
1092 if (!ignoreBecauseOfShadowDOM
) {
1093 // If we ignored the target because of Shadow DOM retargeting, we
1094 // shouldn't treat the target to be in the event path at all.
1095 item
->SetNewTarget(parentTarget
);
1097 topEtci
= parentEtci
;
1104 if (activationTargetItemIndex
) {
1105 chain
[activationTargetItemIndex
.value()].LegacyPreActivationBehavior(
1109 if (NS_SUCCEEDED(rv
)) {
1112 uint32_t numTargets
= chain
.Length();
1113 EventTarget
** targets
= aTargets
->AppendElements(numTargets
);
1114 for (uint32_t i
= 0; i
< numTargets
; ++i
) {
1115 targets
[i
] = chain
[i
].CurrentTarget()->GetTargetForDOMEvent();
1118 // Event target chain is created. PreHandle the chain.
1119 for (uint32_t i
= 0; i
< chain
.Length(); ++i
) {
1120 chain
[i
].PreHandleEvent(preVisitor
);
1123 RefPtr
<nsRefreshDriver
> refreshDriver
;
1124 if (aEvent
->IsTrusted() &&
1125 (aEvent
->mMessage
== eKeyPress
||
1126 aEvent
->mMessage
== eMouseClick
) &&
1127 aPresContext
&& aPresContext
->GetRootPresContext()) {
1128 refreshDriver
= aPresContext
->GetRootPresContext()->RefreshDriver();
1129 if (refreshDriver
) {
1130 refreshDriver
->EnterUserInputProcessing();
1133 auto cleanup
= MakeScopeExit([&] {
1134 if (refreshDriver
) {
1135 refreshDriver
->ExitUserInputProcessing();
1139 clearTargets
= ShouldClearTargets(aEvent
);
1141 // Handle the chain.
1142 EventChainPostVisitor
postVisitor(preVisitor
);
1143 MOZ_RELEASE_ASSERT(!aEvent
->mPath
);
1144 aEvent
->mPath
= &chain
;
1146 if (profiler_is_active()) {
1147 // Add a profiler label and a profiler marker for the actual
1148 // dispatch of the event.
1149 // This is a very hot code path, so we need to make sure not to
1150 // do this extra work when we're not profiling.
1151 if (!postVisitor
.mDOMEvent
) {
1152 // This is tiny bit slow, but happens only once per event.
1153 // Similar code also in EventListenerManager.
1154 nsCOMPtr
<EventTarget
> et
= aEvent
->mOriginalTarget
;
1155 RefPtr
<Event
> event
=
1156 EventDispatcher::CreateEvent(et
, aPresContext
, aEvent
, u
""_ns
);
1157 event
.swap(postVisitor
.mDOMEvent
);
1159 nsAutoString typeStr
;
1160 postVisitor
.mDOMEvent
->GetType(typeStr
);
1161 AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
1162 "EventDispatcher::Dispatch", OTHER
, typeStr
);
1164 nsCOMPtr
<nsIDocShell
> docShell
;
1165 docShell
= nsContentUtils::GetDocShellForEventTarget(aEvent
->mTarget
);
1166 MarkerInnerWindowId innerWindowId
;
1167 if (nsCOMPtr
<nsPIDOMWindowInner
> inner
=
1168 do_QueryInterface(aEvent
->mTarget
->GetOwnerGlobal())) {
1169 innerWindowId
= MarkerInnerWindowId
{inner
->WindowID()};
1172 struct DOMEventMarker
{
1173 static constexpr Span
<const char> MarkerTypeName() {
1174 return MakeStringSpan("DOMEvent");
1176 static void StreamJSONMarkerData(
1177 baseprofiler::SpliceableJSONWriter
& aWriter
,
1178 const ProfilerString16View
& aEventType
,
1179 const nsCString
& aTarget
, const TimeStamp
& aStartTime
,
1180 const TimeStamp
& aEventTimeStamp
) {
1181 aWriter
.StringProperty("eventType",
1182 NS_ConvertUTF16toUTF8(aEventType
));
1183 if (!aTarget
.IsEmpty()) {
1184 aWriter
.StringProperty("target", aTarget
);
1186 // This is the event processing latency, which is the time from
1187 // when the event was created, to when it was started to be
1188 // processed. Note that the computation of this latency is
1189 // deferred until serialization time, at the expense of some extra
1191 aWriter
.DoubleProperty(
1192 "latency", (aStartTime
- aEventTimeStamp
).ToMilliseconds());
1194 static MarkerSchema
MarkerTypeDisplay() {
1195 using MS
= MarkerSchema
;
1196 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
,
1197 MS::Location::TimelineOverview
};
1198 schema
.SetChartLabel("{marker.data.eventType}");
1199 schema
.SetTooltipLabel("{marker.data.eventType} - DOMEvent");
1200 schema
.SetTableLabel(
1201 "{marker.data.eventType} - {marker.data.target}");
1202 schema
.AddKeyLabelFormatSearchable("target", "Event Target",
1204 MS::Searchable::Searchable
);
1205 schema
.AddKeyLabelFormat("latency", "Latency",
1206 MS::Format::Duration
);
1207 schema
.AddKeyLabelFormatSearchable("eventType", "Event Type",
1209 MS::Searchable::Searchable
);
1214 nsAutoCString target
;
1215 DescribeEventTargetForProfilerMarker(aEvent
->mTarget
, target
);
1217 auto startTime
= TimeStamp::Now();
1218 profiler_add_marker("DOMEvent", geckoprofiler::category::DOM
,
1219 {MarkerTiming::IntervalStart(),
1220 MarkerInnerWindowId(innerWindowId
)},
1221 DOMEventMarker
{}, typeStr
, target
, startTime
,
1222 aEvent
->mTimeStamp
);
1224 EventTargetChainItem::HandleEventTargetChain(chain
, postVisitor
,
1227 profiler_add_marker(
1228 "DOMEvent", geckoprofiler::category::DOM
,
1229 {MarkerTiming::IntervalEnd(), std::move(innerWindowId
)},
1230 DOMEventMarker
{}, typeStr
, target
, startTime
, aEvent
->mTimeStamp
);
1232 EventTargetChainItem::HandleEventTargetChain(chain
, postVisitor
,
1235 aEvent
->mPath
= nullptr;
1237 if (aEvent
->IsTrusted() &&
1238 (aEvent
->mMessage
== eKeyPress
||
1239 aEvent
->mMessage
== eMouseClick
) &&
1240 aPresContext
&& aPresContext
->GetRootPresContext()) {
1241 nsRefreshDriver
* driver
=
1242 aPresContext
->GetRootPresContext()->RefreshDriver();
1243 if (driver
&& driver
->HasPendingTick()) {
1244 switch (aEvent
->mMessage
) {
1246 driver
->RegisterCompositionPayload(
1247 {layers::CompositionPayloadType::eKeyPress
,
1248 aEvent
->mTimeStamp
});
1251 if (aEvent
->AsMouseEvent()->mInputSource
==
1252 MouseEvent_Binding::MOZ_SOURCE_MOUSE
||
1253 aEvent
->AsMouseEvent()->mInputSource
==
1254 MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
1255 driver
->RegisterCompositionPayload(
1256 {layers::CompositionPayloadType::eMouseUpFollowedByClick
,
1257 aEvent
->mTimeStamp
});
1267 preVisitor
.mEventStatus
= postVisitor
.mEventStatus
;
1268 // If the DOM event was created during event flow.
1269 if (!preVisitor
.mDOMEvent
&& postVisitor
.mDOMEvent
) {
1270 preVisitor
.mDOMEvent
= postVisitor
.mDOMEvent
;
1276 // Note, EventTargetChainItem objects are deleted when the chain goes out of
1279 aEvent
->mFlags
.mIsBeingDispatched
= false;
1280 aEvent
->mFlags
.mDispatchedAtLeastOnce
= true;
1282 if (eventTimingEntry
) {
1283 eventTimingEntry
->FinalizeEventTiming(aEvent
->mTarget
);
1285 // https://dom.spec.whatwg.org/#concept-event-dispatch
1286 // step 10. If clearTargets, then:
1287 // 1. Set event's target to null.
1288 // 2. Set event's relatedTarget to null.
1289 // 3. Set event's touch target list to the empty list.
1291 aEvent
->mTarget
= nullptr;
1292 aEvent
->mOriginalTarget
= nullptr;
1293 aEvent
->mRelatedTarget
= nullptr;
1294 aEvent
->mOriginalRelatedTarget
= nullptr;
1295 // XXXsmaug Check also all the touch objects.
1298 if (activationTargetItemIndex
) {
1299 EventChainPostVisitor
postVisitor(preVisitor
);
1300 if (preVisitor
.mEventStatus
== nsEventStatus_eConsumeNoDefault
) {
1301 chain
[activationTargetItemIndex
.value()].LegacyCanceledActivationBehavior(
1304 chain
[activationTargetItemIndex
.value()].ActivationBehavior(postVisitor
);
1306 preVisitor
.mEventStatus
= postVisitor
.mEventStatus
;
1307 // If the DOM event was created during event flow.
1308 if (!preVisitor
.mDOMEvent
&& postVisitor
.mDOMEvent
) {
1309 preVisitor
.mDOMEvent
= postVisitor
.mDOMEvent
;
1313 if (!externalDOMEvent
&& preVisitor
.mDOMEvent
) {
1314 // A dom::Event was created while dispatching the event.
1315 // Duplicate private data if someone holds a pointer to it.
1317 NS_RELEASE2(preVisitor
.mDOMEvent
, rc
);
1318 if (preVisitor
.mDOMEvent
) {
1319 preVisitor
.mDOMEvent
->DuplicatePrivateData();
1324 *aEventStatus
= preVisitor
.mEventStatus
;
1327 if (cd
.IsMainThread() && chain
.Capacity() == kCachedMainThreadChainSize
&&
1328 sCachedMainThreadChains
) {
1329 if (sCachedMainThreadChains
->mChain1
.Capacity() !=
1330 kCachedMainThreadChainSize
) {
1331 chain
.ClearAndRetainStorage();
1332 chain
.SwapElements(sCachedMainThreadChains
->mChain1
);
1333 } else if (sCachedMainThreadChains
->mChain2
.Capacity() !=
1334 kCachedMainThreadChainSize
) {
1335 chain
.ClearAndRetainStorage();
1336 chain
.SwapElements(sCachedMainThreadChains
->mChain2
);
1344 nsresult
EventDispatcher::DispatchDOMEvent(EventTarget
* aTarget
,
1345 WidgetEvent
* aEvent
,
1347 nsPresContext
* aPresContext
,
1348 nsEventStatus
* aEventStatus
) {
1350 WidgetEvent
* innerEvent
= aDOMEvent
->WidgetEventPtr();
1351 NS_ENSURE_TRUE(innerEvent
, NS_ERROR_ILLEGAL_VALUE
);
1353 // Don't modify the event if it's being dispatched right now.
1354 if (innerEvent
->mFlags
.mIsBeingDispatched
) {
1355 return NS_ERROR_DOM_INVALID_STATE_ERR
;
1358 bool dontResetTrusted
= false;
1359 if (innerEvent
->mFlags
.mDispatchedAtLeastOnce
) {
1360 innerEvent
->mTarget
= nullptr;
1361 innerEvent
->mOriginalTarget
= nullptr;
1363 dontResetTrusted
= aDOMEvent
->IsTrusted();
1366 if (!dontResetTrusted
) {
1367 // Check security state to determine if dispatcher is trusted
1368 bool trusted
= NS_IsMainThread()
1369 ? nsContentUtils::LegacyIsCallerChromeOrNativeCode()
1370 : IsCurrentThreadRunningChromeWorker();
1371 aDOMEvent
->SetTrusted(trusted
);
1374 return EventDispatcher::Dispatch(aTarget
, aPresContext
, innerEvent
,
1375 aDOMEvent
, aEventStatus
);
1376 } else if (aEvent
) {
1377 return EventDispatcher::Dispatch(aTarget
, aPresContext
, aEvent
, aDOMEvent
,
1380 return NS_ERROR_ILLEGAL_VALUE
;
1383 /* static */ already_AddRefed
<dom::Event
> EventDispatcher::CreateEvent(
1384 EventTarget
* aOwner
, nsPresContext
* aPresContext
, WidgetEvent
* aEvent
,
1385 const nsAString
& aEventType
, CallerType aCallerType
) {
1387 switch (aEvent
->mClass
) {
1388 case eMutationEventClass
:
1389 return NS_NewDOMMutationEvent(aOwner
, aPresContext
,
1390 aEvent
->AsMutationEvent());
1391 case eGUIEventClass
:
1392 case eScrollPortEventClass
:
1394 return NS_NewDOMUIEvent(aOwner
, aPresContext
, aEvent
->AsGUIEvent());
1395 case eScrollAreaEventClass
:
1396 return NS_NewDOMScrollAreaEvent(aOwner
, aPresContext
,
1397 aEvent
->AsScrollAreaEvent());
1398 case eKeyboardEventClass
:
1399 return NS_NewDOMKeyboardEvent(aOwner
, aPresContext
,
1400 aEvent
->AsKeyboardEvent());
1401 case eCompositionEventClass
:
1402 return NS_NewDOMCompositionEvent(aOwner
, aPresContext
,
1403 aEvent
->AsCompositionEvent());
1404 case eMouseEventClass
:
1405 return NS_NewDOMMouseEvent(aOwner
, aPresContext
,
1406 aEvent
->AsMouseEvent());
1407 case eFocusEventClass
:
1408 return NS_NewDOMFocusEvent(aOwner
, aPresContext
,
1409 aEvent
->AsFocusEvent());
1410 case eMouseScrollEventClass
:
1411 return NS_NewDOMMouseScrollEvent(aOwner
, aPresContext
,
1412 aEvent
->AsMouseScrollEvent());
1413 case eWheelEventClass
:
1414 return NS_NewDOMWheelEvent(aOwner
, aPresContext
,
1415 aEvent
->AsWheelEvent());
1416 case eEditorInputEventClass
:
1417 return NS_NewDOMInputEvent(aOwner
, aPresContext
,
1418 aEvent
->AsEditorInputEvent());
1419 case eDragEventClass
:
1420 return NS_NewDOMDragEvent(aOwner
, aPresContext
, aEvent
->AsDragEvent());
1421 case eClipboardEventClass
:
1422 return NS_NewDOMClipboardEvent(aOwner
, aPresContext
,
1423 aEvent
->AsClipboardEvent());
1424 case eSMILTimeEventClass
:
1425 return NS_NewDOMTimeEvent(aOwner
, aPresContext
,
1426 aEvent
->AsSMILTimeEvent());
1427 case eCommandEventClass
:
1428 return NS_NewDOMCommandEvent(aOwner
, aPresContext
,
1429 aEvent
->AsCommandEvent());
1430 case eSimpleGestureEventClass
:
1431 return NS_NewDOMSimpleGestureEvent(aOwner
, aPresContext
,
1432 aEvent
->AsSimpleGestureEvent());
1433 case ePointerEventClass
:
1434 return NS_NewDOMPointerEvent(aOwner
, aPresContext
,
1435 aEvent
->AsPointerEvent());
1436 case eTouchEventClass
:
1437 return NS_NewDOMTouchEvent(aOwner
, aPresContext
,
1438 aEvent
->AsTouchEvent());
1439 case eTransitionEventClass
:
1440 return NS_NewDOMTransitionEvent(aOwner
, aPresContext
,
1441 aEvent
->AsTransitionEvent());
1442 case eAnimationEventClass
:
1443 return NS_NewDOMAnimationEvent(aOwner
, aPresContext
,
1444 aEvent
->AsAnimationEvent());
1446 // For all other types of events, create a vanilla event object.
1447 return NS_NewDOMEvent(aOwner
, aPresContext
, aEvent
);
1451 // And if we didn't get an event, check the type argument.
1453 if (aEventType
.LowerCaseEqualsLiteral("mouseevent") ||
1454 aEventType
.LowerCaseEqualsLiteral("mouseevents")) {
1455 return NS_NewDOMMouseEvent(aOwner
, aPresContext
, nullptr);
1457 if (aEventType
.LowerCaseEqualsLiteral("dragevent")) {
1458 return NS_NewDOMDragEvent(aOwner
, aPresContext
, nullptr);
1460 if (aEventType
.LowerCaseEqualsLiteral("keyboardevent")) {
1461 return NS_NewDOMKeyboardEvent(aOwner
, aPresContext
, nullptr);
1463 if (aEventType
.LowerCaseEqualsLiteral("compositionevent") ||
1464 aEventType
.LowerCaseEqualsLiteral("textevent")) {
1465 return NS_NewDOMCompositionEvent(aOwner
, aPresContext
, nullptr);
1467 if (aEventType
.LowerCaseEqualsLiteral("mutationevent") ||
1468 aEventType
.LowerCaseEqualsLiteral("mutationevents")) {
1469 return NS_NewDOMMutationEvent(aOwner
, aPresContext
, nullptr);
1471 if (aEventType
.LowerCaseEqualsLiteral("deviceorientationevent")) {
1472 DeviceOrientationEventInit init
;
1473 RefPtr
<Event
> event
=
1474 DeviceOrientationEvent::Constructor(aOwner
, u
""_ns
, init
);
1475 event
->MarkUninitialized();
1476 return event
.forget();
1478 if (aEventType
.LowerCaseEqualsLiteral("devicemotionevent")) {
1479 return NS_NewDOMDeviceMotionEvent(aOwner
, aPresContext
, nullptr);
1481 if (aEventType
.LowerCaseEqualsLiteral("uievent") ||
1482 aEventType
.LowerCaseEqualsLiteral("uievents")) {
1483 return NS_NewDOMUIEvent(aOwner
, aPresContext
, nullptr);
1485 if (aEventType
.LowerCaseEqualsLiteral("event") ||
1486 aEventType
.LowerCaseEqualsLiteral("events") ||
1487 aEventType
.LowerCaseEqualsLiteral("htmlevents") ||
1488 aEventType
.LowerCaseEqualsLiteral("svgevents")) {
1489 return NS_NewDOMEvent(aOwner
, aPresContext
, nullptr);
1491 if (aEventType
.LowerCaseEqualsLiteral("messageevent")) {
1492 RefPtr
<Event
> event
= new MessageEvent(aOwner
, aPresContext
, nullptr);
1493 return event
.forget();
1495 if (aEventType
.LowerCaseEqualsLiteral("beforeunloadevent")) {
1496 return NS_NewDOMBeforeUnloadEvent(aOwner
, aPresContext
, nullptr);
1498 if (aEventType
.LowerCaseEqualsLiteral("touchevent") &&
1499 TouchEvent::LegacyAPIEnabled(
1500 nsContentUtils::GetDocShellForEventTarget(aOwner
),
1501 aCallerType
== CallerType::System
)) {
1502 return NS_NewDOMTouchEvent(aOwner
, aPresContext
, nullptr);
1504 if (aEventType
.LowerCaseEqualsLiteral("hashchangeevent")) {
1505 HashChangeEventInit init
;
1506 RefPtr
<Event
> event
= HashChangeEvent::Constructor(aOwner
, u
""_ns
, init
);
1507 event
->MarkUninitialized();
1508 return event
.forget();
1510 if (aEventType
.LowerCaseEqualsLiteral("customevent")) {
1511 return NS_NewDOMCustomEvent(aOwner
, aPresContext
, nullptr);
1513 if (aEventType
.LowerCaseEqualsLiteral("storageevent")) {
1514 RefPtr
<Event
> event
=
1515 StorageEvent::Constructor(aOwner
, u
""_ns
, StorageEventInit());
1516 event
->MarkUninitialized();
1517 return event
.forget();
1519 if (aEventType
.LowerCaseEqualsLiteral("focusevent")) {
1520 RefPtr
<Event
> event
= NS_NewDOMFocusEvent(aOwner
, aPresContext
, nullptr);
1521 event
->MarkUninitialized();
1522 return event
.forget();
1525 // Only allow these events for chrome
1526 if (aCallerType
== CallerType::System
) {
1527 if (aEventType
.LowerCaseEqualsLiteral("simplegestureevent")) {
1528 return NS_NewDOMSimpleGestureEvent(aOwner
, aPresContext
, nullptr);
1530 if (aEventType
.LowerCaseEqualsLiteral("xulcommandevent") ||
1531 aEventType
.LowerCaseEqualsLiteral("xulcommandevents")) {
1532 return NS_NewDOMXULCommandEvent(aOwner
, aPresContext
, nullptr);
1536 // NEW EVENT TYPES SHOULD NOT BE ADDED HERE; THEY SHOULD USE ONLY EVENT
1542 struct CurrentTargetPathInfo
{
1544 int32_t mHiddenSubtreeLevel
;
1547 static CurrentTargetPathInfo
TargetPathInfo(
1548 const nsTArray
<EventTargetChainItem
>& aEventPath
,
1549 const EventTarget
& aCurrentTarget
) {
1550 int32_t currentTargetHiddenSubtreeLevel
= 0;
1551 for (uint32_t index
= aEventPath
.Length(); index
--;) {
1552 const EventTargetChainItem
& item
= aEventPath
.ElementAt(index
);
1553 if (item
.PreHandleEventOnly()) {
1557 if (item
.IsRootOfClosedTree()) {
1558 currentTargetHiddenSubtreeLevel
++;
1561 if (item
.CurrentTarget() == &aCurrentTarget
) {
1562 return {index
, currentTargetHiddenSubtreeLevel
};
1565 if (item
.IsSlotInClosedTree()) {
1566 currentTargetHiddenSubtreeLevel
--;
1569 MOZ_ASSERT_UNREACHABLE("No target found?");
1573 // https://dom.spec.whatwg.org/#dom-event-composedpath
1574 void EventDispatcher::GetComposedPathFor(WidgetEvent
* aEvent
,
1575 nsTArray
<RefPtr
<EventTarget
>>& aPath
) {
1576 MOZ_ASSERT(aPath
.IsEmpty());
1577 nsTArray
<EventTargetChainItem
>* path
= aEvent
->mPath
;
1578 if (!path
|| path
->IsEmpty() || !aEvent
->mCurrentTarget
) {
1582 EventTarget
* currentTarget
=
1583 aEvent
->mCurrentTarget
->GetTargetForEventTargetChain();
1584 if (!currentTarget
) {
1588 CurrentTargetPathInfo currentTargetInfo
=
1589 TargetPathInfo(*path
, *currentTarget
);
1592 int32_t maxHiddenLevel
= currentTargetInfo
.mHiddenSubtreeLevel
;
1593 int32_t currentHiddenLevel
= currentTargetInfo
.mHiddenSubtreeLevel
;
1594 for (uint32_t index
= currentTargetInfo
.mIndex
; index
--;) {
1595 EventTargetChainItem
& item
= path
->ElementAt(index
);
1596 if (item
.PreHandleEventOnly()) {
1600 if (item
.IsRootOfClosedTree()) {
1601 currentHiddenLevel
++;
1604 if (currentHiddenLevel
<= maxHiddenLevel
) {
1605 aPath
.AppendElement(item
.CurrentTarget()->GetTargetForDOMEvent());
1608 if (item
.IsChromeHandler()) {
1612 if (item
.IsSlotInClosedTree()) {
1613 currentHiddenLevel
--;
1614 maxHiddenLevel
= std::min(maxHiddenLevel
, currentHiddenLevel
);
1621 aPath
.AppendElement(currentTarget
->GetTargetForDOMEvent());
1624 int32_t maxHiddenLevel
= currentTargetInfo
.mHiddenSubtreeLevel
;
1625 int32_t currentHiddenLevel
= currentTargetInfo
.mHiddenSubtreeLevel
;
1626 for (uint32_t index
= currentTargetInfo
.mIndex
+ 1; index
< path
->Length();
1628 EventTargetChainItem
& item
= path
->ElementAt(index
);
1629 if (item
.PreHandleEventOnly()) {
1633 if (item
.IsSlotInClosedTree()) {
1634 currentHiddenLevel
++;
1637 if (item
.IsChromeHandler()) {
1641 if (currentHiddenLevel
<= maxHiddenLevel
) {
1642 aPath
.AppendElement(item
.CurrentTarget()->GetTargetForDOMEvent());
1645 if (item
.IsRootOfClosedTree()) {
1646 currentHiddenLevel
--;
1647 maxHiddenLevel
= std::min(maxHiddenLevel
, currentHiddenLevel
);
1653 void EventChainPreVisitor::IgnoreCurrentTargetBecauseOfShadowDOMRetargeting() {
1655 mIgnoreBecauseOfShadowDOM
= true;
1657 EventTarget
* target
= nullptr;
1659 auto getWindow
= [this]() -> nsPIDOMWindowOuter
* {
1660 nsINode
* node
= nsINode::FromEventTargetOrNull(this->mParentTarget
);
1664 Document
* doc
= node
->GetComposedDoc();
1669 return doc
->GetWindow();
1672 // The HTMLEditor is registered to nsWindowRoot, so we
1673 // want to dispatch events to it.
1674 if (nsCOMPtr
<nsPIDOMWindowOuter
> win
= getWindow()) {
1675 target
= win
->GetParentTarget();
1677 SetParentTarget(target
, false);
1679 mEventTargetAtParent
= nullptr;
1682 } // namespace mozilla