| // 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 "base/functional/callback_forward.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/app_lock.h" |
| #include "chrome/browser/web_applications/locks/noop_lock.h" |
| #include "chrome/browser/web_applications/manifest_update_utils.h" |
| #include "chrome/browser/web_applications/proto/web_app.pb.h" |
| #include "chrome/browser/web_applications/web_app_icon_manager.h" |
| #include "chrome/browser/web_applications/web_contents/web_app_data_retriever.h" |
| #include "content/public/browser/web_contents_observer.h" |
| |
| class GURL; |
| |
| namespace content { |
| class WebContents; |
| } // namespace content |
| |
| namespace web_app { |
| |
| // Not actually used in production logic. This is just for debugging output. |
| enum class ManifestSilentUpdateCommandStage { |
| kFetchingNewManifestData, |
| kLoadingExistingManifestData, |
| kAcquiringAppLock, |
| kComparingManifestData, |
| kFinalizingSilentManifestChanges, |
| kWritingPendingUpdateIconBitmapsToDisk, |
| }; |
| |
| // This enum is recorded by UMA, the numeric values must not change. |
| 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, |
| kMaxValue = kUserNavigated, |
| }; |
| |
| struct WebAppInstallInfo; |
| |
| // 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, ManifestSilentUpdateCheckResult>, |
| public content::WebContentsObserver { |
| public: |
| using CompletedCallback = |
| base::OnceCallback<void(ManifestSilentUpdateCheckResult check_result)>; |
| |
| ManifestSilentUpdateCommand( |
| const GURL& url, |
| base::WeakPtr<content::WebContents> web_contents, |
| CompletedCallback callback, |
| std::unique_ptr<WebAppDataRetriever> data_retriever, |
| std::unique_ptr<WebAppIconDownloader> icon_downloader); |
| |
| ~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); |
| |
| // Stage: Upgrade NoopLock to AppLock |
| // (ManifestSilentUpdateCommandStage::kAcquiringAppLock). |
| void OnManifestFetchedAcquireAppLock( |
| blink::mojom::ManifestPtr opt_manifest, |
| bool valid_manifest_for_web_app, |
| webapps::InstallableStatusCode installable_status); |
| |
| // Stage: Starting to fetch new manifest data |
| // (ManifestSilentUpdateCommandStage::kFetchingNewManifestData). |
| 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); |
| void StashValidatedScopeExtensionsAndLoadExistingManifest( |
| ScopeExtensions validated_scope_extensions); |
| |
| // Stage: Loading existing manifest data from disk. |
| // (ManifestSilentUpdateCommandStage::kLoadingExistingManifestData) |
| void StashExistingAppIcons(WebAppIconManager::WebAppBitmaps icon_bitmaps); |
| |
| // Stage: Comparing manifest data and exiting update if no changes detected. |
| // (ManifestSilentUpdateCommandStage::kComparingManifestData) |
| void StashExistingShortcutsMenuIconsFinalizeUpdateIfNeeded( |
| ShortcutsMenuIconBitmaps shortcuts_menu_icon_bitmaps); |
| |
| // Stage: Finalize silent changes to web app. |
| // (ManifestSilentUpdateCommandStage::kFinalizingSilentManifestChanges) |
| void UpdateFinalizedWritePendingInfoIfNeeded( |
| std::optional<proto::PendingUpdateInfo> pending_update_info, |
| const webapps::AppId& app_id, |
| webapps::InstallResultCode code); |
| |
| // Stage: Write pending trusted and pending manifest icon bitmaps to disk. |
| // (ManifestSilentUpdateCommandStage::kWritingPendingUpdateIconBitmapsToDisk) |
| void VerifyPendingUpdateIconBitmapsWrittenToDisk(bool bitmaps_write_success); |
| |
| // Stage: Update check complete. |
| // (ManifestSilentUpdateCommandStage::kCompleteCommand) |
| void CompleteCommandAndSelfDestruct( |
| ManifestSilentUpdateCheckResult check_result); |
| |
| bool IsWebContentsDestroyed(); |
| void AbortCommandOnWebContentsDestruction(); |
| |
| base::WeakPtr<ManifestSilentUpdateCommand> GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| // Manifest update check request parameters. |
| const GURL url_; |
| 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<AppLock> app_lock_; |
| base::WeakPtr<content::WebContents> web_contents_; |
| std::unique_ptr<WebAppDataRetriever> data_retriever_; |
| std::unique_ptr<WebAppIconDownloader> icon_downloader_; |
| std::unique_ptr<ManifestToWebAppInstallInfoJob> manifest_to_install_info_job_; |
| std::optional<apps::IconInfo> new_manifest_trusted_icon_metadata_; |
| std::optional<apps::IconInfo> existing_manifest_trusted_icon_metadata_; |
| |
| // Temporary variables stored here while the update check progresses |
| // asynchronously. |
| std::unique_ptr<WebAppInstallInfo> new_install_info_; |
| 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_; |
| bool has_icon_url_changed_ = false; |
| bool silent_update_required_ = false; |
| |
| // Debug info. |
| ManifestSilentUpdateCommandStage stage_ = |
| ManifestSilentUpdateCommandStage::kFetchingNewManifestData; |
| |
| base::WeakPtrFactory<ManifestSilentUpdateCommand> weak_factory_{this}; |
| }; |
| |
| } // namespace web_app |
| |
| #endif // CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_MANIFEST_SILENT_UPDATE_COMMAND_H_ |