Home | History | Annotate | Download | only in browser
      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 "content/browser/frame_host/cross_process_frame_connector.h"
      8 #include "content/browser/frame_host/frame_tree.h"
      9 #include "content/browser/frame_host/render_frame_proxy_host.h"
     10 #include "content/browser/frame_host/render_widget_host_view_child_frame.h"
     11 #include "content/browser/renderer_host/render_view_host_impl.h"
     12 #include "content/browser/web_contents/web_contents_impl.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/web_contents_observer.h"
     17 #include "content/public/common/content_switches.h"
     18 #include "content/public/test/browser_test_utils.h"
     19 #include "content/public/test/content_browser_test.h"
     20 #include "content/public/test/content_browser_test_utils.h"
     21 #include "content/public/test/test_utils.h"
     22 #include "content/shell/browser/shell.h"
     23 #include "content/test/content_browser_test_utils_internal.h"
     24 #include "net/dns/mock_host_resolver.h"
     25 #include "url/gurl.h"
     26 
     27 namespace content {
     28 
     29 class SitePerProcessWebContentsObserver: public WebContentsObserver {
     30  public:
     31   explicit SitePerProcessWebContentsObserver(WebContents* web_contents)
     32       : WebContentsObserver(web_contents),
     33         navigation_succeeded_(false) {}
     34   virtual ~SitePerProcessWebContentsObserver() {}
     35 
     36   virtual void DidStartProvisionalLoadForFrame(
     37       int64 frame_id,
     38       int64 parent_frame_id,
     39       bool is_main_frame,
     40       const GURL& validated_url,
     41       bool is_error_page,
     42       bool is_iframe_srcdoc,
     43       RenderViewHost* render_view_host) OVERRIDE {
     44     navigation_succeeded_ = false;
     45   }
     46 
     47   virtual void DidFailProvisionalLoad(
     48       int64 frame_id,
     49       const base::string16& frame_unique_name,
     50       bool is_main_frame,
     51       const GURL& validated_url,
     52       int error_code,
     53       const base::string16& error_description,
     54       RenderViewHost* render_view_host) OVERRIDE {
     55     navigation_url_ = validated_url;
     56     navigation_succeeded_ = false;
     57   }
     58 
     59   virtual void DidCommitProvisionalLoadForFrame(
     60       int64 frame_id,
     61       const base::string16& frame_unique_name,
     62       bool is_main_frame,
     63       const GURL& url,
     64       PageTransition transition_type,
     65       RenderViewHost* render_view_host) OVERRIDE{
     66     navigation_url_ = url;
     67     navigation_succeeded_ = true;
     68   }
     69 
     70   const GURL& navigation_url() const {
     71     return navigation_url_;
     72   }
     73 
     74   int navigation_succeeded() const { return navigation_succeeded_; }
     75 
     76  private:
     77   GURL navigation_url_;
     78   bool navigation_succeeded_;
     79 
     80   DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver);
     81 };
     82 
     83 class RedirectNotificationObserver : public NotificationObserver {
     84  public:
     85   // Register to listen for notifications of the given type from either a
     86   // specific source, or from all sources if |source| is
     87   // NotificationService::AllSources().
     88   RedirectNotificationObserver(int notification_type,
     89                                const NotificationSource& source);
     90   virtual ~RedirectNotificationObserver();
     91 
     92   // Wait until the specified notification occurs.  If the notification was
     93   // emitted between the construction of this object and this call then it
     94   // returns immediately.
     95   void Wait();
     96 
     97   // Returns NotificationService::AllSources() if we haven't observed a
     98   // notification yet.
     99   const NotificationSource& source() const {
    100     return source_;
    101   }
    102 
    103   const NotificationDetails& details() const {
    104     return details_;
    105   }
    106 
    107   // NotificationObserver:
    108   virtual void Observe(int type,
    109                        const NotificationSource& source,
    110                        const NotificationDetails& details) OVERRIDE;
    111 
    112  private:
    113   bool seen_;
    114   bool seen_twice_;
    115   bool running_;
    116   NotificationRegistrar registrar_;
    117 
    118   NotificationSource source_;
    119   NotificationDetails details_;
    120   scoped_refptr<MessageLoopRunner> message_loop_runner_;
    121 
    122   DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver);
    123 };
    124 
    125 RedirectNotificationObserver::RedirectNotificationObserver(
    126     int notification_type,
    127     const NotificationSource& source)
    128     : seen_(false),
    129       running_(false),
    130       source_(NotificationService::AllSources()) {
    131   registrar_.Add(this, notification_type, source);
    132 }
    133 
    134 RedirectNotificationObserver::~RedirectNotificationObserver() {}
    135 
    136 void RedirectNotificationObserver::Wait() {
    137   if (seen_ && seen_twice_)
    138     return;
    139 
    140   running_ = true;
    141   message_loop_runner_ = new MessageLoopRunner;
    142   message_loop_runner_->Run();
    143   EXPECT_TRUE(seen_);
    144 }
    145 
    146 void RedirectNotificationObserver::Observe(
    147     int type,
    148     const NotificationSource& source,
    149     const NotificationDetails& details) {
    150   source_ = source;
    151   details_ = details;
    152   seen_twice_ = seen_;
    153   seen_ = true;
    154   if (!running_)
    155     return;
    156 
    157   message_loop_runner_->Quit();
    158   running_ = false;
    159 }
    160 
    161 class SitePerProcessBrowserTest : public ContentBrowserTest {
    162  public:
    163   SitePerProcessBrowserTest() {}
    164 
    165  protected:
    166   // Start at a data URL so each extra navigation creates a navigation entry.
    167   // (The first navigation will silently be classified as AUTO_SUBFRAME.)
    168   // TODO(creis): This won't be necessary when we can wait for LOAD_STOP.
    169   void StartFrameAtDataURL() {
    170     std::string data_url_script =
    171       "var iframes = document.getElementById('test');iframes.src="
    172       "'data:text/html,dataurl';";
    173     ASSERT_TRUE(ExecuteScript(shell()->web_contents(), data_url_script));
    174   }
    175 
    176   bool NavigateIframeToURL(Shell* window,
    177                            const GURL& url,
    178                            std::string iframe_id) {
    179     // TODO(creis): This should wait for LOAD_STOP, but cross-site subframe
    180     // navigations generate extra DidStartLoading and DidStopLoading messages.
    181     // Until we replace swappedout:// with frame proxies, we need to listen for
    182     // something else.  For now, we trigger NEW_SUBFRAME navigations and listen
    183     // for commit.
    184     std::string script = base::StringPrintf(
    185         "setTimeout(\""
    186         "var iframes = document.getElementById('%s');iframes.src='%s';"
    187         "\",0)",
    188         iframe_id.c_str(), url.spec().c_str());
    189     WindowedNotificationObserver load_observer(
    190         NOTIFICATION_NAV_ENTRY_COMMITTED,
    191         Source<NavigationController>(
    192             &window->web_contents()->GetController()));
    193     bool result = ExecuteScript(window->web_contents(), script);
    194     load_observer.Wait();
    195     return result;
    196   }
    197 
    198   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    199     command_line->AppendSwitch(switches::kSitePerProcess);
    200   }
    201 };
    202 
    203 // Ensure that we can complete a cross-process subframe navigation.
    204 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteIframe) {
    205   host_resolver()->AddRule("*", "127.0.0.1");
    206   ASSERT_TRUE(test_server()->Start());
    207   GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
    208   NavigateToURL(shell(), main_url);
    209 
    210   // It is safe to obtain the root frame tree node here, as it doesn't change.
    211   FrameTreeNode* root =
    212       static_cast<WebContentsImpl*>(shell()->web_contents())->
    213           GetFrameTree()->root();
    214 
    215   SitePerProcessWebContentsObserver observer(shell()->web_contents());
    216 
    217   // Load same-site page into iframe.
    218   FrameTreeNode* child = root->child_at(0);
    219   GURL http_url(test_server()->GetURL("files/title1.html"));
    220   NavigateFrameToURL(child, http_url);
    221   EXPECT_EQ(http_url, observer.navigation_url());
    222   EXPECT_TRUE(observer.navigation_succeeded());
    223   {
    224     // There should be only one RenderWidgetHost when there are no
    225     // cross-process iframes.
    226     std::set<RenderWidgetHostView*> views_set =
    227         static_cast<WebContentsImpl*>(shell()->web_contents())
    228             ->GetRenderWidgetHostViewsInTree();
    229     EXPECT_EQ(1U, views_set.size());
    230   }
    231   RenderFrameProxyHost* proxy_to_parent =
    232       child->render_manager()->GetRenderFrameProxyHost(
    233           shell()->web_contents()->GetSiteInstance());
    234   EXPECT_FALSE(proxy_to_parent);
    235 
    236   // These must stay in scope with replace_host.
    237   GURL::Replacements replace_host;
    238   std::string foo_com("foo.com");
    239 
    240   // Load cross-site page into iframe.
    241   GURL cross_site_url(test_server()->GetURL("files/title2.html"));
    242   replace_host.SetHostStr(foo_com);
    243   cross_site_url = cross_site_url.ReplaceComponents(replace_host);
    244   NavigateFrameToURL(root->child_at(0), cross_site_url);
    245   EXPECT_EQ(cross_site_url, observer.navigation_url());
    246   EXPECT_TRUE(observer.navigation_succeeded());
    247 
    248   // Ensure that we have created a new process for the subframe.
    249   ASSERT_EQ(1U, root->child_count());
    250   SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance();
    251   RenderViewHost* rvh = child->current_frame_host()->render_view_host();
    252   RenderProcessHost* rph = child->current_frame_host()->GetProcess();
    253   EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh);
    254   EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
    255   EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph);
    256   {
    257     // There should be now two RenderWidgetHosts, one for each process
    258     // rendering a frame.
    259     std::set<RenderWidgetHostView*> views_set =
    260         static_cast<WebContentsImpl*>(shell()->web_contents())
    261             ->GetRenderWidgetHostViewsInTree();
    262     EXPECT_EQ(2U, views_set.size());
    263   }
    264   proxy_to_parent = child->render_manager()->GetProxyToParent();
    265   EXPECT_TRUE(proxy_to_parent);
    266   EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
    267   EXPECT_EQ(
    268       rvh->GetView(),
    269       proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
    270 
    271   // Load another cross-site page into the same iframe.
    272   cross_site_url = test_server()->GetURL("files/title3.html");
    273   std::string bar_com("bar.com");
    274   replace_host.SetHostStr(bar_com);
    275   cross_site_url = cross_site_url.ReplaceComponents(replace_host);
    276   NavigateFrameToURL(root->child_at(0), cross_site_url);
    277   EXPECT_EQ(cross_site_url, observer.navigation_url());
    278   EXPECT_TRUE(observer.navigation_succeeded());
    279 
    280   // Check again that a new process is created and is different from the
    281   // top level one and the previous one.
    282   ASSERT_EQ(1U, root->child_count());
    283   child = root->child_at(0);
    284   EXPECT_NE(shell()->web_contents()->GetRenderViewHost(),
    285             child->current_frame_host()->render_view_host());
    286   EXPECT_NE(rvh, child->current_frame_host()->render_view_host());
    287   EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
    288             child->current_frame_host()->GetSiteInstance());
    289   EXPECT_NE(site_instance,
    290             child->current_frame_host()->GetSiteInstance());
    291   EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(),
    292             child->current_frame_host()->GetProcess());
    293   EXPECT_NE(rph, child->current_frame_host()->GetProcess());
    294   {
    295     std::set<RenderWidgetHostView*> views_set =
    296         static_cast<WebContentsImpl*>(shell()->web_contents())
    297             ->GetRenderWidgetHostViewsInTree();
    298     EXPECT_EQ(2U, views_set.size());
    299   }
    300   EXPECT_EQ(proxy_to_parent, child->render_manager()->GetProxyToParent());
    301   EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
    302   EXPECT_EQ(
    303       child->current_frame_host()->render_view_host()->GetView(),
    304       proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
    305 }
    306 
    307 // Crash a subframe and ensures its children are cleared from the FrameTree.
    308 // See http://crbug.com/338508.
    309 // TODO(creis): Enable this on Android when we can kill the process there.
    310 #if defined(OS_ANDROID)
    311 #define MAYBE_CrashSubframe DISABLED_CrashSubframe
    312 #else
    313 #define MAYBE_CrashSubframe CrashSubframe
    314 #endif
    315 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, MAYBE_CrashSubframe) {
    316   host_resolver()->AddRule("*", "127.0.0.1");
    317   ASSERT_TRUE(test_server()->Start());
    318   GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
    319   NavigateToURL(shell(), main_url);
    320 
    321   StartFrameAtDataURL();
    322 
    323   // These must stay in scope with replace_host.
    324   GURL::Replacements replace_host;
    325   std::string foo_com("foo.com");
    326 
    327   // Load cross-site page into iframe.
    328   GURL cross_site_url(test_server()->GetURL("files/title2.html"));
    329   replace_host.SetHostStr(foo_com);
    330   cross_site_url = cross_site_url.ReplaceComponents(replace_host);
    331   EXPECT_TRUE(NavigateIframeToURL(shell(), cross_site_url, "test"));
    332 
    333   // Check the subframe process.
    334   FrameTreeNode* root =
    335       static_cast<WebContentsImpl*>(shell()->web_contents())->
    336           GetFrameTree()->root();
    337   ASSERT_EQ(1U, root->child_count());
    338   FrameTreeNode* child = root->child_at(0);
    339   EXPECT_EQ(main_url, root->current_url());
    340   EXPECT_EQ(cross_site_url, child->current_url());
    341 
    342   // Crash the subframe process.
    343   RenderProcessHost* root_process = root->current_frame_host()->GetProcess();
    344   RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
    345   {
    346     RenderProcessHostWatcher crash_observer(
    347         child_process,
    348         RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
    349     base::KillProcess(child_process->GetHandle(), 0, false);
    350     crash_observer.Wait();
    351   }
    352 
    353   // Ensure that the child frame still exists but has been cleared.
    354   EXPECT_EQ(1U, root->child_count());
    355   EXPECT_EQ(main_url, root->current_url());
    356   EXPECT_EQ(GURL(), child->current_url());
    357 
    358   // Now crash the top-level page to clear the child frame.
    359   {
    360     RenderProcessHostWatcher crash_observer(
    361         root_process,
    362         RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
    363     base::KillProcess(root_process->GetHandle(), 0, false);
    364     crash_observer.Wait();
    365   }
    366   EXPECT_EQ(0U, root->child_count());
    367   EXPECT_EQ(GURL(), root->current_url());
    368 }
    369 
    370 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
    371 // security checks are back in place.
    372 // TODO(creis): Replace SpawnedTestServer with host_resolver to get test to run
    373 // on Android (http://crbug.com/187570).
    374 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
    375                        DISABLED_CrossSiteIframeRedirectOnce) {
    376   ASSERT_TRUE(test_server()->Start());
    377   net::SpawnedTestServer https_server(
    378       net::SpawnedTestServer::TYPE_HTTPS,
    379       net::SpawnedTestServer::kLocalhost,
    380       base::FilePath(FILE_PATH_LITERAL("content/test/data")));
    381   ASSERT_TRUE(https_server.Start());
    382 
    383   GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
    384   GURL http_url(test_server()->GetURL("files/title1.html"));
    385   GURL https_url(https_server.GetURL("files/title1.html"));
    386 
    387   NavigateToURL(shell(), main_url);
    388 
    389   SitePerProcessWebContentsObserver observer(shell()->web_contents());
    390   {
    391     // Load cross-site client-redirect page into Iframe.
    392     // Should be blocked.
    393     GURL client_redirect_https_url(https_server.GetURL(
    394         "client-redirect?files/title1.html"));
    395     EXPECT_TRUE(NavigateIframeToURL(shell(),
    396                                     client_redirect_https_url, "test"));
    397     // DidFailProvisionalLoad when navigating to client_redirect_https_url.
    398     EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
    399     EXPECT_FALSE(observer.navigation_succeeded());
    400   }
    401 
    402   {
    403     // Load cross-site server-redirect page into Iframe,
    404     // which redirects to same-site page.
    405     GURL server_redirect_http_url(https_server.GetURL(
    406         "server-redirect?" + http_url.spec()));
    407     EXPECT_TRUE(NavigateIframeToURL(shell(),
    408                                     server_redirect_http_url, "test"));
    409     EXPECT_EQ(observer.navigation_url(), http_url);
    410     EXPECT_TRUE(observer.navigation_succeeded());
    411   }
    412 
    413   {
    414     // Load cross-site server-redirect page into Iframe,
    415     // which redirects to cross-site page.
    416     GURL server_redirect_http_url(https_server.GetURL(
    417         "server-redirect?files/title1.html"));
    418     EXPECT_TRUE(NavigateIframeToURL(shell(),
    419                                     server_redirect_http_url, "test"));
    420     // DidFailProvisionalLoad when navigating to https_url.
    421     EXPECT_EQ(observer.navigation_url(), https_url);
    422     EXPECT_FALSE(observer.navigation_succeeded());
    423   }
    424 
    425   {
    426     // Load same-site server-redirect page into Iframe,
    427     // which redirects to cross-site page.
    428     GURL server_redirect_http_url(test_server()->GetURL(
    429         "server-redirect?" + https_url.spec()));
    430     EXPECT_TRUE(NavigateIframeToURL(shell(),
    431                                     server_redirect_http_url, "test"));
    432 
    433     EXPECT_EQ(observer.navigation_url(), https_url);
    434     EXPECT_FALSE(observer.navigation_succeeded());
    435    }
    436 
    437   {
    438     // Load same-site client-redirect page into Iframe,
    439     // which redirects to cross-site page.
    440     GURL client_redirect_http_url(test_server()->GetURL(
    441         "client-redirect?" + https_url.spec()));
    442 
    443     RedirectNotificationObserver load_observer2(
    444         NOTIFICATION_LOAD_STOP,
    445         Source<NavigationController>(
    446             &shell()->web_contents()->GetController()));
    447 
    448     EXPECT_TRUE(NavigateIframeToURL(shell(),
    449                                     client_redirect_http_url, "test"));
    450 
    451     // Same-site Client-Redirect Page should be loaded successfully.
    452     EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
    453     EXPECT_TRUE(observer.navigation_succeeded());
    454 
    455     // Redirecting to Cross-site Page should be blocked.
    456     load_observer2.Wait();
    457     EXPECT_EQ(observer.navigation_url(), https_url);
    458     EXPECT_FALSE(observer.navigation_succeeded());
    459   }
    460 
    461   {
    462     // Load same-site server-redirect page into Iframe,
    463     // which redirects to same-site page.
    464     GURL server_redirect_http_url(test_server()->GetURL(
    465         "server-redirect?files/title1.html"));
    466     EXPECT_TRUE(NavigateIframeToURL(shell(),
    467                                     server_redirect_http_url, "test"));
    468     EXPECT_EQ(observer.navigation_url(), http_url);
    469     EXPECT_TRUE(observer.navigation_succeeded());
    470    }
    471 
    472   {
    473     // Load same-site client-redirect page into Iframe,
    474     // which redirects to same-site page.
    475     GURL client_redirect_http_url(test_server()->GetURL(
    476         "client-redirect?" + http_url.spec()));
    477     RedirectNotificationObserver load_observer2(
    478         NOTIFICATION_LOAD_STOP,
    479         Source<NavigationController>(
    480             &shell()->web_contents()->GetController()));
    481 
    482     EXPECT_TRUE(NavigateIframeToURL(shell(),
    483                                     client_redirect_http_url, "test"));
    484 
    485     // Same-site Client-Redirect Page should be loaded successfully.
    486     EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
    487     EXPECT_TRUE(observer.navigation_succeeded());
    488 
    489     // Redirecting to Same-site Page should be loaded successfully.
    490     load_observer2.Wait();
    491     EXPECT_EQ(observer.navigation_url(), http_url);
    492     EXPECT_TRUE(observer.navigation_succeeded());
    493   }
    494 }
    495 
    496 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
    497 // security checks are back in place.
    498 // TODO(creis): Replace SpawnedTestServer with host_resolver to get test to run
    499 // on Android (http://crbug.com/187570).
    500 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
    501                        DISABLED_CrossSiteIframeRedirectTwice) {
    502   ASSERT_TRUE(test_server()->Start());
    503   net::SpawnedTestServer https_server(
    504       net::SpawnedTestServer::TYPE_HTTPS,
    505       net::SpawnedTestServer::kLocalhost,
    506       base::FilePath(FILE_PATH_LITERAL("content/test/data")));
    507   ASSERT_TRUE(https_server.Start());
    508 
    509   GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
    510   GURL http_url(test_server()->GetURL("files/title1.html"));
    511   GURL https_url(https_server.GetURL("files/title1.html"));
    512 
    513   NavigateToURL(shell(), main_url);
    514 
    515   SitePerProcessWebContentsObserver observer(shell()->web_contents());
    516   {
    517     // Load client-redirect page pointing to a cross-site client-redirect page,
    518     // which eventually redirects back to same-site page.
    519     GURL client_redirect_https_url(https_server.GetURL(
    520         "client-redirect?" + http_url.spec()));
    521     GURL client_redirect_http_url(test_server()->GetURL(
    522         "client-redirect?" + client_redirect_https_url.spec()));
    523 
    524     // We should wait until second client redirect get cancelled.
    525     RedirectNotificationObserver load_observer2(
    526         NOTIFICATION_LOAD_STOP,
    527         Source<NavigationController>(
    528             &shell()->web_contents()->GetController()));
    529 
    530     EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test"));
    531 
    532     // DidFailProvisionalLoad when navigating to client_redirect_https_url.
    533     load_observer2.Wait();
    534     EXPECT_EQ(observer.navigation_url(), client_redirect_https_url);
    535     EXPECT_FALSE(observer.navigation_succeeded());
    536   }
    537 
    538   {
    539     // Load server-redirect page pointing to a cross-site server-redirect page,
    540     // which eventually redirect back to same-site page.
    541     GURL server_redirect_https_url(https_server.GetURL(
    542         "server-redirect?" + http_url.spec()));
    543     GURL server_redirect_http_url(test_server()->GetURL(
    544         "server-redirect?" + server_redirect_https_url.spec()));
    545     EXPECT_TRUE(NavigateIframeToURL(shell(),
    546                                     server_redirect_http_url, "test"));
    547     EXPECT_EQ(observer.navigation_url(), http_url);
    548     EXPECT_TRUE(observer.navigation_succeeded());
    549   }
    550 
    551   {
    552     // Load server-redirect page pointing to a cross-site server-redirect page,
    553     // which eventually redirects back to cross-site page.
    554     GURL server_redirect_https_url(https_server.GetURL(
    555         "server-redirect?" + https_url.spec()));
    556     GURL server_redirect_http_url(test_server()->GetURL(
    557         "server-redirect?" + server_redirect_https_url.spec()));
    558     EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
    559 
    560     // DidFailProvisionalLoad when navigating to https_url.
    561     EXPECT_EQ(observer.navigation_url(), https_url);
    562     EXPECT_FALSE(observer.navigation_succeeded());
    563   }
    564 
    565   {
    566     // Load server-redirect page pointing to a cross-site client-redirect page,
    567     // which eventually redirects back to same-site page.
    568     GURL client_redirect_http_url(https_server.GetURL(
    569         "client-redirect?" + http_url.spec()));
    570     GURL server_redirect_http_url(test_server()->GetURL(
    571         "server-redirect?" + client_redirect_http_url.spec()));
    572     EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test"));
    573 
    574     // DidFailProvisionalLoad when navigating to client_redirect_http_url.
    575     EXPECT_EQ(observer.navigation_url(), client_redirect_http_url);
    576     EXPECT_FALSE(observer.navigation_succeeded());
    577   }
    578 }
    579 
    580 }  // namespace content
    581