blob: 96d8eff80003f758d9838387220417b7262dbae1 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/input/widget_input_handler_manager.h"
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "content/common/input_messages.h"
#include "content/renderer/gpu/render_widget_compositor.h"
#include "content/renderer/ime_event_guard.h"
#include "content/renderer/input/widget_input_handler_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/render_widget.h"
#include "third_party/WebKit/public/platform/Platform.h"
#include "third_party/WebKit/public/platform/WebCoalescedInputEvent.h"
#include "third_party/WebKit/public/platform/WebKeyboardEvent.h"
#include "third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
namespace content {
namespace {
InputEventAckState InputEventDispositionToAck(
ui::InputHandlerProxy::EventDisposition disposition) {
switch (disposition) {
case ui::InputHandlerProxy::DID_HANDLE:
return INPUT_EVENT_ACK_STATE_CONSUMED;
case ui::InputHandlerProxy::DID_NOT_HANDLE:
return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
case ui::InputHandlerProxy::DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING:
return INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING_DUE_TO_FLING;
case ui::InputHandlerProxy::DROP_EVENT:
return INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
case ui::InputHandlerProxy::DID_HANDLE_NON_BLOCKING:
return INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING;
case ui::InputHandlerProxy::DID_HANDLE_SHOULD_BUBBLE:
return INPUT_EVENT_ACK_STATE_CONSUMED_SHOULD_BUBBLE;
}
NOTREACHED();
return INPUT_EVENT_ACK_STATE_UNKNOWN;
}
void CallCallback(mojom::WidgetInputHandler::DispatchEventCallback callback,
InputEventAckState ack_state,
const ui::LatencyInfo& latency_info,
std::unique_ptr<ui::DidOverscrollParams> overscroll_params) {
std::move(callback).Run(
InputEventAckSource::MAIN_THREAD, latency_info, ack_state,
overscroll_params
? base::Optional<ui::DidOverscrollParams>(*overscroll_params)
: base::nullopt);
}
} // namespace
scoped_refptr<WidgetInputHandlerManager> WidgetInputHandlerManager::Create(
base::WeakPtr<RenderWidget> render_widget,
IPC::Sender* legacy_host_channel,
scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
blink::scheduler::RendererScheduler* renderer_scheduler) {
scoped_refptr<WidgetInputHandlerManager> manager =
new WidgetInputHandlerManager(
std::move(render_widget), legacy_host_channel,
std::move(compositor_task_runner), renderer_scheduler);
manager->Init();
return manager;
}
WidgetInputHandlerManager::WidgetInputHandlerManager(
base::WeakPtr<RenderWidget> render_widget,
IPC::Sender* legacy_host_channel,
scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
blink::scheduler::RendererScheduler* renderer_scheduler)
: render_widget_(render_widget),
renderer_scheduler_(renderer_scheduler),
input_event_queue_(render_widget->GetInputEventQueue()),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
legacy_host_message_sender_(legacy_host_channel),
compositor_task_runner_(compositor_task_runner) {
// TODO(dtapuska): Define a mojo channel for back to the host. Currently
// we use legacy IPC.
legacy_host_message_routing_id_ = render_widget->routing_id();
}
void WidgetInputHandlerManager::Init() {
if (compositor_task_runner_) {
compositor_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WidgetInputHandlerManager::InitOnCompositorThread, this,
render_widget_->compositor()->GetInputHandler(),
render_widget_->compositor_deps()->IsScrollAnimatorEnabled()));
}
}
WidgetInputHandlerManager::~WidgetInputHandlerManager() {}
void WidgetInputHandlerManager::AddAssociatedInterface(
mojom::WidgetInputHandlerAssociatedRequest request) {
if (compositor_task_runner_) {
// Mojo channel bound on compositor thread.
compositor_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WidgetInputHandlerManager::BindAssociatedChannel, this,
std::move(request)));
} else {
// Mojo channel bound on main thread.
BindAssociatedChannel(std::move(request));
}
}
void WidgetInputHandlerManager::AddInterface(
mojom::WidgetInputHandlerRequest request) {
if (compositor_task_runner_) {
// Mojo channel bound on compositor thread.
compositor_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&WidgetInputHandlerManager::BindChannel, this,
std::move(request)));
} else {
// Mojo channel bound on main thread.
BindChannel(std::move(request));
}
}
void WidgetInputHandlerManager::WillShutdown() {}
void WidgetInputHandlerManager::TransferActiveWheelFlingAnimation(
const blink::WebActiveWheelFlingParameters& params) {
main_thread_task_runner_->PostTask(
FROM_HERE, base::Bind(&RenderWidget::TransferActiveWheelFlingAnimation,
render_widget_, params));
}
void WidgetInputHandlerManager::DispatchNonBlockingEventToMainThread(
ui::WebScopedInputEvent event,
const ui::LatencyInfo& latency_info) {
DCHECK(input_event_queue_);
input_event_queue_->HandleEvent(
std::move(event), latency_info, DISPATCH_TYPE_NON_BLOCKING,
INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING, HandledEventCallback());
}
std::unique_ptr<blink::WebGestureCurve>
WidgetInputHandlerManager::CreateFlingAnimationCurve(
blink::WebGestureDevice device_source,
const blink::WebFloatPoint& velocity,
const blink::WebSize& cumulative_scroll) {
return blink::Platform::Current()->CreateFlingAnimationCurve(
device_source, velocity, cumulative_scroll);
}
void WidgetInputHandlerManager::DidOverscroll(
const gfx::Vector2dF& accumulated_overscroll,
const gfx::Vector2dF& latest_overscroll_delta,
const gfx::Vector2dF& current_fling_velocity,
const gfx::PointF& causal_event_viewport_point) {
ui::DidOverscrollParams params;
params.accumulated_overscroll = accumulated_overscroll;
params.latest_overscroll_delta = latest_overscroll_delta;
params.current_fling_velocity = current_fling_velocity;
params.causal_event_viewport_point = causal_event_viewport_point;
if (legacy_host_message_sender_) {
legacy_host_message_sender_->Send(new InputHostMsg_DidOverscroll(
legacy_host_message_routing_id_, params));
}
}
void WidgetInputHandlerManager::DidStopFlinging() {
if (legacy_host_message_sender_) {
legacy_host_message_sender_->Send(
new InputHostMsg_DidStopFlinging(legacy_host_message_routing_id_));
}
}
void WidgetInputHandlerManager::DidAnimateForInput() {
renderer_scheduler_->DidAnimateForInputOnCompositorThread();
}
void WidgetInputHandlerManager::GenerateScrollBeginAndSendToMainThread(
const blink::WebGestureEvent& update_event) {
DCHECK_EQ(update_event.GetType(), blink::WebInputEvent::kGestureScrollUpdate);
blink::WebGestureEvent scroll_begin(update_event);
scroll_begin.SetType(blink::WebInputEvent::kGestureScrollBegin);
scroll_begin.data.scroll_begin.inertial_phase =
update_event.data.scroll_update.inertial_phase;
scroll_begin.data.scroll_begin.delta_x_hint =
update_event.data.scroll_update.delta_x;
scroll_begin.data.scroll_begin.delta_y_hint =
update_event.data.scroll_update.delta_y;
scroll_begin.data.scroll_begin.delta_hint_units =
update_event.data.scroll_update.delta_units;
DispatchNonBlockingEventToMainThread(
ui::WebInputEventTraits::Clone(scroll_begin), ui::LatencyInfo());
}
void WidgetInputHandlerManager::SetWhiteListedTouchAction(
cc::TouchAction touch_action,
uint32_t unique_touch_event_id,
ui::InputHandlerProxy::EventDisposition event_disposition) {
InputEventAckState ack_state = InputEventDispositionToAck(event_disposition);
legacy_host_message_sender_->Send(new InputHostMsg_SetWhiteListedTouchAction(
legacy_host_message_routing_id_, touch_action, unique_touch_event_id,
ack_state));
}
void WidgetInputHandlerManager::ObserveGestureEventOnMainThread(
const blink::WebGestureEvent& gesture_event,
const cc::InputHandlerScrollResult& scroll_result) {
if (compositor_task_runner_) {
compositor_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WidgetInputHandlerManager::ObserveGestureEventOnCompositorThread,
this, gesture_event, scroll_result));
}
}
void WidgetInputHandlerManager::DispatchEvent(
std::unique_ptr<content::InputEvent> event,
mojom::WidgetInputHandler::DispatchEventCallback callback) {
if (!event || !event->web_event) {
return;
}
if (compositor_task_runner_) {
CHECK(!main_thread_task_runner_->BelongsToCurrentThread());
input_handler_proxy_->HandleInputEventWithLatencyInfo(
std::move(event->web_event), event->latency_info,
base::BindOnce(
&WidgetInputHandlerManager::DidHandleInputEventAndOverscroll, this,
std::move(callback)));
} else {
HandleInputEvent(std::move(event->web_event), event->latency_info,
std::move(callback));
}
}
void WidgetInputHandlerManager::InitOnCompositorThread(
const base::WeakPtr<cc::InputHandler>& input_handler,
bool smooth_scroll_enabled) {
input_handler_proxy_ = base::MakeUnique<ui::InputHandlerProxy>(
input_handler.get(), this,
base::FeatureList::IsEnabled(features::kTouchpadAndWheelScrollLatching));
input_handler_proxy_->set_smooth_scroll_enabled(smooth_scroll_enabled);
}
void WidgetInputHandlerManager::BindAssociatedChannel(
mojom::WidgetInputHandlerAssociatedRequest request) {
WidgetInputHandlerImpl* handler = new WidgetInputHandlerImpl(
this, main_thread_task_runner_, input_event_queue_, render_widget_);
handler->SetAssociatedBinding(std::move(request));
}
void WidgetInputHandlerManager::BindChannel(
mojom::WidgetInputHandlerRequest request) {
WidgetInputHandlerImpl* handler = new WidgetInputHandlerImpl(
this, main_thread_task_runner_, input_event_queue_, render_widget_);
handler->SetBinding(std::move(request));
}
void WidgetInputHandlerManager::HandleInputEvent(
const ui::WebScopedInputEvent& event,
const ui::LatencyInfo& latency,
mojom::WidgetInputHandler::DispatchEventCallback callback) {
if (!render_widget_) {
std::move(callback).Run(InputEventAckSource::MAIN_THREAD, latency,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED, base::nullopt);
return;
}
auto send_callback = base::BindOnce(
&WidgetInputHandlerManager::HandledInputEvent, this, std::move(callback));
blink::WebCoalescedInputEvent coalesced_event(*event);
render_widget_->HandleInputEvent(coalesced_event, latency,
std::move(send_callback));
}
void WidgetInputHandlerManager::DidHandleInputEventAndOverscroll(
mojom::WidgetInputHandler::DispatchEventCallback callback,
ui::InputHandlerProxy::EventDisposition event_disposition,
ui::WebScopedInputEvent input_event,
const ui::LatencyInfo& latency_info,
std::unique_ptr<ui::DidOverscrollParams> overscroll_params) {
InputEventAckState ack_state = InputEventDispositionToAck(event_disposition);
switch (ack_state) {
case INPUT_EVENT_ACK_STATE_CONSUMED:
renderer_scheduler_->DidHandleInputEventOnCompositorThread(
*input_event, blink::scheduler::RendererScheduler::InputEventState::
EVENT_CONSUMED_BY_COMPOSITOR);
break;
case INPUT_EVENT_ACK_STATE_NOT_CONSUMED:
case INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING_DUE_TO_FLING:
renderer_scheduler_->DidHandleInputEventOnCompositorThread(
*input_event, blink::scheduler::RendererScheduler::InputEventState::
EVENT_FORWARDED_TO_MAIN_THREAD);
break;
default:
break;
}
if (ack_state == INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING ||
ack_state == INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING_DUE_TO_FLING ||
ack_state == INPUT_EVENT_ACK_STATE_NOT_CONSUMED) {
DCHECK(!overscroll_params);
InputEventDispatchType dispatch_type = callback.is_null()
? DISPATCH_TYPE_NON_BLOCKING
: DISPATCH_TYPE_BLOCKING;
HandledEventCallback handled_event =
base::BindOnce(&WidgetInputHandlerManager::HandledInputEvent, this,
std::move(callback));
input_event_queue_->HandleEvent(std::move(input_event), latency_info,
dispatch_type, ack_state,
std::move(handled_event));
return;
}
if (callback) {
std::move(callback).Run(
InputEventAckSource::COMPOSITOR_THREAD, latency_info, ack_state,
overscroll_params
? base::Optional<ui::DidOverscrollParams>(*overscroll_params)
: base::nullopt);
}
}
void WidgetInputHandlerManager::HandledInputEvent(
mojom::WidgetInputHandler::DispatchEventCallback callback,
InputEventAckState ack_state,
const ui::LatencyInfo& latency_info,
std::unique_ptr<ui::DidOverscrollParams> overscroll_params) {
if (!callback)
return;
if (compositor_task_runner_) {
compositor_task_runner_->PostTask(
FROM_HERE, base::BindOnce(CallCallback, std::move(callback), ack_state,
latency_info, std::move(overscroll_params)));
} else {
std::move(callback).Run(
InputEventAckSource::COMPOSITOR_THREAD, latency_info, ack_state,
overscroll_params
? base::Optional<ui::DidOverscrollParams>(*overscroll_params)
: base::nullopt);
}
}
void WidgetInputHandlerManager::ObserveGestureEventOnCompositorThread(
const blink::WebGestureEvent& gesture_event,
const cc::InputHandlerScrollResult& scroll_result) {
DCHECK(input_handler_proxy_->scroll_elasticity_controller());
input_handler_proxy_->scroll_elasticity_controller()
->ObserveGestureEventAndResult(gesture_event, scroll_result);
}
} // namespace content