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.
5 #include "chrome/browser/ui/tabs/tab_strip_model.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/path_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/defaults.h"
19 #include "chrome/browser/extensions/tab_helper.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_tabstrip.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h"
25 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
26 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
27 #include "chrome/common/url_constants.h"
28 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
29 #include "chrome/test/base/testing_profile.h"
30 #include "components/web_modal/web_contents_modal_dialog_manager.h"
31 #include "content/public/browser/navigation_controller.h"
32 #include "content/public/browser/navigation_entry.h"
33 #include "content/public/browser/render_process_host.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/browser/web_contents_observer.h"
36 #include "extensions/common/extension.h"
37 #include "testing/gtest/include/gtest/gtest.h"
39 using content::SiteInstance;
40 using content::WebContents;
41 using extensions::Extension;
42 using web_modal::NativeWebContentsModalDialog;
46 // Class used to delete a WebContents and TabStripModel when another WebContents
48 class DeleteWebContentsOnDestroyedObserver
49 : public content::WebContentsObserver {
51 // When |source| is deleted both |tab_to_delete| and |tab_strip| are deleted.
52 // |tab_to_delete| and |tab_strip| may be NULL.
53 DeleteWebContentsOnDestroyedObserver(WebContents* source,
54 WebContents* tab_to_delete,
55 TabStripModel* tab_strip)
56 : WebContentsObserver(source),
57 tab_to_delete_(tab_to_delete),
58 tab_strip_(tab_strip) {
61 virtual void WebContentsDestroyed() OVERRIDE {
62 WebContents* tab_to_delete = tab_to_delete_;
63 tab_to_delete_ = NULL;
64 TabStripModel* tab_strip_to_delete = tab_strip_;
67 delete tab_strip_to_delete;
71 WebContents* tab_to_delete_;
72 TabStripModel* tab_strip_;
74 DISALLOW_COPY_AND_ASSIGN(DeleteWebContentsOnDestroyedObserver);
77 class TabStripDummyDelegate : public TestTabStripModelDelegate {
79 TabStripDummyDelegate() : run_unload_(false) {}
80 virtual ~TabStripDummyDelegate() {}
82 void set_run_unload_listener(bool value) { run_unload_ = value; }
84 virtual bool RunUnloadListenerBeforeClosing(WebContents* contents) OVERRIDE {
89 // Whether to report that we need to run an unload listener before closing.
92 DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate);
95 const char kTabStripModelTestIDUserDataKey[] = "TabStripModelTestIDUserData";
97 class TabStripModelTestIDUserData : public base::SupportsUserData::Data {
99 explicit TabStripModelTestIDUserData(int id) : id_(id) {}
100 virtual ~TabStripModelTestIDUserData() {}
101 int id() { return id_; }
107 class DummySingleWebContentsDialogManager
108 : public web_modal::SingleWebContentsDialogManager {
110 explicit DummySingleWebContentsDialogManager(
111 NativeWebContentsModalDialog dialog,
112 web_modal::SingleWebContentsDialogManagerDelegate* delegate)
113 : delegate_(delegate),
115 virtual ~DummySingleWebContentsDialogManager() {}
117 virtual void Show() OVERRIDE {}
118 virtual void Hide() OVERRIDE {}
119 virtual void Close() OVERRIDE {
120 delegate_->WillClose(dialog_);
122 virtual void Focus() OVERRIDE {}
123 virtual void Pulse() OVERRIDE {}
124 virtual void HostChanged(
125 web_modal::WebContentsModalDialogHost* new_host) OVERRIDE {}
126 virtual NativeWebContentsModalDialog dialog() OVERRIDE { return dialog_; }
129 web_modal::SingleWebContentsDialogManagerDelegate* delegate_;
130 NativeWebContentsModalDialog dialog_;
132 DISALLOW_COPY_AND_ASSIGN(DummySingleWebContentsDialogManager);
135 // Test Browser-like class for TabStripModelTest.TabBlockedState.
136 class TabBlockedStateTestBrowser
137 : public TabStripModelObserver,
138 public web_modal::WebContentsModalDialogManagerDelegate {
140 explicit TabBlockedStateTestBrowser(TabStripModel* tab_strip_model)
141 : tab_strip_model_(tab_strip_model) {
142 tab_strip_model_->AddObserver(this);
145 virtual ~TabBlockedStateTestBrowser() {
146 tab_strip_model_->RemoveObserver(this);
150 // TabStripModelObserver
151 virtual void TabInsertedAt(WebContents* contents,
153 bool foreground) OVERRIDE {
154 web_modal::WebContentsModalDialogManager* manager =
155 web_modal::WebContentsModalDialogManager::FromWebContents(contents);
157 manager->SetDelegate(this);
160 // WebContentsModalDialogManagerDelegate
161 virtual void SetWebContentsBlocked(content::WebContents* contents,
162 bool blocked) OVERRIDE {
163 int index = tab_strip_model_->GetIndexOfWebContents(contents);
165 tab_strip_model_->SetTabBlocked(index, blocked);
168 TabStripModel* tab_strip_model_;
170 DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser);
175 class TabStripModelTest : public ChromeRenderViewHostTestHarness {
177 WebContents* CreateWebContents() {
178 return WebContents::Create(WebContents::CreateParams(profile()));
181 WebContents* CreateWebContentsWithSharedRPH(WebContents* web_contents) {
182 WebContents::CreateParams create_params(
183 profile(), web_contents->GetRenderViewHost()->GetSiteInstance());
184 WebContents* retval = WebContents::Create(create_params);
185 EXPECT_EQ(retval->GetRenderProcessHost(),
186 web_contents->GetRenderProcessHost());
190 // Sets the id of the specified contents.
191 void SetID(WebContents* contents, int id) {
192 contents->SetUserData(&kTabStripModelTestIDUserDataKey,
193 new TabStripModelTestIDUserData(id));
196 // Returns the id of the specified contents.
197 int GetID(WebContents* contents) {
198 TabStripModelTestIDUserData* user_data =
199 static_cast<TabStripModelTestIDUserData*>(
200 contents->GetUserData(&kTabStripModelTestIDUserDataKey));
202 return user_data ? user_data->id() : -1;
205 // Returns the state of the given tab strip as a string. The state consists
206 // of the ID of each web contents followed by a 'p' if pinned. For example,
207 // if the model consists of two tabs with ids 2 and 1, with the first
208 // tab pinned, this returns "2p 1".
209 std::string GetTabStripStateString(const TabStripModel& model) {
211 for (int i = 0; i < model.count(); ++i) {
215 actual += base::IntToString(GetID(model.GetWebContentsAt(i)));
217 if (model.IsAppTab(i))
220 if (model.IsTabPinned(i))
226 std::string GetIndicesClosedByCommandAsString(
227 const TabStripModel& model,
229 TabStripModel::ContextMenuCommand id) const {
230 std::vector<int> indices = model.GetIndicesClosedByCommand(index, id);
232 for (size_t i = 0; i < indices.size(); ++i) {
235 result += base::IntToString(indices[i]);
240 void PrepareTabstripForSelectionTest(TabStripModel* model,
243 const std::string& selected_tabs) {
244 for (int i = 0; i < tab_count; ++i) {
245 WebContents* contents = CreateWebContents();
247 model->AppendWebContents(contents, true);
249 for (int i = 0; i < pinned_count; ++i)
250 model->SetTabPinned(i, true);
252 ui::ListSelectionModel selection_model;
253 std::vector<std::string> selection;
254 base::SplitStringAlongWhitespace(selected_tabs, &selection);
255 for (size_t i = 0; i < selection.size(); ++i) {
257 ASSERT_TRUE(base::StringToInt(selection[i], &value));
258 selection_model.AddIndexToSelection(value);
260 selection_model.set_active(selection_model.selected_indices()[0]);
261 model->SetSelectionFromModel(selection_model);
265 class MockTabStripModelObserver : public TabStripModelObserver {
267 explicit MockTabStripModelObserver(TabStripModel* model)
271 virtual ~MockTabStripModelObserver() {}
273 enum TabStripModelObserverAction {
289 State(WebContents* a_dst_contents,
291 TabStripModelObserverAction a_action)
292 : src_contents(NULL),
293 dst_contents(a_dst_contents),
295 dst_index(a_dst_index),
296 change_reason(CHANGE_REASON_NONE),
301 WebContents* src_contents;
302 WebContents* dst_contents;
307 TabStripModelObserverAction action;
310 int GetStateCount() const {
311 return static_cast<int>(states_.size());
314 // Returns (by way of parameters) the number of state's with CLOSE_ALL and
315 // CLOSE_ALL_CANCELED.
316 void GetCloseCounts(int* close_all_count,
317 int* close_all_canceled_count) {
318 *close_all_count = *close_all_canceled_count = 0;
319 for (int i = 0; i < GetStateCount(); ++i) {
320 switch (GetStateAt(i).action) {
322 (*close_all_count)++;
324 case CLOSE_ALL_CANCELED:
325 (*close_all_canceled_count)++;
333 const State& GetStateAt(int index) const {
334 DCHECK(index >= 0 && index < GetStateCount());
335 return states_[index];
338 bool StateEquals(int index, const State& state) {
339 const State& s = GetStateAt(index);
340 return (s.src_contents == state.src_contents &&
341 s.dst_contents == state.dst_contents &&
342 s.src_index == state.src_index &&
343 s.dst_index == state.dst_index &&
344 s.change_reason == state.change_reason &&
345 s.foreground == state.foreground &&
346 s.action == state.action);
349 // TabStripModelObserver implementation:
350 virtual void TabInsertedAt(WebContents* contents,
352 bool foreground) OVERRIDE {
354 State s(contents, index, INSERT);
355 s.foreground = foreground;
356 states_.push_back(s);
358 virtual void ActiveTabChanged(WebContents* old_contents,
359 WebContents* new_contents,
361 int reason) OVERRIDE {
362 State s(new_contents, index, ACTIVATE);
363 s.src_contents = old_contents;
364 s.change_reason = reason;
365 states_.push_back(s);
367 virtual void TabSelectionChanged(
368 TabStripModel* tab_strip_model,
369 const ui::ListSelectionModel& old_model) OVERRIDE {
370 State s(model()->GetActiveWebContents(), model()->active_index(), SELECT);
371 s.src_contents = model()->GetWebContentsAt(old_model.active());
372 s.src_index = old_model.active();
373 states_.push_back(s);
375 virtual void TabMoved(WebContents* contents,
377 int to_index) OVERRIDE {
378 State s(contents, to_index, MOVE);
379 s.src_index = from_index;
380 states_.push_back(s);
383 virtual void TabClosingAt(TabStripModel* tab_strip_model,
384 WebContents* contents,
385 int index) OVERRIDE {
386 states_.push_back(State(contents, index, CLOSE));
388 virtual void TabDetachedAt(WebContents* contents, int index) OVERRIDE {
389 states_.push_back(State(contents, index, DETACH));
391 virtual void TabDeactivated(WebContents* contents) OVERRIDE {
392 states_.push_back(State(contents, model()->active_index(), DEACTIVATE));
394 virtual void TabChangedAt(WebContents* contents,
396 TabChangeType change_type) OVERRIDE {
397 states_.push_back(State(contents, index, CHANGE));
399 virtual void TabReplacedAt(TabStripModel* tab_strip_model,
400 WebContents* old_contents,
401 WebContents* new_contents,
402 int index) OVERRIDE {
403 State s(new_contents, index, REPLACED);
404 s.src_contents = old_contents;
405 states_.push_back(s);
407 virtual void TabPinnedStateChanged(WebContents* contents,
408 int index) OVERRIDE {
409 states_.push_back(State(contents, index, PINNED));
411 virtual void TabStripEmpty() OVERRIDE {
414 virtual void WillCloseAllTabs() OVERRIDE {
415 states_.push_back(State(NULL, -1, CLOSE_ALL));
417 virtual void CloseAllTabsCanceled() OVERRIDE {
418 states_.push_back(State(NULL, -1, CLOSE_ALL_CANCELED));
420 virtual void TabStripModelDeleted() OVERRIDE {
428 bool empty() const { return empty_; }
429 bool deleted() const { return deleted_; }
430 TabStripModel* model() { return model_; }
433 std::vector<State> states_;
437 TabStripModel* model_;
439 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver);
442 TEST_F(TabStripModelTest, TestBasicAPI) {
443 TabStripDummyDelegate delegate;
444 TabStripModel tabstrip(&delegate, profile());
445 MockTabStripModelObserver observer(&tabstrip);
446 tabstrip.AddObserver(&observer);
448 EXPECT_TRUE(tabstrip.empty());
450 typedef MockTabStripModelObserver::State State;
452 WebContents* contents1 = CreateWebContents();
455 // Note! The ordering of these tests is important, each subsequent test
456 // builds on the state established in the previous. This is important if you
457 // ever insert tests rather than append.
459 // Test AppendWebContents, ContainsIndex
461 EXPECT_FALSE(tabstrip.ContainsIndex(0));
462 tabstrip.AppendWebContents(contents1, true);
463 EXPECT_TRUE(tabstrip.ContainsIndex(0));
464 EXPECT_EQ(1, tabstrip.count());
465 EXPECT_EQ(3, observer.GetStateCount());
466 State s1(contents1, 0, MockTabStripModelObserver::INSERT);
467 s1.foreground = true;
468 EXPECT_TRUE(observer.StateEquals(0, s1));
469 State s2(contents1, 0, MockTabStripModelObserver::ACTIVATE);
470 EXPECT_TRUE(observer.StateEquals(1, s2));
471 State s3(contents1, 0, MockTabStripModelObserver::SELECT);
472 s3.src_contents = NULL;
473 s3.src_index = ui::ListSelectionModel::kUnselectedIndex;
474 EXPECT_TRUE(observer.StateEquals(2, s3));
475 observer.ClearStates();
477 EXPECT_EQ("1", GetTabStripStateString(tabstrip));
479 // Test InsertWebContentsAt, foreground tab.
480 WebContents* contents2 = CreateWebContents();
483 tabstrip.InsertWebContentsAt(1, contents2, TabStripModel::ADD_ACTIVE);
485 EXPECT_EQ(2, tabstrip.count());
486 EXPECT_EQ(4, observer.GetStateCount());
487 State s1(contents2, 1, MockTabStripModelObserver::INSERT);
488 s1.foreground = true;
489 EXPECT_TRUE(observer.StateEquals(0, s1));
490 State s2(contents1, 0, MockTabStripModelObserver::DEACTIVATE);
491 EXPECT_TRUE(observer.StateEquals(1, s2));
492 State s3(contents2, 1, MockTabStripModelObserver::ACTIVATE);
493 s3.src_contents = contents1;
494 EXPECT_TRUE(observer.StateEquals(2, s3));
495 State s4(contents2, 1, MockTabStripModelObserver::SELECT);
496 s4.src_contents = contents1;
498 EXPECT_TRUE(observer.StateEquals(3, s4));
499 observer.ClearStates();
501 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip));
503 // Test InsertWebContentsAt, background tab.
504 WebContents* contents3 = CreateWebContents();
507 tabstrip.InsertWebContentsAt(2, contents3, TabStripModel::ADD_NONE);
509 EXPECT_EQ(3, tabstrip.count());
510 EXPECT_EQ(1, observer.GetStateCount());
511 State s1(contents3, 2, MockTabStripModelObserver::INSERT);
512 s1.foreground = false;
513 EXPECT_TRUE(observer.StateEquals(0, s1));
514 observer.ClearStates();
516 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
518 // Test ActivateTabAt
520 tabstrip.ActivateTabAt(2, true);
521 EXPECT_EQ(3, observer.GetStateCount());
522 State s1(contents2, 1, MockTabStripModelObserver::DEACTIVATE);
523 EXPECT_TRUE(observer.StateEquals(0, s1));
524 State s2(contents3, 2, MockTabStripModelObserver::ACTIVATE);
525 s2.src_contents = contents2;
526 s2.change_reason = TabStripModelObserver::CHANGE_REASON_USER_GESTURE;
527 EXPECT_TRUE(observer.StateEquals(1, s2));
528 State s3(contents3, 2, MockTabStripModelObserver::SELECT);
529 s3.src_contents = contents2;
531 EXPECT_TRUE(observer.StateEquals(2, s3));
532 observer.ClearStates();
534 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
536 // Test DetachWebContentsAt
539 WebContents* detached = tabstrip.DetachWebContentsAt(2);
540 // ... and append again because we want this for later.
541 tabstrip.AppendWebContents(detached, true);
542 EXPECT_EQ(8, observer.GetStateCount());
543 State s1(detached, 2, MockTabStripModelObserver::DETACH);
544 EXPECT_TRUE(observer.StateEquals(0, s1));
545 State s2(detached, ui::ListSelectionModel::kUnselectedIndex,
546 MockTabStripModelObserver::DEACTIVATE);
547 EXPECT_TRUE(observer.StateEquals(1, s2));
548 State s3(contents2, 1, MockTabStripModelObserver::ACTIVATE);
549 s3.src_contents = contents3;
550 s3.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
551 EXPECT_TRUE(observer.StateEquals(2, s3));
552 State s4(contents2, 1, MockTabStripModelObserver::SELECT);
553 s4.src_contents = NULL;
554 s4.src_index = ui::ListSelectionModel::kUnselectedIndex;
555 EXPECT_TRUE(observer.StateEquals(3, s4));
556 State s5(detached, 2, MockTabStripModelObserver::INSERT);
557 s5.foreground = true;
558 EXPECT_TRUE(observer.StateEquals(4, s5));
559 State s6(contents2, 1, MockTabStripModelObserver::DEACTIVATE);
560 EXPECT_TRUE(observer.StateEquals(5, s6));
561 State s7(detached, 2, MockTabStripModelObserver::ACTIVATE);
562 s7.src_contents = contents2;
563 s7.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
564 EXPECT_TRUE(observer.StateEquals(6, s7));
565 State s8(detached, 2, MockTabStripModelObserver::SELECT);
566 s8.src_contents = contents2;
568 EXPECT_TRUE(observer.StateEquals(7, s8));
569 observer.ClearStates();
571 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
573 // Test CloseWebContentsAt
575 EXPECT_TRUE(tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE));
576 EXPECT_EQ(2, tabstrip.count());
578 EXPECT_EQ(5, observer.GetStateCount());
579 State s1(contents3, 2, MockTabStripModelObserver::CLOSE);
580 EXPECT_TRUE(observer.StateEquals(0, s1));
581 State s2(contents3, 2, MockTabStripModelObserver::DETACH);
582 EXPECT_TRUE(observer.StateEquals(1, s2));
583 State s3(contents3, ui::ListSelectionModel::kUnselectedIndex,
584 MockTabStripModelObserver::DEACTIVATE);
585 EXPECT_TRUE(observer.StateEquals(2, s3));
586 State s4(contents2, 1, MockTabStripModelObserver::ACTIVATE);
587 s4.src_contents = contents3;
588 s4.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
589 EXPECT_TRUE(observer.StateEquals(3, s4));
590 State s5(contents2, 1, MockTabStripModelObserver::SELECT);
591 s5.src_contents = NULL;
592 s5.src_index = ui::ListSelectionModel::kUnselectedIndex;
593 EXPECT_TRUE(observer.StateEquals(4, s5));
594 observer.ClearStates();
596 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip));
598 // Test MoveWebContentsAt, select_after_move == true
600 tabstrip.MoveWebContentsAt(1, 0, true);
602 EXPECT_EQ(1, observer.GetStateCount());
603 State s1(contents2, 0, MockTabStripModelObserver::MOVE);
605 EXPECT_TRUE(observer.StateEquals(0, s1));
606 EXPECT_EQ(0, tabstrip.active_index());
607 observer.ClearStates();
609 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip));
611 // Test MoveWebContentsAt, select_after_move == false
613 tabstrip.MoveWebContentsAt(1, 0, false);
614 EXPECT_EQ(1, observer.GetStateCount());
615 State s1(contents1, 0, MockTabStripModelObserver::MOVE);
617 EXPECT_TRUE(observer.StateEquals(0, s1));
618 EXPECT_EQ(1, tabstrip.active_index());
620 tabstrip.MoveWebContentsAt(0, 1, false);
621 observer.ClearStates();
623 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip));
627 EXPECT_EQ(contents2, tabstrip.GetActiveWebContents());
628 EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(0));
629 EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
630 EXPECT_EQ(0, tabstrip.GetIndexOfWebContents(contents2));
631 EXPECT_EQ(1, tabstrip.GetIndexOfWebContents(contents1));
634 // Test UpdateWebContentsStateAt
636 tabstrip.UpdateWebContentsStateAt(0, TabStripModelObserver::ALL);
637 EXPECT_EQ(1, observer.GetStateCount());
638 State s1(contents2, 0, MockTabStripModelObserver::CHANGE);
639 EXPECT_TRUE(observer.StateEquals(0, s1));
640 observer.ClearStates();
643 // Test SelectNextTab, SelectPreviousTab, SelectLastTab
645 // Make sure the second of the two tabs is selected first...
646 tabstrip.ActivateTabAt(1, true);
647 tabstrip.SelectPreviousTab();
648 EXPECT_EQ(0, tabstrip.active_index());
649 tabstrip.SelectLastTab();
650 EXPECT_EQ(1, tabstrip.active_index());
651 tabstrip.SelectNextTab();
652 EXPECT_EQ(0, tabstrip.active_index());
655 // Test CloseSelectedTabs
657 tabstrip.CloseSelectedTabs();
658 // |CloseSelectedTabs| calls CloseWebContentsAt, we already tested that, now
659 // just verify that the count and selected index have changed
661 EXPECT_EQ(1, tabstrip.count());
662 EXPECT_EQ(0, tabstrip.active_index());
665 observer.ClearStates();
666 tabstrip.CloseAllTabs();
668 int close_all_count = 0, close_all_canceled_count = 0;
669 observer.GetCloseCounts(&close_all_count, &close_all_canceled_count);
670 EXPECT_EQ(1, close_all_count);
671 EXPECT_EQ(0, close_all_canceled_count);
673 // TabStripModel should now be empty.
674 EXPECT_TRUE(tabstrip.empty());
676 // Opener methods are tested below...
678 tabstrip.RemoveObserver(&observer);
681 TEST_F(TabStripModelTest, TestBasicOpenerAPI) {
682 TabStripDummyDelegate delegate;
683 TabStripModel tabstrip(&delegate, profile());
684 EXPECT_TRUE(tabstrip.empty());
686 // This is a basic test of opener functionality. opener is created
687 // as the first tab in the strip and then we create 5 other tabs in the
688 // background with opener set as their opener.
690 WebContents* opener = CreateWebContents();
691 tabstrip.AppendWebContents(opener, true);
692 WebContents* contents1 = CreateWebContents();
693 WebContents* contents2 = CreateWebContents();
694 WebContents* contents3 = CreateWebContents();
695 WebContents* contents4 = CreateWebContents();
696 WebContents* contents5 = CreateWebContents();
698 // We use |InsertWebContentsAt| here instead of |AppendWebContents| so that
699 // openership relationships are preserved.
700 tabstrip.InsertWebContentsAt(tabstrip.count(), contents1,
701 TabStripModel::ADD_INHERIT_GROUP);
702 tabstrip.InsertWebContentsAt(tabstrip.count(), contents2,
703 TabStripModel::ADD_INHERIT_GROUP);
704 tabstrip.InsertWebContentsAt(tabstrip.count(), contents3,
705 TabStripModel::ADD_INHERIT_GROUP);
706 tabstrip.InsertWebContentsAt(tabstrip.count(), contents4,
707 TabStripModel::ADD_INHERIT_GROUP);
708 tabstrip.InsertWebContentsAt(tabstrip.count(), contents5,
709 TabStripModel::ADD_INHERIT_GROUP);
711 // All the tabs should have the same opener.
712 for (int i = 1; i < tabstrip.count(); ++i)
713 EXPECT_EQ(opener, tabstrip.GetOpenerOfWebContentsAt(i));
715 // If there is a next adjacent item, then the index should be of that item.
716 EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 1, false));
717 // If the last tab in the group is closed, the preceding tab in the same
718 // group should be selected.
719 EXPECT_EQ(4, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 5, false));
721 // Tests the method that finds the last tab opened by the same opener in the
722 // strip (this is the insertion index for the next background tab for the
723 // specified opener).
724 EXPECT_EQ(5, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
726 // For a tab that has opened no other tabs, the return value should always be
729 tabstrip.GetIndexOfNextWebContentsOpenedBy(contents1, 3, false));
731 tabstrip.GetIndexOfLastWebContentsOpenedBy(contents1, 3));
733 // ForgetAllOpeners should destroy all opener relationships.
734 tabstrip.ForgetAllOpeners();
735 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 1, false));
736 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 5, false));
737 EXPECT_EQ(-1, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
739 // Specify the last tab as the opener of the others.
740 for (int i = 0; i < tabstrip.count() - 1; ++i)
741 tabstrip.SetOpenerOfWebContentsAt(i, contents5);
743 for (int i = 0; i < tabstrip.count() - 1; ++i)
744 EXPECT_EQ(contents5, tabstrip.GetOpenerOfWebContentsAt(i));
746 // If there is a next adjacent item, then the index should be of that item.
747 EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(contents5, 1, false));
749 // If the last tab in the group is closed, the preceding tab in the same
750 // group should be selected.
751 EXPECT_EQ(3, tabstrip.GetIndexOfNextWebContentsOpenedBy(contents5, 4, false));
753 tabstrip.CloseAllTabs();
754 EXPECT_TRUE(tabstrip.empty());
757 static int GetInsertionIndex(TabStripModel* tabstrip) {
758 return tabstrip->order_controller()->DetermineInsertionIndex(
759 content::PAGE_TRANSITION_LINK, false);
762 static void InsertWebContentses(TabStripModel* tabstrip,
763 WebContents* contents1,
764 WebContents* contents2,
765 WebContents* contents3) {
766 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
768 TabStripModel::ADD_INHERIT_GROUP);
769 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
771 TabStripModel::ADD_INHERIT_GROUP);
772 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
774 TabStripModel::ADD_INHERIT_GROUP);
777 // Tests opening background tabs.
778 TEST_F(TabStripModelTest, TestLTRInsertionOptions) {
779 TabStripDummyDelegate delegate;
780 TabStripModel tabstrip(&delegate, profile());
781 EXPECT_TRUE(tabstrip.empty());
783 WebContents* opener = CreateWebContents();
784 tabstrip.AppendWebContents(opener, true);
786 WebContents* contents1 = CreateWebContents();
787 WebContents* contents2 = CreateWebContents();
788 WebContents* contents3 = CreateWebContents();
791 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
792 EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
793 EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(2));
794 EXPECT_EQ(contents3, tabstrip.GetWebContentsAt(3));
796 tabstrip.CloseAllTabs();
797 EXPECT_TRUE(tabstrip.empty());
800 // This test constructs a tabstrip, and then simulates loading several tabs in
801 // the background from link clicks on the first tab. Then it simulates opening
802 // a new tab from the first tab in the foreground via a link click, verifies
803 // that this tab is opened adjacent to the opener, then closes it.
804 // Finally it tests that a tab opened for some non-link purpose opens at the
805 // end of the strip, not bundled to any existing context.
806 TEST_F(TabStripModelTest, TestInsertionIndexDetermination) {
807 TabStripDummyDelegate delegate;
808 TabStripModel tabstrip(&delegate, profile());
809 EXPECT_TRUE(tabstrip.empty());
811 WebContents* opener = CreateWebContents();
812 tabstrip.AppendWebContents(opener, true);
814 // Open some other random unrelated tab in the background to monkey with our
816 WebContents* other = CreateWebContents();
817 tabstrip.AppendWebContents(other, false);
819 WebContents* contents1 = CreateWebContents();
820 WebContents* contents2 = CreateWebContents();
821 WebContents* contents3 = CreateWebContents();
823 // Start by testing LTR.
824 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
825 EXPECT_EQ(opener, tabstrip.GetWebContentsAt(0));
826 EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
827 EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(2));
828 EXPECT_EQ(contents3, tabstrip.GetWebContentsAt(3));
829 EXPECT_EQ(other, tabstrip.GetWebContentsAt(4));
831 // The opener API should work...
832 EXPECT_EQ(3, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 2, false));
833 EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
834 EXPECT_EQ(3, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
836 // Now open a foreground tab from a link. It should be opened adjacent to the
838 WebContents* fg_link_contents = CreateWebContents();
839 int insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
840 content::PAGE_TRANSITION_LINK, true);
841 EXPECT_EQ(1, insert_index);
842 tabstrip.InsertWebContentsAt(insert_index, fg_link_contents,
843 TabStripModel::ADD_ACTIVE |
844 TabStripModel::ADD_INHERIT_GROUP);
845 EXPECT_EQ(1, tabstrip.active_index());
846 EXPECT_EQ(fg_link_contents, tabstrip.GetActiveWebContents());
848 // Now close this contents. The selection should move to the opener contents.
849 tabstrip.CloseSelectedTabs();
850 EXPECT_EQ(0, tabstrip.active_index());
852 // Now open a new empty tab. It should open at the end of the strip.
853 WebContents* fg_nonlink_contents = CreateWebContents();
854 insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
855 content::PAGE_TRANSITION_AUTO_BOOKMARK, true);
856 EXPECT_EQ(tabstrip.count(), insert_index);
857 // We break the opener relationship...
858 tabstrip.InsertWebContentsAt(insert_index,
860 TabStripModel::ADD_NONE);
861 // Now select it, so that user_gesture == true causes the opener relationship
862 // to be forgotten...
863 tabstrip.ActivateTabAt(tabstrip.count() - 1, true);
864 EXPECT_EQ(tabstrip.count() - 1, tabstrip.active_index());
865 EXPECT_EQ(fg_nonlink_contents, tabstrip.GetActiveWebContents());
867 // Verify that all opener relationships are forgotten.
868 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 2, false));
869 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
870 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
871 EXPECT_EQ(-1, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
873 tabstrip.CloseAllTabs();
874 EXPECT_TRUE(tabstrip.empty());
877 // Tests that selection is shifted to the correct tab when a tab is closed.
878 // If a tab is in the background when it is closed, the selection does not
880 // If a tab is in the foreground (selected),
881 // If that tab does not have an opener, selection shifts to the right.
882 // If the tab has an opener,
883 // The next tab (scanning LTR) in the entire strip that has the same opener
885 // If there are no other tabs that have the same opener,
886 // The opener is selected
888 TEST_F(TabStripModelTest, TestSelectOnClose) {
889 TabStripDummyDelegate delegate;
890 TabStripModel tabstrip(&delegate, profile());
891 EXPECT_TRUE(tabstrip.empty());
893 WebContents* opener = CreateWebContents();
894 tabstrip.AppendWebContents(opener, true);
896 WebContents* contents1 = CreateWebContents();
897 WebContents* contents2 = CreateWebContents();
898 WebContents* contents3 = CreateWebContents();
900 // Note that we use Detach instead of Close throughout this test to avoid
901 // having to keep reconstructing these WebContentses.
903 // First test that closing tabs that are in the background doesn't adjust the
904 // current selection.
905 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
906 EXPECT_EQ(0, tabstrip.active_index());
908 tabstrip.DetachWebContentsAt(1);
909 EXPECT_EQ(0, tabstrip.active_index());
911 for (int i = tabstrip.count() - 1; i >= 1; --i)
912 tabstrip.DetachWebContentsAt(i);
914 // Now test that when a tab doesn't have an opener, selection shifts to the
915 // right when the tab is closed.
916 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
917 EXPECT_EQ(0, tabstrip.active_index());
919 tabstrip.ForgetAllOpeners();
920 tabstrip.ActivateTabAt(1, true);
921 EXPECT_EQ(1, tabstrip.active_index());
922 tabstrip.DetachWebContentsAt(1);
923 EXPECT_EQ(1, tabstrip.active_index());
924 tabstrip.DetachWebContentsAt(1);
925 EXPECT_EQ(1, tabstrip.active_index());
926 tabstrip.DetachWebContentsAt(1);
927 EXPECT_EQ(0, tabstrip.active_index());
929 for (int i = tabstrip.count() - 1; i >= 1; --i)
930 tabstrip.DetachWebContentsAt(i);
932 // Now test that when a tab does have an opener, it selects the next tab
933 // opened by the same opener scanning LTR when it is closed.
934 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
935 EXPECT_EQ(0, tabstrip.active_index());
936 tabstrip.ActivateTabAt(2, false);
937 EXPECT_EQ(2, tabstrip.active_index());
938 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
939 EXPECT_EQ(2, tabstrip.active_index());
940 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
941 EXPECT_EQ(1, tabstrip.active_index());
942 tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
943 EXPECT_EQ(0, tabstrip.active_index());
944 // Finally test that when a tab has no "siblings" that the opener is
946 WebContents* other_contents = CreateWebContents();
947 tabstrip.InsertWebContentsAt(1, other_contents,
948 TabStripModel::ADD_NONE);
949 EXPECT_EQ(2, tabstrip.count());
950 WebContents* opened_contents = CreateWebContents();
951 tabstrip.InsertWebContentsAt(2, opened_contents,
952 TabStripModel::ADD_ACTIVE |
953 TabStripModel::ADD_INHERIT_GROUP);
954 EXPECT_EQ(2, tabstrip.active_index());
955 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
956 EXPECT_EQ(0, tabstrip.active_index());
958 tabstrip.CloseAllTabs();
959 EXPECT_TRUE(tabstrip.empty());
962 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
964 TEST_F(TabStripModelTest, CommandCloseTab) {
965 TabStripDummyDelegate delegate;
966 TabStripModel tabstrip(&delegate, profile());
967 EXPECT_TRUE(tabstrip.empty());
969 // Make sure can_close is honored.
970 ASSERT_NO_FATAL_FAILURE(
971 PrepareTabstripForSelectionTest(&tabstrip, 1, 0, "0"));
972 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
973 0, TabStripModel::CommandCloseTab));
974 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
975 ASSERT_TRUE(tabstrip.empty());
977 // Make sure close on a tab that is selected affects all the selected tabs.
978 ASSERT_NO_FATAL_FAILURE(
979 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
980 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
981 0, TabStripModel::CommandCloseTab));
982 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
983 // Should have closed tabs 0 and 1.
984 EXPECT_EQ("2", GetTabStripStateString(tabstrip));
986 tabstrip.CloseAllTabs();
987 EXPECT_TRUE(tabstrip.empty());
989 // Select two tabs and make close on a tab that isn't selected doesn't affect
991 ASSERT_NO_FATAL_FAILURE(
992 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
993 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
994 2, TabStripModel::CommandCloseTab));
995 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
996 // Should have closed tab 2.
997 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
998 tabstrip.CloseAllTabs();
999 EXPECT_TRUE(tabstrip.empty());
1001 // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned.
1002 ASSERT_NO_FATAL_FAILURE(
1003 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1"));
1004 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1005 0, TabStripModel::CommandCloseTab));
1006 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
1007 // Should have closed tab 2.
1008 EXPECT_EQ("2", GetTabStripStateString(tabstrip));
1009 tabstrip.CloseAllTabs();
1010 EXPECT_TRUE(tabstrip.empty());
1013 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1014 // CommandCloseTabs.
1015 TEST_F(TabStripModelTest, CommandCloseOtherTabs) {
1016 TabStripDummyDelegate delegate;
1017 TabStripModel tabstrip(&delegate, profile());
1018 EXPECT_TRUE(tabstrip.empty());
1020 // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled
1021 // and close two tabs.
1022 ASSERT_NO_FATAL_FAILURE(
1023 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
1024 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1025 0, TabStripModel::CommandCloseOtherTabs));
1026 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs);
1027 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
1028 tabstrip.CloseAllTabs();
1029 EXPECT_TRUE(tabstrip.empty());
1031 // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it
1032 // with a non-selected index should close the two other tabs.
1033 ASSERT_NO_FATAL_FAILURE(
1034 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
1035 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1036 2, TabStripModel::CommandCloseOtherTabs));
1037 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs);
1038 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
1039 tabstrip.CloseAllTabs();
1040 EXPECT_TRUE(tabstrip.empty());
1042 // Select all, CommandCloseOtherTabs should not be enabled.
1043 ASSERT_NO_FATAL_FAILURE(
1044 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1 2"));
1045 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1046 2, TabStripModel::CommandCloseOtherTabs));
1047 tabstrip.CloseAllTabs();
1048 EXPECT_TRUE(tabstrip.empty());
1050 // Three tabs, pin one, select the two non-pinned.
1051 ASSERT_NO_FATAL_FAILURE(
1052 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1 2"));
1053 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1054 1, TabStripModel::CommandCloseOtherTabs));
1055 // If we don't pass in the pinned index, the command should be enabled.
1056 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1057 0, TabStripModel::CommandCloseOtherTabs));
1058 tabstrip.CloseAllTabs();
1059 EXPECT_TRUE(tabstrip.empty());
1061 // 3 tabs, one pinned.
1062 ASSERT_NO_FATAL_FAILURE(
1063 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1"));
1064 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1065 1, TabStripModel::CommandCloseOtherTabs));
1066 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1067 0, TabStripModel::CommandCloseOtherTabs));
1068 tabstrip.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs);
1069 // The pinned tab shouldn't be closed.
1070 EXPECT_EQ("0p 1", GetTabStripStateString(tabstrip));
1071 tabstrip.CloseAllTabs();
1072 EXPECT_TRUE(tabstrip.empty());
1075 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1076 // CommandCloseTabsToRight.
1077 TEST_F(TabStripModelTest, CommandCloseTabsToRight) {
1078 TabStripDummyDelegate delegate;
1079 TabStripModel tabstrip(&delegate, profile());
1080 EXPECT_TRUE(tabstrip.empty());
1082 // Create three tabs, select last two tabs, CommandCloseTabsToRight should
1083 // only be enabled for the first tab.
1084 ASSERT_NO_FATAL_FAILURE(
1085 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "1 2"));
1086 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1087 0, TabStripModel::CommandCloseTabsToRight));
1088 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1089 1, TabStripModel::CommandCloseTabsToRight));
1090 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1091 2, TabStripModel::CommandCloseTabsToRight));
1092 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
1093 EXPECT_EQ("0", GetTabStripStateString(tabstrip));
1094 tabstrip.CloseAllTabs();
1095 EXPECT_TRUE(tabstrip.empty());
1098 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1099 // CommandTogglePinned.
1100 TEST_F(TabStripModelTest, CommandTogglePinned) {
1101 TabStripDummyDelegate delegate;
1102 TabStripModel tabstrip(&delegate, profile());
1103 EXPECT_TRUE(tabstrip.empty());
1105 // Create three tabs with one pinned, pin the first two.
1106 ASSERT_NO_FATAL_FAILURE(
1107 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1"));
1108 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1109 0, TabStripModel::CommandTogglePinned));
1110 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1111 1, TabStripModel::CommandTogglePinned));
1112 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1113 2, TabStripModel::CommandTogglePinned));
1114 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned);
1115 EXPECT_EQ("0p 1p 2", GetTabStripStateString(tabstrip));
1117 // Execute CommandTogglePinned again, this should unpin.
1118 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned);
1119 EXPECT_EQ("0 1 2", GetTabStripStateString(tabstrip));
1122 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned);
1123 EXPECT_EQ("2p 0 1", GetTabStripStateString(tabstrip));
1125 tabstrip.CloseAllTabs();
1126 EXPECT_TRUE(tabstrip.empty());
1129 // Tests the following context menu commands:
1131 // - Close Other Tabs
1132 // - Close Tabs To Right
1133 TEST_F(TabStripModelTest, TestContextMenuCloseCommands) {
1134 TabStripDummyDelegate delegate;
1135 TabStripModel tabstrip(&delegate, profile());
1136 EXPECT_TRUE(tabstrip.empty());
1138 WebContents* opener = CreateWebContents();
1139 tabstrip.AppendWebContents(opener, true);
1141 WebContents* contents1 = CreateWebContents();
1142 WebContents* contents2 = CreateWebContents();
1143 WebContents* contents3 = CreateWebContents();
1145 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
1146 EXPECT_EQ(0, tabstrip.active_index());
1148 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
1149 EXPECT_EQ(3, tabstrip.count());
1151 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
1152 EXPECT_EQ(1, tabstrip.count());
1153 EXPECT_EQ(opener, tabstrip.GetActiveWebContents());
1155 WebContents* dummy = CreateWebContents();
1156 tabstrip.AppendWebContents(dummy, false);
1158 contents1 = CreateWebContents();
1159 contents2 = CreateWebContents();
1160 contents3 = CreateWebContents();
1161 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
1162 EXPECT_EQ(5, tabstrip.count());
1164 int dummy_index = tabstrip.count() - 1;
1165 tabstrip.ActivateTabAt(dummy_index, true);
1166 EXPECT_EQ(dummy, tabstrip.GetActiveWebContents());
1168 tabstrip.ExecuteContextMenuCommand(dummy_index,
1169 TabStripModel::CommandCloseOtherTabs);
1170 EXPECT_EQ(1, tabstrip.count());
1171 EXPECT_EQ(dummy, tabstrip.GetActiveWebContents());
1173 tabstrip.CloseAllTabs();
1174 EXPECT_TRUE(tabstrip.empty());
1177 // Tests GetIndicesClosedByCommand.
1178 TEST_F(TabStripModelTest, GetIndicesClosedByCommand) {
1179 TabStripDummyDelegate delegate;
1180 TabStripModel tabstrip(&delegate, profile());
1181 EXPECT_TRUE(tabstrip.empty());
1183 WebContents* contents1 = CreateWebContents();
1184 WebContents* contents2 = CreateWebContents();
1185 WebContents* contents3 = CreateWebContents();
1186 WebContents* contents4 = CreateWebContents();
1187 WebContents* contents5 = CreateWebContents();
1189 tabstrip.AppendWebContents(contents1, true);
1190 tabstrip.AppendWebContents(contents2, true);
1191 tabstrip.AppendWebContents(contents3, true);
1192 tabstrip.AppendWebContents(contents4, true);
1193 tabstrip.AppendWebContents(contents5, true);
1195 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1196 tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
1197 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1198 tabstrip, 1, TabStripModel::CommandCloseTabsToRight));
1200 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1201 tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
1202 EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
1203 tabstrip, 1, TabStripModel::CommandCloseOtherTabs));
1205 // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
1207 tabstrip.SetTabPinned(0, true);
1208 tabstrip.SetTabPinned(1, true);
1210 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1211 tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
1212 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1213 tabstrip, 2, TabStripModel::CommandCloseTabsToRight));
1215 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1216 tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
1217 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1218 tabstrip, 2, TabStripModel::CommandCloseOtherTabs));
1220 tabstrip.CloseAllTabs();
1221 EXPECT_TRUE(tabstrip.empty());
1224 // Tests whether or not WebContentses are inserted in the correct position
1225 // using this "smart" function with a simulated middle click action on a series
1226 // of links on the home page.
1227 TEST_F(TabStripModelTest, AddWebContents_MiddleClickLinksAndClose) {
1228 TabStripDummyDelegate delegate;
1229 TabStripModel tabstrip(&delegate, profile());
1230 EXPECT_TRUE(tabstrip.empty());
1232 // Open the Home Page.
1233 WebContents* homepage_contents = CreateWebContents();
1234 tabstrip.AddWebContents(
1235 homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1236 TabStripModel::ADD_ACTIVE);
1238 // Open some other tab, by user typing.
1239 WebContents* typed_page_contents = CreateWebContents();
1240 tabstrip.AddWebContents(
1241 typed_page_contents, -1, content::PAGE_TRANSITION_TYPED,
1242 TabStripModel::ADD_ACTIVE);
1244 EXPECT_EQ(2, tabstrip.count());
1246 // Re-select the home page.
1247 tabstrip.ActivateTabAt(0, true);
1249 // Open a bunch of tabs by simulating middle clicking on links on the home
1251 WebContents* middle_click_contents1 = CreateWebContents();
1252 tabstrip.AddWebContents(
1253 middle_click_contents1, -1, content::PAGE_TRANSITION_LINK,
1254 TabStripModel::ADD_NONE);
1255 WebContents* middle_click_contents2 = CreateWebContents();
1256 tabstrip.AddWebContents(
1257 middle_click_contents2, -1, content::PAGE_TRANSITION_LINK,
1258 TabStripModel::ADD_NONE);
1259 WebContents* middle_click_contents3 = CreateWebContents();
1260 tabstrip.AddWebContents(
1261 middle_click_contents3, -1, content::PAGE_TRANSITION_LINK,
1262 TabStripModel::ADD_NONE);
1264 EXPECT_EQ(5, tabstrip.count());
1266 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1267 EXPECT_EQ(middle_click_contents1, tabstrip.GetWebContentsAt(1));
1268 EXPECT_EQ(middle_click_contents2, tabstrip.GetWebContentsAt(2));
1269 EXPECT_EQ(middle_click_contents3, tabstrip.GetWebContentsAt(3));
1270 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(4));
1272 // Now simulate selecting a tab in the middle of the group of tabs opened from
1273 // the home page and start closing them. Each WebContents in the group
1274 // should be closed, right to left. This test is constructed to start at the
1275 // middle WebContents in the group to make sure the cursor wraps around
1276 // to the first WebContents in the group before closing the opener or
1277 // any other WebContents.
1278 tabstrip.ActivateTabAt(2, true);
1279 tabstrip.CloseSelectedTabs();
1280 EXPECT_EQ(middle_click_contents3, tabstrip.GetActiveWebContents());
1281 tabstrip.CloseSelectedTabs();
1282 EXPECT_EQ(middle_click_contents1, tabstrip.GetActiveWebContents());
1283 tabstrip.CloseSelectedTabs();
1284 EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1285 tabstrip.CloseSelectedTabs();
1286 EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1288 EXPECT_EQ(1, tabstrip.count());
1290 tabstrip.CloseAllTabs();
1291 EXPECT_TRUE(tabstrip.empty());
1294 // Tests whether or not a WebContents created by a left click on a link
1295 // that opens a new tab is inserted correctly adjacent to the tab that spawned
1297 TEST_F(TabStripModelTest, AddWebContents_LeftClickPopup) {
1298 TabStripDummyDelegate delegate;
1299 TabStripModel tabstrip(&delegate, profile());
1300 EXPECT_TRUE(tabstrip.empty());
1302 // Open the Home Page.
1303 WebContents* homepage_contents = CreateWebContents();
1304 tabstrip.AddWebContents(
1305 homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1306 TabStripModel::ADD_ACTIVE);
1308 // Open some other tab, by user typing.
1309 WebContents* typed_page_contents = CreateWebContents();
1310 tabstrip.AddWebContents(
1311 typed_page_contents, -1, content::PAGE_TRANSITION_TYPED,
1312 TabStripModel::ADD_ACTIVE);
1314 EXPECT_EQ(2, tabstrip.count());
1316 // Re-select the home page.
1317 tabstrip.ActivateTabAt(0, true);
1319 // Open a tab by simulating a left click on a link that opens in a new tab.
1320 WebContents* left_click_contents = CreateWebContents();
1321 tabstrip.AddWebContents(left_click_contents, -1,
1322 content::PAGE_TRANSITION_LINK,
1323 TabStripModel::ADD_ACTIVE);
1325 // Verify the state meets our expectations.
1326 EXPECT_EQ(3, tabstrip.count());
1327 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1328 EXPECT_EQ(left_click_contents, tabstrip.GetWebContentsAt(1));
1329 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(2));
1331 // The newly created tab should be selected.
1332 EXPECT_EQ(left_click_contents, tabstrip.GetActiveWebContents());
1334 // After closing the selected tab, the selection should move to the left, to
1336 tabstrip.CloseSelectedTabs();
1337 EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1339 EXPECT_EQ(2, tabstrip.count());
1341 tabstrip.CloseAllTabs();
1342 EXPECT_TRUE(tabstrip.empty());
1345 // Tests whether or not new tabs that should split context (typed pages,
1346 // generated urls, also blank tabs) open at the end of the tabstrip instead of
1348 TEST_F(TabStripModelTest, AddWebContents_CreateNewBlankTab) {
1349 TabStripDummyDelegate delegate;
1350 TabStripModel tabstrip(&delegate, profile());
1351 EXPECT_TRUE(tabstrip.empty());
1353 // Open the Home Page.
1354 WebContents* homepage_contents = CreateWebContents();
1355 tabstrip.AddWebContents(
1356 homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1357 TabStripModel::ADD_ACTIVE);
1359 // Open some other tab, by user typing.
1360 WebContents* typed_page_contents = CreateWebContents();
1361 tabstrip.AddWebContents(
1362 typed_page_contents, -1, content::PAGE_TRANSITION_TYPED,
1363 TabStripModel::ADD_ACTIVE);
1365 EXPECT_EQ(2, tabstrip.count());
1367 // Re-select the home page.
1368 tabstrip.ActivateTabAt(0, true);
1370 // Open a new blank tab in the foreground.
1371 WebContents* new_blank_contents = CreateWebContents();
1372 tabstrip.AddWebContents(new_blank_contents, -1,
1373 content::PAGE_TRANSITION_TYPED,
1374 TabStripModel::ADD_ACTIVE);
1376 // Verify the state of the tabstrip.
1377 EXPECT_EQ(3, tabstrip.count());
1378 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1379 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(1));
1380 EXPECT_EQ(new_blank_contents, tabstrip.GetWebContentsAt(2));
1382 // Now open a couple more blank tabs in the background.
1383 WebContents* background_blank_contents1 = CreateWebContents();
1384 tabstrip.AddWebContents(
1385 background_blank_contents1, -1, content::PAGE_TRANSITION_TYPED,
1386 TabStripModel::ADD_NONE);
1387 WebContents* background_blank_contents2 = CreateWebContents();
1388 tabstrip.AddWebContents(
1389 background_blank_contents2, -1, content::PAGE_TRANSITION_GENERATED,
1390 TabStripModel::ADD_NONE);
1391 EXPECT_EQ(5, tabstrip.count());
1392 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1393 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(1));
1394 EXPECT_EQ(new_blank_contents, tabstrip.GetWebContentsAt(2));
1395 EXPECT_EQ(background_blank_contents1, tabstrip.GetWebContentsAt(3));
1396 EXPECT_EQ(background_blank_contents2, tabstrip.GetWebContentsAt(4));
1398 tabstrip.CloseAllTabs();
1399 EXPECT_TRUE(tabstrip.empty());
1402 // Tests whether opener state is correctly forgotten when the user switches
1404 TEST_F(TabStripModelTest, AddWebContents_ForgetOpeners) {
1405 TabStripDummyDelegate delegate;
1406 TabStripModel tabstrip(&delegate, profile());
1407 EXPECT_TRUE(tabstrip.empty());
1409 // Open the Home Page
1410 WebContents* homepage_contents = CreateWebContents();
1411 tabstrip.AddWebContents(
1412 homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1413 TabStripModel::ADD_ACTIVE);
1415 // Open some other tab, by user typing.
1416 WebContents* typed_page_contents = CreateWebContents();
1417 tabstrip.AddWebContents(
1418 typed_page_contents, -1, content::PAGE_TRANSITION_TYPED,
1419 TabStripModel::ADD_ACTIVE);
1421 EXPECT_EQ(2, tabstrip.count());
1423 // Re-select the home page.
1424 tabstrip.ActivateTabAt(0, true);
1426 // Open a bunch of tabs by simulating middle clicking on links on the home
1428 WebContents* middle_click_contents1 = CreateWebContents();
1429 tabstrip.AddWebContents(
1430 middle_click_contents1, -1, content::PAGE_TRANSITION_LINK,
1431 TabStripModel::ADD_NONE);
1432 WebContents* middle_click_contents2 = CreateWebContents();
1433 tabstrip.AddWebContents(
1434 middle_click_contents2, -1, content::PAGE_TRANSITION_LINK,
1435 TabStripModel::ADD_NONE);
1436 WebContents* middle_click_contents3 = CreateWebContents();
1437 tabstrip.AddWebContents(
1438 middle_click_contents3, -1, content::PAGE_TRANSITION_LINK,
1439 TabStripModel::ADD_NONE);
1441 // Break out of the context by selecting a tab in a different context.
1442 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(4));
1443 tabstrip.SelectLastTab();
1444 EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1446 // Step back into the context by selecting a tab inside it.
1447 tabstrip.ActivateTabAt(2, true);
1448 EXPECT_EQ(middle_click_contents2, tabstrip.GetActiveWebContents());
1450 // Now test that closing tabs selects to the right until there are no more,
1451 // then to the left, as if there were no context (context has been
1452 // successfully forgotten).
1453 tabstrip.CloseSelectedTabs();
1454 EXPECT_EQ(middle_click_contents3, tabstrip.GetActiveWebContents());
1455 tabstrip.CloseSelectedTabs();
1456 EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1457 tabstrip.CloseSelectedTabs();
1458 EXPECT_EQ(middle_click_contents1, tabstrip.GetActiveWebContents());
1459 tabstrip.CloseSelectedTabs();
1460 EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1462 EXPECT_EQ(1, tabstrip.count());
1464 tabstrip.CloseAllTabs();
1465 EXPECT_TRUE(tabstrip.empty());
1468 // Added for http://b/issue?id=958960
1469 TEST_F(TabStripModelTest, AppendContentsReselectionTest) {
1470 TabStripDummyDelegate delegate;
1471 TabStripModel tabstrip(&delegate, profile());
1472 EXPECT_TRUE(tabstrip.empty());
1474 // Open the Home Page.
1475 WebContents* homepage_contents = CreateWebContents();
1476 tabstrip.AddWebContents(
1477 homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1478 TabStripModel::ADD_ACTIVE);
1480 // Open some other tab, by user typing.
1481 WebContents* typed_page_contents = CreateWebContents();
1482 tabstrip.AddWebContents(
1483 typed_page_contents, -1, content::PAGE_TRANSITION_TYPED,
1484 TabStripModel::ADD_NONE);
1486 // The selected tab should still be the first.
1487 EXPECT_EQ(0, tabstrip.active_index());
1489 // Now simulate a link click that opens a new tab (by virtue of target=_blank)
1490 // and make sure the correct tab gets selected when the new tab is closed.
1491 WebContents* target_blank = CreateWebContents();
1492 tabstrip.AppendWebContents(target_blank, true);
1493 EXPECT_EQ(2, tabstrip.active_index());
1494 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
1495 EXPECT_EQ(0, tabstrip.active_index());
1497 // Clean up after ourselves.
1498 tabstrip.CloseAllTabs();
1501 // Added for http://b/issue?id=1027661
1502 TEST_F(TabStripModelTest, ReselectionConsidersChildrenTest) {
1503 TabStripDummyDelegate delegate;
1504 TabStripModel strip(&delegate, profile());
1507 WebContents* page_a_contents = CreateWebContents();
1508 strip.AddWebContents(
1509 page_a_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1510 TabStripModel::ADD_ACTIVE);
1512 // Simulate middle click to open page A.A and A.B
1513 WebContents* page_a_a_contents = CreateWebContents();
1514 strip.AddWebContents(page_a_a_contents, -1, content::PAGE_TRANSITION_LINK,
1515 TabStripModel::ADD_NONE);
1516 WebContents* page_a_b_contents = CreateWebContents();
1517 strip.AddWebContents(page_a_b_contents, -1, content::PAGE_TRANSITION_LINK,
1518 TabStripModel::ADD_NONE);
1521 strip.ActivateTabAt(1, true);
1522 EXPECT_EQ(page_a_a_contents, strip.GetActiveWebContents());
1524 // Simulate a middle click to open page A.A.A
1525 WebContents* page_a_a_a_contents = CreateWebContents();
1526 strip.AddWebContents(page_a_a_a_contents, -1, content::PAGE_TRANSITION_LINK,
1527 TabStripModel::ADD_NONE);
1529 EXPECT_EQ(page_a_a_a_contents, strip.GetWebContentsAt(2));
1532 strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1534 // Page A.A.A should be selected, NOT A.B
1535 EXPECT_EQ(page_a_a_a_contents, strip.GetActiveWebContents());
1538 strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1540 // Page A.B should be selected
1541 EXPECT_EQ(page_a_b_contents, strip.GetActiveWebContents());
1544 strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1546 // Page A should be selected
1547 EXPECT_EQ(page_a_contents, strip.GetActiveWebContents());
1550 strip.CloseAllTabs();
1553 TEST_F(TabStripModelTest, AddWebContents_NewTabAtEndOfStripInheritsGroup) {
1554 TabStripDummyDelegate delegate;
1555 TabStripModel strip(&delegate, profile());
1558 WebContents* page_a_contents = CreateWebContents();
1559 strip.AddWebContents(page_a_contents, -1,
1560 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1561 TabStripModel::ADD_ACTIVE);
1563 // Open pages B, C and D in the background from links on page A...
1564 WebContents* page_b_contents = CreateWebContents();
1565 WebContents* page_c_contents = CreateWebContents();
1566 WebContents* page_d_contents = CreateWebContents();
1567 strip.AddWebContents(page_b_contents, -1, content::PAGE_TRANSITION_LINK,
1568 TabStripModel::ADD_NONE);
1569 strip.AddWebContents(page_c_contents, -1, content::PAGE_TRANSITION_LINK,
1570 TabStripModel::ADD_NONE);
1571 strip.AddWebContents(page_d_contents, -1, content::PAGE_TRANSITION_LINK,
1572 TabStripModel::ADD_NONE);
1574 // Switch to page B's tab.
1575 strip.ActivateTabAt(1, true);
1577 // Open a New Tab at the end of the strip (simulate Ctrl+T)
1578 WebContents* new_contents = CreateWebContents();
1579 strip.AddWebContents(new_contents, -1, content::PAGE_TRANSITION_TYPED,
1580 TabStripModel::ADD_ACTIVE);
1582 EXPECT_EQ(4, strip.GetIndexOfWebContents(new_contents));
1583 EXPECT_EQ(4, strip.active_index());
1585 // Close the New Tab that was just opened. We should be returned to page B's
1587 strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1589 EXPECT_EQ(1, strip.active_index());
1591 // Open a non-New Tab tab at the end of the strip, with a TYPED transition.
1592 // This is like typing a URL in the address bar and pressing Alt+Enter. The
1593 // behavior should be the same as above.
1594 WebContents* page_e_contents = CreateWebContents();
1595 strip.AddWebContents(page_e_contents, -1, content::PAGE_TRANSITION_TYPED,
1596 TabStripModel::ADD_ACTIVE);
1598 EXPECT_EQ(4, strip.GetIndexOfWebContents(page_e_contents));
1599 EXPECT_EQ(4, strip.active_index());
1601 // Close the Tab. Selection should shift back to page B's Tab.
1602 strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1604 EXPECT_EQ(1, strip.active_index());
1606 // Open a non-New Tab tab at the end of the strip, with some other
1607 // transition. This is like right clicking on a bookmark and choosing "Open
1608 // in New Tab". No opener relationship should be preserved between this Tab
1609 // and the one that was active when the gesture was performed.
1610 WebContents* page_f_contents = CreateWebContents();
1611 strip.AddWebContents(page_f_contents, -1,
1612 content::PAGE_TRANSITION_AUTO_BOOKMARK,
1613 TabStripModel::ADD_ACTIVE);
1615 EXPECT_EQ(4, strip.GetIndexOfWebContents(page_f_contents));
1616 EXPECT_EQ(4, strip.active_index());
1618 // Close the Tab. The next-adjacent should be selected.
1619 strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1621 EXPECT_EQ(3, strip.active_index());
1624 strip.CloseAllTabs();
1627 // A test of navigations in a tab that is part of a group of opened from some
1628 // parent tab. If the navigations are link clicks, the group relationship of
1629 // the tab to its parent are preserved. If they are of any other type, they are
1631 TEST_F(TabStripModelTest, NavigationForgetsOpeners) {
1632 TabStripDummyDelegate delegate;
1633 TabStripModel strip(&delegate, profile());
1636 WebContents* page_a_contents = CreateWebContents();
1637 strip.AddWebContents(page_a_contents, -1,
1638 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1639 TabStripModel::ADD_ACTIVE);
1641 // Open pages B, C and D in the background from links on page A...
1642 WebContents* page_b_contents = CreateWebContents();
1643 WebContents* page_c_contents = CreateWebContents();
1644 WebContents* page_d_contents = CreateWebContents();
1645 strip.AddWebContents(page_b_contents, -1, content::PAGE_TRANSITION_LINK,
1646 TabStripModel::ADD_NONE);
1647 strip.AddWebContents(page_c_contents, -1, content::PAGE_TRANSITION_LINK,
1648 TabStripModel::ADD_NONE);
1649 strip.AddWebContents(page_d_contents, -1, content::PAGE_TRANSITION_LINK,
1650 TabStripModel::ADD_NONE);
1652 // Open page E in a different opener group from page A.
1653 WebContents* page_e_contents = CreateWebContents();
1654 strip.AddWebContents(page_e_contents, -1,
1655 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1656 TabStripModel::ADD_NONE);
1658 // Tell the TabStripModel that we are navigating page D via a link click.
1659 strip.ActivateTabAt(3, true);
1660 strip.TabNavigating(page_d_contents, content::PAGE_TRANSITION_LINK);
1662 // Close page D, page C should be selected. (part of same group).
1663 strip.CloseWebContentsAt(3, TabStripModel::CLOSE_NONE);
1664 EXPECT_EQ(2, strip.active_index());
1666 // Tell the TabStripModel that we are navigating in page C via a bookmark.
1667 strip.TabNavigating(page_c_contents, content::PAGE_TRANSITION_AUTO_BOOKMARK);
1669 // Close page C, page E should be selected. (C is no longer part of the
1670 // A-B-C-D group, selection moves to the right).
1671 strip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
1672 EXPECT_EQ(page_e_contents, strip.GetWebContentsAt(strip.active_index()));
1674 strip.CloseAllTabs();
1677 // A test that the forgetting behavior tested in NavigationForgetsOpeners above
1678 // doesn't cause the opener relationship for a New Tab opened at the end of the
1679 // TabStrip to be reset (Test 1 below), unless another any other tab is
1680 // selected (Test 2 below).
1681 TEST_F(TabStripModelTest, NavigationForgettingDoesntAffectNewTab) {
1682 TabStripDummyDelegate delegate;
1683 TabStripModel strip(&delegate, profile());
1685 // Open a tab and several tabs from it, then select one of the tabs that was
1687 WebContents* page_a_contents = CreateWebContents();
1688 strip.AddWebContents(page_a_contents, -1,
1689 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1690 TabStripModel::ADD_ACTIVE);
1692 WebContents* page_b_contents = CreateWebContents();
1693 WebContents* page_c_contents = CreateWebContents();
1694 WebContents* page_d_contents = CreateWebContents();
1695 strip.AddWebContents(page_b_contents, -1, content::PAGE_TRANSITION_LINK,
1696 TabStripModel::ADD_NONE);
1697 strip.AddWebContents(page_c_contents, -1, content::PAGE_TRANSITION_LINK,
1698 TabStripModel::ADD_NONE);
1699 strip.AddWebContents(page_d_contents, -1, content::PAGE_TRANSITION_LINK,
1700 TabStripModel::ADD_NONE);
1702 strip.ActivateTabAt(2, true);
1704 // TEST 1: If the user is in a group of tabs and opens a new tab at the end
1705 // of the strip, closing that new tab will select the tab that they were
1708 // Now simulate opening a new tab at the end of the TabStrip.
1709 WebContents* new_contents1 = CreateWebContents();
1710 strip.AddWebContents(new_contents1, -1, content::PAGE_TRANSITION_TYPED,
1711 TabStripModel::ADD_ACTIVE);
1713 // At this point, if we close this tab the last selected one should be
1715 strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1716 EXPECT_EQ(page_c_contents, strip.GetWebContentsAt(strip.active_index()));
1718 // TEST 2: If the user is in a group of tabs and opens a new tab at the end
1719 // of the strip, selecting any other tab in the strip will cause that new
1720 // tab's opener relationship to be forgotten.
1722 // Open a new tab again.
1723 WebContents* new_contents2 = CreateWebContents();
1724 strip.AddWebContents(new_contents2, -1, content::PAGE_TRANSITION_TYPED,
1725 TabStripModel::ADD_ACTIVE);
1727 // Now select the first tab.
1728 strip.ActivateTabAt(0, true);
1730 // Now select the last tab.
1731 strip.ActivateTabAt(strip.count() - 1, true);
1733 // Now close the last tab. The next adjacent should be selected.
1734 strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1735 EXPECT_EQ(page_d_contents, strip.GetWebContentsAt(strip.active_index()));
1737 strip.CloseAllTabs();
1740 // This fails on Linux when run with the rest of unit_tests (crbug.com/302156)
1741 // and fails consistently on Mac and Windows.
1742 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
1743 #define MAYBE_FastShutdown \
1744 DISABLED_FastShutdown
1746 #define MAYBE_FastShutdown \
1749 // Tests that fast shutdown is attempted appropriately.
1750 TEST_F(TabStripModelTest, MAYBE_FastShutdown) {
1751 TabStripDummyDelegate delegate;
1752 TabStripModel tabstrip(&delegate, profile());
1753 MockTabStripModelObserver observer(&tabstrip);
1754 tabstrip.AddObserver(&observer);
1756 EXPECT_TRUE(tabstrip.empty());
1758 // Make sure fast shutdown is attempted when tabs that share a RPH are shut
1761 WebContents* contents1 = CreateWebContents();
1762 WebContents* contents2 = CreateWebContentsWithSharedRPH(contents1);
1764 SetID(contents1, 1);
1765 SetID(contents2, 2);
1767 tabstrip.AppendWebContents(contents1, true);
1768 tabstrip.AppendWebContents(contents2, true);
1770 // Turn on the fake unload listener so the tabs don't actually get shut
1771 // down when we call CloseAllTabs()---we need to be able to check that
1772 // fast shutdown was attempted.
1773 delegate.set_run_unload_listener(true);
1774 tabstrip.CloseAllTabs();
1775 // On a mock RPH this checks whether we *attempted* fast shutdown.
1776 // A real RPH would reject our attempt since there is an unload handler.
1777 EXPECT_TRUE(contents1->GetRenderProcessHost()->FastShutdownStarted());
1778 EXPECT_EQ(2, tabstrip.count());
1780 delegate.set_run_unload_listener(false);
1781 tabstrip.CloseAllTabs();
1782 EXPECT_TRUE(tabstrip.empty());
1785 // Make sure fast shutdown is not attempted when only some tabs that share a
1786 // RPH are shut down.
1788 WebContents* contents1 = CreateWebContents();
1789 WebContents* contents2 = CreateWebContentsWithSharedRPH(contents1);
1791 SetID(contents1, 1);
1792 SetID(contents2, 2);
1794 tabstrip.AppendWebContents(contents1, true);
1795 tabstrip.AppendWebContents(contents2, true);
1797 tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
1798 EXPECT_FALSE(contents1->GetRenderProcessHost()->FastShutdownStarted());
1799 EXPECT_EQ(1, tabstrip.count());
1801 tabstrip.CloseAllTabs();
1802 EXPECT_TRUE(tabstrip.empty());
1806 // Tests various permutations of apps.
1807 TEST_F(TabStripModelTest, Apps) {
1808 TabStripDummyDelegate delegate;
1809 TabStripModel tabstrip(&delegate, profile());
1810 MockTabStripModelObserver observer(&tabstrip);
1811 tabstrip.AddObserver(&observer);
1813 EXPECT_TRUE(tabstrip.empty());
1815 typedef MockTabStripModelObserver::State State;
1818 base::FilePath path(FILE_PATH_LITERAL("c:\\foo"));
1819 #elif defined(OS_POSIX)
1820 base::FilePath path(FILE_PATH_LITERAL("/foo"));
1823 base::DictionaryValue manifest;
1824 manifest.SetString("name", "hi!");
1825 manifest.SetString("version", "1");
1826 manifest.SetString("app.launch.web_url", "http://www.google.com");
1828 scoped_refptr<Extension> extension_app(
1829 Extension::Create(path, extensions::Manifest::INVALID_LOCATION,
1830 manifest, Extension::NO_FLAGS, &error));
1831 WebContents* contents1 = CreateWebContents();
1832 extensions::TabHelper::CreateForWebContents(contents1);
1833 extensions::TabHelper::FromWebContents(contents1)
1834 ->SetExtensionApp(extension_app.get());
1835 WebContents* contents2 = CreateWebContents();
1836 extensions::TabHelper::CreateForWebContents(contents2);
1837 extensions::TabHelper::FromWebContents(contents2)
1838 ->SetExtensionApp(extension_app.get());
1839 WebContents* contents3 = CreateWebContents();
1841 SetID(contents1, 1);
1842 SetID(contents2, 2);
1843 SetID(contents3, 3);
1845 // Note! The ordering of these tests is important, each subsequent test
1846 // builds on the state established in the previous. This is important if you
1847 // ever insert tests rather than append.
1849 // Initial state, tab3 only and selected.
1850 tabstrip.AppendWebContents(contents3, true);
1852 observer.ClearStates();
1854 // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal
1855 // position and tab1 should end up at position 0.
1857 tabstrip.InsertWebContentsAt(1, contents1, TabStripModel::ADD_NONE);
1859 ASSERT_EQ(1, observer.GetStateCount());
1860 State state(contents1, 0, MockTabStripModelObserver::INSERT);
1861 EXPECT_TRUE(observer.StateEquals(0, state));
1863 // And verify the state.
1864 EXPECT_EQ("1ap 3", GetTabStripStateString(tabstrip));
1866 observer.ClearStates();
1869 // Insert tab 2 at position 1.
1871 tabstrip.InsertWebContentsAt(1, contents2, TabStripModel::ADD_NONE);
1873 ASSERT_EQ(1, observer.GetStateCount());
1874 State state(contents2, 1, MockTabStripModelObserver::INSERT);
1875 EXPECT_TRUE(observer.StateEquals(0, state));
1877 // And verify the state.
1878 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1880 observer.ClearStates();
1883 // Try to move tab 3 to position 0. This isn't legal and should be ignored.
1885 tabstrip.MoveWebContentsAt(2, 0, false);
1887 ASSERT_EQ(0, observer.GetStateCount());
1889 // And verify the state didn't change.
1890 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1892 observer.ClearStates();
1895 // Try to move tab 0 to position 3. This isn't legal and should be ignored.
1897 tabstrip.MoveWebContentsAt(0, 2, false);
1899 ASSERT_EQ(0, observer.GetStateCount());
1901 // And verify the state didn't change.
1902 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1904 observer.ClearStates();
1907 // Try to move tab 0 to position 1. This is a legal move.
1909 tabstrip.MoveWebContentsAt(0, 1, false);
1911 ASSERT_EQ(1, observer.GetStateCount());
1912 State state(contents1, 1, MockTabStripModelObserver::MOVE);
1913 state.src_index = 0;
1914 EXPECT_TRUE(observer.StateEquals(0, state));
1916 // And verify the state didn't change.
1917 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip));
1919 observer.ClearStates();
1922 // Remove tab3 and insert at position 0. It should be forced to position 2.
1924 tabstrip.DetachWebContentsAt(2);
1925 observer.ClearStates();
1927 tabstrip.InsertWebContentsAt(0, contents3, TabStripModel::ADD_NONE);
1929 ASSERT_EQ(1, observer.GetStateCount());
1930 State state(contents3, 2, MockTabStripModelObserver::INSERT);
1931 EXPECT_TRUE(observer.StateEquals(0, state));
1933 // And verify the state didn't change.
1934 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip));
1936 observer.ClearStates();
1939 tabstrip.CloseAllTabs();
1942 // Tests various permutations of pinning tabs.
1943 TEST_F(TabStripModelTest, Pinning) {
1944 TabStripDummyDelegate delegate;
1945 TabStripModel tabstrip(&delegate, profile());
1946 MockTabStripModelObserver observer(&tabstrip);
1947 tabstrip.AddObserver(&observer);
1949 EXPECT_TRUE(tabstrip.empty());
1951 typedef MockTabStripModelObserver::State State;
1953 WebContents* contents1 = CreateWebContents();
1954 WebContents* contents2 = CreateWebContents();
1955 WebContents* contents3 = CreateWebContents();
1957 SetID(contents1, 1);
1958 SetID(contents2, 2);
1959 SetID(contents3, 3);
1961 // Note! The ordering of these tests is important, each subsequent test
1962 // builds on the state established in the previous. This is important if you
1963 // ever insert tests rather than append.
1965 // Initial state, three tabs, first selected.
1966 tabstrip.AppendWebContents(contents1, true);
1967 tabstrip.AppendWebContents(contents2, false);
1968 tabstrip.AppendWebContents(contents3, false);
1970 observer.ClearStates();
1972 // Pin the first tab, this shouldn't visually reorder anything.
1974 tabstrip.SetTabPinned(0, true);
1976 // As the order didn't change, we should get a pinned notification.
1977 ASSERT_EQ(1, observer.GetStateCount());
1978 State state(contents1, 0, MockTabStripModelObserver::PINNED);
1979 EXPECT_TRUE(observer.StateEquals(0, state));
1981 // And verify the state.
1982 EXPECT_EQ("1p 2 3", GetTabStripStateString(tabstrip));
1984 observer.ClearStates();
1987 // Unpin the first tab.
1989 tabstrip.SetTabPinned(0, false);
1991 // As the order didn't change, we should get a pinned notification.
1992 ASSERT_EQ(1, observer.GetStateCount());
1993 State state(contents1, 0, MockTabStripModelObserver::PINNED);
1994 EXPECT_TRUE(observer.StateEquals(0, state));
1996 // And verify the state.
1997 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
1999 observer.ClearStates();
2002 // Pin the 3rd tab, which should move it to the front.
2004 tabstrip.SetTabPinned(2, true);
2006 // The pinning should have resulted in a move and a pinned notification.
2007 ASSERT_EQ(2, observer.GetStateCount());
2008 State state(contents3, 0, MockTabStripModelObserver::MOVE);
2009 state.src_index = 2;
2010 EXPECT_TRUE(observer.StateEquals(0, state));
2012 state = State(contents3, 0, MockTabStripModelObserver::PINNED);
2013 EXPECT_TRUE(observer.StateEquals(1, state));
2015 // And verify the state.
2016 EXPECT_EQ("3p 1 2", GetTabStripStateString(tabstrip));
2018 observer.ClearStates();
2021 // Pin the tab "1", which shouldn't move anything.
2023 tabstrip.SetTabPinned(1, true);
2025 // As the order didn't change, we should get a pinned notification.
2026 ASSERT_EQ(1, observer.GetStateCount());
2027 State state(contents1, 1, MockTabStripModelObserver::PINNED);
2028 EXPECT_TRUE(observer.StateEquals(0, state));
2030 // And verify the state.
2031 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip));
2033 observer.ClearStates();
2036 // Try to move tab "2" to the front, it should be ignored.
2038 tabstrip.MoveWebContentsAt(2, 0, false);
2040 // As the order didn't change, we should get a pinned notification.
2041 ASSERT_EQ(0, observer.GetStateCount());
2043 // And verify the state.
2044 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip));
2046 observer.ClearStates();
2049 // Unpin tab "3", which implicitly moves it to the end.
2051 tabstrip.SetTabPinned(0, false);
2053 ASSERT_EQ(2, observer.GetStateCount());
2054 State state(contents3, 1, MockTabStripModelObserver::MOVE);
2055 state.src_index = 0;
2056 EXPECT_TRUE(observer.StateEquals(0, state));
2058 state = State(contents3, 1, MockTabStripModelObserver::PINNED);
2059 EXPECT_TRUE(observer.StateEquals(1, state));
2061 // And verify the state.
2062 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip));
2064 observer.ClearStates();
2067 // Unpin tab "3", nothing should happen.
2069 tabstrip.SetTabPinned(1, false);
2071 ASSERT_EQ(0, observer.GetStateCount());
2073 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip));
2075 observer.ClearStates();
2080 tabstrip.SetTabPinned(0, true);
2081 tabstrip.SetTabPinned(1, true);
2083 EXPECT_EQ("1p 3p 2", GetTabStripStateString(tabstrip));
2085 observer.ClearStates();
2088 WebContents* contents4 = CreateWebContents();
2089 SetID(contents4, 4);
2091 // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end
2094 tabstrip.InsertWebContentsAt(1, contents4, TabStripModel::ADD_NONE);
2096 ASSERT_EQ(1, observer.GetStateCount());
2097 State state(contents4, 2, MockTabStripModelObserver::INSERT);
2098 EXPECT_TRUE(observer.StateEquals(0, state));
2100 EXPECT_EQ("1p 3p 4 2", GetTabStripStateString(tabstrip));
2103 tabstrip.CloseAllTabs();
2106 // Makes sure the TabStripModel calls the right observer methods during a
2108 TEST_F(TabStripModelTest, ReplaceSendsSelected) {
2109 typedef MockTabStripModelObserver::State State;
2111 TabStripDummyDelegate delegate;
2112 TabStripModel strip(&delegate, profile());
2114 WebContents* first_contents = CreateWebContents();
2115 strip.AddWebContents(first_contents, -1, content::PAGE_TRANSITION_TYPED,
2116 TabStripModel::ADD_ACTIVE);
2118 MockTabStripModelObserver tabstrip_observer(&strip);
2119 strip.AddObserver(&tabstrip_observer);
2121 WebContents* new_contents = CreateWebContents();
2122 delete strip.ReplaceWebContentsAt(0, new_contents);
2124 ASSERT_EQ(2, tabstrip_observer.GetStateCount());
2126 // First event should be for replaced.
2127 State state(new_contents, 0, MockTabStripModelObserver::REPLACED);
2128 state.src_contents = first_contents;
2129 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
2131 // And the second for selected.
2132 state = State(new_contents, 0, MockTabStripModelObserver::ACTIVATE);
2133 state.src_contents = first_contents;
2134 state.change_reason = TabStripModelObserver::CHANGE_REASON_REPLACED;
2135 EXPECT_TRUE(tabstrip_observer.StateEquals(1, state));
2137 // Now add another tab and replace it, making sure we don't get a selected
2139 WebContents* third_contents = CreateWebContents();
2140 strip.AddWebContents(third_contents, 1, content::PAGE_TRANSITION_TYPED,
2141 TabStripModel::ADD_NONE);
2143 tabstrip_observer.ClearStates();
2146 new_contents = CreateWebContents();
2147 delete strip.ReplaceWebContentsAt(1, new_contents);
2149 ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2151 state = State(new_contents, 1, MockTabStripModelObserver::REPLACED);
2152 state.src_contents = third_contents;
2153 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
2155 strip.CloseAllTabs();
2158 // Ensures discarding tabs leaves TabStripModel in a good state.
2159 TEST_F(TabStripModelTest, DiscardWebContentsAt) {
2160 typedef MockTabStripModelObserver::State State;
2162 TabStripDummyDelegate delegate;
2163 TabStripModel tabstrip(&delegate, profile());
2165 // Fill it with some tabs.
2166 WebContents* contents1 = CreateWebContents();
2167 tabstrip.AppendWebContents(contents1, true);
2168 WebContents* contents2 = CreateWebContents();
2169 tabstrip.AppendWebContents(contents2, true);
2171 // Start watching for events after the appends to avoid observing state
2172 // transitions that aren't relevant to this test.
2173 MockTabStripModelObserver tabstrip_observer(&tabstrip);
2174 tabstrip.AddObserver(&tabstrip_observer);
2176 // Discard one of the tabs.
2177 WebContents* null_contents1 = tabstrip.DiscardWebContentsAt(0);
2178 ASSERT_EQ(2, tabstrip.count());
2179 EXPECT_TRUE(tabstrip.IsTabDiscarded(0));
2180 EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2181 ASSERT_EQ(null_contents1, tabstrip.GetWebContentsAt(0));
2182 ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1));
2183 ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2184 State state1(null_contents1, 0, MockTabStripModelObserver::REPLACED);
2185 state1.src_contents = contents1;
2186 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state1));
2187 tabstrip_observer.ClearStates();
2189 // Discard the same tab again.
2190 WebContents* null_contents2 = tabstrip.DiscardWebContentsAt(0);
2191 ASSERT_EQ(2, tabstrip.count());
2192 EXPECT_TRUE(tabstrip.IsTabDiscarded(0));
2193 EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2194 ASSERT_EQ(null_contents2, tabstrip.GetWebContentsAt(0));
2195 ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1));
2196 ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2197 State state2(null_contents2, 0, MockTabStripModelObserver::REPLACED);
2198 state2.src_contents = null_contents1;
2199 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state2));
2200 tabstrip_observer.ClearStates();
2202 // Activating the tab should clear its discard state.
2203 tabstrip.ActivateTabAt(0, true /* user_gesture */);
2204 ASSERT_EQ(2, tabstrip.count());
2205 EXPECT_FALSE(tabstrip.IsTabDiscarded(0));
2206 EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2208 // Don't discard active tab.
2209 tabstrip.DiscardWebContentsAt(0);
2210 ASSERT_EQ(2, tabstrip.count());
2211 EXPECT_FALSE(tabstrip.IsTabDiscarded(0));
2212 EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2214 tabstrip.CloseAllTabs();
2217 // Makes sure TabStripModel handles the case of deleting a tab while removing
2219 TEST_F(TabStripModelTest, DeleteFromDestroy) {
2220 TabStripDummyDelegate delegate;
2221 TabStripModel strip(&delegate, profile());
2222 WebContents* contents1 = CreateWebContents();
2223 WebContents* contents2 = CreateWebContents();
2224 MockTabStripModelObserver tab_strip_model_observer(&strip);
2225 strip.AppendWebContents(contents1, true);
2226 strip.AppendWebContents(contents2, true);
2227 // DeleteWebContentsOnDestroyedObserver deletes contents1 when contents2 sends
2228 // out notification that it is being destroyed.
2229 DeleteWebContentsOnDestroyedObserver observer(contents2, contents1, NULL);
2230 strip.AddObserver(&tab_strip_model_observer);
2231 strip.CloseAllTabs();
2233 int close_all_count = 0, close_all_canceled_count = 0;
2234 tab_strip_model_observer.GetCloseCounts(&close_all_count,
2235 &close_all_canceled_count);
2236 EXPECT_EQ(1, close_all_count);
2237 EXPECT_EQ(0, close_all_canceled_count);
2239 strip.RemoveObserver(&tab_strip_model_observer);
2242 // Makes sure TabStripModel handles the case of deleting another tab and the
2243 // TabStrip while removing another tab.
2244 TEST_F(TabStripModelTest, DeleteTabStripFromDestroy) {
2245 TabStripDummyDelegate delegate;
2246 TabStripModel* strip = new TabStripModel(&delegate, profile());
2247 MockTabStripModelObserver tab_strip_model_observer(strip);
2248 strip->AddObserver(&tab_strip_model_observer);
2249 WebContents* contents1 = CreateWebContents();
2250 WebContents* contents2 = CreateWebContents();
2251 strip->AppendWebContents(contents1, true);
2252 strip->AppendWebContents(contents2, true);
2253 // DeleteWebContentsOnDestroyedObserver deletes |contents1| and |strip| when
2254 // |contents2| sends out notification that it is being destroyed.
2255 DeleteWebContentsOnDestroyedObserver observer(contents2, contents1, strip);
2256 strip->CloseAllTabs();
2257 EXPECT_TRUE(tab_strip_model_observer.empty());
2258 EXPECT_TRUE(tab_strip_model_observer.deleted());
2261 TEST_F(TabStripModelTest, MoveSelectedTabsTo) {
2263 // Number of tabs the tab strip should have.
2264 const int tab_count;
2266 // Number of pinned tabs.
2267 const int pinned_count;
2269 // Index of the tabs to select.
2270 const std::string selected_tabs;
2272 // Index to move the tabs to.
2273 const int target_index;
2275 // Expected state after the move (space separated list of indices).
2276 const std::string state_after_move;
2279 { 2, 0, "0", 1, "1 0" },
2280 { 3, 0, "0", 2, "1 2 0" },
2281 { 3, 0, "2", 0, "2 0 1" },
2282 { 3, 0, "2", 1, "0 2 1" },
2283 { 3, 0, "0 1", 0, "0 1 2" },
2286 { 6, 0, "4 5", 1, "0 4 5 1 2 3" },
2287 { 3, 0, "0 1", 1, "2 0 1" },
2288 { 4, 0, "0 2", 1, "1 0 2 3" },
2289 { 6, 0, "0 1", 3, "2 3 4 0 1 5" },
2292 { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" },
2293 { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" },
2294 { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" },
2297 { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" },
2300 { 16, 0, "0 1 2 3 4 7 9", 8, "5 6 8 10 11 12 13 14 0 1 2 3 4 7 9 15" },
2302 // With pinned tabs.
2303 { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" },
2304 { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" },
2305 { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" },
2306 { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" },
2308 { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" },
2311 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
2312 TabStripDummyDelegate delegate;
2313 TabStripModel strip(&delegate, profile());
2314 ASSERT_NO_FATAL_FAILURE(
2315 PrepareTabstripForSelectionTest(&strip, test_data[i].tab_count,
2316 test_data[i].pinned_count,
2317 test_data[i].selected_tabs));
2318 strip.MoveSelectedTabsTo(test_data[i].target_index);
2319 EXPECT_EQ(test_data[i].state_after_move,
2320 GetTabStripStateString(strip)) << i;
2321 strip.CloseAllTabs();
2325 // Tests that moving a tab forgets all groups referencing it.
2326 TEST_F(TabStripModelTest, MoveSelectedTabsTo_ForgetGroups) {
2327 TabStripDummyDelegate delegate;
2328 TabStripModel strip(&delegate, profile());
2330 // Open page A as a new tab and then A1 in the background from A.
2331 WebContents* page_a_contents = CreateWebContents();
2332 strip.AddWebContents(page_a_contents, -1,
2333 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
2334 TabStripModel::ADD_ACTIVE);
2335 WebContents* page_a1_contents = CreateWebContents();
2336 strip.AddWebContents(page_a1_contents, -1, content::PAGE_TRANSITION_LINK,
2337 TabStripModel::ADD_NONE);
2339 // Likewise, open pages B and B1.
2340 WebContents* page_b_contents = CreateWebContents();
2341 strip.AddWebContents(page_b_contents, -1,
2342 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
2343 TabStripModel::ADD_ACTIVE);
2344 WebContents* page_b1_contents = CreateWebContents();
2345 strip.AddWebContents(page_b1_contents, -1, content::PAGE_TRANSITION_LINK,
2346 TabStripModel::ADD_NONE);
2348 EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(0));
2349 EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(1));
2350 EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(2));
2351 EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(3));
2353 // Move page B to the start of the tab strip.
2354 strip.MoveSelectedTabsTo(0);
2356 // Open page B2 in the background from B. It should end up after B.
2357 WebContents* page_b2_contents = CreateWebContents();
2358 strip.AddWebContents(page_b2_contents, -1, content::PAGE_TRANSITION_LINK,
2359 TabStripModel::ADD_NONE);
2360 EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(0));
2361 EXPECT_EQ(page_b2_contents, strip.GetWebContentsAt(1));
2362 EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(2));
2363 EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(3));
2364 EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(4));
2367 strip.ActivateTabAt(2, true);
2368 EXPECT_EQ(page_a_contents, strip.GetActiveWebContents());
2370 // Open page A2 in the background from A. It should end up after A1.
2371 WebContents* page_a2_contents = CreateWebContents();
2372 strip.AddWebContents(page_a2_contents, -1, content::PAGE_TRANSITION_LINK,
2373 TabStripModel::ADD_NONE);
2374 EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(0));
2375 EXPECT_EQ(page_b2_contents, strip.GetWebContentsAt(1));
2376 EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(2));
2377 EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(3));
2378 EXPECT_EQ(page_a2_contents, strip.GetWebContentsAt(4));
2379 EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(5));
2381 strip.CloseAllTabs();
2384 TEST_F(TabStripModelTest, CloseSelectedTabs) {
2385 TabStripDummyDelegate delegate;
2386 TabStripModel strip(&delegate, profile());
2387 WebContents* contents1 = CreateWebContents();
2388 WebContents* contents2 = CreateWebContents();
2389 WebContents* contents3 = CreateWebContents();
2390 strip.AppendWebContents(contents1, true);
2391 strip.AppendWebContents(contents2, true);
2392 strip.AppendWebContents(contents3, true);
2393 strip.ToggleSelectionAt(1);
2394 strip.CloseSelectedTabs();
2395 EXPECT_EQ(1, strip.count());
2396 EXPECT_EQ(0, strip.active_index());
2397 strip.CloseAllTabs();
2400 TEST_F(TabStripModelTest, MultipleSelection) {
2401 typedef MockTabStripModelObserver::State State;
2403 TabStripDummyDelegate delegate;
2404 TabStripModel strip(&delegate, profile());
2405 MockTabStripModelObserver observer(&strip);
2406 WebContents* contents0 = CreateWebContents();
2407 WebContents* contents1 = CreateWebContents();
2408 WebContents* contents2 = CreateWebContents();
2409 WebContents* contents3 = CreateWebContents();
2410 strip.AppendWebContents(contents0, false);
2411 strip.AppendWebContents(contents1, false);
2412 strip.AppendWebContents(contents2, false);
2413 strip.AppendWebContents(contents3, false);
2414 strip.AddObserver(&observer);
2416 // Selection and active tab change.
2417 strip.ActivateTabAt(3, true);
2418 ASSERT_EQ(2, observer.GetStateCount());
2419 ASSERT_EQ(observer.GetStateAt(0).action,
2420 MockTabStripModelObserver::ACTIVATE);
2421 State s1(contents3, 3, MockTabStripModelObserver::SELECT);
2422 EXPECT_TRUE(observer.StateEquals(1, s1));
2423 observer.ClearStates();
2425 // Adding all tabs to selection, active tab is now at 0.
2426 strip.ExtendSelectionTo(0);
2427 ASSERT_EQ(3, observer.GetStateCount());
2428 ASSERT_EQ(observer.GetStateAt(0).action,
2429 MockTabStripModelObserver::DEACTIVATE);
2430 ASSERT_EQ(observer.GetStateAt(1).action,
2431 MockTabStripModelObserver::ACTIVATE);
2432 State s2(contents0, 0, MockTabStripModelObserver::SELECT);
2433 s2.src_contents = contents3;
2435 EXPECT_TRUE(observer.StateEquals(2, s2));
2436 observer.ClearStates();
2438 // Toggle the active tab, should make the next index active.
2439 strip.ToggleSelectionAt(0);
2440 EXPECT_EQ(1, strip.active_index());
2441 EXPECT_EQ(3U, strip.selection_model().size());
2442 EXPECT_EQ(4, strip.count());
2443 ASSERT_EQ(3, observer.GetStateCount());
2444 ASSERT_EQ(observer.GetStateAt(0).action,
2445 MockTabStripModelObserver::DEACTIVATE);
2446 ASSERT_EQ(observer.GetStateAt(1).action,
2447 MockTabStripModelObserver::ACTIVATE);
2448 ASSERT_EQ(observer.GetStateAt(2).action,
2449 MockTabStripModelObserver::SELECT);
2450 observer.ClearStates();
2452 // Toggle the first tab back to selected and active.
2453 strip.ToggleSelectionAt(0);
2454 EXPECT_EQ(0, strip.active_index());
2455 EXPECT_EQ(4U, strip.selection_model().size());
2456 EXPECT_EQ(4, strip.count());
2457 ASSERT_EQ(3, observer.GetStateCount());
2458 ASSERT_EQ(observer.GetStateAt(0).action,
2459 MockTabStripModelObserver::DEACTIVATE);
2460 ASSERT_EQ(observer.GetStateAt(1).action,
2461 MockTabStripModelObserver::ACTIVATE);
2462 ASSERT_EQ(observer.GetStateAt(2).action,
2463 MockTabStripModelObserver::SELECT);
2464 observer.ClearStates();
2466 // Closing one of the selected tabs, not the active one.
2467 strip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
2468 EXPECT_EQ(3, strip.count());
2469 ASSERT_EQ(3, observer.GetStateCount());
2470 ASSERT_EQ(observer.GetStateAt(0).action,
2471 MockTabStripModelObserver::CLOSE);
2472 ASSERT_EQ(observer.GetStateAt(1).action,
2473 MockTabStripModelObserver::DETACH);
2474 ASSERT_EQ(observer.GetStateAt(2).action,
2475 MockTabStripModelObserver::SELECT);
2476 observer.ClearStates();
2478 // Closing the active tab, while there are others tabs selected.
2479 strip.CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
2480 EXPECT_EQ(2, strip.count());
2481 ASSERT_EQ(5, observer.GetStateCount());
2482 ASSERT_EQ(observer.GetStateAt(0).action,
2483 MockTabStripModelObserver::CLOSE);
2484 ASSERT_EQ(observer.GetStateAt(1).action,
2485 MockTabStripModelObserver::DETACH);
2486 ASSERT_EQ(observer.GetStateAt(2).action,
2487 MockTabStripModelObserver::DEACTIVATE);
2488 ASSERT_EQ(observer.GetStateAt(3).action,
2489 MockTabStripModelObserver::ACTIVATE);
2490 ASSERT_EQ(observer.GetStateAt(4).action,
2491 MockTabStripModelObserver::SELECT);
2492 observer.ClearStates();
2494 // Active tab is at 0, deselecting all but the active tab.
2495 strip.ToggleSelectionAt(1);
2496 ASSERT_EQ(1, observer.GetStateCount());
2497 ASSERT_EQ(observer.GetStateAt(0).action,
2498 MockTabStripModelObserver::SELECT);
2499 observer.ClearStates();
2501 // Attempting to deselect the only selected and therefore active tab,
2502 // it is ignored (no notifications being sent) and tab at 0 remains selected
2504 strip.ToggleSelectionAt(0);
2505 ASSERT_EQ(0, observer.GetStateCount());
2507 strip.RemoveObserver(&observer);
2508 strip.CloseAllTabs();
2511 // Verifies that if we change the selection from a multi selection to a single
2512 // selection, but not in a way that changes the selected_index that
2513 // TabSelectionChanged is invoked.
2514 TEST_F(TabStripModelTest, MultipleToSingle) {
2515 typedef MockTabStripModelObserver::State State;
2517 TabStripDummyDelegate delegate;
2518 TabStripModel strip(&delegate, profile());
2519 WebContents* contents1 = CreateWebContents();
2520 WebContents* contents2 = CreateWebContents();
2521 strip.AppendWebContents(contents1, false);
2522 strip.AppendWebContents(contents2, false);
2523 strip.ToggleSelectionAt(0);
2524 strip.ToggleSelectionAt(1);
2526 MockTabStripModelObserver observer(&strip);
2527 strip.AddObserver(&observer);
2528 // This changes the selection (0 is no longer selected) but the selected_index
2529 // still remains at 1.
2530 strip.ActivateTabAt(1, true);
2531 ASSERT_EQ(1, observer.GetStateCount());
2532 State s(contents2, 1, MockTabStripModelObserver::SELECT);
2533 s.src_contents = contents2;
2535 s.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
2536 EXPECT_TRUE(observer.StateEquals(0, s));
2537 strip.RemoveObserver(&observer);
2538 strip.CloseAllTabs();
2541 // Verifies a newly inserted tab retains its previous blocked state.
2542 // http://crbug.com/276334
2543 TEST_F(TabStripModelTest, TabBlockedState) {
2544 // Start with a source tab strip.
2545 TabStripDummyDelegate dummy_tab_strip_delegate;
2546 TabStripModel strip_src(&dummy_tab_strip_delegate, profile());
2547 TabBlockedStateTestBrowser browser_src(&strip_src);
2550 WebContents* contents1 = CreateWebContents();
2551 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1);
2552 strip_src.AppendWebContents(contents1, false);
2555 WebContents* contents2 = CreateWebContents();
2556 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents2);
2557 strip_src.AppendWebContents(contents2, false);
2559 // Create a destination tab strip.
2560 TabStripModel strip_dst(&dummy_tab_strip_delegate, profile());
2561 TabBlockedStateTestBrowser browser_dst(&strip_dst);
2563 // Setup a SingleWebContentsDialogManager for tab |contents2|.
2564 web_modal::WebContentsModalDialogManager* modal_dialog_manager =
2565 web_modal::WebContentsModalDialogManager::FromWebContents(contents2);
2566 web_modal::WebContentsModalDialogManager::TestApi test_api(
2567 modal_dialog_manager);
2569 // Show a dialog that blocks tab |contents2|.
2570 // DummySingleWebContentsDialogManager doesn't care about the
2571 // NativeWebContentsModalDialog value, so any dummy value works.
2572 DummySingleWebContentsDialogManager* native_manager =
2573 new DummySingleWebContentsDialogManager(
2574 reinterpret_cast<NativeWebContentsModalDialog>(0),
2575 modal_dialog_manager);
2576 modal_dialog_manager->ShowDialogWithManager(
2577 reinterpret_cast<NativeWebContentsModalDialog>(0),
2578 scoped_ptr<web_modal::SingleWebContentsDialogManager>(
2579 native_manager).Pass());
2580 EXPECT_TRUE(strip_src.IsTabBlocked(1));
2583 WebContents* moved_contents = strip_src.DetachWebContentsAt(1);
2584 EXPECT_EQ(contents2, moved_contents);
2586 // Attach the tab to the destination tab strip.
2587 strip_dst.AppendWebContents(moved_contents, true);
2588 EXPECT_TRUE(strip_dst.IsTabBlocked(0));
2590 strip_dst.CloseAllTabs();
2591 strip_src.CloseAllTabs();