blob: 2353bdabf9b23a9f8464a73e243537bba73262dc [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_MANIFEST_SILENT_UPDATE_COMMAND_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_MANIFEST_SILENT_UPDATE_COMMAND_H_
#include <iosfwd>
#include <memory>
#include <optional>
#include "base/functional/callback_forward.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/types/pass_key.h"
#include "chrome/browser/web_applications/commands/web_app_command.h"
#include "chrome/browser/web_applications/jobs/manifest_to_web_app_install_info_job.h"
#include "chrome/browser/web_applications/locks/noop_lock.h"
#include "chrome/browser/web_applications/model/web_app_comparison.h"
#include "chrome/browser/web_applications/web_app_icon_manager.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/web_contents_observer.h"
namespace content {
class WebContents;
} // namespace content
namespace web_app {
namespace proto {
class PendingUpdateInfo;
} // namespace proto
class AppLock;
class NoopLock;
class ManifestToWebAppInstallInfoJob;
// Not actually used in production logic. This is just for debugging output.
enum class ManifestSilentUpdateCommandStage {
kNotStarted,
kFetchingNewManifestData,
kLoadingExistingManifestData,
kAcquiringAppLock,
kConstructingWebAppInfo,
kLoadingExistingAndNewManifestIcons,
kComparingManifestData,
kFinalizingSilentManifestChanges,
kWritingPendingUpdateIconBitmapsToDisk,
kDeletingPendingUpdateIconsFromDisk
};
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// LINT.IfChange(ManifestSilentUpdateCheckResult)
enum class ManifestSilentUpdateCheckResult {
kAppNotInstalled = 0,
kAppUpdateFailedDuringInstall = 1,
kSystemShutdown = 2,
kAppSilentlyUpdated = 3,
kAppUpToDate = 4,
kIconReadFromDiskFailed = 5,
kWebContentsDestroyed = 6,
kAppOnlyHasSecurityUpdate = 7,
kAppHasNonSecurityAndSecurityChanges = 8,
kPendingIconWriteToDiskFailed = 9,
kInvalidManifest = 10,
kInvalidPendingUpdateInfo = 11,
kUserNavigated = 12,
kManifestToWebAppInstallInfoError = 13,
kAppHasSecurityUpdateDueToThrottle = 14,
kMaxValue = kAppHasSecurityUpdateDueToThrottle,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/webapps/enums.xml:WebAppManifestSilentUpdateCheckResult)
bool IsAppUpdated(ManifestSilentUpdateCheckResult result);
// Declare the logging operator before the command declaration, so the templated
// completion method can use it to log the result.
std::ostream& operator<<(std::ostream& os,
ManifestSilentUpdateCheckResult result);
// Returns all the information necessary for a manifest's silent update to have
// finished running, including the result of a silent update command and the
// timestamp of a silent icon update if that happened.
struct ManifestSilentUpdateCompletionInfo {
ManifestSilentUpdateCompletionInfo();
explicit ManifestSilentUpdateCompletionInfo(
ManifestSilentUpdateCheckResult result);
~ManifestSilentUpdateCompletionInfo() = default;
base::Value::Dict ToDebugValue();
// Move operation only for simplicity.
ManifestSilentUpdateCompletionInfo(ManifestSilentUpdateCompletionInfo&&);
ManifestSilentUpdateCompletionInfo& operator=(
ManifestSilentUpdateCompletionInfo&&);
ManifestSilentUpdateCheckResult result;
std::optional<base::Time> time_for_icon_diff_check;
};
// Downloads a currently linked manifest in the given web contents. Non-security
// -sensitive manifest members are updated immediately. Security sensitive
// changes are saved in the WebApp's PendingUpdateInfo.
//
// Invariants:
// - This command assumes that the load for the given web contents has been
// completed, and the manifest is already linked.
//
// High level procedure for this command:
// - Download new manifest data from site.
// - Load existing manifest data from disk including external resources.
// - Diff the non-security sensitive manifest data. This includes all fields of
// the manifest excluding icons and app name.
// - Update non-security sensitive fields silently.
// - Choose two golden icons (one each from the new and existing manifest).
// - Compare their icon's URL which determines a silent update of the icon (<10%
// image diff) or store it as a PendingUpdateInfo (>10% image diff).
// - Finalize silent update of icon (if needed) and destroy command.
class ManifestSilentUpdateCommand
: public WebAppCommand<NoopLock, ManifestSilentUpdateCompletionInfo>,
public content::WebContentsObserver {
public:
using CompletedCallback =
base::OnceCallback<void(ManifestSilentUpdateCompletionInfo check_result)>;
ManifestSilentUpdateCommand(
content::WebContents& web_contents,
std::optional<base::Time> previous_time_for_silent_icon_update,
CompletedCallback callback);
~ManifestSilentUpdateCommand() override;
// content::WebContentsObserver:
void PrimaryPageChanged(content::Page& page) override;
protected:
// WebAppCommand:
void StartWithLock(std::unique_ptr<NoopLock> lock) override;
private:
void SetStage(ManifestSilentUpdateCommandStage stage);
void OnManifestFetchedAcquireAppLock(
blink::mojom::ManifestPtr opt_manifest,
bool valid_manifest_for_web_app,
webapps::InstallableStatusCode installable_status);
void StartManifestToInstallInfoJob(blink::mojom::ManifestPtr opt_manifest);
// The `install_info` will have icons populated if they were found in the
// manifest.
void OnWebAppInfoCreatedFromManifest(
std::unique_ptr<WebAppInstallInfo> install_info);
// Identify whether or not the app needs to be silently updated, or if a
// pending update needs to be stored, and starts the writes if needed.
void FinalizeUpdateIfSilentChangesExist();
void UpdateFinalizedWritePendingInfo(
std::optional<proto::PendingUpdateInfo> pending_update_info,
const webapps::AppId& app_id,
webapps::InstallResultCode code);
void WritePendingUpdateInfoThenComplete(
std::optional<proto::PendingUpdateInfo>,
ManifestSilentUpdateCheckResult result);
void WritePendingUpdateToWebAppUpdateObservers(
std::optional<proto::PendingUpdateInfo> pending_update);
void CompleteCommandAndSelfDestruct(
base::Location location,
ManifestSilentUpdateCheckResult check_result);
bool IsWebContentsDestroyed();
base::WeakPtr<ManifestSilentUpdateCommand> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
// Loads `existing_trusted_icon_bitmaps_`, `existing_manifest_icon_bitmaps_`,
// and `existing_shortcuts_menu_icon_bitmaps_`, and then calls `on_complete`.
void LoadExistingAppAndShortcutIcons(base::OnceClosure on_complete);
void OnAppIconsLoaded(WebAppIconManager::WebAppBitmaps icon_bitmaps);
void OnShortcutIconsLoaded(
ShortcutsMenuIconBitmaps shortcuts_menu_icon_bitmaps);
// Manifest update check request parameters.
webapps::AppId app_id_;
// Populated when the command should fail, but the command hasn't started yet.
// Used for when the attached page is navigated or changed, so the manifest
// cannot be loaded from here.
std::optional<ManifestSilentUpdateCheckResult> failed_before_start_;
// Resources and helpers used to fetch manifest data.
std::unique_ptr<NoopLock> lock_;
std::unique_ptr<WebAppDataRetriever> data_retriever_;
std::unique_ptr<AppLock> app_lock_;
// Temporary variables stored here while the update check progresses
// asynchronously.
std::unique_ptr<WebAppInstallInfo> new_install_info_;
WebAppComparison web_app_comparison_;
IconBitmaps existing_manifest_icon_bitmaps_;
IconBitmaps existing_trusted_icon_bitmaps_;
IconBitmaps pending_trusted_icon_bitmaps_;
IconBitmaps pending_manifest_icon_bitmaps_;
ShortcutsMenuIconBitmaps existing_shortcuts_menu_icon_bitmaps_;
// Stores whether a silent update can happen depending on the state of the
// system after the manifest update process has started and the web app's new
// fields have been downloaded.
bool silent_update_required_ = false;
// Stores whether a silent update is allowed depending on the state of the web
// app itself, like if the app is trusted, or if the generated icons can be
// fixed. For these cases, a pending update is not stored inside the web app.
bool silently_update_app_identity_ = false;
base::WeakPtr<content::WebContents> web_contents_;
// Note: This must be destroyed before `new_install_info_` since it holds a
// raw_ptr to it.
std::unique_ptr<ManifestToWebAppInstallInfoJob> manifest_to_install_info_job_;
// Debug info.
ManifestSilentUpdateCommandStage stage_ =
ManifestSilentUpdateCommandStage::kFetchingNewManifestData;
// Stores the last time a silent icon update was triggered for `app_id_` if
// that happened.
std::optional<base::Time> previous_time_for_silent_icon_update_;
ManifestSilentUpdateCompletionInfo completion_info_;
base::WeakPtrFactory<ManifestSilentUpdateCommand> weak_factory_{this};
};
std::ostream& operator<<(std::ostream& os,
ManifestSilentUpdateCheckResult stage);
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_MANIFEST_SILENT_UPDATE_COMMAND_H_