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