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