| 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 c49846cf376a6df6dada3efe4bbb8b171a98bf92..5f1b36aa4217b8e478d68c38f4377674e5e3ff01 100644
|
| --- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
|
| +++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
|
| @@ -44,6 +44,21 @@
|
| #include "net/test/embedded_test_server/embedded_test_server.h"
|
| #include "net/test/url_request/url_request_failed_job.h"
|
|
|
| +namespace {
|
| +
|
| +static std::string kAddNamedFrameScript =
|
| + "var f = document.createElement('iframe');"
|
| + "f.name = 'foo-frame-name';"
|
| + "document.body.appendChild(f);";
|
| +static std::string kAddFrameScript =
|
| + "var f = document.createElement('iframe');"
|
| + "document.body.appendChild(f);";
|
| +static std::string kRemoveFrameScript =
|
| + "var f = document.querySelector('iframe');"
|
| + "f.parentNode.removeChild(f);";
|
| +
|
| +} // namespace
|
| +
|
| namespace content {
|
|
|
| class NavigationControllerBrowserTest : public ContentBrowserTest {
|
| @@ -5426,4 +5441,179 @@ IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, PostViaOpenUrlMsg) {
|
| EXPECT_EQ("text=value\n", body);
|
| }
|
|
|
| +// Tests that inserting a named subframe into the FrameTree clears any
|
| +// previously existing FrameNavigationEntry objects for the same name.
|
| +// See https://crbug.com/628677.
|
| +IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
|
| + EnsureFrameNavigationEntriesClearedOnMismatch) {
|
| + WebContentsImpl* web_contents =
|
| + static_cast<WebContentsImpl*>(shell()->web_contents());
|
| + NavigationControllerImpl& controller = web_contents->GetController();
|
| + FrameTreeNode* root = web_contents->GetFrameTree()->root();
|
| +
|
| + // Start by navigating to a page with complex frame hierarchy.
|
| + GURL start_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
|
| + EXPECT_TRUE(NavigateToURL(shell(), start_url));
|
| + EXPECT_EQ(3U, root->child_count());
|
| + EXPECT_EQ(2U, root->child_at(0)->child_count());
|
| +
|
| + NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
|
| +
|
| + // Verify only the parts of the NavigationEntry affected by this test.
|
| + {
|
| + // * Main frame has 3 subframes.
|
| + FrameNavigationEntry* root_entry = entry->GetFrameEntry(root);
|
| + EXPECT_NE(nullptr, root_entry);
|
| + EXPECT_EQ("", root_entry->frame_unique_name());
|
| + if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
|
| + EXPECT_EQ(3U, entry->root_node()->children.size());
|
| +
|
| + // * The first child of the main frame is named and has two more children.
|
| + FrameTreeNode* frame = root->child_at(0);
|
| + FrameNavigationEntry* frame_entry = entry->GetFrameEntry(frame);
|
| + EXPECT_NE(nullptr, frame_entry);
|
| + EXPECT_EQ("1-1-name", frame_entry->frame_unique_name());
|
| + EXPECT_EQ(2U, entry->root_node()->children[0]->children.size());
|
| + }
|
| + }
|
| +
|
| + // Removing the first child of the main frame should remove the corresponding
|
| + // FrameTreeNode.
|
| + EXPECT_TRUE(ExecuteScript(root, kRemoveFrameScript));
|
| + EXPECT_EQ(2U, root->child_count());
|
| +
|
| + // However, the FrameNavigationEntry objects for the frame that was removed
|
| + // should still be around.
|
| + {
|
| + FrameNavigationEntry* root_entry = entry->GetFrameEntry(root);
|
| + EXPECT_NE(nullptr, root_entry);
|
| + if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
|
| + EXPECT_EQ(3U, entry->root_node()->children.size());
|
| + EXPECT_EQ(2U, entry->root_node()->children[0]->children.size());
|
| + }
|
| + }
|
| +
|
| + // Now, insert a frame with the same name as the previously removed one
|
| + // at a different layer of the frame tree.
|
| + FrameTreeNode* subframe = root->child_at(1)->child_at(1)->child_at(0);
|
| + EXPECT_EQ(2U, root->child_at(1)->child_count());
|
| + EXPECT_EQ(0U, subframe->child_count());
|
| + std::string add_matching_name_frame_script =
|
| + "var f = document.createElement('iframe');"
|
| + "f.name = '1-1-name';"
|
| + "document.body.appendChild(f);";
|
| + EXPECT_TRUE(ExecuteScript(subframe, add_matching_name_frame_script));
|
| + EXPECT_EQ(1U, subframe->child_count());
|
| +
|
| + // Verify that the FrameNavigationEntry for the original frame is now gone.
|
| + {
|
| + FrameNavigationEntry* root_entry = entry->GetFrameEntry(root);
|
| + EXPECT_NE(nullptr, root_entry);
|
| + if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
|
| + EXPECT_EQ(2U, entry->root_node()->children.size());
|
| + }
|
| + }
|
| +}
|
| +
|
| +// Tests that sending a PageState update from a named subframe does not get
|
| +// incorrectly set on previously existing FrameNavigationEntry for the same
|
| +// name. It is similar to EnsureFrameNavigationEntriesClearedOnMismatch, but
|
| +// doesn't navigate the iframes to real URLs when added to the DOM.
|
| +// See https://crbug.com/628677.
|
| +IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
|
| + EnsureFrameNavigationEntriesClearedOnMismatchNoSrc) {
|
| + WebContentsImpl* web_contents =
|
| + static_cast<WebContentsImpl*>(shell()->web_contents());
|
| + FrameTreeNode* root = web_contents->GetFrameTree()->root();
|
| +
|
| + GURL start_url(embedded_test_server()->GetURL("/title1.html"));
|
| + EXPECT_TRUE(NavigateToURL(shell(), start_url));
|
| + NavigationEntryImpl* nav_entry =
|
| + web_contents->GetController().GetLastCommittedEntry();
|
| +
|
| + EXPECT_TRUE(ExecuteScript(root, kAddNamedFrameScript));
|
| + EXPECT_EQ(1U, root->child_count());
|
| + EXPECT_EQ("foo-frame-name", root->child_at(0)->frame_name());
|
| +
|
| + EXPECT_TRUE(ExecuteScript(root, kRemoveFrameScript));
|
| + EXPECT_EQ(0U, root->child_count());
|
| +
|
| + // When a frame is removed from the page, the corresponding
|
| + // FrameNavigationEntry is not removed. This is done intentionally to support
|
| + // back-forward navigations in subframes and more intuitive UX on tab restore.
|
| + if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
|
| + EXPECT_EQ(1U, nav_entry->root_node()->children.size());
|
| + FrameNavigationEntry* frame_entry =
|
| + nav_entry->root_node()->children[0]->frame_entry.get();
|
| + EXPECT_EQ("foo-frame-name", frame_entry->frame_unique_name());
|
| + }
|
| +
|
| + EXPECT_TRUE(ExecuteScript(root, kAddFrameScript));
|
| + EXPECT_EQ(1U, root->child_count());
|
| + EXPECT_NE("foo-frame-name", root->child_at(0)->frame_name());
|
| +
|
| + // Add a nested frame with the previously used name.
|
| + EXPECT_TRUE(ExecuteScript(root->child_at(0), kAddNamedFrameScript));
|
| + EXPECT_EQ(1U, root->child_at(0)->child_count());
|
| + EXPECT_EQ("foo-frame-name", root->child_at(0)->child_at(0)->frame_name());
|
| +
|
| + if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
|
| + EXPECT_EQ(1U, nav_entry->root_node()->children.size());
|
| +
|
| + NavigationEntryImpl::TreeNode* tree_node =
|
| + nav_entry->root_node()->children[0];
|
| + EXPECT_EQ(1U, tree_node->children.size());
|
| +
|
| + tree_node = tree_node->children[0];
|
| + EXPECT_EQ(0U, tree_node->children.size());
|
| + EXPECT_EQ("foo-frame-name", tree_node->frame_entry->frame_unique_name());
|
| + }
|
| +
|
| + EXPECT_TRUE(ExecuteScript(root->child_at(0), kRemoveFrameScript));
|
| + EXPECT_EQ(0U, root->child_at(0)->child_count());
|
| +}
|
| +
|
| +// This test ensures that the comparison of tree position between a
|
| +// FrameTreeNode and FrameNavigationEntry works correctly for matching
|
| +// first-level frames.
|
| +IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
|
| + EnsureFirstLevelFrameNavigationEntriesMatch) {
|
| + WebContentsImpl* web_contents =
|
| + static_cast<WebContentsImpl*>(shell()->web_contents());
|
| + FrameTreeNode* root = web_contents->GetFrameTree()->root();
|
| + NavigationEntryImpl::TreeNode* tree_node = nullptr;
|
| +
|
| + GURL start_url(embedded_test_server()->GetURL("/title1.html"));
|
| + EXPECT_TRUE(NavigateToURL(shell(), start_url));
|
| + NavigationEntryImpl* nav_entry =
|
| + web_contents->GetController().GetLastCommittedEntry();
|
| +
|
| + // Add, then remove a named frame. It will create a FrameNavigationEntry
|
| + // for the name and leave it around.
|
| + EXPECT_TRUE(ExecuteScript(root, kAddNamedFrameScript));
|
| + EXPECT_EQ(1U, root->child_count());
|
| + if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
|
| + EXPECT_EQ(1U, nav_entry->root_node()->children.size());
|
| + tree_node = nav_entry->root_node()->children[0];
|
| + }
|
| +
|
| + EXPECT_TRUE(ExecuteScript(root, kRemoveFrameScript));
|
| + EXPECT_EQ(0U, root->child_count());
|
| + if (SiteIsolationPolicy::UseSubframeNavigationEntries())
|
| + EXPECT_EQ(1U, nav_entry->root_node()->children.size());
|
| +
|
| + // Add another frame with the same name as before. The matching logic
|
| + // should consider them the same and result in the FrameNavigationEntry
|
| + // being reused.
|
| + EXPECT_TRUE(ExecuteScript(root, kAddNamedFrameScript));
|
| + EXPECT_EQ(1U, root->child_count());
|
| + if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
|
| + EXPECT_EQ(1U, nav_entry->root_node()->children.size());
|
| + EXPECT_EQ(tree_node, nav_entry->root_node()->children[0]);
|
| + }
|
| +
|
| + EXPECT_TRUE(ExecuteScript(root, kRemoveFrameScript));
|
| + EXPECT_EQ(0U, root->child_count());
|
| +}
|
| +
|
| } // namespace content
|
|
|