1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/tabs/tab_strip_model.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/path_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/defaults.h"
19 #include "chrome/browser/extensions/tab_helper.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_tabstrip.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h"
25 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
26 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
27 #include "chrome/common/url_constants.h"
28 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
29 #include "chrome/test/base/testing_profile.h"
30 #include "components/web_modal/popup_manager.h"
31 #include "components/web_modal/web_contents_modal_dialog_manager.h"
32 #include "content/public/browser/navigation_controller.h"
33 #include "content/public/browser/navigation_entry.h"
34 #include "content/public/browser/render_process_host.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/browser/web_contents_observer.h"
37 #include "extensions/common/extension.h"
38 #include "testing/gtest/include/gtest/gtest.h"
40 using content::SiteInstance
;
41 using content::WebContents
;
42 using extensions::Extension
;
43 using web_modal::NativeWebContentsModalDialog
;
47 // Class used to delete a WebContents and TabStripModel when another WebContents
49 class DeleteWebContentsOnDestroyedObserver
50 : public content::WebContentsObserver
{
52 // When |source| is deleted both |tab_to_delete| and |tab_strip| are deleted.
53 // |tab_to_delete| and |tab_strip| may be NULL.
54 DeleteWebContentsOnDestroyedObserver(WebContents
* source
,
55 WebContents
* tab_to_delete
,
56 TabStripModel
* tab_strip
)
57 : WebContentsObserver(source
),
58 tab_to_delete_(tab_to_delete
),
59 tab_strip_(tab_strip
) {
62 virtual void WebContentsDestroyed() OVERRIDE
{
63 WebContents
* tab_to_delete
= tab_to_delete_
;
64 tab_to_delete_
= NULL
;
65 TabStripModel
* tab_strip_to_delete
= tab_strip_
;
68 delete tab_strip_to_delete
;
72 WebContents
* tab_to_delete_
;
73 TabStripModel
* tab_strip_
;
75 DISALLOW_COPY_AND_ASSIGN(DeleteWebContentsOnDestroyedObserver
);
78 class TabStripDummyDelegate
: public TestTabStripModelDelegate
{
80 TabStripDummyDelegate() : run_unload_(false) {}
81 virtual ~TabStripDummyDelegate() {}
83 void set_run_unload_listener(bool value
) { run_unload_
= value
; }
85 virtual bool RunUnloadListenerBeforeClosing(WebContents
* contents
) OVERRIDE
{
90 // Whether to report that we need to run an unload listener before closing.
93 DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate
);
96 const char kTabStripModelTestIDUserDataKey
[] = "TabStripModelTestIDUserData";
98 class TabStripModelTestIDUserData
: public base::SupportsUserData::Data
{
100 explicit TabStripModelTestIDUserData(int id
) : id_(id
) {}
101 virtual ~TabStripModelTestIDUserData() {}
102 int id() { return id_
; }
108 class DummySingleWebContentsDialogManager
109 : public web_modal::SingleWebContentsDialogManager
{
111 explicit DummySingleWebContentsDialogManager(
112 NativeWebContentsModalDialog dialog
,
113 web_modal::SingleWebContentsDialogManagerDelegate
* delegate
)
114 : delegate_(delegate
),
116 virtual ~DummySingleWebContentsDialogManager() {}
118 virtual void Show() OVERRIDE
{}
119 virtual void Hide() OVERRIDE
{}
120 virtual void Close() OVERRIDE
{
121 delegate_
->WillClose(dialog_
);
123 virtual void Focus() OVERRIDE
{}
124 virtual void Pulse() OVERRIDE
{}
125 virtual void HostChanged(
126 web_modal::WebContentsModalDialogHost
* new_host
) OVERRIDE
{}
127 virtual NativeWebContentsModalDialog
dialog() OVERRIDE
{ return dialog_
; }
130 web_modal::SingleWebContentsDialogManagerDelegate
* delegate_
;
131 NativeWebContentsModalDialog dialog_
;
133 DISALLOW_COPY_AND_ASSIGN(DummySingleWebContentsDialogManager
);
136 // Test Browser-like class for TabStripModelTest.TabBlockedState.
137 class TabBlockedStateTestBrowser
138 : public TabStripModelObserver
,
139 public web_modal::WebContentsModalDialogManagerDelegate
{
141 explicit TabBlockedStateTestBrowser(TabStripModel
* tab_strip_model
)
142 : tab_strip_model_(tab_strip_model
) {
143 tab_strip_model_
->AddObserver(this);
146 virtual ~TabBlockedStateTestBrowser() {
147 tab_strip_model_
->RemoveObserver(this);
151 // TabStripModelObserver
152 virtual void TabInsertedAt(WebContents
* contents
,
154 bool foreground
) OVERRIDE
{
155 web_modal::WebContentsModalDialogManager
* manager
=
156 web_modal::WebContentsModalDialogManager::FromWebContents(contents
);
158 manager
->SetDelegate(this);
161 // WebContentsModalDialogManagerDelegate
162 virtual void SetWebContentsBlocked(content::WebContents
* contents
,
163 bool blocked
) OVERRIDE
{
164 int index
= tab_strip_model_
->GetIndexOfWebContents(contents
);
166 tab_strip_model_
->SetTabBlocked(index
, blocked
);
169 TabStripModel
* tab_strip_model_
;
171 DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser
);
176 class TabStripModelTest
: public ChromeRenderViewHostTestHarness
{
178 WebContents
* CreateWebContents() {
179 return WebContents::Create(WebContents::CreateParams(profile()));
182 WebContents
* CreateWebContentsWithSharedRPH(WebContents
* web_contents
) {
183 WebContents::CreateParams
create_params(
184 profile(), web_contents
->GetRenderViewHost()->GetSiteInstance());
185 WebContents
* retval
= WebContents::Create(create_params
);
186 EXPECT_EQ(retval
->GetRenderProcessHost(),
187 web_contents
->GetRenderProcessHost());
191 // Sets the id of the specified contents.
192 void SetID(WebContents
* contents
, int id
) {
193 contents
->SetUserData(&kTabStripModelTestIDUserDataKey
,
194 new TabStripModelTestIDUserData(id
));
197 // Returns the id of the specified contents.
198 int GetID(WebContents
* contents
) {
199 TabStripModelTestIDUserData
* user_data
=
200 static_cast<TabStripModelTestIDUserData
*>(
201 contents
->GetUserData(&kTabStripModelTestIDUserDataKey
));
203 return user_data
? user_data
->id() : -1;
206 // Returns the state of the given tab strip as a string. The state consists
207 // of the ID of each web contents followed by a 'p' if pinned. For example,
208 // if the model consists of two tabs with ids 2 and 1, with the first
209 // tab pinned, this returns "2p 1".
210 std::string
GetTabStripStateString(const TabStripModel
& model
) {
212 for (int i
= 0; i
< model
.count(); ++i
) {
216 actual
+= base::IntToString(GetID(model
.GetWebContentsAt(i
)));
218 if (model
.IsAppTab(i
))
221 if (model
.IsTabPinned(i
))
227 std::string
GetIndicesClosedByCommandAsString(
228 const TabStripModel
& model
,
230 TabStripModel::ContextMenuCommand id
) const {
231 std::vector
<int> indices
= model
.GetIndicesClosedByCommand(index
, id
);
233 for (size_t i
= 0; i
< indices
.size(); ++i
) {
236 result
+= base::IntToString(indices
[i
]);
241 void PrepareTabstripForSelectionTest(TabStripModel
* model
,
244 const std::string
& selected_tabs
) {
245 for (int i
= 0; i
< tab_count
; ++i
) {
246 WebContents
* contents
= CreateWebContents();
248 model
->AppendWebContents(contents
, true);
250 for (int i
= 0; i
< pinned_count
; ++i
)
251 model
->SetTabPinned(i
, true);
253 ui::ListSelectionModel selection_model
;
254 std::vector
<std::string
> selection
;
255 base::SplitStringAlongWhitespace(selected_tabs
, &selection
);
256 for (size_t i
= 0; i
< selection
.size(); ++i
) {
258 ASSERT_TRUE(base::StringToInt(selection
[i
], &value
));
259 selection_model
.AddIndexToSelection(value
);
261 selection_model
.set_active(selection_model
.selected_indices()[0]);
262 model
->SetSelectionFromModel(selection_model
);
266 class MockTabStripModelObserver
: public TabStripModelObserver
{
268 explicit MockTabStripModelObserver(TabStripModel
* model
)
272 virtual ~MockTabStripModelObserver() {}
274 enum TabStripModelObserverAction
{
290 State(WebContents
* a_dst_contents
,
292 TabStripModelObserverAction a_action
)
293 : src_contents(NULL
),
294 dst_contents(a_dst_contents
),
296 dst_index(a_dst_index
),
297 change_reason(CHANGE_REASON_NONE
),
302 WebContents
* src_contents
;
303 WebContents
* dst_contents
;
308 TabStripModelObserverAction action
;
311 int GetStateCount() const {
312 return static_cast<int>(states_
.size());
315 // Returns (by way of parameters) the number of state's with CLOSE_ALL and
316 // CLOSE_ALL_CANCELED.
317 void GetCloseCounts(int* close_all_count
,
318 int* close_all_canceled_count
) {
319 *close_all_count
= *close_all_canceled_count
= 0;
320 for (int i
= 0; i
< GetStateCount(); ++i
) {
321 switch (GetStateAt(i
).action
) {
323 (*close_all_count
)++;
325 case CLOSE_ALL_CANCELED
:
326 (*close_all_canceled_count
)++;
334 const State
& GetStateAt(int index
) const {
335 DCHECK(index
>= 0 && index
< GetStateCount());
336 return states_
[index
];
339 bool StateEquals(int index
, const State
& state
) {
340 const State
& s
= GetStateAt(index
);
341 return (s
.src_contents
== state
.src_contents
&&
342 s
.dst_contents
== state
.dst_contents
&&
343 s
.src_index
== state
.src_index
&&
344 s
.dst_index
== state
.dst_index
&&
345 s
.change_reason
== state
.change_reason
&&
346 s
.foreground
== state
.foreground
&&
347 s
.action
== state
.action
);
350 // TabStripModelObserver implementation:
351 virtual void TabInsertedAt(WebContents
* contents
,
353 bool foreground
) OVERRIDE
{
355 State
s(contents
, index
, INSERT
);
356 s
.foreground
= foreground
;
357 states_
.push_back(s
);
359 virtual void ActiveTabChanged(WebContents
* old_contents
,
360 WebContents
* new_contents
,
362 int reason
) OVERRIDE
{
363 State
s(new_contents
, index
, ACTIVATE
);
364 s
.src_contents
= old_contents
;
365 s
.change_reason
= reason
;
366 states_
.push_back(s
);
368 virtual void TabSelectionChanged(
369 TabStripModel
* tab_strip_model
,
370 const ui::ListSelectionModel
& old_model
) OVERRIDE
{
371 State
s(model()->GetActiveWebContents(), model()->active_index(), SELECT
);
372 s
.src_contents
= model()->GetWebContentsAt(old_model
.active());
373 s
.src_index
= old_model
.active();
374 states_
.push_back(s
);
376 virtual void TabMoved(WebContents
* contents
,
378 int to_index
) OVERRIDE
{
379 State
s(contents
, to_index
, MOVE
);
380 s
.src_index
= from_index
;
381 states_
.push_back(s
);
384 virtual void TabClosingAt(TabStripModel
* tab_strip_model
,
385 WebContents
* contents
,
386 int index
) OVERRIDE
{
387 states_
.push_back(State(contents
, index
, CLOSE
));
389 virtual void TabDetachedAt(WebContents
* contents
, int index
) OVERRIDE
{
390 states_
.push_back(State(contents
, index
, DETACH
));
392 virtual void TabDeactivated(WebContents
* contents
) OVERRIDE
{
393 states_
.push_back(State(contents
, model()->active_index(), DEACTIVATE
));
395 virtual void TabChangedAt(WebContents
* contents
,
397 TabChangeType change_type
) OVERRIDE
{
398 states_
.push_back(State(contents
, index
, CHANGE
));
400 virtual void TabReplacedAt(TabStripModel
* tab_strip_model
,
401 WebContents
* old_contents
,
402 WebContents
* new_contents
,
403 int index
) OVERRIDE
{
404 State
s(new_contents
, index
, REPLACED
);
405 s
.src_contents
= old_contents
;
406 states_
.push_back(s
);
408 virtual void TabPinnedStateChanged(WebContents
* contents
,
409 int index
) OVERRIDE
{
410 states_
.push_back(State(contents
, index
, PINNED
));
412 virtual void TabStripEmpty() OVERRIDE
{
415 virtual void WillCloseAllTabs() OVERRIDE
{
416 states_
.push_back(State(NULL
, -1, CLOSE_ALL
));
418 virtual void CloseAllTabsCanceled() OVERRIDE
{
419 states_
.push_back(State(NULL
, -1, CLOSE_ALL_CANCELED
));
421 virtual void TabStripModelDeleted() OVERRIDE
{
429 bool empty() const { return empty_
; }
430 bool deleted() const { return deleted_
; }
431 TabStripModel
* model() { return model_
; }
434 std::vector
<State
> states_
;
438 TabStripModel
* model_
;
440 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver
);
443 TEST_F(TabStripModelTest
, TestBasicAPI
) {
444 TabStripDummyDelegate delegate
;
445 TabStripModel
tabstrip(&delegate
, profile());
446 MockTabStripModelObserver
observer(&tabstrip
);
447 tabstrip
.AddObserver(&observer
);
449 EXPECT_TRUE(tabstrip
.empty());
451 typedef MockTabStripModelObserver::State State
;
453 WebContents
* contents1
= CreateWebContents();
456 // Note! The ordering of these tests is important, each subsequent test
457 // builds on the state established in the previous. This is important if you
458 // ever insert tests rather than append.
460 // Test AppendWebContents, ContainsIndex
462 EXPECT_FALSE(tabstrip
.ContainsIndex(0));
463 tabstrip
.AppendWebContents(contents1
, true);
464 EXPECT_TRUE(tabstrip
.ContainsIndex(0));
465 EXPECT_EQ(1, tabstrip
.count());
466 EXPECT_EQ(3, observer
.GetStateCount());
467 State
s1(contents1
, 0, MockTabStripModelObserver::INSERT
);
468 s1
.foreground
= true;
469 EXPECT_TRUE(observer
.StateEquals(0, s1
));
470 State
s2(contents1
, 0, MockTabStripModelObserver::ACTIVATE
);
471 EXPECT_TRUE(observer
.StateEquals(1, s2
));
472 State
s3(contents1
, 0, MockTabStripModelObserver::SELECT
);
473 s3
.src_contents
= NULL
;
474 s3
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
475 EXPECT_TRUE(observer
.StateEquals(2, s3
));
476 observer
.ClearStates();
478 EXPECT_EQ("1", GetTabStripStateString(tabstrip
));
480 // Test InsertWebContentsAt, foreground tab.
481 WebContents
* contents2
= CreateWebContents();
484 tabstrip
.InsertWebContentsAt(1, contents2
, TabStripModel::ADD_ACTIVE
);
486 EXPECT_EQ(2, tabstrip
.count());
487 EXPECT_EQ(4, observer
.GetStateCount());
488 State
s1(contents2
, 1, MockTabStripModelObserver::INSERT
);
489 s1
.foreground
= true;
490 EXPECT_TRUE(observer
.StateEquals(0, s1
));
491 State
s2(contents1
, 0, MockTabStripModelObserver::DEACTIVATE
);
492 EXPECT_TRUE(observer
.StateEquals(1, s2
));
493 State
s3(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
494 s3
.src_contents
= contents1
;
495 EXPECT_TRUE(observer
.StateEquals(2, s3
));
496 State
s4(contents2
, 1, MockTabStripModelObserver::SELECT
);
497 s4
.src_contents
= contents1
;
499 EXPECT_TRUE(observer
.StateEquals(3, s4
));
500 observer
.ClearStates();
502 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip
));
504 // Test InsertWebContentsAt, background tab.
505 WebContents
* contents3
= CreateWebContents();
508 tabstrip
.InsertWebContentsAt(2, contents3
, TabStripModel::ADD_NONE
);
510 EXPECT_EQ(3, tabstrip
.count());
511 EXPECT_EQ(1, observer
.GetStateCount());
512 State
s1(contents3
, 2, MockTabStripModelObserver::INSERT
);
513 s1
.foreground
= false;
514 EXPECT_TRUE(observer
.StateEquals(0, s1
));
515 observer
.ClearStates();
517 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
519 // Test ActivateTabAt
521 tabstrip
.ActivateTabAt(2, true);
522 EXPECT_EQ(3, observer
.GetStateCount());
523 State
s1(contents2
, 1, MockTabStripModelObserver::DEACTIVATE
);
524 EXPECT_TRUE(observer
.StateEquals(0, s1
));
525 State
s2(contents3
, 2, MockTabStripModelObserver::ACTIVATE
);
526 s2
.src_contents
= contents2
;
527 s2
.change_reason
= TabStripModelObserver::CHANGE_REASON_USER_GESTURE
;
528 EXPECT_TRUE(observer
.StateEquals(1, s2
));
529 State
s3(contents3
, 2, MockTabStripModelObserver::SELECT
);
530 s3
.src_contents
= contents2
;
532 EXPECT_TRUE(observer
.StateEquals(2, s3
));
533 observer
.ClearStates();
535 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
537 // Test DetachWebContentsAt
540 WebContents
* detached
= tabstrip
.DetachWebContentsAt(2);
541 // ... and append again because we want this for later.
542 tabstrip
.AppendWebContents(detached
, true);
543 EXPECT_EQ(8, observer
.GetStateCount());
544 State
s1(detached
, 2, MockTabStripModelObserver::DETACH
);
545 EXPECT_TRUE(observer
.StateEquals(0, s1
));
546 State
s2(detached
, ui::ListSelectionModel::kUnselectedIndex
,
547 MockTabStripModelObserver::DEACTIVATE
);
548 EXPECT_TRUE(observer
.StateEquals(1, s2
));
549 State
s3(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
550 s3
.src_contents
= contents3
;
551 s3
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
552 EXPECT_TRUE(observer
.StateEquals(2, s3
));
553 State
s4(contents2
, 1, MockTabStripModelObserver::SELECT
);
554 s4
.src_contents
= NULL
;
555 s4
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
556 EXPECT_TRUE(observer
.StateEquals(3, s4
));
557 State
s5(detached
, 2, MockTabStripModelObserver::INSERT
);
558 s5
.foreground
= true;
559 EXPECT_TRUE(observer
.StateEquals(4, s5
));
560 State
s6(contents2
, 1, MockTabStripModelObserver::DEACTIVATE
);
561 EXPECT_TRUE(observer
.StateEquals(5, s6
));
562 State
s7(detached
, 2, MockTabStripModelObserver::ACTIVATE
);
563 s7
.src_contents
= contents2
;
564 s7
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
565 EXPECT_TRUE(observer
.StateEquals(6, s7
));
566 State
s8(detached
, 2, MockTabStripModelObserver::SELECT
);
567 s8
.src_contents
= contents2
;
569 EXPECT_TRUE(observer
.StateEquals(7, s8
));
570 observer
.ClearStates();
572 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
574 // Test CloseWebContentsAt
576 EXPECT_TRUE(tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
));
577 EXPECT_EQ(2, tabstrip
.count());
579 EXPECT_EQ(5, observer
.GetStateCount());
580 State
s1(contents3
, 2, MockTabStripModelObserver::CLOSE
);
581 EXPECT_TRUE(observer
.StateEquals(0, s1
));
582 State
s2(contents3
, 2, MockTabStripModelObserver::DETACH
);
583 EXPECT_TRUE(observer
.StateEquals(1, s2
));
584 State
s3(contents3
, ui::ListSelectionModel::kUnselectedIndex
,
585 MockTabStripModelObserver::DEACTIVATE
);
586 EXPECT_TRUE(observer
.StateEquals(2, s3
));
587 State
s4(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
588 s4
.src_contents
= contents3
;
589 s4
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
590 EXPECT_TRUE(observer
.StateEquals(3, s4
));
591 State
s5(contents2
, 1, MockTabStripModelObserver::SELECT
);
592 s5
.src_contents
= NULL
;
593 s5
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
594 EXPECT_TRUE(observer
.StateEquals(4, s5
));
595 observer
.ClearStates();
597 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip
));
599 // Test MoveWebContentsAt, select_after_move == true
601 tabstrip
.MoveWebContentsAt(1, 0, true);
603 EXPECT_EQ(1, observer
.GetStateCount());
604 State
s1(contents2
, 0, MockTabStripModelObserver::MOVE
);
606 EXPECT_TRUE(observer
.StateEquals(0, s1
));
607 EXPECT_EQ(0, tabstrip
.active_index());
608 observer
.ClearStates();
610 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip
));
612 // Test MoveWebContentsAt, select_after_move == false
614 tabstrip
.MoveWebContentsAt(1, 0, false);
615 EXPECT_EQ(1, observer
.GetStateCount());
616 State
s1(contents1
, 0, MockTabStripModelObserver::MOVE
);
618 EXPECT_TRUE(observer
.StateEquals(0, s1
));
619 EXPECT_EQ(1, tabstrip
.active_index());
621 tabstrip
.MoveWebContentsAt(0, 1, false);
622 observer
.ClearStates();
624 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip
));
628 EXPECT_EQ(contents2
, tabstrip
.GetActiveWebContents());
629 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(0));
630 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
631 EXPECT_EQ(0, tabstrip
.GetIndexOfWebContents(contents2
));
632 EXPECT_EQ(1, tabstrip
.GetIndexOfWebContents(contents1
));
635 // Test UpdateWebContentsStateAt
637 tabstrip
.UpdateWebContentsStateAt(0, TabStripModelObserver::ALL
);
638 EXPECT_EQ(1, observer
.GetStateCount());
639 State
s1(contents2
, 0, MockTabStripModelObserver::CHANGE
);
640 EXPECT_TRUE(observer
.StateEquals(0, s1
));
641 observer
.ClearStates();
644 // Test SelectNextTab, SelectPreviousTab, SelectLastTab
646 // Make sure the second of the two tabs is selected first...
647 tabstrip
.ActivateTabAt(1, true);
648 tabstrip
.SelectPreviousTab();
649 EXPECT_EQ(0, tabstrip
.active_index());
650 tabstrip
.SelectLastTab();
651 EXPECT_EQ(1, tabstrip
.active_index());
652 tabstrip
.SelectNextTab();
653 EXPECT_EQ(0, tabstrip
.active_index());
656 // Test CloseSelectedTabs
658 tabstrip
.CloseSelectedTabs();
659 // |CloseSelectedTabs| calls CloseWebContentsAt, we already tested that, now
660 // just verify that the count and selected index have changed
662 EXPECT_EQ(1, tabstrip
.count());
663 EXPECT_EQ(0, tabstrip
.active_index());
666 observer
.ClearStates();
667 tabstrip
.CloseAllTabs();
669 int close_all_count
= 0, close_all_canceled_count
= 0;
670 observer
.GetCloseCounts(&close_all_count
, &close_all_canceled_count
);
671 EXPECT_EQ(1, close_all_count
);
672 EXPECT_EQ(0, close_all_canceled_count
);
674 // TabStripModel should now be empty.
675 EXPECT_TRUE(tabstrip
.empty());
677 // Opener methods are tested below...
679 tabstrip
.RemoveObserver(&observer
);
682 TEST_F(TabStripModelTest
, TestBasicOpenerAPI
) {
683 TabStripDummyDelegate delegate
;
684 TabStripModel
tabstrip(&delegate
, profile());
685 EXPECT_TRUE(tabstrip
.empty());
687 // This is a basic test of opener functionality. opener is created
688 // as the first tab in the strip and then we create 5 other tabs in the
689 // background with opener set as their opener.
691 WebContents
* opener
= CreateWebContents();
692 tabstrip
.AppendWebContents(opener
, true);
693 WebContents
* contents1
= CreateWebContents();
694 WebContents
* contents2
= CreateWebContents();
695 WebContents
* contents3
= CreateWebContents();
696 WebContents
* contents4
= CreateWebContents();
697 WebContents
* contents5
= CreateWebContents();
699 // We use |InsertWebContentsAt| here instead of |AppendWebContents| so that
700 // openership relationships are preserved.
701 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents1
,
702 TabStripModel::ADD_INHERIT_GROUP
);
703 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents2
,
704 TabStripModel::ADD_INHERIT_GROUP
);
705 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents3
,
706 TabStripModel::ADD_INHERIT_GROUP
);
707 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents4
,
708 TabStripModel::ADD_INHERIT_GROUP
);
709 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents5
,
710 TabStripModel::ADD_INHERIT_GROUP
);
712 // All the tabs should have the same opener.
713 for (int i
= 1; i
< tabstrip
.count(); ++i
)
714 EXPECT_EQ(opener
, tabstrip
.GetOpenerOfWebContentsAt(i
));
716 // If there is a next adjacent item, then the index should be of that item.
717 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 1, false));
718 // If the last tab in the group is closed, the preceding tab in the same
719 // group should be selected.
720 EXPECT_EQ(4, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 5, false));
722 // Tests the method that finds the last tab opened by the same opener in the
723 // strip (this is the insertion index for the next background tab for the
724 // specified opener).
725 EXPECT_EQ(5, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
727 // For a tab that has opened no other tabs, the return value should always be
730 tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents1
, 3, false));
732 tabstrip
.GetIndexOfLastWebContentsOpenedBy(contents1
, 3));
734 // ForgetAllOpeners should destroy all opener relationships.
735 tabstrip
.ForgetAllOpeners();
736 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 1, false));
737 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 5, false));
738 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
740 // Specify the last tab as the opener of the others.
741 for (int i
= 0; i
< tabstrip
.count() - 1; ++i
)
742 tabstrip
.SetOpenerOfWebContentsAt(i
, contents5
);
744 for (int i
= 0; i
< tabstrip
.count() - 1; ++i
)
745 EXPECT_EQ(contents5
, tabstrip
.GetOpenerOfWebContentsAt(i
));
747 // If there is a next adjacent item, then the index should be of that item.
748 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents5
, 1, false));
750 // If the last tab in the group is closed, the preceding tab in the same
751 // group should be selected.
752 EXPECT_EQ(3, tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents5
, 4, false));
754 tabstrip
.CloseAllTabs();
755 EXPECT_TRUE(tabstrip
.empty());
758 static int GetInsertionIndex(TabStripModel
* tabstrip
) {
759 return tabstrip
->order_controller()->DetermineInsertionIndex(
760 content::PAGE_TRANSITION_LINK
, false);
763 static void InsertWebContentses(TabStripModel
* tabstrip
,
764 WebContents
* contents1
,
765 WebContents
* contents2
,
766 WebContents
* contents3
) {
767 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
769 TabStripModel::ADD_INHERIT_GROUP
);
770 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
772 TabStripModel::ADD_INHERIT_GROUP
);
773 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
775 TabStripModel::ADD_INHERIT_GROUP
);
778 // Tests opening background tabs.
779 TEST_F(TabStripModelTest
, TestLTRInsertionOptions
) {
780 TabStripDummyDelegate delegate
;
781 TabStripModel
tabstrip(&delegate
, profile());
782 EXPECT_TRUE(tabstrip
.empty());
784 WebContents
* opener
= CreateWebContents();
785 tabstrip
.AppendWebContents(opener
, true);
787 WebContents
* contents1
= CreateWebContents();
788 WebContents
* contents2
= CreateWebContents();
789 WebContents
* contents3
= CreateWebContents();
792 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
793 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
794 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(2));
795 EXPECT_EQ(contents3
, tabstrip
.GetWebContentsAt(3));
797 tabstrip
.CloseAllTabs();
798 EXPECT_TRUE(tabstrip
.empty());
801 // This test constructs a tabstrip, and then simulates loading several tabs in
802 // the background from link clicks on the first tab. Then it simulates opening
803 // a new tab from the first tab in the foreground via a link click, verifies
804 // that this tab is opened adjacent to the opener, then closes it.
805 // Finally it tests that a tab opened for some non-link purpose opens at the
806 // end of the strip, not bundled to any existing context.
807 TEST_F(TabStripModelTest
, TestInsertionIndexDetermination
) {
808 TabStripDummyDelegate delegate
;
809 TabStripModel
tabstrip(&delegate
, profile());
810 EXPECT_TRUE(tabstrip
.empty());
812 WebContents
* opener
= CreateWebContents();
813 tabstrip
.AppendWebContents(opener
, true);
815 // Open some other random unrelated tab in the background to monkey with our
817 WebContents
* other
= CreateWebContents();
818 tabstrip
.AppendWebContents(other
, false);
820 WebContents
* contents1
= CreateWebContents();
821 WebContents
* contents2
= CreateWebContents();
822 WebContents
* contents3
= CreateWebContents();
824 // Start by testing LTR.
825 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
826 EXPECT_EQ(opener
, tabstrip
.GetWebContentsAt(0));
827 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
828 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(2));
829 EXPECT_EQ(contents3
, tabstrip
.GetWebContentsAt(3));
830 EXPECT_EQ(other
, tabstrip
.GetWebContentsAt(4));
832 // The opener API should work...
833 EXPECT_EQ(3, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 2, false));
834 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
835 EXPECT_EQ(3, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
837 // Now open a foreground tab from a link. It should be opened adjacent to the
839 WebContents
* fg_link_contents
= CreateWebContents();
840 int insert_index
= tabstrip
.order_controller()->DetermineInsertionIndex(
841 content::PAGE_TRANSITION_LINK
, true);
842 EXPECT_EQ(1, insert_index
);
843 tabstrip
.InsertWebContentsAt(insert_index
, fg_link_contents
,
844 TabStripModel::ADD_ACTIVE
|
845 TabStripModel::ADD_INHERIT_GROUP
);
846 EXPECT_EQ(1, tabstrip
.active_index());
847 EXPECT_EQ(fg_link_contents
, tabstrip
.GetActiveWebContents());
849 // Now close this contents. The selection should move to the opener contents.
850 tabstrip
.CloseSelectedTabs();
851 EXPECT_EQ(0, tabstrip
.active_index());
853 // Now open a new empty tab. It should open at the end of the strip.
854 WebContents
* fg_nonlink_contents
= CreateWebContents();
855 insert_index
= tabstrip
.order_controller()->DetermineInsertionIndex(
856 content::PAGE_TRANSITION_AUTO_BOOKMARK
, true);
857 EXPECT_EQ(tabstrip
.count(), insert_index
);
858 // We break the opener relationship...
859 tabstrip
.InsertWebContentsAt(insert_index
,
861 TabStripModel::ADD_NONE
);
862 // Now select it, so that user_gesture == true causes the opener relationship
863 // to be forgotten...
864 tabstrip
.ActivateTabAt(tabstrip
.count() - 1, true);
865 EXPECT_EQ(tabstrip
.count() - 1, tabstrip
.active_index());
866 EXPECT_EQ(fg_nonlink_contents
, tabstrip
.GetActiveWebContents());
868 // Verify that all opener relationships are forgotten.
869 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 2, false));
870 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
871 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
872 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
874 tabstrip
.CloseAllTabs();
875 EXPECT_TRUE(tabstrip
.empty());
878 // Tests that selection is shifted to the correct tab when a tab is closed.
879 // If a tab is in the background when it is closed, the selection does not
881 // If a tab is in the foreground (selected),
882 // If that tab does not have an opener, selection shifts to the right.
883 // If the tab has an opener,
884 // The next tab (scanning LTR) in the entire strip that has the same opener
886 // If there are no other tabs that have the same opener,
887 // The opener is selected
889 TEST_F(TabStripModelTest
, TestSelectOnClose
) {
890 TabStripDummyDelegate delegate
;
891 TabStripModel
tabstrip(&delegate
, profile());
892 EXPECT_TRUE(tabstrip
.empty());
894 WebContents
* opener
= CreateWebContents();
895 tabstrip
.AppendWebContents(opener
, true);
897 WebContents
* contents1
= CreateWebContents();
898 WebContents
* contents2
= CreateWebContents();
899 WebContents
* contents3
= CreateWebContents();
901 // Note that we use Detach instead of Close throughout this test to avoid
902 // having to keep reconstructing these WebContentses.
904 // First test that closing tabs that are in the background doesn't adjust the
905 // current selection.
906 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
907 EXPECT_EQ(0, tabstrip
.active_index());
909 tabstrip
.DetachWebContentsAt(1);
910 EXPECT_EQ(0, tabstrip
.active_index());
912 for (int i
= tabstrip
.count() - 1; i
>= 1; --i
)
913 tabstrip
.DetachWebContentsAt(i
);
915 // Now test that when a tab doesn't have an opener, selection shifts to the
916 // right when the tab is closed.
917 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
918 EXPECT_EQ(0, tabstrip
.active_index());
920 tabstrip
.ForgetAllOpeners();
921 tabstrip
.ActivateTabAt(1, true);
922 EXPECT_EQ(1, tabstrip
.active_index());
923 tabstrip
.DetachWebContentsAt(1);
924 EXPECT_EQ(1, tabstrip
.active_index());
925 tabstrip
.DetachWebContentsAt(1);
926 EXPECT_EQ(1, tabstrip
.active_index());
927 tabstrip
.DetachWebContentsAt(1);
928 EXPECT_EQ(0, tabstrip
.active_index());
930 for (int i
= tabstrip
.count() - 1; i
>= 1; --i
)
931 tabstrip
.DetachWebContentsAt(i
);
933 // Now test that when a tab does have an opener, it selects the next tab
934 // opened by the same opener scanning LTR when it is closed.
935 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
936 EXPECT_EQ(0, tabstrip
.active_index());
937 tabstrip
.ActivateTabAt(2, false);
938 EXPECT_EQ(2, tabstrip
.active_index());
939 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
940 EXPECT_EQ(2, tabstrip
.active_index());
941 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
942 EXPECT_EQ(1, tabstrip
.active_index());
943 tabstrip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
944 EXPECT_EQ(0, tabstrip
.active_index());
945 // Finally test that when a tab has no "siblings" that the opener is
947 WebContents
* other_contents
= CreateWebContents();
948 tabstrip
.InsertWebContentsAt(1, other_contents
,
949 TabStripModel::ADD_NONE
);
950 EXPECT_EQ(2, tabstrip
.count());
951 WebContents
* opened_contents
= CreateWebContents();
952 tabstrip
.InsertWebContentsAt(2, opened_contents
,
953 TabStripModel::ADD_ACTIVE
|
954 TabStripModel::ADD_INHERIT_GROUP
);
955 EXPECT_EQ(2, tabstrip
.active_index());
956 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
957 EXPECT_EQ(0, tabstrip
.active_index());
959 tabstrip
.CloseAllTabs();
960 EXPECT_TRUE(tabstrip
.empty());
963 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
965 TEST_F(TabStripModelTest
, CommandCloseTab
) {
966 TabStripDummyDelegate delegate
;
967 TabStripModel
tabstrip(&delegate
, profile());
968 EXPECT_TRUE(tabstrip
.empty());
970 // Make sure can_close is honored.
971 ASSERT_NO_FATAL_FAILURE(
972 PrepareTabstripForSelectionTest(&tabstrip
, 1, 0, "0"));
973 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
974 0, TabStripModel::CommandCloseTab
));
975 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
976 ASSERT_TRUE(tabstrip
.empty());
978 // Make sure close on a tab that is selected affects all the selected tabs.
979 ASSERT_NO_FATAL_FAILURE(
980 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
981 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
982 0, TabStripModel::CommandCloseTab
));
983 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
984 // Should have closed tabs 0 and 1.
985 EXPECT_EQ("2", GetTabStripStateString(tabstrip
));
987 tabstrip
.CloseAllTabs();
988 EXPECT_TRUE(tabstrip
.empty());
990 // Select two tabs and make close on a tab that isn't selected doesn't affect
992 ASSERT_NO_FATAL_FAILURE(
993 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
994 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
995 2, TabStripModel::CommandCloseTab
));
996 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab
);
997 // Should have closed tab 2.
998 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
999 tabstrip
.CloseAllTabs();
1000 EXPECT_TRUE(tabstrip
.empty());
1002 // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned.
1003 ASSERT_NO_FATAL_FAILURE(
1004 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "0 1"));
1005 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1006 0, TabStripModel::CommandCloseTab
));
1007 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
1008 // Should have closed tab 2.
1009 EXPECT_EQ("2", GetTabStripStateString(tabstrip
));
1010 tabstrip
.CloseAllTabs();
1011 EXPECT_TRUE(tabstrip
.empty());
1014 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1015 // CommandCloseTabs.
1016 TEST_F(TabStripModelTest
, CommandCloseOtherTabs
) {
1017 TabStripDummyDelegate delegate
;
1018 TabStripModel
tabstrip(&delegate
, profile());
1019 EXPECT_TRUE(tabstrip
.empty());
1021 // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled
1022 // and close two tabs.
1023 ASSERT_NO_FATAL_FAILURE(
1024 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
1025 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1026 0, TabStripModel::CommandCloseOtherTabs
));
1027 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs
);
1028 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
1029 tabstrip
.CloseAllTabs();
1030 EXPECT_TRUE(tabstrip
.empty());
1032 // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it
1033 // with a non-selected index should close the two other tabs.
1034 ASSERT_NO_FATAL_FAILURE(
1035 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
1036 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1037 2, TabStripModel::CommandCloseOtherTabs
));
1038 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs
);
1039 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
1040 tabstrip
.CloseAllTabs();
1041 EXPECT_TRUE(tabstrip
.empty());
1043 // Select all, CommandCloseOtherTabs should not be enabled.
1044 ASSERT_NO_FATAL_FAILURE(
1045 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1 2"));
1046 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1047 2, TabStripModel::CommandCloseOtherTabs
));
1048 tabstrip
.CloseAllTabs();
1049 EXPECT_TRUE(tabstrip
.empty());
1051 // Three tabs, pin one, select the two non-pinned.
1052 ASSERT_NO_FATAL_FAILURE(
1053 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "1 2"));
1054 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1055 1, TabStripModel::CommandCloseOtherTabs
));
1056 // If we don't pass in the pinned index, the command should be enabled.
1057 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1058 0, TabStripModel::CommandCloseOtherTabs
));
1059 tabstrip
.CloseAllTabs();
1060 EXPECT_TRUE(tabstrip
.empty());
1062 // 3 tabs, one pinned.
1063 ASSERT_NO_FATAL_FAILURE(
1064 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "1"));
1065 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1066 1, TabStripModel::CommandCloseOtherTabs
));
1067 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1068 0, TabStripModel::CommandCloseOtherTabs
));
1069 tabstrip
.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs
);
1070 // The pinned tab shouldn't be closed.
1071 EXPECT_EQ("0p 1", GetTabStripStateString(tabstrip
));
1072 tabstrip
.CloseAllTabs();
1073 EXPECT_TRUE(tabstrip
.empty());
1076 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1077 // CommandCloseTabsToRight.
1078 TEST_F(TabStripModelTest
, CommandCloseTabsToRight
) {
1079 TabStripDummyDelegate delegate
;
1080 TabStripModel
tabstrip(&delegate
, profile());
1081 EXPECT_TRUE(tabstrip
.empty());
1083 // Create three tabs, select last two tabs, CommandCloseTabsToRight should
1084 // only be enabled for the first tab.
1085 ASSERT_NO_FATAL_FAILURE(
1086 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "1 2"));
1087 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1088 0, TabStripModel::CommandCloseTabsToRight
));
1089 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1090 1, TabStripModel::CommandCloseTabsToRight
));
1091 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1092 2, TabStripModel::CommandCloseTabsToRight
));
1093 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight
);
1094 EXPECT_EQ("0", GetTabStripStateString(tabstrip
));
1095 tabstrip
.CloseAllTabs();
1096 EXPECT_TRUE(tabstrip
.empty());
1099 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1100 // CommandTogglePinned.
1101 TEST_F(TabStripModelTest
, CommandTogglePinned
) {
1102 TabStripDummyDelegate delegate
;
1103 TabStripModel
tabstrip(&delegate
, profile());
1104 EXPECT_TRUE(tabstrip
.empty());
1106 // Create three tabs with one pinned, pin the first two.
1107 ASSERT_NO_FATAL_FAILURE(
1108 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "0 1"));
1109 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1110 0, TabStripModel::CommandTogglePinned
));
1111 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1112 1, TabStripModel::CommandTogglePinned
));
1113 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1114 2, TabStripModel::CommandTogglePinned
));
1115 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned
);
1116 EXPECT_EQ("0p 1p 2", GetTabStripStateString(tabstrip
));
1118 // Execute CommandTogglePinned again, this should unpin.
1119 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned
);
1120 EXPECT_EQ("0 1 2", GetTabStripStateString(tabstrip
));
1123 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned
);
1124 EXPECT_EQ("2p 0 1", GetTabStripStateString(tabstrip
));
1126 tabstrip
.CloseAllTabs();
1127 EXPECT_TRUE(tabstrip
.empty());
1130 // Tests the following context menu commands:
1132 // - Close Other Tabs
1133 // - Close Tabs To Right
1134 TEST_F(TabStripModelTest
, TestContextMenuCloseCommands
) {
1135 TabStripDummyDelegate delegate
;
1136 TabStripModel
tabstrip(&delegate
, profile());
1137 EXPECT_TRUE(tabstrip
.empty());
1139 WebContents
* opener
= CreateWebContents();
1140 tabstrip
.AppendWebContents(opener
, true);
1142 WebContents
* contents1
= CreateWebContents();
1143 WebContents
* contents2
= CreateWebContents();
1144 WebContents
* contents3
= CreateWebContents();
1146 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1147 EXPECT_EQ(0, tabstrip
.active_index());
1149 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab
);
1150 EXPECT_EQ(3, tabstrip
.count());
1152 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight
);
1153 EXPECT_EQ(1, tabstrip
.count());
1154 EXPECT_EQ(opener
, tabstrip
.GetActiveWebContents());
1156 WebContents
* dummy
= CreateWebContents();
1157 tabstrip
.AppendWebContents(dummy
, false);
1159 contents1
= CreateWebContents();
1160 contents2
= CreateWebContents();
1161 contents3
= CreateWebContents();
1162 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1163 EXPECT_EQ(5, tabstrip
.count());
1165 int dummy_index
= tabstrip
.count() - 1;
1166 tabstrip
.ActivateTabAt(dummy_index
, true);
1167 EXPECT_EQ(dummy
, tabstrip
.GetActiveWebContents());
1169 tabstrip
.ExecuteContextMenuCommand(dummy_index
,
1170 TabStripModel::CommandCloseOtherTabs
);
1171 EXPECT_EQ(1, tabstrip
.count());
1172 EXPECT_EQ(dummy
, tabstrip
.GetActiveWebContents());
1174 tabstrip
.CloseAllTabs();
1175 EXPECT_TRUE(tabstrip
.empty());
1178 // Tests GetIndicesClosedByCommand.
1179 TEST_F(TabStripModelTest
, GetIndicesClosedByCommand
) {
1180 TabStripDummyDelegate delegate
;
1181 TabStripModel
tabstrip(&delegate
, profile());
1182 EXPECT_TRUE(tabstrip
.empty());
1184 WebContents
* contents1
= CreateWebContents();
1185 WebContents
* contents2
= CreateWebContents();
1186 WebContents
* contents3
= CreateWebContents();
1187 WebContents
* contents4
= CreateWebContents();
1188 WebContents
* contents5
= CreateWebContents();
1190 tabstrip
.AppendWebContents(contents1
, true);
1191 tabstrip
.AppendWebContents(contents2
, true);
1192 tabstrip
.AppendWebContents(contents3
, true);
1193 tabstrip
.AppendWebContents(contents4
, true);
1194 tabstrip
.AppendWebContents(contents5
, true);
1196 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1197 tabstrip
, 0, TabStripModel::CommandCloseTabsToRight
));
1198 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1199 tabstrip
, 1, TabStripModel::CommandCloseTabsToRight
));
1201 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1202 tabstrip
, 0, TabStripModel::CommandCloseOtherTabs
));
1203 EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
1204 tabstrip
, 1, TabStripModel::CommandCloseOtherTabs
));
1206 // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
1208 tabstrip
.SetTabPinned(0, true);
1209 tabstrip
.SetTabPinned(1, true);
1211 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1212 tabstrip
, 0, TabStripModel::CommandCloseTabsToRight
));
1213 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1214 tabstrip
, 2, TabStripModel::CommandCloseTabsToRight
));
1216 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1217 tabstrip
, 0, TabStripModel::CommandCloseOtherTabs
));
1218 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1219 tabstrip
, 2, TabStripModel::CommandCloseOtherTabs
));
1221 tabstrip
.CloseAllTabs();
1222 EXPECT_TRUE(tabstrip
.empty());
1225 // Tests whether or not WebContentses are inserted in the correct position
1226 // using this "smart" function with a simulated middle click action on a series
1227 // of links on the home page.
1228 TEST_F(TabStripModelTest
, AddWebContents_MiddleClickLinksAndClose
) {
1229 TabStripDummyDelegate delegate
;
1230 TabStripModel
tabstrip(&delegate
, profile());
1231 EXPECT_TRUE(tabstrip
.empty());
1233 // Open the Home Page.
1234 WebContents
* homepage_contents
= CreateWebContents();
1235 tabstrip
.AddWebContents(
1236 homepage_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1237 TabStripModel::ADD_ACTIVE
);
1239 // Open some other tab, by user typing.
1240 WebContents
* typed_page_contents
= CreateWebContents();
1241 tabstrip
.AddWebContents(
1242 typed_page_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1243 TabStripModel::ADD_ACTIVE
);
1245 EXPECT_EQ(2, tabstrip
.count());
1247 // Re-select the home page.
1248 tabstrip
.ActivateTabAt(0, true);
1250 // Open a bunch of tabs by simulating middle clicking on links on the home
1252 WebContents
* middle_click_contents1
= CreateWebContents();
1253 tabstrip
.AddWebContents(
1254 middle_click_contents1
, -1, content::PAGE_TRANSITION_LINK
,
1255 TabStripModel::ADD_NONE
);
1256 WebContents
* middle_click_contents2
= CreateWebContents();
1257 tabstrip
.AddWebContents(
1258 middle_click_contents2
, -1, content::PAGE_TRANSITION_LINK
,
1259 TabStripModel::ADD_NONE
);
1260 WebContents
* middle_click_contents3
= CreateWebContents();
1261 tabstrip
.AddWebContents(
1262 middle_click_contents3
, -1, content::PAGE_TRANSITION_LINK
,
1263 TabStripModel::ADD_NONE
);
1265 EXPECT_EQ(5, tabstrip
.count());
1267 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1268 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetWebContentsAt(1));
1269 EXPECT_EQ(middle_click_contents2
, tabstrip
.GetWebContentsAt(2));
1270 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetWebContentsAt(3));
1271 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(4));
1273 // Now simulate selecting a tab in the middle of the group of tabs opened from
1274 // the home page and start closing them. Each WebContents in the group
1275 // should be closed, right to left. This test is constructed to start at the
1276 // middle WebContents in the group to make sure the cursor wraps around
1277 // to the first WebContents in the group before closing the opener or
1278 // any other WebContents.
1279 tabstrip
.ActivateTabAt(2, true);
1280 tabstrip
.CloseSelectedTabs();
1281 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetActiveWebContents());
1282 tabstrip
.CloseSelectedTabs();
1283 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetActiveWebContents());
1284 tabstrip
.CloseSelectedTabs();
1285 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1286 tabstrip
.CloseSelectedTabs();
1287 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1289 EXPECT_EQ(1, tabstrip
.count());
1291 tabstrip
.CloseAllTabs();
1292 EXPECT_TRUE(tabstrip
.empty());
1295 // Tests whether or not a WebContents created by a left click on a link
1296 // that opens a new tab is inserted correctly adjacent to the tab that spawned
1298 TEST_F(TabStripModelTest
, AddWebContents_LeftClickPopup
) {
1299 TabStripDummyDelegate delegate
;
1300 TabStripModel
tabstrip(&delegate
, profile());
1301 EXPECT_TRUE(tabstrip
.empty());
1303 // Open the Home Page.
1304 WebContents
* homepage_contents
= CreateWebContents();
1305 tabstrip
.AddWebContents(
1306 homepage_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1307 TabStripModel::ADD_ACTIVE
);
1309 // Open some other tab, by user typing.
1310 WebContents
* typed_page_contents
= CreateWebContents();
1311 tabstrip
.AddWebContents(
1312 typed_page_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1313 TabStripModel::ADD_ACTIVE
);
1315 EXPECT_EQ(2, tabstrip
.count());
1317 // Re-select the home page.
1318 tabstrip
.ActivateTabAt(0, true);
1320 // Open a tab by simulating a left click on a link that opens in a new tab.
1321 WebContents
* left_click_contents
= CreateWebContents();
1322 tabstrip
.AddWebContents(left_click_contents
, -1,
1323 content::PAGE_TRANSITION_LINK
,
1324 TabStripModel::ADD_ACTIVE
);
1326 // Verify the state meets our expectations.
1327 EXPECT_EQ(3, tabstrip
.count());
1328 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1329 EXPECT_EQ(left_click_contents
, tabstrip
.GetWebContentsAt(1));
1330 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(2));
1332 // The newly created tab should be selected.
1333 EXPECT_EQ(left_click_contents
, tabstrip
.GetActiveWebContents());
1335 // After closing the selected tab, the selection should move to the left, to
1337 tabstrip
.CloseSelectedTabs();
1338 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1340 EXPECT_EQ(2, tabstrip
.count());
1342 tabstrip
.CloseAllTabs();
1343 EXPECT_TRUE(tabstrip
.empty());
1346 // Tests whether or not new tabs that should split context (typed pages,
1347 // generated urls, also blank tabs) open at the end of the tabstrip instead of
1349 TEST_F(TabStripModelTest
, AddWebContents_CreateNewBlankTab
) {
1350 TabStripDummyDelegate delegate
;
1351 TabStripModel
tabstrip(&delegate
, profile());
1352 EXPECT_TRUE(tabstrip
.empty());
1354 // Open the Home Page.
1355 WebContents
* homepage_contents
= CreateWebContents();
1356 tabstrip
.AddWebContents(
1357 homepage_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1358 TabStripModel::ADD_ACTIVE
);
1360 // Open some other tab, by user typing.
1361 WebContents
* typed_page_contents
= CreateWebContents();
1362 tabstrip
.AddWebContents(
1363 typed_page_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1364 TabStripModel::ADD_ACTIVE
);
1366 EXPECT_EQ(2, tabstrip
.count());
1368 // Re-select the home page.
1369 tabstrip
.ActivateTabAt(0, true);
1371 // Open a new blank tab in the foreground.
1372 WebContents
* new_blank_contents
= CreateWebContents();
1373 tabstrip
.AddWebContents(new_blank_contents
, -1,
1374 content::PAGE_TRANSITION_TYPED
,
1375 TabStripModel::ADD_ACTIVE
);
1377 // Verify the state of the tabstrip.
1378 EXPECT_EQ(3, tabstrip
.count());
1379 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1380 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(1));
1381 EXPECT_EQ(new_blank_contents
, tabstrip
.GetWebContentsAt(2));
1383 // Now open a couple more blank tabs in the background.
1384 WebContents
* background_blank_contents1
= CreateWebContents();
1385 tabstrip
.AddWebContents(
1386 background_blank_contents1
, -1, content::PAGE_TRANSITION_TYPED
,
1387 TabStripModel::ADD_NONE
);
1388 WebContents
* background_blank_contents2
= CreateWebContents();
1389 tabstrip
.AddWebContents(
1390 background_blank_contents2
, -1, content::PAGE_TRANSITION_GENERATED
,
1391 TabStripModel::ADD_NONE
);
1392 EXPECT_EQ(5, tabstrip
.count());
1393 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1394 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(1));
1395 EXPECT_EQ(new_blank_contents
, tabstrip
.GetWebContentsAt(2));
1396 EXPECT_EQ(background_blank_contents1
, tabstrip
.GetWebContentsAt(3));
1397 EXPECT_EQ(background_blank_contents2
, tabstrip
.GetWebContentsAt(4));
1399 tabstrip
.CloseAllTabs();
1400 EXPECT_TRUE(tabstrip
.empty());
1403 // Tests whether opener state is correctly forgotten when the user switches
1405 TEST_F(TabStripModelTest
, AddWebContents_ForgetOpeners
) {
1406 TabStripDummyDelegate delegate
;
1407 TabStripModel
tabstrip(&delegate
, profile());
1408 EXPECT_TRUE(tabstrip
.empty());
1410 // Open the Home Page
1411 WebContents
* homepage_contents
= CreateWebContents();
1412 tabstrip
.AddWebContents(
1413 homepage_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1414 TabStripModel::ADD_ACTIVE
);
1416 // Open some other tab, by user typing.
1417 WebContents
* typed_page_contents
= CreateWebContents();
1418 tabstrip
.AddWebContents(
1419 typed_page_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1420 TabStripModel::ADD_ACTIVE
);
1422 EXPECT_EQ(2, tabstrip
.count());
1424 // Re-select the home page.
1425 tabstrip
.ActivateTabAt(0, true);
1427 // Open a bunch of tabs by simulating middle clicking on links on the home
1429 WebContents
* middle_click_contents1
= CreateWebContents();
1430 tabstrip
.AddWebContents(
1431 middle_click_contents1
, -1, content::PAGE_TRANSITION_LINK
,
1432 TabStripModel::ADD_NONE
);
1433 WebContents
* middle_click_contents2
= CreateWebContents();
1434 tabstrip
.AddWebContents(
1435 middle_click_contents2
, -1, content::PAGE_TRANSITION_LINK
,
1436 TabStripModel::ADD_NONE
);
1437 WebContents
* middle_click_contents3
= CreateWebContents();
1438 tabstrip
.AddWebContents(
1439 middle_click_contents3
, -1, content::PAGE_TRANSITION_LINK
,
1440 TabStripModel::ADD_NONE
);
1442 // Break out of the context by selecting a tab in a different context.
1443 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(4));
1444 tabstrip
.SelectLastTab();
1445 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1447 // Step back into the context by selecting a tab inside it.
1448 tabstrip
.ActivateTabAt(2, true);
1449 EXPECT_EQ(middle_click_contents2
, tabstrip
.GetActiveWebContents());
1451 // Now test that closing tabs selects to the right until there are no more,
1452 // then to the left, as if there were no context (context has been
1453 // successfully forgotten).
1454 tabstrip
.CloseSelectedTabs();
1455 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetActiveWebContents());
1456 tabstrip
.CloseSelectedTabs();
1457 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1458 tabstrip
.CloseSelectedTabs();
1459 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetActiveWebContents());
1460 tabstrip
.CloseSelectedTabs();
1461 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1463 EXPECT_EQ(1, tabstrip
.count());
1465 tabstrip
.CloseAllTabs();
1466 EXPECT_TRUE(tabstrip
.empty());
1469 // Added for http://b/issue?id=958960
1470 TEST_F(TabStripModelTest
, AppendContentsReselectionTest
) {
1471 TabStripDummyDelegate delegate
;
1472 TabStripModel
tabstrip(&delegate
, profile());
1473 EXPECT_TRUE(tabstrip
.empty());
1475 // Open the Home Page.
1476 WebContents
* homepage_contents
= CreateWebContents();
1477 tabstrip
.AddWebContents(
1478 homepage_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1479 TabStripModel::ADD_ACTIVE
);
1481 // Open some other tab, by user typing.
1482 WebContents
* typed_page_contents
= CreateWebContents();
1483 tabstrip
.AddWebContents(
1484 typed_page_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1485 TabStripModel::ADD_NONE
);
1487 // The selected tab should still be the first.
1488 EXPECT_EQ(0, tabstrip
.active_index());
1490 // Now simulate a link click that opens a new tab (by virtue of target=_blank)
1491 // and make sure the correct tab gets selected when the new tab is closed.
1492 WebContents
* target_blank
= CreateWebContents();
1493 tabstrip
.AppendWebContents(target_blank
, true);
1494 EXPECT_EQ(2, tabstrip
.active_index());
1495 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1496 EXPECT_EQ(0, tabstrip
.active_index());
1498 // Clean up after ourselves.
1499 tabstrip
.CloseAllTabs();
1502 // Added for http://b/issue?id=1027661
1503 TEST_F(TabStripModelTest
, ReselectionConsidersChildrenTest
) {
1504 TabStripDummyDelegate delegate
;
1505 TabStripModel
strip(&delegate
, profile());
1508 WebContents
* page_a_contents
= CreateWebContents();
1509 strip
.AddWebContents(
1510 page_a_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1511 TabStripModel::ADD_ACTIVE
);
1513 // Simulate middle click to open page A.A and A.B
1514 WebContents
* page_a_a_contents
= CreateWebContents();
1515 strip
.AddWebContents(page_a_a_contents
, -1, content::PAGE_TRANSITION_LINK
,
1516 TabStripModel::ADD_NONE
);
1517 WebContents
* page_a_b_contents
= CreateWebContents();
1518 strip
.AddWebContents(page_a_b_contents
, -1, content::PAGE_TRANSITION_LINK
,
1519 TabStripModel::ADD_NONE
);
1522 strip
.ActivateTabAt(1, true);
1523 EXPECT_EQ(page_a_a_contents
, strip
.GetActiveWebContents());
1525 // Simulate a middle click to open page A.A.A
1526 WebContents
* page_a_a_a_contents
= CreateWebContents();
1527 strip
.AddWebContents(page_a_a_a_contents
, -1, content::PAGE_TRANSITION_LINK
,
1528 TabStripModel::ADD_NONE
);
1530 EXPECT_EQ(page_a_a_a_contents
, strip
.GetWebContentsAt(2));
1533 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1535 // Page A.A.A should be selected, NOT A.B
1536 EXPECT_EQ(page_a_a_a_contents
, strip
.GetActiveWebContents());
1539 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1541 // Page A.B should be selected
1542 EXPECT_EQ(page_a_b_contents
, strip
.GetActiveWebContents());
1545 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1547 // Page A should be selected
1548 EXPECT_EQ(page_a_contents
, strip
.GetActiveWebContents());
1551 strip
.CloseAllTabs();
1554 TEST_F(TabStripModelTest
, AddWebContents_NewTabAtEndOfStripInheritsGroup
) {
1555 TabStripDummyDelegate delegate
;
1556 TabStripModel
strip(&delegate
, profile());
1559 WebContents
* page_a_contents
= CreateWebContents();
1560 strip
.AddWebContents(page_a_contents
, -1,
1561 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1562 TabStripModel::ADD_ACTIVE
);
1564 // Open pages B, C and D in the background from links on page A...
1565 WebContents
* page_b_contents
= CreateWebContents();
1566 WebContents
* page_c_contents
= CreateWebContents();
1567 WebContents
* page_d_contents
= CreateWebContents();
1568 strip
.AddWebContents(page_b_contents
, -1, content::PAGE_TRANSITION_LINK
,
1569 TabStripModel::ADD_NONE
);
1570 strip
.AddWebContents(page_c_contents
, -1, content::PAGE_TRANSITION_LINK
,
1571 TabStripModel::ADD_NONE
);
1572 strip
.AddWebContents(page_d_contents
, -1, content::PAGE_TRANSITION_LINK
,
1573 TabStripModel::ADD_NONE
);
1575 // Switch to page B's tab.
1576 strip
.ActivateTabAt(1, true);
1578 // Open a New Tab at the end of the strip (simulate Ctrl+T)
1579 WebContents
* new_contents
= CreateWebContents();
1580 strip
.AddWebContents(new_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1581 TabStripModel::ADD_ACTIVE
);
1583 EXPECT_EQ(4, strip
.GetIndexOfWebContents(new_contents
));
1584 EXPECT_EQ(4, strip
.active_index());
1586 // Close the New Tab that was just opened. We should be returned to page B's
1588 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1590 EXPECT_EQ(1, strip
.active_index());
1592 // Open a non-New Tab tab at the end of the strip, with a TYPED transition.
1593 // This is like typing a URL in the address bar and pressing Alt+Enter. The
1594 // behavior should be the same as above.
1595 WebContents
* page_e_contents
= CreateWebContents();
1596 strip
.AddWebContents(page_e_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1597 TabStripModel::ADD_ACTIVE
);
1599 EXPECT_EQ(4, strip
.GetIndexOfWebContents(page_e_contents
));
1600 EXPECT_EQ(4, strip
.active_index());
1602 // Close the Tab. Selection should shift back to page B's Tab.
1603 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1605 EXPECT_EQ(1, strip
.active_index());
1607 // Open a non-New Tab tab at the end of the strip, with some other
1608 // transition. This is like right clicking on a bookmark and choosing "Open
1609 // in New Tab". No opener relationship should be preserved between this Tab
1610 // and the one that was active when the gesture was performed.
1611 WebContents
* page_f_contents
= CreateWebContents();
1612 strip
.AddWebContents(page_f_contents
, -1,
1613 content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1614 TabStripModel::ADD_ACTIVE
);
1616 EXPECT_EQ(4, strip
.GetIndexOfWebContents(page_f_contents
));
1617 EXPECT_EQ(4, strip
.active_index());
1619 // Close the Tab. The next-adjacent should be selected.
1620 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1622 EXPECT_EQ(3, strip
.active_index());
1625 strip
.CloseAllTabs();
1628 // A test of navigations in a tab that is part of a group of opened from some
1629 // parent tab. If the navigations are link clicks, the group relationship of
1630 // the tab to its parent are preserved. If they are of any other type, they are
1632 TEST_F(TabStripModelTest
, NavigationForgetsOpeners
) {
1633 TabStripDummyDelegate delegate
;
1634 TabStripModel
strip(&delegate
, profile());
1637 WebContents
* page_a_contents
= CreateWebContents();
1638 strip
.AddWebContents(page_a_contents
, -1,
1639 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1640 TabStripModel::ADD_ACTIVE
);
1642 // Open pages B, C and D in the background from links on page A...
1643 WebContents
* page_b_contents
= CreateWebContents();
1644 WebContents
* page_c_contents
= CreateWebContents();
1645 WebContents
* page_d_contents
= CreateWebContents();
1646 strip
.AddWebContents(page_b_contents
, -1, content::PAGE_TRANSITION_LINK
,
1647 TabStripModel::ADD_NONE
);
1648 strip
.AddWebContents(page_c_contents
, -1, content::PAGE_TRANSITION_LINK
,
1649 TabStripModel::ADD_NONE
);
1650 strip
.AddWebContents(page_d_contents
, -1, content::PAGE_TRANSITION_LINK
,
1651 TabStripModel::ADD_NONE
);
1653 // Open page E in a different opener group from page A.
1654 WebContents
* page_e_contents
= CreateWebContents();
1655 strip
.AddWebContents(page_e_contents
, -1,
1656 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1657 TabStripModel::ADD_NONE
);
1659 // Tell the TabStripModel that we are navigating page D via a link click.
1660 strip
.ActivateTabAt(3, true);
1661 strip
.TabNavigating(page_d_contents
, content::PAGE_TRANSITION_LINK
);
1663 // Close page D, page C should be selected. (part of same group).
1664 strip
.CloseWebContentsAt(3, TabStripModel::CLOSE_NONE
);
1665 EXPECT_EQ(2, strip
.active_index());
1667 // Tell the TabStripModel that we are navigating in page C via a bookmark.
1668 strip
.TabNavigating(page_c_contents
, content::PAGE_TRANSITION_AUTO_BOOKMARK
);
1670 // Close page C, page E should be selected. (C is no longer part of the
1671 // A-B-C-D group, selection moves to the right).
1672 strip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1673 EXPECT_EQ(page_e_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1675 strip
.CloseAllTabs();
1678 // A test that the forgetting behavior tested in NavigationForgetsOpeners above
1679 // doesn't cause the opener relationship for a New Tab opened at the end of the
1680 // TabStrip to be reset (Test 1 below), unless another any other tab is
1681 // selected (Test 2 below).
1682 TEST_F(TabStripModelTest
, NavigationForgettingDoesntAffectNewTab
) {
1683 TabStripDummyDelegate delegate
;
1684 TabStripModel
strip(&delegate
, profile());
1686 // Open a tab and several tabs from it, then select one of the tabs that was
1688 WebContents
* page_a_contents
= CreateWebContents();
1689 strip
.AddWebContents(page_a_contents
, -1,
1690 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1691 TabStripModel::ADD_ACTIVE
);
1693 WebContents
* page_b_contents
= CreateWebContents();
1694 WebContents
* page_c_contents
= CreateWebContents();
1695 WebContents
* page_d_contents
= CreateWebContents();
1696 strip
.AddWebContents(page_b_contents
, -1, content::PAGE_TRANSITION_LINK
,
1697 TabStripModel::ADD_NONE
);
1698 strip
.AddWebContents(page_c_contents
, -1, content::PAGE_TRANSITION_LINK
,
1699 TabStripModel::ADD_NONE
);
1700 strip
.AddWebContents(page_d_contents
, -1, content::PAGE_TRANSITION_LINK
,
1701 TabStripModel::ADD_NONE
);
1703 strip
.ActivateTabAt(2, true);
1705 // TEST 1: If the user is in a group of tabs and opens a new tab at the end
1706 // of the strip, closing that new tab will select the tab that they were
1709 // Now simulate opening a new tab at the end of the TabStrip.
1710 WebContents
* new_contents1
= CreateWebContents();
1711 strip
.AddWebContents(new_contents1
, -1, content::PAGE_TRANSITION_TYPED
,
1712 TabStripModel::ADD_ACTIVE
);
1714 // At this point, if we close this tab the last selected one should be
1716 strip
.CloseWebContentsAt(strip
.count() - 1, TabStripModel::CLOSE_NONE
);
1717 EXPECT_EQ(page_c_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1719 // TEST 2: If the user is in a group of tabs and opens a new tab at the end
1720 // of the strip, selecting any other tab in the strip will cause that new
1721 // tab's opener relationship to be forgotten.
1723 // Open a new tab again.
1724 WebContents
* new_contents2
= CreateWebContents();
1725 strip
.AddWebContents(new_contents2
, -1, content::PAGE_TRANSITION_TYPED
,
1726 TabStripModel::ADD_ACTIVE
);
1728 // Now select the first tab.
1729 strip
.ActivateTabAt(0, true);
1731 // Now select the last tab.
1732 strip
.ActivateTabAt(strip
.count() - 1, true);
1734 // Now close the last tab. The next adjacent should be selected.
1735 strip
.CloseWebContentsAt(strip
.count() - 1, TabStripModel::CLOSE_NONE
);
1736 EXPECT_EQ(page_d_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1738 strip
.CloseAllTabs();
1741 // This fails on Linux when run with the rest of unit_tests (crbug.com/302156)
1742 // and fails consistently on Mac and Windows.
1743 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
1744 #define MAYBE_FastShutdown \
1745 DISABLED_FastShutdown
1747 #define MAYBE_FastShutdown \
1750 // Tests that fast shutdown is attempted appropriately.
1751 TEST_F(TabStripModelTest
, MAYBE_FastShutdown
) {
1752 TabStripDummyDelegate delegate
;
1753 TabStripModel
tabstrip(&delegate
, profile());
1754 MockTabStripModelObserver
observer(&tabstrip
);
1755 tabstrip
.AddObserver(&observer
);
1757 EXPECT_TRUE(tabstrip
.empty());
1759 // Make sure fast shutdown is attempted when tabs that share a RPH are shut
1762 WebContents
* contents1
= CreateWebContents();
1763 WebContents
* contents2
= CreateWebContentsWithSharedRPH(contents1
);
1765 SetID(contents1
, 1);
1766 SetID(contents2
, 2);
1768 tabstrip
.AppendWebContents(contents1
, true);
1769 tabstrip
.AppendWebContents(contents2
, true);
1771 // Turn on the fake unload listener so the tabs don't actually get shut
1772 // down when we call CloseAllTabs()---we need to be able to check that
1773 // fast shutdown was attempted.
1774 delegate
.set_run_unload_listener(true);
1775 tabstrip
.CloseAllTabs();
1776 // On a mock RPH this checks whether we *attempted* fast shutdown.
1777 // A real RPH would reject our attempt since there is an unload handler.
1778 EXPECT_TRUE(contents1
->GetRenderProcessHost()->FastShutdownStarted());
1779 EXPECT_EQ(2, tabstrip
.count());
1781 delegate
.set_run_unload_listener(false);
1782 tabstrip
.CloseAllTabs();
1783 EXPECT_TRUE(tabstrip
.empty());
1786 // Make sure fast shutdown is not attempted when only some tabs that share a
1787 // RPH are shut down.
1789 WebContents
* contents1
= CreateWebContents();
1790 WebContents
* contents2
= CreateWebContentsWithSharedRPH(contents1
);
1792 SetID(contents1
, 1);
1793 SetID(contents2
, 2);
1795 tabstrip
.AppendWebContents(contents1
, true);
1796 tabstrip
.AppendWebContents(contents2
, true);
1798 tabstrip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
1799 EXPECT_FALSE(contents1
->GetRenderProcessHost()->FastShutdownStarted());
1800 EXPECT_EQ(1, tabstrip
.count());
1802 tabstrip
.CloseAllTabs();
1803 EXPECT_TRUE(tabstrip
.empty());
1807 // Tests various permutations of apps.
1808 TEST_F(TabStripModelTest
, Apps
) {
1809 TabStripDummyDelegate delegate
;
1810 TabStripModel
tabstrip(&delegate
, profile());
1811 MockTabStripModelObserver
observer(&tabstrip
);
1812 tabstrip
.AddObserver(&observer
);
1814 EXPECT_TRUE(tabstrip
.empty());
1816 typedef MockTabStripModelObserver::State State
;
1819 base::FilePath
path(FILE_PATH_LITERAL("c:\\foo"));
1820 #elif defined(OS_POSIX)
1821 base::FilePath
path(FILE_PATH_LITERAL("/foo"));
1824 base::DictionaryValue manifest
;
1825 manifest
.SetString("name", "hi!");
1826 manifest
.SetString("version", "1");
1827 manifest
.SetString("app.launch.web_url", "http://www.google.com");
1829 scoped_refptr
<Extension
> extension_app(
1830 Extension::Create(path
, extensions::Manifest::INVALID_LOCATION
,
1831 manifest
, Extension::NO_FLAGS
, &error
));
1832 WebContents
* contents1
= CreateWebContents();
1833 extensions::TabHelper::CreateForWebContents(contents1
);
1834 extensions::TabHelper::FromWebContents(contents1
)
1835 ->SetExtensionApp(extension_app
.get());
1836 WebContents
* contents2
= CreateWebContents();
1837 extensions::TabHelper::CreateForWebContents(contents2
);
1838 extensions::TabHelper::FromWebContents(contents2
)
1839 ->SetExtensionApp(extension_app
.get());
1840 WebContents
* contents3
= CreateWebContents();
1842 SetID(contents1
, 1);
1843 SetID(contents2
, 2);
1844 SetID(contents3
, 3);
1846 // Note! The ordering of these tests is important, each subsequent test
1847 // builds on the state established in the previous. This is important if you
1848 // ever insert tests rather than append.
1850 // Initial state, tab3 only and selected.
1851 tabstrip
.AppendWebContents(contents3
, true);
1853 observer
.ClearStates();
1855 // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal
1856 // position and tab1 should end up at position 0.
1858 tabstrip
.InsertWebContentsAt(1, contents1
, TabStripModel::ADD_NONE
);
1860 ASSERT_EQ(1, observer
.GetStateCount());
1861 State
state(contents1
, 0, MockTabStripModelObserver::INSERT
);
1862 EXPECT_TRUE(observer
.StateEquals(0, state
));
1864 // And verify the state.
1865 EXPECT_EQ("1ap 3", GetTabStripStateString(tabstrip
));
1867 observer
.ClearStates();
1870 // Insert tab 2 at position 1.
1872 tabstrip
.InsertWebContentsAt(1, contents2
, TabStripModel::ADD_NONE
);
1874 ASSERT_EQ(1, observer
.GetStateCount());
1875 State
state(contents2
, 1, MockTabStripModelObserver::INSERT
);
1876 EXPECT_TRUE(observer
.StateEquals(0, state
));
1878 // And verify the state.
1879 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip
));
1881 observer
.ClearStates();
1884 // Try to move tab 3 to position 0. This isn't legal and should be ignored.
1886 tabstrip
.MoveWebContentsAt(2, 0, 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 3. This isn't legal and should be ignored.
1898 tabstrip
.MoveWebContentsAt(0, 2, false);
1900 ASSERT_EQ(0, observer
.GetStateCount());
1902 // And verify the state didn't change.
1903 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip
));
1905 observer
.ClearStates();
1908 // Try to move tab 0 to position 1. This is a legal move.
1910 tabstrip
.MoveWebContentsAt(0, 1, false);
1912 ASSERT_EQ(1, observer
.GetStateCount());
1913 State
state(contents1
, 1, MockTabStripModelObserver::MOVE
);
1914 state
.src_index
= 0;
1915 EXPECT_TRUE(observer
.StateEquals(0, state
));
1917 // And verify the state didn't change.
1918 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip
));
1920 observer
.ClearStates();
1923 // Remove tab3 and insert at position 0. It should be forced to position 2.
1925 tabstrip
.DetachWebContentsAt(2);
1926 observer
.ClearStates();
1928 tabstrip
.InsertWebContentsAt(0, contents3
, TabStripModel::ADD_NONE
);
1930 ASSERT_EQ(1, observer
.GetStateCount());
1931 State
state(contents3
, 2, MockTabStripModelObserver::INSERT
);
1932 EXPECT_TRUE(observer
.StateEquals(0, state
));
1934 // And verify the state didn't change.
1935 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip
));
1937 observer
.ClearStates();
1940 tabstrip
.CloseAllTabs();
1943 // Tests various permutations of pinning tabs.
1944 TEST_F(TabStripModelTest
, Pinning
) {
1945 TabStripDummyDelegate delegate
;
1946 TabStripModel
tabstrip(&delegate
, profile());
1947 MockTabStripModelObserver
observer(&tabstrip
);
1948 tabstrip
.AddObserver(&observer
);
1950 EXPECT_TRUE(tabstrip
.empty());
1952 typedef MockTabStripModelObserver::State State
;
1954 WebContents
* contents1
= CreateWebContents();
1955 WebContents
* contents2
= CreateWebContents();
1956 WebContents
* contents3
= CreateWebContents();
1958 SetID(contents1
, 1);
1959 SetID(contents2
, 2);
1960 SetID(contents3
, 3);
1962 // Note! The ordering of these tests is important, each subsequent test
1963 // builds on the state established in the previous. This is important if you
1964 // ever insert tests rather than append.
1966 // Initial state, three tabs, first selected.
1967 tabstrip
.AppendWebContents(contents1
, true);
1968 tabstrip
.AppendWebContents(contents2
, false);
1969 tabstrip
.AppendWebContents(contents3
, false);
1971 observer
.ClearStates();
1973 // Pin the first tab, this shouldn't visually reorder anything.
1975 tabstrip
.SetTabPinned(0, true);
1977 // As the order didn't change, we should get a pinned notification.
1978 ASSERT_EQ(1, observer
.GetStateCount());
1979 State
state(contents1
, 0, MockTabStripModelObserver::PINNED
);
1980 EXPECT_TRUE(observer
.StateEquals(0, state
));
1982 // And verify the state.
1983 EXPECT_EQ("1p 2 3", GetTabStripStateString(tabstrip
));
1985 observer
.ClearStates();
1988 // Unpin the first tab.
1990 tabstrip
.SetTabPinned(0, false);
1992 // As the order didn't change, we should get a pinned notification.
1993 ASSERT_EQ(1, observer
.GetStateCount());
1994 State
state(contents1
, 0, MockTabStripModelObserver::PINNED
);
1995 EXPECT_TRUE(observer
.StateEquals(0, state
));
1997 // And verify the state.
1998 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
2000 observer
.ClearStates();
2003 // Pin the 3rd tab, which should move it to the front.
2005 tabstrip
.SetTabPinned(2, true);
2007 // The pinning should have resulted in a move and a pinned notification.
2008 ASSERT_EQ(2, observer
.GetStateCount());
2009 State
state(contents3
, 0, MockTabStripModelObserver::MOVE
);
2010 state
.src_index
= 2;
2011 EXPECT_TRUE(observer
.StateEquals(0, state
));
2013 state
= State(contents3
, 0, MockTabStripModelObserver::PINNED
);
2014 EXPECT_TRUE(observer
.StateEquals(1, state
));
2016 // And verify the state.
2017 EXPECT_EQ("3p 1 2", GetTabStripStateString(tabstrip
));
2019 observer
.ClearStates();
2022 // Pin the tab "1", which shouldn't move anything.
2024 tabstrip
.SetTabPinned(1, true);
2026 // As the order didn't change, we should get a pinned notification.
2027 ASSERT_EQ(1, observer
.GetStateCount());
2028 State
state(contents1
, 1, MockTabStripModelObserver::PINNED
);
2029 EXPECT_TRUE(observer
.StateEquals(0, state
));
2031 // And verify the state.
2032 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip
));
2034 observer
.ClearStates();
2037 // Try to move tab "2" to the front, it should be ignored.
2039 tabstrip
.MoveWebContentsAt(2, 0, false);
2041 // As the order didn't change, we should get a pinned notification.
2042 ASSERT_EQ(0, observer
.GetStateCount());
2044 // And verify the state.
2045 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip
));
2047 observer
.ClearStates();
2050 // Unpin tab "3", which implicitly moves it to the end.
2052 tabstrip
.SetTabPinned(0, false);
2054 ASSERT_EQ(2, observer
.GetStateCount());
2055 State
state(contents3
, 1, MockTabStripModelObserver::MOVE
);
2056 state
.src_index
= 0;
2057 EXPECT_TRUE(observer
.StateEquals(0, state
));
2059 state
= State(contents3
, 1, MockTabStripModelObserver::PINNED
);
2060 EXPECT_TRUE(observer
.StateEquals(1, state
));
2062 // And verify the state.
2063 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip
));
2065 observer
.ClearStates();
2068 // Unpin tab "3", nothing should happen.
2070 tabstrip
.SetTabPinned(1, false);
2072 ASSERT_EQ(0, observer
.GetStateCount());
2074 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip
));
2076 observer
.ClearStates();
2081 tabstrip
.SetTabPinned(0, true);
2082 tabstrip
.SetTabPinned(1, true);
2084 EXPECT_EQ("1p 3p 2", GetTabStripStateString(tabstrip
));
2086 observer
.ClearStates();
2089 WebContents
* contents4
= CreateWebContents();
2090 SetID(contents4
, 4);
2092 // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end
2095 tabstrip
.InsertWebContentsAt(1, contents4
, TabStripModel::ADD_NONE
);
2097 ASSERT_EQ(1, observer
.GetStateCount());
2098 State
state(contents4
, 2, MockTabStripModelObserver::INSERT
);
2099 EXPECT_TRUE(observer
.StateEquals(0, state
));
2101 EXPECT_EQ("1p 3p 4 2", GetTabStripStateString(tabstrip
));
2104 tabstrip
.CloseAllTabs();
2107 // Makes sure the TabStripModel calls the right observer methods during a
2109 TEST_F(TabStripModelTest
, ReplaceSendsSelected
) {
2110 typedef MockTabStripModelObserver::State State
;
2112 TabStripDummyDelegate delegate
;
2113 TabStripModel
strip(&delegate
, profile());
2115 WebContents
* first_contents
= CreateWebContents();
2116 strip
.AddWebContents(first_contents
, -1, content::PAGE_TRANSITION_TYPED
,
2117 TabStripModel::ADD_ACTIVE
);
2119 MockTabStripModelObserver
tabstrip_observer(&strip
);
2120 strip
.AddObserver(&tabstrip_observer
);
2122 WebContents
* new_contents
= CreateWebContents();
2123 delete strip
.ReplaceWebContentsAt(0, new_contents
);
2125 ASSERT_EQ(2, tabstrip_observer
.GetStateCount());
2127 // First event should be for replaced.
2128 State
state(new_contents
, 0, MockTabStripModelObserver::REPLACED
);
2129 state
.src_contents
= first_contents
;
2130 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state
));
2132 // And the second for selected.
2133 state
= State(new_contents
, 0, MockTabStripModelObserver::ACTIVATE
);
2134 state
.src_contents
= first_contents
;
2135 state
.change_reason
= TabStripModelObserver::CHANGE_REASON_REPLACED
;
2136 EXPECT_TRUE(tabstrip_observer
.StateEquals(1, state
));
2138 // Now add another tab and replace it, making sure we don't get a selected
2140 WebContents
* third_contents
= CreateWebContents();
2141 strip
.AddWebContents(third_contents
, 1, content::PAGE_TRANSITION_TYPED
,
2142 TabStripModel::ADD_NONE
);
2144 tabstrip_observer
.ClearStates();
2147 new_contents
= CreateWebContents();
2148 delete strip
.ReplaceWebContentsAt(1, new_contents
);
2150 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2152 state
= State(new_contents
, 1, MockTabStripModelObserver::REPLACED
);
2153 state
.src_contents
= third_contents
;
2154 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state
));
2156 strip
.CloseAllTabs();
2159 // Ensures discarding tabs leaves TabStripModel in a good state.
2160 TEST_F(TabStripModelTest
, DiscardWebContentsAt
) {
2161 typedef MockTabStripModelObserver::State State
;
2163 TabStripDummyDelegate delegate
;
2164 TabStripModel
tabstrip(&delegate
, profile());
2166 // Fill it with some tabs.
2167 WebContents
* contents1
= CreateWebContents();
2168 tabstrip
.AppendWebContents(contents1
, true);
2169 WebContents
* contents2
= CreateWebContents();
2170 tabstrip
.AppendWebContents(contents2
, true);
2172 // Start watching for events after the appends to avoid observing state
2173 // transitions that aren't relevant to this test.
2174 MockTabStripModelObserver
tabstrip_observer(&tabstrip
);
2175 tabstrip
.AddObserver(&tabstrip_observer
);
2177 // Discard one of the tabs.
2178 WebContents
* null_contents1
= tabstrip
.DiscardWebContentsAt(0);
2179 ASSERT_EQ(2, tabstrip
.count());
2180 EXPECT_TRUE(tabstrip
.IsTabDiscarded(0));
2181 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2182 ASSERT_EQ(null_contents1
, tabstrip
.GetWebContentsAt(0));
2183 ASSERT_EQ(contents2
, tabstrip
.GetWebContentsAt(1));
2184 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2185 State
state1(null_contents1
, 0, MockTabStripModelObserver::REPLACED
);
2186 state1
.src_contents
= contents1
;
2187 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state1
));
2188 tabstrip_observer
.ClearStates();
2190 // Discard the same tab again.
2191 WebContents
* null_contents2
= tabstrip
.DiscardWebContentsAt(0);
2192 ASSERT_EQ(2, tabstrip
.count());
2193 EXPECT_TRUE(tabstrip
.IsTabDiscarded(0));
2194 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2195 ASSERT_EQ(null_contents2
, tabstrip
.GetWebContentsAt(0));
2196 ASSERT_EQ(contents2
, tabstrip
.GetWebContentsAt(1));
2197 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2198 State
state2(null_contents2
, 0, MockTabStripModelObserver::REPLACED
);
2199 state2
.src_contents
= null_contents1
;
2200 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state2
));
2201 tabstrip_observer
.ClearStates();
2203 // Activating the tab should clear its discard state.
2204 tabstrip
.ActivateTabAt(0, true /* user_gesture */);
2205 ASSERT_EQ(2, tabstrip
.count());
2206 EXPECT_FALSE(tabstrip
.IsTabDiscarded(0));
2207 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2209 // Don't discard active tab.
2210 tabstrip
.DiscardWebContentsAt(0);
2211 ASSERT_EQ(2, tabstrip
.count());
2212 EXPECT_FALSE(tabstrip
.IsTabDiscarded(0));
2213 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2215 tabstrip
.CloseAllTabs();
2218 // Makes sure TabStripModel handles the case of deleting a tab while removing
2220 TEST_F(TabStripModelTest
, DeleteFromDestroy
) {
2221 TabStripDummyDelegate delegate
;
2222 TabStripModel
strip(&delegate
, profile());
2223 WebContents
* contents1
= CreateWebContents();
2224 WebContents
* contents2
= CreateWebContents();
2225 MockTabStripModelObserver
tab_strip_model_observer(&strip
);
2226 strip
.AppendWebContents(contents1
, true);
2227 strip
.AppendWebContents(contents2
, true);
2228 // DeleteWebContentsOnDestroyedObserver deletes contents1 when contents2 sends
2229 // out notification that it is being destroyed.
2230 DeleteWebContentsOnDestroyedObserver
observer(contents2
, contents1
, NULL
);
2231 strip
.AddObserver(&tab_strip_model_observer
);
2232 strip
.CloseAllTabs();
2234 int close_all_count
= 0, close_all_canceled_count
= 0;
2235 tab_strip_model_observer
.GetCloseCounts(&close_all_count
,
2236 &close_all_canceled_count
);
2237 EXPECT_EQ(1, close_all_count
);
2238 EXPECT_EQ(0, close_all_canceled_count
);
2240 strip
.RemoveObserver(&tab_strip_model_observer
);
2243 // Makes sure TabStripModel handles the case of deleting another tab and the
2244 // TabStrip while removing another tab.
2245 TEST_F(TabStripModelTest
, DeleteTabStripFromDestroy
) {
2246 TabStripDummyDelegate delegate
;
2247 TabStripModel
* strip
= new TabStripModel(&delegate
, profile());
2248 MockTabStripModelObserver
tab_strip_model_observer(strip
);
2249 strip
->AddObserver(&tab_strip_model_observer
);
2250 WebContents
* contents1
= CreateWebContents();
2251 WebContents
* contents2
= CreateWebContents();
2252 strip
->AppendWebContents(contents1
, true);
2253 strip
->AppendWebContents(contents2
, true);
2254 // DeleteWebContentsOnDestroyedObserver deletes |contents1| and |strip| when
2255 // |contents2| sends out notification that it is being destroyed.
2256 DeleteWebContentsOnDestroyedObserver
observer(contents2
, contents1
, strip
);
2257 strip
->CloseAllTabs();
2258 EXPECT_TRUE(tab_strip_model_observer
.empty());
2259 EXPECT_TRUE(tab_strip_model_observer
.deleted());
2262 TEST_F(TabStripModelTest
, MoveSelectedTabsTo
) {
2264 // Number of tabs the tab strip should have.
2265 const int tab_count
;
2267 // Number of pinned tabs.
2268 const int pinned_count
;
2270 // Index of the tabs to select.
2271 const std::string selected_tabs
;
2273 // Index to move the tabs to.
2274 const int target_index
;
2276 // Expected state after the move (space separated list of indices).
2277 const std::string state_after_move
;
2280 { 2, 0, "0", 1, "1 0" },
2281 { 3, 0, "0", 2, "1 2 0" },
2282 { 3, 0, "2", 0, "2 0 1" },
2283 { 3, 0, "2", 1, "0 2 1" },
2284 { 3, 0, "0 1", 0, "0 1 2" },
2287 { 6, 0, "4 5", 1, "0 4 5 1 2 3" },
2288 { 3, 0, "0 1", 1, "2 0 1" },
2289 { 4, 0, "0 2", 1, "1 0 2 3" },
2290 { 6, 0, "0 1", 3, "2 3 4 0 1 5" },
2293 { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" },
2294 { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" },
2295 { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" },
2298 { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" },
2301 { 16, 0, "0 1 2 3 4 7 9", 8, "5 6 8 10 11 12 13 14 0 1 2 3 4 7 9 15" },
2303 // With pinned tabs.
2304 { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" },
2305 { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" },
2306 { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" },
2307 { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" },
2309 { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" },
2312 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
2313 TabStripDummyDelegate delegate
;
2314 TabStripModel
strip(&delegate
, profile());
2315 ASSERT_NO_FATAL_FAILURE(
2316 PrepareTabstripForSelectionTest(&strip
, test_data
[i
].tab_count
,
2317 test_data
[i
].pinned_count
,
2318 test_data
[i
].selected_tabs
));
2319 strip
.MoveSelectedTabsTo(test_data
[i
].target_index
);
2320 EXPECT_EQ(test_data
[i
].state_after_move
,
2321 GetTabStripStateString(strip
)) << i
;
2322 strip
.CloseAllTabs();
2326 // Tests that moving a tab forgets all groups referencing it.
2327 TEST_F(TabStripModelTest
, MoveSelectedTabsTo_ForgetGroups
) {
2328 TabStripDummyDelegate delegate
;
2329 TabStripModel
strip(&delegate
, profile());
2331 // Open page A as a new tab and then A1 in the background from A.
2332 WebContents
* page_a_contents
= CreateWebContents();
2333 strip
.AddWebContents(page_a_contents
, -1,
2334 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
2335 TabStripModel::ADD_ACTIVE
);
2336 WebContents
* page_a1_contents
= CreateWebContents();
2337 strip
.AddWebContents(page_a1_contents
, -1, content::PAGE_TRANSITION_LINK
,
2338 TabStripModel::ADD_NONE
);
2340 // Likewise, open pages B and B1.
2341 WebContents
* page_b_contents
= CreateWebContents();
2342 strip
.AddWebContents(page_b_contents
, -1,
2343 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
2344 TabStripModel::ADD_ACTIVE
);
2345 WebContents
* page_b1_contents
= CreateWebContents();
2346 strip
.AddWebContents(page_b1_contents
, -1, content::PAGE_TRANSITION_LINK
,
2347 TabStripModel::ADD_NONE
);
2349 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(0));
2350 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(1));
2351 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(2));
2352 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(3));
2354 // Move page B to the start of the tab strip.
2355 strip
.MoveSelectedTabsTo(0);
2357 // Open page B2 in the background from B. It should end up after B.
2358 WebContents
* page_b2_contents
= CreateWebContents();
2359 strip
.AddWebContents(page_b2_contents
, -1, content::PAGE_TRANSITION_LINK
,
2360 TabStripModel::ADD_NONE
);
2361 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(0));
2362 EXPECT_EQ(page_b2_contents
, strip
.GetWebContentsAt(1));
2363 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(2));
2364 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(3));
2365 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(4));
2368 strip
.ActivateTabAt(2, true);
2369 EXPECT_EQ(page_a_contents
, strip
.GetActiveWebContents());
2371 // Open page A2 in the background from A. It should end up after A1.
2372 WebContents
* page_a2_contents
= CreateWebContents();
2373 strip
.AddWebContents(page_a2_contents
, -1, content::PAGE_TRANSITION_LINK
,
2374 TabStripModel::ADD_NONE
);
2375 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(0));
2376 EXPECT_EQ(page_b2_contents
, strip
.GetWebContentsAt(1));
2377 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(2));
2378 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(3));
2379 EXPECT_EQ(page_a2_contents
, strip
.GetWebContentsAt(4));
2380 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(5));
2382 strip
.CloseAllTabs();
2385 TEST_F(TabStripModelTest
, CloseSelectedTabs
) {
2386 TabStripDummyDelegate delegate
;
2387 TabStripModel
strip(&delegate
, profile());
2388 WebContents
* contents1
= CreateWebContents();
2389 WebContents
* contents2
= CreateWebContents();
2390 WebContents
* contents3
= CreateWebContents();
2391 strip
.AppendWebContents(contents1
, true);
2392 strip
.AppendWebContents(contents2
, true);
2393 strip
.AppendWebContents(contents3
, true);
2394 strip
.ToggleSelectionAt(1);
2395 strip
.CloseSelectedTabs();
2396 EXPECT_EQ(1, strip
.count());
2397 EXPECT_EQ(0, strip
.active_index());
2398 strip
.CloseAllTabs();
2401 TEST_F(TabStripModelTest
, MultipleSelection
) {
2402 typedef MockTabStripModelObserver::State State
;
2404 TabStripDummyDelegate delegate
;
2405 TabStripModel
strip(&delegate
, profile());
2406 MockTabStripModelObserver
observer(&strip
);
2407 WebContents
* contents0
= CreateWebContents();
2408 WebContents
* contents1
= CreateWebContents();
2409 WebContents
* contents2
= CreateWebContents();
2410 WebContents
* contents3
= CreateWebContents();
2411 strip
.AppendWebContents(contents0
, false);
2412 strip
.AppendWebContents(contents1
, false);
2413 strip
.AppendWebContents(contents2
, false);
2414 strip
.AppendWebContents(contents3
, false);
2415 strip
.AddObserver(&observer
);
2417 // Selection and active tab change.
2418 strip
.ActivateTabAt(3, true);
2419 ASSERT_EQ(2, observer
.GetStateCount());
2420 ASSERT_EQ(observer
.GetStateAt(0).action
,
2421 MockTabStripModelObserver::ACTIVATE
);
2422 State
s1(contents3
, 3, MockTabStripModelObserver::SELECT
);
2423 EXPECT_TRUE(observer
.StateEquals(1, s1
));
2424 observer
.ClearStates();
2426 // Adding all tabs to selection, active tab is now at 0.
2427 strip
.ExtendSelectionTo(0);
2428 ASSERT_EQ(3, observer
.GetStateCount());
2429 ASSERT_EQ(observer
.GetStateAt(0).action
,
2430 MockTabStripModelObserver::DEACTIVATE
);
2431 ASSERT_EQ(observer
.GetStateAt(1).action
,
2432 MockTabStripModelObserver::ACTIVATE
);
2433 State
s2(contents0
, 0, MockTabStripModelObserver::SELECT
);
2434 s2
.src_contents
= contents3
;
2436 EXPECT_TRUE(observer
.StateEquals(2, s2
));
2437 observer
.ClearStates();
2439 // Toggle the active tab, should make the next index active.
2440 strip
.ToggleSelectionAt(0);
2441 EXPECT_EQ(1, strip
.active_index());
2442 EXPECT_EQ(3U, strip
.selection_model().size());
2443 EXPECT_EQ(4, strip
.count());
2444 ASSERT_EQ(3, observer
.GetStateCount());
2445 ASSERT_EQ(observer
.GetStateAt(0).action
,
2446 MockTabStripModelObserver::DEACTIVATE
);
2447 ASSERT_EQ(observer
.GetStateAt(1).action
,
2448 MockTabStripModelObserver::ACTIVATE
);
2449 ASSERT_EQ(observer
.GetStateAt(2).action
,
2450 MockTabStripModelObserver::SELECT
);
2451 observer
.ClearStates();
2453 // Toggle the first tab back to selected and active.
2454 strip
.ToggleSelectionAt(0);
2455 EXPECT_EQ(0, strip
.active_index());
2456 EXPECT_EQ(4U, strip
.selection_model().size());
2457 EXPECT_EQ(4, strip
.count());
2458 ASSERT_EQ(3, observer
.GetStateCount());
2459 ASSERT_EQ(observer
.GetStateAt(0).action
,
2460 MockTabStripModelObserver::DEACTIVATE
);
2461 ASSERT_EQ(observer
.GetStateAt(1).action
,
2462 MockTabStripModelObserver::ACTIVATE
);
2463 ASSERT_EQ(observer
.GetStateAt(2).action
,
2464 MockTabStripModelObserver::SELECT
);
2465 observer
.ClearStates();
2467 // Closing one of the selected tabs, not the active one.
2468 strip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
2469 EXPECT_EQ(3, strip
.count());
2470 ASSERT_EQ(3, observer
.GetStateCount());
2471 ASSERT_EQ(observer
.GetStateAt(0).action
,
2472 MockTabStripModelObserver::CLOSE
);
2473 ASSERT_EQ(observer
.GetStateAt(1).action
,
2474 MockTabStripModelObserver::DETACH
);
2475 ASSERT_EQ(observer
.GetStateAt(2).action
,
2476 MockTabStripModelObserver::SELECT
);
2477 observer
.ClearStates();
2479 // Closing the active tab, while there are others tabs selected.
2480 strip
.CloseWebContentsAt(0, TabStripModel::CLOSE_NONE
);
2481 EXPECT_EQ(2, strip
.count());
2482 ASSERT_EQ(5, observer
.GetStateCount());
2483 ASSERT_EQ(observer
.GetStateAt(0).action
,
2484 MockTabStripModelObserver::CLOSE
);
2485 ASSERT_EQ(observer
.GetStateAt(1).action
,
2486 MockTabStripModelObserver::DETACH
);
2487 ASSERT_EQ(observer
.GetStateAt(2).action
,
2488 MockTabStripModelObserver::DEACTIVATE
);
2489 ASSERT_EQ(observer
.GetStateAt(3).action
,
2490 MockTabStripModelObserver::ACTIVATE
);
2491 ASSERT_EQ(observer
.GetStateAt(4).action
,
2492 MockTabStripModelObserver::SELECT
);
2493 observer
.ClearStates();
2495 // Active tab is at 0, deselecting all but the active tab.
2496 strip
.ToggleSelectionAt(1);
2497 ASSERT_EQ(1, observer
.GetStateCount());
2498 ASSERT_EQ(observer
.GetStateAt(0).action
,
2499 MockTabStripModelObserver::SELECT
);
2500 observer
.ClearStates();
2502 // Attempting to deselect the only selected and therefore active tab,
2503 // it is ignored (no notifications being sent) and tab at 0 remains selected
2505 strip
.ToggleSelectionAt(0);
2506 ASSERT_EQ(0, observer
.GetStateCount());
2508 strip
.RemoveObserver(&observer
);
2509 strip
.CloseAllTabs();
2512 // Verifies that if we change the selection from a multi selection to a single
2513 // selection, but not in a way that changes the selected_index that
2514 // TabSelectionChanged is invoked.
2515 TEST_F(TabStripModelTest
, MultipleToSingle
) {
2516 typedef MockTabStripModelObserver::State State
;
2518 TabStripDummyDelegate delegate
;
2519 TabStripModel
strip(&delegate
, profile());
2520 WebContents
* contents1
= CreateWebContents();
2521 WebContents
* contents2
= CreateWebContents();
2522 strip
.AppendWebContents(contents1
, false);
2523 strip
.AppendWebContents(contents2
, false);
2524 strip
.ToggleSelectionAt(0);
2525 strip
.ToggleSelectionAt(1);
2527 MockTabStripModelObserver
observer(&strip
);
2528 strip
.AddObserver(&observer
);
2529 // This changes the selection (0 is no longer selected) but the selected_index
2530 // still remains at 1.
2531 strip
.ActivateTabAt(1, true);
2532 ASSERT_EQ(1, observer
.GetStateCount());
2533 State
s(contents2
, 1, MockTabStripModelObserver::SELECT
);
2534 s
.src_contents
= contents2
;
2536 s
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
2537 EXPECT_TRUE(observer
.StateEquals(0, s
));
2538 strip
.RemoveObserver(&observer
);
2539 strip
.CloseAllTabs();
2542 // Verifies a newly inserted tab retains its previous blocked state.
2543 // http://crbug.com/276334
2544 TEST_F(TabStripModelTest
, TabBlockedState
) {
2545 // Start with a source tab strip.
2546 TabStripDummyDelegate dummy_tab_strip_delegate
;
2547 TabStripModel
strip_src(&dummy_tab_strip_delegate
, profile());
2548 TabBlockedStateTestBrowser
browser_src(&strip_src
);
2551 WebContents
* contents1
= CreateWebContents();
2552 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1
);
2553 strip_src
.AppendWebContents(contents1
, false);
2556 WebContents
* contents2
= CreateWebContents();
2557 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents2
);
2558 strip_src
.AppendWebContents(contents2
, false);
2560 // Create a destination tab strip.
2561 TabStripModel
strip_dst(&dummy_tab_strip_delegate
, profile());
2562 TabBlockedStateTestBrowser
browser_dst(&strip_dst
);
2564 // Setup a SingleWebContentsDialogManager for tab |contents2|.
2565 web_modal::WebContentsModalDialogManager
* modal_dialog_manager
=
2566 web_modal::WebContentsModalDialogManager::FromWebContents(contents2
);
2567 web_modal::PopupManager
popup_manager(NULL
);
2568 popup_manager
.RegisterWith(contents2
);
2570 // Show a dialog that blocks tab |contents2|.
2571 // DummySingleWebContentsDialogManager doesn't care about the
2572 // NativeWebContentsModalDialog value, so any dummy value works.
2573 DummySingleWebContentsDialogManager
* native_manager
=
2574 new DummySingleWebContentsDialogManager(
2575 reinterpret_cast<NativeWebContentsModalDialog
>(0),
2576 modal_dialog_manager
);
2577 modal_dialog_manager
->ShowDialogWithManager(
2578 reinterpret_cast<NativeWebContentsModalDialog
>(0),
2579 scoped_ptr
<web_modal::SingleWebContentsDialogManager
>(
2580 native_manager
).Pass());
2581 EXPECT_TRUE(strip_src
.IsTabBlocked(1));
2584 WebContents
* moved_contents
= strip_src
.DetachWebContentsAt(1);
2585 EXPECT_EQ(contents2
, moved_contents
);
2587 // Attach the tab to the destination tab strip.
2588 strip_dst
.AppendWebContents(moved_contents
, true);
2589 EXPECT_TRUE(strip_dst
.IsTabBlocked(0));
2591 strip_dst
.CloseAllTabs();
2592 strip_src
.CloseAllTabs();