Home | History | Annotate | Download | only in toolbar
      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