Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/renderer_host/input/passthrough_touch_event_queue.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/auto_reset.h" | |
| 10 #include "base/macros.h" | |
| 11 #include "base/memory/ptr_util.h" | |
| 12 #include "base/metrics/histogram_macros.h" | |
| 13 #include "base/trace_event/trace_event.h" | |
| 14 #include "content/browser/renderer_host/input/touch_timeout_handler.h" | |
| 15 #include "content/common/input/web_touch_event_traits.h" | |
| 16 #include "ui/events/base_event_utils.h" | |
| 17 #include "ui/gfx/geometry/point_f.h" | |
|
tdresser
2017/02/23 15:10:45
How thoroughly has this changed from legacy_touch_
dtapuska
2017/02/23 16:52:12
Will comment on the interesting bits.
| |
| 18 | |
| 19 using blink::WebInputEvent; | |
| 20 using blink::WebTouchEvent; | |
| 21 using blink::WebTouchPoint; | |
| 22 using ui::LatencyInfo; | |
| 23 | |
| 24 namespace content { | |
| 25 namespace { | |
| 26 | |
| 27 // Compare all properties of touch points to determine the state. | |
| 28 bool HasPointChanged(const WebTouchPoint& point_1, | |
| 29 const WebTouchPoint& point_2) { | |
| 30 DCHECK_EQ(point_1.id, point_2.id); | |
| 31 if (point_1.screenPosition != point_2.screenPosition || | |
| 32 point_1.position != point_2.position || | |
| 33 point_1.radiusX != point_2.radiusX || | |
| 34 point_1.radiusY != point_2.radiusY || | |
| 35 point_1.rotationAngle != point_2.rotationAngle || | |
| 36 point_1.force != point_2.force || point_1.tiltX != point_2.tiltX || | |
| 37 point_1.tiltY != point_2.tiltY) { | |
| 38 return true; | |
| 39 } | |
| 40 return false; | |
| 41 } | |
| 42 | |
| 43 } // namespace | |
| 44 | |
| 45 PassthroughTouchEventQueue::TouchEventWithLatencyInfoAndAckState:: | |
| 46 TouchEventWithLatencyInfoAndAckState(const TouchEventWithLatencyInfo& event) | |
| 47 : TouchEventWithLatencyInfo(event), | |
| 48 ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {} | |
| 49 | |
| 50 bool PassthroughTouchEventQueue::TouchEventWithLatencyInfoAndAckState:: | |
| 51 operator<(const TouchEventWithLatencyInfoAndAckState& other) const { | |
| 52 return event.uniqueTouchEventId < other.event.uniqueTouchEventId; | |
| 53 } | |
| 54 | |
| 55 PassthroughTouchEventQueue::PassthroughTouchEventQueue( | |
| 56 TouchEventQueueClient* client, | |
| 57 const Config& config) | |
| 58 : client_(client), | |
| 59 has_handlers_(true), | |
| 60 maybe_has_handler_for_current_sequence_(false), | |
| 61 drop_remaining_touches_in_sequence_(false), | |
| 62 send_touch_events_async_(false) { | |
| 63 if (config.touch_ack_timeout_supported) { | |
| 64 timeout_handler_.reset( | |
| 65 new TouchTimeoutHandler(this, config.desktop_touch_ack_timeout_delay, | |
| 66 config.mobile_touch_ack_timeout_delay)); | |
| 67 } | |
| 68 } | |
| 69 | |
| 70 PassthroughTouchEventQueue::~PassthroughTouchEventQueue() {} | |
| 71 | |
| 72 void PassthroughTouchEventQueue::SendTouchCancelEventForTouchEvent( | |
| 73 const TouchEventWithLatencyInfo& event_to_cancel) { | |
| 74 TouchEventWithLatencyInfo event = event_to_cancel; | |
| 75 WebTouchEventTraits::ResetTypeAndTouchStates( | |
| 76 WebInputEvent::TouchCancel, | |
| 77 // TODO(rbyers): Shouldn't we use a fresh timestamp? | |
| 78 event.event.timeStampSeconds(), &event.event); | |
| 79 SendTouchEventImmediately(&event, false); | |
| 80 } | |
| 81 | |
| 82 void PassthroughTouchEventQueue::QueueEvent( | |
| 83 const TouchEventWithLatencyInfo& event) { | |
| 84 TRACE_EVENT0("input", "PassthroughTouchEventQueue::QueueEvent"); | |
| 85 PreFilterResult filter_result = FilterBeforeForwarding(event.event); | |
| 86 if (filter_result != FORWARD_TO_RENDERER) { | |
| 87 client_->OnFilteringTouchEvent(event.event); | |
|
dtapuska
2017/02/23 16:52:12
This is interesting code here. The Filtered touch
| |
| 88 client_->OnTouchEventAck(event, | |
| 89 filter_result == ACK_WITH_NO_CONSUMER_EXISTS | |
| 90 ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS | |
| 91 : INPUT_EVENT_ACK_STATE_NOT_CONSUMED); | |
| 92 return; | |
| 93 } | |
| 94 TouchEventWithLatencyInfo cloned_event(event); | |
| 95 SendTouchEventImmediately(&cloned_event, true); | |
| 96 } | |
| 97 | |
| 98 void PassthroughTouchEventQueue::PrependTouchScrollNotification() { | |
| 99 TRACE_EVENT0("input", | |
| 100 "PassthroughTouchEventQueue::PrependTouchScrollNotification"); | |
| 101 | |
| 102 TouchEventWithLatencyInfo touch( | |
| 103 WebInputEvent::TouchScrollStarted, WebInputEvent::NoModifiers, | |
| 104 ui::EventTimeStampToSeconds(ui::EventTimeForNow()), LatencyInfo()); | |
| 105 touch.event.dispatchType = WebInputEvent::EventNonBlocking; | |
| 106 SendTouchEventImmediately(&touch, true); | |
| 107 } | |
| 108 | |
| 109 void PassthroughTouchEventQueue::ProcessTouchAck( | |
| 110 InputEventAckState ack_result, | |
| 111 const LatencyInfo& latency_info, | |
| 112 const uint32_t unique_touch_event_id) { | |
| 113 TRACE_EVENT0("input", "PassthroughTouchEventQueue::ProcessTouchAck"); | |
| 114 if (timeout_handler_ && | |
| 115 timeout_handler_->ConfirmTouchEvent(unique_touch_event_id, ack_result)) | |
| 116 return; | |
| 117 | |
| 118 auto touch_event_iter = outstanding_touches_.begin(); | |
| 119 while (touch_event_iter != outstanding_touches_.end()) { | |
| 120 if (unique_touch_event_id == touch_event_iter->event.uniqueTouchEventId) | |
| 121 break; | |
| 122 ++touch_event_iter; | |
| 123 } | |
| 124 | |
| 125 if (touch_event_iter == outstanding_touches_.end()) { | |
| 126 NOTREACHED(); | |
| 127 return; | |
| 128 } | |
| 129 | |
| 130 TouchEventWithLatencyInfoAndAckState event = *touch_event_iter; | |
| 131 touch_event_iter = outstanding_touches_.erase(touch_event_iter); | |
| 132 event.latency.AddNewLatencyFrom(latency_info); | |
| 133 event.set_ack_state(ack_result); | |
| 134 outstanding_touches_.insert(touch_event_iter, event); | |
| 135 | |
| 136 AckCompletedEvents(); | |
| 137 } | |
| 138 | |
| 139 void PassthroughTouchEventQueue::OnGestureScrollEvent( | |
| 140 const GestureEventWithLatencyInfo& gesture_event) { | |
| 141 if (gesture_event.event.type() == blink::WebInputEvent::GestureScrollUpdate && | |
| 142 gesture_event.event.resendingPluginId == -1) { | |
| 143 send_touch_events_async_ = true; | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 void PassthroughTouchEventQueue::OnGestureEventAck( | |
| 148 const GestureEventWithLatencyInfo& event, | |
| 149 InputEventAckState ack_result) { | |
| 150 // Turn events sent during gesture scrolls to be async. | |
| 151 if (event.event.type() == blink::WebInputEvent::GestureScrollUpdate && | |
| 152 event.event.resendingPluginId == -1) { | |
| 153 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 void PassthroughTouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { | |
| 158 has_handlers_ = has_handlers; | |
| 159 } | |
| 160 | |
| 161 bool PassthroughTouchEventQueue::IsPendingAckTouchStart() const { | |
| 162 if (outstanding_touches_.empty()) | |
| 163 return false; | |
| 164 | |
| 165 return (outstanding_touches_.begin()->event.type() == | |
| 166 WebInputEvent::TouchStart); | |
| 167 } | |
| 168 | |
| 169 void PassthroughTouchEventQueue::SetAckTimeoutEnabled(bool enabled) { | |
| 170 if (timeout_handler_) | |
| 171 timeout_handler_->SetEnabled(enabled); | |
| 172 } | |
| 173 | |
| 174 void PassthroughTouchEventQueue::SetIsMobileOptimizedSite( | |
| 175 bool mobile_optimized_site) { | |
| 176 if (timeout_handler_) | |
| 177 timeout_handler_->SetUseMobileTimeout(mobile_optimized_site); | |
| 178 } | |
| 179 | |
| 180 bool PassthroughTouchEventQueue::IsAckTimeoutEnabled() const { | |
| 181 return timeout_handler_ && timeout_handler_->IsEnabled(); | |
| 182 } | |
| 183 | |
| 184 bool PassthroughTouchEventQueue::Empty() const { | |
| 185 return outstanding_touches_.empty(); | |
| 186 } | |
| 187 | |
| 188 void PassthroughTouchEventQueue::FlushQueue() { | |
| 189 drop_remaining_touches_in_sequence_ = true; | |
|
dtapuska
2017/02/23 16:52:11
This is interesting
| |
| 190 while (!outstanding_touches_.empty()) { | |
| 191 auto iter = outstanding_touches_.begin(); | |
| 192 TouchEventWithLatencyInfoAndAckState event = *iter; | |
| 193 outstanding_touches_.erase(iter); | |
| 194 if (event.ack_state() == INPUT_EVENT_ACK_STATE_UNKNOWN) | |
| 195 event.set_ack_state(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); | |
| 196 AckTouchEventToClient(event, event.ack_state()); | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 void PassthroughTouchEventQueue::AckCompletedEvents() { | |
|
dtapuska
2017/02/23 16:52:11
Interesting
| |
| 201 while (!outstanding_touches_.empty()) { | |
| 202 auto iter = outstanding_touches_.begin(); | |
| 203 if (iter->ack_state() == INPUT_EVENT_ACK_STATE_UNKNOWN) | |
| 204 break; | |
| 205 TouchEventWithLatencyInfoAndAckState event = *iter; | |
| 206 outstanding_touches_.erase(iter); | |
| 207 AckTouchEventToClient(event, event.ack_state()); | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 void PassthroughTouchEventQueue::AckTouchEventToClient( | |
| 212 const TouchEventWithLatencyInfo& acked_event, | |
| 213 InputEventAckState ack_result) { | |
| 214 UpdateTouchConsumerStates(acked_event.event, ack_result); | |
| 215 | |
| 216 // Skip ack for TouchScrollStarted since it was synthesized within the queue. | |
| 217 if (acked_event.event.type() != WebInputEvent::TouchScrollStarted) { | |
| 218 client_->OnTouchEventAck(acked_event, ack_result); | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 void PassthroughTouchEventQueue::SendTouchEventImmediately( | |
| 223 TouchEventWithLatencyInfo* touch, | |
| 224 bool wait_for_ack) { | |
| 225 // Note: Touchstart events are marked cancelable to allow transitions between | |
| 226 // platform scrolling and JS pinching. Touchend events, however, remain | |
| 227 // uncancelable, mitigating the risk of jank when transitioning to a fling. | |
| 228 if (send_touch_events_async_ && | |
| 229 touch->event.type() != WebInputEvent::TouchStart) | |
| 230 touch->event.dispatchType = WebInputEvent::EventNonBlocking; | |
| 231 | |
| 232 if (touch->event.type() == WebInputEvent::TouchStart) | |
| 233 touch->event.touchStartOrFirstTouchMove = true; | |
| 234 | |
| 235 // For touchmove events, compare touch points position from current event | |
| 236 // to last sent event and update touch points state. | |
| 237 if (touch->event.type() == WebInputEvent::TouchMove) { | |
| 238 CHECK(last_sent_touchevent_); | |
| 239 if (last_sent_touchevent_->type() == WebInputEvent::TouchStart) | |
| 240 touch->event.touchStartOrFirstTouchMove = true; | |
| 241 for (unsigned int i = 0; i < last_sent_touchevent_->touchesLength; ++i) { | |
| 242 const WebTouchPoint& last_touch_point = last_sent_touchevent_->touches[i]; | |
| 243 // Touches with same id may not have same index in Touches array. | |
| 244 for (unsigned int j = 0; j < touch->event.touchesLength; ++j) { | |
| 245 const WebTouchPoint& current_touchmove_point = touch->event.touches[j]; | |
| 246 if (current_touchmove_point.id != last_touch_point.id) | |
| 247 continue; | |
| 248 | |
| 249 if (!HasPointChanged(last_touch_point, current_touchmove_point)) | |
| 250 touch->event.touches[j].state = WebTouchPoint::StateStationary; | |
| 251 | |
| 252 break; | |
| 253 } | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 if (touch->event.type() != WebInputEvent::TouchScrollStarted) { | |
| 258 if (last_sent_touchevent_) | |
| 259 *last_sent_touchevent_ = touch->event; | |
| 260 else | |
| 261 last_sent_touchevent_.reset(new WebTouchEvent(touch->event)); | |
| 262 } | |
| 263 | |
| 264 if (timeout_handler_) | |
| 265 timeout_handler_->StartIfNecessary(*touch); | |
| 266 if (wait_for_ack) | |
| 267 outstanding_touches_.insert(*touch); | |
| 268 client_->SendTouchEventImmediately(*touch); | |
| 269 } | |
| 270 | |
| 271 PassthroughTouchEventQueue::PreFilterResult | |
| 272 PassthroughTouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { | |
| 273 if (event.type() == WebInputEvent::TouchScrollStarted) | |
| 274 return FORWARD_TO_RENDERER; | |
| 275 | |
| 276 if (WebTouchEventTraits::IsTouchSequenceStart(event)) { | |
| 277 // We don't know if we have a handler until we get the ACK back so | |
| 278 // assume it is true. | |
| 279 maybe_has_handler_for_current_sequence_ = true; | |
|
dtapuska
2017/02/23 16:52:11
maybe_ has been added here and inverted.
| |
| 280 send_touch_events_async_ = false; | |
| 281 last_sent_touchevent_.reset(); | |
| 282 | |
| 283 drop_remaining_touches_in_sequence_ = false; | |
| 284 if (!has_handlers_) { | |
| 285 drop_remaining_touches_in_sequence_ = true; | |
| 286 return ACK_WITH_NO_CONSUMER_EXISTS; | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) | |
| 291 return ACK_WITH_NO_CONSUMER_EXISTS; | |
| 292 | |
| 293 if (drop_remaining_touches_in_sequence_ && | |
| 294 event.type() != WebInputEvent::TouchCancel) { | |
| 295 return ACK_WITH_NO_CONSUMER_EXISTS; | |
| 296 } | |
| 297 | |
| 298 if (event.type() == WebInputEvent::TouchStart) { | |
| 299 return (has_handlers_ || maybe_has_handler_for_current_sequence_) | |
| 300 ? FORWARD_TO_RENDERER | |
| 301 : ACK_WITH_NO_CONSUMER_EXISTS; | |
| 302 } | |
| 303 | |
| 304 if (maybe_has_handler_for_current_sequence_) { | |
| 305 // Only forward a touch if it has a non-stationary pointer that is active | |
| 306 // in the current touch sequence. | |
| 307 for (size_t i = 0; i < event.touchesLength; ++i) { | |
| 308 const WebTouchPoint& point = event.touches[i]; | |
| 309 if (point.state == WebTouchPoint::StateStationary) | |
| 310 continue; | |
| 311 | |
| 312 // |last_sent_touchevent_| will be non-null as long as there is an | |
| 313 // active touch sequence being forwarded to the renderer. | |
| 314 if (!last_sent_touchevent_) | |
| 315 continue; | |
| 316 | |
| 317 for (size_t j = 0; j < last_sent_touchevent_->touchesLength; ++j) { | |
| 318 if (point.id != last_sent_touchevent_->touches[j].id) | |
| 319 continue; | |
| 320 | |
| 321 if (event.type() != WebInputEvent::TouchMove) | |
| 322 return FORWARD_TO_RENDERER; | |
| 323 | |
| 324 // All pointers in TouchMove events may have state as StateMoved, | |
| 325 // even though none of the pointers have not changed in real. | |
| 326 // Forward these events when at least one pointer has changed. | |
| 327 if (HasPointChanged(last_sent_touchevent_->touches[j], point)) | |
| 328 return FORWARD_TO_RENDERER; | |
| 329 | |
| 330 // This is a TouchMove event for which we have yet to find a | |
| 331 // non-stationary pointer. Continue checking the next pointers | |
| 332 // in the |event|. | |
| 333 break; | |
| 334 } | |
| 335 } | |
| 336 } | |
| 337 | |
| 338 return ACK_WITH_NO_CONSUMER_EXISTS; | |
| 339 } | |
| 340 | |
| 341 void PassthroughTouchEventQueue::UpdateTouchConsumerStates( | |
| 342 const WebTouchEvent& event, | |
| 343 InputEventAckState ack_result) { | |
| 344 if (event.type() == WebInputEvent::TouchStart) { | |
| 345 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) | |
| 346 send_touch_events_async_ = false; | |
| 347 | |
| 348 // Once we have the ack back for the sequence we know if there | |
|
dtapuska
2017/02/23 16:52:12
This is an interesting block.
| |
| 349 // is a handler or not. Other touch-starts sent can upgrade | |
| 350 // whether we have a handler or not as well. | |
| 351 if (WebTouchEventTraits::IsTouchSequenceStart(event)) { | |
| 352 maybe_has_handler_for_current_sequence_ = | |
| 353 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; | |
| 354 } else { | |
| 355 maybe_has_handler_for_current_sequence_ |= | |
| 356 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; | |
| 357 } | |
| 358 } else if (WebTouchEventTraits::IsTouchSequenceEnd(event)) { | |
| 359 maybe_has_handler_for_current_sequence_ = false; | |
| 360 } | |
| 361 } | |
| 362 | |
| 363 size_t PassthroughTouchEventQueue::SizeForTesting() const { | |
| 364 return outstanding_touches_.size(); | |
| 365 } | |
| 366 | |
| 367 bool PassthroughTouchEventQueue::IsTimeoutRunningForTesting() const { | |
| 368 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); | |
| 369 } | |
| 370 | |
| 371 const TouchEventWithLatencyInfo& | |
| 372 PassthroughTouchEventQueue::GetLatestEventForTesting() const { | |
| 373 return *outstanding_touches_.rbegin(); | |
| 374 } | |
| 375 | |
| 376 } // namespace content | |
| OLD | NEW |