Home | History | Annotate | Download | only in history
      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 // Navigates the browser to server and client redirect pages and makes sure
      6 // that the correct redirects are reflected in the history database. Errors
      7 // here might indicate that WebKit changed the calls our glue layer gets in
      8 // the case of redirects. It may also mean problems with the history system.
      9 
     10 #include "base/bind.h"
     11 #include "base/files/file_util.h"
     12 #include "base/files/scoped_temp_dir.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/strings/string16.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/strings/stringprintf.h"
     17 #include "base/strings/utf_string_conversions.h"
     18 #include "base/task/cancelable_task_tracker.h"
     19 #include "base/test/test_timeouts.h"
     20 #include "base/threading/platform_thread.h"
     21 #include "chrome/browser/history/history_service.h"
     22 #include "chrome/browser/history/history_service_factory.h"
     23 #include "chrome/browser/profiles/profile.h"
     24 #include "chrome/browser/ui/browser.h"
     25 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     26 #include "chrome/browser/ui/view_ids.h"
     27 #include "chrome/test/base/in_process_browser_test.h"
     28 #include "chrome/test/base/ui_test_utils.h"
     29 #include "content/public/browser/web_contents.h"
     30 #include "content/public/test/browser_test_utils.h"
     31 #include "content/public/test/test_navigation_observer.h"
     32 #include "net/base/filename_util.h"
     33 #include "net/test/spawned_test_server/spawned_test_server.h"
     34 #include "ui/events/event_constants.h"
     35 
     36 class RedirectTest : public InProcessBrowserTest {
     37  public:
     38   RedirectTest() {}
     39 
     40   std::vector<GURL> GetRedirects(const GURL& url) {
     41     HistoryService* history_service =
     42         HistoryServiceFactory::GetForProfile(browser()->profile(),
     43                                              Profile::EXPLICIT_ACCESS);
     44 
     45     // Schedule a history query for redirects. The response will be sent
     46     // asynchronously from the callback the history system uses to notify us
     47     // that it's done: OnRedirectQueryComplete.
     48     std::vector<GURL> rv;
     49     history_service->QueryRedirectsFrom(
     50         url,
     51         base::Bind(&RedirectTest::OnRedirectQueryComplete,
     52                    base::Unretained(this),
     53                    &rv),
     54         &tracker_);
     55     content::RunMessageLoop();
     56     return rv;
     57   }
     58 
     59  protected:
     60   void OnRedirectQueryComplete(std::vector<GURL>* rv,
     61                                const history::RedirectList* redirects) {
     62     rv->insert(rv->end(), redirects->begin(), redirects->end());
     63     base::MessageLoop::current()->PostTask(FROM_HERE,
     64                                            base::MessageLoop::QuitClosure());
     65   }
     66 
     67   // Tracker for asynchronous history queries.
     68   base::CancelableTaskTracker tracker_;
     69 };
     70 
     71 // Tests a single server redirect
     72 IN_PROC_BROWSER_TEST_F(RedirectTest, Server) {
     73   ASSERT_TRUE(test_server()->Start());
     74   GURL final_url = test_server()->GetURL(std::string());
     75   GURL first_url = test_server()->GetURL(
     76       "server-redirect?" + final_url.spec());
     77 
     78   ui_test_utils::NavigateToURL(browser(), first_url);
     79 
     80   std::vector<GURL> redirects = GetRedirects(first_url);
     81 
     82   ASSERT_EQ(1U, redirects.size());
     83   EXPECT_EQ(final_url.spec(), redirects[0].spec());
     84 }
     85 
     86 // Tests a single client redirect.
     87 IN_PROC_BROWSER_TEST_F(RedirectTest, Client) {
     88   ASSERT_TRUE(test_server()->Start());
     89 
     90   GURL final_url = test_server()->GetURL(std::string());
     91   GURL first_url = test_server()->GetURL(
     92       "client-redirect?" + final_url.spec());
     93 
     94   // The client redirect appears as two page visits in the browser.
     95   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
     96       browser(), first_url, 2);
     97 
     98   std::vector<GURL> redirects = GetRedirects(first_url);
     99 
    100   ASSERT_EQ(1U, redirects.size());
    101   EXPECT_EQ(final_url.spec(), redirects[0].spec());
    102 
    103   // The address bar should display the final URL.
    104   EXPECT_EQ(final_url,
    105             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    106 
    107   // Navigate one more time.
    108   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
    109       browser(), first_url, 2);
    110 
    111   // The address bar should still display the final URL.
    112   EXPECT_EQ(final_url,
    113             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
    114 }
    115 
    116 // http://code.google.com/p/chromium/issues/detail?id=62772
    117 IN_PROC_BROWSER_TEST_F(RedirectTest, ClientEmptyReferer) {
    118   ASSERT_TRUE(test_server()->Start());
    119 
    120   // Create the file contents, which will do a redirect to the
    121   // test server.
    122   GURL final_url = test_server()->GetURL(std::string());
    123   ASSERT_TRUE(final_url.is_valid());
    124   std::string file_redirect_contents = base::StringPrintf(
    125       "<html>"
    126       "<head></head>"
    127       "<body onload=\"document.location='%s'\"></body>"
    128       "</html>",
    129       final_url.spec().c_str());
    130 
    131   // Write the contents to a temporary file.
    132   base::ScopedTempDir temp_directory;
    133   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
    134   base::FilePath temp_file;
    135   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_directory.path(),
    136                                              &temp_file));
    137   ASSERT_EQ(static_cast<int>(file_redirect_contents.size()),
    138             base::WriteFile(temp_file,
    139                             file_redirect_contents.data(),
    140                             file_redirect_contents.size()));
    141 
    142   // Navigate to the file through the browser. The client redirect will appear
    143   // as two page visits in the browser.
    144   GURL first_url = net::FilePathToFileURL(temp_file);
    145   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
    146       browser(), first_url, 2);
    147 
    148   std::vector<GURL> redirects = GetRedirects(first_url);
    149   ASSERT_EQ(1U, redirects.size());
    150   EXPECT_EQ(final_url.spec(), redirects[0].spec());
    151 }
    152 
    153 // Tests to make sure a location change when a pending redirect exists isn't
    154 // flagged as a redirect.
    155 IN_PROC_BROWSER_TEST_F(RedirectTest, ClientCancelled) {
    156   GURL first_url = ui_test_utils::GetTestUrl(
    157       base::FilePath(),
    158       base::FilePath().AppendASCII("cancelled_redirect_test.html"));
    159   ui_test_utils::NavigateToURL(browser(), first_url);
    160 
    161   content::WebContents* web_contents =
    162       browser()->tab_strip_model()->GetActiveWebContents();
    163   content::TestNavigationObserver navigation_observer(web_contents);
    164 
    165   // Simulate a click to force to make a user-initiated location change;
    166   // otherwise, a non user-initiated in-page location change will be treated
    167   // as client redirect and the redirect will be recoreded, which can cause
    168   // this test failed.
    169   content::SimulateMouseClick(web_contents, 0,
    170       blink::WebMouseEvent::ButtonLeft);
    171   navigation_observer.Wait();
    172 
    173   std::vector<GURL> redirects = GetRedirects(first_url);
    174 
    175   // There should be no redirects from first_url, because the anchor location
    176   // change that occurs should not be flagged as a redirect and the meta-refresh
    177   // won't have fired yet.
    178   ASSERT_EQ(0U, redirects.size());
    179   EXPECT_EQ("myanchor", web_contents->GetURL().ref());
    180 }
    181 
    182 // Tests a client->server->server redirect
    183 IN_PROC_BROWSER_TEST_F(RedirectTest, ClientServerServer) {
    184   ASSERT_TRUE(test_server()->Start());
    185 
    186   GURL final_url = test_server()->GetURL(std::string());
    187   GURL next_to_last = test_server()->GetURL(
    188       "server-redirect?" + final_url.spec());
    189   GURL second_url = test_server()->GetURL(
    190       "server-redirect?" + next_to_last.spec());
    191   GURL first_url = test_server()->GetURL(
    192       "client-redirect?" + second_url.spec());
    193 
    194   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
    195       browser(), first_url, 2);
    196 
    197   std::vector<GURL> redirects = GetRedirects(first_url);
    198   ASSERT_EQ(3U, redirects.size());
    199   EXPECT_EQ(second_url.spec(), redirects[0].spec());
    200   EXPECT_EQ(next_to_last.spec(), redirects[1].spec());
    201   EXPECT_EQ(final_url.spec(), redirects[2].spec());
    202 }
    203 
    204 // Tests that the "#reference" gets preserved across server redirects.
    205 IN_PROC_BROWSER_TEST_F(RedirectTest, ServerReference) {
    206   ASSERT_TRUE(test_server()->Start());
    207 
    208   const std::string ref("reference");
    209 
    210   GURL final_url = test_server()->GetURL(std::string());
    211   GURL initial_url = test_server()->GetURL(
    212       "server-redirect?" + final_url.spec() + "#" + ref);
    213 
    214   ui_test_utils::NavigateToURL(browser(), initial_url);
    215 
    216   EXPECT_EQ(ref,
    217             browser()->tab_strip_model()->GetActiveWebContents()->
    218                 GetURL().ref());
    219 }
    220 
    221 // Test that redirect from http:// to file:// :
    222 // A) does not crash the browser or confuse the redirect chain, see bug 1080873
    223 // B) does not take place.
    224 //
    225 // Flaky on XP and Vista, http://crbug.com/69390.
    226 IN_PROC_BROWSER_TEST_F(RedirectTest, NoHttpToFile) {
    227   ASSERT_TRUE(test_server()->Start());
    228   GURL file_url = ui_test_utils::GetTestUrl(
    229       base::FilePath(), base::FilePath().AppendASCII("http_to_file.html"));
    230 
    231   GURL initial_url = test_server()->GetURL(
    232       "client-redirect?" + file_url.spec());
    233 
    234   ui_test_utils::NavigateToURL(browser(), initial_url);
    235   // We make sure the title doesn't match the title from the file, because the
    236   // nav should not have taken place.
    237   EXPECT_NE(base::ASCIIToUTF16("File!"),
    238             browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
    239 }
    240 
    241 // Ensures that non-user initiated location changes (within page) are
    242 // flagged as client redirects. See bug 1139823.
    243 IN_PROC_BROWSER_TEST_F(RedirectTest, ClientFragments) {
    244   ASSERT_TRUE(test_server()->Start());
    245   GURL first_url = ui_test_utils::GetTestUrl(
    246       base::FilePath(), base::FilePath().AppendASCII("ref_redirect.html"));
    247   ui_test_utils::NavigateToURL(browser(), first_url);
    248   std::vector<GURL> redirects = GetRedirects(first_url);
    249   EXPECT_EQ(1U, redirects.size());
    250   EXPECT_EQ(first_url.spec() + "#myanchor", redirects[0].spec());
    251 }
    252 
    253 // TODO(timsteele): This is disabled because our current testserver can't
    254 // handle multiple requests in parallel, making it hang on the first request
    255 // to /slow?60. It's unable to serve our second request for files/title2.html
    256 // until /slow? completes, which doesn't give the desired behavior. We could
    257 // alternatively load the second page from disk, but we would need to start
    258 // the browser for this testcase with --process-per-tab, and I don't think
    259 // we can do this at test-case-level granularity at the moment.
    260 // http://crbug.com/45056
    261 IN_PROC_BROWSER_TEST_F(RedirectTest,
    262        DISABLED_ClientCancelledByNewNavigationAfterProvisionalLoad) {
    263   // We want to initiate a second navigation after the provisional load for
    264   // the client redirect destination has started, but before this load is
    265   // committed. To achieve this, we tell the browser to load a slow page,
    266   // which causes it to start a provisional load, and while it is waiting
    267   // for the response (which means it hasn't committed the load for the client
    268   // redirect destination page yet), we issue a new navigation request.
    269   ASSERT_TRUE(test_server()->Start());
    270 
    271   GURL final_url = test_server()->GetURL("files/title2.html");
    272   GURL slow = test_server()->GetURL("slow?60");
    273   GURL first_url = test_server()->GetURL(
    274       "client-redirect?" + slow.spec());
    275 
    276   content::WebContents* web_contents =
    277       browser()->tab_strip_model()->GetActiveWebContents();
    278   content::TestNavigationObserver observer(web_contents, 2);
    279 
    280   ui_test_utils::NavigateToURLWithDisposition(
    281       browser(), first_url, CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE);
    282   // We don't sleep here - the first navigation won't have been committed yet
    283   // because we told the server to wait a minute. This means the browser has
    284   // started it's provisional load for the client redirect destination page but
    285   // hasn't completed. Our time is now!
    286   ui_test_utils::NavigateToURLWithDisposition(
    287       browser(), final_url, CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE);
    288   observer.Wait();
    289 
    290   // Check to make sure the navigation did in fact take place and we are
    291   // at the expected page.
    292   EXPECT_EQ(base::ASCIIToUTF16("Title Of Awesomeness"),
    293             browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
    294 
    295   bool final_navigation_not_redirect = true;
    296   std::vector<GURL> redirects = GetRedirects(first_url);
    297   // Check to make sure our request for files/title2.html doesn't get flagged
    298   // as a client redirect from the first (/client-redirect?) page.
    299   for (std::vector<GURL>::iterator it = redirects.begin();
    300        it != redirects.end(); ++it) {
    301     if (final_url.spec() == it->spec()) {
    302       final_navigation_not_redirect = false;
    303       break;
    304     }
    305   }
    306   EXPECT_TRUE(final_navigation_not_redirect);
    307 }
    308