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