Home | History | Annotate | Download | only in toolbar
      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 "chrome/browser/ui/toolbar/back_forward_menu_model.h"
      6 
      7 #include "base/path_service.h"
      8 #include "base/strings/string16.h"
      9 #include "base/strings/string_util.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "base/time/time.h"
     12 #include "chrome/browser/favicon/favicon_service_factory.h"
     13 #include "chrome/browser/history/history_service.h"
     14 #include "chrome/browser/history/history_service_factory.h"
     15 #include "chrome/browser/profiles/profile_manager.h"
     16 #include "chrome/browser/ui/browser.h"
     17 #include "chrome/browser/ui/browser_tabstrip.h"
     18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     19 #include "chrome/common/url_constants.h"
     20 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
     21 #include "chrome/test/base/test_browser_window.h"
     22 #include "chrome/test/base/testing_profile.h"
     23 #include "content/public/browser/navigation_controller.h"
     24 #include "content/public/browser/navigation_entry.h"
     25 #include "content/public/browser/web_contents.h"
     26 #include "content/public/test/web_contents_tester.h"
     27 #include "testing/gtest/include/gtest/gtest.h"
     28 #include "third_party/skia/include/core/SkBitmap.h"
     29 #include "ui/gfx/codec/png_codec.h"
     30 
     31 using base::ASCIIToUTF16;
     32 using content::WebContentsTester;
     33 
     34 namespace {
     35 
     36 // Creates a bitmap of the specified color.
     37 SkBitmap CreateBitmap(SkColor color) {
     38   SkBitmap bitmap;
     39   bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
     40   bitmap.allocPixels();
     41   bitmap.eraseColor(color);
     42   return bitmap;
     43 }
     44 
     45 class FaviconDelegate : public ui::MenuModelDelegate {
     46  public:
     47   FaviconDelegate() : was_called_(false) {}
     48 
     49   virtual void OnIconChanged(int model_index) OVERRIDE {
     50     was_called_ = true;
     51     base::MessageLoop::current()->Quit();
     52   }
     53 
     54   bool was_called() const { return was_called_; }
     55 
     56  private:
     57   bool was_called_;
     58 
     59   DISALLOW_COPY_AND_ASSIGN(FaviconDelegate);
     60 };
     61 
     62 }  // namespace
     63 
     64 class BackFwdMenuModelTest : public ChromeRenderViewHostTestHarness {
     65  public:
     66   void ValidateModel(BackForwardMenuModel* model, int history_items,
     67                      int chapter_stops) {
     68     int h = std::min(BackForwardMenuModel::kMaxHistoryItems, history_items);
     69     int c = std::min(BackForwardMenuModel::kMaxChapterStops, chapter_stops);
     70     EXPECT_EQ(h, model->GetHistoryItemCount());
     71     EXPECT_EQ(c, model->GetChapterStopCount(h));
     72     if (h > 0)
     73       h += 2;  // Separator and View History link.
     74     if (c > 0)
     75       ++c;
     76     EXPECT_EQ(h + c, model->GetItemCount());
     77   }
     78 
     79   void LoadURLAndUpdateState(const char* url, const char* title) {
     80     NavigateAndCommit(GURL(url));
     81     controller().GetLastCommittedEntry()->SetTitle(base::UTF8ToUTF16(title));
     82   }
     83 
     84   // Navigate back or forward the given amount and commits the entry (which
     85   // will be pending after we ask to navigate there).
     86   void NavigateToOffset(int offset) {
     87     controller().GoToOffset(offset);
     88     WebContentsTester::For(web_contents())->CommitPendingNavigation();
     89   }
     90 
     91   // Same as NavigateToOffset but goes to an absolute index.
     92   void NavigateToIndex(int index) {
     93     controller().GoToIndex(index);
     94     WebContentsTester::For(web_contents())->CommitPendingNavigation();
     95   }
     96 
     97   // Goes back/forward and commits the load.
     98   void GoBack() {
     99     controller().GoBack();
    100     WebContentsTester::For(web_contents())->CommitPendingNavigation();
    101   }
    102   void GoForward() {
    103     controller().GoForward();
    104     WebContentsTester::For(web_contents())->CommitPendingNavigation();
    105   }
    106 };
    107 
    108 TEST_F(BackFwdMenuModelTest, BasicCase) {
    109   scoped_ptr<BackForwardMenuModel> back_model(new BackForwardMenuModel(
    110       NULL, BackForwardMenuModel::BACKWARD_MENU));
    111   back_model->set_test_web_contents(web_contents());
    112 
    113   scoped_ptr<BackForwardMenuModel> forward_model(new BackForwardMenuModel(
    114       NULL, BackForwardMenuModel::FORWARD_MENU));
    115   forward_model->set_test_web_contents(web_contents());
    116 
    117   EXPECT_EQ(0, back_model->GetItemCount());
    118   EXPECT_EQ(0, forward_model->GetItemCount());
    119   EXPECT_FALSE(back_model->ItemHasCommand(1));
    120 
    121   // Seed the controller with a few URLs
    122   LoadURLAndUpdateState("http://www.a.com/1", "A1");
    123   LoadURLAndUpdateState("http://www.a.com/2", "A2");
    124   LoadURLAndUpdateState("http://www.a.com/3", "A3");
    125   LoadURLAndUpdateState("http://www.b.com/1", "B1");
    126   LoadURLAndUpdateState("http://www.b.com/2", "B2");
    127   LoadURLAndUpdateState("http://www.c.com/1", "C1");
    128   LoadURLAndUpdateState("http://www.c.com/2", "C2");
    129   LoadURLAndUpdateState("http://www.c.com/3", "C3");
    130 
    131   // There're two more items here: a separator and a "Show Full History".
    132   EXPECT_EQ(9, back_model->GetItemCount());
    133   EXPECT_EQ(0, forward_model->GetItemCount());
    134   EXPECT_EQ(ASCIIToUTF16("C2"), back_model->GetLabelAt(0));
    135   EXPECT_EQ(ASCIIToUTF16("A1"), back_model->GetLabelAt(6));
    136   EXPECT_EQ(back_model->GetShowFullHistoryLabel(),
    137             back_model->GetLabelAt(8));
    138 
    139   EXPECT_TRUE(back_model->ItemHasCommand(0));
    140   EXPECT_TRUE(back_model->ItemHasCommand(6));
    141   EXPECT_TRUE(back_model->IsSeparator(7));
    142   EXPECT_TRUE(back_model->ItemHasCommand(8));
    143   EXPECT_FALSE(back_model->ItemHasCommand(9));
    144   EXPECT_FALSE(back_model->ItemHasCommand(9));
    145 
    146   NavigateToOffset(-7);
    147 
    148   EXPECT_EQ(0, back_model->GetItemCount());
    149   EXPECT_EQ(9, forward_model->GetItemCount());
    150   EXPECT_EQ(ASCIIToUTF16("A2"), forward_model->GetLabelAt(0));
    151   EXPECT_EQ(ASCIIToUTF16("C3"), forward_model->GetLabelAt(6));
    152   EXPECT_EQ(forward_model->GetShowFullHistoryLabel(),
    153             forward_model->GetLabelAt(8));
    154 
    155   EXPECT_TRUE(forward_model->ItemHasCommand(0));
    156   EXPECT_TRUE(forward_model->ItemHasCommand(6));
    157   EXPECT_TRUE(forward_model->IsSeparator(7));
    158   EXPECT_TRUE(forward_model->ItemHasCommand(8));
    159   EXPECT_FALSE(forward_model->ItemHasCommand(7));
    160   EXPECT_FALSE(forward_model->ItemHasCommand(9));
    161 
    162   NavigateToOffset(4);
    163 
    164   EXPECT_EQ(6, back_model->GetItemCount());
    165   EXPECT_EQ(5, forward_model->GetItemCount());
    166   EXPECT_EQ(ASCIIToUTF16("B1"), back_model->GetLabelAt(0));
    167   EXPECT_EQ(ASCIIToUTF16("A1"), back_model->GetLabelAt(3));
    168   EXPECT_EQ(back_model->GetShowFullHistoryLabel(),
    169             back_model->GetLabelAt(5));
    170   EXPECT_EQ(ASCIIToUTF16("C1"), forward_model->GetLabelAt(0));
    171   EXPECT_EQ(ASCIIToUTF16("C3"), forward_model->GetLabelAt(2));
    172   EXPECT_EQ(forward_model->GetShowFullHistoryLabel(),
    173             forward_model->GetLabelAt(4));
    174 }
    175 
    176 TEST_F(BackFwdMenuModelTest, MaxItemsTest) {
    177   scoped_ptr<BackForwardMenuModel> back_model(new BackForwardMenuModel(
    178       NULL, BackForwardMenuModel::BACKWARD_MENU));
    179   back_model->set_test_web_contents(web_contents());
    180 
    181   scoped_ptr<BackForwardMenuModel> forward_model(new BackForwardMenuModel(
    182       NULL, BackForwardMenuModel::FORWARD_MENU));
    183   forward_model->set_test_web_contents(web_contents());
    184 
    185   // Seed the controller with 32 URLs
    186   LoadURLAndUpdateState("http://www.a.com/1", "A1");
    187   LoadURLAndUpdateState("http://www.a.com/2", "A2");
    188   LoadURLAndUpdateState("http://www.a.com/3", "A3");
    189   LoadURLAndUpdateState("http://www.b.com/1", "B1");
    190   LoadURLAndUpdateState("http://www.b.com/2", "B2");
    191   LoadURLAndUpdateState("http://www.b.com/3", "B3");
    192   LoadURLAndUpdateState("http://www.c.com/1", "C1");
    193   LoadURLAndUpdateState("http://www.c.com/2", "C2");
    194   LoadURLAndUpdateState("http://www.c.com/3", "C3");
    195   LoadURLAndUpdateState("http://www.d.com/1", "D1");
    196   LoadURLAndUpdateState("http://www.d.com/2", "D2");
    197   LoadURLAndUpdateState("http://www.d.com/3", "D3");
    198   LoadURLAndUpdateState("http://www.e.com/1", "E1");
    199   LoadURLAndUpdateState("http://www.e.com/2", "E2");
    200   LoadURLAndUpdateState("http://www.e.com/3", "E3");
    201   LoadURLAndUpdateState("http://www.f.com/1", "F1");
    202   LoadURLAndUpdateState("http://www.f.com/2", "F2");
    203   LoadURLAndUpdateState("http://www.f.com/3", "F3");
    204   LoadURLAndUpdateState("http://www.g.com/1", "G1");
    205   LoadURLAndUpdateState("http://www.g.com/2", "G2");
    206   LoadURLAndUpdateState("http://www.g.com/3", "G3");
    207   LoadURLAndUpdateState("http://www.h.com/1", "H1");
    208   LoadURLAndUpdateState("http://www.h.com/2", "H2");
    209   LoadURLAndUpdateState("http://www.h.com/3", "H3");
    210   LoadURLAndUpdateState("http://www.i.com/1", "I1");
    211   LoadURLAndUpdateState("http://www.i.com/2", "I2");
    212   LoadURLAndUpdateState("http://www.i.com/3", "I3");
    213   LoadURLAndUpdateState("http://www.j.com/1", "J1");
    214   LoadURLAndUpdateState("http://www.j.com/2", "J2");
    215   LoadURLAndUpdateState("http://www.j.com/3", "J3");
    216   LoadURLAndUpdateState("http://www.k.com/1", "K1");
    217   LoadURLAndUpdateState("http://www.k.com/2", "K2");
    218 
    219   // Also there're two more for a separator and a "Show Full History".
    220   int chapter_stop_offset = 6;
    221   EXPECT_EQ(BackForwardMenuModel::kMaxHistoryItems + 2 + chapter_stop_offset,
    222             back_model->GetItemCount());
    223   EXPECT_EQ(0, forward_model->GetItemCount());
    224   EXPECT_EQ(ASCIIToUTF16("K1"), back_model->GetLabelAt(0));
    225   EXPECT_EQ(back_model->GetShowFullHistoryLabel(),
    226       back_model->GetLabelAt(BackForwardMenuModel::kMaxHistoryItems + 1 +
    227                                chapter_stop_offset));
    228 
    229   // Test for out of bounds (beyond Show Full History).
    230   EXPECT_FALSE(back_model->ItemHasCommand(
    231       BackForwardMenuModel::kMaxHistoryItems + chapter_stop_offset + 2));
    232 
    233   EXPECT_TRUE(back_model->ItemHasCommand(
    234               BackForwardMenuModel::kMaxHistoryItems - 1));
    235   EXPECT_TRUE(back_model->IsSeparator(
    236               BackForwardMenuModel::kMaxHistoryItems));
    237 
    238   NavigateToIndex(0);
    239 
    240   EXPECT_EQ(BackForwardMenuModel::kMaxHistoryItems + 2 + chapter_stop_offset,
    241             forward_model->GetItemCount());
    242   EXPECT_EQ(0, back_model->GetItemCount());
    243   EXPECT_EQ(ASCIIToUTF16("A2"), forward_model->GetLabelAt(0));
    244   EXPECT_EQ(forward_model->GetShowFullHistoryLabel(),
    245       forward_model->GetLabelAt(BackForwardMenuModel::kMaxHistoryItems + 1 +
    246                                     chapter_stop_offset));
    247 
    248   // Out of bounds
    249   EXPECT_FALSE(forward_model->ItemHasCommand(
    250       BackForwardMenuModel::kMaxHistoryItems + 2 + chapter_stop_offset));
    251 
    252   EXPECT_TRUE(forward_model->ItemHasCommand(
    253       BackForwardMenuModel::kMaxHistoryItems - 1));
    254   EXPECT_TRUE(forward_model->IsSeparator(
    255       BackForwardMenuModel::kMaxHistoryItems));
    256 }
    257 
    258 TEST_F(BackFwdMenuModelTest, ChapterStops) {
    259   scoped_ptr<BackForwardMenuModel> back_model(new BackForwardMenuModel(
    260     NULL, BackForwardMenuModel::BACKWARD_MENU));
    261   back_model->set_test_web_contents(web_contents());
    262 
    263   scoped_ptr<BackForwardMenuModel> forward_model(new BackForwardMenuModel(
    264       NULL, BackForwardMenuModel::FORWARD_MENU));
    265   forward_model->set_test_web_contents(web_contents());
    266 
    267   // Seed the controller with 32 URLs.
    268   int i = 0;
    269   LoadURLAndUpdateState("http://www.a.com/1", "A1");
    270   ValidateModel(back_model.get(), i++, 0);
    271   LoadURLAndUpdateState("http://www.a.com/2", "A2");
    272   ValidateModel(back_model.get(), i++, 0);
    273   LoadURLAndUpdateState("http://www.a.com/3", "A3");
    274   ValidateModel(back_model.get(), i++, 0);
    275   LoadURLAndUpdateState("http://www.b.com/1", "B1");
    276   ValidateModel(back_model.get(), i++, 0);
    277   LoadURLAndUpdateState("http://www.b.com/2", "B2");
    278   ValidateModel(back_model.get(), i++, 0);
    279   // i = 5
    280   LoadURLAndUpdateState("http://www.b.com/3", "B3");
    281   ValidateModel(back_model.get(), i++, 0);
    282   LoadURLAndUpdateState("http://www.c.com/1", "C1");
    283   ValidateModel(back_model.get(), i++, 0);
    284   LoadURLAndUpdateState("http://www.c.com/2", "C2");
    285   ValidateModel(back_model.get(), i++, 0);
    286   LoadURLAndUpdateState("http://www.c.com/3", "C3");
    287   ValidateModel(back_model.get(), i++, 0);
    288   LoadURLAndUpdateState("http://www.d.com/1", "D1");
    289   ValidateModel(back_model.get(), i++, 0);
    290   // i = 10
    291   LoadURLAndUpdateState("http://www.d.com/2", "D2");
    292   ValidateModel(back_model.get(), i++, 0);
    293   LoadURLAndUpdateState("http://www.d.com/3", "D3");
    294   ValidateModel(back_model.get(), i++, 0);
    295   LoadURLAndUpdateState("http://www.e.com/1", "E1");
    296   ValidateModel(back_model.get(), i++, 0);
    297   LoadURLAndUpdateState("http://www.e.com/2", "E2");
    298   ValidateModel(back_model.get(), i++, 0);
    299   LoadURLAndUpdateState("http://www.e.com/3", "E3");
    300   ValidateModel(back_model.get(), i++, 0);
    301   // i = 15
    302   LoadURLAndUpdateState("http://www.f.com/1", "F1");
    303   ValidateModel(back_model.get(), i++, 1);
    304   LoadURLAndUpdateState("http://www.f.com/2", "F2");
    305   ValidateModel(back_model.get(), i++, 1);
    306   LoadURLAndUpdateState("http://www.f.com/3", "F3");
    307   ValidateModel(back_model.get(), i++, 1);
    308   LoadURLAndUpdateState("http://www.g.com/1", "G1");
    309   ValidateModel(back_model.get(), i++, 2);
    310   LoadURLAndUpdateState("http://www.g.com/2", "G2");
    311   ValidateModel(back_model.get(), i++, 2);
    312   // i = 20
    313   LoadURLAndUpdateState("http://www.g.com/3", "G3");
    314   ValidateModel(back_model.get(), i++, 2);
    315   LoadURLAndUpdateState("http://www.h.com/1", "H1");
    316   ValidateModel(back_model.get(), i++, 3);
    317   LoadURLAndUpdateState("http://www.h.com/2", "H2");
    318   ValidateModel(back_model.get(), i++, 3);
    319   LoadURLAndUpdateState("http://www.h.com/3", "H3");
    320   ValidateModel(back_model.get(), i++, 3);
    321   LoadURLAndUpdateState("http://www.i.com/1", "I1");
    322   ValidateModel(back_model.get(), i++, 4);
    323   // i = 25
    324   LoadURLAndUpdateState("http://www.i.com/2", "I2");
    325   ValidateModel(back_model.get(), i++, 4);
    326   LoadURLAndUpdateState("http://www.i.com/3", "I3");
    327   ValidateModel(back_model.get(), i++, 4);
    328   LoadURLAndUpdateState("http://www.j.com/1", "J1");
    329   ValidateModel(back_model.get(), i++, 5);
    330   LoadURLAndUpdateState("http://www.j.com/2", "J2");
    331   ValidateModel(back_model.get(), i++, 5);
    332   LoadURLAndUpdateState("http://www.j.com/3", "J3");
    333   ValidateModel(back_model.get(), i++, 5);
    334   // i = 30
    335   LoadURLAndUpdateState("http://www.k.com/1", "K1");
    336   ValidateModel(back_model.get(), i++, 6);
    337   LoadURLAndUpdateState("http://www.k.com/2", "K2");
    338   ValidateModel(back_model.get(), i++, 6);
    339   // i = 32
    340   LoadURLAndUpdateState("http://www.k.com/3", "K3");
    341   ValidateModel(back_model.get(), i++, 6);
    342 
    343   // A chapter stop is defined as the last page the user
    344   // browsed to within the same domain.
    345 
    346   // Check to see if the chapter stops have the right labels.
    347   int index = BackForwardMenuModel::kMaxHistoryItems;
    348   // Empty string indicates item is a separator.
    349   EXPECT_EQ(base::string16(), back_model->GetLabelAt(index++));
    350   EXPECT_EQ(ASCIIToUTF16("F3"), back_model->GetLabelAt(index++));
    351   EXPECT_EQ(ASCIIToUTF16("E3"), back_model->GetLabelAt(index++));
    352   EXPECT_EQ(ASCIIToUTF16("D3"), back_model->GetLabelAt(index++));
    353   EXPECT_EQ(ASCIIToUTF16("C3"), back_model->GetLabelAt(index++));
    354   // The menu should only show a maximum of 5 chapter stops.
    355   EXPECT_EQ(ASCIIToUTF16("B3"), back_model->GetLabelAt(index));
    356   // Empty string indicates item is a separator.
    357   EXPECT_EQ(base::string16(), back_model->GetLabelAt(index + 1));
    358   EXPECT_EQ(back_model->GetShowFullHistoryLabel(),
    359             back_model->GetLabelAt(index + 2));
    360 
    361   // If we go back two we should still see the same chapter stop at the end.
    362   GoBack();
    363   EXPECT_EQ(ASCIIToUTF16("B3"), back_model->GetLabelAt(index));
    364   GoBack();
    365   EXPECT_EQ(ASCIIToUTF16("B3"), back_model->GetLabelAt(index));
    366   // But if we go back again, it should change.
    367   GoBack();
    368   EXPECT_EQ(ASCIIToUTF16("A3"), back_model->GetLabelAt(index));
    369   GoBack();
    370   EXPECT_EQ(ASCIIToUTF16("A3"), back_model->GetLabelAt(index));
    371   GoBack();
    372   EXPECT_EQ(ASCIIToUTF16("A3"), back_model->GetLabelAt(index));
    373   GoBack();
    374   // It is now a separator.
    375   EXPECT_EQ(base::string16(), back_model->GetLabelAt(index));
    376   // Undo our position change.
    377   NavigateToOffset(6);
    378 
    379   // Go back enough to make sure no chapter stops should appear.
    380   NavigateToOffset(-BackForwardMenuModel::kMaxHistoryItems);
    381   ValidateModel(forward_model.get(), BackForwardMenuModel::kMaxHistoryItems, 0);
    382   // Go forward (still no chapter stop)
    383   GoForward();
    384   ValidateModel(forward_model.get(),
    385                 BackForwardMenuModel::kMaxHistoryItems - 1, 0);
    386   // Go back two (one chapter stop should show up)
    387   GoBack();
    388   GoBack();
    389   ValidateModel(forward_model.get(),
    390                 BackForwardMenuModel::kMaxHistoryItems, 1);
    391 
    392   // Go to beginning.
    393   NavigateToIndex(0);
    394 
    395   // Check to see if the chapter stops have the right labels.
    396   index = BackForwardMenuModel::kMaxHistoryItems;
    397   // Empty string indicates item is a separator.
    398   EXPECT_EQ(base::string16(), forward_model->GetLabelAt(index++));
    399   EXPECT_EQ(ASCIIToUTF16("E3"), forward_model->GetLabelAt(index++));
    400   EXPECT_EQ(ASCIIToUTF16("F3"), forward_model->GetLabelAt(index++));
    401   EXPECT_EQ(ASCIIToUTF16("G3"), forward_model->GetLabelAt(index++));
    402   EXPECT_EQ(ASCIIToUTF16("H3"), forward_model->GetLabelAt(index++));
    403   // The menu should only show a maximum of 5 chapter stops.
    404   EXPECT_EQ(ASCIIToUTF16("I3"), forward_model->GetLabelAt(index));
    405   // Empty string indicates item is a separator.
    406   EXPECT_EQ(base::string16(), forward_model->GetLabelAt(index + 1));
    407   EXPECT_EQ(forward_model->GetShowFullHistoryLabel(),
    408       forward_model->GetLabelAt(index + 2));
    409 
    410   // If we advance one we should still see the same chapter stop at the end.
    411   GoForward();
    412   EXPECT_EQ(ASCIIToUTF16("I3"), forward_model->GetLabelAt(index));
    413   // But if we advance one again, it should change.
    414   GoForward();
    415   EXPECT_EQ(ASCIIToUTF16("J3"), forward_model->GetLabelAt(index));
    416   GoForward();
    417   EXPECT_EQ(ASCIIToUTF16("J3"), forward_model->GetLabelAt(index));
    418   GoForward();
    419   EXPECT_EQ(ASCIIToUTF16("J3"), forward_model->GetLabelAt(index));
    420   GoForward();
    421   EXPECT_EQ(ASCIIToUTF16("K3"), forward_model->GetLabelAt(index));
    422 
    423   // Now test the boundary cases by using the chapter stop function directly.
    424   // Out of bounds, first too far right (incrementing), then too far left.
    425   EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(33, false));
    426   EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(-1, true));
    427   // Test being at end and going right, then at beginning going left.
    428   EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(32, true));
    429   EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(0, false));
    430   // Test success: beginning going right and end going left.
    431   EXPECT_EQ(2,  back_model->GetIndexOfNextChapterStop(0, true));
    432   EXPECT_EQ(29, back_model->GetIndexOfNextChapterStop(32, false));
    433   // Now see when the chapter stops begin to show up.
    434   EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(1, false));
    435   EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(2, false));
    436   EXPECT_EQ(2,  back_model->GetIndexOfNextChapterStop(3, false));
    437   // Now see when the chapter stops end.
    438   EXPECT_EQ(32, back_model->GetIndexOfNextChapterStop(30, true));
    439   EXPECT_EQ(32, back_model->GetIndexOfNextChapterStop(31, true));
    440   EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(32, true));
    441 
    442   // Bug found during review (two different sites, but first wasn't considered
    443   // a chapter-stop).
    444   // Go to A1;
    445   NavigateToIndex(0);
    446   LoadURLAndUpdateState("http://www.b.com/1", "B1");
    447   EXPECT_EQ(0, back_model->GetIndexOfNextChapterStop(1, false));
    448   EXPECT_EQ(1, back_model->GetIndexOfNextChapterStop(0, true));
    449 
    450   // Now see if it counts 'www.x.com' and 'mail.x.com' as same domain, which
    451   // it should.
    452   // Go to A1.
    453   NavigateToIndex(0);
    454   LoadURLAndUpdateState("http://mail.a.com/2", "A2-mai");
    455   LoadURLAndUpdateState("http://www.b.com/1", "B1");
    456   LoadURLAndUpdateState("http://mail.b.com/2", "B2-mai");
    457   LoadURLAndUpdateState("http://new.site.com", "new");
    458   EXPECT_EQ(1, back_model->GetIndexOfNextChapterStop(0, true));
    459   EXPECT_EQ(3, back_model->GetIndexOfNextChapterStop(1, true));
    460   EXPECT_EQ(3, back_model->GetIndexOfNextChapterStop(2, true));
    461   EXPECT_EQ(4, back_model->GetIndexOfNextChapterStop(3, true));
    462   // And try backwards as well.
    463   EXPECT_EQ(3, back_model->GetIndexOfNextChapterStop(4, false));
    464   EXPECT_EQ(1, back_model->GetIndexOfNextChapterStop(3, false));
    465   EXPECT_EQ(1, back_model->GetIndexOfNextChapterStop(2, false));
    466   EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(1, false));
    467 }
    468 
    469 TEST_F(BackFwdMenuModelTest, EscapeLabel) {
    470   scoped_ptr<BackForwardMenuModel> back_model(new BackForwardMenuModel(
    471       NULL, BackForwardMenuModel::BACKWARD_MENU));
    472   back_model->set_test_web_contents(web_contents());
    473 
    474   EXPECT_EQ(0, back_model->GetItemCount());
    475   EXPECT_FALSE(back_model->ItemHasCommand(1));
    476 
    477   LoadURLAndUpdateState("http://www.a.com/1", "A B");
    478   LoadURLAndUpdateState("http://www.a.com/1", "A & B");
    479   LoadURLAndUpdateState("http://www.a.com/2", "A && B");
    480   LoadURLAndUpdateState("http://www.a.com/2", "A &&& B");
    481   LoadURLAndUpdateState("http://www.a.com/3", "");
    482 
    483   EXPECT_EQ(6, back_model->GetItemCount());
    484 
    485   // On Mac ui::MenuModel::GetLabelAt should return unescaped strings.
    486 #if defined(OS_MACOSX)
    487   EXPECT_EQ(ASCIIToUTF16("A B"), back_model->GetLabelAt(3));
    488   EXPECT_EQ(ASCIIToUTF16("A & B"), back_model->GetLabelAt(2));
    489   EXPECT_EQ(ASCIIToUTF16("A && B"), back_model->GetLabelAt(1));
    490   EXPECT_EQ(ASCIIToUTF16("A &&& B"), back_model->GetLabelAt(0));
    491 #else
    492   EXPECT_EQ(ASCIIToUTF16("A B"), back_model->GetLabelAt(3));
    493   EXPECT_EQ(ASCIIToUTF16("A && B"), back_model->GetLabelAt(2));
    494   EXPECT_EQ(ASCIIToUTF16("A &&&& B"), back_model->GetLabelAt(1));
    495   EXPECT_EQ(ASCIIToUTF16("A &&&&&& B"), back_model->GetLabelAt(0));
    496 #endif // defined(OS_MACOSX)
    497 }
    498 
    499 // Test asynchronous loading of favicon from history service.
    500 TEST_F(BackFwdMenuModelTest, FaviconLoadTest) {
    501   ASSERT_TRUE(profile()->CreateHistoryService(true, false));
    502   profile()->CreateFaviconService();
    503   Browser::CreateParams native_params(profile(), chrome::GetActiveDesktop());
    504   scoped_ptr<Browser> browser(
    505       chrome::CreateBrowserWithTestWindowForParams(&native_params));
    506   FaviconDelegate favicon_delegate;
    507 
    508   BackForwardMenuModel back_model(
    509       browser.get(), BackForwardMenuModel::BACKWARD_MENU);
    510   back_model.set_test_web_contents(controller().GetWebContents());
    511   back_model.SetMenuModelDelegate(&favicon_delegate);
    512 
    513   SkBitmap new_icon_bitmap(CreateBitmap(SK_ColorRED));
    514 
    515   GURL url1 = GURL("http://www.a.com/1");
    516   GURL url2 = GURL("http://www.a.com/2");
    517   GURL url1_favicon("http://www.a.com/1/favicon.ico");
    518 
    519   NavigateAndCommit(url1);
    520   // Navigate to a new URL so that url1 will be in the BackForwardMenuModel.
    521   NavigateAndCommit(url2);
    522 
    523   // Set the desired favicon for url1.
    524   HistoryServiceFactory::GetForProfile(
    525       profile(), Profile::EXPLICIT_ACCESS)->AddPage(
    526           url1, base::Time::Now(), history::SOURCE_BROWSED);
    527   FaviconServiceFactory::GetForProfile(profile(), Profile::EXPLICIT_ACCESS)
    528       ->SetFavicons(url1,
    529                     url1_favicon,
    530                     favicon_base::FAVICON,
    531                     gfx::Image::CreateFrom1xBitmap(new_icon_bitmap));
    532 
    533   // Will return the current icon (default) but start an anync call
    534   // to retrieve the favicon from the favicon service.
    535   gfx::Image default_icon;
    536   back_model.GetIconAt(0, &default_icon);
    537 
    538   // Make the favicon service run GetFavIconForURL,
    539   // FaviconDelegate.OnIconChanged will be called.
    540   base::MessageLoop::current()->Run();
    541 
    542   // Verify that the callback executed.
    543   EXPECT_TRUE(favicon_delegate.was_called());
    544 
    545   // Verify the bitmaps match.
    546   gfx::Image valid_icon;
    547   // This time we will get the new favicon returned.
    548   back_model.GetIconAt(0, &valid_icon);
    549 
    550   SkBitmap default_icon_bitmap = *default_icon.ToSkBitmap();
    551   SkBitmap valid_icon_bitmap = *valid_icon.ToSkBitmap();
    552 
    553   SkAutoLockPixels a(new_icon_bitmap);
    554   SkAutoLockPixels b(valid_icon_bitmap);
    555   SkAutoLockPixels c(default_icon_bitmap);
    556   // Verify we did not get the default favicon.
    557   EXPECT_NE(0, memcmp(default_icon_bitmap.getPixels(),
    558                       valid_icon_bitmap.getPixels(),
    559                       default_icon_bitmap.getSize()));
    560   // Verify we did get the expected favicon.
    561   EXPECT_EQ(0, memcmp(new_icon_bitmap.getPixels(),
    562                       valid_icon_bitmap.getPixels(),
    563                       new_icon_bitmap.getSize()));
    564 
    565   // Make sure the browser deconstructor doesn't have problems.
    566   browser->tab_strip_model()->CloseAllTabs();
    567   // This is required to prevent the message loop from hanging.
    568   profile()->DestroyHistoryService();
    569 }
    570