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() &&
340 aEvent
->pointerId
!= (uint32_t)GetSpoofedPointerIdForRFP() &&
342 PointerCaptureInfo
* spoofedCaptureInfo
=
343 GetPointerCaptureInfo(GetSpoofedPointerIdForRFP());
345 // We need to check the target element is content or chrome. If it is chrome
346 // we don't need to send a capture event since the capture info of the
347 // original pointer id doesn't exist in the case.
348 if (!spoofedCaptureInfo
||
349 (spoofedCaptureInfo
->mPendingElement
&&
350 spoofedCaptureInfo
->mPendingElement
->IsInChromeDocument())) {
354 captureInfo
= spoofedCaptureInfo
;
358 captureInfo
->mPendingElement
== captureInfo
->mOverrideElement
) {
362 RefPtr
<Element
> overrideElement
= captureInfo
->mOverrideElement
;
363 RefPtr
<Element
> pendingElement
= captureInfo
->mPendingElement
;
365 // Update captureInfo before dispatching event since sPointerCaptureList may
366 // be changed in the pointer event listener.
367 captureInfo
->mOverrideElement
= captureInfo
->mPendingElement
;
368 if (captureInfo
->Empty()) {
369 sPointerCaptureList
->Remove(aEvent
->pointerId
);
372 if (overrideElement
) {
373 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent
,
376 if (pendingElement
) {
377 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent
,
383 void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame
* aFrame
,
384 WidgetEvent
* aEvent
) {
385 MOZ_ASSERT(aEvent
->mMessage
== ePointerDown
);
386 if (!aFrame
|| !IsPointerEventImplicitCaptureForTouchEnabled()) {
389 WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent();
390 NS_WARNING_ASSERTION(pointerEvent
,
391 "Call ImplicitlyCapturePointer with non-pointer event");
392 if (!pointerEvent
->mFromTouchEvent
) {
393 // We only implicitly capture the pointer for touch device.
396 nsCOMPtr
<nsIContent
> target
;
397 aFrame
->GetContentForEvent(aEvent
, getter_AddRefs(target
));
398 while (target
&& !target
->IsElement()) {
399 target
= target
->GetParent();
401 if (NS_WARN_IF(!target
)) {
404 RequestPointerCaptureById(pointerEvent
->pointerId
, target
->AsElement());
408 void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent
* aEvent
) {
410 if (aEvent
->mMessage
!= ePointerUp
&& aEvent
->mMessage
!= ePointerCancel
) {
413 WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent();
414 ReleasePointerCaptureById(pointerEvent
->pointerId
);
415 CheckPointerCaptureState(pointerEvent
);
419 Element
* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId
) {
420 PointerCaptureInfo
* pointerCaptureInfo
= GetPointerCaptureInfo(aPointerId
);
421 if (pointerCaptureInfo
) {
422 return pointerCaptureInfo
->mOverrideElement
;
428 Element
* PointerEventHandler::GetPointerCapturingElement(
429 WidgetGUIEvent
* aEvent
) {
430 if ((aEvent
->mClass
!= ePointerEventClass
&&
431 aEvent
->mClass
!= eMouseEventClass
) ||
432 aEvent
->mMessage
== ePointerDown
|| aEvent
->mMessage
== eMouseDown
) {
433 // Pointer capture should only be applied to all pointer events and mouse
434 // events except ePointerDown and eMouseDown;
438 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
442 return GetPointerCapturingElement(mouseEvent
->pointerId
);
446 void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent
* aContent
) {
447 // We should check that aChild does not contain pointer capturing elements.
448 // If it does we should release the pointer capture for the elements.
449 for (const auto& entry
: *sPointerCaptureList
) {
450 PointerCaptureInfo
* data
= entry
.GetWeak();
451 if (data
&& data
->mPendingElement
&&
452 data
->mPendingElement
->IsInclusiveDescendantOf(aContent
)) {
453 ReleasePointerCaptureById(entry
.GetKey());
459 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
460 WidgetPointerEvent
* aPointerEvent
, WidgetGUIEvent
* aMouseOrTouchEvent
) {
461 if (!aPointerEvent
->mIsPrimary
|| aPointerEvent
->mMessage
== ePointerDown
) {
464 PointerInfo
* pointerInfo
= nullptr;
465 if (!sActivePointersIds
->Get(aPointerEvent
->pointerId
, &pointerInfo
) ||
467 // The PointerInfo for active pointer should be added for normal cases. But
468 // in some cases, we may receive mouse events before adding PointerInfo in
469 // sActivePointersIds. (e.g. receive mousemove before
470 // eMouseEnterIntoWidget). In these cases, we could ignore them because they
471 // are not the events between a DefaultPrevented pointerdown and the
472 // corresponding pointerup.
475 if (!pointerInfo
->mPreventMouseEventByContent
) {
478 aMouseOrTouchEvent
->PreventDefault(false);
479 aMouseOrTouchEvent
->mFlags
.mOnlyChromeDispatch
= true;
480 if (aPointerEvent
->mMessage
== ePointerUp
) {
481 pointerInfo
->mPreventMouseEventByContent
= false;
486 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
487 WidgetPointerEvent
* aPointerEvent
, WidgetGUIEvent
* aMouseOrTouchEvent
) {
488 if (!aPointerEvent
->mIsPrimary
|| aPointerEvent
->mMessage
!= ePointerDown
||
489 !aPointerEvent
->DefaultPreventedByContent()) {
492 PointerInfo
* pointerInfo
= nullptr;
493 if (!sActivePointersIds
->Get(aPointerEvent
->pointerId
, &pointerInfo
) ||
495 // We already added the PointerInfo for active pointer when
496 // PresShell::HandleEvent handling pointerdown event.
498 MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
499 #endif // #ifdef DEBUG
502 // PreventDefault only applied for active pointers.
503 if (!pointerInfo
->mActiveState
) {
506 aMouseOrTouchEvent
->PreventDefault(false);
507 aMouseOrTouchEvent
->mFlags
.mOnlyChromeDispatch
= true;
508 pointerInfo
->mPreventMouseEventByContent
= true;
512 void PointerEventHandler::InitPointerEventFromMouse(
513 WidgetPointerEvent
* aPointerEvent
, WidgetMouseEvent
* aMouseEvent
,
514 EventMessage aMessage
) {
515 MOZ_ASSERT(aPointerEvent
);
516 MOZ_ASSERT(aMouseEvent
);
517 aPointerEvent
->pointerId
= aMouseEvent
->pointerId
;
518 aPointerEvent
->mInputSource
= aMouseEvent
->mInputSource
;
519 aPointerEvent
->mMessage
= aMessage
;
520 aPointerEvent
->mButton
= aMouseEvent
->mMessage
== eMouseMove
521 ? MouseButton::eNotPressed
522 : aMouseEvent
->mButton
;
524 aPointerEvent
->mButtons
= aMouseEvent
->mButtons
;
525 aPointerEvent
->mPressure
=
526 aPointerEvent
->mButtons
527 ? aMouseEvent
->mPressure
? aMouseEvent
->mPressure
: 0.5f
532 void PointerEventHandler::InitPointerEventFromTouch(
533 WidgetPointerEvent
& aPointerEvent
, const WidgetTouchEvent
& aTouchEvent
,
534 const mozilla::dom::Touch
& aTouch
, bool aIsPrimary
) {
535 // Use mButton/mButtons only when mButton got a value (from pen input)
536 int16_t button
= aTouchEvent
.mMessage
== eTouchMove
? MouseButton::eNotPressed
537 : aTouchEvent
.mButton
!= MouseButton::eNotPressed
538 ? aTouchEvent
.mButton
539 : MouseButton::ePrimary
;
540 int16_t buttons
= aTouchEvent
.mMessage
== eTouchEnd
541 ? MouseButtonsFlag::eNoButtons
542 : aTouchEvent
.mButton
!= MouseButton::eNotPressed
543 ? aTouchEvent
.mButtons
544 : MouseButtonsFlag::ePrimaryFlag
;
546 aPointerEvent
.mIsPrimary
= aIsPrimary
;
547 aPointerEvent
.pointerId
= aTouch
.Identifier();
548 aPointerEvent
.mRefPoint
= aTouch
.mRefPoint
;
549 aPointerEvent
.mModifiers
= aTouchEvent
.mModifiers
;
550 aPointerEvent
.mWidth
= aTouch
.RadiusX(CallerType::System
);
551 aPointerEvent
.mHeight
= aTouch
.RadiusY(CallerType::System
);
552 aPointerEvent
.tiltX
= aTouch
.tiltX
;
553 aPointerEvent
.tiltY
= aTouch
.tiltY
;
554 aPointerEvent
.twist
= aTouch
.twist
;
555 aPointerEvent
.mTime
= aTouchEvent
.mTime
;
556 aPointerEvent
.mTimeStamp
= aTouchEvent
.mTimeStamp
;
557 aPointerEvent
.mFlags
= aTouchEvent
.mFlags
;
558 aPointerEvent
.mButton
= button
;
559 aPointerEvent
.mButtons
= buttons
;
560 aPointerEvent
.mInputSource
= aTouchEvent
.mInputSource
;
561 aPointerEvent
.mFromTouchEvent
= true;
562 aPointerEvent
.mPressure
= aTouch
.mForce
;
566 void PointerEventHandler::DispatchPointerFromMouseOrTouch(
567 PresShell
* aShell
, nsIFrame
* aFrame
, nsIContent
* aContent
,
568 WidgetGUIEvent
* aEvent
, bool aDontRetargetEvents
, nsEventStatus
* aStatus
,
569 nsIContent
** aTargetContent
) {
570 MOZ_ASSERT(aFrame
|| aContent
);
573 EventMessage pointerMessage
= eVoidEvent
;
574 if (aEvent
->mClass
== eMouseEventClass
) {
575 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
576 // Don't dispatch pointer events caused by a mouse when simulating touch
578 Document
* doc
= aShell
->GetDocument();
583 BrowsingContext
* bc
= doc
->GetBrowsingContext();
584 if (bc
&& bc
->TouchEventsOverride() == TouchEventsOverride::Enabled
&&
589 // 1. If it is not mouse then it is likely will come as touch event
590 // 2. We don't synthesize pointer events for those events that are not
591 // dispatched to DOM.
592 if (!mouseEvent
->convertToPointer
||
593 !aEvent
->IsAllowedToDispatchDOMEvent()) {
597 switch (mouseEvent
->mMessage
) {
599 pointerMessage
= ePointerMove
;
602 pointerMessage
= mouseEvent
->mButtons
? ePointerMove
: ePointerUp
;
606 mouseEvent
->mButtons
& ~nsContentUtils::GetButtonsFlagForButton(
615 WidgetPointerEvent
event(*mouseEvent
);
616 InitPointerEventFromMouse(&event
, mouseEvent
, pointerMessage
);
617 event
.convertToPointer
= mouseEvent
->convertToPointer
= false;
618 RefPtr
<PresShell
> shell(aShell
);
620 shell
= PresShell::GetShellForEventTarget(nullptr, aContent
);
625 PreHandlePointerEventsPreventDefault(&event
, aEvent
);
626 // Dispatch pointer event to the same target which is found by the
627 // corresponding mouse event.
628 shell
->HandleEventWithTarget(&event
, aFrame
, aContent
, aStatus
, true,
630 PostHandlePointerEventsPreventDefault(&event
, aEvent
);
631 } else if (aEvent
->mClass
== eTouchEventClass
) {
632 WidgetTouchEvent
* touchEvent
= aEvent
->AsTouchEvent();
633 // loop over all touches and dispatch pointer events on each touch
635 switch (touchEvent
->mMessage
) {
637 pointerMessage
= ePointerMove
;
640 pointerMessage
= ePointerUp
;
643 pointerMessage
= ePointerDown
;
646 case eTouchPointerCancel
:
647 pointerMessage
= ePointerCancel
;
653 RefPtr
<PresShell
> shell(aShell
);
654 for (uint32_t i
= 0; i
< touchEvent
->mTouches
.Length(); ++i
) {
655 Touch
* touch
= touchEvent
->mTouches
[i
];
656 if (!TouchManager::ShouldConvertTouchToPointer(touch
, touchEvent
)) {
660 WidgetPointerEvent
event(touchEvent
->IsTrusted(), pointerMessage
,
661 touchEvent
->mWidget
);
663 InitPointerEventFromTouch(event
, *touchEvent
, *touch
, i
== 0);
664 event
.convertToPointer
= touch
->convertToPointer
= false;
665 event
.mCoalescedWidgetEvents
= touch
->mCoalescedWidgetEvents
;
666 if (aEvent
->mMessage
== eTouchStart
) {
667 // We already did hit test for touchstart in PresShell. We should
668 // dispatch pointerdown to the same target as touchstart.
669 nsCOMPtr
<nsIContent
> content
=
670 nsIContent::FromEventTargetOrNull(touch
->mTarget
);
675 nsIFrame
* frame
= content
->GetPrimaryFrame();
676 shell
= PresShell::GetShellForEventTarget(frame
, content
);
681 PreHandlePointerEventsPreventDefault(&event
, aEvent
);
682 shell
->HandleEventWithTarget(&event
, frame
, content
, aStatus
, true,
684 PostHandlePointerEventsPreventDefault(&event
, aEvent
);
686 // We didn't hit test for other touch events. Spec doesn't mention that
687 // all pointer events should be dispatched to the same target as their
688 // corresponding touch events. Call PresShell::HandleEvent so that we do
689 // hit test for pointer events.
690 PreHandlePointerEventsPreventDefault(&event
, aEvent
);
691 shell
->HandleEvent(aFrame
, &event
, aDontRetargetEvents
, aStatus
);
692 PostHandlePointerEventsPreventDefault(&event
, aEvent
);
699 void PointerEventHandler::NotifyDestroyPresContext(
700 nsPresContext
* aPresContext
) {
701 // Clean up pointer capture info
702 for (auto iter
= sPointerCaptureList
->Iter(); !iter
.Done(); iter
.Next()) {
703 PointerCaptureInfo
* data
= iter
.UserData();
704 MOZ_ASSERT(data
, "how could we have a null PointerCaptureInfo here?");
705 if (data
->mPendingElement
&&
706 data
->mPendingElement
->GetPresContext(Element::eForComposedDoc
) ==
708 data
->mPendingElement
= nullptr;
710 if (data
->mOverrideElement
&&
711 data
->mOverrideElement
->GetPresContext(Element::eForComposedDoc
) ==
713 data
->mOverrideElement
= nullptr;
721 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent
& aEvent
) {
723 if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) {
724 // WM_POINTER does not support drag and drop, see bug 1692277
725 return (aEvent
.mInputSource
!= dom::MouseEvent_Binding::MOZ_SOURCE_PEN
&&
726 aEvent
.mReason
!= WidgetMouseEvent::eSynthesized
); // bug 1692151
733 uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId
) {
734 PointerInfo
* pointerInfo
= nullptr;
735 if (sActivePointersIds
->Get(aPointerId
, &pointerInfo
) && pointerInfo
) {
736 return pointerInfo
->mPointerType
;
738 return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
742 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId
) {
743 PointerInfo
* pointerInfo
= nullptr;
744 if (sActivePointersIds
->Get(aPointerId
, &pointerInfo
) && pointerInfo
) {
745 return pointerInfo
->mPrimaryState
;
751 void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
752 bool aIsGotCapture
, const WidgetPointerEvent
* aPointerEvent
,
753 Element
* aCaptureTarget
) {
754 Document
* targetDoc
= aCaptureTarget
->OwnerDoc();
755 RefPtr
<PresShell
> presShell
= targetDoc
->GetPresShell();
756 if (NS_WARN_IF(!presShell
|| presShell
->IsDestroying())) {
760 if (!aIsGotCapture
&& !aCaptureTarget
->IsInComposedDoc()) {
761 // If the capturing element was removed from the DOM tree, fire
762 // ePointerLostCapture at the document.
763 PointerEventInit init
;
764 init
.mPointerId
= aPointerEvent
->pointerId
;
765 init
.mBubbles
= true;
766 init
.mComposed
= true;
767 ConvertPointerTypeToString(aPointerEvent
->mInputSource
, init
.mPointerType
);
768 init
.mIsPrimary
= aPointerEvent
->mIsPrimary
;
769 RefPtr
<PointerEvent
> event
;
770 event
= PointerEvent::Constructor(aCaptureTarget
, u
"lostpointercapture"_ns
,
772 targetDoc
->DispatchEvent(*event
);
775 nsEventStatus status
= nsEventStatus_eIgnore
;
776 WidgetPointerEvent
localEvent(
777 aPointerEvent
->IsTrusted(),
778 aIsGotCapture
? ePointerGotCapture
: ePointerLostCapture
,
779 aPointerEvent
->mWidget
);
781 localEvent
.AssignPointerEventData(*aPointerEvent
, true);
782 DebugOnly
<nsresult
> rv
= presShell
->HandleEventWithTarget(
783 &localEvent
, aCaptureTarget
->GetPrimaryFrame(), aCaptureTarget
, &status
);
785 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
786 "DispatchGotOrLostPointerCaptureEvent failed");
790 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource
,
791 uint32_t aPointerId
) {
792 if (sSpoofedPointerId
.isSome() || aInputSource
!= SPOOFED_POINTER_INTERFACE
) {
796 sSpoofedPointerId
.emplace(aPointerId
);
799 } // namespace mozilla