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/. */
6 
7 #include "PointerEventHandler.h"
8 #include "nsIFrame.h"
9 #include "PointerEvent.h"
10 #include "mozilla/PresShell.h"
11 
12 namespace mozilla {
13 
14 using namespace dom;
15 
16 static bool sPointerEventEnabled = true;
17 static bool sPointerEventImplicitCapture = false;
18 
19 class PointerInfo final {
20  public:
21   uint16_t mPointerType;
22   bool mActiveState;
23   bool mPrimaryState;
24   bool mPreventMouseEventByContent;
PointerInfo(bool aActiveState,uint16_t aPointerType,bool aPrimaryState)25   explicit PointerInfo(bool aActiveState, uint16_t aPointerType,
26                        bool aPrimaryState)
27       : mPointerType(aPointerType),
28         mActiveState(aActiveState),
29         mPrimaryState(aPrimaryState),
30         mPreventMouseEventByContent(false) {}
31 };
32 
33 // Keeps a map between pointerId and element that currently capturing pointer
34 // with such pointerId. If pointerId is absent in this map then nobody is
35 // capturing it. Additionally keep information about pending capturing content.
36 static nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>*
37     sPointerCaptureList;
38 
39 // Keeps information about pointers such as pointerId, activeState, pointerType,
40 // primaryState
41 static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
42 
Initialize()43 /* static */ void PointerEventHandler::Initialize() {
44   static bool initialized = false;
45   if (initialized) {
46     return;
47   }
48   initialized = true;
49   Preferences::AddBoolVarCache(&sPointerEventEnabled,
50                                "dom.w3c_pointer_events.enabled", true);
51   Preferences::AddBoolVarCache(&sPointerEventImplicitCapture,
52                                "dom.w3c_pointer_events.implicit_capture", true);
53 }
54 
InitializeStatics()55 /* static */ void PointerEventHandler::InitializeStatics() {
56   MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
57   sPointerCaptureList =
58       new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
59   sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
60 }
61 
ReleaseStatics()62 /* static */ void PointerEventHandler::ReleaseStatics() {
63   MOZ_ASSERT(sPointerCaptureList, "ReleaseStatics called without Initialize!");
64   delete sPointerCaptureList;
65   sPointerCaptureList = nullptr;
66   delete sActivePointersIds;
67   sActivePointersIds = nullptr;
68 }
69 
IsPointerEventEnabled()70 /* static */ bool PointerEventHandler::IsPointerEventEnabled() {
71   return sPointerEventEnabled;
72 }
73 
74 /* static */ bool
IsPointerEventImplicitCaptureForTouchEnabled()75 PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() {
76   return sPointerEventEnabled && sPointerEventImplicitCapture;
77 }
78 
UpdateActivePointerState(WidgetMouseEvent * aEvent)79 /* static */ void PointerEventHandler::UpdateActivePointerState(
80     WidgetMouseEvent* aEvent) {
81   if (!IsPointerEventEnabled() || !aEvent) {
82     return;
83   }
84   switch (aEvent->mMessage) {
85     case eMouseEnterIntoWidget:
86       // In this case we have to know information about available mouse pointers
87       sActivePointersIds->Put(
88           aEvent->pointerId, new PointerInfo(false, aEvent->inputSource, true));
89       break;
90     case ePointerDown:
91       // In this case we switch pointer to active state
92       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
93         sActivePointersIds->Put(pointerEvent->pointerId,
94                                 new PointerInfo(true, pointerEvent->inputSource,
95                                                 pointerEvent->mIsPrimary));
96       }
97       break;
98     case ePointerCancel:
99       // pointercancel means a pointer is unlikely to continue to produce
100       // pointer events. In that case, we should turn off active state or remove
101       // the pointer from active pointers.
102     case ePointerUp:
103       // In this case we remove information about pointer or turn off active
104       // state
105       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
106         if (pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
107           sActivePointersIds->Put(
108               pointerEvent->pointerId,
109               new PointerInfo(false, pointerEvent->inputSource,
110                               pointerEvent->mIsPrimary));
111         } else {
112           sActivePointersIds->Remove(pointerEvent->pointerId);
113         }
114       }
115       break;
116     case eMouseExitFromWidget:
117       // In this case we have to remove information about disappeared mouse
118       // pointers
119       sActivePointersIds->Remove(aEvent->pointerId);
120       break;
121     default:
122       break;
123   }
124 }
125 
SetPointerCaptureById(uint32_t aPointerId,nsIContent * aContent)126 /* static */ void PointerEventHandler::SetPointerCaptureById(
127     uint32_t aPointerId, nsIContent* aContent) {
128   MOZ_ASSERT(aContent);
129   if (nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == GetPointerType(aPointerId)) {
130     nsIPresShell::SetCapturingContent(aContent, CAPTURE_PREVENTDRAG);
131   }
132 
133   PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
134   if (pointerCaptureInfo) {
135     pointerCaptureInfo->mPendingContent = aContent;
136   } else {
137     sPointerCaptureList->Put(aPointerId, new PointerCaptureInfo(aContent));
138   }
139 }
140 
GetPointerCaptureInfo(uint32_t aPointerId)141 /* static */ PointerCaptureInfo* PointerEventHandler::GetPointerCaptureInfo(
142     uint32_t aPointerId) {
143   PointerCaptureInfo* pointerCaptureInfo = nullptr;
144   sPointerCaptureList->Get(aPointerId, &pointerCaptureInfo);
145   return pointerCaptureInfo;
146 }
147 
ReleasePointerCaptureById(uint32_t aPointerId)148 /* static */ void PointerEventHandler::ReleasePointerCaptureById(
149     uint32_t aPointerId) {
150   PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
151   if (pointerCaptureInfo && pointerCaptureInfo->mPendingContent) {
152     if (nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == GetPointerType(aPointerId)) {
153       nsIPresShell::SetCapturingContent(nullptr, CAPTURE_PREVENTDRAG);
154     }
155     pointerCaptureInfo->mPendingContent = nullptr;
156   }
157 }
158 
ReleaseAllPointerCapture()159 /* static */ void PointerEventHandler::ReleaseAllPointerCapture() {
160   for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
161     PointerCaptureInfo* data = iter.UserData();
162     if (data && data->mPendingContent) {
163       ReleasePointerCaptureById(iter.Key());
164     }
165   }
166 }
167 
GetPointerInfo(uint32_t aPointerId,bool & aActiveState)168 /* static */ bool PointerEventHandler::GetPointerInfo(uint32_t aPointerId,
169                                                       bool& aActiveState) {
170   PointerInfo* pointerInfo = nullptr;
171   if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
172     aActiveState = pointerInfo->mActiveState;
173     return true;
174   }
175   return false;
176 }
177 
MaybeProcessPointerCapture(WidgetGUIEvent * aEvent)178 /* static */ void PointerEventHandler::MaybeProcessPointerCapture(
179     WidgetGUIEvent* aEvent) {
180   switch (aEvent->mClass) {
181     case eMouseEventClass:
182       ProcessPointerCaptureForMouse(aEvent->AsMouseEvent());
183       break;
184     case eTouchEventClass:
185       ProcessPointerCaptureForTouch(aEvent->AsTouchEvent());
186       break;
187     default:
188       break;
189   }
190 }
191 
ProcessPointerCaptureForMouse(WidgetMouseEvent * aEvent)192 /* static */ void PointerEventHandler::ProcessPointerCaptureForMouse(
193     WidgetMouseEvent* aEvent) {
194   if (!ShouldGeneratePointerEventFromMouse(aEvent)) {
195     return;
196   }
197 
198   PointerCaptureInfo* info = GetPointerCaptureInfo(aEvent->pointerId);
199   if (!info || info->mPendingContent == info->mOverrideContent) {
200     return;
201   }
202   WidgetPointerEvent localEvent(*aEvent);
203   InitPointerEventFromMouse(&localEvent, aEvent, eVoidEvent);
204   CheckPointerCaptureState(&localEvent);
205 }
206 
ProcessPointerCaptureForTouch(WidgetTouchEvent * aEvent)207 /* static */ void PointerEventHandler::ProcessPointerCaptureForTouch(
208     WidgetTouchEvent* aEvent) {
209   if (!ShouldGeneratePointerEventFromTouch(aEvent)) {
210     return;
211   }
212 
213   for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
214     Touch* touch = aEvent->mTouches[i];
215     if (!TouchManager::ShouldConvertTouchToPointer(touch, aEvent)) {
216       continue;
217     }
218     PointerCaptureInfo* info = GetPointerCaptureInfo(touch->Identifier());
219     if (!info || info->mPendingContent == info->mOverrideContent) {
220       continue;
221     }
222     WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget);
223     InitPointerEventFromTouch(&event, aEvent, touch, i == 0);
224     CheckPointerCaptureState(&event);
225   }
226 }
227 
CheckPointerCaptureState(WidgetPointerEvent * aEvent)228 /* static */ void PointerEventHandler::CheckPointerCaptureState(
229     WidgetPointerEvent* aEvent) {
230   // Handle pending pointer capture before any pointer events except
231   // gotpointercapture / lostpointercapture.
232   if (!aEvent) {
233     return;
234   }
235   MOZ_ASSERT(IsPointerEventEnabled());
236   MOZ_ASSERT(aEvent->mClass == ePointerEventClass);
237 
238   PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId);
239 
240   if (!captureInfo ||
241       captureInfo->mPendingContent == captureInfo->mOverrideContent) {
242     return;
243   }
244   // cache captureInfo->mPendingContent since it may be changed in the pointer
245   // event listener
246   nsIContent* pendingContent = captureInfo->mPendingContent.get();
247   if (captureInfo->mOverrideContent) {
248     DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent,
249                                          captureInfo->mOverrideContent);
250   }
251   if (pendingContent) {
252     DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
253                                          pendingContent);
254   }
255 
256   captureInfo->mOverrideContent = pendingContent;
257   if (captureInfo->Empty()) {
258     sPointerCaptureList->Remove(aEvent->pointerId);
259   }
260 }
261 
ImplicitlyCapturePointer(nsIFrame * aFrame,WidgetEvent * aEvent)262 /* static */ void PointerEventHandler::ImplicitlyCapturePointer(
263     nsIFrame* aFrame, WidgetEvent* aEvent) {
264   MOZ_ASSERT(aEvent->mMessage == ePointerDown);
265   if (!aFrame || !IsPointerEventEnabled() ||
266       !IsPointerEventImplicitCaptureForTouchEnabled()) {
267     return;
268   }
269   WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
270   NS_WARNING_ASSERTION(pointerEvent,
271                        "Call ImplicitlyCapturePointer with non-pointer event");
272   if (pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
273     // We only implicitly capture the pointer for touch device.
274     return;
275   }
276   nsCOMPtr<nsIContent> target;
277   aFrame->GetContentForEvent(aEvent, getter_AddRefs(target));
278   while (target && !target->IsElement()) {
279     target = target->GetParent();
280   }
281   if (NS_WARN_IF(!target)) {
282     return;
283   }
284   SetPointerCaptureById(pointerEvent->pointerId, target);
285 }
286 
ImplicitlyReleasePointerCapture(WidgetEvent * aEvent)287 /* static */ void PointerEventHandler::ImplicitlyReleasePointerCapture(
288     WidgetEvent* aEvent) {
289   MOZ_ASSERT(aEvent);
290   if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) {
291     return;
292   }
293   WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
294   ReleasePointerCaptureById(pointerEvent->pointerId);
295   CheckPointerCaptureState(pointerEvent);
296 }
297 
GetPointerCapturingContent(uint32_t aPointerId)298 /* static */ nsIContent* PointerEventHandler::GetPointerCapturingContent(
299     uint32_t aPointerId) {
300   PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
301   if (pointerCaptureInfo) {
302     return pointerCaptureInfo->mOverrideContent;
303   }
304   return nullptr;
305 }
306 
GetPointerCapturingContent(WidgetGUIEvent * aEvent)307 /* static */ nsIContent* PointerEventHandler::GetPointerCapturingContent(
308     WidgetGUIEvent* aEvent) {
309   if (!IsPointerEventEnabled() ||
310       (aEvent->mClass != ePointerEventClass &&
311        aEvent->mClass != eMouseEventClass) ||
312       aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) {
313     // Pointer capture should only be applied to all pointer events and mouse
314     // events except ePointerDown and eMouseDown;
315     return nullptr;
316   }
317 
318   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
319   if (!mouseEvent) {
320     return nullptr;
321   }
322   return GetPointerCapturingContent(mouseEvent->pointerId);
323 }
324 
ReleaseIfCaptureByDescendant(nsIContent * aContent)325 /* static */ void PointerEventHandler::ReleaseIfCaptureByDescendant(
326     nsIContent* aContent) {
327   // We should check that aChild does not contain pointer capturing elements.
328   // If it does we should release the pointer capture for the elements.
329   for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
330     PointerCaptureInfo* data = iter.UserData();
331     if (data && data->mPendingContent &&
332         nsContentUtils::ContentIsDescendantOf(data->mPendingContent,
333                                               aContent)) {
334       ReleasePointerCaptureById(iter.Key());
335     }
336   }
337 }
338 
PreHandlePointerEventsPreventDefault(WidgetPointerEvent * aPointerEvent,WidgetGUIEvent * aMouseOrTouchEvent)339 /* static */ void PointerEventHandler::PreHandlePointerEventsPreventDefault(
340     WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
341   if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
342     return;
343   }
344   PointerInfo* pointerInfo = nullptr;
345   if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
346       !pointerInfo) {
347     // The PointerInfo for active pointer should be added for normal cases. But
348     // in some cases, we may receive mouse events before adding PointerInfo in
349     // sActivePointersIds. (e.g. receive mousemove before eMouseEnterIntoWidget
350     // or change preference 'dom.w3c_pointer_events.enabled' from off to on).
351     // In these cases, we could ignore them because they are not the events
352     // between a DefaultPrevented pointerdown and the corresponding pointerup.
353     return;
354   }
355   if (!pointerInfo->mPreventMouseEventByContent) {
356     return;
357   }
358   aMouseOrTouchEvent->PreventDefault(false);
359   aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
360   if (aPointerEvent->mMessage == ePointerUp) {
361     pointerInfo->mPreventMouseEventByContent = false;
362   }
363 }
364 
PostHandlePointerEventsPreventDefault(WidgetPointerEvent * aPointerEvent,WidgetGUIEvent * aMouseOrTouchEvent)365 /* static */ void PointerEventHandler::PostHandlePointerEventsPreventDefault(
366     WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
367   if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
368       !aPointerEvent->DefaultPreventedByContent()) {
369     return;
370   }
371   PointerInfo* pointerInfo = nullptr;
372   if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
373       !pointerInfo) {
374   // We already added the PointerInfo for active pointer when
375   // PresShell::HandleEvent handling pointerdown event.
376 #ifdef DEBUG
377     MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
378 #endif  // #ifdef DEBUG
379     return;
380   }
381   // PreventDefault only applied for active pointers.
382   if (!pointerInfo->mActiveState) {
383     return;
384   }
385   aMouseOrTouchEvent->PreventDefault(false);
386   aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
387   pointerInfo->mPreventMouseEventByContent = true;
388 }
389 
InitPointerEventFromMouse(WidgetPointerEvent * aPointerEvent,WidgetMouseEvent * aMouseEvent,EventMessage aMessage)390 /* static */ void PointerEventHandler::InitPointerEventFromMouse(
391     WidgetPointerEvent* aPointerEvent, WidgetMouseEvent* aMouseEvent,
392     EventMessage aMessage) {
393   MOZ_ASSERT(aPointerEvent);
394   MOZ_ASSERT(aMouseEvent);
395   aPointerEvent->pointerId = aMouseEvent->pointerId;
396   aPointerEvent->inputSource = aMouseEvent->inputSource;
397   aPointerEvent->mMessage = aMessage;
398   aPointerEvent->button = aMouseEvent->mMessage == eMouseMove
399                               ? WidgetMouseEvent::eNoButton
400                               : aMouseEvent->button;
401 
402   aPointerEvent->buttons = aMouseEvent->buttons;
403   aPointerEvent->pressure =
404       aPointerEvent->buttons
405           ? aMouseEvent->pressure ? aMouseEvent->pressure : 0.5f
406           : 0.0f;
407 }
408 
InitPointerEventFromTouch(WidgetPointerEvent * aPointerEvent,WidgetTouchEvent * aTouchEvent,mozilla::dom::Touch * aTouch,bool aIsPrimary)409 /* static */ void PointerEventHandler::InitPointerEventFromTouch(
410     WidgetPointerEvent* aPointerEvent, WidgetTouchEvent* aTouchEvent,
411     mozilla::dom::Touch* aTouch, bool aIsPrimary) {
412   MOZ_ASSERT(aPointerEvent);
413   MOZ_ASSERT(aTouchEvent);
414 
415   int16_t button = aTouchEvent->mMessage == eTouchMove
416                        ? WidgetMouseEvent::eNoButton
417                        : WidgetMouseEvent::eLeftButton;
418 
419   int16_t buttons = aTouchEvent->mMessage == eTouchEnd
420                         ? WidgetMouseEvent::eNoButtonFlag
421                         : WidgetMouseEvent::eLeftButtonFlag;
422 
423   aPointerEvent->mIsPrimary = aIsPrimary;
424   aPointerEvent->pointerId = aTouch->Identifier();
425   aPointerEvent->mRefPoint = aTouch->mRefPoint;
426   aPointerEvent->mModifiers = aTouchEvent->mModifiers;
427   aPointerEvent->mWidth = aTouch->RadiusX(CallerType::System);
428   aPointerEvent->mHeight = aTouch->RadiusY(CallerType::System);
429   aPointerEvent->tiltX = aTouch->tiltX;
430   aPointerEvent->tiltY = aTouch->tiltY;
431   aPointerEvent->mTime = aTouchEvent->mTime;
432   aPointerEvent->mTimeStamp = aTouchEvent->mTimeStamp;
433   aPointerEvent->mFlags = aTouchEvent->mFlags;
434   aPointerEvent->button = button;
435   aPointerEvent->buttons = buttons;
436   aPointerEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
437 }
438 
DispatchPointerFromMouseOrTouch(PresShell * aShell,nsIFrame * aFrame,nsIContent * aContent,WidgetGUIEvent * aEvent,bool aDontRetargetEvents,nsEventStatus * aStatus,nsIContent ** aTargetContent)439 /* static */ void PointerEventHandler::DispatchPointerFromMouseOrTouch(
440     PresShell* aShell, nsIFrame* aFrame, nsIContent* aContent,
441     WidgetGUIEvent* aEvent, bool aDontRetargetEvents, nsEventStatus* aStatus,
442     nsIContent** aTargetContent) {
443   MOZ_ASSERT(IsPointerEventEnabled());
444   MOZ_ASSERT(aFrame || aContent);
445   MOZ_ASSERT(aEvent);
446 
447   EventMessage pointerMessage = eVoidEvent;
448   if (aEvent->mClass == eMouseEventClass) {
449     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
450     // 1. If it is not mouse then it is likely will come as touch event
451     // 2. We don't synthesize pointer events for those events that are not
452     //    dispatched to DOM.
453     if (!mouseEvent->convertToPointer ||
454         !aEvent->IsAllowedToDispatchDOMEvent()) {
455       return;
456     }
457     int16_t button = mouseEvent->button;
458     switch (mouseEvent->mMessage) {
459       case eMouseMove:
460         button = WidgetMouseEvent::eNoButton;
461         pointerMessage = ePointerMove;
462         break;
463       case eMouseUp:
464         pointerMessage = mouseEvent->buttons ? ePointerMove : ePointerUp;
465         break;
466       case eMouseDown:
467         pointerMessage =
468             mouseEvent->buttons &
469                     ~nsContentUtils::GetButtonsFlagForButton(button)
470                 ? ePointerMove
471                 : ePointerDown;
472         break;
473       default:
474         return;
475     }
476 
477     WidgetPointerEvent event(*mouseEvent);
478     InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
479     event.convertToPointer = mouseEvent->convertToPointer = false;
480     RefPtr<PresShell> shell(aShell);
481     if (!aFrame) {
482       shell = PresShell::GetShellForEventTarget(nullptr, aContent);
483       if (!shell) {
484         return;
485       }
486     }
487     PreHandlePointerEventsPreventDefault(&event, aEvent);
488     // Dispatch pointer event to the same target which is found by the
489     // corresponding mouse event.
490     shell->HandleEventWithTarget(&event, aFrame, aContent, aStatus, true,
491                                  aTargetContent);
492     PostHandlePointerEventsPreventDefault(&event, aEvent);
493   } else if (aEvent->mClass == eTouchEventClass) {
494     WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
495     // loop over all touches and dispatch pointer events on each touch
496     // copy the event
497     switch (touchEvent->mMessage) {
498       case eTouchMove:
499         pointerMessage = ePointerMove;
500         break;
501       case eTouchEnd:
502         pointerMessage = ePointerUp;
503         break;
504       case eTouchStart:
505         pointerMessage = ePointerDown;
506         break;
507       case eTouchCancel:
508       case eTouchPointerCancel:
509         pointerMessage = ePointerCancel;
510         break;
511       default:
512         return;
513     }
514 
515     RefPtr<PresShell> shell(aShell);
516     for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
517       Touch* touch = touchEvent->mTouches[i];
518       if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
519         continue;
520       }
521 
522       WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
523                                touchEvent->mWidget);
524 
525       InitPointerEventFromTouch(&event, touchEvent, touch, i == 0);
526       event.convertToPointer = touch->convertToPointer = false;
527       if (aEvent->mMessage == eTouchStart) {
528         // We already did hit test for touchstart in PresShell. We should
529         // dispatch pointerdown to the same target as touchstart.
530         nsCOMPtr<nsIContent> content = do_QueryInterface(touch->mTarget);
531         if (!content) {
532           continue;
533         }
534 
535         nsIFrame* frame = content->GetPrimaryFrame();
536         shell = PresShell::GetShellForEventTarget(frame, content);
537         if (!shell) {
538           continue;
539         }
540 
541         PreHandlePointerEventsPreventDefault(&event, aEvent);
542         shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
543                                      nullptr);
544         PostHandlePointerEventsPreventDefault(&event, aEvent);
545       } else {
546         // We didn't hit test for other touch events. Spec doesn't mention that
547         // all pointer events should be dispatched to the same target as their
548         // corresponding touch events. Call PresShell::HandleEvent so that we do
549         // hit test for pointer events.
550         PreHandlePointerEventsPreventDefault(&event, aEvent);
551         shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
552         PostHandlePointerEventsPreventDefault(&event, aEvent);
553       }
554     }
555   }
556 }
557 
GetPointerType(uint32_t aPointerId)558 /* static */ uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId) {
559   PointerInfo* pointerInfo = nullptr;
560   if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
561     return pointerInfo->mPointerType;
562   }
563   return nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
564 }
565 
GetPointerPrimaryState(uint32_t aPointerId)566 /* static */ bool PointerEventHandler::GetPointerPrimaryState(
567     uint32_t aPointerId) {
568   PointerInfo* pointerInfo = nullptr;
569   if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
570     return pointerInfo->mPrimaryState;
571   }
572   return false;
573 }
574 
DispatchGotOrLostPointerCaptureEvent(bool aIsGotCapture,const WidgetPointerEvent * aPointerEvent,nsIContent * aCaptureTarget)575 /* static */ void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
576     bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent,
577     nsIContent* aCaptureTarget) {
578   nsIDocument* targetDoc = aCaptureTarget->OwnerDoc();
579   nsCOMPtr<nsIPresShell> shell = targetDoc->GetShell();
580   if (NS_WARN_IF(!shell)) {
581     return;
582   }
583 
584   if (!aIsGotCapture && !aCaptureTarget->IsInUncomposedDoc()) {
585     // If the capturing element was removed from the DOM tree, fire
586     // ePointerLostCapture at the document.
587     PointerEventInit init;
588     init.mPointerId = aPointerEvent->pointerId;
589     init.mBubbles = true;
590     init.mComposed = true;
591     ConvertPointerTypeToString(aPointerEvent->inputSource, init.mPointerType);
592     init.mIsPrimary = aPointerEvent->mIsPrimary;
593     RefPtr<PointerEvent> event;
594     event = PointerEvent::Constructor(
595         aCaptureTarget, NS_LITERAL_STRING("lostpointercapture"), init);
596     bool dummy;
597     targetDoc->DispatchEvent(event->InternalDOMEvent(), &dummy);
598     return;
599   }
600   nsEventStatus status = nsEventStatus_eIgnore;
601   WidgetPointerEvent localEvent(
602       aPointerEvent->IsTrusted(),
603       aIsGotCapture ? ePointerGotCapture : ePointerLostCapture,
604       aPointerEvent->mWidget);
605 
606   localEvent.AssignPointerEventData(*aPointerEvent, true);
607   DebugOnly<nsresult> rv = shell->HandleEventWithTarget(
608       &localEvent, aCaptureTarget->GetPrimaryFrame(), aCaptureTarget, &status);
609 
610   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
611                        "DispatchGotOrLostPointerCaptureEvent failed");
612 }
613 
614 }  // namespace mozilla
615