blob: b9d8db96ec06f3d6cb548b8011a0b0cb20278bfe [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/optional.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/launch_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_finalizer_utils.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_registrar.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/web_application_info.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/uninstall_reason.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/extension_set.h"
#include "url/gurl.h"
#if defined(OS_MACOSX)
#include "chrome/browser/web_applications/extensions/web_app_extension_shortcut_mac.h"
#endif
namespace extensions {
BookmarkAppInstallFinalizer::BookmarkAppInstallFinalizer(Profile* profile)
: externally_installed_app_prefs_(profile->GetPrefs()), profile_(profile) {
crx_installer_factory_ = base::BindRepeating([](Profile* profile) {
ExtensionService* extension_service =
ExtensionSystem::Get(profile)->extension_service();
DCHECK(extension_service);
return CrxInstaller::CreateSilent(extension_service);
});
}
BookmarkAppInstallFinalizer::~BookmarkAppInstallFinalizer() = default;
void BookmarkAppInstallFinalizer::FinalizeInstall(
const WebApplicationInfo& web_app_info,
const FinalizeOptions& options,
InstallFinalizedCallback callback) {
scoped_refptr<CrxInstaller> crx_installer =
crx_installer_factory_.Run(profile_);
extensions::LaunchType launch_type =
web_app_info.open_as_window ? LAUNCH_TYPE_WINDOW : LAUNCH_TYPE_REGULAR;
crx_installer->set_installer_callback(base::BindOnce(
&BookmarkAppInstallFinalizer::OnExtensionInstalled,
weak_ptr_factory_.GetWeakPtr(), web_app_info.app_url, launch_type,
options.locally_installed, std::move(callback), crx_installer));
switch (options.install_source) {
// TODO(nigeltao/ortuno): should these two cases lead to different
// Manifest::Location values: INTERNAL vs EXTERNAL_PREF_DOWNLOAD?
case WebappInstallSource::INTERNAL_DEFAULT:
case WebappInstallSource::EXTERNAL_DEFAULT:
crx_installer->set_install_source(Manifest::EXTERNAL_PREF_DOWNLOAD);
// CrxInstaller::InstallWebApp will OR the creation flags with
// FROM_BOOKMARK.
crx_installer->set_creation_flags(Extension::WAS_INSTALLED_BY_DEFAULT);
break;
case WebappInstallSource::EXTERNAL_POLICY:
crx_installer->set_install_source(Manifest::EXTERNAL_POLICY_DOWNLOAD);
break;
case WebappInstallSource::SYSTEM_DEFAULT:
// System Apps are considered EXTERNAL_COMPONENT as they are downloaded
// from the WebUI they point to. COMPONENT seems like the more correct
// value, but usages (icon loading, filesystem cleanup), are tightly
// coupled to this value, making it unsuitable.
crx_installer->set_install_source(Manifest::EXTERNAL_COMPONENT);
// InstallWebApp will OR the creation flags with FROM_BOOKMARK.
crx_installer->set_creation_flags(Extension::WAS_INSTALLED_BY_DEFAULT);
break;
case WebappInstallSource::ARC:
// Ensure that WebApk is not synced. There is some mechanism to propagate
// the local source of data in place of usual extension sync.
crx_installer->set_install_source(Manifest::EXTERNAL_PREF_DOWNLOAD);
break;
case WebappInstallSource::COUNT:
NOTREACHED();
break;
default:
// All other install sources mean user-installed app. Do nothing.
break;
}
crx_installer->InstallWebApp(web_app_info);
}
void BookmarkAppInstallFinalizer::UninstallExternalWebApp(
const GURL& app_url,
UninstallWebAppCallback callback) {
base::Optional<web_app::AppId> app_id =
externally_installed_app_prefs_.LookupAppId(app_url);
if (!app_id.has_value()) {
LOG(WARNING) << "Couldn't uninstall app with url " << app_url
<< "; No corresponding extension for url.";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), false));
return;
}
UninstallWebApp(*app_id, std::move(callback));
}
void BookmarkAppInstallFinalizer::UninstallWebApp(
const web_app::AppId& app_id,
UninstallWebAppCallback callback) {
if (!GetEnabledExtension(app_id)) {
LOG(WARNING) << "Couldn't uninstall app " << app_id
<< "; Extension not installed.";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), false));
return;
}
base::string16 error;
bool uninstalled =
ExtensionSystem::Get(profile_)->extension_service()->UninstallExtension(
app_id, UNINSTALL_REASON_ORPHANED_EXTERNAL_EXTENSION, &error);
if (!uninstalled) {
LOG(WARNING) << "Couldn't uninstall app " << app_id << ". " << error;
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), uninstalled));
}
bool BookmarkAppInstallFinalizer::CanCreateOsShortcuts() const {
return CanBookmarkAppCreateOsShortcuts();
}
void BookmarkAppInstallFinalizer::CreateOsShortcuts(
const web_app::AppId& app_id,
bool add_to_desktop,
CreateOsShortcutsCallback callback) {
const Extension* app = GetEnabledExtension(app_id);
DCHECK(app);
BookmarkAppCreateOsShortcuts(profile_, app, add_to_desktop,
std::move(callback));
}
bool BookmarkAppInstallFinalizer::CanRevealAppShim() const {
#if defined(OS_MACOSX)
return true;
#else // defined(OS_MACOSX)
return false;
#endif // !defined(OS_MACOSX)
}
void BookmarkAppInstallFinalizer::RevealAppShim(const web_app::AppId& app_id) {
DCHECK(CanRevealAppShim());
#if defined(OS_MACOSX)
const Extension* app = GetEnabledExtension(app_id);
DCHECK(app);
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kDisableHostedAppShimCreation)) {
web_app::RevealAppShimInFinderForApp(profile_, app);
}
#endif // defined(OS_MACOSX)
}
bool BookmarkAppInstallFinalizer::CanSkipAppUpdateForSync(
const web_app::AppId& app_id,
const WebApplicationInfo& web_app_info) const {
ExtensionRegistry* extension_registry = ExtensionRegistry::Get(profile_);
DCHECK(extension_registry);
const Extension* extension =
extension_registry->GetInstalledExtension(app_id);
if (!extension)
return false;
// We can skip if there are no bookmark app details that need updating.
// TODO(loyso): We need to check more data fields. crbug.com/949427.
const std::string extension_sync_data_name =
base::UTF16ToUTF8(web_app_info.title);
const std::string bookmark_app_description =
base::UTF16ToUTF8(web_app_info.description);
if (extension->non_localized_name() == extension_sync_data_name &&
extension->description() == bookmark_app_description) {
return true;
}
return false;
}
bool BookmarkAppInstallFinalizer::CanUserUninstallFromSync(
const web_app::AppId& app_id) const {
const Extension* app = GetEnabledExtension(app_id);
DCHECK(app);
return extensions::ExtensionSystem::Get(profile_)
->management_policy()
->UserMayModifySettings(app, nullptr);
}
void BookmarkAppInstallFinalizer::SetCrxInstallerFactoryForTesting(
CrxInstallerFactory crx_installer_factory) {
crx_installer_factory_ = crx_installer_factory;
}
const Extension* BookmarkAppInstallFinalizer::GetEnabledExtension(
const web_app::AppId& app_id) const {
const Extension* app =
ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(app_id);
return app;
}
void BookmarkAppInstallFinalizer::OnExtensionInstalled(
const GURL& app_url,
LaunchType launch_type,
bool is_locally_installed,
web_app::InstallFinalizer::InstallFinalizedCallback callback,
scoped_refptr<CrxInstaller> crx_installer,
const base::Optional<CrxInstallError>& error) {
if (error) {
std::move(callback).Run(web_app::AppId(),
web_app::InstallResultCode::kFailedUnknownReason);
return;
}
const Extension* extension = crx_installer->extension();
DCHECK(extension);
if (extension != GetEnabledExtension(extension->id())) {
std::move(callback).Run(web_app::AppId(),
web_app::InstallResultCode::kWebAppDisabled);
return;
}
DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension), app_url);
SetLaunchType(profile_, extension->id(), launch_type);
SetBookmarkAppIsLocallyInstalled(profile_, extension, is_locally_installed);
registrar().NotifyWebAppInstalled(extension->id());
std::move(callback).Run(extension->id(),
web_app::InstallResultCode::kSuccessNewInstall);
}
} // namespace extensions