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 <algorithm>
      8 
      9 #include "base/command_line.h"
     10 #include "base/debug/trace_event.h"
     11 #include "base/i18n/number_formatting.h"
     12 #include "base/prefs/pref_service.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "chrome/app/chrome_command_ids.h"
     15 #include "chrome/browser/chrome_notification_types.h"
     16 #include "chrome/browser/command_updater.h"
     17 #include "chrome/browser/extensions/extension_commands_global_registry.h"
     18 #include "chrome/browser/extensions/extension_util.h"
     19 #include "chrome/browser/profiles/profile.h"
     20 #include "chrome/browser/themes/theme_service.h"
     21 #include "chrome/browser/ui/browser.h"
     22 #include "chrome/browser/ui/browser_command_controller.h"
     23 #include "chrome/browser/ui/browser_commands.h"
     24 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
     25 #include "chrome/browser/ui/browser_instant_controller.h"
     26 #include "chrome/browser/ui/browser_tabstrip.h"
     27 #include "chrome/browser/ui/browser_window.h"
     28 #include "chrome/browser/ui/global_error/global_error_service.h"
     29 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
     30 #include "chrome/browser/ui/omnibox/omnibox_view.h"
     31 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     32 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
     33 #include "chrome/browser/ui/view_ids.h"
     34 #include "chrome/browser/ui/views/extensions/extension_message_bubble_view.h"
     35 #include "chrome/browser/ui/views/extensions/extension_popup.h"
     36 #include "chrome/browser/ui/views/frame/browser_view.h"
     37 #include "chrome/browser/ui/views/location_bar/page_action_image_view.h"
     38 #include "chrome/browser/ui/views/location_bar/page_action_with_badge_view.h"
     39 #include "chrome/browser/ui/views/location_bar/star_view.h"
     40 #include "chrome/browser/ui/views/location_bar/translate_icon_view.h"
     41 #include "chrome/browser/ui/views/outdated_upgrade_bubble_view.h"
     42 #include "chrome/browser/ui/views/toolbar/back_button.h"
     43 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
     44 #include "chrome/browser/ui/views/toolbar/home_button.h"
     45 #include "chrome/browser/ui/views/toolbar/reload_button.h"
     46 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
     47 #include "chrome/browser/ui/views/toolbar/wrench_menu.h"
     48 #include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h"
     49 #include "chrome/common/chrome_switches.h"
     50 #include "chrome/common/pref_names.h"
     51 #include "chrome/grit/chromium_strings.h"
     52 #include "chrome/grit/generated_resources.h"
     53 #include "content/public/browser/browser_accessibility_state.h"
     54 #include "content/public/browser/notification_service.h"
     55 #include "content/public/browser/render_view_host.h"
     56 #include "content/public/browser/user_metrics.h"
     57 #include "content/public/browser/web_contents.h"
     58 #include "grit/theme_resources.h"
     59 #include "ui/accessibility/ax_view_state.h"
     60 #include "ui/aura/window.h"
     61 #include "ui/base/l10n/l10n_util.h"
     62 #include "ui/base/theme_provider.h"
     63 #include "ui/base/window_open_disposition.h"
     64 #include "ui/compositor/layer.h"
     65 #include "ui/gfx/canvas.h"
     66 #include "ui/gfx/image/canvas_image_source.h"
     67 #include "ui/keyboard/keyboard_controller.h"
     68 #include "ui/native_theme/native_theme_aura.h"
     69 #include "ui/views/controls/menu/menu_listener.h"
     70 #include "ui/views/focus/view_storage.h"
     71 #include "ui/views/view_targeter.h"
     72 #include "ui/views/widget/tooltip_manager.h"
     73 #include "ui/views/widget/widget.h"
     74 #include "ui/views/window/non_client_view.h"
     75 
     76 #if defined(OS_WIN)
     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 #if !defined(OS_CHROMEOS)
    107 bool HasAshShell() {
    108 #if defined(USE_ASH)
    109   return ash::Shell::HasInstance();
    110 #else
    111   return false;
    112 #endif  // USE_ASH
    113 }
    114 #endif  // OS_CHROMEOS
    115 
    116 }  // namespace
    117 
    118 // static
    119 const char ToolbarView::kViewClassName[] = "ToolbarView";
    120 
    121 ////////////////////////////////////////////////////////////////////////////////
    122 // ToolbarView, public:
    123 
    124 ToolbarView::ToolbarView(Browser* browser)
    125     : back_(NULL),
    126       forward_(NULL),
    127       reload_(NULL),
    128       home_(NULL),
    129       location_bar_(NULL),
    130       browser_actions_(NULL),
    131       app_menu_(NULL),
    132       browser_(browser),
    133       badge_controller_(browser->profile(), this),
    134       extension_message_bubble_factory_(
    135           new extensions::ExtensionMessageBubbleFactory(browser->profile(),
    136                                                         this)) {
    137   set_id(VIEW_ID_TOOLBAR);
    138 
    139   SetEventTargeter(
    140       scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
    141 
    142   chrome::AddCommandObserver(browser_, IDC_BACK, this);
    143   chrome::AddCommandObserver(browser_, IDC_FORWARD, this);
    144   chrome::AddCommandObserver(browser_, IDC_RELOAD, this);
    145   chrome::AddCommandObserver(browser_, IDC_HOME, this);
    146   chrome::AddCommandObserver(browser_, IDC_LOAD_NEW_TAB_PAGE, this);
    147 
    148   display_mode_ = DISPLAYMODE_LOCATION;
    149   if (browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ||
    150       (browser->is_app() && extensions::util::IsStreamlinedHostedAppsEnabled()))
    151     display_mode_ = DISPLAYMODE_NORMAL;
    152 
    153   if (OutdatedUpgradeBubbleView::IsAvailable()) {
    154     registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL,
    155                    content::NotificationService::AllSources());
    156     registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU,
    157                    content::NotificationService::AllSources());
    158   }
    159 #if defined(OS_WIN)
    160   registrar_.Add(this, chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED,
    161                  content::NotificationService::AllSources());
    162 #endif
    163 }
    164 
    165 ToolbarView::~ToolbarView() {
    166   // NOTE: Don't remove the command observers here.  This object gets destroyed
    167   // after the Browser (which owns the CommandUpdater), so the CommandUpdater is
    168   // already gone.
    169 }
    170 
    171 void ToolbarView::Init() {
    172   GetWidget()->AddObserver(this);
    173 
    174   back_ = new BackButton(this, new BackForwardMenuModel(
    175       browser_, BackForwardMenuModel::BACKWARD_MENU));
    176   back_->set_triggerable_event_flags(
    177       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
    178   back_->set_tag(IDC_BACK);
    179   back_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK));
    180   back_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
    181   back_->set_id(VIEW_ID_BACK_BUTTON);
    182   back_->Init();
    183 
    184   forward_ = new ToolbarButton(this, new BackForwardMenuModel(
    185       browser_, BackForwardMenuModel::FORWARD_MENU));
    186   forward_->set_triggerable_event_flags(
    187       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
    188   forward_->set_tag(IDC_FORWARD);
    189   forward_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD));
    190   forward_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD));
    191   forward_->set_id(VIEW_ID_FORWARD_BUTTON);
    192   forward_->Init();
    193 
    194   location_bar_ = new LocationBarView(
    195       browser_, browser_->profile(),
    196       browser_->command_controller()->command_updater(), this,
    197       display_mode_ == DISPLAYMODE_LOCATION);
    198 
    199   reload_ = new ReloadButton(browser_->command_controller()->command_updater());
    200   reload_->set_triggerable_event_flags(
    201       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
    202   reload_->set_tag(IDC_RELOAD);
    203   reload_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD));
    204   reload_->set_id(VIEW_ID_RELOAD_BUTTON);
    205   reload_->Init();
    206 
    207   home_ = new HomeButton(this, browser_);
    208   home_->set_triggerable_event_flags(
    209       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
    210   home_->set_tag(IDC_HOME);
    211   home_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_HOME));
    212   home_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_HOME));
    213   home_->set_id(VIEW_ID_HOME_BUTTON);
    214   home_->Init();
    215 
    216   browser_actions_ = new BrowserActionsContainer(
    217       browser_,
    218       this,   // Owner.
    219       NULL);  // No master container for this one (it is master).
    220 
    221   app_menu_ = new WrenchToolbarButton(this);
    222   app_menu_->EnableCanvasFlippingForRTLUI(true);
    223   app_menu_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_APP));
    224   app_menu_->SetTooltipText(l10n_util::GetStringUTF16(IDS_APPMENU_TOOLTIP));
    225   app_menu_->set_id(VIEW_ID_APP_MENU);
    226 
    227   // Always add children in order from left to right, for accessibility.
    228   AddChildView(back_);
    229   AddChildView(forward_);
    230   AddChildView(reload_);
    231   AddChildView(home_);
    232   AddChildView(location_bar_);
    233   AddChildView(browser_actions_);
    234   AddChildView(app_menu_);
    235 
    236   LoadImages();
    237 
    238   // Start global error services now so we badge the menu correctly in non-Ash.
    239 #if !defined(OS_CHROMEOS)
    240   if (!HasAshShell()) {
    241     SigninGlobalErrorFactory::GetForProfile(browser_->profile());
    242 #if !defined(OS_ANDROID)
    243     SyncGlobalErrorFactory::GetForProfile(browser_->profile());
    244 #endif
    245   }
    246 #endif  // OS_CHROMEOS
    247 
    248   // Add any necessary badges to the menu item based on the system state.
    249   // Do this after |app_menu_| has been added as a bubble may be shown that
    250   // needs the widget (widget found by way of app_menu_->GetWidget()).
    251   badge_controller_.UpdateDelegate();
    252 
    253   location_bar_->Init();
    254 
    255   show_home_button_.Init(prefs::kShowHomeButton,
    256                          browser_->profile()->GetPrefs(),
    257                          base::Bind(&ToolbarView::OnShowHomeButtonChanged,
    258                                     base::Unretained(this)));
    259 
    260   browser_actions_->Init();
    261 
    262   // Accessibility specific tooltip text.
    263   if (content::BrowserAccessibilityState::GetInstance()->
    264           IsAccessibleBrowser()) {
    265     back_->SetTooltipText(
    266         l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK));
    267     forward_->SetTooltipText(
    268         l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD));
    269   }
    270 }
    271 
    272 void ToolbarView::OnWidgetVisibilityChanged(views::Widget* widget,
    273                                             bool visible) {
    274   if (visible) {
    275     // Safe to call multiple times; the bubble will only appear once.
    276     extension_message_bubble_factory_->MaybeShow(app_menu_);
    277   }
    278 }
    279 
    280 void ToolbarView::OnWidgetActivationChanged(views::Widget* widget,
    281                                             bool active) {
    282   extensions::ExtensionCommandsGlobalRegistry* registry =
    283       extensions::ExtensionCommandsGlobalRegistry::Get(browser_->profile());
    284   if (registry) {
    285     if (active) {
    286       registry->set_registry_for_active_window(
    287           browser_actions_->extension_keybinding_registry());
    288     } else if (registry->registry_for_active_window() ==
    289                browser_actions_->extension_keybinding_registry()) {
    290       registry->set_registry_for_active_window(NULL);
    291     }
    292   }
    293 }
    294 
    295 void ToolbarView::Update(WebContents* tab) {
    296   if (location_bar_)
    297     location_bar_->Update(tab);
    298   if (browser_actions_)
    299     browser_actions_->RefreshBrowserActionViews();
    300   if (reload_)
    301     reload_->set_menu_enabled(chrome::IsDebuggerAttachedToCurrentTab(browser_));
    302 }
    303 
    304 void ToolbarView::SetPaneFocusAndFocusAppMenu() {
    305   SetPaneFocus(app_menu_);
    306 }
    307 
    308 bool ToolbarView::IsAppMenuFocused() {
    309   return app_menu_->HasFocus();
    310 }
    311 
    312 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
    313   menu_listeners_.AddObserver(listener);
    314 }
    315 
    316 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
    317   menu_listeners_.RemoveObserver(listener);
    318 }
    319 
    320 views::View* ToolbarView::GetBookmarkBubbleAnchor() {
    321   views::View* star_view = location_bar()->star_view();
    322   return (star_view && star_view->visible()) ? star_view : app_menu_;
    323 }
    324 
    325 views::View* ToolbarView::GetTranslateBubbleAnchor() {
    326   views::View* translate_icon_view = location_bar()->translate_icon_view();
    327   return (translate_icon_view && translate_icon_view->visible()) ?
    328       translate_icon_view : app_menu_;
    329 }
    330 
    331 void ToolbarView::ExecuteExtensionCommand(
    332     const extensions::Extension* extension,
    333     const extensions::Command& command) {
    334   browser_actions_->ExecuteExtensionCommand(extension, command);
    335 }
    336 
    337 void ToolbarView::ShowAppMenu(bool for_drop) {
    338   if (wrench_menu_.get() && wrench_menu_->IsShowing())
    339     return;
    340 
    341   if (keyboard::KeyboardController::GetInstance() &&
    342       keyboard::KeyboardController::GetInstance()->keyboard_visible()) {
    343     keyboard::KeyboardController::GetInstance()->HideKeyboard(
    344         keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
    345   }
    346 
    347   wrench_menu_.reset(
    348       new WrenchMenu(browser_, for_drop ? WrenchMenu::FOR_DROP : 0));
    349   wrench_menu_model_.reset(new WrenchMenuModel(this, browser_));
    350   wrench_menu_->Init(wrench_menu_model_.get());
    351 
    352   FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened());
    353 
    354   wrench_menu_->RunMenu(app_menu_);
    355 }
    356 
    357 views::MenuButton* ToolbarView::app_menu() const {
    358   return app_menu_;
    359 }
    360 
    361 ////////////////////////////////////////////////////////////////////////////////
    362 // ToolbarView, AccessiblePaneView overrides:
    363 
    364 bool ToolbarView::SetPaneFocus(views::View* initial_focus) {
    365   if (!AccessiblePaneView::SetPaneFocus(initial_focus))
    366     return false;
    367 
    368   location_bar_->SetShowFocusRect(true);
    369   return true;
    370 }
    371 
    372 void ToolbarView::GetAccessibleState(ui::AXViewState* state) {
    373   state->role = ui::AX_ROLE_TOOLBAR;
    374   state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR);
    375 }
    376 
    377 ////////////////////////////////////////////////////////////////////////////////
    378 // ToolbarView, Menu::Delegate overrides:
    379 
    380 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
    381   return GetWidget()->GetAccelerator(id, accel);
    382 }
    383 
    384 ////////////////////////////////////////////////////////////////////////////////
    385 // ToolbarView, views::MenuButtonListener implementation:
    386 
    387 void ToolbarView::OnMenuButtonClicked(views::View* source,
    388                                       const gfx::Point& point) {
    389   TRACE_EVENT0("views", "ToolbarView::OnMenuButtonClicked");
    390   DCHECK_EQ(VIEW_ID_APP_MENU, source->id());
    391   ShowAppMenu(false);  // Not for drop.
    392 }
    393 
    394 ////////////////////////////////////////////////////////////////////////////////
    395 // ToolbarView, LocationBarView::Delegate implementation:
    396 
    397 WebContents* ToolbarView::GetWebContents() {
    398   return browser_->tab_strip_model()->GetActiveWebContents();
    399 }
    400 
    401 ToolbarModel* ToolbarView::GetToolbarModel() {
    402   return browser_->toolbar_model();
    403 }
    404 
    405 const ToolbarModel* ToolbarView::GetToolbarModel() const {
    406   return browser_->toolbar_model();
    407 }
    408 
    409 InstantController* ToolbarView::GetInstant() {
    410   return browser_->instant_controller() ?
    411       browser_->instant_controller()->instant() : NULL;
    412 }
    413 
    414 ContentSettingBubbleModelDelegate*
    415 ToolbarView::GetContentSettingBubbleModelDelegate() {
    416   return browser_->content_setting_bubble_model_delegate();
    417 }
    418 
    419 void ToolbarView::ShowWebsiteSettings(content::WebContents* web_contents,
    420                                       const GURL& url,
    421                                       const content::SSLStatus& ssl) {
    422   chrome::ShowWebsiteSettings(browser_, web_contents, url, ssl);
    423 }
    424 
    425 views::Widget* ToolbarView::CreateViewsBubble(
    426     views::BubbleDelegateView* bubble_delegate) {
    427   return views::BubbleDelegateView::CreateBubble(bubble_delegate);
    428 }
    429 
    430 PageActionImageView* ToolbarView::CreatePageActionImageView(
    431     LocationBarView* owner, ExtensionAction* action) {
    432   return new PageActionImageView(owner, action, browser_);
    433 }
    434 
    435 ////////////////////////////////////////////////////////////////////////////////
    436 // ToolbarView, CommandObserver implementation:
    437 
    438 void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
    439   views::Button* button = NULL;
    440   switch (id) {
    441     case IDC_BACK:
    442       button = back_;
    443       break;
    444     case IDC_FORWARD:
    445       button = forward_;
    446       break;
    447     case IDC_RELOAD:
    448       button = reload_;
    449       break;
    450     case IDC_HOME:
    451       button = home_;
    452       break;
    453   }
    454   if (button)
    455     button->SetEnabled(enabled);
    456 }
    457 
    458 ////////////////////////////////////////////////////////////////////////////////
    459 // ToolbarView, views::Button::ButtonListener implementation:
    460 
    461 void ToolbarView::ButtonPressed(views::Button* sender,
    462                                 const ui::Event& event) {
    463   chrome::ExecuteCommandWithDisposition(
    464       browser_, sender->tag(), ui::DispositionFromEventFlags(event.flags()));
    465 }
    466 
    467 ////////////////////////////////////////////////////////////////////////////////
    468 // ToolbarView, content::NotificationObserver implementation:
    469 
    470 void ToolbarView::Observe(int type,
    471                           const content::NotificationSource& source,
    472                           const content::NotificationDetails& details) {
    473   switch (type) {
    474     case chrome::NOTIFICATION_OUTDATED_INSTALL:
    475       ShowOutdatedInstallNotification(true);
    476       break;
    477     case chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU:
    478       ShowOutdatedInstallNotification(false);
    479       break;
    480 #if defined(OS_WIN)
    481     case chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED:
    482       ShowCriticalNotification();
    483       break;
    484 #endif
    485     default:
    486       NOTREACHED();
    487   }
    488 }
    489 
    490 ////////////////////////////////////////////////////////////////////////////////
    491 // ToolbarView, ui::AcceleratorProvider implementation:
    492 
    493 bool ToolbarView::GetAcceleratorForCommandId(int command_id,
    494     ui::Accelerator* accelerator) {
    495   return GetWidget()->GetAccelerator(command_id, accelerator);
    496 }
    497 
    498 ////////////////////////////////////////////////////////////////////////////////
    499 // ToolbarView, views::View overrides:
    500 
    501 gfx::Size ToolbarView::GetPreferredSize() const {
    502   gfx::Size size(location_bar_->GetPreferredSize());
    503   if (is_display_mode_normal()) {
    504     int content_width = kLeftEdgeSpacing + back_->GetPreferredSize().width() +
    505         forward_->GetPreferredSize().width() +
    506         reload_->GetPreferredSize().width() +
    507         (show_home_button_.GetValue() ? home_->GetPreferredSize().width() : 0) +
    508         kStandardSpacing + browser_actions_->GetPreferredSize().width() +
    509         app_menu_->GetPreferredSize().width() + kRightEdgeSpacing;
    510     size.Enlarge(content_width, 0);
    511   }
    512   return SizeForContentSize(size);
    513 }
    514 
    515 gfx::Size ToolbarView::GetMinimumSize() const {
    516   gfx::Size size(location_bar_->GetMinimumSize());
    517   if (is_display_mode_normal()) {
    518     int content_width = kLeftEdgeSpacing + back_->GetMinimumSize().width() +
    519         forward_->GetMinimumSize().width() + reload_->GetMinimumSize().width() +
    520         (show_home_button_.GetValue() ? home_->GetMinimumSize().width() : 0) +
    521         kStandardSpacing + browser_actions_->GetMinimumSize().width() +
    522         app_menu_->GetMinimumSize().width() + kRightEdgeSpacing;
    523     size.Enlarge(content_width, 0);
    524   }
    525   return SizeForContentSize(size);
    526 }
    527 
    528 void ToolbarView::Layout() {
    529   // If we have not been initialized yet just do nothing.
    530   if (back_ == NULL)
    531     return;
    532 
    533   if (!is_display_mode_normal()) {
    534     location_bar_->SetBounds(0, PopupTopSpacing(), width(),
    535                              location_bar_->GetPreferredSize().height());
    536     return;
    537   }
    538 
    539   // We assume all child elements except the location bar are the same height.
    540   // Set child_y such that buttons appear vertically centered. We put any excess
    541   // padding above the buttons.
    542   int child_height =
    543       std::min(back_->GetPreferredSize().height(), height());
    544   int child_y = (height() - child_height + 1) / 2;
    545 
    546   // If the window is maximized, we extend the back button to the left so that
    547   // clicking on the left-most pixel will activate the back button.
    548   // TODO(abarth):  If the window becomes maximized but is not resized,
    549   //                then Layout() might not be called and the back button
    550   //                will be slightly the wrong size.  We should force a
    551   //                Layout() in this case.
    552   //                http://crbug.com/5540
    553   bool maximized = browser_->window() && browser_->window()->IsMaximized();
    554   int back_width = back_->GetPreferredSize().width();
    555   if (maximized) {
    556     back_->SetBounds(0, child_y, back_width + kLeftEdgeSpacing, child_height);
    557     back_->SetLeadingMargin(kLeftEdgeSpacing);
    558   } else {
    559     back_->SetBounds(kLeftEdgeSpacing, child_y, back_width, child_height);
    560     back_->SetLeadingMargin(0);
    561   }
    562   int next_element_x = back_->bounds().right();
    563 
    564   forward_->SetBounds(next_element_x, child_y,
    565                       forward_->GetPreferredSize().width(), child_height);
    566   next_element_x = forward_->bounds().right();
    567 
    568   reload_->SetBounds(next_element_x, child_y,
    569                      reload_->GetPreferredSize().width(), child_height);
    570   next_element_x = reload_->bounds().right();
    571 
    572   if (show_home_button_.GetValue() ||
    573       (browser_->is_app() &&
    574        extensions::util::IsStreamlinedHostedAppsEnabled())) {
    575     home_->SetVisible(true);
    576     home_->SetBounds(next_element_x, child_y,
    577                      home_->GetPreferredSize().width(), child_height);
    578   } else {
    579     home_->SetVisible(false);
    580     home_->SetBounds(next_element_x, child_y, 0, child_height);
    581   }
    582   next_element_x = home_->bounds().right() + kStandardSpacing;
    583 
    584   int browser_actions_width = browser_actions_->GetPreferredSize().width();
    585   int app_menu_width = app_menu_->GetPreferredSize().width();
    586   int available_width = std::max(0, width() - kRightEdgeSpacing -
    587       app_menu_width - browser_actions_width - next_element_x);
    588 
    589   int location_height = location_bar_->GetPreferredSize().height();
    590   int location_y = (height() - location_height + 1) / 2;
    591   location_bar_->SetBounds(next_element_x, location_y,
    592                            std::max(available_width, 0), location_height);
    593   next_element_x = location_bar_->bounds().right();
    594 
    595   browser_actions_->SetBounds(
    596       next_element_x, child_y, browser_actions_width, child_height);
    597   next_element_x = browser_actions_->bounds().right();
    598 
    599   // The browser actions need to do a layout explicitly, because when an
    600   // extension is loaded/unloaded/changed, BrowserActionContainer removes and
    601   // re-adds everything, regardless of whether it has a page action. For a
    602   // page action, browser action bounds do not change, as a result of which
    603   // SetBounds does not do a layout at all.
    604   // TODO(sidchat): Rework the above behavior so that explicit layout is not
    605   //                required.
    606   browser_actions_->Layout();
    607 
    608   // Extend the app menu to the screen's right edge in maximized mode just like
    609   // we extend the back button to the left edge.
    610   if (maximized)
    611     app_menu_width += kRightEdgeSpacing;
    612   app_menu_->SetBounds(next_element_x, child_y, app_menu_width, child_height);
    613 }
    614 
    615 void ToolbarView::OnPaint(gfx::Canvas* canvas) {
    616   View::OnPaint(canvas);
    617 
    618   if (is_display_mode_normal())
    619     return;
    620 
    621   // For glass, we need to draw a black line below the location bar to separate
    622   // it from the content area.  For non-glass, the NonClientView draws the
    623   // toolbar background below the location bar for us.
    624   // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()!
    625   if (GetWidget()->ShouldWindowContentsBeTransparent())
    626     canvas->FillRect(gfx::Rect(0, height() - 1, width(), 1), SK_ColorBLACK);
    627 }
    628 
    629 void ToolbarView::OnThemeChanged() {
    630   LoadImages();
    631 }
    632 
    633 const char* ToolbarView::GetClassName() const {
    634   return kViewClassName;
    635 }
    636 
    637 bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) {
    638   const views::View* focused_view = focus_manager()->GetFocusedView();
    639   if (focused_view && (focused_view->id() == VIEW_ID_OMNIBOX))
    640     return false;  // Let the omnibox handle all accelerator events.
    641   return AccessiblePaneView::AcceleratorPressed(accelerator);
    642 }
    643 
    644 bool ToolbarView::IsWrenchMenuShowing() const {
    645   return wrench_menu_.get() && wrench_menu_->IsShowing();
    646 }
    647 
    648 bool ToolbarView::ShouldPaintBackground() const {
    649   return display_mode_ == DISPLAYMODE_NORMAL;
    650 }
    651 
    652 ////////////////////////////////////////////////////////////////////////////////
    653 // ToolbarView, protected:
    654 
    655 // Override this so that when the user presses F6 to rotate toolbar panes,
    656 // the location bar gets focus, not the first control in the toolbar - and
    657 // also so that it selects all content in the location bar.
    658 bool ToolbarView::SetPaneFocusAndFocusDefault() {
    659   if (!location_bar_->HasFocus()) {
    660     SetPaneFocus(location_bar_);
    661     location_bar_->FocusLocation(true);
    662     return true;
    663   }
    664 
    665   if (!AccessiblePaneView::SetPaneFocusAndFocusDefault())
    666     return false;
    667   browser_->window()->RotatePaneFocus(true);
    668   return true;
    669 }
    670 
    671 void ToolbarView::RemovePaneFocus() {
    672   AccessiblePaneView::RemovePaneFocus();
    673   location_bar_->SetShowFocusRect(false);
    674 }
    675 
    676 ////////////////////////////////////////////////////////////////////////////////
    677 // ToolbarView, private:
    678 
    679 // views::ViewTargeterDelegate:
    680 bool ToolbarView::DoesIntersectRect(const views::View* target,
    681                                     const gfx::Rect& rect) const {
    682   CHECK_EQ(target, this);
    683 
    684   // Fall through to the tab strip above us if none of |rect| intersects
    685   // with this view (intersection with the top shadow edge does not
    686   // count as intersection with this view).
    687   if (rect.bottom() < content_shadow_height())
    688     return false;
    689   // Otherwise let our superclass take care of it.
    690   return ViewTargeterDelegate::DoesIntersectRect(this, rect);
    691 }
    692 
    693 void ToolbarView::UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,
    694                                       WrenchIconPainter::Severity severity,
    695                                       bool animate)  {
    696   // Showing the bubble requires |app_menu_| to be in a widget. See comment
    697   // in ConflictingModuleView for details.
    698   DCHECK(app_menu_->GetWidget());
    699 
    700   base::string16 accname_app = l10n_util::GetStringUTF16(IDS_ACCNAME_APP);
    701   if (type == WrenchMenuBadgeController::BADGE_TYPE_UPGRADE_NOTIFICATION) {
    702     accname_app = l10n_util::GetStringFUTF16(
    703         IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app);
    704   }
    705   app_menu_->SetAccessibleName(accname_app);
    706   app_menu_->SetSeverity(severity, animate);
    707 
    708   // Keep track of whether we were showing the badge before, so we don't send
    709   // multiple UMA events for example when multiple Chrome windows are open.
    710   static bool incompatibility_badge_showing = false;
    711   // Save the old value before resetting it.
    712   bool was_showing = incompatibility_badge_showing;
    713   incompatibility_badge_showing = false;
    714 
    715   if (type == WrenchMenuBadgeController::BADGE_TYPE_INCOMPATIBILITY_WARNING) {
    716     if (!was_showing) {
    717       content::RecordAction(UserMetricsAction("ConflictBadge"));
    718 #if defined(OS_WIN)
    719       ConflictingModuleView::MaybeShow(browser_, app_menu_);
    720 #endif
    721     }
    722     incompatibility_badge_showing = true;
    723     return;
    724   }
    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::OnShowHomeButtonChanged() {
    785   Layout();
    786   SchedulePaint();
    787 }
    788 
    789 int ToolbarView::content_shadow_height() const {
    790   return browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH ?
    791       kContentShadowHeightAsh : kContentShadowHeight;
    792 }
    793