blob: e997bec16c65008e36fd7f7258d89c967bc30532 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/toolbar/app_menu_model.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include "base/command_line.h"
#include "base/debug/debugging_buildflags.h"
#include "base/debug/profiler.h"
#include "base/functional/bind.h"
#include "base/i18n/number_formatting.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/ui/bookmarks/bookmark_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/commander/commander.h"
#include "chrome/browser/ui/global_error/global_error.h"
#include "chrome/browser/ui/global_error/global_error_service.h"
#include "chrome/browser/ui/global_error/global_error_service_factory.h"
#include "chrome/browser/ui/managed_ui.h"
#include "chrome/browser/ui/side_panel/companion/companion_utils.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/app_menu_icon_controller.h"
#include "chrome/browser/ui/toolbar/bookmark_sub_menu_model.h"
#include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
#include "chrome/browser/upgrade_detector/upgrade_detector.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/theme_resources.h"
#include "components/bookmarks/common/bookmark_pref_names.h"
#include "components/dom_distiller/content/browser/distillable_page_utils.h"
#include "components/dom_distiller/content/browser/uma_helper.h"
#include "components/dom_distiller/core/dom_distiller_features.h"
#include "components/dom_distiller/core/url_utils.h"
#include "components/feature_engagement/public/event_constants.h"
#include "components/password_manager/content/common/web_ui_constants.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/performance_manager/public/features.h"
#include "components/prefs/pref_service.h"
#include "components/profile_metrics/browser_profile_type.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "components/webapps/browser/banners/app_banner_manager.h"
#include "components/zoom/zoom_controller.h"
#include "components/zoom/zoom_event_manager.h"
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/profiling.h"
#include "content/public/common/url_constants.h"
#include "media/base/media_switches.h"
#include "ui/base/accelerators/menu_label_accelerator_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/layout.h"
#include "ui/base/models/button_menu_item_model.h"
#include "ui/base/models/image_model.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_features.h"
#include "ui/color/color_id.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/text_elider.h"
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) || BUILDFLAG(IS_CHROMEOS_ASH)
#include "base/feature_list.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/policy/system_features_disable_list_policy_handler.h"
#include "chromeos/ui/base/tablet_state.h"
#include "components/policy/core/common/policy_pref_names.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "base/win/shortcut.h"
#include "base/win/windows_version.h"
#include "content/public/browser/gpu_data_manager.h"
#endif
using base::UserMetricsAction;
using content::WebContents;
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kBookmarksMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kDownloadsMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kHistoryMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kExtensionsMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kMoreToolsMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kIncognitoMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kPasswordManagerMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ToolsMenuModel, kPerformanceMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ExtensionsMenuModel,
kManageExtensionsMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ExtensionsMenuModel,
kVisitChromeWebStoreMenuItem);
namespace {
constexpr size_t kMaxAppNameLength = 30;
// Conditionally return the update app menu item title based on upgrade detector
// state.
std::u16string GetUpgradeDialogMenuItemName() {
if (UpgradeDetector::GetInstance()->is_outdated_install() ||
UpgradeDetector::GetInstance()->is_outdated_install_no_au()) {
return l10n_util::GetStringUTF16(IDS_UPGRADE_BUBBLE_MENU_ITEM);
} else {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && \
(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX))
return l10n_util::GetStringUTF16(
base::FeatureList::IsEnabled(features::kUpdateTextOptions)
? IDS_RELAUNCH_TO_UPDATE_ALT
: IDS_RELAUNCH_TO_UPDATE);
#else
return l10n_util::GetStringUTF16(IDS_RELAUNCH_TO_UPDATE);
#endif
}
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Returns the menu item name for Lacros data migration.
std::u16string GetLacrosDataMigrationMenuItemName() {
return l10n_util::GetStringUTF16(IDS_LACROS_DATA_MIGRATION_RELAUNCH);
}
#endif
// Returns the appropriate menu label for the IDC_INSTALL_PWA command if
// available.
absl::optional<std::u16string> GetInstallPWAAppMenuItemName(Browser* browser) {
WebContents* web_contents =
browser->tab_strip_model()->GetActiveWebContents();
if (!web_contents)
return absl::nullopt;
std::u16string app_name =
webapps::AppBannerManager::GetInstallableWebAppName(web_contents);
if (app_name.empty())
return absl::nullopt;
return l10n_util::GetStringFUTF16(IDS_INSTALL_TO_OS_LAUNCH_SURFACE,
ui::EscapeMenuLabelAmpersands(app_name));
}
bool IsPasswordManagerPage(const GURL& url) {
return url.SchemeIs(content::kChromeUIScheme) &&
url.DomainIs(password_manager::kChromeUIPasswordManagerHost);
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// LogWrenchMenuAction
void LogWrenchMenuAction(AppMenuAction action_id) {
UMA_HISTOGRAM_ENUMERATION("WrenchMenu.MenuAction", action_id,
LIMIT_MENU_ACTION);
}
////////////////////////////////////////////////////////////////////////////////
// ZoomMenuModel
ZoomMenuModel::ZoomMenuModel(ui::SimpleMenuModel::Delegate* delegate)
: SimpleMenuModel(delegate) {
Build();
}
ZoomMenuModel::~ZoomMenuModel() = default;
void ZoomMenuModel::Build() {
AddItemWithStringId(IDC_ZOOM_PLUS, IDS_ZOOM_PLUS);
AddItemWithStringId(IDC_ZOOM_NORMAL, IDS_ZOOM_NORMAL);
AddItemWithStringId(IDC_ZOOM_MINUS, IDS_ZOOM_MINUS);
}
////////////////////////////////////////////////////////////////////////////////
// HelpMenuModel
// Only used in branded builds.
class HelpMenuModel : public ui::SimpleMenuModel {
public:
HelpMenuModel(ui::SimpleMenuModel::Delegate* delegate, Browser* browser)
: SimpleMenuModel(delegate) {
Build(browser);
}
HelpMenuModel(const HelpMenuModel&) = delete;
HelpMenuModel& operator=(const HelpMenuModel&) = delete;
private:
void Build(Browser* browser) {
#if BUILDFLAG(IS_CHROMEOS_ASH) && defined(OFFICIAL_BUILD)
int help_string_id = IDS_GET_HELP;
#else
int help_string_id = IDS_HELP_PAGE;
#endif
AddItemWithStringId(IDC_ABOUT, IDS_ABOUT);
if (features::IsChromeRefresh2023()) {
SetIcon(
GetIndexOfCommandId(IDC_ABOUT).value(),
ui::ImageModel::FromVectorIcon(vector_icons::kInfoRefreshIcon,
ui::kColorMenuIcon, kDefaultIconSize));
}
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
if (base::FeatureList::IsEnabled(features::kChromeWhatsNewUI)) {
AddItemWithStringId(IDC_CHROME_WHATS_NEW, IDS_CHROME_WHATS_NEW);
if (features::IsChromeRefresh2023()) {
SetIcon(GetIndexOfCommandId(IDC_CHROME_WHATS_NEW).value(),
ui::ImageModel::FromVectorIcon(
kReleaseAlertIcon, ui::kColorMenuIcon, kDefaultIconSize));
}
}
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
AddItemWithStringId(IDC_HELP_PAGE_VIA_MENU, help_string_id);
if (browser_defaults::kShowHelpMenuItemIcon) {
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
SetIcon(GetIndexOfCommandId(IDC_HELP_PAGE_VIA_MENU).value(),
ui::ImageModel::FromImage(rb.GetNativeImageNamed(IDR_HELP_MENU)));
} else if (features::IsChromeRefresh2023()) {
SetIcon(GetIndexOfCommandId(IDC_HELP_PAGE_VIA_MENU).value(),
ui::ImageModel::FromVectorIcon(kHelpMenuIcon, ui::kColorMenuIcon,
kDefaultIconSize));
}
if (browser->profile()->GetPrefs()->GetBoolean(
prefs::kUserFeedbackAllowed)) {
AddItemWithStringId(IDC_FEEDBACK, IDS_FEEDBACK);
if (features::IsChromeRefresh2023()) {
SetIcon(GetIndexOfCommandId(IDC_FEEDBACK).value(),
ui::ImageModel::FromVectorIcon(kReportIcon, ui::kColorMenuIcon,
kDefaultIconSize));
}
}
}
};
////////////////////////////////////////////////////////////////////////////////
// ToolsMenuModel
ToolsMenuModel::ToolsMenuModel(ui::SimpleMenuModel::Delegate* delegate,
Browser* browser)
: SimpleMenuModel(delegate) {
Build(browser);
}
ToolsMenuModel::~ToolsMenuModel() = default;
// More tools submenu is constructed as follows:
// - Page specific actions overflow (save page, adding to desktop).
// - Browser / OS level tools (extensions, task manager).
// - Developer tools.
// - Option to enable profiling.
void ToolsMenuModel::Build(Browser* browser) {
AddItemWithStringId(IDC_SAVE_PAGE, IDS_SAVE_PAGE);
AddItemWithStringId(IDC_CREATE_SHORTCUT, IDS_ADD_TO_OS_LAUNCH_SURFACE);
AddItemWithStringId(IDC_NAME_WINDOW, IDS_NAME_WINDOW);
if (commander::IsEnabled())
AddItemWithStringId(IDC_TOGGLE_QUICK_COMMANDS, IDS_TOGGLE_QUICK_COMMANDS);
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringId(IDC_CLEAR_BROWSING_DATA, IDS_CLEAR_BROWSING_DATA);
if (!base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu) &&
!features::IsChromeRefresh2023()) {
AddItemWithStringId(IDC_MANAGE_EXTENSIONS, IDS_SHOW_EXTENSIONS);
}
AddItemWithStringId(IDC_PERFORMANCE, IDS_SHOW_PERFORMANCE);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_PERFORMANCE).value(),
kPerformanceMenuItem);
if (chrome::CanOpenTaskManager())
AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER);
#if BUILDFLAG(IS_CHROMEOS_ASH)
AddItemWithStringId(IDC_TAKE_SCREENSHOT, IDS_TAKE_SCREENSHOT);
#endif
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringId(IDC_DEV_TOOLS, IDS_DEV_TOOLS);
if (base::debug::IsProfilingSupported()) {
AddSeparator(ui::NORMAL_SEPARATOR);
AddCheckItemWithStringId(IDC_PROFILING_ENABLED, IDS_PROFILING_ENABLED);
}
}
////////////////////////////////////////////////////////////////////////////////
// ExtensionsMenuModel
ExtensionsMenuModel::ExtensionsMenuModel(
ui::SimpleMenuModel::Delegate* delegate,
Browser* browser)
: SimpleMenuModel(delegate) {
Build(browser);
}
ExtensionsMenuModel::~ExtensionsMenuModel() = default;
// Extensions (sub)menu is constructed as follows:
// - An overflow with two items:
// - An item to manage extensions at chrome://extensions
// - An item to visit the Chrome Web Store
void ExtensionsMenuModel::Build(Browser* browser) {
AddItemWithStringId(IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS,
IDS_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS_ITEM);
SetElementIdentifierAt(
GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS).value(),
kManageExtensionsMenuItem);
AddItemWithStringId(IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE,
IDS_EXTENSIONS_SUBMENU_CHROME_WEBSTORE_ITEM);
SetElementIdentifierAt(
GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE)
.value(),
kVisitChromeWebStoreMenuItem);
if (features::IsChromeRefresh2023()) {
SetIcon(
GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS).value(),
ui::ImageModel::FromVectorIcon(
vector_icons::kExtensionChromeRefreshIcon, ui::kColorMenuIcon,
kDefaultIconSize));
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
SetIcon(
GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE)
.value(),
ui::ImageModel::FromVectorIcon(vector_icons::kGoogleChromeWebstoreIcon,
ui::kColorMenuIcon, kDefaultIconSize));
#endif
}
}
////////////////////////////////////////////////////////////////////////////////
// AutofillSubMenuModel
AutofillSubMenuModel::AutofillSubMenuModel(
ui::SimpleMenuModel::Delegate* delegate)
: SimpleMenuModel(delegate) {
AddItemWithStringIdAndIcon(
IDC_SHOW_PASSWORD_MANAGER, IDS_PASSWORD_MANAGER_SUBMENU_OPTION,
ui::ImageModel::FromVectorIcon(kKeyChromeRefreshIcon, ui::kColorMenuIcon,
kDefaultIconSize));
AddItemWithStringIdAndIcon(
IDC_SHOW_PAYMENT_METHODS, IDS_PAYMENT_METHOD_SUBMENU_OPTION,
ui::ImageModel::FromVectorIcon(kCreditCardChromeRefreshIcon,
ui::kColorMenuIcon, kDefaultIconSize));
AddItemWithStringIdAndIcon(
IDC_SHOW_ADDRESSES, IDS_ADDRESSES_AND_MORE_SUBMENU_OPTION,
ui::ImageModel::FromVectorIcon(kLocationOnChromeRefreshIcon,
ui::kColorMenuIcon, kDefaultIconSize));
}
AutofillSubMenuModel::~AutofillSubMenuModel() = default;
////////////////////////////////////////////////////////////////////////////////
// FindAndEditSubMenuModel
FindAndEditSubMenuModel::FindAndEditSubMenuModel(
ui::SimpleMenuModel::Delegate* delegate)
: SimpleMenuModel(delegate) {
AddItemWithStringIdAndIcon(
IDC_FIND, IDS_FIND,
ui::ImageModel::FromVectorIcon(kSearchMenuIcon, ui::kColorMenuIcon,
kDefaultIconSize));
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringIdAndIcon(
IDC_CUT, IDS_CUT,
ui::ImageModel::FromVectorIcon(kCutMenuIcon, ui::kColorMenuIcon,
kDefaultIconSize));
AddItemWithStringIdAndIcon(
IDC_COPY, IDS_COPY,
ui::ImageModel::FromVectorIcon(kCopyMenuIcon, ui::kColorMenuIcon,
kDefaultIconSize));
AddItemWithStringIdAndIcon(
IDC_PASTE, IDS_PASTE,
ui::ImageModel::FromVectorIcon(kPasteMenuIcon, ui::kColorMenuIcon,
kDefaultIconSize));
}
FindAndEditSubMenuModel::~FindAndEditSubMenuModel() = default;
////////////////////////////////////////////////////////////////////////////////
// AppMenuModel
// static
const int AppMenuModel::kMinRecentTabsCommandId;
const int AppMenuModel::kNumUnboundedMenuTypes;
AppMenuModel::AppMenuModel(ui::AcceleratorProvider* provider,
Browser* browser,
AppMenuIconController* app_menu_icon_controller,
AlertMenuItem alert_item)
: ui::SimpleMenuModel(this),
uma_action_recorded_(false),
provider_(provider),
browser_(browser),
app_menu_icon_controller_(app_menu_icon_controller),
alert_item_(alert_item) {
DCHECK(browser_);
}
AppMenuModel::~AppMenuModel() {
browser_->tab_strip_model()->RemoveObserver(this);
}
void AppMenuModel::Init() {
Build();
browser_zoom_subscription_ =
zoom::ZoomEventManager::GetForBrowserContext(browser_->profile())
->AddZoomLevelChangedCallback(base::BindRepeating(
&AppMenuModel::OnZoomLevelChanged, base::Unretained(this)));
TabStripModel* tab_strip_model = browser_->tab_strip_model();
tab_strip_model->AddObserver(this);
Observe(tab_strip_model->GetActiveWebContents());
UpdateZoomControls();
#if BUILDFLAG(IS_CHROMEOS)
PrefService* const local_state = g_browser_process->local_state();
if (local_state) {
local_state_pref_change_registrar_.Init(local_state);
local_state_pref_change_registrar_.Add(
policy::policy_prefs::kSystemFeaturesDisableList,
base::BindRepeating(&AppMenuModel::UpdateSettingsItemState,
base::Unretained(this)));
UpdateSettingsItemState();
}
#endif // BUILDFLAG(IS_CHROMEOS)
}
bool AppMenuModel::DoesCommandIdDismissMenu(int command_id) const {
return command_id != IDC_ZOOM_MINUS && command_id != IDC_ZOOM_PLUS;
}
bool AppMenuModel::IsItemForCommandIdDynamic(int command_id) const {
return command_id == IDC_ZOOM_PERCENT_DISPLAY ||
#if BUILDFLAG(IS_MAC)
command_id == IDC_FULLSCREEN ||
#elif BUILDFLAG(IS_WIN)
command_id == IDC_PIN_TO_START_SCREEN ||
#elif BUILDFLAG(IS_CHROMEOS_ASH)
command_id == IDC_LACROS_DATA_MIGRATION ||
#endif
(command_id == IDC_INSTALL_PWA && !features::IsChromeRefresh2023()) ||
command_id == IDC_UPGRADE_DIALOG;
}
std::u16string AppMenuModel::GetLabelForCommandId(int command_id) const {
switch (command_id) {
case IDC_ZOOM_PERCENT_DISPLAY:
return zoom_label_;
#if BUILDFLAG(IS_MAC)
case IDC_FULLSCREEN: {
int string_id = IDS_ENTER_FULLSCREEN_MAC; // Default to Enter.
// Note: On startup, |window()| may be NULL.
if (browser_->window() && browser_->window()->IsFullscreen())
string_id = IDS_EXIT_FULLSCREEN_MAC;
return l10n_util::GetStringUTF16(string_id);
}
#elif BUILDFLAG(IS_WIN)
case IDC_PIN_TO_START_SCREEN: {
int string_id = IDS_PIN_TO_START_SCREEN;
// TODO(scottmg): Remove http://crbug.com/558054.
return l10n_util::GetStringUTF16(string_id);
}
#elif BUILDFLAG(IS_CHROMEOS_ASH)
case IDC_LACROS_DATA_MIGRATION:
return GetLacrosDataMigrationMenuItemName();
#endif
case IDC_INSTALL_PWA:
return GetInstallPWAAppMenuItemName(browser_).value();
case IDC_UPGRADE_DIALOG:
DCHECK(browser_defaults::kShowUpgradeMenuItem);
return GetUpgradeDialogMenuItemName();
default:
NOTREACHED();
return std::u16string();
}
}
ui::ImageModel AppMenuModel::GetIconForCommandId(int command_id) const {
bool upgrade_icon_requested = false;
if (command_id == IDC_UPGRADE_DIALOG) {
DCHECK(browser_defaults::kShowUpgradeMenuItem);
upgrade_icon_requested = true;
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (command_id == IDC_LACROS_DATA_MIGRATION)
upgrade_icon_requested = true;
#endif
if (upgrade_icon_requested) {
DCHECK(app_menu_icon_controller_);
return ui::ImageModel::FromVectorIcon(
kBrowserToolsUpdateIcon,
app_menu_icon_controller_->GetIconColor(absl::nullopt));
}
return ui::ImageModel();
}
void AppMenuModel::ExecuteCommand(int command_id, int event_flags) {
GlobalError* error =
GlobalErrorServiceFactory::GetForProfile(browser_->profile())
->GetGlobalErrorByMenuItemCommandID(command_id);
if (error) {
error->ExecuteMenuItem(browser_);
return;
}
if (command_id == IDC_PERFORMANCE) {
browser()->window()->NotifyFeatureEngagementEvent(
feature_engagement::events::kPerformanceMenuItemActivated);
}
if (command_id == IDC_VIEW_PASSWORDS) {
browser()->profile()->GetPrefs()->SetBoolean(
password_manager::prefs::kPasswordsPrefWithNewLabelUsed, true);
}
LogMenuMetrics(command_id);
chrome::ExecuteCommand(browser_, command_id);
}
void AppMenuModel::LogMenuMetrics(int command_id) {
base::TimeDelta delta = timer_.Elapsed();
switch (command_id) {
case IDC_UPGRADE_DIALOG:
LogMenuAction(MENU_ACTION_UPGRADE_DIALOG);
break;
#if BUILDFLAG(IS_CHROMEOS_ASH)
case IDC_LACROS_DATA_MIGRATION:
LogMenuAction(MENU_ACTION_LACROS_DATA_MIGRATION);
break;
#endif
case IDC_NEW_TAB:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.NewTab", delta);
LogMenuAction(MENU_ACTION_NEW_TAB);
break;
case IDC_NEW_WINDOW:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.NewWindow", delta);
LogMenuAction(MENU_ACTION_NEW_WINDOW);
break;
case IDC_NEW_INCOGNITO_WINDOW:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.NewIncognitoWindow",
delta);
}
LogMenuAction(MENU_ACTION_NEW_INCOGNITO_WINDOW);
break;
// Bookmarks sub menu.
case IDC_SHOW_BOOKMARK_BAR:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowBookmarkBar",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_BOOKMARK_BAR);
break;
case IDC_SHOW_BOOKMARK_MANAGER:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowBookmarkMgr",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_BOOKMARK_MANAGER);
break;
case IDC_IMPORT_SETTINGS:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ImportSettings",
delta);
}
LogMenuAction(MENU_ACTION_IMPORT_SETTINGS);
break;
case IDC_BOOKMARK_THIS_TAB:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.BookmarkPage",
delta);
}
LogMenuAction(MENU_ACTION_BOOKMARK_THIS_TAB);
break;
case IDC_BOOKMARK_ALL_TABS:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.BookmarkAllTabs",
delta);
}
LogMenuAction(MENU_ACTION_BOOKMARK_ALL_TABS);
break;
case IDC_PIN_TO_START_SCREEN:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.PinToStartScreen",
delta);
}
LogMenuAction(MENU_ACTION_PIN_TO_START_SCREEN);
break;
// Extensions menu.
case IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS:
CHECK(base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu) ||
features::IsChromeRefresh2023());
// Logging the original histograms for experiment comparison purposes.
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ManageExtensions",
delta);
}
LogMenuAction(MENU_ACTION_MANAGE_EXTENSIONS);
break;
case IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE:
CHECK(base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu) ||
features::IsChromeRefresh2023());
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES(
"WrenchMenu.TimeToAction.VisitChromeWebStore", delta);
}
LogMenuAction(MENU_ACTION_VISIT_CHROME_WEB_STORE);
break;
// Recent tabs menu.
case IDC_RESTORE_TAB:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.RestoreTab", delta);
LogMenuAction(MENU_ACTION_RESTORE_TAB);
break;
case IDC_DISTILL_PAGE:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.DistillPage",
delta);
}
LogMenuAction(MENU_ACTION_DISTILL_PAGE);
if (dom_distiller::url_utils::IsDistilledPage(
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL())) {
dom_distiller::UMAHelper::RecordReaderModeExit(
dom_distiller::UMAHelper::ReaderModeEntryPoint::kMenuOption);
} else {
dom_distiller::UMAHelper::RecordReaderModeEntry(
dom_distiller::UMAHelper::ReaderModeEntryPoint::kMenuOption);
}
break;
case IDC_SAVE_PAGE:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.SavePage", delta);
LogMenuAction(MENU_ACTION_SAVE_PAGE);
break;
case IDC_FIND:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Find", delta);
LogMenuAction(MENU_ACTION_FIND);
break;
case IDC_PRINT:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Print", delta);
LogMenuAction(MENU_ACTION_PRINT);
break;
case IDC_TRANSLATE_PAGE:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.TranslatePage",
delta);
}
LogMenuAction(MENU_ACTION_TRANSLATE_PAGE);
break;
case IDC_ROUTE_MEDIA:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Cast", delta);
LogMenuAction(MENU_ACTION_CAST);
break;
// Edit menu.
case IDC_CUT:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Cut", delta);
LogMenuAction(MENU_ACTION_CUT);
break;
case IDC_COPY:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Copy", delta);
LogMenuAction(MENU_ACTION_COPY);
break;
case IDC_PASTE:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Paste", delta);
LogMenuAction(MENU_ACTION_PASTE);
break;
// Tools menu.
case IDC_CREATE_SHORTCUT:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.CreateHostedApp",
delta);
}
LogMenuAction(MENU_ACTION_CREATE_HOSTED_APP);
break;
case IDC_MANAGE_EXTENSIONS:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ManageExtensions",
delta);
}
LogMenuAction(MENU_ACTION_MANAGE_EXTENSIONS);
break;
case IDC_TASK_MANAGER:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.TaskManager",
delta);
}
LogMenuAction(MENU_ACTION_TASK_MANAGER);
break;
case IDC_CLEAR_BROWSING_DATA:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ClearBrowsingData",
delta);
}
LogMenuAction(MENU_ACTION_CLEAR_BROWSING_DATA);
break;
case IDC_VIEW_SOURCE:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ViewSource", delta);
LogMenuAction(MENU_ACTION_VIEW_SOURCE);
break;
case IDC_DEV_TOOLS:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.DevTools", delta);
LogMenuAction(MENU_ACTION_DEV_TOOLS);
break;
case IDC_DEV_TOOLS_CONSOLE:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.DevToolsConsole",
delta);
}
LogMenuAction(MENU_ACTION_DEV_TOOLS_CONSOLE);
break;
case IDC_DEV_TOOLS_DEVICES:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.DevToolsDevices",
delta);
}
LogMenuAction(MENU_ACTION_DEV_TOOLS_DEVICES);
break;
case IDC_PROFILING_ENABLED:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ProfilingEnabled",
delta);
}
LogMenuAction(MENU_ACTION_PROFILING_ENABLED);
break;
// Zoom menu
case IDC_ZOOM_MINUS:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ZoomMinus", delta);
LogMenuAction(MENU_ACTION_ZOOM_MINUS);
}
break;
case IDC_ZOOM_PLUS:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ZoomPlus", delta);
LogMenuAction(MENU_ACTION_ZOOM_PLUS);
}
break;
case IDC_FULLSCREEN:
base::RecordAction(UserMetricsAction("EnterFullScreenWithWrenchMenu"));
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.EnterFullScreen",
delta);
}
LogMenuAction(MENU_ACTION_FULLSCREEN);
break;
case IDC_SHOW_HISTORY:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowHistory",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_HISTORY);
break;
case IDC_SHOW_DOWNLOADS:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowDownloads",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_DOWNLOADS);
base::UmaHistogramEnumeration(
"Download.OpenDownloadsFromMenu.PerProfileType",
profile_metrics::GetBrowserProfileType(browser_->profile()));
break;
case IDC_SHOW_SIGNIN:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowSyncSetup",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_SYNC_SETUP);
break;
case IDC_OPTIONS:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Settings", delta);
LogMenuAction(MENU_ACTION_OPTIONS);
base::UmaHistogramEnumeration(
"Settings.OpenSettingsFromMenu.PerProfileType",
profile_metrics::GetBrowserProfileType(browser_->profile()));
break;
case IDC_ABOUT:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.About", delta);
LogMenuAction(MENU_ACTION_ABOUT);
break;
// Help menu.
case IDC_HELP_PAGE_VIA_MENU:
base::RecordAction(UserMetricsAction("ShowHelpTabViaWrenchMenu"));
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.HelpPage", delta);
LogMenuAction(MENU_ACTION_HELP_PAGE_VIA_MENU);
break;
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
case IDC_SHOW_BETA_FORUM:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.BetaForum", delta);
LogMenuAction(MENU_ACTION_BETA_FORUM);
break;
case IDC_FEEDBACK:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Feedback", delta);
LogMenuAction(MENU_ACTION_FEEDBACK);
break;
case IDC_CHROME_TIPS:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ChromeTips", delta);
LogMenuAction(MENU_ACTION_CHROME_TIPS);
break;
case IDC_CHROME_WHATS_NEW:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ChromeWhatsNew",
delta);
}
LogMenuAction(MENU_ACTION_CHROME_WHATS_NEW);
break;
#endif
case IDC_TOGGLE_REQUEST_TABLET_SITE:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.RequestTabletSite",
delta);
}
LogMenuAction(MENU_ACTION_TOGGLE_REQUEST_TABLET_SITE);
break;
case IDC_EXIT:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.Exit", delta);
LogMenuAction(MENU_ACTION_EXIT);
break;
// Hosted App menu.
case IDC_COPY_URL:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.CopyUrl", delta);
LogMenuAction(MENU_ACTION_COPY_URL);
break;
case IDC_OPEN_IN_CHROME:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.OpenInChrome",
delta);
}
LogMenuAction(MENU_ACTION_OPEN_IN_CHROME);
break;
case IDC_WEB_APP_MENU_APP_INFO:
if (!uma_action_recorded_)
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.AppInfo", delta);
LogMenuAction(MENU_ACTION_APP_INFO);
break;
case IDC_VIEW_PASSWORDS:
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.PasswordManager",
delta);
}
LogMenuAction(MENU_ACTION_PASSWORD_MANAGER);
break;
}
if (!uma_action_recorded_) {
UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction", delta);
uma_action_recorded_ = true;
}
}
bool AppMenuModel::IsCommandIdChecked(int command_id) const {
if (command_id == IDC_SHOW_BOOKMARK_BAR) {
return browser_->profile()->GetPrefs()->GetBoolean(
bookmarks::prefs::kShowBookmarkBar);
}
if (command_id == IDC_PROFILING_ENABLED)
return content::Profiling::BeingProfiled();
if (command_id == IDC_TOGGLE_REQUEST_TABLET_SITE)
return chrome::IsRequestingTabletSite(browser_);
return false;
}
bool AppMenuModel::IsCommandIdEnabled(int command_id) const {
GlobalError* error =
GlobalErrorServiceFactory::GetForProfile(browser_->profile())
->GetGlobalErrorByMenuItemCommandID(command_id);
if (error)
return true;
switch (command_id) {
case IDC_NEW_INCOGNITO_WINDOW:
return IncognitoModePrefs::IsIncognitoAllowed(browser_->profile());
default:
return chrome::IsCommandEnabled(browser_, command_id);
}
}
bool AppMenuModel::IsCommandIdVisible(int command_id) const {
switch (command_id) {
case IDC_PIN_TO_START_SCREEN:
return false;
case IDC_UPGRADE_DIALOG: {
if (!browser_defaults::kShowUpgradeMenuItem || !app_menu_icon_controller_)
return false;
return app_menu_icon_controller_->GetTypeAndSeverity().type ==
AppMenuIconController::IconType::UPGRADE_NOTIFICATION;
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
case IDC_LACROS_DATA_MIGRATION: {
if (!app_menu_icon_controller_)
return false;
return app_menu_icon_controller_->GetTypeAndSeverity().type ==
AppMenuIconController::IconType::UPGRADE_NOTIFICATION;
}
#endif
default:
return true;
}
}
bool AppMenuModel::IsCommandIdAlerted(int command_id) const {
if ((command_id == IDC_RECENT_TABS_MENU) ||
(command_id == AppMenuModel::kMinRecentTabsCommandId)) {
return alert_item_ == AlertMenuItem::kReopenTabs;
}
if (command_id == IDC_PERFORMANCE) {
return alert_item_ == AlertMenuItem::kPerformance;
}
return false;
}
bool AppMenuModel::GetAcceleratorForCommandId(
int command_id,
ui::Accelerator* accelerator) const {
return provider_->GetAcceleratorForCommandId(command_id, accelerator);
}
void AppMenuModel::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
if (tab_strip_model->empty())
return;
if (selection.active_tab_changed()) {
// The user has switched between tabs and the new tab may have a different
// zoom setting. Or web contents for a tab has been replaced.
Observe(selection.new_contents);
UpdateZoomControls();
}
}
void AppMenuModel::NavigationEntryCommitted(
const content::LoadCommittedDetails& load_details) {
UpdateZoomControls();
}
void AppMenuModel::LogMenuAction(AppMenuAction action_id) {
LogWrenchMenuAction(action_id);
}
// Note: When adding new menu items please place under an appropriate section.
// Menu is organised as follows:
// - Extension toolbar overflow.
// - Global browser errors and warnings.
// - Tabs and windows.
// - Places previously been e.g. History, bookmarks, recent tabs.
// - Page actions e.g. zoom, edit, find, print.
// - Learn about the browser and global customisation e.g. settings, help.
// - Browser relaunch, quit.
void AppMenuModel::Build() {
// Build (and, by extension, Init) should only be called once.
DCHECK_EQ(0u, GetItemCount());
bool need_separator = false;
if (IsCommandIdVisible(IDC_UPGRADE_DIALOG)) {
AddItem(IDC_UPGRADE_DIALOG, GetUpgradeDialogMenuItemName());
need_separator = true;
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (IsCommandIdVisible(IDC_LACROS_DATA_MIGRATION)) {
AddItem(IDC_LACROS_DATA_MIGRATION, GetLacrosDataMigrationMenuItemName());
need_separator = true;
}
#endif
if (AddGlobalErrorMenuItems() || need_separator)
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringId(IDC_NEW_TAB, browser_->profile()->IsIncognitoProfile()
? IDS_NEW_INCOGNITO_TAB
: IDS_NEW_TAB);
AddItemWithStringId(IDC_NEW_WINDOW, IDS_NEW_WINDOW);
// This menu item is not visible in Guest Mode. If incognito mode is not
// available, it will be shown in disabled state. (crbug.com/1100791)
if (!browser_->profile()->IsGuestSession()) {
AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW, IDS_NEW_INCOGNITO_WINDOW);
SetElementIdentifierAt(
GetIndexOfCommandId(IDC_NEW_INCOGNITO_WINDOW).value(),
kIncognitoMenuItem);
}
AddSeparator(ui::NORMAL_SEPARATOR);
if (!browser_->profile()->IsGuestSession() &&
features::IsChromeRefresh2023() &&
!base::FeatureList::IsEnabled(
password_manager::features::kPasswordManagerRedesign)) {
sub_menus_.push_back(std::make_unique<AutofillSubMenuModel>(this));
AddSubMenuWithStringId(IDC_AUTOFILL_MENU, IDS_PASSWORDS_AND_AUTOFILL_MENU,
sub_menus_.back().get());
}
if (!browser_->profile()->IsOffTheRecord()) {
sub_menus_.push_back(
std::make_unique<RecentTabsSubMenuModel>(provider_, browser_));
AddSubMenuWithStringId(IDC_RECENT_TABS_MENU, IDS_HISTORY_MENU,
sub_menus_.back().get());
SetElementIdentifierAt(GetIndexOfCommandId(IDC_RECENT_TABS_MENU).value(),
kHistoryMenuItem);
}
AddItemWithStringId(IDC_SHOW_DOWNLOADS, IDS_SHOW_DOWNLOADS);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_SHOW_DOWNLOADS).value(),
kDownloadsMenuItem);
if (!browser_->profile()->IsGuestSession()) {
bookmark_sub_menu_model_ =
std::make_unique<BookmarkSubMenuModel>(this, browser_);
AddSubMenuWithStringId(IDC_BOOKMARKS_MENU,
features::IsChromeRefresh2023()
? IDS_BOOKMARKS_AND_LISTS_MENU
: IDS_BOOKMARKS_MENU,
bookmark_sub_menu_model_.get());
SetElementIdentifierAt(GetIndexOfCommandId(IDC_BOOKMARKS_MENU).value(),
kBookmarksMenuItem);
}
WebContents* web_contents =
browser_->tab_strip_model()->GetActiveWebContents();
if (!browser_->profile()->IsOffTheRecord() && web_contents &&
!IsPasswordManagerPage(web_contents->GetURL()) &&
base::FeatureList::IsEnabled(
password_manager::features::kPasswordManagerRedesign)) {
AddItemWithStringId(IDC_VIEW_PASSWORDS, IDS_VIEW_PASSWORDS);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_VIEW_PASSWORDS).value(),
kPasswordManagerMenuItem);
if (!browser_->profile()->GetPrefs()->GetBoolean(
password_manager::prefs::kPasswordsPrefWithNewLabelUsed)) {
SetIsNewFeatureAt(GetIndexOfCommandId(IDC_VIEW_PASSWORDS).value(), true);
}
}
if (base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu) ||
features::IsChromeRefresh2023()) {
// Extensions sub menu.
sub_menus_.push_back(std::make_unique<ExtensionsMenuModel>(this, browser_));
AddSubMenuWithStringId(IDC_EXTENSIONS_SUBMENU, IDS_EXTENSIONS_SUBMENU,
sub_menus_.back().get());
SetElementIdentifierAt(GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU).value(),
kExtensionsMenuItem);
if (features::IsChromeRefresh2023()) {
SetIcon(GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU).value(),
ui::ImageModel::FromVectorIcon(
vector_icons::kExtensionChromeRefreshIcon, ui::kColorMenuIcon,
kDefaultIconSize));
}
}
AddSeparator(features::IsChromeRefresh2023() ? ui::NORMAL_SEPARATOR
: ui::LOWER_SEPARATOR);
CreateZoomMenu();
AddSeparator(features::IsChromeRefresh2023() ? ui::NORMAL_SEPARATOR
: ui::UPPER_SEPARATOR);
AddItemWithStringId(IDC_PRINT, IDS_PRINT);
if (media_router::MediaRouterEnabled(browser()->profile()))
AddItemWithStringId(IDC_ROUTE_MEDIA, IDS_MEDIA_ROUTER_MENU_ITEM_TITLE);
if (features::IsChromeRefresh2023()) {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
if (companion::IsCompanionFeatureEnabled()) {
// TODO(josephjoopark): Update CSC string with StringId when finalized.
AddItem(IDC_SHOW_SEARCH_COMPANION, u"Chrome Search Companion");
}
#endif
// TODO(josephjoopark): Update translate string with StringId when
// finalized.
AddItem(IDC_TRANSLATE_PAGE, u"Google Translate");
}
if (features::IsChromeRefresh2023()) {
sub_menus_.push_back(std::make_unique<FindAndEditSubMenuModel>(this));
AddSubMenuWithStringId(IDC_FIND_AND_EDIT_MENU, IDS_FIND_AND_EDIT_MENU,
sub_menus_.back().get());
} else {
AddItemWithStringId(IDC_FIND, IDS_FIND);
}
if (absl::optional<std::u16string> name =
GetInstallPWAAppMenuItemName(browser_)) {
AddItem(IDC_INSTALL_PWA, *name);
if (features::IsChromeRefresh2023()) {
SetIcon(
GetIndexOfCommandId(IDC_INSTALL_PWA).value(),
ui::ImageModel::FromVectorIcon(kInstallDesktopChromeRefreshIcon,
ui::kColorMenuIcon, kDefaultIconSize));
}
} else if (absl::optional<web_app::AppId> app_id =
web_app::GetWebAppForActiveTab(browser_)) {
auto* provider =
web_app::WebAppProvider::GetForLocalAppsUnchecked(browser_->profile());
// Only applies to apps that open in an app window.
if (provider->registrar_unsafe().GetAppUserDisplayMode(*app_id) !=
web_app::mojom::UserDisplayMode::kBrowser) {
const std::u16string short_name = base::UTF8ToUTF16(
provider->registrar_unsafe().GetAppShortName(*app_id));
const std::u16string truncated_name = gfx::TruncateString(
short_name, kMaxAppNameLength, gfx::CHARACTER_BREAK);
AddItem(
IDC_OPEN_IN_PWA_WINDOW,
l10n_util::GetStringFUTF16(IDS_OPEN_IN_APP_WINDOW, truncated_name));
if (features::IsChromeRefresh2023()) {
SetIcon(GetIndexOfCommandId(IDC_OPEN_IN_PWA_WINDOW).value(),
ui::ImageModel::FromVectorIcon(
kDesktopWindowsIcon, ui::kColorMenuIcon, kDefaultIconSize));
}
}
}
if (dom_distiller::IsDomDistillerEnabled() &&
browser()->tab_strip_model()->GetActiveWebContents()) {
// Only show the reader mode toggle when it will do something.
if (dom_distiller::url_utils::IsDistilledPage(
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL())) {
// Show the menu option if we are on a distilled page.
AddItemWithStringId(IDC_DISTILL_PAGE, IDS_EXIT_DISTILLED_PAGE);
} else if (dom_distiller::ShowReaderModeOption(
browser_->profile()->GetPrefs())) {
// Show the menu option if the page is distillable.
absl::optional<dom_distiller::DistillabilityResult> distillability =
dom_distiller::GetLatestResult(
browser()->tab_strip_model()->GetActiveWebContents());
if (distillability && distillability.value().is_distillable)
AddItemWithStringId(IDC_DISTILL_PAGE, IDS_DISTILL_PAGE);
}
}
#if BUILDFLAG(IS_CHROMEOS)
// Always show this option if we're in tablet mode on Chrome OS.
if (chromeos::TabletState::Get() &&
chromeos::TabletState::Get()->InTabletMode()) {
AddCheckItemWithStringId(IDC_TOGGLE_REQUEST_TABLET_SITE,
IDS_TOGGLE_REQUEST_TABLET_SITE);
}
#endif
sub_menus_.push_back(std::make_unique<ToolsMenuModel>(this, browser_));
AddSubMenuWithStringId(IDC_MORE_TOOLS_MENU, IDS_MORE_TOOLS_MENU,
sub_menus_.back().get());
SetElementIdentifierAt(GetIndexOfCommandId(IDC_MORE_TOOLS_MENU).value(),
kMoreToolsMenuItem);
if (!features::IsChromeRefresh2023()) {
AddSeparator(ui::LOWER_SEPARATOR);
CreateCutCopyPasteMenu();
AddSeparator(ui::UPPER_SEPARATOR);
}
if (!features::IsChromeRefresh2023()) {
AddItemWithStringId(IDC_OPTIONS, IDS_SETTINGS);
} else {
AddSeparator(ui::NORMAL_SEPARATOR);
}
// The help submenu is only displayed on official Chrome builds. As the
// 'About' item has been moved to this submenu, it's reinstated here for
// Chromium builds.
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
sub_menus_.push_back(std::make_unique<HelpMenuModel>(this, browser_));
AddSubMenuWithStringId(IDC_HELP_MENU, IDS_HELP_MENU, sub_menus_.back().get());
#else
#if BUILDFLAG(IS_CHROMEOS_ASH)
AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT));
#else
AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT));
#endif
#endif
if (features::IsChromeRefresh2023()) {
AddItemWithStringId(IDC_OPTIONS, IDS_SETTINGS);
}
if (browser_defaults::kShowExitMenuItem) {
if (!features::IsChromeRefresh2023()) {
AddSeparator(ui::NORMAL_SEPARATOR);
}
AddItemWithStringId(IDC_EXIT, IDS_EXIT);
}
// On Chrome OS, similar UI is displayed in the system tray menu, instead of
// this menu.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
if (chrome::ShouldDisplayManagedUi(browser_->profile())) {
AddSeparator(features::IsChromeRefresh2023() ? ui::NORMAL_SEPARATOR
: ui::LOWER_SEPARATOR);
if (!features::IsChromeRefresh2023()) {
const int kIconSize = 18;
AddHighlightedItemWithIcon(
IDC_SHOW_MANAGEMENT_PAGE,
chrome::GetManagedUiMenuItemLabel(browser_->profile()),
ui::ImageModel::FromVectorIcon(
vector_icons::kBusinessIcon,
ui::kColorMenuItemForegroundHighlighted, kIconSize));
} else {
AddItemWithIcon(IDC_SHOW_MANAGEMENT_PAGE,
chrome::GetManagedUiMenuItemLabel(browser_->profile()),
ui::ImageModel::FromVectorIcon(
vector_icons::kBusinessChromeRefreshIcon,
ui::kColorMenuIcon, kDefaultIconSize));
}
}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
if (features::IsChromeRefresh2023()) {
auto set_icon = [this](int command_id, const gfx::VectorIcon& vector_icon) {
auto index = GetIndexOfCommandId(command_id);
if (index) {
SetIcon(index.value(),
ui::ImageModel::FromVectorIcon(vector_icon, ui::kColorMenuIcon,
kDefaultIconSize));
}
};
set_icon(IDC_NEW_TAB, kNewTabRefreshIcon);
set_icon(IDC_NEW_WINDOW, kNewWindowIcon);
set_icon(IDC_NEW_INCOGNITO_WINDOW, kIncognitoRefreshMenuIcon);
set_icon(IDC_RECENT_TABS_MENU, kHistoryIcon);
set_icon(IDC_SHOW_DOWNLOADS, kDownloadMenuIcon);
set_icon(IDC_BOOKMARKS_MENU, kBookmarksListsMenuIcon);
set_icon(IDC_VIEW_PASSWORDS, kKeyChromeRefreshIcon);
set_icon(IDC_ZOOM_MENU, kZoomInIcon);
set_icon(IDC_PRINT, kPrintMenuIcon);
set_icon(IDC_TRANSLATE_PAGE, kTranslateChromeRefreshIcon);
set_icon(IDC_ROUTE_MEDIA, kCastMenuIcon);
set_icon(IDC_FIND_AND_EDIT_MENU, kSearchMenuIcon);
set_icon(IDC_AUTOFILL_MENU, kKeyChromeRefreshIcon);
set_icon(IDC_MORE_TOOLS_MENU, kMoreToolsMenuIcon);
set_icon(IDC_OPTIONS, kSettingsMenuIcon);
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
set_icon(IDC_HELP_MENU, kHelpMenuIcon);
set_icon(IDC_SHOW_SEARCH_COMPANION,
vector_icons::kGoogleGLogoMonochromeIcon);
#endif
set_icon(IDC_EXIT, kExitMenuIcon);
}
uma_action_recorded_ = false;
}
void AppMenuModel::CreateCutCopyPasteMenu() {
// WARNING: Mac does not use the ButtonMenuItemModel, but instead defines
// the layout for this menu item in AppMenu.xib. It does, however, use the
// command_id value from AddButtonItem() to identify this special item.
edit_menu_item_model_ =
std::make_unique<ui::ButtonMenuItemModel>(IDS_EDIT, this);
edit_menu_item_model_->AddGroupItemWithStringId(IDC_CUT, IDS_CUT);
edit_menu_item_model_->AddGroupItemWithStringId(IDC_COPY, IDS_COPY);
edit_menu_item_model_->AddGroupItemWithStringId(IDC_PASTE, IDS_PASTE);
AddButtonItem(IDC_EDIT_MENU, edit_menu_item_model_.get());
}
void AppMenuModel::CreateZoomMenu() {
zoom_menu_item_model_ =
std::make_unique<ui::ButtonMenuItemModel>(IDS_ZOOM_MENU, this);
zoom_menu_item_model_->AddGroupItemWithStringId(IDC_ZOOM_MINUS,
IDS_ZOOM_MINUS2);
zoom_menu_item_model_->AddGroupItemWithStringId(IDC_ZOOM_PLUS,
IDS_ZOOM_PLUS2);
zoom_menu_item_model_->AddImageItem(IDC_FULLSCREEN);
AddButtonItem(IDC_ZOOM_MENU, zoom_menu_item_model_.get());
}
void AppMenuModel::UpdateZoomControls() {
int zoom_percent = 100; // Defaults to 100% zoom.
if (web_contents()) {
zoom_percent =
zoom::ZoomController::FromWebContents(web_contents())->GetZoomPercent();
}
zoom_label_ = base::FormatPercent(zoom_percent);
}
bool AppMenuModel::AddGlobalErrorMenuItems() {
// TODO(sail): Currently we only build the app menu once per browser
// window. This means that if a new error is added after the menu is built
// it won't show in the existing app menu. To fix this we need to some
// how update the menu if new errors are added.
const GlobalErrorService::GlobalErrorList& errors =
GlobalErrorServiceFactory::GetForProfile(browser_->profile())->errors();
bool menu_items_added = false;
for (auto* error : errors) {
DCHECK(error);
if (error->HasMenuItem()) {
AddItem(error->MenuItemCommandID(), error->MenuItemLabel());
SetIcon(GetIndexOfCommandId(error->MenuItemCommandID()).value(),
error->MenuItemIcon());
menu_items_added = true;
}
}
return menu_items_added;
}
void AppMenuModel::OnZoomLevelChanged(
const content::HostZoomMap::ZoomLevelChange& change) {
UpdateZoomControls();
}
#if BUILDFLAG(IS_CHROMEOS)
void AppMenuModel::UpdateSettingsItemState() {
bool is_disabled =
policy::SystemFeaturesDisableListPolicyHandler::IsSystemFeatureDisabled(
policy::SystemFeature::kBrowserSettings,
g_browser_process->local_state());
absl::optional<size_t> index = GetIndexOfCommandId(IDC_OPTIONS);
if (index.has_value())
SetEnabledAt(index.value(), !is_disabled);
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
index = GetIndexOfCommandId(IDC_HELP_MENU);
if (index.has_value()) {
ui::SimpleMenuModel* help_menu =
static_cast<ui::SimpleMenuModel*>(GetSubmenuModelAt(index.value()));
index = help_menu->GetIndexOfCommandId(IDC_ABOUT);
if (index.has_value())
help_menu->SetEnabledAt(index.value(), !is_disabled);
}
#else // BUILDFLAG(GOOGLE_CHROME_BRANDING)
index = GetIndexOfCommandId(IDC_ABOUT);
if (index.has_value())
SetEnabledAt(index.value(), !is_disabled);
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
}
#endif // BUILDFLAG(IS_CHROMEOS)