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