blob: ae78597fad75303869233692189d7434d082465d [file] [log] [blame]
// Copyright 2019 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 <utility>
#include "chrome/browser/web_applications/web_app_install_task.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/components/install_finalizer.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/components/web_app_data_retriever.h"
#include "chrome/browser/web_applications/components/web_app_icon_generator.h"
#include "chrome/browser/web_applications/components/web_app_utils.h"
#include "chrome/common/web_application_info.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/common/manifest/manifest.h"
namespace web_app {
WebAppInstallTask::WebAppInstallTask(Profile* profile,
InstallFinalizer* install_finalizer)
: data_retriever_(std::make_unique<WebAppDataRetriever>()),
install_finalizer_(install_finalizer),
profile_(profile) {}
WebAppInstallTask::~WebAppInstallTask() = default;
void WebAppInstallTask::InstallWebAppFromManifest(
content::WebContents* contents,
WebappInstallSource install_source,
InstallManager::WebAppInstallDialogCallback dialog_callback,
InstallManager::OnceInstallCallback install_callback) {
CheckInstallPreconditions();
Observe(contents);
dialog_callback_ = std::move(dialog_callback);
install_callback_ = std::move(install_callback);
install_source_ = install_source;
auto web_app_info = std::make_unique<WebApplicationInfo>();
data_retriever_->CheckInstallabilityAndRetrieveManifest(
web_contents(),
base::BindOnce(&WebAppInstallTask::OnDidPerformInstallableCheck,
base::Unretained(this), std::move(web_app_info),
/*force_shortcut_app=*/false));
}
void WebAppInstallTask::InstallWebAppFromManifestWithFallback(
content::WebContents* contents,
bool force_shortcut_app,
WebappInstallSource install_source,
InstallManager::WebAppInstallDialogCallback dialog_callback,
InstallManager::OnceInstallCallback install_callback) {
CheckInstallPreconditions();
Observe(contents);
dialog_callback_ = std::move(dialog_callback);
install_callback_ = std::move(install_callback);
install_source_ = install_source;
data_retriever_->GetWebApplicationInfo(
web_contents(),
base::BindOnce(&WebAppInstallTask::OnGetWebApplicationInfo,
base::Unretained(this), force_shortcut_app));
}
void WebAppInstallTask::WebContentsDestroyed() {
CallInstallCallback(AppId(), InstallResultCode::kWebContentsDestroyed);
}
void WebAppInstallTask::SetDataRetrieverForTesting(
std::unique_ptr<WebAppDataRetriever> data_retriever) {
data_retriever_ = std::move(data_retriever);
}
void WebAppInstallTask::SetInstallFinalizerForTesting(
InstallFinalizer* install_finalizer) {
install_finalizer_ = install_finalizer;
}
void WebAppInstallTask::CheckInstallPreconditions() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(AreWebAppsUserInstallable(profile_));
// Concurrent calls are not allowed.
DCHECK(!web_contents());
CHECK(!install_callback_);
}
void WebAppInstallTask::CallInstallCallback(const AppId& app_id,
InstallResultCode code) {
Observe(nullptr);
dialog_callback_.Reset();
DCHECK(install_source_ != kNoInstallSource);
install_source_ = kNoInstallSource;
DCHECK(install_callback_);
std::move(install_callback_).Run(app_id, code);
}
bool WebAppInstallTask::ShouldStopInstall() const {
// Install should stop early if WebContents is being destroyed.
// WebAppInstallTask::WebContentsDestroyed will get called eventually and the
// callback will be invoked at that point.
return !web_contents() || web_contents()->IsBeingDestroyed();
}
void WebAppInstallTask::OnGetWebApplicationInfo(
bool force_shortcut_app,
std::unique_ptr<WebApplicationInfo> web_app_info) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (ShouldStopInstall())
return;
if (!web_app_info) {
CallInstallCallback(AppId(),
InstallResultCode::kGetWebApplicationInfoFailed);
return;
}
data_retriever_->CheckInstallabilityAndRetrieveManifest(
web_contents(),
base::BindOnce(&WebAppInstallTask::OnDidPerformInstallableCheck,
base::Unretained(this), std::move(web_app_info),
force_shortcut_app));
}
void WebAppInstallTask::OnDidPerformInstallableCheck(
std::unique_ptr<WebApplicationInfo> web_app_info,
bool force_shortcut_app,
const blink::Manifest& manifest,
bool is_installable) {
if (ShouldStopInstall())
return;
DCHECK(web_app_info);
const auto for_installable_site = is_installable && !force_shortcut_app
? ForInstallableSite::kYes
: ForInstallableSite::kNo;
// TODO(loyso): Implement UpdateShareTargetInPrefs installation logic.
UpdateWebAppInfoFromManifest(manifest, web_app_info.get(),
for_installable_site);
std::vector<GURL> icon_urls;
for (auto& icon_info : web_app_info->icons) {
if (icon_info.url.is_valid())
icon_urls.push_back(icon_info.url);
}
// If the manifest specified icons, don't use the page icons.
const bool skip_page_fav_icons = !manifest.icons.empty();
data_retriever_->GetIcons(
web_contents(), icon_urls, skip_page_fav_icons,
base::BindOnce(&WebAppInstallTask::OnIconsRetrieved,
base::Unretained(this), std::move(web_app_info),
for_installable_site));
}
void WebAppInstallTask::OnIconsRetrieved(
std::unique_ptr<WebApplicationInfo> web_app_info,
ForInstallableSite for_installable_site,
IconsMap icons_map) {
if (ShouldStopInstall())
return;
DCHECK(web_app_info);
std::vector<BitmapAndSource> downloaded_icons =
FilterSquareIcons(icons_map, *web_app_info);
ResizeDownloadedIconsGenerateMissing(std::move(downloaded_icons),
web_app_info.get());
std::move(dialog_callback_)
.Run(
web_contents(), std::move(web_app_info), for_installable_site,
base::BindOnce(&WebAppInstallTask::OnDialogCompleted,
weak_ptr_factory_.GetWeakPtr(), for_installable_site));
}
void WebAppInstallTask::OnDialogCompleted(
ForInstallableSite for_installable_site,
bool user_accepted,
std::unique_ptr<WebApplicationInfo> web_app_info) {
if (ShouldStopInstall())
return;
if (!user_accepted) {
CallInstallCallback(AppId(), InstallResultCode::kUserInstallDeclined);
return;
}
WebApplicationInfo web_app_info_copy = *web_app_info;
DCHECK(install_source_ != kNoInstallSource);
// This metric is recorded regardless of the installation result.
if (InstallableMetrics::IsReportableInstallSource(install_source_) &&
for_installable_site == ForInstallableSite::kYes) {
InstallableMetrics::TrackInstallEvent(install_source_);
}
InstallFinalizer::FinalizeOptions options;
install_finalizer_->FinalizeInstall(
web_app_info_copy, options,
base::BindOnce(&WebAppInstallTask::OnInstallFinalized,
weak_ptr_factory_.GetWeakPtr(), std::move(web_app_info)));
// Check that the finalizer hasn't called OnInstallFinalized synchronously:
DCHECK(install_callback_);
}
void WebAppInstallTask::OnInstallFinalized(
std::unique_ptr<WebApplicationInfo> web_app_info,
const AppId& app_id,
InstallResultCode code) {
if (ShouldStopInstall())
return;
if (code != InstallResultCode::kSuccess) {
CallInstallCallback(app_id, code);
return;
}
RecordAppBanner(web_contents(), web_app_info->app_url);
// TODO(loyso): Implement |create_shortcuts| to skip OS shortcuts creation.
auto create_shortcuts_callback = base::BindOnce(
&WebAppInstallTask::OnShortcutsCreated, weak_ptr_factory_.GetWeakPtr(),
std::move(web_app_info), app_id);
if (install_finalizer_->CanCreateOsShortcuts()) {
install_finalizer_->CreateOsShortcuts(app_id,
std::move(create_shortcuts_callback));
} else {
std::move(create_shortcuts_callback).Run(false /* created_shortcuts */);
}
}
void WebAppInstallTask::OnShortcutsCreated(
std::unique_ptr<WebApplicationInfo> web_app_info,
const AppId& app_id,
bool shortcut_created) {
if (ShouldStopInstall())
return;
if (install_finalizer_->CanPinAppToShelf())
install_finalizer_->PinAppToShelf(app_id);
// TODO(loyso): Implement |reparent_tab| to skip tab reparenting logic.
if (web_app_info->open_as_window &&
install_finalizer_->CanReparentTab(app_id, shortcut_created)) {
install_finalizer_->ReparentTab(app_id, web_contents());
}
if (install_finalizer_->CanRevealAppShim())
install_finalizer_->RevealAppShim(app_id);
CallInstallCallback(app_id, InstallResultCode::kSuccess);
}
} // namespace web_app