Commit a53cf24b authored by Anatoliy Potapchuk's avatar Anatoliy Potapchuk Committed by Commit Bot

Add app installation/launch logic for Web App Kiosks

This CL provides basic logic presuming we have network connection during
the installation. Network handling will be done in a following CL.

Bug: 1015383,1006230
Change-Id: Id60b480fd5fdce04a61fdd7c226424e204f18882
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1879451
Commit-Queue: Anatoliy Potapchuk <apotapchuk@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarSergey Poromov <poromov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713414}
parent df6a24aa
......@@ -67,7 +67,9 @@ bool IsRunningInAppMode() {
bool IsRunningInForcedAppMode() {
return GetForcedAppModeApp().has_value() ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kForceAndroidAppMode);
switches::kForceAndroidAppMode) ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kForceWebAppMode);
}
bool IsRunningInForcedAppModeForApp(const std::string& app_id) {
......
......@@ -435,6 +435,8 @@ source_set("chromeos") {
"app_mode/startup_app_launcher_update_checker.h",
"app_mode/web_app/web_kiosk_app_data.cc",
"app_mode/web_app/web_kiosk_app_data.h",
"app_mode/web_app/web_kiosk_app_launcher.cc",
"app_mode/web_app/web_kiosk_app_launcher.h",
"app_mode/web_app/web_kiosk_app_manager.cc",
"app_mode/web_app/web_kiosk_app_manager.h",
"apps/apk_web_app_installer.cc",
......
......@@ -4,15 +4,22 @@
#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_data_delegate.h"
#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.h"
#include "chrome/common/web_application_info.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
namespace chromeos {
namespace {
constexpr int kIconSize = 128; // size of the icon in px.
const char kKeyLaunchUrl[] = "launch_url";
} // namespace
WebKioskAppData::WebKioskAppData(KioskAppDataDelegate* delegate,
const std::string& app_id,
const AccountId& account_id,
......@@ -22,8 +29,8 @@ WebKioskAppData::WebKioskAppData(KioskAppDataDelegate* delegate,
account_id),
delegate_(delegate),
status_(STATUS_INIT),
url_(url) {
name_ = url_.spec();
install_url_(url) {
name_ = install_url_.spec();
}
WebKioskAppData::~WebKioskAppData() = default;
......@@ -35,26 +42,75 @@ bool WebKioskAppData::LoadFromCache() {
if (!LoadFromDictionary(base::Value::AsDictionaryValue(*dict)))
return false;
if (LoadLaunchUrlFromDictionary(*dict)) {
SetStatus(STATUS_INSTALLED);
return true;
}
// Wait while icon is loaded.
if (status_ == STATUS_INIT)
SetStatus(STATUS_LOADING);
return true;
}
void WebKioskAppData::UpdateFromWebAppInfo(
std::unique_ptr<WebApplicationInfo> app_info) {
DCHECK(app_info);
name_ = base::UTF16ToUTF8(app_info->title);
base::FilePath cache_dir;
if (delegate_)
delegate_->GetKioskAppIconCacheDir(&cache_dir);
for (const WebApplicationIconInfo& icon_info : app_info->icons) {
if (icon_info.width == kIconSize) {
icon_ = gfx::ImageSkia::CreateFrom1xBitmap(icon_info.data);
icon_.MakeThreadSafe();
SaveIcon(icon_info.data, cache_dir);
break;
}
}
PrefService* local_state = g_browser_process->local_state();
DictionaryPrefUpdate dict_update(local_state, dictionary_name());
launch_url_ = GURL(app_info->app_url);
const std::string app_key =
std::string(KioskAppDataBase::kKeyApps) + '.' + app_id();
const std::string launch_url_key = app_key + '.' + kKeyLaunchUrl;
dict_update->SetString(launch_url_key, launch_url_.spec());
SaveToDictionary(dict_update);
SetStatus(STATUS_INSTALLED);
}
void WebKioskAppData::SetStatus(Status status) {
if (status_ == status)
return;
status_ = status;
if (delegate_)
delegate_->OnKioskAppDataChanged(app_id());
}
bool WebKioskAppData::LoadLaunchUrlFromDictionary(const base::Value& dict) {
const std::string app_key =
std::string(KioskAppDataBase::kKeyApps) + '.' + app_id();
const std::string launch_url_key = app_key + '.' + kKeyLaunchUrl;
const std::string* launch_url_string = dict.FindStringKey(launch_url_key);
if (!launch_url_string)
return false;
launch_url_ = GURL(*launch_url_string);
return true;
}
void WebKioskAppData::OnIconLoadSuccess(const gfx::ImageSkia& icon) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
kiosk_app_icon_loader_.reset();
icon_ = icon;
SetStatus(STATUS_LOADED);
if (status_ != STATUS_INSTALLED)
SetStatus(STATUS_LOADED);
else
SetStatus(STATUS_INSTALLED); // To notify menu controller.
}
void WebKioskAppData::OnIconLoadFailure() {
......
......@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_CHROMEOS_APP_MODE_WEB_APP_WEB_KIOSK_APP_DATA_H_
#define CHROME_BROWSER_CHROMEOS_APP_MODE_WEB_APP_WEB_KIOSK_APP_DATA_H_
#include <memory>
#include <string>
#include "base/macros.h"
......@@ -13,6 +14,8 @@
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
struct WebApplicationInfo;
namespace chromeos {
class KioskAppDataDelegate;
......@@ -20,9 +23,11 @@ class KioskAppDataDelegate;
class WebKioskAppData : public KioskAppDataBase {
public:
enum Status {
STATUS_INIT, // Data initialized with app id.
STATUS_LOADING, // Loading data from cache or web store.
STATUS_LOADED, // Data load finished.
STATUS_INIT, // Data initialized with app id.
STATUS_LOADING, // Loading data from cache or web store.
STATUS_LOADED, // Data load finished.
STATUS_INSTALLED, // Icon and launch url are fetched and app can be run
// without them.
};
WebKioskAppData(KioskAppDataDelegate* delegate,
const std::string& app_id,
......@@ -40,13 +45,19 @@ class WebKioskAppData : public KioskAppDataBase {
void SetStatus(Status status);
void UpdateFromWebAppInfo(std::unique_ptr<WebApplicationInfo> app_info);
Status status() const { return status_; }
const GURL& url() const { return url_; }
const GURL& install_url() const { return install_url_; }
const GURL& launch_url() const { return launch_url_; }
private:
bool LoadLaunchUrlFromDictionary(const base::Value& dict);
const KioskAppDataDelegate* delegate_; // not owned.
Status status_;
const GURL url_;
const GURL install_url_; // installation url.
GURL launch_url_; // app launch url.
DISALLOW_COPY_AND_ASSIGN(WebKioskAppData);
};
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.h>
#include <memory>
#include "ash/public/cpp/window_pin_type.h"
#include "ash/public/cpp/window_properties.h"
#include "base/bind.h"
#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.h"
#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.h"
#include "chrome/browser/extensions/api/tabs/tabs_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/web_applications/components/web_app_data_retriever.h"
#include "chrome/browser/web_applications/components/web_app_url_loader.h"
#include "chrome/browser/web_applications/web_app_install_task.h"
#include "components/account_id/account_id.h"
#include "ui/aura/window.h"
#include "ui/base/page_transition_types.h"
namespace chromeos {
WebKioskAppLauncher::WebKioskAppLauncher(
Profile* profile,
WebKioskAppLauncher::Delegate* delegate)
: profile_(profile),
delegate_(delegate),
url_loader_(std::make_unique<web_app::WebAppUrlLoader>()) {}
WebKioskAppLauncher::~WebKioskAppLauncher() = default;
void WebKioskAppLauncher::Initialize(const AccountId& account_id) {
account_id_ = account_id;
const WebKioskAppData* app =
WebKioskAppManager::Get()->GetAppByAccountId(account_id_);
DCHECK(app);
if (app->status() == WebKioskAppData::STATUS_INSTALLED) {
delegate_->OnAppPrepared();
return;
}
// If the app is not yet installed -- require network connection.
delegate_->InitializeNetwork();
}
void WebKioskAppLauncher::ContinueWithNetworkReady() {
delegate_->OnAppStartedInstalling();
DCHECK(!is_installed_);
install_task_.reset(new web_app::WebAppInstallTask(
profile_, /*shortcut_manager=*/nullptr, /*install_finalizer=*/nullptr,
std::make_unique<web_app::WebAppDataRetriever>()));
install_task_->LoadAndRetrieveWebApplicationInfoWithIcons(
WebKioskAppManager::Get()->GetAppByAccountId(account_id_)->install_url(),
url_loader_.get(),
base::BindOnce(&WebKioskAppLauncher::OnAppDataObtained,
weak_ptr_factory_.GetWeakPtr()));
}
void WebKioskAppLauncher::OnAppDataObtained(
std::unique_ptr<WebApplicationInfo> app_info) {
if (app_info) {
WebKioskAppManager::Get()->UpdateAppByAccountId(account_id_,
std::move(app_info));
}
// If we could not update the app data, we should still launch the app(we may
// be under captive portal, there can be redirect, etc).
delegate_->OnAppPrepared();
}
void WebKioskAppLauncher::LaunchApp() {
DCHECK(!browser_);
const WebKioskAppData* app =
WebKioskAppManager::Get()->GetAppByAccountId(account_id_);
DCHECK(app);
GURL url = app->status() == WebKioskAppData::STATUS_INSTALLED
? app->launch_url()
: app->install_url();
Browser::CreateParams params(Browser::TYPE_APP, profile_, false);
browser_ = Browser::Create(params);
NavigateParams nav_params(browser_, url,
ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL);
Navigate(&nav_params);
CHECK(browser_);
CHECK(browser_->window());
CHECK(browser_->window()->GetNativeWindow());
browser_->window()->GetNativeWindow()->SetProperty(
ash::kWindowPinTypeKey, ash::WindowPinType::kTrustedPinned);
browser_->window()->Show();
delegate_->OnAppLaunched();
}
} // namespace chromeos
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_APP_MODE_WEB_APP_WEB_KIOSK_APP_LAUNCHER_H_
#define CHROME_BROWSER_CHROMEOS_APP_MODE_WEB_APP_WEB_KIOSK_APP_LAUNCHER_H_
#include <memory>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h"
#include "chrome/browser/web_applications/components/web_app_install_utils.h"
#include "chrome/browser/web_applications/components/web_app_url_loader.h"
#include "components/account_id/account_id.h"
#include "content/public/browser/web_contents.h"
#include "url/gurl.h"
class Browser;
class Profile;
namespace web_app {
class WebAppInstallTask;
class WebAppUrlLoader;
} // namespace web_app
namespace chromeos {
// Object responsible for preparing and launching web kiosk app. Is destroyed
// upon app launch.
class WebKioskAppLauncher {
public:
class Delegate {
public:
virtual void InitializeNetwork() = 0;
virtual void OnAppStartedInstalling() = 0;
virtual void OnAppPrepared() = 0;
virtual void OnAppLaunched() = 0;
virtual void OnAppLaunchFailed() = 0;
protected:
virtual ~Delegate() {}
};
WebKioskAppLauncher(Profile* profile, Delegate* delegate);
~WebKioskAppLauncher();
// Prepares the environment for an app launch.
void Initialize(const AccountId& account_id);
// Continues the installation when the network i
void ContinueWithNetworkReady();
// Launches the app after the initialization is done.
void LaunchApp();
private:
void OnAppDataObtained(std::unique_ptr<WebApplicationInfo> app_info);
bool is_installed_ = false; // Whether the installation was completed.
AccountId account_id_;
Profile* const profile_;
Delegate* const delegate_; // Not owned. Owns us.
Browser* browser_ = nullptr; // Browser instance that runs the web kiosk app.
std::unique_ptr<web_app::WebAppInstallTask>
install_task_; // task that is used to install the app.
std::unique_ptr<web_app::WebAppUrlLoader>
url_loader_; // Loads the app to be installed.
base::WeakPtrFactory<WebKioskAppLauncher> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(WebKioskAppLauncher);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_APP_MODE_WEB_APP_WEB_KIOSK_APP_LAUNCHER_H_
......@@ -10,6 +10,7 @@
#include "chrome/browser/chromeos/app_mode/kiosk_cryptohome_remover.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h"
#include "chrome/common/web_application_info.h"
#include "chromeos/settings/cros_settings_names.h"
#include "components/prefs/pref_registry_simple.h"
......@@ -52,7 +53,7 @@ void WebKioskAppManager::GetApps(std::vector<App>* apps) const {
apps->reserve(apps_.size());
for (auto& web_app : apps_) {
App app(*web_app);
app.url = web_app->url();
app.url = web_app->install_url();
apps->push_back(std::move(app));
}
}
......@@ -71,6 +72,18 @@ const WebKioskAppData* WebKioskAppManager::GetAppByAccountId(
return nullptr;
}
void WebKioskAppManager::UpdateAppByAccountId(
const AccountId& account_id,
std::unique_ptr<WebApplicationInfo> app_info) {
for (auto& web_app : apps_) {
if (web_app->account_id() == account_id) {
web_app->UpdateFromWebAppInfo(std::move(app_info));
return;
}
}
NOTREACHED();
}
void WebKioskAppManager::UpdateAppsFromPolicy() {
// Store current apps. We will compare old and new apps to determine which
// apps are new, and which were deleted.
......
......@@ -14,6 +14,7 @@
#include "components/account_id/account_id.h"
class PrefRegistrySimple;
struct WebApplicationInfo;
namespace chromeos {
......@@ -39,6 +40,10 @@ class WebKioskAppManager : public KioskAppManagerBase {
// Obtains an app associated with given |account_id|.
const WebKioskAppData* GetAppByAccountId(const AccountId& account_id) const;
// Updates app by the data obtained during installation.
void UpdateAppByAccountId(const AccountId& account_id,
std::unique_ptr<WebApplicationInfo> app_info);
private:
// KioskAppManagerBase:
// Updates |apps_| based on CrosSettings.
......
......@@ -41,8 +41,7 @@ ash::mojom::AssistantAllowedState IsAssistantAllowedForProfile(
if (user_manager::UserManager::Get()->IsLoggedInAsPublicAccount())
return ash::mojom::AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION;
if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp() ||
user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp()) {
if (user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp()) {
return ash::mojom::AssistantAllowedState::DISALLOWED_BY_KIOSK_MODE;
}
......
......@@ -866,6 +866,10 @@ bool DeviceLocalAccountManagementPolicyProvider::UserMayLoad(
extension->GetType() == extensions::Manifest::TYPE_EXTENSION) {
return true;
}
} else if (account_type_ == policy::DeviceLocalAccount::TYPE_WEB_KIOSK_APP) {
if (extension->GetType() == extensions::Manifest::TYPE_EXTENSION) {
return true;
}
}
// Disallow all other extensions.
......
......@@ -1042,6 +1042,7 @@ void ChromeUserManagerImpl::WebKioskAppLoggedIn(user_manager::User* user) {
user_manager::User::USER_IMAGE_INVALID, false);
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitch(::switches::kForceWebAppMode);
command_line->AppendSwitch(
::switches::kSilentLaunch); // To open no extra windows.
......
......@@ -558,6 +558,11 @@ bool FakeChromeUserManager::IsLoggedInAsArcKioskApp() const {
: false;
}
bool FakeChromeUserManager::IsLoggedInAsAnyKioskApp() const {
const user_manager::User* active_user = GetActiveUser();
return active_user && active_user->IsKioskType();
}
bool FakeChromeUserManager::IsLoggedInAsStub() const {
return false;
}
......
......@@ -109,6 +109,7 @@ class FakeChromeUserManager : public ChromeUserManager {
bool IsLoggedInAsSupervisedUser() const override;
bool IsLoggedInAsKioskApp() const override;
bool IsLoggedInAsArcKioskApp() const override;
bool IsLoggedInAsAnyKioskApp() const override;
bool IsLoggedInAsStub() const override;
bool IsUserNonCryptohomeDataEphemeral(
const AccountId& account_id) const override;
......
......@@ -61,7 +61,7 @@ KioskAppManagerBase::App WebKioskController::GetAppData() {
void WebKioskController::OnTimerFire() {
// Start launching now.
if (app_prepared_) {
// TODO(crbug.com/1006230): Implement LaunchApp();
app_launcher_->LaunchApp();
} else {
launch_on_install_ = true;
}
......@@ -152,8 +152,16 @@ void WebKioskController::OnProfilePrepared(Profile* profile,
// Reset virtual keyboard to use IME engines in app profile early.
ChromeKeyboardControllerClient::Get()->RebuildKeyboardIfEnabled();
// TODO(crbug.com/1006230): Implement app installation/initalization start
// here.
app_launcher_.reset(new WebKioskAppLauncher(profile, this));
app_launcher_->Initialize(account_id_);
}
void WebKioskController::InitializeNetwork() {
if (!web_kiosk_splash_screen_view_)
return;
// TODO(crbug.com/1006230): Implement network dialog flow.
app_launcher_->ContinueWithNetworkReady();
}
void WebKioskController::OnAppStartedInstalling() {
......@@ -167,14 +175,14 @@ void WebKioskController::OnAppStartedInstalling() {
void WebKioskController::OnAppPrepared() {
app_prepared_ = true;
if (web_kiosk_splash_screen_view_)
if (!web_kiosk_splash_screen_view_)
return;
web_kiosk_splash_screen_view_->UpdateAppLaunchState(
AppLaunchSplashScreenView::AppLaunchState::
APP_LAUNCH_STATE_WAITING_APP_WINDOW);
web_kiosk_splash_screen_view_->Show();
if (launch_on_install_) {
// TODO(crbug.com/1006230): Launch app here.
app_launcher_->LaunchApp();
}
}
......@@ -183,6 +191,7 @@ void WebKioskController::OnAppLaunched() {
// more seconds to give the user ability to exit Web kiosk.
SYSLOG(INFO) << "Kiosk launch succeeded, wait for app window.";
session_manager::SessionManager::Get()->SessionStarted();
CloseSplashScreen();
}
void WebKioskController::OnAppLaunchFailed() {
......
......@@ -8,6 +8,7 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_base.h"
#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.h"
#include "chrome/browser/chromeos/login/session/user_session_manager.h"
#include "chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h"
#include "chromeos/login/auth/login_performer.h"
......@@ -29,19 +30,14 @@ class UserContext;
// kiosk profile, and updating the splash screen UI.
class WebKioskController : public LoginPerformer::Delegate,
public UserSessionManagerDelegate,
public AppLaunchSplashScreenView::Delegate {
public AppLaunchSplashScreenView::Delegate,
public WebKioskAppLauncher::Delegate {
public:
WebKioskController(LoginDisplayHost* host, OobeUI* oobe_ui);
~WebKioskController() override;
void StartWebKiosk(const AccountId& account_id);
// Callbacks to Web Kiosk Launcher
void OnAppStartedInstalling();
void OnAppPrepared();
void OnAppLaunched();
void OnAppLaunchFailed();
private:
// LoginPerformer::Delegate:
void OnAuthFailure(const AuthFailure& error) override;
......@@ -63,6 +59,13 @@ class WebKioskController : public LoginPerformer::Delegate,
void OnDeletingSplashScreenView() override;
KioskAppManagerBase::App GetAppData() override;
// WebKioskAppLauncher:
void InitializeNetwork() override;
void OnAppStartedInstalling() override;
void OnAppPrepared() override;
void OnAppLaunched() override;
void OnAppLaunchFailed() override;
void CleanUp();
void OnTimerFire();
void CloseSplashScreen();
......@@ -75,6 +78,9 @@ class WebKioskController : public LoginPerformer::Delegate,
bool app_prepared_ = false;
bool launch_on_install_ = false;
// Used to prepare and launch the actual web kiosk app, is created after
// profile initialization.
std::unique_ptr<WebKioskAppLauncher> app_launcher_;
// Used to execute login operations.
std::unique_ptr<LoginPerformer> login_performer_;
......
......@@ -344,6 +344,10 @@ const char kForceFirstRun[] = "force-first-run";
// Forces Chrome to use a stacked tab strip layout.
const char kForceStackedTabStripLayout[] = "force-stacked-tab-strip-layout";
// Forces web-application mode. This hides certain system UI elements and forces
// the app to be installed if it hasn't been already.
const char kForceWebAppMode[] = "force-web-app-mode";
// Specifies which page will be displayed in newly-opened tabs. We need this
// for testing purposes so that the UI tests don't depend on what comes up for
// http://google.com.
......
......@@ -110,6 +110,7 @@ extern const char kForceAndroidAppMode[];
extern const char kForceAppMode[];
extern const char kForceFirstRun[];
extern const char kForceStackedTabStripLayout[];
extern const char kForceWebAppMode[];
extern const char kHomePage[];
extern const char kIncognito[];
extern const char kInstallAutogeneratedTheme[];
......
......@@ -260,6 +260,11 @@ bool FakeUserManager::IsLoggedInAsArcKioskApp() const {
: false;
}
bool FakeUserManager::IsLoggedInAsAnyKioskApp() const {
const User* active_user = GetActiveUser();
return active_user && active_user->IsKioskType();
}
bool FakeUserManager::IsLoggedInAsStub() const {
return false;
}
......
......@@ -96,6 +96,7 @@ class USER_MANAGER_EXPORT FakeUserManager : public UserManagerBase {
bool IsLoggedInAsSupervisedUser() const override;
bool IsLoggedInAsKioskApp() const override;
bool IsLoggedInAsArcKioskApp() const override;
bool IsLoggedInAsAnyKioskApp() const override;
bool IsLoggedInAsStub() const override;
bool IsUserNonCryptohomeDataEphemeral(
const AccountId& account_id) const override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment