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