1 // Copyright 2015 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/renderer/platform/widget/input/widget_base_input_handler.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <utility>
10 
11 #include "base/metrics/histogram_macros.h"
12 #include "build/build_config.h"
13 #include "cc/metrics/event_metrics.h"
14 #include "cc/paint/element_id.h"
15 #include "cc/trees/latency_info_swap_promise_monitor.h"
16 #include "cc/trees/layer_tree_host.h"
17 #include "services/tracing/public/cpp/perfetto/flow_event_utils.h"
18 #include "services/tracing/public/cpp/perfetto/macros.h"
19 #include "third_party/blink/public/common/input/web_gesture_device.h"
20 #include "third_party/blink/public/common/input/web_gesture_event.h"
21 #include "third_party/blink/public/common/input/web_input_event_attribution.h"
22 #include "third_party/blink/public/common/input/web_keyboard_event.h"
23 #include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
24 #include "third_party/blink/public/common/input/web_pointer_event.h"
25 #include "third_party/blink/public/common/input/web_touch_event.h"
26 #include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h"
27 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
28 #include "third_party/blink/renderer/platform/widget/input/ime_event_guard.h"
29 #include "third_party/blink/renderer/platform/widget/widget_base.h"
30 #include "third_party/blink/renderer/platform/widget/widget_base_client.h"
31 #include "ui/latency/latency_info.h"
32 
33 #if defined(OS_ANDROID)
34 #include <android/keycodes.h>
35 #endif
36 
37 using perfetto::protos::pbzero::ChromeLatencyInfo;
38 using perfetto::protos::pbzero::TrackEvent;
39 
40 namespace blink {
41 
42 namespace {
43 
GetEventLatencyMicros(base::TimeTicks event_timestamp,base::TimeTicks now)44 int64_t GetEventLatencyMicros(base::TimeTicks event_timestamp,
45                               base::TimeTicks now) {
46   return (now - event_timestamp).InMicroseconds();
47 }
48 
LogInputEventLatencyUma(const WebInputEvent & event,base::TimeTicks now)49 void LogInputEventLatencyUma(const WebInputEvent& event, base::TimeTicks now) {
50   UMA_HISTOGRAM_CUSTOM_COUNTS(
51       "Event.AggregatedLatency.Renderer2",
52       base::saturated_cast<base::HistogramBase::Sample>(
53           GetEventLatencyMicros(event.TimeStamp(), now)),
54       1, 10000000, 100);
55 }
56 
LogPassiveEventListenersUma(WebInputEventResult result,WebInputEvent::DispatchType dispatch_type)57 void LogPassiveEventListenersUma(WebInputEventResult result,
58                                  WebInputEvent::DispatchType dispatch_type) {
59   // This enum is backing a histogram. Do not remove or reorder members.
60   enum ListenerEnum {
61     PASSIVE_LISTENER_UMA_ENUM_PASSIVE,
62     PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE,
63     PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED,
64     PASSIVE_LISTENER_UMA_ENUM_CANCELABLE,
65     PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED,
66     PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING,
67     PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_MAIN_THREAD_RESPONSIVENESS_DEPRECATED,
68     PASSIVE_LISTENER_UMA_ENUM_COUNT
69   };
70 
71   ListenerEnum enum_value;
72   switch (dispatch_type) {
73     case WebInputEvent::DispatchType::kListenersForcedNonBlockingDueToFling:
74       enum_value = PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING;
75       break;
76     case WebInputEvent::DispatchType::kListenersNonBlockingPassive:
77       enum_value = PASSIVE_LISTENER_UMA_ENUM_PASSIVE;
78       break;
79     case WebInputEvent::DispatchType::kEventNonBlocking:
80       enum_value = PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE;
81       break;
82     case WebInputEvent::DispatchType::kBlocking:
83       if (result == WebInputEventResult::kHandledApplication)
84         enum_value = PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED;
85       else if (result == WebInputEventResult::kHandledSuppressed)
86         enum_value = PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED;
87       else
88         enum_value = PASSIVE_LISTENER_UMA_ENUM_CANCELABLE;
89       break;
90     default:
91       NOTREACHED();
92       return;
93   }
94 
95   UMA_HISTOGRAM_ENUMERATION("Event.PassiveListeners", enum_value,
96                             PASSIVE_LISTENER_UMA_ENUM_COUNT);
97 }
98 
LogAllPassiveEventListenersUma(const WebInputEvent & input_event,WebInputEventResult result)99 void LogAllPassiveEventListenersUma(const WebInputEvent& input_event,
100                                     WebInputEventResult result) {
101   // TODO(dtapuska): Use the input_event.timeStampSeconds as the start
102   // ideally this should be when the event was sent by the compositor to the
103   // renderer. https://crbug.com/565348.
104   if (input_event.GetType() == WebInputEvent::Type::kTouchStart ||
105       input_event.GetType() == WebInputEvent::Type::kTouchMove ||
106       input_event.GetType() == WebInputEvent::Type::kTouchEnd) {
107     const WebTouchEvent& touch = static_cast<const WebTouchEvent&>(input_event);
108 
109     LogPassiveEventListenersUma(result, touch.dispatch_type);
110   } else if (input_event.GetType() == WebInputEvent::Type::kMouseWheel) {
111     LogPassiveEventListenersUma(
112         result,
113         static_cast<const WebMouseWheelEvent&>(input_event).dispatch_type);
114   }
115 }
116 
GetCoalescedWebPointerEventForTouch(const WebPointerEvent & pointer_event,const std::vector<std::unique_ptr<WebInputEvent>> & coalesced_events,const std::vector<std::unique_ptr<WebInputEvent>> & predicted_events,const ui::LatencyInfo & latency)117 WebCoalescedInputEvent GetCoalescedWebPointerEventForTouch(
118     const WebPointerEvent& pointer_event,
119     const std::vector<std::unique_ptr<WebInputEvent>>& coalesced_events,
120     const std::vector<std::unique_ptr<WebInputEvent>>& predicted_events,
121     const ui::LatencyInfo& latency) {
122   std::vector<std::unique_ptr<WebInputEvent>> related_pointer_events;
123   for (const std::unique_ptr<WebInputEvent>& event : coalesced_events) {
124     DCHECK(WebInputEvent::IsTouchEventType(event->GetType()));
125     const WebTouchEvent& touch_event =
126         static_cast<const WebTouchEvent&>(*event);
127     for (unsigned i = 0; i < touch_event.touches_length; ++i) {
128       if (touch_event.touches[i].id == pointer_event.id &&
129           touch_event.touches[i].state !=
130               WebTouchPoint::State::kStateStationary) {
131         related_pointer_events.emplace_back(std::make_unique<WebPointerEvent>(
132             touch_event, touch_event.touches[i]));
133       }
134     }
135   }
136   std::vector<std::unique_ptr<WebInputEvent>> predicted_pointer_events;
137   for (const std::unique_ptr<WebInputEvent>& event : predicted_events) {
138     DCHECK(WebInputEvent::IsTouchEventType(event->GetType()));
139     const WebTouchEvent& touch_event =
140         static_cast<const WebTouchEvent&>(*event);
141     for (unsigned i = 0; i < touch_event.touches_length; ++i) {
142       if (touch_event.touches[i].id == pointer_event.id &&
143           touch_event.touches[i].state !=
144               WebTouchPoint::State::kStateStationary) {
145         predicted_pointer_events.emplace_back(std::make_unique<WebPointerEvent>(
146             touch_event, touch_event.touches[i]));
147       }
148     }
149   }
150 
151   return WebCoalescedInputEvent(pointer_event.Clone(),
152                                 std::move(related_pointer_events),
153                                 std::move(predicted_pointer_events), latency);
154 }
155 
GetAckResult(WebInputEventResult processed)156 mojom::InputEventResultState GetAckResult(WebInputEventResult processed) {
157   return processed == WebInputEventResult::kNotHandled
158              ? mojom::InputEventResultState::kNotConsumed
159              : mojom::InputEventResultState::kConsumed;
160 }
161 
IsGestureScroll(WebInputEvent::Type type)162 bool IsGestureScroll(WebInputEvent::Type type) {
163   switch (type) {
164     case WebGestureEvent::Type::kGestureScrollBegin:
165     case WebGestureEvent::Type::kGestureScrollUpdate:
166     case WebGestureEvent::Type::kGestureScrollEnd:
167       return true;
168     default:
169       return false;
170   }
171 }
172 
PositionInWidgetFromInputEvent(const WebInputEvent & event)173 gfx::PointF PositionInWidgetFromInputEvent(const WebInputEvent& event) {
174   if (WebInputEvent::IsMouseEventType(event.GetType())) {
175     return static_cast<const WebMouseEvent&>(event).PositionInWidget();
176   } else if (WebInputEvent::IsGestureEventType(event.GetType())) {
177     return static_cast<const WebGestureEvent&>(event).PositionInWidget();
178   } else {
179     return gfx::PointF(0, 0);
180   }
181 }
182 
IsTouchStartOrMove(const WebInputEvent & event)183 bool IsTouchStartOrMove(const WebInputEvent& event) {
184   if (WebInputEvent::IsPointerEventType(event.GetType())) {
185     return static_cast<const WebPointerEvent&>(event)
186         .touch_start_or_first_touch_move;
187   } else if (WebInputEvent::IsTouchEventType(event.GetType())) {
188     return static_cast<const WebTouchEvent&>(event)
189         .touch_start_or_first_touch_move;
190   } else {
191     return false;
192   }
193 }
194 
195 }  // namespace
196 
197 // This class should be placed on the stack when handling an input event. It
198 // stores information from callbacks from blink while handling an input event
199 // and allows them to be returned in the InputEventAck result.
200 class WidgetBaseInputHandler::HandlingState {
201  public:
HandlingState(base::WeakPtr<WidgetBaseInputHandler> input_handler_param,bool is_touch_start_or_move)202   HandlingState(base::WeakPtr<WidgetBaseInputHandler> input_handler_param,
203                 bool is_touch_start_or_move)
204       : touch_start_or_move_(is_touch_start_or_move),
205         input_handler_(std::move(input_handler_param)) {
206     previous_was_handling_input_ = input_handler_->handling_input_event_;
207     previous_state_ = input_handler_->handling_input_state_;
208     input_handler_->handling_input_event_ = true;
209     input_handler_->handling_input_state_ = this;
210   }
211 
~HandlingState()212   ~HandlingState() {
213     // Unwinding the HandlingState on the stack might result in an
214     // input_handler_ that got destroyed. i.e. via a nested event loop.
215     if (!input_handler_)
216       return;
217     input_handler_->handling_input_event_ = previous_was_handling_input_;
218     DCHECK_EQ(input_handler_->handling_input_state_, this);
219     input_handler_->handling_input_state_ = previous_state_;
220   }
221 
event_overscroll()222   std::unique_ptr<InputHandlerProxy::DidOverscrollParams>& event_overscroll() {
223     return event_overscroll_;
224   }
set_event_overscroll(std::unique_ptr<InputHandlerProxy::DidOverscrollParams> params)225   void set_event_overscroll(
226       std::unique_ptr<InputHandlerProxy::DidOverscrollParams> params) {
227     event_overscroll_ = std::move(params);
228   }
229 
touch_action()230   base::Optional<WebTouchAction>& touch_action() { return touch_action_; }
231 
232   std::vector<WidgetBaseInputHandler::InjectScrollGestureParams>&
injected_scroll_params()233   injected_scroll_params() {
234     return injected_scroll_params_;
235   }
236 
touch_start_or_move()237   bool touch_start_or_move() { return touch_start_or_move_; }
238 
239  private:
240   // Used to intercept overscroll notifications while an event is being
241   // handled. If the event causes overscroll, the overscroll metadata can be
242   // bundled in the event ack, saving an IPC.  Note that we must continue
243   // supporting overscroll IPC notifications due to fling animation updates.
244   std::unique_ptr<InputHandlerProxy::DidOverscrollParams> event_overscroll_;
245 
246   base::Optional<WebTouchAction> touch_action_;
247 
248   // Used to hold a sequence of parameters corresponding to scroll gesture
249   // events that should be injected once the current input event is done
250   // being processed.
251   std::vector<WidgetBaseInputHandler::InjectScrollGestureParams>
252       injected_scroll_params_;
253 
254   // Whether the event we are handling is a touch start or move.
255   bool touch_start_or_move_;
256 
257   HandlingState* previous_state_;
258   bool previous_was_handling_input_;
259   base::WeakPtr<WidgetBaseInputHandler> input_handler_;
260 };
261 
WidgetBaseInputHandler(WidgetBase * widget)262 WidgetBaseInputHandler::WidgetBaseInputHandler(WidgetBase* widget)
263     : widget_(widget),
264       supports_buffered_touch_(
265           widget_->client()->SupportsBufferedTouchEvents()) {}
266 
HandleTouchEvent(const WebCoalescedInputEvent & coalesced_event)267 WebInputEventResult WidgetBaseInputHandler::HandleTouchEvent(
268     const WebCoalescedInputEvent& coalesced_event) {
269   const WebInputEvent& input_event = coalesced_event.Event();
270 
271   if (input_event.GetType() == WebInputEvent::Type::kTouchScrollStarted) {
272     WebPointerEvent pointer_event =
273         WebPointerEvent::CreatePointerCausesUaActionEvent(
274             WebPointerProperties::PointerType::kUnknown,
275             input_event.TimeStamp());
276     return widget_->client()->HandleInputEvent(
277         WebCoalescedInputEvent(pointer_event, coalesced_event.latency_info()));
278   }
279 
280   const WebTouchEvent touch_event =
281       static_cast<const WebTouchEvent&>(input_event);
282   for (unsigned i = 0; i < touch_event.touches_length; ++i) {
283     const WebTouchPoint& touch_point = touch_event.touches[i];
284     if (touch_point.state != WebTouchPoint::State::kStateStationary) {
285       const WebPointerEvent& pointer_event =
286           WebPointerEvent(touch_event, touch_point);
287       const WebCoalescedInputEvent& coalesced_pointer_event =
288           GetCoalescedWebPointerEventForTouch(
289               pointer_event, coalesced_event.GetCoalescedEventsPointers(),
290               coalesced_event.GetPredictedEventsPointers(),
291               coalesced_event.latency_info());
292       widget_->client()->HandleInputEvent(coalesced_pointer_event);
293     }
294   }
295   return widget_->client()->DispatchBufferedTouchEvents();
296 }
297 
HandleInputEvent(const WebCoalescedInputEvent & coalesced_event,std::unique_ptr<cc::EventMetrics> metrics,HandledEventCallback callback)298 void WidgetBaseInputHandler::HandleInputEvent(
299     const WebCoalescedInputEvent& coalesced_event,
300     std::unique_ptr<cc::EventMetrics> metrics,
301     HandledEventCallback callback) {
302   const WebInputEvent& input_event = coalesced_event.Event();
303 
304   // Keep a WeakPtr to this WidgetBaseInputHandler to detect if executing the
305   // input event destroyed the associated RenderWidget (and this handler).
306   base::WeakPtr<WidgetBaseInputHandler> weak_self =
307       weak_ptr_factory_.GetWeakPtr();
308   HandlingState handling_state(weak_self, IsTouchStartOrMove(input_event));
309 
310 #if defined(OS_ANDROID)
311   ImeEventGuard guard(widget_->GetWeakPtr());
312 #endif
313 
314   base::TimeTicks start_time;
315   if (base::TimeTicks::IsHighResolution())
316     start_time = base::TimeTicks::Now();
317 
318   TRACE_EVENT1("renderer,benchmark,rail",
319                "WidgetBaseInputHandler::OnHandleInputEvent", "event",
320                WebInputEvent::GetName(input_event.GetType()));
321   int64_t trace_id = coalesced_event.latency_info().trace_id();
322   TRACE_EVENT("input,benchmark", "LatencyInfo.Flow",
323               [trace_id](perfetto::EventContext ctx) {
324                 ChromeLatencyInfo* info =
325                     ctx.event()->set_chrome_latency_info();
326                 info->set_trace_id(trace_id);
327                 info->set_step(ChromeLatencyInfo::STEP_HANDLE_INPUT_EVENT_MAIN);
328                 tracing::FillFlowEvent(ctx, TrackEvent::LegacyEvent::FLOW_INOUT,
329                                        trace_id);
330               });
331 
332   // If we don't have a high res timer, these metrics won't be accurate enough
333   // to be worth collecting. Note that this does introduce some sampling bias.
334   if (!start_time.is_null())
335     LogInputEventLatencyUma(input_event, start_time);
336 
337   ui::LatencyInfo swap_latency_info(coalesced_event.latency_info());
338   swap_latency_info.AddLatencyNumber(
339       ui::LatencyComponentType::INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT);
340   cc::LatencyInfoSwapPromiseMonitor swap_promise_monitor(
341       &swap_latency_info, widget_->LayerTreeHost()->GetSwapPromiseManager());
342   base::Optional<base::TimeTicks> metrics_timestamp;
343   cc::EventsMetricsManager::ScopedMonitor::DoneCallback done_callback;
344   if (metrics) {
345     // Get the timestamp of `metrics` before moving it to the following
346     // callback. This timestamp would later be useful in creating
347     // `cc::EventMetrics` objects for injected scroll events.
348     metrics_timestamp = metrics->time_stamp();
349     done_callback = base::BindOnce(
350         [](std::unique_ptr<cc::EventMetrics> metrics, bool handled) {
351           std::unique_ptr<cc::EventMetrics> result =
352               handled ? std::move(metrics) : nullptr;
353           return result;
354         },
355         std::move(metrics));
356   }
357   auto event_metrics_monitor =
358       widget_->LayerTreeHost()->GetScopedEventMetricsMonitor(
359           std::move(done_callback));
360 
361   bool prevent_default = false;
362   bool show_virtual_keyboard_for_mouse = false;
363   if (WebInputEvent::IsMouseEventType(input_event.GetType())) {
364     const WebMouseEvent& mouse_event =
365         static_cast<const WebMouseEvent&>(input_event);
366     TRACE_EVENT2("renderer", "HandleMouseMove", "x",
367                  mouse_event.PositionInWidget().x(), "y",
368                  mouse_event.PositionInWidget().y());
369 
370     widget_->client()->WillHandleMouseEvent(mouse_event);
371 
372     // Reset the last known cursor if mouse has left this widget. So next
373     // time that the mouse enters we always set the cursor accordingly.
374     if (mouse_event.GetType() == WebInputEvent::Type::kMouseLeave)
375       current_cursor_.reset();
376 
377     if (mouse_event.button == WebPointerProperties::Button::kLeft &&
378         mouse_event.GetType() == WebInputEvent::Type::kMouseUp) {
379       show_virtual_keyboard_for_mouse = true;
380     }
381   }
382 
383   if (WebInputEvent::IsKeyboardEventType(input_event.GetType())) {
384 #if defined(OS_ANDROID)
385     // The DPAD_CENTER key on Android has a dual semantic: (1) in the general
386     // case it should behave like a select key (i.e. causing a click if a button
387     // is focused). However, if a text field is focused (2), its intended
388     // behavior is to just show the IME and don't propagate the key.
389     // A typical use case is a web form: the DPAD_CENTER should bring up the IME
390     // when clicked on an input text field and cause the form submit if clicked
391     // when the submit button is focused, but not vice-versa.
392     // The UI layer takes care of translating DPAD_CENTER into a RETURN key,
393     // but at this point we have to swallow the event for the scenario (2).
394     const WebKeyboardEvent& key_event =
395         static_cast<const WebKeyboardEvent&>(input_event);
396     if (key_event.native_key_code == AKEYCODE_DPAD_CENTER &&
397         widget_->client()->GetTextInputType() !=
398             WebTextInputType::kWebTextInputTypeNone) {
399       // Show the keyboard on keyup (not keydown) to match the behavior of
400       // Android's TextView.
401       if (key_event.GetType() == WebInputEvent::Type::kKeyUp)
402         widget_->ShowVirtualKeyboardOnElementFocus();
403       // Prevent default for both keydown and keyup (letting the keydown go
404       // through to the web app would cause compatibility problems since
405       // DPAD_CENTER is also used as a "confirm" button).
406       prevent_default = true;
407     }
408 #endif
409   }
410 
411   if (WebInputEvent::IsGestureEventType(input_event.GetType())) {
412     const WebGestureEvent& gesture_event =
413         static_cast<const WebGestureEvent&>(input_event);
414     prevent_default = prevent_default ||
415                       widget_->client()->WillHandleGestureEvent(gesture_event);
416   }
417 
418   WebInputEventResult processed = prevent_default
419                                       ? WebInputEventResult::kHandledSuppressed
420                                       : WebInputEventResult::kNotHandled;
421   if (input_event.GetType() != WebInputEvent::Type::kChar ||
422       !suppress_next_char_events_) {
423     suppress_next_char_events_ = false;
424     if (processed == WebInputEventResult::kNotHandled) {
425       if (supports_buffered_touch_ &&
426           WebInputEvent::IsTouchEventType(input_event.GetType()))
427         processed = HandleTouchEvent(coalesced_event);
428       else
429         processed = widget_->client()->HandleInputEvent(coalesced_event);
430     }
431 
432     // The associated WidgetBase (and this WidgetBaseInputHandler) could
433     // have been destroyed. If it was return early before accessing any more of
434     // this class.
435     if (!weak_self) {
436       if (callback) {
437         std::move(callback).Run(GetAckResult(processed), swap_latency_info,
438                                 std::move(handling_state.event_overscroll()),
439                                 std::move(handling_state.touch_action()));
440       }
441       return;
442     }
443   }
444 
445   // Handling |input_event| is finished and further down, we might start
446   // handling injected scroll events. So, stop monitoring EventMetrics for
447   // |input_event| to avoid nested monitors.
448   event_metrics_monitor = nullptr;
449 
450   LogAllPassiveEventListenersUma(input_event, processed);
451 
452   // If this RawKeyDown event corresponds to a browser keyboard shortcut and
453   // it's not processed by webkit, then we need to suppress the upcoming Char
454   // events.
455   bool is_keyboard_shortcut =
456       input_event.GetType() == WebInputEvent::Type::kRawKeyDown &&
457       static_cast<const WebKeyboardEvent&>(input_event).is_browser_shortcut;
458   if (processed == WebInputEventResult::kNotHandled && is_keyboard_shortcut)
459     suppress_next_char_events_ = true;
460 
461   // The handling of some input events on the main thread may require injecting
462   // scroll gestures back into blink, e.g., a mousedown on a scrollbar. We
463   // do this here so that we can attribute latency information from the mouse as
464   // a scroll interaction, instead of just classifying as mouse input.
465   if (handling_state.injected_scroll_params().size()) {
466     HandleInjectedScrollGestures(
467         std::move(handling_state.injected_scroll_params()), input_event,
468         coalesced_event.latency_info(), metrics_timestamp);
469   }
470 
471   // Send gesture scroll events and their dispositions to the compositor thread,
472   // so that they can be used to produce the elastic overscroll effect.
473   if (input_event.GetType() == WebInputEvent::Type::kGestureScrollBegin ||
474       input_event.GetType() == WebInputEvent::Type::kGestureScrollEnd ||
475       input_event.GetType() == WebInputEvent::Type::kGestureScrollUpdate) {
476     const WebGestureEvent& gesture_event =
477         static_cast<const WebGestureEvent&>(input_event);
478     if (gesture_event.SourceDevice() == WebGestureDevice::kTouchpad ||
479         gesture_event.SourceDevice() == WebGestureDevice::kTouchscreen) {
480       gfx::Vector2dF latest_overscroll_delta =
481           handling_state.event_overscroll()
482               ? handling_state.event_overscroll()->latest_overscroll_delta
483               : gfx::Vector2dF();
484       cc::OverscrollBehavior overscroll_behavior =
485           handling_state.event_overscroll()
486               ? handling_state.event_overscroll()->overscroll_behavior
487               : cc::OverscrollBehavior();
488       widget_->client()->ObserveGestureEventAndResult(
489           gesture_event, latest_overscroll_delta, overscroll_behavior,
490           processed != WebInputEventResult::kNotHandled);
491     }
492   }
493 
494   if (callback) {
495     std::move(callback).Run(GetAckResult(processed), swap_latency_info,
496                             std::move(handling_state.event_overscroll()),
497                             std::move(handling_state.touch_action()));
498   } else {
499     DCHECK(!handling_state.event_overscroll())
500         << "Unexpected overscroll for un-acked event";
501   }
502 
503   // Show the virtual keyboard if enabled and a user gesture triggers a focus
504   // change.
505   if ((processed != WebInputEventResult::kNotHandled &&
506        input_event.GetType() == WebInputEvent::Type::kTouchEnd) ||
507       show_virtual_keyboard_for_mouse) {
508     widget_->ShowVirtualKeyboard();
509   }
510 
511   if (!prevent_default &&
512       WebInputEvent::IsKeyboardEventType(input_event.GetType()))
513     widget_->client()->DidHandleKeyEvent();
514 
515 // TODO(rouslan): Fix ChromeOS and Windows 8 behavior of autofill popup with
516 // virtual keyboard.
517 #if !defined(OS_ANDROID)
518   // Virtual keyboard is not supported, so react to focus change immediately.
519   if ((processed != WebInputEventResult::kNotHandled &&
520        input_event.GetType() == WebInputEvent::Type::kMouseDown) ||
521       input_event.GetType() == WebInputEvent::Type::kGestureTap) {
522     widget_->client()->FocusChangeComplete();
523   }
524 #endif
525 
526   // Ensure all injected scrolls were handled or queue up - any remaining
527   // injected scrolls at this point would not be processed.
528   DCHECK(handling_state.injected_scroll_params().empty());
529 }
530 
DidOverscrollFromBlink(const gfx::Vector2dF & overscroll_delta,const gfx::Vector2dF & accumulated_overscroll,const gfx::PointF & position,const gfx::Vector2dF & velocity,const cc::OverscrollBehavior & behavior)531 bool WidgetBaseInputHandler::DidOverscrollFromBlink(
532     const gfx::Vector2dF& overscroll_delta,
533     const gfx::Vector2dF& accumulated_overscroll,
534     const gfx::PointF& position,
535     const gfx::Vector2dF& velocity,
536     const cc::OverscrollBehavior& behavior) {
537   // We aren't currently handling an event. Allow the processing to be
538   // dispatched separately from the ACK.
539   if (!handling_input_state_)
540     return true;
541 
542   // If we're currently handling an event, stash the overscroll data such that
543   // it can be bundled in the event ack.
544   std::unique_ptr<InputHandlerProxy::DidOverscrollParams> params =
545       std::make_unique<InputHandlerProxy::DidOverscrollParams>();
546   params->accumulated_overscroll = accumulated_overscroll;
547   params->latest_overscroll_delta = overscroll_delta;
548   params->current_fling_velocity = velocity;
549   params->causal_event_viewport_point = position;
550   params->overscroll_behavior = behavior;
551   handling_input_state_->set_event_overscroll(std::move(params));
552   return false;
553 }
554 
InjectGestureScrollEvent(WebGestureDevice device,const gfx::Vector2dF & delta,ui::ScrollGranularity granularity,cc::ElementId scrollable_area_element_id,WebInputEvent::Type injected_type)555 void WidgetBaseInputHandler::InjectGestureScrollEvent(
556     WebGestureDevice device,
557     const gfx::Vector2dF& delta,
558     ui::ScrollGranularity granularity,
559     cc::ElementId scrollable_area_element_id,
560     WebInputEvent::Type injected_type) {
561   DCHECK(IsGestureScroll(injected_type));
562   // If we're currently handling an input event, cache the appropriate
563   // parameters so we can dispatch the events directly once blink finishes
564   // handling the event.
565   // Otherwise, queue the event on the main thread event queue.
566   // The latter may occur when scrollbar scrolls are injected due to
567   // autoscroll timer - i.e. not within the handling of a mouse event.
568   // We don't always just enqueue events, since events queued to the
569   // MainThreadEventQueue in the middle of dispatch (which we are) won't
570   // be dispatched until the next time the queue gets to run. The side effect
571   // of that would be an extra frame of latency if we're injecting a scroll
572   // during the handling of a rAF aligned input event, such as mouse move.
573   if (handling_input_state_) {
574     InjectScrollGestureParams params{device, delta, granularity,
575                                      scrollable_area_element_id, injected_type};
576     handling_input_state_->injected_scroll_params().push_back(params);
577   } else {
578     base::TimeTicks now = base::TimeTicks::Now();
579     std::unique_ptr<WebGestureEvent> gesture_event =
580         WebGestureEvent::GenerateInjectedScrollGesture(
581             injected_type, now, device, gfx::PointF(0, 0), delta, granularity);
582     if (injected_type == WebInputEvent::Type::kGestureScrollBegin) {
583       gesture_event->data.scroll_begin.scrollable_area_element_id =
584           scrollable_area_element_id.GetStableId();
585     }
586 
587     std::unique_ptr<WebCoalescedInputEvent> web_scoped_gesture_event =
588         std::make_unique<WebCoalescedInputEvent>(std::move(gesture_event),
589                                                  ui::LatencyInfo());
590     widget_->QueueSyntheticEvent(std::move(web_scoped_gesture_event));
591   }
592 }
593 
HandleInjectedScrollGestures(std::vector<InjectScrollGestureParams> injected_scroll_params,const WebInputEvent & input_event,const ui::LatencyInfo & original_latency_info,base::Optional<base::TimeTicks> original_metrics_timestamp)594 void WidgetBaseInputHandler::HandleInjectedScrollGestures(
595     std::vector<InjectScrollGestureParams> injected_scroll_params,
596     const WebInputEvent& input_event,
597     const ui::LatencyInfo& original_latency_info,
598     base::Optional<base::TimeTicks> original_metrics_timestamp) {
599   DCHECK(injected_scroll_params.size());
600 
601   base::TimeTicks original_timestamp;
602   bool found_original_component = original_latency_info.FindLatency(
603       ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, &original_timestamp);
604   DCHECK(found_original_component);
605 
606   gfx::PointF position = PositionInWidgetFromInputEvent(input_event);
607   for (const InjectScrollGestureParams& params : injected_scroll_params) {
608     // Set up a new LatencyInfo for the injected scroll - this is the original
609     // LatencyInfo for the input event that was being handled when the scroll
610     // was injected. This new LatencyInfo will have a modified type, and an
611     // additional scroll update component. Also set up a SwapPromiseMonitor that
612     // will cause the LatencyInfo to be sent up with the compositor frame, if
613     // the GSU causes a commit. This allows end to end latency to be logged for
614     // the injected scroll, annotated with the correct type.
615     ui::LatencyInfo scrollbar_latency_info(original_latency_info);
616 
617     // Currently only scrollbar is supported - if this DCHECK hits due to a
618     // new type being injected, please modify the type passed to
619     // |set_source_event_type()|.
620     DCHECK(params.device == WebGestureDevice::kScrollbar);
621     scrollbar_latency_info.set_source_event_type(
622         ui::SourceEventType::SCROLLBAR);
623     scrollbar_latency_info.AddLatencyNumber(
624         ui::LatencyComponentType::INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT);
625 
626     base::Optional<cc::EventMetrics::ScrollUpdateType> scroll_update_type;
627     if (params.type == WebInputEvent::Type::kGestureScrollUpdate) {
628       if (input_event.GetType() != WebInputEvent::Type::kGestureScrollUpdate) {
629         scrollbar_latency_info.AddLatencyNumberWithTimestamp(
630             last_injected_gesture_was_begin_
631                 ? ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT
632                 : ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT,
633             original_timestamp);
634       } else {
635         // If we're injecting a GSU in response to a GSU (touch drags of the
636         // scrollbar thumb in Blink handles GSUs, and reverses them with
637         // injected GSUs), the LatencyInfo will already have the appropriate
638         // SCROLL_UPDATE component set.
639         DCHECK(
640             scrollbar_latency_info.FindLatency(
641                 ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT,
642                 nullptr) ||
643             scrollbar_latency_info.FindLatency(
644                 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT,
645                 nullptr));
646       }
647       scroll_update_type = last_injected_gesture_was_begin_
648                                ? cc::EventMetrics::ScrollUpdateType::kStarted
649                                : cc::EventMetrics::ScrollUpdateType::kContinued;
650     }
651 
652     std::unique_ptr<WebGestureEvent> gesture_event =
653         WebGestureEvent::GenerateInjectedScrollGesture(
654             params.type, input_event.TimeStamp(), params.device, position,
655             params.scroll_delta, params.granularity);
656     if (params.type == WebInputEvent::Type::kGestureScrollBegin) {
657       gesture_event->data.scroll_begin.scrollable_area_element_id =
658           params.scrollable_area_element_id.GetStableId();
659       last_injected_gesture_was_begin_ = true;
660     } else {
661       last_injected_gesture_was_begin_ = false;
662     }
663 
664     {
665       cc::LatencyInfoSwapPromiseMonitor swap_promise_monitor(
666           &scrollbar_latency_info,
667           widget_->LayerTreeHost()->GetSwapPromiseManager());
668       // For latency metrics, we need the original timestamp of the
669       // `input_event` as its current timestamp might have changed due to
670       // coalescing in the pipeline. `original_metrics_timestamp` which is the
671       // timestamp from the metrics object for the `input_event` contains this
672       // original timestamp. We could have used `original_timestamp` for this
673       // purpose, but we don't do that as `original_timestamp` is extracted from
674       // `ui::LatencyInfo` of `input_event` which we hope to be able to get rid
675       // of. Moreover, we plan to add more breakdown timestamps to
676       // `EventMetrcis` which are not available in `ui::LatencyInfo`.
677       base::TimeTicks time_stamp =
678           original_metrics_timestamp.value_or(gesture_event->TimeStamp());
679       std::unique_ptr<cc::EventMetrics> metrics = cc::EventMetrics::Create(
680           gesture_event->GetTypeAsUiEventType(), scroll_update_type, time_stamp,
681           gesture_event->GetScrollInputType());
682       cc::EventsMetricsManager::ScopedMonitor::DoneCallback done_callback;
683       if (metrics) {
684         // Since we don't need `metrics` for this event beyond this point (i.e.
685         // we don't intend to add further breakdowns to the metrics while
686         // processing the event, at least for now), it is safe to move the
687         // metrics object to the callback.
688         done_callback = base::BindOnce(
689             [](std::unique_ptr<cc::EventMetrics> metrics, bool handled) {
690               std::unique_ptr<cc::EventMetrics> result =
691                   handled ? std::move(metrics) : nullptr;
692               return result;
693             },
694             std::move(metrics));
695       }
696       auto event_metrics_monitor =
697           widget_->LayerTreeHost()->GetScopedEventMetricsMonitor(
698               std::move(done_callback));
699       widget_->client()->HandleInputEvent(
700           WebCoalescedInputEvent(*gesture_event, scrollbar_latency_info));
701     }
702   }
703 }
704 
DidChangeCursor(const ui::Cursor & cursor)705 bool WidgetBaseInputHandler::DidChangeCursor(const ui::Cursor& cursor) {
706   if (current_cursor_.has_value() && current_cursor_.value() == cursor)
707     return false;
708   current_cursor_ = cursor;
709   return true;
710 }
711 
ProcessTouchAction(WebTouchAction touch_action)712 bool WidgetBaseInputHandler::ProcessTouchAction(WebTouchAction touch_action) {
713   if (!handling_input_state_)
714     return false;
715   // Ignore setTouchAction calls that result from synthetic touch events (eg.
716   // when blink is emulating touch with mouse).
717   if (!handling_input_state_->touch_start_or_move())
718     return false;
719   handling_input_state_->touch_action() = touch_action;
720   return true;
721 }
722 
723 }  // namespace blink
724