blob: 3dd2a80a2a400a5ebaf1d18d6d4fae36abf9916d [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/password_manager/web_app_profile_switcher.h"
#include <memory>
#include <optional>
#include "ash/constants/web_app_id_constants.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/web_applications/locks/app_lock.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom-shared.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_icon_manager.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/webapps/browser/install_result_code.h"
#include "content/public/browser/browser_thread.h"
namespace {
std::unique_ptr<web_app::WebAppInstallInfo> MakeInstallInfoFromApp(
const web_app::WebApp& web_app) {
auto install_info = std::make_unique<web_app::WebAppInstallInfo>(
web_app.manifest_id(), web_app.start_url());
install_info->title = base::UTF8ToUTF16(web_app.untranslated_name());
install_info->description =
base::UTF8ToUTF16(web_app.untranslated_description());
install_info->manifest_url = web_app.manifest_url();
install_info->scope = web_app.scope();
install_info->manifest_icons = web_app.manifest_icons();
install_info->trusted_icons = web_app.trusted_icons();
install_info->display_mode = web_app.display_mode();
return install_info;
}
} // namespace
WebAppProfileSwitcher::WebAppProfileSwitcher(const webapps::AppId& app_id,
Profile& active_profile,
base::OnceClosure on_completion)
: app_id_(app_id),
active_profile_(active_profile),
on_completion_(std::move(on_completion)),
weak_factory_(this) {
profiles_observation_.AddObservation(&active_profile);
}
WebAppProfileSwitcher::~WebAppProfileSwitcher() = default;
void WebAppProfileSwitcher::SwitchToProfile(
const base::FilePath& profile_to_open) {
base::OnceCallback<void(Profile*)> open_web_app_callback = base::BindOnce(
&WebAppProfileSwitcher::QueryProfileWebAppRegistryToOpenWebApp,
weak_factory_.GetWeakPtr());
profiles::LoadProfileAsync(profile_to_open, std::move(open_web_app_callback));
if (app_id_ == ash::kPasswordManagerAppId) {
base::UmaHistogramEnumeration(
"PasswordManager.ShortcutMetric",
password_manager::metrics_util::PasswordManagerShortcutMetric::
kProfileSwitched);
}
}
void WebAppProfileSwitcher::OnProfileWillBeDestroyed(Profile* profile) {
// If any of observed profiles is destroyed before the switch is completed,
// the profile switcher should be destroyed.
weak_factory_.InvalidateWeakPtrs();
RunCompletionCallback();
}
void WebAppProfileSwitcher::QueryProfileWebAppRegistryToOpenWebApp(
Profile* new_profile) {
CHECK(!new_profile->IsGuestSession());
new_profile_ = new_profile;
profiles_observation_.AddObservation(new_profile);
auto* provider = web_app::WebAppProvider::GetForWebApps(new_profile);
CHECK(provider);
provider->scheduler().ScheduleCallback(
"QueryProfileWebAppRegistryToOpenWebApp",
web_app::AppLockDescription(app_id_),
base::BindOnce(
&WebAppProfileSwitcher::InstallOrOpenWebAppWindowForProfile,
weak_factory_.GetWeakPtr()),
/*on_complete=*/base::DoNothing());
}
// TODO(crbug.com/379136842): Verify the allowed states called within
// IsInstallState() here.
void WebAppProfileSwitcher::InstallOrOpenWebAppWindowForProfile(
web_app::AppLock& new_profile_lock,
base::Value::Dict& debug_value) {
if (new_profile_lock.registrar().IsInstallState(
app_id_,
{web_app::proto::InstallState::SUGGESTED_FROM_ANOTHER_DEVICE,
web_app::proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION,
web_app::proto::InstallState::INSTALLED_WITH_OS_INTEGRATION})) {
// The web app is already installed and can be launched, or foregrounded,
// if it's already launched.
Browser* launched_app =
web_app::AppBrowserController::FindForWebApp(*new_profile_, app_id_);
debug_value.Set("launched_app", !!launched_app);
if (launched_app) {
launched_app->window()->Activate();
RunCompletionCallback();
} else {
LaunchAppWithId(app_id_,
webapps::InstallResultCode::kSuccessAlreadyInstalled);
}
return;
}
// Fetch app icons from the already installed app prior to
// installation.
// TODO(crbug.com/40256076) Use the icon loading command once it's available.
web_app::WebAppProvider::GetForWebApps(&active_profile_.get())
->icon_manager()
.ReadAllIcons(app_id_, base::BindOnce(
&WebAppProfileSwitcher::InstallAndLaunchWebApp,
weak_factory_.GetWeakPtr()));
}
void WebAppProfileSwitcher::InstallAndLaunchWebApp(
web_app::WebAppIconManager::WebAppBitmaps icon_bitmaps) {
web_app::WebAppProvider* active_profile_provider =
web_app::WebAppProvider::GetForWebApps(&active_profile_.get());
if (!active_profile_provider->registrar_unsafe().IsInstallState(
app_id_,
{web_app::proto::InstallState::SUGGESTED_FROM_ANOTHER_DEVICE,
web_app::proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION,
web_app::proto::InstallState::INSTALLED_WITH_OS_INTEGRATION})) {
RunCompletionCallback();
return;
}
const web_app::WebApp* web_app =
active_profile_provider->registrar_unsafe().GetAppById(app_id_);
DCHECK(web_app);
auto install_info = MakeInstallInfoFromApp(*web_app);
install_info->icon_bitmaps = std::move(icon_bitmaps.manifest_icons);
install_info->trusted_icon_bitmaps = std::move(icon_bitmaps.trusted_icons);
web_app::WebAppInstallParams install_params;
install_params.add_to_desktop = true;
install_params.add_to_quick_launch_bar = true;
install_params.add_to_applications_menu = true;
install_params.user_display_mode =
web_app::mojom::UserDisplayMode::kStandalone;
auto* provider = web_app::WebAppProvider::GetForWebApps(new_profile_);
CHECK(provider);
provider->scheduler().InstallFromInfoWithParams(
std::move(install_info),
/*overwrite_existing_manifest_fields=*/false,
webapps::WebappInstallSource::PROFILE_MENU,
base::BindOnce(&WebAppProfileSwitcher::LaunchAppWithId,
weak_factory_.GetWeakPtr()),
install_params);
}
void WebAppProfileSwitcher::LaunchAppWithId(
const webapps::AppId& app_id,
webapps::InstallResultCode install_result) {
// TODO(crbug.com/40256076): Record metrics for installation failures.
if (!IsSuccess(install_result)) {
RunCompletionCallback();
return;
}
web_app::WebAppProvider::GetForWebApps(new_profile_)
->scheduler()
.LaunchApp(app_id, *base::CommandLine::ForCurrentProcess(),
/*current_directory=*/base::FilePath(),
/*url_handler_launch_url=*/std::nullopt,
/*protocol_handler_launch_url=*/std::nullopt,
/*file_launch_url=*/std::nullopt, /*launch_files=*/{},
base::IgnoreArgs<base::WeakPtr<Browser>,
base::WeakPtr<content::WebContents>,
apps::LaunchContainer>(base::BindOnce(
&WebAppProfileSwitcher::RunCompletionCallback,
weak_factory_.GetWeakPtr())));
}
void WebAppProfileSwitcher::RunCompletionCallback() {
std::move(on_completion_).Run();
}