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