1 // Copyright 2017 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_input_handler_manager.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/check_op.h"
11 #include "base/feature_list.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/notreached.h"
14 #include "cc/base/features.h"
15 #include "cc/metrics/event_metrics.h"
16 #include "cc/trees/layer_tree_host.h"
17 #include "services/tracing/public/cpp/perfetto/flow_event_utils.h"
18 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
19 #include "third_party/blink/public/common/input/web_input_event_attribution.h"
20 #include "third_party/blink/public/common/input/web_keyboard_event.h"
21 #include "third_party/blink/public/platform/platform.h"
22 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
23 #include "third_party/blink/public/platform/scheduler/web_widget_scheduler.h"
24 #include "third_party/blink/renderer/platform/widget/frame_widget.h"
25 #include "third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller.h"
26 #include "third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h"
27 #include "third_party/blink/renderer/platform/widget/input/widget_input_handler_impl.h"
28 #include "third_party/blink/renderer/platform/widget/widget_base.h"
29 #include "third_party/blink/renderer/platform/widget/widget_base_client.h"
30
31 #if defined(OS_ANDROID)
32 #include "third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_compositor_registry.h"
33 #include "third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.h"
34 #endif
35
36 namespace blink {
37
38 using ::perfetto::protos::pbzero::ChromeLatencyInfo;
39 using ::perfetto::protos::pbzero::TrackEvent;
40
41 namespace {
42
ToDidOverscrollParams(const InputHandlerProxy::DidOverscrollParams * overscroll_params)43 mojom::blink::DidOverscrollParamsPtr ToDidOverscrollParams(
44 const InputHandlerProxy::DidOverscrollParams* overscroll_params) {
45 if (!overscroll_params)
46 return nullptr;
47 return mojom::blink::DidOverscrollParams::New(
48 overscroll_params->accumulated_overscroll,
49 overscroll_params->latest_overscroll_delta,
50 overscroll_params->current_fling_velocity,
51 overscroll_params->causal_event_viewport_point,
52 overscroll_params->overscroll_behavior);
53 }
54
CallCallback(mojom::blink::WidgetInputHandler::DispatchEventCallback callback,mojom::blink::InputEventResultState result_state,const ui::LatencyInfo & latency_info,mojom::blink::DidOverscrollParamsPtr overscroll_params,base::Optional<cc::TouchAction> touch_action)55 void CallCallback(
56 mojom::blink::WidgetInputHandler::DispatchEventCallback callback,
57 mojom::blink::InputEventResultState result_state,
58 const ui::LatencyInfo& latency_info,
59 mojom::blink::DidOverscrollParamsPtr overscroll_params,
60 base::Optional<cc::TouchAction> touch_action) {
61 ui::LatencyInfo::TraceIntermediateFlowEvents(
62 {latency_info}, ChromeLatencyInfo::STEP_HANDLED_INPUT_EVENT_IMPL);
63 std::move(callback).Run(
64 mojom::blink::InputEventResultSource::kMainThread, latency_info,
65 result_state, std::move(overscroll_params),
66 touch_action
67 ? mojom::blink::TouchActionOptional::New(touch_action.value())
68 : nullptr);
69 }
70
InputEventDispositionToAck(InputHandlerProxy::EventDisposition disposition)71 mojom::blink::InputEventResultState InputEventDispositionToAck(
72 InputHandlerProxy::EventDisposition disposition) {
73 switch (disposition) {
74 case InputHandlerProxy::DID_HANDLE:
75 return mojom::blink::InputEventResultState::kConsumed;
76 case InputHandlerProxy::DID_NOT_HANDLE:
77 return mojom::blink::InputEventResultState::kNotConsumed;
78 case InputHandlerProxy::DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING:
79 return mojom::blink::InputEventResultState::kSetNonBlockingDueToFling;
80 case InputHandlerProxy::DROP_EVENT:
81 return mojom::blink::InputEventResultState::kNoConsumerExists;
82 case InputHandlerProxy::DID_HANDLE_NON_BLOCKING:
83 return mojom::blink::InputEventResultState::kSetNonBlocking;
84 case InputHandlerProxy::DID_HANDLE_SHOULD_BUBBLE:
85 return mojom::blink::InputEventResultState::kConsumedShouldBubble;
86 case InputHandlerProxy::REQUIRES_MAIN_THREAD_HIT_TEST:
87 default:
88 NOTREACHED();
89 return mojom::blink::InputEventResultState::kUnknown;
90 }
91 }
92
ScrollBeginFromScrollUpdate(const WebGestureEvent & gesture_update)93 std::unique_ptr<blink::WebGestureEvent> ScrollBeginFromScrollUpdate(
94 const WebGestureEvent& gesture_update) {
95 DCHECK(gesture_update.GetType() == WebInputEvent::Type::kGestureScrollUpdate);
96
97 auto scroll_begin = std::make_unique<WebGestureEvent>(gesture_update);
98 scroll_begin->SetType(WebInputEvent::Type::kGestureScrollBegin);
99
100 scroll_begin->data.scroll_begin.delta_x_hint =
101 gesture_update.data.scroll_update.delta_x;
102 scroll_begin->data.scroll_begin.delta_y_hint =
103 gesture_update.data.scroll_update.delta_y;
104 scroll_begin->data.scroll_begin.delta_hint_units =
105 gesture_update.data.scroll_update.delta_units;
106 scroll_begin->data.scroll_begin.target_viewport = false;
107 scroll_begin->data.scroll_begin.inertial_phase =
108 gesture_update.data.scroll_update.inertial_phase;
109 scroll_begin->data.scroll_begin.synthetic = false;
110 scroll_begin->data.scroll_begin.pointer_count = 0;
111 scroll_begin->data.scroll_begin.scrollable_area_element_id = 0;
112
113 return scroll_begin;
114 }
115
116 } // namespace
117
118 #if defined(OS_ANDROID)
119 class SynchronousCompositorProxyRegistry
120 : public SynchronousCompositorRegistry {
121 public:
SynchronousCompositorProxyRegistry(scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner)122 explicit SynchronousCompositorProxyRegistry(
123 scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner)
124 : compositor_task_runner_(std::move(compositor_task_runner)) {}
125
~SynchronousCompositorProxyRegistry()126 ~SynchronousCompositorProxyRegistry() override {
127 // Ensure the proxy has already been release on the compositor thread
128 // before destroying this object.
129 DCHECK(!proxy_);
130 }
131
CreateProxy(SynchronousInputHandlerProxy * handler)132 void CreateProxy(SynchronousInputHandlerProxy* handler) {
133 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
134 proxy_ = std::make_unique<SynchronousCompositorProxy>(handler);
135 proxy_->Init();
136
137 if (sink_)
138 proxy_->SetLayerTreeFrameSink(sink_);
139 }
140
proxy()141 SynchronousCompositorProxy* proxy() { return proxy_.get(); }
142
RegisterLayerTreeFrameSink(SynchronousLayerTreeFrameSink * layer_tree_frame_sink)143 void RegisterLayerTreeFrameSink(
144 SynchronousLayerTreeFrameSink* layer_tree_frame_sink) override {
145 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
146 DCHECK_EQ(nullptr, sink_);
147 sink_ = layer_tree_frame_sink;
148 if (proxy_)
149 proxy_->SetLayerTreeFrameSink(layer_tree_frame_sink);
150 }
151
UnregisterLayerTreeFrameSink(SynchronousLayerTreeFrameSink * layer_tree_frame_sink)152 void UnregisterLayerTreeFrameSink(
153 SynchronousLayerTreeFrameSink* layer_tree_frame_sink) override {
154 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
155 DCHECK_EQ(layer_tree_frame_sink, sink_);
156 sink_ = nullptr;
157 }
158
DestroyProxy()159 void DestroyProxy() {
160 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
161 proxy_.reset();
162 }
163
164 private:
165 scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
166 std::unique_ptr<SynchronousCompositorProxy> proxy_;
167 SynchronousLayerTreeFrameSink* sink_ = nullptr;
168 };
169
170 #endif
171
Create(base::WeakPtr<WidgetBase> widget,bool never_composited,scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,scheduler::WebThreadScheduler * main_thread_scheduler,bool uses_input_handler)172 scoped_refptr<WidgetInputHandlerManager> WidgetInputHandlerManager::Create(
173 base::WeakPtr<WidgetBase> widget,
174 bool never_composited,
175 scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
176 scheduler::WebThreadScheduler* main_thread_scheduler,
177 bool uses_input_handler) {
178 scoped_refptr<WidgetInputHandlerManager> manager =
179 new WidgetInputHandlerManager(std::move(widget), never_composited,
180 std::move(compositor_task_runner),
181 main_thread_scheduler);
182 if (uses_input_handler)
183 manager->InitInputHandler();
184
185 // A compositor thread implies we're using an input handler.
186 DCHECK(!manager->compositor_task_runner_ || uses_input_handler);
187 // Conversely, if we don't use an input handler we must not have a compositor
188 // thread.
189 DCHECK(uses_input_handler || !manager->compositor_task_runner_);
190
191 return manager;
192 }
193
WidgetInputHandlerManager(base::WeakPtr<WidgetBase> widget,bool never_composited,scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,scheduler::WebThreadScheduler * main_thread_scheduler)194 WidgetInputHandlerManager::WidgetInputHandlerManager(
195 base::WeakPtr<WidgetBase> widget,
196 bool never_composited,
197 scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
198 scheduler::WebThreadScheduler* main_thread_scheduler)
199 : widget_(widget),
200 widget_scheduler_(main_thread_scheduler->CreateWidgetScheduler()),
201 main_thread_scheduler_(main_thread_scheduler),
202 input_event_queue_(base::MakeRefCounted<MainThreadEventQueue>(
203 this,
204 widget_scheduler_->InputTaskRunner(),
205 main_thread_scheduler,
206 /*allow_raf_aligned_input=*/!never_composited)),
207 main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
208 compositor_task_runner_(std::move(compositor_task_runner)) {
209 #if defined(OS_ANDROID)
210 if (compositor_task_runner_) {
211 synchronous_compositor_registry_ =
212 std::make_unique<SynchronousCompositorProxyRegistry>(
213 compositor_task_runner_);
214 }
215 #endif
216 }
217
InitInputHandler()218 void WidgetInputHandlerManager::InitInputHandler() {
219 bool sync_compositing = false;
220 #if defined(OS_ANDROID)
221 sync_compositing =
222 Platform::Current()->IsSynchronousCompositingEnabledForAndroidWebView();
223 #endif
224 uses_input_handler_ = true;
225 base::OnceClosure init_closure = base::BindOnce(
226 &WidgetInputHandlerManager::InitOnInputHandlingThread, this,
227 widget_->LayerTreeHost()->GetDelegateForInput(), sync_compositing);
228 InputThreadTaskRunner()->PostTask(FROM_HERE, std::move(init_closure));
229 }
230
231 WidgetInputHandlerManager::~WidgetInputHandlerManager() = default;
232
AddInterface(mojo::PendingReceiver<mojom::blink::WidgetInputHandler> receiver,mojo::PendingRemote<mojom::blink::WidgetInputHandlerHost> host)233 void WidgetInputHandlerManager::AddInterface(
234 mojo::PendingReceiver<mojom::blink::WidgetInputHandler> receiver,
235 mojo::PendingRemote<mojom::blink::WidgetInputHandlerHost> host) {
236 if (compositor_task_runner_) {
237 host_ = mojo::SharedRemote<mojom::blink::WidgetInputHandlerHost>(
238 std::move(host), compositor_task_runner_);
239 // Mojo channel bound on compositor thread.
240 compositor_task_runner_->PostTask(
241 FROM_HERE, base::BindOnce(&WidgetInputHandlerManager::BindChannel, this,
242 std::move(receiver)));
243 } else {
244 host_ = mojo::SharedRemote<mojom::blink::WidgetInputHandlerHost>(
245 std::move(host));
246 // Mojo channel bound on main thread.
247 BindChannel(std::move(receiver));
248 }
249 }
250
HandleInputEvent(const WebCoalescedInputEvent & event,std::unique_ptr<cc::EventMetrics> metrics,HandledEventCallback handled_callback)251 bool WidgetInputHandlerManager::HandleInputEvent(
252 const WebCoalescedInputEvent& event,
253 std::unique_ptr<cc::EventMetrics> metrics,
254 HandledEventCallback handled_callback) {
255 WidgetBaseInputHandler::HandledEventCallback blink_callback = base::BindOnce(
256 [](HandledEventCallback callback,
257 blink::mojom::InputEventResultState ack_state,
258 const ui::LatencyInfo& latency_info,
259 std::unique_ptr<InputHandlerProxy::DidOverscrollParams>
260 overscroll_params,
261 base::Optional<cc::TouchAction> touch_action) {
262 if (!callback)
263 return;
264 std::move(callback).Run(ack_state, latency_info,
265 ToDidOverscrollParams(overscroll_params.get()),
266 touch_action);
267 },
268 std::move(handled_callback));
269 widget_->input_handler().HandleInputEvent(event, std::move(metrics),
270 std::move(blink_callback));
271 return true;
272 }
273
SetNeedsMainFrame()274 void WidgetInputHandlerManager::SetNeedsMainFrame() {
275 widget_->client()->ScheduleAnimation();
276 }
277
WillShutdown()278 void WidgetInputHandlerManager::WillShutdown() {
279 #if defined(OS_ANDROID)
280 if (synchronous_compositor_registry_)
281 synchronous_compositor_registry_->DestroyProxy();
282 #endif
283 input_handler_proxy_.reset();
284 }
285
DispatchNonBlockingEventToMainThread(std::unique_ptr<WebCoalescedInputEvent> event,const WebInputEventAttribution & attribution,std::unique_ptr<cc::EventMetrics> metrics)286 void WidgetInputHandlerManager::DispatchNonBlockingEventToMainThread(
287 std::unique_ptr<WebCoalescedInputEvent> event,
288 const WebInputEventAttribution& attribution,
289 std::unique_ptr<cc::EventMetrics> metrics) {
290 DCHECK(input_event_queue_);
291 input_event_queue_->HandleEvent(
292 std::move(event), MainThreadEventQueue::DispatchType::kNonBlocking,
293 mojom::blink::InputEventResultState::kSetNonBlocking, attribution,
294 std::move(metrics), HandledEventCallback());
295 }
296
FindScrollTargetOnMainThread(const gfx::PointF & point,ElementAtPointCallback callback)297 void WidgetInputHandlerManager::FindScrollTargetOnMainThread(
298 const gfx::PointF& point,
299 ElementAtPointCallback callback) {
300 TRACE_EVENT2("input",
301 "WidgetInputHandlerManager::FindScrollTargetOnMainThread",
302 "point.x", point.x(), "point.y", point.y());
303 DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
304 DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
305
306 uint64_t element_id =
307 widget_->client()->FrameWidget()->GetScrollableContainerIdAt(point);
308
309 InputThreadTaskRunner()->PostTask(
310 FROM_HERE, base::BindOnce(std::move(callback), element_id));
311 }
312
DidAnimateForInput()313 void WidgetInputHandlerManager::DidAnimateForInput() {
314 main_thread_scheduler_->DidAnimateForInputOnCompositorThread();
315 }
316
DidStartScrollingViewport()317 void WidgetInputHandlerManager::DidStartScrollingViewport() {
318 mojom::blink::WidgetInputHandlerHost* host = GetWidgetInputHandlerHost();
319 if (!host)
320 return;
321 host->DidStartScrollingViewport();
322 }
323
GenerateScrollBeginAndSendToMainThread(const WebGestureEvent & update_event,const WebInputEventAttribution & attribution,const cc::EventMetrics * update_metrics)324 void WidgetInputHandlerManager::GenerateScrollBeginAndSendToMainThread(
325 const WebGestureEvent& update_event,
326 const WebInputEventAttribution& attribution,
327 const cc::EventMetrics* update_metrics) {
328 DCHECK_EQ(update_event.GetType(), WebInputEvent::Type::kGestureScrollUpdate);
329 auto event = std::make_unique<WebCoalescedInputEvent>(
330 ScrollBeginFromScrollUpdate(update_event), ui::LatencyInfo());
331 base::TimeTicks metrics_time_stamp = update_metrics
332 ? update_metrics->time_stamp()
333 : event->Event().TimeStamp();
334 std::unique_ptr<cc::EventMetrics> metrics = cc::EventMetrics::Create(
335 event->Event().GetTypeAsUiEventType(), base::nullopt, metrics_time_stamp,
336 event->Event().GetScrollInputType());
337
338 DispatchNonBlockingEventToMainThread(std::move(event), attribution,
339 std::move(metrics));
340 }
341
SetAllowedTouchAction(cc::TouchAction touch_action,uint32_t unique_touch_event_id,InputHandlerProxy::EventDisposition event_disposition)342 void WidgetInputHandlerManager::SetAllowedTouchAction(
343 cc::TouchAction touch_action,
344 uint32_t unique_touch_event_id,
345 InputHandlerProxy::EventDisposition event_disposition) {
346 allowed_touch_action_ = touch_action;
347 }
348
ProcessTouchAction(cc::TouchAction touch_action)349 void WidgetInputHandlerManager::ProcessTouchAction(
350 cc::TouchAction touch_action) {
351 if (mojom::blink::WidgetInputHandlerHost* host = GetWidgetInputHandlerHost())
352 host->SetTouchActionFromMain(touch_action);
353 }
354
355 mojom::blink::WidgetInputHandlerHost*
GetWidgetInputHandlerHost()356 WidgetInputHandlerManager::GetWidgetInputHandlerHost() {
357 if (host_)
358 return host_.get();
359 return nullptr;
360 }
361
AttachSynchronousCompositor(mojo::PendingRemote<mojom::blink::SynchronousCompositorControlHost> control_host,mojo::PendingAssociatedRemote<mojom::blink::SynchronousCompositorHost> host,mojo::PendingAssociatedReceiver<mojom::blink::SynchronousCompositor> compositor_request)362 void WidgetInputHandlerManager::AttachSynchronousCompositor(
363 mojo::PendingRemote<mojom::blink::SynchronousCompositorControlHost>
364 control_host,
365 mojo::PendingAssociatedRemote<mojom::blink::SynchronousCompositorHost> host,
366 mojo::PendingAssociatedReceiver<mojom::blink::SynchronousCompositor>
367 compositor_request) {
368 #if defined(OS_ANDROID)
369 DCHECK(synchronous_compositor_registry_);
370 if (synchronous_compositor_registry_->proxy()) {
371 synchronous_compositor_registry_->proxy()->BindChannel(
372 std::move(control_host), std::move(host),
373 std::move(compositor_request));
374 }
375 #endif
376 }
377
ObserveGestureEventOnMainThread(const WebGestureEvent & gesture_event,const cc::InputHandlerScrollResult & scroll_result)378 void WidgetInputHandlerManager::ObserveGestureEventOnMainThread(
379 const WebGestureEvent& gesture_event,
380 const cc::InputHandlerScrollResult& scroll_result) {
381 base::OnceClosure observe_gesture_event_closure = base::BindOnce(
382 &WidgetInputHandlerManager::ObserveGestureEventOnInputHandlingThread,
383 this, gesture_event, scroll_result);
384 InputThreadTaskRunner()->PostTask(FROM_HERE,
385 std::move(observe_gesture_event_closure));
386 }
387
LogInputTimingUMA()388 void WidgetInputHandlerManager::LogInputTimingUMA() {
389 if (!have_emitted_uma_) {
390 InitialInputTiming lifecycle_state = InitialInputTiming::kBeforeLifecycle;
391 if (!(renderer_deferral_state_ &
392 (unsigned)RenderingDeferralBits::kDeferMainFrameUpdates)) {
393 if (renderer_deferral_state_ &
394 (unsigned)RenderingDeferralBits::kDeferCommits) {
395 lifecycle_state = InitialInputTiming::kBeforeCommit;
396 } else {
397 lifecycle_state = InitialInputTiming::kAfterCommit;
398 }
399 }
400 UMA_HISTOGRAM_ENUMERATION("PaintHolding.InputTiming2", lifecycle_state);
401 have_emitted_uma_ = true;
402 }
403 }
404
DispatchScrollGestureToCompositor(std::unique_ptr<WebGestureEvent> event)405 void WidgetInputHandlerManager::DispatchScrollGestureToCompositor(
406 std::unique_ptr<WebGestureEvent> event) {
407 DCHECK(base::FeatureList::IsEnabled(features::kScrollUnification));
408 std::unique_ptr<WebCoalescedInputEvent> web_scoped_gesture_event =
409 std::make_unique<WebCoalescedInputEvent>(std::move(event),
410 ui::LatencyInfo());
411 // input thread task runner is |main_thread_task_runner_| only in tests
412 InputThreadTaskRunner()->PostTask(
413 FROM_HERE,
414 base::BindOnce(&WidgetInputHandlerManager::
415 HandleInputEventWithLatencyOnInputHandlingThread,
416 this, std::move(web_scoped_gesture_event)));
417 }
418
419 void WidgetInputHandlerManager::
HandleInputEventWithLatencyOnInputHandlingThread(std::unique_ptr<WebCoalescedInputEvent> event)420 HandleInputEventWithLatencyOnInputHandlingThread(
421 std::unique_ptr<WebCoalescedInputEvent> event) {
422 DCHECK(base::FeatureList::IsEnabled(features::kScrollUnification));
423 DCHECK(input_handler_proxy_);
424 input_handler_proxy_->HandleInputEventWithLatencyInfo(
425 std::move(event), nullptr, base::DoNothing());
426 }
427
DispatchEvent(std::unique_ptr<WebCoalescedInputEvent> event,mojom::blink::WidgetInputHandler::DispatchEventCallback callback)428 void WidgetInputHandlerManager::DispatchEvent(
429 std::unique_ptr<WebCoalescedInputEvent> event,
430 mojom::blink::WidgetInputHandler::DispatchEventCallback callback) {
431 bool event_is_move =
432 event->Event().GetType() == WebInputEvent::Type::kMouseMove ||
433 event->Event().GetType() == WebInputEvent::Type::kPointerMove;
434 if (!event_is_move)
435 LogInputTimingUMA();
436
437 // Drop input if we are deferring a rendering pipeline phase, unless it's a
438 // move event.
439 // We don't want users interacting with stuff they can't see, so we drop it.
440 // We allow moves because we need to keep the current pointer location up
441 // to date. Tests and other code can allow pre-commit input through the
442 // "allow-pre-commit-input" command line flag.
443 // TODO(schenney): Also allow scrolls? This would make some tests not flaky,
444 // it seems, because they sometimes crash on seeing a scroll update/end
445 // without a begin. Scrolling, pinch-zoom etc. don't seem dangerous.
446 if (renderer_deferral_state_ && !allow_pre_commit_input_ && !event_is_move) {
447 if (callback) {
448 std::move(callback).Run(
449 mojom::blink::InputEventResultSource::kMainThread, ui::LatencyInfo(),
450 mojom::blink::InputEventResultState::kNotConsumed, nullptr, nullptr);
451 }
452 return;
453 }
454
455 // If TimeTicks is not consistent across processes we cannot use the event's
456 // platform timestamp in this process. Instead use the time that the event is
457 // received as the event's timestamp.
458 if (!base::TimeTicks::IsConsistentAcrossProcesses()) {
459 event->EventPointer()->SetTimeStamp(base::TimeTicks::Now());
460 }
461
462 base::Optional<cc::EventMetrics::ScrollUpdateType> scroll_update_type;
463 if (event->Event().GetType() == WebInputEvent::Type::kGestureScrollBegin) {
464 has_seen_first_gesture_scroll_update_after_begin_ = false;
465 } else if (event->Event().GetType() ==
466 WebInputEvent::Type::kGestureScrollUpdate) {
467 if (has_seen_first_gesture_scroll_update_after_begin_) {
468 scroll_update_type = cc::EventMetrics::ScrollUpdateType::kContinued;
469 } else {
470 scroll_update_type = cc::EventMetrics::ScrollUpdateType::kStarted;
471 has_seen_first_gesture_scroll_update_after_begin_ = true;
472 }
473 }
474 std::unique_ptr<cc::EventMetrics> metrics = cc::EventMetrics::Create(
475 event->Event().GetTypeAsUiEventType(), scroll_update_type,
476 event->Event().TimeStamp(), event->Event().GetScrollInputType());
477
478 if (uses_input_handler_) {
479 // If the input_handler_proxy has disappeared ensure we just ack event.
480 if (!input_handler_proxy_) {
481 if (callback) {
482 std::move(callback).Run(
483 mojom::blink::InputEventResultSource::kMainThread,
484 ui::LatencyInfo(),
485 mojom::blink::InputEventResultState::kNotConsumed, nullptr,
486 nullptr);
487 }
488 return;
489 }
490
491 // The InputHandlerProxy will be the first to try handling the event on the
492 // compositor thread. It will respond to this class by calling
493 // DidHandleInputEventSentToCompositor with the result of its attempt. Based
494 // on the resulting disposition, DidHandleInputEventSentToCompositor will
495 // either ACK the event as handled to the browser or forward it to the main
496 // thread.
497 input_handler_proxy_->HandleInputEventWithLatencyInfo(
498 std::move(event), std::move(metrics),
499 base::BindOnce(
500 &WidgetInputHandlerManager::DidHandleInputEventSentToCompositor,
501 this, std::move(callback)));
502 } else {
503 DCHECK(!input_handler_proxy_);
504 DispatchDirectlyToWidget(std::move(event), std::move(metrics),
505 std::move(callback));
506 }
507 }
508
InvokeInputProcessedCallback()509 void WidgetInputHandlerManager::InvokeInputProcessedCallback() {
510 DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
511
512 // We can call this method even if we didn't request a callback (e.g. when
513 // the renderer becomes hidden).
514 if (!input_processed_callback_)
515 return;
516
517 // The handler's method needs to respond to the mojo message so it needs to
518 // run on the input handling thread. Even if we're already on the correct
519 // thread, we PostTask for symmetry.
520 InputThreadTaskRunner()->PostTask(FROM_HERE,
521 std::move(input_processed_callback_));
522 }
523
WaitForInputProcessedFromMain(base::WeakPtr<WidgetBase> widget)524 static void WaitForInputProcessedFromMain(base::WeakPtr<WidgetBase> widget) {
525 // If the widget is destroyed while we're posting to the main thread, the
526 // Mojo message will be acked in WidgetInputHandlerImpl's destructor.
527 if (!widget)
528 return;
529
530 WidgetInputHandlerManager* manager = widget->widget_input_handler_manager();
531
532 // TODO(bokan): Temporary to unblock synthetic gesture events running under
533 // VR. https://crbug.com/940063
534 bool ack_immediately = widget->client()->ShouldAckSyntheticInputImmediately();
535
536 // If the WidgetBase is hidden, we won't produce compositor frames for it
537 // so just ACK the input to prevent blocking the browser indefinitely.
538 if (widget->is_hidden() || ack_immediately) {
539 manager->InvokeInputProcessedCallback();
540 return;
541 }
542
543 auto redraw_complete_callback =
544 base::BindOnce(&WidgetInputHandlerManager::InvokeInputProcessedCallback,
545 manager->AsWeakPtr());
546
547 // Since wheel-events can kick off animations, we can not consider
548 // all observable effects of an input gesture to be processed
549 // when the CompositorFrame caused by that input has been produced, send, and
550 // displayed. Therefore, explicitly request the presentation *after* any
551 // ongoing scroll-animation ends. After the scroll-animation ends (if any),
552 // the call will force a commit and redraw and callback when the
553 // CompositorFrame has been displayed in the display service. Some examples of
554 // non-trivial effects that require waiting that long: committing
555 // NonFastScrollRegions to the compositor, sending touch-action rects to the
556 // browser, and sending updated surface information to the display compositor
557 // for up-to-date OOPIF hit-testing.
558
559 widget->RequestPresentationAfterScrollAnimationEnd(
560 std::move(redraw_complete_callback));
561 }
562
WaitForInputProcessed(base::OnceClosure callback)563 void WidgetInputHandlerManager::WaitForInputProcessed(
564 base::OnceClosure callback) {
565 // Note, this will be called from the mojo-bound thread which could be either
566 // main or compositor.
567 DCHECK(!input_processed_callback_);
568 input_processed_callback_ = std::move(callback);
569
570 // We mustn't touch widget_ from the impl thread so post all the setup
571 // to the main thread. Make sure the callback runs after all the queued events
572 // are dispatched.
573 input_event_queue_->QueueClosure(
574 base::BindOnce(&WaitForInputProcessedFromMain, widget_));
575 }
576
DidNavigate()577 void WidgetInputHandlerManager::DidNavigate() {
578 renderer_deferral_state_ = 0;
579 have_emitted_uma_ = false;
580 }
581
OnDeferMainFrameUpdatesChanged(bool status)582 void WidgetInputHandlerManager::OnDeferMainFrameUpdatesChanged(bool status) {
583 if (status) {
584 renderer_deferral_state_ |=
585 static_cast<uint16_t>(RenderingDeferralBits::kDeferMainFrameUpdates);
586 } else {
587 renderer_deferral_state_ &=
588 ~static_cast<uint16_t>(RenderingDeferralBits::kDeferMainFrameUpdates);
589 }
590 }
591
OnDeferCommitsChanged(bool status)592 void WidgetInputHandlerManager::OnDeferCommitsChanged(bool status) {
593 if (status) {
594 renderer_deferral_state_ |=
595 static_cast<uint16_t>(RenderingDeferralBits::kDeferCommits);
596 } else {
597 renderer_deferral_state_ &=
598 ~static_cast<uint16_t>(RenderingDeferralBits::kDeferCommits);
599 }
600 }
601
InitOnInputHandlingThread(const base::WeakPtr<cc::CompositorDelegateForInput> & compositor_delegate,bool sync_compositing)602 void WidgetInputHandlerManager::InitOnInputHandlingThread(
603 const base::WeakPtr<cc::CompositorDelegateForInput>& compositor_delegate,
604 bool sync_compositing) {
605 DCHECK(InputThreadTaskRunner()->BelongsToCurrentThread());
606 DCHECK(uses_input_handler_);
607
608 // It is possible that the input_handler has already been destroyed before
609 // this Init() call was invoked. If so, early out.
610 if (!compositor_delegate)
611 return;
612
613 // The input handler is created and ownership is passed to the compositor
614 // delegate; hence we only receive a WeakPtr back.
615 base::WeakPtr<cc::InputHandler> input_handler =
616 cc::InputHandler::Create(*compositor_delegate);
617 DCHECK(input_handler);
618
619 input_handler_proxy_ =
620 std::make_unique<InputHandlerProxy>(*input_handler.get(), this);
621
622 #if defined(OS_ANDROID)
623 if (sync_compositing) {
624 DCHECK(synchronous_compositor_registry_);
625 synchronous_compositor_registry_->CreateProxy(input_handler_proxy_.get());
626 }
627 #endif
628 }
629
BindChannel(mojo::PendingReceiver<mojom::blink::WidgetInputHandler> receiver)630 void WidgetInputHandlerManager::BindChannel(
631 mojo::PendingReceiver<mojom::blink::WidgetInputHandler> receiver) {
632 if (!receiver.is_valid())
633 return;
634 // Don't pass the |input_event_queue_| on if we don't have a
635 // |compositor_task_runner_| as events might get out of order.
636 WidgetInputHandlerImpl* handler = new WidgetInputHandlerImpl(
637 this, main_thread_task_runner_,
638 compositor_task_runner_ ? input_event_queue_ : nullptr, widget_);
639 handler->SetReceiver(std::move(receiver));
640 }
641
DispatchDirectlyToWidget(std::unique_ptr<WebCoalescedInputEvent> event,std::unique_ptr<cc::EventMetrics> metrics,mojom::blink::WidgetInputHandler::DispatchEventCallback callback)642 void WidgetInputHandlerManager::DispatchDirectlyToWidget(
643 std::unique_ptr<WebCoalescedInputEvent> event,
644 std::unique_ptr<cc::EventMetrics> metrics,
645 mojom::blink::WidgetInputHandler::DispatchEventCallback callback) {
646 // This path should only be taken by non-frame WidgetBase that don't use a
647 // compositor (e.g. popups, plugins). Events bounds for a frame WidgetBase
648 // must be passed through the InputHandlerProxy first.
649 DCHECK(!uses_input_handler_);
650
651 // Input messages must not be processed if the WidgetBase was destroyed or
652 // was just recreated for a provisional frame.
653 if (!widget_ || widget_->IsForProvisionalFrame()) {
654 if (callback) {
655 std::move(callback).Run(mojom::blink::InputEventResultSource::kMainThread,
656 event->latency_info(),
657 mojom::blink::InputEventResultState::kNotConsumed,
658 nullptr, nullptr);
659 }
660 return;
661 }
662
663 auto send_callback = base::BindOnce(
664 &WidgetInputHandlerManager::DidHandleInputEventSentToMainFromWidgetBase,
665 this, std::move(callback));
666
667 widget_->input_handler().HandleInputEvent(*event, std::move(metrics),
668 std::move(send_callback));
669 }
670
FindScrollTargetReply(std::unique_ptr<WebCoalescedInputEvent> event,std::unique_ptr<cc::EventMetrics> metrics,mojom::blink::WidgetInputHandler::DispatchEventCallback browser_callback,uint64_t hit_test_result)671 void WidgetInputHandlerManager::FindScrollTargetReply(
672 std::unique_ptr<WebCoalescedInputEvent> event,
673 std::unique_ptr<cc::EventMetrics> metrics,
674 mojom::blink::WidgetInputHandler::DispatchEventCallback browser_callback,
675 uint64_t hit_test_result) {
676 TRACE_EVENT1("input", "WidgetInputHandlerManager::FindScrollTargetReply",
677 "hit_test_result", hit_test_result);
678 DCHECK(InputThreadTaskRunner()->BelongsToCurrentThread());
679 DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
680
681 // If the input_handler was destroyed in the mean time just ACK the event as
682 // unconsumed to the browser and drop further handling.
683 if (!input_handler_proxy_) {
684 std::move(browser_callback)
685 .Run(mojom::blink::InputEventResultSource::kMainThread,
686 ui::LatencyInfo(),
687 mojom::blink::InputEventResultState::kNotConsumed, nullptr,
688 nullptr);
689 return;
690 }
691
692 input_handler_proxy_->ContinueScrollBeginAfterMainThreadHitTest(
693 std::move(event), std::move(metrics),
694 base::BindOnce(
695 &WidgetInputHandlerManager::DidHandleInputEventSentToCompositor, this,
696 std::move(browser_callback)),
697 hit_test_result);
698 }
699
DidHandleInputEventSentToCompositor(mojom::blink::WidgetInputHandler::DispatchEventCallback callback,InputHandlerProxy::EventDisposition event_disposition,std::unique_ptr<WebCoalescedInputEvent> event,std::unique_ptr<InputHandlerProxy::DidOverscrollParams> overscroll_params,const WebInputEventAttribution & attribution,std::unique_ptr<cc::EventMetrics> metrics)700 void WidgetInputHandlerManager::DidHandleInputEventSentToCompositor(
701 mojom::blink::WidgetInputHandler::DispatchEventCallback callback,
702 InputHandlerProxy::EventDisposition event_disposition,
703 std::unique_ptr<WebCoalescedInputEvent> event,
704 std::unique_ptr<InputHandlerProxy::DidOverscrollParams> overscroll_params,
705 const WebInputEventAttribution& attribution,
706 std::unique_ptr<cc::EventMetrics> metrics) {
707 TRACE_EVENT1("input",
708 "WidgetInputHandlerManager::DidHandleInputEventSentToCompositor",
709 "Disposition", event_disposition);
710 DCHECK(InputThreadTaskRunner()->BelongsToCurrentThread());
711
712 ui::LatencyInfo::TraceIntermediateFlowEvents(
713 {event->latency_info()},
714 ChromeLatencyInfo::STEP_DID_HANDLE_INPUT_AND_OVERSCROLL);
715
716 if (event_disposition == InputHandlerProxy::REQUIRES_MAIN_THREAD_HIT_TEST) {
717 TRACE_EVENT_INSTANT0("input", "PostingHitTestToMainThread",
718 TRACE_EVENT_SCOPE_THREAD);
719 // TODO(bokan): We're going to need to perform a hit test on the main thread
720 // before we can continue handling the event. This is the critical path of a
721 // scroll so we should probably ensure the scheduler can prioritize it
722 // accordingly. https://crbug.com/1082618.
723 DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
724 DCHECK_EQ(event->Event().GetType(),
725 WebInputEvent::Type::kGestureScrollBegin);
726 DCHECK(input_handler_proxy_);
727
728 gfx::PointF event_position =
729 static_cast<const WebGestureEvent&>(event->Event()).PositionInWidget();
730
731 ElementAtPointCallback result_callback = base::BindOnce(
732 &WidgetInputHandlerManager::FindScrollTargetReply, this->AsWeakPtr(),
733 std::move(event), std::move(metrics), std::move(callback));
734
735 main_thread_task_runner_->PostTask(
736 FROM_HERE,
737 base::BindOnce(&WidgetInputHandlerManager::FindScrollTargetOnMainThread,
738 this, event_position, std::move(result_callback)));
739 return;
740 }
741
742 mojom::blink::InputEventResultState ack_state =
743 InputEventDispositionToAck(event_disposition);
744 if (ack_state == mojom::blink::InputEventResultState::kConsumed) {
745 main_thread_scheduler_->DidHandleInputEventOnCompositorThread(
746 event->Event(), scheduler::WebThreadScheduler::InputEventState::
747 EVENT_CONSUMED_BY_COMPOSITOR);
748 } else if (MainThreadEventQueue::IsForwardedAndSchedulerKnown(ack_state)) {
749 main_thread_scheduler_->DidHandleInputEventOnCompositorThread(
750 event->Event(), scheduler::WebThreadScheduler::InputEventState::
751 EVENT_FORWARDED_TO_MAIN_THREAD);
752 }
753
754 if (ack_state == mojom::blink::InputEventResultState::kSetNonBlocking ||
755 ack_state ==
756 mojom::blink::InputEventResultState::kSetNonBlockingDueToFling ||
757 ack_state == mojom::blink::InputEventResultState::kNotConsumed) {
758 DCHECK(!overscroll_params);
759 DCHECK(!event->latency_info().coalesced());
760 MainThreadEventQueue::DispatchType dispatch_type =
761 callback.is_null() ? MainThreadEventQueue::DispatchType::kNonBlocking
762 : MainThreadEventQueue::DispatchType::kBlocking;
763 HandledEventCallback handled_event = base::BindOnce(
764 &WidgetInputHandlerManager::DidHandleInputEventSentToMain, this,
765 std::move(callback));
766 input_event_queue_->HandleEvent(std::move(event), dispatch_type, ack_state,
767 attribution, std::move(metrics),
768 std::move(handled_event));
769 return;
770 }
771
772 if (callback) {
773 std::move(callback).Run(
774 mojom::blink::InputEventResultSource::kCompositorThread,
775 event->latency_info(), ack_state,
776 ToDidOverscrollParams(overscroll_params.get()),
777 allowed_touch_action_ ? mojom::blink::TouchActionOptional::New(
778 allowed_touch_action_.value())
779 : nullptr);
780 }
781 }
782
DidHandleInputEventSentToMainFromWidgetBase(mojom::blink::WidgetInputHandler::DispatchEventCallback callback,mojom::blink::InputEventResultState ack_state,const ui::LatencyInfo & latency_info,std::unique_ptr<blink::InputHandlerProxy::DidOverscrollParams> overscroll_params,base::Optional<cc::TouchAction> touch_action)783 void WidgetInputHandlerManager::DidHandleInputEventSentToMainFromWidgetBase(
784 mojom::blink::WidgetInputHandler::DispatchEventCallback callback,
785 mojom::blink::InputEventResultState ack_state,
786 const ui::LatencyInfo& latency_info,
787 std::unique_ptr<blink::InputHandlerProxy::DidOverscrollParams>
788 overscroll_params,
789 base::Optional<cc::TouchAction> touch_action) {
790 DidHandleInputEventSentToMain(std::move(callback), ack_state, latency_info,
791 ToDidOverscrollParams(overscroll_params.get()),
792 touch_action);
793 }
794
DidHandleInputEventSentToMain(mojom::blink::WidgetInputHandler::DispatchEventCallback callback,mojom::blink::InputEventResultState ack_state,const ui::LatencyInfo & latency_info,mojom::blink::DidOverscrollParamsPtr overscroll_params,base::Optional<cc::TouchAction> touch_action)795 void WidgetInputHandlerManager::DidHandleInputEventSentToMain(
796 mojom::blink::WidgetInputHandler::DispatchEventCallback callback,
797 mojom::blink::InputEventResultState ack_state,
798 const ui::LatencyInfo& latency_info,
799 mojom::blink::DidOverscrollParamsPtr overscroll_params,
800 base::Optional<cc::TouchAction> touch_action) {
801 if (!callback)
802 return;
803
804 TRACE_EVENT1("input",
805 "WidgetInputHandlerManager::DidHandleInputEventSentToMain",
806 "ack_state", ack_state);
807 ui::LatencyInfo::TraceIntermediateFlowEvents(
808 {latency_info}, ChromeLatencyInfo::STEP_HANDLED_INPUT_EVENT_MAIN_OR_IMPL);
809
810 if (!touch_action.has_value()) {
811 TRACE_EVENT_INSTANT0("input", "Using allowed_touch_action",
812 TRACE_EVENT_SCOPE_THREAD);
813 touch_action = allowed_touch_action_;
814 allowed_touch_action_.reset();
815 }
816 // This method is called from either the main thread or the compositor thread.
817 bool is_compositor_thread = compositor_task_runner_ &&
818 compositor_task_runner_->BelongsToCurrentThread();
819
820 // If there is a compositor task runner and the current thread isn't the
821 // compositor thread proxy it over to the compositor thread.
822 if (compositor_task_runner_ && !is_compositor_thread) {
823 TRACE_EVENT_INSTANT0("input", "PostingToCompositor",
824 TRACE_EVENT_SCOPE_THREAD);
825 compositor_task_runner_->PostTask(
826 FROM_HERE, base::BindOnce(CallCallback, std::move(callback), ack_state,
827 latency_info, std::move(overscroll_params),
828 touch_action));
829 } else {
830 // Otherwise call the callback immediately.
831 std::move(callback).Run(
832 is_compositor_thread
833 ? mojom::blink::InputEventResultSource::kCompositorThread
834 : mojom::blink::InputEventResultSource::kMainThread,
835 latency_info, ack_state, std::move(overscroll_params),
836 touch_action
837 ? mojom::blink::TouchActionOptional::New(touch_action.value())
838 : nullptr);
839 }
840 }
841
ObserveGestureEventOnInputHandlingThread(const WebGestureEvent & gesture_event,const cc::InputHandlerScrollResult & scroll_result)842 void WidgetInputHandlerManager::ObserveGestureEventOnInputHandlingThread(
843 const WebGestureEvent& gesture_event,
844 const cc::InputHandlerScrollResult& scroll_result) {
845 if (!input_handler_proxy_)
846 return;
847 DCHECK(input_handler_proxy_->elastic_overscroll_controller());
848 input_handler_proxy_->elastic_overscroll_controller()
849 ->ObserveGestureEventAndResult(gesture_event, scroll_result);
850 }
851
852 const scoped_refptr<base::SingleThreadTaskRunner>&
InputThreadTaskRunner() const853 WidgetInputHandlerManager::InputThreadTaskRunner() const {
854 if (compositor_task_runner_)
855 return compositor_task_runner_;
856 return main_thread_task_runner_;
857 }
858
859 #if defined(OS_ANDROID)
860 SynchronousCompositorRegistry*
GetSynchronousCompositorRegistry()861 WidgetInputHandlerManager::GetSynchronousCompositorRegistry() {
862 DCHECK(synchronous_compositor_registry_);
863 return synchronous_compositor_registry_.get();
864 }
865 #endif
866
ClearClient()867 void WidgetInputHandlerManager::ClearClient() {
868 input_event_queue_->ClearClient();
869 }
870
871 } // namespace blink
872