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 aVisitor
.mEvent
->mFlags
.mInTargetPhase
= true;
589 EventTargetChainItem
& targetItem
= chain
[firstCanHandleEventTargetIdx
];
590 // Need to explicitly retarget touch targets so that initial targets get set
591 // properly in case nothing else retargeted touches.
592 if (targetItem
.HasRetargetTouchTargets()) {
593 targetItem
.RetargetTouchTargets(touchEvent
, aVisitor
.mDOMEvent
);
595 if (!aVisitor
.mEvent
->PropagationStopped() &&
596 (!aVisitor
.mEvent
->mFlags
.mNoContentDispatch
||
597 targetItem
.ForceContentDispatch())) {
598 targetItem
.HandleEvent(aVisitor
, aCd
);
600 aVisitor
.mEvent
->mFlags
.mInCapturePhase
= false;
601 aVisitor
.mEvent
->mFlags
.mInBubblingPhase
= true;
602 if (!aVisitor
.mEvent
->PropagationStopped() &&
603 (!aVisitor
.mEvent
->mFlags
.mNoContentDispatch
||
604 targetItem
.ForceContentDispatch())) {
605 targetItem
.HandleEvent(aVisitor
, aCd
);
608 if (aVisitor
.mEvent
->mFlags
.mInSystemGroup
) {
609 targetItem
.PostHandleEvent(aVisitor
);
611 aVisitor
.mEvent
->mFlags
.mInTargetPhase
= false;
614 for (uint32_t i
= firstCanHandleEventTargetIdx
+ 1; i
< chainLength
; ++i
) {
615 EventTargetChainItem
& item
= chain
[i
];
616 if (item
.PreHandleEventOnly()) {
619 EventTarget
* newTarget
= item
.GetNewTarget();
621 // Item is at anonymous boundary. Need to retarget for the current item
622 // and for parent items.
623 aVisitor
.mEvent
->mTarget
= newTarget
;
626 // https://dom.spec.whatwg.org/#dispatching-events
628 // "Set event's relatedTarget to tuple's relatedTarget."
629 EventTarget
* relatedTarget
= item
.GetRetargetedRelatedTarget();
631 aVisitor
.mEvent
->mRelatedTarget
= relatedTarget
;
634 if (item
.HasRetargetTouchTargets()) {
635 item
.RetargetTouchTargets(touchEvent
, aVisitor
.mDOMEvent
);
638 if (aVisitor
.mEvent
->mFlags
.mBubbles
|| newTarget
) {
639 if ((!aVisitor
.mEvent
->mFlags
.mNoContentDispatch
||
640 item
.ForceContentDispatch()) &&
641 !aVisitor
.mEvent
->PropagationStopped()) {
642 item
.HandleEvent(aVisitor
, aCd
);
644 if (aVisitor
.mEvent
->mFlags
.mInSystemGroup
) {
645 item
.PostHandleEvent(aVisitor
);
649 aVisitor
.mEvent
->mFlags
.mInBubblingPhase
= false;
651 if (!aVisitor
.mEvent
->mFlags
.mInSystemGroup
&&
652 aVisitor
.mEvent
->IsAllowedToDispatchInSystemGroup()) {
653 // Dispatch to the system event group. Make sure to clear the
654 // STOP_DISPATCH flag since this resets for each event group.
655 aVisitor
.mEvent
->mFlags
.mPropagationStopped
= false;
656 aVisitor
.mEvent
->mFlags
.mImmediatePropagationStopped
= false;
658 // Setting back the original target of the event.
659 aVisitor
.mEvent
->mTarget
= aVisitor
.mEvent
->mOriginalTarget
;
660 aVisitor
.mEvent
->mRelatedTarget
= aVisitor
.mEvent
->mOriginalRelatedTarget
;
661 if (firstTouchTargets
) {
662 WidgetTouchEvent::TouchArray
& touches
= touchEvent
->mTouches
;
663 for (uint32_t i
= 0; i
< touches
.Length(); ++i
) {
664 touches
[i
]->mTarget
= touches
[i
]->mOriginalTarget
;
668 // Special handling if PresShell (or some other caller)
669 // used a callback object.
671 aCallback
->HandleEvent(aVisitor
);
674 // Retarget for system event group (which does the default handling too).
675 // Setting back the target which was used also for default event group.
676 aVisitor
.mEvent
->mTarget
= firstTarget
;
677 aVisitor
.mEvent
->mRelatedTarget
= firstRelatedTarget
;
678 if (firstTouchTargets
) {
679 WidgetTouchEvent::TouchArray
& touches
= touchEvent
->mTouches
;
680 for (uint32_t i
= 0; i
< firstTouchTargets
->Length(); ++i
) {
681 touches
[i
]->mTarget
= firstTouchTargets
->ElementAt(i
);
685 aVisitor
.mEvent
->mFlags
.mInSystemGroup
= true;
686 HandleEventTargetChain(aChain
, aVisitor
, aCallback
, aCd
);
687 aVisitor
.mEvent
->mFlags
.mInSystemGroup
= false;
689 // After dispatch, clear all the propagation flags so that
690 // system group listeners don't affect to the event.
691 aVisitor
.mEvent
->mFlags
.mPropagationStopped
= false;
692 aVisitor
.mEvent
->mFlags
.mImmediatePropagationStopped
= false;
696 // There are often 2 nested event dispatches ongoing at the same time, so
697 // have 2 separate caches.
698 static const uint32_t kCachedMainThreadChainSize
= 128;
699 struct CachedChains
{
700 nsTArray
<EventTargetChainItem
> mChain1
;
701 nsTArray
<EventTargetChainItem
> mChain2
;
703 static CachedChains
* sCachedMainThreadChains
= nullptr;
706 void EventDispatcher::Shutdown() {
707 delete sCachedMainThreadChains
;
708 sCachedMainThreadChains
= nullptr;
711 EventTargetChainItem
* EventTargetChainItemForChromeTarget(
712 nsTArray
<EventTargetChainItem
>& aChain
, nsINode
* aNode
,
713 EventTargetChainItem
* aChild
= nullptr) {
714 if (!aNode
->IsInComposedDoc()) {
717 nsPIDOMWindowInner
* win
= aNode
->OwnerDoc()->GetInnerWindow();
718 EventTarget
* piTarget
= win
? win
->GetParentTarget() : nullptr;
719 NS_ENSURE_TRUE(piTarget
, nullptr);
721 EventTargetChainItem
* etci
= EventTargetChainItem::Create(
722 aChain
, piTarget
->GetTargetForEventTargetChain(), aChild
);
723 if (!etci
->IsValid()) {
724 EventTargetChainItem::DestroyLast(aChain
, etci
);
730 /* static */ EventTargetChainItem
* MayRetargetToChromeIfCanNotHandleEvent(
731 nsTArray
<EventTargetChainItem
>& aChain
, EventChainPreVisitor
& aPreVisitor
,
732 EventTargetChainItem
* aTargetEtci
, EventTargetChainItem
* aChildEtci
,
734 if (!aPreVisitor
.mWantsPreHandleEvent
) {
735 // Keep EventTargetChainItem if we need to call PreHandleEvent on it.
736 EventTargetChainItem::DestroyLast(aChain
, aTargetEtci
);
738 if (aPreVisitor
.mAutomaticChromeDispatch
&& aContent
) {
739 aPreVisitor
.mRelatedTargetRetargetedInCurrentScope
= false;
740 // Event target couldn't handle the event. Try to propagate to chrome.
741 EventTargetChainItem
* chromeTargetEtci
=
742 EventTargetChainItemForChromeTarget(aChain
, aContent
, aChildEtci
);
743 if (chromeTargetEtci
) {
744 // If we propagate to chrome, need to ensure we mark
745 // EventTargetChainItem to be chrome handler so that event.composedPath()
746 // can return the right value.
747 chromeTargetEtci
->SetIsChromeHandler(true);
748 chromeTargetEtci
->GetEventTargetParent(aPreVisitor
);
749 return chromeTargetEtci
;
755 static bool ShouldClearTargets(WidgetEvent
* aEvent
) {
756 if (nsIContent
* finalTarget
=
757 nsIContent::FromEventTargetOrNull(aEvent
->mTarget
)) {
758 if (finalTarget
->SubtreeRoot()->IsShadowRoot()) {
763 if (nsIContent
* finalRelatedTarget
=
764 nsIContent::FromEventTargetOrNull(aEvent
->mRelatedTarget
)) {
765 if (finalRelatedTarget
->SubtreeRoot()->IsShadowRoot()) {
769 // XXXsmaug Check also all the touch objects.
774 static void DescribeEventTargetForProfilerMarker(const EventTarget
* aTarget
,
775 nsACString
& aDescription
) {
776 auto* node
= aTarget
->GetAsNode();
778 if (node
->IsElement()) {
779 nsAutoString nodeDescription
;
780 node
->AsElement()->Describe(nodeDescription
, true);
781 aDescription
= NS_ConvertUTF16toUTF8(nodeDescription
);
782 } else if (node
->IsDocument()) {
783 aDescription
.AssignLiteral("document");
784 } else if (node
->IsText()) {
785 aDescription
.AssignLiteral("text");
786 } else if (node
->IsDocumentFragment()) {
787 aDescription
.AssignLiteral("document fragment");
789 } else if (aTarget
->IsInnerWindow() || aTarget
->IsOuterWindow()) {
790 aDescription
.AssignLiteral("window");
791 } else if (aTarget
->IsRootWindow()) {
792 aDescription
.AssignLiteral("root window");
794 // Probably something that inherits from DOMEventTargetHelper.
799 nsresult
EventDispatcher::Dispatch(EventTarget
* aTarget
,
800 nsPresContext
* aPresContext
,
801 WidgetEvent
* aEvent
, Event
* aDOMEvent
,
802 nsEventStatus
* aEventStatus
,
803 EventDispatchingCallback
* aCallback
,
804 nsTArray
<EventTarget
*>* aTargets
) {
805 AUTO_PROFILER_LABEL_HOT("EventDispatcher::Dispatch", OTHER
);
807 NS_ASSERTION(aEvent
, "Trying to dispatch without WidgetEvent!");
808 NS_ENSURE_TRUE(!aEvent
->mFlags
.mIsBeingDispatched
,
809 NS_ERROR_DOM_INVALID_STATE_ERR
);
810 NS_ASSERTION(!aTargets
|| !aEvent
->mMessage
, "Wrong parameters!");
812 // If we're dispatching an already created DOMEvent object, make
813 // sure it is initialized!
814 // If aTargets is non-null, the event isn't going to be dispatched.
815 NS_ENSURE_TRUE(aEvent
->mMessage
|| !aDOMEvent
|| aTargets
,
816 NS_ERROR_DOM_INVALID_STATE_ERR
);
818 // Events shall not be fired while we are in stable state to prevent anything
819 // visible from the scripts.
820 MOZ_ASSERT(!nsContentUtils::IsInStableOrMetaStableState());
821 NS_ENSURE_TRUE(!nsContentUtils::IsInStableOrMetaStableState(),
822 NS_ERROR_DOM_INVALID_STATE_ERR
);
824 nsCOMPtr
<EventTarget
> target(aTarget
);
826 RefPtr
<PerformanceEventTiming
> eventTimingEntry
;
827 // Similar to PerformancePaintTiming, we don't need to
828 // expose them for printing documents
829 if (aPresContext
&& !aPresContext
->IsPrintingOrPrintPreview()) {
831 PerformanceEventTiming::TryGenerateEventTiming(target
, aEvent
);
833 if (aEvent
->IsTrusted() && aEvent
->mMessage
== eScroll
) {
834 if (auto* perf
= aPresContext
->GetPerformanceMainThread()) {
835 if (!perf
->HasDispatchedScrollEvent()) {
836 perf
->SetHasDispatchedScrollEvent();
842 bool retargeted
= false;
844 if (aEvent
->mFlags
.mRetargetToNonNativeAnonymous
) {
845 nsIContent
* content
= nsIContent::FromEventTargetOrNull(target
);
846 if (content
&& content
->IsInNativeAnonymousSubtree()) {
847 nsCOMPtr
<EventTarget
> newTarget
=
848 content
->FindFirstNonChromeOnlyAccessContent();
849 NS_ENSURE_STATE(newTarget
);
851 aEvent
->mOriginalTarget
= target
;
857 if (aEvent
->mFlags
.mOnlyChromeDispatch
) {
858 nsCOMPtr
<Document
> doc
;
859 if (!IsEventTargetChrome(target
, getter_AddRefs(doc
)) && doc
) {
860 nsPIDOMWindowInner
* win
= doc
->GetInnerWindow();
861 // If we can't dispatch the event to chrome, do nothing.
862 EventTarget
* piTarget
= win
? win
->GetParentTarget() : nullptr;
867 // Set the target to be the original dispatch target,
868 aEvent
->mTarget
= target
;
869 // but use chrome event handler or BrowserChildMessageManager for event
872 } else if (NS_WARN_IF(!doc
)) {
873 return NS_ERROR_UNEXPECTED
;
878 if (NS_IsMainThread() && aEvent
->mMessage
!= eVoidEvent
&&
879 !nsContentUtils::IsSafeToRunScript()) {
880 static const auto warn
= [](bool aIsSystem
) {
882 NS_WARNING("Fix the caller!");
884 MOZ_CRASH("This is unsafe! Fix the caller!");
887 if (nsINode
* node
= nsINode::FromEventTargetOrNull(target
)) {
888 // If this is a node, it's possible that this is some sort of DOM tree
889 // that is never accessed by script (for example an SVG image or XBL
890 // binding document or whatnot). We really only want to warn/assert here
891 // if there might be actual scripted listeners for this event, so restrict
892 // the warnings/asserts to the case when script can or once could touch
893 // this node's document.
894 Document
* doc
= node
->OwnerDoc();
895 bool hasHadScriptHandlingObject
;
896 nsIGlobalObject
* global
=
897 doc
->GetScriptHandlingObject(hasHadScriptHandlingObject
);
898 if (global
|| hasHadScriptHandlingObject
) {
899 warn(nsContentUtils::IsChromeDoc(doc
));
901 } else if (nsCOMPtr
<nsIGlobalObject
> global
= target
->GetOwnerGlobal()) {
902 warn(global
->PrincipalOrNull()->IsSystemPrincipal());
907 WidgetEvent
* innerEvent
= aDOMEvent
->WidgetEventPtr();
908 NS_ASSERTION(innerEvent
== aEvent
,
909 "The inner event of aDOMEvent is not the same as aEvent!");
914 bool externalDOMEvent
= !!(aDOMEvent
);
916 // If we have a PresContext, make sure it doesn't die before
917 // event dispatching is finished.
918 RefPtr
<nsPresContext
> kungFuDeathGrip(aPresContext
);
920 ELMCreationDetector cd
;
921 nsTArray
<EventTargetChainItem
> chain
;
922 if (cd
.IsMainThread()) {
923 if (!sCachedMainThreadChains
) {
924 sCachedMainThreadChains
= new CachedChains();
927 if (sCachedMainThreadChains
->mChain1
.Capacity() ==
928 kCachedMainThreadChainSize
) {
929 chain
= std::move(sCachedMainThreadChains
->mChain1
);
930 } else if (sCachedMainThreadChains
->mChain2
.Capacity() ==
931 kCachedMainThreadChainSize
) {
932 chain
= std::move(sCachedMainThreadChains
->mChain2
);
934 chain
.SetCapacity(kCachedMainThreadChainSize
);
938 // Create the event target chain item for the event target.
939 EventTargetChainItem
* targetEtci
= EventTargetChainItem::Create(
940 chain
, target
->GetTargetForEventTargetChain());
941 MOZ_ASSERT(&chain
[0] == targetEtci
);
942 if (!targetEtci
->IsValid()) {
943 EventTargetChainItem::DestroyLast(chain
, targetEtci
);
944 return NS_ERROR_FAILURE
;
947 // Make sure that Event::target and Event::originalTarget
948 // point to the last item in the chain.
949 if (!aEvent
->mTarget
) {
950 // Note, CurrentTarget() points always to the object returned by
951 // GetTargetForEventTargetChain().
952 aEvent
->mTarget
= targetEtci
->CurrentTarget();
954 // XXX But if the target is already set, use that. This is a hack
955 // for the 'load', 'beforeunload' and 'unload' events,
956 // which are dispatched to |window| but have document as their target.
958 // Make sure that the event target points to the right object.
959 aEvent
->mTarget
= aEvent
->mTarget
->GetTargetForEventTargetChain();
960 NS_ENSURE_STATE(aEvent
->mTarget
);
964 aEvent
->mOriginalTarget
=
965 aEvent
->mOriginalTarget
->GetTargetForEventTargetChain();
966 NS_ENSURE_STATE(aEvent
->mOriginalTarget
);
968 aEvent
->mOriginalTarget
= aEvent
->mTarget
;
971 aEvent
->mOriginalRelatedTarget
= aEvent
->mRelatedTarget
;
973 bool clearTargets
= false;
975 nsCOMPtr
<nsIContent
> content
=
976 nsIContent::FromEventTargetOrNull(aEvent
->mOriginalTarget
);
977 bool isInAnon
= content
&& content
->IsInNativeAnonymousSubtree();
979 aEvent
->mFlags
.mIsBeingDispatched
= true;
981 Maybe
<uint32_t> activationTargetItemIndex
;
983 // Create visitor object and start event dispatching.
984 // GetEventTargetParent for the original target.
985 nsEventStatus status
= aDOMEvent
&& aDOMEvent
->DefaultPrevented()
986 ? nsEventStatus_eConsumeNoDefault
987 : aEventStatus
? *aEventStatus
988 : nsEventStatus_eIgnore
;
989 nsCOMPtr
<EventTarget
> targetForPreVisitor
= aEvent
->mTarget
;
990 EventChainPreVisitor
preVisitor(aPresContext
, aEvent
, aDOMEvent
, status
,
991 isInAnon
, targetForPreVisitor
);
992 targetEtci
->GetEventTargetParent(preVisitor
);
994 if (preVisitor
.mWantsActivationBehavior
) {
995 MOZ_ASSERT(&chain
[0] == targetEtci
);
996 activationTargetItemIndex
.emplace(0);
999 if (!preVisitor
.mCanHandle
) {
1000 targetEtci
= MayRetargetToChromeIfCanNotHandleEvent(
1001 chain
, preVisitor
, targetEtci
, nullptr, content
);
1003 if (!preVisitor
.mCanHandle
) {
1004 // The original target and chrome target (mAutomaticChromeDispatch=true)
1005 // can not handle the event but we still have to call their PreHandleEvent.
1006 for (uint32_t i
= 0; i
< chain
.Length(); ++i
) {
1007 chain
[i
].PreHandleEvent(preVisitor
);
1010 clearTargets
= ShouldClearTargets(aEvent
);
1012 // At least the original target can handle the event.
1013 // Setting the retarget to the |target| simplifies retargeting code.
1014 nsCOMPtr
<EventTarget
> t
= aEvent
->mTarget
;
1015 targetEtci
->SetNewTarget(t
);
1016 // In order to not change the targetTouches array passed to TouchEvents
1017 // when dispatching events from JS, we need to store the initial Touch
1018 // objects on the list.
1019 if (aEvent
->mClass
== eTouchEventClass
&& aDOMEvent
) {
1020 TouchEvent
* touchEvent
= static_cast<TouchEvent
*>(aDOMEvent
);
1021 TouchList
* targetTouches
= touchEvent
->GetExistingTargetTouches();
1022 if (targetTouches
) {
1023 Maybe
<nsTArray
<RefPtr
<dom::Touch
>>> initialTargetTouches
;
1024 initialTargetTouches
.emplace();
1025 for (uint32_t i
= 0; i
< targetTouches
->Length(); ++i
) {
1026 initialTargetTouches
->AppendElement(targetTouches
->Item(i
));
1028 targetEtci
->SetInitialTargetTouches(std::move(initialTargetTouches
));
1029 targetTouches
->Clear();
1032 EventTargetChainItem
* topEtci
= targetEtci
;
1033 targetEtci
= nullptr;
1034 while (preVisitor
.GetParentTarget()) {
1035 EventTarget
* parentTarget
= preVisitor
.GetParentTarget();
1036 EventTargetChainItem
* parentEtci
=
1037 EventTargetChainItem::Create(chain
, parentTarget
, topEtci
);
1038 if (!parentEtci
->IsValid()) {
1039 EventTargetChainItem::DestroyLast(chain
, parentEtci
);
1040 rv
= NS_ERROR_FAILURE
;
1044 parentEtci
->SetIsSlotInClosedTree(preVisitor
.mParentIsSlotInClosedTree
);
1045 parentEtci
->SetIsChromeHandler(preVisitor
.mParentIsChromeHandler
);
1047 // Item needs event retargetting.
1048 if (preVisitor
.mEventTargetAtParent
) {
1049 // Need to set the target of the event
1050 // so that also the next retargeting works.
1051 preVisitor
.mTargetInKnownToBeHandledScope
= preVisitor
.mEvent
->mTarget
;
1052 preVisitor
.mEvent
->mTarget
= preVisitor
.mEventTargetAtParent
;
1053 parentEtci
->SetNewTarget(preVisitor
.mEventTargetAtParent
);
1056 if (preVisitor
.mRetargetedRelatedTarget
) {
1057 preVisitor
.mEvent
->mRelatedTarget
= preVisitor
.mRetargetedRelatedTarget
;
1060 parentEtci
->GetEventTargetParent(preVisitor
);
1062 if (preVisitor
.mWantsActivationBehavior
&&
1063 activationTargetItemIndex
.isNothing() && aEvent
->mFlags
.mBubbles
) {
1064 MOZ_ASSERT(&chain
.LastElement() == parentEtci
);
1065 activationTargetItemIndex
.emplace(chain
.Length() - 1);
1068 if (preVisitor
.mCanHandle
) {
1069 preVisitor
.mTargetInKnownToBeHandledScope
= preVisitor
.mEvent
->mTarget
;
1070 topEtci
= parentEtci
;
1072 bool ignoreBecauseOfShadowDOM
= preVisitor
.mIgnoreBecauseOfShadowDOM
;
1073 nsCOMPtr
<nsINode
> disabledTarget
=
1074 nsINode::FromEventTargetOrNull(parentTarget
);
1075 parentEtci
= MayRetargetToChromeIfCanNotHandleEvent(
1076 chain
, preVisitor
, parentEtci
, topEtci
, disabledTarget
);
1077 if (parentEtci
&& preVisitor
.mCanHandle
) {
1078 preVisitor
.mTargetInKnownToBeHandledScope
=
1079 preVisitor
.mEvent
->mTarget
;
1080 EventTargetChainItem
* item
=
1081 EventTargetChainItem::GetFirstCanHandleEventTarget(chain
);
1082 if (!ignoreBecauseOfShadowDOM
) {
1083 // If we ignored the target because of Shadow DOM retargeting, we
1084 // shouldn't treat the target to be in the event path at all.
1085 item
->SetNewTarget(parentTarget
);
1087 topEtci
= parentEtci
;
1094 if (activationTargetItemIndex
) {
1095 chain
[activationTargetItemIndex
.value()].LegacyPreActivationBehavior(
1099 if (NS_SUCCEEDED(rv
)) {
1102 uint32_t numTargets
= chain
.Length();
1103 EventTarget
** targets
= aTargets
->AppendElements(numTargets
);
1104 for (uint32_t i
= 0; i
< numTargets
; ++i
) {
1105 targets
[i
] = chain
[i
].CurrentTarget()->GetTargetForDOMEvent();
1108 // Event target chain is created. PreHandle the chain.
1109 for (uint32_t i
= 0; i
< chain
.Length(); ++i
) {
1110 chain
[i
].PreHandleEvent(preVisitor
);
1113 RefPtr
<nsRefreshDriver
> refreshDriver
;
1114 if (aEvent
->IsTrusted() &&
1115 (aEvent
->mMessage
== eKeyPress
||
1116 aEvent
->mMessage
== eMouseClick
) &&
1117 aPresContext
&& aPresContext
->GetRootPresContext()) {
1118 refreshDriver
= aPresContext
->GetRootPresContext()->RefreshDriver();
1119 if (refreshDriver
) {
1120 refreshDriver
->EnterUserInputProcessing();
1123 auto cleanup
= MakeScopeExit([&] {
1124 if (refreshDriver
) {
1125 refreshDriver
->ExitUserInputProcessing();
1129 clearTargets
= ShouldClearTargets(aEvent
);
1131 // Handle the chain.
1132 EventChainPostVisitor
postVisitor(preVisitor
);
1133 MOZ_RELEASE_ASSERT(!aEvent
->mPath
);
1134 aEvent
->mPath
= &chain
;
1136 if (profiler_is_active()) {
1137 // Add a profiler label and a profiler marker for the actual
1138 // dispatch of the event.
1139 // This is a very hot code path, so we need to make sure not to
1140 // do this extra work when we're not profiling.
1141 if (!postVisitor
.mDOMEvent
) {
1142 // This is tiny bit slow, but happens only once per event.
1143 // Similar code also in EventListenerManager.
1144 nsCOMPtr
<EventTarget
> et
= aEvent
->mOriginalTarget
;
1145 RefPtr
<Event
> event
=
1146 EventDispatcher::CreateEvent(et
, 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 nsCOMPtr
<nsIDocShell
> docShell
;
1155 docShell
= nsContentUtils::GetDocShellForEventTarget(aEvent
->mTarget
);
1156 MarkerInnerWindowId innerWindowId
;
1157 if (nsCOMPtr
<nsPIDOMWindowInner
> inner
=
1158 do_QueryInterface(aEvent
->mTarget
->GetOwnerGlobal())) {
1159 innerWindowId
= MarkerInnerWindowId
{inner
->WindowID()};
1162 struct DOMEventMarker
{
1163 static constexpr Span
<const char> MarkerTypeName() {
1164 return MakeStringSpan("DOMEvent");
1166 static void StreamJSONMarkerData(
1167 baseprofiler::SpliceableJSONWriter
& aWriter
,
1168 const ProfilerString16View
& aEventType
,
1169 const nsCString
& aTarget
, const TimeStamp
& aStartTime
,
1170 const TimeStamp
& aEventTimeStamp
) {
1171 aWriter
.StringProperty("eventType",
1172 NS_ConvertUTF16toUTF8(aEventType
));
1173 if (!aTarget
.IsEmpty()) {
1174 aWriter
.StringProperty("target", aTarget
);
1176 // This is the event processing latency, which is the time from
1177 // when the event was created, to when it was started to be
1178 // processed. Note that the computation of this latency is
1179 // deferred until serialization time, at the expense of some extra
1181 aWriter
.DoubleProperty(
1182 "latency", (aStartTime
- aEventTimeStamp
).ToMilliseconds());
1184 static MarkerSchema
MarkerTypeDisplay() {
1185 using MS
= MarkerSchema
;
1186 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
,
1187 MS::Location::TimelineOverview
};
1188 schema
.SetChartLabel("{marker.data.eventType}");
1189 schema
.SetTooltipLabel("{marker.data.eventType} - DOMEvent");
1190 schema
.SetTableLabel(
1191 "{marker.data.eventType} - {marker.data.target}");
1192 schema
.AddKeyLabelFormatSearchable("target", "Event Target",
1194 MS::Searchable::Searchable
);
1195 schema
.AddKeyLabelFormat("latency", "Latency",
1196 MS::Format::Duration
);
1197 schema
.AddKeyLabelFormatSearchable("eventType", "Event Type",
1199 MS::Searchable::Searchable
);
1204 nsAutoCString target
;
1205 DescribeEventTargetForProfilerMarker(aEvent
->mTarget
, target
);
1207 auto startTime
= TimeStamp::Now();
1208 profiler_add_marker("DOMEvent", geckoprofiler::category::DOM
,
1209 {MarkerTiming::IntervalStart(),
1210 MarkerInnerWindowId(innerWindowId
)},
1211 DOMEventMarker
{}, typeStr
, target
, startTime
,
1212 aEvent
->mTimeStamp
);
1214 EventTargetChainItem::HandleEventTargetChain(chain
, postVisitor
,
1217 profiler_add_marker(
1218 "DOMEvent", geckoprofiler::category::DOM
,
1219 {MarkerTiming::IntervalEnd(), std::move(innerWindowId
)},
1220 DOMEventMarker
{}, typeStr
, target
, startTime
, aEvent
->mTimeStamp
);
1222 EventTargetChainItem::HandleEventTargetChain(chain
, postVisitor
,
1225 aEvent
->mPath
= nullptr;
1227 if (aEvent
->IsTrusted() &&
1228 (aEvent
->mMessage
== eKeyPress
||
1229 aEvent
->mMessage
== eMouseClick
) &&
1230 aPresContext
&& aPresContext
->GetRootPresContext()) {
1231 nsRefreshDriver
* driver
=
1232 aPresContext
->GetRootPresContext()->RefreshDriver();
1233 if (driver
&& driver
->HasPendingTick()) {
1234 switch (aEvent
->mMessage
) {
1236 driver
->RegisterCompositionPayload(
1237 {layers::CompositionPayloadType::eKeyPress
,
1238 aEvent
->mTimeStamp
});
1241 if (aEvent
->AsMouseEvent()->mInputSource
==
1242 MouseEvent_Binding::MOZ_SOURCE_MOUSE
||
1243 aEvent
->AsMouseEvent()->mInputSource
==
1244 MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
1245 driver
->RegisterCompositionPayload(
1246 {layers::CompositionPayloadType::eMouseUpFollowedByClick
,
1247 aEvent
->mTimeStamp
});
1257 preVisitor
.mEventStatus
= postVisitor
.mEventStatus
;
1258 // If the DOM event was created during event flow.
1259 if (!preVisitor
.mDOMEvent
&& postVisitor
.mDOMEvent
) {
1260 preVisitor
.mDOMEvent
= postVisitor
.mDOMEvent
;
1266 // Note, EventTargetChainItem objects are deleted when the chain goes out of
1269 aEvent
->mFlags
.mIsBeingDispatched
= false;
1270 aEvent
->mFlags
.mDispatchedAtLeastOnce
= true;
1272 if (eventTimingEntry
) {
1273 eventTimingEntry
->FinalizeEventTiming(aEvent
->mTarget
);
1275 // https://dom.spec.whatwg.org/#concept-event-dispatch
1276 // step 10. If clearTargets, then:
1277 // 1. Set event's target to null.
1278 // 2. Set event's relatedTarget to null.
1279 // 3. Set event's touch target list to the empty list.
1281 aEvent
->mTarget
= nullptr;
1282 aEvent
->mOriginalTarget
= nullptr;
1283 aEvent
->mRelatedTarget
= nullptr;
1284 aEvent
->mOriginalRelatedTarget
= nullptr;
1285 // XXXsmaug Check also all the touch objects.
1288 if (activationTargetItemIndex
) {
1289 EventChainPostVisitor
postVisitor(preVisitor
);
1290 if (preVisitor
.mEventStatus
== nsEventStatus_eConsumeNoDefault
) {
1291 chain
[activationTargetItemIndex
.value()].LegacyCanceledActivationBehavior(
1294 chain
[activationTargetItemIndex
.value()].ActivationBehavior(postVisitor
);
1296 preVisitor
.mEventStatus
= postVisitor
.mEventStatus
;
1297 // If the DOM event was created during event flow.
1298 if (!preVisitor
.mDOMEvent
&& postVisitor
.mDOMEvent
) {
1299 preVisitor
.mDOMEvent
= postVisitor
.mDOMEvent
;
1303 if (!externalDOMEvent
&& preVisitor
.mDOMEvent
) {
1304 // A dom::Event was created while dispatching the event.
1305 // Duplicate private data if someone holds a pointer to it.
1307 NS_RELEASE2(preVisitor
.mDOMEvent
, rc
);
1308 if (preVisitor
.mDOMEvent
) {
1309 preVisitor
.mDOMEvent
->DuplicatePrivateData();
1314 *aEventStatus
= preVisitor
.mEventStatus
;
1317 if (cd
.IsMainThread() && chain
.Capacity() == kCachedMainThreadChainSize
&&
1318 sCachedMainThreadChains
) {
1319 if (sCachedMainThreadChains
->mChain1
.Capacity() !=
1320 kCachedMainThreadChainSize
) {
1321 chain
.ClearAndRetainStorage();
1322 chain
.SwapElements(sCachedMainThreadChains
->mChain1
);
1323 } else if (sCachedMainThreadChains
->mChain2
.Capacity() !=
1324 kCachedMainThreadChainSize
) {
1325 chain
.ClearAndRetainStorage();
1326 chain
.SwapElements(sCachedMainThreadChains
->mChain2
);
1334 nsresult
EventDispatcher::DispatchDOMEvent(EventTarget
* aTarget
,
1335 WidgetEvent
* aEvent
,
1337 nsPresContext
* aPresContext
,
1338 nsEventStatus
* aEventStatus
) {
1340 WidgetEvent
* innerEvent
= aDOMEvent
->WidgetEventPtr();
1341 NS_ENSURE_TRUE(innerEvent
, NS_ERROR_ILLEGAL_VALUE
);
1343 // Don't modify the event if it's being dispatched right now.
1344 if (innerEvent
->mFlags
.mIsBeingDispatched
) {
1345 return NS_ERROR_DOM_INVALID_STATE_ERR
;
1348 bool dontResetTrusted
= false;
1349 if (innerEvent
->mFlags
.mDispatchedAtLeastOnce
) {
1350 innerEvent
->mTarget
= nullptr;
1351 innerEvent
->mOriginalTarget
= nullptr;
1353 dontResetTrusted
= aDOMEvent
->IsTrusted();
1356 if (!dontResetTrusted
) {
1357 // Check security state to determine if dispatcher is trusted
1358 bool trusted
= NS_IsMainThread()
1359 ? nsContentUtils::LegacyIsCallerChromeOrNativeCode()
1360 : IsCurrentThreadRunningChromeWorker();
1361 aDOMEvent
->SetTrusted(trusted
);
1364 return EventDispatcher::Dispatch(aTarget
, aPresContext
, innerEvent
,
1365 aDOMEvent
, aEventStatus
);
1366 } else if (aEvent
) {
1367 return EventDispatcher::Dispatch(aTarget
, aPresContext
, aEvent
, aDOMEvent
,
1370 return NS_ERROR_ILLEGAL_VALUE
;
1373 /* static */ already_AddRefed
<dom::Event
> EventDispatcher::CreateEvent(
1374 EventTarget
* aOwner
, nsPresContext
* aPresContext
, WidgetEvent
* aEvent
,
1375 const nsAString
& aEventType
, CallerType aCallerType
) {
1377 switch (aEvent
->mClass
) {
1378 case eMutationEventClass
:
1379 return NS_NewDOMMutationEvent(aOwner
, aPresContext
,
1380 aEvent
->AsMutationEvent());
1381 case eGUIEventClass
:
1382 case eScrollPortEventClass
:
1384 return NS_NewDOMUIEvent(aOwner
, aPresContext
, aEvent
->AsGUIEvent());
1385 case eScrollAreaEventClass
:
1386 return NS_NewDOMScrollAreaEvent(aOwner
, aPresContext
,
1387 aEvent
->AsScrollAreaEvent());
1388 case eKeyboardEventClass
:
1389 return NS_NewDOMKeyboardEvent(aOwner
, aPresContext
,
1390 aEvent
->AsKeyboardEvent());
1391 case eCompositionEventClass
:
1392 return NS_NewDOMCompositionEvent(aOwner
, aPresContext
,
1393 aEvent
->AsCompositionEvent());
1394 case eMouseEventClass
:
1395 return NS_NewDOMMouseEvent(aOwner
, aPresContext
,
1396 aEvent
->AsMouseEvent());
1397 case eFocusEventClass
:
1398 return NS_NewDOMFocusEvent(aOwner
, aPresContext
,
1399 aEvent
->AsFocusEvent());
1400 case eMouseScrollEventClass
:
1401 return NS_NewDOMMouseScrollEvent(aOwner
, aPresContext
,
1402 aEvent
->AsMouseScrollEvent());
1403 case eWheelEventClass
:
1404 return NS_NewDOMWheelEvent(aOwner
, aPresContext
,
1405 aEvent
->AsWheelEvent());
1406 case eEditorInputEventClass
:
1407 return NS_NewDOMInputEvent(aOwner
, aPresContext
,
1408 aEvent
->AsEditorInputEvent());
1409 case eDragEventClass
:
1410 return NS_NewDOMDragEvent(aOwner
, aPresContext
, aEvent
->AsDragEvent());
1411 case eClipboardEventClass
:
1412 return NS_NewDOMClipboardEvent(aOwner
, aPresContext
,
1413 aEvent
->AsClipboardEvent());
1414 case eSMILTimeEventClass
:
1415 return NS_NewDOMTimeEvent(aOwner
, aPresContext
,
1416 aEvent
->AsSMILTimeEvent());
1417 case eCommandEventClass
:
1418 return NS_NewDOMCommandEvent(aOwner
, aPresContext
,
1419 aEvent
->AsCommandEvent());
1420 case eSimpleGestureEventClass
:
1421 return NS_NewDOMSimpleGestureEvent(aOwner
, aPresContext
,
1422 aEvent
->AsSimpleGestureEvent());
1423 case ePointerEventClass
:
1424 return NS_NewDOMPointerEvent(aOwner
, aPresContext
,
1425 aEvent
->AsPointerEvent());
1426 case eTouchEventClass
:
1427 return NS_NewDOMTouchEvent(aOwner
, aPresContext
,
1428 aEvent
->AsTouchEvent());
1429 case eTransitionEventClass
:
1430 return NS_NewDOMTransitionEvent(aOwner
, aPresContext
,
1431 aEvent
->AsTransitionEvent());
1432 case eAnimationEventClass
:
1433 return NS_NewDOMAnimationEvent(aOwner
, aPresContext
,
1434 aEvent
->AsAnimationEvent());
1436 // For all other types of events, create a vanilla event object.
1437 return NS_NewDOMEvent(aOwner
, aPresContext
, aEvent
);
1441 // And if we didn't get an event, check the type argument.
1443 if (aEventType
.LowerCaseEqualsLiteral("mouseevent") ||
1444 aEventType
.LowerCaseEqualsLiteral("mouseevents")) {
1445 return NS_NewDOMMouseEvent(aOwner
, aPresContext
, nullptr);
1447 if (aEventType
.LowerCaseEqualsLiteral("dragevent")) {
1448 return NS_NewDOMDragEvent(aOwner
, aPresContext
, nullptr);
1450 if (aEventType
.LowerCaseEqualsLiteral("keyboardevent")) {
1451 return NS_NewDOMKeyboardEvent(aOwner
, aPresContext
, nullptr);
1453 if (aEventType
.LowerCaseEqualsLiteral("compositionevent") ||
1454 aEventType
.LowerCaseEqualsLiteral("textevent")) {
1455 return NS_NewDOMCompositionEvent(aOwner
, aPresContext
, nullptr);
1457 if (aEventType
.LowerCaseEqualsLiteral("mutationevent") ||
1458 aEventType
.LowerCaseEqualsLiteral("mutationevents")) {
1459 return NS_NewDOMMutationEvent(aOwner
, aPresContext
, nullptr);
1461 if (aEventType
.LowerCaseEqualsLiteral("deviceorientationevent")) {
1462 DeviceOrientationEventInit init
;
1463 RefPtr
<Event
> event
=
1464 DeviceOrientationEvent::Constructor(aOwner
, u
""_ns
, init
);
1465 event
->MarkUninitialized();
1466 return event
.forget();
1468 if (aEventType
.LowerCaseEqualsLiteral("devicemotionevent")) {
1469 return NS_NewDOMDeviceMotionEvent(aOwner
, aPresContext
, nullptr);
1471 if (aEventType
.LowerCaseEqualsLiteral("uievent") ||
1472 aEventType
.LowerCaseEqualsLiteral("uievents")) {
1473 return NS_NewDOMUIEvent(aOwner
, aPresContext
, nullptr);
1475 if (aEventType
.LowerCaseEqualsLiteral("event") ||
1476 aEventType
.LowerCaseEqualsLiteral("events") ||
1477 aEventType
.LowerCaseEqualsLiteral("htmlevents") ||
1478 aEventType
.LowerCaseEqualsLiteral("svgevents")) {
1479 return NS_NewDOMEvent(aOwner
, aPresContext
, nullptr);
1481 if (aEventType
.LowerCaseEqualsLiteral("messageevent")) {
1482 RefPtr
<Event
> event
= new MessageEvent(aOwner
, aPresContext
, nullptr);
1483 return event
.forget();
1485 if (aEventType
.LowerCaseEqualsLiteral("beforeunloadevent")) {
1486 return NS_NewDOMBeforeUnloadEvent(aOwner
, aPresContext
, nullptr);
1488 if (aEventType
.LowerCaseEqualsLiteral("touchevent") &&
1489 TouchEvent::LegacyAPIEnabled(
1490 nsContentUtils::GetDocShellForEventTarget(aOwner
),
1491 aCallerType
== CallerType::System
)) {
1492 return NS_NewDOMTouchEvent(aOwner
, aPresContext
, nullptr);
1494 if (aEventType
.LowerCaseEqualsLiteral("hashchangeevent")) {
1495 HashChangeEventInit init
;
1496 RefPtr
<Event
> event
= HashChangeEvent::Constructor(aOwner
, u
""_ns
, init
);
1497 event
->MarkUninitialized();
1498 return event
.forget();
1500 if (aEventType
.LowerCaseEqualsLiteral("customevent")) {
1501 return NS_NewDOMCustomEvent(aOwner
, aPresContext
, nullptr);
1503 if (aEventType
.LowerCaseEqualsLiteral("storageevent")) {
1504 RefPtr
<Event
> event
=
1505 StorageEvent::Constructor(aOwner
, u
""_ns
, StorageEventInit());
1506 event
->MarkUninitialized();
1507 return event
.forget();
1509 if (aEventType
.LowerCaseEqualsLiteral("focusevent")) {
1510 RefPtr
<Event
> event
= NS_NewDOMFocusEvent(aOwner
, aPresContext
, nullptr);
1511 event
->MarkUninitialized();
1512 return event
.forget();
1515 // Only allow these events for chrome
1516 if (aCallerType
== CallerType::System
) {
1517 if (aEventType
.LowerCaseEqualsLiteral("simplegestureevent")) {
1518 return NS_NewDOMSimpleGestureEvent(aOwner
, aPresContext
, nullptr);
1520 if (aEventType
.LowerCaseEqualsLiteral("xulcommandevent") ||
1521 aEventType
.LowerCaseEqualsLiteral("xulcommandevents")) {
1522 return NS_NewDOMXULCommandEvent(aOwner
, aPresContext
, nullptr);
1526 // NEW EVENT TYPES SHOULD NOT BE ADDED HERE; THEY SHOULD USE ONLY EVENT
1532 struct CurrentTargetPathInfo
{
1534 int32_t mHiddenSubtreeLevel
;
1537 static CurrentTargetPathInfo
TargetPathInfo(
1538 const nsTArray
<EventTargetChainItem
>& aEventPath
,
1539 const EventTarget
& aCurrentTarget
) {
1540 int32_t currentTargetHiddenSubtreeLevel
= 0;
1541 for (uint32_t index
= aEventPath
.Length(); index
--;) {
1542 const EventTargetChainItem
& item
= aEventPath
.ElementAt(index
);
1543 if (item
.PreHandleEventOnly()) {
1547 if (item
.IsRootOfClosedTree()) {
1548 currentTargetHiddenSubtreeLevel
++;
1551 if (item
.CurrentTarget() == &aCurrentTarget
) {
1552 return {index
, currentTargetHiddenSubtreeLevel
};
1555 if (item
.IsSlotInClosedTree()) {
1556 currentTargetHiddenSubtreeLevel
--;
1559 MOZ_ASSERT_UNREACHABLE("No target found?");
1563 // https://dom.spec.whatwg.org/#dom-event-composedpath
1564 void EventDispatcher::GetComposedPathFor(WidgetEvent
* aEvent
,
1565 nsTArray
<RefPtr
<EventTarget
>>& aPath
) {
1566 MOZ_ASSERT(aPath
.IsEmpty());
1567 nsTArray
<EventTargetChainItem
>* path
= aEvent
->mPath
;
1568 if (!path
|| path
->IsEmpty() || !aEvent
->mCurrentTarget
) {
1572 EventTarget
* currentTarget
=
1573 aEvent
->mCurrentTarget
->GetTargetForEventTargetChain();
1574 if (!currentTarget
) {
1578 CurrentTargetPathInfo currentTargetInfo
=
1579 TargetPathInfo(*path
, *currentTarget
);
1582 int32_t maxHiddenLevel
= currentTargetInfo
.mHiddenSubtreeLevel
;
1583 int32_t currentHiddenLevel
= currentTargetInfo
.mHiddenSubtreeLevel
;
1584 for (uint32_t index
= currentTargetInfo
.mIndex
; index
--;) {
1585 EventTargetChainItem
& item
= path
->ElementAt(index
);
1586 if (item
.PreHandleEventOnly()) {
1590 if (item
.IsRootOfClosedTree()) {
1591 currentHiddenLevel
++;
1594 if (currentHiddenLevel
<= maxHiddenLevel
) {
1595 aPath
.AppendElement(item
.CurrentTarget()->GetTargetForDOMEvent());
1598 if (item
.IsChromeHandler()) {
1602 if (item
.IsSlotInClosedTree()) {
1603 currentHiddenLevel
--;
1604 maxHiddenLevel
= std::min(maxHiddenLevel
, currentHiddenLevel
);
1611 aPath
.AppendElement(currentTarget
->GetTargetForDOMEvent());
1614 int32_t maxHiddenLevel
= currentTargetInfo
.mHiddenSubtreeLevel
;
1615 int32_t currentHiddenLevel
= currentTargetInfo
.mHiddenSubtreeLevel
;
1616 for (uint32_t index
= currentTargetInfo
.mIndex
+ 1; index
< path
->Length();
1618 EventTargetChainItem
& item
= path
->ElementAt(index
);
1619 if (item
.PreHandleEventOnly()) {
1623 if (item
.IsSlotInClosedTree()) {
1624 currentHiddenLevel
++;
1627 if (item
.IsChromeHandler()) {
1631 if (currentHiddenLevel
<= maxHiddenLevel
) {
1632 aPath
.AppendElement(item
.CurrentTarget()->GetTargetForDOMEvent());
1635 if (item
.IsRootOfClosedTree()) {
1636 currentHiddenLevel
--;
1637 maxHiddenLevel
= std::min(maxHiddenLevel
, currentHiddenLevel
);
1643 void EventChainPreVisitor::IgnoreCurrentTargetBecauseOfShadowDOMRetargeting() {
1645 mIgnoreBecauseOfShadowDOM
= true;
1647 EventTarget
* target
= nullptr;
1649 auto getWindow
= [this]() -> nsPIDOMWindowOuter
* {
1650 nsINode
* node
= nsINode::FromEventTargetOrNull(this->mParentTarget
);
1654 Document
* doc
= node
->GetComposedDoc();
1659 return doc
->GetWindow();
1662 // The HTMLEditor is registered to nsWindowRoot, so we
1663 // want to dispatch events to it.
1664 if (nsCOMPtr
<nsPIDOMWindowOuter
> win
= getWindow()) {
1665 target
= win
->GetParentTarget();
1667 SetParentTarget(target
, false);
1669 mEventTargetAtParent
= nullptr;
1672 } // namespace mozilla