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