chromium/chrome/browser/ash/system_web_apps/system_web_app_manager.h

// 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.

#ifndef CHROME_BROWSER_ASH_SYSTEM_WEB_APPS_SYSTEM_WEB_APP_MANAGER_H_
#define CHROME_BROWSER_ASH_SYSTEM_WEB_APPS_SYSTEM_WEB_APP_MANAGER_H_

#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "ash/webui/system_apps/public/system_web_app_type.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/one_shot_event.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ash/system_web_apps/system_web_app_background_task.h"
#include "chrome/browser/ash/system_web_apps/system_web_app_icon_checker.h"
#include "chrome/browser/ash/system_web_apps/types/system_web_app_delegate.h"
#include "chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.h"
#include "chrome/browser/web_applications/externally_managed_app_manager.h"
#include "chrome/browser/web_applications/web_app_ui_manager.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/webapps/common/web_app_id.h"
#include "url/gurl.h"

namespace base {
class Version;
}

namespace content {
class NavigationHandle;
}

namespace web_app {
class WebAppProvider;
}  // namespace web_app

class PrefService;
class Profile;

namespace ash {

// Installs, uninstalls, and updates System Web Apps.
// System Web Apps are built-in, highly-privileged Web Apps for Chrome OS. They
// have access to more APIs and are part of the Chrome OS image. All clients
// should await `on_apps_synchronized()` event to start working with SWAs.
class SystemWebAppManager : public KeyedService,
                            public web_app::WebAppUiManagerObserver {
 public:
  // Policy for when the SystemWebAppManager will update apps/install new apps.
  enum class UpdatePolicy {
    // Update every system start.
    kAlwaysUpdate,
    // Update when the Chrome version number changes.
    kOnVersionChange,
  };

  // Number of attempts to install a given version & locale of the SWAs before
  // bailing out.
  static constexpr int kInstallFailureAttempts = 3;

  static constexpr char kSystemWebAppSessionHasBrokenIconsPrefName[] =
      "web_apps.system_web_app_has_broken_icons_in_session";

  static constexpr char kInstallResultHistogramName[] =
      "Webapp.InstallResult.System";
  static constexpr char kInstallDurationHistogramName[] =
      "Webapp.SystemApps.FreshInstallDuration";
  static constexpr char kIconsFixedOnReinstallHistogramName[] =
      "Webapp.SystemApps.IconsFixedOnReinstall";

  // Returns whether the given app type is enabled.
  bool IsAppEnabled(SystemWebAppType type) const;

  explicit SystemWebAppManager(Profile* profile);
  SystemWebAppManager(const SystemWebAppManager&) = delete;
  SystemWebAppManager& operator=(const SystemWebAppManager&) = delete;
  ~SystemWebAppManager() override;

  // Return the SystemWebAppManager that hosts system web apps in profile.
  // Returns nullptr if the profile doesn't support system web apps (e.g. Kiosk,
  // lock-screen, system profile).
  static SystemWebAppManager* Get(Profile* profile);
  // Gets the associated WebAppProvider for system web apps. `WebAppProvider` is
  // always present in the `profile` if the `Get` above returns non-nullptr.
  static web_app::WebAppProvider* GetWebAppProvider(Profile* profile);

  // Returns the SystemWebAppManager for tests. Blocks if the web app registry
  // is not yet ready.
  static SystemWebAppManager* GetForTest(Profile* profile);

  // Calls `Start` when `WebAppProvider` is ready.
  void ScheduleStart();

  // Initialize the SystemWebAppManager.
  void Start();

  // KeyedService:
  void Shutdown() override;

  // By default, we don't install system web apps in browser tests to avoid
  // running installation tasks (inefficient because most browser tests don't
  // need SWAs).
  //
  // Call this to install default enabled system apps if the test needs them.
  // (e.g. test opening OS Settings from an Ash views button).
  //
  // This can be called multiple times to simulate reinstallation from system
  // restart.
  void InstallSystemAppsForTesting();

  // Returns the app id for the given System App |type|.
  std::optional<webapps::AppId> GetAppIdForSystemApp(
      SystemWebAppType type) const;

  // Returns the System App Type for the given |app_id|.
  std::optional<SystemWebAppType> GetSystemAppTypeForAppId(
      const webapps::AppId& app_id) const;

  // Returns the System App Delegate for the given App |type|.
  const SystemWebAppDelegate* GetSystemApp(SystemWebAppType type) const;

  // Returns the App Ids for all installed System Web Apps.
  std::vector<webapps::AppId> GetAppIds() const;

  // Returns whether |app_id| points to an installed System App.
  bool IsSystemWebApp(const webapps::AppId& app_id) const;

  // Returns the SystemWebAppType that should handle |url|.
  //
  // Under the hood, it returns the system web app whose `start_url` shares
  // the same origin with the given |url|. It does not take
  // `SystemWebAppDelegate::IsURLInSystemAppScope` into account.
  std::optional<SystemWebAppType> GetSystemAppForURL(const GURL& url) const;

  // Returns the SystemWebAppType that should capture the navigation to |url|.
  std::optional<SystemWebAppType> GetCapturingSystemAppForURL(
      const GURL& url) const;

  const base::OneShotEvent& on_apps_synchronized() const {
    return *on_apps_synchronized_;
  }

  // Return the OneShotEvent that is fired after all of the background tasks
  // have started and their timers become active.
  const base::OneShotEvent& on_tasks_started() const {
    return *on_tasks_started_;
  }

  // Returns the OneShotEvent that is fired after icon checks are complete.
  const base::OneShotEvent& on_icon_check_completed() const {
    return *on_icon_check_completed_;
  }

  // Returns a map of registered system app types and infos, these apps will be
  // installed on the system.
  const SystemWebAppDelegateMap& system_app_delegates() const {
    return system_app_delegates_;
  }

  // This call will override default System Apps configuration. You should call
  // Start() after this call to install |system_apps|.
  void SetSystemAppsForTesting(SystemWebAppDelegateMap system_apps);

  // Overrides the update policy. If AlwaysReinstallSystemWebApps feature is
  // enabled, this method does nothing, and system apps will be reinstalled.
  void SetUpdatePolicyForTesting(UpdatePolicy policy);

  void ResetForTesting();

  // Get the timers. Only use this for testing.
  const std::vector<std::unique_ptr<SystemWebAppBackgroundTask>>&
  GetBackgroundTasksForTesting();
  void StopBackgroundTasksForTesting();

  const Profile* profile() const { return profile_; }

 protected:
  virtual const base::Version& CurrentVersion() const;
  virtual const std::string& CurrentLocale() const;
  virtual bool PreviousSessionHadBrokenIcons() const;
  void StopBackgroundTasks();

 private:
  // Returns the list of origin trials to enable for |url| loaded in System
  // App |type|. Returns an empty vector if the App does not specify origin
  // trials for |url|.
  const std::vector<std::string>* GetEnabledOriginTrials(
      const SystemWebAppDelegate* system_app,
      const GURL& url) const;

  void OnAppsSynchronized(
      bool did_force_install_apps,
      const base::TimeTicks& install_start_time,
      std::map<GURL, web_app::ExternallyManagedAppManager::InstallResult>
          install_results,
      std::map<GURL, webapps::UninstallResultCode> uninstall_results);
  bool ShouldForceInstallApps() const;
  void UpdateLastAttemptedInfo();
  // Returns if we have exceeded the number of retry attempts allowed for this
  // version.
  bool CheckAndIncrementRetryAttempts();

  void RecordSystemWebAppInstallResults(
      const std::map<GURL, web_app::ExternallyManagedAppManager::InstallResult>&
          install_results) const;

  void RecordSystemWebAppInstallDuration(
      const base::TimeDelta& time_duration) const;

  void StartBackgroundTasks() const;

  void OnIconCheckResult(SystemWebAppIconChecker::IconState result);

  // web_app::WebAppUiManagerObserver:
  void OnReadyToCommitNavigation(
      const webapps::AppId& app_id,
      content::NavigationHandle* navigation_handle) override;
  void OnWebAppUiManagerDestroyed() override;

  void ConnectProviderToSystemWebAppDelegateMap(
      const SystemWebAppDelegateMap* system_web_apps_delegate_map) const;

  raw_ptr<Profile> profile_;
  // SystemWebAppManager KeyedService depends on WebAppProvider KeyedService,
  // therefore this pointer is always valid.
  raw_ptr<web_app::WebAppProvider> provider_ = nullptr;

  std::unique_ptr<base::OneShotEvent> on_apps_synchronized_;
  std::unique_ptr<base::OneShotEvent> on_tasks_started_;
  std::unique_ptr<base::OneShotEvent> on_icon_check_completed_;

  bool shutting_down_ = false;

  bool previous_session_had_broken_icons_ = false;

  std::string install_result_per_profile_histogram_name_;

  UpdatePolicy update_policy_;

  // We skip app installation in tests by default. Tests can trigger
  // installation by calling `InstallSystemAppsForTesting()` or
  // `SetSystemAppsForTesting()`.
  bool skip_app_installation_in_test_ = true;

  SystemWebAppDelegateMap system_app_delegates_;

  const raw_ptr<PrefService> pref_service_;

  std::vector<std::unique_ptr<SystemWebAppBackgroundTask>> tasks_;

  base::ScopedObservation<web_app::WebAppUiManager,
                          web_app::WebAppUiManagerObserver>
      ui_manager_observation_{this};

  // Always a valid pointer, has the same lifecycle as `this` in production.
  // Might be reset in tests.
  std::unique_ptr<SystemWebAppIconChecker> icon_checker_;

  base::WeakPtrFactory<SystemWebAppManager> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // CHROME_BROWSER_ASH_SYSTEM_WEB_APPS_SYSTEM_WEB_APP_MANAGER_H_