| // Copyright 2020 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/apps/app_service/menu_util.h" |
| |
| #include <string_view> |
| #include <utility> |
| |
| #include "ash/public/cpp/app_menu_constants.h" |
| #include "base/check.h" |
| #include "base/check_op.h" |
| #include "base/notreached.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" |
| #include "chrome/browser/prefs/incognito_mode_prefs.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/policy/core/common/policy_pref_names.h" |
| #include "components/services/app_service/public/cpp/app_registry_cache.h" |
| #include "components/services/app_service/public/cpp/app_update.h" |
| #include "content/public/common/content_features.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/models/image_model.h" |
| #include "ui/color/color_id.h" |
| #include "ui/gfx/image/image_skia.h" |
| |
| namespace { |
| |
| const int kInvalidRadioGroupId = -1; |
| const int kGroupId = 1; |
| |
| apps::MenuItemPtr CreateRadioItem(uint32_t command_id, |
| uint32_t string_id, |
| int group_id) { |
| apps::MenuItemPtr menu_item = |
| std::make_unique<apps::MenuItem>(apps::MenuItemType::kRadio, command_id); |
| menu_item->string_id = string_id; |
| menu_item->radio_group_id = group_id; |
| return menu_item; |
| } |
| |
| void PopulateRadioItemFromMenuItems( |
| const std::vector<apps::MenuItemPtr>& menu_items, |
| ui::SimpleMenuModel* model) { |
| for (const auto& item : menu_items) { |
| DCHECK_EQ(apps::MenuItemType::kRadio, item->type); |
| model->AddRadioItem(item->command_id, |
| l10n_util::GetStringUTF16(item->string_id), |
| item->radio_group_id); |
| } |
| } |
| |
| } // namespace |
| |
| namespace apps { |
| |
| DEFINE_ELEMENT_IDENTIFIER_VALUE(kLaunchNewMenuItem); |
| |
| void AddCommandItem(uint32_t command_id, |
| uint32_t string_id, |
| MenuItems& menu_items) { |
| MenuItemPtr menu_item = |
| std::make_unique<MenuItem>(MenuItemType::kCommand, command_id); |
| menu_item->string_id = string_id; |
| menu_item->radio_group_id = kInvalidRadioGroupId; |
| menu_items.items.push_back(std::move(menu_item)); |
| } |
| |
| void AddSeparator(ui::MenuSeparatorType separator_type, MenuItems& menu_items) { |
| MenuItemPtr menu_item = |
| std::make_unique<MenuItem>(MenuItemType::kSeparator, separator_type); |
| menu_items.items.push_back(std::move(menu_item)); |
| } |
| |
| void AddShortcutCommandItem(int command_id, |
| const std::string& shortcut_id, |
| const std::string& label, |
| const gfx::ImageSkia& icon, |
| MenuItems& menu_items) { |
| MenuItemPtr menu_item = |
| std::make_unique<MenuItem>(MenuItemType::kPublisherCommand, command_id); |
| menu_item->shortcut_id = shortcut_id; |
| menu_item->label = label; |
| menu_item->image = icon; |
| menu_items.items.push_back(std::move(menu_item)); |
| } |
| |
| void CreateOpenNewSubmenu(uint32_t string_id, MenuItems& menu_items) { |
| MenuItemPtr menu_item = |
| std::make_unique<MenuItem>(MenuItemType::kSubmenu, ash::LAUNCH_NEW); |
| menu_item->string_id = string_id; |
| |
| menu_item->submenu.push_back(CreateRadioItem( |
| ash::USE_LAUNCH_TYPE_REGULAR, |
| StringIdForUseLaunchTypeCommand(ash::USE_LAUNCH_TYPE_REGULAR), kGroupId)); |
| menu_item->submenu.push_back(CreateRadioItem( |
| ash::USE_LAUNCH_TYPE_WINDOW, |
| StringIdForUseLaunchTypeCommand(ash::USE_LAUNCH_TYPE_WINDOW), kGroupId)); |
| if (base::FeatureList::IsEnabled(blink::features::kDesktopPWAsTabStrip) && |
| base::FeatureList::IsEnabled(features::kDesktopPWAsTabStripSettings)) { |
| menu_item->submenu.push_back(CreateRadioItem( |
| ash::USE_LAUNCH_TYPE_TABBED_WINDOW, |
| StringIdForUseLaunchTypeCommand(ash::USE_LAUNCH_TYPE_TABBED_WINDOW), |
| kGroupId)); |
| } |
| |
| menu_item->radio_group_id = kInvalidRadioGroupId; |
| |
| menu_items.items.push_back(std::move(menu_item)); |
| } |
| |
| bool ShouldAddOpenItem(const std::string& app_id, |
| MenuType menu_type, |
| Profile* profile) { |
| if (menu_type != MenuType::kShelf) { |
| return false; |
| } |
| |
| return !apps::AppServiceProxyFactory::GetForProfile(profile) |
| ->InstanceRegistry() |
| .ContainsAppId(app_id); |
| } |
| |
| bool ShouldAddCloseItem(const std::string& app_id, |
| MenuType menu_type, |
| Profile* profile) { |
| if (menu_type != MenuType::kShelf) { |
| return false; |
| } |
| |
| bool can_close = true; |
| apps::AppServiceProxyFactory::GetForProfile(profile) |
| ->AppRegistryCache() |
| .ForOneApp(app_id, [&can_close](const apps::AppUpdate& update) { |
| can_close = update.AllowClose().value_or(true); |
| }); |
| |
| return can_close && apps::AppServiceProxyFactory::GetForProfile(profile) |
| ->InstanceRegistry() |
| .ContainsAppId(app_id); |
| } |
| |
| void PopulateLaunchNewItemFromMenuItem(const MenuItemPtr& menu_item, |
| ui::SimpleMenuModel* model, |
| ui::SimpleMenuModel* submenu, |
| int* launch_new_string_id) { |
| DCHECK_EQ(menu_item->command_id, ash::LAUNCH_NEW); |
| |
| if (launch_new_string_id) { |
| *launch_new_string_id = menu_item->string_id; |
| } |
| |
| switch (menu_item->type) { |
| case apps::MenuItemType::kCommand: { |
| model->AddItemWithStringId(menu_item->command_id, menu_item->string_id); |
| model->SetElementIdentifierAt( |
| model->GetIndexOfCommandId(menu_item->command_id).value(), |
| kLaunchNewMenuItem); |
| break; |
| } |
| case apps::MenuItemType::kSubmenu: |
| if (!menu_item->submenu.empty()) { |
| PopulateRadioItemFromMenuItems(menu_item->submenu, submenu); |
| model->AddActionableSubMenu( |
| menu_item->command_id, |
| l10n_util::GetStringUTF16(menu_item->string_id), submenu); |
| } |
| break; |
| case apps::MenuItemType::kRadio: |
| case apps::MenuItemType::kSeparator: |
| case apps::MenuItemType::kPublisherCommand: |
| NOTREACHED(); |
| } |
| } |
| |
| void PopulateItemFromMenuItem(const apps::MenuItemPtr& item, |
| ui::SimpleMenuModel* model, |
| apps::AppShortcutItems* arc_shortcut_items) { |
| switch (item->type) { |
| case apps::MenuItemType::kSeparator: |
| model->AddSeparator(static_cast<ui::MenuSeparatorType>(item->command_id)); |
| break; |
| case apps::MenuItemType::kPublisherCommand: { |
| model->AddItemWithIcon(item->command_id, base::UTF8ToUTF16(item->label), |
| ui::ImageModel::FromImageSkia(item->image)); |
| apps::AppShortcutItem arc_shortcut_item; |
| arc_shortcut_item.shortcut_id = item->shortcut_id; |
| arc_shortcut_items->push_back(arc_shortcut_item); |
| break; |
| } |
| case apps::MenuItemType::kCommand: |
| case apps::MenuItemType::kRadio: |
| case apps::MenuItemType::kSubmenu: |
| NOTREACHED(); |
| } |
| } |
| |
| std::string_view MenuTypeToString(MenuType menu_type) { |
| switch (menu_type) { |
| case MenuType::kShelf: |
| return "shelf"; |
| case MenuType::kAppList: |
| return "applist"; |
| } |
| } |
| |
| MenuType MenuTypeFromString(std::string_view menu_type) { |
| if (base::EqualsCaseInsensitiveASCII(menu_type, "shelf")) { |
| return MenuType::kShelf; |
| } |
| if (base::EqualsCaseInsensitiveASCII(menu_type, "applist")) { |
| return MenuType::kAppList; |
| } |
| return MenuType::kShelf; |
| } |
| |
| MenuItems CreateBrowserMenuItems(const Profile* profile) { |
| DCHECK(profile); |
| MenuItems menu_items; |
| |
| // "Normal" windows are not allowed when incognito is enforced. |
| if (IncognitoModePrefs::GetAvailability(profile->GetPrefs()) != |
| policy::IncognitoModeAvailability::kForced) { |
| AddCommandItem(ash::APP_CONTEXT_MENU_NEW_WINDOW, IDS_APP_LIST_NEW_WINDOW, |
| menu_items); |
| } |
| |
| // Incognito windows are not allowed when incognito is disabled. |
| if (!profile->IsOffTheRecord() && |
| IncognitoModePrefs::GetAvailability(profile->GetPrefs()) != |
| policy::IncognitoModeAvailability::kDisabled) { |
| AddCommandItem(ash::APP_CONTEXT_MENU_NEW_INCOGNITO_WINDOW, |
| IDS_APP_LIST_NEW_INCOGNITO_WINDOW, menu_items); |
| } |
| |
| AddCommandItem(ash::SHOW_APP_INFO, IDS_APP_CONTEXT_MENU_SHOW_INFO, |
| menu_items); |
| |
| return menu_items; |
| } |
| |
| ui::ColorId GetColorIdForMenuItemIcon() { |
| #if BUILDFLAG(IS_CHROMEOS) |
| return ui::kColorAshSystemUIMenuIcon; |
| #else |
| return ui::kColorMenuIcon; |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| } |
| |
| uint32_t StringIdForUseLaunchTypeCommand(uint32_t command_id) { |
| DCHECK(command_id >= ash::USE_LAUNCH_TYPE_COMMAND_START && |
| command_id < ash::USE_LAUNCH_TYPE_COMMAND_END); |
| switch (command_id) { |
| case ash::USE_LAUNCH_TYPE_REGULAR: |
| return IDS_APP_LIST_CONTEXT_MENU_NEW_TAB; |
| case ash::USE_LAUNCH_TYPE_WINDOW: |
| return IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW; |
| case ash::USE_LAUNCH_TYPE_TABBED_WINDOW: |
| return IDS_APP_LIST_CONTEXT_MENU_NEW_TABBED_WINDOW; |
| case ash::DEPRECATED_USE_LAUNCH_TYPE_PINNED: |
| case ash::DEPRECATED_USE_LAUNCH_TYPE_FULLSCREEN: |
| [[fallthrough]]; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| } // namespace apps |