blob: 5544fd5553695a1253f2e97132e390ff79d9bc3a [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/browser/renderer_host/render_widget_targeter.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "third_party/WebKit/public/platform/WebInputEvent.h"
#include "ui/events/blink/blink_event_util.h"
namespace content {
namespace {
bool MergeEventIfPossible(const blink::WebInputEvent& event,
ui::WebScopedInputEvent* blink_event) {
if (ui::CanCoalesce(event, **blink_event)) {
*blink_event = ui::WebInputEventTraits::Clone(event);
return true;
}
return false;
}
} // namespace
RenderWidgetTargetResult::RenderWidgetTargetResult() = default;
RenderWidgetTargetResult::RenderWidgetTargetResult(
const RenderWidgetTargetResult&) = default;
RenderWidgetTargetResult::RenderWidgetTargetResult(
RenderWidgetHostViewBase* in_view,
bool in_should_query_view,
base::Optional<gfx::PointF> in_location)
: view(in_view),
should_query_view(in_should_query_view),
target_location(in_location) {}
RenderWidgetTargetResult::~RenderWidgetTargetResult() = default;
RenderWidgetTargeter::TargetingRequest::TargetingRequest() = default;
RenderWidgetTargeter::TargetingRequest::TargetingRequest(
TargetingRequest&& request) = default;
RenderWidgetTargeter::TargetingRequest& RenderWidgetTargeter::TargetingRequest::
operator=(TargetingRequest&&) = default;
RenderWidgetTargeter::TargetingRequest::~TargetingRequest() = default;
RenderWidgetTargeter::RenderWidgetTargeter(Delegate* delegate)
: delegate_(delegate), weak_ptr_factory_(this) {
DCHECK(delegate_);
}
RenderWidgetTargeter::~RenderWidgetTargeter() = default;
void RenderWidgetTargeter::FindTargetAndDispatch(
RenderWidgetHostViewBase* root_view,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency) {
if (request_in_flight_) {
if (!requests_.empty()) {
auto& request = requests_.back();
if (MergeEventIfPossible(event, &request.event))
return;
}
TargetingRequest request;
request.root_view = root_view->GetWeakPtr();
request.event = ui::WebInputEventTraits::Clone(event);
request.latency = latency;
requests_.push(std::move(request));
return;
}
RenderWidgetTargetResult result =
delegate_->FindTargetSynchronously(root_view, event);
RenderWidgetHostViewBase* target = result.view;
auto* event_ptr = &event;
if (result.should_query_view) {
QueryClient(root_view, target, *event_ptr, latency, result.target_location);
} else {
FoundTarget(root_view, target, *event_ptr, latency, result.target_location);
}
}
void RenderWidgetTargeter::QueryClient(
RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const base::Optional<gfx::PointF>& target_location) {
DCHECK(!request_in_flight_);
DCHECK(target_location.has_value());
request_in_flight_ = true;
auto* target_client =
target->GetRenderWidgetHostImpl()->input_target_client();
// TODO: Unify the codepaths by converting to ui::WebScopedInputEvent here (or
// earlier).
if (blink::WebInputEvent::IsMouseEventType(event.GetType())) {
target_client->FrameSinkIdAt(
gfx::ToCeiledPoint(target_location.value()),
base::BindOnce(&RenderWidgetTargeter::FoundFrameSinkId,
weak_ptr_factory_.GetWeakPtr(), root_view->GetWeakPtr(),
target->GetWeakPtr(),
static_cast<const blink::WebMouseEvent&>(event), latency,
target_location));
} else if (event.GetType() == blink::WebInputEvent::kMouseWheel) {
target_client->FrameSinkIdAt(
gfx::ToCeiledPoint(target_location.value()),
base::BindOnce(&RenderWidgetTargeter::FoundFrameSinkId,
weak_ptr_factory_.GetWeakPtr(), root_view->GetWeakPtr(),
target->GetWeakPtr(),
static_cast<const blink::WebMouseWheelEvent&>(event),
latency, target_location));
} else {
// TODO(crbug.com/796656): Handle other types of events.
NOTREACHED();
}
}
void RenderWidgetTargeter::FlushEventQueue() {
while (!request_in_flight_ && !requests_.empty()) {
auto request = std::move(requests_.front());
requests_.pop();
// The root-view has gone away. Ignore this event, and try to process the
// next event.
if (!request.root_view) {
continue;
}
FindTargetAndDispatch(request.root_view.get(), *request.event,
request.latency);
}
}
void RenderWidgetTargeter::FoundFrameSinkId(
base::WeakPtr<RenderWidgetHostViewBase> root_view,
base::WeakPtr<RenderWidgetHostViewBase> target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const base::Optional<gfx::PointF>& target_location,
const viz::FrameSinkId& frame_sink_id) {
request_in_flight_ = false;
auto* view = delegate_->FindViewFromFrameSinkId(frame_sink_id);
if (!view)
view = target.get();
// If a client was asked to find a target, then it is necessary to keep
// asking the clients until a client claims an event for itself.
if (view == target.get()) {
FoundTarget(root_view.get(), view, event, latency, target_location);
} else {
base::Optional<gfx::PointF> location = target_location;
if (target_location) {
gfx::PointF updated_location = *target_location;
if (target->TransformPointToCoordSpaceForView(updated_location, view,
&updated_location)) {
location.emplace(updated_location);
}
}
QueryClient(root_view.get(), view, event, latency, location);
}
}
void RenderWidgetTargeter::FoundTarget(
RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const base::Optional<gfx::PointF>& target_location) {
if (!root_view)
return;
// TODO: Unify position conversion for all event types.
if (blink::WebInputEvent::IsMouseEventType(event.GetType())) {
blink::WebMouseEvent mouse_event =
static_cast<const blink::WebMouseEvent&>(event);
if (target_location.has_value()) {
mouse_event.SetPositionInWidget(target_location->x(),
target_location->y());
}
if (mouse_event.GetType() != blink::WebInputEvent::kUndefined)
delegate_->DispatchEventToTarget(root_view, target, mouse_event, latency);
} else if (event.GetType() == blink::WebInputEvent::kMouseWheel) {
delegate_->DispatchEventToTarget(
root_view, target, static_cast<const blink::WebMouseWheelEvent&>(event),
latency);
} else {
// TODO(crbug.com/796656): Handle other types of events.
NOTREACHED();
return;
}
FlushEventQueue();
}
} // namespace content