Home | History | Annotate | Download | only in views
      1 // Copyright (c) 2011 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/i18n/number_formatting.h"
      8 #include "base/utf_string_conversions.h"
      9 #include "chrome/app/chrome_command_ids.h"
     10 #include "chrome/browser/accessibility/browser_accessibility_state.h"
     11 #include "chrome/browser/metrics/user_metrics.h"
     12 #include "chrome/browser/prefs/pref_service.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/browser/ui/browser.h"
     15 #include "chrome/browser/ui/browser_window.h"
     16 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
     17 #include "chrome/browser/ui/view_ids.h"
     18 #include "chrome/browser/ui/views/browser_actions_container.h"
     19 #include "chrome/browser/ui/views/event_utils.h"
     20 #include "chrome/browser/upgrade_detector.h"
     21 #include "chrome/common/pref_names.h"
     22 #include "content/common/notification_service.h"
     23 #include "grit/chromium_strings.h"
     24 #include "grit/generated_resources.h"
     25 #include "grit/theme_resources.h"
     26 #include "ui/base/accessibility/accessible_view_state.h"
     27 #include "ui/base/l10n/l10n_util.h"
     28 #include "ui/base/resource/resource_bundle.h"
     29 #include "ui/base/theme_provider.h"
     30 #include "ui/gfx/canvas.h"
     31 #include "ui/gfx/canvas_skia.h"
     32 #include "ui/gfx/skbitmap_operations.h"
     33 #include "views/controls/button/button_dropdown.h"
     34 #include "views/focus/view_storage.h"
     35 #include "views/widget/tooltip_manager.h"
     36 #include "views/window/non_client_view.h"
     37 #include "views/window/window.h"
     38 
     39 #if defined(OS_CHROMEOS)
     40 #include "chrome/browser/chromeos/cros/cros_library.h"
     41 #include "chrome/browser/chromeos/cros/update_library.h"
     42 #include "views/controls/menu/menu_2.h"
     43 #endif
     44 #include "chrome/browser/ui/views/wrench_menu.h"
     45 
     46 #if defined(OS_WIN)
     47 #include "chrome/browser/enumerate_modules_model_win.h"
     48 #endif
     49 
     50 // The space between items is 4 px in general.
     51 const int ToolbarView::kStandardSpacing = 4;
     52 // The top of the toolbar has an edge we have to skip over in addition to the 4
     53 // px of spacing.
     54 const int ToolbarView::kVertSpacing = kStandardSpacing + 1;
     55 // The edge graphics have some built-in spacing/shadowing, so we have to adjust
     56 // our spacing to make it still appear to be 4 px.
     57 static const int kEdgeSpacing = ToolbarView::kStandardSpacing - 1;
     58 // The buttons to the left of the omnibox are close together.
     59 static const int kButtonSpacing = 1;
     60 
     61 static const int kStatusBubbleWidth = 480;
     62 
     63 // The length of time to run the upgrade notification animation (the time it
     64 // takes one pulse to run its course and go back to its original brightness).
     65 static const int kPulseDuration = 2000;
     66 
     67 // How long to wait between pulsating the upgrade notifier.
     68 static const int kPulsateEveryMs = 8000;
     69 
     70 static const int kPopupTopSpacingNonGlass = 3;
     71 static const int kPopupBottomSpacingNonGlass = 2;
     72 static const int kPopupBottomSpacingGlass = 1;
     73 
     74 // Top margin for the wrench menu badges (badge is placed in the upper right
     75 // corner of the wrench menu).
     76 static const int kBadgeTopMargin = 2;
     77 
     78 static SkBitmap* kPopupBackgroundEdge = NULL;
     79 
     80 ////////////////////////////////////////////////////////////////////////////////
     81 // ToolbarView, public:
     82 
     83 ToolbarView::ToolbarView(Browser* browser)
     84     : model_(browser->toolbar_model()),
     85       back_(NULL),
     86       forward_(NULL),
     87       reload_(NULL),
     88       home_(NULL),
     89       location_bar_(NULL),
     90       browser_actions_(NULL),
     91       app_menu_(NULL),
     92       profile_(NULL),
     93       browser_(browser),
     94       profiles_menu_contents_(NULL),
     95       destroyed_flag_(NULL) {
     96   SetID(VIEW_ID_TOOLBAR);
     97 
     98   browser_->command_updater()->AddCommandObserver(IDC_BACK, this);
     99   browser_->command_updater()->AddCommandObserver(IDC_FORWARD, this);
    100   browser_->command_updater()->AddCommandObserver(IDC_HOME, this);
    101 
    102   display_mode_ = browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ?
    103       DISPLAYMODE_NORMAL : DISPLAYMODE_LOCATION;
    104 
    105   if (!kPopupBackgroundEdge) {
    106     kPopupBackgroundEdge = ResourceBundle::GetSharedInstance().GetBitmapNamed(
    107         IDR_LOCATIONBG_POPUPMODE_EDGE);
    108   }
    109 
    110   if (!IsUpgradeRecommended()) {
    111     registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED,
    112                    NotificationService::AllSources());
    113   }
    114   registrar_.Add(this, NotificationType::MODULE_INCOMPATIBILITY_BADGE_CHANGE,
    115                  NotificationService::AllSources());
    116 }
    117 
    118 ToolbarView::~ToolbarView() {
    119   if (destroyed_flag_)
    120     *destroyed_flag_ = true;
    121 
    122   // NOTE: Don't remove the command observers here.  This object gets destroyed
    123   // after the Browser (which owns the CommandUpdater), so the CommandUpdater is
    124   // already gone.
    125 }
    126 
    127 void ToolbarView::Init(Profile* profile) {
    128   back_menu_model_.reset(new BackForwardMenuModel(
    129       browser_, BackForwardMenuModel::BACKWARD_MENU));
    130   forward_menu_model_.reset(new BackForwardMenuModel(
    131       browser_, BackForwardMenuModel::FORWARD_MENU));
    132   wrench_menu_model_.reset(new WrenchMenuModel(this, browser_));
    133   back_ = new views::ButtonDropDown(this, back_menu_model_.get());
    134   back_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN |
    135                                      ui::EF_MIDDLE_BUTTON_DOWN);
    136   back_->set_tag(IDC_BACK);
    137   back_->SetImageAlignment(views::ImageButton::ALIGN_RIGHT,
    138                            views::ImageButton::ALIGN_TOP);
    139   back_->SetTooltipText(
    140       UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK)));
    141   back_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
    142   back_->SetID(VIEW_ID_BACK_BUTTON);
    143 
    144   forward_ = new views::ButtonDropDown(this, forward_menu_model_.get());
    145   forward_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN |
    146                                         ui::EF_MIDDLE_BUTTON_DOWN);
    147   forward_->set_tag(IDC_FORWARD);
    148   forward_->SetTooltipText(
    149       UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD)));
    150   forward_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD));
    151   forward_->SetID(VIEW_ID_FORWARD_BUTTON);
    152 
    153   // Have to create this before |reload_| as |reload_|'s constructor needs it.
    154   location_bar_ = new LocationBarView(profile, browser_->command_updater(),
    155       model_, this, (display_mode_ == DISPLAYMODE_LOCATION) ?
    156           LocationBarView::POPUP : LocationBarView::NORMAL);
    157 
    158   reload_ = new ReloadButton(location_bar_, browser_);
    159   reload_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN |
    160                                        ui::EF_MIDDLE_BUTTON_DOWN);
    161   reload_->set_tag(IDC_RELOAD);
    162   reload_->SetTooltipText(
    163       UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_RELOAD)));
    164   reload_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD));
    165   reload_->SetID(VIEW_ID_RELOAD_BUTTON);
    166 
    167   home_ = new views::ImageButton(this);
    168   home_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN |
    169                                      ui::EF_MIDDLE_BUTTON_DOWN);
    170   home_->set_tag(IDC_HOME);
    171   home_->SetTooltipText(
    172       UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_HOME)));
    173   home_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_HOME));
    174   home_->SetID(VIEW_ID_HOME_BUTTON);
    175 
    176   browser_actions_ = new BrowserActionsContainer(browser_, this);
    177 
    178   app_menu_ = new views::MenuButton(NULL, std::wstring(), this, false);
    179   app_menu_->set_border(NULL);
    180   app_menu_->EnableCanvasFlippingForRTLUI(true);
    181   app_menu_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_APP));
    182   app_menu_->SetTooltipText(UTF16ToWide(l10n_util::GetStringFUTF16(
    183       IDS_APPMENU_TOOLTIP,
    184       l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))));
    185   app_menu_->SetID(VIEW_ID_APP_MENU);
    186 
    187   // Add any necessary badges to the menu item based on the system state.
    188   if (IsUpgradeRecommended() || ShouldShowIncompatibilityWarning()) {
    189     UpdateAppMenuBadge();
    190   }
    191   LoadImages();
    192 
    193   // Always add children in order from left to right, for accessibility.
    194   AddChildView(back_);
    195   AddChildView(forward_);
    196   AddChildView(reload_);
    197   AddChildView(home_);
    198   AddChildView(location_bar_);
    199   AddChildView(browser_actions_);
    200   AddChildView(app_menu_);
    201 
    202   location_bar_->Init();
    203   show_home_button_.Init(prefs::kShowHomeButton, profile->GetPrefs(), this);
    204   browser_actions_->Init();
    205 
    206   SetProfile(profile);
    207 
    208   // Accessibility specific tooltip text.
    209   if (BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
    210     back_->SetTooltipText(
    211         UTF16ToWide(l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK)));
    212     forward_->SetTooltipText(
    213         UTF16ToWide(l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD)));
    214   }
    215 }
    216 
    217 void ToolbarView::SetProfile(Profile* profile) {
    218   if (profile != profile_) {
    219     profile_ = profile;
    220     location_bar_->SetProfile(profile);
    221   }
    222 }
    223 
    224 void ToolbarView::Update(TabContents* tab, bool should_restore_state) {
    225   if (location_bar_)
    226     location_bar_->Update(should_restore_state ? tab : NULL);
    227 
    228   if (browser_actions_)
    229     browser_actions_->RefreshBrowserActionViews();
    230 }
    231 
    232 void ToolbarView::SetPaneFocusAndFocusLocationBar(int view_storage_id) {
    233   SetPaneFocus(view_storage_id, location_bar_);
    234 }
    235 
    236 void ToolbarView::SetPaneFocusAndFocusAppMenu(int view_storage_id) {
    237   SetPaneFocus(view_storage_id, app_menu_);
    238 }
    239 
    240 bool ToolbarView::IsAppMenuFocused() {
    241   return app_menu_->HasFocus();
    242 }
    243 
    244 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
    245   menu_listeners_.push_back(listener);
    246 }
    247 
    248 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
    249   for (std::vector<views::MenuListener*>::iterator i(menu_listeners_.begin());
    250        i != menu_listeners_.end(); ++i) {
    251     if (*i == listener) {
    252       menu_listeners_.erase(i);
    253       return;
    254     }
    255   }
    256 }
    257 
    258 ////////////////////////////////////////////////////////////////////////////////
    259 // ToolbarView, AccessiblePaneView overrides:
    260 
    261 bool ToolbarView::SetPaneFocus(
    262     int view_storage_id, views::View* initial_focus) {
    263   if (!AccessiblePaneView::SetPaneFocus(view_storage_id, initial_focus))
    264     return false;
    265 
    266   location_bar_->SetShowFocusRect(true);
    267   return true;
    268 }
    269 
    270 void ToolbarView::GetAccessibleState(ui::AccessibleViewState* state) {
    271   state->role = ui::AccessibilityTypes::ROLE_TOOLBAR;
    272   state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR);
    273 }
    274 
    275 ////////////////////////////////////////////////////////////////////////////////
    276 // ToolbarView, Menu::Delegate overrides:
    277 
    278 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
    279   return GetWidget()->GetAccelerator(id, accel);
    280 }
    281 
    282 ////////////////////////////////////////////////////////////////////////////////
    283 // ToolbarView, views::MenuDelegate implementation:
    284 
    285 void ToolbarView::RunMenu(views::View* source, const gfx::Point& /* pt */) {
    286   DCHECK_EQ(VIEW_ID_APP_MENU, source->GetID());
    287 
    288   bool destroyed_flag = false;
    289   destroyed_flag_ = &destroyed_flag;
    290   wrench_menu_ = new WrenchMenu(browser_);
    291   wrench_menu_->Init(wrench_menu_model_.get());
    292 
    293   for (size_t i = 0; i < menu_listeners_.size(); ++i)
    294     menu_listeners_[i]->OnMenuOpened();
    295 
    296   wrench_menu_->RunMenu(app_menu_);
    297 
    298   if (destroyed_flag)
    299     return;
    300   destroyed_flag_ = NULL;
    301 }
    302 
    303 ////////////////////////////////////////////////////////////////////////////////
    304 // ToolbarView, LocationBarView::Delegate implementation:
    305 
    306 TabContentsWrapper* ToolbarView::GetTabContentsWrapper() const {
    307   return browser_->GetSelectedTabContentsWrapper();
    308 }
    309 
    310 InstantController* ToolbarView::GetInstant() {
    311   return browser_->instant();
    312 }
    313 
    314 void ToolbarView::OnInputInProgress(bool in_progress) {
    315   // The edit should make sure we're only notified when something changes.
    316   DCHECK(model_->input_in_progress() != in_progress);
    317 
    318   model_->set_input_in_progress(in_progress);
    319   location_bar_->Update(NULL);
    320 }
    321 
    322 ////////////////////////////////////////////////////////////////////////////////
    323 // ToolbarView, CommandUpdater::CommandObserver implementation:
    324 
    325 void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
    326   views::Button* button = NULL;
    327   switch (id) {
    328     case IDC_BACK:
    329       button = back_;
    330       break;
    331     case IDC_FORWARD:
    332       button = forward_;
    333       break;
    334     case IDC_HOME:
    335       button = home_;
    336       break;
    337   }
    338   if (button)
    339     button->SetEnabled(enabled);
    340 }
    341 
    342 ////////////////////////////////////////////////////////////////////////////////
    343 // ToolbarView, views::Button::ButtonListener implementation:
    344 
    345 void ToolbarView::ButtonPressed(views::Button* sender,
    346                                 const views::Event& event) {
    347   int command = sender->tag();
    348   WindowOpenDisposition disposition =
    349       event_utils::DispositionFromEventFlags(sender->mouse_event_flags());
    350   if ((disposition == CURRENT_TAB) &&
    351       ((command == IDC_BACK) || (command == IDC_FORWARD))) {
    352     // Forcibly reset the location bar, since otherwise it won't discard any
    353     // ongoing user edits, since it doesn't realize this is a user-initiated
    354     // action.
    355     location_bar_->Revert();
    356   }
    357   browser_->ExecuteCommandWithDisposition(command, disposition);
    358 }
    359 
    360 ////////////////////////////////////////////////////////////////////////////////
    361 // ToolbarView, NotificationObserver implementation:
    362 
    363 void ToolbarView::Observe(NotificationType type,
    364                           const NotificationSource& source,
    365                           const NotificationDetails& details) {
    366   if (type == NotificationType::PREF_CHANGED) {
    367     std::string* pref_name = Details<std::string>(details).ptr();
    368     if (*pref_name == prefs::kShowHomeButton) {
    369       Layout();
    370       SchedulePaint();
    371     }
    372   } else if (type == NotificationType::UPGRADE_RECOMMENDED ||
    373              type == NotificationType::MODULE_INCOMPATIBILITY_BADGE_CHANGE) {
    374     UpdateAppMenuBadge();
    375   }
    376 }
    377 
    378 ////////////////////////////////////////////////////////////////////////////////
    379 // ToolbarView, ui::AcceleratorProvider implementation:
    380 
    381 bool ToolbarView::GetAcceleratorForCommandId(int command_id,
    382     ui::Accelerator* accelerator) {
    383   // The standard Ctrl-X, Ctrl-V and Ctrl-C are not defined as accelerators
    384   // anywhere so we need to check for them explicitly here.
    385   // TODO(cpu) Bug 1109102. Query WebKit land for the actual bindings.
    386   switch (command_id) {
    387     case IDC_CUT:
    388       *accelerator = views::Accelerator(ui::VKEY_X, false, true, false);
    389       return true;
    390     case IDC_COPY:
    391       *accelerator = views::Accelerator(ui::VKEY_C, false, true, false);
    392       return true;
    393     case IDC_PASTE:
    394       *accelerator = views::Accelerator(ui::VKEY_V, false, true, false);
    395       return true;
    396   }
    397   // Else, we retrieve the accelerator information from the frame.
    398   return GetWidget()->GetAccelerator(command_id, accelerator);
    399 }
    400 
    401 ////////////////////////////////////////////////////////////////////////////////
    402 // ToolbarView, views::View overrides:
    403 
    404 gfx::Size ToolbarView::GetPreferredSize() {
    405   if (IsDisplayModeNormal()) {
    406     int min_width = kEdgeSpacing +
    407         back_->GetPreferredSize().width() + kButtonSpacing +
    408         forward_->GetPreferredSize().width() + kButtonSpacing +
    409         reload_->GetPreferredSize().width() + kStandardSpacing +
    410         (show_home_button_.GetValue() ?
    411             (home_->GetPreferredSize().width() + kButtonSpacing) : 0) +
    412         browser_actions_->GetPreferredSize().width() +
    413         app_menu_->GetPreferredSize().width() + kEdgeSpacing;
    414 
    415     static SkBitmap normal_background;
    416     if (normal_background.isNull()) {
    417       ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    418       normal_background = *rb.GetBitmapNamed(IDR_CONTENT_TOP_CENTER);
    419     }
    420 
    421     return gfx::Size(min_width, normal_background.height());
    422   }
    423 
    424   int vertical_spacing = PopupTopSpacing() +
    425       (GetWindow()->non_client_view()->UseNativeFrame() ?
    426           kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass);
    427   return gfx::Size(0, location_bar_->GetPreferredSize().height() +
    428       vertical_spacing);
    429 }
    430 
    431 void ToolbarView::Layout() {
    432   // If we have not been initialized yet just do nothing.
    433   if (back_ == NULL)
    434     return;
    435 
    436   bool maximized = browser_->window() && browser_->window()->IsMaximized();
    437   if (!IsDisplayModeNormal()) {
    438     int edge_width = maximized ?
    439         0 : kPopupBackgroundEdge->width();  // See OnPaint().
    440     location_bar_->SetBounds(edge_width, PopupTopSpacing(),
    441         width() - (edge_width * 2), location_bar_->GetPreferredSize().height());
    442     return;
    443   }
    444 
    445   int child_y = std::min(kVertSpacing, height());
    446   // We assume all child elements are the same height.
    447   int child_height =
    448       std::min(back_->GetPreferredSize().height(), height() - child_y);
    449 
    450   // If the window is maximized, we extend the back button to the left so that
    451   // clicking on the left-most pixel will activate the back button.
    452   // TODO(abarth):  If the window becomes maximized but is not resized,
    453   //                then Layout() might not be called and the back button
    454   //                will be slightly the wrong size.  We should force a
    455   //                Layout() in this case.
    456   //                http://crbug.com/5540
    457   int back_width = back_->GetPreferredSize().width();
    458   if (maximized)
    459     back_->SetBounds(0, child_y, back_width + kEdgeSpacing, child_height);
    460   else
    461     back_->SetBounds(kEdgeSpacing, child_y, back_width, child_height);
    462 
    463   forward_->SetBounds(back_->x() + back_->width() + kButtonSpacing,
    464       child_y, forward_->GetPreferredSize().width(), child_height);
    465 
    466   reload_->SetBounds(forward_->x() + forward_->width() + kButtonSpacing,
    467       child_y, reload_->GetPreferredSize().width(), child_height);
    468 
    469   if (show_home_button_.GetValue()) {
    470     home_->SetVisible(true);
    471     home_->SetBounds(reload_->x() + reload_->width() + kButtonSpacing, child_y,
    472                      home_->GetPreferredSize().width(), child_height);
    473   } else {
    474     home_->SetVisible(false);
    475     home_->SetBounds(reload_->x() + reload_->width(), child_y, 0, child_height);
    476   }
    477 
    478   int browser_actions_width = browser_actions_->GetPreferredSize().width();
    479   int app_menu_width = app_menu_->GetPreferredSize().width();
    480   int location_x = home_->x() + home_->width() + kStandardSpacing;
    481   int available_width = width() - kEdgeSpacing - app_menu_width -
    482       browser_actions_width - location_x;
    483 
    484   location_bar_->SetBounds(location_x, child_y, std::max(available_width, 0),
    485                            child_height);
    486 
    487   browser_actions_->SetBounds(location_bar_->x() + location_bar_->width(), 0,
    488                               browser_actions_width, height());
    489   // The browser actions need to do a layout explicitly, because when an
    490   // extension is loaded/unloaded/changed, BrowserActionContainer removes and
    491   // re-adds everything, regardless of whether it has a page action. For a
    492   // page action, browser action bounds do not change, as a result of which
    493   // SetBounds does not do a layout at all.
    494   // TODO(sidchat): Rework the above behavior so that explicit layout is not
    495   //                required.
    496   browser_actions_->Layout();
    497 
    498   // Extend the app menu to the screen's right edge in maximized mode just like
    499   // we extend the back button to the left edge.
    500   if (maximized)
    501     app_menu_width += kEdgeSpacing;
    502   app_menu_->SetBounds(browser_actions_->x() + browser_actions_width, child_y,
    503                        app_menu_width, child_height);
    504 }
    505 
    506 void ToolbarView::OnPaint(gfx::Canvas* canvas) {
    507   View::OnPaint(canvas);
    508 
    509   if (IsDisplayModeNormal())
    510     return;
    511 
    512   // In maximized mode, we don't draw the endcaps on the location bar, because
    513   // when they're flush against the edge of the screen they just look glitchy.
    514   if (!browser_->window() || !browser_->window()->IsMaximized()) {
    515     int top_spacing = PopupTopSpacing();
    516     canvas->DrawBitmapInt(*kPopupBackgroundEdge, 0, top_spacing);
    517     canvas->DrawBitmapInt(*kPopupBackgroundEdge,
    518                           width() - kPopupBackgroundEdge->width(), top_spacing);
    519   }
    520 
    521   // For glass, we need to draw a black line below the location bar to separate
    522   // it from the content area.  For non-glass, the NonClientView draws the
    523   // toolbar background below the location bar for us.
    524   // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()!
    525   if (GetWindow()->non_client_view()->UseNativeFrame())
    526     canvas->FillRectInt(SK_ColorBLACK, 0, height() - 1, width(), 1);
    527 }
    528 
    529 // Note this method is ignored on Windows, but needs to be implemented for
    530 // linux, where it is called before CanDrop().
    531 bool ToolbarView::GetDropFormats(
    532     int* formats,
    533     std::set<OSExchangeData::CustomFormat>* custom_formats) {
    534   *formats = ui::OSExchangeData::URL | ui::OSExchangeData::STRING;
    535   return true;
    536 }
    537 
    538 bool ToolbarView::CanDrop(const ui::OSExchangeData& data) {
    539   // To support loading URLs by dropping into the toolbar, we need to support
    540   // dropping URLs and/or text.
    541   return data.HasURL() || data.HasString();
    542 }
    543 
    544 int ToolbarView::OnDragUpdated(const views::DropTargetEvent& event) {
    545   if (event.source_operations() & ui::DragDropTypes::DRAG_COPY) {
    546     return ui::DragDropTypes::DRAG_COPY;
    547   } else if (event.source_operations() & ui::DragDropTypes::DRAG_LINK) {
    548     return ui::DragDropTypes::DRAG_LINK;
    549   }
    550   return ui::DragDropTypes::DRAG_NONE;
    551 }
    552 
    553 int ToolbarView::OnPerformDrop(const views::DropTargetEvent& event) {
    554   return location_bar_->location_entry()->OnPerformDrop(event);
    555 }
    556 
    557 void ToolbarView::OnThemeChanged() {
    558   LoadImages();
    559 }
    560 
    561 ////////////////////////////////////////////////////////////////////////////////
    562 // ToolbarView, protected:
    563 
    564 // Override this so that when the user presses F6 to rotate toolbar panes,
    565 // the location bar gets focus, not the first control in the toolbar.
    566 views::View* ToolbarView::GetDefaultFocusableChild() {
    567   return location_bar_;
    568 }
    569 
    570 void ToolbarView::RemovePaneFocus() {
    571   AccessiblePaneView::RemovePaneFocus();
    572   location_bar_->SetShowFocusRect(false);
    573 }
    574 
    575 ////////////////////////////////////////////////////////////////////////////////
    576 // ToolbarView, private:
    577 
    578 bool ToolbarView::IsUpgradeRecommended() {
    579 #if defined(OS_CHROMEOS)
    580   return (chromeos::CrosLibrary::Get()->GetUpdateLibrary()->status().status ==
    581           chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT);
    582 #else
    583   return (UpgradeDetector::GetInstance()->notify_upgrade());
    584 #endif
    585 }
    586 
    587 int ToolbarView::GetUpgradeRecommendedBadge() const {
    588 #if defined(OS_CHROMEOS)
    589   return IDR_UPDATE_BADGE;
    590 #else
    591   switch (UpgradeDetector::GetInstance()->upgrade_notification_stage()) {
    592     case UpgradeDetector::UPGRADE_ANNOYANCE_SEVERE:
    593       return IDR_UPDATE_BADGE4;
    594     case UpgradeDetector::UPGRADE_ANNOYANCE_HIGH:
    595       return IDR_UPDATE_BADGE3;
    596     case UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED:
    597       return IDR_UPDATE_BADGE2;
    598     default:
    599       return IDR_UPDATE_BADGE;
    600   }
    601 #endif
    602 }
    603 
    604 bool ToolbarView::ShouldShowIncompatibilityWarning() {
    605 #if defined(OS_WIN)
    606   EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetInstance();
    607   return loaded_modules->ShouldShowConflictWarning();
    608 #else
    609   return false;
    610 #endif
    611 }
    612 
    613 int ToolbarView::PopupTopSpacing() const {
    614   // TODO(beng): For some reason GetWindow() returns NULL here in some
    615   //             unidentified circumstances on ChromeOS. This means GetWidget()
    616   //             succeeded but we were (probably) unable to locate a WidgetGtk*
    617   //             on it using NativeWidget::GetNativeWidgetForNativeView.
    618   //             I am throwing in a NULL check for now to stop the hurt, but
    619   //             it's possible the crash may just show up somewhere else.
    620   const views::Window* window = GetWindow();
    621   DCHECK(window) << "If you hit this please talk to beng";
    622   return window && window->non_client_view()->UseNativeFrame() ?
    623       0 : kPopupTopSpacingNonGlass;
    624 }
    625 
    626 void ToolbarView::LoadImages() {
    627   ui::ThemeProvider* tp = GetThemeProvider();
    628 
    629   back_->SetImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_BACK));
    630   back_->SetImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_BACK_H));
    631   back_->SetImage(views::CustomButton::BS_PUSHED,
    632       tp->GetBitmapNamed(IDR_BACK_P));
    633   back_->SetImage(views::CustomButton::BS_DISABLED,
    634       tp->GetBitmapNamed(IDR_BACK_D));
    635 
    636   forward_->SetImage(views::CustomButton::BS_NORMAL,
    637       tp->GetBitmapNamed(IDR_FORWARD));
    638   forward_->SetImage(views::CustomButton::BS_HOT,
    639       tp->GetBitmapNamed(IDR_FORWARD_H));
    640   forward_->SetImage(views::CustomButton::BS_PUSHED,
    641       tp->GetBitmapNamed(IDR_FORWARD_P));
    642   forward_->SetImage(views::CustomButton::BS_DISABLED,
    643       tp->GetBitmapNamed(IDR_FORWARD_D));
    644 
    645   reload_->SetImage(views::CustomButton::BS_NORMAL,
    646       tp->GetBitmapNamed(IDR_RELOAD));
    647   reload_->SetImage(views::CustomButton::BS_HOT,
    648       tp->GetBitmapNamed(IDR_RELOAD_H));
    649   reload_->SetImage(views::CustomButton::BS_PUSHED,
    650       tp->GetBitmapNamed(IDR_RELOAD_P));
    651   reload_->SetToggledImage(views::CustomButton::BS_NORMAL,
    652       tp->GetBitmapNamed(IDR_STOP));
    653   reload_->SetToggledImage(views::CustomButton::BS_HOT,
    654       tp->GetBitmapNamed(IDR_STOP_H));
    655   reload_->SetToggledImage(views::CustomButton::BS_PUSHED,
    656       tp->GetBitmapNamed(IDR_STOP_P));
    657   reload_->SetToggledImage(views::CustomButton::BS_DISABLED,
    658       tp->GetBitmapNamed(IDR_STOP_D));
    659 
    660   home_->SetImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_HOME));
    661   home_->SetImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_HOME_H));
    662   home_->SetImage(views::CustomButton::BS_PUSHED,
    663       tp->GetBitmapNamed(IDR_HOME_P));
    664 
    665   app_menu_->SetIcon(GetAppMenuIcon(views::CustomButton::BS_NORMAL));
    666   app_menu_->SetHoverIcon(GetAppMenuIcon(views::CustomButton::BS_HOT));
    667   app_menu_->SetPushedIcon(GetAppMenuIcon(views::CustomButton::BS_PUSHED));
    668 }
    669 
    670 void ToolbarView::UpdateAppMenuBadge() {
    671   app_menu_->SetIcon(GetAppMenuIcon(views::CustomButton::BS_NORMAL));
    672   app_menu_->SetHoverIcon(GetAppMenuIcon(views::CustomButton::BS_HOT));
    673   app_menu_->SetPushedIcon(GetAppMenuIcon(views::CustomButton::BS_PUSHED));
    674   SchedulePaint();
    675 }
    676 
    677 SkBitmap ToolbarView::GetAppMenuIcon(views::CustomButton::ButtonState state) {
    678   ui::ThemeProvider* tp = GetThemeProvider();
    679 
    680   int id = 0;
    681   switch (state) {
    682     case views::CustomButton::BS_NORMAL: id = IDR_TOOLS;   break;
    683     case views::CustomButton::BS_HOT:    id = IDR_TOOLS_H; break;
    684     case views::CustomButton::BS_PUSHED: id = IDR_TOOLS_P; break;
    685     default:                             NOTREACHED();     break;
    686   }
    687   SkBitmap icon = *tp->GetBitmapNamed(id);
    688 
    689 #if defined(OS_WIN)
    690   // Keep track of whether we were showing the badge before, so we don't send
    691   // multiple UMA events for example when multiple Chrome windows are open.
    692   static bool incompatibility_badge_showing = false;
    693   // Save the old value before resetting it.
    694   bool was_showing = incompatibility_badge_showing;
    695   incompatibility_badge_showing = false;
    696 #endif
    697 
    698   bool add_badge = IsUpgradeRecommended() || ShouldShowIncompatibilityWarning();
    699   if (!add_badge)
    700     return icon;
    701 
    702   // Draw the chrome app menu icon onto the canvas.
    703   scoped_ptr<gfx::CanvasSkia> canvas(
    704       new gfx::CanvasSkia(icon.width(), icon.height(), false));
    705   canvas->DrawBitmapInt(icon, 0, 0);
    706 
    707   SkBitmap badge;
    708   // Only one badge can be active at any given time. The Upgrade notification
    709   // is deemed most important, then the DLL conflict badge.
    710   if (IsUpgradeRecommended()) {
    711     badge = *tp->GetBitmapNamed(GetUpgradeRecommendedBadge());
    712   } else if (ShouldShowIncompatibilityWarning()) {
    713 #if defined(OS_WIN)
    714     if (!was_showing)
    715       UserMetrics::RecordAction(UserMetricsAction("ConflictBadge"), profile_);
    716     badge = *tp->GetBitmapNamed(IDR_CONFLICT_BADGE);
    717     incompatibility_badge_showing = true;
    718 #else
    719     NOTREACHED();
    720 #endif
    721   } else {
    722     NOTREACHED();
    723   }
    724 
    725   canvas->DrawBitmapInt(badge, icon.width() - badge.width(), kBadgeTopMargin);
    726 
    727   return canvas->ExtractBitmap();
    728 }
    729