File: redirect_browsertest.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (315 lines) | stat: -rw-r--r-- 13,008 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Navigates the browser to server and client redirect pages and makes sure
// that the correct redirects are reflected in the history database. Errors
// here might indicate that WebKit changed the calls our glue layer gets in
// the case of redirects. It may also mean problems with the history system.

#include <memory>
#include <string>

#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/view_ids.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/history/core/browser/history_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/base/filename_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/events/event_constants.h"

class RedirectTest : public InProcessBrowserTest {
 public:
  RedirectTest() = default;

  std::vector<GURL> GetRedirects(const GURL& url) {
    history::HistoryService* history_service =
        HistoryServiceFactory::GetForProfile(
            browser()->profile(), ServiceAccessType::EXPLICIT_ACCESS);

    // Schedule a history query for redirects. The response will be sent
    // asynchronously from the callback the history system uses to notify us
    // that it's done: OnRedirectQueryComplete.
    std::vector<GURL> rv;
    base::RunLoop loop(base::RunLoop::Type::kNestableTasksAllowed);
    history_service->QueryRedirectsFrom(
        url,
        base::BindOnce(&RedirectTest::OnRedirectQueryComplete,
                       base::Unretained(this), &rv, loop.QuitWhenIdleClosure()),
        &tracker_);
    loop.Run();
    return rv;
  }

 protected:
  void OnRedirectQueryComplete(std::vector<GURL>* rv,
                               base::OnceClosure quit_closure,
                               history::RedirectList redirects) {
    rv->insert(rv->end(), redirects.begin(), redirects.end());
    std::move(quit_closure).Run();
  }

  // Tracker for asynchronous history queries.
  base::CancelableTaskTracker tracker_;
};

// Tests a single server redirect
IN_PROC_BROWSER_TEST_F(RedirectTest, Server) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL final_url = embedded_test_server()->GetURL("/defaultresponse");
  GURL first_url =
      embedded_test_server()->GetURL("/server-redirect?" + final_url.spec());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), first_url));

  std::vector<GURL> redirects = GetRedirects(first_url);

  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());
}

// Tests a single client redirect.
IN_PROC_BROWSER_TEST_F(RedirectTest, Client) {
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL final_url = embedded_test_server()->GetURL("/defaultresponse");
  GURL first_url =
      embedded_test_server()->GetURL("/client-redirect?" + final_url.spec());

  // The client redirect appears as two page visits in the browser.
  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), first_url, 2);

  std::vector<GURL> redirects = GetRedirects(first_url);

  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());

  // The address bar should display the final URL.
  EXPECT_EQ(final_url, browser()
                           ->tab_strip_model()
                           ->GetActiveWebContents()
                           ->GetLastCommittedURL());

  // Navigate one more time.
  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), first_url, 2);

  // The address bar should still display the final URL.
  EXPECT_EQ(final_url, browser()
                           ->tab_strip_model()
                           ->GetActiveWebContents()
                           ->GetLastCommittedURL());
}

// http://code.google.com/p/chromium/issues/detail?id=62772
IN_PROC_BROWSER_TEST_F(RedirectTest, ClientEmptyReferer) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Create the file contents, which will do a redirect to the
  // test server.
  GURL final_url = embedded_test_server()->GetURL("/defaultresponse");
  ASSERT_TRUE(final_url.is_valid());
  std::string file_redirect_contents = base::StringPrintf(
      "<html>"
      "<head></head>"
      "<body onload=\"document.location='%s'\"></body>"
      "</html>",
      final_url.spec().c_str());

  // Write the contents to a temporary file.
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::ScopedTempDir temp_directory;
  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
  base::FilePath temp_file = temp_directory.GetPath().AppendASCII("foo.html");
  ASSERT_TRUE(base::WriteFile(temp_file, file_redirect_contents));

  // Navigate to the file through the browser.
  GURL first_url = net::FilePathToFileURL(temp_file);
  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(),
                                                            first_url, 1);

  std::vector<GURL> redirects = GetRedirects(first_url);
  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());
}

// Tests to make sure a location change when a pending redirect exists isn't
// flagged as a redirect.
IN_PROC_BROWSER_TEST_F(RedirectTest, ClientCancelled) {
  GURL first_url = ui_test_utils::GetTestUrl(
      base::FilePath(),
      base::FilePath().AppendASCII("cancelled_redirect_test.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), first_url));

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  content::TestNavigationObserver navigation_observer(web_contents);

  // Simulate a click to force to make a user-initiated location change;
  // otherwise, a non user-initiated in-page location change will be treated
  // as client redirect and the redirect will be recoreded, which can cause
  // this test failed.
  content::SimulateMouseClick(web_contents, 0,
                              blink::WebMouseEvent::Button::kLeft);
  navigation_observer.Wait();

  std::vector<GURL> redirects = GetRedirects(first_url);

  // There should be no redirect from first_url, because the anchor location
  // change is not considered as client redirect and the meta-refresh
  // won't have fired yet.
  ASSERT_EQ(0U, redirects.size());
  EXPECT_EQ("myanchor", web_contents->GetLastCommittedURL().ref());
}

// Tests a client->server->server redirect
IN_PROC_BROWSER_TEST_F(RedirectTest, ClientServerServer) {
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL final_url = embedded_test_server()->GetURL("/defaultresponse");
  GURL next_to_last =
      embedded_test_server()->GetURL("/server-redirect?" + final_url.spec());
  GURL second_url =
      embedded_test_server()->GetURL("/server-redirect?" + next_to_last.spec());
  GURL first_url =
      embedded_test_server()->GetURL("/client-redirect?" + second_url.spec());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), first_url, 2);

  std::vector<GURL> redirects = GetRedirects(first_url);
  ASSERT_EQ(3U, redirects.size());
  EXPECT_EQ(second_url.spec(), redirects[0].spec());
  EXPECT_EQ(next_to_last.spec(), redirects[1].spec());
  EXPECT_EQ(final_url.spec(), redirects[2].spec());
}

