blob: a67a68e653eb43409011a52eeb7247395854ba5a [file] [log] [blame]
// Copyright 2023 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/commands/fetch_install_info_from_install_url_command.h"
#include <memory>
#include <optional>
#include "base/check_op.h"
#include "base/feature_list.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/web_applications/locks/shared_web_contents_lock.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_operations.h"
#include "chrome/browser/web_applications/web_app_install_utils.h"
#include "chrome/browser/web_applications/web_contents/web_app_data_retriever.h"
#include "chrome/browser/web_applications/web_contents/web_contents_manager.h"
#include "chrome/common/chrome_features.h"
#include "components/webapps/browser/web_contents/web_app_url_loader.h"
#include "content/public/browser/web_contents.h"
#include "url/origin.h"
namespace web_app {
std::ostream& operator<<(std::ostream& os, FetchInstallInfoResult result) {
switch (result) {
case FetchInstallInfoResult::kAppInfoObtained:
return os << "kAppInfoObtained";
case FetchInstallInfoResult::kWebContentsDestroyed:
return os << "kWebContentsDestroyed";
case FetchInstallInfoResult::kInstallUrlInvalid:
return os << "kInstallUrlInvalid";
case FetchInstallInfoResult::kManifestIdInvalid:
return os << "kManifestIdInvalid";
case FetchInstallInfoResult::kParentManifestIdInvalid:
return os << "kParentManifestIdInvalid";
case FetchInstallInfoResult::kUrlLoadingFailure:
return os << "kUrlLoadingFailure";
case FetchInstallInfoResult::kNoValidManifest:
return os << "kNoValidManifest";
case FetchInstallInfoResult::kWrongManifestId:
return os << "kWrongManifestId";
case FetchInstallInfoResult::kFailure:
return os << "kFailure";
}
}
bool FetchInstallInfoFromInstallUrlCommand::
FetchInstallInfoFromInstallUrlCommand::IsWebContentsDestroyed() {
return lock_->shared_web_contents().IsBeingDestroyed();
}
FetchInstallInfoFromInstallUrlCommand::FetchInstallInfoFromInstallUrlCommand(
webapps::ManifestId manifest_id,
GURL install_url,
std::optional<webapps::ManifestId> parent_manifest_id,
base::OnceCallback<void(std::unique_ptr<WebAppInstallInfo>)> callback)
: WebAppCommand<SharedWebContentsLock, std::unique_ptr<WebAppInstallInfo>>(
"FetchInstallInfoFromInstallUrlCommand",
SharedWebContentsLockDescription(),
std::move(callback),
/*args_for_shutdown=*/nullptr),
manifest_id_(manifest_id),
install_url_(install_url),
parent_manifest_id_(parent_manifest_id),
install_error_log_entry_(/*background_installation=*/true,
webapps::WebappInstallSource::SUB_APP) {
CHECK(manifest_id_.is_valid());
CHECK(install_url_.is_valid());
if (parent_manifest_id_.has_value()) {
CHECK(parent_manifest_id_.value().is_valid());
CHECK(url::Origin::Create(manifest_id_)
.IsSameOriginWith(
url::Origin::Create(parent_manifest_id_.value())));
CHECK_NE(parent_manifest_id_.value(), manifest_id_);
}
GetMutableDebugValue().Set("manifest_id", manifest_id_.spec());
GetMutableDebugValue().Set("parent_manifest_id",
parent_manifest_id_.value_or(GURL("")).spec());
GetMutableDebugValue().Set("install_url", install_url_.spec());
}
FetchInstallInfoFromInstallUrlCommand::
~FetchInstallInfoFromInstallUrlCommand() = default;
void FetchInstallInfoFromInstallUrlCommand::StartWithLock(
std::unique_ptr<SharedWebContentsLock> lock) {
lock_ = std::move(lock);
url_loader_ = lock_->web_contents_manager().CreateUrlLoader();
data_retriever_ = lock_->web_contents_manager().CreateDataRetriever();
if (IsWebContentsDestroyed()) {
CompleteCommandAndSelfDestruct(
FetchInstallInfoResult::kWebContentsDestroyed,
/*install_info=*/nullptr);
return;
}
if (!install_url_.is_valid()) {
CompleteCommandAndSelfDestruct(FetchInstallInfoResult::kInstallUrlInvalid,
/*install_info=*/nullptr);
return;
}
if (!manifest_id_.is_valid()) {
CompleteCommandAndSelfDestruct(FetchInstallInfoResult::kManifestIdInvalid,
/*install_info=*/nullptr);
return;
}
url_loader_->LoadUrl(
install_url_, &lock_->shared_web_contents(),
webapps::WebAppUrlLoader::UrlComparison::kIgnoreQueryParamsAndRef,
base::BindOnce(&FetchInstallInfoFromInstallUrlCommand::
OnWebAppUrlLoadedGetWebAppInstallInfo,
weak_ptr_factory_.GetWeakPtr()));
}
void FetchInstallInfoFromInstallUrlCommand::
OnWebAppUrlLoadedGetWebAppInstallInfo(
webapps::WebAppUrlLoaderResult result) {
GetMutableDebugValue().Set("url_loading_result",
ConvertUrlLoaderResultToString(result));
if (result != webapps::WebAppUrlLoaderResult::kUrlLoaded) {
install_error_log_entry_.LogUrlLoaderError(
"OnWebAppUrlLoadedGetWebAppInstallInfo", install_url_.spec(), result);
CompleteCommandAndSelfDestruct(FetchInstallInfoResult::kUrlLoadingFailure,
/*install_info=*/nullptr);
return;
}
data_retriever_->GetWebAppInstallInfo(
&lock_->shared_web_contents(),
base::BindOnce(
&FetchInstallInfoFromInstallUrlCommand::OnGetWebAppInstallInfo,
weak_ptr_factory_.GetWeakPtr()));
}
void FetchInstallInfoFromInstallUrlCommand::OnGetWebAppInstallInfo(
std::unique_ptr<WebAppInstallInfo> install_info) {
if (!install_info) {
CompleteCommandAndSelfDestruct(FetchInstallInfoResult::kFailure,
/*install_info=*/nullptr);
return;
}
install_info->start_url = install_url_;
install_info->install_url = install_url_;
install_info->parent_app_manifest_id = parent_manifest_id_;
data_retriever_->CheckInstallabilityAndRetrieveManifest(
&lock_->shared_web_contents(),
base::BindOnce(
&FetchInstallInfoFromInstallUrlCommand::OnManifestRetrieved,
weak_ptr_factory_.GetWeakPtr(), std::move(install_info)));
}
void FetchInstallInfoFromInstallUrlCommand::OnManifestRetrieved(
std::unique_ptr<WebAppInstallInfo> web_app_info,
blink::mojom::ManifestPtr opt_manifest,
const GURL& manifest_url,
bool valid_manifest_for_web_app,
webapps::InstallableStatusCode error_code) {
CHECK(web_app_info);
if (!valid_manifest_for_web_app) {
LOG(WARNING) << "Did not install " << install_url_.spec()
<< " because it didn't have a manifest for web app";
CompleteCommandAndSelfDestruct(FetchInstallInfoResult::kNoValidManifest,
/*install_info=*/nullptr);
return;
}
if (opt_manifest) {
UpdateWebAppInfoFromManifest(*opt_manifest, manifest_url,
web_app_info.get());
}
webapps::AppId app_id = GenerateAppIdFromManifestId(
web_app_info->manifest_id, web_app_info->parent_app_manifest_id);
const webapps::AppId expected_app_id = GenerateAppIdFromManifestId(
manifest_id_, web_app_info->parent_app_manifest_id);
if (app_id != expected_app_id) {
install_error_log_entry_.LogExpectedAppIdError(
"OnManifestRetrieved", web_app_info->start_url.spec(), app_id,
expected_app_id);
CompleteCommandAndSelfDestruct(FetchInstallInfoResult::kWrongManifestId,
/*install_info=*/nullptr);
return;
}
// If the manifest specified icons, don't use the page icons.
const bool skip_page_favicons = opt_manifest && !opt_manifest->icons.empty();
IconUrlSizeSet icon_urls = GetValidIconUrlsToDownload(*web_app_info);
data_retriever_->GetIcons(
&lock_->shared_web_contents(), std::move(icon_urls), skip_page_favicons,
/*fail_all_if_any_fail=*/false,
base::BindOnce(&FetchInstallInfoFromInstallUrlCommand::OnIconsRetrieved,
weak_ptr_factory_.GetWeakPtr(), std::move(web_app_info)));
}
void FetchInstallInfoFromInstallUrlCommand::OnIconsRetrieved(
std::unique_ptr<WebAppInstallInfo> web_app_info,
IconsDownloadedResult result,
IconsMap icons_map,
DownloadedIconsHttpResults icons_http_results) {
CHECK(web_app_info);
PopulateProductIcons(web_app_info.get(), &icons_map);
PopulateOtherIcons(web_app_info.get(), icons_map);
RecordDownloadedIconsResultAndHttpStatusCodes(result, icons_http_results);
CompleteCommandAndSelfDestruct(FetchInstallInfoResult::kAppInfoObtained,
std::move(web_app_info));
}
void FetchInstallInfoFromInstallUrlCommand::CompleteCommandAndSelfDestruct(
FetchInstallInfoResult result,
std::unique_ptr<WebAppInstallInfo> install_info) {
GetMutableDebugValue().Set("command_result", base::ToString(result));
CommandResult command_result = [&] {
switch (result) {
case FetchInstallInfoResult::kAppInfoObtained:
return CommandResult::kSuccess;
default:
return CommandResult::kFailure;
}
}();
if (base::FeatureList::IsEnabled(features::kRecordWebAppDebugInfo) &&
install_error_log_entry_.HasErrorDict()) {
command_manager()->LogToInstallManager(
install_error_log_entry_.TakeErrorDict());
}
CompleteAndSelfDestruct(command_result, std::move(install_info));
}
} // namespace web_app