chromium/content/browser/find_request_manager.cc

// Copyright 2016 The Chromium Authors
// 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 <utility>

#include "base/containers/contains.h"
#include "base/containers/queue.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#include "content/browser/find_in_page_client.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_client.h"

namespace content {

namespace {

// The following functions allow traversal over all RenderFrameHosts, including
// those across WebContentses.
//
// An inner WebContents may be embedded in an outer WebContents via an inner
// WebContentsTreeNode of the outer WebContents's WebContentsTreeNode.
std::vector<RenderFrameHostImpl*> GetChildren(RenderFrameHostImpl* rfh) {}

// Returns the first child RenderFrameHostImpl under |rfh|, if |rfh| has a
// child, or nullptr otherwise.
RenderFrameHostImpl* GetFirstChild(RenderFrameHostImpl* rfh) {}

// Returns the last child RenderFrameHostImpl under |rfh|, if |rfh| has a
// child, or nullptr otherwise.
RenderFrameHostImpl* GetLastChild(RenderFrameHostImpl* rfh) {}

// Returns the deepest last child frame under |rfh| in the frame tree.
RenderFrameHostImpl* GetDeepestLastChild(RenderFrameHostImpl* rfh) {}

// Returns the parent RenderFrameHost of |rfh|, if |rfh| has a parent, or
// nullptr otherwise.
RenderFrameHostImpl* GetAncestor(RenderFrameHostImpl* rfh) {}

// Returns the previous sibling RenderFrameHostImpl of |rfh|, if one exists,
// or nullptr otherwise.
RenderFrameHostImpl* GetPreviousSibling(RenderFrameHostImpl* rfh) {}

// Returns the next sibling RenderFrameHostImpl of |rfh|, if one exists, or
// nullptr otherwise.
RenderFrameHostImpl* GetNextSibling(RenderFrameHostImpl* rfh) {}

// Returns the RenderFrameHostImpl directly after |rfh| in the rfh 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().
RenderFrameHostImpl* TraverseNext(RenderFrameHostImpl* rfh, bool wrap) {}

// Returns the RenderFrameHostImpl directly before |rfh| 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().
RenderFrameHostImpl* TraversePrevious(RenderFrameHostImpl* rfh, bool wrap) {}

// The same as either TraverseNext() or TraversePrevious(), depending on
// |forward|.
RenderFrameHostImpl* TraverseFrame(RenderFrameHostImpl* rfh,
                                   bool forward,
                                   bool wrap) {}

bool IsFindInPageDisabled(RenderFrameHost* rfh) {}

bool IsUnattachedGuestView(RenderFrameHost* rfh) {}

// kMinKeystrokesWithoutDelay should be high enough that script in the page
// can't provide every possible search result at the same time.
constexpr int kMinKeystrokesWithoutDelay =;

// The delay for very short queries, before sending find requests. This should
// be higher than the duration in between two keystrokes. This is based on
// WebCore.FindInPage.DurationBetweenKeystrokes metrics, this is higher than
// 90% of them.
constexpr int kDelayMs =;

}  // namespace

// Observes searched WebContentses for RenderFrameHost state updates, including
// deletion and loads.
class FindRequestManager::FrameObserver : public WebContentsObserver {};

bool FindRequestManager::RunDelayedFindTaskForTesting() {}

FindRequestManager::FindRequest::FindRequest() = default;

FindRequestManager::FindRequest::FindRequest(
    int id,
    const std::u16string& search_text,
    blink::mojom::FindOptionsPtr options)
    :{}

FindRequestManager::FindRequest::FindRequest(const FindRequest& request)
    :{}

FindRequestManager::FindRequest::~FindRequest() = default;

FindRequestManager::FindRequest& FindRequestManager::FindRequest::operator=(
    const FindRequest& request) {}

#if BUILDFLAG(IS_ANDROID)
FindRequestManager::ActivateNearestFindResultState::
ActivateNearestFindResultState() = default;
FindRequestManager::ActivateNearestFindResultState::
    ActivateNearestFindResultState(float x, float y)
    : current_request_id(GetNextID()), point(x, y) {}
FindRequestManager::ActivateNearestFindResultState::
    ~ActivateNearestFindResultState() = default;

FindRequestManager::FrameRects::FrameRects() = default;
FindRequestManager::FrameRects::FrameRects(const std::vector<gfx::RectF>& rects,
                                           int version)
    : rects(rects), version(version) {}
FindRequestManager::FrameRects::~FrameRects() = default;

FindRequestManager::FindMatchRectsState::FindMatchRectsState() = default;
FindRequestManager::FindMatchRectsState::~FindMatchRectsState() = default;
#endif

// static
const int FindRequestManager::kInvalidId =;

FindRequestManager::FindRequestManager(WebContentsImpl* web_contents)
    :{}

FindRequestManager::~FindRequestManager() = default;

void FindRequestManager::Find(int request_id,
                              const std::u16string& search_text,
                              blink::mojom::FindOptionsPtr options,
                              bool skip_delay) {}

void FindRequestManager::EmitFindRequest(int request_id,
                                         const std::u16string& search_text,
                                         blink::mojom::FindOptionsPtr options) {}

void FindRequestManager::ForEachAddedFindInPageRenderFrameHost(
    base::FunctionRef<void(RenderFrameHostImpl*)> func_ref) {}

void FindRequestManager::StopFinding(StopFindAction action) {}

bool FindRequestManager::ShouldIgnoreReply(RenderFrameHostImpl* rfh,
                                           int request_id) {}

void FindRequestManager::HandleFinalUpdateForFrame(RenderFrameHostImpl* rfh,
                                                   int request_id) {}

void FindRequestManager::UpdatedFrameNumberOfMatches(RenderFrameHostImpl* rfh,
                                                     unsigned int old_count,
                                                     unsigned int new_count) {}

void FindRequestManager::SetActiveMatchRect(
    const gfx::Rect& active_match_rect) {}

void FindRequestManager::SetActiveMatchOrdinal(RenderFrameHostImpl* rfh,
                                               int request_id,
                                               int active_match_ordinal) {}

void FindRequestManager::RemoveFrame(RenderFrameHost* rfh) {}

void FindRequestManager::ClearActiveFindMatch() {}

#if BUILDFLAG(IS_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.
  ForEachAddedFindInPageRenderFrameHost([this](RenderFrameHostImpl* rfh) {
    activate_.pending_replies.insert(rfh);
    // Lifetime of FindRequestManager > RenderFrameHost > Mojo
    // connection, so it's safe to bind |this| and |rfh|.
    rfh->GetFindInPage()->GetNearestFindResult(
        activate_.point,
        base::BindOnce(&FindRequestManager::OnGetNearestFindResultReply,
                       base::Unretained(this), rfh,
                       activate_.current_request_id));
  });
}

