Upstream version 11.40.277.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       NULL);  // No master container for this one (it is master).
219
220   app_menu_ = new WrenchToolbarButton(this);
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   AddChildView(browser_actions_);
233   AddChildView(app_menu_);
234
235   LoadImages();
236
237   // Start global error services now so we badge the menu correctly in non-Ash.
238 #if !defined(OS_CHROMEOS)
239   if (!HasAshShell()) {
240     SigninGlobalErrorFactory::GetForProfile(browser_->profile());
241 #if !defined(OS_ANDROID)
242     SyncGlobalErrorFactory::GetForProfile(browser_->profile());
243 #endif
244   }
245 #endif  // OS_CHROMEOS
246
247   // Add any necessary badges to the menu item based on the system state.
248   // Do this after |app_menu_| has been added as a bubble may be shown that
249   // needs the widget (widget found by way of app_menu_->GetWidget()).
250   badge_controller_.UpdateDelegate();
251
252   location_bar_->Init();
253
254   show_home_button_.Init(prefs::kShowHomeButton,
255                          browser_->profile()->GetPrefs(),
256                          base::Bind(&ToolbarView::OnShowHomeButtonChanged,
257                                     base::Unretained(this)));
258
259   browser_actions_->Init();
260
261   // Accessibility specific tooltip text.
262   if (content::BrowserAccessibilityState::GetInstance()->
263           IsAccessibleBrowser()) {
264     back_->SetTooltipText(
265         l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK));
266     forward_->SetTooltipText(
267         l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD));
268   }
269 }
270
271 void ToolbarView::OnWidgetVisibilityChanged(views::Widget* widget,
272                                             bool visible) {
273   if (visible) {
274     // Safe to call multiple times; the bubble will only appear once.
275     extension_message_bubble_factory_->MaybeShow(app_menu_);
276   }
277 }
278
279 void ToolbarView::OnWidgetActivationChanged(views::Widget* widget,
280                                             bool active) {
281   extensions::ExtensionCommandsGlobalRegistry* registry =
282       extensions::ExtensionCommandsGlobalRegistry::Get(browser_->profile());
283   if (registry) {
284     if (active) {
285       registry->set_registry_for_active_window(
286           browser_actions_->extension_keybinding_registry());
287     } else if (registry->registry_for_active_window() ==
288                browser_actions_->extension_keybinding_registry()) {
289       registry->set_registry_for_active_window(NULL);
290     }
291   }
292 }
293
294 void ToolbarView::Update(WebContents* tab) {
295   if (location_bar_)
296     location_bar_->Update(tab);
297   if (browser_actions_)
298     browser_actions_->RefreshToolbarActionViews();
299   if (reload_)
300     reload_->set_menu_enabled(chrome::IsDebuggerAttachedToCurrentTab(browser_));
301 }
302
303 void ToolbarView::SetPaneFocusAndFocusAppMenu() {
304   SetPaneFocus(app_menu_);
305 }
306
307 bool ToolbarView::IsAppMenuFocused() {
308   return app_menu_->HasFocus();
309 }
310
311 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
312   menu_listeners_.AddObserver(listener);
313 }
314
315 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
316   menu_listeners_.RemoveObserver(listener);
317 }
318
319 views::View* ToolbarView::GetBookmarkBubbleAnchor() {
320   views::View* star_view = location_bar()->star_view();
321   return (star_view && star_view->visible()) ? star_view : app_menu_;
322 }
323
324 views::View* ToolbarView::GetTranslateBubbleAnchor() {
325   views::View* translate_icon_view = location_bar()->translate_icon_view();
326   return (translate_icon_view && translate_icon_view->visible()) ?
327       translate_icon_view : app_menu_;
328 }
329
330 void ToolbarView::ExecuteExtensionCommand(
331     const extensions::Extension* extension,
332     const extensions::Command& command) {
333   browser_actions_->ExecuteExtensionCommand(extension, command);
334 }
335
336 void ToolbarView::ShowAppMenu(bool for_drop) {
337   if (wrench_menu_.get() && wrench_menu_->IsShowing())
338     return;
339
340   if (keyboard::KeyboardController::GetInstance() &&
341       keyboard::KeyboardController::GetInstance()->keyboard_visible()) {
342     keyboard::KeyboardController::GetInstance()->HideKeyboard(
343         keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
344   }
345
346   wrench_menu_.reset(
347       new WrenchMenu(browser_, for_drop ? WrenchMenu::FOR_DROP : 0));
348   wrench_menu_model_.reset(new WrenchMenuModel(this, browser_));
349   wrench_menu_->Init(wrench_menu_model_.get());
350
351   FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened());
352
353   wrench_menu_->RunMenu(app_menu_);
354 }
355
356 views::MenuButton* ToolbarView::app_menu() const {
357   return app_menu_;
358 }
359
360 ////////////////////////////////////////////////////////////////////////////////
361 // ToolbarView, AccessiblePaneView overrides:
362
363 bool ToolbarView::SetPaneFocus(views::View* initial_focus) {
364   if (!AccessiblePaneView::SetPaneFocus(initial_focus))
365     return false;
366
367   location_bar_->SetShowFocusRect(true);
368   return true;
369 }
370
371 void ToolbarView::GetAccessibleState(ui::AXViewState* state) {
372   state->role = ui::AX_ROLE_TOOLBAR;
373   state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR);
374 }
375
376 ////////////////////////////////////////////////////////////////////////////////
377 // ToolbarView, Menu::Delegate overrides:
378
379 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
380   return GetWidget()->GetAccelerator(id, accel);
381 }
382
383 ////////////////////////////////////////////////////////////////////////////////
384 // ToolbarView, views::MenuButtonListener implementation:
385
386 void ToolbarView::OnMenuButtonClicked(views::View* source,
387                                       const gfx::Point& point) {
388   TRACE_EVENT0("views", "ToolbarView::OnMenuButtonClicked");
389   DCHECK_EQ(VIEW_ID_APP_MENU, source->id());
390   ShowAppMenu(false);  // Not for drop.
391 }
392
393 ////////////////////////////////////////////////////////////////////////////////
394 // ToolbarView, LocationBarView::Delegate implementation:
395
396 WebContents* ToolbarView::GetWebContents() {
397   return browser_->tab_strip_model()->GetActiveWebContents();
398 }
399
400 ToolbarModel* ToolbarView::GetToolbarModel() {
401   return browser_->toolbar_model();
402 }
403
404 const ToolbarModel* ToolbarView::GetToolbarModel() const {
405   return browser_->toolbar_model();
406 }
407
408 InstantController* ToolbarView::GetInstant() {
409   return browser_->instant_controller() ?
410       browser_->instant_controller()->instant() : NULL;
411 }
412
413 ContentSettingBubbleModelDelegate*
414 ToolbarView::GetContentSettingBubbleModelDelegate() {
415   return browser_->content_setting_bubble_model_delegate();
416 }
417
418 void ToolbarView::ShowWebsiteSettings(content::WebContents* web_contents,
419                                       const GURL& url,
420                                       const content::SSLStatus& ssl) {
421   chrome::ShowWebsiteSettings(browser_, web_contents, url, ssl);
422 }
423
424 views::Widget* ToolbarView::CreateViewsBubble(
425     views::BubbleDelegateView* bubble_delegate) {
426   return views::BubbleDelegateView::CreateBubble(bubble_delegate);
427 }
428
429 PageActionImageView* ToolbarView::CreatePageActionImageView(
430     LocationBarView* owner, ExtensionAction* action) {
431   return new PageActionImageView(owner, action, browser_);
432 }
433
434 ////////////////////////////////////////////////////////////////////////////////
435 // ToolbarView, CommandObserver implementation:
436
437 void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) {
438   views::Button* button = NULL;
439   switch (id) {
440     case IDC_BACK:
441       button = back_;
442       break;
443     case IDC_FORWARD:
444       button = forward_;
445       break;
446     case IDC_RELOAD:
447       button = reload_;
448       break;
449     case IDC_HOME:
450       button = home_;
451       break;
452   }
453   if (button)
454     button->SetEnabled(enabled);
455 }
456
457 ////////////////////////////////////////////////////////////////////////////////
458 // ToolbarView, views::Button::ButtonListener implementation:
459
460 void ToolbarView::ButtonPressed(views::Button* sender,
461                                 const ui::Event& event) {
462   chrome::ExecuteCommandWithDisposition(
463       browser_, sender->tag(), ui::DispositionFromEventFlags(event.flags()));
464 }
465
466 ////////////////////////////////////////////////////////////////////////////////
467 // ToolbarView, content::NotificationObserver implementation:
468
469 void ToolbarView::Observe(int type,
470                           const content::NotificationSource& source,
471                           const content::NotificationDetails& details) {
472   switch (type) {
473     case chrome::NOTIFICATION_OUTDATED_INSTALL:
474       ShowOutdatedInstallNotification(true);
475       break;
476     case chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU:
477       ShowOutdatedInstallNotification(false);
478       break;
479 #if defined(OS_WIN)
480     case chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED:
481       ShowCriticalNotification();
482       break;
483 #endif
484     default:
485       NOTREACHED();
486   }
487 }
488
489 ////////////////////////////////////////////////////////////////////////////////
490 // ToolbarView, ui::AcceleratorProvider implementation:
491
492 bool ToolbarView::GetAcceleratorForCommandId(int command_id,
493     ui::Accelerator* accelerator) {
494   return GetWidget()->GetAccelerator(command_id, accelerator);
495 }
496
497 ////////////////////////////////////////////////////////////////////////////////
498 // ToolbarView, views::View overrides:
499
500 gfx::Size ToolbarView::GetPreferredSize() const {
501   gfx::Size size(location_bar_->GetPreferredSize());
502   if (is_display_mode_normal()) {
503     int content_width = kLeftEdgeSpacing + back_->GetPreferredSize().width() +
504         forward_->GetPreferredSize().width() +
505         reload_->GetPreferredSize().width() +
506         (show_home_button_.GetValue() ? home_->GetPreferredSize().width() : 0) +
507         kStandardSpacing + browser_actions_->GetPreferredSize().width() +
508         app_menu_->GetPreferredSize().width() + kRightEdgeSpacing;
509     size.Enlarge(content_width, 0);
510   }
511   return SizeForContentSize(size);
512 }
513
514 gfx::Size ToolbarView::GetMinimumSize() const {
515   gfx::Size size(location_bar_->GetMinimumSize());
516   if (is_display_mode_normal()) {
517     int content_width = kLeftEdgeSpacing + back_->GetMinimumSize().width() +
518         forward_->GetMinimumSize().width() + reload_->GetMinimumSize().width() +
519         (show_home_button_.GetValue() ? home_->GetMinimumSize().width() : 0) +
520         kStandardSpacing + browser_actions_->GetMinimumSize().width() +
521         app_menu_->GetMinimumSize().width() + kRightEdgeSpacing;
522     size.Enlarge(content_width, 0);
523   }
524   return SizeForContentSize(size);
525 }
526
527 void ToolbarView::Layout() {
528   // If we have not been initialized yet just do nothing.
529   if (back_ == NULL)
530     return;
531
532   if (!is_display_mode_normal()) {
533     location_bar_->SetBounds(0, PopupTopSpacing(), width(),
534                              location_bar_->GetPreferredSize().height());
535     return;
536   }
537
538   // We assume all child elements except the location bar are the same height.
539   // Set child_y such that buttons appear vertically centered. We put any excess
540   // padding above the buttons.
541   int child_height =
542       std::min(back_->GetPreferredSize().height(), height());
543   int child_y = (height() - child_height + 1) / 2;
544
545   // If the window is maximized, we extend the back button to the left so that
546   // clicking on the left-most pixel will activate the back button.
547   // TODO(abarth):  If the window becomes maximized but is not resized,
548   //                then Layout() might not be called and the back button
549   //                will be slightly the wrong size.  We should force a
550   //                Layout() in this case.
551   //                http://crbug.com/5540
552   bool maximized = browser_->window() && browser_->window()->IsMaximized();
553   int back_width = back_->GetPreferredSize().width();
554   if (maximized) {
555     back_->SetBounds(0, child_y, back_width + kLeftEdgeSpacing, child_height);
556     back_->SetLeadingMargin(kLeftEdgeSpacing);
557   } else {
558     back_->SetBounds(kLeftEdgeSpacing, child_y, back_width, child_height);
559     back_->SetLeadingMargin(0);
560   }
561   int next_element_x = back_->bounds().right();
562
563   forward_->SetBounds(next_element_x, child_y,
564                       forward_->GetPreferredSize().width(), child_height);
565   next_element_x = forward_->bounds().right();
566
567   reload_->SetBounds(next_element_x, child_y,
568                      reload_->GetPreferredSize().width(), child_height);
569   next_element_x = reload_->bounds().right();
570
571   if (show_home_button_.GetValue() ||
572       (browser_->is_app() &&
573        extensions::util::IsStreamlinedHostedAppsEnabled())) {
574     home_->SetVisible(true);
575     home_->SetBounds(next_element_x, child_y,
576                      home_->GetPreferredSize().width(), child_height);
577   } else {
578     home_->SetVisible(false);
579     home_->SetBounds(next_element_x, child_y, 0, child_height);
580   }
581   next_element_x = home_->bounds().right() + kStandardSpacing;
582
583   int browser_actions_width = browser_actions_->GetPreferredSize().width();
584   int app_menu_width = app_menu_->GetPreferredSize().width();
585   int available_width = std::max(0, width() - kRightEdgeSpacing -
586       app_menu_width - browser_actions_width - next_element_x);
587
588   int location_height = location_bar_->GetPreferredSize().height();
589   int location_y = (height() - location_height + 1) / 2;
590   location_bar_->SetBounds(next_element_x, location_y,
591                            std::max(available_width, 0), location_height);
592   next_element_x = location_bar_->bounds().right();
593
594   browser_actions_->SetBounds(
595       next_element_x, child_y, browser_actions_width, child_height);
596   next_element_x = browser_actions_->bounds().right();
597
598   // The browser actions need to do a layout explicitly, because when an
599   // extension is loaded/unloaded/changed, BrowserActionContainer removes and
600   // re-adds everything, regardless of whether it has a page action. For a
601   // page action, browser action bounds do not change, as a result of which
602   // SetBounds does not do a layout at all.
603   // TODO(sidchat): Rework the above behavior so that explicit layout is not
604   //                required.
605   browser_actions_->Layout();
606
607   // Extend the app menu to the screen's right edge in maximized mode just like
608   // we extend the back button to the left edge.
609   if (maximized)
610     app_menu_width += kRightEdgeSpacing;
611   app_menu_->SetBounds(next_element_x, child_y, app_menu_width, child_height);
612 }
613
614 void ToolbarView::OnPaint(gfx::Canvas* canvas) {
615   View::OnPaint(canvas);
616
617   if (is_display_mode_normal())
618     return;
619
620   // For glass, we need to draw a black line below the location bar to separate
621   // it from the content area.  For non-glass, the NonClientView draws the
622   // toolbar background below the location bar for us.
623   // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()!
624   if (GetWidget()->ShouldWindowContentsBeTransparent())
625     canvas->FillRect(gfx::Rect(0, height() - 1, width(), 1), SK_ColorBLACK);
626 }
627
628 void ToolbarView::OnThemeChanged() {
629   LoadImages();
630 }
631
632 const char* ToolbarView::GetClassName() const {
633   return kViewClassName;
634 }
635
636 bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) {
637   const views::View* focused_view = focus_manager()->GetFocusedView();
638   if (focused_view && (focused_view->id() == VIEW_ID_OMNIBOX))
639     return false;  // Let the omnibox handle all accelerator events.
640   return AccessiblePaneView::AcceleratorPressed(accelerator);
641 }
642
643 bool ToolbarView::IsWrenchMenuShowing() const {
644   return wrench_menu_.get() && wrench_menu_->IsShowing();
645 }
646
647 bool ToolbarView::ShouldPaintBackground() const {
648   return display_mode_ == DISPLAYMODE_NORMAL;
649 }
650
651 ////////////////////////////////////////////////////////////////////////////////
652 // ToolbarView, protected:
653
654 // Override this so that when the user presses F6 to rotate toolbar panes,
655 // the location bar gets focus, not the first control in the toolbar - and
656 // also so that it selects all content in the location bar.
657 bool ToolbarView::SetPaneFocusAndFocusDefault() {
658   if (!location_bar_->HasFocus()) {
659     SetPaneFocus(location_bar_);
660     location_bar_->FocusLocation(true);
661     return true;
662   }
663
664   if (!AccessiblePaneView::SetPaneFocusAndFocusDefault())
665     return false;
666   browser_->window()->RotatePaneFocus(true);
667   return true;
668 }
669
670 void ToolbarView::RemovePaneFocus() {
671   AccessiblePaneView::RemovePaneFocus();
672   location_bar_->SetShowFocusRect(false);
673 }
674
675 ////////////////////////////////////////////////////////////////////////////////
676 // ToolbarView, private:
677
678 // views::ViewTargeterDelegate:
679 bool ToolbarView::DoesIntersectRect(const views::View* target,
680                                     const gfx::Rect& rect) const {
681   CHECK_EQ(target, this);
682
683   // Fall through to the tab strip above us if none of |rect| intersects
684   // with this view (intersection with the top shadow edge does not
685   // count as intersection with this view).
686   if (rect.bottom() < content_shadow_height())
687     return false;
688   // Otherwise let our superclass take care of it.
689   return ViewTargeterDelegate::DoesIntersectRect(this, rect);
690 }
691
692 void ToolbarView::UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,
693                                       WrenchIconPainter::Severity severity,
694                                       bool animate)  {
695   // Showing the bubble requires |app_menu_| to be in a widget. See comment
696   // in ConflictingModuleView for details.
697   DCHECK(app_menu_->GetWidget());
698
699   base::string16 accname_app = l10n_util::GetStringUTF16(IDS_ACCNAME_APP);
700   if (type == WrenchMenuBadgeController::BADGE_TYPE_UPGRADE_NOTIFICATION) {
701     accname_app = l10n_util::GetStringFUTF16(
702         IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app);
703   }
704   app_menu_->SetAccessibleName(accname_app);
705   app_menu_->SetSeverity(severity, animate);
706
707   // Keep track of whether we were showing the badge before, so we don't send
708   // multiple UMA events for example when multiple Chrome windows are open.
709   static bool incompatibility_badge_showing = false;
710   // Save the old value before resetting it.
711   bool was_showing = incompatibility_badge_showing;
712   incompatibility_badge_showing = false;
713
714   if (type == WrenchMenuBadgeController::BADGE_TYPE_INCOMPATIBILITY_WARNING) {
715     if (!was_showing) {
716       content::RecordAction(UserMetricsAction("ConflictBadge"));
717 #if defined(OS_WIN)
718       ConflictingModuleView::MaybeShow(browser_, app_menu_);
719 #endif
720     }
721     incompatibility_badge_showing = true;
722     return;
723   }
724 }
725
726 int ToolbarView::PopupTopSpacing() const {
727   const int kPopupTopSpacingNonGlass = 3;
728   return GetWidget()->ShouldWindowContentsBeTransparent() ?
729       0 : kPopupTopSpacingNonGlass;
730 }
731
732 gfx::Size ToolbarView::SizeForContentSize(gfx::Size size) const {
733   if (is_display_mode_normal()) {
734     gfx::ImageSkia* normal_background =
735         GetThemeProvider()->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER);
736     size.SetToMax(
737         gfx::Size(0, normal_background->height() - content_shadow_height()));
738   } else {
739     const int kPopupBottomSpacingGlass = 1;
740     const int kPopupBottomSpacingNonGlass = 2;
741     size.Enlarge(
742         0,
743         PopupTopSpacing() + (GetWidget()->ShouldWindowContentsBeTransparent() ?
744             kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass));
745   }
746   return size;
747 }
748
749 void ToolbarView::LoadImages() {
750   ui::ThemeProvider* tp = GetThemeProvider();
751
752   back_->SetImage(views::Button::STATE_NORMAL,
753                   *(tp->GetImageSkiaNamed(IDR_BACK)));
754   back_->SetImage(views::Button::STATE_DISABLED,
755                   *(tp->GetImageSkiaNamed(IDR_BACK_D)));
756
757   forward_->SetImage(views::Button::STATE_NORMAL,
758                     *(tp->GetImageSkiaNamed(IDR_FORWARD)));
759   forward_->SetImage(views::Button::STATE_DISABLED,
760                      *(tp->GetImageSkiaNamed(IDR_FORWARD_D)));
761
762   reload_->LoadImages();
763
764   home_->SetImage(views::Button::STATE_NORMAL,
765                   *(tp->GetImageSkiaNamed(IDR_HOME)));
766 }
767
768 void ToolbarView::ShowCriticalNotification() {
769 #if defined(OS_WIN)
770   CriticalNotificationBubbleView* bubble_delegate =
771       new CriticalNotificationBubbleView(app_menu_);
772   views::BubbleDelegateView::CreateBubble(bubble_delegate)->Show();
773 #endif
774 }
775
776 void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) {
777   if (OutdatedUpgradeBubbleView::IsAvailable()) {
778     OutdatedUpgradeBubbleView::ShowBubble(
779         app_menu_, browser_, auto_update_enabled);
780   }
781 }
782
783 void ToolbarView::OnShowHomeButtonChanged() {
784   Layout();
785   SchedulePaint();
786 }
787
788 int ToolbarView::content_shadow_height() const {
789   return browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH ?
790       kContentShadowHeightAsh : kContentShadowHeight;
791 }