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