Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / events / PointerEventHandler.cpp
blob608e864b611ed4a2023e11c67f24a80a6c3458a5
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"
9 #include "nsIFrame.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"
20 namespace mozilla {
22 using namespace dom;
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>*
30 sPointerCaptureList;
32 // Keeps information about pointers such as pointerId, activeState, pointerType,
33 // primaryState
34 static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
36 // Keeps track of which BrowserParent requested pointer capture for a pointer
37 // id.
38 static nsTHashMap<nsUint32HashKey, BrowserParent*>*
39 sPointerCaptureRemoteTargetTable = nullptr;
41 /* static */
42 void PointerEventHandler::InitializeStatics() {
43 MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
44 sPointerCaptureList =
45 new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
46 sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
47 if (XRE_IsParentProcess()) {
48 sPointerCaptureRemoteTargetTable =
49 new nsTHashMap<nsUint32HashKey, BrowserParent*>;
53 /* static */
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;
67 /* static */
68 bool PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() {
69 return StaticPrefs::dom_w3c_pointer_events_implicit_capture();
72 /* static */
73 void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent,
74 nsIContent* aTargetContent) {
75 if (!aEvent) {
76 return;
78 switch (aEvent->mMessage) {
79 case eMouseEnterIntoWidget:
80 // In this case we have to know information about available mouse pointers
81 sActivePointersIds->InsertOrUpdate(
82 aEvent->pointerId,
83 MakeUnique<PointerInfo>(false, aEvent->mInputSource, true, nullptr));
85 MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId);
86 break;
87 case ePointerDown:
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);
101 break;
102 case ePointerCancel:
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.
106 case ePointerUp:
107 // In this case we remove information about pointer or turn off active
108 // state
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));
116 } else {
117 sActivePointersIds->Remove(pointerEvent->pointerId);
120 break;
121 case eMouseExitFromWidget:
122 // In this case we have to remove information about disappeared mouse
123 // pointers
124 sActivePointersIds->Remove(aEvent->pointerId);
125 break;
126 default:
127 MOZ_ASSERT_UNREACHABLE("event has invalid type");
128 break;
132 /* static */
133 void PointerEventHandler::RequestPointerCaptureById(uint32_t aPointerId,
134 Element* aElement) {
135 SetPointerCaptureById(aPointerId, aElement);
137 if (BrowserChild* browserChild =
138 BrowserChild::GetFrom(aElement->OwnerDoc()->GetDocShell())) {
139 browserChild->SendRequestPointerCapture(
140 aPointerId,
141 [aPointerId](bool aSuccess) {
142 if (!aSuccess) {
143 PointerEventHandler::ReleasePointerCaptureById(aPointerId);
146 [](mozilla::ipc::ResponseRejectReason) {});
150 /* static */
151 void PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId,
152 Element* aElement) {
153 MOZ_ASSERT(aElement);
154 sPointerCaptureList->WithEntryHandle(aPointerId, [&](auto&& entry) {
155 if (entry) {
156 entry.Data()->mPendingElement = aElement;
157 } else {
158 entry.Insert(MakeUnique<PointerCaptureInfo>(aElement));
163 /* static */
164 PointerCaptureInfo* PointerEventHandler::GetPointerCaptureInfo(
165 uint32_t aPointerId) {
166 PointerCaptureInfo* pointerCaptureInfo = nullptr;
167 sPointerCaptureList->Get(aPointerId, &pointerCaptureInfo);
168 return pointerCaptureInfo;
171 /* static */
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;
185 /* static */
186 void PointerEventHandler::ReleaseAllPointerCapture() {
187 for (const auto& entry : *sPointerCaptureList) {
188 PointerCaptureInfo* data = entry.GetWeak();
189 if (data && data->mPendingElement) {
190 ReleasePointerCaptureById(entry.GetKey());
195 /* static */
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()) {
203 return false;
206 BrowserParent* currentRemoteTarget =
207 PointerEventHandler::GetPointerCapturingRemoteTarget(aPointerId);
208 if (currentRemoteTarget && currentRemoteTarget != aBrowserParent) {
209 return false;
212 sPointerCaptureRemoteTargetTable->InsertOrUpdate(aPointerId, aBrowserParent);
213 return true;
216 /* static */
217 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
218 BrowserParent* aBrowserParent) {
219 MOZ_ASSERT(XRE_IsParentProcess());
220 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
221 MOZ_ASSERT(aBrowserParent);
223 sPointerCaptureRemoteTargetTable->RemoveIf([aBrowserParent](
224 const auto& iter) {
225 BrowserParent* browserParent = iter.Data();
226 MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");
228 return aBrowserParent == browserParent;
232 /* static */
233 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
234 uint32_t aPointerId) {
235 MOZ_ASSERT(XRE_IsParentProcess());
236 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
238 sPointerCaptureRemoteTargetTable->Remove(aPointerId);
241 /* static */
242 BrowserParent* PointerEventHandler::GetPointerCapturingRemoteTarget(
243 uint32_t aPointerId) {
244 MOZ_ASSERT(XRE_IsParentProcess());
245 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
247 return sPointerCaptureRemoteTargetTable->Get(aPointerId);
250 /* static */
251 void PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget() {
252 MOZ_ASSERT(XRE_IsParentProcess());
253 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
255 for (auto iter = sPointerCaptureRemoteTargetTable->Iter(); !iter.Done();
256 iter.Next()) {
257 BrowserParent* browserParent = iter.Data();
258 MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");
260 Unused << browserParent->SendReleaseAllPointerCapture();
261 iter.Remove();
265 /* static */
266 const PointerInfo* PointerEventHandler::GetPointerInfo(uint32_t aPointerId) {
267 return sActivePointersIds->Get(aPointerId);
270 /* static */
271 void PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent* aEvent) {
272 switch (aEvent->mClass) {
273 case eMouseEventClass:
274 ProcessPointerCaptureForMouse(aEvent->AsMouseEvent());
275 break;
276 case eTouchEventClass:
277 ProcessPointerCaptureForTouch(aEvent->AsTouchEvent());
278 break;
279 default:
280 break;
284 /* static */
285 void PointerEventHandler::ProcessPointerCaptureForMouse(
286 WidgetMouseEvent* aEvent) {
287 if (!ShouldGeneratePointerEventFromMouse(aEvent)) {
288 return;
291 PointerCaptureInfo* info = GetPointerCaptureInfo(aEvent->pointerId);
292 if (!info || info->mPendingElement == info->mOverrideElement) {
293 return;
295 WidgetPointerEvent localEvent(*aEvent);
296 InitPointerEventFromMouse(&localEvent, aEvent, eVoidEvent);
297 CheckPointerCaptureState(&localEvent);
300 /* static */
301 void PointerEventHandler::ProcessPointerCaptureForTouch(
302 WidgetTouchEvent* aEvent) {
303 if (!ShouldGeneratePointerEventFromTouch(aEvent)) {
304 return;
307 for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
308 Touch* touch = aEvent->mTouches[i];
309 if (!TouchManager::ShouldConvertTouchToPointer(touch, aEvent)) {
310 continue;
312 PointerCaptureInfo* info = GetPointerCaptureInfo(touch->Identifier());
313 if (!info || info->mPendingElement == info->mOverrideElement) {
314 continue;
316 WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget);
317 InitPointerEventFromTouch(event, *aEvent, *touch, i == 0);
318 CheckPointerCaptureState(&event);
322 /* static */
323 void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) {
324 // Handle pending pointer capture before any pointer events except
325 // gotpointercapture / lostpointercapture.
326 if (!aEvent) {
327 return;
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() &&
342 !captureInfo) {
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
349 // in this case.
350 if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement ||
351 !spoofedCaptureInfo->mPendingElement->OwnerDoc()
352 ->ShouldResistFingerprinting(RFPTarget::PointerEvents)) {
353 return;
356 captureInfo = spoofedCaptureInfo;
359 if (!captureInfo ||
360 captureInfo->mPendingElement == captureInfo->mOverrideElement) {
361 return;
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,
376 overrideElement);
378 if (pendingElement) {
379 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
380 pendingElement);
384 /* static */
385 void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
386 WidgetEvent* aEvent) {
387 MOZ_ASSERT(aEvent->mMessage == ePointerDown);
388 if (!aFrame || !IsPointerEventImplicitCaptureForTouchEnabled()) {
389 return;
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.
396 return;
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)) {
404 return;
406 RequestPointerCaptureById(pointerEvent->pointerId, target->AsElement());
409 /* static */
410 void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent* aEvent) {
411 MOZ_ASSERT(aEvent);
412 if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) {
413 return;
415 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
416 ReleasePointerCaptureById(pointerEvent->pointerId);
417 CheckPointerCaptureState(pointerEvent);
420 /* static */
421 void PointerEventHandler::MaybeImplicitlyReleasePointerCapture(
422 WidgetGUIEvent* aEvent) {
423 MOZ_ASSERT(aEvent);
424 const EventMessage pointerEventMessage =
425 PointerEventHandler::ToPointerEventMessage(aEvent);
426 if (pointerEventMessage != ePointerUp &&
427 pointerEventMessage != ePointerCancel) {
428 return;
430 PointerEventHandler::MaybeProcessPointerCapture(aEvent);
433 /* static */
434 Element* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId) {
435 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
436 if (pointerCaptureInfo) {
437 return pointerCaptureInfo->mOverrideElement;
439 return nullptr;
442 /* static */
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;
450 return nullptr;
453 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
454 if (!mouseEvent) {
455 return nullptr;
457 return GetPointerCapturingElement(mouseEvent->pointerId);
460 /* static */
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());
475 /* static */
476 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
477 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
478 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
479 return;
481 PointerInfo* pointerInfo = nullptr;
482 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
483 !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.
490 return;
492 if (!pointerInfo->mPreventMouseEventByContent) {
493 return;
495 aMouseOrTouchEvent->PreventDefault(false);
496 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
497 if (aPointerEvent->mMessage == ePointerUp) {
498 pointerInfo->mPreventMouseEventByContent = false;
502 /* static */
503 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
504 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
505 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
506 !aPointerEvent->DefaultPreventedByContent()) {
507 return;
509 PointerInfo* pointerInfo = nullptr;
510 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
511 !pointerInfo) {
512 // We already added the PointerInfo for active pointer when
513 // PresShell::HandleEvent handling pointerdown event.
514 #ifdef DEBUG
515 MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
516 #endif // #ifdef DEBUG
517 return;
519 // PreventDefault only applied for active pointers.
520 if (!pointerInfo->mActiveState) {
521 return;
523 aMouseOrTouchEvent->PreventDefault(false);
524 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
525 pointerInfo->mPreventMouseEventByContent = true;
528 /* static */
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
545 : 0.0f;
548 /* static */
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;
581 /* static */
582 EventMessage PointerEventHandler::ToPointerEventMessage(
583 const WidgetGUIEvent* aMouseOrTouchEvent) {
584 MOZ_ASSERT(aMouseOrTouchEvent);
586 switch (aMouseOrTouchEvent->mMessage) {
587 case eMouseMove:
588 return ePointerMove;
589 case eMouseUp:
590 return aMouseOrTouchEvent->AsMouseEvent()->mButtons ? ePointerMove
591 : ePointerUp;
592 case eMouseDown: {
593 const WidgetMouseEvent* mouseEvent = aMouseOrTouchEvent->AsMouseEvent();
594 return mouseEvent->mButtons & ~nsContentUtils::GetButtonsFlagForButton(
595 mouseEvent->mButton)
596 ? ePointerMove
597 : ePointerDown;
599 case eTouchMove:
600 return ePointerMove;
601 case eTouchEnd:
602 return ePointerUp;
603 case eTouchStart:
604 return ePointerDown;
605 case eTouchCancel:
606 case eTouchPointerCancel:
607 return ePointerCancel;
608 default:
609 return eVoidEvent;
613 /* static */
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
626 // devices in RDM.
627 Document* doc = aShell->GetDocument();
628 if (!doc) {
629 return;
632 BrowsingContext* bc = doc->GetBrowsingContext();
633 if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled &&
634 bc->InRDMPane()) {
635 return;
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()) {
643 return;
646 pointerMessage = PointerEventHandler::ToPointerEventMessage(mouseEvent);
647 if (pointerMessage == eVoidEvent) {
648 return;
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);
656 if (!shell) {
657 return;
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
669 // copy the event
670 pointerMessage = PointerEventHandler::ToPointerEventMessage(touchEvent);
671 if (pointerMessage == eVoidEvent) {
672 return;
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)) {
678 continue;
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);
692 if (!content) {
693 continue;
696 nsIFrame* frame = content->GetPrimaryFrame();
697 shell = PresShell::GetShellForEventTarget(frame, content);
698 if (!shell) {
699 continue;
702 PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
703 shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
704 aMouseOrTouchEventTarget);
705 PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
706 } else {
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,
716 aStatus);
717 PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
723 /* static */
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) ==
732 aPresContext) {
733 data->mPendingElement = nullptr;
735 if (data->mOverrideElement &&
736 data->mOverrideElement->GetPresContext(Element::eForComposedDoc) ==
737 aPresContext) {
738 data->mOverrideElement = nullptr;
740 if (data->Empty()) {
741 iter.Remove();
746 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) {
747 #ifdef XP_WIN
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
753 #endif
754 return true;
757 /* static */
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;
766 /* static */
767 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId) {
768 PointerInfo* pointerInfo = nullptr;
769 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
770 return pointerInfo->mPrimaryState;
772 return false;
775 /* static */
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())) {
782 return;
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,
796 init);
797 targetDoc->DispatchEvent(*event);
798 return;
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");
814 /* static */
815 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource,
816 uint32_t aPointerId) {
817 if (sSpoofedPointerId.isSome() || aInputSource != SPOOFED_POINTER_INTERFACE) {
818 return;
821 sSpoofedPointerId.emplace(aPointerId);
824 } // namespace mozilla