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 // TODO(crbug.com/732842): Note that event sender API allows sending any | |
298 // mix of input and as long as we don't crash or anything we should be good | |
299 // for now. | |
300 if (touch_event_type == WebInputEvent::kTouchMove) { | |
301 if (touch_pointer_event.GetType() == WebInputEvent::kPointerDown) | |
302 touch_event_type = WebInputEvent::kTouchStart; | |
303 else if (touch_pointer_event.GetType() == WebInputEvent::kPointerCancel) | |
304 touch_event_type = WebInputEvent::kTouchCancel; | |
305 else if (touch_pointer_event.GetType() == WebInputEvent::kPointerUp) | |
306 touch_event_type = WebInputEvent::kTouchEnd; | |
307 } | |
308 | |
309 for (const WebPointerEvent& coalesced_event : | |
310 touch_point_attribute->coalesced_events_) { | |
311 all_coalesced_events.push_back(coalesced_event); | |
312 } | |
313 } | |
314 event.SetType(touch_event_type); | |
315 last_coalesced_touch_event_.SetType(touch_event_type); | |
316 | |
317 // Create all coalesced touch events based on pointerevents | |
318 struct { | |
319 bool operator()(const WebPointerEvent& a, const WebPointerEvent& b) { | |
320 return a.TimeStampSeconds() < b.TimeStampSeconds(); | |
321 } | |
322 } timestamp_based_event_comparison; | |
323 std::sort(all_coalesced_events.begin(), all_coalesced_events.end(), | |
324 timestamp_based_event_comparison); | |
325 WebCoalescedInputEvent result(event, std::vector<const WebInputEvent*>()); | |
326 for (const auto& web_pointer_event : all_coalesced_events) { | |
327 if (web_pointer_event.GetType() == WebInputEvent::kPointerDown) { | |
328 // TODO(crbug.com/732842): Technically we should never receive the | |
329 // pointerdown twice for the same touch point. But event sender API allows | |
330 // that. So we should handle it gracefully. | |
331 WebTouchPoint web_touch_point(web_pointer_event); | |
332 bool found_existing_id = false; | |
333 for (unsigned i = 0; i < last_coalesced_touch_event_.touches_length; | |
334 ++i) { | |
335 if (last_coalesced_touch_event_.touches[i].id == web_pointer_event.id) { | |
336 last_coalesced_touch_event_.touches[i] = | |
337 CreateWebTouchPointFromWebPointerEvent(web_pointer_event, false); | |
338 found_existing_id = true; | |
339 break; | |
340 } | |
341 } | |
342 // If the pointerdown point didn't exist add a new point to the array. | |
343 if (!found_existing_id) { | |
344 last_coalesced_touch_event_ | |
345 .touches[last_coalesced_touch_event_.touches_length++] = | |
346 CreateWebTouchPointFromWebPointerEvent(web_pointer_event, false); | |
347 } | |
348 struct { | |
349 bool operator()(const WebTouchPoint& a, const WebTouchPoint& b) { | |
350 return a.id < b.id; | |
351 } | |
352 } id_based_event_comparison; | |
353 std::sort(last_coalesced_touch_event_.touches, | |
354 last_coalesced_touch_event_.touches + | |
355 last_coalesced_touch_event_.touches_length, | |
356 id_based_event_comparison); | |
357 result.AddCoalescedEvent(last_coalesced_touch_event_); | |
358 } else { | |
359 for (unsigned i = 0; i < last_coalesced_touch_event_.touches_length; | |
360 ++i) { | |
361 if (last_coalesced_touch_event_.touches[i].id == web_pointer_event.id) { | |
362 last_coalesced_touch_event_.touches[i].movement_x = | |
jkwang
2017/07/10 23:49:39
Why not call "CreateWebTouchPointFromWebPointerEve
Navid Zolghadr
2017/07/11 02:21:28
I cannot think of any reason not to. Calling "Crea
| |
363 web_pointer_event.movement_x; | |
364 last_coalesced_touch_event_.touches[i].movement_y = | |
365 web_pointer_event.movement_y; | |
366 last_coalesced_touch_event_.SetTimeStampSeconds( | |
367 web_pointer_event.TimeStampSeconds()); | |
368 last_coalesced_touch_event_.touches[i].state = | |
369 TouchPointStateFromPointerEventType(web_pointer_event.GetType(), | |
370 false); | |
371 result.AddCoalescedEvent(last_coalesced_touch_event_); | |
372 | |
373 // Remove up and canceled points. | |
374 unsigned result_size = 0; | |
375 for (unsigned j = 0; j < last_coalesced_touch_event_.touches_length; | |
376 j++) { | |
377 if (last_coalesced_touch_event_.touches[j].state != | |
378 WebTouchPoint::kStateCancelled && | |
379 last_coalesced_touch_event_.touches[j].state != | |
380 WebTouchPoint::kStateReleased) { | |
381 last_coalesced_touch_event_.touches[result_size++] = | |
382 last_coalesced_touch_event_.touches[j]; | |
383 } | |
384 } | |
385 last_coalesced_touch_event_.touches_length = result_size; | |
386 break; | |
387 } | |
388 } | |
389 } | |
390 | |
391 for (unsigned i = 0; i < event.touches_length; ++i) { | |
392 event.touches[i].state = blink::WebTouchPoint::kStateStationary; | |
393 event.touches[i].movement_x = 0; | |
394 event.touches[i].movement_y = 0; | |
395 } | |
396 } | |
397 | |
398 return result; | |
399 } | |
400 | |
401 WebInputEventResult | |
402 TouchEventManager::DispatchTouchEventFromAccumulatdTouchPoints() { | |
190 // Build up the lists to use for the |touches|, |targetTouches| and | 403 // Build up the lists to use for the |touches|, |targetTouches| and |
191 // |changedTouches| attributes in the JS event. See | 404 // |changedTouches| attributes in the JS event. See |
192 // http://www.w3.org/TR/touch-events/#touchevent-interface for how these | 405 // http://www.w3.org/TR/touch-events/#touchevent-interface for how these |
193 // lists fit together. | 406 // lists fit together. |
194 | 407 |
195 if (event.GetType() == WebInputEvent::kTouchEnd || | 408 bool new_touch_point_since_last_dispatch = false; |
196 event.GetType() == WebInputEvent::kTouchCancel || | 409 bool any_touch_canceled_or_ended = false; |
197 event.touches_length > 1) { | 410 bool all_touch_points_pressed = true; |
198 suppressing_touchmoves_within_slop_ = false; | 411 |
412 for (const auto& attr : touch_attribute_map_.Values()) { | |
413 if (!attr->stale_) | |
414 new_touch_point_since_last_dispatch = true; | |
415 if (attr->event_.GetType() == WebInputEvent::kPointerUp || | |
416 attr->event_.GetType() == WebInputEvent::kPointerCancel) | |
417 any_touch_canceled_or_ended = true; | |
418 if (attr->event_.GetType() != WebInputEvent::kPointerDown) | |
419 all_touch_points_pressed = false; | |
199 } | 420 } |
200 | 421 |
201 if (suppressing_touchmoves_within_slop_ && | 422 if (!new_touch_point_since_last_dispatch) |
202 event.GetType() == WebInputEvent::kTouchMove) { | 423 return WebInputEventResult::kNotHandled; |
203 if (!event.moved_beyond_slop_region) | 424 |
204 return WebInputEventResult::kHandledSuppressed; | 425 if (any_touch_canceled_or_ended || touch_attribute_map_.size() > 1) |
205 suppressing_touchmoves_within_slop_ = false; | 426 suppressing_touchmoves_within_slop_ = false; |
427 | |
428 if (suppressing_touchmoves_within_slop_) { | |
429 // There is exactly one touch point here otherwise | |
430 // |suppressing_touchmoves_within_slop_| would have been false. | |
431 DCHECK_EQ(1U, touch_attribute_map_.size()); | |
432 const auto& touch_point_attribute = touch_attribute_map_.begin()->value; | |
433 if (touch_point_attribute->event_.GetType() == | |
434 WebInputEvent::kPointerMove) { | |
435 if (!touch_point_attribute->event_.moved_beyond_slop_region) | |
436 return WebInputEventResult::kHandledSuppressed; | |
437 suppressing_touchmoves_within_slop_ = false; | |
438 } | |
206 } | 439 } |
207 | 440 |
208 // Holds the complete set of touches on the screen. | 441 // Holds the complete set of touches on the screen. |
209 TouchList* touches = TouchList::Create(); | 442 TouchList* touches = TouchList::Create(); |
210 | 443 |
211 // A different view on the 'touches' list above, filtered and grouped by | 444 // A different view on the 'touches' list above, filtered and grouped by |
212 // event target. Used for the |targetTouches| list in the JS event. | 445 // event target. Used for the |targetTouches| list in the JS event. |
213 using TargetTouchesHeapMap = HeapHashMap<EventTarget*, Member<TouchList>>; | 446 using TargetTouchesHeapMap = HeapHashMap<EventTarget*, Member<TouchList>>; |
214 TargetTouchesHeapMap touches_by_target; | 447 TargetTouchesHeapMap touches_by_target; |
215 | 448 |
216 // Array of touches per state, used to assemble the |changedTouches| list. | 449 // Array of touches per state, used to assemble the |changedTouches| list. |
217 ChangedTouches changed_touches[WebTouchPoint::kStateMax + 1]; | 450 ChangedTouches changed_touches[WebInputEvent::kPointerTypeLast - |
451 WebInputEvent::kPointerTypeFirst + 1]; | |
218 | 452 |
219 for (unsigned touch_point_idx = 0; touch_point_idx < event.touches_length; | 453 Vector<int> available_ids; |
220 touch_point_idx++) { | 454 for (const auto& id : touch_attribute_map_.Keys()) |
221 const WebTouchPoint& point = event.TouchPointInRootFrame(touch_point_idx); | 455 available_ids.push_back(id); |
222 WebTouchPoint::State point_state = point.state; | 456 std::sort(available_ids.begin(), available_ids.end()); |
457 for (const int& touch_point_id : available_ids) { | |
458 const auto& touch_point_attribute = touch_attribute_map_.at(touch_point_id); | |
459 WebInputEvent::Type event_type = touch_point_attribute->event_.GetType(); | |
223 bool known_target; | 460 bool known_target; |
224 | 461 |
225 Touch* touch = CreateDomTouch(point, &known_target); | 462 Touch* touch = CreateDomTouch(touch_point_attribute, &known_target); |
226 EventTarget* touch_target = touch->target(); | 463 EventTarget* touch_target = touch->target(); |
227 | 464 |
228 // Ensure this target's touch list exists, even if it ends up empty, so | 465 // Ensure this target's touch list exists, even if it ends up empty, so |
229 // it can always be passed to TouchEvent::Create below. | 466 // it can always be passed to TouchEvent::Create below. |
230 TargetTouchesHeapMap::iterator target_touches_iterator = | 467 TargetTouchesHeapMap::iterator target_touches_iterator = |
231 touches_by_target.find(touch_target); | 468 touches_by_target.find(touch_target); |
232 if (target_touches_iterator == touches_by_target.end()) { | 469 if (target_touches_iterator == touches_by_target.end()) { |
233 touches_by_target.Set(touch_target, TouchList::Create()); | 470 touches_by_target.Set(touch_target, TouchList::Create()); |
234 target_touches_iterator = touches_by_target.find(touch_target); | 471 target_touches_iterator = touches_by_target.find(touch_target); |
235 } | 472 } |
236 | 473 |
237 // |touches| and |targetTouches| should only contain information about | 474 // |touches| and |targetTouches| should only contain information about |
238 // touches still on the screen, so if this point is released or | 475 // touches still on the screen, so if this point is released or |
239 // cancelled it will only appear in the |changedTouches| list. | 476 // cancelled it will only appear in the |changedTouches| list. |
240 if (point_state != WebTouchPoint::kStateReleased && | 477 if (event_type != WebInputEvent::kPointerUp && |
241 point_state != WebTouchPoint::kStateCancelled) { | 478 event_type != WebInputEvent::kPointerCancel) { |
242 touches->Append(touch); | 479 touches->Append(touch); |
243 target_touches_iterator->value->Append(touch); | 480 target_touches_iterator->value->Append(touch); |
244 } | 481 } |
245 | 482 |
246 // Now build up the correct list for |changedTouches|. | 483 // Now build up the correct list for |changedTouches|. |
247 // Note that any touches that are in the TouchStationary state (e.g. if | 484 // 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 | 485 // 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 | 486 // 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 | 487 // explicitly here. See https://bugs.webkit.org/show_bug.cgi?id=37609 |
251 // for further discussion about the TouchStationary state. | 488 // for further discussion about the TouchStationary state. |
252 if (point_state != WebTouchPoint::kStateStationary && known_target) { | 489 if (!touch_point_attribute->stale_ && known_target) { |
253 DCHECK_LE(point_state, WebTouchPoint::kStateMax); | 490 size_t event_type_idx = event_type - WebInputEvent::kPointerTypeFirst; |
254 if (!changed_touches[point_state].touches_) | 491 if (!changed_touches[event_type_idx].touches_) |
255 changed_touches[point_state].touches_ = TouchList::Create(); | 492 changed_touches[event_type_idx].touches_ = TouchList::Create(); |
256 changed_touches[point_state].touches_->Append(touch); | 493 changed_touches[event_type_idx].touches_->Append(touch); |
257 changed_touches[point_state].targets_.insert(touch_target); | 494 changed_touches[event_type_idx].targets_.insert(touch_target); |
258 changed_touches[point_state].pointer_type_ = point.pointer_type; | |
259 } | 495 } |
260 } | 496 } |
261 | 497 |
262 if (all_touches_released) { | 498 WebInputEventResult event_result = WebInputEventResult::kNotHandled; |
263 touch_sequence_document_.Clear(); | |
264 current_touch_action_ = TouchAction::kTouchActionAuto; | |
265 } | |
266 | 499 |
267 WebInputEventResult event_result = WebInputEventResult::kNotHandled; | 500 // First we construct the webcoalescedinputevent containing all the coalesced |
268 // First we construct the webcoalescedinputevent contains all the coalesced | |
269 // touch event. | 501 // touch event. |
270 std::vector<const WebInputEvent*> coalesced_touches; | 502 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 | 503 |
276 // Now iterate through the |changedTouches| list and |m_targets| within it, | 504 // Now iterate through the |changedTouches| list and |m_targets| within it, |
277 // sending TouchEvents to the targets as required. | 505 // sending TouchEvents to the targets as required. |
278 for (unsigned state = 0; state <= WebTouchPoint::kStateMax; ++state) { | 506 for (unsigned action = WebInputEvent::kPointerTypeFirst; |
279 if (!changed_touches[state].touches_) | 507 action <= WebInputEvent::kPointerTypeLast; ++action) { |
508 size_t action_idx = action - WebInputEvent::kPointerTypeFirst; | |
509 if (!changed_touches[action_idx].touches_) | |
280 continue; | 510 continue; |
281 | 511 |
282 const AtomicString& event_name(TouchEventNameForTouchPointState( | 512 const AtomicString& event_name(TouchEventNameForPointerEventType( |
283 static_cast<WebTouchPoint::State>(state))); | 513 static_cast<WebInputEvent::Type>(action))); |
284 for (const auto& event_target : changed_touches[state].targets_) { | 514 |
515 for (const auto& event_target : changed_touches[action_idx].targets_) { | |
285 EventTarget* touch_event_target = event_target; | 516 EventTarget* touch_event_target = event_target; |
286 TouchEvent* touch_event = TouchEvent::Create( | 517 TouchEvent* touch_event = TouchEvent::Create( |
287 coalesced_event, touches, touches_by_target.at(touch_event_target), | 518 coalesced_event, touches, touches_by_target.at(touch_event_target), |
288 changed_touches[state].touches_.Get(), event_name, | 519 changed_touches[action_idx].touches_.Get(), event_name, |
289 touch_event_target->ToNode()->GetDocument().domWindow(), | 520 touch_event_target->ToNode()->GetDocument().domWindow(), |
290 current_touch_action_); | 521 current_touch_action_); |
291 | 522 |
292 DispatchEventResult dom_dispatch_result = | 523 DispatchEventResult dom_dispatch_result = |
293 touch_event_target->DispatchEvent(touch_event); | 524 touch_event_target->DispatchEvent(touch_event); |
294 | 525 |
295 // Only report for top level documents with a single touch on | 526 // Only report for top level documents with a single touch on |
296 // touch-start or the first touch-move. | 527 // touch-start or the first touch-move. |
297 if (event.touch_start_or_first_touch_move && event.touches_length == 1 && | 528 if (touch_attribute_map_.size() == 1 && frame_->IsMainFrame()) { |
298 frame_->IsMainFrame()) { | 529 const auto& event = touch_attribute_map_.begin()->value->event_; |
299 // Record the disposition and latency of touch starts and first touch | 530 if (event.touch_start_or_first_touch_move) { |
300 // moves before and after the page is fully loaded respectively. | 531 // Record the disposition and latency of touch starts and first touch |
301 int64_t latency_in_micros = | 532 // moves before and after the page is fully loaded respectively. |
302 (TimeTicks::Now() - | 533 ReportMetricsForTouch( |
303 TimeTicks::FromSeconds(event.TimeStampSeconds())) | 534 event, dom_dispatch_result, |
304 .InMicroseconds(); | 535 touch_event->PreventDefaultCalledOnUncancelableEvent(), |
305 if (event.IsCancelable()) { | 536 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 } | 537 } |
358 } | 538 } |
359 event_result = EventHandlingUtil::MergeEventResult( | 539 event_result = EventHandlingUtil::MergeEventResult( |
360 event_result, | 540 event_result, |
361 EventHandlingUtil::ToWebInputEventResult(dom_dispatch_result)); | 541 EventHandlingUtil::ToWebInputEventResult(dom_dispatch_result)); |
362 } | 542 } |
363 } | 543 } |
364 | 544 |
365 // Do not suppress any touchmoves if the touchstart is consumed. | 545 // Suppress following touchmoves within the slop region if the touchstart is |
366 if (IsTouchSequenceStart(event) && | 546 // not consumed. |
547 if (all_touch_points_pressed && | |
367 event_result == WebInputEventResult::kNotHandled) { | 548 event_result == WebInputEventResult::kNotHandled) { |
368 suppressing_touchmoves_within_slop_ = true; | 549 suppressing_touchmoves_within_slop_ = true; |
369 } | 550 } |
370 | 551 |
371 return event_result; | 552 return event_result; |
372 } | 553 } |
373 | 554 |
374 void TouchEventManager::UpdateTargetAndRegionMapsForTouchStart( | 555 void TouchEventManager::UpdateTouchAttributeMapsForPointerDown( |
375 const WebTouchPoint& touch_point, | 556 const WebPointerEvent& event, |
376 const EventHandlingUtil::PointerEventTarget& pointer_event_target) { | 557 const EventHandlingUtil::PointerEventTarget& pointer_event_target) { |
377 // Touch events implicitly capture to the touched node, and don't change | 558 // Touch events implicitly capture to the touched node, and don't change |
378 // active/hover states themselves (Gesture events do). So we only need | 559 // 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 | 560 // to hit-test on touchstart and when the target could be different than |
380 // the corresponding pointer event target. | 561 // the corresponding pointer event target. |
381 DCHECK(touch_point.state == WebTouchPoint::kStatePressed); | 562 DCHECK(event.GetType() == WebInputEvent::kPointerDown); |
563 // Ideally we'd DCHECK(!touch_attribute_map_.Contains(event.id)) | |
564 // since we shouldn't get a touchstart for a touch that's already | |
565 // down. However EventSender allows this to be violated and there's | |
566 // some tests that take advantage of it. There may also be edge | |
567 // cases in the browser where this happens. | |
568 // See http://crbug.com/345372. | |
569 touch_attribute_map_.Set(event.id, new TouchPointAttributes(event)); | |
570 | |
382 Node* touch_node = pointer_event_target.target_node; | 571 Node* touch_node = pointer_event_target.target_node; |
383 String region = pointer_event_target.region; | 572 String region = pointer_event_target.region; |
384 | 573 |
385 HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kTouchEvent | | 574 HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kTouchEvent | |
386 HitTestRequest::kReadOnly | | 575 HitTestRequest::kReadOnly | |
387 HitTestRequest::kActive; | 576 HitTestRequest::kActive; |
388 HitTestResult result; | 577 HitTestResult result; |
389 // For the touchPressed points hit-testing is done in | 578 // For the touchPressed points hit-testing is done in |
390 // PointerEventManager. If it was the second touch there is a | 579 // PointerEventManager. If it was the second touch there is a |
391 // capturing documents for the touch and |m_touchSequenceDocument| | 580 // capturing documents for the touch and |m_touchSequenceDocument| |
392 // is not null. So if PointerEventManager should hit-test again | 581 // is not null. So if PointerEventManager should hit-test again |
393 // against |m_touchSequenceDocument| if the target set by | 582 // against |m_touchSequenceDocument| if the target set by |
394 // PointerEventManager was either null or not in | 583 // PointerEventManager was either null or not in |
395 // |m_touchSequenceDocument|. | 584 // |m_touchSequenceDocument|. |
396 if (touch_sequence_document_ && | 585 if (touch_sequence_document_ && |
397 (!touch_node || &touch_node->GetDocument() != touch_sequence_document_)) { | 586 (!touch_node || &touch_node->GetDocument() != touch_sequence_document_)) { |
398 if (touch_sequence_document_->GetFrame()) { | 587 if (touch_sequence_document_->GetFrame()) { |
399 LayoutPoint frame_point = LayoutPoint( | 588 LayoutPoint frame_point = LayoutPoint( |
400 touch_sequence_document_->GetFrame()->View()->RootFrameToContents( | 589 touch_sequence_document_->GetFrame()->View()->RootFrameToContents( |
401 touch_point.PositionInWidget())); | 590 event.PositionInWidget())); |
402 result = EventHandlingUtil::HitTestResultInFrame( | 591 result = EventHandlingUtil::HitTestResultInFrame( |
403 touch_sequence_document_->GetFrame(), frame_point, hit_type); | 592 touch_sequence_document_->GetFrame(), frame_point, hit_type); |
404 Node* node = result.InnerNode(); | 593 Node* node = result.InnerNode(); |
405 if (!node) | 594 if (!node) |
406 return; | 595 return; |
407 if (isHTMLCanvasElement(node)) { | 596 if (isHTMLCanvasElement(node)) { |
408 HitTestCanvasResult* hit_test_canvas_result = | 597 HitTestCanvasResult* hit_test_canvas_result = |
409 toHTMLCanvasElement(node)->GetControlAndIdIfHitRegionExists( | 598 toHTMLCanvasElement(node)->GetControlAndIdIfHitRegionExists( |
410 result.PointInInnerNodeFrame()); | 599 result.PointInInnerNodeFrame()); |
411 if (hit_test_canvas_result->GetControl()) | 600 if (hit_test_canvas_result->GetControl()) |
(...skipping 11 matching lines...) Expand all Loading... | |
423 if (!touch_node) | 612 if (!touch_node) |
424 return; | 613 return; |
425 if (!touch_sequence_document_) { | 614 if (!touch_sequence_document_) { |
426 // Keep track of which document should receive all touch events | 615 // Keep track of which document should receive all touch events |
427 // in the active sequence. This must be a single document to | 616 // in the active sequence. This must be a single document to |
428 // ensure we don't leak Nodes between documents. | 617 // ensure we don't leak Nodes between documents. |
429 touch_sequence_document_ = &(touch_node->GetDocument()); | 618 touch_sequence_document_ = &(touch_node->GetDocument()); |
430 DCHECK(touch_sequence_document_->GetFrame()->View()); | 619 DCHECK(touch_sequence_document_->GetFrame()->View()); |
431 } | 620 } |
432 | 621 |
433 // Ideally we'd DCHECK(!m_targetForTouchID.contains(point.id()) | 622 TouchPointAttributes* attributes = touch_attribute_map_.at(event.id); |
434 // since we shouldn't get a touchstart for a touch that's already | 623 attributes->target_ = touch_node; |
435 // down. However EventSender allows this to be violated and there's | 624 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 | 625 |
443 TouchAction effective_touch_action = | 626 TouchAction effective_touch_action = |
444 TouchActionUtil::ComputeEffectiveTouchAction(*touch_node); | 627 TouchActionUtil::ComputeEffectiveTouchAction(*touch_node); |
445 if (effective_touch_action != TouchAction::kTouchActionAuto) { | 628 if (effective_touch_action != TouchAction::kTouchActionAuto) { |
446 frame_->GetPage()->GetChromeClient().SetTouchAction(frame_, | 629 frame_->GetPage()->GetChromeClient().SetTouchAction(frame_, |
447 effective_touch_action); | 630 effective_touch_action); |
448 | 631 |
449 // Combine the current touch action sequence with the touch action | 632 // Combine the current touch action sequence with the touch action |
450 // for the current finger press. | 633 // for the current finger press. |
451 current_touch_action_ &= effective_touch_action; | 634 current_touch_action_ &= effective_touch_action; |
452 } | 635 } |
453 } | 636 } |
454 | 637 |
455 bool TouchEventManager::HitTestTouchPointsIfNeeded( | 638 void TouchEventManager::HandleTouchPoint( |
456 const WebTouchEvent& event, | 639 const WebPointerEvent& event, |
457 const HeapVector<EventHandlingUtil::PointerEventTarget>& | 640 const Vector<WebPointerEvent>& coalesced_events, |
458 pointer_event_targets) { | 641 const EventHandlingUtil::PointerEventTarget& pointer_event_target) { |
459 bool new_touch_sequence = true; | 642 DCHECK_GE(event.GetType(), WebInputEvent::kPointerTypeFirst); |
460 bool all_touches_released = true; | 643 DCHECK_LE(event.GetType(), WebInputEvent::kPointerTypeLast); |
461 | 644 |
462 for (unsigned i = 0; i < event.touches_length; ++i) { | 645 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 | 646 // Ideally we'd DCHECK(!m_touchSequenceDocument) here since we should |
472 // have cleared the active document when we saw the last release. But we | 647 // 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 | 648 // have some tests that violate this, ClusterFuzz could trigger it, and |
474 // there may be cases where the browser doesn't reliably release all | 649 // there may be cases where the browser doesn't reliably release all |
475 // touches. http://crbug.com/345372 tracks this. | 650 // touches. http://crbug.com/345372 tracks this. |
476 touch_sequence_document_.Clear(); | 651 AllTouchesReleasedCleanup(); |
477 } | 652 } |
478 | 653 |
479 DCHECK(frame_->View()); | 654 DCHECK(frame_->View()); |
480 if (touch_sequence_document_ && | 655 if (touch_sequence_document_ && |
481 (!touch_sequence_document_->GetFrame() || | 656 (!touch_sequence_document_->GetFrame() || |
482 !touch_sequence_document_->GetFrame()->View())) { | 657 !touch_sequence_document_->GetFrame()->View())) { |
483 // If the active touch document has no frame or view, it's probably being | 658 // If the active touch document has no frame or view, it's probably being |
484 // destroyed so we can't dispatch events. | 659 // destroyed so we can't dispatch events. |
485 return false; | 660 return; |
486 } | 661 } |
487 | 662 |
488 for (unsigned i = 0; i < event.touches_length; ++i) { | 663 // 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 | 664 // the touch event always goes to that target. |
490 // the touch event always goes to that target. | 665 if (event.GetType() == WebInputEvent::kPointerDown) { |
491 if (event.touches[i].state == WebTouchPoint::kStatePressed) { | 666 UpdateTouchAttributeMapsForPointerDown(event, pointer_event_target); |
492 UpdateTargetAndRegionMapsForTouchStart(event.TouchPointInRootFrame(i), | |
493 pointer_event_targets[i]); | |
494 } | |
495 } | 667 } |
496 | 668 |
497 touch_pressed_ = !all_touches_released; | 669 // We might not receive the down action for a touch point. In that case we |
670 // would have never added them to |touch_attribute_map_| or hit-tested | |
671 // them. For those just keep them in the map with a null target. Later they | |
672 // will be targeted at the |touch_sequence_document_|. | |
673 if (!touch_attribute_map_.Contains(event.id)) { | |
674 touch_attribute_map_.insert(event.id, new TouchPointAttributes(event)); | |
675 } | |
676 | |
677 TouchPointAttributes* attributes = touch_attribute_map_.at(event.id); | |
678 attributes->event_ = event; | |
679 attributes->coalesced_events_ = coalesced_events; | |
680 attributes->stale_ = false; | |
681 } | |
682 | |
683 WebInputEventResult TouchEventManager::FlushEvents() { | |
684 WebInputEventResult result = WebInputEventResult::kNotHandled; | |
498 | 685 |
499 // If there's no document receiving touch events, or no handlers on the | 686 // 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 | 687 // document set to receive the events, then we can skip all the rest of |
501 // this work. | 688 // sending the event. |
502 if (!touch_sequence_document_ || !touch_sequence_document_->GetPage() || | 689 if (touch_sequence_document_ && touch_sequence_document_->GetPage() && |
503 !HasTouchHandlers( | 690 HasTouchHandlers( |
504 touch_sequence_document_->GetPage()->GetEventHandlerRegistry()) || | 691 touch_sequence_document_->GetPage()->GetEventHandlerRegistry()) && |
505 !touch_sequence_document_->GetFrame()) { | 692 touch_sequence_document_->GetFrame() && |
506 if (all_touches_released) { | 693 touch_sequence_document_->GetFrame()->View()) { |
507 touch_sequence_document_.Clear(); | 694 result = DispatchTouchEventFromAccumulatdTouchPoints(); |
508 } | |
509 return false; | |
510 } | 695 } |
511 | 696 |
512 return true; | 697 // Cleanup the |touch_attribute_map_| map from released and canceled |
698 // touch points. | |
699 Vector<int> released_canceled_points; | |
700 for (auto& attributes : touch_attribute_map_.Values()) { | |
701 if (attributes->event_.GetType() == WebInputEvent::kPointerUp || | |
702 attributes->event_.GetType() == WebInputEvent::kPointerCancel) { | |
703 released_canceled_points.push_back(attributes->event_.id); | |
704 } else { | |
705 attributes->stale_ = true; | |
706 attributes->event_.movement_x = 0; | |
707 attributes->event_.movement_y = 0; | |
708 attributes->coalesced_events_.clear(); | |
709 } | |
710 } | |
711 touch_attribute_map_.RemoveAll(released_canceled_points); | |
712 | |
713 if (touch_attribute_map_.IsEmpty()) { | |
714 AllTouchesReleasedCleanup(); | |
715 } | |
716 | |
717 return result; | |
513 } | 718 } |
514 | 719 |
515 WebInputEventResult TouchEventManager::HandleTouchEvent( | 720 void TouchEventManager::AllTouchesReleasedCleanup() { |
516 const WebTouchEvent& event, | 721 touch_sequence_document_.Clear(); |
517 const Vector<WebTouchEvent>& coalesced_events, | 722 current_touch_action_ = TouchAction::kTouchActionAuto; |
518 const HeapVector<EventHandlingUtil::PointerEventTarget>& | 723 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 } | 724 } |
535 | 725 |
536 bool TouchEventManager::IsAnyTouchActive() const { | 726 bool TouchEventManager::IsAnyTouchActive() const { |
537 return touch_pressed_; | 727 return !touch_attribute_map_.IsEmpty(); |
538 } | 728 } |
539 | 729 |
540 } // namespace blink | 730 } // namespace blink |
OLD | NEW |