Chromium Code Reviews| Index: content/browser/find_request_manager.cc |
| diff --git a/content/browser/find_request_manager.cc b/content/browser/find_request_manager.cc |
| index 915ecf8d9fd51fead4daa96df981924bbf1d753a..adbb9c738f99f65466467eb0d90ee6acef9b5e0c 100644 |
| --- a/content/browser/find_request_manager.cc |
| +++ b/content/browser/find_request_manager.cc |
| @@ -11,13 +11,90 @@ |
| namespace content { |
| +namespace { |
| + |
| +// Returns the deepest last child frame under |node|/|rfh| in the frame tree. |
| +FrameTreeNode* GetDeepestLastChild(FrameTreeNode* node) { |
|
dglazkov
2016/06/02 04:48:57
Can this live in some common FrameTreeNode place?
paulmeyer
2016/06/02 17:36:36
Yeah, it can, and I agree that it should.
I had a
|
| + while (node->child_count()) |
| + node = node->child_at(node->child_count() - 1); |
| + return node; |
| +} |
| +RenderFrameHost* GetDeepestLastChild(RenderFrameHost* rfh) { |
| + FrameTreeNode* node = |
| + static_cast<RenderFrameHostImpl*>(rfh)->frame_tree_node(); |
| + return GetDeepestLastChild(node)->current_frame_host(); |
| +} |
| + |
| +// Returns the FrameTreeNode directly after |node| in the frame tree in search |
| +// order, or nullptr if one does not exist. If |wrap| is set, then wrapping |
| +// between the first and last frames is permitted. Note that this traversal |
| +// follows the same ordering as in blink::FrameTree::traverseNextWithWrap(). |
| +FrameTreeNode* TraverseNext(FrameTreeNode* node, bool wrap) { |
| + if (node->child_count()) |
| + return node->child_at(0); |
| + |
| + FrameTreeNode* sibling = node->NextSibling(); |
| + while (!sibling) { |
| + if (!node->parent()) |
| + return wrap ? node : nullptr; |
| + node = node->parent(); |
| + sibling = node->NextSibling(); |
| + } |
| + return sibling; |
| +} |
| + |
| +// Returns the FrameTreeNode directly before |node| in the frame tree in search |
| +// order, or nullptr if one does not exist. If |wrap| is set, then wrapping |
| +// between the first and last frames is permitted. Note that this traversal |
| +// follows the same ordering as in blink::FrameTree::traversePreviousWithWrap(). |
| +FrameTreeNode* TraversePrevious(FrameTreeNode* node, bool wrap) { |
| + if (FrameTreeNode* previous_sibling = node->PreviousSibling()) |
| + return GetDeepestLastChild(previous_sibling); |
| + if (node->parent()) |
| + return node->parent(); |
| + return wrap ? GetDeepestLastChild(node) : nullptr; |
| +} |
| + |
| +// The same as either TraverseNext() or TraversePrevious() depending on |
| +// |forward|. |
| +FrameTreeNode* TraverseNode(FrameTreeNode* node, bool forward, bool wrap) { |
| + return forward ? TraverseNext(node, wrap) : TraversePrevious(node, wrap); |
| +} |
| + |
| +} // namespace |
| + |
| +#if defined(OS_ANDROID) |
| +FindRequestManager::ActivateNearestFindResultState:: |
| +ActivateNearestFindResultState() = default; |
| +FindRequestManager::ActivateNearestFindResultState:: |
| +ActivateNearestFindResultState(float x, float y) |
| + : current_request_id(GetNextID()), |
| + x(x), |
| + y(y) {} |
| +FindRequestManager::ActivateNearestFindResultState:: |
| +~ActivateNearestFindResultState() {} |
| + |
| +FindRequestManager::FrameRects::FrameRects() = default; |
| +FindRequestManager::FrameRects::FrameRects(const std::vector<gfx::RectF>& rects, |
| + int version) |
| + : rects(rects), version(version) {} |
| +FindRequestManager::FrameRects::~FrameRects() {} |
| + |
| +FindRequestManager::FindMatchRectsState::FindMatchRectsState() = default; |
| +FindRequestManager::FindMatchRectsState::~FindMatchRectsState() {} |
| +#endif |
| + |
| // static |
| const int FindRequestManager::kInvalidId = -1; |
| FindRequestManager::FindRequestManager(WebContentsImpl* web_contents) |
| - : contents_(web_contents), |
| + : WebContentsObserver(web_contents), |
| + contents_(web_contents), |
| current_session_id_(kInvalidId), |
| + pending_active_match_ordinal_(false), |
| number_of_matches_(0), |
| + active_frame_(nullptr), |
| + relative_active_match_ordinal_(0), |
| active_match_ordinal_(0) {} |
| FindRequestManager::~FindRequestManager() {} |
| @@ -31,28 +108,26 @@ void FindRequestManager::Find(int request_id, |
| DCHECK_GT(request_id, current_request_.id); |
| DCHECK_GT(request_id, current_session_id_); |
| - FindRequest request(request_id, search_text, options); |
| - |
| - if (options.findNext) { |
| - // This is a find next operation. |
| - |
| - // This implies that there is an ongoing find session with the same search |
| - // text. |
| - DCHECK_GE(current_session_id_, 0); |
| - DCHECK_EQ(request.search_text, current_request_.search_text); |
| - |
| - current_request_ = request; |
| - } else { |
| - // This is an initial find operation. |
| - Reset(request); |
| - } |
| + // If this is a new find session, clear any queued requests from last session. |
| + if (!options.findNext) |
| + find_request_queue_ = std::queue<FindRequest>(); |
| - SendFindIPC(request, contents_->GetMainFrame()); |
| + find_request_queue_.emplace(request_id, search_text, options); |
| + if (find_request_queue_.size() == 1) |
| + FindInternal(find_request_queue_.front()); |
| } |
| void FindRequestManager::StopFinding(StopFindAction action) { |
| - SendStopFindingIPC(action, contents_->GetMainFrame()); |
| + contents_->SendToAllFrames( |
| + new FrameMsg_StopFinding(MSG_ROUTING_NONE, action)); |
| + |
| current_session_id_ = kInvalidId; |
| +#if defined(OS_ANDROID) |
| + // It is important that these pending replies are cleared whenever a find |
| + // session ends, so that subsequent replies for the old session are ignored. |
| + activate_.pending_replies.clear(); |
| + match_rects_.pending_replies.clear(); |
| +#endif |
| } |
| void FindRequestManager::OnFindReply(RenderFrameHost* rfh, |
| @@ -64,29 +139,175 @@ void FindRequestManager::OnFindReply(RenderFrameHost* rfh, |
| // Ignore stale replies from abandoned find sessions. |
| if (current_session_id_ == kInvalidId || request_id < current_session_id_) |
| return; |
| + DCHECK(CheckFrame(rfh)); |
| + |
| + // Update the stored find results. |
| + |
| + DCHECK_GE(number_of_matches, -1); |
| + DCHECK_GE(active_match_ordinal, -1); |
| - // Update the stored results. |
| - number_of_matches_ = number_of_matches; |
| - selection_rect_ = selection_rect; |
| - active_match_ordinal_ = active_match_ordinal; |
| + // Check for an update to the number of matches. |
| + if (number_of_matches != -1) { |
| + DCHECK_GE(number_of_matches, 0); |
| + // Increment the global number of matches by the number of additional |
| + // matches found for this frame. |
| + auto matches_per_frame_it = matches_per_frame_.find(rfh); |
| + number_of_matches_ += number_of_matches - matches_per_frame_it->second; |
| + matches_per_frame_it->second = number_of_matches; |
| + } |
| + |
| + // Check for an update to the selection rect. |
| + if (!selection_rect.IsEmpty()) |
| + selection_rect_ = selection_rect; |
| + |
| + // Check for an update to the active match ordinal. |
| + if (active_match_ordinal > 0) { |
| + if (rfh == active_frame_) { |
| + active_match_ordinal_ += |
| + active_match_ordinal - relative_active_match_ordinal_; |
| + relative_active_match_ordinal_ = active_match_ordinal; |
| + } else { |
| + if (active_frame_) { |
| + // The new active match is in a different frame than the previous, so |
| + // the previous active frame needs to be informed (to clear its active |
| + // match highlighting). |
| + active_frame_->Send(new FrameMsg_ClearActiveFindMatch( |
| + active_frame_->GetRoutingID())); |
| + } |
| + active_frame_ = rfh; |
| + relative_active_match_ordinal_ = active_match_ordinal; |
| + UpdateActiveMatchOrdinal(); |
| + } |
| + if (pending_active_match_ordinal_ && request_id == current_request_.id) |
| + pending_active_match_ordinal_ = false; |
| + AdvanceQueue(request_id); |
| + } |
| - NotifyFindReply(request_id, final_update); |
| + if (!final_update) { |
| + NotifyFindReply(request_id, false /* final_update */); |
| + return; |
| + } |
| + |
| + // This is the final update for this frame for the current find operation. |
| + |
| + pending_replies_.erase(rfh); |
| + if (request_id == current_session_id_ && !pending_replies_.empty()) { |
| + NotifyFindReply(request_id, false /* final_update */); |
| + return; |
| + } |
| + DCHECK(request_id == current_session_id_ || |
| + current_request_.options.findNext); |
| + |
| + // This is the final update for the current find operation. |
| + FinalUpdate(request_id, rfh); |
| } |
| +void FindRequestManager::RemoveFrame(RenderFrameHost* rfh) { |
| + if (current_session_id_ == kInvalidId || !CheckFrame(rfh)) |
| + return; |
| + |
| + // If matches are counted for the frame that is being removed, decrement the |
| + // match total before erasing that entry. |
| + auto it = matches_per_frame_.find(rfh); |
| + if (it != matches_per_frame_.end()) { |
| + number_of_matches_ -= it->second; |
| + matches_per_frame_.erase(it); |
| + } |
| + |
| + // Update the active match ordinal, since it may have changed. |
| + if (active_frame_ == rfh) { |
| + active_frame_ = nullptr; |
| + relative_active_match_ordinal_ = 0; |
| + } |
| + UpdateActiveMatchOrdinal(); |
| + |
| #if defined(OS_ANDROID) |
| -void FindRequestManager::ActivateNearestFindResult(float x, |
| - float y) { |
| + // The removed frame may contain the nearest find result known so far. Note |
| + // that once all queried frames have responded, if this result was the overall |
| + // nearest, then no activation will occur. |
| + if (rfh == activate_.nearest_frame) |
| + activate_.nearest_frame = nullptr; |
| + |
| + // Match rects in the removed frame are no longer relevant. |
| + if (match_rects_.count(rfh)) { |
| + match_rects_.frame_rects.erase(rfh); |
| + ++match_rects_.known_version; |
| + } |
| + |
| + // A reply should not be expected from the removed frame. |
| + RemoveNearestFindResultPendingReply(rfh); |
| + RemoveFindMatchRectsPendingReply(rfh); |
| +#endif |
| + |
| + if (pending_replies_.count(rfh)) { |
| + // A reply should not be expected from the removed frame. |
| + pending_replies_.erase(rfh); |
| + if (pending_replies_.empty()) { |
| + FinalUpdate(current_request_.id, rfh); |
| + return; |
| + } |
| + } |
| + |
| + NotifyFindReply(current_session_id_, |
| + pending_replies_.empty() /* final_update */); |
| +} |
| + |
| +#if defined(OS_ANDROID) |
| +void FindRequestManager::ActivateNearestFindResult(float x, float y) { |
| if (current_session_id_ == kInvalidId) |
| return; |
| - auto rfh = contents_->GetMainFrame(); |
| - rfh->Send(new InputMsg_ActivateNearestFindResult( |
| - rfh->GetRoutingID(), current_session_id_, x, y)); |
| + activate_ = ActivateNearestFindResultState(x, y); |
| + |
| + // Request from each frame the distance to the nearest find result (in that |
| + // frame) from the point (x, y), defined in find-in-page coordinates. |
| + for (FrameTreeNode* node : contents_->GetFrameTree()->Nodes()) { |
| + RenderFrameHost* rfh = node->current_frame_host(); |
| + |
| + if (!CheckFrame(rfh)) |
| + continue; |
| + |
| + activate_.pending_replies.insert(rfh); |
| + rfh->Send(new FrameMsg_GetNearestFindResult( |
| + rfh->GetRoutingID(), activate_.current_request_id, |
| + activate_.x, activate_.y)); |
| + } |
| +} |
| + |
| +void FindRequestManager::OnGetNearestFindResultReply(RenderFrameHost* rfh, |
| + int request_id, |
| + float distance) { |
| + if (request_id != activate_.current_request_id || |
| + !activate_.pending_replies.count(rfh)) { |
| + return; |
| + } |
| + |
| + // Check if this frame has a nearer find result than the current nearest. |
| + if (distance < activate_.nearest_distance) { |
| + activate_.nearest_frame = rfh; |
| + activate_.nearest_distance = distance; |
| + } |
| + |
| + RemoveNearestFindResultPendingReply(rfh); |
| } |
| void FindRequestManager::RequestFindMatchRects(int current_version) { |
| + match_rects_.pending_replies.clear(); |
| match_rects_.request_version = current_version; |
| - SendFindMatchRectsIPC(contents_->GetMainFrame()); |
| + |
| + // Request the latest find match rects from each frame. |
| + for (FrameTreeNode* node : contents_->GetFrameTree()->Nodes()) { |
| + RenderFrameHost* rfh = node->current_frame_host(); |
| + |
| + if (!CheckFrame(rfh)) |
| + continue; |
| + |
| + match_rects_.pending_replies.insert(rfh); |
| + auto it = match_rects_.frame_rects.find(rfh); |
| + int version = (it != match_rects_.frame_rects.end()) |
| + ? it->second.version : kInvalidId; |
| + rfh->Send(new FrameMsg_FindMatchRects(rfh->GetRoutingID(), version)); |
| + } |
| } |
| void FindRequestManager::OnFindMatchRectsReply( |
| @@ -94,32 +315,96 @@ void FindRequestManager::OnFindMatchRectsReply( |
| int version, |
| const std::vector<gfx::RectF>& rects, |
| const gfx::RectF& active_rect) { |
| - contents_->NotifyFindMatchRectsReply(version, rects, active_rect); |
| + auto it = match_rects_.frame_rects.find(rfh); |
| + if (it == match_rects_.frame_rects.end() || it->second.version != version) { |
| + // New version of rects has been received, so update the data. |
| + match_rects_.frame_rects[rfh] = FrameRects(rects, version); |
| + ++match_rects_.known_version; |
| + } |
| + if (!active_rect.IsEmpty()) |
| + match_rects_.active_rect = active_rect; |
| + RemoveFindMatchRectsPendingReply(rfh); |
| } |
| #endif |
| +void FindRequestManager::RenderFrameDeleted(RenderFrameHost* rfh) { |
| + RemoveFrame(rfh); |
| +} |
| + |
| +void FindRequestManager::RenderFrameHostChanged(RenderFrameHost* old_host, |
| + RenderFrameHost* new_host) { |
| + RemoveFrame(old_host); |
| +} |
| + |
| +void FindRequestManager::FrameDeleted(RenderFrameHost* rfh) { |
| + RemoveFrame(rfh); |
| +} |
| + |
| void FindRequestManager::Reset(const FindRequest& initial_request) { |
| current_session_id_ = initial_request.id; |
| current_request_ = initial_request; |
| + pending_replies_.clear(); |
| + pending_active_match_ordinal_ = true; |
| + matches_per_frame_.clear(); |
| number_of_matches_ = 0; |
| + active_frame_ = nullptr; |
| + relative_active_match_ordinal_ = 0; |
| active_match_ordinal_ = 0; |
| selection_rect_ = gfx::Rect(); |
| #if defined(OS_ANDROID) |
| - match_rects_ = FindMatchRectsState(); |
| + activate_ = ActivateNearestFindResultState(); |
| + match_rects_.pending_replies.clear(); |
| #endif |
| } |
| +void FindRequestManager::FindInternal(const FindRequest& request) { |
| + DCHECK_GT(request.id, current_request_.id); |
| + DCHECK_GT(request.id, current_session_id_); |
| + |
| + if (request.options.findNext) { |
| + // This is a find next operation. |
| + |
| + // This implies that there is an ongoing find session with the same search |
| + // text. |
| + DCHECK_GE(current_session_id_, 0); |
| + DCHECK_EQ(request.search_text, current_request_.search_text); |
| + |
| + // The find next request will be directed at the focused frame if there is |
| + // one, or the first frame with matches otherwise. |
| + RenderFrameHost* target_rfh = contents_->GetFocusedFrame(); |
| + if (!target_rfh || !CheckFrame(target_rfh)) |
| + target_rfh = GetInitialFrame(request.options.forward); |
| + |
| + SendFindIPC(request, target_rfh); |
| + current_request_ = request; |
| + pending_active_match_ordinal_ = true; |
| + return; |
| + } |
| + |
| + // This is an initial find operation. |
| + Reset(request); |
| + for (FrameTreeNode* node : contents_->GetFrameTree()->Nodes()) |
| + AddFrame(node->current_frame_host()); |
| +} |
| + |
| +void FindRequestManager::AdvanceQueue(int request_id) { |
| + if (find_request_queue_.empty() || |
| + request_id != find_request_queue_.front().id) { |
| + return; |
| + } |
| + |
| + find_request_queue_.pop(); |
| + if (!find_request_queue_.empty()) |
| + FindInternal(find_request_queue_.front()); |
| +} |
| + |
| void FindRequestManager::SendFindIPC(const FindRequest& request, |
| RenderFrameHost* rfh) { |
| + pending_replies_.insert(rfh); |
| rfh->Send(new FrameMsg_Find(rfh->GetRoutingID(), request.id, |
| request.search_text, request.options)); |
| } |
| -void FindRequestManager::SendStopFindingIPC(StopFindAction action, |
| - RenderFrameHost* rfh) const { |
| - rfh->Send(new FrameMsg_StopFinding(rfh->GetRoutingID(), action)); |
| -} |
| - |
| void FindRequestManager::NotifyFindReply(int request_id, |
| bool final_update) const { |
| if (request_id == kInvalidId) { |
| @@ -131,10 +416,173 @@ void FindRequestManager::NotifyFindReply(int request_id, |
| active_match_ordinal_, final_update); |
| } |
| +RenderFrameHost* FindRequestManager::GetInitialFrame(bool forward) const { |
| + RenderFrameHost* rfh = contents_->GetMainFrame(); |
| + |
| + if (!forward) |
| + rfh = GetDeepestLastChild(rfh); |
| + |
| + return rfh; |
| +} |
| + |
| +RenderFrameHost* FindRequestManager::Traverse(RenderFrameHost* from_rfh, |
| + bool forward, |
| + bool matches_only, |
| + bool wrap) const { |
| + FrameTreeNode* node = |
| + static_cast<RenderFrameHostImpl*>(from_rfh)->frame_tree_node(); |
| + |
| + while ((node = TraverseNode(node, forward, wrap)) != nullptr) { |
| + if (!CheckFrame(node->current_frame_host())) |
| + continue; |
| + RenderFrameHost* current_rfh = node->current_frame_host(); |
| + if (!matches_only || matches_per_frame_.find(current_rfh)->second || |
| + pending_replies_.count(current_rfh)) { |
| + // Note that if there is still a pending reply expected for this frame, |
| + // then it may have unaccounted matches and will not be skipped via |
| + // |matches_only|. |
| + return node->current_frame_host(); |
| + } |
| + if (wrap && node->current_frame_host() == from_rfh) |
| + return nullptr; |
| + } |
| + |
| + return nullptr; |
| +} |
| + |
| +void FindRequestManager::AddFrame(RenderFrameHost* rfh) { |
| + if (!rfh || !rfh->IsRenderFrameLive()) |
| + return; |
| + |
| + // A frame that is already being searched should not be added again. |
| + DCHECK(!CheckFrame(rfh)); |
| + |
| + matches_per_frame_[rfh] = 0; |
| + |
| + FindRequest request = current_request_; |
| + request.id = current_session_id_; |
| + request.options.findNext = false; |
| + SendFindIPC(request, rfh); |
| +} |
| + |
| +bool FindRequestManager::CheckFrame(RenderFrameHost* rfh) const { |
| + return rfh && rfh->IsRenderFrameLive() && matches_per_frame_.count(rfh); |
| +} |
| + |
| +void FindRequestManager::UpdateActiveMatchOrdinal() { |
| + active_match_ordinal_ = 0; |
| + |
| + if (!active_frame_ || !relative_active_match_ordinal_) { |
| + DCHECK(!active_frame_ && !relative_active_match_ordinal_); |
| + return; |
| + } |
| + |
| + // Traverse the frame tree backwards (in search order) and count all of the |
| + // matches in frames before the frame with the active match, in order to |
| + // determine the overall active match ordinal. |
| + RenderFrameHost* frame = active_frame_; |
| + while ((frame = Traverse(frame, |
| + false /* forward */, |
| + true /* matches_only */, |
| + false /* wrap */))) { |
| + active_match_ordinal_ += matches_per_frame_[frame]; |
| + } |
| + active_match_ordinal_ += relative_active_match_ordinal_; |
| +} |
| + |
| +void FindRequestManager::FinalUpdate(int request_id, RenderFrameHost* rfh) { |
| + if (!number_of_matches_ || |
| + !pending_active_match_ordinal_ || |
| + request_id != current_request_.id) { |
| + NotifyFindReply(request_id, true /* final_update */); |
| + AdvanceQueue(request_id); |
| + return; |
| + } |
| + |
| + // There are matches, but no active match was returned, so another find next |
| + // request must be sent. |
| + |
| + RenderFrameHost* target_rfh; |
| + if (current_request_.options.findNext) { |
| + // If this was a find next operation, then the active match will be in the |
| + // next frame with matches after this one. |
| + target_rfh = Traverse(rfh, |
| + current_request_.options.forward, |
| + true /* matches_only */, |
| + true /* wrap */); |
| + } else if ((target_rfh = contents_->GetFocusedFrame()) != nullptr) { |
| + // Otherwise, if there is a focused frame, then the active match will be in |
| + // the next frame with matches after that one. |
| + target_rfh = Traverse(target_rfh, |
| + current_request_.options.forward, |
| + true /* matches_only */, |
| + true /* wrap */); |
| + } else { |
| + // Otherwise, the first frame with matches will have the active match. |
| + target_rfh = GetInitialFrame(current_request_.options.forward); |
| + if (!CheckFrame(target_rfh) || !matches_per_frame_[target_rfh]) { |
| + target_rfh = Traverse(target_rfh, |
| + current_request_.options.forward, |
| + true /* matches_only */, |
| + false /* wrap */); |
| + } |
| + } |
| + DCHECK(target_rfh); |
| + |
| + // Forward the find reply without |final_update| set because the active match |
| + // has not yet been found. |
| + NotifyFindReply(request_id, false /* final_update */); |
| + |
| + current_request_.options.findNext = true; |
| + SendFindIPC(current_request_, target_rfh); |
| +} |
| + |
| #if defined(OS_ANDROID) |
| -void FindRequestManager::SendFindMatchRectsIPC(RenderFrameHost* rfh) { |
| - rfh->Send(new FrameMsg_FindMatchRects(rfh->GetRoutingID(), |
| - match_rects_.request_version)); |
| +void FindRequestManager::RemoveNearestFindResultPendingReply( |
| + RenderFrameHost* rfh) { |
| + auto it = activate_.pending_replies.find(rfh); |
| + if (it == activate_.pending_replies.end()) |
| + return; |
| + |
| + activate_.pending_replies.erase(it); |
| + if (activate_.pending_replies.empty() && |
| + CheckFrame(activate_.nearest_frame)) { |
| + activate_.nearest_frame->Send(new FrameMsg_ActivateNearestFindResult( |
| + activate_.nearest_frame->GetRoutingID(), |
| + current_session_id_, activate_.x, activate_.y)); |
| + } |
| +} |
| + |
| +void FindRequestManager::RemoveFindMatchRectsPendingReply( |
| + RenderFrameHost* rfh) { |
| + auto it = match_rects_.pending_replies.find(rfh); |
| + if (it == match_rects_.pending_replies.end()) |
| + return; |
| + |
| + match_rects_.pending_replies.erase(it); |
| + if (match_rects_.pending_replies.empty()) { |
| + // All replies are in. |
| + std::vector<gfx::RectF> aggregate_rects; |
| + if (match_rects_.request_version != match_rects_.known_version) { |
| + // Request version is stale, so aggregate and report the newer find |
| + // match rects. The rects should be aggregated in search order. |
| + for (RenderFrameHost* frame = GetInitialFrame(true /* forward */); frame; |
| + frame = Traverse(frame, |
| + true /* forward */, |
| + true /* matches_only */, |
| + false /* wrap */)) { |
| + auto it = match_rects_.frame_rects.find(frame); |
| + if (it == match_rects_.frame_rects.end()) |
| + continue; |
| + |
| + std::vector<gfx::RectF>& frame_rects = it->second.rects; |
| + aggregate_rects.insert( |
| + aggregate_rects.end(), frame_rects.begin(), frame_rects.end()); |
| + } |
| + } |
| + contents_->NotifyFindMatchRectsReply( |
| + match_rects_.known_version, aggregate_rects, match_rects_.active_rect); |
| + } |
| } |
| #endif |