Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / toolbar / toolbar_view.cc
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
90 using base::UserMetricsAction;
91 using content::WebContents;
92
93 namespace {
94
95 // The edge graphics have some built-in spacing/shadowing, so we have to adjust
96 // our spacing to make it match.
97 const int kLeftEdgeSpacing = 3;
98 const int kRightEdgeSpacing = 2;
99
100 // Ash doesn't use a rounded content area and its top edge has an extra shadow.
101 const int kContentShadowHeightAsh = 2;
102
103 // Non-ash uses a rounded content area with no shadow in the assets.
104 const int kContentShadowHeight = 0;
105
106 #if !defined(OS_CHROMEOS)
107 bool 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
119 const char ToolbarView::kViewClassName[] = "ToolbarView";
120
121 ////////////////////////////////////////////////////////////////////////////////
122 // ToolbarView, public:
123
124 ToolbarView::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
165 ToolbarView::~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
171 void 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
272 void 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
280 void 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
295 void 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
304 void ToolbarView::SetPaneFocusAndFocusAppMenu() {
305   SetPaneFocus(app_menu_);
306 }
307
308 bool ToolbarView::IsAppMenuFocused() {
309   return app_menu_->HasFocus();
310 }
311
312 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
313   menu_listeners_.AddObserver(listener);
314 }
315
316 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
317   menu_listeners_.RemoveObserver(listener);
318 }
319
320 views::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
325 views::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
331 void ToolbarView::ExecuteExtensionCommand(
332     const extensions::Extension* extension,
333     const extensions::Command& command) {
334   browser_actions_->ExecuteExtensionCommand(extension, command);
335 }
336
337 void 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
357 views::MenuButton* ToolbarView::app_menu() const {
358   return app_menu_;
359 }
360
361 ////////////////////////////////////////////////////////////////////////////////
362 // ToolbarView, AccessiblePaneView overrides:
363
364 bool 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
372 void 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
380 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
381   return GetWidget()->GetAccelerator(id, accel);
382 }
383
384 ////////////////////////////////////////////////////////////////////////////////
385 // ToolbarView, views::MenuButtonListener implementation:
386
387 void 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
397 WebContents* ToolbarView::GetWebContents() {
398   return browser_->tab_strip_model()->GetActiveWebContents();
399 }
400
401 ToolbarModel* ToolbarView::GetToolbarModel() {
402   return browser_->toolbar_model();
403 }
404
405 const ToolbarModel* ToolbarView::GetToolbarModel() const {
406   return browser_->toolbar_model();
407 }
408
409 InstantController* ToolbarView::GetInstant() {
410   return browser_->instant_controller() ?
411       browser_->instant_controller()->instant() : NULL;
412 }
413
414 ContentSettingBubbleModelDelegate*
415 ToolbarView::GetContentSettingBubbleModelDelegate() {
416   return browser_->content_setting_bubble_model_delegate();
417 }
418
419 void 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
425 views::Widget* ToolbarView::CreateViewsBubble(
426     views::BubbleDelegateView* bubble_delegate) {
427   return views::BubbleDelegateView::CreateBubble(bubble_delegate);
428 }
429
430 PageActionImageView* ToolbarView::CreatePageActionImageView(
431     LocationBarView* owner, ExtensionAction* action) {
432   return new PageActionImageView(owner, action, browser_);
433 }
434
435 ////////////////////////////////////////////////////////////////////////////////
436 // ToolbarView, CommandObserver implementation:
437
438 void 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
461 void 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
470 void 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
493 bool 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
501 gfx::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
515 gfx::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
528 void 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
615 void 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
629 void ToolbarView::OnThemeChanged() {
630   LoadImages();
631 }
632
633 const char* ToolbarView::GetClassName() const {
634   return kViewClassName;
635 }
636
637 bool 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
644 bool ToolbarView::IsWrenchMenuShowing() const {
645   return wrench_menu_.get() && wrench_menu_->IsShowing();
646 }
647
648 bool 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.
658 bool 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
671 void ToolbarView::RemovePaneFocus() {
672   AccessiblePaneView::RemovePaneFocus();
673   location_bar_->SetShowFocusRect(false);
674 }
675
676 ////////////////////////////////////////////////////////////////////////////////
677 // ToolbarView, private:
678
679 // views::ViewTargeterDelegate:
680 bool 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
693 void 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
727 int ToolbarView::PopupTopSpacing() const {
728   const int kPopupTopSpacingNonGlass = 3;
729   return GetWidget()->ShouldWindowContentsBeTransparent() ?
730       0 : kPopupTopSpacingNonGlass;
731 }
732
733 gfx::Size ToolbarView::SizeForContentSize(gfx::Size size) const {
734   if (is_display_mode_normal()) {
735     gfx::ImageSkia* normal_background =
736         GetThemeProvider()->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER);
737     size.SetToMax(
738         gfx::Size(0, normal_background->height() - content_shadow_height()));
739   } else {
740     const int kPopupBottomSpacingGlass = 1;
741     const int kPopupBottomSpacingNonGlass = 2;
742     size.Enlarge(
743         0,
744         PopupTopSpacing() + (GetWidget()->ShouldWindowContentsBeTransparent() ?
745             kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass));
746   }
747   return size;
748 }
749
750 void ToolbarView::LoadImages() {
751   ui::ThemeProvider* tp = GetThemeProvider();
752
753   back_->SetImage(views::Button::STATE_NORMAL,
754                   *(tp->GetImageSkiaNamed(IDR_BACK)));
755   back_->SetImage(views::Button::STATE_DISABLED,
756                   *(tp->GetImageSkiaNamed(IDR_BACK_D)));
757
758   forward_->SetImage(views::Button::STATE_NORMAL,
759                     *(tp->GetImageSkiaNamed(IDR_FORWARD)));
760   forward_->SetImage(views::Button::STATE_DISABLED,
761                      *(tp->GetImageSkiaNamed(IDR_FORWARD_D)));
762
763   reload_->LoadImages();
764
765   home_->SetImage(views::Button::STATE_NORMAL,
766                   *(tp->GetImageSkiaNamed(IDR_HOME)));
767 }
768
769 void ToolbarView::ShowCriticalNotification() {
770 #if defined(OS_WIN)
771   CriticalNotificationBubbleView* bubble_delegate =
772       new CriticalNotificationBubbleView(app_menu_);
773   views::BubbleDelegateView::CreateBubble(bubble_delegate)->Show();
774 #endif
775 }
776
777 void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) {
778   if (OutdatedUpgradeBubbleView::IsAvailable()) {
779     OutdatedUpgradeBubbleView::ShowBubble(
780         app_menu_, browser_, auto_update_enabled);
781   }
782 }
783
784 void ToolbarView::OnShowHomeButtonChanged() {
785   Layout();
786   SchedulePaint();
787 }
788
789 int ToolbarView::content_shadow_height() const {
790   return browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH ?
791       kContentShadowHeightAsh : kContentShadowHeight;
792 }