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