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