blob: 54aaf79889834bb5d97e65999fe62c909c0fc832 [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 "chrome/browser/web_applications/manifest_update_manager.h"
#include "base/command_line.h"
#include "base/metrics/histogram_macros.h"
#include "base/util/values/values_util.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.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/system_web_app_manager.h"
#include "chrome/common/chrome_features.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_types.h"
namespace web_app {
constexpr const char kDisableManifestUpdateThrottle[] =
"disable-manifest-update-throttle";
ManifestUpdateManager::ManifestUpdateManager() = default;
ManifestUpdateManager::~ManifestUpdateManager() = default;
void ManifestUpdateManager::SetSubsystems(
AppRegistrar* registrar,
AppIconManager* icon_manager,
WebAppUiManager* ui_manager,
InstallManager* install_manager,
SystemWebAppManager* system_web_app_manager) {
registrar_ = registrar;
icon_manager_ = icon_manager;
ui_manager_ = ui_manager;
install_manager_ = install_manager;
system_web_app_manager_ = system_web_app_manager;
}
void ManifestUpdateManager::Start() {
registrar_observer_.Add(registrar_);
DCHECK(!started_);
started_ = true;
}
void ManifestUpdateManager::Shutdown() {
registrar_observer_.RemoveAll();
tasks_.clear();
started_ = false;
}
void ManifestUpdateManager::MaybeUpdate(const GURL& url,
const AppId& app_id,
content::WebContents* web_contents) {
if (!started_ ||
!base::FeatureList::IsEnabled(features::kDesktopPWAsLocalUpdating)) {
return;
}
if (app_id.empty() || !registrar_->IsLocallyInstalled(app_id)) {
NotifyResult(url, ManifestUpdateResult::kNoAppInScope);
return;
}
if (system_web_app_manager_->IsSystemWebApp(app_id)) {
NotifyResult(url, ManifestUpdateResult::kAppIsSystemWebApp);
return;
}
if (registrar_->IsPlaceholderApp(app_id)) {
NotifyResult(url, ManifestUpdateResult::kAppIsPlaceholder);
return;
}
if (base::Contains(tasks_, app_id))
return;
if (!MaybeConsumeUpdateCheck(url.GetOrigin(), app_id)) {
NotifyResult(url, ManifestUpdateResult::kThrottled);
return;
}
tasks_.insert_or_assign(
app_id, std::make_unique<ManifestUpdateTask>(
url, app_id, web_contents,
base::Bind(&ManifestUpdateManager::OnUpdateStopped,
base::Unretained(this)),
hang_update_checks_for_testing_, *registrar_, *icon_manager_,
ui_manager_, install_manager_));
}
// AppRegistrarObserver:
void ManifestUpdateManager::OnWebAppUninstalled(const AppId& app_id) {
DCHECK(started_);
auto it = tasks_.find(app_id);
if (it != tasks_.end()) {
NotifyResult(it->second->url(), ManifestUpdateResult::kAppUninstalled);
tasks_.erase(it);
}
DCHECK(!tasks_.contains(app_id));
last_update_check_.erase(app_id);
}
bool ManifestUpdateManager::MaybeConsumeUpdateCheck(const GURL& origin,
const AppId& app_id) {
constexpr base::TimeDelta kDelayBetweenChecks = base::TimeDelta::FromDays(1);
base::Optional<base::Time> last_check_time =
GetLastUpdateCheckTime(origin, app_id);
base::Time now = time_override_for_testing_.value_or(base::Time::Now());
// Throttling updates to at most once per day is consistent with Android.
// See |UPDATE_INTERVAL| in WebappDataStorage.java.
if (last_check_time.has_value() &&
now < *last_check_time + kDelayBetweenChecks &&
!base::CommandLine::ForCurrentProcess()->HasSwitch(
kDisableManifestUpdateThrottle)) {
return false;
}
SetLastUpdateCheckTime(origin, app_id, now);
return true;
}
base::Optional<base::Time> ManifestUpdateManager::GetLastUpdateCheckTime(
const GURL& origin,
const AppId& app_id) const {
auto it = last_update_check_.find(app_id);
return it != last_update_check_.end() ? base::Optional<base::Time>(it->second)
: base::nullopt;
}
void ManifestUpdateManager::SetLastUpdateCheckTime(const GURL& origin,
const AppId& app_id,
base::Time time) {
last_update_check_[app_id] = time;
}
void ManifestUpdateManager::OnUpdateStopped(const ManifestUpdateTask& task,
ManifestUpdateResult result) {
DCHECK_EQ(&task, tasks_[task.app_id()].get());
NotifyResult(task.url(), result);
tasks_.erase(task.app_id());
}
void ManifestUpdateManager::SetResultCallbackForTesting(
ResultCallback callback) {
DCHECK(result_callback_for_testing_.is_null());
result_callback_for_testing_ = std::move(callback);
}
void ManifestUpdateManager::NotifyResult(const GURL& url,
ManifestUpdateResult result) {
// Don't log kNoAppInScope because it will be far too noisy (most page loads
// will hit it).
if (result != ManifestUpdateResult::kNoAppInScope) {
UMA_HISTOGRAM_ENUMERATION("Webapp.Update.ManifestUpdateResult", result);
}
if (result_callback_for_testing_)
std::move(result_callback_for_testing_).Run(url, result);
}
} // namespace web_app