1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/feature_list.h"
13 #include "base/i18n/number_formatting.h"
14 #include "base/metrics/user_metrics.h"
15 #include "base/ranges/algorithm.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/trace_event/trace_event.h"
18 #include "build/build_config.h"
19 #include "chrome/app/chrome_command_ids.h"
20 #include "chrome/browser/command_updater.h"
21 #include "chrome/browser/media/router/media_router_feature.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/themes/theme_properties.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_command_controller.h"
26 #include "chrome/browser/ui/browser_commands.h"
27 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
28 #include "chrome/browser/ui/browser_tabstrip.h"
29 #include "chrome/browser/ui/browser_window.h"
30 #include "chrome/browser/ui/global_error/global_error_service.h"
31 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
32 #include "chrome/browser/ui/intent_picker_tab_helper.h"
33 #include "chrome/browser/ui/layout_constants.h"
34 #include "chrome/browser/ui/tabs/tab_strip_model.h"
35 #include "chrome/browser/ui/ui_features.h"
36 #include "chrome/browser/ui/view_ids.h"
37 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h"
38 #include "chrome/browser/ui/views/extensions/extension_popup.h"
39 #include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
40 #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
41 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
42 #include "chrome/browser/ui/views/frame/browser_view.h"
43 #include "chrome/browser/ui/views/frame/top_container_background.h"
44 #include "chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h"
45 #include "chrome/browser/ui/views/location_bar/star_view.h"
46 #include "chrome/browser/ui/views/media_router/cast_toolbar_button.h"
47 #include "chrome/browser/ui/views/page_action/page_action_icon_container.h"
48 #include "chrome/browser/ui/views/page_action/page_action_icon_controller.h"
49 #include "chrome/browser/ui/views/tabs/tab_strip.h"
50 #include "chrome/browser/ui/views/toolbar/app_menu.h"
51 #include "chrome/browser/ui/views/toolbar/back_forward_button.h"
52 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
53 #include "chrome/browser/ui/views/toolbar/browser_app_menu_button.h"
54 #include "chrome/browser/ui/views/toolbar/home_button.h"
55 #include "chrome/browser/ui/views/toolbar/reload_button.h"
56 #include "chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h"
57 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
58 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
59 #include "chrome/browser/upgrade_detector/upgrade_detector.h"
60 #include "chrome/common/chrome_features.h"
61 #include "chrome/common/pref_names.h"
62 #include "chrome/grit/chromium_strings.h"
63 #include "chrome/grit/generated_resources.h"
64 #include "chrome/grit/theme_resources.h"
65 #include "components/autofill/core/common/autofill_payments_features.h"
66 #include "components/omnibox/browser/omnibox_view.h"
67 #include "components/prefs/pref_service.h"
68 #include "components/strings/grit/components_strings.h"
69 #include "content/public/browser/render_view_host.h"
70 #include "content/public/browser/web_contents.h"
71 #include "media/base/media_switches.h"
72 #include "ui/accessibility/ax_node_data.h"
73 #include "ui/base/l10n/l10n_util.h"
74 #include "ui/base/theme_provider.h"
75 #include "ui/base/window_open_disposition.h"
76 #include "ui/compositor/layer.h"
77 #include "ui/gfx/canvas.h"
78 #include "ui/gfx/image/canvas_image_source.h"
79 #include "ui/gfx/paint_vector_icon.h"
80 #include "ui/gfx/scoped_canvas.h"
81 #include "ui/native_theme/native_theme_aura.h"
82 #include "ui/views/layout/fill_layout.h"
83 #include "ui/views/layout/flex_layout.h"
84 #include "ui/views/view_class_properties.h"
85 #include "ui/views/widget/tooltip_manager.h"
86 #include "ui/views/widget/widget.h"
87 #include "ui/views/window/non_client_view.h"
88
89 #if defined(OS_WIN) || defined(OS_MAC)
90 #include "chrome/browser/recovery/recovery_install_global_error_factory.h"
91 #endif
92
93 #if defined(OS_WIN)
94 #include "chrome/browser/ui/views/critical_notification_bubble_view.h"
95 #endif
96
97 #if defined(OS_CHROMEOS)
98 #include "chromeos/constants/chromeos_features.h"
99 #else
100 #include "chrome/browser/signin/signin_global_error_factory.h"
101 #include "chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h"
102 #include "chrome/browser/ui/views/outdated_upgrade_bubble_view.h"
103 #endif
104
105 #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
106 #include "chrome/browser/ui/views/frame/webui_tab_strip_container_view.h"
107 #endif // BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
108
109 using base::UserMetricsAction;
110 using content::WebContents;
111
112 namespace {
113
114 // Gets the display mode for a given browser.
GetDisplayMode(Browser * browser)115 ToolbarView::DisplayMode GetDisplayMode(Browser* browser) {
116 #if defined(OS_CHROMEOS)
117 if (browser->is_type_custom_tab())
118 return ToolbarView::DisplayMode::CUSTOM_TAB;
119 #endif
120
121 // Checked in this order because even tabbed PWAs use the CUSTOM_TAB
122 // display mode.
123 if (web_app::AppBrowserController::IsForWebAppBrowser(browser))
124 return ToolbarView::DisplayMode::CUSTOM_TAB;
125
126 if (browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP))
127 return ToolbarView::DisplayMode::NORMAL;
128
129 return ToolbarView::DisplayMode::LOCATION;
130 }
131
GetViewCommandMap()132 const base::flat_map<int, int>& GetViewCommandMap() {
133 static const base::NoDestructor<base::flat_map<int, int>> kViewCommandMap(
134 {{VIEW_ID_BACK_BUTTON, IDC_BACK},
135 {VIEW_ID_FORWARD_BUTTON, IDC_FORWARD},
136 {VIEW_ID_HOME_BUTTON, IDC_HOME},
137 {VIEW_ID_RELOAD_BUTTON, IDC_RELOAD},
138 {VIEW_ID_AVATAR_BUTTON, IDC_SHOW_AVATAR_MENU}});
139 return *kViewCommandMap;
140 }
141
142 } // namespace
143
144 // static
145 const char ToolbarView::kViewClassName[] = "ToolbarView";
146
147 ////////////////////////////////////////////////////////////////////////////////
148 // ToolbarView, public:
149
ToolbarView(Browser * browser,BrowserView * browser_view)150 ToolbarView::ToolbarView(Browser* browser, BrowserView* browser_view)
151 : AnimationDelegateViews(this),
152 browser_(browser),
153 browser_view_(browser_view),
154 app_menu_icon_controller_(browser->profile(), this),
155 display_mode_(GetDisplayMode(browser)) {
156 SetID(VIEW_ID_TOOLBAR);
157
158 UpgradeDetector::GetInstance()->AddObserver(this);
159
160 if (display_mode_ == DisplayMode::NORMAL) {
161 SetBackground(std::make_unique<TopContainerBackground>(browser_view));
162
163 for (const auto& view_and_command : GetViewCommandMap())
164 chrome::AddCommandObserver(browser_, view_and_command.second, this);
165 }
166 }
167
~ToolbarView()168 ToolbarView::~ToolbarView() {
169 UpgradeDetector::GetInstance()->RemoveObserver(this);
170
171 if (display_mode_ != DisplayMode::NORMAL)
172 return;
173
174 for (const auto& view_and_command : GetViewCommandMap())
175 chrome::RemoveCommandObserver(browser_, view_and_command.second, this);
176 }
177
Init()178 void ToolbarView::Init() {
179 auto location_bar = std::make_unique<LocationBarView>(
180 browser_, browser_->profile(), browser_->command_controller(), this,
181 display_mode_ != DisplayMode::NORMAL);
182 // Make sure the toolbar shows by default.
183 size_animation_.Reset(1);
184
185 if (display_mode_ != DisplayMode::NORMAL) {
186 location_bar_ = AddChildView(std::move(location_bar));
187 location_bar_->Init();
188
189 if (display_mode_ == DisplayMode::CUSTOM_TAB) {
190 custom_tab_bar_ =
191 AddChildView(std::make_unique<CustomTabBarView>(browser_view_, this));
192 }
193
194 SetLayoutManager(std::make_unique<views::FillLayout>());
195 initialized_ = true;
196 return;
197 }
198
199 const auto callback = [](Browser* browser, int command,
200 const ui::Event& event) {
201 chrome::ExecuteCommandWithDisposition(
202 browser, command, ui::DispositionFromEventFlags(event.flags()));
203 };
204 std::unique_ptr<ToolbarButton> back = std::make_unique<BackForwardButton>(
205 BackForwardButton::Direction::kBack,
206 base::BindRepeating(callback, browser_, IDC_BACK), browser_);
207
208 std::unique_ptr<ToolbarButton> forward = std::make_unique<BackForwardButton>(
209 BackForwardButton::Direction::kForward,
210 base::BindRepeating(callback, browser_, IDC_FORWARD), browser_);
211
212 std::unique_ptr<ReloadButton> reload =
213 std::make_unique<ReloadButton>(browser_->command_controller());
214
215 std::unique_ptr<HomeButton> home = std::make_unique<HomeButton>(
216 base::BindRepeating(callback, browser_, IDC_HOME), browser_);
217
218 std::unique_ptr<ExtensionsToolbarContainer> extensions_container;
219 std::unique_ptr<BrowserActionsContainer> browser_actions;
220
221 // Do not create the extensions or browser actions container if it is a guest
222 // profile (only regular and incognito profiles host extensions).
223 if (!browser_->profile()->IsGuestSession()) {
224 if (base::FeatureList::IsEnabled(features::kExtensionsToolbarMenu)) {
225 extensions_container =
226 std::make_unique<ExtensionsToolbarContainer>(browser_);
227 } else {
228 browser_actions =
229 std::make_unique<BrowserActionsContainer>(browser_, nullptr, this);
230 }
231 }
232 std::unique_ptr<media_router::CastToolbarButton> cast;
233 if (media_router::MediaRouterEnabled(browser_->profile()))
234 cast = media_router::CastToolbarButton::Create(browser_);
235
236 std::unique_ptr<MediaToolbarButtonView> media_button;
237 if (base::FeatureList::IsEnabled(media::kGlobalMediaControls)) {
238 media_button = std::make_unique<MediaToolbarButtonView>(browser_view_);
239 }
240
241 std::unique_ptr<ToolbarAccountIconContainerView>
242 toolbar_account_icon_container;
243 bool show_avatar_toolbar_button = true;
244 #if defined(OS_CHROMEOS)
245 if (!base::FeatureList::IsEnabled(chromeos::features::kAvatarToolbarButton)) {
246 // ChromeOS only badges Incognito and Guest icons in the browser window.
247 show_avatar_toolbar_button = browser_->profile()->IsOffTheRecord() ||
248 browser_->profile()->IsGuestSession();
249 }
250 #endif
251 if (base::FeatureList::IsEnabled(
252 autofill::features::kAutofillEnableToolbarStatusChip)) {
253 // The avatar button is contained inside the page-action container and
254 // should not be created twice.
255 show_avatar_toolbar_button = false;
256 toolbar_account_icon_container =
257 std::make_unique<ToolbarAccountIconContainerView>(browser_);
258 }
259
260 // Always add children in order from left to right, for accessibility.
261 back_ = AddChildView(std::move(back));
262 forward_ = AddChildView(std::move(forward));
263 reload_ = AddChildView(std::move(reload));
264 home_ = AddChildView(std::move(home));
265 location_bar_ = AddChildView(std::move(location_bar));
266 if (browser_actions)
267 browser_actions_ = AddChildView(std::move(browser_actions));
268
269 if (extensions_container)
270 extensions_container_ = AddChildView(std::move(extensions_container));
271
272 if (cast)
273 cast_ = AddChildView(std::move(cast));
274
275 if (media_button)
276 media_button_ = AddChildView(std::move(media_button));
277
278 if (toolbar_account_icon_container) {
279 toolbar_account_icon_container_ =
280 AddChildView(std::move(toolbar_account_icon_container));
281 avatar_ = toolbar_account_icon_container_->avatar_button();
282 } else {
283 // TODO(crbug.com/932818): Remove this once the
284 // |kAutofillEnableToolbarStatusChip| is fully launched.
285 avatar_ = AddChildView(std::make_unique<AvatarToolbarButton>(browser_));
286 avatar_->SetVisible(show_avatar_toolbar_button);
287 }
288
289 auto app_menu_button = std::make_unique<BrowserAppMenuButton>(
290 base::BindRepeating(&ToolbarView::AppMenuButtonPressed,
291 base::Unretained(this)),
292 this);
293 app_menu_button->SetFlipCanvasOnPaintForRTLUI(true);
294 app_menu_button->SetAccessibleName(
295 l10n_util::GetStringUTF16(IDS_ACCNAME_APP));
296 app_menu_button->SetTooltipText(
297 l10n_util::GetStringUTF16(IDS_APPMENU_TOOLTIP));
298 app_menu_button->SetID(VIEW_ID_APP_MENU);
299 app_menu_button_ = AddChildView(std::move(app_menu_button));
300
301 LoadImages();
302
303 // Start global error services now so we set the icon on the menu correctly.
304 #if !defined(OS_CHROMEOS)
305 SigninGlobalErrorFactory::GetForProfile(browser_->profile());
306 #if defined(OS_WIN) || defined(OS_MAC)
307 RecoveryInstallGlobalErrorFactory::GetForProfile(browser_->profile());
308 #endif
309 #endif // OS_CHROMEOS
310
311 // Set the button icon based on the system state. Do this after
312 // |app_menu_button_| has been added as a bubble may be shown that needs
313 // the widget (widget found by way of app_menu_button_->GetWidget()).
314 app_menu_icon_controller_.UpdateDelegate();
315
316 location_bar_->Init();
317
318 show_home_button_.Init(
319 prefs::kShowHomeButton, browser_->profile()->GetPrefs(),
320 base::BindRepeating(&ToolbarView::OnShowHomeButtonChanged,
321 base::Unretained(this)));
322 UpdateHomeButtonVisibility();
323
324 InitLayout();
325
326 for (auto* button : std::array<views::Button*, 5>{back_, forward_, reload_,
327 home_, avatar_}) {
328 if (button)
329 button->set_tag(GetViewCommandMap().at(button->GetID()));
330 }
331
332 initialized_ = true;
333 }
334
AnimationEnded(const gfx::Animation * animation)335 void ToolbarView::AnimationEnded(const gfx::Animation* animation) {
336 if (animation->GetCurrentValue() == 0)
337 SetToolbarVisibility(false);
338 browser()->window()->ToolbarSizeChanged(/*is_animating=*/false);
339 }
340
AnimationProgressed(const gfx::Animation * animation)341 void ToolbarView::AnimationProgressed(const gfx::Animation* animation) {
342 browser()->window()->ToolbarSizeChanged(/*is_animating=*/true);
343 }
344
Update(WebContents * tab)345 void ToolbarView::Update(WebContents* tab) {
346 if (location_bar_)
347 location_bar_->Update(tab);
348
349 if (browser_actions_)
350 browser_actions_->RefreshToolbarActionViews();
351
352 if (extensions_container_)
353 extensions_container_->UpdateAllIcons();
354
355 if (reload_)
356 reload_->SetMenuEnabled(chrome::IsDebuggerAttachedToCurrentTab(browser_));
357
358 if (toolbar_account_icon_container_)
359 toolbar_account_icon_container_->UpdateAllIcons();
360 }
361
SetToolbarVisibility(bool visible)362 void ToolbarView::SetToolbarVisibility(bool visible) {
363 SetVisible(visible);
364 views::View* bar = display_mode_ == DisplayMode::CUSTOM_TAB
365 ? static_cast<views::View*>(custom_tab_bar_)
366 : static_cast<views::View*>(location_bar_);
367
368 bar->SetVisible(visible);
369 }
370
UpdateCustomTabBarVisibility(bool visible,bool animate)371 void ToolbarView::UpdateCustomTabBarVisibility(bool visible, bool animate) {
372 DCHECK_EQ(display_mode_, DisplayMode::CUSTOM_TAB);
373
374 if (!animate) {
375 size_animation_.Reset(visible ? 1.0 : 0.0);
376 SetToolbarVisibility(visible);
377 browser()->window()->ToolbarSizeChanged(/*is_animating=*/false);
378 return;
379 }
380
381 if (visible) {
382 SetToolbarVisibility(true);
383 size_animation_.Show();
384 } else {
385 size_animation_.Hide();
386 }
387 }
388
UpdateForWebUITabStrip()389 void ToolbarView::UpdateForWebUITabStrip() {
390 #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
391 if (browser_view_->webui_tab_strip() && app_menu_button_) {
392 const int insertion_index = GetIndexOf(app_menu_button_);
393 AddChildViewAt(browser_view_->webui_tab_strip()->CreateTabCounter(),
394 insertion_index);
395 LoadImages();
396 }
397 #endif // BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
398 }
399
ResetTabState(WebContents * tab)400 void ToolbarView::ResetTabState(WebContents* tab) {
401 if (location_bar_)
402 location_bar_->ResetTabState(tab);
403 }
404
SetPaneFocusAndFocusAppMenu()405 void ToolbarView::SetPaneFocusAndFocusAppMenu() {
406 if (app_menu_button_)
407 SetPaneFocus(app_menu_button_);
408 }
409
IsAppMenuFocused()410 bool ToolbarView::IsAppMenuFocused() {
411 return app_menu_button_ && app_menu_button_->HasFocus();
412 }
413
ShowIntentPickerBubble(std::vector<IntentPickerBubbleView::AppInfo> app_info,bool show_stay_in_chrome,bool show_remember_selection,PageActionIconType icon_type,const base::Optional<url::Origin> & initiating_origin,IntentPickerResponse callback)414 void ToolbarView::ShowIntentPickerBubble(
415 std::vector<IntentPickerBubbleView::AppInfo> app_info,
416 bool show_stay_in_chrome,
417 bool show_remember_selection,
418 PageActionIconType icon_type,
419 const base::Optional<url::Origin>& initiating_origin,
420 IntentPickerResponse callback) {
421 PageActionIconView* const intent_picker_view =
422 GetPageActionIconView(icon_type);
423 if (!intent_picker_view)
424 return;
425
426 IntentPickerBubbleView::ShowBubble(
427 location_bar(), intent_picker_view, icon_type, GetWebContents(),
428 std::move(app_info), show_stay_in_chrome, show_remember_selection,
429 initiating_origin, std::move(callback));
430 // TODO(knollr): find a way that the icon updates implicitly.
431 intent_picker_view->Update();
432 }
433
ShowBookmarkBubble(const GURL & url,bool already_bookmarked,bookmarks::BookmarkBubbleObserver * observer)434 void ToolbarView::ShowBookmarkBubble(
435 const GURL& url,
436 bool already_bookmarked,
437 bookmarks::BookmarkBubbleObserver* observer) {
438 views::View* const anchor_view = location_bar();
439 PageActionIconView* const bookmark_star_icon =
440 GetPageActionIconView(PageActionIconType::kBookmarkStar);
441
442 std::unique_ptr<BubbleSyncPromoDelegate> delegate;
443 #if !defined(OS_CHROMEOS)
444 // ChromeOS does not show the signin promo.
445 delegate = std::make_unique<BookmarkBubbleSignInDelegate>(browser_);
446 #endif
447 BookmarkBubbleView::ShowBubble(anchor_view, bookmark_star_icon, observer,
448 std::move(delegate), browser_->profile(), url,
449 already_bookmarked);
450 }
451
GetExtensionsButton() const452 ExtensionsToolbarButton* ToolbarView::GetExtensionsButton() const {
453 return extensions_container_->extensions_button();
454 }
455
456 ////////////////////////////////////////////////////////////////////////////////
457 // ToolbarView, LocationBarView::Delegate implementation:
458
GetWebContents()459 WebContents* ToolbarView::GetWebContents() {
460 return browser_->tab_strip_model()->GetActiveWebContents();
461 }
462
GetLocationBarModel()463 LocationBarModel* ToolbarView::GetLocationBarModel() {
464 return browser_->location_bar_model();
465 }
466
GetLocationBarModel() const467 const LocationBarModel* ToolbarView::GetLocationBarModel() const {
468 return browser_->location_bar_model();
469 }
470
471 ContentSettingBubbleModelDelegate*
GetContentSettingBubbleModelDelegate()472 ToolbarView::GetContentSettingBubbleModelDelegate() {
473 return browser_->content_setting_bubble_model_delegate();
474 }
475
476 ////////////////////////////////////////////////////////////////////////////////
477 // ToolbarView, BrowserActionsContainer::Delegate implementation:
478
GetOverflowReferenceView()479 views::LabelButton* ToolbarView::GetOverflowReferenceView() {
480 return app_menu_button_;
481 }
482
GetMaxBrowserActionsWidth() const483 base::Optional<int> ToolbarView::GetMaxBrowserActionsWidth() const {
484 // The browser actions container is allowed to grow, but only up until the
485 // omnibox reaches its preferred size. So its maximum allowed width is its
486 // current size, plus any that the omnibox could give up.
487 return std::max(0, (browser_actions_ ? browser_actions_->width()
488 : extensions_container_->width()) +
489 (location_bar_->width() -
490 location_bar_->GetPreferredSize().width()));
491 }
492
CreateToolbarActionsBar(ToolbarActionsBarDelegate * delegate,Browser * browser,ToolbarActionsBar * main_bar) const493 std::unique_ptr<ToolbarActionsBar> ToolbarView::CreateToolbarActionsBar(
494 ToolbarActionsBarDelegate* delegate,
495 Browser* browser,
496 ToolbarActionsBar* main_bar) const {
497 DCHECK_EQ(browser_, browser);
498 return std::make_unique<ToolbarActionsBar>(delegate, browser, main_bar);
499 }
500
501 ////////////////////////////////////////////////////////////////////////////////
502 // ToolbarView, CommandObserver implementation:
503
EnabledStateChangedForCommand(int id,bool enabled)504 void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
505 DCHECK(display_mode_ == DisplayMode::NORMAL);
506 const std::array<views::Button*, 5> kButtons{back_, forward_, reload_, home_,
507 avatar_};
508 auto* button = *base::ranges::find(kButtons, id, &views::Button::tag);
509 DCHECK(button);
510 button->SetEnabled(enabled);
511 }
512
513 ////////////////////////////////////////////////////////////////////////////////
514 // ToolbarView, UpgradeObserver implementation:
OnOutdatedInstall()515 void ToolbarView::OnOutdatedInstall() {
516 ShowOutdatedInstallNotification(true);
517 }
518
OnOutdatedInstallNoAutoUpdate()519 void ToolbarView::OnOutdatedInstallNoAutoUpdate() {
520 ShowOutdatedInstallNotification(false);
521 }
522
OnCriticalUpgradeInstalled()523 void ToolbarView::OnCriticalUpgradeInstalled() {
524 ShowCriticalNotification();
525 }
526
527 ////////////////////////////////////////////////////////////////////////////////
528 // ToolbarView, ui::AcceleratorProvider implementation:
529
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator) const530 bool ToolbarView::GetAcceleratorForCommandId(int command_id,
531 ui::Accelerator* accelerator) const {
532 return GetWidget()->GetAccelerator(command_id, accelerator);
533 }
534
535 ////////////////////////////////////////////////////////////////////////////////
536 // ToolbarView, views::View overrides:
537
CalculatePreferredSize() const538 gfx::Size ToolbarView::CalculatePreferredSize() const {
539 gfx::Size size;
540 switch (display_mode_) {
541 case DisplayMode::CUSTOM_TAB:
542 size = custom_tab_bar_->GetPreferredSize();
543 break;
544 case DisplayMode::LOCATION:
545 size = location_bar_->GetPreferredSize();
546 break;
547 case DisplayMode::NORMAL:
548 size = View::CalculatePreferredSize();
549 // Because there are odd cases where something causes one of the views in
550 // the toolbar to report an unreasonable height (see crbug.com/985909), we
551 // cap the height at the size of known child views (location bar and back
552 // button) plus margins.
553 // TODO(crbug.com/1033627): Figure out why the height reports incorrectly
554 // on some installations.
555 if (layout_manager_ && location_bar_->GetVisible()) {
556 const int max_height =
557 std::max(location_bar_->GetPreferredSize().height(),
558 back_->GetPreferredSize().height()) +
559 layout_manager_->interior_margin().height();
560 size.SetToMin({size.width(), max_height});
561 }
562 }
563 size.set_height(size.height() * size_animation_.GetCurrentValue());
564 return size;
565 }
566
GetMinimumSize() const567 gfx::Size ToolbarView::GetMinimumSize() const {
568 gfx::Size size;
569 switch (display_mode_) {
570 case DisplayMode::CUSTOM_TAB:
571 size = custom_tab_bar_->GetMinimumSize();
572 break;
573 case DisplayMode::LOCATION:
574 size = location_bar_->GetMinimumSize();
575 break;
576 case DisplayMode::NORMAL:
577 size = View::GetMinimumSize();
578 // Because there are odd cases where something causes one of the views in
579 // the toolbar to report an unreasonable height (see crbug.com/985909), we
580 // cap the height at the size of known child views (location bar and back
581 // button) plus margins.
582 // TODO(crbug.com/1033627): Figure out why the height reports incorrectly
583 // on some installations.
584 if (layout_manager_ && location_bar_->GetVisible()) {
585 const int max_height =
586 std::max(location_bar_->GetMinimumSize().height(),
587 back_->GetMinimumSize().height()) +
588 layout_manager_->interior_margin().height();
589 size.SetToMin({size.width(), max_height});
590 }
591 }
592 size.set_height(size.height() * size_animation_.GetCurrentValue());
593 return size;
594 }
595
Layout()596 void ToolbarView::Layout() {
597 // If we have not been initialized yet just do nothing.
598 if (!initialized_)
599 return;
600
601 if (display_mode_ == DisplayMode::CUSTOM_TAB) {
602 custom_tab_bar_->SetBounds(0, 0, width(),
603 custom_tab_bar_->GetPreferredSize().height());
604 location_bar_->SetVisible(false);
605 return;
606 }
607
608 if (display_mode_ == DisplayMode::LOCATION) {
609 location_bar_->SetBounds(0, 0, width(),
610 location_bar_->GetPreferredSize().height());
611 return;
612 }
613
614 LayoutCommon();
615
616 // Call super implementation to ensure layout manager and child layouts
617 // happen.
618 AccessiblePaneView::Layout();
619 }
620
OnThemeChanged()621 void ToolbarView::OnThemeChanged() {
622 views::AccessiblePaneView::OnThemeChanged();
623 if (!initialized_)
624 return;
625
626 if (display_mode_ == DisplayMode::NORMAL)
627 LoadImages();
628
629 SchedulePaint();
630 }
631
GetClassName() const632 const char* ToolbarView::GetClassName() const {
633 return kViewClassName;
634 }
635
AcceleratorPressed(const ui::Accelerator & accelerator)636 bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) {
637 const views::View* focused_view = focus_manager()->GetFocusedView();
638 if (focused_view && (focused_view->GetID() == VIEW_ID_OMNIBOX))
639 return false; // Let the omnibox handle all accelerator events.
640 return AccessiblePaneView::AcceleratorPressed(accelerator);
641 }
642
ChildPreferredSizeChanged(views::View * child)643 void ToolbarView::ChildPreferredSizeChanged(views::View* child) {
644 InvalidateLayout();
645 if (size() != GetPreferredSize())
646 PreferredSizeChanged();
647 }
648
649 ////////////////////////////////////////////////////////////////////////////////
650 // ToolbarView, protected:
651
652 // Override this so that when the user presses F6 to rotate toolbar panes,
653 // the location bar gets focus, not the first control in the toolbar - and
654 // also so that it selects all content in the location bar.
SetPaneFocusAndFocusDefault()655 bool ToolbarView::SetPaneFocusAndFocusDefault() {
656 if (!location_bar_->HasFocus()) {
657 SetPaneFocus(location_bar_);
658 location_bar_->FocusLocation(true);
659 return true;
660 }
661
662 if (!AccessiblePaneView::SetPaneFocusAndFocusDefault())
663 return false;
664 browser_->window()->RotatePaneFocus(true);
665 return true;
666 }
667
668 ////////////////////////////////////////////////////////////////////////////////
669 // ToolbarView, private:
670
InitLayout()671 void ToolbarView::InitLayout() {
672 const int default_margin = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
673 // TODO(dfried): rename this constant.
674 const int location_bar_margin = GetLayoutConstant(TOOLBAR_STANDARD_SPACING);
675 const views::FlexSpecification account_container_flex_rule =
676 views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
677 views::MaximumFlexSizeRule::kPreferred)
678 .WithOrder(1);
679 const views::FlexSpecification location_bar_flex_rule =
680 views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
681 views::MaximumFlexSizeRule::kUnbounded)
682 .WithOrder(2);
683 constexpr int kExtensionsFlexOrder = 3;
684
685 layout_manager_ = SetLayoutManager(std::make_unique<views::FlexLayout>());
686
687 layout_manager_->SetOrientation(views::LayoutOrientation::kHorizontal)
688 .SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
689 .SetCollapseMargins(true)
690 .SetDefault(views::kMarginsKey, gfx::Insets(0, default_margin));
691
692 location_bar_->SetProperty(views::kFlexBehaviorKey, location_bar_flex_rule);
693 location_bar_->SetProperty(views::kMarginsKey,
694 gfx::Insets(0, location_bar_margin));
695
696 if (browser_actions_) {
697 const views::FlexSpecification browser_actions_flex_rule =
698 views::FlexSpecification(BrowserActionsContainer::GetFlexRule())
699 .WithOrder(kExtensionsFlexOrder);
700
701 browser_actions_->SetProperty(views::kFlexBehaviorKey,
702 browser_actions_flex_rule);
703 browser_actions_->SetProperty(views::kMarginsKey, gfx::Insets());
704 browser_actions_->SetProperty(views::kInternalPaddingKey,
705 gfx::Insets(0, location_bar_margin));
706 } else if (extensions_container_) {
707 const views::FlexSpecification extensions_flex_rule =
708 views::FlexSpecification(
709 extensions_container_->animating_layout_manager()
710 ->GetDefaultFlexRule())
711 .WithOrder(kExtensionsFlexOrder);
712
713 extensions_container_->SetProperty(views::kFlexBehaviorKey,
714 extensions_flex_rule);
715 }
716
717 if (toolbar_account_icon_container_) {
718 toolbar_account_icon_container_->SetProperty(views::kFlexBehaviorKey,
719 account_container_flex_rule);
720 }
721
722 LayoutCommon();
723 }
724
LayoutCommon()725 void ToolbarView::LayoutCommon() {
726 DCHECK(display_mode_ == DisplayMode::NORMAL);
727
728 const gfx::Insets interior_margin =
729 GetLayoutInsets(LayoutInset::TOOLBAR_INTERIOR_MARGIN);
730 layout_manager_->SetInteriorMargin(interior_margin);
731
732 // Extend buttons to the window edge if we're either in a maximized or
733 // fullscreen window. This makes the buttons easier to hit, see Fitts' law.
734 const bool extend_buttons_to_edge =
735 browser_->window() &&
736 (browser_->window()->IsMaximized() || browser_->window()->IsFullscreen());
737 back_->SetLeadingMargin(extend_buttons_to_edge ? interior_margin.left() : 0);
738 app_menu_button_->SetTrailingMargin(
739 extend_buttons_to_edge ? interior_margin.right() : 0);
740
741 // Cast button visibility is controlled externally.
742 }
743
744 // AppMenuIconController::Delegate:
UpdateTypeAndSeverity(AppMenuIconController::TypeAndSeverity type_and_severity)745 void ToolbarView::UpdateTypeAndSeverity(
746 AppMenuIconController::TypeAndSeverity type_and_severity) {
747 // There's no app menu in tabless windows.
748 if (!app_menu_button_)
749 return;
750
751 base::string16 accname_app = l10n_util::GetStringUTF16(IDS_ACCNAME_APP);
752 if (type_and_severity.type ==
753 AppMenuIconController::IconType::UPGRADE_NOTIFICATION) {
754 accname_app = l10n_util::GetStringFUTF16(
755 IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app);
756 }
757 app_menu_button_->SetAccessibleName(accname_app);
758 app_menu_button_->SetTypeAndSeverity(type_and_severity);
759 }
760
GetDefaultColorForSeverity(AppMenuIconController::Severity severity) const761 SkColor ToolbarView::GetDefaultColorForSeverity(
762 AppMenuIconController::Severity severity) const {
763 ui::NativeTheme::ColorId color_id;
764 switch (severity) {
765 case AppMenuIconController::Severity::NONE:
766 return GetThemeProvider()->GetColor(
767 ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
768 case AppMenuIconController::Severity::LOW:
769 color_id = ui::NativeTheme::kColorId_AlertSeverityLow;
770 break;
771 case AppMenuIconController::Severity::MEDIUM:
772 color_id = ui::NativeTheme::kColorId_AlertSeverityMedium;
773 break;
774 case AppMenuIconController::Severity::HIGH:
775 color_id = ui::NativeTheme::kColorId_AlertSeverityHigh;
776 break;
777 }
778 return GetNativeTheme()->GetSystemColor(color_id);
779 }
780
781 // ToolbarButtonProvider:
GetBrowserActionsContainer()782 BrowserActionsContainer* ToolbarView::GetBrowserActionsContainer() {
783 CHECK(!base::FeatureList::IsEnabled(features::kExtensionsToolbarMenu));
784 return browser_actions_;
785 }
786
GetExtensionsToolbarContainer()787 ExtensionsToolbarContainer* ToolbarView::GetExtensionsToolbarContainer() {
788 return extensions_container_;
789 }
790
GetToolbarButtonSize() const791 gfx::Size ToolbarView::GetToolbarButtonSize() const {
792 const int size = GetLayoutConstant(LayoutConstant::TOOLBAR_BUTTON_HEIGHT);
793 return gfx::Size(size, size);
794 }
795
GetDefaultExtensionDialogAnchorView()796 views::View* ToolbarView::GetDefaultExtensionDialogAnchorView() {
797 if (extensions_container_)
798 return extensions_container_->extensions_button();
799 return GetAppMenuButton();
800 }
801
GetPageActionIconView(PageActionIconType type)802 PageActionIconView* ToolbarView::GetPageActionIconView(
803 PageActionIconType type) {
804 PageActionIconView* icon =
805 location_bar()->page_action_icon_controller()->GetIconView(type);
806 if (icon)
807 return icon;
808 return toolbar_account_icon_container_
809 ? toolbar_account_icon_container_->page_action_icon_controller()
810 ->GetIconView(type)
811 : nullptr;
812 }
813
GetAppMenuButton()814 AppMenuButton* ToolbarView::GetAppMenuButton() {
815 if (app_menu_button_)
816 return app_menu_button_;
817
818 return custom_tab_bar_ ? custom_tab_bar_->custom_tab_menu_button() : nullptr;
819 }
820
GetFindBarBoundingBox(int contents_bottom)821 gfx::Rect ToolbarView::GetFindBarBoundingBox(int contents_bottom) {
822 if (!browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR))
823 return gfx::Rect();
824
825 if (!location_bar_->IsDrawn())
826 return gfx::Rect();
827
828 gfx::Rect bounds =
829 location_bar_->ConvertRectToWidget(location_bar_->GetLocalBounds());
830 return gfx::Rect(bounds.x(), bounds.bottom(), bounds.width(),
831 contents_bottom - bounds.bottom());
832 }
833
FocusToolbar()834 void ToolbarView::FocusToolbar() {
835 SetPaneFocus(nullptr);
836 }
837
GetAsAccessiblePaneView()838 views::AccessiblePaneView* ToolbarView::GetAsAccessiblePaneView() {
839 return this;
840 }
841
GetAnchorView(PageActionIconType type)842 views::View* ToolbarView::GetAnchorView(PageActionIconType type) {
843 // Return the container visually housing the icon so all the bubbles align
844 // with the same visible edge.
845 if (toolbar_account_icon_container_) {
846 views::View* icon = GetPageActionIconView(type);
847 if (toolbar_account_icon_container_->Contains(icon)) {
848 DCHECK(base::FeatureList::IsEnabled(
849 autofill::features::kAutofillEnableToolbarStatusChip));
850 return toolbar_account_icon_container_;
851 }
852 }
853 return location_bar_;
854 }
855
ZoomChangedForActiveTab(bool can_show_bubble)856 void ToolbarView::ZoomChangedForActiveTab(bool can_show_bubble) {
857 location_bar_->page_action_icon_controller()->ZoomChangedForActiveTab(
858 can_show_bubble);
859 }
860
GetAvatarToolbarButton()861 AvatarToolbarButton* ToolbarView::GetAvatarToolbarButton() {
862 if (toolbar_account_icon_container_ &&
863 toolbar_account_icon_container_->avatar_button()) {
864 return toolbar_account_icon_container_->avatar_button();
865 }
866
867 if (avatar_)
868 return avatar_;
869
870 return nullptr;
871 }
872
GetBackButton()873 ToolbarButton* ToolbarView::GetBackButton() {
874 return back_;
875 }
876
GetReloadButton()877 ReloadButton* ToolbarView::GetReloadButton() {
878 return reload_;
879 }
880
GetDropIndex(const ui::DropTargetEvent & event)881 BrowserRootView::DropIndex ToolbarView::GetDropIndex(
882 const ui::DropTargetEvent& event) {
883 return {browser_->tab_strip_model()->active_index(), false};
884 }
885
GetViewForDrop()886 views::View* ToolbarView::GetViewForDrop() {
887 return this;
888 }
889
LoadImages()890 void ToolbarView::LoadImages() {
891 DCHECK_EQ(display_mode_, DisplayMode::NORMAL);
892
893 if (browser_actions_) {
894 browser_actions_->SetSeparatorColor(GetThemeProvider()->GetColor(
895 ThemeProperties::COLOR_TOOLBAR_VERTICAL_SEPARATOR));
896 }
897
898 if (extensions_container_)
899 extensions_container_->UpdateAllIcons();
900
901 if (toolbar_account_icon_container_)
902 toolbar_account_icon_container_->UpdateAllIcons();
903 }
904
ShowCriticalNotification()905 void ToolbarView::ShowCriticalNotification() {
906 #if defined(OS_WIN)
907 views::BubbleDialogDelegateView::CreateBubble(
908 new CriticalNotificationBubbleView(app_menu_button_))->Show();
909 #endif
910 }
911
ShowOutdatedInstallNotification(bool auto_update_enabled)912 void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) {
913 #if !defined(OS_CHROMEOS)
914 OutdatedUpgradeBubbleView::ShowBubble(app_menu_button_, browser_,
915 auto_update_enabled);
916 #endif
917 }
918
OnShowHomeButtonChanged()919 void ToolbarView::OnShowHomeButtonChanged() {
920 UpdateHomeButtonVisibility();
921 Layout();
922 SchedulePaint();
923 }
924
UpdateHomeButtonVisibility()925 void ToolbarView::UpdateHomeButtonVisibility() {
926 home_->SetVisible(show_home_button_.GetValue());
927 }
928
OnTouchUiChanged()929 void ToolbarView::OnTouchUiChanged() {
930 if (display_mode_ == DisplayMode::NORMAL) {
931 // Update the internal margins for touch layout.
932 // TODO(dfried): I think we can do better than this by making the touch UI
933 // code cleaner.
934 const int default_margin = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
935 const int location_bar_margin = GetLayoutConstant(TOOLBAR_STANDARD_SPACING);
936 layout_manager_->SetDefault(views::kMarginsKey,
937 gfx::Insets(0, default_margin));
938 location_bar_->SetProperty(views::kMarginsKey,
939 gfx::Insets(0, location_bar_margin));
940 if (browser_actions_) {
941 browser_actions_->SetProperty(views::kInternalPaddingKey,
942 gfx::Insets(0, location_bar_margin));
943 }
944
945 LoadImages();
946 PreferredSizeChanged();
947 }
948 }
949
AppMenuButtonPressed(const ui::Event & event)950 void ToolbarView::AppMenuButtonPressed(const ui::Event& event) {
951 app_menu_button_->ShowMenu(event.IsKeyEvent()
952 ? views::MenuRunner::SHOULD_SHOW_MNEMONICS
953 : views::MenuRunner::NO_FLAGS);
954 }
955