find_in_page_manger_impl_unittest.mm 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #import "ios/web/find_in_page/find_in_page_manager_impl.h"
  5. #include "base/run_loop.h"
  6. #import "base/test/ios/wait_util.h"
  7. #include "base/test/metrics/user_action_tester.h"
  8. #include "base/values.h"
  9. #import "ios/web/find_in_page/find_in_page_constants.h"
  10. #import "ios/web/find_in_page/find_in_page_java_script_feature.h"
  11. #import "ios/web/js_messaging/java_script_feature_manager.h"
  12. #import "ios/web/public/js_messaging/web_frames_manager.h"
  13. #import "ios/web/public/test/fakes/fake_find_in_page_manager_delegate.h"
  14. #import "ios/web/public/test/fakes/fake_web_client.h"
  15. #import "ios/web/public/test/fakes/fake_web_frame.h"
  16. #import "ios/web/public/test/fakes/fake_web_frames_manager.h"
  17. #import "ios/web/public/test/fakes/fake_web_state.h"
  18. #include "ios/web/public/test/web_test.h"
  19. #include "testing/gtest/include/gtest/gtest.h"
  20. #if !defined(__has_feature) || !__has_feature(objc_arc)
  21. #error "This file requires ARC support."
  22. #endif
  23. using base::test::ios::kWaitForJSCompletionTimeout;
  24. using base::test::ios::WaitUntilConditionOrTimeout;
  25. namespace web {
  26. // Tests FindInPageManagerImpl and verifies that the state of
  27. // FindInPageManagerDelegate is correct depending on what web frames return.
  28. class FindInPageManagerImplTest : public WebTest {
  29. protected:
  30. FindInPageManagerImplTest() : WebTest(std::make_unique<FakeWebClient>()) {}
  31. void SetUp() override {
  32. WebTest::SetUp();
  33. fake_web_state_ = std::make_unique<FakeWebState>();
  34. fake_web_state_->SetBrowserState(GetBrowserState());
  35. auto frames_manager = std::make_unique<FakeWebFramesManager>();
  36. fake_web_frames_manager_ = frames_manager.get();
  37. fake_web_state_->SetWebFramesManager(std::move(frames_manager));
  38. JavaScriptFeatureManager::FromBrowserState(GetBrowserState())
  39. ->ConfigureFeatures({FindInPageJavaScriptFeature::GetInstance()});
  40. FindInPageManagerImpl::CreateForWebState(fake_web_state_.get());
  41. GetFindInPageManager()->SetDelegate(&fake_delegate_);
  42. }
  43. // Returns the FindInPageManager associated with |fake_web_state_|.
  44. FindInPageManager* GetFindInPageManager() {
  45. return FindInPageManager::FromWebState(fake_web_state_.get());
  46. }
  47. // Returns a fake WebFrame that represents the main frame which will return
  48. // |js_result| for the JavaScript function call "findInString.findString".
  49. std::unique_ptr<FakeWebFrame> CreateMainWebFrameWithJsResultForFind(
  50. base::Value* js_result) {
  51. auto frame = FakeWebFrame::CreateMainWebFrame(GURL());
  52. frame->AddJsResultForFunctionCall(js_result, kFindInPageSearch);
  53. frame->set_browser_state(GetBrowserState());
  54. return frame;
  55. }
  56. // Returns a fake WebFrame that represents a child frame which will return
  57. // |js_result| for the JavaScript function call "findInString.findString".
  58. std::unique_ptr<FakeWebFrame> CreateChildWebFrameWithJsResultForFind(
  59. base::Value* js_result) {
  60. auto frame = FakeWebFrame::CreateChildWebFrame(GURL());
  61. frame->AddJsResultForFunctionCall(js_result, kFindInPageSearch);
  62. frame->set_browser_state(GetBrowserState());
  63. return frame;
  64. }
  65. void AddWebFrame(std::unique_ptr<FakeWebFrame> frame) {
  66. WebFrame* frame_ptr = frame.get();
  67. fake_web_frames_manager_->AddWebFrame(std::move(frame));
  68. fake_web_state_->OnWebFrameDidBecomeAvailable(frame_ptr);
  69. }
  70. void RemoveWebFrame(const std::string& frame_id) {
  71. WebFrame* frame_ptr = fake_web_frames_manager_->GetFrameWithId(frame_id);
  72. fake_web_state_->OnWebFrameWillBecomeUnavailable(frame_ptr);
  73. fake_web_frames_manager_->RemoveWebFrame(frame_id);
  74. }
  75. std::unique_ptr<FakeWebState> fake_web_state_;
  76. FakeWebFramesManager* fake_web_frames_manager_;
  77. FakeFindInPageManagerDelegate fake_delegate_;
  78. base::UserActionTester user_action_tester_;
  79. };
  80. // Tests that Find In Page responds with a total match count of three when a
  81. // frame has one match and another frame has two matches.
  82. TEST_F(FindInPageManagerImplTest, FindMatchesMultipleFrames) {
  83. auto one = std::make_unique<base::Value>(1.0);
  84. auto two = std::make_unique<base::Value>(2.0);
  85. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  86. FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
  87. auto frame_with_two_matches =
  88. CreateChildWebFrameWithJsResultForFind(two.get());
  89. FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
  90. AddWebFrame(std::move(frame_with_one_match));
  91. AddWebFrame(std::move(frame_with_two_matches));
  92. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  93. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  94. base::RunLoop().RunUntilIdle();
  95. return fake_delegate_.state();
  96. }));
  97. ASSERT_EQ(2ul, frame_with_one_match_ptr->GetJavaScriptCallHistory().size());
  98. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  99. frame_with_one_match_ptr->GetJavaScriptCallHistory()[1]);
  100. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  101. frame_with_one_match_ptr->GetJavaScriptCallHistory()[0]);
  102. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  103. frame_with_two_matches_ptr->GetLastJavaScriptCall());
  104. EXPECT_EQ(3, fake_delegate_.state()->match_count);
  105. }
  106. // Tests that Find In Page responds with a total match count of one when a frame
  107. // has one match but find in one frame was cancelled. This can occur if the
  108. // frame becomes unavailable.
  109. TEST_F(FindInPageManagerImplTest, FrameCancelFind) {
  110. auto null = std::make_unique<base::Value>();
  111. auto one = std::make_unique<base::Value>(1.0);
  112. auto frame_with_null_result =
  113. CreateMainWebFrameWithJsResultForFind(null.get());
  114. FakeWebFrame* frame_with_null_result_ptr = frame_with_null_result.get();
  115. auto frame_with_one_match = CreateChildWebFrameWithJsResultForFind(one.get());
  116. FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
  117. AddWebFrame(std::move(frame_with_null_result));
  118. AddWebFrame(std::move(frame_with_one_match));
  119. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  120. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  121. base::RunLoop().RunUntilIdle();
  122. return fake_delegate_.state();
  123. }));
  124. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  125. frame_with_null_result_ptr->GetLastJavaScriptCall());
  126. ASSERT_EQ(2ul, frame_with_one_match_ptr->GetJavaScriptCallHistory().size());
  127. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  128. frame_with_one_match_ptr->GetJavaScriptCallHistory()[1]);
  129. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  130. frame_with_one_match_ptr->GetJavaScriptCallHistory()[0]);
  131. EXPECT_EQ(1, fake_delegate_.state()->match_count);
  132. }
  133. // Tests that Find In Page returns a total match count matching the latest find
  134. // if two finds are called.
  135. TEST_F(FindInPageManagerImplTest, ReturnLatestFind) {
  136. auto one = std::make_unique<base::Value>(1.0);
  137. auto two = std::make_unique<base::Value>(2.0);
  138. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  139. auto frame_with_two_matches =
  140. CreateChildWebFrameWithJsResultForFind(two.get());
  141. FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
  142. AddWebFrame(std::move(frame_with_one_match));
  143. AddWebFrame(std::move(frame_with_two_matches));
  144. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  145. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  146. base::RunLoop().RunUntilIdle();
  147. return fake_delegate_.state();
  148. }));
  149. fake_delegate_.Reset();
  150. RemoveWebFrame(kMainFakeFrameId);
  151. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  152. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  153. base::RunLoop().RunUntilIdle();
  154. return fake_delegate_.state();
  155. }));
  156. ASSERT_EQ(3ul, frame_with_two_matches_ptr->GetJavaScriptCallHistory().size());
  157. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  158. frame_with_two_matches_ptr->GetJavaScriptCallHistory()[2]);
  159. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  160. frame_with_two_matches_ptr->GetJavaScriptCallHistory()[1]);
  161. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  162. frame_with_two_matches_ptr->GetJavaScriptCallHistory()[0]);
  163. EXPECT_EQ(2, fake_delegate_.state()->match_count);
  164. }
  165. // Tests that Find In Page should not return if the web state is destroyed
  166. // during a find.
  167. TEST_F(FindInPageManagerImplTest, DestroyWebStateDuringFind) {
  168. auto one = std::make_unique<base::Value>(1.0);
  169. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  170. AddWebFrame(std::move(frame_with_one_match));
  171. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  172. fake_web_state_.reset();
  173. base::RunLoop().RunUntilIdle();
  174. EXPECT_FALSE(fake_delegate_.state());
  175. }
  176. // Tests that Find In Page updates total match count when a frame with matches
  177. // becomes unavailable during find.
  178. TEST_F(FindInPageManagerImplTest, FrameUnavailableAfterDelegateCallback) {
  179. auto one = std::make_unique<base::Value>(1.0);
  180. auto two = std::make_unique<base::Value>(2.0);
  181. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  182. FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
  183. auto frame_with_two_matches =
  184. CreateChildWebFrameWithJsResultForFind(two.get());
  185. AddWebFrame(std::move(frame_with_one_match));
  186. AddWebFrame(std::move(frame_with_two_matches));
  187. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  188. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  189. base::RunLoop().RunUntilIdle();
  190. return fake_delegate_.state();
  191. }));
  192. fake_delegate_.Reset();
  193. RemoveWebFrame(kChildFakeFrameId);
  194. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  195. base::RunLoop().RunUntilIdle();
  196. return fake_delegate_.state();
  197. }));
  198. ASSERT_EQ(2ul, frame_with_one_match_ptr->GetJavaScriptCallHistory().size());
  199. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  200. frame_with_one_match_ptr->GetJavaScriptCallHistory()[1]);
  201. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  202. frame_with_one_match_ptr->GetJavaScriptCallHistory()[0]);
  203. EXPECT_EQ(1, fake_delegate_.state()->match_count);
  204. }
  205. // Tests that Find In Page returns with the right match count for a frame with
  206. // one match and another that requires pumping to return its two matches.
  207. TEST_F(FindInPageManagerImplTest, FrameRespondsWithPending) {
  208. auto negative_one = std::make_unique<base::Value>(-1.0);
  209. auto one = std::make_unique<base::Value>(1.0);
  210. auto two = std::make_unique<base::Value>(2.0);
  211. std::unique_ptr<FakeWebFrame> frame_with_two_matches =
  212. CreateMainWebFrameWithJsResultForFind(negative_one.get());
  213. frame_with_two_matches->AddJsResultForFunctionCall(two.get(),
  214. kFindInPagePump);
  215. FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
  216. AddWebFrame(std::move(frame_with_two_matches));
  217. auto frame_with_one_match = CreateChildWebFrameWithJsResultForFind(one.get());
  218. FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
  219. AddWebFrame(std::move(frame_with_one_match));
  220. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  221. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  222. base::RunLoop().RunUntilIdle();
  223. return fake_delegate_.state();
  224. }));
  225. ASSERT_EQ(3ul, frame_with_two_matches_ptr->GetJavaScriptCallHistory().size());
  226. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  227. frame_with_two_matches_ptr->GetJavaScriptCallHistory()[2]);
  228. EXPECT_EQ(u"__gCrWeb.findInPage.pumpSearch(100.0);",
  229. frame_with_two_matches_ptr->GetJavaScriptCallHistory()[1]);
  230. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  231. frame_with_two_matches_ptr->GetJavaScriptCallHistory()[0]);
  232. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  233. frame_with_one_match_ptr->GetLastJavaScriptCall());
  234. EXPECT_EQ(3, fake_delegate_.state()->match_count);
  235. }
  236. // Tests that Find In Page doesn't fail when delegate is not set.
  237. TEST_F(FindInPageManagerImplTest, DelegateNotSet) {
  238. GetFindInPageManager()->SetDelegate(nullptr);
  239. auto one = std::make_unique<base::Value>(1.0);
  240. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  241. FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
  242. AddWebFrame(std::move(frame_with_one_match));
  243. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  244. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  245. frame_with_one_match_ptr->GetLastJavaScriptCall());
  246. base::RunLoop().RunUntilIdle();
  247. }
  248. // Tests that Find In Page returns no matches if can't call JavaScript function.
  249. TEST_F(FindInPageManagerImplTest, FrameCannotCallJavaScriptFunction) {
  250. auto one = std::make_unique<base::Value>(1.0);
  251. auto frame_cannot_call_func =
  252. CreateMainWebFrameWithJsResultForFind(one.get());
  253. frame_cannot_call_func->set_can_call_function(false);
  254. AddWebFrame(std::move(frame_cannot_call_func));
  255. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  256. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  257. base::RunLoop().RunUntilIdle();
  258. return fake_delegate_.state();
  259. }));
  260. EXPECT_EQ(0, fake_delegate_.state()->match_count);
  261. }
  262. // Tests that Find In Page responds with a total match count of zero when there
  263. // are no known webpage frames.
  264. TEST_F(FindInPageManagerImplTest, NoFrames) {
  265. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  266. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  267. base::RunLoop().RunUntilIdle();
  268. return fake_delegate_.state();
  269. }));
  270. EXPECT_EQ(0, fake_delegate_.state()->match_count);
  271. }
  272. // Tests that Find in Page responds with a total match count of zero when there
  273. // are no matches in the only frame. Tests that Find in Page also did not
  274. // respond with an selected match index value.
  275. TEST_F(FindInPageManagerImplTest, FrameWithNoMatchNoHighlight) {
  276. auto zero = std::make_unique<base::Value>(0.0);
  277. auto frame_with_zero_matches =
  278. CreateMainWebFrameWithJsResultForFind(zero.get());
  279. FakeWebFrame* frame_with_zero_matches_ptr = frame_with_zero_matches.get();
  280. AddWebFrame(std::move(frame_with_zero_matches));
  281. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  282. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  283. base::RunLoop().RunUntilIdle();
  284. return fake_delegate_.state();
  285. }));
  286. ASSERT_EQ(1ul,
  287. frame_with_zero_matches_ptr->GetJavaScriptCallHistory().size());
  288. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  289. frame_with_zero_matches_ptr->GetJavaScriptCallHistory()[0]);
  290. EXPECT_EQ(0, fake_delegate_.state()->match_count);
  291. EXPECT_EQ(-1, fake_delegate_.state()->index);
  292. }
  293. // Tests that Find in Page responds with index zero after a find when there are
  294. // two matches in a frame.
  295. TEST_F(FindInPageManagerImplTest, DidHighlightFirstIndex) {
  296. auto two = std::make_unique<base::Value>(2.0);
  297. auto frame_with_two_matches =
  298. CreateMainWebFrameWithJsResultForFind(two.get());
  299. FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
  300. AddWebFrame(std::move(frame_with_two_matches));
  301. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  302. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  303. base::RunLoop().RunUntilIdle();
  304. return fake_delegate_.state();
  305. }));
  306. ASSERT_EQ(2ul, frame_with_two_matches_ptr->GetJavaScriptCallHistory().size());
  307. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  308. frame_with_two_matches_ptr->GetJavaScriptCallHistory()[1]);
  309. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  310. frame_with_two_matches_ptr->GetJavaScriptCallHistory()[0]);
  311. EXPECT_EQ(0, fake_delegate_.state()->index);
  312. }
  313. // Tests that Find in Page responds with index one to a FindInPageNext find
  314. // after a FindInPageSearch find finishes when there are two matches in a frame.
  315. TEST_F(FindInPageManagerImplTest, FindDidHighlightSecondIndexAfterNextCall) {
  316. auto two = std::make_unique<base::Value>(2.0);
  317. auto frame_with_two_matches =
  318. CreateMainWebFrameWithJsResultForFind(two.get());
  319. FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
  320. AddWebFrame(std::move(frame_with_two_matches));
  321. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  322. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  323. base::RunLoop().RunUntilIdle();
  324. return fake_delegate_.state();
  325. }));
  326. ASSERT_EQ(2ul, frame_with_two_matches_ptr->GetJavaScriptCallHistory().size());
  327. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  328. frame_with_two_matches_ptr->GetJavaScriptCallHistory()[1]);
  329. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  330. frame_with_two_matches_ptr->GetJavaScriptCallHistory()[0]);
  331. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
  332. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  333. base::RunLoop().RunUntilIdle();
  334. return fake_delegate_.state()->index > -1;
  335. }));
  336. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
  337. frame_with_two_matches_ptr->GetLastJavaScriptCall());
  338. EXPECT_EQ(1, fake_delegate_.state()->index);
  339. }
  340. // Tests that Find in Page selects all matches in a page with one frame with one
  341. // match and another with two matches when making successive FindInPageNext
  342. // calls.
  343. TEST_F(FindInPageManagerImplTest, FindDidSelectAllMatchesWithNextCall) {
  344. auto one = std::make_unique<base::Value>(1.0);
  345. auto two = std::make_unique<base::Value>(2.0);
  346. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  347. FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
  348. auto frame_with_two_matches =
  349. CreateChildWebFrameWithJsResultForFind(two.get());
  350. FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
  351. AddWebFrame(std::move(frame_with_one_match));
  352. AddWebFrame(std::move(frame_with_two_matches));
  353. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  354. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  355. base::RunLoop().RunUntilIdle();
  356. return fake_delegate_.state();
  357. }));
  358. EXPECT_EQ(0, fake_delegate_.state()->index);
  359. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  360. frame_with_one_match_ptr->GetLastJavaScriptCall());
  361. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
  362. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  363. base::RunLoop().RunUntilIdle();
  364. return fake_delegate_.state();
  365. }));
  366. EXPECT_EQ(1, fake_delegate_.state()->index);
  367. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  368. frame_with_two_matches_ptr->GetLastJavaScriptCall());
  369. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
  370. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  371. base::RunLoop().RunUntilIdle();
  372. return fake_delegate_.state();
  373. }));
  374. EXPECT_EQ(2, fake_delegate_.state()->index);
  375. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
  376. frame_with_two_matches_ptr->GetLastJavaScriptCall());
  377. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
  378. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  379. base::RunLoop().RunUntilIdle();
  380. return fake_delegate_.state();
  381. }));
  382. EXPECT_EQ(0, fake_delegate_.state()->index);
  383. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  384. frame_with_one_match_ptr->GetLastJavaScriptCall());
  385. }
  386. // Tests that Find in Page selects all matches in a page with one frame with one
  387. // match and another with two matches when making successive FindInPagePrevious
  388. // calls.
  389. TEST_F(FindInPageManagerImplTest,
  390. FindDidLoopThroughAllMatchesWithPreviousCall) {
  391. auto one = std::make_unique<base::Value>(1.0);
  392. auto two = std::make_unique<base::Value>(2.0);
  393. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  394. FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
  395. auto frame_with_two_matches =
  396. CreateChildWebFrameWithJsResultForFind(two.get());
  397. FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
  398. AddWebFrame(std::move(frame_with_one_match));
  399. AddWebFrame(std::move(frame_with_two_matches));
  400. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  401. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  402. base::RunLoop().RunUntilIdle();
  403. return fake_delegate_.state();
  404. }));
  405. EXPECT_EQ(0, fake_delegate_.state()->index);
  406. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  407. frame_with_one_match_ptr->GetLastJavaScriptCall());
  408. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPagePrevious);
  409. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  410. base::RunLoop().RunUntilIdle();
  411. return fake_delegate_.state();
  412. }));
  413. EXPECT_EQ(2, fake_delegate_.state()->index);
  414. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
  415. frame_with_two_matches_ptr->GetLastJavaScriptCall());
  416. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPagePrevious);
  417. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  418. base::RunLoop().RunUntilIdle();
  419. return fake_delegate_.state();
  420. }));
  421. EXPECT_EQ(1, fake_delegate_.state()->index);
  422. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  423. frame_with_two_matches_ptr->GetLastJavaScriptCall());
  424. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPagePrevious);
  425. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  426. base::RunLoop().RunUntilIdle();
  427. return fake_delegate_.state();
  428. }));
  429. EXPECT_EQ(0, fake_delegate_.state()->index);
  430. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  431. frame_with_one_match_ptr->GetLastJavaScriptCall());
  432. }
  433. // Tests that Find in Page responds with index two to a FindInPagePrevious find
  434. // after a FindInPageSearch find finishes when there are two matches in a
  435. // frame and one match in another.
  436. TEST_F(FindInPageManagerImplTest, FindDidHighlightLastIndexAfterPreviousCall) {
  437. auto one = std::make_unique<base::Value>(1.0);
  438. auto two = std::make_unique<base::Value>(2.0);
  439. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  440. FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
  441. auto frame_with_two_matches =
  442. CreateChildWebFrameWithJsResultForFind(two.get());
  443. FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
  444. AddWebFrame(std::move(frame_with_one_match));
  445. AddWebFrame(std::move(frame_with_two_matches));
  446. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  447. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  448. base::RunLoop().RunUntilIdle();
  449. return fake_delegate_.state();
  450. }));
  451. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  452. frame_with_two_matches_ptr->GetLastJavaScriptCall());
  453. ASSERT_EQ(2ul, frame_with_one_match_ptr->GetJavaScriptCallHistory().size());
  454. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  455. frame_with_one_match_ptr->GetJavaScriptCallHistory()[1]);
  456. EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
  457. frame_with_one_match_ptr->GetJavaScriptCallHistory()[0]);
  458. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPagePrevious);
  459. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  460. base::RunLoop().RunUntilIdle();
  461. return fake_delegate_.state()->index == 2;
  462. }));
  463. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
  464. frame_with_two_matches_ptr->GetLastJavaScriptCall());
  465. }
  466. // Tests that Find in Page does not respond to a FindInPageNext or a
  467. // FindInPagePrevious call if no FindInPageSearch find was executed beforehand.
  468. TEST_F(FindInPageManagerImplTest, FindDidNotRepondToNextOrPrevIfNoSearch) {
  469. auto three = std::make_unique<base::Value>(3.0);
  470. auto frame_with_three_matches =
  471. CreateMainWebFrameWithJsResultForFind(three.get());
  472. AddWebFrame(std::move(frame_with_three_matches));
  473. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
  474. base::RunLoop().RunUntilIdle();
  475. EXPECT_FALSE(fake_delegate_.state());
  476. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPagePrevious);
  477. base::RunLoop().RunUntilIdle();
  478. EXPECT_FALSE(fake_delegate_.state());
  479. }
  480. // Tests that Find in Page responds with index one for a successive
  481. // FindInPageNext after the frame containing the currently selected match is
  482. // removed.
  483. TEST_F(FindInPageManagerImplTest,
  484. FindDidHighlightNextMatchAfterFrameDisappears) {
  485. auto one = std::make_unique<base::Value>(1.0);
  486. auto two = std::make_unique<base::Value>(2.0);
  487. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  488. FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
  489. auto frame_with_two_matches =
  490. CreateChildWebFrameWithJsResultForFind(two.get());
  491. FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
  492. AddWebFrame(std::move(frame_with_one_match));
  493. AddWebFrame(std::move(frame_with_two_matches));
  494. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  495. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  496. base::RunLoop().RunUntilIdle();
  497. return fake_delegate_.state();
  498. }));
  499. ASSERT_EQ(2ul, frame_with_one_match_ptr->GetJavaScriptCallHistory().size());
  500. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  501. frame_with_one_match_ptr->GetJavaScriptCallHistory()[1]);
  502. RemoveWebFrame(kMainFakeFrameId);
  503. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
  504. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  505. base::RunLoop().RunUntilIdle();
  506. return fake_delegate_.state()->index == 0;
  507. }));
  508. EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
  509. frame_with_two_matches_ptr->GetLastJavaScriptCall());
  510. }
  511. // Tests that Find in Page does not respond when frame is removed
  512. TEST_F(FindInPageManagerImplTest, FindDidNotRepondAfterFrameRemoved) {
  513. auto one = std::make_unique<base::Value>(1.0);
  514. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  515. AddWebFrame(std::move(frame_with_one_match));
  516. RemoveWebFrame(kMainFakeFrameId);
  517. base::RunLoop().RunUntilIdle();
  518. EXPECT_FALSE(fake_delegate_.state());
  519. }
  520. // Tests that Find in Page responds with a total match count of one to a
  521. // FindInPageSearch find when there is one match in a frame and then responds
  522. // with a total match count of zero when that frame is removed.
  523. TEST_F(FindInPageManagerImplTest, FindInPageUpdateMatchCountAfterFrameRemoved) {
  524. auto one = std::make_unique<base::Value>(1.0);
  525. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  526. AddWebFrame(std::move(frame_with_one_match));
  527. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  528. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  529. base::RunLoop().RunUntilIdle();
  530. return fake_delegate_.state();
  531. }));
  532. RemoveWebFrame(kMainFakeFrameId);
  533. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  534. base::RunLoop().RunUntilIdle();
  535. return fake_delegate_.state()->match_count == 0;
  536. }));
  537. }
  538. // Tests that DidHighlightMatches is not called when a frame with no matches is
  539. // removed from the page.
  540. TEST_F(FindInPageManagerImplTest, FindDidNotResponseAfterFrameDisappears) {
  541. auto zero = std::make_unique<base::Value>(0.0);
  542. auto two = std::make_unique<base::Value>(2.0);
  543. auto frame_with_zero_matches =
  544. CreateMainWebFrameWithJsResultForFind(zero.get());
  545. auto frame_with_two_matches =
  546. CreateChildWebFrameWithJsResultForFind(two.get());
  547. AddWebFrame(std::move(frame_with_zero_matches));
  548. AddWebFrame(std::move(frame_with_two_matches));
  549. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  550. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  551. base::RunLoop().RunUntilIdle();
  552. return fake_delegate_.state();
  553. }));
  554. fake_delegate_.Reset();
  555. RemoveWebFrame(kMainFakeFrameId);
  556. EXPECT_FALSE(fake_delegate_.state());
  557. }
  558. // Tests that Find in Page SetContentIsHTML() returns true if the web state's
  559. // content is HTML and returns false if the web state's content is not HTML.
  560. TEST_F(FindInPageManagerImplTest, FindInPageCanSearchContent) {
  561. fake_web_state_->SetContentIsHTML(false);
  562. EXPECT_FALSE(GetFindInPageManager()->CanSearchContent());
  563. fake_web_state_->SetContentIsHTML(true);
  564. EXPECT_TRUE(GetFindInPageManager()->CanSearchContent());
  565. }
  566. // Tests that Find in Page resets the match count to 0 and the query to nil
  567. // after calling StopFinding().
  568. TEST_F(FindInPageManagerImplTest, FindInPageCanStopFind) {
  569. auto one = std::make_unique<base::Value>(1.0);
  570. auto frame_with_one_match = CreateMainWebFrameWithJsResultForFind(one.get());
  571. AddWebFrame(std::move(frame_with_one_match));
  572. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  573. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  574. base::RunLoop().RunUntilIdle();
  575. return fake_delegate_.state() && fake_delegate_.state()->match_count == 1;
  576. }));
  577. GetFindInPageManager()->StopFinding();
  578. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  579. base::RunLoop().RunUntilIdle();
  580. return fake_delegate_.state() && fake_delegate_.state()->match_count == 0;
  581. }));
  582. EXPECT_FALSE(fake_delegate_.state()->query);
  583. }
  584. // Tests that Find in Page responds with an updated match count when calling
  585. // FindInPageNext after the visible match count in a frame changes following a
  586. // FindInPageSearch. This simulates a once hidden match becoming visible between
  587. // a FindInPageSearch and a FindInPageNext.
  588. TEST_F(FindInPageManagerImplTest, FindInPageNextUpdatesMatchCount) {
  589. auto two = std::make_unique<base::Value>(2.0);
  590. auto frame_with_hidden_match =
  591. CreateMainWebFrameWithJsResultForFind(two.get());
  592. FakeWebFrame* frame_with_hidden_match_ptr = frame_with_hidden_match.get();
  593. AddWebFrame(std::move(frame_with_hidden_match));
  594. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  595. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  596. base::RunLoop().RunUntilIdle();
  597. return fake_delegate_.state() && fake_delegate_.state()->match_count == 2;
  598. }));
  599. base::Value::Dict select_and_scroll_result;
  600. select_and_scroll_result.Set("matches", 3.0);
  601. select_and_scroll_result.Set("index", 1.0);
  602. base::Value select_and_scroll_result_value(
  603. std::move(select_and_scroll_result));
  604. frame_with_hidden_match_ptr->AddJsResultForFunctionCall(
  605. &select_and_scroll_result_value, kFindInPageSelectAndScrollToMatch);
  606. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
  607. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
  608. base::RunLoop().RunUntilIdle();
  609. return fake_delegate_.state() && fake_delegate_.state()->match_count == 3;
  610. }));
  611. EXPECT_EQ(1, fake_delegate_.state()->index);
  612. }
  613. // Tests that Find in Page logs correct UserActions for given API calls.
  614. TEST_F(FindInPageManagerImplTest, FindUserActions) {
  615. ASSERT_EQ(0, user_action_tester_.GetActionCount(kFindActionName));
  616. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
  617. EXPECT_EQ(1, user_action_tester_.GetActionCount(kFindActionName));
  618. ASSERT_EQ(0, user_action_tester_.GetActionCount(kFindNextActionName));
  619. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
  620. EXPECT_EQ(1, user_action_tester_.GetActionCount(kFindNextActionName));
  621. ASSERT_EQ(0, user_action_tester_.GetActionCount(kFindPreviousActionName));
  622. GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPagePrevious);
  623. EXPECT_EQ(1, user_action_tester_.GetActionCount(kFindPreviousActionName));
  624. }
  625. } // namespace web