1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // 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.
10 #include "base/bind.h"
11 #include "base/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"
36 class RedirectTest : public InProcessBrowserTest {
40 std::vector<GURL> GetRedirects(const GURL& url) {
41 HistoryService* history_service =
42 HistoryServiceFactory::GetForProfile(browser()->profile(),
43 Profile::EXPLICIT_ACCESS);
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.
49 history_service->QueryRedirectsFrom(
51 base::Bind(&RedirectTest::OnRedirectQueryComplete,
52 base::Unretained(this),
55 content::RunMessageLoop();
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());
67 // Tracker for asynchronous history queries.
68 base::CancelableTaskTracker tracker_;
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());
78 ui_test_utils::NavigateToURL(browser(), first_url);
80 std::vector<GURL> redirects = GetRedirects(first_url);
82 ASSERT_EQ(1U, redirects.size());
83 EXPECT_EQ(final_url.spec(), redirects[0].spec());
86 // Tests a single client redirect.
87 IN_PROC_BROWSER_TEST_F(RedirectTest, Client) {
88 ASSERT_TRUE(test_server()->Start());
90 GURL final_url = test_server()->GetURL(std::string());
91 GURL first_url = test_server()->GetURL(
92 "client-redirect?" + final_url.spec());
94 // The client redirect appears as two page visits in the browser.
95 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
96 browser(), first_url, 2);
98 std::vector<GURL> redirects = GetRedirects(first_url);
100 ASSERT_EQ(1U, redirects.size());
101 EXPECT_EQ(final_url.spec(), redirects[0].spec());
103 // The address bar should display the final URL.
105 browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
107 // Navigate one more time.
108 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
109 browser(), first_url, 2);
111 // The address bar should still display the final URL.
113 browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
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());
120 // Create the file contents, which will do a redirect to the
122 GURL final_url = test_server()->GetURL(std::string());
123 ASSERT_TRUE(final_url.is_valid());
124 std::string file_redirect_contents = base::StringPrintf(
127 "<body onload=\"document.location='%s'\"></body>"
129 final_url.spec().c_str());
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(),
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()));
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);
148 std::vector<GURL> redirects = GetRedirects(first_url);
149 ASSERT_EQ(1U, redirects.size());
150 EXPECT_EQ(final_url.spec(), redirects[0].spec());
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(
158 base::FilePath().AppendASCII("cancelled_redirect_test.html"));
159 ui_test_utils::NavigateToURL(browser(), first_url);
161 content::WebContents* web_contents =
162 browser()->tab_strip_model()->GetActiveWebContents();
163 content::TestNavigationObserver navigation_observer(web_contents);
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
169 content::SimulateMouseClick(web_contents, 0,
170 blink::WebMouseEvent::ButtonLeft);
171 navigation_observer.Wait();
173 std::vector<GURL> redirects = GetRedirects(first_url);
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());
182 // Tests a client->server->server redirect
183 IN_PROC_BROWSER_TEST_F(RedirectTest, ClientServerServer) {
184 ASSERT_TRUE(test_server()->Start());
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());
194 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
195 browser(), first_url, 2);
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());
204 // Tests that the "#reference" gets preserved across server redirects.
205 IN_PROC_BROWSER_TEST_F(RedirectTest, ServerReference) {
206 ASSERT_TRUE(test_server()->Start());
208 const std::string ref("reference");
210 GURL final_url = test_server()->GetURL(std::string());
211 GURL initial_url = test_server()->GetURL(
212 "server-redirect?" + final_url.spec() + "#" + ref);
214 ui_test_utils::NavigateToURL(browser(), initial_url);
217 browser()->tab_strip_model()->GetActiveWebContents()->
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.
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"));
231 GURL initial_url = test_server()->GetURL(
232 "client-redirect?" + file_url.spec());
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());
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());
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());
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());
276 content::WebContents* web_contents =
277 browser()->tab_strip_model()->GetActiveWebContents();
278 content::TestNavigationObserver observer(web_contents, 2);
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);
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());
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;
306 EXPECT_TRUE(final_navigation_not_redirect);