blob: db2f4dee3640e53a34594590b500c90cb25a0b65 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// 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 <memory>
#include <utility>
#include "base/command_line.h"
#include "base/containers/fixed_flat_map.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/i18n/number_formatting.h"
#include "base/i18n/rtl.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.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/performance_manager/public/user_tuning/user_tuning_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/actions/chrome_action_id.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_element_identifiers.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/color/chrome_color_id.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/omnibox/omnibox_view.h"
#include "chrome/browser/ui/tabs/public/tab_features.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_prefs.h"
#include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_prefs.h"
#include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_utils.h"
#include "chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.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_toolbar_button.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_coordinator.h"
#include "chrome/browser/ui/views/frame/browser_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_contextual_menu.h"
#include "chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h"
#include "chrome/browser/ui/views/location_bar/intent_chip_button.h"
#include "chrome/browser/ui/views/location_bar/star_view.h"
#include "chrome/browser/ui/views/page_action/page_action_container_view.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/page_action/page_action_properties_provider.h"
#include "chrome/browser/ui/views/page_action/page_action_view.h"
#include "chrome/browser/ui/views/performance_controls/battery_saver_button.h"
#include "chrome/browser/ui/views/performance_controls/performance_intervention_button.h"
#include "chrome/browser/ui/views/side_panel/side_panel.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/browser/ui/views/tabs/tab_strip_controller.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/chrome_labs_coordinator.h"
#include "chrome/browser/ui/views/toolbar/home_button.h"
#include "chrome/browser/ui/views/toolbar/pinned_toolbar_actions_container.h"
#include "chrome/browser/ui/views/toolbar/reload_button.h"
#include "chrome/browser/ui/views/toolbar/reload_button_web_view.h"
#include "chrome/browser/ui/views/toolbar/split_tabs_button.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h"
#include "chrome/browser/ui/views/toolbar/toolbar_controller.h"
#include "chrome/browser/ui/views/zoom/zoom_view_controller.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/web_applications/link_capturing_features.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/branded_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/prefs/pref_service.h"
#include "components/safe_browsing/core/common/features.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 "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkPathBuilder.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/interaction/element_identifier.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/ui_base_features.h"
#include "ui/base/window_open_disposition.h"
#include "ui/base/window_open_disposition_utils.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.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/views/accessibility/view_accessibility.h"
#include "ui/views/background.h"
#include "ui/views/cascading_property.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/layout/proposed_layout.h"
#include "ui/views/view.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/frame_view.h"
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#include "chrome/browser/recovery/recovery_install_global_error_factory.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;
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kActionItemUnderlineIndicatorKey, false)
namespace {
// Gets the display mode for a given browser.
ToolbarView::DisplayMode GetDisplayMode(Browser* browser) {
#if BUILDFLAG(IS_CHROMEOS)
if (browser->is_type_custom_tab()) {
return ToolbarView::DisplayMode::kCustomTab;
}
#endif
// Checked in this order because even tabbed PWAs use the CUSTOM_TAB
// display mode.
if (web_app::AppBrowserController::IsWebApp(browser)) {
return ToolbarView::DisplayMode::kCustomTab;
}
if (browser->SupportsWindowFeature(
Browser::WindowFeature::kFeatureTabStrip)) {
return ToolbarView::DisplayMode::kNormal;
}
return ToolbarView::DisplayMode::kLocation;
}
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;
}
constexpr int kBrowserAppMenuRefreshExpandedMargin = 5;
constexpr int kBrowserAppMenuRefreshCollapsedMargin = 2;
// Draws background akin to the tabstrip.
class TabstripLikeBackground : public views::Background {
public:
explicit TabstripLikeBackground(BrowserView* browser_view)
: browser_view_(browser_view) {}
private:
// views::Background:
void Paint(gfx::Canvas* canvas, views::View* view) const override {
bool painted = TopContainerBackground::PaintThemeCustomImage(canvas, view,
browser_view_);
if (!painted) {
SkColor frame_color;
// If the toolbar height side panel is visible, background should be
// painted the same as the toolbar rather than tab strip.
if (browser_view_->toolbar_height_side_panel()->GetVisible()) {
frame_color = view->GetColorProvider()->GetColor(kColorToolbar);
} else {
frame_color =
browser_view_->browser_widget()->GetFrameView()->GetFrameColor(
BrowserFrameActiveState::kUseCurrent);
}
canvas->DrawColor(frame_color);
}
}
const raw_ptr<BrowserView> browser_view_;
};
bool IsMigratedClickToCallBubble(
IntentPickerBubbleView::BubbleType bubble_type) {
return bubble_type == IntentPickerBubbleView::BubbleType::kClickToCall &&
IsPageActionMigrated(PageActionIconType::kClickToCall);
}
} // namespace
class ToolbarView::ContainerView : public views::View {
METADATA_HEADER(ContainerView, views::View)
public:
// Calling PreferredSizeChanged() will trigger the parent's
// ChildPreferredSizeChanged.
// Bubble up calls to ChildPreferredSizeChanged.
void ChildPreferredSizeChanged(View* child) override {
PreferredSizeChanged();
}
};
BEGIN_METADATA(ToolbarView, ContainerView)
END_METADATA
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, public:
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ToolbarView, kToolbarElementId);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ToolbarView, kToolbarContainerElementId);
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);
SetProperty(views::kElementIdentifierKey, kToolbarElementId);
container_view_ = AddChildView(std::make_unique<ContainerView>());
container_view_->SetProperty(views::kElementIdentifierKey,
kToolbarContainerElementId);
GetViewAccessibility().SetRole(ax::mojom::Role::kToolbar);
if (display_mode_ == DisplayMode::kNormal) {
container_view_->SetBackground(
std::make_unique<TopContainerBackground>(browser_view));
for (const auto& view_and_command : GetViewCommandMap()) {
chrome::AddCommandObserver(browser_, view_and_command.second, this);
}
}
views::SetCascadingColorProviderColor(
container_view_, views::kCascadingBackgroundColor, kColorToolbar);
}
ToolbarView::~ToolbarView() {
if (display_mode_ != DisplayMode::kNormal) {
return;
}
overflow_button_->set_toolbar_controller(nullptr);
for (const auto& view_and_command : GetViewCommandMap()) {
chrome::RemoveCommandObserver(browser_, view_and_command.second, this);
}
if (browser_view_->GetSupportsTabStrip()) {
browser()->GetTabStripModel()->RemoveObserver(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
// The background views must be behind container_view_.
background_view_left_ = AddChildViewAt(std::make_unique<View>(), 0);
background_view_left_->SetBackground(
std::make_unique<TabstripLikeBackground>(browser_view_));
background_view_right_ = AddChildViewAt(std::make_unique<View>(), 0);
background_view_right_->SetBackground(
std::make_unique<TabstripLikeBackground>(browser_view_));
active_state_subscription_ =
GetWidget()->RegisterPaintAsActiveChangedCallback(base::BindRepeating(
&ToolbarView::ActiveStateChanged, base::Unretained(this)));
auto location_bar = std::make_unique<LocationBarView>(
browser_, browser_->profile(), browser_->command_controller(), this,
display_mode_ != DisplayMode::kNormal);
// Make sure the toolbar shows by default.
size_animation_.Reset(1);
if (display_mode_ != DisplayMode::kNormal) {
location_bar_ = container_view_->AddChildView(std::move(location_bar));
location_bar_->Init();
}
if (display_mode_ == DisplayMode::kCustomTab) {
custom_tab_bar_ = container_view_->AddChildView(
std::make_unique<CustomTabBarView>(browser_view_, this));
container_view_->SetLayoutManager(std::make_unique<views::FillLayout>());
initialized_ = true;
return;
} else if (display_mode_ == DisplayMode::kLocation) {
// Add the pinned toolbar actions container so that downloads can be shown
// in popups.
pinned_toolbar_actions_container_ = container_view_->AddChildView(
std::make_unique<PinnedToolbarActionsContainer>(browser_view_, this));
container_view_->SetBackground(
views::CreateSolidBackground(kColorLocationBarBackground));
container_view_->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
.SetDefault(views::kFlexBehaviorKey,
views::FlexSpecification(
views::LayoutOrientation::kHorizontal,
views::MinimumFlexSizeRule::kPreferredSnapToZero))
.SetFlexAllocationOrder(views::FlexAllocationOrder::kReverse);
location_bar_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::LayoutOrientation::kHorizontal,
views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kUnbounded));
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_);
PrefService* const prefs = browser_->profile()->GetPrefs();
std::unique_ptr<HomeButton> home = std::make_unique<HomeButton>(
browser_, base::BindRepeating(callback, browser_, IDC_HOME));
std::unique_ptr<ExtensionsToolbarContainer> extensions_container;
std::unique_ptr<views::View> toolbar_divider;
// 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_);
toolbar_divider = std::make_unique<views::View>();
}
std::unique_ptr<MediaToolbarButtonView> media_button;
if (base::FeatureList::IsEnabled(media::kGlobalMediaControls)) {
media_button = std::make_unique<MediaToolbarButtonView>(
browser_view_,
std::make_unique<MediaToolbarButtonContextualMenu>(browser_));
}
// Always add children in order from left to right, for accessibility.
back_ = container_view_->AddChildView(std::move(back));
forward_ = container_view_->AddChildView(std::move(forward));
if (features::IsWebUIReloadButtonEnabled()) {
auto reload_webview = std::make_unique<ReloadButtonWebView>(
browser_, browser_->command_controller());
reload_webview_ = container_view_->AddChildView(std::move(reload_webview));
} else {
std::unique_ptr<ReloadButton> reload = std::make_unique<ReloadButton>(
browser_->GetProfile(), browser_->command_controller());
reload_ = container_view_->AddChildView(std::move(reload));
}
home_ = container_view_->AddChildView(std::move(home));
if (base::FeatureList::IsEnabled(features::kSideBySide)) {
std::unique_ptr<SplitTabsToolbarButton> split =
std::make_unique<SplitTabsToolbarButton>(browser_);
split_tabs_ = container_view_->AddChildView(std::move(split));
}
location_bar_ = container_view_->AddChildView(std::move(location_bar));
if (extensions_container) {
extensions_container_ =
container_view_->AddChildView(std::move(extensions_container));
extensions_toolbar_coordinator_ =
std::make_unique<ExtensionsToolbarCoordinator>(browser_,
extensions_container_);
}
if (toolbar_divider) {
toolbar_divider_ =
container_view_->AddChildView(std::move(toolbar_divider));
toolbar_divider_->SetPreferredSize(
gfx::Size(GetLayoutConstant(TOOLBAR_DIVIDER_WIDTH),
GetLayoutConstant(TOOLBAR_DIVIDER_HEIGHT)));
toolbar_divider_->SetBackground(views::CreateRoundedRectBackground(
kColorToolbarExtensionSeparatorEnabled,
GetLayoutConstant(TOOLBAR_DIVIDER_CORNER_RADIUS)));
}
pinned_toolbar_actions_container_ = container_view_->AddChildView(
std::make_unique<PinnedToolbarActionsContainer>(browser_view_, this));
if (features::HasTabSearchToolbarButton()) {
tab_search_button_ =
pinned_toolbar_actions_container()->CreatePermanentButtonFor(
kActionTabSearch);
tab_search_button_->SetProperty(views::kElementIdentifierKey,
kTabSearchButtonElementId);
}
if (IsChromeLabsEnabled()) {
UpdateChromeLabsNewBadgePrefs(browser_->profile());
const bool should_show_chrome_labs_ui =
ShouldShowChromeLabsUI(browser_->profile());
if (should_show_chrome_labs_ui) {
show_chrome_labs_button_.Init(
chrome_labs_prefs::kBrowserLabsEnabledEnterprisePolicy, prefs,
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.
pinned_toolbar_actions_container_->GetActionItemFor(kActionShowChromeLabs)
->SetVisible(show_chrome_labs_button_.GetValue() &&
should_show_chrome_labs_ui);
}
}
// Only show the Battery Saver button when it is not controlled by the OS. On
// ChromeOS the battery icon in the shelf shows the same information.
if (!performance_manager::user_tuning::IsBatterySaverModeManagedByOS()) {
battery_saver_button_ = container_view_->AddChildView(
std::make_unique<BatterySaverButton>(browser_view_));
}
performance_intervention_button_ = container_view_->AddChildView(
std::make_unique<PerformanceInterventionButton>(browser_view_));
if (media_button) {
media_button_ = container_view_->AddChildView(std::move(media_button));
}
avatar_ = container_view_->AddChildView(
std::make_unique<AvatarToolbarButton>(browser_view_));
bool show_avatar_toolbar_button = true;
#if BUILDFLAG(IS_CHROMEOS)
// ChromeOS only badges Incognito, Guest, and captive portal signin icons in
// the browser window.
show_avatar_toolbar_button =
browser_->profile()->IsIncognitoProfile() ||
browser_->profile()->IsGuestSession() ||
(browser_->profile()->IsOffTheRecord() &&
browser_->profile()->GetOTRProfileID().IsCaptivePortal());
#else
// DevTools profiles are OffTheRecord, so hide it there.
show_avatar_toolbar_button = browser_->profile()->IsIncognitoProfile() ||
browser_->profile()->IsGuestSession() ||
browser_->profile()->IsRegularProfile();
#endif
avatar_->SetVisible(show_avatar_toolbar_button);
#if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
auto new_tab_button = std::make_unique<ToolbarButton>(base::BindRepeating(
&ToolbarView::NewTabButtonPressed, base::Unretained(this)));
new_tab_button->SetTooltipText(
l10n_util::GetStringUTF16(IDS_TOOLTIP_NEW_TAB));
new_tab_button->SetHorizontalAlignment(gfx::ALIGN_CENTER);
new_tab_button->SetVectorIcon(kNewTabToolbarButtonIcon);
new_tab_button->SetVisible(false);
new_tab_button->SetProperty(views::kElementIdentifierKey,
kToolbarNewTabButtonElementId);
new_tab_button_ = container_view_->AddChildView(std::move(new_tab_button));
#endif // BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
overflow_button_ =
container_view_->AddChildView(std::make_unique<OverflowButton>());
overflow_button_->SetVisible(false);
auto app_menu_button = std::make_unique<BrowserAppMenuButton>(this);
app_menu_button->SetFlipCanvasOnPaintForRTLUI(true);
app_menu_button->GetViewAccessibility().SetName(
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_ = container_view_->AddChildView(std::move(app_menu_button));
LoadImages();
// Start global error services now so we set the icon on the menu correctly.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
RecoveryInstallGlobalErrorFactory::GetForProfile(browser_->profile());
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
// 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_forward_button_.Init(
prefs::kShowForwardButton, prefs,
base::BindRepeating(&ToolbarView::OnShowForwardButtonChanged,
base::Unretained(this)));
forward_->SetVisible(show_forward_button_.GetValue());
show_home_button_.Init(
prefs::kShowHomeButton, prefs,
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()));
}
}
if (browser_view_->GetSupportsTabStrip()) {
browser()->GetTabStripModel()->AddObserver(this);
}
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 (pinned_toolbar_actions_container_) {
pinned_toolbar_actions_container_->UpdateAllIcons();
}
if (ReloadControl* reload_control = GetReloadButton(); reload_control) {
reload_control->SetMenuEnabled(
chrome::IsDebuggerAttachedToCurrentTab(browser_));
}
}
bool ToolbarView::UpdateSecurityState() {
if (location_bar_ && location_bar_->HasSecurityStateChanged()) {
Update(nullptr);
return true;
}
return false;
}
void ToolbarView::SetToolbarVisibility(bool visible) {
SetVisible(visible);
views::View* bar = display_mode_ == DisplayMode::kCustomTab
? 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::kCustomTab);
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 (!new_tab_button_) {
return;
}
if (browser_view_->webui_tab_strip()) {
const int button_height = GetLayoutConstant(TOOLBAR_BUTTON_HEIGHT);
new_tab_button_->SetPreferredSize(gfx::Size(button_height, button_height));
new_tab_button_->SetVisible(true);
const size_t insertion_index =
container_view_->GetIndexOf(new_tab_button_).value();
container_view_->AddChildViewAt(
browser_view_->webui_tab_strip()->CreateTabCounter(), insertion_index);
LoadImages();
} else {
new_tab_button_->SetVisible(false);
}
UpdateRecedingCornerRadius();
#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,
IntentPickerBubbleView::BubbleType bubble_type,
const std::optional<url::Origin>& initiating_origin,
IntentPickerResponse callback) {
views::Button* highlighted_button = nullptr;
if (bubble_type == IntentPickerBubbleView::BubbleType::kClickToCall) {
highlighted_button =
GetPageActionIconView(PageActionIconType::kClickToCall);
} else if (highlighted_button = GetIntentChipButton(); !highlighted_button) {
highlighted_button = GetPageActionView(kActionShowIntentPicker);
}
// Post migration, highlighted_button is a nullptr for ClickToCall
// BubbleType but the bubble still gets shown without a page action being
// shown/highlighted.
if (highlighted_button || IsMigratedClickToCallBubble(bubble_type)) {
IntentPickerBubbleView::ShowBubble(
location_bar(), highlighted_button, bubble_type, GetWebContents(),
std::move(app_info), show_stay_in_chrome, show_remember_selection,
initiating_origin, std::move(callback));
}
}
void ToolbarView::ShowBookmarkBubble(const GURL& url, bool already_bookmarked) {
views::View* const anchor_view = location_bar();
views::Button* const bookmark_star_icon =
GetPageActionIconView(PageActionIconType::kBookmarkStar);
CHECK(bookmark_star_icon);
BookmarkBubbleView::ShowBubble(anchor_view, GetWebContents(),
bookmark_star_icon, browser_, url,
already_bookmarked);
}
views::Button* ToolbarView::GetChromeLabsButton() const {
return browser_->GetFeatures()
.chrome_labs_coordinator()
->GetChromeLabsButton();
}
ExtensionsToolbarButton* ToolbarView::GetExtensionsButton() const {
return extensions_container_->GetExtensionsButton();
}
ToolbarButton* ToolbarView::GetCastButton() const {
return pinned_toolbar_actions_container()
? pinned_toolbar_actions_container()->GetButtonFor(
kActionRouteMedia)
: nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, LocationBarView::Delegate implementation:
WebContents* ToolbarView::GetWebContents() {
return browser_->tab_strip_model()->GetActiveWebContents();
}
LocationBarModel* ToolbarView::GetLocationBarModel() {
return browser_->GetFeatures().location_bar_model();
}
const LocationBarModel* ToolbarView::GetLocationBarModel() const {
return browser_->GetFeatures().location_bar_model();
}
ContentSettingBubbleModelDelegate*
ToolbarView::GetContentSettingBubbleModelDelegate() {
return browser_->GetFeatures().content_setting_bubble_model_delegate();
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, CommandObserver implementation:
void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
DCHECK(display_mode_ == DisplayMode::kNormal);
const std::array<views::Button*, 5> kButtons{back_, forward_, reload_, home_,
avatar_};
auto* button = *std::ranges::find(kButtons, id, &views::Button::tag);
DCHECK(button);
button->SetEnabled(enabled);
}
////////////////////////////////////////////////////////////////////////////////
// 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 views::SizeBounds& available_size) const {
gfx::Size size;
switch (display_mode_) {
case DisplayMode::kCustomTab:
size = custom_tab_bar_->GetPreferredSize();
break;
case DisplayMode::kLocation:
size = location_bar_->GetPreferredSize();
break;
case DisplayMode::kNormal:
size = container_view_->GetPreferredSize();
// 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/40663413): 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::kCustomTab:
size = custom_tab_bar_->GetMinimumSize();
break;
case DisplayMode::kLocation:
size = location_bar_->GetMinimumSize();
break;
case DisplayMode::kNormal:
size = container_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/40663413): 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(PassKey) {
// If we have not been initialized yet just do nothing.
if (!initialized_) {
return;
}
// The container view should be the exact same size/position as ToolbarView.
container_view_->SetSize(size());
// The background views should be behind the top-left and top-right corners
// of the container_view_.
background_view_left_->SetBounds(0, 0, receding_corner_radius_,
receding_corner_radius_);
background_view_right_->SetBounds(width() - receding_corner_radius_, 0,
receding_corner_radius_,
receding_corner_radius_);
if (display_mode_ == DisplayMode::kCustomTab) {
custom_tab_bar_->SetBounds(0, 0, width(),
custom_tab_bar_->GetPreferredSize().height());
location_bar_->SetVisible(false);
return;
}
if (display_mode_ == DisplayMode::kNormal) {
LayoutCommon();
UpdateClipPath();
}
if (toolbar_controller_) {
// Need to determine whether the overflow button should be visible, and only
// update it if the visibility changes.
const bool was_overflow_button_visible =
toolbar_controller_->overflow_button()->GetVisible();
const bool show_overflow_button =
toolbar_controller_->ShouldShowOverflowButton(size());
if (was_overflow_button_visible != show_overflow_button) {
views::ManualLayoutUtil(layout_manager_)
.SetViewHidden(toolbar_controller_->overflow_button(),
!show_overflow_button);
base::RecordAction(base::UserMetricsAction(
show_overflow_button ? "ResponsiveToolbar.OverflowButtonShown"
: "ResponsiveToolbar.OverflowButtonHidden"));
}
}
// Call super implementation to ensure layout manager and child layouts
// happen.
LayoutSuperclass<AccessiblePaneView>(this);
}
void ToolbarView::OnThemeChanged() {
views::AccessiblePaneView::OnThemeChanged();
if (!initialized_) {
return;
}
if (display_mode_ == DisplayMode::kNormal) {
LoadImages();
}
SchedulePaint();
}
void ToolbarView::UpdateClipPath() {
const gfx::Rect local_bounds = GetLocalBounds();
// The bottom of the toolbar may be clipped more than necessary in
// certain scale factor so adds extra 2dp so that even if the origin
// and the height are rounded down, we still can paint til the
// bottom of the toolbar. The similar logic is applied to
// BookmarkBarView which can be the bottom component within the
// TopContainerView, and TopContainerView which is the parent and
// can also clip the paint region for child views.
// TODO(crbug.com/41344902): Remove this hack once the pixel canvas is
// enabled on all aura platforms.
const int extended_height = local_bounds.height() + 2;
const SkPath path =
SkPathBuilder()
.moveTo(0, local_bounds.height())
.lineTo(0, receding_corner_radius_)
.arcTo(SkVector(receding_corner_radius_, receding_corner_radius_), 0,
SkPathBuilder::kSmall_ArcSize, SkPathDirection::kCW,
SkPoint(receding_corner_radius_, 0))
.lineTo(local_bounds.width() - receding_corner_radius_, 0)
.arcTo(SkVector(receding_corner_radius_, receding_corner_radius_), 0,
SkPathBuilder::kSmall_ArcSize, SkPathDirection::kCW,
SkPoint(local_bounds.width(), receding_corner_radius_))
.lineTo(local_bounds.width(), extended_height)
.lineTo(0, extended_height)
.detach();
container_view_->SetClipPath(path);
}
void ToolbarView::ActiveStateChanged() {
background_view_left_->SchedulePaint();
background_view_right_->SchedulePaint();
}
void ToolbarView::NewTabButtonPressed(const ui::Event& event) {
chrome::NewTab(browser_view_->browser(),
NewTabTypes::kNewTabButtonInToolbarForTouch);
}
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::InitLayout() {
const int default_margin = GetLayoutConstant(TOOLBAR_ICON_DEFAULT_MARGIN);
// TODO(dfried): rename this constant.
const int location_bar_margin = GetLayoutConstant(TOOLBAR_STANDARD_SPACING);
// Shift previously flex-able elements' order by `kOrderOffset`.
// This will cause them to be the first ones to drop out or shrink to minimum.
// Order 1 - kOrderOffset will be assigned to new flex-able elements.
constexpr int kOrderOffset = 1000;
constexpr int kLocationBarFlexOrder = kOrderOffset + 1;
constexpr int kToolbarActionsFlexOrder = kOrderOffset + 2;
constexpr int kExtensionsFlexOrder = kOrderOffset + 3;
const views::FlexSpecification location_bar_flex_rule =
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kUnbounded)
.WithOrder(kLocationBarFlexOrder);
layout_manager_ =
container_view_->SetLayoutManager(std::make_unique<views::FlexLayout>());
layout_manager_->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
.SetCollapseMargins(true)
.SetDefault(views::kMarginsKey, gfx::Insets::VH(0, default_margin));
location_bar_->SetProperty(views::kFlexBehaviorKey, location_bar_flex_rule);
location_bar_->SetProperty(views::kMarginsKey,
gfx::Insets::VH(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 (pinned_toolbar_actions_container_) {
pinned_toolbar_actions_container_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(
pinned_toolbar_actions_container_->GetAnimatingLayoutManager()
->GetDefaultFlexRule())
.WithOrder(kToolbarActionsFlexOrder));
}
if (toolbar_divider_) {
toolbar_divider_->SetProperty(
views::kMarginsKey,
gfx::Insets::VH(0, GetLayoutConstant(TOOLBAR_DIVIDER_SPACING)));
}
constexpr int kToolbarFlexOrderStart = 1;
// TODO(crbug.com/40929989): Ignore containers till issue addressed.
toolbar_controller_ = std::make_unique<ToolbarController>(
ToolbarController::GetDefaultResponsiveElements(browser_),
ToolbarController::GetDefaultOverflowOrder(), kToolbarFlexOrderStart,
container_view_, overflow_button_, pinned_toolbar_actions_container_,
PinnedToolbarActionsModel::Get(browser_view_->GetProfile()));
overflow_button_->set_toolbar_controller(toolbar_controller_.get());
LayoutCommon();
}
void ToolbarView::LayoutCommon() {
DCHECK(display_mode_ == DisplayMode::kNormal);
gfx::Insets interior_margin =
GetLayoutInsets(browser_view_->webui_tab_strip()
? LayoutInset::WEBUI_TAB_STRIP_TOOLBAR_INTERIOR_MARGIN
: LayoutInset::TOOLBAR_INTERIOR_MARGIN);
if (!browser_view_->webui_tab_strip()) {
if (app_menu_button_->IsLabelPresentAndVisible()) {
// The interior margin in an expanded state should be more than in a
// collapsed state.
interior_margin.set_right(interior_margin.right() + 1);
app_menu_button_->SetProperty(
views::kMarginsKey,
gfx::Insets::VH(0, kBrowserAppMenuRefreshExpandedMargin));
} else {
app_menu_button_->SetProperty(
views::kMarginsKey,
gfx::Insets::VH(0, kBrowserAppMenuRefreshCollapsedMargin));
}
// The margins of the `avatar_` uses the same constants as the
// `app_menu_button_`.
if (avatar_->IsLabelPresentAndVisible()) {
avatar_->SetProperty(
views::kMarginsKey,
gfx::Insets::VH(0, kBrowserAppMenuRefreshExpandedMargin));
} else {
avatar_->SetProperty(
views::kMarginsKey,
gfx::Insets::VH(0, kBrowserAppMenuRefreshCollapsedMargin));
}
}
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);
if (toolbar_divider_ && extensions_container_) {
views::ManualLayoutUtil(layout_manager_)
.SetViewHidden(toolbar_divider_, !extensions_container_->GetVisible());
}
// 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::kUpgradeNotification) {
accname_app = l10n_util::GetStringFUTF16(
IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app);
}
app_menu_button_->GetViewAccessibility().SetName(accname_app);
app_menu_button_->SetTypeAndSeverity(type_and_severity);
}
ExtensionsToolbarContainer* ToolbarView::GetExtensionsToolbarContainer() {
return extensions_container_;
}
PinnedToolbarActionsContainer* ToolbarView::GetPinnedToolbarActionsContainer() {
return pinned_toolbar_actions_container_;
}
gfx::Size ToolbarView::GetToolbarButtonSize() const {
// Since DisplayMode::kLocation is for a slimline toolbar showing only compact
// location bar used for popups, toolbar buttons (ie downloads) must be
// smaller to accommodate the smaller size.
const int size =
display_mode_ == DisplayMode::kLocation
? location_bar_->GetPreferredSize().height()
: GetLayoutConstant(LayoutConstant::TOOLBAR_BUTTON_HEIGHT);
return gfx::Size(size, size);
}
views::View* ToolbarView::GetDefaultExtensionDialogAnchorView() {
if (extensions_container_ && extensions_container_->GetVisible()) {
return extensions_container_->GetExtensionsButton();
}
return GetAppMenuButton();
}
PageActionIconView* ToolbarView::GetPageActionIconView(
PageActionIconType type) {
return location_bar()->page_action_icon_controller()->GetIconView(type);
}
IconLabelBubbleView* ToolbarView::GetPageActionView(
actions::ActionId action_id) {
page_actions::PageActionPropertiesProvider provider;
if (!provider.Contains(action_id)) {
return nullptr;
}
const auto& properties = provider.GetProperties(action_id);
if (IsPageActionMigrated(properties.type)) {
return location_bar()->page_action_container()->GetPageActionView(
action_id);
}
return GetPageActionIconView(properties.type);
}
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::WindowFeature::kFeatureLocationBar)) {
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(
std::optional<actions::ActionId> action_id) {
if (pinned_toolbar_actions_container_ && action_id.has_value() &&
pinned_toolbar_actions_container_->IsActionPinnedOrPoppedOut(
action_id.value())) {
return pinned_toolbar_actions_container_->GetButtonFor(action_id.value());
}
return location_bar_;
}
void ToolbarView::ZoomChangedForActiveTab(bool can_show_bubble) {
if (IsPageActionMigrated(PageActionIconType::kZoom)) {
auto* zoom_view_controller = browser_->GetActiveTabInterface()
->GetTabFeatures()
->zoom_view_controller();
CHECK(zoom_view_controller);
zoom_view_controller->UpdatePageActionIconAndBubbleVisibility(
/*prefer_to_show_bubble=*/can_show_bubble, /*from_user_gesture=*/false);
return;
}
location_bar_->page_action_icon_controller()->ZoomChangedForActiveTab(
can_show_bubble);
}
AvatarToolbarButton* ToolbarView::GetAvatarToolbarButton() {
return avatar_;
}
ToolbarButton* ToolbarView::GetBackButton() {
return back_;
}
ReloadControl* ToolbarView::GetReloadButton() {
if (features::IsWebUIReloadButtonEnabled()) {
return reload_webview_;
}
return reload_;
}
IntentChipButton* ToolbarView::GetIntentChipButton() {
return location_bar()->intent_chip();
}
ToolbarButton* ToolbarView::GetDownloadButton() {
return pinned_toolbar_actions_container_
? pinned_toolbar_actions_container_->GetButtonFor(
kActionShowDownloads)
: nullptr;
}
std::optional<BrowserRootView::DropIndex> ToolbarView::GetDropIndex(
const ui::DropTargetEvent& event) {
return BrowserRootView::DropIndex{
.index = browser_->tab_strip_model()->active_index(),
.relative_to_index =
BrowserRootView::DropIndex::RelativeToIndex::kReplaceIndex};
}
BrowserRootView::DropTarget* ToolbarView::GetDropTarget(
gfx::Point loc_in_local_coords) {
return HitTestPoint(loc_in_local_coords) ? this : nullptr;
}
views::View* ToolbarView::GetViewForDrop() {
return this;
}
void ToolbarView::OnChromeLabsPrefChanged() {
actions::ActionItem* chrome_labs_action =
pinned_toolbar_actions_container_->GetActionItemFor(
kActionShowChromeLabs);
chrome_labs_action->SetVisible(show_chrome_labs_button_.GetValue() &&
ShouldShowChromeLabsUI(browser_->profile()));
GetViewAccessibility().AnnounceText(l10n_util::GetStringUTF16(
chrome_labs_action->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::kNormal);
if (extensions_container_) {
extensions_container_->UpdateAllIcons();
}
}
void ToolbarView::OnShowForwardButtonChanged() {
forward_->SetVisible(show_forward_button_.GetValue());
InvalidateLayout();
}
void ToolbarView::OnShowHomeButtonChanged() {
home_->SetVisible(show_home_button_.GetValue());
}
void ToolbarView::OnTouchUiChanged() {
if (display_mode_ == DisplayMode::kNormal) {
// 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::VH(0, default_margin));
location_bar_->SetProperty(views::kMarginsKey,
gfx::Insets::VH(0, location_bar_margin));
LoadImages();
PreferredSizeChanged();
}
}
void ToolbarView::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
UpdateRecedingCornerRadius();
}
void ToolbarView::UpdateRecedingCornerRadius() {
bool tab_strip_has_trailing_frame_buttons =
!browser_view_->browser_widget()
->GetFrameView()
->CaptionButtonsOnLeadingEdge();
bool tab_strip_has_leading_action_buttons =
(!tabs::GetTabSearchTrailingTabstrip(browser()->profile()) &&
!features::HasTabSearchToolbarButton());
bool first_tab_selected = browser_->tab_strip_model()->IsTabInForeground(0);
int new_corner_radius;
// If there is anything on the leading side or not the first tab is selected,
// then the corner radius is shown, otherwise we hide the corner radius.
// Also when showing WebUITabStrip, toolbar should not have receding corners.
if (!browser_view_->webui_tab_strip() &&
(!tab_strip_has_trailing_frame_buttons ||
tab_strip_has_leading_action_buttons || !first_tab_selected)) {
new_corner_radius = GetLayoutConstant(TOOLBAR_CORNER_RADIUS);
} else {
new_corner_radius = 0;
}
if (receding_corner_radius_ != new_corner_radius) {
receding_corner_radius_ = new_corner_radius;
InvalidateLayout();
}
}
BEGIN_METADATA(ToolbarView)
ADD_READONLY_PROPERTY_METADATA(bool, AppMenuFocused)
END_METADATA