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/loader/resource_dispatcher_host_impl.h"
10 #include "content/browser/renderer_host/render_view_host_impl.h"
11 #include "content/browser/web_contents/web_contents_impl.h"
12 #include "content/public/browser/navigation_entry.h"
13 #include "content/public/browser/notification_observer.h"
14 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/resource_dispatcher_host_delegate.h"
17 #include "content/public/browser/resource_throttle.h"
18 #include "content/public/browser/web_contents_observer.h"
19 #include "content/public/common/content_switches.h"
20 #include "content/public/common/url_constants.h"
21 #include "content/public/test/browser_test_utils.h"
22 #include "content/public/test/test_navigation_observer.h"
23 #include "content/public/test/test_utils.h"
24 #include "content/shell/browser/shell.h"
25 #include "content/shell/browser/shell_content_browser_client.h"
26 #include "content/shell/browser/shell_resource_dispatcher_host_delegate.h"
27 #include "content/test/content_browser_test.h"
28 #include "content/test/content_browser_test_utils.h"
29 #include "net/base/escape.h"
30 #include "net/dns/mock_host_resolver.h"
31 #include "net/url_request/url_request.h"
32 #include "net/url_request/url_request_status.h"
36 class SitePerProcessWebContentsObserver: public WebContentsObserver {
38 explicit SitePerProcessWebContentsObserver(WebContents* web_contents)
39 : WebContentsObserver(web_contents),
40 navigation_succeeded_(false) {}
41 virtual ~SitePerProcessWebContentsObserver() {}
43 virtual void DidStartProvisionalLoadForFrame(
45 int64 parent_frame_id,
47 const GURL& validated_url,
49 bool is_iframe_srcdoc,
50 RenderViewHost* render_view_host) OVERRIDE {
51 navigation_succeeded_ = false;
54 virtual void DidFailProvisionalLoad(
56 const base::string16& frame_unique_name,
58 const GURL& validated_url,
60 const base::string16& error_description,
61 RenderViewHost* render_view_host) OVERRIDE {
62 navigation_url_ = validated_url;
63 navigation_succeeded_ = false;
66 virtual void DidCommitProvisionalLoadForFrame(
68 const base::string16& frame_unique_name,
71 PageTransition transition_type,
72 RenderViewHost* render_view_host) OVERRIDE{
73 navigation_url_ = url;
74 navigation_succeeded_ = true;
77 const GURL& navigation_url() const {
78 return navigation_url_;
81 int navigation_succeeded() const { return navigation_succeeded_; }
85 bool navigation_succeeded_;
87 DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver);
90 class RedirectNotificationObserver : public NotificationObserver {
92 // Register to listen for notifications of the given type from either a
93 // specific source, or from all sources if |source| is
94 // NotificationService::AllSources().
95 RedirectNotificationObserver(int notification_type,
96 const NotificationSource& source);
97 virtual ~RedirectNotificationObserver();
99 // Wait until the specified notification occurs. If the notification was
100 // emitted between the construction of this object and this call then it
101 // returns immediately.
104 // Returns NotificationService::AllSources() if we haven't observed a
106 const NotificationSource& source() const {
110 const NotificationDetails& details() const {
114 // NotificationObserver:
115 virtual void Observe(int type,
116 const NotificationSource& source,
117 const NotificationDetails& details) OVERRIDE;
123 NotificationRegistrar registrar_;
125 NotificationSource source_;
126 NotificationDetails details_;
127 scoped_refptr<MessageLoopRunner> message_loop_runner_;
129 DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver);
132 RedirectNotificationObserver::RedirectNotificationObserver(
133 int notification_type,
134 const NotificationSource& source)
137 source_(NotificationService::AllSources()) {
138 registrar_.Add(this, notification_type, source);
141 RedirectNotificationObserver::~RedirectNotificationObserver() {}
143 void RedirectNotificationObserver::Wait() {
144 if (seen_ && seen_twice_)
148 message_loop_runner_ = new MessageLoopRunner;
149 message_loop_runner_->Run();
153 void RedirectNotificationObserver::Observe(
155 const NotificationSource& source,
156 const NotificationDetails& details) {
164 message_loop_runner_->Quit();
168 // Tracks a single request for a specified URL, and allows waiting until the
169 // request is destroyed, and then inspecting whether it completed successfully.
170 class TrackingResourceDispatcherHostDelegate
171 : public ShellResourceDispatcherHostDelegate {
173 TrackingResourceDispatcherHostDelegate() : throttle_created_(false) {
176 virtual void RequestBeginning(
177 net::URLRequest* request,
178 ResourceContext* resource_context,
179 appcache::AppCacheService* appcache_service,
180 ResourceType::Type resource_type,
183 ScopedVector<ResourceThrottle>* throttles) OVERRIDE {
184 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
185 ShellResourceDispatcherHostDelegate::RequestBeginning(
186 request, resource_context, appcache_service, resource_type, child_id,
187 route_id, throttles);
188 // Expect only a single request for the tracked url.
189 ASSERT_FALSE(throttle_created_);
190 // If this is a request for the tracked URL, add a throttle to track it.
191 if (request->url() == tracked_url_)
192 throttles->push_back(new TrackingThrottle(request, this));
195 // Starts tracking a URL. The request for previously tracked URL, if any,
196 // must have been made and deleted before calling this function.
197 void SetTrackedURL(const GURL& tracked_url) {
198 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
199 // Should not currently be tracking any URL.
200 ASSERT_FALSE(run_loop_);
202 // Create a RunLoop that will be stopped once the request for the tracked
203 // URL has been destroyed, to allow tracking the URL while also waiting for
205 run_loop_.reset(new base::RunLoop());
207 BrowserThread::PostTask(
208 BrowserThread::IO, FROM_HERE,
210 &TrackingResourceDispatcherHostDelegate::SetTrackedURLOnIOThread,
211 base::Unretained(this),
215 // Waits until the tracked URL has been requests, and the request for it has
217 bool WaitForTrackedURLAndGetCompleted() {
218 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221 return tracked_request_completed_;
225 // ResourceThrottle attached to request for the tracked URL. On destruction,
226 // passes the final URLRequestStatus back to the delegate.
227 class TrackingThrottle : public ResourceThrottle {
229 TrackingThrottle(net::URLRequest* request,
230 TrackingResourceDispatcherHostDelegate* tracker)
231 : request_(request), tracker_(tracker) {
234 virtual ~TrackingThrottle() {
235 // If the request is deleted without being cancelled, its status will
236 // indicate it succeeded, so have to check if the request is still pending
238 tracker_->OnTrackedRequestDestroyed(
239 !request_->is_pending() && request_->status().is_success());
242 // ResourceThrottle implementation:
243 virtual const char* GetNameForLogging() const OVERRIDE {
244 return "TrackingThrottle";
248 net::URLRequest* request_;
249 TrackingResourceDispatcherHostDelegate* tracker_;
251 DISALLOW_COPY_AND_ASSIGN(TrackingThrottle);
254 void SetTrackedURLOnIOThread(const GURL& tracked_url) {
255 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
256 throttle_created_ = false;
257 tracked_url_ = tracked_url;
260 void OnTrackedRequestDestroyed(bool completed) {
261 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
262 tracked_request_completed_ = completed;
263 tracked_url_ = GURL();
265 BrowserThread::PostTask(
266 BrowserThread::UI, FROM_HERE, run_loop_->QuitClosure());
269 // These live on the IO thread.
271 bool throttle_created_;
273 // This is created and destroyed on the UI thread, but stopped on the IO
275 scoped_ptr<base::RunLoop> run_loop_;
277 // Set on the IO thread while |run_loop_| is non-NULL, read on the UI thread
278 // after deleting run_loop_.
279 bool tracked_request_completed_;
281 DISALLOW_COPY_AND_ASSIGN(TrackingResourceDispatcherHostDelegate);
284 // WebContentsDelegate that fails to open a URL when there's a request that
285 // needs to be transferred between renderers.
286 class NoTransferRequestDelegate : public WebContentsDelegate {
288 NoTransferRequestDelegate() {}
290 virtual WebContents* OpenURLFromTab(WebContents* source,
291 const OpenURLParams& params) OVERRIDE {
293 (params.transferred_global_request_id != GlobalRequestID());
296 NavigationController::LoadURLParams load_url_params(params.url);
297 load_url_params.referrer = params.referrer;
298 load_url_params.frame_tree_node_id = params.frame_tree_node_id;
299 load_url_params.transition_type = params.transition;
300 load_url_params.extra_headers = params.extra_headers;
301 load_url_params.should_replace_current_entry =
302 params.should_replace_current_entry;
303 load_url_params.is_renderer_initiated = true;
304 source->GetController().LoadURLWithParams(load_url_params);
309 DISALLOW_COPY_AND_ASSIGN(NoTransferRequestDelegate);
312 class SitePerProcessBrowserTest : public ContentBrowserTest {
314 SitePerProcessBrowserTest() : old_delegate_(NULL) {
317 // ContentBrowserTest implementation:
318 virtual void SetUpOnMainThread() OVERRIDE {
319 BrowserThread::PostTask(
320 BrowserThread::IO, FROM_HERE,
322 &SitePerProcessBrowserTest::InjectResourceDisptcherHostDelegate,
323 base::Unretained(this)));
326 virtual void TearDownOnMainThread() OVERRIDE {
327 BrowserThread::PostTask(
328 BrowserThread::IO, FROM_HERE,
330 &SitePerProcessBrowserTest::RestoreResourceDisptcherHostDelegate,
331 base::Unretained(this)));
335 // Start at a data URL so each extra navigation creates a navigation entry.
336 // (The first navigation will silently be classified as AUTO_SUBFRAME.)
337 // TODO(creis): This won't be necessary when we can wait for LOAD_STOP.
338 void StartFrameAtDataURL() {
339 std::string data_url_script =
340 "var iframes = document.getElementById('test');iframes.src="
341 "'data:text/html,dataurl';";
342 ASSERT_TRUE(ExecuteScript(shell()->web_contents(), data_url_script));
345 bool NavigateIframeToURL(Shell* window,
347 std::string iframe_id) {
348 // TODO(creis): This should wait for LOAD_STOP, but cross-site subframe
349 // navigations generate extra DidStartLoading and DidStopLoading messages.
350 // Until we replace swappedout:// with frame proxies, we need to listen for
351 // something else. For now, we trigger NEW_SUBFRAME navigations and listen
353 std::string script = base::StringPrintf(
355 "var iframes = document.getElementById('%s');iframes.src='%s';"
357 iframe_id.c_str(), url.spec().c_str());
358 WindowedNotificationObserver load_observer(
359 NOTIFICATION_NAV_ENTRY_COMMITTED,
360 Source<NavigationController>(
361 &window->web_contents()->GetController()));
362 bool result = ExecuteScript(window->web_contents(), script);
363 load_observer.Wait();
367 void NavigateToURLContentInitiated(Shell* window,
369 bool should_replace_current_entry,
370 bool should_wait_for_navigation) {
372 if (should_replace_current_entry)
373 script = base::StringPrintf("location.replace('%s')", url.spec().c_str());
375 script = base::StringPrintf("location.href = '%s'", url.spec().c_str());
376 TestNavigationObserver load_observer(shell()->web_contents(), 1);
377 bool result = ExecuteScript(window->web_contents(), script);
379 if (should_wait_for_navigation)
380 load_observer.Wait();
383 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
384 command_line->AppendSwitch(switches::kSitePerProcess);
386 // TODO(creis): Remove this when GTK is no longer a supported platform.
387 command_line->AppendSwitch(switches::kForceCompositingMode);
390 void InjectResourceDisptcherHostDelegate() {
391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
392 old_delegate_ = ResourceDispatcherHostImpl::Get()->delegate();
393 ResourceDispatcherHostImpl::Get()->SetDelegate(&tracking_delegate_);
396 void RestoreResourceDisptcherHostDelegate() {
397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
398 ResourceDispatcherHostImpl::Get()->SetDelegate(old_delegate_);
399 old_delegate_ = NULL;
402 TrackingResourceDispatcherHostDelegate& tracking_delegate() {
403 return tracking_delegate_;
407 TrackingResourceDispatcherHostDelegate tracking_delegate_;
408 ResourceDispatcherHostDelegate* old_delegate_;
411 // Ensure that we can complete a cross-process subframe navigation.
412 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteIframe) {
413 host_resolver()->AddRule("*", "127.0.0.1");
414 ASSERT_TRUE(test_server()->Start());
415 GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
416 NavigateToURL(shell(), main_url);
418 StartFrameAtDataURL();
420 SitePerProcessWebContentsObserver observer(shell()->web_contents());
422 // Load same-site page into iframe.
423 GURL http_url(test_server()->GetURL("files/title1.html"));
424 EXPECT_TRUE(NavigateIframeToURL(shell(), http_url, "test"));
425 EXPECT_EQ(http_url, observer.navigation_url());
426 EXPECT_TRUE(observer.navigation_succeeded());
428 // These must stay in scope with replace_host.
429 GURL::Replacements replace_host;
430 std::string foo_com("foo.com");
432 // Load cross-site page into iframe.
433 GURL cross_site_url(test_server()->GetURL("files/title2.html"));
434 replace_host.SetHostStr(foo_com);
435 cross_site_url = cross_site_url.ReplaceComponents(replace_host);
436 EXPECT_TRUE(NavigateIframeToURL(shell(), cross_site_url, "test"));
437 EXPECT_EQ(cross_site_url, observer.navigation_url());
438 EXPECT_TRUE(observer.navigation_succeeded());
440 // Ensure that we have created a new process for the subframe.
441 FrameTreeNode* root =
442 static_cast<WebContentsImpl*>(shell()->web_contents())->
443 GetFrameTree()->root();
444 ASSERT_EQ(1U, root->child_count());
445 FrameTreeNode* child = root->child_at(0);
446 EXPECT_NE(shell()->web_contents()->GetRenderViewHost(),
447 child->current_frame_host()->render_view_host());
448 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
449 child->current_frame_host()->render_view_host()->GetSiteInstance());
450 EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(),
451 child->current_frame_host()->GetProcess());
454 // Crash a subframe and ensures its children are cleared from the FrameTree.
455 // See http://crbug.com/338508.
456 // TODO(creis): Enable this on Android when we can kill the process there.
457 #if defined(OS_ANDROID)
458 #define MAYBE_CrashSubframe DISABLED_CrashSubframe
460 #define MAYBE_CrashSubframe CrashSubframe
462 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, MAYBE_CrashSubframe) {
463 host_resolver()->AddRule("*", "127.0.0.1");
464 ASSERT_TRUE(test_server()->Start());
465 GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
466 NavigateToURL(shell(), main_url);
468 StartFrameAtDataURL();
470 // These must stay in scope with replace_host.
471 GURL::Replacements replace_host;
472 std::string foo_com("foo.com");
474 // Load cross-site page into iframe.
475 GURL cross_site_url(test_server()->GetURL("files/title2.html"));
476 replace_host.SetHostStr(foo_com);
477 cross_site_url = cross_site_url.ReplaceComponents(replace_host);
478 EXPECT_TRUE(NavigateIframeToURL(shell(), cross_site_url, "test"));
480 // Check the subframe process.
481 FrameTreeNode* root =
482 static_cast<WebContentsImpl*>(shell()->web_contents())->
483 GetFrameTree()->root();
484 ASSERT_EQ(1U, root->child_count());
485 FrameTreeNode* child = root->child_at(0);
486 EXPECT_NE(FrameTreeNode::kInvalidFrameId, root->frame_id());
487 EXPECT_NE(FrameTreeNode::kInvalidFrameId, root->child_at(0)->frame_id());
489 // Crash the subframe process.
490 RenderProcessHost* root_process = root->current_frame_host()->GetProcess();
491 RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
493 RenderProcessHostWatcher crash_observer(
495 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
496 base::KillProcess(child_process->GetHandle(), 0, false);
497 crash_observer.Wait();
500 // Ensure that the child frame still exists but has been cleared.
501 EXPECT_EQ(1U, root->child_count());
502 EXPECT_EQ(FrameTreeNode::kInvalidFrameId, root->child_at(0)->frame_id());
504 // Now crash the top-level page to clear the child frame.
506 RenderProcessHostWatcher crash_observer(
508 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
509 base::KillProcess(root_process->GetHandle(), 0, false);
510 crash_observer.Wait();
512 EXPECT_EQ(0U, root->child_count());
513 EXPECT_EQ(FrameTreeNode::kInvalidFrameId, root->frame_id());
516 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
517 // security checks are back in place.
518 // TODO(creis): Replace SpawnedTestServer with host_resolver to get test to run
519 // on Android (http://crbug.com/187570).
520 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
521 DISABLED_CrossSiteIframeRedirectOnce) {
522 ASSERT_TRUE(test_server()->Start());
523 net::SpawnedTestServer https_server(
524 net::SpawnedTestServer::TYPE_HTTPS,
525 net::SpawnedTestServer::kLocalhost,
526 base::FilePath(FILE_PATH_LITERAL("content/test/data")));
527 ASSERT_TRUE(https_server.Start());
529 GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
530 GURL http_url(test_server()->GetURL("files/title1.html"));
531 GURL https_url(https_server.GetURL("files/title1.html"));
533 NavigateToURL(shell(), main_url);
535 SitePerProcessWebContentsObserver observer(shell()->web_contents());
537 // Load cross-site client-redirect page into Iframe.
538 // Should be blocked.
539 GURL client_redirect_https_url(https_server.GetURL(
540 "client-redirect?files/title1.html"));
541 EXPECT_TRUE(NavigateIframeToURL(shell(),
542 client_redirect_https_url, "test"));
543 // DidFailProvisionalLoad when navigating to client_redirect_https_url.
544 EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
545 EXPECT_FALSE(observer.navigation_succeeded());
549 // Load cross-site server-redirect page into Iframe,
550 // which redirects to same-site page.
551 GURL server_redirect_http_url(https_server.GetURL(
552 "server-redirect?" + http_url.spec()));
553 EXPECT_TRUE(NavigateIframeToURL(shell(),
554 server_redirect_http_url, "test"));
555 EXPECT_EQ(observer.navigation_url(), http_url);
556 EXPECT_TRUE(observer.navigation_succeeded());
560 // Load cross-site server-redirect page into Iframe,
561 // which redirects to cross-site page.
562 GURL server_redirect_http_url(https_server.GetURL(
563 "server-redirect?files/title1.html"));
564 EXPECT_TRUE(NavigateIframeToURL(shell(),
565 server_redirect_http_url, "test"));
566 // DidFailProvisionalLoad when navigating to https_url.
567 EXPECT_EQ(observer.navigation_url(), https_url);
568 EXPECT_FALSE(observer.navigation_succeeded());
572 // Load same-site server-redirect page into Iframe,
573 // which redirects to cross-site page.
574 GURL server_redirect_http_url(test_server()->GetURL(
575 "server-redirect?" + https_url.spec()));
576 EXPECT_TRUE(NavigateIframeToURL(shell(),
577 server_redirect_http_url, "test"));
579 EXPECT_EQ(observer.navigation_url(), https_url);
580 EXPECT_FALSE(observer.navigation_succeeded());
584 // Load same-site client-redirect page into Iframe,
585 // which redirects to cross-site page.
586 GURL client_redirect_http_url(test_server()->GetURL(
587 "client-redirect?" + https_url.spec()));
589 RedirectNotificationObserver load_observer2(
590 NOTIFICATION_LOAD_STOP,
591 Source<NavigationController>(
592 &shell()->web_contents()->GetController()));
594 EXPECT_TRUE(NavigateIframeToURL(shell(),
595 client_redirect_http_url, "test"));
597 // Same-site Client-Redirect Page should be loaded successfully.
598 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
599 EXPECT_TRUE(observer.navigation_succeeded());
601 // Redirecting to Cross-site Page should be blocked.
602 load_observer2.Wait();
603 EXPECT_EQ(observer.navigation_url(), https_url);
604 EXPECT_FALSE(observer.navigation_succeeded());
608 // Load same-site server-redirect page into Iframe,
609 // which redirects to same-site page.
610 GURL server_redirect_http_url(test_server()->GetURL(
611 "server-redirect?files/title1.html"));
612 EXPECT_TRUE(NavigateIframeToURL(shell(),
613 server_redirect_http_url, "test"));
614 EXPECT_EQ(observer.navigation_url(), http_url);
615 EXPECT_TRUE(observer.navigation_succeeded());
619 // Load same-site client-redirect page into Iframe,
620 // which redirects to same-site page.
621 GURL client_redirect_http_url(test_server()->GetURL(
622 "client-redirect?" + http_url.spec()));
623 RedirectNotificationObserver load_observer2(
624 NOTIFICATION_LOAD_STOP,
625 Source<NavigationController>(
626 &shell()->web_contents()->GetController()));
628 EXPECT_TRUE(NavigateIframeToURL(shell(),
629 client_redirect_http_url, "test"));
631 // Same-site Client-Redirect Page should be loaded successfully.
632 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
633 EXPECT_TRUE(observer.navigation_succeeded());
635 // Redirecting to Same-site Page should be loaded successfully.
636 load_observer2.Wait();
637 EXPECT_EQ(observer.navigation_url(), http_url);
638 EXPECT_TRUE(observer.navigation_succeeded());
642 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
643 // security checks are back in place.
644 // TODO(creis): Replace SpawnedTestServer with host_resolver to get test to run
645 // on Android (http://crbug.com/187570).
646 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
647 DISABLED_CrossSiteIframeRedirectTwice) {
648 ASSERT_TRUE(test_server()->Start());
649 net::SpawnedTestServer https_server(
650 net::SpawnedTestServer::TYPE_HTTPS,
651 net::SpawnedTestServer::kLocalhost,
652 base::FilePath(FILE_PATH_LITERAL("content/test/data")));
653 ASSERT_TRUE(https_server.Start());
655 GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
656 GURL http_url(test_server()->GetURL("files/title1.html"));
657 GURL https_url(https_server.GetURL("files/title1.html"));
659 NavigateToURL(shell(), main_url);
661 SitePerProcessWebContentsObserver observer(shell()->web_contents());
663 // Load client-redirect page pointing to a cross-site client-redirect page,
664 // which eventually redirects back to same-site page.
665 GURL client_redirect_https_url(https_server.GetURL(
666 "client-redirect?" + http_url.spec()));
667 GURL client_redirect_http_url(test_server()->GetURL(
668 "client-redirect?" + client_redirect_https_url.spec()));
670 // We should wait until second client redirect get cancelled.
671 RedirectNotificationObserver load_observer2(
672 NOTIFICATION_LOAD_STOP,
673 Source<NavigationController>(
674 &shell()->web_contents()->GetController()));
676 EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test"));
678 // DidFailProvisionalLoad when navigating to client_redirect_https_url.
679 load_observer2.Wait();
680 EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
681 EXPECT_FALSE(observer.navigation_succeeded());
685 // Load server-redirect page pointing to a cross-site server-redirect page,
686 // which eventually redirect back to same-site page.
687 GURL server_redirect_https_url(https_server.GetURL(
688 "server-redirect?" + http_url.spec()));
689 GURL server_redirect_http_url(test_server()->GetURL(
690 "server-redirect?" + server_redirect_https_url.spec()));
691 EXPECT_TRUE(NavigateIframeToURL(shell(),
692 server_redirect_http_url, "test"));
693 EXPECT_EQ(observer.navigation_url(), http_url);
694 EXPECT_TRUE(observer.navigation_succeeded());
698 // Load server-redirect page pointing to a cross-site server-redirect page,
699 // which eventually redirects back to cross-site page.
700 GURL server_redirect_https_url(https_server.GetURL(
701 "server-redirect?" + https_url.spec()));
702 GURL server_redirect_http_url(test_server()->GetURL(
703 "server-redirect?" + server_redirect_https_url.spec()));
704 EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
706 // DidFailProvisionalLoad when navigating to https_url.
707 EXPECT_EQ(observer.navigation_url(), https_url);
708 EXPECT_FALSE(observer.navigation_succeeded());
712 // Load server-redirect page pointing to a cross-site client-redirect page,
713 // which eventually redirects back to same-site page.
714 GURL client_redirect_http_url(https_server.GetURL(
715 "client-redirect?" + http_url.spec()));
716 GURL server_redirect_http_url(test_server()->GetURL(
717 "server-redirect?" + client_redirect_http_url.spec()));
718 EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
720 // DidFailProvisionalLoad when navigating to client_redirect_http_url.
721 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
722 EXPECT_FALSE(observer.navigation_succeeded());
726 // Tests that the |should_replace_current_entry| flag persists correctly across
727 // request transfers that began with a cross-process navigation.
728 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
729 ReplaceEntryCrossProcessThenTransfer) {
730 const NavigationController& controller =
731 shell()->web_contents()->GetController();
732 host_resolver()->AddRule("*", "127.0.0.1");
733 ASSERT_TRUE(test_server()->Start());
735 // These must all stay in scope with replace_host.
736 GURL::Replacements replace_host;
737 std::string a_com("A.com");
738 std::string b_com("B.com");
740 // Navigate to a starting URL, so there is a history entry to replace.
741 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
742 NavigateToURL(shell(), url1);
744 // Force all future navigations to transfer. Note that this includes same-site
745 // navigiations which may cause double process swaps (via OpenURL and then via
746 // transfer). This test intentionally exercises that case.
747 ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
749 // Navigate to a page on A.com with entry replacement. This navigation is
750 // cross-site, so the renderer will send it to the browser via OpenURL to give
751 // to a new process. It will then be transferred into yet another process due
752 // to the call above.
753 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
754 replace_host.SetHostStr(a_com);
755 url2 = url2.ReplaceComponents(replace_host);
756 // Used to make sure the request for url2 succeeds, and there was only one of
758 tracking_delegate().SetTrackedURL(url2);
759 NavigateToURLContentInitiated(shell(), url2, true, true);
761 // There should be one history entry. url2 should have replaced url1.
762 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
763 EXPECT_EQ(1, controller.GetEntryCount());
764 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
765 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
766 // Make sure the request succeeded.
767 EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
769 // Now navigate as before to a page on B.com, but normally (without
770 // replacement). This will still perform a double process-swap as above, via
771 // OpenURL and then transfer.
772 GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
773 replace_host.SetHostStr(b_com);
774 url3 = url3.ReplaceComponents(replace_host);
775 // Used to make sure the request for url3 succeeds, and there was only one of
777 tracking_delegate().SetTrackedURL(url3);
778 NavigateToURLContentInitiated(shell(), url3, false, true);
780 // There should be two history entries. url2 should have replaced url1. url2
781 // should not have replaced url3.
782 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
783 EXPECT_EQ(2, controller.GetEntryCount());
784 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
785 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
786 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
788 // Make sure the request succeeded.
789 EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
792 // Tests that the |should_replace_current_entry| flag persists correctly across
793 // request transfers that began with a content-initiated in-process
794 // navigation. This test is the same as the test above, except transfering from
796 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
797 ReplaceEntryInProcessThenTranfers) {
798 const NavigationController& controller =
799 shell()->web_contents()->GetController();
800 ASSERT_TRUE(test_server()->Start());
802 // Navigate to a starting URL, so there is a history entry to replace.
803 GURL url = test_server()->GetURL("files/site_isolation/blank.html?1");
804 NavigateToURL(shell(), url);
806 // Force all future navigations to transfer. Note that this includes same-site
807 // navigiations which may cause double process swaps (via OpenURL and then via
808 // transfer). All navigations in this test are same-site, so it only swaps
809 // processes via request transfer.
810 ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
812 // Navigate in-process with entry replacement. It will then be transferred
813 // into a new one due to the call above.
814 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
815 NavigateToURLContentInitiated(shell(), url2, true, true);
817 // There should be one history entry. url2 should have replaced url1.
818 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
819 EXPECT_EQ(1, controller.GetEntryCount());
820 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
821 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
823 // Now navigate as before, but without replacement.
824 GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
825 NavigateToURLContentInitiated(shell(), url3, false, true);
827 // There should be two history entries. url2 should have replaced url1. url2
828 // should not have replaced url3.
829 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
830 EXPECT_EQ(2, controller.GetEntryCount());
831 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
832 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
833 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
836 // Tests that the |should_replace_current_entry| flag persists correctly across
837 // request transfers that cross processes twice from renderer policy.
838 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
839 ReplaceEntryCrossProcessTwice) {
840 const NavigationController& controller =
841 shell()->web_contents()->GetController();
842 host_resolver()->AddRule("*", "127.0.0.1");
843 ASSERT_TRUE(test_server()->Start());
845 // These must all stay in scope with replace_host.
846 GURL::Replacements replace_host;
847 std::string a_com("A.com");
848 std::string b_com("B.com");
850 // Navigate to a starting URL, so there is a history entry to replace.
851 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
852 NavigateToURL(shell(), url1);
854 // Navigate to a page on A.com which redirects to B.com with entry
855 // replacement. This will switch processes via OpenURL twice. First to A.com,
856 // and second in response to the server redirect to B.com. The second swap is
857 // also renderer-initiated via OpenURL because decidePolicyForNavigation is
858 // currently applied on redirects.
859 GURL url2b = test_server()->GetURL("files/site_isolation/blank.html?2");
860 replace_host.SetHostStr(b_com);
861 url2b = url2b.ReplaceComponents(replace_host);
862 GURL url2a = test_server()->GetURL(
863 "server-redirect?" + net::EscapeQueryParamValue(url2b.spec(), false));
864 replace_host.SetHostStr(a_com);
865 url2a = url2a.ReplaceComponents(replace_host);
866 NavigateToURLContentInitiated(shell(), url2a, true, true);
868 // There should be one history entry. url2b should have replaced url1.
869 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
870 EXPECT_EQ(1, controller.GetEntryCount());
871 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
872 EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
874 // Now repeat without replacement.
875 GURL url3b = test_server()->GetURL("files/site_isolation/blank.html?3");
876 replace_host.SetHostStr(b_com);
877 url3b = url3b.ReplaceComponents(replace_host);
878 GURL url3a = test_server()->GetURL(
879 "server-redirect?" + net::EscapeQueryParamValue(url3b.spec(), false));
880 replace_host.SetHostStr(a_com);
881 url3a = url3a.ReplaceComponents(replace_host);
882 NavigateToURLContentInitiated(shell(), url3a, false, true);
884 // There should be two history entries. url2b should have replaced url1. url2b
885 // should not have replaced url3b.
886 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
887 EXPECT_EQ(2, controller.GetEntryCount());
888 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
889 EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
890 EXPECT_EQ(url3b, controller.GetEntryAtIndex(1)->GetURL());
893 // Tests that the request is destroyed when a cross process navigation is
895 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NoLeakOnCrossSiteCancel) {
896 const NavigationController& controller =
897 shell()->web_contents()->GetController();
898 host_resolver()->AddRule("*", "127.0.0.1");
899 ASSERT_TRUE(test_server()->Start());
901 // These must all stay in scope with replace_host.
902 GURL::Replacements replace_host;
903 std::string a_com("A.com");
904 std::string b_com("B.com");
906 // Navigate to a starting URL, so there is a history entry to replace.
907 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
908 NavigateToURL(shell(), url1);
910 // Force all future navigations to transfer.
911 ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
913 NoTransferRequestDelegate no_transfer_request_delegate;
914 WebContentsDelegate* old_delegate = shell()->web_contents()->GetDelegate();
915 shell()->web_contents()->SetDelegate(&no_transfer_request_delegate);
917 // Navigate to a page on A.com with entry replacement. This navigation is
918 // cross-site, so the renderer will send it to the browser via OpenURL to give
919 // to a new process. It will then be transferred into yet another process due
920 // to the call above.
921 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
922 replace_host.SetHostStr(a_com);
923 url2 = url2.ReplaceComponents(replace_host);
924 // Used to make sure the second request is cancelled, and there is only one
926 tracking_delegate().SetTrackedURL(url2);
928 // Don't wait for the navigation to complete, since that never happens in
930 NavigateToURLContentInitiated(shell(), url2, false, false);
932 // There should be one history entry, with url1.
933 EXPECT_EQ(1, controller.GetEntryCount());
934 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
935 EXPECT_EQ(url1, controller.GetEntryAtIndex(0)->GetURL());
937 // Make sure the request for url2 did not complete.
938 EXPECT_FALSE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
940 shell()->web_contents()->SetDelegate(old_delegate);
943 } // namespace content