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(&params);
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