blob: 74cfdd4af562d67839638a2da7cb897480735ca7 [file] [log] [blame]
// Copyright 2021 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/full_restore/app_launch_handler.h"
#include <set>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/browser_app_launcher.h"
#include "chrome/browser/ash/login/session/user_session_manager.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/chromeos/full_restore/arc_window_utils.h"
#include "chrome/browser/chromeos/full_restore/full_restore_arc_task_handler.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "components/full_restore/app_launch_info.h"
#include "components/full_restore/full_restore_read_handler.h"
#include "components/full_restore/full_restore_save_handler.h"
#include "components/full_restore/full_restore_utils.h"
#include "components/full_restore/restore_data.h"
#include "components/services/app_service/public/cpp/app_update.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
#include "extensions/common/constants.h"
#include "ui/base/window_open_disposition.h"
namespace chromeos {
namespace full_restore {
AppLaunchHandler::AppLaunchHandler(Profile* profile) : profile_(profile) {
// FullRestoreReadHandler reads the full restore data from the full restore
// data file on a background task runner.
::full_restore::FullRestoreReadHandler::GetInstance()->ReadFromFile(
profile_->GetPath(), base::BindOnce(&AppLaunchHandler::OnGetRestoreData,
weak_ptr_factory_.GetWeakPtr()));
}
AppLaunchHandler::~AppLaunchHandler() = default;
void AppLaunchHandler::OnAppUpdate(const apps::AppUpdate& update) {
// If the restore flag |should_restore_| is false, or the restore data has not
// been read yet, or the app is not ready, don't launch the app for the
// restoration.
if (!should_restore_ || !restore_data_ || !update.ReadinessChanged() ||
update.Readiness() != apps::mojom::Readiness::kReady) {
return;
}
// If there is no restore data or the launch list for the app is empty, don't
// launch the app.
const auto& app_id_to_launch_list = restore_data_->app_id_to_launch_list();
if (app_id_to_launch_list.find(update.AppId()) ==
app_id_to_launch_list.end()) {
return;
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&AppLaunchHandler::LaunchApp,
weak_ptr_factory_.GetWeakPtr(),
update.AppType(), update.AppId()));
}
void AppLaunchHandler::OnAppRegistryCacheWillBeDestroyed(
apps::AppRegistryCache* cache) {
apps::AppRegistryCache::Observer::Observe(nullptr);
}
void AppLaunchHandler::LaunchBrowserWhenReady() {
// If the restore data has been loaded, and the user has chosen to restore,
// launch the browser.
if (should_restore_ && restore_data_) {
LaunchBrowser();
return;
}
// If the restore data hasn't been loaded, or the user hasn't chosen to
// restore, set should_launch_browser_ as true, and wait the restore data
// loaded, and the user selection, then we can launch the browser.
should_launch_browser_ = true;
}
void AppLaunchHandler::SetShouldRestore() {
should_restore_ = true;
MaybePostRestore();
}
void AppLaunchHandler::SetForceLaunchBrowserForTesting() {
force_launch_browser_ = true;
}
void AppLaunchHandler::OnGetRestoreData(
std::unique_ptr<::full_restore::RestoreData> restore_data) {
restore_data_ = std::move(restore_data);
if (ProfileHelper::Get()->GetUserByProfile(profile_) ==
user_manager::UserManager::Get()->GetPrimaryUser()) {
::full_restore::FullRestoreSaveHandler::GetInstance()
->SetPrimaryProfilePath(profile_->GetPath());
// In Multi-Profile mode, only set for the primary user. For other users,
// active profile path is set when switch users.
::full_restore::SetActiveProfilePath(profile_->GetPath());
}
MaybePostRestore();
}
void AppLaunchHandler::MaybePostRestore() {
// If the restore flag |should_restore_| is not true, or reading the restore
// data hasn't finished, don't restore.
if (!should_restore_ || !restore_data_)
return;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&AppLaunchHandler::MaybeRestore,
weak_ptr_factory_.GetWeakPtr()));
}
void AppLaunchHandler::MaybeRestore() {
if (should_launch_browser_) {
LaunchBrowser();
should_launch_browser_ = false;
}
// If there is no launch list from the restore data, we don't need to handle
// the restoration.
const auto& launch_list = restore_data_->app_id_to_launch_list();
if (launch_list.empty())
return;
// Observe AppRegistryCache to get the notification when the app is ready.
DCHECK(
apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile_));
auto* cache = &apps::AppServiceProxyFactory::GetForProfile(profile_)
->AppRegistryCache();
Observe(cache);
// Add the app to |app_ids| if there is a launch list from the restore data
// for the app.
std::set<std::string> app_ids;
cache->ForEachApp([&app_ids, &launch_list](const apps::AppUpdate& update) {
if (update.Readiness() == apps::mojom::Readiness::kReady &&
launch_list.find(update.AppId()) != launch_list.end()) {
app_ids.insert(update.AppId());
}
});
for (const auto& app_id : app_ids)
LaunchApp(cache->GetAppType(app_id), app_id);
}
void AppLaunchHandler::LaunchBrowser() {
// If the browser is not launched before reboot, don't launch browser during
// the startup phase.
const auto& launch_list = restore_data_->app_id_to_launch_list();
if (launch_list.find(extension_misc::kChromeAppId) == launch_list.end() &&
!force_launch_browser_) {
return;
}
restore_data_->RemoveApp(extension_misc::kChromeAppId);
// Modify the command line to restore browser sessions if not restore from
// crash.
if (profile_->GetLastSessionExitType() != Profile::EXIT_CRASHED) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kRestoreLastSession);
}
UserSessionManager::GetInstance()->LaunchBrowser(profile_);
UserSessionManager::GetInstance()->MaybeLaunchSettings(profile_);
}
void AppLaunchHandler::LaunchApp(apps::mojom::AppType app_type,
const std::string& app_id) {
DCHECK(restore_data_);
// For the Chrome browser, the browser session restore is used to restore the
// web pages, so we don't need to launch the app.
if (app_id == extension_misc::kChromeAppId) {
return;
}
const auto it = restore_data_->app_id_to_launch_list().find(app_id);
if (it == restore_data_->app_id_to_launch_list().end() ||
it->second.empty()) {
restore_data_->RemoveApp(app_id);
return;
}
switch (app_type) {
case apps::mojom::AppType::kArc:
LaunchArcApp(app_id, it->second);
break;
case apps::mojom::AppType::kExtension:
::full_restore::FullRestoreReadHandler::GetInstance()
->SetNextRestoreWindowIdForChromeApp(profile_->GetPath(), app_id);
// Deliberately fall through to apps::mojom::AppType::kWeb to launch the
// app.
FALLTHROUGH;
case apps::mojom::AppType::kWeb:
case apps::mojom::AppType::kSystemWeb:
LaunchSystemWebAppOrChromeApp(app_id, it->second);
break;
case apps::mojom::AppType::kBuiltIn:
case apps::mojom::AppType::kCrostini:
case apps::mojom::AppType::kPluginVm:
case apps::mojom::AppType::kUnknown:
case apps::mojom::AppType::kMacOs:
case apps::mojom::AppType::kStandaloneBrowser:
case apps::mojom::AppType::kRemote:
case apps::mojom::AppType::kBorealis:
NOTREACHED();
break;
}
restore_data_->RemoveApp(app_id);
}
void AppLaunchHandler::LaunchSystemWebAppOrChromeApp(
const std::string& app_id,
const ::full_restore::RestoreData::LaunchList& launch_list) {
auto* launcher = apps::AppServiceProxyFactory::GetForProfile(profile_)
->BrowserAppLauncher();
if (!launcher)
return;
for (const auto& it : launch_list) {
DCHECK(it.second->container.has_value());
DCHECK(it.second->disposition.has_value());
DCHECK(it.second->display_id.has_value());
apps::mojom::IntentPtr intent;
apps::AppLaunchParams params(
app_id,
static_cast<apps::mojom::LaunchContainer>(it.second->container.value()),
static_cast<WindowOpenDisposition>(it.second->disposition.value()),
apps::mojom::AppLaunchSource::kSourceChromeInternal,
it.second->display_id.value(),
it.second->file_paths.has_value() ? it.second->file_paths.value()
: std::vector<base::FilePath>{},
it.second->intent.has_value() ? it.second->intent.value() : intent);
params.restore_id = it.first;
launcher->LaunchAppWithParams(std::move(params));
}
}
void AppLaunchHandler::LaunchArcApp(
const std::string& app_id,
const ::full_restore::RestoreData::LaunchList& launch_list) {
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile_);
DCHECK(proxy);
auto* arc_handler = FullRestoreArcTaskHandler::GetForProfile(profile_);
for (const auto& it : launch_list) {
DCHECK(it.second->event_flag.has_value());
apps::mojom::WindowInfoPtr window_info =
HandleArcWindowInfo(it.second->GetAppWindowInfo());
// Set an ARC session id to find the restore window id based on the new
// created ARC task id in FullRestoreReadHandler.
int32_t arc_session_id =
::full_restore::FullRestoreReadHandler::GetInstance()
->GetArcSessionId();
window_info->window_id = arc_session_id;
::full_restore::FullRestoreReadHandler::GetInstance()
->SetArcSessionIdForWindowId(arc_session_id, it.first);
#if BUILDFLAG(ENABLE_WAYLAND_SERVER)
if (!window_info->bounds.is_null() && arc_handler &&
arc_handler->window_handler()) {
arc_handler->window_handler()->LaunchArcGhostWindow(
app_id, arc_session_id, it.second.get());
}
#endif
if (it.second->intent.has_value()) {
proxy->LaunchAppWithIntent(app_id, it.second->event_flag.value(),
std::move(it.second->intent.value()),
apps::mojom::LaunchSource::kFromFullRestore,
std::move(window_info));
} else {
proxy->Launch(app_id, it.second->event_flag.value(),
apps::mojom::LaunchSource::kFromFullRestore,
std::move(window_info));
}
}
}
} // namespace full_restore
} // namespace chromeos