This source file includes following definitions.
- TabContentsNetworkState
- DetermineTabStripLayout
- FindURLMimeType
- last_command_
- TabContextMenuContents
- Cancel
- RunMenuAt
- IsCommandIdChecked
- IsCommandIdEnabled
- GetAcceleratorForCommandId
- CommandIdHighlighted
- ExecuteCommand
- MenuClosed
- weak_ptr_factory_
- InitFromModel
- IsCommandEnabledForTab
- ExecuteCommandForTab
- IsTabPinned
- GetSelectionModel
- GetCount
- IsValidIndex
- IsActiveTab
- GetActiveIndex
- IsTabSelected
- IsTabPinned
- IsNewTabPage
- SelectTab
- ExtendSelectionTo
- ToggleSelected
- AddSelectionFromAnchorTo
- CloseTab
- ShowContextMenuForTab
- UpdateLoadingAnimations
- HasAvailableDragActions
- OnDropIndexUpdate
- PerformDrop
- IsCompatibleWith
- CreateNewTab
- CreateNewTabWithLocation
- IsIncognito
- LayoutTypeMaybeChanged
- OnStartedDraggingTabs
- OnStoppedDraggingTabs
- CheckFileSupported
- TabInsertedAt
- TabDetachedAt
- TabSelectionChanged
- TabMoved
- TabChangedAt
- TabReplacedAt
- TabPinnedStateChanged
- TabMiniStateChanged
- TabBlockedStateChanged
- SetTabRendererDataFromModel
- SetTabDataAt
- StartHighlightTabsForCommand
- StopHighlightTabsForCommand
- AddTab
- UpdateLayoutType
- OnFindURLMimeTypeCompleted
#include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/prefs/pref_service.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "chrome/browser/autocomplete/autocomplete_classifier.h"
#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
#include "chrome/browser/autocomplete/autocomplete_input.h"
#include "chrome/browser/autocomplete/autocomplete_match.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/favicon/favicon_tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/tabs/tab_menu_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/tabs/tab.h"
#include "chrome/browser/ui/views/tabs/tab_renderer_data.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/webplugininfo.h"
#include "ipc/ipc_message.h"
#include "net/base/net_util.h"
#include "ui/base/layout.h"
#include "ui/base/models/list_selection_model.h"
#include "ui/gfx/image/image.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/widget/widget.h"
using base::UserMetricsAction;
using content::WebContents;
namespace {
TabRendererData::NetworkState TabContentsNetworkState(
WebContents* contents) {
if (!contents || !contents->IsLoading())
return TabRendererData::NETWORK_STATE_NONE;
if (contents->IsWaitingForResponse())
return TabRendererData::NETWORK_STATE_WAITING;
return TabRendererData::NETWORK_STATE_LOADING;
}
TabStripLayoutType DetermineTabStripLayout(
PrefService* prefs,
chrome::HostDesktopType host_desktop_type,
bool* adjust_layout) {
*adjust_layout = false;
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableStackedTabStrip)) {
return TAB_STRIP_LAYOUT_STACKED;
}
#if defined(USE_AURA)
if (host_desktop_type != chrome::HOST_DESKTOP_TYPE_ASH)
return TAB_STRIP_LAYOUT_SHRINK;
#else
if (ui::GetDisplayLayout() != ui::LAYOUT_TOUCH)
return TAB_STRIP_LAYOUT_SHRINK;
#endif
*adjust_layout = true;
switch (prefs->GetInteger(prefs::kTabStripLayoutType)) {
case TAB_STRIP_LAYOUT_STACKED:
return TAB_STRIP_LAYOUT_STACKED;
default:
return TAB_STRIP_LAYOUT_SHRINK;
}
}
std::string FindURLMimeType(const GURL& url) {
DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
base::FilePath full_path;
net::FileURLToFilePath(url, &full_path);
std::string mime_type;
net::GetMimeTypeFromFile(full_path, &mime_type);
return mime_type;
}
}
class BrowserTabStripController::TabContextMenuContents
: public ui::SimpleMenuModel::Delegate {
public:
TabContextMenuContents(Tab* tab,
BrowserTabStripController* controller)
: tab_(tab),
controller_(controller),
last_command_(TabStripModel::CommandFirst) {
model_.reset(new TabMenuModel(
this, controller->model_,
controller->tabstrip_->GetModelIndexOfTab(tab)));
menu_runner_.reset(new views::MenuRunner(model_.get()));
}
virtual ~TabContextMenuContents() {
if (controller_)
controller_->tabstrip_->StopAllHighlighting();
}
void Cancel() {
controller_ = NULL;
}
void RunMenuAt(const gfx::Point& point, ui::MenuSourceType source_type) {
if (menu_runner_->RunMenuAt(
tab_->GetWidget(), NULL, gfx::Rect(point, gfx::Size()),
views::MenuItemView::TOPLEFT, source_type,
views::MenuRunner::HAS_MNEMONICS |
views::MenuRunner::CONTEXT_MENU) ==
views::MenuRunner::MENU_DELETED)
return;
}
virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
return false;
}
virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
return controller_->IsCommandEnabledForTab(
static_cast<TabStripModel::ContextMenuCommand>(command_id),
tab_);
}
virtual bool GetAcceleratorForCommandId(
int command_id,
ui::Accelerator* accelerator) OVERRIDE {
int browser_cmd;
return TabStripModel::ContextMenuCommandToBrowserCommand(command_id,
&browser_cmd) ?
controller_->tabstrip_->GetWidget()->GetAccelerator(browser_cmd,
accelerator) :
false;
}
virtual void CommandIdHighlighted(int command_id) OVERRIDE {
controller_->StopHighlightTabsForCommand(last_command_, tab_);
last_command_ = static_cast<TabStripModel::ContextMenuCommand>(command_id);
controller_->StartHighlightTabsForCommand(last_command_, tab_);
}
virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE {
controller_->tabstrip_->StopAllHighlighting();
controller_->ExecuteCommandForTab(
static_cast<TabStripModel::ContextMenuCommand>(command_id),
tab_);
}
virtual void MenuClosed(ui::SimpleMenuModel* ) OVERRIDE {
if (controller_)
controller_->tabstrip_->StopAllHighlighting();
}
private:
scoped_ptr<TabMenuModel> model_;
scoped_ptr<views::MenuRunner> menu_runner_;
Tab* tab_;
BrowserTabStripController* controller_;
TabStripModel::ContextMenuCommand last_command_;
DISALLOW_COPY_AND_ASSIGN(TabContextMenuContents);
};
BrowserTabStripController::BrowserTabStripController(Browser* browser,
TabStripModel* model)
: model_(model),
tabstrip_(NULL),
browser_(browser),
hover_tab_selector_(model),
weak_ptr_factory_(this) {
model_->AddObserver(this);
local_pref_registrar_.Init(g_browser_process->local_state());
local_pref_registrar_.Add(
prefs::kTabStripLayoutType,
base::Bind(&BrowserTabStripController::UpdateLayoutType,
base::Unretained(this)));
}
BrowserTabStripController::~BrowserTabStripController() {
if (context_menu_contents_.get())
context_menu_contents_->Cancel();
model_->RemoveObserver(this);
}
void BrowserTabStripController::InitFromModel(TabStrip* tabstrip) {
tabstrip_ = tabstrip;
UpdateLayoutType();
for (int i = 0; i < model_->count(); ++i)
AddTab(model_->GetWebContentsAt(i), i, model_->active_index() == i);
}
bool BrowserTabStripController::IsCommandEnabledForTab(
TabStripModel::ContextMenuCommand command_id,
Tab* tab) const {
int model_index = tabstrip_->GetModelIndexOfTab(tab);
return model_->ContainsIndex(model_index) ?
model_->IsContextMenuCommandEnabled(model_index, command_id) : false;
}
void BrowserTabStripController::ExecuteCommandForTab(
TabStripModel::ContextMenuCommand command_id,
Tab* tab) {
int model_index = tabstrip_->GetModelIndexOfTab(tab);
if (model_->ContainsIndex(model_index))
model_->ExecuteContextMenuCommand(model_index, command_id);
}
bool BrowserTabStripController::IsTabPinned(Tab* tab) const {
return IsTabPinned(tabstrip_->GetModelIndexOfTab(tab));
}
const ui::ListSelectionModel& BrowserTabStripController::GetSelectionModel() {
return model_->selection_model();
}
int BrowserTabStripController::GetCount() const {
return model_->count();
}
bool BrowserTabStripController::IsValidIndex(int index) const {
return model_->ContainsIndex(index);
}
bool BrowserTabStripController::IsActiveTab(int model_index) const {
return model_->active_index() == model_index;
}
int BrowserTabStripController::GetActiveIndex() const {
return model_->active_index();
}
bool BrowserTabStripController::IsTabSelected(int model_index) const {
return model_->IsTabSelected(model_index);
}
bool BrowserTabStripController::IsTabPinned(int model_index) const {
return model_->ContainsIndex(model_index) && model_->IsTabPinned(model_index);
}
bool BrowserTabStripController::IsNewTabPage(int model_index) const {
if (!model_->ContainsIndex(model_index))
return false;
const WebContents* contents = model_->GetWebContentsAt(model_index);
return contents && (contents->GetURL() == GURL(chrome::kChromeUINewTabURL) ||
chrome::IsInstantNTP(contents));
}
void BrowserTabStripController::SelectTab(int model_index) {
model_->ActivateTabAt(model_index, true);
}
void BrowserTabStripController::ExtendSelectionTo(int model_index) {
model_->ExtendSelectionTo(model_index);
}
void BrowserTabStripController::ToggleSelected(int model_index) {
model_->ToggleSelectionAt(model_index);
}
void BrowserTabStripController::AddSelectionFromAnchorTo(int model_index) {
model_->AddSelectionFromAnchorTo(model_index);
}
void BrowserTabStripController::CloseTab(int model_index,
CloseTabSource source) {
hover_tab_selector_.CancelTabTransition();
tabstrip_->PrepareForCloseAt(model_index, source);
model_->CloseWebContentsAt(model_index,
TabStripModel::CLOSE_USER_GESTURE |
TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
}
void BrowserTabStripController::ShowContextMenuForTab(
Tab* tab,
const gfx::Point& p,
ui::MenuSourceType source_type) {
context_menu_contents_.reset(new TabContextMenuContents(tab, this));
context_menu_contents_->RunMenuAt(p, source_type);
}
void BrowserTabStripController::UpdateLoadingAnimations() {
for (int i = 0, tab_count = tabstrip_->tab_count(); i < tab_count; ++i) {
if (model_->ContainsIndex(i)) {
Tab* tab = tabstrip_->tab_at(i);
WebContents* contents = model_->GetWebContentsAt(i);
tab->UpdateLoadingAnimation(TabContentsNetworkState(contents));
}
}
}
int BrowserTabStripController::HasAvailableDragActions() const {
return model_->delegate()->GetDragActions();
}
void BrowserTabStripController::OnDropIndexUpdate(int index,
bool drop_before) {
if (index != -1 && !drop_before) {
hover_tab_selector_.StartTabTransition(index);
} else {
hover_tab_selector_.CancelTabTransition();
}
}
void BrowserTabStripController::PerformDrop(bool drop_before,
int index,
const GURL& url) {
chrome::NavigateParams params(browser_, url, content::PAGE_TRANSITION_LINK);
params.tabstrip_index = index;
if (drop_before) {
content::RecordAction(UserMetricsAction("Tab_DropURLBetweenTabs"));
params.disposition = NEW_FOREGROUND_TAB;
} else {
content::RecordAction(UserMetricsAction("Tab_DropURLOnTab"));
params.disposition = CURRENT_TAB;
params.source_contents = model_->GetWebContentsAt(index);
}
params.window_action = chrome::NavigateParams::SHOW_WINDOW;
chrome::Navigate(¶ms);
}
bool BrowserTabStripController::IsCompatibleWith(TabStrip* other) const {
Profile* other_profile =
static_cast<BrowserTabStripController*>(other->controller())->profile();
return other_profile == profile();
}
void BrowserTabStripController::CreateNewTab() {
model_->delegate()->AddTabAt(GURL(), -1, true);
}
void BrowserTabStripController::CreateNewTabWithLocation(
const base::string16& location) {
AutocompleteMatch match;
AutocompleteClassifierFactory::GetForProfile(profile())->Classify(
location, false, false, AutocompleteInput::BLANK, &match, NULL);
if (match.destination_url.is_valid())
model_->delegate()->AddTabAt(match.destination_url, -1, true);
}
bool BrowserTabStripController::IsIncognito() {
return browser_->profile()->IsOffTheRecord();
}
void BrowserTabStripController::LayoutTypeMaybeChanged() {
bool adjust_layout = false;
TabStripLayoutType layout_type =
DetermineTabStripLayout(g_browser_process->local_state(),
browser_->host_desktop_type(), &adjust_layout);
if (!adjust_layout || layout_type == tabstrip_->layout_type())
return;
g_browser_process->local_state()->SetInteger(
prefs::kTabStripLayoutType,
static_cast<int>(tabstrip_->layout_type()));
}
void BrowserTabStripController::OnStartedDraggingTabs() {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
if (browser_view && !immersive_reveal_lock_.get()) {
immersive_reveal_lock_.reset(
browser_view->immersive_mode_controller()->GetRevealedLock(
ImmersiveModeController::ANIMATE_REVEAL_NO));
}
}
void BrowserTabStripController::OnStoppedDraggingTabs() {
immersive_reveal_lock_.reset();
}
void BrowserTabStripController::CheckFileSupported(const GURL& url) {
base::PostTaskAndReplyWithResult(
content::BrowserThread::GetBlockingPool(),
FROM_HERE,
base::Bind(&FindURLMimeType, url),
base::Bind(&BrowserTabStripController::OnFindURLMimeTypeCompleted,
weak_ptr_factory_.GetWeakPtr(),
url));
}
void BrowserTabStripController::TabInsertedAt(WebContents* contents,
int model_index,
bool is_active) {
DCHECK(contents);
DCHECK(model_->ContainsIndex(model_index));
AddTab(contents, model_index, is_active);
}
void BrowserTabStripController::TabDetachedAt(WebContents* contents,
int model_index) {
hover_tab_selector_.CancelTabTransition();
tabstrip_->RemoveTabAt(model_index);
}
void BrowserTabStripController::TabSelectionChanged(
TabStripModel* tab_strip_model,
const ui::ListSelectionModel& old_model) {
tabstrip_->SetSelection(old_model, model_->selection_model());
}
void BrowserTabStripController::TabMoved(WebContents* contents,
int from_model_index,
int to_model_index) {
hover_tab_selector_.CancelTabTransition();
TabRendererData data;
SetTabRendererDataFromModel(contents, to_model_index, &data, EXISTING_TAB);
tabstrip_->MoveTab(from_model_index, to_model_index, data);
}
void BrowserTabStripController::TabChangedAt(WebContents* contents,
int model_index,
TabChangeType change_type) {
if (change_type == TITLE_NOT_LOADING) {
tabstrip_->TabTitleChangedNotLoading(model_index);
return;
}
SetTabDataAt(contents, model_index);
}
void BrowserTabStripController::TabReplacedAt(TabStripModel* tab_strip_model,
WebContents* old_contents,
WebContents* new_contents,
int model_index) {
SetTabDataAt(new_contents, model_index);
}
void BrowserTabStripController::TabPinnedStateChanged(WebContents* contents,
int model_index) {
}
void BrowserTabStripController::TabMiniStateChanged(WebContents* contents,
int model_index) {
SetTabDataAt(contents, model_index);
}
void BrowserTabStripController::TabBlockedStateChanged(WebContents* contents,
int model_index) {
SetTabDataAt(contents, model_index);
}
void BrowserTabStripController::SetTabRendererDataFromModel(
WebContents* contents,
int model_index,
TabRendererData* data,
TabStatus tab_status) {
FaviconTabHelper* favicon_tab_helper =
FaviconTabHelper::FromWebContents(contents);
data->favicon = favicon_tab_helper->GetFavicon().AsImageSkia();
data->network_state = TabContentsNetworkState(contents);
data->title = contents->GetTitle();
data->url = contents->GetURL();
data->loading = contents->IsLoading();
data->crashed_status = contents->GetCrashedStatus();
data->incognito = contents->GetBrowserContext()->IsOffTheRecord();
data->mini = model_->IsMiniTab(model_index);
data->show_icon = data->mini || favicon_tab_helper->ShouldDisplayFavicon();
data->blocked = model_->IsTabBlocked(model_index);
data->app = extensions::TabHelper::FromWebContents(contents)->is_app();
data->media_state = chrome::GetTabMediaStateForContents(contents);
}
void BrowserTabStripController::SetTabDataAt(content::WebContents* web_contents,
int model_index) {
TabRendererData data;
SetTabRendererDataFromModel(web_contents, model_index, &data, EXISTING_TAB);
tabstrip_->SetTabData(model_index, data);
}
void BrowserTabStripController::StartHighlightTabsForCommand(
TabStripModel::ContextMenuCommand command_id,
Tab* tab) {
if (command_id == TabStripModel::CommandCloseOtherTabs ||
command_id == TabStripModel::CommandCloseTabsToRight) {
int model_index = tabstrip_->GetModelIndexOfTab(tab);
if (IsValidIndex(model_index)) {
std::vector<int> indices =
model_->GetIndicesClosedByCommand(model_index, command_id);
for (std::vector<int>::const_iterator i(indices.begin());
i != indices.end(); ++i) {
tabstrip_->StartHighlight(*i);
}
}
}
}
void BrowserTabStripController::StopHighlightTabsForCommand(
TabStripModel::ContextMenuCommand command_id,
Tab* tab) {
if (command_id == TabStripModel::CommandCloseTabsToRight ||
command_id == TabStripModel::CommandCloseOtherTabs) {
tabstrip_->StopAllHighlighting();
}
}
void BrowserTabStripController::AddTab(WebContents* contents,
int index,
bool is_active) {
hover_tab_selector_.CancelTabTransition();
TabRendererData data;
SetTabRendererDataFromModel(contents, index, &data, NEW_TAB);
tabstrip_->AddTabAt(index, data, is_active);
}
void BrowserTabStripController::UpdateLayoutType() {
bool adjust_layout = false;
TabStripLayoutType layout_type =
DetermineTabStripLayout(g_browser_process->local_state(),
browser_->host_desktop_type(), &adjust_layout);
tabstrip_->SetLayoutType(layout_type, adjust_layout);
}
void BrowserTabStripController::OnFindURLMimeTypeCompleted(
const GURL& url,
const std::string& mime_type) {
content::WebPluginInfo plugin;
tabstrip_->FileSupported(
url,
mime_type.empty() ||
net::IsSupportedMimeType(mime_type) ||
content::PluginService::GetInstance()->GetPluginInfo(
-1,
MSG_ROUTING_NONE,
model_->profile()->GetResourceContext(),
url, GURL(), mime_type, false,
NULL, &plugin, NULL));
}