0

Add managed indicators to the new Cookies Page control elements

The new cookies page introduces a new control element for interacting
with cookie settings. This CL adds the appropriate managed indicators
to the element and disables the appropriate controls when cookie
settings are managed or recommended.

Bug: 1062649
Change-Id: Ifa2e6abc878a482e045f06fde35fd0723506e615
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2107864
Commit-Queue: Theodore Olsauskas-Warren <sauski@google.com>
Reviewed-by: dpapad <dpapad@chromium.org>
Reviewed-by: Martin Šrámek <msramek@chromium.org>
Reviewed-by: Christian Dullweber <dullweber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#753586}
This commit is contained in:
Theodore Olsauskas-Warren
2020-03-26 12:04:29 +00:00
committed by Commit Bot
parent 4b2808a611
commit 4b529385e1
14 changed files with 779 additions and 18 deletions

@ -51,7 +51,10 @@
on-selected-changed="onCookiesControlRadioChange_">
<settings-collapse-radio-button id="allowAll"
name="[[cookiesControlEnum_.ALLOW_ALL]]"
label="$i18n{cookiePageAllowAll}">
label="$i18n{cookiePageAllowAll}"
disabled="[[cookieControlsManagedState_.allowAll.disabled]]"
policy-indicator-type=
"[[cookieControlsManagedState_.allowAll.indicator]]">
<div slot="collapse">
<div class="bullet-line">
<iron-icon icon="settings:cookie"></iron-icon>
@ -65,7 +68,12 @@
</settings-collapse-radio-button>
<settings-collapse-radio-button id="blockThirdPartyIncognito"
name="[[cookiesControlEnum_.BLOCK_THIRD_PARTY_INCOGNITO]]"
label="$i18n{cookiePageBlockThirdIncognito}">
label="$i18n{cookiePageBlockThirdIncognito}"
disabled="[[
cookieControlsManagedState_.blockThirdPartyIncognito.disabled]]"
policy-indicator-type="[[
cookieControlsManagedState_.blockThirdPartyIncognito.indicator
]]">
<div slot="collapse">
<div class="bullet-line">
<iron-icon icon="settings:cookie"></iron-icon>
@ -83,7 +91,10 @@
</settings-collapse-radio-button>
<settings-collapse-radio-button id="blockThirdParty"
name="[[cookiesControlEnum_.BLOCK_THIRD_PARTY]]"
label="$i18n{cookiePageBlockThird}">
label="$i18n{cookiePageBlockThird}"
disabled="[[cookieControlsManagedState_.blockThirdParty.disabled]]"
policy-indicator-type="[[
cookieControlsManagedState_.blockThirdParty.indicator]]">
<div slot="collapse">
<div class="bullet-line">
<iron-icon icon="settings:cookie"></iron-icon>
@ -97,7 +108,10 @@
</settings-collapse-radio-button>
<settings-collapse-radio-button id="blockAll"
name="[[cookiesControlEnum_.BLOCK_ALL]]"
label="$i18n{cookiePageBlockAll}">
label="$i18n{cookiePageBlockAll}"
disabled="[[cookieControlsManagedState_.blockAll.disabled]]"
policy-indicator-type="[[
cookieControlsManagedState_.blockAll.indicator]]">
<div slot="collapse">
<div class="bullet-line">
<iron-icon icon="settings:block"></iron-icon>
@ -138,21 +152,21 @@
category="[[cookiesContentSettingType_]]"
category-subtype="[[contentSetting_.ALLOW]]"
category-header="$i18n{cookiePageAllowExceptions}"
read-only-list="[[cookiesContentSettingManaged_]]"
read-only-list="[[exceptionListsReadOnly_]]"
search-filter="[[searchTerm]]">
</site-list>
<site-list id="sessionOnlyExceptionsList"
category="[[cookiesContentSettingType_]]"
category-subtype="[[contentSetting_.SESSION_ONLY]]"
category-header="$i18n{cookiePageSessionOnlyExceptions}"
read-only-list="[[cookiesContentSettingManaged_]]"
read-only-list="[[exceptionListsReadOnly_]]"
search-filter="[[searchTerm]]">
</site-list>
<site-list id="blockExceptionsList"
category="[[cookiesContentSettingType_]]"
category-subtype="[[contentSetting_.BLOCK]]"
category-header="$i18n{cookiePageBlockExceptions}"
read-only-list="[[cookiesContentSettingManaged_]]"
read-only-list="[[exceptionListsReadOnly_]]"
search-filter="[[searchTerm]]">
</site-list>
</template>

