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#include "base/strings/string_util.h"
6#include "base/strings/stringprintf.h"
7#include "base/strings/utf_string_conversions.h"
8#include "content/public/browser/navigation_controller.h"
9#include "content/public/browser/notification_service.h"
10#include "content/public/browser/notification_types.h"
11#include "content/public/browser/web_contents.h"
12#include "content/public/common/url_constants.h"
13#include "content/public/test/browser_test_utils.h"
14#include "content/public/test/content_browser_test.h"
15#include "content/public/test/content_browser_test_utils.h"
16#include "content/public/test/test_utils.h"
17#include "content/shell/browser/shell.h"
18#include "net/test/embedded_test_server/embedded_test_server.h"
19#include "net/test/embedded_test_server/http_request.h"
20#include "net/test/embedded_test_server/http_response.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23namespace content {
24
25namespace {
26
27// Handles |request| by serving a response with title set to request contents.
28scoped_ptr<net::test_server::HttpResponse> HandleEchoTitleRequest(
29    const std::string& echotitle_path,
30    const net::test_server::HttpRequest& request) {
31  if (!StartsWithASCII(request.relative_url, echotitle_path, true))
32    return scoped_ptr<net::test_server::HttpResponse>();
33
34  scoped_ptr<net::test_server::BasicHttpResponse> http_response(
35      new net::test_server::BasicHttpResponse);
36  http_response->set_code(net::HTTP_OK);
37  http_response->set_content(
38      base::StringPrintf(
39          "<html><head><title>%s</title></head></html>",
40          request.content.c_str()));
41  return http_response.PassAs<net::test_server::HttpResponse>();
42}
43
44}  // namespace
45
46class SessionHistoryTest : public ContentBrowserTest {
47 protected:
48  SessionHistoryTest() {}
49
50  virtual void SetUpOnMainThread() OVERRIDE {
51    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
52    embedded_test_server()->RegisterRequestHandler(
53        base::Bind(&HandleEchoTitleRequest, "/echotitle"));
54
55    NavigateToURL(shell(), GURL(url::kAboutBlankURL));
56  }
57
58  // Simulate clicking a link.  Only works on the frames.html testserver page.
59  void ClickLink(std::string node_id) {
60    GURL url("javascript:clickLink('" + node_id + "')");
61    NavigateToURL(shell(), url);
62  }
63
64  // Simulate filling in form data.  Only works on the frames.html page with
65  // subframe = form.html, and on form.html itself.
66  void FillForm(std::string node_id, std::string value) {
67    GURL url("javascript:fillForm('" + node_id + "', '" + value + "')");
68    // This will return immediately, but since the JS executes synchronously
69    // on the renderer, it will complete before the next navigate message is
70    // processed.
71    NavigateToURL(shell(), url);
72  }
73
74  // Simulate submitting a form.  Only works on the frames.html page with
75  // subframe = form.html, and on form.html itself.
76  void SubmitForm(std::string node_id) {
77    GURL url("javascript:submitForm('" + node_id + "')");
78    NavigateToURL(shell(), url);
79  }
80
81  // Navigate session history using history.go(distance).
82  void JavascriptGo(std::string distance) {
83    GURL url("javascript:history.go('" + distance + "')");
84    NavigateToURL(shell(), url);
85  }
86
87  std::string GetTabTitle() {
88    return base::UTF16ToASCII(shell()->web_contents()->GetTitle());
89  }
90
91  GURL GetTabURL() {
92    return shell()->web_contents()->GetLastCommittedURL();
93  }
94
95  GURL GetURL(const std::string file) {
96    return embedded_test_server()->GetURL(
97        std::string("/session_history/") + file);
98  }
99
100  void NavigateAndCheckTitle(const char* filename,
101                             const std::string& expected_title) {
102    base::string16 expected_title16(base::ASCIIToUTF16(expected_title));
103    TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
104    NavigateToURL(shell(), GetURL(filename));
105    ASSERT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
106  }
107
108  bool CanGoBack() {
109    return shell()->web_contents()->GetController().CanGoBack();
110  }
111
112  bool CanGoForward() {
113    return shell()->web_contents()->GetController().CanGoForward();
114  }
115
116  void GoBack() {
117    WindowedNotificationObserver load_stop_observer(
118        NOTIFICATION_LOAD_STOP,
119        NotificationService::AllSources());
120    shell()->web_contents()->GetController().GoBack();
121    load_stop_observer.Wait();
122  }
123
124  void GoForward() {
125    WindowedNotificationObserver load_stop_observer(
126        NOTIFICATION_LOAD_STOP,
127        NotificationService::AllSources());
128    shell()->web_contents()->GetController().GoForward();
129    load_stop_observer.Wait();
130  }
131};
132
133// If this flakes, use http://crbug.com/61619 on windows and
134// http://crbug.com/102094 on mac.
135IN_PROC_BROWSER_TEST_F(SessionHistoryTest, BasicBackForward) {
136  ASSERT_FALSE(CanGoBack());
137
138  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot1.html", "bot1"));
139  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2"));
140  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
141
142  // history is [blank, bot1, bot2, *bot3]
143
144  GoBack();
145  EXPECT_EQ("bot2", GetTabTitle());
146
147  GoBack();
148  EXPECT_EQ("bot1", GetTabTitle());
149
150  GoForward();
151  EXPECT_EQ("bot2", GetTabTitle());
152
153  GoBack();
154  EXPECT_EQ("bot1", GetTabTitle());
155
156  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
157
158  // history is [blank, bot1, *bot3]
159
160  ASSERT_FALSE(CanGoForward());
161  EXPECT_EQ("bot3", GetTabTitle());
162
163  GoBack();
164  EXPECT_EQ("bot1", GetTabTitle());
165
166  GoBack();
167  EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
168
169  ASSERT_FALSE(CanGoBack());
170  EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
171
172  GoForward();
173  EXPECT_EQ("bot1", GetTabTitle());
174
175  GoForward();
176  EXPECT_EQ("bot3", GetTabTitle());
177}
178
179// Test that back/forward works when navigating in subframes.
180// If this flakes, use http://crbug.com/48833
181IN_PROC_BROWSER_TEST_F(SessionHistoryTest, FrameBackForward) {
182  ASSERT_FALSE(CanGoBack());
183
184  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1"));
185
186  ClickLink("abot2");
187  EXPECT_EQ("bot2", GetTabTitle());
188  GURL frames(GetURL("frames.html"));
189  EXPECT_EQ(frames, GetTabURL());
190
191  ClickLink("abot3");
192  EXPECT_EQ("bot3", GetTabTitle());
193  EXPECT_EQ(frames, GetTabURL());
194
195  // history is [blank, bot1, bot2, *bot3]
196
197  GoBack();
198  EXPECT_EQ("bot2", GetTabTitle());
199  EXPECT_EQ(frames, GetTabURL());
200
201  GoBack();
202  EXPECT_EQ("bot1", GetTabTitle());
203  EXPECT_EQ(frames, GetTabURL());
204
205  GoBack();
206  EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
207  EXPECT_EQ(GURL(url::kAboutBlankURL), GetTabURL());
208
209  GoForward();
210  EXPECT_EQ("bot1", GetTabTitle());
211  EXPECT_EQ(frames, GetTabURL());
212
213  GoForward();
214  EXPECT_EQ("bot2", GetTabTitle());
215  EXPECT_EQ(frames, GetTabURL());
216
217  ClickLink("abot1");
218  EXPECT_EQ("bot1", GetTabTitle());
219  EXPECT_EQ(frames, GetTabURL());
220
221  // history is [blank, bot1, bot2, *bot1]
222
223  ASSERT_FALSE(CanGoForward());
224  EXPECT_EQ("bot1", GetTabTitle());
225  EXPECT_EQ(frames, GetTabURL());
226
227  GoBack();
228  EXPECT_EQ("bot2", GetTabTitle());
229  EXPECT_EQ(frames, GetTabURL());
230
231  GoBack();
232  EXPECT_EQ("bot1", GetTabTitle());
233  EXPECT_EQ(frames, GetTabURL());
234}
235
236// Test that back/forward preserves POST data and document state in subframes.
237// If this flakes use http://crbug.com/61619
238IN_PROC_BROWSER_TEST_F(SessionHistoryTest, FrameFormBackForward) {
239  ASSERT_FALSE(CanGoBack());
240
241  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1"));
242
243  ClickLink("aform");
244  EXPECT_EQ("form", GetTabTitle());
245  GURL frames(GetURL("frames.html"));
246  EXPECT_EQ(frames, GetTabURL());
247
248  SubmitForm("isubmit");
249  EXPECT_EQ("text=&select=a", GetTabTitle());
250  EXPECT_EQ(frames, GetTabURL());
251
252  GoBack();
253  EXPECT_EQ("form", GetTabTitle());
254  EXPECT_EQ(frames, GetTabURL());
255
256  // history is [blank, bot1, *form, post]
257
258  ClickLink("abot2");
259  EXPECT_EQ("bot2", GetTabTitle());
260  EXPECT_EQ(frames, GetTabURL());
261
262  // history is [blank, bot1, form, *bot2]
263
264  GoBack();
265  EXPECT_EQ("form", GetTabTitle());
266  EXPECT_EQ(frames, GetTabURL());
267
268  SubmitForm("isubmit");
269  EXPECT_EQ("text=&select=a", GetTabTitle());
270  EXPECT_EQ(frames, GetTabURL());
271
272  // history is [blank, bot1, form, *post]
273
274  // TODO(mpcomplete): reenable this when WebKit bug 10199 is fixed:
275  // "returning to a POST result within a frame does a GET instead of a POST"
276  ClickLink("abot2");
277  EXPECT_EQ("bot2", GetTabTitle());
278  EXPECT_EQ(frames, GetTabURL());
279
280  GoBack();
281  EXPECT_EQ("text=&select=a", GetTabTitle());
282  EXPECT_EQ(frames, GetTabURL());
283}
284
285// TODO(mpcomplete): enable this when Bug 734372 is fixed:
286// "Doing a session history navigation does not restore newly-created subframe
287// document state"
288// Test that back/forward preserves POST data and document state when navigating
289// across frames (ie, from frame -> nonframe).
290// Hangs, see http://crbug.com/45058.
291IN_PROC_BROWSER_TEST_F(SessionHistoryTest, CrossFrameFormBackForward) {
292  ASSERT_FALSE(CanGoBack());
293
294  GURL frames(GetURL("frames.html"));
295  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("frames.html", "bot1"));
296
297  ClickLink("aform");
298  EXPECT_EQ("form", GetTabTitle());
299  EXPECT_EQ(frames, GetTabURL());
300
301  SubmitForm("isubmit");
302  EXPECT_EQ("text=&select=a", GetTabTitle());
303  EXPECT_EQ(frames, GetTabURL());
304
305  GoBack();
306  EXPECT_EQ("form", GetTabTitle());
307  EXPECT_EQ(frames, GetTabURL());
308
309  // history is [blank, bot1, *form, post]
310
311  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2"));
312
313  // history is [blank, bot1, form, *bot2]
314
315  GoBack();
316  EXPECT_EQ("bot1", GetTabTitle());
317  EXPECT_EQ(frames, GetTabURL());
318
319  SubmitForm("isubmit");
320  EXPECT_EQ("text=&select=a", GetTabTitle());
321  EXPECT_EQ(frames, GetTabURL());
322}
323
324// Test that back/forward entries are created for reference fragment
325// navigations. Bug 730379.
326// If this flakes use http://crbug.com/61619.
327IN_PROC_BROWSER_TEST_F(SessionHistoryTest, FragmentBackForward) {
328  embedded_test_server()->RegisterRequestHandler(
329      base::Bind(&HandleEchoTitleRequest, "/echotitle"));
330
331  ASSERT_FALSE(CanGoBack());
332
333  GURL fragment(GetURL("fragment.html"));
334  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html", "fragment"));
335
336  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#a", "fragment"));
337  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#b", "fragment"));
338  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("fragment.html#c", "fragment"));
339
340  // history is [blank, fragment, fragment#a, fragment#b, *fragment#c]
341
342  GoBack();
343  EXPECT_EQ(GetURL("fragment.html#b"), GetTabURL());
344
345  GoBack();
346  EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL());
347
348  GoBack();
349  EXPECT_EQ(GetURL("fragment.html"), GetTabURL());
350
351  GoForward();
352  EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL());
353
354  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
355
356  // history is [blank, fragment, fragment#a, bot3]
357
358  ASSERT_FALSE(CanGoForward());
359  EXPECT_EQ(GetURL("bot3.html"), GetTabURL());
360
361  GoBack();
362  EXPECT_EQ(GetURL("fragment.html#a"), GetTabURL());
363
364  GoBack();
365  EXPECT_EQ(GetURL("fragment.html"), GetTabURL());
366}
367
368// Test that the javascript window.history object works.
369// NOTE: history.go(N) does not do anything if N is outside the bounds of the
370// back/forward list (such as trigger our start/stop loading events).  This
371// means the test will hang if it attempts to navigate too far forward or back,
372// since we'll be waiting forever for a load stop event.
373//
374// TODO(brettw) bug 50648: fix flakyness. This test seems like it was failing
375// about 1/4 of the time on Vista by failing to execute JavascriptGo (see bug).
376IN_PROC_BROWSER_TEST_F(SessionHistoryTest, JavascriptHistory) {
377  ASSERT_FALSE(CanGoBack());
378
379  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot1.html", "bot1"));
380  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot2.html", "bot2"));
381  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
382
383  // history is [blank, bot1, bot2, *bot3]
384
385  JavascriptGo("-1");
386  EXPECT_EQ("bot2", GetTabTitle());
387
388  JavascriptGo("-1");
389  EXPECT_EQ("bot1", GetTabTitle());
390
391  JavascriptGo("1");
392  EXPECT_EQ("bot2", GetTabTitle());
393
394  JavascriptGo("-1");
395  EXPECT_EQ("bot1", GetTabTitle());
396
397  JavascriptGo("2");
398  EXPECT_EQ("bot3", GetTabTitle());
399
400  // history is [blank, bot1, bot2, *bot3]
401
402  JavascriptGo("-3");
403  EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
404
405  ASSERT_FALSE(CanGoBack());
406  EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
407
408  JavascriptGo("1");
409  EXPECT_EQ("bot1", GetTabTitle());
410
411  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle("bot3.html", "bot3"));
412
413  // history is [blank, bot1, *bot3]
414
415  ASSERT_FALSE(CanGoForward());
416  EXPECT_EQ("bot3", GetTabTitle());
417
418  JavascriptGo("-1");
419  EXPECT_EQ("bot1", GetTabTitle());
420
421  JavascriptGo("-1");
422  EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
423
424  ASSERT_FALSE(CanGoBack());
425  EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
426
427  JavascriptGo("1");
428  EXPECT_EQ("bot1", GetTabTitle());
429
430  JavascriptGo("1");
431  EXPECT_EQ("bot3", GetTabTitle());
432
433  // TODO(creis): Test that JavaScript history navigations work across tab
434  // types.  For example, load about:network in a tab, then a real page, then
435  // try to go back and forward with JavaScript.  Bug 1136715.
436  // (Hard to test right now, because pages like about:network cause the
437  // TabProxy to hang.  This is because they do not appear to use the
438  // NotificationService.)
439}
440
441// This test is failing consistently. See http://crbug.com/22560
442IN_PROC_BROWSER_TEST_F(SessionHistoryTest, LocationReplace) {
443  // Test that using location.replace doesn't leave the title of the old page
444  // visible.
445  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle(
446      "replace.html?bot1.html", "bot1"));
447}
448
449IN_PROC_BROWSER_TEST_F(SessionHistoryTest, LocationChangeInSubframe) {
450  ASSERT_NO_FATAL_FAILURE(NavigateAndCheckTitle(
451      "location_redirect.html", "Default Title"));
452
453  NavigateToURL(shell(), GURL("javascript:void(frames[0].navigate())"));
454  EXPECT_EQ("foo", GetTabTitle());
455
456  GoBack();
457  EXPECT_EQ("Default Title", GetTabTitle());
458}
459
460// http://code.google.com/p/chromium/issues/detail?id=56267
461IN_PROC_BROWSER_TEST_F(SessionHistoryTest, HistoryLength) {
462  int length;
463  ASSERT_TRUE(ExecuteScriptAndExtractInt(
464      shell()->web_contents(),
465      "domAutomationController.send(history.length)",
466      &length));
467  EXPECT_EQ(1, length);
468
469  NavigateToURL(shell(), GetURL("title1.html"));
470
471  ASSERT_TRUE(ExecuteScriptAndExtractInt(
472      shell()->web_contents(),
473      "domAutomationController.send(history.length)",
474      &length));
475  EXPECT_EQ(2, length);
476
477  // Now test that history.length is updated when the navigation is committed.
478  NavigateToURL(shell(), GetURL("record_length.html"));
479
480  ASSERT_TRUE(ExecuteScriptAndExtractInt(
481      shell()->web_contents(),
482      "domAutomationController.send(history.length)",
483      &length));
484  EXPECT_EQ(3, length);
485
486  GoBack();
487  GoBack();
488
489  // Ensure history.length is properly truncated.
490  NavigateToURL(shell(), GetURL("title2.html"));
491
492  ASSERT_TRUE(ExecuteScriptAndExtractInt(
493      shell()->web_contents(),
494      "domAutomationController.send(history.length)",
495      &length));
496  EXPECT_EQ(2, length);
497}
498
499}  // namespace content
500