| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "core/input/TouchEventManager.h" |
| 6 |
| 7 #include "core/dom/Document.h" |
| 8 #include "core/events/TouchEvent.h" |
| 9 #include "core/frame/EventHandlerRegistry.h" |
| 10 #include "core/frame/FrameHost.h" |
| 11 #include "core/frame/FrameView.h" |
| 12 #include "core/html/HTMLCanvasElement.h" |
| 13 #include "core/input/EventHandler.h" |
| 14 #include "core/input/TouchActionUtil.h" |
| 15 #include "core/page/ChromeClient.h" |
| 16 #include "core/page/Page.h" |
| 17 #include "platform/Histogram.h" |
| 18 #include "platform/PlatformTouchEvent.h" |
| 19 |
| 20 |
| 21 |
| 22 namespace blink { |
| 23 |
| 24 namespace { |
| 25 |
| 26 bool hasTouchHandlers(const EventHandlerRegistry& registry) |
| 27 { |
| 28 return registry.hasEventHandlers(EventHandlerRegistry::TouchStartOrMoveEvent
Blocking) |
| 29 || registry.hasEventHandlers(EventHandlerRegistry::TouchStartOrMoveEvent
Passive) |
| 30 || registry.hasEventHandlers(EventHandlerRegistry::TouchEndOrCancelEvent
Blocking) |
| 31 || registry.hasEventHandlers(EventHandlerRegistry::TouchEndOrCancelEvent
Passive); |
| 32 } |
| 33 |
| 34 const AtomicString& touchEventNameForTouchPointState(PlatformTouchPoint::TouchSt
ate state) |
| 35 { |
| 36 switch (state) { |
| 37 case PlatformTouchPoint::TouchReleased: |
| 38 return EventTypeNames::touchend; |
| 39 case PlatformTouchPoint::TouchCancelled: |
| 40 return EventTypeNames::touchcancel; |
| 41 case PlatformTouchPoint::TouchPressed: |
| 42 return EventTypeNames::touchstart; |
| 43 case PlatformTouchPoint::TouchMoved: |
| 44 return EventTypeNames::touchmove; |
| 45 case PlatformTouchPoint::TouchStationary: |
| 46 // Fall through to default |
| 47 default: |
| 48 ASSERT_NOT_REACHED(); |
| 49 return emptyAtom; |
| 50 } |
| 51 } |
| 52 |
| 53 // These offsets change indicies into the ListenerHistogram |
| 54 // enumeration. The addition of a series of offsets then |
| 55 // produces the resulting ListenerHistogram value. |
| 56 const size_t kTouchTargetHistogramRootScrollerOffset = 4; |
| 57 const size_t kTouchTargetHistogramScrollableDocumentOffset = 2; |
| 58 const size_t kTouchTargetHistogramHandledOffset = 1; |
| 59 |
| 60 enum TouchTargetAndDispatchResultType { |
| 61 NonRootScrollerNonScrollableNotHandled, // Non-root-scroller, non-scrollable
document, not handled. |
| 62 NonRootScrollerNonScrollableHandled, // Non-root-scroller, non-scrollable do
cument, handled application. |
| 63 NonRootScrollerScrollableDocumentNotHandled, // Non-root-scroller, scrollabl
e document, not handled. |
| 64 NonRootScrollerScrollableDocumentHandled, // Non-root-scroller, scrollable d
ocument, handled application. |
| 65 RootScrollerNonScrollableNotHandled, // Root-scroller, non-scrollable docume
nt, not handled. |
| 66 RootScrollerNonScrollableHandled, // Root-scroller, non-scrollable document,
handled. |
| 67 RootScrollerScrollableDocumentNotHandled, // Root-scroller, scrollable docum
ent, not handled. |
| 68 RootScrollerScrollableDocumentHandled, // Root-scroller, scrollable document
, handled. |
| 69 TouchTargetAndDispatchResultTypeMax, |
| 70 }; |
| 71 |
| 72 TouchTargetAndDispatchResultType toTouchTargetHistogramValue(EventTarget* eventT
arget, DispatchEventResult dispatchResult) |
| 73 { |
| 74 int result = 0; |
| 75 Document* document = nullptr; |
| 76 |
| 77 if (const LocalDOMWindow* domWindow = eventTarget->toLocalDOMWindow()) { |
| 78 // Treat the window as a root scroller as well. |
| 79 document = domWindow->document(); |
| 80 result += kTouchTargetHistogramRootScrollerOffset; |
| 81 } else if (Node* node = eventTarget->toNode()) { |
| 82 // Report if the target node is the document or body. |
| 83 if (node->isDocumentNode() || static_cast<Node*>(node->document().docume
ntElement()) == node || static_cast<Node*>(node->document().body()) == node) { |
| 84 result += kTouchTargetHistogramRootScrollerOffset; |
| 85 } |
| 86 document = &node->document(); |
| 87 } |
| 88 |
| 89 if (document) { |
| 90 FrameView* view = document->view(); |
| 91 if (view && view->isScrollable()) |
| 92 result += kTouchTargetHistogramScrollableDocumentOffset; |
| 93 } |
| 94 |
| 95 if (dispatchResult != DispatchEventResult::NotCanceled) |
| 96 result += kTouchTargetHistogramHandledOffset; |
| 97 return static_cast<TouchTargetAndDispatchResultType>(result); |
| 98 } |
| 99 |
| 100 enum TouchEventDispatchResultType { |
| 101 UnhandledTouches, // Unhandled touch events. |
| 102 HandledTouches, // Handled touch events. |
| 103 TouchEventDispatchResultTypeMax, |
| 104 }; |
| 105 |
| 106 // Defining this class type local to dispatchTouchEvents() and annotating |
| 107 // it with STACK_ALLOCATED(), runs into MSVC(VS 2013)'s C4822 warning |
| 108 // that the local class doesn't provide a local definition for 'operator new'. |
| 109 // Which it intentionally doesn't and shouldn't. |
| 110 // |
| 111 // Work around such toolchain bugginess by lifting out the type, thereby |
| 112 // taking it out of C4822's reach. |
| 113 class ChangedTouches final { |
| 114 STACK_ALLOCATED(); |
| 115 public: |
| 116 // The touches corresponding to the particular change state this struct |
| 117 // instance represents. |
| 118 Member<TouchList> m_touches; |
| 119 |
| 120 using EventTargetSet = HeapHashSet<Member<EventTarget>>; |
| 121 // Set of targets involved in m_touches. |
| 122 EventTargetSet m_targets; |
| 123 }; |
| 124 |
| 125 } // namespace |
| 126 |
| 127 TouchEventManager::TouchEventManager(LocalFrame* frame) |
| 128 : m_frame(frame) |
| 129 { |
| 130 clear(); |
| 131 } |
| 132 |
| 133 TouchEventManager::~TouchEventManager() |
| 134 { |
| 135 } |
| 136 |
| 137 WebInputEventResult TouchEventManager::dispatchTouchEvents( |
| 138 const PlatformTouchEvent& event, |
| 139 const HeapVector<TouchInfo>& touchInfos, |
| 140 bool allTouchesReleased) |
| 141 { |
| 142 bool touchStartOrFirstTouchMove = false; |
| 143 if (event.type() == PlatformEvent::TouchStart) { |
| 144 m_waitingForFirstTouchMove = true; |
| 145 touchStartOrFirstTouchMove = true; |
| 146 } else if (event.type() == PlatformEvent::TouchMove) { |
| 147 touchStartOrFirstTouchMove = m_waitingForFirstTouchMove; |
| 148 m_waitingForFirstTouchMove = false; |
| 149 } |
| 150 |
| 151 // Build up the lists to use for the |touches|, |targetTouches| and |
| 152 // |changedTouches| attributes in the JS event. See |
| 153 // http://www.w3.org/TR/touch-events/#touchevent-interface for how these |
| 154 // lists fit together. |
| 155 |
| 156 // Holds the complete set of touches on the screen. |
| 157 TouchList* touches = TouchList::create(); |
| 158 |
| 159 // A different view on the 'touches' list above, filtered and grouped by |
| 160 // event target. Used for the |targetTouches| list in the JS event. |
| 161 using TargetTouchesHeapMap = HeapHashMap<EventTarget*, Member<TouchList>>; |
| 162 TargetTouchesHeapMap touchesByTarget; |
| 163 |
| 164 // Array of touches per state, used to assemble the |changedTouches| list. |
| 165 ChangedTouches changedTouches[PlatformTouchPoint::TouchStateEnd]; |
| 166 |
| 167 for (unsigned i = 0; i < touchInfos.size(); ++i) { |
| 168 const TouchInfo& touchInfo = touchInfos[i]; |
| 169 const PlatformTouchPoint& point = touchInfo.point; |
| 170 PlatformTouchPoint::TouchState pointState = point.state(); |
| 171 |
| 172 if (touchInfo.consumed) |
| 173 continue; |
| 174 |
| 175 Touch* touch = Touch::create( |
| 176 touchInfo.targetFrame.get(), |
| 177 touchInfo.touchNode.get(), |
| 178 point.id(), |
| 179 point.screenPos(), |
| 180 touchInfo.adjustedPagePoint, |
| 181 touchInfo.adjustedRadius, |
| 182 point.rotationAngle(), |
| 183 point.force(), |
| 184 touchInfo.region); |
| 185 |
| 186 // Ensure this target's touch list exists, even if it ends up empty, so |
| 187 // it can always be passed to TouchEvent::Create below. |
| 188 TargetTouchesHeapMap::iterator targetTouchesIterator = touchesByTarget.f
ind(touchInfo.touchNode.get()); |
| 189 if (targetTouchesIterator == touchesByTarget.end()) { |
| 190 touchesByTarget.set(touchInfo.touchNode.get(), TouchList::create()); |
| 191 targetTouchesIterator = touchesByTarget.find(touchInfo.touchNode.get
()); |
| 192 } |
| 193 |
| 194 // |touches| and |targetTouches| should only contain information about |
| 195 // touches still on the screen, so if this point is released or |
| 196 // cancelled it will only appear in the |changedTouches| list. |
| 197 if (pointState != PlatformTouchPoint::TouchReleased && pointState != Pla
tformTouchPoint::TouchCancelled) { |
| 198 touches->append(touch); |
| 199 targetTouchesIterator->value->append(touch); |
| 200 } |
| 201 |
| 202 // Now build up the correct list for |changedTouches|. |
| 203 // Note that any touches that are in the TouchStationary state (e.g. if |
| 204 // the user had several points touched but did not move them all) should |
| 205 // never be in the |changedTouches| list so we do not handle them |
| 206 // explicitly here. See https://bugs.webkit.org/show_bug.cgi?id=37609 |
| 207 // for further discussion about the TouchStationary state. |
| 208 if (pointState != PlatformTouchPoint::TouchStationary && touchInfo.known
Target) { |
| 209 ASSERT(pointState < PlatformTouchPoint::TouchStateEnd); |
| 210 if (!changedTouches[pointState].m_touches) |
| 211 changedTouches[pointState].m_touches = TouchList::create(); |
| 212 changedTouches[pointState].m_touches->append(touch); |
| 213 changedTouches[pointState].m_targets.add(touchInfo.touchNode); |
| 214 } |
| 215 } |
| 216 |
| 217 if (allTouchesReleased) { |
| 218 m_touchSequenceDocument.clear(); |
| 219 m_touchSequenceUserGestureToken.clear(); |
| 220 } |
| 221 |
| 222 WebInputEventResult eventResult = WebInputEventResult::NotHandled; |
| 223 |
| 224 // Now iterate through the |changedTouches| list and |m_targets| within it, |
| 225 // sending TouchEvents to the targets as required. |
| 226 for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; ++state
) { |
| 227 if (!changedTouches[state].m_touches) |
| 228 continue; |
| 229 |
| 230 const AtomicString& eventName(touchEventNameForTouchPointState(static_ca
st<PlatformTouchPoint::TouchState>(state))); |
| 231 for (const auto& eventTarget : changedTouches[state].m_targets) { |
| 232 EventTarget* touchEventTarget = eventTarget; |
| 233 TouchEvent* touchEvent = TouchEvent::create( |
| 234 touches, touchesByTarget.get(touchEventTarget), changedTouches[s
tate].m_touches.get(), |
| 235 eventName, touchEventTarget->toNode()->document().domWindow(), |
| 236 event.getModifiers(), event.cancelable(), event.causesScrollingI
fUncanceled(), event.timestamp()); |
| 237 |
| 238 DispatchEventResult domDispatchResult = touchEventTarget->dispatchEv
ent(touchEvent); |
| 239 |
| 240 // Only report for top level documents with a single touch on |
| 241 // touch-start or the first touch-move. |
| 242 if (touchStartOrFirstTouchMove && touchInfos.size() == 1 && event.ca
ncelable() && !m_frame->document()->ownerElement()) { |
| 243 DEFINE_STATIC_LOCAL(EnumerationHistogram, rootDocumentListenerHi
stogram, ("Event.Touch.TargetAndDispatchResult", TouchTargetAndDispatchResultTyp
eMax)); |
| 244 rootDocumentListenerHistogram.count(toTouchTargetHistogramValue(
eventTarget, domDispatchResult)); |
| 245 |
| 246 // Count the handled touch starts and first touch moves before a
nd after the page is fully loaded respectively. |
| 247 if (m_frame->document()->isLoadCompleted()) { |
| 248 DEFINE_STATIC_LOCAL(EnumerationHistogram, touchDispositionsA
fterPageLoadHistogram, ("Event.Touch.TouchDispositionsAfterPageLoad", TouchEvent
DispatchResultTypeMax)); |
| 249 touchDispositionsAfterPageLoadHistogram.count((domDispatchRe
sult != DispatchEventResult::NotCanceled) ? HandledTouches : UnhandledTouches); |
| 250 } else { |
| 251 DEFINE_STATIC_LOCAL(EnumerationHistogram, touchDispositionsB
eforePageLoadHistogram, ("Event.Touch.TouchDispositionsBeforePageLoad", TouchEve
ntDispatchResultTypeMax)); |
| 252 touchDispositionsBeforePageLoadHistogram.count((domDispatchR
esult != DispatchEventResult::NotCanceled) ? HandledTouches : UnhandledTouches); |
| 253 } |
| 254 } |
| 255 eventResult = EventHandler::mergeEventResult(eventResult, |
| 256 EventHandler::toWebInputEventResult(domDispatchResult)); |
| 257 } |
| 258 } |
| 259 return eventResult; |
| 260 } |
| 261 |
| 262 void TouchEventManager::updateTargetAndRegionMapsForTouchStarts( |
| 263 HeapVector<TouchInfo>& touchInfos) |
| 264 { |
| 265 for (auto& touchInfo : touchInfos) { |
| 266 // Touch events implicitly capture to the touched node, and don't change |
| 267 // active/hover states themselves (Gesture events do). So we only need |
| 268 // to hit-test on touchstart and when the target could be different than |
| 269 // the corresponding pointer event target. |
| 270 if (touchInfo.point.state() == PlatformTouchPoint::TouchPressed) { |
| 271 HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEv
ent | HitTestRequest::ReadOnly | HitTestRequest::Active; |
| 272 LayoutPoint pagePoint = roundedLayoutPoint(m_frame->view()->rootFram
eToContents(touchInfo.point.pos())); |
| 273 HitTestResult result; |
| 274 if (!m_touchSequenceDocument) { |
| 275 result = m_frame->eventHandler().hitTestResultAtPoint(pagePoint,
hitType); |
| 276 } else if (m_touchSequenceDocument->frame()) { |
| 277 LayoutPoint framePoint = roundedLayoutPoint(m_touchSequenceDocum
ent->frame()->view()->rootFrameToContents(touchInfo.point.pos())); |
| 278 result = EventHandler::hitTestResultInFrame(m_touchSequenceDocum
ent->frame(), framePoint, hitType); |
| 279 } else { |
| 280 continue; |
| 281 } |
| 282 |
| 283 Node* node = result.innerNode(); |
| 284 if (!node) |
| 285 continue; |
| 286 if (isHTMLCanvasElement(node)) { |
| 287 std::pair<Element*, String> regionInfo = toHTMLCanvasElement(nod
e)->getControlAndIdIfHitRegionExists(result.pointInInnerNodeFrame()); |
| 288 if (regionInfo.first) |
| 289 node = regionInfo.first; |
| 290 touchInfo.region = regionInfo.second; |
| 291 } |
| 292 // Touch events should not go to text nodes. |
| 293 if (node->isTextNode()) |
| 294 node = FlatTreeTraversal::parent(*node); |
| 295 touchInfo.touchNode = node; |
| 296 |
| 297 if (!m_touchSequenceDocument) { |
| 298 // Keep track of which document should receive all touch events |
| 299 // in the active sequence. This must be a single document to |
| 300 // ensure we don't leak Nodes between documents. |
| 301 m_touchSequenceDocument = &(touchInfo.touchNode->document()); |
| 302 ASSERT(m_touchSequenceDocument->frame()->view()); |
| 303 } |
| 304 |
| 305 // Ideally we'd ASSERT(!m_targetForTouchID.contains(point.id()) |
| 306 // since we shouldn't get a touchstart for a touch that's already |
| 307 // down. However EventSender allows this to be violated and there's |
| 308 // some tests that take advantage of it. There may also be edge |
| 309 // cases in the browser where this happens. |
| 310 // See http://crbug.com/345372. |
| 311 m_targetForTouchID.set(touchInfo.point.id(), touchInfo.touchNode); |
| 312 |
| 313 m_regionForTouchID.set(touchInfo.point.id(), touchInfo.region); |
| 314 |
| 315 TouchAction effectiveTouchAction = |
| 316 TouchActionUtil::computeEffectiveTouchAction( |
| 317 *touchInfo.touchNode); |
| 318 if (effectiveTouchAction != TouchActionAuto) |
| 319 m_frame->page()->chromeClient().setTouchAction(effectiveTouchAct
ion); |
| 320 } |
| 321 } |
| 322 } |
| 323 |
| 324 void TouchEventManager::setAllPropertiesOfTouchInfos( |
| 325 HeapVector<TouchInfo>& touchInfos) |
| 326 { |
| 327 for (auto& touchInfo : touchInfos) { |
| 328 PlatformTouchPoint::TouchState pointState = touchInfo.point.state(); |
| 329 Node* touchNode = nullptr; |
| 330 String regionID; |
| 331 |
| 332 if (pointState == PlatformTouchPoint::TouchReleased |
| 333 || pointState == PlatformTouchPoint::TouchCancelled) { |
| 334 // The target should be the original target for this touch, so get |
| 335 // it from the hashmap. As it's a release or cancel we also remove |
| 336 // it from the map. |
| 337 touchNode = m_targetForTouchID.take(touchInfo.point.id()); |
| 338 regionID = m_regionForTouchID.take(touchInfo.point.id()); |
| 339 } else { |
| 340 // No hittest is performed on move or stationary, since the target |
| 341 // is not allowed to change anyway. |
| 342 touchNode = m_targetForTouchID.get(touchInfo.point.id()); |
| 343 regionID = m_regionForTouchID.get(touchInfo.point.id()); |
| 344 } |
| 345 |
| 346 LocalFrame* targetFrame = nullptr; |
| 347 bool knownTarget = false; |
| 348 if (touchNode) { |
| 349 Document& doc = touchNode->document(); |
| 350 // If the target node has moved to a new document while it was being
touched, |
| 351 // we can't send events to the new document because that could leak
nodes |
| 352 // from one document to another. See http://crbug.com/394339. |
| 353 if (&doc == m_touchSequenceDocument.get()) { |
| 354 targetFrame = doc.frame(); |
| 355 knownTarget = true; |
| 356 } |
| 357 } |
| 358 if (!knownTarget) { |
| 359 // If we don't have a target registered for the point it means we've |
| 360 // missed our opportunity to do a hit test for it (due to some |
| 361 // optimization that prevented blink from ever seeing the |
| 362 // touchstart), or that the touch started outside the active touch |
| 363 // sequence document. We should still include the touch in the |
| 364 // Touches list reported to the application (eg. so it can |
| 365 // differentiate between a one and two finger gesture), but we won't |
| 366 // actually dispatch any events for it. Set the target to the |
| 367 // Document so that there's some valid node here. Perhaps this |
| 368 // should really be LocalDOMWindow, but in all other cases the targe
t of |
| 369 // a Touch is a Node so using the window could be a breaking change. |
| 370 // Since we know there was no handler invoked, the specific target |
| 371 // should be completely irrelevant to the application. |
| 372 touchNode = m_touchSequenceDocument; |
| 373 targetFrame = m_touchSequenceDocument->frame(); |
| 374 } |
| 375 ASSERT(targetFrame); |
| 376 |
| 377 // pagePoint should always be in the target element's document coordinat
es. |
| 378 FloatPoint pagePoint = targetFrame->view()->rootFrameToContents( |
| 379 touchInfo.point.pos()); |
| 380 float scaleFactor = 1.0f / targetFrame->pageZoomFactor(); |
| 381 |
| 382 touchInfo.touchNode = touchNode; |
| 383 touchInfo.targetFrame = targetFrame; |
| 384 touchInfo.adjustedPagePoint = pagePoint.scaledBy(scaleFactor); |
| 385 touchInfo.adjustedRadius = touchInfo.point.radius().scaledBy(scaleFactor
); |
| 386 touchInfo.knownTarget = knownTarget; |
| 387 touchInfo.consumed = false; |
| 388 touchInfo.region = regionID; |
| 389 } |
| 390 } |
| 391 |
| 392 bool TouchEventManager::generateTouchInfosAfterHittest( |
| 393 const PlatformTouchEvent& event, |
| 394 HeapVector<TouchInfo>& touchInfos) |
| 395 { |
| 396 bool newTouchSequence = true; |
| 397 bool allTouchesReleased = true; |
| 398 |
| 399 for (const auto& point : event.touchPoints()) { |
| 400 if (point.state() != PlatformTouchPoint::TouchPressed) |
| 401 newTouchSequence = false; |
| 402 if (point.state() != PlatformTouchPoint::TouchReleased && point.state()
!= PlatformTouchPoint::TouchCancelled) |
| 403 allTouchesReleased = false; |
| 404 } |
| 405 if (newTouchSequence) { |
| 406 // Ideally we'd ASSERT(!m_touchSequenceDocument) here since we should |
| 407 // have cleared the active document when we saw the last release. But we |
| 408 // have some tests that violate this, ClusterFuzz could trigger it, and |
| 409 // there may be cases where the browser doesn't reliably release all |
| 410 // touches. http://crbug.com/345372 tracks this. |
| 411 m_touchSequenceDocument.clear(); |
| 412 m_touchSequenceUserGestureToken.clear(); |
| 413 } |
| 414 |
| 415 ASSERT(m_frame->view()); |
| 416 if (m_touchSequenceDocument && (!m_touchSequenceDocument->frame() || !m_touc
hSequenceDocument->frame()->view())) { |
| 417 // If the active touch document has no frame or view, it's probably bein
g destroyed |
| 418 // so we can't dispatch events. |
| 419 return false; |
| 420 } |
| 421 |
| 422 for (const auto& point : event.touchPoints()) { |
| 423 TouchEventManager::TouchInfo touchInfo; |
| 424 touchInfo.point = point; |
| 425 touchInfos.append(touchInfo); |
| 426 } |
| 427 |
| 428 updateTargetAndRegionMapsForTouchStarts(touchInfos); |
| 429 |
| 430 m_touchPressed = !allTouchesReleased; |
| 431 |
| 432 // If there's no document receiving touch events, or no handlers on the |
| 433 // document set to receive the events, then we can skip all the rest of |
| 434 // this work. |
| 435 if (!m_touchSequenceDocument || !m_touchSequenceDocument->frameHost() || !ha
sTouchHandlers(m_touchSequenceDocument->frameHost()->eventHandlerRegistry()) ||
!m_touchSequenceDocument->frame()) { |
| 436 if (allTouchesReleased) { |
| 437 m_touchSequenceDocument.clear(); |
| 438 m_touchSequenceUserGestureToken.clear(); |
| 439 } |
| 440 return false; |
| 441 } |
| 442 |
| 443 setAllPropertiesOfTouchInfos(touchInfos); |
| 444 |
| 445 return true; |
| 446 } |
| 447 |
| 448 WebInputEventResult TouchEventManager::handleTouchEvent( |
| 449 const PlatformTouchEvent& event, |
| 450 const HeapVector<TouchInfo>& touchInfos) |
| 451 { |
| 452 // Note that the disposition of any pointer events affects only the generati
on of touch |
| 453 // events. If all pointer events were handled (and hence no touch events wer
e fired), that |
| 454 // is still equivalent to the touch events going unhandled because pointer e
vent handler |
| 455 // don't block scroll gesture generation. |
| 456 |
| 457 // TODO(crbug.com/507408): If PE handlers always call preventDefault, we won
't see TEs until after |
| 458 // scrolling starts because the scrolling would suppress upcoming PEs. This
sudden "break" in TE |
| 459 // suppression can make the visible TEs inconsistent (e.g. touchmove without
a touchstart). |
| 460 |
| 461 bool allTouchesReleased = true; |
| 462 for (const auto& point : event.touchPoints()) { |
| 463 if (point.state() != PlatformTouchPoint::TouchReleased |
| 464 && point.state() != PlatformTouchPoint::TouchCancelled) |
| 465 allTouchesReleased = false; |
| 466 } |
| 467 |
| 468 // Whether a touch should be considered a "user gesture" or not is a tricky
question. |
| 469 // https://docs.google.com/document/d/1oF1T3O7_E4t1PYHV6gyCwHxOi3ystm0eSL5xZ
u7nvOg/edit# |
| 470 // TODO(rbyers): Disable user gesture in some cases but retain logging for n
ow (crbug.com/582140). |
| 471 OwnPtr<UserGestureIndicator> gestureIndicator; |
| 472 if (event.touchPoints().size() == 1 |
| 473 && event.touchPoints()[0].state() == PlatformTouchPoint::TouchReleased |
| 474 && !event.causesScrollingIfUncanceled()) { |
| 475 // This is a touchend corresponding to a tap, definitely a user gesture.
So don't supply |
| 476 // a UserGestureUtilizedCallback. |
| 477 gestureIndicator = adoptPtr(new UserGestureIndicator(DefinitelyProcessin
gUserGesture)); |
| 478 } else { |
| 479 // This is some other touch event that perhaps shouldn't be considered a
user gesture. So |
| 480 // use a UserGestureUtilizedCallback to get metrics / deprecation warnin
gs. |
| 481 if (m_touchSequenceUserGestureToken) |
| 482 gestureIndicator = adoptPtr(new UserGestureIndicator(m_touchSequence
UserGestureToken.release(), &m_touchSequenceDocument->frame()->eventHandler())); |
| 483 else |
| 484 gestureIndicator = adoptPtr(new UserGestureIndicator(DefinitelyProce
ssingUserGesture, &m_touchSequenceDocument->frame()->eventHandler())); |
| 485 m_touchSequenceUserGestureToken = UserGestureIndicator::currentToken(); |
| 486 } |
| 487 |
| 488 return dispatchTouchEvents(event, touchInfos, allTouchesReleased); |
| 489 } |
| 490 |
| 491 void TouchEventManager::clear() |
| 492 { |
| 493 m_touchSequenceDocument.clear(); |
| 494 m_touchSequenceUserGestureToken.clear(); |
| 495 m_targetForTouchID.clear(); |
| 496 m_regionForTouchID.clear(); |
| 497 m_touchPressed = false; |
| 498 m_waitingForFirstTouchMove = false; |
| 499 } |
| 500 |
| 501 bool TouchEventManager::isAnyTouchActive() const |
| 502 { |
| 503 return m_touchPressed; |
| 504 } |
| 505 |
| 506 DEFINE_TRACE(TouchEventManager) |
| 507 { |
| 508 visitor->trace(m_frame); |
| 509 visitor->trace(m_touchSequenceDocument); |
| 510 visitor->trace(m_targetForTouchID); |
| 511 } |
| 512 |
| 513 } // namespace blink |
| OLD | NEW |