@ -118,12 +118,17 @@ Polymer({
notify: false,
},
/** @private {!CookieControlsManagedState} */
cookieControlsManagedState_: {
type: Object,
notify: true,
},
/** @type {!Map<string, (string|Function)>} */
focusConfig: {
type: Object,
observer: 'focusConfigChanged_',
},
},
observers: [
@ -186,7 +191,7 @@ Polymer({
await this.browserProxy_.getDefaultValueForContentType(
settings.ContentSettingsTypes.COOKIES);
// Set radio toggle state.
// Set control state.
if (contentSetting.setting === settings.ContentSetting.BLOCK) {
this.cookiesControlRadioSelected_ = CookiesControl.BLOCK_ALL;
} else if (blockThirdParty.value) {
@ -199,22 +204,30 @@ Polymer({
} else {
this.cookiesControlRadioSelected_ = CookiesControl.ALLOW_ALL;
}
this.clearOnExitDisabled_ =
contentSetting.setting === settings.ContentSetting.BLOCK;
// Set clear on exit state.
// Update virtual preference for the clear on exit toggle.
// TODO(crbug.com/1063265): Create toggle that can directly accept state.
this.clearOnExitPref_ = {
key: '',
type: chrome.settingsPrivate.PrefType.BOOLEAN,
value: contentSetting.setting === settings.ContentSetting.BLOCK ?
false :
contentSetting.setting === settings.ContentSetting.SESSION_ONLY,
controlledBy: this.getContolledBy_(contentSetting),
controlledBy: this.getControlledBy_(contentSetting),
enforcement: this.getEnforced_(contentSetting),
};
this.clearOnExitDisabled_ =
contentSetting.setting === settings.ContentSetting.BLOCK;
this.cookiesContentSettingManaged_ =
// Set the exception lists to read only if the content setting is managed.
// Display of managed state for individual entries will be handled by each
// site-list-entry.
this.exceptionListsReadOnly_ =
contentSetting.source === settings.SiteSettingSource.POLICY;
// Retrieve and update remaining controls managed state.
this.cookieControlsManagedState_ =
await this.browserProxy_.getCookieControlsManagedState();
},
/**
@ -223,7 +236,7 @@ Polymer({
* @return {!chrome.settingsPrivate.ControlledBy}
* @private
*/
getContolledBy_({source}) {
getControlledBy_({source}) {
switch (source) {
case ContentSettingProvider.POLICY:
return chrome.settingsPrivate.ControlledBy.DEVICE_POLICY;

@ -38,6 +38,26 @@
*/
let IsValid;
/**
* Stores information about the management state of a control, i.e. whether
* it is disabled and what indicator should be shown (including
* CrPolicyIndicatorType::NONE)
* @typedef {{disabled: boolean,
* indicator: !CrPolicyIndicatorType}}
*/
let ManagedState;
/**
* Stores information about whether individual cookies controls are managed,
* and if so what the source of that management is.
* @typedef {{allowAll: !ManagedState,
* blockThirdPartyIncognito: !ManagedState,
* blockThirdParty: !ManagedState,
* blockAll: !ManagedState,
sessionOnly: !ManagedState}}
*/
let CookieControlsManagedState;
/**
* Stores origin information. The |hasPermissionSettings| will be set to true
* when this origin has permissions or when there is a pattern permission
@ -174,6 +194,13 @@ cr.define('settings', function() {
*/
getAllSites(contentTypes) {}
/**
* Get whether each of the cookie controls are managed or not, and what
* the source of that management is.
* @return {!Promise<!CookieControlsManagedState>}
*/
getCookieControlsManagedState() {}
/**
* Get the string which describes the current effective cookie setting.
* @return {!Promise<string>}
@ -427,6 +454,11 @@ cr.define('settings', function() {
return cr.sendWithPromise('getAllSites', contentTypes);
}
/** @override */
getCookieControlsManagedState() {
return cr.sendWithPromise('getCookieControlsManagedState');
}
/** @override */
getCookieSettingDescription() {
return cr.sendWithPromise('getCookieSettingDescription');

@ -347,6 +347,15 @@ int GetNumCookieExceptionsOfTypes(HostContentSettingsMap* map,
});
}
base::Value GetValueForManagedState(const site_settings::ManagedState& state) {
base::Value value(base::Value::Type::DICTIONARY);
value.SetKey(site_settings::kDisabled, base::Value(state.disabled));
value.SetKey(
site_settings::kPolicyIndicator,
base::Value(site_settings::PolicyIndicatorTypeToString(state.indicator)));
return value;
}
std::string GetCookieSettingDescription(Profile* profile) {
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile);
@ -417,6 +426,11 @@ void SiteSettingsHandler::RegisterMessages() {
"getAllSites",
base::BindRepeating(&SiteSettingsHandler::HandleGetAllSites,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getCookieControlsManagedState",
base::BindRepeating(
&SiteSettingsHandler::HandleGetCookieControlsManagedState,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getCookieSettingDescription",
base::BindRepeating(
@ -812,6 +826,30 @@ void SiteSettingsHandler::HandleGetAllSites(const base::ListValue* args) {
ResolveJavascriptCallback(base::Value(callback_id), result);
}
void SiteSettingsHandler::HandleGetCookieControlsManagedState(
const base::ListValue* args) {
AllowJavascript();
CHECK_EQ(1U, args->GetList().size());
std::string callback_id = args->GetList()[0].GetString();
auto managed_states = site_settings::GetCookieControlsManagedState(profile_);
base::Value result(base::Value::Type::DICTIONARY);
result.SetKey(site_settings::kAllowAll,
GetValueForManagedState(managed_states.allow_all));
result.SetKey(
site_settings::kBlockThirdPartyIncognito,
GetValueForManagedState(managed_states.block_third_party_incognito));
result.SetKey(site_settings::kBlockThirdParty,
GetValueForManagedState(managed_states.block_third_party));
result.SetKey(site_settings::kBlockAll,
GetValueForManagedState(managed_states.block_all));
result.SetKey(site_settings::kSessionOnly,
GetValueForManagedState(managed_states.session_only));
ResolveJavascriptCallback(base::Value(callback_id), result);
}
void SiteSettingsHandler::HandleGetCookieSettingDescription(
const base::ListValue* args) {
AllowJavascript();

@ -125,6 +125,7 @@ class SiteSettingsHandler
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ZoomLevels);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
HandleClearEtldPlus1DataAndCookies);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, CookieControlsManagedState);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, CookieSettingDescription);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, HandleGetFormattedBytes);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
@ -167,6 +168,10 @@ class SiteSettingsHandler
// the front end when fetching finished.
void HandleGetAllSites(const base::ListValue* args);
// Returns whether each of the cookie controls is managed and if so what
// the source of that management is.
void HandleGetCookieControlsManagedState(const base::ListValue* args);
// Returns a string for display describing the current cookie settings.
void HandleGetCookieSettingDescription(const base::ListValue* args);

@ -45,6 +45,8 @@
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/features.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/content_settings/core/test/content_settings_mock_provider.h"
#include "components/content_settings/core/test/content_settings_test_utils.h"
#include "components/history/core/browser/history_service.h"
#include "components/infobars/core/infobar.h"
#include "components/permissions/chooser_context_base.h"
@ -2175,6 +2177,61 @@ TEST_F(SiteSettingsHandlerTest, HandleClearEtldPlus1DataAndCookies) {
EXPECT_EQ(0U, storage_and_cookie_list->GetSize());
}
TEST_F(SiteSettingsHandlerTest, CookieControlsManagedState) {
// Test that the handler correctly wraps the helper result. Helper with
// extensive logic is tested in site_settings_helper_unittest.cc.
const std::string kNone = "none";
const std::string kDevicePolicy = "devicePolicy";
const std::vector<std::string> kControlNames = {
"allowAll", "blockAll", "blockThirdParty", "blockThirdPartyIncognito",
"sessionOnly"};
// Check that the default cookie control state is handled correctly.
base::ListValue get_args;
get_args.AppendString(kCallbackId);
handler()->HandleGetCookieControlsManagedState(&get_args);
{
const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
EXPECT_EQ("cr.webUIResponse", data.function_name());
EXPECT_EQ(kCallbackId, data.arg1()->GetString());
ASSERT_TRUE(data.arg2()->GetBool());
for (const auto& control_name : kControlNames) {
auto* control_state = data.arg3()->FindPath(control_name);
ASSERT_FALSE(control_state->FindKey("disabled")->GetBool());
ASSERT_EQ(kNone, control_state->FindKey("indicator")->GetString());
}
}
// Check that a fully managed cookie state is handled correctly.
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile());
auto provider = std::make_unique<content_settings::MockProvider>();
provider->SetWebsiteSetting(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
ContentSettingsType::COOKIES, std::string(),
std::make_unique<base::Value>(CONTENT_SETTING_ALLOW));
content_settings::TestUtils::OverrideProvider(
map, std::move(provider), HostContentSettingsMap::POLICY_PROVIDER);
sync_preferences::TestingPrefServiceSyncable* pref_service =
profile()->GetTestingPrefService();
pref_service->SetManagedPref(prefs::kBlockThirdPartyCookies,
std::make_unique<base::Value>(true));
handler()->HandleGetCookieControlsManagedState(&get_args);
{
const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
EXPECT_EQ("cr.webUIResponse", data.function_name());
EXPECT_EQ(kCallbackId, data.arg1()->GetString());
ASSERT_TRUE(data.arg2()->GetBool());
for (const auto& control_name : kControlNames) {
auto* control_state = data.arg3()->FindPath(control_name);
ASSERT_TRUE(control_state->FindKey("disabled")->GetBool());
ASSERT_EQ(kDevicePolicy,
control_state->FindKey("indicator")->GetString());
}
}
}
TEST_F(SiteSettingsHandlerTest, CookieSettingDescription) {
const auto kBlocked = [](int num) {
return l10n_util::GetPluralStringFUTF8(

@ -26,10 +26,12 @@
#include "chrome/browser/usb/usb_chooser_context.h"
#include "chrome/browser/usb/usb_chooser_context_factory.h"
#include "chrome/common/pref_names.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/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/permissions/chooser_context_base.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_result.h"
@ -166,6 +168,28 @@ static_assert(base::size(kSiteSettingSourceStringMapping) ==
"kSiteSettingSourceStringMapping should have "
"SiteSettingSource::kNumSources elements");
struct PolicyIndicatorTypeStringMapping {
PolicyIndicatorType source;
const char* indicator_str;
};
// Converts a policy indicator type to its JS usable string representation.
const PolicyIndicatorTypeStringMapping kPolicyIndicatorTypeStringMapping[] = {
{PolicyIndicatorType::kDevicePolicy, "devicePolicy"},
{PolicyIndicatorType::kExtension, "extension"},
{PolicyIndicatorType::kNone, "none"},
{PolicyIndicatorType::kOwner, "owner"},
{PolicyIndicatorType::kPrimaryUser, "primary_user"},
{PolicyIndicatorType::kRecommended, "recommended"},
{PolicyIndicatorType::kUserPolicy, "userPolicy"},
{PolicyIndicatorType::kParent, "parent"},
{PolicyIndicatorType::kChildRestriction, "childRestriction"},
};
static_assert(base::size(kPolicyIndicatorTypeStringMapping) ==
static_cast<int>(PolicyIndicatorType::kNumIndicators),
"kPolicyIndicatorStringMapping should have "
"PolicyIndicatorType::kNumIndicators elements");
// Retrieves the corresponding string, according to the following precedence
// order from highest to lowest priority:
// 1. Kill-switch.
@ -315,6 +339,45 @@ const ChooserTypeNameEntry kChooserTypeGroupNames[] = {
{&GetHidChooserContext, kHidChooserDataGroupType},
{&GetBluetoothChooserContext, kBluetoothChooserDataGroupType}};
PolicyIndicatorType GetPolicyIndicatorFromSettingSource(
content_settings::SettingSource setting_source) {
switch (setting_source) {
case content_settings::SETTING_SOURCE_POLICY:
return PolicyIndicatorType::kDevicePolicy;
case content_settings::SETTING_SOURCE_SUPERVISED:
return PolicyIndicatorType::kParent;
case content_settings::SETTING_SOURCE_EXTENSION:
return PolicyIndicatorType::kExtension;
case content_settings::SETTING_SOURCE_USER:
case content_settings::SETTING_SOURCE_WHITELIST:
case content_settings::SETTING_SOURCE_NONE:
return PolicyIndicatorType::kNone;
case content_settings::SETTING_SOURCE_INSTALLED_WEBAPP:
NOTREACHED();
return PolicyIndicatorType::kNone;
}
}
PolicyIndicatorType GetPolicyIndicatorFromPref(
const PrefService::Preference* pref) {
if (!pref) {
return PolicyIndicatorType::kNone;
}
if (pref->IsExtensionControlled()) {
return PolicyIndicatorType::kExtension;
}
if (pref->IsManagedByCustodian()) {
return PolicyIndicatorType::kParent;
}
if (pref->IsManaged()) {
return PolicyIndicatorType::kDevicePolicy;
}
if (pref->GetRecommendedValue()) {
return PolicyIndicatorType::kRecommended;
}
return PolicyIndicatorType::kNone;
}
} // namespace
bool HasRegisteredGroupName(ContentSettingsType type) {
@ -808,4 +871,125 @@ base::Value GetChooserExceptionListFromProfile(
return exceptions;
}
CookieControlsManagedState GetCookieControlsManagedState(Profile* profile) {
// Setup a default unmanaged state that is updated based on the actual
// managed state.
CookieControlsManagedState managed_state;
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile);
std::string content_setting_provider;
auto content_setting = map->GetDefaultContentSetting(
ContentSettingsType::COOKIES, &content_setting_provider);
auto content_setting_source =
HostContentSettingsMap::GetSettingSourceFromProviderName(
content_setting_provider);
bool content_setting_enforced =
content_setting_source !=
content_settings::SettingSource::SETTING_SOURCE_USER;
// Both the content setting and the block_third_party preference can
// be controlled via policy.
const PrefService::Preference* block_third_party_pref =
profile->GetPrefs()->FindPreference(prefs::kBlockThirdPartyCookies);
bool block_third_party_on = block_third_party_pref->GetValue()->GetBool();
bool block_third_party_enforced = !block_third_party_pref->IsUserModifiable();
// IsRecommended() cannot be used as we care if a recommended value exists at
// all, even if a user has overwritten it.
bool block_third_party_recommended =
(block_third_party_pref && block_third_party_pref->GetRecommendedValue());
bool block_third_party_recommended_on =
block_third_party_recommended &&
block_third_party_pref->GetRecommendedValue()->GetBool();
// kCookieControlsMode == kOn should imply block_third_party is on.
auto control_mode = static_cast<content_settings::CookieControlsMode>(
profile->GetPrefs()->GetInteger(prefs::kCookieControlsMode));
DCHECK(control_mode != content_settings::CookieControlsMode::kOn ||
block_third_party_on)
<< "kCookieControlsMode == kOn should imply "
<< "kBlockThirdPartyCookies is true";
// Get indicators representing each settings source. These may or may not
// be used depending on the determined managed state.
auto content_setting_source_indicator =
GetPolicyIndicatorFromSettingSource(content_setting_source);
auto block_third_party_source_indicator =
GetPolicyIndicatorFromPref(block_third_party_pref);
if (!content_setting_enforced && !block_third_party_enforced &&
!block_third_party_recommended) {
// No cookie controls are managed or recommended.
return managed_state;
}
if (content_setting_enforced) {
// Block and session only managed state only depend on the content setting.
managed_state.block_all = {/*disabled*/ true,
content_setting_source_indicator};
managed_state.session_only = {/*disabled*/ true,
content_setting_source_indicator};
}
if (content_setting_enforced && content_setting == CONTENT_SETTING_BLOCK) {
// All remaining controls are managed by the content setting source.
managed_state.allow_all = {/*disabled*/ true,
content_setting_source_indicator};
managed_state.block_third_party_incognito = {
/*disabled*/ true, content_setting_source_indicator};
managed_state.block_third_party = {/*disabled*/ true,
content_setting_source_indicator};
return managed_state;
}
if (content_setting_enforced && block_third_party_enforced) {
// All remaining controls are managed by the block_third_party source.
managed_state.allow_all = {/*disabled*/ true,
block_third_party_source_indicator};
managed_state.block_third_party_incognito = {
/*disabled*/ true, block_third_party_source_indicator};
managed_state.block_third_party = {/*disabled*/ true,
block_third_party_source_indicator};
return managed_state;
}
DCHECK(!content_setting_enforced ||
content_setting == CONTENT_SETTING_ALLOW ||
content_setting == CONTENT_SETTING_SESSION_ONLY);
DCHECK(!content_setting_enforced || !block_third_party_enforced);
// 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.
if (block_third_party_enforced) {
DCHECK(!content_setting_enforced);
managed_state.block_third_party_incognito = {
true, block_third_party_source_indicator};
if (block_third_party_on) {
managed_state.allow_all = {/*disabled*/ true,
block_third_party_source_indicator};
} else {
managed_state.block_third_party = {/*disabled*/ true,
block_third_party_source_indicator};
}
return managed_state;
}
if (block_third_party_recommended) {
if (block_third_party_recommended_on) {
managed_state.block_third_party = {/*disabled*/ false,
block_third_party_source_indicator};
} else {
managed_state.allow_all = {/*disabled*/ false,
block_third_party_source_indicator};
}
return managed_state;
}
DCHECK(content_setting_enforced && !block_third_party_enforced &&
!block_third_party_recommended);
return managed_state;
}
std::string PolicyIndicatorTypeToString(const PolicyIndicatorType type) {
return kPolicyIndicatorTypeStringMapping[static_cast<int>(type)]
.indicator_str;
}
} // namespace site_settings

@ -52,16 +52,23 @@ typedef std::map<std::pair<ContentSettingsPattern, std::string>,
using ChooserExceptionDetails =
std::map<std::pair<GURL, std::string>, std::set<std::pair<GURL, bool>>>;
constexpr char kAllowAll[] = "allowAll";
constexpr char kBlockThirdPartyIncognito[] = "blockThirdPartyIncognito";
constexpr char kBlockThirdParty[] = "blockThirdParty";
constexpr char kBlockAll[] = "blockAll";
constexpr char kSessionOnly[] = "sessionOnly";
constexpr char kChooserType[] = "chooserType";
constexpr char kDisplayName[] = "displayName";
constexpr char kEmbeddingOrigin[] = "embeddingOrigin";
constexpr char kIncognito[] = "incognito";
constexpr char kObject[] = "object";
constexpr char kDisabled[] = "disabled";
constexpr char kOrigin[] = "origin";
constexpr char kOriginForFavicon[] = "originForFavicon";
constexpr char kRecentPermissions[] = "recentPermissions";
constexpr char kSetting[] = "setting";
constexpr char kSites[] = "sites";
constexpr char kPolicyIndicator[] = "indicator";
constexpr char kSource[] = "source";
constexpr char kType[] = "type";
@ -78,6 +85,40 @@ enum class SiteSettingSource {
kNumSources,
};
// Possible policy indicators that can be shown in settings.
// Must be kept in sync with the CrPolicyIndicatorType enum located in
// src/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.js
enum class PolicyIndicatorType {
kDevicePolicy,
kExtension,
kNone,
kOwner,
kPrimaryUser,
kRecommended,
kUserPolicy,
kParent,
kChildRestriction,
kNumIndicators,
};
// Represents the managed state for a single settings control.
struct ManagedState {
bool disabled = false;
PolicyIndicatorType indicator = PolicyIndicatorType::kNone;
};
// Represents the manage states for all of the cookie controls.
struct CookieControlsManagedState {
ManagedState allow_all;
ManagedState block_third_party_incognito;
ManagedState block_third_party;
ManagedState block_all;
ManagedState session_only;
};
// Concerts a PolicyIndicatorType to its string identifier.
std::string PolicyIndicatorTypeToString(const PolicyIndicatorType type);
// Returns whether a group name has been registered for the given type.
bool HasRegisteredGroupName(ContentSettingsType type);
@ -186,6 +227,9 @@ base::Value GetChooserExceptionListFromProfile(
Profile* profile,
const ChooserTypeNameEntry& chooser_type);
// Returns the cookie controls manage state for a given profile.
CookieControlsManagedState GetCookieControlsManagedState(Profile* profile);
} // namespace site_settings
#endif // CHROME_BROWSER_UI_WEBUI_SITE_SETTINGS_HELPER_H_

@ -21,6 +21,7 @@
#include "components/content_settings/core/test/content_settings_test_utils.h"
#include "components/permissions/chooser_context_base.h"
#include "components/prefs/pref_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/extension_registry.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
@ -622,4 +623,302 @@ TEST_F(SiteSettingsHelperChooserExceptionTest,
}
}
namespace {
// All of the possible managed states for a boolean preference that can be
// both enforced and recommended.
enum class PrefSetting {
kEnforcedOff,
kEnforcedOn,
kRecommendedOff,
kRecommendedOn,
kNotSet,
};
// Possible preference sources supported by TestingPrefService.
// TODO(crbug.com/1063281): Extend TestingPrefService to support prefs set for
// supervised users.
enum class PrefSource {
kExtension,
kDevicePolicy,
kRecommended,
kNone,
};
// Represents a set of settings, preferences and the associated expected
// CookieControlsManagedState.
struct CookiesManagedStateTestCase {
ContentSetting default_content_setting;
content_settings::SettingSource default_content_setting_source;
PrefSetting block_third_party;
PrefSource block_third_party_source;
CookieControlsManagedState expected_result;
};
const std::vector<CookiesManagedStateTestCase> test_cases = {
{CONTENT_SETTING_DEFAULT,
content_settings::SETTING_SOURCE_NONE,
PrefSetting::kEnforcedOff,
PrefSource::kExtension,
{{false, PolicyIndicatorType::kNone},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone}}},
{CONTENT_SETTING_DEFAULT,
content_settings::SETTING_SOURCE_NONE,
PrefSetting::kEnforcedOn,
PrefSource::kDevicePolicy,
{{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone}}},
{CONTENT_SETTING_DEFAULT,
content_settings::SETTING_SOURCE_NONE,
PrefSetting::kRecommendedOff,
PrefSource::kRecommended,
{{false, PolicyIndicatorType::kRecommended},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone}}},
{CONTENT_SETTING_DEFAULT,
content_settings::SETTING_SOURCE_NONE,
PrefSetting::kRecommendedOn,
PrefSource::kRecommended,
{{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kRecommended},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone}}},
{CONTENT_SETTING_DEFAULT,
content_settings::SETTING_SOURCE_NONE,
PrefSetting::kNotSet,
PrefSource::kNone,
{{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone}}},
{CONTENT_SETTING_ALLOW,
content_settings::SETTING_SOURCE_POLICY,
PrefSetting::kEnforcedOff,
PrefSource::kExtension,
{{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy}}},
{CONTENT_SETTING_ALLOW,
content_settings::SETTING_SOURCE_EXTENSION,
PrefSetting::kEnforcedOn,
PrefSource::kDevicePolicy,
{{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension}}},
{CONTENT_SETTING_ALLOW,
content_settings::SETTING_SOURCE_SUPERVISED,
PrefSetting::kRecommendedOff,
PrefSource::kRecommended,
{{false, PolicyIndicatorType::kRecommended},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent}}},
{CONTENT_SETTING_ALLOW,
content_settings::SETTING_SOURCE_POLICY,
PrefSetting::kRecommendedOn,
PrefSource::kRecommended,
{{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kRecommended},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy}}},
{CONTENT_SETTING_ALLOW,
content_settings::SETTING_SOURCE_EXTENSION,
PrefSetting::kNotSet,
PrefSource::kNone,
{{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension}}},
{CONTENT_SETTING_BLOCK,
content_settings::SETTING_SOURCE_SUPERVISED,
PrefSetting::kEnforcedOff,
PrefSource::kDevicePolicy,
{{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent}}},
{CONTENT_SETTING_BLOCK,
content_settings::SETTING_SOURCE_POLICY,
PrefSetting::kEnforcedOn,
PrefSource::kExtension,
{{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy}}},
{CONTENT_SETTING_BLOCK,
content_settings::SETTING_SOURCE_EXTENSION,
PrefSetting::kRecommendedOff,
PrefSource::kRecommended,
{{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension}}},
{CONTENT_SETTING_BLOCK,
content_settings::SETTING_SOURCE_SUPERVISED,
PrefSetting::kRecommendedOn,
PrefSource::kRecommended,
{{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent}}},
{CONTENT_SETTING_BLOCK,
content_settings::SETTING_SOURCE_POLICY,
PrefSetting::kNotSet,
PrefSource::kNone,
{{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy}}},
{CONTENT_SETTING_SESSION_ONLY,
content_settings::SETTING_SOURCE_EXTENSION,
PrefSetting::kEnforcedOff,
PrefSource::kDevicePolicy,
{{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension}}},
{CONTENT_SETTING_SESSION_ONLY,
content_settings::SETTING_SOURCE_SUPERVISED,
PrefSetting::kEnforcedOn,
PrefSource::kExtension,
{{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent}}},
{CONTENT_SETTING_SESSION_ONLY,
content_settings::SETTING_SOURCE_POLICY,
PrefSetting::kRecommendedOff,
PrefSource::kRecommended,
{{false, PolicyIndicatorType::kRecommended},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{true, PolicyIndicatorType::kDevicePolicy},
{true, PolicyIndicatorType::kDevicePolicy}}},
{CONTENT_SETTING_SESSION_ONLY,
content_settings::SETTING_SOURCE_EXTENSION,
PrefSetting::kRecommendedOn,
PrefSource::kRecommended,
{{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kRecommended},
{true, PolicyIndicatorType::kExtension},
{true, PolicyIndicatorType::kExtension}}},
{CONTENT_SETTING_SESSION_ONLY,
content_settings::SETTING_SOURCE_SUPERVISED,
PrefSetting::kNotSet,
PrefSource::kNone,
{{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{false, PolicyIndicatorType::kNone},
{true, PolicyIndicatorType::kParent},
{true, PolicyIndicatorType::kParent}}}};
void SetupTestConditions(HostContentSettingsMap* map,
sync_preferences::TestingPrefServiceSyncable* prefs,
const CookiesManagedStateTestCase& test_case) {
if (test_case.default_content_setting != CONTENT_SETTING_DEFAULT) {
auto provider = std::make_unique<content_settings::MockProvider>();
provider->SetWebsiteSetting(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
ContentSettingsType::COOKIES, std::string(),
std::make_unique<base::Value>(test_case.default_content_setting));
HostContentSettingsMap::ProviderType provider_type;
switch (test_case.default_content_setting_source) {
case content_settings::SETTING_SOURCE_POLICY:
provider_type = HostContentSettingsMap::POLICY_PROVIDER;
break;
case content_settings::SETTING_SOURCE_EXTENSION:
provider_type = HostContentSettingsMap::CUSTOM_EXTENSION_PROVIDER;
break;
case content_settings::SETTING_SOURCE_SUPERVISED:
provider_type = HostContentSettingsMap::SUPERVISED_PROVIDER;
break;
case content_settings::SETTING_SOURCE_NONE:
default:
provider_type = HostContentSettingsMap::DEFAULT_PROVIDER;
}
content_settings::TestUtils::OverrideProvider(map, std::move(provider),
provider_type);
}
if (test_case.block_third_party != PrefSetting::kNotSet) {
bool third_party_value =
test_case.block_third_party == PrefSetting::kRecommendedOn ||
test_case.block_third_party == PrefSetting::kEnforcedOn;
if (test_case.block_third_party_source == PrefSource::kExtension) {
prefs->SetExtensionPref(prefs::kBlockThirdPartyCookies,
std::make_unique<base::Value>(third_party_value));
} else if (test_case.block_third_party_source ==
PrefSource::kDevicePolicy) {
prefs->SetManagedPref(prefs::kBlockThirdPartyCookies,
std::make_unique<base::Value>(third_party_value));
} else if (test_case.block_third_party_source == PrefSource::kRecommended) {
prefs->SetRecommendedPref(
prefs::kBlockThirdPartyCookies,
std::make_unique<base::Value>(third_party_value));
}
}
}
void AssertManagedCookieStateEqual(const CookieControlsManagedState& a,
const CookieControlsManagedState b) {
ASSERT_EQ(a.allow_all.disabled, b.allow_all.disabled);
ASSERT_EQ(a.allow_all.indicator, b.allow_all.indicator);
ASSERT_EQ(a.block_third_party_incognito.disabled,
b.block_third_party_incognito.disabled);
ASSERT_EQ(a.block_third_party_incognito.indicator,
b.block_third_party_incognito.indicator);
ASSERT_EQ(a.block_third_party.disabled, b.block_third_party.disabled);
ASSERT_EQ(a.block_third_party.indicator, b.block_third_party.indicator);
ASSERT_EQ(a.block_all.disabled, b.block_all.disabled);
ASSERT_EQ(a.block_all.indicator, b.block_all.indicator);
ASSERT_EQ(a.session_only.disabled, b.session_only.disabled);
ASSERT_EQ(a.session_only.indicator, b.session_only.indicator);
}
TEST_F(SiteSettingsHelperTest, CookiesManagedState) {
for (auto test_case : test_cases) {
TestingProfile profile;
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(&profile);
sync_preferences::TestingPrefServiceSyncable* prefs =
profile.GetTestingPrefService();
testing::Message scope_message;
scope_message << "Content Setting:" << test_case.default_content_setting
<< " Block Third Party:"
<< static_cast<int>(test_case.block_third_party);
SCOPED_TRACE(scope_message);
SetupTestConditions(map, prefs, test_case);
AssertManagedCookieStateEqual(
site_settings::GetCookieControlsManagedState(&profile),
test_case.expected_result);
}
}
} // namespace
} // namespace site_settings

