• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
CreateBitmap(SkColor 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:
FaviconDelegate()47   FaviconDelegate() : was_called_(false) {}
48 
OnIconChanged(int model_index)49   virtual void OnIconChanged(int model_index) OVERRIDE {
50     was_called_ = true;
51     base::MessageLoop::current()->Quit();
52   }
53 
was_called() const54   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:
ValidateModel(BackForwardMenuModel * model,int history_items,int chapter_stops)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 
LoadURLAndUpdateState(const char * url,const char * title)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).
NavigateToOffset(int offset)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.
NavigateToIndex(int 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.
GoBack()98   void GoBack() {
99     controller().GoBack();
100     WebContentsTester::For(web_contents())->CommitPendingNavigation();
101   }
GoForward()102   void GoForward() {
103     controller().GoForward();
104     WebContentsTester::For(web_contents())->CommitPendingNavigation();
105   }
106 };
107 
TEST_F(BackFwdMenuModelTest,BasicCase)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 
TEST_F(BackFwdMenuModelTest,MaxItemsTest)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 
TEST_F(BackFwdMenuModelTest,ChapterStops)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 
TEST_F(BackFwdMenuModelTest,EscapeLabel)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.
TEST_F(BackFwdMenuModelTest,FaviconLoadTest)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