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
36class 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
72IN_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.
87IN_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
117IN_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.
155IN_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
183IN_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.
205IN_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.
226IN_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.
243IN_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
261IN_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