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