Upstream version 5.34.104.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/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"
33
34 namespace content {
35
36 class SitePerProcessWebContentsObserver: public WebContentsObserver {
37  public:
38   explicit SitePerProcessWebContentsObserver(WebContents* web_contents)
39       : WebContentsObserver(web_contents),
40         navigation_succeeded_(false) {}
41   virtual ~SitePerProcessWebContentsObserver() {}
42
43   virtual void DidStartProvisionalLoadForFrame(
44       int64 frame_id,
45       int64 parent_frame_id,
46       bool is_main_frame,
47       const GURL& validated_url,
48       bool is_error_page,
49       bool is_iframe_srcdoc,
50       RenderViewHost* render_view_host) OVERRIDE {
51     navigation_succeeded_ = false;
52   }
53
54   virtual void DidFailProvisionalLoad(
55       int64 frame_id,
56       const base::string16& frame_unique_name,
57       bool is_main_frame,
58       const GURL& validated_url,
59       int error_code,
60       const base::string16& error_description,
61       RenderViewHost* render_view_host) OVERRIDE {
62     navigation_url_ = validated_url;
63     navigation_succeeded_ = false;
64   }
65
66   virtual void DidCommitProvisionalLoadForFrame(
67       int64 frame_id,
68       const base::string16& frame_unique_name,
69       bool is_main_frame,
70       const GURL& url,
71       PageTransition transition_type,
72       RenderViewHost* render_view_host) OVERRIDE{
73     navigation_url_ = url;
74     navigation_succeeded_ = true;
75   }
76
77   const GURL& navigation_url() const {
78     return navigation_url_;
79   }
80
81   int navigation_succeeded() const { return navigation_succeeded_; }
82
83  private:
84   GURL navigation_url_;
85   bool navigation_succeeded_;
86
87   DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver);
88 };
89
90 class RedirectNotificationObserver : public NotificationObserver {
91  public:
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();
98
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.
102   void Wait();
103
104   // Returns NotificationService::AllSources() if we haven't observed a
105   // notification yet.
106   const NotificationSource& source() const {
107     return source_;
108   }
109
110   const NotificationDetails& details() const {
111     return details_;
112   }
113
114   // NotificationObserver:
115   virtual void Observe(int type,
116                        const NotificationSource& source,
117                        const NotificationDetails& details) OVERRIDE;
118
119  private:
120   bool seen_;
121   bool seen_twice_;
122   bool running_;
123   NotificationRegistrar registrar_;
124
125   NotificationSource source_;
126   NotificationDetails details_;
127   scoped_refptr<MessageLoopRunner> message_loop_runner_;
128
129   DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver);
130 };
131
132 RedirectNotificationObserver::RedirectNotificationObserver(
133     int notification_type,
134     const NotificationSource& source)
135     : seen_(false),
136       running_(false),
137       source_(NotificationService::AllSources()) {
138   registrar_.Add(this, notification_type, source);
139 }
140
141 RedirectNotificationObserver::~RedirectNotificationObserver() {}
142
143 void RedirectNotificationObserver::Wait() {
144   if (seen_ && seen_twice_)
145     return;
146
147   running_ = true;
148   message_loop_runner_ = new MessageLoopRunner;
149   message_loop_runner_->Run();
150   EXPECT_TRUE(seen_);
151 }
152
153 void RedirectNotificationObserver::Observe(
154     int type,
155     const NotificationSource& source,
156     const NotificationDetails& details) {
157   source_ = source;
158   details_ = details;
159   seen_twice_ = seen_;
160   seen_ = true;
161   if (!running_)
162     return;
163
164   message_loop_runner_->Quit();
165   running_ = false;
166 }
167
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 {
172  public:
173   TrackingResourceDispatcherHostDelegate() : throttle_created_(false) {
174   }
175
176   virtual void RequestBeginning(
177       net::URLRequest* request,
178       ResourceContext* resource_context,
179       appcache::AppCacheService* appcache_service,
180       ResourceType::Type resource_type,
181       int child_id,
182       int route_id,
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));
193   }
194
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_);
201
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
204     // other events.
205     run_loop_.reset(new base::RunLoop());
206
207     BrowserThread::PostTask(
208         BrowserThread::IO, FROM_HERE,
209         base::Bind(
210             &TrackingResourceDispatcherHostDelegate::SetTrackedURLOnIOThread,
211             base::Unretained(this),
212             tracked_url));
213   }
214
215   // Waits until the tracked URL has been requests, and the request for it has
216   // been destroyed.
217   bool WaitForTrackedURLAndGetCompleted() {
218     CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
219     run_loop_->Run();
220     run_loop_.reset();
221     return tracked_request_completed_;
222   }
223
224  private:
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 {
228    public:
229     TrackingThrottle(net::URLRequest* request,
230                      TrackingResourceDispatcherHostDelegate* tracker)
231         : request_(request), tracker_(tracker) {
232     }
233
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
237       // as well.
238       tracker_->OnTrackedRequestDestroyed(
239           !request_->is_pending() && request_->status().is_success());
240     }
241
242     // ResourceThrottle implementation:
243     virtual const char* GetNameForLogging() const OVERRIDE {
244       return "TrackingThrottle";
245     }
246
247    private:
248     net::URLRequest* request_;
249     TrackingResourceDispatcherHostDelegate* tracker_;
250
251     DISALLOW_COPY_AND_ASSIGN(TrackingThrottle);
252   };
253
254   void SetTrackedURLOnIOThread(const GURL& tracked_url) {
255     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
256     throttle_created_ = false;
257     tracked_url_ = tracked_url;
258   }
259
260   void OnTrackedRequestDestroyed(bool completed) {
261     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
262     tracked_request_completed_ = completed;
263     tracked_url_ = GURL();
264
265     BrowserThread::PostTask(
266         BrowserThread::UI, FROM_HERE, run_loop_->QuitClosure());
267   }
268
269   // These live on the IO thread.
270   GURL tracked_url_;
271   bool throttle_created_;
272
273   // This is created and destroyed on the UI thread, but stopped on the IO
274   // thread.
275   scoped_ptr<base::RunLoop> run_loop_;
276
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_;
280
281   DISALLOW_COPY_AND_ASSIGN(TrackingResourceDispatcherHostDelegate);
282 };
283
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 {
287  public:
288   NoTransferRequestDelegate() {}
289
290   virtual WebContents* OpenURLFromTab(WebContents* source,
291                                       const OpenURLParams& params) OVERRIDE {
292     bool is_transfer =
293         (params.transferred_global_request_id != GlobalRequestID());
294     if (is_transfer)
295       return NULL;
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);
305     return source;
306   }
307
308  private:
309   DISALLOW_COPY_AND_ASSIGN(NoTransferRequestDelegate);
310 };
311
312 class SitePerProcessBrowserTest : public ContentBrowserTest {
313  public:
314   SitePerProcessBrowserTest() : old_delegate_(NULL) {
315   }
316
317   // ContentBrowserTest implementation:
318   virtual void SetUpOnMainThread() OVERRIDE {
319     BrowserThread::PostTask(
320         BrowserThread::IO, FROM_HERE,
321         base::Bind(
322             &SitePerProcessBrowserTest::InjectResourceDisptcherHostDelegate,
323             base::Unretained(this)));
324   }
325
326   virtual void TearDownOnMainThread() OVERRIDE {
327     BrowserThread::PostTask(
328         BrowserThread::IO, FROM_HERE,
329         base::Bind(
330             &SitePerProcessBrowserTest::RestoreResourceDisptcherHostDelegate,
331             base::Unretained(this)));
332   }
333
334  protected:
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));
343   }
344
345   bool NavigateIframeToURL(Shell* window,
346                            const GURL& url,
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
352     // for commit.
353     std::string script = base::StringPrintf(
354         "setTimeout(\""
355         "var iframes = document.getElementById('%s');iframes.src='%s';"
356         "\",0)",
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();
364     return result;
365   }
366
367   void NavigateToURLContentInitiated(Shell* window,
368                                      const GURL& url,
369                                      bool should_replace_current_entry,
370                                      bool should_wait_for_navigation) {
371     std::string script;
372     if (should_replace_current_entry)
373       script = base::StringPrintf("location.replace('%s')", url.spec().c_str());
374     else
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);
378     EXPECT_TRUE(result);
379     if (should_wait_for_navigation)
380       load_observer.Wait();
381   }
382
383   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
384     command_line->AppendSwitch(switches::kSitePerProcess);
385
386     // TODO(creis): Remove this when GTK is no longer a supported platform.
387     command_line->AppendSwitch(switches::kForceCompositingMode);
388   }
389
390   void InjectResourceDisptcherHostDelegate() {
391     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
392     old_delegate_ = ResourceDispatcherHostImpl::Get()->delegate();
393     ResourceDispatcherHostImpl::Get()->SetDelegate(&tracking_delegate_);
394   }
395
396   void RestoreResourceDisptcherHostDelegate() {
397     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
398     ResourceDispatcherHostImpl::Get()->SetDelegate(old_delegate_);
399     old_delegate_ = NULL;
400   }
401
402   TrackingResourceDispatcherHostDelegate& tracking_delegate() {
403     return tracking_delegate_;
404   }
405
406  private:
407   TrackingResourceDispatcherHostDelegate tracking_delegate_;
408   ResourceDispatcherHostDelegate* old_delegate_;
409 };
410
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);
417
418   StartFrameAtDataURL();
419
420   SitePerProcessWebContentsObserver observer(shell()->web_contents());
421
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());
427
428   // These must stay in scope with replace_host.
429   GURL::Replacements replace_host;
430   std::string foo_com("foo.com");
431
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());
439
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());
452 }
453
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
459 #else
460 #define MAYBE_CrashSubframe CrashSubframe
461 #endif
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);
467
468   StartFrameAtDataURL();
469
470   // These must stay in scope with replace_host.
471   GURL::Replacements replace_host;
472   std::string foo_com("foo.com");
473
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"));
479
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());
488
489   // Crash the subframe process.
490   RenderProcessHost* root_process = root->current_frame_host()->GetProcess();
491   RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
492   {
493     RenderProcessHostWatcher crash_observer(
494         child_process,
495         RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
496     base::KillProcess(child_process->GetHandle(), 0, false);
497     crash_observer.Wait();
498   }
499
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());
503
504   // Now crash the top-level page to clear the child frame.
505   {
506     RenderProcessHostWatcher crash_observer(
507         root_process,
508         RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
509     base::KillProcess(root_process->GetHandle(), 0, false);
510     crash_observer.Wait();
511   }
512   EXPECT_EQ(0U, root->child_count());
513   EXPECT_EQ(FrameTreeNode::kInvalidFrameId, root->frame_id());
514 }
515
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());
528
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"));
532
533   NavigateToURL(shell(), main_url);
534
535   SitePerProcessWebContentsObserver observer(shell()->web_contents());
536   {
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());
546   }
547
548   {
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());
557   }
558
559   {
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());
569   }
570
571   {
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"));
578
579     EXPECT_EQ(observer.navigation_url(), https_url);
580     EXPECT_FALSE(observer.navigation_succeeded());
581    }
582
583   {
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()));
588
589     RedirectNotificationObserver load_observer2(
590         NOTIFICATION_LOAD_STOP,
591         Source<NavigationController>(
592             &shell()->web_contents()->GetController()));
593
594     EXPECT_TRUE(NavigateIframeToURL(shell(),
595                                     client_redirect_http_url, "test"));
596
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());
600
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());
605   }
606
607   {
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());
616    }
617
618   {
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()));
627
628     EXPECT_TRUE(NavigateIframeToURL(shell(),
629                                     client_redirect_http_url, "test"));
630
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());
634
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());
639   }
640 }
641
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());
654
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"));
658
659   NavigateToURL(shell(), main_url);
660
661   SitePerProcessWebContentsObserver observer(shell()->web_contents());
662   {
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()));
669
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()));
675
676     EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test"));
677
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());
682   }
683
684   {
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());
695   }
696
697   {
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"));
705
706     // DidFailProvisionalLoad when navigating to https_url.
707     EXPECT_EQ(observer.navigation_url(), https_url);
708     EXPECT_FALSE(observer.navigation_succeeded());
709   }
710
711   {
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"));
719
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());
723   }
724 }
725
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());
734
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");
739
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);
743
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);
748
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
757   // them.
758   tracking_delegate().SetTrackedURL(url2);
759   NavigateToURLContentInitiated(shell(), url2, true, true);
760
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());
768
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
776   // them.
777   tracking_delegate().SetTrackedURL(url3);
778   NavigateToURLContentInitiated(shell(), url3, false, true);
779
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());
787
788   // Make sure the request succeeded.
789   EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
790 }
791
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
795 // in-process.
796 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
797                        ReplaceEntryInProcessThenTranfers) {
798   const NavigationController& controller =
799       shell()->web_contents()->GetController();
800   ASSERT_TRUE(test_server()->Start());
801
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);
805
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);
811
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);
816
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());
822
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);
826
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());
834 }
835
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());
844
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");
849
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);
853
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);
867
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());
873
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);
883
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());
891 }
892
893 // Tests that the request is destroyed when a cross process navigation is
894 // cancelled.
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());
900
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");
905
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);
909
910   // Force all future navigations to transfer.
911   ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
912
913   NoTransferRequestDelegate no_transfer_request_delegate;
914   WebContentsDelegate* old_delegate = shell()->web_contents()->GetDelegate();
915   shell()->web_contents()->SetDelegate(&no_transfer_request_delegate);
916
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
925   // request for url2.
926   tracking_delegate().SetTrackedURL(url2);
927
928   // Don't wait for the navigation to complete, since that never happens in
929   // this case.
930   NavigateToURLContentInitiated(shell(), url2, false, false);
931
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());
936
937   // Make sure the request for url2 did not complete.
938   EXPECT_FALSE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
939
940   shell()->web_contents()->SetDelegate(old_delegate);
941 }
942
943 }  // namespace content