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