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