#include "third_party/blink/renderer/core/editing/finder/text_finder.h"
#include "components/ukm/test_ukm_recorder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/core/dom/comment.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/node_list.h"
#include "third_party/blink/renderer/core/dom/range.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/editing/finder/find_in_page_coordinates.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
#include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/layout/text_autosizer.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/script/classic_script.h"
#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/testing/task_environment.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
class TextFinderTest : public testing::Test { … };
class TextFinderSimTest : public SimTest { … };
v8::Local<v8::Value> TextFinderTest::EvalJs(const std::string& script) { … }
Document& TextFinderTest::GetDocument() const { … }
TextFinder& TextFinderTest::GetTextFinder() const { … }
gfx::RectF TextFinderTest::FindInPageRect(Node* start_container,
int start_offset,
Node* end_container,
int end_offset) { … }
TEST_F(TextFinderTest, FindTextSimple) { … }
TEST_F(TextFinderTest, FindTextAutosizing) { … }
TEST_F(TextFinderTest, FindTextNotFound) { … }
TEST_F(TextFinderTest, FindTextInShadowDOM) { … }
#if BUILDFLAG(IS_ANDROID)
TEST_F(TextFinderTest, ScopeTextMatchesSimple) {
GetDocument().body()->setInnerHTML("XXXXFindMeYYYYfindmeZZZZ");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
Node* text_node = GetDocument().body()->firstChild();
int identifier = 0;
WebString search_text(String("FindMe"));
auto find_options =
mojom::blink::FindOptions::New();
find_options->run_synchronously_for_testing = true;
GetTextFinder().ResetMatchCount();
GetTextFinder().StartScopingStringMatches(identifier, search_text,
*find_options);
EXPECT_EQ(2, GetTextFinder().TotalMatchCount());
WebVector<gfx::RectF> match_rects = GetTextFinder().FindMatchRects();
ASSERT_EQ(2u, match_rects.size());
EXPECT_EQ(FindInPageRect(text_node, 4, text_node, 10), match_rects[0]);
EXPECT_EQ(FindInPageRect(text_node, 14, text_node, 20), match_rects[1]);
GetDocument().body()->setAttribute(html_names::kStyleAttr,
AtomicString("margin: 2000px"));
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
match_rects = GetTextFinder().FindMatchRects();
ASSERT_EQ(2u, match_rects.size());
EXPECT_EQ(FindInPageRect(text_node, 4, text_node, 10), match_rects[0]);
EXPECT_EQ(FindInPageRect(text_node, 14, text_node, 20), match_rects[1]);
}
TEST_F(TextFinderTest, ScopeTextMatchesRepeated) {
GetDocument().body()->setInnerHTML("XXXXFindMeYYYYfindmeZZZZ");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
Node* text_node = GetDocument().body()->firstChild();
int identifier = 0;
WebString search_text1(String("XFindMe"));
WebString search_text2(String("FindMe"));
auto find_options =
mojom::blink::FindOptions::New();
find_options->run_synchronously_for_testing = true;
GetTextFinder().ResetMatchCount();
GetTextFinder().StartScopingStringMatches(identifier, search_text1,
*find_options);
GetTextFinder().StartScopingStringMatches(identifier, search_text2,
*find_options);
EXPECT_EQ(2, GetTextFinder().TotalMatchCount());
WebVector<gfx::RectF> match_rects = GetTextFinder().FindMatchRects();
ASSERT_EQ(2u, match_rects.size());
EXPECT_EQ(FindInPageRect(text_node, 4, text_node, 10), match_rects[0]);
EXPECT_EQ(FindInPageRect(text_node, 14, text_node, 20), match_rects[1]);
}
TEST_F(TextFinderTest, ScopeTextMatchesWithShadowDOM) {
GetDocument().body()->setInnerHTML("<b>FOO</b><i slot='bar'>foo</i>");
ShadowRoot& shadow_root =
GetDocument().body()->AttachShadowRootForTesting(ShadowRootMode::kOpen);
shadow_root.setInnerHTML("<slot name='bar'></slot><u>Foo</u><slot></slot>");
Node* text_in_b_element = GetDocument().body()->firstChild()->firstChild();
Node* text_in_i_element = GetDocument().body()->lastChild()->firstChild();
Node* text_in_u_element = shadow_root.childNodes()->item(1)->firstChild();
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
int identifier = 0;
WebString search_text(String("fOO"));
auto find_options =
mojom::blink::FindOptions::New();
find_options->run_synchronously_for_testing = true;
GetTextFinder().ResetMatchCount();
GetTextFinder().StartScopingStringMatches(identifier, search_text,
*find_options);
EXPECT_EQ(3, GetTextFinder().TotalMatchCount());
WebVector<gfx::RectF> match_rects = GetTextFinder().FindMatchRects();
ASSERT_EQ(3u, match_rects.size());
EXPECT_EQ(FindInPageRect(text_in_i_element, 0, text_in_i_element, 3),
match_rects[0]);
EXPECT_EQ(FindInPageRect(text_in_u_element, 0, text_in_u_element, 3),
match_rects[1]);
EXPECT_EQ(FindInPageRect(text_in_b_element, 0, text_in_b_element, 3),
match_rects[2]);
}
TEST_F(TextFinderTest, ScopeRepeatPatternTextMatches) {
GetDocument().body()->setInnerHTML("ab ab ab ab ab");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
Node* text_node = GetDocument().body()->firstChild();
int identifier = 0;
WebString search_text(String("ab ab"));
auto find_options =
mojom::blink::FindOptions::New();
find_options->run_synchronously_for_testing = true;
GetTextFinder().ResetMatchCount();
GetTextFinder().StartScopingStringMatches(identifier, search_text,
*find_options);
EXPECT_EQ(2, GetTextFinder().TotalMatchCount());
WebVector<gfx::RectF> match_rects = GetTextFinder().FindMatchRects();
ASSERT_EQ(2u, match_rects.size());
EXPECT_EQ(FindInPageRect(text_node, 0, text_node, 5), match_rects[0]);
EXPECT_EQ(FindInPageRect(text_node, 6, text_node, 11), match_rects[1]);
}
TEST_F(TextFinderTest, OverlappingMatches) {
GetDocument().body()->setInnerHTML("aababaa");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
Node* text_node = GetDocument().body()->firstChild();
int identifier = 0;
WebString search_text(String("aba"));
auto find_options =
mojom::blink::FindOptions::New();
find_options->run_synchronously_for_testing = true;
GetTextFinder().ResetMatchCount();
GetTextFinder().StartScopingStringMatches(identifier, search_text,
*find_options);
EXPECT_EQ(1, GetTextFinder().TotalMatchCount());
WebVector<gfx::RectF> match_rects = GetTextFinder().FindMatchRects();
ASSERT_EQ(1u, match_rects.size());
EXPECT_EQ(FindInPageRect(text_node, 1, text_node, 4), match_rects[0]);
}
TEST_F(TextFinderTest, SequentialMatches) {
GetDocument().body()->setInnerHTML("ababab");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
Node* text_node = GetDocument().body()->firstChild();
int identifier = 0;
WebString search_text(String("ab"));
auto find_options =
mojom::blink::FindOptions::New();
find_options->run_synchronously_for_testing = true;
GetTextFinder().ResetMatchCount();
GetTextFinder().StartScopingStringMatches(identifier, search_text,
*find_options);
EXPECT_EQ(3, GetTextFinder().TotalMatchCount());
WebVector<gfx::RectF> match_rects = GetTextFinder().FindMatchRects();
ASSERT_EQ(3u, match_rects.size());
EXPECT_EQ(FindInPageRect(text_node, 0, text_node, 2), match_rects[0]);
EXPECT_EQ(FindInPageRect(text_node, 2, text_node, 4), match_rects[1]);
EXPECT_EQ(FindInPageRect(text_node, 4, text_node, 6), match_rects[2]);
}
TEST_F(TextFinderTest, FindTextJavaScriptUpdatesDOM) {
GetDocument().body()->setInnerHTML("<b>XXXXFindMeYYYY</b><i></i>");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
int identifier = 0;
WebString search_text(String("FindMe"));
auto find_options =
mojom::blink::FindOptions::New();
find_options->run_synchronously_for_testing = true;
bool wrap_within_frame = true;
bool active_now;
GetTextFinder().ResetMatchCount();
GetTextFinder().StartScopingStringMatches(identifier, search_text,
*find_options);
find_options->new_session = false;
ASSERT_TRUE(GetTextFinder().Find(identifier, search_text, *find_options,
wrap_within_frame, &active_now));
EXPECT_TRUE(active_now);
ASSERT_TRUE(GetTextFinder().Find(identifier, search_text, *find_options,
wrap_within_frame, &active_now));
EXPECT_TRUE(active_now);
auto* i_element = To<Element>(GetDocument().body()->lastChild());
ASSERT_TRUE(i_element);
i_element->setInnerHTML("ZZFindMe");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
ASSERT_TRUE(GetTextFinder().Find(identifier, search_text, *find_options,
wrap_within_frame, &active_now));
Range* active_match = GetTextFinder().ActiveMatch();
ASSERT_TRUE(active_match);
EXPECT_FALSE(active_now);
EXPECT_EQ(2u, active_match->startOffset());
EXPECT_EQ(8u, active_match->endOffset());
find_options->new_session = true;
GetTextFinder().ResetMatchCount();
GetTextFinder().CancelPendingScopingEffort();
GetTextFinder().StartScopingStringMatches(identifier, search_text,
*find_options);
EXPECT_EQ(2, GetTextFinder().TotalMatchCount());
WebVector<gfx::RectF> match_rects = GetTextFinder().FindMatchRects();
ASSERT_EQ(2u, match_rects.size());
Node* text_in_b_element = GetDocument().body()->firstChild()->firstChild();
Node* text_in_i_element = GetDocument().body()->lastChild()->firstChild();
EXPECT_EQ(FindInPageRect(text_in_b_element, 4, text_in_b_element, 10),
match_rects[0]);
EXPECT_EQ(FindInPageRect(text_in_i_element, 2, text_in_i_element, 8),
match_rects[1]);
}
TEST_F(TextFinderTest, FindTextJavaScriptUpdatesDOMAfterNoMatches) {
GetDocument().body()->setInnerHTML("<b>XXXXYYYY</b><i></i>");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
int identifier = 0;
WebString search_text(String("FindMe"));
auto find_options =
mojom::blink::FindOptions::New();
find_options->run_synchronously_for_testing = true;
bool wrap_within_frame = true;
bool active_now = false;
GetTextFinder().ResetMatchCount();
GetTextFinder().StartScopingStringMatches(identifier, search_text,
*find_options);
find_options->new_session = false;
ASSERT_FALSE(GetTextFinder().Find(identifier, search_text, *find_options,
wrap_within_frame, &active_now));
EXPECT_FALSE(active_now);
auto* i_element = To<Element>(GetDocument().body()->lastChild());
ASSERT_TRUE(i_element);
i_element->setInnerHTML("ZZFindMe");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
ASSERT_TRUE(GetTextFinder().Find(identifier, search_text, *find_options,
wrap_within_frame, &active_now));
Range* active_match = GetTextFinder().ActiveMatch();
ASSERT_TRUE(active_match);
EXPECT_FALSE(active_now);
EXPECT_EQ(2u, active_match->startOffset());
EXPECT_EQ(8u, active_match->endOffset());
find_options->new_session = true;
GetTextFinder().ResetMatchCount();
GetTextFinder().CancelPendingScopingEffort();
GetTextFinder().StartScopingStringMatches(identifier, search_text,
*find_options);
EXPECT_EQ(1, GetTextFinder().TotalMatchCount());
WebVector<gfx::RectF> match_rects = GetTextFinder().FindMatchRects();
ASSERT_EQ(1u, match_rects.size());
Node* text_in_i_element = GetDocument().body()->lastChild()->firstChild();
EXPECT_EQ(FindInPageRect(text_in_i_element, 2, text_in_i_element, 8),
match_rects[0]);
}
#endif
TEST_F(TextFinderTest, ScopeWithTimeouts) { … }
TEST_F(TextFinderTest, BeforeMatchEvent) { … }
TEST_F(TextFinderTest, BeforeMatchEventRemoveElement) { … }
TEST_F(TextFinderSimTest, BeforeMatchEventAsyncExpandHighlight) { … }
TEST_F(TextFinderSimTest, BeforeMatchExpandedHiddenMatchableUkm) { … }
TEST_F(TextFinderSimTest, BeforeMatchExpandedHiddenMatchableUseCounter) { … }
TEST_F(TextFinderSimTest,
BeforeMatchExpandedHiddenMatchableUseCounterNoHandler) { … }
TEST_F(TextFinderTest, FindTextAcrossCommentNode) { … }
TEST_F(TextFinderTest, CommentAfterDoucmentElement) { … }
}