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