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