frame_impl_browsertest.cc 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272
  1. // Copyright 2018 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. #include <fuchsia/ui/policy/cpp/fidl.h>
  5. #include <lib/ui/scenic/cpp/view_token_pair.h>
  6. #include "base/fuchsia/mem_buffer_util.h"
  7. #include "base/test/test_future.h"
  8. #include "build/build_config.h"
  9. #include "content/public/browser/navigation_handle.h"
  10. #include "content/public/test/browser_test.h"
  11. #include "content/public/test/test_utils.h"
  12. #include "fuchsia_web/common/string_util.h"
  13. #include "fuchsia_web/common/test/fit_adapter.h"
  14. #include "fuchsia_web/common/test/frame_test_util.h"
  15. #include "fuchsia_web/common/test/test_navigation_listener.h"
  16. #include "fuchsia_web/webengine/browser/context_impl.h"
  17. #include "fuchsia_web/webengine/browser/fake_semantics_manager.h"
  18. #include "fuchsia_web/webengine/browser/frame_impl.h"
  19. #include "fuchsia_web/webengine/browser/frame_impl_browser_test_base.h"
  20. #include "fuchsia_web/webengine/test/frame_for_test.h"
  21. #include "net/test/embedded_test_server/request_handler_util.h"
  22. #include "testing/gmock/include/gmock/gmock.h"
  23. using testing::_;
  24. using testing::AllOf;
  25. using testing::AtLeast;
  26. using testing::Contains;
  27. using testing::Field;
  28. using testing::InvokeWithoutArgs;
  29. using testing::Key;
  30. using testing::Mock;
  31. using testing::Not;
  32. // Use a shorter name for NavigationState, because it is referenced frequently
  33. // in this file.
  34. using NavigationDetails = fuchsia::web::NavigationState;
  35. using OnNavigationStateChangedCallback =
  36. fuchsia::web::NavigationEventListener::OnNavigationStateChangedCallback;
  37. namespace {
  38. constexpr char kPage1Path[] = "/title1.html";
  39. constexpr char kPage2Path[] = "/title2.html";
  40. constexpr char kPage3Path[] = "/websql.html";
  41. constexpr char kVisibilityPath[] = "/visibility.html";
  42. constexpr char kWaitSizePath[] = "/wait-size.html";
  43. constexpr char kPage1Title[] = "title 1";
  44. constexpr char kPage2Title[] = "title 2";
  45. constexpr char kPage3Title[] = "websql not available";
  46. constexpr char kDataUrl[] =
  47. "data:text/html;base64,PGI+SGVsbG8sIHdvcmxkLi4uPC9iPg==";
  48. MATCHER_P(NavigationHandleUrlEquals,
  49. url,
  50. "Checks equality with a NavigationHandle's URL.") {
  51. return arg->GetURL() == url;
  52. }
  53. class MockWebContentsObserver : public content::WebContentsObserver {
  54. public:
  55. explicit MockWebContentsObserver(content::WebContents* web_contents) {
  56. Observe(web_contents);
  57. }
  58. ~MockWebContentsObserver() override = default;
  59. MOCK_METHOD1(DidStartNavigation, void(content::NavigationHandle*));
  60. MOCK_METHOD1(RenderViewDeleted,
  61. void(content::RenderViewHost* render_view_host));
  62. };
  63. } // namespace
  64. // Defines a suite of tests that exercise Frame-level functionality, such as
  65. // navigation commands and page events.
  66. class FrameImplTest : public FrameImplTestBase {
  67. public:
  68. FrameImplTest() = default;
  69. ~FrameImplTest() override = default;
  70. FrameImplTest(const FrameImplTest&) = delete;
  71. FrameImplTest& operator=(const FrameImplTest&) = delete;
  72. MOCK_METHOD1(OnServeHttpRequest,
  73. void(const net::test_server::HttpRequest& request));
  74. };
  75. std::string GetDocumentVisibilityState(fuchsia::web::Frame* frame) {
  76. auto visibility = base::MakeRefCounted<base::RefCountedData<std::string>>();
  77. base::RunLoop loop;
  78. frame->ExecuteJavaScript(
  79. {"*"}, base::MemBufferFromString("document.visibilityState;", "test"),
  80. [visibility, quit_loop = loop.QuitClosure()](
  81. fuchsia::web::Frame_ExecuteJavaScript_Result result) {
  82. ASSERT_TRUE(result.is_response());
  83. visibility->data = *base::StringFromMemBuffer(result.response().result);
  84. quit_loop.Run();
  85. });
  86. loop.Run();
  87. return visibility->data;
  88. }
  89. // Verifies that Frames are initially "hidden", changes to "visible" once the
  90. // View is attached to a Presenter and back to "hidden" when the View is
  91. // detached from the Presenter.
  92. // TODO(https://crbug.com/1314086): Re-enable this test when we can make it work
  93. // with the fake hardware display controller provider used for tests.
  94. IN_PROC_BROWSER_TEST_F(FrameImplTest, DISABLED_VisibilityState) {
  95. net::test_server::EmbeddedTestServerHandle test_server_handle;
  96. ASSERT_TRUE(test_server_handle =
  97. embedded_test_server()->StartAndReturnHandle());
  98. GURL page_url(embedded_test_server()->GetURL(kVisibilityPath));
  99. auto frame = FrameForTest::Create(context(), {});
  100. base::RunLoop().RunUntilIdle();
  101. // Navigate to a page and wait for it to finish loading.
  102. ASSERT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  103. fuchsia::web::LoadUrlParams(),
  104. page_url.spec()));
  105. frame.navigation_listener().RunUntilUrlEquals(page_url);
  106. // Query the document.visibilityState before creating a View.
  107. EXPECT_EQ(GetDocumentVisibilityState(frame.ptr().get()), "\"hidden\"");
  108. // Query the document.visibilityState after creating the View, but without it
  109. // actually "attached" to the view tree.
  110. auto view_tokens = scenic::ViewTokenPair::New();
  111. frame->CreateView(std::move(view_tokens.view_token));
  112. base::RunLoop().RunUntilIdle();
  113. EXPECT_EQ(GetDocumentVisibilityState(frame.ptr().get()), "\"hidden\"");
  114. // Attach the View to a Presenter, the page should be visible.
  115. auto presenter = base::ComponentContextForProcess()
  116. ->svc()
  117. ->Connect<::fuchsia::ui::policy::Presenter>();
  118. presenter.set_error_handler(
  119. [](zx_status_t) { ADD_FAILURE() << "Presenter disconnected."; });
  120. presenter->PresentOrReplaceView(std::move(view_tokens.view_holder_token),
  121. nullptr);
  122. frame.navigation_listener().RunUntilTitleEquals("visible");
  123. // Attach a new View to the Presenter, the page should be hidden again.
  124. // This part of the test is a regression test for crbug.com/1141093.
  125. auto view_tokens2 = scenic::ViewTokenPair::New();
  126. presenter->PresentOrReplaceView(std::move(view_tokens2.view_holder_token),
  127. nullptr);
  128. frame.navigation_listener().RunUntilTitleEquals("hidden");
  129. }
  130. void VerifyCanGoBackAndForward(
  131. const fuchsia::web::NavigationControllerPtr& controller,
  132. bool can_go_back_expected,
  133. bool can_go_forward_expected) {
  134. base::test::TestFuture<fuchsia::web::NavigationState> visible_entry;
  135. controller->GetVisibleEntry(
  136. CallbackToFitFunction(visible_entry.GetCallback()));
  137. ASSERT_TRUE(visible_entry.Wait());
  138. EXPECT_TRUE(visible_entry.Get().has_can_go_back());
  139. EXPECT_EQ(visible_entry.Get().can_go_back(), can_go_back_expected);
  140. EXPECT_TRUE(visible_entry.Get().has_can_go_forward());
  141. EXPECT_EQ(visible_entry.Get().can_go_forward(), can_go_forward_expected);
  142. }
  143. // Verifies that the browser will navigate and generate a navigation listener
  144. // event when LoadUrl() is called.
  145. IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigateFrame) {
  146. auto frame = FrameForTest::Create(context(), {});
  147. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  148. fuchsia::web::LoadUrlParams(),
  149. url::kAboutBlankURL));
  150. frame.navigation_listener().RunUntilUrlAndTitleEquals(
  151. GURL(url::kAboutBlankURL), url::kAboutBlankURL);
  152. }
  153. // Verifies that the browser does not crash if the Renderer process exits while
  154. // a navigation event listener is attached to the Frame.
  155. IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigationListenerRendererProcessGone) {
  156. net::test_server::EmbeddedTestServerHandle test_server_handle;
  157. ASSERT_TRUE(test_server_handle =
  158. embedded_test_server()->StartAndReturnHandle());
  159. GURL url(embedded_test_server()->GetURL("/nocontent"));
  160. // Create a Frame and navigate it to a URL that will not "commit".
  161. auto frame = FrameForTest::Create(context(), {});
  162. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  163. fuchsia::web::LoadUrlParams(),
  164. url.spec()));
  165. // Terminate the Renderer process and run the browser until that is observed.
  166. content::RenderProcessHost* child_process =
  167. context_impl()
  168. ->GetFrameImplForTest(&frame.ptr())
  169. ->web_contents()
  170. ->GetPrimaryMainFrame()
  171. ->GetProcess();
  172. content::RenderProcessHostWatcher(
  173. child_process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY)
  174. .Wait();
  175. ASSERT_TRUE(child_process->IsReady());
  176. content::RenderProcessHostWatcher exit_observer(
  177. child_process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
  178. child_process->Shutdown(0);
  179. exit_observer.Wait();
  180. EXPECT_FALSE(exit_observer.did_exit_normally());
  181. // Spin the browser main loop to flush any queued work, or FIDL activity.
  182. base::RunLoop().RunUntilIdle();
  183. }
  184. // Verifies that the renderer process consumes more memory for document
  185. // rendering.
  186. IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigationIncreasesMemoryUsage) {
  187. net::test_server::EmbeddedTestServerHandle test_server_handle;
  188. ASSERT_TRUE(test_server_handle =
  189. embedded_test_server()->StartAndReturnHandle());
  190. GURL url(embedded_test_server()->GetURL(kPage1Path));
  191. auto frame = FrameForTest::Create(context(), {});
  192. // Get the renderer size when no renderer process is active.
  193. base::test::TestFuture<uint64_t> before_nav_size;
  194. frame->GetPrivateMemorySize(
  195. CallbackToFitFunction(before_nav_size.GetCallback()));
  196. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  197. fuchsia::web::LoadUrlParams(),
  198. url.spec()));
  199. frame.navigation_listener().RunUntilUrlAndTitleEquals(url, kPage1Title);
  200. base::test::TestFuture<uint64_t> after_nav_size;
  201. frame->GetPrivateMemorySize(
  202. CallbackToFitFunction(after_nav_size.GetCallback()));
  203. ASSERT_TRUE(after_nav_size.Wait());
  204. EXPECT_EQ(before_nav_size.Get(), 0u); // No render process - zero bytes.
  205. EXPECT_GT(after_nav_size.Get(), 0u);
  206. }
  207. IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigateDataFrame) {
  208. auto frame = FrameForTest::Create(context(), {});
  209. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  210. fuchsia::web::LoadUrlParams(),
  211. kDataUrl));
  212. frame.navigation_listener().RunUntilUrlAndTitleEquals(GURL(kDataUrl),
  213. kDataUrl);
  214. }
  215. IN_PROC_BROWSER_TEST_F(FrameImplTest, FrameDeletedBeforeContext) {
  216. auto frame = FrameForTest::Create(context(), {});
  217. // Process the frame creation message.
  218. base::RunLoop().RunUntilIdle();
  219. FrameImpl* frame_impl = context_impl()->GetFrameImplForTest(&frame.ptr());
  220. MockWebContentsObserver deletion_observer(frame_impl->web_contents());
  221. base::RunLoop run_loop;
  222. EXPECT_CALL(deletion_observer, RenderViewDeleted(_))
  223. .WillOnce(InvokeWithoutArgs([&run_loop] { run_loop.Quit(); }));
  224. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  225. fuchsia::web::LoadUrlParams(),
  226. url::kAboutBlankURL));
  227. frame.ptr().Unbind();
  228. run_loop.Run();
  229. // Check that |context| remains bound after the frame is closed.
  230. EXPECT_TRUE(context());
  231. }
  232. IN_PROC_BROWSER_TEST_F(FrameImplTest, ContextDeletedBeforeFrame) {
  233. auto frame = FrameForTest::Create(context(), {});
  234. EXPECT_TRUE(frame.ptr());
  235. base::RunLoop run_loop;
  236. frame.ptr().set_error_handler([&run_loop](zx_status_t status) {
  237. EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
  238. run_loop.Quit();
  239. });
  240. context().Unbind();
  241. run_loop.Run();
  242. EXPECT_FALSE(frame.ptr());
  243. }
  244. IN_PROC_BROWSER_TEST_F(FrameImplTest, ContextDeletedBeforeFrameWithView) {
  245. auto frame = FrameForTest::Create(context(), {});
  246. EXPECT_TRUE(frame.ptr());
  247. base::RunLoop().RunUntilIdle();
  248. FrameImpl* frame_impl = context_impl()->GetFrameImplForTest(&frame.ptr());
  249. auto view_tokens = scenic::ViewTokenPair::New();
  250. frame->CreateView(std::move(view_tokens.view_token));
  251. base::RunLoop().RunUntilIdle();
  252. EXPECT_TRUE(frame_impl->has_view_for_test());
  253. base::RunLoop run_loop;
  254. frame.ptr().set_error_handler([&run_loop](zx_status_t status) {
  255. EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
  256. run_loop.Quit();
  257. });
  258. context().Unbind();
  259. run_loop.Run();
  260. EXPECT_FALSE(frame.ptr());
  261. }
  262. // TODO(https://crbug.com/695592): Remove this test when WebSQL is removed from
  263. // Chrome.
  264. IN_PROC_BROWSER_TEST_F(FrameImplTest, EnsureWebSqlDisabled) {
  265. auto frame = FrameForTest::Create(context(), {});
  266. net::test_server::EmbeddedTestServerHandle test_server_handle;
  267. ASSERT_TRUE(test_server_handle =
  268. embedded_test_server()->StartAndReturnHandle());
  269. GURL title3(embedded_test_server()->GetURL(kPage3Path));
  270. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  271. fuchsia::web::LoadUrlParams(),
  272. title3.spec()));
  273. frame.navigation_listener().RunUntilUrlAndTitleEquals(title3, kPage3Title);
  274. }
  275. IN_PROC_BROWSER_TEST_F(FrameImplTest, GoBackAndForward) {
  276. auto frame = FrameForTest::Create(context(), {});
  277. net::test_server::EmbeddedTestServerHandle test_server_handle;
  278. ASSERT_TRUE(test_server_handle =
  279. embedded_test_server()->StartAndReturnHandle());
  280. GURL title1(embedded_test_server()->GetURL(kPage1Path));
  281. GURL title2(embedded_test_server()->GetURL(kPage2Path));
  282. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  283. fuchsia::web::LoadUrlParams(),
  284. title1.spec()));
  285. frame.navigation_listener().RunUntilUrlTitleBackForwardEquals(
  286. title1, kPage1Title, false, false);
  287. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  288. fuchsia::web::LoadUrlParams(),
  289. title2.spec()));
  290. frame.navigation_listener().RunUntilUrlTitleBackForwardEquals(
  291. title2, kPage2Title, true, false);
  292. VerifyCanGoBackAndForward(frame.GetNavigationController(), true, false);
  293. frame.GetNavigationController()->GoBack();
  294. frame.navigation_listener().RunUntilUrlTitleBackForwardEquals(
  295. title1, kPage1Title, false, true);
  296. // At the top of the navigation entry list; this should be a no-op.
  297. VerifyCanGoBackAndForward(frame.GetNavigationController(), false, true);
  298. frame.GetNavigationController()->GoBack();
  299. // Process the navigation request message.
  300. base::RunLoop().RunUntilIdle();
  301. VerifyCanGoBackAndForward(frame.GetNavigationController(), false, true);
  302. frame.GetNavigationController()->GoForward();
  303. frame.navigation_listener().RunUntilUrlTitleBackForwardEquals(
  304. title2, kPage2Title, true, false);
  305. // At the end of the navigation entry list; this should be a no-op.
  306. VerifyCanGoBackAndForward(frame.GetNavigationController(), true, false);
  307. frame.GetNavigationController()->GoForward();
  308. // Process the navigation request message.
  309. base::RunLoop().RunUntilIdle();
  310. }
  311. // An HTTP response stream whose response payload can be sent as "chunks"
  312. // with indeterminate-length pauses in between.
  313. class ChunkedHttpTransaction {
  314. public:
  315. explicit ChunkedHttpTransaction(
  316. base::WeakPtr<net::test_server::HttpResponseDelegate> delegate)
  317. : io_task_runner_(base::ThreadTaskRunnerHandle::Get()),
  318. send_state_(SendState::IDLE),
  319. delegate_(delegate) {
  320. EXPECT_FALSE(current_instance_);
  321. current_instance_ = this;
  322. }
  323. ChunkedHttpTransaction(const ChunkedHttpTransaction&) = delete;
  324. ChunkedHttpTransaction& operator=(const ChunkedHttpTransaction&) = delete;
  325. static ChunkedHttpTransaction* current() {
  326. EXPECT_TRUE(current_instance_);
  327. return current_instance_;
  328. }
  329. void Close() {
  330. EnsureSendCompleted();
  331. io_task_runner_->PostTask(
  332. FROM_HERE,
  333. base::BindOnce(&net::test_server::HttpResponseDelegate::FinishResponse,
  334. delegate_));
  335. delete this;
  336. }
  337. void EnsureSendCompleted() {
  338. if (send_state_ == SendState::IDLE)
  339. return;
  340. base::RunLoop run_loop;
  341. send_chunk_complete_callback_ = run_loop.QuitClosure();
  342. run_loop.Run();
  343. EXPECT_EQ(send_state_, SendState::IDLE);
  344. }
  345. void SendChunk(const std::string& chunk) {
  346. EnsureSendCompleted();
  347. send_state_ = SendState::BLOCKED;
  348. io_task_runner_->PostTask(
  349. FROM_HERE,
  350. base::BindOnce(
  351. &net::test_server::HttpResponseDelegate::SendContents, delegate_,
  352. chunk,
  353. base::BindOnce(&ChunkedHttpTransaction::SendChunkCompleteOnIoThread,
  354. base::Unretained(this),
  355. base::ThreadTaskRunnerHandle::Get())));
  356. }
  357. private:
  358. static ChunkedHttpTransaction* current_instance_;
  359. ~ChunkedHttpTransaction() { current_instance_ = nullptr; }
  360. void SendChunkCompleteOnIoThread(
  361. scoped_refptr<base::TaskRunner> ui_thread_task_runner) {
  362. ui_thread_task_runner->PostTask(
  363. FROM_HERE,
  364. base::BindOnce(&ChunkedHttpTransaction::SendChunkCompleteOnUiThread,
  365. base::Unretained(this)));
  366. }
  367. void SendChunkCompleteOnUiThread() {
  368. send_state_ = SendState::IDLE;
  369. if (send_chunk_complete_callback_)
  370. std::move(send_chunk_complete_callback_).Run();
  371. }
  372. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
  373. // Set by callers to SendChunk() waiting for the previous chunk to complete.
  374. base::OnceClosure send_chunk_complete_callback_;
  375. enum SendState { IDLE, BLOCKED };
  376. SendState send_state_;
  377. base::WeakPtr<net::test_server::HttpResponseDelegate> delegate_;
  378. };
  379. ChunkedHttpTransaction* ChunkedHttpTransaction::current_instance_ = nullptr;
  380. class ChunkedHttpTransactionFactory : public net::test_server::HttpResponse {
  381. public:
  382. ChunkedHttpTransactionFactory() = default;
  383. ChunkedHttpTransactionFactory(const ChunkedHttpTransactionFactory&) = delete;
  384. ChunkedHttpTransactionFactory& operator=(
  385. const ChunkedHttpTransactionFactory&) = delete;
  386. ~ChunkedHttpTransactionFactory() override = default;
  387. void SetOnResponseCreatedCallback(base::OnceClosure on_response_created) {
  388. on_response_created_ = std::move(on_response_created);
  389. }
  390. // net::test_server::HttpResponse implementation.
  391. void SendResponse(
  392. base::WeakPtr<net::test_server::HttpResponseDelegate> delegate) override {
  393. // The ChunkedHttpTransaction manages its own lifetime.
  394. new ChunkedHttpTransaction(delegate);
  395. if (on_response_created_)
  396. std::move(on_response_created_).Run();
  397. }
  398. private:
  399. base::OnceClosure on_response_created_;
  400. };
  401. IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigationEventDuringPendingLoad) {
  402. auto frame = FrameForTest::Create(context(), {});
  403. ChunkedHttpTransactionFactory* factory = new ChunkedHttpTransactionFactory;
  404. base::RunLoop transaction_created_run_loop;
  405. factory->SetOnResponseCreatedCallback(
  406. transaction_created_run_loop.QuitClosure());
  407. embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
  408. &net::test_server::HandlePrefixedRequest, "/pausable",
  409. base::BindRepeating(
  410. [](std::unique_ptr<ChunkedHttpTransactionFactory> out_factory,
  411. const net::test_server::HttpRequest&)
  412. -> std::unique_ptr<net::test_server::HttpResponse> {
  413. return out_factory;
  414. },
  415. base::Passed(base::WrapUnique(factory)))));
  416. net::test_server::EmbeddedTestServerHandle test_server_handle;
  417. ASSERT_TRUE(test_server_handle =
  418. embedded_test_server()->StartAndReturnHandle());
  419. GURL hung_url(embedded_test_server()->GetURL("/pausable"));
  420. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  421. fuchsia::web::LoadUrlParams(),
  422. hung_url.spec()));
  423. fuchsia::web::NavigationState state_change;
  424. state_change.set_url(hung_url.spec());
  425. state_change.set_is_main_document_loaded(false);
  426. transaction_created_run_loop.Run();
  427. ChunkedHttpTransaction* transaction = ChunkedHttpTransaction::current();
  428. transaction->SendChunk(
  429. "HTTP/1.0 200 OK\r\n"
  430. "Host: localhost\r\n"
  431. "Content-Type: text/html\r\n\r\n"
  432. "<html><head><title>initial load</title>");
  433. state_change.set_title("initial load");
  434. state_change.set_is_main_document_loaded(false);
  435. frame.navigation_listener().RunUntilNavigationStateMatches(state_change);
  436. transaction->SendChunk(
  437. "<script>document.title='final load';</script><body></body>");
  438. transaction->Close();
  439. state_change.set_title("final load");
  440. state_change.set_is_main_document_loaded(true);
  441. frame.navigation_listener().RunUntilNavigationStateMatches(state_change);
  442. }
  443. IN_PROC_BROWSER_TEST_F(FrameImplTest, ReloadFrame) {
  444. auto frame = FrameForTest::Create(context(), {});
  445. embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
  446. &FrameImplTest::OnServeHttpRequest, base::Unretained(this)));
  447. net::test_server::EmbeddedTestServerHandle test_server_handle;
  448. ASSERT_TRUE(test_server_handle =
  449. embedded_test_server()->StartAndReturnHandle());
  450. GURL url(embedded_test_server()->GetURL(kPage1Path));
  451. EXPECT_CALL(*this, OnServeHttpRequest(_)).Times(AtLeast(1));
  452. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  453. fuchsia::web::LoadUrlParams(),
  454. url.spec()));
  455. frame.navigation_listener().RunUntilUrlAndTitleEquals(url, kPage1Title);
  456. // Reload with NO_CACHE.
  457. {
  458. MockWebContentsObserver web_contents_observer(
  459. context_impl()->GetFrameImplForTest(&frame.ptr())->web_contents_.get());
  460. base::RunLoop run_loop;
  461. EXPECT_CALL(web_contents_observer,
  462. DidStartNavigation(NavigationHandleUrlEquals(url)))
  463. .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  464. frame.GetNavigationController()->Reload(fuchsia::web::ReloadType::NO_CACHE);
  465. run_loop.Run();
  466. }
  467. // Reload with PARTIAL_CACHE.
  468. {
  469. MockWebContentsObserver web_contents_observer(
  470. context_impl()->GetFrameImplForTest(&frame.ptr())->web_contents_.get());
  471. base::RunLoop run_loop;
  472. EXPECT_CALL(web_contents_observer,
  473. DidStartNavigation(NavigationHandleUrlEquals(url)))
  474. .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  475. frame.GetNavigationController()->Reload(
  476. fuchsia::web::ReloadType::PARTIAL_CACHE);
  477. run_loop.Run();
  478. }
  479. }
  480. IN_PROC_BROWSER_TEST_F(FrameImplTest, GetVisibleEntry) {
  481. auto frame = FrameForTest::Create(context(), {});
  482. // Verify that a Frame returns an empty NavigationState prior to receiving any
  483. // LoadUrl() calls.
  484. {
  485. base::test::TestFuture<fuchsia::web::NavigationState> result;
  486. auto controller = frame.GetNavigationController();
  487. controller->GetVisibleEntry(CallbackToFitFunction(result.GetCallback()));
  488. ASSERT_TRUE(result.Wait());
  489. EXPECT_FALSE(result.Get().has_title());
  490. EXPECT_FALSE(result.Get().has_url());
  491. EXPECT_FALSE(result.Get().has_page_type());
  492. }
  493. net::test_server::EmbeddedTestServerHandle test_server_handle;
  494. ASSERT_TRUE(test_server_handle =
  495. embedded_test_server()->StartAndReturnHandle());
  496. GURL title1(embedded_test_server()->GetURL(kPage1Path));
  497. GURL title2(embedded_test_server()->GetURL(kPage2Path));
  498. // Navigate to a page.
  499. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  500. fuchsia::web::LoadUrlParams(),
  501. title1.spec()));
  502. frame.navigation_listener().RunUntilUrlAndTitleEquals(title1, kPage1Title);
  503. // Verify that GetVisibleEntry() reflects the new Frame navigation state.
  504. {
  505. base::test::TestFuture<fuchsia::web::NavigationState> result;
  506. auto controller = frame.GetNavigationController();
  507. controller->GetVisibleEntry(CallbackToFitFunction(result.GetCallback()));
  508. ASSERT_TRUE(result.Wait());
  509. ASSERT_TRUE(result.Get().has_url());
  510. EXPECT_EQ(result.Get().url(), title1.spec());
  511. ASSERT_TRUE(result.Get().has_title());
  512. EXPECT_EQ(result.Get().title(), kPage1Title);
  513. ASSERT_TRUE(result.Get().has_page_type());
  514. EXPECT_EQ(result.Get().page_type(), fuchsia::web::PageType::NORMAL);
  515. }
  516. // Navigate to another page.
  517. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  518. fuchsia::web::LoadUrlParams(),
  519. title2.spec()));
  520. frame.navigation_listener().RunUntilUrlAndTitleEquals(title2, kPage2Title);
  521. // Verify the navigation with GetVisibleEntry().
  522. {
  523. base::test::TestFuture<fuchsia::web::NavigationState> result;
  524. auto controller = frame.GetNavigationController();
  525. controller->GetVisibleEntry(CallbackToFitFunction(result.GetCallback()));
  526. ASSERT_TRUE(result.Wait());
  527. ASSERT_TRUE(result.Get().has_url());
  528. EXPECT_EQ(result.Get().url(), title2.spec());
  529. ASSERT_TRUE(result.Get().has_title());
  530. EXPECT_EQ(result.Get().title(), kPage2Title);
  531. ASSERT_TRUE(result.Get().has_page_type());
  532. EXPECT_EQ(result.Get().page_type(), fuchsia::web::PageType::NORMAL);
  533. }
  534. // Navigate back to the first page.
  535. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  536. fuchsia::web::LoadUrlParams(),
  537. title1.spec()));
  538. frame.navigation_listener().RunUntilUrlAndTitleEquals(title1, kPage1Title);
  539. // Verify the navigation with GetVisibleEntry().
  540. {
  541. base::test::TestFuture<fuchsia::web::NavigationState> result;
  542. auto controller = frame.GetNavigationController();
  543. controller->GetVisibleEntry(CallbackToFitFunction(result.GetCallback()));
  544. ASSERT_TRUE(result.Wait());
  545. ASSERT_TRUE(result.Get().has_url());
  546. EXPECT_EQ(result.Get().url(), title1.spec());
  547. ASSERT_TRUE(result.Get().has_title());
  548. EXPECT_EQ(result.Get().title(), kPage1Title);
  549. ASSERT_TRUE(result.Get().has_page_type());
  550. EXPECT_EQ(result.Get().page_type(), fuchsia::web::PageType::NORMAL);
  551. }
  552. }
  553. // Verifies that NavigationState correctly reports when the Renderer terminates
  554. // or crashes. Also verifies that GetVisibleEntry() reports the same state.
  555. IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigationState_RendererGone) {
  556. auto frame = FrameForTest::Create(context(), {});
  557. net::test_server::EmbeddedTestServerHandle test_server_handle;
  558. ASSERT_TRUE(test_server_handle =
  559. embedded_test_server()->StartAndReturnHandle());
  560. GURL url(embedded_test_server()->GetURL(kPage1Path));
  561. // Navigate to a page.
  562. ASSERT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  563. fuchsia::web::LoadUrlParams(),
  564. url.spec()));
  565. frame.navigation_listener().RunUntilUrlAndTitleEquals(url, kPage1Title);
  566. // Kill the renderer for the tab.
  567. auto* frame_impl = context_impl()->GetFrameImplForTest(&frame.ptr());
  568. auto* web_contents = frame_impl->web_contents_for_test();
  569. {
  570. content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
  571. content::RenderFrameDeletedObserver crash_observer(
  572. web_contents->GetPrimaryMainFrame());
  573. web_contents->GetPrimaryMainFrame()->GetProcess()->Shutdown(1);
  574. crash_observer.WaitUntilDeleted();
  575. }
  576. // Wait for the NavigationListener to also observe the transition.
  577. fuchsia::web::NavigationState error_state;
  578. error_state.set_page_type(fuchsia::web::PageType::ERROR);
  579. frame.navigation_listener().RunUntilNavigationStateMatches(error_state);
  580. const fuchsia::web::NavigationState* current_state =
  581. frame.navigation_listener().current_state();
  582. ASSERT_TRUE(current_state->has_url());
  583. EXPECT_EQ(current_state->url(), url.spec());
  584. ASSERT_TRUE(current_state->has_title());
  585. EXPECT_EQ(current_state->title(), kPage1Title);
  586. ASSERT_TRUE(current_state->has_page_type());
  587. EXPECT_EQ(current_state->page_type(), fuchsia::web::PageType::ERROR);
  588. // Verify that GetVisibleEntry() also reflects the expected error state.
  589. {
  590. base::test::TestFuture<fuchsia::web::NavigationState> result;
  591. auto controller = frame.GetNavigationController();
  592. controller->GetVisibleEntry(CallbackToFitFunction(result.GetCallback()));
  593. ASSERT_TRUE(result.Wait());
  594. ASSERT_TRUE(result.Get().has_url());
  595. EXPECT_EQ(result.Get().url(), url.spec());
  596. ASSERT_TRUE(result.Get().has_title());
  597. EXPECT_EQ(result.Get().title(), kPage1Title);
  598. ASSERT_TRUE(result.Get().has_page_type());
  599. EXPECT_EQ(result.Get().page_type(), fuchsia::web::PageType::ERROR);
  600. }
  601. }
  602. IN_PROC_BROWSER_TEST_F(FrameImplTest, NoNavigationObserverAttached) {
  603. auto frame = FrameForTest::Create(context(), {});
  604. base::RunLoop().RunUntilIdle();
  605. net::test_server::EmbeddedTestServerHandle test_server_handle;
  606. ASSERT_TRUE(test_server_handle =
  607. embedded_test_server()->StartAndReturnHandle());
  608. GURL title1(embedded_test_server()->GetURL(kPage1Path));
  609. GURL title2(embedded_test_server()->GetURL(kPage2Path));
  610. MockWebContentsObserver observer(
  611. context_impl()->GetFrameImplForTest(&frame.ptr())->web_contents_.get());
  612. {
  613. base::RunLoop run_loop;
  614. EXPECT_CALL(observer, DidStartNavigation(NavigationHandleUrlEquals(title1)))
  615. .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  616. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  617. fuchsia::web::LoadUrlParams(),
  618. title1.spec()));
  619. run_loop.Run();
  620. }
  621. {
  622. base::RunLoop run_loop;
  623. EXPECT_CALL(observer, DidStartNavigation(NavigationHandleUrlEquals(title2)))
  624. .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  625. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  626. fuchsia::web::LoadUrlParams(),
  627. title2.spec()));
  628. run_loop.Run();
  629. }
  630. }
  631. // Verifies that a Frame will handle navigation listener disconnection events
  632. // gracefully.
  633. IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigationObserverDisconnected) {
  634. auto frame = FrameForTest::Create(context(), {});
  635. base::RunLoop().RunUntilIdle();
  636. net::test_server::EmbeddedTestServerHandle test_server_handle;
  637. ASSERT_TRUE(test_server_handle =
  638. embedded_test_server()->StartAndReturnHandle());
  639. GURL title1(embedded_test_server()->GetURL(kPage1Path));
  640. GURL title2(embedded_test_server()->GetURL(kPage2Path));
  641. MockWebContentsObserver web_contents_observer(
  642. context_impl()->GetFrameImplForTest(&frame.ptr())->web_contents_.get());
  643. EXPECT_CALL(web_contents_observer,
  644. DidStartNavigation(NavigationHandleUrlEquals(title1)));
  645. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  646. fuchsia::web::LoadUrlParams(),
  647. title1.spec()));
  648. frame.navigation_listener().RunUntilUrlAndTitleEquals(title1, kPage1Title);
  649. // Disconnect the listener & spin the runloop to propagate the disconnection
  650. // event over IPC.
  651. frame.navigation_listener_binding().Close(ZX_ERR_PEER_CLOSED);
  652. base::RunLoop().RunUntilIdle();
  653. base::RunLoop run_loop;
  654. EXPECT_CALL(web_contents_observer,
  655. DidStartNavigation(NavigationHandleUrlEquals(title2)))
  656. .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  657. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  658. fuchsia::web::LoadUrlParams(),
  659. title2.spec()));
  660. run_loop.Run();
  661. }
  662. IN_PROC_BROWSER_TEST_F(FrameImplTest, DelayedNavigationEventAck) {
  663. auto frame = FrameForTest::Create(context(), {});
  664. base::RunLoop().RunUntilIdle();
  665. net::test_server::EmbeddedTestServerHandle test_server_handle;
  666. ASSERT_TRUE(test_server_handle =
  667. embedded_test_server()->StartAndReturnHandle());
  668. GURL title1(embedded_test_server()->GetURL(kPage1Path));
  669. GURL title2(embedded_test_server()->GetURL(kPage2Path));
  670. // Expect an navigation event here, but deliberately postpone acknowledgement
  671. // until the end of the test.
  672. OnNavigationStateChangedCallback captured_ack_cb;
  673. frame.navigation_listener().SetBeforeAckHook(base::BindRepeating(
  674. [](OnNavigationStateChangedCallback* dest_cb,
  675. const fuchsia::web::NavigationState& state,
  676. OnNavigationStateChangedCallback cb) { *dest_cb = std::move(cb); },
  677. base::Unretained(&captured_ack_cb)));
  678. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  679. fuchsia::web::LoadUrlParams(),
  680. title1.spec()));
  681. fuchsia::web::NavigationState expected_state;
  682. expected_state.set_url(title1.spec());
  683. frame.navigation_listener().RunUntilNavigationStateMatches(expected_state);
  684. EXPECT_TRUE(captured_ack_cb);
  685. frame.navigation_listener().SetBeforeAckHook({});
  686. // Navigate to a second page.
  687. {
  688. // Since we have blocked NavigationEventObserver's flow, we must observe the
  689. // lower level browser navigation events directly from the WebContents.
  690. MockWebContentsObserver web_contents_observer(
  691. context_impl()->GetFrameImplForTest(&frame.ptr())->web_contents_.get());
  692. base::RunLoop run_loop;
  693. EXPECT_CALL(web_contents_observer,
  694. DidStartNavigation(NavigationHandleUrlEquals(title2)))
  695. .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  696. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  697. fuchsia::web::LoadUrlParams(),
  698. title2.spec()));
  699. run_loop.Run();
  700. }
  701. // Navigate to the first page.
  702. {
  703. MockWebContentsObserver web_contents_observer(
  704. context_impl()->GetFrameImplForTest(&frame.ptr())->web_contents_.get());
  705. base::RunLoop run_loop;
  706. EXPECT_CALL(web_contents_observer,
  707. DidStartNavigation(NavigationHandleUrlEquals(title1)))
  708. .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  709. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  710. fuchsia::web::LoadUrlParams(),
  711. title1.spec()));
  712. run_loop.Run();
  713. }
  714. // Since there was no observable change in navigation state since the last
  715. // ack, there should be no more NavigationEvents generated.
  716. captured_ack_cb();
  717. frame.navigation_listener().RunUntilUrlAndTitleEquals(title1, kPage1Title);
  718. }
  719. // Observes events specific to the Stop() test case.
  720. struct WebContentsObserverForStop : public content::WebContentsObserver {
  721. using content::WebContentsObserver::Observe;
  722. MOCK_METHOD1(DidStartNavigation, void(content::NavigationHandle*));
  723. MOCK_METHOD0(NavigationStopped, void());
  724. };
  725. IN_PROC_BROWSER_TEST_F(FrameImplTest, Stop) {
  726. auto frame = FrameForTest::Create(context(), {});
  727. base::RunLoop().RunUntilIdle();
  728. net::test_server::EmbeddedTestServerHandle test_server_handle;
  729. ASSERT_TRUE(test_server_handle =
  730. embedded_test_server()->StartAndReturnHandle());
  731. // Use a request handler that will accept the connection and stall
  732. // indefinitely.
  733. GURL hung_url(embedded_test_server()->GetURL("/hung"));
  734. {
  735. base::RunLoop run_loop;
  736. WebContentsObserverForStop observer;
  737. observer.Observe(
  738. context_impl()->GetFrameImplForTest(&frame.ptr())->web_contents_.get());
  739. EXPECT_CALL(observer, DidStartNavigation(_))
  740. .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  741. frame.GetNavigationController()->LoadUrl(
  742. hung_url.spec(), fuchsia::web::LoadUrlParams(),
  743. [](fuchsia::web::NavigationController_LoadUrl_Result) {});
  744. run_loop.Run();
  745. }
  746. EXPECT_TRUE(context_impl()
  747. ->GetFrameImplForTest(&frame.ptr())
  748. ->web_contents_->IsLoading());
  749. {
  750. base::RunLoop run_loop;
  751. WebContentsObserverForStop observer;
  752. observer.Observe(
  753. context_impl()->GetFrameImplForTest(&frame.ptr())->web_contents_.get());
  754. EXPECT_CALL(observer, NavigationStopped())
  755. .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  756. frame.GetNavigationController()->Stop();
  757. run_loop.Run();
  758. }
  759. EXPECT_FALSE(context_impl()
  760. ->GetFrameImplForTest(&frame.ptr())
  761. ->web_contents_->IsLoading());
  762. }
  763. // TODO(crbug.com/1058247): Re-enable this test on Arm64 when femu is available
  764. // for that architecture. This test requires Vulkan and Scenic to properly
  765. // signal the Views visibility.
  766. #if defined(ARCH_CPU_ARM_FAMILY)
  767. #define MAYBE_SetPageScale DISABLED_SetPageScale
  768. #else
  769. // TODO(crbug.com/1239135): SetPageScale/ExecuteJavaScript is racey, causing
  770. // the test to flake.
  771. #define MAYBE_SetPageScale DISABLED_SetPageScale
  772. #endif
  773. IN_PROC_BROWSER_TEST_F(FrameImplTest, MAYBE_SetPageScale) {
  774. auto frame = FrameForTest::Create(context(), {});
  775. auto view_tokens = scenic::ViewTokenPair::New();
  776. frame->CreateView(std::move(view_tokens.view_token));
  777. // Attach the View to a Presenter, the page should be visible.
  778. auto presenter = base::ComponentContextForProcess()
  779. ->svc()
  780. ->Connect<::fuchsia::ui::policy::Presenter>();
  781. presenter.set_error_handler(
  782. [](zx_status_t) { ADD_FAILURE() << "Presenter disconnected."; });
  783. presenter->PresentOrReplaceView(std::move(view_tokens.view_holder_token),
  784. nullptr);
  785. net::test_server::EmbeddedTestServerHandle test_server_handle;
  786. ASSERT_TRUE(test_server_handle =
  787. embedded_test_server()->StartAndReturnHandle());
  788. GURL url = embedded_test_server()->GetURL(kWaitSizePath);
  789. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  790. fuchsia::web::LoadUrlParams(),
  791. url.spec()));
  792. frame.navigation_listener().RunUntilUrlAndTitleEquals(url, "done");
  793. absl::optional<base::Value> default_dpr =
  794. ExecuteJavaScript(frame.ptr().get(), "window.devicePixelRatio");
  795. ASSERT_TRUE(default_dpr);
  796. EXPECT_EQ(default_dpr->GetDouble(), 1.0f);
  797. // Update scale and verify that devicePixelRatio is updated accordingly.
  798. const float kZoomInScale = 1.5;
  799. frame->SetPageScale(kZoomInScale);
  800. absl::optional<base::Value> scaled_dpr =
  801. ExecuteJavaScript(frame.ptr().get(), "window.devicePixelRatio");
  802. ASSERT_TRUE(scaled_dpr);
  803. EXPECT_EQ(scaled_dpr->GetDouble(), kZoomInScale);
  804. // Navigate to the same page on http://localhost. This is a different site,
  805. // so it will be loaded in a new renderer process. Page scale value should be
  806. // preserved.
  807. GURL url2 = embedded_test_server()->GetURL("localhost", kWaitSizePath);
  808. EXPECT_NE(url.host(), url2.host());
  809. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  810. fuchsia::web::LoadUrlParams(),
  811. url2.spec()));
  812. frame.navigation_listener().RunUntilUrlAndTitleEquals(url2, "done");
  813. absl::optional<base::Value> dpr_after_navigation =
  814. ExecuteJavaScript(frame.ptr().get(), "window.devicePixelRatio");
  815. ASSERT_TRUE(scaled_dpr);
  816. EXPECT_EQ(dpr_after_navigation, scaled_dpr);
  817. // Reset the scale to 1.0 (default) and verify that reported DPR is updated
  818. // to 1.0.
  819. frame->SetPageScale(1.0);
  820. absl::optional<base::Value> dpr_after_reset =
  821. ExecuteJavaScript(frame.ptr().get(), "window.devicePixelRatio");
  822. ASSERT_TRUE(dpr_after_reset);
  823. EXPECT_EQ(dpr_after_reset->GetDouble(), 1.0);
  824. // Zoom out by setting scale to 0.5.
  825. const float kZoomOutScale = 0.5;
  826. frame->SetPageScale(kZoomOutScale);
  827. absl::optional<base::Value> zoomed_out_dpr =
  828. ExecuteJavaScript(frame.ptr().get(), "window.devicePixelRatio");
  829. ASSERT_TRUE(zoomed_out_dpr);
  830. EXPECT_EQ(zoomed_out_dpr->GetDouble(), kZoomOutScale);
  831. // Create another frame. Verify that the scale factor is not applied to the
  832. // new frame.
  833. auto frame2 = FrameForTest::Create(context(), {});
  834. view_tokens = scenic::ViewTokenPair::New();
  835. frame2->CreateView(std::move(view_tokens.view_token));
  836. presenter->PresentOrReplaceView(std::move(view_tokens.view_holder_token),
  837. nullptr);
  838. EXPECT_TRUE(LoadUrlAndExpectResponse(frame2.GetNavigationController(),
  839. fuchsia::web::LoadUrlParams(),
  840. url.spec()));
  841. frame2.navigation_listener().RunUntilUrlAndTitleEquals(url, "done");
  842. absl::optional<base::Value> frame2_dpr =
  843. ExecuteJavaScript(frame2.ptr().get(), "window.devicePixelRatio");
  844. ASSERT_TRUE(frame2_dpr);
  845. EXPECT_EQ(frame2_dpr->GetDouble(), 1.0);
  846. }
  847. IN_PROC_BROWSER_TEST_F(FrameImplTest, RecreateView) {
  848. auto frame = FrameForTest::Create(context(), {});
  849. net::test_server::EmbeddedTestServerHandle test_server_handle;
  850. ASSERT_TRUE(test_server_handle =
  851. embedded_test_server()->StartAndReturnHandle());
  852. // Process the Frame creation request, and verify we can get the FrameImpl.
  853. base::RunLoop().RunUntilIdle();
  854. FrameImpl* frame_impl = context_impl()->GetFrameImplForTest(&frame.ptr());
  855. ASSERT_TRUE(frame_impl);
  856. EXPECT_FALSE(frame_impl->has_view_for_test());
  857. // Verify that the Frame can navigate, prior to the View being created.
  858. const GURL page1_url(embedded_test_server()->GetURL(kPage1Path));
  859. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  860. fuchsia::web::LoadUrlParams(),
  861. page1_url.spec()));
  862. frame.navigation_listener().RunUntilUrlAndTitleEquals(page1_url, kPage1Title);
  863. // Request a View from the Frame, and pump the loop to process the request.
  864. auto view_tokens = scenic::ViewTokenPair::New();
  865. frame->CreateView(std::move(view_tokens.view_token));
  866. base::RunLoop().RunUntilIdle();
  867. EXPECT_TRUE(frame_impl->has_view_for_test());
  868. // Verify that the Frame still works, by navigating to Page #2.
  869. const GURL page2_url(embedded_test_server()->GetURL(kPage2Path));
  870. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  871. fuchsia::web::LoadUrlParams(),
  872. page2_url.spec()));
  873. frame.navigation_listener().RunUntilUrlAndTitleEquals(page2_url, kPage2Title);
  874. // Create new View tokens and request a new view.
  875. auto view_tokens2 = scenic::ViewTokenPair::New();
  876. frame->CreateView(std::move(view_tokens2.view_token));
  877. base::RunLoop().RunUntilIdle();
  878. EXPECT_TRUE(frame_impl->has_view_for_test());
  879. // Verify that the Frame still works, by navigating back to Page #1.
  880. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  881. fuchsia::web::LoadUrlParams(),
  882. page1_url.spec()));
  883. frame.navigation_listener().RunUntilUrlAndTitleEquals(page1_url, kPage1Title);
  884. }
  885. IN_PROC_BROWSER_TEST_F(FrameImplTest, ChildFrameNavigationIgnored) {
  886. net::test_server::EmbeddedTestServerHandle test_server_handle;
  887. ASSERT_TRUE(test_server_handle =
  888. embedded_test_server()->StartAndReturnHandle());
  889. GURL page_url(embedded_test_server()->GetURL("/creates_child_frame.html"));
  890. // Navigate to a page and wait for the navigation to complete.
  891. auto frame = FrameForTest::Create(context(), {});
  892. fuchsia::web::NavigationControllerPtr controller;
  893. frame->GetNavigationController(controller.NewRequest());
  894. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  895. fuchsia::web::LoadUrlParams(),
  896. page_url.spec()));
  897. fuchsia::web::NavigationState expected_state;
  898. expected_state.set_url(page_url.spec());
  899. expected_state.set_title("main frame");
  900. expected_state.set_is_main_document_loaded(true);
  901. frame.navigation_listener().RunUntilNavigationStateMatches(
  902. std::move(expected_state));
  903. // Notify the page so that it constructs a child iframe.
  904. fuchsia::web::WebMessage message;
  905. message.set_data(base::MemBufferFromString("test", "test"));
  906. base::test::TestFuture<fuchsia::web::Frame_PostMessage_Result> post_result;
  907. frame->PostMessage(page_url.DeprecatedGetOriginAsURL().spec(),
  908. std::move(message),
  909. CallbackToFitFunction(post_result.GetCallback()));
  910. frame.navigation_listener().SetBeforeAckHook(
  911. base::BindRepeating([](const fuchsia::web::NavigationState& change,
  912. OnNavigationStateChangedCallback callback) {
  913. // The child iframe's loading status should not affect the
  914. // is_main_document_loaded() bit.
  915. if (change.has_is_main_document_loaded())
  916. ADD_FAILURE();
  917. callback();
  918. }));
  919. frame.navigation_listener().RunUntilUrlAndTitleEquals(page_url,
  920. "iframe loaded");
  921. }
  922. // Tests SetNavigationEventListener() immediately returns a NavigationEvent,
  923. // even in the absence of a new navigation.
  924. IN_PROC_BROWSER_TEST_F(FrameImplTest, ImmediateNavigationEvent) {
  925. net::test_server::EmbeddedTestServerHandle test_server_handle;
  926. ASSERT_TRUE(test_server_handle =
  927. embedded_test_server()->StartAndReturnHandle());
  928. GURL page_url(embedded_test_server()->GetURL(kPage1Path));
  929. // The first NavigationState received should be empty.
  930. base::RunLoop run_loop;
  931. auto frame = FrameForTest::Create(context(), {});
  932. frame.navigation_listener().SetBeforeAckHook(base::BindRepeating(
  933. [](base::OnceClosure quit_loop,
  934. const fuchsia::web::NavigationState& change,
  935. OnNavigationStateChangedCallback callback) {
  936. std::move(quit_loop).Run();
  937. EXPECT_TRUE(change.IsEmpty());
  938. callback();
  939. },
  940. run_loop.QuitClosure()));
  941. run_loop.Run();
  942. frame.navigation_listener().SetBeforeAckHook({});
  943. // Navigate to a page and wait for the navigation to complete.
  944. EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
  945. fuchsia::web::LoadUrlParams(),
  946. page_url.spec()));
  947. frame.navigation_listener().RunUntilUrlEquals(page_url);
  948. // Attach a new navigation listener, we should get the new page state, even if
  949. // no new navigation occurred.
  950. TestNavigationListener navigation_listener;
  951. fidl::Binding<fuchsia::web::NavigationEventListener>
  952. navigation_listener_binding(&navigation_listener);
  953. frame.ptr()->SetNavigationEventListener2(
  954. navigation_listener_binding.NewBinding(), /*flags=*/{});
  955. navigation_listener.RunUntilUrlAndTitleEquals(page_url, kPage1Title);
  956. }
  957. // Check loading an invalid URL in NavigationController.LoadUrl() sets the right
  958. // error.
  959. IN_PROC_BROWSER_TEST_F(FrameImplTest, InvalidUrl) {
  960. auto frame = FrameForTest::Create(context(), {});
  961. base::test::TestFuture<fuchsia::web::NavigationController_LoadUrl_Result>
  962. result;
  963. auto controller = frame.GetNavigationController();
  964. controller->LoadUrl("http:google.com:foo", fuchsia::web::LoadUrlParams(),
  965. CallbackToFitFunction(result.GetCallback()));
  966. ASSERT_TRUE(result.Wait());
  967. ASSERT_TRUE(result.Get().is_err());
  968. EXPECT_EQ(result.Get().err(),
  969. fuchsia::web::NavigationControllerError::INVALID_URL);
  970. }
  971. // Check setting invalid headers in NavigationController.LoadUrl() sets the
  972. // right error.
  973. IN_PROC_BROWSER_TEST_F(FrameImplTest, InvalidHeader) {
  974. auto frame = FrameForTest::Create(context(), {});
  975. {
  976. // Set an invalid header name.
  977. fuchsia::web::LoadUrlParams load_url_params;
  978. fuchsia::net::http::Header header;
  979. header.name = StringToBytes("Invalid:Header");
  980. header.value = StringToBytes("1");
  981. load_url_params.set_headers({header});
  982. base::test::TestFuture<fuchsia::web::NavigationController_LoadUrl_Result>
  983. result;
  984. auto controller = frame.GetNavigationController();
  985. controller->LoadUrl("http://site.ext/", std::move(load_url_params),
  986. CallbackToFitFunction(result.GetCallback()));
  987. ASSERT_TRUE(result.Wait());
  988. ASSERT_TRUE(result.Get().is_err());
  989. EXPECT_EQ(result.Get().err(),
  990. fuchsia::web::NavigationControllerError::INVALID_HEADER);
  991. }
  992. {
  993. // Set an invalid header value.
  994. fuchsia::web::LoadUrlParams load_url_params;
  995. fuchsia::net::http::Header header;
  996. header.name = StringToBytes("Header");
  997. header.value = StringToBytes("Invalid\rValue");
  998. load_url_params.set_headers({header});
  999. base::test::TestFuture<fuchsia::web::NavigationController_LoadUrl_Result>
  1000. result;
  1001. auto controller = frame.GetNavigationController();
  1002. controller->LoadUrl("http://site.ext/", std::move(load_url_params),
  1003. CallbackToFitFunction(result.GetCallback()));
  1004. ASSERT_TRUE(result.Wait());
  1005. ASSERT_TRUE(result.Get().is_err());
  1006. EXPECT_EQ(result.Get().err(),
  1007. fuchsia::web::NavigationControllerError::INVALID_HEADER);
  1008. }
  1009. }
  1010. IN_PROC_BROWSER_TEST_F(FrameImplTest, ContentAreaSettings) {
  1011. auto frame = FrameForTest::Create(context(), {});
  1012. base::RunLoop().RunUntilIdle();
  1013. auto* frame_impl = context_impl()->GetFrameImplForTest(&frame.ptr());
  1014. auto* web_contents = frame_impl->web_contents_for_test();
  1015. // Frame should start with default values in web_contents.
  1016. {
  1017. blink::web_pref::WebPreferences web_prefs =
  1018. web_contents->GetOrCreateWebPreferences();
  1019. EXPECT_FALSE(web_prefs.hide_scrollbars);
  1020. EXPECT_EQ(web_prefs.autoplay_policy,
  1021. blink::mojom::AutoplayPolicy::kDocumentUserActivationRequired);
  1022. EXPECT_EQ(web_prefs.preferred_color_scheme,
  1023. blink::mojom::PreferredColorScheme::kLight);
  1024. }
  1025. // SetContentAreaSettings with empty settings object should not change any
  1026. // existing settings.
  1027. {
  1028. fuchsia::web::ContentAreaSettings settings;
  1029. frame->SetContentAreaSettings(std::move(settings));
  1030. base::RunLoop().RunUntilIdle();
  1031. blink::web_pref::WebPreferences web_prefs =
  1032. web_contents->GetOrCreateWebPreferences();
  1033. EXPECT_FALSE(web_prefs.hide_scrollbars);
  1034. EXPECT_EQ(web_prefs.autoplay_policy,
  1035. blink::mojom::AutoplayPolicy::kDocumentUserActivationRequired);
  1036. EXPECT_EQ(web_prefs.preferred_color_scheme,
  1037. blink::mojom::PreferredColorScheme::kLight);
  1038. }
  1039. // Set hide_scrollbars field and expect that it's reflected in web_contents.
  1040. // Expect other fields to be unchanged.
  1041. {
  1042. fuchsia::web::ContentAreaSettings settings;
  1043. settings.set_hide_scrollbars(true);
  1044. frame->SetContentAreaSettings(std::move(settings));
  1045. base::RunLoop().RunUntilIdle();
  1046. blink::web_pref::WebPreferences web_prefs =
  1047. web_contents->GetOrCreateWebPreferences();
  1048. EXPECT_TRUE(web_prefs.hide_scrollbars);
  1049. EXPECT_EQ(web_prefs.autoplay_policy,
  1050. blink::mojom::AutoplayPolicy::kDocumentUserActivationRequired);
  1051. EXPECT_EQ(web_prefs.preferred_color_scheme,
  1052. blink::mojom::PreferredColorScheme::kLight);
  1053. }
  1054. // ResetContentAreaSettings should revert preferences to their default values
  1055. // in web_contents.
  1056. {
  1057. frame->ResetContentAreaSettings();
  1058. base::RunLoop().RunUntilIdle();
  1059. blink::web_pref::WebPreferences web_prefs =
  1060. web_contents->GetOrCreateWebPreferences();
  1061. EXPECT_FALSE(web_prefs.hide_scrollbars);
  1062. EXPECT_EQ(web_prefs.autoplay_policy,
  1063. blink::mojom::AutoplayPolicy::kDocumentUserActivationRequired);
  1064. EXPECT_EQ(web_prefs.preferred_color_scheme,
  1065. blink::mojom::PreferredColorScheme::kLight);
  1066. }
  1067. }