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)
HasAshShell()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
ToolbarView(Browser * browser)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
~ToolbarView()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
Init()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
OnWidgetVisibilityChanged(views::Widget * widget,bool visible)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
OnWidgetActivationChanged(views::Widget * widget,bool active)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
Update(WebContents * tab)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
SetPaneFocusAndFocusAppMenu()304 void ToolbarView::SetPaneFocusAndFocusAppMenu() {
305 SetPaneFocus(app_menu_);
306 }
307
IsAppMenuFocused()308 bool ToolbarView::IsAppMenuFocused() {
309 return app_menu_->HasFocus();
310 }
311
AddMenuListener(views::MenuListener * listener)312 void ToolbarView::AddMenuListener(views::MenuListener* listener) {
313 menu_listeners_.AddObserver(listener);
314 }
315
RemoveMenuListener(views::MenuListener * listener)316 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
317 menu_listeners_.RemoveObserver(listener);
318 }
319
GetBookmarkBubbleAnchor()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
GetTranslateBubbleAnchor()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
ExecuteExtensionCommand(const extensions::Extension * extension,const extensions::Command & command)331 void ToolbarView::ExecuteExtensionCommand(
332 const extensions::Extension* extension,
333 const extensions::Command& command) {
334 browser_actions_->ExecuteExtensionCommand(extension, command);
335 }
336
ShowAppMenu(bool for_drop)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
app_menu() const357 views::MenuButton* ToolbarView::app_menu() const {
358 return app_menu_;
359 }
360
361 ////////////////////////////////////////////////////////////////////////////////
362 // ToolbarView, AccessiblePaneView overrides:
363
SetPaneFocus(views::View * initial_focus)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
GetAccessibleState(ui::AXViewState * state)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
GetAcceleratorInfo(int id,ui::Accelerator * accel)380 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
381 return GetWidget()->GetAccelerator(id, accel);
382 }
383
384 ////////////////////////////////////////////////////////////////////////////////
385 // ToolbarView, views::MenuButtonListener implementation:
386
OnMenuButtonClicked(views::View * source,const gfx::Point & point)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
GetWebContents()397 WebContents* ToolbarView::GetWebContents() {
398 return browser_->tab_strip_model()->GetActiveWebContents();
399 }
400
GetToolbarModel()401 ToolbarModel* ToolbarView::GetToolbarModel() {
402 return browser_->toolbar_model();
403 }
404
GetToolbarModel() const405 const ToolbarModel* ToolbarView::GetToolbarModel() const {
406 return browser_->toolbar_model();
407 }
408
GetInstant()409 InstantController* ToolbarView::GetInstant() {
410 return browser_->instant_controller() ?
411 browser_->instant_controller()->instant() : NULL;
412 }
413
414 ContentSettingBubbleModelDelegate*
GetContentSettingBubbleModelDelegate()415 ToolbarView::GetContentSettingBubbleModelDelegate() {
416 return browser_->content_setting_bubble_model_delegate();
417 }
418
ShowWebsiteSettings(content::WebContents * web_contents,const GURL & url,const content::SSLStatus & ssl)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
CreateViewsBubble(views::BubbleDelegateView * bubble_delegate)425 views::Widget* ToolbarView::CreateViewsBubble(
426 views::BubbleDelegateView* bubble_delegate) {
427 return views::BubbleDelegateView::CreateBubble(bubble_delegate);
428 }
429
CreatePageActionImageView(LocationBarView * owner,ExtensionAction * action)430 PageActionImageView* ToolbarView::CreatePageActionImageView(
431 LocationBarView* owner, ExtensionAction* action) {
432 return new PageActionImageView(owner, action, browser_);
433 }
434
435 ////////////////////////////////////////////////////////////////////////////////
436 // ToolbarView, CommandObserver implementation:
437
EnabledStateChangedForCommand(int id,bool enabled)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
ButtonPressed(views::Button * sender,const ui::Event & event)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
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)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
GetPreferredSize() const501 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
GetMinimumSize() const515 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
Layout()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
OnPaint(gfx::Canvas * canvas)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
OnThemeChanged()629 void ToolbarView::OnThemeChanged() {
630 LoadImages();
631 }
632
GetClassName() const633 const char* ToolbarView::GetClassName() const {
634 return kViewClassName;
635 }
636
AcceleratorPressed(const ui::Accelerator & accelerator)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
IsWrenchMenuShowing() const644 bool ToolbarView::IsWrenchMenuShowing() const {
645 return wrench_menu_.get() && wrench_menu_->IsShowing();
646 }
647
ShouldPaintBackground() const648 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.
SetPaneFocusAndFocusDefault()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
RemovePaneFocus()671 void ToolbarView::RemovePaneFocus() {
672 AccessiblePaneView::RemovePaneFocus();
673 location_bar_->SetShowFocusRect(false);
674 }
675
676 ////////////////////////////////////////////////////////////////////////////////
677 // ToolbarView, private:
678
679 // views::ViewTargeterDelegate:
DoesIntersectRect(const views::View * target,const gfx::Rect & rect) const680 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
UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,WrenchIconPainter::Severity severity,bool animate)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
PopupTopSpacing() const727 int ToolbarView::PopupTopSpacing() const {
728 const int kPopupTopSpacingNonGlass = 3;
729 return GetWidget()->ShouldWindowContentsBeTransparent() ?
730 0 : kPopupTopSpacingNonGlass;
731 }
732
SizeForContentSize(gfx::Size size) const733 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
LoadImages()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
ShowCriticalNotification()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
ShowOutdatedInstallNotification(bool auto_update_enabled)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
OnShowHomeButtonChanged()784 void ToolbarView::OnShowHomeButtonChanged() {
785 Layout();
786 SchedulePaint();
787 }
788
content_shadow_height() const789 int ToolbarView::content_shadow_height() const {
790 return browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH ?
791 kContentShadowHeightAsh : kContentShadowHeight;
792 }
793