blob: 19833e444e98fbe5c6bbbb0d1b952f7f4f4a7276 [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/web_applications/system_web_app_manager.h"
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/version.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/components/app_registrar.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/components/web_app_install_utils.h"
#include "chrome/browser/web_applications/components/web_app_ui_manager.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/webui_url_constants.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/version_info/version_info.h"
#include "content/public/common/content_switches.h"
#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
#if defined(OS_CHROMEOS)
#include "ash/public/cpp/app_list/internal_app_id_constants.h"
#include "chrome/browser/chromeos/extensions/default_web_app_ids.h"
#include "chromeos/constants/chromeos_features.h"
#endif // defined(OS_CHROMEOS)
namespace web_app {
namespace {
base::flat_map<SystemAppType, SystemAppInfo> CreateSystemWebApps() {
base::flat_map<SystemAppType, SystemAppInfo> infos;
// TODO(calamity): Split this into per-platform functions.
#if defined(OS_CHROMEOS)
if (SystemWebAppManager::IsAppEnabled(SystemAppType::DISCOVER))
infos[SystemAppType::DISCOVER].install_url =
GURL(chrome::kChromeUIDiscoverURL);
if (SystemWebAppManager::IsAppEnabled(SystemAppType::CAMERA)) {
constexpr char kCameraAppPWAURL[] = "chrome://camera/pwa.html";
infos[SystemAppType::CAMERA].install_url = GURL(kCameraAppPWAURL);
infos[SystemAppType::CAMERA].uninstall_and_replace = {
ash::kInternalAppIdCamera};
}
if (base::FeatureList::IsEnabled(chromeos::features::kSplitSettings)) {
constexpr char kChromeSettingsPWAURL[] = "chrome://os-settings/pwa.html";
infos[SystemAppType::SETTINGS].install_url = GURL(kChromeSettingsPWAURL);
infos[SystemAppType::SETTINGS].uninstall_and_replace = {
chromeos::default_web_apps::kSettingsAppId,
ash::kInternalAppIdSettings};
} else {
constexpr char kChromeSettingsPWAURL[] = "chrome://settings/pwa.html";
infos[SystemAppType::SETTINGS].install_url = GURL(kChromeSettingsPWAURL);
infos[SystemAppType::SETTINGS].uninstall_and_replace = {
ash::kInternalAppIdSettings};
}
// Large enough to see the heading text "Settings" in the top-left.
infos[SystemAppType::SETTINGS].minimum_window_size = {300, 100};
if (SystemWebAppManager::IsAppEnabled(SystemAppType::TERMINAL)) {
constexpr char kChromeTerminalPWAURL[] = "chrome://terminal/html/pwa.html";
infos[SystemAppType::TERMINAL].install_url = GURL(kChromeTerminalPWAURL);
}
#endif // OS_CHROMEOS
return infos;
}
ExternalInstallOptions CreateInstallOptionsForSystemApp(
const SystemAppInfo& info,
bool force_update) {
DCHECK_EQ(content::kChromeUIScheme, info.install_url.scheme());
web_app::ExternalInstallOptions install_options(
info.install_url, blink::mojom::DisplayMode::kStandalone,
ExternalInstallSource::kSystemInstalled);
install_options.add_to_applications_menu = false;
install_options.add_to_desktop = false;
install_options.add_to_quick_launch_bar = false;
install_options.bypass_service_worker_check = true;
install_options.force_reinstall = force_update;
install_options.uninstall_and_replace = info.uninstall_and_replace;
return install_options;
}
} // namespace
SystemAppInfo::SystemAppInfo() = default;
SystemAppInfo::SystemAppInfo(const GURL& install_url)
: install_url(install_url) {}
SystemAppInfo::SystemAppInfo(const SystemAppInfo& other) = default;
SystemAppInfo::~SystemAppInfo() = default;
// static
const char SystemWebAppManager::kInstallResultHistogramName[];
// static
bool SystemWebAppManager::IsAppEnabled(SystemAppType type) {
#if defined(OS_CHROMEOS)
switch (type) {
case SystemAppType::SETTINGS:
return true;
case SystemAppType::DISCOVER:
return base::FeatureList::IsEnabled(chromeos::features::kDiscoverApp);
case SystemAppType::CAMERA:
return base::FeatureList::IsEnabled(
chromeos::features::kCameraSystemWebApp);
case SystemAppType::TERMINAL:
return base::FeatureList::IsEnabled(features::kTerminalSystemApp);
}
#else
return false;
#endif // OS_CHROMEOS
}
SystemWebAppManager::SystemWebAppManager(Profile* profile)
: on_apps_synchronized_(new base::OneShotEvent()),
pref_service_(profile->GetPrefs()) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kTestType)) {
// Always update in tests, and return early to avoid populating with real
// system apps.
update_policy_ = UpdatePolicy::kAlwaysUpdate;
return;
}
#if defined(OFFICIAL_BUILD)
// Official builds should trigger updates whenever the version number changes.
update_policy_ = UpdatePolicy::kOnVersionChange;
#else
// Dev builds should update every launch.
update_policy_ = UpdatePolicy::kAlwaysUpdate;
#endif
system_app_infos_ = CreateSystemWebApps();
}
SystemWebAppManager::~SystemWebAppManager() = default;
void SystemWebAppManager::SetSubsystems(PendingAppManager* pending_app_manager,
AppRegistrar* registrar,
WebAppUiManager* ui_manager) {
pending_app_manager_ = pending_app_manager;
registrar_ = registrar;
ui_manager_ = ui_manager;
}
void SystemWebAppManager::Start() {
std::vector<ExternalInstallOptions> install_options_list;
if (IsEnabled()) {
// Skipping this will uninstall all System Apps currently installed.
for (const auto& app : system_app_infos_) {
install_options_list.push_back(
CreateInstallOptionsForSystemApp(app.second, NeedsUpdate()));
}
}
pending_app_manager_->SynchronizeInstalledApps(
std::move(install_options_list), ExternalInstallSource::kSystemInstalled,
base::BindOnce(&SystemWebAppManager::OnAppsSynchronized,
weak_ptr_factory_.GetWeakPtr()));
}
void SystemWebAppManager::InstallSystemAppsForTesting() {
on_apps_synchronized_.reset(new base::OneShotEvent());
system_app_infos_ = CreateSystemWebApps();
Start();
// Wait for the System Web Apps to install.
base::RunLoop run_loop;
on_apps_synchronized().Post(FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
}
base::Optional<AppId> SystemWebAppManager::GetAppIdForSystemApp(
SystemAppType id) const {
auto app_url_it = system_app_infos_.find(id);
if (app_url_it == system_app_infos_.end())
return base::Optional<AppId>();
return registrar_->LookupExternalAppId(app_url_it->second.install_url);
}
bool SystemWebAppManager::IsSystemWebApp(const AppId& app_id) const {
return registrar_->HasExternalAppWithInstallSource(
app_id, ExternalInstallSource::kSystemInstalled);
}
gfx::Size SystemWebAppManager::GetMinimumWindowSize(const AppId& app_id) const {
auto app_type_it = app_id_to_app_type_.find(app_id);
if (app_type_it == app_id_to_app_type_.end())
return gfx::Size();
const SystemAppType& app_type = app_type_it->second;
auto app_info_it = system_app_infos_.find(app_type);
if (app_info_it == system_app_infos_.end())
return gfx::Size();
return app_info_it->second.minimum_window_size;
}
void SystemWebAppManager::SetSystemAppsForTesting(
base::flat_map<SystemAppType, SystemAppInfo> system_apps) {
system_app_infos_ = std::move(system_apps);
}
void SystemWebAppManager::SetUpdatePolicyForTesting(UpdatePolicy policy) {
update_policy_ = policy;
}
// static
bool SystemWebAppManager::IsEnabled() {
return base::FeatureList::IsEnabled(features::kSystemWebApps);
}
// static
void SystemWebAppManager::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterStringPref(prefs::kSystemWebAppLastUpdateVersion, "");
}
const base::Version& SystemWebAppManager::CurrentVersion() const {
return version_info::GetVersion();
}
void SystemWebAppManager::OnAppsSynchronized(
std::map<GURL, InstallResultCode> install_results,
std::map<GURL, bool> uninstall_results) {
if (IsEnabled()) {
pref_service_->SetString(prefs::kSystemWebAppLastUpdateVersion,
CurrentVersion().GetString());
}
RecordExternalAppInstallResultCode(kInstallResultHistogramName,
install_results);
// Build the map from installed app id to app type.
for (const auto& it : system_app_infos_) {
const SystemAppType& app_type = it.first;
base::Optional<AppId> app_id =
registrar_->LookupExternalAppId(it.second.install_url);
if (app_id.has_value())
app_id_to_app_type_[app_id.value()] = app_type;
}
// May be called more than once in tests.
if (!on_apps_synchronized_->is_signaled())
on_apps_synchronized_->Signal();
}
bool SystemWebAppManager::NeedsUpdate() const {
if (update_policy_ == UpdatePolicy::kAlwaysUpdate)
return true;
base::Version last_update_version(
pref_service_->GetString(prefs::kSystemWebAppLastUpdateVersion));
// This also updates if the version rolls back for some reason to ensure that
// the System Web Apps are always in sync with the Chrome version.
return !last_update_version.IsValid() ||
last_update_version != CurrentVersion();
}
} // namespace web_app