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