toolbar_view.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 <algorithm>
8
9#include "base/command_line.h"
10#include "base/debug/trace_event.h"
11#include "base/i18n/number_formatting.h"
12#include "base/prefs/pref_service.h"
13#include "base/strings/utf_string_conversions.h"
14#include "chrome/app/chrome_command_ids.h"
15#include "chrome/browser/chrome_notification_types.h"
16#include "chrome/browser/command_updater.h"
17#include "chrome/browser/extensions/extension_commands_global_registry.h"
18#include "chrome/browser/extensions/extension_util.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/themes/theme_service.h"
21#include "chrome/browser/ui/browser.h"
22#include "chrome/browser/ui/browser_command_controller.h"
23#include "chrome/browser/ui/browser_commands.h"
24#include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
25#include "chrome/browser/ui/browser_instant_controller.h"
26#include "chrome/browser/ui/browser_tabstrip.h"
27#include "chrome/browser/ui/browser_window.h"
28#include "chrome/browser/ui/global_error/global_error_service.h"
29#include "chrome/browser/ui/global_error/global_error_service_factory.h"
30#include "chrome/browser/ui/omnibox/omnibox_view.h"
31#include "chrome/browser/ui/tabs/tab_strip_model.h"
32#include "chrome/browser/ui/toolbar/wrench_menu_model.h"
33#include "chrome/browser/ui/view_ids.h"
34#include "chrome/browser/ui/views/extensions/extension_message_bubble_view.h"
35#include "chrome/browser/ui/views/extensions/extension_popup.h"
36#include "chrome/browser/ui/views/frame/browser_view.h"
37#include "chrome/browser/ui/views/location_bar/page_action_image_view.h"
38#include "chrome/browser/ui/views/location_bar/page_action_with_badge_view.h"
39#include "chrome/browser/ui/views/location_bar/star_view.h"
40#include "chrome/browser/ui/views/location_bar/translate_icon_view.h"
41#include "chrome/browser/ui/views/outdated_upgrade_bubble_view.h"
42#include "chrome/browser/ui/views/toolbar/back_button.h"
43#include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
44#include "chrome/browser/ui/views/toolbar/home_button.h"
45#include "chrome/browser/ui/views/toolbar/reload_button.h"
46#include "chrome/browser/ui/views/toolbar/toolbar_button.h"
47#include "chrome/browser/ui/views/toolbar/wrench_menu.h"
48#include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h"
49#include "chrome/common/chrome_switches.h"
50#include "chrome/common/pref_names.h"
51#include "chrome/grit/chromium_strings.h"
52#include "chrome/grit/generated_resources.h"
53#include "content/public/browser/browser_accessibility_state.h"
54#include "content/public/browser/notification_service.h"
55#include "content/public/browser/render_view_host.h"
56#include "content/public/browser/user_metrics.h"
57#include "content/public/browser/web_contents.h"
58#include "grit/theme_resources.h"
59#include "ui/accessibility/ax_view_state.h"
60#include "ui/aura/window.h"
61#include "ui/base/l10n/l10n_util.h"
62#include "ui/base/theme_provider.h"
63#include "ui/base/window_open_disposition.h"
64#include "ui/compositor/layer.h"
65#include "ui/gfx/canvas.h"
66#include "ui/gfx/image/canvas_image_source.h"
67#include "ui/keyboard/keyboard_controller.h"
68#include "ui/native_theme/native_theme_aura.h"
69#include "ui/views/controls/menu/menu_listener.h"
70#include "ui/views/focus/view_storage.h"
71#include "ui/views/view_targeter.h"
72#include "ui/views/widget/tooltip_manager.h"
73#include "ui/views/widget/widget.h"
74#include "ui/views/window/non_client_view.h"
75
76#if defined(OS_WIN)
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
90using base::UserMetricsAction;
91using content::WebContents;
92
93namespace {
94
95// The edge graphics have some built-in spacing/shadowing, so we have to adjust
96// our spacing to make it match.
97const int kLeftEdgeSpacing = 3;
98const int kRightEdgeSpacing = 2;
99
100// Ash doesn't use a rounded content area and its top edge has an extra shadow.
101const int kContentShadowHeightAsh = 2;
102
103// Non-ash uses a rounded content area with no shadow in the assets.
104const int kContentShadowHeight = 0;
105
106#if !defined(OS_CHROMEOS)
107bool HasAshShell() {
108#if defined(USE_ASH)
109  return ash::Shell::HasInstance();
110#else
111  return false;
112#endif  // USE_ASH
113}
114#endif  // OS_CHROMEOS
115
116}  // namespace
117
118// static
119const char ToolbarView::kViewClassName[] = "ToolbarView";
120
121////////////////////////////////////////////////////////////////////////////////
122// ToolbarView, public:
123
124ToolbarView::ToolbarView(Browser* browser)
125    : back_(NULL),
126      forward_(NULL),
127      reload_(NULL),
128      home_(NULL),
129      location_bar_(NULL),
130      browser_actions_(NULL),
131      app_menu_(NULL),
132      browser_(browser),
133      badge_controller_(browser->profile(), this),
134      extension_message_bubble_factory_(
135          new extensions::ExtensionMessageBubbleFactory(browser->profile(),
136                                                        this)) {
137  set_id(VIEW_ID_TOOLBAR);
138
139  SetEventTargeter(
140      scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
141
142  chrome::AddCommandObserver(browser_, IDC_BACK, this);
143  chrome::AddCommandObserver(browser_, IDC_FORWARD, this);
144  chrome::AddCommandObserver(browser_, IDC_RELOAD, this);
145  chrome::AddCommandObserver(browser_, IDC_HOME, this);
146  chrome::AddCommandObserver(browser_, IDC_LOAD_NEW_TAB_PAGE, this);
147
148  display_mode_ = DISPLAYMODE_LOCATION;
149  if (browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ||
150      (browser->is_app() && extensions::util::IsStreamlinedHostedAppsEnabled()))
151    display_mode_ = DISPLAYMODE_NORMAL;
152
153  if (OutdatedUpgradeBubbleView::IsAvailable()) {
154    registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL,
155                   content::NotificationService::AllSources());
156    registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU,
157                   content::NotificationService::AllSources());
158  }
159#if defined(OS_WIN)
160  registrar_.Add(this, chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED,
161                 content::NotificationService::AllSources());
162#endif
163}
164
165ToolbarView::~ToolbarView() {
166  // NOTE: Don't remove the command observers here.  This object gets destroyed
167  // after the Browser (which owns the CommandUpdater), so the CommandUpdater is
168  // already gone.
169}
170
171void ToolbarView::Init() {
172  GetWidget()->AddObserver(this);
173
174  back_ = new BackButton(this, new BackForwardMenuModel(
175      browser_, BackForwardMenuModel::BACKWARD_MENU));
176  back_->set_triggerable_event_flags(
177      ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
178  back_->set_tag(IDC_BACK);
179  back_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK));
180  back_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
181  back_->set_id(VIEW_ID_BACK_BUTTON);
182  back_->Init();
183
184  forward_ = new ToolbarButton(this, new BackForwardMenuModel(
185      browser_, BackForwardMenuModel::FORWARD_MENU));
186  forward_->set_triggerable_event_flags(
187      ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
188  forward_->set_tag(IDC_FORWARD);
189  forward_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD));
190  forward_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD));
191  forward_->set_id(VIEW_ID_FORWARD_BUTTON);
192  forward_->Init();
193
194  location_bar_ = new LocationBarView(
195      browser_, browser_->profile(),
196      browser_->command_controller()->command_updater(), this,
197      display_mode_ == DISPLAYMODE_LOCATION);
198
199  reload_ = new ReloadButton(browser_->command_controller()->command_updater());
200  reload_->set_triggerable_event_flags(
201      ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
202  reload_->set_tag(IDC_RELOAD);
203  reload_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD));
204  reload_->set_id(VIEW_ID_RELOAD_BUTTON);
205  reload_->Init();
206
207  home_ = new HomeButton(this, browser_);
208  home_->set_triggerable_event_flags(
209      ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON);
210  home_->set_tag(IDC_HOME);
211  home_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_HOME));
212  home_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_HOME));
213  home_->set_id(VIEW_ID_HOME_BUTTON);
214  home_->Init();
215
216  browser_actions_ = new BrowserActionsContainer(
217      browser_,
218      this,   // Owner.
219      NULL);  // No master container for this one (it is master).
220
221  app_menu_ = new WrenchToolbarButton(this);
222  app_menu_->EnableCanvasFlippingForRTLUI(true);
223  app_menu_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_APP));
224  app_menu_->SetTooltipText(l10n_util::GetStringUTF16(IDS_APPMENU_TOOLTIP));
225  app_menu_->set_id(VIEW_ID_APP_MENU);
226
227  // Always add children in order from left to right, for accessibility.
228  AddChildView(back_);
229  AddChildView(forward_);
230  AddChildView(reload_);
231  AddChildView(home_);
232  AddChildView(location_bar_);
233  AddChildView(browser_actions_);
234  AddChildView(app_menu_);
235
236  LoadImages();
237
238  // Start global error services now so we badge the menu correctly in non-Ash.
239#if !defined(OS_CHROMEOS)
240  if (!HasAshShell()) {
241    SigninGlobalErrorFactory::GetForProfile(browser_->profile());
242#if !defined(OS_ANDROID)
243    SyncGlobalErrorFactory::GetForProfile(browser_->profile());
244#endif
245  }
246#endif  // OS_CHROMEOS
247
248  // Add any necessary badges to the menu item based on the system state.
249  // Do this after |app_menu_| has been added as a bubble may be shown that
250  // needs the widget (widget found by way of app_menu_->GetWidget()).
251  badge_controller_.UpdateDelegate();
252
253  location_bar_->Init();
254
255  show_home_button_.Init(prefs::kShowHomeButton,
256                         browser_->profile()->GetPrefs(),
257                         base::Bind(&ToolbarView::OnShowHomeButtonChanged,
258                                    base::Unretained(this)));
259
260  browser_actions_->Init();
261
262  // Accessibility specific tooltip text.
263  if (content::BrowserAccessibilityState::GetInstance()->
264          IsAccessibleBrowser()) {
265    back_->SetTooltipText(
266        l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK));
267    forward_->SetTooltipText(
268        l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD));
269  }
270}
271
272void ToolbarView::OnWidgetVisibilityChanged(views::Widget* widget,
273                                            bool visible) {
274  if (visible) {
275    // Safe to call multiple times; the bubble will only appear once.
276    extension_message_bubble_factory_->MaybeShow(app_menu_);
277  }
278}
279
280void ToolbarView::OnWidgetActivationChanged(views::Widget* widget,
281                                            bool active) {
282  extensions::ExtensionCommandsGlobalRegistry* registry =
283      extensions::ExtensionCommandsGlobalRegistry::Get(browser_->profile());
284  if (registry) {
285    if (active) {
286      registry->set_registry_for_active_window(
287          browser_actions_->extension_keybinding_registry());
288    } else if (registry->registry_for_active_window() ==
289               browser_actions_->extension_keybinding_registry()) {
290      registry->set_registry_for_active_window(NULL);
291    }
292  }
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::ShowAppMenu(bool for_drop) {
338  if (wrench_menu_.get() && wrench_menu_->IsShowing())
339    return;
340
341  if (keyboard::KeyboardController::GetInstance() &&
342      keyboard::KeyboardController::GetInstance()->keyboard_visible()) {
343    keyboard::KeyboardController::GetInstance()->HideKeyboard(
344        keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
345  }
346
347  wrench_menu_.reset(
348      new WrenchMenu(browser_, for_drop ? WrenchMenu::FOR_DROP : 0));
349  wrench_menu_model_.reset(new WrenchMenuModel(this, browser_));
350  wrench_menu_->Init(wrench_menu_model_.get());
351
352  FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened());
353
354  wrench_menu_->RunMenu(app_menu_);
355}
356
357views::MenuButton* ToolbarView::app_menu() const {
358  return app_menu_;
359}
360
361////////////////////////////////////////////////////////////////////////////////
362// ToolbarView, AccessiblePaneView overrides:
363
364bool ToolbarView::SetPaneFocus(views::View* initial_focus) {
365  if (!AccessiblePaneView::SetPaneFocus(initial_focus))
366    return false;
367
368  location_bar_->SetShowFocusRect(true);
369  return true;
370}
371
372void ToolbarView::GetAccessibleState(ui::AXViewState* state) {
373  state->role = ui::AX_ROLE_TOOLBAR;
374  state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR);
375}
376
377////////////////////////////////////////////////////////////////////////////////
378// ToolbarView, Menu::Delegate overrides:
379
380bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
381  return GetWidget()->GetAccelerator(id, accel);
382}
383
384////////////////////////////////////////////////////////////////////////////////
385// ToolbarView, views::MenuButtonListener implementation:
386
387void ToolbarView::OnMenuButtonClicked(views::View* source,
388                                      const gfx::Point& point) {
389  TRACE_EVENT0("views", "ToolbarView::OnMenuButtonClicked");
390  DCHECK_EQ(VIEW_ID_APP_MENU, source->id());
391  ShowAppMenu(false);  // Not for drop.
392}
393
394////////////////////////////////////////////////////////////////////////////////
395// ToolbarView, LocationBarView::Delegate implementation:
396
397WebContents* ToolbarView::GetWebContents() {
398  return browser_->tab_strip_model()->GetActiveWebContents();
399}
400
401ToolbarModel* ToolbarView::GetToolbarModel() {
402  return browser_->toolbar_model();
403}
404
405const ToolbarModel* ToolbarView::GetToolbarModel() const {
406  return browser_->toolbar_model();
407}
408
409InstantController* ToolbarView::GetInstant() {
410  return browser_->instant_controller() ?
411      browser_->instant_controller()->instant() : NULL;
412}
413
414ContentSettingBubbleModelDelegate*
415ToolbarView::GetContentSettingBubbleModelDelegate() {
416  return browser_->content_setting_bubble_model_delegate();
417}
418
419void ToolbarView::ShowWebsiteSettings(content::WebContents* web_contents,
420                                      const GURL& url,
421                                      const content::SSLStatus& ssl) {
422  chrome::ShowWebsiteSettings(browser_, web_contents, url, ssl);
423}
424
425views::Widget* ToolbarView::CreateViewsBubble(
426    views::BubbleDelegateView* bubble_delegate) {
427  return views::BubbleDelegateView::CreateBubble(bubble_delegate);
428}
429
430PageActionImageView* ToolbarView::CreatePageActionImageView(
431    LocationBarView* owner, ExtensionAction* action) {
432  return new PageActionImageView(owner, action, browser_);
433}
434
435////////////////////////////////////////////////////////////////////////////////
436// ToolbarView, CommandObserver implementation:
437
438void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
439  views::Button* button = NULL;
440  switch (id) {
441    case IDC_BACK:
442      button = back_;
443      break;
444    case IDC_FORWARD:
445      button = forward_;
446      break;
447    case IDC_RELOAD:
448      button = reload_;
449      break;
450    case IDC_HOME:
451      button = home_;
452      break;
453  }
454  if (button)
455    button->SetEnabled(enabled);
456}
457
458////////////////////////////////////////////////////////////////////////////////
459// ToolbarView, views::Button::ButtonListener implementation:
460
461void ToolbarView::ButtonPressed(views::Button* sender,
462                                const ui::Event& event) {
463  chrome::ExecuteCommandWithDisposition(
464      browser_, sender->tag(), ui::DispositionFromEventFlags(event.flags()));
465}
466
467////////////////////////////////////////////////////////////////////////////////
468// ToolbarView, content::NotificationObserver implementation:
469
470void ToolbarView::Observe(int type,
471                          const content::NotificationSource& source,
472                          const content::NotificationDetails& details) {
473  switch (type) {
474    case chrome::NOTIFICATION_OUTDATED_INSTALL:
475      ShowOutdatedInstallNotification(true);
476      break;
477    case chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU:
478      ShowOutdatedInstallNotification(false);
479      break;
480#if defined(OS_WIN)
481    case chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED:
482      ShowCriticalNotification();
483      break;
484#endif
485    default:
486      NOTREACHED();
487  }
488}
489
490////////////////////////////////////////////////////////////////////////////////
491// ToolbarView, ui::AcceleratorProvider implementation:
492
493bool ToolbarView::GetAcceleratorForCommandId(int command_id,
494    ui::Accelerator* accelerator) {
495  return GetWidget()->GetAccelerator(command_id, accelerator);
496}
497
498////////////////////////////////////////////////////////////////////////////////
499// ToolbarView, views::View overrides:
500
501gfx::Size ToolbarView::GetPreferredSize() const {
502  gfx::Size size(location_bar_->GetPreferredSize());
503  if (is_display_mode_normal()) {
504    int content_width = kLeftEdgeSpacing + back_->GetPreferredSize().width() +
505        forward_->GetPreferredSize().width() +
506        reload_->GetPreferredSize().width() +
507        (show_home_button_.GetValue() ? home_->GetPreferredSize().width() : 0) +
508        kStandardSpacing + browser_actions_->GetPreferredSize().width() +
509        app_menu_->GetPreferredSize().width() + kRightEdgeSpacing;
510    size.Enlarge(content_width, 0);
511  }
512  return SizeForContentSize(size);
513}
514
515gfx::Size ToolbarView::GetMinimumSize() const {
516  gfx::Size size(location_bar_->GetMinimumSize());
517  if (is_display_mode_normal()) {
518    int content_width = kLeftEdgeSpacing + back_->GetMinimumSize().width() +
519        forward_->GetMinimumSize().width() + reload_->GetMinimumSize().width() +
520        (show_home_button_.GetValue() ? home_->GetMinimumSize().width() : 0) +
521        kStandardSpacing + browser_actions_->GetMinimumSize().width() +
522        app_menu_->GetMinimumSize().width() + kRightEdgeSpacing;
523    size.Enlarge(content_width, 0);
524  }
525  return SizeForContentSize(size);
526}
527
528void ToolbarView::Layout() {
529  // If we have not been initialized yet just do nothing.
530  if (back_ == NULL)
531    return;
532
533  if (!is_display_mode_normal()) {
534    location_bar_->SetBounds(0, PopupTopSpacing(), width(),
535                             location_bar_->GetPreferredSize().height());
536    return;
537  }
538
539  // We assume all child elements except the location bar are the same height.
540  // Set child_y such that buttons appear vertically centered. We put any excess
541  // padding above the buttons.
542  int child_height =
543      std::min(back_->GetPreferredSize().height(), height());
544  int child_y = (height() - child_height + 1) / 2;
545
546  // If the window is maximized, we extend the back button to the left so that
547  // clicking on the left-most pixel will activate the back button.
548  // TODO(abarth):  If the window becomes maximized but is not resized,
549  //                then Layout() might not be called and the back button
550  //                will be slightly the wrong size.  We should force a
551  //                Layout() in this case.
552  //                http://crbug.com/5540
553  bool maximized = browser_->window() && browser_->window()->IsMaximized();
554  int back_width = back_->GetPreferredSize().width();
555  if (maximized) {
556    back_->SetBounds(0, child_y, back_width + kLeftEdgeSpacing, child_height);
557    back_->SetLeadingMargin(kLeftEdgeSpacing);
558  } else {
559    back_->SetBounds(kLeftEdgeSpacing, child_y, back_width, child_height);
560    back_->SetLeadingMargin(0);
561  }
562  int next_element_x = back_->bounds().right();
563
564  forward_->SetBounds(next_element_x, child_y,
565                      forward_->GetPreferredSize().width(), child_height);
566  next_element_x = forward_->bounds().right();
567
568  reload_->SetBounds(next_element_x, child_y,
569                     reload_->GetPreferredSize().width(), child_height);
570  next_element_x = reload_->bounds().right();
571
572  if (show_home_button_.GetValue() ||
573      (browser_->is_app() &&
574       extensions::util::IsStreamlinedHostedAppsEnabled())) {
575    home_->SetVisible(true);
576    home_->SetBounds(next_element_x, child_y,
577                     home_->GetPreferredSize().width(), child_height);
578  } else {
579    home_->SetVisible(false);
580    home_->SetBounds(next_element_x, child_y, 0, child_height);
581  }
582  next_element_x = home_->bounds().right() + kStandardSpacing;
583
584  int browser_actions_width = browser_actions_->GetPreferredSize().width();
585  int app_menu_width = app_menu_->GetPreferredSize().width();
586  int available_width = std::max(0, width() - kRightEdgeSpacing -
587      app_menu_width - browser_actions_width - next_element_x);
588
589  int location_height = location_bar_->GetPreferredSize().height();
590  int location_y = (height() - location_height + 1) / 2;
591  location_bar_->SetBounds(next_element_x, location_y,
592                           std::max(available_width, 0), location_height);
593  next_element_x = location_bar_->bounds().right();
594
595  browser_actions_->SetBounds(
596      next_element_x, child_y, browser_actions_width, child_height);
597  next_element_x = browser_actions_->bounds().right();
598
599  // The browser actions need to do a layout explicitly, because when an
600  // extension is loaded/unloaded/changed, BrowserActionContainer removes and
601  // re-adds everything, regardless of whether it has a page action. For a
602  // page action, browser action bounds do not change, as a result of which
603  // SetBounds does not do a layout at all.
604  // TODO(sidchat): Rework the above behavior so that explicit layout is not
605  //                required.
606  browser_actions_->Layout();
607
608  // Extend the app menu to the screen's right edge in maximized mode just like
609  // we extend the back button to the left edge.
610  if (maximized)
611    app_menu_width += kRightEdgeSpacing;
612  app_menu_->SetBounds(next_element_x, child_y, app_menu_width, child_height);
613}
614
615void ToolbarView::OnPaint(gfx::Canvas* canvas) {
616  View::OnPaint(canvas);
617
618  if (is_display_mode_normal())
619    return;
620
621  // For glass, we need to draw a black line below the location bar to separate
622  // it from the content area.  For non-glass, the NonClientView draws the
623  // toolbar background below the location bar for us.
624  // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()!
625  if (GetWidget()->ShouldWindowContentsBeTransparent())
626    canvas->FillRect(gfx::Rect(0, height() - 1, width(), 1), SK_ColorBLACK);
627}
628
629void ToolbarView::OnThemeChanged() {
630  LoadImages();
631}
632
633const char* ToolbarView::GetClassName() const {
634  return kViewClassName;
635}
636
637bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) {
638  const views::View* focused_view = focus_manager()->GetFocusedView();
639  if (focused_view && (focused_view->id() == VIEW_ID_OMNIBOX))
640    return false;  // Let the omnibox handle all accelerator events.
641  return AccessiblePaneView::AcceleratorPressed(accelerator);
642}
643
644bool ToolbarView::IsWrenchMenuShowing() const {
645  return wrench_menu_.get() && wrench_menu_->IsShowing();
646}
647
648bool ToolbarView::ShouldPaintBackground() const {
649  return display_mode_ == DISPLAYMODE_NORMAL;
650}
651
652////////////////////////////////////////////////////////////////////////////////
653// ToolbarView, protected:
654
655// Override this so that when the user presses F6 to rotate toolbar panes,
656// the location bar gets focus, not the first control in the toolbar - and
657// also so that it selects all content in the location bar.
658bool ToolbarView::SetPaneFocusAndFocusDefault() {
659  if (!location_bar_->HasFocus()) {
660    SetPaneFocus(location_bar_);
661    location_bar_->FocusLocation(true);
662    return true;
663  }
664
665  if (!AccessiblePaneView::SetPaneFocusAndFocusDefault())
666    return false;
667  browser_->window()->RotatePaneFocus(true);
668  return true;
669}
670
671void ToolbarView::RemovePaneFocus() {
672  AccessiblePaneView::RemovePaneFocus();
673  location_bar_->SetShowFocusRect(false);
674}
675
676////////////////////////////////////////////////////////////////////////////////
677// ToolbarView, private:
678
679// views::ViewTargeterDelegate:
680bool ToolbarView::DoesIntersectRect(const views::View* target,
681                                    const gfx::Rect& rect) const {
682  CHECK_EQ(target, this);
683
684  // Fall through to the tab strip above us if none of |rect| intersects
685  // with this view (intersection with the top shadow edge does not
686  // count as intersection with this view).
687  if (rect.bottom() < content_shadow_height())
688    return false;
689  // Otherwise let our superclass take care of it.
690  return ViewTargeterDelegate::DoesIntersectRect(this, rect);
691}
692
693void ToolbarView::UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,
694                                      WrenchIconPainter::Severity severity,
695                                      bool animate)  {
696  // Showing the bubble requires |app_menu_| to be in a widget. See comment
697  // in ConflictingModuleView for details.
698  DCHECK(app_menu_->GetWidget());
699
700  base::string16 accname_app = l10n_util::GetStringUTF16(IDS_ACCNAME_APP);
701  if (type == WrenchMenuBadgeController::BADGE_TYPE_UPGRADE_NOTIFICATION) {
702    accname_app = l10n_util::GetStringFUTF16(
703        IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app);
704  }
705  app_menu_->SetAccessibleName(accname_app);
706  app_menu_->SetSeverity(severity, animate);
707
708  // Keep track of whether we were showing the badge before, so we don't send
709  // multiple UMA events for example when multiple Chrome windows are open.
710  static bool incompatibility_badge_showing = false;
711  // Save the old value before resetting it.
712  bool was_showing = incompatibility_badge_showing;
713  incompatibility_badge_showing = false;
714
715  if (type == WrenchMenuBadgeController::BADGE_TYPE_INCOMPATIBILITY_WARNING) {
716    if (!was_showing) {
717      content::RecordAction(UserMetricsAction("ConflictBadge"));
718#if defined(OS_WIN)
719      ConflictingModuleView::MaybeShow(browser_, app_menu_);
720#endif
721    }
722    incompatibility_badge_showing = true;
723    return;
724  }
725}
726
727int ToolbarView::PopupTopSpacing() const {
728  const int kPopupTopSpacingNonGlass = 3;
729  return GetWidget()->ShouldWindowContentsBeTransparent() ?
730      0 : kPopupTopSpacingNonGlass;
731}
732
733gfx::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
750void 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
769void 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
777void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) {
778  if (OutdatedUpgradeBubbleView::IsAvailable()) {
779    OutdatedUpgradeBubbleView::ShowBubble(
780        app_menu_, browser_, auto_update_enabled);
781  }
782}
783
784void ToolbarView::OnShowHomeButtonChanged() {
785  Layout();
786  SchedulePaint();
787}
788
789int ToolbarView::content_shadow_height() const {
790  return browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH ?
791      kContentShadowHeightAsh : kContentShadowHeight;
792}
793