void FindRequestManager::OnGetNearestFindResultReply(RenderFrameHostImpl* rfh,
                                                     int request_id,
                                                     float distance) {
  if (request_id != activate_.current_request_id ||
      !base::Contains(activate_.pending_replies, 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;
  match_rects_.active_rect = gfx::RectF();

  // Request the latest find match rects from each frame.
  ForEachAddedFindInPageRenderFrameHost([this](RenderFrameHostImpl* rfh) {
    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;
    // Lifetime of FindRequestManager > RenderFrameHost > Mojo
    // connection, so it's safe to bind |this| and |rfh|.
    rfh->GetFindInPage()->FindMatchRects(
        version, base::BindOnce(&FindRequestManager::OnFindMatchRectsReply,
                                base::Unretained(this), rfh));
  });
}

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) {}

void FindRequestManager::FindInternal(const FindRequest& request) {}

void FindRequestManager::AdvanceQueue(int request_id) {}

void FindRequestManager::SendFindRequest(const FindRequest& request,
                                         RenderFrameHost* rfh) {}

void FindRequestManager::NotifyFindReply(int request_id, bool final_update) {}

RenderFrameHost* FindRequestManager::GetInitialFrame(bool forward) const {}

RenderFrameHost* FindRequestManager::Traverse(RenderFrameHost* from_rfh,
                                              bool forward,
                                              bool matches_only,
                                              bool wrap) const {}

void FindRequestManager::AddFrame(RenderFrameHost* rfh, bool force) {}

bool FindRequestManager::CheckFrame(RenderFrameHost* rfh) const {}

void FindRequestManager::UpdateActiveMatchOrdinal() {}

void FindRequestManager::FinalUpdateReceived(int request_id,
                                             RenderFrameHost* rfh) {}

std::unique_ptr<FindInPageClient> FindRequestManager::CreateFindInPageClient(
    RenderFrameHostImpl* rfh) {}

#if BUILDFLAG(IS_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)) {
    const auto client_it = find_in_page_clients_.find(activate_.nearest_frame);
    if (client_it != find_in_page_clients_.end())
      client_it->second->ActivateNearestFindResult(current_session_id_,
                                                   activate_.point);
  }
}

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())
    return;

  // 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 frame_it = match_rects_.frame_rects.find(frame);
      if (frame_it == match_rects_.frame_rects.end())
        continue;

      std::vector<gfx::RectF>& frame_rects = frame_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  // BUILDFLAG(IS_ANDROID)

}  // namespace content