IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / site_per_process_browsertest.cc
blob1523ec543d674def40af35ebff109ed8766a8ffc
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/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"
28 namespace content {
30 class SitePerProcessWebContentsObserver: public WebContentsObserver {
31 public:
32 explicit SitePerProcessWebContentsObserver(WebContents* web_contents)
33 : WebContentsObserver(web_contents),
34 navigation_succeeded_(false) {}
35 virtual ~SitePerProcessWebContentsObserver() {}
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;
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;
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;
71 const GURL& navigation_url() const {
72 return navigation_url_;
75 int navigation_succeeded() const { return navigation_succeeded_; }
77 private:
78 GURL navigation_url_;
79 bool navigation_succeeded_;
81 DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver);
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();
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();
98 // Returns NotificationService::AllSources() if we haven't observed a
99 // notification yet.
100 const NotificationSource& source() const {
101 return source_;
104 const NotificationDetails& details() const {
105 return details_;
108 // NotificationObserver:
109 virtual void Observe(int type,
110 const NotificationSource& source,
111 const NotificationDetails& details) OVERRIDE;
113 private:
114 bool seen_;
115 bool seen_twice_;
116 bool running_;
117 NotificationRegistrar registrar_;
119 NotificationSource source_;
120 NotificationDetails details_;
121 scoped_refptr<MessageLoopRunner> message_loop_runner_;
123 DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver);
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);
135 RedirectNotificationObserver::~RedirectNotificationObserver() {}
137 void RedirectNotificationObserver::Wait() {
138 if (seen_ && seen_twice_)
139 return;
141 running_ = true;
142 message_loop_runner_ = new MessageLoopRunner;
143 message_loop_runner_->Run();
144 EXPECT_TRUE(seen_);
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;
158 message_loop_runner_->Quit();
159 running_ = false;
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;
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();
193 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
194 command_line->AppendSwitch(switches::kSitePerProcess);
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"));
211 NavigateToURL(shell(), main_url);
213 SitePerProcessWebContentsObserver observer(shell()->web_contents());
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());
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());
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());
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());
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"));
256 NavigateToURL(shell(), main_url);
258 SitePerProcessWebContentsObserver observer(shell()->web_contents());
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());
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());
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());
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"));
302 EXPECT_EQ(observer.navigation_url(), https_url);
303 EXPECT_FALSE(observer.navigation_succeeded());
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()));
312 RedirectNotificationObserver load_observer2(
313 NOTIFICATION_LOAD_STOP,
314 Source<NavigationController>(
315 &shell()->web_contents()->GetController()));
317 EXPECT_TRUE(NavigateIframeToURL(shell(),
318 client_redirect_http_url, "test"));
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());
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());
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());
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()));
351 EXPECT_TRUE(NavigateIframeToURL(shell(),
352 client_redirect_http_url, "test"));
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());
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());
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());
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"));
380 NavigateToURL(shell(), main_url);
382 SitePerProcessWebContentsObserver observer(shell()->web_contents());
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()));
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()));
397 EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test"));
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());
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());
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"));
427 // DidFailProvisionalLoad when navigating to https_url.
428 EXPECT_EQ(observer.navigation_url(), https_url);
429 EXPECT_FALSE(observer.navigation_succeeded());
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"));
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());
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());
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");
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);
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);
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);
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());
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);
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());
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());
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);
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);
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);
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());
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);
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());
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());
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");
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);
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);
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());
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);
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());
603 } // namespace content