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