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 Element
* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId
) {
422 PointerCaptureInfo
* pointerCaptureInfo
= GetPointerCaptureInfo(aPointerId
);
423 if (pointerCaptureInfo
) {
424 return pointerCaptureInfo
->mOverrideElement
;
430 Element
* PointerEventHandler::GetPointerCapturingElement(
431 WidgetGUIEvent
* aEvent
) {
432 if ((aEvent
->mClass
!= ePointerEventClass
&&
433 aEvent
->mClass
!= eMouseEventClass
) ||
434 aEvent
->mMessage
== ePointerDown
|| aEvent
->mMessage
== eMouseDown
) {
435 // Pointer capture should only be applied to all pointer events and mouse
436 // events except ePointerDown and eMouseDown;
440 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
444 return GetPointerCapturingElement(mouseEvent
->pointerId
);
448 void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent
* aContent
) {
449 // We should check that aChild does not contain pointer capturing elements.
450 // If it does we should release the pointer capture for the elements.
451 for (const auto& entry
: *sPointerCaptureList
) {
452 PointerCaptureInfo
* data
= entry
.GetWeak();
453 if (data
&& data
->mPendingElement
&&
454 data
->mPendingElement
->IsInclusiveDescendantOf(aContent
)) {
455 ReleasePointerCaptureById(entry
.GetKey());
461 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
462 WidgetPointerEvent
* aPointerEvent
, WidgetGUIEvent
* aMouseOrTouchEvent
) {
463 if (!aPointerEvent
->mIsPrimary
|| aPointerEvent
->mMessage
== ePointerDown
) {
466 PointerInfo
* pointerInfo
= nullptr;
467 if (!sActivePointersIds
->Get(aPointerEvent
->pointerId
, &pointerInfo
) ||
469 // The PointerInfo for active pointer should be added for normal cases. But
470 // in some cases, we may receive mouse events before adding PointerInfo in
471 // sActivePointersIds. (e.g. receive mousemove before
472 // eMouseEnterIntoWidget). In these cases, we could ignore them because they
473 // are not the events between a DefaultPrevented pointerdown and the
474 // corresponding pointerup.
477 if (!pointerInfo
->mPreventMouseEventByContent
) {
480 aMouseOrTouchEvent
->PreventDefault(false);
481 aMouseOrTouchEvent
->mFlags
.mOnlyChromeDispatch
= true;
482 if (aPointerEvent
->mMessage
== ePointerUp
) {
483 pointerInfo
->mPreventMouseEventByContent
= false;
488 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
489 WidgetPointerEvent
* aPointerEvent
, WidgetGUIEvent
* aMouseOrTouchEvent
) {
490 if (!aPointerEvent
->mIsPrimary
|| aPointerEvent
->mMessage
!= ePointerDown
||
491 !aPointerEvent
->DefaultPreventedByContent()) {
494 PointerInfo
* pointerInfo
= nullptr;
495 if (!sActivePointersIds
->Get(aPointerEvent
->pointerId
, &pointerInfo
) ||
497 // We already added the PointerInfo for active pointer when
498 // PresShell::HandleEvent handling pointerdown event.
500 MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
501 #endif // #ifdef DEBUG
504 // PreventDefault only applied for active pointers.
505 if (!pointerInfo
->mActiveState
) {
508 aMouseOrTouchEvent
->PreventDefault(false);
509 aMouseOrTouchEvent
->mFlags
.mOnlyChromeDispatch
= true;
510 pointerInfo
->mPreventMouseEventByContent
= true;
514 void PointerEventHandler::InitPointerEventFromMouse(
515 WidgetPointerEvent
* aPointerEvent
, WidgetMouseEvent
* aMouseEvent
,
516 EventMessage aMessage
) {
517 MOZ_ASSERT(aPointerEvent
);
518 MOZ_ASSERT(aMouseEvent
);
519 aPointerEvent
->pointerId
= aMouseEvent
->pointerId
;
520 aPointerEvent
->mInputSource
= aMouseEvent
->mInputSource
;
521 aPointerEvent
->mMessage
= aMessage
;
522 aPointerEvent
->mButton
= aMouseEvent
->mMessage
== eMouseMove
523 ? MouseButton::eNotPressed
524 : aMouseEvent
->mButton
;
526 aPointerEvent
->mButtons
= aMouseEvent
->mButtons
;
527 aPointerEvent
->mPressure
=
528 aPointerEvent
->mButtons
529 ? aMouseEvent
->mPressure
? aMouseEvent
->mPressure
: 0.5f
534 void PointerEventHandler::InitPointerEventFromTouch(
535 WidgetPointerEvent
& aPointerEvent
, const WidgetTouchEvent
& aTouchEvent
,
536 const mozilla::dom::Touch
& aTouch
, bool aIsPrimary
) {
537 // Use mButton/mButtons only when mButton got a value (from pen input)
538 int16_t button
= aTouchEvent
.mMessage
== eTouchMove
? MouseButton::eNotPressed
539 : aTouchEvent
.mButton
!= MouseButton::eNotPressed
540 ? aTouchEvent
.mButton
541 : MouseButton::ePrimary
;
542 int16_t buttons
= aTouchEvent
.mMessage
== eTouchEnd
543 ? MouseButtonsFlag::eNoButtons
544 : aTouchEvent
.mButton
!= MouseButton::eNotPressed
545 ? aTouchEvent
.mButtons
546 : MouseButtonsFlag::ePrimaryFlag
;
548 aPointerEvent
.mIsPrimary
= aIsPrimary
;
549 aPointerEvent
.pointerId
= aTouch
.Identifier();
550 aPointerEvent
.mRefPoint
= aTouch
.mRefPoint
;
551 aPointerEvent
.mModifiers
= aTouchEvent
.mModifiers
;
552 aPointerEvent
.mWidth
= aTouch
.RadiusX(CallerType::System
);
553 aPointerEvent
.mHeight
= aTouch
.RadiusY(CallerType::System
);
554 aPointerEvent
.tiltX
= aTouch
.tiltX
;
555 aPointerEvent
.tiltY
= aTouch
.tiltY
;
556 aPointerEvent
.twist
= aTouch
.twist
;
557 aPointerEvent
.mTimeStamp
= aTouchEvent
.mTimeStamp
;
558 aPointerEvent
.mFlags
= aTouchEvent
.mFlags
;
559 aPointerEvent
.mButton
= button
;
560 aPointerEvent
.mButtons
= buttons
;
561 aPointerEvent
.mInputSource
= aTouchEvent
.mInputSource
;
562 aPointerEvent
.mFromTouchEvent
= true;
563 aPointerEvent
.mPressure
= aTouch
.mForce
;
567 void PointerEventHandler::DispatchPointerFromMouseOrTouch(
568 PresShell
* aShell
, nsIFrame
* aFrame
, nsIContent
* aContent
,
569 WidgetGUIEvent
* aEvent
, bool aDontRetargetEvents
, nsEventStatus
* aStatus
,
570 nsIContent
** aTargetContent
) {
571 MOZ_ASSERT(aFrame
|| aContent
);
574 EventMessage pointerMessage
= eVoidEvent
;
575 if (aEvent
->mClass
== eMouseEventClass
) {
576 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
577 // Don't dispatch pointer events caused by a mouse when simulating touch
579 Document
* doc
= aShell
->GetDocument();
584 BrowsingContext
* bc
= doc
->GetBrowsingContext();
585 if (bc
&& bc
->TouchEventsOverride() == TouchEventsOverride::Enabled
&&
590 // 1. If it is not mouse then it is likely will come as touch event
591 // 2. We don't synthesize pointer events for those events that are not
592 // dispatched to DOM.
593 if (!mouseEvent
->convertToPointer
||
594 !aEvent
->IsAllowedToDispatchDOMEvent()) {
598 switch (mouseEvent
->mMessage
) {
600 pointerMessage
= ePointerMove
;
603 pointerMessage
= mouseEvent
->mButtons
? ePointerMove
: ePointerUp
;
607 mouseEvent
->mButtons
& ~nsContentUtils::GetButtonsFlagForButton(
616 WidgetPointerEvent
event(*mouseEvent
);
617 InitPointerEventFromMouse(&event
, mouseEvent
, pointerMessage
);
618 event
.convertToPointer
= mouseEvent
->convertToPointer
= false;
619 RefPtr
<PresShell
> shell(aShell
);
621 shell
= PresShell::GetShellForEventTarget(nullptr, aContent
);
626 PreHandlePointerEventsPreventDefault(&event
, aEvent
);
627 // Dispatch pointer event to the same target which is found by the
628 // corresponding mouse event.
629 shell
->HandleEventWithTarget(&event
, aFrame
, aContent
, aStatus
, true,
631 PostHandlePointerEventsPreventDefault(&event
, aEvent
);
632 } else if (aEvent
->mClass
== eTouchEventClass
) {
633 WidgetTouchEvent
* touchEvent
= aEvent
->AsTouchEvent();
634 // loop over all touches and dispatch pointer events on each touch
636 switch (touchEvent
->mMessage
) {
638 pointerMessage
= ePointerMove
;
641 pointerMessage
= ePointerUp
;
644 pointerMessage
= ePointerDown
;
647 case eTouchPointerCancel
:
648 pointerMessage
= ePointerCancel
;
654 RefPtr
<PresShell
> shell(aShell
);
655 for (uint32_t i
= 0; i
< touchEvent
->mTouches
.Length(); ++i
) {
656 Touch
* touch
= touchEvent
->mTouches
[i
];
657 if (!TouchManager::ShouldConvertTouchToPointer(touch
, touchEvent
)) {
661 WidgetPointerEvent
event(touchEvent
->IsTrusted(), pointerMessage
,
662 touchEvent
->mWidget
);
664 InitPointerEventFromTouch(event
, *touchEvent
, *touch
, i
== 0);
665 event
.convertToPointer
= touch
->convertToPointer
= false;
666 event
.mCoalescedWidgetEvents
= touch
->mCoalescedWidgetEvents
;
667 if (aEvent
->mMessage
== eTouchStart
) {
668 // We already did hit test for touchstart in PresShell. We should
669 // dispatch pointerdown to the same target as touchstart.
670 nsCOMPtr
<nsIContent
> content
=
671 nsIContent::FromEventTargetOrNull(touch
->mTarget
);
676 nsIFrame
* frame
= content
->GetPrimaryFrame();
677 shell
= PresShell::GetShellForEventTarget(frame
, content
);
682 PreHandlePointerEventsPreventDefault(&event
, aEvent
);
683 shell
->HandleEventWithTarget(&event
, frame
, content
, aStatus
, true,
685 PostHandlePointerEventsPreventDefault(&event
, aEvent
);
687 // We didn't hit test for other touch events. Spec doesn't mention that
688 // all pointer events should be dispatched to the same target as their
689 // corresponding touch events. Call PresShell::HandleEvent so that we do
690 // hit test for pointer events.
691 PreHandlePointerEventsPreventDefault(&event
, aEvent
);
692 shell
->HandleEvent(aFrame
, &event
, aDontRetargetEvents
, aStatus
);
693 PostHandlePointerEventsPreventDefault(&event
, aEvent
);
700 void PointerEventHandler::NotifyDestroyPresContext(
701 nsPresContext
* aPresContext
) {
702 // Clean up pointer capture info
703 for (auto iter
= sPointerCaptureList
->Iter(); !iter
.Done(); iter
.Next()) {
704 PointerCaptureInfo
* data
= iter
.UserData();
705 MOZ_ASSERT(data
, "how could we have a null PointerCaptureInfo here?");
706 if (data
->mPendingElement
&&
707 data
->mPendingElement
->GetPresContext(Element::eForComposedDoc
) ==
709 data
->mPendingElement
= nullptr;
711 if (data
->mOverrideElement
&&
712 data
->mOverrideElement
->GetPresContext(Element::eForComposedDoc
) ==
714 data
->mOverrideElement
= nullptr;
722 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent
& aEvent
) {
724 if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) {
725 // WM_POINTER does not support drag and drop, see bug 1692277
726 return (aEvent
.mInputSource
!= dom::MouseEvent_Binding::MOZ_SOURCE_PEN
&&
727 aEvent
.mReason
!= WidgetMouseEvent::eSynthesized
); // bug 1692151
734 uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId
) {
735 PointerInfo
* pointerInfo
= nullptr;
736 if (sActivePointersIds
->Get(aPointerId
, &pointerInfo
) && pointerInfo
) {
737 return pointerInfo
->mPointerType
;
739 return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
743 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId
) {
744 PointerInfo
* pointerInfo
= nullptr;
745 if (sActivePointersIds
->Get(aPointerId
, &pointerInfo
) && pointerInfo
) {
746 return pointerInfo
->mPrimaryState
;
752 void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
753 bool aIsGotCapture
, const WidgetPointerEvent
* aPointerEvent
,
754 Element
* aCaptureTarget
) {
755 Document
* targetDoc
= aCaptureTarget
->OwnerDoc();
756 RefPtr
<PresShell
> presShell
= targetDoc
->GetPresShell();
757 if (NS_WARN_IF(!presShell
|| presShell
->IsDestroying())) {
761 if (!aIsGotCapture
&& !aCaptureTarget
->IsInComposedDoc()) {
762 // If the capturing element was removed from the DOM tree, fire
763 // ePointerLostCapture at the document.
764 PointerEventInit init
;
765 init
.mPointerId
= aPointerEvent
->pointerId
;
766 init
.mBubbles
= true;
767 init
.mComposed
= true;
768 ConvertPointerTypeToString(aPointerEvent
->mInputSource
, init
.mPointerType
);
769 init
.mIsPrimary
= aPointerEvent
->mIsPrimary
;
770 RefPtr
<PointerEvent
> event
;
771 event
= PointerEvent::Constructor(aCaptureTarget
, u
"lostpointercapture"_ns
,
773 targetDoc
->DispatchEvent(*event
);
776 nsEventStatus status
= nsEventStatus_eIgnore
;
777 WidgetPointerEvent
localEvent(
778 aPointerEvent
->IsTrusted(),
779 aIsGotCapture
? ePointerGotCapture
: ePointerLostCapture
,
780 aPointerEvent
->mWidget
);
782 localEvent
.AssignPointerEventData(*aPointerEvent
, true);
783 DebugOnly
<nsresult
> rv
= presShell
->HandleEventWithTarget(
784 &localEvent
, aCaptureTarget
->GetPrimaryFrame(), aCaptureTarget
, &status
);
786 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
787 "DispatchGotOrLostPointerCaptureEvent failed");
791 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource
,
792 uint32_t aPointerId
) {
793 if (sSpoofedPointerId
.isSome() || aInputSource
!= SPOOFED_POINTER_INTERFACE
) {
797 sSpoofedPointerId
.emplace(aPointerId
);
800 } // namespace mozilla