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