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