1 // Copyright (c) 2012 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.
5 #include "base/command_line.h"
6 #include "base/strings/stringprintf.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "content/browser/frame_host/frame_tree.h"
9 #include "content/browser/renderer_host/render_view_host_impl.h"
10 #include "content/browser/web_contents/web_contents_impl.h"
11 #include "content/public/browser/notification_observer.h"
12 #include "content/public/browser/notification_service.h"
13 #include "content/public/browser/notification_types.h"
14 #include "content/public/browser/web_contents_observer.h"
15 #include "content/public/common/content_switches.h"
16 #include "content/public/test/browser_test_utils.h"
17 #include "content/public/test/test_utils.h"
18 #include "content/shell/browser/shell.h"
19 #include "content/test/content_browser_test.h"
20 #include "content/test/content_browser_test_utils.h"
21 #include "net/dns/mock_host_resolver.h"
25 class SitePerProcessWebContentsObserver: public WebContentsObserver {
27 explicit SitePerProcessWebContentsObserver(WebContents* web_contents)
28 : WebContentsObserver(web_contents),
29 navigation_succeeded_(true) {}
30 virtual ~SitePerProcessWebContentsObserver() {}
32 virtual void DidFailProvisionalLoad(
34 const string16& frame_unique_name,
36 const GURL& validated_url,
38 const string16& error_description,
39 RenderViewHost* render_view_host) OVERRIDE {
40 navigation_url_ = validated_url;
41 navigation_succeeded_ = false;
44 virtual void DidCommitProvisionalLoadForFrame(
46 const string16& frame_unique_name,
49 PageTransition transition_type,
50 RenderViewHost* render_view_host) OVERRIDE{
51 navigation_url_ = url;
52 navigation_succeeded_ = true;
55 const GURL& navigation_url() const {
56 return navigation_url_;
59 int navigation_succeeded() const { return navigation_succeeded_; }
63 bool navigation_succeeded_;
65 DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver);
68 class RedirectNotificationObserver : public NotificationObserver {
70 // Register to listen for notifications of the given type from either a
71 // specific source, or from all sources if |source| is
72 // NotificationService::AllSources().
73 RedirectNotificationObserver(int notification_type,
74 const NotificationSource& source);
75 virtual ~RedirectNotificationObserver();
77 // Wait until the specified notification occurs. If the notification was
78 // emitted between the construction of this object and this call then it
79 // returns immediately.
82 // Returns NotificationService::AllSources() if we haven't observed a
84 const NotificationSource& source() const {
88 const NotificationDetails& details() const {
92 // NotificationObserver:
93 virtual void Observe(int type,
94 const NotificationSource& source,
95 const NotificationDetails& details) OVERRIDE;
101 NotificationRegistrar registrar_;
103 NotificationSource source_;
104 NotificationDetails details_;
105 scoped_refptr<MessageLoopRunner> message_loop_runner_;
107 DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver);
110 RedirectNotificationObserver::RedirectNotificationObserver(
111 int notification_type,
112 const NotificationSource& source)
115 source_(NotificationService::AllSources()) {
116 registrar_.Add(this, notification_type, source);
119 RedirectNotificationObserver::~RedirectNotificationObserver() {}
121 void RedirectNotificationObserver::Wait() {
122 if (seen_ && seen_twice_)
126 message_loop_runner_ = new MessageLoopRunner;
127 message_loop_runner_->Run();
131 void RedirectNotificationObserver::Observe(
133 const NotificationSource& source,
134 const NotificationDetails& details) {
142 message_loop_runner_->Quit();
146 class SitePerProcessBrowserTest : public ContentBrowserTest {
148 bool NavigateIframeToURL(Shell* window,
150 std::string iframe_id) {
151 std::string script = base::StringPrintf(
152 "var iframes = document.getElementById('%s');iframes.src='%s';",
153 iframe_id.c_str(), url.spec().c_str());
154 WindowedNotificationObserver load_observer(
155 NOTIFICATION_LOAD_STOP,
156 Source<NavigationController>(
157 &shell()->web_contents()->GetController()));
158 bool result = ExecuteScript(window->web_contents(), script);
159 load_observer.Wait();
163 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
164 command_line->AppendSwitch(switches::kSitePerProcess);
168 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
169 // security checks are back in place.
170 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DISABLED_CrossSiteIframe) {
171 ASSERT_TRUE(test_server()->Start());
172 net::SpawnedTestServer https_server(
173 net::SpawnedTestServer::TYPE_HTTPS,
174 net::SpawnedTestServer::kLocalhost,
175 base::FilePath(FILE_PATH_LITERAL("content/test/data")));
176 ASSERT_TRUE(https_server.Start());
177 GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
179 NavigateToURL(shell(), main_url);
181 SitePerProcessWebContentsObserver observer(shell()->web_contents());
183 // Load same-site page into Iframe.
184 GURL http_url(test_server()->GetURL("files/title1.html"));
185 EXPECT_TRUE(NavigateIframeToURL(shell(), http_url, "test"));
186 EXPECT_EQ(observer.navigation_url(), http_url);
187 EXPECT_TRUE(observer.navigation_succeeded());
191 // Load cross-site page into Iframe.
192 GURL https_url(https_server.GetURL("files/title1.html"));
193 EXPECT_TRUE(NavigateIframeToURL(shell(), https_url, "test"));
194 EXPECT_EQ(observer.navigation_url(), https_url);
195 EXPECT_FALSE(observer.navigation_succeeded());
199 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
200 // security checks are back in place.
201 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
202 DISABLED_CrossSiteIframeRedirectOnce) {
203 ASSERT_TRUE(test_server()->Start());
204 net::SpawnedTestServer https_server(
205 net::SpawnedTestServer::TYPE_HTTPS,
206 net::SpawnedTestServer::kLocalhost,
207 base::FilePath(FILE_PATH_LITERAL("content/test/data")));
208 ASSERT_TRUE(https_server.Start());
210 GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
211 GURL http_url(test_server()->GetURL("files/title1.html"));
212 GURL https_url(https_server.GetURL("files/title1.html"));
214 NavigateToURL(shell(), main_url);
216 SitePerProcessWebContentsObserver observer(shell()->web_contents());
218 // Load cross-site client-redirect page into Iframe.
219 // Should be blocked.
220 GURL client_redirect_https_url(https_server.GetURL(
221 "client-redirect?files/title1.html"));
222 EXPECT_TRUE(NavigateIframeToURL(shell(),
223 client_redirect_https_url, "test"));
224 // DidFailProvisionalLoad when navigating to client_redirect_https_url.
225 EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
226 EXPECT_FALSE(observer.navigation_succeeded());
230 // Load cross-site server-redirect page into Iframe,
231 // which redirects to same-site page.
232 GURL server_redirect_http_url(https_server.GetURL(
233 "server-redirect?" + http_url.spec()));
234 EXPECT_TRUE(NavigateIframeToURL(shell(),
235 server_redirect_http_url, "test"));
236 EXPECT_EQ(observer.navigation_url(), http_url);
237 EXPECT_TRUE(observer.navigation_succeeded());
241 // Load cross-site server-redirect page into Iframe,
242 // which redirects to cross-site page.
243 GURL server_redirect_http_url(https_server.GetURL(
244 "server-redirect?files/title1.html"));
245 EXPECT_TRUE(NavigateIframeToURL(shell(),
246 server_redirect_http_url, "test"));
247 // DidFailProvisionalLoad when navigating to https_url.
248 EXPECT_EQ(observer.navigation_url(), https_url);
249 EXPECT_FALSE(observer.navigation_succeeded());
253 // Load same-site server-redirect page into Iframe,
254 // which redirects to cross-site page.
255 GURL server_redirect_http_url(test_server()->GetURL(
256 "server-redirect?" + https_url.spec()));
257 EXPECT_TRUE(NavigateIframeToURL(shell(),
258 server_redirect_http_url, "test"));
260 EXPECT_EQ(observer.navigation_url(), https_url);
261 EXPECT_FALSE(observer.navigation_succeeded());
265 // Load same-site client-redirect page into Iframe,
266 // which redirects to cross-site page.
267 GURL client_redirect_http_url(test_server()->GetURL(
268 "client-redirect?" + https_url.spec()));
270 RedirectNotificationObserver load_observer2(
271 NOTIFICATION_LOAD_STOP,
272 Source<NavigationController>(
273 &shell()->web_contents()->GetController()));
275 EXPECT_TRUE(NavigateIframeToURL(shell(),
276 client_redirect_http_url, "test"));
278 // Same-site Client-Redirect Page should be loaded successfully.
279 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
280 EXPECT_TRUE(observer.navigation_succeeded());
282 // Redirecting to Cross-site Page should be blocked.
283 load_observer2.Wait();
284 EXPECT_EQ(observer.navigation_url(), https_url);
285 EXPECT_FALSE(observer.navigation_succeeded());
289 // Load same-site server-redirect page into Iframe,
290 // which redirects to same-site page.
291 GURL server_redirect_http_url(test_server()->GetURL(
292 "server-redirect?files/title1.html"));
293 EXPECT_TRUE(NavigateIframeToURL(shell(),
294 server_redirect_http_url, "test"));
295 EXPECT_EQ(observer.navigation_url(), http_url);
296 EXPECT_TRUE(observer.navigation_succeeded());
300 // Load same-site client-redirect page into Iframe,
301 // which redirects to same-site page.
302 GURL client_redirect_http_url(test_server()->GetURL(
303 "client-redirect?" + http_url.spec()));
304 RedirectNotificationObserver load_observer2(
305 NOTIFICATION_LOAD_STOP,
306 Source<NavigationController>(
307 &shell()->web_contents()->GetController()));
309 EXPECT_TRUE(NavigateIframeToURL(shell(),
310 client_redirect_http_url, "test"));
312 // Same-site Client-Redirect Page should be loaded successfully.
313 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
314 EXPECT_TRUE(observer.navigation_succeeded());
316 // Redirecting to Same-site Page should be loaded successfully.
317 load_observer2.Wait();
318 EXPECT_EQ(observer.navigation_url(), http_url);
319 EXPECT_TRUE(observer.navigation_succeeded());
323 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
324 // security checks are back in place.
325 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
326 DISABLED_CrossSiteIframeRedirectTwice) {
327 ASSERT_TRUE(test_server()->Start());
328 net::SpawnedTestServer https_server(
329 net::SpawnedTestServer::TYPE_HTTPS,
330 net::SpawnedTestServer::kLocalhost,
331 base::FilePath(FILE_PATH_LITERAL("content/test/data")));
332 ASSERT_TRUE(https_server.Start());
334 GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
335 GURL http_url(test_server()->GetURL("files/title1.html"));
336 GURL https_url(https_server.GetURL("files/title1.html"));
338 NavigateToURL(shell(), main_url);
340 SitePerProcessWebContentsObserver observer(shell()->web_contents());
342 // Load client-redirect page pointing to a cross-site client-redirect page,
343 // which eventually redirects back to same-site page.
344 GURL client_redirect_https_url(https_server.GetURL(
345 "client-redirect?" + http_url.spec()));
346 GURL client_redirect_http_url(test_server()->GetURL(
347 "client-redirect?" + client_redirect_https_url.spec()));
349 // We should wait until second client redirect get cancelled.
350 RedirectNotificationObserver load_observer2(
351 NOTIFICATION_LOAD_STOP,
352 Source<NavigationController>(
353 &shell()->web_contents()->GetController()));
355 EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test"));
357 // DidFailProvisionalLoad when navigating to client_redirect_https_url.
358 load_observer2.Wait();
359 EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
360 EXPECT_FALSE(observer.navigation_succeeded());
364 // Load server-redirect page pointing to a cross-site server-redirect page,
365 // which eventually redirect back to same-site page.
366 GURL server_redirect_https_url(https_server.GetURL(
367 "server-redirect?" + http_url.spec()));
368 GURL server_redirect_http_url(test_server()->GetURL(
369 "server-redirect?" + server_redirect_https_url.spec()));
370 EXPECT_TRUE(NavigateIframeToURL(shell(),
371 server_redirect_http_url, "test"));
372 EXPECT_EQ(observer.navigation_url(), http_url);
373 EXPECT_TRUE(observer.navigation_succeeded());
377 // Load server-redirect page pointing to a cross-site server-redirect page,
378 // which eventually redirects back to cross-site page.
379 GURL server_redirect_https_url(https_server.GetURL(
380 "server-redirect?" + https_url.spec()));
381 GURL server_redirect_http_url(test_server()->GetURL(
382 "server-redirect?" + server_redirect_https_url.spec()));
383 EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
385 // DidFailProvisionalLoad when navigating to https_url.
386 EXPECT_EQ(observer.navigation_url(), https_url);
387 EXPECT_FALSE(observer.navigation_succeeded());
391 // Load server-redirect page pointing to a cross-site client-redirect page,
392 // which eventually redirects back to same-site page.
393 GURL client_redirect_http_url(https_server.GetURL(
394 "client-redirect?" + http_url.spec()));
395 GURL server_redirect_http_url(test_server()->GetURL(
396 "server-redirect?" + client_redirect_http_url.spec()));
397 EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
399 // DidFailProvisionalLoad when navigating to client_redirect_http_url.
400 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
401 EXPECT_FALSE(observer.navigation_succeeded());
405 // Ensures FrameTree correctly reflects page structure during navigations.
406 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
408 host_resolver()->AddRule("*", "127.0.0.1");
409 ASSERT_TRUE(test_server()->Start());
411 GURL base_url = test_server()->GetURL("files/site_isolation/");
412 GURL::Replacements replace_host;
413 std::string host_str("A.com"); // Must stay in scope with replace_host.
414 replace_host.SetHostStr(host_str);
415 base_url = base_url.ReplaceComponents(replace_host);
417 // Load doc without iframes. Verify FrameTree just has root.
420 NavigateToURL(shell(), base_url.Resolve("blank.html"));
421 FrameTreeNode* root =
422 static_cast<WebContentsImpl*>(shell()->web_contents())->
423 GetFrameTree()->GetRootForTesting();
424 EXPECT_EQ(0U, root->child_count());
426 // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
428 // Site-A Root -- Site-A frame1
430 WindowedNotificationObserver observer1(
431 content::NOTIFICATION_LOAD_STOP,
432 content::Source<NavigationController>(
433 &shell()->web_contents()->GetController()));
434 NavigateToURL(shell(), base_url.Resolve("frames-X-X.html"));
436 ASSERT_EQ(2U, root->child_count());
437 EXPECT_EQ(0U, root->child_at(0)->child_count());
438 EXPECT_EQ(0U, root->child_at(1)->child_count());
441 // TODO(ajwong): Talk with nasko and merge this functionality with
443 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
445 host_resolver()->AddRule("*", "127.0.0.1");
446 ASSERT_TRUE(test_server()->Start());
448 NavigateToURL(shell(),
449 test_server()->GetURL("files/frame_tree/top.html"));
451 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
452 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
453 wc->GetRenderViewHost());
454 FrameTreeNode* root = wc->GetFrameTree()->GetRootForTesting();
456 // Check that the root node is properly created with the frame id of the
457 // initial navigation.
458 ASSERT_EQ(3UL, root->child_count());
459 EXPECT_EQ(std::string(), root->frame_name());
460 EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
462 ASSERT_EQ(2UL, root->child_at(0)->child_count());
463 EXPECT_STREQ("1-1-name", root->child_at(0)->frame_name().c_str());
465 // Verify the deepest node exists and has the right name.
466 ASSERT_EQ(2UL, root->child_at(2)->child_count());
467 EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count());
468 EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count());
469 EXPECT_STREQ("3-1-id",
470 root->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str());
472 // Navigate to about:blank, which should leave only the root node of the frame
473 // tree in the browser process.
474 NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
476 root = wc->GetFrameTree()->GetRootForTesting();
477 EXPECT_EQ(0UL, root->child_count());
478 EXPECT_EQ(std::string(), root->frame_name());
479 EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
482 } // namespace content