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