blob: 34021e7d12448523a1ef983e72df630651d9076e [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <set>
#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/memory/ref_counted.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/webui/web_ui_impl.h"
#include "content/common/content_constants_internal.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/base/net_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
using base::ASCIIToUTF16;
namespace content {
namespace {
const char kOpenUrlViaClickTargetFunc[] =
"(function(url) {\n"
" var lnk = document.createElement(\"a\");\n"
" lnk.href = url;\n"
" lnk.target = \"_blank\";\n"
" document.body.appendChild(lnk);\n"
" lnk.click();\n"
"})";
// Adds a link with given url and target=_blank, and clicks on it.
void OpenUrlViaClickTarget(const internal::ToRenderFrameHost& adapter,
const GURL& url) {
EXPECT_TRUE(ExecuteScript(adapter,
std::string(kOpenUrlViaClickTargetFunc) + "(\"" + url.spec() + "\");"));
}
} // anonymous namespace
class RenderFrameHostManagerTest : public ContentBrowserTest {
public:
RenderFrameHostManagerTest() : foo_com_("foo.com") {
replace_host_.SetHostStr(foo_com_);
}
static bool GetFilePathWithHostAndPortReplacement(
const std::string& original_file_path,
const net::HostPortPair& host_port_pair,
std::string* replacement_path) {
std::vector<net::SpawnedTestServer::StringPair> replacement_text;
replacement_text.push_back(
make_pair("REPLACE_WITH_HOST_AND_PORT", host_port_pair.ToString()));
return net::SpawnedTestServer::GetFilePathWithReplacements(
original_file_path, replacement_text, replacement_path);
}
void StartServer() {
// Support multiple sites on the test server.
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(test_server()->Start());
foo_host_port_ = test_server()->host_port_pair();
foo_host_port_.set_host(foo_com_);
}
// Returns a URL on foo.com with the given path.
GURL GetCrossSiteURL(const std::string& path) {
GURL cross_site_url(test_server()->GetURL(path));
return cross_site_url.ReplaceComponents(replace_host_);
}
protected:
std::string foo_com_;
GURL::Replacements replace_host_;
net::HostPortPair foo_host_port_;
};
// Web pages should not have script access to the swapped out page.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, NoScriptAccessAfterSwapOut) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Open a same-site link in a new window.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickSameSiteTargetedLink());",
&success));
EXPECT_TRUE(success);
Shell* new_shell = new_shell_observer.GetShell();
// Wait for the navigation in the new window to finish, if it hasn't.
WaitForLoadStop(new_shell->web_contents());
EXPECT_EQ("/files/navigate_opener.html",
new_shell->web_contents()->GetLastCommittedURL().path());
// Should have the same SiteInstance.
scoped_refptr<SiteInstance> blank_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, blank_site_instance);
// We should have access to the opened window's location.
success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(testScriptAccessToWindow());",
&success));
EXPECT_TRUE(success);
// Now navigate the new window to a different site.
NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
// We should no longer have script access to the opened window's location.
success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(testScriptAccessToWindow());",
&success));
EXPECT_FALSE(success);
}
// Test for crbug.com/24447. Following a cross-site link with rel=noreferrer
// and target=_blank should create a new SiteInstance.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
SwapProcessWithRelNoreferrerAndTargetBlank) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Test clicking a rel=noreferrer + target=blank link.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickNoRefTargetBlankLink());",
&success));
EXPECT_TRUE(success);
// Wait for the window to open.
Shell* new_shell = new_shell_observer.GetShell();
EXPECT_EQ("/files/title2.html",
new_shell->web_contents()->GetVisibleURL().path());
// Wait for the cross-site transition in the new tab to finish.
WaitForLoadStop(new_shell->web_contents());
WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
new_shell->web_contents());
EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->
pending_render_view_host());
// Should have a new SiteInstance.
scoped_refptr<SiteInstance> noref_blank_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, noref_blank_site_instance);
}
// As of crbug.com/69267, we create a new BrowsingInstance (and SiteInstance)
// for rel=noreferrer links in new windows, even to same site pages and named
// targets.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
SwapProcessWithSameSiteRelNoreferrer) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Test clicking a same-site rel=noreferrer + target=foo link.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickSameSiteNoRefTargetedLink());",
&success));
EXPECT_TRUE(success);
// Wait for the window to open.
Shell* new_shell = new_shell_observer.GetShell();
// Opens in new window.
EXPECT_EQ("/files/title2.html",
new_shell->web_contents()->GetVisibleURL().path());
// Wait for the cross-site transition in the new tab to finish.
WaitForLoadStop(new_shell->web_contents());
WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
new_shell->web_contents());
EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->
pending_render_view_host());
// Should have a new SiteInstance (in a new BrowsingInstance).
scoped_refptr<SiteInstance> noref_blank_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, noref_blank_site_instance);
}
// Test for crbug.com/24447. Following a cross-site link with just
// target=_blank should not create a new SiteInstance.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
DontSwapProcessWithOnlyTargetBlank) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Test clicking a target=blank link.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickTargetBlankLink());",
&success));
EXPECT_TRUE(success);
// Wait for the window to open.
Shell* new_shell = new_shell_observer.GetShell();
// Wait for the cross-site transition in the new tab to finish.
WaitForLoadStop(new_shell->web_contents());
EXPECT_EQ("/files/title2.html",
new_shell->web_contents()->GetLastCommittedURL().path());
// Should have the same SiteInstance.
scoped_refptr<SiteInstance> blank_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, blank_site_instance);
}
// Test for crbug.com/24447. Following a cross-site link with rel=noreferrer
// and no target=_blank should not create a new SiteInstance.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
DontSwapProcessWithOnlyRelNoreferrer) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Test clicking a rel=noreferrer link.
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickNoRefLink());",
&success));
EXPECT_TRUE(success);
// Wait for the cross-site transition in the current tab to finish.
WaitForLoadStop(shell()->web_contents());
// Opens in same window.
EXPECT_EQ(1u, Shell::windows().size());
EXPECT_EQ("/files/title2.html",
shell()->web_contents()->GetLastCommittedURL().path());
// Should have the same SiteInstance.
scoped_refptr<SiteInstance> noref_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, noref_site_instance);
}
// Test for crbug.com/116192. Targeted links should still work after the
// named target window has swapped processes.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
AllowTargetedNavigationsAfterSwap) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Test clicking a target=foo link.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickSameSiteTargetedLink());",
&success));
EXPECT_TRUE(success);
Shell* new_shell = new_shell_observer.GetShell();
// Wait for the navigation in the new tab to finish, if it hasn't.
WaitForLoadStop(new_shell->web_contents());
EXPECT_EQ("/files/navigate_opener.html",
new_shell->web_contents()->GetLastCommittedURL().path());
// Should have the same SiteInstance.
scoped_refptr<SiteInstance> blank_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, blank_site_instance);
// Now navigate the new tab to a different site.
GURL cross_site_url(GetCrossSiteURL("files/title1.html"));
NavigateToURL(new_shell, cross_site_url);
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
// Clicking the original link in the first tab should cause us to swap back.
TestNavigationObserver navigation_observer(new_shell->web_contents());
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickSameSiteTargetedLink());",
&success));
EXPECT_TRUE(success);
navigation_observer.Wait();
// Should have swapped back and shown the new window again.
scoped_refptr<SiteInstance> revisit_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, revisit_site_instance);
// If it navigates away to another process, the original window should
// still be able to close it (using a cross-process close message).
NavigateToURL(new_shell, cross_site_url);
EXPECT_EQ(new_site_instance.get(),
new_shell->web_contents()->GetSiteInstance());
WebContentsDestroyedWatcher close_watcher(new_shell->web_contents());
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(testCloseWindow());",
&success));
EXPECT_TRUE(success);
close_watcher.Wait();
}
// Test that setting the opener to null in a window affects cross-process
// navigations, including those to existing entries. http://crbug.com/156669.
// This test crashes under ThreadSanitizer, http://crbug.com/356758.
#if defined(THREAD_SANITIZER)
#define MAYBE_DisownOpener DISABLED_DisownOpener
#else
#define MAYBE_DisownOpener DisownOpener
#endif
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, MAYBE_DisownOpener) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Test clicking a target=_blank link.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickSameSiteTargetBlankLink());",
&success));
EXPECT_TRUE(success);
Shell* new_shell = new_shell_observer.GetShell();
EXPECT_TRUE(new_shell->web_contents()->HasOpener());
// Wait for the navigation in the new tab to finish, if it hasn't.
WaitForLoadStop(new_shell->web_contents());
EXPECT_EQ("/files/title2.html",
new_shell->web_contents()->GetLastCommittedURL().path());
// Should have the same SiteInstance.
scoped_refptr<SiteInstance> blank_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, blank_site_instance);
// Now navigate the new tab to a different site.
NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
EXPECT_TRUE(new_shell->web_contents()->HasOpener());
// Now disown the opener.
EXPECT_TRUE(ExecuteScript(new_shell->web_contents(),
"window.opener = null;"));
EXPECT_FALSE(new_shell->web_contents()->HasOpener());
// Go back and ensure the opener is still null.
{
TestNavigationObserver back_nav_load_observer(new_shell->web_contents());
new_shell->web_contents()->GetController().GoBack();
back_nav_load_observer.Wait();
}
success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
new_shell->web_contents(),
"window.domAutomationController.send(window.opener == null);",
&success));
EXPECT_TRUE(success);
EXPECT_FALSE(new_shell->web_contents()->HasOpener());
// Now navigate forward again (creating a new process) and check opener.
NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
new_shell->web_contents(),
"window.domAutomationController.send(window.opener == null);",
&success));
EXPECT_TRUE(success);
EXPECT_FALSE(new_shell->web_contents()->HasOpener());
}
// Test that subframes can disown their openers. http://crbug.com/225528.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, DisownSubframeOpener) {
const GURL frame_url("data:text/html,<iframe name=\"foo\"></iframe>");
NavigateToURL(shell(), frame_url);
// Give the frame an opener using window.open.
EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
"window.open('about:blank','foo');"));
// Now disown the frame's opener. Shouldn't crash.
EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
"window.frames[0].opener = null;"));
}
// Test for crbug.com/99202. PostMessage calls should still work after
// navigating the source and target windows to different sites.
// Specifically:
// 1) Create 3 windows (opener, "foo", and _blank) and send "foo" cross-process.
// 2) Fail to post a message from "foo" to opener with the wrong target origin.
// 3) Post a message from "foo" to opener, which replies back to "foo".
// 4) Post a message from _blank to "foo".
// 5) Post a message from "foo" to a subframe of opener, which replies back.
// 6) Post a message from _blank to a subframe of "foo".
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
SupportCrossProcessPostMessage) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original SiteInstance and RVHM for later comparison.
WebContents* opener_contents = shell()->web_contents();
scoped_refptr<SiteInstance> orig_site_instance(
opener_contents->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
RenderFrameHostManager* opener_manager = static_cast<WebContentsImpl*>(
opener_contents)->GetRenderManagerForTesting();
// 1) Open two more windows, one named. These initially have openers but no
// reference to each other. We will later post a message between them.
// First, a named target=foo window.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
opener_contents,
"window.domAutomationController.send(clickSameSiteTargetedLink());",
&success));
EXPECT_TRUE(success);
Shell* new_shell = new_shell_observer.GetShell();
// Wait for the navigation in the new window to finish, if it hasn't, then
// send it to post_message.html on a different site.
WebContents* foo_contents = new_shell->web_contents();
WaitForLoadStop(foo_contents);
EXPECT_EQ("/files/navigate_opener.html",
foo_contents->GetLastCommittedURL().path());
NavigateToURL(new_shell, GetCrossSiteURL("files/post_message.html"));
scoped_refptr<SiteInstance> foo_site_instance(
foo_contents->GetSiteInstance());
EXPECT_NE(orig_site_instance, foo_site_instance);
// Second, a target=_blank window.
ShellAddedObserver new_shell_observer2;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickSameSiteTargetBlankLink());",
&success));
EXPECT_TRUE(success);
// Wait for the navigation in the new window to finish, if it hasn't, then
// send it to post_message.html on the original site.
Shell* new_shell2 = new_shell_observer2.GetShell();
WebContents* new_contents = new_shell2->web_contents();
WaitForLoadStop(new_contents);
EXPECT_EQ("/files/title2.html", new_contents->GetLastCommittedURL().path());
NavigateToURL(new_shell2, test_server()->GetURL("files/post_message.html"));
EXPECT_EQ(orig_site_instance.get(), new_contents->GetSiteInstance());
RenderFrameHostManager* new_manager =
static_cast<WebContentsImpl*>(new_contents)->GetRenderManagerForTesting();
// We now have three windows. The opener should have a swapped out RVH
// for the new SiteInstance, but the _blank window should not.
EXPECT_EQ(3u, Shell::windows().size());
EXPECT_TRUE(
opener_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
EXPECT_FALSE(
new_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
// 2) Fail to post a message from the foo window to the opener if the target
// origin is wrong. We won't see an error, but we can check for the right
// number of received messages below.
EXPECT_TRUE(ExecuteScriptAndExtractBool(
foo_contents,
"window.domAutomationController.send(postToOpener('msg',"
" 'http://google.com'));",
&success));
EXPECT_TRUE(success);
ASSERT_FALSE(
opener_manager->GetSwappedOutRenderViewHost(orig_site_instance.get()));
// 3) Post a message from the foo window to the opener. The opener will
// reply, causing the foo window to update its own title.
base::string16 expected_title = ASCIIToUTF16("msg");
TitleWatcher title_watcher(foo_contents, expected_title);
EXPECT_TRUE(ExecuteScriptAndExtractBool(
foo_contents,
"window.domAutomationController.send(postToOpener('msg','*'));",
&success));
EXPECT_TRUE(success);
ASSERT_FALSE(
opener_manager->GetSwappedOutRenderViewHost(orig_site_instance.get()));
ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
// We should have received only 1 message in the opener and "foo" tabs,
// and updated the title.
int opener_received_messages = 0;
EXPECT_TRUE(ExecuteScriptAndExtractInt(
opener_contents,
"window.domAutomationController.send(window.receivedMessages);",
&opener_received_messages));
int foo_received_messages = 0;
EXPECT_TRUE(ExecuteScriptAndExtractInt(
foo_contents,
"window.domAutomationController.send(window.receivedMessages);",
&foo_received_messages));
EXPECT_EQ(1, foo_received_messages);
EXPECT_EQ(1, opener_received_messages);
EXPECT_EQ(ASCIIToUTF16("msg"), foo_contents->GetTitle());
// 4) Now post a message from the _blank window to the foo window. The
// foo window will update its title and will not reply.
expected_title = ASCIIToUTF16("msg2");
TitleWatcher title_watcher2(foo_contents, expected_title);
EXPECT_TRUE(ExecuteScriptAndExtractBool(
new_contents,
"window.domAutomationController.send(postToFoo('msg2'));",
&success));
EXPECT_TRUE(success);
ASSERT_EQ(expected_title, title_watcher2.WaitAndGetTitle());
// This postMessage should have created a swapped out RVH for the new
// SiteInstance in the target=_blank window.
EXPECT_TRUE(
new_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
// TODO(nasko): Test subframe targeting of postMessage once
// http://crbug.com/153701 is fixed.
}
// Test for crbug.com/278336. MessagePorts should work cross-process. I.e.,
// messages which contain Transferables and get intercepted by
// RenderViewImpl::willCheckAndDispatchMessageEvent (because the RenderView is
// swapped out) should work.
// Specifically:
// 1) Create 2 windows (opener and "foo") and send "foo" cross-process.
// 2) Post a message containing a message port from opener to "foo".
// 3) Post a message from "foo" back to opener via the passed message port.
// The test will be enabled when the feature implementation lands.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
SupportCrossProcessPostMessageWithMessagePort) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original SiteInstance and RVHM for later comparison.
WebContents* opener_contents = shell()->web_contents();
scoped_refptr<SiteInstance> orig_site_instance(
opener_contents->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
RenderFrameHostManager* opener_manager = static_cast<WebContentsImpl*>(
opener_contents)->GetRenderManagerForTesting();
// 1) Open a named target=foo window. We will later post a message between the
// opener and the new window.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
opener_contents,
"window.domAutomationController.send(clickSameSiteTargetedLink());",
&success));
EXPECT_TRUE(success);
Shell* new_shell = new_shell_observer.GetShell();
// Wait for the navigation in the new window to finish, if it hasn't, then
// send it to post_message.html on a different site.
WebContents* foo_contents = new_shell->web_contents();
WaitForLoadStop(foo_contents);
EXPECT_EQ("/files/navigate_opener.html",
foo_contents->GetLastCommittedURL().path());
NavigateToURL(new_shell, GetCrossSiteURL("files/post_message.html"));
scoped_refptr<SiteInstance> foo_site_instance(
foo_contents->GetSiteInstance());
EXPECT_NE(orig_site_instance, foo_site_instance);
// We now have two windows. The opener should have a swapped out RVH
// for the new SiteInstance.
EXPECT_EQ(2u, Shell::windows().size());
EXPECT_TRUE(
opener_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
// 2) Post a message containing a MessagePort from opener to the the foo
// window. The foo window will reply via the passed port, causing the opener
// to update its own title.
base::string16 expected_title = ASCIIToUTF16("msg-back-via-port");
TitleWatcher title_observer(opener_contents, expected_title);
EXPECT_TRUE(ExecuteScriptAndExtractBool(
opener_contents,
"window.domAutomationController.send(postWithPortToFoo());",
&success));
EXPECT_TRUE(success);
ASSERT_FALSE(
opener_manager->GetSwappedOutRenderViewHost(orig_site_instance.get()));
ASSERT_EQ(expected_title, title_observer.WaitAndGetTitle());
// Check message counts.
int opener_received_messages_via_port = 0;
EXPECT_TRUE(ExecuteScriptAndExtractInt(
opener_contents,
"window.domAutomationController.send(window.receivedMessagesViaPort);",
&opener_received_messages_via_port));
int foo_received_messages = 0;
EXPECT_TRUE(ExecuteScriptAndExtractInt(
foo_contents,
"window.domAutomationController.send(window.receivedMessages);",
&foo_received_messages));
int foo_received_messages_with_port = 0;
EXPECT_TRUE(ExecuteScriptAndExtractInt(
foo_contents,
"window.domAutomationController.send(window.receivedMessagesWithPort);",
&foo_received_messages_with_port));
EXPECT_EQ(1, foo_received_messages);
EXPECT_EQ(1, foo_received_messages_with_port);
EXPECT_EQ(1, opener_received_messages_via_port);
EXPECT_EQ(ASCIIToUTF16("msg-with-port"), foo_contents->GetTitle());
EXPECT_EQ(ASCIIToUTF16("msg-back-via-port"), opener_contents->GetTitle());
}
// Test for crbug.com/116192. Navigations to a window's opener should
// still work after a process swap.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
AllowTargetedNavigationsInOpenerAfterSwap) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original tab and SiteInstance for later comparison.
WebContents* orig_contents = shell()->web_contents();
scoped_refptr<SiteInstance> orig_site_instance(
orig_contents->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Test clicking a target=foo link.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
orig_contents,
"window.domAutomationController.send(clickSameSiteTargetedLink());",
&success));
EXPECT_TRUE(success);
Shell* new_shell = new_shell_observer.GetShell();
// Wait for the navigation in the new window to finish, if it hasn't.
WaitForLoadStop(new_shell->web_contents());
EXPECT_EQ("/files/navigate_opener.html",
new_shell->web_contents()->GetLastCommittedURL().path());
// Should have the same SiteInstance.
scoped_refptr<SiteInstance> blank_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, blank_site_instance);
// Now navigate the original (opener) tab to a different site.
NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
scoped_refptr<SiteInstance> new_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
// The opened tab should be able to navigate the opener back to its process.
TestNavigationObserver navigation_observer(orig_contents);
EXPECT_TRUE(ExecuteScriptAndExtractBool(
new_shell->web_contents(),
"window.domAutomationController.send(navigateOpener());",
&success));
EXPECT_TRUE(success);
navigation_observer.Wait();
// Should have swapped back into this process.
scoped_refptr<SiteInstance> revisit_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, revisit_site_instance);
}
// Test that opening a new window in the same SiteInstance and then navigating
// both windows to a different SiteInstance allows the first process to exit.
// See http://crbug.com/126333.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
ProcessExitWithSwappedOutViews) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Test clicking a target=foo link.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickSameSiteTargetedLink());",
&success));
EXPECT_TRUE(success);
Shell* new_shell = new_shell_observer.GetShell();
// Wait for the navigation in the new window to finish, if it hasn't.
WaitForLoadStop(new_shell->web_contents());
EXPECT_EQ("/files/navigate_opener.html",
new_shell->web_contents()->GetLastCommittedURL().path());
// Should have the same SiteInstance.
scoped_refptr<SiteInstance> opened_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, opened_site_instance);
// Now navigate the opened window to a different site.
NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
// The original process should still be alive, since it is still used in the
// first window.
RenderProcessHost* orig_process = orig_site_instance->GetProcess();
EXPECT_TRUE(orig_process->HasConnection());
// Navigate the first window to a different site as well. The original
// process should exit, since all of its views are now swapped out.
RenderProcessHostWatcher exit_observer(
orig_process,
RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
exit_observer.Wait();
scoped_refptr<SiteInstance> new_site_instance2(
shell()->web_contents()->GetSiteInstance());
EXPECT_EQ(new_site_instance, new_site_instance2);
}
// Test for crbug.com/76666. A cross-site navigation that fails with a 204
// error should not make us ignore future renderer-initiated navigations.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClickLinkAfter204Error) {
StartServer();
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Load a cross-site page that fails with a 204 error.
NavigateToURL(shell(), GetCrossSiteURL("nocontent"));
// We should still be looking at the normal page. Because we started from a
// blank new tab, the typed URL will still be visible until the user clears it
// manually. The last committed URL will be the previous page.
scoped_refptr<SiteInstance> post_nav_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, post_nav_site_instance);
EXPECT_EQ("/nocontent",
shell()->web_contents()->GetVisibleURL().path());
EXPECT_FALSE(
shell()->web_contents()->GetController().GetLastCommittedEntry());
// Renderer-initiated navigations should work.
base::string16 expected_title = ASCIIToUTF16("Title Of Awesomeness");
TitleWatcher title_watcher(shell()->web_contents(), expected_title);
GURL url = test_server()->GetURL("files/title2.html");
EXPECT_TRUE(ExecuteScript(
shell()->web_contents(),
base::StringPrintf("location.href = '%s'", url.spec().c_str())));
ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
// Opens in same tab.
EXPECT_EQ(1u, Shell::windows().size());
EXPECT_EQ("/files/title2.html",
shell()->web_contents()->GetLastCommittedURL().path());
// Should have the same SiteInstance.
scoped_refptr<SiteInstance> new_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, new_site_instance);
}
// Test for crbug.com/9682. We should show the URL for a pending renderer-
// initiated navigation in a new tab, until the content of the initial
// about:blank page is modified by another window. At that point, we should
// revert to showing about:blank to prevent a URL spoof.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ShowLoadingURLUntilSpoof) {
ASSERT_TRUE(test_server()->Start());
// Load a page that can open a URL that won't commit in a new window.
NavigateToURL(
shell(), test_server()->GetURL("files/click-nocontent-link.html"));
WebContents* orig_contents = shell()->web_contents();
// Click a /nocontent link that opens in a new window but never commits.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
orig_contents,
"window.domAutomationController.send(clickNoContentTargetedLink());",
&success));
EXPECT_TRUE(success);
// Wait for the window to open.
Shell* new_shell = new_shell_observer.GetShell();
// Ensure the destination URL is visible, because it is considered the
// initial navigation.
WebContents* contents = new_shell->web_contents();
EXPECT_TRUE(contents->GetController().IsInitialNavigation());
EXPECT_EQ("/nocontent",
contents->GetController().GetVisibleEntry()->GetURL().path());
// Now modify the contents of the new window from the opener. This will also
// modify the title of the document to give us something to listen for.
base::string16 expected_title = ASCIIToUTF16("Modified Title");
TitleWatcher title_watcher(contents, expected_title);
success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
orig_contents,
"window.domAutomationController.send(modifyNewWindow());",
&success));
EXPECT_TRUE(success);
ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
// At this point, we should no longer be showing the destination URL.
// The visible entry should be null, resulting in about:blank in the address
// bar.
EXPECT_FALSE(contents->GetController().GetVisibleEntry());
}
// Test for crbug.com/9682. We should not show the URL for a pending renderer-
// initiated navigation in a new tab if it is not the initial navigation. In
// this case, the renderer will not notify us of a modification, so we cannot
// show the pending URL without allowing a spoof.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
DontShowLoadingURLIfNotInitialNav) {
ASSERT_TRUE(test_server()->Start());
// Load a page that can open a URL that won't commit in a new window.
NavigateToURL(
shell(), test_server()->GetURL("files/click-nocontent-link.html"));
WebContents* orig_contents = shell()->web_contents();
// Click a /nocontent link that opens in a new window but never commits.
// By using an onclick handler that first creates the window, the slow
// navigation is not considered an initial navigation.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
orig_contents,
"window.domAutomationController.send("
"clickNoContentScriptedTargetedLink());",
&success));
EXPECT_TRUE(success);
// Wait for the window to open.
Shell* new_shell = new_shell_observer.GetShell();
// Ensure the destination URL is not visible, because it is not the initial
// navigation.
WebContents* contents = new_shell->web_contents();
EXPECT_FALSE(contents->GetController().IsInitialNavigation());
EXPECT_FALSE(contents->GetController().GetVisibleEntry());
}
// Crashes under ThreadSanitizer, http://crbug.com/356758.
#if defined(THREAD_SANITIZER)
#define MAYBE_BackForwardNotStale DISABLED_BackForwardNotStale
#else
#define MAYBE_BackForwardNotStale BackForwardNotStale
#endif
// Test for http://crbug.com/93427. Ensure that cross-site navigations
// do not cause back/forward navigations to be considered stale by the
// renderer.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, MAYBE_BackForwardNotStale) {
StartServer();
NavigateToURL(shell(), GURL(url::kAboutBlankURL));
// Visit a page on first site.
NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
// Visit three pages on second site.
NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
NavigateToURL(shell(), GetCrossSiteURL("files/title2.html"));
NavigateToURL(shell(), GetCrossSiteURL("files/title3.html"));
// History is now [blank, A1, B1, B2, *B3].
WebContents* contents = shell()->web_contents();
EXPECT_EQ(5, contents->GetController().GetEntryCount());
// Open another window in same process to keep this process alive.
Shell* new_shell = CreateBrowser();
NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
// Go back three times to first site.
{
TestNavigationObserver back_nav_load_observer(shell()->web_contents());
shell()->web_contents()->GetController().GoBack();
back_nav_load_observer.Wait();
}
{
TestNavigationObserver back_nav_load_observer(shell()->web_contents());
shell()->web_contents()->GetController().GoBack();
back_nav_load_observer.Wait();
}
{
TestNavigationObserver back_nav_load_observer(shell()->web_contents());
shell()->web_contents()->GetController().GoBack();
back_nav_load_observer.Wait();
}
// Now go forward twice to B2. Shouldn't be left spinning.
{
TestNavigationObserver forward_nav_load_observer(shell()->web_contents());
shell()->web_contents()->GetController().GoForward();
forward_nav_load_observer.Wait();
}
{
TestNavigationObserver forward_nav_load_observer(shell()->web_contents());
shell()->web_contents()->GetController().GoForward();
forward_nav_load_observer.Wait();
}
// Go back twice to first site.
{
TestNavigationObserver back_nav_load_observer(shell()->web_contents());
shell()->web_contents()->GetController().GoBack();
back_nav_load_observer.Wait();
}
{
TestNavigationObserver back_nav_load_observer(shell()->web_contents());
shell()->web_contents()->GetController().GoBack();
back_nav_load_observer.Wait();
}
// Now go forward directly to B3. Shouldn't be left spinning.
{
TestNavigationObserver forward_nav_load_observer(shell()->web_contents());
shell()->web_contents()->GetController().GoToIndex(4);
forward_nav_load_observer.Wait();
}
}
// Test for http://crbug.com/130016.
// Swapping out a render view should update its visiblity state.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
SwappedOutViewHasCorrectVisibilityState) {
StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
// Open a same-site link in a new widnow.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(clickSameSiteTargetedLink());",
&success));
EXPECT_TRUE(success);
Shell* new_shell = new_shell_observer.GetShell();
// Wait for the navigation in the new tab to finish, if it hasn't.
WaitForLoadStop(new_shell->web_contents());
EXPECT_EQ("/files/navigate_opener.html",
new_shell->web_contents()->GetLastCommittedURL().path());
RenderViewHost* rvh = new_shell->web_contents()->GetRenderViewHost();
EXPECT_TRUE(ExecuteScriptAndExtractBool(
rvh,
"window.domAutomationController.send("
" document.visibilityState == 'visible');",
&success));
EXPECT_TRUE(success);
// Now navigate the new window to a different site. This should swap out the
// tab's existing RenderView, causing it become hidden.
NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
EXPECT_TRUE(ExecuteScriptAndExtractBool(
rvh,
"window.domAutomationController.send("
" document.visibilityState == 'hidden');",
&success));
EXPECT_TRUE(success);
// Going back should make the previously swapped-out view to become visible
// again.
{
TestNavigationObserver back_nav_load_observer(new_shell->web_contents());
new_shell->web_contents()->GetController().GoBack();
back_nav_load_observer.Wait();
}
EXPECT_EQ("/files/navigate_opener.html",
new_shell->web_contents()->GetLastCommittedURL().path());
EXPECT_EQ(rvh, new_shell->web_contents()->GetRenderViewHost());
EXPECT_TRUE(ExecuteScriptAndExtractBool(
rvh,
"window.domAutomationController.send("
" document.visibilityState == 'visible');",
&success));
EXPECT_TRUE(success);
}
// This class ensures that all the given RenderViewHosts have properly been
// shutdown.
class RenderViewHostDestructionObserver : public WebContentsObserver {
public:
explicit RenderViewHostDestructionObserver(WebContents* web_contents)
: WebContentsObserver(web_contents) {}
virtual ~RenderViewHostDestructionObserver() {}
void EnsureRVHGetsDestructed(RenderViewHost* rvh) {
watched_render_view_hosts_.insert(rvh);
}
size_t GetNumberOfWatchedRenderViewHosts() const {
return watched_render_view_hosts_.size();
}
private:
// WebContentsObserver implementation:
virtual void RenderViewDeleted(RenderViewHost* rvh) OVERRIDE {
watched_render_view_hosts_.erase(rvh);
}
std::set<RenderViewHost*> watched_render_view_hosts_;
};
// Crashes under ThreadSanitizer, http://crbug.com/356758.
#if defined(THREAD_SANITIZER)
#define MAYBE_LeakingRenderViewHosts DISABLED_LeakingRenderViewHosts
#else
#define MAYBE_LeakingRenderViewHosts LeakingRenderViewHosts
#endif
// Test for crbug.com/90867. Make sure we don't leak render view hosts since
// they may cause crashes or memory corruptions when trying to call dead
// delegate_. This test also verifies crbug.com/117420 and crbug.com/143255 to
// ensure that a separate SiteInstance is created when navigating to view-source
// URLs, regardless of current URL.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
MAYBE_LeakingRenderViewHosts) {
StartServer();
// Observe the created render_view_host's to make sure they will not leak.
RenderViewHostDestructionObserver rvh_observers(shell()->web_contents());
GURL navigated_url(test_server()->GetURL("files/title2.html"));
GURL view_source_url(kViewSourceScheme + std::string(":") +
navigated_url.spec());
// Let's ensure that when we start with a blank window, navigating away to a
// view-source URL, we create a new SiteInstance.
RenderViewHost* blank_rvh = shell()->web_contents()->GetRenderViewHost();
SiteInstance* blank_site_instance = blank_rvh->GetSiteInstance();
EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), GURL::EmptyGURL());
EXPECT_EQ(blank_site_instance->GetSiteURL(), GURL::EmptyGURL());
rvh_observers.EnsureRVHGetsDestructed(blank_rvh);
// Now navigate to the view-source URL and ensure we got a different
// SiteInstance and RenderViewHost.
NavigateToURL(shell(), view_source_url);
EXPECT_NE(blank_rvh, shell()->web_contents()->GetRenderViewHost());
EXPECT_NE(blank_site_instance, shell()->web_contents()->
GetRenderViewHost()->GetSiteInstance());
rvh_observers.EnsureRVHGetsDestructed(
shell()->web_contents()->GetRenderViewHost());
// Load a random page and then navigate to view-source: of it.
// This used to cause two RVH instances for the same SiteInstance, which
// was a problem. This is no longer the case.
NavigateToURL(shell(), navigated_url);
SiteInstance* site_instance1 = shell()->web_contents()->
GetRenderViewHost()->GetSiteInstance();
rvh_observers.EnsureRVHGetsDestructed(
shell()->web_contents()->GetRenderViewHost());
NavigateToURL(shell(), view_source_url);
rvh_observers.EnsureRVHGetsDestructed(
shell()->web_contents()->GetRenderViewHost());
SiteInstance* site_instance2 = shell()->web_contents()->
GetRenderViewHost()->GetSiteInstance();
// Ensure that view-source navigations force a new SiteInstance.
EXPECT_NE(site_instance1, site_instance2);
// Now navigate to a different instance so that we swap out again.
NavigateToURL(shell(), GetCrossSiteURL("files/title2.html"));
rvh_observers.EnsureRVHGetsDestructed(
shell()->web_contents()->GetRenderViewHost());
// This used to leak a render view host.
shell()->Close();
RunAllPendingInMessageLoop(); // Needed on ChromeOS.
EXPECT_EQ(0U, rvh_observers.GetNumberOfWatchedRenderViewHosts());
}
// Test for crbug.com/143155. Frame tree updates during unload should not
// interrupt the intended navigation and show swappedout:// instead.
// Specifically:
// 1) Open 2 tabs in an HTTP SiteInstance, with a subframe in the opener.
// 2) Send the second tab to a different foo.com SiteInstance.
// This creates a swapped out opener for the first tab in the foo process.
// 3) Navigate the first tab to the foo.com SiteInstance, and have the first
// tab's unload handler remove its frame.
// This used to cause an update to the frame tree of the swapped out RV,
// just as it was navigating to a real page. That pre-empted the real
// navigation and visibly sent the tab to swappedout://.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
DontPreemptNavigationWithFrameTreeUpdate) {
StartServer();
// 1. Load a page that deletes its iframe during unload.
NavigateToURL(shell(),
test_server()->GetURL("files/remove_frame_on_unload.html"));
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
shell()->web_contents()->GetSiteInstance());
// Open a same-site page in a new window.
ShellAddedObserver new_shell_observer;
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
shell()->web_contents(),
"window.domAutomationController.send(openWindow());",
&success));
EXPECT_TRUE(success);
Shell* new_shell = new_shell_observer.GetShell();
// Wait for the navigation in the new window to finish, if it hasn't.
WaitForLoadStop(new_shell->web_contents());
EXPECT_EQ("/files/title1.html",
new_shell->web_contents()->GetLastCommittedURL().path());
// Should have the same SiteInstance.
EXPECT_EQ(orig_site_instance.get(),
new_shell->web_contents()->GetSiteInstance());
// 2. Send the second tab to a different process.
NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
// 3. Send the first tab to the second tab's process.
NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
// Make sure it ends up at the right page.
WaitForLoadStop(shell()->web_contents());
EXPECT_EQ(GetCrossSiteURL("files/title1.html"),
shell()->web_contents()->GetLastCommittedURL());
EXPECT_EQ(new_site_instance.get(),
shell()->web_contents()->GetSiteInstance());
}
// Ensure that renderer-side debug URLs do not cause a process swap, since they
// are meant to run in the current page. We had a bug where we expected a
// BrowsingInstance swap to occur on pages like view-source and extensions,
// which broke chrome://crash and javascript: URLs.
// See http://crbug.com/335503.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, RendererDebugURLsDontSwap) {
ASSERT_TRUE(test_server()->Start());
GURL original_url(test_server()->GetURL("files/title2.html"));
GURL view_source_url(kViewSourceScheme + std::string(":") +
original_url.spec());
NavigateToURL(shell(), view_source_url);
// Check that javascript: URLs work.
base::string16 expected_title = ASCIIToUTF16("msg");
TitleWatcher title_watcher(shell()->web_contents(), expected_title);
shell()->LoadURL(GURL("javascript:document.title='msg'"));
ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
// Crash the renderer of the view-source page.
RenderProcessHostWatcher crash_observer(
shell()->web_contents(),
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
NavigateToURL(shell(), GURL(kChromeUICrashURL));
crash_observer.Wait();
}
// Ensure that renderer-side debug URLs don't take effect on crashed renderers.
// Otherwise, we might try to load an unprivileged about:blank page into a
// WebUI-enabled RenderProcessHost, failing a safety check in InitRenderView.
// See http://crbug.com/334214.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
IgnoreRendererDebugURLsWhenCrashed) {
// Visit a WebUI page with bindings.
GURL webui_url = GURL(std::string(kChromeUIScheme) + "://" +
std::string(kChromeUIGpuHost));
NavigateToURL(shell(), webui_url);
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetRenderProcessHost()->GetID()));
// Crash the renderer of the WebUI page.
RenderProcessHostWatcher crash_observer(
shell()->web_contents(),
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
NavigateToURL(shell(), GURL(kChromeUICrashURL));
crash_observer.Wait();
// Load the crash URL again but don't wait for any action. If it is not
// ignored this time, we will fail the WebUI CHECK in InitRenderView.
shell()->LoadURL(GURL(kChromeUICrashURL));
// Ensure that such URLs can still work as the initial navigation of a tab.
// We postpone the initial navigation of the tab using an empty GURL, so that
// we can add a watcher for crashes.
Shell* shell2 = Shell::CreateNewWindow(
shell()->web_contents()->GetBrowserContext(), GURL(), NULL,
MSG_ROUTING_NONE, gfx::Size());
RenderProcessHostWatcher crash_observer2(
shell2->web_contents(),
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
NavigateToURL(shell2, GURL(kChromeUIKillURL));
crash_observer2.Wait();
}
// Ensure that pending_and_current_web_ui_ is cleared when a URL commits.
// Otherwise it might get picked up by InitRenderView when granting bindings
// to other RenderViewHosts. See http://crbug.com/330811.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClearPendingWebUIOnCommit) {
// Visit a WebUI page with bindings.
GURL webui_url(GURL(std::string(kChromeUIScheme) + "://" +
std::string(kChromeUIGpuHost)));
NavigateToURL(shell(), webui_url);
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetRenderProcessHost()->GetID()));
WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
shell()->web_contents());
WebUIImpl* webui = web_contents->GetRenderManagerForTesting()->web_ui();
EXPECT_TRUE(webui);
EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->pending_web_ui());
// Navigate to another WebUI URL that reuses the WebUI object. Make sure we
// clear pending_web_ui() when it commits.
GURL webui_url2(webui_url.spec() + "#foo");
NavigateToURL(shell(), webui_url2);
EXPECT_EQ(webui, web_contents->GetRenderManagerForTesting()->web_ui());
EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->pending_web_ui());
}
class RFHMProcessPerTabTest : public RenderFrameHostManagerTest {
public:
RFHMProcessPerTabTest() {}
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
command_line->AppendSwitch(switches::kProcessPerTab);
}
};
// Test that we still swap processes for BrowsingInstance changes even in
// --process-per-tab mode. See http://crbug.com/343017.
// Disabled on Android: http://crbug.com/345873.
// Crashes under ThreadSanitizer, http://crbug.com/356758.
#if defined(OS_ANDROID) || defined(THREAD_SANITIZER)
#define MAYBE_BackFromWebUI DISABLED_BackFromWebUI
#else
#define MAYBE_BackFromWebUI BackFromWebUI
#endif
IN_PROC_BROWSER_TEST_F(RFHMProcessPerTabTest, MAYBE_BackFromWebUI) {
ASSERT_TRUE(test_server()->Start());
GURL original_url(test_server()->GetURL("files/title2.html"));
NavigateToURL(shell(), original_url);
// Visit a WebUI page with bindings.
GURL webui_url(GURL(std::string(kChromeUIScheme) + "://" +
std::string(kChromeUIGpuHost)));
NavigateToURL(shell(), webui_url);
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetRenderProcessHost()->GetID()));
// Go back and ensure we have no WebUI bindings.
TestNavigationObserver back_nav_load_observer(shell()->web_contents());
shell()->web_contents()->GetController().GoBack();
back_nav_load_observer.Wait();
EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL());
EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetRenderProcessHost()->GetID()));
}
// crbug.com/372360
// The test loads url1, opens a link pointing to url2 in a new tab, and
// navigates the new tab to url1.
// The following is needed for the bug to happen:
// - url1 must require webui bindings;
// - navigating to url2 in the site instance of url1 should not swap
// browsing instances, but should require a new site instance.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, WebUIGetsBindings) {
GURL url1(std::string(kChromeUIScheme) + "://" +
std::string(kChromeUIGpuHost));
GURL url2(std::string(kChromeUIScheme) + "://" +
std::string(kChromeUIAccessibilityHost));
// Visit a WebUI page with bindings.
NavigateToURL(shell(), url1);
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetRenderProcessHost()->GetID()));
SiteInstance* site_instance1 = shell()->web_contents()->GetSiteInstance();
// Open a new tab. Initially it gets a render view in the original tab's
// current site instance.
TestNavigationObserver nav_observer(NULL);
nav_observer.StartWatchingNewWebContents();
ShellAddedObserver shao;
OpenUrlViaClickTarget(shell()->web_contents(), url2);
nav_observer.Wait();
Shell* new_shell = shao.GetShell();
WebContentsImpl* new_web_contents = static_cast<WebContentsImpl*>(
new_shell->web_contents());
SiteInstance* site_instance2 = new_web_contents->GetSiteInstance();
EXPECT_NE(site_instance2, site_instance1);
EXPECT_TRUE(site_instance2->IsRelatedSiteInstance(site_instance1));
RenderViewHost* initial_rvh = new_web_contents->
GetRenderManagerForTesting()->GetSwappedOutRenderViewHost(site_instance1);
ASSERT_TRUE(initial_rvh);
// The following condition is what was causing the bug.
EXPECT_EQ(0, initial_rvh->GetEnabledBindings());
// Navigate to url1 and check bindings.
NavigateToURL(new_shell, url1);
// The navigation should have used the first SiteInstance, otherwise
// |initial_rvh| did not have a chance to be used.
EXPECT_EQ(new_web_contents->GetSiteInstance(), site_instance1);
EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
new_web_contents->GetRenderViewHost()->GetEnabledBindings());
}
} // namespace content