blob: faf82c11968151797d603f1c9f781c2d6dc0c40b [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 <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/apps/app_service/app_platform_metrics.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/profiles/profile.h"
#include "components/full_restore/full_restore_read_handler.h"
#include "components/services/app_service/public/cpp/types_util.h"
#include "extensions/common/constants.h"
namespace chromeos {
namespace {
// Returns apps::AppTypeName used for metrics.
apps::AppTypeName GetHistogrameAppType(apps::mojom::AppType app_type) {
switch (app_type) {
case apps::mojom::AppType::kUnknown:
return apps::AppTypeName::kUnknown;
case apps::mojom::AppType::kArc:
return apps::AppTypeName::kArc;
case apps::mojom::AppType::kBuiltIn:
case apps::mojom::AppType::kCrostini:
return apps::AppTypeName::kUnknown;
case apps::mojom::AppType::kExtension:
return apps::AppTypeName::kChromeApp;
case apps::mojom::AppType::kWeb:
return apps::AppTypeName::kWeb;
case apps::mojom::AppType::kMacOs:
case apps::mojom::AppType::kPluginVm:
case apps::mojom::AppType::kStandaloneBrowser:
case apps::mojom::AppType::kRemote:
case apps::mojom::AppType::kBorealis:
return apps::AppTypeName::kUnknown;
case apps::mojom::AppType::kSystemWeb:
return apps::AppTypeName::kSystemWeb;
}
}
} // namespace
AppLaunchHandler::AppLaunchHandler(Profile* profile) : profile_(profile) {}
AppLaunchHandler::~AppLaunchHandler() = default;
bool AppLaunchHandler::HasRestoreData() {
return restore_data_ && !restore_data_->app_id_to_launch_list().empty();
}
void AppLaunchHandler::LaunchApps() {
// If there is no launch list from the restore data, we don't need to handle
// launching.
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::OnAppUpdate(const apps::AppUpdate& update) {
if (!restore_data_ || !update.ReadinessChanged())
return;
if (!apps_util::IsInstalled(update.Readiness())) {
restore_data_->RemoveApp(update.AppId());
return;
}
// If the app is not ready, don't launch the app for the restoration.
if (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, GetWeakPtrAppLaunchHandler(),
update.AppType(), update.AppId()));
}
void AppLaunchHandler::OnAppRegistryCacheWillBeDestroyed(
apps::AppRegistryCache* cache) {
apps::AppRegistryCache::Observer::Observe(nullptr);
}
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);
// ARC apps restoration could be delayed, so return to preserve the
// restore data for ARC apps.
return;
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_type, 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(
apps::mojom::AppType app_type,
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) {
RecordRestoredAppLaunch(GetHistogrameAppType(app_type));
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));
}
}
} // namespace chromeos