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/. */
6 
7 #include "nsPresContext.h"
8 #include "nsContentUtils.h"
9 #include "nsDocShell.h"
10 #include "nsError.h"
11 #include <new>
12 #include "nsIContent.h"
13 #include "nsIContentInlines.h"
14 #include "mozilla/dom/Document.h"
15 #include "nsINode.h"
16 #include "nsIScriptObjectPrincipal.h"
17 #include "nsPIDOMWindow.h"
18 #include "nsRefreshDriver.h"
19 #include "AnimationEvent.h"
20 #include "BeforeUnloadEvent.h"
21 #include "ClipboardEvent.h"
22 #include "CommandEvent.h"
23 #include "CompositionEvent.h"
24 #include "DeviceMotionEvent.h"
25 #include "DragEvent.h"
26 #include "KeyboardEvent.h"
27 #include "Layers.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/PointerEvent.h"
44 #include "mozilla/dom/RootedDictionary.h"
45 #include "mozilla/dom/ScrollAreaEvent.h"
46 #include "mozilla/dom/SimpleGestureEvent.h"
47 #include "mozilla/dom/ScriptSettings.h"
48 #include "mozilla/dom/StorageEvent.h"
49 #include "mozilla/dom/TimeEvent.h"
50 #include "mozilla/dom/TouchEvent.h"
51 #include "mozilla/dom/TransitionEvent.h"
52 #include "mozilla/dom/WheelEvent.h"
53 #include "mozilla/dom/WorkerPrivate.h"
54 #include "mozilla/dom/XULCommandEvent.h"
55 #include "mozilla/EventDispatcher.h"
56 #include "mozilla/EventListenerManager.h"
57 #include "mozilla/InternalMutationEvent.h"
58 #include "mozilla/ipc/MessageChannel.h"
59 #include "mozilla/MiscEvents.h"
60 #include "mozilla/MouseEvents.h"
61 #include "mozilla/ProfilerLabels.h"
62 #include "mozilla/ProfilerMarkers.h"
63 #include "mozilla/ScopeExit.h"
64 #include "mozilla/Telemetry.h"
65 #include "mozilla/TextEvents.h"
66 #include "mozilla/TouchEvents.h"
67 #include "mozilla/Unused.h"
68 
69 namespace mozilla {
70 
71 using namespace dom;
72 
73 class ELMCreationDetector {
74  public:
ELMCreationDetector()75   ELMCreationDetector()
76       // We can do this optimization only in the main thread.
77       : mNonMainThread(!NS_IsMainThread()),
78         mInitialCount(mNonMainThread
79                           ? 0
80                           : EventListenerManager::sMainThreadCreatedCount) {}
81 
MayHaveNewListenerManager()82   bool MayHaveNewListenerManager() {
83     return mNonMainThread ||
84            mInitialCount != EventListenerManager::sMainThreadCreatedCount;
85   }
86 
IsMainThread()87   bool IsMainThread() { return !mNonMainThread; }
88 
89  private:
90   bool mNonMainThread;
91   uint32_t mInitialCount;
92 };
93 
IsEventTargetChrome(EventTarget * aEventTarget,Document ** aDocument=nullptr)94 static bool IsEventTargetChrome(EventTarget* aEventTarget,
95                                 Document** aDocument = nullptr) {
96   if (aDocument) {
97     *aDocument = nullptr;
98   }
99 
100   Document* doc = nullptr;
101   if (nsCOMPtr<nsINode> node = do_QueryInterface(aEventTarget)) {
102     doc = node->OwnerDoc();
103   } else if (nsCOMPtr<nsPIDOMWindowInner> window =
104                  do_QueryInterface(aEventTarget)) {
105     doc = window->GetExtantDoc();
106   }
107 
108   // nsContentUtils::IsChromeDoc is null-safe.
109   bool isChrome = false;
110   if (doc) {
111     isChrome = nsContentUtils::IsChromeDoc(doc);
112     if (aDocument) {
113       nsCOMPtr<Document> retVal = doc;
114       retVal.swap(*aDocument);
115     }
116   } else if (nsCOMPtr<nsIScriptObjectPrincipal> sop =
117                  do_QueryInterface(aEventTarget->GetOwnerGlobal())) {
118     isChrome = sop->GetPrincipal()->IsSystemPrincipal();
119   }
120   return isChrome;
121 }
122 
123 // EventTargetChainItem represents a single item in the event target chain.
124 class EventTargetChainItem {
125  public:
EventTargetChainItem(EventTarget * aTarget)126   explicit EventTargetChainItem(EventTarget* aTarget)
127       : mTarget(aTarget), mItemFlags(0) {
128     MOZ_COUNT_CTOR(EventTargetChainItem);
129   }
130 
MOZ_COUNTED_DTOR(EventTargetChainItem)131   MOZ_COUNTED_DTOR(EventTargetChainItem)
132 
133   static EventTargetChainItem* Create(nsTArray<EventTargetChainItem>& aChain,
134                                       EventTarget* aTarget,
135                                       EventTargetChainItem* aChild = nullptr) {
136     // The last item which can handle the event must be aChild.
137     MOZ_ASSERT(GetLastCanHandleEventTarget(aChain) == aChild);
138     MOZ_ASSERT(!aTarget || aTarget == aTarget->GetTargetForEventTargetChain());
139     EventTargetChainItem* etci = aChain.AppendElement(aTarget);
140     return etci;
141   }
142 
DestroyLast(nsTArray<EventTargetChainItem> & aChain,EventTargetChainItem * aItem)143   static void DestroyLast(nsTArray<EventTargetChainItem>& aChain,
144                           EventTargetChainItem* aItem) {
145     MOZ_ASSERT(&aChain.LastElement() == aItem);
146     aChain.RemoveLastElement();
147   }
148 
GetFirstCanHandleEventTarget(nsTArray<EventTargetChainItem> & aChain)149   static EventTargetChainItem* GetFirstCanHandleEventTarget(
150       nsTArray<EventTargetChainItem>& aChain) {
151     return &aChain[GetFirstCanHandleEventTargetIdx(aChain)];
152   }
153 
GetFirstCanHandleEventTargetIdx(nsTArray<EventTargetChainItem> & aChain)154   static uint32_t GetFirstCanHandleEventTargetIdx(
155       nsTArray<EventTargetChainItem>& aChain) {
156     // aChain[i].PreHandleEventOnly() = true only when the target element wants
157     // PreHandleEvent and set mCanHandle=false. So we find the first element
158     // which can handle the event.
159     for (uint32_t i = 0; i < aChain.Length(); ++i) {
160       if (!aChain[i].PreHandleEventOnly()) {
161         return i;
162       }
163     }
164     MOZ_ASSERT(false);
165     return 0;
166   }
167 
GetLastCanHandleEventTarget(nsTArray<EventTargetChainItem> & aChain)168   static EventTargetChainItem* GetLastCanHandleEventTarget(
169       nsTArray<EventTargetChainItem>& aChain) {
170     // Fine the last item which can handle the event.
171     for (int32_t i = aChain.Length() - 1; i >= 0; --i) {
172       if (!aChain[i].PreHandleEventOnly()) {
173         return &aChain[i];
174       }
175     }
176     return nullptr;
177   }
178 
IsValid() const179   bool IsValid() const {
180     NS_WARNING_ASSERTION(!!(mTarget), "Event target is not valid!");
181     return !!(mTarget);
182   }
183 
GetNewTarget() const184   EventTarget* GetNewTarget() const { return mNewTarget; }
185 
SetNewTarget(EventTarget * aNewTarget)186   void SetNewTarget(EventTarget* aNewTarget) { mNewTarget = aNewTarget; }
187 
GetRetargetedRelatedTarget()188   EventTarget* GetRetargetedRelatedTarget() { return mRetargetedRelatedTarget; }
189 
SetRetargetedRelatedTarget(EventTarget * aTarget)190   void SetRetargetedRelatedTarget(EventTarget* aTarget) {
191     mRetargetedRelatedTarget = aTarget;
192   }
193 
SetRetargetedTouchTarget(Maybe<nsTArray<RefPtr<EventTarget>>> && aTargets)194   void SetRetargetedTouchTarget(
195       Maybe<nsTArray<RefPtr<EventTarget>>>&& aTargets) {
196     mRetargetedTouchTargets = std::move(aTargets);
197   }
198 
HasRetargetTouchTargets() const199   bool HasRetargetTouchTargets() const {
200     return mRetargetedTouchTargets.isSome() || mInitialTargetTouches.isSome();
201   }
202 
RetargetTouchTargets(WidgetTouchEvent * aTouchEvent,Event * aDOMEvent)203   void RetargetTouchTargets(WidgetTouchEvent* aTouchEvent, Event* aDOMEvent) {
204     MOZ_ASSERT(HasRetargetTouchTargets());
205     MOZ_ASSERT(aTouchEvent,
206                "mRetargetedTouchTargets should be empty when dispatching "
207                "non-touch events.");
208 
209     if (mRetargetedTouchTargets.isSome()) {
210       WidgetTouchEvent::TouchArray& touches = aTouchEvent->mTouches;
211       MOZ_ASSERT(!touches.Length() ||
212                  touches.Length() == mRetargetedTouchTargets->Length());
213       for (uint32_t i = 0; i < touches.Length(); ++i) {
214         touches[i]->mTarget = mRetargetedTouchTargets->ElementAt(i);
215       }
216     }
217 
218     if (aDOMEvent) {
219       // The number of touch objects in targetTouches list may change depending
220       // on the retargeting.
221       TouchEvent* touchDOMEvent = static_cast<TouchEvent*>(aDOMEvent);
222       TouchList* targetTouches = touchDOMEvent->GetExistingTargetTouches();
223       if (targetTouches) {
224         targetTouches->Clear();
225         if (mInitialTargetTouches.isSome()) {
226           for (uint32_t i = 0; i < mInitialTargetTouches->Length(); ++i) {
227             Touch* touch = mInitialTargetTouches->ElementAt(i);
228             if (touch) {
229               touch->mTarget = touch->mOriginalTarget;
230             }
231             targetTouches->Append(touch);
232           }
233         }
234       }
235     }
236   }
237 
SetInitialTargetTouches(Maybe<nsTArray<RefPtr<dom::Touch>>> && aInitialTargetTouches)238   void SetInitialTargetTouches(
239       Maybe<nsTArray<RefPtr<dom::Touch>>>&& aInitialTargetTouches) {
240     mInitialTargetTouches = std::move(aInitialTargetTouches);
241   }
242 
SetForceContentDispatch(bool aForce)243   void SetForceContentDispatch(bool aForce) {
244     mFlags.mForceContentDispatch = aForce;
245   }
246 
ForceContentDispatch() const247   bool ForceContentDispatch() const { return mFlags.mForceContentDispatch; }
248 
SetWantsWillHandleEvent(bool aWants)249   void SetWantsWillHandleEvent(bool aWants) {
250     mFlags.mWantsWillHandleEvent = aWants;
251   }
252 
WantsWillHandleEvent() const253   bool WantsWillHandleEvent() const { return mFlags.mWantsWillHandleEvent; }
254 
SetWantsPreHandleEvent(bool aWants)255   void SetWantsPreHandleEvent(bool aWants) {
256     mFlags.mWantsPreHandleEvent = aWants;
257   }
258 
WantsPreHandleEvent() const259   bool WantsPreHandleEvent() const { return mFlags.mWantsPreHandleEvent; }
260 
SetPreHandleEventOnly(bool aWants)261   void SetPreHandleEventOnly(bool aWants) {
262     mFlags.mPreHandleEventOnly = aWants;
263   }
264 
PreHandleEventOnly() const265   bool PreHandleEventOnly() const { return mFlags.mPreHandleEventOnly; }
266 
SetRootOfClosedTree(bool aSet)267   void SetRootOfClosedTree(bool aSet) { mFlags.mRootOfClosedTree = aSet; }
268 
IsRootOfClosedTree() const269   bool IsRootOfClosedTree() const { return mFlags.mRootOfClosedTree; }
270 
SetItemInShadowTree(bool aSet)271   void SetItemInShadowTree(bool aSet) { mFlags.mItemInShadowTree = aSet; }
272 
IsItemInShadowTree() const273   bool IsItemInShadowTree() const { return mFlags.mItemInShadowTree; }
274 
SetIsSlotInClosedTree(bool aSet)275   void SetIsSlotInClosedTree(bool aSet) { mFlags.mIsSlotInClosedTree = aSet; }
276 
IsSlotInClosedTree() const277   bool IsSlotInClosedTree() const { return mFlags.mIsSlotInClosedTree; }
278 
SetIsChromeHandler(bool aSet)279   void SetIsChromeHandler(bool aSet) { mFlags.mIsChromeHandler = aSet; }
280 
IsChromeHandler() const281   bool IsChromeHandler() const { return mFlags.mIsChromeHandler; }
282 
SetMayHaveListenerManager(bool aMayHave)283   void SetMayHaveListenerManager(bool aMayHave) {
284     mFlags.mMayHaveManager = aMayHave;
285   }
286 
MayHaveListenerManager()287   bool MayHaveListenerManager() { return mFlags.mMayHaveManager; }
288 
CurrentTarget() const289   EventTarget* CurrentTarget() const { return mTarget; }
290 
291   /**
292    * Dispatches event through the event target chain.
293    * Handles capture, target and bubble phases both in default
294    * and system event group and calls also PostHandleEvent for each
295    * item in the chain.
296    */
297   MOZ_CAN_RUN_SCRIPT
298   static void HandleEventTargetChain(nsTArray<EventTargetChainItem>& aChain,
299                                      EventChainPostVisitor& aVisitor,
300                                      EventDispatchingCallback* aCallback,
301                                      ELMCreationDetector& aCd);
302 
303   /**
304    * Resets aVisitor object and calls GetEventTargetParent.
305    * Copies mItemFlags and mItemData to the current EventTargetChainItem.
306    */
307   void GetEventTargetParent(EventChainPreVisitor& aVisitor);
308 
309   /**
310    * Calls PreHandleEvent for those items which called SetWantsPreHandleEvent.
311    */
312   void PreHandleEvent(EventChainVisitor& aVisitor);
313 
314   /**
315    * If the current item in the event target chain has an event listener
316    * manager, this method calls EventListenerManager::HandleEvent().
317    */
HandleEvent(EventChainPostVisitor & aVisitor,ELMCreationDetector & aCd)318   void HandleEvent(EventChainPostVisitor& aVisitor, ELMCreationDetector& aCd) {
319     if (WantsWillHandleEvent()) {
320       mTarget->WillHandleEvent(aVisitor);
321     }
322     if (aVisitor.mEvent->PropagationStopped()) {
323       return;
324     }
325     if (aVisitor.mEvent->mFlags.mOnlySystemGroupDispatch &&
326         !aVisitor.mEvent->mFlags.mInSystemGroup) {
327       return;
328     }
329     if (aVisitor.mEvent->mFlags.mOnlySystemGroupDispatchInContent &&
330         !aVisitor.mEvent->mFlags.mInSystemGroup && !IsCurrentTargetChrome()) {
331       return;
332     }
333     if (!mManager) {
334       if (!MayHaveListenerManager() && !aCd.MayHaveNewListenerManager()) {
335         return;
336       }
337       mManager = mTarget->GetExistingListenerManager();
338     }
339     if (mManager) {
340       NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr,
341                    "CurrentTarget should be null!");
342 
343       if (aVisitor.mEvent->mMessage == eMouseClick) {
344         aVisitor.mEvent->mFlags.mHadNonPrivilegedClickListeners =
345             aVisitor.mEvent->mFlags.mHadNonPrivilegedClickListeners ||
346             mManager->HasNonPrivilegedClickListeners();
347       }
348       mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent,
349                             &aVisitor.mDOMEvent, CurrentTarget(),
350                             &aVisitor.mEventStatus, IsItemInShadowTree());
351       NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr,
352                    "CurrentTarget should be null!");
353     }
354   }
355 
356   /**
357    * Copies mItemFlags and mItemData to aVisitor and calls PostHandleEvent.
358    */
359   MOZ_CAN_RUN_SCRIPT void PostHandleEvent(EventChainPostVisitor& aVisitor);
360 
361  private:
362   const nsCOMPtr<EventTarget> mTarget;
363   nsCOMPtr<EventTarget> mRetargetedRelatedTarget;
364   Maybe<nsTArray<RefPtr<EventTarget>>> mRetargetedTouchTargets;
365   Maybe<nsTArray<RefPtr<dom::Touch>>> mInitialTargetTouches;
366 
367   class EventTargetChainFlags {
368    public:
EventTargetChainFlags()369     explicit EventTargetChainFlags() { SetRawFlags(0); }
370     // Cached flags for each EventTargetChainItem which are set when calling
371     // GetEventTargetParent to create event target chain. They are used to
372     // manage or speedup event dispatching.
373     bool mForceContentDispatch : 1;
374     bool mWantsWillHandleEvent : 1;
375     bool mMayHaveManager : 1;
376     bool mChechedIfChrome : 1;
377     bool mIsChromeContent : 1;
378     bool mWantsPreHandleEvent : 1;
379     bool mPreHandleEventOnly : 1;
380     bool mRootOfClosedTree : 1;
381     bool mItemInShadowTree : 1;
382     bool mIsSlotInClosedTree : 1;
383     bool mIsChromeHandler : 1;
384 
385    private:
386     typedef uint32_t RawFlags;
SetRawFlags(RawFlags aRawFlags)387     void SetRawFlags(RawFlags aRawFlags) {
388       static_assert(
389           sizeof(EventTargetChainFlags) <= sizeof(RawFlags),
390           "EventTargetChainFlags must not be bigger than the RawFlags");
391       memcpy(this, &aRawFlags, sizeof(EventTargetChainFlags));
392     }
393   } mFlags;
394 
395   uint16_t mItemFlags;
396   nsCOMPtr<nsISupports> mItemData;
397   // Event retargeting must happen whenever mNewTarget is non-null.
398   nsCOMPtr<EventTarget> mNewTarget;
399   // Cache mTarget's event listener manager.
400   RefPtr<EventListenerManager> mManager;
401 
IsCurrentTargetChrome()402   bool IsCurrentTargetChrome() {
403     if (!mFlags.mChechedIfChrome) {
404       mFlags.mChechedIfChrome = true;
405       if (IsEventTargetChrome(mTarget)) {
406         mFlags.mIsChromeContent = true;
407       }
408     }
409     return mFlags.mIsChromeContent;
410   }
411 };
412 
GetEventTargetParent(EventChainPreVisitor & aVisitor)413 void EventTargetChainItem::GetEventTargetParent(
414     EventChainPreVisitor& aVisitor) {
415   aVisitor.Reset();
416   mTarget->GetEventTargetParent(aVisitor);
417   SetForceContentDispatch(aVisitor.mForceContentDispatch);
418   SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent);
419   SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager);
420   SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
421   SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
422   SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
423   SetItemInShadowTree(aVisitor.mItemInShadowTree);
424   SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
425   SetRetargetedTouchTarget(std::move(aVisitor.mRetargetedTouchTargets));
426   mItemFlags = aVisitor.mItemFlags;
427   mItemData = aVisitor.mItemData;
428 }
429 
PreHandleEvent(EventChainVisitor & aVisitor)430 void EventTargetChainItem::PreHandleEvent(EventChainVisitor& aVisitor) {
431   if (!WantsPreHandleEvent()) {
432     return;
433   }
434   aVisitor.mItemFlags = mItemFlags;
435   aVisitor.mItemData = mItemData;
436   Unused << mTarget->PreHandleEvent(aVisitor);
437 }
438 
PostHandleEvent(EventChainPostVisitor & aVisitor)439 void EventTargetChainItem::PostHandleEvent(EventChainPostVisitor& aVisitor) {
440   aVisitor.mItemFlags = mItemFlags;
441   aVisitor.mItemData = mItemData;
442   mTarget->PostHandleEvent(aVisitor);
443 }
444 
HandleEventTargetChain(nsTArray<EventTargetChainItem> & aChain,EventChainPostVisitor & aVisitor,EventDispatchingCallback * aCallback,ELMCreationDetector & aCd)445 void EventTargetChainItem::HandleEventTargetChain(
446     nsTArray<EventTargetChainItem>& aChain, EventChainPostVisitor& aVisitor,
447     EventDispatchingCallback* aCallback, ELMCreationDetector& aCd) {
448   // Save the target so that it can be restored later.
449   nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->mTarget;
450   nsCOMPtr<EventTarget> firstRelatedTarget = aVisitor.mEvent->mRelatedTarget;
451   Maybe<AutoTArray<nsCOMPtr<EventTarget>, 10>> firstTouchTargets;
452   WidgetTouchEvent* touchEvent = nullptr;
453   if (aVisitor.mEvent->mClass == eTouchEventClass) {
454     touchEvent = aVisitor.mEvent->AsTouchEvent();
455     if (!aVisitor.mEvent->mFlags.mInSystemGroup) {
456       firstTouchTargets.emplace();
457       WidgetTouchEvent* touchEvent = aVisitor.mEvent->AsTouchEvent();
458       WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
459       for (uint32_t i = 0; i < touches.Length(); ++i) {
460         firstTouchTargets->AppendElement(touches[i]->mTarget);
461       }
462     }
463   }
464 
465   uint32_t chainLength = aChain.Length();
466   uint32_t firstCanHandleEventTargetIdx =
467       EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain);
468 
469   // Capture
470   aVisitor.mEvent->mFlags.mInCapturePhase = true;
471   aVisitor.mEvent->mFlags.mInBubblingPhase = false;
472   for (uint32_t i = chainLength - 1; i > firstCanHandleEventTargetIdx; --i) {
473     EventTargetChainItem& item = aChain[i];
474     if (item.PreHandleEventOnly()) {
475       continue;
476     }
477     if ((!aVisitor.mEvent->mFlags.mNoContentDispatch ||
478          item.ForceContentDispatch()) &&
479         !aVisitor.mEvent->PropagationStopped()) {
480       item.HandleEvent(aVisitor, aCd);
481     }
482 
483     if (item.GetNewTarget()) {
484       // item is at anonymous boundary. Need to retarget for the child items.
485       for (uint32_t j = i; j > 0; --j) {
486         uint32_t childIndex = j - 1;
487         EventTarget* newTarget = aChain[childIndex].GetNewTarget();
488         if (newTarget) {
489           aVisitor.mEvent->mTarget = newTarget;
490           break;
491         }
492       }
493     }
494 
495     // https://dom.spec.whatwg.org/#dispatching-events
496     // Step 14.2
497     // "Set event's relatedTarget to tuple's relatedTarget."
498     // Note, the initial retargeting was done already when creating
499     // event target chain, so we need to do this only after calling
500     // HandleEvent, not before, like in the specification.
501     if (item.GetRetargetedRelatedTarget()) {
502       bool found = false;
503       for (uint32_t j = i; j > 0; --j) {
504         uint32_t childIndex = j - 1;
505         EventTarget* relatedTarget =
506             aChain[childIndex].GetRetargetedRelatedTarget();
507         if (relatedTarget) {
508           found = true;
509           aVisitor.mEvent->mRelatedTarget = relatedTarget;
510           break;
511         }
512       }
513       if (!found) {
514         aVisitor.mEvent->mRelatedTarget =
515             aVisitor.mEvent->mOriginalRelatedTarget;
516       }
517     }
518 
519     if (item.HasRetargetTouchTargets()) {
520       bool found = false;
521       for (uint32_t j = i; j > 0; --j) {
522         uint32_t childIndex = j - 1;
523         if (aChain[childIndex].HasRetargetTouchTargets()) {
524           found = true;
525           aChain[childIndex].RetargetTouchTargets(touchEvent,
526                                                   aVisitor.mDOMEvent);
527           break;
528         }
529       }
530       if (!found) {
531         WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
532         for (uint32_t i = 0; i < touches.Length(); ++i) {
533           touches[i]->mTarget = touches[i]->mOriginalTarget;
534         }
535       }
536     }
537   }
538 
539   // Target
540   aVisitor.mEvent->mFlags.mInBubblingPhase = true;
541   EventTargetChainItem& targetItem = aChain[firstCanHandleEventTargetIdx];
542   // Need to explicitly retarget touch targets so that initial targets get set
543   // properly in case nothing else retargeted touches.
544   if (targetItem.HasRetargetTouchTargets()) {
545     targetItem.RetargetTouchTargets(touchEvent, aVisitor.mDOMEvent);
546   }
547   if (!aVisitor.mEvent->PropagationStopped() &&
548       (!aVisitor.mEvent->mFlags.mNoContentDispatch ||
549        targetItem.ForceContentDispatch())) {
550     targetItem.HandleEvent(aVisitor, aCd);
551   }
552   if (aVisitor.mEvent->mFlags.mInSystemGroup) {
553     targetItem.PostHandleEvent(aVisitor);
554   }
555 
556   // Bubble
557   aVisitor.mEvent->mFlags.mInCapturePhase = false;
558   for (uint32_t i = firstCanHandleEventTargetIdx + 1; i < chainLength; ++i) {
559     EventTargetChainItem& item = aChain[i];
560     if (item.PreHandleEventOnly()) {
561       continue;
562     }
563     EventTarget* newTarget = item.GetNewTarget();
564     if (newTarget) {
565       // Item is at anonymous boundary. Need to retarget for the current item
566       // and for parent items.
567       aVisitor.mEvent->mTarget = newTarget;
568     }
569 
570     // https://dom.spec.whatwg.org/#dispatching-events
571     // Step 15.2
572     // "Set event's relatedTarget to tuple's relatedTarget."
573     EventTarget* relatedTarget = item.GetRetargetedRelatedTarget();
574     if (relatedTarget) {
575       aVisitor.mEvent->mRelatedTarget = relatedTarget;
576     }
577 
578     if (item.HasRetargetTouchTargets()) {
579       item.RetargetTouchTargets(touchEvent, aVisitor.mDOMEvent);
580     }
581 
582     if (aVisitor.mEvent->mFlags.mBubbles || newTarget) {
583       if ((!aVisitor.mEvent->mFlags.mNoContentDispatch ||
584            item.ForceContentDispatch()) &&
585           !aVisitor.mEvent->PropagationStopped()) {
586         item.HandleEvent(aVisitor, aCd);
587       }
588       if (aVisitor.mEvent->mFlags.mInSystemGroup) {
589         item.PostHandleEvent(aVisitor);
590       }
591     }
592   }
593   aVisitor.mEvent->mFlags.mInBubblingPhase = false;
594 
595   if (!aVisitor.mEvent->mFlags.mInSystemGroup &&
596       aVisitor.mEvent->IsAllowedToDispatchInSystemGroup()) {
597     // Dispatch to the system event group.  Make sure to clear the
598     // STOP_DISPATCH flag since this resets for each event group.
599     aVisitor.mEvent->mFlags.mPropagationStopped = false;
600     aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false;
601 
602     // Setting back the original target of the event.
603     aVisitor.mEvent->mTarget = aVisitor.mEvent->mOriginalTarget;
604     aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
605     if (firstTouchTargets) {
606       WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
607       for (uint32_t i = 0; i < touches.Length(); ++i) {
608         touches[i]->mTarget = touches[i]->mOriginalTarget;
609       }
610     }
611 
612     // Special handling if PresShell (or some other caller)
613     // used a callback object.
614     if (aCallback) {
615       aCallback->HandleEvent(aVisitor);
616     }
617 
618     // Retarget for system event group (which does the default handling too).
619     // Setting back the target which was used also for default event group.
620     aVisitor.mEvent->mTarget = firstTarget;
621     aVisitor.mEvent->mRelatedTarget = firstRelatedTarget;
622     if (firstTouchTargets) {
623       WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
624       for (uint32_t i = 0; i < firstTouchTargets->Length(); ++i) {
625         touches[i]->mTarget = firstTouchTargets->ElementAt(i);
626       }
627     }
628 
629     aVisitor.mEvent->mFlags.mInSystemGroup = true;
630     HandleEventTargetChain(aChain, aVisitor, aCallback, aCd);
631     aVisitor.mEvent->mFlags.mInSystemGroup = false;
632 
633     // After dispatch, clear all the propagation flags so that
634     // system group listeners don't affect to the event.
635     aVisitor.mEvent->mFlags.mPropagationStopped = false;
636     aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false;
637   }
638 }
639 
640 static nsTArray<EventTargetChainItem>* sCachedMainThreadChain = nullptr;
641 
642 /* static */
Shutdown()643 void EventDispatcher::Shutdown() {
644   delete sCachedMainThreadChain;
645   sCachedMainThreadChain = nullptr;
646 }
647 
EventTargetChainItemForChromeTarget(nsTArray<EventTargetChainItem> & aChain,nsINode * aNode,EventTargetChainItem * aChild=nullptr)648 EventTargetChainItem* EventTargetChainItemForChromeTarget(
649     nsTArray<EventTargetChainItem>& aChain, nsINode* aNode,
650     EventTargetChainItem* aChild = nullptr) {
651   if (!aNode->IsInComposedDoc()) {
652     return nullptr;
653   }
654   nsPIDOMWindowInner* win = aNode->OwnerDoc()->GetInnerWindow();
655   EventTarget* piTarget = win ? win->GetParentTarget() : nullptr;
656   NS_ENSURE_TRUE(piTarget, nullptr);
657 
658   EventTargetChainItem* etci = EventTargetChainItem::Create(
659       aChain, piTarget->GetTargetForEventTargetChain(), aChild);
660   if (!etci->IsValid()) {
661     EventTargetChainItem::DestroyLast(aChain, etci);
662     return nullptr;
663   }
664   return etci;
665 }
666 
MayRetargetToChromeIfCanNotHandleEvent(nsTArray<EventTargetChainItem> & aChain,EventChainPreVisitor & aPreVisitor,EventTargetChainItem * aTargetEtci,EventTargetChainItem * aChildEtci,nsINode * aContent)667 /* static */ EventTargetChainItem* MayRetargetToChromeIfCanNotHandleEvent(
668     nsTArray<EventTargetChainItem>& aChain, EventChainPreVisitor& aPreVisitor,
669     EventTargetChainItem* aTargetEtci, EventTargetChainItem* aChildEtci,
670     nsINode* aContent) {
671   if (!aPreVisitor.mWantsPreHandleEvent) {
672     // Keep EventTargetChainItem if we need to call PreHandleEvent on it.
673     EventTargetChainItem::DestroyLast(aChain, aTargetEtci);
674   }
675   if (aPreVisitor.mAutomaticChromeDispatch && aContent) {
676     aPreVisitor.mRelatedTargetRetargetedInCurrentScope = false;
677     // Event target couldn't handle the event. Try to propagate to chrome.
678     EventTargetChainItem* chromeTargetEtci =
679         EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci);
680     if (chromeTargetEtci) {
681       // If we propagate to chrome, need to ensure we mark
682       // EventTargetChainItem to be chrome handler so that event.composedPath()
683       // can return the right value.
684       chromeTargetEtci->SetIsChromeHandler(true);
685       chromeTargetEtci->GetEventTargetParent(aPreVisitor);
686       return chromeTargetEtci;
687     }
688   }
689   return nullptr;
690 }
691 
ShouldClearTargets(WidgetEvent * aEvent)692 static bool ShouldClearTargets(WidgetEvent* aEvent) {
693   nsCOMPtr<nsIContent> finalTarget;
694   nsCOMPtr<nsIContent> finalRelatedTarget;
695   if ((finalTarget = do_QueryInterface(aEvent->mTarget)) &&
696       finalTarget->SubtreeRoot()->IsShadowRoot()) {
697     return true;
698   }
699 
700   if ((finalRelatedTarget = do_QueryInterface(aEvent->mRelatedTarget)) &&
701       finalRelatedTarget->SubtreeRoot()->IsShadowRoot()) {
702     return true;
703   }
704   // XXXsmaug Check also all the touch objects.
705 
706   return false;
707 }
708 
709 /* static */
Dispatch(nsISupports * aTarget,nsPresContext * aPresContext,WidgetEvent * aEvent,Event * aDOMEvent,nsEventStatus * aEventStatus,EventDispatchingCallback * aCallback,nsTArray<EventTarget * > * aTargets)710 nsresult EventDispatcher::Dispatch(nsISupports* aTarget,
711                                    nsPresContext* aPresContext,
712                                    WidgetEvent* aEvent, Event* aDOMEvent,
713                                    nsEventStatus* aEventStatus,
714                                    EventDispatchingCallback* aCallback,
715                                    nsTArray<EventTarget*>* aTargets) {
716   AUTO_PROFILER_LABEL("EventDispatcher::Dispatch", OTHER);
717 
718   NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!");
719   NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched,
720                  NS_ERROR_DOM_INVALID_STATE_ERR);
721   NS_ASSERTION(!aTargets || !aEvent->mMessage, "Wrong parameters!");
722 
723   // If we're dispatching an already created DOMEvent object, make
724   // sure it is initialized!
725   // If aTargets is non-null, the event isn't going to be dispatched.
726   NS_ENSURE_TRUE(aEvent->mMessage || !aDOMEvent || aTargets,
727                  NS_ERROR_DOM_INVALID_STATE_ERR);
728 
729   // Events shall not be fired while we are in stable state to prevent anything
730   // visible from the scripts.
731   MOZ_ASSERT(!nsContentUtils::IsInStableOrMetaStableState());
732   NS_ENSURE_TRUE(!nsContentUtils::IsInStableOrMetaStableState(),
733                  NS_ERROR_DOM_INVALID_STATE_ERR);
734 
735   nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
736 
737   RefPtr<PerformanceEventTiming> eventTimingEntry;
738   // Similar to PerformancePaintTiming, we don't need to
739   // expose them for printing documents
740   if (aPresContext && !aPresContext->IsPrintingOrPrintPreview()) {
741     eventTimingEntry =
742         PerformanceEventTiming::TryGenerateEventTiming(target, aEvent);
743   }
744 
745   bool retargeted = false;
746 
747   if (aEvent->mFlags.mRetargetToNonNativeAnonymous) {
748     nsCOMPtr<nsIContent> content = do_QueryInterface(target);
749     if (content && content->IsInNativeAnonymousSubtree()) {
750       nsCOMPtr<EventTarget> newTarget =
751           content->FindFirstNonChromeOnlyAccessContent();
752       NS_ENSURE_STATE(newTarget);
753 
754       aEvent->mOriginalTarget = target;
755       target = newTarget;
756       retargeted = true;
757     }
758   }
759 
760   if (aEvent->mFlags.mOnlyChromeDispatch) {
761     nsCOMPtr<Document> doc;
762     if (!IsEventTargetChrome(target, getter_AddRefs(doc)) && doc) {
763       nsPIDOMWindowInner* win = doc->GetInnerWindow();
764       // If we can't dispatch the event to chrome, do nothing.
765       EventTarget* piTarget = win ? win->GetParentTarget() : nullptr;
766       if (!piTarget) {
767         return NS_OK;
768       }
769 
770       // Set the target to be the original dispatch target,
771       aEvent->mTarget = target;
772       // but use chrome event handler or BrowserChildMessageManager for event
773       // target chain.
774       target = piTarget;
775     } else if (NS_WARN_IF(!doc)) {
776       return NS_ERROR_UNEXPECTED;
777     }
778   }
779 
780 #ifdef DEBUG
781   if (NS_IsMainThread() && aEvent->mMessage != eVoidEvent &&
782       !nsContentUtils::IsSafeToRunScript()) {
783     static const auto warn = [](bool aIsSystem) {
784       if (aIsSystem) {
785         NS_WARNING("Fix the caller!");
786       } else {
787         MOZ_CRASH("This is unsafe! Fix the caller!");
788       }
789     };
790     if (nsCOMPtr<nsINode> node = do_QueryInterface(target)) {
791       // If this is a node, it's possible that this is some sort of DOM tree
792       // that is never accessed by script (for example an SVG image or XBL
793       // binding document or whatnot).  We really only want to warn/assert here
794       // if there might be actual scripted listeners for this event, so restrict
795       // the warnings/asserts to the case when script can or once could touch
796       // this node's document.
797       Document* doc = node->OwnerDoc();
798       bool hasHadScriptHandlingObject;
799       nsIGlobalObject* global =
800           doc->GetScriptHandlingObject(hasHadScriptHandlingObject);
801       if (global || hasHadScriptHandlingObject) {
802         warn(nsContentUtils::IsChromeDoc(doc));
803       }
804     } else if (nsCOMPtr<nsIGlobalObject> global = target->GetOwnerGlobal()) {
805       warn(global->PrincipalOrNull()->IsSystemPrincipal());
806     }
807   }
808 
809   if (aDOMEvent) {
810     WidgetEvent* innerEvent = aDOMEvent->WidgetEventPtr();
811     NS_ASSERTION(innerEvent == aEvent,
812                  "The inner event of aDOMEvent is not the same as aEvent!");
813   }
814 #endif
815 
816   nsresult rv = NS_OK;
817   bool externalDOMEvent = !!(aDOMEvent);
818 
819   // If we have a PresContext, make sure it doesn't die before
820   // event dispatching is finished.
821   RefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
822 
823   ELMCreationDetector cd;
824   nsTArray<EventTargetChainItem> chain;
825   if (cd.IsMainThread()) {
826     if (!sCachedMainThreadChain) {
827       sCachedMainThreadChain = new nsTArray<EventTargetChainItem>();
828     }
829     chain = std::move(*sCachedMainThreadChain);
830     chain.SetCapacity(128);
831   }
832 
833   // Create the event target chain item for the event target.
834   EventTargetChainItem* targetEtci = EventTargetChainItem::Create(
835       chain, target->GetTargetForEventTargetChain());
836   MOZ_ASSERT(&chain[0] == targetEtci);
837   if (!targetEtci->IsValid()) {
838     EventTargetChainItem::DestroyLast(chain, targetEtci);
839     return NS_ERROR_FAILURE;
840   }
841 
842   // Make sure that Event::target and Event::originalTarget
843   // point to the last item in the chain.
844   if (!aEvent->mTarget) {
845     // Note, CurrentTarget() points always to the object returned by
846     // GetTargetForEventTargetChain().
847     aEvent->mTarget = targetEtci->CurrentTarget();
848   } else {
849     // XXX But if the target is already set, use that. This is a hack
850     //     for the 'load', 'beforeunload' and 'unload' events,
851     //     which are dispatched to |window| but have document as their target.
852     //
853     // Make sure that the event target points to the right object.
854     aEvent->mTarget = aEvent->mTarget->GetTargetForEventTargetChain();
855     NS_ENSURE_STATE(aEvent->mTarget);
856   }
857 
858   if (retargeted) {
859     aEvent->mOriginalTarget =
860         aEvent->mOriginalTarget->GetTargetForEventTargetChain();
861     NS_ENSURE_STATE(aEvent->mOriginalTarget);
862   } else {
863     aEvent->mOriginalTarget = aEvent->mTarget;
864   }
865 
866   aEvent->mOriginalRelatedTarget = aEvent->mRelatedTarget;
867 
868   bool clearTargets = false;
869 
870   nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->mOriginalTarget);
871   bool isInAnon = content && content->IsInNativeAnonymousSubtree();
872 
873   aEvent->mFlags.mIsBeingDispatched = true;
874 
875   // Create visitor object and start event dispatching.
876   // GetEventTargetParent for the original target.
877   nsEventStatus status = aDOMEvent && aDOMEvent->DefaultPrevented()
878                              ? nsEventStatus_eConsumeNoDefault
879                          : aEventStatus ? *aEventStatus
880                                         : nsEventStatus_eIgnore;
881   nsCOMPtr<EventTarget> targetForPreVisitor = aEvent->mTarget;
882   EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
883                                   isInAnon, targetForPreVisitor);
884   targetEtci->GetEventTargetParent(preVisitor);
885 
886   if (!preVisitor.mCanHandle) {
887     targetEtci = MayRetargetToChromeIfCanNotHandleEvent(
888         chain, preVisitor, targetEtci, nullptr, content);
889   }
890   if (!preVisitor.mCanHandle) {
891     // The original target and chrome target (mAutomaticChromeDispatch=true)
892     // can not handle the event but we still have to call their PreHandleEvent.
893     for (uint32_t i = 0; i < chain.Length(); ++i) {
894       chain[i].PreHandleEvent(preVisitor);
895     }
896 
897     clearTargets = ShouldClearTargets(aEvent);
898   } else {
899     // At least the original target can handle the event.
900     // Setting the retarget to the |target| simplifies retargeting code.
901     nsCOMPtr<EventTarget> t = aEvent->mTarget;
902     targetEtci->SetNewTarget(t);
903     // In order to not change the targetTouches array passed to TouchEvents
904     // when dispatching events from JS, we need to store the initial Touch
905     // objects on the list.
906     if (aEvent->mClass == eTouchEventClass && aDOMEvent) {
907       TouchEvent* touchEvent = static_cast<TouchEvent*>(aDOMEvent);
908       TouchList* targetTouches = touchEvent->GetExistingTargetTouches();
909       if (targetTouches) {
910         Maybe<nsTArray<RefPtr<dom::Touch>>> initialTargetTouches;
911         initialTargetTouches.emplace();
912         for (uint32_t i = 0; i < targetTouches->Length(); ++i) {
913           initialTargetTouches->AppendElement(targetTouches->Item(i));
914         }
915         targetEtci->SetInitialTargetTouches(std::move(initialTargetTouches));
916         targetTouches->Clear();
917       }
918     }
919     EventTargetChainItem* topEtci = targetEtci;
920     targetEtci = nullptr;
921     while (preVisitor.GetParentTarget()) {
922       EventTarget* parentTarget = preVisitor.GetParentTarget();
923       EventTargetChainItem* parentEtci =
924           EventTargetChainItem::Create(chain, parentTarget, topEtci);
925       if (!parentEtci->IsValid()) {
926         EventTargetChainItem::DestroyLast(chain, parentEtci);
927         rv = NS_ERROR_FAILURE;
928         break;
929       }
930 
931       parentEtci->SetIsSlotInClosedTree(preVisitor.mParentIsSlotInClosedTree);
932       parentEtci->SetIsChromeHandler(preVisitor.mParentIsChromeHandler);
933 
934       // Item needs event retargetting.
935       if (preVisitor.mEventTargetAtParent) {
936         // Need to set the target of the event
937         // so that also the next retargeting works.
938         preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
939         preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
940         parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
941       }
942 
943       if (preVisitor.mRetargetedRelatedTarget) {
944         preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget;
945       }
946 
947       parentEtci->GetEventTargetParent(preVisitor);
948       if (preVisitor.mCanHandle) {
949         preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
950         topEtci = parentEtci;
951       } else {
952         bool ignoreBecauseOfShadowDOM = preVisitor.mIgnoreBecauseOfShadowDOM;
953         nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget);
954         parentEtci = MayRetargetToChromeIfCanNotHandleEvent(
955             chain, preVisitor, parentEtci, topEtci, disabledTarget);
956         if (parentEtci && preVisitor.mCanHandle) {
957           preVisitor.mTargetInKnownToBeHandledScope =
958               preVisitor.mEvent->mTarget;
959           EventTargetChainItem* item =
960               EventTargetChainItem::GetFirstCanHandleEventTarget(chain);
961           if (!ignoreBecauseOfShadowDOM) {
962             // If we ignored the target because of Shadow DOM retargeting, we
963             // shouldn't treat the target to be in the event path at all.
964             item->SetNewTarget(parentTarget);
965           }
966           topEtci = parentEtci;
967           continue;
968         }
969         break;
970       }
971     }
972     if (NS_SUCCEEDED(rv)) {
973       if (aTargets) {
974         aTargets->Clear();
975         uint32_t numTargets = chain.Length();
976         EventTarget** targets = aTargets->AppendElements(numTargets);
977         for (uint32_t i = 0; i < numTargets; ++i) {
978           targets[i] = chain[i].CurrentTarget()->GetTargetForDOMEvent();
979         }
980       } else {
981         // Event target chain is created. PreHandle the chain.
982         for (uint32_t i = 0; i < chain.Length(); ++i) {
983           chain[i].PreHandleEvent(preVisitor);
984         }
985 
986         RefPtr<nsRefreshDriver> refreshDriver;
987         if (aPresContext && aPresContext->GetRootPresContext() &&
988             aEvent->IsTrusted() &&
989             (aEvent->mMessage == eKeyPress ||
990              aEvent->mMessage == eMouseClick)) {
991           refreshDriver = aPresContext->GetRootPresContext()->RefreshDriver();
992           if (refreshDriver) {
993             refreshDriver->EnterUserInputProcessing();
994           }
995         }
996         auto cleanup = MakeScopeExit([&] {
997           if (refreshDriver) {
998             refreshDriver->ExitUserInputProcessing();
999           }
1000         });
1001 
1002         clearTargets = ShouldClearTargets(aEvent);
1003 
1004         // Handle the chain.
1005         EventChainPostVisitor postVisitor(preVisitor);
1006         MOZ_RELEASE_ASSERT(!aEvent->mPath);
1007         aEvent->mPath = &chain;
1008 
1009         if (profiler_is_active()) {
1010           // Add a profiler label and a profiler marker for the actual
1011           // dispatch of the event.
1012           // This is a very hot code path, so we need to make sure not to
1013           // do this extra work when we're not profiling.
1014           if (!postVisitor.mDOMEvent) {
1015             // This is tiny bit slow, but happens only once per event.
1016             // Similar code also in EventListenerManager.
1017             nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget;
1018             RefPtr<Event> event =
1019                 EventDispatcher::CreateEvent(et, aPresContext, aEvent, u""_ns);
1020             event.swap(postVisitor.mDOMEvent);
1021           }
1022           nsAutoString typeStr;
1023           postVisitor.mDOMEvent->GetType(typeStr);
1024           AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
1025               "EventDispatcher::Dispatch", OTHER, typeStr);
1026 
1027           nsCOMPtr<nsIDocShell> docShell;
1028           docShell = nsContentUtils::GetDocShellForEventTarget(aEvent->mTarget);
1029           MarkerInnerWindowId innerWindowId;
1030           if (nsCOMPtr<nsPIDOMWindowInner> inner =
1031                   do_QueryInterface(aEvent->mTarget->GetOwnerGlobal())) {
1032             innerWindowId = MarkerInnerWindowId{inner->WindowID()};
1033           }
1034 
1035           struct DOMEventMarker {
1036             static constexpr Span<const char> MarkerTypeName() {
1037               return MakeStringSpan("DOMEvent");
1038             }
1039             static void StreamJSONMarkerData(
1040                 baseprofiler::SpliceableJSONWriter& aWriter,
1041                 const ProfilerString16View& aEventType,
1042                 const TimeStamp& aStartTime, const TimeStamp& aEventTimeStamp) {
1043               aWriter.StringProperty(
1044                   "eventType", NS_ConvertUTF16toUTF8(aEventType.Data(),
1045                                                      aEventType.Length()));
1046               // This is the event processing latency, which is the time from
1047               // when the event was created, to when it was started to be
1048               // processed. Note that the computation of this latency is
1049               // deferred until serialization time, at the expense of some extra
1050               // memory.
1051               aWriter.DoubleProperty(
1052                   "latency", (aStartTime - aEventTimeStamp).ToMilliseconds());
1053             }
1054             static MarkerSchema MarkerTypeDisplay() {
1055               using MS = MarkerSchema;
1056               MS schema{MS::Location::markerChart, MS::Location::markerTable,
1057                         MS::Location::timelineOverview};
1058               schema.SetChartLabel("{marker.data.eventType}");
1059               schema.SetTooltipLabel("{marker.data.eventType} - DOMEvent");
1060               schema.SetTableLabel("{marker.data.eventType}");
1061               schema.AddKeyLabelFormat("latency", "Latency",
1062                                        MS::Format::duration);
1063               return schema;
1064             }
1065           };
1066 
1067           auto startTime = TimeStamp::NowUnfuzzed();
1068           profiler_add_marker("DOMEvent", geckoprofiler::category::DOM,
1069                               {MarkerTiming::IntervalStart(),
1070                                MarkerInnerWindowId(innerWindowId)},
1071                               DOMEventMarker{}, typeStr, startTime,
1072                               aEvent->mTimeStamp);
1073 
1074           EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
1075                                                        aCallback, cd);
1076 
1077           profiler_add_marker(
1078               "DOMEvent", geckoprofiler::category::DOM,
1079               {MarkerTiming::IntervalEnd(), std::move(innerWindowId)},
1080               DOMEventMarker{}, typeStr, startTime, aEvent->mTimeStamp);
1081         } else {
1082           EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
1083                                                        aCallback, cd);
1084         }
1085         aEvent->mPath = nullptr;
1086 
1087         if (aPresContext && aPresContext->GetRootPresContext() &&
1088             aEvent->IsTrusted()) {
1089           nsRefreshDriver* driver =
1090               aPresContext->GetRootPresContext()->RefreshDriver();
1091           if (driver && driver->HasPendingTick()) {
1092             switch (aEvent->mMessage) {
1093               case eKeyPress:
1094                 driver->RegisterCompositionPayload(
1095                     {layers::CompositionPayloadType::eKeyPress,
1096                      aEvent->mTimeStamp});
1097                 break;
1098               case eMouseClick: {
1099                 if (aEvent->AsMouseEvent()->mInputSource ==
1100                         MouseEvent_Binding::MOZ_SOURCE_MOUSE ||
1101                     aEvent->AsMouseEvent()->mInputSource ==
1102                         MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
1103                   driver->RegisterCompositionPayload(
1104                       {layers::CompositionPayloadType::eMouseUpFollowedByClick,
1105                        aEvent->mTimeStamp});
1106                 }
1107                 break;
1108               }
1109               default:
1110                 break;
1111             }
1112           }
1113         }
1114 
1115         preVisitor.mEventStatus = postVisitor.mEventStatus;
1116         // If the DOM event was created during event flow.
1117         if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
1118           preVisitor.mDOMEvent = postVisitor.mDOMEvent;
1119         }
1120       }
1121     }
1122   }
1123 
1124   // Note, EventTargetChainItem objects are deleted when the chain goes out of
1125   // the scope.
1126 
1127   aEvent->mFlags.mIsBeingDispatched = false;
1128   aEvent->mFlags.mDispatchedAtLeastOnce = true;
1129 
1130   if (eventTimingEntry) {
1131     eventTimingEntry->FinalizeEventTiming(aEvent->mTarget);
1132   }
1133   // https://dom.spec.whatwg.org/#concept-event-dispatch
1134   // step 10. If clearTargets, then:
1135   //          1. Set event's target to null.
1136   //          2. Set event's relatedTarget to null.
1137   //          3. Set event's touch target list to the empty list.
1138   if (clearTargets) {
1139     aEvent->mTarget = nullptr;
1140     aEvent->mOriginalTarget = nullptr;
1141     aEvent->mRelatedTarget = nullptr;
1142     aEvent->mOriginalRelatedTarget = nullptr;
1143     // XXXsmaug Check also all the touch objects.
1144   }
1145 
1146   if (!externalDOMEvent && preVisitor.mDOMEvent) {
1147     // A dom::Event was created while dispatching the event.
1148     // Duplicate private data if someone holds a pointer to it.
1149     nsrefcnt rc = 0;
1150     NS_RELEASE2(preVisitor.mDOMEvent, rc);
1151     if (preVisitor.mDOMEvent) {
1152       preVisitor.mDOMEvent->DuplicatePrivateData();
1153     }
1154   }
1155 
1156   if (aEventStatus) {
1157     *aEventStatus = preVisitor.mEventStatus;
1158   }
1159 
1160   if (cd.IsMainThread() && chain.Capacity() == 128 && sCachedMainThreadChain) {
1161     chain.ClearAndRetainStorage();
1162     chain.SwapElements(*sCachedMainThreadChain);
1163   }
1164 
1165   return rv;
1166 }
1167 
1168 /* static */
DispatchDOMEvent(nsISupports * aTarget,WidgetEvent * aEvent,Event * aDOMEvent,nsPresContext * aPresContext,nsEventStatus * aEventStatus)1169 nsresult EventDispatcher::DispatchDOMEvent(nsISupports* aTarget,
1170                                            WidgetEvent* aEvent,
1171                                            Event* aDOMEvent,
1172                                            nsPresContext* aPresContext,
1173                                            nsEventStatus* aEventStatus) {
1174   if (aDOMEvent) {
1175     WidgetEvent* innerEvent = aDOMEvent->WidgetEventPtr();
1176     NS_ENSURE_TRUE(innerEvent, NS_ERROR_ILLEGAL_VALUE);
1177 
1178     // Don't modify the event if it's being dispatched right now.
1179     if (innerEvent->mFlags.mIsBeingDispatched) {
1180       return NS_ERROR_DOM_INVALID_STATE_ERR;
1181     }
1182 
1183     bool dontResetTrusted = false;
1184     if (innerEvent->mFlags.mDispatchedAtLeastOnce) {
1185       innerEvent->mTarget = nullptr;
1186       innerEvent->mOriginalTarget = nullptr;
1187     } else {
1188       dontResetTrusted = aDOMEvent->IsTrusted();
1189     }
1190 
1191     if (!dontResetTrusted) {
1192       // Check security state to determine if dispatcher is trusted
1193       bool trusted = NS_IsMainThread()
1194                          ? nsContentUtils::LegacyIsCallerChromeOrNativeCode()
1195                          : IsCurrentThreadRunningChromeWorker();
1196       aDOMEvent->SetTrusted(trusted);
1197     }
1198 
1199     return EventDispatcher::Dispatch(aTarget, aPresContext, innerEvent,
1200                                      aDOMEvent, aEventStatus);
1201   } else if (aEvent) {
1202     return EventDispatcher::Dispatch(aTarget, aPresContext, aEvent, aDOMEvent,
1203                                      aEventStatus);
1204   }
1205   return NS_ERROR_ILLEGAL_VALUE;
1206 }
1207 
CreateEvent(EventTarget * aOwner,nsPresContext * aPresContext,WidgetEvent * aEvent,const nsAString & aEventType,CallerType aCallerType)1208 /* static */ already_AddRefed<dom::Event> EventDispatcher::CreateEvent(
1209     EventTarget* aOwner, nsPresContext* aPresContext, WidgetEvent* aEvent,
1210     const nsAString& aEventType, CallerType aCallerType) {
1211   if (aEvent) {
1212     switch (aEvent->mClass) {
1213       case eMutationEventClass:
1214         return NS_NewDOMMutationEvent(aOwner, aPresContext,
1215                                       aEvent->AsMutationEvent());
1216       case eGUIEventClass:
1217       case eScrollPortEventClass:
1218       case eUIEventClass:
1219         return NS_NewDOMUIEvent(aOwner, aPresContext, aEvent->AsGUIEvent());
1220       case eScrollAreaEventClass:
1221         return NS_NewDOMScrollAreaEvent(aOwner, aPresContext,
1222                                         aEvent->AsScrollAreaEvent());
1223       case eKeyboardEventClass:
1224         return NS_NewDOMKeyboardEvent(aOwner, aPresContext,
1225                                       aEvent->AsKeyboardEvent());
1226       case eCompositionEventClass:
1227         return NS_NewDOMCompositionEvent(aOwner, aPresContext,
1228                                          aEvent->AsCompositionEvent());
1229       case eMouseEventClass:
1230         return NS_NewDOMMouseEvent(aOwner, aPresContext,
1231                                    aEvent->AsMouseEvent());
1232       case eFocusEventClass:
1233         return NS_NewDOMFocusEvent(aOwner, aPresContext,
1234                                    aEvent->AsFocusEvent());
1235       case eMouseScrollEventClass:
1236         return NS_NewDOMMouseScrollEvent(aOwner, aPresContext,
1237                                          aEvent->AsMouseScrollEvent());
1238       case eWheelEventClass:
1239         return NS_NewDOMWheelEvent(aOwner, aPresContext,
1240                                    aEvent->AsWheelEvent());
1241       case eEditorInputEventClass:
1242         return NS_NewDOMInputEvent(aOwner, aPresContext,
1243                                    aEvent->AsEditorInputEvent());
1244       case eDragEventClass:
1245         return NS_NewDOMDragEvent(aOwner, aPresContext, aEvent->AsDragEvent());
1246       case eClipboardEventClass:
1247         return NS_NewDOMClipboardEvent(aOwner, aPresContext,
1248                                        aEvent->AsClipboardEvent());
1249       case eSMILTimeEventClass:
1250         return NS_NewDOMTimeEvent(aOwner, aPresContext,
1251                                   aEvent->AsSMILTimeEvent());
1252       case eCommandEventClass:
1253         return NS_NewDOMCommandEvent(aOwner, aPresContext,
1254                                      aEvent->AsCommandEvent());
1255       case eSimpleGestureEventClass:
1256         return NS_NewDOMSimpleGestureEvent(aOwner, aPresContext,
1257                                            aEvent->AsSimpleGestureEvent());
1258       case ePointerEventClass:
1259         return NS_NewDOMPointerEvent(aOwner, aPresContext,
1260                                      aEvent->AsPointerEvent());
1261       case eTouchEventClass:
1262         return NS_NewDOMTouchEvent(aOwner, aPresContext,
1263                                    aEvent->AsTouchEvent());
1264       case eTransitionEventClass:
1265         return NS_NewDOMTransitionEvent(aOwner, aPresContext,
1266                                         aEvent->AsTransitionEvent());
1267       case eAnimationEventClass:
1268         return NS_NewDOMAnimationEvent(aOwner, aPresContext,
1269                                        aEvent->AsAnimationEvent());
1270       default:
1271         // For all other types of events, create a vanilla event object.
1272         return NS_NewDOMEvent(aOwner, aPresContext, aEvent);
1273     }
1274   }
1275 
1276   // And if we didn't get an event, check the type argument.
1277 
1278   if (aEventType.LowerCaseEqualsLiteral("mouseevent") ||
1279       aEventType.LowerCaseEqualsLiteral("mouseevents")) {
1280     return NS_NewDOMMouseEvent(aOwner, aPresContext, nullptr);
1281   }
1282   if (aEventType.LowerCaseEqualsLiteral("dragevent")) {
1283     return NS_NewDOMDragEvent(aOwner, aPresContext, nullptr);
1284   }
1285   if (aEventType.LowerCaseEqualsLiteral("keyboardevent")) {
1286     return NS_NewDOMKeyboardEvent(aOwner, aPresContext, nullptr);
1287   }
1288   if (aEventType.LowerCaseEqualsLiteral("compositionevent") ||
1289       aEventType.LowerCaseEqualsLiteral("textevent")) {
1290     return NS_NewDOMCompositionEvent(aOwner, aPresContext, nullptr);
1291   }
1292   if (aEventType.LowerCaseEqualsLiteral("mutationevent") ||
1293       aEventType.LowerCaseEqualsLiteral("mutationevents")) {
1294     return NS_NewDOMMutationEvent(aOwner, aPresContext, nullptr);
1295   }
1296   if (aEventType.LowerCaseEqualsLiteral("deviceorientationevent")) {
1297     DeviceOrientationEventInit init;
1298     RefPtr<Event> event =
1299         DeviceOrientationEvent::Constructor(aOwner, u""_ns, init);
1300     event->MarkUninitialized();
1301     return event.forget();
1302   }
1303   if (aEventType.LowerCaseEqualsLiteral("devicemotionevent")) {
1304     return NS_NewDOMDeviceMotionEvent(aOwner, aPresContext, nullptr);
1305   }
1306   if (aEventType.LowerCaseEqualsLiteral("uievent") ||
1307       aEventType.LowerCaseEqualsLiteral("uievents")) {
1308     return NS_NewDOMUIEvent(aOwner, aPresContext, nullptr);
1309   }
1310   if (aEventType.LowerCaseEqualsLiteral("event") ||
1311       aEventType.LowerCaseEqualsLiteral("events") ||
1312       aEventType.LowerCaseEqualsLiteral("htmlevents") ||
1313       aEventType.LowerCaseEqualsLiteral("svgevents")) {
1314     return NS_NewDOMEvent(aOwner, aPresContext, nullptr);
1315   }
1316   if (aEventType.LowerCaseEqualsLiteral("messageevent")) {
1317     RefPtr<Event> event = new MessageEvent(aOwner, aPresContext, nullptr);
1318     return event.forget();
1319   }
1320   if (aEventType.LowerCaseEqualsLiteral("beforeunloadevent")) {
1321     return NS_NewDOMBeforeUnloadEvent(aOwner, aPresContext, nullptr);
1322   }
1323   if (aEventType.LowerCaseEqualsLiteral("touchevent") &&
1324       TouchEvent::LegacyAPIEnabled(
1325           nsContentUtils::GetDocShellForEventTarget(aOwner),
1326           aCallerType == CallerType::System)) {
1327     return NS_NewDOMTouchEvent(aOwner, aPresContext, nullptr);
1328   }
1329   if (aEventType.LowerCaseEqualsLiteral("hashchangeevent")) {
1330     HashChangeEventInit init;
1331     RefPtr<Event> event = HashChangeEvent::Constructor(aOwner, u""_ns, init);
1332     event->MarkUninitialized();
1333     return event.forget();
1334   }
1335   if (aEventType.LowerCaseEqualsLiteral("customevent")) {
1336     return NS_NewDOMCustomEvent(aOwner, aPresContext, nullptr);
1337   }
1338   if (aEventType.LowerCaseEqualsLiteral("storageevent")) {
1339     RefPtr<Event> event =
1340         StorageEvent::Constructor(aOwner, u""_ns, StorageEventInit());
1341     event->MarkUninitialized();
1342     return event.forget();
1343   }
1344   if (aEventType.LowerCaseEqualsLiteral("focusevent")) {
1345     RefPtr<Event> event = NS_NewDOMFocusEvent(aOwner, aPresContext, nullptr);
1346     event->MarkUninitialized();
1347     return event.forget();
1348   }
1349 
1350   // Only allow these events for chrome
1351   if (aCallerType == CallerType::System) {
1352     if (aEventType.LowerCaseEqualsLiteral("simplegestureevent")) {
1353       return NS_NewDOMSimpleGestureEvent(aOwner, aPresContext, nullptr);
1354     }
1355     if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") ||
1356         aEventType.LowerCaseEqualsLiteral("xulcommandevents")) {
1357       return NS_NewDOMXULCommandEvent(aOwner, aPresContext, nullptr);
1358     }
1359   }
1360 
1361   // NEW EVENT TYPES SHOULD NOT BE ADDED HERE; THEY SHOULD USE ONLY EVENT
1362   // CONSTRUCTORS
1363 
1364   return nullptr;
1365 }
1366 
1367 struct CurrentTargetPathInfo {
1368   uint32_t mIndex;
1369   int32_t mHiddenSubtreeLevel;
1370 };
1371 
TargetPathInfo(const nsTArray<EventTargetChainItem> & aEventPath,const EventTarget & aCurrentTarget)1372 static CurrentTargetPathInfo TargetPathInfo(
1373     const nsTArray<EventTargetChainItem>& aEventPath,
1374     const EventTarget& aCurrentTarget) {
1375   int32_t currentTargetHiddenSubtreeLevel = 0;
1376   for (uint32_t index = aEventPath.Length(); index--;) {
1377     const EventTargetChainItem& item = aEventPath.ElementAt(index);
1378     if (item.PreHandleEventOnly()) {
1379       continue;
1380     }
1381 
1382     if (item.IsRootOfClosedTree()) {
1383       currentTargetHiddenSubtreeLevel++;
1384     }
1385 
1386     if (item.CurrentTarget() == &aCurrentTarget) {
1387       return {index, currentTargetHiddenSubtreeLevel};
1388     }
1389 
1390     if (item.IsSlotInClosedTree()) {
1391       currentTargetHiddenSubtreeLevel--;
1392     }
1393   }
1394   MOZ_ASSERT_UNREACHABLE("No target found?");
1395   return {0, 0};
1396 }
1397 
1398 // https://dom.spec.whatwg.org/#dom-event-composedpath
GetComposedPathFor(WidgetEvent * aEvent,nsTArray<RefPtr<EventTarget>> & aPath)1399 void EventDispatcher::GetComposedPathFor(WidgetEvent* aEvent,
1400                                          nsTArray<RefPtr<EventTarget>>& aPath) {
1401   MOZ_ASSERT(aPath.IsEmpty());
1402   nsTArray<EventTargetChainItem>* path = aEvent->mPath;
1403   if (!path || path->IsEmpty() || !aEvent->mCurrentTarget) {
1404     return;
1405   }
1406 
1407   EventTarget* currentTarget =
1408       aEvent->mCurrentTarget->GetTargetForEventTargetChain();
1409   if (!currentTarget) {
1410     return;
1411   }
1412 
1413   CurrentTargetPathInfo currentTargetInfo =
1414       TargetPathInfo(*path, *currentTarget);
1415 
1416   {
1417     int32_t maxHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1418     int32_t currentHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1419     for (uint32_t index = currentTargetInfo.mIndex; index--;) {
1420       EventTargetChainItem& item = path->ElementAt(index);
1421       if (item.PreHandleEventOnly()) {
1422         continue;
1423       }
1424 
1425       if (item.IsRootOfClosedTree()) {
1426         currentHiddenLevel++;
1427       }
1428 
1429       if (currentHiddenLevel <= maxHiddenLevel) {
1430         aPath.AppendElement(item.CurrentTarget()->GetTargetForDOMEvent());
1431       }
1432 
1433       if (item.IsChromeHandler()) {
1434         break;
1435       }
1436 
1437       if (item.IsSlotInClosedTree()) {
1438         currentHiddenLevel--;
1439         maxHiddenLevel = std::min(maxHiddenLevel, currentHiddenLevel);
1440       }
1441     }
1442 
1443     aPath.Reverse();
1444   }
1445 
1446   aPath.AppendElement(currentTarget->GetTargetForDOMEvent());
1447 
1448   {
1449     int32_t maxHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1450     int32_t currentHiddenLevel = currentTargetInfo.mHiddenSubtreeLevel;
1451     for (uint32_t index = currentTargetInfo.mIndex + 1; index < path->Length();
1452          ++index) {
1453       EventTargetChainItem& item = path->ElementAt(index);
1454       if (item.PreHandleEventOnly()) {
1455         continue;
1456       }
1457 
1458       if (item.IsSlotInClosedTree()) {
1459         currentHiddenLevel++;
1460       }
1461 
1462       if (item.IsChromeHandler()) {
1463         break;
1464       }
1465 
1466       if (currentHiddenLevel <= maxHiddenLevel) {
1467         aPath.AppendElement(item.CurrentTarget()->GetTargetForDOMEvent());
1468       }
1469 
1470       if (item.IsRootOfClosedTree()) {
1471         currentHiddenLevel--;
1472         maxHiddenLevel = std::min(maxHiddenLevel, currentHiddenLevel);
1473       }
1474     }
1475   }
1476 }
1477 
1478 }  // namespace mozilla
1479