#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "base/command_line.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "content/browser/find_in_page_client.h"
#include "content/browser/find_request_manager.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/find_test_utils.h"
#include "content/public/test/no_renderer_crashes_assertion.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "third_party/blink/public/mojom/page/widget.mojom-test-utils.h"
#include "url/origin.h"
#if BUILDFLAG(IS_ANDROID)
#include "ui/android/view_android.h"
#endif
namespace content {
namespace {
const int kInvalidId = …;
const url::Origin& GetOriginForFrameTreeNode(FrameTreeNode* node) { … }
#if BUILDFLAG(IS_ANDROID)
double GetFrameDeviceScaleFactor(const ToRenderFrameHost& adapter) {
return EvalJs(adapter, "window.devicePixelRatio;").ExtractDouble();
}
#endif
}
class FindRequestManagerTestBase : public ContentBrowserTest { … };
class FindRequestManagerTest : public FindRequestManagerTestBase,
public testing::WithParamInterface<bool> { … };
INSTANTIATE_TEST_SUITE_P(…);
#if BUILDFLAG(IS_ANDROID)
#define MAYBE …
#else
#define MAYBE(x) …
#endif
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(Basic)) { … }
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, FindInPage_Issue615291) { … }
bool ExecuteScriptAndExtractRect(FrameTreeNode* frame,
const std::string& script,
gfx::Rect* out) { … }
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, ScrollAndZoomIntoView) { … }
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(CharacterByCharacter)) { … }
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DISABLED_RapidFire) { … }
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DISABLED_RemoveFrame) { … }
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, RemoveMainFrame) { … }
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DISABLED_AddFrame) { … }
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest,
AddInprocessHiddenFrameDuringFind) { … }
IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(AddFrameAfterNoMatches)) { … }
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(NavigateFrame)) { … }
IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(HiddenFrame)) { … }
#if BUILDFLAG(IS_ANDROID) || \
(BUILDFLAG(IS_LINUX) && !defined(UNDEFINED_SANITIZER))
#define MAYBE_FindNewMatches …
#else
#define MAYBE_FindNewMatches …
#endif
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE_FindNewMatches) { … }
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_FindInPage_Issue627799 …
#else
#define MAYBE_FindInPage_Issue627799 …
#endif
IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE_FindInPage_Issue627799) { … }
IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, DetachFrameWithMatch) { … }
IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(FindInPage_Issue644448)) { … }
#if BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, EmptyActiveMatchRect) {
LoadAndWait("/find_in_page.html");
auto default_options = blink::mojom::FindOptions::New();
default_options->run_synchronously_for_testing = true;
Find("result 01", default_options.Clone());
delegate()->WaitForFinalReply();
EXPECT_EQ(1, delegate()->GetFindResults().number_of_matches);
contents()->RequestFindMatchRects(-1);
delegate()->WaitForMatchRects();
const std::vector<gfx::RectF>& rects = delegate()->find_match_rects();
EXPECT_EQ(rects[0], delegate()->active_match_rect());
Find("result 00", default_options.Clone());
delegate()->WaitForFinalReply();
EXPECT_EQ(1, delegate()->GetFindResults().number_of_matches);
contents()->RequestFindMatchRects(-1);
delegate()->WaitForMatchRects();
EXPECT_EQ(gfx::RectF(), delegate()->active_match_rect());
}
class MainFrameSizeChangedWaiter : public WebContentsObserver {
public:
MainFrameSizeChangedWaiter(WebContents* web_contents)
: WebContentsObserver(web_contents) {}
void Wait() { run_loop_.Run(); }
private:
void FrameSizeChanged(RenderFrameHost* render_frame_host,
const gfx::Size& frame_size) override {
if (render_frame_host->IsInPrimaryMainFrame())
run_loop_.Quit();
}
base::RunLoop run_loop_;
};
IN_PROC_BROWSER_TEST_F(FindRequestManagerTest,
RectsUpdateWhenMainFrameSizeChanged) {
LoadAndWait("/find_in_page.html");
const int kWidth = 1080;
const int kHeight = 1286;
gfx::Size size(kWidth, kHeight);
contents()->GetNativeView()->OnSizeChanged(kWidth, kHeight);
contents()->GetNativeView()->OnPhysicalBackingSizeChanged(size);
auto options = blink::mojom::FindOptions::New();
options->run_synchronously_for_testing = true;
Find("result", options->Clone());
delegate()->WaitForFinalReply();
EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches);
contents()->RequestFindMatchRects(-1);
delegate()->WaitForMatchRects();
const int kNewHeight = 2121;
size = gfx::Size(kWidth, kNewHeight);
contents()->GetNativeView()->OnSizeChanged(kWidth, kNewHeight);
contents()->GetNativeView()->OnPhysicalBackingSizeChanged(size);
MainFrameSizeChangedWaiter(contents()).Wait();
contents()->RequestFindMatchRects(-1);
delegate()->WaitForMatchRects();
std::vector<gfx::RectF> new_rects = delegate()->find_match_rects();
EXPECT_EQ(new_rects[0], delegate()->active_match_rect());
for (int i = 1; i < 19; i++) {
options->new_session = false;
options->forward = true;
Find("result", options->Clone());
delegate()->WaitForFinalReply();
EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches);
contents()->RequestFindMatchRects(-1);
delegate()->WaitForMatchRects();
new_rects = delegate()->find_match_rects();
EXPECT_EQ(new_rects[i], delegate()->active_match_rect());
}
}
IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(FindMatchRects)) {
LoadAndWait("/find_in_page.html");
if (test_with_oopif())
MakeChildFrameCrossProcess();
auto default_options = blink::mojom::FindOptions::New();
default_options->run_synchronously_for_testing = true;
Find("result", default_options.Clone());
delegate()->WaitForFinalReply();
EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches);
contents()->RequestFindMatchRects(-1);
delegate()->WaitForMatchRects();
const std::vector<gfx::RectF>& rects = delegate()->find_match_rects();
EXPECT_EQ(rects[0], delegate()->active_match_rect());
EXPECT_LT(rects[0].y(), rects[1].y());
for (int i = 2; i < 19; ++i) {
EXPECT_LT(rects[0].y(), rects[i].y());
EXPECT_GT(rects[1].y(), rects[i].y());
}
EXPECT_GT(rects[3].y(), rects[2].y());
EXPECT_GT(rects[3].y(), rects[4].y());
EXPECT_LT(rects[6].y(), rects[7].y());
EXPECT_LT(rects[7].y(), rects[8].y());
EXPECT_LT(rects[8].y(), rects[9].y());
EXPECT_GT(rects[11].y(), rects[10].y());
EXPECT_GT(rects[12].y(), rects[10].y());
EXPECT_GT(rects[13].y(), rects[10].y());
EXPECT_GT(rects[14].y(), rects[10].y());
EXPECT_LT(rects[11].y(), rects[15].y());
EXPECT_LT(rects[12].y(), rects[15].y());
EXPECT_LT(rects[13].y(), rects[15].y());
EXPECT_LT(rects[14].y(), rects[15].y());
EXPECT_LT(rects[11].y(), rects[12].y());
EXPECT_LT(rects[11].y(), rects[13].y());
EXPECT_LT(rects[11].y(), rects[14].y());
EXPECT_GT(rects[11].x(), rects[12].x());
EXPECT_GT(rects[11].x(), rects[13].x());
EXPECT_GT(rects[11].x(), rects[14].x());
EXPECT_LT(rects[12].x(), rects[11].x());
EXPECT_LT(rects[12].x(), rects[13].x());
EXPECT_LT(rects[12].x(), rects[14].x());
EXPECT_LT(rects[13].y(), rects[12].y());
EXPECT_LT(rects[12].y(), rects[14].y());
EXPECT_GT(rects[15].y(), rects[14].y());
EXPECT_GT(rects[17].y(), rects[18].y());
}
namespace {
class ZoomToFindInPageRectMessageFilter
: public blink::mojom::FrameWidgetHostInterceptorForTesting {
public:
ZoomToFindInPageRectMessageFilter(RenderWidgetHostImpl* rwhi)
: impl_(rwhi->frame_widget_host_receiver_for_testing().SwapImplForTesting(
this)),
widget_message_seen_(false) {}
ZoomToFindInPageRectMessageFilter(const ZoomToFindInPageRectMessageFilter&) =
delete;
ZoomToFindInPageRectMessageFilter& operator=(
const ZoomToFindInPageRectMessageFilter&) = delete;
~ZoomToFindInPageRectMessageFilter() override {}
blink::mojom::FrameWidgetHost* GetForwardingInterface() override {
return impl_;
}
void Reset() {
widget_rect_seen_ = gfx::Rect();
widget_message_seen_ = false;
}
void WaitForWidgetHostMessage() {
if (widget_message_seen_)
return;
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
gfx::Rect& widget_message_rect() { return widget_rect_seen_; }
private:
void ZoomToFindInPageRectInMainFrame(const gfx::Rect& rect_to_zoom) override {
widget_rect_seen_ = rect_to_zoom;
widget_message_seen_ = true;
if (!quit_closure_.is_null())
std::move(quit_closure_).Run();
}
raw_ptr<blink::mojom::FrameWidgetHost> impl_;
gfx::Rect widget_rect_seen_;
bool widget_message_seen_;
base::OnceClosure quit_closure_;
};
}
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest,
DISABLED_ActivateNearestFindMatch) {
LoadAndWait("/find_in_page.html");
if (test_with_oopif())
MakeChildFrameCrossProcess();
std::unique_ptr<ZoomToFindInPageRectMessageFilter> message_interceptor_child;
if (test_with_oopif()) {
message_interceptor_child =
std::make_unique<ZoomToFindInPageRectMessageFilter>(
first_child()->current_frame_host()->GetRenderWidgetHost());
}
auto default_options = blink::mojom::FindOptions::New();
default_options->run_synchronously_for_testing = true;
Find("result", default_options.Clone());
delegate()->WaitForFinalReply();
EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches);
auto* find_request_manager = contents()->GetFindRequestManagerForTesting();
contents()->RequestFindMatchRects(-1);
delegate()->WaitForMatchRects();
const std::vector<gfx::RectF>& rects = delegate()->find_match_rects();
double device_scale_factor = GetFrameDeviceScaleFactor(contents());
int order[19] =
{11, 13, 2, 0, 16, 5, 7, 10, 6, 1, 15, 14, 9, 17, 18, 3, 8, 12, 4};
for (int i = 0; i < 19; ++i) {
delegate()->MarkNextReply();
contents()->ActivateNearestFindResult(
rects[order[i]].CenterPoint().x(), rects[order[i]].CenterPoint().y());
delegate()->WaitForNextReply();
bool is_match_in_oopif = order[i] > 1 && test_with_oopif();
if (is_match_in_oopif) {
message_interceptor_child->WaitForWidgetHostMessage();
auto expected_rect = gfx::ScaleToEnclosingRect(
message_interceptor_child->widget_message_rect(),
1.f / device_scale_factor);
EXPECT_EQ(find_request_manager->GetSelectionRectForTesting(),
expected_rect);
message_interceptor_child->Reset();
}
EXPECT_EQ(order[i] + 1, delegate()->GetFindResults().active_match_ordinal);
}
}
#endif
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DISABLED_HistoryBackAndForth) { … }
class FindInPageDisabledForOriginBrowserClient
: public ContentBrowserTestContentBrowserClient { … };
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, FindInPageDisabledForOrigin) { … }
class FindTestWebContentsPrerenderingDelegate
: public FindTestWebContentsDelegate { … };
class FindRequestManagerPrerenderingTest : public FindRequestManagerTest { … };
IN_PROC_BROWSER_TEST_F(FindRequestManagerPrerenderingTest, Basic) { … }
class FindRequestManagerTestWithBFCache : public FindRequestManagerTest { … };
IN_PROC_BROWSER_TEST_F(FindRequestManagerTestWithBFCache, Basic) { … }
class WaitForFindTestWebContentsDelegate : public FindTestWebContentsDelegate { … };
class FindRequestManagerFencedFrameTest : public FindRequestManagerTest { … };
class NeverFinishFencedFrameFindInPageClient : public FindInPageClient { … };
static std::unique_ptr<FindInPageClient> CreateFencedFrameFindInPageClient(
FindRequestManager* find_request_manager,
RenderFrameHostImpl* rfh) { … }
IN_PROC_BROWSER_TEST_F(FindRequestManagerFencedFrameTest,
OnlyPrimaryMainFrameClearsFindRequestQueue) { … }
class NeverFinishSubframeFindInPageClient : public FindInPageClient { … };
class FindRequestManagerTestObserver : public WebContentsObserver { … };
static std::unique_ptr<FindInPageClient> CreateFindInPageClient(
FindRequestManager* find_request_manager,
RenderFrameHostImpl* rfh) { … }
enum class FrameSiteType { … };
enum class FrameTestType { … };
class FindRequestManagerTestWithTestConfig
: public FindRequestManagerTestBase,
public testing::WithParamInterface<
::testing::tuple<FrameSiteType, FrameTestType>> { … };
INSTANTIATE_TEST_SUITE_P(…);
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
BUILDFLAG(IS_ANDROID)
#define MAYBE_NavigateFrameDuringFind …
#else
#define MAYBE_NavigateFrameDuringFind …
#endif
IN_PROC_BROWSER_TEST_P(FindRequestManagerTestWithTestConfig,
MAYBE_NavigateFrameDuringFind) { … }
IN_PROC_BROWSER_TEST_F(FindRequestManagerTestWithBFCache,
NavigateFrameDuringFind) { … }
IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, CrashDuringFind) { … }
IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DelayThenStop) { … }
}