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