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