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.
5 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
10 #include "base/command_line.h"
11 #include "base/metrics/histogram.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/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/extensions/extension_util.h"
20 #include "chrome/browser/prefs/incognito_mode_prefs.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/profiles/profile_manager.h"
23 #include "chrome/browser/search/search.h"
24 #include "chrome/browser/signin/signin_manager_factory.h"
25 #include "chrome/browser/signin/signin_ui_util.h"
26 #include "chrome/browser/task_manager/task_manager.h"
27 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
28 #include "chrome/browser/ui/browser.h"
29 #include "chrome/browser/ui/browser_commands.h"
30 #include "chrome/browser/ui/browser_finder.h"
31 #include "chrome/browser/ui/browser_window.h"
32 #include "chrome/browser/ui/global_error/global_error.h"
33 #include "chrome/browser/ui/global_error/global_error_service.h"
34 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
35 #include "chrome/browser/ui/tabs/tab_strip_model.h"
36 #include "chrome/browser/ui/toolbar/bookmark_sub_menu_model.h"
37 #include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
38 #include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
39 #include "chrome/browser/ui/toolbar/toolbar_actions_model.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/dom_distiller/core/dom_distiller_switches.h"
48 #include "components/signin/core/browser/signin_manager.h"
49 #include "components/signin/core/common/profile_management_switches.h"
50 #include "components/ui/zoom/zoom_controller.h"
51 #include "components/ui/zoom/zoom_event_manager.h"
52 #include "content/public/browser/host_zoom_map.h"
53 #include "content/public/browser/navigation_entry.h"
54 #include "content/public/browser/notification_service.h"
55 #include "content/public/browser/notification_source.h"
56 #include "content/public/browser/notification_types.h"
57 #include "content/public/browser/user_metrics.h"
58 #include "content/public/browser/web_contents.h"
59 #include "extensions/common/feature_switch.h"
60 #include "grit/theme_resources.h"
61 #include "ui/base/l10n/l10n_util.h"
62 #include "ui/base/layout.h"
63 #include "ui/base/models/button_menu_item_model.h"
64 #include "ui/base/resource/resource_bundle.h"
65 #include "ui/gfx/image/image.h"
66 #include "ui/gfx/image/image_skia.h"
68 #if defined(OS_CHROMEOS)
69 #include "chromeos/chromeos_switches.h"
73 #include "base/win/metro.h"
74 #include "base/win/windows_version.h"
75 #include "chrome/browser/enumerate_modules_model_win.h"
76 #include "chrome/browser/ui/metro_pin_tab_helper_win.h"
77 #include "content/public/browser/gpu_data_manager.h"
81 #include "ash/shell.h"
84 using base::UserMetricsAction
;
85 using content::WebContents
;
89 #if defined(OS_MACOSX)
90 // An empty command used because of a bug in AppKit menus.
91 // See comment in CreateActionToolbarOverflowMenu().
92 const int kEmptyMenuItemCommand
= 0;
95 // Conditionally return the update app menu item title based on upgrade detector
97 base::string16
GetUpgradeDialogMenuItemName() {
98 if (UpgradeDetector::GetInstance()->is_outdated_install() ||
99 UpgradeDetector::GetInstance()->is_outdated_install_no_au()) {
100 return l10n_util::GetStringUTF16(IDS_UPGRADE_BUBBLE_MENU_ITEM
);
102 return l10n_util::GetStringUTF16(IDS_UPDATE_NOW
);
107 bool GetRestartMenuItemIfRequired(const chrome::HostDesktopType
& desktop_type
,
110 if (base::win::GetVersion() == base::win::VERSION_WIN8
||
111 base::win::GetVersion() == base::win::VERSION_WIN8_1
) {
112 if (desktop_type
!= chrome::HOST_DESKTOP_TYPE_ASH
) {
113 *command_id
= IDC_WIN8_METRO_RESTART
;
114 *string_id
= IDS_WIN8_METRO_RESTART
;
116 *command_id
= IDC_WIN_DESKTOP_RESTART
;
117 *string_id
= IDS_WIN_DESKTOP_RESTART
;
122 // Windows 7 ASH mode is only supported in DEBUG for now.
124 // Windows 8 can support ASH mode using WARP, but Windows 7 requires a working
126 if (base::win::GetVersion() == base::win::VERSION_WIN7
&&
127 content::GpuDataManager::GetInstance()->CanUseGpuBrowserCompositor()) {
128 if (desktop_type
!= chrome::HOST_DESKTOP_TYPE_ASH
) {
129 *command_id
= IDC_WIN_CHROMEOS_RESTART
;
130 *string_id
= IDS_WIN_CHROMEOS_RESTART
;
132 *command_id
= IDC_WIN_DESKTOP_RESTART
;
133 *string_id
= IDS_WIN_DESKTOP_RESTART
;
144 ////////////////////////////////////////////////////////////////////////////////
147 EncodingMenuModel::EncodingMenuModel(Browser
* browser
)
148 : ui::SimpleMenuModel(this),
153 EncodingMenuModel::~EncodingMenuModel() {
156 void EncodingMenuModel::Build() {
157 EncodingMenuController::EncodingMenuItemList encoding_menu_items
;
158 EncodingMenuController encoding_menu_controller
;
159 encoding_menu_controller
.GetEncodingMenuItems(browser_
->profile(),
160 &encoding_menu_items
);
163 EncodingMenuController::EncodingMenuItemList::iterator it
=
164 encoding_menu_items
.begin();
165 for (; it
!= encoding_menu_items
.end(); ++it
) {
167 base::string16
& label
= it
->second
;
169 AddSeparator(ui::NORMAL_SEPARATOR
);
171 if (id
== IDC_ENCODING_AUTO_DETECT
) {
172 AddCheckItem(id
, label
);
174 // Use the id of the first radio command as the id of the group.
177 AddRadioItem(id
, label
, group_id
);
183 bool EncodingMenuModel::IsCommandIdChecked(int command_id
) const {
184 WebContents
* current_tab
=
185 browser_
->tab_strip_model()->GetActiveWebContents();
188 EncodingMenuController controller
;
189 return controller
.IsItemChecked(browser_
->profile(),
190 current_tab
->GetEncoding(), command_id
);
193 bool EncodingMenuModel::IsCommandIdEnabled(int command_id
) const {
194 bool enabled
= chrome::IsCommandEnabled(browser_
, command_id
);
195 // Special handling for the contents of the Encoding submenu. On Mac OS,
196 // instead of enabling/disabling the top-level menu item, the submenu's
197 // contents get disabled, per Apple's HIG.
198 #if defined(OS_MACOSX)
199 enabled
&= chrome::IsCommandEnabled(browser_
, IDC_ENCODING_MENU
);
204 bool EncodingMenuModel::GetAcceleratorForCommandId(
206 ui::Accelerator
* accelerator
) {
210 void EncodingMenuModel::ExecuteCommand(int command_id
, int event_flags
) {
211 chrome::ExecuteCommand(browser_
, command_id
);
214 ////////////////////////////////////////////////////////////////////////////////
217 ZoomMenuModel::ZoomMenuModel(ui::SimpleMenuModel::Delegate
* delegate
)
218 : SimpleMenuModel(delegate
) {
222 ZoomMenuModel::~ZoomMenuModel() {
225 void ZoomMenuModel::Build() {
226 AddItemWithStringId(IDC_ZOOM_PLUS
, IDS_ZOOM_PLUS
);
227 AddItemWithStringId(IDC_ZOOM_NORMAL
, IDS_ZOOM_NORMAL
);
228 AddItemWithStringId(IDC_ZOOM_MINUS
, IDS_ZOOM_MINUS
);
231 ////////////////////////////////////////////////////////////////////////////////
234 #if defined(GOOGLE_CHROME_BUILD)
236 class WrenchMenuModel::HelpMenuModel
: public ui::SimpleMenuModel
{
238 HelpMenuModel(ui::SimpleMenuModel::Delegate
* delegate
,
240 : SimpleMenuModel(delegate
) {
245 void Build(Browser
* browser
) {
246 #if defined(OS_CHROMEOS) && defined(OFFICIAL_BUILD)
247 int help_string_id
= IDS_GET_HELP
;
249 int help_string_id
= IDS_HELP_PAGE
;
251 AddItem(IDC_ABOUT
, l10n_util::GetStringUTF16(IDS_ABOUT
));
252 AddItemWithStringId(IDC_HELP_PAGE_VIA_MENU
, help_string_id
);
253 if (browser_defaults::kShowHelpMenuItemIcon
) {
254 ui::ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
255 SetIcon(GetIndexOfCommandId(IDC_HELP_PAGE_VIA_MENU
),
256 rb
.GetNativeImageNamed(IDR_HELP_MENU
));
258 AddItemWithStringId(IDC_FEEDBACK
, IDS_FEEDBACK
);
261 DISALLOW_COPY_AND_ASSIGN(HelpMenuModel
);
264 #endif // defined(GOOGLE_CHROME_BUILD)
266 ////////////////////////////////////////////////////////////////////////////////
269 ToolsMenuModel::ToolsMenuModel(ui::SimpleMenuModel::Delegate
* delegate
,
271 : SimpleMenuModel(delegate
) {
275 ToolsMenuModel::~ToolsMenuModel() {}
277 // More tools submenu is constructed as follows:
278 // - Page specific actions overflow (save page, adding to taskbar).
279 // - Browser / OS level tools (extensions, task manager).
280 // - Developer tools.
281 // - Option to enable profiling.
282 void ToolsMenuModel::Build(Browser
* browser
) {
283 bool show_create_shortcuts
= true;
284 #if defined(OS_CHROMEOS) || defined(OS_MACOSX)
285 show_create_shortcuts
= false;
286 #elif defined(USE_ASH)
287 if (browser
->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH
)
288 show_create_shortcuts
= false;
290 AddItemWithStringId(IDC_SAVE_PAGE
, IDS_SAVE_PAGE
);
291 if (extensions::util::IsNewBookmarkAppsEnabled()) {
292 #if defined(OS_MACOSX)
293 int string_id
= IDS_ADD_TO_APPLICATIONS
;
294 #elif defined(OS_WIN)
295 int string_id
= IDS_ADD_TO_TASKBAR
;
297 int string_id
= IDS_ADD_TO_DESKTOP
;
300 if (browser
->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH
)
301 string_id
= IDS_ADD_TO_SHELF
;
303 AddItemWithStringId(IDC_CREATE_HOSTED_APP
, string_id
);
304 } else if (show_create_shortcuts
) {
305 AddItemWithStringId(IDC_CREATE_SHORTCUTS
, IDS_CREATE_SHORTCUTS
);
308 AddSeparator(ui::NORMAL_SEPARATOR
);
309 AddItemWithStringId(IDC_CLEAR_BROWSING_DATA
, IDS_CLEAR_BROWSING_DATA
);
310 AddItemWithStringId(IDC_MANAGE_EXTENSIONS
, IDS_SHOW_EXTENSIONS
);
311 if (chrome::CanOpenTaskManager())
312 AddItemWithStringId(IDC_TASK_MANAGER
, IDS_TASK_MANAGER
);
313 #if defined(OS_CHROMEOS)
314 AddItemWithStringId(IDC_TAKE_SCREENSHOT
, IDS_TAKE_SCREENSHOT
);
316 encoding_menu_model_
.reset(new EncodingMenuModel(browser
));
317 AddSubMenuWithStringId(IDC_ENCODING_MENU
, IDS_ENCODING_MENU
,
318 encoding_menu_model_
.get());
320 AddSeparator(ui::NORMAL_SEPARATOR
);
321 AddItemWithStringId(IDC_DEV_TOOLS
, IDS_DEV_TOOLS
);
323 #if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
324 AddSeparator(ui::NORMAL_SEPARATOR
);
325 AddCheckItemWithStringId(IDC_PROFILING_ENABLED
, IDS_PROFILING_ENABLED
);
329 ////////////////////////////////////////////////////////////////////////////////
332 WrenchMenuModel::WrenchMenuModel(ui::AcceleratorProvider
* provider
,
334 : ui::SimpleMenuModel(this),
335 uma_action_recorded_(false),
338 tab_strip_model_(browser_
->tab_strip_model()) {
340 UpdateZoomControls();
342 browser_zoom_subscription_
=
343 ui_zoom::ZoomEventManager::GetForBrowserContext(browser
->profile())
344 ->AddZoomLevelChangedCallback(base::Bind(
345 &WrenchMenuModel::OnZoomLevelChanged
, base::Unretained(this)));
347 tab_strip_model_
->AddObserver(this);
349 registrar_
.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED
,
350 content::NotificationService::AllSources());
353 WrenchMenuModel::~WrenchMenuModel() {
354 if (tab_strip_model_
)
355 tab_strip_model_
->RemoveObserver(this);
358 bool WrenchMenuModel::DoesCommandIdDismissMenu(int command_id
) const {
359 return command_id
!= IDC_ZOOM_MINUS
&& command_id
!= IDC_ZOOM_PLUS
;
362 bool WrenchMenuModel::IsItemForCommandIdDynamic(int command_id
) const {
363 return command_id
== IDC_ZOOM_PERCENT_DISPLAY
||
364 #if defined(OS_MACOSX)
365 command_id
== IDC_FULLSCREEN
||
366 #elif defined(OS_WIN)
367 command_id
== IDC_PIN_TO_START_SCREEN
||
369 command_id
== IDC_UPGRADE_DIALOG
;
372 base::string16
WrenchMenuModel::GetLabelForCommandId(int command_id
) const {
373 switch (command_id
) {
374 case IDC_ZOOM_PERCENT_DISPLAY
:
376 #if defined(OS_MACOSX)
377 case IDC_FULLSCREEN
: {
378 int string_id
= IDS_ENTER_FULLSCREEN_MAC
; // Default to Enter.
379 // Note: On startup, |window()| may be NULL.
380 if (browser_
->window() && browser_
->window()->IsFullscreen())
381 string_id
= IDS_EXIT_FULLSCREEN_MAC
;
382 return l10n_util::GetStringUTF16(string_id
);
384 #elif defined(OS_WIN)
385 case IDC_PIN_TO_START_SCREEN
: {
386 int string_id
= IDS_PIN_TO_START_SCREEN
;
387 WebContents
* web_contents
=
388 browser_
->tab_strip_model()->GetActiveWebContents();
389 MetroPinTabHelper
* tab_helper
=
390 web_contents
? MetroPinTabHelper::FromWebContents(web_contents
)
392 if (tab_helper
&& tab_helper
->IsPinned())
393 string_id
= IDS_UNPIN_FROM_START_SCREEN
;
394 return l10n_util::GetStringUTF16(string_id
);
397 case IDC_UPGRADE_DIALOG
:
398 return GetUpgradeDialogMenuItemName();
401 return base::string16();
405 bool WrenchMenuModel::GetIconForCommandId(int command_id
,
406 gfx::Image
* icon
) const {
407 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
408 switch (command_id
) {
409 case IDC_UPGRADE_DIALOG
: {
410 if (UpgradeDetector::GetInstance()->notify_upgrade()) {
411 *icon
= rb
.GetNativeImageNamed(
412 UpgradeDetector::GetInstance()->GetIconResourceID());
423 void WrenchMenuModel::ExecuteCommand(int command_id
, int event_flags
) {
424 GlobalError
* error
= GlobalErrorServiceFactory::GetForProfile(
425 browser_
->profile())->GetGlobalErrorByMenuItemCommandID(command_id
);
427 error
->ExecuteMenuItem(browser_
);
431 LogMenuMetrics(command_id
);
432 chrome::ExecuteCommand(browser_
, command_id
);
435 void WrenchMenuModel::LogMenuMetrics(int command_id
) {
436 base::TimeDelta delta
= timer_
.Elapsed();
438 switch (command_id
) {
440 if (!uma_action_recorded_
)
441 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.NewTab", delta
);
442 LogMenuAction(MENU_ACTION_NEW_TAB
);
445 if (!uma_action_recorded_
)
446 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.NewWindow", delta
);
447 LogMenuAction(MENU_ACTION_NEW_WINDOW
);
449 case IDC_NEW_INCOGNITO_WINDOW
:
450 if (!uma_action_recorded_
) {
451 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.NewIncognitoWindow",
454 LogMenuAction(MENU_ACTION_NEW_INCOGNITO_WINDOW
);
457 // Bookmarks sub menu.
458 case IDC_SHOW_BOOKMARK_BAR
:
459 if (!uma_action_recorded_
) {
460 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowBookmarkBar",
463 LogMenuAction(MENU_ACTION_SHOW_BOOKMARK_BAR
);
465 case IDC_SHOW_BOOKMARK_MANAGER
:
466 if (!uma_action_recorded_
) {
467 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowBookmarkMgr",
470 LogMenuAction(MENU_ACTION_SHOW_BOOKMARK_MANAGER
);
472 case IDC_IMPORT_SETTINGS
:
473 if (!uma_action_recorded_
) {
474 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ImportSettings",
477 LogMenuAction(MENU_ACTION_IMPORT_SETTINGS
);
479 case IDC_BOOKMARK_PAGE
:
480 if (!uma_action_recorded_
) {
481 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.BookmarkPage",
484 LogMenuAction(MENU_ACTION_BOOKMARK_PAGE
);
486 case IDC_BOOKMARK_ALL_TABS
:
487 if (!uma_action_recorded_
) {
488 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.BookmarkAllTabs",
491 LogMenuAction(MENU_ACTION_BOOKMARK_ALL_TABS
);
493 case IDC_PIN_TO_START_SCREEN
:
494 if (!uma_action_recorded_
) {
495 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.PinToStartScreen",
498 LogMenuAction(MENU_ACTION_PIN_TO_START_SCREEN
);
502 case IDC_RESTORE_TAB
:
503 if (!uma_action_recorded_
)
504 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.RestoreTab", delta
);
505 LogMenuAction(MENU_ACTION_RESTORE_TAB
);
509 case IDC_WIN_DESKTOP_RESTART
:
510 if (!uma_action_recorded_
) {
511 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.WinDesktopRestart",
514 LogMenuAction(MENU_ACTION_WIN_DESKTOP_RESTART
);
516 case IDC_WIN8_METRO_RESTART
:
517 if (!uma_action_recorded_
) {
518 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Win8MetroRestart",
521 LogMenuAction(MENU_ACTION_WIN8_METRO_RESTART
);
524 case IDC_WIN_CHROMEOS_RESTART
:
525 if (!uma_action_recorded_
) {
526 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ChromeOSRestart",
529 LogMenuAction(MENU_ACTION_WIN_CHROMEOS_RESTART
);
531 case IDC_DISTILL_PAGE
:
532 if (!uma_action_recorded_
) {
533 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.DistillPage",
536 LogMenuAction(MENU_ACTION_DISTILL_PAGE
);
539 if (!uma_action_recorded_
)
540 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.SavePage", delta
);
541 LogMenuAction(MENU_ACTION_SAVE_PAGE
);
544 if (!uma_action_recorded_
)
545 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Find", delta
);
546 LogMenuAction(MENU_ACTION_FIND
);
549 if (!uma_action_recorded_
)
550 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Print", delta
);
551 LogMenuAction(MENU_ACTION_PRINT
);
556 if (!uma_action_recorded_
)
557 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Cut", delta
);
558 LogMenuAction(MENU_ACTION_CUT
);
561 if (!uma_action_recorded_
)
562 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Copy", delta
);
563 LogMenuAction(MENU_ACTION_COPY
);
566 if (!uma_action_recorded_
)
567 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Paste", delta
);
568 LogMenuAction(MENU_ACTION_PASTE
);
572 case IDC_CREATE_HOSTED_APP
:
573 if (!uma_action_recorded_
) {
574 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.CreateHostedApp",
577 LogMenuAction(MENU_ACTION_CREATE_HOSTED_APP
);
579 case IDC_CREATE_SHORTCUTS
:
580 if (!uma_action_recorded_
)
581 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.CreateShortcuts",
583 LogMenuAction(MENU_ACTION_CREATE_SHORTCUTS
);
585 case IDC_MANAGE_EXTENSIONS
:
586 if (!uma_action_recorded_
) {
587 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ManageExtensions",
590 LogMenuAction(MENU_ACTION_MANAGE_EXTENSIONS
);
592 case IDC_TASK_MANAGER
:
593 if (!uma_action_recorded_
) {
594 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.TaskManager",
597 LogMenuAction(MENU_ACTION_TASK_MANAGER
);
599 case IDC_CLEAR_BROWSING_DATA
:
600 if (!uma_action_recorded_
) {
601 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ClearBrowsingData",
604 LogMenuAction(MENU_ACTION_CLEAR_BROWSING_DATA
);
606 case IDC_VIEW_SOURCE
:
607 if (!uma_action_recorded_
)
608 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ViewSource", delta
);
609 LogMenuAction(MENU_ACTION_VIEW_SOURCE
);
612 if (!uma_action_recorded_
)
613 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.DevTools", delta
);
614 LogMenuAction(MENU_ACTION_DEV_TOOLS
);
616 case IDC_DEV_TOOLS_CONSOLE
:
617 if (!uma_action_recorded_
) {
618 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.DevToolsConsole",
621 LogMenuAction(MENU_ACTION_DEV_TOOLS_CONSOLE
);
623 case IDC_DEV_TOOLS_DEVICES
:
624 if (!uma_action_recorded_
) {
625 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.DevToolsDevices",
628 LogMenuAction(MENU_ACTION_DEV_TOOLS_DEVICES
);
630 case IDC_PROFILING_ENABLED
:
631 if (!uma_action_recorded_
) {
632 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ProfilingEnabled",
635 LogMenuAction(MENU_ACTION_PROFILING_ENABLED
);
640 if (!uma_action_recorded_
) {
641 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ZoomMinus", delta
);
642 LogMenuAction(MENU_ACTION_ZOOM_MINUS
);
646 if (!uma_action_recorded_
) {
647 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ZoomPlus", delta
);
648 LogMenuAction(MENU_ACTION_ZOOM_PLUS
);
652 content::RecordAction(UserMetricsAction("EnterFullScreenWithWrenchMenu"));
654 if (!uma_action_recorded_
) {
655 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.EnterFullScreen",
658 LogMenuAction(MENU_ACTION_FULLSCREEN
);
661 case IDC_SHOW_HISTORY
:
662 if (!uma_action_recorded_
) {
663 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowHistory",
666 LogMenuAction(MENU_ACTION_SHOW_HISTORY
);
668 case IDC_SHOW_DOWNLOADS
:
669 if (!uma_action_recorded_
) {
670 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowDownloads",
673 LogMenuAction(MENU_ACTION_SHOW_DOWNLOADS
);
675 case IDC_SHOW_SYNC_SETUP
:
676 if (!uma_action_recorded_
) {
677 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowSyncSetup",
680 LogMenuAction(MENU_ACTION_SHOW_SYNC_SETUP
);
683 if (!uma_action_recorded_
)
684 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Settings", delta
);
685 LogMenuAction(MENU_ACTION_OPTIONS
);
688 if (!uma_action_recorded_
)
689 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.About", delta
);
690 LogMenuAction(MENU_ACTION_ABOUT
);
694 case IDC_HELP_PAGE_VIA_MENU
:
695 content::RecordAction(UserMetricsAction("ShowHelpTabViaWrenchMenu"));
697 if (!uma_action_recorded_
)
698 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.HelpPage", delta
);
699 LogMenuAction(MENU_ACTION_HELP_PAGE_VIA_MENU
);
701 #if defined(GOOGLE_CHROME_BUILD)
703 if (!uma_action_recorded_
)
704 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Feedback", delta
);
705 LogMenuAction(MENU_ACTION_FEEDBACK
);
709 case IDC_TOGGLE_REQUEST_TABLET_SITE
:
710 if (!uma_action_recorded_
) {
711 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.RequestTabletSite",
714 LogMenuAction(MENU_ACTION_TOGGLE_REQUEST_TABLET_SITE
);
717 if (!uma_action_recorded_
)
718 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Exit", delta
);
719 LogMenuAction(MENU_ACTION_EXIT
);
723 if (!uma_action_recorded_
) {
724 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction", delta
);
725 uma_action_recorded_
= true;
729 void WrenchMenuModel::LogMenuAction(int action_id
) {
730 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.MenuAction", action_id
,
734 bool WrenchMenuModel::IsCommandIdChecked(int command_id
) const {
735 if (command_id
== IDC_SHOW_BOOKMARK_BAR
) {
736 return browser_
->profile()->GetPrefs()->GetBoolean(
737 bookmarks::prefs::kShowBookmarkBar
);
738 } else if (command_id
== IDC_PROFILING_ENABLED
) {
739 return Profiling::BeingProfiled();
740 } else if (command_id
== IDC_TOGGLE_REQUEST_TABLET_SITE
) {
741 return chrome::IsRequestingTabletSite(browser_
);
747 bool WrenchMenuModel::IsCommandIdEnabled(int command_id
) const {
748 GlobalError
* error
= GlobalErrorServiceFactory::GetForProfile(
749 browser_
->profile())->GetGlobalErrorByMenuItemCommandID(command_id
);
753 return chrome::IsCommandEnabled(browser_
, command_id
);
756 bool WrenchMenuModel::IsCommandIdVisible(int command_id
) const {
757 switch (command_id
) {
758 #if defined(OS_MACOSX)
759 case kEmptyMenuItemCommand
:
760 return false; // Always hidden (see CreateActionToolbarOverflowMenu).
763 case IDC_VIEW_INCOMPATIBILITIES
: {
764 EnumerateModulesModel
* loaded_modules
=
765 EnumerateModulesModel::GetInstance();
766 if (loaded_modules
->confirmed_bad_modules_detected() <= 0)
768 // We'll leave the wrench adornment on until the user clicks the link.
769 if (loaded_modules
->modules_to_notify_about() <= 0)
770 loaded_modules
->AcknowledgeConflictNotification();
773 case IDC_PIN_TO_START_SCREEN
:
774 return base::win::IsMetroProcess();
776 case IDC_VIEW_INCOMPATIBILITIES
:
777 case IDC_PIN_TO_START_SCREEN
:
780 case IDC_UPGRADE_DIALOG
:
781 return browser_defaults::kShowUpgradeMenuItem
&&
782 UpgradeDetector::GetInstance()->notify_upgrade();
783 #if !defined(OS_LINUX) || defined(USE_AURA)
784 case IDC_BOOKMARK_PAGE
:
785 return !chrome::ShouldRemoveBookmarkThisPageUI(browser_
->profile());
786 case IDC_BOOKMARK_ALL_TABS
:
787 return !chrome::ShouldRemoveBookmarkOpenPagesUI(browser_
->profile());
794 bool WrenchMenuModel::GetAcceleratorForCommandId(
796 ui::Accelerator
* accelerator
) {
797 return provider_
->GetAcceleratorForCommandId(command_id
, accelerator
);
800 void WrenchMenuModel::ActiveTabChanged(WebContents
* old_contents
,
801 WebContents
* new_contents
,
804 // The user has switched between tabs and the new tab may have a different
806 UpdateZoomControls();
809 void WrenchMenuModel::TabReplacedAt(TabStripModel
* tab_strip_model
,
810 WebContents
* old_contents
,
811 WebContents
* new_contents
,
813 UpdateZoomControls();
816 void WrenchMenuModel::TabStripModelDeleted() {
817 // During views shutdown, the tabstrip model/browser is deleted first, while
818 // it is the opposite in gtk land.
819 tab_strip_model_
->RemoveObserver(this);
820 tab_strip_model_
= NULL
;
823 void WrenchMenuModel::Observe(int type
,
824 const content::NotificationSource
& source
,
825 const content::NotificationDetails
& details
) {
826 DCHECK(type
== content::NOTIFICATION_NAV_ENTRY_COMMITTED
);
827 UpdateZoomControls();
831 WrenchMenuModel::WrenchMenuModel()
832 : ui::SimpleMenuModel(this),
833 uma_action_recorded_(false),
836 tab_strip_model_(NULL
) {}
838 bool WrenchMenuModel::ShouldShowNewIncognitoWindowMenuItem() {
839 if (browser_
->profile()->IsGuestSession())
842 return IncognitoModePrefs::GetAvailability(browser_
->profile()->GetPrefs()) !=
843 IncognitoModePrefs::DISABLED
;
846 // Note: When adding new menu items please place under an appropriate section.
847 // Menu is organised as follows:
848 // - Extension toolbar overflow.
849 // - Global browser errors and warnings.
850 // - Tabs and windows.
851 // - Places previously been e.g. History, bookmarks, recent tabs.
852 // - Page actions e.g. zoom, edit, find, print.
853 // - Learn about the browser and global customisation e.g. settings, help.
854 // - Browser relaunch, quit.
855 void WrenchMenuModel::Build() {
856 if (extensions::FeatureSwitch::extension_action_redesign()->IsEnabled())
857 CreateActionToolbarOverflowMenu();
859 AddItem(IDC_VIEW_INCOMPATIBILITIES
,
860 l10n_util::GetStringUTF16(IDS_VIEW_INCOMPATIBILITIES
));
861 SetIcon(GetIndexOfCommandId(IDC_VIEW_INCOMPATIBILITIES
),
862 ui::ResourceBundle::GetSharedInstance().
863 GetNativeImageNamed(IDR_INPUT_ALERT_MENU
));
864 if (IsCommandIdVisible(IDC_UPGRADE_DIALOG
))
865 AddItem(IDC_UPGRADE_DIALOG
, GetUpgradeDialogMenuItemName());
866 if (AddGlobalErrorMenuItems() ||
867 IsCommandIdVisible(IDC_VIEW_INCOMPATIBILITIES
) ||
868 IsCommandIdVisible(IDC_UPGRADE_DIALOG
))
869 AddSeparator(ui::NORMAL_SEPARATOR
);
871 AddItemWithStringId(IDC_NEW_TAB
, IDS_NEW_TAB
);
872 AddItemWithStringId(IDC_NEW_WINDOW
, IDS_NEW_WINDOW
);
873 if (ShouldShowNewIncognitoWindowMenuItem())
874 AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW
, IDS_NEW_INCOGNITO_WINDOW
);
875 AddSeparator(ui::NORMAL_SEPARATOR
);
877 if (!browser_
->profile()->IsOffTheRecord()) {
878 recent_tabs_sub_menu_model_
.reset(new RecentTabsSubMenuModel(provider_
,
881 AddSubMenuWithStringId(IDC_RECENT_TABS_MENU
, IDS_HISTORY_MENU
,
882 recent_tabs_sub_menu_model_
.get());
884 AddItemWithStringId(IDC_SHOW_DOWNLOADS
, IDS_SHOW_DOWNLOADS
);
885 if (!browser_
->profile()->IsGuestSession()) {
886 bookmark_sub_menu_model_
.reset(new BookmarkSubMenuModel(this, browser_
));
887 AddSubMenuWithStringId(IDC_BOOKMARKS_MENU
, IDS_BOOKMARKS_MENU
,
888 bookmark_sub_menu_model_
.get());
892 AddItemWithStringId(IDC_PRINT
, IDS_PRINT
);
893 AddItemWithStringId(IDC_FIND
, IDS_FIND
);
894 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
895 switches::kEnableDomDistiller
))
896 AddItemWithStringId(IDC_DISTILL_PAGE
, IDS_DISTILL_PAGE
);
897 tools_menu_model_
.reset(new ToolsMenuModel(this, browser_
));
898 AddSubMenuWithStringId(
899 IDC_MORE_TOOLS_MENU
, IDS_MORE_TOOLS_MENU
, tools_menu_model_
.get());
900 // Append the full menu including separators. The final separator only gets
901 // appended when this is a touch menu - otherwise it would get added twice.
902 CreateCutCopyPasteMenu();
904 AddItemWithStringId(IDC_OPTIONS
, IDS_SETTINGS
);
905 // The help submenu is only displayed on official Chrome builds. As the
906 // 'About' item has been moved to this submenu, it's reinstated here for
908 #if defined(GOOGLE_CHROME_BUILD)
909 help_menu_model_
.reset(new HelpMenuModel(this, browser_
));
910 AddSubMenuWithStringId(IDC_HELP_MENU
, IDS_HELP_MENU
,
911 help_menu_model_
.get());
913 AddItem(IDC_ABOUT
, l10n_util::GetStringUTF16(IDS_ABOUT
));
915 #if defined(OS_CHROMEOS)
916 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
917 chromeos::switches::kEnableRequestTabletSite
))
918 AddCheckItemWithStringId(IDC_TOGGLE_REQUEST_TABLET_SITE
,
919 IDS_TOGGLE_REQUEST_TABLET_SITE
);
923 int command_id
= IDC_WIN_DESKTOP_RESTART
;
924 int string_id
= IDS_WIN_DESKTOP_RESTART
;
925 if (GetRestartMenuItemIfRequired(browser_
->host_desktop_type(),
928 AddSeparator(ui::NORMAL_SEPARATOR
);
929 AddItemWithStringId(command_id
, string_id
);
932 bool show_exit_menu
= browser_defaults::kShowExitMenuItem
;
934 if (browser_
->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH
)
935 show_exit_menu
= false;
937 if (show_exit_menu
) {
938 AddSeparator(ui::NORMAL_SEPARATOR
);
939 AddItemWithStringId(IDC_EXIT
, IDS_EXIT
);
941 uma_action_recorded_
= false;
944 bool WrenchMenuModel::AddGlobalErrorMenuItems() {
945 // TODO(sail): Currently we only build the wrench menu once per browser
946 // window. This means that if a new error is added after the menu is built
947 // it won't show in the existing wrench menu. To fix this we need to some
948 // how update the menu if new errors are added.
949 const GlobalErrorService::GlobalErrorList
& errors
=
950 GlobalErrorServiceFactory::GetForProfile(browser_
->profile())->errors();
951 bool menu_items_added
= false;
952 for (GlobalErrorService::GlobalErrorList::const_iterator
953 it
= errors
.begin(); it
!= errors
.end(); ++it
) {
954 GlobalError
* error
= *it
;
956 if (error
->HasMenuItem()) {
957 AddItem(error
->MenuItemCommandID(), error
->MenuItemLabel());
958 SetIcon(GetIndexOfCommandId(error
->MenuItemCommandID()),
959 error
->MenuItemIcon());
960 menu_items_added
= true;
963 return menu_items_added
;
966 void WrenchMenuModel::CreateActionToolbarOverflowMenu() {
967 // We only add the extensions overflow container if there are any icons that
968 // aren't shown in the main container.
969 if (!ToolbarActionsModel::Get(browser_
->profile())->all_icons_visible()) {
970 #if defined(OS_MACOSX)
971 // There's a bug in AppKit menus, where if a menu item with a custom view
972 // (like the extensions overflow menu) is the first menu item, it is not
973 // highlightable or keyboard-selectable.
974 // Adding any menu item before it (even one which is never visible) prevents
975 // it, so add a bogus item here that will always be hidden.
976 AddItem(kEmptyMenuItemCommand
, base::string16());
978 AddItem(IDC_EXTENSIONS_OVERFLOW_MENU
, base::string16());
979 AddSeparator(ui::UPPER_SEPARATOR
);
983 void WrenchMenuModel::CreateCutCopyPasteMenu() {
984 AddSeparator(ui::LOWER_SEPARATOR
);
986 // WARNING: Mac does not use the ButtonMenuItemModel, but instead defines the
987 // layout for this menu item in WrenchMenu.xib. It does, however, use the
988 // command_id value from AddButtonItem() to identify this special item.
989 edit_menu_item_model_
.reset(new ui::ButtonMenuItemModel(IDS_EDIT
, this));
990 edit_menu_item_model_
->AddGroupItemWithStringId(IDC_CUT
, IDS_CUT
);
991 edit_menu_item_model_
->AddGroupItemWithStringId(IDC_COPY
, IDS_COPY
);
992 edit_menu_item_model_
->AddGroupItemWithStringId(IDC_PASTE
, IDS_PASTE
);
993 AddButtonItem(IDC_EDIT_MENU
, edit_menu_item_model_
.get());
995 AddSeparator(ui::UPPER_SEPARATOR
);
998 void WrenchMenuModel::CreateZoomMenu() {
999 // This menu needs to be enclosed by separators.
1000 AddSeparator(ui::LOWER_SEPARATOR
);
1002 // WARNING: Mac does not use the ButtonMenuItemModel, but instead defines the
1003 // layout for this menu item in WrenchMenu.xib. It does, however, use the
1004 // command_id value from AddButtonItem() to identify this special item.
1005 zoom_menu_item_model_
.reset(
1006 new ui::ButtonMenuItemModel(IDS_ZOOM_MENU
, this));
1007 zoom_menu_item_model_
->AddGroupItemWithStringId(IDC_ZOOM_MINUS
,
1009 zoom_menu_item_model_
->AddGroupItemWithStringId(IDC_ZOOM_PLUS
,
1011 zoom_menu_item_model_
->AddItemWithImage(IDC_FULLSCREEN
,
1012 IDR_FULLSCREEN_MENU_BUTTON
);
1013 AddButtonItem(IDC_ZOOM_MENU
, zoom_menu_item_model_
.get());
1015 AddSeparator(ui::UPPER_SEPARATOR
);
1018 void WrenchMenuModel::UpdateZoomControls() {
1019 int zoom_percent
= 100;
1020 if (browser_
->tab_strip_model() &&
1021 browser_
->tab_strip_model()->GetActiveWebContents()) {
1022 zoom_percent
= ui_zoom::ZoomController::FromWebContents(
1023 browser_
->tab_strip_model()->GetActiveWebContents())
1026 zoom_label_
= l10n_util::GetStringFUTF16(
1027 IDS_ZOOM_PERCENT
, base::IntToString16(zoom_percent
));
1030 void WrenchMenuModel::OnZoomLevelChanged(
1031 const content::HostZoomMap::ZoomLevelChange
& change
) {
1032 UpdateZoomControls();