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