| // Copyright 2022 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/webui/app_home/app_home_page_handler.h" |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "chrome/browser/apps/app_service/app_icon/app_icon_source.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_ui_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/apps/app_info_dialog.h" |
| #include "chrome/browser/ui/browser_dialogs.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/chrome_pages.h" |
| #include "chrome/browser/ui/web_applications/web_app_dialog_manager.h" |
| #include "chrome/browser/ui/web_applications/web_app_ui_manager_impl.h" |
| #include "chrome/browser/ui/webui/extensions/extension_icon_source.h" |
| #include "chrome/browser/web_applications/web_app.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_utils.h" |
| #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" |
| #include "components/webapps/browser/uninstall_result_code.h" |
| #include "content/public/browser/web_ui.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_system.h" |
| #include "url/gurl.h" |
| |
| using content::WebUI; |
| using extensions::Extension; |
| using extensions::ExtensionRegistry; |
| using extensions::ExtensionSet; |
| |
| namespace webapps { |
| |
| namespace { |
| |
| const int kWebAppLargeIconSize = 128; |
| |
| // The Youtube app is incorrectly hardcoded to be a 'bookmark app'. However, it |
| // is a platform app. |
| // TODO(crbug.com/1065748): Remove this hack once the youtube app is fixed. |
| bool IsYoutubeExtension(const std::string& extension_id) { |
| return extension_id == extension_misc::kYoutubeAppId; |
| } |
| } // namespace |
| |
| AppHomePageHandler::AppHomePageHandler( |
| content::WebUI* web_ui, |
| Profile* profile, |
| mojo::PendingReceiver<app_home::mojom::PageHandler> receiver, |
| mojo::PendingRemote<app_home::mojom::Page> page) |
| : web_ui_(web_ui), |
| profile_(profile), |
| receiver_(this, std::move(receiver)), |
| page_(std::move(page)), |
| web_app_provider_(web_app::WebAppProvider::GetForWebApps(profile)), |
| extension_service_( |
| extensions::ExtensionSystem::Get(profile)->extension_service()) { |
| install_manager_observation_.Observe(&web_app_provider_->install_manager()); |
| ExtensionRegistry::Get(profile)->AddObserver(this); |
| } |
| |
| AppHomePageHandler::~AppHomePageHandler() { |
| ExtensionRegistry::Get(profile_)->RemoveObserver(this); |
| // Destroy `extension_uninstall_dialog_` now, since `this` is an |
| // `ExtensionUninstallDialog::Delegate` and the dialog may call back into |
| // `this` when destroyed. |
| extension_uninstall_dialog_.reset(); |
| } |
| |
| Browser* AppHomePageHandler::GetCurrentBrowser() { |
| return chrome::FindBrowserWithWebContents(web_ui_->GetWebContents()); |
| } |
| |
| void AppHomePageHandler::ShowWebAppSettings(const std::string& app_id) { |
| chrome::ShowWebAppSettings( |
| GetCurrentBrowser(), app_id, |
| web_app::AppSettingsPageEntryPoint::kChromeAppsPage); |
| } |
| |
| void AppHomePageHandler::ShowExtensionAppSettings( |
| const extensions::Extension* extension) { |
| ShowAppInfoInNativeDialog(web_ui_->GetWebContents(), profile_, extension, |
| base::DoNothing()); |
| } |
| |
| void AppHomePageHandler::CreateWebAppShortcut(const std::string& app_id, |
| base::OnceClosure done) { |
| Browser* browser = GetCurrentBrowser(); |
| chrome::ShowCreateChromeAppShortcutsDialog( |
| browser->window()->GetNativeWindow(), browser->profile(), app_id, |
| base::BindOnce( |
| [](base::OnceClosure done, bool success) { |
| base::UmaHistogramBoolean( |
| "Apps.AppInfoDialog.CreateWebAppShortcutSuccess", success); |
| std::move(done).Run(); |
| }, |
| std::move(done))); |
| } |
| |
| void AppHomePageHandler::CreateExtensionAppShortcut( |
| const extensions::Extension* extension, |
| base::OnceClosure done) { |
| Browser* browser = GetCurrentBrowser(); |
| chrome::ShowCreateChromeAppShortcutsDialog( |
| browser->window()->GetNativeWindow(), browser->profile(), extension, |
| base::BindOnce( |
| [](base::OnceClosure done, bool success) { |
| base::UmaHistogramBoolean( |
| "Apps.AppInfoDialog.CreateExtensionShortcutSuccess", success); |
| std::move(done).Run(); |
| }, |
| std::move(done))); |
| } |
| |
| app_home::mojom::AppInfoPtr AppHomePageHandler::CreateAppInfoPtrFromWebApp( |
| const web_app::AppId& app_id) { |
| auto& registrar = web_app_provider_->registrar(); |
| |
| auto app_info = app_home::mojom::AppInfo::New(); |
| |
| app_info->id = app_id; |
| |
| GURL start_url = registrar.GetAppStartUrl(app_id); |
| app_info->start_url = start_url; |
| |
| std::string name = registrar.GetAppShortName(app_id); |
| app_info->name = name; |
| |
| app_info->icon_url = |
| apps::AppIconSource::GetIconURL(app_id, kWebAppLargeIconSize); |
| |
| return app_info; |
| } |
| |
| app_home::mojom::AppInfoPtr AppHomePageHandler::CreateAppInfoPtrFromExtension( |
| const Extension* extension) { |
| auto app_info = app_home::mojom::AppInfo::New(); |
| |
| app_info->id = extension->id(); |
| |
| GURL start_url = extensions::AppLaunchInfo::GetFullLaunchURL(extension); |
| app_info->start_url = start_url; |
| |
| app_info->name = extension->name(); |
| |
| app_info->icon_url = extensions::ExtensionIconSource::GetIconURL( |
| extension, extension_misc::EXTENSION_ICON_LARGE, |
| ExtensionIconSet::MATCH_BIGGER, false /*grayscale*/); |
| |
| return app_info; |
| } |
| |
| void AppHomePageHandler::FillWebAppInfoList( |
| std::vector<app_home::mojom::AppInfoPtr>* result) { |
| web_app::WebAppRegistrar& registrar = web_app_provider_->registrar(); |
| |
| for (const web_app::AppId& web_app_id : registrar.GetAppIds()) { |
| if (IsYoutubeExtension(web_app_id)) |
| continue; |
| result->emplace_back(CreateAppInfoPtrFromWebApp(web_app_id)); |
| } |
| } |
| |
| void AppHomePageHandler::FillExtensionInfoList( |
| std::vector<app_home::mojom::AppInfoPtr>* result) { |
| ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); |
| std::unique_ptr<ExtensionSet> extension_apps = |
| registry->GenerateInstalledExtensionsSet(ExtensionRegistry::ENABLED | |
| ExtensionRegistry::DISABLED | |
| ExtensionRegistry::TERMINATED); |
| for (const auto& extension : *extension_apps) { |
| if (extensions::ui_util::ShouldDisplayInNewTabPage(extension.get(), |
| profile_)) |
| result->emplace_back(CreateAppInfoPtrFromExtension(extension.get())); |
| } |
| } |
| |
| void AppHomePageHandler::OnExtensionUninstallDialogClosed( |
| bool did_start_uninstall, |
| const std::u16string& error) { |
| CleanupAfterUninstall(); |
| } |
| |
| void AppHomePageHandler::CleanupAfterUninstall() { |
| uninstall_dialog_prompting_ = false; |
| } |
| |
| void AppHomePageHandler::UninstallWebApp(const std::string& web_app_id) { |
| if (!web_app_provider_->install_finalizer().CanUserUninstallWebApp( |
| web_app_id)) { |
| LOG(ERROR) << "Attempt to uninstall a webapp that is non-usermanagable " |
| "was made. App id : " |
| << web_app_id; |
| return; |
| } |
| |
| uninstall_dialog_prompting_ = true; |
| |
| auto uninstall_success_callback = base::BindOnce( |
| [](base::WeakPtr<AppHomePageHandler> app_home_page_handler, |
| webapps::UninstallResultCode code) { |
| if (app_home_page_handler) { |
| app_home_page_handler->CleanupAfterUninstall(); |
| } |
| }, |
| weak_ptr_factory_.GetWeakPtr()); |
| |
| Browser* browser = GetCurrentBrowser(); |
| web_app::WebAppUiManagerImpl::Get(web_app_provider_) |
| ->dialog_manager() |
| .UninstallWebApp(web_app_id, webapps::WebappUninstallSource::kAppsPage, |
| browser->window(), |
| std::move(uninstall_success_callback)); |
| return; |
| } |
| |
| extensions::ExtensionUninstallDialog* |
| AppHomePageHandler::CreateExtensionUninstallDialog() { |
| Browser* browser = GetCurrentBrowser(); |
| extension_uninstall_dialog_ = extensions::ExtensionUninstallDialog::Create( |
| extension_service_->profile(), browser->window()->GetNativeWindow(), |
| this); |
| return extension_uninstall_dialog_.get(); |
| } |
| |
| void AppHomePageHandler::UninstallExtensionApp(const Extension* extension) { |
| if (!extensions::ExtensionSystem::Get(extension_service_->profile()) |
| ->management_policy() |
| ->UserMayModifySettings(extension, nullptr)) { |
| LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable " |
| "was made. Extension id : " |
| << extension->id(); |
| return; |
| } |
| |
| uninstall_dialog_prompting_ = true; |
| |
| Browser* browser = GetCurrentBrowser(); |
| extension_uninstall_dialog_ = extensions::ExtensionUninstallDialog::Create( |
| extension_service_->profile(), browser->window()->GetNativeWindow(), |
| this); |
| |
| extension_uninstall_dialog_->ConfirmUninstall( |
| extension, extensions::UNINSTALL_REASON_USER_INITIATED, |
| extensions::UNINSTALL_SOURCE_CHROME_APPS_PAGE); |
| } |
| |
| void AppHomePageHandler::GetApps(GetAppsCallback callback) { |
| std::vector<app_home::mojom::AppInfoPtr> result; |
| FillWebAppInfoList(&result); |
| FillExtensionInfoList(&result); |
| std::move(callback).Run(std::move(result)); |
| } |
| |
| void AppHomePageHandler::OnWebAppWillBeUninstalled( |
| const web_app::AppId& app_id) { |
| auto app_info = app_home::mojom::AppInfo::New(); |
| app_info->id = app_id; |
| page_->RemoveApp(std::move(app_info)); |
| } |
| |
| void AppHomePageHandler::OnWebAppInstalled(const web_app::AppId& app_id) { |
| page_->AddApp(CreateAppInfoPtrFromWebApp(app_id)); |
| } |
| |
| void AppHomePageHandler::OnWebAppInstallManagerDestroyed() { |
| install_manager_observation_.Reset(); |
| } |
| |
| void AppHomePageHandler::OnExtensionLoaded( |
| content::BrowserContext* browser_context, |
| const extensions::Extension* extension) { |
| page_->AddApp(CreateAppInfoPtrFromExtension(extension)); |
| } |
| |
| void AppHomePageHandler::OnExtensionUninstalled( |
| content::BrowserContext* browser_context, |
| const Extension* extension, |
| extensions::UninstallReason reason) { |
| auto app_info = app_home::mojom::AppInfo::New(); |
| app_info->id = extension->id(); |
| page_->RemoveApp(std::move(app_info)); |
| } |
| |
| void AppHomePageHandler::UninstallApp(const std::string& app_id) { |
| if (uninstall_dialog_prompting_) |
| return; |
| |
| if (web_app_provider_->registrar().IsInstalled(app_id) && |
| !IsYoutubeExtension(app_id)) { |
| UninstallWebApp(app_id); |
| return; |
| } |
| |
| const Extension* extension = |
| ExtensionRegistry::Get(extension_service_->profile()) |
| ->GetInstalledExtension(app_id); |
| if (extension) { |
| UninstallExtensionApp(extension); |
| } |
| } |
| |
| void AppHomePageHandler::ShowAppSettings(const std::string& app_id) { |
| if (web_app_provider_->registrar().IsInstalled(app_id) && |
| !IsYoutubeExtension(app_id)) { |
| ShowWebAppSettings(app_id); |
| return; |
| } |
| |
| const Extension* extension = |
| extensions::ExtensionRegistry::Get(extension_service_->profile()) |
| ->GetExtensionById(app_id, |
| extensions::ExtensionRegistry::ENABLED | |
| extensions::ExtensionRegistry::DISABLED | |
| extensions::ExtensionRegistry::TERMINATED); |
| if (extension) { |
| ShowExtensionAppSettings(extension); |
| } |
| } |
| |
| void AppHomePageHandler::CreateAppShortcut(const std::string& app_id, |
| CreateAppShortcutCallback callback) { |
| if (web_app_provider_->registrar().IsInstalled(app_id) && |
| !IsYoutubeExtension(app_id)) { |
| CreateWebAppShortcut(app_id, std::move(callback)); |
| return; |
| } |
| |
| const Extension* extension = |
| extensions::ExtensionRegistry::Get(extension_service_->profile()) |
| ->GetExtensionById(app_id, |
| extensions::ExtensionRegistry::ENABLED | |
| extensions::ExtensionRegistry::DISABLED | |
| extensions::ExtensionRegistry::TERMINATED); |
| if (extension) |
| CreateExtensionAppShortcut(extension, std::move(callback)); |
| } |
| |
| } // namespace webapps |