1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/run_loop.h"
6 #include "build/build_config.h"
7 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
8 #include "content/browser/frame_host/frame_tree.h"
9 #include "content/browser/site_per_process_browsertest.h"
10 #include "content/browser/web_contents/web_contents_impl.h"
11 #include "content/public/browser/browser_context.h"
12 #include "content/public/browser/devtools_agent_host.h"
13 #include "content/public/browser/download_manager.h"
14 #include "content/public/test/content_browser_test_utils.h"
15 #include "content/public/test/test_utils.h"
16 #include "content/shell/browser/shell.h"
17 #include "content/shell/browser/shell_download_manager_delegate.h"
18 #include "content/test/content_browser_test_utils_internal.h"
19 #include "net/dns/mock_host_resolver.h"
20
21 namespace content {
22
23 class SitePerProcessDevToolsBrowserTest : public SitePerProcessBrowserTest {
24 public:
SitePerProcessDevToolsBrowserTest()25 SitePerProcessDevToolsBrowserTest() {}
SetUpOnMainThread()26 void SetUpOnMainThread() override {
27 host_resolver()->AddRule("*", "127.0.0.1");
28 SitePerProcessBrowserTest::SetUpOnMainThread();
29 }
30 };
31
32 class TestClient: public DevToolsAgentHostClient {
33 public:
TestClient()34 TestClient() : closed_(false), waiting_for_reply_(false) {}
~TestClient()35 ~TestClient() override {}
36
closed()37 bool closed() { return closed_; }
38
DispatchProtocolMessage(DevToolsAgentHost * agent_host,base::span<const uint8_t> message)39 void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
40 base::span<const uint8_t> message) override {
41 if (waiting_for_reply_) {
42 waiting_for_reply_ = false;
43 base::RunLoop::QuitCurrentDeprecated();
44 }
45 }
46
AgentHostClosed(DevToolsAgentHost * agent_host)47 void AgentHostClosed(DevToolsAgentHost* agent_host) override {
48 closed_ = true;
49 }
50
WaitForReply()51 void WaitForReply() {
52 waiting_for_reply_ = true;
53 base::RunLoop().Run();
54 }
55
56 private:
57 bool closed_;
58 bool waiting_for_reply_;
59 };
60
61 // Fails on Android, http://crbug.com/464993.
62 #if defined(OS_ANDROID)
63 #define MAYBE_CrossSiteIframeAgentHost DISABLED_CrossSiteIframeAgentHost
64 #else
65 #define MAYBE_CrossSiteIframeAgentHost CrossSiteIframeAgentHost
66 #endif
IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest,MAYBE_CrossSiteIframeAgentHost)67 IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest,
68 MAYBE_CrossSiteIframeAgentHost) {
69 DevToolsAgentHost::List list;
70 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
71 EXPECT_TRUE(NavigateToURL(shell(), main_url));
72
73 // It is safe to obtain the root frame tree node here, as it doesn't change.
74 FrameTreeNode* root =
75 static_cast<WebContentsImpl*>(shell()->web_contents())->
76 GetFrameTree()->root();
77
78 list = DevToolsAgentHost::GetOrCreateAll();
79 EXPECT_EQ(1U, list.size());
80 EXPECT_EQ(DevToolsAgentHost::kTypePage, list[0]->GetType());
81 EXPECT_EQ(main_url.spec(), list[0]->GetURL().spec());
82
83 // Load same-site page into iframe.
84 FrameTreeNode* child = root->child_at(0);
85 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
86 NavigateFrameToURL(child, http_url);
87
88 list = DevToolsAgentHost::GetOrCreateAll();
89 EXPECT_EQ(1U, list.size());
90 EXPECT_EQ(DevToolsAgentHost::kTypePage, list[0]->GetType());
91 EXPECT_EQ(main_url.spec(), list[0]->GetURL().spec());
92
93 // Load cross-site page into iframe.
94 GURL::Replacements replace_host;
95 GURL cross_site_url(embedded_test_server()->GetURL("/title2.html"));
96 replace_host.SetHostStr("foo.com");
97 cross_site_url = cross_site_url.ReplaceComponents(replace_host);
98 NavigateFrameToURL(root->child_at(0), cross_site_url);
99
100 list = DevToolsAgentHost::GetOrCreateAll();
101 EXPECT_EQ(2U, list.size());
102 EXPECT_EQ(DevToolsAgentHost::kTypePage, list[0]->GetType());
103 EXPECT_EQ(main_url.spec(), list[0]->GetURL().spec());
104 EXPECT_EQ(DevToolsAgentHost::kTypeFrame, list[1]->GetType());
105 EXPECT_EQ(cross_site_url.spec(), list[1]->GetURL().spec());
106 EXPECT_EQ(std::string(), list[0]->GetParentId());
107 EXPECT_EQ(list[0]->GetId(), list[1]->GetParentId());
108 EXPECT_NE(list[1]->GetId(), list[0]->GetId());
109
110 // Attaching to both agent hosts.
111 scoped_refptr<DevToolsAgentHost> child_host = list[1];
112 TestClient child_client;
113 child_host->AttachClient(&child_client);
114 scoped_refptr<DevToolsAgentHost> parent_host = list[0];
115 TestClient parent_client;
116 parent_host->AttachClient(&parent_client);
117
118 // Send message to parent and child frames and get result back.
119 constexpr char kMsg[] = R"({"id":0,"method":"incorrect.method"})";
120 auto message = base::as_bytes(base::make_span(kMsg, strlen(kMsg)));
121 child_host->DispatchProtocolMessage(&child_client, message);
122 child_client.WaitForReply();
123 parent_host->DispatchProtocolMessage(&parent_client, message);
124 parent_client.WaitForReply();
125
126 // Load back same-site page into iframe.
127 NavigateFrameToURL(root->child_at(0), http_url);
128
129 list = DevToolsAgentHost::GetOrCreateAll();
130 EXPECT_EQ(1U, list.size());
131 EXPECT_EQ(DevToolsAgentHost::kTypePage, list[0]->GetType());
132 EXPECT_EQ(main_url.spec(), list[0]->GetURL().spec());
133 EXPECT_TRUE(child_client.closed());
134 child_host->DetachClient(&child_client);
135 child_host = nullptr;
136 EXPECT_FALSE(parent_client.closed());
137 parent_host->DetachClient(&parent_client);
138 parent_host = nullptr;
139 }
140
IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest,AgentHostForFrames)141 IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest, AgentHostForFrames) {
142 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
143 EXPECT_TRUE(NavigateToURL(shell(), main_url));
144
145 scoped_refptr<DevToolsAgentHost> page_agent =
146 DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
147
148 // It is safe to obtain the root frame tree node here, as it doesn't change.
149 FrameTreeNode* root =
150 static_cast<WebContentsImpl*>(shell()->web_contents())->
151 GetFrameTree()->root();
152
153 scoped_refptr<DevToolsAgentHost> main_frame_agent =
154 RenderFrameDevToolsAgentHost::GetOrCreateFor(root);
155 EXPECT_EQ(page_agent.get(), main_frame_agent.get());
156
157 // Load same-site page into iframe.
158 FrameTreeNode* child = root->child_at(0);
159 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
160 NavigateFrameToURL(child, http_url);
161
162 scoped_refptr<DevToolsAgentHost> child_frame_agent =
163 RenderFrameDevToolsAgentHost::GetOrCreateFor(child);
164 EXPECT_EQ(page_agent.get(), child_frame_agent.get());
165
166 // Load cross-site page into iframe.
167 GURL::Replacements replace_host;
168 GURL cross_site_url(embedded_test_server()->GetURL("/title2.html"));
169 replace_host.SetHostStr("foo.com");
170 cross_site_url = cross_site_url.ReplaceComponents(replace_host);
171 NavigateFrameToURL(root->child_at(0), cross_site_url);
172
173 child_frame_agent = RenderFrameDevToolsAgentHost::GetOrCreateFor(child);
174 EXPECT_NE(page_agent.get(), child_frame_agent.get());
175 EXPECT_EQ(child_frame_agent->GetParentId(), page_agent->GetId());
176 EXPECT_NE(child_frame_agent->GetId(), page_agent->GetId());
177 }
178
IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest,AgentHostForPageEqualsOneForMainFrame)179 IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest,
180 AgentHostForPageEqualsOneForMainFrame) {
181 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
182 EXPECT_TRUE(NavigateToURL(shell(), main_url));
183
184 // It is safe to obtain the root frame tree node here, as it doesn't change.
185 FrameTreeNode* root =
186 static_cast<WebContentsImpl*>(shell()->web_contents())->
187 GetFrameTree()->root();
188 FrameTreeNode* child = root->child_at(0);
189
190 // Load cross-site page into iframe.
191 GURL::Replacements replace_host;
192 GURL cross_site_url(embedded_test_server()->GetURL("/title2.html"));
193 replace_host.SetHostStr("foo.com");
194 cross_site_url = cross_site_url.ReplaceComponents(replace_host);
195 NavigateFrameToURL(child, cross_site_url);
196
197 // First ask for child frame, then for main frame.
198 scoped_refptr<DevToolsAgentHost> child_frame_agent =
199 RenderFrameDevToolsAgentHost::GetOrCreateFor(child);
200 scoped_refptr<DevToolsAgentHost> main_frame_agent =
201 RenderFrameDevToolsAgentHost::GetOrCreateFor(root);
202 EXPECT_NE(main_frame_agent.get(), child_frame_agent.get());
203 EXPECT_EQ(child_frame_agent->GetParentId(), main_frame_agent->GetId());
204 EXPECT_NE(child_frame_agent->GetId(), main_frame_agent->GetId());
205
206 // Agent for web contents should be the the main frame's one.
207 scoped_refptr<DevToolsAgentHost> page_agent =
208 DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
209 EXPECT_EQ(page_agent.get(), main_frame_agent.get());
210 }
211
212 class SitePerProcessDownloadDevToolsBrowserTest
213 : public SitePerProcessBrowserTest {
214 public:
SitePerProcessDownloadDevToolsBrowserTest()215 SitePerProcessDownloadDevToolsBrowserTest() {}
216
SetUpOnMainThread()217 void SetUpOnMainThread() override {
218 SitePerProcessBrowserTest::SetUpOnMainThread();
219 ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir());
220 DownloadManager* download_manager = BrowserContext::GetDownloadManager(
221 shell()->web_contents()->GetBrowserContext());
222 ShellDownloadManagerDelegate* download_delegate =
223 static_cast<ShellDownloadManagerDelegate*>(
224 download_manager->GetDelegate());
225 download_delegate->SetDownloadBehaviorForTesting(
226 downloads_directory_.GetPath());
227 }
228
229 base::ScopedTempDir downloads_directory_;
230 };
231
IN_PROC_BROWSER_TEST_F(SitePerProcessDownloadDevToolsBrowserTest,NotCommittedNavigationDoesNotBlockAgent)232 IN_PROC_BROWSER_TEST_F(SitePerProcessDownloadDevToolsBrowserTest,
233 NotCommittedNavigationDoesNotBlockAgent) {
234 ASSERT_TRUE(
235 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
236 scoped_refptr<DevToolsAgentHost> agent =
237 DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
238 TestClient client;
239 agent->AttachClient(&client);
240 constexpr char kMsg[] = R"({"id":0,"method":"incorrect.method"})";
241 auto message = base::as_bytes(base::make_span(kMsg, strlen(kMsg)));
242 // Check that client is responsive.
243 agent->DispatchProtocolMessage(&client, message);
244 client.WaitForReply();
245
246 // Do cross process navigation that ends up being download, which will be
247 // not committed navigation in that web contents/render frame.
248 GURL::Replacements replace_host;
249 GURL cross_site_url(embedded_test_server()->GetURL("/download/empty.bin"));
250 replace_host.SetHostStr("foo.com");
251 cross_site_url = cross_site_url.ReplaceComponents(replace_host);
252 ASSERT_TRUE(NavigateToURLAndExpectNoCommit(shell(), cross_site_url));
253
254 // Check that client is still responding after not committed navigation
255 // is finished.
256 agent->DispatchProtocolMessage(&client, message);
257 client.WaitForReply();
258 ASSERT_TRUE(agent->DetachClient(&client));
259 }
260
261 } // namespace content
262