Home | History | Annotate | Download | only in toolbar
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
      6 
      7 #include "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"
     73 
     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
     80 
     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
     85 
     86 #if defined(USE_ASH)
     87 #include "ash/shell.h"
     88 #endif
     89 
     90 using base::UserMetricsAction;
     91 using content::WebContents;
     92 
     93 namespace {
     94 
     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;
     99 
    100 // Ash doesn't use a rounded content area and its top edge has an extra shadow.
    101 const int kContentShadowHeightAsh = 2;
    102 
    103 // Non-ash uses a rounded content area with no shadow in the assets.
    104 const int kContentShadowHeight = 0;
    105 
    106 bool IsStreamlinedHostedAppsEnabled() {
    107   return CommandLine::ForCurrentProcess()->HasSwitch(
    108       switches::kEnableStreamlinedHostedApps);
    109 }
    110 
    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
    118 }
    119 #endif  // OS_CHROMEOS
    120 
    121 }  // namespace
    122 
    123 // static
    124 const char ToolbarView::kViewClassName[] = "ToolbarView";
    125 
    126 ////////////////////////////////////////////////////////////////////////////////
    127 // ToolbarView, public:
    128 
    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);
    142 
    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);
    148 
    149   display_mode_ = DISPLAYMODE_LOCATION;
    150   if (browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ||
    151       (browser->is_app() && IsStreamlinedHostedAppsEnabled()))
    152     display_mode_ = DISPLAYMODE_NORMAL;
    153 
    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());
    161   }
    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());
    168   }
    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()));
    175 }
    176 
    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.
    181 }
    182 
    183 void ToolbarView::Init() {
    184   GetWidget()->AddObserver(this);
    185 
    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();
    195 
    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();
    205 
    206   location_bar_ = new LocationBarView(
    207       browser_, browser_->profile(),
    208       browser_->command_controller()->command_updater(), this,
    209       display_mode_ == DISPLAYMODE_LOCATION);
    210 
    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();
    218 
    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();
    227 
    228   browser_actions_ = new BrowserActionsContainer(browser_, this);
    229 
    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);
    236 
    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_);
    245 
    246   LoadImages();
    247 
    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
    255   }
    256 #endif  // OS_CHROMEOS
    257 
    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();
    262 
    263   location_bar_->Init();
    264 
    265   show_home_button_.Init(prefs::kShowHomeButton,
    266                          browser_->profile()->GetPrefs(),
    267                          base::Bind(&ToolbarView::OnShowHomeButtonChanged,
    268                                     base::Unretained(this)));
    269 
    270   browser_actions_->Init();
    271 
    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));
    279   }
    280 }
    281 
    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_);
    287 }
    288 
    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_));
    296 }
    297 
    298 void ToolbarView::SetPaneFocusAndFocusAppMenu() {
    299   SetPaneFocus(app_menu_);
    300 }
    301 
    302 bool ToolbarView::IsAppMenuFocused() {
    303   return app_menu_->HasFocus();
    304 }
    305 
    306 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
    307   menu_listeners_.AddObserver(listener);
    308 }
    309 
    310 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
    311   menu_listeners_.RemoveObserver(listener);
    312 }
    313 
    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_;
    317 }
    318 
    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_;
    323 }
    324 
    325 void ToolbarView::ExecuteExtensionCommand(
    326     const extensions::Extension* extension,
    327     const extensions::Command& command) {
    328   browser_actions_->ExecuteExtensionCommand(extension, command);
    329 }
    330 
    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);
    339   }
    340 }
    341 
    342 void ToolbarView::ShowBrowserActionPopup(
    343     const extensions::Extension* extension) {
    344   browser_actions_->ShowPopup(extension, true);
    345 }
    346 
    347 views::MenuButton* ToolbarView::app_menu() const {
    348   return app_menu_;
    349 }
    350 
    351 ////////////////////////////////////////////////////////////////////////////////
    352 // ToolbarView, AccessiblePaneView overrides:
    353 
    354 bool ToolbarView::SetPaneFocus(views::View* initial_focus) {
    355   if (!AccessiblePaneView::SetPaneFocus(initial_focus))
    356     return false;
    357 
    358   location_bar_->SetShowFocusRect(true);
    359   return true;
    360 }
    361 
    362 void ToolbarView::GetAccessibleState(ui::AXViewState* state) {
    363   state->role = ui::AX_ROLE_TOOLBAR;
    364   state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR);
    365 }
    366 
    367 ////////////////////////////////////////////////////////////////////////////////
    368 // ToolbarView, Menu::Delegate overrides:
    369 
    370 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
    371   return GetWidget()->GetAccelerator(id, accel);
    372 }
    373 
    374 ////////////////////////////////////////////////////////////////////////////////
    375 // ToolbarView, views::MenuButtonListener implementation:
    376 
    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());
    381 
    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
    390 
    391   if (keyboard::KeyboardController::GetInstance() &&
    392       keyboard::KeyboardController::GetInstance()->keyboard_visible()) {
    393     keyboard::KeyboardController::GetInstance()->HideKeyboard(
    394         keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
    395   }
    396 
    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());
    401 
    402   FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened());
    403 
    404   wrench_menu_->RunMenu(app_menu_);
    405 }
    406 
    407 ////////////////////////////////////////////////////////////////////////////////
    408 // ToolbarView, LocationBarView::Delegate implementation:
    409 
    410 WebContents* ToolbarView::GetWebContents() {
    411   return browser_->tab_strip_model()->GetActiveWebContents();
    412 }
    413 
    414 ToolbarModel* ToolbarView::GetToolbarModel() {
    415   return browser_->toolbar_model();
    416 }
    417 
    418 const ToolbarModel* ToolbarView::GetToolbarModel() const {
    419   return browser_->toolbar_model();
    420 }
    421 
    422 InstantController* ToolbarView::GetInstant() {
    423   return browser_->instant_controller() ?
    424       browser_->instant_controller()->instant() : NULL;
    425 }
    426 
    427 ContentSettingBubbleModelDelegate*
    428 ToolbarView::GetContentSettingBubbleModelDelegate() {
    429   return browser_->content_setting_bubble_model_delegate();
    430 }
    431 
    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);
    436 }
    437 
    438 views::Widget* ToolbarView::CreateViewsBubble(
    439     views::BubbleDelegateView* bubble_delegate) {
    440   return views::BubbleDelegateView::CreateBubble(bubble_delegate);
    441 }
    442 
    443 PageActionImageView* ToolbarView::CreatePageActionImageView(
    444     LocationBarView* owner, ExtensionAction* action) {
    445   return new PageActionImageView(owner, action, browser_);
    446 }
    447 
    448 ////////////////////////////////////////////////////////////////////////////////
    449 // ToolbarView, CommandObserver implementation:
    450 
    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;
    466   }
    467   if (button)
    468     button->SetEnabled(enabled);
    469 }
    470 
    471 ////////////////////////////////////////////////////////////////////////////////
    472 // ToolbarView, views::Button::ButtonListener implementation:
    473 
    474 void ToolbarView::ButtonPressed(views::Button* sender,
    475                                 const ui::Event& event) {
    476   chrome::ExecuteCommandWithDisposition(
    477       browser_, sender->tag(), ui::DispositionFromEventFlags(event.flags()));
    478 }
    479 
    480 ////////////////////////////////////////////////////////////////////////////////
    481 // ToolbarView, content::NotificationObserver implementation:
    482 
    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();
    506   }
    507 }
    508 
    509 ////////////////////////////////////////////////////////////////////////////////
    510 // ToolbarView, ui::AcceleratorProvider implementation:
    511 
    512 bool ToolbarView::GetAcceleratorForCommandId(int command_id,
    513     ui::Accelerator* accelerator) {
    514   return GetWidget()->GetAccelerator(command_id, accelerator);
    515 }
    516 
    517 ////////////////////////////////////////////////////////////////////////////////
    518 // ToolbarView, views::View overrides:
    519 
    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);
    530   }
    531   return SizeForContentSize(size);
    532 }
    533 
    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);
    543   }
    544   return SizeForContentSize(size);
    545 }
    546 
    547 void ToolbarView::Layout() {
    548   // If we have not been initialized yet just do nothing.
    549   if (back_ == NULL)
    550     return;
    551 
    552   if (!is_display_mode_normal()) {
    553     location_bar_->SetBounds(0, PopupTopSpacing(), width(),
    554                              location_bar_->GetPreferredSize().height());
    555     return;
    556   }
    557 
    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;
    564 
    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);
    580   }
    581   int next_element_x = back_->bounds().right();
    582 
    583   forward_->SetBounds(next_element_x, child_y,
    584                       forward_->GetPreferredSize().width(), child_height);
    585   next_element_x = forward_->bounds().right();
    586 
    587   reload_->SetBounds(next_element_x, child_y,
    588                      reload_->GetPreferredSize().width(), child_height);
    589   next_element_x = reload_->bounds().right();
    590 
    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);
    599   }
    600   next_element_x = home_->bounds().right() + kStandardSpacing;
    601 
    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);
    606 
    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();
    612 
    613   browser_actions_->SetBounds(
    614       next_element_x, child_y, browser_actions_width, child_height);
    615   next_element_x = browser_actions_->bounds().right();
    616 
    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();
    625 
    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);
    631 }
    632 
    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);
    641 }
    642 
    643 void ToolbarView::OnPaint(gfx::Canvas* canvas) {
    644   View::OnPaint(canvas);
    645 
    646   if (is_display_mode_normal())
    647     return;
    648 
    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);
    655 }
    656 
    657 void ToolbarView::OnThemeChanged() {
    658   LoadImages();
    659 }
    660 
    661 const char* ToolbarView::GetClassName() const {
    662   return kViewClassName;
    663 }
    664 
    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);
    670 }
    671 
    672 bool ToolbarView::IsWrenchMenuShowing() const {
    673   return wrench_menu_.get() && wrench_menu_->IsShowing();
    674 }
    675 
    676 bool ToolbarView::ShouldPaintBackground() const {
    677   return display_mode_ == DISPLAYMODE_NORMAL;
    678 }
    679 
    680 ////////////////////////////////////////////////////////////////////////////////
    681 // ToolbarView, protected:
    682 
    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;
    691   }
    692 
    693   if (!AccessiblePaneView::SetPaneFocusAndFocusDefault())
    694     return false;
    695   browser_->window()->RotatePaneFocus(true);
    696   return true;
    697 }
    698 
    699 void ToolbarView::RemovePaneFocus() {
    700   AccessiblePaneView::RemovePaneFocus();
    701   location_bar_->SetShowFocusRect(false);
    702 }
    703 
    704 ////////////////////////////////////////////////////////////////////////////////
    705 // ToolbarView, private:
    706 
    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
    715 }
    716 
    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
    725 }
    726 
    727 int ToolbarView::PopupTopSpacing() const {
    728   const int kPopupTopSpacingNonGlass = 3;
    729   return GetWidget()->ShouldWindowContentsBeTransparent() ?
    730       0 : kPopupTopSpacingNonGlass;
    731 }
    732 
    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(
    743         0,
    744         PopupTopSpacing() + (GetWidget()->ShouldWindowContentsBeTransparent() ?
    745             kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass));
    746   }
    747   return size;
    748 }
    749 
    750 void ToolbarView::LoadImages() {
    751   ui::ThemeProvider* tp = GetThemeProvider();
    752 
    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)));
    757 
    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)));
    762 
    763   reload_->LoadImages();
    764 
    765   home_->SetImage(views::Button::STATE_NORMAL,
    766                   *(tp->GetImageSkiaNamed(IDR_HOME)));
    767 }
    768 
    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
    775 }
    776 
    777 void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) {
    778   if (OutdatedUpgradeBubbleView::IsAvailable()) {
    779     OutdatedUpgradeBubbleView::ShowBubble(
    780         app_menu_, browser_, auto_update_enabled);
    781   }
    782 }
    783 
    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);
    789   }
    790   app_menu_->SetAccessibleName(accname_app);
    791 
    792   UpdateWrenchButtonSeverity();
    793   SchedulePaint();
    794 }
    795 
    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());
    800 
    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;
    807 
    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;
    814   }
    815 
    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
    822     }
    823     app_menu_->SetSeverity(WrenchIconPainter::SEVERITY_MEDIUM, true);
    824     incompatibility_badge_showing = true;
    825     return;
    826   }
    827 
    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;
    835   }
    836 
    837   app_menu_->SetSeverity(WrenchIconPainter::SEVERITY_NONE, true);
    838 }
    839 
    840 void ToolbarView::OnShowHomeButtonChanged() {
    841   Layout();
    842   SchedulePaint();
    843 }
    844 
    845 int ToolbarView::content_shadow_height() const {
    846   return browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH ?
    847       kContentShadowHeightAsh : kContentShadowHeight;
    848 }
    849