blob: c794db9f6c29a97b7332586180e263ef063a4c36 [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.
#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_TARGETER_H_
#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_TARGETER_H_
#include <queue>
#include <unordered_set>
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "content/common/content_constants_internal.h"
#include "content/common/content_export.h"
#include "ui/events/blink/web_input_event_traits.h"
#include "ui/latency/latency_info.h"
namespace blink {
class WebInputEvent;
} // namespace blink
namespace gfx {
class PointF;
}
namespace content {
class RenderWidgetHostViewBase;
class OneShotTimeoutMonitor;
// TODO(sunxd): Make |RenderWidgetTargetResult| a class. Merge the booleans into
// a mask to reduce the size. Make the constructor take in enums for better
// readability.
struct CONTENT_EXPORT RenderWidgetTargetResult {
RenderWidgetTargetResult();
RenderWidgetTargetResult(const RenderWidgetTargetResult&);
RenderWidgetTargetResult(RenderWidgetHostViewBase* view,
bool should_query_view,
base::Optional<gfx::PointF> location,
bool latched_target,
bool should_verify_result);
~RenderWidgetTargetResult();
RenderWidgetHostViewBase* view = nullptr;
bool should_query_view = false;
base::Optional<gfx::PointF> target_location = base::nullopt;
// When |latched_target| is false, we explicitly hit-tested events instead of
// using a known target.
bool latched_target = false;
// When |should_verify_result| is true, RenderWidgetTargeter will do async hit
// testing and compare the target with the result of synchronous hit testing.
// |should_verify_result| will always be false if we are doing draw quad based
// hit testing.
bool should_verify_result = false;
};
class TracingUmaTracker;
class RenderWidgetTargeter {
public:
class Delegate {
public:
virtual ~Delegate() {}
virtual RenderWidgetTargetResult FindTargetSynchronously(
RenderWidgetHostViewBase* root_view,
const blink::WebInputEvent& event) = 0;
// |event| is in |root_view|'s coordinate space.
virtual void DispatchEventToTarget(
RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const base::Optional<gfx::PointF>& target_location) = 0;
virtual void SetEventsBeingFlushed(bool events_being_flushed) = 0;
virtual RenderWidgetHostViewBase* FindViewFromFrameSinkId(
const viz::FrameSinkId& frame_sink_id) const = 0;
// Returns true if a further asynchronous query should be sent to the
// candidate RenderWidgetHostView.
virtual bool ShouldContinueHitTesting(
RenderWidgetHostViewBase* target_view) const = 0;
};
// The delegate must outlive this targeter.
explicit RenderWidgetTargeter(Delegate* delegate);
~RenderWidgetTargeter();
// Finds the appropriate target inside |root_view| for |event|, and dispatches
// it through the delegate. |event| is in the coord-space of |root_view|.
void FindTargetAndDispatch(RenderWidgetHostViewBase* root_view,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency);
void ViewWillBeDestroyed(RenderWidgetHostViewBase* view);
void set_async_hit_test_timeout_delay_for_testing(
const base::TimeDelta& delay) {
async_hit_test_timeout_delay_ = delay;
}
size_t num_requests_in_queue_for_testing() { return requests_.size(); }
bool is_request_in_flight_for_testing() { return request_in_flight_; }
private:
// Attempts to target and dispatch all events in the queue. It stops if it has
// to query a client, or if the queue becomes empty.
void FlushEventQueue(bool is_verifying);
// Queries |target| to find the correct target for |event|.
// |event| is in the coordinate space of |root_view|.
// |target_location|, if set, is the location in |target|'s coordinate space.
// |last_request_target| and |last_target_location| provide a fallback target
// the case that the query times out. These should be null values when
// querying the root view, and the target's immediate parent view otherwise.
// |expected_frame_sink_id| is temporarily added for v2 viz hit testing.
// V2 uses cc generated hit test data and we need to verify its correctness.
// The variable is the target frame sink id v2 finds in synchronous hit
// testing. It should be the same as the async hit testing target if v2 works
// correctly.
// TODO(sunxd): Remove |expected_frame_sink_id| after verifying synchronous
// hit testing correctness. See https://crbug.com/871996.
void QueryClientInternal(RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const gfx::PointF& target_location,
RenderWidgetHostViewBase* last_request_target,
const gfx::PointF& last_target_location,
const viz::FrameSinkId& expected_frame_sink_id);
void QueryClient(RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const gfx::PointF& target_location,
RenderWidgetHostViewBase* last_request_target,
const gfx::PointF& last_target_location);
void QueryAndVerifyClient(RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const gfx::PointF& target_location,
RenderWidgetHostViewBase* last_request_target,
const gfx::PointF& last_target_location,
const viz::FrameSinkId& expected_frame_sink_id);
// |event| is in the coordinate space of |root_view|. |target_location|, if
// set, is the location in |target|'s coordinate space.
// |target| is the current target that will be queried using its
// InputTargetClient interface.
// |frame_sink_id| is returned from the InputTargetClient to indicate where
// the event should be routed, and |transformed_location| is the point in
// that new target's coordinate space.
// |expected_frame_sink_id| is the expected hit test result based on
// synchronous event targeting with cc generated data.
void FoundFrameSinkId(base::WeakPtr<RenderWidgetHostViewBase> root_view,
base::WeakPtr<RenderWidgetHostViewBase> target,
ui::WebScopedInputEvent event,
const ui::LatencyInfo& latency,
uint32_t request_id,
const gfx::PointF& target_location,
TracingUmaTracker tracker,
const viz::FrameSinkId& expected_frame_sink_id,
const viz::FrameSinkId& frame_sink_id,
const gfx::PointF& transformed_location);
// |event| is in the coordinate space of |root_view|. |target_location|, if
// set, is the location in |target|'s coordinate space. If |latched_target| is
// false, we explicitly did hit-testing for this event, instead of using a
// known target.
void FoundTarget(RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const base::Optional<gfx::PointF>& target_location,
bool latched_target,
const viz::FrameSinkId& expected_frame_sink_id);
// Callback when the hit testing timer fires, to resume event processing
// without further waiting for a response to the last targeting request.
void AsyncHitTestTimedOut(
base::WeakPtr<RenderWidgetHostViewBase> current_request_root_view,
base::WeakPtr<RenderWidgetHostViewBase> current_request_target,
const gfx::PointF& current_target_location,
base::WeakPtr<RenderWidgetHostViewBase> last_request_target,
const gfx::PointF& last_target_location,
ui::WebScopedInputEvent event,
const ui::LatencyInfo& latency,
const viz::FrameSinkId& expected_frame_sink_id);
base::TimeDelta async_hit_test_timeout_delay() {
return async_hit_test_timeout_delay_;
}
struct TargetingRequest {
TargetingRequest();
TargetingRequest(TargetingRequest&& request);
TargetingRequest& operator=(TargetingRequest&& other);
~TargetingRequest();
base::WeakPtr<RenderWidgetHostViewBase> root_view;
ui::WebScopedInputEvent event;
ui::LatencyInfo latency;
viz::FrameSinkId expected_frame_sink_id;
std::unique_ptr<TracingUmaTracker> tracker;
};
bool request_in_flight_ = false;
uint32_t last_request_id_ = 0;
std::queue<TargetingRequest> requests_;
// With viz-hit-testing-surface-layer being enabled, we do async hit testing
// for already dispatched events for verification. These verification requests
// should not block normal hit testing requests.
bool verify_request_in_flight_ = false;
uint32_t last_verify_request_id_ = 0;
std::queue<TargetingRequest> verify_requests_;
std::unordered_set<RenderWidgetHostViewBase*> unresponsive_views_;
// This value keeps track of the number of clients we have asked in order to
// do async hit-testing.
uint32_t async_depth_ = 0;
// This value limits how long to wait for a response from the renderer
// process before giving up and resuming event processing.
base::TimeDelta async_hit_test_timeout_delay_ =
base::TimeDelta::FromMilliseconds(kAsyncHitTestTimeoutMs);
std::unique_ptr<OneShotTimeoutMonitor> async_hit_test_timeout_;
std::unique_ptr<OneShotTimeoutMonitor> async_verify_hit_test_timeout_;
uint64_t trace_id_;
const bool is_viz_hit_testing_debug_enabled_;
// Stores SurfaceIds for regions that were async queried if hit-test debugging
// is enabled. This allows us to send the queried regions in batches.
std::vector<viz::FrameSinkId> hit_test_async_queried_debug_queue_;
Delegate* const delegate_;
base::WeakPtrFactory<RenderWidgetTargeter> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(RenderWidgetTargeter);
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_TARGETER_H_