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