1 // Copyright (c) 2011 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/i18n/number_formatting.h" 8 #include "base/utf_string_conversions.h" 9 #include "chrome/app/chrome_command_ids.h" 10 #include "chrome/browser/accessibility/browser_accessibility_state.h" 11 #include "chrome/browser/metrics/user_metrics.h" 12 #include "chrome/browser/prefs/pref_service.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/ui/browser.h" 15 #include "chrome/browser/ui/browser_window.h" 16 #include "chrome/browser/ui/toolbar/wrench_menu_model.h" 17 #include "chrome/browser/ui/view_ids.h" 18 #include "chrome/browser/ui/views/browser_actions_container.h" 19 #include "chrome/browser/ui/views/event_utils.h" 20 #include "chrome/browser/upgrade_detector.h" 21 #include "chrome/common/pref_names.h" 22 #include "content/common/notification_service.h" 23 #include "grit/chromium_strings.h" 24 #include "grit/generated_resources.h" 25 #include "grit/theme_resources.h" 26 #include "ui/base/accessibility/accessible_view_state.h" 27 #include "ui/base/l10n/l10n_util.h" 28 #include "ui/base/resource/resource_bundle.h" 29 #include "ui/base/theme_provider.h" 30 #include "ui/gfx/canvas.h" 31 #include "ui/gfx/canvas_skia.h" 32 #include "ui/gfx/skbitmap_operations.h" 33 #include "views/controls/button/button_dropdown.h" 34 #include "views/focus/view_storage.h" 35 #include "views/widget/tooltip_manager.h" 36 #include "views/window/non_client_view.h" 37 #include "views/window/window.h" 38 39 #if defined(OS_CHROMEOS) 40 #include "chrome/browser/chromeos/cros/cros_library.h" 41 #include "chrome/browser/chromeos/cros/update_library.h" 42 #include "views/controls/menu/menu_2.h" 43 #endif 44 #include "chrome/browser/ui/views/wrench_menu.h" 45 46 #if defined(OS_WIN) 47 #include "chrome/browser/enumerate_modules_model_win.h" 48 #endif 49 50 // The space between items is 4 px in general. 51 const int ToolbarView::kStandardSpacing = 4; 52 // The top of the toolbar has an edge we have to skip over in addition to the 4 53 // px of spacing. 54 const int ToolbarView::kVertSpacing = kStandardSpacing + 1; 55 // The edge graphics have some built-in spacing/shadowing, so we have to adjust 56 // our spacing to make it still appear to be 4 px. 57 static const int kEdgeSpacing = ToolbarView::kStandardSpacing - 1; 58 // The buttons to the left of the omnibox are close together. 59 static const int kButtonSpacing = 1; 60 61 static const int kStatusBubbleWidth = 480; 62 63 // The length of time to run the upgrade notification animation (the time it 64 // takes one pulse to run its course and go back to its original brightness). 65 static const int kPulseDuration = 2000; 66 67 // How long to wait between pulsating the upgrade notifier. 68 static const int kPulsateEveryMs = 8000; 69 70 static const int kPopupTopSpacingNonGlass = 3; 71 static const int kPopupBottomSpacingNonGlass = 2; 72 static const int kPopupBottomSpacingGlass = 1; 73 74 // Top margin for the wrench menu badges (badge is placed in the upper right 75 // corner of the wrench menu). 76 static const int kBadgeTopMargin = 2; 77 78 static SkBitmap* kPopupBackgroundEdge = NULL; 79 80 //////////////////////////////////////////////////////////////////////////////// 81 // ToolbarView, public: 82 83 ToolbarView::ToolbarView(Browser* browser) 84 : model_(browser->toolbar_model()), 85 back_(NULL), 86 forward_(NULL), 87 reload_(NULL), 88 home_(NULL), 89 location_bar_(NULL), 90 browser_actions_(NULL), 91 app_menu_(NULL), 92 profile_(NULL), 93 browser_(browser), 94 profiles_menu_contents_(NULL), 95 destroyed_flag_(NULL) { 96 SetID(VIEW_ID_TOOLBAR); 97 98 browser_->command_updater()->AddCommandObserver(IDC_BACK, this); 99 browser_->command_updater()->AddCommandObserver(IDC_FORWARD, this); 100 browser_->command_updater()->AddCommandObserver(IDC_HOME, this); 101 102 display_mode_ = browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ? 103 DISPLAYMODE_NORMAL : DISPLAYMODE_LOCATION; 104 105 if (!kPopupBackgroundEdge) { 106 kPopupBackgroundEdge = ResourceBundle::GetSharedInstance().GetBitmapNamed( 107 IDR_LOCATIONBG_POPUPMODE_EDGE); 108 } 109 110 if (!IsUpgradeRecommended()) { 111 registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED, 112 NotificationService::AllSources()); 113 } 114 registrar_.Add(this, NotificationType::MODULE_INCOMPATIBILITY_BADGE_CHANGE, 115 NotificationService::AllSources()); 116 } 117 118 ToolbarView::~ToolbarView() { 119 if (destroyed_flag_) 120 *destroyed_flag_ = true; 121 122 // NOTE: Don't remove the command observers here. This object gets destroyed 123 // after the Browser (which owns the CommandUpdater), so the CommandUpdater is 124 // already gone. 125 } 126 127 void ToolbarView::Init(Profile* profile) { 128 back_menu_model_.reset(new BackForwardMenuModel( 129 browser_, BackForwardMenuModel::BACKWARD_MENU)); 130 forward_menu_model_.reset(new BackForwardMenuModel( 131 browser_, BackForwardMenuModel::FORWARD_MENU)); 132 wrench_menu_model_.reset(new WrenchMenuModel(this, browser_)); 133 back_ = new views::ButtonDropDown(this, back_menu_model_.get()); 134 back_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN | 135 ui::EF_MIDDLE_BUTTON_DOWN); 136 back_->set_tag(IDC_BACK); 137 back_->SetImageAlignment(views::ImageButton::ALIGN_RIGHT, 138 views::ImageButton::ALIGN_TOP); 139 back_->SetTooltipText( 140 UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK))); 141 back_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK)); 142 back_->SetID(VIEW_ID_BACK_BUTTON); 143 144 forward_ = new views::ButtonDropDown(this, forward_menu_model_.get()); 145 forward_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN | 146 ui::EF_MIDDLE_BUTTON_DOWN); 147 forward_->set_tag(IDC_FORWARD); 148 forward_->SetTooltipText( 149 UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD))); 150 forward_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD)); 151 forward_->SetID(VIEW_ID_FORWARD_BUTTON); 152 153 // Have to create this before |reload_| as |reload_|'s constructor needs it. 154 location_bar_ = new LocationBarView(profile, browser_->command_updater(), 155 model_, this, (display_mode_ == DISPLAYMODE_LOCATION) ? 156 LocationBarView::POPUP : LocationBarView::NORMAL); 157 158 reload_ = new ReloadButton(location_bar_, browser_); 159 reload_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN | 160 ui::EF_MIDDLE_BUTTON_DOWN); 161 reload_->set_tag(IDC_RELOAD); 162 reload_->SetTooltipText( 163 UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_RELOAD))); 164 reload_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD)); 165 reload_->SetID(VIEW_ID_RELOAD_BUTTON); 166 167 home_ = new views::ImageButton(this); 168 home_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN | 169 ui::EF_MIDDLE_BUTTON_DOWN); 170 home_->set_tag(IDC_HOME); 171 home_->SetTooltipText( 172 UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_HOME))); 173 home_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_HOME)); 174 home_->SetID(VIEW_ID_HOME_BUTTON); 175 176 browser_actions_ = new BrowserActionsContainer(browser_, this); 177 178 app_menu_ = new views::MenuButton(NULL, std::wstring(), this, false); 179 app_menu_->set_border(NULL); 180 app_menu_->EnableCanvasFlippingForRTLUI(true); 181 app_menu_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_APP)); 182 app_menu_->SetTooltipText(UTF16ToWide(l10n_util::GetStringFUTF16( 183 IDS_APPMENU_TOOLTIP, 184 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)))); 185 app_menu_->SetID(VIEW_ID_APP_MENU); 186 187 // Add any necessary badges to the menu item based on the system state. 188 if (IsUpgradeRecommended() || ShouldShowIncompatibilityWarning()) { 189 UpdateAppMenuBadge(); 190 } 191 LoadImages(); 192 193 // Always add children in order from left to right, for accessibility. 194 AddChildView(back_); 195 AddChildView(forward_); 196 AddChildView(reload_); 197 AddChildView(home_); 198 AddChildView(location_bar_); 199 AddChildView(browser_actions_); 200 AddChildView(app_menu_); 201 202 location_bar_->Init(); 203 show_home_button_.Init(prefs::kShowHomeButton, profile->GetPrefs(), this); 204 browser_actions_->Init(); 205 206 SetProfile(profile); 207 208 // Accessibility specific tooltip text. 209 if (BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) { 210 back_->SetTooltipText( 211 UTF16ToWide(l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK))); 212 forward_->SetTooltipText( 213 UTF16ToWide(l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD))); 214 } 215 } 216 217 void ToolbarView::SetProfile(Profile* profile) { 218 if (profile != profile_) { 219 profile_ = profile; 220 location_bar_->SetProfile(profile); 221 } 222 } 223 224 void ToolbarView::Update(TabContents* tab, bool should_restore_state) { 225 if (location_bar_) 226 location_bar_->Update(should_restore_state ? tab : NULL); 227 228 if (browser_actions_) 229 browser_actions_->RefreshBrowserActionViews(); 230 } 231 232 void ToolbarView::SetPaneFocusAndFocusLocationBar(int view_storage_id) { 233 SetPaneFocus(view_storage_id, location_bar_); 234 } 235 236 void ToolbarView::SetPaneFocusAndFocusAppMenu(int view_storage_id) { 237 SetPaneFocus(view_storage_id, app_menu_); 238 } 239 240 bool ToolbarView::IsAppMenuFocused() { 241 return app_menu_->HasFocus(); 242 } 243 244 void ToolbarView::AddMenuListener(views::MenuListener* listener) { 245 menu_listeners_.push_back(listener); 246 } 247 248 void ToolbarView::RemoveMenuListener(views::MenuListener* listener) { 249 for (std::vector<views::MenuListener*>::iterator i(menu_listeners_.begin()); 250 i != menu_listeners_.end(); ++i) { 251 if (*i == listener) { 252 menu_listeners_.erase(i); 253 return; 254 } 255 } 256 } 257 258 //////////////////////////////////////////////////////////////////////////////// 259 // ToolbarView, AccessiblePaneView overrides: 260 261 bool ToolbarView::SetPaneFocus( 262 int view_storage_id, views::View* initial_focus) { 263 if (!AccessiblePaneView::SetPaneFocus(view_storage_id, initial_focus)) 264 return false; 265 266 location_bar_->SetShowFocusRect(true); 267 return true; 268 } 269 270 void ToolbarView::GetAccessibleState(ui::AccessibleViewState* state) { 271 state->role = ui::AccessibilityTypes::ROLE_TOOLBAR; 272 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR); 273 } 274 275 //////////////////////////////////////////////////////////////////////////////// 276 // ToolbarView, Menu::Delegate overrides: 277 278 bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) { 279 return GetWidget()->GetAccelerator(id, accel); 280 } 281 282 //////////////////////////////////////////////////////////////////////////////// 283 // ToolbarView, views::MenuDelegate implementation: 284 285 void ToolbarView::RunMenu(views::View* source, const gfx::Point& /* pt */) { 286 DCHECK_EQ(VIEW_ID_APP_MENU, source->GetID()); 287 288 bool destroyed_flag = false; 289 destroyed_flag_ = &destroyed_flag; 290 wrench_menu_ = new WrenchMenu(browser_); 291 wrench_menu_->Init(wrench_menu_model_.get()); 292 293 for (size_t i = 0; i < menu_listeners_.size(); ++i) 294 menu_listeners_[i]->OnMenuOpened(); 295 296 wrench_menu_->RunMenu(app_menu_); 297 298 if (destroyed_flag) 299 return; 300 destroyed_flag_ = NULL; 301 } 302 303 //////////////////////////////////////////////////////////////////////////////// 304 // ToolbarView, LocationBarView::Delegate implementation: 305 306 TabContentsWrapper* ToolbarView::GetTabContentsWrapper() const { 307 return browser_->GetSelectedTabContentsWrapper(); 308 } 309 310 InstantController* ToolbarView::GetInstant() { 311 return browser_->instant(); 312 } 313 314 void ToolbarView::OnInputInProgress(bool in_progress) { 315 // The edit should make sure we're only notified when something changes. 316 DCHECK(model_->input_in_progress() != in_progress); 317 318 model_->set_input_in_progress(in_progress); 319 location_bar_->Update(NULL); 320 } 321 322 //////////////////////////////////////////////////////////////////////////////// 323 // ToolbarView, CommandUpdater::CommandObserver implementation: 324 325 void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) { 326 views::Button* button = NULL; 327 switch (id) { 328 case IDC_BACK: 329 button = back_; 330 break; 331 case IDC_FORWARD: 332 button = forward_; 333 break; 334 case IDC_HOME: 335 button = home_; 336 break; 337 } 338 if (button) 339 button->SetEnabled(enabled); 340 } 341 342 //////////////////////////////////////////////////////////////////////////////// 343 // ToolbarView, views::Button::ButtonListener implementation: 344 345 void ToolbarView::ButtonPressed(views::Button* sender, 346 const views::Event& event) { 347 int command = sender->tag(); 348 WindowOpenDisposition disposition = 349 event_utils::DispositionFromEventFlags(sender->mouse_event_flags()); 350 if ((disposition == CURRENT_TAB) && 351 ((command == IDC_BACK) || (command == IDC_FORWARD))) { 352 // Forcibly reset the location bar, since otherwise it won't discard any 353 // ongoing user edits, since it doesn't realize this is a user-initiated 354 // action. 355 location_bar_->Revert(); 356 } 357 browser_->ExecuteCommandWithDisposition(command, disposition); 358 } 359 360 //////////////////////////////////////////////////////////////////////////////// 361 // ToolbarView, NotificationObserver implementation: 362 363 void ToolbarView::Observe(NotificationType type, 364 const NotificationSource& source, 365 const NotificationDetails& details) { 366 if (type == NotificationType::PREF_CHANGED) { 367 std::string* pref_name = Details<std::string>(details).ptr(); 368 if (*pref_name == prefs::kShowHomeButton) { 369 Layout(); 370 SchedulePaint(); 371 } 372 } else if (type == NotificationType::UPGRADE_RECOMMENDED || 373 type == NotificationType::MODULE_INCOMPATIBILITY_BADGE_CHANGE) { 374 UpdateAppMenuBadge(); 375 } 376 } 377 378 //////////////////////////////////////////////////////////////////////////////// 379 // ToolbarView, ui::AcceleratorProvider implementation: 380 381 bool ToolbarView::GetAcceleratorForCommandId(int command_id, 382 ui::Accelerator* accelerator) { 383 // The standard Ctrl-X, Ctrl-V and Ctrl-C are not defined as accelerators 384 // anywhere so we need to check for them explicitly here. 385 // TODO(cpu) Bug 1109102. Query WebKit land for the actual bindings. 386 switch (command_id) { 387 case IDC_CUT: 388 *accelerator = views::Accelerator(ui::VKEY_X, false, true, false); 389 return true; 390 case IDC_COPY: 391 *accelerator = views::Accelerator(ui::VKEY_C, false, true, false); 392 return true; 393 case IDC_PASTE: 394 *accelerator = views::Accelerator(ui::VKEY_V, false, true, false); 395 return true; 396 } 397 // Else, we retrieve the accelerator information from the frame. 398 return GetWidget()->GetAccelerator(command_id, accelerator); 399 } 400 401 //////////////////////////////////////////////////////////////////////////////// 402 // ToolbarView, views::View overrides: 403 404 gfx::Size ToolbarView::GetPreferredSize() { 405 if (IsDisplayModeNormal()) { 406 int min_width = kEdgeSpacing + 407 back_->GetPreferredSize().width() + kButtonSpacing + 408 forward_->GetPreferredSize().width() + kButtonSpacing + 409 reload_->GetPreferredSize().width() + kStandardSpacing + 410 (show_home_button_.GetValue() ? 411 (home_->GetPreferredSize().width() + kButtonSpacing) : 0) + 412 browser_actions_->GetPreferredSize().width() + 413 app_menu_->GetPreferredSize().width() + kEdgeSpacing; 414 415 static SkBitmap normal_background; 416 if (normal_background.isNull()) { 417 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 418 normal_background = *rb.GetBitmapNamed(IDR_CONTENT_TOP_CENTER); 419 } 420 421 return gfx::Size(min_width, normal_background.height()); 422 } 423 424 int vertical_spacing = PopupTopSpacing() + 425 (GetWindow()->non_client_view()->UseNativeFrame() ? 426 kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass); 427 return gfx::Size(0, location_bar_->GetPreferredSize().height() + 428 vertical_spacing); 429 } 430 431 void ToolbarView::Layout() { 432 // If we have not been initialized yet just do nothing. 433 if (back_ == NULL) 434 return; 435 436 bool maximized = browser_->window() && browser_->window()->IsMaximized(); 437 if (!IsDisplayModeNormal()) { 438 int edge_width = maximized ? 439 0 : kPopupBackgroundEdge->width(); // See OnPaint(). 440 location_bar_->SetBounds(edge_width, PopupTopSpacing(), 441 width() - (edge_width * 2), location_bar_->GetPreferredSize().height()); 442 return; 443 } 444 445 int child_y = std::min(kVertSpacing, height()); 446 // We assume all child elements are the same height. 447 int child_height = 448 std::min(back_->GetPreferredSize().height(), height() - child_y); 449 450 // If the window is maximized, we extend the back button to the left so that 451 // clicking on the left-most pixel will activate the back button. 452 // TODO(abarth): If the window becomes maximized but is not resized, 453 // then Layout() might not be called and the back button 454 // will be slightly the wrong size. We should force a 455 // Layout() in this case. 456 // http://crbug.com/5540 457 int back_width = back_->GetPreferredSize().width(); 458 if (maximized) 459 back_->SetBounds(0, child_y, back_width + kEdgeSpacing, child_height); 460 else 461 back_->SetBounds(kEdgeSpacing, child_y, back_width, child_height); 462 463 forward_->SetBounds(back_->x() + back_->width() + kButtonSpacing, 464 child_y, forward_->GetPreferredSize().width(), child_height); 465 466 reload_->SetBounds(forward_->x() + forward_->width() + kButtonSpacing, 467 child_y, reload_->GetPreferredSize().width(), child_height); 468 469 if (show_home_button_.GetValue()) { 470 home_->SetVisible(true); 471 home_->SetBounds(reload_->x() + reload_->width() + kButtonSpacing, child_y, 472 home_->GetPreferredSize().width(), child_height); 473 } else { 474 home_->SetVisible(false); 475 home_->SetBounds(reload_->x() + reload_->width(), child_y, 0, child_height); 476 } 477 478 int browser_actions_width = browser_actions_->GetPreferredSize().width(); 479 int app_menu_width = app_menu_->GetPreferredSize().width(); 480 int location_x = home_->x() + home_->width() + kStandardSpacing; 481 int available_width = width() - kEdgeSpacing - app_menu_width - 482 browser_actions_width - location_x; 483 484 location_bar_->SetBounds(location_x, child_y, std::max(available_width, 0), 485 child_height); 486 487 browser_actions_->SetBounds(location_bar_->x() + location_bar_->width(), 0, 488 browser_actions_width, height()); 489 // The browser actions need to do a layout explicitly, because when an 490 // extension is loaded/unloaded/changed, BrowserActionContainer removes and 491 // re-adds everything, regardless of whether it has a page action. For a 492 // page action, browser action bounds do not change, as a result of which 493 // SetBounds does not do a layout at all. 494 // TODO(sidchat): Rework the above behavior so that explicit layout is not 495 // required. 496 browser_actions_->Layout(); 497 498 // Extend the app menu to the screen's right edge in maximized mode just like 499 // we extend the back button to the left edge. 500 if (maximized) 501 app_menu_width += kEdgeSpacing; 502 app_menu_->SetBounds(browser_actions_->x() + browser_actions_width, child_y, 503 app_menu_width, child_height); 504 } 505 506 void ToolbarView::OnPaint(gfx::Canvas* canvas) { 507 View::OnPaint(canvas); 508 509 if (IsDisplayModeNormal()) 510 return; 511 512 // In maximized mode, we don't draw the endcaps on the location bar, because 513 // when they're flush against the edge of the screen they just look glitchy. 514 if (!browser_->window() || !browser_->window()->IsMaximized()) { 515 int top_spacing = PopupTopSpacing(); 516 canvas->DrawBitmapInt(*kPopupBackgroundEdge, 0, top_spacing); 517 canvas->DrawBitmapInt(*kPopupBackgroundEdge, 518 width() - kPopupBackgroundEdge->width(), top_spacing); 519 } 520 521 // For glass, we need to draw a black line below the location bar to separate 522 // it from the content area. For non-glass, the NonClientView draws the 523 // toolbar background below the location bar for us. 524 // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()! 525 if (GetWindow()->non_client_view()->UseNativeFrame()) 526 canvas->FillRectInt(SK_ColorBLACK, 0, height() - 1, width(), 1); 527 } 528 529 // Note this method is ignored on Windows, but needs to be implemented for 530 // linux, where it is called before CanDrop(). 531 bool ToolbarView::GetDropFormats( 532 int* formats, 533 std::set<OSExchangeData::CustomFormat>* custom_formats) { 534 *formats = ui::OSExchangeData::URL | ui::OSExchangeData::STRING; 535 return true; 536 } 537 538 bool ToolbarView::CanDrop(const ui::OSExchangeData& data) { 539 // To support loading URLs by dropping into the toolbar, we need to support 540 // dropping URLs and/or text. 541 return data.HasURL() || data.HasString(); 542 } 543 544 int ToolbarView::OnDragUpdated(const views::DropTargetEvent& event) { 545 if (event.source_operations() & ui::DragDropTypes::DRAG_COPY) { 546 return ui::DragDropTypes::DRAG_COPY; 547 } else if (event.source_operations() & ui::DragDropTypes::DRAG_LINK) { 548 return ui::DragDropTypes::DRAG_LINK; 549 } 550 return ui::DragDropTypes::DRAG_NONE; 551 } 552 553 int ToolbarView::OnPerformDrop(const views::DropTargetEvent& event) { 554 return location_bar_->location_entry()->OnPerformDrop(event); 555 } 556 557 void ToolbarView::OnThemeChanged() { 558 LoadImages(); 559 } 560 561 //////////////////////////////////////////////////////////////////////////////// 562 // ToolbarView, protected: 563 564 // Override this so that when the user presses F6 to rotate toolbar panes, 565 // the location bar gets focus, not the first control in the toolbar. 566 views::View* ToolbarView::GetDefaultFocusableChild() { 567 return location_bar_; 568 } 569 570 void ToolbarView::RemovePaneFocus() { 571 AccessiblePaneView::RemovePaneFocus(); 572 location_bar_->SetShowFocusRect(false); 573 } 574 575 //////////////////////////////////////////////////////////////////////////////// 576 // ToolbarView, private: 577 578 bool ToolbarView::IsUpgradeRecommended() { 579 #if defined(OS_CHROMEOS) 580 return (chromeos::CrosLibrary::Get()->GetUpdateLibrary()->status().status == 581 chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT); 582 #else 583 return (UpgradeDetector::GetInstance()->notify_upgrade()); 584 #endif 585 } 586 587 int ToolbarView::GetUpgradeRecommendedBadge() const { 588 #if defined(OS_CHROMEOS) 589 return IDR_UPDATE_BADGE; 590 #else 591 switch (UpgradeDetector::GetInstance()->upgrade_notification_stage()) { 592 case UpgradeDetector::UPGRADE_ANNOYANCE_SEVERE: 593 return IDR_UPDATE_BADGE4; 594 case UpgradeDetector::UPGRADE_ANNOYANCE_HIGH: 595 return IDR_UPDATE_BADGE3; 596 case UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED: 597 return IDR_UPDATE_BADGE2; 598 default: 599 return IDR_UPDATE_BADGE; 600 } 601 #endif 602 } 603 604 bool ToolbarView::ShouldShowIncompatibilityWarning() { 605 #if defined(OS_WIN) 606 EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetInstance(); 607 return loaded_modules->ShouldShowConflictWarning(); 608 #else 609 return false; 610 #endif 611 } 612 613 int ToolbarView::PopupTopSpacing() const { 614 // TODO(beng): For some reason GetWindow() returns NULL here in some 615 // unidentified circumstances on ChromeOS. This means GetWidget() 616 // succeeded but we were (probably) unable to locate a WidgetGtk* 617 // on it using NativeWidget::GetNativeWidgetForNativeView. 618 // I am throwing in a NULL check for now to stop the hurt, but 619 // it's possible the crash may just show up somewhere else. 620 const views::Window* window = GetWindow(); 621 DCHECK(window) << "If you hit this please talk to beng"; 622 return window && window->non_client_view()->UseNativeFrame() ? 623 0 : kPopupTopSpacingNonGlass; 624 } 625 626 void ToolbarView::LoadImages() { 627 ui::ThemeProvider* tp = GetThemeProvider(); 628 629 back_->SetImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_BACK)); 630 back_->SetImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_BACK_H)); 631 back_->SetImage(views::CustomButton::BS_PUSHED, 632 tp->GetBitmapNamed(IDR_BACK_P)); 633 back_->SetImage(views::CustomButton::BS_DISABLED, 634 tp->GetBitmapNamed(IDR_BACK_D)); 635 636 forward_->SetImage(views::CustomButton::BS_NORMAL, 637 tp->GetBitmapNamed(IDR_FORWARD)); 638 forward_->SetImage(views::CustomButton::BS_HOT, 639 tp->GetBitmapNamed(IDR_FORWARD_H)); 640 forward_->SetImage(views::CustomButton::BS_PUSHED, 641 tp->GetBitmapNamed(IDR_FORWARD_P)); 642 forward_->SetImage(views::CustomButton::BS_DISABLED, 643 tp->GetBitmapNamed(IDR_FORWARD_D)); 644 645 reload_->SetImage(views::CustomButton::BS_NORMAL, 646 tp->GetBitmapNamed(IDR_RELOAD)); 647 reload_->SetImage(views::CustomButton::BS_HOT, 648 tp->GetBitmapNamed(IDR_RELOAD_H)); 649 reload_->SetImage(views::CustomButton::BS_PUSHED, 650 tp->GetBitmapNamed(IDR_RELOAD_P)); 651 reload_->SetToggledImage(views::CustomButton::BS_NORMAL, 652 tp->GetBitmapNamed(IDR_STOP)); 653 reload_->SetToggledImage(views::CustomButton::BS_HOT, 654 tp->GetBitmapNamed(IDR_STOP_H)); 655 reload_->SetToggledImage(views::CustomButton::BS_PUSHED, 656 tp->GetBitmapNamed(IDR_STOP_P)); 657 reload_->SetToggledImage(views::CustomButton::BS_DISABLED, 658 tp->GetBitmapNamed(IDR_STOP_D)); 659 660 home_->SetImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_HOME)); 661 home_->SetImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_HOME_H)); 662 home_->SetImage(views::CustomButton::BS_PUSHED, 663 tp->GetBitmapNamed(IDR_HOME_P)); 664 665 app_menu_->SetIcon(GetAppMenuIcon(views::CustomButton::BS_NORMAL)); 666 app_menu_->SetHoverIcon(GetAppMenuIcon(views::CustomButton::BS_HOT)); 667 app_menu_->SetPushedIcon(GetAppMenuIcon(views::CustomButton::BS_PUSHED)); 668 } 669 670 void ToolbarView::UpdateAppMenuBadge() { 671 app_menu_->SetIcon(GetAppMenuIcon(views::CustomButton::BS_NORMAL)); 672 app_menu_->SetHoverIcon(GetAppMenuIcon(views::CustomButton::BS_HOT)); 673 app_menu_->SetPushedIcon(GetAppMenuIcon(views::CustomButton::BS_PUSHED)); 674 SchedulePaint(); 675 } 676 677 SkBitmap ToolbarView::GetAppMenuIcon(views::CustomButton::ButtonState state) { 678 ui::ThemeProvider* tp = GetThemeProvider(); 679 680 int id = 0; 681 switch (state) { 682 case views::CustomButton::BS_NORMAL: id = IDR_TOOLS; break; 683 case views::CustomButton::BS_HOT: id = IDR_TOOLS_H; break; 684 case views::CustomButton::BS_PUSHED: id = IDR_TOOLS_P; break; 685 default: NOTREACHED(); break; 686 } 687 SkBitmap icon = *tp->GetBitmapNamed(id); 688 689 #if defined(OS_WIN) 690 // Keep track of whether we were showing the badge before, so we don't send 691 // multiple UMA events for example when multiple Chrome windows are open. 692 static bool incompatibility_badge_showing = false; 693 // Save the old value before resetting it. 694 bool was_showing = incompatibility_badge_showing; 695 incompatibility_badge_showing = false; 696 #endif 697 698 bool add_badge = IsUpgradeRecommended() || ShouldShowIncompatibilityWarning(); 699 if (!add_badge) 700 return icon; 701 702 // Draw the chrome app menu icon onto the canvas. 703 scoped_ptr<gfx::CanvasSkia> canvas( 704 new gfx::CanvasSkia(icon.width(), icon.height(), false)); 705 canvas->DrawBitmapInt(icon, 0, 0); 706 707 SkBitmap badge; 708 // Only one badge can be active at any given time. The Upgrade notification 709 // is deemed most important, then the DLL conflict badge. 710 if (IsUpgradeRecommended()) { 711 badge = *tp->GetBitmapNamed(GetUpgradeRecommendedBadge()); 712 } else if (ShouldShowIncompatibilityWarning()) { 713 #if defined(OS_WIN) 714 if (!was_showing) 715 UserMetrics::RecordAction(UserMetricsAction("ConflictBadge"), profile_); 716 badge = *tp->GetBitmapNamed(IDR_CONFLICT_BADGE); 717 incompatibility_badge_showing = true; 718 #else 719 NOTREACHED(); 720 #endif 721 } else { 722 NOTREACHED(); 723 } 724 725 canvas->DrawBitmapInt(badge, icon.width() - badge.width(), kBadgeTopMargin); 726 727 return canvas->ExtractBitmap(); 728 } 729