Index: content/browser/frame_host/navigation_controller_impl_browsertest.cc |
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc |
index e88f507f5769faa5bf3ec34f341fde320a2ffb90..b95539518570c76dc9db4711c94f047a9b2e98ab 100644 |
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc |
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc |
@@ -3041,6 +3041,111 @@ IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, |
EXPECT_EQ(data_url, entry2->root_node()->children[1]->frame_entry->url()); |
} |
+// Allows waiting until an URL with a data scheme commits in any frame. |
+class DataUrlCommitObserver : public WebContentsObserver { |
+ public: |
+ explicit DataUrlCommitObserver(WebContents* web_contents) |
+ : WebContentsObserver(web_contents), |
+ message_loop_runner_(new MessageLoopRunner) {} |
+ |
+ void Wait() { message_loop_runner_->Run(); } |
+ |
+ private: |
+ void DidFinishNavigation(NavigationHandle* navigation_handle) override { |
+ if (navigation_handle->HasCommitted() && |
+ !navigation_handle->IsErrorPage() && |
+ navigation_handle->GetURL().scheme() == "data") |
+ message_loop_runner_->Quit(); |
+ } |
+ |
+ // The MessageLoopRunner used to spin the message loop. |
+ scoped_refptr<MessageLoopRunner> message_loop_runner_; |
+}; |
+ |
+// Verify that dynamically generated iframes load properly during a history |
+// navigation if no history item can be found for them. |
+// See https://crbug.com/649345. |
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, |
+ FrameNavigationEntry_DynamicSubframeHistoryFallback) { |
+ // This test only makes sense when subframe FrameNavigationEntries are in use. |
+ if (!SiteIsolationPolicy::UseSubframeNavigationEntries()) |
+ return; |
+ |
+ // 1. Start on a page with a script-generated iframe. The iframe has a |
+ // dynamic name, starts at about:blank, and gets navigated to a dynamic data |
+ // URL as the page is loading. |
+ GURL main_url_a(embedded_test_server()->GetURL( |
+ "a.com", "/navigation_controller/dynamic_iframe.html")); |
+ { |
+ // Wait until the data URL has committed, even if load stop happens after |
+ // about:blank load. |
+ DataUrlCommitObserver data_observer(shell()->web_contents()); |
+ EXPECT_TRUE(NavigateToURL(shell(), main_url_a)); |
+ data_observer.Wait(); |
+ } |
+ const NavigationControllerImpl& controller = |
+ static_cast<const NavigationControllerImpl&>( |
+ shell()->web_contents()->GetController()); |
+ FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
+ ->GetFrameTree() |
+ ->root(); |
+ ASSERT_EQ(1U, root->child_count()); |
+ ASSERT_EQ(0U, root->child_at(0)->child_count()); |
+ EXPECT_EQ(main_url_a, root->current_url()); |
+ EXPECT_EQ("data", root->child_at(0)->current_url().scheme()); |
+ |
+ EXPECT_EQ(1, controller.GetEntryCount()); |
+ EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); |
+ NavigationEntryImpl* entry1 = controller.GetLastCommittedEntry(); |
+ |
+ // The entry should have a FrameNavigationEntry for the data subframe. |
+ ASSERT_EQ(1U, entry1->root_node()->children.size()); |
+ EXPECT_EQ("data", |
+ entry1->root_node()->children[0]->frame_entry->url().scheme()); |
+ |
+ // 2. Navigate main frame cross-site, destroying the frames. |
+ GURL main_url_b(embedded_test_server()->GetURL( |
+ "b.com", "/navigation_controller/simple_page_2.html")); |
+ EXPECT_TRUE(NavigateToURL(shell(), main_url_b)); |
+ ASSERT_EQ(0U, root->child_count()); |
+ EXPECT_EQ(main_url_b, root->current_url()); |
+ |
+ EXPECT_EQ(2, controller.GetEntryCount()); |
+ EXPECT_EQ(1, controller.GetLastCommittedEntryIndex()); |
+ NavigationEntryImpl* entry2 = controller.GetLastCommittedEntry(); |
+ EXPECT_EQ(0U, entry2->root_node()->children.size()); |
+ |
+ // 3. Go back, recreating the iframe. The subframe will have a new name this |
+ // time, so we won't find a history item for it. We should let the new data |
+ // URL be loaded into it, rather than clobbering it with an about:blank page. |
+ { |
+ // Wait until the data URL has committed, even if load stop happens first. |
+ DataUrlCommitObserver back_load_observer(shell()->web_contents()); |
+ shell()->web_contents()->GetController().GoBack(); |
+ back_load_observer.Wait(); |
+ } |
+ EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); |
+ ASSERT_EQ(1U, root->child_count()); |
+ EXPECT_EQ(main_url_a, root->current_url()); |
+ EXPECT_EQ("data", root->child_at(0)->current_url().scheme()); |
+ |
+ EXPECT_EQ(2, controller.GetEntryCount()); |
+ EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); |
+ EXPECT_EQ(entry1, controller.GetLastCommittedEntry()); |
+ |
+ // The entry should have both the stale FrameNavigationEntry with the old |
+ // name and the new FrameNavigationEntry for the fallback navigation. |
+ ASSERT_EQ(2U, entry1->root_node()->children.size()); |
+ EXPECT_EQ("data", |
+ entry1->root_node()->children[0]->frame_entry->url().scheme()); |
+ EXPECT_EQ("data", |
+ entry1->root_node()->children[1]->frame_entry->url().scheme()); |
+ |
+ // The iframe commit should have been classified AUTO_SUBFRAME and not |
+ // NEW_SUBFRAME, so we should still be able to go forward. |
+ EXPECT_TRUE(shell()->web_contents()->GetController().CanGoForward()); |
+} |
+ |
// Verify that we don't clobber any content injected into the initial blank page |
// if we go back to an about:blank subframe. See https://crbug.com/626416. |
IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, |