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