1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/tabs/tab_strip_model.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/path_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/defaults.h"
19 #include "chrome/browser/extensions/tab_helper.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_tabstrip.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h"
25 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
26 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
27 #include "chrome/common/url_constants.h"
28 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
29 #include "chrome/test/base/testing_profile.h"
30 #include "components/web_modal/web_contents_modal_dialog_manager.h"
31 #include "content/public/browser/navigation_controller.h"
32 #include "content/public/browser/navigation_entry.h"
33 #include "content/public/browser/render_process_host.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/browser/web_contents_observer.h"
36 #include "extensions/common/extension.h"
37 #include "testing/gtest/include/gtest/gtest.h"
39 using content::SiteInstance
;
40 using content::WebContents
;
41 using extensions::Extension
;
42 using web_modal::NativeWebContentsModalDialog
;
46 // Class used to delete a WebContents and TabStripModel when another WebContents
48 class DeleteWebContentsOnDestroyedObserver
49 : public content::WebContentsObserver
{
51 // When |source| is deleted both |tab_to_delete| and |tab_strip| are deleted.
52 // |tab_to_delete| and |tab_strip| may be NULL.
53 DeleteWebContentsOnDestroyedObserver(WebContents
* source
,
54 WebContents
* tab_to_delete
,
55 TabStripModel
* tab_strip
)
56 : WebContentsObserver(source
),
57 tab_to_delete_(tab_to_delete
),
58 tab_strip_(tab_strip
) {
61 virtual void WebContentsDestroyed(WebContents
* web_contents
) OVERRIDE
{
62 WebContents
* tab_to_delete
= tab_to_delete_
;
63 tab_to_delete_
= NULL
;
64 TabStripModel
* tab_strip_to_delete
= tab_strip_
;
67 delete tab_strip_to_delete
;
71 WebContents
* tab_to_delete_
;
72 TabStripModel
* tab_strip_
;
74 DISALLOW_COPY_AND_ASSIGN(DeleteWebContentsOnDestroyedObserver
);
77 class TabStripDummyDelegate
: public TestTabStripModelDelegate
{
79 TabStripDummyDelegate() : run_unload_(false) {}
80 virtual ~TabStripDummyDelegate() {}
82 void set_run_unload_listener(bool value
) { run_unload_
= value
; }
84 virtual bool RunUnloadListenerBeforeClosing(WebContents
* contents
) OVERRIDE
{
89 // Whether to report that we need to run an unload listener before closing.
92 DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate
);
95 const char kTabStripModelTestIDUserDataKey
[] = "TabStripModelTestIDUserData";
97 class TabStripModelTestIDUserData
: public base::SupportsUserData::Data
{
99 explicit TabStripModelTestIDUserData(int id
) : id_(id
) {}
100 virtual ~TabStripModelTestIDUserData() {}
101 int id() { return id_
; }
107 class DummyNativeWebContentsModalDialogManager
108 : public web_modal::NativeWebContentsModalDialogManager
{
110 explicit DummyNativeWebContentsModalDialogManager(
111 web_modal::NativeWebContentsModalDialogManagerDelegate
* delegate
)
112 : delegate_(delegate
) {}
113 virtual ~DummyNativeWebContentsModalDialogManager() {}
115 virtual void ManageDialog(NativeWebContentsModalDialog dialog
) OVERRIDE
{}
116 virtual void ShowDialog(NativeWebContentsModalDialog dialog
) OVERRIDE
{}
117 virtual void HideDialog(NativeWebContentsModalDialog dialog
) OVERRIDE
{}
118 virtual void CloseDialog(NativeWebContentsModalDialog dialog
) OVERRIDE
{
119 delegate_
->WillClose(dialog
);
121 virtual void FocusDialog(NativeWebContentsModalDialog dialog
) OVERRIDE
{}
122 virtual void PulseDialog(NativeWebContentsModalDialog dialog
) OVERRIDE
{}
123 virtual void HostChanged(
124 web_modal::WebContentsModalDialogHost
* new_host
) OVERRIDE
{}
127 web_modal::NativeWebContentsModalDialogManagerDelegate
* delegate_
;
129 DISALLOW_COPY_AND_ASSIGN(DummyNativeWebContentsModalDialogManager
);
132 // Test Browser-like class for TabStripModelTest.TabBlockedState.
133 class TabBlockedStateTestBrowser
134 : public TabStripModelObserver
,
135 public web_modal::WebContentsModalDialogManagerDelegate
{
137 explicit TabBlockedStateTestBrowser(TabStripModel
* tab_strip_model
)
138 : tab_strip_model_(tab_strip_model
) {
139 tab_strip_model_
->AddObserver(this);
142 virtual ~TabBlockedStateTestBrowser() {
143 tab_strip_model_
->RemoveObserver(this);
147 // TabStripModelObserver
148 virtual void TabInsertedAt(WebContents
* contents
,
150 bool foreground
) OVERRIDE
{
151 web_modal::WebContentsModalDialogManager
* manager
=
152 web_modal::WebContentsModalDialogManager::FromWebContents(contents
);
154 manager
->SetDelegate(this);
157 // WebContentsModalDialogManagerDelegate
158 virtual void SetWebContentsBlocked(content::WebContents
* contents
,
159 bool blocked
) OVERRIDE
{
160 int index
= tab_strip_model_
->GetIndexOfWebContents(contents
);
162 tab_strip_model_
->SetTabBlocked(index
, blocked
);
165 TabStripModel
* tab_strip_model_
;
167 DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser
);
172 class TabStripModelTest
: public ChromeRenderViewHostTestHarness
{
174 WebContents
* CreateWebContents() {
175 return WebContents::Create(WebContents::CreateParams(profile()));
178 WebContents
* CreateWebContentsWithSharedRPH(WebContents
* web_contents
) {
179 WebContents::CreateParams
create_params(
180 profile(), web_contents
->GetRenderViewHost()->GetSiteInstance());
181 WebContents
* retval
= WebContents::Create(create_params
);
182 EXPECT_EQ(retval
->GetRenderProcessHost(),
183 web_contents
->GetRenderProcessHost());
187 // Sets the id of the specified contents.
188 void SetID(WebContents
* contents
, int id
) {
189 contents
->SetUserData(&kTabStripModelTestIDUserDataKey
,
190 new TabStripModelTestIDUserData(id
));
193 // Returns the id of the specified contents.
194 int GetID(WebContents
* contents
) {
195 TabStripModelTestIDUserData
* user_data
=
196 static_cast<TabStripModelTestIDUserData
*>(
197 contents
->GetUserData(&kTabStripModelTestIDUserDataKey
));
199 return user_data
? user_data
->id() : -1;
202 // Returns the state of the given tab strip as a string. The state consists
203 // of the ID of each web contents followed by a 'p' if pinned. For example,
204 // if the model consists of two tabs with ids 2 and 1, with the first
205 // tab pinned, this returns "2p 1".
206 std::string
GetTabStripStateString(const TabStripModel
& model
) {
208 for (int i
= 0; i
< model
.count(); ++i
) {
212 actual
+= base::IntToString(GetID(model
.GetWebContentsAt(i
)));
214 if (model
.IsAppTab(i
))
217 if (model
.IsTabPinned(i
))
223 std::string
GetIndicesClosedByCommandAsString(
224 const TabStripModel
& model
,
226 TabStripModel::ContextMenuCommand id
) const {
227 std::vector
<int> indices
= model
.GetIndicesClosedByCommand(index
, id
);
229 for (size_t i
= 0; i
< indices
.size(); ++i
) {
232 result
+= base::IntToString(indices
[i
]);
237 void PrepareTabstripForSelectionTest(TabStripModel
* model
,
240 const std::string
& selected_tabs
) {
241 for (int i
= 0; i
< tab_count
; ++i
) {
242 WebContents
* contents
= CreateWebContents();
244 model
->AppendWebContents(contents
, true);
246 for (int i
= 0; i
< pinned_count
; ++i
)
247 model
->SetTabPinned(i
, true);
249 ui::ListSelectionModel selection_model
;
250 std::vector
<std::string
> selection
;
251 base::SplitStringAlongWhitespace(selected_tabs
, &selection
);
252 for (size_t i
= 0; i
< selection
.size(); ++i
) {
254 ASSERT_TRUE(base::StringToInt(selection
[i
], &value
));
255 selection_model
.AddIndexToSelection(value
);
257 selection_model
.set_active(selection_model
.selected_indices()[0]);
258 model
->SetSelectionFromModel(selection_model
);
262 class MockTabStripModelObserver
: public TabStripModelObserver
{
264 explicit MockTabStripModelObserver(TabStripModel
* model
)
268 virtual ~MockTabStripModelObserver() {}
270 enum TabStripModelObserverAction
{
284 State(WebContents
* a_dst_contents
,
286 TabStripModelObserverAction a_action
)
287 : src_contents(NULL
),
288 dst_contents(a_dst_contents
),
290 dst_index(a_dst_index
),
291 change_reason(CHANGE_REASON_NONE
),
296 WebContents
* src_contents
;
297 WebContents
* dst_contents
;
302 TabStripModelObserverAction action
;
305 int GetStateCount() const {
306 return static_cast<int>(states_
.size());
309 State
GetStateAt(int index
) const {
310 DCHECK(index
>= 0 && index
< GetStateCount());
311 return states_
[index
];
314 bool StateEquals(int index
, const State
& state
) {
315 State s
= GetStateAt(index
);
316 return (s
.src_contents
== state
.src_contents
&&
317 s
.dst_contents
== state
.dst_contents
&&
318 s
.src_index
== state
.src_index
&&
319 s
.dst_index
== state
.dst_index
&&
320 s
.change_reason
== state
.change_reason
&&
321 s
.foreground
== state
.foreground
&&
322 s
.action
== state
.action
);
325 // TabStripModelObserver implementation:
326 virtual void TabInsertedAt(WebContents
* contents
,
328 bool foreground
) OVERRIDE
{
330 State
s(contents
, index
, INSERT
);
331 s
.foreground
= foreground
;
332 states_
.push_back(s
);
334 virtual void ActiveTabChanged(WebContents
* old_contents
,
335 WebContents
* new_contents
,
337 int reason
) OVERRIDE
{
338 State
s(new_contents
, index
, ACTIVATE
);
339 s
.src_contents
= old_contents
;
340 s
.change_reason
= reason
;
341 states_
.push_back(s
);
343 virtual void TabSelectionChanged(
344 TabStripModel
* tab_strip_model
,
345 const ui::ListSelectionModel
& old_model
) OVERRIDE
{
346 State
s(model()->GetActiveWebContents(), model()->active_index(), SELECT
);
347 s
.src_contents
= model()->GetWebContentsAt(old_model
.active());
348 s
.src_index
= old_model
.active();
349 states_
.push_back(s
);
351 virtual void TabMoved(WebContents
* contents
,
353 int to_index
) OVERRIDE
{
354 State
s(contents
, to_index
, MOVE
);
355 s
.src_index
= from_index
;
356 states_
.push_back(s
);
359 virtual void TabClosingAt(TabStripModel
* tab_strip_model
,
360 WebContents
* contents
,
361 int index
) OVERRIDE
{
362 states_
.push_back(State(contents
, index
, CLOSE
));
364 virtual void TabDetachedAt(WebContents
* contents
, int index
) OVERRIDE
{
365 states_
.push_back(State(contents
, index
, DETACH
));
367 virtual void TabDeactivated(WebContents
* contents
) OVERRIDE
{
368 states_
.push_back(State(contents
, model()->active_index(), DEACTIVATE
));
370 virtual void TabChangedAt(WebContents
* contents
,
372 TabChangeType change_type
) OVERRIDE
{
373 states_
.push_back(State(contents
, index
, CHANGE
));
375 virtual void TabReplacedAt(TabStripModel
* tab_strip_model
,
376 WebContents
* old_contents
,
377 WebContents
* new_contents
,
378 int index
) OVERRIDE
{
379 State
s(new_contents
, index
, REPLACED
);
380 s
.src_contents
= old_contents
;
381 states_
.push_back(s
);
383 virtual void TabPinnedStateChanged(WebContents
* contents
,
384 int index
) OVERRIDE
{
385 states_
.push_back(State(contents
, index
, PINNED
));
387 virtual void TabStripEmpty() OVERRIDE
{
390 virtual void TabStripModelDeleted() OVERRIDE
{
398 bool empty() const { return empty_
; }
399 bool deleted() const { return deleted_
; }
400 TabStripModel
* model() { return model_
; }
403 std::vector
<State
> states_
;
407 TabStripModel
* model_
;
409 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver
);
412 TEST_F(TabStripModelTest
, TestBasicAPI
) {
413 TabStripDummyDelegate delegate
;
414 TabStripModel
tabstrip(&delegate
, profile());
415 MockTabStripModelObserver
observer(&tabstrip
);
416 tabstrip
.AddObserver(&observer
);
418 EXPECT_TRUE(tabstrip
.empty());
420 typedef MockTabStripModelObserver::State State
;
422 WebContents
* contents1
= CreateWebContents();
425 // Note! The ordering of these tests is important, each subsequent test
426 // builds on the state established in the previous. This is important if you
427 // ever insert tests rather than append.
429 // Test AppendWebContents, ContainsIndex
431 EXPECT_FALSE(tabstrip
.ContainsIndex(0));
432 tabstrip
.AppendWebContents(contents1
, true);
433 EXPECT_TRUE(tabstrip
.ContainsIndex(0));
434 EXPECT_EQ(1, tabstrip
.count());
435 EXPECT_EQ(3, observer
.GetStateCount());
436 State
s1(contents1
, 0, MockTabStripModelObserver::INSERT
);
437 s1
.foreground
= true;
438 EXPECT_TRUE(observer
.StateEquals(0, s1
));
439 State
s2(contents1
, 0, MockTabStripModelObserver::ACTIVATE
);
440 EXPECT_TRUE(observer
.StateEquals(1, s2
));
441 State
s3(contents1
, 0, MockTabStripModelObserver::SELECT
);
442 s3
.src_contents
= NULL
;
443 s3
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
444 EXPECT_TRUE(observer
.StateEquals(2, s3
));
445 observer
.ClearStates();
447 EXPECT_EQ("1", GetTabStripStateString(tabstrip
));
449 // Test InsertWebContentsAt, foreground tab.
450 WebContents
* contents2
= CreateWebContents();
453 tabstrip
.InsertWebContentsAt(1, contents2
, TabStripModel::ADD_ACTIVE
);
455 EXPECT_EQ(2, tabstrip
.count());
456 EXPECT_EQ(4, observer
.GetStateCount());
457 State
s1(contents2
, 1, MockTabStripModelObserver::INSERT
);
458 s1
.foreground
= true;
459 EXPECT_TRUE(observer
.StateEquals(0, s1
));
460 State
s2(contents1
, 0, MockTabStripModelObserver::DEACTIVATE
);
461 EXPECT_TRUE(observer
.StateEquals(1, s2
));
462 State
s3(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
463 s3
.src_contents
= contents1
;
464 EXPECT_TRUE(observer
.StateEquals(2, s3
));
465 State
s4(contents2
, 1, MockTabStripModelObserver::SELECT
);
466 s4
.src_contents
= contents1
;
468 EXPECT_TRUE(observer
.StateEquals(3, s4
));
469 observer
.ClearStates();
471 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip
));
473 // Test InsertWebContentsAt, background tab.
474 WebContents
* contents3
= CreateWebContents();
477 tabstrip
.InsertWebContentsAt(2, contents3
, TabStripModel::ADD_NONE
);
479 EXPECT_EQ(3, tabstrip
.count());
480 EXPECT_EQ(1, observer
.GetStateCount());
481 State
s1(contents3
, 2, MockTabStripModelObserver::INSERT
);
482 s1
.foreground
= false;
483 EXPECT_TRUE(observer
.StateEquals(0, s1
));
484 observer
.ClearStates();
486 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
488 // Test ActivateTabAt
490 tabstrip
.ActivateTabAt(2, true);
491 EXPECT_EQ(3, observer
.GetStateCount());
492 State
s1(contents2
, 1, MockTabStripModelObserver::DEACTIVATE
);
493 EXPECT_TRUE(observer
.StateEquals(0, s1
));
494 State
s2(contents3
, 2, MockTabStripModelObserver::ACTIVATE
);
495 s2
.src_contents
= contents2
;
496 s2
.change_reason
= TabStripModelObserver::CHANGE_REASON_USER_GESTURE
;
497 EXPECT_TRUE(observer
.StateEquals(1, s2
));
498 State
s3(contents3
, 2, MockTabStripModelObserver::SELECT
);
499 s3
.src_contents
= contents2
;
501 EXPECT_TRUE(observer
.StateEquals(2, s3
));
502 observer
.ClearStates();
504 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
506 // Test DetachWebContentsAt
509 WebContents
* detached
= tabstrip
.DetachWebContentsAt(2);
510 // ... and append again because we want this for later.
511 tabstrip
.AppendWebContents(detached
, true);
512 EXPECT_EQ(8, observer
.GetStateCount());
513 State
s1(detached
, 2, MockTabStripModelObserver::DETACH
);
514 EXPECT_TRUE(observer
.StateEquals(0, s1
));
515 State
s2(detached
, ui::ListSelectionModel::kUnselectedIndex
,
516 MockTabStripModelObserver::DEACTIVATE
);
517 EXPECT_TRUE(observer
.StateEquals(1, s2
));
518 State
s3(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
519 s3
.src_contents
= contents3
;
520 s3
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
521 EXPECT_TRUE(observer
.StateEquals(2, s3
));
522 State
s4(contents2
, 1, MockTabStripModelObserver::SELECT
);
523 s4
.src_contents
= NULL
;
524 s4
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
525 EXPECT_TRUE(observer
.StateEquals(3, s4
));
526 State
s5(detached
, 2, MockTabStripModelObserver::INSERT
);
527 s5
.foreground
= true;
528 EXPECT_TRUE(observer
.StateEquals(4, s5
));
529 State
s6(contents2
, 1, MockTabStripModelObserver::DEACTIVATE
);
530 EXPECT_TRUE(observer
.StateEquals(5, s6
));
531 State
s7(detached
, 2, MockTabStripModelObserver::ACTIVATE
);
532 s7
.src_contents
= contents2
;
533 s7
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
534 EXPECT_TRUE(observer
.StateEquals(6, s7
));
535 State
s8(detached
, 2, MockTabStripModelObserver::SELECT
);
536 s8
.src_contents
= contents2
;
538 EXPECT_TRUE(observer
.StateEquals(7, s8
));
539 observer
.ClearStates();
541 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
543 // Test CloseWebContentsAt
545 EXPECT_TRUE(tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
));
546 EXPECT_EQ(2, tabstrip
.count());
548 EXPECT_EQ(5, observer
.GetStateCount());
549 State
s1(contents3
, 2, MockTabStripModelObserver::CLOSE
);
550 EXPECT_TRUE(observer
.StateEquals(0, s1
));
551 State
s2(contents3
, 2, MockTabStripModelObserver::DETACH
);
552 EXPECT_TRUE(observer
.StateEquals(1, s2
));
553 State
s3(contents3
, ui::ListSelectionModel::kUnselectedIndex
,
554 MockTabStripModelObserver::DEACTIVATE
);
555 EXPECT_TRUE(observer
.StateEquals(2, s3
));
556 State
s4(contents2
, 1, MockTabStripModelObserver::ACTIVATE
);
557 s4
.src_contents
= contents3
;
558 s4
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
559 EXPECT_TRUE(observer
.StateEquals(3, s4
));
560 State
s5(contents2
, 1, MockTabStripModelObserver::SELECT
);
561 s5
.src_contents
= NULL
;
562 s5
.src_index
= ui::ListSelectionModel::kUnselectedIndex
;
563 EXPECT_TRUE(observer
.StateEquals(4, s5
));
564 observer
.ClearStates();
566 EXPECT_EQ("1 2", GetTabStripStateString(tabstrip
));
568 // Test MoveWebContentsAt, select_after_move == true
570 tabstrip
.MoveWebContentsAt(1, 0, true);
572 EXPECT_EQ(1, observer
.GetStateCount());
573 State
s1(contents2
, 0, MockTabStripModelObserver::MOVE
);
575 EXPECT_TRUE(observer
.StateEquals(0, s1
));
576 EXPECT_EQ(0, tabstrip
.active_index());
577 observer
.ClearStates();
579 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip
));
581 // Test MoveWebContentsAt, select_after_move == false
583 tabstrip
.MoveWebContentsAt(1, 0, false);
584 EXPECT_EQ(1, observer
.GetStateCount());
585 State
s1(contents1
, 0, MockTabStripModelObserver::MOVE
);
587 EXPECT_TRUE(observer
.StateEquals(0, s1
));
588 EXPECT_EQ(1, tabstrip
.active_index());
590 tabstrip
.MoveWebContentsAt(0, 1, false);
591 observer
.ClearStates();
593 EXPECT_EQ("2 1", GetTabStripStateString(tabstrip
));
597 EXPECT_EQ(contents2
, tabstrip
.GetActiveWebContents());
598 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(0));
599 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
600 EXPECT_EQ(0, tabstrip
.GetIndexOfWebContents(contents2
));
601 EXPECT_EQ(1, tabstrip
.GetIndexOfWebContents(contents1
));
604 // Test UpdateWebContentsStateAt
606 tabstrip
.UpdateWebContentsStateAt(0, TabStripModelObserver::ALL
);
607 EXPECT_EQ(1, observer
.GetStateCount());
608 State
s1(contents2
, 0, MockTabStripModelObserver::CHANGE
);
609 EXPECT_TRUE(observer
.StateEquals(0, s1
));
610 observer
.ClearStates();
613 // Test SelectNextTab, SelectPreviousTab, SelectLastTab
615 // Make sure the second of the two tabs is selected first...
616 tabstrip
.ActivateTabAt(1, true);
617 tabstrip
.SelectPreviousTab();
618 EXPECT_EQ(0, tabstrip
.active_index());
619 tabstrip
.SelectLastTab();
620 EXPECT_EQ(1, tabstrip
.active_index());
621 tabstrip
.SelectNextTab();
622 EXPECT_EQ(0, tabstrip
.active_index());
625 // Test CloseSelectedTabs
627 tabstrip
.CloseSelectedTabs();
628 // |CloseSelectedTabs| calls CloseWebContentsAt, we already tested that, now
629 // just verify that the count and selected index have changed
631 EXPECT_EQ(1, tabstrip
.count());
632 EXPECT_EQ(0, tabstrip
.active_index());
635 tabstrip
.CloseAllTabs();
636 // TabStripModel should now be empty.
637 EXPECT_TRUE(tabstrip
.empty());
639 // Opener methods are tested below...
641 tabstrip
.RemoveObserver(&observer
);
644 TEST_F(TabStripModelTest
, TestBasicOpenerAPI
) {
645 TabStripDummyDelegate delegate
;
646 TabStripModel
tabstrip(&delegate
, profile());
647 EXPECT_TRUE(tabstrip
.empty());
649 // This is a basic test of opener functionality. opener is created
650 // as the first tab in the strip and then we create 5 other tabs in the
651 // background with opener set as their opener.
653 WebContents
* opener
= CreateWebContents();
654 tabstrip
.AppendWebContents(opener
, true);
655 WebContents
* contents1
= CreateWebContents();
656 WebContents
* contents2
= CreateWebContents();
657 WebContents
* contents3
= CreateWebContents();
658 WebContents
* contents4
= CreateWebContents();
659 WebContents
* contents5
= CreateWebContents();
661 // We use |InsertWebContentsAt| here instead of |AppendWebContents| so that
662 // openership relationships are preserved.
663 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents1
,
664 TabStripModel::ADD_INHERIT_GROUP
);
665 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents2
,
666 TabStripModel::ADD_INHERIT_GROUP
);
667 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents3
,
668 TabStripModel::ADD_INHERIT_GROUP
);
669 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents4
,
670 TabStripModel::ADD_INHERIT_GROUP
);
671 tabstrip
.InsertWebContentsAt(tabstrip
.count(), contents5
,
672 TabStripModel::ADD_INHERIT_GROUP
);
674 // All the tabs should have the same opener.
675 for (int i
= 1; i
< tabstrip
.count(); ++i
)
676 EXPECT_EQ(opener
, tabstrip
.GetOpenerOfWebContentsAt(i
));
678 // If there is a next adjacent item, then the index should be of that item.
679 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 1, false));
680 // If the last tab in the group is closed, the preceding tab in the same
681 // group should be selected.
682 EXPECT_EQ(4, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 5, false));
684 // Tests the method that finds the last tab opened by the same opener in the
685 // strip (this is the insertion index for the next background tab for the
686 // specified opener).
687 EXPECT_EQ(5, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
689 // For a tab that has opened no other tabs, the return value should always be
692 tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents1
, 3, false));
694 tabstrip
.GetIndexOfLastWebContentsOpenedBy(contents1
, 3));
696 // ForgetAllOpeners should destroy all opener relationships.
697 tabstrip
.ForgetAllOpeners();
698 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 1, false));
699 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 5, false));
700 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
702 // Specify the last tab as the opener of the others.
703 for (int i
= 0; i
< tabstrip
.count() - 1; ++i
)
704 tabstrip
.SetOpenerOfWebContentsAt(i
, contents5
);
706 for (int i
= 0; i
< tabstrip
.count() - 1; ++i
)
707 EXPECT_EQ(contents5
, tabstrip
.GetOpenerOfWebContentsAt(i
));
709 // If there is a next adjacent item, then the index should be of that item.
710 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents5
, 1, false));
712 // If the last tab in the group is closed, the preceding tab in the same
713 // group should be selected.
714 EXPECT_EQ(3, tabstrip
.GetIndexOfNextWebContentsOpenedBy(contents5
, 4, false));
716 tabstrip
.CloseAllTabs();
717 EXPECT_TRUE(tabstrip
.empty());
720 static int GetInsertionIndex(TabStripModel
* tabstrip
) {
721 return tabstrip
->order_controller()->DetermineInsertionIndex(
722 content::PAGE_TRANSITION_LINK
, false);
725 static void InsertWebContentses(TabStripModel
* tabstrip
,
726 WebContents
* contents1
,
727 WebContents
* contents2
,
728 WebContents
* contents3
) {
729 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
731 TabStripModel::ADD_INHERIT_GROUP
);
732 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
734 TabStripModel::ADD_INHERIT_GROUP
);
735 tabstrip
->InsertWebContentsAt(GetInsertionIndex(tabstrip
),
737 TabStripModel::ADD_INHERIT_GROUP
);
740 // Tests opening background tabs.
741 TEST_F(TabStripModelTest
, TestLTRInsertionOptions
) {
742 TabStripDummyDelegate delegate
;
743 TabStripModel
tabstrip(&delegate
, profile());
744 EXPECT_TRUE(tabstrip
.empty());
746 WebContents
* opener
= CreateWebContents();
747 tabstrip
.AppendWebContents(opener
, true);
749 WebContents
* contents1
= CreateWebContents();
750 WebContents
* contents2
= CreateWebContents();
751 WebContents
* contents3
= CreateWebContents();
754 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
755 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
756 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(2));
757 EXPECT_EQ(contents3
, tabstrip
.GetWebContentsAt(3));
759 tabstrip
.CloseAllTabs();
760 EXPECT_TRUE(tabstrip
.empty());
763 // This test constructs a tabstrip, and then simulates loading several tabs in
764 // the background from link clicks on the first tab. Then it simulates opening
765 // a new tab from the first tab in the foreground via a link click, verifies
766 // that this tab is opened adjacent to the opener, then closes it.
767 // Finally it tests that a tab opened for some non-link purpose opens at the
768 // end of the strip, not bundled to any existing context.
769 TEST_F(TabStripModelTest
, TestInsertionIndexDetermination
) {
770 TabStripDummyDelegate delegate
;
771 TabStripModel
tabstrip(&delegate
, profile());
772 EXPECT_TRUE(tabstrip
.empty());
774 WebContents
* opener
= CreateWebContents();
775 tabstrip
.AppendWebContents(opener
, true);
777 // Open some other random unrelated tab in the background to monkey with our
779 WebContents
* other
= CreateWebContents();
780 tabstrip
.AppendWebContents(other
, false);
782 WebContents
* contents1
= CreateWebContents();
783 WebContents
* contents2
= CreateWebContents();
784 WebContents
* contents3
= CreateWebContents();
786 // Start by testing LTR.
787 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
788 EXPECT_EQ(opener
, tabstrip
.GetWebContentsAt(0));
789 EXPECT_EQ(contents1
, tabstrip
.GetWebContentsAt(1));
790 EXPECT_EQ(contents2
, tabstrip
.GetWebContentsAt(2));
791 EXPECT_EQ(contents3
, tabstrip
.GetWebContentsAt(3));
792 EXPECT_EQ(other
, tabstrip
.GetWebContentsAt(4));
794 // The opener API should work...
795 EXPECT_EQ(3, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 2, false));
796 EXPECT_EQ(2, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
797 EXPECT_EQ(3, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
799 // Now open a foreground tab from a link. It should be opened adjacent to the
801 WebContents
* fg_link_contents
= CreateWebContents();
802 int insert_index
= tabstrip
.order_controller()->DetermineInsertionIndex(
803 content::PAGE_TRANSITION_LINK
, true);
804 EXPECT_EQ(1, insert_index
);
805 tabstrip
.InsertWebContentsAt(insert_index
, fg_link_contents
,
806 TabStripModel::ADD_ACTIVE
|
807 TabStripModel::ADD_INHERIT_GROUP
);
808 EXPECT_EQ(1, tabstrip
.active_index());
809 EXPECT_EQ(fg_link_contents
, tabstrip
.GetActiveWebContents());
811 // Now close this contents. The selection should move to the opener contents.
812 tabstrip
.CloseSelectedTabs();
813 EXPECT_EQ(0, tabstrip
.active_index());
815 // Now open a new empty tab. It should open at the end of the strip.
816 WebContents
* fg_nonlink_contents
= CreateWebContents();
817 insert_index
= tabstrip
.order_controller()->DetermineInsertionIndex(
818 content::PAGE_TRANSITION_AUTO_BOOKMARK
, true);
819 EXPECT_EQ(tabstrip
.count(), insert_index
);
820 // We break the opener relationship...
821 tabstrip
.InsertWebContentsAt(insert_index
,
823 TabStripModel::ADD_NONE
);
824 // Now select it, so that user_gesture == true causes the opener relationship
825 // to be forgotten...
826 tabstrip
.ActivateTabAt(tabstrip
.count() - 1, true);
827 EXPECT_EQ(tabstrip
.count() - 1, tabstrip
.active_index());
828 EXPECT_EQ(fg_nonlink_contents
, tabstrip
.GetActiveWebContents());
830 // Verify that all opener relationships are forgotten.
831 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 2, false));
832 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
833 EXPECT_EQ(-1, tabstrip
.GetIndexOfNextWebContentsOpenedBy(opener
, 3, false));
834 EXPECT_EQ(-1, tabstrip
.GetIndexOfLastWebContentsOpenedBy(opener
, 1));
836 tabstrip
.CloseAllTabs();
837 EXPECT_TRUE(tabstrip
.empty());
840 // Tests that selection is shifted to the correct tab when a tab is closed.
841 // If a tab is in the background when it is closed, the selection does not
843 // If a tab is in the foreground (selected),
844 // If that tab does not have an opener, selection shifts to the right.
845 // If the tab has an opener,
846 // The next tab (scanning LTR) in the entire strip that has the same opener
848 // If there are no other tabs that have the same opener,
849 // The opener is selected
851 TEST_F(TabStripModelTest
, TestSelectOnClose
) {
852 TabStripDummyDelegate delegate
;
853 TabStripModel
tabstrip(&delegate
, profile());
854 EXPECT_TRUE(tabstrip
.empty());
856 WebContents
* opener
= CreateWebContents();
857 tabstrip
.AppendWebContents(opener
, true);
859 WebContents
* contents1
= CreateWebContents();
860 WebContents
* contents2
= CreateWebContents();
861 WebContents
* contents3
= CreateWebContents();
863 // Note that we use Detach instead of Close throughout this test to avoid
864 // having to keep reconstructing these WebContentses.
866 // First test that closing tabs that are in the background doesn't adjust the
867 // current selection.
868 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
869 EXPECT_EQ(0, tabstrip
.active_index());
871 tabstrip
.DetachWebContentsAt(1);
872 EXPECT_EQ(0, tabstrip
.active_index());
874 for (int i
= tabstrip
.count() - 1; i
>= 1; --i
)
875 tabstrip
.DetachWebContentsAt(i
);
877 // Now test that when a tab doesn't have an opener, selection shifts to the
878 // right when the tab is closed.
879 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
880 EXPECT_EQ(0, tabstrip
.active_index());
882 tabstrip
.ForgetAllOpeners();
883 tabstrip
.ActivateTabAt(1, true);
884 EXPECT_EQ(1, tabstrip
.active_index());
885 tabstrip
.DetachWebContentsAt(1);
886 EXPECT_EQ(1, tabstrip
.active_index());
887 tabstrip
.DetachWebContentsAt(1);
888 EXPECT_EQ(1, tabstrip
.active_index());
889 tabstrip
.DetachWebContentsAt(1);
890 EXPECT_EQ(0, tabstrip
.active_index());
892 for (int i
= tabstrip
.count() - 1; i
>= 1; --i
)
893 tabstrip
.DetachWebContentsAt(i
);
895 // Now test that when a tab does have an opener, it selects the next tab
896 // opened by the same opener scanning LTR when it is closed.
897 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
898 EXPECT_EQ(0, tabstrip
.active_index());
899 tabstrip
.ActivateTabAt(2, false);
900 EXPECT_EQ(2, tabstrip
.active_index());
901 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
902 EXPECT_EQ(2, tabstrip
.active_index());
903 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
904 EXPECT_EQ(1, tabstrip
.active_index());
905 tabstrip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
906 EXPECT_EQ(0, tabstrip
.active_index());
907 // Finally test that when a tab has no "siblings" that the opener is
909 WebContents
* other_contents
= CreateWebContents();
910 tabstrip
.InsertWebContentsAt(1, other_contents
,
911 TabStripModel::ADD_NONE
);
912 EXPECT_EQ(2, tabstrip
.count());
913 WebContents
* opened_contents
= CreateWebContents();
914 tabstrip
.InsertWebContentsAt(2, opened_contents
,
915 TabStripModel::ADD_ACTIVE
|
916 TabStripModel::ADD_INHERIT_GROUP
);
917 EXPECT_EQ(2, tabstrip
.active_index());
918 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
919 EXPECT_EQ(0, tabstrip
.active_index());
921 tabstrip
.CloseAllTabs();
922 EXPECT_TRUE(tabstrip
.empty());
925 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
927 TEST_F(TabStripModelTest
, CommandCloseTab
) {
928 TabStripDummyDelegate delegate
;
929 TabStripModel
tabstrip(&delegate
, profile());
930 EXPECT_TRUE(tabstrip
.empty());
932 // Make sure can_close is honored.
933 ASSERT_NO_FATAL_FAILURE(
934 PrepareTabstripForSelectionTest(&tabstrip
, 1, 0, "0"));
935 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
936 0, TabStripModel::CommandCloseTab
));
937 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
938 ASSERT_TRUE(tabstrip
.empty());
940 // Make sure close on a tab that is selected affects all the selected tabs.
941 ASSERT_NO_FATAL_FAILURE(
942 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
943 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
944 0, TabStripModel::CommandCloseTab
));
945 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
946 // Should have closed tabs 0 and 1.
947 EXPECT_EQ("2", GetTabStripStateString(tabstrip
));
949 tabstrip
.CloseAllTabs();
950 EXPECT_TRUE(tabstrip
.empty());
952 // Select two tabs and make close on a tab that isn't selected doesn't affect
954 ASSERT_NO_FATAL_FAILURE(
955 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
956 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
957 2, TabStripModel::CommandCloseTab
));
958 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab
);
959 // Should have closed tab 2.
960 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
961 tabstrip
.CloseAllTabs();
962 EXPECT_TRUE(tabstrip
.empty());
964 // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned.
965 ASSERT_NO_FATAL_FAILURE(
966 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "0 1"));
967 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
968 0, TabStripModel::CommandCloseTab
));
969 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab
);
970 // Should have closed tab 2.
971 EXPECT_EQ("2", GetTabStripStateString(tabstrip
));
972 tabstrip
.CloseAllTabs();
973 EXPECT_TRUE(tabstrip
.empty());
976 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
978 TEST_F(TabStripModelTest
, CommandCloseOtherTabs
) {
979 TabStripDummyDelegate delegate
;
980 TabStripModel
tabstrip(&delegate
, profile());
981 EXPECT_TRUE(tabstrip
.empty());
983 // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled
984 // and close two tabs.
985 ASSERT_NO_FATAL_FAILURE(
986 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
987 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
988 0, TabStripModel::CommandCloseOtherTabs
));
989 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs
);
990 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
991 tabstrip
.CloseAllTabs();
992 EXPECT_TRUE(tabstrip
.empty());
994 // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it
995 // with a non-selected index should close the two other tabs.
996 ASSERT_NO_FATAL_FAILURE(
997 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1"));
998 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
999 2, TabStripModel::CommandCloseOtherTabs
));
1000 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs
);
1001 EXPECT_EQ("0 1", GetTabStripStateString(tabstrip
));
1002 tabstrip
.CloseAllTabs();
1003 EXPECT_TRUE(tabstrip
.empty());
1005 // Select all, CommandCloseOtherTabs should not be enabled.
1006 ASSERT_NO_FATAL_FAILURE(
1007 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "0 1 2"));
1008 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1009 2, TabStripModel::CommandCloseOtherTabs
));
1010 tabstrip
.CloseAllTabs();
1011 EXPECT_TRUE(tabstrip
.empty());
1013 // Three tabs, pin one, select the two non-pinned.
1014 ASSERT_NO_FATAL_FAILURE(
1015 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "1 2"));
1016 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1017 1, TabStripModel::CommandCloseOtherTabs
));
1018 // If we don't pass in the pinned index, the command should be enabled.
1019 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1020 0, TabStripModel::CommandCloseOtherTabs
));
1021 tabstrip
.CloseAllTabs();
1022 EXPECT_TRUE(tabstrip
.empty());
1024 // 3 tabs, one pinned.
1025 ASSERT_NO_FATAL_FAILURE(
1026 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "1"));
1027 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1028 1, TabStripModel::CommandCloseOtherTabs
));
1029 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1030 0, TabStripModel::CommandCloseOtherTabs
));
1031 tabstrip
.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs
);
1032 // The pinned tab shouldn't be closed.
1033 EXPECT_EQ("0p 1", GetTabStripStateString(tabstrip
));
1034 tabstrip
.CloseAllTabs();
1035 EXPECT_TRUE(tabstrip
.empty());
1038 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1039 // CommandCloseTabsToRight.
1040 TEST_F(TabStripModelTest
, CommandCloseTabsToRight
) {
1041 TabStripDummyDelegate delegate
;
1042 TabStripModel
tabstrip(&delegate
, profile());
1043 EXPECT_TRUE(tabstrip
.empty());
1045 // Create three tabs, select last two tabs, CommandCloseTabsToRight should
1046 // only be enabled for the first tab.
1047 ASSERT_NO_FATAL_FAILURE(
1048 PrepareTabstripForSelectionTest(&tabstrip
, 3, 0, "1 2"));
1049 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1050 0, TabStripModel::CommandCloseTabsToRight
));
1051 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1052 1, TabStripModel::CommandCloseTabsToRight
));
1053 EXPECT_FALSE(tabstrip
.IsContextMenuCommandEnabled(
1054 2, TabStripModel::CommandCloseTabsToRight
));
1055 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight
);
1056 EXPECT_EQ("0", GetTabStripStateString(tabstrip
));
1057 tabstrip
.CloseAllTabs();
1058 EXPECT_TRUE(tabstrip
.empty());
1061 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1062 // CommandTogglePinned.
1063 TEST_F(TabStripModelTest
, CommandTogglePinned
) {
1064 TabStripDummyDelegate delegate
;
1065 TabStripModel
tabstrip(&delegate
, profile());
1066 EXPECT_TRUE(tabstrip
.empty());
1068 // Create three tabs with one pinned, pin the first two.
1069 ASSERT_NO_FATAL_FAILURE(
1070 PrepareTabstripForSelectionTest(&tabstrip
, 3, 1, "0 1"));
1071 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1072 0, TabStripModel::CommandTogglePinned
));
1073 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1074 1, TabStripModel::CommandTogglePinned
));
1075 EXPECT_TRUE(tabstrip
.IsContextMenuCommandEnabled(
1076 2, TabStripModel::CommandTogglePinned
));
1077 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned
);
1078 EXPECT_EQ("0p 1p 2", GetTabStripStateString(tabstrip
));
1080 // Execute CommandTogglePinned again, this should unpin.
1081 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned
);
1082 EXPECT_EQ("0 1 2", GetTabStripStateString(tabstrip
));
1085 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned
);
1086 EXPECT_EQ("2p 0 1", GetTabStripStateString(tabstrip
));
1088 tabstrip
.CloseAllTabs();
1089 EXPECT_TRUE(tabstrip
.empty());
1092 // Tests the following context menu commands:
1094 // - Close Other Tabs
1095 // - Close Tabs To Right
1096 TEST_F(TabStripModelTest
, TestContextMenuCloseCommands
) {
1097 TabStripDummyDelegate delegate
;
1098 TabStripModel
tabstrip(&delegate
, profile());
1099 EXPECT_TRUE(tabstrip
.empty());
1101 WebContents
* opener
= CreateWebContents();
1102 tabstrip
.AppendWebContents(opener
, true);
1104 WebContents
* contents1
= CreateWebContents();
1105 WebContents
* contents2
= CreateWebContents();
1106 WebContents
* contents3
= CreateWebContents();
1108 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1109 EXPECT_EQ(0, tabstrip
.active_index());
1111 tabstrip
.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab
);
1112 EXPECT_EQ(3, tabstrip
.count());
1114 tabstrip
.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight
);
1115 EXPECT_EQ(1, tabstrip
.count());
1116 EXPECT_EQ(opener
, tabstrip
.GetActiveWebContents());
1118 WebContents
* dummy
= CreateWebContents();
1119 tabstrip
.AppendWebContents(dummy
, false);
1121 contents1
= CreateWebContents();
1122 contents2
= CreateWebContents();
1123 contents3
= CreateWebContents();
1124 InsertWebContentses(&tabstrip
, contents1
, contents2
, contents3
);
1125 EXPECT_EQ(5, tabstrip
.count());
1127 int dummy_index
= tabstrip
.count() - 1;
1128 tabstrip
.ActivateTabAt(dummy_index
, true);
1129 EXPECT_EQ(dummy
, tabstrip
.GetActiveWebContents());
1131 tabstrip
.ExecuteContextMenuCommand(dummy_index
,
1132 TabStripModel::CommandCloseOtherTabs
);
1133 EXPECT_EQ(1, tabstrip
.count());
1134 EXPECT_EQ(dummy
, tabstrip
.GetActiveWebContents());
1136 tabstrip
.CloseAllTabs();
1137 EXPECT_TRUE(tabstrip
.empty());
1140 // Tests GetIndicesClosedByCommand.
1141 TEST_F(TabStripModelTest
, GetIndicesClosedByCommand
) {
1142 TabStripDummyDelegate delegate
;
1143 TabStripModel
tabstrip(&delegate
, profile());
1144 EXPECT_TRUE(tabstrip
.empty());
1146 WebContents
* contents1
= CreateWebContents();
1147 WebContents
* contents2
= CreateWebContents();
1148 WebContents
* contents3
= CreateWebContents();
1149 WebContents
* contents4
= CreateWebContents();
1150 WebContents
* contents5
= CreateWebContents();
1152 tabstrip
.AppendWebContents(contents1
, true);
1153 tabstrip
.AppendWebContents(contents2
, true);
1154 tabstrip
.AppendWebContents(contents3
, true);
1155 tabstrip
.AppendWebContents(contents4
, true);
1156 tabstrip
.AppendWebContents(contents5
, true);
1158 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1159 tabstrip
, 0, TabStripModel::CommandCloseTabsToRight
));
1160 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1161 tabstrip
, 1, TabStripModel::CommandCloseTabsToRight
));
1163 EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1164 tabstrip
, 0, TabStripModel::CommandCloseOtherTabs
));
1165 EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
1166 tabstrip
, 1, TabStripModel::CommandCloseOtherTabs
));
1168 // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
1170 tabstrip
.SetTabPinned(0, true);
1171 tabstrip
.SetTabPinned(1, true);
1173 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1174 tabstrip
, 0, TabStripModel::CommandCloseTabsToRight
));
1175 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1176 tabstrip
, 2, TabStripModel::CommandCloseTabsToRight
));
1178 EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1179 tabstrip
, 0, TabStripModel::CommandCloseOtherTabs
));
1180 EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1181 tabstrip
, 2, TabStripModel::CommandCloseOtherTabs
));
1183 tabstrip
.CloseAllTabs();
1184 EXPECT_TRUE(tabstrip
.empty());
1187 // Tests whether or not WebContentses are inserted in the correct position
1188 // using this "smart" function with a simulated middle click action on a series
1189 // of links on the home page.
1190 TEST_F(TabStripModelTest
, AddWebContents_MiddleClickLinksAndClose
) {
1191 TabStripDummyDelegate delegate
;
1192 TabStripModel
tabstrip(&delegate
, profile());
1193 EXPECT_TRUE(tabstrip
.empty());
1195 // Open the Home Page.
1196 WebContents
* homepage_contents
= CreateWebContents();
1197 tabstrip
.AddWebContents(
1198 homepage_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1199 TabStripModel::ADD_ACTIVE
);
1201 // Open some other tab, by user typing.
1202 WebContents
* typed_page_contents
= CreateWebContents();
1203 tabstrip
.AddWebContents(
1204 typed_page_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1205 TabStripModel::ADD_ACTIVE
);
1207 EXPECT_EQ(2, tabstrip
.count());
1209 // Re-select the home page.
1210 tabstrip
.ActivateTabAt(0, true);
1212 // Open a bunch of tabs by simulating middle clicking on links on the home
1214 WebContents
* middle_click_contents1
= CreateWebContents();
1215 tabstrip
.AddWebContents(
1216 middle_click_contents1
, -1, content::PAGE_TRANSITION_LINK
,
1217 TabStripModel::ADD_NONE
);
1218 WebContents
* middle_click_contents2
= CreateWebContents();
1219 tabstrip
.AddWebContents(
1220 middle_click_contents2
, -1, content::PAGE_TRANSITION_LINK
,
1221 TabStripModel::ADD_NONE
);
1222 WebContents
* middle_click_contents3
= CreateWebContents();
1223 tabstrip
.AddWebContents(
1224 middle_click_contents3
, -1, content::PAGE_TRANSITION_LINK
,
1225 TabStripModel::ADD_NONE
);
1227 EXPECT_EQ(5, tabstrip
.count());
1229 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1230 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetWebContentsAt(1));
1231 EXPECT_EQ(middle_click_contents2
, tabstrip
.GetWebContentsAt(2));
1232 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetWebContentsAt(3));
1233 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(4));
1235 // Now simulate selecting a tab in the middle of the group of tabs opened from
1236 // the home page and start closing them. Each WebContents in the group
1237 // should be closed, right to left. This test is constructed to start at the
1238 // middle WebContents in the group to make sure the cursor wraps around
1239 // to the first WebContents in the group before closing the opener or
1240 // any other WebContents.
1241 tabstrip
.ActivateTabAt(2, true);
1242 tabstrip
.CloseSelectedTabs();
1243 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetActiveWebContents());
1244 tabstrip
.CloseSelectedTabs();
1245 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetActiveWebContents());
1246 tabstrip
.CloseSelectedTabs();
1247 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1248 tabstrip
.CloseSelectedTabs();
1249 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1251 EXPECT_EQ(1, tabstrip
.count());
1253 tabstrip
.CloseAllTabs();
1254 EXPECT_TRUE(tabstrip
.empty());
1257 // Tests whether or not a WebContents created by a left click on a link
1258 // that opens a new tab is inserted correctly adjacent to the tab that spawned
1260 TEST_F(TabStripModelTest
, AddWebContents_LeftClickPopup
) {
1261 TabStripDummyDelegate delegate
;
1262 TabStripModel
tabstrip(&delegate
, profile());
1263 EXPECT_TRUE(tabstrip
.empty());
1265 // Open the Home Page.
1266 WebContents
* homepage_contents
= CreateWebContents();
1267 tabstrip
.AddWebContents(
1268 homepage_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1269 TabStripModel::ADD_ACTIVE
);
1271 // Open some other tab, by user typing.
1272 WebContents
* typed_page_contents
= CreateWebContents();
1273 tabstrip
.AddWebContents(
1274 typed_page_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1275 TabStripModel::ADD_ACTIVE
);
1277 EXPECT_EQ(2, tabstrip
.count());
1279 // Re-select the home page.
1280 tabstrip
.ActivateTabAt(0, true);
1282 // Open a tab by simulating a left click on a link that opens in a new tab.
1283 WebContents
* left_click_contents
= CreateWebContents();
1284 tabstrip
.AddWebContents(left_click_contents
, -1,
1285 content::PAGE_TRANSITION_LINK
,
1286 TabStripModel::ADD_ACTIVE
);
1288 // Verify the state meets our expectations.
1289 EXPECT_EQ(3, tabstrip
.count());
1290 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1291 EXPECT_EQ(left_click_contents
, tabstrip
.GetWebContentsAt(1));
1292 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(2));
1294 // The newly created tab should be selected.
1295 EXPECT_EQ(left_click_contents
, tabstrip
.GetActiveWebContents());
1297 // After closing the selected tab, the selection should move to the left, to
1299 tabstrip
.CloseSelectedTabs();
1300 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1302 EXPECT_EQ(2, tabstrip
.count());
1304 tabstrip
.CloseAllTabs();
1305 EXPECT_TRUE(tabstrip
.empty());
1308 // Tests whether or not new tabs that should split context (typed pages,
1309 // generated urls, also blank tabs) open at the end of the tabstrip instead of
1311 TEST_F(TabStripModelTest
, AddWebContents_CreateNewBlankTab
) {
1312 TabStripDummyDelegate delegate
;
1313 TabStripModel
tabstrip(&delegate
, profile());
1314 EXPECT_TRUE(tabstrip
.empty());
1316 // Open the Home Page.
1317 WebContents
* homepage_contents
= CreateWebContents();
1318 tabstrip
.AddWebContents(
1319 homepage_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1320 TabStripModel::ADD_ACTIVE
);
1322 // Open some other tab, by user typing.
1323 WebContents
* typed_page_contents
= CreateWebContents();
1324 tabstrip
.AddWebContents(
1325 typed_page_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1326 TabStripModel::ADD_ACTIVE
);
1328 EXPECT_EQ(2, tabstrip
.count());
1330 // Re-select the home page.
1331 tabstrip
.ActivateTabAt(0, true);
1333 // Open a new blank tab in the foreground.
1334 WebContents
* new_blank_contents
= CreateWebContents();
1335 tabstrip
.AddWebContents(new_blank_contents
, -1,
1336 content::PAGE_TRANSITION_TYPED
,
1337 TabStripModel::ADD_ACTIVE
);
1339 // Verify the state of the tabstrip.
1340 EXPECT_EQ(3, tabstrip
.count());
1341 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1342 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(1));
1343 EXPECT_EQ(new_blank_contents
, tabstrip
.GetWebContentsAt(2));
1345 // Now open a couple more blank tabs in the background.
1346 WebContents
* background_blank_contents1
= CreateWebContents();
1347 tabstrip
.AddWebContents(
1348 background_blank_contents1
, -1, content::PAGE_TRANSITION_TYPED
,
1349 TabStripModel::ADD_NONE
);
1350 WebContents
* background_blank_contents2
= CreateWebContents();
1351 tabstrip
.AddWebContents(
1352 background_blank_contents2
, -1, content::PAGE_TRANSITION_GENERATED
,
1353 TabStripModel::ADD_NONE
);
1354 EXPECT_EQ(5, tabstrip
.count());
1355 EXPECT_EQ(homepage_contents
, tabstrip
.GetWebContentsAt(0));
1356 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(1));
1357 EXPECT_EQ(new_blank_contents
, tabstrip
.GetWebContentsAt(2));
1358 EXPECT_EQ(background_blank_contents1
, tabstrip
.GetWebContentsAt(3));
1359 EXPECT_EQ(background_blank_contents2
, tabstrip
.GetWebContentsAt(4));
1361 tabstrip
.CloseAllTabs();
1362 EXPECT_TRUE(tabstrip
.empty());
1365 // Tests whether opener state is correctly forgotten when the user switches
1367 TEST_F(TabStripModelTest
, AddWebContents_ForgetOpeners
) {
1368 TabStripDummyDelegate delegate
;
1369 TabStripModel
tabstrip(&delegate
, profile());
1370 EXPECT_TRUE(tabstrip
.empty());
1372 // Open the Home Page
1373 WebContents
* homepage_contents
= CreateWebContents();
1374 tabstrip
.AddWebContents(
1375 homepage_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1376 TabStripModel::ADD_ACTIVE
);
1378 // Open some other tab, by user typing.
1379 WebContents
* typed_page_contents
= CreateWebContents();
1380 tabstrip
.AddWebContents(
1381 typed_page_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1382 TabStripModel::ADD_ACTIVE
);
1384 EXPECT_EQ(2, tabstrip
.count());
1386 // Re-select the home page.
1387 tabstrip
.ActivateTabAt(0, true);
1389 // Open a bunch of tabs by simulating middle clicking on links on the home
1391 WebContents
* middle_click_contents1
= CreateWebContents();
1392 tabstrip
.AddWebContents(
1393 middle_click_contents1
, -1, content::PAGE_TRANSITION_LINK
,
1394 TabStripModel::ADD_NONE
);
1395 WebContents
* middle_click_contents2
= CreateWebContents();
1396 tabstrip
.AddWebContents(
1397 middle_click_contents2
, -1, content::PAGE_TRANSITION_LINK
,
1398 TabStripModel::ADD_NONE
);
1399 WebContents
* middle_click_contents3
= CreateWebContents();
1400 tabstrip
.AddWebContents(
1401 middle_click_contents3
, -1, content::PAGE_TRANSITION_LINK
,
1402 TabStripModel::ADD_NONE
);
1404 // Break out of the context by selecting a tab in a different context.
1405 EXPECT_EQ(typed_page_contents
, tabstrip
.GetWebContentsAt(4));
1406 tabstrip
.SelectLastTab();
1407 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1409 // Step back into the context by selecting a tab inside it.
1410 tabstrip
.ActivateTabAt(2, true);
1411 EXPECT_EQ(middle_click_contents2
, tabstrip
.GetActiveWebContents());
1413 // Now test that closing tabs selects to the right until there are no more,
1414 // then to the left, as if there were no context (context has been
1415 // successfully forgotten).
1416 tabstrip
.CloseSelectedTabs();
1417 EXPECT_EQ(middle_click_contents3
, tabstrip
.GetActiveWebContents());
1418 tabstrip
.CloseSelectedTabs();
1419 EXPECT_EQ(typed_page_contents
, tabstrip
.GetActiveWebContents());
1420 tabstrip
.CloseSelectedTabs();
1421 EXPECT_EQ(middle_click_contents1
, tabstrip
.GetActiveWebContents());
1422 tabstrip
.CloseSelectedTabs();
1423 EXPECT_EQ(homepage_contents
, tabstrip
.GetActiveWebContents());
1425 EXPECT_EQ(1, tabstrip
.count());
1427 tabstrip
.CloseAllTabs();
1428 EXPECT_TRUE(tabstrip
.empty());
1431 // Added for http://b/issue?id=958960
1432 TEST_F(TabStripModelTest
, AppendContentsReselectionTest
) {
1433 TabStripDummyDelegate delegate
;
1434 TabStripModel
tabstrip(&delegate
, profile());
1435 EXPECT_TRUE(tabstrip
.empty());
1437 // Open the Home Page.
1438 WebContents
* homepage_contents
= CreateWebContents();
1439 tabstrip
.AddWebContents(
1440 homepage_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1441 TabStripModel::ADD_ACTIVE
);
1443 // Open some other tab, by user typing.
1444 WebContents
* typed_page_contents
= CreateWebContents();
1445 tabstrip
.AddWebContents(
1446 typed_page_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1447 TabStripModel::ADD_NONE
);
1449 // The selected tab should still be the first.
1450 EXPECT_EQ(0, tabstrip
.active_index());
1452 // Now simulate a link click that opens a new tab (by virtue of target=_blank)
1453 // and make sure the correct tab gets selected when the new tab is closed.
1454 WebContents
* target_blank
= CreateWebContents();
1455 tabstrip
.AppendWebContents(target_blank
, true);
1456 EXPECT_EQ(2, tabstrip
.active_index());
1457 tabstrip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1458 EXPECT_EQ(0, tabstrip
.active_index());
1460 // Clean up after ourselves.
1461 tabstrip
.CloseAllTabs();
1464 // Added for http://b/issue?id=1027661
1465 TEST_F(TabStripModelTest
, ReselectionConsidersChildrenTest
) {
1466 TabStripDummyDelegate delegate
;
1467 TabStripModel
strip(&delegate
, profile());
1470 WebContents
* page_a_contents
= CreateWebContents();
1471 strip
.AddWebContents(
1472 page_a_contents
, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1473 TabStripModel::ADD_ACTIVE
);
1475 // Simulate middle click to open page A.A and A.B
1476 WebContents
* page_a_a_contents
= CreateWebContents();
1477 strip
.AddWebContents(page_a_a_contents
, -1, content::PAGE_TRANSITION_LINK
,
1478 TabStripModel::ADD_NONE
);
1479 WebContents
* page_a_b_contents
= CreateWebContents();
1480 strip
.AddWebContents(page_a_b_contents
, -1, content::PAGE_TRANSITION_LINK
,
1481 TabStripModel::ADD_NONE
);
1484 strip
.ActivateTabAt(1, true);
1485 EXPECT_EQ(page_a_a_contents
, strip
.GetActiveWebContents());
1487 // Simulate a middle click to open page A.A.A
1488 WebContents
* page_a_a_a_contents
= CreateWebContents();
1489 strip
.AddWebContents(page_a_a_a_contents
, -1, content::PAGE_TRANSITION_LINK
,
1490 TabStripModel::ADD_NONE
);
1492 EXPECT_EQ(page_a_a_a_contents
, strip
.GetWebContentsAt(2));
1495 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1497 // Page A.A.A should be selected, NOT A.B
1498 EXPECT_EQ(page_a_a_a_contents
, strip
.GetActiveWebContents());
1501 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1503 // Page A.B should be selected
1504 EXPECT_EQ(page_a_b_contents
, strip
.GetActiveWebContents());
1507 strip
.CloseWebContentsAt(strip
.active_index(), TabStripModel::CLOSE_NONE
);
1509 // Page A should be selected
1510 EXPECT_EQ(page_a_contents
, strip
.GetActiveWebContents());
1513 strip
.CloseAllTabs();
1516 TEST_F(TabStripModelTest
, AddWebContents_NewTabAtEndOfStripInheritsGroup
) {
1517 TabStripDummyDelegate delegate
;
1518 TabStripModel
strip(&delegate
, profile());
1521 WebContents
* page_a_contents
= CreateWebContents();
1522 strip
.AddWebContents(page_a_contents
, -1,
1523 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1524 TabStripModel::ADD_ACTIVE
);
1526 // Open pages B, C and D in the background from links on page A...
1527 WebContents
* page_b_contents
= CreateWebContents();
1528 WebContents
* page_c_contents
= CreateWebContents();
1529 WebContents
* page_d_contents
= CreateWebContents();
1530 strip
.AddWebContents(page_b_contents
, -1, content::PAGE_TRANSITION_LINK
,
1531 TabStripModel::ADD_NONE
);
1532 strip
.AddWebContents(page_c_contents
, -1, content::PAGE_TRANSITION_LINK
,
1533 TabStripModel::ADD_NONE
);
1534 strip
.AddWebContents(page_d_contents
, -1, content::PAGE_TRANSITION_LINK
,
1535 TabStripModel::ADD_NONE
);
1537 // Switch to page B's tab.
1538 strip
.ActivateTabAt(1, true);
1540 // Open a New Tab at the end of the strip (simulate Ctrl+T)
1541 WebContents
* new_contents
= CreateWebContents();
1542 strip
.AddWebContents(new_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1543 TabStripModel::ADD_ACTIVE
);
1545 EXPECT_EQ(4, strip
.GetIndexOfWebContents(new_contents
));
1546 EXPECT_EQ(4, strip
.active_index());
1548 // Close the New Tab that was just opened. We should be returned to page B's
1550 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1552 EXPECT_EQ(1, strip
.active_index());
1554 // Open a non-New Tab tab at the end of the strip, with a TYPED transition.
1555 // This is like typing a URL in the address bar and pressing Alt+Enter. The
1556 // behavior should be the same as above.
1557 WebContents
* page_e_contents
= CreateWebContents();
1558 strip
.AddWebContents(page_e_contents
, -1, content::PAGE_TRANSITION_TYPED
,
1559 TabStripModel::ADD_ACTIVE
);
1561 EXPECT_EQ(4, strip
.GetIndexOfWebContents(page_e_contents
));
1562 EXPECT_EQ(4, strip
.active_index());
1564 // Close the Tab. Selection should shift back to page B's Tab.
1565 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1567 EXPECT_EQ(1, strip
.active_index());
1569 // Open a non-New Tab tab at the end of the strip, with some other
1570 // transition. This is like right clicking on a bookmark and choosing "Open
1571 // in New Tab". No opener relationship should be preserved between this Tab
1572 // and the one that was active when the gesture was performed.
1573 WebContents
* page_f_contents
= CreateWebContents();
1574 strip
.AddWebContents(page_f_contents
, -1,
1575 content::PAGE_TRANSITION_AUTO_BOOKMARK
,
1576 TabStripModel::ADD_ACTIVE
);
1578 EXPECT_EQ(4, strip
.GetIndexOfWebContents(page_f_contents
));
1579 EXPECT_EQ(4, strip
.active_index());
1581 // Close the Tab. The next-adjacent should be selected.
1582 strip
.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE
);
1584 EXPECT_EQ(3, strip
.active_index());
1587 strip
.CloseAllTabs();
1590 // A test of navigations in a tab that is part of a group of opened from some
1591 // parent tab. If the navigations are link clicks, the group relationship of
1592 // the tab to its parent are preserved. If they are of any other type, they are
1594 TEST_F(TabStripModelTest
, NavigationForgetsOpeners
) {
1595 TabStripDummyDelegate delegate
;
1596 TabStripModel
strip(&delegate
, profile());
1599 WebContents
* page_a_contents
= CreateWebContents();
1600 strip
.AddWebContents(page_a_contents
, -1,
1601 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1602 TabStripModel::ADD_ACTIVE
);
1604 // Open pages B, C and D in the background from links on page A...
1605 WebContents
* page_b_contents
= CreateWebContents();
1606 WebContents
* page_c_contents
= CreateWebContents();
1607 WebContents
* page_d_contents
= CreateWebContents();
1608 strip
.AddWebContents(page_b_contents
, -1, content::PAGE_TRANSITION_LINK
,
1609 TabStripModel::ADD_NONE
);
1610 strip
.AddWebContents(page_c_contents
, -1, content::PAGE_TRANSITION_LINK
,
1611 TabStripModel::ADD_NONE
);
1612 strip
.AddWebContents(page_d_contents
, -1, content::PAGE_TRANSITION_LINK
,
1613 TabStripModel::ADD_NONE
);
1615 // Open page E in a different opener group from page A.
1616 WebContents
* page_e_contents
= CreateWebContents();
1617 strip
.AddWebContents(page_e_contents
, -1,
1618 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1619 TabStripModel::ADD_NONE
);
1621 // Tell the TabStripModel that we are navigating page D via a link click.
1622 strip
.ActivateTabAt(3, true);
1623 strip
.TabNavigating(page_d_contents
, content::PAGE_TRANSITION_LINK
);
1625 // Close page D, page C should be selected. (part of same group).
1626 strip
.CloseWebContentsAt(3, TabStripModel::CLOSE_NONE
);
1627 EXPECT_EQ(2, strip
.active_index());
1629 // Tell the TabStripModel that we are navigating in page C via a bookmark.
1630 strip
.TabNavigating(page_c_contents
, content::PAGE_TRANSITION_AUTO_BOOKMARK
);
1632 // Close page C, page E should be selected. (C is no longer part of the
1633 // A-B-C-D group, selection moves to the right).
1634 strip
.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE
);
1635 EXPECT_EQ(page_e_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1637 strip
.CloseAllTabs();
1640 // A test that the forgetting behavior tested in NavigationForgetsOpeners above
1641 // doesn't cause the opener relationship for a New Tab opened at the end of the
1642 // TabStrip to be reset (Test 1 below), unless another any other tab is
1643 // selected (Test 2 below).
1644 TEST_F(TabStripModelTest
, NavigationForgettingDoesntAffectNewTab
) {
1645 TabStripDummyDelegate delegate
;
1646 TabStripModel
strip(&delegate
, profile());
1648 // Open a tab and several tabs from it, then select one of the tabs that was
1650 WebContents
* page_a_contents
= CreateWebContents();
1651 strip
.AddWebContents(page_a_contents
, -1,
1652 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
1653 TabStripModel::ADD_ACTIVE
);
1655 WebContents
* page_b_contents
= CreateWebContents();
1656 WebContents
* page_c_contents
= CreateWebContents();
1657 WebContents
* page_d_contents
= CreateWebContents();
1658 strip
.AddWebContents(page_b_contents
, -1, content::PAGE_TRANSITION_LINK
,
1659 TabStripModel::ADD_NONE
);
1660 strip
.AddWebContents(page_c_contents
, -1, content::PAGE_TRANSITION_LINK
,
1661 TabStripModel::ADD_NONE
);
1662 strip
.AddWebContents(page_d_contents
, -1, content::PAGE_TRANSITION_LINK
,
1663 TabStripModel::ADD_NONE
);
1665 strip
.ActivateTabAt(2, true);
1667 // TEST 1: If the user is in a group of tabs and opens a new tab at the end
1668 // of the strip, closing that new tab will select the tab that they were
1671 // Now simulate opening a new tab at the end of the TabStrip.
1672 WebContents
* new_contents1
= CreateWebContents();
1673 strip
.AddWebContents(new_contents1
, -1, content::PAGE_TRANSITION_TYPED
,
1674 TabStripModel::ADD_ACTIVE
);
1676 // At this point, if we close this tab the last selected one should be
1678 strip
.CloseWebContentsAt(strip
.count() - 1, TabStripModel::CLOSE_NONE
);
1679 EXPECT_EQ(page_c_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1681 // TEST 2: If the user is in a group of tabs and opens a new tab at the end
1682 // of the strip, selecting any other tab in the strip will cause that new
1683 // tab's opener relationship to be forgotten.
1685 // Open a new tab again.
1686 WebContents
* new_contents2
= CreateWebContents();
1687 strip
.AddWebContents(new_contents2
, -1, content::PAGE_TRANSITION_TYPED
,
1688 TabStripModel::ADD_ACTIVE
);
1690 // Now select the first tab.
1691 strip
.ActivateTabAt(0, true);
1693 // Now select the last tab.
1694 strip
.ActivateTabAt(strip
.count() - 1, true);
1696 // Now close the last tab. The next adjacent should be selected.
1697 strip
.CloseWebContentsAt(strip
.count() - 1, TabStripModel::CLOSE_NONE
);
1698 EXPECT_EQ(page_d_contents
, strip
.GetWebContentsAt(strip
.active_index()));
1700 strip
.CloseAllTabs();
1703 // This fails on Linux when run with the rest of unit_tests (crbug.com/302156)
1704 // and fails consistently on Mac and Windows.
1705 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
1706 #define MAYBE_FastShutdown \
1707 DISABLED_FastShutdown
1709 #define MAYBE_FastShutdown \
1712 // Tests that fast shutdown is attempted appropriately.
1713 TEST_F(TabStripModelTest
, MAYBE_FastShutdown
) {
1714 TabStripDummyDelegate delegate
;
1715 TabStripModel
tabstrip(&delegate
, profile());
1716 MockTabStripModelObserver
observer(&tabstrip
);
1717 tabstrip
.AddObserver(&observer
);
1719 EXPECT_TRUE(tabstrip
.empty());
1721 // Make sure fast shutdown is attempted when tabs that share a RPH are shut
1724 WebContents
* contents1
= CreateWebContents();
1725 WebContents
* contents2
= CreateWebContentsWithSharedRPH(contents1
);
1727 SetID(contents1
, 1);
1728 SetID(contents2
, 2);
1730 tabstrip
.AppendWebContents(contents1
, true);
1731 tabstrip
.AppendWebContents(contents2
, true);
1733 // Turn on the fake unload listener so the tabs don't actually get shut
1734 // down when we call CloseAllTabs()---we need to be able to check that
1735 // fast shutdown was attempted.
1736 delegate
.set_run_unload_listener(true);
1737 tabstrip
.CloseAllTabs();
1738 // On a mock RPH this checks whether we *attempted* fast shutdown.
1739 // A real RPH would reject our attempt since there is an unload handler.
1740 EXPECT_TRUE(contents1
->GetRenderProcessHost()->FastShutdownStarted());
1741 EXPECT_EQ(2, tabstrip
.count());
1743 delegate
.set_run_unload_listener(false);
1744 tabstrip
.CloseAllTabs();
1745 EXPECT_TRUE(tabstrip
.empty());
1748 // Make sure fast shutdown is not attempted when only some tabs that share a
1749 // RPH are shut down.
1751 WebContents
* contents1
= CreateWebContents();
1752 WebContents
* contents2
= CreateWebContentsWithSharedRPH(contents1
);
1754 SetID(contents1
, 1);
1755 SetID(contents2
, 2);
1757 tabstrip
.AppendWebContents(contents1
, true);
1758 tabstrip
.AppendWebContents(contents2
, true);
1760 tabstrip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
1761 EXPECT_FALSE(contents1
->GetRenderProcessHost()->FastShutdownStarted());
1762 EXPECT_EQ(1, tabstrip
.count());
1764 tabstrip
.CloseAllTabs();
1765 EXPECT_TRUE(tabstrip
.empty());
1769 // Tests various permutations of apps.
1770 TEST_F(TabStripModelTest
, Apps
) {
1771 TabStripDummyDelegate delegate
;
1772 TabStripModel
tabstrip(&delegate
, profile());
1773 MockTabStripModelObserver
observer(&tabstrip
);
1774 tabstrip
.AddObserver(&observer
);
1776 EXPECT_TRUE(tabstrip
.empty());
1778 typedef MockTabStripModelObserver::State State
;
1781 base::FilePath
path(FILE_PATH_LITERAL("c:\\foo"));
1782 #elif defined(OS_POSIX)
1783 base::FilePath
path(FILE_PATH_LITERAL("/foo"));
1786 base::DictionaryValue manifest
;
1787 manifest
.SetString("name", "hi!");
1788 manifest
.SetString("version", "1");
1789 manifest
.SetString("app.launch.web_url", "http://www.google.com");
1791 scoped_refptr
<Extension
> extension_app(
1792 Extension::Create(path
, extensions::Manifest::INVALID_LOCATION
,
1793 manifest
, Extension::NO_FLAGS
, &error
));
1794 WebContents
* contents1
= CreateWebContents();
1795 extensions::TabHelper::CreateForWebContents(contents1
);
1796 extensions::TabHelper::FromWebContents(contents1
)
1797 ->SetExtensionApp(extension_app
.get());
1798 WebContents
* contents2
= CreateWebContents();
1799 extensions::TabHelper::CreateForWebContents(contents2
);
1800 extensions::TabHelper::FromWebContents(contents2
)
1801 ->SetExtensionApp(extension_app
.get());
1802 WebContents
* contents3
= CreateWebContents();
1804 SetID(contents1
, 1);
1805 SetID(contents2
, 2);
1806 SetID(contents3
, 3);
1808 // Note! The ordering of these tests is important, each subsequent test
1809 // builds on the state established in the previous. This is important if you
1810 // ever insert tests rather than append.
1812 // Initial state, tab3 only and selected.
1813 tabstrip
.AppendWebContents(contents3
, true);
1815 observer
.ClearStates();
1817 // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal
1818 // position and tab1 should end up at position 0.
1820 tabstrip
.InsertWebContentsAt(1, contents1
, TabStripModel::ADD_NONE
);
1822 ASSERT_EQ(1, observer
.GetStateCount());
1823 State
state(contents1
, 0, MockTabStripModelObserver::INSERT
);
1824 EXPECT_TRUE(observer
.StateEquals(0, state
));
1826 // And verify the state.
1827 EXPECT_EQ("1ap 3", GetTabStripStateString(tabstrip
));
1829 observer
.ClearStates();
1832 // Insert tab 2 at position 1.
1834 tabstrip
.InsertWebContentsAt(1, contents2
, TabStripModel::ADD_NONE
);
1836 ASSERT_EQ(1, observer
.GetStateCount());
1837 State
state(contents2
, 1, MockTabStripModelObserver::INSERT
);
1838 EXPECT_TRUE(observer
.StateEquals(0, state
));
1840 // And verify the state.
1841 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip
));
1843 observer
.ClearStates();
1846 // Try to move tab 3 to position 0. This isn't legal and should be ignored.
1848 tabstrip
.MoveWebContentsAt(2, 0, false);
1850 ASSERT_EQ(0, observer
.GetStateCount());
1852 // And verify the state didn't change.
1853 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip
));
1855 observer
.ClearStates();
1858 // Try to move tab 0 to position 3. This isn't legal and should be ignored.
1860 tabstrip
.MoveWebContentsAt(0, 2, false);
1862 ASSERT_EQ(0, observer
.GetStateCount());
1864 // And verify the state didn't change.
1865 EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip
));
1867 observer
.ClearStates();
1870 // Try to move tab 0 to position 1. This is a legal move.
1872 tabstrip
.MoveWebContentsAt(0, 1, false);
1874 ASSERT_EQ(1, observer
.GetStateCount());
1875 State
state(contents1
, 1, MockTabStripModelObserver::MOVE
);
1876 state
.src_index
= 0;
1877 EXPECT_TRUE(observer
.StateEquals(0, state
));
1879 // And verify the state didn't change.
1880 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip
));
1882 observer
.ClearStates();
1885 // Remove tab3 and insert at position 0. It should be forced to position 2.
1887 tabstrip
.DetachWebContentsAt(2);
1888 observer
.ClearStates();
1890 tabstrip
.InsertWebContentsAt(0, contents3
, TabStripModel::ADD_NONE
);
1892 ASSERT_EQ(1, observer
.GetStateCount());
1893 State
state(contents3
, 2, MockTabStripModelObserver::INSERT
);
1894 EXPECT_TRUE(observer
.StateEquals(0, state
));
1896 // And verify the state didn't change.
1897 EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip
));
1899 observer
.ClearStates();
1902 tabstrip
.CloseAllTabs();
1905 // Tests various permutations of pinning tabs.
1906 TEST_F(TabStripModelTest
, Pinning
) {
1907 TabStripDummyDelegate delegate
;
1908 TabStripModel
tabstrip(&delegate
, profile());
1909 MockTabStripModelObserver
observer(&tabstrip
);
1910 tabstrip
.AddObserver(&observer
);
1912 EXPECT_TRUE(tabstrip
.empty());
1914 typedef MockTabStripModelObserver::State State
;
1916 WebContents
* contents1
= CreateWebContents();
1917 WebContents
* contents2
= CreateWebContents();
1918 WebContents
* contents3
= CreateWebContents();
1920 SetID(contents1
, 1);
1921 SetID(contents2
, 2);
1922 SetID(contents3
, 3);
1924 // Note! The ordering of these tests is important, each subsequent test
1925 // builds on the state established in the previous. This is important if you
1926 // ever insert tests rather than append.
1928 // Initial state, three tabs, first selected.
1929 tabstrip
.AppendWebContents(contents1
, true);
1930 tabstrip
.AppendWebContents(contents2
, false);
1931 tabstrip
.AppendWebContents(contents3
, false);
1933 observer
.ClearStates();
1935 // Pin the first tab, this shouldn't visually reorder anything.
1937 tabstrip
.SetTabPinned(0, true);
1939 // As the order didn't change, we should get a pinned notification.
1940 ASSERT_EQ(1, observer
.GetStateCount());
1941 State
state(contents1
, 0, MockTabStripModelObserver::PINNED
);
1942 EXPECT_TRUE(observer
.StateEquals(0, state
));
1944 // And verify the state.
1945 EXPECT_EQ("1p 2 3", GetTabStripStateString(tabstrip
));
1947 observer
.ClearStates();
1950 // Unpin the first tab.
1952 tabstrip
.SetTabPinned(0, false);
1954 // As the order didn't change, we should get a pinned notification.
1955 ASSERT_EQ(1, observer
.GetStateCount());
1956 State
state(contents1
, 0, MockTabStripModelObserver::PINNED
);
1957 EXPECT_TRUE(observer
.StateEquals(0, state
));
1959 // And verify the state.
1960 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip
));
1962 observer
.ClearStates();
1965 // Pin the 3rd tab, which should move it to the front.
1967 tabstrip
.SetTabPinned(2, true);
1969 // The pinning should have resulted in a move and a pinned notification.
1970 ASSERT_EQ(2, observer
.GetStateCount());
1971 State
state(contents3
, 0, MockTabStripModelObserver::MOVE
);
1972 state
.src_index
= 2;
1973 EXPECT_TRUE(observer
.StateEquals(0, state
));
1975 state
= State(contents3
, 0, MockTabStripModelObserver::PINNED
);
1976 EXPECT_TRUE(observer
.StateEquals(1, state
));
1978 // And verify the state.
1979 EXPECT_EQ("3p 1 2", GetTabStripStateString(tabstrip
));
1981 observer
.ClearStates();
1984 // Pin the tab "1", which shouldn't move anything.
1986 tabstrip
.SetTabPinned(1, true);
1988 // As the order didn't change, we should get a pinned notification.
1989 ASSERT_EQ(1, observer
.GetStateCount());
1990 State
state(contents1
, 1, MockTabStripModelObserver::PINNED
);
1991 EXPECT_TRUE(observer
.StateEquals(0, state
));
1993 // And verify the state.
1994 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip
));
1996 observer
.ClearStates();
1999 // Try to move tab "2" to the front, it should be ignored.
2001 tabstrip
.MoveWebContentsAt(2, 0, false);
2003 // As the order didn't change, we should get a pinned notification.
2004 ASSERT_EQ(0, observer
.GetStateCount());
2006 // And verify the state.
2007 EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip
));
2009 observer
.ClearStates();
2012 // Unpin tab "3", which implicitly moves it to the end.
2014 tabstrip
.SetTabPinned(0, false);
2016 ASSERT_EQ(2, observer
.GetStateCount());
2017 State
state(contents3
, 1, MockTabStripModelObserver::MOVE
);
2018 state
.src_index
= 0;
2019 EXPECT_TRUE(observer
.StateEquals(0, state
));
2021 state
= State(contents3
, 1, MockTabStripModelObserver::PINNED
);
2022 EXPECT_TRUE(observer
.StateEquals(1, state
));
2024 // And verify the state.
2025 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip
));
2027 observer
.ClearStates();
2030 // Unpin tab "3", nothing should happen.
2032 tabstrip
.SetTabPinned(1, false);
2034 ASSERT_EQ(0, observer
.GetStateCount());
2036 EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip
));
2038 observer
.ClearStates();
2043 tabstrip
.SetTabPinned(0, true);
2044 tabstrip
.SetTabPinned(1, true);
2046 EXPECT_EQ("1p 3p 2", GetTabStripStateString(tabstrip
));
2048 observer
.ClearStates();
2051 WebContents
* contents4
= CreateWebContents();
2052 SetID(contents4
, 4);
2054 // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end
2057 tabstrip
.InsertWebContentsAt(1, contents4
, TabStripModel::ADD_NONE
);
2059 ASSERT_EQ(1, observer
.GetStateCount());
2060 State
state(contents4
, 2, MockTabStripModelObserver::INSERT
);
2061 EXPECT_TRUE(observer
.StateEquals(0, state
));
2063 EXPECT_EQ("1p 3p 4 2", GetTabStripStateString(tabstrip
));
2066 tabstrip
.CloseAllTabs();
2069 // Makes sure the TabStripModel calls the right observer methods during a
2071 TEST_F(TabStripModelTest
, ReplaceSendsSelected
) {
2072 typedef MockTabStripModelObserver::State State
;
2074 TabStripDummyDelegate delegate
;
2075 TabStripModel
strip(&delegate
, profile());
2077 WebContents
* first_contents
= CreateWebContents();
2078 strip
.AddWebContents(first_contents
, -1, content::PAGE_TRANSITION_TYPED
,
2079 TabStripModel::ADD_ACTIVE
);
2081 MockTabStripModelObserver
tabstrip_observer(&strip
);
2082 strip
.AddObserver(&tabstrip_observer
);
2084 WebContents
* new_contents
= CreateWebContents();
2085 delete strip
.ReplaceWebContentsAt(0, new_contents
);
2087 ASSERT_EQ(2, tabstrip_observer
.GetStateCount());
2089 // First event should be for replaced.
2090 State
state(new_contents
, 0, MockTabStripModelObserver::REPLACED
);
2091 state
.src_contents
= first_contents
;
2092 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state
));
2094 // And the second for selected.
2095 state
= State(new_contents
, 0, MockTabStripModelObserver::ACTIVATE
);
2096 state
.src_contents
= first_contents
;
2097 state
.change_reason
= TabStripModelObserver::CHANGE_REASON_REPLACED
;
2098 EXPECT_TRUE(tabstrip_observer
.StateEquals(1, state
));
2100 // Now add another tab and replace it, making sure we don't get a selected
2102 WebContents
* third_contents
= CreateWebContents();
2103 strip
.AddWebContents(third_contents
, 1, content::PAGE_TRANSITION_TYPED
,
2104 TabStripModel::ADD_NONE
);
2106 tabstrip_observer
.ClearStates();
2109 new_contents
= CreateWebContents();
2110 delete strip
.ReplaceWebContentsAt(1, new_contents
);
2112 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2114 state
= State(new_contents
, 1, MockTabStripModelObserver::REPLACED
);
2115 state
.src_contents
= third_contents
;
2116 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state
));
2118 strip
.CloseAllTabs();
2121 // Ensures discarding tabs leaves TabStripModel in a good state.
2122 TEST_F(TabStripModelTest
, DiscardWebContentsAt
) {
2123 typedef MockTabStripModelObserver::State State
;
2125 TabStripDummyDelegate delegate
;
2126 TabStripModel
tabstrip(&delegate
, profile());
2128 // Fill it with some tabs.
2129 WebContents
* contents1
= CreateWebContents();
2130 tabstrip
.AppendWebContents(contents1
, true);
2131 WebContents
* contents2
= CreateWebContents();
2132 tabstrip
.AppendWebContents(contents2
, true);
2134 // Start watching for events after the appends to avoid observing state
2135 // transitions that aren't relevant to this test.
2136 MockTabStripModelObserver
tabstrip_observer(&tabstrip
);
2137 tabstrip
.AddObserver(&tabstrip_observer
);
2139 // Discard one of the tabs.
2140 WebContents
* null_contents1
= tabstrip
.DiscardWebContentsAt(0);
2141 ASSERT_EQ(2, tabstrip
.count());
2142 EXPECT_TRUE(tabstrip
.IsTabDiscarded(0));
2143 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2144 ASSERT_EQ(null_contents1
, tabstrip
.GetWebContentsAt(0));
2145 ASSERT_EQ(contents2
, tabstrip
.GetWebContentsAt(1));
2146 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2147 State
state1(null_contents1
, 0, MockTabStripModelObserver::REPLACED
);
2148 state1
.src_contents
= contents1
;
2149 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state1
));
2150 tabstrip_observer
.ClearStates();
2152 // Discard the same tab again.
2153 WebContents
* null_contents2
= tabstrip
.DiscardWebContentsAt(0);
2154 ASSERT_EQ(2, tabstrip
.count());
2155 EXPECT_TRUE(tabstrip
.IsTabDiscarded(0));
2156 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2157 ASSERT_EQ(null_contents2
, tabstrip
.GetWebContentsAt(0));
2158 ASSERT_EQ(contents2
, tabstrip
.GetWebContentsAt(1));
2159 ASSERT_EQ(1, tabstrip_observer
.GetStateCount());
2160 State
state2(null_contents2
, 0, MockTabStripModelObserver::REPLACED
);
2161 state2
.src_contents
= null_contents1
;
2162 EXPECT_TRUE(tabstrip_observer
.StateEquals(0, state2
));
2163 tabstrip_observer
.ClearStates();
2165 // Activating the tab should clear its discard state.
2166 tabstrip
.ActivateTabAt(0, true /* user_gesture */);
2167 ASSERT_EQ(2, tabstrip
.count());
2168 EXPECT_FALSE(tabstrip
.IsTabDiscarded(0));
2169 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2171 // Don't discard active tab.
2172 tabstrip
.DiscardWebContentsAt(0);
2173 ASSERT_EQ(2, tabstrip
.count());
2174 EXPECT_FALSE(tabstrip
.IsTabDiscarded(0));
2175 EXPECT_FALSE(tabstrip
.IsTabDiscarded(1));
2177 tabstrip
.CloseAllTabs();
2180 // Makes sure TabStripModel handles the case of deleting a tab while removing
2182 TEST_F(TabStripModelTest
, DeleteFromDestroy
) {
2183 TabStripDummyDelegate delegate
;
2184 TabStripModel
strip(&delegate
, profile());
2185 WebContents
* contents1
= CreateWebContents();
2186 WebContents
* contents2
= CreateWebContents();
2187 strip
.AppendWebContents(contents1
, true);
2188 strip
.AppendWebContents(contents2
, true);
2189 // DeleteWebContentsOnDestroyedObserver deletes contents1 when contents2 sends
2190 // out notification that it is being destroyed.
2191 DeleteWebContentsOnDestroyedObserver
observer(contents2
, contents1
, NULL
);
2192 strip
.CloseAllTabs();
2195 // Makes sure TabStripModel handles the case of deleting another tab and the
2196 // TabStrip while removing another tab.
2197 TEST_F(TabStripModelTest
, DeleteTabStripFromDestroy
) {
2198 TabStripDummyDelegate delegate
;
2199 TabStripModel
* strip
= new TabStripModel(&delegate
, profile());
2200 MockTabStripModelObserver
tab_strip_model_observer(strip
);
2201 strip
->AddObserver(&tab_strip_model_observer
);
2202 WebContents
* contents1
= CreateWebContents();
2203 WebContents
* contents2
= CreateWebContents();
2204 strip
->AppendWebContents(contents1
, true);
2205 strip
->AppendWebContents(contents2
, true);
2206 // DeleteWebContentsOnDestroyedObserver deletes |contents1| and |strip| when
2207 // |contents2| sends out notification that it is being destroyed.
2208 DeleteWebContentsOnDestroyedObserver
observer(contents2
, contents1
, strip
);
2209 strip
->CloseAllTabs();
2210 EXPECT_TRUE(tab_strip_model_observer
.empty());
2211 EXPECT_TRUE(tab_strip_model_observer
.deleted());
2214 TEST_F(TabStripModelTest
, MoveSelectedTabsTo
) {
2216 // Number of tabs the tab strip should have.
2217 const int tab_count
;
2219 // Number of pinned tabs.
2220 const int pinned_count
;
2222 // Index of the tabs to select.
2223 const std::string selected_tabs
;
2225 // Index to move the tabs to.
2226 const int target_index
;
2228 // Expected state after the move (space separated list of indices).
2229 const std::string state_after_move
;
2232 { 2, 0, "0", 1, "1 0" },
2233 { 3, 0, "0", 2, "1 2 0" },
2234 { 3, 0, "2", 0, "2 0 1" },
2235 { 3, 0, "2", 1, "0 2 1" },
2236 { 3, 0, "0 1", 0, "0 1 2" },
2239 { 6, 0, "4 5", 1, "0 4 5 1 2 3" },
2240 { 3, 0, "0 1", 1, "2 0 1" },
2241 { 4, 0, "0 2", 1, "1 0 2 3" },
2242 { 6, 0, "0 1", 3, "2 3 4 0 1 5" },
2245 { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" },
2246 { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" },
2247 { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" },
2250 { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" },
2253 { 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" },
2255 // With pinned tabs.
2256 { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" },
2257 { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" },
2258 { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" },
2259 { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" },
2261 { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" },
2264 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
2265 TabStripDummyDelegate delegate
;
2266 TabStripModel
strip(&delegate
, profile());
2267 ASSERT_NO_FATAL_FAILURE(
2268 PrepareTabstripForSelectionTest(&strip
, test_data
[i
].tab_count
,
2269 test_data
[i
].pinned_count
,
2270 test_data
[i
].selected_tabs
));
2271 strip
.MoveSelectedTabsTo(test_data
[i
].target_index
);
2272 EXPECT_EQ(test_data
[i
].state_after_move
,
2273 GetTabStripStateString(strip
)) << i
;
2274 strip
.CloseAllTabs();
2278 // Tests that moving a tab forgets all groups referencing it.
2279 TEST_F(TabStripModelTest
, MoveSelectedTabsTo_ForgetGroups
) {
2280 TabStripDummyDelegate delegate
;
2281 TabStripModel
strip(&delegate
, profile());
2283 // Open page A as a new tab and then A1 in the background from A.
2284 WebContents
* page_a_contents
= CreateWebContents();
2285 strip
.AddWebContents(page_a_contents
, -1,
2286 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
2287 TabStripModel::ADD_ACTIVE
);
2288 WebContents
* page_a1_contents
= CreateWebContents();
2289 strip
.AddWebContents(page_a1_contents
, -1, content::PAGE_TRANSITION_LINK
,
2290 TabStripModel::ADD_NONE
);
2292 // Likewise, open pages B and B1.
2293 WebContents
* page_b_contents
= CreateWebContents();
2294 strip
.AddWebContents(page_b_contents
, -1,
2295 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
2296 TabStripModel::ADD_ACTIVE
);
2297 WebContents
* page_b1_contents
= CreateWebContents();
2298 strip
.AddWebContents(page_b1_contents
, -1, content::PAGE_TRANSITION_LINK
,
2299 TabStripModel::ADD_NONE
);
2301 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(0));
2302 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(1));
2303 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(2));
2304 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(3));
2306 // Move page B to the start of the tab strip.
2307 strip
.MoveSelectedTabsTo(0);
2309 // Open page B2 in the background from B. It should end up after B.
2310 WebContents
* page_b2_contents
= CreateWebContents();
2311 strip
.AddWebContents(page_b2_contents
, -1, content::PAGE_TRANSITION_LINK
,
2312 TabStripModel::ADD_NONE
);
2313 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(0));
2314 EXPECT_EQ(page_b2_contents
, strip
.GetWebContentsAt(1));
2315 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(2));
2316 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(3));
2317 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(4));
2320 strip
.ActivateTabAt(2, true);
2321 EXPECT_EQ(page_a_contents
, strip
.GetActiveWebContents());
2323 // Open page A2 in the background from A. It should end up after A1.
2324 WebContents
* page_a2_contents
= CreateWebContents();
2325 strip
.AddWebContents(page_a2_contents
, -1, content::PAGE_TRANSITION_LINK
,
2326 TabStripModel::ADD_NONE
);
2327 EXPECT_EQ(page_b_contents
, strip
.GetWebContentsAt(0));
2328 EXPECT_EQ(page_b2_contents
, strip
.GetWebContentsAt(1));
2329 EXPECT_EQ(page_a_contents
, strip
.GetWebContentsAt(2));
2330 EXPECT_EQ(page_a1_contents
, strip
.GetWebContentsAt(3));
2331 EXPECT_EQ(page_a2_contents
, strip
.GetWebContentsAt(4));
2332 EXPECT_EQ(page_b1_contents
, strip
.GetWebContentsAt(5));
2334 strip
.CloseAllTabs();
2337 TEST_F(TabStripModelTest
, CloseSelectedTabs
) {
2338 TabStripDummyDelegate delegate
;
2339 TabStripModel
strip(&delegate
, profile());
2340 WebContents
* contents1
= CreateWebContents();
2341 WebContents
* contents2
= CreateWebContents();
2342 WebContents
* contents3
= CreateWebContents();
2343 strip
.AppendWebContents(contents1
, true);
2344 strip
.AppendWebContents(contents2
, true);
2345 strip
.AppendWebContents(contents3
, true);
2346 strip
.ToggleSelectionAt(1);
2347 strip
.CloseSelectedTabs();
2348 EXPECT_EQ(1, strip
.count());
2349 EXPECT_EQ(0, strip
.active_index());
2350 strip
.CloseAllTabs();
2353 TEST_F(TabStripModelTest
, MultipleSelection
) {
2354 typedef MockTabStripModelObserver::State State
;
2356 TabStripDummyDelegate delegate
;
2357 TabStripModel
strip(&delegate
, profile());
2358 MockTabStripModelObserver
observer(&strip
);
2359 WebContents
* contents0
= CreateWebContents();
2360 WebContents
* contents1
= CreateWebContents();
2361 WebContents
* contents2
= CreateWebContents();
2362 WebContents
* contents3
= CreateWebContents();
2363 strip
.AppendWebContents(contents0
, false);
2364 strip
.AppendWebContents(contents1
, false);
2365 strip
.AppendWebContents(contents2
, false);
2366 strip
.AppendWebContents(contents3
, false);
2367 strip
.AddObserver(&observer
);
2369 // Selection and active tab change.
2370 strip
.ActivateTabAt(3, true);
2371 ASSERT_EQ(2, observer
.GetStateCount());
2372 ASSERT_EQ(observer
.GetStateAt(0).action
,
2373 MockTabStripModelObserver::ACTIVATE
);
2374 State
s1(contents3
, 3, MockTabStripModelObserver::SELECT
);
2375 EXPECT_TRUE(observer
.StateEquals(1, s1
));
2376 observer
.ClearStates();
2378 // Adding all tabs to selection, active tab is now at 0.
2379 strip
.ExtendSelectionTo(0);
2380 ASSERT_EQ(3, observer
.GetStateCount());
2381 ASSERT_EQ(observer
.GetStateAt(0).action
,
2382 MockTabStripModelObserver::DEACTIVATE
);
2383 ASSERT_EQ(observer
.GetStateAt(1).action
,
2384 MockTabStripModelObserver::ACTIVATE
);
2385 State
s2(contents0
, 0, MockTabStripModelObserver::SELECT
);
2386 s2
.src_contents
= contents3
;
2388 EXPECT_TRUE(observer
.StateEquals(2, s2
));
2389 observer
.ClearStates();
2391 // Toggle the active tab, should make the next index active.
2392 strip
.ToggleSelectionAt(0);
2393 EXPECT_EQ(1, strip
.active_index());
2394 EXPECT_EQ(3U, strip
.selection_model().size());
2395 EXPECT_EQ(4, strip
.count());
2396 ASSERT_EQ(3, observer
.GetStateCount());
2397 ASSERT_EQ(observer
.GetStateAt(0).action
,
2398 MockTabStripModelObserver::DEACTIVATE
);
2399 ASSERT_EQ(observer
.GetStateAt(1).action
,
2400 MockTabStripModelObserver::ACTIVATE
);
2401 ASSERT_EQ(observer
.GetStateAt(2).action
,
2402 MockTabStripModelObserver::SELECT
);
2403 observer
.ClearStates();
2405 // Toggle the first tab back to selected and active.
2406 strip
.ToggleSelectionAt(0);
2407 EXPECT_EQ(0, strip
.active_index());
2408 EXPECT_EQ(4U, strip
.selection_model().size());
2409 EXPECT_EQ(4, strip
.count());
2410 ASSERT_EQ(3, observer
.GetStateCount());
2411 ASSERT_EQ(observer
.GetStateAt(0).action
,
2412 MockTabStripModelObserver::DEACTIVATE
);
2413 ASSERT_EQ(observer
.GetStateAt(1).action
,
2414 MockTabStripModelObserver::ACTIVATE
);
2415 ASSERT_EQ(observer
.GetStateAt(2).action
,
2416 MockTabStripModelObserver::SELECT
);
2417 observer
.ClearStates();
2419 // Closing one of the selected tabs, not the active one.
2420 strip
.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE
);
2421 EXPECT_EQ(3, strip
.count());
2422 ASSERT_EQ(3, observer
.GetStateCount());
2423 ASSERT_EQ(observer
.GetStateAt(0).action
,
2424 MockTabStripModelObserver::CLOSE
);
2425 ASSERT_EQ(observer
.GetStateAt(1).action
,
2426 MockTabStripModelObserver::DETACH
);
2427 ASSERT_EQ(observer
.GetStateAt(2).action
,
2428 MockTabStripModelObserver::SELECT
);
2429 observer
.ClearStates();
2431 // Closing the active tab, while there are others tabs selected.
2432 strip
.CloseWebContentsAt(0, TabStripModel::CLOSE_NONE
);
2433 EXPECT_EQ(2, strip
.count());
2434 ASSERT_EQ(5, observer
.GetStateCount());
2435 ASSERT_EQ(observer
.GetStateAt(0).action
,
2436 MockTabStripModelObserver::CLOSE
);
2437 ASSERT_EQ(observer
.GetStateAt(1).action
,
2438 MockTabStripModelObserver::DETACH
);
2439 ASSERT_EQ(observer
.GetStateAt(2).action
,
2440 MockTabStripModelObserver::DEACTIVATE
);
2441 ASSERT_EQ(observer
.GetStateAt(3).action
,
2442 MockTabStripModelObserver::ACTIVATE
);
2443 ASSERT_EQ(observer
.GetStateAt(4).action
,
2444 MockTabStripModelObserver::SELECT
);
2445 observer
.ClearStates();
2447 // Active tab is at 0, deselecting all but the active tab.
2448 strip
.ToggleSelectionAt(1);
2449 ASSERT_EQ(1, observer
.GetStateCount());
2450 ASSERT_EQ(observer
.GetStateAt(0).action
,
2451 MockTabStripModelObserver::SELECT
);
2452 observer
.ClearStates();
2454 // Attempting to deselect the only selected and therefore active tab,
2455 // it is ignored (no notifications being sent) and tab at 0 remains selected
2457 strip
.ToggleSelectionAt(0);
2458 ASSERT_EQ(0, observer
.GetStateCount());
2460 strip
.RemoveObserver(&observer
);
2461 strip
.CloseAllTabs();
2464 // Verifies that if we change the selection from a multi selection to a single
2465 // selection, but not in a way that changes the selected_index that
2466 // TabSelectionChanged is invoked.
2467 TEST_F(TabStripModelTest
, MultipleToSingle
) {
2468 typedef MockTabStripModelObserver::State State
;
2470 TabStripDummyDelegate delegate
;
2471 TabStripModel
strip(&delegate
, profile());
2472 WebContents
* contents1
= CreateWebContents();
2473 WebContents
* contents2
= CreateWebContents();
2474 strip
.AppendWebContents(contents1
, false);
2475 strip
.AppendWebContents(contents2
, false);
2476 strip
.ToggleSelectionAt(0);
2477 strip
.ToggleSelectionAt(1);
2479 MockTabStripModelObserver
observer(&strip
);
2480 strip
.AddObserver(&observer
);
2481 // This changes the selection (0 is no longer selected) but the selected_index
2482 // still remains at 1.
2483 strip
.ActivateTabAt(1, true);
2484 ASSERT_EQ(1, observer
.GetStateCount());
2485 State
s(contents2
, 1, MockTabStripModelObserver::SELECT
);
2486 s
.src_contents
= contents2
;
2488 s
.change_reason
= TabStripModelObserver::CHANGE_REASON_NONE
;
2489 EXPECT_TRUE(observer
.StateEquals(0, s
));
2490 strip
.RemoveObserver(&observer
);
2491 strip
.CloseAllTabs();
2494 // Verifies a newly inserted tab retains its previous blocked state.
2495 // http://crbug.com/276334
2496 TEST_F(TabStripModelTest
, TabBlockedState
) {
2497 // Start with a source tab strip.
2498 TabStripDummyDelegate dummy_tab_strip_delegate
;
2499 TabStripModel
strip_src(&dummy_tab_strip_delegate
, profile());
2500 TabBlockedStateTestBrowser
browser_src(&strip_src
);
2503 WebContents
* contents1
= CreateWebContents();
2504 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1
);
2505 strip_src
.AppendWebContents(contents1
, false);
2508 WebContents
* contents2
= CreateWebContents();
2509 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents2
);
2510 strip_src
.AppendWebContents(contents2
, false);
2512 // Create a destination tab strip.
2513 TabStripModel
strip_dst(&dummy_tab_strip_delegate
, profile());
2514 TabBlockedStateTestBrowser
browser_dst(&strip_dst
);
2516 // Setup a NativeWebContentsModalDialogManager for tab |contents2|.
2517 web_modal::WebContentsModalDialogManager
* modal_dialog_manager
=
2518 web_modal::WebContentsModalDialogManager::FromWebContents(contents2
);
2519 web_modal::WebContentsModalDialogManager::TestApi
test_api(
2520 modal_dialog_manager
);
2521 test_api
.ResetNativeManager(
2522 new DummyNativeWebContentsModalDialogManager(modal_dialog_manager
));
2524 // Show a dialog that blocks tab |contents2|.
2525 // DummyNativeWebContentsModalDialogManager doesn't care about the
2526 // NativeWebContentsModalDialog value, so any dummy value works.
2527 modal_dialog_manager
->ShowDialog(
2528 reinterpret_cast<NativeWebContentsModalDialog
>(0));
2529 EXPECT_TRUE(strip_src
.IsTabBlocked(1));
2532 WebContents
* moved_contents
= strip_src
.DetachWebContentsAt(1);
2533 EXPECT_EQ(contents2
, moved_contents
);
2535 // Attach the tab to the destination tab strip.
2536 strip_dst
.AppendWebContents(moved_contents
, true);
2537 EXPECT_TRUE(strip_dst
.IsTabBlocked(0));
2539 strip_dst
.CloseAllTabs();
2540 strip_src
.CloseAllTabs();