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 <map> 6 #include <string> 7 8 #include "base/file_path.h" 9 #include "base/file_util.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/path_service.h" 12 #include "base/stl_util-inl.h" 13 #include "base/string_number_conversions.h" 14 #include "base/string_split.h" 15 #include "base/string_util.h" 16 #include "base/utf_string_conversions.h" 17 #include "chrome/browser/defaults.h" 18 #include "chrome/browser/extensions/extension_tab_helper.h" 19 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/browser/profiles/profile_manager.h" 21 #include "chrome/browser/tabs/tab_strip_model.h" 22 #include "chrome/browser/tabs/tab_strip_model_delegate.h" 23 #include "chrome/browser/tabs/tab_strip_model_order_controller.h" 24 #include "chrome/browser/ui/browser.h" 25 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 26 #include "chrome/browser/ui/webui/new_tab_ui.h" 27 #include "chrome/common/extensions/extension.h" 28 #include "chrome/common/url_constants.h" 29 #include "chrome/test/testing_profile.h" 30 #include "content/browser/renderer_host/test_render_view_host.h" 31 #include "content/browser/tab_contents/navigation_controller.h" 32 #include "content/browser/tab_contents/navigation_entry.h" 33 #include "content/browser/tab_contents/tab_contents.h" 34 #include "content/common/notification_details.h" 35 #include "content/common/notification_observer_mock.h" 36 #include "content/common/notification_registrar.h" 37 #include "content/common/notification_source.h" 38 #include "content/common/property_bag.h" 39 #include "testing/gtest/include/gtest/gtest.h" 40 #include "ui/base/system_monitor/system_monitor.h" 41 42 using testing::_; 43 44 namespace { 45 46 // Class used to delete a TabContents when another TabContents is destroyed. 47 class DeleteTabContentsOnDestroyedObserver : public NotificationObserver { 48 public: 49 DeleteTabContentsOnDestroyedObserver(TabContentsWrapper* source, 50 TabContentsWrapper* tab_to_delete) 51 : source_(source), 52 tab_to_delete_(tab_to_delete) { 53 registrar_.Add(this, 54 NotificationType::TAB_CONTENTS_DESTROYED, 55 Source<TabContents>(source->tab_contents())); 56 } 57 58 virtual void Observe(NotificationType type, 59 const NotificationSource& source, 60 const NotificationDetails& details) { 61 TabContentsWrapper* tab_to_delete = tab_to_delete_; 62 tab_to_delete_ = NULL; 63 delete tab_to_delete; 64 } 65 66 private: 67 TabContentsWrapper* source_; 68 TabContentsWrapper* tab_to_delete_; 69 NotificationRegistrar registrar_; 70 71 DISALLOW_COPY_AND_ASSIGN(DeleteTabContentsOnDestroyedObserver); 72 }; 73 74 } // namespace 75 76 class TabStripDummyDelegate : public TabStripModelDelegate { 77 public: 78 explicit TabStripDummyDelegate(TabContentsWrapper* dummy) 79 : dummy_contents_(dummy), can_close_(true), run_unload_(false) {} 80 virtual ~TabStripDummyDelegate() {} 81 82 void set_can_close(bool value) { can_close_ = value; } 83 void set_run_unload_listener(bool value) { run_unload_ = value; } 84 85 // Overridden from TabStripModelDelegate: 86 virtual TabContentsWrapper* AddBlankTab(bool foreground) { 87 return NULL; 88 } 89 virtual TabContentsWrapper* AddBlankTabAt(int index, bool foreground) { 90 return NULL; 91 } 92 virtual Browser* CreateNewStripWithContents(TabContentsWrapper* contents, 93 const gfx::Rect& window_bounds, 94 const DockInfo& dock_info, 95 bool maximize) { 96 return NULL; 97 } 98 virtual void ContinueDraggingDetachedTab(TabContentsWrapper* contents, 99 const gfx::Rect& window_bounds, 100 const gfx::Rect& tab_bounds) { 101 } 102 virtual int GetDragActions() const { return 0; } 103 virtual TabContentsWrapper* CreateTabContentsForURL( 104 const GURL& url, 105 const GURL& referrer, 106 Profile* profile, 107 PageTransition::Type transition, 108 bool defer_load, 109 SiteInstance* instance) const { 110 if (url == GURL(chrome::kChromeUINewTabURL)) 111 return dummy_contents_; 112 return NULL; 113 } 114 virtual bool CanDuplicateContentsAt(int index) { return false; } 115 virtual void DuplicateContentsAt(int index) {} 116 virtual void CloseFrameAfterDragSession() {} 117 virtual void CreateHistoricalTab(TabContentsWrapper* contents) {} 118 virtual bool RunUnloadListenerBeforeClosing(TabContentsWrapper* contents) { 119 return run_unload_; 120 } 121 virtual bool CanRestoreTab() { return false; } 122 virtual void RestoreTab() {} 123 virtual bool CanCloseContentsAt(int index) { return can_close_ ; } 124 virtual bool CanBookmarkAllTabs() const { return false; } 125 virtual void BookmarkAllTabs() {} 126 virtual bool CanCloseTab() const { return true; } 127 virtual bool UseVerticalTabs() const { return false; } 128 virtual void ToggleUseVerticalTabs() {} 129 virtual bool LargeIconsPermitted() const { return true; } 130 131 private: 132 // A dummy TabContents we give to callers that expect us to actually build a 133 // Destinations tab for them. 134 TabContentsWrapper* dummy_contents_; 135 136 // Whether tabs can be closed. 137 bool can_close_; 138 139 // Whether to report that we need to run an unload listener before closing. 140 bool run_unload_; 141 142 DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate); 143 }; 144 145 class TabStripModelTest : public RenderViewHostTestHarness { 146 public: 147 TabContentsWrapper* CreateTabContents() { 148 return Browser::TabContentsFactory(profile(), NULL, 0, NULL, NULL); 149 } 150 151 TabContentsWrapper* CreateTabContentsWithSharedRPH( 152 TabContents* tab_contents) { 153 TabContentsWrapper* retval = Browser::TabContentsFactory(profile(), 154 tab_contents->render_view_host()->site_instance(), MSG_ROUTING_NONE, 155 NULL, NULL); 156 EXPECT_EQ(retval->tab_contents()->GetRenderProcessHost(), 157 tab_contents->GetRenderProcessHost()); 158 return retval; 159 } 160 161 // Forwards a URL "load" request through to our dummy TabContents 162 // implementation. 163 void LoadURL(TabContents* con, const std::wstring& url) { 164 controller().LoadURL(GURL(WideToUTF16(url)), GURL(), PageTransition::LINK); 165 } 166 167 void GoBack(TabContents* contents) { 168 controller().GoBack(); 169 } 170 171 void GoForward(TabContents* contents) { 172 controller().GoForward(); 173 } 174 175 void SwitchTabTo(TabContents* contents) { 176 // contents()->DidBecomeSelected(); 177 } 178 179 // Sets the id of the specified contents. 180 void SetID(TabContents* contents, int id) { 181 GetIDAccessor()->SetProperty(contents->property_bag(), id); 182 } 183 184 // Returns the id of the specified contents. 185 int GetID(TabContents* contents) { 186 return *GetIDAccessor()->GetProperty(contents->property_bag()); 187 } 188 189 // Returns the state of the given tab strip as a string. The state consists 190 // of the ID of each tab contents followed by a 'p' if pinned. For example, 191 // if the model consists of two tabs with ids 2 and 1, with the first 192 // tab pinned, this returns "2p 1". 193 std::string GetPinnedState(const TabStripModel& model) { 194 std::string actual; 195 for (int i = 0; i < model.count(); ++i) { 196 if (i > 0) 197 actual += " "; 198 199 actual += 200 base::IntToString(GetID(model.GetTabContentsAt(i)->tab_contents())); 201 202 if (model.IsAppTab(i)) 203 actual += "a"; 204 205 if (model.IsTabPinned(i)) 206 actual += "p"; 207 } 208 return actual; 209 } 210 211 std::string GetIndicesClosedByCommandAsString( 212 const TabStripModel& model, 213 int index, 214 TabStripModel::ContextMenuCommand id) const { 215 std::vector<int> indices = model.GetIndicesClosedByCommand(index, id); 216 std::string result; 217 for (size_t i = 0; i < indices.size(); ++i) { 218 if (i != 0) 219 result += " "; 220 result += base::IntToString(indices[i]); 221 } 222 return result; 223 } 224 225 void PrepareTabstripForSelectionTest(TabStripModel* model, 226 int tab_count, 227 int pinned_count, 228 const std::string& selected_tabs) { 229 for (int i = 0; i < tab_count; ++i) { 230 TabContentsWrapper* contents = CreateTabContents(); 231 SetID(contents->tab_contents(), i); 232 model->AppendTabContents(contents, true); 233 } 234 for (int i = 0; i < pinned_count; ++i) 235 model->SetTabPinned(i, true); 236 237 TabStripSelectionModel selection_model; 238 std::vector<std::string> selection; 239 base::SplitStringAlongWhitespace(selected_tabs, &selection); 240 for (size_t i = 0; i < selection.size(); ++i) { 241 int value; 242 ASSERT_TRUE(base::StringToInt(selection[i], &value)); 243 selection_model.AddIndexToSelection(value); 244 } 245 selection_model.set_active(selection_model.selected_indices()[0]); 246 model->SetSelectionFromModel(selection_model); 247 } 248 249 private: 250 PropertyAccessor<int>* GetIDAccessor() { 251 static PropertyAccessor<int> accessor; 252 return &accessor; 253 } 254 255 std::wstring test_dir_; 256 std::wstring profile_path_; 257 std::map<TabContents*, int> foo_; 258 259 // ProfileManager requires a ui::SystemMonitor. 260 ui::SystemMonitor system_monitor; 261 262 ProfileManager pm_; 263 }; 264 265 class MockTabStripModelObserver : public TabStripModelObserver { 266 public: 267 MockTabStripModelObserver() : empty_(true) {} 268 ~MockTabStripModelObserver() { 269 STLDeleteContainerPointers(states_.begin(), states_.end()); 270 } 271 272 enum TabStripModelObserverAction { 273 INSERT, 274 CLOSE, 275 DETACH, 276 SELECT, 277 MOVE, 278 CHANGE, 279 PINNED, 280 REPLACED 281 }; 282 283 struct State { 284 State(TabContentsWrapper* a_dst_contents, 285 int a_dst_index, 286 TabStripModelObserverAction a_action) 287 : src_contents(NULL), 288 dst_contents(a_dst_contents), 289 src_index(-1), 290 dst_index(a_dst_index), 291 user_gesture(false), 292 foreground(false), 293 action(a_action) { 294 } 295 296 TabContentsWrapper* src_contents; 297 TabContentsWrapper* dst_contents; 298 int src_index; 299 int dst_index; 300 bool user_gesture; 301 bool foreground; 302 TabStripModelObserverAction action; 303 }; 304 305 int GetStateCount() const { 306 return static_cast<int>(states_.size()); 307 } 308 309 State* GetStateAt(int index) const { 310 DCHECK(index >= 0 && index < GetStateCount()); 311 return states_.at(index); 312 } 313 314 bool StateEquals(int index, const State& state) { 315 State* s = GetStateAt(index); 316 EXPECT_EQ(state.src_contents, s->src_contents); 317 EXPECT_EQ(state.dst_contents, s->dst_contents); 318 EXPECT_EQ(state.src_index, s->src_index); 319 EXPECT_EQ(state.dst_index, s->dst_index); 320 EXPECT_EQ(state.user_gesture, s->user_gesture); 321 EXPECT_EQ(state.foreground, s->foreground); 322 EXPECT_EQ(state.action, s->action); 323 return (s->src_contents == state.src_contents && 324 s->dst_contents == state.dst_contents && 325 s->src_index == state.src_index && 326 s->dst_index == state.dst_index && 327 s->user_gesture == state.user_gesture && 328 s->foreground == state.foreground && 329 s->action == state.action); 330 } 331 332 // TabStripModelObserver implementation: 333 virtual void TabInsertedAt(TabContentsWrapper* contents, 334 int index, 335 bool foreground) { 336 empty_ = false; 337 State* s = new State(contents, index, INSERT); 338 s->foreground = foreground; 339 states_.push_back(s); 340 } 341 virtual void TabSelectedAt(TabContentsWrapper* old_contents, 342 TabContentsWrapper* new_contents, 343 int index, 344 bool user_gesture) { 345 State* s = new State(new_contents, index, SELECT); 346 s->src_contents = old_contents; 347 s->user_gesture = user_gesture; 348 states_.push_back(s); 349 } 350 virtual void TabMoved( 351 TabContentsWrapper* contents, int from_index, int to_index) { 352 State* s = new State(contents, to_index, MOVE); 353 s->src_index = from_index; 354 states_.push_back(s); 355 } 356 357 virtual void TabClosingAt(TabStripModel* tab_strip_model, 358 TabContentsWrapper* contents, 359 int index) { 360 states_.push_back(new State(contents, index, CLOSE)); 361 } 362 virtual void TabDetachedAt(TabContentsWrapper* contents, int index) { 363 states_.push_back(new State(contents, index, DETACH)); 364 } 365 virtual void TabChangedAt(TabContentsWrapper* contents, int index, 366 TabChangeType change_type) { 367 states_.push_back(new State(contents, index, CHANGE)); 368 } 369 virtual void TabReplacedAt(TabStripModel* tab_strip_model, 370 TabContentsWrapper* old_contents, 371 TabContentsWrapper* new_contents, 372 int index) { 373 State* s = new State(new_contents, index, REPLACED); 374 s ->src_contents = old_contents; 375 states_.push_back(s); 376 } 377 virtual void TabPinnedStateChanged(TabContentsWrapper* contents, int index) { 378 states_.push_back(new State(contents, index, PINNED)); 379 } 380 virtual void TabStripEmpty() { 381 empty_ = true; 382 } 383 384 void ClearStates() { 385 STLDeleteContainerPointers(states_.begin(), states_.end()); 386 states_.clear(); 387 } 388 389 bool empty() const { return empty_; } 390 391 private: 392 std::vector<State*> states_; 393 394 bool empty_; 395 396 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver); 397 }; 398 399 TEST_F(TabStripModelTest, TestBasicAPI) { 400 TabStripDummyDelegate delegate(NULL); 401 TabStripModel tabstrip(&delegate, profile()); 402 MockTabStripModelObserver observer; 403 tabstrip.AddObserver(&observer); 404 405 EXPECT_TRUE(tabstrip.empty()); 406 407 typedef MockTabStripModelObserver::State State; 408 409 TabContentsWrapper* contents1 = CreateTabContents(); 410 411 // Note! The ordering of these tests is important, each subsequent test 412 // builds on the state established in the previous. This is important if you 413 // ever insert tests rather than append. 414 415 // Test AppendTabContents, ContainsIndex 416 { 417 EXPECT_FALSE(tabstrip.ContainsIndex(0)); 418 tabstrip.AppendTabContents(contents1, true); 419 EXPECT_TRUE(tabstrip.ContainsIndex(0)); 420 EXPECT_EQ(1, tabstrip.count()); 421 EXPECT_EQ(2, observer.GetStateCount()); 422 State s1(contents1, 0, MockTabStripModelObserver::INSERT); 423 s1.foreground = true; 424 EXPECT_TRUE(observer.StateEquals(0, s1)); 425 State s2(contents1, 0, MockTabStripModelObserver::SELECT); 426 s2.src_contents = NULL; 427 EXPECT_TRUE(observer.StateEquals(1, s2)); 428 observer.ClearStates(); 429 } 430 431 // Test InsertTabContentsAt, foreground tab. 432 TabContentsWrapper* contents2 = CreateTabContents(); 433 { 434 tabstrip.InsertTabContentsAt(1, contents2, TabStripModel::ADD_ACTIVE); 435 436 EXPECT_EQ(2, tabstrip.count()); 437 EXPECT_EQ(2, observer.GetStateCount()); 438 State s1(contents2, 1, MockTabStripModelObserver::INSERT); 439 s1.foreground = true; 440 EXPECT_TRUE(observer.StateEquals(0, s1)); 441 State s2(contents2, 1, MockTabStripModelObserver::SELECT); 442 s2.src_contents = contents1; 443 EXPECT_TRUE(observer.StateEquals(1, s2)); 444 observer.ClearStates(); 445 } 446 447 // Test InsertTabContentsAt, background tab. 448 TabContentsWrapper* contents3 = CreateTabContents(); 449 { 450 tabstrip.InsertTabContentsAt(2, contents3, TabStripModel::ADD_NONE); 451 452 EXPECT_EQ(3, tabstrip.count()); 453 EXPECT_EQ(1, observer.GetStateCount()); 454 State s1(contents3, 2, MockTabStripModelObserver::INSERT); 455 s1.foreground = false; 456 EXPECT_TRUE(observer.StateEquals(0, s1)); 457 observer.ClearStates(); 458 } 459 460 // Test ActivateTabAt 461 { 462 tabstrip.ActivateTabAt(2, true); 463 EXPECT_EQ(1, observer.GetStateCount()); 464 State s1(contents3, 2, MockTabStripModelObserver::SELECT); 465 s1.src_contents = contents2; 466 s1.user_gesture = true; 467 EXPECT_TRUE(observer.StateEquals(0, s1)); 468 observer.ClearStates(); 469 } 470 471 // Test DetachTabContentsAt 472 { 473 // Detach 474 TabContentsWrapper* detached = tabstrip.DetachTabContentsAt(2); 475 // ... and append again because we want this for later. 476 tabstrip.AppendTabContents(detached, true); 477 EXPECT_EQ(4, observer.GetStateCount()); 478 State s1(detached, 2, MockTabStripModelObserver::DETACH); 479 EXPECT_TRUE(observer.StateEquals(0, s1)); 480 State s2(contents2, 1, MockTabStripModelObserver::SELECT); 481 s2.src_contents = contents3; 482 s2.user_gesture = false; 483 EXPECT_TRUE(observer.StateEquals(1, s2)); 484 State s3(detached, 2, MockTabStripModelObserver::INSERT); 485 s3.foreground = true; 486 EXPECT_TRUE(observer.StateEquals(2, s3)); 487 State s4(detached, 2, MockTabStripModelObserver::SELECT); 488 s4.src_contents = contents2; 489 s4.user_gesture = false; 490 EXPECT_TRUE(observer.StateEquals(3, s4)); 491 observer.ClearStates(); 492 } 493 494 // Test CloseTabContentsAt 495 { 496 // Let's test nothing happens when the delegate veto the close. 497 delegate.set_can_close(false); 498 EXPECT_FALSE(tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE)); 499 EXPECT_EQ(3, tabstrip.count()); 500 EXPECT_EQ(0, observer.GetStateCount()); 501 502 // Now let's close for real. 503 delegate.set_can_close(true); 504 EXPECT_TRUE(tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE)); 505 EXPECT_EQ(2, tabstrip.count()); 506 507 EXPECT_EQ(3, observer.GetStateCount()); 508 State s1(contents3, 2, MockTabStripModelObserver::CLOSE); 509 EXPECT_TRUE(observer.StateEquals(0, s1)); 510 State s2(contents3, 2, MockTabStripModelObserver::DETACH); 511 EXPECT_TRUE(observer.StateEquals(1, s2)); 512 State s3(contents2, 1, MockTabStripModelObserver::SELECT); 513 s3.src_contents = contents3; 514 s3.user_gesture = false; 515 EXPECT_TRUE(observer.StateEquals(2, s3)); 516 observer.ClearStates(); 517 } 518 519 // Test MoveTabContentsAt, select_after_move == true 520 { 521 tabstrip.MoveTabContentsAt(1, 0, true); 522 523 EXPECT_EQ(1, observer.GetStateCount()); 524 State s1(contents2, 0, MockTabStripModelObserver::MOVE); 525 s1.src_index = 1; 526 EXPECT_TRUE(observer.StateEquals(0, s1)); 527 EXPECT_EQ(0, tabstrip.active_index()); 528 observer.ClearStates(); 529 } 530 531 // Test MoveTabContentsAt, select_after_move == false 532 { 533 tabstrip.MoveTabContentsAt(1, 0, false); 534 EXPECT_EQ(1, observer.GetStateCount()); 535 State s1(contents1, 0, MockTabStripModelObserver::MOVE); 536 s1.src_index = 1; 537 EXPECT_TRUE(observer.StateEquals(0, s1)); 538 EXPECT_EQ(1, tabstrip.active_index()); 539 540 tabstrip.MoveTabContentsAt(0, 1, false); 541 observer.ClearStates(); 542 } 543 544 // Test Getters 545 { 546 EXPECT_EQ(contents2, tabstrip.GetSelectedTabContents()); 547 EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(0)); 548 EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1)); 549 EXPECT_EQ(0, tabstrip.GetIndexOfTabContents(contents2)); 550 EXPECT_EQ(1, tabstrip.GetIndexOfTabContents(contents1)); 551 EXPECT_EQ(0, tabstrip.GetIndexOfController(&contents2->controller())); 552 EXPECT_EQ(1, tabstrip.GetIndexOfController(&contents1->controller())); 553 } 554 555 // Test UpdateTabContentsStateAt 556 { 557 tabstrip.UpdateTabContentsStateAt(0, TabStripModelObserver::ALL); 558 EXPECT_EQ(1, observer.GetStateCount()); 559 State s1(contents2, 0, MockTabStripModelObserver::CHANGE); 560 EXPECT_TRUE(observer.StateEquals(0, s1)); 561 observer.ClearStates(); 562 } 563 564 // Test SelectNextTab, SelectPreviousTab, SelectLastTab 565 { 566 // Make sure the second of the two tabs is selected first... 567 tabstrip.ActivateTabAt(1, true); 568 tabstrip.SelectPreviousTab(); 569 EXPECT_EQ(0, tabstrip.active_index()); 570 tabstrip.SelectLastTab(); 571 EXPECT_EQ(1, tabstrip.active_index()); 572 tabstrip.SelectNextTab(); 573 EXPECT_EQ(0, tabstrip.active_index()); 574 } 575 576 // Test CloseSelectedTabs 577 { 578 tabstrip.CloseSelectedTabs(); 579 // |CloseSelectedTabs| calls CloseTabContentsAt, we already tested that, now 580 // just verify that the count and selected index have changed 581 // appropriately... 582 EXPECT_EQ(1, tabstrip.count()); 583 EXPECT_EQ(0, tabstrip.active_index()); 584 } 585 586 tabstrip.CloseAllTabs(); 587 // TabStripModel should now be empty. 588 EXPECT_TRUE(tabstrip.empty()); 589 590 // Opener methods are tested below... 591 592 tabstrip.RemoveObserver(&observer); 593 } 594 595 TEST_F(TabStripModelTest, TestBasicOpenerAPI) { 596 TabStripDummyDelegate delegate(NULL); 597 TabStripModel tabstrip(&delegate, profile()); 598 EXPECT_TRUE(tabstrip.empty()); 599 600 // This is a basic test of opener functionality. opener_contents is created 601 // as the first tab in the strip and then we create 5 other tabs in the 602 // background with opener_contents set as their opener. 603 604 TabContentsWrapper* opener_contents = CreateTabContents(); 605 NavigationController* opener = &opener_contents->controller(); 606 tabstrip.AppendTabContents(opener_contents, true); 607 TabContentsWrapper* contents1 = CreateTabContents(); 608 TabContentsWrapper* contents2 = CreateTabContents(); 609 TabContentsWrapper* contents3 = CreateTabContents(); 610 TabContentsWrapper* contents4 = CreateTabContents(); 611 TabContentsWrapper* contents5 = CreateTabContents(); 612 613 // We use |InsertTabContentsAt| here instead of AppendTabContents so that 614 // openership relationships are preserved. 615 tabstrip.InsertTabContentsAt(tabstrip.count(), contents1, 616 TabStripModel::ADD_INHERIT_GROUP); 617 tabstrip.InsertTabContentsAt(tabstrip.count(), contents2, 618 TabStripModel::ADD_INHERIT_GROUP); 619 tabstrip.InsertTabContentsAt(tabstrip.count(), contents3, 620 TabStripModel::ADD_INHERIT_GROUP); 621 tabstrip.InsertTabContentsAt(tabstrip.count(), contents4, 622 TabStripModel::ADD_INHERIT_GROUP); 623 tabstrip.InsertTabContentsAt(tabstrip.count(), contents5, 624 TabStripModel::ADD_INHERIT_GROUP); 625 626 // All the tabs should have the same opener. 627 for (int i = 1; i < tabstrip.count(); ++i) 628 EXPECT_EQ(opener, tabstrip.GetOpenerOfTabContentsAt(i)); 629 630 // If there is a next adjacent item, then the index should be of that item. 631 EXPECT_EQ(2, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 1, false)); 632 // If the last tab in the group is closed, the preceding tab in the same 633 // group should be selected. 634 EXPECT_EQ(4, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 5, false)); 635 636 // Tests the method that finds the last tab opened by the same opener in the 637 // strip (this is the insertion index for the next background tab for the 638 // specified opener). 639 EXPECT_EQ(5, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1)); 640 641 // For a tab that has opened no other tabs, the return value should always be 642 // -1... 643 NavigationController* o1 = &contents1->controller(); 644 EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(o1, 3, false)); 645 EXPECT_EQ(-1, tabstrip.GetIndexOfLastTabContentsOpenedBy(o1, 3)); 646 647 // ForgetAllOpeners should destroy all opener relationships. 648 tabstrip.ForgetAllOpeners(); 649 EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 1, false)); 650 EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 5, false)); 651 EXPECT_EQ(-1, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1)); 652 653 tabstrip.CloseAllTabs(); 654 EXPECT_TRUE(tabstrip.empty()); 655 } 656 657 static int GetInsertionIndex(TabStripModel* tabstrip, 658 TabContentsWrapper* contents) { 659 return tabstrip->order_controller()->DetermineInsertionIndex( 660 contents, PageTransition::LINK, false); 661 } 662 663 static void InsertTabContentses(TabStripModel* tabstrip, 664 TabContentsWrapper* contents1, 665 TabContentsWrapper* contents2, 666 TabContentsWrapper* contents3) { 667 tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents1), 668 contents1, TabStripModel::ADD_INHERIT_GROUP); 669 tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents2), 670 contents2, TabStripModel::ADD_INHERIT_GROUP); 671 tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents3), 672 contents3, TabStripModel::ADD_INHERIT_GROUP); 673 } 674 675 // Tests opening background tabs. 676 TEST_F(TabStripModelTest, TestLTRInsertionOptions) { 677 TabStripDummyDelegate delegate(NULL); 678 TabStripModel tabstrip(&delegate, profile()); 679 EXPECT_TRUE(tabstrip.empty()); 680 681 TabContentsWrapper* opener_contents = CreateTabContents(); 682 tabstrip.AppendTabContents(opener_contents, true); 683 684 TabContentsWrapper* contents1 = CreateTabContents(); 685 TabContentsWrapper* contents2 = CreateTabContents(); 686 TabContentsWrapper* contents3 = CreateTabContents(); 687 688 // Test LTR 689 InsertTabContentses(&tabstrip, contents1, contents2, contents3); 690 EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1)); 691 EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(2)); 692 EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(3)); 693 694 tabstrip.CloseAllTabs(); 695 EXPECT_TRUE(tabstrip.empty()); 696 } 697 698 // Tests inserting tabs with InsertAfter set to false. 699 TEST_F(TabStripModelTest, InsertBefore) { 700 TabStripDummyDelegate delegate(NULL); 701 TabStripModel tabstrip(&delegate, profile()); 702 tabstrip.SetInsertionPolicy(TabStripModel::INSERT_BEFORE); 703 EXPECT_TRUE(tabstrip.empty()); 704 705 TabContentsWrapper* contents1 = CreateTabContents(); 706 TabContentsWrapper* contents2 = CreateTabContents(); 707 TabContentsWrapper* contents3 = CreateTabContents(); 708 709 InsertTabContentses(&tabstrip, contents1, contents2, contents3); 710 711 // The order should be reversed. 712 EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(0)); 713 EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(1)); 714 EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(2)); 715 716 tabstrip.CloseAllTabs(); 717 EXPECT_TRUE(tabstrip.empty()); 718 } 719 720 // Tests opening background tabs with InsertAfter set to false. 721 TEST_F(TabStripModelTest, InsertBeforeOpeners) { 722 TabStripDummyDelegate delegate(NULL); 723 TabStripModel tabstrip(&delegate, profile()); 724 tabstrip.SetInsertionPolicy(TabStripModel::INSERT_BEFORE); 725 EXPECT_TRUE(tabstrip.empty()); 726 TabContentsWrapper* opener_contents = CreateTabContents(); 727 tabstrip.AppendTabContents(opener_contents, true); 728 729 TabContentsWrapper* contents1 = CreateTabContents(); 730 TabContentsWrapper* contents2 = CreateTabContents(); 731 TabContentsWrapper* contents3 = CreateTabContents(); 732 733 InsertTabContentses(&tabstrip, contents1, contents2, contents3); 734 735 // The order should be reversed. 736 EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(0)); 737 EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(1)); 738 EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(2)); 739 740 tabstrip.CloseAllTabs(); 741 EXPECT_TRUE(tabstrip.empty()); 742 } 743 744 // This test constructs a tabstrip, and then simulates loading several tabs in 745 // the background from link clicks on the first tab. Then it simulates opening 746 // a new tab from the first tab in the foreground via a link click, verifies 747 // that this tab is opened adjacent to the opener, then closes it. 748 // Finally it tests that a tab opened for some non-link purpose openes at the 749 // end of the strip, not bundled to any existing context. 750 TEST_F(TabStripModelTest, TestInsertionIndexDetermination) { 751 TabStripDummyDelegate delegate(NULL); 752 TabStripModel tabstrip(&delegate, profile()); 753 EXPECT_TRUE(tabstrip.empty()); 754 755 TabContentsWrapper* opener_contents = CreateTabContents(); 756 NavigationController* opener = &opener_contents->controller(); 757 tabstrip.AppendTabContents(opener_contents, true); 758 759 // Open some other random unrelated tab in the background to monkey with our 760 // insertion index. 761 TabContentsWrapper* other_contents = CreateTabContents(); 762 tabstrip.AppendTabContents(other_contents, false); 763 764 TabContentsWrapper* contents1 = CreateTabContents(); 765 TabContentsWrapper* contents2 = CreateTabContents(); 766 TabContentsWrapper* contents3 = CreateTabContents(); 767 768 // Start by testing LTR 769 InsertTabContentses(&tabstrip, contents1, contents2, contents3); 770 EXPECT_EQ(opener_contents, tabstrip.GetTabContentsAt(0)); 771 EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1)); 772 EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(2)); 773 EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(3)); 774 EXPECT_EQ(other_contents, tabstrip.GetTabContentsAt(4)); 775 776 // The opener API should work... 777 EXPECT_EQ(3, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 2, false)); 778 EXPECT_EQ(2, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 3, false)); 779 EXPECT_EQ(3, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1)); 780 781 // Now open a foreground tab from a link. It should be opened adjacent to the 782 // opener tab. 783 TabContentsWrapper* fg_link_contents = CreateTabContents(); 784 int insert_index = tabstrip.order_controller()->DetermineInsertionIndex( 785 fg_link_contents, PageTransition::LINK, true); 786 EXPECT_EQ(1, insert_index); 787 tabstrip.InsertTabContentsAt(insert_index, fg_link_contents, 788 TabStripModel::ADD_ACTIVE | 789 TabStripModel::ADD_INHERIT_GROUP); 790 EXPECT_EQ(1, tabstrip.active_index()); 791 EXPECT_EQ(fg_link_contents, tabstrip.GetSelectedTabContents()); 792 793 // Now close this contents. The selection should move to the opener contents. 794 tabstrip.CloseSelectedTabs(); 795 EXPECT_EQ(0, tabstrip.active_index()); 796 797 // Now open a new empty tab. It should open at the end of the strip. 798 TabContentsWrapper* fg_nonlink_contents = CreateTabContents(); 799 insert_index = tabstrip.order_controller()->DetermineInsertionIndex( 800 fg_nonlink_contents, PageTransition::AUTO_BOOKMARK, true); 801 EXPECT_EQ(tabstrip.count(), insert_index); 802 // We break the opener relationship... 803 tabstrip.InsertTabContentsAt(insert_index, fg_nonlink_contents, 804 TabStripModel::ADD_NONE); 805 // Now select it, so that user_gesture == true causes the opener relationship 806 // to be forgotten... 807 tabstrip.ActivateTabAt(tabstrip.count() - 1, true); 808 EXPECT_EQ(tabstrip.count() - 1, tabstrip.active_index()); 809 EXPECT_EQ(fg_nonlink_contents, tabstrip.GetSelectedTabContents()); 810 811 // Verify that all opener relationships are forgotten. 812 EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 2, false)); 813 EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 3, false)); 814 EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 3, false)); 815 EXPECT_EQ(-1, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1)); 816 817 tabstrip.CloseAllTabs(); 818 EXPECT_TRUE(tabstrip.empty()); 819 } 820 821 // Tests that selection is shifted to the correct tab when a tab is closed. 822 // If a tab is in the background when it is closed, the selection does not 823 // change. 824 // If a tab is in the foreground (selected), 825 // If that tab does not have an opener, selection shifts to the right. 826 // If the tab has an opener, 827 // The next tab (scanning LTR) in the entire strip that has the same opener 828 // is selected 829 // If there are no other tabs that have the same opener, 830 // The opener is selected 831 // 832 TEST_F(TabStripModelTest, TestSelectOnClose) { 833 TabStripDummyDelegate delegate(NULL); 834 TabStripModel tabstrip(&delegate, profile()); 835 EXPECT_TRUE(tabstrip.empty()); 836 837 TabContentsWrapper* opener_contents = CreateTabContents(); 838 tabstrip.AppendTabContents(opener_contents, true); 839 840 TabContentsWrapper* contents1 = CreateTabContents(); 841 TabContentsWrapper* contents2 = CreateTabContents(); 842 TabContentsWrapper* contents3 = CreateTabContents(); 843 844 // Note that we use Detach instead of Close throughout this test to avoid 845 // having to keep reconstructing these TabContentses. 846 847 // First test that closing tabs that are in the background doesn't adjust the 848 // current selection. 849 InsertTabContentses(&tabstrip, contents1, contents2, contents3); 850 EXPECT_EQ(0, tabstrip.active_index()); 851 852 tabstrip.DetachTabContentsAt(1); 853 EXPECT_EQ(0, tabstrip.active_index()); 854 855 for (int i = tabstrip.count() - 1; i >= 1; --i) 856 tabstrip.DetachTabContentsAt(i); 857 858 // Now test that when a tab doesn't have an opener, selection shifts to the 859 // right when the tab is closed. 860 InsertTabContentses(&tabstrip, contents1, contents2, contents3); 861 EXPECT_EQ(0, tabstrip.active_index()); 862 863 tabstrip.ForgetAllOpeners(); 864 tabstrip.ActivateTabAt(1, true); 865 EXPECT_EQ(1, tabstrip.active_index()); 866 tabstrip.DetachTabContentsAt(1); 867 EXPECT_EQ(1, tabstrip.active_index()); 868 tabstrip.DetachTabContentsAt(1); 869 EXPECT_EQ(1, tabstrip.active_index()); 870 tabstrip.DetachTabContentsAt(1); 871 EXPECT_EQ(0, tabstrip.active_index()); 872 873 for (int i = tabstrip.count() - 1; i >= 1; --i) 874 tabstrip.DetachTabContentsAt(i); 875 876 // Now test that when a tab does have an opener, it selects the next tab 877 // opened by the same opener scanning LTR when it is closed. 878 InsertTabContentses(&tabstrip, contents1, contents2, contents3); 879 EXPECT_EQ(0, tabstrip.active_index()); 880 tabstrip.ActivateTabAt(2, false); 881 EXPECT_EQ(2, tabstrip.active_index()); 882 tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE); 883 EXPECT_EQ(2, tabstrip.active_index()); 884 tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE); 885 EXPECT_EQ(1, tabstrip.active_index()); 886 tabstrip.CloseTabContentsAt(1, TabStripModel::CLOSE_NONE); 887 EXPECT_EQ(0, tabstrip.active_index()); 888 // Finally test that when a tab has no "siblings" that the opener is 889 // selected. 890 TabContentsWrapper* other_contents = CreateTabContents(); 891 tabstrip.InsertTabContentsAt(1, other_contents, TabStripModel::ADD_NONE); 892 EXPECT_EQ(2, tabstrip.count()); 893 TabContentsWrapper* opened_contents = CreateTabContents(); 894 tabstrip.InsertTabContentsAt(2, opened_contents, 895 TabStripModel::ADD_ACTIVE | 896 TabStripModel::ADD_INHERIT_GROUP); 897 EXPECT_EQ(2, tabstrip.active_index()); 898 tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE); 899 EXPECT_EQ(0, tabstrip.active_index()); 900 901 tabstrip.CloseAllTabs(); 902 EXPECT_TRUE(tabstrip.empty()); 903 } 904 905 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with 906 // CommandCloseTab. 907 TEST_F(TabStripModelTest, CommandCloseTab) { 908 TabStripDummyDelegate delegate(NULL); 909 TabStripModel tabstrip(&delegate, profile()); 910 EXPECT_TRUE(tabstrip.empty()); 911 912 // Make sure can_close is honored. 913 ASSERT_NO_FATAL_FAILURE( 914 PrepareTabstripForSelectionTest(&tabstrip, 1, 0, "0")); 915 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 916 0, TabStripModel::CommandCloseTab)); 917 delegate.set_can_close(false); 918 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 919 0, TabStripModel::CommandCloseTab)); 920 delegate.set_can_close(true); 921 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab); 922 ASSERT_TRUE(tabstrip.empty()); 923 924 // Make sure close on a tab that is selected effects all the selected tabs. 925 ASSERT_NO_FATAL_FAILURE( 926 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1")); 927 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 928 0, TabStripModel::CommandCloseTab)); 929 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab); 930 // Should have closed tabs 0 and 1. 931 EXPECT_EQ("2", GetPinnedState(tabstrip)); 932 933 tabstrip.CloseAllTabs(); 934 EXPECT_TRUE(tabstrip.empty()); 935 936 // Select two tabs and make close on a tab that isn't selected doesn't effect 937 // selected tabs. 938 ASSERT_NO_FATAL_FAILURE( 939 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1")); 940 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 941 2, TabStripModel::CommandCloseTab)); 942 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab); 943 // Should have closed tab 2. 944 EXPECT_EQ("0 1", GetPinnedState(tabstrip)); 945 tabstrip.CloseAllTabs(); 946 EXPECT_TRUE(tabstrip.empty()); 947 948 // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned. 949 ASSERT_NO_FATAL_FAILURE( 950 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1")); 951 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 952 0, TabStripModel::CommandCloseTab)); 953 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab); 954 // Should have closed tab 2. 955 EXPECT_EQ("2", GetPinnedState(tabstrip)); 956 tabstrip.CloseAllTabs(); 957 EXPECT_TRUE(tabstrip.empty()); 958 } 959 960 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with 961 // CommandCloseTabs. 962 TEST_F(TabStripModelTest, CommandCloseOtherTabs) { 963 TabStripDummyDelegate delegate(NULL); 964 TabStripModel tabstrip(&delegate, profile()); 965 EXPECT_TRUE(tabstrip.empty()); 966 967 // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled 968 // and close two tabs. 969 ASSERT_NO_FATAL_FAILURE( 970 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1")); 971 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 972 0, TabStripModel::CommandCloseOtherTabs)); 973 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs); 974 EXPECT_EQ("0 1", GetPinnedState(tabstrip)); 975 tabstrip.CloseAllTabs(); 976 EXPECT_TRUE(tabstrip.empty()); 977 978 // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it 979 // with a non-selected index should close the two other tabs. 980 ASSERT_NO_FATAL_FAILURE( 981 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1")); 982 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 983 2, TabStripModel::CommandCloseOtherTabs)); 984 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs); 985 EXPECT_EQ("0 1", GetPinnedState(tabstrip)); 986 tabstrip.CloseAllTabs(); 987 EXPECT_TRUE(tabstrip.empty()); 988 989 // Select all, CommandCloseOtherTabs should not be enabled. 990 ASSERT_NO_FATAL_FAILURE( 991 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1 2")); 992 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled( 993 2, TabStripModel::CommandCloseOtherTabs)); 994 tabstrip.CloseAllTabs(); 995 EXPECT_TRUE(tabstrip.empty()); 996 997 // Three tabs, pin one, select the two non-pinned. 998 ASSERT_NO_FATAL_FAILURE( 999 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1 2")); 1000 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled( 1001 1, TabStripModel::CommandCloseOtherTabs)); 1002 // If we don't pass in the pinned index, the command should be enabled. 1003 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1004 0, TabStripModel::CommandCloseOtherTabs)); 1005 tabstrip.CloseAllTabs(); 1006 EXPECT_TRUE(tabstrip.empty()); 1007 1008 // 3 tabs, one pinned. 1009 ASSERT_NO_FATAL_FAILURE( 1010 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1")); 1011 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1012 1, TabStripModel::CommandCloseOtherTabs)); 1013 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1014 0, TabStripModel::CommandCloseOtherTabs)); 1015 tabstrip.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs); 1016 // The pinned tab shouldn't be closed. 1017 EXPECT_EQ("0p 1", GetPinnedState(tabstrip)); 1018 tabstrip.CloseAllTabs(); 1019 EXPECT_TRUE(tabstrip.empty()); 1020 } 1021 1022 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with 1023 // CommandCloseTabsToRight. 1024 TEST_F(TabStripModelTest, CommandCloseTabsToRight) { 1025 TabStripDummyDelegate delegate(NULL); 1026 TabStripModel tabstrip(&delegate, profile()); 1027 EXPECT_TRUE(tabstrip.empty()); 1028 1029 // Create three tabs, select last two tabs, CommandCloseTabsToRight should 1030 // only be enabled for the first tab. 1031 ASSERT_NO_FATAL_FAILURE( 1032 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "1 2")); 1033 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1034 0, TabStripModel::CommandCloseTabsToRight)); 1035 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled( 1036 1, TabStripModel::CommandCloseTabsToRight)); 1037 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled( 1038 2, TabStripModel::CommandCloseTabsToRight)); 1039 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight); 1040 EXPECT_EQ("0", GetPinnedState(tabstrip)); 1041 tabstrip.CloseAllTabs(); 1042 EXPECT_TRUE(tabstrip.empty()); 1043 } 1044 1045 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with 1046 // CommandTogglePinned. 1047 TEST_F(TabStripModelTest, CommandTogglePinned) { 1048 TabStripDummyDelegate delegate(NULL); 1049 TabStripModel tabstrip(&delegate, profile()); 1050 EXPECT_TRUE(tabstrip.empty()); 1051 1052 // Create three tabs with one pinned, pin the first two. 1053 ASSERT_NO_FATAL_FAILURE( 1054 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1")); 1055 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1056 0, TabStripModel::CommandTogglePinned)); 1057 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1058 1, TabStripModel::CommandTogglePinned)); 1059 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1060 2, TabStripModel::CommandTogglePinned)); 1061 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned); 1062 EXPECT_EQ("0p 1p 2", GetPinnedState(tabstrip)); 1063 1064 // Execute CommandTogglePinned again, this should unpin. 1065 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned); 1066 EXPECT_EQ("0 1 2", GetPinnedState(tabstrip)); 1067 1068 // Pin the last. 1069 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned); 1070 EXPECT_EQ("2p 0 1", GetPinnedState(tabstrip)); 1071 1072 tabstrip.CloseAllTabs(); 1073 EXPECT_TRUE(tabstrip.empty()); 1074 } 1075 1076 // Tests the following context menu commands: 1077 // - Close Tab 1078 // - Close Other Tabs 1079 // - Close Tabs To Right 1080 TEST_F(TabStripModelTest, TestContextMenuCloseCommands) { 1081 TabStripDummyDelegate delegate(NULL); 1082 TabStripModel tabstrip(&delegate, profile()); 1083 EXPECT_TRUE(tabstrip.empty()); 1084 1085 TabContentsWrapper* opener_contents = CreateTabContents(); 1086 tabstrip.AppendTabContents(opener_contents, true); 1087 1088 TabContentsWrapper* contents1 = CreateTabContents(); 1089 TabContentsWrapper* contents2 = CreateTabContents(); 1090 TabContentsWrapper* contents3 = CreateTabContents(); 1091 1092 InsertTabContentses(&tabstrip, contents1, contents2, contents3); 1093 EXPECT_EQ(0, tabstrip.active_index()); 1094 1095 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab); 1096 EXPECT_EQ(3, tabstrip.count()); 1097 1098 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight); 1099 EXPECT_EQ(1, tabstrip.count()); 1100 EXPECT_EQ(opener_contents, tabstrip.GetSelectedTabContents()); 1101 1102 TabContentsWrapper* dummy_contents = CreateTabContents(); 1103 tabstrip.AppendTabContents(dummy_contents, false); 1104 1105 contents1 = CreateTabContents(); 1106 contents2 = CreateTabContents(); 1107 contents3 = CreateTabContents(); 1108 InsertTabContentses(&tabstrip, contents1, contents2, contents3); 1109 EXPECT_EQ(5, tabstrip.count()); 1110 1111 int dummy_index = tabstrip.count() - 1; 1112 tabstrip.ActivateTabAt(dummy_index, true); 1113 EXPECT_EQ(dummy_contents, tabstrip.GetSelectedTabContents()); 1114 1115 tabstrip.ExecuteContextMenuCommand(dummy_index, 1116 TabStripModel::CommandCloseOtherTabs); 1117 EXPECT_EQ(1, tabstrip.count()); 1118 EXPECT_EQ(dummy_contents, tabstrip.GetSelectedTabContents()); 1119 1120 tabstrip.CloseAllTabs(); 1121 EXPECT_TRUE(tabstrip.empty()); 1122 } 1123 1124 // Tests GetIndicesClosedByCommand. 1125 TEST_F(TabStripModelTest, GetIndicesClosedByCommand) { 1126 TabStripDummyDelegate delegate(NULL); 1127 TabStripModel tabstrip(&delegate, profile()); 1128 EXPECT_TRUE(tabstrip.empty()); 1129 1130 TabContentsWrapper* contents1 = CreateTabContents(); 1131 TabContentsWrapper* contents2 = CreateTabContents(); 1132 TabContentsWrapper* contents3 = CreateTabContents(); 1133 TabContentsWrapper* contents4 = CreateTabContents(); 1134 TabContentsWrapper* contents5 = CreateTabContents(); 1135 1136 tabstrip.AppendTabContents(contents1, true); 1137 tabstrip.AppendTabContents(contents2, true); 1138 tabstrip.AppendTabContents(contents3, true); 1139 tabstrip.AppendTabContents(contents4, true); 1140 tabstrip.AppendTabContents(contents5, true); 1141 1142 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString( 1143 tabstrip, 0, TabStripModel::CommandCloseTabsToRight)); 1144 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString( 1145 tabstrip, 1, TabStripModel::CommandCloseTabsToRight)); 1146 1147 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString( 1148 tabstrip, 0, TabStripModel::CommandCloseOtherTabs)); 1149 EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString( 1150 tabstrip, 1, TabStripModel::CommandCloseOtherTabs)); 1151 1152 // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other 1153 // commands. 1154 tabstrip.SetTabPinned(0, true); 1155 tabstrip.SetTabPinned(1, true); 1156 1157 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString( 1158 tabstrip, 0, TabStripModel::CommandCloseTabsToRight)); 1159 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString( 1160 tabstrip, 2, TabStripModel::CommandCloseTabsToRight)); 1161 1162 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString( 1163 tabstrip, 0, TabStripModel::CommandCloseOtherTabs)); 1164 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString( 1165 tabstrip, 2, TabStripModel::CommandCloseOtherTabs)); 1166 1167 tabstrip.CloseAllTabs(); 1168 EXPECT_TRUE(tabstrip.empty()); 1169 } 1170 1171 // Tests whether or not TabContentses are inserted in the correct position 1172 // using this "smart" function with a simulated middle click action on a series 1173 // of links on the home page. 1174 TEST_F(TabStripModelTest, AddTabContents_MiddleClickLinksAndClose) { 1175 TabStripDummyDelegate delegate(NULL); 1176 TabStripModel tabstrip(&delegate, profile()); 1177 EXPECT_TRUE(tabstrip.empty()); 1178 1179 // Open the Home Page 1180 TabContentsWrapper* homepage_contents = CreateTabContents(); 1181 tabstrip.AddTabContents( 1182 homepage_contents, -1, PageTransition::AUTO_BOOKMARK, 1183 TabStripModel::ADD_ACTIVE); 1184 1185 // Open some other tab, by user typing. 1186 TabContentsWrapper* typed_page_contents = CreateTabContents(); 1187 tabstrip.AddTabContents( 1188 typed_page_contents, -1, PageTransition::TYPED, 1189 TabStripModel::ADD_ACTIVE); 1190 1191 EXPECT_EQ(2, tabstrip.count()); 1192 1193 // Re-select the home page. 1194 tabstrip.ActivateTabAt(0, true); 1195 1196 // Open a bunch of tabs by simulating middle clicking on links on the home 1197 // page. 1198 TabContentsWrapper* middle_click_contents1 = CreateTabContents(); 1199 tabstrip.AddTabContents( 1200 middle_click_contents1, -1, PageTransition::LINK, 1201 TabStripModel::ADD_NONE); 1202 TabContentsWrapper* middle_click_contents2 = CreateTabContents(); 1203 tabstrip.AddTabContents( 1204 middle_click_contents2, -1, PageTransition::LINK, 1205 TabStripModel::ADD_NONE); 1206 TabContentsWrapper* middle_click_contents3 = CreateTabContents(); 1207 tabstrip.AddTabContents( 1208 middle_click_contents3, -1, PageTransition::LINK, 1209 TabStripModel::ADD_NONE); 1210 1211 EXPECT_EQ(5, tabstrip.count()); 1212 1213 EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0)); 1214 EXPECT_EQ(middle_click_contents1, tabstrip.GetTabContentsAt(1)); 1215 EXPECT_EQ(middle_click_contents2, tabstrip.GetTabContentsAt(2)); 1216 EXPECT_EQ(middle_click_contents3, tabstrip.GetTabContentsAt(3)); 1217 EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(4)); 1218 1219 // Now simulate seleting a tab in the middle of the group of tabs opened from 1220 // the home page and start closing them. Each TabContents in the group should 1221 // be closed, right to left. This test is constructed to start at the middle 1222 // TabContents in the group to make sure the cursor wraps around to the first 1223 // TabContents in the group before closing the opener or any other 1224 // TabContents. 1225 tabstrip.ActivateTabAt(2, true); 1226 tabstrip.CloseSelectedTabs(); 1227 EXPECT_EQ(middle_click_contents3, tabstrip.GetSelectedTabContents()); 1228 tabstrip.CloseSelectedTabs(); 1229 EXPECT_EQ(middle_click_contents1, tabstrip.GetSelectedTabContents()); 1230 tabstrip.CloseSelectedTabs(); 1231 EXPECT_EQ(homepage_contents, tabstrip.GetSelectedTabContents()); 1232 tabstrip.CloseSelectedTabs(); 1233 EXPECT_EQ(typed_page_contents, tabstrip.GetSelectedTabContents()); 1234 1235 EXPECT_EQ(1, tabstrip.count()); 1236 1237 tabstrip.CloseAllTabs(); 1238 EXPECT_TRUE(tabstrip.empty()); 1239 } 1240 1241 // Tests whether or not a TabContents created by a left click on a link that 1242 // opens a new tab is inserted correctly adjacent to the tab that spawned it. 1243 TEST_F(TabStripModelTest, AddTabContents_LeftClickPopup) { 1244 TabStripDummyDelegate delegate(NULL); 1245 TabStripModel tabstrip(&delegate, profile()); 1246 EXPECT_TRUE(tabstrip.empty()); 1247 1248 // Open the Home Page 1249 TabContentsWrapper* homepage_contents = CreateTabContents(); 1250 tabstrip.AddTabContents( 1251 homepage_contents, -1, PageTransition::AUTO_BOOKMARK, 1252 TabStripModel::ADD_ACTIVE); 1253 1254 // Open some other tab, by user typing. 1255 TabContentsWrapper* typed_page_contents = CreateTabContents(); 1256 tabstrip.AddTabContents( 1257 typed_page_contents, -1, PageTransition::TYPED, 1258 TabStripModel::ADD_ACTIVE); 1259 1260 EXPECT_EQ(2, tabstrip.count()); 1261 1262 // Re-select the home page. 1263 tabstrip.ActivateTabAt(0, true); 1264 1265 // Open a tab by simulating a left click on a link that opens in a new tab. 1266 TabContentsWrapper* left_click_contents = CreateTabContents(); 1267 tabstrip.AddTabContents(left_click_contents, -1, PageTransition::LINK, 1268 TabStripModel::ADD_ACTIVE); 1269 1270 // Verify the state meets our expectations. 1271 EXPECT_EQ(3, tabstrip.count()); 1272 EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0)); 1273 EXPECT_EQ(left_click_contents, tabstrip.GetTabContentsAt(1)); 1274 EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(2)); 1275 1276 // The newly created tab should be selected. 1277 EXPECT_EQ(left_click_contents, tabstrip.GetSelectedTabContents()); 1278 1279 // After closing the selected tab, the selection should move to the left, to 1280 // the opener. 1281 tabstrip.CloseSelectedTabs(); 1282 EXPECT_EQ(homepage_contents, tabstrip.GetSelectedTabContents()); 1283 1284 EXPECT_EQ(2, tabstrip.count()); 1285 1286 tabstrip.CloseAllTabs(); 1287 EXPECT_TRUE(tabstrip.empty()); 1288 } 1289 1290 // Tests whether or not new tabs that should split context (typed pages, 1291 // generated urls, also blank tabs) open at the end of the tabstrip instead of 1292 // in the middle. 1293 TEST_F(TabStripModelTest, AddTabContents_CreateNewBlankTab) { 1294 TabStripDummyDelegate delegate(NULL); 1295 TabStripModel tabstrip(&delegate, profile()); 1296 EXPECT_TRUE(tabstrip.empty()); 1297 1298 // Open the Home Page 1299 TabContentsWrapper* homepage_contents = CreateTabContents(); 1300 tabstrip.AddTabContents( 1301 homepage_contents, -1, PageTransition::AUTO_BOOKMARK, 1302 TabStripModel::ADD_ACTIVE); 1303 1304 // Open some other tab, by user typing. 1305 TabContentsWrapper* typed_page_contents = CreateTabContents(); 1306 tabstrip.AddTabContents( 1307 typed_page_contents, -1, PageTransition::TYPED, 1308 TabStripModel::ADD_ACTIVE); 1309 1310 EXPECT_EQ(2, tabstrip.count()); 1311 1312 // Re-select the home page. 1313 tabstrip.ActivateTabAt(0, true); 1314 1315 // Open a new blank tab in the foreground. 1316 TabContentsWrapper* new_blank_contents = CreateTabContents(); 1317 tabstrip.AddTabContents(new_blank_contents, -1, PageTransition::TYPED, 1318 TabStripModel::ADD_ACTIVE); 1319 1320 // Verify the state of the tabstrip. 1321 EXPECT_EQ(3, tabstrip.count()); 1322 EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0)); 1323 EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(1)); 1324 EXPECT_EQ(new_blank_contents, tabstrip.GetTabContentsAt(2)); 1325 1326 // Now open a couple more blank tabs in the background. 1327 TabContentsWrapper* background_blank_contents1 = CreateTabContents(); 1328 tabstrip.AddTabContents( 1329 background_blank_contents1, -1, PageTransition::TYPED, 1330 TabStripModel::ADD_NONE); 1331 TabContentsWrapper* background_blank_contents2 = CreateTabContents(); 1332 tabstrip.AddTabContents( 1333 background_blank_contents2, -1, PageTransition::GENERATED, 1334 TabStripModel::ADD_NONE); 1335 EXPECT_EQ(5, tabstrip.count()); 1336 EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0)); 1337 EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(1)); 1338 EXPECT_EQ(new_blank_contents, tabstrip.GetTabContentsAt(2)); 1339 EXPECT_EQ(background_blank_contents1, tabstrip.GetTabContentsAt(3)); 1340 EXPECT_EQ(background_blank_contents2, tabstrip.GetTabContentsAt(4)); 1341 1342 tabstrip.CloseAllTabs(); 1343 EXPECT_TRUE(tabstrip.empty()); 1344 } 1345 1346 // Tests whether opener state is correctly forgotten when the user switches 1347 // context. 1348 TEST_F(TabStripModelTest, AddTabContents_ForgetOpeners) { 1349 TabStripDummyDelegate delegate(NULL); 1350 TabStripModel tabstrip(&delegate, profile()); 1351 EXPECT_TRUE(tabstrip.empty()); 1352 1353 // Open the Home Page 1354 TabContentsWrapper* homepage_contents = CreateTabContents(); 1355 tabstrip.AddTabContents( 1356 homepage_contents, -1, PageTransition::AUTO_BOOKMARK, 1357 TabStripModel::ADD_ACTIVE); 1358 1359 // Open some other tab, by user typing. 1360 TabContentsWrapper* typed_page_contents = CreateTabContents(); 1361 tabstrip.AddTabContents( 1362 typed_page_contents, -1, PageTransition::TYPED, 1363 TabStripModel::ADD_ACTIVE); 1364 1365 EXPECT_EQ(2, tabstrip.count()); 1366 1367 // Re-select the home page. 1368 tabstrip.ActivateTabAt(0, true); 1369 1370 // Open a bunch of tabs by simulating middle clicking on links on the home 1371 // page. 1372 TabContentsWrapper* middle_click_contents1 = CreateTabContents(); 1373 tabstrip.AddTabContents( 1374 middle_click_contents1, -1, PageTransition::LINK, 1375 TabStripModel::ADD_NONE); 1376 TabContentsWrapper* middle_click_contents2 = CreateTabContents(); 1377 tabstrip.AddTabContents( 1378 middle_click_contents2, -1, PageTransition::LINK, 1379 TabStripModel::ADD_NONE); 1380 TabContentsWrapper* middle_click_contents3 = CreateTabContents(); 1381 tabstrip.AddTabContents( 1382 middle_click_contents3, -1, PageTransition::LINK, 1383 TabStripModel::ADD_NONE); 1384 1385 // Break out of the context by selecting a tab in a different context. 1386 EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(4)); 1387 tabstrip.SelectLastTab(); 1388 EXPECT_EQ(typed_page_contents, tabstrip.GetSelectedTabContents()); 1389 1390 // Step back into the context by selecting a tab inside it. 1391 tabstrip.ActivateTabAt(2, true); 1392 EXPECT_EQ(middle_click_contents2, tabstrip.GetSelectedTabContents()); 1393 1394 // Now test that closing tabs selects to the right until there are no more, 1395 // then to the left, as if there were no context (context has been 1396 // successfully forgotten). 1397 tabstrip.CloseSelectedTabs(); 1398 EXPECT_EQ(middle_click_contents3, tabstrip.GetSelectedTabContents()); 1399 tabstrip.CloseSelectedTabs(); 1400 EXPECT_EQ(typed_page_contents, tabstrip.GetSelectedTabContents()); 1401 tabstrip.CloseSelectedTabs(); 1402 EXPECT_EQ(middle_click_contents1, tabstrip.GetSelectedTabContents()); 1403 tabstrip.CloseSelectedTabs(); 1404 EXPECT_EQ(homepage_contents, tabstrip.GetSelectedTabContents()); 1405 1406 EXPECT_EQ(1, tabstrip.count()); 1407 1408 tabstrip.CloseAllTabs(); 1409 EXPECT_TRUE(tabstrip.empty()); 1410 } 1411 1412 // Added for http://b/issue?id=958960 1413 TEST_F(TabStripModelTest, AppendContentsReselectionTest) { 1414 TabContents* fake_destinations_tab = 1415 new TabContents(profile(), NULL, 0, NULL, NULL); 1416 TabContentsWrapper wrapper(fake_destinations_tab); 1417 TabStripDummyDelegate delegate(&wrapper); 1418 TabStripModel tabstrip(&delegate, profile()); 1419 EXPECT_TRUE(tabstrip.empty()); 1420 1421 // Open the Home Page 1422 TabContentsWrapper* homepage_contents = CreateTabContents(); 1423 tabstrip.AddTabContents( 1424 homepage_contents, -1, PageTransition::AUTO_BOOKMARK, 1425 TabStripModel::ADD_ACTIVE); 1426 1427 // Open some other tab, by user typing. 1428 TabContentsWrapper* typed_page_contents = CreateTabContents(); 1429 tabstrip.AddTabContents( 1430 typed_page_contents, -1, PageTransition::TYPED, 1431 TabStripModel::ADD_NONE); 1432 1433 // The selected tab should still be the first. 1434 EXPECT_EQ(0, tabstrip.active_index()); 1435 1436 // Now simulate a link click that opens a new tab (by virtue of target=_blank) 1437 // and make sure the right tab gets selected when the new tab is closed. 1438 TabContentsWrapper* target_blank_contents = CreateTabContents(); 1439 tabstrip.AppendTabContents(target_blank_contents, true); 1440 EXPECT_EQ(2, tabstrip.active_index()); 1441 tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE); 1442 EXPECT_EQ(0, tabstrip.active_index()); 1443 1444 // clean up after ourselves 1445 tabstrip.CloseAllTabs(); 1446 } 1447 1448 // Added for http://b/issue?id=1027661 1449 TEST_F(TabStripModelTest, ReselectionConsidersChildrenTest) { 1450 TabStripDummyDelegate delegate(NULL); 1451 TabStripModel strip(&delegate, profile()); 1452 1453 // Open page A 1454 TabContentsWrapper* page_a_contents = CreateTabContents(); 1455 strip.AddTabContents( 1456 page_a_contents, -1, PageTransition::AUTO_BOOKMARK, 1457 TabStripModel::ADD_ACTIVE); 1458 1459 // Simulate middle click to open page A.A and A.B 1460 TabContentsWrapper* page_a_a_contents = CreateTabContents(); 1461 strip.AddTabContents(page_a_a_contents, -1, PageTransition::LINK, 1462 TabStripModel::ADD_NONE); 1463 TabContentsWrapper* page_a_b_contents = CreateTabContents(); 1464 strip.AddTabContents(page_a_b_contents, -1, PageTransition::LINK, 1465 TabStripModel::ADD_NONE); 1466 1467 // Select page A.A 1468 strip.ActivateTabAt(1, true); 1469 EXPECT_EQ(page_a_a_contents, strip.GetSelectedTabContents()); 1470 1471 // Simulate a middle click to open page A.A.A 1472 TabContentsWrapper* page_a_a_a_contents = CreateTabContents(); 1473 strip.AddTabContents(page_a_a_a_contents, -1, PageTransition::LINK, 1474 TabStripModel::ADD_NONE); 1475 1476 EXPECT_EQ(page_a_a_a_contents, strip.GetTabContentsAt(2)); 1477 1478 // Close page A.A 1479 strip.CloseTabContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE); 1480 1481 // Page A.A.A should be selected, NOT A.B 1482 EXPECT_EQ(page_a_a_a_contents, strip.GetSelectedTabContents()); 1483 1484 // Close page A.A.A 1485 strip.CloseTabContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE); 1486 1487 // Page A.B should be selected 1488 EXPECT_EQ(page_a_b_contents, strip.GetSelectedTabContents()); 1489 1490 // Close page A.B 1491 strip.CloseTabContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE); 1492 1493 // Page A should be selected 1494 EXPECT_EQ(page_a_contents, strip.GetSelectedTabContents()); 1495 1496 // Clean up. 1497 strip.CloseAllTabs(); 1498 } 1499 1500 TEST_F(TabStripModelTest, AddTabContents_NewTabAtEndOfStripInheritsGroup) { 1501 TabStripDummyDelegate delegate(NULL); 1502 TabStripModel strip(&delegate, profile()); 1503 1504 // Open page A 1505 TabContentsWrapper* page_a_contents = CreateTabContents(); 1506 strip.AddTabContents(page_a_contents, -1, PageTransition::START_PAGE, 1507 TabStripModel::ADD_ACTIVE); 1508 1509 // Open pages B, C and D in the background from links on page A... 1510 TabContentsWrapper* page_b_contents = CreateTabContents(); 1511 TabContentsWrapper* page_c_contents = CreateTabContents(); 1512 TabContentsWrapper* page_d_contents = CreateTabContents(); 1513 strip.AddTabContents(page_b_contents, -1, PageTransition::LINK, 1514 TabStripModel::ADD_NONE); 1515 strip.AddTabContents(page_c_contents, -1, PageTransition::LINK, 1516 TabStripModel::ADD_NONE); 1517 strip.AddTabContents(page_d_contents, -1, PageTransition::LINK, 1518 TabStripModel::ADD_NONE); 1519 1520 // Switch to page B's tab. 1521 strip.ActivateTabAt(1, true); 1522 1523 // Open a New Tab at the end of the strip (simulate Ctrl+T) 1524 TabContentsWrapper* new_tab_contents = CreateTabContents(); 1525 strip.AddTabContents(new_tab_contents, -1, PageTransition::TYPED, 1526 TabStripModel::ADD_ACTIVE); 1527 1528 EXPECT_EQ(4, strip.GetIndexOfTabContents(new_tab_contents)); 1529 EXPECT_EQ(4, strip.active_index()); 1530 1531 // Close the New Tab that was just opened. We should be returned to page B's 1532 // Tab... 1533 strip.CloseTabContentsAt(4, TabStripModel::CLOSE_NONE); 1534 1535 EXPECT_EQ(1, strip.active_index()); 1536 1537 // Open a non-New Tab tab at the end of the strip, with a TYPED transition. 1538 // This is like typing a URL in the address bar and pressing Alt+Enter. The 1539 // behavior should be the same as above. 1540 TabContentsWrapper* page_e_contents = CreateTabContents(); 1541 strip.AddTabContents(page_e_contents, -1, PageTransition::TYPED, 1542 TabStripModel::ADD_ACTIVE); 1543 1544 EXPECT_EQ(4, strip.GetIndexOfTabContents(page_e_contents)); 1545 EXPECT_EQ(4, strip.active_index()); 1546 1547 // Close the Tab. Selection should shift back to page B's Tab. 1548 strip.CloseTabContentsAt(4, TabStripModel::CLOSE_NONE); 1549 1550 EXPECT_EQ(1, strip.active_index()); 1551 1552 // Open a non-New Tab tab at the end of the strip, with some other 1553 // transition. This is like right clicking on a bookmark and choosing "Open 1554 // in New Tab". No opener relationship should be preserved between this Tab 1555 // and the one that was active when the gesture was performed. 1556 TabContentsWrapper* page_f_contents = CreateTabContents(); 1557 strip.AddTabContents(page_f_contents, -1, PageTransition::AUTO_BOOKMARK, 1558 TabStripModel::ADD_ACTIVE); 1559 1560 EXPECT_EQ(4, strip.GetIndexOfTabContents(page_f_contents)); 1561 EXPECT_EQ(4, strip.active_index()); 1562 1563 // Close the Tab. The next-adjacent should be selected. 1564 strip.CloseTabContentsAt(4, TabStripModel::CLOSE_NONE); 1565 1566 EXPECT_EQ(3, strip.active_index()); 1567 1568 // Clean up. 1569 strip.CloseAllTabs(); 1570 } 1571 1572 // A test of navigations in a tab that is part of a group of opened from some 1573 // parent tab. If the navigations are link clicks, the group relationship of 1574 // the tab to its parent are preserved. If they are of any other type, they are 1575 // not preserved. 1576 TEST_F(TabStripModelTest, NavigationForgetsOpeners) { 1577 TabStripDummyDelegate delegate(NULL); 1578 TabStripModel strip(&delegate, profile()); 1579 1580 // Open page A 1581 TabContentsWrapper* page_a_contents = CreateTabContents(); 1582 strip.AddTabContents(page_a_contents, -1, PageTransition::START_PAGE, 1583 TabStripModel::ADD_ACTIVE); 1584 1585 // Open pages B, C and D in the background from links on page A... 1586 TabContentsWrapper* page_b_contents = CreateTabContents(); 1587 TabContentsWrapper* page_c_contents = CreateTabContents(); 1588 TabContentsWrapper* page_d_contents = CreateTabContents(); 1589 strip.AddTabContents(page_b_contents, -1, PageTransition::LINK, 1590 TabStripModel::ADD_NONE); 1591 strip.AddTabContents(page_c_contents, -1, PageTransition::LINK, 1592 TabStripModel::ADD_NONE); 1593 strip.AddTabContents(page_d_contents, -1, PageTransition::LINK, 1594 TabStripModel::ADD_NONE); 1595 1596 // Open page E in a different opener group from page A. 1597 TabContentsWrapper* page_e_contents = CreateTabContents(); 1598 strip.AddTabContents(page_e_contents, -1, PageTransition::START_PAGE, 1599 TabStripModel::ADD_NONE); 1600 1601 // Tell the TabStripModel that we are navigating page D via a link click. 1602 strip.ActivateTabAt(3, true); 1603 strip.TabNavigating(page_d_contents, PageTransition::LINK); 1604 1605 // Close page D, page C should be selected. (part of same group). 1606 strip.CloseTabContentsAt(3, TabStripModel::CLOSE_NONE); 1607 EXPECT_EQ(2, strip.active_index()); 1608 1609 // Tell the TabStripModel that we are navigating in page C via a bookmark. 1610 strip.TabNavigating(page_c_contents, PageTransition::AUTO_BOOKMARK); 1611 1612 // Close page C, page E should be selected. (C is no longer part of the 1613 // A-B-C-D group, selection moves to the right). 1614 strip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE); 1615 EXPECT_EQ(page_e_contents, strip.GetTabContentsAt(strip.active_index())); 1616 1617 strip.CloseAllTabs(); 1618 } 1619 1620 // A test that the forgetting behavior tested in NavigationForgetsOpeners above 1621 // doesn't cause the opener relationship for a New Tab opened at the end of the 1622 // TabStrip to be reset (Test 1 below), unless another any other tab is 1623 // seelcted (Test 2 below). 1624 TEST_F(TabStripModelTest, NavigationForgettingDoesntAffectNewTab) { 1625 TabStripDummyDelegate delegate(NULL); 1626 TabStripModel strip(&delegate, profile()); 1627 1628 // Open a tab and several tabs from it, then select one of the tabs that was 1629 // opened. 1630 TabContentsWrapper* page_a_contents = CreateTabContents(); 1631 strip.AddTabContents(page_a_contents, -1, PageTransition::START_PAGE, 1632 TabStripModel::ADD_ACTIVE); 1633 1634 TabContentsWrapper* page_b_contents = CreateTabContents(); 1635 TabContentsWrapper* page_c_contents = CreateTabContents(); 1636 TabContentsWrapper* page_d_contents = CreateTabContents(); 1637 strip.AddTabContents(page_b_contents, -1, PageTransition::LINK, 1638 TabStripModel::ADD_NONE); 1639 strip.AddTabContents(page_c_contents, -1, PageTransition::LINK, 1640 TabStripModel::ADD_NONE); 1641 strip.AddTabContents(page_d_contents, -1, PageTransition::LINK, 1642 TabStripModel::ADD_NONE); 1643 1644 strip.ActivateTabAt(2, true); 1645 1646 // TEST 1: If the user is in a group of tabs and opens a new tab at the end 1647 // of the strip, closing that new tab will select the tab that they were 1648 // last on. 1649 1650 // Now simulate opening a new tab at the end of the TabStrip. 1651 TabContentsWrapper* new_tab_contents1 = CreateTabContents(); 1652 strip.AddTabContents(new_tab_contents1, -1, PageTransition::TYPED, 1653 TabStripModel::ADD_ACTIVE); 1654 1655 // At this point, if we close this tab the last selected one should be 1656 // re-selected. 1657 strip.CloseTabContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE); 1658 EXPECT_EQ(page_c_contents, strip.GetTabContentsAt(strip.active_index())); 1659 1660 // TEST 2: If the user is in a group of tabs and opens a new tab at the end 1661 // of the strip, selecting any other tab in the strip will cause that new 1662 // tab's opener relationship to be forgotten. 1663 1664 // Open a new tab again. 1665 TabContentsWrapper* new_tab_contents2 = CreateTabContents(); 1666 strip.AddTabContents(new_tab_contents2, -1, PageTransition::TYPED, 1667 TabStripModel::ADD_ACTIVE); 1668 1669 // Now select the first tab. 1670 strip.ActivateTabAt(0, true); 1671 1672 // Now select the last tab. 1673 strip.ActivateTabAt(strip.count() - 1, true); 1674 1675 // Now close the last tab. The next adjacent should be selected. 1676 strip.CloseTabContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE); 1677 EXPECT_EQ(page_d_contents, strip.GetTabContentsAt(strip.active_index())); 1678 1679 strip.CloseAllTabs(); 1680 } 1681 1682 // Tests that fast shutdown is attempted appropriately. 1683 TEST_F(TabStripModelTest, FastShutdown) { 1684 TabStripDummyDelegate delegate(NULL); 1685 TabStripModel tabstrip(&delegate, profile()); 1686 MockTabStripModelObserver observer; 1687 tabstrip.AddObserver(&observer); 1688 1689 EXPECT_TRUE(tabstrip.empty()); 1690 1691 // Make sure fast shutdown is attempted when tabs that share a RPH are shut 1692 // down. 1693 { 1694 TabContentsWrapper* contents1 = CreateTabContents(); 1695 TabContentsWrapper* contents2 = 1696 CreateTabContentsWithSharedRPH(contents1->tab_contents()); 1697 1698 SetID(contents1->tab_contents(), 1); 1699 SetID(contents2->tab_contents(), 2); 1700 1701 tabstrip.AppendTabContents(contents1, true); 1702 tabstrip.AppendTabContents(contents2, true); 1703 1704 // Turn on the fake unload listener so the tabs don't actually get shut 1705 // down when we call CloseAllTabs()---we need to be able to check that 1706 // fast shutdown was attempted. 1707 delegate.set_run_unload_listener(true); 1708 tabstrip.CloseAllTabs(); 1709 // On a mock RPH this checks whether we *attempted* fast shutdown. 1710 // A real RPH would reject our attempt since there is an unload handler. 1711 EXPECT_TRUE(contents1->tab_contents()-> 1712 GetRenderProcessHost()->fast_shutdown_started()); 1713 EXPECT_EQ(2, tabstrip.count()); 1714 1715 delegate.set_run_unload_listener(false); 1716 tabstrip.CloseAllTabs(); 1717 EXPECT_TRUE(tabstrip.empty()); 1718 } 1719 1720 // Make sure fast shutdown is not attempted when only some tabs that share a 1721 // RPH are shut down. 1722 { 1723 TabContentsWrapper* contents1 = CreateTabContents(); 1724 TabContentsWrapper* contents2 = 1725 CreateTabContentsWithSharedRPH(contents1->tab_contents()); 1726 1727 SetID(contents1->tab_contents(), 1); 1728 SetID(contents2->tab_contents(), 2); 1729 1730 tabstrip.AppendTabContents(contents1, true); 1731 tabstrip.AppendTabContents(contents2, true); 1732 1733 tabstrip.CloseTabContentsAt(1, TabStripModel::CLOSE_NONE); 1734 EXPECT_FALSE(contents1->tab_contents()-> 1735 GetRenderProcessHost()->fast_shutdown_started()); 1736 EXPECT_EQ(1, tabstrip.count()); 1737 1738 tabstrip.CloseAllTabs(); 1739 EXPECT_TRUE(tabstrip.empty()); 1740 } 1741 } 1742 1743 // Tests various permutations of apps. 1744 TEST_F(TabStripModelTest, Apps) { 1745 TabStripDummyDelegate delegate(NULL); 1746 TabStripModel tabstrip(&delegate, profile()); 1747 MockTabStripModelObserver observer; 1748 tabstrip.AddObserver(&observer); 1749 1750 EXPECT_TRUE(tabstrip.empty()); 1751 1752 typedef MockTabStripModelObserver::State State; 1753 1754 #if defined(OS_WIN) 1755 FilePath path(FILE_PATH_LITERAL("c:\\foo")); 1756 #elif defined(OS_POSIX) 1757 FilePath path(FILE_PATH_LITERAL("/foo")); 1758 #endif 1759 scoped_refptr<Extension> extension_app(new Extension(path, 1760 Extension::INVALID)); 1761 extension_app->launch_web_url_ = "http://www.google.com"; 1762 TabContentsWrapper* contents1 = CreateTabContents(); 1763 contents1->extension_tab_helper()->SetExtensionApp(extension_app); 1764 TabContentsWrapper* contents2 = CreateTabContents(); 1765 contents2->extension_tab_helper()->SetExtensionApp(extension_app); 1766 TabContentsWrapper* contents3 = CreateTabContents(); 1767 1768 SetID(contents1->tab_contents(), 1); 1769 SetID(contents2->tab_contents(), 2); 1770 SetID(contents3->tab_contents(), 3); 1771 1772 // Note! The ordering of these tests is important, each subsequent test 1773 // builds on the state established in the previous. This is important if you 1774 // ever insert tests rather than append. 1775 1776 // Initial state, tab3 only and selected. 1777 tabstrip.AppendTabContents(contents3, true); 1778 1779 observer.ClearStates(); 1780 1781 // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal 1782 // position and tab1 should end up at position 0. 1783 { 1784 tabstrip.InsertTabContentsAt(1, contents1, TabStripModel::ADD_NONE); 1785 1786 ASSERT_EQ(1, observer.GetStateCount()); 1787 State state(contents1, 0, MockTabStripModelObserver::INSERT); 1788 EXPECT_TRUE(observer.StateEquals(0, state)); 1789 1790 // And verify the state. 1791 EXPECT_EQ("1ap 3", GetPinnedState(tabstrip)); 1792 1793 observer.ClearStates(); 1794 } 1795 1796 // Insert tab 2 at position 1. 1797 { 1798 tabstrip.InsertTabContentsAt(1, contents2, TabStripModel::ADD_NONE); 1799 1800 ASSERT_EQ(1, observer.GetStateCount()); 1801 State state(contents2, 1, MockTabStripModelObserver::INSERT); 1802 EXPECT_TRUE(observer.StateEquals(0, state)); 1803 1804 // And verify the state. 1805 EXPECT_EQ("1ap 2ap 3", GetPinnedState(tabstrip)); 1806 1807 observer.ClearStates(); 1808 } 1809 1810 // Try to move tab 3 to position 0. This isn't legal and should be ignored. 1811 { 1812 tabstrip.MoveTabContentsAt(2, 0, false); 1813 1814 ASSERT_EQ(0, observer.GetStateCount()); 1815 1816 // And verify the state didn't change. 1817 EXPECT_EQ("1ap 2ap 3", GetPinnedState(tabstrip)); 1818 1819 observer.ClearStates(); 1820 } 1821 1822 // Try to move tab 0 to position 3. This isn't legal and should be ignored. 1823 { 1824 tabstrip.MoveTabContentsAt(0, 2, false); 1825 1826 ASSERT_EQ(0, observer.GetStateCount()); 1827 1828 // And verify the state didn't change. 1829 EXPECT_EQ("1ap 2ap 3", GetPinnedState(tabstrip)); 1830 1831 observer.ClearStates(); 1832 } 1833 1834 // Try to move tab 0 to position 1. This is a legal move. 1835 { 1836 tabstrip.MoveTabContentsAt(0, 1, false); 1837 1838 ASSERT_EQ(1, observer.GetStateCount()); 1839 State state(contents1, 1, MockTabStripModelObserver::MOVE); 1840 state.src_index = 0; 1841 EXPECT_TRUE(observer.StateEquals(0, state)); 1842 1843 // And verify the state didn't change. 1844 EXPECT_EQ("2ap 1ap 3", GetPinnedState(tabstrip)); 1845 1846 observer.ClearStates(); 1847 } 1848 1849 // Remove tab3 and insert at position 0. It should be forced to position 2. 1850 { 1851 tabstrip.DetachTabContentsAt(2); 1852 observer.ClearStates(); 1853 1854 tabstrip.InsertTabContentsAt(0, contents3, TabStripModel::ADD_NONE); 1855 1856 ASSERT_EQ(1, observer.GetStateCount()); 1857 State state(contents3, 2, MockTabStripModelObserver::INSERT); 1858 EXPECT_TRUE(observer.StateEquals(0, state)); 1859 1860 // And verify the state didn't change. 1861 EXPECT_EQ("2ap 1ap 3", GetPinnedState(tabstrip)); 1862 1863 observer.ClearStates(); 1864 } 1865 1866 tabstrip.CloseAllTabs(); 1867 } 1868 1869 // Tests various permutations of pinning tabs. 1870 TEST_F(TabStripModelTest, Pinning) { 1871 TabStripDummyDelegate delegate(NULL); 1872 TabStripModel tabstrip(&delegate, profile()); 1873 MockTabStripModelObserver observer; 1874 tabstrip.AddObserver(&observer); 1875 1876 EXPECT_TRUE(tabstrip.empty()); 1877 1878 typedef MockTabStripModelObserver::State State; 1879 1880 TabContentsWrapper* contents1 = CreateTabContents(); 1881 TabContentsWrapper* contents2 = CreateTabContents(); 1882 TabContentsWrapper* contents3 = CreateTabContents(); 1883 1884 SetID(contents1->tab_contents(), 1); 1885 SetID(contents2->tab_contents(), 2); 1886 SetID(contents3->tab_contents(), 3); 1887 1888 // Note! The ordering of these tests is important, each subsequent test 1889 // builds on the state established in the previous. This is important if you 1890 // ever insert tests rather than append. 1891 1892 // Initial state, three tabs, first selected. 1893 tabstrip.AppendTabContents(contents1, true); 1894 tabstrip.AppendTabContents(contents2, false); 1895 tabstrip.AppendTabContents(contents3, false); 1896 1897 observer.ClearStates(); 1898 1899 // Pin the first tab, this shouldn't visually reorder anything. 1900 { 1901 tabstrip.SetTabPinned(0, true); 1902 1903 // As the order didn't change, we should get a pinned notification. 1904 ASSERT_EQ(1, observer.GetStateCount()); 1905 State state(contents1, 0, MockTabStripModelObserver::PINNED); 1906 EXPECT_TRUE(observer.StateEquals(0, state)); 1907 1908 // And verify the state. 1909 EXPECT_EQ("1p 2 3", GetPinnedState(tabstrip)); 1910 1911 observer.ClearStates(); 1912 } 1913 1914 // Unpin the first tab. 1915 { 1916 tabstrip.SetTabPinned(0, false); 1917 1918 // As the order didn't change, we should get a pinned notification. 1919 ASSERT_EQ(1, observer.GetStateCount()); 1920 State state(contents1, 0, MockTabStripModelObserver::PINNED); 1921 EXPECT_TRUE(observer.StateEquals(0, state)); 1922 1923 // And verify the state. 1924 EXPECT_EQ("1 2 3", GetPinnedState(tabstrip)); 1925 1926 observer.ClearStates(); 1927 } 1928 1929 // Pin the 3rd tab, which should move it to the front. 1930 { 1931 tabstrip.SetTabPinned(2, true); 1932 1933 // The pinning should have resulted in a move and a pinned notification. 1934 ASSERT_EQ(2, observer.GetStateCount()); 1935 State state(contents3, 0, MockTabStripModelObserver::MOVE); 1936 state.src_index = 2; 1937 EXPECT_TRUE(observer.StateEquals(0, state)); 1938 1939 state = State(contents3, 0, MockTabStripModelObserver::PINNED); 1940 EXPECT_TRUE(observer.StateEquals(1, state)); 1941 1942 // And verify the state. 1943 EXPECT_EQ("3p 1 2", GetPinnedState(tabstrip)); 1944 1945 observer.ClearStates(); 1946 } 1947 1948 // Pin the tab "1", which shouldn't move anything. 1949 { 1950 tabstrip.SetTabPinned(1, true); 1951 1952 // As the order didn't change, we should get a pinned notification. 1953 ASSERT_EQ(1, observer.GetStateCount()); 1954 State state(contents1, 1, MockTabStripModelObserver::PINNED); 1955 EXPECT_TRUE(observer.StateEquals(0, state)); 1956 1957 // And verify the state. 1958 EXPECT_EQ("3p 1p 2", GetPinnedState(tabstrip)); 1959 1960 observer.ClearStates(); 1961 } 1962 1963 // Try to move tab "2" to the front, it should be ignored. 1964 { 1965 tabstrip.MoveTabContentsAt(2, 0, false); 1966 1967 // As the order didn't change, we should get a pinned notification. 1968 ASSERT_EQ(0, observer.GetStateCount()); 1969 1970 // And verify the state. 1971 EXPECT_EQ("3p 1p 2", GetPinnedState(tabstrip)); 1972 1973 observer.ClearStates(); 1974 } 1975 1976 // Unpin tab "3", which implicitly moves it to the end. 1977 { 1978 tabstrip.SetTabPinned(0, false); 1979 1980 ASSERT_EQ(2, observer.GetStateCount()); 1981 State state(contents3, 1, MockTabStripModelObserver::MOVE); 1982 state.src_index = 0; 1983 EXPECT_TRUE(observer.StateEquals(0, state)); 1984 1985 state = State(contents3, 1, MockTabStripModelObserver::PINNED); 1986 EXPECT_TRUE(observer.StateEquals(1, state)); 1987 1988 // And verify the state. 1989 EXPECT_EQ("1p 3 2", GetPinnedState(tabstrip)); 1990 1991 observer.ClearStates(); 1992 } 1993 1994 // Unpin tab "3", nothing should happen. 1995 { 1996 tabstrip.SetTabPinned(1, false); 1997 1998 ASSERT_EQ(0, observer.GetStateCount()); 1999 2000 EXPECT_EQ("1p 3 2", GetPinnedState(tabstrip)); 2001 2002 observer.ClearStates(); 2003 } 2004 2005 // Pin "3" and "1". 2006 { 2007 tabstrip.SetTabPinned(0, true); 2008 tabstrip.SetTabPinned(1, true); 2009 2010 EXPECT_EQ("1p 3p 2", GetPinnedState(tabstrip)); 2011 2012 observer.ClearStates(); 2013 } 2014 2015 TabContentsWrapper* contents4 = CreateTabContents(); 2016 SetID(contents4->tab_contents(), 4); 2017 2018 // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end 2019 // up after them. 2020 { 2021 tabstrip.InsertTabContentsAt(1, contents4, TabStripModel::ADD_NONE); 2022 2023 ASSERT_EQ(1, observer.GetStateCount()); 2024 State state(contents4, 2, MockTabStripModelObserver::INSERT); 2025 EXPECT_TRUE(observer.StateEquals(0, state)); 2026 2027 EXPECT_EQ("1p 3p 4 2", GetPinnedState(tabstrip)); 2028 } 2029 2030 tabstrip.CloseAllTabs(); 2031 } 2032 2033 // Makes sure the TabStripModel calls the right observer methods during a 2034 // replace. 2035 TEST_F(TabStripModelTest, ReplaceSendsSelected) { 2036 typedef MockTabStripModelObserver::State State; 2037 2038 TabStripDummyDelegate delegate(NULL); 2039 TabStripModel strip(&delegate, profile()); 2040 2041 TabContentsWrapper* first_contents = CreateTabContents(); 2042 strip.AddTabContents(first_contents, -1, PageTransition::TYPED, 2043 TabStripModel::ADD_ACTIVE); 2044 2045 MockTabStripModelObserver tabstrip_observer; 2046 strip.AddObserver(&tabstrip_observer); 2047 2048 TabContentsWrapper* new_contents = CreateTabContents(); 2049 delete strip.ReplaceTabContentsAt(0, new_contents); 2050 2051 ASSERT_EQ(2, tabstrip_observer.GetStateCount()); 2052 2053 // First event should be for replaced. 2054 State state(new_contents, 0, MockTabStripModelObserver::REPLACED); 2055 state.src_contents = first_contents; 2056 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state)); 2057 2058 // And the second for selected. 2059 state = State(new_contents, 0, MockTabStripModelObserver::SELECT); 2060 state.src_contents = first_contents; 2061 EXPECT_TRUE(tabstrip_observer.StateEquals(1, state)); 2062 2063 // Now add another tab and replace it, making sure we don't get a selected 2064 // event this time. 2065 TabContentsWrapper* third_contents = CreateTabContents(); 2066 strip.AddTabContents(third_contents, 1, PageTransition::TYPED, 2067 TabStripModel::ADD_NONE); 2068 2069 tabstrip_observer.ClearStates(); 2070 2071 // And replace it. 2072 new_contents = CreateTabContents(); 2073 delete strip.ReplaceTabContentsAt(1, new_contents); 2074 2075 ASSERT_EQ(1, tabstrip_observer.GetStateCount()); 2076 2077 state = State(new_contents, 1, MockTabStripModelObserver::REPLACED); 2078 state.src_contents = third_contents; 2079 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state)); 2080 2081 strip.CloseAllTabs(); 2082 } 2083 2084 // Makes sure TabStripModel handles the case of deleting a tab while removing 2085 // another tab. 2086 TEST_F(TabStripModelTest, DeleteFromDestroy) { 2087 TabStripDummyDelegate delegate(NULL); 2088 TabStripModel strip(&delegate, profile()); 2089 TabContentsWrapper* contents1 = CreateTabContents(); 2090 TabContentsWrapper* contents2 = CreateTabContents(); 2091 strip.AppendTabContents(contents1, true); 2092 strip.AppendTabContents(contents2, true); 2093 // DeleteTabContentsOnDestroyedObserver deletes contents1 when contents2 sends 2094 // out notification that it is being destroyed. 2095 DeleteTabContentsOnDestroyedObserver observer(contents2, contents1); 2096 strip.CloseAllTabs(); 2097 } 2098 2099 TEST_F(TabStripModelTest, MoveSelectedTabsTo) { 2100 struct TestData { 2101 // Number of tabs the tab strip should have. 2102 const int tab_count; 2103 2104 // Number of pinned tabs. 2105 const int pinned_count; 2106 2107 // Index of the tabs to select. 2108 const std::string selected_tabs; 2109 2110 // Index to move the tabs to. 2111 const int target_index; 2112 2113 // Expected state after the move (space separated list of indices). 2114 const std::string state_after_move; 2115 } test_data[] = { 2116 // 1 selected tab. 2117 { 2, 0, "0", 1, "1 0" }, 2118 { 3, 0, "0", 2, "1 2 0" }, 2119 { 3, 0, "2", 0, "2 0 1" }, 2120 { 3, 0, "2", 1, "0 2 1" }, 2121 { 3, 0, "0 1", 0, "0 1 2" }, 2122 2123 // 2 selected tabs. 2124 { 6, 0, "4 5", 1, "0 4 5 1 2 3" }, 2125 { 3, 0, "0 1", 1, "2 0 1" }, 2126 { 4, 0, "0 2", 1, "1 0 2 3" }, 2127 { 6, 0, "0 1", 3, "2 3 4 0 1 5" }, 2128 2129 // 3 selected tabs. 2130 { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" }, 2131 { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" }, 2132 { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" }, 2133 2134 // 5 selected tabs. 2135 { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" }, 2136 2137 // 7 selected tabs 2138 { 16, 0, "0 1 2 3 4 7 9", 8, "5 6 8 10 11 12 13 14 0 1 2 3 4 7 9 15" }, 2139 2140 // With pinned tabs. 2141 { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" }, 2142 { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" }, 2143 { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" }, 2144 { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" }, 2145 2146 { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" }, 2147 }; 2148 2149 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 2150 TabStripDummyDelegate delegate(NULL); 2151 TabStripModel strip(&delegate, profile()); 2152 ASSERT_NO_FATAL_FAILURE( 2153 PrepareTabstripForSelectionTest(&strip, test_data[i].tab_count, 2154 test_data[i].pinned_count, 2155 test_data[i].selected_tabs)); 2156 strip.MoveSelectedTabsTo(test_data[i].target_index); 2157 EXPECT_EQ(test_data[i].state_after_move, GetPinnedState(strip)) << i; 2158 strip.CloseAllTabs(); 2159 } 2160 } 2161 2162 TEST_F(TabStripModelTest, CloseSelectedTabs) { 2163 TabStripDummyDelegate delegate(NULL); 2164 TabStripModel strip(&delegate, profile()); 2165 TabContentsWrapper* contents1 = CreateTabContents(); 2166 TabContentsWrapper* contents2 = CreateTabContents(); 2167 TabContentsWrapper* contents3 = CreateTabContents(); 2168 strip.AppendTabContents(contents1, true); 2169 strip.AppendTabContents(contents2, true); 2170 strip.AppendTabContents(contents3, true); 2171 strip.ToggleSelectionAt(1); 2172 strip.CloseSelectedTabs(); 2173 EXPECT_EQ(1, strip.count()); 2174 EXPECT_EQ(0, strip.active_index()); 2175 strip.CloseAllTabs(); 2176 } 2177 2178 // Verifies that if we change the selection from a multi selection to a single 2179 // selection, but not in a way that changes the selected_index that 2180 // TabSelectedAt is still invoked. 2181 TEST_F(TabStripModelTest, MultipleToSingle) { 2182 TabStripDummyDelegate delegate(NULL); 2183 TabStripModel strip(&delegate, profile()); 2184 TabContentsWrapper* contents1 = CreateTabContents(); 2185 TabContentsWrapper* contents2 = CreateTabContents(); 2186 strip.AppendTabContents(contents1, false); 2187 strip.AppendTabContents(contents2, false); 2188 strip.ToggleSelectionAt(0); 2189 strip.ToggleSelectionAt(1); 2190 2191 MockTabStripModelObserver observer; 2192 strip.AddObserver(&observer); 2193 // This changes the selection (0 is no longer selected) but the selected_index 2194 // still remains at 1. 2195 strip.ActivateTabAt(1, true); 2196 ASSERT_EQ(1, observer.GetStateCount()); 2197 MockTabStripModelObserver::State s( 2198 contents2, 1, MockTabStripModelObserver::SELECT); 2199 s.src_contents = contents2; 2200 s.user_gesture = true; 2201 EXPECT_TRUE(observer.StateEquals(0, s)); 2202 strip.RemoveObserver(&observer); 2203 strip.CloseAllTabs(); 2204 } 2205