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