blob: b9101495ee9b02c7eff2b896530d8bfb40ab0a7f [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 <optional>
#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/memory/raw_ptr.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 "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/extensions/extension_ui_util.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/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/search/background/ntp_custom_background_service_factory.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/sharing_hub/sharing_hub_features.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/sync/sync_ui_util.h"
#include "chrome/browser/themes/theme_service_factory.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_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.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/hats/trust_safety_sentiment_service_factory.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/lens/lens_overlay_controller.h"
#include "chrome/browser/ui/lens/lens_overlay_entry_point_controller.h"
#include "chrome/browser/ui/lens/lens_string_utils.h"
#include "chrome/browser/ui/managed_ui.h"
#include "chrome/browser/ui/profiles/profile_colors_util.h"
#include "chrome/browser/ui/profiles/profile_view_utils.h"
#include "chrome/browser/ui/safety_hub/menu_notification_service_factory.h"
#include "chrome/browser/ui/safety_hub/safety_hub_constants.h"
#include "chrome/browser/ui/safety_hub/safety_hub_hats_service.h"
#include "chrome/browser/ui/safety_hub/safety_hub_hats_service_factory.h"
#include "chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.h"
#include "chrome/browser/ui/tabs/organization/tab_declutter_controller.h"
#include "chrome/browser/ui/tabs/organization/tab_organization_service_factory.h"
#include "chrome/browser/ui/tabs/organization/tab_organization_utils.h"
#include "chrome/browser/ui/tabs/recent_tabs_sub_menu_model.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/chrome_labs/chrome_labs_prefs.h"
#include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_utils.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/user_education/browser_user_education_interface.h"
#include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h"
#include "chrome/browser/ui/webui/signin/signin_utils_desktop.h"
#include "chrome/browser/ui/webui/whats_new/whats_new_util.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/proto/web_app_install_state.pb.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_tab_helper.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/branded_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/feature_engagement/public/feature_constants.h"
#include "components/lens/lens_features.h"
#include "components/omnibox/browser/vector_icons.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/prefs/pref_member.h"
#include "components/prefs/pref_service.h"
#include "components/profile_metrics/browser_profile_type.h"
#include "components/saved_tab_groups/public/features.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/strings/grit/components_strings.h"
#include "components/sync/base/features.h"
#include "components/user_education/common/feature_promo/feature_promo_controller.h"
#include "components/vector_icons/vector_icons.h"
#include "components/webapps/browser/banners/app_banner_manager.h"
#include "components/webapps/browser/banners/install_banner_config.h"
#include "components/webapps/browser/banners/installable_web_app_check_result.h"
#include "components/webapps/browser/banners/web_app_banner_data.h"
#include "components/webapps/common/web_app_id.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 "extensions/common/extension_features.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/models/button_menu_item_model.h"
#include "ui/base/models/image_model.h"
#include "ui/base/models/menu_separator_types.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/text_elider.h"
#include "ui/menus/simple_menu_model.h"
#if BUILDFLAG(ENABLE_GLIC)
#include "chrome/browser/glic/browser_ui/glic_vector_icon_manager.h"
#include "chrome/browser/glic/public/glic_enabling.h"
#include "chrome/browser/glic/resources/grit/glic_browser_resources.h"
#endif
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) || BUILDFLAG(IS_CHROMEOS)
#include "base/feature_list.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/policy/system_features_disable_list_policy_handler.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "ui/display/screen.h"
#else
#include "chrome/browser/ui/webui/signin/signin_ui_error.h"
#include "chrome/browser/ui/webui/signin/signin_utils_desktop.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, kProfileMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kProfileOpenGuestItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kBookmarksMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kTabGroupsMenuItem);
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,
kPasswordAndAutofillMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kPasswordManagerMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kShowLensOverlay);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kSaveAndShareMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kCastTitleItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kInstallAppItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kCreateShortcutItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel,
kSetBrowserAsDefaultMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ToolsMenuModel, kPerformanceMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ToolsMenuModel, kChromeLabsMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ToolsMenuModel, kReadingModeMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ExtensionsMenuModel,
kManageExtensionsMenuItem);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ExtensionsMenuModel,
kVisitChromeWebStoreMenuItem);
namespace {
////////////////////////////////////////////////////////////////////////////////
// AddItemWithStringIdAndVectorIcon
void AddItemWithStringIdAndVectorIcon(ui::SimpleMenuModel* model,
int command_id,
int string_id,
const gfx::VectorIcon& vector_icon) {
return model->AddItemWithStringIdAndIcon(
command_id, string_id,
ui::ImageModel::FromVectorIcon(vector_icon, ui::kColorMenuIcon,
ui::SimpleMenuModel::kDefaultIconSize));
}
////////////////////////////////////////////////////////////////////////////////
// AddSubMenuWithStringIdAndVectorIcon
void AddSubMenuWithStringIdAndVectorIcon(ui::SimpleMenuModel* model,
int command_id,
int string_id,
ui::MenuModel* sub_menu,
const gfx::VectorIcon& vector_icon) {
return model->AddSubMenuWithStringIdAndIcon(
command_id, string_id, sub_menu,
ui::ImageModel::FromVectorIcon(vector_icon, ui::kColorMenuIcon,
ui::SimpleMenuModel::kDefaultIconSize));
}
// Conditionally return the update app menu item title based on upgrade detector
// state.
std::u16string GetUpgradeDialogTitleText() {
if (UpgradeDetector::GetInstance()->is_outdated_install() ||
UpgradeDetector::GetInstance()->is_outdated_install_no_au()) {
return l10n_util::GetStringUTF16(IDS_UPGRADE_BUBBLE_MENU_ITEM);
}
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && \
(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX))
return l10n_util::GetStringUTF16(IDS_RELAUNCH_TO_UPDATE_ALT);
#else
return l10n_util::GetStringUTF16(IDS_RELAUNCH_TO_UPDATE);
#endif
}
// Returns the appropriate menu label for the IDC_INSTALL_PWA command if
// available.
std::u16string GetInstallPWALabel(const Browser* browser) {
// There may be no active web contents in tests.
auto* const web_contents = browser->tab_strip_model()->GetActiveWebContents();
if (!web_contents) {
return std::u16string();
}
if (!web_app::CanCreateWebApp(browser)) {
return std::u16string();
}
// Don't allow apps created from chrome-extension urls.
if (web_contents->GetLastCommittedURL().SchemeIs("chrome-extension")) {
return std::u16string();
}
// TODO(b/328077967): Support async nature of AppBannerManager pipeline runs
// with the menu model instead of needing this workaround to verify if an
// non-installable site is installed.
const webapps::AppId* app_id =
web_app::WebAppTabHelper::GetAppId(web_contents);
web_app::WebAppProvider* const provider =
web_app::WebAppProvider::GetForLocalAppsUnchecked(browser->profile());
if (app_id &&
provider->registrar_unsafe().GetInstallState(*app_id) ==
web_app::proto::INSTALLED_WITH_OS_INTEGRATION &&
provider->registrar_unsafe().GetAppUserDisplayMode(*app_id) !=
web_app::mojom::UserDisplayMode::kBrowser) {
return std::u16string();
}
std::u16string install_page_as_app_label =
l10n_util::GetStringUTF16(IDS_INSTALL_DIY_TO_OS_LAUNCH_SURFACE);
webapps::AppBannerManager* banner =
webapps::AppBannerManager::FromWebContents(web_contents);
if (!banner) {
// Showing `Install Page as App` allows the user to refetch the manifest and
// go through the install flow without relying on the AppBannerManager to
// finish working.
return install_page_as_app_label;
}
std::optional<webapps::InstallBannerConfig> install_config =
banner->GetCurrentBannerConfig();
if (!install_config) {
// In some edge cases where the `AppBannerManager` pipeline hasn't run yet,
// the information populated to be used for determining installability and
// other parameters is not available. In this case, allow users to try
// installability by refetching the manifest.
return install_page_as_app_label;
}
CHECK_EQ(install_config->mode, webapps::AppBannerMode::kWebApp);
webapps::InstallableWebAppCheckResult installable =
banner->GetInstallableWebAppCheckResult();
switch (installable) {
case webapps::InstallableWebAppCheckResult::kUnknown:
// Loading of the menu model is synchronous, so there could be a condition
// where the `AppBannerManager` has not yet finished the pipeline while
// the menu item has been triggered. In such a case,
// `banner->GetInstallableWebAppCheckResult()` returns the default value
// of `kUnknown`.
// Show `Install Page as App` for that use-case, since that allows the
// user to trigger the install flow to verify all the data required for
// installability. The correct dialog will be shown to the user depending
// on whether the app turns out to be installable or not.
return install_page_as_app_label;
case webapps::InstallableWebAppCheckResult::kNo_AlreadyInstalled:
// Returning an empty string here allows the `launch page as app` field to
// get populated in place of the `install` strings.
return std::u16string();
case webapps::InstallableWebAppCheckResult::kNo:
return install_page_as_app_label;
case webapps::InstallableWebAppCheckResult::kYes_ByUserRequest:
case webapps::InstallableWebAppCheckResult::kYes_Promotable:
std::u16string app_name = install_config->GetWebOrNativeAppName();
if (app_name.empty()) {
// Prefer showing `Install Page as App` here, as users can set the name
// of the installed app on the DIY app dialog anyway.
return install_page_as_app_label;
}
return l10n_util::GetStringFUTF16(
IDS_INSTALL_TO_OS_LAUNCH_SURFACE,
ui::EscapeMenuLabelAmpersands(app_name));
}
}
// TODO(b/328077967): Implement async updates of menu for app icon.
ui::ImageModel GetInstallPWAIcon(Browser* browser) {
ui::ImageModel app_icon_to_use = ui::ImageModel::FromVectorIcon(
kInstallDesktopChromeRefreshIcon, ui::kColorMenuIcon,
ui::SimpleMenuModel::kDefaultIconSize);
content::WebContents* const web_contents =
browser->tab_strip_model()->GetActiveWebContents();
if (!web_contents) {
return app_icon_to_use;
}
webapps::AppBannerManager* const banner =
webapps::AppBannerManager::FromWebContents(web_contents);
if (!banner) {
return app_icon_to_use;
}
// For sites that are not installable (DIY apps), do not return any icons,
// instead use the default chrome refresh icon for installing.
auto installable_check_result = banner->GetInstallableWebAppCheckResult();
if (installable_check_result == webapps::InstallableWebAppCheckResult::kNo ||
installable_check_result ==
webapps::InstallableWebAppCheckResult::kUnknown) {
return app_icon_to_use;
}
std::optional<webapps::WebAppBannerData> install_config =
banner->GetCurrentWebAppBannerData();
// If no data or no icons have been obtained by the AppBannerManager, return
// the default icon.
if (!install_config || install_config->primary_icon.empty()) {
return app_icon_to_use;
}
gfx::ImageSkia primary_icon =
gfx::ImageSkia::CreateFrom1xBitmap(install_config->primary_icon);
gfx::ImageSkia resized_app_icon =
gfx::ImageSkiaOperations::CreateResizedImage(
primary_icon, skia::ImageOperations::RESIZE_BEST,
gfx::Size(ui::SimpleMenuModel::kDefaultIconSize,
ui::SimpleMenuModel::kDefaultIconSize));
app_icon_to_use = ui::ImageModel::FromImageSkia(resized_app_icon);
return app_icon_to_use;
}
// Returns the appropriate menu label for the IDC_OPEN_IN_PWA_WINDOW command if
// available.
std::u16string GetOpenPWALabel(const Browser* browser) {
std::optional<webapps::AppId> app_id =
web_app::GetWebAppForActiveTab(browser);
if (!app_id.has_value()) {
return std::u16string();
}
// Only show this menu item for apps that open in an app window.
const auto* const provider =
web_app::WebAppProvider::GetForLocalAppsUnchecked(browser->profile());
if (provider->registrar_unsafe().GetAppUserDisplayMode(*app_id) ==
web_app::mojom::UserDisplayMode::kBrowser) {
return std::u16string();
}
const std::u16string short_name =
base::UTF8ToUTF16(provider->registrar_unsafe().GetAppShortName(*app_id));
return l10n_util::GetStringFUTF16(
IDS_OPEN_IN_APP_WINDOW,
ui::EscapeMenuLabelAmpersands(gfx::TruncateString(
short_name, GetLayoutConstant(APP_MENU_MAXIMUM_CHARACTER_LENGTH),
gfx::CHARACTER_BREAK)));
}
std::u16string GetSyncSectionTitle(Profile* profile,
signin::IdentityManager* identity_manager) {
const AccountInfo account = GetAccountInfoFromProfile(profile);
if (IsSyncPaused(profile) || account.IsEmpty()) {
return l10n_util::GetStringUTF16(IDS_PROFILES_LOCAL_PROFILE_STATE);
}
if (signin_util::IsSigninPending(identity_manager)) {
return base::UTF8ToUTF16(account.email);
}
return l10n_util::GetStringFUTF16(
IDS_PROFILE_ROW_SIGNED_IN_MESSAGE_WITH_EMAIL,
{base::UTF8ToUTF16(account.email)});
}
class ProfileSubMenuModel : public ui::SimpleMenuModel,
public ui::SimpleMenuModel::Delegate {
public:
ProfileSubMenuModel(ui::SimpleMenuModel::Delegate* delegate,
Profile* profile,
const ui::ColorProvider* color_provider);
ProfileSubMenuModel(const ProfileSubMenuModel&) = delete;
ProfileSubMenuModel& operator=(const ProfileSubMenuModel&) = delete;
~ProfileSubMenuModel() override = default;
const ui::ImageModel& avatar_image_model() const {
return avatar_image_model_;
}
const std::u16string& profile_name() const { return profile_name_; }
// Returns |next_menu_id_| and increments it by n. This allows for 'sharing'
// command ids with the other variable sized menu, which also uses every other
// int as an id.
int GetAndIncrementNextMenuID();
// Overridden from ui::SimpleMenuModel::Delegate:
bool IsCommandIdChecked(int command_id) const override;
bool IsCommandIdEnabled(int command_id) const override;
bool GetAcceleratorForCommandId(int command_id,
ui::Accelerator* accelerator) const override;
void ExecuteCommand(int command_id, int event_flags) override;
private:
bool BuildSyncSection();
void BuildGuestProfileRow(Profile* profile);
void BuildCustomizeProfileRow(Profile* profile);
void BuildCloseProfileRow(Profile* profile);
void BuildManageGoogleAccountRow(Profile* profile);
ui::ImageModel avatar_image_model_;
std::u16string profile_name_;
raw_ptr<Profile> profile_;
raw_ptr<ui::SimpleMenuModel::Delegate> app_menu_model_delegate_;
// ID of the next menu item.
int next_other_profile_menu_id_;
base::flat_map<int, base::FilePath> other_profiles_;
};
ProfileSubMenuModel::ProfileSubMenuModel(
ui::SimpleMenuModel::Delegate* delegate,
Profile* profile,
const ui::ColorProvider* color_provider)
: SimpleMenuModel(this),
profile_(profile),
app_menu_model_delegate_(delegate),
next_other_profile_menu_id_(AppMenuModel::kMinOtherProfileCommandId) {
const int avatar_icon_size =
GetLayoutConstant(APP_MENU_PROFILE_ROW_AVATAR_ICON_SIZE);
avatar_image_model_ = ui::ImageModel::FromVectorIcon(
kAccountCircleChromeRefreshIcon, ui::kColorMenuIcon, avatar_icon_size);
if (profile->IsIncognitoProfile()) {
avatar_image_model_ = ui::ImageModel::FromVectorIcon(
kIncognitoIcon, ui::kColorAvatarIconIncognito, avatar_icon_size);
profile_name_ = l10n_util::GetStringUTF16(IDS_INCOGNITO_PROFILE_MENU_TITLE);
} else if (profile->IsGuestSession()) {
profile_name_ = l10n_util::GetStringUTF16(IDS_GUEST_PROFILE_NAME);
} else {
if (BuildSyncSection()) {
AddSeparator(ui::NORMAL_SEPARATOR);
}
ProfileAttributesEntry* profile_attributes =
GetProfileAttributesFromProfile(profile);
// If the profile is being deleted, profile_attributes may be null.
if (profile_attributes) {
AccountInfo account_info = GetAccountInfoFromProfile(profile);
gfx::Image avatar_image =
account_info.IsEmpty()
? profile_attributes->GetAvatarIcon(
avatar_icon_size, /*use_high_res_file=*/true,
GetPlaceholderAvatarIconParamsDependingOnTheme(
ThemeServiceFactory::GetForProfile(profile),
/*background_color_id=*/ui::kColorMenuBackground,
*color_provider))
: account_info.account_image;
// The avatar image can be empty if the account image hasn't been
// fetched yet, if there is no image, or in tests.
if (!avatar_image.IsEmpty()) {
avatar_image_model_ =
ui::ImageModel::FromImage(profiles::GetSizedAvatarIcon(
avatar_image, avatar_icon_size, avatar_icon_size,
profiles::SHAPE_CIRCLE));
}
profile_name_ = GetProfileMenuDisplayName(profile_attributes);
}
}
BuildManageGoogleAccountRow(profile);
BuildCustomizeProfileRow(profile);
BuildCloseProfileRow(profile);
bool needs_separator = false;
const bool is_guest_mode_enabled = profiles::IsGuestModeEnabled(*profile);
if (!profile->IsIncognitoProfile() && !profile->IsGuestSession()) {
AddSeparator(ui::NORMAL_SEPARATOR);
AddTitle(l10n_util::GetStringUTF16(IDS_OTHER_CHROME_PROFILES_TITLE));
auto profile_entries = GetAllOtherProfileEntriesForProfileSubMenu(profile);
needs_separator = !profile_entries.empty();
profiles::PlaceholderAvatarIconParams icon_params =
GetPlaceholderAvatarIconParamsVisibleAgainstColor(
color_provider->GetColor(ui::kColorMenuBackground));
for (ProfileAttributesEntry* profile_entry : profile_entries) {
std::u16string display_name = GetProfileMenuDisplayName(profile_entry);
int menu_id = GetAndIncrementNextMenuID();
AddItemWithIcon(
menu_id,
ui::EscapeMenuLabelAmpersands(gfx::TruncateString(
display_name,
GetLayoutConstant(APP_MENU_MAXIMUM_CHARACTER_LENGTH),
gfx::CHARACTER_BREAK)),
ui::ImageModel::FromImage(profiles::GetSizedAvatarIcon(
profile_entry->GetAvatarIcon(
avatar_icon_size, /*use_high_res_file=*/true, icon_params),
avatar_icon_size, avatar_icon_size, profiles::SHAPE_CIRCLE)));
other_profiles_.insert({menu_id, profile_entry->GetPath()});
}
if (needs_separator) {
AddSeparator(ui::NORMAL_SEPARATOR);
}
if (profiles::IsProfileCreationAllowed()) {
AddItemWithStringIdAndVectorIcon(this, IDC_ADD_NEW_PROFILE,
IDS_ADD_NEW_PROFILE,
kAccountAddChromeRefreshIcon);
}
if (is_guest_mode_enabled) {
BuildGuestProfileRow(profile);
}
AddItemWithStringIdAndVectorIcon(this, IDC_MANAGE_CHROME_PROFILES,
IDS_MANAGE_CHROME_PROFILES,
kAccountManageChromeRefreshIcon);
}
}
bool ProfileSubMenuModel::IsCommandIdChecked(int command_id) const {
return false;
}
bool ProfileSubMenuModel::IsCommandIdEnabled(int command_id) const {
if (command_id >= AppMenuModel::kMinOtherProfileCommandId) {
return true;
}
return app_menu_model_delegate_ &&
app_menu_model_delegate_->IsCommandIdEnabled(command_id);
}
bool ProfileSubMenuModel::GetAcceleratorForCommandId(
int command_id,
ui::Accelerator* accelerator) const {
return app_menu_model_delegate_ &&
app_menu_model_delegate_->GetAcceleratorForCommandId(command_id,
accelerator);
}
void ProfileSubMenuModel::ExecuteCommand(int command_id, int event_flags) {
if (other_profiles_.find(command_id) != other_profiles_.end()) {
return profiles::SwitchToProfile(other_profiles_[command_id],
/*always_create=*/false);
}
if (app_menu_model_delegate_) {
app_menu_model_delegate_->ExecuteCommand(command_id, event_flags);
}
return;
}
int ProfileSubMenuModel::GetAndIncrementNextMenuID() {
const int current_id = next_other_profile_menu_id_;
next_other_profile_menu_id_ += AppMenuModel::kNumUnboundedMenuTypes;
return current_id;
}
bool ProfileSubMenuModel::BuildSyncSection() {
#if !BUILDFLAG(IS_CHROMEOS)
// TODO(crbug.com/440342282): Support personalized signin button.
if (!CanOfferSignin(profile_, GaiaId(), /*email=*/std::string(),
/*allow_account_from_other_profile=*/true)
.IsOk()) {
return false;
}
#endif // BUILDFLAG(IS_CHROMEOS)
if (!SyncServiceFactory::IsSyncAllowed(profile_)) {
return false;
}
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_);
AddTitle(GetSyncSectionTitle(profile_, identity_manager));
// First, check for sync errors. They may exist even if sync-the-feature is
// disabled and only sync-the-transport is running.
const std::optional<AvatarSyncErrorType> error =
GetAvatarSyncErrorType(profile_);
if (error.has_value()) {
if (error == AvatarSyncErrorType::kSyncPaused) {
if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
// If sync is paused the menu item will be specific to the paused error.
AddItemWithStringIdAndVectorIcon(
this, IDC_SHOW_SIGNIN_WHEN_PAUSED, IDS_PROFILE_ROW_SIGN_IN_AGAIN,
vector_icons::kSyncOffChromeRefreshIcon);
} else {
AddItemWithStringIdAndVectorIcon(
this, IDC_SHOW_SIGNIN_WHEN_PAUSED,
IDS_PROFILES_VERIFY_ACCOUNT_BUTTON,
vector_icons::kAccountCircleOffChromeRefreshIcon);
}
} else {
// All remaining errors will have the same menu item.
AddItemWithStringIdAndVectorIcon(
this, IDC_SHOW_SYNC_SETTINGS, IDS_PROFILE_ROW_SYNC_ERROR_MESSAGE,
vector_icons::kSyncProblemChromeRefreshIcon);
}
return true;
}
if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
AddItemWithStringIdAndVectorIcon(this, IDC_SHOW_SYNC_SETTINGS,
IDS_PROFILE_ROW_SYNC_IS_ON,
vector_icons::kSyncChromeRefreshIcon);
} else {
if (base::FeatureList::IsEnabled(
syncer::kReplaceSyncPromosWithSignInPromos)) {
if (!identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
AddItemWithStringIdAndVectorIcon(this, IDC_SHOW_SIGNIN,
IDS_PROFILE_MENU_SIGNIN_PROMO_BUTTON,
vector_icons::kAccountCircleIcon);
}
} else {
AddItemWithStringIdAndVectorIcon(this, IDC_TURN_ON_SYNC,
IDS_PROFILE_ROW_TURN_ON_SYNC,
vector_icons::kSyncOffChromeRefreshIcon);
}
}
return true;
}
void ProfileSubMenuModel::BuildGuestProfileRow(Profile* profile) {
AddItemWithStringIdAndVectorIcon(this, IDC_OPEN_GUEST_PROFILE,
IDS_OPEN_GUEST_PROFILE, kAccountBoxIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_OPEN_GUEST_PROFILE).value(),
AppMenuModel::kProfileOpenGuestItem);
}
void ProfileSubMenuModel::BuildCustomizeProfileRow(Profile* profile) {
if (!profile->IsIncognitoProfile() && !profile->IsGuestSession()) {
AddItemWithStringIdAndVectorIcon(this, IDC_CUSTOMIZE_CHROME,
IDS_CUSTOMIZE_CHROME,
vector_icons::kEditChromeRefreshIcon);
}
}
void ProfileSubMenuModel::BuildCloseProfileRow(Profile* profile) {
AddItemWithIcon(
IDC_CLOSE_PROFILE,
l10n_util::GetPluralStringFUTF16(IDS_CLOSE_PROFILE,
CountBrowsersFor(profile)),
ui::ImageModel::FromVectorIcon(vector_icons::kCloseChromeRefreshIcon,
ui::kColorMenuIcon, kDefaultIconSize));
}
void ProfileSubMenuModel::BuildManageGoogleAccountRow(Profile* profile) {
if (HasUnconstentedProfile(profile) && !IsSyncPaused(profile) &&
!profile->IsIncognitoProfile()) {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
const gfx::VectorIcon& manage_account_icon =
vector_icons::kGoogleGLogoMonochromeIcon;
#else
const gfx::VectorIcon& manage_account_icon =
kAccountManageChromeRefreshIcon;
#endif
AddItemWithStringIdAndVectorIcon(this, IDC_MANAGE_GOOGLE_ACCOUNT,
IDS_MANAGE_GOOGLE_ACCOUNT,
manage_account_icon);
}
}
class PasswordsAndAutofillSubMenuModel : public ui::SimpleMenuModel {
public:
explicit PasswordsAndAutofillSubMenuModel(
ui::SimpleMenuModel::Delegate* delegate);
PasswordsAndAutofillSubMenuModel(const PasswordsAndAutofillSubMenuModel&) =
delete;
PasswordsAndAutofillSubMenuModel& operator=(
const PasswordsAndAutofillSubMenuModel&) = delete;
~PasswordsAndAutofillSubMenuModel() override = default;
};
PasswordsAndAutofillSubMenuModel::PasswordsAndAutofillSubMenuModel(
ui::SimpleMenuModel::Delegate* delegate)
: SimpleMenuModel(delegate) {
AddItemWithStringIdAndVectorIcon(this, IDC_SHOW_PASSWORD_MANAGER,
IDS_VIEW_PASSWORDS,
vector_icons::kPasswordManagerIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_SHOW_PASSWORD_MANAGER).value(),
AppMenuModel::kPasswordManagerMenuItem);
AddItemWithStringIdAndVectorIcon(this, IDC_SHOW_PAYMENT_METHODS,
IDS_PAYMENT_METHOD_SUBMENU_OPTION,
kCreditCardChromeRefreshIcon);
AddItemWithStringIdAndVectorIcon(this, IDC_SHOW_ADDRESSES,
IDS_ADDRESSES_AND_MORE_SUBMENU_OPTION,
vector_icons::kLocationOnChromeRefreshIcon);
}
class FindAndEditSubMenuModel : public ui::SimpleMenuModel {
public:
explicit FindAndEditSubMenuModel(ui::SimpleMenuModel::Delegate* delegate);
FindAndEditSubMenuModel(const FindAndEditSubMenuModel&) = delete;
FindAndEditSubMenuModel& operator=(const FindAndEditSubMenuModel&) = delete;
~FindAndEditSubMenuModel() override = default;
};
FindAndEditSubMenuModel::FindAndEditSubMenuModel(
ui::SimpleMenuModel::Delegate* delegate)
: SimpleMenuModel(delegate) {
AddItemWithStringIdAndVectorIcon(this, IDC_FIND, IDS_FIND, kSearchMenuIcon);
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringIdAndVectorIcon(this, IDC_CUT, IDS_CUT, kCutMenuIcon);
AddItemWithStringIdAndVectorIcon(this, IDC_COPY, IDS_COPY, kCopyMenuIcon);
AddItemWithStringIdAndVectorIcon(this, IDC_PASTE, IDS_PASTE, kPasteMenuIcon);
}
class SaveAndShareSubMenuModel : public ui::SimpleMenuModel {
public:
SaveAndShareSubMenuModel(ui::SimpleMenuModel::Delegate* delegate,
Browser* browser);
SaveAndShareSubMenuModel(const SaveAndShareSubMenuModel&) = delete;
SaveAndShareSubMenuModel& operator=(const SaveAndShareSubMenuModel&) = delete;
~SaveAndShareSubMenuModel() override = default;
};
SaveAndShareSubMenuModel::SaveAndShareSubMenuModel(
ui::SimpleMenuModel::Delegate* delegate,
Browser* browser)
: SimpleMenuModel(delegate) {
if (media_router::MediaRouterEnabled(browser->profile())) {
AddTitle(l10n_util::GetStringUTF16(IDS_SAVE_AND_SHARE_MENU_CAST));
SetElementIdentifierAt(GetItemCount() - 1, AppMenuModel::kCastTitleItem);
AddItemWithStringIdAndVectorIcon(this, IDC_ROUTE_MEDIA,
IDS_MEDIA_ROUTER_MENU_ITEM_TITLE,
kCastChromeRefreshIcon);
AddSeparator(ui::NORMAL_SEPARATOR);
}
AddTitle(l10n_util::GetStringUTF16(IDS_SAVE_AND_SHARE_MENU_SAVE));
AddItemWithStringIdAndVectorIcon(this, IDC_SAVE_PAGE, IDS_SAVE_PAGE,
kFileSaveChromeRefreshIcon);
AddSeparator(ui::NORMAL_SEPARATOR);
if (std::u16string install_item = GetInstallPWALabel(browser);
!install_item.empty()) {
AddItemWithIcon(IDC_INSTALL_PWA, install_item, GetInstallPWAIcon(browser));
SetElementIdentifierAt(GetItemCount() - 1, AppMenuModel::kInstallAppItem);
} else if (std::u16string open_item = GetOpenPWALabel(browser);
!open_item.empty()) {
AddItemWithIcon(
IDC_OPEN_IN_PWA_WINDOW, open_item,
ui::ImageModel::FromVectorIcon(kDesktopWindowsChromeRefreshIcon,
ui::kColorMenuIcon, kDefaultIconSize));
}
AddItemWithStringIdAndVectorIcon(this, IDC_CREATE_SHORTCUT,
IDS_ADD_TO_OS_LAUNCH_SURFACE,
kDriveShortcutChromeRefreshIcon);
SetElementIdentifierAt(GetItemCount() - 1, AppMenuModel::kCreateShortcutItem);
if (!sharing_hub::SharingIsDisabledByPolicy(browser->profile()) ||
media_router::MediaRouterEnabled(browser->profile())) {
AddSeparator(ui::NORMAL_SEPARATOR);
AddTitle(l10n_util::GetStringUTF16(IDS_SAVE_AND_SHARE_MENU_SHARE));
if (!sharing_hub::SharingIsDisabledByPolicy(browser->profile())) {
AddItemWithStringIdAndVectorIcon(
this, IDC_COPY_URL, IDS_APP_MENU_COPY_LINK, kLinkChromeRefreshIcon);
AddItemWithStringIdAndVectorIcon(this, IDC_SEND_TAB_TO_SELF,
IDS_MENU_SEND_TAB_TO_SELF,
kDevicesChromeRefreshIcon);
AddItemWithStringIdAndVectorIcon(this, IDC_QRCODE_GENERATOR,
IDS_APP_MENU_CREATE_QR_CODE,
kQrCodeChromeRefreshIcon);
}
}
if (sharing_hub::DesktopScreenshotsFeatureEnabled(browser->profile())) {
AddItemWithStringIdAndVectorIcon(this, IDC_SHARING_HUB_SCREENSHOT,
IDS_SHARING_HUB_SCREENSHOT_LABEL,
kSharingHubScreenshotIcon);
}
}
bool ArePromotionsEnabled() {
PrefService* local_state = g_browser_process->local_state();
return local_state && local_state->GetBoolean(prefs::kPromotionsEnabled);
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// SetCommandIcon
void SetCommandIcon(ui::SimpleMenuModel* model,
int command_id,
const gfx::VectorIcon& vector_icon) {
auto index = model->GetIndexOfCommandId(command_id);
if (index) {
model->SetIcon(index.value(), ui::ImageModel::FromVectorIcon(
vector_icon, ui::kColorMenuIcon,
ui::SimpleMenuModel::kDefaultIconSize));
}
}
////////////////////////////////////////////////////////////////////////////////
// LogWrenchMenuAction
void LogWrenchMenuAction(AppMenuAction action_id) {
base::UmaHistogramEnumeration("WrenchMenu.MenuAction", action_id,
LIMIT_MENU_ACTION);
}
////////////////////////////////////////////////////////////////////////////////
// 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) && defined(OFFICIAL_BUILD)
int help_string_id = IDS_GET_HELP;
#else
int help_string_id = IDS_HELP_PAGE;
#endif
AddItemWithStringIdAndVectorIcon(this, IDC_ABOUT, IDS_ABOUT,
vector_icons::kInfoRefreshIcon);
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
if (whats_new::IsEnabled()) {
AddItemWithStringIdAndVectorIcon(this, IDC_CHROME_WHATS_NEW,
IDS_CHROME_WHATS_NEW, kReleaseAlertIcon);
}
#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 {
SetCommandIcon(this, IDC_HELP_PAGE_VIA_MENU, kHelpMenuIcon);
}
if (browser->profile()->GetPrefs()->GetBoolean(
prefs::kUserFeedbackAllowed)) {
AddItemWithStringIdAndVectorIcon(this, IDC_FEEDBACK, IDS_FEEDBACK,
kReportIcon);
}
}
};
////////////////////////////////////////////////////////////////////////////////
// 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).
// - Reading mode.
// - Developer tools.
// - Option to enable profiling.
void ToolsMenuModel::Build(Browser* browser) {
// Tablet mode does not have a Tab Search button, so tab organization and
// declutter are unavailable. We should not show tablet mode users these menu
// items.
bool is_tablet_mode = false;
#if BUILDFLAG(IS_CHROMEOS)
is_tablet_mode = display::Screen::Get()->InTabletMode();
#endif // BUILDFLAG(IS_CHROMEOS)
if (!is_tablet_mode) {
if (features::HasTabSearchToolbarButton()) {
AddItemWithStringIdAndVectorIcon(this, IDC_TAB_SEARCH,
IDS_TAB_SEARCH_MENU,
vector_icons::kTabSearchIcon);
}
if (base::FeatureList::IsEnabled(features::kTabOrganizationAppMenuItem) &&
TabOrganizationUtils::GetInstance()->IsEnabled(browser->profile())) {
auto* const tab_organization_service =
TabOrganizationServiceFactory::GetForProfile(browser->profile());
if (tab_organization_service) {
AddItemWithStringIdAndVectorIcon(
this, IDC_ORGANIZE_TABS, IDS_TAB_ORGANIZE_MENU, kAutoTabGroupsIcon);
}
}
if (base::FeatureList::IsEnabled(features::kTabstripDeclutter) &&
!browser->profile()->IsIncognitoProfile()) {
AddItemWithStringIdAndVectorIcon(this, IDC_DECLUTTER_TABS,
features::IsTabstripDedupeEnabled()
? IDS_DECLUTTER_MENU
: IDS_DECLUTTER_MENU_NO_DEDUPE,
kTabCloseInactiveIcon);
SetIsNewFeatureAt(
GetIndexOfCommandId(IDC_DECLUTTER_TABS).value(),
BrowserUserEducationInterface::From(browser)->MaybeShowNewBadgeFor(
features::kTabstripDeclutter));
}
}
AddItemWithStringIdAndVectorIcon(this, IDC_NAME_WINDOW, IDS_NAME_WINDOW,
kNameWindowIcon);
if (CustomizeChromePageHandler::IsSupported(
NtpCustomBackgroundServiceFactory::GetForProfile(browser->profile()),
browser->profile())) {
AddItemWithStringIdAndVectorIcon(this, IDC_SHOW_CUSTOMIZE_CHROME_SIDE_PANEL,
IDS_SHOW_CUSTOMIZE_CHROME_SIDE_PANEL,
kEditChromeRefreshIcon);
}
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringIdAndVectorIcon(this, IDC_SHOW_READING_MODE_SIDE_PANEL,
IDS_SHOW_READING_MODE_SIDE_PANEL,
kMenuBookChromeRefreshIcon);
SetElementIdentifierAt(
GetIndexOfCommandId(IDC_SHOW_READING_MODE_SIDE_PANEL).value(),
kReadingModeMenuItem);
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringIdAndVectorIcon(this, IDC_PERFORMANCE, IDS_SHOW_PERFORMANCE,
kPerformanceIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_PERFORMANCE).value(),
kPerformanceMenuItem);
if (chrome::CanOpenTaskManager()) {
AddItemWithStringIdAndVectorIcon(this, IDC_TASK_MANAGER_APP_MENU,
IDS_TASK_MANAGER, kTaskManagerIcon);
}
#if BUILDFLAG(IS_CHROMEOS)
AddItemWithStringId(IDC_TAKE_SCREENSHOT, IDS_TAKE_SCREENSHOT);
#endif
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringIdAndVectorIcon(this, IDC_DEV_TOOLS, IDS_DEV_TOOLS,
kDeveloperToolsIcon);
if (base::debug::IsProfilingSupported()) {
AddSeparator(ui::NORMAL_SEPARATOR);
AddCheckItemWithStringId(IDC_PROFILING_ENABLED, IDS_PROFILING_ENABLED);
}
if (IsChromeLabsEnabled()) {
auto* profile = browser->profile();
UpdateChromeLabsNewBadgePrefs(profile);
if (ShouldShowChromeLabsUI(profile)) {
BooleanPrefMember show_chrome_labs_item;
show_chrome_labs_item.Init(
chrome_labs_prefs::kBrowserLabsEnabledEnterprisePolicy,
profile->GetPrefs());
if (show_chrome_labs_item.GetValue()) {
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringIdAndVectorIcon(this, IDC_SHOW_CHROME_LABS,
IDS_CHROMELABS, kScienceIcon);
SetElementIdentifierAt(
GetIndexOfCommandId(IDC_SHOW_CHROME_LABS).value(),
kChromeLabsMenuItem);
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
// 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) {
AddItemWithStringIdAndVectorIcon(
this, IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS,
IDS_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS_ITEM,
vector_icons::kExtensionChromeRefreshIcon);
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 BUILDFLAG(GOOGLE_CHROME_BRANDING)
SetCommandIcon(this, IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE,
vector_icons::kGoogleChromeWebstoreIcon);
#endif
}
////////////////////////////////////////////////////////////////////////////////
// AppMenuModel
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() = default;
void AppMenuModel::Init() {
Build();
#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;
}
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_VIEW_PASSWORDS) {
browser()->profile()->GetPrefs()->SetBoolean(
password_manager::prefs::kPasswordsPrefWithNewLabelUsed, true);
}
LogMenuMetrics(command_id);
chrome::ExecuteCommand(browser_, command_id);
}
void AppMenuModel::LogSafetyHubInteractionMetrics(
safety_hub::SafetyHubModuleType sh_module,
int event_flags) {
base::UmaHistogramEnumeration("Settings.SafetyHub.Interaction",
safety_hub::SafetyHubSurfaces::kThreeDotMenu);
base::UmaHistogramEnumeration(
"Settings.SafetyHub.EntryPointInteraction",
safety_hub::SafetyHubEntryPoint::kMenuNotifications);
base::UmaHistogramEnumeration("Settings.SafetyHub.MenuNotificationClicked",
sh_module);
if (SafetyHubHatsService* hats_service =
SafetyHubHatsServiceFactory::GetForProfile(browser_->profile())) {
hats_service->SafetyHubNotificationClicked(sh_module);
}
}
void AppMenuModel::LogMenuMetrics(int command_id) {
base::TimeDelta delta = timer_.Elapsed();
switch (command_id) {
case IDC_UPGRADE_DIALOG:
LogMenuAction(MENU_ACTION_UPGRADE_DIALOG);
break;
case IDC_NEW_TAB:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.NewTab", delta);
}
LogMenuAction(MENU_ACTION_NEW_TAB);
break;
case IDC_NEW_WINDOW:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.NewWindow",
delta);
}
LogMenuAction(MENU_ACTION_NEW_WINDOW);
break;
case IDC_NEW_INCOGNITO_WINDOW:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.NewIncognitoWindow", delta);
}
LogMenuAction(MENU_ACTION_NEW_INCOGNITO_WINDOW);
break;
// Bookmarks sub menu.
case IDC_SHOW_BOOKMARK_BAR:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ShowBookmarkBar",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_BOOKMARK_BAR);
break;
case IDC_SHOW_BOOKMARK_SIDE_PANEL:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ShowBookmarkSidePanel", delta);
}
LogMenuAction(MENU_ACTION_SHOW_BOOKMARK_SIDE_PANEL);
// Close IPH for side panel menu, if shown.
BrowserUserEducationInterface::From(browser())
->NotifyFeaturePromoFeatureUsed(
feature_engagement::kIPHPowerBookmarksSidePanelFeature,
FeaturePromoFeatureUsedAction::kIgnorePromoIfPresent);
break;
case IDC_SHOW_BOOKMARK_MANAGER:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ShowBookmarkMgr",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_BOOKMARK_MANAGER);
break;
case IDC_IMPORT_SETTINGS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ImportSettings",
delta);
}
LogMenuAction(MENU_ACTION_IMPORT_SETTINGS);
break;
case IDC_BOOKMARK_THIS_TAB:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.BookmarkPage",
delta);
}
LogMenuAction(MENU_ACTION_BOOKMARK_THIS_TAB);
break;
case IDC_BOOKMARK_ALL_TABS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.BookmarkAllTabs",
delta);
}
LogMenuAction(MENU_ACTION_BOOKMARK_ALL_TABS);
break;
// Lens overlay.
case IDC_CONTENT_CONTEXT_LENS_OVERLAY:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ShowLensOverlay",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_LENS_OVERLAY);
break;
// Extensions menu.
case IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS:
// Logging the original histograms for experiment comparison purposes.
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ManageExtensions", delta);
}
LogMenuAction(MENU_ACTION_MANAGE_EXTENSIONS);
break;
case IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.VisitChromeWebStore", delta);
}
LogMenuAction(MENU_ACTION_VISIT_CHROME_WEB_STORE);
break;
case IDC_FIND_EXTENSIONS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.FindExtensions",
delta);
}
LogMenuAction(MENU_ACTION_FIND_EXTENSIONS);
break;
// Recent tabs menu.
case IDC_RESTORE_TAB:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.RestoreTab",
delta);
}
LogMenuAction(MENU_ACTION_RESTORE_TAB);
break;
case IDC_OPEN_RECENT_TAB:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.OpenRecentTab",
delta);
}
LogMenuAction(MENU_ACTION_RECENT_TAB);
break;
case IDC_RECENT_TABS_LOGIN_FOR_DEVICE_TABS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.LoginForDeviceTabs", delta);
}
LogMenuAction(MENU_ACTION_RECENT_TABS_LOGIN_FOR_DEVICE_TABS);
break;
case IDC_FIND:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.Find", delta);
}
LogMenuAction(MENU_ACTION_FIND);
break;
case IDC_PRINT:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.Print", delta);
}
LogMenuAction(MENU_ACTION_PRINT);
break;
#if BUILDFLAG(ENABLE_GLIC)
case IDC_OPEN_GLIC:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.OpenGlic",
delta);
}
LogMenuAction(MENU_ACTION_OPEN_GLIC);
break;
#endif
case IDC_SHOW_TRANSLATE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ShowTranslate",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_TRANSLATE);
break;
// Edit menu.
case IDC_CUT:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.Cut", delta);
}
LogMenuAction(MENU_ACTION_CUT);
break;
case IDC_COPY:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.Copy", delta);
}
LogMenuAction(MENU_ACTION_COPY);
break;
case IDC_PASTE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.Paste", delta);
}
LogMenuAction(MENU_ACTION_PASTE);
break;
// Save and share menu.
case IDC_SAVE_PAGE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.SavePage",
delta);
}
LogMenuAction(MENU_ACTION_SAVE_PAGE);
break;
case IDC_INSTALL_PWA:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.InstallPwa",
delta);
}
LogMenuAction(MENU_ACTION_INSTALL_PWA);
break;
case IDC_OPEN_IN_PWA_WINDOW:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.OpenInPwaWindow",
delta);
}
LogMenuAction(MENU_ACTION_OPEN_IN_PWA_WINDOW);
break;
case IDC_CREATE_SHORTCUT:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.CreateHostedApp",
delta);
}
LogMenuAction(MENU_ACTION_CREATE_HOSTED_APP);
break;
case IDC_COPY_URL:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.CopyUrl", delta);
}
LogMenuAction(MENU_ACTION_COPY_URL);
break;
case IDC_SEND_TAB_TO_SELF:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.SendToDevices",
delta);
}
LogMenuAction(MENU_ACTION_SEND_TO_DEVICES);
break;
case IDC_QRCODE_GENERATOR:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.CreateQrCode",
delta);
}
LogMenuAction(MENU_ACTION_CREATE_QR_CODE);
break;
case IDC_ROUTE_MEDIA:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.Cast", delta);
}
LogMenuAction(MENU_ACTION_CAST);
break;
// Tools menu.
case IDC_MANAGE_EXTENSIONS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ManageExtensions", delta);
}
LogMenuAction(MENU_ACTION_MANAGE_EXTENSIONS);
break;
case IDC_TASK_MANAGER:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.TaskManager",
delta);
}
LogMenuAction(MENU_ACTION_TASK_MANAGER);
break;
case IDC_CLEAR_BROWSING_DATA:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ClearBrowsingData", delta);
}
LogMenuAction(MENU_ACTION_CLEAR_BROWSING_DATA);
break;
case IDC_VIEW_SOURCE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ViewSource",
delta);
}
LogMenuAction(MENU_ACTION_VIEW_SOURCE);
break;
case IDC_DEV_TOOLS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.DevTools",
delta);
}
LogMenuAction(MENU_ACTION_DEV_TOOLS);
break;
case IDC_DEV_TOOLS_CONSOLE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.DevToolsConsole",
delta);
}
LogMenuAction(MENU_ACTION_DEV_TOOLS_CONSOLE);
break;
case IDC_DEV_TOOLS_DEVICES:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.DevToolsDevices",
delta);
}
LogMenuAction(MENU_ACTION_DEV_TOOLS_DEVICES);
break;
case IDC_PROFILING_ENABLED:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ProfilingEnabled", delta);
}
LogMenuAction(MENU_ACTION_PROFILING_ENABLED);
break;
case IDC_SHOW_CHROME_LABS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ShowChromeLabs",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_CHROME_LABS);
break;
case IDC_SHOW_HISTORY_CLUSTERS_SIDE_PANEL:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ShowHistoryClustersSidePanel", delta);
}
LogMenuAction(MENU_ACTION_SHOW_HISTORY_CLUSTER_SIDE_PANEL);
break;
case IDC_SHOW_READING_MODE_SIDE_PANEL:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ShowReadingModeSidePanel", delta);
}
LogMenuAction(MENU_ACTION_SHOW_READING_MODE_SIDE_PANEL);
// Close IPH for side panel menu, if shown.
BrowserUserEducationInterface::From(browser())
->NotifyFeaturePromoFeatureUsed(
feature_engagement::kIPHReadingModeSidePanelFeature,
FeaturePromoFeatureUsedAction::kIgnorePromoIfPresent);
break;
case IDC_SHOW_CUSTOMIZE_CHROME_SIDE_PANEL:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ShowCustomizeChromeSidePanel", delta);
}
LogMenuAction(MENU_ACTION_SHOW_CUSTOMIZE_CHROME_SIDE_PANEL);
// Close IPH for side panel menu, if shown.
BrowserUserEducationInterface::From(browser())
->NotifyFeaturePromoFeatureUsed(
feature_engagement::kIPHDesktopCustomizeChromeRefreshFeature,
FeaturePromoFeatureUsedAction::kIgnorePromoIfPresent);
break;
// Zoom menu
case IDC_ZOOM_MINUS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ZoomMinus",
delta);
LogMenuAction(MENU_ACTION_ZOOM_MINUS);
}
break;
case IDC_ZOOM_PLUS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ZoomPlus",
delta);
LogMenuAction(MENU_ACTION_ZOOM_PLUS);
}
break;
case IDC_FULLSCREEN:
base::RecordAction(UserMetricsAction("EnterFullScreenWithWrenchMenu"));
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.EnterFullScreen",
delta);
}
LogMenuAction(MENU_ACTION_FULLSCREEN);
break;
case IDC_SHOW_HISTORY:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ShowHistory",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_HISTORY);
break;
case IDC_SHOW_DOWNLOADS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ShowDownloads",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_DOWNLOADS);
base::UmaHistogramEnumeration(
"Download.OpenDownloadsFromMenu.PerProfileType",
profile_metrics::GetBrowserProfileType(browser_->profile()));
break;
case IDC_OPTIONS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("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_) {
base::UmaHistogramMediumTimes("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_) {
base::UmaHistogramMediumTimes("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_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.BetaForum",
delta);
}
LogMenuAction(MENU_ACTION_BETA_FORUM);
break;
case IDC_FEEDBACK:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.Feedback",
delta);
}
LogMenuAction(MENU_ACTION_FEEDBACK);
break;
case IDC_CHROME_TIPS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ChromeTips",
delta);
}
LogMenuAction(MENU_ACTION_CHROME_TIPS);
break;
case IDC_CHROME_WHATS_NEW:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ChromeWhatsNew",
delta);
}
LogMenuAction(MENU_ACTION_CHROME_WHATS_NEW);
break;
#endif
case IDC_TOGGLE_REQUEST_TABLET_SITE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.RequestTabletSite", delta);
}
LogMenuAction(MENU_ACTION_TOGGLE_REQUEST_TABLET_SITE);
break;
case IDC_EXIT:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.Exit", delta);
}
LogMenuAction(MENU_ACTION_EXIT);
break;
// Hosted App menu.
case IDC_OPEN_IN_CHROME:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.OpenInChrome",
delta);
}
LogMenuAction(MENU_ACTION_OPEN_IN_CHROME);
break;
case IDC_WEB_APP_MENU_APP_INFO:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.AppInfo", delta);
}
LogMenuAction(MENU_ACTION_APP_INFO);
break;
case IDC_VIEW_PASSWORDS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.PasswordManager",
delta);
}
LogMenuAction(MENU_ACTION_PASSWORD_MANAGER);
break;
// Profile submenu.
#if !BUILDFLAG(IS_CHROMEOS)
case IDC_CUSTOMIZE_CHROME:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.CustomizeChrome",
delta);
}
LogMenuAction(MENU_ACTION_CUSTOMIZE_CHROME);
break;
case IDC_CLOSE_PROFILE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.CloseProfile",
delta);
}
LogMenuAction(MENU_ACTION_CLOSE_PROFILE);
break;
case IDC_MANAGE_GOOGLE_ACCOUNT:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ManageGoogleAccount", delta);
}
LogMenuAction(MENU_ACTION_MANAGE_GOOGLE_ACCOUNT);
break;
case IDC_SHOW_SYNC_SETTINGS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ShowSyncSettings", delta);
}
LogMenuAction(MENU_SHOW_SYNC_SETTINGS);
break;
case IDC_SHOW_SIGNIN:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ShowSignin",
delta);
}
LogMenuAction(MENU_SHOW_SIGNIN);
break;
case IDC_TURN_ON_SYNC:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ShowTurnOnSync",
delta);
}
LogMenuAction(MENU_TURN_ON_SYNC);
break;
case IDC_SHOW_SIGNIN_WHEN_PAUSED:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ShowSigninWhenPaused", delta);
}
LogMenuAction(MENU_SHOW_SIGNIN_WHEN_PAUSED);
break;
case IDC_OPEN_GUEST_PROFILE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.OpenGuestProfile", delta);
}
LogMenuAction(MENU_ACTION_OPEN_GUEST_PROFILE);
break;
case IDC_ADD_NEW_PROFILE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.AddNewProfile",
delta);
}
LogMenuAction(MENU_ACTION_ADD_NEW_PROFILE);
break;
case IDC_MANAGE_CHROME_PROFILES:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ManageChromeProfiles", delta);
}
LogMenuAction(MENU_ACTION_MANAGE_CHROME_PROFILES);
break;
#endif
// Reading list submenu.
case IDC_READING_LIST_MENU_ADD_TAB:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ReadingListAddTab", delta);
}
LogMenuAction(MENU_ACTION_READING_LIST_ADD_TAB);
break;
case IDC_READING_LIST_MENU_SHOW_UI:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ReadingListShowUi", delta);
}
LogMenuAction(MENU_ACTION_READING_LIST_SHOW_UI);
break;
// Password autofill submenu.
case IDC_SHOW_PASSWORD_MANAGER:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ShowPasswordManager", delta);
}
LogMenuAction(MENU_ACTION_SHOW_PASSWORD_MANAGER);
break;
case IDC_SHOW_PAYMENT_METHODS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ShowPaymentMethods", delta);
}
LogMenuAction(MENU_ACTION_SHOW_PAYMENT_METHODS);
break;
case IDC_SHOW_ADDRESSES:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.ShowAddresses",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_ADDRESSES);
break;
case IDC_PERFORMANCE:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.ShowPerformanceSettings", delta);
}
LogMenuAction(MENU_ACTION_SHOW_PERFORMANCE_SETTINGS);
break;
case IDC_SET_BROWSER_AS_DEFAULT:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.SetBrowserAsDefault", delta);
}
LogMenuAction(MENU_ACTION_SET_BROWSER_AS_DEFAULT);
break;
case IDC_SAFETY_HUB_SHOW_PASSWORD_CHECKUP:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.SafetyHubNotificationPasswordCheck",
delta);
}
LogMenuAction(MENU_ACTION_SAFETY_HUB_SHOW_PASSWORD_CHECKUP);
break;
case IDC_OPEN_SAFETY_HUB:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.SafetyHubNotificationOpenSafetyHub",
delta);
}
LogMenuAction(MENU_ACTION_SHOW_SAFETY_HUB);
break;
case IDC_DECLUTTER_TABS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("WrenchMenu.TimeToAction.DeclutterTabs",
delta);
}
tabs::TabDeclutterController::EmitEntryPointHistogram(
tab_search::mojom::TabDeclutterEntryPoint::kAppMenu);
LogMenuAction(MENU_ACTION_DECLUTTER_TABS);
break;
case IDC_SAFETY_HUB_MANAGE_EXTENSIONS:
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.SafetyHubNotificationManageExtensions",
delta);
}
LogMenuAction(MENU_ACTION_SAFETY_HUB_MANAGE_EXTENSIONS);
break;
default: {
if (IsOtherProfileCommand(command_id)) {
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes(
"WrenchMenu.TimeToAction.SwitchToAnotherProfile", delta);
}
LogMenuAction(MENU_ACTION_SWITCH_TO_ANOTHER_PROFILE);
}
break;
}
}
if (!uma_action_recorded_) {
base::UmaHistogramMediumTimes("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::IsCommandIdAlerted(int command_id) const {
if (command_id == IDC_VIEW_PASSWORDS ||
command_id == IDC_SHOW_PASSWORD_MANAGER) {
return alert_item_ == AlertMenuItem::kPasswordManager;
}
return false;
}
bool AppMenuModel::GetAcceleratorForCommandId(
int command_id,
ui::Accelerator* accelerator) const {
return provider_->GetAcceleratorForCommandId(command_id, accelerator);
}
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());
if (app_menu_icon_controller_ &&
app_menu_icon_controller_->GetTypeAndSeverity().type ==
AppMenuIconController::IconType::UPGRADE_NOTIFICATION) {
AddSeparator(ui::SPACING_SEPARATOR);
const auto update_icon = ui::ImageModel::FromVectorIcon(
kBrowserToolsUpdateChromeRefreshIcon,
ui::kColorMenuIconOnEmphasizedBackground, kDefaultIconSize);
if (browser_defaults::kShowUpgradeMenuItem) {
AddItemWithIcon(IDC_UPGRADE_DIALOG, GetUpgradeDialogTitleText(),
update_icon);
AddSeparator(ui::SPACING_SEPARATOR);
}
}
if (AddSafetyHubMenuItem() || AddGlobalErrorMenuItems() ||
AddDefaultBrowserMenuItems()) {
AddSeparator(ui::NORMAL_SEPARATOR);
}
AddItemWithStringIdAndVectorIcon(
this, IDC_NEW_TAB,
browser_->profile()->IsIncognitoProfile() &&
!browser_->profile()->IsGuestSession()
? IDS_NEW_INCOGNITO_TAB
: IDS_NEW_TAB,
kNewTabRefreshIcon);
AddItemWithStringIdAndVectorIcon(this, IDC_NEW_WINDOW, IDS_NEW_WINDOW,
kNewWindowIcon);
// 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()) {
AddItemWithStringIdAndVectorIcon(this, IDC_NEW_INCOGNITO_WINDOW,
IDS_NEW_INCOGNITO_WINDOW,
kIncognitoRefreshMenuIcon);
SetElementIdentifierAt(
GetIndexOfCommandId(IDC_NEW_INCOGNITO_WINDOW).value(),
kIncognitoMenuItem);
}
AddSeparator(ui::NORMAL_SEPARATOR);
#if !BUILDFLAG(IS_CHROMEOS)
sub_menus_.push_back(std::make_unique<ProfileSubMenuModel>(
this, browser()->profile(), browser()->window()->GetColorProvider()));
auto* const profile_submenu_model =
static_cast<ProfileSubMenuModel*>(sub_menus_.back().get());
AddSubMenu(IDC_PROFILE_MENU_IN_APP_MENU,
profile_submenu_model->profile_name(), profile_submenu_model);
SetIcon(GetIndexOfCommandId(IDC_PROFILE_MENU_IN_APP_MENU).value(),
profile_submenu_model->avatar_image_model());
SetElementIdentifierAt(
GetIndexOfCommandId(IDC_PROFILE_MENU_IN_APP_MENU).value(),
kProfileMenuItem);
AddSeparator(ui::SPACING_SEPARATOR);
#endif
if (!browser_->profile()->IsGuestSession()) {
sub_menus_.push_back(
std::make_unique<PasswordsAndAutofillSubMenuModel>(this));
AddSubMenuWithStringIdAndVectorIcon(
this, IDC_PASSWORDS_AND_AUTOFILL_MENU, IDS_PASSWORDS_AND_AUTOFILL_MENU,
sub_menus_.back().get(), vector_icons::kPasswordManagerIcon);
SetElementIdentifierAt(
GetIndexOfCommandId(IDC_PASSWORDS_AND_AUTOFILL_MENU).value(),
kPasswordAndAutofillMenuItem);
}
if (!browser_->profile()->IsOffTheRecord()) {
auto recent_tabs_sub_menu =
std::make_unique<RecentTabsSubMenuModel>(provider_, browser_);
recent_tabs_sub_menu->RegisterLogMenuMetricsCallback(base::BindRepeating(
&AppMenuModel::LogMenuMetrics, base::Unretained(this)));
sub_menus_.push_back(std::move(recent_tabs_sub_menu));
AddSubMenuWithStringIdAndVectorIcon(this, IDC_RECENT_TABS_MENU,
IDS_HISTORY_MENU,
sub_menus_.back().get(), kHistoryIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_RECENT_TABS_MENU).value(),
kHistoryMenuItem);
}
AddItemWithStringIdAndVectorIcon(this, IDC_SHOW_DOWNLOADS, IDS_SHOW_DOWNLOADS,
kDownloadMenuIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_SHOW_DOWNLOADS).value(),
kDownloadsMenuItem);
if (!browser_->profile()->IsGuestSession()) {
bookmark_sub_menu_model_ =
std::make_unique<BookmarkSubMenuModel>(this, browser_);
AddSubMenuWithStringIdAndVectorIcon(
this, IDC_BOOKMARKS_MENU, IDS_BOOKMARKS_AND_LISTS_MENU,
bookmark_sub_menu_model_.get(), kBookmarksListsMenuIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_BOOKMARKS_MENU).value(),
kBookmarksMenuItem);
}
if (browser_->profile()->IsRegularProfile()) {
auto saved_tab_groups_model = std::make_unique<ui::SimpleMenuModel>(this);
sub_menus_.push_back(std::move(saved_tab_groups_model));
AddSubMenuWithStringIdAndVectorIcon(
this, IDC_SAVED_TAB_GROUPS_MENU, IDS_SAVED_TAB_GROUPS_MENU,
sub_menus_.back().get(), kSavedTabGroupBarEverythingIcon);
SetElementIdentifierAt(
GetIndexOfCommandId(IDC_SAVED_TAB_GROUPS_MENU).value(),
kTabGroupsMenuItem);
}
// Extensions sub menu.
if (ArePromotionsEnabled() &&
base::FeatureList::IsEnabled(features::kExtensionsCollapseMainMenu) &&
!extensions::ui_util::HasManageableExtensions(browser_->profile())) {
AddItemWithStringIdAndVectorIcon(this, IDC_FIND_EXTENSIONS,
IDS_FIND_EXTENSIONS,
vector_icons::kExtensionChromeRefreshIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_FIND_EXTENSIONS).value(),
ExtensionsMenuModel::kVisitChromeWebStoreMenuItem);
} else {
sub_menus_.push_back(std::make_unique<ExtensionsMenuModel>(this, browser_));
AddSubMenuWithStringIdAndVectorIcon(
this, IDC_EXTENSIONS_SUBMENU, IDS_EXTENSIONS_SUBMENU,
sub_menus_.back().get(), vector_icons::kExtensionChromeRefreshIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU).value(),
kExtensionsMenuItem);
}
AddItemWithStringIdAndVectorIcon(this, IDC_CLEAR_BROWSING_DATA,
IDS_CLEAR_BROWSING_DATA,
kTrashCanRefreshIcon);
AddSeparator(ui::NORMAL_SEPARATOR);
CreateZoomMenu();
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringIdAndVectorIcon(this, IDC_PRINT, IDS_PRINT, kPrintMenuIcon);
#if BUILDFLAG(ENABLE_GLIC)
if (glic::GlicEnabling::IsEnabledForProfile(browser_->profile())) {
AddItemWithStringIdAndVectorIcon(this, IDC_OPEN_GLIC,
IDS_GLIC_THREE_DOT_MENU_ITEM,
glic::GlicVectorIconManager::GetVectorIcon(
IDR_GLIC_BUTTON_VECTOR_ICON));
SetIsNewFeatureAt(
GetIndexOfCommandId(IDC_OPEN_GLIC).value(),
BrowserUserEducationInterface::From(browser())->MaybeShowNewBadgeFor(
features::kGlicAppMenuNewBadge));
}
#endif
if (browser()
->GetFeatures()
.lens_overlay_entry_point_controller()
->IsEnabled()) {
const gfx::VectorIcon& icon =
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
vector_icons::kGoogleLensMonochromeLogoIcon;
#else
vector_icons::kSearchChromeRefreshIcon;
#endif
AddItemWithStringIdAndVectorIcon(
this, IDC_CONTENT_CONTEXT_LENS_OVERLAY,
lens::GetLensOverlayEntrypointLabelAltIds(IDS_SHOW_LENS_OVERLAY), icon);
const int lens_command_index =
GetIndexOfCommandId(IDC_CONTENT_CONTEXT_LENS_OVERLAY).value();
SetElementIdentifierAt(lens_command_index, kShowLensOverlay);
SetIsNewFeatureAt(
lens_command_index,
BrowserUserEducationInterface::From(browser())->MaybeShowNewBadgeFor(
lens::features::kLensOverlay));
}
AddItemWithStringIdAndVectorIcon(this, IDC_SHOW_TRANSLATE, IDS_SHOW_TRANSLATE,
kTranslateIcon);
CreateFindAndEditSubMenu();
sub_menus_.push_back(
std::make_unique<SaveAndShareSubMenuModel>(this, browser_));
int string_id = media_router::MediaRouterEnabled(browser()->profile())
? IDS_CAST_SAVE_AND_SHARE_MENU
: IDS_SAVE_AND_SHARE_MENU;
AddSubMenuWithStringIdAndVectorIcon(this, IDC_SAVE_AND_SHARE_MENU, string_id,
sub_menus_.back().get(),
kFileSaveChromeRefreshIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_SAVE_AND_SHARE_MENU).value(),
kSaveAndShareMenuItem);
#if BUILDFLAG(IS_CHROMEOS)
// Always show this option if we're in tablet mode on Chrome OS.
if (display::Screen::Get()->InTabletMode()) {
AddItemWithStringIdAndVectorIcon(this, IDC_TOGGLE_REQUEST_TABLET_SITE,
IDS_TOGGLE_REQUEST_TABLET_SITE,
chrome::IsRequestingTabletSite(browser_)
? kRequestMobileSiteCheckedIcon
: kRequestMobileSiteUncheckedIcon);
}
#endif
sub_menus_.push_back(std::make_unique<ToolsMenuModel>(this, browser_));
AddSubMenuWithStringIdAndVectorIcon(
this, IDC_MORE_TOOLS_MENU, IDS_MORE_TOOLS_MENU, sub_menus_.back().get(),
kMoreToolsMenuIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_MORE_TOOLS_MENU).value(),
kMoreToolsMenuItem);
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_));
AddSubMenuWithStringIdAndVectorIcon(this, IDC_HELP_MENU, IDS_HELP_MENU,
sub_menus_.back().get(), kHelpMenuIcon);
#else
#if BUILDFLAG(IS_CHROMEOS)
AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT));
#else
AddItemWithStringIdAndVectorIcon(this, IDC_ABOUT, IDS_ABOUT,
vector_icons::kInfoRefreshIcon);
#endif
#endif
AddItemWithStringIdAndVectorIcon(this, IDC_OPTIONS, IDS_SETTINGS,
kSettingsMenuIcon);
if (browser_defaults::kShowExitMenuItem) {
AddItemWithStringIdAndVectorIcon(this, IDC_EXIT, IDS_EXIT, kExitMenuIcon);
}
// On Chrome OS, similar UI is displayed in the system tray menu, instead of
// this menu.
#if !BUILDFLAG(IS_CHROMEOS)
if (ShouldDisplayManagedUi(browser_->profile())) {
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithIcon(
IDC_SHOW_MANAGEMENT_PAGE,
GetManagedUiMenuItemLabel(browser_->profile()),
ui::ImageModel::FromVectorIcon(GetManagedUiIcon(browser_->profile()),
ui::kColorMenuIcon, kDefaultIconSize));
SetAccessibleNameAt(GetIndexOfCommandId(IDC_SHOW_MANAGEMENT_PAGE).value(),
GetManagedUiMenuItemTooltip(browser_->profile()));
}
#endif // !BUILDFLAG(IS_CHROMEOS)
uma_action_recorded_ = false;
}
void AppMenuModel::CreateCutCopyPasteMenu() {
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::CreateFindAndEditSubMenu() {
sub_menus_.push_back(std::make_unique<FindAndEditSubMenuModel>(this));
AddSubMenuWithStringIdAndVectorIcon(this, IDC_FIND_AND_EDIT_MENU,
IDS_FIND_AND_EDIT_MENU,
sub_menus_.back().get(), kSearchMenuIcon);
}
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());
SetCommandIcon(this, IDC_ZOOM_MENU, kZoomInIcon);
}
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 (GlobalError* 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;
}
bool AppMenuModel::AddDefaultBrowserMenuItems() {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
if (browser_->profile()->IsIncognitoProfile() ||
browser_->profile()->IsGuestSession()) {
return false;
}
if ((app_menu_icon_controller_ &&
app_menu_icon_controller_->GetTypeAndSeverity().type ==
AppMenuIconController::IconType::DEFAULT_BROWSER_PROMPT) ||
(DefaultBrowserPromptManager::GetInstance()->show_app_menu_item())) {
AddItemWithIcon(
IDC_SET_BROWSER_AS_DEFAULT,
l10n_util::GetStringUTF16(IDS_SET_BROWSER_AS_DEFAULT_MENU_ITEM),
ui::ImageModel::FromVectorIcon(omnibox::kProductChromeRefreshIcon,
ui::kColorMenuIcon, kDefaultIconSize));
SetElementIdentifierAt(GetItemCount() - 1,
AppMenuModel::kSetBrowserAsDefaultMenuItem);
return true;
}
#endif
return false;
}
bool AppMenuModel::AddSafetyHubMenuItem() {
auto* safety_hub_menu_notification_service =
SafetyHubMenuNotificationServiceFactory::GetForProfile(
browser_->profile());
if (!safety_hub_menu_notification_service) {
return false;
}
safety_hub_menu_notification_service->MaybeTriggerControlSurvey();
std::optional<MenuNotificationEntry> notification =
safety_hub_menu_notification_service->GetNotificationToShow();
if (!notification.has_value()) {
return false;
}
base::UmaHistogramEnumeration("Settings.SafetyHub.Impression",
safety_hub::SafetyHubSurfaces::kThreeDotMenu);
base::UmaHistogramEnumeration(
"Settings.SafetyHub.EntryPointImpression",
safety_hub::SafetyHubEntryPoint::kMenuNotifications);
base::UmaHistogramEnumeration("Settings.SafetyHub.MenuNotificationImpression",
notification->module);
const auto safety_hub_icon = ui::ImageModel::FromVectorIcon(
kSecurityIcon, ui::kColorMenuIcon, kDefaultIconSize);
AddItemWithIcon(notification->command, notification->label, safety_hub_icon);
SetExecuteCallbackAt(
GetIndexOfCommandId(notification->command).value(),
base::BindRepeating(&AppMenuModel::LogSafetyHubInteractionMetrics,
base::Unretained(this), notification->module));
return true;
}
#if BUILDFLAG(IS_CHROMEOS)
void AppMenuModel::UpdateSettingsItemState() {
bool is_disabled =
policy::SystemFeaturesDisableListPolicyHandler::IsSystemFeatureDisabled(
policy::SystemFeature::kBrowserSettings,
g_browser_process->local_state());
std::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)