• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
6 
7 #include "base/auto_reset.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/task_runner_util.h"
10 #include "base/threading/sequenced_worker_pool.h"
11 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
12 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
13 #include "chrome/browser/autocomplete/autocomplete_match.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/extensions/tab_helper.h"
17 #include "chrome/browser/favicon/favicon_tab_helper.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/search/search.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_tabstrip.h"
22 #include "chrome/browser/ui/tabs/tab_menu_model.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
25 #include "chrome/browser/ui/tabs/tab_utils.h"
26 #include "chrome/browser/ui/views/frame/browser_view.h"
27 #include "chrome/browser/ui/views/tabs/tab.h"
28 #include "chrome/browser/ui/views/tabs/tab_renderer_data.h"
29 #include "chrome/browser/ui/views/tabs/tab_strip.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/common/url_constants.h"
32 #include "components/metrics/proto/omnibox_event.pb.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/notification_service.h"
35 #include "content/public/browser/plugin_service.h"
36 #include "content/public/browser/user_metrics.h"
37 #include "content/public/browser/web_contents.h"
38 #include "content/public/common/webplugininfo.h"
39 #include "ipc/ipc_message.h"
40 #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 
46 using base::UserMetricsAction;
47 using content::WebContents;
48 
49 namespace {
50 
TabContentsNetworkState(WebContents * contents)51 TabRendererData::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 
DetermineTabStripLayoutStacked(PrefService * prefs,chrome::HostDesktopType host_desktop_type,bool * adjust_layout)60 bool 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.
FindURLMimeType(const GURL & url)74 std::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 
88 class BrowserTabStripController::TabContextMenuContents
89     : public ui::SimpleMenuModel::Delegate {
90  public:
TabContextMenuContents(Tab * tab,BrowserTabStripController * controller)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 
~TabContextMenuContents()102   virtual ~TabContextMenuContents() {
103     if (controller_)
104       controller_->tabstrip_->StopAllHighlighting();
105   }
106 
Cancel()107   void Cancel() {
108     controller_ = NULL;
109   }
110 
RunMenuAt(const gfx::Point & point,ui::MenuSourceType source_type)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:
IsCommandIdChecked(int command_id) const125   virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
126     return false;
127   }
IsCommandIdEnabled(int command_id) const128   virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
129     return controller_->IsCommandEnabledForTab(
130         static_cast<TabStripModel::ContextMenuCommand>(command_id),
131         tab_);
132   }
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)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   }
CommandIdHighlighted(int command_id)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   }
ExecuteCommand(int command_id,int event_flags)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 
MenuClosed(ui::SimpleMenuModel *)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 
BrowserTabStripController(Browser * browser,TabStripModel * model)182 BrowserTabStripController::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 
~BrowserTabStripController()198 BrowserTabStripController::~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 
InitFromModel(TabStrip * tabstrip)208 void 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 
IsCommandEnabledForTab(TabStripModel::ContextMenuCommand command_id,Tab * tab) const219 bool 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 
ExecuteCommandForTab(TabStripModel::ContextMenuCommand command_id,Tab * tab)227 void 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 
IsTabPinned(Tab * tab) const235 bool BrowserTabStripController::IsTabPinned(Tab* tab) const {
236   return IsTabPinned(tabstrip_->GetModelIndexOfTab(tab));
237 }
238 
GetSelectionModel()239 const ui::ListSelectionModel& BrowserTabStripController::GetSelectionModel() {
240   return model_->selection_model();
241 }
242 
GetCount() const243 int BrowserTabStripController::GetCount() const {
244   return model_->count();
245 }
246 
IsValidIndex(int index) const247 bool BrowserTabStripController::IsValidIndex(int index) const {
248   return model_->ContainsIndex(index);
249 }
250 
IsActiveTab(int model_index) const251 bool BrowserTabStripController::IsActiveTab(int model_index) const {
252   return model_->active_index() == model_index;
253 }
254 
GetActiveIndex() const255 int BrowserTabStripController::GetActiveIndex() const {
256   return model_->active_index();
257 }
258 
IsTabSelected(int model_index) const259 bool BrowserTabStripController::IsTabSelected(int model_index) const {
260   return model_->IsTabSelected(model_index);
261 }
262 
IsTabPinned(int model_index) const263 bool BrowserTabStripController::IsTabPinned(int model_index) const {
264   return model_->ContainsIndex(model_index) && model_->IsTabPinned(model_index);
265 }
266 
IsNewTabPage(int model_index) const267 bool 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 
SelectTab(int model_index)276 void BrowserTabStripController::SelectTab(int model_index) {
277   model_->ActivateTabAt(model_index, true);
278 }
279 
ExtendSelectionTo(int model_index)280 void BrowserTabStripController::ExtendSelectionTo(int model_index) {
281   model_->ExtendSelectionTo(model_index);
282 }
283 
ToggleSelected(int model_index)284 void BrowserTabStripController::ToggleSelected(int model_index) {
285   model_->ToggleSelectionAt(model_index);
286 }
287 
AddSelectionFromAnchorTo(int model_index)288 void BrowserTabStripController::AddSelectionFromAnchorTo(int model_index) {
289   model_->AddSelectionFromAnchorTo(model_index);
290 }
291 
CloseTab(int model_index,CloseTabSource source)292 void 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 
ShowContextMenuForTab(Tab * tab,const gfx::Point & p,ui::MenuSourceType source_type)303 void 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 
UpdateLoadingAnimations()311 void 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 
HasAvailableDragActions() const324 int BrowserTabStripController::HasAvailableDragActions() const {
325   return model_->delegate()->GetDragActions();
326 }
327 
OnDropIndexUpdate(int index,bool drop_before)328 void 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 
PerformDrop(bool drop_before,int index,const GURL & url)339 void 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(&params);
355 }
356 
IsCompatibleWith(TabStrip * other) const357 bool BrowserTabStripController::IsCompatibleWith(TabStrip* other) const {
358   Profile* other_profile =
359       static_cast<BrowserTabStripController*>(other->controller())->profile();
360   return other_profile == profile();
361 }
362 
CreateNewTab()363 void BrowserTabStripController::CreateNewTab() {
364   model_->delegate()->AddTabAt(GURL(), -1, true);
365 }
366 
CreateNewTabWithLocation(const base::string16 & location)367 void 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 
IsIncognito()378 bool BrowserTabStripController::IsIncognito() {
379   return browser_->profile()->IsOffTheRecord();
380 }
381 
StackedLayoutMaybeChanged()382 void 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 
OnStartedDraggingTabs()395 void 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 
OnStoppedDraggingTabs()408 void BrowserTabStripController::OnStoppedDraggingTabs() {
409   immersive_reveal_lock_.reset();
410 }
411 
CheckFileSupported(const GURL & url)412 void 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 
TabInsertedAt(WebContents * contents,int model_index,bool is_active)425 void 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 
TabDetachedAt(WebContents * contents,int model_index)433 void 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 
TabSelectionChanged(TabStripModel * tab_strip_model,const ui::ListSelectionModel & old_model)441 void BrowserTabStripController::TabSelectionChanged(
442     TabStripModel* tab_strip_model,
443     const ui::ListSelectionModel& old_model) {
444   tabstrip_->SetSelection(old_model, model_->selection_model());
445 }
446 
TabMoved(WebContents * contents,int from_model_index,int to_model_index)447 void 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 
TabChangedAt(WebContents * contents,int model_index,TabChangeType change_type)459 void 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 
TabReplacedAt(TabStripModel * tab_strip_model,WebContents * old_contents,WebContents * new_contents,int model_index)471 void 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 
TabPinnedStateChanged(WebContents * contents,int model_index)478 void BrowserTabStripController::TabPinnedStateChanged(WebContents* contents,
479                                                       int model_index) {
480   // Currently none of the renderers render pinned state differently.
481 }
482 
TabMiniStateChanged(WebContents * contents,int model_index)483 void BrowserTabStripController::TabMiniStateChanged(WebContents* contents,
484                                                     int model_index) {
485   SetTabDataAt(contents, model_index);
486 }
487 
TabBlockedStateChanged(WebContents * contents,int model_index)488 void BrowserTabStripController::TabBlockedStateChanged(WebContents* contents,
489                                                        int model_index) {
490   SetTabDataAt(contents, model_index);
491 }
492 
SetTabRendererDataFromModel(WebContents * contents,int model_index,TabRendererData * data,TabStatus tab_status)493 void 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 
SetTabDataAt(content::WebContents * web_contents,int model_index)515 void 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 
StartHighlightTabsForCommand(TabStripModel::ContextMenuCommand command_id,Tab * tab)522 void 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 
StopHighlightTabsForCommand(TabStripModel::ContextMenuCommand command_id,Tab * tab)539 void 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 
AddTab(WebContents * contents,int index,bool is_active)549 void 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 
UpdateStackedLayout()560 void 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 
OnFindURLMimeTypeCompleted(const GURL & url,const std::string & mime_type)570 void 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