Extension Toolbar redesign, part 1 (overflow)
[chromium-blink-merge.git] / chrome / browser / ui / views / toolbar / toolbar_view.cc
blob107005e2e3be0b2fceece486f29051e315389065
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 "base/command_line.h"
8 #include "base/debug/trace_event.h"
9 #include "base/i18n/number_formatting.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/app/chrome_command_ids.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/command_updater.h"
15 #include "chrome/browser/extensions/extension_action.h"
16 #include "chrome/browser/extensions/extension_action_manager.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/themes/theme_service.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_command_controller.h"
21 #include "chrome/browser/ui/browser_commands.h"
22 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
23 #include "chrome/browser/ui/browser_instant_controller.h"
24 #include "chrome/browser/ui/browser_tabstrip.h"
25 #include "chrome/browser/ui/browser_window.h"
26 #include "chrome/browser/ui/global_error/global_error_service.h"
27 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
28 #include "chrome/browser/ui/omnibox/omnibox_view.h"
29 #include "chrome/browser/ui/tabs/tab_strip_model.h"
30 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
31 #include "chrome/browser/ui/view_ids.h"
32 #include "chrome/browser/ui/views/extensions/extension_message_bubble_view.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/browser/upgrade_detector.h"
48 #include "chrome/common/chrome_switches.h"
49 #include "chrome/common/pref_names.h"
50 #include "content/public/browser/browser_accessibility_state.h"
51 #include "content/public/browser/notification_service.h"
52 #include "content/public/browser/render_view_host.h"
53 #include "content/public/browser/user_metrics.h"
54 #include "content/public/browser/web_contents.h"
55 #include "grit/chromium_strings.h"
56 #include "grit/generated_resources.h"
57 #include "grit/theme_resources.h"
58 #include "ui/accessibility/ax_view_state.h"
59 #include "ui/aura/window.h"
60 #include "ui/base/l10n/l10n_util.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/widget/tooltip_manager.h"
71 #include "ui/views/widget/widget.h"
72 #include "ui/views/window/non_client_view.h"
74 #if defined(OS_WIN)
75 #include "base/win/windows_version.h"
76 #include "chrome/browser/enumerate_modules_model_win.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 // The edge graphics have some built-in spacing/shadowing, so we have to adjust
96 // our spacing to make it match.
97 const int kLeftEdgeSpacing = 3;
98 const int kRightEdgeSpacing = 2;
100 // Ash doesn't use a rounded content area and its top edge has an extra shadow.
101 const int kContentShadowHeightAsh = 2;
103 // Non-ash uses a rounded content area with no shadow in the assets.
104 const int kContentShadowHeight = 0;
106 bool IsStreamlinedHostedAppsEnabled() {
107 return CommandLine::ForCurrentProcess()->HasSwitch(
108 switches::kEnableStreamlinedHostedApps);
111 #if !defined(OS_CHROMEOS)
112 bool HasAshShell() {
113 #if defined(USE_ASH)
114 return ash::Shell::HasInstance();
115 #else
116 return false;
117 #endif // USE_ASH
119 #endif // OS_CHROMEOS
121 } // namespace
123 // static
124 const char ToolbarView::kViewClassName[] = "ToolbarView";
126 ////////////////////////////////////////////////////////////////////////////////
127 // ToolbarView, public:
129 ToolbarView::ToolbarView(Browser* browser)
130 : back_(NULL),
131 forward_(NULL),
132 reload_(NULL),
133 home_(NULL),
134 location_bar_(NULL),
135 browser_actions_(NULL),
136 app_menu_(NULL),
137 browser_(browser),
138 extension_message_bubble_factory_(
139 new extensions::ExtensionMessageBubbleFactory(browser->profile(),
140 this)) {
141 set_id(VIEW_ID_TOOLBAR);
143 chrome::AddCommandObserver(browser_, IDC_BACK, this);
144 chrome::AddCommandObserver(browser_, IDC_FORWARD, this);
145 chrome::AddCommandObserver(browser_, IDC_RELOAD, this);
146 chrome::AddCommandObserver(browser_, IDC_HOME, this);
147 chrome::AddCommandObserver(browser_, IDC_LOAD_NEW_TAB_PAGE, this);
149 display_mode_ = DISPLAYMODE_LOCATION;
150 if (browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ||
151 (browser->is_app() && IsStreamlinedHostedAppsEnabled()))
152 display_mode_ = DISPLAYMODE_NORMAL;
154 registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
155 content::NotificationService::AllSources());
156 if (OutdatedUpgradeBubbleView::IsAvailable()) {
157 registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL,
158 content::NotificationService::AllSources());
159 registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU,
160 content::NotificationService::AllSources());
162 #if defined(OS_WIN)
163 registrar_.Add(this, chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED,
164 content::NotificationService::AllSources());
165 if (base::win::GetVersion() == base::win::VERSION_XP) {
166 registrar_.Add(this, chrome::NOTIFICATION_MODULE_LIST_ENUMERATED,
167 content::NotificationService::AllSources());
169 #endif
170 registrar_.Add(this,
171 chrome::NOTIFICATION_MODULE_INCOMPATIBILITY_BADGE_CHANGE,
172 content::NotificationService::AllSources());
173 registrar_.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
174 content::Source<Profile>(browser_->profile()));
177 ToolbarView::~ToolbarView() {
178 // NOTE: Don't remove the command observers here. This object gets destroyed
179 // after the Browser (which owns the CommandUpdater), so the CommandUpdater is
180 // already gone.
183 void ToolbarView::Init() {
184 GetWidget()->AddObserver(this);
186 back_ = new BackButton(this, new BackForwardMenuModel(
187 browser_, BackForwardMenuModel::BACKWARD_MENU));
188 back_->set_triggerable_event_flags(
189 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
190 back_->set_tag(IDC_BACK);
191 back_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK));
192 back_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
193 back_->set_id(VIEW_ID_BACK_BUTTON);
194 back_->Init();
196 forward_ = new ToolbarButton(this, new BackForwardMenuModel(
197 browser_, BackForwardMenuModel::FORWARD_MENU));
198 forward_->set_triggerable_event_flags(
199 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
200 forward_->set_tag(IDC_FORWARD);
201 forward_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD));
202 forward_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD));
203 forward_->set_id(VIEW_ID_FORWARD_BUTTON);
204 forward_->Init();
206 location_bar_ = new LocationBarView(
207 browser_, browser_->profile(),
208 browser_->command_controller()->command_updater(), this,
209 display_mode_ == DISPLAYMODE_LOCATION);
211 reload_ = new ReloadButton(browser_->command_controller()->command_updater());
212 reload_->set_triggerable_event_flags(
213 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
214 reload_->set_tag(IDC_RELOAD);
215 reload_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD));
216 reload_->set_id(VIEW_ID_RELOAD_BUTTON);
217 reload_->Init();
219 home_ = new HomeButton(this, browser_);
220 home_->set_triggerable_event_flags(
221 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
222 home_->set_tag(IDC_HOME);
223 home_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_HOME));
224 home_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_HOME));
225 home_->set_id(VIEW_ID_HOME_BUTTON);
226 home_->Init();
228 browser_actions_ = new BrowserActionsContainer(
229 browser_,
230 this, // Owner.
231 NULL); // No master container for this one (it is master).
233 app_menu_ = new WrenchToolbarButton(this);
234 app_menu_->EnableCanvasFlippingForRTLUI(true);
235 app_menu_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_APP));
236 app_menu_->SetTooltipText(l10n_util::GetStringUTF16(IDS_APPMENU_TOOLTIP));
237 app_menu_->set_id(VIEW_ID_APP_MENU);
239 // Always add children in order from left to right, for accessibility.
240 AddChildView(back_);
241 AddChildView(forward_);
242 AddChildView(reload_);
243 AddChildView(home_);
244 AddChildView(location_bar_);
245 AddChildView(browser_actions_);
246 AddChildView(app_menu_);
248 LoadImages();
250 // Start global error services now so we badge the menu correctly in non-Ash.
251 #if !defined(OS_CHROMEOS)
252 if (!HasAshShell()) {
253 SigninGlobalErrorFactory::GetForProfile(browser_->profile());
254 #if !defined(OS_ANDROID)
255 SyncGlobalErrorFactory::GetForProfile(browser_->profile());
256 #endif
258 #endif // OS_CHROMEOS
260 // Add any necessary badges to the menu item based on the system state.
261 // Do this after |app_menu_| has been added as a bubble may be shown that
262 // needs the widget (widget found by way of app_menu_->GetWidget()).
263 UpdateAppMenuState();
265 location_bar_->Init();
267 show_home_button_.Init(prefs::kShowHomeButton,
268 browser_->profile()->GetPrefs(),
269 base::Bind(&ToolbarView::OnShowHomeButtonChanged,
270 base::Unretained(this)));
272 browser_actions_->Init();
274 // Accessibility specific tooltip text.
275 if (content::BrowserAccessibilityState::GetInstance()->
276 IsAccessibleBrowser()) {
277 back_->SetTooltipText(
278 l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK));
279 forward_->SetTooltipText(
280 l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD));
284 void ToolbarView::OnWidgetVisibilityChanged(views::Widget* widget,
285 bool visible) {
286 // Safe to call multiple times; the bubble will only appear once.
287 if (visible)
288 extension_message_bubble_factory_->MaybeShow(app_menu_);
291 void ToolbarView::Update(WebContents* tab) {
292 if (location_bar_)
293 location_bar_->Update(tab);
294 if (browser_actions_)
295 browser_actions_->RefreshBrowserActionViews();
296 if (reload_)
297 reload_->set_menu_enabled(chrome::IsDebuggerAttachedToCurrentTab(browser_));
300 void ToolbarView::SetPaneFocusAndFocusAppMenu() {
301 SetPaneFocus(app_menu_);
304 bool ToolbarView::IsAppMenuFocused() {
305 return app_menu_->HasFocus();
308 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
309 menu_listeners_.AddObserver(listener);
312 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
313 menu_listeners_.RemoveObserver(listener);
316 views::View* ToolbarView::GetBookmarkBubbleAnchor() {
317 views::View* star_view = location_bar()->star_view();
318 return (star_view && star_view->visible()) ? star_view : app_menu_;
321 views::View* ToolbarView::GetTranslateBubbleAnchor() {
322 views::View* translate_icon_view = location_bar()->translate_icon_view();
323 return (translate_icon_view && translate_icon_view->visible()) ?
324 translate_icon_view : app_menu_;
327 void ToolbarView::ExecuteExtensionCommand(
328 const extensions::Extension* extension,
329 const extensions::Command& command) {
330 browser_actions_->ExecuteExtensionCommand(extension, command);
333 void ToolbarView::ShowPageActionPopup(const extensions::Extension* extension) {
334 extensions::ExtensionActionManager* extension_manager =
335 extensions::ExtensionActionManager::Get(browser_->profile());
336 ExtensionAction* extension_action =
337 extension_manager->GetPageAction(*extension);
338 if (extension_action) {
339 location_bar_->GetPageActionView(extension_action)->image_view()->
340 ExecuteAction(ExtensionPopup::SHOW);
344 void ToolbarView::ShowBrowserActionPopup(
345 const extensions::Extension* extension) {
346 browser_actions_->ShowPopup(extension, true);
349 views::MenuButton* ToolbarView::app_menu() const {
350 return app_menu_;
353 ////////////////////////////////////////////////////////////////////////////////
354 // ToolbarView, AccessiblePaneView overrides:
356 bool ToolbarView::SetPaneFocus(views::View* initial_focus) {
357 if (!AccessiblePaneView::SetPaneFocus(initial_focus))
358 return false;
360 location_bar_->SetShowFocusRect(true);
361 return true;
364 void ToolbarView::GetAccessibleState(ui::AXViewState* state) {
365 state->role = ui::AX_ROLE_TOOLBAR;
366 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR);
369 ////////////////////////////////////////////////////////////////////////////////
370 // ToolbarView, Menu::Delegate overrides:
372 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
373 return GetWidget()->GetAccelerator(id, accel);
376 ////////////////////////////////////////////////////////////////////////////////
377 // ToolbarView, views::MenuButtonListener implementation:
379 void ToolbarView::OnMenuButtonClicked(views::View* source,
380 const gfx::Point& point) {
381 TRACE_EVENT0("views", "ToolbarView::OnMenuButtonClicked");
382 DCHECK_EQ(VIEW_ID_APP_MENU, source->id());
384 bool use_new_menu = false;
385 bool supports_new_separators = false;
386 // TODO: remove this.
387 #if !defined(OS_LINUX) || defined(OS_CHROMEOS)
388 supports_new_separators =
389 GetNativeTheme() == ui::NativeThemeAura::instance();
390 use_new_menu = supports_new_separators;
391 #endif
393 if (keyboard::KeyboardController::GetInstance() &&
394 keyboard::KeyboardController::GetInstance()->keyboard_visible()) {
395 keyboard::KeyboardController::GetInstance()->HideKeyboard(
396 keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
399 wrench_menu_.reset(new WrenchMenu(browser_, use_new_menu,
400 supports_new_separators));
401 wrench_menu_model_.reset(new WrenchMenuModel(this, browser_, use_new_menu));
402 wrench_menu_->Init(wrench_menu_model_.get());
404 FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened());
406 wrench_menu_->RunMenu(app_menu_);
409 ////////////////////////////////////////////////////////////////////////////////
410 // ToolbarView, LocationBarView::Delegate implementation:
412 WebContents* ToolbarView::GetWebContents() {
413 return browser_->tab_strip_model()->GetActiveWebContents();
416 ToolbarModel* ToolbarView::GetToolbarModel() {
417 return browser_->toolbar_model();
420 const ToolbarModel* ToolbarView::GetToolbarModel() const {
421 return browser_->toolbar_model();
424 InstantController* ToolbarView::GetInstant() {
425 return browser_->instant_controller() ?
426 browser_->instant_controller()->instant() : NULL;
429 ContentSettingBubbleModelDelegate*
430 ToolbarView::GetContentSettingBubbleModelDelegate() {
431 return browser_->content_setting_bubble_model_delegate();
434 void ToolbarView::ShowWebsiteSettings(content::WebContents* web_contents,
435 const GURL& url,
436 const content::SSLStatus& ssl) {
437 chrome::ShowWebsiteSettings(browser_, web_contents, url, ssl);
440 views::Widget* ToolbarView::CreateViewsBubble(
441 views::BubbleDelegateView* bubble_delegate) {
442 return views::BubbleDelegateView::CreateBubble(bubble_delegate);
445 PageActionImageView* ToolbarView::CreatePageActionImageView(
446 LocationBarView* owner, ExtensionAction* action) {
447 return new PageActionImageView(owner, action, browser_);
450 ////////////////////////////////////////////////////////////////////////////////
451 // ToolbarView, CommandObserver implementation:
453 void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
454 views::Button* button = NULL;
455 switch (id) {
456 case IDC_BACK:
457 button = back_;
458 break;
459 case IDC_FORWARD:
460 button = forward_;
461 break;
462 case IDC_RELOAD:
463 button = reload_;
464 break;
465 case IDC_HOME:
466 button = home_;
467 break;
469 if (button)
470 button->SetEnabled(enabled);
473 ////////////////////////////////////////////////////////////////////////////////
474 // ToolbarView, views::Button::ButtonListener implementation:
476 void ToolbarView::ButtonPressed(views::Button* sender,
477 const ui::Event& event) {
478 chrome::ExecuteCommandWithDisposition(
479 browser_, sender->tag(), ui::DispositionFromEventFlags(event.flags()));
482 ////////////////////////////////////////////////////////////////////////////////
483 // ToolbarView, content::NotificationObserver implementation:
485 void ToolbarView::Observe(int type,
486 const content::NotificationSource& source,
487 const content::NotificationDetails& details) {
488 switch (type) {
489 case chrome::NOTIFICATION_UPGRADE_RECOMMENDED:
490 case chrome::NOTIFICATION_MODULE_INCOMPATIBILITY_BADGE_CHANGE:
491 case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED:
492 case chrome::NOTIFICATION_MODULE_LIST_ENUMERATED:
493 UpdateAppMenuState();
494 break;
495 case chrome::NOTIFICATION_OUTDATED_INSTALL:
496 ShowOutdatedInstallNotification(true);
497 break;
498 case chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU:
499 ShowOutdatedInstallNotification(false);
500 break;
501 #if defined(OS_WIN)
502 case chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED:
503 ShowCriticalNotification();
504 break;
505 #endif
506 default:
507 NOTREACHED();
511 ////////////////////////////////////////////////////////////////////////////////
512 // ToolbarView, ui::AcceleratorProvider implementation:
514 bool ToolbarView::GetAcceleratorForCommandId(int command_id,
515 ui::Accelerator* accelerator) {
516 return GetWidget()->GetAccelerator(command_id, accelerator);
519 ////////////////////////////////////////////////////////////////////////////////
520 // ToolbarView, views::View overrides:
522 gfx::Size ToolbarView::GetPreferredSize() const {
523 gfx::Size size(location_bar_->GetPreferredSize());
524 if (is_display_mode_normal()) {
525 int content_width = kLeftEdgeSpacing + back_->GetPreferredSize().width() +
526 forward_->GetPreferredSize().width() +
527 reload_->GetPreferredSize().width() +
528 (show_home_button_.GetValue() ? home_->GetPreferredSize().width() : 0) +
529 kStandardSpacing + browser_actions_->GetPreferredSize().width() +
530 app_menu_->GetPreferredSize().width() + kRightEdgeSpacing;
531 size.Enlarge(content_width, 0);
533 return SizeForContentSize(size);
536 gfx::Size ToolbarView::GetMinimumSize() const {
537 gfx::Size size(location_bar_->GetMinimumSize());
538 if (is_display_mode_normal()) {
539 int content_width = kLeftEdgeSpacing + back_->GetMinimumSize().width() +
540 forward_->GetMinimumSize().width() + reload_->GetMinimumSize().width() +
541 (show_home_button_.GetValue() ? home_->GetMinimumSize().width() : 0) +
542 kStandardSpacing + browser_actions_->GetMinimumSize().width() +
543 app_menu_->GetMinimumSize().width() + kRightEdgeSpacing;
544 size.Enlarge(content_width, 0);
546 return SizeForContentSize(size);
549 void ToolbarView::Layout() {
550 // If we have not been initialized yet just do nothing.
551 if (back_ == NULL)
552 return;
554 if (!is_display_mode_normal()) {
555 location_bar_->SetBounds(0, PopupTopSpacing(), width(),
556 location_bar_->GetPreferredSize().height());
557 return;
560 // We assume all child elements except the location bar are the same height.
561 // Set child_y such that buttons appear vertically centered. We put any excess
562 // padding above the buttons.
563 int child_height =
564 std::min(back_->GetPreferredSize().height(), height());
565 int child_y = (height() - child_height + 1) / 2;
567 // If the window is maximized, we extend the back button to the left so that
568 // clicking on the left-most pixel will activate the back button.
569 // TODO(abarth): If the window becomes maximized but is not resized,
570 // then Layout() might not be called and the back button
571 // will be slightly the wrong size. We should force a
572 // Layout() in this case.
573 // http://crbug.com/5540
574 bool maximized = browser_->window() && browser_->window()->IsMaximized();
575 int back_width = back_->GetPreferredSize().width();
576 if (maximized) {
577 back_->SetBounds(0, child_y, back_width + kLeftEdgeSpacing, child_height);
578 back_->SetLeadingMargin(kLeftEdgeSpacing);
579 } else {
580 back_->SetBounds(kLeftEdgeSpacing, child_y, back_width, child_height);
581 back_->SetLeadingMargin(0);
583 int next_element_x = back_->bounds().right();
585 forward_->SetBounds(next_element_x, child_y,
586 forward_->GetPreferredSize().width(), child_height);
587 next_element_x = forward_->bounds().right();
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() && IsStreamlinedHostedAppsEnabled())) {
595 home_->SetVisible(true);
596 home_->SetBounds(next_element_x, child_y,
597 home_->GetPreferredSize().width(), child_height);
598 } else {
599 home_->SetVisible(false);
600 home_->SetBounds(next_element_x, child_y, 0, child_height);
602 next_element_x = home_->bounds().right() + kStandardSpacing;
604 int browser_actions_width = browser_actions_->GetPreferredSize().width();
605 int app_menu_width = app_menu_->GetPreferredSize().width();
606 int available_width = std::max(0, width() - kRightEdgeSpacing -
607 app_menu_width - browser_actions_width - next_element_x);
609 int location_height = location_bar_->GetPreferredSize().height();
610 int location_y = (height() - location_height + 1) / 2;
611 location_bar_->SetBounds(next_element_x, location_y,
612 std::max(available_width, 0), location_height);
613 next_element_x = location_bar_->bounds().right();
615 browser_actions_->SetBounds(
616 next_element_x, child_y, browser_actions_width, child_height);
617 next_element_x = browser_actions_->bounds().right();
619 // The browser actions need to do a layout explicitly, because when an
620 // extension is loaded/unloaded/changed, BrowserActionContainer removes and
621 // re-adds everything, regardless of whether it has a page action. For a
622 // page action, browser action bounds do not change, as a result of which
623 // SetBounds does not do a layout at all.
624 // TODO(sidchat): Rework the above behavior so that explicit layout is not
625 // required.
626 browser_actions_->Layout();
628 // Extend the app menu to the screen's right edge in maximized mode just like
629 // we extend the back button to the left edge.
630 if (maximized)
631 app_menu_width += kRightEdgeSpacing;
632 app_menu_->SetBounds(next_element_x, child_y, app_menu_width, child_height);
635 bool ToolbarView::HitTestRect(const gfx::Rect& rect) const {
636 // Fall through to the tab strip above us if none of |rect| intersects
637 // with this view (intersection with the top shadow edge does not
638 // count as intersection with this view).
639 if (rect.bottom() < content_shadow_height())
640 return false;
641 // Otherwise let our superclass take care of it.
642 return AccessiblePaneView::HitTestRect(rect);
645 void ToolbarView::OnPaint(gfx::Canvas* canvas) {
646 View::OnPaint(canvas);
648 if (is_display_mode_normal())
649 return;
651 // For glass, we need to draw a black line below the location bar to separate
652 // it from the content area. For non-glass, the NonClientView draws the
653 // toolbar background below the location bar for us.
654 // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()!
655 if (GetWidget()->ShouldWindowContentsBeTransparent())
656 canvas->FillRect(gfx::Rect(0, height() - 1, width(), 1), SK_ColorBLACK);
659 void ToolbarView::OnThemeChanged() {
660 LoadImages();
663 const char* ToolbarView::GetClassName() const {
664 return kViewClassName;
667 bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) {
668 const views::View* focused_view = focus_manager()->GetFocusedView();
669 if (focused_view && (focused_view->id() == VIEW_ID_OMNIBOX))
670 return false; // Let the omnibox handle all accelerator events.
671 return AccessiblePaneView::AcceleratorPressed(accelerator);
674 bool ToolbarView::IsWrenchMenuShowing() const {
675 return wrench_menu_.get() && wrench_menu_->IsShowing();
678 bool ToolbarView::ShouldPaintBackground() const {
679 return display_mode_ == DISPLAYMODE_NORMAL;
682 ////////////////////////////////////////////////////////////////////////////////
683 // ToolbarView, protected:
685 // Override this so that when the user presses F6 to rotate toolbar panes,
686 // the location bar gets focus, not the first control in the toolbar - and
687 // also so that it selects all content in the location bar.
688 bool ToolbarView::SetPaneFocusAndFocusDefault() {
689 if (!location_bar_->HasFocus()) {
690 SetPaneFocus(location_bar_);
691 location_bar_->FocusLocation(true);
692 return true;
695 if (!AccessiblePaneView::SetPaneFocusAndFocusDefault())
696 return false;
697 browser_->window()->RotatePaneFocus(true);
698 return true;
701 void ToolbarView::RemovePaneFocus() {
702 AccessiblePaneView::RemovePaneFocus();
703 location_bar_->SetShowFocusRect(false);
706 ////////////////////////////////////////////////////////////////////////////////
707 // ToolbarView, private:
709 bool ToolbarView::ShouldShowUpgradeRecommended() {
710 #if defined(OS_CHROMEOS)
711 // In chromeos, the update recommendation is shown in the system tray. So it
712 // should not be displayed in the wrench menu.
713 return false;
714 #else
715 return (UpgradeDetector::GetInstance()->notify_upgrade());
716 #endif
719 bool ToolbarView::ShouldShowIncompatibilityWarning() {
720 #if defined(OS_WIN)
721 EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetInstance();
722 loaded_modules->MaybePostScanningTask();
723 return loaded_modules->ShouldShowConflictWarning();
724 #else
725 return false;
726 #endif
729 int ToolbarView::PopupTopSpacing() const {
730 const int kPopupTopSpacingNonGlass = 3;
731 return GetWidget()->ShouldWindowContentsBeTransparent() ?
732 0 : kPopupTopSpacingNonGlass;
735 gfx::Size ToolbarView::SizeForContentSize(gfx::Size size) const {
736 if (is_display_mode_normal()) {
737 gfx::ImageSkia* normal_background =
738 GetThemeProvider()->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER);
739 size.SetToMax(
740 gfx::Size(0, normal_background->height() - content_shadow_height()));
741 } else {
742 const int kPopupBottomSpacingGlass = 1;
743 const int kPopupBottomSpacingNonGlass = 2;
744 size.Enlarge(
746 PopupTopSpacing() + (GetWidget()->ShouldWindowContentsBeTransparent() ?
747 kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass));
749 return size;
752 void ToolbarView::LoadImages() {
753 ui::ThemeProvider* tp = GetThemeProvider();
755 back_->SetImage(views::Button::STATE_NORMAL,
756 *(tp->GetImageSkiaNamed(IDR_BACK)));
757 back_->SetImage(views::Button::STATE_DISABLED,
758 *(tp->GetImageSkiaNamed(IDR_BACK_D)));
760 forward_->SetImage(views::Button::STATE_NORMAL,
761 *(tp->GetImageSkiaNamed(IDR_FORWARD)));
762 forward_->SetImage(views::Button::STATE_DISABLED,
763 *(tp->GetImageSkiaNamed(IDR_FORWARD_D)));
765 reload_->LoadImages();
767 home_->SetImage(views::Button::STATE_NORMAL,
768 *(tp->GetImageSkiaNamed(IDR_HOME)));
771 void ToolbarView::ShowCriticalNotification() {
772 #if defined(OS_WIN)
773 CriticalNotificationBubbleView* bubble_delegate =
774 new CriticalNotificationBubbleView(app_menu_);
775 views::BubbleDelegateView::CreateBubble(bubble_delegate)->Show();
776 #endif
779 void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) {
780 if (OutdatedUpgradeBubbleView::IsAvailable()) {
781 OutdatedUpgradeBubbleView::ShowBubble(
782 app_menu_, browser_, auto_update_enabled);
786 void ToolbarView::UpdateAppMenuState() {
787 base::string16 accname_app = l10n_util::GetStringUTF16(IDS_ACCNAME_APP);
788 if (ShouldShowUpgradeRecommended()) {
789 accname_app = l10n_util::GetStringFUTF16(
790 IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app);
792 app_menu_->SetAccessibleName(accname_app);
794 UpdateWrenchButtonSeverity();
795 SchedulePaint();
798 void ToolbarView::UpdateWrenchButtonSeverity() {
799 // Showing the bubble requires |app_menu_| to be in a widget. See comment
800 // in ConflictingModuleView for details.
801 DCHECK(app_menu_->GetWidget());
803 // Keep track of whether we were showing the badge before, so we don't send
804 // multiple UMA events for example when multiple Chrome windows are open.
805 static bool incompatibility_badge_showing = false;
806 // Save the old value before resetting it.
807 bool was_showing = incompatibility_badge_showing;
808 incompatibility_badge_showing = false;
810 if (ShouldShowUpgradeRecommended()) {
811 UpgradeDetector::UpgradeNotificationAnnoyanceLevel level =
812 UpgradeDetector::GetInstance()->upgrade_notification_stage();
813 app_menu_->SetSeverity(WrenchIconPainter::SeverityFromUpgradeLevel(level),
814 WrenchIconPainter::ShouldAnimateUpgradeLevel(level));
815 return;
818 if (ShouldShowIncompatibilityWarning()) {
819 if (!was_showing) {
820 content::RecordAction(UserMetricsAction("ConflictBadge"));
821 #if defined(OS_WIN)
822 ConflictingModuleView::MaybeShow(browser_, app_menu_);
823 #endif
825 app_menu_->SetSeverity(WrenchIconPainter::SEVERITY_MEDIUM, true);
826 incompatibility_badge_showing = true;
827 return;
830 GlobalErrorService* service =
831 GlobalErrorServiceFactory::GetForProfile(browser_->profile());
832 GlobalError* error =
833 service->GetHighestSeverityGlobalErrorWithWrenchMenuItem();
834 if (error) {
835 app_menu_->SetSeverity(WrenchIconPainter::GlobalErrorSeverity(), true);
836 return;
839 app_menu_->SetSeverity(WrenchIconPainter::SEVERITY_NONE, true);
842 void ToolbarView::OnShowHomeButtonChanged() {
843 Layout();
844 SchedulePaint();
847 int ToolbarView::content_shadow_height() const {
848 return browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH ?
849 kContentShadowHeightAsh : kContentShadowHeight;