blob: 1cb935afea71ae7f52928d2c1a098c510afae73c [file] [log] [blame]
// Copyright 2018 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/web_applications/extensions/web_app_extension_ids_map.h"
#include <string>
#include <vector>
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/extension_registry.h"
#include "url/gurl.h"
namespace web_app {
namespace {
// The stored preferences look like:
//
// "web_apps": {
// "extension_ids": {
// "https://events.google.com/io2016/?utm_source=web_app_manifest": {
// "extension_id": "mjgafbdfajpigcjmkgmeokfbodbcfijl",
// "install_source": 1
// },
// "https://www.chromestatus.com/features": {
// "extension_id": "fedbieoalmbobgfjapopkghdmhgncnaa",
// "install_source": 1
// }
// }
// }
//
// From the top, prefs::kWebAppsExtensionIDs is "web_apps.extension_ids".
//
// Two levels in is a dictionary (key/value pairs) whose keys are URLs and
// values are leaf dictionaries. Those leaf dictionaries have keys such as
// kExtensionId and kInstallSource.
constexpr char kExtensionId[] = "extension_id";
constexpr char kInstallSource[] = "install_source";
// Returns the base::Value in |pref_service| corresponding to our stored dict
// for |extension_id|, or nullptr if it doesn't exist.
const base::Value* GetPreferenceValue(const PrefService* pref_service,
const std::string& extension_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
const base::DictionaryValue* urls_to_dicts =
pref_service->GetDictionary(prefs::kWebAppsExtensionIDs);
if (!urls_to_dicts) {
return nullptr;
}
// Do a simple O(N) scan for extension_id being a value in each dictionary's
// key/value pairs. We expect both N and the number of times
// GetPreferenceValue is called to be relatively small in practice. If they
// turn out to be large, we can write a more sophisticated implementation.
for (const auto& it : urls_to_dicts->DictItems()) {
const base::Value* root = &it.second;
const base::Value* v = root;
if (v->is_dict()) {
v = v->FindKey(kExtensionId);
if (v && v->is_string() && (v->GetString() == extension_id)) {
return root;
}
}
}
return nullptr;
}
} // namespace
// static
void ExtensionIdsMap::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterDictionaryPref(prefs::kWebAppsExtensionIDs);
}
// static
bool ExtensionIdsMap::HasExtensionId(const PrefService* pref_service,
const std::string& extension_id) {
return GetPreferenceValue(pref_service, extension_id) != nullptr;
}
// static
bool ExtensionIdsMap::HasExtensionIdWithInstallSource(
const PrefService* pref_service,
const std::string& extension_id,
InstallSource install_source) {
const base::Value* v = GetPreferenceValue(pref_service, extension_id);
if (v == nullptr || !v->is_dict())
return false;
v = v->FindKeyOfType(kInstallSource, base::Value::Type::INTEGER);
return (v && v->GetInt() == static_cast<int>(install_source));
}
// static
std::vector<GURL> ExtensionIdsMap::GetInstalledAppUrls(
Profile* profile,
InstallSource install_source) {
const base::DictionaryValue* urls_to_dicts =
profile->GetPrefs()->GetDictionary(prefs::kWebAppsExtensionIDs);
std::vector<GURL> installed_app_urls;
if (!urls_to_dicts) {
return installed_app_urls;
}
for (const auto& it : urls_to_dicts->DictItems()) {
const base::Value* v = &it.second;
if (!v->is_dict()) {
continue;
}
const base::Value* install_source_value =
v->FindKeyOfType(kInstallSource, base::Value::Type::INTEGER);
if (!install_source_value ||
(install_source_value->GetInt() != static_cast<int>(install_source))) {
continue;
}
v = v->FindKey(kExtensionId);
if (!v || !v->is_string()) {
continue;
}
auto* extension =
extensions::ExtensionRegistry::Get(profile)->GetExtensionById(
v->GetString(), extensions::ExtensionRegistry::EVERYTHING);
if (!extension) {
continue;
}
installed_app_urls.emplace_back(it.first);
}
return installed_app_urls;
}
ExtensionIdsMap::ExtensionIdsMap(PrefService* pref_service)
: pref_service_(pref_service) {}
void ExtensionIdsMap::Insert(const GURL& url,
const std::string& extension_id,
InstallSource install_source) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetKey(kExtensionId, base::Value(extension_id));
dict.SetKey(kInstallSource, base::Value(static_cast<int>(install_source)));
DictionaryPrefUpdate update(pref_service_, prefs::kWebAppsExtensionIDs);
update->SetKey(url.spec(), std::move(dict));
}
base::Optional<std::string> ExtensionIdsMap::LookupExtensionId(
const GURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
const base::Value* v =
pref_service_->GetDictionary(prefs::kWebAppsExtensionIDs)
->FindKey(url.spec());
if (v && v->is_dict()) {
v = v->FindKey(kExtensionId);
if (v && v->is_string()) {
return base::make_optional(v->GetString());
}
}
return base::nullopt;
}
} // namespace web_app