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