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