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
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(
100 model_.get(),
101 views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
102 }
103
~TabContextMenuContents()104 virtual ~TabContextMenuContents() {
105 if (controller_)
106 controller_->tabstrip_->StopAllHighlighting();
107 }
108
Cancel()109 void Cancel() {
110 controller_ = NULL;
111 }
112
RunMenuAt(const gfx::Point & point,ui::MenuSourceType source_type)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:
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
ToggleTabAudioMute(int model_index)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
ShowContextMenuForTab(Tab * tab,const gfx::Point & p,ui::MenuSourceType source_type)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
UpdateLoadingAnimations()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
HasAvailableDragActions() const329 int BrowserTabStripController::HasAvailableDragActions() const {
330 return model_->delegate()->GetDragActions();
331 }
332
OnDropIndexUpdate(int index,bool drop_before)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
PerformDrop(bool drop_before,int index,const GURL & url)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(¶ms);
360 }
361
IsCompatibleWith(TabStrip * other) const362 bool BrowserTabStripController::IsCompatibleWith(TabStrip* other) const {
363 Profile* other_profile =
364 static_cast<BrowserTabStripController*>(other->controller())->profile();
365 return other_profile == profile();
366 }
367
CreateNewTab()368 void BrowserTabStripController::CreateNewTab() {
369 model_->delegate()->AddTabAt(GURL(), -1, true);
370 }
371
CreateNewTabWithLocation(const base::string16 & location)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
IsIncognito()383 bool BrowserTabStripController::IsIncognito() {
384 return browser_->profile()->IsOffTheRecord();
385 }
386
StackedLayoutMaybeChanged()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
OnStartedDraggingTabs()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
OnStoppedDraggingTabs()413 void BrowserTabStripController::OnStoppedDraggingTabs() {
414 immersive_reveal_lock_.reset();
415 }
416
CheckFileSupported(const GURL & url)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
TabInsertedAt(WebContents * contents,int model_index,bool is_active)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
TabDetachedAt(WebContents * contents,int model_index)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
TabSelectionChanged(TabStripModel * tab_strip_model,const ui::ListSelectionModel & old_model)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
TabMoved(WebContents * contents,int from_model_index,int to_model_index)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
TabChangedAt(WebContents * contents,int model_index,TabChangeType change_type)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
TabReplacedAt(TabStripModel * tab_strip_model,WebContents * old_contents,WebContents * new_contents,int model_index)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
TabPinnedStateChanged(WebContents * contents,int model_index)483 void BrowserTabStripController::TabPinnedStateChanged(WebContents* contents,
484 int model_index) {
485 // Currently none of the renderers render pinned state differently.
486 }
487
TabMiniStateChanged(WebContents * contents,int model_index)488 void BrowserTabStripController::TabMiniStateChanged(WebContents* contents,
489 int model_index) {
490 SetTabDataAt(contents, model_index);
491 }
492
TabBlockedStateChanged(WebContents * contents,int model_index)493 void BrowserTabStripController::TabBlockedStateChanged(WebContents* contents,
494 int model_index) {
495 SetTabDataAt(contents, model_index);
496 }
497
SetTabRendererDataFromModel(WebContents * contents,int model_index,TabRendererData * data,TabStatus tab_status)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
SetTabDataAt(content::WebContents * web_contents,int model_index)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
StartHighlightTabsForCommand(TabStripModel::ContextMenuCommand command_id,Tab * tab)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
StopHighlightTabsForCommand(TabStripModel::ContextMenuCommand command_id,Tab * tab)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
AddTab(WebContents * contents,int index,bool is_active)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
UpdateStackedLayout()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
OnFindURLMimeTypeCompleted(const GURL & url,const std::string & mime_type)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