Chromium Code Reviews| 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 <memory> | 7 #include <memory> |
| 8 #include "core/dom/Document.h" | 8 #include "core/dom/Document.h" |
| 9 #include "core/events/TouchEvent.h" | 9 #include "core/events/TouchEvent.h" |
| 10 #include "core/frame/Deprecation.h" | 10 #include "core/frame/Deprecation.h" |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 110 suppressing_touchmoves_within_slop_ = false; | 110 suppressing_touchmoves_within_slop_ = false; |
| 111 current_touch_action_ = TouchAction::kTouchActionAuto; | 111 current_touch_action_ = TouchAction::kTouchActionAuto; |
| 112 } | 112 } |
| 113 | 113 |
| 114 DEFINE_TRACE(TouchEventManager) { | 114 DEFINE_TRACE(TouchEventManager) { |
| 115 visitor->Trace(frame_); | 115 visitor->Trace(frame_); |
| 116 visitor->Trace(touch_sequence_document_); | 116 visitor->Trace(touch_sequence_document_); |
| 117 visitor->Trace(target_for_touch_id_); | 117 visitor->Trace(target_for_touch_id_); |
| 118 } | 118 } |
| 119 | 119 |
| 120 Touch* TouchEventManager::CreateDomTouch(const WebTouchPoint& point, | |
| 121 bool* known_target) { | |
| 122 Node* touch_node = nullptr; | |
| 123 String region_id; | |
| 124 *known_target = false; | |
| 125 FloatPoint content_point; | |
| 126 FloatSize adjusted_radius; | |
| 127 | |
| 128 if (point.state == WebTouchPoint::kStateReleased || | |
| 129 point.state == WebTouchPoint::kStateCancelled) { | |
| 130 // The target should be the original target for this touch, so get | |
| 131 // it from the hashmap. As it's a release or cancel we also remove | |
| 132 // it from the map. | |
| 133 touch_node = target_for_touch_id_.Take(point.id); | |
| 134 region_id = region_for_touch_id_.Take(point.id); | |
| 135 } else { | |
| 136 // No hittest is performed on move or stationary, since the target | |
| 137 // is not allowed to change anyway. | |
| 138 touch_node = target_for_touch_id_.at(point.id); | |
| 139 region_id = region_for_touch_id_.at(point.id); | |
| 140 } | |
| 141 | |
| 142 LocalFrame* target_frame = nullptr; | |
| 143 if (touch_node) { | |
| 144 Document& doc = touch_node->GetDocument(); | |
| 145 // If the target node has moved to a new document while it was being | |
| 146 // touched, we can't send events to the new document because that could | |
| 147 // leak nodes from one document to another. See http://crbug.com/394339. | |
| 148 if (&doc == touch_sequence_document_.Get()) { | |
| 149 target_frame = doc.GetFrame(); | |
| 150 *known_target = true; | |
| 151 } | |
| 152 } | |
| 153 if (!(*known_target)) { | |
| 154 // If we don't have a target registered for the point it means we've | |
| 155 // missed our opportunity to do a hit test for it (due to some | |
| 156 // optimization that prevented blink from ever seeing the | |
| 157 // touchstart), or that the touch started outside the active touch | |
| 158 // sequence document. We should still include the touch in the | |
| 159 // Touches list reported to the application (eg. so it can | |
| 160 // differentiate between a one and two finger gesture), but we won't | |
| 161 // actually dispatch any events for it. Set the target to the | |
| 162 // Document so that there's some valid node here. Perhaps this | |
| 163 // should really be LocalDOMWindow, but in all other cases the target of | |
| 164 // a Touch is a Node so using the window could be a breaking change. | |
| 165 // Since we know there was no handler invoked, the specific target | |
| 166 // should be completely irrelevant to the application. | |
| 167 touch_node = touch_sequence_document_; | |
| 168 target_frame = touch_sequence_document_->GetFrame(); | |
| 169 } | |
| 170 DCHECK(target_frame); | |
| 171 | |
| 172 // pagePoint should always be in the target element's document coordinates. | |
| 173 FloatPoint page_point = | |
| 174 target_frame->View()->RootFrameToContents(point.position); | |
| 175 float scale_factor = 1.0f / target_frame->PageZoomFactor(); | |
| 176 | |
| 177 content_point = page_point.ScaledBy(scale_factor); | |
| 178 adjusted_radius = | |
| 179 FloatSize(point.radius_x, point.radius_y).ScaledBy(scale_factor); | |
| 180 | |
| 181 return Touch::Create(target_frame, touch_node, point.id, | |
| 182 point.screen_position, content_point, adjusted_radius, | |
| 183 point.rotation_angle, point.force, region_id); | |
| 184 } | |
| 185 | |
| 120 WebInputEventResult TouchEventManager::DispatchTouchEvents( | 186 WebInputEventResult TouchEventManager::DispatchTouchEvents( |
| 121 const WebTouchEvent& event, | 187 const WebTouchEvent& event, |
| 122 const Vector<WebTouchEvent>& coalesced_events, | 188 const Vector<WebTouchEvent>& coalesced_events, |
| 123 const HeapVector<TouchInfo>& touch_infos, | |
| 124 bool all_touches_released) { | 189 bool all_touches_released) { |
| 125 // Build up the lists to use for the |touches|, |targetTouches| and | 190 // Build up the lists to use for the |touches|, |targetTouches| and |
| 126 // |changedTouches| attributes in the JS event. See | 191 // |changedTouches| attributes in the JS event. See |
| 127 // http://www.w3.org/TR/touch-events/#touchevent-interface for how these | 192 // http://www.w3.org/TR/touch-events/#touchevent-interface for how these |
| 128 // lists fit together. | 193 // lists fit together. |
| 129 | 194 |
| 130 if (event.GetType() == WebInputEvent::kTouchEnd || | 195 if (event.GetType() == WebInputEvent::kTouchEnd || |
| 131 event.GetType() == WebInputEvent::kTouchCancel || | 196 event.GetType() == WebInputEvent::kTouchCancel || |
| 132 event.touches_length > 1) { | 197 event.touches_length > 1) { |
| 133 suppressing_touchmoves_within_slop_ = false; | 198 suppressing_touchmoves_within_slop_ = false; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 144 TouchList* touches = TouchList::Create(); | 209 TouchList* touches = TouchList::Create(); |
| 145 | 210 |
| 146 // A different view on the 'touches' list above, filtered and grouped by | 211 // A different view on the 'touches' list above, filtered and grouped by |
| 147 // event target. Used for the |targetTouches| list in the JS event. | 212 // event target. Used for the |targetTouches| list in the JS event. |
| 148 using TargetTouchesHeapMap = HeapHashMap<EventTarget*, Member<TouchList>>; | 213 using TargetTouchesHeapMap = HeapHashMap<EventTarget*, Member<TouchList>>; |
| 149 TargetTouchesHeapMap touches_by_target; | 214 TargetTouchesHeapMap touches_by_target; |
| 150 | 215 |
| 151 // Array of touches per state, used to assemble the |changedTouches| list. | 216 // Array of touches per state, used to assemble the |changedTouches| list. |
| 152 ChangedTouches changed_touches[WebTouchPoint::kStateMax + 1]; | 217 ChangedTouches changed_touches[WebTouchPoint::kStateMax + 1]; |
| 153 | 218 |
| 154 for (auto touch_info : touch_infos) { | 219 for (unsigned touch_point_idx = 0; touch_point_idx < event.touches_length; |
| 155 const WebTouchPoint& point = touch_info.point; | 220 touch_point_idx++) { |
| 221 const WebTouchPoint& point = event.TouchPointInRootFrame(touch_point_idx); | |
| 156 WebTouchPoint::State point_state = point.state; | 222 WebTouchPoint::State point_state = point.state; |
| 223 bool known_target; | |
| 157 | 224 |
| 158 Touch* touch = Touch::Create( | 225 Touch* touch = CreateDomTouch(point, &known_target); |
| 159 touch_info.target_frame.Get(), touch_info.touch_node.Get(), point.id, | 226 EventTarget* touch_target = touch->target(); |
| 160 point.screen_position, touch_info.content_point, | |
| 161 touch_info.adjusted_radius, point.rotation_angle, point.force, | |
| 162 touch_info.region); | |
| 163 | 227 |
| 164 // Ensure this target's touch list exists, even if it ends up empty, so | 228 // Ensure this target's touch list exists, even if it ends up empty, so |
| 165 // it can always be passed to TouchEvent::Create below. | 229 // it can always be passed to TouchEvent::Create below. |
| 166 TargetTouchesHeapMap::iterator target_touches_iterator = | 230 TargetTouchesHeapMap::iterator target_touches_iterator = |
| 167 touches_by_target.find(touch_info.touch_node.Get()); | 231 touches_by_target.find(touch_target); |
| 168 if (target_touches_iterator == touches_by_target.end()) { | 232 if (target_touches_iterator == touches_by_target.end()) { |
| 169 touches_by_target.Set(touch_info.touch_node.Get(), TouchList::Create()); | 233 touches_by_target.Set(touch_target, TouchList::Create()); |
| 170 target_touches_iterator = | 234 target_touches_iterator = touches_by_target.find(touch_target); |
| 171 touches_by_target.find(touch_info.touch_node.Get()); | |
| 172 } | 235 } |
| 173 | 236 |
| 174 // |touches| and |targetTouches| should only contain information about | 237 // |touches| and |targetTouches| should only contain information about |
| 175 // touches still on the screen, so if this point is released or | 238 // touches still on the screen, so if this point is released or |
| 176 // cancelled it will only appear in the |changedTouches| list. | 239 // cancelled it will only appear in the |changedTouches| list. |
| 177 if (point_state != WebTouchPoint::kStateReleased && | 240 if (point_state != WebTouchPoint::kStateReleased && |
| 178 point_state != WebTouchPoint::kStateCancelled) { | 241 point_state != WebTouchPoint::kStateCancelled) { |
| 179 touches->Append(touch); | 242 touches->Append(touch); |
| 180 target_touches_iterator->value->Append(touch); | 243 target_touches_iterator->value->Append(touch); |
| 181 } | 244 } |
| 182 | 245 |
| 183 // Now build up the correct list for |changedTouches|. | 246 // Now build up the correct list for |changedTouches|. |
| 184 // Note that any touches that are in the TouchStationary state (e.g. if | 247 // Note that any touches that are in the TouchStationary state (e.g. if |
| 185 // the user had several points touched but did not move them all) should | 248 // the user had several points touched but did not move them all) should |
| 186 // never be in the |changedTouches| list so we do not handle them | 249 // never be in the |changedTouches| list so we do not handle them |
| 187 // explicitly here. See https://bugs.webkit.org/show_bug.cgi?id=37609 | 250 // explicitly here. See https://bugs.webkit.org/show_bug.cgi?id=37609 |
| 188 // for further discussion about the TouchStationary state. | 251 // for further discussion about the TouchStationary state. |
| 189 if (point_state != WebTouchPoint::kStateStationary && | 252 if (point_state != WebTouchPoint::kStateStationary && known_target) { |
| 190 touch_info.known_target) { | |
| 191 DCHECK_LE(point_state, WebTouchPoint::kStateMax); | 253 DCHECK_LE(point_state, WebTouchPoint::kStateMax); |
| 192 if (!changed_touches[point_state].touches_) | 254 if (!changed_touches[point_state].touches_) |
| 193 changed_touches[point_state].touches_ = TouchList::Create(); | 255 changed_touches[point_state].touches_ = TouchList::Create(); |
| 194 changed_touches[point_state].touches_->Append(touch); | 256 changed_touches[point_state].touches_->Append(touch); |
| 195 changed_touches[point_state].targets_.insert(touch_info.touch_node); | 257 changed_touches[point_state].targets_.insert(touch_target); |
| 196 changed_touches[point_state].pointer_type_ = point.pointer_type; | 258 changed_touches[point_state].pointer_type_ = point.pointer_type; |
| 197 } | 259 } |
| 198 } | 260 } |
| 199 | 261 |
| 200 if (all_touches_released) { | 262 if (all_touches_released) { |
| 201 touch_sequence_document_.Clear(); | 263 touch_sequence_document_.Clear(); |
| 202 current_touch_action_ = TouchAction::kTouchActionAuto; | 264 current_touch_action_ = TouchAction::kTouchActionAuto; |
| 203 } | 265 } |
| 204 | 266 |
| 205 WebInputEventResult event_result = WebInputEventResult::kNotHandled; | 267 WebInputEventResult event_result = WebInputEventResult::kNotHandled; |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 225 coalesced_event, touches, touches_by_target.at(touch_event_target), | 287 coalesced_event, touches, touches_by_target.at(touch_event_target), |
| 226 changed_touches[state].touches_.Get(), event_name, | 288 changed_touches[state].touches_.Get(), event_name, |
| 227 touch_event_target->ToNode()->GetDocument().domWindow(), | 289 touch_event_target->ToNode()->GetDocument().domWindow(), |
| 228 current_touch_action_); | 290 current_touch_action_); |
| 229 | 291 |
| 230 DispatchEventResult dom_dispatch_result = | 292 DispatchEventResult dom_dispatch_result = |
| 231 touch_event_target->DispatchEvent(touch_event); | 293 touch_event_target->DispatchEvent(touch_event); |
| 232 | 294 |
| 233 // Only report for top level documents with a single touch on | 295 // Only report for top level documents with a single touch on |
| 234 // touch-start or the first touch-move. | 296 // touch-start or the first touch-move. |
| 235 if (event.touch_start_or_first_touch_move && touch_infos.size() == 1 && | 297 if (event.touch_start_or_first_touch_move && event.touches_length == 1 && |
| 236 frame_->IsMainFrame()) { | 298 frame_->IsMainFrame()) { |
| 237 // Record the disposition and latency of touch starts and first touch | 299 // Record the disposition and latency of touch starts and first touch |
| 238 // moves before and after the page is fully loaded respectively. | 300 // moves before and after the page is fully loaded respectively. |
| 239 int64_t latency_in_micros = | 301 int64_t latency_in_micros = |
| 240 (TimeTicks::Now() - | 302 (TimeTicks::Now() - |
| 241 TimeTicks::FromSeconds(event.TimeStampSeconds())) | 303 TimeTicks::FromSeconds(event.TimeStampSeconds())) |
| 242 .InMicroseconds(); | 304 .InMicroseconds(); |
| 243 if (event.IsCancelable()) { | 305 if (event.IsCancelable()) { |
| 244 if (frame_->GetDocument()->IsLoadCompleted()) { | 306 if (frame_->GetDocument()->IsLoadCompleted()) { |
| 245 DEFINE_STATIC_LOCAL(EnumerationHistogram, | 307 DEFINE_STATIC_LOCAL(EnumerationHistogram, |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 302 | 364 |
| 303 // Do not suppress any touchmoves if the touchstart is consumed. | 365 // Do not suppress any touchmoves if the touchstart is consumed. |
| 304 if (IsTouchSequenceStart(event) && | 366 if (IsTouchSequenceStart(event) && |
| 305 event_result == WebInputEventResult::kNotHandled) { | 367 event_result == WebInputEventResult::kNotHandled) { |
| 306 suppressing_touchmoves_within_slop_ = true; | 368 suppressing_touchmoves_within_slop_ = true; |
| 307 } | 369 } |
| 308 | 370 |
| 309 return event_result; | 371 return event_result; |
| 310 } | 372 } |
| 311 | 373 |
| 312 void TouchEventManager::UpdateTargetAndRegionMapsForTouchStarts( | 374 void TouchEventManager::UpdateTargetAndRegionMapsForTouchStarts( |
|
mustaq
2017/05/31 18:22:44
Since this now handles a single touch point, you c
Navid Zolghadr
2017/06/01 17:57:51
Done.
| |
| 313 HeapVector<TouchInfo>& touch_infos) { | 375 const WebTouchPoint& touch_point, |
| 314 for (auto& touch_info : touch_infos) { | 376 const EventHandlingUtil::PointerEventTarget& pointer_event_target) { |
| 315 // Touch events implicitly capture to the touched node, and don't change | 377 // Touch events implicitly capture to the touched node, and don't change |
| 316 // active/hover states themselves (Gesture events do). So we only need | 378 // active/hover states themselves (Gesture events do). So we only need |
| 317 // to hit-test on touchstart and when the target could be different than | 379 // to hit-test on touchstart and when the target could be different than |
| 318 // the corresponding pointer event target. | 380 // the corresponding pointer event target. |
| 319 if (touch_info.point.state == WebTouchPoint::kStatePressed) { | 381 if (touch_point.state == WebTouchPoint::kStatePressed) { |
| 320 HitTestRequest::HitTestRequestType hit_type = | 382 Node* touch_node = pointer_event_target.target_node; |
| 321 HitTestRequest::kTouchEvent | HitTestRequest::kReadOnly | | 383 String region = pointer_event_target.region; |
| 322 HitTestRequest::kActive; | 384 |
| 323 HitTestResult result; | 385 HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kTouchEvent | |
| 324 // For the touchPressed points hit-testing is done in | 386 HitTestRequest::kReadOnly | |
| 325 // PointerEventManager. If it was the second touch there is a | 387 HitTestRequest::kActive; |
| 326 // capturing documents for the touch and |m_touchSequenceDocument| | 388 HitTestResult result; |
| 327 // is not null. So if PointerEventManager should hit-test again | 389 // For the touchPressed points hit-testing is done in |
| 328 // against |m_touchSequenceDocument| if the target set by | 390 // PointerEventManager. If it was the second touch there is a |
| 329 // PointerEventManager was either null or not in | 391 // capturing documents for the touch and |m_touchSequenceDocument| |
| 330 // |m_touchSequenceDocument|. | 392 // is not null. So if PointerEventManager should hit-test again |
| 331 if (touch_sequence_document_ && | 393 // against |m_touchSequenceDocument| if the target set by |
| 332 (!touch_info.touch_node || | 394 // PointerEventManager was either null or not in |
| 333 &touch_info.touch_node->GetDocument() != touch_sequence_document_)) { | 395 // |m_touchSequenceDocument|. |
| 334 if (touch_sequence_document_->GetFrame()) { | 396 if (touch_sequence_document_ && |
| 335 LayoutPoint frame_point = LayoutPoint( | 397 (!touch_node || |
| 336 touch_sequence_document_->GetFrame()->View()->RootFrameToContents( | 398 &touch_node->GetDocument() != touch_sequence_document_)) { |
| 337 touch_info.point.position)); | 399 if (touch_sequence_document_->GetFrame()) { |
| 338 result = EventHandlingUtil::HitTestResultInFrame( | 400 LayoutPoint frame_point = LayoutPoint( |
| 339 touch_sequence_document_->GetFrame(), frame_point, hit_type); | 401 touch_sequence_document_->GetFrame()->View()->RootFrameToContents( |
| 340 Node* node = result.InnerNode(); | 402 touch_point.position)); |
| 341 if (!node) | 403 result = EventHandlingUtil::HitTestResultInFrame( |
| 342 continue; | 404 touch_sequence_document_->GetFrame(), frame_point, hit_type); |
| 343 if (isHTMLCanvasElement(node)) { | 405 Node* node = result.InnerNode(); |
| 344 HitTestCanvasResult* hit_test_canvas_result = | 406 if (!node) |
| 345 toHTMLCanvasElement(node)->GetControlAndIdIfHitRegionExists( | 407 return; |
| 346 result.PointInInnerNodeFrame()); | 408 if (isHTMLCanvasElement(node)) { |
| 347 if (hit_test_canvas_result->GetControl()) | 409 HitTestCanvasResult* hit_test_canvas_result = |
| 348 node = hit_test_canvas_result->GetControl(); | 410 toHTMLCanvasElement(node)->GetControlAndIdIfHitRegionExists( |
| 349 touch_info.region = hit_test_canvas_result->GetId(); | 411 result.PointInInnerNodeFrame()); |
| 350 } | 412 if (hit_test_canvas_result->GetControl()) |
| 351 // Touch events should not go to text nodes. | 413 node = hit_test_canvas_result->GetControl(); |
| 352 if (node->IsTextNode()) | 414 region = hit_test_canvas_result->GetId(); |
| 353 node = FlatTreeTraversal::Parent(*node); | |
| 354 touch_info.touch_node = node; | |
| 355 } else { | |
| 356 continue; | |
| 357 } | 415 } |
| 358 } | 416 // Touch events should not go to text nodes. |
| 359 if (!touch_info.touch_node) | 417 if (node->IsTextNode()) |
| 360 continue; | 418 node = FlatTreeTraversal::Parent(*node); |
| 361 if (!touch_sequence_document_) { | 419 touch_node = node; |
| 362 // Keep track of which document should receive all touch events | 420 } else { |
| 363 // in the active sequence. This must be a single document to | 421 return; |
| 364 // ensure we don't leak Nodes between documents. | |
| 365 touch_sequence_document_ = &(touch_info.touch_node->GetDocument()); | |
| 366 DCHECK(touch_sequence_document_->GetFrame()->View()); | |
| 367 } | |
| 368 | |
| 369 // Ideally we'd DCHECK(!m_targetForTouchID.contains(point.id()) | |
| 370 // since we shouldn't get a touchstart for a touch that's already | |
| 371 // down. However EventSender allows this to be violated and there's | |
| 372 // some tests that take advantage of it. There may also be edge | |
| 373 // cases in the browser where this happens. | |
| 374 // See http://crbug.com/345372. | |
| 375 target_for_touch_id_.Set(touch_info.point.id, touch_info.touch_node); | |
| 376 | |
| 377 region_for_touch_id_.Set(touch_info.point.id, touch_info.region); | |
| 378 | |
| 379 TouchAction effective_touch_action = | |
| 380 TouchActionUtil::ComputeEffectiveTouchAction(*touch_info.touch_node); | |
| 381 if (effective_touch_action != TouchAction::kTouchActionAuto) { | |
| 382 frame_->GetPage()->GetChromeClient().SetTouchAction( | |
| 383 frame_, effective_touch_action); | |
| 384 | |
| 385 // Combine the current touch action sequence with the touch action | |
| 386 // for the current finger press. | |
| 387 current_touch_action_ &= effective_touch_action; | |
| 388 } | 422 } |
| 389 } | 423 } |
| 390 } | 424 if (!touch_node) |
| 391 } | 425 return; |
| 392 | 426 if (!touch_sequence_document_) { |
| 393 void TouchEventManager::SetAllPropertiesOfTouchInfos( | 427 // Keep track of which document should receive all touch events |
| 394 HeapVector<TouchInfo>& touch_infos) { | 428 // in the active sequence. This must be a single document to |
| 395 for (auto& touch_info : touch_infos) { | 429 // ensure we don't leak Nodes between documents. |
| 396 WebTouchPoint::State point_state = touch_info.point.state; | 430 touch_sequence_document_ = &(touch_node->GetDocument()); |
| 397 Node* touch_node = nullptr; | 431 DCHECK(touch_sequence_document_->GetFrame()->View()); |
| 398 String region_id; | |
| 399 | |
| 400 if (point_state == WebTouchPoint::kStateReleased || | |
| 401 point_state == WebTouchPoint::kStateCancelled) { | |
| 402 // The target should be the original target for this touch, so get | |
| 403 // it from the hashmap. As it's a release or cancel we also remove | |
| 404 // it from the map. | |
| 405 touch_node = target_for_touch_id_.Take(touch_info.point.id); | |
| 406 region_id = region_for_touch_id_.Take(touch_info.point.id); | |
| 407 } else { | |
| 408 // No hittest is performed on move or stationary, since the target | |
| 409 // is not allowed to change anyway. | |
| 410 touch_node = target_for_touch_id_.at(touch_info.point.id); | |
| 411 region_id = region_for_touch_id_.at(touch_info.point.id); | |
| 412 } | 432 } |
| 413 | 433 |
| 414 LocalFrame* target_frame = nullptr; | 434 // Ideally we'd DCHECK(!m_targetForTouchID.contains(point.id()) |
| 415 bool known_target = false; | 435 // since we shouldn't get a touchstart for a touch that's already |
| 416 if (touch_node) { | 436 // down. However EventSender allows this to be violated and there's |
| 417 Document& doc = touch_node->GetDocument(); | 437 // some tests that take advantage of it. There may also be edge |
| 418 // If the target node has moved to a new document while it was being | 438 // cases in the browser where this happens. |
| 419 // touched, we can't send events to the new document because that could | 439 // See http://crbug.com/345372. |
| 420 // leak nodes from one document to another. See http://crbug.com/394339. | 440 target_for_touch_id_.Set(touch_point.id, touch_node); |
| 421 if (&doc == touch_sequence_document_.Get()) { | 441 |
| 422 target_frame = doc.GetFrame(); | 442 region_for_touch_id_.Set(touch_point.id, region); |
| 423 known_target = true; | 443 |
| 424 } | 444 TouchAction effective_touch_action = |
| 445 TouchActionUtil::ComputeEffectiveTouchAction(*touch_node); | |
| 446 if (effective_touch_action != TouchAction::kTouchActionAuto) { | |
| 447 frame_->GetPage()->GetChromeClient().SetTouchAction( | |
| 448 frame_, effective_touch_action); | |
| 449 | |
| 450 // Combine the current touch action sequence with the touch action | |
| 451 // for the current finger press. | |
| 452 current_touch_action_ &= effective_touch_action; | |
| 425 } | 453 } |
| 426 if (!known_target) { | |
| 427 // If we don't have a target registered for the point it means we've | |
| 428 // missed our opportunity to do a hit test for it (due to some | |
| 429 // optimization that prevented blink from ever seeing the | |
| 430 // touchstart), or that the touch started outside the active touch | |
| 431 // sequence document. We should still include the touch in the | |
| 432 // Touches list reported to the application (eg. so it can | |
| 433 // differentiate between a one and two finger gesture), but we won't | |
| 434 // actually dispatch any events for it. Set the target to the | |
| 435 // Document so that there's some valid node here. Perhaps this | |
| 436 // should really be LocalDOMWindow, but in all other cases the target of | |
| 437 // a Touch is a Node so using the window could be a breaking change. | |
| 438 // Since we know there was no handler invoked, the specific target | |
| 439 // should be completely irrelevant to the application. | |
| 440 touch_node = touch_sequence_document_; | |
| 441 target_frame = touch_sequence_document_->GetFrame(); | |
| 442 } | |
| 443 DCHECK(target_frame); | |
| 444 | |
| 445 // pagePoint should always be in the target element's document coordinates. | |
| 446 FloatPoint page_point = | |
| 447 target_frame->View()->RootFrameToContents(touch_info.point.position); | |
| 448 float scale_factor = 1.0f / target_frame->PageZoomFactor(); | |
| 449 | |
| 450 touch_info.touch_node = touch_node; | |
| 451 touch_info.target_frame = target_frame; | |
| 452 touch_info.content_point = page_point.ScaledBy(scale_factor); | |
| 453 touch_info.adjusted_radius = | |
| 454 FloatSize(touch_info.point.radius_x, touch_info.point.radius_y) | |
| 455 .ScaledBy(scale_factor); | |
| 456 touch_info.known_target = known_target; | |
| 457 touch_info.region = region_id; | |
| 458 } | 454 } |
| 459 } | 455 } |
| 460 | 456 |
| 461 bool TouchEventManager::ReHitTestTouchPointsIfNeeded( | 457 bool TouchEventManager::ReHitTestTouchPointsIfNeeded( |
| 462 const WebTouchEvent& event, | 458 const WebTouchEvent& event, |
| 463 HeapVector<TouchInfo>& touch_infos) { | 459 const HeapVector<EventHandlingUtil::PointerEventTarget>& |
| 460 pointer_event_targets) { | |
| 464 bool new_touch_sequence = true; | 461 bool new_touch_sequence = true; |
| 465 bool all_touches_released = true; | 462 bool all_touches_released = true; |
| 466 | 463 |
| 467 for (unsigned i = 0; i < event.touches_length; ++i) { | 464 for (unsigned i = 0; i < event.touches_length; ++i) { |
| 468 WebTouchPoint::State state = event.touches[i].state; | 465 WebTouchPoint::State state = event.touches[i].state; |
| 469 if (state != WebTouchPoint::kStatePressed) | 466 if (state != WebTouchPoint::kStatePressed) |
| 470 new_touch_sequence = false; | 467 new_touch_sequence = false; |
| 471 if (state != WebTouchPoint::kStateReleased && | 468 if (state != WebTouchPoint::kStateReleased && |
| 472 state != WebTouchPoint::kStateCancelled) | 469 state != WebTouchPoint::kStateCancelled) |
| 473 all_touches_released = false; | 470 all_touches_released = false; |
| 474 } | 471 } |
| 475 if (new_touch_sequence) { | 472 if (new_touch_sequence) { |
| 476 // Ideally we'd DCHECK(!m_touchSequenceDocument) here since we should | 473 // Ideally we'd DCHECK(!m_touchSequenceDocument) here since we should |
| 477 // have cleared the active document when we saw the last release. But we | 474 // have cleared the active document when we saw the last release. But we |
| 478 // have some tests that violate this, ClusterFuzz could trigger it, and | 475 // have some tests that violate this, ClusterFuzz could trigger it, and |
| 479 // there may be cases where the browser doesn't reliably release all | 476 // there may be cases where the browser doesn't reliably release all |
| 480 // touches. http://crbug.com/345372 tracks this. | 477 // touches. http://crbug.com/345372 tracks this. |
| 481 touch_sequence_document_.Clear(); | 478 touch_sequence_document_.Clear(); |
| 482 } | 479 } |
| 483 | 480 |
| 484 DCHECK(frame_->View()); | 481 DCHECK(frame_->View()); |
| 485 if (touch_sequence_document_ && | 482 if (touch_sequence_document_ && |
| 486 (!touch_sequence_document_->GetFrame() || | 483 (!touch_sequence_document_->GetFrame() || |
| 487 !touch_sequence_document_->GetFrame()->View())) { | 484 !touch_sequence_document_->GetFrame()->View())) { |
| 488 // If the active touch document has no frame or view, it's probably being | 485 // If the active touch document has no frame or view, it's probably being |
| 489 // destroyed so we can't dispatch events. | 486 // destroyed so we can't dispatch events. |
| 490 return false; | 487 return false; |
| 491 } | 488 } |
| 492 | 489 |
| 493 UpdateTargetAndRegionMapsForTouchStarts(touch_infos); | 490 for (unsigned i = 0; i < event.touches_length; ++i) { |
| 491 UpdateTargetAndRegionMapsForTouchStarts(event.TouchPointInRootFrame(i), | |
| 492 pointer_event_targets[i]); | |
| 493 } | |
| 494 | 494 |
| 495 touch_pressed_ = !all_touches_released; | 495 touch_pressed_ = !all_touches_released; |
| 496 | 496 |
| 497 // If there's no document receiving touch events, or no handlers on the | 497 // If there's no document receiving touch events, or no handlers on the |
| 498 // document set to receive the events, then we can skip all the rest of | 498 // document set to receive the events, then we can skip all the rest of |
| 499 // this work. | 499 // this work. |
| 500 if (!touch_sequence_document_ || !touch_sequence_document_->GetPage() || | 500 if (!touch_sequence_document_ || !touch_sequence_document_->GetPage() || |
| 501 !HasTouchHandlers( | 501 !HasTouchHandlers( |
| 502 touch_sequence_document_->GetPage()->GetEventHandlerRegistry()) || | 502 touch_sequence_document_->GetPage()->GetEventHandlerRegistry()) || |
| 503 !touch_sequence_document_->GetFrame()) { | 503 !touch_sequence_document_->GetFrame()) { |
| 504 if (all_touches_released) { | 504 if (all_touches_released) { |
| 505 touch_sequence_document_.Clear(); | 505 touch_sequence_document_.Clear(); |
| 506 } | 506 } |
| 507 return false; | 507 return false; |
| 508 } | 508 } |
| 509 | 509 |
| 510 SetAllPropertiesOfTouchInfos(touch_infos); | |
| 511 | |
| 512 return true; | 510 return true; |
| 513 } | 511 } |
| 514 | 512 |
| 515 WebInputEventResult TouchEventManager::HandleTouchEvent( | 513 WebInputEventResult TouchEventManager::HandleTouchEvent( |
| 516 const WebTouchEvent& event, | 514 const WebTouchEvent& event, |
| 517 const Vector<WebTouchEvent>& coalesced_events, | 515 const Vector<WebTouchEvent>& coalesced_events, |
| 518 HeapVector<TouchInfo>& touch_infos) { | 516 const HeapVector<EventHandlingUtil::PointerEventTarget>& |
| 519 if (!ReHitTestTouchPointsIfNeeded(event, touch_infos)) | 517 pointer_event_targets) { |
| 518 DCHECK(event.touches_length == pointer_event_targets.size()); | |
| 519 | |
| 520 if (!ReHitTestTouchPointsIfNeeded(event, pointer_event_targets)) | |
| 520 return WebInputEventResult::kNotHandled; | 521 return WebInputEventResult::kNotHandled; |
| 521 | 522 |
| 522 bool all_touches_released = true; | 523 bool all_touches_released = true; |
| 523 for (unsigned i = 0; i < event.touches_length; ++i) { | 524 for (unsigned i = 0; i < event.touches_length; ++i) { |
| 524 WebTouchPoint::State state = event.touches[i].state; | 525 WebTouchPoint::State state = event.touches[i].state; |
| 525 if (state != WebTouchPoint::kStateReleased && | 526 if (state != WebTouchPoint::kStateReleased && |
| 526 state != WebTouchPoint::kStateCancelled) | 527 state != WebTouchPoint::kStateCancelled) |
| 527 all_touches_released = false; | 528 all_touches_released = false; |
| 528 } | 529 } |
| 529 | 530 |
| 530 return DispatchTouchEvents(event, coalesced_events, touch_infos, | 531 return DispatchTouchEvents(event, coalesced_events, all_touches_released); |
| 531 all_touches_released); | |
| 532 } | 532 } |
| 533 | 533 |
| 534 bool TouchEventManager::IsAnyTouchActive() const { | 534 bool TouchEventManager::IsAnyTouchActive() const { |
| 535 return touch_pressed_; | 535 return touch_pressed_; |
| 536 } | 536 } |
| 537 | 537 |
| 538 } // namespace blink | 538 } // namespace blink |
| OLD | NEW |