| // Copyright 2017 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/web_applications/web_app_menu_model.h" |
| |
| #include "base/feature_list.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/notimplemented.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/app/vector_icons/vector_icons.h" |
| #include "chrome/browser/media/router/media_router_feature.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/browser_window/public/browser_window_features.h" |
| #include "chrome/browser/ui/extensions/extensions_container.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/toolbar/app_menu_model.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/browser/ui/web_applications/app_browser_controller.h" |
| #include "chrome/browser/ui/web_applications/web_app_browser_controller.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/grit/branded_strings.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/omnibox/browser/location_bar_model.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "components/webapps/browser/installable/installable_metrics.h" |
| #include "content/public/common/content_features.h" |
| #include "ui/base/accelerators/menu_label_accelerator_util.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/models/image_model.h" |
| #include "ui/base/ui_base_features.h" |
| #include "url/gurl.h" |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| #include "chromeos/strings/grit/chromeos_strings.h" |
| #include "chromeos/ui/frame/desks/move_to_desks_menu_delegate.h" |
| #include "chromeos/ui/frame/desks/move_to_desks_menu_model.h" |
| #include "ui/views/widget/widget.h" |
| #endif |
| |
| namespace { |
| |
| bool ShouldAllowOpenInChrome(Browser* browser) { |
| // Isolated Web Apps shouldn't be opened in Chrome. |
| const bool is_isolated_web_app = |
| browser->app_controller() && |
| browser->app_controller()->IsIsolatedWebApp(); |
| // Web Apps with enabled prevent close shouldn't be opened in Chrome. |
| const bool prevent_close_enabled = |
| browser->app_controller() && |
| browser->app_controller()->IsPreventCloseEnabled(); |
| return !is_isolated_web_app && !prevent_close_enabled; |
| } |
| |
| 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)); |
| } |
| |
| } // namespace |
| |
| constexpr int WebAppMenuModel::kUninstallAppCommandId; |
| constexpr int WebAppMenuModel::kExtensionsMenuCommandId; |
| |
| WebAppMenuModel::WebAppMenuModel(ui::AcceleratorProvider* provider, |
| Browser* browser) |
| : AppMenuModel(provider, browser) {} |
| |
| WebAppMenuModel::~WebAppMenuModel() = default; |
| |
| bool WebAppMenuModel::IsCommandIdEnabled(int command_id) const { |
| switch (command_id) { |
| case kUninstallAppCommandId: |
| return browser()->app_controller()->CanUserUninstall(); |
| case kExtensionsMenuCommandId: |
| case IDC_OPEN_IN_CHROME: |
| case IDC_WEB_APP_UPGRADE_DIALOG: |
| #if BUILDFLAG(IS_CHROMEOS) |
| case chromeos::MoveToDesksMenuModel::kMenuCommandId: |
| #endif |
| // These commands only exist in the menu model if they should be visible |
| // and enabled. |
| return true; |
| default: |
| return AppMenuModel::IsCommandIdEnabled(command_id); |
| } |
| } |
| |
| void WebAppMenuModel::ExecuteCommand(int command_id, int event_flags) { |
| switch (command_id) { |
| case kUninstallAppCommandId: |
| LogMenuAction(MENU_ACTION_UNINSTALL_APP); |
| browser()->app_controller()->Uninstall( |
| webapps::WebappUninstallSource::kAppMenu); |
| break; |
| case kExtensionsMenuCommandId: |
| browser()->window()->GetExtensionsContainer()->ToggleExtensionsMenu(); |
| break; |
| case IDC_OPEN_IN_CHROME: |
| if (ShouldAllowOpenInChrome(browser())) { |
| AppMenuModel::ExecuteCommand(command_id, event_flags); |
| } |
| break; |
| case IDC_WEB_APP_UPGRADE_DIALOG: |
| CHECK(base::FeatureList::IsEnabled( |
| features::kWebAppPredictableAppUpdating)); |
| LogMenuAction(MENU_ACTION_TRIGGER_APP_UPDATE_DIALOG); |
| browser()->app_controller()->CreateMetadataAndTriggerAppUpdateDialog( |
| base::TimeTicks::Now()); |
| break; |
| default: |
| AppMenuModel::ExecuteCommand(command_id, event_flags); |
| break; |
| } |
| } |
| |
| void WebAppMenuModel::Build() { |
| CHECK(browser()->app_controller()); |
| web_app::WebAppBrowserController* app_controller = |
| browser()->app_controller()->AsWebAppBrowserController(); |
| if (app_controller && app_controller->HasPendingUpdate()) { |
| CHECK( |
| base::FeatureList::IsEnabled(features::kWebAppPredictableAppUpdating)); |
| AddSeparator(ui::SPACING_SEPARATOR); |
| gfx::ImageSkia icon = app_controller->GetAppMenuIcon(); |
| ui::ImageModel update_icon; |
| if (!icon.isNull()) { |
| update_icon = ui::ImageModel::FromImageSkia(icon); |
| } |
| if (update_icon.IsEmpty()) { |
| update_icon = ui::ImageModel::FromVectorIcon( |
| kBrowserToolsUpdateChromeRefreshIcon, |
| ui::kColorMenuIconOnEmphasizedBackground, kDefaultIconSize); |
| } |
| AddItemWithStringIdAndIcon(IDC_WEB_APP_UPGRADE_DIALOG, |
| IDS_REVIEW_APP_UPDATE, update_icon); |
| AddSeparator(ui::SPACING_SEPARATOR); |
| } |
| |
| AddItemWithStringIdAndVectorIcon( |
| this, IDC_WEB_APP_MENU_APP_INFO, IDS_APP_CONTEXT_MENU_SHOW_INFO, |
| browser()->GetFeatures().location_bar_model()->GetVectorIcon()); |
| size_t app_info_index = GetItemCount() - 1; |
| |
| CHECK(browser()); |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| bool is_isolated_web_app = browser()->app_controller()->IsIsolatedWebApp(); |
| |
| if (web_contents) { |
| std::u16string display_text = |
| web_app::AppBrowserController::FormatUrlOrigin( |
| web_contents->GetVisibleURL()); |
| |
| // For Isolated Web Apps the origin's host name is a non-human-readable |
| // string of characters, so instead of displaying the origin, the short name |
| // of the app will be displayed. |
| if (is_isolated_web_app) { |
| std::u16string short_name = |
| browser()->app_controller()->GetAppShortName(); |
| // For Isolated Web Apps, |GetAppShortName()| must be non-empty. |
| display_text = short_name; |
| } |
| SetMinorText(app_info_index, display_text); |
| } |
| |
| AddSeparator(ui::NORMAL_SEPARATOR); |
| |
| if (base::FeatureList::IsEnabled( |
| features::kDesktopPWAsElidedExtensionsMenu) && |
| browser()->window()->GetExtensionsContainer() && |
| browser()->window()->GetExtensionsContainer()->HasAnyExtensions() && |
| // Extensions are not supported inside Isolated Web Apps. |
| !browser()->app_controller()->IsIsolatedWebApp()) { |
| AddItemWithStringIdAndVectorIcon(this, kExtensionsMenuCommandId, |
| IDS_SHOW_EXTENSIONS, |
| vector_icons::kExtensionChromeRefreshIcon); |
| AddSeparator(ui::NORMAL_SEPARATOR); |
| } |
| |
| if (browser()->app_controller() && |
| browser()->app_controller()->has_tab_strip() && |
| !browser()->app_controller()->ShouldHideNewTabButton()) { |
| AddItemWithStringIdAndVectorIcon(this, IDC_NEW_TAB, IDS_NEW_TAB, |
| kNewTabRefreshIcon); |
| } |
| AddItemWithStringIdAndVectorIcon(this, IDC_COPY_URL, IDS_COPY_URL, |
| kLinkChromeRefreshIcon); |
| |
| if (ShouldAllowOpenInChrome(browser())) { |
| AddItemWithStringIdAndVectorIcon(this, IDC_OPEN_IN_CHROME, |
| IDS_OPEN_IN_CHROME, kBrowserLogoIcon); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| if (chromeos::MoveToDesksMenuDelegate::ShouldShowMoveToDesksMenu( |
| browser()->window()->GetNativeWindow())) { |
| AddSeparator(ui::NORMAL_SEPARATOR); |
| move_to_desks_submenu_ = std::make_unique<chromeos::MoveToDesksMenuModel>( |
| std::make_unique<chromeos::MoveToDesksMenuDelegate>( |
| views::Widget::GetWidgetForNativeWindow( |
| browser()->window()->GetNativeWindow()))); |
| AddSubMenuWithStringId(chromeos::MoveToDesksMenuModel::kMenuCommandId, |
| IDS_MOVE_TO_DESKS_MENU, |
| move_to_desks_submenu_.get()); |
| } |
| #endif |
| |
| // Chrome OS's app list is prominent enough to not need a separate uninstall |
| // option in the app menu. |
| #if !BUILDFLAG(IS_CHROMEOS) |
| DCHECK(browser()->app_controller()); |
| if (browser()->app_controller()->IsInstalled()) { |
| AddSeparator(ui::NORMAL_SEPARATOR); |
| AddItemWithIcon(kUninstallAppCommandId, |
| l10n_util::GetStringFUTF16( |
| IDS_UNINSTALL_FROM_OS_LAUNCH_SURFACE, |
| ui::EscapeMenuLabelAmpersands( |
| browser()->app_controller()->GetAppShortName())), |
| ui::ImageModel::FromVectorIcon(kTrashCanRefreshIcon)); |
| } |
| #endif // !BUILDFLAG(IS_CHROMEOS) |
| AddSeparator(ui::NORMAL_SEPARATOR); |
| CreateZoomMenu(); |
| AddSeparator(ui::NORMAL_SEPARATOR); |
| AddItemWithStringIdAndVectorIcon(this, IDC_PRINT, IDS_PRINT, kPrintMenuIcon); |
| CreateFindAndEditSubMenu(); |
| |
| if (media_router::MediaRouterEnabled(browser()->profile())) { |
| AddItemWithStringIdAndVectorIcon(this, IDC_ROUTE_MEDIA, |
| IDS_MEDIA_ROUTER_MENU_ITEM_TITLE, |
| kCastChromeRefreshIcon); |
| } |
| } |