Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / content / browser / site_per_process_browsertest.cc
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:
32   explicit SitePerProcessWebContentsObserver(WebContents* web_contents)
33       : WebContentsObserver(web_contents),
34         navigation_succeeded_(false) {}
35   virtual ~SitePerProcessWebContentsObserver() {}
36
37   virtual void DidStartProvisionalLoadForFrame(
38       int64 frame_id,
39       int64 parent_frame_id,
40       bool is_main_frame,
41       const GURL& validated_url,
42       bool is_error_page,
43       bool is_iframe_srcdoc,
44       RenderViewHost* render_view_host) OVERRIDE {
45     navigation_succeeded_ = false;
46   }
47
48   virtual void DidFailProvisionalLoad(
49       int64 frame_id,
50       const base::string16& frame_unique_name,
51       bool is_main_frame,
52       const GURL& validated_url,
53       int error_code,
54       const base::string16& error_description,
55       RenderViewHost* render_view_host) OVERRIDE {
56     navigation_url_ = validated_url;
57     navigation_succeeded_ = false;
58   }
59
60   virtual void DidCommitProvisionalLoadForFrame(
61       int64 frame_id,
62       const base::string16& frame_unique_name,
63       bool is_main_frame,
64       const GURL& url,
65       PageTransition transition_type,
66       RenderViewHost* render_view_host) OVERRIDE{
67     navigation_url_ = url;
68     navigation_succeeded_ = true;
69   }
70
71   const GURL& navigation_url() const {
72     return navigation_url_;
73   }
74
75   int navigation_succeeded() const { return navigation_succeeded_; }
76
77  private:
78   GURL navigation_url_;
79   bool navigation_succeeded_;
80
81   DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver);
82 };
83
84 class RedirectNotificationObserver : public NotificationObserver {
85  public:
86   // Register to listen for notifications of the given type from either a
87   // specific source, or from all sources if |source| is
88   // NotificationService::AllSources().
89   RedirectNotificationObserver(int notification_type,
90                                const NotificationSource& source);
91   virtual ~RedirectNotificationObserver();
92
93   // Wait until the specified notification occurs.  If the notification was
94   // emitted between the construction of this object and this call then it
95   // returns immediately.
96   void Wait();
97
98   // Returns NotificationService::AllSources() if we haven't observed a
99   // notification yet.
100   const NotificationSource& source() const {
101     return source_;
102   }
103
104   const NotificationDetails& details() const {
105     return details_;
106   }
107
108   // NotificationObserver:
109   virtual void Observe(int type,
110                        const NotificationSource& source,
111                        const NotificationDetails& details) OVERRIDE;
112
113  private:
114   bool seen_;
115   bool seen_twice_;
116   bool running_;
117   NotificationRegistrar registrar_;
118
119   NotificationSource source_;
120   NotificationDetails details_;
121   scoped_refptr<MessageLoopRunner> message_loop_runner_;
122
123   DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver);
124 };
125
126 RedirectNotificationObserver::RedirectNotificationObserver(
127     int notification_type,
128     const NotificationSource& source)
129     : seen_(false),
130       running_(false),
131       source_(NotificationService::AllSources()) {
132   registrar_.Add(this, notification_type, source);
133 }
134
135 RedirectNotificationObserver::~RedirectNotificationObserver() {}
136
137 void RedirectNotificationObserver::Wait() {
138   if (seen_ && seen_twice_)
139     return;
140
141   running_ = true;
142   message_loop_runner_ = new MessageLoopRunner;
143   message_loop_runner_->Run();
144   EXPECT_TRUE(seen_);
145 }
146
147 void RedirectNotificationObserver::Observe(
148     int type,
149     const NotificationSource& source,
150     const NotificationDetails& details) {
151   source_ = source;
152   details_ = details;
153   seen_twice_ = seen_;
154   seen_ = true;
155   if (!running_)
156     return;
157
158   message_loop_runner_->Quit();
159   running_ = false;
160 }
161
162 class SitePerProcessBrowserTest : public ContentBrowserTest {
163  protected:
164   bool NavigateIframeToURL(Shell* window,
165                            const GURL& url,
166                            std::string iframe_id) {
167     std::string script = base::StringPrintf(
168         "var iframes = document.getElementById('%s');iframes.src='%s';",
169         iframe_id.c_str(), url.spec().c_str());
170     WindowedNotificationObserver load_observer(
171         NOTIFICATION_LOAD_STOP,
172         Source<NavigationController>(
173             &shell()->web_contents()->GetController()));
174     bool result = ExecuteScript(window->web_contents(), script);
175     load_observer.Wait();
176     return result;
177   }
178
179   void NavigateToURLContentInitiated(Shell* window,
180                                      const GURL& url,
181                                      bool should_replace_current_entry) {
182     std::string script;
183     if (should_replace_current_entry)
184       script = base::StringPrintf("location.replace('%s')", url.spec().c_str());
185     else
186       script = base::StringPrintf("location.href = '%s'", url.spec().c_str());
187     TestNavigationObserver load_observer(shell()->web_contents(), 1);
188     bool result = ExecuteScript(window->web_contents(), script);
189     EXPECT_TRUE(result);
190     load_observer.Wait();
191   }
192
193   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
194     command_line->AppendSwitch(switches::kSitePerProcess);
195   }
196 };
197
198 // Ensure that we can complete a cross-process subframe navigation.
199 // TODO(nasko): Disable this test for now, since enabling swapping out of
200 // RenderFrameHosts causes this to break. Fix this test once
201 // didFailProvisionalLoad is moved from RenderView to RenderFrame.
202 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DISABLED_CrossSiteIframe) {
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());
209   GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
210
211   NavigateToURL(shell(), main_url);
212
213   SitePerProcessWebContentsObserver observer(shell()->web_contents());
214
215   // Load same-site page into iframe.
216   GURL http_url(test_server()->GetURL("files/title1.html"));
217   EXPECT_TRUE(NavigateIframeToURL(shell(), http_url, "test"));
218   EXPECT_EQ(observer.navigation_url(), http_url);
219   EXPECT_TRUE(observer.navigation_succeeded());
220
221   // Load cross-site page into iframe.
222   GURL https_url(https_server.GetURL("files/title1.html"));
223   EXPECT_TRUE(NavigateIframeToURL(shell(), https_url, "test"));
224   EXPECT_EQ(observer.navigation_url(), https_url);
225   EXPECT_TRUE(observer.navigation_succeeded());
226
227   // Ensure that we have created a new process for the subframe.
228   FrameTreeNode* root =
229       static_cast<WebContentsImpl*>(shell()->web_contents())->
230           GetFrameTree()->root();
231   ASSERT_EQ(1U, root->child_count());
232   FrameTreeNode* child = root->child_at(0);
233   EXPECT_NE(shell()->web_contents()->GetRenderViewHost(),
234             child->current_frame_host()->render_view_host());
235   EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
236             child->current_frame_host()->render_view_host()->GetSiteInstance());
237   EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(),
238             child->current_frame_host()->GetProcess());
239 }
240
241 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
242 // security checks are back in place.
243 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
244                        DISABLED_CrossSiteIframeRedirectOnce) {
245   ASSERT_TRUE(test_server()->Start());
246   net::SpawnedTestServer https_server(
247       net::SpawnedTestServer::TYPE_HTTPS,
248       net::SpawnedTestServer::kLocalhost,
249       base::FilePath(FILE_PATH_LITERAL("content/test/data")));
250   ASSERT_TRUE(https_server.Start());
251
252   GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
253   GURL http_url(test_server()->GetURL("files/title1.html"));
254   GURL https_url(https_server.GetURL("files/title1.html"));
255
256   NavigateToURL(shell(), main_url);
257
258   SitePerProcessWebContentsObserver observer(shell()->web_contents());
259   {
260     // Load cross-site client-redirect page into Iframe.
261     // Should be blocked.
262     GURL client_redirect_https_url(https_server.GetURL(
263         "client-redirect?files/title1.html"));
264     EXPECT_TRUE(NavigateIframeToURL(shell(),
265                                     client_redirect_https_url, "test"));
266     // DidFailProvisionalLoad when navigating to client_redirect_https_url.
267     EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
268     EXPECT_FALSE(observer.navigation_succeeded());
269   }
270
271   {
272     // Load cross-site server-redirect page into Iframe,
273     // which redirects to same-site page.
274     GURL server_redirect_http_url(https_server.GetURL(
275         "server-redirect?" + http_url.spec()));
276     EXPECT_TRUE(NavigateIframeToURL(shell(),
277                                     server_redirect_http_url, "test"));
278     EXPECT_EQ(observer.navigation_url(), http_url);
279     EXPECT_TRUE(observer.navigation_succeeded());
280   }
281
282   {
283     // Load cross-site server-redirect page into Iframe,
284     // which redirects to cross-site page.
285     GURL server_redirect_http_url(https_server.GetURL(
286         "server-redirect?files/title1.html"));
287     EXPECT_TRUE(NavigateIframeToURL(shell(),
288                                     server_redirect_http_url, "test"));
289     // DidFailProvisionalLoad when navigating to https_url.
290     EXPECT_EQ(observer.navigation_url(), https_url);
291     EXPECT_FALSE(observer.navigation_succeeded());
292   }
293
294   {
295     // Load same-site server-redirect page into Iframe,
296     // which redirects to cross-site page.
297     GURL server_redirect_http_url(test_server()->GetURL(
298         "server-redirect?" + https_url.spec()));
299     EXPECT_TRUE(NavigateIframeToURL(shell(),
300                                     server_redirect_http_url, "test"));
301
302     EXPECT_EQ(observer.navigation_url(), https_url);
303     EXPECT_FALSE(observer.navigation_succeeded());
304    }
305
306   {
307     // Load same-site client-redirect page into Iframe,
308     // which redirects to cross-site page.
309     GURL client_redirect_http_url(test_server()->GetURL(
310         "client-redirect?" + https_url.spec()));
311
312     RedirectNotificationObserver load_observer2(
313         NOTIFICATION_LOAD_STOP,
314         Source<NavigationController>(
315             &shell()->web_contents()->GetController()));
316
317     EXPECT_TRUE(NavigateIframeToURL(shell(),
318                                     client_redirect_http_url, "test"));
319
320     // Same-site Client-Redirect Page should be loaded successfully.
321     EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
322     EXPECT_TRUE(observer.navigation_succeeded());
323
324     // Redirecting to Cross-site Page should be blocked.
325     load_observer2.Wait();
326     EXPECT_EQ(observer.navigation_url(), https_url);
327     EXPECT_FALSE(observer.navigation_succeeded());
328   }
329
330   {
331     // Load same-site server-redirect page into Iframe,
332     // which redirects to same-site page.
333     GURL server_redirect_http_url(test_server()->GetURL(
334         "server-redirect?files/title1.html"));
335     EXPECT_TRUE(NavigateIframeToURL(shell(),
336                                     server_redirect_http_url, "test"));
337     EXPECT_EQ(observer.navigation_url(), http_url);
338     EXPECT_TRUE(observer.navigation_succeeded());
339    }
340
341   {
342     // Load same-site client-redirect page into Iframe,
343     // which redirects to same-site page.
344     GURL client_redirect_http_url(test_server()->GetURL(
345         "client-redirect?" + http_url.spec()));
346     RedirectNotificationObserver load_observer2(
347         NOTIFICATION_LOAD_STOP,
348         Source<NavigationController>(
349             &shell()->web_contents()->GetController()));
350
351     EXPECT_TRUE(NavigateIframeToURL(shell(),
352                                     client_redirect_http_url, "test"));
353
354     // Same-site Client-Redirect Page should be loaded successfully.
355     EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
356     EXPECT_TRUE(observer.navigation_succeeded());
357
358     // Redirecting to Same-site Page should be loaded successfully.
359     load_observer2.Wait();
360     EXPECT_EQ(observer.navigation_url(), http_url);
361     EXPECT_TRUE(observer.navigation_succeeded());
362   }
363 }
364
365 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
366 // security checks are back in place.
367 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
368                        DISABLED_CrossSiteIframeRedirectTwice) {
369   ASSERT_TRUE(test_server()->Start());
370   net::SpawnedTestServer https_server(
371       net::SpawnedTestServer::TYPE_HTTPS,
372       net::SpawnedTestServer::kLocalhost,
373       base::FilePath(FILE_PATH_LITERAL("content/test/data")));
374   ASSERT_TRUE(https_server.Start());
375
376   GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
377   GURL http_url(test_server()->GetURL("files/title1.html"));
378   GURL https_url(https_server.GetURL("files/title1.html"));
379
380   NavigateToURL(shell(), main_url);
381
382   SitePerProcessWebContentsObserver observer(shell()->web_contents());
383   {
384     // Load client-redirect page pointing to a cross-site client-redirect page,
385     // which eventually redirects back to same-site page.
386     GURL client_redirect_https_url(https_server.GetURL(
387         "client-redirect?" + http_url.spec()));
388     GURL client_redirect_http_url(test_server()->GetURL(
389         "client-redirect?" + client_redirect_https_url.spec()));
390
391     // We should wait until second client redirect get cancelled.
392     RedirectNotificationObserver load_observer2(
393         NOTIFICATION_LOAD_STOP,
394         Source<NavigationController>(
395             &shell()->web_contents()->GetController()));
396
397     EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test"));
398
399     // DidFailProvisionalLoad when navigating to client_redirect_https_url.
400     load_observer2.Wait();
401     EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
402     EXPECT_FALSE(observer.navigation_succeeded());
403   }
404
405   {
406     // Load server-redirect page pointing to a cross-site server-redirect page,
407     // which eventually redirect back to same-site page.
408     GURL server_redirect_https_url(https_server.GetURL(
409         "server-redirect?" + http_url.spec()));
410     GURL server_redirect_http_url(test_server()->GetURL(
411         "server-redirect?" + server_redirect_https_url.spec()));
412     EXPECT_TRUE(NavigateIframeToURL(shell(),
413                                     server_redirect_http_url, "test"));
414     EXPECT_EQ(observer.navigation_url(), http_url);
415     EXPECT_TRUE(observer.navigation_succeeded());
416   }
417
418   {
419     // Load server-redirect page pointing to a cross-site server-redirect page,
420     // which eventually redirects back to cross-site page.
421     GURL server_redirect_https_url(https_server.GetURL(
422         "server-redirect?" + https_url.spec()));
423     GURL server_redirect_http_url(test_server()->GetURL(
424         "server-redirect?" + server_redirect_https_url.spec()));
425     EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
426
427     // DidFailProvisionalLoad when navigating to https_url.
428     EXPECT_EQ(observer.navigation_url(), https_url);
429     EXPECT_FALSE(observer.navigation_succeeded());
430   }
431
432   {
433     // Load server-redirect page pointing to a cross-site client-redirect page,
434     // which eventually redirects back to same-site page.
435     GURL client_redirect_http_url(https_server.GetURL(
436         "client-redirect?" + http_url.spec()));
437     GURL server_redirect_http_url(test_server()->GetURL(
438         "server-redirect?" + client_redirect_http_url.spec()));
439     EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
440
441     // DidFailProvisionalLoad when navigating to client_redirect_http_url.
442     EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
443     EXPECT_FALSE(observer.navigation_succeeded());
444   }
445 }
446
447 // Tests that the |should_replace_current_entry| flag persists correctly across
448 // request transfers that began with a cross-process navigation.
449 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
450                        ReplaceEntryCrossProcessThenTranfers) {
451   const NavigationController& controller =
452       shell()->web_contents()->GetController();
453   host_resolver()->AddRule("*", "127.0.0.1");
454   ASSERT_TRUE(test_server()->Start());
455
456   // These must all stay in scope with replace_host.
457   GURL::Replacements replace_host;
458   std::string a_com("A.com");
459   std::string b_com("B.com");
460
461   // Navigate to a starting URL, so there is a history entry to replace.
462   GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
463   NavigateToURL(shell(), url1);
464
465   // Force all future navigations to transfer. Note that this includes same-site
466   // navigiations which may cause double process swaps (via OpenURL and then via
467   // transfer). This test intentionally exercises that case.
468   ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
469
470   // Navigate to a page on A.com with entry replacement. This navigation is
471   // cross-site, so the renderer will send it to the browser via OpenURL to give
472   // to a new process. It will then be transferred into yet another process due
473   // to the call above.
474   GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
475   replace_host.SetHostStr(a_com);
476   url2 = url2.ReplaceComponents(replace_host);
477   NavigateToURLContentInitiated(shell(), url2, true);
478
479   // There should be one history entry. url2 should have replaced url1.
480   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
481   EXPECT_EQ(1, controller.GetEntryCount());
482   EXPECT_EQ(0, controller.GetCurrentEntryIndex());
483   EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
484
485   // Now navigate as before to a page on B.com, but normally (without
486   // replacement). This will still perform a double process-swap as above, via
487   // OpenURL and then transfer.
488   GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
489   replace_host.SetHostStr(b_com);
490   url3 = url3.ReplaceComponents(replace_host);
491   NavigateToURLContentInitiated(shell(), url3, false);
492
493   // There should be two history entries. url2 should have replaced url1. url2
494   // should not have replaced url3.
495   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
496   EXPECT_EQ(2, controller.GetEntryCount());
497   EXPECT_EQ(1, controller.GetCurrentEntryIndex());
498   EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
499   EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
500 }
501
502 // Tests that the |should_replace_current_entry| flag persists correctly across
503 // request transfers that began with a content-initiated in-process
504 // navigation. This test is the same as the test above, except transfering from
505 // in-process.
506 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
507                        ReplaceEntryInProcessThenTranfers) {
508   const NavigationController& controller =
509       shell()->web_contents()->GetController();
510   ASSERT_TRUE(test_server()->Start());
511
512   // Navigate to a starting URL, so there is a history entry to replace.
513   GURL url = test_server()->GetURL("files/site_isolation/blank.html?1");
514   NavigateToURL(shell(), url);
515
516   // Force all future navigations to transfer. Note that this includes same-site
517   // navigiations which may cause double process swaps (via OpenURL and then via
518   // transfer). All navigations in this test are same-site, so it only swaps
519   // processes via request transfer.
520   ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
521
522   // Navigate in-process with entry replacement. It will then be transferred
523   // into a new one due to the call above.
524   GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
525   NavigateToURLContentInitiated(shell(), url2, true);
526
527   // There should be one history entry. url2 should have replaced url1.
528   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
529   EXPECT_EQ(1, controller.GetEntryCount());
530   EXPECT_EQ(0, controller.GetCurrentEntryIndex());
531   EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
532
533   // Now navigate as before, but without replacement.
534   GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
535   NavigateToURLContentInitiated(shell(), url3, false);
536
537   // There should be two history entries. url2 should have replaced url1. url2
538   // should not have replaced url3.
539   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
540   EXPECT_EQ(2, controller.GetEntryCount());
541   EXPECT_EQ(1, controller.GetCurrentEntryIndex());
542   EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
543   EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
544 }
545
546 // Tests that the |should_replace_current_entry| flag persists correctly across
547 // request transfers that cross processes twice from renderer policy.
548 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
549                        ReplaceEntryCrossProcessTwice) {
550   const NavigationController& controller =
551       shell()->web_contents()->GetController();
552   host_resolver()->AddRule("*", "127.0.0.1");
553   ASSERT_TRUE(test_server()->Start());
554
555   // These must all stay in scope with replace_host.
556   GURL::Replacements replace_host;
557   std::string a_com("A.com");
558   std::string b_com("B.com");
559
560   // Navigate to a starting URL, so there is a history entry to replace.
561   GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
562   NavigateToURL(shell(), url1);
563
564   // Navigate to a page on A.com which redirects to B.com with entry
565   // replacement. This will switch processes via OpenURL twice. First to A.com,
566   // and second in response to the server redirect to B.com. The second swap is
567   // also renderer-initiated via OpenURL because decidePolicyForNavigation is
568   // currently applied on redirects.
569   GURL url2b = test_server()->GetURL("files/site_isolation/blank.html?2");
570   replace_host.SetHostStr(b_com);
571   url2b = url2b.ReplaceComponents(replace_host);
572   GURL url2a = test_server()->GetURL(
573       "server-redirect?" + net::EscapeQueryParamValue(url2b.spec(), false));
574   replace_host.SetHostStr(a_com);
575   url2a = url2a.ReplaceComponents(replace_host);
576   NavigateToURLContentInitiated(shell(), url2a, true);
577
578   // There should be one history entry. url2b should have replaced url1.
579   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
580   EXPECT_EQ(1, controller.GetEntryCount());
581   EXPECT_EQ(0, controller.GetCurrentEntryIndex());
582   EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
583
584   // Now repeat without replacement.
585   GURL url3b = test_server()->GetURL("files/site_isolation/blank.html?3");
586   replace_host.SetHostStr(b_com);
587   url3b = url3b.ReplaceComponents(replace_host);
588   GURL url3a = test_server()->GetURL(
589       "server-redirect?" + net::EscapeQueryParamValue(url3b.spec(), false));
590   replace_host.SetHostStr(a_com);
591   url3a = url3a.ReplaceComponents(replace_host);
592   NavigateToURLContentInitiated(shell(), url3a, false);
593
594   // There should be two history entries. url2b should have replaced url1. url2b
595   // should not have replaced url3b.
596   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
597   EXPECT_EQ(2, controller.GetEntryCount());
598   EXPECT_EQ(1, controller.GetCurrentEntryIndex());
599   EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
600   EXPECT_EQ(url3b, controller.GetEntryAtIndex(1)->GetURL());
601 }
602
603 }  // namespace content