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