blob: 8ca7de28cdb82f7f4de686337dc4a50547ce42ce [file] [log] [blame]
// Copyright 2015 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/permissions/object_permission_context_base.h"
#include <utility>
#include "base/bind.h"
#include "base/containers/contains.h"
#include "base/observer_list.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "url/origin.h"
namespace permissions {
const char kObjectListKey[] = "chosen-objects";
ObjectPermissionContextBase::ObjectPermissionContextBase(
ContentSettingsType guard_content_settings_type,
ContentSettingsType data_content_settings_type,
HostContentSettingsMap* host_content_settings_map)
: guard_content_settings_type_(guard_content_settings_type),
data_content_settings_type_(data_content_settings_type),
host_content_settings_map_(host_content_settings_map) {
DCHECK(host_content_settings_map_);
}
ObjectPermissionContextBase::ObjectPermissionContextBase(
ContentSettingsType data_content_settings_type,
HostContentSettingsMap* host_content_settings_map)
: guard_content_settings_type_(absl::nullopt),
data_content_settings_type_(data_content_settings_type),
host_content_settings_map_(host_content_settings_map) {
DCHECK(host_content_settings_map_);
}
ObjectPermissionContextBase::~ObjectPermissionContextBase() {
FlushScheduledSaveSettingsCalls();
}
ObjectPermissionContextBase::Object::Object(
const url::Origin& origin,
base::Value value,
content_settings::SettingSource source,
bool incognito)
: origin(origin.GetURL()),
value(std::move(value)),
source(source),
incognito(incognito) {}
ObjectPermissionContextBase::Object::~Object() = default;
std::unique_ptr<ObjectPermissionContextBase::Object>
ObjectPermissionContextBase::Object::Clone() {
return std::make_unique<Object>(url::Origin::Create(origin), value.Clone(),
source, incognito);
}
void ObjectPermissionContextBase::PermissionObserver::OnObjectPermissionChanged(
absl::optional<ContentSettingsType> guard_content_settings_type,
ContentSettingsType data_content_settings_type) {}
void ObjectPermissionContextBase::PermissionObserver::OnPermissionRevoked(
const url::Origin& origin) {}
void ObjectPermissionContextBase::AddObserver(PermissionObserver* observer) {
permission_observer_list_.AddObserver(observer);
}
void ObjectPermissionContextBase::RemoveObserver(PermissionObserver* observer) {
permission_observer_list_.RemoveObserver(observer);
}
bool ObjectPermissionContextBase::CanRequestObjectPermission(
const url::Origin& origin) {
if (!guard_content_settings_type_)
return true;
ContentSetting content_setting =
host_content_settings_map_->GetContentSetting(
origin.GetURL(), GURL(), *guard_content_settings_type_);
DCHECK(content_setting == CONTENT_SETTING_ASK ||
content_setting == CONTENT_SETTING_BLOCK);
return content_setting == CONTENT_SETTING_ASK;
}
std::unique_ptr<ObjectPermissionContextBase::Object>
ObjectPermissionContextBase::GetGrantedObject(const url::Origin& origin,
const base::StringPiece key) {
if (!CanRequestObjectPermission(origin))
return nullptr;
const auto& origin_objects_it = objects().find(origin);
if (origin_objects_it == objects().end())
return nullptr;
const auto& object_it = origin_objects_it->second.find(std::string(key));
if (object_it == origin_objects_it->second.end())
return nullptr;
return object_it->second->Clone();
}
std::vector<std::unique_ptr<ObjectPermissionContextBase::Object>>
ObjectPermissionContextBase::GetGrantedObjects(const url::Origin& origin) {
if (!CanRequestObjectPermission(origin))
return {};
const auto& origin_objects_it = objects().find(origin);
if (origin_objects_it == objects().end())
return {};
std::vector<std::unique_ptr<Object>> results;
for (const auto& object : origin_objects_it->second)
results.push_back(object.second->Clone());
return results;
}
std::vector<std::unique_ptr<ObjectPermissionContextBase::Object>>
ObjectPermissionContextBase::GetAllGrantedObjects() {
std::vector<std::unique_ptr<Object>> results;
for (const auto& objects_entry : objects()) {
if (!CanRequestObjectPermission(objects_entry.first))
continue;
for (const auto& object : objects_entry.second)
results.push_back(object.second->Clone());
}
return results;
}
void ObjectPermissionContextBase::GrantObjectPermission(
const url::Origin& origin,
base::Value object) {
DCHECK(IsValidObject(object));
const std::string key = GetKeyForObject(object);
objects()[origin][key] = std::make_unique<Object>(
origin, std::move(object),
content_settings::SettingSource::SETTING_SOURCE_USER,
host_content_settings_map_->IsOffTheRecord());
ScheduleSaveWebsiteSetting(origin);
NotifyPermissionChanged();
}
void ObjectPermissionContextBase::UpdateObjectPermission(
const url::Origin& origin,
const base::Value& old_object,
base::Value new_object) {
auto origin_objects_it = objects().find(origin);
if (origin_objects_it == objects().end())
return;
std::string key = GetKeyForObject(old_object);
auto object_it = origin_objects_it->second.find(key);
if (object_it == origin_objects_it->second.end())
return;
origin_objects_it->second.erase(object_it);
key = GetKeyForObject(new_object);
DCHECK(!base::Contains(origin_objects_it->second, key));
GrantObjectPermission(origin, std::move(new_object));
}
void ObjectPermissionContextBase::RevokeObjectPermission(
const url::Origin& origin,
const base::Value& object) {
DCHECK(IsValidObject(object));
RevokeObjectPermission(origin, GetKeyForObject(object));
}
void ObjectPermissionContextBase::RevokeObjectPermission(
const url::Origin& origin,
const base::StringPiece key) {
auto origin_objects_it = objects().find(origin);
if (origin_objects_it == objects().end())
return;
auto object_it = origin_objects_it->second.find(std::string(key));
if (object_it == origin_objects_it->second.end())
return;
origin_objects_it->second.erase(object_it);
if (!origin_objects_it->second.size())
objects().erase(origin_objects_it);
ScheduleSaveWebsiteSetting(origin);
NotifyPermissionRevoked(origin);
}
bool ObjectPermissionContextBase::HasGrantedObjects(const url::Origin& origin) {
auto origin_objects_it = objects().find(origin);
return origin_objects_it != objects().end() &&
!origin_objects_it->second.empty();
}
void ObjectPermissionContextBase::FlushScheduledSaveSettingsCalls() {
// Persist any pending object updates that did not have the chance to be
// persisted yet.
while (!origins_with_scheduled_save_settings_calls_.empty()) {
const url::Origin origin =
*(origins_with_scheduled_save_settings_calls_.begin());
SaveWebsiteSetting(origin);
}
}
bool ObjectPermissionContextBase::IsOffTheRecord() {
return host_content_settings_map_->IsOffTheRecord();
}
void ObjectPermissionContextBase::NotifyPermissionChanged() {
for (auto& observer : permission_observer_list_) {
observer.OnObjectPermissionChanged(guard_content_settings_type_,
data_content_settings_type_);
}
}
void ObjectPermissionContextBase::NotifyPermissionRevoked(
const url::Origin& origin) {
for (auto& observer : permission_observer_list_) {
observer.OnObjectPermissionChanged(guard_content_settings_type_,
data_content_settings_type_);
observer.OnPermissionRevoked(origin);
}
}
base::Value ObjectPermissionContextBase::GetWebsiteSetting(
const url::Origin& origin,
content_settings::SettingInfo* info) {
base::Value value = host_content_settings_map_->GetWebsiteSetting(
origin.GetURL(), GURL(), data_content_settings_type_, info);
if (value.is_none())
return base::Value(base::Value::Type::DICTIONARY);
return value;
}
void ObjectPermissionContextBase::SaveWebsiteSetting(
const url::Origin& origin) {
auto scheduled_save_it =
origins_with_scheduled_save_settings_calls_.find(origin);
if (scheduled_save_it == origins_with_scheduled_save_settings_calls_.end()) {
// Another scheduled `SaveWebsiteSetting` call has handled this origin
// already.
return;
}
origins_with_scheduled_save_settings_calls_.erase(scheduled_save_it);
auto origin_objects_it = objects().find(origin);
if (origin_objects_it == objects().end()) {
host_content_settings_map_->SetWebsiteSettingDefaultScope(
origin.GetURL(), GURL(), data_content_settings_type_, base::Value());
return;
}
base::Value objects_list(base::Value::Type::LIST);
for (const auto& object : origin_objects_it->second) {
objects_list.Append(object.second->value.Clone());
}
base::Value website_setting_value(base::Value::Type::DICTIONARY);
website_setting_value.SetKey(kObjectListKey, std::move(objects_list));
host_content_settings_map_->SetWebsiteSettingDefaultScope(
origin.GetURL(), GURL(), data_content_settings_type_,
std::move(website_setting_value));
}
void ObjectPermissionContextBase::ScheduleSaveWebsiteSetting(
const url::Origin& origin) {
bool success =
origins_with_scheduled_save_settings_calls_.insert(origin).second;
if (!success) {
// There is already a scheduled `SaveWebsiteSetting` call, no need to
// schedule another.
return;
}
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&ObjectPermissionContextBase::SaveWebsiteSetting,
weak_factory_.GetWeakPtr(), origin));
}
std::vector<std::unique_ptr<ObjectPermissionContextBase::Object>>
ObjectPermissionContextBase::GetWebsiteSettingObjects() {
ContentSettingsForOneType content_settings;
host_content_settings_map_->GetSettingsForOneType(data_content_settings_type_,
&content_settings);
std::vector<std::unique_ptr<Object>> results;
for (const ContentSettingPatternSource& content_setting : content_settings) {
// Old settings used the (requesting,embedding) pair whereas the new
// settings simply use (embedding, *). The migration logic in
// HostContentSettingsMap::MigrateSettingsPrecedingPermissionDelegationActivation
// ensures that there is no way for leftover old settings to make us pick
// the wrong pattern here.
GURL origin_url(content_setting.primary_pattern.ToString());
if (!origin_url.is_valid())
continue;
const auto origin = url::Origin::Create(origin_url);
if (!CanRequestObjectPermission(origin))
continue;
content_settings::SettingInfo info;
base::Value setting = GetWebsiteSetting(origin, &info);
base::Value* objects = setting.FindListKey(kObjectListKey);
if (!objects)
continue;
for (auto& object : objects->GetListDeprecated()) {
if (!IsValidObject(object)) {
continue;
}
results.push_back(std::make_unique<Object>(
origin, std::move(object), info.source, content_setting.incognito));
}
}
return results;
}
void ObjectPermissionContextBase::LoadWebsiteSettingsIntoObjects() {
auto loaded_objects = GetWebsiteSettingObjects();
for (auto& object : loaded_objects) {
objects_[url::Origin::Create(object->origin)].emplace(
GetKeyForObject(object->value), std::move(object));
}
}
ObjectPermissionContextBase::ObjectMap& ObjectPermissionContextBase::objects() {
if (!objects_initialized_) {
LoadWebsiteSettingsIntoObjects();
objects_initialized_ = true;
}
return objects_;
}
} // namespace permissions