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/DocumentInlines.h"
19 #include "mozilla/dom/MouseEventBinding.h"
25 Maybe
<int32_t> PointerEventHandler::sSpoofedPointerId
;
27 // Keeps a map between pointerId and element that currently capturing pointer
28 // with such pointerId. If pointerId is absent in this map then nobody is
29 // capturing it. Additionally keep information about pending capturing content.
30 static nsClassHashtable
<nsUint32HashKey
, PointerCaptureInfo
>*
33 // Keeps information about pointers such as pointerId, activeState, pointerType,
35 static nsClassHashtable
<nsUint32HashKey
, PointerInfo
>* sActivePointersIds
;
37 // Keeps track of which BrowserParent requested pointer capture for a pointer
39 static nsTHashMap
<nsUint32HashKey
, BrowserParent
*>*
40 sPointerCaptureRemoteTargetTable
= nullptr;
43 void PointerEventHandler::InitializeStatics() {
44 MOZ_ASSERT(!sPointerCaptureList
, "InitializeStatics called multiple times!");
46 new nsClassHashtable
<nsUint32HashKey
, PointerCaptureInfo
>;
47 sActivePointersIds
= new nsClassHashtable
<nsUint32HashKey
, PointerInfo
>;
48 if (XRE_IsParentProcess()) {
49 sPointerCaptureRemoteTargetTable
=
50 new nsTHashMap
<nsUint32HashKey
, BrowserParent
*>;
55 void PointerEventHandler::ReleaseStatics() {
56 MOZ_ASSERT(sPointerCaptureList
, "ReleaseStatics called without Initialize!");
57 delete sPointerCaptureList
;
58 sPointerCaptureList
= nullptr;
59 delete sActivePointersIds
;
60 sActivePointersIds
= nullptr;
61 if (sPointerCaptureRemoteTargetTable
) {
62 MOZ_ASSERT(XRE_IsParentProcess());
63 delete sPointerCaptureRemoteTargetTable
;
64 sPointerCaptureRemoteTargetTable
= nullptr;
69 bool PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() {
70 return StaticPrefs::dom_w3c_pointer_events_implicit_capture();
74 void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent
* aEvent
,
75 nsIContent
* aTargetContent
) {
79 switch (aEvent
->mMessage
) {
80 case eMouseEnterIntoWidget
:
81 // In this case we have to know information about available mouse pointers
82 sActivePointersIds
->InsertOrUpdate(
84 MakeUnique
<PointerInfo
>(false, aEvent
->mInputSource
, true, false,
87 MaybeCacheSpoofedPointerID(aEvent
->mInputSource
, aEvent
->pointerId
);
90 // In this case we switch pointer to active state
91 if (WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent()) {
92 // XXXedgar, test could possibly synthesize a mousedown event on a
93 // coordinate outside the browser window and cause aTargetContent to be
94 // nullptr, not sure if this also happens on real usage.
95 sActivePointersIds
->InsertOrUpdate(
96 pointerEvent
->pointerId
,
97 MakeUnique
<PointerInfo
>(
98 true, pointerEvent
->mInputSource
, pointerEvent
->mIsPrimary
,
99 pointerEvent
->mFromTouchEvent
,
100 aTargetContent
? aTargetContent
->OwnerDoc() : nullptr));
101 MaybeCacheSpoofedPointerID(pointerEvent
->mInputSource
,
102 pointerEvent
->pointerId
);
106 // pointercancel means a pointer is unlikely to continue to produce
107 // pointer events. In that case, we should turn off active state or remove
108 // the pointer from active pointers.
110 // In this case we remove information about pointer or turn off active
112 if (WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent()) {
113 if (pointerEvent
->mInputSource
!=
114 MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
115 sActivePointersIds
->InsertOrUpdate(
116 pointerEvent
->pointerId
,
117 MakeUnique
<PointerInfo
>(false, pointerEvent
->mInputSource
,
118 pointerEvent
->mIsPrimary
,
119 pointerEvent
->mFromTouchEvent
, nullptr));
121 sActivePointersIds
->Remove(pointerEvent
->pointerId
);
125 case eMouseExitFromWidget
:
126 // In this case we have to remove information about disappeared mouse
128 sActivePointersIds
->Remove(aEvent
->pointerId
);
131 MOZ_ASSERT_UNREACHABLE("event has invalid type");
137 void PointerEventHandler::RequestPointerCaptureById(uint32_t aPointerId
,
139 SetPointerCaptureById(aPointerId
, aElement
);
141 if (BrowserChild
* browserChild
=
142 BrowserChild::GetFrom(aElement
->OwnerDoc()->GetDocShell())) {
143 browserChild
->SendRequestPointerCapture(
145 [aPointerId
](bool aSuccess
) {
147 PointerEventHandler::ReleasePointerCaptureById(aPointerId
);
150 [](mozilla::ipc::ResponseRejectReason
) {});
155 void PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId
,
157 MOZ_ASSERT(aElement
);
158 sPointerCaptureList
->WithEntryHandle(aPointerId
, [&](auto&& entry
) {
160 entry
.Data()->mPendingElement
= aElement
;
162 entry
.Insert(MakeUnique
<PointerCaptureInfo
>(aElement
));
168 PointerCaptureInfo
* PointerEventHandler::GetPointerCaptureInfo(
169 uint32_t aPointerId
) {
170 PointerCaptureInfo
* pointerCaptureInfo
= nullptr;
171 sPointerCaptureList
->Get(aPointerId
, &pointerCaptureInfo
);
172 return pointerCaptureInfo
;
176 void PointerEventHandler::ReleasePointerCaptureById(uint32_t aPointerId
) {
177 PointerCaptureInfo
* pointerCaptureInfo
= GetPointerCaptureInfo(aPointerId
);
178 if (pointerCaptureInfo
) {
179 if (Element
* pendingElement
= pointerCaptureInfo
->mPendingElement
) {
180 if (BrowserChild
* browserChild
= BrowserChild::GetFrom(
181 pendingElement
->OwnerDoc()->GetDocShell())) {
182 browserChild
->SendReleasePointerCapture(aPointerId
);
185 pointerCaptureInfo
->mPendingElement
= nullptr;
190 void PointerEventHandler::ReleaseAllPointerCapture() {
191 for (const auto& entry
: *sPointerCaptureList
) {
192 PointerCaptureInfo
* data
= entry
.GetWeak();
193 if (data
&& data
->mPendingElement
) {
194 ReleasePointerCaptureById(entry
.GetKey());
200 bool PointerEventHandler::SetPointerCaptureRemoteTarget(
201 uint32_t aPointerId
, dom::BrowserParent
* aBrowserParent
) {
202 MOZ_ASSERT(XRE_IsParentProcess());
203 MOZ_ASSERT(sPointerCaptureRemoteTargetTable
);
204 MOZ_ASSERT(aBrowserParent
);
206 if (PointerLockManager::GetLockedRemoteTarget()) {
210 BrowserParent
* currentRemoteTarget
=
211 PointerEventHandler::GetPointerCapturingRemoteTarget(aPointerId
);
212 if (currentRemoteTarget
&& currentRemoteTarget
!= aBrowserParent
) {
216 sPointerCaptureRemoteTargetTable
->InsertOrUpdate(aPointerId
, aBrowserParent
);
221 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
222 BrowserParent
* aBrowserParent
) {
223 MOZ_ASSERT(XRE_IsParentProcess());
224 MOZ_ASSERT(sPointerCaptureRemoteTargetTable
);
225 MOZ_ASSERT(aBrowserParent
);
227 sPointerCaptureRemoteTargetTable
->RemoveIf([aBrowserParent
](
229 BrowserParent
* browserParent
= iter
.Data();
230 MOZ_ASSERT(browserParent
, "Null BrowserParent in pointer captured table?");
232 return aBrowserParent
== browserParent
;
237 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
238 uint32_t aPointerId
) {
239 MOZ_ASSERT(XRE_IsParentProcess());
240 MOZ_ASSERT(sPointerCaptureRemoteTargetTable
);
242 sPointerCaptureRemoteTargetTable
->Remove(aPointerId
);
246 BrowserParent
* PointerEventHandler::GetPointerCapturingRemoteTarget(
247 uint32_t aPointerId
) {
248 MOZ_ASSERT(XRE_IsParentProcess());
249 MOZ_ASSERT(sPointerCaptureRemoteTargetTable
);
251 return sPointerCaptureRemoteTargetTable
->Get(aPointerId
);
255 void PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget() {
256 MOZ_ASSERT(XRE_IsParentProcess());
257 MOZ_ASSERT(sPointerCaptureRemoteTargetTable
);
259 for (auto iter
= sPointerCaptureRemoteTargetTable
->Iter(); !iter
.Done();
261 BrowserParent
* browserParent
= iter
.Data();
262 MOZ_ASSERT(browserParent
, "Null BrowserParent in pointer captured table?");
264 Unused
<< browserParent
->SendReleaseAllPointerCapture();
270 const PointerInfo
* PointerEventHandler::GetPointerInfo(uint32_t aPointerId
) {
271 return sActivePointersIds
->Get(aPointerId
);
275 void PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent
* aEvent
) {
276 switch (aEvent
->mClass
) {
277 case eMouseEventClass
:
278 ProcessPointerCaptureForMouse(aEvent
->AsMouseEvent());
280 case eTouchEventClass
:
281 ProcessPointerCaptureForTouch(aEvent
->AsTouchEvent());
289 void PointerEventHandler::ProcessPointerCaptureForMouse(
290 WidgetMouseEvent
* aEvent
) {
291 if (!ShouldGeneratePointerEventFromMouse(aEvent
)) {
295 PointerCaptureInfo
* info
= GetPointerCaptureInfo(aEvent
->pointerId
);
296 if (!info
|| info
->mPendingElement
== info
->mOverrideElement
) {
299 WidgetPointerEvent
localEvent(*aEvent
);
300 InitPointerEventFromMouse(&localEvent
, aEvent
, eVoidEvent
);
301 CheckPointerCaptureState(&localEvent
);
305 void PointerEventHandler::ProcessPointerCaptureForTouch(
306 WidgetTouchEvent
* aEvent
) {
307 if (!ShouldGeneratePointerEventFromTouch(aEvent
)) {
311 for (uint32_t i
= 0; i
< aEvent
->mTouches
.Length(); ++i
) {
312 Touch
* touch
= aEvent
->mTouches
[i
];
313 if (!TouchManager::ShouldConvertTouchToPointer(touch
, aEvent
)) {
316 PointerCaptureInfo
* info
= GetPointerCaptureInfo(touch
->Identifier());
317 if (!info
|| info
->mPendingElement
== info
->mOverrideElement
) {
320 WidgetPointerEvent
event(aEvent
->IsTrusted(), eVoidEvent
, aEvent
->mWidget
);
321 InitPointerEventFromTouch(event
, *aEvent
, *touch
);
322 CheckPointerCaptureState(&event
);
327 void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent
* aEvent
) {
328 // Handle pending pointer capture before any pointer events except
329 // gotpointercapture / lostpointercapture.
333 MOZ_ASSERT(aEvent
->mClass
== ePointerEventClass
);
335 PointerCaptureInfo
* captureInfo
= GetPointerCaptureInfo(aEvent
->pointerId
);
337 // When fingerprinting resistance is enabled, we need to map other pointer
338 // ids into the spoofed one. We don't have to do the mapping if the capture
339 // info exists for the non-spoofed pointer id because of we won't allow
340 // content to set pointer capture other than the spoofed one. Thus, it must be
341 // from chrome if the capture info exists in this case. And we don't have to
342 // do anything if the pointer id is the same as the spoofed one.
343 if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check",
344 RFPTarget::PointerEvents
) &&
345 aEvent
->pointerId
!= (uint32_t)GetSpoofedPointerIdForRFP() &&
347 PointerCaptureInfo
* spoofedCaptureInfo
=
348 GetPointerCaptureInfo(GetSpoofedPointerIdForRFP());
350 // We need to check the target element's document should resist
351 // fingerprinting. If not, we don't need to send a capture event
352 // since the capture info of the original pointer id doesn't exist
354 if (!spoofedCaptureInfo
|| !spoofedCaptureInfo
->mPendingElement
||
355 !spoofedCaptureInfo
->mPendingElement
->OwnerDoc()
356 ->ShouldResistFingerprinting(RFPTarget::PointerEvents
)) {
360 captureInfo
= spoofedCaptureInfo
;
364 captureInfo
->mPendingElement
== captureInfo
->mOverrideElement
) {
368 RefPtr
<Element
> overrideElement
= captureInfo
->mOverrideElement
;
369 RefPtr
<Element
> pendingElement
= captureInfo
->mPendingElement
;
371 // Update captureInfo before dispatching event since sPointerCaptureList may
372 // be changed in the pointer event listener.
373 captureInfo
->mOverrideElement
= captureInfo
->mPendingElement
;
374 if (captureInfo
->Empty()) {
375 sPointerCaptureList
->Remove(aEvent
->pointerId
);
378 if (overrideElement
) {
379 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent
,
382 if (pendingElement
) {
383 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent
,
389 void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame
* aFrame
,
390 WidgetEvent
* aEvent
) {
391 MOZ_ASSERT(aEvent
->mMessage
== ePointerDown
);
392 if (!aFrame
|| !IsPointerEventImplicitCaptureForTouchEnabled()) {
395 WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent();
396 NS_WARNING_ASSERTION(pointerEvent
,
397 "Call ImplicitlyCapturePointer with non-pointer event");
398 if (!pointerEvent
->mFromTouchEvent
) {
399 // We only implicitly capture the pointer for touch device.
402 nsCOMPtr
<nsIContent
> target
;
403 aFrame
->GetContentForEvent(aEvent
, getter_AddRefs(target
));
404 while (target
&& !target
->IsElement()) {
405 target
= target
->GetParent();
407 if (NS_WARN_IF(!target
)) {
410 RequestPointerCaptureById(pointerEvent
->pointerId
, target
->AsElement());
414 void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent
* aEvent
) {
416 if (aEvent
->mMessage
!= ePointerUp
&& aEvent
->mMessage
!= ePointerCancel
) {
419 WidgetPointerEvent
* pointerEvent
= aEvent
->AsPointerEvent();
420 ReleasePointerCaptureById(pointerEvent
->pointerId
);
421 CheckPointerCaptureState(pointerEvent
);
425 void PointerEventHandler::MaybeImplicitlyReleasePointerCapture(
426 WidgetGUIEvent
* aEvent
) {
428 const EventMessage pointerEventMessage
=
429 PointerEventHandler::ToPointerEventMessage(aEvent
);
430 if (pointerEventMessage
!= ePointerUp
&&
431 pointerEventMessage
!= ePointerCancel
) {
434 PointerEventHandler::MaybeProcessPointerCapture(aEvent
);
438 Element
* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId
) {
439 PointerCaptureInfo
* pointerCaptureInfo
= GetPointerCaptureInfo(aPointerId
);
440 if (pointerCaptureInfo
) {
441 return pointerCaptureInfo
->mOverrideElement
;
447 Element
* PointerEventHandler::GetPointerCapturingElement(
448 WidgetGUIEvent
* aEvent
) {
449 if ((aEvent
->mClass
!= ePointerEventClass
&&
450 aEvent
->mClass
!= eMouseEventClass
) ||
451 aEvent
->mMessage
== ePointerDown
|| aEvent
->mMessage
== eMouseDown
) {
452 // Pointer capture should only be applied to all pointer events and mouse
453 // events except ePointerDown and eMouseDown;
457 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
461 return GetPointerCapturingElement(mouseEvent
->pointerId
);
465 void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent
* aContent
) {
466 // We should check that aChild does not contain pointer capturing elements.
467 // If it does we should release the pointer capture for the elements.
468 if (!sPointerCaptureList
->IsEmpty()) {
469 for (const auto& entry
: *sPointerCaptureList
) {
470 PointerCaptureInfo
* data
= entry
.GetWeak();
471 if (data
&& data
->mPendingElement
&&
472 data
->mPendingElement
->IsInclusiveDescendantOf(aContent
)) {
473 ReleasePointerCaptureById(entry
.GetKey());
480 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
481 WidgetPointerEvent
* aPointerEvent
, WidgetGUIEvent
* aMouseOrTouchEvent
) {
482 if (!aPointerEvent
->mIsPrimary
|| aPointerEvent
->mMessage
== ePointerDown
) {
485 PointerInfo
* pointerInfo
= nullptr;
486 if (!sActivePointersIds
->Get(aPointerEvent
->pointerId
, &pointerInfo
) ||
488 // The PointerInfo for active pointer should be added for normal cases. But
489 // in some cases, we may receive mouse events before adding PointerInfo in
490 // sActivePointersIds. (e.g. receive mousemove before
491 // eMouseEnterIntoWidget). In these cases, we could ignore them because they
492 // are not the events between a DefaultPrevented pointerdown and the
493 // corresponding pointerup.
496 if (!pointerInfo
->mPreventMouseEventByContent
) {
499 aMouseOrTouchEvent
->PreventDefault(false);
500 aMouseOrTouchEvent
->mFlags
.mOnlyChromeDispatch
= true;
501 if (aPointerEvent
->mMessage
== ePointerUp
) {
502 pointerInfo
->mPreventMouseEventByContent
= false;
507 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
508 WidgetPointerEvent
* aPointerEvent
, WidgetGUIEvent
* aMouseOrTouchEvent
) {
509 if (!aPointerEvent
->mIsPrimary
|| aPointerEvent
->mMessage
!= ePointerDown
||
510 !aPointerEvent
->DefaultPreventedByContent()) {
513 PointerInfo
* pointerInfo
= nullptr;
514 if (!sActivePointersIds
->Get(aPointerEvent
->pointerId
, &pointerInfo
) ||
516 // We already added the PointerInfo for active pointer when
517 // PresShell::HandleEvent handling pointerdown event.
519 MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
520 #endif // #ifdef DEBUG
523 // PreventDefault only applied for active pointers.
524 if (!pointerInfo
->mActiveState
) {
527 aMouseOrTouchEvent
->PreventDefault(false);
528 aMouseOrTouchEvent
->mFlags
.mOnlyChromeDispatch
= true;
529 pointerInfo
->mPreventMouseEventByContent
= true;
533 void PointerEventHandler::InitPointerEventFromMouse(
534 WidgetPointerEvent
* aPointerEvent
, WidgetMouseEvent
* aMouseEvent
,
535 EventMessage aMessage
) {
536 MOZ_ASSERT(aPointerEvent
);
537 MOZ_ASSERT(aMouseEvent
);
538 aPointerEvent
->pointerId
= aMouseEvent
->pointerId
;
539 aPointerEvent
->mInputSource
= aMouseEvent
->mInputSource
;
540 aPointerEvent
->mMessage
= aMessage
;
541 aPointerEvent
->mButton
= aMouseEvent
->mMessage
== eMouseMove
542 ? MouseButton::eNotPressed
543 : aMouseEvent
->mButton
;
545 aPointerEvent
->mButtons
= aMouseEvent
->mButtons
;
546 aPointerEvent
->mPressure
=
547 aPointerEvent
->mButtons
548 ? aMouseEvent
->mPressure
? aMouseEvent
->mPressure
: 0.5f
553 void PointerEventHandler::InitPointerEventFromTouch(
554 WidgetPointerEvent
& aPointerEvent
, const WidgetTouchEvent
& aTouchEvent
,
555 const mozilla::dom::Touch
& aTouch
) {
556 // Use mButton/mButtons only when mButton got a value (from pen input)
557 int16_t button
= aTouchEvent
.mMessage
== eTouchMove
? MouseButton::eNotPressed
558 : aTouchEvent
.mButton
!= MouseButton::eNotPressed
559 ? aTouchEvent
.mButton
560 : MouseButton::ePrimary
;
561 int16_t buttons
= aTouchEvent
.mMessage
== eTouchEnd
562 ? MouseButtonsFlag::eNoButtons
563 : aTouchEvent
.mButton
!= MouseButton::eNotPressed
564 ? aTouchEvent
.mButtons
565 : MouseButtonsFlag::ePrimaryFlag
;
567 // Only the first touch would be the primary pointer.
568 aPointerEvent
.mIsPrimary
= aTouchEvent
.mMessage
== eTouchStart
569 ? !HasActiveTouchPointer()
570 : GetPointerPrimaryState(aTouch
.Identifier());
571 aPointerEvent
.pointerId
= aTouch
.Identifier();
572 aPointerEvent
.mRefPoint
= aTouch
.mRefPoint
;
573 aPointerEvent
.mModifiers
= aTouchEvent
.mModifiers
;
574 aPointerEvent
.mWidth
= aTouch
.RadiusX(CallerType::System
);
575 aPointerEvent
.mHeight
= aTouch
.RadiusY(CallerType::System
);
576 aPointerEvent
.tiltX
= aTouch
.tiltX
;
577 aPointerEvent
.tiltY
= aTouch
.tiltY
;
578 aPointerEvent
.twist
= aTouch
.twist
;
579 aPointerEvent
.mTimeStamp
= aTouchEvent
.mTimeStamp
;
580 aPointerEvent
.mFlags
= aTouchEvent
.mFlags
;
581 aPointerEvent
.mButton
= button
;
582 aPointerEvent
.mButtons
= buttons
;
583 aPointerEvent
.mInputSource
= aTouchEvent
.mInputSource
;
584 aPointerEvent
.mFromTouchEvent
= true;
585 aPointerEvent
.mPressure
= aTouch
.mForce
;
589 EventMessage
PointerEventHandler::ToPointerEventMessage(
590 const WidgetGUIEvent
* aMouseOrTouchEvent
) {
591 MOZ_ASSERT(aMouseOrTouchEvent
);
593 switch (aMouseOrTouchEvent
->mMessage
) {
597 return aMouseOrTouchEvent
->AsMouseEvent()->mButtons
? ePointerMove
600 const WidgetMouseEvent
* mouseEvent
= aMouseOrTouchEvent
->AsMouseEvent();
601 return mouseEvent
->mButtons
& ~nsContentUtils::GetButtonsFlagForButton(
613 case eTouchPointerCancel
:
614 return ePointerCancel
;
621 void PointerEventHandler::DispatchPointerFromMouseOrTouch(
622 PresShell
* aShell
, nsIFrame
* aEventTargetFrame
,
623 nsIContent
* aEventTargetContent
, WidgetGUIEvent
* aMouseOrTouchEvent
,
624 bool aDontRetargetEvents
, nsEventStatus
* aStatus
,
625 nsIContent
** aMouseOrTouchEventTarget
/* = nullptr */) {
626 MOZ_ASSERT(aEventTargetFrame
|| aEventTargetContent
);
627 MOZ_ASSERT(aMouseOrTouchEvent
);
629 EventMessage pointerMessage
= eVoidEvent
;
630 if (aMouseOrTouchEvent
->mClass
== eMouseEventClass
) {
631 WidgetMouseEvent
* mouseEvent
= aMouseOrTouchEvent
->AsMouseEvent();
632 // Don't dispatch pointer events caused by a mouse when simulating touch
634 Document
* doc
= aShell
->GetDocument();
639 BrowsingContext
* bc
= doc
->GetBrowsingContext();
640 if (bc
&& bc
->TouchEventsOverride() == TouchEventsOverride::Enabled
&&
645 // 1. If it is not mouse then it is likely will come as touch event
646 // 2. We don't synthesize pointer events for those events that are not
647 // dispatched to DOM.
648 if (!mouseEvent
->convertToPointer
||
649 !aMouseOrTouchEvent
->IsAllowedToDispatchDOMEvent()) {
653 pointerMessage
= PointerEventHandler::ToPointerEventMessage(mouseEvent
);
654 if (pointerMessage
== eVoidEvent
) {
657 WidgetPointerEvent
event(*mouseEvent
);
658 InitPointerEventFromMouse(&event
, mouseEvent
, pointerMessage
);
659 event
.convertToPointer
= mouseEvent
->convertToPointer
= false;
660 RefPtr
<PresShell
> shell(aShell
);
661 if (!aEventTargetFrame
) {
662 shell
= PresShell::GetShellForEventTarget(nullptr, aEventTargetContent
);
667 PreHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
668 // Dispatch pointer event to the same target which is found by the
669 // corresponding mouse event.
670 shell
->HandleEventWithTarget(&event
, aEventTargetFrame
, aEventTargetContent
,
671 aStatus
, true, aMouseOrTouchEventTarget
);
672 PostHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
673 } else if (aMouseOrTouchEvent
->mClass
== eTouchEventClass
) {
674 WidgetTouchEvent
* touchEvent
= aMouseOrTouchEvent
->AsTouchEvent();
675 // loop over all touches and dispatch pointer events on each touch
677 pointerMessage
= PointerEventHandler::ToPointerEventMessage(touchEvent
);
678 if (pointerMessage
== eVoidEvent
) {
681 RefPtr
<PresShell
> shell(aShell
);
682 for (uint32_t i
= 0; i
< touchEvent
->mTouches
.Length(); ++i
) {
683 Touch
* touch
= touchEvent
->mTouches
[i
];
684 if (!TouchManager::ShouldConvertTouchToPointer(touch
, touchEvent
)) {
688 WidgetPointerEvent
event(touchEvent
->IsTrusted(), pointerMessage
,
689 touchEvent
->mWidget
);
691 InitPointerEventFromTouch(event
, *touchEvent
, *touch
);
692 event
.convertToPointer
= touch
->convertToPointer
= false;
693 event
.mCoalescedWidgetEvents
= touch
->mCoalescedWidgetEvents
;
694 if (aMouseOrTouchEvent
->mMessage
== eTouchStart
) {
695 // We already did hit test for touchstart in PresShell. We should
696 // dispatch pointerdown to the same target as touchstart.
697 nsCOMPtr
<nsIContent
> content
=
698 nsIContent::FromEventTargetOrNull(touch
->mTarget
);
703 nsIFrame
* frame
= content
->GetPrimaryFrame();
704 shell
= PresShell::GetShellForEventTarget(frame
, content
);
709 PreHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
710 shell
->HandleEventWithTarget(&event
, frame
, content
, aStatus
, true,
711 aMouseOrTouchEventTarget
);
712 PostHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
714 // We didn't hit test for other touch events. Spec doesn't mention that
715 // all pointer events should be dispatched to the same target as their
716 // corresponding touch events. Call PresShell::HandleEvent so that we do
717 // hit test for pointer events.
718 // FIXME: If aDontRetargetEvents is true and the event is fired on
719 // different document, we cannot track the pointer event target when
720 // it's removed from the tree.
721 PreHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
722 shell
->HandleEvent(aEventTargetFrame
, &event
, aDontRetargetEvents
,
724 PostHandlePointerEventsPreventDefault(&event
, aMouseOrTouchEvent
);
731 void PointerEventHandler::NotifyDestroyPresContext(
732 nsPresContext
* aPresContext
) {
733 // Clean up pointer capture info
734 for (auto iter
= sPointerCaptureList
->Iter(); !iter
.Done(); iter
.Next()) {
735 PointerCaptureInfo
* data
= iter
.UserData();
736 MOZ_ASSERT(data
, "how could we have a null PointerCaptureInfo here?");
737 if (data
->mPendingElement
&&
738 data
->mPendingElement
->GetPresContext(Element::eForComposedDoc
) ==
740 data
->mPendingElement
= nullptr;
742 if (data
->mOverrideElement
&&
743 data
->mOverrideElement
->GetPresContext(Element::eForComposedDoc
) ==
745 data
->mOverrideElement
= nullptr;
751 // Clean up active pointer info
752 for (auto iter
= sActivePointersIds
->Iter(); !iter
.Done(); iter
.Next()) {
753 PointerInfo
* data
= iter
.UserData();
754 MOZ_ASSERT(data
, "how could we have a null PointerInfo here?");
755 if (data
->mActiveDocument
&&
756 data
->mActiveDocument
->GetPresContext() == aPresContext
) {
762 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent
& aEvent
) {
764 if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) {
765 // WM_POINTER does not support drag and drop, see bug 1692277
766 return (aEvent
.mInputSource
!= dom::MouseEvent_Binding::MOZ_SOURCE_PEN
&&
767 aEvent
.mReason
!= WidgetMouseEvent::eSynthesized
); // bug 1692151
774 uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId
) {
775 PointerInfo
* pointerInfo
= nullptr;
776 if (sActivePointersIds
->Get(aPointerId
, &pointerInfo
) && pointerInfo
) {
777 return pointerInfo
->mPointerType
;
779 return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
783 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId
) {
784 PointerInfo
* pointerInfo
= nullptr;
785 if (sActivePointersIds
->Get(aPointerId
, &pointerInfo
) && pointerInfo
) {
786 return pointerInfo
->mPrimaryState
;
792 bool PointerEventHandler::HasActiveTouchPointer() {
793 for (auto iter
= sActivePointersIds
->ConstIter(); !iter
.Done(); iter
.Next()) {
794 if (iter
.Data()->mFromTouchEvent
) {
802 void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
803 bool aIsGotCapture
, const WidgetPointerEvent
* aPointerEvent
,
804 Element
* aCaptureTarget
) {
805 Document
* targetDoc
= aCaptureTarget
->OwnerDoc();
806 RefPtr
<PresShell
> presShell
= targetDoc
->GetPresShell();
807 if (NS_WARN_IF(!presShell
|| presShell
->IsDestroying())) {
811 if (!aIsGotCapture
&& !aCaptureTarget
->IsInComposedDoc()) {
812 // If the capturing element was removed from the DOM tree, fire
813 // ePointerLostCapture at the document.
814 PointerEventInit init
;
815 init
.mPointerId
= aPointerEvent
->pointerId
;
816 init
.mBubbles
= true;
817 init
.mComposed
= true;
818 ConvertPointerTypeToString(aPointerEvent
->mInputSource
, init
.mPointerType
);
819 init
.mIsPrimary
= aPointerEvent
->mIsPrimary
;
820 RefPtr
<PointerEvent
> event
;
821 event
= PointerEvent::Constructor(aCaptureTarget
, u
"lostpointercapture"_ns
,
823 targetDoc
->DispatchEvent(*event
);
826 nsEventStatus status
= nsEventStatus_eIgnore
;
827 WidgetPointerEvent
localEvent(
828 aPointerEvent
->IsTrusted(),
829 aIsGotCapture
? ePointerGotCapture
: ePointerLostCapture
,
830 aPointerEvent
->mWidget
);
832 localEvent
.AssignPointerEventData(*aPointerEvent
, true);
833 DebugOnly
<nsresult
> rv
= presShell
->HandleEventWithTarget(
834 &localEvent
, aCaptureTarget
->GetPrimaryFrame(), aCaptureTarget
, &status
);
836 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
837 "DispatchGotOrLostPointerCaptureEvent failed");
841 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource
,
842 uint32_t aPointerId
) {
843 if (sSpoofedPointerId
.isSome() || aInputSource
!= SPOOFED_POINTER_INTERFACE
) {
847 sSpoofedPointerId
.emplace(aPointerId
);
850 } // namespace mozilla