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/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/defaults.h"
18 #include "chrome/browser/extensions/tab_helper.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_tabstrip.h"
22 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h"
24 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
25 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
26 #include "chrome/common/url_constants.h"
27 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
28 #include "chrome/test/base/testing_profile.h"
29 #include "components/web_modal/popup_manager.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 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 ~TabStripDummyDelegate() override {}
82 void set_run_unload_listener(bool value) { run_unload_ = value; }
84 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 ~TabStripModelTestIDUserData() override {}
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 ~DummySingleWebContentsDialogManager() override {}
117 void Show() override {}
118 void Hide() override {}
119 void Close() override { delegate_->WillClose(dialog_); }
120 void Focus() override {}
121 void Pulse() override {}
122 void HostChanged(web_modal::WebContentsModalDialogHost* new_host) override {}
123 NativeWebContentsModalDialog dialog() override { return dialog_; }
126 web_modal::SingleWebContentsDialogManagerDelegate* delegate_;
127 NativeWebContentsModalDialog dialog_;
129 DISALLOW_COPY_AND_ASSIGN(DummySingleWebContentsDialogManager);
132 // Test Browser-like class for TabStripModelTest.TabBlockedState.
133 class TabBlockedStateTestBrowser
134 : public TabStripModelObserver,
135 public web_modal::WebContentsModalDialogManagerDelegate {
137 explicit TabBlockedStateTestBrowser(TabStripModel* tab_strip_model)
138 : tab_strip_model_(tab_strip_model) {
139 tab_strip_model_->AddObserver(this);
142 ~TabBlockedStateTestBrowser() override {
143 tab_strip_model_->RemoveObserver(this);
147 // TabStripModelObserver
148 void TabInsertedAt(WebContents* contents,
150 bool foreground) override {
151 web_modal::WebContentsModalDialogManager* manager =
152 web_modal::WebContentsModalDialogManager::FromWebContents(contents);
154 manager->SetDelegate(this);
157 // WebContentsModalDialogManagerDelegate
158 void SetWebContentsBlocked(content::WebContents* contents,
159 bool blocked) override {
160 int index = tab_strip_model_->GetIndexOfWebContents(contents);
162 tab_strip_model_->SetTabBlocked(index, blocked);
165 TabStripModel* tab_strip_model_;
167 DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser);
172 class TabStripModelTest : public ChromeRenderViewHostTestHarness {
174 WebContents* CreateWebContents() {
175 return WebContents::Create(WebContents::CreateParams(profile()));
178 WebContents* CreateWebContentsWithSharedRPH(WebContents* web_contents) {
179 WebContents::CreateParams create_params(
180 profile(), web_contents->GetRenderViewHost()->GetSiteInstance());
181 WebContents* retval = WebContents::Create(create_params);
182 EXPECT_EQ(retval->GetRenderProcessHost(),
183 web_contents->GetRenderProcessHost());
187 // Sets the id of the specified contents.
188 void SetID(WebContents* contents, int id) {
189 contents->SetUserData(&kTabStripModelTestIDUserDataKey,
190 new TabStripModelTestIDUserData(id));
193 // Returns the id of the specified contents.
194 int GetID(WebContents* contents) {
195 TabStripModelTestIDUserData* user_data =
196 static_cast<TabStripModelTestIDUserData*>(
197 contents->GetUserData(&kTabStripModelTestIDUserDataKey));
199 return user_data ? user_data->id() : -1;
202 // Returns the state of the given tab strip as a string. The state consists
203 // of the ID of each web contents followed by a 'p' if pinned. For example,
204 // if the model consists of two tabs with ids 2 and 1, with the first
205 // tab pinned, this returns "2p 1".
206 std::string GetTabStripStateString(const TabStripModel& model) {
208 for (int i = 0; i < model.count(); ++i) {
212 actual += base::IntToString(GetID(model.GetWebContentsAt(i)));
214 if (model.IsAppTab(i))
217 if (model.IsTabPinned(i))
223 std::string GetIndicesClosedByCommandAsString(
224 const TabStripModel& model,
226 TabStripModel::ContextMenuCommand id) const {
227 std::vector<int> indices = model.GetIndicesClosedByCommand(index, id);
229 for (size_t i = 0; i < indices.size(); ++i) {
232 result += base::IntToString(indices[i]);
237 void PrepareTabstripForSelectionTest(TabStripModel* model,
240 const std::string& selected_tabs) {
241 for (int i = 0; i < tab_count; ++i) {
242 WebContents* contents = CreateWebContents();
244 model->AppendWebContents(contents, true);
246 for (int i = 0; i < pinned_count; ++i)
247 model->SetTabPinned(i, true);
249 ui::ListSelectionModel selection_model;
250 std::vector<std::string> selection;
251 base::SplitStringAlongWhitespace(selected_tabs, &selection);
252 for (size_t i = 0; i < selection.size(); ++i) {
254 ASSERT_TRUE(base::StringToInt(selection[i], &value));
255 selection_model.AddIndexToSelection(value);
257 selection_model.set_active(selection_model.selected_indices()[0]);
258 model->SetSelectionFromModel(selection_model);
262 class MockTabStripModelObserver : public TabStripModelObserver {
264 explicit MockTabStripModelObserver(TabStripModel* model)
268 ~MockTabStripModelObserver() override {}
270 enum TabStripModelObserverAction {
286 State(WebContents* a_dst_contents,
288 TabStripModelObserverAction a_action)
289 : src_contents(NULL),
290 dst_contents(a_dst_contents),
292 dst_index(a_dst_index),
293 change_reason(CHANGE_REASON_NONE),
298 WebContents* src_contents;
299 WebContents* dst_contents;
304 TabStripModelObserverAction action;
307 int GetStateCount() const {
308 return static_cast<int>(states_.size());
311 // Returns (by way of parameters) the number of state's with CLOSE_ALL and
312 // CLOSE_ALL_CANCELED.
313 void GetCloseCounts(int* close_all_count,
314 int* close_all_canceled_count) {
315 *close_all_count = *close_all_canceled_count = 0;
316 for (int i = 0; i < GetStateCount(); ++i) {
317 switch (GetStateAt(i).action) {
319 (*close_all_count)++;
321 case CLOSE_ALL_CANCELED:
322 (*close_all_canceled_count)++;
330 const State& GetStateAt(int index) const {
331 DCHECK(index >= 0 && index < GetStateCount());
332 return states_[index];
335 bool StateEquals(int index, const State& state) {
336 const State& s = GetStateAt(index);
337 return (s.src_contents == state.src_contents &&
338 s.dst_contents == state.dst_contents &&
339 s.src_index == state.src_index &&
340 s.dst_index == state.dst_index &&
341 s.change_reason == state.change_reason &&
342 s.foreground == state.foreground &&
343 s.action == state.action);
346 // TabStripModelObserver implementation:
347 void TabInsertedAt(WebContents* contents,
349 bool foreground) override {
351 State s(contents, index, INSERT);
352 s.foreground = foreground;
353 states_.push_back(s);
355 void ActiveTabChanged(WebContents* old_contents,
356 WebContents* new_contents,
358 int reason) override {
359 State s(new_contents, index, ACTIVATE);
360 s.src_contents = old_contents;
361 s.change_reason = reason;
362 states_.push_back(s);
364 void TabSelectionChanged(TabStripModel* tab_strip_model,
365 const ui::ListSelectionModel& old_model) override {
366 State s(model()->GetActiveWebContents(), model()->active_index(), SELECT);
367 s.src_contents = model()->GetWebContentsAt(old_model.active());
368 s.src_index = old_model.active();
369 states_.push_back(s);
371 void TabMoved(WebContents* contents, int from_index, int to_index) override {
372 State s(contents, to_index, MOVE);
373 s.src_index = from_index;
374 states_.push_back(s);
377 void TabClosingAt(TabStripModel* tab_strip_model,
378 WebContents* contents,
379 int index) override {
380 states_.push_back(State(contents, index, CLOSE));
382 void TabDetachedAt(WebContents* contents, int index) override {
383 states_.push_back(State(contents, index, DETACH));
385 void TabDeactivated(WebContents* contents) override {
386 states_.push_back(State(contents, model()->active_index(), DEACTIVATE));
388 void TabChangedAt(WebContents* contents,
390 TabChangeType change_type) override {
391 states_.push_back(State(contents, index, CHANGE));
393 void TabReplacedAt(TabStripModel* tab_strip_model,
394 WebContents* old_contents,
395 WebContents* new_contents,
396 int index) override {
397 State s(new_contents, index, REPLACED);
398 s.src_contents = old_contents;
399 states_.push_back(s);
401 void TabPinnedStateChanged(WebContents* contents, int index) override {
402 states_.push_back(State(contents, index, PINNED));
404 void TabStripEmpty() override { empty_ = true; }
405 void WillCloseAllTabs() override {
406 states_.push_back(State(NULL, -1, CLOSE_ALL));
408 void CloseAllTabsCanceled() override {
409 states_.push_back(State(NULL, -1, CLOSE_ALL_CANCELED));
411 void TabStripModelDeleted() override { deleted_ = true; }
417 bool empty() const { return empty_; }
418 bool deleted() const { return deleted_; }
419 TabStripModel* model() { return model_; }
422 std::vector<State> states_;
426 TabStripModel* model_;
428 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver);
431 TEST_F(TabStripModelTest, TestBasicAPI) {
432 TabStripDummyDelegate delegate;
433 TabStripModel tabstrip(&delegate, profile());
434 MockTabStripModelObserver observer(&tabstrip);
435 tabstrip.AddObserver(&observer);
437 EXPECT_TRUE(tabstrip.empty());
439 typedef MockTabStripModelObserver::State State;
441 WebContents* contents1 = CreateWebContents();
444 // Note! The ordering of these tests is important, each subsequent test
445 // builds on the state established in the previous. This is important if you
446 // ever insert tests rather than append.
448 // Test AppendWebContents, ContainsIndex
450 EXPECT_FALSE(tabstrip.ContainsIndex(0));
451 tabstrip.AppendWebContents(contents1, true);
452 EXPECT_TRUE(tabstrip.ContainsIndex(0));
453 EXPECT_EQ(1, tabstrip.count());
454 EXPECT_EQ(3, observer.GetStateCount());
455 State s1(contents1, 0, MockTabStripModelObserver::INSERT);
456 s1.foreground = true;
457 EXPECT_TRUE(observer.StateEquals(0, s1));
458 State s2(contents1, 0, MockTabStripModelObserver::ACTIVATE);
459 EXPECT_TRUE(observer.StateEquals(1, s2));
460 State s3(contents1, 0, MockTabStripModelObserver::SELECT);
461 s3.src_contents = NULL;
462 s3.src_index = ui::ListSelectionModel::kUnselectedIndex;
463 EXPECT_TRUE(observer.StateEquals(2, s3));
464 observer.ClearStates();
466 EXPECT_EQ("1", GetTabStripStateString(tabstrip));
468 // Test InsertWebContentsAt, foreground tab.
469 WebContents* contents2 = CreateWebContents();
472 tabstrip.InsertWebContentsAt(1, contents2, TabStripModel::ADD_ACTIVE);
474 EXPECT_EQ(2, tabstrip.count());
475 EXPECT_EQ(4, observer.GetStateCount());
476 State s1(contents2, 1, MockTabStripModelObserver::INSERT);
477 s1.foreground = true;
478 EXPECT_TRUE(observer.StateEquals(0, s1));
479 State s2(contents1, 0, MockTabStripModelObserver::DEACTIVATE);
480 EXPECT_TRUE(observer.StateEquals(1, s2));
481 State s3(contents2, 1, MockTabStripModelObserver::ACTIVATE);
482 s3.src_contents = contents1;
483 EXPECT_TRUE(observer.StateEquals(2, s3));
484 State s4(contents2, 1, MockTabStripModelObserver::SELECT);
485 s4.src_contents = contents1;
487 EXPECT_TRUE(observer.StateEquals(3, s4));
488 observer.ClearStates();
490 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip));
492 // Test InsertWebContentsAt, background tab.
493 WebContents* contents3 = CreateWebContents();
496 tabstrip.InsertWebContentsAt(2, contents3, TabStripModel::ADD_NONE);
498 EXPECT_EQ(3, tabstrip.count());
499 EXPECT_EQ(1, observer.GetStateCount());
500 State s1(contents3, 2, MockTabStripModelObserver::INSERT);
501 s1.foreground = false;
502 EXPECT_TRUE(observer.StateEquals(0, s1));
503 observer.ClearStates();
505 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
507 // Test ActivateTabAt
509 tabstrip.ActivateTabAt(2, true);
510 EXPECT_EQ(3, observer.GetStateCount());
511 State s1(contents2, 1, MockTabStripModelObserver::DEACTIVATE);
512 EXPECT_TRUE(observer.StateEquals(0, s1));
513 State s2(contents3, 2, MockTabStripModelObserver::ACTIVATE);
514 s2.src_contents = contents2;
515 s2.change_reason = TabStripModelObserver::CHANGE_REASON_USER_GESTURE;
516 EXPECT_TRUE(observer.StateEquals(1, s2));
517 State s3(contents3, 2, MockTabStripModelObserver::SELECT);
518 s3.src_contents = contents2;
520 EXPECT_TRUE(observer.StateEquals(2, s3));
521 observer.ClearStates();
523 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
525 // Test DetachWebContentsAt
528 WebContents* detached = tabstrip.DetachWebContentsAt(2);
529 // ... and append again because we want this for later.
530 tabstrip.AppendWebContents(detached, true);
531 EXPECT_EQ(8, observer.GetStateCount());
532 State s1(detached, 2, MockTabStripModelObserver::DETACH);
533 EXPECT_TRUE(observer.StateEquals(0, s1));
534 State s2(detached, ui::ListSelectionModel::kUnselectedIndex,
535 MockTabStripModelObserver::DEACTIVATE);
536 EXPECT_TRUE(observer.StateEquals(1, s2));
537 State s3(contents2, 1, MockTabStripModelObserver::ACTIVATE);
538 s3.src_contents = contents3;
539 s3.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
540 EXPECT_TRUE(observer.StateEquals(2, s3));
541 State s4(contents2, 1, MockTabStripModelObserver::SELECT);
542 s4.src_contents = NULL;
543 s4.src_index = ui::ListSelectionModel::kUnselectedIndex;
544 EXPECT_TRUE(observer.StateEquals(3, s4));
545 State s5(detached, 2, MockTabStripModelObserver::INSERT);
546 s5.foreground = true;
547 EXPECT_TRUE(observer.StateEquals(4, s5));
548 State s6(contents2, 1, MockTabStripModelObserver::DEACTIVATE);
549 EXPECT_TRUE(observer.StateEquals(5, s6));
550 State s7(detached, 2, MockTabStripModelObserver::ACTIVATE);
551 s7.src_contents = contents2;
552 s7.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
553 EXPECT_TRUE(observer.StateEquals(6, s7));
554 State s8(detached, 2, MockTabStripModelObserver::SELECT);
555 s8.src_contents = contents2;
557 EXPECT_TRUE(observer.StateEquals(7, s8));
558 observer.ClearStates();
560 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
562 // Test CloseWebContentsAt
564 EXPECT_TRUE(tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE));
565 EXPECT_EQ(2, tabstrip.count());
567 EXPECT_EQ(5, observer.GetStateCount());
568 State s1(contents3, 2, MockTabStripModelObserver::CLOSE);
569 EXPECT_TRUE(observer.StateEquals(0, s1));
570 State s2(contents3, 2, MockTabStripModelObserver::DETACH);
571 EXPECT_TRUE(observer.StateEquals(1, s2));
572 State s3(contents3, ui::ListSelectionModel::kUnselectedIndex,
573 MockTabStripModelObserver::DEACTIVATE);
574 EXPECT_TRUE(observer.StateEquals(2, s3));
575 State s4(contents2, 1, MockTabStripModelObserver::ACTIVATE);
576 s4.src_contents = contents3;
577 s4.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
578 EXPECT_TRUE(observer.StateEquals(3, s4));
579 State s5(contents2, 1, MockTabStripModelObserver::SELECT);
580 s5.src_contents = NULL;
581 s5.src_index = ui::ListSelectionModel::kUnselectedIndex;
582 EXPECT_TRUE(observer.StateEquals(4, s5));
583 observer.ClearStates();
585 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip));
587 // Test MoveWebContentsAt, select_after_move == true
589 tabstrip.MoveWebContentsAt(1, 0, true);
591 EXPECT_EQ(1, observer.GetStateCount());
592 State s1(contents2, 0, MockTabStripModelObserver::MOVE);
594 EXPECT_TRUE(observer.StateEquals(0, s1));
595 EXPECT_EQ(0, tabstrip.active_index());
596 observer.ClearStates();
598 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip));
600 // Test MoveWebContentsAt, select_after_move == false
602 tabstrip.MoveWebContentsAt(1, 0, false);
603 EXPECT_EQ(1, observer.GetStateCount());
604 State s1(contents1, 0, MockTabStripModelObserver::MOVE);
606 EXPECT_TRUE(observer.StateEquals(0, s1));
607 EXPECT_EQ(1, tabstrip.active_index());
609 tabstrip.MoveWebContentsAt(0, 1, false);
610 observer.ClearStates();
612 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip));
616 EXPECT_EQ(contents2, tabstrip.GetActiveWebContents());
617 EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(0));
618 EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
619 EXPECT_EQ(0, tabstrip.GetIndexOfWebContents(contents2));
620 EXPECT_EQ(1, tabstrip.GetIndexOfWebContents(contents1));
623 // Test UpdateWebContentsStateAt
625 tabstrip.UpdateWebContentsStateAt(0, TabStripModelObserver::ALL);
626 EXPECT_EQ(1, observer.GetStateCount());
627 State s1(contents2, 0, MockTabStripModelObserver::CHANGE);
628 EXPECT_TRUE(observer.StateEquals(0, s1));
629 observer.ClearStates();
632 // Test SelectNextTab, SelectPreviousTab, SelectLastTab
634 // Make sure the second of the two tabs is selected first...
635 tabstrip.ActivateTabAt(1, true);
636 tabstrip.SelectPreviousTab();
637 EXPECT_EQ(0, tabstrip.active_index());
638 tabstrip.SelectLastTab();
639 EXPECT_EQ(1, tabstrip.active_index());
640 tabstrip.SelectNextTab();
641 EXPECT_EQ(0, tabstrip.active_index());
644 // Test CloseSelectedTabs
646 tabstrip.CloseSelectedTabs();
647 // |CloseSelectedTabs| calls CloseWebContentsAt, we already tested that, now
648 // just verify that the count and selected index have changed
650 EXPECT_EQ(1, tabstrip.count());
651 EXPECT_EQ(0, tabstrip.active_index());
654 observer.ClearStates();
655 tabstrip.CloseAllTabs();
657 int close_all_count = 0, close_all_canceled_count = 0;
658 observer.GetCloseCounts(&close_all_count, &close_all_canceled_count);
659 EXPECT_EQ(1, close_all_count);
660 EXPECT_EQ(0, close_all_canceled_count);
662 // TabStripModel should now be empty.
663 EXPECT_TRUE(tabstrip.empty());
665 // Opener methods are tested below...
667 tabstrip.RemoveObserver(&observer);
670 TEST_F(TabStripModelTest, TestBasicOpenerAPI) {
671 TabStripDummyDelegate delegate;
672 TabStripModel tabstrip(&delegate, profile());
673 EXPECT_TRUE(tabstrip.empty());
675 // This is a basic test of opener functionality. opener is created
676 // as the first tab in the strip and then we create 5 other tabs in the
677 // background with opener set as their opener.
679 WebContents* opener = CreateWebContents();
680 tabstrip.AppendWebContents(opener, true);
681 WebContents* contents1 = CreateWebContents();
682 WebContents* contents2 = CreateWebContents();
683 WebContents* contents3 = CreateWebContents();
684 WebContents* contents4 = CreateWebContents();
685 WebContents* contents5 = CreateWebContents();
687 // We use |InsertWebContentsAt| here instead of |AppendWebContents| so that
688 // openership relationships are preserved.
689 tabstrip.InsertWebContentsAt(tabstrip.count(), contents1,
690 TabStripModel::ADD_INHERIT_GROUP);
691 tabstrip.InsertWebContentsAt(tabstrip.count(), contents2,
692 TabStripModel::ADD_INHERIT_GROUP);
693 tabstrip.InsertWebContentsAt(tabstrip.count(), contents3,
694 TabStripModel::ADD_INHERIT_GROUP);
695 tabstrip.InsertWebContentsAt(tabstrip.count(), contents4,
696 TabStripModel::ADD_INHERIT_GROUP);
697 tabstrip.InsertWebContentsAt(tabstrip.count(), contents5,
698 TabStripModel::ADD_INHERIT_GROUP);
700 // All the tabs should have the same opener.
701 for (int i = 1; i < tabstrip.count(); ++i)
702 EXPECT_EQ(opener, tabstrip.GetOpenerOfWebContentsAt(i));
704 // If there is a next adjacent item, then the index should be of that item.
705 EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 1, false));
706 // If the last tab in the group is closed, the preceding tab in the same
707 // group should be selected.
708 EXPECT_EQ(4, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 5, false));
710 // Tests the method that finds the last tab opened by the same opener in the
711 // strip (this is the insertion index for the next background tab for the
712 // specified opener).
713 EXPECT_EQ(5, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
715 // For a tab that has opened no other tabs, the return value should always be
718 tabstrip.GetIndexOfNextWebContentsOpenedBy(contents1, 3, false));
720 tabstrip.GetIndexOfLastWebContentsOpenedBy(contents1, 3));
722 // ForgetAllOpeners should destroy all opener relationships.
723 tabstrip.ForgetAllOpeners();
724 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 1, false));
725 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 5, false));
726 EXPECT_EQ(-1, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
728 // Specify the last tab as the opener of the others.
729 for (int i = 0; i < tabstrip.count() - 1; ++i)
730 tabstrip.SetOpenerOfWebContentsAt(i, contents5);
732 for (int i = 0; i < tabstrip.count() - 1; ++i)
733 EXPECT_EQ(contents5, tabstrip.GetOpenerOfWebContentsAt(i));
735 // If there is a next adjacent item, then the index should be of that item.
736 EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(contents5, 1, false));
738 // If the last tab in the group is closed, the preceding tab in the same
739 // group should be selected.
740 EXPECT_EQ(3, tabstrip.GetIndexOfNextWebContentsOpenedBy(contents5, 4, false));
742 tabstrip.CloseAllTabs();
743 EXPECT_TRUE(tabstrip.empty());
746 static int GetInsertionIndex(TabStripModel* tabstrip) {
747 return tabstrip->order_controller()->DetermineInsertionIndex(
748 ui::PAGE_TRANSITION_LINK, false);
751 static void InsertWebContentses(TabStripModel* tabstrip,
752 WebContents* contents1,
753 WebContents* contents2,
754 WebContents* contents3) {
755 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
757 TabStripModel::ADD_INHERIT_GROUP);
758 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
760 TabStripModel::ADD_INHERIT_GROUP);
761 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
763 TabStripModel::ADD_INHERIT_GROUP);
766 // Tests opening background tabs.
767 TEST_F(TabStripModelTest, TestLTRInsertionOptions) {
768 TabStripDummyDelegate delegate;
769 TabStripModel tabstrip(&delegate, profile());
770 EXPECT_TRUE(tabstrip.empty());
772 WebContents* opener = CreateWebContents();
773 tabstrip.AppendWebContents(opener, true);
775 WebContents* contents1 = CreateWebContents();
776 WebContents* contents2 = CreateWebContents();
777 WebContents* contents3 = CreateWebContents();
780 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
781 EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
782 EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(2));
783 EXPECT_EQ(contents3, tabstrip.GetWebContentsAt(3));
785 tabstrip.CloseAllTabs();
786 EXPECT_TRUE(tabstrip.empty());
789 // This test constructs a tabstrip, and then simulates loading several tabs in
790 // the background from link clicks on the first tab. Then it simulates opening
791 // a new tab from the first tab in the foreground via a link click, verifies
792 // that this tab is opened adjacent to the opener, then closes it.
793 // Finally it tests that a tab opened for some non-link purpose opens at the
794 // end of the strip, not bundled to any existing context.
795 TEST_F(TabStripModelTest, TestInsertionIndexDetermination) {
796 TabStripDummyDelegate delegate;
797 TabStripModel tabstrip(&delegate, profile());
798 EXPECT_TRUE(tabstrip.empty());
800 WebContents* opener = CreateWebContents();
801 tabstrip.AppendWebContents(opener, true);
803 // Open some other random unrelated tab in the background to monkey with our
805 WebContents* other = CreateWebContents();
806 tabstrip.AppendWebContents(other, false);
808 WebContents* contents1 = CreateWebContents();
809 WebContents* contents2 = CreateWebContents();
810 WebContents* contents3 = CreateWebContents();
812 // Start by testing LTR.
813 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
814 EXPECT_EQ(opener, tabstrip.GetWebContentsAt(0));
815 EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
816 EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(2));
817 EXPECT_EQ(contents3, tabstrip.GetWebContentsAt(3));
818 EXPECT_EQ(other, tabstrip.GetWebContentsAt(4));
820 // The opener API should work...
821 EXPECT_EQ(3, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 2, false));
822 EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
823 EXPECT_EQ(3, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
825 // Now open a foreground tab from a link. It should be opened adjacent to the
827 WebContents* fg_link_contents = CreateWebContents();
828 int insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
829 ui::PAGE_TRANSITION_LINK, true);
830 EXPECT_EQ(1, insert_index);
831 tabstrip.InsertWebContentsAt(insert_index, fg_link_contents,
832 TabStripModel::ADD_ACTIVE |
833 TabStripModel::ADD_INHERIT_GROUP);
834 EXPECT_EQ(1, tabstrip.active_index());
835 EXPECT_EQ(fg_link_contents, tabstrip.GetActiveWebContents());
837 // Now close this contents. The selection should move to the opener contents.
838 tabstrip.CloseSelectedTabs();
839 EXPECT_EQ(0, tabstrip.active_index());
841 // Now open a new empty tab. It should open at the end of the strip.
842 WebContents* fg_nonlink_contents = CreateWebContents();
843 insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
844 ui::PAGE_TRANSITION_AUTO_BOOKMARK, true);
845 EXPECT_EQ(tabstrip.count(), insert_index);
846 // We break the opener relationship...
847 tabstrip.InsertWebContentsAt(insert_index,
849 TabStripModel::ADD_NONE);
850 // Now select it, so that user_gesture == true causes the opener relationship
851 // to be forgotten...
852 tabstrip.ActivateTabAt(tabstrip.count() - 1, true);
853 EXPECT_EQ(tabstrip.count() - 1, tabstrip.active_index());
854 EXPECT_EQ(fg_nonlink_contents, tabstrip.GetActiveWebContents());
856 // Verify that all opener relationships are forgotten.
857 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 2, false));
858 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
859 EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
860 EXPECT_EQ(-1, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
862 tabstrip.CloseAllTabs();
863 EXPECT_TRUE(tabstrip.empty());
866 // Tests that selection is shifted to the correct tab when a tab is closed.
867 // If a tab is in the background when it is closed, the selection does not
869 // If a tab is in the foreground (selected),
870 // If that tab does not have an opener, selection shifts to the right.
871 // If the tab has an opener,
872 // The next tab (scanning LTR) in the entire strip that has the same opener
874 // If there are no other tabs that have the same opener,
875 // The opener is selected
877 TEST_F(TabStripModelTest, TestSelectOnClose) {
878 TabStripDummyDelegate delegate;
879 TabStripModel tabstrip(&delegate, profile());
880 EXPECT_TRUE(tabstrip.empty());
882 WebContents* opener = CreateWebContents();
883 tabstrip.AppendWebContents(opener, true);
885 WebContents* contents1 = CreateWebContents();
886 WebContents* contents2 = CreateWebContents();
887 WebContents* contents3 = CreateWebContents();
889 // Note that we use Detach instead of Close throughout this test to avoid
890 // having to keep reconstructing these WebContentses.
892 // First test that closing tabs that are in the background doesn't adjust the
893 // current selection.
894 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
895 EXPECT_EQ(0, tabstrip.active_index());
897 tabstrip.DetachWebContentsAt(1);
898 EXPECT_EQ(0, tabstrip.active_index());
900 for (int i = tabstrip.count() - 1; i >= 1; --i)
901 tabstrip.DetachWebContentsAt(i);
903 // Now test that when a tab doesn't have an opener, selection shifts to the
904 // right when the tab is closed.
905 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
906 EXPECT_EQ(0, tabstrip.active_index());
908 tabstrip.ForgetAllOpeners();
909 tabstrip.ActivateTabAt(1, true);
910 EXPECT_EQ(1, tabstrip.active_index());
911 tabstrip.DetachWebContentsAt(1);
912 EXPECT_EQ(1, tabstrip.active_index());
913 tabstrip.DetachWebContentsAt(1);
914 EXPECT_EQ(1, tabstrip.active_index());
915 tabstrip.DetachWebContentsAt(1);
916 EXPECT_EQ(0, tabstrip.active_index());
918 for (int i = tabstrip.count() - 1; i >= 1; --i)
919 tabstrip.DetachWebContentsAt(i);
921 // Now test that when a tab does have an opener, it selects the next tab
922 // opened by the same opener scanning LTR when it is closed.
923 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
924 EXPECT_EQ(0, tabstrip.active_index());
925 tabstrip.ActivateTabAt(2, false);
926 EXPECT_EQ(2, tabstrip.active_index());
927 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
928 EXPECT_EQ(2, tabstrip.active_index());
929 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
930 EXPECT_EQ(1, tabstrip.active_index());
931 tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
932 EXPECT_EQ(0, tabstrip.active_index());
933 // Finally test that when a tab has no "siblings" that the opener is
935 WebContents* other_contents = CreateWebContents();
936 tabstrip.InsertWebContentsAt(1, other_contents,
937 TabStripModel::ADD_NONE);
938 EXPECT_EQ(2, tabstrip.count());
939 WebContents* opened_contents = CreateWebContents();
940 tabstrip.InsertWebContentsAt(2, opened_contents,
941 TabStripModel::ADD_ACTIVE |
942 TabStripModel::ADD_INHERIT_GROUP);
943 EXPECT_EQ(2, tabstrip.active_index());
944 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
945 EXPECT_EQ(0, tabstrip.active_index());
947 tabstrip.CloseAllTabs();
948 EXPECT_TRUE(tabstrip.empty());
951 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
953 TEST_F(TabStripModelTest, CommandCloseTab) {
954 TabStripDummyDelegate delegate;
955 TabStripModel tabstrip(&delegate, profile());
956 EXPECT_TRUE(tabstrip.empty());
958 // Make sure can_close is honored.
959 ASSERT_NO_FATAL_FAILURE(
960 PrepareTabstripForSelectionTest(&tabstrip, 1, 0, "0"));
961 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
962 0, TabStripModel::CommandCloseTab));
963 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
964 ASSERT_TRUE(tabstrip.empty());
966 // Make sure close on a tab that is selected affects all the selected tabs.
967 ASSERT_NO_FATAL_FAILURE(
968 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
969 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
970 0, TabStripModel::CommandCloseTab));
971 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
972 // Should have closed tabs 0 and 1.
973 EXPECT_EQ("2", GetTabStripStateString(tabstrip));
975 tabstrip.CloseAllTabs();
976 EXPECT_TRUE(tabstrip.empty());
978 // Select two tabs and make close on a tab that isn't selected doesn't affect
980 ASSERT_NO_FATAL_FAILURE(
981 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
982 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
983 2, TabStripModel::CommandCloseTab));
984 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
985 // Should have closed tab 2.
986 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
987 tabstrip.CloseAllTabs();
988 EXPECT_TRUE(tabstrip.empty());
990 // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned.
991 ASSERT_NO_FATAL_FAILURE(
992 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1"));
993 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
994 0, TabStripModel::CommandCloseTab));
995 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
996 // Should have closed tab 2.
997 EXPECT_EQ("2", GetTabStripStateString(tabstrip));
998 tabstrip.CloseAllTabs();
999 EXPECT_TRUE(tabstrip.empty());
1002 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1003 // CommandCloseTabs.
1004 TEST_F(TabStripModelTest, CommandCloseOtherTabs) {
1005 TabStripDummyDelegate delegate;
1006 TabStripModel tabstrip(&delegate, profile());
1007 EXPECT_TRUE(tabstrip.empty());
1009 // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled
1010 // and close two tabs.
1011 ASSERT_NO_FATAL_FAILURE(
1012 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
1013 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1014 0, TabStripModel::CommandCloseOtherTabs));
1015 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs);
1016 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
1017 tabstrip.CloseAllTabs();
1018 EXPECT_TRUE(tabstrip.empty());
1020 // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it
1021 // with a non-selected index should close the two other tabs.
1022 ASSERT_NO_FATAL_FAILURE(
1023 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
1024 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1025 2, 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 all, CommandCloseOtherTabs should not be enabled.
1032 ASSERT_NO_FATAL_FAILURE(
1033 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1 2"));
1034 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1035 2, TabStripModel::CommandCloseOtherTabs));
1036 tabstrip.CloseAllTabs();
1037 EXPECT_TRUE(tabstrip.empty());
1039 // Three tabs, pin one, select the two non-pinned.
1040 ASSERT_NO_FATAL_FAILURE(
1041 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1 2"));
1042 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1043 1, TabStripModel::CommandCloseOtherTabs));
1044 // If we don't pass in the pinned index, the command should be enabled.
1045 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1046 0, TabStripModel::CommandCloseOtherTabs));
1047 tabstrip.CloseAllTabs();
1048 EXPECT_TRUE(tabstrip.empty());
1050 // 3 tabs, one pinned.
1051 ASSERT_NO_FATAL_FAILURE(
1052 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1"));
1053 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1054 1, TabStripModel::CommandCloseOtherTabs));
1055 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1056 0, TabStripModel::CommandCloseOtherTabs));
1057 tabstrip.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs);
1058 // The pinned tab shouldn't be closed.
1059 EXPECT_EQ("0p 1", GetTabStripStateString(tabstrip));
1060 tabstrip.CloseAllTabs();
1061 EXPECT_TRUE(tabstrip.empty());
1064 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1065 // CommandCloseTabsToRight.
1066 TEST_F(TabStripModelTest, CommandCloseTabsToRight) {
1067 TabStripDummyDelegate delegate;
1068 TabStripModel tabstrip(&delegate, profile());
1069 EXPECT_TRUE(tabstrip.empty());
1071 // Create three tabs, select last two tabs, CommandCloseTabsToRight should
1072 // only be enabled for the first tab.
1073 ASSERT_NO_FATAL_FAILURE(
1074 PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "1 2"));
1075 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1076 0, TabStripModel::CommandCloseTabsToRight));
1077 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1078 1, TabStripModel::CommandCloseTabsToRight));
1079 EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1080 2, TabStripModel::CommandCloseTabsToRight));
1081 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
1082 EXPECT_EQ("0", GetTabStripStateString(tabstrip));
1083 tabstrip.CloseAllTabs();
1084 EXPECT_TRUE(tabstrip.empty());
1087 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1088 // CommandTogglePinned.
1089 TEST_F(TabStripModelTest, CommandTogglePinned) {
1090 TabStripDummyDelegate delegate;
1091 TabStripModel tabstrip(&delegate, profile());
1092 EXPECT_TRUE(tabstrip.empty());
1094 // Create three tabs with one pinned, pin the first two.
1095 ASSERT_NO_FATAL_FAILURE(
1096 PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1"));
1097 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1098 0, TabStripModel::CommandTogglePinned));
1099 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1100 1, TabStripModel::CommandTogglePinned));
1101 EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1102 2, TabStripModel::CommandTogglePinned));
1103 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned);
1104 EXPECT_EQ("0p 1p 2", GetTabStripStateString(tabstrip));
1106 // Execute CommandTogglePinned again, this should unpin.
1107 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned);
1108 EXPECT_EQ("0 1 2", GetTabStripStateString(tabstrip));
1111 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned);
1112 EXPECT_EQ("2p 0 1", GetTabStripStateString(tabstrip));
1114 tabstrip.CloseAllTabs();
1115 EXPECT_TRUE(tabstrip.empty());
1118 // Tests the following context menu commands:
1120 // - Close Other Tabs
1121 // - Close Tabs To Right
1122 TEST_F(TabStripModelTest, TestContextMenuCloseCommands) {
1123 TabStripDummyDelegate delegate;
1124 TabStripModel tabstrip(&delegate, profile());
1125 EXPECT_TRUE(tabstrip.empty());
1127 WebContents* opener = CreateWebContents();
1128 tabstrip.AppendWebContents(opener, true);
1130 WebContents* contents1 = CreateWebContents();
1131 WebContents* contents2 = CreateWebContents();
1132 WebContents* contents3 = CreateWebContents();
1134 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
1135 EXPECT_EQ(0, tabstrip.active_index());
1137 tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
1138 EXPECT_EQ(3, tabstrip.count());
1140 tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
1141 EXPECT_EQ(1, tabstrip.count());
1142 EXPECT_EQ(opener, tabstrip.GetActiveWebContents());
1144 WebContents* dummy = CreateWebContents();
1145 tabstrip.AppendWebContents(dummy, false);
1147 contents1 = CreateWebContents();
1148 contents2 = CreateWebContents();
1149 contents3 = CreateWebContents();
1150 InsertWebContentses(&tabstrip, contents1, contents2, contents3);
1151 EXPECT_EQ(5, tabstrip.count());
1153 int dummy_index = tabstrip.count() - 1;
1154 tabstrip.ActivateTabAt(dummy_index, true);
1155 EXPECT_EQ(dummy, tabstrip.GetActiveWebContents());
1157 tabstrip.ExecuteContextMenuCommand(dummy_index,
1158 TabStripModel::CommandCloseOtherTabs);
1159 EXPECT_EQ(1, tabstrip.count());
1160 EXPECT_EQ(dummy, tabstrip.GetActiveWebContents());
1162 tabstrip.CloseAllTabs();
1163 EXPECT_TRUE(tabstrip.empty());
1166 // Tests GetIndicesClosedByCommand.
1167 TEST_F(TabStripModelTest, GetIndicesClosedByCommand) {
1168 TabStripDummyDelegate delegate;
1169 TabStripModel tabstrip(&delegate, profile());
1170 EXPECT_TRUE(tabstrip.empty());
1172 WebContents* contents1 = CreateWebContents();
1173 WebContents* contents2 = CreateWebContents();
1174 WebContents* contents3 = CreateWebContents();
1175 WebContents* contents4 = CreateWebContents();
1176 WebContents* contents5 = CreateWebContents();
1178 tabstrip.AppendWebContents(contents1, true);
1179 tabstrip.AppendWebContents(contents2, true);
1180 tabstrip.AppendWebContents(contents3, true);
1181 tabstrip.AppendWebContents(contents4, true);
1182 tabstrip.AppendWebContents(contents5, true);
1184 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1185 tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
1186 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1187 tabstrip, 1, TabStripModel::CommandCloseTabsToRight));
1189 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1190 tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
1191 EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
1192 tabstrip, 1, TabStripModel::CommandCloseOtherTabs));
1194 // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
1196 tabstrip.SetTabPinned(0, true);
1197 tabstrip.SetTabPinned(1, true);
1199 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1200 tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
1201 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1202 tabstrip, 2, TabStripModel::CommandCloseTabsToRight));
1204 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1205 tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
1206 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1207 tabstrip, 2, TabStripModel::CommandCloseOtherTabs));
1209 tabstrip.CloseAllTabs();
1210 EXPECT_TRUE(tabstrip.empty());
1213 // Tests whether or not WebContentses are inserted in the correct position
1214 // using this "smart" function with a simulated middle click action on a series
1215 // of links on the home page.
1216 TEST_F(TabStripModelTest, AddWebContents_MiddleClickLinksAndClose) {
1217 TabStripDummyDelegate delegate;
1218 TabStripModel tabstrip(&delegate, profile());
1219 EXPECT_TRUE(tabstrip.empty());
1221 // Open the Home Page.
1222 WebContents* homepage_contents = CreateWebContents();
1223 tabstrip.AddWebContents(
1224 homepage_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1225 TabStripModel::ADD_ACTIVE);
1227 // Open some other tab, by user typing.
1228 WebContents* typed_page_contents = CreateWebContents();
1229 tabstrip.AddWebContents(
1230 typed_page_contents, -1, ui::PAGE_TRANSITION_TYPED,
1231 TabStripModel::ADD_ACTIVE);
1233 EXPECT_EQ(2, tabstrip.count());
1235 // Re-select the home page.
1236 tabstrip.ActivateTabAt(0, true);
1238 // Open a bunch of tabs by simulating middle clicking on links on the home
1240 WebContents* middle_click_contents1 = CreateWebContents();
1241 tabstrip.AddWebContents(
1242 middle_click_contents1, -1, ui::PAGE_TRANSITION_LINK,
1243 TabStripModel::ADD_NONE);
1244 WebContents* middle_click_contents2 = CreateWebContents();
1245 tabstrip.AddWebContents(
1246 middle_click_contents2, -1, ui::PAGE_TRANSITION_LINK,
1247 TabStripModel::ADD_NONE);
1248 WebContents* middle_click_contents3 = CreateWebContents();
1249 tabstrip.AddWebContents(
1250 middle_click_contents3, -1, ui::PAGE_TRANSITION_LINK,
1251 TabStripModel::ADD_NONE);
1253 EXPECT_EQ(5, tabstrip.count());
1255 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1256 EXPECT_EQ(middle_click_contents1, tabstrip.GetWebContentsAt(1));
1257 EXPECT_EQ(middle_click_contents2, tabstrip.GetWebContentsAt(2));
1258 EXPECT_EQ(middle_click_contents3, tabstrip.GetWebContentsAt(3));
1259 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(4));
1261 // Now simulate selecting a tab in the middle of the group of tabs opened from
1262 // the home page and start closing them. Each WebContents in the group
1263 // should be closed, right to left. This test is constructed to start at the
1264 // middle WebContents in the group to make sure the cursor wraps around
1265 // to the first WebContents in the group before closing the opener or
1266 // any other WebContents.
1267 tabstrip.ActivateTabAt(2, true);
1268 tabstrip.CloseSelectedTabs();
1269 EXPECT_EQ(middle_click_contents3, tabstrip.GetActiveWebContents());
1270 tabstrip.CloseSelectedTabs();
1271 EXPECT_EQ(middle_click_contents1, tabstrip.GetActiveWebContents());
1272 tabstrip.CloseSelectedTabs();
1273 EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1274 tabstrip.CloseSelectedTabs();
1275 EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1277 EXPECT_EQ(1, tabstrip.count());
1279 tabstrip.CloseAllTabs();
1280 EXPECT_TRUE(tabstrip.empty());
1283 // Tests whether or not a WebContents created by a left click on a link
1284 // that opens a new tab is inserted correctly adjacent to the tab that spawned
1286 TEST_F(TabStripModelTest, AddWebContents_LeftClickPopup) {
1287 TabStripDummyDelegate delegate;
1288 TabStripModel tabstrip(&delegate, profile());
1289 EXPECT_TRUE(tabstrip.empty());
1291 // Open the Home Page.
1292 WebContents* homepage_contents = CreateWebContents();
1293 tabstrip.AddWebContents(
1294 homepage_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1295 TabStripModel::ADD_ACTIVE);
1297 // Open some other tab, by user typing.
1298 WebContents* typed_page_contents = CreateWebContents();
1299 tabstrip.AddWebContents(
1300 typed_page_contents, -1, ui::PAGE_TRANSITION_TYPED,
1301 TabStripModel::ADD_ACTIVE);
1303 EXPECT_EQ(2, tabstrip.count());
1305 // Re-select the home page.
1306 tabstrip.ActivateTabAt(0, true);
1308 // Open a tab by simulating a left click on a link that opens in a new tab.
1309 WebContents* left_click_contents = CreateWebContents();
1310 tabstrip.AddWebContents(left_click_contents, -1,
1311 ui::PAGE_TRANSITION_LINK,
1312 TabStripModel::ADD_ACTIVE);
1314 // Verify the state meets our expectations.
1315 EXPECT_EQ(3, tabstrip.count());
1316 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1317 EXPECT_EQ(left_click_contents, tabstrip.GetWebContentsAt(1));
1318 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(2));
1320 // The newly created tab should be selected.
1321 EXPECT_EQ(left_click_contents, tabstrip.GetActiveWebContents());
1323 // After closing the selected tab, the selection should move to the left, to
1325 tabstrip.CloseSelectedTabs();
1326 EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1328 EXPECT_EQ(2, tabstrip.count());
1330 tabstrip.CloseAllTabs();
1331 EXPECT_TRUE(tabstrip.empty());
1334 // Tests whether or not new tabs that should split context (typed pages,
1335 // generated urls, also blank tabs) open at the end of the tabstrip instead of
1337 TEST_F(TabStripModelTest, AddWebContents_CreateNewBlankTab) {
1338 TabStripDummyDelegate delegate;
1339 TabStripModel tabstrip(&delegate, profile());
1340 EXPECT_TRUE(tabstrip.empty());
1342 // Open the Home Page.
1343 WebContents* homepage_contents = CreateWebContents();
1344 tabstrip.AddWebContents(
1345 homepage_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1346 TabStripModel::ADD_ACTIVE);
1348 // Open some other tab, by user typing.
1349 WebContents* typed_page_contents = CreateWebContents();
1350 tabstrip.AddWebContents(
1351 typed_page_contents, -1, ui::PAGE_TRANSITION_TYPED,
1352 TabStripModel::ADD_ACTIVE);
1354 EXPECT_EQ(2, tabstrip.count());
1356 // Re-select the home page.
1357 tabstrip.ActivateTabAt(0, true);
1359 // Open a new blank tab in the foreground.
1360 WebContents* new_blank_contents = CreateWebContents();
1361 tabstrip.AddWebContents(new_blank_contents, -1,
1362 ui::PAGE_TRANSITION_TYPED,
1363 TabStripModel::ADD_ACTIVE);
1365 // Verify the state of the tabstrip.
1366 EXPECT_EQ(3, tabstrip.count());
1367 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1368 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(1));
1369 EXPECT_EQ(new_blank_contents, tabstrip.GetWebContentsAt(2));
1371 // Now open a couple more blank tabs in the background.
1372 WebContents* background_blank_contents1 = CreateWebContents();
1373 tabstrip.AddWebContents(
1374 background_blank_contents1, -1, ui::PAGE_TRANSITION_TYPED,
1375 TabStripModel::ADD_NONE);
1376 WebContents* background_blank_contents2 = CreateWebContents();
1377 tabstrip.AddWebContents(
1378 background_blank_contents2, -1, ui::PAGE_TRANSITION_GENERATED,
1379 TabStripModel::ADD_NONE);
1380 EXPECT_EQ(5, tabstrip.count());
1381 EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1382 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(1));
1383 EXPECT_EQ(new_blank_contents, tabstrip.GetWebContentsAt(2));
1384 EXPECT_EQ(background_blank_contents1, tabstrip.GetWebContentsAt(3));
1385 EXPECT_EQ(background_blank_contents2, tabstrip.GetWebContentsAt(4));
1387 tabstrip.CloseAllTabs();
1388 EXPECT_TRUE(tabstrip.empty());
1391 // Tests whether opener state is correctly forgotten when the user switches
1393 TEST_F(TabStripModelTest, AddWebContents_ForgetOpeners) {
1394 TabStripDummyDelegate delegate;
1395 TabStripModel tabstrip(&delegate, profile());
1396 EXPECT_TRUE(tabstrip.empty());
1398 // Open the Home Page
1399 WebContents* homepage_contents = CreateWebContents();
1400 tabstrip.AddWebContents(
1401 homepage_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1402 TabStripModel::ADD_ACTIVE);
1404 // Open some other tab, by user typing.
1405 WebContents* typed_page_contents = CreateWebContents();
1406 tabstrip.AddWebContents(
1407 typed_page_contents, -1, ui::PAGE_TRANSITION_TYPED,
1408 TabStripModel::ADD_ACTIVE);
1410 EXPECT_EQ(2, tabstrip.count());
1412 // Re-select the home page.
1413 tabstrip.ActivateTabAt(0, true);
1415 // Open a bunch of tabs by simulating middle clicking on links on the home
1417 WebContents* middle_click_contents1 = CreateWebContents();
1418 tabstrip.AddWebContents(
1419 middle_click_contents1, -1, ui::PAGE_TRANSITION_LINK,
1420 TabStripModel::ADD_NONE);
1421 WebContents* middle_click_contents2 = CreateWebContents();
1422 tabstrip.AddWebContents(
1423 middle_click_contents2, -1, ui::PAGE_TRANSITION_LINK,
1424 TabStripModel::ADD_NONE);
1425 WebContents* middle_click_contents3 = CreateWebContents();
1426 tabstrip.AddWebContents(
1427 middle_click_contents3, -1, ui::PAGE_TRANSITION_LINK,
1428 TabStripModel::ADD_NONE);
1430 // Break out of the context by selecting a tab in a different context.
1431 EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(4));
1432 tabstrip.SelectLastTab();
1433 EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1435 // Step back into the context by selecting a tab inside it.
1436 tabstrip.ActivateTabAt(2, true);
1437 EXPECT_EQ(middle_click_contents2, tabstrip.GetActiveWebContents());
1439 // Now test that closing tabs selects to the right until there are no more,
1440 // then to the left, as if there were no context (context has been
1441 // successfully forgotten).
1442 tabstrip.CloseSelectedTabs();
1443 EXPECT_EQ(middle_click_contents3, tabstrip.GetActiveWebContents());
1444 tabstrip.CloseSelectedTabs();
1445 EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1446 tabstrip.CloseSelectedTabs();
1447 EXPECT_EQ(middle_click_contents1, tabstrip.GetActiveWebContents());
1448 tabstrip.CloseSelectedTabs();
1449 EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1451 EXPECT_EQ(1, tabstrip.count());
1453 tabstrip.CloseAllTabs();
1454 EXPECT_TRUE(tabstrip.empty());
1457 // Added for http://b/issue?id=958960
1458 TEST_F(TabStripModelTest, AppendContentsReselectionTest) {
1459 TabStripDummyDelegate delegate;
1460 TabStripModel tabstrip(&delegate, profile());
1461 EXPECT_TRUE(tabstrip.empty());
1463 // Open the Home Page.
1464 WebContents* homepage_contents = CreateWebContents();
1465 tabstrip.AddWebContents(
1466 homepage_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1467 TabStripModel::ADD_ACTIVE);
1469 // Open some other tab, by user typing.
1470 WebContents* typed_page_contents = CreateWebContents();
1471 tabstrip.AddWebContents(
1472 typed_page_contents, -1, ui::PAGE_TRANSITION_TYPED,
1473 TabStripModel::ADD_NONE);
1475 // The selected tab should still be the first.
1476 EXPECT_EQ(0, tabstrip.active_index());
1478 // Now simulate a link click that opens a new tab (by virtue of target=_blank)
1479 // and make sure the correct tab gets selected when the new tab is closed.
1480 WebContents* target_blank = CreateWebContents();
1481 tabstrip.AppendWebContents(target_blank, true);
1482 EXPECT_EQ(2, tabstrip.active_index());
1483 tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
1484 EXPECT_EQ(0, tabstrip.active_index());
1486 // Clean up after ourselves.
1487 tabstrip.CloseAllTabs();
1490 // Added for http://b/issue?id=1027661
1491 TEST_F(TabStripModelTest, ReselectionConsidersChildrenTest) {
1492 TabStripDummyDelegate delegate;
1493 TabStripModel strip(&delegate, profile());
1496 WebContents* page_a_contents = CreateWebContents();
1497 strip.AddWebContents(
1498 page_a_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1499 TabStripModel::ADD_ACTIVE);
1501 // Simulate middle click to open page A.A and A.B
1502 WebContents* page_a_a_contents = CreateWebContents();
1503 strip.AddWebContents(page_a_a_contents, -1, ui::PAGE_TRANSITION_LINK,
1504 TabStripModel::ADD_NONE);
1505 WebContents* page_a_b_contents = CreateWebContents();
1506 strip.AddWebContents(page_a_b_contents, -1, ui::PAGE_TRANSITION_LINK,
1507 TabStripModel::ADD_NONE);
1510 strip.ActivateTabAt(1, true);
1511 EXPECT_EQ(page_a_a_contents, strip.GetActiveWebContents());
1513 // Simulate a middle click to open page A.A.A
1514 WebContents* page_a_a_a_contents = CreateWebContents();
1515 strip.AddWebContents(page_a_a_a_contents, -1, ui::PAGE_TRANSITION_LINK,
1516 TabStripModel::ADD_NONE);
1518 EXPECT_EQ(page_a_a_a_contents, strip.GetWebContentsAt(2));
1521 strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1523 // Page A.A.A should be selected, NOT A.B
1524 EXPECT_EQ(page_a_a_a_contents, strip.GetActiveWebContents());
1527 strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1529 // Page A.B should be selected
1530 EXPECT_EQ(page_a_b_contents, strip.GetActiveWebContents());
1533 strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1535 // Page A should be selected
1536 EXPECT_EQ(page_a_contents, strip.GetActiveWebContents());
1539 strip.CloseAllTabs();
1542 TEST_F(TabStripModelTest, AddWebContents_NewTabAtEndOfStripInheritsGroup) {
1543 TabStripDummyDelegate delegate;
1544 TabStripModel strip(&delegate, profile());
1547 WebContents* page_a_contents = CreateWebContents();
1548 strip.AddWebContents(page_a_contents, -1,
1549 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1550 TabStripModel::ADD_ACTIVE);
1552 // Open pages B, C and D in the background from links on page A...
1553 WebContents* page_b_contents = CreateWebContents();
1554 WebContents* page_c_contents = CreateWebContents();
1555 WebContents* page_d_contents = CreateWebContents();
1556 strip.AddWebContents(page_b_contents, -1, ui::PAGE_TRANSITION_LINK,
1557 TabStripModel::ADD_NONE);
1558 strip.AddWebContents(page_c_contents, -1, ui::PAGE_TRANSITION_LINK,
1559 TabStripModel::ADD_NONE);
1560 strip.AddWebContents(page_d_contents, -1, ui::PAGE_TRANSITION_LINK,
1561 TabStripModel::ADD_NONE);
1563 // Switch to page B's tab.
1564 strip.ActivateTabAt(1, true);
1566 // Open a New Tab at the end of the strip (simulate Ctrl+T)
1567 WebContents* new_contents = CreateWebContents();
1568 strip.AddWebContents(new_contents, -1, ui::PAGE_TRANSITION_TYPED,
1569 TabStripModel::ADD_ACTIVE);
1571 EXPECT_EQ(4, strip.GetIndexOfWebContents(new_contents));
1572 EXPECT_EQ(4, strip.active_index());
1574 // Close the New Tab that was just opened. We should be returned to page B's
1576 strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1578 EXPECT_EQ(1, strip.active_index());
1580 // Open a non-New Tab tab at the end of the strip, with a TYPED transition.
1581 // This is like typing a URL in the address bar and pressing Alt+Enter. The
1582 // behavior should be the same as above.
1583 WebContents* page_e_contents = CreateWebContents();
1584 strip.AddWebContents(page_e_contents, -1, ui::PAGE_TRANSITION_TYPED,
1585 TabStripModel::ADD_ACTIVE);
1587 EXPECT_EQ(4, strip.GetIndexOfWebContents(page_e_contents));
1588 EXPECT_EQ(4, strip.active_index());
1590 // Close the Tab. Selection should shift back to page B's Tab.
1591 strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1593 EXPECT_EQ(1, strip.active_index());
1595 // Open a non-New Tab tab at the end of the strip, with some other
1596 // transition. This is like right clicking on a bookmark and choosing "Open
1597 // in New Tab". No opener relationship should be preserved between this Tab
1598 // and the one that was active when the gesture was performed.
1599 WebContents* page_f_contents = CreateWebContents();
1600 strip.AddWebContents(page_f_contents, -1,
1601 ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1602 TabStripModel::ADD_ACTIVE);
1604 EXPECT_EQ(4, strip.GetIndexOfWebContents(page_f_contents));
1605 EXPECT_EQ(4, strip.active_index());
1607 // Close the Tab. The next-adjacent should be selected.
1608 strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1610 EXPECT_EQ(3, strip.active_index());
1613 strip.CloseAllTabs();
1616 // A test of navigations in a tab that is part of a group of opened from some
1617 // parent tab. If the navigations are link clicks, the group relationship of
1618 // the tab to its parent are preserved. If they are of any other type, they are
1620 TEST_F(TabStripModelTest, NavigationForgetsOpeners) {
1621 TabStripDummyDelegate delegate;
1622 TabStripModel strip(&delegate, profile());
1625 WebContents* page_a_contents = CreateWebContents();
1626 strip.AddWebContents(page_a_contents, -1,
1627 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1628 TabStripModel::ADD_ACTIVE);
1630 // Open pages B, C and D in the background from links on page A...
1631 WebContents* page_b_contents = CreateWebContents();
1632 WebContents* page_c_contents = CreateWebContents();
1633 WebContents* page_d_contents = CreateWebContents();
1634 strip.AddWebContents(page_b_contents, -1, ui::PAGE_TRANSITION_LINK,
1635 TabStripModel::ADD_NONE);
1636 strip.AddWebContents(page_c_contents, -1, ui::PAGE_TRANSITION_LINK,
1637 TabStripModel::ADD_NONE);
1638 strip.AddWebContents(page_d_contents, -1, ui::PAGE_TRANSITION_LINK,
1639 TabStripModel::ADD_NONE);
1641 // Open page E in a different opener group from page A.
1642 WebContents* page_e_contents = CreateWebContents();
1643 strip.AddWebContents(page_e_contents, -1,
1644 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1645 TabStripModel::ADD_NONE);
1647 // Tell the TabStripModel that we are navigating page D via a link click.
1648 strip.ActivateTabAt(3, true);
1649 strip.TabNavigating(page_d_contents, ui::PAGE_TRANSITION_LINK);
1651 // Close page D, page C should be selected. (part of same group).
1652 strip.CloseWebContentsAt(3, TabStripModel::CLOSE_NONE);
1653 EXPECT_EQ(2, strip.active_index());
1655 // Tell the TabStripModel that we are navigating in page C via a bookmark.
1656 strip.TabNavigating(page_c_contents, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
1658 // Close page C, page E should be selected. (C is no longer part of the
1659 // A-B-C-D group, selection moves to the right).
1660 strip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
1661 EXPECT_EQ(page_e_contents, strip.GetWebContentsAt(strip.active_index()));
1663 strip.CloseAllTabs();
1666 // A test that the forgetting behavior tested in NavigationForgetsOpeners above
1667 // doesn't cause the opener relationship for a New Tab opened at the end of the
1668 // TabStrip to be reset (Test 1 below), unless another any other tab is
1669 // selected (Test 2 below).
1670 TEST_F(TabStripModelTest, NavigationForgettingDoesntAffectNewTab) {
1671 TabStripDummyDelegate delegate;
1672 TabStripModel strip(&delegate, profile());
1674 // Open a tab and several tabs from it, then select one of the tabs that was
1676 WebContents* page_a_contents = CreateWebContents();
1677 strip.AddWebContents(page_a_contents, -1,
1678 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1679 TabStripModel::ADD_ACTIVE);
1681 WebContents* page_b_contents = CreateWebContents();
1682 WebContents* page_c_contents = CreateWebContents();
1683 WebContents* page_d_contents = CreateWebContents();
1684 strip.AddWebContents(page_b_contents, -1, ui::PAGE_TRANSITION_LINK,
1685 TabStripModel::ADD_NONE);
1686 strip.AddWebContents(page_c_contents, -1, ui::PAGE_TRANSITION_LINK,
1687 TabStripModel::ADD_NONE);
1688 strip.AddWebContents(page_d_contents, -1, ui::PAGE_TRANSITION_LINK,
1689 TabStripModel::ADD_NONE);
1691 strip.ActivateTabAt(2, true);
1693 // TEST 1: If the user is in a group of tabs and opens a new tab at the end
1694 // of the strip, closing that new tab will select the tab that they were
1697 // Now simulate opening a new tab at the end of the TabStrip.
1698 WebContents* new_contents1 = CreateWebContents();
1699 strip.AddWebContents(new_contents1, -1, ui::PAGE_TRANSITION_TYPED,
1700 TabStripModel::ADD_ACTIVE);
1702 // At this point, if we close this tab the last selected one should be
1704 strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1705 EXPECT_EQ(page_c_contents, strip.GetWebContentsAt(strip.active_index()));
1707 // TEST 2: If the user is in a group of tabs and opens a new tab at the end
1708 // of the strip, selecting any other tab in the strip will cause that new
1709 // tab's opener relationship to be forgotten.
1711 // Open a new tab again.
1712 WebContents* new_contents2 = CreateWebContents();
1713 strip.AddWebContents(new_contents2, -1, ui::PAGE_TRANSITION_TYPED,
1714 TabStripModel::ADD_ACTIVE);
1716 // Now select the first tab.
1717 strip.ActivateTabAt(0, true);
1719 // Now select the last tab.
1720 strip.ActivateTabAt(strip.count() - 1, true);
1722 // Now close the last tab. The next adjacent should be selected.
1723 strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1724 EXPECT_EQ(page_d_contents, strip.GetWebContentsAt(strip.active_index()));
1726 strip.CloseAllTabs();
1729 // This fails on Linux when run with the rest of unit_tests (crbug.com/302156)
1730 // and fails consistently on Mac and Windows.
1731 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
1732 #define MAYBE_FastShutdown \
1733 DISABLED_FastShutdown
1735 #define MAYBE_FastShutdown \
1738 // Tests that fast shutdown is attempted appropriately.
1739 TEST_F(TabStripModelTest, MAYBE_FastShutdown) {
1740 TabStripDummyDelegate delegate;
1741 TabStripModel tabstrip(&delegate, profile());
1742 MockTabStripModelObserver observer(&tabstrip);
1743 tabstrip.AddObserver(&observer);
1745 EXPECT_TRUE(tabstrip.empty());
1747 // Make sure fast shutdown is attempted when tabs that share a RPH are shut
1750 WebContents* contents1 = CreateWebContents();
1751 WebContents* contents2 = CreateWebContentsWithSharedRPH(contents1);
1753 SetID(contents1, 1);
1754 SetID(contents2, 2);
1756 tabstrip.AppendWebContents(contents1, true);
1757 tabstrip.AppendWebContents(contents2, true);
1759 // Turn on the fake unload listener so the tabs don't actually get shut
1760 // down when we call CloseAllTabs()---we need to be able to check that
1761 // fast shutdown was attempted.
1762 delegate.set_run_unload_listener(true);
1763 tabstrip.CloseAllTabs();
1764 // On a mock RPH this checks whether we *attempted* fast shutdown.
1765 // A real RPH would reject our attempt since there is an unload handler.
1766 EXPECT_TRUE(contents1->GetRenderProcessHost()->FastShutdownStarted());
1767 EXPECT_EQ(2, tabstrip.count());
1769 delegate.set_run_unload_listener(false);
1770 tabstrip.CloseAllTabs();
1771 EXPECT_TRUE(tabstrip.empty());
1774 // Make sure fast shutdown is not attempted when only some tabs that share a
1775 // RPH are shut down.
1777 WebContents* contents1 = CreateWebContents();
1778 WebContents* contents2 = CreateWebContentsWithSharedRPH(contents1);
1780 SetID(contents1, 1);
1781 SetID(contents2, 2);
1783 tabstrip.AppendWebContents(contents1, true);
1784 tabstrip.AppendWebContents(contents2, true);
1786 tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
1787 EXPECT_FALSE(contents1->GetRenderProcessHost()->FastShutdownStarted());
1788 EXPECT_EQ(1, tabstrip.count());
1790 tabstrip.CloseAllTabs();
1791 EXPECT_TRUE(tabstrip.empty());
1795 // Tests various permutations of apps.
1796 TEST_F(TabStripModelTest, Apps) {
1797 TabStripDummyDelegate delegate;
1798 TabStripModel tabstrip(&delegate, profile());
1799 MockTabStripModelObserver observer(&tabstrip);
1800 tabstrip.AddObserver(&observer);
1802 EXPECT_TRUE(tabstrip.empty());
1804 typedef MockTabStripModelObserver::State State;
1807 base::FilePath path(FILE_PATH_LITERAL("c:\\foo"));
1808 #elif defined(OS_POSIX)
1809 base::FilePath path(FILE_PATH_LITERAL("/foo"));
1812 base::DictionaryValue manifest;
1813 manifest.SetString("name", "hi!");
1814 manifest.SetString("version", "1");
1815 manifest.SetString("app.launch.web_url", "http://www.google.com");
1817 scoped_refptr<Extension> extension_app(
1818 Extension::Create(path, extensions::Manifest::INVALID_LOCATION,
1819 manifest, Extension::NO_FLAGS, &error));
1820 WebContents* contents1 = CreateWebContents();
1821 extensions::TabHelper::CreateForWebContents(contents1);
1822 extensions::TabHelper::FromWebContents(contents1)
1823 ->SetExtensionApp(extension_app.get());
1824 WebContents* contents2 = CreateWebContents();
1825 extensions::TabHelper::CreateForWebContents(contents2);
1826 extensions::TabHelper::FromWebContents(contents2)
1827 ->SetExtensionApp(extension_app.get());
1828 WebContents* contents3 = CreateWebContents();
1830 SetID(contents1, 1);
1831 SetID(contents2, 2);
1832 SetID(contents3, 3);
1834 // Note! The ordering of these tests is important, each subsequent test
1835 // builds on the state established in the previous. This is important if you
1836 // ever insert tests rather than append.
1838 // Initial state, tab3 only and selected.
1839 tabstrip.AppendWebContents(contents3, true);
1841 observer.ClearStates();
1843 // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal
1844 // position and tab1 should end up at position 0.
1846 tabstrip.InsertWebContentsAt(1, contents1, TabStripModel::ADD_NONE);
1848 ASSERT_EQ(1, observer.GetStateCount());
1849 State state(contents1, 0, MockTabStripModelObserver::INSERT);
1850 EXPECT_TRUE(observer.StateEquals(0, state));
1852 // And verify the state.
1853 EXPECT_EQ("1ap 3", GetTabStripStateString(tabstrip));
1855 observer.ClearStates();
1858 // Insert tab 2 at position 1.
1860 tabstrip.InsertWebContentsAt(1, contents2, TabStripModel::ADD_NONE);
1862 ASSERT_EQ(1, observer.GetStateCount());
1863 State state(contents2, 1, MockTabStripModelObserver::INSERT);
1864 EXPECT_TRUE(observer.StateEquals(0, state));
1866 // And verify the state.
1867 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1869 observer.ClearStates();
1872 // Try to move tab 3 to position 0. This isn't legal and should be ignored.
1874 tabstrip.MoveWebContentsAt(2, 0, false);
1876 ASSERT_EQ(0, observer.GetStateCount());
1878 // And verify the state didn't change.
1879 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1881 observer.ClearStates();
1884 // Try to move tab 0 to position 3. This isn't legal and should be ignored.
1886 tabstrip.MoveWebContentsAt(0, 2, false);
1888 ASSERT_EQ(0, observer.GetStateCount());
1890 // And verify the state didn't change.
1891 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1893 observer.ClearStates();
1896 // Try to move tab 0 to position 1. This is a legal move.
1898 tabstrip.MoveWebContentsAt(0, 1, false);
1900 ASSERT_EQ(1, observer.GetStateCount());
1901 State state(contents1, 1, MockTabStripModelObserver::MOVE);
1902 state.src_index = 0;
1903 EXPECT_TRUE(observer.StateEquals(0, state));
1905 // And verify the state didn't change.
1906 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip));
1908 observer.ClearStates();
1911 // Remove tab3 and insert at position 0. It should be forced to position 2.
1913 tabstrip.DetachWebContentsAt(2);
1914 observer.ClearStates();
1916 tabstrip.InsertWebContentsAt(0, contents3, TabStripModel::ADD_NONE);
1918 ASSERT_EQ(1, observer.GetStateCount());
1919 State state(contents3, 2, MockTabStripModelObserver::INSERT);
1920 EXPECT_TRUE(observer.StateEquals(0, state));
1922 // And verify the state didn't change.
1923 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip));
1925 observer.ClearStates();
1928 tabstrip.CloseAllTabs();
1931 // Tests various permutations of pinning tabs.
1932 TEST_F(TabStripModelTest, Pinning) {
1933 TabStripDummyDelegate delegate;
1934 TabStripModel tabstrip(&delegate, profile());
1935 MockTabStripModelObserver observer(&tabstrip);
1936 tabstrip.AddObserver(&observer);
1938 EXPECT_TRUE(tabstrip.empty());
1940 typedef MockTabStripModelObserver::State State;
1942 WebContents* contents1 = CreateWebContents();
1943 WebContents* contents2 = CreateWebContents();
1944 WebContents* contents3 = CreateWebContents();
1946 SetID(contents1, 1);
1947 SetID(contents2, 2);
1948 SetID(contents3, 3);
1950 // Note! The ordering of these tests is important, each subsequent test
1951 // builds on the state established in the previous. This is important if you
1952 // ever insert tests rather than append.
1954 // Initial state, three tabs, first selected.
1955 tabstrip.AppendWebContents(contents1, true);
1956 tabstrip.AppendWebContents(contents2, false);
1957 tabstrip.AppendWebContents(contents3, false);
1959 observer.ClearStates();
1961 // Pin the first tab, this shouldn't visually reorder anything.
1963 tabstrip.SetTabPinned(0, true);
1965 // As the order didn't change, we should get a pinned notification.
1966 ASSERT_EQ(1, observer.GetStateCount());
1967 State state(contents1, 0, MockTabStripModelObserver::PINNED);
1968 EXPECT_TRUE(observer.StateEquals(0, state));
1970 // And verify the state.
1971 EXPECT_EQ("1p 2 3", GetTabStripStateString(tabstrip));
1973 observer.ClearStates();
1976 // Unpin the first tab.
1978 tabstrip.SetTabPinned(0, false);
1980 // As the order didn't change, we should get a pinned notification.
1981 ASSERT_EQ(1, observer.GetStateCount());
1982 State state(contents1, 0, MockTabStripModelObserver::PINNED);
1983 EXPECT_TRUE(observer.StateEquals(0, state));
1985 // And verify the state.
1986 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
1988 observer.ClearStates();
1991 // Pin the 3rd tab, which should move it to the front.
1993 tabstrip.SetTabPinned(2, true);
1995 // The pinning should have resulted in a move and a pinned notification.
1996 ASSERT_EQ(2, observer.GetStateCount());
1997 State state(contents3, 0, MockTabStripModelObserver::MOVE);
1998 state.src_index = 2;
1999 EXPECT_TRUE(observer.StateEquals(0, state));
2001 state = State(contents3, 0, MockTabStripModelObserver::PINNED);
2002 EXPECT_TRUE(observer.StateEquals(1, state));
2004 // And verify the state.
2005 EXPECT_EQ("3p 1 2", GetTabStripStateString(tabstrip));
2007 observer.ClearStates();
2010 // Pin the tab "1", which shouldn't move anything.
2012 tabstrip.SetTabPinned(1, true);
2014 // As the order didn't change, we should get a pinned notification.
2015 ASSERT_EQ(1, observer.GetStateCount());
2016 State state(contents1, 1, MockTabStripModelObserver::PINNED);
2017 EXPECT_TRUE(observer.StateEquals(0, state));
2019 // And verify the state.
2020 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip));
2022 observer.ClearStates();
2025 // Try to move tab "2" to the front, it should be ignored.
2027 tabstrip.MoveWebContentsAt(2, 0, false);
2029 // As the order didn't change, we should get a pinned notification.
2030 ASSERT_EQ(0, observer.GetStateCount());
2032 // And verify the state.
2033 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip));
2035 observer.ClearStates();
2038 // Unpin tab "3", which implicitly moves it to the end.
2040 tabstrip.SetTabPinned(0, false);
2042 ASSERT_EQ(2, observer.GetStateCount());
2043 State state(contents3, 1, MockTabStripModelObserver::MOVE);
2044 state.src_index = 0;
2045 EXPECT_TRUE(observer.StateEquals(0, state));
2047 state = State(contents3, 1, MockTabStripModelObserver::PINNED);
2048 EXPECT_TRUE(observer.StateEquals(1, state));
2050 // And verify the state.
2051 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip));
2053 observer.ClearStates();
2056 // Unpin tab "3", nothing should happen.
2058 tabstrip.SetTabPinned(1, false);
2060 ASSERT_EQ(0, observer.GetStateCount());
2062 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip));
2064 observer.ClearStates();
2069 tabstrip.SetTabPinned(0, true);
2070 tabstrip.SetTabPinned(1, true);
2072 EXPECT_EQ("1p 3p 2", GetTabStripStateString(tabstrip));
2074 observer.ClearStates();
2077 WebContents* contents4 = CreateWebContents();
2078 SetID(contents4, 4);
2080 // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end
2083 tabstrip.InsertWebContentsAt(1, contents4, TabStripModel::ADD_NONE);
2085 ASSERT_EQ(1, observer.GetStateCount());
2086 State state(contents4, 2, MockTabStripModelObserver::INSERT);
2087 EXPECT_TRUE(observer.StateEquals(0, state));
2089 EXPECT_EQ("1p 3p 4 2", GetTabStripStateString(tabstrip));
2092 tabstrip.CloseAllTabs();
2095 // Makes sure the TabStripModel calls the right observer methods during a
2097 TEST_F(TabStripModelTest, ReplaceSendsSelected) {
2098 typedef MockTabStripModelObserver::State State;
2100 TabStripDummyDelegate delegate;
2101 TabStripModel strip(&delegate, profile());
2103 WebContents* first_contents = CreateWebContents();
2104 strip.AddWebContents(first_contents, -1, ui::PAGE_TRANSITION_TYPED,
2105 TabStripModel::ADD_ACTIVE);
2107 MockTabStripModelObserver tabstrip_observer(&strip);
2108 strip.AddObserver(&tabstrip_observer);
2110 WebContents* new_contents = CreateWebContents();
2111 delete strip.ReplaceWebContentsAt(0, new_contents);
2113 ASSERT_EQ(2, tabstrip_observer.GetStateCount());
2115 // First event should be for replaced.
2116 State state(new_contents, 0, MockTabStripModelObserver::REPLACED);
2117 state.src_contents = first_contents;
2118 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
2120 // And the second for selected.
2121 state = State(new_contents, 0, MockTabStripModelObserver::ACTIVATE);
2122 state.src_contents = first_contents;
2123 state.change_reason = TabStripModelObserver::CHANGE_REASON_REPLACED;
2124 EXPECT_TRUE(tabstrip_observer.StateEquals(1, state));
2126 // Now add another tab and replace it, making sure we don't get a selected
2128 WebContents* third_contents = CreateWebContents();
2129 strip.AddWebContents(third_contents, 1, ui::PAGE_TRANSITION_TYPED,
2130 TabStripModel::ADD_NONE);
2132 tabstrip_observer.ClearStates();
2135 new_contents = CreateWebContents();
2136 delete strip.ReplaceWebContentsAt(1, new_contents);
2138 ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2140 state = State(new_contents, 1, MockTabStripModelObserver::REPLACED);
2141 state.src_contents = third_contents;
2142 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
2144 strip.CloseAllTabs();
2147 // Ensures discarding tabs leaves TabStripModel in a good state.
2148 TEST_F(TabStripModelTest, DiscardWebContentsAt) {
2149 typedef MockTabStripModelObserver::State State;
2151 TabStripDummyDelegate delegate;
2152 TabStripModel tabstrip(&delegate, profile());
2154 // Fill it with some tabs.
2155 WebContents* contents1 = CreateWebContents();
2156 tabstrip.AppendWebContents(contents1, true);
2157 WebContents* contents2 = CreateWebContents();
2158 tabstrip.AppendWebContents(contents2, true);
2160 // Start watching for events after the appends to avoid observing state
2161 // transitions that aren't relevant to this test.
2162 MockTabStripModelObserver tabstrip_observer(&tabstrip);
2163 tabstrip.AddObserver(&tabstrip_observer);
2165 // Discard one of the tabs.
2166 WebContents* null_contents1 = tabstrip.DiscardWebContentsAt(0);
2167 ASSERT_EQ(2, tabstrip.count());
2168 EXPECT_TRUE(tabstrip.IsTabDiscarded(0));
2169 EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2170 ASSERT_EQ(null_contents1, tabstrip.GetWebContentsAt(0));
2171 ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1));
2172 ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2173 State state1(null_contents1, 0, MockTabStripModelObserver::REPLACED);
2174 state1.src_contents = contents1;
2175 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state1));
2176 tabstrip_observer.ClearStates();
2178 // Discard the same tab again.
2179 WebContents* null_contents2 = tabstrip.DiscardWebContentsAt(0);
2180 ASSERT_EQ(2, tabstrip.count());
2181 EXPECT_TRUE(tabstrip.IsTabDiscarded(0));
2182 EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2183 ASSERT_EQ(null_contents2, tabstrip.GetWebContentsAt(0));
2184 ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1));
2185 ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2186 State state2(null_contents2, 0, MockTabStripModelObserver::REPLACED);
2187 state2.src_contents = null_contents1;
2188 EXPECT_TRUE(tabstrip_observer.StateEquals(0, state2));
2189 tabstrip_observer.ClearStates();
2191 // Activating the tab should clear its discard state.
2192 tabstrip.ActivateTabAt(0, true /* user_gesture */);
2193 ASSERT_EQ(2, tabstrip.count());
2194 EXPECT_FALSE(tabstrip.IsTabDiscarded(0));
2195 EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2197 // Don't discard active tab.
2198 tabstrip.DiscardWebContentsAt(0);
2199 ASSERT_EQ(2, tabstrip.count());
2200 EXPECT_FALSE(tabstrip.IsTabDiscarded(0));
2201 EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2203 tabstrip.CloseAllTabs();
2206 // Makes sure TabStripModel handles the case of deleting a tab while removing
2208 TEST_F(TabStripModelTest, DeleteFromDestroy) {
2209 TabStripDummyDelegate delegate;
2210 TabStripModel strip(&delegate, profile());
2211 WebContents* contents1 = CreateWebContents();
2212 WebContents* contents2 = CreateWebContents();
2213 MockTabStripModelObserver tab_strip_model_observer(&strip);
2214 strip.AppendWebContents(contents1, true);
2215 strip.AppendWebContents(contents2, true);
2216 // DeleteWebContentsOnDestroyedObserver deletes contents1 when contents2 sends
2217 // out notification that it is being destroyed.
2218 DeleteWebContentsOnDestroyedObserver observer(contents2, contents1, NULL);
2219 strip.AddObserver(&tab_strip_model_observer);
2220 strip.CloseAllTabs();
2222 int close_all_count = 0, close_all_canceled_count = 0;
2223 tab_strip_model_observer.GetCloseCounts(&close_all_count,
2224 &close_all_canceled_count);
2225 EXPECT_EQ(1, close_all_count);
2226 EXPECT_EQ(0, close_all_canceled_count);
2228 strip.RemoveObserver(&tab_strip_model_observer);
2231 // Makes sure TabStripModel handles the case of deleting another tab and the
2232 // TabStrip while removing another tab.
2233 TEST_F(TabStripModelTest, DeleteTabStripFromDestroy) {
2234 TabStripDummyDelegate delegate;
2235 TabStripModel* strip = new TabStripModel(&delegate, profile());
2236 MockTabStripModelObserver tab_strip_model_observer(strip);
2237 strip->AddObserver(&tab_strip_model_observer);
2238 WebContents* contents1 = CreateWebContents();
2239 WebContents* contents2 = CreateWebContents();
2240 strip->AppendWebContents(contents1, true);
2241 strip->AppendWebContents(contents2, true);
2242 // DeleteWebContentsOnDestroyedObserver deletes |contents1| and |strip| when
2243 // |contents2| sends out notification that it is being destroyed.
2244 DeleteWebContentsOnDestroyedObserver observer(contents2, contents1, strip);
2245 strip->CloseAllTabs();
2246 EXPECT_TRUE(tab_strip_model_observer.empty());
2247 EXPECT_TRUE(tab_strip_model_observer.deleted());
2250 TEST_F(TabStripModelTest, MoveSelectedTabsTo) {
2252 // Number of tabs the tab strip should have.
2253 const int tab_count;
2255 // Number of pinned tabs.
2256 const int pinned_count;
2258 // Index of the tabs to select.
2259 const std::string selected_tabs;
2261 // Index to move the tabs to.
2262 const int target_index;
2264 // Expected state after the move (space separated list of indices).
2265 const std::string state_after_move;
2268 { 2, 0, "0", 1, "1 0" },
2269 { 3, 0, "0", 2, "1 2 0" },
2270 { 3, 0, "2", 0, "2 0 1" },
2271 { 3, 0, "2", 1, "0 2 1" },
2272 { 3, 0, "0 1", 0, "0 1 2" },
2275 { 6, 0, "4 5", 1, "0 4 5 1 2 3" },
2276 { 3, 0, "0 1", 1, "2 0 1" },
2277 { 4, 0, "0 2", 1, "1 0 2 3" },
2278 { 6, 0, "0 1", 3, "2 3 4 0 1 5" },
2281 { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" },
2282 { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" },
2283 { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" },
2286 { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" },
2289 { 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" },
2291 // With pinned tabs.
2292 { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" },
2293 { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" },
2294 { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" },
2295 { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" },
2297 { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" },
2300 for (size_t i = 0; i < arraysize(test_data); ++i) {
2301 TabStripDummyDelegate delegate;
2302 TabStripModel strip(&delegate, profile());
2303 ASSERT_NO_FATAL_FAILURE(
2304 PrepareTabstripForSelectionTest(&strip, test_data[i].tab_count,
2305 test_data[i].pinned_count,
2306 test_data[i].selected_tabs));
2307 strip.MoveSelectedTabsTo(test_data[i].target_index);
2308 EXPECT_EQ(test_data[i].state_after_move,
2309 GetTabStripStateString(strip)) << i;
2310 strip.CloseAllTabs();
2314 // Tests that moving a tab forgets all groups referencing it.
2315 TEST_F(TabStripModelTest, MoveSelectedTabsTo_ForgetGroups) {
2316 TabStripDummyDelegate delegate;
2317 TabStripModel strip(&delegate, profile());
2319 // Open page A as a new tab and then A1 in the background from A.
2320 WebContents* page_a_contents = CreateWebContents();
2321 strip.AddWebContents(page_a_contents, -1,
2322 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
2323 TabStripModel::ADD_ACTIVE);
2324 WebContents* page_a1_contents = CreateWebContents();
2325 strip.AddWebContents(page_a1_contents, -1, ui::PAGE_TRANSITION_LINK,
2326 TabStripModel::ADD_NONE);
2328 // Likewise, open pages B and B1.
2329 WebContents* page_b_contents = CreateWebContents();
2330 strip.AddWebContents(page_b_contents, -1,
2331 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
2332 TabStripModel::ADD_ACTIVE);
2333 WebContents* page_b1_contents = CreateWebContents();
2334 strip.AddWebContents(page_b1_contents, -1, ui::PAGE_TRANSITION_LINK,
2335 TabStripModel::ADD_NONE);
2337 EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(0));
2338 EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(1));
2339 EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(2));
2340 EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(3));
2342 // Move page B to the start of the tab strip.
2343 strip.MoveSelectedTabsTo(0);
2345 // Open page B2 in the background from B. It should end up after B.
2346 WebContents* page_b2_contents = CreateWebContents();
2347 strip.AddWebContents(page_b2_contents, -1, ui::PAGE_TRANSITION_LINK,
2348 TabStripModel::ADD_NONE);
2349 EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(0));
2350 EXPECT_EQ(page_b2_contents, strip.GetWebContentsAt(1));
2351 EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(2));
2352 EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(3));
2353 EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(4));
2356 strip.ActivateTabAt(2, true);
2357 EXPECT_EQ(page_a_contents, strip.GetActiveWebContents());
2359 // Open page A2 in the background from A. It should end up after A1.
2360 WebContents* page_a2_contents = CreateWebContents();
2361 strip.AddWebContents(page_a2_contents, -1, ui::PAGE_TRANSITION_LINK,
2362 TabStripModel::ADD_NONE);
2363 EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(0));
2364 EXPECT_EQ(page_b2_contents, strip.GetWebContentsAt(1));
2365 EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(2));
2366 EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(3));
2367 EXPECT_EQ(page_a2_contents, strip.GetWebContentsAt(4));
2368 EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(5));
2370 strip.CloseAllTabs();
2373 TEST_F(TabStripModelTest, CloseSelectedTabs) {
2374 TabStripDummyDelegate delegate;
2375 TabStripModel strip(&delegate, profile());
2376 WebContents* contents1 = CreateWebContents();
2377 WebContents* contents2 = CreateWebContents();
2378 WebContents* contents3 = CreateWebContents();
2379 strip.AppendWebContents(contents1, true);
2380 strip.AppendWebContents(contents2, true);
2381 strip.AppendWebContents(contents3, true);
2382 strip.ToggleSelectionAt(1);
2383 strip.CloseSelectedTabs();
2384 EXPECT_EQ(1, strip.count());
2385 EXPECT_EQ(0, strip.active_index());
2386 strip.CloseAllTabs();
2389 TEST_F(TabStripModelTest, MultipleSelection) {
2390 typedef MockTabStripModelObserver::State State;
2392 TabStripDummyDelegate delegate;
2393 TabStripModel strip(&delegate, profile());
2394 MockTabStripModelObserver observer(&strip);
2395 WebContents* contents0 = CreateWebContents();
2396 WebContents* contents1 = CreateWebContents();
2397 WebContents* contents2 = CreateWebContents();
2398 WebContents* contents3 = CreateWebContents();
2399 strip.AppendWebContents(contents0, false);
2400 strip.AppendWebContents(contents1, false);
2401 strip.AppendWebContents(contents2, false);
2402 strip.AppendWebContents(contents3, false);
2403 strip.AddObserver(&observer);
2405 // Selection and active tab change.
2406 strip.ActivateTabAt(3, true);
2407 ASSERT_EQ(2, observer.GetStateCount());
2408 ASSERT_EQ(observer.GetStateAt(0).action,
2409 MockTabStripModelObserver::ACTIVATE);
2410 State s1(contents3, 3, MockTabStripModelObserver::SELECT);
2411 EXPECT_TRUE(observer.StateEquals(1, s1));
2412 observer.ClearStates();
2414 // Adding all tabs to selection, active tab is now at 0.
2415 strip.ExtendSelectionTo(0);
2416 ASSERT_EQ(3, observer.GetStateCount());
2417 ASSERT_EQ(observer.GetStateAt(0).action,
2418 MockTabStripModelObserver::DEACTIVATE);
2419 ASSERT_EQ(observer.GetStateAt(1).action,
2420 MockTabStripModelObserver::ACTIVATE);
2421 State s2(contents0, 0, MockTabStripModelObserver::SELECT);
2422 s2.src_contents = contents3;
2424 EXPECT_TRUE(observer.StateEquals(2, s2));
2425 observer.ClearStates();
2427 // Toggle the active tab, should make the next index active.
2428 strip.ToggleSelectionAt(0);
2429 EXPECT_EQ(1, strip.active_index());
2430 EXPECT_EQ(3U, strip.selection_model().size());
2431 EXPECT_EQ(4, strip.count());
2432 ASSERT_EQ(3, observer.GetStateCount());
2433 ASSERT_EQ(observer.GetStateAt(0).action,
2434 MockTabStripModelObserver::DEACTIVATE);
2435 ASSERT_EQ(observer.GetStateAt(1).action,
2436 MockTabStripModelObserver::ACTIVATE);
2437 ASSERT_EQ(observer.GetStateAt(2).action,
2438 MockTabStripModelObserver::SELECT);
2439 observer.ClearStates();
2441 // Toggle the first tab back to selected and active.
2442 strip.ToggleSelectionAt(0);
2443 EXPECT_EQ(0, strip.active_index());
2444 EXPECT_EQ(4U, strip.selection_model().size());
2445 EXPECT_EQ(4, strip.count());
2446 ASSERT_EQ(3, observer.GetStateCount());
2447 ASSERT_EQ(observer.GetStateAt(0).action,
2448 MockTabStripModelObserver::DEACTIVATE);
2449 ASSERT_EQ(observer.GetStateAt(1).action,
2450 MockTabStripModelObserver::ACTIVATE);
2451 ASSERT_EQ(observer.GetStateAt(2).action,
2452 MockTabStripModelObserver::SELECT);
2453 observer.ClearStates();
2455 // Closing one of the selected tabs, not the active one.
2456 strip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
2457 EXPECT_EQ(3, strip.count());
2458 ASSERT_EQ(3, observer.GetStateCount());
2459 ASSERT_EQ(observer.GetStateAt(0).action,
2460 MockTabStripModelObserver::CLOSE);
2461 ASSERT_EQ(observer.GetStateAt(1).action,
2462 MockTabStripModelObserver::DETACH);
2463 ASSERT_EQ(observer.GetStateAt(2).action,
2464 MockTabStripModelObserver::SELECT);
2465 observer.ClearStates();
2467 // Closing the active tab, while there are others tabs selected.
2468 strip.CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
2469 EXPECT_EQ(2, strip.count());
2470 ASSERT_EQ(5, observer.GetStateCount());
2471 ASSERT_EQ(observer.GetStateAt(0).action,
2472 MockTabStripModelObserver::CLOSE);
2473 ASSERT_EQ(observer.GetStateAt(1).action,
2474 MockTabStripModelObserver::DETACH);
2475 ASSERT_EQ(observer.GetStateAt(2).action,
2476 MockTabStripModelObserver::DEACTIVATE);
2477 ASSERT_EQ(observer.GetStateAt(3).action,
2478 MockTabStripModelObserver::ACTIVATE);
2479 ASSERT_EQ(observer.GetStateAt(4).action,
2480 MockTabStripModelObserver::SELECT);
2481 observer.ClearStates();
2483 // Active tab is at 0, deselecting all but the active tab.
2484 strip.ToggleSelectionAt(1);
2485 ASSERT_EQ(1, observer.GetStateCount());
2486 ASSERT_EQ(observer.GetStateAt(0).action,
2487 MockTabStripModelObserver::SELECT);
2488 observer.ClearStates();
2490 // Attempting to deselect the only selected and therefore active tab,
2491 // it is ignored (no notifications being sent) and tab at 0 remains selected
2493 strip.ToggleSelectionAt(0);
2494 ASSERT_EQ(0, observer.GetStateCount());
2496 strip.RemoveObserver(&observer);
2497 strip.CloseAllTabs();
2500 // Verifies that if we change the selection from a multi selection to a single
2501 // selection, but not in a way that changes the selected_index that
2502 // TabSelectionChanged is invoked.
2503 TEST_F(TabStripModelTest, MultipleToSingle) {
2504 typedef MockTabStripModelObserver::State State;
2506 TabStripDummyDelegate delegate;
2507 TabStripModel strip(&delegate, profile());
2508 WebContents* contents1 = CreateWebContents();
2509 WebContents* contents2 = CreateWebContents();
2510 strip.AppendWebContents(contents1, false);
2511 strip.AppendWebContents(contents2, false);
2512 strip.ToggleSelectionAt(0);
2513 strip.ToggleSelectionAt(1);
2515 MockTabStripModelObserver observer(&strip);
2516 strip.AddObserver(&observer);
2517 // This changes the selection (0 is no longer selected) but the selected_index
2518 // still remains at 1.
2519 strip.ActivateTabAt(1, true);
2520 ASSERT_EQ(1, observer.GetStateCount());
2521 State s(contents2, 1, MockTabStripModelObserver::SELECT);
2522 s.src_contents = contents2;
2524 s.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
2525 EXPECT_TRUE(observer.StateEquals(0, s));
2526 strip.RemoveObserver(&observer);
2527 strip.CloseAllTabs();
2530 // Verifies a newly inserted tab retains its previous blocked state.
2531 // http://crbug.com/276334
2532 TEST_F(TabStripModelTest, TabBlockedState) {
2533 // Start with a source tab strip.
2534 TabStripDummyDelegate dummy_tab_strip_delegate;
2535 TabStripModel strip_src(&dummy_tab_strip_delegate, profile());
2536 TabBlockedStateTestBrowser browser_src(&strip_src);
2539 WebContents* contents1 = CreateWebContents();
2540 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1);
2541 strip_src.AppendWebContents(contents1, false);
2544 WebContents* contents2 = CreateWebContents();
2545 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents2);
2546 strip_src.AppendWebContents(contents2, false);
2548 // Create a destination tab strip.
2549 TabStripModel strip_dst(&dummy_tab_strip_delegate, profile());
2550 TabBlockedStateTestBrowser browser_dst(&strip_dst);
2552 // Setup a SingleWebContentsDialogManager for tab |contents2|.
2553 web_modal::WebContentsModalDialogManager* modal_dialog_manager =
2554 web_modal::WebContentsModalDialogManager::FromWebContents(contents2);
2555 web_modal::PopupManager popup_manager(NULL);
2556 popup_manager.RegisterWith(contents2);
2558 // Show a dialog that blocks tab |contents2|.
2559 // DummySingleWebContentsDialogManager doesn't care about the
2560 // NativeWebContentsModalDialog value, so any dummy value works.
2561 DummySingleWebContentsDialogManager* native_manager =
2562 new DummySingleWebContentsDialogManager(
2563 reinterpret_cast<NativeWebContentsModalDialog>(0),
2564 modal_dialog_manager);
2565 modal_dialog_manager->ShowDialogWithManager(
2566 reinterpret_cast<NativeWebContentsModalDialog>(0),
2567 scoped_ptr<web_modal::SingleWebContentsDialogManager>(
2568 native_manager).Pass());
2569 EXPECT_TRUE(strip_src.IsTabBlocked(1));
2572 WebContents* moved_contents = strip_src.DetachWebContentsAt(1);
2573 EXPECT_EQ(contents2, moved_contents);
2575 // Attach the tab to the destination tab strip.
2576 strip_dst.AppendWebContents(moved_contents, true);
2577 EXPECT_TRUE(strip_dst.IsTabBlocked(0));
2579 strip_dst.CloseAllTabs();
2580 strip_src.CloseAllTabs();