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