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