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