1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/tabs/tab_strip_model.h" 6 7 #include <map> 8 #include <string> 9 10 #include "base/files/file_path.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/path_service.h" 13 #include "base/stl_util.h" 14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_split.h" 16 #include "base/strings/string_util.h" 17 #include "base/strings/utf_string_conversions.h" 18 #include "chrome/browser/defaults.h" 19 #include "chrome/browser/extensions/tab_helper.h" 20 #include "chrome/browser/profiles/profile.h" 21 #include "chrome/browser/ui/browser.h" 22 #include "chrome/browser/ui/browser_tabstrip.h" 23 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" 24 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h" 25 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" 26 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h" 27 #include "chrome/common/url_constants.h" 28 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 29 #include "chrome/test/base/testing_profile.h" 30 #include "components/web_modal/web_contents_modal_dialog_manager.h" 31 #include "content/public/browser/navigation_controller.h" 32 #include "content/public/browser/navigation_entry.h" 33 #include "content/public/browser/render_process_host.h" 34 #include "content/public/browser/web_contents.h" 35 #include "content/public/browser/web_contents_observer.h" 36 #include "extensions/common/extension.h" 37 #include "testing/gtest/include/gtest/gtest.h" 38 39 using content::SiteInstance; 40 using content::WebContents; 41 using extensions::Extension; 42 using web_modal::NativeWebContentsModalDialog; 43 44 namespace { 45 46 // Class used to delete a WebContents and TabStripModel when another WebContents 47 // is destroyed. 48 class DeleteWebContentsOnDestroyedObserver 49 : public content::WebContentsObserver { 50 public: 51 // When |source| is deleted both |tab_to_delete| and |tab_strip| are deleted. 52 // |tab_to_delete| and |tab_strip| may be NULL. 53 DeleteWebContentsOnDestroyedObserver(WebContents* source, 54 WebContents* tab_to_delete, 55 TabStripModel* tab_strip) 56 : WebContentsObserver(source), 57 tab_to_delete_(tab_to_delete), 58 tab_strip_(tab_strip) { 59 } 60 61 virtual void WebContentsDestroyed() OVERRIDE { 62 WebContents* tab_to_delete = tab_to_delete_; 63 tab_to_delete_ = NULL; 64 TabStripModel* tab_strip_to_delete = tab_strip_; 65 tab_strip_ = NULL; 66 delete tab_to_delete; 67 delete tab_strip_to_delete; 68 } 69 70 private: 71 WebContents* tab_to_delete_; 72 TabStripModel* tab_strip_; 73 74 DISALLOW_COPY_AND_ASSIGN(DeleteWebContentsOnDestroyedObserver); 75 }; 76 77 class TabStripDummyDelegate : public TestTabStripModelDelegate { 78 public: 79 TabStripDummyDelegate() : run_unload_(false) {} 80 virtual ~TabStripDummyDelegate() {} 81 82 void set_run_unload_listener(bool value) { run_unload_ = value; } 83 84 virtual bool RunUnloadListenerBeforeClosing(WebContents* contents) OVERRIDE { 85 return run_unload_; 86 } 87 88 private: 89 // Whether to report that we need to run an unload listener before closing. 90 bool run_unload_; 91 92 DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate); 93 }; 94 95 const char kTabStripModelTestIDUserDataKey[] = "TabStripModelTestIDUserData"; 96 97 class TabStripModelTestIDUserData : public base::SupportsUserData::Data { 98 public: 99 explicit TabStripModelTestIDUserData(int id) : id_(id) {} 100 virtual ~TabStripModelTestIDUserData() {} 101 int id() { return id_; } 102 103 private: 104 int id_; 105 }; 106 107 class DummySingleWebContentsDialogManager 108 : public web_modal::SingleWebContentsDialogManager { 109 public: 110 explicit DummySingleWebContentsDialogManager( 111 NativeWebContentsModalDialog dialog, 112 web_modal::SingleWebContentsDialogManagerDelegate* delegate) 113 : delegate_(delegate), 114 dialog_(dialog) {} 115 virtual ~DummySingleWebContentsDialogManager() {} 116 117 virtual void Show() OVERRIDE {} 118 virtual void Hide() OVERRIDE {} 119 virtual void Close() OVERRIDE { 120 delegate_->WillClose(dialog_); 121 } 122 virtual void Focus() OVERRIDE {} 123 virtual void Pulse() OVERRIDE {} 124 virtual void HostChanged( 125 web_modal::WebContentsModalDialogHost* new_host) OVERRIDE {} 126 virtual NativeWebContentsModalDialog dialog() OVERRIDE { return dialog_; } 127 128 private: 129 web_modal::SingleWebContentsDialogManagerDelegate* delegate_; 130 NativeWebContentsModalDialog dialog_; 131 132 DISALLOW_COPY_AND_ASSIGN(DummySingleWebContentsDialogManager); 133 }; 134 135 // Test Browser-like class for TabStripModelTest.TabBlockedState. 136 class TabBlockedStateTestBrowser 137 : public TabStripModelObserver, 138 public web_modal::WebContentsModalDialogManagerDelegate { 139 public: 140 explicit TabBlockedStateTestBrowser(TabStripModel* tab_strip_model) 141 : tab_strip_model_(tab_strip_model) { 142 tab_strip_model_->AddObserver(this); 143 } 144 145 virtual ~TabBlockedStateTestBrowser() { 146 tab_strip_model_->RemoveObserver(this); 147 } 148 149 private: 150 // TabStripModelObserver 151 virtual void TabInsertedAt(WebContents* contents, 152 int index, 153 bool foreground) OVERRIDE { 154 web_modal::WebContentsModalDialogManager* manager = 155 web_modal::WebContentsModalDialogManager::FromWebContents(contents); 156 if (manager) 157 manager->SetDelegate(this); 158 } 159 160 // WebContentsModalDialogManagerDelegate 161 virtual void SetWebContentsBlocked(content::WebContents* contents, 162 bool blocked) OVERRIDE { 163 int index = tab_strip_model_->GetIndexOfWebContents(contents); 164 ASSERT_GE(index, 0); 165 tab_strip_model_->SetTabBlocked(index, blocked); 166 } 167 168 TabStripModel* tab_strip_model_; 169 170 DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser); 171 }; 172 173 } // namespace 174 175 class TabStripModelTest : public ChromeRenderViewHostTestHarness { 176 public: 177 WebContents* CreateWebContents() { 178 return WebContents::Create(WebContents::CreateParams(profile())); 179 } 180 181 WebContents* CreateWebContentsWithSharedRPH(WebContents* web_contents) { 182 WebContents::CreateParams create_params( 183 profile(), web_contents->GetRenderViewHost()->GetSiteInstance()); 184 WebContents* retval = WebContents::Create(create_params); 185 EXPECT_EQ(retval->GetRenderProcessHost(), 186 web_contents->GetRenderProcessHost()); 187 return retval; 188 } 189 190 // Sets the id of the specified contents. 191 void SetID(WebContents* contents, int id) { 192 contents->SetUserData(&kTabStripModelTestIDUserDataKey, 193 new TabStripModelTestIDUserData(id)); 194 } 195 196 // Returns the id of the specified contents. 197 int GetID(WebContents* contents) { 198 TabStripModelTestIDUserData* user_data = 199 static_cast<TabStripModelTestIDUserData*>( 200 contents->GetUserData(&kTabStripModelTestIDUserDataKey)); 201 202 return user_data ? user_data->id() : -1; 203 } 204 205 // Returns the state of the given tab strip as a string. The state consists 206 // of the ID of each web contents followed by a 'p' if pinned. For example, 207 // if the model consists of two tabs with ids 2 and 1, with the first 208 // tab pinned, this returns "2p 1". 209 std::string GetTabStripStateString(const TabStripModel& model) { 210 std::string actual; 211 for (int i = 0; i < model.count(); ++i) { 212 if (i > 0) 213 actual += " "; 214 215 actual += base::IntToString(GetID(model.GetWebContentsAt(i))); 216 217 if (model.IsAppTab(i)) 218 actual += "a"; 219 220 if (model.IsTabPinned(i)) 221 actual += "p"; 222 } 223 return actual; 224 } 225 226 std::string GetIndicesClosedByCommandAsString( 227 const TabStripModel& model, 228 int index, 229 TabStripModel::ContextMenuCommand id) const { 230 std::vector<int> indices = model.GetIndicesClosedByCommand(index, id); 231 std::string result; 232 for (size_t i = 0; i < indices.size(); ++i) { 233 if (i != 0) 234 result += " "; 235 result += base::IntToString(indices[i]); 236 } 237 return result; 238 } 239 240 void PrepareTabstripForSelectionTest(TabStripModel* model, 241 int tab_count, 242 int pinned_count, 243 const std::string& selected_tabs) { 244 for (int i = 0; i < tab_count; ++i) { 245 WebContents* contents = CreateWebContents(); 246 SetID(contents, i); 247 model->AppendWebContents(contents, true); 248 } 249 for (int i = 0; i < pinned_count; ++i) 250 model->SetTabPinned(i, true); 251 252 ui::ListSelectionModel selection_model; 253 std::vector<std::string> selection; 254 base::SplitStringAlongWhitespace(selected_tabs, &selection); 255 for (size_t i = 0; i < selection.size(); ++i) { 256 int value; 257 ASSERT_TRUE(base::StringToInt(selection[i], &value)); 258 selection_model.AddIndexToSelection(value); 259 } 260 selection_model.set_active(selection_model.selected_indices()[0]); 261 model->SetSelectionFromModel(selection_model); 262 } 263 }; 264 265 class MockTabStripModelObserver : public TabStripModelObserver { 266 public: 267 explicit MockTabStripModelObserver(TabStripModel* model) 268 : empty_(true), 269 deleted_(false), 270 model_(model) {} 271 virtual ~MockTabStripModelObserver() {} 272 273 enum TabStripModelObserverAction { 274 INSERT, 275 CLOSE, 276 DETACH, 277 ACTIVATE, 278 DEACTIVATE, 279 SELECT, 280 MOVE, 281 CHANGE, 282 PINNED, 283 REPLACED, 284 CLOSE_ALL, 285 CLOSE_ALL_CANCELED, 286 }; 287 288 struct State { 289 State(WebContents* a_dst_contents, 290 int a_dst_index, 291 TabStripModelObserverAction a_action) 292 : src_contents(NULL), 293 dst_contents(a_dst_contents), 294 src_index(-1), 295 dst_index(a_dst_index), 296 change_reason(CHANGE_REASON_NONE), 297 foreground(false), 298 action(a_action) { 299 } 300 301 WebContents* src_contents; 302 WebContents* dst_contents; 303 int src_index; 304 int dst_index; 305 int change_reason; 306 bool foreground; 307 TabStripModelObserverAction action; 308 }; 309 310 int GetStateCount() const { 311 return static_cast<int>(states_.size()); 312 } 313 314 // Returns (by way of parameters) the number of state's with CLOSE_ALL and 315 // CLOSE_ALL_CANCELED. 316 void GetCloseCounts(int* close_all_count, 317 int* close_all_canceled_count) { 318 *close_all_count = *close_all_canceled_count = 0; 319 for (int i = 0; i < GetStateCount(); ++i) { 320 switch (GetStateAt(i).action) { 321 case CLOSE_ALL: 322 (*close_all_count)++; 323 break; 324 case CLOSE_ALL_CANCELED: 325 (*close_all_canceled_count)++; 326 break; 327 default: 328 break; 329 } 330 } 331 } 332 333 const State& GetStateAt(int index) const { 334 DCHECK(index >= 0 && index < GetStateCount()); 335 return states_[index]; 336 } 337 338 bool StateEquals(int index, const State& state) { 339 const State& s = GetStateAt(index); 340 return (s.src_contents == state.src_contents && 341 s.dst_contents == state.dst_contents && 342 s.src_index == state.src_index && 343 s.dst_index == state.dst_index && 344 s.change_reason == state.change_reason && 345 s.foreground == state.foreground && 346 s.action == state.action); 347 } 348 349 // TabStripModelObserver implementation: 350 virtual void TabInsertedAt(WebContents* contents, 351 int index, 352 bool foreground) OVERRIDE { 353 empty_ = false; 354 State s(contents, index, INSERT); 355 s.foreground = foreground; 356 states_.push_back(s); 357 } 358 virtual void ActiveTabChanged(WebContents* old_contents, 359 WebContents* new_contents, 360 int index, 361 int reason) OVERRIDE { 362 State s(new_contents, index, ACTIVATE); 363 s.src_contents = old_contents; 364 s.change_reason = reason; 365 states_.push_back(s); 366 } 367 virtual void TabSelectionChanged( 368 TabStripModel* tab_strip_model, 369 const ui::ListSelectionModel& old_model) OVERRIDE { 370 State s(model()->GetActiveWebContents(), model()->active_index(), SELECT); 371 s.src_contents = model()->GetWebContentsAt(old_model.active()); 372 s.src_index = old_model.active(); 373 states_.push_back(s); 374 } 375 virtual void TabMoved(WebContents* contents, 376 int from_index, 377 int to_index) OVERRIDE { 378 State s(contents, to_index, MOVE); 379 s.src_index = from_index; 380 states_.push_back(s); 381 } 382 383 virtual void TabClosingAt(TabStripModel* tab_strip_model, 384 WebContents* contents, 385 int index) OVERRIDE { 386 states_.push_back(State(contents, index, CLOSE)); 387 } 388 virtual void TabDetachedAt(WebContents* contents, int index) OVERRIDE { 389 states_.push_back(State(contents, index, DETACH)); 390 } 391 virtual void TabDeactivated(WebContents* contents) OVERRIDE { 392 states_.push_back(State(contents, model()->active_index(), DEACTIVATE)); 393 } 394 virtual void TabChangedAt(WebContents* contents, 395 int index, 396 TabChangeType change_type) OVERRIDE { 397 states_.push_back(State(contents, index, CHANGE)); 398 } 399 virtual void TabReplacedAt(TabStripModel* tab_strip_model, 400 WebContents* old_contents, 401 WebContents* new_contents, 402 int index) OVERRIDE { 403 State s(new_contents, index, REPLACED); 404 s.src_contents = old_contents; 405 states_.push_back(s); 406 } 407 virtual void TabPinnedStateChanged(WebContents* contents, 408 int index) OVERRIDE { 409 states_.push_back(State(contents, index, PINNED)); 410 } 411 virtual void TabStripEmpty() OVERRIDE { 412 empty_ = true; 413 } 414 virtual void WillCloseAllTabs() OVERRIDE { 415 states_.push_back(State(NULL, -1, CLOSE_ALL)); 416 } 417 virtual void CloseAllTabsCanceled() OVERRIDE { 418 states_.push_back(State(NULL, -1, CLOSE_ALL_CANCELED)); 419 } 420 virtual void TabStripModelDeleted() OVERRIDE { 421 deleted_ = true; 422 } 423 424 void ClearStates() { 425 states_.clear(); 426 } 427 428 bool empty() const { return empty_; } 429 bool deleted() const { return deleted_; } 430 TabStripModel* model() { return model_; } 431 432 private: 433 std::vector<State> states_; 434 435 bool empty_; 436 bool deleted_; 437 TabStripModel* model_; 438 439 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver); 440 }; 441 442 TEST_F(TabStripModelTest, TestBasicAPI) { 443 TabStripDummyDelegate delegate; 444 TabStripModel tabstrip(&delegate, profile()); 445 MockTabStripModelObserver observer(&tabstrip); 446 tabstrip.AddObserver(&observer); 447 448 EXPECT_TRUE(tabstrip.empty()); 449 450 typedef MockTabStripModelObserver::State State; 451 452 WebContents* contents1 = CreateWebContents(); 453 SetID(contents1, 1); 454 455 // Note! The ordering of these tests is important, each subsequent test 456 // builds on the state established in the previous. This is important if you 457 // ever insert tests rather than append. 458 459 // Test AppendWebContents, ContainsIndex 460 { 461 EXPECT_FALSE(tabstrip.ContainsIndex(0)); 462 tabstrip.AppendWebContents(contents1, true); 463 EXPECT_TRUE(tabstrip.ContainsIndex(0)); 464 EXPECT_EQ(1, tabstrip.count()); 465 EXPECT_EQ(3, observer.GetStateCount()); 466 State s1(contents1, 0, MockTabStripModelObserver::INSERT); 467 s1.foreground = true; 468 EXPECT_TRUE(observer.StateEquals(0, s1)); 469 State s2(contents1, 0, MockTabStripModelObserver::ACTIVATE); 470 EXPECT_TRUE(observer.StateEquals(1, s2)); 471 State s3(contents1, 0, MockTabStripModelObserver::SELECT); 472 s3.src_contents = NULL; 473 s3.src_index = ui::ListSelectionModel::kUnselectedIndex; 474 EXPECT_TRUE(observer.StateEquals(2, s3)); 475 observer.ClearStates(); 476 } 477 EXPECT_EQ("1", GetTabStripStateString(tabstrip)); 478 479 // Test InsertWebContentsAt, foreground tab. 480 WebContents* contents2 = CreateWebContents(); 481 SetID(contents2, 2); 482 { 483 tabstrip.InsertWebContentsAt(1, contents2, TabStripModel::ADD_ACTIVE); 484 485 EXPECT_EQ(2, tabstrip.count()); 486 EXPECT_EQ(4, observer.GetStateCount()); 487 State s1(contents2, 1, MockTabStripModelObserver::INSERT); 488 s1.foreground = true; 489 EXPECT_TRUE(observer.StateEquals(0, s1)); 490 State s2(contents1, 0, MockTabStripModelObserver::DEACTIVATE); 491 EXPECT_TRUE(observer.StateEquals(1, s2)); 492 State s3(contents2, 1, MockTabStripModelObserver::ACTIVATE); 493 s3.src_contents = contents1; 494 EXPECT_TRUE(observer.StateEquals(2, s3)); 495 State s4(contents2, 1, MockTabStripModelObserver::SELECT); 496 s4.src_contents = contents1; 497 s4.src_index = 0; 498 EXPECT_TRUE(observer.StateEquals(3, s4)); 499 observer.ClearStates(); 500 } 501 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip)); 502 503 // Test InsertWebContentsAt, background tab. 504 WebContents* contents3 = CreateWebContents(); 505 SetID(contents3, 3); 506 { 507 tabstrip.InsertWebContentsAt(2, contents3, TabStripModel::ADD_NONE); 508 509 EXPECT_EQ(3, tabstrip.count()); 510 EXPECT_EQ(1, observer.GetStateCount()); 511 State s1(contents3, 2, MockTabStripModelObserver::INSERT); 512 s1.foreground = false; 513 EXPECT_TRUE(observer.StateEquals(0, s1)); 514 observer.ClearStates(); 515 } 516 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip)); 517 518 // Test ActivateTabAt 519 { 520 tabstrip.ActivateTabAt(2, true); 521 EXPECT_EQ(3, observer.GetStateCount()); 522 State s1(contents2, 1, MockTabStripModelObserver::DEACTIVATE); 523 EXPECT_TRUE(observer.StateEquals(0, s1)); 524 State s2(contents3, 2, MockTabStripModelObserver::ACTIVATE); 525 s2.src_contents = contents2; 526 s2.change_reason = TabStripModelObserver::CHANGE_REASON_USER_GESTURE; 527 EXPECT_TRUE(observer.StateEquals(1, s2)); 528 State s3(contents3, 2, MockTabStripModelObserver::SELECT); 529 s3.src_contents = contents2; 530 s3.src_index = 1; 531 EXPECT_TRUE(observer.StateEquals(2, s3)); 532 observer.ClearStates(); 533 } 534 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip)); 535 536 // Test DetachWebContentsAt 537 { 538 // Detach ... 539 WebContents* detached = tabstrip.DetachWebContentsAt(2); 540 // ... and append again because we want this for later. 541 tabstrip.AppendWebContents(detached, true); 542 EXPECT_EQ(8, observer.GetStateCount()); 543 State s1(detached, 2, MockTabStripModelObserver::DETACH); 544 EXPECT_TRUE(observer.StateEquals(0, s1)); 545 State s2(detached, ui::ListSelectionModel::kUnselectedIndex, 546 MockTabStripModelObserver::DEACTIVATE); 547 EXPECT_TRUE(observer.StateEquals(1, s2)); 548 State s3(contents2, 1, MockTabStripModelObserver::ACTIVATE); 549 s3.src_contents = contents3; 550 s3.change_reason = TabStripModelObserver::CHANGE_REASON_NONE; 551 EXPECT_TRUE(observer.StateEquals(2, s3)); 552 State s4(contents2, 1, MockTabStripModelObserver::SELECT); 553 s4.src_contents = NULL; 554 s4.src_index = ui::ListSelectionModel::kUnselectedIndex; 555 EXPECT_TRUE(observer.StateEquals(3, s4)); 556 State s5(detached, 2, MockTabStripModelObserver::INSERT); 557 s5.foreground = true; 558 EXPECT_TRUE(observer.StateEquals(4, s5)); 559 State s6(contents2, 1, MockTabStripModelObserver::DEACTIVATE); 560 EXPECT_TRUE(observer.StateEquals(5, s6)); 561 State s7(detached, 2, MockTabStripModelObserver::ACTIVATE); 562 s7.src_contents = contents2; 563 s7.change_reason = TabStripModelObserver::CHANGE_REASON_NONE; 564 EXPECT_TRUE(observer.StateEquals(6, s7)); 565 State s8(detached, 2, MockTabStripModelObserver::SELECT); 566 s8.src_contents = contents2; 567 s8.src_index = 1; 568 EXPECT_TRUE(observer.StateEquals(7, s8)); 569 observer.ClearStates(); 570 } 571 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip)); 572 573 // Test CloseWebContentsAt 574 { 575 EXPECT_TRUE(tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE)); 576 EXPECT_EQ(2, tabstrip.count()); 577 578 EXPECT_EQ(5, observer.GetStateCount()); 579 State s1(contents3, 2, MockTabStripModelObserver::CLOSE); 580 EXPECT_TRUE(observer.StateEquals(0, s1)); 581 State s2(contents3, 2, MockTabStripModelObserver::DETACH); 582 EXPECT_TRUE(observer.StateEquals(1, s2)); 583 State s3(contents3, ui::ListSelectionModel::kUnselectedIndex, 584 MockTabStripModelObserver::DEACTIVATE); 585 EXPECT_TRUE(observer.StateEquals(2, s3)); 586 State s4(contents2, 1, MockTabStripModelObserver::ACTIVATE); 587 s4.src_contents = contents3; 588 s4.change_reason = TabStripModelObserver::CHANGE_REASON_NONE; 589 EXPECT_TRUE(observer.StateEquals(3, s4)); 590 State s5(contents2, 1, MockTabStripModelObserver::SELECT); 591 s5.src_contents = NULL; 592 s5.src_index = ui::ListSelectionModel::kUnselectedIndex; 593 EXPECT_TRUE(observer.StateEquals(4, s5)); 594 observer.ClearStates(); 595 } 596 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip)); 597 598 // Test MoveWebContentsAt, select_after_move == true 599 { 600 tabstrip.MoveWebContentsAt(1, 0, true); 601 602 EXPECT_EQ(1, observer.GetStateCount()); 603 State s1(contents2, 0, MockTabStripModelObserver::MOVE); 604 s1.src_index = 1; 605 EXPECT_TRUE(observer.StateEquals(0, s1)); 606 EXPECT_EQ(0, tabstrip.active_index()); 607 observer.ClearStates(); 608 } 609 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip)); 610 611 // Test MoveWebContentsAt, select_after_move == false 612 { 613 tabstrip.MoveWebContentsAt(1, 0, false); 614 EXPECT_EQ(1, observer.GetStateCount()); 615 State s1(contents1, 0, MockTabStripModelObserver::MOVE); 616 s1.src_index = 1; 617 EXPECT_TRUE(observer.StateEquals(0, s1)); 618 EXPECT_EQ(1, tabstrip.active_index()); 619 620 tabstrip.MoveWebContentsAt(0, 1, false); 621 observer.ClearStates(); 622 } 623 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip)); 624 625 // Test Getters 626 { 627 EXPECT_EQ(contents2, tabstrip.GetActiveWebContents()); 628 EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(0)); 629 EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1)); 630 EXPECT_EQ(0, tabstrip.GetIndexOfWebContents(contents2)); 631 EXPECT_EQ(1, tabstrip.GetIndexOfWebContents(contents1)); 632 } 633 634 // Test UpdateWebContentsStateAt 635 { 636 tabstrip.UpdateWebContentsStateAt(0, TabStripModelObserver::ALL); 637 EXPECT_EQ(1, observer.GetStateCount()); 638 State s1(contents2, 0, MockTabStripModelObserver::CHANGE); 639 EXPECT_TRUE(observer.StateEquals(0, s1)); 640 observer.ClearStates(); 641 } 642 643 // Test SelectNextTab, SelectPreviousTab, SelectLastTab 644 { 645 // Make sure the second of the two tabs is selected first... 646 tabstrip.ActivateTabAt(1, true); 647 tabstrip.SelectPreviousTab(); 648 EXPECT_EQ(0, tabstrip.active_index()); 649 tabstrip.SelectLastTab(); 650 EXPECT_EQ(1, tabstrip.active_index()); 651 tabstrip.SelectNextTab(); 652 EXPECT_EQ(0, tabstrip.active_index()); 653 } 654 655 // Test CloseSelectedTabs 656 { 657 tabstrip.CloseSelectedTabs(); 658 // |CloseSelectedTabs| calls CloseWebContentsAt, we already tested that, now 659 // just verify that the count and selected index have changed 660 // appropriately... 661 EXPECT_EQ(1, tabstrip.count()); 662 EXPECT_EQ(0, tabstrip.active_index()); 663 } 664 665 observer.ClearStates(); 666 tabstrip.CloseAllTabs(); 667 668 int close_all_count = 0, close_all_canceled_count = 0; 669 observer.GetCloseCounts(&close_all_count, &close_all_canceled_count); 670 EXPECT_EQ(1, close_all_count); 671 EXPECT_EQ(0, close_all_canceled_count); 672 673 // TabStripModel should now be empty. 674 EXPECT_TRUE(tabstrip.empty()); 675 676 // Opener methods are tested below... 677 678 tabstrip.RemoveObserver(&observer); 679 } 680 681 TEST_F(TabStripModelTest, TestBasicOpenerAPI) { 682 TabStripDummyDelegate delegate; 683 TabStripModel tabstrip(&delegate, profile()); 684 EXPECT_TRUE(tabstrip.empty()); 685 686 // This is a basic test of opener functionality. opener is created 687 // as the first tab in the strip and then we create 5 other tabs in the 688 // background with opener set as their opener. 689 690 WebContents* opener = CreateWebContents(); 691 tabstrip.AppendWebContents(opener, true); 692 WebContents* contents1 = CreateWebContents(); 693 WebContents* contents2 = CreateWebContents(); 694 WebContents* contents3 = CreateWebContents(); 695 WebContents* contents4 = CreateWebContents(); 696 WebContents* contents5 = CreateWebContents(); 697 698 // We use |InsertWebContentsAt| here instead of |AppendWebContents| so that 699 // openership relationships are preserved. 700 tabstrip.InsertWebContentsAt(tabstrip.count(), contents1, 701 TabStripModel::ADD_INHERIT_GROUP); 702 tabstrip.InsertWebContentsAt(tabstrip.count(), contents2, 703 TabStripModel::ADD_INHERIT_GROUP); 704 tabstrip.InsertWebContentsAt(tabstrip.count(), contents3, 705 TabStripModel::ADD_INHERIT_GROUP); 706 tabstrip.InsertWebContentsAt(tabstrip.count(), contents4, 707 TabStripModel::ADD_INHERIT_GROUP); 708 tabstrip.InsertWebContentsAt(tabstrip.count(), contents5, 709 TabStripModel::ADD_INHERIT_GROUP); 710 711 // All the tabs should have the same opener. 712 for (int i = 1; i < tabstrip.count(); ++i) 713 EXPECT_EQ(opener, tabstrip.GetOpenerOfWebContentsAt(i)); 714 715 // If there is a next adjacent item, then the index should be of that item. 716 EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 1, false)); 717 // If the last tab in the group is closed, the preceding tab in the same 718 // group should be selected. 719 EXPECT_EQ(4, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 5, false)); 720 721 // Tests the method that finds the last tab opened by the same opener in the 722 // strip (this is the insertion index for the next background tab for the 723 // specified opener). 724 EXPECT_EQ(5, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1)); 725 726 // For a tab that has opened no other tabs, the return value should always be 727 // -1... 728 EXPECT_EQ(-1, 729 tabstrip.GetIndexOfNextWebContentsOpenedBy(contents1, 3, false)); 730 EXPECT_EQ(-1, 731 tabstrip.GetIndexOfLastWebContentsOpenedBy(contents1, 3)); 732 733 // ForgetAllOpeners should destroy all opener relationships. 734 tabstrip.ForgetAllOpeners(); 735 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 1, false)); 736 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 5, false)); 737 EXPECT_EQ(-1, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1)); 738 739 // Specify the last tab as the opener of the others. 740 for (int i = 0; i < tabstrip.count() - 1; ++i) 741 tabstrip.SetOpenerOfWebContentsAt(i, contents5); 742 743 for (int i = 0; i < tabstrip.count() - 1; ++i) 744 EXPECT_EQ(contents5, tabstrip.GetOpenerOfWebContentsAt(i)); 745 746 // If there is a next adjacent item, then the index should be of that item. 747 EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(contents5, 1, false)); 748 749 // If the last tab in the group is closed, the preceding tab in the same 750 // group should be selected. 751 EXPECT_EQ(3, tabstrip.GetIndexOfNextWebContentsOpenedBy(contents5, 4, false)); 752 753 tabstrip.CloseAllTabs(); 754 EXPECT_TRUE(tabstrip.empty()); 755 } 756 757 static int GetInsertionIndex(TabStripModel* tabstrip) { 758 return tabstrip->order_controller()->DetermineInsertionIndex( 759 content::PAGE_TRANSITION_LINK, false); 760 } 761 762 static void InsertWebContentses(TabStripModel* tabstrip, 763 WebContents* contents1, 764 WebContents* contents2, 765 WebContents* contents3) { 766 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip), 767 contents1, 768 TabStripModel::ADD_INHERIT_GROUP); 769 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip), 770 contents2, 771 TabStripModel::ADD_INHERIT_GROUP); 772 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip), 773 contents3, 774 TabStripModel::ADD_INHERIT_GROUP); 775 } 776 777 // Tests opening background tabs. 778 TEST_F(TabStripModelTest, TestLTRInsertionOptions) { 779 TabStripDummyDelegate delegate; 780 TabStripModel tabstrip(&delegate, profile()); 781 EXPECT_TRUE(tabstrip.empty()); 782 783 WebContents* opener = CreateWebContents(); 784 tabstrip.AppendWebContents(opener, true); 785 786 WebContents* contents1 = CreateWebContents(); 787 WebContents* contents2 = CreateWebContents(); 788 WebContents* contents3 = CreateWebContents(); 789 790 // Test LTR 791 InsertWebContentses(&tabstrip, contents1, contents2, contents3); 792 EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1)); 793 EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(2)); 794 EXPECT_EQ(contents3, tabstrip.GetWebContentsAt(3)); 795 796 tabstrip.CloseAllTabs(); 797 EXPECT_TRUE(tabstrip.empty()); 798 } 799 800 // This test constructs a tabstrip, and then simulates loading several tabs in 801 // the background from link clicks on the first tab. Then it simulates opening 802 // a new tab from the first tab in the foreground via a link click, verifies 803 // that this tab is opened adjacent to the opener, then closes it. 804 // Finally it tests that a tab opened for some non-link purpose opens at the 805 // end of the strip, not bundled to any existing context. 806 TEST_F(TabStripModelTest, TestInsertionIndexDetermination) { 807 TabStripDummyDelegate delegate; 808 TabStripModel tabstrip(&delegate, profile()); 809 EXPECT_TRUE(tabstrip.empty()); 810 811 WebContents* opener = CreateWebContents(); 812 tabstrip.AppendWebContents(opener, true); 813 814 // Open some other random unrelated tab in the background to monkey with our 815 // insertion index. 816 WebContents* other = CreateWebContents(); 817 tabstrip.AppendWebContents(other, false); 818 819 WebContents* contents1 = CreateWebContents(); 820 WebContents* contents2 = CreateWebContents(); 821 WebContents* contents3 = CreateWebContents(); 822 823 // Start by testing LTR. 824 InsertWebContentses(&tabstrip, contents1, contents2, contents3); 825 EXPECT_EQ(opener, tabstrip.GetWebContentsAt(0)); 826 EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1)); 827 EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(2)); 828 EXPECT_EQ(contents3, tabstrip.GetWebContentsAt(3)); 829 EXPECT_EQ(other, tabstrip.GetWebContentsAt(4)); 830 831 // The opener API should work... 832 EXPECT_EQ(3, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 2, false)); 833 EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false)); 834 EXPECT_EQ(3, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1)); 835 836 // Now open a foreground tab from a link. It should be opened adjacent to the 837 // opener tab. 838 WebContents* fg_link_contents = CreateWebContents(); 839 int insert_index = tabstrip.order_controller()->DetermineInsertionIndex( 840 content::PAGE_TRANSITION_LINK, true); 841 EXPECT_EQ(1, insert_index); 842 tabstrip.InsertWebContentsAt(insert_index, fg_link_contents, 843 TabStripModel::ADD_ACTIVE | 844 TabStripModel::ADD_INHERIT_GROUP); 845 EXPECT_EQ(1, tabstrip.active_index()); 846 EXPECT_EQ(fg_link_contents, tabstrip.GetActiveWebContents()); 847 848 // Now close this contents. The selection should move to the opener contents. 849 tabstrip.CloseSelectedTabs(); 850 EXPECT_EQ(0, tabstrip.active_index()); 851 852 // Now open a new empty tab. It should open at the end of the strip. 853 WebContents* fg_nonlink_contents = CreateWebContents(); 854 insert_index = tabstrip.order_controller()->DetermineInsertionIndex( 855 content::PAGE_TRANSITION_AUTO_BOOKMARK, true); 856 EXPECT_EQ(tabstrip.count(), insert_index); 857 // We break the opener relationship... 858 tabstrip.InsertWebContentsAt(insert_index, 859 fg_nonlink_contents, 860 TabStripModel::ADD_NONE); 861 // Now select it, so that user_gesture == true causes the opener relationship 862 // to be forgotten... 863 tabstrip.ActivateTabAt(tabstrip.count() - 1, true); 864 EXPECT_EQ(tabstrip.count() - 1, tabstrip.active_index()); 865 EXPECT_EQ(fg_nonlink_contents, tabstrip.GetActiveWebContents()); 866 867 // Verify that all opener relationships are forgotten. 868 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 2, false)); 869 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false)); 870 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false)); 871 EXPECT_EQ(-1, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1)); 872 873 tabstrip.CloseAllTabs(); 874 EXPECT_TRUE(tabstrip.empty()); 875 } 876 877 // Tests that selection is shifted to the correct tab when a tab is closed. 878 // If a tab is in the background when it is closed, the selection does not 879 // change. 880 // If a tab is in the foreground (selected), 881 // If that tab does not have an opener, selection shifts to the right. 882 // If the tab has an opener, 883 // The next tab (scanning LTR) in the entire strip that has the same opener 884 // is selected 885 // If there are no other tabs that have the same opener, 886 // The opener is selected 887 // 888 TEST_F(TabStripModelTest, TestSelectOnClose) { 889 TabStripDummyDelegate delegate; 890 TabStripModel tabstrip(&delegate, profile()); 891 EXPECT_TRUE(tabstrip.empty()); 892 893 WebContents* opener = CreateWebContents(); 894 tabstrip.AppendWebContents(opener, true); 895 896 WebContents* contents1 = CreateWebContents(); 897 WebContents* contents2 = CreateWebContents(); 898 WebContents* contents3 = CreateWebContents(); 899 900 // Note that we use Detach instead of Close throughout this test to avoid 901 // having to keep reconstructing these WebContentses. 902 903 // First test that closing tabs that are in the background doesn't adjust the 904 // current selection. 905 InsertWebContentses(&tabstrip, contents1, contents2, contents3); 906 EXPECT_EQ(0, tabstrip.active_index()); 907 908 tabstrip.DetachWebContentsAt(1); 909 EXPECT_EQ(0, tabstrip.active_index()); 910 911 for (int i = tabstrip.count() - 1; i >= 1; --i) 912 tabstrip.DetachWebContentsAt(i); 913 914 // Now test that when a tab doesn't have an opener, selection shifts to the 915 // right when the tab is closed. 916 InsertWebContentses(&tabstrip, contents1, contents2, contents3); 917 EXPECT_EQ(0, tabstrip.active_index()); 918 919 tabstrip.ForgetAllOpeners(); 920 tabstrip.ActivateTabAt(1, true); 921 EXPECT_EQ(1, tabstrip.active_index()); 922 tabstrip.DetachWebContentsAt(1); 923 EXPECT_EQ(1, tabstrip.active_index()); 924 tabstrip.DetachWebContentsAt(1); 925 EXPECT_EQ(1, tabstrip.active_index()); 926 tabstrip.DetachWebContentsAt(1); 927 EXPECT_EQ(0, tabstrip.active_index()); 928 929 for (int i = tabstrip.count() - 1; i >= 1; --i) 930 tabstrip.DetachWebContentsAt(i); 931 932 // Now test that when a tab does have an opener, it selects the next tab 933 // opened by the same opener scanning LTR when it is closed. 934 InsertWebContentses(&tabstrip, contents1, contents2, contents3); 935 EXPECT_EQ(0, tabstrip.active_index()); 936 tabstrip.ActivateTabAt(2, false); 937 EXPECT_EQ(2, tabstrip.active_index()); 938 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE); 939 EXPECT_EQ(2, tabstrip.active_index()); 940 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE); 941 EXPECT_EQ(1, tabstrip.active_index()); 942 tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE); 943 EXPECT_EQ(0, tabstrip.active_index()); 944 // Finally test that when a tab has no "siblings" that the opener is 945 // selected. 946 WebContents* other_contents = CreateWebContents(); 947 tabstrip.InsertWebContentsAt(1, other_contents, 948 TabStripModel::ADD_NONE); 949 EXPECT_EQ(2, tabstrip.count()); 950 WebContents* opened_contents = CreateWebContents(); 951 tabstrip.InsertWebContentsAt(2, opened_contents, 952 TabStripModel::ADD_ACTIVE | 953 TabStripModel::ADD_INHERIT_GROUP); 954 EXPECT_EQ(2, tabstrip.active_index()); 955 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE); 956 EXPECT_EQ(0, tabstrip.active_index()); 957 958 tabstrip.CloseAllTabs(); 959 EXPECT_TRUE(tabstrip.empty()); 960 } 961 962 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with 963 // CommandCloseTab. 964 TEST_F(TabStripModelTest, CommandCloseTab) { 965 TabStripDummyDelegate delegate; 966 TabStripModel tabstrip(&delegate, profile()); 967 EXPECT_TRUE(tabstrip.empty()); 968 969 // Make sure can_close is honored. 970 ASSERT_NO_FATAL_FAILURE( 971 PrepareTabstripForSelectionTest(&tabstrip, 1, 0, "0")); 972 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 973 0, TabStripModel::CommandCloseTab)); 974 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab); 975 ASSERT_TRUE(tabstrip.empty()); 976 977 // Make sure close on a tab that is selected affects all the selected tabs. 978 ASSERT_NO_FATAL_FAILURE( 979 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1")); 980 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 981 0, TabStripModel::CommandCloseTab)); 982 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab); 983 // Should have closed tabs 0 and 1. 984 EXPECT_EQ("2", GetTabStripStateString(tabstrip)); 985 986 tabstrip.CloseAllTabs(); 987 EXPECT_TRUE(tabstrip.empty()); 988 989 // Select two tabs and make close on a tab that isn't selected doesn't affect 990 // selected tabs. 991 ASSERT_NO_FATAL_FAILURE( 992 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1")); 993 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 994 2, TabStripModel::CommandCloseTab)); 995 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab); 996 // Should have closed tab 2. 997 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip)); 998 tabstrip.CloseAllTabs(); 999 EXPECT_TRUE(tabstrip.empty()); 1000 1001 // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned. 1002 ASSERT_NO_FATAL_FAILURE( 1003 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1")); 1004 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1005 0, TabStripModel::CommandCloseTab)); 1006 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab); 1007 // Should have closed tab 2. 1008 EXPECT_EQ("2", GetTabStripStateString(tabstrip)); 1009 tabstrip.CloseAllTabs(); 1010 EXPECT_TRUE(tabstrip.empty()); 1011 } 1012 1013 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with 1014 // CommandCloseTabs. 1015 TEST_F(TabStripModelTest, CommandCloseOtherTabs) { 1016 TabStripDummyDelegate delegate; 1017 TabStripModel tabstrip(&delegate, profile()); 1018 EXPECT_TRUE(tabstrip.empty()); 1019 1020 // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled 1021 // and close two tabs. 1022 ASSERT_NO_FATAL_FAILURE( 1023 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1")); 1024 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1025 0, TabStripModel::CommandCloseOtherTabs)); 1026 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs); 1027 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip)); 1028 tabstrip.CloseAllTabs(); 1029 EXPECT_TRUE(tabstrip.empty()); 1030 1031 // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it 1032 // with a non-selected index should close the two other tabs. 1033 ASSERT_NO_FATAL_FAILURE( 1034 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1")); 1035 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1036 2, TabStripModel::CommandCloseOtherTabs)); 1037 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs); 1038 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip)); 1039 tabstrip.CloseAllTabs(); 1040 EXPECT_TRUE(tabstrip.empty()); 1041 1042 // Select all, CommandCloseOtherTabs should not be enabled. 1043 ASSERT_NO_FATAL_FAILURE( 1044 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1 2")); 1045 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled( 1046 2, TabStripModel::CommandCloseOtherTabs)); 1047 tabstrip.CloseAllTabs(); 1048 EXPECT_TRUE(tabstrip.empty()); 1049 1050 // Three tabs, pin one, select the two non-pinned. 1051 ASSERT_NO_FATAL_FAILURE( 1052 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1 2")); 1053 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled( 1054 1, TabStripModel::CommandCloseOtherTabs)); 1055 // If we don't pass in the pinned index, the command should be enabled. 1056 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1057 0, TabStripModel::CommandCloseOtherTabs)); 1058 tabstrip.CloseAllTabs(); 1059 EXPECT_TRUE(tabstrip.empty()); 1060 1061 // 3 tabs, one pinned. 1062 ASSERT_NO_FATAL_FAILURE( 1063 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1")); 1064 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1065 1, TabStripModel::CommandCloseOtherTabs)); 1066 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1067 0, TabStripModel::CommandCloseOtherTabs)); 1068 tabstrip.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs); 1069 // The pinned tab shouldn't be closed. 1070 EXPECT_EQ("0p 1", GetTabStripStateString(tabstrip)); 1071 tabstrip.CloseAllTabs(); 1072 EXPECT_TRUE(tabstrip.empty()); 1073 } 1074 1075 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with 1076 // CommandCloseTabsToRight. 1077 TEST_F(TabStripModelTest, CommandCloseTabsToRight) { 1078 TabStripDummyDelegate delegate; 1079 TabStripModel tabstrip(&delegate, profile()); 1080 EXPECT_TRUE(tabstrip.empty()); 1081 1082 // Create three tabs, select last two tabs, CommandCloseTabsToRight should 1083 // only be enabled for the first tab. 1084 ASSERT_NO_FATAL_FAILURE( 1085 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "1 2")); 1086 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1087 0, TabStripModel::CommandCloseTabsToRight)); 1088 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled( 1089 1, TabStripModel::CommandCloseTabsToRight)); 1090 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled( 1091 2, TabStripModel::CommandCloseTabsToRight)); 1092 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight); 1093 EXPECT_EQ("0", GetTabStripStateString(tabstrip)); 1094 tabstrip.CloseAllTabs(); 1095 EXPECT_TRUE(tabstrip.empty()); 1096 } 1097 1098 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with 1099 // CommandTogglePinned. 1100 TEST_F(TabStripModelTest, CommandTogglePinned) { 1101 TabStripDummyDelegate delegate; 1102 TabStripModel tabstrip(&delegate, profile()); 1103 EXPECT_TRUE(tabstrip.empty()); 1104 1105 // Create three tabs with one pinned, pin the first two. 1106 ASSERT_NO_FATAL_FAILURE( 1107 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1")); 1108 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1109 0, TabStripModel::CommandTogglePinned)); 1110 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1111 1, TabStripModel::CommandTogglePinned)); 1112 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled( 1113 2, TabStripModel::CommandTogglePinned)); 1114 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned); 1115 EXPECT_EQ("0p 1p 2", GetTabStripStateString(tabstrip)); 1116 1117 // Execute CommandTogglePinned again, this should unpin. 1118 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned); 1119 EXPECT_EQ("0 1 2", GetTabStripStateString(tabstrip)); 1120 1121 // Pin the last. 1122 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned); 1123 EXPECT_EQ("2p 0 1", GetTabStripStateString(tabstrip)); 1124 1125 tabstrip.CloseAllTabs(); 1126 EXPECT_TRUE(tabstrip.empty()); 1127 } 1128 1129 // Tests the following context menu commands: 1130 // - Close Tab 1131 // - Close Other Tabs 1132 // - Close Tabs To Right 1133 TEST_F(TabStripModelTest, TestContextMenuCloseCommands) { 1134 TabStripDummyDelegate delegate; 1135 TabStripModel tabstrip(&delegate, profile()); 1136 EXPECT_TRUE(tabstrip.empty()); 1137 1138 WebContents* opener = CreateWebContents(); 1139 tabstrip.AppendWebContents(opener, true); 1140 1141 WebContents* contents1 = CreateWebContents(); 1142 WebContents* contents2 = CreateWebContents(); 1143 WebContents* contents3 = CreateWebContents(); 1144 1145 InsertWebContentses(&tabstrip, contents1, contents2, contents3); 1146 EXPECT_EQ(0, tabstrip.active_index()); 1147 1148 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab); 1149 EXPECT_EQ(3, tabstrip.count()); 1150 1151 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight); 1152 EXPECT_EQ(1, tabstrip.count()); 1153 EXPECT_EQ(opener, tabstrip.GetActiveWebContents()); 1154 1155 WebContents* dummy = CreateWebContents(); 1156 tabstrip.AppendWebContents(dummy, false); 1157 1158 contents1 = CreateWebContents(); 1159 contents2 = CreateWebContents(); 1160 contents3 = CreateWebContents(); 1161 InsertWebContentses(&tabstrip, contents1, contents2, contents3); 1162 EXPECT_EQ(5, tabstrip.count()); 1163 1164 int dummy_index = tabstrip.count() - 1; 1165 tabstrip.ActivateTabAt(dummy_index, true); 1166 EXPECT_EQ(dummy, tabstrip.GetActiveWebContents()); 1167 1168 tabstrip.ExecuteContextMenuCommand(dummy_index, 1169 TabStripModel::CommandCloseOtherTabs); 1170 EXPECT_EQ(1, tabstrip.count()); 1171 EXPECT_EQ(dummy, tabstrip.GetActiveWebContents()); 1172 1173 tabstrip.CloseAllTabs(); 1174 EXPECT_TRUE(tabstrip.empty()); 1175 } 1176 1177 // Tests GetIndicesClosedByCommand. 1178 TEST_F(TabStripModelTest, GetIndicesClosedByCommand) { 1179 TabStripDummyDelegate delegate; 1180 TabStripModel tabstrip(&delegate, profile()); 1181 EXPECT_TRUE(tabstrip.empty()); 1182 1183 WebContents* contents1 = CreateWebContents(); 1184 WebContents* contents2 = CreateWebContents(); 1185 WebContents* contents3 = CreateWebContents(); 1186 WebContents* contents4 = CreateWebContents(); 1187 WebContents* contents5 = CreateWebContents(); 1188 1189 tabstrip.AppendWebContents(contents1, true); 1190 tabstrip.AppendWebContents(contents2, true); 1191 tabstrip.AppendWebContents(contents3, true); 1192 tabstrip.AppendWebContents(contents4, true); 1193 tabstrip.AppendWebContents(contents5, true); 1194 1195 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString( 1196 tabstrip, 0, TabStripModel::CommandCloseTabsToRight)); 1197 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString( 1198 tabstrip, 1, TabStripModel::CommandCloseTabsToRight)); 1199 1200 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString( 1201 tabstrip, 0, TabStripModel::CommandCloseOtherTabs)); 1202 EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString( 1203 tabstrip, 1, TabStripModel::CommandCloseOtherTabs)); 1204 1205 // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other 1206 // commands. 1207 tabstrip.SetTabPinned(0, true); 1208 tabstrip.SetTabPinned(1, true); 1209 1210 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString( 1211 tabstrip, 0, TabStripModel::CommandCloseTabsToRight)); 1212 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString( 1213 tabstrip, 2, TabStripModel::CommandCloseTabsToRight)); 1214 1215 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString( 1216 tabstrip, 0, TabStripModel::CommandCloseOtherTabs)); 1217 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString( 1218 tabstrip, 2, TabStripModel::CommandCloseOtherTabs)); 1219 1220 tabstrip.CloseAllTabs(); 1221 EXPECT_TRUE(tabstrip.empty()); 1222 } 1223 1224 // Tests whether or not WebContentses are inserted in the correct position 1225 // using this "smart" function with a simulated middle click action on a series 1226 // of links on the home page. 1227 TEST_F(TabStripModelTest, AddWebContents_MiddleClickLinksAndClose) { 1228 TabStripDummyDelegate delegate; 1229 TabStripModel tabstrip(&delegate, profile()); 1230 EXPECT_TRUE(tabstrip.empty()); 1231 1232 // Open the Home Page. 1233 WebContents* homepage_contents = CreateWebContents(); 1234 tabstrip.AddWebContents( 1235 homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK, 1236 TabStripModel::ADD_ACTIVE); 1237 1238 // Open some other tab, by user typing. 1239 WebContents* typed_page_contents = CreateWebContents(); 1240 tabstrip.AddWebContents( 1241 typed_page_contents, -1, content::PAGE_TRANSITION_TYPED, 1242 TabStripModel::ADD_ACTIVE); 1243 1244 EXPECT_EQ(2, tabstrip.count()); 1245 1246 // Re-select the home page. 1247 tabstrip.ActivateTabAt(0, true); 1248 1249 // Open a bunch of tabs by simulating middle clicking on links on the home 1250 // page. 1251 WebContents* middle_click_contents1 = CreateWebContents(); 1252 tabstrip.AddWebContents( 1253 middle_click_contents1, -1, content::PAGE_TRANSITION_LINK, 1254 TabStripModel::ADD_NONE); 1255 WebContents* middle_click_contents2 = CreateWebContents(); 1256 tabstrip.AddWebContents( 1257 middle_click_contents2, -1, content::PAGE_TRANSITION_LINK, 1258 TabStripModel::ADD_NONE); 1259 WebContents* middle_click_contents3 = CreateWebContents(); 1260 tabstrip.AddWebContents( 1261 middle_click_contents3, -1, content::PAGE_TRANSITION_LINK, 1262 TabStripModel::ADD_NONE); 1263 1264 EXPECT_EQ(5, tabstrip.count()); 1265 1266 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0)); 1267 EXPECT_EQ(middle_click_contents1, tabstrip.GetWebContentsAt(1)); 1268 EXPECT_EQ(middle_click_contents2, tabstrip.GetWebContentsAt(2)); 1269 EXPECT_EQ(middle_click_contents3, tabstrip.GetWebContentsAt(3)); 1270 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(4)); 1271 1272 // Now simulate selecting a tab in the middle of the group of tabs opened from 1273 // the home page and start closing them. Each WebContents in the group 1274 // should be closed, right to left. This test is constructed to start at the 1275 // middle WebContents in the group to make sure the cursor wraps around 1276 // to the first WebContents in the group before closing the opener or 1277 // any other WebContents. 1278 tabstrip.ActivateTabAt(2, true); 1279 tabstrip.CloseSelectedTabs(); 1280 EXPECT_EQ(middle_click_contents3, tabstrip.GetActiveWebContents()); 1281 tabstrip.CloseSelectedTabs(); 1282 EXPECT_EQ(middle_click_contents1, tabstrip.GetActiveWebContents()); 1283 tabstrip.CloseSelectedTabs(); 1284 EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents()); 1285 tabstrip.CloseSelectedTabs(); 1286 EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents()); 1287 1288 EXPECT_EQ(1, tabstrip.count()); 1289 1290 tabstrip.CloseAllTabs(); 1291 EXPECT_TRUE(tabstrip.empty()); 1292 } 1293 1294 // Tests whether or not a WebContents created by a left click on a link 1295 // that opens a new tab is inserted correctly adjacent to the tab that spawned 1296 // it. 1297 TEST_F(TabStripModelTest, AddWebContents_LeftClickPopup) { 1298 TabStripDummyDelegate delegate; 1299 TabStripModel tabstrip(&delegate, profile()); 1300 EXPECT_TRUE(tabstrip.empty()); 1301 1302 // Open the Home Page. 1303 WebContents* homepage_contents = CreateWebContents(); 1304 tabstrip.AddWebContents( 1305 homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK, 1306 TabStripModel::ADD_ACTIVE); 1307 1308 // Open some other tab, by user typing. 1309 WebContents* typed_page_contents = CreateWebContents(); 1310 tabstrip.AddWebContents( 1311 typed_page_contents, -1, content::PAGE_TRANSITION_TYPED, 1312 TabStripModel::ADD_ACTIVE); 1313 1314 EXPECT_EQ(2, tabstrip.count()); 1315 1316 // Re-select the home page. 1317 tabstrip.ActivateTabAt(0, true); 1318 1319 // Open a tab by simulating a left click on a link that opens in a new tab. 1320 WebContents* left_click_contents = CreateWebContents(); 1321 tabstrip.AddWebContents(left_click_contents, -1, 1322 content::PAGE_TRANSITION_LINK, 1323 TabStripModel::ADD_ACTIVE); 1324 1325 // Verify the state meets our expectations. 1326 EXPECT_EQ(3, tabstrip.count()); 1327 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0)); 1328 EXPECT_EQ(left_click_contents, tabstrip.GetWebContentsAt(1)); 1329 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(2)); 1330 1331 // The newly created tab should be selected. 1332 EXPECT_EQ(left_click_contents, tabstrip.GetActiveWebContents()); 1333 1334 // After closing the selected tab, the selection should move to the left, to 1335 // the opener. 1336 tabstrip.CloseSelectedTabs(); 1337 EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents()); 1338 1339 EXPECT_EQ(2, tabstrip.count()); 1340 1341 tabstrip.CloseAllTabs(); 1342 EXPECT_TRUE(tabstrip.empty()); 1343 } 1344 1345 // Tests whether or not new tabs that should split context (typed pages, 1346 // generated urls, also blank tabs) open at the end of the tabstrip instead of 1347 // in the middle. 1348 TEST_F(TabStripModelTest, AddWebContents_CreateNewBlankTab) { 1349 TabStripDummyDelegate delegate; 1350 TabStripModel tabstrip(&delegate, profile()); 1351 EXPECT_TRUE(tabstrip.empty()); 1352 1353 // Open the Home Page. 1354 WebContents* homepage_contents = CreateWebContents(); 1355 tabstrip.AddWebContents( 1356 homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK, 1357 TabStripModel::ADD_ACTIVE); 1358 1359 // Open some other tab, by user typing. 1360 WebContents* typed_page_contents = CreateWebContents(); 1361 tabstrip.AddWebContents( 1362 typed_page_contents, -1, content::PAGE_TRANSITION_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 new blank tab in the foreground. 1371 WebContents* new_blank_contents = CreateWebContents(); 1372 tabstrip.AddWebContents(new_blank_contents, -1, 1373 content::PAGE_TRANSITION_TYPED, 1374 TabStripModel::ADD_ACTIVE); 1375 1376 // Verify the state of the tabstrip. 1377 EXPECT_EQ(3, tabstrip.count()); 1378 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0)); 1379 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(1)); 1380 EXPECT_EQ(new_blank_contents, tabstrip.GetWebContentsAt(2)); 1381 1382 // Now open a couple more blank tabs in the background. 1383 WebContents* background_blank_contents1 = CreateWebContents(); 1384 tabstrip.AddWebContents( 1385 background_blank_contents1, -1, content::PAGE_TRANSITION_TYPED, 1386 TabStripModel::ADD_NONE); 1387 WebContents* background_blank_contents2 = CreateWebContents(); 1388 tabstrip.AddWebContents( 1389 background_blank_contents2, -1, content::PAGE_TRANSITION_GENERATED, 1390 TabStripModel::ADD_NONE); 1391 EXPECT_EQ(5, tabstrip.count()); 1392 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0)); 1393 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(1)); 1394 EXPECT_EQ(new_blank_contents, tabstrip.GetWebContentsAt(2)); 1395 EXPECT_EQ(background_blank_contents1, tabstrip.GetWebContentsAt(3)); 1396 EXPECT_EQ(background_blank_contents2, tabstrip.GetWebContentsAt(4)); 1397 1398 tabstrip.CloseAllTabs(); 1399 EXPECT_TRUE(tabstrip.empty()); 1400 } 1401 1402 // Tests whether opener state is correctly forgotten when the user switches 1403 // context. 1404 TEST_F(TabStripModelTest, AddWebContents_ForgetOpeners) { 1405 TabStripDummyDelegate delegate; 1406 TabStripModel tabstrip(&delegate, profile()); 1407 EXPECT_TRUE(tabstrip.empty()); 1408 1409 // Open the Home Page 1410 WebContents* homepage_contents = CreateWebContents(); 1411 tabstrip.AddWebContents( 1412 homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK, 1413 TabStripModel::ADD_ACTIVE); 1414 1415 // Open some other tab, by user typing. 1416 WebContents* typed_page_contents = CreateWebContents(); 1417 tabstrip.AddWebContents( 1418 typed_page_contents, -1, content::PAGE_TRANSITION_TYPED, 1419 TabStripModel::ADD_ACTIVE); 1420 1421 EXPECT_EQ(2, tabstrip.count()); 1422 1423 // Re-select the home page. 1424 tabstrip.ActivateTabAt(0, true); 1425 1426 // Open a bunch of tabs by simulating middle clicking on links on the home 1427 // page. 1428 WebContents* middle_click_contents1 = CreateWebContents(); 1429 tabstrip.AddWebContents( 1430 middle_click_contents1, -1, content::PAGE_TRANSITION_LINK, 1431 TabStripModel::ADD_NONE); 1432 WebContents* middle_click_contents2 = CreateWebContents(); 1433 tabstrip.AddWebContents( 1434 middle_click_contents2, -1, content::PAGE_TRANSITION_LINK, 1435 TabStripModel::ADD_NONE); 1436 WebContents* middle_click_contents3 = CreateWebContents(); 1437 tabstrip.AddWebContents( 1438 middle_click_contents3, -1, content::PAGE_TRANSITION_LINK, 1439 TabStripModel::ADD_NONE); 1440 1441 // Break out of the context by selecting a tab in a different context. 1442 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(4)); 1443 tabstrip.SelectLastTab(); 1444 EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents()); 1445 1446 // Step back into the context by selecting a tab inside it. 1447 tabstrip.ActivateTabAt(2, true); 1448 EXPECT_EQ(middle_click_contents2, tabstrip.GetActiveWebContents()); 1449 1450 // Now test that closing tabs selects to the right until there are no more, 1451 // then to the left, as if there were no context (context has been 1452 // successfully forgotten). 1453 tabstrip.CloseSelectedTabs(); 1454 EXPECT_EQ(middle_click_contents3, tabstrip.GetActiveWebContents()); 1455 tabstrip.CloseSelectedTabs(); 1456 EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents()); 1457 tabstrip.CloseSelectedTabs(); 1458 EXPECT_EQ(middle_click_contents1, tabstrip.GetActiveWebContents()); 1459 tabstrip.CloseSelectedTabs(); 1460 EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents()); 1461 1462 EXPECT_EQ(1, tabstrip.count()); 1463 1464 tabstrip.CloseAllTabs(); 1465 EXPECT_TRUE(tabstrip.empty()); 1466 } 1467 1468 // Added for http://b/issue?id=958960 1469 TEST_F(TabStripModelTest, AppendContentsReselectionTest) { 1470 TabStripDummyDelegate delegate; 1471 TabStripModel tabstrip(&delegate, profile()); 1472 EXPECT_TRUE(tabstrip.empty()); 1473 1474 // Open the Home Page. 1475 WebContents* homepage_contents = CreateWebContents(); 1476 tabstrip.AddWebContents( 1477 homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK, 1478 TabStripModel::ADD_ACTIVE); 1479 1480 // Open some other tab, by user typing. 1481 WebContents* typed_page_contents = CreateWebContents(); 1482 tabstrip.AddWebContents( 1483 typed_page_contents, -1, content::PAGE_TRANSITION_TYPED, 1484 TabStripModel::ADD_NONE); 1485 1486 // The selected tab should still be the first. 1487 EXPECT_EQ(0, tabstrip.active_index()); 1488 1489 // Now simulate a link click that opens a new tab (by virtue of target=_blank) 1490 // and make sure the correct tab gets selected when the new tab is closed. 1491 WebContents* target_blank = CreateWebContents(); 1492 tabstrip.AppendWebContents(target_blank, true); 1493 EXPECT_EQ(2, tabstrip.active_index()); 1494 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE); 1495 EXPECT_EQ(0, tabstrip.active_index()); 1496 1497 // Clean up after ourselves. 1498 tabstrip.CloseAllTabs(); 1499 } 1500 1501 // Added for http://b/issue?id=1027661 1502 TEST_F(TabStripModelTest, ReselectionConsidersChildrenTest) { 1503 TabStripDummyDelegate delegate; 1504 TabStripModel strip(&delegate, profile()); 1505 1506 // Open page A 1507 WebContents* page_a_contents = CreateWebContents(); 1508 strip.AddWebContents( 1509 page_a_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK, 1510 TabStripModel::ADD_ACTIVE); 1511 1512 // Simulate middle click to open page A.A and A.B 1513 WebContents* page_a_a_contents = CreateWebContents(); 1514 strip.AddWebContents(page_a_a_contents, -1, content::PAGE_TRANSITION_LINK, 1515 TabStripModel::ADD_NONE); 1516 WebContents* page_a_b_contents = CreateWebContents(); 1517 strip.AddWebContents(page_a_b_contents, -1, content::PAGE_TRANSITION_LINK, 1518 TabStripModel::ADD_NONE); 1519 1520 // Select page A.A 1521 strip.ActivateTabAt(1, true); 1522 EXPECT_EQ(page_a_a_contents, strip.GetActiveWebContents()); 1523 1524 // Simulate a middle click to open page A.A.A 1525 WebContents* page_a_a_a_contents = CreateWebContents(); 1526 strip.AddWebContents(page_a_a_a_contents, -1, content::PAGE_TRANSITION_LINK, 1527 TabStripModel::ADD_NONE); 1528 1529 EXPECT_EQ(page_a_a_a_contents, strip.GetWebContentsAt(2)); 1530 1531 // Close page A.A 1532 strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE); 1533 1534 // Page A.A.A should be selected, NOT A.B 1535 EXPECT_EQ(page_a_a_a_contents, strip.GetActiveWebContents()); 1536 1537 // Close page A.A.A 1538 strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE); 1539 1540 // Page A.B should be selected 1541 EXPECT_EQ(page_a_b_contents, strip.GetActiveWebContents()); 1542 1543 // Close page A.B 1544 strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE); 1545 1546 // Page A should be selected 1547 EXPECT_EQ(page_a_contents, strip.GetActiveWebContents()); 1548 1549 // Clean up. 1550 strip.CloseAllTabs(); 1551 } 1552 1553 TEST_F(TabStripModelTest, AddWebContents_NewTabAtEndOfStripInheritsGroup) { 1554 TabStripDummyDelegate delegate; 1555 TabStripModel strip(&delegate, profile()); 1556 1557 // Open page A 1558 WebContents* page_a_contents = CreateWebContents(); 1559 strip.AddWebContents(page_a_contents, -1, 1560 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 1561 TabStripModel::ADD_ACTIVE); 1562 1563 // Open pages B, C and D in the background from links on page A... 1564 WebContents* page_b_contents = CreateWebContents(); 1565 WebContents* page_c_contents = CreateWebContents(); 1566 WebContents* page_d_contents = CreateWebContents(); 1567 strip.AddWebContents(page_b_contents, -1, content::PAGE_TRANSITION_LINK, 1568 TabStripModel::ADD_NONE); 1569 strip.AddWebContents(page_c_contents, -1, content::PAGE_TRANSITION_LINK, 1570 TabStripModel::ADD_NONE); 1571 strip.AddWebContents(page_d_contents, -1, content::PAGE_TRANSITION_LINK, 1572 TabStripModel::ADD_NONE); 1573 1574 // Switch to page B's tab. 1575 strip.ActivateTabAt(1, true); 1576 1577 // Open a New Tab at the end of the strip (simulate Ctrl+T) 1578 WebContents* new_contents = CreateWebContents(); 1579 strip.AddWebContents(new_contents, -1, content::PAGE_TRANSITION_TYPED, 1580 TabStripModel::ADD_ACTIVE); 1581 1582 EXPECT_EQ(4, strip.GetIndexOfWebContents(new_contents)); 1583 EXPECT_EQ(4, strip.active_index()); 1584 1585 // Close the New Tab that was just opened. We should be returned to page B's 1586 // Tab... 1587 strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE); 1588 1589 EXPECT_EQ(1, strip.active_index()); 1590 1591 // Open a non-New Tab tab at the end of the strip, with a TYPED transition. 1592 // This is like typing a URL in the address bar and pressing Alt+Enter. The 1593 // behavior should be the same as above. 1594 WebContents* page_e_contents = CreateWebContents(); 1595 strip.AddWebContents(page_e_contents, -1, content::PAGE_TRANSITION_TYPED, 1596 TabStripModel::ADD_ACTIVE); 1597 1598 EXPECT_EQ(4, strip.GetIndexOfWebContents(page_e_contents)); 1599 EXPECT_EQ(4, strip.active_index()); 1600 1601 // Close the Tab. Selection should shift back to page B's Tab. 1602 strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE); 1603 1604 EXPECT_EQ(1, strip.active_index()); 1605 1606 // Open a non-New Tab tab at the end of the strip, with some other 1607 // transition. This is like right clicking on a bookmark and choosing "Open 1608 // in New Tab". No opener relationship should be preserved between this Tab 1609 // and the one that was active when the gesture was performed. 1610 WebContents* page_f_contents = CreateWebContents(); 1611 strip.AddWebContents(page_f_contents, -1, 1612 content::PAGE_TRANSITION_AUTO_BOOKMARK, 1613 TabStripModel::ADD_ACTIVE); 1614 1615 EXPECT_EQ(4, strip.GetIndexOfWebContents(page_f_contents)); 1616 EXPECT_EQ(4, strip.active_index()); 1617 1618 // Close the Tab. The next-adjacent should be selected. 1619 strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE); 1620 1621 EXPECT_EQ(3, strip.active_index()); 1622 1623 // Clean up. 1624 strip.CloseAllTabs(); 1625 } 1626 1627 // A test of navigations in a tab that is part of a group of opened from some 1628 // parent tab. If the navigations are link clicks, the group relationship of 1629 // the tab to its parent are preserved. If they are of any other type, they are 1630 // not preserved. 1631 TEST_F(TabStripModelTest, NavigationForgetsOpeners) { 1632 TabStripDummyDelegate delegate; 1633 TabStripModel strip(&delegate, profile()); 1634 1635 // Open page A 1636 WebContents* page_a_contents = CreateWebContents(); 1637 strip.AddWebContents(page_a_contents, -1, 1638 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 1639 TabStripModel::ADD_ACTIVE); 1640 1641 // Open pages B, C and D in the background from links on page A... 1642 WebContents* page_b_contents = CreateWebContents(); 1643 WebContents* page_c_contents = CreateWebContents(); 1644 WebContents* page_d_contents = CreateWebContents(); 1645 strip.AddWebContents(page_b_contents, -1, content::PAGE_TRANSITION_LINK, 1646 TabStripModel::ADD_NONE); 1647 strip.AddWebContents(page_c_contents, -1, content::PAGE_TRANSITION_LINK, 1648 TabStripModel::ADD_NONE); 1649 strip.AddWebContents(page_d_contents, -1, content::PAGE_TRANSITION_LINK, 1650 TabStripModel::ADD_NONE); 1651 1652 // Open page E in a different opener group from page A. 1653 WebContents* page_e_contents = CreateWebContents(); 1654 strip.AddWebContents(page_e_contents, -1, 1655 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 1656 TabStripModel::ADD_NONE); 1657 1658 // Tell the TabStripModel that we are navigating page D via a link click. 1659 strip.ActivateTabAt(3, true); 1660 strip.TabNavigating(page_d_contents, content::PAGE_TRANSITION_LINK); 1661 1662 // Close page D, page C should be selected. (part of same group). 1663 strip.CloseWebContentsAt(3, TabStripModel::CLOSE_NONE); 1664 EXPECT_EQ(2, strip.active_index()); 1665 1666 // Tell the TabStripModel that we are navigating in page C via a bookmark. 1667 strip.TabNavigating(page_c_contents, content::PAGE_TRANSITION_AUTO_BOOKMARK); 1668 1669 // Close page C, page E should be selected. (C is no longer part of the 1670 // A-B-C-D group, selection moves to the right). 1671 strip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE); 1672 EXPECT_EQ(page_e_contents, strip.GetWebContentsAt(strip.active_index())); 1673 1674 strip.CloseAllTabs(); 1675 } 1676 1677 // A test that the forgetting behavior tested in NavigationForgetsOpeners above 1678 // doesn't cause the opener relationship for a New Tab opened at the end of the 1679 // TabStrip to be reset (Test 1 below), unless another any other tab is 1680 // selected (Test 2 below). 1681 TEST_F(TabStripModelTest, NavigationForgettingDoesntAffectNewTab) { 1682 TabStripDummyDelegate delegate; 1683 TabStripModel strip(&delegate, profile()); 1684 1685 // Open a tab and several tabs from it, then select one of the tabs that was 1686 // opened. 1687 WebContents* page_a_contents = CreateWebContents(); 1688 strip.AddWebContents(page_a_contents, -1, 1689 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 1690 TabStripModel::ADD_ACTIVE); 1691 1692 WebContents* page_b_contents = CreateWebContents(); 1693 WebContents* page_c_contents = CreateWebContents(); 1694 WebContents* page_d_contents = CreateWebContents(); 1695 strip.AddWebContents(page_b_contents, -1, content::PAGE_TRANSITION_LINK, 1696 TabStripModel::ADD_NONE); 1697 strip.AddWebContents(page_c_contents, -1, content::PAGE_TRANSITION_LINK, 1698 TabStripModel::ADD_NONE); 1699 strip.AddWebContents(page_d_contents, -1, content::PAGE_TRANSITION_LINK, 1700 TabStripModel::ADD_NONE); 1701 1702 strip.ActivateTabAt(2, true); 1703 1704 // TEST 1: If the user is in a group of tabs and opens a new tab at the end 1705 // of the strip, closing that new tab will select the tab that they were 1706 // last on. 1707 1708 // Now simulate opening a new tab at the end of the TabStrip. 1709 WebContents* new_contents1 = CreateWebContents(); 1710 strip.AddWebContents(new_contents1, -1, content::PAGE_TRANSITION_TYPED, 1711 TabStripModel::ADD_ACTIVE); 1712 1713 // At this point, if we close this tab the last selected one should be 1714 // re-selected. 1715 strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE); 1716 EXPECT_EQ(page_c_contents, strip.GetWebContentsAt(strip.active_index())); 1717 1718 // TEST 2: If the user is in a group of tabs and opens a new tab at the end 1719 // of the strip, selecting any other tab in the strip will cause that new 1720 // tab's opener relationship to be forgotten. 1721 1722 // Open a new tab again. 1723 WebContents* new_contents2 = CreateWebContents(); 1724 strip.AddWebContents(new_contents2, -1, content::PAGE_TRANSITION_TYPED, 1725 TabStripModel::ADD_ACTIVE); 1726 1727 // Now select the first tab. 1728 strip.ActivateTabAt(0, true); 1729 1730 // Now select the last tab. 1731 strip.ActivateTabAt(strip.count() - 1, true); 1732 1733 // Now close the last tab. The next adjacent should be selected. 1734 strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE); 1735 EXPECT_EQ(page_d_contents, strip.GetWebContentsAt(strip.active_index())); 1736 1737 strip.CloseAllTabs(); 1738 } 1739 1740 // This fails on Linux when run with the rest of unit_tests (crbug.com/302156) 1741 // and fails consistently on Mac and Windows. 1742 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN) 1743 #define MAYBE_FastShutdown \ 1744 DISABLED_FastShutdown 1745 #else 1746 #define MAYBE_FastShutdown \ 1747 FastShutdown 1748 #endif 1749 // Tests that fast shutdown is attempted appropriately. 1750 TEST_F(TabStripModelTest, MAYBE_FastShutdown) { 1751 TabStripDummyDelegate delegate; 1752 TabStripModel tabstrip(&delegate, profile()); 1753 MockTabStripModelObserver observer(&tabstrip); 1754 tabstrip.AddObserver(&observer); 1755 1756 EXPECT_TRUE(tabstrip.empty()); 1757 1758 // Make sure fast shutdown is attempted when tabs that share a RPH are shut 1759 // down. 1760 { 1761 WebContents* contents1 = CreateWebContents(); 1762 WebContents* contents2 = CreateWebContentsWithSharedRPH(contents1); 1763 1764 SetID(contents1, 1); 1765 SetID(contents2, 2); 1766 1767 tabstrip.AppendWebContents(contents1, true); 1768 tabstrip.AppendWebContents(contents2, true); 1769 1770 // Turn on the fake unload listener so the tabs don't actually get shut 1771 // down when we call CloseAllTabs()---we need to be able to check that 1772 // fast shutdown was attempted. 1773 delegate.set_run_unload_listener(true); 1774 tabstrip.CloseAllTabs(); 1775 // On a mock RPH this checks whether we *attempted* fast shutdown. 1776 // A real RPH would reject our attempt since there is an unload handler. 1777 EXPECT_TRUE(contents1->GetRenderProcessHost()->FastShutdownStarted()); 1778 EXPECT_EQ(2, tabstrip.count()); 1779 1780 delegate.set_run_unload_listener(false); 1781 tabstrip.CloseAllTabs(); 1782 EXPECT_TRUE(tabstrip.empty()); 1783 } 1784 1785 // Make sure fast shutdown is not attempted when only some tabs that share a 1786 // RPH are shut down. 1787 { 1788 WebContents* contents1 = CreateWebContents(); 1789 WebContents* contents2 = CreateWebContentsWithSharedRPH(contents1); 1790 1791 SetID(contents1, 1); 1792 SetID(contents2, 2); 1793 1794 tabstrip.AppendWebContents(contents1, true); 1795 tabstrip.AppendWebContents(contents2, true); 1796 1797 tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE); 1798 EXPECT_FALSE(contents1->GetRenderProcessHost()->FastShutdownStarted()); 1799 EXPECT_EQ(1, tabstrip.count()); 1800 1801 tabstrip.CloseAllTabs(); 1802 EXPECT_TRUE(tabstrip.empty()); 1803 } 1804 } 1805 1806 // Tests various permutations of apps. 1807 TEST_F(TabStripModelTest, Apps) { 1808 TabStripDummyDelegate delegate; 1809 TabStripModel tabstrip(&delegate, profile()); 1810 MockTabStripModelObserver observer(&tabstrip); 1811 tabstrip.AddObserver(&observer); 1812 1813 EXPECT_TRUE(tabstrip.empty()); 1814 1815 typedef MockTabStripModelObserver::State State; 1816 1817 #if defined(OS_WIN) 1818 base::FilePath path(FILE_PATH_LITERAL("c:\\foo")); 1819 #elif defined(OS_POSIX) 1820 base::FilePath path(FILE_PATH_LITERAL("/foo")); 1821 #endif 1822 1823 base::DictionaryValue manifest; 1824 manifest.SetString("name", "hi!"); 1825 manifest.SetString("version", "1"); 1826 manifest.SetString("app.launch.web_url", "http://www.google.com"); 1827 std::string error; 1828 scoped_refptr<Extension> extension_app( 1829 Extension::Create(path, extensions::Manifest::INVALID_LOCATION, 1830 manifest, Extension::NO_FLAGS, &error)); 1831 WebContents* contents1 = CreateWebContents(); 1832 extensions::TabHelper::CreateForWebContents(contents1); 1833 extensions::TabHelper::FromWebContents(contents1) 1834 ->SetExtensionApp(extension_app.get()); 1835 WebContents* contents2 = CreateWebContents(); 1836 extensions::TabHelper::CreateForWebContents(contents2); 1837 extensions::TabHelper::FromWebContents(contents2) 1838 ->SetExtensionApp(extension_app.get()); 1839 WebContents* contents3 = CreateWebContents(); 1840 1841 SetID(contents1, 1); 1842 SetID(contents2, 2); 1843 SetID(contents3, 3); 1844 1845 // Note! The ordering of these tests is important, each subsequent test 1846 // builds on the state established in the previous. This is important if you 1847 // ever insert tests rather than append. 1848 1849 // Initial state, tab3 only and selected. 1850 tabstrip.AppendWebContents(contents3, true); 1851 1852 observer.ClearStates(); 1853 1854 // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal 1855 // position and tab1 should end up at position 0. 1856 { 1857 tabstrip.InsertWebContentsAt(1, contents1, TabStripModel::ADD_NONE); 1858 1859 ASSERT_EQ(1, observer.GetStateCount()); 1860 State state(contents1, 0, MockTabStripModelObserver::INSERT); 1861 EXPECT_TRUE(observer.StateEquals(0, state)); 1862 1863 // And verify the state. 1864 EXPECT_EQ("1ap 3", GetTabStripStateString(tabstrip)); 1865 1866 observer.ClearStates(); 1867 } 1868 1869 // Insert tab 2 at position 1. 1870 { 1871 tabstrip.InsertWebContentsAt(1, contents2, TabStripModel::ADD_NONE); 1872 1873 ASSERT_EQ(1, observer.GetStateCount()); 1874 State state(contents2, 1, MockTabStripModelObserver::INSERT); 1875 EXPECT_TRUE(observer.StateEquals(0, state)); 1876 1877 // And verify the state. 1878 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip)); 1879 1880 observer.ClearStates(); 1881 } 1882 1883 // Try to move tab 3 to position 0. This isn't legal and should be ignored. 1884 { 1885 tabstrip.MoveWebContentsAt(2, 0, false); 1886 1887 ASSERT_EQ(0, observer.GetStateCount()); 1888 1889 // And verify the state didn't change. 1890 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip)); 1891 1892 observer.ClearStates(); 1893 } 1894 1895 // Try to move tab 0 to position 3. This isn't legal and should be ignored. 1896 { 1897 tabstrip.MoveWebContentsAt(0, 2, false); 1898 1899 ASSERT_EQ(0, observer.GetStateCount()); 1900 1901 // And verify the state didn't change. 1902 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip)); 1903 1904 observer.ClearStates(); 1905 } 1906 1907 // Try to move tab 0 to position 1. This is a legal move. 1908 { 1909 tabstrip.MoveWebContentsAt(0, 1, false); 1910 1911 ASSERT_EQ(1, observer.GetStateCount()); 1912 State state(contents1, 1, MockTabStripModelObserver::MOVE); 1913 state.src_index = 0; 1914 EXPECT_TRUE(observer.StateEquals(0, state)); 1915 1916 // And verify the state didn't change. 1917 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip)); 1918 1919 observer.ClearStates(); 1920 } 1921 1922 // Remove tab3 and insert at position 0. It should be forced to position 2. 1923 { 1924 tabstrip.DetachWebContentsAt(2); 1925 observer.ClearStates(); 1926 1927 tabstrip.InsertWebContentsAt(0, contents3, TabStripModel::ADD_NONE); 1928 1929 ASSERT_EQ(1, observer.GetStateCount()); 1930 State state(contents3, 2, MockTabStripModelObserver::INSERT); 1931 EXPECT_TRUE(observer.StateEquals(0, state)); 1932 1933 // And verify the state didn't change. 1934 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip)); 1935 1936 observer.ClearStates(); 1937 } 1938 1939 tabstrip.CloseAllTabs(); 1940 } 1941 1942 // Tests various permutations of pinning tabs. 1943 TEST_F(TabStripModelTest, Pinning) { 1944 TabStripDummyDelegate delegate; 1945 TabStripModel tabstrip(&delegate, profile()); 1946 MockTabStripModelObserver observer(&tabstrip); 1947 tabstrip.AddObserver(&observer); 1948 1949 EXPECT_TRUE(tabstrip.empty()); 1950 1951 typedef MockTabStripModelObserver::State State; 1952 1953 WebContents* contents1 = CreateWebContents(); 1954 WebContents* contents2 = CreateWebContents(); 1955 WebContents* contents3 = CreateWebContents(); 1956 1957 SetID(contents1, 1); 1958 SetID(contents2, 2); 1959 SetID(contents3, 3); 1960 1961 // Note! The ordering of these tests is important, each subsequent test 1962 // builds on the state established in the previous. This is important if you 1963 // ever insert tests rather than append. 1964 1965 // Initial state, three tabs, first selected. 1966 tabstrip.AppendWebContents(contents1, true); 1967 tabstrip.AppendWebContents(contents2, false); 1968 tabstrip.AppendWebContents(contents3, false); 1969 1970 observer.ClearStates(); 1971 1972 // Pin the first tab, this shouldn't visually reorder anything. 1973 { 1974 tabstrip.SetTabPinned(0, true); 1975 1976 // As the order didn't change, we should get a pinned notification. 1977 ASSERT_EQ(1, observer.GetStateCount()); 1978 State state(contents1, 0, MockTabStripModelObserver::PINNED); 1979 EXPECT_TRUE(observer.StateEquals(0, state)); 1980 1981 // And verify the state. 1982 EXPECT_EQ("1p 2 3", GetTabStripStateString(tabstrip)); 1983 1984 observer.ClearStates(); 1985 } 1986 1987 // Unpin the first tab. 1988 { 1989 tabstrip.SetTabPinned(0, false); 1990 1991 // As the order didn't change, we should get a pinned notification. 1992 ASSERT_EQ(1, observer.GetStateCount()); 1993 State state(contents1, 0, MockTabStripModelObserver::PINNED); 1994 EXPECT_TRUE(observer.StateEquals(0, state)); 1995 1996 // And verify the state. 1997 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip)); 1998 1999 observer.ClearStates(); 2000 } 2001 2002 // Pin the 3rd tab, which should move it to the front. 2003 { 2004 tabstrip.SetTabPinned(2, true); 2005 2006 // The pinning should have resulted in a move and a pinned notification. 2007 ASSERT_EQ(2, observer.GetStateCount()); 2008 State state(contents3, 0, MockTabStripModelObserver::MOVE); 2009 state.src_index = 2; 2010 EXPECT_TRUE(observer.StateEquals(0, state)); 2011 2012 state = State(contents3, 0, MockTabStripModelObserver::PINNED); 2013 EXPECT_TRUE(observer.StateEquals(1, state)); 2014 2015 // And verify the state. 2016 EXPECT_EQ("3p 1 2", GetTabStripStateString(tabstrip)); 2017 2018 observer.ClearStates(); 2019 } 2020 2021 // Pin the tab "1", which shouldn't move anything. 2022 { 2023 tabstrip.SetTabPinned(1, true); 2024 2025 // As the order didn't change, we should get a pinned notification. 2026 ASSERT_EQ(1, observer.GetStateCount()); 2027 State state(contents1, 1, MockTabStripModelObserver::PINNED); 2028 EXPECT_TRUE(observer.StateEquals(0, state)); 2029 2030 // And verify the state. 2031 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip)); 2032 2033 observer.ClearStates(); 2034 } 2035 2036 // Try to move tab "2" to the front, it should be ignored. 2037 { 2038 tabstrip.MoveWebContentsAt(2, 0, false); 2039 2040 // As the order didn't change, we should get a pinned notification. 2041 ASSERT_EQ(0, observer.GetStateCount()); 2042 2043 // And verify the state. 2044 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip)); 2045 2046 observer.ClearStates(); 2047 } 2048 2049 // Unpin tab "3", which implicitly moves it to the end. 2050 { 2051 tabstrip.SetTabPinned(0, false); 2052 2053 ASSERT_EQ(2, observer.GetStateCount()); 2054 State state(contents3, 1, MockTabStripModelObserver::MOVE); 2055 state.src_index = 0; 2056 EXPECT_TRUE(observer.StateEquals(0, state)); 2057 2058 state = State(contents3, 1, MockTabStripModelObserver::PINNED); 2059 EXPECT_TRUE(observer.StateEquals(1, state)); 2060 2061 // And verify the state. 2062 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip)); 2063 2064 observer.ClearStates(); 2065 } 2066 2067 // Unpin tab "3", nothing should happen. 2068 { 2069 tabstrip.SetTabPinned(1, false); 2070 2071 ASSERT_EQ(0, observer.GetStateCount()); 2072 2073 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip)); 2074 2075 observer.ClearStates(); 2076 } 2077 2078 // Pin "3" and "1". 2079 { 2080 tabstrip.SetTabPinned(0, true); 2081 tabstrip.SetTabPinned(1, true); 2082 2083 EXPECT_EQ("1p 3p 2", GetTabStripStateString(tabstrip)); 2084 2085 observer.ClearStates(); 2086 } 2087 2088 WebContents* contents4 = CreateWebContents(); 2089 SetID(contents4, 4); 2090 2091 // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end 2092 // up after them. 2093 { 2094 tabstrip.InsertWebContentsAt(1, contents4, TabStripModel::ADD_NONE); 2095 2096 ASSERT_EQ(1, observer.GetStateCount()); 2097 State state(contents4, 2, MockTabStripModelObserver::INSERT); 2098 EXPECT_TRUE(observer.StateEquals(0, state)); 2099 2100 EXPECT_EQ("1p 3p 4 2", GetTabStripStateString(tabstrip)); 2101 } 2102 2103 tabstrip.CloseAllTabs(); 2104 } 2105 2106 // Makes sure the TabStripModel calls the right observer methods during a 2107 // replace. 2108 TEST_F(TabStripModelTest, ReplaceSendsSelected) { 2109 typedef MockTabStripModelObserver::State State; 2110 2111 TabStripDummyDelegate delegate; 2112 TabStripModel strip(&delegate, profile()); 2113 2114 WebContents* first_contents = CreateWebContents(); 2115 strip.AddWebContents(first_contents, -1, content::PAGE_TRANSITION_TYPED, 2116 TabStripModel::ADD_ACTIVE); 2117 2118 MockTabStripModelObserver tabstrip_observer(&strip); 2119 strip.AddObserver(&tabstrip_observer); 2120 2121 WebContents* new_contents = CreateWebContents(); 2122 delete strip.ReplaceWebContentsAt(0, new_contents); 2123 2124 ASSERT_EQ(2, tabstrip_observer.GetStateCount()); 2125 2126 // First event should be for replaced. 2127 State state(new_contents, 0, MockTabStripModelObserver::REPLACED); 2128 state.src_contents = first_contents; 2129 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state)); 2130 2131 // And the second for selected. 2132 state = State(new_contents, 0, MockTabStripModelObserver::ACTIVATE); 2133 state.src_contents = first_contents; 2134 state.change_reason = TabStripModelObserver::CHANGE_REASON_REPLACED; 2135 EXPECT_TRUE(tabstrip_observer.StateEquals(1, state)); 2136 2137 // Now add another tab and replace it, making sure we don't get a selected 2138 // event this time. 2139 WebContents* third_contents = CreateWebContents(); 2140 strip.AddWebContents(third_contents, 1, content::PAGE_TRANSITION_TYPED, 2141 TabStripModel::ADD_NONE); 2142 2143 tabstrip_observer.ClearStates(); 2144 2145 // And replace it. 2146 new_contents = CreateWebContents(); 2147 delete strip.ReplaceWebContentsAt(1, new_contents); 2148 2149 ASSERT_EQ(1, tabstrip_observer.GetStateCount()); 2150 2151 state = State(new_contents, 1, MockTabStripModelObserver::REPLACED); 2152 state.src_contents = third_contents; 2153 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state)); 2154 2155 strip.CloseAllTabs(); 2156 } 2157 2158 // Ensures discarding tabs leaves TabStripModel in a good state. 2159 TEST_F(TabStripModelTest, DiscardWebContentsAt) { 2160 typedef MockTabStripModelObserver::State State; 2161 2162 TabStripDummyDelegate delegate; 2163 TabStripModel tabstrip(&delegate, profile()); 2164 2165 // Fill it with some tabs. 2166 WebContents* contents1 = CreateWebContents(); 2167 tabstrip.AppendWebContents(contents1, true); 2168 WebContents* contents2 = CreateWebContents(); 2169 tabstrip.AppendWebContents(contents2, true); 2170 2171 // Start watching for events after the appends to avoid observing state 2172 // transitions that aren't relevant to this test. 2173 MockTabStripModelObserver tabstrip_observer(&tabstrip); 2174 tabstrip.AddObserver(&tabstrip_observer); 2175 2176 // Discard one of the tabs. 2177 WebContents* null_contents1 = tabstrip.DiscardWebContentsAt(0); 2178 ASSERT_EQ(2, tabstrip.count()); 2179 EXPECT_TRUE(tabstrip.IsTabDiscarded(0)); 2180 EXPECT_FALSE(tabstrip.IsTabDiscarded(1)); 2181 ASSERT_EQ(null_contents1, tabstrip.GetWebContentsAt(0)); 2182 ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1)); 2183 ASSERT_EQ(1, tabstrip_observer.GetStateCount()); 2184 State state1(null_contents1, 0, MockTabStripModelObserver::REPLACED); 2185 state1.src_contents = contents1; 2186 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state1)); 2187 tabstrip_observer.ClearStates(); 2188 2189 // Discard the same tab again. 2190 WebContents* null_contents2 = tabstrip.DiscardWebContentsAt(0); 2191 ASSERT_EQ(2, tabstrip.count()); 2192 EXPECT_TRUE(tabstrip.IsTabDiscarded(0)); 2193 EXPECT_FALSE(tabstrip.IsTabDiscarded(1)); 2194 ASSERT_EQ(null_contents2, tabstrip.GetWebContentsAt(0)); 2195 ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1)); 2196 ASSERT_EQ(1, tabstrip_observer.GetStateCount()); 2197 State state2(null_contents2, 0, MockTabStripModelObserver::REPLACED); 2198 state2.src_contents = null_contents1; 2199 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state2)); 2200 tabstrip_observer.ClearStates(); 2201 2202 // Activating the tab should clear its discard state. 2203 tabstrip.ActivateTabAt(0, true /* user_gesture */); 2204 ASSERT_EQ(2, tabstrip.count()); 2205 EXPECT_FALSE(tabstrip.IsTabDiscarded(0)); 2206 EXPECT_FALSE(tabstrip.IsTabDiscarded(1)); 2207 2208 // Don't discard active tab. 2209 tabstrip.DiscardWebContentsAt(0); 2210 ASSERT_EQ(2, tabstrip.count()); 2211 EXPECT_FALSE(tabstrip.IsTabDiscarded(0)); 2212 EXPECT_FALSE(tabstrip.IsTabDiscarded(1)); 2213 2214 tabstrip.CloseAllTabs(); 2215 } 2216 2217 // Makes sure TabStripModel handles the case of deleting a tab while removing 2218 // another tab. 2219 TEST_F(TabStripModelTest, DeleteFromDestroy) { 2220 TabStripDummyDelegate delegate; 2221 TabStripModel strip(&delegate, profile()); 2222 WebContents* contents1 = CreateWebContents(); 2223 WebContents* contents2 = CreateWebContents(); 2224 MockTabStripModelObserver tab_strip_model_observer(&strip); 2225 strip.AppendWebContents(contents1, true); 2226 strip.AppendWebContents(contents2, true); 2227 // DeleteWebContentsOnDestroyedObserver deletes contents1 when contents2 sends 2228 // out notification that it is being destroyed. 2229 DeleteWebContentsOnDestroyedObserver observer(contents2, contents1, NULL); 2230 strip.AddObserver(&tab_strip_model_observer); 2231 strip.CloseAllTabs(); 2232 2233 int close_all_count = 0, close_all_canceled_count = 0; 2234 tab_strip_model_observer.GetCloseCounts(&close_all_count, 2235 &close_all_canceled_count); 2236 EXPECT_EQ(1, close_all_count); 2237 EXPECT_EQ(0, close_all_canceled_count); 2238 2239 strip.RemoveObserver(&tab_strip_model_observer); 2240 } 2241 2242 // Makes sure TabStripModel handles the case of deleting another tab and the 2243 // TabStrip while removing another tab. 2244 TEST_F(TabStripModelTest, DeleteTabStripFromDestroy) { 2245 TabStripDummyDelegate delegate; 2246 TabStripModel* strip = new TabStripModel(&delegate, profile()); 2247 MockTabStripModelObserver tab_strip_model_observer(strip); 2248 strip->AddObserver(&tab_strip_model_observer); 2249 WebContents* contents1 = CreateWebContents(); 2250 WebContents* contents2 = CreateWebContents(); 2251 strip->AppendWebContents(contents1, true); 2252 strip->AppendWebContents(contents2, true); 2253 // DeleteWebContentsOnDestroyedObserver deletes |contents1| and |strip| when 2254 // |contents2| sends out notification that it is being destroyed. 2255 DeleteWebContentsOnDestroyedObserver observer(contents2, contents1, strip); 2256 strip->CloseAllTabs(); 2257 EXPECT_TRUE(tab_strip_model_observer.empty()); 2258 EXPECT_TRUE(tab_strip_model_observer.deleted()); 2259 } 2260 2261 TEST_F(TabStripModelTest, MoveSelectedTabsTo) { 2262 struct TestData { 2263 // Number of tabs the tab strip should have. 2264 const int tab_count; 2265 2266 // Number of pinned tabs. 2267 const int pinned_count; 2268 2269 // Index of the tabs to select. 2270 const std::string selected_tabs; 2271 2272 // Index to move the tabs to. 2273 const int target_index; 2274 2275 // Expected state after the move (space separated list of indices). 2276 const std::string state_after_move; 2277 } test_data[] = { 2278 // 1 selected tab. 2279 { 2, 0, "0", 1, "1 0" }, 2280 { 3, 0, "0", 2, "1 2 0" }, 2281 { 3, 0, "2", 0, "2 0 1" }, 2282 { 3, 0, "2", 1, "0 2 1" }, 2283 { 3, 0, "0 1", 0, "0 1 2" }, 2284 2285 // 2 selected tabs. 2286 { 6, 0, "4 5", 1, "0 4 5 1 2 3" }, 2287 { 3, 0, "0 1", 1, "2 0 1" }, 2288 { 4, 0, "0 2", 1, "1 0 2 3" }, 2289 { 6, 0, "0 1", 3, "2 3 4 0 1 5" }, 2290 2291 // 3 selected tabs. 2292 { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" }, 2293 { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" }, 2294 { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" }, 2295 2296 // 5 selected tabs. 2297 { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" }, 2298 2299 // 7 selected tabs 2300 { 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" }, 2301 2302 // With pinned tabs. 2303 { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" }, 2304 { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" }, 2305 { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" }, 2306 { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" }, 2307 2308 { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" }, 2309 }; 2310 2311 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 2312 TabStripDummyDelegate delegate; 2313 TabStripModel strip(&delegate, profile()); 2314 ASSERT_NO_FATAL_FAILURE( 2315 PrepareTabstripForSelectionTest(&strip, test_data[i].tab_count, 2316 test_data[i].pinned_count, 2317 test_data[i].selected_tabs)); 2318 strip.MoveSelectedTabsTo(test_data[i].target_index); 2319 EXPECT_EQ(test_data[i].state_after_move, 2320 GetTabStripStateString(strip)) << i; 2321 strip.CloseAllTabs(); 2322 } 2323 } 2324 2325 // Tests that moving a tab forgets all groups referencing it. 2326 TEST_F(TabStripModelTest, MoveSelectedTabsTo_ForgetGroups) { 2327 TabStripDummyDelegate delegate; 2328 TabStripModel strip(&delegate, profile()); 2329 2330 // Open page A as a new tab and then A1 in the background from A. 2331 WebContents* page_a_contents = CreateWebContents(); 2332 strip.AddWebContents(page_a_contents, -1, 2333 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 2334 TabStripModel::ADD_ACTIVE); 2335 WebContents* page_a1_contents = CreateWebContents(); 2336 strip.AddWebContents(page_a1_contents, -1, content::PAGE_TRANSITION_LINK, 2337 TabStripModel::ADD_NONE); 2338 2339 // Likewise, open pages B and B1. 2340 WebContents* page_b_contents = CreateWebContents(); 2341 strip.AddWebContents(page_b_contents, -1, 2342 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 2343 TabStripModel::ADD_ACTIVE); 2344 WebContents* page_b1_contents = CreateWebContents(); 2345 strip.AddWebContents(page_b1_contents, -1, content::PAGE_TRANSITION_LINK, 2346 TabStripModel::ADD_NONE); 2347 2348 EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(0)); 2349 EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(1)); 2350 EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(2)); 2351 EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(3)); 2352 2353 // Move page B to the start of the tab strip. 2354 strip.MoveSelectedTabsTo(0); 2355 2356 // Open page B2 in the background from B. It should end up after B. 2357 WebContents* page_b2_contents = CreateWebContents(); 2358 strip.AddWebContents(page_b2_contents, -1, content::PAGE_TRANSITION_LINK, 2359 TabStripModel::ADD_NONE); 2360 EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(0)); 2361 EXPECT_EQ(page_b2_contents, strip.GetWebContentsAt(1)); 2362 EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(2)); 2363 EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(3)); 2364 EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(4)); 2365 2366 // Switch to A. 2367 strip.ActivateTabAt(2, true); 2368 EXPECT_EQ(page_a_contents, strip.GetActiveWebContents()); 2369 2370 // Open page A2 in the background from A. It should end up after A1. 2371 WebContents* page_a2_contents = CreateWebContents(); 2372 strip.AddWebContents(page_a2_contents, -1, content::PAGE_TRANSITION_LINK, 2373 TabStripModel::ADD_NONE); 2374 EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(0)); 2375 EXPECT_EQ(page_b2_contents, strip.GetWebContentsAt(1)); 2376 EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(2)); 2377 EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(3)); 2378 EXPECT_EQ(page_a2_contents, strip.GetWebContentsAt(4)); 2379 EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(5)); 2380 2381 strip.CloseAllTabs(); 2382 } 2383 2384 TEST_F(TabStripModelTest, CloseSelectedTabs) { 2385 TabStripDummyDelegate delegate; 2386 TabStripModel strip(&delegate, profile()); 2387 WebContents* contents1 = CreateWebContents(); 2388 WebContents* contents2 = CreateWebContents(); 2389 WebContents* contents3 = CreateWebContents(); 2390 strip.AppendWebContents(contents1, true); 2391 strip.AppendWebContents(contents2, true); 2392 strip.AppendWebContents(contents3, true); 2393 strip.ToggleSelectionAt(1); 2394 strip.CloseSelectedTabs(); 2395 EXPECT_EQ(1, strip.count()); 2396 EXPECT_EQ(0, strip.active_index()); 2397 strip.CloseAllTabs(); 2398 } 2399 2400 TEST_F(TabStripModelTest, MultipleSelection) { 2401 typedef MockTabStripModelObserver::State State; 2402 2403 TabStripDummyDelegate delegate; 2404 TabStripModel strip(&delegate, profile()); 2405 MockTabStripModelObserver observer(&strip); 2406 WebContents* contents0 = CreateWebContents(); 2407 WebContents* contents1 = CreateWebContents(); 2408 WebContents* contents2 = CreateWebContents(); 2409 WebContents* contents3 = CreateWebContents(); 2410 strip.AppendWebContents(contents0, false); 2411 strip.AppendWebContents(contents1, false); 2412 strip.AppendWebContents(contents2, false); 2413 strip.AppendWebContents(contents3, false); 2414 strip.AddObserver(&observer); 2415 2416 // Selection and active tab change. 2417 strip.ActivateTabAt(3, true); 2418 ASSERT_EQ(2, observer.GetStateCount()); 2419 ASSERT_EQ(observer.GetStateAt(0).action, 2420 MockTabStripModelObserver::ACTIVATE); 2421 State s1(contents3, 3, MockTabStripModelObserver::SELECT); 2422 EXPECT_TRUE(observer.StateEquals(1, s1)); 2423 observer.ClearStates(); 2424 2425 // Adding all tabs to selection, active tab is now at 0. 2426 strip.ExtendSelectionTo(0); 2427 ASSERT_EQ(3, observer.GetStateCount()); 2428 ASSERT_EQ(observer.GetStateAt(0).action, 2429 MockTabStripModelObserver::DEACTIVATE); 2430 ASSERT_EQ(observer.GetStateAt(1).action, 2431 MockTabStripModelObserver::ACTIVATE); 2432 State s2(contents0, 0, MockTabStripModelObserver::SELECT); 2433 s2.src_contents = contents3; 2434 s2.src_index = 3; 2435 EXPECT_TRUE(observer.StateEquals(2, s2)); 2436 observer.ClearStates(); 2437 2438 // Toggle the active tab, should make the next index active. 2439 strip.ToggleSelectionAt(0); 2440 EXPECT_EQ(1, strip.active_index()); 2441 EXPECT_EQ(3U, strip.selection_model().size()); 2442 EXPECT_EQ(4, strip.count()); 2443 ASSERT_EQ(3, observer.GetStateCount()); 2444 ASSERT_EQ(observer.GetStateAt(0).action, 2445 MockTabStripModelObserver::DEACTIVATE); 2446 ASSERT_EQ(observer.GetStateAt(1).action, 2447 MockTabStripModelObserver::ACTIVATE); 2448 ASSERT_EQ(observer.GetStateAt(2).action, 2449 MockTabStripModelObserver::SELECT); 2450 observer.ClearStates(); 2451 2452 // Toggle the first tab back to selected and active. 2453 strip.ToggleSelectionAt(0); 2454 EXPECT_EQ(0, strip.active_index()); 2455 EXPECT_EQ(4U, strip.selection_model().size()); 2456 EXPECT_EQ(4, strip.count()); 2457 ASSERT_EQ(3, observer.GetStateCount()); 2458 ASSERT_EQ(observer.GetStateAt(0).action, 2459 MockTabStripModelObserver::DEACTIVATE); 2460 ASSERT_EQ(observer.GetStateAt(1).action, 2461 MockTabStripModelObserver::ACTIVATE); 2462 ASSERT_EQ(observer.GetStateAt(2).action, 2463 MockTabStripModelObserver::SELECT); 2464 observer.ClearStates(); 2465 2466 // Closing one of the selected tabs, not the active one. 2467 strip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE); 2468 EXPECT_EQ(3, strip.count()); 2469 ASSERT_EQ(3, observer.GetStateCount()); 2470 ASSERT_EQ(observer.GetStateAt(0).action, 2471 MockTabStripModelObserver::CLOSE); 2472 ASSERT_EQ(observer.GetStateAt(1).action, 2473 MockTabStripModelObserver::DETACH); 2474 ASSERT_EQ(observer.GetStateAt(2).action, 2475 MockTabStripModelObserver::SELECT); 2476 observer.ClearStates(); 2477 2478 // Closing the active tab, while there are others tabs selected. 2479 strip.CloseWebContentsAt(0, TabStripModel::CLOSE_NONE); 2480 EXPECT_EQ(2, strip.count()); 2481 ASSERT_EQ(5, observer.GetStateCount()); 2482 ASSERT_EQ(observer.GetStateAt(0).action, 2483 MockTabStripModelObserver::CLOSE); 2484 ASSERT_EQ(observer.GetStateAt(1).action, 2485 MockTabStripModelObserver::DETACH); 2486 ASSERT_EQ(observer.GetStateAt(2).action, 2487 MockTabStripModelObserver::DEACTIVATE); 2488 ASSERT_EQ(observer.GetStateAt(3).action, 2489 MockTabStripModelObserver::ACTIVATE); 2490 ASSERT_EQ(observer.GetStateAt(4).action, 2491 MockTabStripModelObserver::SELECT); 2492 observer.ClearStates(); 2493 2494 // Active tab is at 0, deselecting all but the active tab. 2495 strip.ToggleSelectionAt(1); 2496 ASSERT_EQ(1, observer.GetStateCount()); 2497 ASSERT_EQ(observer.GetStateAt(0).action, 2498 MockTabStripModelObserver::SELECT); 2499 observer.ClearStates(); 2500 2501 // Attempting to deselect the only selected and therefore active tab, 2502 // it is ignored (no notifications being sent) and tab at 0 remains selected 2503 // and active. 2504 strip.ToggleSelectionAt(0); 2505 ASSERT_EQ(0, observer.GetStateCount()); 2506 2507 strip.RemoveObserver(&observer); 2508 strip.CloseAllTabs(); 2509 } 2510 2511 // Verifies that if we change the selection from a multi selection to a single 2512 // selection, but not in a way that changes the selected_index that 2513 // TabSelectionChanged is invoked. 2514 TEST_F(TabStripModelTest, MultipleToSingle) { 2515 typedef MockTabStripModelObserver::State State; 2516 2517 TabStripDummyDelegate delegate; 2518 TabStripModel strip(&delegate, profile()); 2519 WebContents* contents1 = CreateWebContents(); 2520 WebContents* contents2 = CreateWebContents(); 2521 strip.AppendWebContents(contents1, false); 2522 strip.AppendWebContents(contents2, false); 2523 strip.ToggleSelectionAt(0); 2524 strip.ToggleSelectionAt(1); 2525 2526 MockTabStripModelObserver observer(&strip); 2527 strip.AddObserver(&observer); 2528 // This changes the selection (0 is no longer selected) but the selected_index 2529 // still remains at 1. 2530 strip.ActivateTabAt(1, true); 2531 ASSERT_EQ(1, observer.GetStateCount()); 2532 State s(contents2, 1, MockTabStripModelObserver::SELECT); 2533 s.src_contents = contents2; 2534 s.src_index = 1; 2535 s.change_reason = TabStripModelObserver::CHANGE_REASON_NONE; 2536 EXPECT_TRUE(observer.StateEquals(0, s)); 2537 strip.RemoveObserver(&observer); 2538 strip.CloseAllTabs(); 2539 } 2540 2541 // Verifies a newly inserted tab retains its previous blocked state. 2542 // http://crbug.com/276334 2543 TEST_F(TabStripModelTest, TabBlockedState) { 2544 // Start with a source tab strip. 2545 TabStripDummyDelegate dummy_tab_strip_delegate; 2546 TabStripModel strip_src(&dummy_tab_strip_delegate, profile()); 2547 TabBlockedStateTestBrowser browser_src(&strip_src); 2548 2549 // Add a tab. 2550 WebContents* contents1 = CreateWebContents(); 2551 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1); 2552 strip_src.AppendWebContents(contents1, false); 2553 2554 // Add another tab. 2555 WebContents* contents2 = CreateWebContents(); 2556 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents2); 2557 strip_src.AppendWebContents(contents2, false); 2558 2559 // Create a destination tab strip. 2560 TabStripModel strip_dst(&dummy_tab_strip_delegate, profile()); 2561 TabBlockedStateTestBrowser browser_dst(&strip_dst); 2562 2563 // Setup a SingleWebContentsDialogManager for tab |contents2|. 2564 web_modal::WebContentsModalDialogManager* modal_dialog_manager = 2565 web_modal::WebContentsModalDialogManager::FromWebContents(contents2); 2566 web_modal::WebContentsModalDialogManager::TestApi test_api( 2567 modal_dialog_manager); 2568 2569 // Show a dialog that blocks tab |contents2|. 2570 // DummySingleWebContentsDialogManager doesn't care about the 2571 // NativeWebContentsModalDialog value, so any dummy value works. 2572 DummySingleWebContentsDialogManager* native_manager = 2573 new DummySingleWebContentsDialogManager( 2574 reinterpret_cast<NativeWebContentsModalDialog>(0), 2575 modal_dialog_manager); 2576 modal_dialog_manager->ShowDialogWithManager( 2577 reinterpret_cast<NativeWebContentsModalDialog>(0), 2578 scoped_ptr<web_modal::SingleWebContentsDialogManager>( 2579 native_manager).Pass()); 2580 EXPECT_TRUE(strip_src.IsTabBlocked(1)); 2581 2582 // Detach the tab. 2583 WebContents* moved_contents = strip_src.DetachWebContentsAt(1); 2584 EXPECT_EQ(contents2, moved_contents); 2585 2586 // Attach the tab to the destination tab strip. 2587 strip_dst.AppendWebContents(moved_contents, true); 2588 EXPECT_TRUE(strip_dst.IsTabBlocked(0)); 2589 2590 strip_dst.CloseAllTabs(); 2591 strip_src.CloseAllTabs(); 2592 } 2593