| // 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 "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "components/viz/common/surfaces/frame_sink_id.h" |
| #include "content/browser/renderer_host/render_widget_host_view_base.h" |
| #include "content/common/content_export.h" |
| #include "third_party/abseil-cpp/absl/types/optional.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; |
| |
| // 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, |
| absl::optional<gfx::PointF> location, |
| bool latched_target); |
| ~RenderWidgetTargetResult(); |
| |
| raw_ptr<RenderWidgetHostViewBase> view = nullptr; |
| bool should_query_view = false; |
| absl::optional<gfx::PointF> target_location = absl::nullopt; |
| // When |latched_target| is false, we explicitly hit-tested events instead of |
| // using a known target. |
| bool latched_target = false; |
| }; |
| |
| class TracingUmaTracker; |
| |
| class RenderWidgetTargeter { |
| public: |
| using RenderWidgetHostAtPointCallback = |
| base::OnceCallback<void(base::WeakPtr<RenderWidgetHostViewBase>, |
| absl::optional<gfx::PointF>)>; |
| |
| class Delegate { |
| public: |
| virtual ~Delegate() {} |
| |
| virtual RenderWidgetTargetResult FindTargetSynchronouslyAtPoint( |
| RenderWidgetHostViewBase* root_view, |
| const gfx::PointF& location) = 0; |
| |
| virtual RenderWidgetTargetResult FindTargetSynchronously( |
| RenderWidgetHostViewBase* root_view, |
| const blink::WebInputEvent& event) = 0; |
| |
| // |event| must be non-null, and is in |root_view|'s coordinate space. |
| virtual void DispatchEventToTarget( |
| RenderWidgetHostViewBase* root_view, |
| RenderWidgetHostViewBase* target, |
| blink::WebInputEvent* event, |
| const ui::LatencyInfo& latency, |
| const absl::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; |
| }; |
| |
| enum class HitTestResultsMatch { |
| kDoNotMatch = 0, |
| kMatch = 1, |
| kHitTestResultChanged = 2, |
| kHitTestDataOutdated = 3, |
| kMaxValue = kHitTestDataOutdated, |
| }; |
| |
| // The delegate must outlive this targeter. |
| explicit RenderWidgetTargeter(Delegate* delegate); |
| |
| RenderWidgetTargeter(const RenderWidgetTargeter&) = delete; |
| RenderWidgetTargeter& operator=(const RenderWidgetTargeter&) = delete; |
| |
| ~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); |
| |
| // Finds the appropriate target inside |root_view| for |point|, and passes the |
| // target along with the transformed coordinates of the point with respect to |
| // the target's coordinates. |
| void FindTargetAndCallback(RenderWidgetHostViewBase* root_view, |
| const gfx::PointF& point, |
| RenderWidgetHostAtPointCallback callback); |
| |
| void ViewWillBeDestroyed(RenderWidgetHostViewBase* view); |
| |
| bool HasEventsPendingDispatch() const; |
| |
| 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_.has_value(); |
| } |
| |
| void SetIsAutoScrollInProgress(bool autoscroll_in_progress); |
| |
| bool is_auto_scroll_in_progress() const { return is_autoscroll_in_progress_; } |
| |
| private: |
| class TargetingRequest { |
| public: |
| TargetingRequest(base::WeakPtr<RenderWidgetHostViewBase>, |
| const blink::WebInputEvent&, |
| const ui::LatencyInfo&); |
| TargetingRequest(base::WeakPtr<RenderWidgetHostViewBase>, |
| const gfx::PointF&, |
| RenderWidgetHostAtPointCallback); |
| TargetingRequest(TargetingRequest&& request); |
| TargetingRequest& operator=(TargetingRequest&& other); |
| |
| TargetingRequest(const TargetingRequest&) = delete; |
| TargetingRequest& operator=(const TargetingRequest&) = delete; |
| |
| ~TargetingRequest(); |
| |
| void RunCallback(RenderWidgetHostViewBase* target, |
| absl::optional<gfx::PointF> point); |
| |
| bool MergeEventIfPossible(const blink::WebInputEvent& event); |
| bool IsWebInputEventRequest() const; |
| blink::WebInputEvent* GetEvent(); |
| RenderWidgetHostViewBase* GetRootView() const; |
| gfx::PointF GetLocation() const; |
| const ui::LatencyInfo& GetLatency() const; |
| |
| private: |
| base::WeakPtr<RenderWidgetHostViewBase> root_view; |
| |
| RenderWidgetHostAtPointCallback callback; |
| |
| // |location| is in the coordinate space of |root_view| which is |
| // either set directly when event is null or calculated from the event. |
| gfx::PointF location; |
| // |event| if set is in the coordinate space of |root_view|. |
| ui::WebScopedInputEvent event; |
| ui::LatencyInfo latency; |
| }; |
| |
| void ResolveTargetingRequest(TargetingRequest); |
| |
| // 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(); |
| |
| // Queries |target| to find the correct target for |request|. |
| // |target_location| is the location in |target|'s coordinate space. |
| // |last_request_target| and |last_target_location| provide a fallback target |
| // in 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. |
| void QueryClient(RenderWidgetHostViewBase* target, |
| const gfx::PointF& target_location, |
| RenderWidgetHostViewBase* last_request_target, |
| const gfx::PointF& last_target_location, |
| TargetingRequest request); |
| |
| // |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. |
| void FoundFrameSinkId(base::WeakPtr<RenderWidgetHostViewBase> target, |
| uint32_t request_id, |
| const gfx::PointF& target_location, |
| TracingUmaTracker tracker, |
| const viz::FrameSinkId& frame_sink_id, |
| const gfx::PointF& transformed_location); |
| |
| // |target_location|, if |
| // set, is the location in |target|'s coordinate space. |
| void FoundTarget(RenderWidgetHostViewBase* target, |
| const absl::optional<gfx::PointF>& target_location, |
| TargetingRequest* request); |
| |
| // 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_target, |
| const gfx::PointF& current_target_location, |
| base::WeakPtr<RenderWidgetHostViewBase> last_request_target, |
| const gfx::PointF& last_target_location); |
| |
| void OnInputTargetDisconnect(base::WeakPtr<RenderWidgetHostViewBase> target, |
| const gfx::PointF& location); |
| |
| HitTestResultsMatch GetHitTestResultsMatchBucket( |
| RenderWidgetHostViewBase* target, |
| TargetingRequest* request) const; |
| |
| base::TimeDelta async_hit_test_timeout_delay() { |
| return async_hit_test_timeout_delay_; |
| } |
| |
| absl::optional<TargetingRequest> request_in_flight_; |
| uint32_t last_request_id_ = 0; |
| std::queue<TargetingRequest> requests_; |
| |
| std::unordered_set<RenderWidgetHostViewBase*> unresponsive_views_; |
| |
| // Target to send events to if autoscroll is in progress |
| RenderWidgetTargetResult middle_click_result_; |
| |
| // True when the user middle click's mouse for autoscroll |
| bool is_autoscroll_in_progress_ = false; |
| |
| // 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::OneShotTimer async_hit_test_timeout_; |
| |
| uint64_t trace_id_; |
| |
| const raw_ptr<Delegate> delegate_; |
| base::WeakPtrFactory<RenderWidgetTargeter> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_TARGETER_H_ |