blob: 5038709a066554e8ce83663a15280067d239f50d [file] [log] [blame]
// 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/ui/ash/shelf/app_service/app_service_shelf_context_menu.h"
#include "ash/public/cpp/app_menu_constants.h"
#include "ash/public/cpp/new_window_delegate.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.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/apps/app_service/extension_apps_utils.h"
#include "chrome/browser/apps/app_service/menu_util.h"
#include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ash/app_list/extension_app_utils.h"
#include "chrome/browser/ash/app_restore/full_restore_service.h"
#include "chrome/browser/ash/borealis/borealis_window_manager.h"
#include "chrome/browser/ash/crosapi/browser_manager.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chrome/browser/ash/crostini/crostini_util.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
#include "chrome/browser/ash/guest_os/guest_os_shelf_utils.h"
#include "chrome/browser/ash/guest_os/guest_os_terminal.h"
#include "chrome/browser/ash/plugin_vm/plugin_vm_manager.h"
#include "chrome/browser/ash/plugin_vm/plugin_vm_manager_factory.h"
#include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
#include "chrome/browser/extensions/context_menu_matcher.h"
#include "chrome/browser/extensions/launch_util.h"
#include "chrome/browser/extensions/menu_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
#include "chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.h"
#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/views/crostini/crostini_app_restart_dialog.h"
#include "chrome/browser/ui/webui/settings/ash/app_management/app_management_uma.h"
#include "chrome/grit/generated_resources.h"
#include "components/app_constants/constants.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "content/public/browser/context_menu_params.h"
#include "extensions/browser/extension_prefs.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/display/scoped_display_for_new_windows.h"
#include "ui/gfx/vector_icon_types.h"
namespace {
bool MenuItemHasLauncherContext(const extensions::MenuItem* item) {
return item->contexts().Contains(extensions::MenuItem::LAUNCHER);
}
apps::WindowMode ConvertLaunchTypeCommandToWindowMode(int command_id) {
switch (command_id) {
case ash::USE_LAUNCH_TYPE_REGULAR:
return apps::WindowMode::kBrowser;
case ash::USE_LAUNCH_TYPE_WINDOW:
return apps::WindowMode::kWindow;
case ash::USE_LAUNCH_TYPE_TABBED_WINDOW:
return apps::WindowMode::kTabbedWindow;
default:
return apps::WindowMode::kUnknown;
}
}
extensions::LaunchType ConvertLaunchTypeCommandToExtensionLaunchType(
int command_id) {
switch (command_id) {
case ash::USE_LAUNCH_TYPE_REGULAR:
return extensions::LAUNCH_TYPE_REGULAR;
case ash::USE_LAUNCH_TYPE_WINDOW:
return extensions::LAUNCH_TYPE_WINDOW;
case ash::USE_LAUNCH_TYPE_TABBED_WINDOW:
// Not supported for extensions.
[[fallthrough]];
case ash::DEPRECATED_USE_LAUNCH_TYPE_PINNED:
case ash::DEPRECATED_USE_LAUNCH_TYPE_FULLSCREEN:
[[fallthrough]];
default:
NOTREACHED();
return extensions::LAUNCH_TYPE_INVALID;
}
}
std::string GetAppId(const ash::ShelfID& shelf_id) {
// Remove the ARC shelf group prefix.
const arc::ArcAppShelfId arc_shelf_id =
arc::ArcAppShelfId::FromString(shelf_id.app_id);
if (arc_shelf_id.valid())
return arc_shelf_id.app_id();
return shelf_id.app_id;
}
} // namespace
AppServiceShelfContextMenu::AppServiceShelfContextMenu(
ChromeShelfController* controller,
const ash::ShelfItem* item,
int64_t display_id)
: ShelfContextMenu(controller, item, display_id) {
if (guest_os::IsUnregisteredCrostiniShelfAppId(item->id.app_id) ||
borealis::BorealisWindowManager::IsAnonymousAppId(item->id.app_id)) {
// Sometimes GuestOS runs applications that are not registered with the apps
// service. These "anonymous" apps should not be pinnable, so we set type
// "unknown" to avoid the ARC check below.
app_type_ = apps::AppType::kUnknown;
return;
}
app_type_ = apps::AppServiceProxyFactory::GetForProfile(controller->profile())
->AppRegistryCache()
.GetAppType(GetAppId(item->id));
}
AppServiceShelfContextMenu::~AppServiceShelfContextMenu() = default;
ui::ImageModel AppServiceShelfContextMenu::GetIconForCommandId(
int command_id) const {
if (command_id == ash::LAUNCH_NEW) {
const gfx::VectorIcon& icon =
GetCommandIdVectorIcon(command_id, launch_new_string_id_);
return ui::ImageModel::FromVectorIcon(
icon, apps::GetColorIdForMenuItemIcon(), ash::kAppContextMenuIconSize);
}
return ShelfContextMenu::GetIconForCommandId(command_id);
}
std::u16string AppServiceShelfContextMenu::GetLabelForCommandId(
int command_id) const {
if (command_id == ash::LAUNCH_NEW) {
CHECK_GT(launch_new_string_id_, 0)
<< "Unexpected `launch_new_string_id_` value. App id = "
<< item().id.app_id << "; app type = " << apps::EnumToString(app_type_)
<< "; submenu items count = " << submenu_->GetItemCount();
return l10n_util::GetStringUTF16(launch_new_string_id_);
}
return ShelfContextMenu::GetLabelForCommandId(command_id);
}
void AppServiceShelfContextMenu::GetMenuModel(GetMenuModelCallback callback) {
apps::AppServiceProxyFactory::GetForProfile(controller()->profile())
->GetMenuModel(
item().id.app_id, apps::MenuType::kShelf, display_id(),
base::BindOnce(&AppServiceShelfContextMenu::OnGetMenuModel,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void AppServiceShelfContextMenu::ExecuteCommand(int command_id,
int event_flags) {
// Place new windows on the same display as the context menu.
display::ScopedDisplayForNewWindows scoped_display(display_id());
if (ExecuteCommonCommand(command_id, event_flags))
return;
switch (command_id) {
case ash::SHOW_APP_INFO:
ShowAppInfo();
ash::full_restore::FullRestoreService::MaybeCloseNotification(
controller()->profile());
break;
case ash::APP_CONTEXT_MENU_NEW_WINDOW:
if (app_type_ == apps::AppType::kCrostini ||
app_type_ == apps::AppType::kBruschetta) {
ShelfContextMenu::ExecuteCommand(ash::LAUNCH_NEW, event_flags);
} else if (app_type_ == apps::AppType::kStandaloneBrowser) {
crosapi::BrowserManager::Get()->NewWindow(
/*incognito=*/false, /*should_trigger_session_restore=*/false);
} else {
ash::NewWindowDelegate::GetInstance()->NewWindow(
/*incognito=*/false,
/*should_trigger_session_restore=*/false);
}
ash::full_restore::FullRestoreService::MaybeCloseNotification(
controller()->profile());
break;
case ash::APP_CONTEXT_MENU_NEW_INCOGNITO_WINDOW:
if (app_type_ == apps::AppType::kStandaloneBrowser) {
crosapi::BrowserManager::Get()->NewWindow(
/*incognito=*/true, /*should_trigger_session_restore=*/false);
} else {
ash::NewWindowDelegate::GetInstance()->NewWindow(
/*incognito=*/true,
/*should_trigger_session_restore=*/false);
}
ash::full_restore::FullRestoreService::MaybeCloseNotification(
controller()->profile());
break;
case ash::SHUTDOWN_GUEST_OS:
if (item().id.app_id == guest_os::kTerminalSystemAppId) {
crostini::CrostiniManager::GetForProfile(controller()->profile())
->StopRunningVms(base::DoNothing());
} else if (item().id.app_id == plugin_vm::kPluginVmShelfAppId) {
plugin_vm::PluginVmManagerFactory::GetForProfile(
controller()->profile())
->StopPluginVm(plugin_vm::kPluginVmName, /*force=*/false);
} else {
LOG(ERROR) << "App " << item().id.app_id
<< " should not have a shutdown guest OS command.";
}
break;
case ash::USE_LAUNCH_TYPE_TABBED_WINDOW:
[[fallthrough]];
case ash::USE_LAUNCH_TYPE_REGULAR:
[[fallthrough]];
case ash::USE_LAUNCH_TYPE_WINDOW:
launch_new_string_id_ = apps::StringIdForUseLaunchTypeCommand(command_id);
SetLaunchType(command_id);
break;
case ash::DEPRECATED_USE_LAUNCH_TYPE_FULLSCREEN:
case ash::DEPRECATED_USE_LAUNCH_TYPE_PINNED:
NOTREACHED();
break;
case ash::CROSTINI_USE_LOW_DENSITY:
case ash::CROSTINI_USE_HIGH_DENSITY: {
auto* registry_service =
guest_os::GuestOsRegistryServiceFactory::GetForProfile(
controller()->profile());
const bool scaled = command_id == ash::CROSTINI_USE_LOW_DENSITY;
registry_service->SetAppScaled(item().id.app_id, scaled);
if (controller()->IsOpen(item().id))
crostini::ShowAppRestartDialog(display_id());
return;
}
case ash::SETTINGS:
if (item().id.app_id == guest_os::kTerminalSystemAppId) {
guest_os::LaunchTerminalSettings(controller()->profile(), display_id());
ash::full_restore::FullRestoreService::MaybeCloseNotification(
controller()->profile());
}
return;
default:
if (extensions::ContextMenuMatcher::IsExtensionsCustomCommandId(
command_id)) {
extension_menu_items_->ExecuteCommand(command_id, nullptr, nullptr,
content::ContextMenuParams());
return;
}
if (command_id >= ash::LAUNCH_APP_SHORTCUT_FIRST &&
command_id <= ash::LAUNCH_APP_SHORTCUT_LAST) {
ExecutePublisherContextMenuCommand(command_id);
return;
}
ShelfContextMenu::ExecuteCommand(command_id, event_flags);
}
}
bool AppServiceShelfContextMenu::IsCommandIdChecked(int command_id) const {
switch (app_type_) {
case apps::AppType::kStandaloneBrowserChromeApp:
case apps::AppType::kWeb:
case apps::AppType::kSystemWeb: {
if (command_id >= ash::USE_LAUNCH_TYPE_COMMAND_START &&
command_id < ash::USE_LAUNCH_TYPE_COMMAND_END) {
auto user_window_mode = apps::WindowMode::kUnknown;
apps::AppServiceProxyFactory::GetForProfile(controller()->profile())
->AppRegistryCache()
.ForOneApp(item().id.app_id,
[&user_window_mode](const apps::AppUpdate& update) {
user_window_mode = update.WindowMode();
});
return user_window_mode != apps::WindowMode::kUnknown &&
user_window_mode ==
ConvertLaunchTypeCommandToWindowMode(command_id);
}
return ShelfContextMenu::IsCommandIdChecked(command_id);
}
case apps::AppType::kChromeApp:
if (command_id >= ash::USE_LAUNCH_TYPE_COMMAND_START &&
command_id < ash::USE_LAUNCH_TYPE_COMMAND_END) {
return GetExtensionLaunchType() ==
ConvertLaunchTypeCommandToExtensionLaunchType(command_id);
} else if (command_id < ash::COMMAND_ID_COUNT) {
return ShelfContextMenu::IsCommandIdChecked(command_id);
} else {
return (extension_menu_items_ &&
extension_menu_items_->IsCommandIdChecked(command_id));
}
case apps::AppType::kArc:
[[fallthrough]];
case apps::AppType::kCrostini:
[[fallthrough]];
case apps::AppType::kBuiltIn:
[[fallthrough]];
case apps::AppType::kPluginVm:
[[fallthrough]];
case apps::AppType::kBorealis:
[[fallthrough]];
default:
return ShelfContextMenu::IsCommandIdChecked(command_id);
}
}
bool AppServiceShelfContextMenu::IsCommandIdEnabled(int command_id) const {
if (command_id < ash::COMMAND_ID_COUNT)
return ShelfContextMenu::IsCommandIdEnabled(command_id);
if (extensions::ContextMenuMatcher::IsExtensionsCustomCommandId(command_id) &&
extension_menu_items_) {
return extension_menu_items_->IsCommandIdEnabled(command_id);
}
return true;
}
bool AppServiceShelfContextMenu::IsItemForCommandIdDynamic(
int command_id) const {
return command_id == ash::LAUNCH_NEW ||
ShelfContextMenu::IsItemForCommandIdDynamic(command_id);
}
void AppServiceShelfContextMenu::OnGetMenuModel(GetMenuModelCallback callback,
apps::MenuItems menu_items) {
auto menu_model = GetBaseMenuModel();
submenu_ = std::make_unique<ui::SimpleMenuModel>(this);
size_t index = 0;
if (!menu_items.items.empty() &&
menu_items.items[0]->command_id == ash::LAUNCH_NEW) {
apps::PopulateLaunchNewItemFromMenuItem(menu_items.items[0],
menu_model.get(), submenu_.get(),
&launch_new_string_id_);
++index;
}
// The special rule to ensure that FilesManager's first menu item is "New
// window".
const bool build_extension_menu_before_pin =
(app_type_ == apps::AppType::kChromeApp &&
item().id.app_id == extension_misc::kFilesManagerAppId);
if (build_extension_menu_before_pin)
BuildExtensionAppShortcutsMenu(menu_model.get());
// "New Window" should go above "Pin".
if (menu_items.items.size() > index &&
menu_items.items[index]->command_id == ash::APP_CONTEXT_MENU_NEW_WINDOW) {
AddContextMenuOption(menu_model.get(), ash::APP_CONTEXT_MENU_NEW_WINDOW,
menu_items.items[index]->string_id);
++index;
}
if (ShouldAddPinMenu())
AddPinMenu(menu_model.get());
size_t shortcut_index = menu_items.items.size();
for (size_t i = index; i < menu_items.items.size(); i++) {
// For Chrome browser, add the close item before the app info item.
if ((item().id.app_id == app_constants::kChromeAppId ||
item().id.app_id == app_constants::kLacrosAppId) &&
menu_items.items[i]->command_id == ash::SHOW_APP_INFO) {
BuildChromeAppMenu(menu_model.get());
}
if (menu_items.items[i]->command_id == ash::LAUNCH_NEW) {
// Crostini apps have `LAUNCH_NEW` menu item at non-0 position.
apps::PopulateLaunchNewItemFromMenuItem(menu_items.items[i],
menu_model.get(), submenu_.get(),
&launch_new_string_id_);
} else if (menu_items.items[i]->type == apps::MenuItemType::kCommand) {
AddContextMenuOption(
menu_model.get(),
static_cast<ash::CommandId>(menu_items.items[i]->command_id),
menu_items.items[i]->string_id);
} else {
// All shortcut menu items are appended at the end, so break out
// of the loop and continue processing shortcut menu items in
// BuildAppShortcutsMenu and BuildArcAppShortcutsMenu.
shortcut_index = i;
break;
}
}
if (app_type_ == apps::AppType::kArc) {
BuildArcAppShortcutsMenu(std::move(menu_items), std::move(menu_model),
std::move(callback), shortcut_index);
return;
}
if (app_type_ == apps::AppType::kWeb ||
app_type_ == apps::AppType::kSystemWeb ||
app_type_ == apps::AppType::kCrostini) {
BuildAppShortcutsMenu(std::move(menu_items), std::move(menu_model),
std::move(callback), shortcut_index);
return;
}
if (!build_extension_menu_before_pin)
BuildExtensionAppShortcutsMenu(menu_model.get());
// When Crostini generates shelf id with the prefix "crostini:", AppService
// can't generate the menu items, because the app_id doesn't match, so add the
// menu items at UI side, based on the app running status.
if (guest_os::IsUnregisteredCrostiniShelfAppId(item().id.app_id)) {
BuildCrostiniAppMenu(menu_model.get());
}
std::move(callback).Run(std::move(menu_model));
}
void AppServiceShelfContextMenu::BuildExtensionAppShortcutsMenu(
ui::SimpleMenuModel* menu_model) {
extension_menu_items_ = std::make_unique<extensions::ContextMenuMatcher>(
controller()->profile(), this, menu_model,
base::BindRepeating(MenuItemHasLauncherContext));
int index = 0;
extension_menu_items_->AppendExtensionItems(
extensions::MenuItem::ExtensionKey(item().id.app_id), std::u16string(),
&index, false /*is_action_menu*/);
app_list::AddMenuItemIconsForSystemApps(
item().id.app_id, menu_model, menu_model->GetItemCount() - index, index);
}
void AppServiceShelfContextMenu::BuildAppShortcutsMenu(
apps::MenuItems menu_items,
std::unique_ptr<ui::SimpleMenuModel> menu_model,
GetMenuModelCallback callback,
size_t shortcut_index) {
app_shortcut_items_ = std::make_unique<apps::AppShortcutItems>();
for (size_t i = shortcut_index; i < menu_items.items.size(); i++) {
apps::PopulateItemFromMenuItem(menu_items.items[i], menu_model.get(),
app_shortcut_items_.get());
}
std::move(callback).Run(std::move(menu_model));
}
void AppServiceShelfContextMenu::BuildArcAppShortcutsMenu(
apps::MenuItems menu_items,
std::unique_ptr<ui::SimpleMenuModel> menu_model,
GetMenuModelCallback callback,
size_t arc_shortcut_index) {
const ArcAppListPrefs* arc_prefs =
ArcAppListPrefs::Get(controller()->profile());
DCHECK(arc_prefs);
const arc::ArcAppShelfId& arc_shelf_id =
arc::ArcAppShelfId::FromString(item().id.app_id);
DCHECK(arc_shelf_id.valid());
std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
arc_prefs->GetApp(arc_shelf_id.app_id());
if (!app_info && !arc_shelf_id.has_shelf_group_id()) {
LOG(ERROR) << "App " << item().id.app_id << " is not available.";
std::move(callback).Run(std::move(menu_model));
return;
}
if (arc_shelf_id.has_shelf_group_id()) {
const bool app_is_open = controller()->IsOpen(item().id);
if (!app_is_open && !app_info->suspended) {
DCHECK(app_info->launchable);
AddContextMenuOption(menu_model.get(), ash::LAUNCH_NEW,
IDS_APP_CONTEXT_MENU_ACTIVATE_ARC);
launch_new_string_id_ = IDS_APP_CONTEXT_MENU_ACTIVATE_ARC;
}
if (app_is_open) {
AddContextMenuOption(menu_model.get(), ash::MENU_CLOSE,
IDS_SHELF_CONTEXT_MENU_CLOSE);
}
}
BuildAppShortcutsMenu(std::move(menu_items), std::move(menu_model),
std::move(callback), arc_shortcut_index);
}
void AppServiceShelfContextMenu::BuildCrostiniAppMenu(
ui::SimpleMenuModel* menu_model) {
if (controller()->IsOpen(item().id)) {
AddContextMenuOption(menu_model, ash::MENU_CLOSE,
IDS_SHELF_CONTEXT_MENU_CLOSE);
} else {
AddContextMenuOption(menu_model, ash::LAUNCH_NEW,
IDS_APP_CONTEXT_MENU_ACTIVATE_ARC);
launch_new_string_id_ = IDS_APP_CONTEXT_MENU_ACTIVATE_ARC;
}
}
void AppServiceShelfContextMenu::BuildChromeAppMenu(
ui::SimpleMenuModel* menu_model) {
// Don't check list of active browsers for lacros app because the list of
// browsers only tracks in-process browsers (i.e. instances of ash-chrome).
const bool has_active_browsers =
item().id.app_id != app_constants::kLacrosAppId &&
!BrowserShortcutShelfItemController::IsListOfActiveBrowserEmpty(
controller()->shelf_model());
if (has_active_browsers || item().type == ash::TYPE_DIALOG ||
controller()->IsOpen(item().id)) {
AddContextMenuOption(menu_model, ash::MENU_CLOSE,
IDS_SHELF_CONTEXT_MENU_CLOSE);
}
}
void AppServiceShelfContextMenu::ShowAppInfo() {
if (app_type_ == apps::AppType::kArc) {
chrome::ShowAppManagementPage(
controller()->profile(), item().id.app_id,
ash::settings::AppManagementEntryPoint::kShelfContextMenuAppInfoArc);
return;
}
// TODO(crbug.com/1196697): If this comes from Lacros app, it shows the
// top "Apps" settings page. This is fallback, because Lacros app is not
// registered. This is short term workaround to keep the relative
// compatibility for Lacros Primary. We should figure out what should be shown
// by this.
controller()->DoShowAppInfoFlow(controller()->profile(), item().id.app_id);
}
void AppServiceShelfContextMenu::SetLaunchType(int command_id) {
switch (app_type_) {
case apps::AppType::kStandaloneBrowserChromeApp:
case apps::AppType::kWeb:
case apps::AppType::kSystemWeb: {
// Web apps can only toggle between kWindow, kTabbed and kBrowser.
apps::WindowMode user_window_mode =
ConvertLaunchTypeCommandToWindowMode(command_id);
if (user_window_mode != apps::WindowMode::kUnknown) {
apps::AppServiceProxyFactory::GetForProfile(controller()->profile())
->SetWindowMode(item().id.app_id, user_window_mode);
}
return;
}
case apps::AppType::kChromeApp:
SetExtensionLaunchType(command_id);
return;
case apps::AppType::kArc:
[[fallthrough]];
case apps::AppType::kCrostini:
[[fallthrough]];
case apps::AppType::kBuiltIn:
[[fallthrough]];
case apps::AppType::kPluginVm:
[[fallthrough]];
case apps::AppType::kBorealis:
[[fallthrough]];
default:
return;
}
}
void AppServiceShelfContextMenu::SetExtensionLaunchType(int command_id) {
switch (static_cast<ash::CommandId>(command_id)) {
case ash::USE_LAUNCH_TYPE_REGULAR:
extensions::SetLaunchType(controller()->profile(), item().id.app_id,
extensions::LAUNCH_TYPE_REGULAR);
break;
case ash::USE_LAUNCH_TYPE_WINDOW: {
// Hosted apps can only toggle between LAUNCH_WINDOW and LAUNCH_REGULAR.
extensions::LaunchType launch_type =
GetExtensionLaunchType() == extensions::LAUNCH_TYPE_WINDOW
? extensions::LAUNCH_TYPE_REGULAR
: extensions::LAUNCH_TYPE_WINDOW;
extensions::SetLaunchType(controller()->profile(), item().id.app_id,
launch_type);
break;
}
case ash::USE_LAUNCH_TYPE_TABBED_WINDOW:
case ash::DEPRECATED_USE_LAUNCH_TYPE_FULLSCREEN:
case ash::DEPRECATED_USE_LAUNCH_TYPE_PINNED:
NOTREACHED();
break;
default:
return;
}
}
extensions::LaunchType AppServiceShelfContextMenu::GetExtensionLaunchType()
const {
const extensions::Extension* extension =
extensions::ExtensionRegistry::Get(controller()->profile())
->GetExtensionById(item().id.app_id,
extensions::ExtensionRegistry::EVERYTHING);
if (!extension)
return extensions::LAUNCH_TYPE_DEFAULT;
return extensions::GetLaunchType(
extensions::ExtensionPrefs::Get(controller()->profile()), extension);
}
bool AppServiceShelfContextMenu::ShouldAddPinMenu() {
switch (app_type_) {
case apps::AppType::kArc: {
const arc::ArcAppShelfId& arc_shelf_id =
arc::ArcAppShelfId::FromString(item().id.app_id);
DCHECK(arc_shelf_id.valid());
const ArcAppListPrefs* arc_list_prefs =
ArcAppListPrefs::Get(controller()->profile());
DCHECK(arc_list_prefs);
std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
arc_list_prefs->GetApp(arc_shelf_id.app_id());
if (!arc_shelf_id.has_shelf_group_id() && app_info->launchable)
return true;
return false;
}
case apps::AppType::kPluginVm:
case apps::AppType::kBuiltIn: {
bool show_in_launcher = false;
apps::AppServiceProxyFactory::GetForProfile(controller()->profile())
->AppRegistryCache()
.ForOneApp(item().id.app_id,
[&show_in_launcher](const apps::AppUpdate& update) {
show_in_launcher =
update.ShowInLauncher().value_or(false);
});
return show_in_launcher;
}
case apps::AppType::kCrostini:
case apps::AppType::kBorealis:
case apps::AppType::kChromeApp:
case apps::AppType::kWeb:
case apps::AppType::kSystemWeb:
case apps::AppType::kStandaloneBrowserChromeApp:
return true;
case apps::AppType::kStandaloneBrowser:
// Lacros behaves like the Chrome browser icon and cannot be unpinned.
return false;
case apps::AppType::kUnknown:
// Type kUnknown is used for "unregistered" Crostini apps, which do not
// have a .desktop file and can only be closed, not pinned.
return false;
case apps::AppType::kMacOs:
case apps::AppType::kRemote:
case apps::AppType::kExtension:
case apps::AppType::kStandaloneBrowserExtension:
NOTREACHED() << "Type " << (int)app_type_
<< " should not appear in shelf.";
return false;
case apps::AppType::kBruschetta:
return true;
}
}
void AppServiceShelfContextMenu::ExecutePublisherContextMenuCommand(
int command_id) {
DCHECK(command_id >= ash::LAUNCH_APP_SHORTCUT_FIRST &&
command_id <= ash::LAUNCH_APP_SHORTCUT_LAST);
size_t index = command_id - ash::LAUNCH_APP_SHORTCUT_FIRST;
DCHECK(app_shortcut_items_);
DCHECK_LT(index, app_shortcut_items_->size());
apps::AppServiceProxyFactory::GetForProfile(controller()->profile())
->ExecuteContextMenuCommand(item().id.app_id, command_id,
app_shortcut_items_->at(index).shortcut_id,
display_id());
}