blob: 9fe383741657c10916b0a706e7ec2a04e15227b0 [file] [log] [blame]
// Copyright 2013 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/toolbar/toolbar_view.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/containers/fixed_flat_map.h"
#include "base/feature_list.h"
#include "base/i18n/number_formatting.h"
#include "base/metrics/user_metrics.h"
#include "base/ranges/algorithm.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_command_controller.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/global_error/global_error_service.h"
#include "chrome/browser/ui/global_error/global_error_service_factory.h"
#include "chrome/browser/ui/intent_picker_tab_helper.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/chrome_labs_prefs.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/view_ids.h"
#include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h"
#include "chrome/browser/ui/views/extensions/extension_popup.h"
#include "chrome/browser/ui/views/extensions/extensions_side_panel_controller.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
#include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/top_container_background.h"
#include "chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h"
#include "chrome/browser/ui/views/location_bar/star_view.h"
#include "chrome/browser/ui/views/media_router/cast_toolbar_button.h"
#include "chrome/browser/ui/views/page_action/page_action_icon_container.h"
#include "chrome/browser/ui/views/page_action/page_action_icon_controller.h"
#include "chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_toolbar_icon_view.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/browser/ui/views/toolbar/app_menu.h"
#include "chrome/browser/ui/views/toolbar/back_forward_button.h"
#include "chrome/browser/ui/views/toolbar/browser_app_menu_button.h"
#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h"
#include "chrome/browser/ui/views/toolbar/chrome_labs_button.h"
#include "chrome/browser/ui/views/toolbar/home_button.h"
#include "chrome/browser/ui/views/toolbar/read_later_toolbar_button.h"
#include "chrome/browser/ui/views/toolbar/reload_button.h"
#include "chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/upgrade_detector/upgrade_detector.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/theme_resources.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/omnibox/browser/omnibox_view.h"
#include "components/prefs/pref_service.h"
#include "components/reading_list/features/reading_list_switches.h"
#include "components/send_tab_to_self/features.h"
#include "components/signin/public/base/signin_buildflags.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "media/base/media_switches.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/theme_provider.h"
#include "ui/base/window_open_disposition.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/native_theme/native_theme_aura.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/cascading_property.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/tooltip_manager.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/non_client_view.h"
#if defined(OS_WIN) || defined(OS_MAC)
#include "chrome/browser/recovery/recovery_install_global_error_factory.h"
#endif
#if defined(OS_WIN)
#include "chrome/browser/ui/views/critical_notification_bubble_view.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#else
#include "chrome/browser/signin/signin_global_error_factory.h"
#include "chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h"
#include "chrome/browser/ui/views/outdated_upgrade_bubble_view.h"
#endif
#if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
#include "chrome/browser/ui/views/frame/webui_tab_strip_container_view.h"
#endif // BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
#if defined(USE_AURA)
#include "ui/aura/window_occlusion_tracker.h"
#endif
using base::UserMetricsAction;
using content::WebContents;
namespace {
// Gets the display mode for a given browser.
ToolbarView::DisplayMode GetDisplayMode(Browser* browser) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (browser->is_type_custom_tab())
return ToolbarView::DisplayMode::CUSTOM_TAB;
#endif
// Checked in this order because even tabbed PWAs use the CUSTOM_TAB
// display mode.
if (web_app::AppBrowserController::IsWebApp(browser))
return ToolbarView::DisplayMode::CUSTOM_TAB;
if (browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP))
return ToolbarView::DisplayMode::NORMAL;
return ToolbarView::DisplayMode::LOCATION;
}
auto& GetViewCommandMap() {
static constexpr auto kViewCommandMap = base::MakeFixedFlatMap<int, int>(
{{VIEW_ID_BACK_BUTTON, IDC_BACK},
{VIEW_ID_FORWARD_BUTTON, IDC_FORWARD},
{VIEW_ID_HOME_BUTTON, IDC_HOME},
{VIEW_ID_RELOAD_BUTTON, IDC_RELOAD},
{VIEW_ID_AVATAR_BUTTON, IDC_SHOW_AVATAR_MENU}});
return kViewCommandMap;
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, public:
ToolbarView::ToolbarView(Browser* browser, BrowserView* browser_view)
: AnimationDelegateViews(this),
browser_(browser),
browser_view_(browser_view),
app_menu_icon_controller_(browser->profile(), this),
display_mode_(GetDisplayMode(browser)) {
SetID(VIEW_ID_TOOLBAR);
UpgradeDetector::GetInstance()->AddObserver(this);
if (display_mode_ == DisplayMode::NORMAL) {
SetBackground(std::make_unique<TopContainerBackground>(browser_view));
for (const auto& view_and_command : GetViewCommandMap())
chrome::AddCommandObserver(browser_, view_and_command.second, this);
}
views::SetCascadingThemeProviderColor(this, views::kCascadingBackgroundColor,
ThemeProperties::COLOR_TOOLBAR);
}
ToolbarView::~ToolbarView() {
UpgradeDetector::GetInstance()->RemoveObserver(this);
if (display_mode_ != DisplayMode::NORMAL)
return;
for (const auto& view_and_command : GetViewCommandMap())
chrome::RemoveCommandObserver(browser_, view_and_command.second, this);
}
void ToolbarView::Init() {
#if defined(USE_AURA)
// Avoid generating too many occlusion tracking calculation events before this
// function returns. The occlusion status will be computed only once once this
// function returns.
// See crbug.com/1183894#c2
aura::WindowOcclusionTracker::ScopedPause pause_occlusion;
#endif
auto location_bar = std::make_unique<LocationBarView>(
browser_, browser_->profile(), browser_->command_controller(), this,
display_mode_ != DisplayMode::NORMAL);
// Make sure the toolbar shows by default.
size_animation_.Reset(1);
if (display_mode_ != DisplayMode::NORMAL) {
location_bar_ = AddChildView(std::move(location_bar));
location_bar_->Init();
if (display_mode_ == DisplayMode::CUSTOM_TAB) {
custom_tab_bar_ =
AddChildView(std::make_unique<CustomTabBarView>(browser_view_, this));
}
SetLayoutManager(std::make_unique<views::FillLayout>());
initialized_ = true;
return;
}
const auto callback = [](Browser* browser, int command,
const ui::Event& event) {
chrome::ExecuteCommandWithDisposition(
browser, command, ui::DispositionFromEventFlags(event.flags()));
};
std::unique_ptr<ToolbarButton> back = std::make_unique<BackForwardButton>(
BackForwardButton::Direction::kBack,
base::BindRepeating(callback, browser_, IDC_BACK), browser_);
std::unique_ptr<ToolbarButton> forward = std::make_unique<BackForwardButton>(
BackForwardButton::Direction::kForward,
base::BindRepeating(callback, browser_, IDC_FORWARD), browser_);
std::unique_ptr<ReloadButton> reload =
std::make_unique<ReloadButton>(browser_->command_controller());
std::unique_ptr<HomeButton> home = std::make_unique<HomeButton>(
base::BindRepeating(callback, browser_, IDC_HOME), browser_);
std::unique_ptr<ExtensionsToolbarContainer> extensions_container;
// Do not create the extensions or browser actions container if it is a guest
// profile (only regular and incognito profiles host extensions).
if (!browser_->profile()->IsGuestSession()) {
extensions_container =
std::make_unique<ExtensionsToolbarContainer>(browser_);
}
std::unique_ptr<media_router::CastToolbarButton> cast;
if (media_router::MediaRouterEnabled(browser_->profile()))
cast = media_router::CastToolbarButton::Create(browser_);
std::unique_ptr<MediaToolbarButtonView> media_button;
if (base::FeatureList::IsEnabled(media::kGlobalMediaControls)) {
media_button = std::make_unique<MediaToolbarButtonView>(browser_view_);
}
std::unique_ptr<send_tab_to_self::SendTabToSelfToolbarIconView>
send_tab_to_self_button;
if (base::FeatureList::IsEnabled(send_tab_to_self::kSendTabToSelfV2) &&
!browser_->profile()->IsOffTheRecord()) {
send_tab_to_self_button =
std::make_unique<send_tab_to_self::SendTabToSelfToolbarIconView>(
browser_view_);
}
std::unique_ptr<ToolbarAccountIconContainerView>
toolbar_account_icon_container;
bool show_avatar_toolbar_button = true;
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (!base::FeatureList::IsEnabled(chromeos::features::kAvatarToolbarButton)) {
// ChromeOS only badges Incognito and Guest icons in the browser window.
show_avatar_toolbar_button = browser_->profile()->IsOffTheRecord() ||
browser_->profile()->IsGuestSession();
}
#endif
if (base::FeatureList::IsEnabled(
autofill::features::kAutofillEnableToolbarStatusChip)) {
// The avatar button is contained inside the page-action container and
// should not be created twice.
show_avatar_toolbar_button = false;
toolbar_account_icon_container =
std::make_unique<ToolbarAccountIconContainerView>(browser_view_);
}
std::unique_ptr<ReadLaterToolbarButton> read_later_button;
if (browser_view_->right_aligned_side_panel() &&
reading_list::switches::IsReadingListEnabled()) {
read_later_button = std::make_unique<ReadLaterToolbarButton>(browser_);
}
// Always add children in order from left to right, for accessibility.
if (browser_view_->extensions_side_panel_controller()) {
left_side_panel_button_ =
AddChildView(browser_view_->extensions_side_panel_controller()
->CreateToolbarButton());
}
back_ = AddChildView(std::move(back));
forward_ = AddChildView(std::move(forward));
reload_ = AddChildView(std::move(reload));
home_ = AddChildView(std::move(home));
location_bar_ = AddChildView(std::move(location_bar));
if (extensions_container)
extensions_container_ = AddChildView(std::move(extensions_container));
if (base::FeatureList::IsEnabled(features::kChromeLabs)) {
chrome_labs_model_ = std::make_unique<ChromeLabsBubbleViewModel>();
if (ChromeLabsButton::ShouldShowButton(chrome_labs_model_.get(),
browser_->profile())) {
chrome_labs_button_ = AddChildView(std::make_unique<ChromeLabsButton>(
browser_view_, chrome_labs_model_.get()));
show_chrome_labs_button_.Init(
chrome_labs_prefs::kBrowserLabsEnabled,
browser_->profile()->GetPrefs(),
base::BindRepeating(&ToolbarView::OnChromeLabsPrefChanged,
base::Unretained(this)));
// Set the visibility for the button based on initial enterprise policy
// value. Only call OnChromeLabsPrefChanged if there is a change from the
// initial value.
chrome_labs_button_->SetVisible(show_chrome_labs_button_.GetValue());
}
}
if (cast)
cast_ = AddChildView(std::move(cast));
if (media_button)
media_button_ = AddChildView(std::move(media_button));
if (send_tab_to_self_button)
send_tab_to_self_button_ = AddChildView(std::move(send_tab_to_self_button));
if (read_later_button)
read_later_button_ = AddChildView(std::move(read_later_button));
if (toolbar_account_icon_container) {
toolbar_account_icon_container_ =
AddChildView(std::move(toolbar_account_icon_container));
avatar_ = toolbar_account_icon_container_->avatar_button();
} else {
// TODO(crbug.com/932818): Remove this once the
// |kAutofillEnableToolbarStatusChip| is fully launched.
avatar_ =
AddChildView(std::make_unique<AvatarToolbarButton>(browser_view_));
avatar_->SetVisible(show_avatar_toolbar_button);
}
auto app_menu_button = std::make_unique<BrowserAppMenuButton>(this);
app_menu_button->SetFlipCanvasOnPaintForRTLUI(true);
app_menu_button->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_ACCNAME_APP));
app_menu_button->SetTooltipText(
l10n_util::GetStringUTF16(IDS_APPMENU_TOOLTIP));
app_menu_button->SetID(VIEW_ID_APP_MENU);
app_menu_button_ = AddChildView(std::move(app_menu_button));
LoadImages();
// Start global error services now so we set the icon on the menu correctly.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
SigninGlobalErrorFactory::GetForProfile(browser_->profile());
#if defined(OS_WIN) || defined(OS_MAC)
RecoveryInstallGlobalErrorFactory::GetForProfile(browser_->profile());
#endif
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
// Set the button icon based on the system state. Do this after
// |app_menu_button_| has been added as a bubble may be shown that needs
// the widget (widget found by way of app_menu_button_->GetWidget()).
app_menu_icon_controller_.UpdateDelegate();
location_bar_->Init();
show_home_button_.Init(
prefs::kShowHomeButton, browser_->profile()->GetPrefs(),
base::BindRepeating(&ToolbarView::OnShowHomeButtonChanged,
base::Unretained(this)));
home_->SetVisible(show_home_button_.GetValue());
InitLayout();
for (auto* button : std::array<views::Button*, 5>{back_, forward_, reload_,
home_, avatar_}) {
if (button)
button->set_tag(GetViewCommandMap().at(button->GetID()));
}
initialized_ = true;
}
void ToolbarView::AnimationEnded(const gfx::Animation* animation) {
if (animation->GetCurrentValue() == 0)
SetToolbarVisibility(false);
browser()->window()->ToolbarSizeChanged(/*is_animating=*/false);
}
void ToolbarView::AnimationProgressed(const gfx::Animation* animation) {
browser()->window()->ToolbarSizeChanged(/*is_animating=*/true);
}
void ToolbarView::Update(WebContents* tab) {
if (location_bar_)
location_bar_->Update(tab);
if (extensions_container_)
extensions_container_->UpdateAllIcons();
if (reload_)
reload_->SetMenuEnabled(chrome::IsDebuggerAttachedToCurrentTab(browser_));
if (toolbar_account_icon_container_)
toolbar_account_icon_container_->UpdateAllIcons();
}
void ToolbarView::SetToolbarVisibility(bool visible) {
SetVisible(visible);
views::View* bar = display_mode_ == DisplayMode::CUSTOM_TAB
? static_cast<views::View*>(custom_tab_bar_)
: static_cast<views::View*>(location_bar_);
bar->SetVisible(visible);
}
void ToolbarView::UpdateCustomTabBarVisibility(bool visible, bool animate) {
DCHECK_EQ(display_mode_, DisplayMode::CUSTOM_TAB);
if (!animate) {
size_animation_.Reset(visible ? 1.0 : 0.0);
SetToolbarVisibility(visible);
browser()->window()->ToolbarSizeChanged(/*is_animating=*/false);
return;
}
if (visible) {
SetToolbarVisibility(true);
size_animation_.Show();
} else {
size_animation_.Hide();
}
}
void ToolbarView::UpdateForWebUITabStrip() {
#if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
if (browser_view_->webui_tab_strip() && app_menu_button_) {
const int insertion_index = GetIndexOf(app_menu_button_);
if (!base::FeatureList::IsEnabled(
features::kWebUITabStripNewTabButtonInTabStrip))
AddChildViewAt(browser_view_->webui_tab_strip()->CreateNewTabButton(),
insertion_index);
AddChildViewAt(browser_view_->webui_tab_strip()->CreateTabCounter(),
insertion_index);
LoadImages();
}
#endif // BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
}
void ToolbarView::ResetTabState(WebContents* tab) {
if (location_bar_)
location_bar_->ResetTabState(tab);
}
void ToolbarView::SetPaneFocusAndFocusAppMenu() {
if (app_menu_button_)
SetPaneFocus(app_menu_button_);
}
bool ToolbarView::GetAppMenuFocused() const {
return app_menu_button_ && app_menu_button_->HasFocus();
}
void ToolbarView::ShowIntentPickerBubble(
std::vector<IntentPickerBubbleView::AppInfo> app_info,
bool show_stay_in_chrome,
bool show_remember_selection,
PageActionIconType icon_type,
const absl::optional<url::Origin>& initiating_origin,
IntentPickerResponse callback) {
PageActionIconView* const intent_picker_view =
GetPageActionIconView(icon_type);
if (!intent_picker_view)
return;
IntentPickerBubbleView::ShowBubble(
location_bar(), intent_picker_view, icon_type, GetWebContents(),
std::move(app_info), show_stay_in_chrome, show_remember_selection,
initiating_origin, std::move(callback));
// TODO(knollr): find a way that the icon updates implicitly.
intent_picker_view->Update();
}
void ToolbarView::ShowBookmarkBubble(
const GURL& url,
bool already_bookmarked,
bookmarks::BookmarkBubbleObserver* observer) {
views::View* const anchor_view = location_bar();
PageActionIconView* const bookmark_star_icon =
GetPageActionIconView(PageActionIconType::kBookmarkStar);
std::unique_ptr<BubbleSyncPromoDelegate> delegate;
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// BookmarkBubbleSignInDelegate requires DICE.
delegate = std::make_unique<BookmarkBubbleSignInDelegate>(browser_);
#endif
BookmarkBubbleView::ShowBubble(anchor_view, bookmark_star_icon, observer,
std::move(delegate), browser_->profile(), url,
already_bookmarked);
}
ExtensionsToolbarButton* ToolbarView::GetExtensionsButton() const {
return extensions_container_->extensions_button();
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, LocationBarView::Delegate implementation:
WebContents* ToolbarView::GetWebContents() {
return browser_->tab_strip_model()->GetActiveWebContents();
}
LocationBarModel* ToolbarView::GetLocationBarModel() {
return browser_->location_bar_model();
}
const LocationBarModel* ToolbarView::GetLocationBarModel() const {
return browser_->location_bar_model();
}
ContentSettingBubbleModelDelegate*
ToolbarView::GetContentSettingBubbleModelDelegate() {
return browser_->content_setting_bubble_model_delegate();
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, CommandObserver implementation:
void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
DCHECK(display_mode_ == DisplayMode::NORMAL);
const std::array<views::Button*, 5> kButtons{back_, forward_, reload_, home_,
avatar_};
auto* button = *base::ranges::find(kButtons, id, &views::Button::tag);
DCHECK(button);
button->SetEnabled(enabled);
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, UpgradeObserver implementation:
void ToolbarView::OnOutdatedInstall() {
ShowOutdatedInstallNotification(true);
}
void ToolbarView::OnOutdatedInstallNoAutoUpdate() {
ShowOutdatedInstallNotification(false);
}
void ToolbarView::OnCriticalUpgradeInstalled() {
ShowCriticalNotification();
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, ui::AcceleratorProvider implementation:
bool ToolbarView::GetAcceleratorForCommandId(int command_id,
ui::Accelerator* accelerator) const {
return GetWidget()->GetAccelerator(command_id, accelerator);
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, views::View overrides:
gfx::Size ToolbarView::CalculatePreferredSize() const {
gfx::Size size;
switch (display_mode_) {
case DisplayMode::CUSTOM_TAB:
size = custom_tab_bar_->GetPreferredSize();
break;
case DisplayMode::LOCATION:
size = location_bar_->GetPreferredSize();
break;
case DisplayMode::NORMAL:
size = View::CalculatePreferredSize();
// Because there are odd cases where something causes one of the views in
// the toolbar to report an unreasonable height (see crbug.com/985909), we
// cap the height at the size of known child views (location bar and back
// button) plus margins.
// TODO(crbug.com/1033627): Figure out why the height reports incorrectly
// on some installations.
if (layout_manager_ && location_bar_->GetVisible()) {
const int max_height =
std::max(location_bar_->GetPreferredSize().height(),
back_->GetPreferredSize().height()) +
layout_manager_->interior_margin().height();
size.SetToMin({size.width(), max_height});
}
}
size.set_height(size.height() * size_animation_.GetCurrentValue());
return size;
}
gfx::Size ToolbarView::GetMinimumSize() const {
gfx::Size size;
switch (display_mode_) {
case DisplayMode::CUSTOM_TAB:
size = custom_tab_bar_->GetMinimumSize();
break;
case DisplayMode::LOCATION:
size = location_bar_->GetMinimumSize();
break;
case DisplayMode::NORMAL:
size = View::GetMinimumSize();
// Because there are odd cases where something causes one of the views in
// the toolbar to report an unreasonable height (see crbug.com/985909), we
// cap the height at the size of known child views (location bar and back
// button) plus margins.
// TODO(crbug.com/1033627): Figure out why the height reports incorrectly
// on some installations.
if (layout_manager_ && location_bar_->GetVisible()) {
const int max_height =
std::max(location_bar_->GetMinimumSize().height(),
back_->GetMinimumSize().height()) +
layout_manager_->interior_margin().height();
size.SetToMin({size.width(), max_height});
}
}
size.set_height(size.height() * size_animation_.GetCurrentValue());
return size;
}
void ToolbarView::Layout() {
// If we have not been initialized yet just do nothing.
if (!initialized_)
return;
if (display_mode_ == DisplayMode::CUSTOM_TAB) {
custom_tab_bar_->SetBounds(0, 0, width(),
custom_tab_bar_->GetPreferredSize().height());
location_bar_->SetVisible(false);
return;
}
if (display_mode_ == DisplayMode::LOCATION) {
location_bar_->SetBounds(0, 0, width(),
location_bar_->GetPreferredSize().height());
return;
}
LayoutCommon();
// Call super implementation to ensure layout manager and child layouts
// happen.
AccessiblePaneView::Layout();
}
void ToolbarView::OnThemeChanged() {
views::AccessiblePaneView::OnThemeChanged();
if (!initialized_)
return;
if (display_mode_ == DisplayMode::NORMAL)
LoadImages();
SchedulePaint();
}
bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) {
const views::View* focused_view = focus_manager()->GetFocusedView();
if (focused_view && (focused_view->GetID() == VIEW_ID_OMNIBOX))
return false; // Let the omnibox handle all accelerator events.
return AccessiblePaneView::AcceleratorPressed(accelerator);
}
void ToolbarView::ChildPreferredSizeChanged(views::View* child) {
InvalidateLayout();
if (size() != GetPreferredSize())
PreferredSizeChanged();
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, private:
// Override this so that when the user presses F6 to rotate toolbar panes,
// the location bar gets focus, not the first control in the toolbar - and
// also so that it selects all content in the location bar.
views::View* ToolbarView::GetDefaultFocusableChild() {
return location_bar_;
}
void ToolbarView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
node_data->role = ax::mojom::Role::kToolbar;
}
void ToolbarView::InitLayout() {
const int default_margin = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
// TODO(dfried): rename this constant.
const int location_bar_margin = GetLayoutConstant(TOOLBAR_STANDARD_SPACING);
const views::FlexSpecification account_container_flex_rule =
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kPreferred)
.WithOrder(1);
const views::FlexSpecification location_bar_flex_rule =
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kUnbounded)
.WithOrder(2);
constexpr int kExtensionsFlexOrder = 3;
layout_manager_ = SetLayoutManager(std::make_unique<views::FlexLayout>());
layout_manager_->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
.SetCollapseMargins(true)
.SetDefault(views::kMarginsKey, gfx::Insets(0, default_margin));
location_bar_->SetProperty(views::kFlexBehaviorKey, location_bar_flex_rule);
location_bar_->SetProperty(views::kMarginsKey,
gfx::Insets(0, location_bar_margin));
if (extensions_container_) {
const views::FlexSpecification extensions_flex_rule =
views::FlexSpecification(
extensions_container_->GetAnimatingLayoutManager()
->GetDefaultFlexRule())
.WithOrder(kExtensionsFlexOrder);
extensions_container_->SetProperty(views::kFlexBehaviorKey,
extensions_flex_rule);
}
if (toolbar_account_icon_container_) {
toolbar_account_icon_container_->SetProperty(views::kFlexBehaviorKey,
account_container_flex_rule);
}
LayoutCommon();
}
void ToolbarView::LayoutCommon() {
DCHECK(display_mode_ == DisplayMode::NORMAL);
const gfx::Insets interior_margin =
GetLayoutInsets(LayoutInset::TOOLBAR_INTERIOR_MARGIN);
layout_manager_->SetInteriorMargin(interior_margin);
// Extend buttons to the window edge if we're either in a maximized or
// fullscreen window. This makes the buttons easier to hit, see Fitts' law.
const bool extend_buttons_to_edge =
browser_->window() &&
(browser_->window()->IsMaximized() || browser_->window()->IsFullscreen());
back_->SetLeadingMargin(extend_buttons_to_edge ? interior_margin.left() : 0);
app_menu_button_->SetTrailingMargin(
extend_buttons_to_edge ? interior_margin.right() : 0);
// Cast button visibility is controlled externally.
}
// AppMenuIconController::Delegate:
void ToolbarView::UpdateTypeAndSeverity(
AppMenuIconController::TypeAndSeverity type_and_severity) {
// There's no app menu in tabless windows.
if (!app_menu_button_)
return;
std::u16string accname_app = l10n_util::GetStringUTF16(IDS_ACCNAME_APP);
if (type_and_severity.type ==
AppMenuIconController::IconType::UPGRADE_NOTIFICATION) {
accname_app = l10n_util::GetStringFUTF16(
IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app);
}
app_menu_button_->SetAccessibleName(accname_app);
app_menu_button_->SetTypeAndSeverity(type_and_severity);
}
SkColor ToolbarView::GetDefaultColorForSeverity(
AppMenuIconController::Severity severity) const {
ui::NativeTheme::ColorId color_id;
switch (severity) {
case AppMenuIconController::Severity::NONE:
return GetThemeProvider()->GetColor(
ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
case AppMenuIconController::Severity::LOW:
color_id = ui::NativeTheme::kColorId_AlertSeverityLow;
break;
case AppMenuIconController::Severity::MEDIUM:
color_id = ui::NativeTheme::kColorId_AlertSeverityMedium;
break;
case AppMenuIconController::Severity::HIGH:
color_id = ui::NativeTheme::kColorId_AlertSeverityHigh;
break;
}
return GetNativeTheme()->GetSystemColor(color_id);
}
ExtensionsToolbarContainer* ToolbarView::GetExtensionsToolbarContainer() {
return extensions_container_;
}
gfx::Size ToolbarView::GetToolbarButtonSize() const {
const int size = GetLayoutConstant(LayoutConstant::TOOLBAR_BUTTON_HEIGHT);
return gfx::Size(size, size);
}
views::View* ToolbarView::GetDefaultExtensionDialogAnchorView() {
if (extensions_container_)
return extensions_container_->extensions_button();
return GetAppMenuButton();
}
PageActionIconView* ToolbarView::GetPageActionIconView(
PageActionIconType type) {
PageActionIconView* icon =
location_bar()->page_action_icon_controller()->GetIconView(type);
if (icon)
return icon;
return toolbar_account_icon_container_
? toolbar_account_icon_container_->page_action_icon_controller()
->GetIconView(type)
: nullptr;
}
AppMenuButton* ToolbarView::GetAppMenuButton() {
if (app_menu_button_)
return app_menu_button_;
return custom_tab_bar_ ? custom_tab_bar_->custom_tab_menu_button() : nullptr;
}
gfx::Rect ToolbarView::GetFindBarBoundingBox(int contents_bottom) {
if (!browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR))
return gfx::Rect();
if (!location_bar_->IsDrawn())
return gfx::Rect();
gfx::Rect bounds =
location_bar_->ConvertRectToWidget(location_bar_->GetLocalBounds());
return gfx::Rect(bounds.x(), bounds.bottom(), bounds.width(),
contents_bottom - bounds.bottom());
}
void ToolbarView::FocusToolbar() {
SetPaneFocus(nullptr);
}
views::AccessiblePaneView* ToolbarView::GetAsAccessiblePaneView() {
return this;
}
views::View* ToolbarView::GetAnchorView(PageActionIconType type) {
// Return the container visually housing the icon so all the bubbles align
// with the same visible edge.
if (toolbar_account_icon_container_) {
views::View* icon = GetPageActionIconView(type);
if (toolbar_account_icon_container_->Contains(icon)) {
DCHECK(base::FeatureList::IsEnabled(
autofill::features::kAutofillEnableToolbarStatusChip));
return toolbar_account_icon_container_;
}
}
return location_bar_;
}
void ToolbarView::ZoomChangedForActiveTab(bool can_show_bubble) {
location_bar_->page_action_icon_controller()->ZoomChangedForActiveTab(
can_show_bubble);
}
AvatarToolbarButton* ToolbarView::GetAvatarToolbarButton() {
if (toolbar_account_icon_container_ &&
toolbar_account_icon_container_->avatar_button()) {
return toolbar_account_icon_container_->avatar_button();
}
if (avatar_)
return avatar_;
return nullptr;
}
ToolbarButton* ToolbarView::GetBackButton() {
return back_;
}
ReloadButton* ToolbarView::GetReloadButton() {
return reload_;
}
BrowserRootView::DropIndex ToolbarView::GetDropIndex(
const ui::DropTargetEvent& event) {
return {browser_->tab_strip_model()->active_index(), false};
}
views::View* ToolbarView::GetViewForDrop() {
return this;
}
void ToolbarView::OnChromeLabsPrefChanged() {
chrome_labs_button_->SetVisible(show_chrome_labs_button_.GetValue());
GetViewAccessibility().AnnounceText(l10n_util::GetStringUTF16(
chrome_labs_button_->GetVisible()
? IDS_ACCESSIBLE_TEXT_CHROMELABS_BUTTON_ADDED_BY_ENTERPRISE_POLICY
: IDS_ACCESSIBLE_TEXT_CHROMELABS_BUTTON_REMOVED_BY_ENTERPRISE_POLICY));
}
void ToolbarView::LoadImages() {
DCHECK_EQ(display_mode_, DisplayMode::NORMAL);
if (extensions_container_)
extensions_container_->UpdateAllIcons();
if (toolbar_account_icon_container_)
toolbar_account_icon_container_->UpdateAllIcons();
}
void ToolbarView::ShowCriticalNotification() {
#if defined(OS_WIN)
views::BubbleDialogDelegateView::CreateBubble(
new CriticalNotificationBubbleView(app_menu_button_))->Show();
#endif
}
void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) {
#if !BUILDFLAG(IS_CHROMEOS_ASH)
OutdatedUpgradeBubbleView::ShowBubble(app_menu_button_, browser_,
auto_update_enabled);
#endif
}
void ToolbarView::OnShowHomeButtonChanged() {
home_->SetVisible(show_home_button_.GetValue());
Layout();
SchedulePaint();
}
void ToolbarView::OnTouchUiChanged() {
if (display_mode_ == DisplayMode::NORMAL) {
// Update the internal margins for touch layout.
// TODO(dfried): I think we can do better than this by making the touch UI
// code cleaner.
const int default_margin = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
const int location_bar_margin = GetLayoutConstant(TOOLBAR_STANDARD_SPACING);
layout_manager_->SetDefault(views::kMarginsKey,
gfx::Insets(0, default_margin));
location_bar_->SetProperty(views::kMarginsKey,
gfx::Insets(0, location_bar_margin));
LoadImages();
PreferredSizeChanged();
}
}
BEGIN_METADATA(ToolbarView, views::AccessiblePaneView)
ADD_READONLY_PROPERTY_METADATA(bool, AppMenuFocused)
END_METADATA