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