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