browser_tab_strip_controller.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/auto_reset.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/prefs/pref_service.h"
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/task_runner_util.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/sequenced_worker_pool.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_classifier.h"
13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_input.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_match.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_process.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chrome_notification_types.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/tab_helper.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/favicon/favicon_tab_helper.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/search/search.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser.h"
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/ui/browser_tabstrip.h"
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_menu_model.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_utils.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/frame/browser_view.h"
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab_renderer_data.h"
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab_strip.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/url_constants.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_service.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/plugin_service.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/user_metrics.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/common/webplugininfo.h"
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ipc/ipc_message.h"
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/filename_util.h"
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/models/list_selection_model.h"
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image.h"
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/menu/menu_runner.h"
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::UserMetricsAction;
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::WebContents;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TabRendererData::NetworkState TabContentsNetworkState(
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WebContents* contents) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!contents || !contents->IsLoadingToDifferentDocument())
5568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    return TabRendererData::NETWORK_STATE_NONE;
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (contents->IsWaitingForResponse())
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return TabRendererData::NETWORK_STATE_WAITING;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return TabRendererData::NETWORK_STATE_LOADING;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool DetermineTabStripLayoutStacked(
62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    PrefService* prefs,
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    chrome::HostDesktopType host_desktop_type,
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool* adjust_layout) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *adjust_layout = false;
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // For ash, always allow entering stacked mode.
67a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (host_desktop_type != chrome::HOST_DESKTOP_TYPE_ASH)
68a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return false;
69a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  *adjust_layout = true;
70a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return prefs->GetBoolean(prefs::kTabStripStackedLayout);
71a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
72a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Get the MIME type of the file pointed to by the url, based on the file's
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// extension. Must be called on a thread that allows IO.
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)std::string FindURLMimeType(const GURL& url) {
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::FilePath full_path;
78a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  net::FileURLToFilePath(url, &full_path);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the MIME type based on the filename.
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string mime_type;
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  net::GetMimeTypeFromFile(full_path, &mime_type);
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return mime_type;
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)class BrowserTabStripController::TabContextMenuContents
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : public ui::SimpleMenuModel::Delegate {
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  TabContextMenuContents(Tab* tab,
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         BrowserTabStripController* controller)
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      : tab_(tab),
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        controller_(controller),
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        last_command_(TabStripModel::CommandFirst) {
97bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    model_.reset(new TabMenuModel(
983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        this, controller->model_,
993551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        controller->tabstrip_->GetModelIndexOfTab(tab)));
100bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    menu_runner_.reset(new views::MenuRunner(model_.get()));
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual ~TabContextMenuContents() {
1043551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (controller_)
105bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      controller_->tabstrip_->StopAllHighlighting();
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void Cancel() {
1093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    controller_ = NULL;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void RunMenuAt(const gfx::Point& point, ui::MenuSourceType source_type) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (menu_runner_->RunMenuAt(tab_->GetWidget(),
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                NULL,
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                gfx::Rect(point, gfx::Size()),
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                views::MENU_ANCHOR_TOPLEFT,
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                source_type,
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                views::MenuRunner::HAS_MNEMONICS |
1191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                    views::MenuRunner::CONTEXT_MENU) ==
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        views::MenuRunner::MENU_DELETED) {
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return;
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Overridden from ui::SimpleMenuModel::Delegate:
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return controller_->IsCommandEnabledForTab(
131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        static_cast<TabStripModel::ContextMenuCommand>(command_id),
132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        tab_);
133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  virtual bool GetAcceleratorForCommandId(
135a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      int command_id,
1364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      ui::Accelerator* accelerator) OVERRIDE {
1374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    int browser_cmd;
138a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return TabStripModel::ContextMenuCommandToBrowserCommand(command_id,
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                                             &browser_cmd) ?
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        controller_->tabstrip_->GetWidget()->GetAccelerator(browser_cmd,
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                                            accelerator) :
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        false;
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual void CommandIdHighlighted(int command_id) OVERRIDE {
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    controller_->StopHighlightTabsForCommand(last_command_, tab_);
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    last_command_ = static_cast<TabStripModel::ContextMenuCommand>(command_id);
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    controller_->StartHighlightTabsForCommand(last_command_, tab_);
148a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
1494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE {
1504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // Executing the command destroys |this|, and can also end up destroying
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // |controller_|. So stop the highlights before executing the command.
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    controller_->tabstrip_->StopAllHighlighting();
153    controller_->ExecuteCommandForTab(
154        static_cast<TabStripModel::ContextMenuCommand>(command_id),
155        tab_);
156  }
157
158  virtual void MenuClosed(ui::SimpleMenuModel* /*source*/) OVERRIDE {
159    if (controller_)
160      controller_->tabstrip_->StopAllHighlighting();
161  }
162
163 private:
164  scoped_ptr<TabMenuModel> model_;
165  scoped_ptr<views::MenuRunner> menu_runner_;
166
167  // The tab we're showing a menu for.
168  Tab* tab_;
169
170  // A pointer back to our hosting controller, for command state information.
171  BrowserTabStripController* controller_;
172
173  // The last command that was selected, so that we can start/stop highlighting
174  // appropriately as the user moves through the menu.
175  TabStripModel::ContextMenuCommand last_command_;
176
177  DISALLOW_COPY_AND_ASSIGN(TabContextMenuContents);
178};
179
180////////////////////////////////////////////////////////////////////////////////
181// BrowserTabStripController, public:
182
183BrowserTabStripController::BrowserTabStripController(Browser* browser,
184                                                     TabStripModel* model)
185    : model_(model),
186      tabstrip_(NULL),
187      browser_(browser),
188      hover_tab_selector_(model),
189      weak_ptr_factory_(this) {
190  model_->AddObserver(this);
191
192  local_pref_registrar_.Init(g_browser_process->local_state());
193  local_pref_registrar_.Add(
194      prefs::kTabStripStackedLayout,
195      base::Bind(&BrowserTabStripController::UpdateStackedLayout,
196                 base::Unretained(this)));
197}
198
199BrowserTabStripController::~BrowserTabStripController() {
200  // When we get here the TabStrip is being deleted. We need to explicitly
201  // cancel the menu, otherwise it may try to invoke something on the tabstrip
202  // from its destructor.
203  if (context_menu_contents_.get())
204    context_menu_contents_->Cancel();
205
206  model_->RemoveObserver(this);
207}
208
209void BrowserTabStripController::InitFromModel(TabStrip* tabstrip) {
210  tabstrip_ = tabstrip;
211
212  UpdateStackedLayout();
213
214  // Walk the model, calling our insertion observer method for each item within
215  // it.
216  for (int i = 0; i < model_->count(); ++i)
217    AddTab(model_->GetWebContentsAt(i), i, model_->active_index() == i);
218}
219
220bool BrowserTabStripController::IsCommandEnabledForTab(
221    TabStripModel::ContextMenuCommand command_id,
222    Tab* tab) const {
223  int model_index = tabstrip_->GetModelIndexOfTab(tab);
224  return model_->ContainsIndex(model_index) ?
225      model_->IsContextMenuCommandEnabled(model_index, command_id) : false;
226}
227
228void BrowserTabStripController::ExecuteCommandForTab(
229    TabStripModel::ContextMenuCommand command_id,
230    Tab* tab) {
231  int model_index = tabstrip_->GetModelIndexOfTab(tab);
232  if (model_->ContainsIndex(model_index))
233    model_->ExecuteContextMenuCommand(model_index, command_id);
234}
235
236bool BrowserTabStripController::IsTabPinned(Tab* tab) const {
237  return IsTabPinned(tabstrip_->GetModelIndexOfTab(tab));
238}
239
240const ui::ListSelectionModel& BrowserTabStripController::GetSelectionModel() {
241  return model_->selection_model();
242}
243
244int BrowserTabStripController::GetCount() const {
245  return model_->count();
246}
247
248bool BrowserTabStripController::IsValidIndex(int index) const {
249  return model_->ContainsIndex(index);
250}
251
252bool BrowserTabStripController::IsActiveTab(int model_index) const {
253  return model_->active_index() == model_index;
254}
255
256int BrowserTabStripController::GetActiveIndex() const {
257  return model_->active_index();
258}
259
260bool BrowserTabStripController::IsTabSelected(int model_index) const {
261  return model_->IsTabSelected(model_index);
262}
263
264bool BrowserTabStripController::IsTabPinned(int model_index) const {
265  return model_->ContainsIndex(model_index) && model_->IsTabPinned(model_index);
266}
267
268bool BrowserTabStripController::IsNewTabPage(int model_index) const {
269  if (!model_->ContainsIndex(model_index))
270    return false;
271
272  const WebContents* contents = model_->GetWebContentsAt(model_index);
273  return contents && (contents->GetURL() == GURL(chrome::kChromeUINewTabURL) ||
274      chrome::IsInstantNTP(contents));
275}
276
277void BrowserTabStripController::SelectTab(int model_index) {
278  model_->ActivateTabAt(model_index, true);
279}
280
281void BrowserTabStripController::ExtendSelectionTo(int model_index) {
282  model_->ExtendSelectionTo(model_index);
283}
284
285void BrowserTabStripController::ToggleSelected(int model_index) {
286  model_->ToggleSelectionAt(model_index);
287}
288
289void BrowserTabStripController::AddSelectionFromAnchorTo(int model_index) {
290  model_->AddSelectionFromAnchorTo(model_index);
291}
292
293void BrowserTabStripController::CloseTab(int model_index,
294                                         CloseTabSource source) {
295  // Cancel any pending tab transition.
296  hover_tab_selector_.CancelTabTransition();
297
298  tabstrip_->PrepareForCloseAt(model_index, source);
299  model_->CloseWebContentsAt(model_index,
300                             TabStripModel::CLOSE_USER_GESTURE |
301                             TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
302}
303
304void BrowserTabStripController::ShowContextMenuForTab(
305    Tab* tab,
306    const gfx::Point& p,
307    ui::MenuSourceType source_type) {
308  context_menu_contents_.reset(new TabContextMenuContents(tab, this));
309  context_menu_contents_->RunMenuAt(p, source_type);
310}
311
312void BrowserTabStripController::UpdateLoadingAnimations() {
313  // Don't use the model count here as it's possible for this to be invoked
314  // before we've applied an update from the model (Browser::TabInsertedAt may
315  // be processed before us and invokes this).
316  for (int i = 0, tab_count = tabstrip_->tab_count(); i < tab_count; ++i) {
317    if (model_->ContainsIndex(i)) {
318      Tab* tab = tabstrip_->tab_at(i);
319      WebContents* contents = model_->GetWebContentsAt(i);
320      tab->UpdateLoadingAnimation(TabContentsNetworkState(contents));
321    }
322  }
323}
324
325int BrowserTabStripController::HasAvailableDragActions() const {
326  return model_->delegate()->GetDragActions();
327}
328
329void BrowserTabStripController::OnDropIndexUpdate(int index,
330                                                  bool drop_before) {
331  // Perform a delayed tab transition if hovering directly over a tab.
332  // Otherwise, cancel the pending one.
333  if (index != -1 && !drop_before) {
334    hover_tab_selector_.StartTabTransition(index);
335  } else {
336    hover_tab_selector_.CancelTabTransition();
337  }
338}
339
340void BrowserTabStripController::PerformDrop(bool drop_before,
341                                            int index,
342                                            const GURL& url) {
343  chrome::NavigateParams params(browser_, url, content::PAGE_TRANSITION_LINK);
344  params.tabstrip_index = index;
345
346  if (drop_before) {
347    content::RecordAction(UserMetricsAction("Tab_DropURLBetweenTabs"));
348    params.disposition = NEW_FOREGROUND_TAB;
349  } else {
350    content::RecordAction(UserMetricsAction("Tab_DropURLOnTab"));
351    params.disposition = CURRENT_TAB;
352    params.source_contents = model_->GetWebContentsAt(index);
353  }
354  params.window_action = chrome::NavigateParams::SHOW_WINDOW;
355  chrome::Navigate(&params);
356}
357
358bool BrowserTabStripController::IsCompatibleWith(TabStrip* other) const {
359  Profile* other_profile =
360      static_cast<BrowserTabStripController*>(other->controller())->profile();
361  return other_profile == profile();
362}
363
364void BrowserTabStripController::CreateNewTab() {
365  model_->delegate()->AddTabAt(GURL(), -1, true);
366}
367
368void BrowserTabStripController::CreateNewTabWithLocation(
369    const base::string16& location) {
370  // Use autocomplete to clean up the text, going so far as to turn it into
371  // a search query if necessary.
372  AutocompleteMatch match;
373  AutocompleteClassifierFactory::GetForProfile(profile())->Classify(
374      location, false, false, AutocompleteInput::BLANK, &match, NULL);
375  if (match.destination_url.is_valid())
376    model_->delegate()->AddTabAt(match.destination_url, -1, true);
377}
378
379bool BrowserTabStripController::IsIncognito() {
380  return browser_->profile()->IsOffTheRecord();
381}
382
383void BrowserTabStripController::StackedLayoutMaybeChanged() {
384  bool adjust_layout = false;
385  bool stacked_layout =
386      DetermineTabStripLayoutStacked(g_browser_process->local_state(),
387                                     browser_->host_desktop_type(),
388                                     &adjust_layout);
389  if (!adjust_layout || stacked_layout == tabstrip_->stacked_layout())
390    return;
391
392  g_browser_process->local_state()->SetBoolean(prefs::kTabStripStackedLayout,
393                                               tabstrip_->stacked_layout());
394}
395
396void BrowserTabStripController::OnStartedDraggingTabs() {
397  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
398  if (browser_view && !immersive_reveal_lock_.get()) {
399    // The top-of-window views should be revealed while the user is dragging
400    // tabs in immersive fullscreen. The top-of-window views may not be already
401    // revealed if the user is attempting to attach a tab to a tabstrip
402    // belonging to an immersive fullscreen window.
403    immersive_reveal_lock_.reset(
404        browser_view->immersive_mode_controller()->GetRevealedLock(
405            ImmersiveModeController::ANIMATE_REVEAL_NO));
406  }
407}
408
409void BrowserTabStripController::OnStoppedDraggingTabs() {
410  immersive_reveal_lock_.reset();
411}
412
413void BrowserTabStripController::CheckFileSupported(const GURL& url) {
414  base::PostTaskAndReplyWithResult(
415      content::BrowserThread::GetBlockingPool(),
416      FROM_HERE,
417      base::Bind(&FindURLMimeType, url),
418      base::Bind(&BrowserTabStripController::OnFindURLMimeTypeCompleted,
419                 weak_ptr_factory_.GetWeakPtr(),
420                 url));
421}
422
423////////////////////////////////////////////////////////////////////////////////
424// BrowserTabStripController, TabStripModelObserver implementation:
425
426void BrowserTabStripController::TabInsertedAt(WebContents* contents,
427                                              int model_index,
428                                              bool is_active) {
429  DCHECK(contents);
430  DCHECK(model_->ContainsIndex(model_index));
431  AddTab(contents, model_index, is_active);
432}
433
434void BrowserTabStripController::TabDetachedAt(WebContents* contents,
435                                              int model_index) {
436  // Cancel any pending tab transition.
437  hover_tab_selector_.CancelTabTransition();
438
439  tabstrip_->RemoveTabAt(model_index);
440}
441
442void BrowserTabStripController::TabSelectionChanged(
443    TabStripModel* tab_strip_model,
444    const ui::ListSelectionModel& old_model) {
445  tabstrip_->SetSelection(old_model, model_->selection_model());
446}
447
448void BrowserTabStripController::TabMoved(WebContents* contents,
449                                         int from_model_index,
450                                         int to_model_index) {
451  // Cancel any pending tab transition.
452  hover_tab_selector_.CancelTabTransition();
453
454  // Pass in the TabRendererData as the pinned state may have changed.
455  TabRendererData data;
456  SetTabRendererDataFromModel(contents, to_model_index, &data, EXISTING_TAB);
457  tabstrip_->MoveTab(from_model_index, to_model_index, data);
458}
459
460void BrowserTabStripController::TabChangedAt(WebContents* contents,
461                                             int model_index,
462                                             TabChangeType change_type) {
463  if (change_type == TITLE_NOT_LOADING) {
464    tabstrip_->TabTitleChangedNotLoading(model_index);
465    // We'll receive another notification of the change asynchronously.
466    return;
467  }
468
469  SetTabDataAt(contents, model_index);
470}
471
472void BrowserTabStripController::TabReplacedAt(TabStripModel* tab_strip_model,
473                                              WebContents* old_contents,
474                                              WebContents* new_contents,
475                                              int model_index) {
476  SetTabDataAt(new_contents, model_index);
477}
478
479void BrowserTabStripController::TabPinnedStateChanged(WebContents* contents,
480                                                      int model_index) {
481  // Currently none of the renderers render pinned state differently.
482}
483
484void BrowserTabStripController::TabMiniStateChanged(WebContents* contents,
485                                                    int model_index) {
486  SetTabDataAt(contents, model_index);
487}
488
489void BrowserTabStripController::TabBlockedStateChanged(WebContents* contents,
490                                                       int model_index) {
491  SetTabDataAt(contents, model_index);
492}
493
494void BrowserTabStripController::SetTabRendererDataFromModel(
495    WebContents* contents,
496    int model_index,
497    TabRendererData* data,
498    TabStatus tab_status) {
499  FaviconTabHelper* favicon_tab_helper =
500      FaviconTabHelper::FromWebContents(contents);
501
502  data->favicon = favicon_tab_helper->GetFavicon().AsImageSkia();
503  data->network_state = TabContentsNetworkState(contents);
504  data->title = contents->GetTitle();
505  data->url = contents->GetURL();
506  data->loading = contents->IsLoading();
507  data->crashed_status = contents->GetCrashedStatus();
508  data->incognito = contents->GetBrowserContext()->IsOffTheRecord();
509  data->mini = model_->IsMiniTab(model_index);
510  data->show_icon = data->mini || favicon_tab_helper->ShouldDisplayFavicon();
511  data->blocked = model_->IsTabBlocked(model_index);
512  data->app = extensions::TabHelper::FromWebContents(contents)->is_app();
513  data->media_state = chrome::GetTabMediaStateForContents(contents);
514}
515
516void BrowserTabStripController::SetTabDataAt(content::WebContents* web_contents,
517                                             int model_index) {
518  TabRendererData data;
519  SetTabRendererDataFromModel(web_contents, model_index, &data, EXISTING_TAB);
520  tabstrip_->SetTabData(model_index, data);
521}
522
523void BrowserTabStripController::StartHighlightTabsForCommand(
524    TabStripModel::ContextMenuCommand command_id,
525    Tab* tab) {
526  if (command_id == TabStripModel::CommandCloseOtherTabs ||
527      command_id == TabStripModel::CommandCloseTabsToRight) {
528    int model_index = tabstrip_->GetModelIndexOfTab(tab);
529    if (IsValidIndex(model_index)) {
530      std::vector<int> indices =
531          model_->GetIndicesClosedByCommand(model_index, command_id);
532      for (std::vector<int>::const_iterator i(indices.begin());
533           i != indices.end(); ++i) {
534        tabstrip_->StartHighlight(*i);
535      }
536    }
537  }
538}
539
540void BrowserTabStripController::StopHighlightTabsForCommand(
541    TabStripModel::ContextMenuCommand command_id,
542    Tab* tab) {
543  if (command_id == TabStripModel::CommandCloseTabsToRight ||
544      command_id == TabStripModel::CommandCloseOtherTabs) {
545    // Just tell all Tabs to stop pulsing - it's safe.
546    tabstrip_->StopAllHighlighting();
547  }
548}
549
550void BrowserTabStripController::AddTab(WebContents* contents,
551                                       int index,
552                                       bool is_active) {
553  // Cancel any pending tab transition.
554  hover_tab_selector_.CancelTabTransition();
555
556  TabRendererData data;
557  SetTabRendererDataFromModel(contents, index, &data, NEW_TAB);
558  tabstrip_->AddTabAt(index, data, is_active);
559}
560
561void BrowserTabStripController::UpdateStackedLayout() {
562  bool adjust_layout = false;
563  bool stacked_layout =
564      DetermineTabStripLayoutStacked(g_browser_process->local_state(),
565                                     browser_->host_desktop_type(),
566                                     &adjust_layout);
567  tabstrip_->set_adjust_layout(adjust_layout);
568  tabstrip_->SetStackedLayout(stacked_layout);
569}
570
571void BrowserTabStripController::OnFindURLMimeTypeCompleted(
572    const GURL& url,
573    const std::string& mime_type) {
574  // Check whether the mime type, if given, is known to be supported or whether
575  // there is a plugin that supports the mime type (e.g. PDF).
576  // TODO(bauerb): This possibly uses stale information, but it's guaranteed not
577  // to do disk access.
578  content::WebPluginInfo plugin;
579  tabstrip_->FileSupported(
580      url,
581      mime_type.empty() ||
582      net::IsSupportedMimeType(mime_type) ||
583      content::PluginService::GetInstance()->GetPluginInfo(
584          -1,                // process ID
585          MSG_ROUTING_NONE,  // routing ID
586          model_->profile()->GetResourceContext(),
587          url, GURL(), mime_type, false,
588          NULL, &plugin, NULL));
589}
590