blob: 16fba23fd1d18bd18ae47dd83f296f8d6fe04dc7 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/input/input_router_impl.h"
#include <math.h>
#include <utility>
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/renderer_host/input/gesture_event_queue.h"
#include "content/browser/renderer_host/input/input_disposition_handler.h"
#include "content/browser/renderer_host/input/input_router_client.h"
#include "content/common/content_constants_internal.h"
#include "content/common/input/web_touch_event_traits.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/input_event_ack_state.h"
#include "ipc/ipc_sender.h"
#include "services/tracing/public/cpp/perfetto/flow_event_utils.h"
#include "third_party/blink/public/common/input/web_coalesced_input_event.h"
#include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h"
#include "third_party/blink/public/mojom/input/input_handler.mojom-forward.h"
#include "third_party/blink/public/mojom/input/input_handler.mojom-shared.h"
#include "third_party/blink/public/mojom/input/touch_event.mojom.h"
#include "ui/events/blink/blink_event_util.h"
#include "ui/events/blink/blink_features.h"
#include "ui/events/blink/did_overscroll_params.h"
#include "ui/events/blink/web_input_event_traits.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_codes.h"
namespace content {
using blink::WebGestureEvent;
using blink::WebInputEvent;
using blink::WebKeyboardEvent;
using blink::WebMouseEvent;
using blink::WebMouseWheelEvent;
using blink::WebTouchEvent;
using perfetto::protos::pbzero::ChromeLatencyInfo;
using perfetto::protos::pbzero::TrackEvent;
using ui::WebInputEventTraits;
namespace {
bool WasHandled(blink::mojom::InputEventResultState state) {
switch (state) {
case blink::mojom::InputEventResultState::kConsumed:
case blink::mojom::InputEventResultState::kNoConsumerExists:
case blink::mojom::InputEventResultState::kUnknown:
return true;
default:
return false;
}
}
std::unique_ptr<blink::WebCoalescedInputEvent> ScaleEvent(
const WebInputEvent& event,
double scale,
const ui::LatencyInfo& latency_info) {
std::unique_ptr<blink::WebInputEvent> event_in_viewport =
ui::ScaleWebInputEvent(event, scale);
return std::make_unique<blink::WebCoalescedInputEvent>(
event_in_viewport ? std::move(event_in_viewport) : event.Clone(),
std::vector<std::unique_ptr<WebInputEvent>>(),
std::vector<std::unique_ptr<WebInputEvent>>(), latency_info);
}
} // namespace
InputRouterImpl::InputRouterImpl(
InputRouterImplClient* client,
InputDispositionHandler* disposition_handler,
FlingControllerSchedulerClient* fling_scheduler_client,
const Config& config)
: client_(client),
disposition_handler_(disposition_handler),
touch_scroll_started_sent_(false),
wheel_event_queue_(this),
touch_event_queue_(this, config.touch_config),
touchpad_pinch_event_queue_(this),
gesture_event_queue_(this,
this,
fling_scheduler_client,
config.gesture_config),
device_scale_factor_(1.f) {
weak_this_ = weak_ptr_factory_.GetWeakPtr();
DCHECK(client);
DCHECK(disposition_handler);
DCHECK(fling_scheduler_client);
UpdateTouchAckTimeoutEnabled();
}
InputRouterImpl::~InputRouterImpl() {}
void InputRouterImpl::SendMouseEvent(
const MouseEventWithLatencyInfo& mouse_event,
MouseEventCallback event_result_callback) {
if ((mouse_event.event.GetType() == WebInputEvent::Type::kMouseDown &&
gesture_event_queue_.GetTouchpadTapSuppressionController()
->ShouldSuppressMouseDown(mouse_event)) ||
(mouse_event.event.GetType() == WebInputEvent::Type::kMouseUp &&
gesture_event_queue_.GetTouchpadTapSuppressionController()
->ShouldSuppressMouseUp())) {
std::move(event_result_callback)
.Run(mouse_event, blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
return;
}
SendMouseEventImmediately(mouse_event, std::move(event_result_callback));
}
void InputRouterImpl::SendWheelEvent(
const MouseWheelEventWithLatencyInfo& wheel_event) {
wheel_event_queue_.QueueEvent(wheel_event);
}
void InputRouterImpl::SendKeyboardEvent(
const NativeWebKeyboardEventWithLatencyInfo& key_event,
KeyboardEventCallback event_result_callback) {
gesture_event_queue_.StopFling();
blink::mojom::WidgetInputHandler::DispatchEventCallback callback =
base::BindOnce(&InputRouterImpl::KeyboardEventHandled, weak_this_,
key_event, std::move(event_result_callback));
FilterAndSendWebInputEvent(key_event.event, key_event.latency,
std::move(callback));
}
void InputRouterImpl::SendGestureEvent(
const GestureEventWithLatencyInfo& original_gesture_event) {
TRACE_EVENT0("input", "InputRouterImpl::SendGestureEvent");
input_stream_validator_.Validate(original_gesture_event.event);
GestureEventWithLatencyInfo gesture_event(original_gesture_event);
if (gesture_event_queue_.PassToFlingController(gesture_event)) {
TRACE_EVENT_INSTANT0("input", "FilteredForFling", TRACE_EVENT_SCOPE_THREAD);
disposition_handler_->OnGestureEventAck(
gesture_event, blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kConsumed,
/*scroll_result_data=*/nullptr);
return;
}
FilterGestureEventResult result =
touch_action_filter_.FilterGestureEvent(&gesture_event.event);
if (result == FilterGestureEventResult::kFilterGestureEventDelayed) {
TRACE_EVENT_INSTANT0("input", "DeferredForTouchAction",
TRACE_EVENT_SCOPE_THREAD);
gesture_event_queue_.QueueDeferredEvents(gesture_event);
return;
}
SendGestureEventWithoutQueueing(gesture_event, result);
}
void InputRouterImpl::SendGestureEventWithoutQueueing(
GestureEventWithLatencyInfo& gesture_event,
const FilterGestureEventResult& existing_result) {
TRACE_EVENT0("input", "InputRouterImpl::SendGestureEventWithoutQueueing");
DCHECK_NE(existing_result,
FilterGestureEventResult::kFilterGestureEventDelayed);
if (existing_result ==
FilterGestureEventResult::kFilterGestureEventFiltered) {
TRACE_EVENT_INSTANT0("input", "FilteredForTouchAction",
TRACE_EVENT_SCOPE_THREAD);
disposition_handler_->OnGestureEventAck(
gesture_event, blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kConsumed,
/*scroll_result_data=*/nullptr);
return;
}
// Handle scroll gesture events for stylus writing. If we could not start
// writing for any reason, we should not filter the scroll events.
if (HandleGestureScrollForStylusWriting(gesture_event.event)) {
disposition_handler_->OnGestureEventAck(
gesture_event, blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kConsumed,
/*scroll_result_data=*/nullptr);
return;
}
wheel_event_queue_.OnGestureScrollEvent(gesture_event);
if (gesture_event.event.SourceDevice() ==
blink::WebGestureDevice::kTouchscreen) {
if (gesture_event.event.GetType() ==
blink::WebInputEvent::Type::kGestureScrollBegin) {
touch_scroll_started_sent_ = false;
} else if (!touch_scroll_started_sent_ &&
gesture_event.event.GetType() ==
blink::WebInputEvent::Type::kGestureScrollUpdate) {
// A touch scroll hasn't really started until the first
// GestureScrollUpdate event. Eg. if the page consumes all touchmoves
// then no scrolling really ever occurs (even though we still send
// GestureScrollBegin).
touch_scroll_started_sent_ = true;
touch_event_queue_.PrependTouchScrollNotification();
}
}
if (gesture_event.event.IsTouchpadZoomEvent() &&
gesture_event.event.NeedsWheelEvent()) {
touchpad_pinch_event_queue_.QueueEvent(gesture_event);
return;
}
if (!gesture_event_queue_.DebounceOrForwardEvent(gesture_event)) {
TRACE_EVENT_INSTANT0("input", "FilteredForDebounce",
TRACE_EVENT_SCOPE_THREAD);
disposition_handler_->OnGestureEventAck(
gesture_event, blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kConsumed,
/*scroll_result_data=*/nullptr);
}
}
bool InputRouterImpl::HandleGestureScrollForStylusWriting(
const blink::WebGestureEvent& event) {
switch (event.GetType()) {
case WebInputEvent::Type::kGestureScrollBegin: {
if (event.data.scroll_begin.pointer_count != 1)
break;
const float& deltaXHint = event.data.scroll_begin.delta_x_hint;
const float& deltaYHint = event.data.scroll_begin.delta_y_hint;
if (deltaXHint == 0.0 && deltaYHint == 0.0)
break;
if (!client_->GetRenderWidgetHostViewBase())
break;
absl::optional<cc::TouchAction> allowed_touch_action =
AllowedTouchAction();
// Don't handle for non-writable areas as kInternalNotWritable bit is set.
if (!allowed_touch_action.has_value() ||
(allowed_touch_action.value() &
cc::TouchAction::kInternalNotWritable) ==
cc::TouchAction::kInternalNotWritable)
break;
// Request to start stylus writing as we have detected stylus writing
// movement, and treat scroll gesture as stylus input if started.
if (client_->GetRenderWidgetHostViewBase()->RequestStartStylusWriting()) {
stylus_writing_started_ = true;
// The below call is done to Focus the stylus writable input element.
client_->OnStartStylusWriting();
return true;
}
break;
}
case WebInputEvent::Type::kGestureScrollUpdate:
// TODO(crbug.com/1330817): Pass the queued scroll delta to stylus
// writing recognition system.
return stylus_writing_started_;
case WebInputEvent::Type::kGestureScrollEnd: {
// When stylus writing starts, Touch Move events would be forwarded to
// stylus recognition system and gesture detection would be reset. We
// would receive the GestureScrollEnd here after that.
if (stylus_writing_started_) {
stylus_writing_started_ = false;
return true;
}
break;
}
default:
break;
}
return false;
}
void InputRouterImpl::SendTouchEvent(
const TouchEventWithLatencyInfo& touch_event) {
TouchEventWithLatencyInfo updated_touch_event = touch_event;
SetMovementXYForTouchPoints(&updated_touch_event.event);
input_stream_validator_.Validate(updated_touch_event.event);
touch_event_queue_.QueueEvent(updated_touch_event);
}
void InputRouterImpl::NotifySiteIsMobileOptimized(bool is_mobile_optimized) {
touch_event_queue_.SetIsMobileOptimizedSite(is_mobile_optimized);
}
bool InputRouterImpl::HasPendingEvents() const {
return !touch_event_queue_.Empty() || !gesture_event_queue_.empty() ||
wheel_event_queue_.has_pending() ||
touchpad_pinch_event_queue_.has_pending();
}
void InputRouterImpl::SetDeviceScaleFactor(float device_scale_factor) {
device_scale_factor_ = device_scale_factor;
}
void InputRouterImpl::SetForceEnableZoom(bool enabled) {
touch_action_filter_.SetForceEnableZoom(enabled);
}
absl::optional<cc::TouchAction> InputRouterImpl::AllowedTouchAction() {
return touch_action_filter_.allowed_touch_action();
}
absl::optional<cc::TouchAction> InputRouterImpl::ActiveTouchAction() {
return touch_action_filter_.active_touch_action();
}
mojo::PendingRemote<blink::mojom::WidgetInputHandlerHost>
InputRouterImpl::BindNewHost(
scoped_refptr<base::SequencedTaskRunner> task_runner) {
host_receiver_.reset();
return host_receiver_.BindNewPipeAndPassRemote(task_runner);
}
void InputRouterImpl::StopFling() {
gesture_event_queue_.StopFling();
}
void InputRouterImpl::ProcessDeferredGestureEventQueue() {
TRACE_EVENT0("input", "InputRouterImpl::ProcessDeferredGestureEventQueue");
GestureEventQueue::GestureQueue deferred_gesture_events =
gesture_event_queue_.TakeDeferredEvents();
for (auto& it : deferred_gesture_events) {
FilterGestureEventResult result =
touch_action_filter_.FilterGestureEvent(&(it.event));
SendGestureEventWithoutQueueing(it, result);
}
}
void InputRouterImpl::SetTouchActionFromMain(cc::TouchAction touch_action) {
TRACE_EVENT1("input", "InputRouterImpl::SetTouchActionFromMain",
"touch_action", TouchActionToString(touch_action));
touch_action_filter_.OnSetTouchAction(touch_action);
touch_event_queue_.StopTimeoutMonitor();
ProcessDeferredGestureEventQueue();
UpdateTouchAckTimeoutEnabled();
}
void InputRouterImpl::SetPanAction(blink::mojom::PanAction pan_action) {
if (pan_action_ == pan_action)
return;
pan_action_ = pan_action;
// TODO(mahesh.ma): Update PanAction state to view, once RenderWidgetHostView
// is set again.
if (!client_->GetRenderWidgetHostViewBase())
return;
client_->GetRenderWidgetHostViewBase()->SetHoverActionStylusWritable(
pan_action_ == blink::mojom::PanAction::kStylusWritable);
}
void InputRouterImpl::OnSetCompositorAllowedTouchAction(
cc::TouchAction touch_action) {
TRACE_EVENT1("input", "InputRouterImpl::OnSetCompositorAllowedTouchAction",
"action", cc::TouchActionToString(touch_action));
touch_action_filter_.OnSetCompositorAllowedTouchAction(touch_action);
client_->OnSetCompositorAllowedTouchAction(touch_action);
if (touch_action == cc::TouchAction::kAuto)
FlushDeferredGestureQueue();
UpdateTouchAckTimeoutEnabled();
}
void InputRouterImpl::DidOverscroll(
blink::mojom::DidOverscrollParamsPtr params) {
// Touchpad and Touchscreen flings are handled on the browser side.
ui::DidOverscrollParams fling_updated_params = {
params->accumulated_overscroll, params->latest_overscroll_delta,
params->current_fling_velocity, params->causal_event_viewport_point,
params->overscroll_behavior};
fling_updated_params.current_fling_velocity =
gesture_event_queue_.CurrentFlingVelocity();
client_->DidOverscroll(fling_updated_params);
}
void InputRouterImpl::DidStartScrollingViewport() {
client_->DidStartScrollingViewport();
}
void InputRouterImpl::ImeCancelComposition() {
client_->OnImeCancelComposition();
}
void InputRouterImpl::ImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& bounds) {
client_->OnImeCompositionRangeChanged(range, bounds);
}
void InputRouterImpl::SetMouseCapture(bool capture) {
client_->SetMouseCapture(capture);
}
void InputRouterImpl::RequestMouseLock(bool from_user_gesture,
bool unadjusted_movement,
RequestMouseLockCallback response) {
client_->RequestMouseLock(from_user_gesture, unadjusted_movement,
std::move(response));
}
void InputRouterImpl::SetMovementXYForTouchPoints(blink::WebTouchEvent* event) {
for (size_t i = 0; i < event->touches_length; ++i) {
blink::WebTouchPoint* touch_point = &event->touches[i];
if (touch_point->state == blink::WebTouchPoint::State::kStateMoved) {
const gfx::Point& last_position = global_touch_position_[touch_point->id];
touch_point->movement_x =
touch_point->PositionInScreen().x() - last_position.x();
touch_point->movement_y =
touch_point->PositionInScreen().y() - last_position.y();
global_touch_position_[touch_point->id].SetPoint(
touch_point->PositionInScreen().x(),
touch_point->PositionInScreen().y());
} else {
touch_point->movement_x = 0;
touch_point->movement_y = 0;
if (touch_point->state == blink::WebTouchPoint::State::kStateReleased ||
touch_point->state == blink::WebTouchPoint::State::kStateCancelled) {
global_touch_position_.erase(touch_point->id);
} else if (touch_point->state ==
blink::WebTouchPoint::State::kStatePressed) {
DCHECK(global_touch_position_.find(touch_point->id) ==
global_touch_position_.end());
global_touch_position_[touch_point->id] =
gfx::Point(touch_point->PositionInScreen().x(),
touch_point->PositionInScreen().y());
}
}
}
}
// Forwards MouseEvent without passing it through
// TouchpadTapSuppressionController.
void InputRouterImpl::SendMouseEventImmediately(
const MouseEventWithLatencyInfo& mouse_event,
MouseEventCallback event_result_callback) {
blink::mojom::WidgetInputHandler::DispatchEventCallback callback =
base::BindOnce(&InputRouterImpl::MouseEventHandled, weak_this_,
mouse_event, std::move(event_result_callback));
FilterAndSendWebInputEvent(mouse_event.event, mouse_event.latency,
std::move(callback));
}
void InputRouterImpl::SendTouchEventImmediately(
const TouchEventWithLatencyInfo& touch_event) {
blink::mojom::WidgetInputHandler::DispatchEventCallback callback =
base::BindOnce(&InputRouterImpl::TouchEventHandled, weak_this_,
touch_event);
FilterAndSendWebInputEvent(touch_event.event, touch_event.latency,
std::move(callback));
}
void InputRouterImpl::FlushDeferredGestureQueue() {
touch_action_filter_.OnSetTouchAction(cc::TouchAction::kAuto);
ProcessDeferredGestureEventQueue();
}
void InputRouterImpl::OnTouchEventAck(
const TouchEventWithLatencyInfo& event,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result) {
if (WebTouchEventTraits::IsTouchSequenceStart(event.event)) {
touch_action_filter_.AppendToGestureSequenceForDebugging("T");
touch_action_filter_.AppendToGestureSequenceForDebugging(
base::NumberToString(static_cast<uint32_t>(ack_result)).c_str());
touch_action_filter_.AppendToGestureSequenceForDebugging(
base::NumberToString(event.event.unique_touch_event_id).c_str());
touch_action_filter_.IncreaseActiveTouches();
}
disposition_handler_->OnTouchEventAck(event, ack_source, ack_result);
if (WebTouchEventTraits::IsTouchSequenceEnd(event.event)) {
touch_action_filter_.AppendToGestureSequenceForDebugging("E");
touch_action_filter_.AppendToGestureSequenceForDebugging(
base::NumberToString(event.event.unique_touch_event_id).c_str());
touch_action_filter_.DecreaseActiveTouches();
touch_action_filter_.ReportAndResetTouchAction();
UpdateTouchAckTimeoutEnabled();
}
}
void InputRouterImpl::OnFilteringTouchEvent(const WebTouchEvent& touch_event) {
// The event stream given to the renderer is not guaranteed to be
// valid based on the current TouchEventStreamValidator rules. This event will
// never be given to the renderer, but in order to ensure that the event
// stream |output_stream_validator_| sees is valid, we give events which are
// filtered out to the validator. crbug.com/589111 proposes adding an
// additional validator for the events which are actually sent to the
// renderer.
output_stream_validator_.Validate(touch_event);
}
void InputRouterImpl::SendGestureEventImmediately(
const GestureEventWithLatencyInfo& gesture_event) {
blink::mojom::WidgetInputHandler::DispatchEventCallback callback =
base::BindOnce(&InputRouterImpl::GestureEventHandled, weak_this_,
gesture_event);
FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency,
std::move(callback));
}
void InputRouterImpl::OnGestureEventAck(
const GestureEventWithLatencyInfo& event,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result,
blink::mojom::ScrollResultDataPtr scroll_result_data) {
touch_event_queue_.OnGestureEventAck(event, ack_result);
disposition_handler_->OnGestureEventAck(event, ack_source, ack_result,
std::move(scroll_result_data));
}
void InputRouterImpl::SendGeneratedWheelEvent(
const MouseWheelEventWithLatencyInfo& wheel_event) {
client_->ForwardWheelEventWithLatencyInfo(wheel_event.event,
wheel_event.latency);
}
void InputRouterImpl::SendGeneratedGestureScrollEvents(
const GestureEventWithLatencyInfo& gesture_event) {
client_->ForwardGestureEventWithLatencyInfo(gesture_event.event,
gesture_event.latency);
}
gfx::Size InputRouterImpl::GetRootWidgetViewportSize() {
return client_->GetRootWidgetViewportSize();
}
void InputRouterImpl::SendMouseWheelEventImmediately(
const MouseWheelEventWithLatencyInfo& wheel_event,
MouseWheelEventQueueClient::MouseWheelEventHandledCallback
callee_callback) {
blink::mojom::WidgetInputHandler::DispatchEventCallback callback =
base::BindOnce(&InputRouterImpl::MouseWheelEventHandled, weak_this_,
wheel_event, std::move(callee_callback));
FilterAndSendWebInputEvent(wheel_event.event, wheel_event.latency,
std::move(callback));
}
void InputRouterImpl::OnMouseWheelEventAck(
const MouseWheelEventWithLatencyInfo& event,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result) {
disposition_handler_->OnWheelEventAck(event, ack_source, ack_result);
gesture_event_queue_.OnWheelEventAck(event, ack_source, ack_result);
}
void InputRouterImpl::ForwardGestureEventWithLatencyInfo(
const blink::WebGestureEvent& event,
const ui::LatencyInfo& latency_info) {
client_->ForwardGestureEventWithLatencyInfo(event, latency_info);
}
void InputRouterImpl::SendMouseWheelEventForPinchImmediately(
const MouseWheelEventWithLatencyInfo& event,
TouchpadPinchEventQueueClient::MouseWheelEventHandledCallback callback) {
SendMouseWheelEventImmediately(event, std::move(callback));
}
void InputRouterImpl::OnGestureEventForPinchAck(
const GestureEventWithLatencyInfo& event,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result) {
OnGestureEventAck(event, ack_source, ack_result,
/*scroll_result_data=*/nullptr);
}
bool InputRouterImpl::IsWheelScrollInProgress() {
return client_->IsWheelScrollInProgress();
}
bool InputRouterImpl::IsAutoscrollInProgress() {
return client_->IsAutoscrollInProgress();
}
void InputRouterImpl::FilterAndSendWebInputEvent(
const WebInputEvent& input_event,
const ui::LatencyInfo& latency_info,
blink::mojom::WidgetInputHandler::DispatchEventCallback callback) {
TRACE_EVENT1("input", "InputRouterImpl::FilterAndSendWebInputEvent", "type",
WebInputEvent::GetName(input_event.GetType()));
TRACE_EVENT("input,benchmark,devtools.timeline", "LatencyInfo.Flow",
[&latency_info](perfetto::EventContext ctx) {
ChromeLatencyInfo* info =
ctx.event()->set_chrome_latency_info();
info->set_trace_id(latency_info.trace_id());
info->set_step(ChromeLatencyInfo::STEP_SEND_INPUT_EVENT_UI);
tracing::FillFlowEvent(ctx,
perfetto::protos::pbzero::TrackEvent::
LegacyEvent::FLOW_INOUT,
latency_info.trace_id());
});
output_stream_validator_.Validate(input_event);
blink::mojom::InputEventResultState filtered_state =
client_->FilterInputEvent(input_event, latency_info);
if (WasHandled(filtered_state)) {
TRACE_EVENT_INSTANT0("input", "InputEventFiltered",
TRACE_EVENT_SCOPE_THREAD);
if (filtered_state != blink::mojom::InputEventResultState::kUnknown) {
std::move(callback).Run(blink::mojom::InputEventResultSource::kBrowser,
latency_info, filtered_state, nullptr, nullptr,
/*scroll_result_data=*/nullptr);
}
return;
}
std::unique_ptr<blink::WebCoalescedInputEvent> event =
ScaleEvent(input_event, device_scale_factor_, latency_info);
if (WebInputEventTraits::ShouldBlockEventStream(input_event)) {
TRACE_EVENT_INSTANT0("input", "InputEventSentBlocking",
TRACE_EVENT_SCOPE_THREAD);
client_->IncrementInFlightEventCount();
blink::mojom::WidgetInputHandler::DispatchEventCallback renderer_callback =
base::BindOnce(
[](blink::mojom::WidgetInputHandler::DispatchEventCallback callback,
base::WeakPtr<InputRouterImpl> input_router,
blink::mojom::InputEventResultSource source,
const ui::LatencyInfo& latency,
blink::mojom::InputEventResultState state,
blink::mojom::DidOverscrollParamsPtr overscroll,
blink::mojom::TouchActionOptionalPtr touch_action,
blink::mojom::ScrollResultDataPtr scroll_result_data) {
// Filter source to ensure only valid values are sent from the
// renderer.
if (source == blink::mojom::InputEventResultSource::kBrowser) {
if (input_router)
input_router->client_->OnInvalidInputEventSource();
return;
}
std::move(callback).Run(
source, latency, state, std::move(overscroll),
std::move(touch_action), std::move(scroll_result_data));
},
std::move(callback), weak_this_);
client_->GetWidgetInputHandler()->DispatchEvent(
std::move(event), std::move(renderer_callback));
} else {
TRACE_EVENT_INSTANT0("input", "InputEventSentNonBlocking",
TRACE_EVENT_SCOPE_THREAD);
client_->GetWidgetInputHandler()->DispatchNonBlockingEvent(
std::move(event));
std::move(callback).Run(blink::mojom::InputEventResultSource::kBrowser,
latency_info,
blink::mojom::InputEventResultState::kIgnored,
nullptr, nullptr, /*scroll_result_data=*/nullptr);
}
}
void InputRouterImpl::KeyboardEventHandled(
const NativeWebKeyboardEventWithLatencyInfo& event,
KeyboardEventCallback event_result_callback,
blink::mojom::InputEventResultSource source,
const ui::LatencyInfo& latency,
blink::mojom::InputEventResultState state,
blink::mojom::DidOverscrollParamsPtr overscroll,
blink::mojom::TouchActionOptionalPtr touch_action,
blink::mojom::ScrollResultDataPtr scroll_result_data) {
TRACE_EVENT2("input", "InputRouterImpl::KeyboardEventHandled", "type",
WebInputEvent::GetName(event.event.GetType()), "ack",
InputEventResultStateToString(state));
if (source != blink::mojom::InputEventResultSource::kBrowser)
client_->DecrementInFlightEventCount(source);
event.latency.AddNewLatencyFrom(latency);
std::move(event_result_callback).Run(event, source, state);
// WARNING: This InputRouterImpl can be deallocated at this point
// (i.e. in the case of Ctrl+W, where the call to
// HandleKeyboardEvent destroys this InputRouterImpl).
// TODO(jdduke): crbug.com/274029 - Make ack-triggered shutdown async.
}
void InputRouterImpl::MouseEventHandled(
const MouseEventWithLatencyInfo& event,
MouseEventCallback event_result_callback,
blink::mojom::InputEventResultSource source,
const ui::LatencyInfo& latency,
blink::mojom::InputEventResultState state,
blink::mojom::DidOverscrollParamsPtr overscroll,
blink::mojom::TouchActionOptionalPtr touch_action,
blink::mojom::ScrollResultDataPtr scroll_result_data) {
TRACE_EVENT2("input", "InputRouterImpl::MouseEventHandled", "type",
WebInputEvent::GetName(event.event.GetType()), "ack",
InputEventResultStateToString(state));
if (source != blink::mojom::InputEventResultSource::kBrowser)
client_->DecrementInFlightEventCount(source);
event.latency.AddNewLatencyFrom(latency);
std::move(event_result_callback).Run(event, source, state);
}
void InputRouterImpl::TouchEventHandled(
const TouchEventWithLatencyInfo& touch_event,
blink::mojom::InputEventResultSource source,
const ui::LatencyInfo& latency,
blink::mojom::InputEventResultState state,
blink::mojom::DidOverscrollParamsPtr overscroll,
blink::mojom::TouchActionOptionalPtr touch_action,
blink::mojom::ScrollResultDataPtr scroll_result_data) {
TRACE_EVENT2("input", "InputRouterImpl::TouchEventHandled", "type",
WebInputEvent::GetName(touch_event.event.GetType()), "ack",
InputEventResultStateToString(state));
if (source != blink::mojom::InputEventResultSource::kBrowser)
client_->DecrementInFlightEventCount(source);
touch_event.latency.AddNewLatencyFrom(latency);
// The SetTouchAction IPC occurs on a different channel so always
// send it in the input event ack to ensure it is available at the
// time the ACK is handled.
if (touch_action) {
// For main thread ACKs, Blink will directly call SetTouchActionFromMain.
if (source == blink::mojom::InputEventResultSource::kCompositorThread)
OnSetCompositorAllowedTouchAction(touch_action->touch_action);
}
// TODO(crbug.com/953547): find a proper way to stop the timeout monitor.
bool should_stop_timeout_monitor = true;
// |touch_event_queue_| will forward to OnTouchEventAck when appropriate.
touch_event_queue_.ProcessTouchAck(source, state, latency,
touch_event.event.unique_touch_event_id,
should_stop_timeout_monitor);
}
void InputRouterImpl::GestureEventHandled(
const GestureEventWithLatencyInfo& gesture_event,
blink::mojom::InputEventResultSource source,
const ui::LatencyInfo& latency,
blink::mojom::InputEventResultState state,
blink::mojom::DidOverscrollParamsPtr overscroll,
blink::mojom::TouchActionOptionalPtr touch_action,
blink::mojom::ScrollResultDataPtr scroll_result_data) {
TRACE_EVENT2("input", "InputRouterImpl::GestureEventHandled", "type",
WebInputEvent::GetName(gesture_event.event.GetType()), "ack",
InputEventResultStateToString(state));
if (source != blink::mojom::InputEventResultSource::kBrowser)
client_->DecrementInFlightEventCount(source);
if (overscroll) {
DCHECK_EQ(WebInputEvent::Type::kGestureScrollUpdate,
gesture_event.event.GetType());
DidOverscroll(std::move(overscroll));
}
// |gesture_event_queue_| will forward to OnGestureEventAck when appropriate.
gesture_event_queue_.ProcessGestureAck(source, state,
gesture_event.event.GetType(), latency,
std::move(scroll_result_data));
}
void InputRouterImpl::MouseWheelEventHandled(
const MouseWheelEventWithLatencyInfo& event,
MouseWheelEventQueueClient::MouseWheelEventHandledCallback callback,
blink::mojom::InputEventResultSource source,
const ui::LatencyInfo& latency,
blink::mojom::InputEventResultState state,
blink::mojom::DidOverscrollParamsPtr overscroll,
blink::mojom::TouchActionOptionalPtr touch_action,
blink::mojom::ScrollResultDataPtr scroll_result_data) {
TRACE_EVENT2("input", "InputRouterImpl::MouseWheelEventHandled", "type",
WebInputEvent::GetName(event.event.GetType()), "ack",
InputEventResultStateToString(state));
if (source != blink::mojom::InputEventResultSource::kBrowser)
client_->DecrementInFlightEventCount(source);
event.latency.AddNewLatencyFrom(latency);
if (overscroll)
DidOverscroll(std::move(overscroll));
std::move(callback).Run(event, source, state);
}
void InputRouterImpl::OnHasTouchEventConsumers(
blink::mojom::TouchEventConsumersPtr consumers) {
TRACE_EVENT1("input", "InputRouterImpl::OnHasTouchEventHandlers",
"has_handlers", consumers->has_touch_event_handlers);
touch_action_filter_.OnHasTouchEventHandlers(
consumers->has_touch_event_handlers);
touch_event_queue_.OnHasTouchEventHandlers(
consumers->has_touch_event_handlers ||
consumers->has_hit_testable_scrollbar);
}
void InputRouterImpl::WaitForInputProcessed(base::OnceClosure callback) {
// TODO(bokan): Some kinds of input is queued in one of the various queues
// available in this class. To be truly robust, we should wait until those
// queues are flushed before issuing this message. This will be done in a
// follow-up. https://crbug.com/902446.
client_->GetWidgetInputHandler()->WaitForInputProcessed(std::move(callback));
}
void InputRouterImpl::FlushTouchEventQueue() {
touch_event_queue_.FlushQueue();
}
void InputRouterImpl::ForceSetTouchActionAuto() {
touch_action_filter_.AppendToGestureSequenceForDebugging("F");
touch_action_filter_.OnSetTouchAction(cc::TouchAction::kAuto);
// TODO(xidachen): Call FlushDeferredGestureQueue when this flag is enabled.
touch_event_queue_.StopTimeoutMonitor();
ProcessDeferredGestureEventQueue();
}
void InputRouterImpl::ForceResetTouchActionForTest() {
touch_action_filter_.ForceResetTouchActionForTest();
}
bool InputRouterImpl::IsFlingActiveForTest() {
return gesture_event_queue_.IsFlingActiveForTest();
}
void InputRouterImpl::UpdateTouchAckTimeoutEnabled() {
// TouchAction::kNone will prevent scrolling, in which case the timeout serves
// little purpose. It's also a strong signal that touch handling is critical
// to page functionality, so the timeout could do more harm than good.
absl::optional<cc::TouchAction> allowed_touch_action =
touch_action_filter_.allowed_touch_action();
cc::TouchAction compositor_allowed_touch_action =
touch_action_filter_.compositor_allowed_touch_action();
const bool touch_ack_timeout_disabled =
(allowed_touch_action.has_value() &&
allowed_touch_action.value() == cc::TouchAction::kNone) ||
(compositor_allowed_touch_action == cc::TouchAction::kNone);
touch_event_queue_.SetAckTimeoutEnabled(!touch_ack_timeout_disabled);
}
} // namespace content