Bug 1866894 - Update failing subtest for content-visibility-auto-resize.html. r=fredw
[gecko.git] / dom / events / EventDispatcher.cpp
blob03dc3e9dc140d2dd951f4fc2aa671cb0ae59b81d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Assertions.h"
8 #include "nsPresContext.h"
9 #include "nsContentUtils.h"
10 #include "nsDocShell.h"
11 #include "nsError.h"
12 #include <new>
13 #include "nsIContent.h"
14 #include "nsIContentInlines.h"
15 #include "mozilla/dom/Document.h"
16 #include "nsINode.h"
17 #include "nsIScriptObjectPrincipal.h"
18 #include "nsPIDOMWindow.h"
19 #include "nsRefreshDriver.h"
20 #include "AnimationEvent.h"
21 #include "BeforeUnloadEvent.h"
22 #include "ClipboardEvent.h"
23 #include "CommandEvent.h"
24 #include "CompositionEvent.h"
25 #include "DeviceMotionEvent.h"
26 #include "DragEvent.h"
27 #include "KeyboardEvent.h"
28 #include "mozilla/BasePrincipal.h"
29 #include "mozilla/ContentEvents.h"
30 #include "mozilla/dom/CloseEvent.h"
31 #include "mozilla/dom/CustomEvent.h"
32 #include "mozilla/dom/DeviceOrientationEvent.h"
33 #include "mozilla/dom/EventTarget.h"
34 #include "mozilla/dom/FocusEvent.h"
35 #include "mozilla/dom/HashChangeEvent.h"
36 #include "mozilla/dom/InputEvent.h"
37 #include "mozilla/dom/MessageEvent.h"
38 #include "mozilla/dom/MouseScrollEvent.h"
39 #include "mozilla/dom/MutationEvent.h"
40 #include "mozilla/dom/NotifyPaintEvent.h"
41 #include "mozilla/dom/PageTransitionEvent.h"
42 #include "mozilla/dom/PerformanceEventTiming.h"
43 #include "mozilla/dom/PerformanceMainThread.h"
44 #include "mozilla/dom/PointerEvent.h"
45 #include "mozilla/dom/RootedDictionary.h"
46 #include "mozilla/dom/ScrollAreaEvent.h"
47 #include "mozilla/dom/SimpleGestureEvent.h"
48 #include "mozilla/dom/ScriptSettings.h"
49 #include "mozilla/dom/StorageEvent.h"
50 #include "mozilla/dom/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"
70 namespace mozilla {
72 using namespace dom;
74 class ELMCreationDetector {
75 public:
76 ELMCreationDetector()
77 // We can do this optimization only in the main thread.
78 : mNonMainThread(!NS_IsMainThread()),
79 mInitialCount(mNonMainThread
80 ? 0
81 : EventListenerManager::sMainThreadCreatedCount) {}
83 bool MayHaveNewListenerManager() {
84 return mNonMainThread ||
85 mInitialCount != EventListenerManager::sMainThreadCreatedCount;
88 bool IsMainThread() { return !mNonMainThread; }
90 private:
91 bool mNonMainThread;
92 uint32_t mInitialCount;
95 static bool IsEventTargetChrome(EventTarget* aEventTarget,
96 Document** aDocument = nullptr) {
97 if (aDocument) {
98 *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;
111 if (doc) {
112 isChrome = nsContentUtils::IsChromeDoc(doc);
113 if (aDocument) {
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();
121 return isChrome;
124 // EventTargetChainItem represents a single item in the event target chain.
125 class EventTargetChainItem {
126 public:
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);
141 return etci;
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()) {
162 return i;
165 MOZ_ASSERT(false);
166 return 0;
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()) {
174 return &aChain[i];
177 return nullptr;
180 bool IsValid() const {
181 NS_WARNING_ASSERTION(!!(mTarget), "Event target is not valid!");
182 return !!(mTarget);
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);
219 if (aDOMEvent) {
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();
224 if (targetTouches) {
225 targetTouches->Clear();
226 if (mInitialTargetTouches.isSome()) {
227 for (uint32_t i = 0; i < mInitialTargetTouches->Length(); ++i) {
228 Touch* touch = mInitialTargetTouches->ElementAt(i);
229 if (touch) {
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
296 * item in the chain.
298 MOZ_CAN_RUN_SCRIPT
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.
320 MOZ_CAN_RUN_SCRIPT
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()) {
344 return;
346 if (aVisitor.mEvent->mFlags.mOnlySystemGroupDispatch &&
347 !aVisitor.mEvent->mFlags.mInSystemGroup) {
348 return;
350 if (aVisitor.mEvent->mFlags.mOnlySystemGroupDispatchInContent &&
351 !aVisitor.mEvent->mFlags.mInSystemGroup && !IsCurrentTargetChrome()) {
352 return;
354 if (!mManager) {
355 if (!MayHaveListenerManager() && !aCd.MayHaveNewListenerManager()) {
356 return;
358 mManager = mTarget->GetExistingListenerManager();
360 if (mManager) {
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);
377 private:
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 {
384 public:
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;
401 private:
402 using RawFlags = uint32_t;
403 void SetRawFlags(RawFlags aRawFlags) {
404 static_assert(
405 sizeof(EventTargetChainFlags) <= sizeof(RawFlags),
406 "EventTargetChainFlags must not be bigger than the RawFlags");
407 memcpy(this, &aRawFlags, sizeof(EventTargetChainFlags));
409 } mFlags;
411 uint16_t mItemFlags;
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) {
431 aVisitor.Reset();
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()) {
457 return;
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);
516 // Capture
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()) {
523 continue;
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();
536 if (newTarget) {
537 aVisitor.mEvent->mTarget = newTarget;
538 break;
543 // https://dom.spec.whatwg.org/#dispatching-events
544 // Step 14.2
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()) {
550 bool found = false;
551 for (uint32_t j = i; j > 0; --j) {
552 uint32_t childIndex = j - 1;
553 EventTarget* relatedTarget =
554 chain[childIndex].GetRetargetedRelatedTarget();
555 if (relatedTarget) {
556 found = true;
557 aVisitor.mEvent->mRelatedTarget = relatedTarget;
558 break;
561 if (!found) {
562 aVisitor.mEvent->mRelatedTarget =
563 aVisitor.mEvent->mOriginalRelatedTarget;
567 if (item.HasRetargetTouchTargets()) {
568 bool found = false;
569 for (uint32_t j = i; j > 0; --j) {
570 uint32_t childIndex = j - 1;
571 if (chain[childIndex].HasRetargetTouchTargets()) {
572 found = true;
573 chain[childIndex].RetargetTouchTargets(touchEvent,
574 aVisitor.mDOMEvent);
575 break;
578 if (!found) {
579 WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
580 for (uint32_t i = 0; i < touches.Length(); ++i) {
581 touches[i]->mTarget = touches[i]->mOriginalTarget;
587 // Target
588 const bool prefCorrectOrder =
589 StaticPrefs::dom_events_phases_correctOrderOnTarget();
590 aVisitor.mEvent->mFlags.mInTargetPhase = true;
591 if (!prefCorrectOrder) {
592 aVisitor.mEvent->mFlags.mInBubblingPhase = true;
594 EventTargetChainItem& targetItem = chain[firstCanHandleEventTargetIdx];
595 // Need to explicitly retarget touch targets so that initial targets get set
596 // properly in case nothing else retargeted touches.
597 if (targetItem.HasRetargetTouchTargets()) {
598 targetItem.RetargetTouchTargets(touchEvent, aVisitor.mDOMEvent);
600 if (!aVisitor.mEvent->PropagationStopped() &&
601 (!aVisitor.mEvent->mFlags.mNoContentDispatch ||
602 targetItem.ForceContentDispatch())) {
603 targetItem.HandleEvent(aVisitor, aCd);
605 if (prefCorrectOrder) {
606 aVisitor.mEvent->mFlags.mInCapturePhase = false;
607 aVisitor.mEvent->mFlags.mInBubblingPhase = true;
608 if (!aVisitor.mEvent->PropagationStopped() &&
609 (!aVisitor.mEvent->mFlags.mNoContentDispatch ||
610 targetItem.ForceContentDispatch())) {
611 targetItem.HandleEvent(aVisitor, aCd);
615 if (aVisitor.mEvent->mFlags.mInSystemGroup) {
616 targetItem.PostHandleEvent(aVisitor);
618 aVisitor.mEvent->mFlags.mInTargetPhase = false;
619 if (!prefCorrectOrder) {
620 aVisitor.mEvent->mFlags.mInCapturePhase = false;
623 // Bubble
624 for (uint32_t i = firstCanHandleEventTargetIdx + 1; i < chainLength; ++i) {
625 EventTargetChainItem& item = chain[i];
626 if (item.PreHandleEventOnly()) {
627 continue;
629 EventTarget* newTarget = item.GetNewTarget();
630 if (newTarget) {
631 // Item is at anonymous boundary. Need to retarget for the current item
632 // and for parent items.
633 aVisitor.mEvent->mTarget = newTarget;
636 // https://dom.spec.whatwg.org/#dispatching-events
637 // Step 15.2
638 // "Set event's relatedTarget to tuple's relatedTarget."
639 EventTarget* relatedTarget = item.GetRetargetedRelatedTarget();
640 if (relatedTarget) {
641 aVisitor.mEvent->mRelatedTarget = relatedTarget;
644 if (item.HasRetargetTouchTargets()) {
645 item.RetargetTouchTargets(touchEvent, aVisitor.mDOMEvent);
648 if (aVisitor.mEvent->mFlags.mBubbles || newTarget) {
649 if ((!aVisitor.mEvent->mFlags.mNoContentDispatch ||
650 item.ForceContentDispatch()) &&
651 !aVisitor.mEvent->PropagationStopped()) {
652 item.HandleEvent(aVisitor, aCd);
654 if (aVisitor.mEvent->mFlags.mInSystemGroup) {
655 item.PostHandleEvent(aVisitor);
659 aVisitor.mEvent->mFlags.mInBubblingPhase = false;
661 if (!aVisitor.mEvent->mFlags.mInSystemGroup &&
662 aVisitor.mEvent->IsAllowedToDispatchInSystemGroup()) {
663 // Dispatch to the system event group. Make sure to clear the
664 // STOP_DISPATCH flag since this resets for each event group.
665 aVisitor.mEvent->mFlags.mPropagationStopped = false;
666 aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false;
668 // Setting back the original target of the event.
669 aVisitor.mEvent->mTarget = aVisitor.mEvent->mOriginalTarget;
670 aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
671 if (firstTouchTargets) {
672 WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
673 for (uint32_t i = 0; i < touches.Length(); ++i) {
674 touches[i]->mTarget = touches[i]->mOriginalTarget;
678 // Special handling if PresShell (or some other caller)
679 // used a callback object.
680 if (aCallback) {
681 aCallback->HandleEvent(aVisitor);
684 // Retarget for system event group (which does the default handling too).
685 // Setting back the target which was used also for default event group.
686 aVisitor.mEvent->mTarget = firstTarget;
687 aVisitor.mEvent->mRelatedTarget = firstRelatedTarget;
688 if (firstTouchTargets) {
689 WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
690 for (uint32_t i = 0; i < firstTouchTargets->Length(); ++i) {
691 touches[i]->mTarget = firstTouchTargets->ElementAt(i);
695 aVisitor.mEvent->mFlags.mInSystemGroup = true;
696 HandleEventTargetChain(aChain, aVisitor, aCallback, aCd);
697 aVisitor.mEvent->mFlags.mInSystemGroup = false;
699 // After dispatch, clear all the propagation flags so that
700 // system group listeners don't affect to the event.
701 aVisitor.mEvent->mFlags.mPropagationStopped = false;
702 aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false;
706 // There are often 2 nested event dispatches ongoing at the same time, so
707 // have 2 separate caches.
708 static const uint32_t kCachedMainThreadChainSize = 128;
709 struct CachedChains {
710 nsTArray<EventTargetChainItem> mChain1;
711 nsTArray<EventTargetChainItem> mChain2;
713 static CachedChains* sCachedMainThreadChains = nullptr;
715 /* static */
716 void EventDispatcher::Shutdown() {
717 delete sCachedMainThreadChains;
718 sCachedMainThreadChains = nullptr;
721 EventTargetChainItem* EventTargetChainItemForChromeTarget(
722 nsTArray<EventTargetChainItem>& aChain, nsINode* aNode,
723 EventTargetChainItem* aChild = nullptr) {
724 if (!aNode->IsInComposedDoc()) {
725 return nullptr;
727 nsPIDOMWindowInner* win = aNode->OwnerDoc()->GetInnerWindow();
728 EventTarget* piTarget = win ? win->GetParentTarget() : nullptr;
729 NS_ENSURE_TRUE(piTarget, nullptr);
731 EventTargetChainItem* etci = EventTargetChainItem::Create(
732 aChain, piTarget->GetTargetForEventTargetChain(), aChild);
733 if (!etci->IsValid()) {
734 EventTargetChainItem::DestroyLast(aChain, etci);
735 return nullptr;
737 return etci;
740 /* static */ EventTargetChainItem* MayRetargetToChromeIfCanNotHandleEvent(
741 nsTArray<EventTargetChainItem>& aChain, EventChainPreVisitor& aPreVisitor,
742 EventTargetChainItem* aTargetEtci, EventTargetChainItem* aChildEtci,
743 nsINode* aContent) {
744 if (!aPreVisitor.mWantsPreHandleEvent) {
745 // Keep EventTargetChainItem if we need to call PreHandleEvent on it.
746 EventTargetChainItem::DestroyLast(aChain, aTargetEtci);
748 if (aPreVisitor.mAutomaticChromeDispatch && aContent) {
749 aPreVisitor.mRelatedTargetRetargetedInCurrentScope = false;
750 // Event target couldn't handle the event. Try to propagate to chrome.
751 EventTargetChainItem* chromeTargetEtci =
752 EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci);
753 if (chromeTargetEtci) {
754 // If we propagate to chrome, need to ensure we mark
755 // EventTargetChainItem to be chrome handler so that event.composedPath()
756 // can return the right value.
757 chromeTargetEtci->SetIsChromeHandler(true);
758 chromeTargetEtci->GetEventTargetParent(aPreVisitor);
759 return chromeTargetEtci;
762 return nullptr;
765 static bool ShouldClearTargets(WidgetEvent* aEvent) {
766 if (nsIContent* finalTarget =
767 nsIContent::FromEventTargetOrNull(aEvent->mTarget)) {
768 if (finalTarget->SubtreeRoot()->IsShadowRoot()) {
769 return true;
773 if (nsIContent* finalRelatedTarget =
774 nsIContent::FromEventTargetOrNull(aEvent->mRelatedTarget)) {
775 if (finalRelatedTarget->SubtreeRoot()->IsShadowRoot()) {
776 return true;
779 // XXXsmaug Check also all the touch objects.
781 return false;
784 static void DescribeEventTargetForProfilerMarker(const EventTarget* aTarget,
785 nsACString& aDescription) {
786 auto* node = aTarget->GetAsNode();
787 if (node) {
788 if (node->IsElement()) {
789 nsAutoString nodeDescription;
790 node->AsElement()->Describe(nodeDescription, true);
791 aDescription = NS_ConvertUTF16toUTF8(nodeDescription);
792 } else if (node->IsDocument()) {
793 aDescription.AssignLiteral("document");
794 } else if (node->IsText()) {
795 aDescription.AssignLiteral("text");
796 } else if (node->IsDocumentFragment()) {
797 aDescription.AssignLiteral("document fragment");
799 } else if (aTarget->IsInnerWindow() || aTarget->IsOuterWindow()) {
800 aDescription.AssignLiteral("window");
801 } else if (aTarget->IsRootWindow()) {
802 aDescription.AssignLiteral("root window");
803 } else {
804 // Probably something that inherits from DOMEventTargetHelper.
808 /* static */
809 nsresult EventDispatcher::Dispatch(EventTarget* aTarget,
810 nsPresContext* aPresContext,
811 WidgetEvent* aEvent, Event* aDOMEvent,
812 nsEventStatus* aEventStatus,
813 EventDispatchingCallback* aCallback,
814 nsTArray<EventTarget*>* aTargets) {
815 AUTO_PROFILER_LABEL_HOT("EventDispatcher::Dispatch", OTHER);
817 NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!");
818 NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched,
819 NS_ERROR_DOM_INVALID_STATE_ERR);
820 NS_ASSERTION(!aTargets || !aEvent->mMessage, "Wrong parameters!");
822 // If we're dispatching an already created DOMEvent object, make
823 // sure it is initialized!
824 // If aTargets is non-null, the event isn't going to be dispatched.
825 NS_ENSURE_TRUE(aEvent->mMessage || !aDOMEvent || aTargets,
826 NS_ERROR_DOM_INVALID_STATE_ERR);
828 // Events shall not be fired while we are in stable state to prevent anything
829 // visible from the scripts.
830 MOZ_ASSERT(!nsContentUtils::IsInStableOrMetaStableState());
831 NS_ENSURE_TRUE(!nsContentUtils::IsInStableOrMetaStableState(),
832 NS_ERROR_DOM_INVALID_STATE_ERR);
834 nsCOMPtr<EventTarget> target(aTarget);
836 RefPtr<PerformanceEventTiming> eventTimingEntry;
837 // Similar to PerformancePaintTiming, we don't need to
838 // expose them for printing documents
839 if (aPresContext && !aPresContext->IsPrintingOrPrintPreview()) {
840 eventTimingEntry =
841 PerformanceEventTiming::TryGenerateEventTiming(target, aEvent);
843 if (aEvent->IsTrusted() && aEvent->mMessage == eScroll) {
844 if (auto* perf = aPresContext->GetPerformanceMainThread()) {
845 if (!perf->HasDispatchedScrollEvent()) {
846 perf->SetHasDispatchedScrollEvent();
852 bool retargeted = false;
854 if (aEvent->mFlags.mRetargetToNonNativeAnonymous) {
855 nsIContent* content = nsIContent::FromEventTargetOrNull(target);
856 if (content && content->IsInNativeAnonymousSubtree()) {
857 nsCOMPtr<EventTarget> newTarget =
858 content->FindFirstNonChromeOnlyAccessContent();
859 NS_ENSURE_STATE(newTarget);
861 aEvent->mOriginalTarget = target;
862 target = newTarget;
863 retargeted = true;
867 if (aEvent->mFlags.mOnlyChromeDispatch) {
868 nsCOMPtr<Document> doc;
869 if (!IsEventTargetChrome(target, getter_AddRefs(doc)) && doc) {
870 nsPIDOMWindowInner* win = doc->GetInnerWindow();
871 // If we can't dispatch the event to chrome, do nothing.
872 EventTarget* piTarget = win ? win->GetParentTarget() : nullptr;
873 if (!piTarget) {
874 return NS_OK;
877 // Set the target to be the original dispatch target,
878 aEvent->mTarget = target;
879 // but use chrome event handler or BrowserChildMessageManager for event
880 // target chain.
881 target = piTarget;
882 } else if (NS_WARN_IF(!doc)) {
883 return NS_ERROR_UNEXPECTED;
887 #ifdef DEBUG
888 if (NS_IsMainThread() && aEvent->mMessage != eVoidEvent &&
889 !nsContentUtils::IsSafeToRunScript()) {
890 static const auto warn = [](bool aIsSystem) {
891 if (aIsSystem) {
892 NS_WARNING("Fix the caller!");
893 } else {
894 MOZ_CRASH("This is unsafe! Fix the caller!");
897 if (nsINode* node = nsINode::FromEventTargetOrNull(target)) {
898 // If this is a node, it's possible that this is some sort of DOM tree
899 // that is never accessed by script (for example an SVG image or XBL
900 // binding document or whatnot). We really only want to warn/assert here
901 // if there might be actual scripted listeners for this event, so restrict
902 // the warnings/asserts to the case when script can or once could touch
903 // this node's document.
904 Document* doc = node->OwnerDoc();
905 bool hasHadScriptHandlingObject;
906 nsIGlobalObject* global =
907 doc->GetScriptHandlingObject(hasHadScriptHandlingObject);
908 if (global || hasHadScriptHandlingObject) {
909 warn(nsContentUtils::IsChromeDoc(doc));
911 } else if (nsCOMPtr<nsIGlobalObject> global = target->GetOwnerGlobal()) {
912 warn(global->PrincipalOrNull()->IsSystemPrincipal());
916 if (aDOMEvent) {
917 WidgetEvent* innerEvent = aDOMEvent->WidgetEventPtr();
918 NS_ASSERTION(innerEvent == aEvent,
919 "The inner event of aDOMEvent is not the same as aEvent!");
921 #endif
923 nsresult rv = NS_OK;
924 bool externalDOMEvent = !!(aDOMEvent);
926 // If we have a PresContext, make sure it doesn't die before
927 // event dispatching is finished.
928 RefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
930 ELMCreationDetector cd;
931 nsTArray<EventTargetChainItem> chain;
932 if (cd.IsMainThread()) {
933 if (!sCachedMainThreadChains) {
934 sCachedMainThreadChains = new CachedChains();
937 if (sCachedMainThreadChains->mChain1.Capacity() ==
938 kCachedMainThreadChainSize) {
939 chain = std::move(sCachedMainThreadChains->mChain1);
940 } else if (sCachedMainThreadChains->mChain2.Capacity() ==
941 kCachedMainThreadChainSize) {
942 chain = std::move(sCachedMainThreadChains->mChain2);
943 } else {
944 chain.SetCapacity(kCachedMainThreadChainSize);
948 // Create the event target chain item for the event target.
949 EventTargetChainItem* targetEtci = EventTargetChainItem::Create(
950 chain, target->GetTargetForEventTargetChain());
951 MOZ_ASSERT(&chain[0] == targetEtci);
952 if (!targetEtci->IsValid()) {
953 EventTargetChainItem::DestroyLast(chain, targetEtci);
954 return NS_ERROR_FAILURE;
957 // Make sure that Event::target and Event::originalTarget
958 // point to the last item in the chain.
959 if (!aEvent->mTarget) {
960 // Note, CurrentTarget() points always to the object returned by
961 // GetTargetForEventTargetChain().
962 aEvent->mTarget = targetEtci->CurrentTarget();
963 } else {
964 // XXX But if the target is already set, use that. This is a hack
965 // for the 'load', 'beforeunload' and 'unload' events,
966 // which are dispatched to |window| but have document as their target.
968 // Make sure that the event target points to the right object.
969 aEvent->mTarget = aEvent->mTarget->GetTargetForEventTargetChain();
970 NS_ENSURE_STATE(aEvent->mTarget);
973 if (retargeted) {
974 aEvent->mOriginalTarget =
975 aEvent->mOriginalTarget->GetTargetForEventTargetChain();
976 NS_ENSURE_STATE(aEvent->mOriginalTarget);
977 } else {
978 aEvent->mOriginalTarget = aEvent->mTarget;
981 aEvent->mOriginalRelatedTarget = aEvent->mRelatedTarget;
983 bool clearTargets = false;
985 nsCOMPtr<nsIContent> content =
986 nsIContent::FromEventTargetOrNull(aEvent->mOriginalTarget);
987 bool isInAnon = content && content->IsInNativeAnonymousSubtree();
989 aEvent->mFlags.mIsBeingDispatched = true;
991 Maybe<uint32_t> activationTargetItemIndex;
993 // Create visitor object and start event dispatching.
994 // GetEventTargetParent for the original target.
995 nsEventStatus status = aDOMEvent && aDOMEvent->DefaultPrevented()
996 ? nsEventStatus_eConsumeNoDefault
997 : aEventStatus ? *aEventStatus
998 : nsEventStatus_eIgnore;
999 nsCOMPtr<EventTarget> targetForPreVisitor = aEvent->mTarget;
1000 EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
1001 isInAnon, targetForPreVisitor);
1002 targetEtci->GetEventTargetParent(preVisitor);
1004 if (preVisitor.mWantsActivationBehavior) {
1005 MOZ_ASSERT(&chain[0] == targetEtci);
1006 activationTargetItemIndex.emplace(0);
1009 if (!preVisitor.mCanHandle) {
1010 targetEtci = MayRetargetToChromeIfCanNotHandleEvent(
1011 chain, preVisitor, targetEtci, nullptr, content);
1013 if (!preVisitor.mCanHandle) {
1014 // The original target and chrome target (mAutomaticChromeDispatch=true)
1015 // can not handle the event but we still have to call their PreHandleEvent.
1016 for (uint32_t i = 0; i < chain.Length(); ++i) {
1017 chain[i].PreHandleEvent(preVisitor);
1020 clearTargets = ShouldClearTargets(aEvent);
1021 } else {
1022 // At least the original target can handle the event.
1023 // Setting the retarget to the |target| simplifies retargeting code.
1024 nsCOMPtr<EventTarget> t = aEvent->mTarget;
1025 targetEtci->SetNewTarget(t);
1026 // In order to not change the targetTouches array passed to TouchEvents
1027 // when dispatching events from JS, we need to store the initial Touch
1028 // objects on the list.
1029 if (aEvent->mClass == eTouchEventClass && aDOMEvent) {
1030 TouchEvent* touchEvent = static_cast<TouchEvent*>(aDOMEvent);
1031 TouchList* targetTouches = touchEvent->GetExistingTargetTouches();
1032 if (targetTouches) {
1033 Maybe<nsTArray<RefPtr<dom::Touch>>> initialTargetTouches;
1034 initialTargetTouches.emplace();
1035 for (uint32_t i = 0; i < targetTouches->Length(); ++i) {
1036 initialTargetTouches->AppendElement(targetTouches->Item(i));
1038 targetEtci->SetInitialTargetTouches(std::move(initialTargetTouches));
1039 targetTouches->Clear();
1042 EventTargetChainItem* topEtci = targetEtci;
1043 targetEtci = nullptr;
1044 while (preVisitor.GetParentTarget()) {
1045 EventTarget* parentTarget = preVisitor.GetParentTarget();
1046 EventTargetChainItem* parentEtci =
1047 EventTargetChainItem::Create(chain, parentTarget, topEtci);
1048 if (!parentEtci->IsValid()) {
1049 EventTargetChainItem::DestroyLast(chain, parentEtci);
1050 rv = NS_ERROR_FAILURE;
1051 break;
1054 parentEtci->SetIsSlotInClosedTree(preVisitor.mParentIsSlotInClosedTree);
1055 parentEtci->SetIsChromeHandler(preVisitor.mParentIsChromeHandler);
1057 // Item needs event retargetting.
1058 if (preVisitor.mEventTargetAtParent) {
1059 // Need to set the target of the event
1060 // so that also the next retargeting works.
1061 preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
1062 preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
1063 parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
1066 if (preVisitor.mRetargetedRelatedTarget) {
1067 preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget;
1070 parentEtci->GetEventTargetParent(preVisitor);
1072 if (preVisitor.mWantsActivationBehavior &&
1073 activationTargetItemIndex.isNothing() && aEvent->mFlags.mBubbles) {
1074 MOZ_ASSERT(&chain.LastElement() == parentEtci);
1075 activationTargetItemIndex.emplace(chain.Length() - 1);
1078 if (preVisitor.mCanHandle) {
1079 preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
1080 topEtci = parentEtci;
1081 } else {
1082 bool ignoreBecauseOfShadowDOM = preVisitor.mIgnoreBecauseOfShadowDOM;
1083 nsCOMPtr<nsINode> disabledTarget =
1084 nsINode::FromEventTargetOrNull(parentTarget);
1085 parentEtci = MayRetargetToChromeIfCanNotHandleEvent(
1086 chain, preVisitor, parentEtci, topEtci, disabledTarget);
1087 if (parentEtci && preVisitor.mCanHandle) {
1088 preVisitor.mTargetInKnownToBeHandledScope =
1089 preVisitor.mEvent->mTarget;
1090 EventTargetChainItem* item =
1091 EventTargetChainItem::GetFirstCanHandleEventTarget(chain);
1092 if (!ignoreBecauseOfShadowDOM) {
1093 // If we ignored the target because of Shadow DOM retargeting, we
1094 // shouldn't treat the target to be in the event path at all.
1095 item->SetNewTarget(parentTarget);
1097 topEtci = parentEtci;
1098 continue;
1100 break;
1104 if (activationTargetItemIndex) {
1105 chain[activationTargetItemIndex.value()].LegacyPreActivationBehavior(
1106 preVisitor);
1109 if (NS_SUCCEEDED(rv)) {
1110 if (aTargets) {
1111 aTargets->Clear();
1112 uint32_t numTargets = chain.Length();
1113 EventTarget** targets = aTargets->AppendElements(numTargets);
1114 for (uint32_t i = 0; i < numTargets; ++i) {
1115 targets[i] = chain[i].CurrentTarget()->GetTargetForDOMEvent();
1117 } else {
1118 // Event target chain is created. PreHandle the chain.
1119 for (uint32_t i = 0; i < chain.Length(); ++i) {
1120 chain[i].PreHandleEvent(preVisitor);
1123 RefPtr<nsRefreshDriver> refreshDriver;
1124 if (aEvent->IsTrusted() &&
1125 (aEvent->mMessage == eKeyPress ||
1126 aEvent->mMessage == eMouseClick) &&
1127 aPresContext && aPresContext->GetRootPresContext()) {
1128 refreshDriver = aPresContext->GetRootPresContext()->RefreshDriver();
1129 if (refreshDriver) {
1130 refreshDriver->EnterUserInputProcessing();
1133 auto cleanup = MakeScopeExit([&] {
1134 if (refreshDriver) {
1135 refreshDriver->ExitUserInputProcessing();
1139 clearTargets = ShouldClearTargets(aEvent);
1141 // Handle the chain.
1142 EventChainPostVisitor postVisitor(preVisitor);
1143 MOZ_RELEASE_ASSERT(!aEvent->mPath);
1144 aEvent->mPath = &chain;
1146 if (profiler_is_active()) {
1147 // Add a profiler label and a profiler marker for the actual
1148 // dispatch of the event.
1149 // This is a very hot code path, so we need to make sure not to
1150 // do this extra work when we're not profiling.
1151 if (!postVisitor.mDOMEvent) {
1152 // This is tiny bit slow, but happens only once per event.
1153 // Similar code also in EventListenerManager.
1154 nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget;
1155 RefPtr<Event> event =
1156 EventDispatcher::CreateEvent(et, aPresContext, aEvent, u""_ns);
1157 event.swap(postVisitor.mDOMEvent);
1159 nsAutoString typeStr;
1160 postVisitor.mDOMEvent->GetType(typeStr);
1161 AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
1162 "EventDispatcher::Dispatch", OTHER, typeStr);
1164 nsCOMPtr<nsIDocShell> docShell;
1165 docShell = nsContentUtils::GetDocShellForEventTarget(aEvent->mTarget);
1166 MarkerInnerWindowId innerWindowId;
1167 if (nsCOMPtr<nsPIDOMWindowInner> inner =
1168 do_QueryInterface(aEvent->mTarget->GetOwnerGlobal())) {
1169 innerWindowId = MarkerInnerWindowId{inner->WindowID()};
1172 struct DOMEventMarker {
1173 static constexpr Span<const char> MarkerTypeName() {
1174 return MakeStringSpan("DOMEvent");
1176 static void StreamJSONMarkerData(
1177 baseprofiler::SpliceableJSONWriter& aWriter,
1178 const ProfilerString16View& aEventType,
1179 const nsCString& aTarget, const TimeStamp& aStartTime,
1180 const TimeStamp& aEventTimeStamp) {
1181 aWriter.StringProperty("eventType",
1182 NS_ConvertUTF16toUTF8(aEventType));
1183 if (!aTarget.IsEmpty()) {
1184 aWriter.StringProperty("target", aTarget);
1186 // This is the event processing latency, which is the time from
1187 // when the event was created, to when it was started to be
1188 // processed. Note that the computation of this latency is
1189 // deferred until serialization time, at the expense of some extra
1190 // memory.
1191 aWriter.DoubleProperty(
1192 "latency", (aStartTime - aEventTimeStamp).ToMilliseconds());
1194 static MarkerSchema MarkerTypeDisplay() {
1195 using MS = MarkerSchema;
1196 MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable,
1197 MS::Location::TimelineOverview};
1198 schema.SetChartLabel("{marker.data.eventType}");
1199 schema.SetTooltipLabel("{marker.data.eventType} - DOMEvent");
1200 schema.SetTableLabel(
1201 "{marker.data.eventType} - {marker.data.target}");
1202 schema.AddKeyLabelFormatSearchable("target", "Event Target",
1203 MS::Format::String,
1204 MS::Searchable::Searchable);
1205 schema.AddKeyLabelFormat("latency", "Latency",
1206 MS::Format::Duration);
1207 schema.AddKeyLabelFormatSearchable("eventType", "Event Type",
1208 MS::Format::String,
1209 MS::Searchable::Searchable);
1210 return schema;
1214 nsAutoCString target;
1215 DescribeEventTargetForProfilerMarker(aEvent->mTarget, target);
1217 auto startTime = TimeStamp::Now();
1218 profiler_add_marker("DOMEvent", geckoprofiler::category::DOM,
1219 {MarkerTiming::IntervalStart(),
1220 MarkerInnerWindowId(innerWindowId)},
1221 DOMEventMarker{}, typeStr, target, startTime,
1222 aEvent->mTimeStamp);
1224 EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
1225 aCallback, cd);
1227 profiler_add_marker(
1228 "DOMEvent", geckoprofiler::category::DOM,
1229 {MarkerTiming::IntervalEnd(), std::move(innerWindowId)},
1230 DOMEventMarker{}, typeStr, target, startTime, aEvent->mTimeStamp);
1231 } else {
1232 EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
1233 aCallback, cd);
1235 aEvent->mPath = nullptr;
1237 if (aEvent->IsTrusted() &&
1238 (aEvent->mMessage == eKeyPress ||
1239 aEvent->mMessage == eMouseClick) &&
1240 aPresContext && aPresContext->GetRootPresContext()) {
1241 nsRefreshDriver* driver =
1242 aPresContext->GetRootPresContext()->RefreshDriver();
1243 if (driver && driver->HasPendingTick()) {
1244 switch (aEvent->mMessage) {
1245 case eKeyPress:
1246 driver->RegisterCompositionPayload(
1247 {layers::CompositionPayloadType::eKeyPress,
1248 aEvent->mTimeStamp});
1249 break;
1250 case eMouseClick: {
1251 if (aEvent->AsMouseEvent()->mInputSource ==
1252 MouseEvent_Binding::MOZ_SOURCE_MOUSE ||
1253 aEvent->AsMouseEvent()->mInputSource ==
1254 MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
1255 driver->RegisterCompositionPayload(
1256 {layers::CompositionPayloadType::eMouseUpFollowedByClick,
1257 aEvent->mTimeStamp});
1259 break;
1261 default:
1262 break;
1267 preVisitor.mEventStatus = postVisitor.mEventStatus;
1268 // If the DOM event was created during event flow.
1269 if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
1270 preVisitor.mDOMEvent = postVisitor.mDOMEvent;
1276 // Note, EventTargetChainItem objects are deleted when the chain goes out of
1277 // the scope.
1279 aEvent->mFlags.mIsBeingDispatched = false;
1280 aEvent->mFlags.mDispatchedAtLeastOnce = true;
1282 if (eventTimingEntry) {
1283 eventTimingEntry->FinalizeEventTiming(aEvent->mTarget);
1285 // https://dom.spec.whatwg.org/#concept-event-dispatch
1286 // step 10. If clearTargets, then:
1287 // 1. Set event's target to null.
1288 // 2. Set event's relatedTarget to null.
1289 // 3. Set event's touch target list to the empty list.
1290 if (clearTargets) {
1291 aEvent->mTarget = nullptr;
1292 aEvent->mOriginalTarget = nullptr;
1293 aEvent->mRelatedTarget = nullptr;
1294 aEvent->mOriginalRelatedTarget = nullptr;
1295 // XXXsmaug Check also all the touch objects.
1298 if (activationTargetItemIndex) {
1299 EventChainPostVisitor postVisitor(preVisitor);
1300 if (preVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
1301 chain[activationTargetItemIndex.value()].LegacyCanceledActivationBehavior(
1302 postVisitor);
1303 } else {
1304 chain[activationTargetItemIndex.value()].ActivationBehavior(postVisitor);
1306 preVisitor.mEventStatus = postVisitor.mEventStatus;
1307 // If the DOM event was created during event flow.
1308 if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
1309 preVisitor.mDOMEvent = postVisitor.mDOMEvent;
1313 if (!externalDOMEvent && preVisitor.mDOMEvent) {
1314 // A dom::Event was created while dispatching the event.
1315 // Duplicate private data if someone holds a pointer to it.
1316 nsrefcnt rc = 0;
1317 NS_RELEASE2(preVisitor.mDOMEvent, rc);
1318 if (preVisitor.mDOMEvent) {
1319 preVisitor.mDOMEvent->DuplicatePrivateData();
1323 if (aEventStatus) {
1324 *aEventStatus = preVisitor.mEventStatus;
1327 if (cd.IsMainThread() && chain.Capacity() == kCachedMainThreadChainSize &&
1328 sCachedMainThreadChains) {
1329 if (sCachedMainThreadChains->mChain1.Capacity() !=
1330 kCachedMainThreadChainSize) {
1331 chain.ClearAndRetainStorage();
1332 chain.SwapElements(sCachedMainThreadChains->mChain1);
1333 } else if (sCachedMainThreadChains->mChain2.Capacity() !=
1334 kCachedMainThreadChainSize) {
1335 chain.ClearAndRetainStorage();
1336 chain.SwapElements(sCachedMainThreadChains->mChain2);
1340 return rv;
1343 /* static */
1344 nsresult EventDispatcher::DispatchDOMEvent(EventTarget* aTarget,
1345 WidgetEvent* aEvent,
1346 Event* aDOMEvent,
1347 nsPresContext* aPresContext,
1348 nsEventStatus* aEventStatus) {
1349 if (aDOMEvent) {
1350 WidgetEvent* innerEvent = aDOMEvent->WidgetEventPtr();
1351 NS_ENSURE_TRUE(innerEvent, NS_ERROR_ILLEGAL_VALUE);
1353 // Don't modify the event if it's being dispatched right now.
1354 if (innerEvent->mFlags.mIsBeingDispatched) {
1355 return NS_ERROR_DOM_INVALID_STATE_ERR;
1358 bool dontResetTrusted = false;
1359 if (innerEvent->mFlags.mDispatchedAtLeastOnce) {
1360 innerEvent->mTarget = nullptr;
1361 innerEvent->mOriginalTarget = nullptr;
1362 } else {
1363 dontResetTrusted = aDOMEvent->IsTrusted();
1366 if (!dontResetTrusted) {
1367 // Check security state to determine if dispatcher is trusted
1368 bool trusted = NS_IsMainThread()
1369 ? nsContentUtils::LegacyIsCallerChromeOrNativeCode()
1370 : IsCurrentThreadRunningChromeWorker();
1371 aDOMEvent->SetTrusted(trusted);
1374 return EventDispatcher::Dispatch(aTarget, aPresContext, innerEvent,
1375 aDOMEvent, aEventStatus);
1376 } else if (aEvent) {
1377 return EventDispatcher::Dispatch(aTarget, aPresContext, aEvent, aDOMEvent,
1378 aEventStatus);
1380 return NS_ERROR_ILLEGAL_VALUE;
1383 /* static */ already_AddRefed<dom::Event> EventDispatcher::CreateEvent(
1384 EventTarget* aOwner, nsPresContext* aPresContext, WidgetEvent* aEvent,
1385 const nsAString& aEventType, CallerType aCallerType) {
1386 if (aEvent) {
1387 switch (aEvent->mClass) {
1388 case eMutationEventClass:
1389 return NS_NewDOMMutationEvent(aOwner, aPresContext,
1390 aEvent->AsMutationEvent());
1391 case eGUIEventClass:
1392 case eScrollPortEventClass:
1393 case eUIEventClass:
1394 return NS_NewDOMUIEvent(aOwner, aPresContext, aEvent->AsGUIEvent());
1395 case eScrollAreaEventClass:
1396 return NS_NewDOMScrollAreaEvent(aOwner, aPresContext,
1397 aEvent->AsScrollAreaEvent());
1398 case eKeyboardEventClass:
1399 return NS_NewDOMKeyboardEvent(aOwner, aPresContext,
1400 aEvent->AsKeyboardEvent());
1401 case eCompositionEventClass:
1402 return NS_NewDOMCompositionEvent(aOwner, aPresContext,
1403 aEvent->AsCompositionEvent());
1404 case eMouseEventClass:
1405 return NS_NewDOMMouseEvent(aOwner, aPresContext,
1406 aEvent->AsMouseEvent());
1407 case eFocusEventClass:
1408 return NS_NewDOMFocusEvent(aOwner, aPresContext,
1409 aEvent->AsFocusEvent());
1410 case eMouseScrollEventClass:
1411 return NS_NewDOMMouseScrollEvent(aOwner, aPresContext,
1412 aEvent->AsMouseScrollEvent());
1413 case eWheelEventClass:
1414 return NS_NewDOMWheelEvent(aOwner, aPresContext,
1415 aEvent->AsWheelEvent());
1416 case eEditorInputEventClass:
1417 return NS_NewDOMInputEvent(aOwner, aPresContext,
1418 aEvent->AsEditorInputEvent());
1419 case eDragEventClass:
1420 return NS_NewDOMDragEvent(aOwner, aPresContext, aEvent->AsDragEvent());
1421 case eClipboardEventClass:
1422 return NS_NewDOMClipboardEvent(aOwner, aPresContext,
1423 aEvent->AsClipboardEvent());
1424 case eSMILTimeEventClass:
1425 return NS_NewDOMTimeEvent(aOwner, aPresContext,
1426 aEvent->AsSMILTimeEvent());
1427 case eCommandEventClass:
1428 return NS_NewDOMCommandEvent(aOwner, aPresContext,
1429 aEvent->AsCommandEvent());
1430 case eSimpleGestureEventClass:
1431 return NS_NewDOMSimpleGestureEvent(aOwner, aPresContext,
1432 aEvent->AsSimpleGestureEvent());
1433 case ePointerEventClass:
1434 return NS_NewDOMPointerEvent(aOwner, aPresContext,
1435 aEvent->AsPointerEvent());
1436 case eTouchEventClass:
1437 return NS_NewDOMTouchEvent(aOwner, aPresContext,
1438 aEvent->AsTouchEvent());
1439 case eTransitionEventClass:
1440 return NS_NewDOMTransitionEvent(aOwner, aPresContext,
1441 aEvent->AsTransitionEvent());
1442 case eAnimationEventClass:
1443 return NS_NewDOMAnimationEvent(aOwner, aPresContext,
1444 aEvent->AsAnimationEvent());
1445 default:
1446 // For all other types of events, create a vanilla event object.
1447 return NS_NewDOMEvent(aOwner, aPresContext, aEvent);
1451 // And if we didn't get an event, check the type argument.
1453 if (aEventType.LowerCaseEqualsLiteral("mouseevent") ||
1454 aEventType.LowerCaseEqualsLiteral("mouseevents")) {
1455 return NS_NewDOMMouseEvent(aOwner, aPresContext, nullptr);
1457 if (aEventType.LowerCaseEqualsLiteral("dragevent")) {
1458 return NS_NewDOMDragEvent(aOwner, aPresContext, nullptr);
1460 if (aEventType.LowerCaseEqualsLiteral("keyboardevent")) {
1461 return NS_NewDOMKeyboardEvent(aOwner, aPresContext, nullptr);
1463 if (aEventType.LowerCaseEqualsLiteral("compositionevent") ||
1464 aEventType.LowerCaseEqualsLiteral("textevent")) {
1465 return NS_NewDOMCompositionEvent(aOwner, aPresContext, nullptr);
1467 if (aEventType.LowerCaseEqualsLiteral("mutationevent") ||
1468 aEventType.LowerCaseEqualsLiteral("mutationevents")) {
1469 return NS_NewDOMMutationEvent(aOwner, aPresContext, nullptr);
1471 if (aEventType.LowerCaseEqualsLiteral("deviceorientationevent")) {
1472 DeviceOrientationEventInit init;
1473 RefPtr<Event> event =
1474 DeviceOrientationEvent::Constructor(aOwner, u""_ns, init);
1475 event->MarkUninitialized();
1476 return event.forget();
1478 if (aEventType.LowerCaseEqualsLiteral("devicemotionevent")) {
1479 return NS_NewDOMDeviceMotionEvent(aOwner, aPresContext, nullptr);
1481 if (aEventType.LowerCaseEqualsLiteral("uievent") ||
1482 aEventType.LowerCaseEqualsLiteral("uievents")) {
1483 return NS_NewDOMUIEvent(aOwner, aPresContext, nullptr);
1485 if (aEventType.LowerCaseEqualsLiteral("event") ||
1486 aEventType.LowerCaseEqualsLiteral("events") ||
1487 aEventType.LowerCaseEqualsLiteral("htmlevents") ||
1488 aEventType.LowerCaseEqualsLiteral("svgevents")) {
1489 return NS_NewDOMEvent(aOwner, aPresContext, nullptr);
1491 if (aEventType.LowerCaseEqualsLiteral("messageevent")) {
1492 RefPtr<Event> event = new MessageEvent(aOwner, aPresContext, nullptr);
1493 return event.forget();
1495 if (aEventType.LowerCaseEqualsLiteral("beforeunloadevent")) {
1496 return NS_NewDOMBeforeUnloadEvent(aOwner, aPresContext, nullptr);
1498 if (aEventType.LowerCaseEqualsLiteral("touchevent") &&
1499 TouchEvent::LegacyAPIEnabled(
1500 nsContentUtils::GetDocShellForEventTarget(aOwner),
1501 aCallerType == CallerType::System)) {
1502 return NS_NewDOMTouchEvent(aOwner, aPresContext, nullptr);
1504 if (aEventType.LowerCaseEqualsLiteral("hashchangeevent")) {
1505 HashChangeEventInit init;
1506 RefPtr<Event> event = HashChangeEvent::Constructor(aOwner, u""_ns, init);
1507 event->MarkUninitialized();
1508 return event.forget();
1510 if (aEventType.LowerCaseEqualsLiteral("customevent")) {
1511 return NS_NewDOMCustomEvent(aOwner, aPresContext, nullptr);
1513 if (aEventType.LowerCaseEqualsLiteral("storageevent")) {
1514 RefPtr<Event> event =
1515 StorageEvent::Constructor(aOwner, u""_ns, StorageEventInit());
1516 event->MarkUninitialized();
1517 return event.forget();
1519 if (aEventType.LowerCaseEqualsLiteral("focusevent")) {
1520 RefPtr<Event> event = NS_NewDOMFocusEvent(aOwner, aPresContext, nullptr);
1521 event->MarkUninitialized();
1522 return event.forget();
1525 // Only allow these events for chrome
1526 if (aCallerType == CallerType::System) {
1527 if (aEventType.LowerCaseEqualsLiteral("simplegestureevent")) {
1528 return NS_NewDOMSimpleGestureEvent(aOwner, aPresContext, nullptr);
1530 if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") ||
1531 aEventType.LowerCaseEqualsLiteral("xulcommandevents")) {
1532 return NS_NewDOMXULCommandEvent(aOwner, aPresContext, nullptr);
1536 // NEW EVENT TYPES SHOULD NOT BE ADDED HERE; THEY SHOULD USE ONLY EVENT
1537 // CONSTRUCTORS
1539 return nullptr;
1542 struct CurrentTargetPathInfo {
1543 uint32_t mIndex;
1544 int32_t mHiddenSubtreeLevel;
1547 static CurrentTargetPathInfo TargetPathInfo(
1548 const nsTArray<EventTargetChainItem>& aEventPath,
1549 const EventTarget& aCurrentTarget) {
1550 int32_t currentTargetHiddenSubtreeLevel = 0;
1551 for (uint32_t index = aEventPath.Length(); index--;) {
1552 const EventTargetChainItem& item = aEventPath.ElementAt(index);
1553 if (item.PreHandleEventOnly()) {
1554 continue;
1557 if (item.IsRootOfClosedTree()) {
1558 currentTargetHiddenSubtreeLevel++;
1561 if (item.CurrentTarget() == &aCurrentTarget) {
1562 return {index, currentTargetHiddenSubtreeLevel};
1565 if (item.IsSlotInClosedTree()) {
1566 currentTargetHiddenSubtreeLevel--;
1569 MOZ_ASSERT_UNREACHABLE("No target found?");
1570 return {0, 0};
1573 // https://dom.spec.whatwg.org/#dom-event-composedpath
1574 void EventDispatcher::GetComposedPathFor(WidgetEvent* aEvent,
1575 nsTArray<RefPtr<EventTarget>>& aPath) {
1576 MOZ_ASSERT(aPath.IsEmpty());
1577 nsTArray<EventTargetChainItem>* path = aEvent->mPath;
1578 if (!path || path->IsEmpty() || !aEvent->mCurrentTarget) {
1579 return;
1582 EventTarget* currentTarget =
1583 aEvent->mCurrentTarget->GetTargetForEventTargetChain();
1584 if (!currentTarget) {
1585 return;
1588 CurrentTargetPathInfo currentTargetInfo =
1589 TargetPathInfo(*path, *currentTarget);
1592 int32_t maxHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1593 int32_t currentHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1594 for (uint32_t index = currentTargetInfo.mIndex; index--;) {
1595 EventTargetChainItem& item = path->ElementAt(index);
1596 if (item.PreHandleEventOnly()) {
1597 continue;
1600 if (item.IsRootOfClosedTree()) {
1601 currentHiddenLevel++;
1604 if (currentHiddenLevel <= maxHiddenLevel) {
1605 aPath.AppendElement(item.CurrentTarget()->GetTargetForDOMEvent());
1608 if (item.IsChromeHandler()) {
1609 break;
1612 if (item.IsSlotInClosedTree()) {
1613 currentHiddenLevel--;
1614 maxHiddenLevel = std::min(maxHiddenLevel, currentHiddenLevel);
1618 aPath.Reverse();
1621 aPath.AppendElement(currentTarget->GetTargetForDOMEvent());
1624 int32_t maxHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1625 int32_t currentHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1626 for (uint32_t index = currentTargetInfo.mIndex + 1; index < path->Length();
1627 ++index) {
1628 EventTargetChainItem& item = path->ElementAt(index);
1629 if (item.PreHandleEventOnly()) {
1630 continue;
1633 if (item.IsSlotInClosedTree()) {
1634 currentHiddenLevel++;
1637 if (item.IsChromeHandler()) {
1638 break;
1641 if (currentHiddenLevel <= maxHiddenLevel) {
1642 aPath.AppendElement(item.CurrentTarget()->GetTargetForDOMEvent());
1645 if (item.IsRootOfClosedTree()) {
1646 currentHiddenLevel--;
1647 maxHiddenLevel = std::min(maxHiddenLevel, currentHiddenLevel);
1653 void EventChainPreVisitor::IgnoreCurrentTargetBecauseOfShadowDOMRetargeting() {
1654 mCanHandle = false;
1655 mIgnoreBecauseOfShadowDOM = true;
1657 EventTarget* target = nullptr;
1659 auto getWindow = [this]() -> nsPIDOMWindowOuter* {
1660 nsINode* node = nsINode::FromEventTargetOrNull(this->mParentTarget);
1661 if (!node) {
1662 return nullptr;
1664 Document* doc = node->GetComposedDoc();
1665 if (!doc) {
1666 return nullptr;
1669 return doc->GetWindow();
1672 // The HTMLEditor is registered to nsWindowRoot, so we
1673 // want to dispatch events to it.
1674 if (nsCOMPtr<nsPIDOMWindowOuter> win = getWindow()) {
1675 target = win->GetParentTarget();
1677 SetParentTarget(target, false);
1679 mEventTargetAtParent = nullptr;
1682 } // namespace mozilla