OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/input/TouchEventManager.h" | 5 #include "core/input/TouchEventManager.h" |
6 | 6 |
7 #include "core/dom/Document.h" | 7 #include "core/dom/Document.h" |
8 #include "core/dom/DocumentUserGestureToken.h" | |
9 #include "core/events/TouchEvent.h" | 8 #include "core/events/TouchEvent.h" |
10 #include "core/frame/Deprecation.h" | 9 #include "core/frame/Deprecation.h" |
11 #include "core/frame/EventHandlerRegistry.h" | 10 #include "core/frame/EventHandlerRegistry.h" |
12 #include "core/frame/FrameHost.h" | 11 #include "core/frame/FrameHost.h" |
13 #include "core/frame/FrameView.h" | 12 #include "core/frame/FrameView.h" |
14 #include "core/html/HTMLCanvasElement.h" | 13 #include "core/html/HTMLCanvasElement.h" |
15 #include "core/input/EventHandlingUtil.h" | 14 #include "core/input/EventHandlingUtil.h" |
16 #include "core/input/TouchActionUtil.h" | 15 #include "core/input/TouchActionUtil.h" |
17 #include "core/layout/HitTestCanvasResult.h" | 16 #include "core/layout/HitTestCanvasResult.h" |
18 #include "core/page/ChromeClient.h" | 17 #include "core/page/ChromeClient.h" |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 }; | 83 }; |
85 | 84 |
86 } // namespace | 85 } // namespace |
87 | 86 |
88 TouchEventManager::TouchEventManager(LocalFrame* frame) : m_frame(frame) { | 87 TouchEventManager::TouchEventManager(LocalFrame* frame) : m_frame(frame) { |
89 clear(); | 88 clear(); |
90 } | 89 } |
91 | 90 |
92 void TouchEventManager::clear() { | 91 void TouchEventManager::clear() { |
93 m_touchSequenceDocument.clear(); | 92 m_touchSequenceDocument.clear(); |
94 m_touchSequenceUserGestureToken.clear(); | |
95 m_targetForTouchID.clear(); | 93 m_targetForTouchID.clear(); |
96 m_regionForTouchID.clear(); | 94 m_regionForTouchID.clear(); |
97 m_touchPressed = false; | 95 m_touchPressed = false; |
98 m_touchScrollStarted = false; | |
99 m_currentEvent = PlatformEvent::NoType; | 96 m_currentEvent = PlatformEvent::NoType; |
100 } | 97 } |
101 | 98 |
102 DEFINE_TRACE(TouchEventManager) { | 99 DEFINE_TRACE(TouchEventManager) { |
103 visitor->trace(m_frame); | 100 visitor->trace(m_frame); |
104 visitor->trace(m_touchSequenceDocument); | 101 visitor->trace(m_touchSequenceDocument); |
105 visitor->trace(m_targetForTouchID); | 102 visitor->trace(m_targetForTouchID); |
106 } | 103 } |
107 | 104 |
108 WebInputEventResult TouchEventManager::dispatchTouchEvents( | 105 WebInputEventResult TouchEventManager::dispatchTouchEvents( |
109 const PlatformTouchEvent& event, | 106 const PlatformTouchEvent& event, |
110 const HeapVector<TouchInfo>& touchInfos, | 107 const HeapVector<TouchInfo>& touchInfos, |
111 bool allTouchesReleased) { | 108 bool allTouchesReleased) { |
112 // Build up the lists to use for the |touches|, |targetTouches| and | 109 // Build up the lists to use for the |touches|, |targetTouches| and |
113 // |changedTouches| attributes in the JS event. See | 110 // |changedTouches| attributes in the JS event. See |
114 // http://www.w3.org/TR/touch-events/#touchevent-interface for how these | 111 // http://www.w3.org/TR/touch-events/#touchevent-interface for how these |
115 // lists fit together. | 112 // lists fit together. |
116 | 113 |
117 // Holds the complete set of touches on the screen. | 114 // Holds the complete set of touches on the screen. |
118 TouchList* touches = TouchList::create(); | 115 TouchList* touches = TouchList::create(); |
119 | 116 |
120 // A different view on the 'touches' list above, filtered and grouped by | 117 // A different view on the 'touches' list above, filtered and grouped by |
121 // event target. Used for the |targetTouches| list in the JS event. | 118 // event target. Used for the |targetTouches| list in the JS event. |
122 using TargetTouchesHeapMap = HeapHashMap<EventTarget*, Member<TouchList>>; | 119 using TargetTouchesHeapMap = HeapHashMap<EventTarget*, Member<TouchList>>; |
123 TargetTouchesHeapMap touchesByTarget; | 120 TargetTouchesHeapMap touchesByTarget; |
124 | 121 |
125 // Array of touches per state, used to assemble the |changedTouches| list. | 122 // Array of touches per state, used to assemble the |changedTouches| list. |
126 ChangedTouches changedTouches[PlatformTouchPoint::TouchStateEnd]; | 123 ChangedTouches changedTouches[PlatformTouchPoint::TouchStateEnd]; |
127 | 124 |
128 for (unsigned i = 0; i < touchInfos.size(); ++i) { | 125 for (auto touchInfo : touchInfos) { |
129 const TouchInfo& touchInfo = touchInfos[i]; | |
130 const PlatformTouchPoint& point = touchInfo.point; | 126 const PlatformTouchPoint& point = touchInfo.point; |
131 PlatformTouchPoint::TouchState pointState = point.state(); | 127 PlatformTouchPoint::TouchState pointState = point.state(); |
132 | 128 |
133 Touch* touch = Touch::create( | 129 Touch* touch = Touch::create( |
134 touchInfo.targetFrame.get(), touchInfo.touchNode.get(), point.id(), | 130 touchInfo.targetFrame.get(), touchInfo.touchNode.get(), point.id(), |
135 point.screenPos(), touchInfo.contentPoint, touchInfo.adjustedRadius, | 131 point.screenPos(), touchInfo.contentPoint, touchInfo.adjustedRadius, |
136 point.rotationAngle(), point.force(), touchInfo.region); | 132 point.rotationAngle(), point.force(), touchInfo.region); |
137 | 133 |
138 // Ensure this target's touch list exists, even if it ends up empty, so | 134 // Ensure this target's touch list exists, even if it ends up empty, so |
139 // it can always be passed to TouchEvent::Create below. | 135 // it can always be passed to TouchEvent::Create below. |
(...skipping 24 matching lines...) Expand all Loading... |
164 ASSERT(pointState < PlatformTouchPoint::TouchStateEnd); | 160 ASSERT(pointState < PlatformTouchPoint::TouchStateEnd); |
165 if (!changedTouches[pointState].m_touches) | 161 if (!changedTouches[pointState].m_touches) |
166 changedTouches[pointState].m_touches = TouchList::create(); | 162 changedTouches[pointState].m_touches = TouchList::create(); |
167 changedTouches[pointState].m_touches->append(touch); | 163 changedTouches[pointState].m_touches->append(touch); |
168 changedTouches[pointState].m_targets.add(touchInfo.touchNode); | 164 changedTouches[pointState].m_targets.add(touchInfo.touchNode); |
169 } | 165 } |
170 } | 166 } |
171 | 167 |
172 if (allTouchesReleased) { | 168 if (allTouchesReleased) { |
173 m_touchSequenceDocument.clear(); | 169 m_touchSequenceDocument.clear(); |
174 m_touchSequenceUserGestureToken.clear(); | |
175 } | 170 } |
176 | 171 |
177 WebInputEventResult eventResult = WebInputEventResult::NotHandled; | 172 WebInputEventResult eventResult = WebInputEventResult::NotHandled; |
178 | 173 |
179 // Now iterate through the |changedTouches| list and |m_targets| within it, | 174 // Now iterate through the |changedTouches| list and |m_targets| within it, |
180 // sending TouchEvents to the targets as required. | 175 // sending TouchEvents to the targets as required. |
181 for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; | 176 for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; |
182 ++state) { | 177 ++state) { |
183 if (!changedTouches[state].m_touches) | 178 if (!changedTouches[state].m_touches) |
184 continue; | 179 continue; |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 ? HandledTouches | 254 ? HandledTouches |
260 : UnhandledTouches); | 255 : UnhandledTouches); |
261 } | 256 } |
262 } | 257 } |
263 eventResult = EventHandlingUtil::mergeEventResult( | 258 eventResult = EventHandlingUtil::mergeEventResult( |
264 eventResult, | 259 eventResult, |
265 EventHandlingUtil::toWebInputEventResult(domDispatchResult)); | 260 EventHandlingUtil::toWebInputEventResult(domDispatchResult)); |
266 } | 261 } |
267 } | 262 } |
268 | 263 |
269 if (allTouchesReleased) | |
270 m_touchScrollStarted = false; | |
271 | |
272 return eventResult; | 264 return eventResult; |
273 } | 265 } |
274 | 266 |
275 void TouchEventManager::updateTargetAndRegionMapsForTouchStarts( | 267 void TouchEventManager::updateTargetAndRegionMapsForTouchStarts( |
276 HeapVector<TouchInfo>& touchInfos) { | 268 HeapVector<TouchInfo>& touchInfos) { |
277 for (auto& touchInfo : touchInfos) { | 269 for (auto& touchInfo : touchInfos) { |
278 // Touch events implicitly capture to the touched node, and don't change | 270 // Touch events implicitly capture to the touched node, and don't change |
279 // active/hover states themselves (Gesture events do). So we only need | 271 // active/hover states themselves (Gesture events do). So we only need |
280 // to hit-test on touchstart and when the target could be different than | 272 // to hit-test on touchstart and when the target could be different than |
281 // the corresponding pointer event target. | 273 // the corresponding pointer event target. |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
426 point.state() != PlatformTouchPoint::TouchCancelled) | 418 point.state() != PlatformTouchPoint::TouchCancelled) |
427 allTouchesReleased = false; | 419 allTouchesReleased = false; |
428 } | 420 } |
429 if (newTouchSequence) { | 421 if (newTouchSequence) { |
430 // Ideally we'd ASSERT(!m_touchSequenceDocument) here since we should | 422 // Ideally we'd ASSERT(!m_touchSequenceDocument) here since we should |
431 // have cleared the active document when we saw the last release. But we | 423 // have cleared the active document when we saw the last release. But we |
432 // have some tests that violate this, ClusterFuzz could trigger it, and | 424 // have some tests that violate this, ClusterFuzz could trigger it, and |
433 // there may be cases where the browser doesn't reliably release all | 425 // there may be cases where the browser doesn't reliably release all |
434 // touches. http://crbug.com/345372 tracks this. | 426 // touches. http://crbug.com/345372 tracks this. |
435 m_touchSequenceDocument.clear(); | 427 m_touchSequenceDocument.clear(); |
436 m_touchSequenceUserGestureToken.clear(); | |
437 } | 428 } |
438 | 429 |
439 ASSERT(m_frame->view()); | 430 ASSERT(m_frame->view()); |
440 if (m_touchSequenceDocument && (!m_touchSequenceDocument->frame() || | 431 if (m_touchSequenceDocument && (!m_touchSequenceDocument->frame() || |
441 !m_touchSequenceDocument->frame()->view())) { | 432 !m_touchSequenceDocument->frame()->view())) { |
442 // If the active touch document has no frame or view, it's probably being | 433 // If the active touch document has no frame or view, it's probably being |
443 // destroyed so we can't dispatch events. | 434 // destroyed so we can't dispatch events. |
444 return false; | 435 return false; |
445 } | 436 } |
446 | 437 |
447 updateTargetAndRegionMapsForTouchStarts(touchInfos); | 438 updateTargetAndRegionMapsForTouchStarts(touchInfos); |
448 | 439 |
449 m_touchPressed = !allTouchesReleased; | 440 m_touchPressed = !allTouchesReleased; |
450 | 441 |
451 // If there's no document receiving touch events, or no handlers on the | 442 // If there's no document receiving touch events, or no handlers on the |
452 // document set to receive the events, then we can skip all the rest of | 443 // document set to receive the events, then we can skip all the rest of |
453 // this work. | 444 // this work. |
454 if (!m_touchSequenceDocument || !m_touchSequenceDocument->frameHost() || | 445 if (!m_touchSequenceDocument || !m_touchSequenceDocument->frameHost() || |
455 !hasTouchHandlers( | 446 !hasTouchHandlers( |
456 m_touchSequenceDocument->frameHost()->eventHandlerRegistry()) || | 447 m_touchSequenceDocument->frameHost()->eventHandlerRegistry()) || |
457 !m_touchSequenceDocument->frame()) { | 448 !m_touchSequenceDocument->frame()) { |
458 if (allTouchesReleased) { | 449 if (allTouchesReleased) { |
459 m_touchSequenceDocument.clear(); | 450 m_touchSequenceDocument.clear(); |
460 m_touchSequenceUserGestureToken.clear(); | |
461 } | 451 } |
462 return false; | 452 return false; |
463 } | 453 } |
464 | 454 |
465 setAllPropertiesOfTouchInfos(touchInfos); | 455 setAllPropertiesOfTouchInfos(touchInfos); |
466 | 456 |
467 return true; | 457 return true; |
468 } | 458 } |
469 | 459 |
470 // TODO(rbyers): Replace with AutoReset as base/WTF unification permits. | 460 // TODO(rbyers): Replace with AutoReset as base/WTF unification permits. |
(...skipping 22 matching lines...) Expand all Loading... |
493 if (!reHitTestTouchPointsIfNeeded(event, touchInfos)) | 483 if (!reHitTestTouchPointsIfNeeded(event, touchInfos)) |
494 return WebInputEventResult::NotHandled; | 484 return WebInputEventResult::NotHandled; |
495 | 485 |
496 bool allTouchesReleased = true; | 486 bool allTouchesReleased = true; |
497 for (const auto& point : event.touchPoints()) { | 487 for (const auto& point : event.touchPoints()) { |
498 if (point.state() != PlatformTouchPoint::TouchReleased && | 488 if (point.state() != PlatformTouchPoint::TouchReleased && |
499 point.state() != PlatformTouchPoint::TouchCancelled) | 489 point.state() != PlatformTouchPoint::TouchCancelled) |
500 allTouchesReleased = false; | 490 allTouchesReleased = false; |
501 } | 491 } |
502 | 492 |
503 // Whether a touch should be considered a "user gesture" or not is a tricky | |
504 // question. | |
505 // https://docs.google.com/document/d/1oF1T3O7_E4t1PYHV6gyCwHxOi3ystm0eSL5xZu7
nvOg/edit# | |
506 | |
507 // The touchend corresponding to a tap is always a user gesture. | |
508 bool isTap = | |
509 event.touchPoints().size() == 1 && | |
510 event.touchPoints()[0].state() == PlatformTouchPoint::TouchReleased && | |
511 !event.causesScrollingIfUncanceled(); | |
512 | |
513 // For now, disallow dragging as a user gesture when the events are being sent | |
514 // to a cross-origin iframe (crbug.com/582140). | |
515 bool isSameOrigin = false; | |
516 if (m_touchSequenceDocument && m_touchSequenceDocument->frame()) { | |
517 SecurityOrigin* securityOrigin = m_touchSequenceDocument->frame() | |
518 ->securityContext() | |
519 ->getSecurityOrigin(); | |
520 Frame* top = m_frame->tree().top(); | |
521 if (top && | |
522 securityOrigin->canAccess(top->securityContext()->getSecurityOrigin())) | |
523 isSameOrigin = true; | |
524 } | |
525 | |
526 std::unique_ptr<UserGestureIndicator> gestureIndicator; | |
527 if (isTap || isSameOrigin) { | |
528 gestureIndicator = wrapUnique(new UserGestureIndicator( | |
529 m_touchSequenceUserGestureToken | |
530 ? m_touchSequenceUserGestureToken.release() | |
531 : DocumentUserGestureToken::create(m_touchSequenceDocument))); | |
532 | |
533 m_touchSequenceUserGestureToken = UserGestureIndicator::currentToken(); | |
534 // These are cases we'd like to migrate to not hold a user gesture. | |
535 if (event.type() == PlatformEvent::TouchStart || | |
536 event.type() == PlatformEvent::TouchMove || | |
537 (event.type() == PlatformEvent::TouchEnd && m_touchScrollStarted)) { | |
538 // Collect metrics in userGestureUtilized(). | |
539 m_touchSequenceUserGestureToken->setUserGestureUtilizedCallback(this); | |
540 } | |
541 } | |
542 | |
543 return dispatchTouchEvents(event, touchInfos, allTouchesReleased); | 493 return dispatchTouchEvents(event, touchInfos, allTouchesReleased); |
544 } | 494 } |
545 | 495 |
546 bool TouchEventManager::isAnyTouchActive() const { | 496 bool TouchEventManager::isAnyTouchActive() const { |
547 return m_touchPressed; | 497 return m_touchPressed; |
548 } | 498 } |
549 | 499 |
550 void TouchEventManager::userGestureUtilized() { | |
551 // This is invoked for UserGestureIndicators created in | |
552 // TouchEventManger::handleTouchEvent which perhaps represent touch actions | |
553 // which shouldn't be considered a user-gesture. Trigger a UseCounter based | |
554 // on the touch event that's currently being dispatched. | |
555 UseCounter::Feature feature; | |
556 | |
557 switch (m_currentEvent) { | |
558 case PlatformEvent::TouchStart: | |
559 feature = UseCounter::TouchStartUserGestureUtilized; | |
560 break; | |
561 case PlatformEvent::TouchMove: | |
562 feature = UseCounter::TouchMoveUserGestureUtilized; | |
563 break; | |
564 case PlatformEvent::TouchEnd: | |
565 feature = UseCounter::TouchEndDuringScrollUserGestureUtilized; | |
566 break; | |
567 default: | |
568 NOTREACHED(); | |
569 return; | |
570 } | |
571 Deprecation::countDeprecation(m_frame, feature); | |
572 } | |
573 | |
574 } // namespace blink | 500 } // namespace blink |
OLD | NEW |