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 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 ~TabStripDummyDelegate() override
{}
83 void set_run_unload_listener(bool value
) { run_unload_
= value
; }
85 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 ~TabStripModelTestIDUserData() override
{}
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 ~DummySingleWebContentsDialogManager() override
{}
118 void Show() override
{}
119 void Hide() override
{}
120 void Close() override
{ delegate_
->WillClose(dialog_
); }
121 void Focus() override
{}
122 void Pulse() override
{}
123 void HostChanged(web_modal::WebContentsModalDialogHost
* new_host
) override
{}
124 NativeWebContentsModalDialog
dialog() override
{ return dialog_
; }
127 web_modal::SingleWebContentsDialogManagerDelegate
* delegate_
;
128 NativeWebContentsModalDialog dialog_
;
130 DISALLOW_COPY_AND_ASSIGN(DummySingleWebContentsDialogManager
);
133 // Test Browser-like class for TabStripModelTest.TabBlockedState.
134 class TabBlockedStateTestBrowser
135 : public TabStripModelObserver
,
136 public web_modal::WebContentsModalDialogManagerDelegate
{
138 explicit TabBlockedStateTestBrowser(TabStripModel
* tab_strip_model
)
139 : tab_strip_model_(tab_strip_model
) {
140 tab_strip_model_
->AddObserver(this);
143 ~TabBlockedStateTestBrowser() override
{
144 tab_strip_model_
->RemoveObserver(this);
148 // TabStripModelObserver
149 void TabInsertedAt(WebContents
* contents
,
151 bool foreground
) override
{
152 web_modal::WebContentsModalDialogManager
* manager
=
153 web_modal::WebContentsModalDialogManager::FromWebContents(contents
);
155 manager
->SetDelegate(this);
158 // WebContentsModalDialogManagerDelegate
159 void SetWebContentsBlocked(content::WebContents
* contents
,
160 bool blocked
) override
{
161 int index
= tab_strip_model_
->GetIndexOfWebContents(contents
);
163 tab_strip_model_
->SetTabBlocked(index
, blocked
);
166 TabStripModel
* tab_strip_model_
;
168 DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser
);
173 class TabStripModelTest
: public ChromeRenderViewHostTestHarness
{
175 WebContents
* CreateWebContents() {
176 return WebContents::Create(WebContents::CreateParams(profile()));
179 WebContents
* CreateWebContentsWithSharedRPH(WebContents
* web_contents
) {
180 WebContents::CreateParams
create_params(
181 profile(), web_contents
->GetRenderViewHost()->GetSiteInstance());
182 WebContents
* retval
= WebContents::Create(create_params
);
183 EXPECT_EQ(retval
->GetRenderProcessHost(),
184 web_contents
->GetRenderProcessHost());
188 // Sets the id of the specified contents.
189 void SetID(WebContents
* contents
, int id
) {
190 contents
->SetUserData(&kTabStripModelTestIDUserDataKey
,
191 new TabStripModelTestIDUserData(id
));
194 // Returns the id of the specified contents.
195 int GetID(WebContents
* contents
) {
196 TabStripModelTestIDUserData
* user_data
=
197 static_cast<TabStripModelTestIDUserData
*>(
198 contents
->GetUserData(&kTabStripModelTestIDUserDataKey
));
200 return user_data
? user_data
->id() : -1;
203 // Returns the state of the given tab strip as a string. The state consists
204 // of the ID of each web contents followed by a 'p' if pinned. For example,
205 // if the model consists of two tabs with ids 2 and 1, with the first
206 // tab pinned, this returns "2p 1".
207 std::string
GetTabStripStateString(const TabStripModel
& model
) {
209 for (int i
= 0; i
< model
.count(); ++i
) {
213 actual
+= base::IntToString(GetID(model
.GetWebContentsAt(i
)));
215 if (model
.IsAppTab(i
))
218 if (model
.IsTabPinned(i
))
224 std::string
GetIndicesClosedByCommandAsString(
225 const TabStripModel
& model
,
227 TabStripModel::ContextMenuCommand id
) const {
228 std::vector
<int> indices
= model
.GetIndicesClosedByCommand(index
, id
);
230 for (size_t i
= 0; i
< indices
.size(); ++i
) {
233 result
+= base::IntToString(indices
[i
]);
238 void PrepareTabstripForSelectionTest(TabStripModel
* model
,
241 const std::string
& selected_tabs
) {
242 for (int i
= 0; i
< tab_count
; ++i
) {
243 WebContents
* contents
= CreateWebContents();
245 model
->AppendWebContents(contents
, true);
247 for (int i
= 0; i
< pinned_count
; ++i
)
248 model
->SetTabPinned(i
, true);
250 ui::ListSelectionModel selection_model
;
251 std::vector
<std::string
> selection
;
252 base::SplitStringAlongWhitespace(selected_tabs
, &selection
);
253 for (size_t i
= 0; i
< selection
.size(); ++i
) {
255 ASSERT_TRUE(base::StringToInt(selection
[i
], &value
));
256 selection_model
.AddIndexToSelection(value
);
258 selection_model
.set_active(selection_model
.selected_indices()[0]);
259 model
->SetSelectionFromModel(selection_model
);
263 class MockTabStripModelObserver
: public TabStripModelObserver
{
265 explicit MockTabStripModelObserver(TabStripModel
* model
)
269 ~MockTabStripModelObserver() override
{}
271 enum TabStripModelObserverAction
{
287 State(WebContents
* a_dst_contents
,
289 TabStripModelObserverAction a_action
)
290 : src_contents(NULL
),
291 dst_contents(a_dst_contents
),
293 dst_index(a_dst_index
),
294 change_reason(CHANGE_REASON_NONE
),
299 WebContents
* src_contents
;
300 WebContents
* dst_contents
;
305 TabStripModelObserverAction action
;
308 int GetStateCount() const {
309 return static_cast<int>(states_
.size());
312 // Returns (by way of parameters) the number of state's with CLOSE_ALL and
313 // CLOSE_ALL_CANCELED.
314 void GetCloseCounts(int* close_all_count
,
315 int* close_all_canceled_count
) {
316 *close_all_count
= *close_all_canceled_count
= 0;
317 for (int i
= 0; i
< GetStateCount(); ++i
) {
318 switch (GetStateAt(i
).action
) {
320 (*close_all_count
)++;
322 case CLOSE_ALL_CANCELED
:
323 (*close_all_canceled_count
)++;
331 const State
& GetStateAt(int index
) const {
332 DCHECK(index
>= 0 && index
< GetStateCount());
333 return states_
[index
];
336 bool StateEquals(int index
, const State
& state
) {
337 const State
& s
= GetStateAt(index
);
338 return (s
.src_contents
== state
.src_contents
&&
339 s
.dst_contents
== state
.dst_contents
&&
340 s
.src_index
== state
.src_index
&&
341 s
.dst_index
== state
.dst_index
&&
342 s
.change_reason
== state
.change_reason
&&
343 s
.foreground
== state
.foreground
&&
344 s
.action
== state
.action
);
347 // TabStripModelObserver implementation:
348 void TabInsertedAt(WebContents
* contents
,
350 bool foreground
) override
{
352 State
s(contents
, index
, INSERT
);
353 s
.foreground
= foreground
;
354 states_
.push_back(s
);
356 void ActiveTabChanged(WebContents
* old_contents
,
357 WebContents
* new_contents
,
359 int reason
) override
{
360 State
s(new_contents
, index
, ACTIVATE
);
361 s
.src_contents
= old_contents
;
362 s
.change_reason
= reason
;
363 states_
.push_back(s
);
365 void TabSelectionChanged(TabStripModel
* tab_strip_model
,
366 const ui::ListSelectionModel
& old_model
) override
{
367 State
s(model()->GetActiveWebContents(), model()->active_index(), SELECT
);
368 s
.src_contents
= model()->GetWebContentsAt(old_model
.active());
369 s
.src_index
= old_model
.active();
370 states_
.push_back(s
);
372 void TabMoved(WebContents
* contents
, int from_index
, int to_index
) override
{
373 State
s(contents
, to_index
, MOVE
);
374 s
.src_index
= from_index
;
375 states_
.push_back(s
);
378 void TabClosingAt(TabStripModel
* tab_strip_model
,
379 WebContents
* contents
,
380 int index
) override
{
381 states_
.push_back(State(contents
, index
, CLOSE
));
383 void TabDetachedAt(WebContents
* contents
, int index
) override
{
384 states_
.push_back(State(contents
, index
, DETACH
));
386 void TabDeactivated(WebContents
* contents
) override
{
387 states_
.push_back(State(contents
, model()->active_index(), DEACTIVATE
));
389 void TabChangedAt(WebContents
* contents
,
391 TabChangeType change_type
) override
{
392 states_
.push_back(State(contents
, index
, CHANGE
));
394 void TabReplacedAt(TabStripModel
* tab_strip_model
,
395 WebContents
* old_contents
,
396 WebContents
* new_contents
,
397 int index
) override
{
398 State
s(new_contents
, index
, REPLACED
);
399 s
.src_contents
= old_contents
;
400 states_
.push_back(s
);
402 void TabPinnedStateChanged(WebContents
* contents
, int index
) override
{
403 states_
.push_back(State(contents
, index
, PINNED
));
405 void TabStripEmpty() override
{ empty_
= true; }
406 void WillCloseAllTabs() override
{
407 states_
.push_back(State(NULL
, -1, CLOSE_ALL
));
409 void CloseAllTabsCanceled() override
{
410 states_
.push_back(State(NULL
, -1, CLOSE_ALL_CANCELED
));
412 void TabStripModelDeleted() override
{ deleted_
= true; }
418 bool empty() const { return empty_
; }
419 bool deleted() const { return deleted_
; }
420 TabStripModel
* model() { return model_
; }
423 std::vector
<State
> states_
;
427 TabStripModel
* model_
;
429 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver
);
432 TEST_F(TabStripModelTest
, TestBasicAPI
) {
433 TabStripDummyDelegate delegate
;
434 TabStripModel
tabstrip(&delegate
, profile());
435 MockTabStripModelObserver
observer(&tabstrip
);
436 tabstrip
.AddObserver(&observer
);
438 EXPECT_TRUE(tabstrip
.empty());
440 typedef MockTabStripModelObserver::State State
;
442 WebContents
* contents1
= CreateWebContents();
445 // Note! The ordering of these tests is important, each subsequent test
446 // builds on the state established in the previous. This is important if you
447 // ever insert tests rather than append.
449 // Test AppendWebContents, ContainsIndex
451 EXPECT_FALSE(tabstrip
.ContainsIndex(0));
452 tabstrip
.AppendWebContents(contents1
, true);
453 EXPECT_TRUE(tabstrip
.ContainsIndex(0));
454 EXPECT_EQ(1, tabstrip
.count());
455 EXPECT_EQ(3, observer
.GetStateCount());
456 State
s1(contents1
, 0, MockTabStripModelObserver::INSERT
);
457 s1
.foreground
= true;
458 EXPECT_TRUE(observer
.StateEquals(0, s1
));
459 State
s2(contents1
, 0, MockTabStripModelObserver::ACTIVATE
);
460 EXPECT_TRUE(observer
.StateEquals(1, s2
));
461 State
s3(contents1
, 0, MockTabStripModelObserver::SELECT
);
462 s3
.src_contents
= NULL
;
463 s3
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
464 EXPECT_TRUE(observer
.StateEquals(2, s3
));
465 observer
.ClearStates();
467 EXPECT_EQ("1", GetTabStripStateString(tabstrip
));
469 // Test InsertWebContentsAt, foreground tab.
470 WebContents
* contents2
= CreateWebContents();
473 tabstrip
.InsertWebContentsAt(1, contents2
, TabStripModel::ADD_ACTIVE
);
475 EXPECT_EQ(2, tabstrip
.count());
476 EXPECT_EQ(4, observer
.GetStateCount());
477 State
s1(contents2
, 1, MockTabStripModelObserver::INSERT
);
478 s1
.foreground
= true;
479 EXPECT_TRUE(observer
.StateEquals(0, s1
));
480 State
s2(contents1
, 0, MockTabStripModelObserver::DEACTIVATE
);
481 EXPECT_TRUE(observer
.StateEquals(1, s2
));
482 State
s3(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
483 s3
.src_contents
= contents1
;
484 EXPECT_TRUE(observer
.StateEquals(2, s3
));
485 State
s4(contents2
, 1, MockTabStripModelObserver::SELECT
);
486 s4
.src_contents
= contents1
;
488 EXPECT_TRUE(observer
.StateEquals(3, s4
));
489 observer
.ClearStates();
491 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip
));
493 // Test InsertWebContentsAt, background tab.
494 WebContents
* contents3
= CreateWebContents();
497 tabstrip
.InsertWebContentsAt(2, contents3
, TabStripModel::ADD_NONE
);
499 EXPECT_EQ(3, tabstrip
.count());
500 EXPECT_EQ(1, observer
.GetStateCount());
501 State
s1(contents3
, 2, MockTabStripModelObserver::INSERT
);
502 s1
.foreground
= false;
503 EXPECT_TRUE(observer
.StateEquals(0, s1
));
504 observer
.ClearStates();
506 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
508 // Test ActivateTabAt
510 tabstrip
.ActivateTabAt(2, true);
511 EXPECT_EQ(3, observer
.GetStateCount());
512 State
s1(contents2
, 1, MockTabStripModelObserver::DEACTIVATE
);
513 EXPECT_TRUE(observer
.StateEquals(0, s1
));
514 State
s2(contents3
, 2, MockTabStripModelObserver::ACTIVATE
);
515 s2
.src_contents
= contents2
;
516 s2
.change_reason
= TabStripModelObserver::CHANGE_REASON_USER_GESTURE
;
517 EXPECT_TRUE(observer
.StateEquals(1, s2
));
518 State
s3(contents3
, 2, MockTabStripModelObserver::SELECT
);
519 s3
.src_contents
= contents2
;
521 EXPECT_TRUE(observer
.StateEquals(2, s3
));
522 observer
.ClearStates();
524 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
526 // Test DetachWebContentsAt
529 WebContents
* detached
= tabstrip
.DetachWebContentsAt(2);
530 // ... and append again because we want this for later.
531 tabstrip
.AppendWebContents(detached
, true);
532 EXPECT_EQ(8, observer
.GetStateCount());
533 State
s1(detached
, 2, MockTabStripModelObserver::DETACH
);
534 EXPECT_TRUE(observer
.StateEquals(0, s1
));
535 State
s2(detached
, ui::ListSelectionModel::kUnselectedIndex
,
536 MockTabStripModelObserver::DEACTIVATE
);
537 EXPECT_TRUE(observer
.StateEquals(1, s2
));
538 State
s3(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
539 s3
.src_contents
= contents3
;
540 s3
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
541 EXPECT_TRUE(observer
.StateEquals(2, s3
));
542 State
s4(contents2
, 1, MockTabStripModelObserver::SELECT
);
543 s4
.src_contents
= NULL
;
544 s4
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
545 EXPECT_TRUE(observer
.StateEquals(3, s4
));
546 State
s5(detached
, 2, MockTabStripModelObserver::INSERT
);
547 s5
.foreground
= true;
548 EXPECT_TRUE(observer
.StateEquals(4, s5
));
549 State
s6(contents2
, 1, MockTabStripModelObserver::DEACTIVATE
);
550 EXPECT_TRUE(observer
.StateEquals(5, s6
));
551 State
s7(detached
, 2, MockTabStripModelObserver::ACTIVATE
);
552 s7
.src_contents
= contents2
;
553 s7
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
554 EXPECT_TRUE(observer
.StateEquals(6, s7
));
555 State
s8(detached
, 2, MockTabStripModelObserver::SELECT
);
556 s8
.src_contents
= contents2
;
558 EXPECT_TRUE(observer
.StateEquals(7, s8
));
559 observer
.ClearStates();
561 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
563 // Test CloseWebContentsAt
565 EXPECT_TRUE(tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
));
566 EXPECT_EQ(2, tabstrip
.count());
568 EXPECT_EQ(5, observer
.GetStateCount());
569 State
s1(contents3
, 2, MockTabStripModelObserver::CLOSE
);
570 EXPECT_TRUE(observer
.StateEquals(0, s1
));
571 State
s2(contents3
, 2, MockTabStripModelObserver::DETACH
);
572 EXPECT_TRUE(observer
.StateEquals(1, s2
));
573 State
s3(contents3
, ui::ListSelectionModel::kUnselectedIndex
,
574 MockTabStripModelObserver::DEACTIVATE
);
575 EXPECT_TRUE(observer
.StateEquals(2, s3
));
576 State
s4(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
577 s4
.src_contents
= contents3
;
578 s4
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
579 EXPECT_TRUE(observer
.StateEquals(3, s4
));
580 State
s5(contents2
, 1, MockTabStripModelObserver::SELECT
);
581 s5
.src_contents
= NULL
;
582 s5
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
583 EXPECT_TRUE(observer
.StateEquals(4, s5
));
584 observer
.ClearStates();
586 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip
));
588 // Test MoveWebContentsAt, select_after_move == true
590 tabstrip
.MoveWebContentsAt(1, 0, true);
592 EXPECT_EQ(1, observer
.GetStateCount());
593 State
s1(contents2
, 0, MockTabStripModelObserver::MOVE
);
595 EXPECT_TRUE(observer
.StateEquals(0, s1
));
596 EXPECT_EQ(0, tabstrip
.active_index());
597 observer
.ClearStates();
599 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip
));
601 // Test MoveWebContentsAt, select_after_move == false
603 tabstrip
.MoveWebContentsAt(1, 0, false);
604 EXPECT_EQ(1, observer
.GetStateCount());
605 State
s1(contents1
, 0, MockTabStripModelObserver::MOVE
);
607 EXPECT_TRUE(observer
.StateEquals(0, s1
));
608 EXPECT_EQ(1, tabstrip
.active_index());
610 tabstrip
.MoveWebContentsAt(0, 1, false);
611 observer
.ClearStates();
613 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip
));
617 EXPECT_EQ(contents2
, tabstrip
.GetActiveWebContents());
618 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(0));
619 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
620 EXPECT_EQ(0, tabstrip
.GetIndexOfWebContents(contents2
));
621 EXPECT_EQ(1, tabstrip
.GetIndexOfWebContents(contents1
));
624 // Test UpdateWebContentsStateAt
626 tabstrip
.UpdateWebContentsStateAt(0, TabStripModelObserver::ALL
);
627 EXPECT_EQ(1, observer
.GetStateCount());
628 State
s1(contents2
, 0, MockTabStripModelObserver::CHANGE
);
629 EXPECT_TRUE(observer
.StateEquals(0, s1
));
630 observer
.ClearStates();
633 // Test SelectNextTab, SelectPreviousTab, SelectLastTab
635 // Make sure the second of the two tabs is selected first...
636 tabstrip
.ActivateTabAt(1, true);
637 tabstrip
.SelectPreviousTab();
638 EXPECT_EQ(0, tabstrip
.active_index());
639 tabstrip
.SelectLastTab();
640 EXPECT_EQ(1, tabstrip
.active_index());
641 tabstrip
.SelectNextTab();
642 EXPECT_EQ(0, tabstrip
.active_index());
645 // Test CloseSelectedTabs
647 tabstrip
.CloseSelectedTabs();
648 // |CloseSelectedTabs| calls CloseWebContentsAt, we already tested that, now
649 // just verify that the count and selected index have changed
651 EXPECT_EQ(1, tabstrip
.count());
652 EXPECT_EQ(0, tabstrip
.active_index());
655 observer
.ClearStates();
656 tabstrip
.CloseAllTabs();
658 int close_all_count
= 0, close_all_canceled_count
= 0;
659 observer
.GetCloseCounts(&close_all_count
, &close_all_canceled_count
);
660 EXPECT_EQ(1, close_all_count
);
661 EXPECT_EQ(0, close_all_canceled_count
);
663 // TabStripModel should now be empty.
664 EXPECT_TRUE(tabstrip
.empty());
666 // Opener methods are tested below...
668 tabstrip
.RemoveObserver(&observer
);
671 TEST_F(TabStripModelTest
, TestBasicOpenerAPI
) {
672 TabStripDummyDelegate delegate
;
673 TabStripModel
tabstrip(&delegate
, profile());
674 EXPECT_TRUE(tabstrip
.empty());
676 // This is a basic test of opener functionality. opener is created
677 // as the first tab in the strip and then we create 5 other tabs in the
678 // background with opener set as their opener.
680 WebContents
* opener
= CreateWebContents();
681 tabstrip
.AppendWebContents(opener
, true);
682 WebContents
* contents1
= CreateWebContents();
683 WebContents
* contents2
= CreateWebContents();
684 WebContents
* contents3
= CreateWebContents();
685 WebContents
* contents4
= CreateWebContents();
686 WebContents
* contents5
= CreateWebContents();
688 // We use |InsertWebContentsAt| here instead of |AppendWebContents| so that
689 // openership relationships are preserved.
690 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents1
,
691 TabStripModel::ADD_INHERIT_GROUP
);
692 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents2
,
693 TabStripModel::ADD_INHERIT_GROUP
);
694 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents3
,
695 TabStripModel::ADD_INHERIT_GROUP
);
696 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents4
,
697 TabStripModel::ADD_INHERIT_GROUP
);
698 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents5
,
699 TabStripModel::ADD_INHERIT_GROUP
);
701 // All the tabs should have the same opener.
702 for (int i
= 1; i
< tabstrip
.count(); ++i
)
703 EXPECT_EQ(opener
, tabstrip
.GetOpenerOfWebContentsAt(i
));
705 // If there is a next adjacent item, then the index should be of that item.
706 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 1, false));
707 // If the last tab in the group is closed, the preceding tab in the same
708 // group should be selected.
709 EXPECT_EQ(4, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 5, false));
711 // Tests the method that finds the last tab opened by the same opener in the
712 // strip (this is the insertion index for the next background tab for the
713 // specified opener).
714 EXPECT_EQ(5, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
716 // For a tab that has opened no other tabs, the return value should always be
719 tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents1
, 3, false));
721 tabstrip
.GetIndexOfLastWebContentsOpenedBy(contents1
, 3));
723 // ForgetAllOpeners should destroy all opener relationships.
724 tabstrip
.ForgetAllOpeners();
725 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 1, false));
726 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 5, false));
727 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
729 // Specify the last tab as the opener of the others.
730 for (int i
= 0; i
< tabstrip
.count() - 1; ++i
)
731 tabstrip
.SetOpenerOfWebContentsAt(i
, contents5
);
733 for (int i
= 0; i
< tabstrip
.count() - 1; ++i
)
734 EXPECT_EQ(contents5
, tabstrip
.GetOpenerOfWebContentsAt(i
));
736 // If there is a next adjacent item, then the index should be of that item.
737 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents5
, 1, false));
739 // If the last tab in the group is closed, the preceding tab in the same
740 // group should be selected.
741 EXPECT_EQ(3, tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents5
, 4, false));
743 tabstrip
.CloseAllTabs();
744 EXPECT_TRUE(tabstrip
.empty());
747 static int GetInsertionIndex(TabStripModel
* tabstrip
) {
748 return tabstrip
->order_controller()->DetermineInsertionIndex(
749 ui::PAGE_TRANSITION_LINK
, false);
752 static void InsertWebContentses(TabStripModel
* tabstrip
,
753 WebContents
* contents1
,
754 WebContents
* contents2
,
755 WebContents
* contents3
) {
756 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
758 TabStripModel::ADD_INHERIT_GROUP
);
759 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
761 TabStripModel::ADD_INHERIT_GROUP
);
762 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
764 TabStripModel::ADD_INHERIT_GROUP
);
767 // Tests opening background tabs.
768 TEST_F(TabStripModelTest
, TestLTRInsertionOptions
) {
769 TabStripDummyDelegate delegate
;
770 TabStripModel
tabstrip(&delegate
, profile());
771 EXPECT_TRUE(tabstrip
.empty());
773 WebContents
* opener
= CreateWebContents();
774 tabstrip
.AppendWebContents(opener
, true);
776 WebContents
* contents1
= CreateWebContents();
777 WebContents
* contents2
= CreateWebContents();
778 WebContents
* contents3
= CreateWebContents();
781 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
782 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
783 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(2));
784 EXPECT_EQ(contents3
, tabstrip
.GetWebContentsAt(3));
786 tabstrip
.CloseAllTabs();
787 EXPECT_TRUE(tabstrip
.empty());
790 // This test constructs a tabstrip, and then simulates loading several tabs in
791 // the background from link clicks on the first tab. Then it simulates opening
792 // a new tab from the first tab in the foreground via a link click, verifies
793 // that this tab is opened adjacent to the opener, then closes it.
794 // Finally it tests that a tab opened for some non-link purpose opens at the
795 // end of the strip, not bundled to any existing context.
796 TEST_F(TabStripModelTest
, TestInsertionIndexDetermination
) {
797 TabStripDummyDelegate delegate
;
798 TabStripModel
tabstrip(&delegate
, profile());
799 EXPECT_TRUE(tabstrip
.empty());
801 WebContents
* opener
= CreateWebContents();
802 tabstrip
.AppendWebContents(opener
, true);
804 // Open some other random unrelated tab in the background to monkey with our
806 WebContents
* other
= CreateWebContents();
807 tabstrip
.AppendWebContents(other
, false);
809 WebContents
* contents1
= CreateWebContents();
810 WebContents
* contents2
= CreateWebContents();
811 WebContents
* contents3
= CreateWebContents();
813 // Start by testing LTR.
814 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
815 EXPECT_EQ(opener
, tabstrip
.GetWebContentsAt(0));
816 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
817 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(2));
818 EXPECT_EQ(contents3
, tabstrip
.GetWebContentsAt(3));
819 EXPECT_EQ(other
, tabstrip
.GetWebContentsAt(4));
821 // The opener API should work...
822 EXPECT_EQ(3, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 2, false));
823 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
824 EXPECT_EQ(3, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
826 // Now open a foreground tab from a link. It should be opened adjacent to the
828 WebContents
* fg_link_contents
= CreateWebContents();
829 int insert_index
= tabstrip
.order_controller()->DetermineInsertionIndex(
830 ui::PAGE_TRANSITION_LINK
, true);
831 EXPECT_EQ(1, insert_index
);
832 tabstrip
.InsertWebContentsAt(insert_index
, fg_link_contents
,
833 TabStripModel::ADD_ACTIVE
|
834 TabStripModel::ADD_INHERIT_GROUP
);
835 EXPECT_EQ(1, tabstrip
.active_index());
836 EXPECT_EQ(fg_link_contents
, tabstrip
.GetActiveWebContents());
838 // Now close this contents. The selection should move to the opener contents.
839 tabstrip
.CloseSelectedTabs();
840 EXPECT_EQ(0, tabstrip
.active_index());
842 // Now open a new empty tab. It should open at the end of the strip.
843 WebContents
* fg_nonlink_contents
= CreateWebContents();
844 insert_index
= tabstrip
.order_controller()->DetermineInsertionIndex(
845 ui::PAGE_TRANSITION_AUTO_BOOKMARK
, true);
846 EXPECT_EQ(tabstrip
.count(), insert_index
);
847 // We break the opener relationship...
848 tabstrip
.InsertWebContentsAt(insert_index
,
850 TabStripModel::ADD_NONE
);
851 // Now select it, so that user_gesture == true causes the opener relationship
852 // to be forgotten...
853 tabstrip
.ActivateTabAt(tabstrip
.count() - 1, true);
854 EXPECT_EQ(tabstrip
.count() - 1, tabstrip
.active_index());
855 EXPECT_EQ(fg_nonlink_contents
, tabstrip
.GetActiveWebContents());
857 // Verify that all opener relationships are forgotten.
858 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 2, false));
859 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
860 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
861 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
863 tabstrip
.CloseAllTabs();
864 EXPECT_TRUE(tabstrip
.empty());
867 // Tests that selection is shifted to the correct tab when a tab is closed.
868 // If a tab is in the background when it is closed, the selection does not
870 // If a tab is in the foreground (selected),
871 // If that tab does not have an opener, selection shifts to the right.
872 // If the tab has an opener,
873 // The next tab (scanning LTR) in the entire strip that has the same opener
875 // If there are no other tabs that have the same opener,
876 // The opener is selected
878 TEST_F(TabStripModelTest
, TestSelectOnClose
) {
879 TabStripDummyDelegate delegate
;
880 TabStripModel
tabstrip(&delegate
, profile());
881 EXPECT_TRUE(tabstrip
.empty());
883 WebContents
* opener
= CreateWebContents();
884 tabstrip
.AppendWebContents(opener
, true);
886 WebContents
* contents1
= CreateWebContents();
887 WebContents
* contents2
= CreateWebContents();
888 WebContents
* contents3
= CreateWebContents();
890 // Note that we use Detach instead of Close throughout this test to avoid
891 // having to keep reconstructing these WebContentses.
893 // First test that closing tabs that are in the background doesn't adjust the
894 // current selection.
895 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
896 EXPECT_EQ(0, tabstrip
.active_index());
898 tabstrip
.DetachWebContentsAt(1);
899 EXPECT_EQ(0, tabstrip
.active_index());
901 for (int i
= tabstrip
.count() - 1; i
>= 1; --i
)
902 tabstrip
.DetachWebContentsAt(i
);
904 // Now test that when a tab doesn't have an opener, selection shifts to the
905 // right when the tab is closed.
906 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
907 EXPECT_EQ(0, tabstrip
.active_index());
909 tabstrip
.ForgetAllOpeners();
910 tabstrip
.ActivateTabAt(1, true);
911 EXPECT_EQ(1, tabstrip
.active_index());
912 tabstrip
.DetachWebContentsAt(1);
913 EXPECT_EQ(1, tabstrip
.active_index());
914 tabstrip
.DetachWebContentsAt(1);
915 EXPECT_EQ(1, tabstrip
.active_index());
916 tabstrip
.DetachWebContentsAt(1);
917 EXPECT_EQ(0, tabstrip
.active_index());
919 for (int i
= tabstrip
.count() - 1; i
>= 1; --i
)
920 tabstrip
.DetachWebContentsAt(i
);
922 // Now test that when a tab does have an opener, it selects the next tab
923 // opened by the same opener scanning LTR when it is closed.
924 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
925 EXPECT_EQ(0, tabstrip
.active_index());
926 tabstrip
.ActivateTabAt(2, false);
927 EXPECT_EQ(2, tabstrip
.active_index());
928 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
929 EXPECT_EQ(2, tabstrip
.active_index());
930 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
931 EXPECT_EQ(1, tabstrip
.active_index());
932 tabstrip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
933 EXPECT_EQ(0, tabstrip
.active_index());
934 // Finally test that when a tab has no "siblings" that the opener is
936 WebContents
* other_contents
= CreateWebContents();
937 tabstrip
.InsertWebContentsAt(1, other_contents
,
938 TabStripModel::ADD_NONE
);
939 EXPECT_EQ(2, tabstrip
.count());
940 WebContents
* opened_contents
= CreateWebContents();
941 tabstrip
.InsertWebContentsAt(2, opened_contents
,
942 TabStripModel::ADD_ACTIVE
|
943 TabStripModel::ADD_INHERIT_GROUP
);
944 EXPECT_EQ(2, tabstrip
.active_index());
945 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
946 EXPECT_EQ(0, tabstrip
.active_index());
948 tabstrip
.CloseAllTabs();
949 EXPECT_TRUE(tabstrip
.empty());
952 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
954 TEST_F(TabStripModelTest
, CommandCloseTab
) {
955 TabStripDummyDelegate delegate
;
956 TabStripModel
tabstrip(&delegate
, profile());
957 EXPECT_TRUE(tabstrip
.empty());
959 // Make sure can_close is honored.
960 ASSERT_NO_FATAL_FAILURE(
961 PrepareTabstripForSelectionTest(&tabstrip
, 1, 0, "0"));
962 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
963 0, TabStripModel::CommandCloseTab
));
964 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
965 ASSERT_TRUE(tabstrip
.empty());
967 // Make sure close on a tab that is selected affects all the selected tabs.
968 ASSERT_NO_FATAL_FAILURE(
969 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
970 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
971 0, TabStripModel::CommandCloseTab
));
972 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
973 // Should have closed tabs 0 and 1.
974 EXPECT_EQ("2", GetTabStripStateString(tabstrip
));
976 tabstrip
.CloseAllTabs();
977 EXPECT_TRUE(tabstrip
.empty());
979 // Select two tabs and make close on a tab that isn't selected doesn't affect
981 ASSERT_NO_FATAL_FAILURE(
982 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
983 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
984 2, TabStripModel::CommandCloseTab
));
985 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab
);
986 // Should have closed tab 2.
987 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
988 tabstrip
.CloseAllTabs();
989 EXPECT_TRUE(tabstrip
.empty());
991 // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned.
992 ASSERT_NO_FATAL_FAILURE(
993 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "0 1"));
994 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
995 0, TabStripModel::CommandCloseTab
));
996 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
997 // Should have closed tab 2.
998 EXPECT_EQ("2", GetTabStripStateString(tabstrip
));
999 tabstrip
.CloseAllTabs();
1000 EXPECT_TRUE(tabstrip
.empty());
1003 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1004 // CommandCloseTabs.
1005 TEST_F(TabStripModelTest
, CommandCloseOtherTabs
) {
1006 TabStripDummyDelegate delegate
;
1007 TabStripModel
tabstrip(&delegate
, profile());
1008 EXPECT_TRUE(tabstrip
.empty());
1010 // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled
1011 // and close two tabs.
1012 ASSERT_NO_FATAL_FAILURE(
1013 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
1014 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1015 0, TabStripModel::CommandCloseOtherTabs
));
1016 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs
);
1017 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
1018 tabstrip
.CloseAllTabs();
1019 EXPECT_TRUE(tabstrip
.empty());
1021 // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it
1022 // with a non-selected index should close the two other tabs.
1023 ASSERT_NO_FATAL_FAILURE(
1024 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
1025 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1026 2, 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 all, CommandCloseOtherTabs should not be enabled.
1033 ASSERT_NO_FATAL_FAILURE(
1034 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1 2"));
1035 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1036 2, TabStripModel::CommandCloseOtherTabs
));
1037 tabstrip
.CloseAllTabs();
1038 EXPECT_TRUE(tabstrip
.empty());
1040 // Three tabs, pin one, select the two non-pinned.
1041 ASSERT_NO_FATAL_FAILURE(
1042 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "1 2"));
1043 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1044 1, TabStripModel::CommandCloseOtherTabs
));
1045 // If we don't pass in the pinned index, the command should be enabled.
1046 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1047 0, TabStripModel::CommandCloseOtherTabs
));
1048 tabstrip
.CloseAllTabs();
1049 EXPECT_TRUE(tabstrip
.empty());
1051 // 3 tabs, one pinned.
1052 ASSERT_NO_FATAL_FAILURE(
1053 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "1"));
1054 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1055 1, TabStripModel::CommandCloseOtherTabs
));
1056 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1057 0, TabStripModel::CommandCloseOtherTabs
));
1058 tabstrip
.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs
);
1059 // The pinned tab shouldn't be closed.
1060 EXPECT_EQ("0p 1", GetTabStripStateString(tabstrip
));
1061 tabstrip
.CloseAllTabs();
1062 EXPECT_TRUE(tabstrip
.empty());
1065 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1066 // CommandCloseTabsToRight.
1067 TEST_F(TabStripModelTest
, CommandCloseTabsToRight
) {
1068 TabStripDummyDelegate delegate
;
1069 TabStripModel
tabstrip(&delegate
, profile());
1070 EXPECT_TRUE(tabstrip
.empty());
1072 // Create three tabs, select last two tabs, CommandCloseTabsToRight should
1073 // only be enabled for the first tab.
1074 ASSERT_NO_FATAL_FAILURE(
1075 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "1 2"));
1076 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1077 0, TabStripModel::CommandCloseTabsToRight
));
1078 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1079 1, TabStripModel::CommandCloseTabsToRight
));
1080 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1081 2, TabStripModel::CommandCloseTabsToRight
));
1082 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight
);
1083 EXPECT_EQ("0", GetTabStripStateString(tabstrip
));
1084 tabstrip
.CloseAllTabs();
1085 EXPECT_TRUE(tabstrip
.empty());
1088 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1089 // CommandTogglePinned.
1090 TEST_F(TabStripModelTest
, CommandTogglePinned
) {
1091 TabStripDummyDelegate delegate
;
1092 TabStripModel
tabstrip(&delegate
, profile());
1093 EXPECT_TRUE(tabstrip
.empty());
1095 // Create three tabs with one pinned, pin the first two.
1096 ASSERT_NO_FATAL_FAILURE(
1097 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "0 1"));
1098 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1099 0, TabStripModel::CommandTogglePinned
));
1100 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1101 1, TabStripModel::CommandTogglePinned
));
1102 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1103 2, TabStripModel::CommandTogglePinned
));
1104 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned
);
1105 EXPECT_EQ("0p 1p 2", GetTabStripStateString(tabstrip
));
1107 // Execute CommandTogglePinned again, this should unpin.
1108 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned
);
1109 EXPECT_EQ("0 1 2", GetTabStripStateString(tabstrip
));
1112 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned
);
1113 EXPECT_EQ("2p 0 1", GetTabStripStateString(tabstrip
));
1115 tabstrip
.CloseAllTabs();
1116 EXPECT_TRUE(tabstrip
.empty());
1119 // Tests the following context menu commands:
1121 // - Close Other Tabs
1122 // - Close Tabs To Right
1123 TEST_F(TabStripModelTest
, TestContextMenuCloseCommands
) {
1124 TabStripDummyDelegate delegate
;
1125 TabStripModel
tabstrip(&delegate
, profile());
1126 EXPECT_TRUE(tabstrip
.empty());
1128 WebContents
* opener
= CreateWebContents();
1129 tabstrip
.AppendWebContents(opener
, true);
1131 WebContents
* contents1
= CreateWebContents();
1132 WebContents
* contents2
= CreateWebContents();
1133 WebContents
* contents3
= CreateWebContents();
1135 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1136 EXPECT_EQ(0, tabstrip
.active_index());
1138 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab
);
1139 EXPECT_EQ(3, tabstrip
.count());
1141 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight
);
1142 EXPECT_EQ(1, tabstrip
.count());
1143 EXPECT_EQ(opener
, tabstrip
.GetActiveWebContents());
1145 WebContents
* dummy
= CreateWebContents();
1146 tabstrip
.AppendWebContents(dummy
, false);
1148 contents1
= CreateWebContents();
1149 contents2
= CreateWebContents();
1150 contents3
= CreateWebContents();
1151 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1152 EXPECT_EQ(5, tabstrip
.count());
1154 int dummy_index
= tabstrip
.count() - 1;
1155 tabstrip
.ActivateTabAt(dummy_index
, true);
1156 EXPECT_EQ(dummy
, tabstrip
.GetActiveWebContents());
1158 tabstrip
.ExecuteContextMenuCommand(dummy_index
,
1159 TabStripModel::CommandCloseOtherTabs
);
1160 EXPECT_EQ(1, tabstrip
.count());
1161 EXPECT_EQ(dummy
, tabstrip
.GetActiveWebContents());
1163 tabstrip
.CloseAllTabs();
1164 EXPECT_TRUE(tabstrip
.empty());
1167 // Tests GetIndicesClosedByCommand.
1168 TEST_F(TabStripModelTest
, GetIndicesClosedByCommand
) {
1169 TabStripDummyDelegate delegate
;
1170 TabStripModel
tabstrip(&delegate
, profile());
1171 EXPECT_TRUE(tabstrip
.empty());
1173 WebContents
* contents1
= CreateWebContents();
1174 WebContents
* contents2
= CreateWebContents();
1175 WebContents
* contents3
= CreateWebContents();
1176 WebContents
* contents4
= CreateWebContents();
1177 WebContents
* contents5
= CreateWebContents();
1179 tabstrip
.AppendWebContents(contents1
, true);
1180 tabstrip
.AppendWebContents(contents2
, true);
1181 tabstrip
.AppendWebContents(contents3
, true);
1182 tabstrip
.AppendWebContents(contents4
, true);
1183 tabstrip
.AppendWebContents(contents5
, true);
1185 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1186 tabstrip
, 0, TabStripModel::CommandCloseTabsToRight
));
1187 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1188 tabstrip
, 1, TabStripModel::CommandCloseTabsToRight
));
1190 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1191 tabstrip
, 0, TabStripModel::CommandCloseOtherTabs
));
1192 EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
1193 tabstrip
, 1, TabStripModel::CommandCloseOtherTabs
));
1195 // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
1197 tabstrip
.SetTabPinned(0, true);
1198 tabstrip
.SetTabPinned(1, true);
1200 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1201 tabstrip
, 0, TabStripModel::CommandCloseTabsToRight
));
1202 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1203 tabstrip
, 2, TabStripModel::CommandCloseTabsToRight
));
1205 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1206 tabstrip
, 0, TabStripModel::CommandCloseOtherTabs
));
1207 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1208 tabstrip
, 2, TabStripModel::CommandCloseOtherTabs
));
1210 tabstrip
.CloseAllTabs();
1211 EXPECT_TRUE(tabstrip
.empty());
1214 // Tests whether or not WebContentses are inserted in the correct position
1215 // using this "smart" function with a simulated middle click action on a series
1216 // of links on the home page.
1217 TEST_F(TabStripModelTest
, AddWebContents_MiddleClickLinksAndClose
) {
1218 TabStripDummyDelegate delegate
;
1219 TabStripModel
tabstrip(&delegate
, profile());
1220 EXPECT_TRUE(tabstrip
.empty());
1222 // Open the Home Page.
1223 WebContents
* homepage_contents
= CreateWebContents();
1224 tabstrip
.AddWebContents(
1225 homepage_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1226 TabStripModel::ADD_ACTIVE
);
1228 // Open some other tab, by user typing.
1229 WebContents
* typed_page_contents
= CreateWebContents();
1230 tabstrip
.AddWebContents(
1231 typed_page_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1232 TabStripModel::ADD_ACTIVE
);
1234 EXPECT_EQ(2, tabstrip
.count());
1236 // Re-select the home page.
1237 tabstrip
.ActivateTabAt(0, true);
1239 // Open a bunch of tabs by simulating middle clicking on links on the home
1241 WebContents
* middle_click_contents1
= CreateWebContents();
1242 tabstrip
.AddWebContents(
1243 middle_click_contents1
, -1, ui::PAGE_TRANSITION_LINK
,
1244 TabStripModel::ADD_NONE
);
1245 WebContents
* middle_click_contents2
= CreateWebContents();
1246 tabstrip
.AddWebContents(
1247 middle_click_contents2
, -1, ui::PAGE_TRANSITION_LINK
,
1248 TabStripModel::ADD_NONE
);
1249 WebContents
* middle_click_contents3
= CreateWebContents();
1250 tabstrip
.AddWebContents(
1251 middle_click_contents3
, -1, ui::PAGE_TRANSITION_LINK
,
1252 TabStripModel::ADD_NONE
);
1254 EXPECT_EQ(5, tabstrip
.count());
1256 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1257 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetWebContentsAt(1));
1258 EXPECT_EQ(middle_click_contents2
, tabstrip
.GetWebContentsAt(2));
1259 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetWebContentsAt(3));
1260 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(4));
1262 // Now simulate selecting a tab in the middle of the group of tabs opened from
1263 // the home page and start closing them. Each WebContents in the group
1264 // should be closed, right to left. This test is constructed to start at the
1265 // middle WebContents in the group to make sure the cursor wraps around
1266 // to the first WebContents in the group before closing the opener or
1267 // any other WebContents.
1268 tabstrip
.ActivateTabAt(2, true);
1269 tabstrip
.CloseSelectedTabs();
1270 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetActiveWebContents());
1271 tabstrip
.CloseSelectedTabs();
1272 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetActiveWebContents());
1273 tabstrip
.CloseSelectedTabs();
1274 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1275 tabstrip
.CloseSelectedTabs();
1276 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1278 EXPECT_EQ(1, tabstrip
.count());
1280 tabstrip
.CloseAllTabs();
1281 EXPECT_TRUE(tabstrip
.empty());
1284 // Tests whether or not a WebContents created by a left click on a link
1285 // that opens a new tab is inserted correctly adjacent to the tab that spawned
1287 TEST_F(TabStripModelTest
, AddWebContents_LeftClickPopup
) {
1288 TabStripDummyDelegate delegate
;
1289 TabStripModel
tabstrip(&delegate
, profile());
1290 EXPECT_TRUE(tabstrip
.empty());
1292 // Open the Home Page.
1293 WebContents
* homepage_contents
= CreateWebContents();
1294 tabstrip
.AddWebContents(
1295 homepage_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1296 TabStripModel::ADD_ACTIVE
);
1298 // Open some other tab, by user typing.
1299 WebContents
* typed_page_contents
= CreateWebContents();
1300 tabstrip
.AddWebContents(
1301 typed_page_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1302 TabStripModel::ADD_ACTIVE
);
1304 EXPECT_EQ(2, tabstrip
.count());
1306 // Re-select the home page.
1307 tabstrip
.ActivateTabAt(0, true);
1309 // Open a tab by simulating a left click on a link that opens in a new tab.
1310 WebContents
* left_click_contents
= CreateWebContents();
1311 tabstrip
.AddWebContents(left_click_contents
, -1,
1312 ui::PAGE_TRANSITION_LINK
,
1313 TabStripModel::ADD_ACTIVE
);
1315 // Verify the state meets our expectations.
1316 EXPECT_EQ(3, tabstrip
.count());
1317 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1318 EXPECT_EQ(left_click_contents
, tabstrip
.GetWebContentsAt(1));
1319 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(2));
1321 // The newly created tab should be selected.
1322 EXPECT_EQ(left_click_contents
, tabstrip
.GetActiveWebContents());
1324 // After closing the selected tab, the selection should move to the left, to
1326 tabstrip
.CloseSelectedTabs();
1327 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1329 EXPECT_EQ(2, tabstrip
.count());
1331 tabstrip
.CloseAllTabs();
1332 EXPECT_TRUE(tabstrip
.empty());
1335 // Tests whether or not new tabs that should split context (typed pages,
1336 // generated urls, also blank tabs) open at the end of the tabstrip instead of
1338 TEST_F(TabStripModelTest
, AddWebContents_CreateNewBlankTab
) {
1339 TabStripDummyDelegate delegate
;
1340 TabStripModel
tabstrip(&delegate
, profile());
1341 EXPECT_TRUE(tabstrip
.empty());
1343 // Open the Home Page.
1344 WebContents
* homepage_contents
= CreateWebContents();
1345 tabstrip
.AddWebContents(
1346 homepage_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1347 TabStripModel::ADD_ACTIVE
);
1349 // Open some other tab, by user typing.
1350 WebContents
* typed_page_contents
= CreateWebContents();
1351 tabstrip
.AddWebContents(
1352 typed_page_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1353 TabStripModel::ADD_ACTIVE
);
1355 EXPECT_EQ(2, tabstrip
.count());
1357 // Re-select the home page.
1358 tabstrip
.ActivateTabAt(0, true);
1360 // Open a new blank tab in the foreground.
1361 WebContents
* new_blank_contents
= CreateWebContents();
1362 tabstrip
.AddWebContents(new_blank_contents
, -1,
1363 ui::PAGE_TRANSITION_TYPED
,
1364 TabStripModel::ADD_ACTIVE
);
1366 // Verify the state of the tabstrip.
1367 EXPECT_EQ(3, tabstrip
.count());
1368 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1369 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(1));
1370 EXPECT_EQ(new_blank_contents
, tabstrip
.GetWebContentsAt(2));
1372 // Now open a couple more blank tabs in the background.
1373 WebContents
* background_blank_contents1
= CreateWebContents();
1374 tabstrip
.AddWebContents(
1375 background_blank_contents1
, -1, ui::PAGE_TRANSITION_TYPED
,
1376 TabStripModel::ADD_NONE
);
1377 WebContents
* background_blank_contents2
= CreateWebContents();
1378 tabstrip
.AddWebContents(
1379 background_blank_contents2
, -1, ui::PAGE_TRANSITION_GENERATED
,
1380 TabStripModel::ADD_NONE
);
1381 EXPECT_EQ(5, tabstrip
.count());
1382 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1383 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(1));
1384 EXPECT_EQ(new_blank_contents
, tabstrip
.GetWebContentsAt(2));
1385 EXPECT_EQ(background_blank_contents1
, tabstrip
.GetWebContentsAt(3));
1386 EXPECT_EQ(background_blank_contents2
, tabstrip
.GetWebContentsAt(4));
1388 tabstrip
.CloseAllTabs();
1389 EXPECT_TRUE(tabstrip
.empty());
1392 // Tests whether opener state is correctly forgotten when the user switches
1394 TEST_F(TabStripModelTest
, AddWebContents_ForgetOpeners
) {
1395 TabStripDummyDelegate delegate
;
1396 TabStripModel
tabstrip(&delegate
, profile());
1397 EXPECT_TRUE(tabstrip
.empty());
1399 // Open the Home Page
1400 WebContents
* homepage_contents
= CreateWebContents();
1401 tabstrip
.AddWebContents(
1402 homepage_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1403 TabStripModel::ADD_ACTIVE
);
1405 // Open some other tab, by user typing.
1406 WebContents
* typed_page_contents
= CreateWebContents();
1407 tabstrip
.AddWebContents(
1408 typed_page_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1409 TabStripModel::ADD_ACTIVE
);
1411 EXPECT_EQ(2, tabstrip
.count());
1413 // Re-select the home page.
1414 tabstrip
.ActivateTabAt(0, true);
1416 // Open a bunch of tabs by simulating middle clicking on links on the home
1418 WebContents
* middle_click_contents1
= CreateWebContents();
1419 tabstrip
.AddWebContents(
1420 middle_click_contents1
, -1, ui::PAGE_TRANSITION_LINK
,
1421 TabStripModel::ADD_NONE
);
1422 WebContents
* middle_click_contents2
= CreateWebContents();
1423 tabstrip
.AddWebContents(
1424 middle_click_contents2
, -1, ui::PAGE_TRANSITION_LINK
,
1425 TabStripModel::ADD_NONE
);
1426 WebContents
* middle_click_contents3
= CreateWebContents();
1427 tabstrip
.AddWebContents(
1428 middle_click_contents3
, -1, ui::PAGE_TRANSITION_LINK
,
1429 TabStripModel::ADD_NONE
);
1431 // Break out of the context by selecting a tab in a different context.
1432 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(4));
1433 tabstrip
.SelectLastTab();
1434 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1436 // Step back into the context by selecting a tab inside it.
1437 tabstrip
.ActivateTabAt(2, true);
1438 EXPECT_EQ(middle_click_contents2
, tabstrip
.GetActiveWebContents());
1440 // Now test that closing tabs selects to the right until there are no more,
1441 // then to the left, as if there were no context (context has been
1442 // successfully forgotten).
1443 tabstrip
.CloseSelectedTabs();
1444 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetActiveWebContents());
1445 tabstrip
.CloseSelectedTabs();
1446 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1447 tabstrip
.CloseSelectedTabs();
1448 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetActiveWebContents());
1449 tabstrip
.CloseSelectedTabs();
1450 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1452 EXPECT_EQ(1, tabstrip
.count());
1454 tabstrip
.CloseAllTabs();
1455 EXPECT_TRUE(tabstrip
.empty());
1458 // Added for http://b/issue?id=958960
1459 TEST_F(TabStripModelTest
, AppendContentsReselectionTest
) {
1460 TabStripDummyDelegate delegate
;
1461 TabStripModel
tabstrip(&delegate
, profile());
1462 EXPECT_TRUE(tabstrip
.empty());
1464 // Open the Home Page.
1465 WebContents
* homepage_contents
= CreateWebContents();
1466 tabstrip
.AddWebContents(
1467 homepage_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1468 TabStripModel::ADD_ACTIVE
);
1470 // Open some other tab, by user typing.
1471 WebContents
* typed_page_contents
= CreateWebContents();
1472 tabstrip
.AddWebContents(
1473 typed_page_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1474 TabStripModel::ADD_NONE
);
1476 // The selected tab should still be the first.
1477 EXPECT_EQ(0, tabstrip
.active_index());
1479 // Now simulate a link click that opens a new tab (by virtue of target=_blank)
1480 // and make sure the correct tab gets selected when the new tab is closed.
1481 WebContents
* target_blank
= CreateWebContents();
1482 tabstrip
.AppendWebContents(target_blank
, true);
1483 EXPECT_EQ(2, tabstrip
.active_index());
1484 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1485 EXPECT_EQ(0, tabstrip
.active_index());
1487 // Clean up after ourselves.
1488 tabstrip
.CloseAllTabs();
1491 // Added for http://b/issue?id=1027661
1492 TEST_F(TabStripModelTest
, ReselectionConsidersChildrenTest
) {
1493 TabStripDummyDelegate delegate
;
1494 TabStripModel
strip(&delegate
, profile());
1497 WebContents
* page_a_contents
= CreateWebContents();
1498 strip
.AddWebContents(
1499 page_a_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1500 TabStripModel::ADD_ACTIVE
);
1502 // Simulate middle click to open page A.A and A.B
1503 WebContents
* page_a_a_contents
= CreateWebContents();
1504 strip
.AddWebContents(page_a_a_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1505 TabStripModel::ADD_NONE
);
1506 WebContents
* page_a_b_contents
= CreateWebContents();
1507 strip
.AddWebContents(page_a_b_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1508 TabStripModel::ADD_NONE
);
1511 strip
.ActivateTabAt(1, true);
1512 EXPECT_EQ(page_a_a_contents
, strip
.GetActiveWebContents());
1514 // Simulate a middle click to open page A.A.A
1515 WebContents
* page_a_a_a_contents
= CreateWebContents();
1516 strip
.AddWebContents(page_a_a_a_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1517 TabStripModel::ADD_NONE
);
1519 EXPECT_EQ(page_a_a_a_contents
, strip
.GetWebContentsAt(2));
1522 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1524 // Page A.A.A should be selected, NOT A.B
1525 EXPECT_EQ(page_a_a_a_contents
, strip
.GetActiveWebContents());
1528 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1530 // Page A.B should be selected
1531 EXPECT_EQ(page_a_b_contents
, strip
.GetActiveWebContents());
1534 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1536 // Page A should be selected
1537 EXPECT_EQ(page_a_contents
, strip
.GetActiveWebContents());
1540 strip
.CloseAllTabs();
1543 TEST_F(TabStripModelTest
, AddWebContents_NewTabAtEndOfStripInheritsGroup
) {
1544 TabStripDummyDelegate delegate
;
1545 TabStripModel
strip(&delegate
, profile());
1548 WebContents
* page_a_contents
= CreateWebContents();
1549 strip
.AddWebContents(page_a_contents
, -1,
1550 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1551 TabStripModel::ADD_ACTIVE
);
1553 // Open pages B, C and D in the background from links on page A...
1554 WebContents
* page_b_contents
= CreateWebContents();
1555 WebContents
* page_c_contents
= CreateWebContents();
1556 WebContents
* page_d_contents
= CreateWebContents();
1557 strip
.AddWebContents(page_b_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1558 TabStripModel::ADD_NONE
);
1559 strip
.AddWebContents(page_c_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1560 TabStripModel::ADD_NONE
);
1561 strip
.AddWebContents(page_d_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1562 TabStripModel::ADD_NONE
);
1564 // Switch to page B's tab.
1565 strip
.ActivateTabAt(1, true);
1567 // Open a New Tab at the end of the strip (simulate Ctrl+T)
1568 WebContents
* new_contents
= CreateWebContents();
1569 strip
.AddWebContents(new_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1570 TabStripModel::ADD_ACTIVE
);
1572 EXPECT_EQ(4, strip
.GetIndexOfWebContents(new_contents
));
1573 EXPECT_EQ(4, strip
.active_index());
1575 // Close the New Tab that was just opened. We should be returned to page B's
1577 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1579 EXPECT_EQ(1, strip
.active_index());
1581 // Open a non-New Tab tab at the end of the strip, with a TYPED transition.
1582 // This is like typing a URL in the address bar and pressing Alt+Enter. The
1583 // behavior should be the same as above.
1584 WebContents
* page_e_contents
= CreateWebContents();
1585 strip
.AddWebContents(page_e_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1586 TabStripModel::ADD_ACTIVE
);
1588 EXPECT_EQ(4, strip
.GetIndexOfWebContents(page_e_contents
));
1589 EXPECT_EQ(4, strip
.active_index());
1591 // Close the Tab. Selection should shift back to page B's Tab.
1592 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1594 EXPECT_EQ(1, strip
.active_index());
1596 // Open a non-New Tab tab at the end of the strip, with some other
1597 // transition. This is like right clicking on a bookmark and choosing "Open
1598 // in New Tab". No opener relationship should be preserved between this Tab
1599 // and the one that was active when the gesture was performed.
1600 WebContents
* page_f_contents
= CreateWebContents();
1601 strip
.AddWebContents(page_f_contents
, -1,
1602 ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1603 TabStripModel::ADD_ACTIVE
);
1605 EXPECT_EQ(4, strip
.GetIndexOfWebContents(page_f_contents
));
1606 EXPECT_EQ(4, strip
.active_index());
1608 // Close the Tab. The next-adjacent should be selected.
1609 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1611 EXPECT_EQ(3, strip
.active_index());
1614 strip
.CloseAllTabs();
1617 // A test of navigations in a tab that is part of a group of opened from some
1618 // parent tab. If the navigations are link clicks, the group relationship of
1619 // the tab to its parent are preserved. If they are of any other type, they are
1621 TEST_F(TabStripModelTest
, NavigationForgetsOpeners
) {
1622 TabStripDummyDelegate delegate
;
1623 TabStripModel
strip(&delegate
, profile());
1626 WebContents
* page_a_contents
= CreateWebContents();
1627 strip
.AddWebContents(page_a_contents
, -1,
1628 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1629 TabStripModel::ADD_ACTIVE
);
1631 // Open pages B, C and D in the background from links on page A...
1632 WebContents
* page_b_contents
= CreateWebContents();
1633 WebContents
* page_c_contents
= CreateWebContents();
1634 WebContents
* page_d_contents
= CreateWebContents();
1635 strip
.AddWebContents(page_b_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1636 TabStripModel::ADD_NONE
);
1637 strip
.AddWebContents(page_c_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1638 TabStripModel::ADD_NONE
);
1639 strip
.AddWebContents(page_d_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1640 TabStripModel::ADD_NONE
);
1642 // Open page E in a different opener group from page A.
1643 WebContents
* page_e_contents
= CreateWebContents();
1644 strip
.AddWebContents(page_e_contents
, -1,
1645 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1646 TabStripModel::ADD_NONE
);
1648 // Tell the TabStripModel that we are navigating page D via a link click.
1649 strip
.ActivateTabAt(3, true);
1650 strip
.TabNavigating(page_d_contents
, ui::PAGE_TRANSITION_LINK
);
1652 // Close page D, page C should be selected. (part of same group).
1653 strip
.CloseWebContentsAt(3, TabStripModel::CLOSE_NONE
);
1654 EXPECT_EQ(2, strip
.active_index());
1656 // Tell the TabStripModel that we are navigating in page C via a bookmark.
1657 strip
.TabNavigating(page_c_contents
, ui::PAGE_TRANSITION_AUTO_BOOKMARK
);
1659 // Close page C, page E should be selected. (C is no longer part of the
1660 // A-B-C-D group, selection moves to the right).
1661 strip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1662 EXPECT_EQ(page_e_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1664 strip
.CloseAllTabs();
1667 // A test that the forgetting behavior tested in NavigationForgetsOpeners above
1668 // doesn't cause the opener relationship for a New Tab opened at the end of the
1669 // TabStrip to be reset (Test 1 below), unless another any other tab is
1670 // selected (Test 2 below).
1671 TEST_F(TabStripModelTest
, NavigationForgettingDoesntAffectNewTab
) {
1672 TabStripDummyDelegate delegate
;
1673 TabStripModel
strip(&delegate
, profile());
1675 // Open a tab and several tabs from it, then select one of the tabs that was
1677 WebContents
* page_a_contents
= CreateWebContents();
1678 strip
.AddWebContents(page_a_contents
, -1,
1679 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1680 TabStripModel::ADD_ACTIVE
);
1682 WebContents
* page_b_contents
= CreateWebContents();
1683 WebContents
* page_c_contents
= CreateWebContents();
1684 WebContents
* page_d_contents
= CreateWebContents();
1685 strip
.AddWebContents(page_b_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1686 TabStripModel::ADD_NONE
);
1687 strip
.AddWebContents(page_c_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1688 TabStripModel::ADD_NONE
);
1689 strip
.AddWebContents(page_d_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1690 TabStripModel::ADD_NONE
);
1692 strip
.ActivateTabAt(2, true);
1694 // TEST 1: If the user is in a group of tabs and opens a new tab at the end
1695 // of the strip, closing that new tab will select the tab that they were
1698 // Now simulate opening a new tab at the end of the TabStrip.
1699 WebContents
* new_contents1
= CreateWebContents();
1700 strip
.AddWebContents(new_contents1
, -1, ui::PAGE_TRANSITION_TYPED
,
1701 TabStripModel::ADD_ACTIVE
);
1703 // At this point, if we close this tab the last selected one should be
1705 strip
.CloseWebContentsAt(strip
.count() - 1, TabStripModel::CLOSE_NONE
);
1706 EXPECT_EQ(page_c_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1708 // TEST 2: If the user is in a group of tabs and opens a new tab at the end
1709 // of the strip, selecting any other tab in the strip will cause that new
1710 // tab's opener relationship to be forgotten.
1712 // Open a new tab again.
1713 WebContents
* new_contents2
= CreateWebContents();
1714 strip
.AddWebContents(new_contents2
, -1, ui::PAGE_TRANSITION_TYPED
,
1715 TabStripModel::ADD_ACTIVE
);
1717 // Now select the first tab.
1718 strip
.ActivateTabAt(0, true);
1720 // Now select the last tab.
1721 strip
.ActivateTabAt(strip
.count() - 1, true);
1723 // Now close the last tab. The next adjacent should be selected.
1724 strip
.CloseWebContentsAt(strip
.count() - 1, TabStripModel::CLOSE_NONE
);
1725 EXPECT_EQ(page_d_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1727 strip
.CloseAllTabs();
1730 // This fails on Linux when run with the rest of unit_tests (crbug.com/302156)
1731 // and fails consistently on Mac and Windows.
1732 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
1733 #define MAYBE_FastShutdown \
1734 DISABLED_FastShutdown
1736 #define MAYBE_FastShutdown \
1739 // Tests that fast shutdown is attempted appropriately.
1740 TEST_F(TabStripModelTest
, MAYBE_FastShutdown
) {
1741 TabStripDummyDelegate delegate
;
1742 TabStripModel
tabstrip(&delegate
, profile());
1743 MockTabStripModelObserver
observer(&tabstrip
);
1744 tabstrip
.AddObserver(&observer
);
1746 EXPECT_TRUE(tabstrip
.empty());
1748 // Make sure fast shutdown is attempted when tabs that share a RPH are shut
1751 WebContents
* contents1
= CreateWebContents();
1752 WebContents
* contents2
= CreateWebContentsWithSharedRPH(contents1
);
1754 SetID(contents1
, 1);
1755 SetID(contents2
, 2);
1757 tabstrip
.AppendWebContents(contents1
, true);
1758 tabstrip
.AppendWebContents(contents2
, true);
1760 // Turn on the fake unload listener so the tabs don't actually get shut
1761 // down when we call CloseAllTabs()---we need to be able to check that
1762 // fast shutdown was attempted.
1763 delegate
.set_run_unload_listener(true);
1764 tabstrip
.CloseAllTabs();
1765 // On a mock RPH this checks whether we *attempted* fast shutdown.
1766 // A real RPH would reject our attempt since there is an unload handler.
1767 EXPECT_TRUE(contents1
->GetRenderProcessHost()->FastShutdownStarted());
1768 EXPECT_EQ(2, tabstrip
.count());
1770 delegate
.set_run_unload_listener(false);
1771 tabstrip
.CloseAllTabs();
1772 EXPECT_TRUE(tabstrip
.empty());
1775 // Make sure fast shutdown is not attempted when only some tabs that share a
1776 // RPH are shut down.
1778 WebContents
* contents1
= CreateWebContents();
1779 WebContents
* contents2
= CreateWebContentsWithSharedRPH(contents1
);
1781 SetID(contents1
, 1);
1782 SetID(contents2
, 2);
1784 tabstrip
.AppendWebContents(contents1
, true);
1785 tabstrip
.AppendWebContents(contents2
, true);
1787 tabstrip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
1788 EXPECT_FALSE(contents1
->GetRenderProcessHost()->FastShutdownStarted());
1789 EXPECT_EQ(1, tabstrip
.count());
1791 tabstrip
.CloseAllTabs();
1792 EXPECT_TRUE(tabstrip
.empty());
1796 // Tests various permutations of apps.
1797 TEST_F(TabStripModelTest
, Apps
) {
1798 TabStripDummyDelegate delegate
;
1799 TabStripModel
tabstrip(&delegate
, profile());
1800 MockTabStripModelObserver
observer(&tabstrip
);
1801 tabstrip
.AddObserver(&observer
);
1803 EXPECT_TRUE(tabstrip
.empty());
1805 typedef MockTabStripModelObserver::State State
;
1808 base::FilePath
path(FILE_PATH_LITERAL("c:\\foo"));
1809 #elif defined(OS_POSIX)
1810 base::FilePath
path(FILE_PATH_LITERAL("/foo"));
1813 base::DictionaryValue manifest
;
1814 manifest
.SetString("name", "hi!");
1815 manifest
.SetString("version", "1");
1816 manifest
.SetString("app.launch.web_url", "http://www.google.com");
1818 scoped_refptr
<Extension
> extension_app(
1819 Extension::Create(path
, extensions::Manifest::INVALID_LOCATION
,
1820 manifest
, Extension::NO_FLAGS
, &error
));
1821 WebContents
* contents1
= CreateWebContents();
1822 extensions::TabHelper::CreateForWebContents(contents1
);
1823 extensions::TabHelper::FromWebContents(contents1
)
1824 ->SetExtensionApp(extension_app
.get());
1825 WebContents
* contents2
= CreateWebContents();
1826 extensions::TabHelper::CreateForWebContents(contents2
);
1827 extensions::TabHelper::FromWebContents(contents2
)
1828 ->SetExtensionApp(extension_app
.get());
1829 WebContents
* contents3
= CreateWebContents();
1831 SetID(contents1
, 1);
1832 SetID(contents2
, 2);
1833 SetID(contents3
, 3);
1835 // Note! The ordering of these tests is important, each subsequent test
1836 // builds on the state established in the previous. This is important if you
1837 // ever insert tests rather than append.
1839 // Initial state, tab3 only and selected.
1840 tabstrip
.AppendWebContents(contents3
, true);
1842 observer
.ClearStates();
1844 // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal
1845 // position and tab1 should end up at position 0.
1847 tabstrip
.InsertWebContentsAt(1, contents1
, TabStripModel::ADD_NONE
);
1849 ASSERT_EQ(1, observer
.GetStateCount());
1850 State
state(contents1
, 0, MockTabStripModelObserver::INSERT
);
1851 EXPECT_TRUE(observer
.StateEquals(0, state
));
1853 // And verify the state.
1854 EXPECT_EQ("1ap 3", GetTabStripStateString(tabstrip
));
1856 observer
.ClearStates();
1859 // Insert tab 2 at position 1.
1861 tabstrip
.InsertWebContentsAt(1, contents2
, TabStripModel::ADD_NONE
);
1863 ASSERT_EQ(1, observer
.GetStateCount());
1864 State
state(contents2
, 1, MockTabStripModelObserver::INSERT
);
1865 EXPECT_TRUE(observer
.StateEquals(0, state
));
1867 // And verify the state.
1868 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip
));
1870 observer
.ClearStates();
1873 // Try to move tab 3 to position 0. This isn't legal and should be ignored.
1875 tabstrip
.MoveWebContentsAt(2, 0, false);
1877 ASSERT_EQ(0, observer
.GetStateCount());
1879 // And verify the state didn't change.
1880 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip
));
1882 observer
.ClearStates();
1885 // Try to move tab 0 to position 3. This isn't legal and should be ignored.
1887 tabstrip
.MoveWebContentsAt(0, 2, false);
1889 ASSERT_EQ(0, observer
.GetStateCount());
1891 // And verify the state didn't change.
1892 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip
));
1894 observer
.ClearStates();
1897 // Try to move tab 0 to position 1. This is a legal move.
1899 tabstrip
.MoveWebContentsAt(0, 1, false);
1901 ASSERT_EQ(1, observer
.GetStateCount());
1902 State
state(contents1
, 1, MockTabStripModelObserver::MOVE
);
1903 state
.src_index
= 0;
1904 EXPECT_TRUE(observer
.StateEquals(0, state
));
1906 // And verify the state didn't change.
1907 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip
));
1909 observer
.ClearStates();
1912 // Remove tab3 and insert at position 0. It should be forced to position 2.
1914 tabstrip
.DetachWebContentsAt(2);
1915 observer
.ClearStates();
1917 tabstrip
.InsertWebContentsAt(0, contents3
, TabStripModel::ADD_NONE
);
1919 ASSERT_EQ(1, observer
.GetStateCount());
1920 State
state(contents3
, 2, MockTabStripModelObserver::INSERT
);
1921 EXPECT_TRUE(observer
.StateEquals(0, state
));
1923 // And verify the state didn't change.
1924 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip
));
1926 observer
.ClearStates();
1929 tabstrip
.CloseAllTabs();
1932 // Tests various permutations of pinning tabs.
1933 TEST_F(TabStripModelTest
, Pinning
) {
1934 TabStripDummyDelegate delegate
;
1935 TabStripModel
tabstrip(&delegate
, profile());
1936 MockTabStripModelObserver
observer(&tabstrip
);
1937 tabstrip
.AddObserver(&observer
);
1939 EXPECT_TRUE(tabstrip
.empty());
1941 typedef MockTabStripModelObserver::State State
;
1943 WebContents
* contents1
= CreateWebContents();
1944 WebContents
* contents2
= CreateWebContents();
1945 WebContents
* contents3
= CreateWebContents();
1947 SetID(contents1
, 1);
1948 SetID(contents2
, 2);
1949 SetID(contents3
, 3);
1951 // Note! The ordering of these tests is important, each subsequent test
1952 // builds on the state established in the previous. This is important if you
1953 // ever insert tests rather than append.
1955 // Initial state, three tabs, first selected.
1956 tabstrip
.AppendWebContents(contents1
, true);
1957 tabstrip
.AppendWebContents(contents2
, false);
1958 tabstrip
.AppendWebContents(contents3
, false);
1960 observer
.ClearStates();
1962 // Pin the first tab, this shouldn't visually reorder anything.
1964 tabstrip
.SetTabPinned(0, true);
1966 // As the order didn't change, we should get a pinned notification.
1967 ASSERT_EQ(1, observer
.GetStateCount());
1968 State
state(contents1
, 0, MockTabStripModelObserver::PINNED
);
1969 EXPECT_TRUE(observer
.StateEquals(0, state
));
1971 // And verify the state.
1972 EXPECT_EQ("1p 2 3", GetTabStripStateString(tabstrip
));
1974 observer
.ClearStates();
1977 // Unpin the first tab.
1979 tabstrip
.SetTabPinned(0, false);
1981 // As the order didn't change, we should get a pinned notification.
1982 ASSERT_EQ(1, observer
.GetStateCount());
1983 State
state(contents1
, 0, MockTabStripModelObserver::PINNED
);
1984 EXPECT_TRUE(observer
.StateEquals(0, state
));
1986 // And verify the state.
1987 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
1989 observer
.ClearStates();
1992 // Pin the 3rd tab, which should move it to the front.
1994 tabstrip
.SetTabPinned(2, true);
1996 // The pinning should have resulted in a move and a pinned notification.
1997 ASSERT_EQ(2, observer
.GetStateCount());
1998 State
state(contents3
, 0, MockTabStripModelObserver::MOVE
);
1999 state
.src_index
= 2;
2000 EXPECT_TRUE(observer
.StateEquals(0, state
));
2002 state
= State(contents3
, 0, MockTabStripModelObserver::PINNED
);
2003 EXPECT_TRUE(observer
.StateEquals(1, state
));
2005 // And verify the state.
2006 EXPECT_EQ("3p 1 2", GetTabStripStateString(tabstrip
));
2008 observer
.ClearStates();
2011 // Pin the tab "1", which shouldn't move anything.
2013 tabstrip
.SetTabPinned(1, true);
2015 // As the order didn't change, we should get a pinned notification.
2016 ASSERT_EQ(1, observer
.GetStateCount());
2017 State
state(contents1
, 1, MockTabStripModelObserver::PINNED
);
2018 EXPECT_TRUE(observer
.StateEquals(0, state
));
2020 // And verify the state.
2021 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip
));
2023 observer
.ClearStates();
2026 // Try to move tab "2" to the front, it should be ignored.
2028 tabstrip
.MoveWebContentsAt(2, 0, false);
2030 // As the order didn't change, we should get a pinned notification.
2031 ASSERT_EQ(0, observer
.GetStateCount());
2033 // And verify the state.
2034 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip
));
2036 observer
.ClearStates();
2039 // Unpin tab "3", which implicitly moves it to the end.
2041 tabstrip
.SetTabPinned(0, false);
2043 ASSERT_EQ(2, observer
.GetStateCount());
2044 State
state(contents3
, 1, MockTabStripModelObserver::MOVE
);
2045 state
.src_index
= 0;
2046 EXPECT_TRUE(observer
.StateEquals(0, state
));
2048 state
= State(contents3
, 1, MockTabStripModelObserver::PINNED
);
2049 EXPECT_TRUE(observer
.StateEquals(1, state
));
2051 // And verify the state.
2052 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip
));
2054 observer
.ClearStates();
2057 // Unpin tab "3", nothing should happen.
2059 tabstrip
.SetTabPinned(1, false);
2061 ASSERT_EQ(0, observer
.GetStateCount());
2063 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip
));
2065 observer
.ClearStates();
2070 tabstrip
.SetTabPinned(0, true);
2071 tabstrip
.SetTabPinned(1, true);
2073 EXPECT_EQ("1p 3p 2", GetTabStripStateString(tabstrip
));
2075 observer
.ClearStates();
2078 WebContents
* contents4
= CreateWebContents();
2079 SetID(contents4
, 4);
2081 // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end
2084 tabstrip
.InsertWebContentsAt(1, contents4
, TabStripModel::ADD_NONE
);
2086 ASSERT_EQ(1, observer
.GetStateCount());
2087 State
state(contents4
, 2, MockTabStripModelObserver::INSERT
);
2088 EXPECT_TRUE(observer
.StateEquals(0, state
));
2090 EXPECT_EQ("1p 3p 4 2", GetTabStripStateString(tabstrip
));
2093 tabstrip
.CloseAllTabs();
2096 // Makes sure the TabStripModel calls the right observer methods during a
2098 TEST_F(TabStripModelTest
, ReplaceSendsSelected
) {
2099 typedef MockTabStripModelObserver::State State
;
2101 TabStripDummyDelegate delegate
;
2102 TabStripModel
strip(&delegate
, profile());
2104 WebContents
* first_contents
= CreateWebContents();
2105 strip
.AddWebContents(first_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
2106 TabStripModel::ADD_ACTIVE
);
2108 MockTabStripModelObserver
tabstrip_observer(&strip
);
2109 strip
.AddObserver(&tabstrip_observer
);
2111 WebContents
* new_contents
= CreateWebContents();
2112 delete strip
.ReplaceWebContentsAt(0, new_contents
);
2114 ASSERT_EQ(2, tabstrip_observer
.GetStateCount());
2116 // First event should be for replaced.
2117 State
state(new_contents
, 0, MockTabStripModelObserver::REPLACED
);
2118 state
.src_contents
= first_contents
;
2119 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state
));
2121 // And the second for selected.
2122 state
= State(new_contents
, 0, MockTabStripModelObserver::ACTIVATE
);
2123 state
.src_contents
= first_contents
;
2124 state
.change_reason
= TabStripModelObserver::CHANGE_REASON_REPLACED
;
2125 EXPECT_TRUE(tabstrip_observer
.StateEquals(1, state
));
2127 // Now add another tab and replace it, making sure we don't get a selected
2129 WebContents
* third_contents
= CreateWebContents();
2130 strip
.AddWebContents(third_contents
, 1, ui::PAGE_TRANSITION_TYPED
,
2131 TabStripModel::ADD_NONE
);
2133 tabstrip_observer
.ClearStates();
2136 new_contents
= CreateWebContents();
2137 delete strip
.ReplaceWebContentsAt(1, new_contents
);
2139 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2141 state
= State(new_contents
, 1, MockTabStripModelObserver::REPLACED
);
2142 state
.src_contents
= third_contents
;
2143 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state
));
2145 strip
.CloseAllTabs();
2148 // Ensures discarding tabs leaves TabStripModel in a good state.
2149 TEST_F(TabStripModelTest
, DiscardWebContentsAt
) {
2150 typedef MockTabStripModelObserver::State State
;
2152 TabStripDummyDelegate delegate
;
2153 TabStripModel
tabstrip(&delegate
, profile());
2155 // Fill it with some tabs.
2156 WebContents
* contents1
= CreateWebContents();
2157 tabstrip
.AppendWebContents(contents1
, true);
2158 WebContents
* contents2
= CreateWebContents();
2159 tabstrip
.AppendWebContents(contents2
, true);
2161 // Start watching for events after the appends to avoid observing state
2162 // transitions that aren't relevant to this test.
2163 MockTabStripModelObserver
tabstrip_observer(&tabstrip
);
2164 tabstrip
.AddObserver(&tabstrip_observer
);
2166 // Discard one of the tabs.
2167 WebContents
* null_contents1
= tabstrip
.DiscardWebContentsAt(0);
2168 ASSERT_EQ(2, tabstrip
.count());
2169 EXPECT_TRUE(tabstrip
.IsTabDiscarded(0));
2170 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2171 ASSERT_EQ(null_contents1
, tabstrip
.GetWebContentsAt(0));
2172 ASSERT_EQ(contents2
, tabstrip
.GetWebContentsAt(1));
2173 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2174 State
state1(null_contents1
, 0, MockTabStripModelObserver::REPLACED
);
2175 state1
.src_contents
= contents1
;
2176 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state1
));
2177 tabstrip_observer
.ClearStates();
2179 // Discard the same tab again.
2180 WebContents
* null_contents2
= tabstrip
.DiscardWebContentsAt(0);
2181 ASSERT_EQ(2, tabstrip
.count());
2182 EXPECT_TRUE(tabstrip
.IsTabDiscarded(0));
2183 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2184 ASSERT_EQ(null_contents2
, tabstrip
.GetWebContentsAt(0));
2185 ASSERT_EQ(contents2
, tabstrip
.GetWebContentsAt(1));
2186 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2187 State
state2(null_contents2
, 0, MockTabStripModelObserver::REPLACED
);
2188 state2
.src_contents
= null_contents1
;
2189 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state2
));
2190 tabstrip_observer
.ClearStates();
2192 // Activating the tab should clear its discard state.
2193 tabstrip
.ActivateTabAt(0, true /* user_gesture */);
2194 ASSERT_EQ(2, tabstrip
.count());
2195 EXPECT_FALSE(tabstrip
.IsTabDiscarded(0));
2196 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2198 // Don't discard active tab.
2199 tabstrip
.DiscardWebContentsAt(0);
2200 ASSERT_EQ(2, tabstrip
.count());
2201 EXPECT_FALSE(tabstrip
.IsTabDiscarded(0));
2202 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2204 tabstrip
.CloseAllTabs();
2207 // Makes sure TabStripModel handles the case of deleting a tab while removing
2209 TEST_F(TabStripModelTest
, DeleteFromDestroy
) {
2210 TabStripDummyDelegate delegate
;
2211 TabStripModel
strip(&delegate
, profile());
2212 WebContents
* contents1
= CreateWebContents();
2213 WebContents
* contents2
= CreateWebContents();
2214 MockTabStripModelObserver
tab_strip_model_observer(&strip
);
2215 strip
.AppendWebContents(contents1
, true);
2216 strip
.AppendWebContents(contents2
, true);
2217 // DeleteWebContentsOnDestroyedObserver deletes contents1 when contents2 sends
2218 // out notification that it is being destroyed.
2219 DeleteWebContentsOnDestroyedObserver
observer(contents2
, contents1
, NULL
);
2220 strip
.AddObserver(&tab_strip_model_observer
);
2221 strip
.CloseAllTabs();
2223 int close_all_count
= 0, close_all_canceled_count
= 0;
2224 tab_strip_model_observer
.GetCloseCounts(&close_all_count
,
2225 &close_all_canceled_count
);
2226 EXPECT_EQ(1, close_all_count
);
2227 EXPECT_EQ(0, close_all_canceled_count
);
2229 strip
.RemoveObserver(&tab_strip_model_observer
);
2232 // Makes sure TabStripModel handles the case of deleting another tab and the
2233 // TabStrip while removing another tab.
2234 TEST_F(TabStripModelTest
, DeleteTabStripFromDestroy
) {
2235 TabStripDummyDelegate delegate
;
2236 TabStripModel
* strip
= new TabStripModel(&delegate
, profile());
2237 MockTabStripModelObserver
tab_strip_model_observer(strip
);
2238 strip
->AddObserver(&tab_strip_model_observer
);
2239 WebContents
* contents1
= CreateWebContents();
2240 WebContents
* contents2
= CreateWebContents();
2241 strip
->AppendWebContents(contents1
, true);
2242 strip
->AppendWebContents(contents2
, true);
2243 // DeleteWebContentsOnDestroyedObserver deletes |contents1| and |strip| when
2244 // |contents2| sends out notification that it is being destroyed.
2245 DeleteWebContentsOnDestroyedObserver
observer(contents2
, contents1
, strip
);
2246 strip
->CloseAllTabs();
2247 EXPECT_TRUE(tab_strip_model_observer
.empty());
2248 EXPECT_TRUE(tab_strip_model_observer
.deleted());
2251 TEST_F(TabStripModelTest
, MoveSelectedTabsTo
) {
2253 // Number of tabs the tab strip should have.
2254 const int tab_count
;
2256 // Number of pinned tabs.
2257 const int pinned_count
;
2259 // Index of the tabs to select.
2260 const std::string selected_tabs
;
2262 // Index to move the tabs to.
2263 const int target_index
;
2265 // Expected state after the move (space separated list of indices).
2266 const std::string state_after_move
;
2269 { 2, 0, "0", 1, "1 0" },
2270 { 3, 0, "0", 2, "1 2 0" },
2271 { 3, 0, "2", 0, "2 0 1" },
2272 { 3, 0, "2", 1, "0 2 1" },
2273 { 3, 0, "0 1", 0, "0 1 2" },
2276 { 6, 0, "4 5", 1, "0 4 5 1 2 3" },
2277 { 3, 0, "0 1", 1, "2 0 1" },
2278 { 4, 0, "0 2", 1, "1 0 2 3" },
2279 { 6, 0, "0 1", 3, "2 3 4 0 1 5" },
2282 { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" },
2283 { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" },
2284 { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" },
2287 { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" },
2290 { 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" },
2292 // With pinned tabs.
2293 { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" },
2294 { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" },
2295 { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" },
2296 { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" },
2298 { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" },
2301 for (size_t i
= 0; i
< arraysize(test_data
); ++i
) {
2302 TabStripDummyDelegate delegate
;
2303 TabStripModel
strip(&delegate
, profile());
2304 ASSERT_NO_FATAL_FAILURE(
2305 PrepareTabstripForSelectionTest(&strip
, test_data
[i
].tab_count
,
2306 test_data
[i
].pinned_count
,
2307 test_data
[i
].selected_tabs
));
2308 strip
.MoveSelectedTabsTo(test_data
[i
].target_index
);
2309 EXPECT_EQ(test_data
[i
].state_after_move
,
2310 GetTabStripStateString(strip
)) << i
;
2311 strip
.CloseAllTabs();
2315 // Tests that moving a tab forgets all groups referencing it.
2316 TEST_F(TabStripModelTest
, MoveSelectedTabsTo_ForgetGroups
) {
2317 TabStripDummyDelegate delegate
;
2318 TabStripModel
strip(&delegate
, profile());
2320 // Open page A as a new tab and then A1 in the background from A.
2321 WebContents
* page_a_contents
= CreateWebContents();
2322 strip
.AddWebContents(page_a_contents
, -1,
2323 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
2324 TabStripModel::ADD_ACTIVE
);
2325 WebContents
* page_a1_contents
= CreateWebContents();
2326 strip
.AddWebContents(page_a1_contents
, -1, ui::PAGE_TRANSITION_LINK
,
2327 TabStripModel::ADD_NONE
);
2329 // Likewise, open pages B and B1.
2330 WebContents
* page_b_contents
= CreateWebContents();
2331 strip
.AddWebContents(page_b_contents
, -1,
2332 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
2333 TabStripModel::ADD_ACTIVE
);
2334 WebContents
* page_b1_contents
= CreateWebContents();
2335 strip
.AddWebContents(page_b1_contents
, -1, ui::PAGE_TRANSITION_LINK
,
2336 TabStripModel::ADD_NONE
);
2338 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(0));
2339 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(1));
2340 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(2));
2341 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(3));
2343 // Move page B to the start of the tab strip.
2344 strip
.MoveSelectedTabsTo(0);
2346 // Open page B2 in the background from B. It should end up after B.
2347 WebContents
* page_b2_contents
= CreateWebContents();
2348 strip
.AddWebContents(page_b2_contents
, -1, ui::PAGE_TRANSITION_LINK
,
2349 TabStripModel::ADD_NONE
);
2350 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(0));
2351 EXPECT_EQ(page_b2_contents
, strip
.GetWebContentsAt(1));
2352 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(2));
2353 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(3));
2354 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(4));
2357 strip
.ActivateTabAt(2, true);
2358 EXPECT_EQ(page_a_contents
, strip
.GetActiveWebContents());
2360 // Open page A2 in the background from A. It should end up after A1.
2361 WebContents
* page_a2_contents
= CreateWebContents();
2362 strip
.AddWebContents(page_a2_contents
, -1, ui::PAGE_TRANSITION_LINK
,
2363 TabStripModel::ADD_NONE
);
2364 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(0));
2365 EXPECT_EQ(page_b2_contents
, strip
.GetWebContentsAt(1));
2366 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(2));
2367 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(3));
2368 EXPECT_EQ(page_a2_contents
, strip
.GetWebContentsAt(4));
2369 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(5));
2371 strip
.CloseAllTabs();
2374 TEST_F(TabStripModelTest
, CloseSelectedTabs
) {
2375 TabStripDummyDelegate delegate
;
2376 TabStripModel
strip(&delegate
, profile());
2377 WebContents
* contents1
= CreateWebContents();
2378 WebContents
* contents2
= CreateWebContents();
2379 WebContents
* contents3
= CreateWebContents();
2380 strip
.AppendWebContents(contents1
, true);
2381 strip
.AppendWebContents(contents2
, true);
2382 strip
.AppendWebContents(contents3
, true);
2383 strip
.ToggleSelectionAt(1);
2384 strip
.CloseSelectedTabs();
2385 EXPECT_EQ(1, strip
.count());
2386 EXPECT_EQ(0, strip
.active_index());
2387 strip
.CloseAllTabs();
2390 TEST_F(TabStripModelTest
, MultipleSelection
) {
2391 typedef MockTabStripModelObserver::State State
;
2393 TabStripDummyDelegate delegate
;
2394 TabStripModel
strip(&delegate
, profile());
2395 MockTabStripModelObserver
observer(&strip
);
2396 WebContents
* contents0
= CreateWebContents();
2397 WebContents
* contents1
= CreateWebContents();
2398 WebContents
* contents2
= CreateWebContents();
2399 WebContents
* contents3
= CreateWebContents();
2400 strip
.AppendWebContents(contents0
, false);
2401 strip
.AppendWebContents(contents1
, false);
2402 strip
.AppendWebContents(contents2
, false);
2403 strip
.AppendWebContents(contents3
, false);
2404 strip
.AddObserver(&observer
);
2406 // Selection and active tab change.
2407 strip
.ActivateTabAt(3, true);
2408 ASSERT_EQ(2, observer
.GetStateCount());
2409 ASSERT_EQ(observer
.GetStateAt(0).action
,
2410 MockTabStripModelObserver::ACTIVATE
);
2411 State
s1(contents3
, 3, MockTabStripModelObserver::SELECT
);
2412 EXPECT_TRUE(observer
.StateEquals(1, s1
));
2413 observer
.ClearStates();
2415 // Adding all tabs to selection, active tab is now at 0.
2416 strip
.ExtendSelectionTo(0);
2417 ASSERT_EQ(3, observer
.GetStateCount());
2418 ASSERT_EQ(observer
.GetStateAt(0).action
,
2419 MockTabStripModelObserver::DEACTIVATE
);
2420 ASSERT_EQ(observer
.GetStateAt(1).action
,
2421 MockTabStripModelObserver::ACTIVATE
);
2422 State
s2(contents0
, 0, MockTabStripModelObserver::SELECT
);
2423 s2
.src_contents
= contents3
;
2425 EXPECT_TRUE(observer
.StateEquals(2, s2
));
2426 observer
.ClearStates();
2428 // Toggle the active tab, should make the next index active.
2429 strip
.ToggleSelectionAt(0);
2430 EXPECT_EQ(1, strip
.active_index());
2431 EXPECT_EQ(3U, strip
.selection_model().size());
2432 EXPECT_EQ(4, strip
.count());
2433 ASSERT_EQ(3, observer
.GetStateCount());
2434 ASSERT_EQ(observer
.GetStateAt(0).action
,
2435 MockTabStripModelObserver::DEACTIVATE
);
2436 ASSERT_EQ(observer
.GetStateAt(1).action
,
2437 MockTabStripModelObserver::ACTIVATE
);
2438 ASSERT_EQ(observer
.GetStateAt(2).action
,
2439 MockTabStripModelObserver::SELECT
);
2440 observer
.ClearStates();
2442 // Toggle the first tab back to selected and active.
2443 strip
.ToggleSelectionAt(0);
2444 EXPECT_EQ(0, strip
.active_index());
2445 EXPECT_EQ(4U, strip
.selection_model().size());
2446 EXPECT_EQ(4, strip
.count());
2447 ASSERT_EQ(3, observer
.GetStateCount());
2448 ASSERT_EQ(observer
.GetStateAt(0).action
,
2449 MockTabStripModelObserver::DEACTIVATE
);
2450 ASSERT_EQ(observer
.GetStateAt(1).action
,
2451 MockTabStripModelObserver::ACTIVATE
);
2452 ASSERT_EQ(observer
.GetStateAt(2).action
,
2453 MockTabStripModelObserver::SELECT
);
2454 observer
.ClearStates();
2456 // Closing one of the selected tabs, not the active one.
2457 strip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
2458 EXPECT_EQ(3, strip
.count());
2459 ASSERT_EQ(3, observer
.GetStateCount());
2460 ASSERT_EQ(observer
.GetStateAt(0).action
,
2461 MockTabStripModelObserver::CLOSE
);
2462 ASSERT_EQ(observer
.GetStateAt(1).action
,
2463 MockTabStripModelObserver::DETACH
);
2464 ASSERT_EQ(observer
.GetStateAt(2).action
,
2465 MockTabStripModelObserver::SELECT
);
2466 observer
.ClearStates();
2468 // Closing the active tab, while there are others tabs selected.
2469 strip
.CloseWebContentsAt(0, TabStripModel::CLOSE_NONE
);
2470 EXPECT_EQ(2, strip
.count());
2471 ASSERT_EQ(5, observer
.GetStateCount());
2472 ASSERT_EQ(observer
.GetStateAt(0).action
,
2473 MockTabStripModelObserver::CLOSE
);
2474 ASSERT_EQ(observer
.GetStateAt(1).action
,
2475 MockTabStripModelObserver::DETACH
);
2476 ASSERT_EQ(observer
.GetStateAt(2).action
,
2477 MockTabStripModelObserver::DEACTIVATE
);
2478 ASSERT_EQ(observer
.GetStateAt(3).action
,
2479 MockTabStripModelObserver::ACTIVATE
);
2480 ASSERT_EQ(observer
.GetStateAt(4).action
,
2481 MockTabStripModelObserver::SELECT
);
2482 observer
.ClearStates();
2484 // Active tab is at 0, deselecting all but the active tab.
2485 strip
.ToggleSelectionAt(1);
2486 ASSERT_EQ(1, observer
.GetStateCount());
2487 ASSERT_EQ(observer
.GetStateAt(0).action
,
2488 MockTabStripModelObserver::SELECT
);
2489 observer
.ClearStates();
2491 // Attempting to deselect the only selected and therefore active tab,
2492 // it is ignored (no notifications being sent) and tab at 0 remains selected
2494 strip
.ToggleSelectionAt(0);
2495 ASSERT_EQ(0, observer
.GetStateCount());
2497 strip
.RemoveObserver(&observer
);
2498 strip
.CloseAllTabs();
2501 // Verifies that if we change the selection from a multi selection to a single
2502 // selection, but not in a way that changes the selected_index that
2503 // TabSelectionChanged is invoked.
2504 TEST_F(TabStripModelTest
, MultipleToSingle
) {
2505 typedef MockTabStripModelObserver::State State
;
2507 TabStripDummyDelegate delegate
;
2508 TabStripModel
strip(&delegate
, profile());
2509 WebContents
* contents1
= CreateWebContents();
2510 WebContents
* contents2
= CreateWebContents();
2511 strip
.AppendWebContents(contents1
, false);
2512 strip
.AppendWebContents(contents2
, false);
2513 strip
.ToggleSelectionAt(0);
2514 strip
.ToggleSelectionAt(1);
2516 MockTabStripModelObserver
observer(&strip
);
2517 strip
.AddObserver(&observer
);
2518 // This changes the selection (0 is no longer selected) but the selected_index
2519 // still remains at 1.
2520 strip
.ActivateTabAt(1, true);
2521 ASSERT_EQ(1, observer
.GetStateCount());
2522 State
s(contents2
, 1, MockTabStripModelObserver::SELECT
);
2523 s
.src_contents
= contents2
;
2525 s
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
2526 EXPECT_TRUE(observer
.StateEquals(0, s
));
2527 strip
.RemoveObserver(&observer
);
2528 strip
.CloseAllTabs();
2531 // Verifies a newly inserted tab retains its previous blocked state.
2532 // http://crbug.com/276334
2533 TEST_F(TabStripModelTest
, TabBlockedState
) {
2534 // Start with a source tab strip.
2535 TabStripDummyDelegate dummy_tab_strip_delegate
;
2536 TabStripModel
strip_src(&dummy_tab_strip_delegate
, profile());
2537 TabBlockedStateTestBrowser
browser_src(&strip_src
);
2540 WebContents
* contents1
= CreateWebContents();
2541 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1
);
2542 strip_src
.AppendWebContents(contents1
, false);
2545 WebContents
* contents2
= CreateWebContents();
2546 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents2
);
2547 strip_src
.AppendWebContents(contents2
, false);
2549 // Create a destination tab strip.
2550 TabStripModel
strip_dst(&dummy_tab_strip_delegate
, profile());
2551 TabBlockedStateTestBrowser
browser_dst(&strip_dst
);
2553 // Setup a SingleWebContentsDialogManager for tab |contents2|.
2554 web_modal::WebContentsModalDialogManager
* modal_dialog_manager
=
2555 web_modal::WebContentsModalDialogManager::FromWebContents(contents2
);
2556 web_modal::PopupManager
popup_manager(NULL
);
2557 popup_manager
.RegisterWith(contents2
);
2559 // Show a dialog that blocks tab |contents2|.
2560 // DummySingleWebContentsDialogManager doesn't care about the
2561 // NativeWebContentsModalDialog value, so any dummy value works.
2562 DummySingleWebContentsDialogManager
* native_manager
=
2563 new DummySingleWebContentsDialogManager(
2564 reinterpret_cast<NativeWebContentsModalDialog
>(0),
2565 modal_dialog_manager
);
2566 modal_dialog_manager
->ShowDialogWithManager(
2567 reinterpret_cast<NativeWebContentsModalDialog
>(0),
2568 scoped_ptr
<web_modal::SingleWebContentsDialogManager
>(
2569 native_manager
).Pass());
2570 EXPECT_TRUE(strip_src
.IsTabBlocked(1));
2573 WebContents
* moved_contents
= strip_src
.DetachWebContentsAt(1);
2574 EXPECT_EQ(contents2
, moved_contents
);
2576 // Attach the tab to the destination tab strip.
2577 strip_dst
.AppendWebContents(moved_contents
, true);
2578 EXPECT_TRUE(strip_dst
.IsTabBlocked(0));
2580 strip_dst
.CloseAllTabs();
2581 strip_src
.CloseAllTabs();