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 "content/renderer/input/main_thread_event_queue.h" | 5 #include "content/renderer/input/main_thread_event_queue.h" |
6 | 6 |
7 #include "base/metrics/field_trial.h" | 7 #include "base/metrics/field_trial.h" |
8 #include "base/metrics/histogram_macros.h" | 8 #include "base/metrics/histogram_macros.h" |
9 #include "base/strings/string_number_conversions.h" | 9 #include "base/strings/string_number_conversions.h" |
10 #include "content/common/input/event_with_latency_info.h" | 10 #include "content/common/input/event_with_latency_info.h" |
11 #include "content/common/input_messages.h" | 11 #include "content/common/input_messages.h" |
| 12 #include "content/renderer/render_widget.h" |
12 | 13 |
13 namespace content { | 14 namespace content { |
14 | 15 |
15 namespace { | 16 namespace { |
16 | 17 |
17 const size_t kTenSeconds = 10 * 1000 * 1000; | 18 const size_t kTenSeconds = 10 * 1000 * 1000; |
18 | 19 |
19 class QueuedClosure : public MainThreadEventQueueTask { | 20 class QueuedClosure : public MainThreadEventQueueTask { |
20 public: | 21 public: |
21 QueuedClosure(const base::Closure& closure) : closure_(closure) {} | 22 QueuedClosure(const base::Closure& closure) : closure_(closure) {} |
22 | 23 |
23 ~QueuedClosure() override {} | 24 ~QueuedClosure() override {} |
24 | 25 |
25 FilterResult FilterNewEvent( | 26 FilterResult FilterNewEvent( |
26 const MainThreadEventQueueTask& other_task) override { | 27 const MainThreadEventQueueTask& other_task) override { |
27 return other_task.IsWebInputEvent() ? FilterResult::KeepIterating | 28 return other_task.IsWebInputEvent() ? FilterResult::KeepIterating |
28 : FilterResult::StopIterating; | 29 : FilterResult::StopIterating; |
29 } | 30 } |
30 | 31 |
31 bool IsWebInputEvent() const override { return false; } | 32 bool IsWebInputEvent() const override { return false; } |
32 | 33 |
33 void Dispatch(int routing_id, MainThreadEventQueueClient*) override { | 34 void Dispatch(MainThreadEventQueue*) override { closure_.Run(); } |
34 closure_.Run(); | |
35 } | |
36 | |
37 void EventHandled(int routing_id, | |
38 blink::scheduler::RendererScheduler* renderer_scheduler, | |
39 MainThreadEventQueueClient* client, | |
40 blink::WebInputEvent::Type type, | |
41 blink::WebInputEventResult result, | |
42 InputEventAckState ack_result) override {} | |
43 | 35 |
44 private: | 36 private: |
45 base::Closure closure_; | 37 base::Closure closure_; |
46 }; | 38 }; |
47 | 39 |
| 40 // Time interval at which touchmove events during scroll will be skipped |
| 41 // during rAF signal. |
| 42 const base::TimeDelta kAsyncTouchMoveInterval = |
| 43 base::TimeDelta::FromMilliseconds(200); |
| 44 |
| 45 } // namespace |
| 46 |
48 class QueuedWebInputEvent : public ScopedWebInputEventWithLatencyInfo, | 47 class QueuedWebInputEvent : public ScopedWebInputEventWithLatencyInfo, |
49 public MainThreadEventQueueTask { | 48 public MainThreadEventQueueTask { |
50 public: | 49 public: |
51 QueuedWebInputEvent(ui::WebScopedInputEvent event, | 50 QueuedWebInputEvent(ui::WebScopedInputEvent event, |
52 const ui::LatencyInfo& latency, | 51 const ui::LatencyInfo& latency, |
53 InputEventDispatchType dispatch_type, | 52 InputEventDispatchType dispatch_type, |
54 bool originally_cancelable) | 53 bool originally_cancelable) |
55 : ScopedWebInputEventWithLatencyInfo(std::move(event), latency), | 54 : ScopedWebInputEventWithLatencyInfo(std::move(event), latency), |
56 dispatch_type_(dispatch_type), | 55 dispatch_type_(dispatch_type), |
57 non_blocking_coalesced_count_(0), | 56 non_blocking_coalesced_count_(0), |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 | 91 |
93 // The newest event (|other_item|) always wins when updating fields. | 92 // The newest event (|other_item|) always wins when updating fields. |
94 dispatch_type_ = other_event.dispatch_type_; | 93 dispatch_type_ = other_event.dispatch_type_; |
95 originally_cancelable_ = other_event.originally_cancelable_; | 94 originally_cancelable_ = other_event.originally_cancelable_; |
96 | 95 |
97 return FilterResult::CoalescedEvent; | 96 return FilterResult::CoalescedEvent; |
98 } | 97 } |
99 | 98 |
100 bool IsWebInputEvent() const override { return true; } | 99 bool IsWebInputEvent() const override { return true; } |
101 | 100 |
102 void Dispatch(int routing_id, MainThreadEventQueueClient* client) override { | 101 void Dispatch(MainThreadEventQueue* queue) override { |
103 // Report the coalesced count only for continuous events; otherwise | 102 // Report the coalesced count only for continuous events; otherwise |
104 // the zero value would be dominated by non-continuous events. | 103 // the zero value would be dominated by non-continuous events. |
105 base::TimeTicks now = base::TimeTicks::Now(); | 104 base::TimeTicks now = base::TimeTicks::Now(); |
106 if (IsContinuousEvent()) { | 105 if (IsContinuousEvent()) { |
107 UMA_HISTOGRAM_CUSTOM_COUNTS( | 106 UMA_HISTOGRAM_CUSTOM_COUNTS( |
108 "Event.MainThreadEventQueue.Continuous.QueueingTime", | 107 "Event.MainThreadEventQueue.Continuous.QueueingTime", |
109 (now - creationTimestamp()).InMicroseconds(), 1, kTenSeconds, 50); | 108 (now - creationTimestamp()).InMicroseconds(), 1, kTenSeconds, 50); |
110 | 109 |
111 UMA_HISTOGRAM_CUSTOM_COUNTS( | 110 UMA_HISTOGRAM_CUSTOM_COUNTS( |
112 "Event.MainThreadEventQueue.Continuous.FreshnessTime", | 111 "Event.MainThreadEventQueue.Continuous.FreshnessTime", |
113 (now - lastCoalescedTimestamp()).InMicroseconds(), 1, kTenSeconds, | 112 (now - lastCoalescedTimestamp()).InMicroseconds(), 1, kTenSeconds, |
114 50); | 113 50); |
115 | 114 |
116 UMA_HISTOGRAM_COUNTS_1000("Event.MainThreadEventQueue.CoalescedCount", | 115 UMA_HISTOGRAM_COUNTS_1000("Event.MainThreadEventQueue.CoalescedCount", |
117 coalescedCount()); | 116 coalescedCount()); |
118 } else { | 117 } else { |
119 UMA_HISTOGRAM_CUSTOM_COUNTS( | 118 UMA_HISTOGRAM_CUSTOM_COUNTS( |
120 "Event.MainThreadEventQueue.NonContinuous.QueueingTime", | 119 "Event.MainThreadEventQueue.NonContinuous.QueueingTime", |
121 (now - creationTimestamp()).InMicroseconds(), 1, kTenSeconds, 50); | 120 (now - creationTimestamp()).InMicroseconds(), 1, kTenSeconds, 50); |
122 } | 121 } |
123 | 122 |
124 InputEventDispatchType dispatch_type = dispatchType(); | 123 InputEventAckState ack_result = queue->HandleEventOnMainThread( |
125 if (!blockingCoalescedEventIds().empty()) { | 124 coalesced_event(), latencyInfo(), dispatchType()); |
126 switch (dispatch_type) { | 125 for (const auto id : blockingCoalescedEventIds()) |
127 case DISPATCH_TYPE_BLOCKING: | 126 queue->SendInputEventAck(event(), ack_result, id); |
128 dispatch_type = DISPATCH_TYPE_BLOCKING_NOTIFY_MAIN; | |
129 break; | |
130 case DISPATCH_TYPE_NON_BLOCKING: | |
131 dispatch_type = DISPATCH_TYPE_NON_BLOCKING_NOTIFY_MAIN; | |
132 break; | |
133 default: | |
134 NOTREACHED(); | |
135 } | |
136 } | |
137 client->HandleEventOnMainThread(routing_id, &coalesced_event(), | |
138 latencyInfo(), dispatch_type); | |
139 } | |
140 | |
141 void EventHandled(int routing_id, | |
142 blink::scheduler::RendererScheduler* renderer_scheduler, | |
143 MainThreadEventQueueClient* client, | |
144 blink::WebInputEvent::Type type, | |
145 blink::WebInputEventResult result, | |
146 InputEventAckState ack_result) override { | |
147 for (const auto id : blockingCoalescedEventIds()) { | |
148 client->SendInputEventAck(routing_id, type, ack_result, id); | |
149 if (renderer_scheduler) { | |
150 renderer_scheduler->DidHandleInputEventOnMainThread(event(), result); | |
151 } | |
152 } | |
153 } | 127 } |
154 | 128 |
155 bool originallyCancelable() const { return originally_cancelable_; } | 129 bool originallyCancelable() const { return originally_cancelable_; } |
156 | 130 |
157 private: | 131 private: |
158 FilterResult HandleTouchScrollStartQueued() { | 132 FilterResult HandleTouchScrollStartQueued() { |
159 // A TouchScrollStart will queued after this touch move which will make all | 133 // A TouchScrollStart will queued after this touch move which will make all |
160 // previous touch moves that are queued uncancelable. | 134 // previous touch moves that are queued uncancelable. |
161 switch (event().GetType()) { | 135 switch (event().GetType()) { |
162 case blink::WebInputEvent::kTouchMove: { | 136 case blink::WebInputEvent::kTouchMove: { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 base::TimeTicks creation_timestamp_; | 187 base::TimeTicks creation_timestamp_; |
214 base::TimeTicks last_coalesced_timestamp_; | 188 base::TimeTicks last_coalesced_timestamp_; |
215 | 189 |
216 // Whether the received event was originally cancelable or not. The compositor | 190 // Whether the received event was originally cancelable or not. The compositor |
217 // input handler can change the event based on presence of event handlers so | 191 // input handler can change the event based on presence of event handlers so |
218 // this is the state at which the renderer received the event from the | 192 // this is the state at which the renderer received the event from the |
219 // browser. | 193 // browser. |
220 bool originally_cancelable_; | 194 bool originally_cancelable_; |
221 }; | 195 }; |
222 | 196 |
223 // Time interval at which touchmove events will be skipped during rAF signal. | |
224 const base::TimeDelta kAsyncTouchMoveInterval = | |
225 base::TimeDelta::FromMilliseconds(200); | |
226 | |
227 bool IsAsyncTouchMove( | |
228 const std::unique_ptr<MainThreadEventQueueTask>& queued_item) { | |
229 if (!queued_item->IsWebInputEvent()) | |
230 return false; | |
231 const QueuedWebInputEvent* event = | |
232 static_cast<const QueuedWebInputEvent*>(queued_item.get()); | |
233 if (event->event().GetType() != blink::WebInputEvent::kTouchMove) | |
234 return false; | |
235 const blink::WebTouchEvent& touch_event = | |
236 static_cast<const blink::WebTouchEvent&>(event->event()); | |
237 return touch_event.moved_beyond_slop_region && !event->originallyCancelable(); | |
238 } | |
239 | |
240 } // namespace | |
241 | |
242 MainThreadEventQueue::SharedState::SharedState() | 197 MainThreadEventQueue::SharedState::SharedState() |
243 : sent_main_frame_request_(false), sent_post_task_(false) {} | 198 : sent_main_frame_request_(false), sent_post_task_(false) {} |
244 | 199 |
245 MainThreadEventQueue::SharedState::~SharedState() {} | 200 MainThreadEventQueue::SharedState::~SharedState() {} |
246 | 201 |
247 MainThreadEventQueue::MainThreadEventQueue( | 202 MainThreadEventQueue::MainThreadEventQueue( |
248 int routing_id, | |
249 MainThreadEventQueueClient* client, | 203 MainThreadEventQueueClient* client, |
250 const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner, | 204 const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner, |
251 blink::scheduler::RendererScheduler* renderer_scheduler) | 205 blink::scheduler::RendererScheduler* renderer_scheduler) |
252 : routing_id_(routing_id), | 206 : client_(client), |
253 client_(client), | |
254 last_touch_start_forced_nonblocking_due_to_fling_(false), | 207 last_touch_start_forced_nonblocking_due_to_fling_(false), |
255 enable_fling_passive_listener_flag_(base::FeatureList::IsEnabled( | 208 enable_fling_passive_listener_flag_(base::FeatureList::IsEnabled( |
256 features::kPassiveEventListenersDueToFling)), | 209 features::kPassiveEventListenersDueToFling)), |
257 enable_non_blocking_due_to_main_thread_responsiveness_flag_( | 210 enable_non_blocking_due_to_main_thread_responsiveness_flag_( |
258 base::FeatureList::IsEnabled( | 211 base::FeatureList::IsEnabled( |
259 features::kMainThreadBusyScrollIntervention)), | 212 features::kMainThreadBusyScrollIntervention)), |
260 handle_raf_aligned_touch_input_( | 213 handle_raf_aligned_touch_input_( |
261 base::FeatureList::IsEnabled(features::kRafAlignedTouchInputEvents)), | 214 base::FeatureList::IsEnabled(features::kRafAlignedTouchInputEvents)), |
262 handle_raf_aligned_mouse_input_( | 215 handle_raf_aligned_mouse_input_( |
263 base::FeatureList::IsEnabled(features::kRafAlignedMouseInputEvents)), | 216 base::FeatureList::IsEnabled(features::kRafAlignedMouseInputEvents)), |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
384 needs_post_task = !shared_state_.sent_post_task_; | 337 needs_post_task = !shared_state_.sent_post_task_; |
385 shared_state_.sent_post_task_ = true; | 338 shared_state_.sent_post_task_ = true; |
386 } | 339 } |
387 | 340 |
388 if (needs_post_task) | 341 if (needs_post_task) |
389 PostTaskToMainThread(); | 342 PostTaskToMainThread(); |
390 } | 343 } |
391 | 344 |
392 void MainThreadEventQueue::DispatchInFlightEvent() { | 345 void MainThreadEventQueue::DispatchInFlightEvent() { |
393 if (in_flight_event_) { | 346 if (in_flight_event_) { |
394 in_flight_event_->Dispatch(routing_id_, client_); | 347 in_flight_event_->Dispatch(this); |
395 in_flight_event_.reset(); | 348 in_flight_event_.reset(); |
396 } | 349 } |
397 } | 350 } |
398 | 351 |
399 void MainThreadEventQueue::PossiblyScheduleMainFrame() { | 352 void MainThreadEventQueue::PossiblyScheduleMainFrame() { |
400 if (IsRafAlignedInputDisabled()) | 353 if (IsRafAlignedInputDisabled()) |
401 return; | 354 return; |
402 bool needs_main_frame = false; | 355 bool needs_main_frame = false; |
403 { | 356 { |
404 base::AutoLock lock(shared_state_lock_); | 357 base::AutoLock lock(shared_state_lock_); |
405 if (!shared_state_.sent_main_frame_request_ && | 358 if (!shared_state_.sent_main_frame_request_ && |
406 !shared_state_.events_.empty() && | 359 !shared_state_.events_.empty() && |
407 IsRafAlignedEvent(shared_state_.events_.front())) { | 360 IsRafAlignedEvent(shared_state_.events_.front())) { |
408 needs_main_frame = true; | 361 needs_main_frame = true; |
409 shared_state_.sent_main_frame_request_ = true; | 362 shared_state_.sent_main_frame_request_ = true; |
410 } | 363 } |
411 } | 364 } |
412 if (needs_main_frame) | 365 if (needs_main_frame) |
413 client_->NeedsMainFrame(routing_id_); | 366 SetNeedsMainFrame(); |
414 } | 367 } |
415 | 368 |
416 void MainThreadEventQueue::DispatchEvents() { | 369 void MainThreadEventQueue::DispatchEvents() { |
417 size_t events_to_process; | 370 size_t events_to_process; |
418 | 371 |
419 // Record the queue size so that we only process | 372 // Record the queue size so that we only process |
420 // that maximum number of events. | 373 // that maximum number of events. |
421 { | 374 { |
422 base::AutoLock lock(shared_state_lock_); | 375 base::AutoLock lock(shared_state_lock_); |
423 shared_state_.sent_post_task_ = false; | 376 shared_state_.sent_post_task_ = false; |
(...skipping 13 matching lines...) Expand all Loading... |
437 return; | 390 return; |
438 in_flight_event_ = shared_state_.events_.Pop(); | 391 in_flight_event_ = shared_state_.events_.Pop(); |
439 } | 392 } |
440 | 393 |
441 // Dispatching the event is outside of critical section. | 394 // Dispatching the event is outside of critical section. |
442 DispatchInFlightEvent(); | 395 DispatchInFlightEvent(); |
443 } | 396 } |
444 PossiblyScheduleMainFrame(); | 397 PossiblyScheduleMainFrame(); |
445 } | 398 } |
446 | 399 |
447 void MainThreadEventQueue::EventHandled(blink::WebInputEvent::Type type, | 400 static bool IsAsyncTouchMove( |
448 blink::WebInputEventResult result, | 401 const std::unique_ptr<MainThreadEventQueueTask>& queued_item) { |
449 InputEventAckState ack_result) { | 402 if (!queued_item->IsWebInputEvent()) |
450 if (in_flight_event_) { | 403 return false; |
451 in_flight_event_->EventHandled(routing_id_, renderer_scheduler_, client_, | 404 const QueuedWebInputEvent* event = |
452 type, result, ack_result); | 405 static_cast<const QueuedWebInputEvent*>(queued_item.get()); |
453 } | 406 if (event->event().GetType() != blink::WebInputEvent::kTouchMove) |
| 407 return false; |
| 408 const blink::WebTouchEvent& touch_event = |
| 409 static_cast<const blink::WebTouchEvent&>(event->event()); |
| 410 return touch_event.moved_beyond_slop_region && !event->originallyCancelable(); |
454 } | 411 } |
455 | 412 |
456 void MainThreadEventQueue::DispatchRafAlignedInput(base::TimeTicks frame_time) { | 413 void MainThreadEventQueue::DispatchRafAlignedInput(base::TimeTicks frame_time) { |
457 if (IsRafAlignedInputDisabled()) | 414 if (IsRafAlignedInputDisabled()) |
458 return; | 415 return; |
459 | 416 |
460 size_t queue_size_at_start; | 417 size_t queue_size_at_start; |
461 | 418 |
462 // Record the queue size so that we only process | 419 // Record the queue size so that we only process |
463 // that maximum number of events. | 420 // that maximum number of events. |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
519 } else { | 476 } else { |
520 needs_main_frame = !shared_state_.sent_main_frame_request_; | 477 needs_main_frame = !shared_state_.sent_main_frame_request_; |
521 shared_state_.sent_main_frame_request_ = true; | 478 shared_state_.sent_main_frame_request_ = true; |
522 } | 479 } |
523 } | 480 } |
524 } | 481 } |
525 | 482 |
526 if (needs_post_task) | 483 if (needs_post_task) |
527 PostTaskToMainThread(); | 484 PostTaskToMainThread(); |
528 if (needs_main_frame) | 485 if (needs_main_frame) |
529 client_->NeedsMainFrame(routing_id_); | 486 SetNeedsMainFrame(); |
530 } | 487 } |
531 | 488 |
532 bool MainThreadEventQueue::IsRafAlignedInputDisabled() const { | 489 bool MainThreadEventQueue::IsRafAlignedInputDisabled() const { |
533 return !handle_raf_aligned_mouse_input_ && !handle_raf_aligned_touch_input_; | 490 return !handle_raf_aligned_mouse_input_ && !handle_raf_aligned_touch_input_; |
534 } | 491 } |
535 | 492 |
536 bool MainThreadEventQueue::IsRafAlignedEvent( | 493 bool MainThreadEventQueue::IsRafAlignedEvent( |
537 const std::unique_ptr<MainThreadEventQueueTask>& item) const { | 494 const std::unique_ptr<MainThreadEventQueueTask>& item) const { |
538 if (!item->IsWebInputEvent()) | 495 if (!item->IsWebInputEvent()) |
539 return false; | 496 return false; |
540 const QueuedWebInputEvent* event = | 497 const QueuedWebInputEvent* event = |
541 static_cast<const QueuedWebInputEvent*>(item.get()); | 498 static_cast<const QueuedWebInputEvent*>(item.get()); |
542 switch (event->event().GetType()) { | 499 switch (event->event().GetType()) { |
543 case blink::WebInputEvent::kMouseMove: | 500 case blink::WebInputEvent::kMouseMove: |
544 case blink::WebInputEvent::kMouseWheel: | 501 case blink::WebInputEvent::kMouseWheel: |
545 return handle_raf_aligned_mouse_input_; | 502 return handle_raf_aligned_mouse_input_; |
546 case blink::WebInputEvent::kTouchMove: | 503 case blink::WebInputEvent::kTouchMove: |
547 return handle_raf_aligned_touch_input_; | 504 return handle_raf_aligned_touch_input_; |
548 default: | 505 default: |
549 return false; | 506 return false; |
550 } | 507 } |
551 } | 508 } |
552 | 509 |
| 510 InputEventAckState MainThreadEventQueue::HandleEventOnMainThread( |
| 511 const blink::WebCoalescedInputEvent& event, |
| 512 const ui::LatencyInfo& latency, |
| 513 InputEventDispatchType dispatch_type) { |
| 514 if (client_) |
| 515 return client_->HandleInputEvent(event, latency, dispatch_type); |
| 516 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; |
| 517 } |
| 518 |
| 519 void MainThreadEventQueue::SendInputEventAck(const blink::WebInputEvent& event, |
| 520 InputEventAckState ack_result, |
| 521 uint32_t touch_event_id) { |
| 522 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 523 if (!client_) |
| 524 return; |
| 525 client_->SendInputEventAck(event.GetType(), ack_result, touch_event_id); |
| 526 if (renderer_scheduler_) { |
| 527 renderer_scheduler_->DidHandleInputEventOnMainThread( |
| 528 event, ack_result == INPUT_EVENT_ACK_STATE_CONSUMED |
| 529 ? blink::WebInputEventResult::kHandledApplication |
| 530 : blink::WebInputEventResult::kNotHandled); |
| 531 } |
| 532 } |
| 533 |
| 534 void MainThreadEventQueue::SetNeedsMainFrame() { |
| 535 if (main_task_runner_->BelongsToCurrentThread()) { |
| 536 if (client_) |
| 537 client_->SetNeedsMainFrame(); |
| 538 return; |
| 539 } |
| 540 |
| 541 main_task_runner_->PostTask( |
| 542 FROM_HERE, base::Bind(&MainThreadEventQueue::SetNeedsMainFrame, this)); |
| 543 } |
| 544 |
| 545 void MainThreadEventQueue::ClearClient() { |
| 546 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 547 client_ = nullptr; |
| 548 } |
| 549 |
553 } // namespace content | 550 } // namespace content |
OLD | NEW |