@ -30,6 +30,9 @@ suite('CrSettingsCookiesPageTest', function() {
/** @type {Element} */
let blockAll;
/** @type {array<!Element>} */
let radioButtons;
setup(function() {
testMetricsBrowserProxy = new TestMetricsBrowserProxy();
settings.MetricsBrowserProxyImpl.instance_ = testMetricsBrowserProxy;
@ -53,6 +56,8 @@ suite('CrSettingsCookiesPageTest', function() {
blockAll = page.$$('#blockAll');
clearOnExit = page.$$('#clearOnExit');
networkPrediction = page.$$('#networkPrediction');
radioButtons =
[allowAll, blockThirdPartyIncognito, blockThirdParty, blockAll];
});
teardown(function() {
@ -246,7 +251,14 @@ suite('CrSettingsCookiesPageTest', function() {
}
});
test('CookieSettingExceptions_Managed', async function() {
test('CookieControls_ManagedState', async function() {
const managedControlState = {
allowAll: {disabled: true, indicator: 'devicePolicy'},
blockThirdPartyIncognito: {disabled: true, indicator: 'devicePolicy'},
blockThirdParty: {disabled: true, indicator: 'devicePolicy'},
blockAll: {disabled: true, indicator: 'devicePolicy'},
sessionOnly: {disabled: true, indicator: 'devicePolicy'},
};
const managedPrefs = test_util.createSiteSettingsPrefs(
[test_util.createContentSettingTypeToValuePair(
settings.ContentSettingsTypes.COOKIES,
@ -255,16 +267,40 @@ suite('CrSettingsCookiesPageTest', function() {
source: settings.SiteSettingSource.POLICY
}))],
[]);
siteSettingsBrowserProxy.setResultFor(
'getCookieControlsManagedState', Promise.resolve(managedControlState));
siteSettingsBrowserProxy.setPrefs(managedPrefs);
await siteSettingsBrowserProxy.whenCalled('getDefaultValueForContentType');
await siteSettingsBrowserProxy.whenCalled('getCookieControlsManagedState');
Polymer.dom.flush();
// Check the four radio buttons are correctly indicating they are managed.
for (button of radioButtons) {
assertTrue(button.disabled);
assertEquals(button.policyIndicatorType, 'devicePolicy');
}
// Check the clear on exit toggle is correctly indicating it is managed.
assertTrue(clearOnExit.checked);
assertTrue(clearOnExit.controlDisabled());
assertTrue(
test_util.isChildVisible(clearOnExit, 'cr-policy-pref-indicator'));
let exceptionLists = page.shadowRoot.querySelectorAll('site-list');
// Check all exception lists are read only.
assertEquals(exceptionLists.length, 3);
for (const list of exceptionLists) {
assertTrue(!!list.readOnlyList);
}
// Revert to an unmanaged state and ensure all controls return to unmanged.
const unmanagedControlState = {
allowAll: {disabled: false, indicator: 'none'},
blockThirdPartyIncognito: {disabled: false, indicator: 'none'},
blockThirdParty: {disabled: false, indicator: 'none'},
blockAll: {disabled: false, indicator: 'none'},
sessionOnly: {disabled: false, indicator: 'none'},
};
const unmanagedPrefs = test_util.createSiteSettingsPrefs(
[test_util.createContentSettingTypeToValuePair(
settings.ContentSettingsTypes.COOKIES,
@ -272,10 +308,27 @@ suite('CrSettingsCookiesPageTest', function() {
setting: settings.ContentSetting.ALLOW,
}))],
[]);
siteSettingsBrowserProxy.reset();
siteSettingsBrowserProxy.setResultFor(
'getCookieControlsManagedState',
Promise.resolve(unmanagedControlState));
siteSettingsBrowserProxy.setPrefs(unmanagedPrefs);
await siteSettingsBrowserProxy.whenCalled('getDefaultValueForContentType');
Polymer.dom.flush();
await siteSettingsBrowserProxy.whenCalled('getCookieControlsManagedState');
// Check the four radio buttons no longer indicate they are managed.
for (button of radioButtons) {
assertFalse(button.disabled);
assertEquals(button.policyIndicatorType, 'none');
}
// Check the clear on exit toggle no longer indicates it is managed.
assertFalse(clearOnExit.checked);
assertFalse(clearOnExit.controlDisabled());
assertFalse(
test_util.isChildVisible(clearOnExit, 'cr-policy-pref-indicator'));
// Check all exception lists are no longer read only.
exceptionLists = page.shadowRoot.querySelectorAll('site-list');
assertEquals(exceptionLists.length, 3);
for (const list of exceptionLists) {

@ -82,6 +82,7 @@ let SiteSettingsPref;
this.mockMethods([
'getCookieSettingDescription',
'getCookieControlsManagedState',
'getRecentSitePermissions',
]);
}

@ -911,6 +911,18 @@ HostContentSettingsMap::GetProviderTypeFromSource(const std::string& source) {
return DEFAULT_PROVIDER;
}
// static
content_settings::SettingSource
HostContentSettingsMap::GetSettingSourceFromProviderName(
const std::string& provider_name) {
for (const auto& provider_name_source : kProviderNamesSourceMap) {
if (provider_name == provider_name_source.provider_name)
return provider_name_source.provider_source;
}
NOTREACHED();
return content_settings::SETTING_SOURCE_NONE;
}
std::unique_ptr<base::Value> HostContentSettingsMap::GetWebsiteSettingInternal(
const GURL& primary_url,
const GURL& secondary_url,

@ -288,6 +288,10 @@ class HostContentSettingsMap : public content_settings::Observer,
// to convert backwards.
static ProviderType GetProviderTypeFromSource(const std::string& source);
// Returns the SettingSource associated with the given |provider_name| string.
static content_settings::SettingSource GetSettingSourceFromProviderName(
const std::string& provider_name);
// Whether this settings map is for an incognito or guest session.
bool IsOffTheRecord() const { return is_off_the_record_; }

@ -29,7 +29,12 @@
// eslint-disable-next-line no-var
var CrPolicyStrings;
/** @enum {string} */
/**
* Possible policy indicators that can be shown in settings.
* Must be kept in sync with the PolicyIndicatorType enum located in
* chrome/browser/ui/webui/site_settings_helper.h
* @enum {string}
*/
/* #export */ const CrPolicyIndicatorType = {
DEVICE_POLICY: 'devicePolicy',
EXTENSION: 'extension',