blob: 85c710d678c1870eef40f204d833e00ab9d582b3 [file] [log] [blame]
// Copyright (c) 2012 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 "content/browser/site_per_process_browsertest.h"
#include "base/command_line.h"
#include "base/strings/stringprintf.h"
#include "content/browser/frame_host/cross_process_frame_connector.h"
#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/navigator.h"
#include "content/browser/frame_host/render_frame_proxy_host.h"
#include "content/browser/frame_host/render_widget_host_view_child_frame.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "content/test/test_frame_navigation_observer.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace content {
class SitePerProcessWebContentsObserver: public WebContentsObserver {
public:
explicit SitePerProcessWebContentsObserver(WebContents* web_contents)
: WebContentsObserver(web_contents),
navigation_succeeded_(false) {}
~SitePerProcessWebContentsObserver() override {}
void DidStartProvisionalLoadForFrame(RenderFrameHost* render_frame_host,
const GURL& validated_url,
bool is_error_page,
bool is_iframe_srcdoc) override {
navigation_succeeded_ = false;
}
void DidFailProvisionalLoad(
RenderFrameHost* render_frame_host,
const GURL& validated_url,
int error_code,
const base::string16& error_description) override {
navigation_url_ = validated_url;
navigation_succeeded_ = false;
}
void DidCommitProvisionalLoadForFrame(
RenderFrameHost* render_frame_host,
const GURL& url,
ui::PageTransition transition_type) override {
navigation_url_ = url;
navigation_succeeded_ = true;
}
const GURL& navigation_url() const {
return navigation_url_;
}
int navigation_succeeded() const { return navigation_succeeded_; }
private:
GURL navigation_url_;
bool navigation_succeeded_;
DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver);
};
class RedirectNotificationObserver : public NotificationObserver {
public:
// Register to listen for notifications of the given type from either a
// specific source, or from all sources if |source| is
// NotificationService::AllSources().
RedirectNotificationObserver(int notification_type,
const NotificationSource& source);
~RedirectNotificationObserver() override;
// Wait until the specified notification occurs. If the notification was
// emitted between the construction of this object and this call then it
// returns immediately.
void Wait();
// Returns NotificationService::AllSources() if we haven't observed a
// notification yet.
const NotificationSource& source() const {
return source_;
}
const NotificationDetails& details() const {
return details_;
}
// NotificationObserver:
void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) override;
private:
bool seen_;
bool seen_twice_;
bool running_;
NotificationRegistrar registrar_;
NotificationSource source_;
NotificationDetails details_;
scoped_refptr<MessageLoopRunner> message_loop_runner_;
DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver);
};
RedirectNotificationObserver::RedirectNotificationObserver(
int notification_type,
const NotificationSource& source)
: seen_(false),
running_(false),
source_(NotificationService::AllSources()) {
registrar_.Add(this, notification_type, source);
}
RedirectNotificationObserver::~RedirectNotificationObserver() {}
void RedirectNotificationObserver::Wait() {
if (seen_ && seen_twice_)
return;
running_ = true;
message_loop_runner_ = new MessageLoopRunner;
message_loop_runner_->Run();
EXPECT_TRUE(seen_);
}
void RedirectNotificationObserver::Observe(
int type,
const NotificationSource& source,
const NotificationDetails& details) {
source_ = source;
details_ = details;
seen_twice_ = seen_;
seen_ = true;
if (!running_)
return;
message_loop_runner_->Quit();
running_ = false;
}
//
// SitePerProcessBrowserTest
//
SitePerProcessBrowserTest::SitePerProcessBrowserTest() {
};
void SitePerProcessBrowserTest::StartFrameAtDataURL() {
std::string data_url_script =
"var iframes = document.getElementById('test');iframes.src="
"'data:text/html,dataurl';";
ASSERT_TRUE(ExecuteScript(shell()->web_contents(), data_url_script));
}
bool SitePerProcessBrowserTest::NavigateIframeToURL(Shell* window,
const GURL& url,
std::string iframe_id) {
// TODO(creis): This should wait for LOAD_STOP, but cross-site subframe
// navigations generate extra DidStartLoading and DidStopLoading messages.
// Until we replace swappedout:// with frame proxies, we need to listen for
// something else. For now, we trigger NEW_SUBFRAME navigations and listen
// for commit.
std::string script = base::StringPrintf(
"setTimeout(\""
"var iframes = document.getElementById('%s');iframes.src='%s';"
"\",0)",
iframe_id.c_str(), url.spec().c_str());
WindowedNotificationObserver load_observer(
NOTIFICATION_NAV_ENTRY_COMMITTED,
Source<NavigationController>(
&window->web_contents()->GetController()));
bool result = ExecuteScript(window->web_contents(), script);
load_observer.Wait();
return result;
}
void SitePerProcessBrowserTest::SetUpCommandLine(CommandLine* command_line) {
command_line->AppendSwitch(switches::kSitePerProcess);
};
void SitePerProcessBrowserTest::SetUpOnMainThread() {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
SetupCrossSiteRedirector(embedded_test_server());
}
// Ensure that navigating subframes in --site-per-process mode works and the
// correct documents are committed.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteIframe) {
GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
NavigateToURL(shell(), main_url);
// It is safe to obtain the root frame tree node here, as it doesn't change.
FrameTreeNode* root =
static_cast<WebContentsImpl*>(shell()->web_contents())->
GetFrameTree()->root();
SitePerProcessWebContentsObserver observer(shell()->web_contents());
// Load same-site page into iframe.
FrameTreeNode* child = root->child_at(0);
GURL http_url(embedded_test_server()->GetURL("/title1.html"));
NavigateFrameToURL(child, http_url);
EXPECT_EQ(http_url, observer.navigation_url());
EXPECT_TRUE(observer.navigation_succeeded());
{
// There should be only one RenderWidgetHost when there are no
// cross-process iframes.
std::set<RenderWidgetHostView*> views_set =
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetRenderWidgetHostViewsInTree();
EXPECT_EQ(1U, views_set.size());
}
RenderFrameProxyHost* proxy_to_parent =
child->render_manager()->GetRenderFrameProxyHost(
shell()->web_contents()->GetSiteInstance());
EXPECT_FALSE(proxy_to_parent);
// Load cross-site page into iframe.
NavigateFrameToURL(
root->child_at(0),
embedded_test_server()->GetURL("/cross-site/foo.com/title2.html"));
// Verify that the navigation succeeded and the expected URL was loaded.
EXPECT_TRUE(observer.navigation_succeeded());
EXPECT_EQ(embedded_test_server()->base_url().scheme(),
observer.navigation_url().scheme());
EXPECT_EQ("foo.com", observer.navigation_url().host());
EXPECT_EQ("/title2.html", observer.navigation_url().path());
// Ensure that we have created a new process for the subframe.
ASSERT_EQ(2U, root->child_count());
SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance();
RenderViewHost* rvh = child->current_frame_host()->render_view_host();
RenderProcessHost* rph = child->current_frame_host()->GetProcess();
EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh);
EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph);
{
// There should be now two RenderWidgetHosts, one for each process
// rendering a frame.
std::set<RenderWidgetHostView*> views_set =
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetRenderWidgetHostViewsInTree();
EXPECT_EQ(2U, views_set.size());
}
proxy_to_parent = child->render_manager()->GetProxyToParent();
EXPECT_TRUE(proxy_to_parent);
EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
EXPECT_EQ(
rvh->GetView(),
proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
// Load another cross-site page into the same iframe.
NavigateFrameToURL(
root->child_at(0),
embedded_test_server()->GetURL("/cross-site/bar.com/title3.html"));
EXPECT_TRUE(observer.navigation_succeeded());
EXPECT_EQ(embedded_test_server()->base_url().scheme(),
observer.navigation_url().scheme());
EXPECT_EQ("bar.com", observer.navigation_url().host());
EXPECT_EQ("/title3.html", observer.navigation_url().path());
// Check again that a new process is created and is different from the
// top level one and the previous one.
ASSERT_EQ(2U, root->child_count());
child = root->child_at(0);
EXPECT_NE(shell()->web_contents()->GetRenderViewHost(),
child->current_frame_host()->render_view_host());
EXPECT_NE(rvh, child->current_frame_host()->render_view_host());
EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
child->current_frame_host()->GetSiteInstance());
EXPECT_NE(site_instance,
child->current_frame_host()->GetSiteInstance());
EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(),
child->current_frame_host()->GetProcess());
EXPECT_NE(rph, child->current_frame_host()->GetProcess());
{
std::set<RenderWidgetHostView*> views_set =
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetRenderWidgetHostViewsInTree();
EXPECT_EQ(2U, views_set.size());
}
EXPECT_EQ(proxy_to_parent, child->render_manager()->GetProxyToParent());
EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
EXPECT_EQ(
child->current_frame_host()->render_view_host()->GetView(),
proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
}
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigateRemoteFrame) {
GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
NavigateToURL(shell(), main_url);
// It is safe to obtain the root frame tree node here, as it doesn't change.
FrameTreeNode* root =
static_cast<WebContentsImpl*>(shell()->web_contents())->
GetFrameTree()->root();
SitePerProcessWebContentsObserver observer(shell()->web_contents());
// Load same-site page into iframe.
FrameTreeNode* child = root->child_at(0);
GURL http_url(embedded_test_server()->GetURL("/title1.html"));
NavigateFrameToURL(child, http_url);
EXPECT_EQ(http_url, observer.navigation_url());
EXPECT_TRUE(observer.navigation_succeeded());
// Load cross-site page into iframe.
NavigateFrameToURL(
root->child_at(0),
embedded_test_server()->GetURL("/cross-site/foo.com/title2.html"));
EXPECT_TRUE(observer.navigation_succeeded());
EXPECT_EQ("foo.com", observer.navigation_url().host());
EXPECT_EQ("/title2.html", observer.navigation_url().path());
// Ensure that we have created a new process for the subframe.
ASSERT_EQ(2U, root->child_count());
SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance();
EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
// Emulate the main frame changing the src of the iframe such that it
// navigates cross-site.
NavigateIframeToURL(
shell(),
embedded_test_server()->GetURL("/cross-site/bar.com/title3.html"),
"test");
EXPECT_TRUE(observer.navigation_succeeded());
EXPECT_EQ("bar.com", observer.navigation_url().host());
EXPECT_EQ("/title3.html", observer.navigation_url().path());
// Check again that a new process is created and is different from the
// top level one and the previous one.
ASSERT_EQ(2U, root->child_count());
child = root->child_at(0);
EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
child->current_frame_host()->GetSiteInstance());
EXPECT_NE(site_instance,
child->current_frame_host()->GetSiteInstance());
// TODO(japhet): This currently causes an assertion in the renderer process.
// Enable when the assertion is fixed.
//NavigateFrameToURL(child, http_url);
//EXPECT_EQ(http_url, observer.navigation_url());
//EXPECT_TRUE(observer.navigation_succeeded());
//EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
// child->current_frame_host()->GetSiteInstance());
}
// Crash a subframe and ensures its children are cleared from the FrameTree.
// See http://crbug.com/338508.
// TODO(creis): Disabled for flakiness; see http://crbug.com/405582.
// TODO(creis): Enable this on Android when we can kill the process there.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DISABLED_CrashSubframe) {
GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
NavigateToURL(shell(), main_url);
StartFrameAtDataURL();
// These must stay in scope with replace_host.
GURL::Replacements replace_host;
std::string foo_com("foo.com");
// Load cross-site page into iframe.
EXPECT_TRUE(NavigateIframeToURL(
shell(),
embedded_test_server()->GetURL("/cross-site/foo.com/title2.html"),
"test"));
// Check the subframe process.
FrameTreeNode* root =
static_cast<WebContentsImpl*>(shell()->web_contents())->
GetFrameTree()->root();
ASSERT_EQ(1U, root->child_count());
FrameTreeNode* child = root->child_at(0);
EXPECT_EQ(main_url, root->current_url());
EXPECT_EQ("foo.com", child->current_url().host());
EXPECT_EQ("/title2.html", child->current_url().path());
EXPECT_TRUE(
child->current_frame_host()->render_view_host()->IsRenderViewLive());
EXPECT_TRUE(child->current_frame_host()->IsRenderFrameLive());
// Crash the subframe process.
RenderProcessHost* root_process = root->current_frame_host()->GetProcess();
RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
{
RenderProcessHostWatcher crash_observer(
child_process,
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
base::KillProcess(child_process->GetHandle(), 0, false);
crash_observer.Wait();
}
// Ensure that the child frame still exists but has been cleared.
EXPECT_EQ(1U, root->child_count());
EXPECT_EQ(main_url, root->current_url());
EXPECT_EQ(GURL(), child->current_url());
EXPECT_FALSE(
child->current_frame_host()->render_view_host()->IsRenderViewLive());
EXPECT_FALSE(child->current_frame_host()->IsRenderFrameLive());
EXPECT_FALSE(child->current_frame_host()->render_frame_created_);
// Now crash the top-level page to clear the child frame.
{
RenderProcessHostWatcher crash_observer(
root_process,
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
base::KillProcess(root_process->GetHandle(), 0, false);
crash_observer.Wait();
}
EXPECT_EQ(0U, root->child_count());
EXPECT_EQ(GURL(), root->current_url());
}
// TODO(nasko): Disable this test until out-of-process iframes is ready and the
// security checks are back in place.
// TODO(creis): Replace SpawnedTestServer with host_resolver to get test to run
// on Android (http://crbug.com/187570).
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
DISABLED_CrossSiteIframeRedirectOnce) {
ASSERT_TRUE(test_server()->Start());
net::SpawnedTestServer https_server(
net::SpawnedTestServer::TYPE_HTTPS,
net::SpawnedTestServer::kLocalhost,
base::FilePath(FILE_PATH_LITERAL("content/test/data")));
ASSERT_TRUE(https_server.Start());
GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
GURL http_url(test_server()->GetURL("files/title1.html"));
GURL https_url(https_server.GetURL("files/title1.html"));
NavigateToURL(shell(), main_url);
SitePerProcessWebContentsObserver observer(shell()->web_contents());
{
// Load cross-site client-redirect page into Iframe.
// Should be blocked.
GURL client_redirect_https_url(https_server.GetURL(
"client-redirect?files/title1.html"));
EXPECT_TRUE(NavigateIframeToURL(shell(),
client_redirect_https_url, "test"));
// DidFailProvisionalLoad when navigating to client_redirect_https_url.
EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
EXPECT_FALSE(observer.navigation_succeeded());
}
{
// Load cross-site server-redirect page into Iframe,
// which redirects to same-site page.
GURL server_redirect_http_url(https_server.GetURL(
"server-redirect?" + http_url.spec()));
EXPECT_TRUE(NavigateIframeToURL(shell(),
server_redirect_http_url, "test"));
EXPECT_EQ(observer.navigation_url(), http_url);
EXPECT_TRUE(observer.navigation_succeeded());
}
{
// Load cross-site server-redirect page into Iframe,
// which redirects to cross-site page.
GURL server_redirect_http_url(https_server.GetURL(
"server-redirect?files/title1.html"));
EXPECT_TRUE(NavigateIframeToURL(shell(),
server_redirect_http_url, "test"));
// DidFailProvisionalLoad when navigating to https_url.
EXPECT_EQ(observer.navigation_url(), https_url);
EXPECT_FALSE(observer.navigation_succeeded());
}
{
// Load same-site server-redirect page into Iframe,
// which redirects to cross-site page.
GURL server_redirect_http_url(test_server()->GetURL(
"server-redirect?" + https_url.spec()));
EXPECT_TRUE(NavigateIframeToURL(shell(),
server_redirect_http_url, "test"));
EXPECT_EQ(observer.navigation_url(), https_url);
EXPECT_FALSE(observer.navigation_succeeded());
}
{
// Load same-site client-redirect page into Iframe,
// which redirects to cross-site page.
GURL client_redirect_http_url(test_server()->GetURL(
"client-redirect?" + https_url.spec()));
RedirectNotificationObserver load_observer2(
NOTIFICATION_LOAD_STOP,
Source<NavigationController>(
&shell()->web_contents()->GetController()));
EXPECT_TRUE(NavigateIframeToURL(shell(),
client_redirect_http_url, "test"));
// Same-site Client-Redirect Page should be loaded successfully.
EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
EXPECT_TRUE(observer.navigation_succeeded());
// Redirecting to Cross-site Page should be blocked.
load_observer2.Wait();
EXPECT_EQ(observer.navigation_url(), https_url);
EXPECT_FALSE(observer.navigation_succeeded());
}
{
// Load same-site server-redirect page into Iframe,
// which redirects to same-site page.
GURL server_redirect_http_url(test_server()->GetURL(
"server-redirect?files/title1.html"));
EXPECT_TRUE(NavigateIframeToURL(shell(),
server_redirect_http_url, "test"));
EXPECT_EQ(observer.navigation_url(), http_url);
EXPECT_TRUE(observer.navigation_succeeded());
}
{
// Load same-site client-redirect page into Iframe,
// which redirects to same-site page.
GURL client_redirect_http_url(test_server()->GetURL(
"client-redirect?" + http_url.spec()));
RedirectNotificationObserver load_observer2(
NOTIFICATION_LOAD_STOP,
Source<NavigationController>(
&shell()->web_contents()->GetController()));
EXPECT_TRUE(NavigateIframeToURL(shell(),
client_redirect_http_url, "test"));
// Same-site Client-Redirect Page should be loaded successfully.
EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
EXPECT_TRUE(observer.navigation_succeeded());
// Redirecting to Same-site Page should be loaded successfully.
load_observer2.Wait();
EXPECT_EQ(observer.navigation_url(), http_url);
EXPECT_TRUE(observer.navigation_succeeded());
}
}
// TODO(nasko): Disable this test until out-of-process iframes is ready and the
// security checks are back in place.
// TODO(creis): Replace SpawnedTestServer with host_resolver to get test to run
// on Android (http://crbug.com/187570).
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
DISABLED_CrossSiteIframeRedirectTwice) {
ASSERT_TRUE(test_server()->Start());
net::SpawnedTestServer https_server(
net::SpawnedTestServer::TYPE_HTTPS,
net::SpawnedTestServer::kLocalhost,
base::FilePath(FILE_PATH_LITERAL("content/test/data")));
ASSERT_TRUE(https_server.Start());
GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
GURL http_url(test_server()->GetURL("files/title1.html"));
GURL https_url(https_server.GetURL("files/title1.html"));
NavigateToURL(shell(), main_url);
SitePerProcessWebContentsObserver observer(shell()->web_contents());
{
// Load client-redirect page pointing to a cross-site client-redirect page,
// which eventually redirects back to same-site page.
GURL client_redirect_https_url(https_server.GetURL(
"client-redirect?" + http_url.spec()));
GURL client_redirect_http_url(test_server()->GetURL(
"client-redirect?" + client_redirect_https_url.spec()));
// We should wait until second client redirect get cancelled.
RedirectNotificationObserver load_observer2(
NOTIFICATION_LOAD_STOP,
Source<NavigationController>(
&shell()->web_contents()->GetController()));
EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test"));
// DidFailProvisionalLoad when navigating to client_redirect_https_url.
load_observer2.Wait();
EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
EXPECT_FALSE(observer.navigation_succeeded());
}
{
// Load server-redirect page pointing to a cross-site server-redirect page,
// which eventually redirect back to same-site page.
GURL server_redirect_https_url(https_server.GetURL(
"server-redirect?" + http_url.spec()));
GURL server_redirect_http_url(test_server()->GetURL(
"server-redirect?" + server_redirect_https_url.spec()));
EXPECT_TRUE(NavigateIframeToURL(shell(),
server_redirect_http_url, "test"));
EXPECT_EQ(observer.navigation_url(), http_url);
EXPECT_TRUE(observer.navigation_succeeded());
}
{
// Load server-redirect page pointing to a cross-site server-redirect page,
// which eventually redirects back to cross-site page.
GURL server_redirect_https_url(https_server.GetURL(
"server-redirect?" + https_url.spec()));
GURL server_redirect_http_url(test_server()->GetURL(
"server-redirect?" + server_redirect_https_url.spec()));
EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
// DidFailProvisionalLoad when navigating to https_url.
EXPECT_EQ(observer.navigation_url(), https_url);
EXPECT_FALSE(observer.navigation_succeeded());
}
{
// Load server-redirect page pointing to a cross-site client-redirect page,
// which eventually redirects back to same-site page.
GURL client_redirect_http_url(https_server.GetURL(
"client-redirect?" + http_url.spec()));
GURL server_redirect_http_url(test_server()->GetURL(
"server-redirect?" + client_redirect_http_url.spec()));
EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
// DidFailProvisionalLoad when navigating to client_redirect_http_url.
EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
EXPECT_FALSE(observer.navigation_succeeded());
}
}
// Ensure that when navigating a frame cross-process RenderFrameProxyHosts are
// created in the FrameTree skipping the subtree of the navigating frame.
//
// Disabled on Mac due to flakiness on ASAN. http://crbug.com/425248
#if defined(OS_MACOSX)
#define MAYBE_ProxyCreationSkipsSubtree DISABLED_ProxyCreationSkipsSubtree
#else
#define MAYBE_ProxyCreationSkipsSubtree ProxyCreationSkipsSubtree
#endif
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
MAYBE_ProxyCreationSkipsSubtree) {
GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
NavigateToURL(shell(), main_url);
// It is safe to obtain the root frame tree node here, as it doesn't change.
FrameTreeNode* root =
static_cast<WebContentsImpl*>(shell()->web_contents())->
GetFrameTree()->root();
EXPECT_TRUE(root->child_at(1) != NULL);
EXPECT_EQ(2U, root->child_at(1)->child_count());
{
// Load same-site page into iframe.
SitePerProcessWebContentsObserver observer(shell()->web_contents());
GURL http_url(embedded_test_server()->GetURL("/title1.html"));
NavigateFrameToURL(root->child_at(0), http_url);
EXPECT_EQ(http_url, observer.navigation_url());
EXPECT_TRUE(observer.navigation_succeeded());
RenderFrameProxyHost* proxy_to_parent =
root->child_at(0)->render_manager()->GetRenderFrameProxyHost(
shell()->web_contents()->GetSiteInstance());
EXPECT_FALSE(proxy_to_parent);
}
// Create the cross-site URL to navigate to.
GURL::Replacements replace_host;
std::string foo_com("foo.com");
GURL cross_site_url(embedded_test_server()->GetURL("/frame_tree/1-1.html"));
replace_host.SetHostStr(foo_com);
cross_site_url = cross_site_url.ReplaceComponents(replace_host);
// Load cross-site page into the second iframe without waiting for the
// navigation to complete. Once LoadURLWithParams returns, we would expect
// proxies to have been created in the frame tree, but children of the
// navigating frame to still be present. The reason is that we don't run the
// message loop, so no IPCs that alter the frame tree can be processed.
FrameTreeNode* child = root->child_at(1);
SiteInstance* site = NULL;
{
SitePerProcessWebContentsObserver observer(shell()->web_contents());
TestFrameNavigationObserver navigation_observer(child);
NavigationController::LoadURLParams params(cross_site_url);
params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK);
params.frame_tree_node_id = child->frame_tree_node_id();
child->navigator()->GetController()->LoadURLWithParams(params);
EXPECT_TRUE(child->render_manager()->pending_frame_host());
site = child->render_manager()->pending_frame_host()->GetSiteInstance();
EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site);
EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(site));
EXPECT_TRUE(
root->child_at(0)->render_manager()->GetRenderFrameProxyHost(site));
EXPECT_FALSE(child->render_manager()->GetRenderFrameProxyHost(site));
for (size_t i = 0; i < child->child_count(); ++i) {
EXPECT_FALSE(
child->child_at(i)->render_manager()->GetRenderFrameProxyHost(site));
}
// Now that the verification is done, run the message loop and wait for the
// navigation to complete.
navigation_observer.Wait();
EXPECT_FALSE(child->render_manager()->pending_frame_host());
EXPECT_EQ(cross_site_url, observer.navigation_url());
EXPECT_TRUE(observer.navigation_succeeded());
}
// Load another cross-site page into the same iframe.
cross_site_url = embedded_test_server()->GetURL("/title2.html");
std::string bar_com("bar.com");
replace_host.SetHostStr(bar_com);
cross_site_url = cross_site_url.ReplaceComponents(replace_host);
{
// Perform the same checks as the first cross-site navigation, since
// there have been issues in subsequent cross-site navigations. Also ensure
// that the SiteInstance has properly changed.
// TODO(nasko): Once we have proper cleanup of resources, add code to
// verify that the intermediate SiteInstance/RenderFrameHost have been
// properly cleaned up.
SitePerProcessWebContentsObserver observer(shell()->web_contents());
TestFrameNavigationObserver navigation_observer(child);
NavigationController::LoadURLParams params(cross_site_url);
params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK);
params.frame_tree_node_id = child->frame_tree_node_id();
child->navigator()->GetController()->LoadURLWithParams(params);
EXPECT_TRUE(child->render_manager()->pending_frame_host() != NULL);
SiteInstance* site2 =
child->render_manager()->pending_frame_host()->GetSiteInstance();
EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site2);
EXPECT_NE(site, site2);
EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(site2));
EXPECT_TRUE(
root->child_at(0)->render_manager()->GetRenderFrameProxyHost(site2));
EXPECT_FALSE(child->render_manager()->GetRenderFrameProxyHost(site2));
for (size_t i = 0; i < child->child_count(); ++i) {
EXPECT_FALSE(
child->child_at(i)->render_manager()->GetRenderFrameProxyHost(site2));
}
navigation_observer.Wait();
EXPECT_EQ(cross_site_url, observer.navigation_url());
EXPECT_TRUE(observer.navigation_succeeded());
EXPECT_EQ(0U, child->child_count());
}
}
} // namespace content