| 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 |