blob: 4600d04c32aeeca90af1e6e055a0d35d06736111 [file] [log] [blame]
// Copyright 2018 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/web_applications/web_app_install_finalizer.h"
#include <map>
#include <utility>
#include "base/barrier_callback.h"
#include "base/check_is_test.h"
#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/types/optional_ref.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/commands/web_app_uninstall_command.h"
#include "chrome/browser/web_applications/jobs/uninstall/remove_install_source_job.h"
#include "chrome/browser/web_applications/jobs/uninstall/remove_install_url_job.h"
#include "chrome/browser/web_applications/jobs/uninstall/remove_web_app_job.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
#include "chrome/browser/web_applications/os_integration/web_app_shortcuts_menu.h"
#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_command_manager.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_icon_generator.h"
#include "chrome/browser/web_applications/web_app_icon_manager.h"
#include "chrome/browser/web_applications/web_app_id.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_install_manager.h"
#include "chrome/browser/web_applications/web_app_install_utils.h"
#include "chrome/browser/web_applications/web_app_origin_association_manager.h"
#include "chrome/browser/web_applications/web_app_prefs_utils.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_registry_update.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
#include "chrome/browser/web_applications/web_app_translation_manager.h"
#include "chrome/browser/web_applications/web_app_ui_manager.h"
#include "chrome/browser/web_applications/web_app_utils.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/webapps/browser/uninstall_result_code.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/skia/include/core/SkColor.h"
#include "url/origin.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/system_web_apps/types/system_web_app_data.h"
#endif
namespace web_app {
namespace {
// Overwrite the user display mode if the install source indicates a
// user-initiated installation
bool ShouldInstallOverwriteUserDisplayMode(
webapps::WebappInstallSource source) {
using InstallSource = webapps::WebappInstallSource;
switch (source) {
case InstallSource::MENU_BROWSER_TAB:
case InstallSource::MENU_CUSTOM_TAB:
case InstallSource::AUTOMATIC_PROMPT_BROWSER_TAB:
case InstallSource::AUTOMATIC_PROMPT_CUSTOM_TAB:
case InstallSource::API_BROWSER_TAB:
case InstallSource::API_CUSTOM_TAB:
case InstallSource::AMBIENT_BADGE_BROWSER_TAB:
case InstallSource::AMBIENT_BADGE_CUSTOM_TAB:
case InstallSource::RICH_INSTALL_UI_WEBLAYER:
case InstallSource::ARC:
case InstallSource::CHROME_SERVICE:
case InstallSource::ML_PROMOTION:
case InstallSource::OMNIBOX_INSTALL_ICON:
case InstallSource::MENU_CREATE_SHORTCUT:
case InstallSource::PROFILE_MENU:
return true;
case InstallSource::DEVTOOLS:
case InstallSource::MANAGEMENT_API:
case InstallSource::INTERNAL_DEFAULT:
case InstallSource::ISOLATED_APP_DEV_INSTALL:
case InstallSource::EXTERNAL_DEFAULT:
case InstallSource::EXTERNAL_POLICY:
case InstallSource::EXTERNAL_LOCK_SCREEN:
case InstallSource::SYSTEM_DEFAULT:
case InstallSource::SYNC:
case InstallSource::SUB_APP:
case InstallSource::KIOSK:
case InstallSource::PRELOADED_OEM:
case InstallSource::MICROSOFT_365_SETUP:
return false;
case InstallSource::COUNT:
NOTREACHED();
return false;
}
}
} // namespace
WebAppInstallFinalizer::FinalizeOptions::FinalizeOptions(
webapps::WebappInstallSource install_surface)
: source(ConvertInstallSurfaceToWebAppSource(install_surface)),
install_surface(install_surface) {}
WebAppInstallFinalizer::FinalizeOptions::~FinalizeOptions() = default;
WebAppInstallFinalizer::FinalizeOptions::FinalizeOptions(
const FinalizeOptions&) = default;
WebAppInstallFinalizer::WebAppInstallFinalizer(Profile* profile)
: profile_(profile) {}
WebAppInstallFinalizer::~WebAppInstallFinalizer() = default;
void WebAppInstallFinalizer::FinalizeInstall(
const WebAppInstallInfo& web_app_info,
const FinalizeOptions& options,
InstallFinalizedCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// TODO(crbug.com/1084939): Implement a before-start queue in
// WebAppInstallManager and replace this runtime error in
// WebAppInstallFinalizer with DCHECK(started_).
if (!started_) {
std::move(callback).Run(AppId(),
webapps::InstallResultCode::kWebAppProviderNotReady,
OsHooksErrors());
return;
}
ManifestId manifest_id = web_app_info.manifest_id;
if (manifest_id.is_valid()) {
CHECK(url::Origin::Create(manifest_id)
.IsSameOriginWith(url::Origin::Create(web_app_info.start_url)));
} else {
// TODO(b/280862254): After the manifest id constructor is required, this
// can be removed.
manifest_id = GenerateManifestIdFromStartUrlOnly(web_app_info.start_url);
}
AppId app_id = GenerateAppIdFromManifestId(manifest_id);
OnDidGetWebAppOriginAssociations origin_association_validated_callback =
base::BindOnce(&WebAppInstallFinalizer::OnOriginAssociationValidated,
weak_ptr_factory_.GetWeakPtr(), web_app_info.Clone(),
options, std::move(callback), app_id);
if (options.skip_origin_association_validation ||
web_app_info.scope_extensions.empty() ||
web_app_info.validated_scope_extensions.has_value()) {
std::move(origin_association_validated_callback).Run(ScopeExtensions());
return;
}
provider_->origin_association_manager().GetWebAppOriginAssociations(
manifest_id, web_app_info.scope_extensions,
std::move(origin_association_validated_callback));
}
void WebAppInstallFinalizer::OnOriginAssociationValidated(
WebAppInstallInfo web_app_info,
FinalizeOptions options,
InstallFinalizedCallback callback,
AppId app_id,
ScopeExtensions validated_scope_extensions) {
const WebApp* existing_web_app =
provider_->registrar_unsafe().GetAppById(app_id);
std::unique_ptr<WebApp> web_app;
if (existing_web_app) {
web_app = std::make_unique<WebApp>(*existing_web_app);
} else {
web_app = std::make_unique<WebApp>(app_id);
}
web_app->SetValidatedScopeExtensions(validated_scope_extensions);
if (existing_web_app) {
// There is a chance that existing sources type(s) are user uninstallable
// but the newly added source type is NOT user uninstallable. In this
// case, the following call will unregister os uninstallation.
// TODO(https://crbug.com/1273270): This does NOT block installation, and
// there is a possible edge case here where installation completes before
// this os hook is written. The best place to fix this is to put this code
// is where OS Hooks are called - however that is currently separate from
// this class. See https://crbug.com/1273269.
MaybeUnregisterOsUninstall(web_app.get(), options.source,
provider_->os_integration_manager());
}
// The UI may initiate a full install to overwrite the existing
// non-locally-installed app. Therefore, |is_locally_installed| can be
// promoted to |true|, but not vice versa.
web_app->SetIsLocallyInstalled(web_app->is_locally_installed() ||
options.locally_installed);
// The last install time is always updated if the app has been locally
// installed, but the first install time is updated only once.
const base::Time now_time = base::Time::Now();
if (options.locally_installed && web_app->first_install_time().is_null()) {
web_app->SetFirstInstallTime(now_time);
}
// The last install time is updated whenever we (re)install/update.
if (options.locally_installed) {
web_app->SetLatestInstallTime(now_time);
}
if (!web_app->run_on_os_login_os_integration_state()) {
web_app->SetRunOnOsLoginOsIntegrationState(RunOnOsLoginMode::kNotRun);
}
// Set |user_display_mode| and any user-controllable fields here if this
// install is user initiated or it's a new app.
if (ShouldInstallOverwriteUserDisplayMode(options.install_surface) ||
!existing_web_app) {
DCHECK(web_app_info.user_display_mode.has_value());
web_app->SetUserDisplayMode(*web_app_info.user_display_mode);
}
// `WebApp::chromeos_data` has a default value already. Only override if the
// caller provided a new value.
if (options.chromeos_data.has_value())
web_app->SetWebAppChromeOsData(options.chromeos_data.value());
if (provider_->policy_manager().IsWebAppInDisabledList(app_id) &&
web_app->chromeos_data().has_value() &&
!web_app->chromeos_data()->is_disabled) {
absl::optional<WebAppChromeOsData> cros_data = web_app->chromeos_data();
cros_data->is_disabled = true;
web_app->SetWebAppChromeOsData(std::move(cros_data));
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
// `WebApp::system_web_app_data` has a default value already. Only override if
// the caller provided a new value.
if (options.system_web_app_data.has_value()) {
web_app->client_data()->system_web_app_data =
options.system_web_app_data.value();
}
#endif
if (options.isolated_web_app_location.has_value()) {
UpdateIsolationDataAndResetPendingUpdateInfo(
web_app.get(), *options.isolated_web_app_location,
web_app_info.isolated_web_app_version);
}
web_app->SetParentAppId(web_app_info.parent_app_id);
web_app->SetAdditionalSearchTerms(web_app_info.additional_search_terms);
web_app->AddSource(options.source);
web_app->SetIsFromSyncAndPendingInstallation(false);
web_app->SetLatestInstallSource(options.install_surface);
WriteExternalConfigMapInfo(
*web_app, options.source, web_app_info.is_placeholder,
web_app_info.install_url, web_app_info.additional_policy_ids);
if (!options.locally_installed) {
DCHECK(!(options.add_to_applications_menu || options.add_to_desktop ||
options.add_to_quick_launch_bar))
<< "Cannot create os hooks for a non-locally installed app";
}
CommitCallback commit_callback = base::BindOnce(
&WebAppInstallFinalizer::OnDatabaseCommitCompletedForInstall,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), app_id, options);
if (options.overwrite_existing_manifest_fields || !existing_web_app) {
SetWebAppManifestFieldsAndWriteData(
web_app_info, std::move(web_app), std::move(commit_callback),
options.skip_icon_writes_on_download_failure);
} else {
// Updates the web app with an additional source.
CommitToSyncBridge(std::move(web_app), std::move(commit_callback),
/*success=*/true);
}
}
bool WebAppInstallFinalizer::CanReparentTab(const AppId& app_id,
bool shortcut_created) const {
// Reparent the web contents into its own window only if that is the
// app's launch type.
DCHECK(provider_);
if (provider_->registrar_unsafe().GetAppUserDisplayMode(app_id) ==
mojom::UserDisplayMode::kBrowser) {
return false;
}
return provider_->ui_manager().CanReparentAppTabToWindow(app_id,
shortcut_created);
}
void WebAppInstallFinalizer::ReparentTab(const AppId& app_id,
bool shortcut_created,
content::WebContents* web_contents) {
DCHECK(web_contents);
return provider_->ui_manager().ReparentAppTabToWindow(web_contents, app_id,
shortcut_created);
}
void WebAppInstallFinalizer::FinalizeUpdate(
const WebAppInstallInfo& web_app_info,
InstallFinalizedCallback callback) {
CHECK(started_);
CHECK(web_app_info.start_url.is_valid());
ManifestId manifest_id = web_app_info.manifest_id;
if (manifest_id.is_valid()) {
CHECK(url::Origin::Create(manifest_id)
.IsSameOriginWith(url::Origin::Create(web_app_info.start_url)));
} else {
// TODO(b/280862254): After the manifest id constructor is required, this
// can be removed.
CHECK_IS_TEST();
manifest_id = GenerateManifestIdFromStartUrlOnly(web_app_info.start_url);
}
CHECK(manifest_id.is_valid());
const AppId app_id = GenerateAppIdFromManifestId(manifest_id);
const WebApp* existing_web_app =
provider_->registrar_unsafe().GetAppById(app_id);
if (!existing_web_app ||
existing_web_app->is_from_sync_and_pending_installation() ||
app_id != existing_web_app->app_id()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), AppId(),
webapps::InstallResultCode::kWebAppDisabled,
OsHooksErrors()));
return;
}
CommitCallback commit_callback = base::BindOnce(
&WebAppInstallFinalizer::OnDatabaseCommitCompletedForUpdate,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), app_id,
provider_->registrar_unsafe().GetAppShortName(app_id),
GetFileHandlerUpdateAction(app_id, web_app_info), web_app_info.Clone());
auto web_app = std::make_unique<WebApp>(*existing_web_app);
if (web_app->isolation_data().has_value()) {
base::optional_ref<const WebApp::IsolationData::PendingUpdateInfo>
pending_update_info = web_app->isolation_data()->pending_update_info();
CHECK(pending_update_info.has_value())
<< "Isolated Web Apps can only be updated if "
"`WebApp::IsolationData::PendingUpdateInfo` is set.";
CHECK_EQ(web_app_info.isolated_web_app_version,
pending_update_info->version);
UpdateIsolationDataAndResetPendingUpdateInfo(web_app.get(),
pending_update_info->location,
pending_update_info->version);
}
// Prepare copy-on-write to update existing app.
// This is not reached unless the data obtained from the manifest
// update process is valid, so an invariant of the system is that
// icons are valid here.
SetWebAppManifestFieldsAndWriteData(
web_app_info, std::move(web_app), std::move(commit_callback),
/*skip_icon_writes_on_download_failure=*/false);
}
void WebAppInstallFinalizer::SetProvider(base::PassKey<WebAppProvider>,
WebAppProvider& provider) {
provider_ = &provider;
}
void WebAppInstallFinalizer::Start() {
DCHECK(!started_);
started_ = true;
}
void WebAppInstallFinalizer::Shutdown() {
started_ = false;
}
void WebAppInstallFinalizer::UpdateIsolationDataAndResetPendingUpdateInfo(
WebApp* web_app,
const IsolatedWebAppLocation& location,
const base::Version& version) {
CHECK(version.IsValid());
// If previous `controlled_frame_partitions` exist, keep them the same. This
// can only happen during an update, and never during an install.
std::set<std::string> controlled_frame_partitions;
if (web_app->isolation_data().has_value()) {
controlled_frame_partitions =
web_app->isolation_data()->controlled_frame_partitions;
}
web_app->SetIsolationData(WebApp::IsolationData(
location, version, controlled_frame_partitions,
// Always reset `pending_update_info`, because reaching this point means
// that an install or update just succeeded.
/*pending_update_info=*/absl::nullopt));
}
void WebAppInstallFinalizer::SetWebAppManifestFieldsAndWriteData(
const WebAppInstallInfo& web_app_info,
std::unique_ptr<WebApp> web_app,
CommitCallback commit_callback,
bool skip_icon_writes_on_download_failure) {
SetWebAppManifestFields(web_app_info, *web_app,
skip_icon_writes_on_download_failure);
AppId app_id = web_app->app_id();
auto write_translations_callback = base::BindOnce(
&WebAppInstallFinalizer::WriteTranslations,
weak_ptr_factory_.GetWeakPtr(), app_id, web_app_info.translations);
auto commit_to_sync_bridge_callback =
base::BindOnce(&WebAppInstallFinalizer::CommitToSyncBridge,
weak_ptr_factory_.GetWeakPtr(), std::move(web_app));
auto on_icon_write_complete_callback =
base::BindOnce(std::move(write_translations_callback),
base::BindOnce(std::move(commit_to_sync_bridge_callback),
std::move(commit_callback)));
// Do not overwrite the icon data in the DB if icon downloading has failed. We
// skip directly to writing translations and then writing the app via the
// WebAppSyncBridge.
if (skip_icon_writes_on_download_failure) {
std::move(on_icon_write_complete_callback).Run(/*success=*/true);
} else {
IconBitmaps icon_bitmaps = web_app_info.icon_bitmaps;
ShortcutsMenuIconBitmaps shortcuts_menu_icon_bitmaps =
web_app_info.shortcuts_menu_icon_bitmaps;
IconsMap other_icon_bitmaps = web_app_info.other_icon_bitmaps;
provider_->icon_manager().WriteData(
app_id, std::move(icon_bitmaps), std::move(shortcuts_menu_icon_bitmaps),
std::move(other_icon_bitmaps),
std::move(on_icon_write_complete_callback));
}
}
void WebAppInstallFinalizer::WriteTranslations(
const AppId& app_id,
const base::flat_map<std::string, blink::Manifest::TranslationItem>&
translations,
CommitCallback commit_callback,
bool success) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!success) {
std::move(commit_callback).Run(success);
return;
}
provider_->translation_manager().WriteTranslations(
app_id, translations, std::move(commit_callback));
}
void WebAppInstallFinalizer::CommitToSyncBridge(std::unique_ptr<WebApp> web_app,
CommitCallback commit_callback,
bool success) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!success) {
std::move(commit_callback).Run(success);
return;
}
AppId app_id = web_app->app_id();
ScopedRegistryUpdate update =
provider_->sync_bridge_unsafe().BeginUpdate(std::move(commit_callback));
WebApp* app_to_override = update->UpdateApp(app_id);
if (app_to_override) {
*app_to_override = std::move(*web_app);
} else {
update->CreateApp(std::move(web_app));
}
}
void WebAppInstallFinalizer::OnDatabaseCommitCompletedForInstall(
InstallFinalizedCallback callback,
AppId app_id,
FinalizeOptions finalize_options,
bool success) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!success) {
std::move(callback).Run(
AppId(), webapps::InstallResultCode::kWriteDataFailed, OsHooksErrors());
return;
}
provider_->install_manager().NotifyWebAppInstalled(app_id);
const WebApp* web_app = provider_->registrar_unsafe().GetAppById(app_id);
// TODO(dmurph): Verify this check is not needed and remove after
// isolation work is done. https://crbug.com/1298130
if (!web_app) {
std::move(callback).Run(
AppId(), webapps::InstallResultCode::kAppNotInRegistrarAfterCommit,
OsHooksErrors());
return;
}
#if BUILDFLAG(IS_CHROMEOS) // Deeper OS integration is expected on ChromeOS.
const bool should_install_os_hooks = !finalize_options.bypass_os_hooks;
#else
const bool should_install_os_hooks =
!finalize_options.bypass_os_hooks &&
!web_app->HasOnlySource(WebAppManagement::Type::kDefault) &&
finalize_options.locally_installed;
#endif // BUILDFLAG(IS_CHROMEOS)
if (!should_install_os_hooks) {
std::move(callback).Run(app_id,
webapps::InstallResultCode::kSuccessNewInstall,
OsHooksErrors());
return;
}
InstallOsHooksOptions hooks_options;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
hooks_options.os_hooks[OsHookType::kUrlHandlers] = true;
#else
hooks_options.os_hooks[OsHookType::kUrlHandlers] = false;
#endif
hooks_options.os_hooks[OsHookType::kShortcuts] =
finalize_options.add_to_applications_menu;
hooks_options.os_hooks[OsHookType::kShortcutsMenu] =
finalize_options.add_to_applications_menu;
{
RunOnOsLoginMode current_mode =
provider_->registrar_unsafe().GetAppRunOnOsLoginMode(app_id).value;
hooks_options.os_hooks[OsHookType::kRunOnOsLogin] =
current_mode == RunOnOsLoginMode::kWindowed;
}
hooks_options.add_to_quick_launch_bar =
finalize_options.add_to_quick_launch_bar;
hooks_options.add_to_desktop = finalize_options.add_to_desktop;
// Apps that can't be uninstalled from users shouldn't register to
// OS Settings.
hooks_options.os_hooks[OsHookType::kUninstallationViaOsSettings] =
web_app->CanUserUninstallWebApp();
hooks_options.os_hooks[OsHookType::kFileHandlers] = true;
hooks_options.os_hooks[OsHookType::kProtocolHandlers] = true;
switch (finalize_options.source) {
case WebAppManagement::kSystem:
case WebAppManagement::kPolicy:
case WebAppManagement::kDefault:
case WebAppManagement::kOem:
hooks_options.reason = SHORTCUT_CREATION_AUTOMATED;
break;
case WebAppManagement::kKiosk:
case WebAppManagement::kSubApp:
case WebAppManagement::kWebAppStore:
case WebAppManagement::kOneDriveIntegration:
case WebAppManagement::kSync:
case WebAppManagement::kCommandLine:
hooks_options.reason = SHORTCUT_CREATION_BY_USER;
break;
}
auto os_hooks_barrier =
OsIntegrationManager::GetBarrierForSynchronize(base::BindOnce(
&WebAppInstallFinalizer::OnInstallHooksFinished,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), app_id));
// TODO(crbug.com/1401125): Remove InstallOsHooks() once OS integration
// sub managers have been implemented.
provider_->os_integration_manager().InstallOsHooks(
app_id, os_hooks_barrier, /*web_app_info=*/nullptr, hooks_options);
SynchronizeOsOptions synchronize_options;
synchronize_options.add_shortcut_to_desktop = hooks_options.add_to_desktop;
synchronize_options.add_to_quick_launch_bar =
hooks_options.add_to_quick_launch_bar;
synchronize_options.reason = hooks_options.reason;
provider_->os_integration_manager().Synchronize(
app_id, base::BindOnce(os_hooks_barrier, OsHooksErrors()),
synchronize_options);
}
void WebAppInstallFinalizer::OnInstallHooksFinished(
InstallFinalizedCallback callback,
AppId app_id,
OsHooksErrors os_hooks_errors) {
auto joined = std::move(callback).Then(
base::BindOnce(&WebAppInstallFinalizer::NotifyWebAppInstalledWithOsHooks,
weak_ptr_factory_.GetWeakPtr(), app_id));
std::move(joined).Run(app_id, webapps::InstallResultCode::kSuccessNewInstall,
os_hooks_errors);
}
void WebAppInstallFinalizer::NotifyWebAppInstalledWithOsHooks(AppId app_id) {
provider_->install_manager().NotifyWebAppInstalledWithOsHooks(app_id);
}
bool WebAppInstallFinalizer::ShouldUpdateOsHooks(const AppId& app_id) {
#if BUILDFLAG(IS_CHROMEOS)
// OS integration should always be enabled on ChromeOS.
return true;
#else
// If the app being updated was installed by default and not also manually
// installed by the user or an enterprise policy, disable os integration.
return !provider_->registrar_unsafe().WasInstalledByDefaultOnly(app_id);
#endif // BUILDFLAG(IS_CHROMEOS)
}
void WebAppInstallFinalizer::OnDatabaseCommitCompletedForUpdate(
InstallFinalizedCallback callback,
AppId app_id,
std::string old_name,
FileHandlerUpdateAction file_handlers_need_os_update,
const WebAppInstallInfo& web_app_info,
bool success) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!success) {
std::move(callback).Run(
AppId(), webapps::InstallResultCode::kWriteDataFailed, OsHooksErrors());
return;
}
if (!ShouldUpdateOsHooks(app_id)) {
provider_->install_manager().NotifyWebAppManifestUpdated(app_id);
std::move(callback).Run(
app_id, webapps::InstallResultCode::kSuccessAlreadyInstalled,
OsHooksErrors());
return;
}
auto os_hooks_barrier =
OsIntegrationManager::GetBarrierForSynchronize(base::BindOnce(
&WebAppInstallFinalizer::OnUpdateHooksFinished,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), app_id));
// TODO(crbug.com/1401125): Remove UpdateOsHooks() once OS integration
// sub managers have been implemented.
provider_->os_integration_manager().UpdateOsHooks(
app_id, old_name, file_handlers_need_os_update, web_app_info,
os_hooks_barrier);
provider_->os_integration_manager().Synchronize(
app_id, base::BindOnce(os_hooks_barrier, OsHooksErrors()));
}
void WebAppInstallFinalizer::OnUpdateHooksFinished(
InstallFinalizedCallback callback,
AppId app_id,
OsHooksErrors os_hooks_errors) {
provider_->install_manager().NotifyWebAppManifestUpdated(app_id);
std::move(callback).Run(
app_id,
os_hooks_errors.any()
? webapps::InstallResultCode::kUpdateTaskFailed
: webapps::InstallResultCode::kSuccessAlreadyInstalled,
os_hooks_errors);
}
void WebAppInstallFinalizer::WriteExternalConfigMapInfo(
WebApp& web_app,
WebAppManagement::Type source,
bool is_placeholder,
GURL install_url,
std::vector<std::string> additional_policy_ids) {
DCHECK(!(source == WebAppManagement::Type::kSync && is_placeholder));
if (source != WebAppManagement::Type::kSync) {
web_app.AddPlaceholderInfoToManagementExternalConfigMap(source,
is_placeholder);
if (install_url.is_valid()) {
web_app.AddInstallURLToManagementExternalConfigMap(
source, std::move(install_url));
}
for (const auto& policy_id : additional_policy_ids) {
web_app.AddPolicyIdToManagementExternalConfigMap(source,
std::move(policy_id));
}
}
}
FileHandlerUpdateAction WebAppInstallFinalizer::GetFileHandlerUpdateAction(
const AppId& app_id,
const WebAppInstallInfo& new_web_app_info) {
if (provider_->registrar_unsafe().GetAppFileHandlerApprovalState(app_id) ==
ApiApprovalState::kDisallowed) {
return FileHandlerUpdateAction::kNoUpdate;
}
// TODO(https://crbug.com/1197013): Consider trying to re-use the comparison
// results from the ManifestUpdateDataFetchCommand.
const apps::FileHandlers* old_handlers =
provider_->registrar_unsafe().GetAppFileHandlers(app_id);
DCHECK(old_handlers);
if (*old_handlers == new_web_app_info.file_handlers)
return FileHandlerUpdateAction::kNoUpdate;
return FileHandlerUpdateAction::kUpdate;
}
} // namespace web_app