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