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(¶ms); 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