1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_TARGETER_H_
6 #define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_TARGETER_H_
7 
8 #include <queue>
9 
10 #include "base/memory/weak_ptr.h"
11 #include "base/optional.h"
12 #include "base/time/time.h"
13 #include "components/viz/common/surfaces/frame_sink_id.h"
14 #include "content/browser/renderer_host/render_widget_host_view_base.h"
15 #include "content/common/content_export.h"
16 #include "ui/events/blink/web_input_event_traits.h"
17 #include "ui/latency/latency_info.h"
18 
19 namespace blink {
20 class WebInputEvent;
21 }  // namespace blink
22 
23 namespace gfx {
24 class PointF;
25 }
26 
27 namespace content {
28 
29 class RenderWidgetHostViewBase;
30 class OneShotTimeoutMonitor;
31 
32 // TODO(sunxd): Make |RenderWidgetTargetResult| a class. Merge the booleans into
33 // a mask to reduce the size. Make the constructor take in enums for better
34 // readability.
35 struct CONTENT_EXPORT RenderWidgetTargetResult {
36   RenderWidgetTargetResult();
37   RenderWidgetTargetResult(const RenderWidgetTargetResult&);
38   RenderWidgetTargetResult(RenderWidgetHostViewBase* view,
39                            bool should_query_view,
40                            base::Optional<gfx::PointF> location,
41                            bool latched_target);
42   ~RenderWidgetTargetResult();
43 
44   RenderWidgetHostViewBase* view = nullptr;
45   bool should_query_view = false;
46   base::Optional<gfx::PointF> target_location = base::nullopt;
47   // When |latched_target| is false, we explicitly hit-tested events instead of
48   // using a known target.
49   bool latched_target = false;
50 };
51 
52 class TracingUmaTracker;
53 
54 class RenderWidgetTargeter {
55  public:
56   using RenderWidgetHostAtPointCallback =
57       base::OnceCallback<void(base::WeakPtr<RenderWidgetHostViewBase>,
58                               base::Optional<gfx::PointF>)>;
59 
60   class Delegate {
61    public:
~Delegate()62     virtual ~Delegate() {}
63 
64     virtual RenderWidgetTargetResult FindTargetSynchronouslyAtPoint(
65         RenderWidgetHostViewBase* root_view,
66         const gfx::PointF& location) = 0;
67 
68     virtual RenderWidgetTargetResult FindTargetSynchronously(
69         RenderWidgetHostViewBase* root_view,
70         const blink::WebInputEvent& event) = 0;
71 
72     // |event| must be non-null, and is in |root_view|'s coordinate space.
73     virtual void DispatchEventToTarget(
74         RenderWidgetHostViewBase* root_view,
75         RenderWidgetHostViewBase* target,
76         blink::WebInputEvent* event,
77         const ui::LatencyInfo& latency,
78         const base::Optional<gfx::PointF>& target_location) = 0;
79 
80     virtual void SetEventsBeingFlushed(bool events_being_flushed) = 0;
81 
82     virtual RenderWidgetHostViewBase* FindViewFromFrameSinkId(
83         const viz::FrameSinkId& frame_sink_id) const = 0;
84 
85     // Returns true if a further asynchronous query should be sent to the
86     // candidate RenderWidgetHostView.
87     virtual bool ShouldContinueHitTesting(
88         RenderWidgetHostViewBase* target_view) const = 0;
89   };
90 
91   enum class HitTestResultsMatch {
92     kDoNotMatch = 0,
93     kMatch = 1,
94     kHitTestResultChanged = 2,
95     kHitTestDataOutdated = 3,
96     kMaxValue = kHitTestDataOutdated,
97   };
98 
99   // The delegate must outlive this targeter.
100   explicit RenderWidgetTargeter(Delegate* delegate);
101   ~RenderWidgetTargeter();
102 
103   // Finds the appropriate target inside |root_view| for |event|, and dispatches
104   // it through the delegate. |event| is in the coord-space of |root_view|.
105   void FindTargetAndDispatch(RenderWidgetHostViewBase* root_view,
106                              const blink::WebInputEvent& event,
107                              const ui::LatencyInfo& latency);
108 
109   // Finds the appropriate target inside |root_view| for |point|, and passes the
110   // target along with the transformed coordinates of the point with respect to
111   // the target's coordinates.
112   void FindTargetAndCallback(RenderWidgetHostViewBase* root_view,
113                              const gfx::PointF& point,
114                              RenderWidgetHostAtPointCallback callback);
115 
116   void ViewWillBeDestroyed(RenderWidgetHostViewBase* view);
117 
118   bool HasEventsPendingDispatch() const;
119 
set_async_hit_test_timeout_delay_for_testing(const base::TimeDelta & delay)120   void set_async_hit_test_timeout_delay_for_testing(
121       const base::TimeDelta& delay) {
122     async_hit_test_timeout_delay_ = delay;
123   }
124 
num_requests_in_queue_for_testing()125   size_t num_requests_in_queue_for_testing() { return requests_.size(); }
is_request_in_flight_for_testing()126   bool is_request_in_flight_for_testing() {
127     return request_in_flight_.has_value();
128   }
129 
130   void SetIsAutoScrollInProgress(bool autoscroll_in_progress);
131 
is_auto_scroll_in_progress()132   bool is_auto_scroll_in_progress() const { return is_autoscroll_in_progress_; }
133 
134  private:
135   class TargetingRequest {
136    public:
137     TargetingRequest(base::WeakPtr<RenderWidgetHostViewBase>,
138                      const blink::WebInputEvent&,
139                      const ui::LatencyInfo&);
140     TargetingRequest(base::WeakPtr<RenderWidgetHostViewBase>,
141                      const gfx::PointF&,
142                      RenderWidgetHostAtPointCallback);
143     TargetingRequest(TargetingRequest&& request);
144     TargetingRequest& operator=(TargetingRequest&& other);
145     ~TargetingRequest();
146 
147     void RunCallback(RenderWidgetHostViewBase* target,
148                      base::Optional<gfx::PointF> point);
149 
150     bool MergeEventIfPossible(const blink::WebInputEvent& event);
151     bool IsWebInputEventRequest() const;
152     blink::WebInputEvent* GetEvent();
153     RenderWidgetHostViewBase* GetRootView() const;
154     gfx::PointF GetLocation() const;
155     const ui::LatencyInfo& GetLatency() const;
156 
157    private:
158     base::WeakPtr<RenderWidgetHostViewBase> root_view;
159 
160     RenderWidgetHostAtPointCallback callback;
161 
162     // |location| is in the coordinate space of |root_view| which is
163     // either set directly when event is null or calculated from the event.
164     gfx::PointF location;
165     // |event| if set is in the coordinate space of |root_view|.
166     ui::WebScopedInputEvent event;
167     ui::LatencyInfo latency;
168 
169     DISALLOW_COPY_AND_ASSIGN(TargetingRequest);
170   };
171 
172   void ResolveTargetingRequest(TargetingRequest);
173 
174   // Attempts to target and dispatch all events in the queue. It stops if it has
175   // to query a client, or if the queue becomes empty.
176   void FlushEventQueue();
177 
178   // Queries |target| to find the correct target for |request|.
179   // |target_location| is the location in |target|'s coordinate space.
180   // |last_request_target| and |last_target_location| provide a fallback target
181   // in the case that the query times out. These should be null values when
182   // querying the root view, and the target's immediate parent view otherwise.
183   void QueryClient(RenderWidgetHostViewBase* target,
184                    const gfx::PointF& target_location,
185                    RenderWidgetHostViewBase* last_request_target,
186                    const gfx::PointF& last_target_location,
187                    TargetingRequest request);
188 
189   // |target_location|, if
190   // set, is the location in |target|'s coordinate space.
191   // |target| is the current target that will be queried using its
192   // InputTargetClient interface.
193   // |frame_sink_id| is returned from the InputTargetClient to indicate where
194   // the event should be routed, and |transformed_location| is the point in
195   // that new target's coordinate space.
196   void FoundFrameSinkId(base::WeakPtr<RenderWidgetHostViewBase> target,
197                         uint32_t request_id,
198                         const gfx::PointF& target_location,
199                         TracingUmaTracker tracker,
200                         const viz::FrameSinkId& frame_sink_id,
201                         const gfx::PointF& transformed_location);
202 
203   // |target_location|, if
204   // set, is the location in |target|'s coordinate space. If |latched_target| is
205   // false, we explicitly did hit-testing for this event, instead of using a
206   // known target.
207   void FoundTarget(RenderWidgetHostViewBase* target,
208                    const base::Optional<gfx::PointF>& target_location,
209                    bool latched_target,
210                    TargetingRequest* request);
211 
212   // Callback when the hit testing timer fires, to resume event processing
213   // without further waiting for a response to the last targeting request.
214   void AsyncHitTestTimedOut(
215       base::WeakPtr<RenderWidgetHostViewBase> current_request_target,
216       const gfx::PointF& current_target_location,
217       base::WeakPtr<RenderWidgetHostViewBase> last_request_target,
218       const gfx::PointF& last_target_location);
219 
220   void OnInputTargetDisconnect(base::WeakPtr<RenderWidgetHostViewBase> target,
221                                const gfx::PointF& location);
222 
223   HitTestResultsMatch GetHitTestResultsMatchBucket(
224       RenderWidgetHostViewBase* target,
225       TargetingRequest* request) const;
226 
async_hit_test_timeout_delay()227   base::TimeDelta async_hit_test_timeout_delay() {
228     return async_hit_test_timeout_delay_;
229   }
230 
231   base::Optional<TargetingRequest> request_in_flight_;
232   uint32_t last_request_id_ = 0;
233   std::queue<TargetingRequest> requests_;
234 
235   std::unordered_set<RenderWidgetHostViewBase*> unresponsive_views_;
236 
237   // This value keeps track of the number of clients we have asked in order to
238   // do async hit-testing.
239   uint32_t async_depth_ = 0;
240 
241   // Target to send events to if autoscroll is in progress
242   RenderWidgetTargetResult middle_click_result_;
243 
244   // True when the user middle click's mouse for autoscroll
245   bool is_autoscroll_in_progress_ = false;
246 
247   // This value limits how long to wait for a response from the renderer
248   // process before giving up and resuming event processing.
249   base::TimeDelta async_hit_test_timeout_delay_;
250 
251   std::unique_ptr<OneShotTimeoutMonitor> async_hit_test_timeout_;
252 
253   uint64_t trace_id_;
254 
255   const bool is_viz_hit_testing_debug_enabled_;
256   // Stores SurfaceIds for regions that were async queried if hit-test debugging
257   // is enabled. This allows us to send the queried regions in batches.
258   std::vector<viz::FrameSinkId> hit_test_async_queried_debug_queue_;
259 
260   Delegate* const delegate_;
261   base::WeakPtrFactory<RenderWidgetTargeter> weak_ptr_factory_{this};
262 
263   DISALLOW_COPY_AND_ASSIGN(RenderWidgetTargeter);
264 };
265 
266 }  // namespace content
267 
268 #endif  // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_TARGETER_H_
269