toolbar_view.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/toolbar/toolbar_view.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h" 82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/debug/trace_event.h" 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/number_formatting.h" 102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/app/chrome_command_ids.h" 132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chrome_notification_types.h" 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/command_updater.h" 152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/extension_action.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_action_manager.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/search/search.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/themes/theme_service.h" 202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_command_controller.h" 222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_commands.h" 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h" 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_instant_controller.h" 252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_tabstrip.h" 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_window.h" 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/global_error/global_error_service.h" 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/global_error/global_error_service_factory.h" 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/omnibox/omnibox_view.h" 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h" 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/toolbar/wrench_menu_model.h" 322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/view_ids.h" 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/extensions/extension_message_bubble_view.h" 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/extensions/extension_popup.h" 352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/frame/browser_view.h" 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/location_bar/page_action_image_view.h" 372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/location_bar/page_action_with_badge_view.h" 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/location_bar/star_view.h" 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/location_bar/translate_icon_view.h" 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/outdated_upgrade_bubble_view.h" 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/back_button.h" 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/browser_actions_container.h" 432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/home_button.h" 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/toolbar/reload_button.h" 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/toolbar/toolbar_button.h" 462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/toolbar_origin_chip_view.h" 472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/wrench_menu.h" 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h" 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/upgrade_detector.h" 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h" 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h" 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_accessibility_state.h" 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h" 54#include "content/public/browser/render_view_host.h" 55#include "content/public/browser/user_metrics.h" 56#include "content/public/browser/web_contents.h" 57#include "content/public/browser/web_contents_view.h" 58#include "grit/chromium_strings.h" 59#include "grit/generated_resources.h" 60#include "grit/theme_resources.h" 61#include "ui/accessibility/ax_view_state.h" 62#include "ui/base/l10n/l10n_util.h" 63#include "ui/base/layout.h" 64#include "ui/base/theme_provider.h" 65#include "ui/base/window_open_disposition.h" 66#include "ui/gfx/canvas.h" 67#include "ui/gfx/image/canvas_image_source.h" 68#include "ui/views/controls/menu/menu_listener.h" 69#include "ui/views/focus/view_storage.h" 70#include "ui/views/widget/tooltip_manager.h" 71#include "ui/views/widget/widget.h" 72#include "ui/views/window/non_client_view.h" 73 74#if defined(OS_WIN) 75#include "base/win/windows_version.h" 76#include "chrome/browser/enumerate_modules_model_win.h" 77#include "chrome/browser/ui/views/conflicting_module_view_win.h" 78#include "chrome/browser/ui/views/critical_notification_bubble_view.h" 79#endif 80 81#if defined(USE_AURA) 82#include "ui/aura/window.h" 83#include "ui/compositor/layer.h" 84#include "ui/native_theme/native_theme_aura.h" 85#endif 86 87#if !defined(OS_CHROMEOS) 88#include "chrome/browser/signin/signin_global_error_factory.h" 89#include "chrome/browser/sync/sync_global_error_factory.h" 90#endif 91 92#if defined(USE_ASH) 93#include "ash/shell.h" 94#endif 95 96using base::UserMetricsAction; 97using content::WebContents; 98 99namespace { 100 101// The edge graphics have some built-in spacing/shadowing, so we have to adjust 102// our spacing to make it match. 103const int kLeftEdgeSpacing = 3; 104const int kRightEdgeSpacing = 2; 105 106// Ash doesn't use a rounded content area and its top edge has an extra shadow. 107const int kContentShadowHeightAsh = 2; 108 109// Non-ash uses a rounded content area with no shadow in the assets. 110const int kContentShadowHeight = 0; 111 112int GetButtonSpacing() { 113 return (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) ? 114 ToolbarView::kStandardSpacing : 0; 115} 116 117bool IsStreamlinedHostedAppsEnabled() { 118 return CommandLine::ForCurrentProcess()->HasSwitch( 119 switches::kEnableStreamlinedHostedApps); 120} 121 122#if !defined(OS_CHROMEOS) 123bool HasAshShell() { 124#if defined(USE_ASH) 125 return ash::Shell::HasInstance(); 126#else 127 return false; 128#endif // USE_ASH 129} 130#endif // OS_CHROMEOS 131 132} // namespace 133 134// static 135const char ToolbarView::kViewClassName[] = "ToolbarView"; 136 137//////////////////////////////////////////////////////////////////////////////// 138// ToolbarView, public: 139 140ToolbarView::ToolbarView(Browser* browser) 141 : back_(NULL), 142 forward_(NULL), 143 reload_(NULL), 144 home_(NULL), 145 location_bar_(NULL), 146 origin_chip_view_(NULL), 147 browser_actions_(NULL), 148 app_menu_(NULL), 149 browser_(browser), 150 extension_message_bubble_factory_( 151 new extensions::ExtensionMessageBubbleFactory(browser->profile(), 152 this)) { 153 set_id(VIEW_ID_TOOLBAR); 154 155 chrome::AddCommandObserver(browser_, IDC_BACK, this); 156 chrome::AddCommandObserver(browser_, IDC_FORWARD, this); 157 chrome::AddCommandObserver(browser_, IDC_RELOAD, this); 158 chrome::AddCommandObserver(browser_, IDC_HOME, this); 159 chrome::AddCommandObserver(browser_, IDC_LOAD_NEW_TAB_PAGE, this); 160 161 display_mode_ = DISPLAYMODE_LOCATION; 162 if (browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) || 163 (browser->is_app() && IsStreamlinedHostedAppsEnabled())) 164 display_mode_ = DISPLAYMODE_NORMAL; 165 166 registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED, 167 content::NotificationService::AllSources()); 168 if (OutdatedUpgradeBubbleView::IsAvailable()) { 169 registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL, 170 content::NotificationService::AllSources()); 171 registrar_.Add(this, chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU, 172 content::NotificationService::AllSources()); 173 } 174#if defined(OS_WIN) 175 registrar_.Add(this, chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED, 176 content::NotificationService::AllSources()); 177 if (base::win::GetVersion() == base::win::VERSION_XP) { 178 registrar_.Add(this, chrome::NOTIFICATION_MODULE_LIST_ENUMERATED, 179 content::NotificationService::AllSources()); 180 } 181#endif 182 registrar_.Add(this, 183 chrome::NOTIFICATION_MODULE_INCOMPATIBILITY_BADGE_CHANGE, 184 content::NotificationService::AllSources()); 185 registrar_.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED, 186 content::Source<Profile>(browser_->profile())); 187} 188 189ToolbarView::~ToolbarView() { 190 // NOTE: Don't remove the command observers here. This object gets destroyed 191 // after the Browser (which owns the CommandUpdater), so the CommandUpdater is 192 // already gone. 193} 194 195void ToolbarView::Init() { 196 GetWidget()->AddObserver(this); 197 198 back_ = new BackButton(this, new BackForwardMenuModel( 199 browser_, BackForwardMenuModel::BACKWARD_MENU)); 200 back_->set_triggerable_event_flags( 201 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON); 202 back_->set_tag(IDC_BACK); 203 back_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK)); 204 back_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK)); 205 back_->set_id(VIEW_ID_BACK_BUTTON); 206 back_->Init(); 207 208 forward_ = new ToolbarButton(this, new BackForwardMenuModel( 209 browser_, BackForwardMenuModel::FORWARD_MENU)); 210 forward_->set_triggerable_event_flags( 211 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON); 212 forward_->set_tag(IDC_FORWARD); 213 forward_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD)); 214 forward_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD)); 215 forward_->set_id(VIEW_ID_FORWARD_BUTTON); 216 forward_->Init(); 217 218 // Have to create this before |reload_| as |reload_|'s constructor needs it. 219 location_bar_ = new LocationBarView( 220 browser_, browser_->profile(), 221 browser_->command_controller()->command_updater(), this, 222 display_mode_ == DISPLAYMODE_LOCATION); 223 224 reload_ = new ReloadButton(location_bar_, 225 browser_->command_controller()->command_updater()); 226 reload_->set_triggerable_event_flags( 227 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON); 228 reload_->set_tag(IDC_RELOAD); 229 reload_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD)); 230 reload_->set_id(VIEW_ID_RELOAD_BUTTON); 231 reload_->Init(); 232 233 home_ = new HomeButton(this, browser_); 234 home_->set_triggerable_event_flags( 235 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON); 236 home_->set_tag(IDC_HOME); 237 home_->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_HOME)); 238 home_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_HOME)); 239 home_->set_id(VIEW_ID_HOME_BUTTON); 240 home_->Init(); 241 242 browser_actions_ = new BrowserActionsContainer(browser_, this); 243 244 app_menu_ = new WrenchToolbarButton(this); 245 app_menu_->SetBorder(views::Border::NullBorder()); 246 app_menu_->EnableCanvasFlippingForRTLUI(true); 247 app_menu_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_APP)); 248 app_menu_->SetTooltipText(l10n_util::GetStringUTF16(IDS_APPMENU_TOOLTIP)); 249 app_menu_->set_id(VIEW_ID_APP_MENU); 250 251 // Always add children in order from left to right, for accessibility. 252 origin_chip_view_ = new ToolbarOriginChipView(this); 253 chrome::OriginChipPosition origin_chip_position = 254 chrome::GetOriginChipPosition(); 255 AddChildView(back_); 256 AddChildView(forward_); 257 AddChildView(reload_); 258 AddChildView(home_); 259 if (origin_chip_position == chrome::ORIGIN_CHIP_LEADING_LOCATION_BAR) 260 AddChildView(origin_chip_view_); 261 AddChildView(location_bar_); 262 if (origin_chip_position == chrome::ORIGIN_CHIP_TRAILING_LOCATION_BAR) 263 AddChildView(origin_chip_view_); 264 AddChildView(browser_actions_); 265 if (origin_chip_position == chrome::ORIGIN_CHIP_LEADING_MENU_BUTTON || 266 origin_chip_position == chrome::ORIGIN_CHIP_DISABLED) 267 AddChildView(origin_chip_view_); 268 AddChildView(app_menu_); 269 270 LoadImages(); 271 272 // Start global error services now so we badge the menu correctly in non-Ash. 273#if !defined(OS_CHROMEOS) 274 if (!HasAshShell()) { 275 SigninGlobalErrorFactory::GetForProfile(browser_->profile()); 276#if !defined(OS_ANDROID) 277 SyncGlobalErrorFactory::GetForProfile(browser_->profile()); 278#endif 279 } 280#endif // OS_CHROMEOS 281 282 // Add any necessary badges to the menu item based on the system state. 283 // Do this after |app_menu_| has been added as a bubble may be shown that 284 // needs the widget (widget found by way of app_menu_->GetWidget()). 285 UpdateAppMenuState(); 286 287 location_bar_->Init(); 288 289 origin_chip_view_->Init(); 290 if (origin_chip_view_->ShouldShow()) 291 location_bar_->set_toolbar_origin_chip_view(origin_chip_view_); 292 293 show_home_button_.Init(prefs::kShowHomeButton, 294 browser_->profile()->GetPrefs(), 295 base::Bind(&ToolbarView::OnShowHomeButtonChanged, 296 base::Unretained(this))); 297 298 browser_actions_->Init(); 299 300 // Accessibility specific tooltip text. 301 if (content::BrowserAccessibilityState::GetInstance()-> 302 IsAccessibleBrowser()) { 303 back_->SetTooltipText( 304 l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK)); 305 forward_->SetTooltipText( 306 l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD)); 307 } 308} 309 310void ToolbarView::OnWidgetVisibilityChanged(views::Widget* widget, 311 bool visible) { 312 // Safe to call multiple times; the bubble will only appear once. 313 if (visible) 314 extension_message_bubble_factory_->MaybeShow(app_menu_); 315} 316 317void ToolbarView::Update(WebContents* tab) { 318 if (location_bar_) 319 location_bar_->Update(tab); 320 if (origin_chip_view_->visible()) 321 origin_chip_view_->Update(tab); 322 323 if (browser_actions_) 324 browser_actions_->RefreshBrowserActionViews(); 325 326 if (reload_) 327 reload_->set_menu_enabled(chrome::IsDebuggerAttachedToCurrentTab(browser_)); 328} 329 330void ToolbarView::SetPaneFocusAndFocusAppMenu() { 331 SetPaneFocus(app_menu_); 332} 333 334bool ToolbarView::IsAppMenuFocused() { 335 return app_menu_->HasFocus(); 336} 337 338void ToolbarView::AddMenuListener(views::MenuListener* listener) { 339 menu_listeners_.AddObserver(listener); 340} 341 342void ToolbarView::RemoveMenuListener(views::MenuListener* listener) { 343 menu_listeners_.RemoveObserver(listener); 344} 345 346views::View* ToolbarView::GetBookmarkBubbleAnchor() { 347 views::View* star_view = location_bar()->star_view(); 348 return (star_view && star_view->visible()) ? star_view : app_menu_; 349} 350 351views::View* ToolbarView::GetTranslateBubbleAnchor() { 352 views::View* translate_icon_view = location_bar()->translate_icon_view(); 353 return (translate_icon_view && translate_icon_view->visible()) ? 354 translate_icon_view : app_menu_; 355} 356 357void ToolbarView::ExecuteExtensionCommand( 358 const extensions::Extension* extension, 359 const extensions::Command& command) { 360 browser_actions_->ExecuteExtensionCommand(extension, command); 361} 362 363void ToolbarView::ShowPageActionPopup(const extensions::Extension* extension) { 364 extensions::ExtensionActionManager* extension_manager = 365 extensions::ExtensionActionManager::Get(browser_->profile()); 366 ExtensionAction* extension_action = 367 extension_manager->GetPageAction(*extension); 368 if (extension_action) { 369 location_bar_->GetPageActionView(extension_action)->image_view()-> 370 ExecuteAction(ExtensionPopup::SHOW); 371 } 372} 373 374void ToolbarView::ShowBrowserActionPopup( 375 const extensions::Extension* extension) { 376 browser_actions_->ShowPopup(extension, true); 377} 378 379views::MenuButton* ToolbarView::app_menu() const { 380 return app_menu_; 381} 382 383//////////////////////////////////////////////////////////////////////////////// 384// ToolbarView, AccessiblePaneView overrides: 385 386bool ToolbarView::SetPaneFocus(views::View* initial_focus) { 387 if (!AccessiblePaneView::SetPaneFocus(initial_focus)) 388 return false; 389 390 location_bar_->SetShowFocusRect(true); 391 return true; 392} 393 394void ToolbarView::GetAccessibleState(ui::AXViewState* state) { 395 state->role = ui::AX_ROLE_TOOLBAR; 396 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR); 397} 398 399//////////////////////////////////////////////////////////////////////////////// 400// ToolbarView, Menu::Delegate overrides: 401 402bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) { 403 return GetWidget()->GetAccelerator(id, accel); 404} 405 406//////////////////////////////////////////////////////////////////////////////// 407// ToolbarView, views::MenuButtonListener implementation: 408 409void ToolbarView::OnMenuButtonClicked(views::View* source, 410 const gfx::Point& point) { 411 TRACE_EVENT0("views", "ToolbarView::OnMenuButtonClicked"); 412 DCHECK_EQ(VIEW_ID_APP_MENU, source->id()); 413 414 bool use_new_menu = false; 415 bool supports_new_separators = false; 416 // TODO: remove this. 417#if defined(USE_AURA) 418 supports_new_separators = 419 GetNativeTheme() == ui::NativeThemeAura::instance(); 420 use_new_menu = supports_new_separators; 421#endif 422#if defined(OS_WIN) 423 use_new_menu = use_new_menu || ui::GetDisplayLayout() == ui::LAYOUT_TOUCH; 424#endif 425 426 wrench_menu_.reset(new WrenchMenu(browser_, use_new_menu, 427 supports_new_separators)); 428 wrench_menu_model_.reset(new WrenchMenuModel(this, browser_, use_new_menu)); 429 wrench_menu_->Init(wrench_menu_model_.get()); 430 431 FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened()); 432 433 wrench_menu_->RunMenu(app_menu_); 434} 435 436//////////////////////////////////////////////////////////////////////////////// 437// ToolbarView, LocationBarView::Delegate implementation: 438 439WebContents* ToolbarView::GetWebContents() { 440 return browser_->tab_strip_model()->GetActiveWebContents(); 441} 442 443ToolbarModel* ToolbarView::GetToolbarModel() { 444 return browser_->toolbar_model(); 445} 446 447const ToolbarModel* ToolbarView::GetToolbarModel() const { 448 return browser_->toolbar_model(); 449} 450 451InstantController* ToolbarView::GetInstant() { 452 return browser_->instant_controller() ? 453 browser_->instant_controller()->instant() : NULL; 454} 455 456ContentSettingBubbleModelDelegate* 457ToolbarView::GetContentSettingBubbleModelDelegate() { 458 return browser_->content_setting_bubble_model_delegate(); 459} 460 461void ToolbarView::ShowWebsiteSettings(content::WebContents* web_contents, 462 const GURL& url, 463 const content::SSLStatus& ssl) { 464 chrome::ShowWebsiteSettings(browser_, web_contents, url, ssl); 465} 466 467views::Widget* ToolbarView::CreateViewsBubble( 468 views::BubbleDelegateView* bubble_delegate) { 469 return views::BubbleDelegateView::CreateBubble(bubble_delegate); 470} 471 472PageActionImageView* ToolbarView::CreatePageActionImageView( 473 LocationBarView* owner, ExtensionAction* action) { 474 return new PageActionImageView(owner, action, browser_); 475} 476 477//////////////////////////////////////////////////////////////////////////////// 478// ToolbarView, CommandObserver implementation: 479 480void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) { 481 views::Button* button = NULL; 482 switch (id) { 483 case IDC_BACK: 484 button = back_; 485 break; 486 case IDC_FORWARD: 487 button = forward_; 488 break; 489 case IDC_RELOAD: 490 button = reload_; 491 break; 492 case IDC_HOME: 493 button = home_; 494 break; 495 } 496 if (button) 497 button->SetEnabled(enabled); 498} 499 500//////////////////////////////////////////////////////////////////////////////// 501// ToolbarView, views::Button::ButtonListener implementation: 502 503void ToolbarView::ButtonPressed(views::Button* sender, 504 const ui::Event& event) { 505 chrome::ExecuteCommandWithDisposition( 506 browser_, sender->tag(), ui::DispositionFromEventFlags(event.flags())); 507} 508 509//////////////////////////////////////////////////////////////////////////////// 510// ToolbarView, content::NotificationObserver implementation: 511 512void ToolbarView::Observe(int type, 513 const content::NotificationSource& source, 514 const content::NotificationDetails& details) { 515 switch (type) { 516 case chrome::NOTIFICATION_UPGRADE_RECOMMENDED: 517 case chrome::NOTIFICATION_MODULE_INCOMPATIBILITY_BADGE_CHANGE: 518 case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED: 519 case chrome::NOTIFICATION_MODULE_LIST_ENUMERATED: 520 UpdateAppMenuState(); 521 break; 522 case chrome::NOTIFICATION_OUTDATED_INSTALL: 523 ShowOutdatedInstallNotification(true); 524 break; 525 case chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU: 526 ShowOutdatedInstallNotification(false); 527 break; 528#if defined(OS_WIN) 529 case chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED: 530 ShowCriticalNotification(); 531 break; 532#endif 533 default: 534 NOTREACHED(); 535 } 536} 537 538//////////////////////////////////////////////////////////////////////////////// 539// ToolbarView, ui::AcceleratorProvider implementation: 540 541bool ToolbarView::GetAcceleratorForCommandId(int command_id, 542 ui::Accelerator* accelerator) { 543 return GetWidget()->GetAccelerator(command_id, accelerator); 544} 545 546//////////////////////////////////////////////////////////////////////////////// 547// ToolbarView, views::View overrides: 548 549gfx::Size ToolbarView::GetPreferredSize() { 550 gfx::Size size(location_bar_->GetPreferredSize()); 551 if (is_display_mode_normal()) { 552 int button_spacing = GetButtonSpacing(); 553 size.Enlarge( 554 kLeftEdgeSpacing + back_->GetPreferredSize().width() + button_spacing + 555 forward_->GetPreferredSize().width() + button_spacing + 556 reload_->GetPreferredSize().width() + kStandardSpacing + 557 (show_home_button_.GetValue() ? 558 (home_->GetPreferredSize().width() + button_spacing) : 0) + 559 (origin_chip_view_->visible() ? 560 (origin_chip_view_->GetPreferredSize().width() + 561 2 * kStandardSpacing) : 562 0) + 563 browser_actions_->GetPreferredSize().width() + 564 app_menu_->GetPreferredSize().width() + kRightEdgeSpacing, 565 0); 566 gfx::ImageSkia* normal_background = 567 GetThemeProvider()->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER); 568 size.SetToMax( 569 gfx::Size(0, normal_background->height() - content_shadow_height())); 570 } else { 571 const int kPopupBottomSpacingGlass = 1; 572 const int kPopupBottomSpacingNonGlass = 2; 573 size.Enlarge( 574 0, 575 PopupTopSpacing() + (GetWidget()->ShouldWindowContentsBeTransparent() ? 576 kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass)); 577 } 578 return size; 579} 580 581void ToolbarView::Layout() { 582 // If we have not been initialized yet just do nothing. 583 if (back_ == NULL) 584 return; 585 586 if (!is_display_mode_normal()) { 587 location_bar_->SetBounds(0, PopupTopSpacing(), width(), 588 location_bar_->GetPreferredSize().height()); 589 return; 590 } 591 592 // We assume all child elements except the location bar are the same height. 593 // Set child_y such that buttons appear vertically centered. We put any excess 594 // padding above the buttons. 595 int child_height = 596 std::min(back_->GetPreferredSize().height(), height()); 597 int child_y = (height() - child_height + 1) / 2; 598 599 // If the window is maximized, we extend the back button to the left so that 600 // clicking on the left-most pixel will activate the back button. 601 // TODO(abarth): If the window becomes maximized but is not resized, 602 // then Layout() might not be called and the back button 603 // will be slightly the wrong size. We should force a 604 // Layout() in this case. 605 // http://crbug.com/5540 606 bool maximized = browser_->window() && browser_->window()->IsMaximized(); 607 int back_width = back_->GetPreferredSize().width(); 608 if (maximized) { 609 back_->SetBounds(0, child_y, back_width + kLeftEdgeSpacing, child_height); 610 back_->SetLeadingMargin(kLeftEdgeSpacing); 611 } else { 612 back_->SetBounds(kLeftEdgeSpacing, child_y, back_width, child_height); 613 back_->SetLeadingMargin(0); 614 } 615 int button_spacing = GetButtonSpacing(); 616 int next_element_x = back_->bounds().right() + button_spacing; 617 618 forward_->SetBounds(next_element_x, child_y, 619 forward_->GetPreferredSize().width(), child_height); 620 next_element_x = forward_->bounds().right() + button_spacing; 621 622 reload_->SetBounds(next_element_x, child_y, 623 reload_->GetPreferredSize().width(), child_height); 624 next_element_x = reload_->bounds().right(); 625 626 if (show_home_button_.GetValue() || 627 (browser_->is_app() && IsStreamlinedHostedAppsEnabled())) { 628 home_->SetVisible(true); 629 home_->SetBounds(next_element_x + button_spacing, child_y, 630 home_->GetPreferredSize().width(), child_height); 631 } else { 632 home_->SetVisible(false); 633 home_->SetBounds(next_element_x, child_y, 0, child_height); 634 } 635 next_element_x = home_->bounds().right() + kStandardSpacing; 636 637 int browser_actions_width = browser_actions_->GetPreferredSize().width(); 638 int app_menu_width = app_menu_->GetPreferredSize().width(); 639 int available_width = std::max(0, width() - kRightEdgeSpacing - 640 app_menu_width - browser_actions_width - next_element_x); 641 642 origin_chip_view_->SetVisible(origin_chip_view_->ShouldShow()); 643 int origin_chip_width = 644 origin_chip_view_->ElideDomainTarget(available_width / 2); 645 if (origin_chip_view_->visible()) 646 available_width -= origin_chip_width + kStandardSpacing; 647 648 chrome::OriginChipPosition origin_chip_position = 649 chrome::GetOriginChipPosition(); 650 if (origin_chip_view_->visible() && 651 origin_chip_position == chrome::ORIGIN_CHIP_LEADING_LOCATION_BAR) { 652 origin_chip_view_->SetBounds(next_element_x, child_y, 653 origin_chip_width, child_height); 654 next_element_x = origin_chip_view_->bounds().right() + kStandardSpacing; 655 } 656 657 int location_height = location_bar_->GetPreferredSize().height(); 658 int location_y = (height() - location_height + 1) / 2; 659 location_bar_->SetBounds(next_element_x, location_y, 660 std::max(available_width, 0), location_height); 661 next_element_x = location_bar_->bounds().right(); 662 663 if (origin_chip_view_->visible() && 664 origin_chip_position == chrome::ORIGIN_CHIP_TRAILING_LOCATION_BAR) { 665 origin_chip_view_->SetBounds(next_element_x + kStandardSpacing, child_y, 666 origin_chip_width, child_height); 667 next_element_x = origin_chip_view_->bounds().right(); 668 } 669 670 browser_actions_->SetBounds( 671 next_element_x, child_y, browser_actions_width, child_height); 672 next_element_x = browser_actions_->bounds().right(); 673 674 // The browser actions need to do a layout explicitly, because when an 675 // extension is loaded/unloaded/changed, BrowserActionContainer removes and 676 // re-adds everything, regardless of whether it has a page action. For a 677 // page action, browser action bounds do not change, as a result of which 678 // SetBounds does not do a layout at all. 679 // TODO(sidchat): Rework the above behavior so that explicit layout is not 680 // required. 681 browser_actions_->Layout(); 682 683 if (origin_chip_view_->visible() && 684 origin_chip_position == chrome::ORIGIN_CHIP_LEADING_MENU_BUTTON) { 685 origin_chip_view_->SetBounds(next_element_x, child_y, 686 origin_chip_width, child_height); 687 next_element_x = origin_chip_view_->bounds().right() + kStandardSpacing; 688 } 689 690 // Extend the app menu to the screen's right edge in maximized mode just like 691 // we extend the back button to the left edge. 692 if (maximized) 693 app_menu_width += kRightEdgeSpacing; 694 app_menu_->SetBounds(next_element_x, child_y, app_menu_width, child_height); 695} 696 697bool ToolbarView::HitTestRect(const gfx::Rect& rect) const { 698 // Fall through to the tab strip above us if none of |rect| intersects 699 // with this view (intersection with the top shadow edge does not 700 // count as intersection with this view). 701 if (rect.bottom() < content_shadow_height()) 702 return false; 703 // Otherwise let our superclass take care of it. 704 return AccessiblePaneView::HitTestRect(rect); 705} 706 707void ToolbarView::OnPaint(gfx::Canvas* canvas) { 708 View::OnPaint(canvas); 709 710 if (is_display_mode_normal()) 711 return; 712 713 // For glass, we need to draw a black line below the location bar to separate 714 // it from the content area. For non-glass, the NonClientView draws the 715 // toolbar background below the location bar for us. 716 // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()! 717 if (GetWidget()->ShouldWindowContentsBeTransparent()) 718 canvas->FillRect(gfx::Rect(0, height() - 1, width(), 1), SK_ColorBLACK); 719} 720 721void ToolbarView::OnThemeChanged() { 722 LoadImages(); 723} 724 725const char* ToolbarView::GetClassName() const { 726 return kViewClassName; 727} 728 729bool ToolbarView::AcceleratorPressed(const ui::Accelerator& accelerator) { 730 const views::View* focused_view = focus_manager()->GetFocusedView(); 731 if (focused_view && (focused_view->id() == VIEW_ID_OMNIBOX)) 732 return false; // Let the omnibox handle all accelerator events. 733 return AccessiblePaneView::AcceleratorPressed(accelerator); 734} 735 736bool ToolbarView::IsWrenchMenuShowing() const { 737 return wrench_menu_.get() && wrench_menu_->IsShowing(); 738} 739 740bool ToolbarView::ShouldPaintBackground() const { 741 return display_mode_ == DISPLAYMODE_NORMAL; 742} 743 744//////////////////////////////////////////////////////////////////////////////// 745// ToolbarView, protected: 746 747// Override this so that when the user presses F6 to rotate toolbar panes, 748// the location bar gets focus, not the first control in the toolbar - and 749// also so that it selects all content in the location bar. 750bool ToolbarView::SetPaneFocusAndFocusDefault() { 751 if (!location_bar_->HasFocus()) { 752 SetPaneFocus(location_bar_); 753 location_bar_->FocusLocation(true); 754 return true; 755 } 756 757 if (!AccessiblePaneView::SetPaneFocusAndFocusDefault()) 758 return false; 759 browser_->window()->RotatePaneFocus(true); 760 return true; 761} 762 763void ToolbarView::RemovePaneFocus() { 764 AccessiblePaneView::RemovePaneFocus(); 765 location_bar_->SetShowFocusRect(false); 766} 767 768//////////////////////////////////////////////////////////////////////////////// 769// ToolbarView, private: 770 771bool ToolbarView::ShouldShowUpgradeRecommended() { 772#if defined(OS_CHROMEOS) 773 // In chromeos, the update recommendation is shown in the system tray. So it 774 // should not be displayed in the wrench menu. 775 return false; 776#else 777 return (UpgradeDetector::GetInstance()->notify_upgrade()); 778#endif 779} 780 781bool ToolbarView::ShouldShowIncompatibilityWarning() { 782#if defined(OS_WIN) 783 EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetInstance(); 784 loaded_modules->MaybePostScanningTask(); 785 return loaded_modules->ShouldShowConflictWarning(); 786#else 787 return false; 788#endif 789} 790 791int ToolbarView::PopupTopSpacing() const { 792 const int kPopupTopSpacingNonGlass = 3; 793 return GetWidget()->ShouldWindowContentsBeTransparent() 794 ? 0 795 : kPopupTopSpacingNonGlass; 796} 797 798void ToolbarView::LoadImages() { 799 ui::ThemeProvider* tp = GetThemeProvider(); 800 801 back_->SetImage(views::Button::STATE_NORMAL, 802 *(tp->GetImageSkiaNamed(IDR_BACK))); 803 back_->SetImage(views::Button::STATE_DISABLED, 804 *(tp->GetImageSkiaNamed(IDR_BACK_D))); 805 806 forward_->SetImage(views::Button::STATE_NORMAL, 807 *(tp->GetImageSkiaNamed(IDR_FORWARD))); 808 forward_->SetImage(views::Button::STATE_DISABLED, 809 *(tp->GetImageSkiaNamed(IDR_FORWARD_D))); 810 811 reload_->LoadImages(); 812 813 home_->SetImage(views::Button::STATE_NORMAL, 814 *(tp->GetImageSkiaNamed(IDR_HOME))); 815} 816 817void ToolbarView::ShowCriticalNotification() { 818#if defined(OS_WIN) 819 CriticalNotificationBubbleView* bubble_delegate = 820 new CriticalNotificationBubbleView(app_menu_); 821 views::BubbleDelegateView::CreateBubble(bubble_delegate)->Show(); 822#endif 823} 824 825void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) { 826 if (OutdatedUpgradeBubbleView::IsAvailable()) { 827 OutdatedUpgradeBubbleView::ShowBubble( 828 app_menu_, browser_, auto_update_enabled); 829 } 830} 831 832void ToolbarView::UpdateAppMenuState() { 833 base::string16 accname_app = l10n_util::GetStringUTF16(IDS_ACCNAME_APP); 834 if (ShouldShowUpgradeRecommended()) { 835 accname_app = l10n_util::GetStringFUTF16( 836 IDS_ACCNAME_APP_UPGRADE_RECOMMENDED, accname_app); 837 } 838 app_menu_->SetAccessibleName(accname_app); 839 840 UpdateWrenchButtonSeverity(); 841 SchedulePaint(); 842} 843 844void ToolbarView::UpdateWrenchButtonSeverity() { 845 // Showing the bubble requires |app_menu_| to be in a widget. See comment 846 // in ConflictingModuleView for details. 847 DCHECK(app_menu_->GetWidget()); 848 849 // Keep track of whether we were showing the badge before, so we don't send 850 // multiple UMA events for example when multiple Chrome windows are open. 851 static bool incompatibility_badge_showing = false; 852 // Save the old value before resetting it. 853 bool was_showing = incompatibility_badge_showing; 854 incompatibility_badge_showing = false; 855 856 if (ShouldShowUpgradeRecommended()) { 857 UpgradeDetector::UpgradeNotificationAnnoyanceLevel level = 858 UpgradeDetector::GetInstance()->upgrade_notification_stage(); 859 app_menu_->SetSeverity(WrenchIconPainter::SeverityFromUpgradeLevel(level), 860 WrenchIconPainter::ShouldAnimateUpgradeLevel(level)); 861 return; 862 } 863 864 if (ShouldShowIncompatibilityWarning()) { 865 if (!was_showing) { 866 content::RecordAction(UserMetricsAction("ConflictBadge")); 867#if defined(OS_WIN) 868 ConflictingModuleView::MaybeShow(browser_, app_menu_); 869#endif 870 } 871 app_menu_->SetSeverity(WrenchIconPainter::SEVERITY_MEDIUM, true); 872 incompatibility_badge_showing = true; 873 return; 874 } 875 876 GlobalErrorService* service = 877 GlobalErrorServiceFactory::GetForProfile(browser_->profile()); 878 GlobalError* error = 879 service->GetHighestSeverityGlobalErrorWithWrenchMenuItem(); 880 if (error) { 881 app_menu_->SetSeverity(WrenchIconPainter::GlobalErrorSeverity(), true); 882 return; 883 } 884 885 app_menu_->SetSeverity(WrenchIconPainter::SEVERITY_NONE, true); 886} 887 888void ToolbarView::OnShowHomeButtonChanged() { 889 Layout(); 890 SchedulePaint(); 891} 892 893int ToolbarView::content_shadow_height() const { 894 return browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH ? 895 kContentShadowHeightAsh : kContentShadowHeight; 896} 897