• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
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/navigation_entry.h"
12 #include "content/public/browser/notification_observer.h"
13 #include "content/public/browser/notification_service.h"
14 #include "content/public/browser/notification_types.h"
15 #include "content/public/browser/web_contents_observer.h"
16 #include "content/public/common/content_switches.h"
17 #include "content/public/common/url_constants.h"
18 #include "content/public/test/browser_test_utils.h"
19 #include "content/public/test/test_navigation_observer.h"
20 #include "content/public/test/test_utils.h"
21 #include "content/shell/browser/shell.h"
22 #include "content/shell/browser/shell_content_browser_client.h"
23 #include "content/test/content_browser_test.h"
24 #include "content/test/content_browser_test_utils.h"
25 #include "net/base/escape.h"
26 #include "net/dns/mock_host_resolver.h"
27 
28 namespace content {
29 
30 class SitePerProcessWebContentsObserver: public WebContentsObserver {
31  public:
SitePerProcessWebContentsObserver(WebContents * web_contents)32   explicit SitePerProcessWebContentsObserver(WebContents* web_contents)
33       : WebContentsObserver(web_contents),
34         navigation_succeeded_(true) {}
~SitePerProcessWebContentsObserver()35   virtual ~SitePerProcessWebContentsObserver() {}
36 
DidFailProvisionalLoad(int64 frame_id,const base::string16 & frame_unique_name,bool is_main_frame,const GURL & validated_url,int error_code,const base::string16 & error_description,RenderViewHost * render_view_host)37   virtual void DidFailProvisionalLoad(
38       int64 frame_id,
39       const base::string16& frame_unique_name,
40       bool is_main_frame,
41       const GURL& validated_url,
42       int error_code,
43       const base::string16& error_description,
44       RenderViewHost* render_view_host) OVERRIDE {
45     navigation_url_ = validated_url;
46     navigation_succeeded_ = false;
47   }
48 
DidCommitProvisionalLoadForFrame(int64 frame_id,const base::string16 & frame_unique_name,bool is_main_frame,const GURL & url,PageTransition transition_type,RenderViewHost * render_view_host)49   virtual void DidCommitProvisionalLoadForFrame(
50       int64 frame_id,
51       const base::string16& frame_unique_name,
52       bool is_main_frame,
53       const GURL& url,
54       PageTransition transition_type,
55       RenderViewHost* render_view_host) OVERRIDE{
56     navigation_url_ = url;
57     navigation_succeeded_ = true;
58   }
59 
navigation_url() const60   const GURL& navigation_url() const {
61     return navigation_url_;
62   }
63 
navigation_succeeded() const64   int navigation_succeeded() const { return navigation_succeeded_; }
65 
66  private:
67   GURL navigation_url_;
68   bool navigation_succeeded_;
69 
70   DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver);
71 };
72 
73 class RedirectNotificationObserver : public NotificationObserver {
74  public:
75   // Register to listen for notifications of the given type from either a
76   // specific source, or from all sources if |source| is
77   // NotificationService::AllSources().
78   RedirectNotificationObserver(int notification_type,
79                                const NotificationSource& source);
80   virtual ~RedirectNotificationObserver();
81 
82   // Wait until the specified notification occurs.  If the notification was
83   // emitted between the construction of this object and this call then it
84   // returns immediately.
85   void Wait();
86 
87   // Returns NotificationService::AllSources() if we haven't observed a
88   // notification yet.
source() const89   const NotificationSource& source() const {
90     return source_;
91   }
92 
details() const93   const NotificationDetails& details() const {
94     return details_;
95   }
96 
97   // NotificationObserver:
98   virtual void Observe(int type,
99                        const NotificationSource& source,
100                        const NotificationDetails& details) OVERRIDE;
101 
102  private:
103   bool seen_;
104   bool seen_twice_;
105   bool running_;
106   NotificationRegistrar registrar_;
107 
108   NotificationSource source_;
109   NotificationDetails details_;
110   scoped_refptr<MessageLoopRunner> message_loop_runner_;
111 
112   DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver);
113 };
114 
RedirectNotificationObserver(int notification_type,const NotificationSource & source)115 RedirectNotificationObserver::RedirectNotificationObserver(
116     int notification_type,
117     const NotificationSource& source)
118     : seen_(false),
119       running_(false),
120       source_(NotificationService::AllSources()) {
121   registrar_.Add(this, notification_type, source);
122 }
123 
~RedirectNotificationObserver()124 RedirectNotificationObserver::~RedirectNotificationObserver() {}
125 
Wait()126 void RedirectNotificationObserver::Wait() {
127   if (seen_ && seen_twice_)
128     return;
129 
130   running_ = true;
131   message_loop_runner_ = new MessageLoopRunner;
132   message_loop_runner_->Run();
133   EXPECT_TRUE(seen_);
134 }
135 
Observe(int type,const NotificationSource & source,const NotificationDetails & details)136 void RedirectNotificationObserver::Observe(
137     int type,
138     const NotificationSource& source,
139     const NotificationDetails& details) {
140   source_ = source;
141   details_ = details;
142   seen_twice_ = seen_;
143   seen_ = true;
144   if (!running_)
145     return;
146 
147   message_loop_runner_->Quit();
148   running_ = false;
149 }
150 
151 class SitePerProcessBrowserTest : public ContentBrowserTest {
152  protected:
NavigateIframeToURL(Shell * window,const GURL & url,std::string iframe_id)153   bool NavigateIframeToURL(Shell* window,
154                            const GURL& url,
155                            std::string iframe_id) {
156     std::string script = base::StringPrintf(
157         "var iframes = document.getElementById('%s');iframes.src='%s';",
158         iframe_id.c_str(), url.spec().c_str());
159     WindowedNotificationObserver load_observer(
160         NOTIFICATION_LOAD_STOP,
161         Source<NavigationController>(
162             &shell()->web_contents()->GetController()));
163     bool result = ExecuteScript(window->web_contents(), script);
164     load_observer.Wait();
165     return result;
166   }
167 
NavigateToURLContentInitiated(Shell * window,const GURL & url,bool should_replace_current_entry)168   void NavigateToURLContentInitiated(Shell* window,
169                                      const GURL& url,
170                                      bool should_replace_current_entry) {
171     std::string script;
172     if (should_replace_current_entry)
173       script = base::StringPrintf("location.replace('%s')", url.spec().c_str());
174     else
175       script = base::StringPrintf("location.href = '%s'", url.spec().c_str());
176     TestNavigationObserver load_observer(shell()->web_contents(), 1);
177     bool result = ExecuteScript(window->web_contents(), script);
178     EXPECT_TRUE(result);
179     load_observer.Wait();
180   }
181 
SetUpCommandLine(CommandLine * command_line)182   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
183     command_line->AppendSwitch(switches::kSitePerProcess);
184   }
185 };
186 
187 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
188 // security checks are back in place.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,DISABLED_CrossSiteIframe)189 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DISABLED_CrossSiteIframe) {
190   ASSERT_TRUE(test_server()->Start());
191   net::SpawnedTestServer https_server(
192       net::SpawnedTestServer::TYPE_HTTPS,
193       net::SpawnedTestServer::kLocalhost,
194       base::FilePath(FILE_PATH_LITERAL("content/test/data")));
195   ASSERT_TRUE(https_server.Start());
196   GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
197 
198   NavigateToURL(shell(), main_url);
199 
200   SitePerProcessWebContentsObserver observer(shell()->web_contents());
201   {
202     // Load same-site page into Iframe.
203     GURL http_url(test_server()->GetURL("files/title1.html"));
204     EXPECT_TRUE(NavigateIframeToURL(shell(), http_url, "test"));
205     EXPECT_EQ(observer.navigation_url(), http_url);
206     EXPECT_TRUE(observer.navigation_succeeded());
207   }
208 
209   {
210     // Load cross-site page into Iframe.
211     GURL https_url(https_server.GetURL("files/title1.html"));
212     EXPECT_TRUE(NavigateIframeToURL(shell(), https_url, "test"));
213     EXPECT_EQ(observer.navigation_url(), https_url);
214     EXPECT_FALSE(observer.navigation_succeeded());
215   }
216 }
217 
218 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
219 // security checks are back in place.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,DISABLED_CrossSiteIframeRedirectOnce)220 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
221                        DISABLED_CrossSiteIframeRedirectOnce) {
222   ASSERT_TRUE(test_server()->Start());
223   net::SpawnedTestServer https_server(
224       net::SpawnedTestServer::TYPE_HTTPS,
225       net::SpawnedTestServer::kLocalhost,
226       base::FilePath(FILE_PATH_LITERAL("content/test/data")));
227   ASSERT_TRUE(https_server.Start());
228 
229   GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
230   GURL http_url(test_server()->GetURL("files/title1.html"));
231   GURL https_url(https_server.GetURL("files/title1.html"));
232 
233   NavigateToURL(shell(), main_url);
234 
235   SitePerProcessWebContentsObserver observer(shell()->web_contents());
236   {
237     // Load cross-site client-redirect page into Iframe.
238     // Should be blocked.
239     GURL client_redirect_https_url(https_server.GetURL(
240         "client-redirect?files/title1.html"));
241     EXPECT_TRUE(NavigateIframeToURL(shell(),
242                                     client_redirect_https_url, "test"));
243     // DidFailProvisionalLoad when navigating to client_redirect_https_url.
244     EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
245     EXPECT_FALSE(observer.navigation_succeeded());
246   }
247 
248   {
249     // Load cross-site server-redirect page into Iframe,
250     // which redirects to same-site page.
251     GURL server_redirect_http_url(https_server.GetURL(
252         "server-redirect?" + http_url.spec()));
253     EXPECT_TRUE(NavigateIframeToURL(shell(),
254                                     server_redirect_http_url, "test"));
255     EXPECT_EQ(observer.navigation_url(), http_url);
256     EXPECT_TRUE(observer.navigation_succeeded());
257   }
258 
259   {
260     // Load cross-site server-redirect page into Iframe,
261     // which redirects to cross-site page.
262     GURL server_redirect_http_url(https_server.GetURL(
263         "server-redirect?files/title1.html"));
264     EXPECT_TRUE(NavigateIframeToURL(shell(),
265                                     server_redirect_http_url, "test"));
266     // DidFailProvisionalLoad when navigating to https_url.
267     EXPECT_EQ(observer.navigation_url(), https_url);
268     EXPECT_FALSE(observer.navigation_succeeded());
269   }
270 
271   {
272     // Load same-site server-redirect page into Iframe,
273     // which redirects to cross-site page.
274     GURL server_redirect_http_url(test_server()->GetURL(
275         "server-redirect?" + https_url.spec()));
276     EXPECT_TRUE(NavigateIframeToURL(shell(),
277                                     server_redirect_http_url, "test"));
278 
279     EXPECT_EQ(observer.navigation_url(), https_url);
280     EXPECT_FALSE(observer.navigation_succeeded());
281    }
282 
283   {
284     // Load same-site client-redirect page into Iframe,
285     // which redirects to cross-site page.
286     GURL client_redirect_http_url(test_server()->GetURL(
287         "client-redirect?" + https_url.spec()));
288 
289     RedirectNotificationObserver load_observer2(
290         NOTIFICATION_LOAD_STOP,
291         Source<NavigationController>(
292             &shell()->web_contents()->GetController()));
293 
294     EXPECT_TRUE(NavigateIframeToURL(shell(),
295                                     client_redirect_http_url, "test"));
296 
297     // Same-site Client-Redirect Page should be loaded successfully.
298     EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
299     EXPECT_TRUE(observer.navigation_succeeded());
300 
301     // Redirecting to Cross-site Page should be blocked.
302     load_observer2.Wait();
303     EXPECT_EQ(observer.navigation_url(), https_url);
304     EXPECT_FALSE(observer.navigation_succeeded());
305   }
306 
307   {
308     // Load same-site server-redirect page into Iframe,
309     // which redirects to same-site page.
310     GURL server_redirect_http_url(test_server()->GetURL(
311         "server-redirect?files/title1.html"));
312     EXPECT_TRUE(NavigateIframeToURL(shell(),
313                                     server_redirect_http_url, "test"));
314     EXPECT_EQ(observer.navigation_url(), http_url);
315     EXPECT_TRUE(observer.navigation_succeeded());
316    }
317 
318   {
319     // Load same-site client-redirect page into Iframe,
320     // which redirects to same-site page.
321     GURL client_redirect_http_url(test_server()->GetURL(
322         "client-redirect?" + http_url.spec()));
323     RedirectNotificationObserver load_observer2(
324         NOTIFICATION_LOAD_STOP,
325         Source<NavigationController>(
326             &shell()->web_contents()->GetController()));
327 
328     EXPECT_TRUE(NavigateIframeToURL(shell(),
329                                     client_redirect_http_url, "test"));
330 
331     // Same-site Client-Redirect Page should be loaded successfully.
332     EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
333     EXPECT_TRUE(observer.navigation_succeeded());
334 
335     // Redirecting to Same-site Page should be loaded successfully.
336     load_observer2.Wait();
337     EXPECT_EQ(observer.navigation_url(), http_url);
338     EXPECT_TRUE(observer.navigation_succeeded());
339   }
340 }
341 
342 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
343 // security checks are back in place.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,DISABLED_CrossSiteIframeRedirectTwice)344 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
345                        DISABLED_CrossSiteIframeRedirectTwice) {
346   ASSERT_TRUE(test_server()->Start());
347   net::SpawnedTestServer https_server(
348       net::SpawnedTestServer::TYPE_HTTPS,
349       net::SpawnedTestServer::kLocalhost,
350       base::FilePath(FILE_PATH_LITERAL("content/test/data")));
351   ASSERT_TRUE(https_server.Start());
352 
353   GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
354   GURL http_url(test_server()->GetURL("files/title1.html"));
355   GURL https_url(https_server.GetURL("files/title1.html"));
356 
357   NavigateToURL(shell(), main_url);
358 
359   SitePerProcessWebContentsObserver observer(shell()->web_contents());
360   {
361     // Load client-redirect page pointing to a cross-site client-redirect page,
362     // which eventually redirects back to same-site page.
363     GURL client_redirect_https_url(https_server.GetURL(
364         "client-redirect?" + http_url.spec()));
365     GURL client_redirect_http_url(test_server()->GetURL(
366         "client-redirect?" + client_redirect_https_url.spec()));
367 
368     // We should wait until second client redirect get cancelled.
369     RedirectNotificationObserver load_observer2(
370         NOTIFICATION_LOAD_STOP,
371         Source<NavigationController>(
372             &shell()->web_contents()->GetController()));
373 
374     EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test"));
375 
376     // DidFailProvisionalLoad when navigating to client_redirect_https_url.
377     load_observer2.Wait();
378     EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
379     EXPECT_FALSE(observer.navigation_succeeded());
380   }
381 
382   {
383     // Load server-redirect page pointing to a cross-site server-redirect page,
384     // which eventually redirect back to same-site page.
385     GURL server_redirect_https_url(https_server.GetURL(
386         "server-redirect?" + http_url.spec()));
387     GURL server_redirect_http_url(test_server()->GetURL(
388         "server-redirect?" + server_redirect_https_url.spec()));
389     EXPECT_TRUE(NavigateIframeToURL(shell(),
390                                     server_redirect_http_url, "test"));
391     EXPECT_EQ(observer.navigation_url(), http_url);
392     EXPECT_TRUE(observer.navigation_succeeded());
393   }
394 
395   {
396     // Load server-redirect page pointing to a cross-site server-redirect page,
397     // which eventually redirects back to cross-site page.
398     GURL server_redirect_https_url(https_server.GetURL(
399         "server-redirect?" + https_url.spec()));
400     GURL server_redirect_http_url(test_server()->GetURL(
401         "server-redirect?" + server_redirect_https_url.spec()));
402     EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
403 
404     // DidFailProvisionalLoad when navigating to https_url.
405     EXPECT_EQ(observer.navigation_url(), https_url);
406     EXPECT_FALSE(observer.navigation_succeeded());
407   }
408 
409   {
410     // Load server-redirect page pointing to a cross-site client-redirect page,
411     // which eventually redirects back to same-site page.
412     GURL client_redirect_http_url(https_server.GetURL(
413         "client-redirect?" + http_url.spec()));
414     GURL server_redirect_http_url(test_server()->GetURL(
415         "server-redirect?" + client_redirect_http_url.spec()));
416     EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
417 
418     // DidFailProvisionalLoad when navigating to client_redirect_http_url.
419     EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
420     EXPECT_FALSE(observer.navigation_succeeded());
421   }
422 }
423 
424 // Ensures FrameTree correctly reflects page structure during navigations.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,FrameTreeShape)425 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
426                        FrameTreeShape) {
427   host_resolver()->AddRule("*", "127.0.0.1");
428   ASSERT_TRUE(test_server()->Start());
429 
430   GURL base_url = test_server()->GetURL("files/site_isolation/");
431   GURL::Replacements replace_host;
432   std::string host_str("A.com");  // Must stay in scope with replace_host.
433   replace_host.SetHostStr(host_str);
434   base_url = base_url.ReplaceComponents(replace_host);
435 
436   // Load doc without iframes. Verify FrameTree just has root.
437   // Frame tree:
438   //   Site-A Root
439   NavigateToURL(shell(), base_url.Resolve("blank.html"));
440   FrameTreeNode* root =
441       static_cast<WebContentsImpl*>(shell()->web_contents())->
442       GetFrameTree()->root();
443   EXPECT_EQ(0U, root->child_count());
444 
445   // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
446   // Frame tree:
447   //   Site-A Root -- Site-A frame1
448   //              \-- Site-A frame2
449   WindowedNotificationObserver observer1(
450       content::NOTIFICATION_LOAD_STOP,
451       content::Source<NavigationController>(
452           &shell()->web_contents()->GetController()));
453   NavigateToURL(shell(), base_url.Resolve("frames-X-X.html"));
454   observer1.Wait();
455   ASSERT_EQ(2U, root->child_count());
456   EXPECT_EQ(0U, root->child_at(0)->child_count());
457   EXPECT_EQ(0U, root->child_at(1)->child_count());
458 }
459 
460 // TODO(ajwong): Talk with nasko and merge this functionality with
461 // FrameTreeShape.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,FrameTreeShape2)462 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
463                        FrameTreeShape2) {
464   host_resolver()->AddRule("*", "127.0.0.1");
465   ASSERT_TRUE(test_server()->Start());
466 
467   NavigateToURL(shell(),
468                 test_server()->GetURL("files/frame_tree/top.html"));
469 
470   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
471   RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
472       wc->GetRenderViewHost());
473   FrameTreeNode* root = wc->GetFrameTree()->root();
474 
475   // Check that the root node is properly created with the frame id of the
476   // initial navigation.
477   ASSERT_EQ(3UL, root->child_count());
478   EXPECT_EQ(std::string(), root->frame_name());
479   EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
480 
481   ASSERT_EQ(2UL, root->child_at(0)->child_count());
482   EXPECT_STREQ("1-1-name", root->child_at(0)->frame_name().c_str());
483 
484   // Verify the deepest node exists and has the right name.
485   ASSERT_EQ(2UL, root->child_at(2)->child_count());
486   EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count());
487   EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count());
488   EXPECT_STREQ("3-1-id",
489       root->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str());
490 
491   // Navigate to about:blank, which should leave only the root node of the frame
492   // tree in the browser process.
493   NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
494 
495   root = wc->GetFrameTree()->root();
496   EXPECT_EQ(0UL, root->child_count());
497   EXPECT_EQ(std::string(), root->frame_name());
498   EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
499 }
500 
501 // Test that we can navigate away if the previous renderer doesn't clean up its
502 // child frames.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,FrameTreeAfterCrash)503 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FrameTreeAfterCrash) {
504   ASSERT_TRUE(test_server()->Start());
505   NavigateToURL(shell(),
506                 test_server()->GetURL("files/frame_tree/top.html"));
507 
508   // Crash the renderer so that it doesn't send any FrameDetached messages.
509   WindowedNotificationObserver crash_observer(
510       NOTIFICATION_RENDERER_PROCESS_CLOSED,
511       NotificationService::AllSources());
512   NavigateToURL(shell(), GURL(kChromeUICrashURL));
513   crash_observer.Wait();
514 
515   // The frame tree should be cleared, and the frame ID should be reset.
516   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
517   RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
518       wc->GetRenderViewHost());
519   FrameTreeNode* root = wc->GetFrameTree()->root();
520   EXPECT_EQ(0UL, root->child_count());
521   EXPECT_EQ(FrameTreeNode::kInvalidFrameId, root->frame_id());
522   EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
523 
524   // Navigate to a new URL.
525   NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
526 
527   // The frame ID should now be set.
528   EXPECT_EQ(0UL, root->child_count());
529   EXPECT_NE(FrameTreeNode::kInvalidFrameId, root->frame_id());
530   EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
531 }
532 
533 // Test that we can navigate away if the previous renderer doesn't clean up its
534 // child frames.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,NavigateWithLeftoverFrames)535 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigateWithLeftoverFrames) {
536   host_resolver()->AddRule("*", "127.0.0.1");
537   ASSERT_TRUE(test_server()->Start());
538 
539   GURL base_url = test_server()->GetURL("files/site_isolation/");
540   GURL::Replacements replace_host;
541   std::string host_str("A.com");  // Must stay in scope with replace_host.
542   replace_host.SetHostStr(host_str);
543   base_url = base_url.ReplaceComponents(replace_host);
544 
545   NavigateToURL(shell(),
546                 test_server()->GetURL("files/frame_tree/top.html"));
547 
548   // Hang the renderer so that it doesn't send any FrameDetached messages.
549   // (This navigation will never complete, so don't wait for it.)
550   shell()->LoadURL(GURL(kChromeUIHangURL));
551 
552   // Check that the frame tree still has children.
553   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
554   FrameTreeNode* root = wc->GetFrameTree()->root();
555   ASSERT_EQ(3UL, root->child_count());
556 
557   // Navigate to a new URL.  We use LoadURL because NavigateToURL will try to
558   // wait for the previous navigation to stop.
559   TestNavigationObserver tab_observer(wc, 1);
560   shell()->LoadURL(base_url.Resolve("blank.html"));
561   tab_observer.Wait();
562 
563   // The frame tree should now be cleared, and the frame ID should be valid.
564   EXPECT_EQ(0UL, root->child_count());
565   EXPECT_NE(FrameTreeNode::kInvalidFrameId, root->frame_id());
566 }
567 
568 // Tests that the |should_replace_current_entry| flag persists correctly across
569 // request transfers that began with a cross-process navigation.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,ReplaceEntryCrossProcessThenTranfers)570 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
571                        ReplaceEntryCrossProcessThenTranfers) {
572   const NavigationController& controller =
573       shell()->web_contents()->GetController();
574   host_resolver()->AddRule("*", "127.0.0.1");
575   ASSERT_TRUE(test_server()->Start());
576 
577   // These must all stay in scope with replace_host.
578   GURL::Replacements replace_host;
579   std::string a_com("A.com");
580   std::string b_com("B.com");
581 
582   // Navigate to a starting URL, so there is a history entry to replace.
583   GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
584   NavigateToURL(shell(), url1);
585 
586   // Force all future navigations to transfer. Note that this includes same-site
587   // navigiations which may cause double process swaps (via OpenURL and then via
588   // transfer). This test intentionally exercises that case.
589   ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
590 
591   // Navigate to a page on A.com with entry replacement. This navigation is
592   // cross-site, so the renderer will send it to the browser via OpenURL to give
593   // to a new process. It will then be transferred into yet another process due
594   // to the call above.
595   GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
596   replace_host.SetHostStr(a_com);
597   url2 = url2.ReplaceComponents(replace_host);
598   NavigateToURLContentInitiated(shell(), url2, true);
599 
600   // There should be one history entry. url2 should have replaced url1.
601   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
602   EXPECT_EQ(1, controller.GetEntryCount());
603   EXPECT_EQ(0, controller.GetCurrentEntryIndex());
604   EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
605 
606   // Now navigate as before to a page on B.com, but normally (without
607   // replacement). This will still perform a double process-swap as above, via
608   // OpenURL and then transfer.
609   GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
610   replace_host.SetHostStr(b_com);
611   url3 = url3.ReplaceComponents(replace_host);
612   NavigateToURLContentInitiated(shell(), url3, false);
613 
614   // There should be two history entries. url2 should have replaced url1. url2
615   // should not have replaced url3.
616   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
617   EXPECT_EQ(2, controller.GetEntryCount());
618   EXPECT_EQ(1, controller.GetCurrentEntryIndex());
619   EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
620   EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
621 }
622 
623 // Tests that the |should_replace_current_entry| flag persists correctly across
624 // request transfers that began with a content-initiated in-process
625 // navigation. This test is the same as the test above, except transfering from
626 // in-process.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,ReplaceEntryInProcessThenTranfers)627 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
628                        ReplaceEntryInProcessThenTranfers) {
629   const NavigationController& controller =
630       shell()->web_contents()->GetController();
631   ASSERT_TRUE(test_server()->Start());
632 
633   // Navigate to a starting URL, so there is a history entry to replace.
634   GURL url = test_server()->GetURL("files/site_isolation/blank.html?1");
635   NavigateToURL(shell(), url);
636 
637   // Force all future navigations to transfer. Note that this includes same-site
638   // navigiations which may cause double process swaps (via OpenURL and then via
639   // transfer). All navigations in this test are same-site, so it only swaps
640   // processes via request transfer.
641   ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
642 
643   // Navigate in-process with entry replacement. It will then be transferred
644   // into a new one due to the call above.
645   GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
646   NavigateToURLContentInitiated(shell(), url2, true);
647 
648   // There should be one history entry. url2 should have replaced url1.
649   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
650   EXPECT_EQ(1, controller.GetEntryCount());
651   EXPECT_EQ(0, controller.GetCurrentEntryIndex());
652   EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
653 
654   // Now navigate as before, but without replacement.
655   GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
656   NavigateToURLContentInitiated(shell(), url3, false);
657 
658   // There should be two history entries. url2 should have replaced url1. url2
659   // should not have replaced url3.
660   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
661   EXPECT_EQ(2, controller.GetEntryCount());
662   EXPECT_EQ(1, controller.GetCurrentEntryIndex());
663   EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
664   EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
665 }
666 
667 // Tests that the |should_replace_current_entry| flag persists correctly across
668 // request transfers that cross processes twice from renderer policy.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,ReplaceEntryCrossProcessTwice)669 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
670                        ReplaceEntryCrossProcessTwice) {
671   const NavigationController& controller =
672       shell()->web_contents()->GetController();
673   host_resolver()->AddRule("*", "127.0.0.1");
674   ASSERT_TRUE(test_server()->Start());
675 
676   // These must all stay in scope with replace_host.
677   GURL::Replacements replace_host;
678   std::string a_com("A.com");
679   std::string b_com("B.com");
680 
681   // Navigate to a starting URL, so there is a history entry to replace.
682   GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
683   NavigateToURL(shell(), url1);
684 
685   // Navigate to a page on A.com which redirects to B.com with entry
686   // replacement. This will switch processes via OpenURL twice. First to A.com,
687   // and second in response to the server redirect to B.com. The second swap is
688   // also renderer-initiated via OpenURL because decidePolicyForNavigation is
689   // currently applied on redirects.
690   GURL url2b = test_server()->GetURL("files/site_isolation/blank.html?2");
691   replace_host.SetHostStr(b_com);
692   url2b = url2b.ReplaceComponents(replace_host);
693   GURL url2a = test_server()->GetURL(
694       "server-redirect?" + net::EscapeQueryParamValue(url2b.spec(), false));
695   replace_host.SetHostStr(a_com);
696   url2a = url2a.ReplaceComponents(replace_host);
697   NavigateToURLContentInitiated(shell(), url2a, true);
698 
699   // There should be one history entry. url2b should have replaced url1.
700   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
701   EXPECT_EQ(1, controller.GetEntryCount());
702   EXPECT_EQ(0, controller.GetCurrentEntryIndex());
703   EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
704 
705   // Now repeat without replacement.
706   GURL url3b = test_server()->GetURL("files/site_isolation/blank.html?3");
707   replace_host.SetHostStr(b_com);
708   url3b = url3b.ReplaceComponents(replace_host);
709   GURL url3a = test_server()->GetURL(
710       "server-redirect?" + net::EscapeQueryParamValue(url3b.spec(), false));
711   replace_host.SetHostStr(a_com);
712   url3a = url3a.ReplaceComponents(replace_host);
713   NavigateToURLContentInitiated(shell(), url3a, false);
714 
715   // There should be two history entries. url2b should have replaced url1. url2b
716   // should not have replaced url3b.
717   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
718   EXPECT_EQ(2, controller.GetEntryCount());
719   EXPECT_EQ(1, controller.GetCurrentEntryIndex());
720   EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
721   EXPECT_EQ(url3b, controller.GetEntryAtIndex(1)->GetURL());
722 }
723 
724 }  // namespace content
725