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.
CreateBitmap(SkColor 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:
FaviconDelegate()38 FaviconDelegate() : was_called_(false) {}
39
OnIconChanged(int model_index)40 void OnIconChanged(int model_index) {
41 was_called_ = true;
42 MessageLoop::current()->Quit();
43 }
44
was_called() const45 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:
BackFwdMenuModelTest()57 BackFwdMenuModelTest()
58 : ui_thread_(BrowserThread::UI, &message_loop_) {
59 }
60
ValidateModel(BackForwardMenuModel * model,int history_items,int chapter_stops)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
LoadURLAndUpdateState(const char * url,const char * title)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).
NavigateToOffset(int offset)81 void NavigateToOffset(int offset) {
82 controller().GoToOffset(offset);
83 contents()->CommitPendingNavigation();
84 }
85
86 // Same as NavigateToOffset but goes to an absolute index.
NavigateToIndex(int index)87 void NavigateToIndex(int index) {
88 controller().GoToIndex(index);
89 contents()->CommitPendingNavigation();
90 }
91
92 // Goes back/forward and commits the load.
GoBack()93 void GoBack() {
94 controller().GoBack();
95 contents()->CommitPendingNavigation();
96 }
GoForward()97 void GoForward() {
98 controller().GoForward();
99 contents()->CommitPendingNavigation();
100 }
101
102 BrowserThread ui_thread_;
103 };
104
TEST_F(BackFwdMenuModelTest,BasicCase)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
TEST_F(BackFwdMenuModelTest,MaxItemsTest)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
TEST_F(BackFwdMenuModelTest,ChapterStops)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
TEST_F(BackFwdMenuModelTest,EscapeLabel)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.
TEST_F(BackFwdMenuModelTest,FaviconLoadTest)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