| 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 5dec7a88a0665071b5a302f8e3eb215b62b0cda2..3d564f92f8fba87cc70ec768b8597349d9f08a75 100644
|
| --- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
|
| +++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
|
| @@ -17,6 +17,7 @@
|
| #include "content/browser/frame_host/navigation_entry_impl.h"
|
| #include "content/browser/loader/resource_dispatcher_host_impl.h"
|
| #include "content/browser/web_contents/web_contents_impl.h"
|
| +#include "content/common/page_state_serialization.h"
|
| #include "content/common/site_isolation_policy.h"
|
| #include "content/public/browser/navigation_handle.h"
|
| #include "content/public/browser/render_view_host.h"
|
| @@ -3520,4 +3521,149 @@ IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
|
| EXPECT_EQ(frame_url_1, frame->current_url());
|
| }
|
|
|
| +// Ensure that we do not corrupt a NavigationEntry's PageState if a subframe
|
| +// forward navigation commits after we've already started another forward
|
| +// navigation in the main frame. See https://crbug.com/597322.
|
| +IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
|
| + ForwardInSubframeWithPendingForward) {
|
| + // Navigate to a page with an iframe.
|
| + GURL url_a(embedded_test_server()->GetURL(
|
| + "/navigation_controller/page_with_data_iframe.html"));
|
| + GURL frame_url_a1("data:text/html,Subframe");
|
| + EXPECT_TRUE(NavigateToURL(shell(), url_a));
|
| +
|
| + NavigationController& controller = shell()->web_contents()->GetController();
|
| + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
|
| + ->GetFrameTree()
|
| + ->root();
|
| + ASSERT_EQ(1U, root->child_count());
|
| + EXPECT_EQ(url_a, root->current_url());
|
| + FrameTreeNode* frame = root->child_at(0);
|
| + EXPECT_EQ(frame_url_a1, frame->current_url());
|
| +
|
| + // Navigate the iframe to a second page.
|
| + GURL frame_url_a2 = embedded_test_server()->GetURL(
|
| + "/navigation_controller/simple_page_1.html");
|
| + NavigateFrameToURL(frame, frame_url_a2);
|
| +
|
| + EXPECT_EQ(2, controller.GetEntryCount());
|
| + EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
|
| + EXPECT_EQ(url_a, root->current_url());
|
| + EXPECT_EQ(frame_url_a2, frame->current_url());
|
| +
|
| + // Navigate the top-level frame to another page with an iframe.
|
| + GURL url_b(embedded_test_server()->GetURL(
|
| + "/navigation_controller/page_with_iframe.html"));
|
| + GURL frame_url_b1(url::kAboutBlankURL);
|
| + EXPECT_TRUE(NavigateToURL(shell(), url_b));
|
| + EXPECT_EQ(3, controller.GetEntryCount());
|
| + EXPECT_EQ(url_b, root->current_url());
|
| + EXPECT_EQ(frame_url_b1, root->child_at(0)->current_url());
|
| +
|
| + // Go back two entries. The original frame URL should be back.
|
| + ASSERT_TRUE(controller.CanGoToOffset(-2));
|
| + controller.GoToOffset(-2);
|
| + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
|
| +
|
| + EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
|
| + EXPECT_EQ(url_a, root->current_url());
|
| + EXPECT_EQ(frame_url_a1, root->child_at(0)->current_url());
|
| +
|
| + // Go forward two times in a row, being careful that the subframe commits
|
| + // after the second forward navigation begins but before the main frame
|
| + // commits.
|
| + TestNavigationManager subframe_delayer(shell()->web_contents(), frame_url_a2);
|
| + TestNavigationManager mainframe_delayer(shell()->web_contents(), url_b);
|
| + controller.GoForward();
|
| + subframe_delayer.WaitForWillStartRequest();
|
| + controller.GoForward();
|
| + mainframe_delayer.WaitForWillStartRequest();
|
| + EXPECT_EQ(2, controller.GetPendingEntryIndex());
|
| +
|
| + // Let the subframe commit.
|
| + subframe_delayer.ResumeNavigation();
|
| + subframe_delayer.WaitForNavigationFinished();
|
| + EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
|
| + EXPECT_EQ(url_a, root->current_url());
|
| + EXPECT_EQ(frame_url_a2, root->child_at(0)->current_url());
|
| +
|
| + // Let the main frame commit.
|
| + mainframe_delayer.ResumeNavigation();
|
| + mainframe_delayer.WaitForNavigationFinished();
|
| + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
|
| + EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
|
| + EXPECT_EQ(url_b, root->current_url());
|
| + EXPECT_EQ(frame_url_b1, root->child_at(0)->current_url());
|
| +
|
| + // Check the PageState of the previous entry to ensure it isn't corrupted.
|
| + NavigationEntry* entry = controller.GetEntryAtIndex(1);
|
| + EXPECT_EQ(url_a, entry->GetURL());
|
| + ExplodedPageState exploded_state;
|
| + EXPECT_TRUE(
|
| + DecodePageState(entry->GetPageState().ToEncodedData(), &exploded_state));
|
| + EXPECT_EQ(url_a, GURL(exploded_state.top.url_string.string()));
|
| + EXPECT_EQ(frame_url_a2,
|
| + GURL(exploded_state.top.children.at(0).url_string.string()));
|
| +}
|
| +
|
| +// Ensure that forward navigations in cloned tabs can commit if they redirect to
|
| +// a different site than before. This causes the navigation's item sequence
|
| +// number to change, meaning that we can't use it for determining whether the
|
| +// commit matches the history item. See https://crbug.com/600238.
|
| +IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
|
| + ForwardRedirectWithNoCommittedEntry) {
|
| + NavigationController& controller = shell()->web_contents()->GetController();
|
| + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
|
| + ->GetFrameTree()
|
| + ->root();
|
| +
|
| + // Put 2 pages in history.
|
| + GURL url_1(embedded_test_server()->GetURL(
|
| + "/navigation_controller/simple_page_1.html"));
|
| + EXPECT_TRUE(NavigateToURL(shell(), url_1));
|
| +
|
| + GURL url_2(embedded_test_server()->GetURL(
|
| + "/navigation_controller/simple_page_2.html"));
|
| + EXPECT_TRUE(NavigateToURL(shell(), url_2));
|
| +
|
| + EXPECT_EQ(url_2, root->current_url());
|
| + EXPECT_EQ(2, controller.GetEntryCount());
|
| + EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
|
| +
|
| + // Do a replaceState to a URL that will redirect when we come back to it via
|
| + // session history.
|
| + GURL url_3(embedded_test_server()->GetURL(
|
| + "foo.com", "/navigation_controller/page_with_links.html"));
|
| + {
|
| + TestNavigationObserver observer(shell()->web_contents());
|
| + std::string script =
|
| + "history.replaceState({}, '', '/server-redirect?" + url_3.spec() + "')";
|
| + EXPECT_TRUE(ExecuteScript(root->current_frame_host(), script));
|
| + observer.Wait();
|
| + }
|
| +
|
| + // Go back.
|
| + controller.GoBack();
|
| + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
|
| + EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
|
| + EXPECT_EQ(url_1, root->current_url());
|
| +
|
| + // Clone the tab without navigating it.
|
| + std::unique_ptr<WebContentsImpl> new_tab(
|
| + static_cast<WebContentsImpl*>(shell()->web_contents()->Clone()));
|
| + NavigationController& new_controller = new_tab->GetController();
|
| + FrameTreeNode* new_root = new_tab->GetFrameTree()->root();
|
| + EXPECT_TRUE(new_controller.IsInitialNavigation());
|
| + EXPECT_TRUE(new_controller.NeedsReload());
|
| +
|
| + // Go forward in the new tab.
|
| + {
|
| + TestNavigationObserver observer(new_tab.get());
|
| + new_controller.GoForward();
|
| + observer.Wait();
|
| + }
|
| + EXPECT_TRUE(new_root->current_frame_host()->IsRenderFrameLive());
|
| + EXPECT_EQ(url_3, new_root->current_url());
|
| +}
|
| +
|
| } // namespace content
|
|
|