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"
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"
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"
87 #include "ash/shell.h"
90 using base::UserMetricsAction
;
91 using content::WebContents
;
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)
114 return ash::Shell::HasInstance();
119 #endif // OS_CHROMEOS
124 const char ToolbarView::kViewClassName
[] = "ToolbarView";
126 ////////////////////////////////////////////////////////////////////////////////
127 // ToolbarView, public:
129 ToolbarView::ToolbarView(Browser
* browser
)
135 browser_actions_(NULL
),
138 extension_message_bubble_factory_(
139 new extensions::ExtensionMessageBubbleFactory(browser
->profile(),
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());
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());
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
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
);
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
);
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
);
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
);
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.
239 AddChildView(forward_
);
240 AddChildView(reload_
);
242 AddChildView(location_bar_
);
243 AddChildView(browser_actions_
);
244 AddChildView(app_menu_
);
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());
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
,
284 // Safe to call multiple times; the bubble will only appear once.
286 extension_message_bubble_factory_
->MaybeShow(app_menu_
);
289 void ToolbarView::Update(WebContents
* tab
) {
291 location_bar_
->Update(tab
);
292 if (browser_actions_
)
293 browser_actions_
->RefreshBrowserActionViews();
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 {
351 ////////////////////////////////////////////////////////////////////////////////
352 // ToolbarView, AccessiblePaneView overrides:
354 bool ToolbarView::SetPaneFocus(views::View
* initial_focus
) {
355 if (!AccessiblePaneView::SetPaneFocus(initial_focus
))
358 location_bar_
->SetShowFocusRect(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
;
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
,
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
;
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
) {
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();
493 case chrome::NOTIFICATION_OUTDATED_INSTALL
:
494 ShowOutdatedInstallNotification(true);
496 case chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU
:
497 ShowOutdatedInstallNotification(false);
500 case chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED
:
501 ShowCriticalNotification();
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.
552 if (!is_display_mode_normal()) {
553 location_bar_
->SetBounds(0, PopupTopSpacing(), width(),
554 location_bar_
->GetPreferredSize().height());
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.
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();
575 back_
->SetBounds(0, child_y
, back_width
+ kLeftEdgeSpacing
, child_height
);
576 back_
->SetLeadingMargin(kLeftEdgeSpacing
);
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
);
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
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.
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())
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())
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() {
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);
693 if (!AccessiblePaneView::SetPaneFocusAndFocusDefault())
695 browser_
->window()->RotatePaneFocus(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.
713 return (UpgradeDetector::GetInstance()->notify_upgrade());
717 bool ToolbarView::ShouldShowIncompatibilityWarning() {
719 EnumerateModulesModel
* loaded_modules
= EnumerateModulesModel::GetInstance();
720 loaded_modules
->MaybePostScanningTask();
721 return loaded_modules
->ShouldShowConflictWarning();
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
);
738 gfx::Size(0, normal_background
->height() - content_shadow_height()));
740 const int kPopupBottomSpacingGlass
= 1;
741 const int kPopupBottomSpacingNonGlass
= 2;
744 PopupTopSpacing() + (GetWidget()->ShouldWindowContentsBeTransparent() ?
745 kPopupBottomSpacingGlass
: kPopupBottomSpacingNonGlass
));
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() {
771 CriticalNotificationBubbleView
* bubble_delegate
=
772 new CriticalNotificationBubbleView(app_menu_
);
773 views::BubbleDelegateView::CreateBubble(bubble_delegate
)->Show();
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();
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
));
816 if (ShouldShowIncompatibilityWarning()) {
818 content::RecordAction(UserMetricsAction("ConflictBadge"));
820 ConflictingModuleView::MaybeShow(browser_
, app_menu_
);
823 app_menu_
->SetSeverity(WrenchIconPainter::SEVERITY_MEDIUM
, true);
824 incompatibility_badge_showing
= true;
828 GlobalErrorService
* service
=
829 GlobalErrorServiceFactory::GetForProfile(browser_
->profile());
831 service
->GetHighestSeverityGlobalErrorWithWrenchMenuItem();
833 app_menu_
->SetSeverity(WrenchIconPainter::GlobalErrorSeverity(), true);
837 app_menu_
->SetSeverity(WrenchIconPainter::SEVERITY_NONE
, true);
840 void ToolbarView::OnShowHomeButtonChanged() {
845 int ToolbarView::content_shadow_height() const {
846 return browser_
->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH
?
847 kContentShadowHeightAsh
: kContentShadowHeight
;