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