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