1 // Copyright 2016 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_FIND_REQUEST_MANAGER_H_ 6 #define CONTENT_BROWSER_FIND_REQUEST_MANAGER_H_ 7 8 #include <memory> 9 #include <unordered_map> 10 #include <unordered_set> 11 #include <vector> 12 13 #include "base/containers/queue.h" 14 #include "content/common/content_export.h" 15 #include "content/public/browser/web_contents_observer.h" 16 #include "content/public/common/stop_find_action.h" 17 #include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" 18 #include "ui/gfx/geometry/rect.h" 19 #include "ui/gfx/geometry/rect_f.h" 20 21 namespace content { 22 23 class FindInPageClient; 24 class RenderFrameHost; 25 class RenderFrameHostImpl; 26 class WebContentsImpl; 27 28 // FindRequestManager manages all of the find-in-page requests/replies 29 // initiated/received through a WebContents. It coordinates searching across 30 // multiple (potentially out-of-process) frames, handles the aggregation of find 31 // results from each frame, and facilitates active match traversal. It is 32 // instantiated once per top-level WebContents, and is owned by that 33 // WebContents. 34 class CONTENT_EXPORT FindRequestManager { 35 public: 36 explicit FindRequestManager(WebContentsImpl* web_contents); 37 ~FindRequestManager(); 38 39 // Initiates a find operation for |search_text| with the options specified in 40 // |options|. |request_id| uniquely identifies the find request. 41 void Find(int request_id, 42 const base::string16& search_text, 43 blink::mojom::FindOptionsPtr options); 44 45 // Stops the active find session and clears the general highlighting of the 46 // matches. |action| determines whether the last active match (if any) will be 47 // activated, cleared, or remain highlighted. 48 void StopFinding(StopFindAction action); 49 50 // Handles the final update from |rfh| for the find request with id 51 // |request_id|. 52 void HandleFinalUpdateForFrame(RenderFrameHostImpl* rfh, int request_id); 53 54 // The number of matches on |rfh| has changed from |old_count| to |new_count|. 55 // This method updates the total number of matches and also updates 56 // |active_match_ordinal_| accordingly. 57 void UpdatedFrameNumberOfMatches(RenderFrameHostImpl* rfh, 58 unsigned int old_count, 59 unsigned int new_count); 60 61 bool ShouldIgnoreReply(RenderFrameHostImpl* rfh, int request_id); 62 63 void SetActiveMatchRect(const gfx::Rect& active_match_rect); 64 65 void SetActiveMatchOrdinal(RenderFrameHostImpl* rfh, 66 int request_id, 67 int active_match_ordinal); 68 69 // Sends the find results (as they currently are) to the WebContents. 70 // |final_update| is true if we have received all of the updates from 71 // every frame for this request. 72 void NotifyFindReply(int request_id, bool final_update); 73 74 // Removes a frame from the set of frames being searched. This should be 75 // called whenever a frame is discovered to no longer exist. 76 void RemoveFrame(RenderFrameHost* rfh); 77 78 // Tells active frame to clear the active match highlighting. 79 void ClearActiveFindMatch(); 80 81 #if defined(OS_ANDROID) 82 // Selects and zooms to the find result nearest to the point (x, y), defined 83 // in find-in-page coordinates. 84 void ActivateNearestFindResult(float x, float y); 85 86 // Called when a reply is received from a frame in response to the 87 // GetNearestFindResult mojo call. 88 void OnGetNearestFindResultReply(RenderFrameHostImpl* rfh, 89 int request_id, 90 float distance); 91 92 // Requests the rects of the current find matches from the renderer process. 93 void RequestFindMatchRects(int current_version); 94 95 // Called when a reply is received from a frame in response to a request for 96 // find match rects. 97 void OnFindMatchRectsReply(RenderFrameHost* rfh, 98 int version, 99 const std::vector<gfx::RectF>& rects, 100 const gfx::RectF& active_rect); 101 #endif 102 103 const std::unordered_set<RenderFrameHost*> render_frame_hosts_pending_initial_reply_for_testing()104 render_frame_hosts_pending_initial_reply_for_testing() const { 105 return pending_initial_replies_; 106 } 107 GetSelectionRectForTesting()108 gfx::Rect GetSelectionRectForTesting() { return selection_rect_; } 109 110 private: 111 // An invalid ID. This value is invalid for any render process ID, render 112 // frame ID, find request ID, or find match rects version number. 113 static const int kInvalidId; 114 115 class FrameObserver; 116 117 // The request data for a single find request. 118 struct FindRequest { 119 // The find request ID that uniquely identifies this find request. 120 int id = kInvalidId; 121 122 // The text that is being searched for in this find request. 123 base::string16 search_text; 124 125 // The set of find options in effect for this find request. 126 blink::mojom::FindOptionsPtr options; 127 128 FindRequest(); 129 FindRequest(int id, 130 const base::string16& search_text, 131 blink::mojom::FindOptionsPtr options); 132 FindRequest(const FindRequest& request); 133 ~FindRequest(); 134 135 FindRequest& operator=(const FindRequest& request); 136 }; 137 138 // Resets all of the per-session state for a new find-in-page session. 139 void Reset(const FindRequest& initial_request); 140 141 // Called internally as find requests come up in the queue. 142 void FindInternal(const FindRequest& request); 143 144 // Called when an informative response (a response with enough information to 145 // be able to route subsequent find requests) comes in for the find request 146 // with ID |request_id|. Advances the |find_request_queue_| if appropriate. 147 void AdvanceQueue(int request_id); 148 149 // Sends find request |request| through mojo to the RenderFrame associated 150 // with |rfh|. 151 void SendFindRequest(const FindRequest& request, RenderFrameHost* rfh); 152 153 // Returns the initial frame in search order. This will be either the first 154 // frame, if searching forward, or the last frame, if searching backward. 155 RenderFrameHost* GetInitialFrame(bool forward) const; 156 157 // Traverses the frame tree to find and return the next RenderFrameHost after 158 // |from_rfh| in search order. |forward| indicates whether the frame tree 159 // should be traversed forward (if true) or backward (if false). If 160 // |matches_only| is set, then the frame tree will be traversed until the 161 // first frame is found for which matches have been found. If |wrap| is set, 162 // then the traversal can wrap around past the last frame to the first one (or 163 // vice-versa, if |forward| == false). If no frame can be found under these 164 // conditions, nullptr is returned. 165 RenderFrameHost* Traverse(RenderFrameHost* from_rfh, 166 bool forward, 167 bool matches_only, 168 bool wrap) const; 169 170 // Adds a frame to the set of frames that are being searched. The new frame 171 // will automatically be searched when added, using the same options (stored 172 // in |current_request_.options|). |force| should be set to true when a 173 // dynamic content change is suspected, which will treat the frame as a newly 174 // added frame even if it has already been searched. This will force a 175 // re-search of the frame. 176 void AddFrame(RenderFrameHost* rfh, bool force); 177 178 // Returns whether |rfh| is in the set of frames being searched in the current 179 // find session. 180 bool CheckFrame(RenderFrameHost* rfh) const; 181 182 // Computes and updates |active_match_ordinal_| based on |active_frame_| and 183 // |relative_active_match_ordinal_|. 184 void UpdateActiveMatchOrdinal(); 185 186 // Called when all pending find replies have been received for the find 187 // request with ID |request_id|. The final update was received from |rfh|. 188 // 189 // Note that this is the final update for this particular find request, but 190 // not necessarily for all issued requests. If there are still pending replies 191 // expected for a previous find request, then the outgoing find reply issued 192 // from this function will not be marked final. 193 void FinalUpdateReceived(int request_id, RenderFrameHost* rfh); 194 195 #if defined(OS_ANDROID) 196 // Called when a nearest find result reply is no longer pending for a frame. 197 void RemoveNearestFindResultPendingReply(RenderFrameHost* rfh); 198 199 // Called when a find match rects reply is no longer pending for a frame. 200 void RemoveFindMatchRectsPendingReply(RenderFrameHost* rfh); 201 202 // State related to ActivateNearestFindResult requests. 203 struct ActivateNearestFindResultState { 204 // An ID to uniquely identify the current nearest find result request and 205 // its replies. 206 int current_request_id = kInvalidId; 207 208 // The value of the requested point, in find-in-page coordinates. 209 gfx::PointF point = gfx::PointF(0.0f, 0.0f); 210 211 float nearest_distance = FLT_MAX; 212 213 // The frame containing the nearest result found so far. 214 RenderFrameHostImpl* nearest_frame = nullptr; 215 216 // Nearest find result replies are still pending for these frames. 217 std::unordered_set<RenderFrameHost*> pending_replies; 218 219 ActivateNearestFindResultState(); 220 ActivateNearestFindResultState(float x, float y); 221 ~ActivateNearestFindResultState(); 222 GetNextIDActivateNearestFindResultState223 static int GetNextID() { 224 static int next_id = 0; 225 return next_id++; 226 } 227 } activate_; 228 229 // Data for find match rects in a single frame. 230 struct FrameRects { 231 // The rects contained in a single frame. 232 std::vector<gfx::RectF> rects; 233 234 // The version number for these rects, as reported by their containing 235 // frame. This version is incremented independently in each frame. 236 int version = kInvalidId; 237 238 FrameRects(); 239 FrameRects(const std::vector<gfx::RectF>& rects, int version); 240 ~FrameRects(); 241 }; 242 243 // State related to FindMatchRects requests. 244 struct FindMatchRectsState { 245 // The latest find match rects version known by the requester. This will be 246 // compared to |known_version_| after polling frames for updates to their 247 // match rects, in order to determine if the requester already has the 248 // latest version of rects or not. 249 int request_version = kInvalidId; 250 251 // The current overall find match rects version known by 252 // FindRequestManager. This version should be incremented whenever 253 // |frame_rects| is updated. 254 int known_version = 0; 255 256 // A map from each frame to its find match rects. 257 std::unordered_map<RenderFrameHost*, FrameRects> frame_rects; 258 259 // The active find match rect. 260 gfx::RectF active_rect; 261 262 // Find match rects replies are still pending for these frames. 263 std::unordered_set<RenderFrameHost*> pending_replies; 264 265 FindMatchRectsState(); 266 ~FindMatchRectsState(); 267 } match_rects_; 268 #endif 269 270 // The WebContents that owns this FindRequestManager. This also defines the 271 // scope of all find sessions. Only frames in |contents_| and any inner 272 // WebContentses within it will be searched. 273 WebContentsImpl* const contents_; 274 275 // The request ID of the initial find request in the current find-in-page 276 // session, which uniquely identifies this session. Request IDs are included 277 // in all find-related IPCs, which allows reply IPCs containing results from 278 // previous sessions (with |request_id| < |current_session_id_|) to be easily 279 // identified and ignored. 280 int current_session_id_ = kInvalidId; 281 282 // The current find request. 283 FindRequest current_request_; 284 285 // The set of frames that are still expected to reply to a pending initial 286 // find request. Frames are removed from |pending_initial_replies_| when their 287 // reply to the initial find request is received with |final_update| set to 288 // true. 289 std::unordered_set<RenderFrameHost*> pending_initial_replies_; 290 291 // The frame (if any) that is still expected to reply to the last pending 292 // "find next" request. 293 RenderFrameHost* pending_find_next_reply_ = nullptr; 294 295 // Indicates whether an update to the active match ordinal is expected. Once 296 // set, |pending_active_match_ordinal_| will not reset until an update to the 297 // active match ordinal is received in response to the find request with ID 298 // |current_request_.id| (the latest request). 299 bool pending_active_match_ordinal_ = false; 300 301 // The FindInPageClient associated with each frame. There will necessarily be 302 // entries in this map for every frame that is being (or has been) searched in 303 // the current find session, and no other frames. 304 std::unordered_map<RenderFrameHost*, std::unique_ptr<FindInPageClient>> 305 find_in_page_clients_; 306 307 // The total number of matches found in the current find-in-page session. This 308 // should always be equal to the sum of all the entries in 309 // |matches_per_frame_|. 310 int number_of_matches_ = 0; 311 312 // The frame containing the active match, if one exists, or nullptr otherwise. 313 RenderFrameHostImpl* active_frame_ = nullptr; 314 315 // The active match ordinal relative to the matches found in its own frame. 316 int relative_active_match_ordinal_ = 0; 317 318 // The overall active match ordinal for the current find-in-page session. 319 int active_match_ordinal_ = 0; 320 321 // The rectangle around the active match, in screen coordinates. 322 gfx::Rect selection_rect_; 323 324 // Find requests are queued here when previous requests need to be handled 325 // before these ones can be properly routed. 326 base::queue<FindRequest> find_request_queue_; 327 328 // Keeps track of the find request ID of the last find reply reported via 329 // NotifyFindReply(). 330 int last_reported_id_ = kInvalidId; 331 332 // WebContentsObservers to observe frame changes in |contents_| and its inner 333 // WebContentses. 334 std::vector<std::unique_ptr<FrameObserver>> frame_observers_; 335 336 DISALLOW_COPY_AND_ASSIGN(FindRequestManager); 337 }; 338 339 } // namespace content 340 341 #endif // CONTENT_BROWSER_FIND_REQUEST_MANAGER_H_ 342