1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "PointerEventHandler.h"
8 #include "nsIContentInlines.h"
10 #include "PointerEvent.h"
11 #include "PointerLockManager.h"
12 #include "nsRFPService.h"
13 #include "mozilla/PresShell.h"
14 #include "mozilla/StaticPrefs_dom.h"
15 #include "mozilla/dom/BrowserChild.h"
16 #include "mozilla/dom/BrowserParent.h"
17 #include "mozilla/dom/Document.h"
18 #include "mozilla/dom/MouseEventBinding.h"
24 Maybe
<int32_t> PointerEventHandler::sSpoofedPointerId
;
26 // Keeps a map between pointerId and element that currently capturing pointer
27 // with such pointerId. If pointerId is absent in this map then nobody is
28 // capturing it. Additionally keep information about pending capturing content.
29 static nsClassHashtable
<nsUint32HashKey
, PointerCaptureInfo
>*
32 // Keeps information about pointers such as pointerId, activeState, pointerType,
34 static nsClassHashtable
<nsUint32HashKey
, PointerInfo
>* sActivePointersIds
;
36 // Keeps track of which BrowserParent requested pointer capture for a pointer
38 static nsTHashMap
<nsUint32HashKey
, BrowserParent
*>*
39 sPointerCaptureRemoteTargetTable
= nullptr;
42 void PointerEventHandler::InitializeStatics() {
43 MOZ_ASSERT(!sPointerCaptureList
, "InitializeStatics called multiple times!");
45 new nsClassHashtable
<nsUint32HashKey
, PointerCaptureInfo
>;
46 sActivePointersIds
= new nsClassHashtable
<nsUint32HashKey
, PointerInfo
>;
47 if (XRE_IsParentProcess()) {
48 sPointerCaptureRemoteTargetTable
=
49 new nsTHashMap
<nsUint32HashKey
, BrowserParent
*>;
54 void PointerEventHandler::ReleaseStatics() {
55 MOZ_ASSERT(sPointerCaptureList
, "ReleaseStatics called without Initialize!");
56 delete sPointerCaptureList
;
57 sPointerCaptureList
= nullptr;
58 delete sActivePointersIds
;
59 sActivePointersIds
= nullptr;
60 if (sPointerCaptureRemoteTargetTable
) {
61 MOZ_ASSERT(XRE_IsParentProcess());
62 delete sPointerCaptureRemoteTargetTable
;
63 sPointerCaptureRemoteTargetTable
= nullptr;
68 bool PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() {
69 return StaticPrefs::dom_w3c_pointer_events_implicit_capture();
73 void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent
* aEvent
,
74 nsIContent
* aTargetContent
) {
78 switch (aEvent
->mMessage
) {
79 case eMouseEnterIntoWidget
:
80 // In this case we have to know information about available mouse pointers
81 sActivePointersIds
->InsertOrUpdate(
83 MakeUnique
<PointerInfo
>(false, aEvent
->mInputSource
, true, nullptr));
85 MaybeCacheSpoofedPointerID(aEvent
->mInputSource
, aEvent
->pointerId
);
88 // In this case we switch pointer to active state
89 if (WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent()) {
90 // XXXedgar, test could possibly synthesize a mousedown event on a
91 // coordinate outside the browser window and cause aTargetContent to be
92 // nullptr, not sure if this also happens on real usage.
93 sActivePointersIds
->InsertOrUpdate(
94 pointerEvent
->pointerId
,
95 MakeUnique
<PointerInfo
>(
96 true, pointerEvent
->mInputSource
, pointerEvent
->mIsPrimary
,
97 aTargetContent
? aTargetContent
->OwnerDoc() : nullptr));
98 MaybeCacheSpoofedPointerID(pointerEvent
->mInputSource
,
99 pointerEvent
->pointerId
);
103 // pointercancel means a pointer is unlikely to continue to produce
104 // pointer events. In that case, we should turn off active state or remove
105 // the pointer from active pointers.
107 // In this case we remove information about pointer or turn off active
109 if (WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent()) {
110 if (pointerEvent
->mInputSource
!=
111 MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
112 sActivePointersIds
->InsertOrUpdate(
113 pointerEvent
->pointerId
,
114 MakeUnique
<PointerInfo
>(false, pointerEvent
->mInputSource
,
115 pointerEvent
->mIsPrimary
, nullptr));
117 sActivePointersIds
->Remove(pointerEvent
->pointerId
);
121 case eMouseExitFromWidget
:
122 // In this case we have to remove information about disappeared mouse
124 sActivePointersIds
->Remove(aEvent
->pointerId
);
127 MOZ_ASSERT_UNREACHABLE("event has invalid type");
133 void PointerEventHandler::RequestPointerCaptureById(uint32_t aPointerId
,
135 SetPointerCaptureById(aPointerId
, aElement
);
137 if (BrowserChild
* browserChild
=
138 BrowserChild::GetFrom(aElement
->OwnerDoc()->GetDocShell())) {
139 browserChild
->SendRequestPointerCapture(
141 [aPointerId
](bool aSuccess
) {
143 PointerEventHandler::ReleasePointerCaptureById(aPointerId
);
146 [](mozilla::ipc::ResponseRejectReason
) {});
151 void PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId
,
153 MOZ_ASSERT(aElement
);
154 sPointerCaptureList
->WithEntryHandle(aPointerId
, [&](auto&& entry
) {
156 entry
.Data()->mPendingElement
= aElement
;
158 entry
.Insert(MakeUnique
<PointerCaptureInfo
>(aElement
));
164 PointerCaptureInfo
* PointerEventHandler::GetPointerCaptureInfo(
165 uint32_t aPointerId
) {
166 PointerCaptureInfo
* pointerCaptureInfo
= nullptr;
167 sPointerCaptureList
->Get(aPointerId
, &pointerCaptureInfo
);
168 return pointerCaptureInfo
;
172 void PointerEventHandler::ReleasePointerCaptureById(uint32_t aPointerId
) {
173 PointerCaptureInfo
* pointerCaptureInfo
= GetPointerCaptureInfo(aPointerId
);
174 if (pointerCaptureInfo
) {
175 if (Element
* pendingElement
= pointerCaptureInfo
->mPendingElement
) {
176 if (BrowserChild
* browserChild
= BrowserChild::GetFrom(
177 pendingElement
->OwnerDoc()->GetDocShell())) {
178 browserChild
->SendReleasePointerCapture(aPointerId
);
181 pointerCaptureInfo
->mPendingElement
= nullptr;
186 void PointerEventHandler::ReleaseAllPointerCapture() {
187 for (const auto& entry
: *sPointerCaptureList
) {
188 PointerCaptureInfo
* data
= entry
.GetWeak();
189 if (data
&& data
->mPendingElement
) {
190 ReleasePointerCaptureById(entry
.GetKey());
196 bool PointerEventHandler::SetPointerCaptureRemoteTarget(
197 uint32_t aPointerId
, dom::BrowserParent
* aBrowserParent
) {
198 MOZ_ASSERT(XRE_IsParentProcess());
199 MOZ_ASSERT(sPointerCaptureRemoteTargetTable
);
200 MOZ_ASSERT(aBrowserParent
);
202 if (PointerLockManager::GetLockedRemoteTarget()) {
206 BrowserParent
* currentRemoteTarget
=
207 PointerEventHandler::GetPointerCapturingRemoteTarget(aPointerId
);
208 if (currentRemoteTarget
&& currentRemoteTarget
!= aBrowserParent
) {
212 sPointerCaptureRemoteTargetTable
->InsertOrUpdate(aPointerId
, aBrowserParent
);
217 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
218 BrowserParent
* aBrowserParent
) {
219 MOZ_ASSERT(XRE_IsParentProcess());
220 MOZ_ASSERT(sPointerCaptureRemoteTargetTable
);
221 MOZ_ASSERT(aBrowserParent
);
223 sPointerCaptureRemoteTargetTable
->RemoveIf([aBrowserParent
](
225 BrowserParent
* browserParent
= iter
.Data();
226 MOZ_ASSERT(browserParent
, "Null BrowserParent in pointer captured table?");
228 return aBrowserParent
== browserParent
;
233 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
234 uint32_t aPointerId
) {
235 MOZ_ASSERT(XRE_IsParentProcess());
236 MOZ_ASSERT(sPointerCaptureRemoteTargetTable
);
238 sPointerCaptureRemoteTargetTable
->Remove(aPointerId
);
242 BrowserParent
* PointerEventHandler::GetPointerCapturingRemoteTarget(
243 uint32_t aPointerId
) {
244 MOZ_ASSERT(XRE_IsParentProcess());
245 MOZ_ASSERT(sPointerCaptureRemoteTargetTable
);
247 return sPointerCaptureRemoteTargetTable
->Get(aPointerId
);
251 void PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget() {
252 MOZ_ASSERT(XRE_IsParentProcess());
253 MOZ_ASSERT(sPointerCaptureRemoteTargetTable
);
255 for (auto iter
= sPointerCaptureRemoteTargetTable
->Iter(); !iter
.Done();
257 BrowserParent
* browserParent
= iter
.Data();
258 MOZ_ASSERT(browserParent
, "Null BrowserParent in pointer captured table?");
260 Unused
<< browserParent
->SendReleaseAllPointerCapture();
266 const PointerInfo
* PointerEventHandler::GetPointerInfo(uint32_t aPointerId
) {
267 return sActivePointersIds
->Get(aPointerId
);
271 void PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent
* aEvent
) {
272 switch (aEvent
->mClass
) {
273 case eMouseEventClass
:
274 ProcessPointerCaptureForMouse(aEvent
->AsMouseEvent());
276 case eTouchEventClass
:
277 ProcessPointerCaptureForTouch(aEvent
->AsTouchEvent());
285 void PointerEventHandler::ProcessPointerCaptureForMouse(
286 WidgetMouseEvent
* aEvent
) {
287 if (!ShouldGeneratePointerEventFromMouse(aEvent
)) {
291 PointerCaptureInfo
* info
= GetPointerCaptureInfo(aEvent
->pointerId
);
292 if (!info
|| info
->mPendingElement
== info
->mOverrideElement
) {
295 WidgetPointerEvent
localEvent(*aEvent
);
296 InitPointerEventFromMouse(&localEvent
, aEvent
, eVoidEvent
);
297 CheckPointerCaptureState(&localEvent
);
301 void PointerEventHandler::ProcessPointerCaptureForTouch(
302 WidgetTouchEvent
* aEvent
) {
303 if (!ShouldGeneratePointerEventFromTouch(aEvent
)) {
307 for (uint32_t i
= 0; i
< aEvent
->mTouches
.Length(); ++i
) {
308 Touch
* touch
= aEvent
->mTouches
[i
];
309 if (!TouchManager::ShouldConvertTouchToPointer(touch
, aEvent
)) {
312 PointerCaptureInfo
* info
= GetPointerCaptureInfo(touch
->Identifier());
313 if (!info
|| info
->mPendingElement
== info
->mOverrideElement
) {
316 WidgetPointerEvent
event(aEvent
->IsTrusted(), eVoidEvent
, aEvent
->mWidget
);
317 InitPointerEventFromTouch(event
, *aEvent
, *touch
, i
== 0);
318 CheckPointerCaptureState(&event
);
323 void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent
* aEvent
) {
324 // Handle pending pointer capture before any pointer events except
325 // gotpointercapture / lostpointercapture.
329 MOZ_ASSERT(aEvent
->mClass
== ePointerEventClass
);
331 PointerCaptureInfo
* captureInfo
= GetPointerCaptureInfo(aEvent
->pointerId
);
333 // When fingerprinting resistance is enabled, we need to map other pointer
334 // ids into the spoofed one. We don't have to do the mapping if the capture
335 // info exists for the non-spoofed pointer id because of we won't allow
336 // content to set pointer capture other than the spoofed one. Thus, it must be
337 // from chrome if the capture info exists in this case. And we don't have to
338 // do anything if the pointer id is the same as the spoofed one.
339 if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check",
340 RFPTarget::PointerEvents
) &&
341 aEvent
->pointerId
!= (uint32_t)GetSpoofedPointerIdForRFP() &&
343 PointerCaptureInfo
* spoofedCaptureInfo
=
344 GetPointerCaptureInfo(GetSpoofedPointerIdForRFP());
346 // We need to check the target element's document should resist
347 // fingerprinting. If not, we don't need to send a capture event
348 // since the capture info of the original pointer id doesn't exist
350 if (!spoofedCaptureInfo
|| !spoofedCaptureInfo
->mPendingElement
||
351 !spoofedCaptureInfo
->mPendingElement
->OwnerDoc()
352 ->ShouldResistFingerprinting(RFPTarget::PointerEvents
)) {
356 captureInfo
= spoofedCaptureInfo
;
360 captureInfo
->mPendingElement
== captureInfo
->mOverrideElement
) {
364 RefPtr
<Element
> overrideElement
= captureInfo
->mOverrideElement
;
365 RefPtr
<Element
> pendingElement
= captureInfo
->mPendingElement
;
367 // Update captureInfo before dispatching event since sPointerCaptureList may
368 // be changed in the pointer event listener.
369 captureInfo
->mOverrideElement
= captureInfo
->mPendingElement
;
370 if (captureInfo
->Empty()) {
371 sPointerCaptureList
->Remove(aEvent
->pointerId
);
374 if (overrideElement
) {
375 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent
,
378 if (pendingElement
) {
379 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent
,
385 void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame
* aFrame
,
386 WidgetEvent
* aEvent
) {
387 MOZ_ASSERT(aEvent
->mMessage
== ePointerDown
);
388 if (!aFrame
|| !IsPointerEventImplicitCaptureForTouchEnabled()) {
391 WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent();
392 NS_WARNING_ASSERTION(pointerEvent
,
393 "Call ImplicitlyCapturePointer with non-pointer event");
394 if (!pointerEvent
->mFromTouchEvent
) {
395 // We only implicitly capture the pointer for touch device.
398 nsCOMPtr
<nsIContent
> target
;
399 aFrame
->GetContentForEvent(aEvent
, getter_AddRefs(target
));
400 while (target
&& !target
->IsElement()) {
401 target
= target
->GetParent();
403 if (NS_WARN_IF(!target
)) {
406 RequestPointerCaptureById(pointerEvent
->pointerId
, target
->AsElement());
410 void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent
* aEvent
) {
412 if (aEvent
->mMessage
!= ePointerUp
&& aEvent
->mMessage
!= ePointerCancel
) {
415 WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent();
416 ReleasePointerCaptureById(pointerEvent
->pointerId
);
417 CheckPointerCaptureState(pointerEvent
);
421 void PointerEventHandler::MaybeImplicitlyReleasePointerCapture(
422 WidgetGUIEvent
* aEvent
) {
424 const EventMessage pointerEventMessage
=
425 PointerEventHandler::ToPointerEventMessage(aEvent
);
426 if (pointerEventMessage
!= ePointerUp
&&
427 pointerEventMessage
!= ePointerCancel
) {
430 PointerEventHandler::MaybeProcessPointerCapture(aEvent
);
434 Element
* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId
) {
435 PointerCaptureInfo
* pointerCaptureInfo
= GetPointerCaptureInfo(aPointerId
);
436 if (pointerCaptureInfo
) {
437 return pointerCaptureInfo
->mOverrideElement
;
443 Element
* PointerEventHandler::GetPointerCapturingElement(
444 WidgetGUIEvent
* aEvent
) {
445 if ((aEvent
->mClass
!= ePointerEventClass
&&
446 aEvent
->mClass
!= eMouseEventClass
) ||
447 aEvent
->mMessage
== ePointerDown
|| aEvent
->mMessage
== eMouseDown
) {
448 // Pointer capture should only be applied to all pointer events and mouse
449 // events except ePointerDown and eMouseDown;
453 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
457 return GetPointerCapturingElement(mouseEvent
->pointerId
);
461 void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent
* aContent
) {
462 // We should check that aChild does not contain pointer capturing elements.
463 // If it does we should release the pointer capture for the elements.
464 if (!sPointerCaptureList
->IsEmpty()) {
465 for (const auto& entry
: *sPointerCaptureList
) {
466 PointerCaptureInfo
* data
= entry
.GetWeak();
467 if (data
&& data
->mPendingElement
&&
468 data
->mPendingElement
->IsInclusiveDescendantOf(aContent
)) {
469 ReleasePointerCaptureById(entry
.GetKey());
476 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
477 WidgetPointerEvent
* aPointerEvent
, WidgetGUIEvent
* aMouseOrTouchEvent
) {
478 if (!aPointerEvent
->mIsPrimary
|| aPointerEvent
->mMessage
== ePointerDown
) {
481 PointerInfo
* pointerInfo
= nullptr;
482 if (!sActivePointersIds
->Get(aPointerEvent
->pointerId
, &pointerInfo
) ||
484 // The PointerInfo for active pointer should be added for normal cases. But
485 // in some cases, we may receive mouse events before adding PointerInfo in
486 // sActivePointersIds. (e.g. receive mousemove before
487 // eMouseEnterIntoWidget). In these cases, we could ignore them because they
488 // are not the events between a DefaultPrevented pointerdown and the
489 // corresponding pointerup.
492 if (!pointerInfo
->mPreventMouseEventByContent
) {
495 aMouseOrTouchEvent
->PreventDefault(false);
496 aMouseOrTouchEvent
->mFlags
.mOnlyChromeDispatch
= true;
497 if (aPointerEvent
->mMessage
== ePointerUp
) {
498 pointerInfo
->mPreventMouseEventByContent
= false;
503 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
504 WidgetPointerEvent
* aPointerEvent
, WidgetGUIEvent
* aMouseOrTouchEvent
) {
505 if (!aPointerEvent
->mIsPrimary
|| aPointerEvent
->mMessage
!= ePointerDown
||
506 !aPointerEvent
->DefaultPreventedByContent()) {
509 PointerInfo
* pointerInfo
= nullptr;
510 if (!sActivePointersIds
->Get(aPointerEvent
->pointerId
, &pointerInfo
) ||
512 // We already added the PointerInfo for active pointer when
513 // PresShell::HandleEvent handling pointerdown event.
515 MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
516 #endif // #ifdef DEBUG
519 // PreventDefault only applied for active pointers.
520 if (!pointerInfo
->mActiveState
) {
523 aMouseOrTouchEvent
->PreventDefault(false);
524 aMouseOrTouchEvent
->mFlags
.mOnlyChromeDispatch
= true;
525 pointerInfo
->mPreventMouseEventByContent
= true;
529 void PointerEventHandler::InitPointerEventFromMouse(
530 WidgetPointerEvent
* aPointerEvent
, WidgetMouseEvent
* aMouseEvent
,
531 EventMessage aMessage
) {
532 MOZ_ASSERT(aPointerEvent
);
533 MOZ_ASSERT(aMouseEvent
);
534 aPointerEvent
->pointerId
= aMouseEvent
->pointerId
;
535 aPointerEvent
->mInputSource
= aMouseEvent
->mInputSource
;
536 aPointerEvent
->mMessage
= aMessage
;
537 aPointerEvent
->mButton
= aMouseEvent
->mMessage
== eMouseMove
538 ? MouseButton::eNotPressed
539 : aMouseEvent
->mButton
;
541 aPointerEvent
->mButtons
= aMouseEvent
->mButtons
;
542 aPointerEvent
->mPressure
=
543 aPointerEvent
->mButtons
544 ? aMouseEvent
->mPressure
? aMouseEvent
->mPressure
: 0.5f
549 void PointerEventHandler::InitPointerEventFromTouch(
550 WidgetPointerEvent
& aPointerEvent
, const WidgetTouchEvent
& aTouchEvent
,
551 const mozilla::dom::Touch
& aTouch
, bool aIsPrimary
) {
552 // Use mButton/mButtons only when mButton got a value (from pen input)
553 int16_t button
= aTouchEvent
.mMessage
== eTouchMove
? MouseButton::eNotPressed
554 : aTouchEvent
.mButton
!= MouseButton::eNotPressed
555 ? aTouchEvent
.mButton
556 : MouseButton::ePrimary
;
557 int16_t buttons
= aTouchEvent
.mMessage
== eTouchEnd
558 ? MouseButtonsFlag::eNoButtons
559 : aTouchEvent
.mButton
!= MouseButton::eNotPressed
560 ? aTouchEvent
.mButtons
561 : MouseButtonsFlag::ePrimaryFlag
;
563 aPointerEvent
.mIsPrimary
= aIsPrimary
;
564 aPointerEvent
.pointerId
= aTouch
.Identifier();
565 aPointerEvent
.mRefPoint
= aTouch
.mRefPoint
;
566 aPointerEvent
.mModifiers
= aTouchEvent
.mModifiers
;
567 aPointerEvent
.mWidth
= aTouch
.RadiusX(CallerType::System
);
568 aPointerEvent
.mHeight
= aTouch
.RadiusY(CallerType::System
);
569 aPointerEvent
.tiltX
= aTouch
.tiltX
;
570 aPointerEvent
.tiltY
= aTouch
.tiltY
;
571 aPointerEvent
.twist
= aTouch
.twist
;
572 aPointerEvent
.mTimeStamp
= aTouchEvent
.mTimeStamp
;
573 aPointerEvent
.mFlags
= aTouchEvent
.mFlags
;
574 aPointerEvent
.mButton
= button
;
575 aPointerEvent
.mButtons
= buttons
;
576 aPointerEvent
.mInputSource
= aTouchEvent
.mInputSource
;
577 aPointerEvent
.mFromTouchEvent
= true;
578 aPointerEvent
.mPressure
= aTouch
.mForce
;
582 EventMessage
PointerEventHandler::ToPointerEventMessage(
583 const WidgetGUIEvent
* aMouseOrTouchEvent
) {
584 MOZ_ASSERT(aMouseOrTouchEvent
);
586 switch (aMouseOrTouchEvent
->mMessage
) {
590 return aMouseOrTouchEvent
->AsMouseEvent()->mButtons
? ePointerMove
593 const WidgetMouseEvent
* mouseEvent
= aMouseOrTouchEvent
->AsMouseEvent();
594 return mouseEvent
->mButtons
& ~nsContentUtils::GetButtonsFlagForButton(
606 case eTouchPointerCancel
:
607 return ePointerCancel
;
614 void PointerEventHandler::DispatchPointerFromMouseOrTouch(
615 PresShell
* aShell
, nsIFrame
* aEventTargetFrame
,
616 nsIContent
* aEventTargetContent
, WidgetGUIEvent
* aMouseOrTouchEvent
,
617 bool aDontRetargetEvents
, nsEventStatus
* aStatus
,
618 nsIContent
** aMouseOrTouchEventTarget
/* = nullptr */) {
619 MOZ_ASSERT(aEventTargetFrame
|| aEventTargetContent
);
620 MOZ_ASSERT(aMouseOrTouchEvent
);
622 EventMessage pointerMessage
= eVoidEvent
;
623 if (aMouseOrTouchEvent
->mClass
== eMouseEventClass
) {
624 WidgetMouseEvent
* mouseEvent
= aMouseOrTouchEvent
->AsMouseEvent();
625 // Don't dispatch pointer events caused by a mouse when simulating touch
627 Document
* doc
= aShell
->GetDocument();
632 BrowsingContext
* bc
= doc
->GetBrowsingContext();
633 if (bc
&& bc
->TouchEventsOverride() == TouchEventsOverride::Enabled
&&
638 // 1. If it is not mouse then it is likely will come as touch event
639 // 2. We don't synthesize pointer events for those events that are not
640 // dispatched to DOM.
641 if (!mouseEvent
->convertToPointer
||
642 !aMouseOrTouchEvent
->IsAllowedToDispatchDOMEvent()) {
646 pointerMessage
= PointerEventHandler::ToPointerEventMessage(mouseEvent
);
647 if (pointerMessage
== eVoidEvent
) {
650 WidgetPointerEvent
event(*mouseEvent
);
651 InitPointerEventFromMouse(&event
, mouseEvent
, pointerMessage
);
652 event
.convertToPointer
= mouseEvent
->convertToPointer
= false;
653 RefPtr
<PresShell
> shell(aShell
);
654 if (!aEventTargetFrame
) {
655 shell
= PresShell::GetShellForEventTarget(nullptr, aEventTargetContent
);
660 PreHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
661 // Dispatch pointer event to the same target which is found by the
662 // corresponding mouse event.
663 shell
->HandleEventWithTarget(&event
, aEventTargetFrame
, aEventTargetContent
,
664 aStatus
, true, aMouseOrTouchEventTarget
);
665 PostHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
666 } else if (aMouseOrTouchEvent
->mClass
== eTouchEventClass
) {
667 WidgetTouchEvent
* touchEvent
= aMouseOrTouchEvent
->AsTouchEvent();
668 // loop over all touches and dispatch pointer events on each touch
670 pointerMessage
= PointerEventHandler::ToPointerEventMessage(touchEvent
);
671 if (pointerMessage
== eVoidEvent
) {
674 RefPtr
<PresShell
> shell(aShell
);
675 for (uint32_t i
= 0; i
< touchEvent
->mTouches
.Length(); ++i
) {
676 Touch
* touch
= touchEvent
->mTouches
[i
];
677 if (!TouchManager::ShouldConvertTouchToPointer(touch
, touchEvent
)) {
681 WidgetPointerEvent
event(touchEvent
->IsTrusted(), pointerMessage
,
682 touchEvent
->mWidget
);
684 InitPointerEventFromTouch(event
, *touchEvent
, *touch
, i
== 0);
685 event
.convertToPointer
= touch
->convertToPointer
= false;
686 event
.mCoalescedWidgetEvents
= touch
->mCoalescedWidgetEvents
;
687 if (aMouseOrTouchEvent
->mMessage
== eTouchStart
) {
688 // We already did hit test for touchstart in PresShell. We should
689 // dispatch pointerdown to the same target as touchstart.
690 nsCOMPtr
<nsIContent
> content
=
691 nsIContent::FromEventTargetOrNull(touch
->mTarget
);
696 nsIFrame
* frame
= content
->GetPrimaryFrame();
697 shell
= PresShell::GetShellForEventTarget(frame
, content
);
702 PreHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
703 shell
->HandleEventWithTarget(&event
, frame
, content
, aStatus
, true,
704 aMouseOrTouchEventTarget
);
705 PostHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
707 // We didn't hit test for other touch events. Spec doesn't mention that
708 // all pointer events should be dispatched to the same target as their
709 // corresponding touch events. Call PresShell::HandleEvent so that we do
710 // hit test for pointer events.
711 // FIXME: If aDontRetargetEvents is true and the event is fired on
712 // different document, we cannot track the pointer event target when
713 // it's removed from the tree.
714 PreHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
715 shell
->HandleEvent(aEventTargetFrame
, &event
, aDontRetargetEvents
,
717 PostHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
724 void PointerEventHandler::NotifyDestroyPresContext(
725 nsPresContext
* aPresContext
) {
726 // Clean up pointer capture info
727 for (auto iter
= sPointerCaptureList
->Iter(); !iter
.Done(); iter
.Next()) {
728 PointerCaptureInfo
* data
= iter
.UserData();
729 MOZ_ASSERT(data
, "how could we have a null PointerCaptureInfo here?");
730 if (data
->mPendingElement
&&
731 data
->mPendingElement
->GetPresContext(Element::eForComposedDoc
) ==
733 data
->mPendingElement
= nullptr;
735 if (data
->mOverrideElement
&&
736 data
->mOverrideElement
->GetPresContext(Element::eForComposedDoc
) ==
738 data
->mOverrideElement
= nullptr;
746 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent
& aEvent
) {
748 if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) {
749 // WM_POINTER does not support drag and drop, see bug 1692277
750 return (aEvent
.mInputSource
!= dom::MouseEvent_Binding::MOZ_SOURCE_PEN
&&
751 aEvent
.mReason
!= WidgetMouseEvent::eSynthesized
); // bug 1692151
758 uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId
) {
759 PointerInfo
* pointerInfo
= nullptr;
760 if (sActivePointersIds
->Get(aPointerId
, &pointerInfo
) && pointerInfo
) {
761 return pointerInfo
->mPointerType
;
763 return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
767 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId
) {
768 PointerInfo
* pointerInfo
= nullptr;
769 if (sActivePointersIds
->Get(aPointerId
, &pointerInfo
) && pointerInfo
) {
770 return pointerInfo
->mPrimaryState
;
776 void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
777 bool aIsGotCapture
, const WidgetPointerEvent
* aPointerEvent
,
778 Element
* aCaptureTarget
) {
779 Document
* targetDoc
= aCaptureTarget
->OwnerDoc();
780 RefPtr
<PresShell
> presShell
= targetDoc
->GetPresShell();
781 if (NS_WARN_IF(!presShell
|| presShell
->IsDestroying())) {
785 if (!aIsGotCapture
&& !aCaptureTarget
->IsInComposedDoc()) {
786 // If the capturing element was removed from the DOM tree, fire
787 // ePointerLostCapture at the document.
788 PointerEventInit init
;
789 init
.mPointerId
= aPointerEvent
->pointerId
;
790 init
.mBubbles
= true;
791 init
.mComposed
= true;
792 ConvertPointerTypeToString(aPointerEvent
->mInputSource
, init
.mPointerType
);
793 init
.mIsPrimary
= aPointerEvent
->mIsPrimary
;
794 RefPtr
<PointerEvent
> event
;
795 event
= PointerEvent::Constructor(aCaptureTarget
, u
"lostpointercapture"_ns
,
797 targetDoc
->DispatchEvent(*event
);
800 nsEventStatus status
= nsEventStatus_eIgnore
;
801 WidgetPointerEvent
localEvent(
802 aPointerEvent
->IsTrusted(),
803 aIsGotCapture
? ePointerGotCapture
: ePointerLostCapture
,
804 aPointerEvent
->mWidget
);
806 localEvent
.AssignPointerEventData(*aPointerEvent
, true);
807 DebugOnly
<nsresult
> rv
= presShell
->HandleEventWithTarget(
808 &localEvent
, aCaptureTarget
->GetPrimaryFrame(), aCaptureTarget
, &status
);
810 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
811 "DispatchGotOrLostPointerCaptureEvent failed");
815 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource
,
816 uint32_t aPointerId
) {
817 if (sSpoofedPointerId
.isSome() || aInputSource
!= SPOOFED_POINTER_INTERFACE
) {
821 sSpoofedPointerId
.emplace(aPointerId
);
824 } // namespace mozilla