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