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