1// Copyright (c) 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/toolbar/wrench_menu_model.h" 6 7#include <algorithm> 8#include <cmath> 9 10#include "base/command_line.h" 11#include "base/prefs/pref_service.h" 12#include "base/strings/string_number_conversions.h" 13#include "base/strings/string_util.h" 14#include "base/strings/utf_string_conversions.h" 15#include "chrome/app/chrome_command_ids.h" 16#include "chrome/browser/browser_process.h" 17#include "chrome/browser/defaults.h" 18#include "chrome/browser/extensions/extension_toolbar_model.h" 19#include "chrome/browser/extensions/extension_util.h" 20#include "chrome/browser/profiles/profile.h" 21#include "chrome/browser/profiles/profile_manager.h" 22#include "chrome/browser/search/search.h" 23#include "chrome/browser/signin/signin_manager_factory.h" 24#include "chrome/browser/signin/signin_ui_util.h" 25#include "chrome/browser/task_manager/task_manager.h" 26#include "chrome/browser/ui/bookmarks/bookmark_utils.h" 27#include "chrome/browser/ui/browser.h" 28#include "chrome/browser/ui/browser_commands.h" 29#include "chrome/browser/ui/browser_finder.h" 30#include "chrome/browser/ui/browser_window.h" 31#include "chrome/browser/ui/global_error/global_error.h" 32#include "chrome/browser/ui/global_error/global_error_service.h" 33#include "chrome/browser/ui/global_error/global_error_service_factory.h" 34#include "chrome/browser/ui/tabs/tab_strip_model.h" 35#include "chrome/browser/ui/toolbar/bookmark_sub_menu_model.h" 36#include "chrome/browser/ui/toolbar/encoding_menu_controller.h" 37#include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h" 38#include "chrome/browser/ui/zoom/zoom_controller.h" 39#include "chrome/browser/ui/zoom/zoom_event_manager.h" 40#include "chrome/browser/upgrade_detector.h" 41#include "chrome/common/chrome_paths.h" 42#include "chrome/common/chrome_switches.h" 43#include "chrome/common/pref_names.h" 44#include "chrome/common/profiling.h" 45#include "chrome/grit/chromium_strings.h" 46#include "chrome/grit/generated_resources.h" 47#include "components/signin/core/browser/signin_manager.h" 48#include "components/signin/core/common/profile_management_switches.h" 49#include "content/public/browser/host_zoom_map.h" 50#include "content/public/browser/navigation_entry.h" 51#include "content/public/browser/notification_service.h" 52#include "content/public/browser/notification_source.h" 53#include "content/public/browser/notification_types.h" 54#include "content/public/browser/user_metrics.h" 55#include "content/public/browser/web_contents.h" 56#include "extensions/common/feature_switch.h" 57#include "grit/theme_resources.h" 58#include "ui/base/l10n/l10n_util.h" 59#include "ui/base/layout.h" 60#include "ui/base/models/button_menu_item_model.h" 61#include "ui/base/resource/resource_bundle.h" 62#include "ui/gfx/image/image.h" 63#include "ui/gfx/image/image_skia.h" 64 65#if defined(OS_CHROMEOS) 66#include "chromeos/chromeos_switches.h" 67#endif 68 69#if defined(OS_WIN) 70#include "base/win/metro.h" 71#include "base/win/windows_version.h" 72#include "chrome/browser/enumerate_modules_model_win.h" 73#include "chrome/browser/ui/metro_pin_tab_helper_win.h" 74#include "content/public/browser/gpu_data_manager.h" 75#endif 76 77#if defined(USE_ASH) 78#include "ash/shell.h" 79#endif 80 81using base::UserMetricsAction; 82using content::WebContents; 83 84namespace { 85// Conditionally return the update app menu item title based on upgrade detector 86// state. 87base::string16 GetUpgradeDialogMenuItemName() { 88 if (UpgradeDetector::GetInstance()->is_outdated_install() || 89 UpgradeDetector::GetInstance()->is_outdated_install_no_au()) { 90 return l10n_util::GetStringUTF16(IDS_UPGRADE_BUBBLE_MENU_ITEM); 91 } else { 92 return l10n_util::GetStringUTF16(IDS_UPDATE_NOW); 93 } 94} 95 96} // namespace 97 98//////////////////////////////////////////////////////////////////////////////// 99// EncodingMenuModel 100 101EncodingMenuModel::EncodingMenuModel(Browser* browser) 102 : ui::SimpleMenuModel(this), 103 browser_(browser) { 104 Build(); 105} 106 107EncodingMenuModel::~EncodingMenuModel() { 108} 109 110void EncodingMenuModel::Build() { 111 EncodingMenuController::EncodingMenuItemList encoding_menu_items; 112 EncodingMenuController encoding_menu_controller; 113 encoding_menu_controller.GetEncodingMenuItems(browser_->profile(), 114 &encoding_menu_items); 115 116 int group_id = 0; 117 EncodingMenuController::EncodingMenuItemList::iterator it = 118 encoding_menu_items.begin(); 119 for (; it != encoding_menu_items.end(); ++it) { 120 int id = it->first; 121 base::string16& label = it->second; 122 if (id == 0) { 123 AddSeparator(ui::NORMAL_SEPARATOR); 124 } else { 125 if (id == IDC_ENCODING_AUTO_DETECT) { 126 AddCheckItem(id, label); 127 } else { 128 // Use the id of the first radio command as the id of the group. 129 if (group_id <= 0) 130 group_id = id; 131 AddRadioItem(id, label, group_id); 132 } 133 } 134 } 135} 136 137bool EncodingMenuModel::IsCommandIdChecked(int command_id) const { 138 WebContents* current_tab = 139 browser_->tab_strip_model()->GetActiveWebContents(); 140 if (!current_tab) 141 return false; 142 EncodingMenuController controller; 143 return controller.IsItemChecked(browser_->profile(), 144 current_tab->GetEncoding(), command_id); 145} 146 147bool EncodingMenuModel::IsCommandIdEnabled(int command_id) const { 148 bool enabled = chrome::IsCommandEnabled(browser_, command_id); 149 // Special handling for the contents of the Encoding submenu. On Mac OS, 150 // instead of enabling/disabling the top-level menu item, the submenu's 151 // contents get disabled, per Apple's HIG. 152#if defined(OS_MACOSX) 153 enabled &= chrome::IsCommandEnabled(browser_, IDC_ENCODING_MENU); 154#endif 155 return enabled; 156} 157 158bool EncodingMenuModel::GetAcceleratorForCommandId( 159 int command_id, 160 ui::Accelerator* accelerator) { 161 return false; 162} 163 164void EncodingMenuModel::ExecuteCommand(int command_id, int event_flags) { 165 chrome::ExecuteCommand(browser_, command_id); 166} 167 168//////////////////////////////////////////////////////////////////////////////// 169// ZoomMenuModel 170 171ZoomMenuModel::ZoomMenuModel(ui::SimpleMenuModel::Delegate* delegate) 172 : SimpleMenuModel(delegate) { 173 Build(); 174} 175 176ZoomMenuModel::~ZoomMenuModel() { 177} 178 179void ZoomMenuModel::Build() { 180 AddItemWithStringId(IDC_ZOOM_PLUS, IDS_ZOOM_PLUS); 181 AddItemWithStringId(IDC_ZOOM_NORMAL, IDS_ZOOM_NORMAL); 182 AddItemWithStringId(IDC_ZOOM_MINUS, IDS_ZOOM_MINUS); 183} 184 185//////////////////////////////////////////////////////////////////////////////// 186// HelpMenuModel 187 188#if defined(GOOGLE_CHROME_BUILD) 189 190class WrenchMenuModel::HelpMenuModel : public ui::SimpleMenuModel { 191 public: 192 HelpMenuModel(ui::SimpleMenuModel::Delegate* delegate, 193 Browser* browser) 194 : SimpleMenuModel(delegate) { 195 Build(browser); 196 } 197 virtual ~HelpMenuModel() { 198 } 199 200 private: 201 void Build(Browser* browser) { 202#if defined(OS_CHROMEOS) && defined(OFFICIAL_BUILD) 203 int help_string_id = IDS_GET_HELP; 204#else 205 int help_string_id = IDS_HELP_PAGE; 206#endif 207 AddItemWithStringId(IDC_HELP_PAGE_VIA_MENU, help_string_id); 208 if (browser_defaults::kShowHelpMenuItemIcon) { 209 ui::ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 210 SetIcon(GetIndexOfCommandId(IDC_HELP_PAGE_VIA_MENU), 211 rb.GetNativeImageNamed(IDR_HELP_MENU)); 212 } 213 214 AddItemWithStringId(IDC_FEEDBACK, IDS_FEEDBACK); 215 } 216 217 DISALLOW_COPY_AND_ASSIGN(HelpMenuModel); 218}; 219 220#endif // defined(GOOGLE_CHROME_BUILD) 221 222//////////////////////////////////////////////////////////////////////////////// 223// ToolsMenuModel 224 225ToolsMenuModel::ToolsMenuModel(ui::SimpleMenuModel::Delegate* delegate, 226 Browser* browser) 227 : SimpleMenuModel(delegate) { 228 Build(browser); 229} 230 231ToolsMenuModel::~ToolsMenuModel() {} 232 233void ToolsMenuModel::Build(Browser* browser) { 234 bool show_create_shortcuts = true; 235#if defined(OS_CHROMEOS) || defined(OS_MACOSX) 236 show_create_shortcuts = false; 237#elif defined(USE_ASH) 238 if (browser->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH) 239 show_create_shortcuts = false; 240#endif 241 242 if (extensions::util::IsStreamlinedHostedAppsEnabled()) { 243 AddItemWithStringId(IDC_CREATE_HOSTED_APP, IDS_CREATE_HOSTED_APP); 244 AddSeparator(ui::NORMAL_SEPARATOR); 245 } else if (show_create_shortcuts) { 246 AddItemWithStringId(IDC_CREATE_SHORTCUTS, IDS_CREATE_SHORTCUTS); 247 AddSeparator(ui::NORMAL_SEPARATOR); 248 } 249 250 AddItemWithStringId(IDC_MANAGE_EXTENSIONS, IDS_SHOW_EXTENSIONS); 251 252 if (chrome::CanOpenTaskManager()) 253 AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); 254 255 AddItemWithStringId(IDC_CLEAR_BROWSING_DATA, IDS_CLEAR_BROWSING_DATA); 256 257 AddSeparator(ui::NORMAL_SEPARATOR); 258 259 encoding_menu_model_.reset(new EncodingMenuModel(browser)); 260 AddSubMenuWithStringId(IDC_ENCODING_MENU, IDS_ENCODING_MENU, 261 encoding_menu_model_.get()); 262 AddItemWithStringId(IDC_VIEW_SOURCE, IDS_VIEW_SOURCE); 263 AddItemWithStringId(IDC_DEV_TOOLS, IDS_DEV_TOOLS); 264 AddItemWithStringId(IDC_DEV_TOOLS_CONSOLE, IDS_DEV_TOOLS_CONSOLE); 265 AddItemWithStringId(IDC_DEV_TOOLS_DEVICES, IDS_DEV_TOOLS_DEVICES); 266 267#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) 268 AddSeparator(ui::NORMAL_SEPARATOR); 269 AddCheckItemWithStringId(IDC_PROFILING_ENABLED, IDS_PROFILING_ENABLED); 270#endif 271} 272 273//////////////////////////////////////////////////////////////////////////////// 274// WrenchMenuModel 275 276WrenchMenuModel::WrenchMenuModel(ui::AcceleratorProvider* provider, 277 Browser* browser) 278 : ui::SimpleMenuModel(this), 279 provider_(provider), 280 browser_(browser), 281 tab_strip_model_(browser_->tab_strip_model()) { 282 Build(); 283 UpdateZoomControls(); 284 285 content_zoom_subscription_ = 286 content::HostZoomMap::GetDefaultForBrowserContext(browser->profile()) 287 ->AddZoomLevelChangedCallback(base::Bind( 288 &WrenchMenuModel::OnZoomLevelChanged, base::Unretained(this))); 289 290 browser_zoom_subscription_ = ZoomEventManager::GetForBrowserContext( 291 browser->profile())->AddZoomLevelChangedCallback( 292 base::Bind(&WrenchMenuModel::OnZoomLevelChanged, 293 base::Unretained(this))); 294 295 tab_strip_model_->AddObserver(this); 296 297 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, 298 content::NotificationService::AllSources()); 299} 300 301WrenchMenuModel::~WrenchMenuModel() { 302 if (tab_strip_model_) 303 tab_strip_model_->RemoveObserver(this); 304} 305 306bool WrenchMenuModel::DoesCommandIdDismissMenu(int command_id) const { 307 return command_id != IDC_ZOOM_MINUS && command_id != IDC_ZOOM_PLUS; 308} 309 310bool WrenchMenuModel::IsItemForCommandIdDynamic(int command_id) const { 311 return command_id == IDC_ZOOM_PERCENT_DISPLAY || 312#if defined(OS_MACOSX) 313 command_id == IDC_FULLSCREEN || 314#elif defined(OS_WIN) 315 command_id == IDC_PIN_TO_START_SCREEN || 316#endif 317 command_id == IDC_UPGRADE_DIALOG || 318 (!switches::IsNewAvatarMenu() && command_id == IDC_SHOW_SIGNIN); 319} 320 321base::string16 WrenchMenuModel::GetLabelForCommandId(int command_id) const { 322 switch (command_id) { 323 case IDC_ZOOM_PERCENT_DISPLAY: 324 return zoom_label_; 325#if defined(OS_MACOSX) 326 case IDC_FULLSCREEN: { 327 int string_id = IDS_ENTER_FULLSCREEN_MAC; // Default to Enter. 328 // Note: On startup, |window()| may be NULL. 329 if (browser_->window() && browser_->window()->IsFullscreen()) 330 string_id = IDS_EXIT_FULLSCREEN_MAC; 331 return l10n_util::GetStringUTF16(string_id); 332 } 333#elif defined(OS_WIN) 334 case IDC_PIN_TO_START_SCREEN: { 335 int string_id = IDS_PIN_TO_START_SCREEN; 336 WebContents* web_contents = 337 browser_->tab_strip_model()->GetActiveWebContents(); 338 MetroPinTabHelper* tab_helper = 339 web_contents ? MetroPinTabHelper::FromWebContents(web_contents) 340 : NULL; 341 if (tab_helper && tab_helper->IsPinned()) 342 string_id = IDS_UNPIN_FROM_START_SCREEN; 343 return l10n_util::GetStringUTF16(string_id); 344 } 345#endif 346 case IDC_UPGRADE_DIALOG: 347 return GetUpgradeDialogMenuItemName(); 348 case IDC_SHOW_SIGNIN: 349 DCHECK(!switches::IsNewAvatarMenu()); 350 return signin_ui_util::GetSigninMenuLabel( 351 browser_->profile()->GetOriginalProfile()); 352 default: 353 NOTREACHED(); 354 return base::string16(); 355 } 356} 357 358bool WrenchMenuModel::GetIconForCommandId(int command_id, 359 gfx::Image* icon) const { 360 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 361 switch (command_id) { 362 case IDC_UPGRADE_DIALOG: { 363 if (UpgradeDetector::GetInstance()->notify_upgrade()) { 364 *icon = rb.GetNativeImageNamed( 365 UpgradeDetector::GetInstance()->GetIconResourceID()); 366 return true; 367 } 368 return false; 369 } 370 case IDC_SHOW_SIGNIN: { 371 DCHECK(!switches::IsNewAvatarMenu()); 372 GlobalError* error = signin_ui_util::GetSignedInServiceError( 373 browser_->profile()->GetOriginalProfile()); 374 if (error) { 375 int icon_id = error->MenuItemIconResourceID(); 376 if (icon_id) { 377 *icon = rb.GetNativeImageNamed(icon_id); 378 return true; 379 } 380 } 381 return false; 382 } 383 default: 384 break; 385 } 386 return false; 387} 388 389void WrenchMenuModel::ExecuteCommand(int command_id, int event_flags) { 390 GlobalError* error = GlobalErrorServiceFactory::GetForProfile( 391 browser_->profile())->GetGlobalErrorByMenuItemCommandID(command_id); 392 if (error) { 393 error->ExecuteMenuItem(browser_); 394 return; 395 } 396 397 if (!switches::IsNewAvatarMenu() && command_id == IDC_SHOW_SIGNIN) { 398 // If a custom error message is being shown, handle it. 399 GlobalError* error = signin_ui_util::GetSignedInServiceError( 400 browser_->profile()->GetOriginalProfile()); 401 if (error) { 402 error->ExecuteMenuItem(browser_); 403 return; 404 } 405 } 406 407 if (command_id == IDC_HELP_PAGE_VIA_MENU) 408 content::RecordAction(UserMetricsAction("ShowHelpTabViaWrenchMenu")); 409 410 if (command_id == IDC_FULLSCREEN) { 411 // We issue the UMA command here and not in BrowserCommandController or even 412 // FullscreenController since we want to be able to distinguish this event 413 // and a menu which is under development. 414 content::RecordAction(UserMetricsAction("EnterFullScreenWithWrenchMenu")); 415 } 416 417 chrome::ExecuteCommand(browser_, command_id); 418} 419 420bool WrenchMenuModel::IsCommandIdChecked(int command_id) const { 421 if (command_id == IDC_SHOW_BOOKMARK_BAR) { 422 return browser_->profile()->GetPrefs()->GetBoolean( 423 bookmarks::prefs::kShowBookmarkBar); 424 } else if (command_id == IDC_PROFILING_ENABLED) { 425 return Profiling::BeingProfiled(); 426 } else if (command_id == IDC_TOGGLE_REQUEST_TABLET_SITE) { 427 return chrome::IsRequestingTabletSite(browser_); 428 } 429 430 return false; 431} 432 433bool WrenchMenuModel::IsCommandIdEnabled(int command_id) const { 434 GlobalError* error = GlobalErrorServiceFactory::GetForProfile( 435 browser_->profile())->GetGlobalErrorByMenuItemCommandID(command_id); 436 if (error) 437 return true; 438 439 return chrome::IsCommandEnabled(browser_, command_id); 440} 441 442bool WrenchMenuModel::IsCommandIdVisible(int command_id) const { 443 switch (command_id) { 444#if defined(OS_WIN) 445 case IDC_VIEW_INCOMPATIBILITIES: { 446 EnumerateModulesModel* loaded_modules = 447 EnumerateModulesModel::GetInstance(); 448 if (loaded_modules->confirmed_bad_modules_detected() <= 0) 449 return false; 450 // We'll leave the wrench adornment on until the user clicks the link. 451 if (loaded_modules->modules_to_notify_about() <= 0) 452 loaded_modules->AcknowledgeConflictNotification(); 453 return true; 454 } 455 case IDC_PIN_TO_START_SCREEN: 456 return base::win::IsMetroProcess(); 457#else 458 case IDC_VIEW_INCOMPATIBILITIES: 459 case IDC_PIN_TO_START_SCREEN: 460 return false; 461#endif 462 case IDC_UPGRADE_DIALOG: 463 return UpgradeDetector::GetInstance()->notify_upgrade(); 464#if !defined(OS_LINUX) || defined(USE_AURA) 465 case IDC_BOOKMARK_PAGE: 466 return !chrome::ShouldRemoveBookmarkThisPageUI(browser_->profile()); 467 case IDC_BOOKMARK_ALL_TABS: 468 return !chrome::ShouldRemoveBookmarkOpenPagesUI(browser_->profile()); 469#endif 470 default: 471 return true; 472 } 473} 474 475bool WrenchMenuModel::GetAcceleratorForCommandId( 476 int command_id, 477 ui::Accelerator* accelerator) { 478 return provider_->GetAcceleratorForCommandId(command_id, accelerator); 479} 480 481void WrenchMenuModel::ActiveTabChanged(WebContents* old_contents, 482 WebContents* new_contents, 483 int index, 484 int reason) { 485 // The user has switched between tabs and the new tab may have a different 486 // zoom setting. 487 UpdateZoomControls(); 488} 489 490void WrenchMenuModel::TabReplacedAt(TabStripModel* tab_strip_model, 491 WebContents* old_contents, 492 WebContents* new_contents, 493 int index) { 494 UpdateZoomControls(); 495} 496 497void WrenchMenuModel::TabStripModelDeleted() { 498 // During views shutdown, the tabstrip model/browser is deleted first, while 499 // it is the opposite in gtk land. 500 tab_strip_model_->RemoveObserver(this); 501 tab_strip_model_ = NULL; 502} 503 504void WrenchMenuModel::Observe(int type, 505 const content::NotificationSource& source, 506 const content::NotificationDetails& details) { 507 DCHECK(type == content::NOTIFICATION_NAV_ENTRY_COMMITTED); 508 UpdateZoomControls(); 509} 510 511// For testing. 512WrenchMenuModel::WrenchMenuModel() 513 : ui::SimpleMenuModel(this), 514 provider_(NULL), 515 browser_(NULL), 516 tab_strip_model_(NULL) { 517} 518 519bool WrenchMenuModel::ShouldShowNewIncognitoWindowMenuItem() { 520 if (browser_->profile()->IsSupervised()) 521 return false; 522 523 return !browser_->profile()->IsGuestSession(); 524} 525 526void WrenchMenuModel::Build() { 527#if defined(OS_WIN) 528 AddItem(IDC_VIEW_INCOMPATIBILITIES, 529 l10n_util::GetStringUTF16(IDS_VIEW_INCOMPATIBILITIES)); 530 EnumerateModulesModel* model = 531 EnumerateModulesModel::GetInstance(); 532 if (model->modules_to_notify_about() > 0 || 533 model->confirmed_bad_modules_detected() > 0) 534 AddSeparator(ui::NORMAL_SEPARATOR); 535#endif 536 537 if (extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) 538 CreateExtensionToolbarOverflowMenu(); 539 540 AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB); 541 AddItemWithStringId(IDC_NEW_WINDOW, IDS_NEW_WINDOW); 542 543 if (ShouldShowNewIncognitoWindowMenuItem()) 544 AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW, IDS_NEW_INCOGNITO_WINDOW); 545 546 bookmark_sub_menu_model_.reset(new BookmarkSubMenuModel(this, browser_)); 547 AddSubMenuWithStringId(IDC_BOOKMARKS_MENU, IDS_BOOKMARKS_MENU, 548 bookmark_sub_menu_model_.get()); 549 550 if (!browser_->profile()->IsOffTheRecord()) { 551 recent_tabs_sub_menu_model_.reset(new RecentTabsSubMenuModel(provider_, 552 browser_, 553 NULL)); 554 AddSubMenuWithStringId(IDC_RECENT_TABS_MENU, IDS_RECENT_TABS_MENU, 555 recent_tabs_sub_menu_model_.get()); 556 } 557 558#if defined(OS_WIN) 559 if (base::win::GetVersion() >= base::win::VERSION_WIN8 && 560 content::GpuDataManager::GetInstance()->CanUseGpuBrowserCompositor()) { 561 if (browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH) { 562 // ASH/Metro mode, add the 'Relaunch Chrome in desktop mode'. 563 AddSeparator(ui::NORMAL_SEPARATOR); 564 AddItemWithStringId(IDC_WIN_DESKTOP_RESTART, IDS_WIN_DESKTOP_RESTART); 565 } else { 566 AddSeparator(ui::NORMAL_SEPARATOR); 567 AddItemWithStringId(IDC_WIN8_METRO_RESTART, IDS_WIN8_METRO_RESTART); 568 } 569 } 570#endif 571 572 // Append the full menu including separators. The final separator only gets 573 // appended when this is a touch menu - otherwise it would get added twice. 574 CreateCutCopyPasteMenu(); 575 576 if (CommandLine::ForCurrentProcess()->HasSwitch( 577 switches::kEnableDomDistiller)) { 578 AddItemWithStringId(IDC_DISTILL_PAGE, IDS_DISTILL_PAGE); 579 } 580 581 AddItemWithStringId(IDC_SAVE_PAGE, IDS_SAVE_PAGE); 582 AddItemWithStringId(IDC_FIND, IDS_FIND); 583 AddItemWithStringId(IDC_PRINT, IDS_PRINT); 584 585 tools_menu_model_.reset(new ToolsMenuModel(this, browser_)); 586 CreateZoomMenu(); 587 588 AddItemWithStringId(IDC_SHOW_HISTORY, IDS_SHOW_HISTORY); 589 AddItemWithStringId(IDC_SHOW_DOWNLOADS, IDS_SHOW_DOWNLOADS); 590 AddSeparator(ui::NORMAL_SEPARATOR); 591 592#if !defined(OS_CHROMEOS) 593 if (!switches::IsNewAvatarMenu()) { 594 // No "Sign in to Chromium..." menu item on ChromeOS. 595 SigninManager* signin = SigninManagerFactory::GetForProfile( 596 browser_->profile()->GetOriginalProfile()); 597 if (signin && signin->IsSigninAllowed()) { 598 const base::string16 short_product_name = 599 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME); 600 AddItem(IDC_SHOW_SYNC_SETUP, l10n_util::GetStringFUTF16( 601 IDS_SYNC_MENU_PRE_SYNCED_LABEL, short_product_name)); 602 AddSeparator(ui::NORMAL_SEPARATOR); 603 } 604 } 605#endif 606 607 AddItemWithStringId(IDC_OPTIONS, IDS_SETTINGS); 608 609// On ChromeOS we don't want the about menu option. 610#if !defined(OS_CHROMEOS) 611 AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT)); 612#endif 613 614#if defined(GOOGLE_CHROME_BUILD) 615 help_menu_model_.reset(new HelpMenuModel(this, browser_)); 616 AddSubMenuWithStringId(IDC_HELP_MENU, IDS_HELP_MENU, 617 help_menu_model_.get()); 618#endif 619 620#if defined(OS_CHROMEOS) 621 if (CommandLine::ForCurrentProcess()->HasSwitch( 622 chromeos::switches::kEnableRequestTabletSite)) 623 AddCheckItemWithStringId(IDC_TOGGLE_REQUEST_TABLET_SITE, 624 IDS_TOGGLE_REQUEST_TABLET_SITE); 625#endif 626 627 if (browser_defaults::kShowUpgradeMenuItem) 628 AddItem(IDC_UPGRADE_DIALOG, GetUpgradeDialogMenuItemName()); 629 630#if defined(OS_WIN) 631 SetIcon(GetIndexOfCommandId(IDC_VIEW_INCOMPATIBILITIES), 632 ui::ResourceBundle::GetSharedInstance(). 633 GetNativeImageNamed(IDR_INPUT_ALERT_MENU)); 634#endif 635 636 AddGlobalErrorMenuItems(); 637 638 AddSeparator(ui::NORMAL_SEPARATOR); 639 AddSubMenuWithStringId( 640 IDC_ZOOM_MENU, IDS_MORE_TOOLS_MENU, tools_menu_model_.get()); 641 642 bool show_exit_menu = browser_defaults::kShowExitMenuItem; 643#if defined(OS_WIN) 644 if (browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH) 645 show_exit_menu = false; 646#endif 647 648 if (show_exit_menu) { 649 AddSeparator(ui::NORMAL_SEPARATOR); 650 AddItemWithStringId(IDC_EXIT, IDS_EXIT); 651 } 652 653 RemoveTrailingSeparators(); 654} 655 656void WrenchMenuModel::AddGlobalErrorMenuItems() { 657 // TODO(sail): Currently we only build the wrench menu once per browser 658 // window. This means that if a new error is added after the menu is built 659 // it won't show in the existing wrench menu. To fix this we need to some 660 // how update the menu if new errors are added. 661 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 662 // GetSignedInServiceErrors() can modify the global error list, so call it 663 // before iterating through that list below. 664 std::vector<GlobalError*> signin_errors; 665 signin_errors = signin_ui_util::GetSignedInServiceErrors( 666 browser_->profile()->GetOriginalProfile()); 667 const GlobalErrorService::GlobalErrorList& errors = 668 GlobalErrorServiceFactory::GetForProfile(browser_->profile())->errors(); 669 for (GlobalErrorService::GlobalErrorList::const_iterator 670 it = errors.begin(); it != errors.end(); ++it) { 671 GlobalError* error = *it; 672 DCHECK(error); 673 if (error->HasMenuItem()) { 674#if !defined(OS_CHROMEOS) 675 // Don't add a signin error if it's already being displayed elsewhere. 676 if (std::find(signin_errors.begin(), signin_errors.end(), error) != 677 signin_errors.end()) { 678 MenuModel* model = this; 679 int index = 0; 680 if (MenuModel::GetModelAndIndexForCommandId( 681 IDC_SHOW_SIGNIN, &model, &index)) { 682 continue; 683 } 684 } 685#endif 686 687 AddItem(error->MenuItemCommandID(), error->MenuItemLabel()); 688 int icon_id = error->MenuItemIconResourceID(); 689 if (icon_id) { 690 const gfx::Image& image = rb.GetNativeImageNamed(icon_id); 691 SetIcon(GetIndexOfCommandId(error->MenuItemCommandID()), 692 image); 693 } 694 } 695 } 696} 697 698void WrenchMenuModel::CreateExtensionToolbarOverflowMenu() { 699#if defined(TOOLKIT_VIEWS) 700 AddItem(IDC_EXTENSIONS_OVERFLOW_MENU, base::string16()); 701 // We only add the separator if there are > 0 items to show in the overflow. 702 extensions::ExtensionToolbarModel* toolbar_model = 703 extensions::ExtensionToolbarModel::Get(browser_->profile()); 704 // A count of -1 means all actions are visible. 705 if (toolbar_model->GetVisibleIconCount() != -1) 706 AddSeparator(ui::UPPER_SEPARATOR); 707#endif // defined(TOOLKIT_VIEWS) 708} 709 710void WrenchMenuModel::CreateCutCopyPasteMenu() { 711 AddSeparator(ui::LOWER_SEPARATOR); 712 713#if defined(OS_POSIX) && !defined(TOOLKIT_VIEWS) 714 // WARNING: Mac does not use the ButtonMenuItemModel, but instead defines the 715 // layout for this menu item in Toolbar.xib. It does, however, use the 716 // command_id value from AddButtonItem() to identify this special item. 717 edit_menu_item_model_.reset(new ui::ButtonMenuItemModel(IDS_EDIT, this)); 718 edit_menu_item_model_->AddGroupItemWithStringId(IDC_CUT, IDS_CUT); 719 edit_menu_item_model_->AddGroupItemWithStringId(IDC_COPY, IDS_COPY); 720 edit_menu_item_model_->AddGroupItemWithStringId(IDC_PASTE, IDS_PASTE); 721 AddButtonItem(IDC_EDIT_MENU, edit_menu_item_model_.get()); 722#else 723 // WARNING: views/wrench_menu assumes these items are added in this order. If 724 // you change the order you'll need to update wrench_menu as well. 725 AddItemWithStringId(IDC_CUT, IDS_CUT); 726 AddItemWithStringId(IDC_COPY, IDS_COPY); 727 AddItemWithStringId(IDC_PASTE, IDS_PASTE); 728#endif 729 730 AddSeparator(ui::UPPER_SEPARATOR); 731} 732 733void WrenchMenuModel::CreateZoomMenu() { 734 // This menu needs to be enclosed by separators. 735 AddSeparator(ui::LOWER_SEPARATOR); 736 737#if defined(OS_POSIX) && !defined(TOOLKIT_VIEWS) 738 // WARNING: Mac does not use the ButtonMenuItemModel, but instead defines the 739 // layout for this menu item in Toolbar.xib. It does, however, use the 740 // command_id value from AddButtonItem() to identify this special item. 741 zoom_menu_item_model_.reset( 742 new ui::ButtonMenuItemModel(IDS_ZOOM_MENU, this)); 743 zoom_menu_item_model_->AddGroupItemWithStringId( 744 IDC_ZOOM_MINUS, IDS_ZOOM_MINUS2); 745 zoom_menu_item_model_->AddButtonLabel(IDC_ZOOM_PERCENT_DISPLAY, 746 IDS_ZOOM_PLUS2); 747 zoom_menu_item_model_->AddGroupItemWithStringId( 748 IDC_ZOOM_PLUS, IDS_ZOOM_PLUS2); 749 zoom_menu_item_model_->AddSpace(); 750 zoom_menu_item_model_->AddItemWithImage( 751 IDC_FULLSCREEN, IDR_FULLSCREEN_MENU_BUTTON); 752 AddButtonItem(IDC_ZOOM_MENU, zoom_menu_item_model_.get()); 753#else 754 // WARNING: views/wrench_menu assumes these items are added in this order. If 755 // you change the order you'll need to update wrench_menu as well. 756 AddItemWithStringId(IDC_ZOOM_MINUS, IDS_ZOOM_MINUS); 757 AddItemWithStringId(IDC_ZOOM_PLUS, IDS_ZOOM_PLUS); 758 AddItemWithStringId(IDC_FULLSCREEN, IDS_FULLSCREEN); 759#endif 760 761 AddSeparator(ui::UPPER_SEPARATOR); 762} 763 764void WrenchMenuModel::UpdateZoomControls() { 765 int zoom_percent = 100; 766 if (browser_->tab_strip_model()->GetActiveWebContents()) { 767 zoom_percent = ZoomController::FromWebContents( 768 browser_->tab_strip_model()->GetActiveWebContents()) 769 ->GetZoomPercent(); 770 } 771 zoom_label_ = l10n_util::GetStringFUTF16( 772 IDS_ZOOM_PERCENT, base::IntToString16(zoom_percent)); 773} 774 775void WrenchMenuModel::OnZoomLevelChanged( 776 const content::HostZoomMap::ZoomLevelChange& change) { 777 UpdateZoomControls(); 778} 779