Bug 1863873 - Block ability to perform audio decoding outside of Utility on release...
[gecko.git] / dom / events / PointerEventHandler.cpp
blob6c537bfb67642ef01d0230f2c55516689e41806e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "PointerEventHandler.h"
8 #include "nsIContentInlines.h"
9 #include "nsIFrame.h"
10 #include "PointerEvent.h"
11 #include "PointerLockManager.h"
12 #include "nsRFPService.h"
13 #include "mozilla/PresShell.h"
14 #include "mozilla/StaticPrefs_dom.h"
15 #include "mozilla/dom/BrowserChild.h"
16 #include "mozilla/dom/BrowserParent.h"
17 #include "mozilla/dom/Document.h"
18 #include "mozilla/dom/MouseEventBinding.h"
20 namespace mozilla {
22 using namespace dom;
24 Maybe<int32_t> PointerEventHandler::sSpoofedPointerId;
26 // Keeps a map between pointerId and element that currently capturing pointer
27 // with such pointerId. If pointerId is absent in this map then nobody is
28 // capturing it. Additionally keep information about pending capturing content.
29 static nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>*
30 sPointerCaptureList;
32 // Keeps information about pointers such as pointerId, activeState, pointerType,
33 // primaryState
34 static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
36 // Keeps track of which BrowserParent requested pointer capture for a pointer
37 // id.
38 static nsTHashMap<nsUint32HashKey, BrowserParent*>*
39 sPointerCaptureRemoteTargetTable = nullptr;
41 /* static */
42 void PointerEventHandler::InitializeStatics() {
43 MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
44 sPointerCaptureList =
45 new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
46 sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
47 if (XRE_IsParentProcess()) {
48 sPointerCaptureRemoteTargetTable =
49 new nsTHashMap<nsUint32HashKey, BrowserParent*>;
53 /* static */
54 void PointerEventHandler::ReleaseStatics() {
55 MOZ_ASSERT(sPointerCaptureList, "ReleaseStatics called without Initialize!");
56 delete sPointerCaptureList;
57 sPointerCaptureList = nullptr;
58 delete sActivePointersIds;
59 sActivePointersIds = nullptr;
60 if (sPointerCaptureRemoteTargetTable) {
61 MOZ_ASSERT(XRE_IsParentProcess());
62 delete sPointerCaptureRemoteTargetTable;
63 sPointerCaptureRemoteTargetTable = nullptr;
67 /* static */
68 bool PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() {
69 return StaticPrefs::dom_w3c_pointer_events_implicit_capture();
72 /* static */
73 void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent,
74 nsIContent* aTargetContent) {
75 if (!aEvent) {
76 return;
78 switch (aEvent->mMessage) {
79 case eMouseEnterIntoWidget:
80 // In this case we have to know information about available mouse pointers
81 sActivePointersIds->InsertOrUpdate(
82 aEvent->pointerId,
83 MakeUnique<PointerInfo>(false, aEvent->mInputSource, true, nullptr));
85 MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId);
86 break;
87 case ePointerDown:
88 // In this case we switch pointer to active state
89 if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
90 // XXXedgar, test could possibly synthesize a mousedown event on a
91 // coordinate outside the browser window and cause aTargetContent to be
92 // nullptr, not sure if this also happens on real usage.
93 sActivePointersIds->InsertOrUpdate(
94 pointerEvent->pointerId,
95 MakeUnique<PointerInfo>(
96 true, pointerEvent->mInputSource, pointerEvent->mIsPrimary,
97 aTargetContent ? aTargetContent->OwnerDoc() : nullptr));
98 MaybeCacheSpoofedPointerID(pointerEvent->mInputSource,
99 pointerEvent->pointerId);
101 break;
102 case ePointerCancel:
103 // pointercancel means a pointer is unlikely to continue to produce
104 // pointer events. In that case, we should turn off active state or remove
105 // the pointer from active pointers.
106 case ePointerUp:
107 // In this case we remove information about pointer or turn off active
108 // state
109 if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
110 if (pointerEvent->mInputSource !=
111 MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
112 sActivePointersIds->InsertOrUpdate(
113 pointerEvent->pointerId,
114 MakeUnique<PointerInfo>(false, pointerEvent->mInputSource,
115 pointerEvent->mIsPrimary, nullptr));
116 } else {
117 sActivePointersIds->Remove(pointerEvent->pointerId);
120 break;
121 case eMouseExitFromWidget:
122 // In this case we have to remove information about disappeared mouse
123 // pointers
124 sActivePointersIds->Remove(aEvent->pointerId);
125 break;
126 default:
127 MOZ_ASSERT_UNREACHABLE("event has invalid type");
128 break;
132 /* static */
133 void PointerEventHandler::RequestPointerCaptureById(uint32_t aPointerId,
134 Element* aElement) {
135 SetPointerCaptureById(aPointerId, aElement);
137 if (BrowserChild* browserChild =
138 BrowserChild::GetFrom(aElement->OwnerDoc()->GetDocShell())) {
139 browserChild->SendRequestPointerCapture(
140 aPointerId,
141 [aPointerId](bool aSuccess) {
142 if (!aSuccess) {
143 PointerEventHandler::ReleasePointerCaptureById(aPointerId);
146 [](mozilla::ipc::ResponseRejectReason) {});
150 /* static */
151 void PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId,
152 Element* aElement) {
153 MOZ_ASSERT(aElement);
154 sPointerCaptureList->WithEntryHandle(aPointerId, [&](auto&& entry) {
155 if (entry) {
156 entry.Data()->mPendingElement = aElement;
157 } else {
158 entry.Insert(MakeUnique<PointerCaptureInfo>(aElement));
163 /* static */
164 PointerCaptureInfo* PointerEventHandler::GetPointerCaptureInfo(
165 uint32_t aPointerId) {
166 PointerCaptureInfo* pointerCaptureInfo = nullptr;
167 sPointerCaptureList->Get(aPointerId, &pointerCaptureInfo);
168 return pointerCaptureInfo;
171 /* static */
172 void PointerEventHandler::ReleasePointerCaptureById(uint32_t aPointerId) {
173 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
174 if (pointerCaptureInfo) {
175 if (Element* pendingElement = pointerCaptureInfo->mPendingElement) {
176 if (BrowserChild* browserChild = BrowserChild::GetFrom(
177 pendingElement->OwnerDoc()->GetDocShell())) {
178 browserChild->SendReleasePointerCapture(aPointerId);
181 pointerCaptureInfo->mPendingElement = nullptr;
185 /* static */
186 void PointerEventHandler::ReleaseAllPointerCapture() {
187 for (const auto& entry : *sPointerCaptureList) {
188 PointerCaptureInfo* data = entry.GetWeak();
189 if (data && data->mPendingElement) {
190 ReleasePointerCaptureById(entry.GetKey());
195 /* static */
196 bool PointerEventHandler::SetPointerCaptureRemoteTarget(
197 uint32_t aPointerId, dom::BrowserParent* aBrowserParent) {
198 MOZ_ASSERT(XRE_IsParentProcess());
199 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
200 MOZ_ASSERT(aBrowserParent);
202 if (PointerLockManager::GetLockedRemoteTarget()) {
203 return false;
206 BrowserParent* currentRemoteTarget =
207 PointerEventHandler::GetPointerCapturingRemoteTarget(aPointerId);
208 if (currentRemoteTarget && currentRemoteTarget != aBrowserParent) {
209 return false;
212 sPointerCaptureRemoteTargetTable->InsertOrUpdate(aPointerId, aBrowserParent);
213 return true;
216 /* static */
217 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
218 BrowserParent* aBrowserParent) {
219 MOZ_ASSERT(XRE_IsParentProcess());
220 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
221 MOZ_ASSERT(aBrowserParent);
223 sPointerCaptureRemoteTargetTable->RemoveIf([aBrowserParent](
224 const auto& iter) {
225 BrowserParent* browserParent = iter.Data();
226 MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");
228 return aBrowserParent == browserParent;
232 /* static */
233 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
234 uint32_t aPointerId) {
235 MOZ_ASSERT(XRE_IsParentProcess());
236 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
238 sPointerCaptureRemoteTargetTable->Remove(aPointerId);
241 /* static */
242 BrowserParent* PointerEventHandler::GetPointerCapturingRemoteTarget(
243 uint32_t aPointerId) {
244 MOZ_ASSERT(XRE_IsParentProcess());
245 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
247 return sPointerCaptureRemoteTargetTable->Get(aPointerId);
250 /* static */
251 void PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget() {
252 MOZ_ASSERT(XRE_IsParentProcess());
253 MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
255 for (auto iter = sPointerCaptureRemoteTargetTable->Iter(); !iter.Done();
256 iter.Next()) {
257 BrowserParent* browserParent = iter.Data();
258 MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");
260 Unused << browserParent->SendReleaseAllPointerCapture();
261 iter.Remove();
265 /* static */
266 const PointerInfo* PointerEventHandler::GetPointerInfo(uint32_t aPointerId) {
267 return sActivePointersIds->Get(aPointerId);
270 /* static */
271 void PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent* aEvent) {
272 switch (aEvent->mClass) {
273 case eMouseEventClass:
274 ProcessPointerCaptureForMouse(aEvent->AsMouseEvent());
275 break;
276 case eTouchEventClass:
277 ProcessPointerCaptureForTouch(aEvent->AsTouchEvent());
278 break;
279 default:
280 break;
284 /* static */
285 void PointerEventHandler::ProcessPointerCaptureForMouse(
286 WidgetMouseEvent* aEvent) {
287 if (!ShouldGeneratePointerEventFromMouse(aEvent)) {
288 return;
291 PointerCaptureInfo* info = GetPointerCaptureInfo(aEvent->pointerId);
292 if (!info || info->mPendingElement == info->mOverrideElement) {
293 return;
295 WidgetPointerEvent localEvent(*aEvent);
296 InitPointerEventFromMouse(&localEvent, aEvent, eVoidEvent);
297 CheckPointerCaptureState(&localEvent);
300 /* static */
301 void PointerEventHandler::ProcessPointerCaptureForTouch(
302 WidgetTouchEvent* aEvent) {
303 if (!ShouldGeneratePointerEventFromTouch(aEvent)) {
304 return;
307 for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
308 Touch* touch = aEvent->mTouches[i];
309 if (!TouchManager::ShouldConvertTouchToPointer(touch, aEvent)) {
310 continue;
312 PointerCaptureInfo* info = GetPointerCaptureInfo(touch->Identifier());
313 if (!info || info->mPendingElement == info->mOverrideElement) {
314 continue;
316 WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget);
317 InitPointerEventFromTouch(event, *aEvent, *touch, i == 0);
318 CheckPointerCaptureState(&event);
322 /* static */
323 void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) {
324 // Handle pending pointer capture before any pointer events except
325 // gotpointercapture / lostpointercapture.
326 if (!aEvent) {
327 return;
329 MOZ_ASSERT(aEvent->mClass == ePointerEventClass);
331 PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId);
333 // When fingerprinting resistance is enabled, we need to map other pointer
334 // ids into the spoofed one. We don't have to do the mapping if the capture
335 // info exists for the non-spoofed pointer id because of we won't allow
336 // content to set pointer capture other than the spoofed one. Thus, it must be
337 // from chrome if the capture info exists in this case. And we don't have to
338 // do anything if the pointer id is the same as the spoofed one.
339 if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check",
340 RFPTarget::PointerEvents) &&
341 aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() &&
342 !captureInfo) {
343 PointerCaptureInfo* spoofedCaptureInfo =
344 GetPointerCaptureInfo(GetSpoofedPointerIdForRFP());
346 // We need to check the target element's document should resist
347 // fingerprinting. If not, we don't need to send a capture event
348 // since the capture info of the original pointer id doesn't exist
349 // in this case.
350 if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement ||
351 !spoofedCaptureInfo->mPendingElement->OwnerDoc()
352 ->ShouldResistFingerprinting(RFPTarget::PointerEvents)) {
353 return;
356 captureInfo = spoofedCaptureInfo;
359 if (!captureInfo ||
360 captureInfo->mPendingElement == captureInfo->mOverrideElement) {
361 return;
364 RefPtr<Element> overrideElement = captureInfo->mOverrideElement;
365 RefPtr<Element> pendingElement = captureInfo->mPendingElement;
367 // Update captureInfo before dispatching event since sPointerCaptureList may
368 // be changed in the pointer event listener.
369 captureInfo->mOverrideElement = captureInfo->mPendingElement;
370 if (captureInfo->Empty()) {
371 sPointerCaptureList->Remove(aEvent->pointerId);
374 if (overrideElement) {
375 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent,
376 overrideElement);
378 if (pendingElement) {
379 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
380 pendingElement);
384 /* static */
385 void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
386 WidgetEvent* aEvent) {
387 MOZ_ASSERT(aEvent->mMessage == ePointerDown);
388 if (!aFrame || !IsPointerEventImplicitCaptureForTouchEnabled()) {
389 return;
391 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
392 NS_WARNING_ASSERTION(pointerEvent,
393 "Call ImplicitlyCapturePointer with non-pointer event");
394 if (!pointerEvent->mFromTouchEvent) {
395 // We only implicitly capture the pointer for touch device.
396 return;
398 nsCOMPtr<nsIContent> target;
399 aFrame->GetContentForEvent(aEvent, getter_AddRefs(target));
400 while (target && !target->IsElement()) {
401 target = target->GetParent();
403 if (NS_WARN_IF(!target)) {
404 return;
406 RequestPointerCaptureById(pointerEvent->pointerId, target->AsElement());
409 /* static */
410 void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent* aEvent) {
411 MOZ_ASSERT(aEvent);
412 if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) {
413 return;
415 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
416 ReleasePointerCaptureById(pointerEvent->pointerId);
417 CheckPointerCaptureState(pointerEvent);
420 /* static */
421 void PointerEventHandler::MaybeImplicitlyReleasePointerCapture(
422 WidgetGUIEvent* aEvent) {
423 MOZ_ASSERT(aEvent);
424 const EventMessage pointerEventMessage =
425 PointerEventHandler::ToPointerEventMessage(aEvent);
426 if (pointerEventMessage != ePointerUp &&
427 pointerEventMessage != ePointerCancel) {
428 return;
430 PointerEventHandler::MaybeProcessPointerCapture(aEvent);
433 /* static */
434 Element* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId) {
435 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
436 if (pointerCaptureInfo) {
437 return pointerCaptureInfo->mOverrideElement;
439 return nullptr;
442 /* static */
443 Element* PointerEventHandler::GetPointerCapturingElement(
444 WidgetGUIEvent* aEvent) {
445 if ((aEvent->mClass != ePointerEventClass &&
446 aEvent->mClass != eMouseEventClass) ||
447 aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) {
448 // Pointer capture should only be applied to all pointer events and mouse
449 // events except ePointerDown and eMouseDown;
450 return nullptr;
453 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
454 if (!mouseEvent) {
455 return nullptr;
457 return GetPointerCapturingElement(mouseEvent->pointerId);
460 /* static */
461 void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent) {
462 // We should check that aChild does not contain pointer capturing elements.
463 // If it does we should release the pointer capture for the elements.
464 for (const auto& entry : *sPointerCaptureList) {
465 PointerCaptureInfo* data = entry.GetWeak();
466 if (data && data->mPendingElement &&
467 data->mPendingElement->IsInclusiveDescendantOf(aContent)) {
468 ReleasePointerCaptureById(entry.GetKey());
473 /* static */
474 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
475 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
476 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
477 return;
479 PointerInfo* pointerInfo = nullptr;
480 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
481 !pointerInfo) {
482 // The PointerInfo for active pointer should be added for normal cases. But
483 // in some cases, we may receive mouse events before adding PointerInfo in
484 // sActivePointersIds. (e.g. receive mousemove before
485 // eMouseEnterIntoWidget). In these cases, we could ignore them because they
486 // are not the events between a DefaultPrevented pointerdown and the
487 // corresponding pointerup.
488 return;
490 if (!pointerInfo->mPreventMouseEventByContent) {
491 return;
493 aMouseOrTouchEvent->PreventDefault(false);
494 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
495 if (aPointerEvent->mMessage == ePointerUp) {
496 pointerInfo->mPreventMouseEventByContent = false;
500 /* static */
501 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
502 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
503 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
504 !aPointerEvent->DefaultPreventedByContent()) {
505 return;
507 PointerInfo* pointerInfo = nullptr;
508 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
509 !pointerInfo) {
510 // We already added the PointerInfo for active pointer when
511 // PresShell::HandleEvent handling pointerdown event.
512 #ifdef DEBUG
513 MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
514 #endif // #ifdef DEBUG
515 return;
517 // PreventDefault only applied for active pointers.
518 if (!pointerInfo->mActiveState) {
519 return;
521 aMouseOrTouchEvent->PreventDefault(false);
522 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
523 pointerInfo->mPreventMouseEventByContent = true;
526 /* static */
527 void PointerEventHandler::InitPointerEventFromMouse(
528 WidgetPointerEvent* aPointerEvent, WidgetMouseEvent* aMouseEvent,
529 EventMessage aMessage) {
530 MOZ_ASSERT(aPointerEvent);
531 MOZ_ASSERT(aMouseEvent);
532 aPointerEvent->pointerId = aMouseEvent->pointerId;
533 aPointerEvent->mInputSource = aMouseEvent->mInputSource;
534 aPointerEvent->mMessage = aMessage;
535 aPointerEvent->mButton = aMouseEvent->mMessage == eMouseMove
536 ? MouseButton::eNotPressed
537 : aMouseEvent->mButton;
539 aPointerEvent->mButtons = aMouseEvent->mButtons;
540 aPointerEvent->mPressure =
541 aPointerEvent->mButtons
542 ? aMouseEvent->mPressure ? aMouseEvent->mPressure : 0.5f
543 : 0.0f;
546 /* static */
547 void PointerEventHandler::InitPointerEventFromTouch(
548 WidgetPointerEvent& aPointerEvent, const WidgetTouchEvent& aTouchEvent,
549 const mozilla::dom::Touch& aTouch, bool aIsPrimary) {
550 // Use mButton/mButtons only when mButton got a value (from pen input)
551 int16_t button = aTouchEvent.mMessage == eTouchMove ? MouseButton::eNotPressed
552 : aTouchEvent.mButton != MouseButton::eNotPressed
553 ? aTouchEvent.mButton
554 : MouseButton::ePrimary;
555 int16_t buttons = aTouchEvent.mMessage == eTouchEnd
556 ? MouseButtonsFlag::eNoButtons
557 : aTouchEvent.mButton != MouseButton::eNotPressed
558 ? aTouchEvent.mButtons
559 : MouseButtonsFlag::ePrimaryFlag;
561 aPointerEvent.mIsPrimary = aIsPrimary;
562 aPointerEvent.pointerId = aTouch.Identifier();
563 aPointerEvent.mRefPoint = aTouch.mRefPoint;
564 aPointerEvent.mModifiers = aTouchEvent.mModifiers;
565 aPointerEvent.mWidth = aTouch.RadiusX(CallerType::System);
566 aPointerEvent.mHeight = aTouch.RadiusY(CallerType::System);
567 aPointerEvent.tiltX = aTouch.tiltX;
568 aPointerEvent.tiltY = aTouch.tiltY;
569 aPointerEvent.twist = aTouch.twist;
570 aPointerEvent.mTimeStamp = aTouchEvent.mTimeStamp;
571 aPointerEvent.mFlags = aTouchEvent.mFlags;
572 aPointerEvent.mButton = button;
573 aPointerEvent.mButtons = buttons;
574 aPointerEvent.mInputSource = aTouchEvent.mInputSource;
575 aPointerEvent.mFromTouchEvent = true;
576 aPointerEvent.mPressure = aTouch.mForce;
579 /* static */
580 EventMessage PointerEventHandler::ToPointerEventMessage(
581 const WidgetGUIEvent* aMouseOrTouchEvent) {
582 MOZ_ASSERT(aMouseOrTouchEvent);
584 switch (aMouseOrTouchEvent->mMessage) {
585 case eMouseMove:
586 return ePointerMove;
587 case eMouseUp:
588 return aMouseOrTouchEvent->AsMouseEvent()->mButtons ? ePointerMove
589 : ePointerUp;
590 case eMouseDown: {
591 const WidgetMouseEvent* mouseEvent = aMouseOrTouchEvent->AsMouseEvent();
592 return mouseEvent->mButtons & ~nsContentUtils::GetButtonsFlagForButton(
593 mouseEvent->mButton)
594 ? ePointerMove
595 : ePointerDown;
597 case eTouchMove:
598 return ePointerMove;
599 case eTouchEnd:
600 return ePointerUp;
601 case eTouchStart:
602 return ePointerDown;
603 case eTouchCancel:
604 case eTouchPointerCancel:
605 return ePointerCancel;
606 default:
607 return eVoidEvent;
611 /* static */
612 void PointerEventHandler::DispatchPointerFromMouseOrTouch(
613 PresShell* aShell, nsIFrame* aFrame, nsIContent* aContent,
614 WidgetGUIEvent* aEvent, bool aDontRetargetEvents, nsEventStatus* aStatus,
615 nsIContent** aTargetContent) {
616 MOZ_ASSERT(aFrame || aContent);
617 MOZ_ASSERT(aEvent);
619 EventMessage pointerMessage = eVoidEvent;
620 if (aEvent->mClass == eMouseEventClass) {
621 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
622 // Don't dispatch pointer events caused by a mouse when simulating touch
623 // devices in RDM.
624 Document* doc = aShell->GetDocument();
625 if (!doc) {
626 return;
629 BrowsingContext* bc = doc->GetBrowsingContext();
630 if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled &&
631 bc->InRDMPane()) {
632 return;
635 // 1. If it is not mouse then it is likely will come as touch event
636 // 2. We don't synthesize pointer events for those events that are not
637 // dispatched to DOM.
638 if (!mouseEvent->convertToPointer ||
639 !aEvent->IsAllowedToDispatchDOMEvent()) {
640 return;
643 pointerMessage = PointerEventHandler::ToPointerEventMessage(mouseEvent);
644 if (pointerMessage == eVoidEvent) {
645 return;
647 WidgetPointerEvent event(*mouseEvent);
648 InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
649 event.convertToPointer = mouseEvent->convertToPointer = false;
650 RefPtr<PresShell> shell(aShell);
651 if (!aFrame) {
652 shell = PresShell::GetShellForEventTarget(nullptr, aContent);
653 if (!shell) {
654 return;
657 PreHandlePointerEventsPreventDefault(&event, aEvent);
658 // Dispatch pointer event to the same target which is found by the
659 // corresponding mouse event.
660 shell->HandleEventWithTarget(&event, aFrame, aContent, aStatus, true,
661 aTargetContent);
662 PostHandlePointerEventsPreventDefault(&event, aEvent);
663 } else if (aEvent->mClass == eTouchEventClass) {
664 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
665 // loop over all touches and dispatch pointer events on each touch
666 // copy the event
667 pointerMessage = PointerEventHandler::ToPointerEventMessage(touchEvent);
668 if (pointerMessage == eVoidEvent) {
669 return;
671 RefPtr<PresShell> shell(aShell);
672 for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
673 Touch* touch = touchEvent->mTouches[i];
674 if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
675 continue;
678 WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
679 touchEvent->mWidget);
681 InitPointerEventFromTouch(event, *touchEvent, *touch, i == 0);
682 event.convertToPointer = touch->convertToPointer = false;
683 event.mCoalescedWidgetEvents = touch->mCoalescedWidgetEvents;
684 if (aEvent->mMessage == eTouchStart) {
685 // We already did hit test for touchstart in PresShell. We should
686 // dispatch pointerdown to the same target as touchstart.
687 nsCOMPtr<nsIContent> content =
688 nsIContent::FromEventTargetOrNull(touch->mTarget);
689 if (!content) {
690 continue;
693 nsIFrame* frame = content->GetPrimaryFrame();
694 shell = PresShell::GetShellForEventTarget(frame, content);
695 if (!shell) {
696 continue;
699 PreHandlePointerEventsPreventDefault(&event, aEvent);
700 shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
701 nullptr);
702 PostHandlePointerEventsPreventDefault(&event, aEvent);
703 } else {
704 // We didn't hit test for other touch events. Spec doesn't mention that
705 // all pointer events should be dispatched to the same target as their
706 // corresponding touch events. Call PresShell::HandleEvent so that we do
707 // hit test for pointer events.
708 PreHandlePointerEventsPreventDefault(&event, aEvent);
709 shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
710 PostHandlePointerEventsPreventDefault(&event, aEvent);
716 /* static */
717 void PointerEventHandler::NotifyDestroyPresContext(
718 nsPresContext* aPresContext) {
719 // Clean up pointer capture info
720 for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
721 PointerCaptureInfo* data = iter.UserData();
722 MOZ_ASSERT(data, "how could we have a null PointerCaptureInfo here?");
723 if (data->mPendingElement &&
724 data->mPendingElement->GetPresContext(Element::eForComposedDoc) ==
725 aPresContext) {
726 data->mPendingElement = nullptr;
728 if (data->mOverrideElement &&
729 data->mOverrideElement->GetPresContext(Element::eForComposedDoc) ==
730 aPresContext) {
731 data->mOverrideElement = nullptr;
733 if (data->Empty()) {
734 iter.Remove();
739 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) {
740 #ifdef XP_WIN
741 if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) {
742 // WM_POINTER does not support drag and drop, see bug 1692277
743 return (aEvent.mInputSource != dom::MouseEvent_Binding::MOZ_SOURCE_PEN &&
744 aEvent.mReason != WidgetMouseEvent::eSynthesized); // bug 1692151
746 #endif
747 return true;
750 /* static */
751 uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId) {
752 PointerInfo* pointerInfo = nullptr;
753 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
754 return pointerInfo->mPointerType;
756 return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
759 /* static */
760 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId) {
761 PointerInfo* pointerInfo = nullptr;
762 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
763 return pointerInfo->mPrimaryState;
765 return false;
768 /* static */
769 void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
770 bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent,
771 Element* aCaptureTarget) {
772 Document* targetDoc = aCaptureTarget->OwnerDoc();
773 RefPtr<PresShell> presShell = targetDoc->GetPresShell();
774 if (NS_WARN_IF(!presShell || presShell->IsDestroying())) {
775 return;
778 if (!aIsGotCapture && !aCaptureTarget->IsInComposedDoc()) {
779 // If the capturing element was removed from the DOM tree, fire
780 // ePointerLostCapture at the document.
781 PointerEventInit init;
782 init.mPointerId = aPointerEvent->pointerId;
783 init.mBubbles = true;
784 init.mComposed = true;
785 ConvertPointerTypeToString(aPointerEvent->mInputSource, init.mPointerType);
786 init.mIsPrimary = aPointerEvent->mIsPrimary;
787 RefPtr<PointerEvent> event;
788 event = PointerEvent::Constructor(aCaptureTarget, u"lostpointercapture"_ns,
789 init);
790 targetDoc->DispatchEvent(*event);
791 return;
793 nsEventStatus status = nsEventStatus_eIgnore;
794 WidgetPointerEvent localEvent(
795 aPointerEvent->IsTrusted(),
796 aIsGotCapture ? ePointerGotCapture : ePointerLostCapture,
797 aPointerEvent->mWidget);
799 localEvent.AssignPointerEventData(*aPointerEvent, true);
800 DebugOnly<nsresult> rv = presShell->HandleEventWithTarget(
801 &localEvent, aCaptureTarget->GetPrimaryFrame(), aCaptureTarget, &status);
803 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
804 "DispatchGotOrLostPointerCaptureEvent failed");
807 /* static */
808 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource,
809 uint32_t aPointerId) {
810 if (sSpoofedPointerId.isSome() || aInputSource != SPOOFED_POINTER_INTERFACE) {
811 return;
814 sSpoofedPointerId.emplace(aPointerId);
817 } // namespace mozilla