Bug 1688354 [wpt PR 27298] - Treat 'rem' as an absolute unit for font size, a=testonly
[gecko.git] / dom / events / PointerEventHandler.cpp
blob61e678864bb0cab272b5992fc93dd7c0bd94a4db
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 "nsIFrame.h"
9 #include "PointerEvent.h"
10 #include "PointerLockManager.h"
11 #include "mozilla/PresShell.h"
12 #include "mozilla/StaticPrefs_dom.h"
13 #include "mozilla/dom/BrowserChild.h"
14 #include "mozilla/dom/BrowserParent.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/MouseEventBinding.h"
18 namespace mozilla {
20 using namespace dom;
22 Maybe<int32_t> PointerEventHandler::sSpoofedPointerId;
24 // Keeps a map between pointerId and element that currently capturing pointer
25 // with such pointerId. If pointerId is absent in this map then nobody is
26 // capturing it. Additionally keep information about pending capturing content.
27 static nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>*
28 sPointerCaptureList;
30 // Keeps information about pointers such as pointerId, activeState, pointerType,
31 // primaryState
32 static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
34 // Keeps track of which BrowserParent requested pointer capture for a pointer
35 // id.
36 static nsDataHashtable<nsUint32HashKey, BrowserParent*>*
37 sPointerCaptureRemoteTargetTable = nullptr;
39 /* static */
40 void PointerEventHandler::InitializeStatics() {
41 MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
42 sPointerCaptureList =
43 new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
44 sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
45 if (XRE_IsParentProcess()) {
46 sPointerCaptureRemoteTargetTable =
47 new nsDataHashtable<nsUint32HashKey, BrowserParent*>;
51 /* static */
52 void PointerEventHandler::ReleaseStatics() {
53 MOZ_ASSERT(sPointerCaptureList, "ReleaseStatics called without Initialize!");
54 delete sPointerCaptureList;
55 sPointerCaptureList = nullptr;
56 delete sActivePointersIds;
57 sActivePointersIds = nullptr;
58 if (sPointerCaptureRemoteTargetTable) {
59 MOZ_ASSERT(XRE_IsParentProcess());
60 delete sPointerCaptureRemoteTargetTable;
61 sPointerCaptureRemoteTargetTable = nullptr;
65 /* static */
66 bool PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() {
67 return StaticPrefs::dom_w3c_pointer_events_implicit_capture();
70 /* static */
71 void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent,
72 nsIContent* aTargetContent) {
73 if (!aEvent) {
74 return;
76 switch (aEvent->mMessage) {
77 case eMouseEnterIntoWidget:
78 // In this case we have to know information about available mouse pointers
79 sActivePointersIds->Put(
80 aEvent->pointerId,
81 new PointerInfo(false, aEvent->mInputSource, true, nullptr));
83 MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId);
84 break;
85 case ePointerDown:
86 // In this case we switch pointer to active state
87 if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
88 // XXXedgar, test could possibly synthesize a mousedown event on a
89 // coordinate outside the browser window and cause aTargetContent to be
90 // nullptr, not sure if this also happens on real usage.
91 sActivePointersIds->Put(
92 pointerEvent->pointerId,
93 new PointerInfo(
94 true, pointerEvent->mInputSource, pointerEvent->mIsPrimary,
95 aTargetContent ? aTargetContent->OwnerDoc() : nullptr));
96 MaybeCacheSpoofedPointerID(pointerEvent->mInputSource,
97 pointerEvent->pointerId);
99 break;
100 case ePointerCancel:
101 // pointercancel means a pointer is unlikely to continue to produce
102 // pointer events. In that case, we should turn off active state or remove
103 // the pointer from active pointers.
104 case ePointerUp:
105 // In this case we remove information about pointer or turn off active
106 // state
107 if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
108 if (pointerEvent->mInputSource !=
109 MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
110 sActivePointersIds->Put(
111 pointerEvent->pointerId,
112 new PointerInfo(false, pointerEvent->mInputSource,
113 pointerEvent->mIsPrimary, nullptr));
114 } else {
115 sActivePointersIds->Remove(pointerEvent->pointerId);
118 break;
119 case eMouseExitFromWidget:
120 // In this case we have to remove information about disappeared mouse
121 // pointers
122 sActivePointersIds->Remove(aEvent->pointerId);
123 break;
124 default:
125 MOZ_ASSERT_UNREACHABLE("event has invalid type");
126 break;
130 /* static */
131 void PointerEventHandler::RequestPointerCaptureById(uint32_t aPointerId,
132 Element* aElement) {
133 SetPointerCaptureById(aPointerId, aElement);
135 if (BrowserChild* browserChild =
136 BrowserChild::GetFrom(aElement->OwnerDoc()->GetDocShell())) {
137 browserChild->SendRequestPointerCapture(
138 aPointerId,
139 [aPointerId](bool aSuccess) {
140 if (!aSuccess) {
141 PointerEventHandler::ReleasePointerCaptureById(aPointerId);
144 [](mozilla::ipc::ResponseRejectReason) {});
148 /* static */
149 void PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId,
150 Element* aElement) {
151 MOZ_ASSERT(aElement);
152 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
153 if (pointerCaptureInfo) {
154 pointerCaptureInfo->mPendingElement = aElement;
155 } else {
156 sPointerCaptureList->Put(aPointerId, new PointerCaptureInfo(aElement));
160 /* static */
161 PointerCaptureInfo* PointerEventHandler::GetPointerCaptureInfo(
162 uint32_t aPointerId) {
163 PointerCaptureInfo* pointerCaptureInfo = nullptr;
164 sPointerCaptureList->Get(aPointerId, &pointerCaptureInfo);
165 return pointerCaptureInfo;
168 /* static */
169 void PointerEventHandler::ReleasePointerCaptureById(uint32_t aPointerId) {
170 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
171 if (pointerCaptureInfo) {
172 if (Element* pendingElement = pointerCaptureInfo->mPendingElement) {
173 if (BrowserChild* browserChild = BrowserChild::GetFrom(
174 pendingElement->OwnerDoc()->GetDocShell())) {
175 browserChild->SendReleasePointerCapture(aPointerId);
178 pointerCaptureInfo->mPendingElement = nullptr;
182 /* static */
183 void PointerEventHandler::ReleaseAllPointerCapture() {
184 for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
185 PointerCaptureInfo* data = iter.UserData();
186 if (data && data->mPendingElement) {
187 ReleasePointerCaptureById(iter.Key());
192 /* static */
193 bool PointerEventHandler::SetPointerCaptureRemoteTarget(
194 uint32_t aPointerId, dom::BrowserParent* aBrowserParent) {
195 MOZ_ASSERT(XRE_IsParentProcess());
196 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
197 MOZ_ASSERT(aBrowserParent);
199 if (PointerLockManager::GetLockedRemoteTarget()) {
200 return false;
203 BrowserParent* currentRemoteTarget =
204 PointerEventHandler::GetPointerCapturingRemoteTarget(aPointerId);
205 if (currentRemoteTarget && currentRemoteTarget != aBrowserParent) {
206 return false;
209 sPointerCaptureRemoteTargetTable->Put(aPointerId, aBrowserParent);
210 return true;
213 /* static */
214 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
215 BrowserParent* aBrowserParent) {
216 MOZ_ASSERT(XRE_IsParentProcess());
217 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
218 MOZ_ASSERT(aBrowserParent);
220 sPointerCaptureRemoteTargetTable->RemoveIf([aBrowserParent](
221 const auto& iter) {
222 BrowserParent* browserParent = iter.Data();
223 MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");
225 return aBrowserParent == browserParent;
229 /* static */
230 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
231 uint32_t aPointerId) {
232 MOZ_ASSERT(XRE_IsParentProcess());
233 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
235 sPointerCaptureRemoteTargetTable->Remove(aPointerId);
238 /* static */
239 BrowserParent* PointerEventHandler::GetPointerCapturingRemoteTarget(
240 uint32_t aPointerId) {
241 MOZ_ASSERT(XRE_IsParentProcess());
242 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
244 return sPointerCaptureRemoteTargetTable->Get(aPointerId);
247 /* static */
248 void PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget() {
249 MOZ_ASSERT(XRE_IsParentProcess());
250 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
252 for (auto iter = sPointerCaptureRemoteTargetTable->Iter(); !iter.Done();
253 iter.Next()) {
254 BrowserParent* browserParent = iter.Data();
255 MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");
257 Unused << browserParent->SendReleaseAllPointerCapture();
258 iter.Remove();
262 /* static */
263 const PointerInfo* PointerEventHandler::GetPointerInfo(uint32_t aPointerId) {
264 return sActivePointersIds->Get(aPointerId);
267 /* static */
268 void PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent* aEvent) {
269 switch (aEvent->mClass) {
270 case eMouseEventClass:
271 ProcessPointerCaptureForMouse(aEvent->AsMouseEvent());
272 break;
273 case eTouchEventClass:
274 ProcessPointerCaptureForTouch(aEvent->AsTouchEvent());
275 break;
276 default:
277 break;
281 /* static */
282 void PointerEventHandler::ProcessPointerCaptureForMouse(
283 WidgetMouseEvent* aEvent) {
284 if (!ShouldGeneratePointerEventFromMouse(aEvent)) {
285 return;
288 PointerCaptureInfo* info = GetPointerCaptureInfo(aEvent->pointerId);
289 if (!info || info->mPendingElement == info->mOverrideElement) {
290 return;
292 WidgetPointerEvent localEvent(*aEvent);
293 InitPointerEventFromMouse(&localEvent, aEvent, eVoidEvent);
294 CheckPointerCaptureState(&localEvent);
297 /* static */
298 void PointerEventHandler::ProcessPointerCaptureForTouch(
299 WidgetTouchEvent* aEvent) {
300 if (!ShouldGeneratePointerEventFromTouch(aEvent)) {
301 return;
304 for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
305 Touch* touch = aEvent->mTouches[i];
306 if (!TouchManager::ShouldConvertTouchToPointer(touch, aEvent)) {
307 continue;
309 PointerCaptureInfo* info = GetPointerCaptureInfo(touch->Identifier());
310 if (!info || info->mPendingElement == info->mOverrideElement) {
311 continue;
313 WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget);
314 InitPointerEventFromTouch(&event, aEvent, touch, i == 0);
315 CheckPointerCaptureState(&event);
319 /* static */
320 void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) {
321 // Handle pending pointer capture before any pointer events except
322 // gotpointercapture / lostpointercapture.
323 if (!aEvent) {
324 return;
326 MOZ_ASSERT(aEvent->mClass == ePointerEventClass);
328 PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId);
330 // When fingerprinting resistance is enabled, we need to map other pointer
331 // ids into the spoofed one. We don't have to do the mapping if the capture
332 // info exists for the non-spoofed pointer id because of we won't allow
333 // content to set pointer capture other than the spoofed one. Thus, it must be
334 // from chrome if the capture info exists in this case. And we don't have to
335 // do anything if the pointer id is the same as the spoofed one.
336 if (nsContentUtils::ShouldResistFingerprinting() &&
337 aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() &&
338 !captureInfo) {
339 PointerCaptureInfo* spoofedCaptureInfo =
340 GetPointerCaptureInfo(GetSpoofedPointerIdForRFP());
342 // We need to check the target element is content or chrome. If it is chrome
343 // we don't need to send a capture event since the capture info of the
344 // original pointer id doesn't exist in the case.
345 if (!spoofedCaptureInfo ||
346 (spoofedCaptureInfo->mPendingElement &&
347 spoofedCaptureInfo->mPendingElement->IsInChromeDocument())) {
348 return;
351 captureInfo = spoofedCaptureInfo;
354 if (!captureInfo ||
355 captureInfo->mPendingElement == captureInfo->mOverrideElement) {
356 return;
359 RefPtr<Element> overrideElement = captureInfo->mOverrideElement;
360 RefPtr<Element> pendingElement = captureInfo->mPendingElement;
362 // Update captureInfo before dispatching event since sPointerCaptureList may
363 // be changed in the pointer event listener.
364 captureInfo->mOverrideElement = captureInfo->mPendingElement;
365 if (captureInfo->Empty()) {
366 sPointerCaptureList->Remove(aEvent->pointerId);
369 if (overrideElement) {
370 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent,
371 overrideElement);
373 if (pendingElement) {
374 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
375 pendingElement);
379 /* static */
380 void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
381 WidgetEvent* aEvent) {
382 MOZ_ASSERT(aEvent->mMessage == ePointerDown);
383 if (!aFrame || !IsPointerEventImplicitCaptureForTouchEnabled()) {
384 return;
386 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
387 NS_WARNING_ASSERTION(pointerEvent,
388 "Call ImplicitlyCapturePointer with non-pointer event");
389 if (pointerEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
390 // We only implicitly capture the pointer for touch device.
391 return;
393 nsCOMPtr<nsIContent> target;
394 aFrame->GetContentForEvent(aEvent, getter_AddRefs(target));
395 while (target && !target->IsElement()) {
396 target = target->GetParent();
398 if (NS_WARN_IF(!target)) {
399 return;
401 RequestPointerCaptureById(pointerEvent->pointerId, target->AsElement());
404 /* static */
405 void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent* aEvent) {
406 MOZ_ASSERT(aEvent);
407 if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) {
408 return;
410 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
411 ReleasePointerCaptureById(pointerEvent->pointerId);
412 CheckPointerCaptureState(pointerEvent);
415 /* static */
416 Element* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId) {
417 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
418 if (pointerCaptureInfo) {
419 return pointerCaptureInfo->mOverrideElement;
421 return nullptr;
424 /* static */
425 Element* PointerEventHandler::GetPointerCapturingElement(
426 WidgetGUIEvent* aEvent) {
427 if ((aEvent->mClass != ePointerEventClass &&
428 aEvent->mClass != eMouseEventClass) ||
429 aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) {
430 // Pointer capture should only be applied to all pointer events and mouse
431 // events except ePointerDown and eMouseDown;
432 return nullptr;
435 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
436 if (!mouseEvent) {
437 return nullptr;
439 return GetPointerCapturingElement(mouseEvent->pointerId);
442 /* static */
443 void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent) {
444 // We should check that aChild does not contain pointer capturing elements.
445 // If it does we should release the pointer capture for the elements.
446 for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
447 PointerCaptureInfo* data = iter.UserData();
448 if (data && data->mPendingElement &&
449 data->mPendingElement->IsInclusiveDescendantOf(aContent)) {
450 ReleasePointerCaptureById(iter.Key());
455 /* static */
456 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
457 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
458 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
459 return;
461 PointerInfo* pointerInfo = nullptr;
462 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
463 !pointerInfo) {
464 // The PointerInfo for active pointer should be added for normal cases. But
465 // in some cases, we may receive mouse events before adding PointerInfo in
466 // sActivePointersIds. (e.g. receive mousemove before
467 // eMouseEnterIntoWidget). In these cases, we could ignore them because they
468 // are not the events between a DefaultPrevented pointerdown and the
469 // corresponding pointerup.
470 return;
472 if (!pointerInfo->mPreventMouseEventByContent) {
473 return;
475 aMouseOrTouchEvent->PreventDefault(false);
476 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
477 if (aPointerEvent->mMessage == ePointerUp) {
478 pointerInfo->mPreventMouseEventByContent = false;
482 /* static */
483 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
484 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
485 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
486 !aPointerEvent->DefaultPreventedByContent()) {
487 return;
489 PointerInfo* pointerInfo = nullptr;
490 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
491 !pointerInfo) {
492 // We already added the PointerInfo for active pointer when
493 // PresShell::HandleEvent handling pointerdown event.
494 #ifdef DEBUG
495 MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
496 #endif // #ifdef DEBUG
497 return;
499 // PreventDefault only applied for active pointers.
500 if (!pointerInfo->mActiveState) {
501 return;
503 aMouseOrTouchEvent->PreventDefault(false);
504 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
505 pointerInfo->mPreventMouseEventByContent = true;
508 /* static */
509 void PointerEventHandler::InitPointerEventFromMouse(
510 WidgetPointerEvent* aPointerEvent, WidgetMouseEvent* aMouseEvent,
511 EventMessage aMessage) {
512 MOZ_ASSERT(aPointerEvent);
513 MOZ_ASSERT(aMouseEvent);
514 aPointerEvent->pointerId = aMouseEvent->pointerId;
515 aPointerEvent->mInputSource = aMouseEvent->mInputSource;
516 aPointerEvent->mMessage = aMessage;
517 aPointerEvent->mButton = aMouseEvent->mMessage == eMouseMove
518 ? MouseButton::eNotPressed
519 : aMouseEvent->mButton;
521 aPointerEvent->mButtons = aMouseEvent->mButtons;
522 aPointerEvent->mPressure =
523 aPointerEvent->mButtons
524 ? aMouseEvent->mPressure ? aMouseEvent->mPressure : 0.5f
525 : 0.0f;
528 /* static */
529 void PointerEventHandler::InitPointerEventFromTouch(
530 WidgetPointerEvent* aPointerEvent, WidgetTouchEvent* aTouchEvent,
531 mozilla::dom::Touch* aTouch, bool aIsPrimary) {
532 MOZ_ASSERT(aPointerEvent);
533 MOZ_ASSERT(aTouchEvent);
535 int16_t button = aTouchEvent->mMessage == eTouchMove
536 ? MouseButton::eNotPressed
537 : MouseButton::ePrimary;
539 int16_t buttons = aTouchEvent->mMessage == eTouchEnd
540 ? MouseButtonsFlag::eNoButtons
541 : MouseButtonsFlag::ePrimaryFlag;
543 aPointerEvent->mIsPrimary = aIsPrimary;
544 aPointerEvent->pointerId = aTouch->Identifier();
545 aPointerEvent->mRefPoint = aTouch->mRefPoint;
546 aPointerEvent->mModifiers = aTouchEvent->mModifiers;
547 aPointerEvent->mWidth = aTouch->RadiusX(CallerType::System);
548 aPointerEvent->mHeight = aTouch->RadiusY(CallerType::System);
549 aPointerEvent->tiltX = aTouch->tiltX;
550 aPointerEvent->tiltY = aTouch->tiltY;
551 aPointerEvent->mTime = aTouchEvent->mTime;
552 aPointerEvent->mTimeStamp = aTouchEvent->mTimeStamp;
553 aPointerEvent->mFlags = aTouchEvent->mFlags;
554 aPointerEvent->mButton = button;
555 aPointerEvent->mButtons = buttons;
556 aPointerEvent->mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
559 /* static */
560 void PointerEventHandler::DispatchPointerFromMouseOrTouch(
561 PresShell* aShell, nsIFrame* aFrame, nsIContent* aContent,
562 WidgetGUIEvent* aEvent, bool aDontRetargetEvents, nsEventStatus* aStatus,
563 nsIContent** aTargetContent) {
564 MOZ_ASSERT(aFrame || aContent);
565 MOZ_ASSERT(aEvent);
567 EventMessage pointerMessage = eVoidEvent;
568 if (aEvent->mClass == eMouseEventClass) {
569 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
570 // Don't dispatch pointer events caused by a mouse when simulating touch
571 // devices in RDM.
572 Document* doc = aShell->GetDocument();
573 if (!doc) {
574 return;
577 BrowsingContext* bc = doc->GetBrowsingContext();
578 if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled &&
579 bc->InRDMPane()) {
580 return;
583 // 1. If it is not mouse then it is likely will come as touch event
584 // 2. We don't synthesize pointer events for those events that are not
585 // dispatched to DOM.
586 if (!mouseEvent->convertToPointer ||
587 !aEvent->IsAllowedToDispatchDOMEvent()) {
588 return;
591 switch (mouseEvent->mMessage) {
592 case eMouseMove:
593 pointerMessage = ePointerMove;
594 break;
595 case eMouseUp:
596 pointerMessage = mouseEvent->mButtons ? ePointerMove : ePointerUp;
597 break;
598 case eMouseDown:
599 pointerMessage =
600 mouseEvent->mButtons & ~nsContentUtils::GetButtonsFlagForButton(
601 mouseEvent->mButton)
602 ? ePointerMove
603 : ePointerDown;
604 break;
605 default:
606 return;
609 WidgetPointerEvent event(*mouseEvent);
610 InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
611 event.convertToPointer = mouseEvent->convertToPointer = false;
612 RefPtr<PresShell> shell(aShell);
613 if (!aFrame) {
614 shell = PresShell::GetShellForEventTarget(nullptr, aContent);
615 if (!shell) {
616 return;
619 PreHandlePointerEventsPreventDefault(&event, aEvent);
620 // Dispatch pointer event to the same target which is found by the
621 // corresponding mouse event.
622 shell->HandleEventWithTarget(&event, aFrame, aContent, aStatus, true,
623 aTargetContent);
624 PostHandlePointerEventsPreventDefault(&event, aEvent);
625 } else if (aEvent->mClass == eTouchEventClass) {
626 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
627 // loop over all touches and dispatch pointer events on each touch
628 // copy the event
629 switch (touchEvent->mMessage) {
630 case eTouchMove:
631 pointerMessage = ePointerMove;
632 break;
633 case eTouchEnd:
634 pointerMessage = ePointerUp;
635 break;
636 case eTouchStart:
637 pointerMessage = ePointerDown;
638 break;
639 case eTouchCancel:
640 case eTouchPointerCancel:
641 pointerMessage = ePointerCancel;
642 break;
643 default:
644 return;
647 RefPtr<PresShell> shell(aShell);
648 for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
649 Touch* touch = touchEvent->mTouches[i];
650 if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
651 continue;
654 WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
655 touchEvent->mWidget);
657 InitPointerEventFromTouch(&event, touchEvent, touch, i == 0);
658 event.convertToPointer = touch->convertToPointer = false;
659 if (aEvent->mMessage == eTouchStart) {
660 // We already did hit test for touchstart in PresShell. We should
661 // dispatch pointerdown to the same target as touchstart.
662 nsCOMPtr<nsIContent> content = do_QueryInterface(touch->mTarget);
663 if (!content) {
664 continue;
667 nsIFrame* frame = content->GetPrimaryFrame();
668 shell = PresShell::GetShellForEventTarget(frame, content);
669 if (!shell) {
670 continue;
673 PreHandlePointerEventsPreventDefault(&event, aEvent);
674 shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
675 nullptr);
676 PostHandlePointerEventsPreventDefault(&event, aEvent);
677 } else {
678 // We didn't hit test for other touch events. Spec doesn't mention that
679 // all pointer events should be dispatched to the same target as their
680 // corresponding touch events. Call PresShell::HandleEvent so that we do
681 // hit test for pointer events.
682 PreHandlePointerEventsPreventDefault(&event, aEvent);
683 shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
684 PostHandlePointerEventsPreventDefault(&event, aEvent);
690 /* static */
691 void PointerEventHandler::NotifyDestroyPresContext(
692 nsPresContext* aPresContext) {
693 // Clean up pointer capture info
694 for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
695 PointerCaptureInfo* data = iter.UserData();
696 MOZ_ASSERT(data, "how could we have a null PointerCaptureInfo here?");
697 if (data->mPendingElement &&
698 data->mPendingElement->GetPresContext(Element::eForComposedDoc) ==
699 aPresContext) {
700 data->mPendingElement = nullptr;
702 if (data->mOverrideElement &&
703 data->mOverrideElement->GetPresContext(Element::eForComposedDoc) ==
704 aPresContext) {
705 data->mOverrideElement = nullptr;
707 if (data->Empty()) {
708 iter.Remove();
713 /* static */
714 uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId) {
715 PointerInfo* pointerInfo = nullptr;
716 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
717 return pointerInfo->mPointerType;
719 return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
722 /* static */
723 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId) {
724 PointerInfo* pointerInfo = nullptr;
725 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
726 return pointerInfo->mPrimaryState;
728 return false;
731 /* static */
732 void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
733 bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent,
734 Element* aCaptureTarget) {
735 Document* targetDoc = aCaptureTarget->OwnerDoc();
736 RefPtr<PresShell> presShell = targetDoc->GetPresShell();
737 if (NS_WARN_IF(!presShell || presShell->IsDestroying())) {
738 return;
741 if (!aIsGotCapture && !aCaptureTarget->IsInComposedDoc()) {
742 // If the capturing element was removed from the DOM tree, fire
743 // ePointerLostCapture at the document.
744 PointerEventInit init;
745 init.mPointerId = aPointerEvent->pointerId;
746 init.mBubbles = true;
747 init.mComposed = true;
748 ConvertPointerTypeToString(aPointerEvent->mInputSource, init.mPointerType);
749 init.mIsPrimary = aPointerEvent->mIsPrimary;
750 RefPtr<PointerEvent> event;
751 event = PointerEvent::Constructor(aCaptureTarget, u"lostpointercapture"_ns,
752 init);
753 targetDoc->DispatchEvent(*event);
754 return;
756 nsEventStatus status = nsEventStatus_eIgnore;
757 WidgetPointerEvent localEvent(
758 aPointerEvent->IsTrusted(),
759 aIsGotCapture ? ePointerGotCapture : ePointerLostCapture,
760 aPointerEvent->mWidget);
762 localEvent.AssignPointerEventData(*aPointerEvent, true);
763 DebugOnly<nsresult> rv = presShell->HandleEventWithTarget(
764 &localEvent, aCaptureTarget->GetPrimaryFrame(), aCaptureTarget, &status);
766 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
767 "DispatchGotOrLostPointerCaptureEvent failed");
770 /* static */
771 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource,
772 uint32_t aPointerId) {
773 if (sSpoofedPointerId.isSome() || aInputSource != SPOOFED_POINTER_INTERFACE) {
774 return;
777 sSpoofedPointerId.emplace(aPointerId);
780 } // namespace mozilla