// Tests that the "#reference" gets preserved across server redirects.
IN_PROC_BROWSER_TEST_F(RedirectTest, ServerReference) {
  ASSERT_TRUE(embedded_test_server()->Start());

  const std::string ref("reference");

  GURL final_url = embedded_test_server()->GetURL("/defaultresponse");
  GURL initial_url = embedded_test_server()->GetURL(
      "/server-redirect?" + final_url.spec() + "#" + ref);

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));

  EXPECT_EQ(ref, browser()
                     ->tab_strip_model()
                     ->GetActiveWebContents()
                     ->GetLastCommittedURL()
                     .ref());
}

// Test that redirect from http:// to file:// :
// A) does not crash the browser or confuse the redirect chain, see bug 1080873
// B) does not take place.
//
// Flaky on XP and Vista, http://crbug.com/69390.
IN_PROC_BROWSER_TEST_F(RedirectTest, NoHttpToFile) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL file_url = ui_test_utils::GetTestUrl(
      base::FilePath(), base::FilePath().AppendASCII("http_to_file.html"));

  GURL initial_url =
      embedded_test_server()->GetURL("/client-redirect?" + file_url.spec());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
  // We make sure the title doesn't match the title from the file, because the
  // nav should not have taken place.
  EXPECT_NE(u"File!",
            browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
}

// Ensures that non-user initiated location changes (within page) are
// flagged as client redirects. See bug 1139823.
IN_PROC_BROWSER_TEST_F(RedirectTest, ClientFragments) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL first_url = ui_test_utils::GetTestUrl(
      base::FilePath(), base::FilePath().AppendASCII("ref_redirect.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), first_url));
  std::vector<GURL> redirects = GetRedirects(first_url);
  EXPECT_EQ(1U, redirects.size());
  EXPECT_EQ(first_url.spec() + "#myanchor", redirects[0].spec());
}

// TODO(timsteele): This is disabled because our current testserver can't
// handle multiple requests in parallel, making it hang on the first request
// to /slow?60. It's unable to serve our second request for /title2.html
// until /slow? completes, which doesn't give the desired behavior. We could
// alternatively load the second page from disk, but we would need to start
// the browser for this testcase with --process-per-tab, and I don't think
// we can do this at test-case-level granularity at the moment.
// http://crbug.com/45056
IN_PROC_BROWSER_TEST_F(RedirectTest,
       DISABLED_ClientCancelledByNewNavigationAfterProvisionalLoad) {
  // We want to initiate a second navigation after the provisional load for
  // the client redirect destination has started, but before this load is
  // committed. To achieve this, we tell the browser to load a slow page,
  // which causes it to start a provisional load, and while it is waiting
  // for the response (which means it hasn't committed the load for the client
  // redirect destination page yet), we issue a new navigation request.
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL final_url = embedded_test_server()->GetURL("/title2.html");
  GURL slow = embedded_test_server()->GetURL("/slow?60");
  GURL first_url =
      embedded_test_server()->GetURL("/client-redirect?" + slow.spec());

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  content::TestNavigationObserver observer(web_contents, 2);

  ui_test_utils::NavigateToURLWithDisposition(
      browser(), first_url, WindowOpenDisposition::CURRENT_TAB,
      ui_test_utils::BROWSER_TEST_NO_WAIT);
  // We don't sleep here - the first navigation won't have been committed yet
  // because we told the server to wait a minute. This means the browser has
  // started it's provisional load for the client redirect destination page but
  // hasn't completed. Our time is now!
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), final_url, WindowOpenDisposition::CURRENT_TAB,
      ui_test_utils::BROWSER_TEST_NO_WAIT);
  observer.Wait();

  // Check to make sure the navigation did in fact take place and we are
  // at the expected page.
  EXPECT_EQ(u"Title Of Awesomeness",
            browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());

  bool final_navigation_not_redirect = true;
  std::vector<GURL> redirects = GetRedirects(first_url);
  // Check to make sure our request for /title2.html doesn't get flagged
  // as a client redirect from the first (/client-redirect?) page.
  for (auto it = redirects.begin(); it != redirects.end(); ++it) {
    if (final_url.spec() == it->spec()) {
      final_navigation_not_redirect = false;
      break;
    }
  }
  EXPECT_TRUE(final_navigation_not_redirect);
}