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