Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / tabs / tab_strip_model_unittest.cc
blob0e520a043f88c7a47e364cc6d65a993d6239983e
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"
7 #include <map>
8 #include <string>
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;
44 namespace {
46 // Class used to delete a WebContents and TabStripModel when another WebContents
47 // is destroyed.
48 class DeleteWebContentsOnDestroyedObserver
49 : public content::WebContentsObserver {
50 public:
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_;
65 tab_strip_ = NULL;
66 delete tab_to_delete;
67 delete tab_strip_to_delete;
70 private:
71 WebContents* tab_to_delete_;
72 TabStripModel* tab_strip_;
74 DISALLOW_COPY_AND_ASSIGN(DeleteWebContentsOnDestroyedObserver);
77 class TabStripDummyDelegate : public TestTabStripModelDelegate {
78 public:
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 {
85 return run_unload_;
88 private:
89 // Whether to report that we need to run an unload listener before closing.
90 bool run_unload_;
92 DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate);
95 const char kTabStripModelTestIDUserDataKey[] = "TabStripModelTestIDUserData";
97 class TabStripModelTestIDUserData : public base::SupportsUserData::Data {
98 public:
99 explicit TabStripModelTestIDUserData(int id) : id_(id) {}
100 virtual ~TabStripModelTestIDUserData() {}
101 int id() { return id_; }
103 private:
104 int id_;
107 class DummyNativeWebContentsModalDialogManager
108 : public web_modal::NativeWebContentsModalDialogManager {
109 public:
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 {}
126 private:
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 {
136 public:
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);
146 private:
147 // TabStripModelObserver
148 virtual void TabInsertedAt(WebContents* contents,
149 int index,
150 bool foreground) OVERRIDE {
151 web_modal::WebContentsModalDialogManager* manager =
152 web_modal::WebContentsModalDialogManager::FromWebContents(contents);
153 if (manager)
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);
161 ASSERT_GE(index, 0);
162 tab_strip_model_->SetTabBlocked(index, blocked);
165 TabStripModel* tab_strip_model_;
167 DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser);
170 } // namespace
172 class TabStripModelTest : public ChromeRenderViewHostTestHarness {
173 public:
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());
184 return retval;
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) {
207 std::string actual;
208 for (int i = 0; i < model.count(); ++i) {
209 if (i > 0)
210 actual += " ";
212 actual += base::IntToString(GetID(model.GetWebContentsAt(i)));
214 if (model.IsAppTab(i))
215 actual += "a";
217 if (model.IsTabPinned(i))
218 actual += "p";
220 return actual;
223 std::string GetIndicesClosedByCommandAsString(
224 const TabStripModel& model,
225 int index,
226 TabStripModel::ContextMenuCommand id) const {
227 std::vector<int> indices = model.GetIndicesClosedByCommand(index, id);
228 std::string result;
229 for (size_t i = 0; i < indices.size(); ++i) {
230 if (i != 0)
231 result += " ";
232 result += base::IntToString(indices[i]);
234 return result;
237 void PrepareTabstripForSelectionTest(TabStripModel* model,
238 int tab_count,
239 int pinned_count,
240 const std::string& selected_tabs) {
241 for (int i = 0; i < tab_count; ++i) {
242 WebContents* contents = CreateWebContents();
243 SetID(contents, i);
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) {
253 int value;
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 {
263 public:
264 explicit MockTabStripModelObserver(TabStripModel* model)
265 : empty_(true),
266 deleted_(false),
267 model_(model) {}
268 virtual ~MockTabStripModelObserver() {}
270 enum TabStripModelObserverAction {
271 INSERT,
272 CLOSE,
273 DETACH,
274 ACTIVATE,
275 DEACTIVATE,
276 SELECT,
277 MOVE,
278 CHANGE,
279 PINNED,
280 REPLACED
283 struct State {
284 State(WebContents* a_dst_contents,
285 int a_dst_index,
286 TabStripModelObserverAction a_action)
287 : src_contents(NULL),
288 dst_contents(a_dst_contents),
289 src_index(-1),
290 dst_index(a_dst_index),
291 change_reason(CHANGE_REASON_NONE),
292 foreground(false),
293 action(a_action) {
296 WebContents* src_contents;
297 WebContents* dst_contents;
298 int src_index;
299 int dst_index;
300 int change_reason;
301 bool foreground;
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,
327 int index,
328 bool foreground) OVERRIDE {
329 empty_ = false;
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,
336 int index,
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,
352 int from_index,
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,
371 int index,
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 {
388 empty_ = true;
390 virtual void TabStripModelDeleted() OVERRIDE {
391 deleted_ = true;
394 void ClearStates() {
395 states_.clear();
398 bool empty() const { return empty_; }
399 bool deleted() const { return deleted_; }
400 TabStripModel* model() { return model_; }
402 private:
403 std::vector<State> states_;
405 bool empty_;
406 bool deleted_;
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();
423 SetID(contents1, 1);
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();
451 SetID(contents2, 2);
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;
467 s4.src_index = 0;
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();
475 SetID(contents3, 3);
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;
500 s3.src_index = 1;
501 EXPECT_TRUE(observer.StateEquals(2, s3));
502 observer.ClearStates();
504 EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
506 // Test DetachWebContentsAt
508 // Detach ...
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;
537 s8.src_index = 1;
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);
574 s1.src_index = 1;
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);
586 s1.src_index = 1;
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));
595 // Test Getters
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
630 // appropriately...
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
690 // -1...
691 EXPECT_EQ(-1,
692 tabstrip.GetIndexOfNextWebContentsOpenedBy(contents1, 3, false));
693 EXPECT_EQ(-1,
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),
730 contents1,
731 TabStripModel::ADD_INHERIT_GROUP);
732 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
733 contents2,
734 TabStripModel::ADD_INHERIT_GROUP);
735 tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
736 contents3,
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();
753 // Test LTR
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
778 // insertion index.
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
800 // opener tab.
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,
822 fg_nonlink_contents,
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
842 // change.
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
847 // is selected
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
908 // selected.
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
926 // CommandCloseTab.
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
953 // selected tabs.
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
977 // CommandCloseTabs.
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));
1084 // Pin the last.
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:
1093 // - Close Tab
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
1169 // commands.
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
1213 // page.
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
1259 // it.
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
1298 // the opener.
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
1310 // in the middle.
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
1366 // context.
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
1390 // page.
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());
1469 // Open page A
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);
1483 // Select page A.A
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));
1494 // Close page A.A
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());
1500 // Close page A.A.A
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());
1506 // Close page A.B
1507 strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1509 // Page A should be selected
1510 EXPECT_EQ(page_a_contents, strip.GetActiveWebContents());
1512 // Clean up.
1513 strip.CloseAllTabs();
1516 TEST_F(TabStripModelTest, AddWebContents_NewTabAtEndOfStripInheritsGroup) {
1517 TabStripDummyDelegate delegate;
1518 TabStripModel strip(&delegate, profile());
1520 // Open page A
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
1549 // Tab...
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());
1586 // Clean up.
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
1593 // not preserved.
1594 TEST_F(TabStripModelTest, NavigationForgetsOpeners) {
1595 TabStripDummyDelegate delegate;
1596 TabStripModel strip(&delegate, profile());
1598 // Open page A
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
1649 // opened.
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
1669 // last on.
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
1677 // re-selected.
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
1708 #else
1709 #define MAYBE_FastShutdown \
1710 FastShutdown
1711 #endif
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
1722 // down.
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;
1780 #if defined(OS_WIN)
1781 base::FilePath path(FILE_PATH_LITERAL("c:\\foo"));
1782 #elif defined(OS_POSIX)
1783 base::FilePath path(FILE_PATH_LITERAL("/foo"));
1784 #endif
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");
1790 std::string error;
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();
2041 // Pin "3" and "1".
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
2055 // up after them.
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
2070 // replace.
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
2101 // event this time.
2102 WebContents* third_contents = CreateWebContents();
2103 strip.AddWebContents(third_contents, 1, content::PAGE_TRANSITION_TYPED,
2104 TabStripModel::ADD_NONE);
2106 tabstrip_observer.ClearStates();
2108 // And replace it.
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
2181 // another tab.
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) {
2215 struct TestData {
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;
2230 } test_data[] = {
2231 // 1 selected tab.
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" },
2238 // 2 selected tabs.
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" },
2244 // 3 selected tabs.
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" },
2249 // 5 selected tabs.
2250 { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" },
2252 // 7 selected tabs
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));
2319 // Switch to A.
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;
2387 s2.src_index = 3;
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
2456 // and active.
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;
2487 s.src_index = 1;
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);
2502 // Add a tab.
2503 WebContents* contents1 = CreateWebContents();
2504 web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1);
2505 strip_src.AppendWebContents(contents1, false);
2507 // Add another tab.
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));
2531 // Detach the tab.
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();