Bug 1776680 [wpt PR 34603] - [@container] Test invalidation of font-relative units...
[gecko.git] / dom / events / PointerEventHandler.cpp
blobf2a20fd5159b408c539896f4f4bc1df9fc310aea
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() &&
340 aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() &&
341 !captureInfo) {
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())) {
351 return;
354 captureInfo = spoofedCaptureInfo;
357 if (!captureInfo ||
358 captureInfo->mPendingElement == captureInfo->mOverrideElement) {
359 return;
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,
374 overrideElement);
376 if (pendingElement) {
377 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
378 pendingElement);
382 /* static */
383 void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
384 WidgetEvent* aEvent) {
385 MOZ_ASSERT(aEvent->mMessage == ePointerDown);
386 if (!aFrame || !IsPointerEventImplicitCaptureForTouchEnabled()) {
387 return;
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.
394 return;
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)) {
402 return;
404 RequestPointerCaptureById(pointerEvent->pointerId, target->AsElement());
407 /* static */
408 void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent* aEvent) {
409 MOZ_ASSERT(aEvent);
410 if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) {
411 return;
413 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
414 ReleasePointerCaptureById(pointerEvent->pointerId);
415 CheckPointerCaptureState(pointerEvent);
418 /* static */
419 Element* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId) {
420 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
421 if (pointerCaptureInfo) {
422 return pointerCaptureInfo->mOverrideElement;
424 return nullptr;
427 /* static */
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;
435 return nullptr;
438 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
439 if (!mouseEvent) {
440 return nullptr;
442 return GetPointerCapturingElement(mouseEvent->pointerId);
445 /* static */
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());
458 /* static */
459 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
460 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
461 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
462 return;
464 PointerInfo* pointerInfo = nullptr;
465 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
466 !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.
473 return;
475 if (!pointerInfo->mPreventMouseEventByContent) {
476 return;
478 aMouseOrTouchEvent->PreventDefault(false);
479 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
480 if (aPointerEvent->mMessage == ePointerUp) {
481 pointerInfo->mPreventMouseEventByContent = false;
485 /* static */
486 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
487 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
488 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
489 !aPointerEvent->DefaultPreventedByContent()) {
490 return;
492 PointerInfo* pointerInfo = nullptr;
493 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
494 !pointerInfo) {
495 // We already added the PointerInfo for active pointer when
496 // PresShell::HandleEvent handling pointerdown event.
497 #ifdef DEBUG
498 MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
499 #endif // #ifdef DEBUG
500 return;
502 // PreventDefault only applied for active pointers.
503 if (!pointerInfo->mActiveState) {
504 return;
506 aMouseOrTouchEvent->PreventDefault(false);
507 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
508 pointerInfo->mPreventMouseEventByContent = true;
511 /* static */
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
528 : 0.0f;
531 /* static */
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;
565 /* static */
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);
571 MOZ_ASSERT(aEvent);
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
577 // devices in RDM.
578 Document* doc = aShell->GetDocument();
579 if (!doc) {
580 return;
583 BrowsingContext* bc = doc->GetBrowsingContext();
584 if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled &&
585 bc->InRDMPane()) {
586 return;
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()) {
594 return;
597 switch (mouseEvent->mMessage) {
598 case eMouseMove:
599 pointerMessage = ePointerMove;
600 break;
601 case eMouseUp:
602 pointerMessage = mouseEvent->mButtons ? ePointerMove : ePointerUp;
603 break;
604 case eMouseDown:
605 pointerMessage =
606 mouseEvent->mButtons & ~nsContentUtils::GetButtonsFlagForButton(
607 mouseEvent->mButton)
608 ? ePointerMove
609 : ePointerDown;
610 break;
611 default:
612 return;
615 WidgetPointerEvent event(*mouseEvent);
616 InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
617 event.convertToPointer = mouseEvent->convertToPointer = false;
618 RefPtr<PresShell> shell(aShell);
619 if (!aFrame) {
620 shell = PresShell::GetShellForEventTarget(nullptr, aContent);
621 if (!shell) {
622 return;
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,
629 aTargetContent);
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
634 // copy the event
635 switch (touchEvent->mMessage) {
636 case eTouchMove:
637 pointerMessage = ePointerMove;
638 break;
639 case eTouchEnd:
640 pointerMessage = ePointerUp;
641 break;
642 case eTouchStart:
643 pointerMessage = ePointerDown;
644 break;
645 case eTouchCancel:
646 case eTouchPointerCancel:
647 pointerMessage = ePointerCancel;
648 break;
649 default:
650 return;
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)) {
657 continue;
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);
671 if (!content) {
672 continue;
675 nsIFrame* frame = content->GetPrimaryFrame();
676 shell = PresShell::GetShellForEventTarget(frame, content);
677 if (!shell) {
678 continue;
681 PreHandlePointerEventsPreventDefault(&event, aEvent);
682 shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
683 nullptr);
684 PostHandlePointerEventsPreventDefault(&event, aEvent);
685 } else {
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);
698 /* static */
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) ==
707 aPresContext) {
708 data->mPendingElement = nullptr;
710 if (data->mOverrideElement &&
711 data->mOverrideElement->GetPresContext(Element::eForComposedDoc) ==
712 aPresContext) {
713 data->mOverrideElement = nullptr;
715 if (data->Empty()) {
716 iter.Remove();
721 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) {
722 #ifdef XP_WIN
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
728 #endif
729 return true;
732 /* static */
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;
741 /* static */
742 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId) {
743 PointerInfo* pointerInfo = nullptr;
744 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
745 return pointerInfo->mPrimaryState;
747 return false;
750 /* static */
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())) {
757 return;
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,
771 init);
772 targetDoc->DispatchEvent(*event);
773 return;
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");
789 /* static */
790 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource,
791 uint32_t aPointerId) {
792 if (sSpoofedPointerId.isSome() || aInputSource != SPOOFED_POINTER_INTERFACE) {
793 return;
796 sSpoofedPointerId.emplace(aPointerId);
799 } // namespace mozilla