kiosk: Implement a base class for web and iwa kiosk launchers

Extracts functionality that is common between PWA and IWA kiosks
(e.g. app service launch, notifying observers, etc.) into a base class.

Bug: 361018151
Change-Id: Ieff5f954e070ee6b597406bff40dc95f3ac1d6ea
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5938230
Reviewed-by: Polina Bondarenko <pbond@chromium.org>
Commit-Queue: Sergii Bykov <sbykov@google.com>
Cr-Commit-Position: refs/heads/main@{#1371286}
diff --git a/chrome/browser/ash/app_mode/BUILD.gn b/chrome/browser/ash/app_mode/BUILD.gn
index d1d10cd..439503f5 100644
--- a/chrome/browser/ash/app_mode/BUILD.gn
+++ b/chrome/browser/ash/app_mode/BUILD.gn
@@ -56,6 +56,8 @@
     "kiosk_profile_load_failed_observer.h",
     "kiosk_system_session.cc",
     "kiosk_system_session.h",
+    "kiosk_web_app_launcher_base.cc",
+    "kiosk_web_app_launcher_base.h",
     "load_profile.cc",
     "load_profile.h",
     "pref_names.cc",
diff --git a/chrome/browser/ash/app_mode/kiosk_web_app_launcher_base.cc b/chrome/browser/ash/app_mode/kiosk_web_app_launcher_base.cc
new file mode 100644
index 0000000..4e8319f
--- /dev/null
+++ b/chrome/browser/ash/app_mode/kiosk_web_app_launcher_base.cc
@@ -0,0 +1,95 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/app_mode/kiosk_web_app_launcher_base.h"
+
+#include <memory>
+
+#include "base/check.h"
+#include "base/functional/bind.h"
+#include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h"
+#include "chrome/browser/ash/app_mode/kiosk_app_launcher.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/web_app_helpers.h"
+#include "components/account_id/account_id.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+
+namespace ash {
+
+KioskWebAppLauncherBase::KioskWebAppLauncherBase(
+    Profile* profile,
+    const AccountId& account_id,
+    KioskAppLauncher::NetworkDelegate* network_delegate)
+    : KioskAppLauncher(network_delegate),
+      profile_(profile),
+      account_id_(account_id) {}
+
+KioskWebAppLauncherBase::~KioskWebAppLauncherBase() = default;
+
+void KioskWebAppLauncherBase::AddObserver(
+    KioskAppLauncher::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void KioskWebAppLauncherBase::RemoveObserver(
+    KioskAppLauncher::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void KioskWebAppLauncherBase::Initialize() {
+  InitAppServiceLauncher();
+}
+
+void KioskWebAppLauncherBase::ContinueWithNetworkReady() {
+  observers().NotifyAppInstalling();
+}
+
+void KioskWebAppLauncherBase::LaunchApp() {
+  app_service_launcher().CheckAndMaybeLaunchApp(
+      GetInstalledWebAppId(),
+      base::BindOnce(&KioskWebAppLauncherBase::OnAppLaunched,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&KioskWebAppLauncherBase::OnAppBecomesVisible,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void KioskWebAppLauncherBase::NotifyAppPrepared() {
+  observers().NotifyAppPrepared();
+}
+
+void KioskWebAppLauncherBase::NotifyLaunchFailed(
+    KioskAppLaunchError::Error error) {
+  observers().NotifyLaunchFailed(error);
+}
+
+void KioskWebAppLauncherBase::OnAppLaunched(bool success) {
+  if (!success) {
+    observers().NotifyLaunchFailed(KioskAppLaunchError::Error::kUnableToLaunch);
+    return;
+  }
+  observers().NotifyAppLaunched();
+}
+
+void KioskWebAppLauncherBase::OnAppBecomesVisible() {
+  observers().NotifyAppWindowCreated(
+      web_app::GenerateApplicationNameFromAppId(GetInstalledWebAppId()));
+}
+
+void KioskWebAppLauncherBase::InitAppServiceLauncher() {
+  CHECK(!app_service_launcher_);
+  app_service_launcher_ =
+      std::make_unique<chromeos::KioskAppServiceLauncher>(profile_);
+
+  app_service_launcher_->EnsureAppTypeInitialized(
+      apps::AppType::kWeb,
+      base::BindOnce(&KioskWebAppLauncherBase::OnWebAppInitialized,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void KioskWebAppLauncherBase::OnWebAppInitialized() {
+  CheckAppInstallState();
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ash/app_mode/kiosk_web_app_launcher_base.h b/chrome/browser/ash/app_mode/kiosk_web_app_launcher_base.h
new file mode 100644
index 0000000..58cb77f
--- /dev/null
+++ b/chrome/browser/ash/app_mode/kiosk_web_app_launcher_base.h
@@ -0,0 +1,72 @@
+// Copyright 2024 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_APP_MODE_KIOSK_WEB_APP_LAUNCHER_BASE_H_
+#define CHROME_BROWSER_ASH_APP_MODE_KIOSK_WEB_APP_LAUNCHER_BASE_H_
+
+#include <memory>
+
+#include "base/check_deref.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h"
+#include "chrome/browser/ash/app_mode/kiosk_app_launcher.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/account_id/account_id.h"
+#include "components/webapps/common/web_app_id.h"
+
+namespace ash {
+
+class KioskWebAppLauncherBase : public KioskAppLauncher {
+ public:
+  KioskWebAppLauncherBase(Profile* profile,
+                          const AccountId& account_id,
+                          KioskAppLauncher::NetworkDelegate* network_delegate);
+  KioskWebAppLauncherBase(const KioskWebAppLauncherBase&) = delete;
+  KioskWebAppLauncherBase& operator=(const KioskWebAppLauncherBase&) = delete;
+  ~KioskWebAppLauncherBase() override;
+
+  // `KioskAppLauncher`:
+  void AddObserver(KioskAppLauncher::Observer* observer) override;
+  void RemoveObserver(KioskAppLauncher::Observer* observer) override;
+  void Initialize() override;
+  void ContinueWithNetworkReady() override;
+  void LaunchApp() override;
+
+ protected:
+  Profile* profile() const { return profile_; }
+  const AccountId& account_id() const { return account_id_; }
+  chromeos::KioskAppServiceLauncher& app_service_launcher() const {
+    return CHECK_DEREF(app_service_launcher_.get());
+  }
+
+  void NotifyAppPrepared();
+  void NotifyLaunchFailed(KioskAppLaunchError::Error error);
+
+ private:
+  KioskAppLauncher::ObserverList& observers() { return observers_; }
+
+  // `KioskAppServiceLauncher` callbacks.
+  void OnAppLaunched(bool success);
+  void OnAppBecomesVisible();
+
+  void InitAppServiceLauncher();
+  void OnWebAppInitialized();
+
+  virtual void CheckAppInstallState() = 0;
+  virtual const webapps::AppId& GetInstalledWebAppId() = 0;
+
+  KioskAppLauncher::ObserverList observers_;
+  raw_ptr<Profile> profile_;
+  const AccountId account_id_;
+
+  std::unique_ptr<chromeos::KioskAppServiceLauncher> app_service_launcher_;
+
+  base::WeakPtrFactory<KioskWebAppLauncherBase> weak_ptr_factory_{this};
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_APP_MODE_KIOSK_WEB_APP_LAUNCHER_BASE_H_
diff --git a/chrome/browser/ash/app_mode/web_app/web_kiosk_app_service_launcher.cc b/chrome/browser/ash/app_mode/web_app/web_kiosk_app_service_launcher.cc
index e3bda26..ea30da3d 100644
--- a/chrome/browser/ash/app_mode/web_app/web_kiosk_app_service_launcher.cc
+++ b/chrome/browser/ash/app_mode/web_app/web_kiosk_app_service_launcher.cc
@@ -4,9 +4,7 @@
 
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_service_launcher.h"
 
-#include <memory>
 #include <optional>
-#include <utility>
 
 #include "base/check.h"
 #include "base/check_deref.h"
@@ -14,9 +12,9 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_launcher.h"
+#include "chrome/browser/ash/app_mode/kiosk_web_app_launcher_base.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_data.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
-#include "chrome/browser/ash/crosapi/web_kiosk_service_ash.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_web_app_install_util.h"
 #include "chrome/browser/extensions/extension_special_storage_policy.h"
@@ -25,9 +23,7 @@
 #include "chromeos/crosapi/mojom/web_kiosk_service.mojom-shared.h"
 #include "chromeos/crosapi/mojom/web_kiosk_service.mojom.h"
 #include "components/account_id/account_id.h"
-#include "components/services/app_service/public/cpp/app_types.h"
 #include "components/webapps/common/web_app_id.h"
-#include "url/gurl.h"
 #include "url/origin.h"
 
 using crosapi::mojom::WebKioskInstaller;
@@ -39,84 +35,39 @@
     Profile* profile,
     const AccountId& account_id,
     KioskAppLauncher::NetworkDelegate* network_delegate)
-    : KioskAppLauncher(network_delegate),
-      profile_(profile),
-      account_id_(account_id) {}
+    : KioskWebAppLauncherBase(profile, account_id, network_delegate) {}
 
 WebKioskAppServiceLauncher::~WebKioskAppServiceLauncher() = default;
 
 const WebKioskAppData* WebKioskAppServiceLauncher::GetCurrentApp() const {
   const WebKioskAppData* app =
-      WebKioskAppManager::Get()->GetAppByAccountId(account_id_);
+      WebKioskAppManager::Get()->GetAppByAccountId(account_id());
   DCHECK(app);
   return app;
 }
 
-void WebKioskAppServiceLauncher::AddObserver(
-    KioskAppLauncher::Observer* observer) {
-  observers_.AddObserver(observer);
-}
-
-void WebKioskAppServiceLauncher::RemoveObserver(
-    KioskAppLauncher::Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
 void WebKioskAppServiceLauncher::Initialize() {
-  DCHECK(!app_service_launcher_);
-
-  app_service_launcher_ =
-      std::make_unique<chromeos::KioskAppServiceLauncher>(profile_);
-  app_service_launcher_->EnsureAppTypeInitialized(
-      apps::AppType::kWeb,
-      base::BindOnce(&WebKioskAppServiceLauncher::OnWebAppInitialized,
-                     weak_ptr_factory_.GetWeakPtr()));
+  KioskWebAppLauncherBase::Initialize();
 
   // By default the app service will try to launch the start_url as defined by
   // the web app's manifest. This is generally not what we want, so we need to
   // set the complete url as override url.
-  app_service_launcher_->SetLaunchUrl(GetCurrentApp()->install_url());
+  app_service_launcher().SetLaunchUrl(GetCurrentApp()->install_url());
 
-  profile_->GetExtensionSpecialStoragePolicy()->AddOriginWithUnlimitedStorage(
+  // TODO(crbug.com/372848158): Move to base class if it works for IWAs.
+  profile()->GetExtensionSpecialStoragePolicy()->AddOriginWithUnlimitedStorage(
       url::Origin::Create(GetCurrentApp()->install_url()));
 }
 
-void WebKioskAppServiceLauncher::OnWebAppInitialized() {
-  GetInstallState(
-      GetCurrentApp()->install_url(),
-      base::BindOnce(&WebKioskAppServiceLauncher::CheckWhetherNetworkIsRequired,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void WebKioskAppServiceLauncher::GetInstallState(
-    const GURL& install_url,
-    WebKioskInstaller::GetWebKioskInstallStateCallback callback) {
-  auto [state, app_id] = chromeos::GetKioskWebAppInstallState(
-      CHECK_DEREF(profile_.get()), install_url);
-  std::move(callback).Run(state, std::move(app_id));
-}
-
-void WebKioskAppServiceLauncher::CheckWhetherNetworkIsRequired(
-    WebKioskInstallState state,
-    const std::optional<webapps::AppId>& id) {
-  if (state != WebKioskInstallState::kInstalled ||
-      !profile_->GetPrefs()->GetBoolean(::prefs::kKioskWebAppOfflineEnabled)) {
-    delegate_->InitializeNetwork();
-    return;
-  }
-
-  NotifyAppPrepared(id);
-}
-
 void WebKioskAppServiceLauncher::ContinueWithNetworkReady() {
-  observers_.NotifyAppInstalling();
+  KioskWebAppLauncherBase::ContinueWithNetworkReady();
 
   // Start observing app update as soon a web app system is ready so that app
   // updates being applied while launching can be handled.
-  WebKioskAppManager::Get()->StartObservingAppUpdate(profile_, account_id_);
+  WebKioskAppManager::Get()->StartObservingAppUpdate(profile(), account_id());
 
   chromeos::InstallKioskWebApp(
-      CHECK_DEREF(profile_.get()), GetCurrentApp()->install_url(),
+      CHECK_DEREF(profile()), GetCurrentApp()->install_url(),
       base::BindOnce(&WebKioskAppServiceLauncher::OnInstallComplete,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -128,37 +79,32 @@
     return;
   }
 
-  observers_.NotifyLaunchFailed(KioskAppLaunchError::Error::kUnableToInstall);
+  NotifyLaunchFailed(KioskAppLaunchError::Error::kUnableToInstall);
 }
 
 void WebKioskAppServiceLauncher::NotifyAppPrepared(
-    const std::optional<webapps::AppId>& id) {
-  CHECK(id.has_value());
-  app_id_ = id.value();
-  observers_.NotifyAppPrepared();
+    const std::optional<webapps::AppId>& app_id) {
+  CHECK(app_id.has_value());
+  installed_app_id_ = app_id;
+  KioskWebAppLauncherBase::NotifyAppPrepared();
 }
 
-void WebKioskAppServiceLauncher::LaunchApp() {
-  DCHECK(app_service_launcher_);
-  app_service_launcher_->CheckAndMaybeLaunchApp(
-      app_id_,
-      base::BindOnce(&WebKioskAppServiceLauncher::OnAppLaunched,
-                     weak_ptr_factory_.GetWeakPtr()),
-      base::BindOnce(&WebKioskAppServiceLauncher::OnAppBecomesVisible,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
+void WebKioskAppServiceLauncher::CheckAppInstallState() {
+  auto [state, app_id] = chromeos::GetKioskWebAppInstallState(
+      CHECK_DEREF(profile()), GetCurrentApp()->install_url());
 
-void WebKioskAppServiceLauncher::OnAppLaunched(bool success) {
-  if (!success) {
-    observers_.NotifyLaunchFailed(KioskAppLaunchError::Error::kUnableToLaunch);
+  if (state != WebKioskInstallState::kInstalled ||
+      !profile()->GetPrefs()->GetBoolean(::prefs::kKioskWebAppOfflineEnabled)) {
+    delegate_->InitializeNetwork();
     return;
   }
-  observers_.NotifyAppLaunched();
+
+  NotifyAppPrepared(app_id);
 }
 
-void WebKioskAppServiceLauncher::OnAppBecomesVisible() {
-  observers_.NotifyAppWindowCreated(
-      web_app::GenerateApplicationNameFromAppId(app_id_));
+const webapps::AppId& WebKioskAppServiceLauncher::GetInstalledWebAppId() {
+  CHECK(installed_app_id_.has_value());
+  return installed_app_id_.value();
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/app_mode/web_app/web_kiosk_app_service_launcher.h b/chrome/browser/ash/app_mode/web_app/web_kiosk_app_service_launcher.h
index 6a325c3..d093116 100644
--- a/chrome/browser/ash/app_mode/web_app/web_kiosk_app_service_launcher.h
+++ b/chrome/browser/ash/app_mode/web_app/web_kiosk_app_service_launcher.h
@@ -5,19 +5,14 @@
 #ifndef CHROME_BROWSER_ASH_APP_MODE_WEB_APP_WEB_KIOSK_APP_SERVICE_LAUNCHER_H_
 #define CHROME_BROWSER_ASH_APP_MODE_WEB_APP_WEB_KIOSK_APP_SERVICE_LAUNCHER_H_
 
-#include <memory>
 #include <optional>
-#include <string>
 
-#include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_launcher.h"
-#include "chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h"
+#include "chrome/browser/ash/app_mode/kiosk_web_app_launcher_base.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_web_app_install_util.h"
 #include "chrome/browser/web_applications/externally_managed_app_manager.h"
-#include "chromeos/crosapi/mojom/web_kiosk_service.mojom-shared.h"
-#include "chromeos/crosapi/mojom/web_kiosk_service.mojom.h"
 #include "components/account_id/account_id.h"
 #include "components/webapps/common/web_app_id.h"
 
@@ -29,7 +24,7 @@
 
 // Responsible of installing and launching web kiosk app using App Service. It's
 // destroyed upon successful app launch.
-class WebKioskAppServiceLauncher : public KioskAppLauncher {
+class WebKioskAppServiceLauncher : public KioskWebAppLauncherBase {
  public:
   // Histogram to log the installed web app is a placeholder.
   static constexpr char kWebAppIsPlaceholderUMA[] =
@@ -49,36 +44,20 @@
   ~WebKioskAppServiceLauncher() override;
 
   // `KioskAppLauncher`:
-  void AddObserver(KioskAppLauncher::Observer* observer) override;
-  void RemoveObserver(KioskAppLauncher::Observer* observer) override;
   void Initialize() override;
   void ContinueWithNetworkReady() override;
-  void LaunchApp() override;
 
  private:
-  // `KioskAppServiceLauncher` callbacks.
-  void OnWebAppInitialized();
   void NotifyAppPrepared(const std::optional<webapps::AppId>& app_id);
-  void OnAppLaunched(bool success);
-  void OnAppBecomesVisible();
-
-  void GetInstallState(
-      const GURL& url,
-      crosapi::mojom::WebKioskInstaller::GetWebKioskInstallStateCallback
-          callback);
-  void CheckWhetherNetworkIsRequired(crosapi::mojom::WebKioskInstallState state,
-                                     const std::optional<webapps::AppId>& id);
   void OnInstallComplete(const std::optional<webapps::AppId>& app_id);
 
   // Get the current web application to be launched in the session.
   const WebKioskAppData* GetCurrentApp() const;
 
-  raw_ptr<Profile> profile_;
-  const AccountId account_id_;
-  std::string app_id_;
-  KioskAppLauncher::ObserverList observers_;
+  void CheckAppInstallState() override;
+  const webapps::AppId& GetInstalledWebAppId() override;
 
-  std::unique_ptr<chromeos::KioskAppServiceLauncher> app_service_launcher_;
+  std::optional<webapps::AppId> installed_app_id_;
 
   base::WeakPtrFactory<WebKioskAppServiceLauncher> weak_ptr_factory_{this};
 };