blob: c40de12c83b2c00affa0015098087a41e677ee2c [file] [log] [blame]
// Copyright (c) 2014 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 "base/run_loop.h"
#include "build/build_config.h"
#include "content/browser/devtools/render_frame_devtools_agent_host.h"
#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/site_per_process_browsertest.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/download_manager.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/shell/browser/shell_download_manager_delegate.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.h"
namespace content {
class SitePerProcessDevToolsBrowserTest : public SitePerProcessBrowserTest {
public:
SitePerProcessDevToolsBrowserTest() {}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
SitePerProcessBrowserTest::SetUpOnMainThread();
}
};
class TestClient: public DevToolsAgentHostClient {
public:
TestClient() : closed_(false), waiting_for_reply_(false) {}
~TestClient() override {}
bool closed() { return closed_; }
void DispatchProtocolMessage(
DevToolsAgentHost* agent_host,
const std::string& message) override {
if (waiting_for_reply_) {
waiting_for_reply_ = false;
base::RunLoop::QuitCurrentDeprecated();
}
}
void AgentHostClosed(DevToolsAgentHost* agent_host) override {
closed_ = true;
}
void WaitForReply() {
waiting_for_reply_ = true;
base::RunLoop().Run();
}
private:
bool closed_;
bool waiting_for_reply_;
};
// Fails on Android, http://crbug.com/464993.
#if defined(OS_ANDROID)
#define MAYBE_CrossSiteIframeAgentHost DISABLED_CrossSiteIframeAgentHost
#else
#define MAYBE_CrossSiteIframeAgentHost CrossSiteIframeAgentHost
#endif
IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest,
MAYBE_CrossSiteIframeAgentHost) {
DevToolsAgentHost::List list;
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();
list = DevToolsAgentHost::GetOrCreateAll();
EXPECT_EQ(1U, list.size());
EXPECT_EQ(DevToolsAgentHost::kTypePage, list[0]->GetType());
EXPECT_EQ(main_url.spec(), list[0]->GetURL().spec());
// 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);
list = DevToolsAgentHost::GetOrCreateAll();
EXPECT_EQ(1U, list.size());
EXPECT_EQ(DevToolsAgentHost::kTypePage, list[0]->GetType());
EXPECT_EQ(main_url.spec(), list[0]->GetURL().spec());
// Load cross-site page into iframe.
GURL::Replacements replace_host;
GURL cross_site_url(embedded_test_server()->GetURL("/title2.html"));
replace_host.SetHostStr("foo.com");
cross_site_url = cross_site_url.ReplaceComponents(replace_host);
NavigateFrameToURL(root->child_at(0), cross_site_url);
list = DevToolsAgentHost::GetOrCreateAll();
EXPECT_EQ(2U, list.size());
EXPECT_EQ(DevToolsAgentHost::kTypePage, list[0]->GetType());
EXPECT_EQ(main_url.spec(), list[0]->GetURL().spec());
EXPECT_EQ(DevToolsAgentHost::kTypeFrame, list[1]->GetType());
EXPECT_EQ(cross_site_url.spec(), list[1]->GetURL().spec());
EXPECT_EQ(std::string(), list[0]->GetParentId());
EXPECT_EQ(list[0]->GetId(), list[1]->GetParentId());
EXPECT_NE(list[1]->GetId(), list[0]->GetId());
// Attaching to both agent hosts.
scoped_refptr<DevToolsAgentHost> child_host = list[1];
TestClient child_client;
child_host->AttachClient(&child_client);
scoped_refptr<DevToolsAgentHost> parent_host = list[0];
TestClient parent_client;
parent_host->AttachClient(&parent_client);
// Send message to parent and child frames and get result back.
char message[] = "{\"id\": 0, \"method\": \"incorrect.method\"}";
child_host->DispatchProtocolMessage(&child_client, message);
child_client.WaitForReply();
parent_host->DispatchProtocolMessage(&parent_client, message);
parent_client.WaitForReply();
// Load back same-site page into iframe.
NavigateFrameToURL(root->child_at(0), http_url);
list = DevToolsAgentHost::GetOrCreateAll();
EXPECT_EQ(1U, list.size());
EXPECT_EQ(DevToolsAgentHost::kTypePage, list[0]->GetType());
EXPECT_EQ(main_url.spec(), list[0]->GetURL().spec());
EXPECT_TRUE(child_client.closed());
child_host->DetachClient(&child_client);
child_host = nullptr;
EXPECT_FALSE(parent_client.closed());
parent_host->DetachClient(&parent_client);
parent_host = nullptr;
}
IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest, AgentHostForFrames) {
GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
NavigateToURL(shell(), main_url);
scoped_refptr<DevToolsAgentHost> page_agent =
DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
// 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();
scoped_refptr<DevToolsAgentHost> main_frame_agent =
RenderFrameDevToolsAgentHost::GetOrCreateFor(root);
EXPECT_EQ(page_agent.get(), main_frame_agent.get());
// 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);
scoped_refptr<DevToolsAgentHost> child_frame_agent =
RenderFrameDevToolsAgentHost::GetOrCreateFor(child);
EXPECT_EQ(page_agent.get(), child_frame_agent.get());
// Load cross-site page into iframe.
GURL::Replacements replace_host;
GURL cross_site_url(embedded_test_server()->GetURL("/title2.html"));
replace_host.SetHostStr("foo.com");
cross_site_url = cross_site_url.ReplaceComponents(replace_host);
NavigateFrameToURL(root->child_at(0), cross_site_url);
child_frame_agent = RenderFrameDevToolsAgentHost::GetOrCreateFor(child);
EXPECT_NE(page_agent.get(), child_frame_agent.get());
EXPECT_EQ(child_frame_agent->GetParentId(), page_agent->GetId());
EXPECT_NE(child_frame_agent->GetId(), page_agent->GetId());
}
IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest,
AgentHostForPageEqualsOneForMainFrame) {
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();
FrameTreeNode* child = root->child_at(0);
// Load cross-site page into iframe.
GURL::Replacements replace_host;
GURL cross_site_url(embedded_test_server()->GetURL("/title2.html"));
replace_host.SetHostStr("foo.com");
cross_site_url = cross_site_url.ReplaceComponents(replace_host);
NavigateFrameToURL(child, cross_site_url);
// First ask for child frame, then for main frame.
scoped_refptr<DevToolsAgentHost> child_frame_agent =
RenderFrameDevToolsAgentHost::GetOrCreateFor(child);
scoped_refptr<DevToolsAgentHost> main_frame_agent =
RenderFrameDevToolsAgentHost::GetOrCreateFor(root);
EXPECT_NE(main_frame_agent.get(), child_frame_agent.get());
EXPECT_EQ(child_frame_agent->GetParentId(), main_frame_agent->GetId());
EXPECT_NE(child_frame_agent->GetId(), main_frame_agent->GetId());
// Agent for web contents should be the the main frame's one.
scoped_refptr<DevToolsAgentHost> page_agent =
DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
EXPECT_EQ(page_agent.get(), main_frame_agent.get());
}
class SitePerProcessDownloadDevToolsBrowserTest
: public SitePerProcessBrowserTest {
public:
SitePerProcessDownloadDevToolsBrowserTest() {}
void SetUpOnMainThread() override {
SitePerProcessBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir());
DownloadManager* download_manager = BrowserContext::GetDownloadManager(
shell()->web_contents()->GetBrowserContext());
ShellDownloadManagerDelegate* download_delegate =
static_cast<ShellDownloadManagerDelegate*>(
download_manager->GetDelegate());
download_delegate->SetDownloadBehaviorForTesting(
downloads_directory_.GetPath());
}
base::ScopedTempDir downloads_directory_;
};
IN_PROC_BROWSER_TEST_F(SitePerProcessDownloadDevToolsBrowserTest,
NotCommittedNavigationDoesNotBlockAgent) {
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
scoped_refptr<DevToolsAgentHost> agent =
DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
TestClient client;
agent->AttachClient(&client);
char message[] = "{\"id\": 0, \"method\": \"incorrect.method\"}";
// Check that client is responsive.
agent->DispatchProtocolMessage(&client, message);
client.WaitForReply();
// Do cross process navigation that ends up being download, which will be
// not committed navigation in that web contents/render frame.
GURL::Replacements replace_host;
GURL cross_site_url(embedded_test_server()->GetURL("/download/empty.bin"));
replace_host.SetHostStr("foo.com");
cross_site_url = cross_site_url.ReplaceComponents(replace_host);
ASSERT_TRUE(NavigateToURLAndExpectNoCommit(shell(), cross_site_url));
// Check that client is still responding after not committed navigation
// is finished.
agent->DispatchProtocolMessage(&client, message);
client.WaitForReply();
ASSERT_TRUE(agent->DetachClient(&client));
}
} // namespace content