Home | History | Annotate | Download | only in views
      1 // Copyright 2012 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_view.h"
      6 
      7 #include "base/debug/trace_event.h"
      8 #include "base/i18n/number_formatting.h"
      9 #include "base/prefs/pref_service.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome/app/chrome_command_ids.h"
     12 #include "chrome/browser/chrome_notification_types.h"
     13 #include "chrome/browser/command_updater.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/themes/theme_service.h"
     16 #include "chrome/browser/ui/browser.h"
     17 #include "chrome/browser/ui/browser_command_controller.h"
     18 #include "chrome/browser/ui/browser_commands.h"
     19 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
     20 #include "chrome/browser/ui/browser_instant_controller.h"
     21 #include "chrome/browser/ui/browser_tabstrip.h"
     22 #include "chrome/browser/ui/browser_window.h"
     23 #include "chrome/browser/ui/global_error/global_error_service.h"
     24 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
     25 #include "chrome/browser/ui/omnibox/omnibox_view.h"
     26 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     27 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
     28 #include "chrome/browser/ui/view_ids.h"
     29 #include "chrome/browser/ui/views/browser_actions_container.h"
     30 #include "chrome/browser/ui/views/frame/browser_view.h"
     31 #include "chrome/browser/ui/views/home_button.h"
     32 #include "chrome/browser/ui/views/location_bar/page_action_image_view.h"
     33 #include "chrome/browser/ui/views/location_bar/star_view.h"
     34 #include "chrome/browser/ui/views/outdated_upgrade_bubble_view.h"
     35 #include "chrome/browser/ui/views/wrench_menu.h"
     36 #include "chrome/browser/ui/views/wrench_toolbar_button.h"
     37 #include "chrome/browser/upgrade_detector.h"
     38 #include "chrome/common/pref_names.h"
     39 #include "content/public/browser/browser_accessibility_state.h"
     40 #include "content/public/browser/notification_service.h"
     41 #include "content/public/browser/render_view_host.h"
     42 #include "content/public/browser/user_metrics.h"
     43 #include "content/public/browser/web_contents.h"
     44 #include "content/public/browser/web_contents_view.h"
     45 #include "grit/chromium_strings.h"
     46 #include "grit/generated_resources.h"
     47 #include "grit/theme_resources.h"
     48 #include "ui/base/accessibility/accessible_view_state.h"
     49 #include "ui/base/l10n/l10n_util.h"
     50 #include "ui/base/layout.h"
     51 #include "ui/base/theme_provider.h"
     52 #include "ui/base/window_open_disposition.h"
     53 #include "ui/gfx/canvas.h"
     54 #include "ui/gfx/image/canvas_image_source.h"
     55 #include "ui/views/controls/button/button_dropdown.h"
     56 #include "ui/views/controls/menu/menu_listener.h"
     57 #include "ui/views/focus/view_storage.h"
     58 #include "ui/views/widget/tooltip_manager.h"
     59 #include "ui/views/widget/widget.h"
     60 #include "ui/views/window/non_client_view.h"
     61 
     62 #if defined(OS_WIN)
     63 #include "base/win/windows_version.h"
     64 #include "chrome/browser/enumerate_modules_model_win.h"
     65 #include "chrome/browser/ui/views/conflicting_module_view_win.h"
     66 #include "chrome/browser/ui/views/critical_notification_bubble_view.h"
     67 #if !defined(USE_AURA)
     68 #include "chrome/browser/ui/views/app_menu_button_win.h"
     69 #endif
     70 #endif
     71 
     72 #if defined(USE_AURA)
     73 #include "ui/aura/window.h"
     74 #include "ui/compositor/layer.h"
     75 #include "ui/native_theme/native_theme_aura.h"
     76 #endif
     77 
     78 using content::UserMetricsAction;
     79 using content::WebContents;
     80 
     81 namespace {
     82 
     83 // The edge graphics have some built-in spacing/shadowing, so we have to adjust
     84 // our spacing to make it match.
     85 const int kLeftEdgeSpacing = 3;
     86 const int kRightEdgeSpacing = 2;
     87 
     88 // Ash doesn't use a rounded content area and its top edge has an extra shadow.
     89 const int kContentShadowHeightAsh = 2;
     90 
     91 // Non-ash uses a rounded content area with no shadow in the assets.
     92 const int kContentShadowHeight = 0;
     93 
     94 // Top margin for the wrench menu badges (badge is placed in the upper right
     95 // corner of the wrench menu).
     96 const int kBadgeTopMargin = 2;
     97 
     98 int GetButtonSpacing() {
     99   return (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) ?
    100       ToolbarView::kStandardSpacing : 0;
    101 }
    102 
    103 }  // namespace
    104 
    105 // static
    106 const char ToolbarView::kViewClassName[] = "ToolbarView";
    107 
    108 // The space between items is 3 px in general.
    109 const int ToolbarView::kStandardSpacing = 3;
    110 // The top of the toolbar has an edge we have to skip over in addition to the
    111 // above spacing.
    112 const int ToolbarView::kVertSpacing = 5;
    113 
    114 ////////////////////////////////////////////////////////////////////////////////
    115 // ToolbarView, public:
    116 
    117 ToolbarView::ToolbarView(Browser* browser)
    118     : model_(browser->toolbar_model()),
    119       back_(NULL),
    120       forward_(NULL),
    121       reload_(NULL),
    122       home_(NULL),
    123       location_bar_(NULL),
    124       browser_actions_(NULL),
    125       app_menu_(NULL),
    126       browser_(browser) {
    127   set_id(VIEW_ID_TOOLBAR);
    128 
    129   chrome::AddCommandObserver(browser_, IDC_BACK, this);
    130   chrome::AddCommandObserver(browser_, IDC_FORWARD, this);
    131   chrome::AddCommandObserver(browser_, IDC_RELOAD, this);
    132   chrome::AddCommandObserver(browser_, IDC_HOME, this);
    133   chrome::AddCommandObserver(browser_, IDC_LOAD_NEW_TAB_PAGE, this);
    134 
    135   display_mode_ = browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ?
    136       DISPLAYMODE_NORMAL : DISPLAYMODE_LOCATION;
    137 
    138   registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
    139                  content::NotificationService::AllSources());
    140   if (OutdatedUpgradeBubbleView::IsAvailable()) {
    141     registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL,
    142                    content::NotificationService::AllSources());
    143   }
    144 #if defined(OS_WIN)
    145   registrar_.Add(this, chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED,
    146                  content::NotificationService::AllSources());
    147   if (base::win::GetVersion() == base::win::VERSION_XP) {
    148     registrar_.Add(this, chrome::NOTIFICATION_MODULE_LIST_ENUMERATED,
    149                    content::NotificationService::AllSources());
    150   }
    151 #endif
    152   registrar_.Add(this,
    153                  chrome::NOTIFICATION_MODULE_INCOMPATIBILITY_BADGE_CHANGE,
    154                  content::NotificationService::AllSources());
    155   registrar_.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
    156                  content::Source<Profile>(browser_->profile()));
    157 }
    158 
    159 ToolbarView::~ToolbarView() {
    160   // NOTE: Don't remove the command observers here.  This object gets destroyed
    161   // after the Browser (which owns the CommandUpdater), so the CommandUpdater is
    162   // already gone.
    163 }
    164 
    165 void ToolbarView::Init() {
    166   back_ = new views::ButtonDropDown(this, new BackForwardMenuModel(
    167       browser_, BackForwardMenuModel::BACKWARD_MENU));
    168   back_->set_triggerable_event_flags(
    169       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
    170   back_->set_tag(IDC_BACK);
    171   back_->SetImageAlignment(views::ImageButton::ALIGN_RIGHT,
    172                            views::ImageButton::ALIGN_TOP);
    173   back_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK));
    174   back_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
    175   back_->set_id(VIEW_ID_BACK_BUTTON);
    176 
    177   forward_ = new views::ButtonDropDown(
    178       this,
    179       new BackForwardMenuModel(browser_, BackForwardMenuModel::FORWARD_MENU));
    180   forward_->set_triggerable_event_flags(
    181       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
    182   forward_->set_tag(IDC_FORWARD);
    183   forward_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD));
    184   forward_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD));
    185   forward_->set_id(VIEW_ID_FORWARD_BUTTON);
    186 
    187   // Have to create this before |reload_| as |reload_|'s constructor needs it.
    188   location_bar_ = new LocationBarView(
    189       browser_, browser_->profile(),
    190       browser_->command_controller()->command_updater(), model_, this,
    191       display_mode_ == DISPLAYMODE_LOCATION);
    192 
    193   reload_ = new ReloadButton(location_bar_,
    194                              browser_->command_controller()->command_updater());
    195   reload_->set_triggerable_event_flags(
    196       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
    197   reload_->set_tag(IDC_RELOAD);
    198   reload_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD));
    199   reload_->set_id(VIEW_ID_RELOAD_BUTTON);
    200 
    201   home_ = new HomeImageButton(this, browser_);
    202   home_->set_triggerable_event_flags(
    203       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
    204   home_->set_tag(IDC_HOME);
    205   home_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_HOME));
    206   home_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_HOME));
    207   home_->set_id(VIEW_ID_HOME_BUTTON);
    208 
    209   browser_actions_ = new BrowserActionsContainer(browser_, this);
    210 
    211 #if defined(OS_WIN) && !defined(USE_AURA)
    212   app_menu_ = new AppMenuButtonWin(this);
    213 #else
    214   app_menu_ = new WrenchToolbarButton(this);
    215 #endif
    216   app_menu_->set_border(NULL);
    217   app_menu_->EnableCanvasFlippingForRTLUI(true);
    218   app_menu_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_APP));
    219   app_menu_->SetTooltipText(l10n_util::GetStringUTF16(IDS_APPMENU_TOOLTIP));
    220   app_menu_->set_id(VIEW_ID_APP_MENU);
    221 
    222   // Add any necessary badges to the menu item based on the system state.
    223   UpdateAppMenuState();
    224   LoadImages();
    225 
    226   // Always add children in order from left to right, for accessibility.
    227   AddChildView(back_);
    228   AddChildView(forward_);
    229   AddChildView(reload_);
    230   AddChildView(home_);
    231   AddChildView(location_bar_);
    232   AddChildView(browser_actions_);
    233   AddChildView(app_menu_);
    234 
    235   location_bar_->Init();
    236   show_home_button_.Init(prefs::kShowHomeButton,
    237                          browser_->profile()->GetPrefs(),
    238                          base::Bind(&ToolbarView::OnShowHomeButtonChanged,
    239                                     base::Unretained(this)));
    240 
    241   browser_actions_->Init();
    242 
    243   // Accessibility specific tooltip text.
    244   if (content::BrowserAccessibilityState::GetInstance()->
    245           IsAccessibleBrowser()) {
    246     back_->SetTooltipText(
    247         l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK));
    248     forward_->SetTooltipText(
    249         l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD));
    250   }
    251 }
    252 
    253 void ToolbarView::Update(WebContents* tab, bool should_restore_state) {
    254   if (location_bar_)
    255     location_bar_->Update(should_restore_state ? tab : NULL);
    256 
    257   if (browser_actions_)
    258     browser_actions_->RefreshBrowserActionViews();
    259 
    260   if (reload_)
    261     reload_->set_menu_enabled(chrome::IsDebuggerAttachedToCurrentTab(browser_));
    262 }
    263 
    264 void ToolbarView::SetPaneFocusAndFocusAppMenu() {
    265   SetPaneFocus(app_menu_);
    266 }
    267 
    268 bool ToolbarView::IsAppMenuFocused() {
    269   return app_menu_->HasFocus();
    270 }
    271 
    272 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
    273   menu_listeners_.AddObserver(listener);
    274 }
    275 
    276 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
    277   menu_listeners_.RemoveObserver(listener);
    278 }
    279 
    280 views::View* ToolbarView::GetBookmarkBubbleAnchor() {
    281   views::View* star_view = location_bar()->star_view();
    282   if (star_view && star_view->visible())
    283     return star_view;
    284   return app_menu_;
    285 }
    286 
    287 views::MenuButton* ToolbarView::app_menu() const {
    288   return app_menu_;
    289 }
    290 
    291 ////////////////////////////////////////////////////////////////////////////////
    292 // ToolbarView, AccessiblePaneView overrides:
    293 
    294 bool ToolbarView::SetPaneFocus(views::View* initial_focus) {
    295   if (!AccessiblePaneView::SetPaneFocus(initial_focus))
    296     return false;
    297 
    298   location_bar_->SetShowFocusRect(true);
    299   return true;
    300 }
    301 
    302 void ToolbarView::GetAccessibleState(ui::AccessibleViewState* state) {
    303   state->role = ui::AccessibilityTypes::ROLE_TOOLBAR;
    304   state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR);
    305 }
    306 
    307 ////////////////////////////////////////////////////////////////////////////////
    308 // ToolbarView, Menu::Delegate overrides:
    309 
    310 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
    311   return GetWidget()->GetAccelerator(id, accel);
    312 }
    313 
    314 ////////////////////////////////////////////////////////////////////////////////
    315 // ToolbarView, views::MenuButtonListener implementation:
    316 
    317 void ToolbarView::OnMenuButtonClicked(views::View* source,
    318                                       const gfx::Point& point) {
    319   TRACE_EVENT0("ui::views", "ToolbarView::OnMenuButtonClicked");
    320   DCHECK_EQ(VIEW_ID_APP_MENU, source->id());
    321 
    322   bool use_new_menu = false;
    323   bool supports_new_separators = false;
    324   // TODO: remove this.
    325 #if defined(USE_AURA)
    326   supports_new_separators =
    327       GetNativeTheme() == ui::NativeThemeAura::instance();
    328   use_new_menu = supports_new_separators;
    329 #endif
    330 #if defined(OS_WIN)
    331   use_new_menu = use_new_menu || ui::GetDisplayLayout() == ui::LAYOUT_TOUCH;
    332 #endif
    333 
    334   wrench_menu_.reset(new WrenchMenu(browser_, use_new_menu,
    335                                     supports_new_separators));
    336   wrench_menu_model_.reset(new WrenchMenuModel(this, browser_, use_new_menu));
    337   wrench_menu_->Init(wrench_menu_model_.get());
    338 
    339   FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened());
    340 
    341   wrench_menu_->RunMenu(app_menu_);
    342 }
    343 
    344 ////////////////////////////////////////////////////////////////////////////////
    345 // ToolbarView, LocationBarView::Delegate implementation:
    346 
    347 WebContents* ToolbarView::GetWebContents() const {
    348   return browser_->tab_strip_model()->GetActiveWebContents();
    349 }
    350 
    351 InstantController* ToolbarView::GetInstant() {
    352   return browser_->instant_controller() ?
    353       browser_->instant_controller()->instant() : NULL;
    354 }
    355 
    356 ContentSettingBubbleModelDelegate*
    357 ToolbarView::GetContentSettingBubbleModelDelegate() {
    358   return browser_->content_setting_bubble_model_delegate();
    359 }
    360 
    361 void ToolbarView::ShowWebsiteSettings(content::WebContents* web_contents,
    362                                       const GURL& url,
    363                                       const content::SSLStatus& ssl) {
    364   chrome::ShowWebsiteSettings(browser_, web_contents, url, ssl);
    365 }
    366 
    367 views::Widget* ToolbarView::CreateViewsBubble(
    368     views::BubbleDelegateView* bubble_delegate) {
    369   return views::BubbleDelegateView::CreateBubble(bubble_delegate);
    370 }
    371 
    372 PageActionImageView* ToolbarView::CreatePageActionImageView(
    373     LocationBarView* owner, ExtensionAction* action) {
    374   return new PageActionImageView(owner, action, browser_);
    375 }
    376 
    377 void ToolbarView::OnInputInProgress(bool in_progress) {
    378   // The edit should make sure we're only notified when something changes.
    379   DCHECK(model_->GetInputInProgress() != in_progress);
    380 
    381   model_->SetInputInProgress(in_progress);
    382   location_bar_->Update(NULL);
    383 }
    384 
    385 ////////////////////////////////////////////////////////////////////////////////
    386 // ToolbarView, CommandObserver implementation:
    387 
    388 void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
    389   views::Button* button = NULL;
    390   switch (id) {
    391     case IDC_BACK:
    392       button = back_;
    393       break;
    394     case IDC_FORWARD:
    395       button = forward_;
    396       break;
    397     case IDC_RELOAD:
    398       button = reload_;
    399       break;
    400     case IDC_HOME:
    401       button = home_;
    402       break;
    403   }
    404   if (button)
    405     button->SetEnabled(enabled);
    406 }
    407 
    408 ////////////////////////////////////////////////////////////////////////////////
    409 // ToolbarView, views::Button::ButtonListener implementation:
    410 
    411 void ToolbarView::ButtonPressed(views::Button* sender,
    412                                 const ui::Event& event) {
    413   int command = sender->tag();
    414   WindowOpenDisposition disposition =
    415       ui::DispositionFromEventFlags(event.flags());
    416   if ((disposition == CURRENT_TAB) &&
    417       ((command == IDC_BACK) || (command == IDC_FORWARD))) {
    418     // Forcibly reset the location bar, since otherwise it won't discard any
    419     // ongoing user edits, since it doesn't realize this is a user-initiated
    420     // action.
    421     location_bar_->Revert();
    422   }
    423   chrome::ExecuteCommandWithDisposition(browser_, command, disposition);
    424 }
    425 
    426 ////////////////////////////////////////////////////////////////////////////////
    427 // ToolbarView, content::NotificationObserver implementation:
    428 
    429 void ToolbarView::Observe(int type,
    430                           const content::NotificationSource& source,
    431                           const content::NotificationDetails& details) {
    432   switch (type) {
    433     case chrome::NOTIFICATION_UPGRADE_RECOMMENDED:
    434     case chrome::NOTIFICATION_MODULE_INCOMPATIBILITY_BADGE_CHANGE:
    435     case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED:
    436     case chrome::NOTIFICATION_MODULE_LIST_ENUMERATED:
    437       UpdateAppMenuState();
    438       break;
    439     case chrome::NOTIFICATION_OUTDATED_INSTALL:
    440       ShowOutdatedInstallNotification();
    441       break;
    442 #if defined(OS_WIN)
    443     case chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED:
    444       ShowCriticalNotification();
    445       break;
    446 #endif
    447     default:
    448       NOTREACHED();
    449   }
    450 }
    451 
    452 ////////////////////////////////////////////////////////////////////////////////
    453 // ToolbarView, ui::AcceleratorProvider implementation:
    454 
    455 bool ToolbarView::GetAcceleratorForCommandId(int command_id,
    456     ui::Accelerator* accelerator) {
    457   return GetWidget()->GetAccelerator(command_id, accelerator);
    458 }
    459 
    460 ////////////////////////////////////////////////////////////////////////////////
    461 // ToolbarView, views::View overrides:
    462 
    463 gfx::Size ToolbarView::GetPreferredSize() {
    464   if (is_display_mode_normal()) {
    465     int button_spacing = GetButtonSpacing();
    466     int min_width = kLeftEdgeSpacing +
    467         back_->GetPreferredSize().width() + button_spacing +
    468         forward_->GetPreferredSize().width() + button_spacing +
    469         reload_->GetPreferredSize().width() + kStandardSpacing +
    470         (show_home_button_.GetValue() ?
    471             (home_->GetPreferredSize().width() + button_spacing) : 0) +
    472         location_bar_->GetPreferredSize().width() +
    473         browser_actions_->GetPreferredSize().width() +
    474         app_menu_->GetPreferredSize().width() + kRightEdgeSpacing;
    475     gfx::ImageSkia* normal_background =
    476         GetThemeProvider()->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER);
    477     return gfx::Size(min_width,
    478                      normal_background->height() - content_shadow_height());
    479   }
    480 
    481   const int kPopupBottomSpacingGlass = 1;
    482   const int kPopupBottomSpacingNonGlass = 2;
    483   int vertical_spacing = PopupTopSpacing() +
    484       (GetWidget()->ShouldUseNativeFrame() ?
    485           kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass);
    486   return gfx::Size(0, location_bar_->GetPreferredSize().height() +
    487       vertical_spacing);
    488 }
    489 
    490 void ToolbarView::Layout() {
    491   // If we have not been initialized yet just do nothing.
    492   if (back_ == NULL)
    493     return;
    494 
    495   if (!is_display_mode_normal()) {
    496     location_bar_->SetBounds(0, PopupTopSpacing(), width(),
    497                              location_bar_->GetPreferredSize().height());
    498     return;
    499   }
    500 
    501   // We assume all child elements except the location bar are the same height.
    502   // Set child_y such that buttons appear vertically centered. We put any excess
    503   // padding above the buttons.
    504   int child_height =
    505       std::min(back_->GetPreferredSize().height(), height());
    506   int child_y = (height() - child_height + 1) / 2;
    507 
    508   // If the window is maximized, we extend the back button to the left so that
    509   // clicking on the left-most pixel will activate the back button.
    510   // TODO(abarth):  If the window becomes maximized but is not resized,
    511   //                then Layout() might not be called and the back button
    512   //                will be slightly the wrong size.  We should force a
    513   //                Layout() in this case.
    514   //                http://crbug.com/5540
    515   bool maximized = browser_->window() && browser_->window()->IsMaximized();
    516   int back_width = back_->GetPreferredSize().width();
    517   if (maximized)
    518     back_->SetBounds(0, child_y, back_width + kLeftEdgeSpacing, child_height);
    519   else
    520     back_->SetBounds(kLeftEdgeSpacing, child_y, back_width, child_height);
    521 
    522   int button_spacing = GetButtonSpacing();
    523   forward_->SetBounds(back_->x() + back_->width() + button_spacing,
    524       child_y, forward_->GetPreferredSize().width(), child_height);
    525 
    526   reload_->SetBounds(forward_->x() + forward_->width() + button_spacing,
    527       child_y, reload_->GetPreferredSize().width(), child_height);
    528 
    529   if (show_home_button_.GetValue()) {
    530     home_->SetVisible(true);
    531     home_->SetBounds(reload_->x() + reload_->width() + button_spacing,
    532                      child_y, home_->GetPreferredSize().width(), child_height);
    533   } else {
    534     home_->SetVisible(false);
    535     home_->SetBounds(reload_->x() + reload_->width(), child_y, 0, child_height);
    536   }
    537 
    538   int browser_actions_width = browser_actions_->GetPreferredSize().width();
    539   int app_menu_width = app_menu_->GetPreferredSize().width();
    540   int location_x = home_->x() + home_->width() + kStandardSpacing;
    541   int available_width = std::max(0, width() - kRightEdgeSpacing -
    542       app_menu_width - browser_actions_width - location_x);
    543 
    544   int location_height = location_bar_->GetPreferredSize().height();
    545   int location_y = (height() - location_height + 1) / 2;
    546   location_bar_->SetBounds(location_x, location_y, std::max(available_width, 0),
    547                            location_height);
    548 
    549   browser_actions_->SetBounds(location_bar_->x() + location_bar_->width(), 0,
    550                               browser_actions_width, height());
    551   // The browser actions need to do a layout explicitly, because when an
    552   // extension is loaded/unloaded/changed, BrowserActionContainer removes and
    553   // re-adds everything, regardless of whether it has a page action. For a
    554   // page action, browser action bounds do not change, as a result of which
    555   // SetBounds does not do a layout at all.
    556   // TODO(sidchat): Rework the above behavior so that explicit layout is not
    557   //                required.
    558   browser_actions_->Layout();
    559 
    560   // Extend the app menu to the screen's right edge in maximized mode just like
    561   // we extend the back button to the left edge.
    562   if (maximized)
    563     app_menu_width += kRightEdgeSpacing;
    564   app_menu_->SetBounds(browser_actions_->x() + browser_actions_width, child_y,
    565                        app_menu_width, child_height);
    566 }
    567 
    568 bool ToolbarView::HitTestRect(const gfx::Rect& rect) const {
    569   // Don't take hits in our top shadow edge.  Let them fall through to the
    570   // tab strip above us.
    571   if (rect.y() < content_shadow_height())
    572     return false;
    573   // Otherwise let our superclass take care of it.
    574   return AccessiblePaneView::HitTestRect(rect);
    575 }
    576 
    577 void ToolbarView::OnPaint(gfx::Canvas* canvas) {
    578   View::OnPaint(canvas);
    579 
    580   if (is_display_mode_normal())
    581     return;
    582 
    583   // For glass, we need to draw a black line below the location bar to separate
    584   // it from the content area.  For non-glass, the NonClientView draws the
    585   // toolbar background below the location bar for us.
    586   // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()!
    587   if (GetWidget()->ShouldUseNativeFrame())
    588     canvas->FillRect(gfx::Rect(0, height() - 1, width(), 1), SK_ColorBLACK);
    589 }
    590 
    591 // Note this method is ignored on Windows, but needs to be implemented for
    592 // linux, where it is called before CanDrop().
    593 bool ToolbarView::GetDropFormats(
    594     int* formats,
    595     std::set<OSExchangeData::CustomFormat>* custom_formats) {
    596   *formats = ui::OSExchangeData::URL | ui::OSExchangeData::STRING;
    597   return true;
    598 }
    599 
    600 bool ToolbarView::CanDrop(const ui::OSExchangeData& data) {
    601   // To support loading URLs by dropping into the toolbar, we need to support
    602   // dropping URLs and/or text.
    603   return data.HasURL() || data.HasString();
    604 }
    605 
    606 int ToolbarView::OnDragUpdated(const ui::DropTargetEvent& event) {
    607   if (event.source_operations() & ui::DragDropTypes::DRAG_COPY) {
    608     return ui::DragDropTypes::DRAG_COPY;
    609   } else if (event.source_operations() & ui::DragDropTypes::DRAG_LINK) {
    610     return ui::DragDropTypes::DRAG_LINK;
    611   }
    612   return ui::DragDropTypes::DRAG_NONE;
    613 }
    614 
    615 int ToolbarView::OnPerformDrop(const ui::DropTargetEvent& event) {
    616   return location_bar_->GetLocationEntry()->OnPerformDrop(event);
    617 }
    618 
    619 void ToolbarView::OnThemeChanged() {
    620   LoadImages();
    621 }
    622 
    623 const char* ToolbarView::GetClassName() const {
    624   return kViewClassName;
    625 }
    626 
    627 bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) {
    628   const views::View* focused_view = focus_manager()->GetFocusedView();
    629   if (focused_view && (focused_view->id() == VIEW_ID_OMNIBOX))
    630     return false;  // Let the omnibox handle all accelerator events.
    631   return AccessiblePaneView::AcceleratorPressed(accelerator);
    632 }
    633 
    634 bool ToolbarView::IsWrenchMenuShowing() const {
    635   return wrench_menu_.get() && wrench_menu_->IsShowing();
    636 }
    637 
    638 ////////////////////////////////////////////////////////////////////////////////
    639 // ToolbarView, protected:
    640 
    641 // Override this so that when the user presses F6 to rotate toolbar panes,
    642 // the location bar gets focus, not the first control in the toolbar - and
    643 // also so that it selects all content in the location bar.
    644 bool ToolbarView::SetPaneFocusAndFocusDefault() {
    645   if (!location_bar_->HasFocus()) {
    646     SetPaneFocus(location_bar_);
    647     location_bar_->FocusLocation(true);
    648     return true;
    649   }
    650 
    651   if (!AccessiblePaneView::SetPaneFocusAndFocusDefault())
    652     return false;
    653   browser_->window()->RotatePaneFocus(true);
    654   return true;
    655 }
    656 
    657 void ToolbarView::RemovePaneFocus() {
    658   AccessiblePaneView::RemovePaneFocus();
    659   location_bar_->SetShowFocusRect(false);
    660 }
    661 
    662 ////////////////////////////////////////////////////////////////////////////////
    663 // ToolbarView, private:
    664 
    665 bool ToolbarView::ShouldShowUpgradeRecommended() {
    666 #if defined(OS_CHROMEOS)
    667   // In chromeos, the update recommendation is shown in the system tray. So it
    668   // should not be displayed in the wrench menu.
    669   return false;
    670 #else
    671   return (UpgradeDetector::GetInstance()->notify_upgrade());
    672 #endif
    673 }
    674 
    675 bool ToolbarView::ShouldShowIncompatibilityWarning() {
    676 #if defined(OS_WIN)
    677   EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetInstance();
    678   loaded_modules->MaybePostScanningTask();
    679   return loaded_modules->ShouldShowConflictWarning();
    680 #else
    681   return false;
    682 #endif
    683 }
    684 
    685 int ToolbarView::PopupTopSpacing() const {
    686   const int kPopupTopSpacingNonGlass = 3;
    687   return GetWidget()->ShouldUseNativeFrame() ? 0 : kPopupTopSpacingNonGlass;
    688 }
    689 
    690 void ToolbarView::LoadImages() {
    691   ui::ThemeProvider* tp = GetThemeProvider();
    692 
    693   back_->SetImage(views::CustomButton::STATE_NORMAL,
    694       tp->GetImageSkiaNamed(IDR_BACK));
    695   back_->SetImage(views::CustomButton::STATE_HOVERED,
    696       tp->GetImageSkiaNamed(IDR_BACK_H));
    697   back_->SetImage(views::CustomButton::STATE_PRESSED,
    698       tp->GetImageSkiaNamed(IDR_BACK_P));
    699   back_->SetImage(views::CustomButton::STATE_DISABLED,
    700       tp->GetImageSkiaNamed(IDR_BACK_D));
    701 
    702   forward_->SetImage(views::CustomButton::STATE_NORMAL,
    703       tp->GetImageSkiaNamed(IDR_FORWARD));
    704   forward_->SetImage(views::CustomButton::STATE_HOVERED,
    705       tp->GetImageSkiaNamed(IDR_FORWARD_H));
    706   forward_->SetImage(views::CustomButton::STATE_PRESSED,
    707       tp->GetImageSkiaNamed(IDR_FORWARD_P));
    708   forward_->SetImage(views::CustomButton::STATE_DISABLED,
    709       tp->GetImageSkiaNamed(IDR_FORWARD_D));
    710 
    711   reload_->LoadImages(tp);
    712 
    713   home_->SetImage(views::CustomButton::STATE_NORMAL,
    714       tp->GetImageSkiaNamed(IDR_HOME));
    715   home_->SetImage(views::CustomButton::STATE_HOVERED,
    716       tp->GetImageSkiaNamed(IDR_HOME_H));
    717   home_->SetImage(views::CustomButton::STATE_PRESSED,
    718       tp->GetImageSkiaNamed(IDR_HOME_P));
    719 }
    720 
    721 void ToolbarView::ShowCriticalNotification() {
    722 #if defined(OS_WIN)
    723   CriticalNotificationBubbleView* bubble_delegate =
    724       new CriticalNotificationBubbleView(app_menu_);
    725   views::BubbleDelegateView::CreateBubble(bubble_delegate);
    726   bubble_delegate->StartFade(true);
    727 #endif
    728 }
    729 
    730 void ToolbarView::ShowOutdatedInstallNotification() {
    731   if (OutdatedUpgradeBubbleView::IsAvailable())
    732     OutdatedUpgradeBubbleView::ShowBubble(app_menu_, browser_);
    733 }
    734 
    735 void ToolbarView::UpdateAppMenuState() {
    736   string16 accname_app = l10n_util::GetStringUTF16(IDS_ACCNAME_APP);
    737   if (ShouldShowUpgradeRecommended()) {
    738     accname_app = l10n_util::GetStringFUTF16(
    739         IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app);
    740   }
    741   app_menu_->SetAccessibleName(accname_app);
    742 
    743   UpdateWrenchButtonSeverity();
    744   SchedulePaint();
    745 }
    746 
    747 void ToolbarView::UpdateWrenchButtonSeverity() {
    748   // Keep track of whether we were showing the badge before, so we don't send
    749   // multiple UMA events for example when multiple Chrome windows are open.
    750   static bool incompatibility_badge_showing = false;
    751   // Save the old value before resetting it.
    752   bool was_showing = incompatibility_badge_showing;
    753   incompatibility_badge_showing = false;
    754 
    755   if (ShouldShowUpgradeRecommended()) {
    756     UpgradeDetector::UpgradeNotificationAnnoyanceLevel level =
    757         UpgradeDetector::GetInstance()->upgrade_notification_stage();
    758     app_menu_->SetSeverity(WrenchIconPainter::SeverityFromUpgradeLevel(level),
    759                            WrenchIconPainter::ShouldAnimateUpgradeLevel(level));
    760     return;
    761   }
    762 
    763   if (ShouldShowIncompatibilityWarning()) {
    764     if (!was_showing) {
    765       content::RecordAction(UserMetricsAction("ConflictBadge"));
    766 #if defined(OS_WIN)
    767       ConflictingModuleView::MaybeShow(browser_, app_menu_);
    768 #endif
    769     }
    770     app_menu_->SetSeverity(WrenchIconPainter::SEVERITY_MEDIUM, true);
    771     incompatibility_badge_showing = true;
    772     return;
    773   }
    774 
    775   GlobalErrorService* service =
    776       GlobalErrorServiceFactory::GetForProfile(browser_->profile());
    777   GlobalError* error =
    778       service->GetHighestSeverityGlobalErrorWithWrenchMenuItem();
    779   if (error) {
    780     app_menu_->SetSeverity(WrenchIconPainter::GlobalErrorSeverity(), true);
    781     return;
    782   }
    783 
    784   app_menu_->SetSeverity(WrenchIconPainter::SEVERITY_NONE, true);
    785 }
    786 
    787 void ToolbarView::OnShowHomeButtonChanged() {
    788   Layout();
    789   SchedulePaint();
    790 }
    791 
    792 int ToolbarView::content_shadow_height() const {
    793   return browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH ?
    794       kContentShadowHeightAsh : kContentShadowHeight;
    795 }
    796