chromium/chrome/browser/lacros/app_mode/kiosk_session_service_lacros.cc

// Copyright 2021 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/lacros/app_mode/kiosk_session_service_lacros.h"

#include <memory>

#include "base/auto_reset.h"
#include "base/check.h"
#include "base/check_deref.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/app_mode/kiosk_browser_session.h"
#include "chrome/browser/extensions/extension_special_storage_policy.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chromeos/lacros/lacros_service.h"
#include "chromeos/startup/browser_params_proxy.h"
#include "components/prefs/pref_registry_simple.h"
#include "url/origin.h"

namespace {

static KioskSessionServiceLacros* g_kiosk_session_service = nullptr;

bool IsWebKioskSession() {
  return chromeos::BrowserParamsProxy::Get()->SessionType() ==
         crosapi::mojom::SessionType::kWebKioskSession;
}

void AttemptUserExit() {
  chromeos::LacrosService* service = chromeos::LacrosService::Get();
  CHECK(service);

  if (!service->IsAvailable<crosapi::mojom::KioskSessionService>()) {
    LOG(ERROR) << "Kiosk session service is not available.";
    return;
  }

  service->GetRemote<crosapi::mojom::KioskSessionService>()->AttemptUserExit();
}

}  // namespace

// Runs callback when a new browser is opened.
class NewBrowserObserver : public BrowserListObserver {
 public:
  explicit NewBrowserObserver(
      base::RepeatingCallback<void(Browser* browser)> on_browser_added)
      : on_browser_added_(std::move(on_browser_added)) {
    browser_list_observation_.Observe(BrowserList::GetInstance());
  }
  NewBrowserObserver(const NewBrowserObserver&) = delete;
  NewBrowserObserver& operator=(const NewBrowserObserver&) = delete;
  ~NewBrowserObserver() override = default;

  // BrowserListObserver:
  void OnBrowserAdded(Browser* browser) override {
    on_browser_added_.Run(browser);
  }

 private:
  base::ScopedObservation<BrowserList, NewBrowserObserver>
      browser_list_observation_{this};
  base::RepeatingCallback<void(Browser* browser)> on_browser_added_;
};

// static
KioskSessionServiceLacros* KioskSessionServiceLacros::Get() {
  CHECK(g_kiosk_session_service);
  return g_kiosk_session_service;
}

// static
void KioskSessionServiceLacros::RegisterLocalStatePrefs(
    PrefRegistrySimple* registry) {
  chromeos::KioskBrowserSession::RegisterLocalStatePrefs(registry);
}

// static
void KioskSessionServiceLacros::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  chromeos::KioskBrowserSession::RegisterProfilePrefs(registry);
}

KioskSessionServiceLacros::KioskSessionServiceLacros()
    : attempt_user_exit_(base::BindOnce(&AttemptUserExit)) {
  // TODO(b/321669108): add CHECK(!g_kiosk_session_service)
  DUMP_WILL_BE_CHECK(!g_kiosk_session_service);
  g_kiosk_session_service = this;

  if (IsWebKioskSession()) {
    new_browser_observer_ =
        std::make_unique<NewBrowserObserver>(base::BindRepeating(
            [](base::WeakPtr<KioskSessionServiceLacros> kiosk_service,
               Browser* browser) {
              if (!kiosk_service) {
                return;
              }
              kiosk_service->KioskSessionServiceLacros::InitWebKioskSession(
                  CHECK_DEREF(browser));
              kiosk_service->new_browser_observer_.reset();
            },
            weak_factory_.GetWeakPtr()));
  }
}

KioskSessionServiceLacros::~KioskSessionServiceLacros() {
  g_kiosk_session_service = nullptr;
}

void KioskSessionServiceLacros::InitChromeKioskSession(
    Profile* profile,
    const std::string& app_id) {
  LOG_IF(FATAL, kiosk_browser_session_)
      << "Kiosk browser session is already initialized.";
  kiosk_browser_session_ = std::make_unique<chromeos::KioskBrowserSession>(
      profile, std::move(attempt_user_exit_), g_browser_process->local_state());
  kiosk_browser_session_->InitForChromeAppKiosk(app_id);
}

void KioskSessionServiceLacros::InitWebKioskSession(Browser& browser) {
  LOG_IF(FATAL, kiosk_browser_session_)
      << "Kiosk session is already initialized.";

  kiosk_browser_session_ = std::make_unique<chromeos::KioskBrowserSession>(
      browser.profile(), std::move(attempt_user_exit_),
      g_browser_process->local_state());
  kiosk_browser_session_->InitForWebKiosk(browser.app_name());
  browser.profile()
      ->GetExtensionSpecialStoragePolicy()
      ->AddOriginWithUnlimitedStorage(url::Origin::Create(install_url_));

  for (auto& observer : observers_) {
    observer.KioskWebSessionInitialized();
  }
}

void KioskSessionServiceLacros::SetInstallUrl(const GURL& install_url) {
  // `SetInstallUrl` should be called once, but if it is called second time,
  // the url should be the same.
  CHECK(install_url_.is_empty() || install_url_ == install_url)
      << "install_url_=" << install_url_ << ", install_url=" << install_url;
  install_url_ = install_url;
}

std::unique_ptr<base::AutoReset<base::OnceClosure>>
KioskSessionServiceLacros::SetAttemptUserExitCallbackForTesting(
    base::OnceClosure attempt_user_exit) {
  return std::make_unique<base::AutoReset<base::OnceClosure>>(
      base::AutoReset<base::OnceClosure>(&attempt_user_exit_,
                                         std::move(attempt_user_exit)));
}

void KioskSessionServiceLacros::AddObserver(
    KioskSessionServiceLacros::Observer* observer) {
  observers_.AddObserver(observer);
}

void KioskSessionServiceLacros::RemoveObserver(
    KioskSessionServiceLacros::Observer* observer) {
  observers_.RemoveObserver(observer);
}