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