• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ToolbarView(Browser * browser)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 
~ToolbarView()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 
Init(Profile * profile)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 
SetProfile(Profile * profile)217 void ToolbarView::SetProfile(Profile* profile) {
218   if (profile != profile_) {
219     profile_ = profile;
220     location_bar_->SetProfile(profile);
221   }
222 }
223 
Update(TabContents * tab,bool should_restore_state)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 
SetPaneFocusAndFocusLocationBar(int view_storage_id)232 void ToolbarView::SetPaneFocusAndFocusLocationBar(int view_storage_id) {
233   SetPaneFocus(view_storage_id, location_bar_);
234 }
235 
SetPaneFocusAndFocusAppMenu(int view_storage_id)236 void ToolbarView::SetPaneFocusAndFocusAppMenu(int view_storage_id) {
237   SetPaneFocus(view_storage_id, app_menu_);
238 }
239 
IsAppMenuFocused()240 bool ToolbarView::IsAppMenuFocused() {
241   return app_menu_->HasFocus();
242 }
243 
AddMenuListener(views::MenuListener * listener)244 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
245   menu_listeners_.push_back(listener);
246 }
247 
RemoveMenuListener(views::MenuListener * listener)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 
SetPaneFocus(int view_storage_id,views::View * initial_focus)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 
GetAccessibleState(ui::AccessibleViewState * state)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 
GetAcceleratorInfo(int id,ui::Accelerator * accel)278 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
279   return GetWidget()->GetAccelerator(id, accel);
280 }
281 
282 ////////////////////////////////////////////////////////////////////////////////
283 // ToolbarView, views::MenuDelegate implementation:
284 
RunMenu(views::View * source,const gfx::Point &)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 
GetTabContentsWrapper() const306 TabContentsWrapper* ToolbarView::GetTabContentsWrapper() const {
307   return browser_->GetSelectedTabContentsWrapper();
308 }
309 
GetInstant()310 InstantController* ToolbarView::GetInstant() {
311   return browser_->instant();
312 }
313 
OnInputInProgress(bool in_progress)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 
EnabledStateChangedForCommand(int id,bool enabled)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 
ButtonPressed(views::Button * sender,const views::Event & event)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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)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 
GetPreferredSize()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 
Layout()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 
OnPaint(gfx::Canvas * canvas)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().
GetDropFormats(int * formats,std::set<OSExchangeData::CustomFormat> * custom_formats)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 
CanDrop(const ui::OSExchangeData & data)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 
OnDragUpdated(const views::DropTargetEvent & event)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 
OnPerformDrop(const views::DropTargetEvent & event)553 int ToolbarView::OnPerformDrop(const views::DropTargetEvent& event) {
554   return location_bar_->location_entry()->OnPerformDrop(event);
555 }
556 
OnThemeChanged()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.
GetDefaultFocusableChild()566 views::View* ToolbarView::GetDefaultFocusableChild() {
567   return location_bar_;
568 }
569 
RemovePaneFocus()570 void ToolbarView::RemovePaneFocus() {
571   AccessiblePaneView::RemovePaneFocus();
572   location_bar_->SetShowFocusRect(false);
573 }
574 
575 ////////////////////////////////////////////////////////////////////////////////
576 // ToolbarView, private:
577 
IsUpgradeRecommended()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 
GetUpgradeRecommendedBadge() const587 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 
ShouldShowIncompatibilityWarning()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 
PopupTopSpacing() const613 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 
LoadImages()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 
UpdateAppMenuBadge()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 
GetAppMenuIcon(views::CustomButton::ButtonState state)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