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