[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / views / toolbar / toolbar_view.cc
blobd610cea3a4d7d7ea8912595ab25af20e4280672b
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(browser_, this);
230 app_menu_ = new WrenchToolbarButton(this);
231 app_menu_->SetBorder(views::Border::NullBorder());
232 app_menu_->EnableCanvasFlippingForRTLUI(true);
233 app_menu_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_APP));
234 app_menu_->SetTooltipText(l10n_util::GetStringUTF16(IDS_APPMENU_TOOLTIP));
235 app_menu_->set_id(VIEW_ID_APP_MENU);
237 // Always add children in order from left to right, for accessibility.
238 AddChildView(back_);
239 AddChildView(forward_);
240 AddChildView(reload_);
241 AddChildView(home_);
242 AddChildView(location_bar_);
243 AddChildView(browser_actions_);
244 AddChildView(app_menu_);
246 LoadImages();
248 // Start global error services now so we badge the menu correctly in non-Ash.
249 #if !defined(OS_CHROMEOS)
250 if (!HasAshShell()) {
251 SigninGlobalErrorFactory::GetForProfile(browser_->profile());
252 #if !defined(OS_ANDROID)
253 SyncGlobalErrorFactory::GetForProfile(browser_->profile());
254 #endif
256 #endif // OS_CHROMEOS
258 // Add any necessary badges to the menu item based on the system state.
259 // Do this after |app_menu_| has been added as a bubble may be shown that
260 // needs the widget (widget found by way of app_menu_->GetWidget()).
261 UpdateAppMenuState();
263 location_bar_->Init();
265 show_home_button_.Init(prefs::kShowHomeButton,
266 browser_->profile()->GetPrefs(),
267 base::Bind(&ToolbarView::OnShowHomeButtonChanged,
268 base::Unretained(this)));
270 browser_actions_->Init();
272 // Accessibility specific tooltip text.
273 if (content::BrowserAccessibilityState::GetInstance()->
274 IsAccessibleBrowser()) {
275 back_->SetTooltipText(
276 l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK));
277 forward_->SetTooltipText(
278 l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD));
282 void ToolbarView::OnWidgetVisibilityChanged(views::Widget* widget,
283 bool visible) {
284 // Safe to call multiple times; the bubble will only appear once.
285 if (visible)
286 extension_message_bubble_factory_->MaybeShow(app_menu_);
289 void ToolbarView::Update(WebContents* tab) {
290 if (location_bar_)
291 location_bar_->Update(tab);
292 if (browser_actions_)
293 browser_actions_->RefreshBrowserActionViews();
294 if (reload_)
295 reload_->set_menu_enabled(chrome::IsDebuggerAttachedToCurrentTab(browser_));
298 void ToolbarView::SetPaneFocusAndFocusAppMenu() {
299 SetPaneFocus(app_menu_);
302 bool ToolbarView::IsAppMenuFocused() {
303 return app_menu_->HasFocus();
306 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
307 menu_listeners_.AddObserver(listener);
310 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
311 menu_listeners_.RemoveObserver(listener);
314 views::View* ToolbarView::GetBookmarkBubbleAnchor() {
315 views::View* star_view = location_bar()->star_view();
316 return (star_view && star_view->visible()) ? star_view : app_menu_;
319 views::View* ToolbarView::GetTranslateBubbleAnchor() {
320 views::View* translate_icon_view = location_bar()->translate_icon_view();
321 return (translate_icon_view && translate_icon_view->visible()) ?
322 translate_icon_view : app_menu_;
325 void ToolbarView::ExecuteExtensionCommand(
326 const extensions::Extension* extension,
327 const extensions::Command& command) {
328 browser_actions_->ExecuteExtensionCommand(extension, command);
331 void ToolbarView::ShowPageActionPopup(const extensions::Extension* extension) {
332 extensions::ExtensionActionManager* extension_manager =
333 extensions::ExtensionActionManager::Get(browser_->profile());
334 ExtensionAction* extension_action =
335 extension_manager->GetPageAction(*extension);
336 if (extension_action) {
337 location_bar_->GetPageActionView(extension_action)->image_view()->
338 ExecuteAction(ExtensionPopup::SHOW);
342 void ToolbarView::ShowBrowserActionPopup(
343 const extensions::Extension* extension) {
344 browser_actions_->ShowPopup(extension, true);
347 views::MenuButton* ToolbarView::app_menu() const {
348 return app_menu_;
351 ////////////////////////////////////////////////////////////////////////////////
352 // ToolbarView, AccessiblePaneView overrides:
354 bool ToolbarView::SetPaneFocus(views::View* initial_focus) {
355 if (!AccessiblePaneView::SetPaneFocus(initial_focus))
356 return false;
358 location_bar_->SetShowFocusRect(true);
359 return true;
362 void ToolbarView::GetAccessibleState(ui::AXViewState* state) {
363 state->role = ui::AX_ROLE_TOOLBAR;
364 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR);
367 ////////////////////////////////////////////////////////////////////////////////
368 // ToolbarView, Menu::Delegate overrides:
370 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
371 return GetWidget()->GetAccelerator(id, accel);
374 ////////////////////////////////////////////////////////////////////////////////
375 // ToolbarView, views::MenuButtonListener implementation:
377 void ToolbarView::OnMenuButtonClicked(views::View* source,
378 const gfx::Point& point) {
379 TRACE_EVENT0("views", "ToolbarView::OnMenuButtonClicked");
380 DCHECK_EQ(VIEW_ID_APP_MENU, source->id());
382 bool use_new_menu = false;
383 bool supports_new_separators = false;
384 // TODO: remove this.
385 #if !defined(OS_LINUX) || defined(OS_CHROMEOS)
386 supports_new_separators =
387 GetNativeTheme() == ui::NativeThemeAura::instance();
388 use_new_menu = supports_new_separators;
389 #endif
391 if (keyboard::KeyboardController::GetInstance() &&
392 keyboard::KeyboardController::GetInstance()->keyboard_visible()) {
393 keyboard::KeyboardController::GetInstance()->HideKeyboard(
394 keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
397 wrench_menu_.reset(new WrenchMenu(browser_, use_new_menu,
398 supports_new_separators));
399 wrench_menu_model_.reset(new WrenchMenuModel(this, browser_, use_new_menu));
400 wrench_menu_->Init(wrench_menu_model_.get());
402 FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened());
404 wrench_menu_->RunMenu(app_menu_);
407 ////////////////////////////////////////////////////////////////////////////////
408 // ToolbarView, LocationBarView::Delegate implementation:
410 WebContents* ToolbarView::GetWebContents() {
411 return browser_->tab_strip_model()->GetActiveWebContents();
414 ToolbarModel* ToolbarView::GetToolbarModel() {
415 return browser_->toolbar_model();
418 const ToolbarModel* ToolbarView::GetToolbarModel() const {
419 return browser_->toolbar_model();
422 InstantController* ToolbarView::GetInstant() {
423 return browser_->instant_controller() ?
424 browser_->instant_controller()->instant() : NULL;
427 ContentSettingBubbleModelDelegate*
428 ToolbarView::GetContentSettingBubbleModelDelegate() {
429 return browser_->content_setting_bubble_model_delegate();
432 void ToolbarView::ShowWebsiteSettings(content::WebContents* web_contents,
433 const GURL& url,
434 const content::SSLStatus& ssl) {
435 chrome::ShowWebsiteSettings(browser_, web_contents, url, ssl);
438 views::Widget* ToolbarView::CreateViewsBubble(
439 views::BubbleDelegateView* bubble_delegate) {
440 return views::BubbleDelegateView::CreateBubble(bubble_delegate);
443 PageActionImageView* ToolbarView::CreatePageActionImageView(
444 LocationBarView* owner, ExtensionAction* action) {
445 return new PageActionImageView(owner, action, browser_);
448 ////////////////////////////////////////////////////////////////////////////////
449 // ToolbarView, CommandObserver implementation:
451 void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
452 views::Button* button = NULL;
453 switch (id) {
454 case IDC_BACK:
455 button = back_;
456 break;
457 case IDC_FORWARD:
458 button = forward_;
459 break;
460 case IDC_RELOAD:
461 button = reload_;
462 break;
463 case IDC_HOME:
464 button = home_;
465 break;
467 if (button)
468 button->SetEnabled(enabled);
471 ////////////////////////////////////////////////////////////////////////////////
472 // ToolbarView, views::Button::ButtonListener implementation:
474 void ToolbarView::ButtonPressed(views::Button* sender,
475 const ui::Event& event) {
476 chrome::ExecuteCommandWithDisposition(
477 browser_, sender->tag(), ui::DispositionFromEventFlags(event.flags()));
480 ////////////////////////////////////////////////////////////////////////////////
481 // ToolbarView, content::NotificationObserver implementation:
483 void ToolbarView::Observe(int type,
484 const content::NotificationSource& source,
485 const content::NotificationDetails& details) {
486 switch (type) {
487 case chrome::NOTIFICATION_UPGRADE_RECOMMENDED:
488 case chrome::NOTIFICATION_MODULE_INCOMPATIBILITY_BADGE_CHANGE:
489 case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED:
490 case chrome::NOTIFICATION_MODULE_LIST_ENUMERATED:
491 UpdateAppMenuState();
492 break;
493 case chrome::NOTIFICATION_OUTDATED_INSTALL:
494 ShowOutdatedInstallNotification(true);
495 break;
496 case chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU:
497 ShowOutdatedInstallNotification(false);
498 break;
499 #if defined(OS_WIN)
500 case chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED:
501 ShowCriticalNotification();
502 break;
503 #endif
504 default:
505 NOTREACHED();
509 ////////////////////////////////////////////////////////////////////////////////
510 // ToolbarView, ui::AcceleratorProvider implementation:
512 bool ToolbarView::GetAcceleratorForCommandId(int command_id,
513 ui::Accelerator* accelerator) {
514 return GetWidget()->GetAccelerator(command_id, accelerator);
517 ////////////////////////////////////////////////////////////////////////////////
518 // ToolbarView, views::View overrides:
520 gfx::Size ToolbarView::GetPreferredSize() const {
521 gfx::Size size(location_bar_->GetPreferredSize());
522 if (is_display_mode_normal()) {
523 int content_width = kLeftEdgeSpacing + back_->GetPreferredSize().width() +
524 forward_->GetPreferredSize().width() +
525 reload_->GetPreferredSize().width() +
526 (show_home_button_.GetValue() ? home_->GetPreferredSize().width() : 0) +
527 kStandardSpacing + browser_actions_->GetPreferredSize().width() +
528 app_menu_->GetPreferredSize().width() + kRightEdgeSpacing;
529 size.Enlarge(content_width, 0);
531 return SizeForContentSize(size);
534 gfx::Size ToolbarView::GetMinimumSize() const {
535 gfx::Size size(location_bar_->GetMinimumSize());
536 if (is_display_mode_normal()) {
537 int content_width = kLeftEdgeSpacing + back_->GetMinimumSize().width() +
538 forward_->GetMinimumSize().width() + reload_->GetMinimumSize().width() +
539 (show_home_button_.GetValue() ? home_->GetMinimumSize().width() : 0) +
540 kStandardSpacing + browser_actions_->GetMinimumSize().width() +
541 app_menu_->GetMinimumSize().width() + kRightEdgeSpacing;
542 size.Enlarge(content_width, 0);
544 return SizeForContentSize(size);
547 void ToolbarView::Layout() {
548 // If we have not been initialized yet just do nothing.
549 if (back_ == NULL)
550 return;
552 if (!is_display_mode_normal()) {
553 location_bar_->SetBounds(0, PopupTopSpacing(), width(),
554 location_bar_->GetPreferredSize().height());
555 return;
558 // We assume all child elements except the location bar are the same height.
559 // Set child_y such that buttons appear vertically centered. We put any excess
560 // padding above the buttons.
561 int child_height =
562 std::min(back_->GetPreferredSize().height(), height());
563 int child_y = (height() - child_height + 1) / 2;
565 // If the window is maximized, we extend the back button to the left so that
566 // clicking on the left-most pixel will activate the back button.
567 // TODO(abarth): If the window becomes maximized but is not resized,
568 // then Layout() might not be called and the back button
569 // will be slightly the wrong size. We should force a
570 // Layout() in this case.
571 // http://crbug.com/5540
572 bool maximized = browser_->window() && browser_->window()->IsMaximized();
573 int back_width = back_->GetPreferredSize().width();
574 if (maximized) {
575 back_->SetBounds(0, child_y, back_width + kLeftEdgeSpacing, child_height);
576 back_->SetLeadingMargin(kLeftEdgeSpacing);
577 } else {
578 back_->SetBounds(kLeftEdgeSpacing, child_y, back_width, child_height);
579 back_->SetLeadingMargin(0);
581 int next_element_x = back_->bounds().right();
583 forward_->SetBounds(next_element_x, child_y,
584 forward_->GetPreferredSize().width(), child_height);
585 next_element_x = forward_->bounds().right();
587 reload_->SetBounds(next_element_x, child_y,
588 reload_->GetPreferredSize().width(), child_height);
589 next_element_x = reload_->bounds().right();
591 if (show_home_button_.GetValue() ||
592 (browser_->is_app() && IsStreamlinedHostedAppsEnabled())) {
593 home_->SetVisible(true);
594 home_->SetBounds(next_element_x, child_y,
595 home_->GetPreferredSize().width(), child_height);
596 } else {
597 home_->SetVisible(false);
598 home_->SetBounds(next_element_x, child_y, 0, child_height);
600 next_element_x = home_->bounds().right() + kStandardSpacing;
602 int browser_actions_width = browser_actions_->GetPreferredSize().width();
603 int app_menu_width = app_menu_->GetPreferredSize().width();
604 int available_width = std::max(0, width() - kRightEdgeSpacing -
605 app_menu_width - browser_actions_width - next_element_x);
607 int location_height = location_bar_->GetPreferredSize().height();
608 int location_y = (height() - location_height + 1) / 2;
609 location_bar_->SetBounds(next_element_x, location_y,
610 std::max(available_width, 0), location_height);
611 next_element_x = location_bar_->bounds().right();
613 browser_actions_->SetBounds(
614 next_element_x, child_y, browser_actions_width, child_height);
615 next_element_x = browser_actions_->bounds().right();
617 // The browser actions need to do a layout explicitly, because when an
618 // extension is loaded/unloaded/changed, BrowserActionContainer removes and
619 // re-adds everything, regardless of whether it has a page action. For a
620 // page action, browser action bounds do not change, as a result of which
621 // SetBounds does not do a layout at all.
622 // TODO(sidchat): Rework the above behavior so that explicit layout is not
623 // required.
624 browser_actions_->Layout();
626 // Extend the app menu to the screen's right edge in maximized mode just like
627 // we extend the back button to the left edge.
628 if (maximized)
629 app_menu_width += kRightEdgeSpacing;
630 app_menu_->SetBounds(next_element_x, child_y, app_menu_width, child_height);
633 bool ToolbarView::HitTestRect(const gfx::Rect& rect) const {
634 // Fall through to the tab strip above us if none of |rect| intersects
635 // with this view (intersection with the top shadow edge does not
636 // count as intersection with this view).
637 if (rect.bottom() < content_shadow_height())
638 return false;
639 // Otherwise let our superclass take care of it.
640 return AccessiblePaneView::HitTestRect(rect);
643 void ToolbarView::OnPaint(gfx::Canvas* canvas) {
644 View::OnPaint(canvas);
646 if (is_display_mode_normal())
647 return;
649 // For glass, we need to draw a black line below the location bar to separate
650 // it from the content area. For non-glass, the NonClientView draws the
651 // toolbar background below the location bar for us.
652 // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()!
653 if (GetWidget()->ShouldWindowContentsBeTransparent())
654 canvas->FillRect(gfx::Rect(0, height() - 1, width(), 1), SK_ColorBLACK);
657 void ToolbarView::OnThemeChanged() {
658 LoadImages();
661 const char* ToolbarView::GetClassName() const {
662 return kViewClassName;
665 bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) {
666 const views::View* focused_view = focus_manager()->GetFocusedView();
667 if (focused_view && (focused_view->id() == VIEW_ID_OMNIBOX))
668 return false; // Let the omnibox handle all accelerator events.
669 return AccessiblePaneView::AcceleratorPressed(accelerator);
672 bool ToolbarView::IsWrenchMenuShowing() const {
673 return wrench_menu_.get() && wrench_menu_->IsShowing();
676 bool ToolbarView::ShouldPaintBackground() const {
677 return display_mode_ == DISPLAYMODE_NORMAL;
680 ////////////////////////////////////////////////////////////////////////////////
681 // ToolbarView, protected:
683 // Override this so that when the user presses F6 to rotate toolbar panes,
684 // the location bar gets focus, not the first control in the toolbar - and
685 // also so that it selects all content in the location bar.
686 bool ToolbarView::SetPaneFocusAndFocusDefault() {
687 if (!location_bar_->HasFocus()) {
688 SetPaneFocus(location_bar_);
689 location_bar_->FocusLocation(true);
690 return true;
693 if (!AccessiblePaneView::SetPaneFocusAndFocusDefault())
694 return false;
695 browser_->window()->RotatePaneFocus(true);
696 return true;
699 void ToolbarView::RemovePaneFocus() {
700 AccessiblePaneView::RemovePaneFocus();
701 location_bar_->SetShowFocusRect(false);
704 ////////////////////////////////////////////////////////////////////////////////
705 // ToolbarView, private:
707 bool ToolbarView::ShouldShowUpgradeRecommended() {
708 #if defined(OS_CHROMEOS)
709 // In chromeos, the update recommendation is shown in the system tray. So it
710 // should not be displayed in the wrench menu.
711 return false;
712 #else
713 return (UpgradeDetector::GetInstance()->notify_upgrade());
714 #endif
717 bool ToolbarView::ShouldShowIncompatibilityWarning() {
718 #if defined(OS_WIN)
719 EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetInstance();
720 loaded_modules->MaybePostScanningTask();
721 return loaded_modules->ShouldShowConflictWarning();
722 #else
723 return false;
724 #endif
727 int ToolbarView::PopupTopSpacing() const {
728 const int kPopupTopSpacingNonGlass = 3;
729 return GetWidget()->ShouldWindowContentsBeTransparent() ?
730 0 : kPopupTopSpacingNonGlass;
733 gfx::Size ToolbarView::SizeForContentSize(gfx::Size size) const {
734 if (is_display_mode_normal()) {
735 gfx::ImageSkia* normal_background =
736 GetThemeProvider()->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER);
737 size.SetToMax(
738 gfx::Size(0, normal_background->height() - content_shadow_height()));
739 } else {
740 const int kPopupBottomSpacingGlass = 1;
741 const int kPopupBottomSpacingNonGlass = 2;
742 size.Enlarge(
744 PopupTopSpacing() + (GetWidget()->ShouldWindowContentsBeTransparent() ?
745 kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass));
747 return size;
750 void ToolbarView::LoadImages() {
751 ui::ThemeProvider* tp = GetThemeProvider();
753 back_->SetImage(views::Button::STATE_NORMAL,
754 *(tp->GetImageSkiaNamed(IDR_BACK)));
755 back_->SetImage(views::Button::STATE_DISABLED,
756 *(tp->GetImageSkiaNamed(IDR_BACK_D)));
758 forward_->SetImage(views::Button::STATE_NORMAL,
759 *(tp->GetImageSkiaNamed(IDR_FORWARD)));
760 forward_->SetImage(views::Button::STATE_DISABLED,
761 *(tp->GetImageSkiaNamed(IDR_FORWARD_D)));
763 reload_->LoadImages();
765 home_->SetImage(views::Button::STATE_NORMAL,
766 *(tp->GetImageSkiaNamed(IDR_HOME)));
769 void ToolbarView::ShowCriticalNotification() {
770 #if defined(OS_WIN)
771 CriticalNotificationBubbleView* bubble_delegate =
772 new CriticalNotificationBubbleView(app_menu_);
773 views::BubbleDelegateView::CreateBubble(bubble_delegate)->Show();
774 #endif
777 void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) {
778 if (OutdatedUpgradeBubbleView::IsAvailable()) {
779 OutdatedUpgradeBubbleView::ShowBubble(
780 app_menu_, browser_, auto_update_enabled);
784 void ToolbarView::UpdateAppMenuState() {
785 base::string16 accname_app = l10n_util::GetStringUTF16(IDS_ACCNAME_APP);
786 if (ShouldShowUpgradeRecommended()) {
787 accname_app = l10n_util::GetStringFUTF16(
788 IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app);
790 app_menu_->SetAccessibleName(accname_app);
792 UpdateWrenchButtonSeverity();
793 SchedulePaint();
796 void ToolbarView::UpdateWrenchButtonSeverity() {
797 // Showing the bubble requires |app_menu_| to be in a widget. See comment
798 // in ConflictingModuleView for details.
799 DCHECK(app_menu_->GetWidget());
801 // Keep track of whether we were showing the badge before, so we don't send
802 // multiple UMA events for example when multiple Chrome windows are open.
803 static bool incompatibility_badge_showing = false;
804 // Save the old value before resetting it.
805 bool was_showing = incompatibility_badge_showing;
806 incompatibility_badge_showing = false;
808 if (ShouldShowUpgradeRecommended()) {
809 UpgradeDetector::UpgradeNotificationAnnoyanceLevel level =
810 UpgradeDetector::GetInstance()->upgrade_notification_stage();
811 app_menu_->SetSeverity(WrenchIconPainter::SeverityFromUpgradeLevel(level),
812 WrenchIconPainter::ShouldAnimateUpgradeLevel(level));
813 return;
816 if (ShouldShowIncompatibilityWarning()) {
817 if (!was_showing) {
818 content::RecordAction(UserMetricsAction("ConflictBadge"));
819 #if defined(OS_WIN)
820 ConflictingModuleView::MaybeShow(browser_, app_menu_);
821 #endif
823 app_menu_->SetSeverity(WrenchIconPainter::SEVERITY_MEDIUM, true);
824 incompatibility_badge_showing = true;
825 return;
828 GlobalErrorService* service =
829 GlobalErrorServiceFactory::GetForProfile(browser_->profile());
830 GlobalError* error =
831 service->GetHighestSeverityGlobalErrorWithWrenchMenuItem();
832 if (error) {
833 app_menu_->SetSeverity(WrenchIconPainter::GlobalErrorSeverity(), true);
834 return;
837 app_menu_->SetSeverity(WrenchIconPainter::SEVERITY_NONE, true);
840 void ToolbarView::OnShowHomeButtonChanged() {
841 Layout();
842 SchedulePaint();
845 int ToolbarView::content_shadow_height() const {
846 return browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH ?
847 kContentShadowHeightAsh : kContentShadowHeight;