blob: de2783cb1f120e893e44dbb71c905fbde7678824 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/find_request_manager.h"
#include <algorithm>
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/frame_messages.h"
#include "content/common/input_messages.h"
#include "content/public/browser/guest_mode.h"
namespace content {
namespace {
// The following functions allow traversal over all frames, including those
// across WebContentses.
//
// Note that there are currently two different ways in which an inner
// WebContents may be embedded in an outer WebContents:
//
// 1) As a guest of the outer WebContents's BrowserPluginEmbedder.
// 2) Within an inner WebContentsTreeNode of the outer WebContents's
// WebContentsTreeNode.
// Returns all child frames of |node|.
std::vector<FrameTreeNode*> GetChildren(FrameTreeNode* node) {
std::vector<FrameTreeNode*> children(node->child_count());
for (size_t i = 0; i != node->child_count(); ++i)
children[i] = node->child_at(i);
if (auto* inner_contents = WebContentsImpl::FromOuterFrameTreeNode(node)) {
children.push_back(inner_contents->GetMainFrame()->frame_tree_node());
} else {
auto* contents = WebContentsImpl::FromFrameTreeNode(node);
if (node->IsMainFrame() && contents->GetBrowserPluginEmbedder()) {
for (auto* inner_contents : contents->GetInnerWebContents()) {
children.push_back(inner_contents->GetMainFrame()->frame_tree_node());
}
}
}
return children;
}
// Returns the first child FrameTreeNode under |node|, if |node| has a child, or
// nullptr otherwise.
FrameTreeNode* GetFirstChild(FrameTreeNode* node) {
auto children = GetChildren(node);
if (!children.empty())
return children.front();
return nullptr;
}
// Returns the last child FrameTreeNode under |node|, if |node| has a child, or
// nullptr otherwise.
FrameTreeNode* GetLastChild(FrameTreeNode* node) {
auto children = GetChildren(node);
if (!children.empty())
return children.back();
return nullptr;
}
// Returns the deepest last child frame under |node|/|rfh| in the frame tree.
FrameTreeNode* GetDeepestLastChild(FrameTreeNode* node) {
while (FrameTreeNode* last_child = GetLastChild(node))
node = last_child;
return node;
}
RenderFrameHost* GetDeepestLastChild(RenderFrameHost* rfh) {
FrameTreeNode* node =
static_cast<RenderFrameHostImpl*>(rfh)->frame_tree_node();
return GetDeepestLastChild(node)->current_frame_host();
}
// Returns the parent FrameTreeNode of |node|, if |node| has a parent, or
// nullptr otherwise.
FrameTreeNode* GetParent(FrameTreeNode* node) {
if (node->parent())
return node->parent();
// The parent frame may be in another WebContents.
if (node->IsMainFrame()) {
auto* contents = WebContentsImpl::FromFrameTreeNode(node);
if (GuestMode::IsCrossProcessFrameGuest(contents)) {
int node_id = contents->GetOuterDelegateFrameTreeNodeId();
if (node_id != FrameTreeNode::kFrameTreeNodeInvalidId)
return FrameTreeNode::GloballyFindByID(node_id);
} else if (auto* outer_contents = contents->GetOuterWebContents()) {
return outer_contents->GetMainFrame()->frame_tree_node();
}
}
return nullptr;
}
// Returns the previous sibling FrameTreeNode of |node|, if one exists, or
// nullptr otherwise.
FrameTreeNode* GetPreviousSibling(FrameTreeNode* node) {
if (FrameTreeNode* previous_sibling = node->PreviousSibling())
return previous_sibling;
// The previous sibling may be in another WebContents.
if (FrameTreeNode* parent = GetParent(node)) {
auto children = GetChildren(parent);
auto it = std::find(children.begin(), children.end(), node);
// It is odd that this node may not be a child of its parent, but this is
// actually possible during teardown, hence the need for the check for
// "it != children.end()".
if (it != children.end() && it != children.begin())
return *--it;
}
return nullptr;
}
// Returns the next sibling FrameTreeNode of |node|, if one exists, or nullptr
// otherwise.
FrameTreeNode* GetNextSibling(FrameTreeNode* node) {
if (FrameTreeNode* next_sibling = node->NextSibling())
return next_sibling;
// The next sibling may be in another WebContents.
if (FrameTreeNode* parent = GetParent(node)) {
auto children = GetChildren(parent);
auto it = std::find(children.begin(), children.end(), node);
// It is odd that this node may not be a child of its parent, but this is
// actually possible during teardown, hence the need for the check for
// "it != children.end()".
if (it != children.end() && ++it != children.end())
return *it;
}
return nullptr;
}
// 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 (FrameTreeNode* first_child = GetFirstChild(node))
return first_child;
FrameTreeNode* sibling = GetNextSibling(node);
while (!sibling) {
FrameTreeNode* parent = GetParent(node);
if (!parent)
return wrap ? node : nullptr;
node = parent;
sibling = GetNextSibling(node);
}
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 = GetPreviousSibling(node))
return GetDeepestLastChild(previous_sibling);
if (FrameTreeNode* parent = GetParent(node))
return 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
// Observes searched WebContentses for frame changes, including deletion,
// creation, and navigation.
class FindRequestManager::FrameObserver : public WebContentsObserver {
public:
FrameObserver(WebContentsImpl* web_contents, FindRequestManager* manager)
: WebContentsObserver(web_contents), manager_(manager) {}
~FrameObserver() override {}
void DidFinishLoad(RenderFrameHost* rfh, const GURL& validated_url) override {
if (manager_->current_session_id_ == kInvalidId)
return;
manager_->RemoveFrame(rfh);
manager_->AddFrame(rfh, true /* force */);
}
void RenderFrameDeleted(RenderFrameHost* rfh) override {
manager_->RemoveFrame(rfh);
}
void RenderFrameHostChanged(RenderFrameHost* old_host,
RenderFrameHost* new_host) override {
manager_->RemoveFrame(old_host);
}
void FrameDeleted(RenderFrameHost* rfh) override {
manager_->RemoveFrame(rfh);
}
private:
// The FindRequestManager that owns this FrameObserver.
FindRequestManager* const manager_;
DISALLOW_COPY_AND_ASSIGN(FrameObserver);
};
#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),
current_session_id_(kInvalidId),
pending_find_next_reply_(nullptr),
pending_active_match_ordinal_(false),
number_of_matches_(0),
active_frame_(nullptr),
relative_active_match_ordinal_(0),
active_match_ordinal_(0),
last_reported_id_(kInvalidId) {}
FindRequestManager::~FindRequestManager() {}
void FindRequestManager::Find(int request_id,
const base::string16& search_text,
const blink::WebFindOptions& options) {
// Every find request must have a unique ID, and these IDs must strictly
// increase so that newer requests always have greater IDs than older
// requests.
DCHECK_GT(request_id, current_request_.id);
DCHECK_GT(request_id, current_session_id_);
// If this is a new find session, clear any queued requests from last session.
if (!options.find_next)
find_request_queue_ = std::queue<FindRequest>();
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) {
for (WebContentsImpl* contents : contents_->GetWebContentsAndAllInner()) {
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,
int request_id,
int number_of_matches,
const gfx::Rect& selection_rect,
int active_match_ordinal,
bool final_update) {
// Ignore stale replies from abandoned find sessions or dead frames.
if (current_session_id_ == kInvalidId ||
request_id < current_session_id_ ||
!CheckFrame(rfh)) {
return;
}
// Update the stored find results.
DCHECK_GE(number_of_matches, -1);
DCHECK_GE(active_match_ordinal, -1);
// Check for an update to the number of matches.
if (number_of_matches != -1) {
DCHECK_GE(number_of_matches, 0);
auto matches_per_frame_it = matches_per_frame_.find(rfh);
if (int matches_delta = number_of_matches - matches_per_frame_it->second) {
// Increment the global number of matches by the number of additional
// matches found for this frame.
number_of_matches_ += matches_delta;
matches_per_frame_it->second = number_of_matches;
// All matches may have been removed since the last find reply.
if (rfh == active_frame_ && !number_of_matches)
relative_active_match_ordinal_ = 0;
// The active match ordinal may need updating since the number of matches
// before the active match may have changed.
if (rfh != active_frame_ || !number_of_matches)
UpdateActiveMatchOrdinal();
}
}
// 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);
}
if (!final_update) {
NotifyFindReply(request_id, false /* final_update */);
return;
}
// This is the final update for this frame for the current find operation.
pending_initial_replies_.erase(rfh);
if (request_id == current_session_id_ && !pending_initial_replies_.empty()) {
NotifyFindReply(request_id, false /* final_update */);
return;
}
// This is the final update for the current find operation.
if (request_id == current_request_.id && request_id != current_session_id_) {
DCHECK(current_request_.options.find_next);
DCHECK_EQ(pending_find_next_reply_, rfh);
pending_find_next_reply_ = nullptr;
}
FinalUpdateReceived(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;
selection_rect_ = gfx::Rect();
}
UpdateActiveMatchOrdinal();
#if defined(OS_ANDROID)
// 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 no pending find replies are expected for the removed frame, then just
// report the updated results.
if (!pending_initial_replies_.count(rfh) && pending_find_next_reply_ != rfh) {
bool final_update =
pending_initial_replies_.empty() && !pending_find_next_reply_;
NotifyFindReply(current_session_id_, final_update);
return;
}
if (pending_initial_replies_.count(rfh)) {
// A reply should not be expected from the removed frame.
pending_initial_replies_.erase(rfh);
if (pending_initial_replies_.empty()) {
FinalUpdateReceived(current_session_id_, rfh);
}
}
if (pending_find_next_reply_ == rfh) {
// A reply should not be expected from the removed frame.
pending_find_next_reply_ = nullptr;
FinalUpdateReceived(current_request_.id, rfh);
}
}
#if defined(OS_ANDROID)
void FindRequestManager::ActivateNearestFindResult(float x, float y) {
if (current_session_id_ == kInvalidId)
return;
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 (WebContentsImpl* contents : contents_->GetWebContentsAndAllInner()) {
for (FrameTreeNode* node : contents->GetFrameTree()->Nodes()) {
RenderFrameHost* rfh = node->current_frame_host();
if (!CheckFrame(rfh) || !rfh->IsRenderFrameLive())
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;
// Request the latest find match rects from each frame.
for (WebContentsImpl* contents : contents_->GetWebContentsAndAllInner()) {
for (FrameTreeNode* node : contents->GetFrameTree()->Nodes()) {
RenderFrameHost* rfh = node->current_frame_host();
if (!CheckFrame(rfh) || !rfh->IsRenderFrameLive())
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(
RenderFrameHost* rfh,
int version,
const std::vector<gfx::RectF>& rects,
const gfx::RectF& 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::Reset(const FindRequest& initial_request) {
current_session_id_ = initial_request.id;
current_request_ = initial_request;
pending_initial_replies_.clear();
pending_find_next_reply_ = nullptr;
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();
last_reported_id_ = kInvalidId;
frame_observers_.clear();
#if defined(OS_ANDROID)
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.find_next) {
// 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_->GetFocusedWebContents()->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 (WebContentsImpl* contents : contents_->GetWebContentsAndAllInner()) {
frame_observers_.push_back(base::MakeUnique<FrameObserver>(contents, this));
for (FrameTreeNode* node : contents->GetFrameTree()->Nodes()) {
AddFrame(node->current_frame_host(), false /* force */);
}
}
}
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) {
DCHECK(CheckFrame(rfh));
DCHECK(rfh->IsRenderFrameLive());
if (request.options.find_next)
pending_find_next_reply_ = rfh;
else
pending_initial_replies_.insert(rfh);
rfh->Send(new FrameMsg_Find(rfh->GetRoutingID(), request.id,
request.search_text, request.options));
}
void FindRequestManager::NotifyFindReply(int request_id, bool final_update) {
if (request_id == kInvalidId) {
NOTREACHED();
return;
}
// Ensure that replies are not reported with IDs lower than the ID of the
// latest request we have results from.
if (request_id < last_reported_id_)
request_id = last_reported_id_;
else
last_reported_id_ = request_id;
contents_->NotifyFindReply(request_id, number_of_matches_, selection_rect_,
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 {
DCHECK(from_rfh);
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_initial_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, bool force) {
if (!rfh || !rfh->IsRenderFrameLive())
return;
// A frame that is already being searched should not normally be added again.
DCHECK(force || !CheckFrame(rfh));
matches_per_frame_[rfh] = 0;
FindRequest request = current_request_;
request.id = current_session_id_;
request.options.find_next = false;
request.options.force = force;
SendFindIPC(request, rfh);
}
bool FindRequestManager::CheckFrame(RenderFrameHost* rfh) const {
return rfh && 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::FinalUpdateReceived(int request_id,
RenderFrameHost* rfh) {
if (!number_of_matches_ ||
(active_match_ordinal_ && !pending_active_match_ordinal_) ||
pending_find_next_reply_) {
// All the find results for |request_id| are in and ready to report. Note
// that |final_update| will be set to false if there are still pending
// replies expected from the initial find request.
NotifyFindReply(request_id,
pending_initial_replies_.empty() /* 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 (request_id == current_request_.id && current_request_.options.find_next) {
// 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_->GetFocusedWebContents()->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.find_next = true;
SendFindIPC(current_request_, target_rfh);
}
#if defined(OS_ANDROID)
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
} // namespace content