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