Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(217)

Unified Diff: content/browser/find_request_manager.cc

Issue 1959183002: Multi-Process Find-in-Page. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Disabled tests on Android Release because of crbug.com/615291. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/find_request_manager.h ('k') | content/browser/find_request_manager_browsertest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..ba97753698a77a8e8f00fe80181909fb083d17a5 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) {
+ 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_.frame_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 */)) != nullptr) {
+ 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
« no previous file with comments | « content/browser/find_request_manager.h ('k') | content/browser/find_request_manager_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698