wrench_menu_model.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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 75using content::HostZoomMap; 76using content::UserMetricsAction; 77using content::WebContents; 78 79namespace { 80// Conditionally return the update app menu item title based on upgrade detector 81// state. 82string16 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 97EncodingMenuModel::EncodingMenuModel(Browser* browser) 98 : ui::SimpleMenuModel(this), 99 browser_(browser) { 100 Build(); 101} 102 103EncodingMenuModel::~EncodingMenuModel() { 104} 105 106void 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 133bool 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 143bool 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 154bool EncodingMenuModel::GetAcceleratorForCommandId( 155 int command_id, 156 ui::Accelerator* accelerator) { 157 return false; 158} 159 160void EncodingMenuModel::ExecuteCommand(int command_id, int event_flags) { 161 chrome::ExecuteCommand(browser_, command_id); 162} 163 164//////////////////////////////////////////////////////////////////////////////// 165// ZoomMenuModel 166 167ZoomMenuModel::ZoomMenuModel(ui::SimpleMenuModel::Delegate* delegate) 168 : SimpleMenuModel(delegate) { 169 Build(); 170} 171 172ZoomMenuModel::~ZoomMenuModel() { 173} 174 175void 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 184ToolsMenuModel::ToolsMenuModel(ui::SimpleMenuModel::Delegate* delegate, 185 Browser* browser) 186 : SimpleMenuModel(delegate) { 187 Build(browser); 188} 189 190ToolsMenuModel::~ToolsMenuModel() {} 191 192void 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 238WrenchMenuModel::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 259WrenchMenuModel::~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 269bool WrenchMenuModel::DoesCommandIdDismissMenu(int command_id) const { 270 return command_id != IDC_ZOOM_MINUS && command_id != IDC_ZOOM_PLUS; 271} 272 273bool 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 284string16 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 320bool 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 351void 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 382bool 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 394bool 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 403bool 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 427bool WrenchMenuModel::GetAcceleratorForCommandId( 428 int command_id, 429 ui::Accelerator* accelerator) { 430 return provider_->GetAcceleratorForCommandId(command_id, accelerator); 431} 432 433void 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 442void WrenchMenuModel::TabReplacedAt(TabStripModel* tab_strip_model, 443 WebContents* old_contents, 444 WebContents* new_contents, 445 int index) { 446 UpdateZoomControls(); 447} 448 449void 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 456void 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. 464WrenchMenuModel::WrenchMenuModel() 465 : ui::SimpleMenuModel(this), 466 provider_(NULL), 467 browser_(NULL), 468 tab_strip_model_(NULL) { 469} 470 471void WrenchMenuModel::Build(bool is_new_menu) { 472#if defined(OS_WIN) 473 AddItem(IDC_VIEW_INCOMPATIBILITIES, 474 l10n_util::GetStringUTF16(IDS_VIEW_INCOMPATIBILITIES)); 475 EnumerateModulesModel* model = 476 EnumerateModulesModel::GetInstance(); 477 if (model->modules_to_notify_about() > 0 || 478 model->confirmed_bad_modules_detected() > 0) 479 AddSeparator(ui::NORMAL_SEPARATOR); 480#endif 481 482 AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB); 483#if defined(OS_WIN) 484 if (win8::IsSingleWindowMetroMode()) { 485 // In Win8's single window Metro mode, we only show the New Window options 486 // if there isn't already a window of the requested type (incognito or not) 487 // that is available. 488 if (browser_->profile()->IsOffTheRecord()) { 489 if (chrome::FindBrowserWithProfile( 490 browser_->profile()->GetOriginalProfile(), 491 browser_->host_desktop_type()) == NULL) { 492 AddItemWithStringId(IDC_NEW_WINDOW, IDS_NEW_WINDOW); 493 } 494 } else if (!browser_->profile()->HasOffTheRecordProfile()) { 495 AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW, IDS_NEW_INCOGNITO_WINDOW); 496 } 497 } else { 498 AddItemWithStringId(IDC_NEW_WINDOW, IDS_NEW_WINDOW); 499 AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW, IDS_NEW_INCOGNITO_WINDOW); 500 } 501#if !defined(NDEBUG) && defined(USE_ASH) 502 if (base::win::GetVersion() < base::win::VERSION_WIN8 && 503 chrome::HOST_DESKTOP_TYPE_NATIVE != chrome::HOST_DESKTOP_TYPE_ASH) { 504 AddItemWithStringId(IDC_TOGGLE_ASH_DESKTOP, 505 ash::Shell::HasInstance() ? IDS_CLOSE_ASH_DESKTOP : 506 IDS_OPEN_ASH_DESKTOP); 507 } 508#endif 509#else // defined(OS_WIN) 510 AddItemWithStringId(IDC_NEW_WINDOW, IDS_NEW_WINDOW); 511#if defined(OS_CHROMEOS) 512 if (!CommandLine::ForCurrentProcess()->HasSwitch( 513 chromeos::switches::kGuestSession)) 514 AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW, IDS_NEW_INCOGNITO_WINDOW); 515#else 516 AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW, IDS_NEW_INCOGNITO_WINDOW); 517#endif 518 519#endif // else of defined(OS_WIN) 520 521 bookmark_sub_menu_model_.reset(new BookmarkSubMenuModel(this, browser_)); 522 AddSubMenuWithStringId(IDC_BOOKMARKS_MENU, IDS_BOOKMARKS_MENU, 523 bookmark_sub_menu_model_.get()); 524 525 if (chrome::IsInstantExtendedAPIEnabled()) { 526 recent_tabs_sub_menu_model_.reset(new RecentTabsSubMenuModel(provider_, 527 browser_, 528 NULL)); 529 AddSubMenuWithStringId(IDC_RECENT_TABS_MENU, IDS_RECENT_TABS_MENU, 530 recent_tabs_sub_menu_model_.get()); 531 } 532 533#if defined(OS_WIN) && !defined(USE_ASH) 534 if (base::win::IsMetroProcess()) { 535 // Metro mode, add the 'Relaunch Chrome in desktop mode'. 536 AddSeparator(ui::SPACING_SEPARATOR); 537 AddItemWithStringId(IDC_WIN8_DESKTOP_RESTART, IDS_WIN8_DESKTOP_RESTART); 538 } else if (base::win::GetVersion() >= base::win::VERSION_WIN8) { 539 // In Windows 8 desktop, add the 'Relaunch Chrome in Windows 8 mode'. 540 AddSeparator(ui::SPACING_SEPARATOR); 541 AddItemWithStringId(IDC_WIN8_METRO_RESTART, IDS_WIN8_METRO_RESTART); 542 } 543#endif 544 545 // Append the full menu including separators. The final separator only gets 546 // appended when this is a touch menu - otherwise it would get added twice. 547 CreateCutCopyPasteMenu(is_new_menu); 548 549 if (!is_new_menu) 550 CreateZoomMenu(is_new_menu); 551 552 AddItemWithStringId(IDC_SAVE_PAGE, IDS_SAVE_PAGE); 553 AddItemWithStringId(IDC_FIND, IDS_FIND); 554 AddItemWithStringId(IDC_PRINT, IDS_PRINT); 555 556 tools_menu_model_.reset(new ToolsMenuModel(this, browser_)); 557 // In case of touch this is the last item. 558 if (!is_new_menu) { 559 AddSubMenuWithStringId(IDC_ZOOM_MENU, IDS_TOOLS_MENU, 560 tools_menu_model_.get()); 561 } 562 563 if (is_new_menu) 564 CreateZoomMenu(is_new_menu); 565 else 566 AddSeparator(ui::NORMAL_SEPARATOR); 567 568 AddItemWithStringId(IDC_SHOW_HISTORY, IDS_SHOW_HISTORY); 569 AddItemWithStringId(IDC_SHOW_DOWNLOADS, IDS_SHOW_DOWNLOADS); 570 AddSeparator(ui::NORMAL_SEPARATOR); 571 572#if !defined(OS_CHROMEOS) 573 // No "Sign in to Chromium..." menu item on ChromeOS. 574 SigninManager* signin = SigninManagerFactory::GetForProfile( 575 browser_->profile()->GetOriginalProfile()); 576 if (signin && signin->IsSigninAllowed()) { 577 const string16 short_product_name = 578 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME); 579 AddItem(IDC_SHOW_SYNC_SETUP, l10n_util::GetStringFUTF16( 580 IDS_SYNC_MENU_PRE_SYNCED_LABEL, short_product_name)); 581 AddSeparator(ui::NORMAL_SEPARATOR); 582 } 583#endif 584 585 AddItemWithStringId(IDC_OPTIONS, IDS_SETTINGS); 586 587#if defined(OS_CHROMEOS) 588 if (CommandLine::ForCurrentProcess()->HasSwitch( 589 chromeos::switches::kEnableRequestTabletSite)) 590 AddCheckItemWithStringId(IDC_TOGGLE_REQUEST_TABLET_SITE, 591 IDS_TOGGLE_REQUEST_TABLET_SITE); 592#endif 593 594// On ChromeOS-Touch, we don't want the about menu option. 595#if defined(OS_CHROMEOS) 596 if (!is_new_menu) 597#endif 598 { 599 AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT)); 600 } 601 602 if (browser_defaults::kShowUpgradeMenuItem) 603 AddItem(IDC_UPGRADE_DIALOG, GetUpgradeDialogMenuItemName()); 604 605#if defined(OS_WIN) 606 SetIcon(GetIndexOfCommandId(IDC_VIEW_INCOMPATIBILITIES), 607 ui::ResourceBundle::GetSharedInstance(). 608 GetNativeImageNamed(IDR_INPUT_ALERT_MENU)); 609#endif 610 611 if (!is_new_menu) { 612 AddItemWithStringId(IDC_HELP_PAGE_VIA_MENU, IDS_HELP_PAGE); 613 614 if (browser_defaults::kShowHelpMenuItemIcon) { 615 ui::ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 616 SetIcon(GetIndexOfCommandId(IDC_HELP_PAGE_VIA_MENU), 617 rb.GetNativeImageNamed(IDR_HELP_MENU)); 618 } 619 } 620 621 if (browser_defaults::kShowFeedbackMenuItem && 622 !chrome::UseAlternateSendFeedbackLocation()) { 623 AddItemWithStringId(IDC_FEEDBACK, 624 chrome::GetSendFeedbackMenuLabelID()); 625 } 626 627 AddGlobalErrorMenuItems(); 628 629 if (is_new_menu) { 630 AddSubMenuWithStringId(IDC_ZOOM_MENU, IDS_MORE_TOOLS_MENU, 631 tools_menu_model_.get()); 632 } 633 634#if !defined(OS_CHROMEOS) 635 // For Send Feedback Link experiment (crbug.com/169339). 636 if (chrome::UseAlternateSendFeedbackLocation()) 637 AddItemWithStringId(IDC_FEEDBACK, 638 chrome::GetSendFeedbackMenuLabelID()); 639#endif 640 641 bool show_exit_menu = browser_defaults::kShowExitMenuItem; 642#if defined(OS_WIN) && defined(USE_AURA) 643 if (browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH) 644 show_exit_menu = false; 645#endif 646 647 if (show_exit_menu) { 648 AddSeparator(ui::NORMAL_SEPARATOR); 649 AddItemWithStringId(IDC_EXIT, IDS_EXIT); 650 } 651} 652 653void WrenchMenuModel::AddGlobalErrorMenuItems() { 654 // TODO(sail): Currently we only build the wrench menu once per browser 655 // window. This means that if a new error is added after the menu is built 656 // it won't show in the existing wrench menu. To fix this we need to some 657 // how update the menu if new errors are added. 658 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 659 const GlobalErrorService::GlobalErrorList& errors = 660 GlobalErrorServiceFactory::GetForProfile(browser_->profile())->errors(); 661 for (GlobalErrorService::GlobalErrorList::const_iterator 662 it = errors.begin(); it != errors.end(); ++it) { 663 GlobalError* error = *it; 664 if (error->HasMenuItem()) { 665 // Don't add a signin error if it's already being displayed elsewhere. 666#if !defined(OS_CHROMEOS) 667 std::vector<GlobalError*> errors = 668 signin_ui_util::GetSignedInServiceErrors( 669 browser_->profile()->GetOriginalProfile()); 670 if (std::find(errors.begin(), errors.end(), error) != errors.end()) { 671 MenuModel* model = this; 672 int index = 0; 673 if (MenuModel::GetModelAndIndexForCommandId( 674 IDC_SHOW_SIGNIN, &model, &index)) { 675 continue; 676 } 677 } 678#endif 679 680 AddItem(error->MenuItemCommandID(), error->MenuItemLabel()); 681 int icon_id = error->MenuItemIconResourceID(); 682 if (icon_id) { 683 const gfx::Image& image = rb.GetNativeImageNamed(icon_id); 684 SetIcon(GetIndexOfCommandId(error->MenuItemCommandID()), 685 image); 686 } 687 } 688 } 689} 690 691void WrenchMenuModel::CreateCutCopyPasteMenu(bool new_menu) { 692 AddSeparator(new_menu ? ui::LOWER_SEPARATOR : ui::NORMAL_SEPARATOR); 693 694#if defined(OS_POSIX) && !defined(TOOLKIT_VIEWS) 695 // WARNING: Mac does not use the ButtonMenuItemModel, but instead defines the 696 // layout for this menu item in Toolbar.xib. It does, however, use the 697 // command_id value from AddButtonItem() to identify this special item. 698 edit_menu_item_model_.reset(new ui::ButtonMenuItemModel(IDS_EDIT, this)); 699 edit_menu_item_model_->AddGroupItemWithStringId(IDC_CUT, IDS_CUT); 700 edit_menu_item_model_->AddGroupItemWithStringId(IDC_COPY, IDS_COPY); 701 edit_menu_item_model_->AddGroupItemWithStringId(IDC_PASTE, IDS_PASTE); 702 AddButtonItem(IDC_EDIT_MENU, edit_menu_item_model_.get()); 703#else 704 // WARNING: views/wrench_menu assumes these items are added in this order. If 705 // you change the order you'll need to update wrench_menu as well. 706 AddItemWithStringId(IDC_CUT, IDS_CUT); 707 AddItemWithStringId(IDC_COPY, IDS_COPY); 708 AddItemWithStringId(IDC_PASTE, IDS_PASTE); 709#endif 710 711 if (new_menu) 712 AddSeparator(ui::UPPER_SEPARATOR); 713} 714 715void WrenchMenuModel::CreateZoomMenu(bool new_menu) { 716 // This menu needs to be enclosed by separators. 717 AddSeparator(new_menu ? ui::LOWER_SEPARATOR : ui::NORMAL_SEPARATOR); 718 719#if defined(OS_POSIX) && !defined(TOOLKIT_VIEWS) 720 // WARNING: Mac does not use the ButtonMenuItemModel, but instead defines the 721 // layout for this menu item in Toolbar.xib. It does, however, use the 722 // command_id value from AddButtonItem() to identify this special item. 723 zoom_menu_item_model_.reset( 724 new ui::ButtonMenuItemModel(IDS_ZOOM_MENU, this)); 725 zoom_menu_item_model_->AddGroupItemWithStringId( 726 IDC_ZOOM_MINUS, IDS_ZOOM_MINUS2); 727 zoom_menu_item_model_->AddButtonLabel(IDC_ZOOM_PERCENT_DISPLAY, 728 IDS_ZOOM_PLUS2); 729 zoom_menu_item_model_->AddGroupItemWithStringId( 730 IDC_ZOOM_PLUS, IDS_ZOOM_PLUS2); 731 zoom_menu_item_model_->AddSpace(); 732 zoom_menu_item_model_->AddItemWithImage( 733 IDC_FULLSCREEN, IDR_FULLSCREEN_MENU_BUTTON); 734 AddButtonItem(IDC_ZOOM_MENU, zoom_menu_item_model_.get()); 735#else 736 // WARNING: views/wrench_menu assumes these items are added in this order. If 737 // you change the order you'll need to update wrench_menu as well. 738 AddItemWithStringId(IDC_ZOOM_MINUS, IDS_ZOOM_MINUS); 739 AddItemWithStringId(IDC_ZOOM_PLUS, IDS_ZOOM_PLUS); 740 AddItemWithStringId(IDC_FULLSCREEN, IDS_FULLSCREEN); 741#endif 742 743 AddSeparator(new_menu ? ui::UPPER_SEPARATOR : ui::NORMAL_SEPARATOR); 744} 745 746void WrenchMenuModel::UpdateZoomControls() { 747 bool enable_increment = false; 748 bool enable_decrement = false; 749 int zoom_percent = 100; 750 if (browser_->tab_strip_model()->GetActiveWebContents()) { 751 zoom_percent = 752 browser_->tab_strip_model()->GetActiveWebContents()->GetZoomPercent( 753 &enable_increment, &enable_decrement); 754 } 755 zoom_label_ = l10n_util::GetStringFUTF16( 756 IDS_ZOOM_PERCENT, base::IntToString16(zoom_percent)); 757} 758 759void WrenchMenuModel::OnZoomLevelChanged( 760 const content::HostZoomMap::ZoomLevelChange& change) { 761 UpdateZoomControls(); 762} 763