Home | History | Annotate | Download | only in tabs
      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/browser_process.h"
     14 #include "chrome/browser/chrome_notification_types.h"
     15 #include "chrome/browser/extensions/tab_helper.h"
     16 #include "chrome/browser/favicon/favicon_tab_helper.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/search/search.h"
     19 #include "chrome/browser/ui/browser.h"
     20 #include "chrome/browser/ui/browser_tabstrip.h"
     21 #include "chrome/browser/ui/tabs/tab_menu_model.h"
     22 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     23 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
     24 #include "chrome/browser/ui/tabs/tab_utils.h"
     25 #include "chrome/browser/ui/views/frame/browser_view.h"
     26 #include "chrome/browser/ui/views/tabs/tab.h"
     27 #include "chrome/browser/ui/views/tabs/tab_renderer_data.h"
     28 #include "chrome/browser/ui/views/tabs/tab_strip.h"
     29 #include "chrome/common/pref_names.h"
     30 #include "chrome/common/url_constants.h"
     31 #include "components/metrics/proto/omnibox_event.pb.h"
     32 #include "components/omnibox/autocomplete_match.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 
     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 
     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.
     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:
     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(
    100         model_.get(),
    101         views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
    102   }
    103 
    104   virtual ~TabContextMenuContents() {
    105     if (controller_)
    106       controller_->tabstrip_->StopAllHighlighting();
    107   }
    108 
    109   void Cancel() {
    110     controller_ = NULL;
    111   }
    112 
    113   void RunMenuAt(const gfx::Point& point, ui::MenuSourceType source_type) {
    114     if (menu_runner_->RunMenuAt(tab_->GetWidget(),
    115                                 NULL,
    116                                 gfx::Rect(point, gfx::Size()),
    117                                 views::MENU_ANCHOR_TOPLEFT,
    118                                 source_type) ==
    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 
    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 
    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 
    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 
    219 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 
    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 
    235 bool BrowserTabStripController::IsTabPinned(Tab* tab) const {
    236   return IsTabPinned(tabstrip_->GetModelIndexOfTab(tab));
    237 }
    238 
    239 const ui::ListSelectionModel& BrowserTabStripController::GetSelectionModel() {
    240   return model_->selection_model();
    241 }
    242 
    243 int BrowserTabStripController::GetCount() const {
    244   return model_->count();
    245 }
    246 
    247 bool BrowserTabStripController::IsValidIndex(int index) const {
    248   return model_->ContainsIndex(index);
    249 }
    250 
    251 bool BrowserTabStripController::IsActiveTab(int model_index) const {
    252   return model_->active_index() == model_index;
    253 }
    254 
    255 int BrowserTabStripController::GetActiveIndex() const {
    256   return model_->active_index();
    257 }
    258 
    259 bool BrowserTabStripController::IsTabSelected(int model_index) const {
    260   return model_->IsTabSelected(model_index);
    261 }
    262 
    263 bool BrowserTabStripController::IsTabPinned(int model_index) const {
    264   return model_->ContainsIndex(model_index) && model_->IsTabPinned(model_index);
    265 }
    266 
    267 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 
    276 void BrowserTabStripController::SelectTab(int model_index) {
    277   model_->ActivateTabAt(model_index, true);
    278 }
    279 
    280 void BrowserTabStripController::ExtendSelectionTo(int model_index) {
    281   model_->ExtendSelectionTo(model_index);
    282 }
    283 
    284 void BrowserTabStripController::ToggleSelected(int model_index) {
    285   model_->ToggleSelectionAt(model_index);
    286 }
    287 
    288 void BrowserTabStripController::AddSelectionFromAnchorTo(int model_index) {
    289   model_->AddSelectionFromAnchorTo(model_index);
    290 }
    291 
    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 
    303 void BrowserTabStripController::ToggleTabAudioMute(int model_index) {
    304   content::WebContents* const contents = model_->GetWebContentsAt(model_index);
    305   chrome::SetTabAudioMuted(contents, !chrome::IsTabAudioMuted(contents));
    306 }
    307 
    308 void BrowserTabStripController::ShowContextMenuForTab(
    309     Tab* tab,
    310     const gfx::Point& p,
    311     ui::MenuSourceType source_type) {
    312   context_menu_contents_.reset(new TabContextMenuContents(tab, this));
    313   context_menu_contents_->RunMenuAt(p, source_type);
    314 }
    315 
    316 void BrowserTabStripController::UpdateLoadingAnimations() {
    317   // Don't use the model count here as it's possible for this to be invoked
    318   // before we've applied an update from the model (Browser::TabInsertedAt may
    319   // be processed before us and invokes this).
    320   for (int i = 0, tab_count = tabstrip_->tab_count(); i < tab_count; ++i) {
    321     if (model_->ContainsIndex(i)) {
    322       Tab* tab = tabstrip_->tab_at(i);
    323       WebContents* contents = model_->GetWebContentsAt(i);
    324       tab->UpdateLoadingAnimation(TabContentsNetworkState(contents));
    325     }
    326   }
    327 }
    328 
    329 int BrowserTabStripController::HasAvailableDragActions() const {
    330   return model_->delegate()->GetDragActions();
    331 }
    332 
    333 void BrowserTabStripController::OnDropIndexUpdate(int index,
    334                                                   bool drop_before) {
    335   // Perform a delayed tab transition if hovering directly over a tab.
    336   // Otherwise, cancel the pending one.
    337   if (index != -1 && !drop_before) {
    338     hover_tab_selector_.StartTabTransition(index);
    339   } else {
    340     hover_tab_selector_.CancelTabTransition();
    341   }
    342 }
    343 
    344 void BrowserTabStripController::PerformDrop(bool drop_before,
    345                                             int index,
    346                                             const GURL& url) {
    347   chrome::NavigateParams params(browser_, url, ui::PAGE_TRANSITION_LINK);
    348   params.tabstrip_index = index;
    349 
    350   if (drop_before) {
    351     content::RecordAction(UserMetricsAction("Tab_DropURLBetweenTabs"));
    352     params.disposition = NEW_FOREGROUND_TAB;
    353   } else {
    354     content::RecordAction(UserMetricsAction("Tab_DropURLOnTab"));
    355     params.disposition = CURRENT_TAB;
    356     params.source_contents = model_->GetWebContentsAt(index);
    357   }
    358   params.window_action = chrome::NavigateParams::SHOW_WINDOW;
    359   chrome::Navigate(&params);
    360 }
    361 
    362 bool BrowserTabStripController::IsCompatibleWith(TabStrip* other) const {
    363   Profile* other_profile =
    364       static_cast<BrowserTabStripController*>(other->controller())->profile();
    365   return other_profile == profile();
    366 }
    367 
    368 void BrowserTabStripController::CreateNewTab() {
    369   model_->delegate()->AddTabAt(GURL(), -1, true);
    370 }
    371 
    372 void BrowserTabStripController::CreateNewTabWithLocation(
    373     const base::string16& location) {
    374   // Use autocomplete to clean up the text, going so far as to turn it into
    375   // a search query if necessary.
    376   AutocompleteMatch match;
    377   AutocompleteClassifierFactory::GetForProfile(profile())->Classify(
    378       location, false, false, metrics::OmniboxEventProto::BLANK, &match, NULL);
    379   if (match.destination_url.is_valid())
    380     model_->delegate()->AddTabAt(match.destination_url, -1, true);
    381 }
    382 
    383 bool BrowserTabStripController::IsIncognito() {
    384   return browser_->profile()->IsOffTheRecord();
    385 }
    386 
    387 void BrowserTabStripController::StackedLayoutMaybeChanged() {
    388   bool adjust_layout = false;
    389   bool stacked_layout =
    390       DetermineTabStripLayoutStacked(g_browser_process->local_state(),
    391                                      browser_->host_desktop_type(),
    392                                      &adjust_layout);
    393   if (!adjust_layout || stacked_layout == tabstrip_->stacked_layout())
    394     return;
    395 
    396   g_browser_process->local_state()->SetBoolean(prefs::kTabStripStackedLayout,
    397                                                tabstrip_->stacked_layout());
    398 }
    399 
    400 void BrowserTabStripController::OnStartedDraggingTabs() {
    401   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
    402   if (browser_view && !immersive_reveal_lock_.get()) {
    403     // The top-of-window views should be revealed while the user is dragging
    404     // tabs in immersive fullscreen. The top-of-window views may not be already
    405     // revealed if the user is attempting to attach a tab to a tabstrip
    406     // belonging to an immersive fullscreen window.
    407     immersive_reveal_lock_.reset(
    408         browser_view->immersive_mode_controller()->GetRevealedLock(
    409             ImmersiveModeController::ANIMATE_REVEAL_NO));
    410   }
    411 }
    412 
    413 void BrowserTabStripController::OnStoppedDraggingTabs() {
    414   immersive_reveal_lock_.reset();
    415 }
    416 
    417 void BrowserTabStripController::CheckFileSupported(const GURL& url) {
    418   base::PostTaskAndReplyWithResult(
    419       content::BrowserThread::GetBlockingPool(),
    420       FROM_HERE,
    421       base::Bind(&FindURLMimeType, url),
    422       base::Bind(&BrowserTabStripController::OnFindURLMimeTypeCompleted,
    423                  weak_ptr_factory_.GetWeakPtr(),
    424                  url));
    425 }
    426 
    427 ////////////////////////////////////////////////////////////////////////////////
    428 // BrowserTabStripController, TabStripModelObserver implementation:
    429 
    430 void BrowserTabStripController::TabInsertedAt(WebContents* contents,
    431                                               int model_index,
    432                                               bool is_active) {
    433   DCHECK(contents);
    434   DCHECK(model_->ContainsIndex(model_index));
    435   AddTab(contents, model_index, is_active);
    436 }
    437 
    438 void BrowserTabStripController::TabDetachedAt(WebContents* contents,
    439                                               int model_index) {
    440   // Cancel any pending tab transition.
    441   hover_tab_selector_.CancelTabTransition();
    442 
    443   tabstrip_->RemoveTabAt(model_index);
    444 }
    445 
    446 void BrowserTabStripController::TabSelectionChanged(
    447     TabStripModel* tab_strip_model,
    448     const ui::ListSelectionModel& old_model) {
    449   tabstrip_->SetSelection(old_model, model_->selection_model());
    450 }
    451 
    452 void BrowserTabStripController::TabMoved(WebContents* contents,
    453                                          int from_model_index,
    454                                          int to_model_index) {
    455   // Cancel any pending tab transition.
    456   hover_tab_selector_.CancelTabTransition();
    457 
    458   // Pass in the TabRendererData as the pinned state may have changed.
    459   TabRendererData data;
    460   SetTabRendererDataFromModel(contents, to_model_index, &data, EXISTING_TAB);
    461   tabstrip_->MoveTab(from_model_index, to_model_index, data);
    462 }
    463 
    464 void BrowserTabStripController::TabChangedAt(WebContents* contents,
    465                                              int model_index,
    466                                              TabChangeType change_type) {
    467   if (change_type == TITLE_NOT_LOADING) {
    468     tabstrip_->TabTitleChangedNotLoading(model_index);
    469     // We'll receive another notification of the change asynchronously.
    470     return;
    471   }
    472 
    473   SetTabDataAt(contents, model_index);
    474 }
    475 
    476 void BrowserTabStripController::TabReplacedAt(TabStripModel* tab_strip_model,
    477                                               WebContents* old_contents,
    478                                               WebContents* new_contents,
    479                                               int model_index) {
    480   SetTabDataAt(new_contents, model_index);
    481 }
    482 
    483 void BrowserTabStripController::TabPinnedStateChanged(WebContents* contents,
    484                                                       int model_index) {
    485   // Currently none of the renderers render pinned state differently.
    486 }
    487 
    488 void BrowserTabStripController::TabMiniStateChanged(WebContents* contents,
    489                                                     int model_index) {
    490   SetTabDataAt(contents, model_index);
    491 }
    492 
    493 void BrowserTabStripController::TabBlockedStateChanged(WebContents* contents,
    494                                                        int model_index) {
    495   SetTabDataAt(contents, model_index);
    496 }
    497 
    498 void BrowserTabStripController::SetTabRendererDataFromModel(
    499     WebContents* contents,
    500     int model_index,
    501     TabRendererData* data,
    502     TabStatus tab_status) {
    503   FaviconTabHelper* favicon_tab_helper =
    504       FaviconTabHelper::FromWebContents(contents);
    505 
    506   data->favicon = favicon_tab_helper->GetFavicon().AsImageSkia();
    507   data->network_state = TabContentsNetworkState(contents);
    508   data->title = contents->GetTitle();
    509   data->url = contents->GetURL();
    510   data->loading = contents->IsLoading();
    511   data->crashed_status = contents->GetCrashedStatus();
    512   data->incognito = contents->GetBrowserContext()->IsOffTheRecord();
    513   data->mini = model_->IsMiniTab(model_index);
    514   data->show_icon = data->mini || favicon_tab_helper->ShouldDisplayFavicon();
    515   data->blocked = model_->IsTabBlocked(model_index);
    516   data->app = extensions::TabHelper::FromWebContents(contents)->is_app();
    517   data->media_state = chrome::GetTabMediaStateForContents(contents);
    518 }
    519 
    520 void BrowserTabStripController::SetTabDataAt(content::WebContents* web_contents,
    521                                              int model_index) {
    522   TabRendererData data;
    523   SetTabRendererDataFromModel(web_contents, model_index, &data, EXISTING_TAB);
    524   tabstrip_->SetTabData(model_index, data);
    525 }
    526 
    527 void BrowserTabStripController::StartHighlightTabsForCommand(
    528     TabStripModel::ContextMenuCommand command_id,
    529     Tab* tab) {
    530   if (command_id == TabStripModel::CommandCloseOtherTabs ||
    531       command_id == TabStripModel::CommandCloseTabsToRight) {
    532     int model_index = tabstrip_->GetModelIndexOfTab(tab);
    533     if (IsValidIndex(model_index)) {
    534       std::vector<int> indices =
    535           model_->GetIndicesClosedByCommand(model_index, command_id);
    536       for (std::vector<int>::const_iterator i(indices.begin());
    537            i != indices.end(); ++i) {
    538         tabstrip_->StartHighlight(*i);
    539       }
    540     }
    541   }
    542 }
    543 
    544 void BrowserTabStripController::StopHighlightTabsForCommand(
    545     TabStripModel::ContextMenuCommand command_id,
    546     Tab* tab) {
    547   if (command_id == TabStripModel::CommandCloseTabsToRight ||
    548       command_id == TabStripModel::CommandCloseOtherTabs) {
    549     // Just tell all Tabs to stop pulsing - it's safe.
    550     tabstrip_->StopAllHighlighting();
    551   }
    552 }
    553 
    554 void BrowserTabStripController::AddTab(WebContents* contents,
    555                                        int index,
    556                                        bool is_active) {
    557   // Cancel any pending tab transition.
    558   hover_tab_selector_.CancelTabTransition();
    559 
    560   TabRendererData data;
    561   SetTabRendererDataFromModel(contents, index, &data, NEW_TAB);
    562   tabstrip_->AddTabAt(index, data, is_active);
    563 }
    564 
    565 void BrowserTabStripController::UpdateStackedLayout() {
    566   bool adjust_layout = false;
    567   bool stacked_layout =
    568       DetermineTabStripLayoutStacked(g_browser_process->local_state(),
    569                                      browser_->host_desktop_type(),
    570                                      &adjust_layout);
    571   tabstrip_->set_adjust_layout(adjust_layout);
    572   tabstrip_->SetStackedLayout(stacked_layout);
    573 }
    574 
    575 void BrowserTabStripController::OnFindURLMimeTypeCompleted(
    576     const GURL& url,
    577     const std::string& mime_type) {
    578   // Check whether the mime type, if given, is known to be supported or whether
    579   // there is a plugin that supports the mime type (e.g. PDF).
    580   // TODO(bauerb): This possibly uses stale information, but it's guaranteed not
    581   // to do disk access.
    582   content::WebPluginInfo plugin;
    583   tabstrip_->FileSupported(
    584       url,
    585       mime_type.empty() ||
    586       net::IsSupportedMimeType(mime_type) ||
    587       content::PluginService::GetInstance()->GetPluginInfo(
    588           -1,                // process ID
    589           MSG_ROUTING_NONE,  // routing ID
    590           model_->profile()->GetResourceContext(),
    591           url, GURL(), mime_type, false,
    592           NULL, &plugin, NULL));
    593 }
    594