1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/tabs/tab_strip_model.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/defaults.h"
18 #include "chrome/browser/extensions/tab_helper.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_tabstrip.h"
22 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h"
24 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
25 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
26 #include "chrome/common/url_constants.h"
27 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
28 #include "chrome/test/base/testing_profile.h"
29 #include "components/web_modal/popup_manager.h"
30 #include "components/web_modal/web_contents_modal_dialog_manager.h"
31 #include "content/public/browser/navigation_controller.h"
32 #include "content/public/browser/navigation_entry.h"
33 #include "content/public/browser/render_process_host.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/browser/web_contents_observer.h"
36 #include "extensions/common/extension.h"
37 #include "testing/gtest/include/gtest/gtest.h"
39 using content::SiteInstance
;
40 using content::WebContents
;
41 using extensions::Extension
;
45 // Class used to delete a WebContents and TabStripModel when another WebContents
47 class DeleteWebContentsOnDestroyedObserver
48 : public content::WebContentsObserver
{
50 // When |source| is deleted both |tab_to_delete| and |tab_strip| are deleted.
51 // |tab_to_delete| and |tab_strip| may be NULL.
52 DeleteWebContentsOnDestroyedObserver(WebContents
* source
,
53 WebContents
* tab_to_delete
,
54 TabStripModel
* tab_strip
)
55 : WebContentsObserver(source
),
56 tab_to_delete_(tab_to_delete
),
57 tab_strip_(tab_strip
) {
60 void WebContentsDestroyed() override
{
61 WebContents
* tab_to_delete
= tab_to_delete_
;
62 tab_to_delete_
= NULL
;
63 TabStripModel
* tab_strip_to_delete
= tab_strip_
;
66 delete tab_strip_to_delete
;
70 WebContents
* tab_to_delete_
;
71 TabStripModel
* tab_strip_
;
73 DISALLOW_COPY_AND_ASSIGN(DeleteWebContentsOnDestroyedObserver
);
76 class TabStripDummyDelegate
: public TestTabStripModelDelegate
{
78 TabStripDummyDelegate() : run_unload_(false) {}
79 ~TabStripDummyDelegate() override
{}
81 void set_run_unload_listener(bool value
) { run_unload_
= value
; }
83 bool RunUnloadListenerBeforeClosing(WebContents
* contents
) override
{
88 // Whether to report that we need to run an unload listener before closing.
91 DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate
);
94 const char kTabStripModelTestIDUserDataKey
[] = "TabStripModelTestIDUserData";
96 class TabStripModelTestIDUserData
: public base::SupportsUserData::Data
{
98 explicit TabStripModelTestIDUserData(int id
) : id_(id
) {}
99 ~TabStripModelTestIDUserData() override
{}
100 int id() { return id_
; }
106 class DummySingleWebContentsDialogManager
107 : public web_modal::SingleWebContentsDialogManager
{
109 explicit DummySingleWebContentsDialogManager(
110 gfx::NativeWindow dialog
,
111 web_modal::SingleWebContentsDialogManagerDelegate
* delegate
)
112 : delegate_(delegate
),
114 ~DummySingleWebContentsDialogManager() override
{}
116 void Show() override
{}
117 void Hide() override
{}
118 void Close() override
{ delegate_
->WillClose(dialog_
); }
119 void Focus() override
{}
120 void Pulse() override
{}
121 void HostChanged(web_modal::WebContentsModalDialogHost
* new_host
) override
{}
122 gfx::NativeWindow
dialog() override
{ return dialog_
; }
125 web_modal::SingleWebContentsDialogManagerDelegate
* delegate_
;
126 gfx::NativeWindow dialog_
;
128 DISALLOW_COPY_AND_ASSIGN(DummySingleWebContentsDialogManager
);
131 // Test Browser-like class for TabStripModelTest.TabBlockedState.
132 class TabBlockedStateTestBrowser
133 : public TabStripModelObserver
,
134 public web_modal::WebContentsModalDialogManagerDelegate
{
136 explicit TabBlockedStateTestBrowser(TabStripModel
* tab_strip_model
)
137 : tab_strip_model_(tab_strip_model
) {
138 tab_strip_model_
->AddObserver(this);
141 ~TabBlockedStateTestBrowser() override
{
142 tab_strip_model_
->RemoveObserver(this);
146 // TabStripModelObserver
147 void TabInsertedAt(WebContents
* contents
,
149 bool foreground
) override
{
150 web_modal::WebContentsModalDialogManager
* manager
=
151 web_modal::WebContentsModalDialogManager::FromWebContents(contents
);
153 manager
->SetDelegate(this);
156 // WebContentsModalDialogManagerDelegate
157 void SetWebContentsBlocked(content::WebContents
* contents
,
158 bool blocked
) override
{
159 int index
= tab_strip_model_
->GetIndexOfWebContents(contents
);
161 tab_strip_model_
->SetTabBlocked(index
, blocked
);
164 TabStripModel
* tab_strip_model_
;
166 DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser
);
171 class TabStripModelTest
: public ChromeRenderViewHostTestHarness
{
173 WebContents
* CreateWebContents() {
174 return WebContents::Create(WebContents::CreateParams(profile()));
177 WebContents
* CreateWebContentsWithSharedRPH(WebContents
* web_contents
) {
178 WebContents::CreateParams
create_params(
179 profile(), web_contents
->GetRenderViewHost()->GetSiteInstance());
180 WebContents
* retval
= WebContents::Create(create_params
);
181 EXPECT_EQ(retval
->GetRenderProcessHost(),
182 web_contents
->GetRenderProcessHost());
186 WebContents
* CreateWebContentsWithID(int id
) {
187 WebContents
* contents
= CreateWebContents();
192 // Sets the id of the specified contents.
193 void SetID(WebContents
* contents
, int id
) {
194 contents
->SetUserData(&kTabStripModelTestIDUserDataKey
,
195 new TabStripModelTestIDUserData(id
));
198 // Returns the id of the specified contents.
199 int GetID(WebContents
* contents
) {
200 TabStripModelTestIDUserData
* user_data
=
201 static_cast<TabStripModelTestIDUserData
*>(
202 contents
->GetUserData(&kTabStripModelTestIDUserDataKey
));
204 return user_data
? user_data
->id() : -1;
207 // Returns the state of the given tab strip as a string. The state consists
208 // of the ID of each web contents followed by a 'p' if pinned. For example,
209 // if the model consists of two tabs with ids 2 and 1, with the first
210 // tab pinned, this returns "2p 1".
211 std::string
GetTabStripStateString(const TabStripModel
& model
) {
213 for (int i
= 0; i
< model
.count(); ++i
) {
217 actual
+= base::IntToString(GetID(model
.GetWebContentsAt(i
)));
219 if (model
.IsTabPinned(i
))
225 std::string
GetIndicesClosedByCommandAsString(
226 const TabStripModel
& model
,
228 TabStripModel::ContextMenuCommand id
) const {
229 std::vector
<int> indices
= model
.GetIndicesClosedByCommand(index
, id
);
231 for (size_t i
= 0; i
< indices
.size(); ++i
) {
234 result
+= base::IntToString(indices
[i
]);
239 void PrepareTabstripForSelectionTest(TabStripModel
* model
,
242 const std::string
& selected_tabs
) {
243 for (int i
= 0; i
< tab_count
; ++i
)
244 model
->AppendWebContents(CreateWebContentsWithID(i
), true);
245 for (int i
= 0; i
< pinned_count
; ++i
)
246 model
->SetTabPinned(i
, true);
248 ui::ListSelectionModel selection_model
;
249 for (const base::StringPiece
& sel
: base::SplitStringPiece(
250 selected_tabs
, base::kWhitespaceASCII
,
251 base::TRIM_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
)) {
253 ASSERT_TRUE(base::StringToInt(sel
, &value
));
254 selection_model
.AddIndexToSelection(value
);
256 selection_model
.set_active(selection_model
.selected_indices()[0]);
257 model
->SetSelectionFromModel(selection_model
);
261 class MockTabStripModelObserver
: public TabStripModelObserver
{
263 explicit MockTabStripModelObserver(TabStripModel
* model
)
267 ~MockTabStripModelObserver() override
{}
269 enum TabStripModelObserverAction
{
285 State(WebContents
* a_dst_contents
,
287 TabStripModelObserverAction a_action
)
288 : src_contents(NULL
),
289 dst_contents(a_dst_contents
),
291 dst_index(a_dst_index
),
292 change_reason(CHANGE_REASON_NONE
),
297 WebContents
* src_contents
;
298 WebContents
* dst_contents
;
303 TabStripModelObserverAction action
;
306 int GetStateCount() const {
307 return static_cast<int>(states_
.size());
310 // Returns (by way of parameters) the number of state's with CLOSE_ALL and
311 // CLOSE_ALL_CANCELED.
312 void GetCloseCounts(int* close_all_count
,
313 int* close_all_canceled_count
) {
314 *close_all_count
= *close_all_canceled_count
= 0;
315 for (int i
= 0; i
< GetStateCount(); ++i
) {
316 switch (GetStateAt(i
).action
) {
318 (*close_all_count
)++;
320 case CLOSE_ALL_CANCELED
:
321 (*close_all_canceled_count
)++;
329 const State
& GetStateAt(int index
) const {
330 DCHECK(index
>= 0 && index
< GetStateCount());
331 return states_
[index
];
334 bool StateEquals(int index
, const State
& state
) {
335 const State
& s
= GetStateAt(index
);
336 return (s
.src_contents
== state
.src_contents
&&
337 s
.dst_contents
== state
.dst_contents
&&
338 s
.src_index
== state
.src_index
&&
339 s
.dst_index
== state
.dst_index
&&
340 s
.change_reason
== state
.change_reason
&&
341 s
.foreground
== state
.foreground
&&
342 s
.action
== state
.action
);
345 // TabStripModelObserver implementation:
346 void TabInsertedAt(WebContents
* contents
,
348 bool foreground
) override
{
350 State
s(contents
, index
, INSERT
);
351 s
.foreground
= foreground
;
352 states_
.push_back(s
);
354 void ActiveTabChanged(WebContents
* old_contents
,
355 WebContents
* new_contents
,
357 int reason
) override
{
358 State
s(new_contents
, index
, ACTIVATE
);
359 s
.src_contents
= old_contents
;
360 s
.change_reason
= reason
;
361 states_
.push_back(s
);
363 void TabSelectionChanged(TabStripModel
* tab_strip_model
,
364 const ui::ListSelectionModel
& old_model
) override
{
365 State
s(model()->GetActiveWebContents(), model()->active_index(), SELECT
);
366 s
.src_contents
= model()->GetWebContentsAt(old_model
.active());
367 s
.src_index
= old_model
.active();
368 states_
.push_back(s
);
370 void TabMoved(WebContents
* contents
, int from_index
, int to_index
) override
{
371 State
s(contents
, to_index
, MOVE
);
372 s
.src_index
= from_index
;
373 states_
.push_back(s
);
376 void TabClosingAt(TabStripModel
* tab_strip_model
,
377 WebContents
* contents
,
378 int index
) override
{
379 states_
.push_back(State(contents
, index
, CLOSE
));
381 void TabDetachedAt(WebContents
* contents
, int index
) override
{
382 states_
.push_back(State(contents
, index
, DETACH
));
384 void TabDeactivated(WebContents
* contents
) override
{
385 states_
.push_back(State(contents
, model()->active_index(), DEACTIVATE
));
387 void TabChangedAt(WebContents
* contents
,
389 TabChangeType change_type
) override
{
390 states_
.push_back(State(contents
, index
, CHANGE
));
392 void TabReplacedAt(TabStripModel
* tab_strip_model
,
393 WebContents
* old_contents
,
394 WebContents
* new_contents
,
395 int index
) override
{
396 State
s(new_contents
, index
, REPLACED
);
397 s
.src_contents
= old_contents
;
398 states_
.push_back(s
);
400 void TabPinnedStateChanged(WebContents
* contents
, int index
) override
{
401 states_
.push_back(State(contents
, index
, PINNED
));
403 void TabStripEmpty() override
{ empty_
= true; }
404 void WillCloseAllTabs() override
{
405 states_
.push_back(State(NULL
, -1, CLOSE_ALL
));
407 void CloseAllTabsCanceled() override
{
408 states_
.push_back(State(NULL
, -1, CLOSE_ALL_CANCELED
));
410 void TabStripModelDeleted() override
{ deleted_
= true; }
416 bool empty() const { return empty_
; }
417 bool deleted() const { return deleted_
; }
418 TabStripModel
* model() { return model_
; }
421 std::vector
<State
> states_
;
425 TabStripModel
* model_
;
427 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver
);
430 TEST_F(TabStripModelTest
, TestBasicAPI
) {
431 TabStripDummyDelegate delegate
;
432 TabStripModel
tabstrip(&delegate
, profile());
433 MockTabStripModelObserver
observer(&tabstrip
);
434 tabstrip
.AddObserver(&observer
);
436 EXPECT_TRUE(tabstrip
.empty());
438 typedef MockTabStripModelObserver::State State
;
440 WebContents
* contents1
= CreateWebContentsWithID(1);
442 // Note! The ordering of these tests is important, each subsequent test
443 // builds on the state established in the previous. This is important if you
444 // ever insert tests rather than append.
446 // Test AppendWebContents, ContainsIndex
448 EXPECT_FALSE(tabstrip
.ContainsIndex(0));
449 tabstrip
.AppendWebContents(contents1
, true);
450 EXPECT_TRUE(tabstrip
.ContainsIndex(0));
451 EXPECT_EQ(1, tabstrip
.count());
452 EXPECT_EQ(3, observer
.GetStateCount());
453 State
s1(contents1
, 0, MockTabStripModelObserver::INSERT
);
454 s1
.foreground
= true;
455 EXPECT_TRUE(observer
.StateEquals(0, s1
));
456 State
s2(contents1
, 0, MockTabStripModelObserver::ACTIVATE
);
457 EXPECT_TRUE(observer
.StateEquals(1, s2
));
458 State
s3(contents1
, 0, MockTabStripModelObserver::SELECT
);
459 s3
.src_contents
= NULL
;
460 s3
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
461 EXPECT_TRUE(observer
.StateEquals(2, s3
));
462 observer
.ClearStates();
464 EXPECT_EQ("1", GetTabStripStateString(tabstrip
));
466 // Test InsertWebContentsAt, foreground tab.
467 WebContents
* contents2
= CreateWebContentsWithID(2);
469 tabstrip
.InsertWebContentsAt(1, contents2
, TabStripModel::ADD_ACTIVE
);
471 EXPECT_EQ(2, tabstrip
.count());
472 EXPECT_EQ(4, observer
.GetStateCount());
473 State
s1(contents2
, 1, MockTabStripModelObserver::INSERT
);
474 s1
.foreground
= true;
475 EXPECT_TRUE(observer
.StateEquals(0, s1
));
476 State
s2(contents1
, 0, MockTabStripModelObserver::DEACTIVATE
);
477 EXPECT_TRUE(observer
.StateEquals(1, s2
));
478 State
s3(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
479 s3
.src_contents
= contents1
;
480 EXPECT_TRUE(observer
.StateEquals(2, s3
));
481 State
s4(contents2
, 1, MockTabStripModelObserver::SELECT
);
482 s4
.src_contents
= contents1
;
484 EXPECT_TRUE(observer
.StateEquals(3, s4
));
485 observer
.ClearStates();
487 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip
));
489 // Test InsertWebContentsAt, background tab.
490 WebContents
* contents3
= CreateWebContentsWithID(3);
492 tabstrip
.InsertWebContentsAt(2, contents3
, TabStripModel::ADD_NONE
);
494 EXPECT_EQ(3, tabstrip
.count());
495 EXPECT_EQ(1, observer
.GetStateCount());
496 State
s1(contents3
, 2, MockTabStripModelObserver::INSERT
);
497 s1
.foreground
= false;
498 EXPECT_TRUE(observer
.StateEquals(0, s1
));
499 observer
.ClearStates();
501 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
503 // Test ActivateTabAt
505 tabstrip
.ActivateTabAt(2, true);
506 EXPECT_EQ(3, observer
.GetStateCount());
507 State
s1(contents2
, 1, MockTabStripModelObserver::DEACTIVATE
);
508 EXPECT_TRUE(observer
.StateEquals(0, s1
));
509 State
s2(contents3
, 2, MockTabStripModelObserver::ACTIVATE
);
510 s2
.src_contents
= contents2
;
511 s2
.change_reason
= TabStripModelObserver::CHANGE_REASON_USER_GESTURE
;
512 EXPECT_TRUE(observer
.StateEquals(1, s2
));
513 State
s3(contents3
, 2, MockTabStripModelObserver::SELECT
);
514 s3
.src_contents
= contents2
;
516 EXPECT_TRUE(observer
.StateEquals(2, s3
));
517 observer
.ClearStates();
519 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
521 // Test DetachWebContentsAt
524 WebContents
* detached
= tabstrip
.DetachWebContentsAt(2);
525 // ... and append again because we want this for later.
526 tabstrip
.AppendWebContents(detached
, true);
527 EXPECT_EQ(8, observer
.GetStateCount());
528 State
s1(detached
, 2, MockTabStripModelObserver::DETACH
);
529 EXPECT_TRUE(observer
.StateEquals(0, s1
));
530 State
s2(detached
, ui::ListSelectionModel::kUnselectedIndex
,
531 MockTabStripModelObserver::DEACTIVATE
);
532 EXPECT_TRUE(observer
.StateEquals(1, s2
));
533 State
s3(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
534 s3
.src_contents
= contents3
;
535 s3
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
536 EXPECT_TRUE(observer
.StateEquals(2, s3
));
537 State
s4(contents2
, 1, MockTabStripModelObserver::SELECT
);
538 s4
.src_contents
= NULL
;
539 s4
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
540 EXPECT_TRUE(observer
.StateEquals(3, s4
));
541 State
s5(detached
, 2, MockTabStripModelObserver::INSERT
);
542 s5
.foreground
= true;
543 EXPECT_TRUE(observer
.StateEquals(4, s5
));
544 State
s6(contents2
, 1, MockTabStripModelObserver::DEACTIVATE
);
545 EXPECT_TRUE(observer
.StateEquals(5, s6
));
546 State
s7(detached
, 2, MockTabStripModelObserver::ACTIVATE
);
547 s7
.src_contents
= contents2
;
548 s7
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
549 EXPECT_TRUE(observer
.StateEquals(6, s7
));
550 State
s8(detached
, 2, MockTabStripModelObserver::SELECT
);
551 s8
.src_contents
= contents2
;
553 EXPECT_TRUE(observer
.StateEquals(7, s8
));
554 observer
.ClearStates();
556 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
558 // Test CloseWebContentsAt
560 EXPECT_TRUE(tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
));
561 EXPECT_EQ(2, tabstrip
.count());
563 EXPECT_EQ(5, observer
.GetStateCount());
564 State
s1(contents3
, 2, MockTabStripModelObserver::CLOSE
);
565 EXPECT_TRUE(observer
.StateEquals(0, s1
));
566 State
s2(contents3
, 2, MockTabStripModelObserver::DETACH
);
567 EXPECT_TRUE(observer
.StateEquals(1, s2
));
568 State
s3(contents3
, ui::ListSelectionModel::kUnselectedIndex
,
569 MockTabStripModelObserver::DEACTIVATE
);
570 EXPECT_TRUE(observer
.StateEquals(2, s3
));
571 State
s4(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
572 s4
.src_contents
= contents3
;
573 s4
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
574 EXPECT_TRUE(observer
.StateEquals(3, s4
));
575 State
s5(contents2
, 1, MockTabStripModelObserver::SELECT
);
576 s5
.src_contents
= NULL
;
577 s5
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
578 EXPECT_TRUE(observer
.StateEquals(4, s5
));
579 observer
.ClearStates();
581 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip
));
583 // Test MoveWebContentsAt, select_after_move == true
585 tabstrip
.MoveWebContentsAt(1, 0, true);
587 EXPECT_EQ(1, observer
.GetStateCount());
588 State
s1(contents2
, 0, MockTabStripModelObserver::MOVE
);
590 EXPECT_TRUE(observer
.StateEquals(0, s1
));
591 EXPECT_EQ(0, tabstrip
.active_index());
592 observer
.ClearStates();
594 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip
));
596 // Test MoveWebContentsAt, select_after_move == false
598 tabstrip
.MoveWebContentsAt(1, 0, false);
599 EXPECT_EQ(1, observer
.GetStateCount());
600 State
s1(contents1
, 0, MockTabStripModelObserver::MOVE
);
602 EXPECT_TRUE(observer
.StateEquals(0, s1
));
603 EXPECT_EQ(1, tabstrip
.active_index());
605 tabstrip
.MoveWebContentsAt(0, 1, false);
606 observer
.ClearStates();
608 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip
));
612 EXPECT_EQ(contents2
, tabstrip
.GetActiveWebContents());
613 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(0));
614 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
615 EXPECT_EQ(0, tabstrip
.GetIndexOfWebContents(contents2
));
616 EXPECT_EQ(1, tabstrip
.GetIndexOfWebContents(contents1
));
619 // Test UpdateWebContentsStateAt
621 tabstrip
.UpdateWebContentsStateAt(0, TabStripModelObserver::ALL
);
622 EXPECT_EQ(1, observer
.GetStateCount());
623 State
s1(contents2
, 0, MockTabStripModelObserver::CHANGE
);
624 EXPECT_TRUE(observer
.StateEquals(0, s1
));
625 observer
.ClearStates();
628 // Test SelectNextTab, SelectPreviousTab, SelectLastTab
630 // Make sure the second of the two tabs is selected first...
631 tabstrip
.ActivateTabAt(1, true);
632 tabstrip
.SelectPreviousTab();
633 EXPECT_EQ(0, tabstrip
.active_index());
634 tabstrip
.SelectLastTab();
635 EXPECT_EQ(1, tabstrip
.active_index());
636 tabstrip
.SelectNextTab();
637 EXPECT_EQ(0, tabstrip
.active_index());
640 // Test CloseSelectedTabs
642 tabstrip
.CloseSelectedTabs();
643 // |CloseSelectedTabs| calls CloseWebContentsAt, we already tested that, now
644 // just verify that the count and selected index have changed
646 EXPECT_EQ(1, tabstrip
.count());
647 EXPECT_EQ(0, tabstrip
.active_index());
650 observer
.ClearStates();
651 tabstrip
.CloseAllTabs();
653 int close_all_count
= 0, close_all_canceled_count
= 0;
654 observer
.GetCloseCounts(&close_all_count
, &close_all_canceled_count
);
655 EXPECT_EQ(1, close_all_count
);
656 EXPECT_EQ(0, close_all_canceled_count
);
658 // TabStripModel should now be empty.
659 EXPECT_TRUE(tabstrip
.empty());
661 // Opener methods are tested below...
663 tabstrip
.RemoveObserver(&observer
);
666 TEST_F(TabStripModelTest
, TestBasicOpenerAPI
) {
667 TabStripDummyDelegate delegate
;
668 TabStripModel
tabstrip(&delegate
, profile());
669 EXPECT_TRUE(tabstrip
.empty());
671 // This is a basic test of opener functionality. opener is created
672 // as the first tab in the strip and then we create 5 other tabs in the
673 // background with opener set as their opener.
675 WebContents
* opener
= CreateWebContents();
676 tabstrip
.AppendWebContents(opener
, true);
677 WebContents
* contents1
= CreateWebContents();
678 WebContents
* contents2
= CreateWebContents();
679 WebContents
* contents3
= CreateWebContents();
680 WebContents
* contents4
= CreateWebContents();
681 WebContents
* contents5
= CreateWebContents();
683 // We use |InsertWebContentsAt| here instead of |AppendWebContents| so that
684 // openership relationships are preserved.
685 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents1
,
686 TabStripModel::ADD_INHERIT_GROUP
);
687 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents2
,
688 TabStripModel::ADD_INHERIT_GROUP
);
689 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents3
,
690 TabStripModel::ADD_INHERIT_GROUP
);
691 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents4
,
692 TabStripModel::ADD_INHERIT_GROUP
);
693 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents5
,
694 TabStripModel::ADD_INHERIT_GROUP
);
696 // All the tabs should have the same opener.
697 for (int i
= 1; i
< tabstrip
.count(); ++i
)
698 EXPECT_EQ(opener
, tabstrip
.GetOpenerOfWebContentsAt(i
));
700 // If there is a next adjacent item, then the index should be of that item.
701 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 1, false));
702 // If the last tab in the group is closed, the preceding tab in the same
703 // group should be selected.
704 EXPECT_EQ(4, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 5, false));
706 // Tests the method that finds the last tab opened by the same opener in the
707 // strip (this is the insertion index for the next background tab for the
708 // specified opener).
709 EXPECT_EQ(5, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
711 // For a tab that has opened no other tabs, the return value should always be
714 tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents1
, 3, false));
716 tabstrip
.GetIndexOfLastWebContentsOpenedBy(contents1
, 3));
718 // ForgetAllOpeners should destroy all opener relationships.
719 tabstrip
.ForgetAllOpeners();
720 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 1, false));
721 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 5, false));
722 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
724 // Specify the last tab as the opener of the others.
725 for (int i
= 0; i
< tabstrip
.count() - 1; ++i
)
726 tabstrip
.SetOpenerOfWebContentsAt(i
, contents5
);
728 for (int i
= 0; i
< tabstrip
.count() - 1; ++i
)
729 EXPECT_EQ(contents5
, tabstrip
.GetOpenerOfWebContentsAt(i
));
731 // If there is a next adjacent item, then the index should be of that item.
732 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents5
, 1, false));
734 // If the last tab in the group is closed, the preceding tab in the same
735 // group should be selected.
736 EXPECT_EQ(3, tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents5
, 4, false));
738 tabstrip
.CloseAllTabs();
739 EXPECT_TRUE(tabstrip
.empty());
742 static int GetInsertionIndex(TabStripModel
* tabstrip
) {
743 return tabstrip
->order_controller()->DetermineInsertionIndex(
744 ui::PAGE_TRANSITION_LINK
, false);
747 static void InsertWebContentses(TabStripModel
* tabstrip
,
748 WebContents
* contents1
,
749 WebContents
* contents2
,
750 WebContents
* contents3
) {
751 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
753 TabStripModel::ADD_INHERIT_GROUP
);
754 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
756 TabStripModel::ADD_INHERIT_GROUP
);
757 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
759 TabStripModel::ADD_INHERIT_GROUP
);
762 // Tests opening background tabs.
763 TEST_F(TabStripModelTest
, TestLTRInsertionOptions
) {
764 TabStripDummyDelegate delegate
;
765 TabStripModel
tabstrip(&delegate
, profile());
766 EXPECT_TRUE(tabstrip
.empty());
768 WebContents
* opener
= CreateWebContents();
769 tabstrip
.AppendWebContents(opener
, true);
771 WebContents
* contents1
= CreateWebContents();
772 WebContents
* contents2
= CreateWebContents();
773 WebContents
* contents3
= CreateWebContents();
776 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
777 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
778 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(2));
779 EXPECT_EQ(contents3
, tabstrip
.GetWebContentsAt(3));
781 tabstrip
.CloseAllTabs();
782 EXPECT_TRUE(tabstrip
.empty());
785 // This test constructs a tabstrip, and then simulates loading several tabs in
786 // the background from link clicks on the first tab. Then it simulates opening
787 // a new tab from the first tab in the foreground via a link click, verifies
788 // that this tab is opened adjacent to the opener, then closes it.
789 // Finally it tests that a tab opened for some non-link purpose opens at the
790 // end of the strip, not bundled to any existing context.
791 TEST_F(TabStripModelTest
, TestInsertionIndexDetermination
) {
792 TabStripDummyDelegate delegate
;
793 TabStripModel
tabstrip(&delegate
, profile());
794 EXPECT_TRUE(tabstrip
.empty());
796 WebContents
* opener
= CreateWebContents();
797 tabstrip
.AppendWebContents(opener
, true);
799 // Open some other random unrelated tab in the background to monkey with our
801 WebContents
* other
= CreateWebContents();
802 tabstrip
.AppendWebContents(other
, false);
804 WebContents
* contents1
= CreateWebContents();
805 WebContents
* contents2
= CreateWebContents();
806 WebContents
* contents3
= CreateWebContents();
808 // Start by testing LTR.
809 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
810 EXPECT_EQ(opener
, tabstrip
.GetWebContentsAt(0));
811 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
812 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(2));
813 EXPECT_EQ(contents3
, tabstrip
.GetWebContentsAt(3));
814 EXPECT_EQ(other
, tabstrip
.GetWebContentsAt(4));
816 // The opener API should work...
817 EXPECT_EQ(3, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 2, false));
818 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
819 EXPECT_EQ(3, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
821 // Now open a foreground tab from a link. It should be opened adjacent to the
823 WebContents
* fg_link_contents
= CreateWebContents();
824 int insert_index
= tabstrip
.order_controller()->DetermineInsertionIndex(
825 ui::PAGE_TRANSITION_LINK
, true);
826 EXPECT_EQ(1, insert_index
);
827 tabstrip
.InsertWebContentsAt(insert_index
, fg_link_contents
,
828 TabStripModel::ADD_ACTIVE
|
829 TabStripModel::ADD_INHERIT_GROUP
);
830 EXPECT_EQ(1, tabstrip
.active_index());
831 EXPECT_EQ(fg_link_contents
, tabstrip
.GetActiveWebContents());
833 // Now close this contents. The selection should move to the opener contents.
834 tabstrip
.CloseSelectedTabs();
835 EXPECT_EQ(0, tabstrip
.active_index());
837 // Now open a new empty tab. It should open at the end of the strip.
838 WebContents
* fg_nonlink_contents
= CreateWebContents();
839 insert_index
= tabstrip
.order_controller()->DetermineInsertionIndex(
840 ui::PAGE_TRANSITION_AUTO_BOOKMARK
, true);
841 EXPECT_EQ(tabstrip
.count(), insert_index
);
842 // We break the opener relationship...
843 tabstrip
.InsertWebContentsAt(insert_index
,
845 TabStripModel::ADD_NONE
);
846 // Now select it, so that user_gesture == true causes the opener relationship
847 // to be forgotten...
848 tabstrip
.ActivateTabAt(tabstrip
.count() - 1, true);
849 EXPECT_EQ(tabstrip
.count() - 1, tabstrip
.active_index());
850 EXPECT_EQ(fg_nonlink_contents
, tabstrip
.GetActiveWebContents());
852 // Verify that all opener relationships are forgotten.
853 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 2, false));
854 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
855 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
856 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
858 tabstrip
.CloseAllTabs();
859 EXPECT_TRUE(tabstrip
.empty());
862 // Tests that non-adjacent tabs with an opener are ignored when deciding where
864 TEST_F(TabStripModelTest
, TestInsertionIndexDeterminationAfterDragged
) {
865 TabStripDummyDelegate delegate
;
866 TabStripModel
tabstrip(&delegate
, profile());
867 EXPECT_TRUE(tabstrip
.empty());
869 // Start with three tabs, of which the first is active.
870 WebContents
* opener1
= CreateWebContentsWithID(1);
871 tabstrip
.AppendWebContents(opener1
, true /* foreground */);
872 tabstrip
.AppendWebContents(CreateWebContentsWithID(2), false);
873 tabstrip
.AppendWebContents(CreateWebContentsWithID(3), false);
874 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
875 EXPECT_EQ(1, GetID(tabstrip
.GetActiveWebContents()));
876 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener1
, 0));
878 // Open a link in a new background tab.
879 tabstrip
.InsertWebContentsAt(GetInsertionIndex(&tabstrip
),
880 CreateWebContentsWithID(11),
881 TabStripModel::ADD_INHERIT_GROUP
);
882 EXPECT_EQ("1 11 2 3", GetTabStripStateString(tabstrip
));
883 EXPECT_EQ(1, GetID(tabstrip
.GetActiveWebContents()));
884 EXPECT_EQ(1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener1
, 0));
886 // Drag that tab (which activates it) one to the right.
887 tabstrip
.MoveWebContentsAt(1, 2, true /* select_after_move */);
888 EXPECT_EQ("1 2 11 3", GetTabStripStateString(tabstrip
));
889 EXPECT_EQ(11, GetID(tabstrip
.GetActiveWebContents()));
890 // It should no longer be counted by GetIndexOfLastWebContentsOpenedBy,
891 // since there is a tab in between, even though its opener is unchanged.
892 // TODO(johnme): Maybe its opener should be reset when it's dragged away.
893 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener1
, 0));
894 EXPECT_EQ(opener1
, tabstrip
.GetOpenerOfWebContentsAt(2));
896 // Activate the parent tab again.
897 tabstrip
.ActivateTabAt(0, true /* user_gesture */);
898 EXPECT_EQ(1, GetID(tabstrip
.GetActiveWebContents()));
900 // Open another link in a new background tab.
901 tabstrip
.InsertWebContentsAt(GetInsertionIndex(&tabstrip
),
902 CreateWebContentsWithID(12),
903 TabStripModel::ADD_INHERIT_GROUP
);
904 // Tab 12 should be next to 1, and considered opened by it.
905 EXPECT_EQ("1 12 2 11 3", GetTabStripStateString(tabstrip
));
906 EXPECT_EQ(1, GetID(tabstrip
.GetActiveWebContents()));
907 EXPECT_EQ(1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener1
, 0));
909 tabstrip
.CloseAllTabs();
910 EXPECT_TRUE(tabstrip
.empty());
913 // Tests that grandchild tabs are considered to be opened by their grandparent
914 // tab when deciding where to position tabs.
915 TEST_F(TabStripModelTest
, TestInsertionIndexDeterminationNestedOpener
) {
916 TabStripDummyDelegate delegate
;
917 TabStripModel
tabstrip(&delegate
, profile());
918 EXPECT_TRUE(tabstrip
.empty());
920 // Start with two tabs, of which the first is active:
921 WebContents
* opener1
= CreateWebContentsWithID(1);
922 tabstrip
.AppendWebContents(opener1
, true /* foreground */);
923 tabstrip
.AppendWebContents(CreateWebContentsWithID(2), false);
924 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip
));
925 EXPECT_EQ(1, GetID(tabstrip
.GetActiveWebContents()));
926 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener1
, 0));
928 // Open a link in a new background child tab.
929 WebContents
* child11
= CreateWebContentsWithID(11);
930 tabstrip
.InsertWebContentsAt(GetInsertionIndex(&tabstrip
),
932 TabStripModel::ADD_INHERIT_GROUP
);
933 EXPECT_EQ("1 11 2", GetTabStripStateString(tabstrip
));
934 EXPECT_EQ(1, GetID(tabstrip
.GetActiveWebContents()));
935 EXPECT_EQ(1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener1
, 0));
937 // Activate the child tab:
938 tabstrip
.ActivateTabAt(1, true /* user_gesture */);
939 EXPECT_EQ(11, GetID(tabstrip
.GetActiveWebContents()));
941 // Open a link in a new background grandchild tab.
942 tabstrip
.InsertWebContentsAt(GetInsertionIndex(&tabstrip
),
943 CreateWebContentsWithID(111),
944 TabStripModel::ADD_INHERIT_GROUP
);
945 EXPECT_EQ("1 11 111 2", GetTabStripStateString(tabstrip
));
946 EXPECT_EQ(11, GetID(tabstrip
.GetActiveWebContents()));
947 // The grandchild tab should be counted by GetIndexOfLastWebContentsOpenedBy
948 // as opened by both its parent (child11) and grandparent (opener1).
949 EXPECT_EQ(2, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener1
, 0));
950 EXPECT_EQ(2, tabstrip
.GetIndexOfLastWebContentsOpenedBy(child11
, 1));
952 // Activate the parent tab again:
953 tabstrip
.ActivateTabAt(0, true /* user_gesture */);
954 EXPECT_EQ(1, GetID(tabstrip
.GetActiveWebContents()));
956 // Open another link in a new background child tab (a sibling of child11).
957 tabstrip
.InsertWebContentsAt(GetInsertionIndex(&tabstrip
),
958 CreateWebContentsWithID(12),
959 TabStripModel::ADD_INHERIT_GROUP
);
960 EXPECT_EQ("1 11 111 12 2", GetTabStripStateString(tabstrip
));
961 EXPECT_EQ(1, GetID(tabstrip
.GetActiveWebContents()));
962 // opener1 has three adjacent descendants (11, 111, 12)
963 EXPECT_EQ(3, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener1
, 0));
964 // child11 has only one adjacent descendant (111)
965 EXPECT_EQ(2, tabstrip
.GetIndexOfLastWebContentsOpenedBy(child11
, 1));
967 // Closing a tab should cause its children to inherit the tab's opener.
968 EXPECT_EQ(true, tabstrip
.CloseWebContentsAt(
970 TabStripModel::CLOSE_USER_GESTURE
|
971 TabStripModel::CLOSE_CREATE_HISTORICAL_TAB
));
972 EXPECT_EQ("1 111 12 2", GetTabStripStateString(tabstrip
));
973 EXPECT_EQ(1, GetID(tabstrip
.GetActiveWebContents()));
974 // opener1 is now the opener of 111, so has two adjacent descendants (111, 12)
975 EXPECT_EQ(opener1
, tabstrip
.GetOpenerOfWebContentsAt(1));
976 EXPECT_EQ(2, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener1
, 0));
978 tabstrip
.CloseAllTabs();
979 EXPECT_TRUE(tabstrip
.empty());
982 // Tests that selection is shifted to the correct tab when a tab is closed.
983 // If a tab is in the background when it is closed, the selection does not
985 // If a tab is in the foreground (selected),
986 // If that tab does not have an opener, selection shifts to the right.
987 // If the tab has an opener,
988 // The next tab (scanning LTR) in the entire strip that has the same opener
990 // If there are no other tabs that have the same opener,
991 // The opener is selected
993 TEST_F(TabStripModelTest
, TestSelectOnClose
) {
994 TabStripDummyDelegate delegate
;
995 TabStripModel
tabstrip(&delegate
, profile());
996 EXPECT_TRUE(tabstrip
.empty());
998 WebContents
* opener
= CreateWebContents();
999 tabstrip
.AppendWebContents(opener
, true);
1001 WebContents
* contents1
= CreateWebContents();
1002 WebContents
* contents2
= CreateWebContents();
1003 WebContents
* contents3
= CreateWebContents();
1005 // Note that we use Detach instead of Close throughout this test to avoid
1006 // having to keep reconstructing these WebContentses.
1008 // First test that closing tabs that are in the background doesn't adjust the
1009 // current selection.
1010 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1011 EXPECT_EQ(0, tabstrip
.active_index());
1013 tabstrip
.DetachWebContentsAt(1);
1014 EXPECT_EQ(0, tabstrip
.active_index());
1016 for (int i
= tabstrip
.count() - 1; i
>= 1; --i
)
1017 tabstrip
.DetachWebContentsAt(i
);
1019 // Now test that when a tab doesn't have an opener, selection shifts to the
1020 // right when the tab is closed.
1021 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1022 EXPECT_EQ(0, tabstrip
.active_index());
1024 tabstrip
.ForgetAllOpeners();
1025 tabstrip
.ActivateTabAt(1, true);
1026 EXPECT_EQ(1, tabstrip
.active_index());
1027 tabstrip
.DetachWebContentsAt(1);
1028 EXPECT_EQ(1, tabstrip
.active_index());
1029 tabstrip
.DetachWebContentsAt(1);
1030 EXPECT_EQ(1, tabstrip
.active_index());
1031 tabstrip
.DetachWebContentsAt(1);
1032 EXPECT_EQ(0, tabstrip
.active_index());
1034 for (int i
= tabstrip
.count() - 1; i
>= 1; --i
)
1035 tabstrip
.DetachWebContentsAt(i
);
1037 // Now test that when a tab does have an opener, it selects the next tab
1038 // opened by the same opener scanning LTR when it is closed.
1039 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1040 EXPECT_EQ(0, tabstrip
.active_index());
1041 tabstrip
.ActivateTabAt(2, false);
1042 EXPECT_EQ(2, tabstrip
.active_index());
1043 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1044 EXPECT_EQ(2, tabstrip
.active_index());
1045 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1046 EXPECT_EQ(1, tabstrip
.active_index());
1047 tabstrip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
1048 EXPECT_EQ(0, tabstrip
.active_index());
1049 // Finally test that when a tab has no "siblings" that the opener is
1051 WebContents
* other_contents
= CreateWebContents();
1052 tabstrip
.InsertWebContentsAt(1, other_contents
,
1053 TabStripModel::ADD_NONE
);
1054 EXPECT_EQ(2, tabstrip
.count());
1055 WebContents
* opened_contents
= CreateWebContents();
1056 tabstrip
.InsertWebContentsAt(2, opened_contents
,
1057 TabStripModel::ADD_ACTIVE
|
1058 TabStripModel::ADD_INHERIT_GROUP
);
1059 EXPECT_EQ(2, tabstrip
.active_index());
1060 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1061 EXPECT_EQ(0, tabstrip
.active_index());
1063 tabstrip
.CloseAllTabs();
1064 EXPECT_TRUE(tabstrip
.empty());
1067 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1069 TEST_F(TabStripModelTest
, CommandCloseTab
) {
1070 TabStripDummyDelegate delegate
;
1071 TabStripModel
tabstrip(&delegate
, profile());
1072 EXPECT_TRUE(tabstrip
.empty());
1074 // Make sure can_close is honored.
1075 ASSERT_NO_FATAL_FAILURE(
1076 PrepareTabstripForSelectionTest(&tabstrip
, 1, 0, "0"));
1077 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1078 0, TabStripModel::CommandCloseTab
));
1079 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
1080 ASSERT_TRUE(tabstrip
.empty());
1082 // Make sure close on a tab that is selected affects all the selected tabs.
1083 ASSERT_NO_FATAL_FAILURE(
1084 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
1085 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1086 0, TabStripModel::CommandCloseTab
));
1087 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
1088 // Should have closed tabs 0 and 1.
1089 EXPECT_EQ("2", GetTabStripStateString(tabstrip
));
1091 tabstrip
.CloseAllTabs();
1092 EXPECT_TRUE(tabstrip
.empty());
1094 // Select two tabs and make close on a tab that isn't selected doesn't affect
1096 ASSERT_NO_FATAL_FAILURE(
1097 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
1098 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1099 2, TabStripModel::CommandCloseTab
));
1100 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab
);
1101 // Should have closed tab 2.
1102 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
1103 tabstrip
.CloseAllTabs();
1104 EXPECT_TRUE(tabstrip
.empty());
1106 // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned.
1107 ASSERT_NO_FATAL_FAILURE(
1108 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "0 1"));
1109 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1110 0, TabStripModel::CommandCloseTab
));
1111 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
1112 // Should have closed tab 2.
1113 EXPECT_EQ("2", GetTabStripStateString(tabstrip
));
1114 tabstrip
.CloseAllTabs();
1115 EXPECT_TRUE(tabstrip
.empty());
1118 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1119 // CommandCloseTabs.
1120 TEST_F(TabStripModelTest
, CommandCloseOtherTabs
) {
1121 TabStripDummyDelegate delegate
;
1122 TabStripModel
tabstrip(&delegate
, profile());
1123 EXPECT_TRUE(tabstrip
.empty());
1125 // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled
1126 // and close two tabs.
1127 ASSERT_NO_FATAL_FAILURE(
1128 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
1129 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1130 0, TabStripModel::CommandCloseOtherTabs
));
1131 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs
);
1132 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
1133 tabstrip
.CloseAllTabs();
1134 EXPECT_TRUE(tabstrip
.empty());
1136 // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it
1137 // with a non-selected index should close the two other tabs.
1138 ASSERT_NO_FATAL_FAILURE(
1139 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
1140 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1141 2, TabStripModel::CommandCloseOtherTabs
));
1142 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs
);
1143 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
1144 tabstrip
.CloseAllTabs();
1145 EXPECT_TRUE(tabstrip
.empty());
1147 // Select all, CommandCloseOtherTabs should not be enabled.
1148 ASSERT_NO_FATAL_FAILURE(
1149 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1 2"));
1150 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1151 2, TabStripModel::CommandCloseOtherTabs
));
1152 tabstrip
.CloseAllTabs();
1153 EXPECT_TRUE(tabstrip
.empty());
1155 // Three tabs, pin one, select the two non-pinned.
1156 ASSERT_NO_FATAL_FAILURE(
1157 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "1 2"));
1158 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1159 1, TabStripModel::CommandCloseOtherTabs
));
1160 // If we don't pass in the pinned index, the command should be enabled.
1161 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1162 0, TabStripModel::CommandCloseOtherTabs
));
1163 tabstrip
.CloseAllTabs();
1164 EXPECT_TRUE(tabstrip
.empty());
1166 // 3 tabs, one pinned.
1167 ASSERT_NO_FATAL_FAILURE(
1168 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "1"));
1169 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1170 1, TabStripModel::CommandCloseOtherTabs
));
1171 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1172 0, TabStripModel::CommandCloseOtherTabs
));
1173 tabstrip
.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs
);
1174 // The pinned tab shouldn't be closed.
1175 EXPECT_EQ("0p 1", GetTabStripStateString(tabstrip
));
1176 tabstrip
.CloseAllTabs();
1177 EXPECT_TRUE(tabstrip
.empty());
1180 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1181 // CommandCloseTabsToRight.
1182 TEST_F(TabStripModelTest
, CommandCloseTabsToRight
) {
1183 TabStripDummyDelegate delegate
;
1184 TabStripModel
tabstrip(&delegate
, profile());
1185 EXPECT_TRUE(tabstrip
.empty());
1187 // Create three tabs, select last two tabs, CommandCloseTabsToRight should
1188 // only be enabled for the first tab.
1189 ASSERT_NO_FATAL_FAILURE(
1190 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "1 2"));
1191 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1192 0, TabStripModel::CommandCloseTabsToRight
));
1193 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1194 1, TabStripModel::CommandCloseTabsToRight
));
1195 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1196 2, TabStripModel::CommandCloseTabsToRight
));
1197 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight
);
1198 EXPECT_EQ("0", GetTabStripStateString(tabstrip
));
1199 tabstrip
.CloseAllTabs();
1200 EXPECT_TRUE(tabstrip
.empty());
1203 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1204 // CommandTogglePinned.
1205 TEST_F(TabStripModelTest
, CommandTogglePinned
) {
1206 TabStripDummyDelegate delegate
;
1207 TabStripModel
tabstrip(&delegate
, profile());
1208 EXPECT_TRUE(tabstrip
.empty());
1210 // Create three tabs with one pinned, pin the first two.
1211 ASSERT_NO_FATAL_FAILURE(
1212 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "0 1"));
1213 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1214 0, TabStripModel::CommandTogglePinned
));
1215 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1216 1, TabStripModel::CommandTogglePinned
));
1217 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1218 2, TabStripModel::CommandTogglePinned
));
1219 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned
);
1220 EXPECT_EQ("0p 1p 2", GetTabStripStateString(tabstrip
));
1222 // Execute CommandTogglePinned again, this should unpin.
1223 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned
);
1224 EXPECT_EQ("0 1 2", GetTabStripStateString(tabstrip
));
1227 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned
);
1228 EXPECT_EQ("2p 0 1", GetTabStripStateString(tabstrip
));
1230 tabstrip
.CloseAllTabs();
1231 EXPECT_TRUE(tabstrip
.empty());
1234 // Tests the following context menu commands:
1236 // - Close Other Tabs
1237 // - Close Tabs To Right
1238 TEST_F(TabStripModelTest
, TestContextMenuCloseCommands
) {
1239 TabStripDummyDelegate delegate
;
1240 TabStripModel
tabstrip(&delegate
, profile());
1241 EXPECT_TRUE(tabstrip
.empty());
1243 WebContents
* opener
= CreateWebContents();
1244 tabstrip
.AppendWebContents(opener
, true);
1246 WebContents
* contents1
= CreateWebContents();
1247 WebContents
* contents2
= CreateWebContents();
1248 WebContents
* contents3
= CreateWebContents();
1250 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1251 EXPECT_EQ(0, tabstrip
.active_index());
1253 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab
);
1254 EXPECT_EQ(3, tabstrip
.count());
1256 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight
);
1257 EXPECT_EQ(1, tabstrip
.count());
1258 EXPECT_EQ(opener
, tabstrip
.GetActiveWebContents());
1260 WebContents
* dummy
= CreateWebContents();
1261 tabstrip
.AppendWebContents(dummy
, false);
1263 contents1
= CreateWebContents();
1264 contents2
= CreateWebContents();
1265 contents3
= CreateWebContents();
1266 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1267 EXPECT_EQ(5, tabstrip
.count());
1269 int dummy_index
= tabstrip
.count() - 1;
1270 tabstrip
.ActivateTabAt(dummy_index
, true);
1271 EXPECT_EQ(dummy
, tabstrip
.GetActiveWebContents());
1273 tabstrip
.ExecuteContextMenuCommand(dummy_index
,
1274 TabStripModel::CommandCloseOtherTabs
);
1275 EXPECT_EQ(1, tabstrip
.count());
1276 EXPECT_EQ(dummy
, tabstrip
.GetActiveWebContents());
1278 tabstrip
.CloseAllTabs();
1279 EXPECT_TRUE(tabstrip
.empty());
1282 // Tests GetIndicesClosedByCommand.
1283 TEST_F(TabStripModelTest
, GetIndicesClosedByCommand
) {
1284 TabStripDummyDelegate delegate
;
1285 TabStripModel
tabstrip(&delegate
, profile());
1286 EXPECT_TRUE(tabstrip
.empty());
1288 WebContents
* contents1
= CreateWebContents();
1289 WebContents
* contents2
= CreateWebContents();
1290 WebContents
* contents3
= CreateWebContents();
1291 WebContents
* contents4
= CreateWebContents();
1292 WebContents
* contents5
= CreateWebContents();
1294 tabstrip
.AppendWebContents(contents1
, true);
1295 tabstrip
.AppendWebContents(contents2
, true);
1296 tabstrip
.AppendWebContents(contents3
, true);
1297 tabstrip
.AppendWebContents(contents4
, true);
1298 tabstrip
.AppendWebContents(contents5
, true);
1300 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1301 tabstrip
, 0, TabStripModel::CommandCloseTabsToRight
));
1302 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1303 tabstrip
, 1, TabStripModel::CommandCloseTabsToRight
));
1305 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1306 tabstrip
, 0, TabStripModel::CommandCloseOtherTabs
));
1307 EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
1308 tabstrip
, 1, TabStripModel::CommandCloseOtherTabs
));
1310 // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
1312 tabstrip
.SetTabPinned(0, true);
1313 tabstrip
.SetTabPinned(1, true);
1315 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1316 tabstrip
, 0, TabStripModel::CommandCloseTabsToRight
));
1317 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1318 tabstrip
, 2, TabStripModel::CommandCloseTabsToRight
));
1320 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1321 tabstrip
, 0, TabStripModel::CommandCloseOtherTabs
));
1322 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1323 tabstrip
, 2, TabStripModel::CommandCloseOtherTabs
));
1325 tabstrip
.CloseAllTabs();
1326 EXPECT_TRUE(tabstrip
.empty());
1329 // Tests whether or not WebContentses are inserted in the correct position
1330 // using this "smart" function with a simulated middle click action on a series
1331 // of links on the home page.
1332 TEST_F(TabStripModelTest
, AddWebContents_MiddleClickLinksAndClose
) {
1333 TabStripDummyDelegate delegate
;
1334 TabStripModel
tabstrip(&delegate
, profile());
1335 EXPECT_TRUE(tabstrip
.empty());
1337 // Open the Home Page.
1338 WebContents
* homepage_contents
= CreateWebContents();
1339 tabstrip
.AddWebContents(
1340 homepage_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1341 TabStripModel::ADD_ACTIVE
);
1343 // Open some other tab, by user typing.
1344 WebContents
* typed_page_contents
= CreateWebContents();
1345 tabstrip
.AddWebContents(
1346 typed_page_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1347 TabStripModel::ADD_ACTIVE
);
1349 EXPECT_EQ(2, tabstrip
.count());
1351 // Re-select the home page.
1352 tabstrip
.ActivateTabAt(0, true);
1354 // Open a bunch of tabs by simulating middle clicking on links on the home
1356 WebContents
* middle_click_contents1
= CreateWebContents();
1357 tabstrip
.AddWebContents(
1358 middle_click_contents1
, -1, ui::PAGE_TRANSITION_LINK
,
1359 TabStripModel::ADD_NONE
);
1360 WebContents
* middle_click_contents2
= CreateWebContents();
1361 tabstrip
.AddWebContents(
1362 middle_click_contents2
, -1, ui::PAGE_TRANSITION_LINK
,
1363 TabStripModel::ADD_NONE
);
1364 WebContents
* middle_click_contents3
= CreateWebContents();
1365 tabstrip
.AddWebContents(
1366 middle_click_contents3
, -1, ui::PAGE_TRANSITION_LINK
,
1367 TabStripModel::ADD_NONE
);
1369 EXPECT_EQ(5, tabstrip
.count());
1371 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1372 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetWebContentsAt(1));
1373 EXPECT_EQ(middle_click_contents2
, tabstrip
.GetWebContentsAt(2));
1374 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetWebContentsAt(3));
1375 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(4));
1377 // Now simulate selecting a tab in the middle of the group of tabs opened from
1378 // the home page and start closing them. Each WebContents in the group
1379 // should be closed, right to left. This test is constructed to start at the
1380 // middle WebContents in the group to make sure the cursor wraps around
1381 // to the first WebContents in the group before closing the opener or
1382 // any other WebContents.
1383 tabstrip
.ActivateTabAt(2, true);
1384 tabstrip
.CloseSelectedTabs();
1385 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetActiveWebContents());
1386 tabstrip
.CloseSelectedTabs();
1387 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetActiveWebContents());
1388 tabstrip
.CloseSelectedTabs();
1389 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1390 tabstrip
.CloseSelectedTabs();
1391 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1393 EXPECT_EQ(1, tabstrip
.count());
1395 tabstrip
.CloseAllTabs();
1396 EXPECT_TRUE(tabstrip
.empty());
1399 // Tests whether or not a WebContents created by a left click on a link
1400 // that opens a new tab is inserted correctly adjacent to the tab that spawned
1402 TEST_F(TabStripModelTest
, AddWebContents_LeftClickPopup
) {
1403 TabStripDummyDelegate delegate
;
1404 TabStripModel
tabstrip(&delegate
, profile());
1405 EXPECT_TRUE(tabstrip
.empty());
1407 // Open the Home Page.
1408 WebContents
* homepage_contents
= CreateWebContents();
1409 tabstrip
.AddWebContents(
1410 homepage_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1411 TabStripModel::ADD_ACTIVE
);
1413 // Open some other tab, by user typing.
1414 WebContents
* typed_page_contents
= CreateWebContents();
1415 tabstrip
.AddWebContents(
1416 typed_page_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1417 TabStripModel::ADD_ACTIVE
);
1419 EXPECT_EQ(2, tabstrip
.count());
1421 // Re-select the home page.
1422 tabstrip
.ActivateTabAt(0, true);
1424 // Open a tab by simulating a left click on a link that opens in a new tab.
1425 WebContents
* left_click_contents
= CreateWebContents();
1426 tabstrip
.AddWebContents(left_click_contents
, -1,
1427 ui::PAGE_TRANSITION_LINK
,
1428 TabStripModel::ADD_ACTIVE
);
1430 // Verify the state meets our expectations.
1431 EXPECT_EQ(3, tabstrip
.count());
1432 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1433 EXPECT_EQ(left_click_contents
, tabstrip
.GetWebContentsAt(1));
1434 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(2));
1436 // The newly created tab should be selected.
1437 EXPECT_EQ(left_click_contents
, tabstrip
.GetActiveWebContents());
1439 // After closing the selected tab, the selection should move to the left, to
1441 tabstrip
.CloseSelectedTabs();
1442 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1444 EXPECT_EQ(2, tabstrip
.count());
1446 tabstrip
.CloseAllTabs();
1447 EXPECT_TRUE(tabstrip
.empty());
1450 // Tests whether or not new tabs that should split context (typed pages,
1451 // generated urls, also blank tabs) open at the end of the tabstrip instead of
1453 TEST_F(TabStripModelTest
, AddWebContents_CreateNewBlankTab
) {
1454 TabStripDummyDelegate delegate
;
1455 TabStripModel
tabstrip(&delegate
, profile());
1456 EXPECT_TRUE(tabstrip
.empty());
1458 // Open the Home Page.
1459 WebContents
* homepage_contents
= CreateWebContents();
1460 tabstrip
.AddWebContents(
1461 homepage_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1462 TabStripModel::ADD_ACTIVE
);
1464 // Open some other tab, by user typing.
1465 WebContents
* typed_page_contents
= CreateWebContents();
1466 tabstrip
.AddWebContents(
1467 typed_page_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1468 TabStripModel::ADD_ACTIVE
);
1470 EXPECT_EQ(2, tabstrip
.count());
1472 // Re-select the home page.
1473 tabstrip
.ActivateTabAt(0, true);
1475 // Open a new blank tab in the foreground.
1476 WebContents
* new_blank_contents
= CreateWebContents();
1477 tabstrip
.AddWebContents(new_blank_contents
, -1,
1478 ui::PAGE_TRANSITION_TYPED
,
1479 TabStripModel::ADD_ACTIVE
);
1481 // Verify the state of the tabstrip.
1482 EXPECT_EQ(3, tabstrip
.count());
1483 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1484 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(1));
1485 EXPECT_EQ(new_blank_contents
, tabstrip
.GetWebContentsAt(2));
1487 // Now open a couple more blank tabs in the background.
1488 WebContents
* background_blank_contents1
= CreateWebContents();
1489 tabstrip
.AddWebContents(
1490 background_blank_contents1
, -1, ui::PAGE_TRANSITION_TYPED
,
1491 TabStripModel::ADD_NONE
);
1492 WebContents
* background_blank_contents2
= CreateWebContents();
1493 tabstrip
.AddWebContents(
1494 background_blank_contents2
, -1, ui::PAGE_TRANSITION_GENERATED
,
1495 TabStripModel::ADD_NONE
);
1496 EXPECT_EQ(5, tabstrip
.count());
1497 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1498 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(1));
1499 EXPECT_EQ(new_blank_contents
, tabstrip
.GetWebContentsAt(2));
1500 EXPECT_EQ(background_blank_contents1
, tabstrip
.GetWebContentsAt(3));
1501 EXPECT_EQ(background_blank_contents2
, tabstrip
.GetWebContentsAt(4));
1503 tabstrip
.CloseAllTabs();
1504 EXPECT_TRUE(tabstrip
.empty());
1507 // Tests whether opener state is correctly forgotten when the user switches
1509 TEST_F(TabStripModelTest
, AddWebContents_ForgetOpeners
) {
1510 TabStripDummyDelegate delegate
;
1511 TabStripModel
tabstrip(&delegate
, profile());
1512 EXPECT_TRUE(tabstrip
.empty());
1514 // Open the Home Page
1515 WebContents
* homepage_contents
= CreateWebContents();
1516 tabstrip
.AddWebContents(
1517 homepage_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1518 TabStripModel::ADD_ACTIVE
);
1520 // Open some other tab, by user typing.
1521 WebContents
* typed_page_contents
= CreateWebContents();
1522 tabstrip
.AddWebContents(
1523 typed_page_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1524 TabStripModel::ADD_ACTIVE
);
1526 EXPECT_EQ(2, tabstrip
.count());
1528 // Re-select the home page.
1529 tabstrip
.ActivateTabAt(0, true);
1531 // Open a bunch of tabs by simulating middle clicking on links on the home
1533 WebContents
* middle_click_contents1
= CreateWebContents();
1534 tabstrip
.AddWebContents(
1535 middle_click_contents1
, -1, ui::PAGE_TRANSITION_LINK
,
1536 TabStripModel::ADD_NONE
);
1537 WebContents
* middle_click_contents2
= CreateWebContents();
1538 tabstrip
.AddWebContents(
1539 middle_click_contents2
, -1, ui::PAGE_TRANSITION_LINK
,
1540 TabStripModel::ADD_NONE
);
1541 WebContents
* middle_click_contents3
= CreateWebContents();
1542 tabstrip
.AddWebContents(
1543 middle_click_contents3
, -1, ui::PAGE_TRANSITION_LINK
,
1544 TabStripModel::ADD_NONE
);
1546 // Break out of the context by selecting a tab in a different context.
1547 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(4));
1548 tabstrip
.SelectLastTab();
1549 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1551 // Step back into the context by selecting a tab inside it.
1552 tabstrip
.ActivateTabAt(2, true);
1553 EXPECT_EQ(middle_click_contents2
, tabstrip
.GetActiveWebContents());
1555 // Now test that closing tabs selects to the right until there are no more,
1556 // then to the left, as if there were no context (context has been
1557 // successfully forgotten).
1558 tabstrip
.CloseSelectedTabs();
1559 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetActiveWebContents());
1560 tabstrip
.CloseSelectedTabs();
1561 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1562 tabstrip
.CloseSelectedTabs();
1563 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetActiveWebContents());
1564 tabstrip
.CloseSelectedTabs();
1565 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1567 EXPECT_EQ(1, tabstrip
.count());
1569 tabstrip
.CloseAllTabs();
1570 EXPECT_TRUE(tabstrip
.empty());
1573 // Added for http://b/issue?id=958960
1574 TEST_F(TabStripModelTest
, AppendContentsReselectionTest
) {
1575 TabStripDummyDelegate delegate
;
1576 TabStripModel
tabstrip(&delegate
, profile());
1577 EXPECT_TRUE(tabstrip
.empty());
1579 // Open the Home Page.
1580 WebContents
* homepage_contents
= CreateWebContents();
1581 tabstrip
.AddWebContents(
1582 homepage_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1583 TabStripModel::ADD_ACTIVE
);
1585 // Open some other tab, by user typing.
1586 WebContents
* typed_page_contents
= CreateWebContents();
1587 tabstrip
.AddWebContents(
1588 typed_page_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1589 TabStripModel::ADD_NONE
);
1591 // The selected tab should still be the first.
1592 EXPECT_EQ(0, tabstrip
.active_index());
1594 // Now simulate a link click that opens a new tab (by virtue of target=_blank)
1595 // and make sure the correct tab gets selected when the new tab is closed.
1596 WebContents
* target_blank
= CreateWebContents();
1597 tabstrip
.AppendWebContents(target_blank
, true);
1598 EXPECT_EQ(2, tabstrip
.active_index());
1599 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1600 EXPECT_EQ(0, tabstrip
.active_index());
1602 // Clean up after ourselves.
1603 tabstrip
.CloseAllTabs();
1606 // Added for http://b/issue?id=1027661
1607 TEST_F(TabStripModelTest
, ReselectionConsidersChildrenTest
) {
1608 TabStripDummyDelegate delegate
;
1609 TabStripModel
strip(&delegate
, profile());
1612 WebContents
* page_a_contents
= CreateWebContents();
1613 strip
.AddWebContents(
1614 page_a_contents
, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1615 TabStripModel::ADD_ACTIVE
);
1617 // Simulate middle click to open page A.A and A.B
1618 WebContents
* page_a_a_contents
= CreateWebContents();
1619 strip
.AddWebContents(page_a_a_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1620 TabStripModel::ADD_NONE
);
1621 WebContents
* page_a_b_contents
= CreateWebContents();
1622 strip
.AddWebContents(page_a_b_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1623 TabStripModel::ADD_NONE
);
1626 strip
.ActivateTabAt(1, true);
1627 EXPECT_EQ(page_a_a_contents
, strip
.GetActiveWebContents());
1629 // Simulate a middle click to open page A.A.A
1630 WebContents
* page_a_a_a_contents
= CreateWebContents();
1631 strip
.AddWebContents(page_a_a_a_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1632 TabStripModel::ADD_NONE
);
1634 EXPECT_EQ(page_a_a_a_contents
, strip
.GetWebContentsAt(2));
1637 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1639 // Page A.A.A should be selected, NOT A.B
1640 EXPECT_EQ(page_a_a_a_contents
, strip
.GetActiveWebContents());
1643 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1645 // Page A.B should be selected
1646 EXPECT_EQ(page_a_b_contents
, strip
.GetActiveWebContents());
1649 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1651 // Page A should be selected
1652 EXPECT_EQ(page_a_contents
, strip
.GetActiveWebContents());
1655 strip
.CloseAllTabs();
1658 TEST_F(TabStripModelTest
, AddWebContents_NewTabAtEndOfStripInheritsGroup
) {
1659 TabStripDummyDelegate delegate
;
1660 TabStripModel
strip(&delegate
, profile());
1663 WebContents
* page_a_contents
= CreateWebContents();
1664 strip
.AddWebContents(page_a_contents
, -1,
1665 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1666 TabStripModel::ADD_ACTIVE
);
1668 // Open pages B, C and D in the background from links on page A...
1669 WebContents
* page_b_contents
= CreateWebContents();
1670 WebContents
* page_c_contents
= CreateWebContents();
1671 WebContents
* page_d_contents
= CreateWebContents();
1672 strip
.AddWebContents(page_b_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1673 TabStripModel::ADD_NONE
);
1674 strip
.AddWebContents(page_c_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1675 TabStripModel::ADD_NONE
);
1676 strip
.AddWebContents(page_d_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1677 TabStripModel::ADD_NONE
);
1679 // Switch to page B's tab.
1680 strip
.ActivateTabAt(1, true);
1682 // Open a New Tab at the end of the strip (simulate Ctrl+T)
1683 WebContents
* new_contents
= CreateWebContents();
1684 strip
.AddWebContents(new_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1685 TabStripModel::ADD_ACTIVE
);
1687 EXPECT_EQ(4, strip
.GetIndexOfWebContents(new_contents
));
1688 EXPECT_EQ(4, strip
.active_index());
1690 // Close the New Tab that was just opened. We should be returned to page B's
1692 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1694 EXPECT_EQ(1, strip
.active_index());
1696 // Open a non-New Tab tab at the end of the strip, with a TYPED transition.
1697 // This is like typing a URL in the address bar and pressing Alt+Enter. The
1698 // behavior should be the same as above.
1699 WebContents
* page_e_contents
= CreateWebContents();
1700 strip
.AddWebContents(page_e_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
1701 TabStripModel::ADD_ACTIVE
);
1703 EXPECT_EQ(4, strip
.GetIndexOfWebContents(page_e_contents
));
1704 EXPECT_EQ(4, strip
.active_index());
1706 // Close the Tab. Selection should shift back to page B's Tab.
1707 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1709 EXPECT_EQ(1, strip
.active_index());
1711 // Open a non-New Tab tab at the end of the strip, with some other
1712 // transition. This is like right clicking on a bookmark and choosing "Open
1713 // in New Tab". No opener relationship should be preserved between this Tab
1714 // and the one that was active when the gesture was performed.
1715 WebContents
* page_f_contents
= CreateWebContents();
1716 strip
.AddWebContents(page_f_contents
, -1,
1717 ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1718 TabStripModel::ADD_ACTIVE
);
1720 EXPECT_EQ(4, strip
.GetIndexOfWebContents(page_f_contents
));
1721 EXPECT_EQ(4, strip
.active_index());
1723 // Close the Tab. The next-adjacent should be selected.
1724 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1726 EXPECT_EQ(3, strip
.active_index());
1729 strip
.CloseAllTabs();
1732 // A test of navigations in a tab that is part of a group of opened from some
1733 // parent tab. If the navigations are link clicks, the group relationship of
1734 // the tab to its parent are preserved. If they are of any other type, they are
1736 TEST_F(TabStripModelTest
, NavigationForgetsOpeners
) {
1737 TabStripDummyDelegate delegate
;
1738 TabStripModel
strip(&delegate
, profile());
1741 WebContents
* page_a_contents
= CreateWebContents();
1742 strip
.AddWebContents(page_a_contents
, -1,
1743 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1744 TabStripModel::ADD_ACTIVE
);
1746 // Open pages B, C and D in the background from links on page A...
1747 WebContents
* page_b_contents
= CreateWebContents();
1748 WebContents
* page_c_contents
= CreateWebContents();
1749 WebContents
* page_d_contents
= CreateWebContents();
1750 strip
.AddWebContents(page_b_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1751 TabStripModel::ADD_NONE
);
1752 strip
.AddWebContents(page_c_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1753 TabStripModel::ADD_NONE
);
1754 strip
.AddWebContents(page_d_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1755 TabStripModel::ADD_NONE
);
1757 // Open page E in a different opener group from page A.
1758 WebContents
* page_e_contents
= CreateWebContents();
1759 strip
.AddWebContents(page_e_contents
, -1,
1760 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1761 TabStripModel::ADD_NONE
);
1763 // Tell the TabStripModel that we are navigating page D via a link click.
1764 strip
.ActivateTabAt(3, true);
1765 strip
.TabNavigating(page_d_contents
, ui::PAGE_TRANSITION_LINK
);
1767 // Close page D, page C should be selected. (part of same group).
1768 strip
.CloseWebContentsAt(3, TabStripModel::CLOSE_NONE
);
1769 EXPECT_EQ(2, strip
.active_index());
1771 // Tell the TabStripModel that we are navigating in page C via a bookmark.
1772 strip
.TabNavigating(page_c_contents
, ui::PAGE_TRANSITION_AUTO_BOOKMARK
);
1774 // Close page C, page E should be selected. (C is no longer part of the
1775 // A-B-C-D group, selection moves to the right).
1776 strip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1777 EXPECT_EQ(page_e_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1779 strip
.CloseAllTabs();
1782 // A test that the forgetting behavior tested in NavigationForgetsOpeners above
1783 // doesn't cause the opener relationship for a New Tab opened at the end of the
1784 // TabStrip to be reset (Test 1 below), unless another any other tab is
1785 // selected (Test 2 below).
1786 TEST_F(TabStripModelTest
, NavigationForgettingDoesntAffectNewTab
) {
1787 TabStripDummyDelegate delegate
;
1788 TabStripModel
strip(&delegate
, profile());
1790 // Open a tab and several tabs from it, then select one of the tabs that was
1792 WebContents
* page_a_contents
= CreateWebContents();
1793 strip
.AddWebContents(page_a_contents
, -1,
1794 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1795 TabStripModel::ADD_ACTIVE
);
1797 WebContents
* page_b_contents
= CreateWebContents();
1798 WebContents
* page_c_contents
= CreateWebContents();
1799 WebContents
* page_d_contents
= CreateWebContents();
1800 strip
.AddWebContents(page_b_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1801 TabStripModel::ADD_NONE
);
1802 strip
.AddWebContents(page_c_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1803 TabStripModel::ADD_NONE
);
1804 strip
.AddWebContents(page_d_contents
, -1, ui::PAGE_TRANSITION_LINK
,
1805 TabStripModel::ADD_NONE
);
1807 strip
.ActivateTabAt(2, true);
1809 // TEST 1: If the user is in a group of tabs and opens a new tab at the end
1810 // of the strip, closing that new tab will select the tab that they were
1813 // Now simulate opening a new tab at the end of the TabStrip.
1814 WebContents
* new_contents1
= CreateWebContents();
1815 strip
.AddWebContents(new_contents1
, -1, ui::PAGE_TRANSITION_TYPED
,
1816 TabStripModel::ADD_ACTIVE
);
1818 // At this point, if we close this tab the last selected one should be
1820 strip
.CloseWebContentsAt(strip
.count() - 1, TabStripModel::CLOSE_NONE
);
1821 EXPECT_EQ(page_c_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1823 // TEST 2: If the user is in a group of tabs and opens a new tab at the end
1824 // of the strip, selecting any other tab in the strip will cause that new
1825 // tab's opener relationship to be forgotten.
1827 // Open a new tab again.
1828 WebContents
* new_contents2
= CreateWebContents();
1829 strip
.AddWebContents(new_contents2
, -1, ui::PAGE_TRANSITION_TYPED
,
1830 TabStripModel::ADD_ACTIVE
);
1832 // Now select the first tab.
1833 strip
.ActivateTabAt(0, true);
1835 // Now select the last tab.
1836 strip
.ActivateTabAt(strip
.count() - 1, true);
1838 // Now close the last tab. The next adjacent should be selected.
1839 strip
.CloseWebContentsAt(strip
.count() - 1, TabStripModel::CLOSE_NONE
);
1840 EXPECT_EQ(page_d_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1842 strip
.CloseAllTabs();
1845 // This fails on Linux when run with the rest of unit_tests (crbug.com/302156)
1846 // and fails consistently on Mac and Windows.
1847 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
1848 #define MAYBE_FastShutdown \
1849 DISABLED_FastShutdown
1851 #define MAYBE_FastShutdown \
1854 // Tests that fast shutdown is attempted appropriately.
1855 TEST_F(TabStripModelTest
, MAYBE_FastShutdown
) {
1856 TabStripDummyDelegate delegate
;
1857 TabStripModel
tabstrip(&delegate
, profile());
1858 MockTabStripModelObserver
observer(&tabstrip
);
1859 tabstrip
.AddObserver(&observer
);
1861 EXPECT_TRUE(tabstrip
.empty());
1863 // Make sure fast shutdown is attempted when tabs that share a RPH are shut
1866 WebContents
* contents1
= CreateWebContents();
1867 WebContents
* contents2
= CreateWebContentsWithSharedRPH(contents1
);
1869 SetID(contents1
, 1);
1870 SetID(contents2
, 2);
1872 tabstrip
.AppendWebContents(contents1
, true);
1873 tabstrip
.AppendWebContents(contents2
, true);
1875 // Turn on the fake unload listener so the tabs don't actually get shut
1876 // down when we call CloseAllTabs()---we need to be able to check that
1877 // fast shutdown was attempted.
1878 delegate
.set_run_unload_listener(true);
1879 tabstrip
.CloseAllTabs();
1880 // On a mock RPH this checks whether we *attempted* fast shutdown.
1881 // A real RPH would reject our attempt since there is an unload handler.
1882 EXPECT_TRUE(contents1
->GetRenderProcessHost()->FastShutdownStarted());
1883 EXPECT_EQ(2, tabstrip
.count());
1885 delegate
.set_run_unload_listener(false);
1886 tabstrip
.CloseAllTabs();
1887 EXPECT_TRUE(tabstrip
.empty());
1890 // Make sure fast shutdown is not attempted when only some tabs that share a
1891 // RPH are shut down.
1893 WebContents
* contents1
= CreateWebContents();
1894 WebContents
* contents2
= CreateWebContentsWithSharedRPH(contents1
);
1896 SetID(contents1
, 1);
1897 SetID(contents2
, 2);
1899 tabstrip
.AppendWebContents(contents1
, true);
1900 tabstrip
.AppendWebContents(contents2
, true);
1902 tabstrip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
1903 EXPECT_FALSE(contents1
->GetRenderProcessHost()->FastShutdownStarted());
1904 EXPECT_EQ(1, tabstrip
.count());
1906 tabstrip
.CloseAllTabs();
1907 EXPECT_TRUE(tabstrip
.empty());
1911 // Tests various permutations of pinning tabs.
1912 TEST_F(TabStripModelTest
, Pinning
) {
1913 TabStripDummyDelegate delegate
;
1914 TabStripModel
tabstrip(&delegate
, profile());
1915 MockTabStripModelObserver
observer(&tabstrip
);
1916 tabstrip
.AddObserver(&observer
);
1918 EXPECT_TRUE(tabstrip
.empty());
1920 typedef MockTabStripModelObserver::State State
;
1922 WebContents
* contents1
= CreateWebContentsWithID(1);
1923 WebContents
* contents2
= CreateWebContentsWithID(2);
1924 WebContents
* contents3
= CreateWebContentsWithID(3);
1926 // Note! The ordering of these tests is important, each subsequent test
1927 // builds on the state established in the previous. This is important if you
1928 // ever insert tests rather than append.
1930 // Initial state, three tabs, first selected.
1931 tabstrip
.AppendWebContents(contents1
, true);
1932 tabstrip
.AppendWebContents(contents2
, false);
1933 tabstrip
.AppendWebContents(contents3
, false);
1935 observer
.ClearStates();
1937 // Pin the first tab, this shouldn't visually reorder anything.
1939 tabstrip
.SetTabPinned(0, true);
1941 // As the order didn't change, we should get a pinned notification.
1942 ASSERT_EQ(1, observer
.GetStateCount());
1943 State
state(contents1
, 0, MockTabStripModelObserver::PINNED
);
1944 EXPECT_TRUE(observer
.StateEquals(0, state
));
1946 // And verify the state.
1947 EXPECT_EQ("1p 2 3", GetTabStripStateString(tabstrip
));
1949 observer
.ClearStates();
1952 // Unpin the first tab.
1954 tabstrip
.SetTabPinned(0, false);
1956 // As the order didn't change, we should get a pinned notification.
1957 ASSERT_EQ(1, observer
.GetStateCount());
1958 State
state(contents1
, 0, MockTabStripModelObserver::PINNED
);
1959 EXPECT_TRUE(observer
.StateEquals(0, state
));
1961 // And verify the state.
1962 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
1964 observer
.ClearStates();
1967 // Pin the 3rd tab, which should move it to the front.
1969 tabstrip
.SetTabPinned(2, true);
1971 // The pinning should have resulted in a move and a pinned notification.
1972 ASSERT_EQ(2, observer
.GetStateCount());
1973 State
state(contents3
, 0, MockTabStripModelObserver::MOVE
);
1974 state
.src_index
= 2;
1975 EXPECT_TRUE(observer
.StateEquals(0, state
));
1977 state
= State(contents3
, 0, MockTabStripModelObserver::PINNED
);
1978 EXPECT_TRUE(observer
.StateEquals(1, state
));
1980 // And verify the state.
1981 EXPECT_EQ("3p 1 2", GetTabStripStateString(tabstrip
));
1983 observer
.ClearStates();
1986 // Pin the tab "1", which shouldn't move anything.
1988 tabstrip
.SetTabPinned(1, true);
1990 // As the order didn't change, we should get a pinned notification.
1991 ASSERT_EQ(1, observer
.GetStateCount());
1992 State
state(contents1
, 1, MockTabStripModelObserver::PINNED
);
1993 EXPECT_TRUE(observer
.StateEquals(0, state
));
1995 // And verify the state.
1996 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip
));
1998 observer
.ClearStates();
2001 // Try to move tab "2" to the front, it should be ignored.
2003 tabstrip
.MoveWebContentsAt(2, 0, false);
2005 // As the order didn't change, we should get a pinned notification.
2006 ASSERT_EQ(0, observer
.GetStateCount());
2008 // And verify the state.
2009 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip
));
2011 observer
.ClearStates();
2014 // Unpin tab "3", which implicitly moves it to the end.
2016 tabstrip
.SetTabPinned(0, false);
2018 ASSERT_EQ(2, observer
.GetStateCount());
2019 State
state(contents3
, 1, MockTabStripModelObserver::MOVE
);
2020 state
.src_index
= 0;
2021 EXPECT_TRUE(observer
.StateEquals(0, state
));
2023 state
= State(contents3
, 1, MockTabStripModelObserver::PINNED
);
2024 EXPECT_TRUE(observer
.StateEquals(1, state
));
2026 // And verify the state.
2027 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip
));
2029 observer
.ClearStates();
2032 // Unpin tab "3", nothing should happen.
2034 tabstrip
.SetTabPinned(1, false);
2036 ASSERT_EQ(0, observer
.GetStateCount());
2038 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip
));
2040 observer
.ClearStates();
2045 tabstrip
.SetTabPinned(0, true);
2046 tabstrip
.SetTabPinned(1, true);
2048 EXPECT_EQ("1p 3p 2", GetTabStripStateString(tabstrip
));
2050 observer
.ClearStates();
2053 WebContents
* contents4
= CreateWebContentsWithID(4);
2055 // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end
2058 tabstrip
.InsertWebContentsAt(1, contents4
, TabStripModel::ADD_NONE
);
2060 ASSERT_EQ(1, observer
.GetStateCount());
2061 State
state(contents4
, 2, MockTabStripModelObserver::INSERT
);
2062 EXPECT_TRUE(observer
.StateEquals(0, state
));
2064 EXPECT_EQ("1p 3p 4 2", GetTabStripStateString(tabstrip
));
2067 tabstrip
.CloseAllTabs();
2070 // Makes sure the TabStripModel calls the right observer methods during a
2072 TEST_F(TabStripModelTest
, ReplaceSendsSelected
) {
2073 typedef MockTabStripModelObserver::State State
;
2075 TabStripDummyDelegate delegate
;
2076 TabStripModel
strip(&delegate
, profile());
2078 WebContents
* first_contents
= CreateWebContents();
2079 strip
.AddWebContents(first_contents
, -1, ui::PAGE_TRANSITION_TYPED
,
2080 TabStripModel::ADD_ACTIVE
);
2082 MockTabStripModelObserver
tabstrip_observer(&strip
);
2083 strip
.AddObserver(&tabstrip_observer
);
2085 WebContents
* new_contents
= CreateWebContents();
2086 delete strip
.ReplaceWebContentsAt(0, new_contents
);
2088 ASSERT_EQ(2, tabstrip_observer
.GetStateCount());
2090 // First event should be for replaced.
2091 State
state(new_contents
, 0, MockTabStripModelObserver::REPLACED
);
2092 state
.src_contents
= first_contents
;
2093 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state
));
2095 // And the second for selected.
2096 state
= State(new_contents
, 0, MockTabStripModelObserver::ACTIVATE
);
2097 state
.src_contents
= first_contents
;
2098 state
.change_reason
= TabStripModelObserver::CHANGE_REASON_REPLACED
;
2099 EXPECT_TRUE(tabstrip_observer
.StateEquals(1, state
));
2101 // Now add another tab and replace it, making sure we don't get a selected
2103 WebContents
* third_contents
= CreateWebContents();
2104 strip
.AddWebContents(third_contents
, 1, ui::PAGE_TRANSITION_TYPED
,
2105 TabStripModel::ADD_NONE
);
2107 tabstrip_observer
.ClearStates();
2110 new_contents
= CreateWebContents();
2111 delete strip
.ReplaceWebContentsAt(1, new_contents
);
2113 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2115 state
= State(new_contents
, 1, MockTabStripModelObserver::REPLACED
);
2116 state
.src_contents
= third_contents
;
2117 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state
));
2119 strip
.CloseAllTabs();
2122 // Ensures discarding tabs leaves TabStripModel in a good state.
2123 TEST_F(TabStripModelTest
, DiscardWebContentsAt
) {
2124 typedef MockTabStripModelObserver::State State
;
2126 TabStripDummyDelegate delegate
;
2127 TabStripModel
tabstrip(&delegate
, profile());
2129 // Fill it with some tabs.
2130 WebContents
* contents1
= CreateWebContents();
2131 tabstrip
.AppendWebContents(contents1
, true);
2132 WebContents
* contents2
= CreateWebContents();
2133 tabstrip
.AppendWebContents(contents2
, true);
2135 // Start watching for events after the appends to avoid observing state
2136 // transitions that aren't relevant to this test.
2137 MockTabStripModelObserver
tabstrip_observer(&tabstrip
);
2138 tabstrip
.AddObserver(&tabstrip_observer
);
2140 // Discard one of the tabs.
2141 WebContents
* null_contents1
= tabstrip
.DiscardWebContentsAt(0);
2142 ASSERT_EQ(2, tabstrip
.count());
2143 EXPECT_TRUE(tabstrip
.IsTabDiscarded(0));
2144 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2145 ASSERT_EQ(null_contents1
, tabstrip
.GetWebContentsAt(0));
2146 ASSERT_EQ(contents2
, tabstrip
.GetWebContentsAt(1));
2147 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2148 State
state1(null_contents1
, 0, MockTabStripModelObserver::REPLACED
);
2149 state1
.src_contents
= contents1
;
2150 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state1
));
2151 tabstrip_observer
.ClearStates();
2153 // Discard the same tab again.
2154 WebContents
* null_contents2
= tabstrip
.DiscardWebContentsAt(0);
2155 ASSERT_EQ(2, tabstrip
.count());
2156 EXPECT_TRUE(tabstrip
.IsTabDiscarded(0));
2157 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2158 ASSERT_EQ(null_contents2
, tabstrip
.GetWebContentsAt(0));
2159 ASSERT_EQ(contents2
, tabstrip
.GetWebContentsAt(1));
2160 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2161 State
state2(null_contents2
, 0, MockTabStripModelObserver::REPLACED
);
2162 state2
.src_contents
= null_contents1
;
2163 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state2
));
2164 tabstrip_observer
.ClearStates();
2166 // Activating the tab should clear its discard state.
2167 tabstrip
.ActivateTabAt(0, true /* user_gesture */);
2168 ASSERT_EQ(2, tabstrip
.count());
2169 EXPECT_FALSE(tabstrip
.IsTabDiscarded(0));
2170 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2172 // Don't discard active tab.
2173 tabstrip
.DiscardWebContentsAt(0);
2174 ASSERT_EQ(2, tabstrip
.count());
2175 EXPECT_FALSE(tabstrip
.IsTabDiscarded(0));
2176 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2178 tabstrip
.CloseAllTabs();
2181 // Makes sure TabStripModel handles the case of deleting a tab while removing
2183 TEST_F(TabStripModelTest
, DeleteFromDestroy
) {
2184 TabStripDummyDelegate delegate
;
2185 TabStripModel
strip(&delegate
, profile());
2186 WebContents
* contents1
= CreateWebContents();
2187 WebContents
* contents2
= CreateWebContents();
2188 MockTabStripModelObserver
tab_strip_model_observer(&strip
);
2189 strip
.AppendWebContents(contents1
, true);
2190 strip
.AppendWebContents(contents2
, true);
2191 // DeleteWebContentsOnDestroyedObserver deletes contents1 when contents2 sends
2192 // out notification that it is being destroyed.
2193 DeleteWebContentsOnDestroyedObserver
observer(contents2
, contents1
, NULL
);
2194 strip
.AddObserver(&tab_strip_model_observer
);
2195 strip
.CloseAllTabs();
2197 int close_all_count
= 0, close_all_canceled_count
= 0;
2198 tab_strip_model_observer
.GetCloseCounts(&close_all_count
,
2199 &close_all_canceled_count
);
2200 EXPECT_EQ(1, close_all_count
);
2201 EXPECT_EQ(0, close_all_canceled_count
);
2203 strip
.RemoveObserver(&tab_strip_model_observer
);
2206 // Makes sure TabStripModel handles the case of deleting another tab and the
2207 // TabStrip while removing another tab.
2208 TEST_F(TabStripModelTest
, DeleteTabStripFromDestroy
) {
2209 TabStripDummyDelegate delegate
;
2210 TabStripModel
* strip
= new TabStripModel(&delegate
, profile());
2211 MockTabStripModelObserver
tab_strip_model_observer(strip
);
2212 strip
->AddObserver(&tab_strip_model_observer
);
2213 WebContents
* contents1
= CreateWebContents();
2214 WebContents
* contents2
= CreateWebContents();
2215 strip
->AppendWebContents(contents1
, true);
2216 strip
->AppendWebContents(contents2
, true);
2217 // DeleteWebContentsOnDestroyedObserver deletes |contents1| and |strip| when
2218 // |contents2| sends out notification that it is being destroyed.
2219 DeleteWebContentsOnDestroyedObserver
observer(contents2
, contents1
, strip
);
2220 strip
->CloseAllTabs();
2221 EXPECT_TRUE(tab_strip_model_observer
.empty());
2222 EXPECT_TRUE(tab_strip_model_observer
.deleted());
2225 TEST_F(TabStripModelTest
, MoveSelectedTabsTo
) {
2227 // Number of tabs the tab strip should have.
2228 const int tab_count
;
2230 // Number of pinned tabs.
2231 const int pinned_count
;
2233 // Index of the tabs to select.
2234 const std::string selected_tabs
;
2236 // Index to move the tabs to.
2237 const int target_index
;
2239 // Expected state after the move (space separated list of indices).
2240 const std::string state_after_move
;
2243 { 2, 0, "0", 1, "1 0" },
2244 { 3, 0, "0", 2, "1 2 0" },
2245 { 3, 0, "2", 0, "2 0 1" },
2246 { 3, 0, "2", 1, "0 2 1" },
2247 { 3, 0, "0 1", 0, "0 1 2" },
2250 { 6, 0, "4 5", 1, "0 4 5 1 2 3" },
2251 { 3, 0, "0 1", 1, "2 0 1" },
2252 { 4, 0, "0 2", 1, "1 0 2 3" },
2253 { 6, 0, "0 1", 3, "2 3 4 0 1 5" },
2256 { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" },
2257 { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" },
2258 { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" },
2261 { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" },
2264 { 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" },
2266 // With pinned tabs.
2267 { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" },
2268 { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" },
2269 { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" },
2270 { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" },
2272 { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" },
2275 for (size_t i
= 0; i
< arraysize(test_data
); ++i
) {
2276 TabStripDummyDelegate delegate
;
2277 TabStripModel
strip(&delegate
, profile());
2278 ASSERT_NO_FATAL_FAILURE(
2279 PrepareTabstripForSelectionTest(&strip
, test_data
[i
].tab_count
,
2280 test_data
[i
].pinned_count
,
2281 test_data
[i
].selected_tabs
));
2282 strip
.MoveSelectedTabsTo(test_data
[i
].target_index
);
2283 EXPECT_EQ(test_data
[i
].state_after_move
,
2284 GetTabStripStateString(strip
)) << i
;
2285 strip
.CloseAllTabs();
2289 // Tests that moving a tab forgets all groups referencing it.
2290 TEST_F(TabStripModelTest
, MoveSelectedTabsTo_ForgetGroups
) {
2291 TabStripDummyDelegate delegate
;
2292 TabStripModel
strip(&delegate
, profile());
2294 // Open page A as a new tab and then A1 in the background from A.
2295 WebContents
* page_a_contents
= CreateWebContents();
2296 strip
.AddWebContents(page_a_contents
, -1,
2297 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
2298 TabStripModel::ADD_ACTIVE
);
2299 WebContents
* page_a1_contents
= CreateWebContents();
2300 strip
.AddWebContents(page_a1_contents
, -1, ui::PAGE_TRANSITION_LINK
,
2301 TabStripModel::ADD_NONE
);
2303 // Likewise, open pages B and B1.
2304 WebContents
* page_b_contents
= CreateWebContents();
2305 strip
.AddWebContents(page_b_contents
, -1,
2306 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
2307 TabStripModel::ADD_ACTIVE
);
2308 WebContents
* page_b1_contents
= CreateWebContents();
2309 strip
.AddWebContents(page_b1_contents
, -1, ui::PAGE_TRANSITION_LINK
,
2310 TabStripModel::ADD_NONE
);
2312 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(0));
2313 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(1));
2314 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(2));
2315 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(3));
2317 // Move page B to the start of the tab strip.
2318 strip
.MoveSelectedTabsTo(0);
2320 // Open page B2 in the background from B. It should end up after B.
2321 WebContents
* page_b2_contents
= CreateWebContents();
2322 strip
.AddWebContents(page_b2_contents
, -1, ui::PAGE_TRANSITION_LINK
,
2323 TabStripModel::ADD_NONE
);
2324 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(0));
2325 EXPECT_EQ(page_b2_contents
, strip
.GetWebContentsAt(1));
2326 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(2));
2327 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(3));
2328 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(4));
2331 strip
.ActivateTabAt(2, true);
2332 EXPECT_EQ(page_a_contents
, strip
.GetActiveWebContents());
2334 // Open page A2 in the background from A. It should end up after A1.
2335 WebContents
* page_a2_contents
= CreateWebContents();
2336 strip
.AddWebContents(page_a2_contents
, -1, ui::PAGE_TRANSITION_LINK
,
2337 TabStripModel::ADD_NONE
);
2338 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(0));
2339 EXPECT_EQ(page_b2_contents
, strip
.GetWebContentsAt(1));
2340 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(2));
2341 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(3));
2342 EXPECT_EQ(page_a2_contents
, strip
.GetWebContentsAt(4));
2343 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(5));
2345 strip
.CloseAllTabs();
2348 TEST_F(TabStripModelTest
, CloseSelectedTabs
) {
2349 TabStripDummyDelegate delegate
;
2350 TabStripModel
strip(&delegate
, profile());
2351 WebContents
* contents1
= CreateWebContents();
2352 WebContents
* contents2
= CreateWebContents();
2353 WebContents
* contents3
= CreateWebContents();
2354 strip
.AppendWebContents(contents1
, true);
2355 strip
.AppendWebContents(contents2
, true);
2356 strip
.AppendWebContents(contents3
, true);
2357 strip
.ToggleSelectionAt(1);
2358 strip
.CloseSelectedTabs();
2359 EXPECT_EQ(1, strip
.count());
2360 EXPECT_EQ(0, strip
.active_index());
2361 strip
.CloseAllTabs();
2364 TEST_F(TabStripModelTest
, MultipleSelection
) {
2365 typedef MockTabStripModelObserver::State State
;
2367 TabStripDummyDelegate delegate
;
2368 TabStripModel
strip(&delegate
, profile());
2369 MockTabStripModelObserver
observer(&strip
);
2370 WebContents
* contents0
= CreateWebContents();
2371 WebContents
* contents1
= CreateWebContents();
2372 WebContents
* contents2
= CreateWebContents();
2373 WebContents
* contents3
= CreateWebContents();
2374 strip
.AppendWebContents(contents0
, false);
2375 strip
.AppendWebContents(contents1
, false);
2376 strip
.AppendWebContents(contents2
, false);
2377 strip
.AppendWebContents(contents3
, false);
2378 strip
.AddObserver(&observer
);
2380 // Selection and active tab change.
2381 strip
.ActivateTabAt(3, true);
2382 ASSERT_EQ(2, observer
.GetStateCount());
2383 ASSERT_EQ(observer
.GetStateAt(0).action
,
2384 MockTabStripModelObserver::ACTIVATE
);
2385 State
s1(contents3
, 3, MockTabStripModelObserver::SELECT
);
2386 EXPECT_TRUE(observer
.StateEquals(1, s1
));
2387 observer
.ClearStates();
2389 // Adding all tabs to selection, active tab is now at 0.
2390 strip
.ExtendSelectionTo(0);
2391 ASSERT_EQ(3, observer
.GetStateCount());
2392 ASSERT_EQ(observer
.GetStateAt(0).action
,
2393 MockTabStripModelObserver::DEACTIVATE
);
2394 ASSERT_EQ(observer
.GetStateAt(1).action
,
2395 MockTabStripModelObserver::ACTIVATE
);
2396 State
s2(contents0
, 0, MockTabStripModelObserver::SELECT
);
2397 s2
.src_contents
= contents3
;
2399 EXPECT_TRUE(observer
.StateEquals(2, s2
));
2400 observer
.ClearStates();
2402 // Toggle the active tab, should make the next index active.
2403 strip
.ToggleSelectionAt(0);
2404 EXPECT_EQ(1, strip
.active_index());
2405 EXPECT_EQ(3U, strip
.selection_model().size());
2406 EXPECT_EQ(4, strip
.count());
2407 ASSERT_EQ(3, observer
.GetStateCount());
2408 ASSERT_EQ(observer
.GetStateAt(0).action
,
2409 MockTabStripModelObserver::DEACTIVATE
);
2410 ASSERT_EQ(observer
.GetStateAt(1).action
,
2411 MockTabStripModelObserver::ACTIVATE
);
2412 ASSERT_EQ(observer
.GetStateAt(2).action
,
2413 MockTabStripModelObserver::SELECT
);
2414 observer
.ClearStates();
2416 // Toggle the first tab back to selected and active.
2417 strip
.ToggleSelectionAt(0);
2418 EXPECT_EQ(0, strip
.active_index());
2419 EXPECT_EQ(4U, strip
.selection_model().size());
2420 EXPECT_EQ(4, strip
.count());
2421 ASSERT_EQ(3, observer
.GetStateCount());
2422 ASSERT_EQ(observer
.GetStateAt(0).action
,
2423 MockTabStripModelObserver::DEACTIVATE
);
2424 ASSERT_EQ(observer
.GetStateAt(1).action
,
2425 MockTabStripModelObserver::ACTIVATE
);
2426 ASSERT_EQ(observer
.GetStateAt(2).action
,
2427 MockTabStripModelObserver::SELECT
);
2428 observer
.ClearStates();
2430 // Closing one of the selected tabs, not the active one.
2431 strip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
2432 EXPECT_EQ(3, strip
.count());
2433 ASSERT_EQ(3, observer
.GetStateCount());
2434 ASSERT_EQ(observer
.GetStateAt(0).action
,
2435 MockTabStripModelObserver::CLOSE
);
2436 ASSERT_EQ(observer
.GetStateAt(1).action
,
2437 MockTabStripModelObserver::DETACH
);
2438 ASSERT_EQ(observer
.GetStateAt(2).action
,
2439 MockTabStripModelObserver::SELECT
);
2440 observer
.ClearStates();
2442 // Closing the active tab, while there are others tabs selected.
2443 strip
.CloseWebContentsAt(0, TabStripModel::CLOSE_NONE
);
2444 EXPECT_EQ(2, strip
.count());
2445 ASSERT_EQ(5, observer
.GetStateCount());
2446 ASSERT_EQ(observer
.GetStateAt(0).action
,
2447 MockTabStripModelObserver::CLOSE
);
2448 ASSERT_EQ(observer
.GetStateAt(1).action
,
2449 MockTabStripModelObserver::DETACH
);
2450 ASSERT_EQ(observer
.GetStateAt(2).action
,
2451 MockTabStripModelObserver::DEACTIVATE
);
2452 ASSERT_EQ(observer
.GetStateAt(3).action
,
2453 MockTabStripModelObserver::ACTIVATE
);
2454 ASSERT_EQ(observer
.GetStateAt(4).action
,
2455 MockTabStripModelObserver::SELECT
);
2456 observer
.ClearStates();
2458 // Active tab is at 0, deselecting all but the active tab.
2459 strip
.ToggleSelectionAt(1);
2460 ASSERT_EQ(1, observer
.GetStateCount());
2461 ASSERT_EQ(observer
.GetStateAt(0).action
,
2462 MockTabStripModelObserver::SELECT
);
2463 observer
.ClearStates();
2465 // Attempting to deselect the only selected and therefore active tab,
2466 // it is ignored (no notifications being sent) and tab at 0 remains selected
2468 strip
.ToggleSelectionAt(0);
2469 ASSERT_EQ(0, observer
.GetStateCount());
2471 strip
.RemoveObserver(&observer
);
2472 strip
.CloseAllTabs();
2475 // Verifies that if we change the selection from a multi selection to a single
2476 // selection, but not in a way that changes the selected_index that
2477 // TabSelectionChanged is invoked.
2478 TEST_F(TabStripModelTest
, MultipleToSingle
) {
2479 typedef MockTabStripModelObserver::State State
;
2481 TabStripDummyDelegate delegate
;
2482 TabStripModel
strip(&delegate
, profile());
2483 WebContents
* contents1
= CreateWebContents();
2484 WebContents
* contents2
= CreateWebContents();
2485 strip
.AppendWebContents(contents1
, false);
2486 strip
.AppendWebContents(contents2
, false);
2487 strip
.ToggleSelectionAt(0);
2488 strip
.ToggleSelectionAt(1);
2490 MockTabStripModelObserver
observer(&strip
);
2491 strip
.AddObserver(&observer
);
2492 // This changes the selection (0 is no longer selected) but the selected_index
2493 // still remains at 1.
2494 strip
.ActivateTabAt(1, true);
2495 ASSERT_EQ(1, observer
.GetStateCount());
2496 State
s(contents2
, 1, MockTabStripModelObserver::SELECT
);
2497 s
.src_contents
= contents2
;
2499 s
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
2500 EXPECT_TRUE(observer
.StateEquals(0, s
));
2501 strip
.RemoveObserver(&observer
);
2502 strip
.CloseAllTabs();
2505 // Verifies a newly inserted tab retains its previous blocked state.
2506 // http://crbug.com/276334
2507 TEST_F(TabStripModelTest
, TabBlockedState
) {
2508 // Start with a source tab strip.
2509 TabStripDummyDelegate dummy_tab_strip_delegate
;
2510 TabStripModel
strip_src(&dummy_tab_strip_delegate
, profile());
2511 TabBlockedStateTestBrowser
browser_src(&strip_src
);
2514 WebContents
* contents1
= CreateWebContents();
2515 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1
);
2516 strip_src
.AppendWebContents(contents1
, false);
2519 WebContents
* contents2
= CreateWebContents();
2520 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents2
);
2521 strip_src
.AppendWebContents(contents2
, false);
2523 // Create a destination tab strip.
2524 TabStripModel
strip_dst(&dummy_tab_strip_delegate
, profile());
2525 TabBlockedStateTestBrowser
browser_dst(&strip_dst
);
2527 // Setup a SingleWebContentsDialogManager for tab |contents2|.
2528 web_modal::WebContentsModalDialogManager
* modal_dialog_manager
=
2529 web_modal::WebContentsModalDialogManager::FromWebContents(contents2
);
2530 web_modal::PopupManager
popup_manager(NULL
);
2531 popup_manager
.RegisterWith(contents2
);
2533 // Show a dialog that blocks tab |contents2|.
2534 // DummySingleWebContentsDialogManager doesn't care about the
2535 // dialog window value, so any dummy value works.
2536 DummySingleWebContentsDialogManager
* native_manager
=
2537 new DummySingleWebContentsDialogManager(
2538 reinterpret_cast<gfx::NativeWindow
>(0), modal_dialog_manager
);
2539 modal_dialog_manager
->ShowDialogWithManager(
2540 reinterpret_cast<gfx::NativeWindow
>(0),
2541 scoped_ptr
<web_modal::SingleWebContentsDialogManager
>(
2542 native_manager
).Pass());
2543 EXPECT_TRUE(strip_src
.IsTabBlocked(1));
2546 WebContents
* moved_contents
= strip_src
.DetachWebContentsAt(1);
2547 EXPECT_EQ(contents2
, moved_contents
);
2549 // Attach the tab to the destination tab strip.
2550 strip_dst
.AppendWebContents(moved_contents
, true);
2551 EXPECT_TRUE(strip_dst
.IsTabBlocked(0));
2553 strip_dst
.CloseAllTabs();
2554 strip_src
.CloseAllTabs();
2557 // Verifies ordering of tabs opened via a link from a pinned tab with a
2558 // subsequent pinned tab.
2559 TEST_F(TabStripModelTest
, LinkClicksWithPinnedTabOrdering
) {
2560 TabStripDummyDelegate delegate
;
2561 TabStripModel
strip(&delegate
, profile());
2563 // Open two pages, pinned.
2564 WebContents
* page_a_contents
= CreateWebContents();
2565 strip
.AddWebContents(page_a_contents
, -1,
2566 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
2567 TabStripModel::ADD_ACTIVE
| TabStripModel::ADD_PINNED
);
2568 WebContents
* page_b_contents
= CreateWebContents();
2569 strip
.AddWebContents(page_b_contents
, -1,
2570 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
2571 TabStripModel::ADD_ACTIVE
| TabStripModel::ADD_PINNED
);
2573 // Activate the first tab (a).
2574 strip
.ActivateTabAt(0, true);
2576 // Open two more tabs as link clicks. The first tab, c, should appear after
2577 // the pinned tabs followed by the second tab (d).
2578 WebContents
* page_c_contents
= CreateWebContents();
2579 WebContents
* page_d_contents
= CreateWebContents();
2580 strip
.AddWebContents(page_c_contents
, -1, ui::PAGE_TRANSITION_LINK
,
2581 TabStripModel::ADD_NONE
);
2582 strip
.AddWebContents(page_d_contents
, -1, ui::PAGE_TRANSITION_LINK
,
2583 TabStripModel::ADD_NONE
);
2585 EXPECT_EQ(2, strip
.GetIndexOfWebContents(page_c_contents
));
2586 EXPECT_EQ(3, strip
.GetIndexOfWebContents(page_d_contents
));
2587 strip
.CloseAllTabs();