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