blob: 2fb03f24cfc723c86f7d20656408c788851430c5 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/web_applications/app_shim_registry_mac.h"
#include <memory>
#include <utility>
#include "base/no_destructor.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
namespace {
const char kAppShims[] = "app_shims";
const char kInstalledProfiles[] = "installed_profiles";
const char kLastActiveProfiles[] = "last_active_profiles";
const char kHandlers[] = "handlers";
const char kFileHandlerExtensions[] = "extensions";
const char kFileHandlerMimeTypes[] = "mime_types";
const char kProtocolHandlers[] = "protocols";
base::Value::List SetToValueList(const std::set<std::string>& values) {
base::Value::List result;
for (const auto& s : values) {
result.Append(s);
}
return result;
}
std::set<std::string> ValueListToSet(const base::Value::List* list) {
std::set<std::string> result;
if (list) {
for (const auto& v : *list) {
if (!v.is_string())
continue;
result.insert(v.GetString());
}
}
return result;
}
} // namespace
AppShimRegistry::HandlerInfo::HandlerInfo() = default;
AppShimRegistry::HandlerInfo::~HandlerInfo() = default;
AppShimRegistry::HandlerInfo::HandlerInfo(HandlerInfo&&) = default;
AppShimRegistry::HandlerInfo::HandlerInfo(const HandlerInfo&) = default;
AppShimRegistry::HandlerInfo& AppShimRegistry::HandlerInfo::operator=(
HandlerInfo&&) = default;
AppShimRegistry::HandlerInfo& AppShimRegistry::HandlerInfo::operator=(
const HandlerInfo&) = default;
// static
AppShimRegistry* AppShimRegistry::Get() {
static base::NoDestructor<AppShimRegistry> instance;
return instance.get();
}
void AppShimRegistry::RegisterLocalPrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(kAppShims);
}
std::set<base::FilePath> AppShimRegistry::GetInstalledProfilesForApp(
const std::string& app_id) const {
std::set<base::FilePath> installed_profiles;
GetProfilesSetForApp(app_id, kInstalledProfiles, &installed_profiles);
return installed_profiles;
}
std::set<base::FilePath> AppShimRegistry::GetLastActiveProfilesForApp(
const std::string& app_id) const {
std::set<base::FilePath> last_active_profiles;
GetProfilesSetForApp(app_id, kLastActiveProfiles, &last_active_profiles);
// Cull out any profiles that are not installed.
std::set<base::FilePath> installed_profiles;
GetProfilesSetForApp(app_id, kInstalledProfiles, &installed_profiles);
for (auto it = last_active_profiles.begin();
it != last_active_profiles.end();) {
if (installed_profiles.count(*it))
it++;
else
last_active_profiles.erase(it++);
}
return last_active_profiles;
}
void AppShimRegistry::GetProfilesSetForApp(
const std::string& app_id,
const std::string& profiles_key,
std::set<base::FilePath>* profiles) const {
const base::Value::Dict& cache = GetPrefService()->GetDict(kAppShims);
const base::Value::Dict* app_info = cache.FindDict(app_id);
if (!app_info)
return;
const base::Value::List* profile_values = app_info->FindList(profiles_key);
if (!profile_values)
return;
for (const auto& profile_path_value : *profile_values) {
if (profile_path_value.is_string())
profiles->insert(GetFullProfilePath(profile_path_value.GetString()));
}
}
void AppShimRegistry::OnAppInstalledForProfile(const std::string& app_id,
const base::FilePath& profile) {
std::set<base::FilePath> installed_profiles =
GetInstalledProfilesForApp(app_id);
if (installed_profiles.count(profile))
return;
installed_profiles.insert(profile);
// Also add the profile to the last active profiles. This way the next time
// the app is launched, it will at least launch in the most recently
// installed profile.
std::set<base::FilePath> last_active_profiles =
GetLastActiveProfilesForApp(app_id);
last_active_profiles.insert(profile);
SetAppInfo(app_id, &installed_profiles, &last_active_profiles,
/*handlers=*/nullptr);
}
bool AppShimRegistry::OnAppUninstalledForProfile(
const std::string& app_id,
const base::FilePath& profile) {
auto installed_profiles = GetInstalledProfilesForApp(app_id);
auto found = installed_profiles.find(profile);
if (found != installed_profiles.end()) {
installed_profiles.erase(profile);
SetAppInfo(app_id, &installed_profiles, /*last_active_profiles=*/nullptr,
/*handlers=*/nullptr);
}
return installed_profiles.empty();
}
void AppShimRegistry::SaveLastActiveProfilesForApp(
const std::string& app_id,
std::set<base::FilePath> last_active_profiles) {
SetAppInfo(app_id, /*installed_profiles=*/nullptr, &last_active_profiles,
/*handlers=*/nullptr);
}
std::set<std::string> AppShimRegistry::GetInstalledAppsForProfile(
const base::FilePath& profile) const {
std::set<std::string> result;
const base::Value::Dict& app_shims = GetPrefService()->GetDict(kAppShims);
for (const auto iter_app : app_shims) {
const base::Value::List* installed_profiles_list =
iter_app.second.GetDict().FindList(kInstalledProfiles);
if (!installed_profiles_list)
continue;
for (const auto& profile_path_value : *installed_profiles_list) {
if (!profile_path_value.is_string())
continue;
if (profile == GetFullProfilePath(profile_path_value.GetString())) {
result.insert(iter_app.first);
break;
}
}
}
return result;
}
void AppShimRegistry::SaveFileHandlersForAppAndProfile(
const std::string& app_id,
const base::FilePath& profile,
std::set<std::string> file_handler_extensions,
std::set<std::string> file_handler_mime_types) {
std::map<base::FilePath, HandlerInfo> handlers = GetHandlersForApp(app_id);
auto it = handlers.emplace(profile, HandlerInfo()).first;
it->second.file_handler_extensions = std::move(file_handler_extensions);
it->second.file_handler_mime_types = std::move(file_handler_mime_types);
if (it->second.IsEmpty())
handlers.erase(it);
SetAppInfo(app_id, /*installed_profiles=*/nullptr,
/*last_active_profiles=*/nullptr, &handlers);
}
void AppShimRegistry::SaveProtocolHandlersForAppAndProfile(
const std::string& app_id,
const base::FilePath& profile,
std::set<std::string> protocol_handlers) {
std::map<base::FilePath, HandlerInfo> handlers = GetHandlersForApp(app_id);
auto it = handlers.emplace(profile, HandlerInfo()).first;
it->second.protocol_handlers = std::move(protocol_handlers);
if (it->second.IsEmpty())
handlers.erase(it);
SetAppInfo(app_id, /*installed_profiles=*/nullptr,
/*last_active_profiles=*/nullptr, &handlers);
}
std::map<base::FilePath, AppShimRegistry::HandlerInfo>
AppShimRegistry::GetHandlersForApp(const std::string& app_id) {
const base::Value::Dict& cache = GetPrefService()->GetDict(kAppShims);
const base::Value::Dict* app_info = cache.FindDict(app_id);
if (!app_info)
return {};
const base::Value::Dict* handlers = app_info->FindDict(kHandlers);
if (!handlers)
return {};
std::map<base::FilePath, HandlerInfo> result;
for (auto profile_handler : *handlers) {
const base::Value::Dict* dict = profile_handler.second.GetIfDict();
if (!dict)
continue;
HandlerInfo info;
info.file_handler_extensions =
ValueListToSet(dict->FindList(kFileHandlerExtensions));
info.file_handler_mime_types =
ValueListToSet(dict->FindList(kFileHandlerMimeTypes));
info.protocol_handlers = ValueListToSet(dict->FindList(kProtocolHandlers));
result.emplace(GetFullProfilePath(profile_handler.first), std::move(info));
}
return result;
}
void AppShimRegistry::SetPrefServiceAndUserDataDirForTesting(
PrefService* pref_service,
const base::FilePath& user_data_dir) {
override_pref_service_ = pref_service;
override_user_data_dir_ = user_data_dir;
}
base::Value::Dict AppShimRegistry::AsDebugDict() const {
const base::Value::Dict& app_shims = GetPrefService()->GetDict(kAppShims);
return app_shims.Clone();
}
PrefService* AppShimRegistry::GetPrefService() const {
if (override_pref_service_)
return override_pref_service_;
return g_browser_process->local_state();
}
base::FilePath AppShimRegistry::GetFullProfilePath(
const std::string& profile_path) const {
base::FilePath relative_profile_path(profile_path);
if (!override_user_data_dir_.empty())
return override_user_data_dir_.Append(relative_profile_path);
ProfileManager* profile_manager = g_browser_process->profile_manager();
return profile_manager->user_data_dir().Append(relative_profile_path);
}
void AppShimRegistry::SetAppInfo(
const std::string& app_id,
const std::set<base::FilePath>* installed_profiles,
const std::set<base::FilePath>* last_active_profiles,
const std::map<base::FilePath, HandlerInfo>* handlers) {
ScopedDictPrefUpdate update(GetPrefService(), kAppShims);
// If there are no installed profiles, clear the app's key.
if (installed_profiles && installed_profiles->empty()) {
update->Remove(app_id);
return;
}
// Look up dictionary for the app.
base::Value::Dict* app_info = update->FindDict(app_id);
if (!app_info) {
// If the key for the app doesn't exist, don't add it unless we are
// specifying a new |installed_profiles| (e.g, for when the app exits
// during uninstall and tells us its last-used profile after we just
// removed the entry for the app).
if (!installed_profiles)
return;
app_info = update->EnsureDict(app_id);
}
if (installed_profiles) {
base::Value::List values;
for (const auto& profile : *installed_profiles)
values.Append(profile.BaseName().value());
app_info->Set(kInstalledProfiles, std::move(values));
}
if (last_active_profiles) {
base::Value::List values;
for (const auto& profile : *last_active_profiles)
values.Append(profile.BaseName().value());
app_info->Set(kLastActiveProfiles, std::move(values));
}
if (handlers) {
base::Value::Dict values;
for (const auto& profile_handlers : *handlers) {
base::Value::Dict value;
value.Set(
kFileHandlerExtensions,
SetToValueList(profile_handlers.second.file_handler_extensions));
value.Set(
kFileHandlerMimeTypes,
SetToValueList(profile_handlers.second.file_handler_mime_types));
value.Set(kProtocolHandlers,
SetToValueList(profile_handlers.second.protocol_handlers));
values.Set(profile_handlers.first.BaseName().value(), std::move(value));
}
app_info->Set(kHandlers, std::move(values));
}
}