| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/app_restore/restore_data.h" |
| |
| #include <utility> |
| |
| #include "base/i18n/number_formatting.h" |
| #include "base/logging.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/values.h" |
| #include "components/app_constants/constants.h" |
| #include "components/app_restore/app_launch_info.h" |
| #include "components/app_restore/window_info.h" |
| |
| namespace app_restore { |
| |
| namespace { |
| |
| // Used to generate unique restore window IDs for desk template launches. These |
| // IDs will all be negative in order to avoid clashes with full restore (which |
| // are all positive). The first generated ID will be one lower than the starting |
| // point and then proceed down. The starting point is a special case value that |
| // a valid RWID should not use. |
| int32_t g_desk_template_window_restore_id = -1; |
| |
| // Key used to record `removing_desk_guid_` when `RestoreData` is converted to |
| // JSON. |
| constexpr char kRemovingDeskGuidKey[] = "removing_desk_guid"; |
| |
| } // namespace |
| |
| RestoreData::RestoreData() = default; |
| |
| RestoreData::RestoreData(base::Value restore_data_value) { |
| base::Value::Dict* dict = restore_data_value.GetIfDict(); |
| if (!dict) { |
| DVLOG(0) << "Fail to parse full restore data. " |
| << "Cannot find the full restore data dict."; |
| return; |
| } |
| |
| if (auto* removing_desk_guid_string = |
| dict->FindString(kRemovingDeskGuidKey)) { |
| removing_desk_guid_ = |
| base::Uuid::ParseLowercase(*removing_desk_guid_string); |
| } |
| |
| for (auto iter : *dict) { |
| // `key` can be an app ID or `kRemovingDeskGuidKey`. |
| const std::string& key = iter.first; |
| base::Value::Dict* value = iter.second.GetIfDict(); |
| |
| // Skip the removing desk GUID because we already covered this before the |
| // loop. |
| if (key == kRemovingDeskGuidKey) { |
| continue; |
| } |
| |
| if (!value) { |
| DVLOG(0) << "Fail to parse full restore data. " |
| << "Cannot find the app restore data dict."; |
| continue; |
| } |
| |
| for (auto data_iter : *value) { |
| const std::string& window_id_string = data_iter.first; |
| |
| int window_id = 0; |
| if (!base::StringToInt(window_id_string, &window_id)) { |
| DVLOG(0) << "Fail to parse full restore data. " |
| << "Cannot find the valid id."; |
| continue; |
| } |
| |
| base::Value::Dict* app_restore_data_dict = data_iter.second.GetIfDict(); |
| if (!app_restore_data_dict) { |
| DVLOG(0) << "Fail to parse app restore data. " |
| << "Cannot find the app restore data dict."; |
| continue; |
| } |
| |
| // If the data is for an app that was on a removing desk, then we can skip |
| // adding the data. |
| auto app_restore_data = |
| std::make_unique<AppRestoreData>(std::move(*app_restore_data_dict)); |
| if (removing_desk_guid_.is_valid() && |
| app_restore_data->desk_guid == removing_desk_guid_) { |
| continue; |
| } |
| |
| app_id_to_launch_list_[key][window_id] = std::move(app_restore_data); |
| } |
| } |
| } |
| |
| RestoreData::~RestoreData() = default; |
| |
| std::unique_ptr<RestoreData> RestoreData::Clone() const { |
| std::unique_ptr<RestoreData> restore_data = std::make_unique<RestoreData>(); |
| for (const auto& it : app_id_to_launch_list_) { |
| for (const auto& data_it : it.second) { |
| restore_data->app_id_to_launch_list_[it.first][data_it.first] = |
| data_it.second->Clone(); |
| } |
| } |
| if (removing_desk_guid_.is_valid()) { |
| restore_data->removing_desk_guid_ = removing_desk_guid_; |
| } |
| return restore_data; |
| } |
| |
| base::Value RestoreData::ConvertToValue() const { |
| base::Value::Dict restore_data_dict; |
| for (const auto& [app_id, launch_list] : app_id_to_launch_list_) { |
| if (launch_list.empty()) { |
| continue; |
| } |
| |
| base::Value::Dict info_dict; |
| for (const auto& [window_id, app_restore_data] : launch_list) { |
| info_dict.Set(base::NumberToString(window_id), |
| app_restore_data->ConvertToValue()); |
| } |
| |
| restore_data_dict.Set(app_id, std::move(info_dict)); |
| } |
| |
| if (removing_desk_guid_.is_valid()) { |
| restore_data_dict.Set(kRemovingDeskGuidKey, |
| removing_desk_guid_.AsLowercaseString()); |
| } |
| |
| return base::Value(std::move(restore_data_dict)); |
| } |
| |
| bool RestoreData::HasAppTypeBrowser() const { |
| auto it = app_id_to_launch_list_.find(app_constants::kChromeAppId); |
| if (it == app_id_to_launch_list_.end()) |
| return false; |
| |
| for (const auto& data : it->second) { |
| if (data.second->app_type_browser.has_value() && |
| data.second->app_type_browser.value()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool RestoreData::HasBrowser() const { |
| auto it = app_id_to_launch_list_.find(app_constants::kChromeAppId); |
| if (it == app_id_to_launch_list_.end()) |
| return false; |
| |
| for (const auto& data : it->second) { |
| if (!data.second->app_type_browser.has_value() || |
| !data.second->app_type_browser.value()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool RestoreData::HasAppRestoreData(const std::string& app_id, |
| int32_t window_id) { |
| return GetAppRestoreData(app_id, window_id) != nullptr; |
| } |
| |
| void RestoreData::AddAppLaunchInfo( |
| std::unique_ptr<AppLaunchInfo> app_launch_info) { |
| if (!app_launch_info || !app_launch_info->window_id.has_value()) |
| return; |
| |
| const std::string app_id = app_launch_info->app_id; |
| const int32_t window_id = app_launch_info->window_id.value(); |
| app_id_to_launch_list_[app_id][window_id] = |
| std::make_unique<AppRestoreData>(std::move(app_launch_info)); |
| } |
| |
| void RestoreData::ModifyWindowId(const std::string& app_id, |
| int32_t old_window_id, |
| int32_t new_window_id) { |
| auto it = app_id_to_launch_list_.find(app_id); |
| if (it == app_id_to_launch_list_.end()) |
| return; |
| |
| auto data_it = it->second.find(old_window_id); |
| if (data_it == it->second.end()) |
| return; |
| |
| it->second[new_window_id] = std::move(data_it->second); |
| it->second.erase(data_it); |
| } |
| |
| void RestoreData::ModifyWindowInfo(const std::string& app_id, |
| int32_t window_id, |
| const WindowInfo& window_info) { |
| auto* app_restore_data = GetAppRestoreDataMutable(app_id, window_id); |
| if (app_restore_data) |
| app_restore_data->ModifyWindowInfo(window_info); |
| } |
| |
| void RestoreData::ModifyThemeColor(const std::string& app_id, |
| int32_t window_id, |
| uint32_t primary_color, |
| uint32_t status_bar_color) { |
| auto* app_restore_data = GetAppRestoreDataMutable(app_id, window_id); |
| if (app_restore_data) |
| app_restore_data->ModifyThemeColor(primary_color, status_bar_color); |
| } |
| |
| void RestoreData::SetNextRestoreWindowIdForChromeApp( |
| const std::string& app_id) { |
| auto it = app_id_to_launch_list_.find(app_id); |
| if (it == app_id_to_launch_list_.end()) |
| return; |
| |
| chrome_app_id_to_current_window_id_[app_id] = it->second.begin()->first; |
| |
| if (it->second.size() == 1) |
| return; |
| |
| // When a chrome app has multiple windows, all windows will be sent to the |
| // background. |
| for (auto& data_it : it->second) |
| data_it.second->activation_index = INT32_MAX; |
| } |
| |
| void RestoreData::RemoveAppRestoreData(const std::string& app_id, |
| int window_id) { |
| if (app_id_to_launch_list_.find(app_id) == app_id_to_launch_list_.end()) |
| return; |
| |
| app_id_to_launch_list_[app_id].erase(window_id); |
| if (app_id_to_launch_list_[app_id].empty()) |
| app_id_to_launch_list_.erase(app_id); |
| } |
| |
| void RestoreData::SendWindowToBackground(const std::string& app_id, |
| int window_id) { |
| auto* app_restore_data = GetAppRestoreDataMutable(app_id, window_id); |
| if (app_restore_data) |
| app_restore_data->activation_index = INT32_MAX; |
| } |
| |
| void RestoreData::RemoveApp(const std::string& app_id) { |
| app_id_to_launch_list_.erase(app_id); |
| chrome_app_id_to_current_window_id_.erase(app_id); |
| } |
| |
| std::unique_ptr<AppLaunchInfo> RestoreData::GetAppLaunchInfo( |
| const std::string& app_id, |
| int window_id) { |
| auto* app_restore_data = GetAppRestoreData(app_id, window_id); |
| return app_restore_data |
| ? app_restore_data->GetAppLaunchInfo(app_id, window_id) |
| : nullptr; |
| } |
| |
| std::unique_ptr<WindowInfo> RestoreData::GetWindowInfo( |
| const std::string& app_id, |
| int window_id) { |
| auto* app_restore_data = GetAppRestoreData(app_id, window_id); |
| return app_restore_data ? app_restore_data->GetWindowInfo() : nullptr; |
| } |
| |
| int32_t RestoreData::FetchRestoreWindowId(const std::string& app_id) { |
| auto it = app_id_to_launch_list_.find(app_id); |
| if (it == app_id_to_launch_list_.end()) |
| return 0; |
| |
| if (chrome_app_id_to_current_window_id_.find(app_id) == |
| chrome_app_id_to_current_window_id_.end()) { |
| return 0; |
| } |
| |
| int window_id = chrome_app_id_to_current_window_id_[app_id]; |
| |
| // Move to the next window_id. |
| auto data_it = it->second.find(window_id); |
| DCHECK(data_it != it->second.end()); |
| ++data_it; |
| if (data_it == it->second.end()) |
| chrome_app_id_to_current_window_id_.erase(app_id); |
| else |
| chrome_app_id_to_current_window_id_[app_id] = data_it->first; |
| |
| return window_id; |
| } |
| |
| const AppRestoreData* RestoreData::GetAppRestoreData(const std::string& app_id, |
| int window_id) const { |
| auto it = app_id_to_launch_list_.find(app_id); |
| if (it == app_id_to_launch_list_.end()) |
| return nullptr; |
| |
| auto data_it = it->second.find(window_id); |
| if (data_it == it->second.end()) |
| return nullptr; |
| |
| return data_it->second.get(); |
| } |
| |
| void RestoreData::SetDeskUuid(const base::Uuid& desk_uuid) { |
| for (auto& [app_id, launch_list] : app_id_to_launch_list_) { |
| for (auto& [window_id, app_restore_data] : launch_list) { |
| app_restore_data->desk_guid = desk_uuid; |
| } |
| } |
| } |
| |
| base::flat_map<int32_t, int32_t> |
| RestoreData::MakeWindowIdsUniqueForDeskTemplate() { |
| if (has_unique_window_ids_for_desk_template_) { |
| return {}; |
| } |
| |
| base::flat_map<int32_t, int32_t> mapping; |
| for (auto& [app_id, launch_list] : app_id_to_launch_list_) { |
| // We don't want to do in-place updates of the launch list since it |
| // complicates traversal. We'll therefore build a new LaunchList and pilfer |
| // the old one for AppRestoreData. |
| LaunchList new_launch_list; |
| for (auto& [window_id, app_restore_data] : launch_list) { |
| int32_t new_rwid = --g_desk_template_window_restore_id; |
| new_launch_list[new_rwid] = std::move(app_restore_data); |
| mapping[new_rwid] = window_id; |
| } |
| launch_list = std::move(new_launch_list); |
| } |
| |
| has_unique_window_ids_for_desk_template_ = true; |
| return mapping; |
| } |
| |
| void RestoreData::UpdateBrowserAppIdToLacros() { |
| auto app_launch_list_iter = |
| app_id_to_launch_list_.find(app_constants::kChromeAppId); |
| if (app_launch_list_iter == app_id_to_launch_list_.end()) { |
| return; |
| } |
| app_id_to_launch_list_[app_constants::kLacrosAppId] = |
| std::move(app_launch_list_iter->second); |
| RemoveApp(app_constants::kChromeAppId); |
| } |
| |
| std::string RestoreData::ToString() const { |
| if (app_id_to_launch_list_.empty() && !removing_desk_guid_.is_valid()) { |
| return "empty"; |
| } |
| |
| std::string result = "( "; |
| for (const auto& entry : app_id_to_launch_list_) { |
| result += base::StringPrintf( |
| "(App ID: %s, Count: %s)", entry.first.c_str(), |
| base::UTF16ToUTF8(base::FormatNumber(entry.second.size())).c_str()); |
| for (const auto& windows : entry.second) { |
| result += |
| base::StringPrintf( |
| "(Window ID: %s)", |
| base::UTF16ToUTF8(base::FormatNumber(windows.first)).c_str()) + |
| windows.second->GetWindowInfo()->ToString(); |
| } |
| } |
| |
| result += " )"; |
| |
| if (removing_desk_guid_.is_valid()) { |
| result += |
| base::StringPrintf(" (Removing Desk GUID: %s)", |
| removing_desk_guid_.AsLowercaseString().c_str()); |
| } |
| |
| return result; |
| } |
| |
| AppRestoreData* RestoreData::GetAppRestoreDataMutable(const std::string& app_id, |
| int window_id) { |
| return const_cast<AppRestoreData*>(GetAppRestoreData(app_id, window_id)); |
| } |
| |
| } // namespace app_restore |