Home | History | Annotate | Download | only in browser
      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/test_utils.h"
     15 #include "content/shell/shell.h"
     16 #include "content/test/content_browser_test.h"
     17 #include "content/test/content_browser_test_utils.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 
     23 namespace content {
     24 
     25 namespace {
     26 
     27 // Handles |request| by serving a response with title set to request contents.
     28 scoped_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 
     46 class 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(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 UTF16ToASCII(shell()->web_contents()->GetTitle());
     89   }
     90 
     91   GURL GetTabURL() {
     92     return shell()->web_contents()->GetURL();
     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     string16 expected_title16(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.
    135 IN_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(kAboutBlankURL), GetTabTitle());
    168 
    169   ASSERT_FALSE(CanGoBack());
    170   EXPECT_EQ(std::string(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
    181 IN_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(kAboutBlankURL), GetTabTitle());
    207   EXPECT_EQ(GURL(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
    238 IN_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.
    291 IN_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.
    327 IN_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).
    376 IN_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(kAboutBlankURL), GetTabTitle());
    404 
    405   ASSERT_FALSE(CanGoBack());
    406   EXPECT_EQ(std::string(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(kAboutBlankURL), GetTabTitle());
    423 
    424   ASSERT_FALSE(CanGoBack());
    425   EXPECT_EQ(std::string(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
    442 IN_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 
    449 IN_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
    461 IN_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