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