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 19 matching lines...) Expand all Loading... | |
| 30 return registry.HasEventHandlers( | 30 return registry.HasEventHandlers( |
| 31 EventHandlerRegistry::kTouchStartOrMoveEventBlocking) || | 31 EventHandlerRegistry::kTouchStartOrMoveEventBlocking) || |
| 32 registry.HasEventHandlers( | 32 registry.HasEventHandlers( |
| 33 EventHandlerRegistry::kTouchStartOrMoveEventPassive) || | 33 EventHandlerRegistry::kTouchStartOrMoveEventPassive) || |
| 34 registry.HasEventHandlers( | 34 registry.HasEventHandlers( |
| 35 EventHandlerRegistry::kTouchEndOrCancelEventBlocking) || | 35 EventHandlerRegistry::kTouchEndOrCancelEventBlocking) || |
| 36 registry.HasEventHandlers( | 36 registry.HasEventHandlers( |
| 37 EventHandlerRegistry::kTouchEndOrCancelEventPassive); | 37 EventHandlerRegistry::kTouchEndOrCancelEventPassive); |
| 38 } | 38 } |
| 39 | 39 |
| 40 const AtomicString& TouchEventNameForTouchPointState( | 40 const AtomicString& TouchEventNameForPointerEventType( |
| 41 WebTouchPoint::State state) { | 41 WebInputEvent::Type type) { |
| 42 switch (state) { | 42 switch (type) { |
| 43 case WebTouchPoint::kStateReleased: | 43 case WebInputEvent::kPointerUp: |
| 44 return EventTypeNames::touchend; | 44 return EventTypeNames::touchend; |
| 45 case WebTouchPoint::kStateCancelled: | 45 case WebInputEvent::kPointerCancel: |
| 46 return EventTypeNames::touchcancel; | 46 return EventTypeNames::touchcancel; |
| 47 case WebTouchPoint::kStatePressed: | 47 case WebInputEvent::kPointerDown: |
| 48 return EventTypeNames::touchstart; | 48 return EventTypeNames::touchstart; |
| 49 case WebTouchPoint::kStateMoved: | 49 case WebInputEvent::kPointerMove: |
| 50 return EventTypeNames::touchmove; | 50 return EventTypeNames::touchmove; |
| 51 case WebTouchPoint::kStateStationary: | |
| 52 // Fall through to default | |
| 53 default: | 51 default: |
| 54 NOTREACHED(); | 52 NOTREACHED(); |
| 55 return g_empty_atom; | 53 return g_empty_atom; |
| 56 } | 54 } |
| 57 } | 55 } |
| 58 | 56 |
| 59 enum TouchEventDispatchResultType { | 57 enum TouchEventDispatchResultType { |
| 60 kUnhandledTouches, // Unhandled touch events. | 58 kUnhandledTouches, // Unhandled touch events. |
| 61 kHandledTouches, // Handled touch events. | 59 kHandledTouches, // Handled touch events. |
| 62 kTouchEventDispatchResultTypeMax, | 60 kTouchEventDispatchResultTypeMax, |
| 63 }; | 61 }; |
| 64 | 62 |
| 65 bool IsTouchSequenceStart(const WebTouchEvent& event) { | 63 WebTouchPoint::State TouchPointStateFromPointerEventType( |
| 66 if (!event.touches_length) | 64 WebInputEvent::Type type, |
| 67 return false; | 65 bool stale) { |
| 68 if (event.GetType() != WebInputEvent::kTouchStart) | 66 if (stale) |
| 69 return false; | 67 return WebTouchPoint::kStateStationary; |
| 70 for (size_t i = 0; i < event.touches_length; ++i) { | 68 switch (type) { |
| 71 if (event.touches[i].state != blink::WebTouchPoint::kStatePressed) | 69 case WebInputEvent::Type::kPointerUp: |
| 72 return false; | 70 return WebTouchPoint::kStateReleased; |
| 71 case WebInputEvent::Type::kPointerCancel: | |
| 72 return WebTouchPoint::kStateCancelled; | |
| 73 case WebInputEvent::Type::kPointerDown: | |
| 74 return WebTouchPoint::kStatePressed; | |
| 75 case WebInputEvent::Type::kPointerMove: | |
| 76 return WebTouchPoint::kStateMoved; | |
| 77 default: | |
| 78 NOTREACHED(); | |
| 79 return WebTouchPoint::kStateUndefined; | |
| 73 } | 80 } |
| 74 return true; | |
| 75 } | 81 } |
| 76 | 82 |
| 77 // Defining this class type local to dispatchTouchEvents() and annotating | 83 WebTouchPoint CreateWebTouchPointFromWebPointerEvent( |
| 84 const WebPointerEvent& web_pointer_event, | |
| 85 bool stale) { | |
| 86 WebTouchPoint web_touch_point(web_pointer_event); | |
| 87 web_touch_point.state = | |
| 88 TouchPointStateFromPointerEventType(web_pointer_event.GetType(), stale); | |
| 89 // TODO(crbug.com/731725): This mapping needs a division by 2. | |
| 90 web_touch_point.radius_x = web_pointer_event.width; | |
| 91 web_touch_point.radius_y = web_pointer_event.height; | |
| 92 web_touch_point.rotation_angle = web_pointer_event.rotation_angle; | |
| 93 return web_touch_point; | |
| 94 } | |
| 95 | |
| 96 void SetWebTouchEventAttributesFromWebPointerEvent( | |
| 97 WebTouchEvent* web_touch_event, | |
| 98 const WebPointerEvent& web_pointer_event) { | |
| 99 web_touch_event->dispatch_type = web_pointer_event.dispatch_type; | |
| 100 web_touch_event->touch_start_or_first_touch_move = | |
| 101 web_pointer_event.touch_start_or_first_touch_move; | |
| 102 web_touch_event->moved_beyond_slop_region = | |
| 103 web_pointer_event.moved_beyond_slop_region; | |
| 104 web_touch_event->SetFrameScale(web_pointer_event.FrameScale()); | |
| 105 web_touch_event->SetFrameTranslate(web_pointer_event.FrameTranslate()); | |
| 106 web_touch_event->SetTimeStampSeconds(web_pointer_event.TimeStampSeconds()); | |
| 107 web_touch_event->SetModifiers(web_pointer_event.GetModifiers()); | |
| 108 } | |
| 109 | |
| 110 // Defining this class type local to | |
| 111 // DispatchTouchEventFromAccumulatdTouchPoints() and annotating | |
| 78 // it with STACK_ALLOCATED(), runs into MSVC(VS 2013)'s C4822 warning | 112 // it with STACK_ALLOCATED(), runs into MSVC(VS 2013)'s C4822 warning |
| 79 // that the local class doesn't provide a local definition for 'operator new'. | 113 // that the local class doesn't provide a local definition for 'operator new'. |
| 80 // Which it intentionally doesn't and shouldn't. | 114 // Which it intentionally doesn't and shouldn't. |
| 81 // | 115 // |
| 82 // Work around such toolchain bugginess by lifting out the type, thereby | 116 // Work around such toolchain bugginess by lifting out the type, thereby |
| 83 // taking it out of C4822's reach. | 117 // taking it out of C4822's reach. |
| 84 class ChangedTouches final { | 118 class ChangedTouches final { |
| 85 STACK_ALLOCATED(); | 119 STACK_ALLOCATED(); |
| 86 | 120 |
| 87 public: | 121 public: |
| 88 // The touches corresponding to the particular change state this struct | 122 // The touches corresponding to the particular change state this struct |
| 89 // instance represents. | 123 // instance represents. |
| 90 Member<TouchList> touches_; | 124 Member<TouchList> touches_; |
| 91 | 125 |
| 92 using EventTargetSet = HeapHashSet<Member<EventTarget>>; | 126 using EventTargetSet = HeapHashSet<Member<EventTarget>>; |
| 93 // Set of targets involved in m_touches. | 127 // Set of targets involved in m_touches. |
| 94 EventTargetSet targets_; | 128 EventTargetSet targets_; |
| 129 }; | |
| 95 | 130 |
| 96 WebPointerProperties::PointerType pointer_type_; | 131 void ReportMetricsForTouch(const WebPointerEvent& event, |
| 97 }; | 132 DispatchEventResult dom_dispatch_result, |
| 133 bool prevent_default_called_on_uncancelable_event, | |
| 134 bool is_frame_loaded) { | |
| 135 int64_t latency_in_micros = | |
| 136 (TimeTicks::Now() - TimeTicks::FromSeconds(event.TimeStampSeconds())) | |
| 137 .InMicroseconds(); | |
| 138 if (event.IsCancelable()) { | |
| 139 if (is_frame_loaded) { | |
| 140 DEFINE_STATIC_LOCAL(EnumerationHistogram, | |
| 141 touch_dispositions_after_page_load_histogram, | |
| 142 ("Event.Touch.TouchDispositionsAfterPageLoad", | |
| 143 kTouchEventDispatchResultTypeMax)); | |
| 144 touch_dispositions_after_page_load_histogram.Count( | |
| 145 (dom_dispatch_result != DispatchEventResult::kNotCanceled) | |
| 146 ? kHandledTouches | |
| 147 : kUnhandledTouches); | |
| 148 | |
| 149 DEFINE_STATIC_LOCAL( | |
| 150 CustomCountHistogram, event_latency_after_page_load_histogram, | |
| 151 ("Event.Touch.TouchLatencyAfterPageLoad", 1, 100000000, 50)); | |
| 152 event_latency_after_page_load_histogram.Count(latency_in_micros); | |
| 153 } else { | |
| 154 DEFINE_STATIC_LOCAL(EnumerationHistogram, | |
| 155 touch_dispositions_before_page_load_histogram, | |
| 156 ("Event.Touch.TouchDispositionsBeforePageLoad", | |
| 157 kTouchEventDispatchResultTypeMax)); | |
| 158 touch_dispositions_before_page_load_histogram.Count( | |
| 159 (dom_dispatch_result != DispatchEventResult::kNotCanceled) | |
| 160 ? kHandledTouches | |
| 161 : kUnhandledTouches); | |
| 162 | |
| 163 DEFINE_STATIC_LOCAL( | |
| 164 CustomCountHistogram, event_latency_before_page_load_histogram, | |
| 165 ("Event.Touch.TouchLatencyBeforePageLoad", 1, 100000000, 50)); | |
| 166 event_latency_before_page_load_histogram.Count(latency_in_micros); | |
| 167 } | |
| 168 // Report the touch disposition there is no active fling animation. | |
| 169 DEFINE_STATIC_LOCAL(EnumerationHistogram, | |
| 170 touch_dispositions_outside_fling_histogram, | |
| 171 ("Event.Touch.TouchDispositionsOutsideFling2", | |
| 172 kTouchEventDispatchResultTypeMax)); | |
| 173 touch_dispositions_outside_fling_histogram.Count( | |
| 174 (dom_dispatch_result != DispatchEventResult::kNotCanceled) | |
| 175 ? kHandledTouches | |
| 176 : kUnhandledTouches); | |
| 177 } | |
| 178 | |
| 179 // Report the touch disposition when there is an active fling | |
| 180 // animation. | |
| 181 if (event.dispatch_type == | |
| 182 WebInputEvent::kListenersForcedNonBlockingDueToFling) { | |
| 183 DEFINE_STATIC_LOCAL(EnumerationHistogram, | |
| 184 touch_dispositions_during_fling_histogram, | |
| 185 ("Event.Touch.TouchDispositionsDuringFling2", | |
| 186 kTouchEventDispatchResultTypeMax)); | |
| 187 touch_dispositions_during_fling_histogram.Count( | |
| 188 prevent_default_called_on_uncancelable_event ? kHandledTouches | |
| 189 : kUnhandledTouches); | |
| 190 } | |
| 191 } | |
| 98 | 192 |
| 99 } // namespace | 193 } // namespace |
| 100 | 194 |
| 101 TouchEventManager::TouchEventManager(LocalFrame& frame) : frame_(frame) { | 195 TouchEventManager::TouchEventManager(LocalFrame& frame) : frame_(frame) { |
| 102 Clear(); | 196 Clear(); |
| 103 } | 197 } |
| 104 | 198 |
| 105 void TouchEventManager::Clear() { | 199 void TouchEventManager::Clear() { |
| 106 touch_sequence_document_.Clear(); | 200 touch_sequence_document_.Clear(); |
| 107 target_for_touch_id_.clear(); | 201 touch_attribute_map_.clear(); |
| 108 region_for_touch_id_.clear(); | 202 last_coalesced_touch_event_ = WebTouchEvent(); |
| 109 touch_pressed_ = false; | |
| 110 suppressing_touchmoves_within_slop_ = false; | 203 suppressing_touchmoves_within_slop_ = false; |
| 111 current_touch_action_ = TouchAction::kTouchActionAuto; | 204 current_touch_action_ = TouchAction::kTouchActionAuto; |
| 112 } | 205 } |
| 113 | 206 |
| 114 DEFINE_TRACE(TouchEventManager) { | 207 DEFINE_TRACE(TouchEventManager) { |
| 115 visitor->Trace(frame_); | 208 visitor->Trace(frame_); |
| 116 visitor->Trace(touch_sequence_document_); | 209 visitor->Trace(touch_sequence_document_); |
| 117 visitor->Trace(target_for_touch_id_); | 210 visitor->Trace(touch_attribute_map_); |
| 118 } | 211 } |
| 119 | 212 |
| 120 Touch* TouchEventManager::CreateDomTouch(const WebTouchPoint& point, | 213 Touch* TouchEventManager::CreateDomTouch( |
| 121 bool* known_target) { | 214 const TouchEventManager::TouchPointAttributes* point_attr, |
| 122 Node* touch_node = nullptr; | 215 bool* known_target) { |
| 123 String region_id; | 216 Node* touch_node = point_attr->target_; |
| 217 String region_id = point_attr->region_; | |
| 124 *known_target = false; | 218 *known_target = false; |
| 125 FloatPoint content_point; | 219 FloatPoint content_point; |
| 126 FloatSize adjusted_radius; | 220 FloatSize adjusted_radius; |
| 127 | 221 |
| 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; | 222 LocalFrame* target_frame = nullptr; |
| 143 if (touch_node) { | 223 if (touch_node) { |
| 144 Document& doc = touch_node->GetDocument(); | 224 Document& doc = touch_node->GetDocument(); |
| 145 // If the target node has moved to a new document while it was being | 225 // 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 | 226 // 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. | 227 // leak nodes from one document to another. See http://crbug.com/394339. |
| 148 if (&doc == touch_sequence_document_.Get()) { | 228 if (&doc == touch_sequence_document_.Get()) { |
| 149 target_frame = doc.GetFrame(); | 229 target_frame = doc.GetFrame(); |
| 150 *known_target = true; | 230 *known_target = true; |
| 151 } | 231 } |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 162 // Document so that there's some valid node here. Perhaps this | 242 // Document so that there's some valid node here. Perhaps this |
| 163 // should really be LocalDOMWindow, but in all other cases the target of | 243 // 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. | 244 // 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 | 245 // Since we know there was no handler invoked, the specific target |
| 166 // should be completely irrelevant to the application. | 246 // should be completely irrelevant to the application. |
| 167 touch_node = touch_sequence_document_; | 247 touch_node = touch_sequence_document_; |
| 168 target_frame = touch_sequence_document_->GetFrame(); | 248 target_frame = touch_sequence_document_->GetFrame(); |
| 169 } | 249 } |
| 170 DCHECK(target_frame); | 250 DCHECK(target_frame); |
| 171 | 251 |
| 252 WebPointerEvent transformed_event = | |
| 253 point_attr->event_.WebPointerEventInRootFrame(); | |
| 172 // pagePoint should always be in the target element's document coordinates. | 254 // pagePoint should always be in the target element's document coordinates. |
| 173 FloatPoint page_point = | 255 FloatPoint page_point = target_frame->View()->RootFrameToContents( |
| 174 target_frame->View()->RootFrameToContents(point.PositionInWidget()); | 256 transformed_event.PositionInWidget()); |
| 175 float scale_factor = 1.0f / target_frame->PageZoomFactor(); | 257 float scale_factor = 1.0f / target_frame->PageZoomFactor(); |
| 176 | 258 |
| 177 content_point = page_point.ScaledBy(scale_factor); | 259 content_point = page_point.ScaledBy(scale_factor); |
| 178 adjusted_radius = | 260 adjusted_radius = FloatSize(transformed_event.width, transformed_event.height) |
| 179 FloatSize(point.radius_x, point.radius_y).ScaledBy(scale_factor); | 261 .ScaledBy(scale_factor); |
| 180 | 262 |
| 181 return Touch::Create(target_frame, touch_node, point.id, | 263 return Touch::Create(target_frame, touch_node, point_attr->event_.id, |
| 182 point.PositionInScreen(), content_point, adjusted_radius, | 264 transformed_event.PositionInScreen(), content_point, |
| 183 point.rotation_angle, point.force, region_id); | 265 adjusted_radius, transformed_event.rotation_angle, |
| 266 transformed_event.force, region_id); | |
| 184 } | 267 } |
| 185 | 268 |
| 186 WebInputEventResult TouchEventManager::DispatchTouchEvents( | 269 WebCoalescedInputEvent TouchEventManager::GenerateWebCoalescedInputEvent() { |
| 187 const WebTouchEvent& event, | 270 DCHECK(!touch_attribute_map_.IsEmpty()); |
| 188 const Vector<WebTouchEvent>& coalesced_events, | 271 |
| 189 bool all_touches_released) { | 272 WebTouchEvent event; |
| 273 | |
| 274 const auto& first_touch_pointer_event = | |
| 275 touch_attribute_map_.begin()->value->event_; | |
| 276 | |
| 277 SetWebTouchEventAttributesFromWebPointerEvent(&event, | |
| 278 first_touch_pointer_event); | |
| 279 SetWebTouchEventAttributesFromWebPointerEvent(&last_coalesced_touch_event_, | |
| 280 first_touch_pointer_event); | |
| 281 WebInputEvent::Type touch_event_type = WebInputEvent::kTouchMove; | |
| 282 Vector<WebPointerEvent> all_coalesced_events; | |
| 283 Vector<int> available_ids; | |
| 284 for (const auto& id : touch_attribute_map_.Keys()) | |
| 285 available_ids.push_back(id); | |
| 286 std::sort(available_ids.begin(), available_ids.end()); | |
| 287 for (const int& touch_point_id : available_ids) { | |
| 288 const auto& touch_point_attribute = touch_attribute_map_.at(touch_point_id); | |
| 289 const WebPointerEvent& touch_pointer_event = touch_point_attribute->event_; | |
| 290 event.touches[event.touches_length++] = | |
| 291 CreateWebTouchPointFromWebPointerEvent(touch_pointer_event, | |
| 292 touch_point_attribute->stale_); | |
| 293 | |
| 294 // Only change the touch event type from move. So if we have two pointers | |
| 295 // in up and down state we just set the touch event type to the first one | |
| 296 // we see. | |
| 297 if (touch_event_type == WebInputEvent::kTouchMove) { | |
| 298 if (touch_pointer_event.GetType() == WebInputEvent::kPointerDown) | |
| 299 touch_event_type = WebInputEvent::kTouchStart; | |
| 300 else if (touch_pointer_event.GetType() == WebInputEvent::kPointerCancel) | |
| 301 touch_event_type = WebInputEvent::kTouchCancel; | |
| 302 else if (touch_pointer_event.GetType() == WebInputEvent::kPointerUp) | |
| 303 touch_event_type = WebInputEvent::kTouchEnd; | |
| 304 } | |
| 305 | |
| 306 for (const WebPointerEvent& coalesced_event : | |
| 307 touch_point_attribute->coalesced_events_) | |
| 308 all_coalesced_events.push_back(coalesced_event); | |
| 309 } | |
| 310 event.SetType(touch_event_type); | |
| 311 last_coalesced_touch_event_.SetType(touch_event_type); | |
| 312 | |
| 313 #if DCHECK_IS_ON() | |
| 314 // If we have more than one coalesced event we expect them all to be the same. | |
| 315 if (all_coalesced_events.size() > 1) { | |
| 316 WebInputEvent::Type event_type = all_coalesced_events[0].GetType(); | |
| 317 for (const WebPointerEvent& web_pointer_event : all_coalesced_events) | |
| 318 DCHECK_EQ(event_type, web_pointer_event.GetType()); | |
|
mustaq
2017/06/13 15:15:24
Any clue why multi-touch-partial-sequence.html cau
Navid Zolghadr
2017/06/13 15:19:08
Apparently this assumption is incorrect. The curre
Navid Zolghadr
2017/06/13 19:31:17
There were also other cases that event_sender allo
| |
| 319 } | |
| 320 #endif | |
| 321 | |
| 322 // Create all coalesced touch events based on pointerevents | |
| 323 struct { | |
| 324 bool operator()(const WebPointerEvent& a, const WebPointerEvent& b) { | |
| 325 return a.TimeStampSeconds() < b.TimeStampSeconds(); | |
| 326 } | |
| 327 } timestamp_based_event_comparison; | |
| 328 std::sort(all_coalesced_events.begin(), all_coalesced_events.end(), | |
| 329 timestamp_based_event_comparison); | |
| 330 WebCoalescedInputEvent result(event, std::vector<const WebInputEvent*>()); | |
| 331 for (const auto& web_pointer_event : all_coalesced_events) { | |
| 332 if (web_pointer_event.GetType() == WebInputEvent::kPointerDown) { | |
| 333 WebTouchPoint web_touch_point(web_pointer_event); | |
| 334 last_coalesced_touch_event_ | |
| 335 .touches[last_coalesced_touch_event_.touches_length++] = | |
| 336 CreateWebTouchPointFromWebPointerEvent(web_pointer_event, false); | |
| 337 struct { | |
| 338 bool operator()(const WebTouchPoint& a, const WebTouchPoint& b) { | |
| 339 return a.id < b.id; | |
| 340 } | |
| 341 } id_based_event_comparison; | |
| 342 std::sort(last_coalesced_touch_event_.touches, | |
| 343 last_coalesced_touch_event_.touches + | |
| 344 last_coalesced_touch_event_.touches_length, | |
| 345 id_based_event_comparison); | |
| 346 result.AddCoalescedEvent(last_coalesced_touch_event_); | |
| 347 } else { | |
| 348 for (unsigned i = 0; i < last_coalesced_touch_event_.touches_length; | |
| 349 ++i) { | |
| 350 if (last_coalesced_touch_event_.touches[i].id == web_pointer_event.id) { | |
| 351 last_coalesced_touch_event_.touches[i].movement_x = | |
| 352 web_pointer_event.movement_x; | |
| 353 last_coalesced_touch_event_.touches[i].movement_y = | |
| 354 web_pointer_event.movement_y; | |
| 355 last_coalesced_touch_event_.SetTimeStampSeconds( | |
| 356 web_pointer_event.TimeStampSeconds()); | |
| 357 last_coalesced_touch_event_.touches[i].state = | |
| 358 TouchPointStateFromPointerEventType(web_pointer_event.GetType(), | |
| 359 false); | |
| 360 result.AddCoalescedEvent(last_coalesced_touch_event_); | |
| 361 | |
| 362 // Remove up and canceled points. | |
| 363 unsigned result_size = 0; | |
| 364 for (unsigned i = 0; i < last_coalesced_touch_event_.touches_length; | |
| 365 i++) { | |
| 366 if (last_coalesced_touch_event_.touches[i].state != | |
| 367 WebTouchPoint::kStateCancelled && | |
| 368 last_coalesced_touch_event_.touches[i].state != | |
| 369 WebTouchPoint::kStateReleased) { | |
| 370 last_coalesced_touch_event_.touches[result_size++] = | |
| 371 last_coalesced_touch_event_.touches[i]; | |
| 372 } | |
| 373 } | |
| 374 last_coalesced_touch_event_.touches_length = result_size; | |
| 375 break; | |
| 376 } | |
| 377 } | |
| 378 } | |
| 379 | |
| 380 for (unsigned i = 0; i < event.touches_length; ++i) { | |
| 381 event.touches[i].state = blink::WebTouchPoint::kStateStationary; | |
| 382 event.touches[i].movement_x = 0; | |
| 383 event.touches[i].movement_y = 0; | |
| 384 } | |
| 385 } | |
| 386 | |
| 387 return result; | |
| 388 } | |
| 389 | |
| 390 WebInputEventResult | |
| 391 TouchEventManager::DispatchTouchEventFromAccumulatdTouchPoints() { | |
| 190 // Build up the lists to use for the |touches|, |targetTouches| and | 392 // Build up the lists to use for the |touches|, |targetTouches| and |
| 191 // |changedTouches| attributes in the JS event. See | 393 // |changedTouches| attributes in the JS event. See |
| 192 // http://www.w3.org/TR/touch-events/#touchevent-interface for how these | 394 // http://www.w3.org/TR/touch-events/#touchevent-interface for how these |
| 193 // lists fit together. | 395 // lists fit together. |
| 194 | 396 |
| 195 if (event.GetType() == WebInputEvent::kTouchEnd || | 397 bool new_touch_point_since_last_dispatch = false; |
| 196 event.GetType() == WebInputEvent::kTouchCancel || | 398 bool any_touch_canceled_or_ended = false; |
| 197 event.touches_length > 1) { | 399 bool all_touch_points_pressed = true; |
| 198 suppressing_touchmoves_within_slop_ = false; | 400 |
| 401 for (const auto& attr : touch_attribute_map_.Values()) { | |
| 402 if (!attr->stale_) | |
| 403 new_touch_point_since_last_dispatch = true; | |
| 404 if (attr->event_.GetType() == WebInputEvent::kPointerUp || | |
| 405 attr->event_.GetType() == WebInputEvent::kPointerCancel) | |
| 406 any_touch_canceled_or_ended = true; | |
| 407 if (attr->event_.GetType() != WebInputEvent::kPointerDown) | |
| 408 all_touch_points_pressed = false; | |
| 199 } | 409 } |
| 200 | 410 |
| 201 if (suppressing_touchmoves_within_slop_ && | 411 if (!new_touch_point_since_last_dispatch) |
| 202 event.GetType() == WebInputEvent::kTouchMove) { | 412 return WebInputEventResult::kNotHandled; |
| 203 if (!event.moved_beyond_slop_region) | 413 |
| 204 return WebInputEventResult::kHandledSuppressed; | 414 if (any_touch_canceled_or_ended || touch_attribute_map_.size() > 1) |
| 205 suppressing_touchmoves_within_slop_ = false; | 415 suppressing_touchmoves_within_slop_ = false; |
| 416 | |
| 417 if (suppressing_touchmoves_within_slop_) { | |
| 418 // There is exactly one touch point here otherwise | |
| 419 // |suppressing_touchmoves_within_slop_| would have been false. | |
| 420 DCHECK_EQ(1U, touch_attribute_map_.size()); | |
| 421 const auto& touch_point_attribute = touch_attribute_map_.begin()->value; | |
| 422 if (touch_point_attribute->event_.GetType() == | |
| 423 WebInputEvent::kPointerMove) { | |
| 424 if (!touch_point_attribute->event_.moved_beyond_slop_region) | |
| 425 return WebInputEventResult::kHandledSuppressed; | |
| 426 suppressing_touchmoves_within_slop_ = false; | |
| 427 } | |
| 206 } | 428 } |
| 207 | 429 |
| 208 // Holds the complete set of touches on the screen. | 430 // Holds the complete set of touches on the screen. |
| 209 TouchList* touches = TouchList::Create(); | 431 TouchList* touches = TouchList::Create(); |
| 210 | 432 |
| 211 // A different view on the 'touches' list above, filtered and grouped by | 433 // A different view on the 'touches' list above, filtered and grouped by |
| 212 // event target. Used for the |targetTouches| list in the JS event. | 434 // event target. Used for the |targetTouches| list in the JS event. |
| 213 using TargetTouchesHeapMap = HeapHashMap<EventTarget*, Member<TouchList>>; | 435 using TargetTouchesHeapMap = HeapHashMap<EventTarget*, Member<TouchList>>; |
| 214 TargetTouchesHeapMap touches_by_target; | 436 TargetTouchesHeapMap touches_by_target; |
| 215 | 437 |
| 216 // Array of touches per state, used to assemble the |changedTouches| list. | 438 // Array of touches per state, used to assemble the |changedTouches| list. |
| 217 ChangedTouches changed_touches[WebTouchPoint::kStateMax + 1]; | 439 ChangedTouches changed_touches[WebInputEvent::kPointerTypeLast - |
| 440 WebInputEvent::kPointerTypeFirst + 1]; | |
| 218 | 441 |
| 219 for (unsigned touch_point_idx = 0; touch_point_idx < event.touches_length; | 442 Vector<int> available_ids; |
| 220 touch_point_idx++) { | 443 for (const auto& id : touch_attribute_map_.Keys()) |
| 221 const WebTouchPoint& point = event.TouchPointInRootFrame(touch_point_idx); | 444 available_ids.push_back(id); |
| 222 WebTouchPoint::State point_state = point.state; | 445 std::sort(available_ids.begin(), available_ids.end()); |
| 446 for (const int& touch_point_id : available_ids) { | |
| 447 const auto& touch_point_attribute = touch_attribute_map_.at(touch_point_id); | |
| 448 WebInputEvent::Type event_type = touch_point_attribute->event_.GetType(); | |
| 223 bool known_target; | 449 bool known_target; |
| 224 | 450 |
| 225 Touch* touch = CreateDomTouch(point, &known_target); | 451 Touch* touch = CreateDomTouch(touch_point_attribute, &known_target); |
| 226 EventTarget* touch_target = touch->target(); | 452 EventTarget* touch_target = touch->target(); |
| 227 | 453 |
| 228 // Ensure this target's touch list exists, even if it ends up empty, so | 454 // Ensure this target's touch list exists, even if it ends up empty, so |
| 229 // it can always be passed to TouchEvent::Create below. | 455 // it can always be passed to TouchEvent::Create below. |
| 230 TargetTouchesHeapMap::iterator target_touches_iterator = | 456 TargetTouchesHeapMap::iterator target_touches_iterator = |
| 231 touches_by_target.find(touch_target); | 457 touches_by_target.find(touch_target); |
| 232 if (target_touches_iterator == touches_by_target.end()) { | 458 if (target_touches_iterator == touches_by_target.end()) { |
| 233 touches_by_target.Set(touch_target, TouchList::Create()); | 459 touches_by_target.Set(touch_target, TouchList::Create()); |
| 234 target_touches_iterator = touches_by_target.find(touch_target); | 460 target_touches_iterator = touches_by_target.find(touch_target); |
| 235 } | 461 } |
| 236 | 462 |
| 237 // |touches| and |targetTouches| should only contain information about | 463 // |touches| and |targetTouches| should only contain information about |
| 238 // touches still on the screen, so if this point is released or | 464 // touches still on the screen, so if this point is released or |
| 239 // cancelled it will only appear in the |changedTouches| list. | 465 // cancelled it will only appear in the |changedTouches| list. |
| 240 if (point_state != WebTouchPoint::kStateReleased && | 466 if (event_type != WebInputEvent::kPointerUp && |
| 241 point_state != WebTouchPoint::kStateCancelled) { | 467 event_type != WebInputEvent::kPointerCancel) { |
| 242 touches->Append(touch); | 468 touches->Append(touch); |
| 243 target_touches_iterator->value->Append(touch); | 469 target_touches_iterator->value->Append(touch); |
| 244 } | 470 } |
| 245 | 471 |
| 246 // Now build up the correct list for |changedTouches|. | 472 // Now build up the correct list for |changedTouches|. |
| 247 // Note that any touches that are in the TouchStationary state (e.g. if | 473 // Note that any touches that are in the TouchStationary state (e.g. if |
| 248 // the user had several points touched but did not move them all) should | 474 // the user had several points touched but did not move them all) should |
| 249 // never be in the |changedTouches| list so we do not handle them | 475 // never be in the |changedTouches| list so we do not handle them |
| 250 // explicitly here. See https://bugs.webkit.org/show_bug.cgi?id=37609 | 476 // explicitly here. See https://bugs.webkit.org/show_bug.cgi?id=37609 |
| 251 // for further discussion about the TouchStationary state. | 477 // for further discussion about the TouchStationary state. |
| 252 if (point_state != WebTouchPoint::kStateStationary && known_target) { | 478 if (!touch_point_attribute->stale_ && known_target) { |
| 253 DCHECK_LE(point_state, WebTouchPoint::kStateMax); | 479 size_t event_type_idx = event_type - WebInputEvent::kPointerTypeFirst; |
| 254 if (!changed_touches[point_state].touches_) | 480 if (!changed_touches[event_type_idx].touches_) |
| 255 changed_touches[point_state].touches_ = TouchList::Create(); | 481 changed_touches[event_type_idx].touches_ = TouchList::Create(); |
| 256 changed_touches[point_state].touches_->Append(touch); | 482 changed_touches[event_type_idx].touches_->Append(touch); |
| 257 changed_touches[point_state].targets_.insert(touch_target); | 483 changed_touches[event_type_idx].targets_.insert(touch_target); |
| 258 changed_touches[point_state].pointer_type_ = point.pointer_type; | |
| 259 } | 484 } |
| 260 } | 485 } |
| 261 | 486 |
| 262 if (all_touches_released) { | 487 WebInputEventResult event_result = WebInputEventResult::kNotHandled; |
| 263 touch_sequence_document_.Clear(); | |
| 264 current_touch_action_ = TouchAction::kTouchActionAuto; | |
| 265 } | |
| 266 | 488 |
| 267 WebInputEventResult event_result = WebInputEventResult::kNotHandled; | 489 // First we construct the webcoalescedinputevent containing all the coalesced |
| 268 // First we construct the webcoalescedinputevent contains all the coalesced | |
| 269 // touch event. | 490 // touch event. |
| 270 std::vector<const WebInputEvent*> coalesced_touches; | 491 WebCoalescedInputEvent coalesced_event = GenerateWebCoalescedInputEvent(); |
| 271 for (size_t i = 0; i < coalesced_events.size(); ++i) { | |
| 272 coalesced_touches.push_back(&coalesced_events[i]); | |
| 273 } | |
| 274 WebCoalescedInputEvent coalesced_event(event, coalesced_touches); | |
| 275 | 492 |
| 276 // Now iterate through the |changedTouches| list and |m_targets| within it, | 493 // Now iterate through the |changedTouches| list and |m_targets| within it, |
| 277 // sending TouchEvents to the targets as required. | 494 // sending TouchEvents to the targets as required. |
| 278 for (unsigned state = 0; state <= WebTouchPoint::kStateMax; ++state) { | 495 for (unsigned action = WebInputEvent::kPointerTypeFirst; |
| 279 if (!changed_touches[state].touches_) | 496 action <= WebInputEvent::kPointerTypeLast; ++action) { |
| 497 size_t action_idx = action - WebInputEvent::kPointerTypeFirst; | |
| 498 if (!changed_touches[action_idx].touches_) | |
| 280 continue; | 499 continue; |
| 281 | 500 |
| 282 const AtomicString& event_name(TouchEventNameForTouchPointState( | 501 const AtomicString& event_name(TouchEventNameForPointerEventType( |
| 283 static_cast<WebTouchPoint::State>(state))); | 502 static_cast<WebInputEvent::Type>(action))); |
| 284 for (const auto& event_target : changed_touches[state].targets_) { | 503 |
| 504 for (const auto& event_target : changed_touches[action_idx].targets_) { | |
| 285 EventTarget* touch_event_target = event_target; | 505 EventTarget* touch_event_target = event_target; |
| 286 TouchEvent* touch_event = TouchEvent::Create( | 506 TouchEvent* touch_event = TouchEvent::Create( |
| 287 coalesced_event, touches, touches_by_target.at(touch_event_target), | 507 coalesced_event, touches, touches_by_target.at(touch_event_target), |
| 288 changed_touches[state].touches_.Get(), event_name, | 508 changed_touches[action_idx].touches_.Get(), event_name, |
| 289 touch_event_target->ToNode()->GetDocument().domWindow(), | 509 touch_event_target->ToNode()->GetDocument().domWindow(), |
| 290 current_touch_action_); | 510 current_touch_action_); |
| 291 | 511 |
| 292 DispatchEventResult dom_dispatch_result = | 512 DispatchEventResult dom_dispatch_result = |
| 293 touch_event_target->DispatchEvent(touch_event); | 513 touch_event_target->DispatchEvent(touch_event); |
| 294 | 514 |
| 295 // Only report for top level documents with a single touch on | 515 // Only report for top level documents with a single touch on |
| 296 // touch-start or the first touch-move. | 516 // touch-start or the first touch-move. |
| 297 if (event.touch_start_or_first_touch_move && event.touches_length == 1 && | 517 if (touch_attribute_map_.size() == 1 && frame_->IsMainFrame()) { |
| 298 frame_->IsMainFrame()) { | 518 const auto& event = touch_attribute_map_.begin()->value->event_; |
| 299 // Record the disposition and latency of touch starts and first touch | 519 if (event.touch_start_or_first_touch_move) { |
| 300 // moves before and after the page is fully loaded respectively. | 520 // Record the disposition and latency of touch starts and first touch |
| 301 int64_t latency_in_micros = | 521 // moves before and after the page is fully loaded respectively. |
| 302 (TimeTicks::Now() - | 522 ReportMetricsForTouch( |
| 303 TimeTicks::FromSeconds(event.TimeStampSeconds())) | 523 event, dom_dispatch_result, |
| 304 .InMicroseconds(); | 524 touch_event->PreventDefaultCalledOnUncancelableEvent(), |
| 305 if (event.IsCancelable()) { | 525 frame_->GetDocument()->IsLoadCompleted()); |
| 306 if (frame_->GetDocument()->IsLoadCompleted()) { | |
| 307 DEFINE_STATIC_LOCAL(EnumerationHistogram, | |
| 308 touch_dispositions_after_page_load_histogram, | |
| 309 ("Event.Touch.TouchDispositionsAfterPageLoad", | |
| 310 kTouchEventDispatchResultTypeMax)); | |
| 311 touch_dispositions_after_page_load_histogram.Count( | |
| 312 (dom_dispatch_result != DispatchEventResult::kNotCanceled) | |
| 313 ? kHandledTouches | |
| 314 : kUnhandledTouches); | |
| 315 | |
| 316 DEFINE_STATIC_LOCAL( | |
| 317 CustomCountHistogram, event_latency_after_page_load_histogram, | |
| 318 ("Event.Touch.TouchLatencyAfterPageLoad", 1, 100000000, 50)); | |
| 319 event_latency_after_page_load_histogram.Count(latency_in_micros); | |
| 320 } else { | |
| 321 DEFINE_STATIC_LOCAL(EnumerationHistogram, | |
| 322 touch_dispositions_before_page_load_histogram, | |
| 323 ("Event.Touch.TouchDispositionsBeforePageLoad", | |
| 324 kTouchEventDispatchResultTypeMax)); | |
| 325 touch_dispositions_before_page_load_histogram.Count( | |
| 326 (dom_dispatch_result != DispatchEventResult::kNotCanceled) | |
| 327 ? kHandledTouches | |
| 328 : kUnhandledTouches); | |
| 329 | |
| 330 DEFINE_STATIC_LOCAL( | |
| 331 CustomCountHistogram, event_latency_before_page_load_histogram, | |
| 332 ("Event.Touch.TouchLatencyBeforePageLoad", 1, 100000000, 50)); | |
| 333 event_latency_before_page_load_histogram.Count(latency_in_micros); | |
| 334 } | |
| 335 // Report the touch disposition there is no active fling animation. | |
| 336 DEFINE_STATIC_LOCAL(EnumerationHistogram, | |
| 337 touch_dispositions_outside_fling_histogram, | |
| 338 ("Event.Touch.TouchDispositionsOutsideFling2", | |
| 339 kTouchEventDispatchResultTypeMax)); | |
| 340 touch_dispositions_outside_fling_histogram.Count( | |
| 341 (dom_dispatch_result != DispatchEventResult::kNotCanceled) | |
| 342 ? kHandledTouches | |
| 343 : kUnhandledTouches); | |
| 344 } | |
| 345 | |
| 346 // Report the touch disposition when there is an active fling animation. | |
| 347 if (event.dispatch_type == | |
| 348 WebInputEvent::kListenersForcedNonBlockingDueToFling) { | |
| 349 DEFINE_STATIC_LOCAL(EnumerationHistogram, | |
| 350 touch_dispositions_during_fling_histogram, | |
| 351 ("Event.Touch.TouchDispositionsDuringFling2", | |
| 352 kTouchEventDispatchResultTypeMax)); | |
| 353 touch_dispositions_during_fling_histogram.Count( | |
| 354 touch_event->PreventDefaultCalledOnUncancelableEvent() | |
| 355 ? kHandledTouches | |
| 356 : kUnhandledTouches); | |
| 357 } | 526 } |
| 358 } | 527 } |
| 359 event_result = EventHandlingUtil::MergeEventResult( | 528 event_result = EventHandlingUtil::MergeEventResult( |
| 360 event_result, | 529 event_result, |
| 361 EventHandlingUtil::ToWebInputEventResult(dom_dispatch_result)); | 530 EventHandlingUtil::ToWebInputEventResult(dom_dispatch_result)); |
| 362 } | 531 } |
| 363 } | 532 } |
| 364 | 533 |
| 365 // Do not suppress any touchmoves if the touchstart is consumed. | 534 // Suppress following touchmoves within the slop region if the touchstart is |
| 366 if (IsTouchSequenceStart(event) && | 535 // not consumed. |
| 536 if (all_touch_points_pressed && | |
| 367 event_result == WebInputEventResult::kNotHandled) { | 537 event_result == WebInputEventResult::kNotHandled) { |
| 368 suppressing_touchmoves_within_slop_ = true; | 538 suppressing_touchmoves_within_slop_ = true; |
| 369 } | 539 } |
| 370 | 540 |
| 371 return event_result; | 541 return event_result; |
| 372 } | 542 } |
| 373 | 543 |
| 374 void TouchEventManager::UpdateTargetAndRegionMapsForTouchStart( | 544 void TouchEventManager::UpdateTouchAttributeMapsForPointerDown( |
| 375 const WebTouchPoint& touch_point, | 545 const WebPointerEvent& event, |
| 376 const EventHandlingUtil::PointerEventTarget& pointer_event_target) { | 546 const EventHandlingUtil::PointerEventTarget& pointer_event_target) { |
| 377 // Touch events implicitly capture to the touched node, and don't change | 547 // Touch events implicitly capture to the touched node, and don't change |
| 378 // active/hover states themselves (Gesture events do). So we only need | 548 // active/hover states themselves (Gesture events do). So we only need |
| 379 // to hit-test on touchstart and when the target could be different than | 549 // to hit-test on touchstart and when the target could be different than |
| 380 // the corresponding pointer event target. | 550 // the corresponding pointer event target. |
| 381 DCHECK(touch_point.state == WebTouchPoint::kStatePressed); | 551 DCHECK(event.GetType() == WebInputEvent::kPointerDown); |
| 552 // Ideally we'd DCHECK(!touch_attribute_map_.Contains(event.id)) | |
| 553 // since we shouldn't get a touchstart for a touch that's already | |
| 554 // down. However EventSender allows this to be violated and there's | |
| 555 // some tests that take advantage of it. There may also be edge | |
| 556 // cases in the browser where this happens. | |
| 557 // See http://crbug.com/345372. | |
| 558 touch_attribute_map_.Set(event.id, new TouchPointAttributes(event)); | |
| 559 | |
| 382 Node* touch_node = pointer_event_target.target_node; | 560 Node* touch_node = pointer_event_target.target_node; |
| 383 String region = pointer_event_target.region; | 561 String region = pointer_event_target.region; |
| 384 | 562 |
| 385 HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kTouchEvent | | 563 HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kTouchEvent | |
| 386 HitTestRequest::kReadOnly | | 564 HitTestRequest::kReadOnly | |
| 387 HitTestRequest::kActive; | 565 HitTestRequest::kActive; |
| 388 HitTestResult result; | 566 HitTestResult result; |
| 389 // For the touchPressed points hit-testing is done in | 567 // For the touchPressed points hit-testing is done in |
| 390 // PointerEventManager. If it was the second touch there is a | 568 // PointerEventManager. If it was the second touch there is a |
| 391 // capturing documents for the touch and |m_touchSequenceDocument| | 569 // capturing documents for the touch and |m_touchSequenceDocument| |
| 392 // is not null. So if PointerEventManager should hit-test again | 570 // is not null. So if PointerEventManager should hit-test again |
| 393 // against |m_touchSequenceDocument| if the target set by | 571 // against |m_touchSequenceDocument| if the target set by |
| 394 // PointerEventManager was either null or not in | 572 // PointerEventManager was either null or not in |
| 395 // |m_touchSequenceDocument|. | 573 // |m_touchSequenceDocument|. |
| 396 if (touch_sequence_document_ && | 574 if (touch_sequence_document_ && |
| 397 (!touch_node || &touch_node->GetDocument() != touch_sequence_document_)) { | 575 (!touch_node || &touch_node->GetDocument() != touch_sequence_document_)) { |
| 398 if (touch_sequence_document_->GetFrame()) { | 576 if (touch_sequence_document_->GetFrame()) { |
| 399 LayoutPoint frame_point = LayoutPoint( | 577 LayoutPoint frame_point = LayoutPoint( |
| 400 touch_sequence_document_->GetFrame()->View()->RootFrameToContents( | 578 touch_sequence_document_->GetFrame()->View()->RootFrameToContents( |
| 401 touch_point.PositionInWidget())); | 579 event.PositionInWidget())); |
| 402 result = EventHandlingUtil::HitTestResultInFrame( | 580 result = EventHandlingUtil::HitTestResultInFrame( |
| 403 touch_sequence_document_->GetFrame(), frame_point, hit_type); | 581 touch_sequence_document_->GetFrame(), frame_point, hit_type); |
| 404 Node* node = result.InnerNode(); | 582 Node* node = result.InnerNode(); |
| 405 if (!node) | 583 if (!node) |
| 406 return; | 584 return; |
| 407 if (isHTMLCanvasElement(node)) { | 585 if (isHTMLCanvasElement(node)) { |
| 408 HitTestCanvasResult* hit_test_canvas_result = | 586 HitTestCanvasResult* hit_test_canvas_result = |
| 409 toHTMLCanvasElement(node)->GetControlAndIdIfHitRegionExists( | 587 toHTMLCanvasElement(node)->GetControlAndIdIfHitRegionExists( |
| 410 result.PointInInnerNodeFrame()); | 588 result.PointInInnerNodeFrame()); |
| 411 if (hit_test_canvas_result->GetControl()) | 589 if (hit_test_canvas_result->GetControl()) |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 423 if (!touch_node) | 601 if (!touch_node) |
| 424 return; | 602 return; |
| 425 if (!touch_sequence_document_) { | 603 if (!touch_sequence_document_) { |
| 426 // Keep track of which document should receive all touch events | 604 // Keep track of which document should receive all touch events |
| 427 // in the active sequence. This must be a single document to | 605 // in the active sequence. This must be a single document to |
| 428 // ensure we don't leak Nodes between documents. | 606 // ensure we don't leak Nodes between documents. |
| 429 touch_sequence_document_ = &(touch_node->GetDocument()); | 607 touch_sequence_document_ = &(touch_node->GetDocument()); |
| 430 DCHECK(touch_sequence_document_->GetFrame()->View()); | 608 DCHECK(touch_sequence_document_->GetFrame()->View()); |
| 431 } | 609 } |
| 432 | 610 |
| 433 // Ideally we'd DCHECK(!m_targetForTouchID.contains(point.id()) | 611 TouchPointAttributes* attributes = touch_attribute_map_.at(event.id); |
| 434 // since we shouldn't get a touchstart for a touch that's already | 612 attributes->target_ = touch_node; |
| 435 // down. However EventSender allows this to be violated and there's | 613 attributes->region_ = region; |
| 436 // some tests that take advantage of it. There may also be edge | |
| 437 // cases in the browser where this happens. | |
| 438 // See http://crbug.com/345372. | |
| 439 target_for_touch_id_.Set(touch_point.id, touch_node); | |
| 440 | |
| 441 region_for_touch_id_.Set(touch_point.id, region); | |
| 442 | 614 |
| 443 TouchAction effective_touch_action = | 615 TouchAction effective_touch_action = |
| 444 TouchActionUtil::ComputeEffectiveTouchAction(*touch_node); | 616 TouchActionUtil::ComputeEffectiveTouchAction(*touch_node); |
| 445 if (effective_touch_action != TouchAction::kTouchActionAuto) { | 617 if (effective_touch_action != TouchAction::kTouchActionAuto) { |
| 446 frame_->GetPage()->GetChromeClient().SetTouchAction(frame_, | 618 frame_->GetPage()->GetChromeClient().SetTouchAction(frame_, |
| 447 effective_touch_action); | 619 effective_touch_action); |
| 448 | 620 |
| 449 // Combine the current touch action sequence with the touch action | 621 // Combine the current touch action sequence with the touch action |
| 450 // for the current finger press. | 622 // for the current finger press. |
| 451 current_touch_action_ &= effective_touch_action; | 623 current_touch_action_ &= effective_touch_action; |
| 452 } | 624 } |
| 453 } | 625 } |
| 454 | 626 |
| 455 bool TouchEventManager::HitTestTouchPointsIfNeeded( | 627 void TouchEventManager::HandleTouchPoint( |
| 456 const WebTouchEvent& event, | 628 const WebPointerEvent& event, |
| 457 const HeapVector<EventHandlingUtil::PointerEventTarget>& | 629 const Vector<WebPointerEvent>& coalesced_events, |
| 458 pointer_event_targets) { | 630 const EventHandlingUtil::PointerEventTarget& pointer_event_target) { |
| 459 bool new_touch_sequence = true; | 631 DCHECK_GE(event.GetType(), WebInputEvent::kPointerTypeFirst); |
| 460 bool all_touches_released = true; | 632 DCHECK_LE(event.GetType(), WebInputEvent::kPointerTypeLast); |
| 461 | 633 |
| 462 for (unsigned i = 0; i < event.touches_length; ++i) { | 634 if (touch_attribute_map_.IsEmpty()) { |
| 463 WebTouchPoint::State state = event.touches[i].state; | |
| 464 if (state != WebTouchPoint::kStatePressed) | |
| 465 new_touch_sequence = false; | |
| 466 if (state != WebTouchPoint::kStateReleased && | |
| 467 state != WebTouchPoint::kStateCancelled) | |
| 468 all_touches_released = false; | |
| 469 } | |
| 470 if (new_touch_sequence) { | |
| 471 // Ideally we'd DCHECK(!m_touchSequenceDocument) here since we should | 635 // Ideally we'd DCHECK(!m_touchSequenceDocument) here since we should |
| 472 // have cleared the active document when we saw the last release. But we | 636 // have cleared the active document when we saw the last release. But we |
| 473 // have some tests that violate this, ClusterFuzz could trigger it, and | 637 // have some tests that violate this, ClusterFuzz could trigger it, and |
| 474 // there may be cases where the browser doesn't reliably release all | 638 // there may be cases where the browser doesn't reliably release all |
| 475 // touches. http://crbug.com/345372 tracks this. | 639 // touches. http://crbug.com/345372 tracks this. |
| 476 touch_sequence_document_.Clear(); | 640 allTouchesReleasedCleanup(); |
| 477 } | 641 } |
| 478 | 642 |
| 479 DCHECK(frame_->View()); | 643 DCHECK(frame_->View()); |
| 480 if (touch_sequence_document_ && | 644 if (touch_sequence_document_ && |
| 481 (!touch_sequence_document_->GetFrame() || | 645 (!touch_sequence_document_->GetFrame() || |
| 482 !touch_sequence_document_->GetFrame()->View())) { | 646 !touch_sequence_document_->GetFrame()->View())) { |
| 483 // If the active touch document has no frame or view, it's probably being | 647 // If the active touch document has no frame or view, it's probably being |
| 484 // destroyed so we can't dispatch events. | 648 // destroyed so we can't dispatch events. |
| 485 return false; | 649 return; |
| 486 } | 650 } |
| 487 | 651 |
| 488 for (unsigned i = 0; i < event.touches_length; ++i) { | 652 // In touch event model only touch starts can set the target and after that |
| 489 // In touch event model only touch starts can set the target and after that | 653 // the touch event always goes to that target. |
| 490 // the touch event always goes to that target. | 654 if (event.GetType() == WebInputEvent::kPointerDown) { |
| 491 if (event.touches[i].state == WebTouchPoint::kStatePressed) { | 655 UpdateTouchAttributeMapsForPointerDown(event, pointer_event_target); |
| 492 UpdateTargetAndRegionMapsForTouchStart(event.TouchPointInRootFrame(i), | 656 } |
| 493 pointer_event_targets[i]); | 657 |
| 658 // We might not receive the down action for a touch point. In that case we | |
| 659 // would have never added them to |touch_attribute_map_| or hit-tested | |
| 660 // them. For those just keep them in the map with a null target. Later they | |
| 661 // will be targeted at the |touch_sequence_document_|. | |
| 662 if (!touch_attribute_map_.Contains(event.id)) { | |
| 663 touch_attribute_map_.insert(event.id, new TouchPointAttributes(event)); | |
| 664 } | |
| 665 | |
| 666 TouchPointAttributes* attributes = touch_attribute_map_.at(event.id); | |
| 667 attributes->event_ = event; | |
| 668 attributes->coalesced_events_ = coalesced_events; | |
| 669 attributes->stale_ = false; | |
| 670 } | |
| 671 | |
| 672 WebInputEventResult TouchEventManager::FlushEvents() { | |
| 673 // If there's no document receiving touch events, or no handlers on the | |
| 674 // document set to receive the events, then we can skip all the rest of | |
| 675 // sending the event. | |
| 676 WebInputEventResult result = WebInputEventResult::kNotHandled; | |
| 677 if (touch_sequence_document_ && touch_sequence_document_->GetPage() && | |
| 678 HasTouchHandlers( | |
| 679 touch_sequence_document_->GetPage()->GetEventHandlerRegistry()) && | |
| 680 touch_sequence_document_->GetFrame() && | |
| 681 touch_sequence_document_->GetFrame()->View()) { | |
| 682 result = DispatchTouchEventFromAccumulatdTouchPoints(); | |
| 683 } | |
| 684 | |
| 685 // Cleanup the |touch_attribute_map_| map from released and canceled | |
| 686 // touch points. | |
| 687 Vector<int> releasedCanceledPoints; | |
| 688 for (auto& attributes : touch_attribute_map_.Values()) { | |
| 689 if (attributes->event_.GetType() == WebInputEvent::kPointerUp || | |
| 690 attributes->event_.GetType() == WebInputEvent::kPointerCancel) { | |
| 691 releasedCanceledPoints.push_back(attributes->event_.id); | |
| 692 } else { | |
| 693 attributes->stale_ = true; | |
| 694 attributes->event_.movement_x = 0; | |
| 695 attributes->event_.movement_y = 0; | |
| 696 attributes->coalesced_events_.clear(); | |
| 494 } | 697 } |
| 495 } | 698 } |
| 699 touch_attribute_map_.RemoveAll(releasedCanceledPoints); | |
| 496 | 700 |
| 497 touch_pressed_ = !all_touches_released; | 701 if (touch_attribute_map_.IsEmpty()) { |
| 498 | 702 allTouchesReleasedCleanup(); |
| 499 // If there's no document receiving touch events, or no handlers on the | |
| 500 // document set to receive the events, then we can skip all the rest of | |
| 501 // this work. | |
| 502 if (!touch_sequence_document_ || !touch_sequence_document_->GetPage() || | |
| 503 !HasTouchHandlers( | |
| 504 touch_sequence_document_->GetPage()->GetEventHandlerRegistry()) || | |
| 505 !touch_sequence_document_->GetFrame()) { | |
| 506 if (all_touches_released) { | |
| 507 touch_sequence_document_.Clear(); | |
| 508 } | |
| 509 return false; | |
| 510 } | 703 } |
| 511 | 704 |
| 512 return true; | 705 return result; |
| 513 } | 706 } |
| 514 | 707 |
| 515 WebInputEventResult TouchEventManager::HandleTouchEvent( | 708 void TouchEventManager::allTouchesReleasedCleanup() { |
| 516 const WebTouchEvent& event, | 709 touch_sequence_document_.Clear(); |
| 517 const Vector<WebTouchEvent>& coalesced_events, | 710 current_touch_action_ = TouchAction::kTouchActionAuto; |
| 518 const HeapVector<EventHandlingUtil::PointerEventTarget>& | 711 last_coalesced_touch_event_ = WebTouchEvent(); |
| 519 pointer_event_targets) { | |
| 520 DCHECK(event.touches_length == pointer_event_targets.size()); | |
| 521 | |
| 522 if (!HitTestTouchPointsIfNeeded(event, pointer_event_targets)) | |
| 523 return WebInputEventResult::kNotHandled; | |
| 524 | |
| 525 bool all_touches_released = true; | |
| 526 for (unsigned i = 0; i < event.touches_length; ++i) { | |
| 527 WebTouchPoint::State state = event.touches[i].state; | |
| 528 if (state != WebTouchPoint::kStateReleased && | |
| 529 state != WebTouchPoint::kStateCancelled) | |
| 530 all_touches_released = false; | |
| 531 } | |
| 532 | |
| 533 return DispatchTouchEvents(event, coalesced_events, all_touches_released); | |
| 534 } | 712 } |
| 535 | 713 |
| 536 bool TouchEventManager::IsAnyTouchActive() const { | 714 bool TouchEventManager::IsAnyTouchActive() const { |
| 537 return touch_pressed_; | 715 return !touch_attribute_map_.IsEmpty(); |
| 538 } | 716 } |
| 539 | 717 |
| 540 } // namespace blink | 718 } // namespace blink |
| OLD | NEW |