1 // Copyright 2013 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 "third_party/blink/public/platform/input/input_handler_proxy.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10
11 #include "base/auto_reset.h"
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/check_op.h"
15 #include "base/command_line.h"
16 #include "base/location.h"
17 #include "base/metrics/field_trial_params.h"
18 #include "base/metrics/histogram_macros.h"
19 #include "base/notreached.h"
20 #include "base/profiler/sample_metadata.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/threading/thread_task_runner_handle.h"
23 #include "base/time/default_tick_clock.h"
24 #include "base/trace_event/trace_event.h"
25 #include "build/build_config.h"
26 #include "cc/base/features.h"
27 #include "cc/input/main_thread_scrolling_reason.h"
28 #include "cc/metrics/event_metrics.h"
29 #include "services/tracing/public/cpp/perfetto/flow_event_utils.h"
30 #include "services/tracing/public/cpp/perfetto/macros.h"
31 #include "third_party/blink/public/common/features.h"
32 #include "third_party/blink/public/common/input/web_input_event.h"
33 #include "third_party/blink/public/common/input/web_input_event_attribution.h"
34 #include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
35 #include "third_party/blink/public/common/input/web_pointer_event.h"
36 #include "third_party/blink/public/common/input/web_touch_event.h"
37 #include "third_party/blink/public/platform/input/input_handler_proxy_client.h"
38 #include "third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.h"
39 #include "third_party/blink/renderer/platform/widget/input/cursor_control_handler.h"
40 #include "third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller.h"
41 #include "third_party/blink/renderer/platform/widget/input/event_with_callback.h"
42 #include "third_party/blink/renderer/platform/widget/input/momentum_scroll_jank_tracker.h"
43 #include "third_party/blink/renderer/platform/widget/input/scroll_predictor.h"
44 #include "ui/events/types/scroll_input_type.h"
45 #include "ui/gfx/geometry/point_conversions.h"
46 #include "ui/latency/latency_info.h"
47
48 using perfetto::protos::pbzero::ChromeLatencyInfo;
49 using perfetto::protos::pbzero::TrackEvent;
50
51 using ScrollThread = cc::InputHandler::ScrollThread;
52
53 namespace blink {
54 namespace {
55
CreateScrollStateForGesture(const WebGestureEvent & event)56 cc::ScrollState CreateScrollStateForGesture(const WebGestureEvent& event) {
57 cc::ScrollStateData scroll_state_data;
58 switch (event.GetType()) {
59 case WebInputEvent::Type::kGestureScrollBegin:
60 scroll_state_data.position_x = event.PositionInWidget().x();
61 scroll_state_data.position_y = event.PositionInWidget().y();
62 scroll_state_data.delta_x_hint = -event.data.scroll_begin.delta_x_hint;
63 scroll_state_data.delta_y_hint = -event.data.scroll_begin.delta_y_hint;
64 scroll_state_data.is_beginning = true;
65 // On Mac, a GestureScrollBegin in the inertial phase indicates a fling
66 // start.
67 scroll_state_data.is_in_inertial_phase =
68 (event.data.scroll_begin.inertial_phase ==
69 WebGestureEvent::InertialPhaseState::kMomentum);
70 scroll_state_data.delta_granularity =
71 event.data.scroll_begin.delta_hint_units;
72
73 if (cc::ElementId::IsValid(
74 event.data.scroll_begin.scrollable_area_element_id)) {
75 cc::ElementId target_scroller(
76 event.data.scroll_begin.scrollable_area_element_id);
77 scroll_state_data.set_current_native_scrolling_element(target_scroller);
78
79 // If the target scroller comes from a main thread hit test, we're in
80 // scroll unification.
81 scroll_state_data.is_main_thread_hit_tested =
82 event.data.scroll_begin.main_thread_hit_tested;
83 DCHECK(!event.data.scroll_begin.main_thread_hit_tested ||
84 base::FeatureList::IsEnabled(::features::kScrollUnification));
85 } else {
86 // If a main thread hit test didn't yield a target we should have
87 // discarded this event before this point.
88 DCHECK(!event.data.scroll_begin.main_thread_hit_tested);
89 }
90
91 break;
92 case WebInputEvent::Type::kGestureScrollUpdate:
93 scroll_state_data.delta_x = -event.data.scroll_update.delta_x;
94 scroll_state_data.delta_y = -event.data.scroll_update.delta_y;
95 scroll_state_data.velocity_x = event.data.scroll_update.velocity_x;
96 scroll_state_data.velocity_y = event.data.scroll_update.velocity_y;
97 scroll_state_data.is_in_inertial_phase =
98 event.data.scroll_update.inertial_phase ==
99 WebGestureEvent::InertialPhaseState::kMomentum;
100 scroll_state_data.delta_granularity =
101 event.data.scroll_update.delta_units;
102 break;
103 case WebInputEvent::Type::kGestureScrollEnd:
104 scroll_state_data.is_ending = true;
105 break;
106 default:
107 NOTREACHED();
108 break;
109 }
110 scroll_state_data.is_direct_manipulation =
111 event.SourceDevice() == WebGestureDevice::kTouchscreen;
112 return cc::ScrollState(scroll_state_data);
113 }
114
CreateScrollStateForInertialUpdate(const gfx::Vector2dF & delta)115 cc::ScrollState CreateScrollStateForInertialUpdate(
116 const gfx::Vector2dF& delta) {
117 cc::ScrollStateData scroll_state_data;
118 scroll_state_data.delta_x = delta.x();
119 scroll_state_data.delta_y = delta.y();
120 scroll_state_data.is_in_inertial_phase = true;
121 return cc::ScrollState(scroll_state_data);
122 }
123
GestureScrollInputType(WebGestureDevice device)124 ui::ScrollInputType GestureScrollInputType(WebGestureDevice device) {
125 switch (device) {
126 case WebGestureDevice::kTouchpad:
127 return ui::ScrollInputType::kWheel;
128 case WebGestureDevice::kTouchscreen:
129 return ui::ScrollInputType::kTouchscreen;
130 case WebGestureDevice::kSyntheticAutoscroll:
131 return ui::ScrollInputType::kAutoscroll;
132 case WebGestureDevice::kScrollbar:
133 return ui::ScrollInputType::kScrollbar;
134 case WebGestureDevice::kUninitialized:
135 NOTREACHED();
136 return ui::ScrollInputType::kMaxValue;
137 }
138 }
139
GestureScrollEventType(WebInputEvent::Type web_event_type)140 cc::SnapFlingController::GestureScrollType GestureScrollEventType(
141 WebInputEvent::Type web_event_type) {
142 switch (web_event_type) {
143 case WebInputEvent::Type::kGestureScrollBegin:
144 return cc::SnapFlingController::GestureScrollType::kBegin;
145 case WebInputEvent::Type::kGestureScrollUpdate:
146 return cc::SnapFlingController::GestureScrollType::kUpdate;
147 case WebInputEvent::Type::kGestureScrollEnd:
148 return cc::SnapFlingController::GestureScrollType::kEnd;
149 default:
150 NOTREACHED();
151 return cc::SnapFlingController::GestureScrollType::kBegin;
152 }
153 }
154
GetGestureScrollUpdateInfo(const WebGestureEvent & event)155 cc::SnapFlingController::GestureScrollUpdateInfo GetGestureScrollUpdateInfo(
156 const WebGestureEvent& event) {
157 cc::SnapFlingController::GestureScrollUpdateInfo info;
158 info.delta = gfx::Vector2dF(-event.data.scroll_update.delta_x,
159 -event.data.scroll_update.delta_y);
160 info.is_in_inertial_phase = event.data.scroll_update.inertial_phase ==
161 WebGestureEvent::InertialPhaseState::kMomentum;
162 info.event_time = event.TimeStamp();
163 return info;
164 }
165
RecordScrollingThread(bool scrolling_on_compositor_thread,bool blocked_on_main_thread_event_handler,WebGestureDevice device)166 cc::ScrollBeginThreadState RecordScrollingThread(
167 bool scrolling_on_compositor_thread,
168 bool blocked_on_main_thread_event_handler,
169 WebGestureDevice device) {
170 const char* kWheelHistogramName = "Renderer4.ScrollingThread.Wheel";
171 const char* kTouchHistogramName = "Renderer4.ScrollingThread.Touch";
172
173 auto status = cc::ScrollBeginThreadState::kScrollingOnMain;
174 if (scrolling_on_compositor_thread) {
175 status =
176 blocked_on_main_thread_event_handler
177 ? cc::ScrollBeginThreadState::kScrollingOnCompositorBlockedOnMain
178 : cc::ScrollBeginThreadState::kScrollingOnCompositor;
179 }
180
181 if (device == WebGestureDevice::kTouchscreen) {
182 UMA_HISTOGRAM_ENUMERATION(kTouchHistogramName, status);
183 } else if (device == WebGestureDevice::kTouchpad) {
184 UMA_HISTOGRAM_ENUMERATION(kWheelHistogramName, status);
185 } else if (device == WebGestureDevice::kScrollbar) {
186 // TODO(crbug.com/1101502): Add support for
187 // Renderer4.ScrollingThread.Scrollbar
188 } else {
189 NOTREACHED();
190 }
191 return status;
192 }
193
IsGestureScrollOrPinch(WebInputEvent::Type type)194 bool IsGestureScrollOrPinch(WebInputEvent::Type type) {
195 switch (type) {
196 case WebGestureEvent::Type::kGestureScrollBegin:
197 case WebGestureEvent::Type::kGestureScrollUpdate:
198 case WebGestureEvent::Type::kGestureScrollEnd:
199 case WebGestureEvent::Type::kGesturePinchBegin:
200 case WebGestureEvent::Type::kGesturePinchUpdate:
201 case WebGestureEvent::Type::kGesturePinchEnd:
202 return true;
203 default:
204 return false;
205 }
206 }
207
208 } // namespace
209
InputHandlerProxy(cc::InputHandler & input_handler,InputHandlerProxyClient * client)210 InputHandlerProxy::InputHandlerProxy(cc::InputHandler& input_handler,
211 InputHandlerProxyClient* client)
212 : client_(client),
213 input_handler_(&input_handler),
214 synchronous_input_handler_(nullptr),
215 handling_gesture_on_impl_thread_(false),
216 scroll_sequence_ignored_(false),
217 current_overscroll_params_(nullptr),
218 has_seen_first_gesture_scroll_update_after_begin_(false),
219 last_injected_gesture_was_begin_(false),
220 tick_clock_(base::DefaultTickClock::GetInstance()),
221 snap_fling_controller_(std::make_unique<cc::SnapFlingController>(this)),
222 cursor_control_handler_(std::make_unique<CursorControlHandler>()) {
223 DCHECK(client);
224 input_handler_->BindToClient(this);
225 cc::ScrollElasticityHelper* scroll_elasticity_helper =
226 input_handler_->CreateScrollElasticityHelper();
227 if (scroll_elasticity_helper) {
228 elastic_overscroll_controller_ =
229 ElasticOverscrollController::Create(scroll_elasticity_helper);
230 }
231 compositor_event_queue_ = std::make_unique<CompositorThreadEventQueue>();
232 scroll_predictor_ =
233 base::FeatureList::IsEnabled(blink::features::kResamplingScrollEvents)
234 ? std::make_unique<ScrollPredictor>()
235 : nullptr;
236
237 if (base::FeatureList::IsEnabled(blink::features::kSkipTouchEventFilter) &&
238 GetFieldTrialParamValueByFeature(
239 blink::features::kSkipTouchEventFilter,
240 blink::features::kSkipTouchEventFilterFilteringProcessParamName) ==
241 blink::features::
242 kSkipTouchEventFilterFilteringProcessParamValueBrowserAndRenderer) {
243 // Skipping filtering for touch events on renderer process is enabled.
244 // Always skip filtering discrete events.
245 skip_touch_filter_discrete_ = true;
246 if (GetFieldTrialParamValueByFeature(
247 blink::features::kSkipTouchEventFilter,
248 blink::features::kSkipTouchEventFilterTypeParamName) ==
249 blink::features::kSkipTouchEventFilterTypeParamValueAll) {
250 // The experiment config also specifies to skip touchmove events.
251 skip_touch_filter_all_ = true;
252 }
253 }
254 }
255
~InputHandlerProxy()256 InputHandlerProxy::~InputHandlerProxy() {}
257
WillShutdown()258 void InputHandlerProxy::WillShutdown() {
259 elastic_overscroll_controller_.reset();
260 input_handler_ = nullptr;
261 client_->WillShutdown();
262 }
263
HandleInputEventWithLatencyInfo(std::unique_ptr<blink::WebCoalescedInputEvent> event,std::unique_ptr<cc::EventMetrics> metrics,EventDispositionCallback callback)264 void InputHandlerProxy::HandleInputEventWithLatencyInfo(
265 std::unique_ptr<blink::WebCoalescedInputEvent> event,
266 std::unique_ptr<cc::EventMetrics> metrics,
267 EventDispositionCallback callback) {
268 DCHECK(input_handler_);
269
270 input_handler_->NotifyInputEvent();
271
272 int64_t trace_id = event->latency_info().trace_id();
273 TRACE_EVENT("input,benchmark", "LatencyInfo.Flow",
274 [trace_id](perfetto::EventContext ctx) {
275 ChromeLatencyInfo* info =
276 ctx.event()->set_chrome_latency_info();
277 info->set_trace_id(trace_id);
278 info->set_step(ChromeLatencyInfo::STEP_HANDLE_INPUT_EVENT_IMPL);
279 tracing::FillFlowEvent(ctx, TrackEvent::LegacyEvent::FLOW_INOUT,
280 trace_id);
281 });
282
283 auto event_with_callback = std::make_unique<EventWithCallback>(
284 std::move(event), tick_clock_->NowTicks(), std::move(callback),
285 std::move(metrics));
286
287 enum {
288 NO_SCROLL_PINCH = 0,
289 ONGOING_SCROLL_PINCH = 1,
290 SCROLL_PINCH = 2,
291 };
292 // Note: Other input can race ahead of gesture input as they don't have to go
293 // through the queue, but we believe it's OK to do so.
294 if (!IsGestureScrollOrPinch(event_with_callback->event().GetType())) {
295 base::ScopedSampleMetadata metadata("Input.GestureScrollOrPinch",
296 NO_SCROLL_PINCH);
297 DispatchSingleInputEvent(std::move(event_with_callback),
298 tick_clock_->NowTicks());
299 return;
300 }
301
302 base::ScopedSampleMetadata metadata(
303 "Input.GestureScrollOrPinch", currently_active_gesture_device_.has_value()
304 ? ONGOING_SCROLL_PINCH
305 : SCROLL_PINCH);
306 const auto& gesture_event =
307 static_cast<const WebGestureEvent&>(event_with_callback->event());
308 const bool is_first_gesture_scroll_update =
309 !has_seen_first_gesture_scroll_update_after_begin_ &&
310 gesture_event.GetType() == WebGestureEvent::Type::kGestureScrollUpdate;
311
312 if (gesture_event.GetType() == WebGestureEvent::Type::kGestureScrollBegin) {
313 has_seen_first_gesture_scroll_update_after_begin_ = false;
314 } else if (gesture_event.GetType() ==
315 WebGestureEvent::Type::kGestureScrollUpdate) {
316 has_seen_first_gesture_scroll_update_after_begin_ = true;
317 }
318
319 if (currently_active_gesture_device_.has_value()) {
320 // Scroll updates should typically be queued and wait until a
321 // BeginImplFrame to dispatch. However, the first scroll update to be
322 // generated from a *blocking* touch sequence will have waited for the
323 // touch event to be ACK'ed by the renderer as unconsumed. Queueing here
324 // again until BeginImplFrame means we'll likely add a whole frame of
325 // latency to so we flush the queue immediately. This happens only for the
326 // first scroll update because once a scroll starts touch events are
327 // dispatched non-blocking so scroll updates don't wait for a touch ACK.
328 // The |is_source_touch_event_set_blocking| bit is set based on the
329 // renderer's reply that a blocking touch stream should be made
330 // non-blocking. Note: unlike wheel events below, the first GSU in a touch
331 // may have come from a non-blocking touch sequence, e.g. if the earlier
332 // touchstart determined we're in a |touch-action: pan-y| region. Because
333 // of this, we can't simply look at the first GSU like wheels do.
334 bool is_from_blocking_touch =
335 gesture_event.SourceDevice() == WebGestureDevice::kTouchscreen &&
336 gesture_event.is_source_touch_event_set_blocking;
337
338 // TODO(bokan): This was added in https://crrev.com/c/557463 before async
339 // wheel events. It's not clear to me why flushing on a scroll end would
340 // help or why this is specific to wheel events but I suspect it's no
341 // longer needed now that wheel scrolling uses non-blocking events.
342 bool is_scroll_end_from_wheel =
343 gesture_event.SourceDevice() == WebGestureDevice::kTouchpad &&
344 gesture_event.GetType() == WebGestureEvent::Type::kGestureScrollEnd;
345
346 // Wheel events have the same issue as the blocking touch issue above.
347 // However, all wheel events are initially sent blocking and become non-
348 // blocking on the first unconsumed event. We can therefore simply look for
349 // the first scroll update in a wheel gesture.
350 bool is_first_wheel_scroll_update =
351 gesture_event.SourceDevice() == WebGestureDevice::kTouchpad &&
352 is_first_gesture_scroll_update;
353
354 // |synchronous_input_handler_| is WebView only. WebView has different
355 // mechanisms and we want to forward all events immediately.
356 if (is_from_blocking_touch || is_scroll_end_from_wheel ||
357 is_first_wheel_scroll_update || synchronous_input_handler_) {
358 compositor_event_queue_->Queue(std::move(event_with_callback),
359 tick_clock_->NowTicks());
360 DispatchQueuedInputEvents();
361 return;
362 }
363
364 bool needs_animate_input = compositor_event_queue_->empty();
365 compositor_event_queue_->Queue(std::move(event_with_callback),
366 tick_clock_->NowTicks());
367 if (needs_animate_input)
368 input_handler_->SetNeedsAnimateInput();
369 return;
370 }
371
372 // We have to dispatch the event to know whether the gesture sequence will be
373 // handled by the compositor or not.
374 DispatchSingleInputEvent(std::move(event_with_callback),
375 tick_clock_->NowTicks());
376 }
377
ContinueScrollBeginAfterMainThreadHitTest(std::unique_ptr<blink::WebCoalescedInputEvent> event,std::unique_ptr<cc::EventMetrics> metrics,EventDispositionCallback callback,cc::ElementIdType hit_test_result)378 void InputHandlerProxy::ContinueScrollBeginAfterMainThreadHitTest(
379 std::unique_ptr<blink::WebCoalescedInputEvent> event,
380 std::unique_ptr<cc::EventMetrics> metrics,
381 EventDispositionCallback callback,
382 cc::ElementIdType hit_test_result) {
383 DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
384 DCHECK_EQ(event->Event().GetType(),
385 WebGestureEvent::Type::kGestureScrollBegin);
386 DCHECK(hit_testing_scroll_begin_on_main_thread_);
387 DCHECK(currently_active_gesture_device_);
388 DCHECK(input_handler_);
389
390 hit_testing_scroll_begin_on_main_thread_ = false;
391
392 // HandleGestureScrollBegin has logic to end an existing scroll when an
393 // unexpected scroll begin arrives. We currently think we're in a scroll
394 // because of the first ScrollBegin so clear this so we don't spurriously
395 // call ScrollEnd. It will be set again in HandleGestureScrollBegin.
396 currently_active_gesture_device_ = base::nullopt;
397
398 auto* gesture_event =
399 static_cast<blink::WebGestureEvent*>(event->EventPointer());
400 if (cc::ElementId::IsValid(hit_test_result)) {
401 gesture_event->data.scroll_begin.scrollable_area_element_id =
402 hit_test_result;
403 gesture_event->data.scroll_begin.main_thread_hit_tested = true;
404
405 auto event_with_callback = std::make_unique<EventWithCallback>(
406 std::move(event), tick_clock_->NowTicks(), std::move(callback),
407 std::move(metrics));
408
409 DispatchSingleInputEvent(std::move(event_with_callback),
410 tick_clock_->NowTicks());
411 } else {
412 // TODO(bokan): This looks odd but is actually what happens in the
413 // non-unified path. If a scroll is DROP_EVENT'ed, we still call
414 // RecordMainThreadScrollingReasons and then LTHI::RecordScrollEnd when we
415 // DROP the ScrollEnd. We call this to ensure symmetry between
416 // RecordScrollBegin and RecordScrollEnd but we should probably be avoiding
417 // this if the scroll never starts. https://crbug.com/1082601.
418 RecordMainThreadScrollingReasons(gesture_event->SourceDevice(), 0);
419
420 // If the main thread failed to return a scroller for whatever reason,
421 // consider the ScrollBegin to be dropped.
422 scroll_sequence_ignored_ = true;
423 WebInputEventAttribution attribution =
424 PerformEventAttribution(event->Event());
425 std::move(callback).Run(DROP_EVENT, std::move(event),
426 /*overscroll_params=*/nullptr, attribution,
427 std::move(metrics));
428 }
429
430 // We blocked the compositor gesture event queue while the hit test was
431 // pending so scroll updates may be waiting in the queue. Now that we've
432 // finished the hit test and performed the scroll begin, flush the queue.
433 DispatchQueuedInputEvents();
434 }
435
DispatchSingleInputEvent(std::unique_ptr<EventWithCallback> event_with_callback,const base::TimeTicks now)436 void InputHandlerProxy::DispatchSingleInputEvent(
437 std::unique_ptr<EventWithCallback> event_with_callback,
438 const base::TimeTicks now) {
439 ui::LatencyInfo monitored_latency_info = event_with_callback->latency_info();
440 std::unique_ptr<cc::SwapPromiseMonitor> latency_info_swap_promise_monitor =
441 input_handler_->CreateLatencyInfoSwapPromiseMonitor(
442 &monitored_latency_info);
443
444 current_overscroll_params_.reset();
445
446 WebInputEventAttribution attribution =
447 PerformEventAttribution(event_with_callback->event());
448 InputHandlerProxy::EventDisposition disposition =
449 RouteToTypeSpecificHandler(event_with_callback.get(), attribution);
450
451 const WebInputEvent& event = event_with_callback->event();
452 const WebGestureEvent::Type type = event.GetType();
453 switch (type) {
454 case WebGestureEvent::Type::kGestureScrollBegin:
455 case WebGestureEvent::Type::kGesturePinchBegin:
456 if (disposition == DID_HANDLE ||
457 disposition == DID_HANDLE_SHOULD_BUBBLE ||
458 disposition == REQUIRES_MAIN_THREAD_HIT_TEST) {
459 // REQUIRES_MAIN_THREAD_HIT_TEST means the scroll will be handled by
460 // the compositor but needs to block until a hit test is performed by
461 // Blink. We need to set this to indicate we're in a scroll so that
462 // gestures are queued rather than dispatched immediately.
463 // TODO(bokan): It's a bit of an open question if we need to also set
464 // |handling_gesture_on_impl_thread_|. Ideally these two bits would be
465 // merged. The queueing behavior is currently just determined by having
466 // an active gesture device.
467 currently_active_gesture_device_ =
468 static_cast<const WebGestureEvent&>(event).SourceDevice();
469 }
470 break;
471
472 case WebGestureEvent::Type::kGestureScrollEnd:
473 case WebGestureEvent::Type::kGesturePinchEnd:
474 if (!handling_gesture_on_impl_thread_)
475 currently_active_gesture_device_ = base::nullopt;
476 break;
477 default:
478 break;
479 }
480
481 // Handle jank tracking during the momentum phase of a scroll gesture. The
482 // class filters non-momentum events internally.
483 switch (type) {
484 case WebGestureEvent::Type::kGestureScrollBegin:
485 momentum_scroll_jank_tracker_ =
486 std::make_unique<MomentumScrollJankTracker>();
487 break;
488 case WebGestureEvent::Type::kGestureScrollUpdate:
489 // It's possible to get a scroll update without a begin. Ignore these
490 // cases.
491 if (momentum_scroll_jank_tracker_) {
492 momentum_scroll_jank_tracker_->OnDispatchedInputEvent(
493 event_with_callback.get(), now);
494 }
495 break;
496 case WebGestureEvent::Type::kGestureScrollEnd:
497 momentum_scroll_jank_tracker_.reset();
498 break;
499 default:
500 break;
501 }
502
503 // Will run callback for every original events.
504 event_with_callback->RunCallbacks(disposition, monitored_latency_info,
505 std::move(current_overscroll_params_),
506 attribution);
507 }
508
DispatchQueuedInputEvents()509 void InputHandlerProxy::DispatchQueuedInputEvents() {
510 // Block flushing the compositor gesture event queue while there's an async
511 // scroll begin hit test outstanding. We'll flush the queue when the hit test
512 // responds.
513 if (hit_testing_scroll_begin_on_main_thread_) {
514 DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
515 return;
516 }
517
518 // Calling |NowTicks()| is expensive so we only want to do it once.
519 base::TimeTicks now = tick_clock_->NowTicks();
520 while (!compositor_event_queue_->empty())
521 DispatchSingleInputEvent(compositor_event_queue_->Pop(), now);
522 }
523
InjectScrollbarGestureScroll(const WebInputEvent::Type type,const gfx::PointF & position_in_widget,const cc::InputHandlerPointerResult & pointer_result,const ui::LatencyInfo & latency_info,const base::TimeTicks original_timestamp,const cc::EventMetrics * original_metrics)524 void InputHandlerProxy::InjectScrollbarGestureScroll(
525 const WebInputEvent::Type type,
526 const gfx::PointF& position_in_widget,
527 const cc::InputHandlerPointerResult& pointer_result,
528 const ui::LatencyInfo& latency_info,
529 const base::TimeTicks original_timestamp,
530 const cc::EventMetrics* original_metrics) {
531 gfx::Vector2dF scroll_delta(pointer_result.scroll_offset.x(),
532 pointer_result.scroll_offset.y());
533
534 std::unique_ptr<WebGestureEvent> synthetic_gesture_event =
535 WebGestureEvent::GenerateInjectedScrollGesture(
536 type, original_timestamp, WebGestureDevice::kScrollbar,
537 position_in_widget, scroll_delta, pointer_result.scroll_units);
538
539 // This will avoid hit testing and directly scroll the scroller with the
540 // provided element_id.
541 if (type == WebInputEvent::Type::kGestureScrollBegin) {
542 synthetic_gesture_event->data.scroll_begin.scrollable_area_element_id =
543 pointer_result.target_scroller.GetStableId();
544 }
545
546 // Send in a LatencyInfo with SCROLLBAR type so that the end to end latency
547 // is calculated specifically for scrollbars.
548 ui::LatencyInfo scrollbar_latency_info(latency_info);
549 scrollbar_latency_info.set_source_event_type(ui::SourceEventType::SCROLLBAR);
550
551 // This latency_info should not have already been scheduled for rendering -
552 // i.e. it should be the original latency_info that was associated with the
553 // input event that caused this scroll injection. If it has already been
554 // scheduled it won't get queued to be shipped off with the CompositorFrame
555 // when the gesture is handled.
556 DCHECK(!scrollbar_latency_info.FindLatency(
557 ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT, nullptr));
558
559 base::Optional<cc::EventMetrics::ScrollUpdateType> scroll_update_type;
560 if (type == WebInputEvent::Type::kGestureScrollBegin) {
561 last_injected_gesture_was_begin_ = true;
562 } else {
563 if (type == WebInputEvent::Type::kGestureScrollUpdate) {
564 // For injected GSUs, add a scroll update component to the latency info
565 // so that it is properly classified as a scroll. If the last injected
566 // gesture was a GSB, then this GSU is the first scroll update - mark
567 // the LatencyInfo as such.
568 scrollbar_latency_info.AddLatencyNumberWithTimestamp(
569 last_injected_gesture_was_begin_
570 ? ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT
571 : ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT,
572 original_timestamp);
573 scroll_update_type = last_injected_gesture_was_begin_
574 ? cc::EventMetrics::ScrollUpdateType::kStarted
575 : cc::EventMetrics::ScrollUpdateType::kContinued;
576 }
577
578 last_injected_gesture_was_begin_ = false;
579 }
580
581 base::TimeTicks metrics_time_stamp =
582 original_metrics ? original_metrics->time_stamp()
583 : synthetic_gesture_event->TimeStamp();
584 std::unique_ptr<cc::EventMetrics> metrics = cc::EventMetrics::Create(
585 synthetic_gesture_event->GetTypeAsUiEventType(), scroll_update_type,
586 metrics_time_stamp, synthetic_gesture_event->GetScrollInputType());
587 auto gesture_event_with_callback_update = std::make_unique<EventWithCallback>(
588 std::make_unique<WebCoalescedInputEvent>(
589 std::move(synthetic_gesture_event), scrollbar_latency_info),
590 original_timestamp, base::DoNothing(), std::move(metrics));
591
592 bool needs_animate_input = compositor_event_queue_->empty();
593 compositor_event_queue_->Queue(std::move(gesture_event_with_callback_update),
594 original_timestamp);
595
596 if (needs_animate_input)
597 input_handler_->SetNeedsAnimateInput();
598 }
599
HasScrollbarJumpKeyModifier(const WebInputEvent & event)600 bool HasScrollbarJumpKeyModifier(const WebInputEvent& event) {
601 #if defined(OS_MAC)
602 // Mac uses the "Option" key (which is mapped to the enum "kAltKey").
603 return event.GetModifiers() & WebInputEvent::kAltKey;
604 #else
605 return event.GetModifiers() & WebInputEvent::kShiftKey;
606 #endif
607 }
608
609 InputHandlerProxy::EventDisposition
RouteToTypeSpecificHandler(EventWithCallback * event_with_callback,const WebInputEventAttribution & original_attribution)610 InputHandlerProxy::RouteToTypeSpecificHandler(
611 EventWithCallback* event_with_callback,
612 const WebInputEventAttribution& original_attribution) {
613 DCHECK(input_handler_);
614
615 cc::EventsMetricsManager::ScopedMonitor::DoneCallback done_callback;
616 if (event_with_callback->metrics()) {
617 done_callback = base::BindOnce(
618 [](EventWithCallback* event, bool handled) {
619 std::unique_ptr<cc::EventMetrics> result =
620 handled ? event->TakeMetrics() : nullptr;
621 return result;
622 },
623 event_with_callback);
624 }
625 auto scoped_event_monitor =
626 input_handler_->GetScopedEventMetricsMonitor(std::move(done_callback));
627
628 const WebInputEvent& event = event_with_callback->event();
629 if (event.IsGestureScroll() &&
630 (snap_fling_controller_->FilterEventForSnap(
631 GestureScrollEventType(event.GetType())))) {
632 return DROP_EVENT;
633 }
634
635 if (base::Optional<InputHandlerProxy::EventDisposition> handled =
636 cursor_control_handler_->ObserveInputEvent(event))
637 return *handled;
638
639 switch (event.GetType()) {
640 case WebInputEvent::Type::kMouseWheel:
641 return HandleMouseWheel(static_cast<const WebMouseWheelEvent&>(event));
642
643 case WebInputEvent::Type::kGestureScrollBegin:
644 return HandleGestureScrollBegin(
645 static_cast<const WebGestureEvent&>(event));
646
647 case WebInputEvent::Type::kGestureScrollUpdate:
648 return HandleGestureScrollUpdate(
649 static_cast<const WebGestureEvent&>(event), original_attribution,
650 event_with_callback->metrics());
651
652 case WebInputEvent::Type::kGestureScrollEnd:
653 return HandleGestureScrollEnd(static_cast<const WebGestureEvent&>(event));
654
655 case WebInputEvent::Type::kGesturePinchBegin: {
656 DCHECK(!gesture_pinch_in_progress_);
657 input_handler_->PinchGestureBegin();
658 gesture_pinch_in_progress_ = true;
659 return DID_HANDLE;
660 }
661
662 case WebInputEvent::Type::kGesturePinchEnd: {
663 DCHECK(gesture_pinch_in_progress_);
664 gesture_pinch_in_progress_ = false;
665 const WebGestureEvent& gesture_event =
666 static_cast<const WebGestureEvent&>(event);
667 input_handler_->PinchGestureEnd(
668 gfx::ToFlooredPoint(gesture_event.PositionInWidget()),
669 gesture_event.SourceDevice() == WebGestureDevice::kTouchpad);
670 return DID_HANDLE;
671 }
672
673 case WebInputEvent::Type::kGesturePinchUpdate: {
674 DCHECK(gesture_pinch_in_progress_);
675 const WebGestureEvent& gesture_event =
676 static_cast<const WebGestureEvent&>(event);
677 input_handler_->PinchGestureUpdate(
678 gesture_event.data.pinch_update.scale,
679 gfx::ToFlooredPoint(gesture_event.PositionInWidget()));
680 return DID_HANDLE;
681 }
682
683 case WebInputEvent::Type::kTouchStart:
684 return HandleTouchStart(event_with_callback);
685
686 case WebInputEvent::Type::kTouchMove:
687 return HandleTouchMove(event_with_callback);
688
689 case WebInputEvent::Type::kTouchEnd:
690 return HandleTouchEnd(event_with_callback);
691
692 case WebInputEvent::Type::kMouseDown: {
693 // Only for check scrollbar captured
694 const WebMouseEvent& mouse_event =
695 static_cast<const WebMouseEvent&>(event);
696
697 if (mouse_event.button == WebMouseEvent::Button::kLeft) {
698 CHECK(input_handler_);
699 // TODO(arakeri): Pass in the modifier instead of a bool once the
700 // refactor (crbug.com/1022097) is done. For details, see
701 // crbug.com/1016955.
702 HandlePointerDown(event_with_callback, mouse_event.PositionInWidget());
703 }
704
705 return DID_NOT_HANDLE;
706 }
707 case WebInputEvent::Type::kMouseUp: {
708 // Only for release scrollbar captured
709 const WebMouseEvent& mouse_event =
710 static_cast<const WebMouseEvent&>(event);
711 CHECK(input_handler_);
712 if (mouse_event.button == WebMouseEvent::Button::kLeft)
713 HandlePointerUp(event_with_callback, mouse_event.PositionInWidget());
714 return DID_NOT_HANDLE;
715 }
716 case WebInputEvent::Type::kMouseMove: {
717 const WebMouseEvent& mouse_event =
718 static_cast<const WebMouseEvent&>(event);
719 // TODO(davemoore): This should never happen, but bug #326635 showed some
720 // surprising crashes.
721 CHECK(input_handler_);
722 HandlePointerMove(event_with_callback, mouse_event.PositionInWidget());
723 return DID_NOT_HANDLE;
724 }
725 case WebInputEvent::Type::kMouseLeave: {
726 CHECK(input_handler_);
727 input_handler_->MouseLeave();
728 return DID_NOT_HANDLE;
729 }
730 // Fling gestures are handled only in the browser process and not sent to
731 // the renderer.
732 case WebInputEvent::Type::kGestureFlingStart:
733 case WebInputEvent::Type::kGestureFlingCancel:
734 NOTREACHED();
735 break;
736
737 default:
738 break;
739 }
740
741 return DID_NOT_HANDLE;
742 }
743
PerformEventAttribution(const WebInputEvent & event)744 WebInputEventAttribution InputHandlerProxy::PerformEventAttribution(
745 const WebInputEvent& event) {
746 if (!event_attribution_enabled_) {
747 return WebInputEventAttribution(WebInputEventAttribution::kUnknown);
748 }
749
750 if (WebInputEvent::IsKeyboardEventType(event.GetType())) {
751 // Keyboard events should be dispatched to the focused frame.
752 return WebInputEventAttribution(WebInputEventAttribution::kFocusedFrame);
753 } else if (WebInputEvent::IsMouseEventType(event.GetType()) ||
754 event.GetType() == WebInputEvent::Type::kMouseWheel) {
755 // Mouse events are dispatched based on their location in the DOM tree.
756 // Perform frame attribution via cc.
757 // TODO(acomminos): handle pointer locks, or provide a hint to the renderer
758 // to check pointer lock state
759 gfx::PointF point =
760 static_cast<const WebMouseEvent&>(event).PositionInWidget();
761 return WebInputEventAttribution(
762 WebInputEventAttribution::kTargetedFrame,
763 input_handler_->FindFrameElementIdAtPoint(point));
764 } else if (WebInputEvent::IsGestureEventType(event.GetType())) {
765 gfx::PointF point =
766 static_cast<const WebGestureEvent&>(event).PositionInWidget();
767 return WebInputEventAttribution(
768 WebInputEventAttribution::kTargetedFrame,
769 input_handler_->FindFrameElementIdAtPoint(point));
770 } else if (WebInputEvent::IsTouchEventType(event.GetType())) {
771 const auto& touch_event = static_cast<const WebTouchEvent&>(event);
772 if (touch_event.touches_length == 0) {
773 return WebInputEventAttribution(WebInputEventAttribution::kTargetedFrame,
774 cc::ElementId());
775 }
776
777 // Use the first touch location to perform frame attribution, similar to
778 // how the renderer host performs touch event dispatch.
779 // https://cs.chromium.org/chromium/src/content/browser/renderer_host/render_widget_host_input_event_router.cc?l=808&rcl=10fe9d0a725d4ed7b69266a5936c525f0a5b26d3
780 gfx::PointF point = touch_event.touches[0].PositionInWidget();
781 const cc::ElementId targeted_element =
782 input_handler_->FindFrameElementIdAtPoint(point);
783
784 return WebInputEventAttribution(WebInputEventAttribution::kTargetedFrame,
785 targeted_element);
786 } else {
787 return WebInputEventAttribution(WebInputEventAttribution::kUnknown);
788 }
789 }
790
RecordMainThreadScrollingReasons(WebGestureDevice device,uint32_t reasons)791 void InputHandlerProxy::RecordMainThreadScrollingReasons(
792 WebGestureDevice device,
793 uint32_t reasons) {
794 static const char* kGestureHistogramName =
795 "Renderer4.MainThreadGestureScrollReason";
796 static const char* kWheelHistogramName =
797 "Renderer4.MainThreadWheelScrollReason";
798
799 if (device != WebGestureDevice::kTouchpad &&
800 device != WebGestureDevice::kScrollbar &&
801 device != WebGestureDevice::kTouchscreen) {
802 return;
803 }
804
805 // NonCompositedScrollReasons should only be set on the main thread.
806 DCHECK(
807 !cc::MainThreadScrollingReason::HasNonCompositedScrollReasons(reasons));
808
809 // This records whether a scroll is handled on the main or compositor
810 // threads. Note: scrolls handled on the compositor but blocked on main due
811 // to event handlers are still considered compositor scrolls.
812 const bool is_compositor_scroll =
813 reasons == cc::MainThreadScrollingReason::kNotScrollingOnMain;
814
815 base::Optional<EventDisposition> disposition =
816 (device == WebGestureDevice::kTouchpad ? mouse_wheel_result_
817 : touch_result_);
818
819 // Scrolling can be handled on the compositor thread but it might be blocked
820 // on the main thread waiting for non-passive event handlers to process the
821 // wheel/touch events (i.e. were they preventDefaulted?).
822 bool blocked_on_main_thread_handler =
823 disposition.has_value() && disposition == DID_NOT_HANDLE;
824
825 auto scroll_start_state = RecordScrollingThread(
826 is_compositor_scroll, blocked_on_main_thread_handler, device);
827 input_handler_->RecordScrollBegin(GestureScrollInputType(device),
828 scroll_start_state);
829
830 if (blocked_on_main_thread_handler) {
831 // We should also collect main thread scrolling reasons if a scroll event
832 // scrolls on impl thread but is blocked by main thread event handlers.
833 reasons |= (device == WebGestureDevice::kTouchpad
834 ? cc::MainThreadScrollingReason::kWheelEventHandlerRegion
835 : cc::MainThreadScrollingReason::kTouchEventHandlerRegion);
836 }
837
838 // Note: This is slightly different from |is_compositor_scroll| above because
839 // at this point, we've also included wheel handler region reasons which will
840 // scroll on the compositor but require blocking on the main thread. The
841 // histograms below don't consider this "not scrolling on main".
842 const bool is_unblocked_compositor_scroll =
843 reasons == cc::MainThreadScrollingReason::kNotScrollingOnMain;
844
845 // UMA_HISTOGRAM_ENUMERATION requires that the enum_max must be strictly
846 // greater than the sample value. kMainThreadScrollingReasonCount doesn't
847 // include the NotScrollingOnMain enum but the histograms do so adding
848 // the +1 is necessary.
849 // TODO(dcheng): Fix https://crbug.com/705169 so this isn't needed.
850 constexpr uint32_t kMainThreadScrollingReasonEnumMax =
851 cc::MainThreadScrollingReason::kMainThreadScrollingReasonCount + 1;
852 if (is_unblocked_compositor_scroll) {
853 if (device == WebGestureDevice::kTouchscreen) {
854 UMA_HISTOGRAM_ENUMERATION(
855 kGestureHistogramName,
856 cc::MainThreadScrollingReason::kNotScrollingOnMain,
857 kMainThreadScrollingReasonEnumMax);
858 } else {
859 UMA_HISTOGRAM_ENUMERATION(
860 kWheelHistogramName,
861 cc::MainThreadScrollingReason::kNotScrollingOnMain,
862 kMainThreadScrollingReasonEnumMax);
863 }
864 }
865
866 for (uint32_t i = 0;
867 i < cc::MainThreadScrollingReason::kMainThreadScrollingReasonCount;
868 ++i) {
869 unsigned val = 1 << i;
870 if (reasons & val) {
871 if (val == cc::MainThreadScrollingReason::kHandlingScrollFromMainThread) {
872 // We only want to record "Handling scroll from main thread" reason if
873 // it's the only reason. If it's not the only reason, the "real" reason
874 // for scrolling on main is something else, and we only want to pay
875 // attention to that reason.
876 if (reasons & ~val)
877 continue;
878 }
879 if (device == WebGestureDevice::kTouchscreen) {
880 UMA_HISTOGRAM_ENUMERATION(kGestureHistogramName, i + 1,
881 kMainThreadScrollingReasonEnumMax);
882 } else {
883 UMA_HISTOGRAM_ENUMERATION(kWheelHistogramName, i + 1,
884 kMainThreadScrollingReasonEnumMax);
885 }
886 }
887 }
888 }
889
HandleMouseWheel(const WebMouseWheelEvent & wheel_event)890 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel(
891 const WebMouseWheelEvent& wheel_event) {
892 InputHandlerProxy::EventDisposition result = DROP_EVENT;
893
894 if (wheel_event.dispatch_type ==
895 WebInputEvent::DispatchType::kEventNonBlocking) {
896 // The first wheel event in the sequence should be cancellable.
897 DCHECK(wheel_event.phase != WebMouseWheelEvent::kPhaseBegan);
898 // Noncancellable wheel events should have phase info.
899 DCHECK(wheel_event.phase != WebMouseWheelEvent::kPhaseNone ||
900 wheel_event.momentum_phase != WebMouseWheelEvent::kPhaseNone);
901
902 DCHECK(mouse_wheel_result_.has_value());
903 // TODO(bokan): This should never happen but after changing
904 // mouse_event_result_ to a base::Optional, crashes indicate that it does
905 // so |if| maintains prior behavior. https://crbug.com/1069760.
906 if (mouse_wheel_result_.has_value()) {
907 result = mouse_wheel_result_.value();
908 if (wheel_event.phase == WebMouseWheelEvent::kPhaseEnded ||
909 wheel_event.phase == WebMouseWheelEvent::kPhaseCancelled ||
910 wheel_event.momentum_phase == WebMouseWheelEvent::kPhaseEnded ||
911 wheel_event.momentum_phase == WebMouseWheelEvent::kPhaseCancelled) {
912 mouse_wheel_result_.reset();
913 } else {
914 return result;
915 }
916 }
917 }
918
919 gfx::PointF position_in_widget = wheel_event.PositionInWidget();
920 if (input_handler_->HasBlockingWheelEventHandlerAt(
921 gfx::Point(position_in_widget.x(), position_in_widget.y()))) {
922 result = DID_NOT_HANDLE;
923 } else {
924 cc::EventListenerProperties properties =
925 input_handler_->GetEventListenerProperties(
926 cc::EventListenerClass::kMouseWheel);
927 switch (properties) {
928 case cc::EventListenerProperties::kBlockingAndPassive:
929 case cc::EventListenerProperties::kPassive:
930 result = DID_HANDLE_NON_BLOCKING;
931 break;
932 case cc::EventListenerProperties::kNone:
933 result = DROP_EVENT;
934 break;
935 default:
936 // If properties is kBlocking, and the event falls outside wheel event
937 // handler region, we should handle it the same as kNone.
938 result = DROP_EVENT;
939 }
940 }
941
942 mouse_wheel_result_ = result;
943 return result;
944 }
945
HandleGestureScrollBegin(const WebGestureEvent & gesture_event)946 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin(
947 const WebGestureEvent& gesture_event) {
948 TRACE_EVENT0("input", "InputHandlerProxy::HandleGestureScrollBegin");
949
950 if (scroll_predictor_)
951 scroll_predictor_->ResetOnGestureScrollBegin(gesture_event);
952
953 // When a GSB is being handled, end any pre-existing gesture scrolls that are
954 // in progress.
955 if (currently_active_gesture_device_.has_value() &&
956 handling_gesture_on_impl_thread_) {
957 // TODO(arakeri): Once crbug.com/1074209 is fixed, delete calls to
958 // RecordScrollEnd.
959 input_handler_->RecordScrollEnd(
960 GestureScrollInputType(*currently_active_gesture_device_));
961 InputHandlerScrollEnd();
962 }
963
964 cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event);
965 cc::InputHandler::ScrollStatus scroll_status;
966 if (gesture_event.data.scroll_begin.target_viewport) {
967 scroll_status = input_handler_->RootScrollBegin(
968 &scroll_state, GestureScrollInputType(gesture_event.SourceDevice()));
969 } else {
970 scroll_status = input_handler_->ScrollBegin(
971 &scroll_state, GestureScrollInputType(gesture_event.SourceDevice()));
972 }
973
974 // If we need a hit test from the main thread, we'll reinject this scroll
975 // begin event once the hit test is complete so avoid everything below for
976 // now, it'll be run on the second iteration.
977 if (scroll_status.needs_main_thread_hit_test) {
978 DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
979 hit_testing_scroll_begin_on_main_thread_ = true;
980 return REQUIRES_MAIN_THREAD_HIT_TEST;
981 }
982
983 RecordMainThreadScrollingReasons(gesture_event.SourceDevice(),
984 scroll_status.main_thread_scrolling_reasons);
985
986 InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE;
987 scroll_sequence_ignored_ = false;
988 in_inertial_scrolling_ = false;
989 switch (scroll_status.thread) {
990 case ScrollThread::SCROLL_ON_IMPL_THREAD:
991 TRACE_EVENT_INSTANT0("input", "Handle On Impl", TRACE_EVENT_SCOPE_THREAD);
992 handling_gesture_on_impl_thread_ = true;
993 if (input_handler_->IsCurrentlyScrollingViewport())
994 client_->DidStartScrollingViewport();
995
996 if (scroll_status.bubble)
997 result = DID_HANDLE_SHOULD_BUBBLE;
998 else
999 result = DID_HANDLE;
1000 break;
1001 case ScrollThread::SCROLL_UNKNOWN:
1002 case ScrollThread::SCROLL_ON_MAIN_THREAD:
1003 TRACE_EVENT_INSTANT0("input", "Handle On Main", TRACE_EVENT_SCOPE_THREAD);
1004 result = DID_NOT_HANDLE;
1005 break;
1006 case ScrollThread::SCROLL_IGNORED:
1007 TRACE_EVENT_INSTANT0("input", "Ignore Scroll", TRACE_EVENT_SCOPE_THREAD);
1008 scroll_sequence_ignored_ = true;
1009 result = DROP_EVENT;
1010 break;
1011 }
1012
1013 // TODO(bokan): Should we really be calling this in cases like DROP_EVENT and
1014 // DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING? I think probably not.
1015 if (elastic_overscroll_controller_ && result != DID_NOT_HANDLE) {
1016 HandleScrollElasticityOverscroll(gesture_event,
1017 cc::InputHandlerScrollResult());
1018 }
1019
1020 return result;
1021 }
1022
1023 InputHandlerProxy::EventDisposition
HandleGestureScrollUpdate(const WebGestureEvent & gesture_event,const WebInputEventAttribution & original_attribution,const cc::EventMetrics * original_metrics)1024 InputHandlerProxy::HandleGestureScrollUpdate(
1025 const WebGestureEvent& gesture_event,
1026 const WebInputEventAttribution& original_attribution,
1027 const cc::EventMetrics* original_metrics) {
1028 TRACE_EVENT2("input", "InputHandlerProxy::HandleGestureScrollUpdate", "dx",
1029 -gesture_event.data.scroll_update.delta_x, "dy",
1030 -gesture_event.data.scroll_update.delta_y);
1031
1032 if (scroll_sequence_ignored_) {
1033 TRACE_EVENT_INSTANT0("input", "Scroll Sequence Ignored",
1034 TRACE_EVENT_SCOPE_THREAD);
1035 return DROP_EVENT;
1036 }
1037
1038 if (!handling_gesture_on_impl_thread_ && !gesture_pinch_in_progress_)
1039 return DID_NOT_HANDLE;
1040
1041 cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event);
1042 in_inertial_scrolling_ = scroll_state.is_in_inertial_phase();
1043
1044 TRACE_EVENT_INSTANT1(
1045 "input", "DeltaUnits", TRACE_EVENT_SCOPE_THREAD, "unit",
1046 static_cast<int>(gesture_event.data.scroll_update.delta_units));
1047
1048 if (snap_fling_controller_->HandleGestureScrollUpdate(
1049 GetGestureScrollUpdateInfo(gesture_event))) {
1050 handling_gesture_on_impl_thread_ = false;
1051 return DROP_EVENT;
1052 }
1053
1054 if (!base::FeatureList::IsEnabled(::features::kScrollUnification) &&
1055 input_handler_->ScrollingShouldSwitchtoMainThread()) {
1056 TRACE_EVENT_INSTANT0("input", "Move Scroll To Main Thread",
1057 TRACE_EVENT_SCOPE_THREAD);
1058 handling_gesture_on_impl_thread_ = false;
1059 currently_active_gesture_device_ = base::nullopt;
1060 client_->GenerateScrollBeginAndSendToMainThread(
1061 gesture_event, original_attribution, original_metrics);
1062
1063 // TODO(bokan): |!gesture_pinch_in_progress_| was put here by
1064 // https://crrev.com/2720903005 but it's not clear to me how this is
1065 // supposed to work - we already generated and sent a GSB to the main
1066 // thread above so it's odd to continue handling on the compositor thread
1067 // if a pinch was in progress. It probably makes more sense to bake this
1068 // condition into ScrollingShouldSwitchToMainThread().
1069 if (!gesture_pinch_in_progress_)
1070 return DID_NOT_HANDLE;
1071 }
1072
1073 base::TimeTicks event_time = gesture_event.TimeStamp();
1074 base::TimeDelta delay = base::TimeTicks::Now() - event_time;
1075
1076 cc::InputHandlerScrollResult scroll_result =
1077 input_handler_->ScrollUpdate(&scroll_state, delay);
1078
1079 HandleOverscroll(gesture_event.PositionInWidget(), scroll_result);
1080
1081 if (elastic_overscroll_controller_)
1082 HandleScrollElasticityOverscroll(gesture_event, scroll_result);
1083
1084 return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
1085 }
1086
1087 // TODO(arakeri): Ensure that redudant GSE(s) in the CompositorThreadEventQueue
1088 // are handled gracefully. (i.e currently, when an ongoing scroll needs to end,
1089 // we call RecordScrollEnd and InputHandlerScrollEnd synchronously. Ideally, we
1090 // should end the scroll when the GSB is being handled).
HandleGestureScrollEnd(const WebGestureEvent & gesture_event)1091 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd(
1092 const WebGestureEvent& gesture_event) {
1093 TRACE_EVENT0("input", "InputHandlerProxy::HandleGestureScrollEnd");
1094
1095 // TODO(bokan): It seems odd that we'd record a ScrollEnd for a scroll
1096 // secuence that was ignored (i.e. the ScrollBegin was dropped). However,
1097 // RecordScrollBegin does get called in that case so this needs to be this
1098 // way for now. This makes life rather awkward for the unified scrolling path
1099 // so perhaps we should only record a scrolling thread if a scroll actually
1100 // started? https://crbug.com/1082601.
1101 input_handler_->RecordScrollEnd(
1102 GestureScrollInputType(gesture_event.SourceDevice()));
1103
1104 if (scroll_sequence_ignored_) {
1105 DCHECK(!currently_active_gesture_device_.has_value());
1106 return DROP_EVENT;
1107 }
1108
1109 if (!handling_gesture_on_impl_thread_) {
1110 DCHECK(!currently_active_gesture_device_.has_value());
1111 return DID_NOT_HANDLE;
1112 }
1113
1114 if (!currently_active_gesture_device_.has_value() ||
1115 (currently_active_gesture_device_.value() !=
1116 gesture_event.SourceDevice()))
1117 return DROP_EVENT;
1118
1119 InputHandlerScrollEnd();
1120 if (elastic_overscroll_controller_) {
1121 HandleScrollElasticityOverscroll(gesture_event,
1122 cc::InputHandlerScrollResult());
1123 }
1124
1125 return DID_HANDLE;
1126 }
1127
InputHandlerScrollEnd()1128 void InputHandlerProxy::InputHandlerScrollEnd() {
1129 input_handler_->ScrollEnd(/*should_snap=*/true);
1130 handling_gesture_on_impl_thread_ = false;
1131
1132 DCHECK(!gesture_pinch_in_progress_);
1133 currently_active_gesture_device_ = base::nullopt;
1134 }
1135
HitTestTouchEvent(const WebTouchEvent & touch_event,bool * is_touching_scrolling_layer,cc::TouchAction * allowed_touch_action)1136 InputHandlerProxy::EventDisposition InputHandlerProxy::HitTestTouchEvent(
1137 const WebTouchEvent& touch_event,
1138 bool* is_touching_scrolling_layer,
1139 cc::TouchAction* allowed_touch_action) {
1140 TRACE_EVENT1("input", "InputHandlerProxy::HitTestTouchEvent",
1141 "Needs allowed TouchAction",
1142 static_cast<bool>(allowed_touch_action));
1143 *is_touching_scrolling_layer = false;
1144 EventDisposition result = DROP_EVENT;
1145 for (size_t i = 0; i < touch_event.touches_length; ++i) {
1146 if (touch_event.touch_start_or_first_touch_move)
1147 DCHECK(allowed_touch_action);
1148 else
1149 DCHECK(!allowed_touch_action);
1150
1151 if (touch_event.GetType() == WebInputEvent::Type::kTouchStart &&
1152 touch_event.touches[i].state != WebTouchPoint::State::kStatePressed) {
1153 continue;
1154 }
1155
1156 cc::TouchAction touch_action = cc::TouchAction::kAuto;
1157 cc::InputHandler::TouchStartOrMoveEventListenerType event_listener_type =
1158 input_handler_->EventListenerTypeForTouchStartOrMoveAt(
1159 gfx::Point(touch_event.touches[i].PositionInWidget().x(),
1160 touch_event.touches[i].PositionInWidget().y()),
1161 &touch_action);
1162 if (allowed_touch_action && touch_action != cc::TouchAction::kAuto) {
1163 TRACE_EVENT_INSTANT1("input", "Adding TouchAction",
1164 TRACE_EVENT_SCOPE_THREAD, "TouchAction",
1165 cc::TouchActionToString(touch_action));
1166 *allowed_touch_action &= touch_action;
1167 }
1168
1169 if (event_listener_type !=
1170 cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER) {
1171 TRACE_EVENT_INSTANT1("input", "HaveHandler", TRACE_EVENT_SCOPE_THREAD,
1172 "Type", event_listener_type);
1173
1174 *is_touching_scrolling_layer =
1175 event_listener_type ==
1176 cc::InputHandler::TouchStartOrMoveEventListenerType::
1177 HANDLER_ON_SCROLLING_LAYER;
1178
1179 // A non-passive touch start / move will always set the allowed touch
1180 // action to TouchAction::kNone, and in that case we do not ack the event
1181 // from the compositor.
1182 if (allowed_touch_action &&
1183 *allowed_touch_action != cc::TouchAction::kNone) {
1184 TRACE_EVENT_INSTANT0("input", "NonBlocking due to allowed touchaction",
1185 TRACE_EVENT_SCOPE_THREAD);
1186 result = DID_HANDLE_NON_BLOCKING;
1187 } else {
1188 TRACE_EVENT_INSTANT0("input", "DidNotHandle due to no touchaction",
1189 TRACE_EVENT_SCOPE_THREAD);
1190 result = DID_NOT_HANDLE;
1191 }
1192 break;
1193 }
1194 }
1195
1196 // If |result| is DROP_EVENT it wasn't processed above.
1197 if (result == DROP_EVENT) {
1198 auto event_listener_class = input_handler_->GetEventListenerProperties(
1199 cc::EventListenerClass::kTouchStartOrMove);
1200 TRACE_EVENT_INSTANT1("input", "DropEvent", TRACE_EVENT_SCOPE_THREAD,
1201 "listener", event_listener_class);
1202 switch (event_listener_class) {
1203 case cc::EventListenerProperties::kPassive:
1204 result = DID_HANDLE_NON_BLOCKING;
1205 break;
1206 case cc::EventListenerProperties::kBlocking:
1207 // The touch area rects above already have checked whether it hits
1208 // a blocking region. Since it does not the event can be dropped.
1209 result = DROP_EVENT;
1210 break;
1211 case cc::EventListenerProperties::kBlockingAndPassive:
1212 // There is at least one passive listener that needs to possibly
1213 // be notified so it can't be dropped.
1214 result = DID_HANDLE_NON_BLOCKING;
1215 break;
1216 case cc::EventListenerProperties::kNone:
1217 result = DROP_EVENT;
1218 break;
1219 default:
1220 NOTREACHED();
1221 result = DROP_EVENT;
1222 break;
1223 }
1224 }
1225
1226 // Depending on which arm of the SkipTouchEventFilter experiment we're on, we
1227 // may need to simulate a passive listener instead of dropping touch events.
1228 if (result == DROP_EVENT &&
1229 (skip_touch_filter_all_ ||
1230 (skip_touch_filter_discrete_ &&
1231 touch_event.GetType() == WebInputEvent::Type::kTouchStart))) {
1232 TRACE_EVENT_INSTANT0("input", "Non blocking due to skip filter",
1233 TRACE_EVENT_SCOPE_THREAD);
1234 result = DID_HANDLE_NON_BLOCKING;
1235 }
1236
1237 // Merge |touch_result_| and |result| so the result has the highest
1238 // priority value according to the sequence; (DROP_EVENT,
1239 // DID_HANDLE_NON_BLOCKING, DID_NOT_HANDLE).
1240 if (!touch_result_.has_value() || touch_result_ == DROP_EVENT ||
1241 result == DID_NOT_HANDLE) {
1242 TRACE_EVENT_INSTANT2(
1243 "input", "Update touch_result_", TRACE_EVENT_SCOPE_THREAD, "old",
1244 (touch_result_ ? touch_result_.value() : -1), "new", result);
1245 touch_result_ = result;
1246 }
1247
1248 return result;
1249 }
1250
HandleTouchStart(EventWithCallback * event_with_callback)1251 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart(
1252 EventWithCallback* event_with_callback) {
1253 TRACE_EVENT0("input", "InputHandlerProxy::HandleTouchStart");
1254 const auto& touch_event =
1255 static_cast<const WebTouchEvent&>(event_with_callback->event());
1256
1257 bool is_touching_scrolling_layer;
1258 cc::TouchAction allowed_touch_action = cc::TouchAction::kAuto;
1259 EventDisposition result = HitTestTouchEvent(
1260 touch_event, &is_touching_scrolling_layer, &allowed_touch_action);
1261 TRACE_EVENT_INSTANT1("input", "HitTest", TRACE_EVENT_SCOPE_THREAD,
1262 "disposition", result);
1263
1264 if (allowed_touch_action != cc::TouchAction::kNone &&
1265 touch_event.touches_length == 1) {
1266 DCHECK(touch_event.touches[0].state == WebTouchPoint::State::kStatePressed);
1267 cc::InputHandlerPointerResult pointer_result = HandlePointerDown(
1268 event_with_callback, touch_event.touches[0].PositionInWidget());
1269 if (pointer_result.type == cc::PointerResultType::kScrollbarScroll) {
1270 client_->SetAllowedTouchAction(
1271 allowed_touch_action, touch_event.unique_touch_event_id, DID_HANDLE);
1272 return DID_HANDLE;
1273 }
1274 }
1275
1276 // If |result| is still DROP_EVENT look at the touch end handler as we may
1277 // not want to discard the entire touch sequence. Note this code is
1278 // explicitly after the assignment of the |touch_result_| in
1279 // HitTestTouchEvent so the touch moves are not sent to the main thread
1280 // un-necessarily.
1281 if (result == DROP_EVENT && input_handler_->GetEventListenerProperties(
1282 cc::EventListenerClass::kTouchEndOrCancel) !=
1283 cc::EventListenerProperties::kNone) {
1284 TRACE_EVENT_INSTANT0("input", "NonBlocking due to TouchEnd handler",
1285 TRACE_EVENT_SCOPE_THREAD);
1286 result = DID_HANDLE_NON_BLOCKING;
1287 }
1288
1289 bool is_in_inertial_scrolling_on_impl =
1290 in_inertial_scrolling_ && handling_gesture_on_impl_thread_;
1291 if (is_in_inertial_scrolling_on_impl && is_touching_scrolling_layer) {
1292 // If the touchstart occurs during a fling, it will be ACK'd immediately
1293 // and it and its following touch moves will be dispatched as non-blocking.
1294 // Due to tap suppression on the browser side, this will reset the
1295 // browser-side touch action (see comment in
1296 // TouchActionFilter::FilterGestureEvent for GestureScrollBegin). Ensure we
1297 // send back an allowed_touch_action that matches this non-blocking behavior
1298 // rather than treating it as if it'll block.
1299 TRACE_EVENT_INSTANT0("input", "NonBlocking due to fling",
1300 TRACE_EVENT_SCOPE_THREAD);
1301 allowed_touch_action = cc::TouchAction::kAuto;
1302 result = DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING;
1303 }
1304
1305 TRACE_EVENT_INSTANT2(
1306 "input", "Allowed TouchAction", TRACE_EVENT_SCOPE_THREAD, "TouchAction",
1307 cc::TouchActionToString(allowed_touch_action), "disposition", result);
1308 client_->SetAllowedTouchAction(allowed_touch_action,
1309 touch_event.unique_touch_event_id, result);
1310
1311 return result;
1312 }
1313
HandleTouchMove(EventWithCallback * event_with_callback)1314 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchMove(
1315 EventWithCallback* event_with_callback) {
1316 const auto& touch_event =
1317 static_cast<const WebTouchEvent&>(event_with_callback->event());
1318 TRACE_EVENT2("input", "InputHandlerProxy::HandleTouchMove", "touch_result",
1319 touch_result_.has_value() ? touch_result_.value() : -1,
1320 "is_start_or_first",
1321 touch_event.touch_start_or_first_touch_move);
1322 if (touch_event.touches_length == 1) {
1323 cc::InputHandlerPointerResult pointer_result = HandlePointerMove(
1324 event_with_callback, touch_event.touches[0].PositionInWidget());
1325 if (pointer_result.type == cc::PointerResultType::kScrollbarScroll) {
1326 return DID_HANDLE;
1327 }
1328 }
1329 // Hit test if this is the first touch move or we don't have any results
1330 // from a previous hit test.
1331 if (!touch_result_.has_value() ||
1332 touch_event.touch_start_or_first_touch_move) {
1333 bool is_touching_scrolling_layer;
1334 cc::TouchAction allowed_touch_action = cc::TouchAction::kAuto;
1335 EventDisposition result = HitTestTouchEvent(
1336 touch_event, &is_touching_scrolling_layer, &allowed_touch_action);
1337 TRACE_EVENT_INSTANT2(
1338 "input", "Allowed TouchAction", TRACE_EVENT_SCOPE_THREAD, "TouchAction",
1339 cc::TouchActionToString(allowed_touch_action), "disposition", result);
1340 client_->SetAllowedTouchAction(allowed_touch_action,
1341 touch_event.unique_touch_event_id, result);
1342 return result;
1343 }
1344 return touch_result_.value();
1345 }
1346
HandleTouchEnd(EventWithCallback * event_with_callback)1347 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchEnd(
1348 EventWithCallback* event_with_callback) {
1349 const auto& touch_event =
1350 static_cast<const WebTouchEvent&>(event_with_callback->event());
1351 TRACE_EVENT1("input", "InputHandlerProxy::HandleTouchEnd", "num_touches",
1352 touch_event.touches_length);
1353 if (touch_event.touches_length == 1) {
1354 cc::InputHandlerPointerResult pointer_result = HandlePointerUp(
1355 event_with_callback, touch_event.touches[0].PositionInWidget());
1356 if (pointer_result.type == cc::PointerResultType::kScrollbarScroll) {
1357 return DID_HANDLE;
1358 }
1359 }
1360 if (touch_event.touches_length == 1)
1361 touch_result_.reset();
1362 return DID_NOT_HANDLE;
1363 }
1364
Animate(base::TimeTicks time)1365 void InputHandlerProxy::Animate(base::TimeTicks time) {
1366 if (elastic_overscroll_controller_)
1367 elastic_overscroll_controller_->Animate(time);
1368
1369 snap_fling_controller_->Animate(time);
1370
1371 // These animations can change the root scroll offset, so inform the
1372 // synchronous input handler.
1373 if (synchronous_input_handler_)
1374 input_handler_->RequestUpdateForSynchronousInputHandler();
1375 }
1376
ReconcileElasticOverscrollAndRootScroll()1377 void InputHandlerProxy::ReconcileElasticOverscrollAndRootScroll() {
1378 if (elastic_overscroll_controller_)
1379 elastic_overscroll_controller_->ReconcileStretchAndScroll();
1380 }
1381
UpdateRootLayerStateForSynchronousInputHandler(const gfx::ScrollOffset & total_scroll_offset,const gfx::ScrollOffset & max_scroll_offset,const gfx::SizeF & scrollable_size,float page_scale_factor,float min_page_scale_factor,float max_page_scale_factor)1382 void InputHandlerProxy::UpdateRootLayerStateForSynchronousInputHandler(
1383 const gfx::ScrollOffset& total_scroll_offset,
1384 const gfx::ScrollOffset& max_scroll_offset,
1385 const gfx::SizeF& scrollable_size,
1386 float page_scale_factor,
1387 float min_page_scale_factor,
1388 float max_page_scale_factor) {
1389 if (synchronous_input_handler_) {
1390 synchronous_input_handler_->UpdateRootLayerState(
1391 total_scroll_offset, max_scroll_offset, scrollable_size,
1392 page_scale_factor, min_page_scale_factor, max_page_scale_factor);
1393 }
1394 }
1395
DeliverInputForBeginFrame(const viz::BeginFrameArgs & args)1396 void InputHandlerProxy::DeliverInputForBeginFrame(
1397 const viz::BeginFrameArgs& args) {
1398 // Block flushing the compositor gesture event queue while there's an async
1399 // scroll begin hit test outstanding. We'll flush the queue when the hit test
1400 // responds.
1401 if (hit_testing_scroll_begin_on_main_thread_) {
1402 DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
1403 return;
1404 }
1405
1406 if (!scroll_predictor_)
1407 DispatchQueuedInputEvents();
1408
1409 // Resampling GSUs and dispatch queued input events.
1410 while (!compositor_event_queue_->empty()) {
1411 std::unique_ptr<EventWithCallback> event_with_callback =
1412 scroll_predictor_->ResampleScrollEvents(compositor_event_queue_->Pop(),
1413 args.frame_time);
1414
1415 DispatchSingleInputEvent(std::move(event_with_callback), args.frame_time);
1416 }
1417 }
1418
DeliverInputForHighLatencyMode()1419 void InputHandlerProxy::DeliverInputForHighLatencyMode() {
1420 // When prediction enabled, do not handle input after commit complete.
1421 if (!scroll_predictor_)
1422 DispatchQueuedInputEvents();
1423 }
1424
SetSynchronousInputHandler(SynchronousInputHandler * synchronous_input_handler)1425 void InputHandlerProxy::SetSynchronousInputHandler(
1426 SynchronousInputHandler* synchronous_input_handler) {
1427 synchronous_input_handler_ = synchronous_input_handler;
1428 if (synchronous_input_handler_)
1429 input_handler_->RequestUpdateForSynchronousInputHandler();
1430 }
1431
SynchronouslySetRootScrollOffset(const gfx::ScrollOffset & root_offset)1432 void InputHandlerProxy::SynchronouslySetRootScrollOffset(
1433 const gfx::ScrollOffset& root_offset) {
1434 DCHECK(synchronous_input_handler_);
1435 input_handler_->SetSynchronousInputHandlerRootScrollOffset(root_offset);
1436 }
1437
SynchronouslyZoomBy(float magnify_delta,const gfx::Point & anchor)1438 void InputHandlerProxy::SynchronouslyZoomBy(float magnify_delta,
1439 const gfx::Point& anchor) {
1440 DCHECK(synchronous_input_handler_);
1441 input_handler_->PinchGestureBegin();
1442 input_handler_->PinchGestureUpdate(magnify_delta, anchor);
1443 input_handler_->PinchGestureEnd(anchor, false);
1444 }
1445
GetSnapFlingInfoAndSetAnimatingSnapTarget(const gfx::Vector2dF & natural_displacement,gfx::Vector2dF * initial_offset,gfx::Vector2dF * target_offset) const1446 bool InputHandlerProxy::GetSnapFlingInfoAndSetAnimatingSnapTarget(
1447 const gfx::Vector2dF& natural_displacement,
1448 gfx::Vector2dF* initial_offset,
1449 gfx::Vector2dF* target_offset) const {
1450 return input_handler_->GetSnapFlingInfoAndSetAnimatingSnapTarget(
1451 natural_displacement, initial_offset, target_offset);
1452 }
1453
ScrollByForSnapFling(const gfx::Vector2dF & delta)1454 gfx::Vector2dF InputHandlerProxy::ScrollByForSnapFling(
1455 const gfx::Vector2dF& delta) {
1456 cc::ScrollState scroll_state = CreateScrollStateForInertialUpdate(delta);
1457
1458 cc::InputHandlerScrollResult scroll_result =
1459 input_handler_->ScrollUpdate(&scroll_state, base::TimeDelta());
1460 return scroll_result.current_visual_offset;
1461 }
1462
ScrollEndForSnapFling(bool did_finish)1463 void InputHandlerProxy::ScrollEndForSnapFling(bool did_finish) {
1464 input_handler_->ScrollEndForSnapFling(did_finish);
1465 }
1466
RequestAnimationForSnapFling()1467 void InputHandlerProxy::RequestAnimationForSnapFling() {
1468 RequestAnimation();
1469 }
1470
HandleOverscroll(const gfx::PointF & causal_event_viewport_point,const cc::InputHandlerScrollResult & scroll_result)1471 void InputHandlerProxy::HandleOverscroll(
1472 const gfx::PointF& causal_event_viewport_point,
1473 const cc::InputHandlerScrollResult& scroll_result) {
1474 DCHECK(client_);
1475 if (!scroll_result.did_overscroll_root)
1476 return;
1477
1478 TRACE_EVENT2("input", "InputHandlerProxy::DidOverscroll", "dx",
1479 scroll_result.unused_scroll_delta.x(), "dy",
1480 scroll_result.unused_scroll_delta.y());
1481
1482 // Bundle overscroll message with triggering event response, saving an IPC.
1483 current_overscroll_params_ = std::make_unique<DidOverscrollParams>();
1484 current_overscroll_params_->accumulated_overscroll =
1485 scroll_result.accumulated_root_overscroll;
1486 current_overscroll_params_->latest_overscroll_delta =
1487 scroll_result.unused_scroll_delta;
1488 current_overscroll_params_->causal_event_viewport_point =
1489 causal_event_viewport_point;
1490 current_overscroll_params_->overscroll_behavior =
1491 scroll_result.overscroll_behavior;
1492 return;
1493 }
1494
RequestAnimation()1495 void InputHandlerProxy::RequestAnimation() {
1496 input_handler_->SetNeedsAnimateInput();
1497 }
1498
HandleScrollElasticityOverscroll(const WebGestureEvent & gesture_event,const cc::InputHandlerScrollResult & scroll_result)1499 void InputHandlerProxy::HandleScrollElasticityOverscroll(
1500 const WebGestureEvent& gesture_event,
1501 const cc::InputHandlerScrollResult& scroll_result) {
1502 DCHECK(elastic_overscroll_controller_);
1503 elastic_overscroll_controller_->ObserveGestureEventAndResult(gesture_event,
1504 scroll_result);
1505 }
1506
SetTickClockForTesting(const base::TickClock * tick_clock)1507 void InputHandlerProxy::SetTickClockForTesting(
1508 const base::TickClock* tick_clock) {
1509 tick_clock_ = tick_clock;
1510 }
1511
HandlePointerDown(EventWithCallback * event_with_callback,const gfx::PointF & position)1512 const cc::InputHandlerPointerResult InputHandlerProxy::HandlePointerDown(
1513 EventWithCallback* event_with_callback,
1514 const gfx::PointF& position) {
1515 CHECK(input_handler_);
1516 // TODO(arakeri): Pass in the modifier instead of a bool once the
1517 // refactor (crbug.com/1022097) is done. For details, see
1518 // crbug.com/1016955.
1519 cc::InputHandlerPointerResult pointer_result = input_handler_->MouseDown(
1520 position, HasScrollbarJumpKeyModifier(event_with_callback->event()));
1521 if (pointer_result.type == cc::PointerResultType::kScrollbarScroll) {
1522 // Since a kScrollbarScroll is about to commence, ensure that any
1523 // existing ongoing scroll is ended.
1524 if (currently_active_gesture_device_.has_value()) {
1525 DCHECK_NE(*currently_active_gesture_device_,
1526 WebGestureDevice::kUninitialized);
1527 if (gesture_pinch_in_progress_) {
1528 input_handler_->PinchGestureEnd(gfx::ToFlooredPoint(position), true);
1529 }
1530 if (handling_gesture_on_impl_thread_) {
1531 input_handler_->RecordScrollEnd(
1532 GestureScrollInputType(*currently_active_gesture_device_));
1533 InputHandlerScrollEnd();
1534 }
1535 }
1536
1537 // Generate GSB and GSU events and add them to the
1538 // CompositorThreadEventQueue.
1539 // Note that the latency info passed in to
1540 // InjectScrollbarGestureScroll is the original LatencyInfo, not the
1541 // one that may be currently monitored. The currently monitored one
1542 // may be modified by the call to InjectScrollbarGestureScroll, as
1543 // it will SetNeedsAnimateInput if the CompositorThreadEventQueue is
1544 // currently empty.
1545 InjectScrollbarGestureScroll(WebInputEvent::Type::kGestureScrollBegin,
1546 position, pointer_result,
1547 event_with_callback->latency_info(),
1548 event_with_callback->event().TimeStamp(),
1549 event_with_callback->metrics());
1550
1551 // Don't need to inject GSU if the scroll offset is zero (this can
1552 // be the case where mouse down occurs on the thumb).
1553 if (!pointer_result.scroll_offset.IsZero()) {
1554 InjectScrollbarGestureScroll(WebInputEvent::Type::kGestureScrollUpdate,
1555 position, pointer_result,
1556 event_with_callback->latency_info(),
1557 event_with_callback->event().TimeStamp(),
1558 event_with_callback->metrics());
1559 }
1560
1561 if (event_with_callback) {
1562 event_with_callback->SetScrollbarManipulationHandledOnCompositorThread();
1563 }
1564 }
1565
1566 return pointer_result;
1567 }
1568
HandlePointerMove(EventWithCallback * event_with_callback,const gfx::PointF & position)1569 const cc::InputHandlerPointerResult InputHandlerProxy::HandlePointerMove(
1570 EventWithCallback* event_with_callback,
1571 const gfx::PointF& position) {
1572 cc::InputHandlerPointerResult pointer_result =
1573 input_handler_->MouseMoveAt(gfx::Point(position.x(), position.y()));
1574 if (pointer_result.type == cc::PointerResultType::kScrollbarScroll) {
1575 // Generate a GSU event and add it to the CompositorThreadEventQueue if
1576 // delta is non zero.
1577 if (!pointer_result.scroll_offset.IsZero()) {
1578 InjectScrollbarGestureScroll(WebInputEvent::Type::kGestureScrollUpdate,
1579 position, pointer_result,
1580 event_with_callback->latency_info(),
1581 event_with_callback->event().TimeStamp(),
1582 event_with_callback->metrics());
1583 }
1584 if (event_with_callback) {
1585 event_with_callback->SetScrollbarManipulationHandledOnCompositorThread();
1586 }
1587 }
1588 return pointer_result;
1589 }
1590
HandlePointerUp(EventWithCallback * event_with_callback,const gfx::PointF & position)1591 const cc::InputHandlerPointerResult InputHandlerProxy::HandlePointerUp(
1592 EventWithCallback* event_with_callback,
1593 const gfx::PointF& position) {
1594 cc::InputHandlerPointerResult pointer_result =
1595 input_handler_->MouseUp(position);
1596 if (pointer_result.type == cc::PointerResultType::kScrollbarScroll) {
1597 // Generate a GSE and add it to the CompositorThreadEventQueue.
1598 InjectScrollbarGestureScroll(WebInputEvent::Type::kGestureScrollEnd,
1599 position, pointer_result,
1600 event_with_callback->latency_info(),
1601 event_with_callback->event().TimeStamp(),
1602 event_with_callback->metrics());
1603 if (event_with_callback) {
1604 event_with_callback->SetScrollbarManipulationHandledOnCompositorThread();
1605 }
1606 }
1607 return pointer_result;
1608 }
1609
1610 } // namespace blink
1611