blob: 0df5c1456888bacd6d7e5446fd8128f98307d24f [file] [log] [blame]
// 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 "chrome/browser/content_settings/generated_cookie_prefs.h"
#include "base/notreached.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/settings_private.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/prefs/pref_service.h"
namespace settings_api = extensions::api::settings_private;
namespace settings_private = extensions::settings_private;
namespace content_settings {
namespace {
bool IsDefaultCookieContentSettingUserControlled(HostContentSettingsMap* map) {
content_settings::ProviderType content_setting_provider;
map->GetDefaultContentSetting(ContentSettingsType::COOKIES,
&content_setting_provider);
auto content_setting_source =
content_settings::GetSettingSourceFromProviderType(
content_setting_provider);
return content_setting_source == SettingSource::kUser;
}
// Updates all user modifiable cookie content settings and preferences to match
// the provided |controls_mode| and |content_setting|. This provides a
// consistent interface to updating these when they are partially managed.
// Returns SetPrefResult::SUCCESS if any settings could be changed, and
// SetPrefResult::PREF_NOT_MODIFIABLE if no setting could be changed.
extensions::settings_private::SetPrefResult SetAllCookieSettings(
Profile* profile,
CookieControlsMode controls_mode,
ContentSetting content_setting) {
bool setting_changed = false;
auto* map = HostContentSettingsMapFactory::GetForProfile(profile);
if (IsDefaultCookieContentSettingUserControlled(map)) {
map->SetDefaultContentSetting(ContentSettingsType::COOKIES,
content_setting);
setting_changed = true;
}
auto* pref_service = profile->GetPrefs();
if (pref_service->FindPreference(prefs::kCookieControlsMode)
->IsUserModifiable()) {
pref_service->SetInteger(prefs::kCookieControlsMode,
static_cast<int>(controls_mode));
setting_changed = true;
}
return setting_changed
? extensions::settings_private::SetPrefResult::SUCCESS
: extensions::settings_private::SetPrefResult::PREF_NOT_MODIFIABLE;
}
CookiePrimarySetting ToCookiePrimarySetting(
CookieControlsMode cookie_controls_mode) {
switch (cookie_controls_mode) {
case CookieControlsMode::kBlockThirdParty:
return CookiePrimarySetting::BLOCK_THIRD_PARTY;
case CookieControlsMode::kIncognitoOnly:
return CookiePrimarySetting::BLOCK_THIRD_PARTY_INCOGNITO;
case CookieControlsMode::kOff:
return CookiePrimarySetting::ALLOW_ALL;
}
NOTREACHED_IN_MIGRATION();
}
} // namespace
const char kCookiePrimarySetting[] = "generated.cookie_primary_setting";
const char kCookieSessionOnly[] = "generated.cookie_session_only";
const char kCookieDefaultContentSetting[] =
"generated.cookie_default_content_setting";
GeneratedCookiePrefBase::GeneratedCookiePrefBase(Profile* profile,
const std::string& pref_name)
: profile_(profile), pref_name_(pref_name) {
host_content_settings_map_ =
HostContentSettingsMapFactory::GetForProfile(profile_);
content_settings_observation_.Observe(host_content_settings_map_.get());
user_prefs_registrar_.Init(profile->GetPrefs());
user_prefs_registrar_.Add(
prefs::kCookieControlsMode,
base::BindRepeating(&GeneratedCookiePrefBase::OnCookiePreferencesChanged,
base::Unretained(this)));
}
GeneratedCookiePrefBase::~GeneratedCookiePrefBase() = default;
void GeneratedCookiePrefBase::OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsTypeSet content_type_set) {
if (content_type_set.Contains(ContentSettingsType::COOKIES)) {
NotifyObservers(pref_name_);
}
}
void GeneratedCookiePrefBase::OnCookiePreferencesChanged() {
NotifyObservers(pref_name_);
}
GeneratedCookiePrimarySettingPref::GeneratedCookiePrimarySettingPref(
Profile* profile)
: GeneratedCookiePrefBase(profile, kCookiePrimarySetting) {}
extensions::settings_private::SetPrefResult
GeneratedCookiePrimarySettingPref::SetPref(const base::Value* value) {
if (!value->is_int())
return extensions::settings_private::SetPrefResult::PREF_TYPE_MISMATCH;
auto current_content_setting =
host_content_settings_map_->GetDefaultContentSetting(
ContentSettingsType::COOKIES, nullptr);
auto allow_setting =
current_content_setting != ContentSetting::CONTENT_SETTING_BLOCK
? current_content_setting
: ContentSetting::CONTENT_SETTING_ALLOW;
auto selection = static_cast<CookiePrimarySetting>(value->GetInt());
switch (selection) {
case (CookiePrimarySetting::ALLOW_ALL):
return SetAllCookieSettings(profile_, CookieControlsMode::kOff,
allow_setting);
case (CookiePrimarySetting::BLOCK_THIRD_PARTY_INCOGNITO):
return SetAllCookieSettings(profile_, CookieControlsMode::kIncognitoOnly,
allow_setting);
case (CookiePrimarySetting::BLOCK_THIRD_PARTY):
return SetAllCookieSettings(
profile_, CookieControlsMode::kBlockThirdParty, allow_setting);
case (CookiePrimarySetting::BLOCK_ALL):
return SetAllCookieSettings(profile_,
CookieControlsMode::kBlockThirdParty,
ContentSetting::CONTENT_SETTING_BLOCK);
default:
return extensions::settings_private::SetPrefResult::PREF_TYPE_MISMATCH;
}
}
extensions::api::settings_private::PrefObject
GeneratedCookiePrimarySettingPref::GetPrefObject() const {
extensions::api::settings_private::PrefObject pref_object;
pref_object.key = pref_name_;
pref_object.type = extensions::api::settings_private::PrefType::kNumber;
auto content_setting = host_content_settings_map_->GetDefaultContentSetting(
ContentSettingsType::COOKIES, nullptr);
auto cookie_controls_mode = static_cast<CookieControlsMode>(
profile_->GetPrefs()->GetInteger(prefs::kCookieControlsMode));
if (content_setting == ContentSetting::CONTENT_SETTING_BLOCK) {
pref_object.value =
base::Value(static_cast<int>(CookiePrimarySetting::BLOCK_ALL));
} else {
pref_object.value = base::Value(
static_cast<int>(ToCookiePrimarySetting(cookie_controls_mode)));
}
ApplyPrimaryCookieSettingManagedState(pref_object, profile_);
// Ensure that if any user selectable values were added, at least two values
// were, so the user is able to select between them.
DCHECK(!pref_object.user_selectable_values ||
pref_object.user_selectable_values->size() >= 2);
if (pref_object.user_selectable_values) {
// Sort user selectable values to make interacting with them simpler in C++.
// This is not required by the SettingsPrivate API, but is expected in the
// unit_tests associated with this file.
std::sort(pref_object.user_selectable_values->begin(),
pref_object.user_selectable_values->end(),
[](const base::Value& a, const base::Value& b) {
return a.GetInt() < b.GetInt();
});
}
return pref_object;
}
/* static */
void GeneratedCookiePrimarySettingPref::ApplyPrimaryCookieSettingManagedState(
settings_api::PrefObject& pref_object,
Profile* profile) {
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile);
content_settings::ProviderType content_setting_provider;
auto content_setting = map->GetDefaultContentSetting(
ContentSettingsType::COOKIES, &content_setting_provider);
auto content_setting_source =
content_settings::GetSettingSourceFromProviderType(
content_setting_provider);
bool content_setting_enforced =
content_setting_source != SettingSource::kUser;
// Both the content setting and the block_third_party preference can
// be controlled via policy.
const PrefService::Preference* cookie_controls_mode_pref =
profile->GetPrefs()->FindPreference(prefs::kCookieControlsMode);
bool cookie_controls_mode_enforced =
!cookie_controls_mode_pref->IsUserModifiable();
// IsRecommended() cannot be used as we care if a recommended value exists at
// all, even if a user has overwritten it.
bool cookie_controls_mode_recommended =
cookie_controls_mode_pref->GetRecommendedValue();
if (!content_setting_enforced && !cookie_controls_mode_enforced &&
!cookie_controls_mode_recommended) {
// No cookie controls are managed or recommended.
return;
}
if (content_setting_enforced && content_setting == CONTENT_SETTING_BLOCK) {
// Preference is fully managed by the content setting.
pref_object.enforcement = settings_api::Enforcement::kEnforced;
settings_private::GeneratedPref::ApplyControlledByFromContentSettingSource(
&pref_object, content_setting_source);
return;
}
if (content_setting_enforced && cookie_controls_mode_enforced) {
// Preference is considered fully managed by the third party preference.
pref_object.enforcement = settings_api::Enforcement::kEnforced;
settings_private::GeneratedPref::ApplyControlledByFromPref(
&pref_object, cookie_controls_mode_pref);
return;
}
// At this stage the content setting is not enforcing a BLOCK state. Given
// this, allow and block_third_party are still valid choices that do not
// contradict the content setting. They can thus be controlled or recommended
// by the block_third_party preference.
DCHECK(!content_setting_enforced || !cookie_controls_mode_enforced);
if (cookie_controls_mode_recommended) {
auto recommended_value = static_cast<CookieControlsMode>(
cookie_controls_mode_pref->GetRecommendedValue()->GetInt());
pref_object.recommended_value = base::Value(
static_cast<int>(ToCookiePrimarySetting(recommended_value)));
// Based on state assessed so far the enforcement is only recommended. This
// may be changed to ENFORCED later in this function.
pref_object.enforcement = settings_api::Enforcement::kRecommended;
}
// If cookie controls are enforced and the content settings is not enforced,
// you can choose between the selected cookie controls setting and "BLOCK"
if (cookie_controls_mode_enforced) {
pref_object.enforcement = settings_api::Enforcement::kEnforced;
settings_private::GeneratedPref::ApplyControlledByFromPref(
&pref_object, cookie_controls_mode_pref);
auto value = static_cast<CookieControlsMode>(
cookie_controls_mode_pref->GetValue()->GetInt());
settings_private::GeneratedPref::AddUserSelectableValue(
&pref_object, static_cast<int>(ToCookiePrimarySetting(value)));
settings_private::GeneratedPref::AddUserSelectableValue(
&pref_object, static_cast<int>(CookiePrimarySetting::BLOCK_ALL));
return;
}
// The content setting is enforced to either ALLOW OR SESSION_ONLY
if (content_setting_enforced) {
DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
content_setting == CONTENT_SETTING_SESSION_ONLY);
pref_object.enforcement = settings_api::Enforcement::kEnforced;
settings_private::GeneratedPref::ApplyControlledByFromContentSettingSource(
&pref_object, content_setting_source);
settings_private::GeneratedPref::AddUserSelectableValue(
&pref_object, static_cast<int>(CookiePrimarySetting::ALLOW_ALL));
settings_private::GeneratedPref::AddUserSelectableValue(
&pref_object,
static_cast<int>(CookiePrimarySetting::BLOCK_THIRD_PARTY));
settings_private::GeneratedPref::AddUserSelectableValue(
&pref_object,
static_cast<int>(CookiePrimarySetting::BLOCK_THIRD_PARTY_INCOGNITO));
}
}
GeneratedCookieSessionOnlyPref::GeneratedCookieSessionOnlyPref(Profile* profile)
: GeneratedCookiePrefBase(profile, kCookieSessionOnly) {}
extensions::settings_private::SetPrefResult
GeneratedCookieSessionOnlyPref::SetPref(const base::Value* value) {
if (!value->is_bool())
return extensions::settings_private::SetPrefResult::PREF_TYPE_MISMATCH;
if (!IsDefaultCookieContentSettingUserControlled(host_content_settings_map_))
return extensions::settings_private::SetPrefResult::PREF_NOT_MODIFIABLE;
if (host_content_settings_map_->GetDefaultContentSetting(
ContentSettingsType::COOKIES, nullptr) ==
ContentSetting::CONTENT_SETTING_BLOCK)
return extensions::settings_private::SetPrefResult::PREF_NOT_MODIFIABLE;
host_content_settings_map_->SetDefaultContentSetting(
ContentSettingsType::COOKIES,
value->GetBool() ? ContentSetting::CONTENT_SETTING_SESSION_ONLY
: ContentSetting::CONTENT_SETTING_ALLOW);
return extensions::settings_private::SetPrefResult::SUCCESS;
}
settings_api::PrefObject GeneratedCookieSessionOnlyPref::GetPrefObject() const {
settings_api::PrefObject pref_object;
pref_object.key = pref_name_;
pref_object.type = settings_api::PrefType::kBoolean;
content_settings::ProviderType content_setting_provider;
auto content_setting = host_content_settings_map_->GetDefaultContentSetting(
ContentSettingsType::COOKIES, &content_setting_provider);
pref_object.user_control_disabled =
content_setting == ContentSetting::CONTENT_SETTING_BLOCK;
pref_object.value = base::Value(content_setting ==
ContentSetting::CONTENT_SETTING_SESSION_ONLY);
// Content settings can be managed via policy, extension or supervision, but
// cannot be recommended.
auto content_setting_source =
content_settings::GetSettingSourceFromProviderType(
content_setting_provider);
if (content_setting_source == SettingSource::kPolicy) {
pref_object.controlled_by = settings_api::ControlledBy::kDevicePolicy;
pref_object.enforcement = settings_api::Enforcement::kEnforced;
}
if (content_setting_source == SettingSource::kExtension) {
pref_object.controlled_by = settings_api::ControlledBy::kExtension;
pref_object.enforcement = settings_api::Enforcement::kEnforced;
}
if (content_setting_source == SettingSource::kSupervised) {
pref_object.controlled_by = settings_api::ControlledBy::kChildRestriction;
pref_object.enforcement = settings_api::Enforcement::kEnforced;
}
return pref_object;
}
GeneratedCookieDefaultContentSettingPref::
GeneratedCookieDefaultContentSettingPref(Profile* profile)
: GeneratedCookiePrefBase(profile, kCookieDefaultContentSetting) {}
extensions::settings_private::SetPrefResult
GeneratedCookieDefaultContentSettingPref::SetPref(const base::Value* value) {
if (!value->is_string()) {
return extensions::settings_private::SetPrefResult::PREF_TYPE_MISMATCH;
}
ContentSetting setting;
if (!content_settings::ContentSettingFromString(value->GetString(),
&setting)) {
return extensions::settings_private::SetPrefResult::PREF_TYPE_MISMATCH;
}
if (setting != CONTENT_SETTING_ALLOW &&
setting != CONTENT_SETTING_SESSION_ONLY &&
setting != CONTENT_SETTING_BLOCK) {
return extensions::settings_private::SetPrefResult::PREF_TYPE_MISMATCH;
}
if (!IsDefaultCookieContentSettingUserControlled(host_content_settings_map_))
return extensions::settings_private::SetPrefResult::PREF_NOT_MODIFIABLE;
host_content_settings_map_->SetDefaultContentSetting(
ContentSettingsType::COOKIES, static_cast<ContentSetting>(setting));
return extensions::settings_private::SetPrefResult::SUCCESS;
}
settings_api::PrefObject
GeneratedCookieDefaultContentSettingPref::GetPrefObject() const {
settings_api::PrefObject pref_object;
pref_object.key = pref_name_;
pref_object.type = settings_api::PrefType::kString;
content_settings::ProviderType content_setting_provider;
auto content_setting = host_content_settings_map_->GetDefaultContentSetting(
ContentSettingsType::COOKIES, &content_setting_provider);
pref_object.value =
base::Value(content_settings::ContentSettingToString(content_setting));
// Cookies content setting can be managed via policy, extension or
// supervision, but cannot be recommended.
auto content_setting_source =
content_settings::GetSettingSourceFromProviderType(
content_setting_provider);
if (content_setting_source == SettingSource::kPolicy) {
pref_object.controlled_by = settings_api::ControlledBy::kDevicePolicy;
pref_object.enforcement = settings_api::Enforcement::kEnforced;
}
if (content_setting_source == SettingSource::kExtension) {
pref_object.controlled_by = settings_api::ControlledBy::kExtension;
pref_object.enforcement = settings_api::Enforcement::kEnforced;
}
if (content_setting_source == SettingSource::kSupervised) {
pref_object.controlled_by = settings_api::ControlledBy::kChildRestriction;
pref_object.enforcement = settings_api::Enforcement::kEnforced;
}
return pref_object;
}
} // namespace content_settings