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