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.
4
5#include "chrome/browser/ui/tabs/tab_strip_model.h"
6
7#include <map>
8#include <string>
9
10#include "base/files/file_path.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/path_service.h"
13#include "base/stl_util.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/string_split.h"
16#include "base/strings/string_util.h"
17#include "base/strings/utf_string_conversions.h"
18#include "chrome/browser/defaults.h"
19#include "chrome/browser/extensions/tab_helper.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/browser/ui/browser.h"
22#include "chrome/browser/ui/browser_tabstrip.h"
23#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
24#include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h"
25#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
26#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
27#include "chrome/common/url_constants.h"
28#include "chrome/test/base/chrome_render_view_host_test_harness.h"
29#include "chrome/test/base/testing_profile.h"
30#include "components/web_modal/popup_manager.h"
31#include "components/web_modal/web_contents_modal_dialog_manager.h"
32#include "content/public/browser/navigation_controller.h"
33#include "content/public/browser/navigation_entry.h"
34#include "content/public/browser/render_process_host.h"
35#include "content/public/browser/web_contents.h"
36#include "content/public/browser/web_contents_observer.h"
37#include "extensions/common/extension.h"
38#include "testing/gtest/include/gtest/gtest.h"
39
40using content::SiteInstance;
41using content::WebContents;
42using extensions::Extension;
43using web_modal::NativeWebContentsModalDialog;
44
45namespace {
46
47// Class used to delete a WebContents and TabStripModel when another WebContents
48// is destroyed.
49class DeleteWebContentsOnDestroyedObserver
50    : public content::WebContentsObserver {
51 public:
52  // When |source| is deleted both |tab_to_delete| and |tab_strip| are deleted.
53  // |tab_to_delete| and |tab_strip| may be NULL.
54  DeleteWebContentsOnDestroyedObserver(WebContents* source,
55                                       WebContents* tab_to_delete,
56                                       TabStripModel* tab_strip)
57      : WebContentsObserver(source),
58        tab_to_delete_(tab_to_delete),
59        tab_strip_(tab_strip) {
60  }
61
62  virtual void WebContentsDestroyed() OVERRIDE {
63    WebContents* tab_to_delete = tab_to_delete_;
64    tab_to_delete_ = NULL;
65    TabStripModel* tab_strip_to_delete = tab_strip_;
66    tab_strip_ = NULL;
67    delete tab_to_delete;
68    delete tab_strip_to_delete;
69  }
70
71 private:
72  WebContents* tab_to_delete_;
73  TabStripModel* tab_strip_;
74
75  DISALLOW_COPY_AND_ASSIGN(DeleteWebContentsOnDestroyedObserver);
76};
77
78class TabStripDummyDelegate : public TestTabStripModelDelegate {
79 public:
80  TabStripDummyDelegate() : run_unload_(false) {}
81  virtual ~TabStripDummyDelegate() {}
82
83  void set_run_unload_listener(bool value) { run_unload_ = value; }
84
85  virtual bool RunUnloadListenerBeforeClosing(WebContents* contents) OVERRIDE {
86    return run_unload_;
87  }
88
89 private:
90  // Whether to report that we need to run an unload listener before closing.
91  bool run_unload_;
92
93  DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate);
94};
95
96const char kTabStripModelTestIDUserDataKey[] = "TabStripModelTestIDUserData";
97
98class TabStripModelTestIDUserData : public base::SupportsUserData::Data {
99 public:
100  explicit TabStripModelTestIDUserData(int id) : id_(id) {}
101  virtual ~TabStripModelTestIDUserData() {}
102  int id() { return id_; }
103
104 private:
105  int id_;
106};
107
108class DummySingleWebContentsDialogManager
109    : public web_modal::SingleWebContentsDialogManager {
110 public:
111  explicit DummySingleWebContentsDialogManager(
112      NativeWebContentsModalDialog dialog,
113      web_modal::SingleWebContentsDialogManagerDelegate* delegate)
114      : delegate_(delegate),
115        dialog_(dialog) {}
116  virtual ~DummySingleWebContentsDialogManager() {}
117
118  virtual void Show() OVERRIDE {}
119  virtual void Hide() OVERRIDE {}
120  virtual void Close() OVERRIDE {
121    delegate_->WillClose(dialog_);
122  }
123  virtual void Focus() OVERRIDE {}
124  virtual void Pulse() OVERRIDE {}
125  virtual void HostChanged(
126      web_modal::WebContentsModalDialogHost* new_host) OVERRIDE {}
127  virtual NativeWebContentsModalDialog dialog() OVERRIDE { return dialog_; }
128
129 private:
130  web_modal::SingleWebContentsDialogManagerDelegate* delegate_;
131  NativeWebContentsModalDialog dialog_;
132
133  DISALLOW_COPY_AND_ASSIGN(DummySingleWebContentsDialogManager);
134};
135
136// Test Browser-like class for TabStripModelTest.TabBlockedState.
137class TabBlockedStateTestBrowser
138    : public TabStripModelObserver,
139      public web_modal::WebContentsModalDialogManagerDelegate {
140 public:
141  explicit TabBlockedStateTestBrowser(TabStripModel* tab_strip_model)
142      : tab_strip_model_(tab_strip_model) {
143    tab_strip_model_->AddObserver(this);
144  }
145
146  virtual ~TabBlockedStateTestBrowser() {
147    tab_strip_model_->RemoveObserver(this);
148  }
149
150 private:
151  // TabStripModelObserver
152  virtual void TabInsertedAt(WebContents* contents,
153                             int index,
154                             bool foreground) OVERRIDE {
155    web_modal::WebContentsModalDialogManager* manager =
156        web_modal::WebContentsModalDialogManager::FromWebContents(contents);
157    if (manager)
158      manager->SetDelegate(this);
159  }
160
161  // WebContentsModalDialogManagerDelegate
162  virtual void SetWebContentsBlocked(content::WebContents* contents,
163                                     bool blocked) OVERRIDE {
164    int index = tab_strip_model_->GetIndexOfWebContents(contents);
165    ASSERT_GE(index, 0);
166    tab_strip_model_->SetTabBlocked(index, blocked);
167  }
168
169  TabStripModel* tab_strip_model_;
170
171  DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser);
172};
173
174}  // namespace
175
176class TabStripModelTest : public ChromeRenderViewHostTestHarness {
177 public:
178  WebContents* CreateWebContents() {
179    return WebContents::Create(WebContents::CreateParams(profile()));
180  }
181
182  WebContents* CreateWebContentsWithSharedRPH(WebContents* web_contents) {
183    WebContents::CreateParams create_params(
184        profile(), web_contents->GetRenderViewHost()->GetSiteInstance());
185    WebContents* retval = WebContents::Create(create_params);
186    EXPECT_EQ(retval->GetRenderProcessHost(),
187              web_contents->GetRenderProcessHost());
188    return retval;
189  }
190
191  // Sets the id of the specified contents.
192  void SetID(WebContents* contents, int id) {
193    contents->SetUserData(&kTabStripModelTestIDUserDataKey,
194                          new TabStripModelTestIDUserData(id));
195  }
196
197  // Returns the id of the specified contents.
198  int GetID(WebContents* contents) {
199    TabStripModelTestIDUserData* user_data =
200        static_cast<TabStripModelTestIDUserData*>(
201            contents->GetUserData(&kTabStripModelTestIDUserDataKey));
202
203    return user_data ? user_data->id() : -1;
204  }
205
206  // Returns the state of the given tab strip as a string. The state consists
207  // of the ID of each web contents followed by a 'p' if pinned. For example,
208  // if the model consists of two tabs with ids 2 and 1, with the first
209  // tab pinned, this returns "2p 1".
210  std::string GetTabStripStateString(const TabStripModel& model) {
211    std::string actual;
212    for (int i = 0; i < model.count(); ++i) {
213      if (i > 0)
214        actual += " ";
215
216      actual += base::IntToString(GetID(model.GetWebContentsAt(i)));
217
218      if (model.IsAppTab(i))
219        actual += "a";
220
221      if (model.IsTabPinned(i))
222        actual += "p";
223    }
224    return actual;
225  }
226
227  std::string GetIndicesClosedByCommandAsString(
228      const TabStripModel& model,
229      int index,
230      TabStripModel::ContextMenuCommand id) const {
231    std::vector<int> indices = model.GetIndicesClosedByCommand(index, id);
232    std::string result;
233    for (size_t i = 0; i < indices.size(); ++i) {
234      if (i != 0)
235        result += " ";
236      result += base::IntToString(indices[i]);
237    }
238    return result;
239  }
240
241  void PrepareTabstripForSelectionTest(TabStripModel* model,
242                                       int tab_count,
243                                       int pinned_count,
244                                       const std::string& selected_tabs) {
245    for (int i = 0; i < tab_count; ++i) {
246      WebContents* contents = CreateWebContents();
247      SetID(contents, i);
248      model->AppendWebContents(contents, true);
249    }
250    for (int i = 0; i < pinned_count; ++i)
251      model->SetTabPinned(i, true);
252
253    ui::ListSelectionModel selection_model;
254    std::vector<std::string> selection;
255    base::SplitStringAlongWhitespace(selected_tabs, &selection);
256    for (size_t i = 0; i < selection.size(); ++i) {
257      int value;
258      ASSERT_TRUE(base::StringToInt(selection[i], &value));
259      selection_model.AddIndexToSelection(value);
260    }
261    selection_model.set_active(selection_model.selected_indices()[0]);
262    model->SetSelectionFromModel(selection_model);
263  }
264};
265
266class MockTabStripModelObserver : public TabStripModelObserver {
267 public:
268  explicit MockTabStripModelObserver(TabStripModel* model)
269      : empty_(true),
270        deleted_(false),
271        model_(model) {}
272  virtual ~MockTabStripModelObserver() {}
273
274  enum TabStripModelObserverAction {
275    INSERT,
276    CLOSE,
277    DETACH,
278    ACTIVATE,
279    DEACTIVATE,
280    SELECT,
281    MOVE,
282    CHANGE,
283    PINNED,
284    REPLACED,
285    CLOSE_ALL,
286    CLOSE_ALL_CANCELED,
287  };
288
289  struct State {
290    State(WebContents* a_dst_contents,
291          int a_dst_index,
292          TabStripModelObserverAction a_action)
293        : src_contents(NULL),
294          dst_contents(a_dst_contents),
295          src_index(-1),
296          dst_index(a_dst_index),
297          change_reason(CHANGE_REASON_NONE),
298          foreground(false),
299          action(a_action) {
300    }
301
302    WebContents* src_contents;
303    WebContents* dst_contents;
304    int src_index;
305    int dst_index;
306    int change_reason;
307    bool foreground;
308    TabStripModelObserverAction action;
309  };
310
311  int GetStateCount() const {
312    return static_cast<int>(states_.size());
313  }
314
315  // Returns (by way of parameters) the number of state's with CLOSE_ALL and
316  // CLOSE_ALL_CANCELED.
317  void GetCloseCounts(int* close_all_count,
318                      int* close_all_canceled_count) {
319    *close_all_count = *close_all_canceled_count = 0;
320    for (int i = 0; i < GetStateCount(); ++i) {
321      switch (GetStateAt(i).action) {
322        case CLOSE_ALL:
323          (*close_all_count)++;
324          break;
325        case CLOSE_ALL_CANCELED:
326          (*close_all_canceled_count)++;
327          break;
328        default:
329          break;
330      }
331    }
332  }
333
334  const State& GetStateAt(int index) const {
335    DCHECK(index >= 0 && index < GetStateCount());
336    return states_[index];
337  }
338
339  bool StateEquals(int index, const State& state) {
340    const State& s = GetStateAt(index);
341    return (s.src_contents == state.src_contents &&
342            s.dst_contents == state.dst_contents &&
343            s.src_index == state.src_index &&
344            s.dst_index == state.dst_index &&
345            s.change_reason == state.change_reason &&
346            s.foreground == state.foreground &&
347            s.action == state.action);
348  }
349
350  // TabStripModelObserver implementation:
351  virtual void TabInsertedAt(WebContents* contents,
352                             int index,
353                             bool foreground) OVERRIDE {
354    empty_ = false;
355    State s(contents, index, INSERT);
356    s.foreground = foreground;
357    states_.push_back(s);
358  }
359  virtual void ActiveTabChanged(WebContents* old_contents,
360                                WebContents* new_contents,
361                                int index,
362                                int reason) OVERRIDE {
363    State s(new_contents, index, ACTIVATE);
364    s.src_contents = old_contents;
365    s.change_reason = reason;
366    states_.push_back(s);
367  }
368  virtual void TabSelectionChanged(
369      TabStripModel* tab_strip_model,
370      const ui::ListSelectionModel& old_model) OVERRIDE {
371    State s(model()->GetActiveWebContents(), model()->active_index(), SELECT);
372    s.src_contents = model()->GetWebContentsAt(old_model.active());
373    s.src_index = old_model.active();
374    states_.push_back(s);
375  }
376  virtual void TabMoved(WebContents* contents,
377                        int from_index,
378                        int to_index) OVERRIDE {
379    State s(contents, to_index, MOVE);
380    s.src_index = from_index;
381    states_.push_back(s);
382  }
383
384  virtual void TabClosingAt(TabStripModel* tab_strip_model,
385                            WebContents* contents,
386                            int index) OVERRIDE {
387    states_.push_back(State(contents, index, CLOSE));
388  }
389  virtual void TabDetachedAt(WebContents* contents, int index) OVERRIDE {
390    states_.push_back(State(contents, index, DETACH));
391  }
392  virtual void TabDeactivated(WebContents* contents) OVERRIDE {
393    states_.push_back(State(contents, model()->active_index(), DEACTIVATE));
394  }
395  virtual void TabChangedAt(WebContents* contents,
396                            int index,
397                            TabChangeType change_type) OVERRIDE {
398    states_.push_back(State(contents, index, CHANGE));
399  }
400  virtual void TabReplacedAt(TabStripModel* tab_strip_model,
401                             WebContents* old_contents,
402                             WebContents* new_contents,
403                             int index) OVERRIDE {
404    State s(new_contents, index, REPLACED);
405    s.src_contents = old_contents;
406    states_.push_back(s);
407  }
408  virtual void TabPinnedStateChanged(WebContents* contents,
409                                     int index) OVERRIDE {
410    states_.push_back(State(contents, index, PINNED));
411  }
412  virtual void TabStripEmpty() OVERRIDE {
413    empty_ = true;
414  }
415  virtual void WillCloseAllTabs() OVERRIDE {
416    states_.push_back(State(NULL, -1, CLOSE_ALL));
417  }
418  virtual void CloseAllTabsCanceled() OVERRIDE {
419    states_.push_back(State(NULL, -1, CLOSE_ALL_CANCELED));
420  }
421  virtual void TabStripModelDeleted() OVERRIDE {
422    deleted_ = true;
423  }
424
425  void ClearStates() {
426    states_.clear();
427  }
428
429  bool empty() const { return empty_; }
430  bool deleted() const { return deleted_; }
431  TabStripModel* model() { return model_; }
432
433 private:
434  std::vector<State> states_;
435
436  bool empty_;
437  bool deleted_;
438  TabStripModel* model_;
439
440  DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver);
441};
442
443TEST_F(TabStripModelTest, TestBasicAPI) {
444  TabStripDummyDelegate delegate;
445  TabStripModel tabstrip(&delegate, profile());
446  MockTabStripModelObserver observer(&tabstrip);
447  tabstrip.AddObserver(&observer);
448
449  EXPECT_TRUE(tabstrip.empty());
450
451  typedef MockTabStripModelObserver::State State;
452
453  WebContents* contents1 = CreateWebContents();
454  SetID(contents1, 1);
455
456  // Note! The ordering of these tests is important, each subsequent test
457  // builds on the state established in the previous. This is important if you
458  // ever insert tests rather than append.
459
460  // Test AppendWebContents, ContainsIndex
461  {
462    EXPECT_FALSE(tabstrip.ContainsIndex(0));
463    tabstrip.AppendWebContents(contents1, true);
464    EXPECT_TRUE(tabstrip.ContainsIndex(0));
465    EXPECT_EQ(1, tabstrip.count());
466    EXPECT_EQ(3, observer.GetStateCount());
467    State s1(contents1, 0, MockTabStripModelObserver::INSERT);
468    s1.foreground = true;
469    EXPECT_TRUE(observer.StateEquals(0, s1));
470    State s2(contents1, 0, MockTabStripModelObserver::ACTIVATE);
471    EXPECT_TRUE(observer.StateEquals(1, s2));
472    State s3(contents1, 0, MockTabStripModelObserver::SELECT);
473    s3.src_contents = NULL;
474    s3.src_index = ui::ListSelectionModel::kUnselectedIndex;
475    EXPECT_TRUE(observer.StateEquals(2, s3));
476    observer.ClearStates();
477  }
478  EXPECT_EQ("1", GetTabStripStateString(tabstrip));
479
480  // Test InsertWebContentsAt, foreground tab.
481  WebContents* contents2 = CreateWebContents();
482  SetID(contents2, 2);
483  {
484    tabstrip.InsertWebContentsAt(1, contents2, TabStripModel::ADD_ACTIVE);
485
486    EXPECT_EQ(2, tabstrip.count());
487    EXPECT_EQ(4, observer.GetStateCount());
488    State s1(contents2, 1, MockTabStripModelObserver::INSERT);
489    s1.foreground = true;
490    EXPECT_TRUE(observer.StateEquals(0, s1));
491    State s2(contents1, 0, MockTabStripModelObserver::DEACTIVATE);
492    EXPECT_TRUE(observer.StateEquals(1, s2));
493    State s3(contents2, 1, MockTabStripModelObserver::ACTIVATE);
494    s3.src_contents = contents1;
495    EXPECT_TRUE(observer.StateEquals(2, s3));
496    State s4(contents2, 1, MockTabStripModelObserver::SELECT);
497    s4.src_contents = contents1;
498    s4.src_index = 0;
499    EXPECT_TRUE(observer.StateEquals(3, s4));
500    observer.ClearStates();
501  }
502  EXPECT_EQ("1 2", GetTabStripStateString(tabstrip));
503
504  // Test InsertWebContentsAt, background tab.
505  WebContents* contents3 = CreateWebContents();
506  SetID(contents3, 3);
507  {
508    tabstrip.InsertWebContentsAt(2, contents3, TabStripModel::ADD_NONE);
509
510    EXPECT_EQ(3, tabstrip.count());
511    EXPECT_EQ(1, observer.GetStateCount());
512    State s1(contents3, 2, MockTabStripModelObserver::INSERT);
513    s1.foreground = false;
514    EXPECT_TRUE(observer.StateEquals(0, s1));
515    observer.ClearStates();
516  }
517  EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
518
519  // Test ActivateTabAt
520  {
521    tabstrip.ActivateTabAt(2, true);
522    EXPECT_EQ(3, observer.GetStateCount());
523    State s1(contents2, 1, MockTabStripModelObserver::DEACTIVATE);
524    EXPECT_TRUE(observer.StateEquals(0, s1));
525    State s2(contents3, 2, MockTabStripModelObserver::ACTIVATE);
526    s2.src_contents = contents2;
527    s2.change_reason = TabStripModelObserver::CHANGE_REASON_USER_GESTURE;
528    EXPECT_TRUE(observer.StateEquals(1, s2));
529    State s3(contents3, 2, MockTabStripModelObserver::SELECT);
530    s3.src_contents = contents2;
531    s3.src_index = 1;
532    EXPECT_TRUE(observer.StateEquals(2, s3));
533    observer.ClearStates();
534  }
535  EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
536
537  // Test DetachWebContentsAt
538  {
539    // Detach ...
540    WebContents* detached = tabstrip.DetachWebContentsAt(2);
541    // ... and append again because we want this for later.
542    tabstrip.AppendWebContents(detached, true);
543    EXPECT_EQ(8, observer.GetStateCount());
544    State s1(detached, 2, MockTabStripModelObserver::DETACH);
545    EXPECT_TRUE(observer.StateEquals(0, s1));
546    State s2(detached, ui::ListSelectionModel::kUnselectedIndex,
547        MockTabStripModelObserver::DEACTIVATE);
548    EXPECT_TRUE(observer.StateEquals(1, s2));
549    State s3(contents2, 1, MockTabStripModelObserver::ACTIVATE);
550    s3.src_contents = contents3;
551    s3.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
552    EXPECT_TRUE(observer.StateEquals(2, s3));
553    State s4(contents2, 1, MockTabStripModelObserver::SELECT);
554    s4.src_contents = NULL;
555    s4.src_index = ui::ListSelectionModel::kUnselectedIndex;
556    EXPECT_TRUE(observer.StateEquals(3, s4));
557    State s5(detached, 2, MockTabStripModelObserver::INSERT);
558    s5.foreground = true;
559    EXPECT_TRUE(observer.StateEquals(4, s5));
560    State s6(contents2, 1, MockTabStripModelObserver::DEACTIVATE);
561    EXPECT_TRUE(observer.StateEquals(5, s6));
562    State s7(detached, 2, MockTabStripModelObserver::ACTIVATE);
563    s7.src_contents = contents2;
564    s7.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
565    EXPECT_TRUE(observer.StateEquals(6, s7));
566    State s8(detached, 2, MockTabStripModelObserver::SELECT);
567    s8.src_contents = contents2;
568    s8.src_index = 1;
569    EXPECT_TRUE(observer.StateEquals(7, s8));
570    observer.ClearStates();
571  }
572  EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
573
574  // Test CloseWebContentsAt
575  {
576    EXPECT_TRUE(tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE));
577    EXPECT_EQ(2, tabstrip.count());
578
579    EXPECT_EQ(5, observer.GetStateCount());
580    State s1(contents3, 2, MockTabStripModelObserver::CLOSE);
581    EXPECT_TRUE(observer.StateEquals(0, s1));
582    State s2(contents3, 2, MockTabStripModelObserver::DETACH);
583    EXPECT_TRUE(observer.StateEquals(1, s2));
584    State s3(contents3, ui::ListSelectionModel::kUnselectedIndex,
585        MockTabStripModelObserver::DEACTIVATE);
586    EXPECT_TRUE(observer.StateEquals(2, s3));
587    State s4(contents2, 1, MockTabStripModelObserver::ACTIVATE);
588    s4.src_contents = contents3;
589    s4.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
590    EXPECT_TRUE(observer.StateEquals(3, s4));
591    State s5(contents2, 1, MockTabStripModelObserver::SELECT);
592    s5.src_contents = NULL;
593    s5.src_index = ui::ListSelectionModel::kUnselectedIndex;
594    EXPECT_TRUE(observer.StateEquals(4, s5));
595    observer.ClearStates();
596  }
597  EXPECT_EQ("1 2", GetTabStripStateString(tabstrip));
598
599  // Test MoveWebContentsAt, select_after_move == true
600  {
601    tabstrip.MoveWebContentsAt(1, 0, true);
602
603    EXPECT_EQ(1, observer.GetStateCount());
604    State s1(contents2, 0, MockTabStripModelObserver::MOVE);
605    s1.src_index = 1;
606    EXPECT_TRUE(observer.StateEquals(0, s1));
607    EXPECT_EQ(0, tabstrip.active_index());
608    observer.ClearStates();
609  }
610  EXPECT_EQ("2 1", GetTabStripStateString(tabstrip));
611
612  // Test MoveWebContentsAt, select_after_move == false
613  {
614    tabstrip.MoveWebContentsAt(1, 0, false);
615    EXPECT_EQ(1, observer.GetStateCount());
616    State s1(contents1, 0, MockTabStripModelObserver::MOVE);
617    s1.src_index = 1;
618    EXPECT_TRUE(observer.StateEquals(0, s1));
619    EXPECT_EQ(1, tabstrip.active_index());
620
621    tabstrip.MoveWebContentsAt(0, 1, false);
622    observer.ClearStates();
623  }
624  EXPECT_EQ("2 1", GetTabStripStateString(tabstrip));
625
626  // Test Getters
627  {
628    EXPECT_EQ(contents2, tabstrip.GetActiveWebContents());
629    EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(0));
630    EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
631    EXPECT_EQ(0, tabstrip.GetIndexOfWebContents(contents2));
632    EXPECT_EQ(1, tabstrip.GetIndexOfWebContents(contents1));
633  }
634
635  // Test UpdateWebContentsStateAt
636  {
637    tabstrip.UpdateWebContentsStateAt(0, TabStripModelObserver::ALL);
638    EXPECT_EQ(1, observer.GetStateCount());
639    State s1(contents2, 0, MockTabStripModelObserver::CHANGE);
640    EXPECT_TRUE(observer.StateEquals(0, s1));
641    observer.ClearStates();
642  }
643
644  // Test SelectNextTab, SelectPreviousTab, SelectLastTab
645  {
646    // Make sure the second of the two tabs is selected first...
647    tabstrip.ActivateTabAt(1, true);
648    tabstrip.SelectPreviousTab();
649    EXPECT_EQ(0, tabstrip.active_index());
650    tabstrip.SelectLastTab();
651    EXPECT_EQ(1, tabstrip.active_index());
652    tabstrip.SelectNextTab();
653    EXPECT_EQ(0, tabstrip.active_index());
654  }
655
656  // Test CloseSelectedTabs
657  {
658    tabstrip.CloseSelectedTabs();
659    // |CloseSelectedTabs| calls CloseWebContentsAt, we already tested that, now
660    // just verify that the count and selected index have changed
661    // appropriately...
662    EXPECT_EQ(1, tabstrip.count());
663    EXPECT_EQ(0, tabstrip.active_index());
664  }
665
666  observer.ClearStates();
667  tabstrip.CloseAllTabs();
668
669  int close_all_count = 0, close_all_canceled_count = 0;
670  observer.GetCloseCounts(&close_all_count, &close_all_canceled_count);
671  EXPECT_EQ(1, close_all_count);
672  EXPECT_EQ(0, close_all_canceled_count);
673
674  // TabStripModel should now be empty.
675  EXPECT_TRUE(tabstrip.empty());
676
677  // Opener methods are tested below...
678
679  tabstrip.RemoveObserver(&observer);
680}
681
682TEST_F(TabStripModelTest, TestBasicOpenerAPI) {
683  TabStripDummyDelegate delegate;
684  TabStripModel tabstrip(&delegate, profile());
685  EXPECT_TRUE(tabstrip.empty());
686
687  // This is a basic test of opener functionality. opener is created
688  // as the first tab in the strip and then we create 5 other tabs in the
689  // background with opener set as their opener.
690
691  WebContents* opener = CreateWebContents();
692  tabstrip.AppendWebContents(opener, true);
693  WebContents* contents1 = CreateWebContents();
694  WebContents* contents2 = CreateWebContents();
695  WebContents* contents3 = CreateWebContents();
696  WebContents* contents4 = CreateWebContents();
697  WebContents* contents5 = CreateWebContents();
698
699  // We use |InsertWebContentsAt| here instead of |AppendWebContents| so that
700  // openership relationships are preserved.
701  tabstrip.InsertWebContentsAt(tabstrip.count(), contents1,
702                               TabStripModel::ADD_INHERIT_GROUP);
703  tabstrip.InsertWebContentsAt(tabstrip.count(), contents2,
704                               TabStripModel::ADD_INHERIT_GROUP);
705  tabstrip.InsertWebContentsAt(tabstrip.count(), contents3,
706                               TabStripModel::ADD_INHERIT_GROUP);
707  tabstrip.InsertWebContentsAt(tabstrip.count(), contents4,
708                               TabStripModel::ADD_INHERIT_GROUP);
709  tabstrip.InsertWebContentsAt(tabstrip.count(), contents5,
710                               TabStripModel::ADD_INHERIT_GROUP);
711
712  // All the tabs should have the same opener.
713  for (int i = 1; i < tabstrip.count(); ++i)
714    EXPECT_EQ(opener, tabstrip.GetOpenerOfWebContentsAt(i));
715
716  // If there is a next adjacent item, then the index should be of that item.
717  EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 1, false));
718  // If the last tab in the group is closed, the preceding tab in the same
719  // group should be selected.
720  EXPECT_EQ(4, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 5, false));
721
722  // Tests the method that finds the last tab opened by the same opener in the
723  // strip (this is the insertion index for the next background tab for the
724  // specified opener).
725  EXPECT_EQ(5, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
726
727  // For a tab that has opened no other tabs, the return value should always be
728  // -1...
729  EXPECT_EQ(-1,
730            tabstrip.GetIndexOfNextWebContentsOpenedBy(contents1, 3, false));
731  EXPECT_EQ(-1,
732            tabstrip.GetIndexOfLastWebContentsOpenedBy(contents1, 3));
733
734  // ForgetAllOpeners should destroy all opener relationships.
735  tabstrip.ForgetAllOpeners();
736  EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 1, false));
737  EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 5, false));
738  EXPECT_EQ(-1, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
739
740  // Specify the last tab as the opener of the others.
741  for (int i = 0; i < tabstrip.count() - 1; ++i)
742    tabstrip.SetOpenerOfWebContentsAt(i, contents5);
743
744  for (int i = 0; i < tabstrip.count() - 1; ++i)
745    EXPECT_EQ(contents5, tabstrip.GetOpenerOfWebContentsAt(i));
746
747  // If there is a next adjacent item, then the index should be of that item.
748  EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(contents5, 1, false));
749
750  // If the last tab in the group is closed, the preceding tab in the same
751  // group should be selected.
752  EXPECT_EQ(3, tabstrip.GetIndexOfNextWebContentsOpenedBy(contents5, 4, false));
753
754  tabstrip.CloseAllTabs();
755  EXPECT_TRUE(tabstrip.empty());
756}
757
758static int GetInsertionIndex(TabStripModel* tabstrip) {
759  return tabstrip->order_controller()->DetermineInsertionIndex(
760      ui::PAGE_TRANSITION_LINK, false);
761}
762
763static void InsertWebContentses(TabStripModel* tabstrip,
764                                WebContents* contents1,
765                                WebContents* contents2,
766                                WebContents* contents3) {
767  tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
768                                contents1,
769                                TabStripModel::ADD_INHERIT_GROUP);
770  tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
771                                contents2,
772                                TabStripModel::ADD_INHERIT_GROUP);
773  tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
774                                contents3,
775                                TabStripModel::ADD_INHERIT_GROUP);
776}
777
778// Tests opening background tabs.
779TEST_F(TabStripModelTest, TestLTRInsertionOptions) {
780  TabStripDummyDelegate delegate;
781  TabStripModel tabstrip(&delegate, profile());
782  EXPECT_TRUE(tabstrip.empty());
783
784  WebContents* opener = CreateWebContents();
785  tabstrip.AppendWebContents(opener, true);
786
787  WebContents* contents1 = CreateWebContents();
788  WebContents* contents2 = CreateWebContents();
789  WebContents* contents3 = CreateWebContents();
790
791  // Test LTR
792  InsertWebContentses(&tabstrip, contents1, contents2, contents3);
793  EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
794  EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(2));
795  EXPECT_EQ(contents3, tabstrip.GetWebContentsAt(3));
796
797  tabstrip.CloseAllTabs();
798  EXPECT_TRUE(tabstrip.empty());
799}
800
801// This test constructs a tabstrip, and then simulates loading several tabs in
802// the background from link clicks on the first tab. Then it simulates opening
803// a new tab from the first tab in the foreground via a link click, verifies
804// that this tab is opened adjacent to the opener, then closes it.
805// Finally it tests that a tab opened for some non-link purpose opens at the
806// end of the strip, not bundled to any existing context.
807TEST_F(TabStripModelTest, TestInsertionIndexDetermination) {
808  TabStripDummyDelegate delegate;
809  TabStripModel tabstrip(&delegate, profile());
810  EXPECT_TRUE(tabstrip.empty());
811
812  WebContents* opener = CreateWebContents();
813  tabstrip.AppendWebContents(opener, true);
814
815  // Open some other random unrelated tab in the background to monkey with our
816  // insertion index.
817  WebContents* other = CreateWebContents();
818  tabstrip.AppendWebContents(other, false);
819
820  WebContents* contents1 = CreateWebContents();
821  WebContents* contents2 = CreateWebContents();
822  WebContents* contents3 = CreateWebContents();
823
824  // Start by testing LTR.
825  InsertWebContentses(&tabstrip, contents1, contents2, contents3);
826  EXPECT_EQ(opener, tabstrip.GetWebContentsAt(0));
827  EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
828  EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(2));
829  EXPECT_EQ(contents3, tabstrip.GetWebContentsAt(3));
830  EXPECT_EQ(other, tabstrip.GetWebContentsAt(4));
831
832  // The opener API should work...
833  EXPECT_EQ(3, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 2, false));
834  EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
835  EXPECT_EQ(3, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
836
837  // Now open a foreground tab from a link. It should be opened adjacent to the
838  // opener tab.
839  WebContents* fg_link_contents = CreateWebContents();
840  int insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
841      ui::PAGE_TRANSITION_LINK, true);
842  EXPECT_EQ(1, insert_index);
843  tabstrip.InsertWebContentsAt(insert_index, fg_link_contents,
844                               TabStripModel::ADD_ACTIVE |
845                               TabStripModel::ADD_INHERIT_GROUP);
846  EXPECT_EQ(1, tabstrip.active_index());
847  EXPECT_EQ(fg_link_contents, tabstrip.GetActiveWebContents());
848
849  // Now close this contents. The selection should move to the opener contents.
850  tabstrip.CloseSelectedTabs();
851  EXPECT_EQ(0, tabstrip.active_index());
852
853  // Now open a new empty tab. It should open at the end of the strip.
854  WebContents* fg_nonlink_contents = CreateWebContents();
855  insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
856      ui::PAGE_TRANSITION_AUTO_BOOKMARK, true);
857  EXPECT_EQ(tabstrip.count(), insert_index);
858  // We break the opener relationship...
859  tabstrip.InsertWebContentsAt(insert_index,
860                               fg_nonlink_contents,
861                               TabStripModel::ADD_NONE);
862  // Now select it, so that user_gesture == true causes the opener relationship
863  // to be forgotten...
864  tabstrip.ActivateTabAt(tabstrip.count() - 1, true);
865  EXPECT_EQ(tabstrip.count() - 1, tabstrip.active_index());
866  EXPECT_EQ(fg_nonlink_contents, tabstrip.GetActiveWebContents());
867
868  // Verify that all opener relationships are forgotten.
869  EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 2, false));
870  EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
871  EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
872  EXPECT_EQ(-1, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
873
874  tabstrip.CloseAllTabs();
875  EXPECT_TRUE(tabstrip.empty());
876}
877
878// Tests that selection is shifted to the correct tab when a tab is closed.
879// If a tab is in the background when it is closed, the selection does not
880// change.
881// If a tab is in the foreground (selected),
882//   If that tab does not have an opener, selection shifts to the right.
883//   If the tab has an opener,
884//     The next tab (scanning LTR) in the entire strip that has the same opener
885//     is selected
886//     If there are no other tabs that have the same opener,
887//       The opener is selected
888//
889TEST_F(TabStripModelTest, TestSelectOnClose) {
890  TabStripDummyDelegate delegate;
891  TabStripModel tabstrip(&delegate, profile());
892  EXPECT_TRUE(tabstrip.empty());
893
894  WebContents* opener = CreateWebContents();
895  tabstrip.AppendWebContents(opener, true);
896
897  WebContents* contents1 = CreateWebContents();
898  WebContents* contents2 = CreateWebContents();
899  WebContents* contents3 = CreateWebContents();
900
901  // Note that we use Detach instead of Close throughout this test to avoid
902  // having to keep reconstructing these WebContentses.
903
904  // First test that closing tabs that are in the background doesn't adjust the
905  // current selection.
906  InsertWebContentses(&tabstrip, contents1, contents2, contents3);
907  EXPECT_EQ(0, tabstrip.active_index());
908
909  tabstrip.DetachWebContentsAt(1);
910  EXPECT_EQ(0, tabstrip.active_index());
911
912  for (int i = tabstrip.count() - 1; i >= 1; --i)
913    tabstrip.DetachWebContentsAt(i);
914
915  // Now test that when a tab doesn't have an opener, selection shifts to the
916  // right when the tab is closed.
917  InsertWebContentses(&tabstrip, contents1, contents2, contents3);
918  EXPECT_EQ(0, tabstrip.active_index());
919
920  tabstrip.ForgetAllOpeners();
921  tabstrip.ActivateTabAt(1, true);
922  EXPECT_EQ(1, tabstrip.active_index());
923  tabstrip.DetachWebContentsAt(1);
924  EXPECT_EQ(1, tabstrip.active_index());
925  tabstrip.DetachWebContentsAt(1);
926  EXPECT_EQ(1, tabstrip.active_index());
927  tabstrip.DetachWebContentsAt(1);
928  EXPECT_EQ(0, tabstrip.active_index());
929
930  for (int i = tabstrip.count() - 1; i >= 1; --i)
931    tabstrip.DetachWebContentsAt(i);
932
933  // Now test that when a tab does have an opener, it selects the next tab
934  // opened by the same opener scanning LTR when it is closed.
935  InsertWebContentses(&tabstrip, contents1, contents2, contents3);
936  EXPECT_EQ(0, tabstrip.active_index());
937  tabstrip.ActivateTabAt(2, false);
938  EXPECT_EQ(2, tabstrip.active_index());
939  tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
940  EXPECT_EQ(2, tabstrip.active_index());
941  tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
942  EXPECT_EQ(1, tabstrip.active_index());
943  tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
944  EXPECT_EQ(0, tabstrip.active_index());
945  // Finally test that when a tab has no "siblings" that the opener is
946  // selected.
947  WebContents* other_contents = CreateWebContents();
948  tabstrip.InsertWebContentsAt(1, other_contents,
949                               TabStripModel::ADD_NONE);
950  EXPECT_EQ(2, tabstrip.count());
951  WebContents* opened_contents = CreateWebContents();
952  tabstrip.InsertWebContentsAt(2, opened_contents,
953                               TabStripModel::ADD_ACTIVE |
954                               TabStripModel::ADD_INHERIT_GROUP);
955  EXPECT_EQ(2, tabstrip.active_index());
956  tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
957  EXPECT_EQ(0, tabstrip.active_index());
958
959  tabstrip.CloseAllTabs();
960  EXPECT_TRUE(tabstrip.empty());
961}
962
963// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
964// CommandCloseTab.
965TEST_F(TabStripModelTest, CommandCloseTab) {
966  TabStripDummyDelegate delegate;
967  TabStripModel tabstrip(&delegate, profile());
968  EXPECT_TRUE(tabstrip.empty());
969
970  // Make sure can_close is honored.
971  ASSERT_NO_FATAL_FAILURE(
972      PrepareTabstripForSelectionTest(&tabstrip, 1, 0, "0"));
973  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
974                  0, TabStripModel::CommandCloseTab));
975  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
976  ASSERT_TRUE(tabstrip.empty());
977
978  // Make sure close on a tab that is selected affects all the selected tabs.
979  ASSERT_NO_FATAL_FAILURE(
980      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
981  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
982                  0, TabStripModel::CommandCloseTab));
983  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
984  // Should have closed tabs 0 and 1.
985  EXPECT_EQ("2", GetTabStripStateString(tabstrip));
986
987  tabstrip.CloseAllTabs();
988  EXPECT_TRUE(tabstrip.empty());
989
990  // Select two tabs and make close on a tab that isn't selected doesn't affect
991  // selected tabs.
992  ASSERT_NO_FATAL_FAILURE(
993      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
994  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
995                  2, TabStripModel::CommandCloseTab));
996  tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
997  // Should have closed tab 2.
998  EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
999  tabstrip.CloseAllTabs();
1000  EXPECT_TRUE(tabstrip.empty());
1001
1002  // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned.
1003  ASSERT_NO_FATAL_FAILURE(
1004      PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1"));
1005  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1006                  0, TabStripModel::CommandCloseTab));
1007  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
1008  // Should have closed tab 2.
1009  EXPECT_EQ("2", GetTabStripStateString(tabstrip));
1010  tabstrip.CloseAllTabs();
1011  EXPECT_TRUE(tabstrip.empty());
1012}
1013
1014// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1015// CommandCloseTabs.
1016TEST_F(TabStripModelTest, CommandCloseOtherTabs) {
1017  TabStripDummyDelegate delegate;
1018  TabStripModel tabstrip(&delegate, profile());
1019  EXPECT_TRUE(tabstrip.empty());
1020
1021  // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled
1022  // and close two tabs.
1023  ASSERT_NO_FATAL_FAILURE(
1024      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
1025  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1026                  0, TabStripModel::CommandCloseOtherTabs));
1027  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs);
1028  EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
1029  tabstrip.CloseAllTabs();
1030  EXPECT_TRUE(tabstrip.empty());
1031
1032  // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it
1033  // with a non-selected index should close the two other tabs.
1034  ASSERT_NO_FATAL_FAILURE(
1035      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
1036  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1037                  2, TabStripModel::CommandCloseOtherTabs));
1038  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs);
1039  EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
1040  tabstrip.CloseAllTabs();
1041  EXPECT_TRUE(tabstrip.empty());
1042
1043  // Select all, CommandCloseOtherTabs should not be enabled.
1044  ASSERT_NO_FATAL_FAILURE(
1045      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1 2"));
1046  EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1047                  2, TabStripModel::CommandCloseOtherTabs));
1048  tabstrip.CloseAllTabs();
1049  EXPECT_TRUE(tabstrip.empty());
1050
1051  // Three tabs, pin one, select the two non-pinned.
1052  ASSERT_NO_FATAL_FAILURE(
1053      PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1 2"));
1054  EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1055                  1, TabStripModel::CommandCloseOtherTabs));
1056  // If we don't pass in the pinned index, the command should be enabled.
1057  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1058                  0, TabStripModel::CommandCloseOtherTabs));
1059  tabstrip.CloseAllTabs();
1060  EXPECT_TRUE(tabstrip.empty());
1061
1062  // 3 tabs, one pinned.
1063  ASSERT_NO_FATAL_FAILURE(
1064      PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1"));
1065  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1066                  1, TabStripModel::CommandCloseOtherTabs));
1067  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1068                  0, TabStripModel::CommandCloseOtherTabs));
1069  tabstrip.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs);
1070  // The pinned tab shouldn't be closed.
1071  EXPECT_EQ("0p 1", GetTabStripStateString(tabstrip));
1072  tabstrip.CloseAllTabs();
1073  EXPECT_TRUE(tabstrip.empty());
1074}
1075
1076// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1077// CommandCloseTabsToRight.
1078TEST_F(TabStripModelTest, CommandCloseTabsToRight) {
1079  TabStripDummyDelegate delegate;
1080  TabStripModel tabstrip(&delegate, profile());
1081  EXPECT_TRUE(tabstrip.empty());
1082
1083  // Create three tabs, select last two tabs, CommandCloseTabsToRight should
1084  // only be enabled for the first tab.
1085  ASSERT_NO_FATAL_FAILURE(
1086      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "1 2"));
1087  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1088                  0, TabStripModel::CommandCloseTabsToRight));
1089  EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1090                   1, TabStripModel::CommandCloseTabsToRight));
1091  EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1092                   2, TabStripModel::CommandCloseTabsToRight));
1093  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
1094  EXPECT_EQ("0", GetTabStripStateString(tabstrip));
1095  tabstrip.CloseAllTabs();
1096  EXPECT_TRUE(tabstrip.empty());
1097}
1098
1099// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1100// CommandTogglePinned.
1101TEST_F(TabStripModelTest, CommandTogglePinned) {
1102  TabStripDummyDelegate delegate;
1103  TabStripModel tabstrip(&delegate, profile());
1104  EXPECT_TRUE(tabstrip.empty());
1105
1106  // Create three tabs with one pinned, pin the first two.
1107  ASSERT_NO_FATAL_FAILURE(
1108      PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1"));
1109  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1110                  0, TabStripModel::CommandTogglePinned));
1111  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1112                  1, TabStripModel::CommandTogglePinned));
1113  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1114                  2, TabStripModel::CommandTogglePinned));
1115  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned);
1116  EXPECT_EQ("0p 1p 2", GetTabStripStateString(tabstrip));
1117
1118  // Execute CommandTogglePinned again, this should unpin.
1119  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned);
1120  EXPECT_EQ("0 1 2", GetTabStripStateString(tabstrip));
1121
1122  // Pin the last.
1123  tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned);
1124  EXPECT_EQ("2p 0 1", GetTabStripStateString(tabstrip));
1125
1126  tabstrip.CloseAllTabs();
1127  EXPECT_TRUE(tabstrip.empty());
1128}
1129
1130// Tests the following context menu commands:
1131//  - Close Tab
1132//  - Close Other Tabs
1133//  - Close Tabs To Right
1134TEST_F(TabStripModelTest, TestContextMenuCloseCommands) {
1135  TabStripDummyDelegate delegate;
1136  TabStripModel tabstrip(&delegate, profile());
1137  EXPECT_TRUE(tabstrip.empty());
1138
1139  WebContents* opener = CreateWebContents();
1140  tabstrip.AppendWebContents(opener, true);
1141
1142  WebContents* contents1 = CreateWebContents();
1143  WebContents* contents2 = CreateWebContents();
1144  WebContents* contents3 = CreateWebContents();
1145
1146  InsertWebContentses(&tabstrip, contents1, contents2, contents3);
1147  EXPECT_EQ(0, tabstrip.active_index());
1148
1149  tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
1150  EXPECT_EQ(3, tabstrip.count());
1151
1152  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
1153  EXPECT_EQ(1, tabstrip.count());
1154  EXPECT_EQ(opener, tabstrip.GetActiveWebContents());
1155
1156  WebContents* dummy = CreateWebContents();
1157  tabstrip.AppendWebContents(dummy, false);
1158
1159  contents1 = CreateWebContents();
1160  contents2 = CreateWebContents();
1161  contents3 = CreateWebContents();
1162  InsertWebContentses(&tabstrip, contents1, contents2, contents3);
1163  EXPECT_EQ(5, tabstrip.count());
1164
1165  int dummy_index = tabstrip.count() - 1;
1166  tabstrip.ActivateTabAt(dummy_index, true);
1167  EXPECT_EQ(dummy, tabstrip.GetActiveWebContents());
1168
1169  tabstrip.ExecuteContextMenuCommand(dummy_index,
1170                                     TabStripModel::CommandCloseOtherTabs);
1171  EXPECT_EQ(1, tabstrip.count());
1172  EXPECT_EQ(dummy, tabstrip.GetActiveWebContents());
1173
1174  tabstrip.CloseAllTabs();
1175  EXPECT_TRUE(tabstrip.empty());
1176}
1177
1178// Tests GetIndicesClosedByCommand.
1179TEST_F(TabStripModelTest, GetIndicesClosedByCommand) {
1180  TabStripDummyDelegate delegate;
1181  TabStripModel tabstrip(&delegate, profile());
1182  EXPECT_TRUE(tabstrip.empty());
1183
1184  WebContents* contents1 = CreateWebContents();
1185  WebContents* contents2 = CreateWebContents();
1186  WebContents* contents3 = CreateWebContents();
1187  WebContents* contents4 = CreateWebContents();
1188  WebContents* contents5 = CreateWebContents();
1189
1190  tabstrip.AppendWebContents(contents1, true);
1191  tabstrip.AppendWebContents(contents2, true);
1192  tabstrip.AppendWebContents(contents3, true);
1193  tabstrip.AppendWebContents(contents4, true);
1194  tabstrip.AppendWebContents(contents5, true);
1195
1196  EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1197                tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
1198  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1199                tabstrip, 1, TabStripModel::CommandCloseTabsToRight));
1200
1201  EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1202                tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
1203  EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
1204                tabstrip, 1, TabStripModel::CommandCloseOtherTabs));
1205
1206  // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
1207  // commands.
1208  tabstrip.SetTabPinned(0, true);
1209  tabstrip.SetTabPinned(1, true);
1210
1211  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1212                tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
1213  EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1214                tabstrip, 2, TabStripModel::CommandCloseTabsToRight));
1215
1216  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1217                tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
1218  EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1219                tabstrip, 2, TabStripModel::CommandCloseOtherTabs));
1220
1221  tabstrip.CloseAllTabs();
1222  EXPECT_TRUE(tabstrip.empty());
1223}
1224
1225// Tests whether or not WebContentses are inserted in the correct position
1226// using this "smart" function with a simulated middle click action on a series
1227// of links on the home page.
1228TEST_F(TabStripModelTest, AddWebContents_MiddleClickLinksAndClose) {
1229  TabStripDummyDelegate delegate;
1230  TabStripModel tabstrip(&delegate, profile());
1231  EXPECT_TRUE(tabstrip.empty());
1232
1233  // Open the Home Page.
1234  WebContents* homepage_contents = CreateWebContents();
1235  tabstrip.AddWebContents(
1236      homepage_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1237      TabStripModel::ADD_ACTIVE);
1238
1239  // Open some other tab, by user typing.
1240  WebContents* typed_page_contents = CreateWebContents();
1241  tabstrip.AddWebContents(
1242      typed_page_contents, -1, ui::PAGE_TRANSITION_TYPED,
1243      TabStripModel::ADD_ACTIVE);
1244
1245  EXPECT_EQ(2, tabstrip.count());
1246
1247  // Re-select the home page.
1248  tabstrip.ActivateTabAt(0, true);
1249
1250  // Open a bunch of tabs by simulating middle clicking on links on the home
1251  // page.
1252  WebContents* middle_click_contents1 = CreateWebContents();
1253  tabstrip.AddWebContents(
1254      middle_click_contents1, -1, ui::PAGE_TRANSITION_LINK,
1255      TabStripModel::ADD_NONE);
1256  WebContents* middle_click_contents2 = CreateWebContents();
1257  tabstrip.AddWebContents(
1258      middle_click_contents2, -1, ui::PAGE_TRANSITION_LINK,
1259      TabStripModel::ADD_NONE);
1260  WebContents* middle_click_contents3 = CreateWebContents();
1261  tabstrip.AddWebContents(
1262      middle_click_contents3, -1, ui::PAGE_TRANSITION_LINK,
1263      TabStripModel::ADD_NONE);
1264
1265  EXPECT_EQ(5, tabstrip.count());
1266
1267  EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1268  EXPECT_EQ(middle_click_contents1, tabstrip.GetWebContentsAt(1));
1269  EXPECT_EQ(middle_click_contents2, tabstrip.GetWebContentsAt(2));
1270  EXPECT_EQ(middle_click_contents3, tabstrip.GetWebContentsAt(3));
1271  EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(4));
1272
1273  // Now simulate selecting a tab in the middle of the group of tabs opened from
1274  // the home page and start closing them. Each WebContents in the group
1275  // should be closed, right to left. This test is constructed to start at the
1276  // middle WebContents in the group to make sure the cursor wraps around
1277  // to the first WebContents in the group before closing the opener or
1278  // any other WebContents.
1279  tabstrip.ActivateTabAt(2, true);
1280  tabstrip.CloseSelectedTabs();
1281  EXPECT_EQ(middle_click_contents3, tabstrip.GetActiveWebContents());
1282  tabstrip.CloseSelectedTabs();
1283  EXPECT_EQ(middle_click_contents1, tabstrip.GetActiveWebContents());
1284  tabstrip.CloseSelectedTabs();
1285  EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1286  tabstrip.CloseSelectedTabs();
1287  EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1288
1289  EXPECT_EQ(1, tabstrip.count());
1290
1291  tabstrip.CloseAllTabs();
1292  EXPECT_TRUE(tabstrip.empty());
1293}
1294
1295// Tests whether or not a WebContents created by a left click on a link
1296// that opens a new tab is inserted correctly adjacent to the tab that spawned
1297// it.
1298TEST_F(TabStripModelTest, AddWebContents_LeftClickPopup) {
1299  TabStripDummyDelegate delegate;
1300  TabStripModel tabstrip(&delegate, profile());
1301  EXPECT_TRUE(tabstrip.empty());
1302
1303  // Open the Home Page.
1304  WebContents* homepage_contents = CreateWebContents();
1305  tabstrip.AddWebContents(
1306      homepage_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1307      TabStripModel::ADD_ACTIVE);
1308
1309  // Open some other tab, by user typing.
1310  WebContents* typed_page_contents = CreateWebContents();
1311  tabstrip.AddWebContents(
1312      typed_page_contents, -1, ui::PAGE_TRANSITION_TYPED,
1313      TabStripModel::ADD_ACTIVE);
1314
1315  EXPECT_EQ(2, tabstrip.count());
1316
1317  // Re-select the home page.
1318  tabstrip.ActivateTabAt(0, true);
1319
1320  // Open a tab by simulating a left click on a link that opens in a new tab.
1321  WebContents* left_click_contents = CreateWebContents();
1322  tabstrip.AddWebContents(left_click_contents, -1,
1323                          ui::PAGE_TRANSITION_LINK,
1324                          TabStripModel::ADD_ACTIVE);
1325
1326  // Verify the state meets our expectations.
1327  EXPECT_EQ(3, tabstrip.count());
1328  EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1329  EXPECT_EQ(left_click_contents, tabstrip.GetWebContentsAt(1));
1330  EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(2));
1331
1332  // The newly created tab should be selected.
1333  EXPECT_EQ(left_click_contents, tabstrip.GetActiveWebContents());
1334
1335  // After closing the selected tab, the selection should move to the left, to
1336  // the opener.
1337  tabstrip.CloseSelectedTabs();
1338  EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1339
1340  EXPECT_EQ(2, tabstrip.count());
1341
1342  tabstrip.CloseAllTabs();
1343  EXPECT_TRUE(tabstrip.empty());
1344}
1345
1346// Tests whether or not new tabs that should split context (typed pages,
1347// generated urls, also blank tabs) open at the end of the tabstrip instead of
1348// in the middle.
1349TEST_F(TabStripModelTest, AddWebContents_CreateNewBlankTab) {
1350  TabStripDummyDelegate delegate;
1351  TabStripModel tabstrip(&delegate, profile());
1352  EXPECT_TRUE(tabstrip.empty());
1353
1354  // Open the Home Page.
1355  WebContents* homepage_contents = CreateWebContents();
1356  tabstrip.AddWebContents(
1357      homepage_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1358      TabStripModel::ADD_ACTIVE);
1359
1360  // Open some other tab, by user typing.
1361  WebContents* typed_page_contents = CreateWebContents();
1362  tabstrip.AddWebContents(
1363      typed_page_contents, -1, ui::PAGE_TRANSITION_TYPED,
1364      TabStripModel::ADD_ACTIVE);
1365
1366  EXPECT_EQ(2, tabstrip.count());
1367
1368  // Re-select the home page.
1369  tabstrip.ActivateTabAt(0, true);
1370
1371  // Open a new blank tab in the foreground.
1372  WebContents* new_blank_contents = CreateWebContents();
1373  tabstrip.AddWebContents(new_blank_contents, -1,
1374                          ui::PAGE_TRANSITION_TYPED,
1375                          TabStripModel::ADD_ACTIVE);
1376
1377  // Verify the state of the tabstrip.
1378  EXPECT_EQ(3, tabstrip.count());
1379  EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1380  EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(1));
1381  EXPECT_EQ(new_blank_contents, tabstrip.GetWebContentsAt(2));
1382
1383  // Now open a couple more blank tabs in the background.
1384  WebContents* background_blank_contents1 = CreateWebContents();
1385  tabstrip.AddWebContents(
1386      background_blank_contents1, -1, ui::PAGE_TRANSITION_TYPED,
1387      TabStripModel::ADD_NONE);
1388  WebContents* background_blank_contents2 = CreateWebContents();
1389  tabstrip.AddWebContents(
1390      background_blank_contents2, -1, ui::PAGE_TRANSITION_GENERATED,
1391      TabStripModel::ADD_NONE);
1392  EXPECT_EQ(5, tabstrip.count());
1393  EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1394  EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(1));
1395  EXPECT_EQ(new_blank_contents, tabstrip.GetWebContentsAt(2));
1396  EXPECT_EQ(background_blank_contents1, tabstrip.GetWebContentsAt(3));
1397  EXPECT_EQ(background_blank_contents2, tabstrip.GetWebContentsAt(4));
1398
1399  tabstrip.CloseAllTabs();
1400  EXPECT_TRUE(tabstrip.empty());
1401}
1402
1403// Tests whether opener state is correctly forgotten when the user switches
1404// context.
1405TEST_F(TabStripModelTest, AddWebContents_ForgetOpeners) {
1406  TabStripDummyDelegate delegate;
1407  TabStripModel tabstrip(&delegate, profile());
1408  EXPECT_TRUE(tabstrip.empty());
1409
1410  // Open the Home Page
1411  WebContents* homepage_contents = CreateWebContents();
1412  tabstrip.AddWebContents(
1413      homepage_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1414      TabStripModel::ADD_ACTIVE);
1415
1416  // Open some other tab, by user typing.
1417  WebContents* typed_page_contents = CreateWebContents();
1418  tabstrip.AddWebContents(
1419      typed_page_contents, -1, ui::PAGE_TRANSITION_TYPED,
1420      TabStripModel::ADD_ACTIVE);
1421
1422  EXPECT_EQ(2, tabstrip.count());
1423
1424  // Re-select the home page.
1425  tabstrip.ActivateTabAt(0, true);
1426
1427  // Open a bunch of tabs by simulating middle clicking on links on the home
1428  // page.
1429  WebContents* middle_click_contents1 = CreateWebContents();
1430  tabstrip.AddWebContents(
1431      middle_click_contents1, -1, ui::PAGE_TRANSITION_LINK,
1432      TabStripModel::ADD_NONE);
1433  WebContents* middle_click_contents2 = CreateWebContents();
1434  tabstrip.AddWebContents(
1435      middle_click_contents2, -1, ui::PAGE_TRANSITION_LINK,
1436      TabStripModel::ADD_NONE);
1437  WebContents* middle_click_contents3 = CreateWebContents();
1438  tabstrip.AddWebContents(
1439      middle_click_contents3, -1, ui::PAGE_TRANSITION_LINK,
1440      TabStripModel::ADD_NONE);
1441
1442  // Break out of the context by selecting a tab in a different context.
1443  EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(4));
1444  tabstrip.SelectLastTab();
1445  EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1446
1447  // Step back into the context by selecting a tab inside it.
1448  tabstrip.ActivateTabAt(2, true);
1449  EXPECT_EQ(middle_click_contents2, tabstrip.GetActiveWebContents());
1450
1451  // Now test that closing tabs selects to the right until there are no more,
1452  // then to the left, as if there were no context (context has been
1453  // successfully forgotten).
1454  tabstrip.CloseSelectedTabs();
1455  EXPECT_EQ(middle_click_contents3, tabstrip.GetActiveWebContents());
1456  tabstrip.CloseSelectedTabs();
1457  EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1458  tabstrip.CloseSelectedTabs();
1459  EXPECT_EQ(middle_click_contents1, tabstrip.GetActiveWebContents());
1460  tabstrip.CloseSelectedTabs();
1461  EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1462
1463  EXPECT_EQ(1, tabstrip.count());
1464
1465  tabstrip.CloseAllTabs();
1466  EXPECT_TRUE(tabstrip.empty());
1467}
1468
1469// Added for http://b/issue?id=958960
1470TEST_F(TabStripModelTest, AppendContentsReselectionTest) {
1471  TabStripDummyDelegate delegate;
1472  TabStripModel tabstrip(&delegate, profile());
1473  EXPECT_TRUE(tabstrip.empty());
1474
1475  // Open the Home Page.
1476  WebContents* homepage_contents = CreateWebContents();
1477  tabstrip.AddWebContents(
1478      homepage_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1479      TabStripModel::ADD_ACTIVE);
1480
1481  // Open some other tab, by user typing.
1482  WebContents* typed_page_contents = CreateWebContents();
1483  tabstrip.AddWebContents(
1484      typed_page_contents, -1, ui::PAGE_TRANSITION_TYPED,
1485      TabStripModel::ADD_NONE);
1486
1487  // The selected tab should still be the first.
1488  EXPECT_EQ(0, tabstrip.active_index());
1489
1490  // Now simulate a link click that opens a new tab (by virtue of target=_blank)
1491  // and make sure the correct tab gets selected when the new tab is closed.
1492  WebContents* target_blank = CreateWebContents();
1493  tabstrip.AppendWebContents(target_blank, true);
1494  EXPECT_EQ(2, tabstrip.active_index());
1495  tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
1496  EXPECT_EQ(0, tabstrip.active_index());
1497
1498  // Clean up after ourselves.
1499  tabstrip.CloseAllTabs();
1500}
1501
1502// Added for http://b/issue?id=1027661
1503TEST_F(TabStripModelTest, ReselectionConsidersChildrenTest) {
1504  TabStripDummyDelegate delegate;
1505  TabStripModel strip(&delegate, profile());
1506
1507  // Open page A
1508  WebContents* page_a_contents = CreateWebContents();
1509  strip.AddWebContents(
1510      page_a_contents, -1, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1511      TabStripModel::ADD_ACTIVE);
1512
1513  // Simulate middle click to open page A.A and A.B
1514  WebContents* page_a_a_contents = CreateWebContents();
1515  strip.AddWebContents(page_a_a_contents, -1, ui::PAGE_TRANSITION_LINK,
1516                       TabStripModel::ADD_NONE);
1517  WebContents* page_a_b_contents = CreateWebContents();
1518  strip.AddWebContents(page_a_b_contents, -1, ui::PAGE_TRANSITION_LINK,
1519                       TabStripModel::ADD_NONE);
1520
1521  // Select page A.A
1522  strip.ActivateTabAt(1, true);
1523  EXPECT_EQ(page_a_a_contents, strip.GetActiveWebContents());
1524
1525  // Simulate a middle click to open page A.A.A
1526  WebContents* page_a_a_a_contents = CreateWebContents();
1527  strip.AddWebContents(page_a_a_a_contents, -1, ui::PAGE_TRANSITION_LINK,
1528                       TabStripModel::ADD_NONE);
1529
1530  EXPECT_EQ(page_a_a_a_contents, strip.GetWebContentsAt(2));
1531
1532  // Close page A.A
1533  strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1534
1535  // Page A.A.A should be selected, NOT A.B
1536  EXPECT_EQ(page_a_a_a_contents, strip.GetActiveWebContents());
1537
1538  // Close page A.A.A
1539  strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1540
1541  // Page A.B should be selected
1542  EXPECT_EQ(page_a_b_contents, strip.GetActiveWebContents());
1543
1544  // Close page A.B
1545  strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1546
1547  // Page A should be selected
1548  EXPECT_EQ(page_a_contents, strip.GetActiveWebContents());
1549
1550  // Clean up.
1551  strip.CloseAllTabs();
1552}
1553
1554TEST_F(TabStripModelTest, AddWebContents_NewTabAtEndOfStripInheritsGroup) {
1555  TabStripDummyDelegate delegate;
1556  TabStripModel strip(&delegate, profile());
1557
1558  // Open page A
1559  WebContents* page_a_contents = CreateWebContents();
1560  strip.AddWebContents(page_a_contents, -1,
1561                       ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1562                       TabStripModel::ADD_ACTIVE);
1563
1564  // Open pages B, C and D in the background from links on page A...
1565  WebContents* page_b_contents = CreateWebContents();
1566  WebContents* page_c_contents = CreateWebContents();
1567  WebContents* page_d_contents = CreateWebContents();
1568  strip.AddWebContents(page_b_contents, -1, ui::PAGE_TRANSITION_LINK,
1569                       TabStripModel::ADD_NONE);
1570  strip.AddWebContents(page_c_contents, -1, ui::PAGE_TRANSITION_LINK,
1571                       TabStripModel::ADD_NONE);
1572  strip.AddWebContents(page_d_contents, -1, ui::PAGE_TRANSITION_LINK,
1573                       TabStripModel::ADD_NONE);
1574
1575  // Switch to page B's tab.
1576  strip.ActivateTabAt(1, true);
1577
1578  // Open a New Tab at the end of the strip (simulate Ctrl+T)
1579  WebContents* new_contents = CreateWebContents();
1580  strip.AddWebContents(new_contents, -1, ui::PAGE_TRANSITION_TYPED,
1581                       TabStripModel::ADD_ACTIVE);
1582
1583  EXPECT_EQ(4, strip.GetIndexOfWebContents(new_contents));
1584  EXPECT_EQ(4, strip.active_index());
1585
1586  // Close the New Tab that was just opened. We should be returned to page B's
1587  // Tab...
1588  strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1589
1590  EXPECT_EQ(1, strip.active_index());
1591
1592  // Open a non-New Tab tab at the end of the strip, with a TYPED transition.
1593  // This is like typing a URL in the address bar and pressing Alt+Enter. The
1594  // behavior should be the same as above.
1595  WebContents* page_e_contents = CreateWebContents();
1596  strip.AddWebContents(page_e_contents, -1, ui::PAGE_TRANSITION_TYPED,
1597                       TabStripModel::ADD_ACTIVE);
1598
1599  EXPECT_EQ(4, strip.GetIndexOfWebContents(page_e_contents));
1600  EXPECT_EQ(4, strip.active_index());
1601
1602  // Close the Tab. Selection should shift back to page B's Tab.
1603  strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1604
1605  EXPECT_EQ(1, strip.active_index());
1606
1607  // Open a non-New Tab tab at the end of the strip, with some other
1608  // transition. This is like right clicking on a bookmark and choosing "Open
1609  // in New Tab". No opener relationship should be preserved between this Tab
1610  // and the one that was active when the gesture was performed.
1611  WebContents* page_f_contents = CreateWebContents();
1612  strip.AddWebContents(page_f_contents, -1,
1613                       ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1614                       TabStripModel::ADD_ACTIVE);
1615
1616  EXPECT_EQ(4, strip.GetIndexOfWebContents(page_f_contents));
1617  EXPECT_EQ(4, strip.active_index());
1618
1619  // Close the Tab. The next-adjacent should be selected.
1620  strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1621
1622  EXPECT_EQ(3, strip.active_index());
1623
1624  // Clean up.
1625  strip.CloseAllTabs();
1626}
1627
1628// A test of navigations in a tab that is part of a group of opened from some
1629// parent tab. If the navigations are link clicks, the group relationship of
1630// the tab to its parent are preserved. If they are of any other type, they are
1631// not preserved.
1632TEST_F(TabStripModelTest, NavigationForgetsOpeners) {
1633  TabStripDummyDelegate delegate;
1634  TabStripModel strip(&delegate, profile());
1635
1636  // Open page A
1637  WebContents* page_a_contents = CreateWebContents();
1638  strip.AddWebContents(page_a_contents, -1,
1639                       ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1640                       TabStripModel::ADD_ACTIVE);
1641
1642  // Open pages B, C and D in the background from links on page A...
1643  WebContents* page_b_contents = CreateWebContents();
1644  WebContents* page_c_contents = CreateWebContents();
1645  WebContents* page_d_contents = CreateWebContents();
1646  strip.AddWebContents(page_b_contents, -1, ui::PAGE_TRANSITION_LINK,
1647                       TabStripModel::ADD_NONE);
1648  strip.AddWebContents(page_c_contents, -1, ui::PAGE_TRANSITION_LINK,
1649                       TabStripModel::ADD_NONE);
1650  strip.AddWebContents(page_d_contents, -1, ui::PAGE_TRANSITION_LINK,
1651                       TabStripModel::ADD_NONE);
1652
1653  // Open page E in a different opener group from page A.
1654  WebContents* page_e_contents = CreateWebContents();
1655  strip.AddWebContents(page_e_contents, -1,
1656                       ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1657                       TabStripModel::ADD_NONE);
1658
1659  // Tell the TabStripModel that we are navigating page D via a link click.
1660  strip.ActivateTabAt(3, true);
1661  strip.TabNavigating(page_d_contents, ui::PAGE_TRANSITION_LINK);
1662
1663  // Close page D, page C should be selected. (part of same group).
1664  strip.CloseWebContentsAt(3, TabStripModel::CLOSE_NONE);
1665  EXPECT_EQ(2, strip.active_index());
1666
1667  // Tell the TabStripModel that we are navigating in page C via a bookmark.
1668  strip.TabNavigating(page_c_contents, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
1669
1670  // Close page C, page E should be selected. (C is no longer part of the
1671  // A-B-C-D group, selection moves to the right).
1672  strip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
1673  EXPECT_EQ(page_e_contents, strip.GetWebContentsAt(strip.active_index()));
1674
1675  strip.CloseAllTabs();
1676}
1677
1678// A test that the forgetting behavior tested in NavigationForgetsOpeners above
1679// doesn't cause the opener relationship for a New Tab opened at the end of the
1680// TabStrip to be reset (Test 1 below), unless another any other tab is
1681// selected (Test 2 below).
1682TEST_F(TabStripModelTest, NavigationForgettingDoesntAffectNewTab) {
1683  TabStripDummyDelegate delegate;
1684  TabStripModel strip(&delegate, profile());
1685
1686  // Open a tab and several tabs from it, then select one of the tabs that was
1687  // opened.
1688  WebContents* page_a_contents = CreateWebContents();
1689  strip.AddWebContents(page_a_contents, -1,
1690                       ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1691                       TabStripModel::ADD_ACTIVE);
1692
1693  WebContents* page_b_contents = CreateWebContents();
1694  WebContents* page_c_contents = CreateWebContents();
1695  WebContents* page_d_contents = CreateWebContents();
1696  strip.AddWebContents(page_b_contents, -1, ui::PAGE_TRANSITION_LINK,
1697                       TabStripModel::ADD_NONE);
1698  strip.AddWebContents(page_c_contents, -1, ui::PAGE_TRANSITION_LINK,
1699                       TabStripModel::ADD_NONE);
1700  strip.AddWebContents(page_d_contents, -1, ui::PAGE_TRANSITION_LINK,
1701                       TabStripModel::ADD_NONE);
1702
1703  strip.ActivateTabAt(2, true);
1704
1705  // TEST 1: If the user is in a group of tabs and opens a new tab at the end
1706  // of the strip, closing that new tab will select the tab that they were
1707  // last on.
1708
1709  // Now simulate opening a new tab at the end of the TabStrip.
1710  WebContents* new_contents1 = CreateWebContents();
1711  strip.AddWebContents(new_contents1, -1, ui::PAGE_TRANSITION_TYPED,
1712                       TabStripModel::ADD_ACTIVE);
1713
1714  // At this point, if we close this tab the last selected one should be
1715  // re-selected.
1716  strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1717  EXPECT_EQ(page_c_contents, strip.GetWebContentsAt(strip.active_index()));
1718
1719  // TEST 2: If the user is in a group of tabs and opens a new tab at the end
1720  // of the strip, selecting any other tab in the strip will cause that new
1721  // tab's opener relationship to be forgotten.
1722
1723  // Open a new tab again.
1724  WebContents* new_contents2 = CreateWebContents();
1725  strip.AddWebContents(new_contents2, -1, ui::PAGE_TRANSITION_TYPED,
1726                       TabStripModel::ADD_ACTIVE);
1727
1728  // Now select the first tab.
1729  strip.ActivateTabAt(0, true);
1730
1731  // Now select the last tab.
1732  strip.ActivateTabAt(strip.count() - 1, true);
1733
1734  // Now close the last tab. The next adjacent should be selected.
1735  strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1736  EXPECT_EQ(page_d_contents, strip.GetWebContentsAt(strip.active_index()));
1737
1738  strip.CloseAllTabs();
1739}
1740
1741// This fails on Linux when run with the rest of unit_tests (crbug.com/302156)
1742// and fails consistently on Mac and Windows.
1743#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
1744#define MAYBE_FastShutdown \
1745    DISABLED_FastShutdown
1746#else
1747#define MAYBE_FastShutdown \
1748    FastShutdown
1749#endif
1750// Tests that fast shutdown is attempted appropriately.
1751TEST_F(TabStripModelTest, MAYBE_FastShutdown) {
1752  TabStripDummyDelegate delegate;
1753  TabStripModel tabstrip(&delegate, profile());
1754  MockTabStripModelObserver observer(&tabstrip);
1755  tabstrip.AddObserver(&observer);
1756
1757  EXPECT_TRUE(tabstrip.empty());
1758
1759  // Make sure fast shutdown is attempted when tabs that share a RPH are shut
1760  // down.
1761  {
1762    WebContents* contents1 = CreateWebContents();
1763    WebContents* contents2 = CreateWebContentsWithSharedRPH(contents1);
1764
1765    SetID(contents1, 1);
1766    SetID(contents2, 2);
1767
1768    tabstrip.AppendWebContents(contents1, true);
1769    tabstrip.AppendWebContents(contents2, true);
1770
1771    // Turn on the fake unload listener so the tabs don't actually get shut
1772    // down when we call CloseAllTabs()---we need to be able to check that
1773    // fast shutdown was attempted.
1774    delegate.set_run_unload_listener(true);
1775    tabstrip.CloseAllTabs();
1776    // On a mock RPH this checks whether we *attempted* fast shutdown.
1777    // A real RPH would reject our attempt since there is an unload handler.
1778    EXPECT_TRUE(contents1->GetRenderProcessHost()->FastShutdownStarted());
1779    EXPECT_EQ(2, tabstrip.count());
1780
1781    delegate.set_run_unload_listener(false);
1782    tabstrip.CloseAllTabs();
1783    EXPECT_TRUE(tabstrip.empty());
1784  }
1785
1786  // Make sure fast shutdown is not attempted when only some tabs that share a
1787  // RPH are shut down.
1788  {
1789    WebContents* contents1 = CreateWebContents();
1790    WebContents* contents2 = CreateWebContentsWithSharedRPH(contents1);
1791
1792    SetID(contents1, 1);
1793    SetID(contents2, 2);
1794
1795    tabstrip.AppendWebContents(contents1, true);
1796    tabstrip.AppendWebContents(contents2, true);
1797
1798    tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
1799    EXPECT_FALSE(contents1->GetRenderProcessHost()->FastShutdownStarted());
1800    EXPECT_EQ(1, tabstrip.count());
1801
1802    tabstrip.CloseAllTabs();
1803    EXPECT_TRUE(tabstrip.empty());
1804  }
1805}
1806
1807// Tests various permutations of apps.
1808TEST_F(TabStripModelTest, Apps) {
1809  TabStripDummyDelegate delegate;
1810  TabStripModel tabstrip(&delegate, profile());
1811  MockTabStripModelObserver observer(&tabstrip);
1812  tabstrip.AddObserver(&observer);
1813
1814  EXPECT_TRUE(tabstrip.empty());
1815
1816  typedef MockTabStripModelObserver::State State;
1817
1818#if defined(OS_WIN)
1819  base::FilePath path(FILE_PATH_LITERAL("c:\\foo"));
1820#elif defined(OS_POSIX)
1821  base::FilePath path(FILE_PATH_LITERAL("/foo"));
1822#endif
1823
1824  base::DictionaryValue manifest;
1825  manifest.SetString("name", "hi!");
1826  manifest.SetString("version", "1");
1827  manifest.SetString("app.launch.web_url", "http://www.google.com");
1828  std::string error;
1829  scoped_refptr<Extension> extension_app(
1830      Extension::Create(path, extensions::Manifest::INVALID_LOCATION,
1831                        manifest, Extension::NO_FLAGS, &error));
1832  WebContents* contents1 = CreateWebContents();
1833  extensions::TabHelper::CreateForWebContents(contents1);
1834  extensions::TabHelper::FromWebContents(contents1)
1835      ->SetExtensionApp(extension_app.get());
1836  WebContents* contents2 = CreateWebContents();
1837  extensions::TabHelper::CreateForWebContents(contents2);
1838  extensions::TabHelper::FromWebContents(contents2)
1839      ->SetExtensionApp(extension_app.get());
1840  WebContents* contents3 = CreateWebContents();
1841
1842  SetID(contents1, 1);
1843  SetID(contents2, 2);
1844  SetID(contents3, 3);
1845
1846  // Note! The ordering of these tests is important, each subsequent test
1847  // builds on the state established in the previous. This is important if you
1848  // ever insert tests rather than append.
1849
1850  // Initial state, tab3 only and selected.
1851  tabstrip.AppendWebContents(contents3, true);
1852
1853  observer.ClearStates();
1854
1855  // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal
1856  // position and tab1 should end up at position 0.
1857  {
1858    tabstrip.InsertWebContentsAt(1, contents1, TabStripModel::ADD_NONE);
1859
1860    ASSERT_EQ(1, observer.GetStateCount());
1861    State state(contents1, 0, MockTabStripModelObserver::INSERT);
1862    EXPECT_TRUE(observer.StateEquals(0, state));
1863
1864    // And verify the state.
1865    EXPECT_EQ("1ap 3", GetTabStripStateString(tabstrip));
1866
1867    observer.ClearStates();
1868  }
1869
1870  // Insert tab 2 at position 1.
1871  {
1872    tabstrip.InsertWebContentsAt(1, contents2, TabStripModel::ADD_NONE);
1873
1874    ASSERT_EQ(1, observer.GetStateCount());
1875    State state(contents2, 1, MockTabStripModelObserver::INSERT);
1876    EXPECT_TRUE(observer.StateEquals(0, state));
1877
1878    // And verify the state.
1879    EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1880
1881    observer.ClearStates();
1882  }
1883
1884  // Try to move tab 3 to position 0. This isn't legal and should be ignored.
1885  {
1886    tabstrip.MoveWebContentsAt(2, 0, false);
1887
1888    ASSERT_EQ(0, observer.GetStateCount());
1889
1890    // And verify the state didn't change.
1891    EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1892
1893    observer.ClearStates();
1894  }
1895
1896  // Try to move tab 0 to position 3. This isn't legal and should be ignored.
1897  {
1898    tabstrip.MoveWebContentsAt(0, 2, false);
1899
1900    ASSERT_EQ(0, observer.GetStateCount());
1901
1902    // And verify the state didn't change.
1903    EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1904
1905    observer.ClearStates();
1906  }
1907
1908  // Try to move tab 0 to position 1. This is a legal move.
1909  {
1910    tabstrip.MoveWebContentsAt(0, 1, false);
1911
1912    ASSERT_EQ(1, observer.GetStateCount());
1913    State state(contents1, 1, MockTabStripModelObserver::MOVE);
1914    state.src_index = 0;
1915    EXPECT_TRUE(observer.StateEquals(0, state));
1916
1917    // And verify the state didn't change.
1918    EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip));
1919
1920    observer.ClearStates();
1921  }
1922
1923  // Remove tab3 and insert at position 0. It should be forced to position 2.
1924  {
1925    tabstrip.DetachWebContentsAt(2);
1926    observer.ClearStates();
1927
1928    tabstrip.InsertWebContentsAt(0, contents3, TabStripModel::ADD_NONE);
1929
1930    ASSERT_EQ(1, observer.GetStateCount());
1931    State state(contents3, 2, MockTabStripModelObserver::INSERT);
1932    EXPECT_TRUE(observer.StateEquals(0, state));
1933
1934    // And verify the state didn't change.
1935    EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip));
1936
1937    observer.ClearStates();
1938  }
1939
1940  tabstrip.CloseAllTabs();
1941}
1942
1943// Tests various permutations of pinning tabs.
1944TEST_F(TabStripModelTest, Pinning) {
1945  TabStripDummyDelegate delegate;
1946  TabStripModel tabstrip(&delegate, profile());
1947  MockTabStripModelObserver observer(&tabstrip);
1948  tabstrip.AddObserver(&observer);
1949
1950  EXPECT_TRUE(tabstrip.empty());
1951
1952  typedef MockTabStripModelObserver::State State;
1953
1954  WebContents* contents1 = CreateWebContents();
1955  WebContents* contents2 = CreateWebContents();
1956  WebContents* contents3 = CreateWebContents();
1957
1958  SetID(contents1, 1);
1959  SetID(contents2, 2);
1960  SetID(contents3, 3);
1961
1962  // Note! The ordering of these tests is important, each subsequent test
1963  // builds on the state established in the previous. This is important if you
1964  // ever insert tests rather than append.
1965
1966  // Initial state, three tabs, first selected.
1967  tabstrip.AppendWebContents(contents1, true);
1968  tabstrip.AppendWebContents(contents2, false);
1969  tabstrip.AppendWebContents(contents3, false);
1970
1971  observer.ClearStates();
1972
1973  // Pin the first tab, this shouldn't visually reorder anything.
1974  {
1975    tabstrip.SetTabPinned(0, true);
1976
1977    // As the order didn't change, we should get a pinned notification.
1978    ASSERT_EQ(1, observer.GetStateCount());
1979    State state(contents1, 0, MockTabStripModelObserver::PINNED);
1980    EXPECT_TRUE(observer.StateEquals(0, state));
1981
1982    // And verify the state.
1983    EXPECT_EQ("1p 2 3", GetTabStripStateString(tabstrip));
1984
1985    observer.ClearStates();
1986  }
1987
1988  // Unpin the first tab.
1989  {
1990    tabstrip.SetTabPinned(0, false);
1991
1992    // As the order didn't change, we should get a pinned notification.
1993    ASSERT_EQ(1, observer.GetStateCount());
1994    State state(contents1, 0, MockTabStripModelObserver::PINNED);
1995    EXPECT_TRUE(observer.StateEquals(0, state));
1996
1997    // And verify the state.
1998    EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
1999
2000    observer.ClearStates();
2001  }
2002
2003  // Pin the 3rd tab, which should move it to the front.
2004  {
2005    tabstrip.SetTabPinned(2, true);
2006
2007    // The pinning should have resulted in a move and a pinned notification.
2008    ASSERT_EQ(2, observer.GetStateCount());
2009    State state(contents3, 0, MockTabStripModelObserver::MOVE);
2010    state.src_index = 2;
2011    EXPECT_TRUE(observer.StateEquals(0, state));
2012
2013    state = State(contents3, 0, MockTabStripModelObserver::PINNED);
2014    EXPECT_TRUE(observer.StateEquals(1, state));
2015
2016    // And verify the state.
2017    EXPECT_EQ("3p 1 2", GetTabStripStateString(tabstrip));
2018
2019    observer.ClearStates();
2020  }
2021
2022  // Pin the tab "1", which shouldn't move anything.
2023  {
2024    tabstrip.SetTabPinned(1, true);
2025
2026    // As the order didn't change, we should get a pinned notification.
2027    ASSERT_EQ(1, observer.GetStateCount());
2028    State state(contents1, 1, MockTabStripModelObserver::PINNED);
2029    EXPECT_TRUE(observer.StateEquals(0, state));
2030
2031    // And verify the state.
2032    EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip));
2033
2034    observer.ClearStates();
2035  }
2036
2037  // Try to move tab "2" to the front, it should be ignored.
2038  {
2039    tabstrip.MoveWebContentsAt(2, 0, false);
2040
2041    // As the order didn't change, we should get a pinned notification.
2042    ASSERT_EQ(0, observer.GetStateCount());
2043
2044    // And verify the state.
2045    EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip));
2046
2047    observer.ClearStates();
2048  }
2049
2050  // Unpin tab "3", which implicitly moves it to the end.
2051  {
2052    tabstrip.SetTabPinned(0, false);
2053
2054    ASSERT_EQ(2, observer.GetStateCount());
2055    State state(contents3, 1, MockTabStripModelObserver::MOVE);
2056    state.src_index = 0;
2057    EXPECT_TRUE(observer.StateEquals(0, state));
2058
2059    state = State(contents3, 1, MockTabStripModelObserver::PINNED);
2060    EXPECT_TRUE(observer.StateEquals(1, state));
2061
2062    // And verify the state.
2063    EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip));
2064
2065    observer.ClearStates();
2066  }
2067
2068  // Unpin tab "3", nothing should happen.
2069  {
2070    tabstrip.SetTabPinned(1, false);
2071
2072    ASSERT_EQ(0, observer.GetStateCount());
2073
2074    EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip));
2075
2076    observer.ClearStates();
2077  }
2078
2079  // Pin "3" and "1".
2080  {
2081    tabstrip.SetTabPinned(0, true);
2082    tabstrip.SetTabPinned(1, true);
2083
2084    EXPECT_EQ("1p 3p 2", GetTabStripStateString(tabstrip));
2085
2086    observer.ClearStates();
2087  }
2088
2089  WebContents* contents4 = CreateWebContents();
2090  SetID(contents4, 4);
2091
2092  // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end
2093  // up after them.
2094  {
2095    tabstrip.InsertWebContentsAt(1, contents4, TabStripModel::ADD_NONE);
2096
2097    ASSERT_EQ(1, observer.GetStateCount());
2098    State state(contents4, 2, MockTabStripModelObserver::INSERT);
2099    EXPECT_TRUE(observer.StateEquals(0, state));
2100
2101    EXPECT_EQ("1p 3p 4 2", GetTabStripStateString(tabstrip));
2102  }
2103
2104  tabstrip.CloseAllTabs();
2105}
2106
2107// Makes sure the TabStripModel calls the right observer methods during a
2108// replace.
2109TEST_F(TabStripModelTest, ReplaceSendsSelected) {
2110  typedef MockTabStripModelObserver::State State;
2111
2112  TabStripDummyDelegate delegate;
2113  TabStripModel strip(&delegate, profile());
2114
2115  WebContents* first_contents = CreateWebContents();
2116  strip.AddWebContents(first_contents, -1, ui::PAGE_TRANSITION_TYPED,
2117                       TabStripModel::ADD_ACTIVE);
2118
2119  MockTabStripModelObserver tabstrip_observer(&strip);
2120  strip.AddObserver(&tabstrip_observer);
2121
2122  WebContents* new_contents = CreateWebContents();
2123  delete strip.ReplaceWebContentsAt(0, new_contents);
2124
2125  ASSERT_EQ(2, tabstrip_observer.GetStateCount());
2126
2127  // First event should be for replaced.
2128  State state(new_contents, 0, MockTabStripModelObserver::REPLACED);
2129  state.src_contents = first_contents;
2130  EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
2131
2132  // And the second for selected.
2133  state = State(new_contents, 0, MockTabStripModelObserver::ACTIVATE);
2134  state.src_contents = first_contents;
2135  state.change_reason = TabStripModelObserver::CHANGE_REASON_REPLACED;
2136  EXPECT_TRUE(tabstrip_observer.StateEquals(1, state));
2137
2138  // Now add another tab and replace it, making sure we don't get a selected
2139  // event this time.
2140  WebContents* third_contents = CreateWebContents();
2141  strip.AddWebContents(third_contents, 1, ui::PAGE_TRANSITION_TYPED,
2142                       TabStripModel::ADD_NONE);
2143
2144  tabstrip_observer.ClearStates();
2145
2146  // And replace it.
2147  new_contents = CreateWebContents();
2148  delete strip.ReplaceWebContentsAt(1, new_contents);
2149
2150  ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2151
2152  state = State(new_contents, 1, MockTabStripModelObserver::REPLACED);
2153  state.src_contents = third_contents;
2154  EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
2155
2156  strip.CloseAllTabs();
2157}
2158
2159// Ensures discarding tabs leaves TabStripModel in a good state.
2160TEST_F(TabStripModelTest, DiscardWebContentsAt) {
2161  typedef MockTabStripModelObserver::State State;
2162
2163  TabStripDummyDelegate delegate;
2164  TabStripModel tabstrip(&delegate, profile());
2165
2166  // Fill it with some tabs.
2167  WebContents* contents1 = CreateWebContents();
2168  tabstrip.AppendWebContents(contents1, true);
2169  WebContents* contents2 = CreateWebContents();
2170  tabstrip.AppendWebContents(contents2, true);
2171
2172  // Start watching for events after the appends to avoid observing state
2173  // transitions that aren't relevant to this test.
2174  MockTabStripModelObserver tabstrip_observer(&tabstrip);
2175  tabstrip.AddObserver(&tabstrip_observer);
2176
2177  // Discard one of the tabs.
2178  WebContents* null_contents1 = tabstrip.DiscardWebContentsAt(0);
2179  ASSERT_EQ(2, tabstrip.count());
2180  EXPECT_TRUE(tabstrip.IsTabDiscarded(0));
2181  EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2182  ASSERT_EQ(null_contents1, tabstrip.GetWebContentsAt(0));
2183  ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1));
2184  ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2185  State state1(null_contents1, 0, MockTabStripModelObserver::REPLACED);
2186  state1.src_contents = contents1;
2187  EXPECT_TRUE(tabstrip_observer.StateEquals(0, state1));
2188  tabstrip_observer.ClearStates();
2189
2190  // Discard the same tab again.
2191  WebContents* null_contents2 = tabstrip.DiscardWebContentsAt(0);
2192  ASSERT_EQ(2, tabstrip.count());
2193  EXPECT_TRUE(tabstrip.IsTabDiscarded(0));
2194  EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2195  ASSERT_EQ(null_contents2, tabstrip.GetWebContentsAt(0));
2196  ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1));
2197  ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2198  State state2(null_contents2, 0, MockTabStripModelObserver::REPLACED);
2199  state2.src_contents = null_contents1;
2200  EXPECT_TRUE(tabstrip_observer.StateEquals(0, state2));
2201  tabstrip_observer.ClearStates();
2202
2203  // Activating the tab should clear its discard state.
2204  tabstrip.ActivateTabAt(0, true /* user_gesture */);
2205  ASSERT_EQ(2, tabstrip.count());
2206  EXPECT_FALSE(tabstrip.IsTabDiscarded(0));
2207  EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2208
2209  // Don't discard active tab.
2210  tabstrip.DiscardWebContentsAt(0);
2211  ASSERT_EQ(2, tabstrip.count());
2212  EXPECT_FALSE(tabstrip.IsTabDiscarded(0));
2213  EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2214
2215  tabstrip.CloseAllTabs();
2216}
2217
2218// Makes sure TabStripModel handles the case of deleting a tab while removing
2219// another tab.
2220TEST_F(TabStripModelTest, DeleteFromDestroy) {
2221  TabStripDummyDelegate delegate;
2222  TabStripModel strip(&delegate, profile());
2223  WebContents* contents1 = CreateWebContents();
2224  WebContents* contents2 = CreateWebContents();
2225  MockTabStripModelObserver tab_strip_model_observer(&strip);
2226  strip.AppendWebContents(contents1, true);
2227  strip.AppendWebContents(contents2, true);
2228  // DeleteWebContentsOnDestroyedObserver deletes contents1 when contents2 sends
2229  // out notification that it is being destroyed.
2230  DeleteWebContentsOnDestroyedObserver observer(contents2, contents1, NULL);
2231  strip.AddObserver(&tab_strip_model_observer);
2232  strip.CloseAllTabs();
2233
2234  int close_all_count = 0, close_all_canceled_count = 0;
2235  tab_strip_model_observer.GetCloseCounts(&close_all_count,
2236                                          &close_all_canceled_count);
2237  EXPECT_EQ(1, close_all_count);
2238  EXPECT_EQ(0, close_all_canceled_count);
2239
2240  strip.RemoveObserver(&tab_strip_model_observer);
2241}
2242
2243// Makes sure TabStripModel handles the case of deleting another tab and the
2244// TabStrip while removing another tab.
2245TEST_F(TabStripModelTest, DeleteTabStripFromDestroy) {
2246  TabStripDummyDelegate delegate;
2247  TabStripModel* strip = new TabStripModel(&delegate, profile());
2248  MockTabStripModelObserver tab_strip_model_observer(strip);
2249  strip->AddObserver(&tab_strip_model_observer);
2250  WebContents* contents1 = CreateWebContents();
2251  WebContents* contents2 = CreateWebContents();
2252  strip->AppendWebContents(contents1, true);
2253  strip->AppendWebContents(contents2, true);
2254  // DeleteWebContentsOnDestroyedObserver deletes |contents1| and |strip| when
2255  // |contents2| sends out notification that it is being destroyed.
2256  DeleteWebContentsOnDestroyedObserver observer(contents2, contents1, strip);
2257  strip->CloseAllTabs();
2258  EXPECT_TRUE(tab_strip_model_observer.empty());
2259  EXPECT_TRUE(tab_strip_model_observer.deleted());
2260}
2261
2262TEST_F(TabStripModelTest, MoveSelectedTabsTo) {
2263  struct TestData {
2264    // Number of tabs the tab strip should have.
2265    const int tab_count;
2266
2267    // Number of pinned tabs.
2268    const int pinned_count;
2269
2270    // Index of the tabs to select.
2271    const std::string selected_tabs;
2272
2273    // Index to move the tabs to.
2274    const int target_index;
2275
2276    // Expected state after the move (space separated list of indices).
2277    const std::string state_after_move;
2278  } test_data[] = {
2279    // 1 selected tab.
2280    { 2, 0, "0", 1, "1 0" },
2281    { 3, 0, "0", 2, "1 2 0" },
2282    { 3, 0, "2", 0, "2 0 1" },
2283    { 3, 0, "2", 1, "0 2 1" },
2284    { 3, 0, "0 1", 0, "0 1 2" },
2285
2286    // 2 selected tabs.
2287    { 6, 0, "4 5", 1, "0 4 5 1 2 3" },
2288    { 3, 0, "0 1", 1, "2 0 1" },
2289    { 4, 0, "0 2", 1, "1 0 2 3" },
2290    { 6, 0, "0 1", 3, "2 3 4 0 1 5" },
2291
2292    // 3 selected tabs.
2293    { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" },
2294    { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" },
2295    { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" },
2296
2297    // 5 selected tabs.
2298    { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" },
2299
2300    // 7 selected tabs
2301    { 16, 0, "0 1 2 3 4 7 9", 8, "5 6 8 10 11 12 13 14 0 1 2 3 4 7 9 15" },
2302
2303    // With pinned tabs.
2304    { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" },
2305    { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" },
2306    { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" },
2307    { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" },
2308
2309    { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" },
2310  };
2311
2312  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
2313    TabStripDummyDelegate delegate;
2314    TabStripModel strip(&delegate, profile());
2315    ASSERT_NO_FATAL_FAILURE(
2316        PrepareTabstripForSelectionTest(&strip, test_data[i].tab_count,
2317                                        test_data[i].pinned_count,
2318                                        test_data[i].selected_tabs));
2319    strip.MoveSelectedTabsTo(test_data[i].target_index);
2320    EXPECT_EQ(test_data[i].state_after_move,
2321              GetTabStripStateString(strip)) << i;
2322    strip.CloseAllTabs();
2323  }
2324}
2325
2326// Tests that moving a tab forgets all groups referencing it.
2327TEST_F(TabStripModelTest, MoveSelectedTabsTo_ForgetGroups) {
2328  TabStripDummyDelegate delegate;
2329  TabStripModel strip(&delegate, profile());
2330
2331  // Open page A as a new tab and then A1 in the background from A.
2332  WebContents* page_a_contents = CreateWebContents();
2333  strip.AddWebContents(page_a_contents, -1,
2334                       ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
2335                       TabStripModel::ADD_ACTIVE);
2336  WebContents* page_a1_contents = CreateWebContents();
2337  strip.AddWebContents(page_a1_contents, -1, ui::PAGE_TRANSITION_LINK,
2338                       TabStripModel::ADD_NONE);
2339
2340  // Likewise, open pages B and B1.
2341  WebContents* page_b_contents = CreateWebContents();
2342  strip.AddWebContents(page_b_contents, -1,
2343                       ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
2344                       TabStripModel::ADD_ACTIVE);
2345  WebContents* page_b1_contents = CreateWebContents();
2346  strip.AddWebContents(page_b1_contents, -1, ui::PAGE_TRANSITION_LINK,
2347                       TabStripModel::ADD_NONE);
2348
2349  EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(0));
2350  EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(1));
2351  EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(2));
2352  EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(3));
2353
2354  // Move page B to the start of the tab strip.
2355  strip.MoveSelectedTabsTo(0);
2356
2357  // Open page B2 in the background from B. It should end up after B.
2358  WebContents* page_b2_contents = CreateWebContents();
2359  strip.AddWebContents(page_b2_contents, -1, ui::PAGE_TRANSITION_LINK,
2360                       TabStripModel::ADD_NONE);
2361  EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(0));
2362  EXPECT_EQ(page_b2_contents, strip.GetWebContentsAt(1));
2363  EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(2));
2364  EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(3));
2365  EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(4));
2366
2367  // Switch to A.
2368  strip.ActivateTabAt(2, true);
2369  EXPECT_EQ(page_a_contents, strip.GetActiveWebContents());
2370
2371  // Open page A2 in the background from A. It should end up after A1.
2372  WebContents* page_a2_contents = CreateWebContents();
2373  strip.AddWebContents(page_a2_contents, -1, ui::PAGE_TRANSITION_LINK,
2374                       TabStripModel::ADD_NONE);
2375  EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(0));
2376  EXPECT_EQ(page_b2_contents, strip.GetWebContentsAt(1));
2377  EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(2));
2378  EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(3));
2379  EXPECT_EQ(page_a2_contents, strip.GetWebContentsAt(4));
2380  EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(5));
2381
2382  strip.CloseAllTabs();
2383}
2384
2385TEST_F(TabStripModelTest, CloseSelectedTabs) {
2386  TabStripDummyDelegate delegate;
2387  TabStripModel strip(&delegate, profile());
2388  WebContents* contents1 = CreateWebContents();
2389  WebContents* contents2 = CreateWebContents();
2390  WebContents* contents3 = CreateWebContents();
2391  strip.AppendWebContents(contents1, true);
2392  strip.AppendWebContents(contents2, true);
2393  strip.AppendWebContents(contents3, true);
2394  strip.ToggleSelectionAt(1);
2395  strip.CloseSelectedTabs();
2396  EXPECT_EQ(1, strip.count());
2397  EXPECT_EQ(0, strip.active_index());
2398  strip.CloseAllTabs();
2399}
2400
2401TEST_F(TabStripModelTest, MultipleSelection) {
2402  typedef MockTabStripModelObserver::State State;
2403
2404  TabStripDummyDelegate delegate;
2405  TabStripModel strip(&delegate, profile());
2406  MockTabStripModelObserver observer(&strip);
2407  WebContents* contents0 = CreateWebContents();
2408  WebContents* contents1 = CreateWebContents();
2409  WebContents* contents2 = CreateWebContents();
2410  WebContents* contents3 = CreateWebContents();
2411  strip.AppendWebContents(contents0, false);
2412  strip.AppendWebContents(contents1, false);
2413  strip.AppendWebContents(contents2, false);
2414  strip.AppendWebContents(contents3, false);
2415  strip.AddObserver(&observer);
2416
2417  // Selection and active tab change.
2418  strip.ActivateTabAt(3, true);
2419  ASSERT_EQ(2, observer.GetStateCount());
2420  ASSERT_EQ(observer.GetStateAt(0).action,
2421            MockTabStripModelObserver::ACTIVATE);
2422  State s1(contents3, 3, MockTabStripModelObserver::SELECT);
2423  EXPECT_TRUE(observer.StateEquals(1, s1));
2424  observer.ClearStates();
2425
2426  // Adding all tabs to selection, active tab is now at 0.
2427  strip.ExtendSelectionTo(0);
2428  ASSERT_EQ(3, observer.GetStateCount());
2429  ASSERT_EQ(observer.GetStateAt(0).action,
2430            MockTabStripModelObserver::DEACTIVATE);
2431  ASSERT_EQ(observer.GetStateAt(1).action,
2432            MockTabStripModelObserver::ACTIVATE);
2433  State s2(contents0, 0, MockTabStripModelObserver::SELECT);
2434  s2.src_contents = contents3;
2435  s2.src_index = 3;
2436  EXPECT_TRUE(observer.StateEquals(2, s2));
2437  observer.ClearStates();
2438
2439  // Toggle the active tab, should make the next index active.
2440  strip.ToggleSelectionAt(0);
2441  EXPECT_EQ(1, strip.active_index());
2442  EXPECT_EQ(3U, strip.selection_model().size());
2443  EXPECT_EQ(4, strip.count());
2444  ASSERT_EQ(3, observer.GetStateCount());
2445  ASSERT_EQ(observer.GetStateAt(0).action,
2446            MockTabStripModelObserver::DEACTIVATE);
2447  ASSERT_EQ(observer.GetStateAt(1).action,
2448            MockTabStripModelObserver::ACTIVATE);
2449  ASSERT_EQ(observer.GetStateAt(2).action,
2450            MockTabStripModelObserver::SELECT);
2451  observer.ClearStates();
2452
2453  // Toggle the first tab back to selected and active.
2454  strip.ToggleSelectionAt(0);
2455  EXPECT_EQ(0, strip.active_index());
2456  EXPECT_EQ(4U, strip.selection_model().size());
2457  EXPECT_EQ(4, strip.count());
2458  ASSERT_EQ(3, observer.GetStateCount());
2459  ASSERT_EQ(observer.GetStateAt(0).action,
2460            MockTabStripModelObserver::DEACTIVATE);
2461  ASSERT_EQ(observer.GetStateAt(1).action,
2462            MockTabStripModelObserver::ACTIVATE);
2463  ASSERT_EQ(observer.GetStateAt(2).action,
2464            MockTabStripModelObserver::SELECT);
2465  observer.ClearStates();
2466
2467  // Closing one of the selected tabs, not the active one.
2468  strip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
2469  EXPECT_EQ(3, strip.count());
2470  ASSERT_EQ(3, observer.GetStateCount());
2471  ASSERT_EQ(observer.GetStateAt(0).action,
2472            MockTabStripModelObserver::CLOSE);
2473  ASSERT_EQ(observer.GetStateAt(1).action,
2474            MockTabStripModelObserver::DETACH);
2475  ASSERT_EQ(observer.GetStateAt(2).action,
2476            MockTabStripModelObserver::SELECT);
2477  observer.ClearStates();
2478
2479  // Closing the active tab, while there are others tabs selected.
2480  strip.CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
2481  EXPECT_EQ(2, strip.count());
2482  ASSERT_EQ(5, observer.GetStateCount());
2483  ASSERT_EQ(observer.GetStateAt(0).action,
2484            MockTabStripModelObserver::CLOSE);
2485  ASSERT_EQ(observer.GetStateAt(1).action,
2486            MockTabStripModelObserver::DETACH);
2487  ASSERT_EQ(observer.GetStateAt(2).action,
2488            MockTabStripModelObserver::DEACTIVATE);
2489  ASSERT_EQ(observer.GetStateAt(3).action,
2490            MockTabStripModelObserver::ACTIVATE);
2491  ASSERT_EQ(observer.GetStateAt(4).action,
2492            MockTabStripModelObserver::SELECT);
2493  observer.ClearStates();
2494
2495  // Active tab is at 0, deselecting all but the active tab.
2496  strip.ToggleSelectionAt(1);
2497  ASSERT_EQ(1, observer.GetStateCount());
2498  ASSERT_EQ(observer.GetStateAt(0).action,
2499            MockTabStripModelObserver::SELECT);
2500  observer.ClearStates();
2501
2502  // Attempting to deselect the only selected and therefore active tab,
2503  // it is ignored (no notifications being sent) and tab at 0 remains selected
2504  // and active.
2505  strip.ToggleSelectionAt(0);
2506  ASSERT_EQ(0, observer.GetStateCount());
2507
2508  strip.RemoveObserver(&observer);
2509  strip.CloseAllTabs();
2510}
2511
2512// Verifies that if we change the selection from a multi selection to a single
2513// selection, but not in a way that changes the selected_index that
2514// TabSelectionChanged is invoked.
2515TEST_F(TabStripModelTest, MultipleToSingle) {
2516  typedef MockTabStripModelObserver::State State;
2517
2518  TabStripDummyDelegate delegate;
2519  TabStripModel strip(&delegate, profile());
2520  WebContents* contents1 = CreateWebContents();
2521  WebContents* contents2 = CreateWebContents();
2522  strip.AppendWebContents(contents1, false);
2523  strip.AppendWebContents(contents2, false);
2524  strip.ToggleSelectionAt(0);
2525  strip.ToggleSelectionAt(1);
2526
2527  MockTabStripModelObserver observer(&strip);
2528  strip.AddObserver(&observer);
2529  // This changes the selection (0 is no longer selected) but the selected_index
2530  // still remains at 1.
2531  strip.ActivateTabAt(1, true);
2532  ASSERT_EQ(1, observer.GetStateCount());
2533  State s(contents2, 1, MockTabStripModelObserver::SELECT);
2534  s.src_contents = contents2;
2535  s.src_index = 1;
2536  s.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
2537  EXPECT_TRUE(observer.StateEquals(0, s));
2538  strip.RemoveObserver(&observer);
2539  strip.CloseAllTabs();
2540}
2541
2542// Verifies a newly inserted tab retains its previous blocked state.
2543// http://crbug.com/276334
2544TEST_F(TabStripModelTest, TabBlockedState) {
2545  // Start with a source tab strip.
2546  TabStripDummyDelegate dummy_tab_strip_delegate;
2547  TabStripModel strip_src(&dummy_tab_strip_delegate, profile());
2548  TabBlockedStateTestBrowser browser_src(&strip_src);
2549
2550  // Add a tab.
2551  WebContents* contents1 = CreateWebContents();
2552  web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1);
2553  strip_src.AppendWebContents(contents1, false);
2554
2555  // Add another tab.
2556  WebContents* contents2 = CreateWebContents();
2557  web_modal::WebContentsModalDialogManager::CreateForWebContents(contents2);
2558  strip_src.AppendWebContents(contents2, false);
2559
2560  // Create a destination tab strip.
2561  TabStripModel strip_dst(&dummy_tab_strip_delegate, profile());
2562  TabBlockedStateTestBrowser browser_dst(&strip_dst);
2563
2564  // Setup a SingleWebContentsDialogManager for tab |contents2|.
2565  web_modal::WebContentsModalDialogManager* modal_dialog_manager =
2566      web_modal::WebContentsModalDialogManager::FromWebContents(contents2);
2567  web_modal::PopupManager popup_manager(NULL);
2568  popup_manager.RegisterWith(contents2);
2569
2570  // Show a dialog that blocks tab |contents2|.
2571  // DummySingleWebContentsDialogManager doesn't care about the
2572  // NativeWebContentsModalDialog value, so any dummy value works.
2573  DummySingleWebContentsDialogManager* native_manager =
2574      new DummySingleWebContentsDialogManager(
2575          reinterpret_cast<NativeWebContentsModalDialog>(0),
2576          modal_dialog_manager);
2577  modal_dialog_manager->ShowDialogWithManager(
2578      reinterpret_cast<NativeWebContentsModalDialog>(0),
2579      scoped_ptr<web_modal::SingleWebContentsDialogManager>(
2580          native_manager).Pass());
2581  EXPECT_TRUE(strip_src.IsTabBlocked(1));
2582
2583  // Detach the tab.
2584  WebContents* moved_contents = strip_src.DetachWebContentsAt(1);
2585  EXPECT_EQ(contents2, moved_contents);
2586
2587  // Attach the tab to the destination tab strip.
2588  strip_dst.AppendWebContents(moved_contents, true);
2589  EXPECT_TRUE(strip_dst.IsTabBlocked(0));
2590
2591  strip_dst.CloseAllTabs();
2592  strip_src.CloseAllTabs();
2593}
2594