browser_tab_strip_controller.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
1aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root// Use of this source code is governed by a BSD-style license that can be
3aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root// found in the LICENSE file.
4aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
5aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
6aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
7aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "base/auto_reset.h"
8aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "base/command_line.h"
9aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "base/prefs/pref_service.h"
10aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/browser/browser_process.h"
11aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/browser/extensions/tab_helper.h"
12aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/browser/favicon/favicon_tab_helper.h"
13aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/browser/profiles/profile.h"
14aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/browser/ui/browser.h"
15aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/browser/ui/browser_tabstrip.h"
16aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/browser/ui/tabs/tab_menu_model.h"
17860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root#include "chrome/browser/ui/tabs/tab_strip_model.h"
18aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
19aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root#include "chrome/browser/ui/tabs/tab_utils.h"
20aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root#include "chrome/browser/ui/views/frame/browser_view.h"
21aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root#include "chrome/browser/ui/views/tabs/tab.h"
22aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root#include "chrome/browser/ui/views/tabs/tab_renderer_data.h"
23aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/browser/ui/views/tabs/tab_strip.h"
24aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/common/chrome_notification_types.h"
25aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/common/chrome_switches.h"
26aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/common/pref_names.h"
27aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "chrome/common/url_constants.h"
28aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "content/public/browser/notification_service.h"
29aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "content/public/browser/user_metrics.h"
3047cc520bd63c1eabfdef23cbab10457701f2a395Kenny Root#include "content/public/browser/web_contents.h"
31c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root#include "ui/base/layout.h"
32c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root#include "ui/base/models/list_selection_model.h"
33aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root#include "ui/gfx/image/image.h"
34aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "ui/views/controls/menu/menu_item_view.h"
35aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root#include "ui/views/controls/menu/menu_runner.h"
36aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#include "ui/views/widget/widget.h"
37aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
38aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Rootusing content::UserMetricsAction;
39aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Rootusing content::WebContents;
40aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
4147cc520bd63c1eabfdef23cbab10457701f2a395Kenny Rootnamespace {
4247cc520bd63c1eabfdef23cbab10457701f2a395Kenny Root
439df6cea08a4eb527ba5b6fc9275e8b0571c12892Kenny RootTabRendererData::NetworkState TabContentsNetworkState(
449df6cea08a4eb527ba5b6fc9275e8b0571c12892Kenny Root    WebContents* contents) {
459df6cea08a4eb527ba5b6fc9275e8b0571c12892Kenny Root  if (!contents || !contents->IsLoading())
46aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    return TabRendererData::NETWORK_STATE_NONE;
47aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  if (contents->IsWaitingForResponse())
48aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    return TabRendererData::NETWORK_STATE_WAITING;
49aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  return TabRendererData::NETWORK_STATE_LOADING;
50aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root}
51aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
52aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny RootTabStripLayoutType DetermineTabStripLayout(PrefService* prefs,
53aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root                                           bool* adjust_layout) {
54aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  *adjust_layout = false;
55aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  if (CommandLine::ForCurrentProcess()->HasSwitch(
56aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root          switches::kEnableStackedTabStrip)) {
57aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    return TAB_STRIP_LAYOUT_STACKED;
58aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  }
59aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  // For chromeos always allow entering stacked mode.
60aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#if !defined(OS_CHROMEOS)
61aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  if (ui::GetDisplayLayout() != ui::LAYOUT_TOUCH)
62aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    return TAB_STRIP_LAYOUT_SHRINK;
63aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root#endif
64aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  *adjust_layout = true;
65aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  switch (prefs->GetInteger(prefs::kTabStripLayoutType)) {
66aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    case TAB_STRIP_LAYOUT_STACKED:
67aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root      return TAB_STRIP_LAYOUT_STACKED;
68aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    default:
69aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root      return TAB_STRIP_LAYOUT_SHRINK;
70aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  }
71aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root}
72aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
73aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root}  // namespace
74aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
75aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Rootclass BrowserTabStripController::TabContextMenuContents
76aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    : public ui::SimpleMenuModel::Delegate {
77aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root public:
78aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  TabContextMenuContents(Tab* tab,
79aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root                         BrowserTabStripController* controller)
80aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root      : tab_(tab),
81aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root        controller_(controller),
8280f0c28bcba376c34616ed27c7af3e02d4677d37Kenny Root        last_command_(TabStripModel::CommandFirst) {
8380f0c28bcba376c34616ed27c7af3e02d4677d37Kenny Root    model_.reset(new TabMenuModel(
8480f0c28bcba376c34616ed27c7af3e02d4677d37Kenny Root        this, controller->model_,
8580f0c28bcba376c34616ed27c7af3e02d4677d37Kenny Root        controller->tabstrip_->GetModelIndexOfTab(tab)));
8680f0c28bcba376c34616ed27c7af3e02d4677d37Kenny Root    menu_runner_.reset(new views::MenuRunner(model_.get()));
8780f0c28bcba376c34616ed27c7af3e02d4677d37Kenny Root  }
8880f0c28bcba376c34616ed27c7af3e02d4677d37Kenny Root
8980f0c28bcba376c34616ed27c7af3e02d4677d37Kenny Root  virtual ~TabContextMenuContents() {
9080f0c28bcba376c34616ed27c7af3e02d4677d37Kenny Root    if (controller_)
91aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root      controller_->tabstrip_->StopAllHighlighting();
92aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  }
93aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
94aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  void Cancel() {
95aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    controller_ = NULL;
96aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  }
97aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
98aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  void RunMenuAt(const gfx::Point& point, ui::MenuSourceType source_type) {
99aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    if (menu_runner_->RunMenuAt(
100aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root            tab_->GetWidget(), NULL, gfx::Rect(point, gfx::Size()),
101aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root            views::MenuItemView::TOPLEFT, source_type,
102aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root            views::MenuRunner::HAS_MNEMONICS |
103aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root            views::MenuRunner::CONTEXT_MENU) ==
104aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root        views::MenuRunner::MENU_DELETED)
105aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root      return;
106aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  }
107aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
108aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  // Overridden from ui::SimpleMenuModel::Delegate:
109aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
110aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    return false;
111aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  }
112aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root  virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
113aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root    return controller_->IsCommandEnabledForTab(
114c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root        static_cast<TabStripModel::ContextMenuCommand>(command_id),
115c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root        tab_);
116c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  }
117c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  virtual bool GetAcceleratorForCommandId(
118c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root      int command_id,
119c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root      ui::Accelerator* accelerator) OVERRIDE {
120c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root    int browser_cmd;
121c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root    return TabStripModel::ContextMenuCommandToBrowserCommand(command_id,
122c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root                                                             &browser_cmd) ?
123c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root        controller_->tabstrip_->GetWidget()->GetAccelerator(browser_cmd,
124c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root                                                            accelerator) :
125c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root        false;
126c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  }
127c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  virtual void CommandIdHighlighted(int command_id) OVERRIDE {
128c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root    controller_->StopHighlightTabsForCommand(last_command_, tab_);
129c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root    last_command_ = static_cast<TabStripModel::ContextMenuCommand>(command_id);
130c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root    controller_->StartHighlightTabsForCommand(last_command_, tab_);
131c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  }
132c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE {
133c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root    // Executing the command destroys |this|, and can also end up destroying
134c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root    // |controller_|. So stop the highlights before executing the command.
135c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root    controller_->tabstrip_->StopAllHighlighting();
136c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root    controller_->ExecuteCommandForTab(
137c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root        static_cast<TabStripModel::ContextMenuCommand>(command_id),
138c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root        tab_);
139c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  }
140c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root
141c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  virtual void MenuClosed(ui::SimpleMenuModel* /*source*/) OVERRIDE {
142c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root    if (controller_)
143c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root      controller_->tabstrip_->StopAllHighlighting();
144c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  }
145c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root
146c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root private:
147c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  scoped_ptr<TabMenuModel> model_;
148c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  scoped_ptr<views::MenuRunner> menu_runner_;
149c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root
150c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  // The tab we're showing a menu for.
151c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  Tab* tab_;
152c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root
153c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  // A pointer back to our hosting controller, for command state information.
154c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  BrowserTabStripController* controller_;
155c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root
156c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  // The last command that was selected, so that we can start/stop highlighting
157c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  // appropriately as the user moves through the menu.
158c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  TabStripModel::ContextMenuCommand last_command_;
159c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root
160c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root  DISALLOW_COPY_AND_ASSIGN(TabContextMenuContents);
161c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root};
162c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root
163c43ab99bd48be940dde4c8ee74155e0482cd1599Kenny Root////////////////////////////////////////////////////////////////////////////////
164aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root// BrowserTabStripController, public:
165aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root
166aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny RootBrowserTabStripController::BrowserTabStripController(Browser* browser,
167aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root                                                     TabStripModel* model)
168aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root    : model_(model),
169aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root      tabstrip_(NULL),
170aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root      browser_(browser),
171aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root      hover_tab_selector_(model) {
172aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root  model_->AddObserver(this);
173aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root
174aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root  local_pref_registrar_.Init(g_browser_process->local_state());
175aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root  local_pref_registrar_.Add(
176aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root      prefs::kTabStripLayoutType,
177aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root      base::Bind(&BrowserTabStripController::UpdateLayoutType,
178aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root                 base::Unretained(this)));
179aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root}
180aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root
181aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny RootBrowserTabStripController::~BrowserTabStripController() {
182aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root  // When we get here the TabStrip is being deleted. We need to explicitly
183aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root  // cancel the menu, otherwise it may try to invoke something on the tabstrip
184aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root  // from its destructor.
185aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root  if (context_menu_contents_.get())
186aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root    context_menu_contents_->Cancel();
187aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root
188aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root  model_->RemoveObserver(this);
189aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root}
190aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root
191aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Rootvoid BrowserTabStripController::InitFromModel(TabStrip* tabstrip) {
192aceaabe71de0da877be36f5bfb88c7111f81fc3eKenny Root  tabstrip_ = tabstrip;
193aa48fdb4d42bc5de668f11055f88fb430fdf4d61Kenny Root
194  UpdateLayoutType();
195
196  // Walk the model, calling our insertion observer method for each item within
197  // it.
198  for (int i = 0; i < model_->count(); ++i)
199    AddTab(model_->GetWebContentsAt(i), i, model_->active_index() == i);
200}
201
202bool BrowserTabStripController::IsCommandEnabledForTab(
203    TabStripModel::ContextMenuCommand command_id,
204    Tab* tab) const {
205  int model_index = tabstrip_->GetModelIndexOfTab(tab);
206  return model_->ContainsIndex(model_index) ?
207      model_->IsContextMenuCommandEnabled(model_index, command_id) : false;
208}
209
210void BrowserTabStripController::ExecuteCommandForTab(
211    TabStripModel::ContextMenuCommand command_id,
212    Tab* tab) {
213  int model_index = tabstrip_->GetModelIndexOfTab(tab);
214  if (model_->ContainsIndex(model_index))
215    model_->ExecuteContextMenuCommand(model_index, command_id);
216}
217
218bool BrowserTabStripController::IsTabPinned(Tab* tab) const {
219  return IsTabPinned(tabstrip_->GetModelIndexOfTab(tab));
220}
221
222const ui::ListSelectionModel& BrowserTabStripController::GetSelectionModel() {
223  return model_->selection_model();
224}
225
226int BrowserTabStripController::GetCount() const {
227  return model_->count();
228}
229
230bool BrowserTabStripController::IsValidIndex(int index) const {
231  return model_->ContainsIndex(index);
232}
233
234bool BrowserTabStripController::IsActiveTab(int model_index) const {
235  return model_->active_index() == model_index;
236}
237
238int BrowserTabStripController::GetActiveIndex() const {
239  return model_->active_index();
240}
241
242bool BrowserTabStripController::IsTabSelected(int model_index) const {
243  return model_->IsTabSelected(model_index);
244}
245
246bool BrowserTabStripController::IsTabPinned(int model_index) const {
247  return model_->ContainsIndex(model_index) && model_->IsTabPinned(model_index);
248}
249
250bool BrowserTabStripController::IsNewTabPage(int model_index) const {
251  return model_->ContainsIndex(model_index) &&
252      model_->GetWebContentsAt(model_index)->GetURL() ==
253      GURL(chrome::kChromeUINewTabURL);
254}
255
256void BrowserTabStripController::SelectTab(int model_index) {
257  model_->ActivateTabAt(model_index, true);
258}
259
260void BrowserTabStripController::ExtendSelectionTo(int model_index) {
261  model_->ExtendSelectionTo(model_index);
262}
263
264void BrowserTabStripController::ToggleSelected(int model_index) {
265  model_->ToggleSelectionAt(model_index);
266}
267
268void BrowserTabStripController::AddSelectionFromAnchorTo(int model_index) {
269  model_->AddSelectionFromAnchorTo(model_index);
270}
271
272void BrowserTabStripController::CloseTab(int model_index,
273                                         CloseTabSource source) {
274  // Cancel any pending tab transition.
275  hover_tab_selector_.CancelTabTransition();
276
277  tabstrip_->PrepareForCloseAt(model_index, source);
278  model_->CloseWebContentsAt(model_index,
279                             TabStripModel::CLOSE_USER_GESTURE |
280                             TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
281}
282
283void BrowserTabStripController::ShowContextMenuForTab(
284    Tab* tab,
285    const gfx::Point& p,
286    ui::MenuSourceType source_type) {
287  context_menu_contents_.reset(new TabContextMenuContents(tab, this));
288  context_menu_contents_->RunMenuAt(p, source_type);
289}
290
291void BrowserTabStripController::UpdateLoadingAnimations() {
292  // Don't use the model count here as it's possible for this to be invoked
293  // before we've applied an update from the model (Browser::TabInsertedAt may
294  // be processed before us and invokes this).
295  for (int i = 0, tab_count = tabstrip_->tab_count(); i < tab_count; ++i) {
296    if (model_->ContainsIndex(i)) {
297      Tab* tab = tabstrip_->tab_at(i);
298      WebContents* contents = model_->GetWebContentsAt(i);
299      tab->UpdateLoadingAnimation(TabContentsNetworkState(contents));
300    }
301  }
302}
303
304int BrowserTabStripController::HasAvailableDragActions() const {
305  return model_->delegate()->GetDragActions();
306}
307
308void BrowserTabStripController::OnDropIndexUpdate(int index,
309                                                  bool drop_before) {
310  // Perform a delayed tab transition if hovering directly over a tab.
311  // Otherwise, cancel the pending one.
312  if (index != -1 && !drop_before) {
313    hover_tab_selector_.StartTabTransition(index);
314  } else {
315    hover_tab_selector_.CancelTabTransition();
316  }
317}
318
319void BrowserTabStripController::PerformDrop(bool drop_before,
320                                            int index,
321                                            const GURL& url) {
322  chrome::NavigateParams params(browser_, url, content::PAGE_TRANSITION_LINK);
323  params.tabstrip_index = index;
324
325  if (drop_before) {
326    content::RecordAction(UserMetricsAction("Tab_DropURLBetweenTabs"));
327    params.disposition = NEW_FOREGROUND_TAB;
328  } else {
329    content::RecordAction(UserMetricsAction("Tab_DropURLOnTab"));
330    params.disposition = CURRENT_TAB;
331    params.source_contents = model_->GetWebContentsAt(index);
332  }
333  params.window_action = chrome::NavigateParams::SHOW_WINDOW;
334  chrome::Navigate(&params);
335}
336
337bool BrowserTabStripController::IsCompatibleWith(TabStrip* other) const {
338  Profile* other_profile =
339      static_cast<BrowserTabStripController*>(other->controller())->profile();
340  return other_profile == profile();
341}
342
343void BrowserTabStripController::CreateNewTab() {
344  model_->delegate()->AddBlankTabAt(-1, true);
345}
346
347bool BrowserTabStripController::IsIncognito() {
348  return browser_->profile()->IsOffTheRecord();
349}
350
351void BrowserTabStripController::LayoutTypeMaybeChanged() {
352  bool adjust_layout = false;
353  TabStripLayoutType layout_type =
354      DetermineTabStripLayout(g_browser_process->local_state(), &adjust_layout);
355  if (!adjust_layout || layout_type == tabstrip_->layout_type())
356    return;
357
358  g_browser_process->local_state()->SetInteger(
359      prefs::kTabStripLayoutType,
360      static_cast<int>(tabstrip_->layout_type()));
361}
362
363void BrowserTabStripController::OnStartedDraggingTabs() {
364  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
365  if (browser_view && !immersive_reveal_lock_.get()) {
366    // The top-of-window views should be revealed while the user is dragging
367    // tabs in immersive fullscreen. The top-of-window views may not be already
368    // revealed if the user is attempting to attach a tab to a tabstrip
369    // belonging to an immersive fullscreen window.
370    immersive_reveal_lock_.reset(
371        browser_view->immersive_mode_controller()->GetRevealedLock(
372            ImmersiveModeController::ANIMATE_REVEAL_NO));
373  }
374}
375
376void BrowserTabStripController::OnStoppedDraggingTabs() {
377  immersive_reveal_lock_.reset();
378}
379
380////////////////////////////////////////////////////////////////////////////////
381// BrowserTabStripController, TabStripModelObserver implementation:
382
383void BrowserTabStripController::TabInsertedAt(WebContents* contents,
384                                              int model_index,
385                                              bool is_active) {
386  DCHECK(contents);
387  DCHECK(model_->ContainsIndex(model_index));
388  AddTab(contents, model_index, is_active);
389}
390
391void BrowserTabStripController::TabDetachedAt(WebContents* contents,
392                                              int model_index) {
393  // Cancel any pending tab transition.
394  hover_tab_selector_.CancelTabTransition();
395
396  tabstrip_->RemoveTabAt(model_index);
397}
398
399void BrowserTabStripController::TabSelectionChanged(
400    TabStripModel* tab_strip_model,
401    const ui::ListSelectionModel& old_model) {
402  tabstrip_->SetSelection(old_model, model_->selection_model());
403}
404
405void BrowserTabStripController::TabMoved(WebContents* contents,
406                                         int from_model_index,
407                                         int to_model_index) {
408  // Cancel any pending tab transition.
409  hover_tab_selector_.CancelTabTransition();
410
411  // Pass in the TabRendererData as the pinned state may have changed.
412  TabRendererData data;
413  SetTabRendererDataFromModel(contents, to_model_index, &data, EXISTING_TAB);
414  tabstrip_->MoveTab(from_model_index, to_model_index, data);
415}
416
417void BrowserTabStripController::TabChangedAt(WebContents* contents,
418                                             int model_index,
419                                             TabChangeType change_type) {
420  if (change_type == TITLE_NOT_LOADING) {
421    tabstrip_->TabTitleChangedNotLoading(model_index);
422    // We'll receive another notification of the change asynchronously.
423    return;
424  }
425
426  SetTabDataAt(contents, model_index);
427}
428
429void BrowserTabStripController::TabReplacedAt(TabStripModel* tab_strip_model,
430                                              WebContents* old_contents,
431                                              WebContents* new_contents,
432                                              int model_index) {
433  SetTabDataAt(new_contents, model_index);
434}
435
436void BrowserTabStripController::TabPinnedStateChanged(WebContents* contents,
437                                                      int model_index) {
438  // Currently none of the renderers render pinned state differently.
439}
440
441void BrowserTabStripController::TabMiniStateChanged(WebContents* contents,
442                                                    int model_index) {
443  SetTabDataAt(contents, model_index);
444}
445
446void BrowserTabStripController::TabBlockedStateChanged(WebContents* contents,
447                                                       int model_index) {
448  SetTabDataAt(contents, model_index);
449}
450
451void BrowserTabStripController::SetTabRendererDataFromModel(
452    WebContents* contents,
453    int model_index,
454    TabRendererData* data,
455    TabStatus tab_status) {
456  FaviconTabHelper* favicon_tab_helper =
457      FaviconTabHelper::FromWebContents(contents);
458
459  data->favicon = favicon_tab_helper->GetFavicon().AsImageSkia();
460  data->network_state = TabContentsNetworkState(contents);
461  data->title = contents->GetTitle();
462  data->url = contents->GetURL();
463  data->loading = contents->IsLoading();
464  data->crashed_status = contents->GetCrashedStatus();
465  data->incognito = contents->GetBrowserContext()->IsOffTheRecord();
466  data->show_icon = favicon_tab_helper->ShouldDisplayFavicon();
467  data->mini = model_->IsMiniTab(model_index);
468  data->blocked = model_->IsTabBlocked(model_index);
469  data->app = extensions::TabHelper::FromWebContents(contents)->is_app();
470  if (chrome::ShouldShowProjectingIndicator(contents))
471    data->capture_state = TabRendererData::CAPTURE_STATE_PROJECTING;
472  else if (chrome::ShouldShowRecordingIndicator(contents))
473    data->capture_state = TabRendererData::CAPTURE_STATE_RECORDING;
474  else
475    data->capture_state = TabRendererData::CAPTURE_STATE_NONE;
476
477  if (chrome::IsPlayingAudio(contents))
478    data->audio_state = TabRendererData::AUDIO_STATE_PLAYING;
479  else
480    data->audio_state = TabRendererData::AUDIO_STATE_NONE;
481}
482
483void BrowserTabStripController::SetTabDataAt(content::WebContents* web_contents,
484                                             int model_index) {
485  TabRendererData data;
486  SetTabRendererDataFromModel(web_contents, model_index, &data, EXISTING_TAB);
487  tabstrip_->SetTabData(model_index, data);
488}
489
490void BrowserTabStripController::StartHighlightTabsForCommand(
491    TabStripModel::ContextMenuCommand command_id,
492    Tab* tab) {
493  if (command_id == TabStripModel::CommandCloseOtherTabs ||
494      command_id == TabStripModel::CommandCloseTabsToRight) {
495    int model_index = tabstrip_->GetModelIndexOfTab(tab);
496    if (IsValidIndex(model_index)) {
497      std::vector<int> indices =
498          model_->GetIndicesClosedByCommand(model_index, command_id);
499      for (std::vector<int>::const_iterator i(indices.begin());
500           i != indices.end(); ++i) {
501        tabstrip_->StartHighlight(*i);
502      }
503    }
504  }
505}
506
507void BrowserTabStripController::StopHighlightTabsForCommand(
508    TabStripModel::ContextMenuCommand command_id,
509    Tab* tab) {
510  if (command_id == TabStripModel::CommandCloseTabsToRight ||
511      command_id == TabStripModel::CommandCloseOtherTabs) {
512    // Just tell all Tabs to stop pulsing - it's safe.
513    tabstrip_->StopAllHighlighting();
514  }
515}
516
517void BrowserTabStripController::AddTab(WebContents* contents,
518                                       int index,
519                                       bool is_active) {
520  // Cancel any pending tab transition.
521  hover_tab_selector_.CancelTabTransition();
522
523  TabRendererData data;
524  SetTabRendererDataFromModel(contents, index, &data, NEW_TAB);
525  tabstrip_->AddTabAt(index, data, is_active);
526}
527
528void BrowserTabStripController::UpdateLayoutType() {
529  bool adjust_layout = false;
530  TabStripLayoutType layout_type =
531      DetermineTabStripLayout(g_browser_process->local_state(), &adjust_layout);
532  tabstrip_->SetLayoutType(layout_type, adjust_layout);
533}
534