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