blob: 16b017f6d9e63d1ed336bc486b60fcaee9f5dc77 [file] [log] [blame]
// Copyright 2020 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 "components/full_restore/full_restore_save_handler.h"
#include "ash/constants/app_types.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/files/file_path.h"
#include "base/no_destructor.h"
#include "base/sequenced_task_runner.h"
#include "base/time/time.h"
#include "components/full_restore/app_launch_info.h"
#include "components/full_restore/full_restore_file_handler.h"
#include "components/full_restore/full_restore_info.h"
#include "components/full_restore/full_restore_read_handler.h"
#include "components/full_restore/full_restore_utils.h"
#include "components/full_restore/restore_data.h"
#include "components/full_restore/window_info.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/sessions/core/session_id.h"
#include "extensions/common/constants.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
namespace full_restore {
namespace {
// Delay between when an update is received, and when we save it to the
// full restore file.
constexpr base::TimeDelta kSaveDelay = base::TimeDelta::FromMilliseconds(2500);
// Delay starting `save_timer_` during the system startup phase.
constexpr base::TimeDelta kWaitDelay = base::TimeDelta::FromSeconds(120);
const char kCrxAppPrefix[] = "_crx_";
std::string GetAppIdFromAppName(const std::string& app_name) {
std::string prefix(kCrxAppPrefix);
if (app_name.substr(0, prefix.length()) != prefix)
return std::string();
return app_name.substr(prefix.length());
}
} // namespace
FullRestoreSaveHandler* FullRestoreSaveHandler::GetInstance() {
static base::NoDestructor<FullRestoreSaveHandler> full_restore_save_handler;
return full_restore_save_handler.get();
}
FullRestoreSaveHandler::FullRestoreSaveHandler() {
if (aura::Env::HasInstance())
env_observer_.Observe(aura::Env::GetInstance());
}
FullRestoreSaveHandler::~FullRestoreSaveHandler() = default;
void FullRestoreSaveHandler::SetPrimaryProfilePath(
const base::FilePath& profile_path) {
primary_profile_path_ = profile_path;
arc_save_handler_ = std::make_unique<ArcSaveHandler>(primary_profile_path_);
}
void FullRestoreSaveHandler::SetActiveProfilePath(
const base::FilePath& profile_path) {
active_profile_path_ = profile_path;
}
void FullRestoreSaveHandler::SetAppRegistryCache(
const base::FilePath& profile_path,
apps::AppRegistryCache* app_registry_cache) {
if (app_registry_cache)
profile_path_to_app_registry_cache_[profile_path] = app_registry_cache;
else
profile_path_to_app_registry_cache_.erase(profile_path);
}
void FullRestoreSaveHandler::AllowSave() {
if (allow_save_)
return;
allow_save_ = true;
if (wait_timer_.IsRunning())
wait_timer_.Stop();
if (!is_shut_down_ &&
base::Contains(pending_save_profile_paths_, active_profile_path_)) {
MaybeStartSaveTimer(active_profile_path_);
}
}
void FullRestoreSaveHandler::SetShutDown() {
is_shut_down_ = true;
}
void FullRestoreSaveHandler::OnWindowInitialized(aura::Window* window) {
if (window->GetProperty(aura::client::kAppType) ==
static_cast<int>(ash::AppType::ARC_APP)) {
observed_windows_.AddObservation(window);
if (arc_save_handler_)
arc_save_handler_->OnWindowInitialized(window);
++window_count_;
return;
}
int32_t window_id = window->GetProperty(::full_restore::kWindowIdKey);
if (!SessionID::IsValidValue(window_id))
return;
++window_count_;
observed_windows_.AddObservation(window);
std::string* app_id_str = window->GetProperty(::full_restore::kAppIdKey);
AppLaunchInfoPtr app_launch_info;
if (app_id_str) {
// For Chrome apps, launched via event, get the app id from the window's
// property, then find the app launch info from
// |app_id_to_app_launch_infos_| to save the app launch info for |app_id|
// and |window_id|.
auto it = app_id_to_app_launch_infos_.find(*app_id_str);
if (it == app_id_to_app_launch_infos_.end())
return;
auto launch_it = it->second.find(active_profile_path_);
if (launch_it == it->second.end())
return;
DCHECK(!launch_it->second.empty());
app_launch_info = std::move(*launch_it->second.begin());
app_launch_info->window_id = window_id;
it->second.erase(active_profile_path_);
if (it->second.empty())
app_id_to_app_launch_infos_.erase(it);
} else {
app_launch_info = std::make_unique<AppLaunchInfo>(
extension_misc::kChromeAppId, window_id);
// If the window is an app type browser window, set `app_type_browser` as
// true, to call the browser session restore to restore apps for the next
// system startup.
if (window->GetProperty(full_restore::kAppTypeBrowser)) {
app_launch_info->app_type_browser = true;
std::string* browser_app_name =
window->GetProperty(full_restore::kBrowserAppNameKey);
if (browser_app_name) {
std::string app_id = GetAppIdFromAppName(*browser_app_name);
auto it =
profile_path_to_app_registry_cache_.find(active_profile_path_);
if (it != profile_path_to_app_registry_cache_.end() && it->second &&
it->second->GetAppType(app_id) == apps::mojom::AppType::kUnknown) {
// If the app doesn't exist in AppRegistryCache, this window is an
// extension window, and we don't need to save the launch info for the
// extension.
return;
}
}
}
}
AddAppLaunchInfo(active_profile_path_, std::move(app_launch_info));
FullRestoreInfo::GetInstance()->OnAppLaunched(window);
}
void FullRestoreSaveHandler::OnWindowDestroyed(aura::Window* window) {
DCHECK(observed_windows_.IsObservingSource(window));
observed_windows_.RemoveObservation(window);
int32_t window_id = window->GetProperty(::full_restore::kWindowIdKey);
if (window->GetProperty(aura::client::kAppType) ==
static_cast<int>(ash::AppType::ARC_APP)) {
if (arc_save_handler_)
arc_save_handler_->OnWindowDestroyed(window);
return;
}
DCHECK(SessionID::IsValidValue(window_id));
RemoveAppRestoreData(window_id);
}
void FullRestoreSaveHandler::SaveAppLaunchInfo(
const base::FilePath& profile_path,
AppLaunchInfoPtr app_launch_info) {
if (profile_path.empty() || !app_launch_info)
return;
const std::string app_id = app_launch_info->app_id;
if (app_launch_info->arc_session_id.has_value()) {
if (arc_save_handler_)
arc_save_handler_->SaveAppLaunchInfo(std::move(app_launch_info));
return;
}
if (!app_launch_info->window_id.has_value()) {
// For Chrome apps, save |app_launch_info| to |app_id_to_app_launch_infos_|,
// and wait for the window to be initialized to get the window id.
app_id_to_app_launch_infos_[app_id][profile_path].push_back(
std::move(app_launch_info));
return;
}
const int window_id = app_launch_info->window_id.value();
std::unique_ptr<WindowInfo> window_info;
if (app_id != extension_misc::kChromeAppId) {
// For browser windows, it could have been saved as
// extension_misc::kChromeAppId in OnWindowInitialized. However, for the
// system web apps, we need to save as the system web app app id and the
// launch parameter, because system web apps can't be restored by the
// browser session restore. So remove the record in
// extension_misc::kChromeAppId, save the launch info as the system web
// app id, and move window info to the record of the system web app id.
auto it = window_id_to_app_restore_info_.find(window_id);
if (it != window_id_to_app_restore_info_.end()) {
window_info =
profile_path_to_restore_data_[it->second.first].GetWindowInfo(
it->second.second, window_id);
RemoveAppRestoreData(window_id);
}
}
AddAppLaunchInfo(profile_path, std::move(app_launch_info));
if (window_info) {
profile_path_to_restore_data_[profile_path].ModifyWindowInfo(
app_id, window_id, *window_info);
}
}
void FullRestoreSaveHandler::SaveWindowInfo(const WindowInfo& window_info) {
if (!window_info.window)
return;
if (window_info.window->GetProperty(aura::client::kAppType) ==
static_cast<int>(ash::AppType::ARC_APP)) {
if (arc_save_handler_)
arc_save_handler_->ModifyWindowInfo(window_info);
return;
}
int32_t window_id =
window_info.window->GetProperty(::full_restore::kWindowIdKey);
if (!SessionID::IsValidValue(window_id))
return;
ModifyWindowInfo(window_id, window_info);
}
void FullRestoreSaveHandler::OnTaskCreated(const std::string& app_id,
int32_t task_id,
int32_t session_id) {
if (arc_save_handler_)
arc_save_handler_->OnTaskCreated(app_id, task_id, session_id);
}
void FullRestoreSaveHandler::OnTaskDestroyed(int32_t task_id) {
if (arc_save_handler_)
arc_save_handler_->OnTaskDestroyed(task_id);
}
void FullRestoreSaveHandler::OnTaskThemeColorUpdated(
int32_t task_id,
uint32_t primary_color,
uint32_t status_bar_color) {
if (arc_save_handler_) {
arc_save_handler_->OnTaskThemeColorUpdated(task_id, primary_color,
status_bar_color);
}
}
void FullRestoreSaveHandler::Flush(const base::FilePath& profile_path) {
if (save_running_.find(profile_path) != save_running_.end())
return;
save_running_.insert(profile_path);
BackendTaskRunner(profile_path)
->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&FullRestoreFileHandler::WriteToFile,
GetFileHandler(profile_path),
profile_path_to_restore_data_[profile_path].Clone()),
base::BindOnce(&FullRestoreSaveHandler::OnSaveFinished,
weak_factory_.GetWeakPtr(), profile_path));
}
bool FullRestoreSaveHandler::HasAppRestoreData(
const base::FilePath& profile_path,
const std::string& app_id,
int32_t window_id) {
auto it = profile_path_to_restore_data_.find(profile_path);
if (it == profile_path_to_restore_data_.end())
return false;
return it->second.HasAppRestoreData(app_id, window_id);
}
void FullRestoreSaveHandler::AddAppLaunchInfo(
const base::FilePath& profile_path,
AppLaunchInfoPtr app_launch_info) {
if (profile_path.empty() || !app_launch_info ||
!app_launch_info->window_id.has_value()) {
return;
}
if (!app_launch_info->arc_session_id.has_value()) {
// If |app_launch_info| is not an ARC app launch info, add to
// |window_id_to_app_restore_info_|.
window_id_to_app_restore_info_[app_launch_info->window_id.value()] =
std::make_pair(profile_path, app_launch_info->app_id);
}
// Each user should have one full restore file saving the restore data in the
// profile directory |profile_path|. So |app_launch_info| is saved to the
// restore data for the user with |profile_path|.
profile_path_to_restore_data_[profile_path].AddAppLaunchInfo(
std::move(app_launch_info));
pending_save_profile_paths_.insert(profile_path);
MaybeStartSaveTimer(profile_path);
}
void FullRestoreSaveHandler::ModifyWindowId(const base::FilePath& profile_path,
const std::string& app_id,
int32_t old_window_id,
int32_t new_window_id) {
auto it = profile_path_to_restore_data_.find(profile_path);
if (it == profile_path_to_restore_data_.end())
return;
profile_path_to_restore_data_[profile_path].ModifyWindowId(
app_id, old_window_id, new_window_id);
pending_save_profile_paths_.insert(profile_path);
MaybeStartSaveTimer(profile_path);
}
void FullRestoreSaveHandler::ModifyWindowInfo(
const base::FilePath& profile_path,
const std::string& app_id,
int32_t window_id,
const WindowInfo& window_info) {
auto it = profile_path_to_restore_data_.find(profile_path);
if (it == profile_path_to_restore_data_.end())
return;
profile_path_to_restore_data_[profile_path].ModifyWindowInfo(
app_id, window_id, window_info);
pending_save_profile_paths_.insert(profile_path);
MaybeStartSaveTimer(profile_path);
}
void FullRestoreSaveHandler::ModifyThemeColor(
const base::FilePath& profile_path,
const std::string& app_id,
int32_t window_id,
uint32_t primary_color,
uint32_t status_bar_color) {
auto it = profile_path_to_restore_data_.find(profile_path);
if (it == profile_path_to_restore_data_.end())
return;
profile_path_to_restore_data_[profile_path].ModifyThemeColor(
app_id, window_id, primary_color, status_bar_color);
pending_save_profile_paths_.insert(profile_path);
MaybeStartSaveTimer(profile_path);
}
void FullRestoreSaveHandler::RemoveApp(const base::FilePath& profile_path,
const std::string& app_id) {
auto it = profile_path_to_restore_data_.find(profile_path);
if (it == profile_path_to_restore_data_.end())
return;
it->second.RemoveApp(app_id);
pending_save_profile_paths_.insert(profile_path);
MaybeStartSaveTimer(profile_path);
}
void FullRestoreSaveHandler::RemoveAppRestoreData(
const base::FilePath& profile_path,
const std::string& app_id,
int window_id) {
auto it = profile_path_to_restore_data_.find(profile_path);
if (it == profile_path_to_restore_data_.end())
return;
it->second.RemoveAppRestoreData(app_id, window_id);
pending_save_profile_paths_.insert(profile_path);
MaybeStartSaveTimer(profile_path);
}
void FullRestoreSaveHandler::RemoveWindowInfo(
const base::FilePath& profile_path,
const std::string& app_id,
int window_id) {
auto it = profile_path_to_restore_data_.find(profile_path);
if (it == profile_path_to_restore_data_.end())
return;
it->second.RemoveWindowInfo(app_id, window_id);
pending_save_profile_paths_.insert(profile_path);
MaybeStartSaveTimer(profile_path);
}
void FullRestoreSaveHandler::ClearRestoreData(
const base::FilePath& profile_path) {
pending_save_profile_paths_.insert(profile_path);
MaybeStartSaveTimer(profile_path);
}
int32_t FullRestoreSaveHandler::GetArcSessionId() {
if (!arc_save_handler_)
return -1;
return arc_save_handler_->GetArcSessionId();
}
const RestoreData* FullRestoreSaveHandler::GetRestoreData(
const base::FilePath& profile_path) {
auto it = profile_path_to_restore_data_.find(profile_path);
if (it == profile_path_to_restore_data_.end())
return nullptr;
return &(profile_path_to_restore_data_[profile_path]);
}
std::string FullRestoreSaveHandler::GetAppId(aura::Window* window) {
DCHECK(window);
if (window->GetProperty(aura::client::kAppType) ==
static_cast<int>(ash::AppType::ARC_APP)) {
return arc_save_handler_ ? arc_save_handler_->GetAppId(window)
: std::string();
} else {
// For other window types (browser, PWAs, SWAs, Chrome apps), get its
// corresponding app id from |window_id_to_app_restore_info_|.
const int32_t window_id = window->GetProperty(kWindowIdKey);
auto iter = window_id_to_app_restore_info_.find(window_id);
return iter != window_id_to_app_restore_info_.end() ? iter->second.second
: std::string();
}
}
void FullRestoreSaveHandler::ClearForTesting() {
profile_path_to_file_handler_.clear();
profile_path_to_restore_data_.clear();
app_id_to_app_launch_infos_.clear();
primary_profile_path_.clear();
save_running_.clear();
pending_save_profile_paths_.clear();
window_id_to_app_restore_info_.clear();
app_id_to_app_launch_infos_.clear();
is_shut_down_ = false;
allow_save_ = false;
save_timer_.Stop();
wait_timer_.Stop();
}
void FullRestoreSaveHandler::MaybeStartSaveTimer(
const base::FilePath& profile_path) {
if (!base::Contains(been_read_profile_paths_, profile_path)) {
// FullRestoreSaveHandler might be called to save the help app before
// FullRestoreAppLaunchHandler reads the full restore data from the full
// restore file during the system startup phase, e.g. when a new user login.
// So call FullRestoreReadHandler to read the file before saving the new
// data.
FullRestoreReadHandler::GetInstance()->ReadFromFile(profile_path,
base::DoNothing());
been_read_profile_paths_.insert(profile_path);
}
if (!allow_save_) {
if (!wait_timer_.IsRunning()) {
wait_timer_.Start(FROM_HERE, kWaitDelay,
base::BindOnce(&FullRestoreSaveHandler::AllowSave,
weak_factory_.GetWeakPtr()));
}
return;
}
if (!save_timer_.IsRunning() && save_running_.empty()) {
save_timer_.Start(FROM_HERE, kSaveDelay,
base::BindOnce(&FullRestoreSaveHandler::Save,
weak_factory_.GetWeakPtr()));
}
}
void FullRestoreSaveHandler::Save() {
if (is_shut_down_ ||
!base::Contains(pending_save_profile_paths_, active_profile_path_)) {
return;
}
Flush(active_profile_path_);
pending_save_profile_paths_.erase(active_profile_path_);
}
void FullRestoreSaveHandler::OnSaveFinished(
const base::FilePath& profile_path) {
save_running_.erase(profile_path);
}
FullRestoreFileHandler* FullRestoreSaveHandler::GetFileHandler(
const base::FilePath& profile_path) {
if (profile_path_to_file_handler_.find(profile_path) ==
profile_path_to_file_handler_.end()) {
profile_path_to_file_handler_[profile_path] =
base::MakeRefCounted<FullRestoreFileHandler>(profile_path);
}
return profile_path_to_file_handler_[profile_path].get();
}
base::SequencedTaskRunner* FullRestoreSaveHandler::BackendTaskRunner(
const base::FilePath& profile_path) {
return GetFileHandler(profile_path)->owning_task_runner();
}
void FullRestoreSaveHandler::ModifyWindowInfo(int window_id,
const WindowInfo& window_info) {
auto it = window_id_to_app_restore_info_.find(window_id);
if (it == window_id_to_app_restore_info_.end())
return;
const base::FilePath& profile_path = it->second.first;
const std::string& app_id = it->second.second;
ModifyWindowInfo(profile_path, app_id, window_id, window_info);
}
void FullRestoreSaveHandler::RemoveAppRestoreData(int window_id) {
auto it = window_id_to_app_restore_info_.find(window_id);
if (it == window_id_to_app_restore_info_.end())
return;
const base::FilePath& profile_path = it->second.first;
const std::string& app_id = it->second.second;
RemoveAppRestoreData(profile_path, app_id, window_id);
window_id_to_app_restore_info_.erase(it);
}
} // namespace full_restore