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