blob: 463bb6655b8c7416256a987346914033b25f1cce [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include <algorithm>
#include <map>
#include "base/i18n/rtl.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
#include "chrome/browser/extensions/extension_action.h"
#include "chrome/browser/extensions/extension_action_manager.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/location_bar_controller.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/translate/chrome_translate_client.h"
#include "chrome/browser/translate/translate_service.h"
#include "chrome/browser/ui/autofill/save_card_bubble_controller_impl.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/autofill/save_card_icon_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/location_bar/background_with_1_px_border.h"
#include "chrome/browser/ui/views/location_bar/content_setting_image_view.h"
#include "chrome/browser/ui/views/location_bar/keyword_hint_view.h"
#include "chrome/browser/ui/views/location_bar/location_bar_layout.h"
#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
#include "chrome/browser/ui/views/location_bar/open_pdf_in_reader_view.h"
#include "chrome/browser/ui/views/location_bar/page_action_image_view.h"
#include "chrome/browser/ui/views/location_bar/page_action_with_badge_view.h"
#include "chrome/browser/ui/views/location_bar/selected_keyword_view.h"
#include "chrome/browser/ui/views/location_bar/star_view.h"
#include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h"
#include "chrome/browser/ui/views/location_bar/zoom_view.h"
#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h"
#include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
#include "chrome/browser/ui/views/translate/translate_bubble_view.h"
#include "chrome/browser/ui/views/translate/translate_icon_view.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/theme_resources.h"
#include "components/bookmarks/common/bookmark_pref_names.h"
#include "components/favicon/content/content_favicon_driver.h"
#include "components/grit/components_scaled_resources.h"
#include "components/omnibox/browser/omnibox_popup_model.h"
#include "components/omnibox/browser/omnibox_popup_view.h"
#include "components/prefs/pref_service.h"
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_service.h"
#include "components/toolbar/toolbar_model.h"
#include "components/translate/core/browser/language_state.h"
#include "components/variations/variations_associated_data.h"
#include "components/zoom/zoom_controller.h"
#include "components/zoom/zoom_event_manager.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/feature_switch.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/events/event.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/text_utils.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/button_drag_utils.h"
#include "ui/views/controls/label.h"
#include "ui/views/widget/widget.h"
#if !defined(OS_CHROMEOS)
#include "chrome/browser/ui/views/first_run_bubble.h"
#endif
using content::WebContents;
using views::View;
namespace {
// The border color for MD windows, as well as non-MD popup windows.
const SkColor kBorderColor = SkColorSetA(SK_ColorBLACK, 0x4D);
} // namespace
// LocationBarView -----------------------------------------------------------
// static
const char LocationBarView::kViewClassName[] = "LocationBarView";
LocationBarView::LocationBarView(Browser* browser,
Profile* profile,
CommandUpdater* command_updater,
Delegate* delegate,
bool is_popup_mode)
: LocationBar(profile),
ChromeOmniboxEditController(command_updater),
browser_(browser),
omnibox_view_(nullptr),
delegate_(delegate),
location_icon_view_(nullptr),
ime_inline_autocomplete_view_(nullptr),
selected_keyword_view_(nullptr),
keyword_hint_view_(nullptr),
zoom_view_(nullptr),
open_pdf_in_reader_view_(nullptr),
manage_passwords_icon_view_(nullptr),
save_credit_card_icon_view_(nullptr),
translate_icon_view_(nullptr),
star_view_(nullptr),
size_animation_(this),
is_popup_mode_(is_popup_mode),
show_focus_rect_(false),
template_url_service_(NULL),
web_contents_null_at_last_refresh_(true) {
edit_bookmarks_enabled_.Init(
bookmarks::prefs::kEditBookmarksEnabled, profile->GetPrefs(),
base::Bind(&LocationBarView::UpdateWithoutTabRestore,
base::Unretained(this)));
zoom::ZoomEventManager::GetForBrowserContext(profile)
->AddZoomEventManagerObserver(this);
}
LocationBarView::~LocationBarView() {
if (template_url_service_)
template_url_service_->RemoveObserver(this);
zoom::ZoomEventManager::GetForBrowserContext(profile())
->RemoveZoomEventManagerObserver(this);
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, public:
// static
SkColor LocationBarView::GetBorderColor(bool incognito) {
return color_utils::AlphaBlend(
SkColorSetA(kBorderColor, SK_AlphaOPAQUE),
ThemeProperties::GetDefaultColor(ThemeProperties::COLOR_TOOLBAR,
incognito),
SkColorGetA(kBorderColor));
}
void LocationBarView::Init() {
// We need to be in a Widget, otherwise GetNativeTheme() may change and we're
// not prepared for that.
DCHECK(GetWidget());
// Make sure children with layers are clipped. See http://crbug.com/589497
SetPaintToLayer(true);
layer()->SetFillsBoundsOpaquely(false);
layer()->SetMasksToBounds(true);
// Determine the main font.
gfx::FontList font_list = ResourceBundle::GetSharedInstance().GetFontList(
ResourceBundle::BaseFont);
const int current_font_size = font_list.GetFontSize();
constexpr int kDesiredFontSize = 14;
if (current_font_size != kDesiredFontSize) {
font_list =
font_list.DeriveWithSizeDelta(kDesiredFontSize - current_font_size);
}
// Shrink large fonts to make them fit.
// TODO(pkasting): Stretch the location bar instead in this case.
const int vertical_padding = GetTotalVerticalPadding();
const int location_height =
std::max(GetPreferredSize().height() - (vertical_padding * 2), 0);
font_list = font_list.DeriveWithHeightUpperBound(location_height);
// Determine the font for use inside the bubbles.
const int bubble_padding =
kBubbleVerticalPadding +
GetLayoutConstant(LOCATION_BAR_BUBBLE_FONT_VERTICAL_PADDING);
const int bubble_height = location_height - (bubble_padding * 2);
const SkColor background_color = GetColor(BACKGROUND);
location_icon_view_ = new LocationIconView(font_list, this);
location_icon_view_->set_drag_controller(this);
AddChildView(location_icon_view_);
// Initialize the Omnibox view.
omnibox_view_ = new OmniboxViewViews(
this, profile(), command_updater(), is_popup_mode_, this, font_list);
omnibox_view_->Init();
AddChildView(omnibox_view_);
// Initialize the inline autocomplete view which is visible only when IME is
// turned on. Use the same font with the omnibox and highlighted background.
ime_inline_autocomplete_view_ = new views::Label(base::string16(), font_list);
ime_inline_autocomplete_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
ime_inline_autocomplete_view_->SetAutoColorReadabilityEnabled(false);
ime_inline_autocomplete_view_->set_background(
views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused)));
ime_inline_autocomplete_view_->SetEnabledColor(
GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_TextfieldSelectionColor));
ime_inline_autocomplete_view_->SetVisible(false);
AddChildView(ime_inline_autocomplete_view_);
selected_keyword_view_ = new SelectedKeywordView(font_list, profile());
AddChildView(selected_keyword_view_);
gfx::FontList bubble_font_list =
font_list.DeriveWithHeightUpperBound(bubble_height);
keyword_hint_view_ = new KeywordHintView(
profile(), font_list, bubble_font_list, location_height,
GetColor(LocationBarView::DEEMPHASIZED_TEXT), background_color);
AddChildView(keyword_hint_view_);
ScopedVector<ContentSettingImageModel> models =
ContentSettingImageModel::GenerateContentSettingImageModels();
for (ContentSettingImageModel* model : models.get()) {
// ContentSettingImageView takes ownership of its model.
ContentSettingImageView* image_view =
new ContentSettingImageView(model, this, font_list);
content_setting_views_.push_back(image_view);
image_view->SetVisible(false);
AddChildView(image_view);
}
models.weak_clear();
zoom_view_ = new ZoomView(delegate_);
AddChildView(zoom_view_);
open_pdf_in_reader_view_ = new OpenPDFInReaderView();
AddChildView(open_pdf_in_reader_view_);
manage_passwords_icon_view_ = new ManagePasswordsIconViews(command_updater());
AddChildView(manage_passwords_icon_view_);
save_credit_card_icon_view_ =
new autofill::SaveCardIconView(command_updater(), browser_);
save_credit_card_icon_view_->SetVisible(false);
AddChildView(save_credit_card_icon_view_);
translate_icon_view_ = new TranslateIconView(command_updater());
translate_icon_view_->SetVisible(false);
AddChildView(translate_icon_view_);
star_view_ = new StarView(command_updater(), browser_);
star_view_->SetVisible(false);
AddChildView(star_view_);
// Initialize the location entry. We do this to avoid a black flash which is
// visible when the location entry has just been initialized.
Update(nullptr);
size_animation_.Reset(1);
}
bool LocationBarView::IsInitialized() const {
return omnibox_view_ != nullptr;
}
SkColor LocationBarView::GetColor(
ColorKind kind) const {
const ui::NativeTheme* native_theme = GetNativeTheme();
switch (kind) {
case BACKGROUND:
return native_theme->GetSystemColor(
ui::NativeTheme::kColorId_TextfieldDefaultBackground);
case TEXT:
return native_theme->GetSystemColor(
ui::NativeTheme::kColorId_TextfieldDefaultColor);
case SELECTED_TEXT:
return native_theme->GetSystemColor(
ui::NativeTheme::kColorId_TextfieldSelectionColor);
case DEEMPHASIZED_TEXT:
return color_utils::AlphaBlend(GetColor(TEXT), GetColor(BACKGROUND), 128);
case SECURITY_CHIP_TEXT:
return GetSecureTextColor(GetToolbarModel()->GetSecurityLevel(false));
}
NOTREACHED();
return gfx::kPlaceholderColor;
}
SkColor LocationBarView::GetSecureTextColor(
security_state::SecurityLevel security_level) const {
if (security_level == security_state::SECURE_WITH_POLICY_INSTALLED_CERT) {
return GetColor(DEEMPHASIZED_TEXT);
}
SkColor text_color = GetColor(TEXT);
if (!color_utils::IsDark(GetColor(BACKGROUND))) {
if ((security_level == security_state::EV_SECURE) ||
(security_level == security_state::SECURE)) {
text_color = gfx::kGoogleGreen700;
} else if (security_level == security_state::DANGEROUS) {
text_color = gfx::kGoogleRed700;
}
}
return color_utils::GetReadableColor(text_color, GetColor(BACKGROUND));
}
void LocationBarView::ZoomChangedForActiveTab(bool can_show_bubble) {
DCHECK(zoom_view_);
if (RefreshZoomView()) {
Layout();
SchedulePaint();
}
WebContents* web_contents = GetWebContents();
if (can_show_bubble && zoom_view_->visible() && web_contents)
ZoomBubbleView::ShowBubble(web_contents, ZoomBubbleView::AUTOMATIC);
}
void LocationBarView::SetPreviewEnabledPageAction(ExtensionAction* page_action,
bool preview_enabled) {
if (is_popup_mode_)
return;
DCHECK(page_action);
WebContents* web_contents = GetWebContents();
RefreshPageActionViews();
PageActionWithBadgeView* page_action_view =
static_cast<PageActionWithBadgeView*>(GetPageActionView(page_action));
DCHECK(page_action_view);
if (!page_action_view)
return;
page_action_view->image_view()->set_preview_enabled(preview_enabled);
page_action_view->UpdateVisibility(web_contents);
Layout();
SchedulePaint();
}
PageActionWithBadgeView* LocationBarView::GetPageActionView(
ExtensionAction* page_action) {
DCHECK(page_action);
for (const auto& action_view : page_action_views_) {
if (action_view->image_view()->extension_action() == page_action)
return action_view.get();
}
return nullptr;
}
void LocationBarView::SetStarToggled(bool on) {
if (star_view_)
star_view_->SetToggled(on);
}
gfx::Point LocationBarView::GetOmniboxViewOrigin() const {
gfx::Point origin(omnibox_view_->bounds().origin());
origin.set_x(GetMirroredXInView(origin.x()));
views::View::ConvertPointToScreen(this, &origin);
return origin;
}
void LocationBarView::SetImeInlineAutocompletion(const base::string16& text) {
ime_inline_autocomplete_view_->SetText(text);
ime_inline_autocomplete_view_->SetVisible(!text.empty());
}
void LocationBarView::SetShowFocusRect(bool show) {
show_focus_rect_ = show;
SchedulePaint();
}
void LocationBarView::SelectAll() {
omnibox_view_->SelectAll(true);
}
gfx::Point LocationBarView::GetLocationBarAnchorPoint() const {
const views::ImageView* image = location_icon_view_->GetImageView();
const gfx::Rect image_bounds(image->GetImageBounds());
gfx::Point point(image_bounds.CenterPoint().x(), image_bounds.bottom());
ConvertPointToTarget(image, this, &point);
return point;
}
void LocationBarView::GetOmniboxPopupPositioningInfo(
gfx::Point* top_left_screen_coord,
int* popup_width,
int* left_margin,
int* right_margin,
int top_edge_overlap) {
*top_left_screen_coord = gfx::Point(0, parent()->height() - top_edge_overlap);
views::View::ConvertPointToScreen(parent(), top_left_screen_coord);
*popup_width = parent()->width();
*left_margin = x();
*right_margin = *popup_width - bounds().right();
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, public LocationBar implementation:
void LocationBarView::FocusLocation(bool select_all) {
omnibox_view_->SetFocus();
if (select_all)
omnibox_view_->SelectAll(true);
}
void LocationBarView::Revert() {
omnibox_view_->RevertAll();
}
OmniboxView* LocationBarView::GetOmniboxView() {
return omnibox_view_;
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, public views::View implementation:
bool LocationBarView::HasFocus() const {
return omnibox_view_->model()->has_focus();
}
void LocationBarView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
node_data->role = ui::AX_ROLE_GROUP;
}
gfx::Size LocationBarView::GetPreferredSize() const {
// Compute minimum height.
gfx::Size min_size(0, GetLayoutConstant(LOCATION_BAR_HEIGHT));
if (!IsInitialized())
return min_size;
min_size.set_height(min_size.height() * size_animation_.GetCurrentValue());
// Compute width of omnibox-leading content.
const int edge_thickness = GetHorizontalEdgeThickness();
int leading_width = edge_thickness;
if (ShouldShowKeywordBubble()) {
// The selected keyword view can collapse completely.
} else if (ShouldShowLocationIconText()) {
leading_width +=
location_icon_view_->GetMinimumSizeForLabelText(GetLocationIconText())
.width();
} else {
leading_width +=
kHorizontalPadding + location_icon_view_->GetMinimumSize().width();
}
// Compute width of omnibox-trailing content.
int trailing_width = edge_thickness;
trailing_width += IncrementalMinimumWidth(star_view_) +
IncrementalMinimumWidth(translate_icon_view_) +
IncrementalMinimumWidth(open_pdf_in_reader_view_) +
IncrementalMinimumWidth(save_credit_card_icon_view_) +
IncrementalMinimumWidth(manage_passwords_icon_view_) +
IncrementalMinimumWidth(zoom_view_);
for (const auto& action_view : page_action_views_)
trailing_width += IncrementalMinimumWidth(action_view.get());
for (auto i = content_setting_views_.begin();
i != content_setting_views_.end(); ++i) {
trailing_width += IncrementalMinimumWidth((*i));
}
min_size.set_width(leading_width + omnibox_view_->GetMinimumSize().width() +
2 * kHorizontalPadding -
omnibox_view_->GetInsets().width() + trailing_width);
return min_size;
}
void LocationBarView::Layout() {
if (!IsInitialized())
return;
selected_keyword_view_->SetVisible(false);
location_icon_view_->SetVisible(false);
keyword_hint_view_->SetVisible(false);
constexpr int item_padding = kHorizontalPadding;
LocationBarLayout leading_decorations(
LocationBarLayout::LEFT_EDGE, item_padding,
item_padding - omnibox_view_->GetInsets().left());
LocationBarLayout trailing_decorations(
LocationBarLayout::RIGHT_EDGE, item_padding,
item_padding - omnibox_view_->GetInsets().right());
const base::string16 keyword(omnibox_view_->model()->keyword());
// In some cases (e.g. fullscreen mode) we may have 0 height. We still want
// to position our child views in this case, because other things may be
// positioned relative to them (e.g. the "bookmark added" bubble if the user
// hits ctrl-d).
const int vertical_padding = GetTotalVerticalPadding();
const int location_height = std::max(height() - (vertical_padding * 2), 0);
location_icon_view_->SetLabel(base::string16());
if (ShouldShowKeywordBubble()) {
leading_decorations.AddDecoration(vertical_padding, location_height, true,
0, 0, item_padding,
selected_keyword_view_);
if (selected_keyword_view_->keyword() != keyword) {
selected_keyword_view_->SetKeyword(keyword);
const TemplateURL* template_url =
TemplateURLServiceFactory::GetForProfile(profile())->
GetTemplateURLForKeyword(keyword);
if (template_url &&
(template_url->type() == TemplateURL::OMNIBOX_API_EXTENSION)) {
gfx::Image image = extensions::OmniboxAPI::Get(profile())->
GetOmniboxIcon(template_url->GetExtensionId());
selected_keyword_view_->SetImage(image.AsImageSkia());
} else {
selected_keyword_view_->ResetImage();
}
}
} else if (ShouldShowLocationIconText()) {
location_icon_view_->SetLabel(GetLocationIconText());
// The largest fraction of the omnibox that can be taken by the EV bubble.
const double kMaxBubbleFraction = 0.5;
leading_decorations.AddDecoration(vertical_padding, location_height, false,
kMaxBubbleFraction, 0, item_padding,
location_icon_view_);
} else {
leading_decorations.AddDecoration(vertical_padding, location_height,
location_icon_view_);
}
if (star_view_->visible()) {
trailing_decorations.AddDecoration(vertical_padding, location_height,
star_view_);
}
if (translate_icon_view_->visible()) {
trailing_decorations.AddDecoration(vertical_padding, location_height,
translate_icon_view_);
}
if (open_pdf_in_reader_view_->visible()) {
trailing_decorations.AddDecoration(vertical_padding, location_height,
open_pdf_in_reader_view_);
}
if (save_credit_card_icon_view_->visible()) {
trailing_decorations.AddDecoration(vertical_padding, location_height,
save_credit_card_icon_view_);
}
if (manage_passwords_icon_view_->visible()) {
trailing_decorations.AddDecoration(vertical_padding, location_height,
manage_passwords_icon_view_);
}
for (const auto& action_view : page_action_views_) {
if (action_view->visible()) {
trailing_decorations.AddDecoration(vertical_padding, location_height,
action_view.get());
}
}
if (zoom_view_->visible()) {
trailing_decorations.AddDecoration(vertical_padding, location_height,
zoom_view_);
}
for (ContentSettingViews::const_reverse_iterator i(
content_setting_views_.rbegin()); i != content_setting_views_.rend();
++i) {
if ((*i)->visible()) {
trailing_decorations.AddDecoration(vertical_padding, location_height,
false, 0, item_padding, item_padding,
*i);
}
}
// Because IMEs may eat the tab key, we don't show "press tab to search" while
// IME composition is in progress.
if (!keyword.empty() && omnibox_view_->model()->is_keyword_hint() &&
!omnibox_view_->IsImeComposing()) {
trailing_decorations.AddDecoration(vertical_padding, location_height, true,
0, item_padding, item_padding,
keyword_hint_view_);
if (keyword_hint_view_->keyword() != keyword)
keyword_hint_view_->SetKeyword(keyword);
}
const int edge_thickness = GetHorizontalEdgeThickness();
// Perform layout.
int full_width = width() - (2 * edge_thickness);
int entry_width = full_width;
leading_decorations.LayoutPass1(&entry_width);
trailing_decorations.LayoutPass1(&entry_width);
leading_decorations.LayoutPass2(&entry_width);
trailing_decorations.LayoutPass2(&entry_width);
int location_needed_width = omnibox_view_->GetTextWidth();
int available_width = entry_width - location_needed_width;
// The bounds must be wide enough for all the decorations to fit.
gfx::Rect location_bounds(edge_thickness, vertical_padding,
std::max(full_width, full_width - entry_width),
location_height);
leading_decorations.LayoutPass3(&location_bounds, &available_width);
trailing_decorations.LayoutPass3(&location_bounds, &available_width);
// Layout |ime_inline_autocomplete_view_| next to the user input.
if (ime_inline_autocomplete_view_->visible()) {
int width =
gfx::GetStringWidth(ime_inline_autocomplete_view_->text(),
ime_inline_autocomplete_view_->font_list()) +
ime_inline_autocomplete_view_->GetInsets().width();
// All the target languages (IMEs) are LTR, and we do not need to support
// RTL so far. In other words, no testable RTL environment so far.
int x = location_needed_width;
if (width > entry_width)
x = 0;
else if (location_needed_width + width > entry_width)
x = entry_width - width;
location_bounds.set_width(x);
ime_inline_autocomplete_view_->SetBounds(
location_bounds.right(), location_bounds.y(),
std::min(width, entry_width), location_bounds.height());
}
omnibox_view_->SetBoundsRect(location_bounds);
}
void LocationBarView::OnNativeThemeChanged(const ui::NativeTheme* theme) {
RefreshLocationIcon();
if (!is_popup_mode_) {
set_background(
new BackgroundWith1PxBorder(GetColor(BACKGROUND), kBorderColor));
}
}
void LocationBarView::Update(const WebContents* contents) {
RefreshContentSettingViews();
RefreshZoomView();
RefreshPageActionViews();
RefreshTranslateIcon();
RefreshSaveCreditCardIconView();
RefreshManagePasswordsIconView();
WebContents* web_contents_for_sub_views =
GetToolbarModel()->input_in_progress() ? nullptr : GetWebContents();
open_pdf_in_reader_view_->Update(web_contents_for_sub_views);
if (star_view_)
UpdateBookmarkStarVisibility();
if (contents)
omnibox_view_->OnTabChanged(contents);
else
omnibox_view_->Update();
location_icon_view_->SetTextVisibility(
ShouldShowLocationIconText(),
!contents && ShouldAnimateLocationIconTextVisibilityChange());
OnChanged(); // NOTE: Calls Layout().
}
void LocationBarView::ResetTabState(WebContents* contents) {
omnibox_view_->ResetTabState(contents);
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, public OmniboxEditController implementation:
void LocationBarView::UpdateWithoutTabRestore() {
Update(nullptr);
}
ToolbarModel* LocationBarView::GetToolbarModel() {
return delegate_->GetToolbarModel();
}
WebContents* LocationBarView::GetWebContents() {
return delegate_->GetWebContents();
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, private:
int LocationBarView::IncrementalMinimumWidth(views::View* view) const {
return view->visible() ? (kHorizontalPadding + view->GetMinimumSize().width())
: 0;
}
int LocationBarView::GetHorizontalEdgeThickness() const {
return is_popup_mode_
? 0
: BackgroundWith1PxBorder::kLocationBarBorderThicknessDip;
}
int LocationBarView::GetTotalVerticalPadding() const {
constexpr int kInteriorPadding = 1;
return BackgroundWith1PxBorder::kLocationBarBorderThicknessDip +
kInteriorPadding;
}
void LocationBarView::RefreshLocationIcon() {
// |omnibox_view_| may not be ready yet if Init() has not been called. The
// icon will be set soon by OnChanged().
if (!omnibox_view_)
return;
security_state::SecurityLevel security_level =
GetToolbarModel()->GetSecurityLevel(false);
SkColor icon_color = (security_level == security_state::NONE ||
security_level == security_state::HTTP_SHOW_WARNING)
? color_utils::DeriveDefaultIconColor(GetColor(TEXT))
: GetSecureTextColor(security_level);
location_icon_view_->SetImage(gfx::CreateVectorIcon(
omnibox_view_->GetVectorIcon(), kIconWidth, icon_color));
}
bool LocationBarView::RefreshContentSettingViews() {
bool visibility_changed = false;
for (ContentSettingViews::const_iterator i(content_setting_views_.begin());
i != content_setting_views_.end(); ++i) {
const bool was_visible = (*i)->visible();
(*i)->Update(GetToolbarModel()->input_in_progress() ? nullptr
: GetWebContents());
if (was_visible != (*i)->visible())
visibility_changed = true;
}
return visibility_changed;
}
void LocationBarView::DeletePageActionViews() {
for (const auto& action_view : page_action_views_)
RemoveChildView(action_view.get());
page_action_views_.clear();
}
bool LocationBarView::RefreshPageActionViews() {
if (is_popup_mode_)
return false;
bool changed = false;
PageActions new_page_actions;
WebContents* web_contents = GetWebContents();
if (web_contents) {
extensions::TabHelper* extensions_tab_helper =
extensions::TabHelper::FromWebContents(web_contents);
extensions::LocationBarController* controller =
extensions_tab_helper->location_bar_controller();
new_page_actions = controller->GetCurrentActions();
}
web_contents_null_at_last_refresh_ = web_contents == nullptr;
// On startup we sometimes haven't loaded any extensions. This makes sure
// we catch up when the extensions (and any page actions) load.
if (PageActionsDiffer(new_page_actions)) {
changed = true;
DeletePageActionViews();
// Create the page action views.
for (PageActions::const_iterator i = new_page_actions.begin();
i != new_page_actions.end(); ++i) {
std::unique_ptr<PageActionWithBadgeView> page_action_view =
base::MakeUnique<PageActionWithBadgeView>(
delegate_->CreatePageActionImageView(this, *i));
page_action_view->SetVisible(false);
page_action_views_.push_back(std::move(page_action_view));
}
View* right_anchor = open_pdf_in_reader_view_;
if (!right_anchor)
right_anchor = star_view_;
DCHECK(right_anchor);
// |page_action_views_| are ordered right-to-left. Add them as children in
// reverse order so the logical order and visual order match for
// accessibility purposes.
for (PageActionViews::reverse_iterator i = page_action_views_.rbegin();
i != page_action_views_.rend(); ++i)
AddChildViewAt(i->get(), GetIndexOf(right_anchor));
}
for (const auto& action_view : page_action_views_) {
bool old_visibility = action_view->visible();
action_view->UpdateVisibility(
GetToolbarModel()->input_in_progress() ? nullptr : web_contents);
changed |= old_visibility != action_view->visible();
}
return changed;
}
bool LocationBarView::PageActionsDiffer(
const PageActions& page_actions) const {
if (page_action_views_.size() != page_actions.size())
return true;
for (size_t index = 0; index < page_actions.size(); ++index) {
PageActionWithBadgeView* view = page_action_views_[index].get();
if (view->image_view()->extension_action() != page_actions[index])
return true;
}
return false;
}
bool LocationBarView::RefreshZoomView() {
DCHECK(zoom_view_);
WebContents* web_contents = GetWebContents();
if (!web_contents)
return false;
const bool was_visible = zoom_view_->visible();
zoom_view_->Update(zoom::ZoomController::FromWebContents(web_contents));
if (!zoom_view_->visible())
ZoomBubbleView::CloseCurrentBubble();
return was_visible != zoom_view_->visible();
}
void LocationBarView::OnDefaultZoomLevelChanged() {
RefreshZoomView();
}
bool LocationBarView::RefreshSaveCreditCardIconView() {
WebContents* web_contents = GetWebContents();
if (!web_contents)
return false;
const bool was_visible = save_credit_card_icon_view_->visible();
// |controller| may be nullptr due to lazy initialization.
autofill::SaveCardBubbleControllerImpl* controller =
autofill::SaveCardBubbleControllerImpl::FromWebContents(web_contents);
bool enabled = controller && controller->IsIconVisible();
command_updater()->UpdateCommandEnabled(IDC_SAVE_CREDIT_CARD_FOR_PAGE,
enabled);
save_credit_card_icon_view_->SetVisible(enabled);
return was_visible != save_credit_card_icon_view_->visible();
}
void LocationBarView::RefreshTranslateIcon() {
WebContents* web_contents = GetWebContents();
if (!web_contents)
return;
translate::LanguageState& language_state =
ChromeTranslateClient::FromWebContents(web_contents)->GetLanguageState();
bool enabled = language_state.translate_enabled();
command_updater()->UpdateCommandEnabled(IDC_TRANSLATE_PAGE, enabled);
translate_icon_view_->SetVisible(enabled);
if (!enabled)
TranslateBubbleView::CloseCurrentBubble();
}
bool LocationBarView::RefreshManagePasswordsIconView() {
DCHECK(manage_passwords_icon_view_);
WebContents* web_contents = GetWebContents();
if (!web_contents)
return false;
const bool was_visible = manage_passwords_icon_view_->visible();
ManagePasswordsUIController::FromWebContents(
web_contents)->UpdateIconAndBubbleState(manage_passwords_icon_view_);
return was_visible != manage_passwords_icon_view_->visible();
}
void LocationBarView::ShowFirstRunBubbleInternal() {
// First run bubble doesn't make sense for Chrome OS.
#if !defined(OS_CHROMEOS)
WebContents* web_contents = delegate_->GetWebContents();
if (!web_contents)
return;
Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
if (browser)
FirstRunBubble::ShowBubble(browser, location_icon_view_);
#endif
}
base::string16 LocationBarView::GetLocationIconText() const {
const base::string16 extension_name = GetExtensionName(
GetToolbarModel()->GetURL(), delegate_->GetWebContents());
if (!extension_name.empty())
return extension_name;
bool has_ev_cert =
(GetToolbarModel()->GetSecurityLevel(false) == security_state::EV_SECURE);
return has_ev_cert ? GetToolbarModel()->GetEVCertName()
: GetToolbarModel()->GetSecureVerboseText();
}
bool LocationBarView::ShouldShowKeywordBubble() const {
return !omnibox_view_->model()->keyword().empty() &&
!omnibox_view_->model()->is_keyword_hint();
}
bool LocationBarView::ShouldShowLocationIconText() const {
if (!GetOmniboxView()->IsEditingOrEmpty() &&
GetToolbarModel()->GetURL().SchemeIs(extensions::kExtensionScheme))
return true;
using SecurityLevel = security_state::SecurityLevel;
const SecurityLevel level = GetToolbarModel()->GetSecurityLevel(false);
return level == SecurityLevel::EV_SECURE || level == SecurityLevel::SECURE ||
level == SecurityLevel::DANGEROUS ||
level == SecurityLevel::HTTP_SHOW_WARNING;
}
bool LocationBarView::ShouldAnimateLocationIconTextVisibilityChange() const {
// Text for extension URLs should not be animated (their security level is
// SecurityLevel::NONE).
using SecurityLevel = security_state::SecurityLevel;
SecurityLevel level = GetToolbarModel()->GetSecurityLevel(false);
return level == SecurityLevel::DANGEROUS ||
level == SecurityLevel::HTTP_SHOW_WARNING;
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, private LocationBar implementation:
void LocationBarView::ShowFirstRunBubble() {
// Wait until search engines have loaded to show the first run bubble.
TemplateURLService* url_service =
TemplateURLServiceFactory::GetForProfile(profile());
if (!url_service->loaded()) {
template_url_service_ = url_service;
template_url_service_->AddObserver(this);
template_url_service_->Load();
return;
}
ShowFirstRunBubbleInternal();
}
GURL LocationBarView::GetDestinationURL() const {
return destination_url();
}
WindowOpenDisposition LocationBarView::GetWindowOpenDisposition() const {
return disposition();
}
ui::PageTransition LocationBarView::GetPageTransition() const {
return transition();
}
void LocationBarView::AcceptInput() {
omnibox_view_->model()->AcceptInput(WindowOpenDisposition::CURRENT_TAB,
false);
}
void LocationBarView::FocusSearch() {
omnibox_view_->SetFocus();
omnibox_view_->EnterKeywordModeForDefaultSearchProvider();
}
void LocationBarView::UpdateContentSettingsIcons() {
if (RefreshContentSettingViews()) {
Layout();
SchedulePaint();
}
}
void LocationBarView::UpdateManagePasswordsIconAndBubble() {
if (RefreshManagePasswordsIconView()) {
Layout();
SchedulePaint();
}
}
void LocationBarView::UpdateSaveCreditCardIcon() {
if (RefreshSaveCreditCardIconView()) {
Layout();
SchedulePaint();
}
}
void LocationBarView::UpdatePageActions() {
if (RefreshPageActionViews()) { // Changed.
Layout();
SchedulePaint();
}
}
void LocationBarView::UpdateBookmarkStarVisibility() {
if (star_view_) {
star_view_->SetVisible(
browser_defaults::bookmarks_enabled && !is_popup_mode_ &&
!GetToolbarModel()->input_in_progress() &&
edit_bookmarks_enabled_.GetValue() &&
!IsBookmarkStarHiddenByExtension());
}
}
void LocationBarView::UpdateLocationBarVisibility(bool visible, bool animate) {
if (!animate) {
size_animation_.Reset(visible ? 1 : 0);
return;
}
if (visible)
size_animation_.Show();
else
size_animation_.Hide();
}
bool LocationBarView::ShowPageActionPopup(
const extensions::Extension* extension,
bool grant_tab_permissions) {
ExtensionAction* extension_action =
extensions::ExtensionActionManager::Get(profile())->GetPageAction(
*extension);
CHECK(extension_action);
PageActionWithBadgeView* page_action_view =
GetPageActionView(extension_action);
if (!page_action_view) {
CHECK(!web_contents_null_at_last_refresh_);
CHECK(!is_popup_mode_);
CHECK(!extensions::FeatureSwitch::extension_action_redesign()->IsEnabled());
CHECK(false);
}
PageActionImageView* page_action_image_view = page_action_view->image_view();
CHECK(page_action_image_view);
ExtensionActionViewController* extension_action_view_controller =
page_action_image_view->view_controller();
CHECK(extension_action_view_controller);
return extension_action_view_controller->ExecuteAction(grant_tab_permissions);
}
void LocationBarView::UpdateOpenPDFInReaderPrompt() {
open_pdf_in_reader_view_->Update(
GetToolbarModel()->input_in_progress() ? nullptr : GetWebContents());
Layout();
SchedulePaint();
}
void LocationBarView::SaveStateToContents(WebContents* contents) {
omnibox_view_->SaveStateToTab(contents);
}
const OmniboxView* LocationBarView::GetOmniboxView() const {
return omnibox_view_;
}
LocationBarTesting* LocationBarView::GetLocationBarForTesting() {
return this;
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, private LocationBarTesting implementation:
int LocationBarView::PageActionCount() {
return page_action_views_.size();
}
int LocationBarView::PageActionVisibleCount() {
int result = 0;
for (const auto& action_view : page_action_views_) {
if (action_view->visible())
++result;
}
return result;
}
ExtensionAction* LocationBarView::GetPageAction(size_t index) {
if (index < page_action_views_.size())
return page_action_views_[index]->image_view()->extension_action();
NOTREACHED();
return nullptr;
}
ExtensionAction* LocationBarView::GetVisiblePageAction(size_t index) {
size_t current = 0;
for (const auto& action_view : page_action_views_) {
if (action_view->visible()) {
if (current == index)
return action_view->image_view()->extension_action();
++current;
}
}
NOTREACHED();
return nullptr;
}
void LocationBarView::TestPageActionPressed(size_t index) {
size_t current = 0;
for (const auto& action_view : page_action_views_) {
if (action_view->visible()) {
if (current == index) {
action_view->image_view()->view_controller()->ExecuteAction(true);
return;
}
++current;
}
}
NOTREACHED();
}
bool LocationBarView::GetBookmarkStarVisibility() {
DCHECK(star_view_);
return star_view_->visible();
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, private views::View implementation:
const char* LocationBarView::GetClassName() const {
return kViewClassName;
}
void LocationBarView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
OmniboxPopupView* popup = omnibox_view_->model()->popup_model()->view();
if (popup->IsOpen())
popup->UpdatePopupAppearance();
}
void LocationBarView::OnFocus() {
omnibox_view_->SetFocus();
}
void LocationBarView::OnPaint(gfx::Canvas* canvas) {
View::OnPaint(canvas);
if (show_focus_rect_ && omnibox_view_->HasFocus()) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(GetNativeTheme()->GetSystemColor(
ui::NativeTheme::NativeTheme::kColorId_FocusedBorderColor));
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(1);
gfx::RectF focus_rect(GetLocalBounds());
focus_rect.Inset(gfx::InsetsF(0.5f));
canvas->DrawRoundRect(focus_rect,
BackgroundWith1PxBorder::kCornerRadius + 0.5f, paint);
}
if (!is_popup_mode_)
return; // The background and border are painted by our Background.
// Fill the location bar background color behind the border. Parts of the
// border images are meant to rest atop the toolbar background and parts atop
// the omnibox background, so we can't just blindly fill our entire bounds.
gfx::Rect bounds(GetContentsBounds());
bounds.Inset(GetHorizontalEdgeThickness(),
is_popup_mode_
? 0
: BackgroundWith1PxBorder::kLocationBarBorderThicknessDip);
SkColor background_color(GetColor(BACKGROUND));
canvas->FillRect(bounds, background_color);
const SkColor border_color = GetBorderColor(profile()->IsOffTheRecord());
BrowserView::Paint1pxHorizontalLine(canvas, border_color, bounds, false);
BrowserView::Paint1pxHorizontalLine(canvas, border_color, bounds, true);
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, private views::DragController implementation:
void LocationBarView::WriteDragDataForView(views::View* sender,
const gfx::Point& press_pt,
OSExchangeData* data) {
DCHECK_NE(GetDragOperationsForView(sender, press_pt),
ui::DragDropTypes::DRAG_NONE);
WebContents* web_contents = GetWebContents();
favicon::FaviconDriver* favicon_driver =
favicon::ContentFaviconDriver::FromWebContents(web_contents);
gfx::ImageSkia favicon = favicon_driver->GetFavicon().AsImageSkia();
button_drag_utils::SetURLAndDragImage(web_contents->GetURL(),
web_contents->GetTitle(), favicon,
nullptr, data, sender->GetWidget());
}
int LocationBarView::GetDragOperationsForView(views::View* sender,
const gfx::Point& p) {
DCHECK_EQ(location_icon_view_, sender);
WebContents* web_contents = delegate_->GetWebContents();
return (web_contents && web_contents->GetURL().is_valid() &&
(!GetOmniboxView()->IsEditingOrEmpty())) ?
(ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK) :
ui::DragDropTypes::DRAG_NONE;
}
bool LocationBarView::CanStartDragForView(View* sender,
const gfx::Point& press_pt,
const gfx::Point& p) {
return true;
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, private gfx::AnimationDelegate implementation:
void LocationBarView::AnimationProgressed(const gfx::Animation* animation) {
GetWidget()->non_client_view()->Layout();
}
void LocationBarView::AnimationEnded(const gfx::Animation* animation) {
AnimationProgressed(animation);
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, private OmniboxEditController implementation:
void LocationBarView::OnChanged() {
RefreshLocationIcon();
location_icon_view_->set_show_tooltip(!GetOmniboxView()->IsEditingOrEmpty());
Layout();
SchedulePaint();
}
const ToolbarModel* LocationBarView::GetToolbarModel() const {
return delegate_->GetToolbarModel();
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, private DropdownBarHostDelegate implementation:
void LocationBarView::SetFocusAndSelection(bool select_all) {
FocusLocation(select_all);
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, private TemplateURLServiceObserver implementation:
void LocationBarView::OnTemplateURLServiceChanged() {
template_url_service_->RemoveObserver(this);
template_url_service_ = nullptr;
// If the browser is no longer active, let's not show the info bubble, as this
// would make the browser the active window again.
if (omnibox_view_ && omnibox_view_->GetWidget()->IsActive())
ShowFirstRunBubble();
}