1// Copyright (c) 2011 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/file_util.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/memory/scoped_temp_dir.h"
13#include "base/string_util.h"
14#include "base/string16.h"
15#include "base/test/test_timeouts.h"
16#include "base/threading/platform_thread.h"
17#include "base/utf_string_conversions.h"
18#include "chrome/browser/ui/view_ids.h"
19#include "chrome/test/automation/browser_proxy.h"
20#include "chrome/test/automation/tab_proxy.h"
21#include "chrome/test/automation/window_proxy.h"
22#include "chrome/test/ui/ui_test.h"
23#include "net/base/net_util.h"
24#include "net/test/test_server.h"
25#include "ui/base/events.h"
26
27namespace {
28
29class RedirectTest : public UITest {
30 public:
31  RedirectTest()
32      : test_server_(net::TestServer::TYPE_HTTP,
33                     FilePath(FILE_PATH_LITERAL("chrome/test/data"))) {
34  }
35
36 protected:
37  net::TestServer test_server_;
38};
39
40// Tests a single server redirect
41TEST_F(RedirectTest, Server) {
42  ASSERT_TRUE(test_server_.Start());
43
44  GURL final_url = test_server_.GetURL(std::string());
45  GURL first_url = test_server_.GetURL(
46      "server-redirect?" + final_url.spec());
47
48  NavigateToURL(first_url);
49
50  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
51  ASSERT_TRUE(tab_proxy.get());
52
53  std::vector<GURL> redirects;
54  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
55
56  ASSERT_EQ(1U, redirects.size());
57  EXPECT_EQ(final_url.spec(), redirects[0].spec());
58}
59
60// Tests a single client redirect.
61TEST_F(RedirectTest, Client) {
62  ASSERT_TRUE(test_server_.Start());
63
64  GURL final_url = test_server_.GetURL(std::string());
65  GURL first_url = test_server_.GetURL(
66      "client-redirect?" + final_url.spec());
67
68  // The client redirect appears as two page visits in the browser.
69  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);
70
71  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
72  ASSERT_TRUE(tab_proxy.get());
73
74  std::vector<GURL> redirects;
75  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
76
77  ASSERT_EQ(1U, redirects.size());
78  EXPECT_EQ(final_url.spec(), redirects[0].spec());
79
80  // The address bar should display the final URL.
81  GURL tab_url;
82  EXPECT_TRUE(tab_proxy->GetCurrentURL(&tab_url));
83  EXPECT_TRUE(final_url == tab_url);
84
85  // Navigate one more time.
86  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);
87
88  // The address bar should still display the final URL.
89  EXPECT_TRUE(tab_proxy->GetCurrentURL(&tab_url));
90  EXPECT_TRUE(final_url == tab_url);
91}
92
93// http://code.google.com/p/chromium/issues/detail?id=62772
94TEST_F(RedirectTest, FLAKY_ClientEmptyReferer) {
95  ASSERT_TRUE(test_server_.Start());
96
97  // Create the file contents, which will do a redirect to the
98  // test server.
99  GURL final_url = test_server_.GetURL(std::string());
100  ASSERT_TRUE(final_url.is_valid());
101  std::string file_redirect_contents = StringPrintf(
102      "<html>"
103      "<head></head>"
104      "<body onload=\"document.location='%s'\"></body>"
105      "</html>",
106      final_url.spec().c_str());
107
108  // Write the contents to a temporary file.
109  ScopedTempDir temp_directory;
110  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
111  FilePath temp_file;
112  ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_directory.path(),
113                                                  &temp_file));
114  ASSERT_EQ(static_cast<int>(file_redirect_contents.size()),
115            file_util::WriteFile(temp_file,
116                                 file_redirect_contents.data(),
117                                 file_redirect_contents.size()));
118
119  // Navigate to the file through the browser. The client redirect will appear
120  // as two page visits in the browser.
121  GURL first_url = net::FilePathToFileURL(temp_file);
122  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);
123
124  std::vector<GURL> redirects;
125  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
126  ASSERT_TRUE(tab_proxy.get());
127  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
128  ASSERT_EQ(1U, redirects.size());
129  EXPECT_EQ(final_url.spec(), redirects[0].spec());
130}
131
132// Tests to make sure a location change when a pending redirect exists isn't
133// flagged as a redirect.
134#if defined(OS_MACOSX)
135// SimulateOSClick is broken on the Mac: http://crbug.com/45162
136#define MAYBE_ClientCancelled DISABLED_ClientCancelled
137#elif defined(OS_WIN)
138// http://crbug.com/53091
139#define MAYBE_ClientCancelled FAILS_ClientCancelled
140#else
141#define MAYBE_ClientCancelled ClientCancelled
142#endif
143
144TEST_F(RedirectTest, MAYBE_ClientCancelled) {
145  FilePath first_path(test_data_directory_);
146  first_path = first_path.AppendASCII("cancelled_redirect_test.html");
147  ASSERT_TRUE(file_util::AbsolutePath(&first_path));
148  GURL first_url = net::FilePathToFileURL(first_path);
149
150  NavigateToURLBlockUntilNavigationsComplete(first_url, 1);
151
152  scoped_refptr<BrowserProxy> browser = automation()->GetBrowserWindow(0);
153  ASSERT_TRUE(browser.get());
154  scoped_refptr<WindowProxy> window = browser->GetWindow();
155  ASSERT_TRUE(window.get());
156  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
157  ASSERT_TRUE(tab_proxy.get());
158  int64 last_nav_time = 0;
159  EXPECT_TRUE(tab_proxy->GetLastNavigationTime(&last_nav_time));
160  // Simulate a click to force to make a user-initiated location change;
161  // otherwise, a non user-initiated in-page location change will be treated
162  // as client redirect and the redirect will be recoreded, which can cause
163  // this test failed.
164  gfx::Rect tab_view_bounds;
165  ASSERT_TRUE(browser->BringToFront());
166  ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER, &tab_view_bounds,
167                                    true));
168  ASSERT_TRUE(window->SimulateOSClick(tab_view_bounds.CenterPoint(),
169                                      ui::EF_LEFT_BUTTON_DOWN));
170  EXPECT_TRUE(tab_proxy->WaitForNavigation(last_nav_time));
171
172  std::vector<GURL> redirects;
173  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
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  GURL current_url;
180  ASSERT_TRUE(tab_proxy->GetCurrentURL(&current_url));
181
182  // Need to test final path and ref separately since constructing a file url
183  // containing an anchor using FilePathToFileURL will escape the anchor as
184  // %23, but in current_url the anchor will be '#'.
185  std::string final_ref = "myanchor";
186  FilePath current_path;
187  ASSERT_TRUE(net::FileURLToFilePath(current_url, &current_path));
188  ASSERT_TRUE(file_util::AbsolutePath(&current_path));
189  // Path should remain unchanged.
190  EXPECT_EQ(StringToLowerASCII(first_path.value()),
191            StringToLowerASCII(current_path.value()));
192  EXPECT_EQ(final_ref, current_url.ref());
193}
194
195// Tests a client->server->server redirect
196TEST_F(RedirectTest, ClientServerServer) {
197  ASSERT_TRUE(test_server_.Start());
198
199  GURL final_url = test_server_.GetURL(std::string());
200  GURL next_to_last = test_server_.GetURL(
201      "server-redirect?" + final_url.spec());
202  GURL second_url = test_server_.GetURL(
203      "server-redirect?" + next_to_last.spec());
204  GURL first_url = test_server_.GetURL(
205      "client-redirect?" + second_url.spec());
206  std::vector<GURL> redirects;
207
208  // We need the sleep for the client redirects, because it appears as two
209  // page visits in the browser.
210  NavigateToURL(first_url);
211
212  for (int i = 0; i < 10; ++i) {
213    base::PlatformThread::Sleep(TestTimeouts::action_timeout_ms());
214    scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
215    ASSERT_TRUE(tab_proxy.get());
216    ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
217    if (!redirects.empty())
218      break;
219  }
220
221  ASSERT_EQ(3U, redirects.size());
222  EXPECT_EQ(second_url.spec(), redirects[0].spec());
223  EXPECT_EQ(next_to_last.spec(), redirects[1].spec());
224  EXPECT_EQ(final_url.spec(), redirects[2].spec());
225}
226
227// Tests that the "#reference" gets preserved across server redirects.
228TEST_F(RedirectTest, ServerReference) {
229  ASSERT_TRUE(test_server_.Start());
230
231  const std::string ref("reference");
232
233  GURL final_url = test_server_.GetURL(std::string());
234  GURL initial_url = test_server_.GetURL(
235      "server-redirect?" + final_url.spec() + "#" + ref);
236
237  NavigateToURL(initial_url);
238
239  GURL url = GetActiveTabURL();
240  EXPECT_EQ(ref, url.ref());
241}
242
243// Test that redirect from http:// to file:// :
244// A) does not crash the browser or confuse the redirect chain, see bug 1080873
245// B) does not take place.
246//
247// Flaky on XP and Vista, http://crbug.com/69390.
248TEST_F(RedirectTest, FLAKY_NoHttpToFile) {
249  ASSERT_TRUE(test_server_.Start());
250  FilePath test_file(test_data_directory_);
251  test_file = test_file.AppendASCII("http_to_file.html");
252  GURL file_url = net::FilePathToFileURL(test_file);
253
254  GURL initial_url = test_server_.GetURL(
255      "client-redirect?" + file_url.spec());
256
257  NavigateToURL(initial_url);
258  // UITest will check for crashes. We make sure the title doesn't match the
259  // title from the file, because the nav should not have taken place.
260  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
261  ASSERT_TRUE(tab_proxy.get());
262  std::wstring actual_title;
263  ASSERT_TRUE(tab_proxy->GetTabTitle(&actual_title));
264  EXPECT_NE("File!", WideToUTF8(actual_title));
265}
266
267// Ensures that non-user initiated location changes (within page) are
268// flagged as client redirects. See bug 1139823.
269TEST_F(RedirectTest, ClientFragments) {
270  ASSERT_TRUE(test_server_.Start());
271
272  FilePath test_file(test_data_directory_);
273  test_file = test_file.AppendASCII("ref_redirect.html");
274  GURL first_url = net::FilePathToFileURL(test_file);
275  std::vector<GURL> redirects;
276
277  NavigateToURL(first_url);
278
279  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
280  ASSERT_TRUE(tab_proxy.get());
281  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
282  EXPECT_EQ(1U, redirects.size());
283  EXPECT_EQ(first_url.spec() + "#myanchor", redirects[0].spec());
284}
285
286// TODO(timsteele): This is disabled because our current testserver can't
287// handle multiple requests in parallel, making it hang on the first request
288// to /slow?60. It's unable to serve our second request for files/title2.html
289// until /slow? completes, which doesn't give the desired behavior. We could
290// alternatively load the second page from disk, but we would need to start
291// the browser for this testcase with --process-per-tab, and I don't think
292// we can do this at test-case-level granularity at the moment.
293// http://crbug.com/45056
294TEST_F(RedirectTest,
295       DISABLED_ClientCancelledByNewNavigationAfterProvisionalLoad) {
296  // We want to initiate a second navigation after the provisional load for
297  // the client redirect destination has started, but before this load is
298  // committed. To achieve this, we tell the browser to load a slow page,
299  // which causes it to start a provisional load, and while it is waiting
300  // for the response (which means it hasn't committed the load for the client
301  // redirect destination page yet), we issue a new navigation request.
302  ASSERT_TRUE(test_server_.Start());
303
304  GURL final_url = test_server_.GetURL("files/title2.html");
305  GURL slow = test_server_.GetURL("slow?60");
306  GURL first_url = test_server_.GetURL(
307      "client-redirect?" + slow.spec());
308  std::vector<GURL> redirects;
309
310  NavigateToURL(first_url);
311  // We don't sleep here - the first navigation won't have been committed yet
312  // because we told the server to wait a minute. This means the browser has
313  // started it's provisional load for the client redirect destination page but
314  // hasn't completed. Our time is now!
315  NavigateToURL(final_url);
316
317  std::wstring tab_title;
318  std::wstring final_url_title = UTF8ToWide("Title Of Awesomeness");
319  // Wait till the final page has been loaded.
320  for (int i = 0; i < 10; ++i) {
321    base::PlatformThread::Sleep(TestTimeouts::action_timeout_ms());
322    scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
323    ASSERT_TRUE(tab_proxy.get());
324    ASSERT_TRUE(tab_proxy->GetTabTitle(&tab_title));
325    if (tab_title == final_url_title) {
326      ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
327      break;
328    }
329  }
330
331  // Check to make sure the navigation did in fact take place and we are
332  // at the expected page.
333  EXPECT_EQ(final_url_title, tab_title);
334
335  bool final_navigation_not_redirect = true;
336  // Check to make sure our request for files/title2.html doesn't get flagged
337  // as a client redirect from the first (/client-redirect?) page.
338  for (std::vector<GURL>::iterator it = redirects.begin();
339       it != redirects.end(); ++it) {
340    if (final_url.spec() == it->spec()) {
341      final_navigation_not_redirect = false;
342      break;
343    }
344  }
345  EXPECT_TRUE(final_navigation_not_redirect);
346}
347
348}  // namespace
349