Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / events / PointerEventHandler.cpp
blobf631b6e494ce9a5bc6e9ac1a07c6091c2c392433
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/DocumentInlines.h"
19 #include "mozilla/dom/MouseEventBinding.h"
21 namespace mozilla {
23 using namespace dom;
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>*
31 sPointerCaptureList;
33 // Keeps information about pointers such as pointerId, activeState, pointerType,
34 // primaryState
35 static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
37 // Keeps track of which BrowserParent requested pointer capture for a pointer
38 // id.
39 static nsTHashMap<nsUint32HashKey, BrowserParent*>*
40 sPointerCaptureRemoteTargetTable = nullptr;
42 /* static */
43 void PointerEventHandler::InitializeStatics() {
44 MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
45 sPointerCaptureList =
46 new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
47 sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
48 if (XRE_IsParentProcess()) {
49 sPointerCaptureRemoteTargetTable =
50 new nsTHashMap<nsUint32HashKey, BrowserParent*>;
54 /* static */
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;
68 /* static */
69 bool PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() {
70 return StaticPrefs::dom_w3c_pointer_events_implicit_capture();
73 /* static */
74 void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent,
75 nsIContent* aTargetContent) {
76 if (!aEvent) {
77 return;
79 switch (aEvent->mMessage) {
80 case eMouseEnterIntoWidget:
81 // In this case we have to know information about available mouse pointers
82 sActivePointersIds->InsertOrUpdate(
83 aEvent->pointerId,
84 MakeUnique<PointerInfo>(false, aEvent->mInputSource, true, false,
85 nullptr));
87 MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId);
88 break;
89 case ePointerDown:
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);
104 break;
105 case ePointerCancel:
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.
109 case ePointerUp:
110 // In this case we remove information about pointer or turn off active
111 // state
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));
120 } else {
121 sActivePointersIds->Remove(pointerEvent->pointerId);
124 break;
125 case eMouseExitFromWidget:
126 // In this case we have to remove information about disappeared mouse
127 // pointers
128 sActivePointersIds->Remove(aEvent->pointerId);
129 break;
130 default:
131 MOZ_ASSERT_UNREACHABLE("event has invalid type");
132 break;
136 /* static */
137 void PointerEventHandler::RequestPointerCaptureById(uint32_t aPointerId,
138 Element* aElement) {
139 SetPointerCaptureById(aPointerId, aElement);
141 if (BrowserChild* browserChild =
142 BrowserChild::GetFrom(aElement->OwnerDoc()->GetDocShell())) {
143 browserChild->SendRequestPointerCapture(
144 aPointerId,
145 [aPointerId](bool aSuccess) {
146 if (!aSuccess) {
147 PointerEventHandler::ReleasePointerCaptureById(aPointerId);
150 [](mozilla::ipc::ResponseRejectReason) {});
154 /* static */
155 void PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId,
156 Element* aElement) {
157 MOZ_ASSERT(aElement);
158 sPointerCaptureList->WithEntryHandle(aPointerId, [&](auto&& entry) {
159 if (entry) {
160 entry.Data()->mPendingElement = aElement;
161 } else {
162 entry.Insert(MakeUnique<PointerCaptureInfo>(aElement));
167 /* static */
168 PointerCaptureInfo* PointerEventHandler::GetPointerCaptureInfo(
169 uint32_t aPointerId) {
170 PointerCaptureInfo* pointerCaptureInfo = nullptr;
171 sPointerCaptureList->Get(aPointerId, &pointerCaptureInfo);
172 return pointerCaptureInfo;
175 /* static */
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;
189 /* static */
190 void PointerEventHandler::ReleaseAllPointerCapture() {
191 for (const auto& entry : *sPointerCaptureList) {
192 PointerCaptureInfo* data = entry.GetWeak();
193 if (data && data->mPendingElement) {
194 ReleasePointerCaptureById(entry.GetKey());
199 /* static */
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()) {
207 return false;
210 BrowserParent* currentRemoteTarget =
211 PointerEventHandler::GetPointerCapturingRemoteTarget(aPointerId);
212 if (currentRemoteTarget && currentRemoteTarget != aBrowserParent) {
213 return false;
216 sPointerCaptureRemoteTargetTable->InsertOrUpdate(aPointerId, aBrowserParent);
217 return true;
220 /* static */
221 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
222 BrowserParent* aBrowserParent) {
223 MOZ_ASSERT(XRE_IsParentProcess());
224 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
225 MOZ_ASSERT(aBrowserParent);
227 sPointerCaptureRemoteTargetTable->RemoveIf([aBrowserParent](
228 const auto& iter) {
229 BrowserParent* browserParent = iter.Data();
230 MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");
232 return aBrowserParent == browserParent;
236 /* static */
237 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
238 uint32_t aPointerId) {
239 MOZ_ASSERT(XRE_IsParentProcess());
240 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
242 sPointerCaptureRemoteTargetTable->Remove(aPointerId);
245 /* static */
246 BrowserParent* PointerEventHandler::GetPointerCapturingRemoteTarget(
247 uint32_t aPointerId) {
248 MOZ_ASSERT(XRE_IsParentProcess());
249 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
251 return sPointerCaptureRemoteTargetTable->Get(aPointerId);
254 /* static */
255 void PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget() {
256 MOZ_ASSERT(XRE_IsParentProcess());
257 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
259 for (auto iter = sPointerCaptureRemoteTargetTable->Iter(); !iter.Done();
260 iter.Next()) {
261 BrowserParent* browserParent = iter.Data();
262 MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");
264 Unused << browserParent->SendReleaseAllPointerCapture();
265 iter.Remove();
269 /* static */
270 const PointerInfo* PointerEventHandler::GetPointerInfo(uint32_t aPointerId) {
271 return sActivePointersIds->Get(aPointerId);
274 /* static */
275 void PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent* aEvent) {
276 switch (aEvent->mClass) {
277 case eMouseEventClass:
278 ProcessPointerCaptureForMouse(aEvent->AsMouseEvent());
279 break;
280 case eTouchEventClass:
281 ProcessPointerCaptureForTouch(aEvent->AsTouchEvent());
282 break;
283 default:
284 break;
288 /* static */
289 void PointerEventHandler::ProcessPointerCaptureForMouse(
290 WidgetMouseEvent* aEvent) {
291 if (!ShouldGeneratePointerEventFromMouse(aEvent)) {
292 return;
295 PointerCaptureInfo* info = GetPointerCaptureInfo(aEvent->pointerId);
296 if (!info || info->mPendingElement == info->mOverrideElement) {
297 return;
299 WidgetPointerEvent localEvent(*aEvent);
300 InitPointerEventFromMouse(&localEvent, aEvent, eVoidEvent);
301 CheckPointerCaptureState(&localEvent);
304 /* static */
305 void PointerEventHandler::ProcessPointerCaptureForTouch(
306 WidgetTouchEvent* aEvent) {
307 if (!ShouldGeneratePointerEventFromTouch(aEvent)) {
308 return;
311 for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
312 Touch* touch = aEvent->mTouches[i];
313 if (!TouchManager::ShouldConvertTouchToPointer(touch, aEvent)) {
314 continue;
316 PointerCaptureInfo* info = GetPointerCaptureInfo(touch->Identifier());
317 if (!info || info->mPendingElement == info->mOverrideElement) {
318 continue;
320 WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget);
321 InitPointerEventFromTouch(event, *aEvent, *touch);
322 CheckPointerCaptureState(&event);
326 /* static */
327 void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) {
328 // Handle pending pointer capture before any pointer events except
329 // gotpointercapture / lostpointercapture.
330 if (!aEvent) {
331 return;
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() &&
346 !captureInfo) {
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
353 // in this case.
354 if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement ||
355 !spoofedCaptureInfo->mPendingElement->OwnerDoc()
356 ->ShouldResistFingerprinting(RFPTarget::PointerEvents)) {
357 return;
360 captureInfo = spoofedCaptureInfo;
363 if (!captureInfo ||
364 captureInfo->mPendingElement == captureInfo->mOverrideElement) {
365 return;
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,
380 overrideElement);
382 if (pendingElement) {
383 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
384 pendingElement);
388 /* static */
389 void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
390 WidgetEvent* aEvent) {
391 MOZ_ASSERT(aEvent->mMessage == ePointerDown);
392 if (!aFrame || !IsPointerEventImplicitCaptureForTouchEnabled()) {
393 return;
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.
400 return;
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)) {
408 return;
410 RequestPointerCaptureById(pointerEvent->pointerId, target->AsElement());
413 /* static */
414 void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent* aEvent) {
415 MOZ_ASSERT(aEvent);
416 if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) {
417 return;
419 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
420 ReleasePointerCaptureById(pointerEvent->pointerId);
421 CheckPointerCaptureState(pointerEvent);
424 /* static */
425 void PointerEventHandler::MaybeImplicitlyReleasePointerCapture(
426 WidgetGUIEvent* aEvent) {
427 MOZ_ASSERT(aEvent);
428 const EventMessage pointerEventMessage =
429 PointerEventHandler::ToPointerEventMessage(aEvent);
430 if (pointerEventMessage != ePointerUp &&
431 pointerEventMessage != ePointerCancel) {
432 return;
434 PointerEventHandler::MaybeProcessPointerCapture(aEvent);
437 /* static */
438 Element* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId) {
439 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
440 if (pointerCaptureInfo) {
441 return pointerCaptureInfo->mOverrideElement;
443 return nullptr;
446 /* static */
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;
454 return nullptr;
457 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
458 if (!mouseEvent) {
459 return nullptr;
461 return GetPointerCapturingElement(mouseEvent->pointerId);
464 /* static */
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());
479 /* static */
480 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
481 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
482 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
483 return;
485 PointerInfo* pointerInfo = nullptr;
486 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
487 !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.
494 return;
496 if (!pointerInfo->mPreventMouseEventByContent) {
497 return;
499 aMouseOrTouchEvent->PreventDefault(false);
500 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
501 if (aPointerEvent->mMessage == ePointerUp) {
502 pointerInfo->mPreventMouseEventByContent = false;
506 /* static */
507 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
508 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
509 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
510 !aPointerEvent->DefaultPreventedByContent()) {
511 return;
513 PointerInfo* pointerInfo = nullptr;
514 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
515 !pointerInfo) {
516 // We already added the PointerInfo for active pointer when
517 // PresShell::HandleEvent handling pointerdown event.
518 #ifdef DEBUG
519 MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
520 #endif // #ifdef DEBUG
521 return;
523 // PreventDefault only applied for active pointers.
524 if (!pointerInfo->mActiveState) {
525 return;
527 aMouseOrTouchEvent->PreventDefault(false);
528 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
529 pointerInfo->mPreventMouseEventByContent = true;
532 /* static */
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
549 : 0.0f;
552 /* static */
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;
588 /* static */
589 EventMessage PointerEventHandler::ToPointerEventMessage(
590 const WidgetGUIEvent* aMouseOrTouchEvent) {
591 MOZ_ASSERT(aMouseOrTouchEvent);
593 switch (aMouseOrTouchEvent->mMessage) {
594 case eMouseMove:
595 return ePointerMove;
596 case eMouseUp:
597 return aMouseOrTouchEvent->AsMouseEvent()->mButtons ? ePointerMove
598 : ePointerUp;
599 case eMouseDown: {
600 const WidgetMouseEvent* mouseEvent = aMouseOrTouchEvent->AsMouseEvent();
601 return mouseEvent->mButtons & ~nsContentUtils::GetButtonsFlagForButton(
602 mouseEvent->mButton)
603 ? ePointerMove
604 : ePointerDown;
606 case eTouchMove:
607 return ePointerMove;
608 case eTouchEnd:
609 return ePointerUp;
610 case eTouchStart:
611 return ePointerDown;
612 case eTouchCancel:
613 case eTouchPointerCancel:
614 return ePointerCancel;
615 default:
616 return eVoidEvent;
620 /* static */
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
633 // devices in RDM.
634 Document* doc = aShell->GetDocument();
635 if (!doc) {
636 return;
639 BrowsingContext* bc = doc->GetBrowsingContext();
640 if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled &&
641 bc->InRDMPane()) {
642 return;
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()) {
650 return;
653 pointerMessage = PointerEventHandler::ToPointerEventMessage(mouseEvent);
654 if (pointerMessage == eVoidEvent) {
655 return;
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);
663 if (!shell) {
664 return;
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
676 // copy the event
677 pointerMessage = PointerEventHandler::ToPointerEventMessage(touchEvent);
678 if (pointerMessage == eVoidEvent) {
679 return;
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)) {
685 continue;
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);
699 if (!content) {
700 continue;
703 nsIFrame* frame = content->GetPrimaryFrame();
704 shell = PresShell::GetShellForEventTarget(frame, content);
705 if (!shell) {
706 continue;
709 PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
710 shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
711 aMouseOrTouchEventTarget);
712 PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
713 } else {
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,
723 aStatus);
724 PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
730 /* static */
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) ==
739 aPresContext) {
740 data->mPendingElement = nullptr;
742 if (data->mOverrideElement &&
743 data->mOverrideElement->GetPresContext(Element::eForComposedDoc) ==
744 aPresContext) {
745 data->mOverrideElement = nullptr;
747 if (data->Empty()) {
748 iter.Remove();
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) {
757 iter.Remove();
762 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) {
763 #ifdef XP_WIN
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
769 #endif
770 return true;
773 /* static */
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;
782 /* static */
783 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId) {
784 PointerInfo* pointerInfo = nullptr;
785 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
786 return pointerInfo->mPrimaryState;
788 return false;
791 /* static */
792 bool PointerEventHandler::HasActiveTouchPointer() {
793 for (auto iter = sActivePointersIds->ConstIter(); !iter.Done(); iter.Next()) {
794 if (iter.Data()->mFromTouchEvent) {
795 return true;
798 return false;
801 /* static */
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())) {
808 return;
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,
822 init);
823 targetDoc->DispatchEvent(*event);
824 return;
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");
840 /* static */
841 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource,
842 uint32_t aPointerId) {
843 if (sSpoofedPointerId.isSome() || aInputSource != SPOOFED_POINTER_INTERFACE) {
844 return;
847 sSpoofedPointerId.emplace(aPointerId);
850 } // namespace mozilla