blob: a4c967d750227a1b0335eb844d6b4a02eb78e2d8 [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_provider.h"
#include <memory>
#include <utility>
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/check_is_test.h"
#include "base/feature_list.h"
#include "base/functional/callback_forward.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/externally_managed_app_manager_impl.h"
#include "chrome/browser/web_applications/file_utils_wrapper.h"
#include "chrome/browser/web_applications/manifest_update_manager.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
#include "chrome/browser/web_applications/os_integration/url_handler_manager.h"
#include "chrome/browser/web_applications/os_integration/url_handler_manager_impl.h"
#include "chrome/browser/web_applications/os_integration/web_app_file_handler_manager.h"
#include "chrome/browser/web_applications/os_integration/web_app_protocol_handler_manager.h"
#include "chrome/browser/web_applications/os_integration/web_app_shortcut_manager.h"
#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
#include "chrome/browser/web_applications/preinstalled_web_app_manager.h"
#include "chrome/browser/web_applications/web_app_audio_focus_id_map.h"
#include "chrome/browser/web_applications/web_app_command_manager.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_database_factory.h"
#include "chrome/browser/web_applications/web_app_icon_manager.h"
#include "chrome/browser/web_applications/web_app_install_finalizer.h"
#include "chrome/browser/web_applications/web_app_install_manager.h"
#include "chrome/browser/web_applications/web_app_prefs_utils.h"
#include "chrome/browser/web_applications/web_app_provider_factory.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 "chrome/common/chrome_features.h"
#include "content/public/browser/web_contents.h"
namespace web_app {
namespace {
WebAppProvider::OsIntegrationManagerFactory
g_os_integration_manager_factory_for_testing = nullptr;
} // namespace
// static
WebAppProvider* WebAppProvider::GetDeprecated(Profile* profile) {
return WebAppProviderFactory::GetForProfile(profile);
}
// static
WebAppProvider* WebAppProvider::GetForWebApps(Profile* profile) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
// If features::kWebAppsCrosapi is enabled, Ash browser only manages system
// web apps (return nullptr here). Otherwise, Ash browser manages all web apps
// (return WebAppProvider).
return IsWebAppsCrosapiEnabled()
? nullptr
: WebAppProviderFactory::GetForProfile(profile);
#else
return WebAppProviderFactory::GetForProfile(profile);
#endif
}
// static
WebAppProvider* WebAppProvider::GetForLocalAppsUnchecked(Profile* profile) {
return WebAppProviderFactory::GetForProfile(profile);
}
// static
WebAppProvider* WebAppProvider::GetForTest(Profile* profile) {
// Running a nested base::RunLoop outside of tests causes a deadlock. Crash
// immediately instead of deadlocking for easier debugging (especially for
// TAST tests which use prod binaries).
CHECK_IS_TEST();
WebAppProvider* provider = GetForLocalAppsUnchecked(profile);
if (!provider)
return nullptr;
if (provider->on_registry_ready().is_signaled())
return provider;
base::RunLoop run_loop;
provider->on_registry_ready().Post(FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
return provider;
}
// static
WebAppProvider* WebAppProvider::GetForWebContents(
content::WebContents* web_contents) {
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
DCHECK(profile);
return WebAppProvider::GetForLocalAppsUnchecked(profile);
}
// static
void WebAppProvider::SetOsIntegrationManagerFactoryForTesting(
OsIntegrationManagerFactory factory) {
g_os_integration_manager_factory_for_testing = factory;
}
WebAppProvider::WebAppProvider(Profile* profile) : profile_(profile) {
DCHECK(AreWebAppsEnabled(profile_));
// WebApp System must have only one instance in original profile.
// Exclude secondary off-the-record profiles.
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (!profile_->IsGuestSession())
DCHECK(!profile_->IsOffTheRecord());
#else
DCHECK(!profile_->IsOffTheRecord());
#endif
CreateSubsystems(profile_);
}
WebAppProvider::~WebAppProvider() = default;
void WebAppProvider::Start() {
CHECK(!started_);
ConnectSubsystems();
started_ = true;
StartImpl();
}
WebAppRegistrar& WebAppProvider::registrar() {
CheckIsConnected();
return *registrar_;
}
const WebAppRegistrar& WebAppProvider::registrar() const {
CheckIsConnected();
return *registrar_;
}
WebAppSyncBridge& WebAppProvider::sync_bridge() {
CheckIsConnected();
return *sync_bridge_;
}
WebAppInstallManager& WebAppProvider::install_manager() {
CheckIsConnected();
return *install_manager_;
}
WebAppInstallFinalizer& WebAppProvider::install_finalizer() {
CheckIsConnected();
return *install_finalizer_;
}
ManifestUpdateManager& WebAppProvider::manifest_update_manager() {
CheckIsConnected();
return *manifest_update_manager_;
}
ExternallyManagedAppManager& WebAppProvider::externally_managed_app_manager() {
CheckIsConnected();
return *externally_managed_app_manager_;
}
WebAppPolicyManager& WebAppProvider::policy_manager() {
CheckIsConnected();
return *web_app_policy_manager_;
}
WebAppUiManager& WebAppProvider::ui_manager() {
CheckIsConnected();
return *ui_manager_;
}
WebAppAudioFocusIdMap& WebAppProvider::audio_focus_id_map() {
CheckIsConnected();
return *audio_focus_id_map_;
}
WebAppIconManager& WebAppProvider::icon_manager() {
CheckIsConnected();
return *icon_manager_;
}
WebAppTranslationManager& WebAppProvider::translation_manager() {
CheckIsConnected();
return *translation_manager_;
}
OsIntegrationManager& WebAppProvider::os_integration_manager() {
CheckIsConnected();
return *os_integration_manager_;
}
const OsIntegrationManager& WebAppProvider::os_integration_manager() const {
CheckIsConnected();
return *os_integration_manager_;
}
WebAppCommandManager& WebAppProvider::command_manager() {
// Note: It is OK to access the command manager before connection or start.
// Internally it will queue commands to only happen after it has started.
return *command_manager_;
}
WebAppCommandScheduler& WebAppProvider::scheduler() {
return *command_scheduler_;
}
void WebAppProvider::Shutdown() {
command_scheduler_->Shutdown();
command_manager_->Shutdown();
ui_manager_->Shutdown();
externally_managed_app_manager_->Shutdown();
manifest_update_manager_->Shutdown();
install_manager_->Shutdown();
icon_manager_->Shutdown();
install_finalizer_->Shutdown();
registrar_->Shutdown();
is_registry_ready_ = false;
}
void WebAppProvider::StartImpl() {
StartSyncBridge();
}
void WebAppProvider::CreateSubsystems(Profile* profile) {
audio_focus_id_map_ = std::make_unique<WebAppAudioFocusIdMap>();
ui_manager_ = WebAppUiManager::Create(profile);
install_manager_ = std::make_unique<WebAppInstallManager>(profile);
manifest_update_manager_ = std::make_unique<ManifestUpdateManager>();
externally_managed_app_manager_ =
std::make_unique<ExternallyManagedAppManagerImpl>(profile);
preinstalled_web_app_manager_ =
std::make_unique<PreinstalledWebAppManager>(profile);
web_app_policy_manager_ = std::make_unique<WebAppPolicyManager>(profile);
database_factory_ = std::make_unique<WebAppDatabaseFactory>(profile);
std::unique_ptr<WebAppRegistrar> registrar;
std::unique_ptr<WebAppSyncBridge> sync_bridge;
// Only WebAppSyncBridge must have an access to mutable WebAppRegistrar.
{
auto mutable_registrar = std::make_unique<WebAppRegistrarMutable>(profile);
sync_bridge = std::make_unique<WebAppSyncBridge>(mutable_registrar.get());
// Upcast to read-only WebAppRegistrar.
registrar = std::move(mutable_registrar);
}
icon_manager_ = std::make_unique<WebAppIconManager>(
profile, base::MakeRefCounted<FileUtilsWrapper>());
translation_manager_ = std::make_unique<WebAppTranslationManager>(
profile, base::MakeRefCounted<FileUtilsWrapper>());
install_finalizer_ = std::make_unique<WebAppInstallFinalizer>(profile);
if (g_os_integration_manager_factory_for_testing) {
os_integration_manager_ =
g_os_integration_manager_factory_for_testing(profile);
} else {
auto file_handler_manager =
std::make_unique<WebAppFileHandlerManager>(profile);
auto protocol_handler_manager =
std::make_unique<WebAppProtocolHandlerManager>(profile);
auto shortcut_manager = std::make_unique<WebAppShortcutManager>(
profile, icon_manager_.get(), file_handler_manager.get(),
protocol_handler_manager.get());
std::unique_ptr<UrlHandlerManager> url_handler_manager;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
url_handler_manager = std::make_unique<UrlHandlerManagerImpl>(profile);
#endif
os_integration_manager_ = std::make_unique<OsIntegrationManager>(
profile, std::move(shortcut_manager), std::move(file_handler_manager),
std::move(protocol_handler_manager), std::move(url_handler_manager));
}
command_manager_ = std::make_unique<WebAppCommandManager>(profile, this);
command_scheduler_ = std::make_unique<WebAppCommandScheduler>(*profile, this);
registrar_ = std::move(registrar);
sync_bridge_ = std::move(sync_bridge);
}
void WebAppProvider::ConnectSubsystems() {
DCHECK(!started_);
sync_bridge_->SetSubsystems(database_factory_.get(), install_manager_.get(),
command_manager_.get());
icon_manager_->SetSubsystems(registrar_.get(), install_manager_.get());
install_finalizer_->SetSubsystems(
install_manager_.get(), registrar_.get(), ui_manager_.get(),
sync_bridge_.get(), os_integration_manager_.get(), icon_manager_.get(),
web_app_policy_manager_.get(), translation_manager_.get(),
command_manager_.get());
install_manager_->SetSubsystems(
registrar_.get(), os_integration_manager_.get(), command_manager_.get(),
install_finalizer_.get(), icon_manager_.get(), sync_bridge_.get(),
translation_manager_.get());
manifest_update_manager_->SetSubsystems(
install_manager_.get(), registrar_.get(), icon_manager_.get(),
ui_manager_.get(), install_finalizer_.get(),
os_integration_manager_.get(), sync_bridge_.get(),
command_scheduler_.get());
externally_managed_app_manager_->SetSubsystems(
registrar_.get(), ui_manager_.get(), install_finalizer_.get(),
command_scheduler_.get(), sync_bridge_.get());
preinstalled_web_app_manager_->SetSubsystems(
registrar_.get(), ui_manager_.get(),
externally_managed_app_manager_.get());
web_app_policy_manager_->SetSubsystems(externally_managed_app_manager_.get(),
registrar_.get(), sync_bridge_.get(),
os_integration_manager_.get());
registrar_->SetSubsystems(web_app_policy_manager_.get(),
translation_manager_.get());
ui_manager_->SetSubsystems(sync_bridge_.get(), os_integration_manager_.get());
os_integration_manager_->SetSubsystems(sync_bridge_.get(), registrar_.get(),
ui_manager_.get(),
icon_manager_.get());
connected_ = true;
}
void WebAppProvider::StartSyncBridge() {
sync_bridge_->Init(base::BindOnce(&WebAppProvider::OnSyncBridgeReady,
weak_ptr_factory_.GetWeakPtr()));
}
void WebAppProvider::OnSyncBridgeReady() {
DCHECK(!on_registry_ready_.is_signaled());
if (base::FeatureList::IsEnabled(
features::kUseWebAppDBInsteadOfExternalPrefs)) {
ExternallyInstalledWebAppPrefs::MigrateExternalPrefData(
profile_->GetPrefs(), sync_bridge_.get());
}
DoMigrateProfilePrefs(profile_);
// Note: This does not wait for the call from the ChromeOS
// SystemWebAppManager, which is a separate keyed service.
int num_barrier_calls = 2;
base::RepeatingClosure external_manager_barrier = base::BarrierClosure(
num_barrier_calls,
base::BindOnce(
[](base::WeakPtr<WebAppProvider> provider) {
if (!provider)
return;
provider->on_external_managers_synchronized_.Signal();
},
weak_ptr_factory_.GetWeakPtr()));
registrar_->Start();
install_finalizer_->Start();
icon_manager_->Start();
translation_manager_->Start();
install_manager_->Start();
preinstalled_web_app_manager_->Start(external_manager_barrier);
web_app_policy_manager_->Start(external_manager_barrier);
manifest_update_manager_->Start();
os_integration_manager_->Start();
ui_manager_->Start();
command_manager_->Start();
on_registry_ready_.Signal();
is_registry_ready_ = true;
}
void WebAppProvider::CheckIsConnected() const {
DCHECK(connected_) << "Attempted to access Web App subsystem while "
"WebAppProvider is not connected. You may need to wait "
"for on_registry_ready().";
}
void WebAppProvider::DoMigrateProfilePrefs(Profile* profile) {
std::map<AppId, int> sources =
TakeAllWebAppInstallSources(profile->GetPrefs());
ScopedRegistryUpdate update(sync_bridge_.get());
for (const auto& iter : sources) {
WebApp* web_app = update->UpdateApp(iter.first);
if (web_app && !web_app->install_source_for_metrics()) {
web_app->SetInstallSourceForMetrics(
static_cast<webapps::WebappInstallSource>(iter.second));
}
}
}
} // namespace web_app