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