Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / events / PointerEventHandler.cpp
blob73e4be715550ad1d34299ca8567645b8fd58ca15
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 Element* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId) {
422 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
423 if (pointerCaptureInfo) {
424 return pointerCaptureInfo->mOverrideElement;
426 return nullptr;
429 /* static */
430 Element* PointerEventHandler::GetPointerCapturingElement(
431 WidgetGUIEvent* aEvent) {
432 if ((aEvent->mClass != ePointerEventClass &&
433 aEvent->mClass != eMouseEventClass) ||
434 aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) {
435 // Pointer capture should only be applied to all pointer events and mouse
436 // events except ePointerDown and eMouseDown;
437 return nullptr;
440 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
441 if (!mouseEvent) {
442 return nullptr;
444 return GetPointerCapturingElement(mouseEvent->pointerId);
447 /* static */
448 void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent) {
449 // We should check that aChild does not contain pointer capturing elements.
450 // If it does we should release the pointer capture for the elements.
451 for (const auto& entry : *sPointerCaptureList) {
452 PointerCaptureInfo* data = entry.GetWeak();
453 if (data && data->mPendingElement &&
454 data->mPendingElement->IsInclusiveDescendantOf(aContent)) {
455 ReleasePointerCaptureById(entry.GetKey());
460 /* static */
461 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
462 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
463 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
464 return;
466 PointerInfo* pointerInfo = nullptr;
467 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
468 !pointerInfo) {
469 // The PointerInfo for active pointer should be added for normal cases. But
470 // in some cases, we may receive mouse events before adding PointerInfo in
471 // sActivePointersIds. (e.g. receive mousemove before
472 // eMouseEnterIntoWidget). In these cases, we could ignore them because they
473 // are not the events between a DefaultPrevented pointerdown and the
474 // corresponding pointerup.
475 return;
477 if (!pointerInfo->mPreventMouseEventByContent) {
478 return;
480 aMouseOrTouchEvent->PreventDefault(false);
481 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
482 if (aPointerEvent->mMessage == ePointerUp) {
483 pointerInfo->mPreventMouseEventByContent = false;
487 /* static */
488 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
489 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
490 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
491 !aPointerEvent->DefaultPreventedByContent()) {
492 return;
494 PointerInfo* pointerInfo = nullptr;
495 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
496 !pointerInfo) {
497 // We already added the PointerInfo for active pointer when
498 // PresShell::HandleEvent handling pointerdown event.
499 #ifdef DEBUG
500 MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
501 #endif // #ifdef DEBUG
502 return;
504 // PreventDefault only applied for active pointers.
505 if (!pointerInfo->mActiveState) {
506 return;
508 aMouseOrTouchEvent->PreventDefault(false);
509 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
510 pointerInfo->mPreventMouseEventByContent = true;
513 /* static */
514 void PointerEventHandler::InitPointerEventFromMouse(
515 WidgetPointerEvent* aPointerEvent, WidgetMouseEvent* aMouseEvent,
516 EventMessage aMessage) {
517 MOZ_ASSERT(aPointerEvent);
518 MOZ_ASSERT(aMouseEvent);
519 aPointerEvent->pointerId = aMouseEvent->pointerId;
520 aPointerEvent->mInputSource = aMouseEvent->mInputSource;
521 aPointerEvent->mMessage = aMessage;
522 aPointerEvent->mButton = aMouseEvent->mMessage == eMouseMove
523 ? MouseButton::eNotPressed
524 : aMouseEvent->mButton;
526 aPointerEvent->mButtons = aMouseEvent->mButtons;
527 aPointerEvent->mPressure =
528 aPointerEvent->mButtons
529 ? aMouseEvent->mPressure ? aMouseEvent->mPressure : 0.5f
530 : 0.0f;
533 /* static */
534 void PointerEventHandler::InitPointerEventFromTouch(
535 WidgetPointerEvent& aPointerEvent, const WidgetTouchEvent& aTouchEvent,
536 const mozilla::dom::Touch& aTouch, bool aIsPrimary) {
537 // Use mButton/mButtons only when mButton got a value (from pen input)
538 int16_t button = aTouchEvent.mMessage == eTouchMove ? MouseButton::eNotPressed
539 : aTouchEvent.mButton != MouseButton::eNotPressed
540 ? aTouchEvent.mButton
541 : MouseButton::ePrimary;
542 int16_t buttons = aTouchEvent.mMessage == eTouchEnd
543 ? MouseButtonsFlag::eNoButtons
544 : aTouchEvent.mButton != MouseButton::eNotPressed
545 ? aTouchEvent.mButtons
546 : MouseButtonsFlag::ePrimaryFlag;
548 aPointerEvent.mIsPrimary = aIsPrimary;
549 aPointerEvent.pointerId = aTouch.Identifier();
550 aPointerEvent.mRefPoint = aTouch.mRefPoint;
551 aPointerEvent.mModifiers = aTouchEvent.mModifiers;
552 aPointerEvent.mWidth = aTouch.RadiusX(CallerType::System);
553 aPointerEvent.mHeight = aTouch.RadiusY(CallerType::System);
554 aPointerEvent.tiltX = aTouch.tiltX;
555 aPointerEvent.tiltY = aTouch.tiltY;
556 aPointerEvent.twist = aTouch.twist;
557 aPointerEvent.mTimeStamp = aTouchEvent.mTimeStamp;
558 aPointerEvent.mFlags = aTouchEvent.mFlags;
559 aPointerEvent.mButton = button;
560 aPointerEvent.mButtons = buttons;
561 aPointerEvent.mInputSource = aTouchEvent.mInputSource;
562 aPointerEvent.mFromTouchEvent = true;
563 aPointerEvent.mPressure = aTouch.mForce;
566 /* static */
567 void PointerEventHandler::DispatchPointerFromMouseOrTouch(
568 PresShell* aShell, nsIFrame* aFrame, nsIContent* aContent,
569 WidgetGUIEvent* aEvent, bool aDontRetargetEvents, nsEventStatus* aStatus,
570 nsIContent** aTargetContent) {
571 MOZ_ASSERT(aFrame || aContent);
572 MOZ_ASSERT(aEvent);
574 EventMessage pointerMessage = eVoidEvent;
575 if (aEvent->mClass == eMouseEventClass) {
576 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
577 // Don't dispatch pointer events caused by a mouse when simulating touch
578 // devices in RDM.
579 Document* doc = aShell->GetDocument();
580 if (!doc) {
581 return;
584 BrowsingContext* bc = doc->GetBrowsingContext();
585 if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled &&
586 bc->InRDMPane()) {
587 return;
590 // 1. If it is not mouse then it is likely will come as touch event
591 // 2. We don't synthesize pointer events for those events that are not
592 // dispatched to DOM.
593 if (!mouseEvent->convertToPointer ||
594 !aEvent->IsAllowedToDispatchDOMEvent()) {
595 return;
598 switch (mouseEvent->mMessage) {
599 case eMouseMove:
600 pointerMessage = ePointerMove;
601 break;
602 case eMouseUp:
603 pointerMessage = mouseEvent->mButtons ? ePointerMove : ePointerUp;
604 break;
605 case eMouseDown:
606 pointerMessage =
607 mouseEvent->mButtons & ~nsContentUtils::GetButtonsFlagForButton(
608 mouseEvent->mButton)
609 ? ePointerMove
610 : ePointerDown;
611 break;
612 default:
613 return;
616 WidgetPointerEvent event(*mouseEvent);
617 InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
618 event.convertToPointer = mouseEvent->convertToPointer = false;
619 RefPtr<PresShell> shell(aShell);
620 if (!aFrame) {
621 shell = PresShell::GetShellForEventTarget(nullptr, aContent);
622 if (!shell) {
623 return;
626 PreHandlePointerEventsPreventDefault(&event, aEvent);
627 // Dispatch pointer event to the same target which is found by the
628 // corresponding mouse event.
629 shell->HandleEventWithTarget(&event, aFrame, aContent, aStatus, true,
630 aTargetContent);
631 PostHandlePointerEventsPreventDefault(&event, aEvent);
632 } else if (aEvent->mClass == eTouchEventClass) {
633 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
634 // loop over all touches and dispatch pointer events on each touch
635 // copy the event
636 switch (touchEvent->mMessage) {
637 case eTouchMove:
638 pointerMessage = ePointerMove;
639 break;
640 case eTouchEnd:
641 pointerMessage = ePointerUp;
642 break;
643 case eTouchStart:
644 pointerMessage = ePointerDown;
645 break;
646 case eTouchCancel:
647 case eTouchPointerCancel:
648 pointerMessage = ePointerCancel;
649 break;
650 default:
651 return;
654 RefPtr<PresShell> shell(aShell);
655 for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
656 Touch* touch = touchEvent->mTouches[i];
657 if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
658 continue;
661 WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
662 touchEvent->mWidget);
664 InitPointerEventFromTouch(event, *touchEvent, *touch, i == 0);
665 event.convertToPointer = touch->convertToPointer = false;
666 event.mCoalescedWidgetEvents = touch->mCoalescedWidgetEvents;
667 if (aEvent->mMessage == eTouchStart) {
668 // We already did hit test for touchstart in PresShell. We should
669 // dispatch pointerdown to the same target as touchstart.
670 nsCOMPtr<nsIContent> content =
671 nsIContent::FromEventTargetOrNull(touch->mTarget);
672 if (!content) {
673 continue;
676 nsIFrame* frame = content->GetPrimaryFrame();
677 shell = PresShell::GetShellForEventTarget(frame, content);
678 if (!shell) {
679 continue;
682 PreHandlePointerEventsPreventDefault(&event, aEvent);
683 shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
684 nullptr);
685 PostHandlePointerEventsPreventDefault(&event, aEvent);
686 } else {
687 // We didn't hit test for other touch events. Spec doesn't mention that
688 // all pointer events should be dispatched to the same target as their
689 // corresponding touch events. Call PresShell::HandleEvent so that we do
690 // hit test for pointer events.
691 PreHandlePointerEventsPreventDefault(&event, aEvent);
692 shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
693 PostHandlePointerEventsPreventDefault(&event, aEvent);
699 /* static */
700 void PointerEventHandler::NotifyDestroyPresContext(
701 nsPresContext* aPresContext) {
702 // Clean up pointer capture info
703 for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
704 PointerCaptureInfo* data = iter.UserData();
705 MOZ_ASSERT(data, "how could we have a null PointerCaptureInfo here?");
706 if (data->mPendingElement &&
707 data->mPendingElement->GetPresContext(Element::eForComposedDoc) ==
708 aPresContext) {
709 data->mPendingElement = nullptr;
711 if (data->mOverrideElement &&
712 data->mOverrideElement->GetPresContext(Element::eForComposedDoc) ==
713 aPresContext) {
714 data->mOverrideElement = nullptr;
716 if (data->Empty()) {
717 iter.Remove();
722 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) {
723 #ifdef XP_WIN
724 if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) {
725 // WM_POINTER does not support drag and drop, see bug 1692277
726 return (aEvent.mInputSource != dom::MouseEvent_Binding::MOZ_SOURCE_PEN &&
727 aEvent.mReason != WidgetMouseEvent::eSynthesized); // bug 1692151
729 #endif
730 return true;
733 /* static */
734 uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId) {
735 PointerInfo* pointerInfo = nullptr;
736 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
737 return pointerInfo->mPointerType;
739 return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
742 /* static */
743 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId) {
744 PointerInfo* pointerInfo = nullptr;
745 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
746 return pointerInfo->mPrimaryState;
748 return false;
751 /* static */
752 void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
753 bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent,
754 Element* aCaptureTarget) {
755 Document* targetDoc = aCaptureTarget->OwnerDoc();
756 RefPtr<PresShell> presShell = targetDoc->GetPresShell();
757 if (NS_WARN_IF(!presShell || presShell->IsDestroying())) {
758 return;
761 if (!aIsGotCapture && !aCaptureTarget->IsInComposedDoc()) {
762 // If the capturing element was removed from the DOM tree, fire
763 // ePointerLostCapture at the document.
764 PointerEventInit init;
765 init.mPointerId = aPointerEvent->pointerId;
766 init.mBubbles = true;
767 init.mComposed = true;
768 ConvertPointerTypeToString(aPointerEvent->mInputSource, init.mPointerType);
769 init.mIsPrimary = aPointerEvent->mIsPrimary;
770 RefPtr<PointerEvent> event;
771 event = PointerEvent::Constructor(aCaptureTarget, u"lostpointercapture"_ns,
772 init);
773 targetDoc->DispatchEvent(*event);
774 return;
776 nsEventStatus status = nsEventStatus_eIgnore;
777 WidgetPointerEvent localEvent(
778 aPointerEvent->IsTrusted(),
779 aIsGotCapture ? ePointerGotCapture : ePointerLostCapture,
780 aPointerEvent->mWidget);
782 localEvent.AssignPointerEventData(*aPointerEvent, true);
783 DebugOnly<nsresult> rv = presShell->HandleEventWithTarget(
784 &localEvent, aCaptureTarget->GetPrimaryFrame(), aCaptureTarget, &status);
786 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
787 "DispatchGotOrLostPointerCaptureEvent failed");
790 /* static */
791 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource,
792 uint32_t aPointerId) {
793 if (sSpoofedPointerId.isSome() || aInputSource != SPOOFED_POINTER_INTERFACE) {
794 return;
797 sSpoofedPointerId.emplace(aPointerId);
800 } // namespace mozilla