Infobar material design refresh: layout
[chromium-blink-merge.git] / chrome / browser / ui / views / toolbar / toolbar_view.cc
blob5f1b2a608f0a7b92ac4182509a32659576a85043
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.
5 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
7 #include <algorithm>
9 #include "base/command_line.h"
10 #include "base/i18n/number_formatting.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/trace_event/trace_event.h"
14 #include "chrome/app/chrome_command_ids.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/command_updater.h"
17 #include "chrome/browser/extensions/extension_commands_global_registry.h"
18 #include "chrome/browser/extensions/extension_util.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/themes/theme_properties.h"
21 #include "chrome/browser/themes/theme_service.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_command_controller.h"
24 #include "chrome/browser/ui/browser_commands.h"
25 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
26 #include "chrome/browser/ui/browser_tabstrip.h"
27 #include "chrome/browser/ui/browser_window.h"
28 #include "chrome/browser/ui/global_error/global_error_service.h"
29 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
30 #include "chrome/browser/ui/tabs/tab_strip_model.h"
31 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
32 #include "chrome/browser/ui/view_ids.h"
33 #include "chrome/browser/ui/views/extensions/extension_popup.h"
34 #include "chrome/browser/ui/views/frame/browser_view.h"
35 #include "chrome/browser/ui/views/location_bar/page_action_image_view.h"
36 #include "chrome/browser/ui/views/location_bar/page_action_with_badge_view.h"
37 #include "chrome/browser/ui/views/location_bar/star_view.h"
38 #include "chrome/browser/ui/views/location_bar/translate_icon_view.h"
39 #include "chrome/browser/ui/views/outdated_upgrade_bubble_view.h"
40 #include "chrome/browser/ui/views/toolbar/back_button.h"
41 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
42 #include "chrome/browser/ui/views/toolbar/home_button.h"
43 #include "chrome/browser/ui/views/toolbar/reload_button.h"
44 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
45 #include "chrome/browser/ui/views/toolbar/wrench_menu.h"
46 #include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h"
47 #include "chrome/common/chrome_switches.h"
48 #include "chrome/common/pref_names.h"
49 #include "chrome/grit/chromium_strings.h"
50 #include "chrome/grit/generated_resources.h"
51 #include "components/omnibox/browser/omnibox_view.h"
52 #include "content/public/browser/browser_accessibility_state.h"
53 #include "content/public/browser/notification_service.h"
54 #include "content/public/browser/render_view_host.h"
55 #include "content/public/browser/user_metrics.h"
56 #include "content/public/browser/web_contents.h"
57 #include "grit/theme_resources.h"
58 #include "ui/accessibility/ax_view_state.h"
59 #include "ui/base/l10n/l10n_util.h"
60 #include "ui/base/resource/material_design/material_design_controller.h"
61 #include "ui/base/theme_provider.h"
62 #include "ui/base/window_open_disposition.h"
63 #include "ui/compositor/layer.h"
64 #include "ui/gfx/canvas.h"
65 #include "ui/gfx/image/canvas_image_source.h"
66 #include "ui/keyboard/keyboard_controller.h"
67 #include "ui/native_theme/native_theme_aura.h"
68 #include "ui/views/controls/menu/menu_listener.h"
69 #include "ui/views/focus/view_storage.h"
70 #include "ui/views/view_targeter.h"
71 #include "ui/views/widget/tooltip_manager.h"
72 #include "ui/views/widget/widget.h"
73 #include "ui/views/window/non_client_view.h"
75 #if defined(OS_WIN)
76 #include "chrome/browser/recovery/recovery_install_global_error_factory.h"
77 #include "chrome/browser/ui/views/conflicting_module_view_win.h"
78 #include "chrome/browser/ui/views/critical_notification_bubble_view.h"
79 #endif
81 #if !defined(OS_CHROMEOS)
82 #include "chrome/browser/signin/signin_global_error_factory.h"
83 #include "chrome/browser/sync/sync_global_error_factory.h"
84 #endif
86 #if defined(USE_ASH)
87 #include "ash/shell.h"
88 #endif
90 using base::UserMetricsAction;
91 using content::WebContents;
93 namespace {
95 #if !defined(OS_CHROMEOS)
96 bool HasAshShell() {
97 #if defined(USE_ASH)
98 return ash::Shell::HasInstance();
99 #else
100 return false;
101 #endif // USE_ASH
103 #endif // OS_CHROMEOS
105 } // namespace
107 // static
108 const char ToolbarView::kViewClassName[] = "ToolbarView";
110 ////////////////////////////////////////////////////////////////////////////////
111 // ToolbarView, public:
113 ToolbarView::ToolbarView(Browser* browser)
114 : back_(NULL),
115 forward_(NULL),
116 reload_(NULL),
117 home_(NULL),
118 location_bar_(NULL),
119 browser_actions_(NULL),
120 app_menu_(NULL),
121 browser_(browser),
122 badge_controller_(browser->profile(), this) {
123 set_id(VIEW_ID_TOOLBAR);
125 SetEventTargeter(
126 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
128 chrome::AddCommandObserver(browser_, IDC_BACK, this);
129 chrome::AddCommandObserver(browser_, IDC_FORWARD, this);
130 chrome::AddCommandObserver(browser_, IDC_RELOAD, this);
131 chrome::AddCommandObserver(browser_, IDC_HOME, this);
132 chrome::AddCommandObserver(browser_, IDC_LOAD_NEW_TAB_PAGE, this);
134 display_mode_ = DISPLAYMODE_LOCATION;
135 if (browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP))
136 display_mode_ = DISPLAYMODE_NORMAL;
138 if (OutdatedUpgradeBubbleView::IsAvailable()) {
139 registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL,
140 content::NotificationService::AllSources());
141 registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU,
142 content::NotificationService::AllSources());
144 #if defined(OS_WIN)
145 registrar_.Add(this, chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED,
146 content::NotificationService::AllSources());
147 #endif
150 ToolbarView::~ToolbarView() {
151 // NOTE: Don't remove the command observers here. This object gets destroyed
152 // after the Browser (which owns the CommandUpdater), so the CommandUpdater is
153 // already gone.
156 void ToolbarView::Init() {
157 GetWidget()->AddObserver(this);
159 back_ = new BackButton(this, new BackForwardMenuModel(
160 browser_, BackForwardMenuModel::BACKWARD_MENU));
161 back_->set_triggerable_event_flags(
162 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
163 back_->set_tag(IDC_BACK);
164 back_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK));
165 back_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
166 back_->set_id(VIEW_ID_BACK_BUTTON);
167 back_->Init();
169 forward_ = new ToolbarButton(this, new BackForwardMenuModel(
170 browser_, BackForwardMenuModel::FORWARD_MENU));
171 forward_->set_triggerable_event_flags(
172 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
173 forward_->set_tag(IDC_FORWARD);
174 forward_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD));
175 forward_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD));
176 forward_->set_id(VIEW_ID_FORWARD_BUTTON);
177 forward_->Init();
179 location_bar_ = new LocationBarView(
180 browser_, browser_->profile(),
181 browser_->command_controller()->command_updater(), this,
182 display_mode_ == DISPLAYMODE_LOCATION);
184 reload_ = new ReloadButton(browser_->command_controller()->command_updater());
185 reload_->set_triggerable_event_flags(
186 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
187 reload_->set_tag(IDC_RELOAD);
188 reload_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD));
189 reload_->set_id(VIEW_ID_RELOAD_BUTTON);
190 reload_->Init();
192 home_ = new HomeButton(this, browser_);
193 home_->set_triggerable_event_flags(
194 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
195 home_->set_tag(IDC_HOME);
196 home_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_HOME));
197 home_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_HOME));
198 home_->set_id(VIEW_ID_HOME_BUTTON);
199 home_->Init();
201 browser_actions_ = new BrowserActionsContainer(
202 browser_,
203 NULL); // No master container for this one (it is master).
205 app_menu_ = new WrenchToolbarButton(this);
206 app_menu_->EnableCanvasFlippingForRTLUI(true);
207 app_menu_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_APP));
208 app_menu_->SetTooltipText(l10n_util::GetStringUTF16(IDS_APPMENU_TOOLTIP));
209 app_menu_->set_id(VIEW_ID_APP_MENU);
211 // Always add children in order from left to right, for accessibility.
212 AddChildView(back_);
213 AddChildView(forward_);
214 AddChildView(reload_);
215 AddChildView(home_);
216 AddChildView(location_bar_);
217 AddChildView(browser_actions_);
218 AddChildView(app_menu_);
220 LoadImages();
222 // Start global error services now so we badge the menu correctly.
223 #if !defined(OS_CHROMEOS)
224 if (!HasAshShell()) {
225 SigninGlobalErrorFactory::GetForProfile(browser_->profile());
226 #if !defined(OS_ANDROID)
227 SyncGlobalErrorFactory::GetForProfile(browser_->profile());
228 #endif
231 #if defined(OS_WIN)
232 RecoveryInstallGlobalErrorFactory::GetForProfile(browser_->profile());
233 #endif
234 #endif // OS_CHROMEOS
236 // Add any necessary badges to the menu item based on the system state.
237 // Do this after |app_menu_| has been added as a bubble may be shown that
238 // needs the widget (widget found by way of app_menu_->GetWidget()).
239 badge_controller_.UpdateDelegate();
241 location_bar_->Init();
243 show_home_button_.Init(prefs::kShowHomeButton,
244 browser_->profile()->GetPrefs(),
245 base::Bind(&ToolbarView::OnShowHomeButtonChanged,
246 base::Unretained(this)));
248 browser_actions_->Init();
250 // Accessibility specific tooltip text.
251 if (content::BrowserAccessibilityState::GetInstance()->
252 IsAccessibleBrowser()) {
253 back_->SetTooltipText(
254 l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK));
255 forward_->SetTooltipText(
256 l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD));
260 void ToolbarView::OnWidgetActivationChanged(views::Widget* widget,
261 bool active) {
262 extensions::ExtensionCommandsGlobalRegistry* registry =
263 extensions::ExtensionCommandsGlobalRegistry::Get(browser_->profile());
264 if (active) {
265 registry->set_registry_for_active_window(
266 browser_actions_->extension_keybinding_registry());
267 } else if (registry->registry_for_active_window() ==
268 browser_actions_->extension_keybinding_registry()) {
269 registry->set_registry_for_active_window(nullptr);
273 void ToolbarView::Update(WebContents* tab) {
274 if (location_bar_)
275 location_bar_->Update(tab);
276 if (browser_actions_)
277 browser_actions_->RefreshToolbarActionViews();
278 if (reload_)
279 reload_->set_menu_enabled(chrome::IsDebuggerAttachedToCurrentTab(browser_));
282 void ToolbarView::ResetTabState(WebContents* tab) {
283 if (location_bar_)
284 location_bar_->ResetTabState(tab);
287 void ToolbarView::SetPaneFocusAndFocusAppMenu() {
288 SetPaneFocus(app_menu_);
291 bool ToolbarView::IsAppMenuFocused() {
292 return app_menu_->HasFocus();
295 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
296 menu_listeners_.AddObserver(listener);
299 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
300 menu_listeners_.RemoveObserver(listener);
303 views::View* ToolbarView::GetBookmarkBubbleAnchor() {
304 views::View* star_view = location_bar()->star_view();
305 return (star_view && star_view->visible()) ? star_view : app_menu_;
308 views::View* ToolbarView::GetTranslateBubbleAnchor() {
309 views::View* translate_icon_view = location_bar()->translate_icon_view();
310 return (translate_icon_view && translate_icon_view->visible()) ?
311 translate_icon_view : app_menu_;
314 void ToolbarView::ExecuteExtensionCommand(
315 const extensions::Extension* extension,
316 const extensions::Command& command) {
317 browser_actions_->ExecuteExtensionCommand(extension, command);
320 void ToolbarView::ShowAppMenu(bool for_drop) {
321 if (wrench_menu_.get() && wrench_menu_->IsShowing())
322 return;
324 #if defined(USE_AURA)
325 if (keyboard::KeyboardController::GetInstance() &&
326 keyboard::KeyboardController::GetInstance()->keyboard_visible()) {
327 keyboard::KeyboardController::GetInstance()->HideKeyboard(
328 keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
330 #endif
332 wrench_menu_.reset(
333 new WrenchMenu(browser_, for_drop ? WrenchMenu::FOR_DROP : 0));
334 wrench_menu_model_.reset(new WrenchMenuModel(this, browser_));
335 wrench_menu_->Init(wrench_menu_model_.get());
337 FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened());
339 wrench_menu_->RunMenu(app_menu_);
342 void ToolbarView::CloseAppMenu() {
343 if (wrench_menu_)
344 wrench_menu_->CloseMenu();
347 ////////////////////////////////////////////////////////////////////////////////
348 // ToolbarView, AccessiblePaneView overrides:
350 bool ToolbarView::SetPaneFocus(views::View* initial_focus) {
351 if (!AccessiblePaneView::SetPaneFocus(initial_focus))
352 return false;
354 location_bar_->SetShowFocusRect(true);
355 return true;
358 void ToolbarView::GetAccessibleState(ui::AXViewState* state) {
359 state->role = ui::AX_ROLE_TOOLBAR;
360 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR);
363 ////////////////////////////////////////////////////////////////////////////////
364 // ToolbarView, Menu::Delegate overrides:
366 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
367 return GetWidget()->GetAccelerator(id, accel);
370 ////////////////////////////////////////////////////////////////////////////////
371 // ToolbarView, views::MenuButtonListener implementation:
373 void ToolbarView::OnMenuButtonClicked(views::View* source,
374 const gfx::Point& point) {
375 TRACE_EVENT0("views", "ToolbarView::OnMenuButtonClicked");
376 DCHECK_EQ(VIEW_ID_APP_MENU, source->id());
377 ShowAppMenu(false); // Not for drop.
380 ////////////////////////////////////////////////////////////////////////////////
381 // ToolbarView, LocationBarView::Delegate implementation:
383 WebContents* ToolbarView::GetWebContents() {
384 return browser_->tab_strip_model()->GetActiveWebContents();
387 ToolbarModel* ToolbarView::GetToolbarModel() {
388 return browser_->toolbar_model();
391 const ToolbarModel* ToolbarView::GetToolbarModel() const {
392 return browser_->toolbar_model();
395 ContentSettingBubbleModelDelegate*
396 ToolbarView::GetContentSettingBubbleModelDelegate() {
397 return browser_->content_setting_bubble_model_delegate();
400 void ToolbarView::ShowWebsiteSettings(content::WebContents* web_contents,
401 const GURL& url,
402 const content::SSLStatus& ssl) {
403 chrome::ShowWebsiteSettings(browser_, web_contents, url, ssl);
406 views::Widget* ToolbarView::CreateViewsBubble(
407 views::BubbleDelegateView* bubble_delegate) {
408 return views::BubbleDelegateView::CreateBubble(bubble_delegate);
411 PageActionImageView* ToolbarView::CreatePageActionImageView(
412 LocationBarView* owner, ExtensionAction* action) {
413 return new PageActionImageView(owner, action, browser_);
416 ////////////////////////////////////////////////////////////////////////////////
417 // ToolbarView, CommandObserver implementation:
419 void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
420 views::Button* button = NULL;
421 switch (id) {
422 case IDC_BACK:
423 button = back_;
424 break;
425 case IDC_FORWARD:
426 button = forward_;
427 break;
428 case IDC_RELOAD:
429 button = reload_;
430 break;
431 case IDC_HOME:
432 button = home_;
433 break;
435 if (button)
436 button->SetEnabled(enabled);
439 ////////////////////////////////////////////////////////////////////////////////
440 // ToolbarView, views::Button::ButtonListener implementation:
442 void ToolbarView::ButtonPressed(views::Button* sender,
443 const ui::Event& event) {
444 chrome::ExecuteCommandWithDisposition(
445 browser_, sender->tag(), ui::DispositionFromEventFlags(event.flags()));
448 ////////////////////////////////////////////////////////////////////////////////
449 // ToolbarView, content::NotificationObserver implementation:
451 void ToolbarView::Observe(int type,
452 const content::NotificationSource& source,
453 const content::NotificationDetails& details) {
454 switch (type) {
455 case chrome::NOTIFICATION_OUTDATED_INSTALL:
456 ShowOutdatedInstallNotification(true);
457 break;
458 case chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU:
459 ShowOutdatedInstallNotification(false);
460 break;
461 #if defined(OS_WIN)
462 case chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED:
463 ShowCriticalNotification();
464 break;
465 #endif
466 default:
467 NOTREACHED();
471 ////////////////////////////////////////////////////////////////////////////////
472 // ToolbarView, ui::AcceleratorProvider implementation:
474 bool ToolbarView::GetAcceleratorForCommandId(int command_id,
475 ui::Accelerator* accelerator) {
476 return GetWidget()->GetAccelerator(command_id, accelerator);
479 ////////////////////////////////////////////////////////////////////////////////
480 // ToolbarView, views::View overrides:
482 gfx::Size ToolbarView::GetPreferredSize() const {
483 gfx::Size size(location_bar_->GetPreferredSize());
484 ui::ThemeProvider* theme_provider = GetThemeProvider();
485 if (is_display_mode_normal()) {
486 const int element_padding = theme_provider->GetDisplayProperty(
487 ThemeProperties::PROPERTY_TOOLBAR_VIEW_ELEMENT_PADDING);
488 const int browser_actions_width =
489 browser_actions_->GetPreferredSize().width();
490 const int content_width =
491 theme_provider->GetDisplayProperty(
492 ThemeProperties::PROPERTY_TOOLBAR_VIEW_LEFT_EDGE_SPACING) +
493 back_->GetPreferredSize().width() + element_padding +
494 forward_->GetPreferredSize().width() + element_padding +
495 reload_->GetPreferredSize().width() +
496 (show_home_button_.GetValue()
497 ? element_padding + home_->GetPreferredSize().width()
498 : 0) +
499 theme_provider->GetDisplayProperty(
500 ThemeProperties::PROPERTY_TOOLBAR_VIEW_STANDARD_SPACING) +
501 theme_provider->GetDisplayProperty(
502 ThemeProperties::PROPERTY_TOOLBAR_VIEW_LOCATION_BAR_RIGHT_PADDING) +
503 browser_actions_width +
504 (browser_actions_width > 0 ? element_padding : 0) +
505 app_menu_->GetPreferredSize().width() +
506 theme_provider->GetDisplayProperty(
507 ThemeProperties::PROPERTY_TOOLBAR_VIEW_RIGHT_EDGE_SPACING);
508 size.Enlarge(content_width, 0);
510 return SizeForContentSize(size);
513 gfx::Size ToolbarView::GetMinimumSize() const {
514 gfx::Size size(location_bar_->GetMinimumSize());
515 ui::ThemeProvider* theme_provider = GetThemeProvider();
516 if (is_display_mode_normal()) {
517 const int element_padding = theme_provider->GetDisplayProperty(
518 ThemeProperties::PROPERTY_TOOLBAR_VIEW_ELEMENT_PADDING);
519 const int browser_actions_width =
520 browser_actions_->GetMinimumSize().width();
521 const int content_width =
522 theme_provider->GetDisplayProperty(
523 ThemeProperties::PROPERTY_TOOLBAR_VIEW_LEFT_EDGE_SPACING) +
524 back_->GetMinimumSize().width() + element_padding +
525 forward_->GetMinimumSize().width() + element_padding +
526 reload_->GetMinimumSize().width() +
527 (show_home_button_.GetValue()
528 ? element_padding + home_->GetMinimumSize().width()
529 : 0) +
530 theme_provider->GetDisplayProperty(
531 ThemeProperties::PROPERTY_TOOLBAR_VIEW_STANDARD_SPACING) +
532 theme_provider->GetDisplayProperty(
533 ThemeProperties::PROPERTY_TOOLBAR_VIEW_LOCATION_BAR_RIGHT_PADDING) +
534 browser_actions_width +
535 (browser_actions_width > 0 ? element_padding : 0) +
536 app_menu_->GetMinimumSize().width() +
537 theme_provider->GetDisplayProperty(
538 ThemeProperties::PROPERTY_TOOLBAR_VIEW_RIGHT_EDGE_SPACING);
539 size.Enlarge(content_width, 0);
541 return SizeForContentSize(size);
544 void ToolbarView::Layout() {
545 // If we have not been initialized yet just do nothing.
546 if (back_ == NULL)
547 return;
549 if (!is_display_mode_normal()) {
550 location_bar_->SetBounds(0, PopupTopSpacing(), width(),
551 location_bar_->GetPreferredSize().height());
552 return;
555 // We assume all child elements except the location bar are the same height.
556 // Set child_y such that buttons appear vertically centered. We put any excess
557 // padding above the buttons.
558 int child_height =
559 std::min(back_->GetPreferredSize().height(), height());
560 int child_y = (height() - child_height + 1) / 2;
562 // If the window is maximized, we extend the back button to the left so that
563 // clicking on the left-most pixel will activate the back button.
564 // TODO(abarth): If the window becomes maximized but is not resized,
565 // then Layout() might not be called and the back button
566 // will be slightly the wrong size. We should force a
567 // Layout() in this case.
568 // http://crbug.com/5540
569 bool maximized = browser_->window() && browser_->window()->IsMaximized();
570 int back_width = back_->GetPreferredSize().width();
571 ui::ThemeProvider* theme_provider = GetThemeProvider();
572 const int left_edge_spacing = theme_provider->GetDisplayProperty(
573 ThemeProperties::PROPERTY_TOOLBAR_VIEW_LEFT_EDGE_SPACING);
574 if (maximized) {
575 back_->SetBounds(0, child_y, back_width + left_edge_spacing, child_height);
576 back_->SetLeadingMargin(left_edge_spacing);
577 } else {
578 back_->SetBounds(left_edge_spacing, child_y, back_width, child_height);
579 back_->SetLeadingMargin(0);
581 const int element_padding = theme_provider->GetDisplayProperty(
582 ThemeProperties::PROPERTY_TOOLBAR_VIEW_ELEMENT_PADDING);
583 int next_element_x = back_->bounds().right() + element_padding;
585 forward_->SetBounds(next_element_x, child_y,
586 forward_->GetPreferredSize().width(), child_height);
587 next_element_x = forward_->bounds().right() + element_padding;
589 reload_->SetBounds(next_element_x, child_y,
590 reload_->GetPreferredSize().width(), child_height);
591 next_element_x = reload_->bounds().right();
593 if (show_home_button_.GetValue() ||
594 (browser_->is_app() && extensions::util::IsNewBookmarkAppsEnabled())) {
595 next_element_x += element_padding;
596 home_->SetVisible(true);
597 home_->SetBounds(next_element_x, child_y,
598 home_->GetPreferredSize().width(), child_height);
599 } else {
600 home_->SetVisible(false);
601 home_->SetBounds(next_element_x, child_y, 0, child_height);
603 next_element_x = home_->bounds().right() +
604 theme_provider->GetDisplayProperty(
605 ThemeProperties::PROPERTY_TOOLBAR_VIEW_STANDARD_SPACING);
607 int browser_actions_width = browser_actions_->GetPreferredSize().width();
608 int app_menu_width = app_menu_->GetPreferredSize().width();
609 const int right_edge_spacing = theme_provider->GetDisplayProperty(
610 ThemeProperties::PROPERTY_TOOLBAR_VIEW_RIGHT_EDGE_SPACING);
611 const int location_bar_right_padding = theme_provider->GetDisplayProperty(
612 ThemeProperties::PROPERTY_TOOLBAR_VIEW_LOCATION_BAR_RIGHT_PADDING);
613 int available_width = std::max(
614 0, width() - right_edge_spacing - app_menu_width - browser_actions_width -
615 (browser_actions_width > 0 ? element_padding : 0) -
616 location_bar_right_padding - next_element_x);
618 int location_height = location_bar_->GetPreferredSize().height();
619 int location_y = (height() - location_height + 1) / 2;
620 location_bar_->SetBounds(next_element_x, location_y,
621 std::max(available_width, 0), location_height);
622 next_element_x = location_bar_->bounds().right() + location_bar_right_padding;
624 browser_actions_->SetBounds(
625 next_element_x, child_y, browser_actions_width, child_height);
626 next_element_x = browser_actions_->bounds().right();
627 if (browser_actions_width > 0)
628 next_element_x += element_padding;
630 // The browser actions need to do a layout explicitly, because when an
631 // extension is loaded/unloaded/changed, BrowserActionContainer removes and
632 // re-adds everything, regardless of whether it has a page action. For a
633 // page action, browser action bounds do not change, as a result of which
634 // SetBounds does not do a layout at all.
635 // TODO(sidchat): Rework the above behavior so that explicit layout is not
636 // required.
637 browser_actions_->Layout();
639 // Extend the app menu to the screen's right edge in maximized mode just like
640 // we extend the back button to the left edge.
641 if (maximized)
642 app_menu_width += right_edge_spacing;
643 app_menu_->SetBounds(next_element_x, child_y, app_menu_width, child_height);
646 void ToolbarView::OnPaint(gfx::Canvas* canvas) {
647 View::OnPaint(canvas);
649 if (is_display_mode_normal())
650 return;
652 // For glass, we need to draw a black line below the location bar to separate
653 // it from the content area. For non-glass, the NonClientView draws the
654 // toolbar background below the location bar for us.
655 // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()!
656 if (GetWidget()->ShouldWindowContentsBeTransparent())
657 canvas->FillRect(gfx::Rect(0, height() - 1, width(), 1), SK_ColorBLACK);
660 void ToolbarView::OnThemeChanged() {
661 LoadImages();
664 const char* ToolbarView::GetClassName() const {
665 return kViewClassName;
668 bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) {
669 const views::View* focused_view = focus_manager()->GetFocusedView();
670 if (focused_view && (focused_view->id() == VIEW_ID_OMNIBOX))
671 return false; // Let the omnibox handle all accelerator events.
672 return AccessiblePaneView::AcceleratorPressed(accelerator);
675 bool ToolbarView::IsWrenchMenuShowing() const {
676 return wrench_menu_.get() && wrench_menu_->IsShowing();
679 bool ToolbarView::ShouldPaintBackground() const {
680 return display_mode_ == DISPLAYMODE_NORMAL;
683 ////////////////////////////////////////////////////////////////////////////////
684 // ToolbarView, protected:
686 // Override this so that when the user presses F6 to rotate toolbar panes,
687 // the location bar gets focus, not the first control in the toolbar - and
688 // also so that it selects all content in the location bar.
689 bool ToolbarView::SetPaneFocusAndFocusDefault() {
690 if (!location_bar_->HasFocus()) {
691 SetPaneFocus(location_bar_);
692 location_bar_->FocusLocation(true);
693 return true;
696 if (!AccessiblePaneView::SetPaneFocusAndFocusDefault())
697 return false;
698 browser_->window()->RotatePaneFocus(true);
699 return true;
702 void ToolbarView::RemovePaneFocus() {
703 AccessiblePaneView::RemovePaneFocus();
704 location_bar_->SetShowFocusRect(false);
707 ////////////////////////////////////////////////////////////////////////////////
708 // ToolbarView, private:
710 // views::ViewTargeterDelegate:
711 bool ToolbarView::DoesIntersectRect(const views::View* target,
712 const gfx::Rect& rect) const {
713 CHECK_EQ(target, this);
715 // Fall through to the tab strip above us if none of |rect| intersects
716 // with this view (intersection with the top shadow edge does not
717 // count as intersection with this view).
718 if (rect.bottom() < content_shadow_height())
719 return false;
720 // Otherwise let our superclass take care of it.
721 return ViewTargeterDelegate::DoesIntersectRect(this, rect);
724 void ToolbarView::UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,
725 WrenchIconPainter::Severity severity,
726 bool animate) {
727 // Showing the bubble requires |app_menu_| to be in a widget. See comment
728 // in ConflictingModuleView for details.
729 DCHECK(app_menu_->GetWidget());
731 base::string16 accname_app = l10n_util::GetStringUTF16(IDS_ACCNAME_APP);
732 if (type == WrenchMenuBadgeController::BADGE_TYPE_UPGRADE_NOTIFICATION) {
733 accname_app = l10n_util::GetStringFUTF16(
734 IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app);
736 app_menu_->SetAccessibleName(accname_app);
737 app_menu_->SetSeverity(severity, animate);
739 // Keep track of whether we were showing the badge before, so we don't send
740 // multiple UMA events for example when multiple Chrome windows are open.
741 static bool incompatibility_badge_showing = false;
742 // Save the old value before resetting it.
743 bool was_showing = incompatibility_badge_showing;
744 incompatibility_badge_showing = false;
746 if (type == WrenchMenuBadgeController::BADGE_TYPE_INCOMPATIBILITY_WARNING) {
747 if (!was_showing) {
748 content::RecordAction(UserMetricsAction("ConflictBadge"));
749 #if defined(OS_WIN)
750 ConflictingModuleView::MaybeShow(browser_, app_menu_);
751 #endif
753 incompatibility_badge_showing = true;
754 return;
758 int ToolbarView::PopupTopSpacing() const {
759 const int kPopupTopSpacingNonGlass = 3;
760 return GetWidget()->ShouldWindowContentsBeTransparent() ?
761 0 : kPopupTopSpacingNonGlass;
764 gfx::Size ToolbarView::SizeForContentSize(gfx::Size size) const {
765 if (is_display_mode_normal()) {
766 // For Material Design the size of ToolbarView is encoded as a constant in
767 // ThemeProperties. For non-material the size is based on the provided
768 // assets.
769 if (ui::MaterialDesignController::IsModeMaterial()) {
770 int content_height = std::max(back_->GetPreferredSize().height(),
771 location_bar_->GetPreferredSize().height());
772 int padding = GetThemeProvider()->GetDisplayProperty(
773 ThemeProperties::PROPERTY_TOOLBAR_VIEW_VERTICAL_PADDING);
774 size.SetToMax(gfx::Size(0, content_height + (padding * 2)));
775 } else {
776 gfx::ImageSkia* normal_background =
777 GetThemeProvider()->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER);
778 size.SetToMax(
779 gfx::Size(0, normal_background->height() - content_shadow_height()));
781 } else if (size.height() == 0) {
782 // Location mode with a 0 height location bar. If on ash, expand by one
783 // pixel to show a border in the title bar, otherwise leave the size as zero
784 // height.
785 const int kAshBorderSpacing = 1;
786 if (browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH)
787 size.Enlarge(0, kAshBorderSpacing);
788 } else {
789 const int kPopupBottomSpacingGlass = 1;
790 const int kPopupBottomSpacingNonGlass = 2;
791 size.Enlarge(
793 PopupTopSpacing() + (GetWidget()->ShouldWindowContentsBeTransparent() ?
794 kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass));
796 return size;
799 void ToolbarView::LoadImages() {
800 ui::ThemeProvider* tp = GetThemeProvider();
802 back_->SetImage(views::Button::STATE_NORMAL,
803 *(tp->GetImageSkiaNamed(IDR_BACK)));
804 back_->SetImage(views::Button::STATE_DISABLED,
805 *(tp->GetImageSkiaNamed(IDR_BACK_D)));
807 forward_->SetImage(views::Button::STATE_NORMAL,
808 *(tp->GetImageSkiaNamed(IDR_FORWARD)));
809 forward_->SetImage(views::Button::STATE_DISABLED,
810 *(tp->GetImageSkiaNamed(IDR_FORWARD_D)));
812 reload_->LoadImages();
814 home_->SetImage(views::Button::STATE_NORMAL,
815 *(tp->GetImageSkiaNamed(IDR_HOME)));
817 if (ui::MaterialDesignController::IsModeMaterial()) {
818 app_menu_->SetImage(views::Button::STATE_NORMAL,
819 *(tp->GetImageSkiaNamed(IDR_TOOLS)));
823 void ToolbarView::ShowCriticalNotification() {
824 #if defined(OS_WIN)
825 CriticalNotificationBubbleView* bubble_delegate =
826 new CriticalNotificationBubbleView(app_menu_);
827 views::BubbleDelegateView::CreateBubble(bubble_delegate)->Show();
828 #endif
831 void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) {
832 if (OutdatedUpgradeBubbleView::IsAvailable()) {
833 OutdatedUpgradeBubbleView::ShowBubble(
834 app_menu_, browser_, auto_update_enabled);
838 void ToolbarView::OnShowHomeButtonChanged() {
839 Layout();
840 SchedulePaint();
843 int ToolbarView::content_shadow_height() const {
844 ui::ThemeProvider* theme_provider = GetThemeProvider();
845 return theme_provider->GetDisplayProperty(
846 browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH
847 ? ThemeProperties::PROPERTY_TOOLBAR_VIEW_CONTENT_SHADOW_HEIGHT_ASH
848 : ThemeProperties::PROPERTY_TOOLBAR_VIEW_CONTENT_SHADOW_HEIGHT);