Commit eee03003 authored by Theodore Olsauskas-Warren's avatar Theodore Olsauskas-Warren Committed by Commit Bot

Add GetRecentSitePermissions handler to SiteSettingsHandler

As part of the privacy settings redesign, the capability to display
recently changed permissions to the user is required. This CL introduces
the a handler to enable querying for this information from JS.

The GetRecentSitePermissions provides the most recent permissions
changes grouped by at most N unique origin/[incognito, normal]
combinations.

Existing functionality which is reused from the GetAllSites handler
is also refactored out.

Bug: 1032584
Change-Id: I885c859d455bc49d8ec9d91bf3101cf337c4ae51
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2002577
Commit-Queue: Theodore Olsauskas-Warren <sauski@google.com>
Reviewed-by: default avatarBalazs Engedy <engedy@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#739027}
parent 6ccb8a6e
...@@ -65,6 +65,7 @@ let SiteGroup; ...@@ -65,6 +65,7 @@ let SiteGroup;
* incognito: boolean, * incognito: boolean,
* origin: string, * origin: string,
* displayName: string, * displayName: string,
* type: string,
* setting: !settings.ContentSetting, * setting: !settings.ContentSetting,
* source: !settings.SiteSettingSource}} * source: !settings.SiteSettingSource}}
*/ */
...@@ -85,6 +86,16 @@ let RawSiteException; ...@@ -85,6 +86,16 @@ let RawSiteException;
*/ */
let SiteException; let SiteException;
/**
* Represents a list of exceptions recently configured for a site, where recent
* is defined by the maximum number of sources parameter passed to
* GetRecentSiterPermissions.
* @typedef {{origin: string,
* incognito: boolean,
* recentPermissions: Array<RawSiteException>}}
*/
let RecentSitePermissions;
/** /**
* The chooser exception information passed from the C++ handler. * The chooser exception information passed from the C++ handler.
* See also: ChooserException. * See also: ChooserException.
...@@ -157,6 +168,18 @@ cr.define('settings', function() { ...@@ -157,6 +168,18 @@ cr.define('settings', function() {
*/ */
getAllSites(contentTypes) {} getAllSites(contentTypes) {}
/**
* Gets most recently changed permissions grouped by host and limited to
* numSources different origin/profile (inconigto/regular) pairings.
* This includes permissions adjusted by embargo, but excludes any set
* via policy.
* @param {!Array<!settings.ContentSettingsTypes>} contentTypes A list of
* the content types to retrieve sites with recently changed settings.
* @param {!number} numSources Maximum number of different sources to return
* @return {!Promise<!Array<!RecentSitePermissions>>}
*/
getRecentSitePermissions(contentTypes, numSources) {}
/** /**
* Gets the chooser exceptions for a particular chooser type. * Gets the chooser exceptions for a particular chooser type.
* @param {settings.ChooserType} chooserType The chooser type to grab * @param {settings.ChooserType} chooserType The chooser type to grab
...@@ -392,6 +415,12 @@ cr.define('settings', function() { ...@@ -392,6 +415,12 @@ cr.define('settings', function() {
return cr.sendWithPromise('getAllSites', contentTypes); return cr.sendWithPromise('getAllSites', contentTypes);
} }
/** @override */
getRecentSitePermissions(contentTypes, numSources) {
return cr.sendWithPromise(
'getRecentSitePermissions', contentTypes, numSources);
}
/** @override */ /** @override */
getChooserExceptionList(chooserType) { getChooserExceptionList(chooserType) {
return cr.sendWithPromise('getChooserExceptionList', chooserType); return cr.sendWithPromise('getChooserExceptionList', chooserType);
......
...@@ -1327,6 +1327,8 @@ jumbo_static_library("ui") { ...@@ -1327,6 +1327,8 @@ jumbo_static_library("ui") {
"webui/policy_indicator_localized_strings_provider.h", "webui/policy_indicator_localized_strings_provider.h",
"webui/profile_info_watcher.cc", "webui/profile_info_watcher.cc",
"webui/profile_info_watcher.h", "webui/profile_info_watcher.h",
"webui/recent_site_settings_helper.cc",
"webui/recent_site_settings_helper.h",
"webui/settings/about_handler.cc", "webui/settings/about_handler.cc",
"webui/settings/about_handler.h", "webui/settings/about_handler.h",
"webui/settings/accessibility_main_handler.cc", "webui/settings/accessibility_main_handler.cc",
......
// Copyright 2020 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/ui/webui/recent_site_settings_helper.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/site_settings_helper.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/permissions/permission_decision_auto_blocker.h"
namespace site_settings {
namespace {
// The priority for surfacing a permission to the user.
constexpr int GetPriorityForType(ContentSettingsType type) {
switch (type) {
case ContentSettingsType::GEOLOCATION:
return 0;
case ContentSettingsType::MEDIASTREAM_CAMERA:
return 1;
case ContentSettingsType::MEDIASTREAM_MIC:
return 2;
case ContentSettingsType::NOTIFICATIONS:
return 3;
case ContentSettingsType::BACKGROUND_SYNC:
return 4;
default:
// Every other |content_type| is considered of lower but equal priority
return 5;
}
}
// Return the most recent timestamp from the settings contained in a
// RecentSitePermissions struct. Returns base::Time() if no settings exist.
base::Time GetMostRecentTimestamp(const RecentSitePermissions& x) {
auto most_recent = base::Time();
for (const auto& setting : x.settings) {
if (setting.timestamp > most_recent)
most_recent = setting.timestamp;
}
return most_recent;
}
// Return a map keyed on URLs of all TimestampedSettings which currently apply
// to the provided profile for the provided content types.
std::map<GURL, std::vector<TimestampedSetting>> GetAllSettingsForProfile(
Profile* profile,
std::vector<ContentSettingsType> content_types) {
auto* auto_blocker =
PermissionDecisionAutoBlockerFactory::GetForProfile(profile);
HostContentSettingsMap* content_settings_map =
HostContentSettingsMapFactory::GetForProfile(profile);
std::map<GURL, std::vector<TimestampedSetting>> results;
for (auto content_type : content_types) {
auto exceptions_for_type = site_settings::GetSiteExceptionsForContentType(
content_settings_map, content_type);
for (const auto& e : exceptions_for_type) {
auto last_modified = content_settings_map->GetSettingLastModifiedDate(
e.primary_pattern, e.secondary_pattern, content_type);
if (last_modified.is_null()) {
continue;
}
GURL origin = GURL(e.primary_pattern.ToString()).GetOrigin();
results[origin].emplace_back(
last_modified, content_type,
content_settings::ValueToContentSetting(&e.setting_value),
site_settings::SiteSettingSource::kPreference);
}
// Get sites under embargo.
auto embargoed_sites = auto_blocker->GetEmbargoedOrigins(content_type);
for (auto& url : embargoed_sites) {
auto last_modified =
PermissionDecisionAutoBlockerFactory::GetForProfile(profile)
->GetEmbargoStartTime(url, content_type);
results[url.GetOrigin()].emplace_back(
last_modified, content_type, ContentSetting::CONTENT_SETTING_BLOCK,
site_settings::SiteSettingSource::kEmbargo);
}
}
// Keep only the first entry for any content type. This will be the enforced
// user set permission appropriate for the profile.
for (auto& source_settings : results) {
std::stable_sort(
source_settings.second.begin(), source_settings.second.end(),
[](const TimestampedSetting& x, const TimestampedSetting& y) {
return x.content_type < y.content_type;
});
source_settings.second.erase(
std::unique(
source_settings.second.begin(), source_settings.second.end(),
[](const TimestampedSetting& x, const TimestampedSetting& y) {
return x.content_type == y.content_type;
}),
source_settings.second.end());
}
return results;
}
} // namespace
TimestampedSetting::TimestampedSetting()
: timestamp(base::Time()),
content_type(ContentSettingsType::DEFAULT),
content_setting(ContentSetting::CONTENT_SETTING_DEFAULT),
setting_source(site_settings::SiteSettingSource::kDefault) {}
TimestampedSetting::TimestampedSetting(const TimestampedSetting& other) =
default;
TimestampedSetting::TimestampedSetting(
base::Time timestamp,
ContentSettingsType content_type,
ContentSetting content_setting,
site_settings::SiteSettingSource setting_source)
: timestamp(timestamp),
content_type(content_type),
content_setting(content_setting),
setting_source(setting_source) {}
TimestampedSetting::~TimestampedSetting() = default;
RecentSitePermissions::RecentSitePermissions()
: origin(GURL()),
incognito(false),
settings(std::vector<TimestampedSetting>()) {}
RecentSitePermissions::RecentSitePermissions(
const RecentSitePermissions& other) = default;
RecentSitePermissions::RecentSitePermissions(RecentSitePermissions&& other) =
default;
RecentSitePermissions::RecentSitePermissions(
GURL origin,
bool incognito,
std::vector<TimestampedSetting> settings)
: origin(origin), incognito(incognito), settings(settings) {}
RecentSitePermissions::~RecentSitePermissions() = default;
std::vector<RecentSitePermissions> GetRecentSitePermissions(
Profile* profile,
std::vector<ContentSettingsType> content_types,
size_t max_sources) {
std::map<GURL, std::vector<TimestampedSetting>> regular_settings =
GetAllSettingsForProfile(profile, content_types);
std::map<GURL, std::vector<TimestampedSetting>> incognito_settings;
if (profile->HasOffTheRecordProfile()) {
incognito_settings = GetAllSettingsForProfile(
profile->GetOffTheRecordProfile(), content_types);
// Remove all permission entries in the incognito map which also have
// an entry in the regular settings. This may result in an empty setting
// vector, in which case we remove the map entry.
// TODO(https://crbug.com/1049531): Make determining actual source of
// active permission simpler.
for (auto incognito_iter = incognito_settings.begin();
incognito_iter != incognito_settings.end();) {
auto regular_iter = regular_settings.find(incognito_iter->first);
if (regular_iter == regular_settings.end()) {
++incognito_iter;
continue;
}
// Define an arbitrary ordering on TimestampedSetting's so we can sort
// and make use of set difference
auto arbitrary_strict_weak_ordering = [](const TimestampedSetting& x,
const TimestampedSetting& y) {
return std::tie(x.timestamp, x.content_type, x.content_setting,
x.setting_source) <
std::tie(y.timestamp, y.content_type, y.content_setting,
y.setting_source);
};
std::vector<TimestampedSetting> incognito_only_settings;
std::sort(regular_iter->second.begin(), regular_iter->second.end(),
arbitrary_strict_weak_ordering);
std::sort(incognito_iter->second.begin(), incognito_iter->second.end(),
arbitrary_strict_weak_ordering);
std::set_difference(
incognito_iter->second.begin(), incognito_iter->second.end(),
regular_iter->second.begin(), regular_iter->second.end(),
std::back_inserter(incognito_only_settings),
arbitrary_strict_weak_ordering);
if (incognito_only_settings.empty()) {
incognito_iter = incognito_settings.erase(incognito_iter);
} else {
incognito_iter->second = incognito_only_settings;
++incognito_iter;
}
}
}
// Combine incognito and regular permissions and sort based on the most
// recent setting for each source.
std::vector<RecentSitePermissions> all_site_permissions;
for (auto& url_settings_pair : regular_settings) {
all_site_permissions.push_back({url_settings_pair.first,
false /*incognito*/,
std::move(url_settings_pair.second)});
}
for (auto& url_settings_pair : incognito_settings) {
all_site_permissions.push_back({url_settings_pair.first, true /*incognito*/,
std::move(url_settings_pair.second)});
}
std::sort(all_site_permissions.begin(), all_site_permissions.end(),
[](const RecentSitePermissions& x, const RecentSitePermissions& y) {
return GetMostRecentTimestamp(x) > GetMostRecentTimestamp(y);
});
// Sort source permissions based on their priority for surfacing to the user
for (auto& site_permissions : all_site_permissions) {
std::sort(site_permissions.settings.begin(),
site_permissions.settings.end(),
[](const TimestampedSetting& x, const TimestampedSetting& y) {
if (GetPriorityForType(x.content_type) !=
GetPriorityForType(y.content_type)) {
return GetPriorityForType(x.content_type) <
GetPriorityForType(y.content_type);
}
return x.timestamp > y.timestamp;
});
}
if (all_site_permissions.size() <= max_sources) {
return all_site_permissions;
}
// We only want to surface the recent permissions for sites. Explicitly no
// settings older than the newest setting for the (max_sources + 1)th source.
auto min_timestamp =
GetMostRecentTimestamp(all_site_permissions[max_sources]);
all_site_permissions.erase(all_site_permissions.begin() + max_sources,
all_site_permissions.end());
for (auto& site_permissions : all_site_permissions) {
site_permissions.settings.erase(
std::remove_if(site_permissions.settings.begin(),
site_permissions.settings.end(),
[min_timestamp](const TimestampedSetting& x) {
return x.timestamp < min_timestamp;
}),
site_permissions.settings.end());
}
return all_site_permissions;
}
} // namespace site_settings
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UI_WEBUI_RECENT_SITE_SETTINGS_HELPER_H_
#define CHROME_BROWSER_UI_WEBUI_RECENT_SITE_SETTINGS_HELPER_H_
#include <vector>
#include "base/time/time.h"
#include "chrome/browser/ui/webui/site_settings_helper.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "url/gurl.h"
class Profile;
namespace site_settings {
struct TimestampedSetting {
base::Time timestamp;
ContentSettingsType content_type;
ContentSetting content_setting;
site_settings::SiteSettingSource setting_source;
TimestampedSetting();
TimestampedSetting(const TimestampedSetting& other);
TimestampedSetting& operator=(const TimestampedSetting& other) = default;
TimestampedSetting(TimestampedSetting&& other) = default;
TimestampedSetting(base::Time timestamp,
ContentSettingsType content_type,
ContentSetting content_setting,
site_settings::SiteSettingSource setting_source);
~TimestampedSetting();
};
struct RecentSitePermissions {
GURL origin;
bool incognito;
std::vector<TimestampedSetting> settings;
RecentSitePermissions();
RecentSitePermissions(const RecentSitePermissions& other);
RecentSitePermissions& operator=(const RecentSitePermissions& other) =
default;
RecentSitePermissions(RecentSitePermissions&& other);
RecentSitePermissions(GURL origin,
bool incognito,
std::vector<TimestampedSetting> settings);
~RecentSitePermissions();
};
// Returns a list containing the most recent permission changes for the
// provided content types grouped by origin/profile (incognito, regular)
// combinations. Limited to |max_sources| origin/profile pairings and ordered
// from most recently adjusted site to least recently. Includes permissions
// changed by embargo, but not those changed by enterprise policy.
std::vector<RecentSitePermissions> GetRecentSitePermissions(
Profile* profile,
std::vector<ContentSettingsType> content_types,
size_t max_sources);
} // namespace site_settings
#endif // CHROME_BROWSER_UI_WEBUI_RECENT_SITE_SETTINGS_HELPER_H_
// Copyright 2020 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/ui/webui/recent_site_settings_helper.h"
#include "base/test/simple_test_clock.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/permissions/permission_decision_auto_blocker.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace site_settings {
namespace {
ContentSetting kBlocked = ContentSetting::CONTENT_SETTING_BLOCK;
ContentSetting kAllowed = ContentSetting::CONTENT_SETTING_ALLOW;
ContentSetting kDefault = ContentSetting::CONTENT_SETTING_DEFAULT;
site_settings::SiteSettingSource kEmbargo =
site_settings::SiteSettingSource::kEmbargo;
site_settings::SiteSettingSource kPreference =
site_settings::SiteSettingSource::kPreference;
ContentSettingsType kNotifications = ContentSettingsType::NOTIFICATIONS;
ContentSettingsType kPlugins = ContentSettingsType::PLUGINS;
ContentSettingsType kPopups = ContentSettingsType::POPUPS;
ContentSettingsType kLocation = ContentSettingsType::GEOLOCATION;
} // namespace
class RecentSiteSettingsHelperTest : public testing::Test {
protected:
TestingProfile* profile() { return &profile_; }
TestingProfile* incognito_profile() { return incognito_profile_; }
base::SimpleTestClock* clock() { return &clock_; }
void CreateIncognitoProfile() {
incognito_profile_ = TestingProfile::Builder().BuildIncognito(profile());
}
private:
content::BrowserTaskEnvironment task_environment_;
base::SimpleTestClock clock_;
TestingProfile profile_;
TestingProfile* incognito_profile_;
};
TEST_F(RecentSiteSettingsHelperTest, IncognitoPermissionTimestamps) {
// Confirm our expectation that content settings prefs copied to the incognito
// profile have a timestamp of base::Time().
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile());
const GURL url("http://example.com");
const ContentSettingsPattern primary_pattern =
ContentSettingsPattern::FromURLNoWildcard(url);
const ContentSettingsPattern wildcard_pattern =
ContentSettingsPattern::Wildcard();
map->SetContentSettingDefaultScope(url, url, kNotifications, std::string(),
kBlocked);
CreateIncognitoProfile();
HostContentSettingsMap* incognito_map =
HostContentSettingsMapFactory::GetForProfile(incognito_profile());
EXPECT_NE(base::Time(),
map->GetSettingLastModifiedDate(primary_pattern, wildcard_pattern,
kNotifications));
EXPECT_EQ(base::Time(),
incognito_map->GetSettingLastModifiedDate(
primary_pattern, wildcard_pattern, kNotifications));
}
TEST_F(RecentSiteSettingsHelperTest, CheckRecentSitePermissions) {
const GURL url1("https://example.com");
const GURL url2("http://example.com");
std::vector<ContentSettingsType> content_types = {kNotifications, kPlugins,
kPopups, kLocation};
clock()->SetNow(base::Time::Now());
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile());
map->SetClockForTesting(clock());
auto* auto_blocker =
PermissionDecisionAutoBlockerFactory::GetForProfile(profile());
auto_blocker->SetClockForTesting(clock());
// Check that with default permissions nothing is returned.
auto recent_permissions =
GetRecentSitePermissions(profile(), content_types, 10);
EXPECT_EQ(0UL, recent_permissions.size());
// Add two permissions for different urls via different mechanisms and
// confirm they're returned correctly. Ensure that simply creating an
// ingocnito profile does not change recent permissions.
for (int i = 0; i < 3; ++i) {
auto_blocker->RecordDismissAndEmbargo(url1, kNotifications, false);
}
clock()->Advance(base::TimeDelta::FromHours(2));
map->SetContentSettingDefaultScope(url2, url2, kPlugins, std::string(),
kAllowed);
CreateIncognitoProfile();
recent_permissions = GetRecentSitePermissions(profile(), content_types, 10);
{
EXPECT_EQ(2UL, recent_permissions.size());
EXPECT_EQ(1UL, recent_permissions[0].settings.size());
EXPECT_EQ(1UL, recent_permissions[1].settings.size());
EXPECT_EQ(url1.spec(), recent_permissions[1].origin);
EXPECT_EQ(url2.spec(), recent_permissions[0].origin);
auto url1_permissions = recent_permissions[1].settings;
auto url2_permissions = recent_permissions[0].settings;
EXPECT_EQ(kNotifications, url1_permissions[0].content_type);
EXPECT_EQ(kBlocked, url1_permissions[0].content_setting);
EXPECT_EQ(kEmbargo, url1_permissions[0].setting_source);
EXPECT_EQ(kPlugins, url2_permissions[0].content_type);
EXPECT_EQ(kAllowed, url2_permissions[0].content_setting);
EXPECT_EQ(kPreference, url2_permissions[0].setting_source);
}
// Ensure incognito generated permissions are separated correctly.
clock()->Advance(base::TimeDelta::FromHours(1));
HostContentSettingsMap* incognito_map =
HostContentSettingsMapFactory::GetForProfile(incognito_profile());
incognito_map->SetClockForTesting(clock());
incognito_map->SetContentSettingDefaultScope(url1, url1, kPlugins,
std::string(), kAllowed);
clock()->Advance(base::TimeDelta::FromHours(1));
permissions::PermissionDecisionAutoBlocker* incognito_auto_blocker =
PermissionDecisionAutoBlockerFactory::GetForProfile(incognito_profile());
incognito_auto_blocker->SetClockForTesting(clock());
for (int i = 0; i < 3; ++i) {
incognito_auto_blocker->RecordDismissAndEmbargo(url1, kNotifications,
false);
}
recent_permissions = GetRecentSitePermissions(profile(), content_types, 10);
{
EXPECT_EQ(3UL, recent_permissions.size());
EXPECT_EQ(2UL, recent_permissions[0].settings.size());
EXPECT_EQ(1UL, recent_permissions[1].settings.size());
EXPECT_EQ(1UL, recent_permissions[2].settings.size());
EXPECT_EQ(url1.spec(), recent_permissions[2].origin);
EXPECT_EQ(url2.spec(), recent_permissions[1].origin);
EXPECT_EQ(url1.spec(), recent_permissions[0].origin);
EXPECT_TRUE(recent_permissions[0].incognito);
EXPECT_FALSE(recent_permissions[1].incognito);
EXPECT_FALSE(recent_permissions[2].incognito);
auto incognito_url1_permissions = recent_permissions[0].settings;
EXPECT_EQ(kNotifications, incognito_url1_permissions[0].content_type);
EXPECT_EQ(kBlocked, incognito_url1_permissions[0].content_setting);
EXPECT_EQ(kEmbargo, incognito_url1_permissions[0].setting_source);
EXPECT_EQ(kPlugins, incognito_url1_permissions[1].content_type);
EXPECT_EQ(kAllowed, incognito_url1_permissions[1].content_setting);
EXPECT_EQ(kPreference, incognito_url1_permissions[1].setting_source);
}
// Test additional permissions are correctly compacted down to the provided
// maximum number of sources and that order of origins is based on the
// most recent permission for that source.
const GURL url3("https://example.com:8443");
clock()->Advance(base::TimeDelta::FromHours(1));
map->SetContentSettingDefaultScope(url1, url1, kPlugins, std::string(),
kBlocked);
clock()->Advance(base::TimeDelta::FromHours(1));
for (int i = 0; i < 4; ++i) {
auto_blocker->RecordIgnoreAndEmbargo(url3, kNotifications, false);
}
clock()->Advance(base::TimeDelta::FromHours(1));
map->SetContentSettingDefaultScope(url2, url2, kPopups, std::string(),
kAllowed);
clock()->Advance(base::TimeDelta::FromHours(1));
map->SetContentSettingDefaultScope(url3, url3, kPopups, std::string(),
kBlocked);
recent_permissions = GetRecentSitePermissions(profile(), content_types, 3);
{
EXPECT_EQ(3UL, recent_permissions.size());
EXPECT_EQ(2UL, recent_permissions[0].settings.size());
EXPECT_EQ(1UL, recent_permissions[1].settings.size());
EXPECT_EQ(1UL, recent_permissions[2].settings.size());
EXPECT_EQ(url3.spec(), recent_permissions[0].origin);
EXPECT_EQ(url2.spec(), recent_permissions[1].origin);
EXPECT_EQ(url1.spec(), recent_permissions[2].origin);
// The oldest record will those for the inconito URL1, they should have
// been removed.
EXPECT_FALSE(recent_permissions[2].incognito);
auto url1_permissions = recent_permissions[2].settings;
auto url2_permissions = recent_permissions[1].settings;
auto url3_permissions = recent_permissions[0].settings;
EXPECT_EQ(kPlugins, url1_permissions[0].content_type);
EXPECT_EQ(kBlocked, url1_permissions[0].content_setting);
EXPECT_EQ(kPreference, url1_permissions[0].setting_source);
EXPECT_EQ(kPopups, url2_permissions[0].content_type);
EXPECT_EQ(kAllowed, url2_permissions[0].content_setting);
EXPECT_EQ(kPreference, url2_permissions[0].setting_source);
EXPECT_EQ(kNotifications, url3_permissions[0].content_type);
EXPECT_EQ(kBlocked, url3_permissions[0].content_setting);
EXPECT_EQ(kEmbargo, url3_permissions[0].setting_source);
EXPECT_EQ(kPopups, url3_permissions[1].content_type);
EXPECT_EQ(kBlocked, url3_permissions[1].content_setting);
EXPECT_EQ(kPreference, url3_permissions[1].setting_source);
}
// Assign a new permission to a previously recorded site whose other
// permissions are too old and ensure only the recent permission is returned.
clock()->Advance(base::TimeDelta::FromHours(1));
incognito_map->SetContentSettingDefaultScope(url1, url1, kPopups,
std::string(), kBlocked);
recent_permissions = GetRecentSitePermissions(profile(), content_types, 3);
{
EXPECT_EQ(3UL, recent_permissions.size());
EXPECT_EQ(1UL, recent_permissions[0].settings.size());
EXPECT_EQ(url1.spec(), recent_permissions[0].origin);
EXPECT_TRUE(recent_permissions[0].incognito);
EXPECT_EQ(kPopups, recent_permissions[0].settings[0].content_type);
EXPECT_EQ(kBlocked, recent_permissions[0].settings[0].content_setting);
EXPECT_EQ(kPreference, recent_permissions[0].settings[0].setting_source);
}
// Reset a changed permission to default and confirm it does not appear as a
// recent permission change.
clock()->Advance(base::TimeDelta::FromHours(1));
map->SetContentSettingDefaultScope(url3, url3, kPopups, std::string(),
kDefault);
recent_permissions = GetRecentSitePermissions(profile(), content_types, 3);
{
EXPECT_EQ(3UL, recent_permissions.size());
EXPECT_EQ(1UL, recent_permissions[0].settings.size());
EXPECT_NE(url3.spec(), recent_permissions[0].origin);
}
// Expire all existing embargoes, then re-embargo a site with a single
// dismissal and confirm this is recorded as a new permission change. Also
// confirm that sources with no permission changes associated are not
// considered. I.e. url3 now has an expired embargo and a default setting. It
// should not be considered and should allow the plugins setting for url1 to
// be included as a recent change.
clock()->Advance(base::TimeDelta::FromDays(7));
auto_blocker->RecordDismissAndEmbargo(url1, kNotifications, false);
recent_permissions = GetRecentSitePermissions(profile(), content_types, 3);
{
EXPECT_EQ(3UL, recent_permissions.size());
EXPECT_EQ(2UL, recent_permissions[0].settings.size());
EXPECT_EQ(2UL, recent_permissions[1].settings.size());
EXPECT_EQ(2UL, recent_permissions[2].settings.size());
EXPECT_EQ(url1.spec(), recent_permissions[0].origin);
EXPECT_FALSE(recent_permissions[0].incognito);
auto url1_permissions = recent_permissions[0].settings;
EXPECT_EQ(kNotifications, url1_permissions[0].content_type);
EXPECT_EQ(kBlocked, url1_permissions[0].content_setting);
EXPECT_EQ(kEmbargo, url1_permissions[0].setting_source);
EXPECT_EQ(kPlugins, url1_permissions[1].content_type);
EXPECT_EQ(kBlocked, url1_permissions[1].content_setting);
EXPECT_EQ(kPreference, url1_permissions[1].setting_source);
}
// Confirm that powerful permissions are listed first, and that other
// permissions remain sorted by time.
clock()->Advance(base::TimeDelta::FromHours(1));
map->SetContentSettingDefaultScope(url1, url1, kPopups, std::string(),
kBlocked);
clock()->Advance(base::TimeDelta::FromHours(1));
map->SetContentSettingDefaultScope(url1, url1, kLocation, std::string(),
kAllowed);
clock()->Advance(base::TimeDelta::FromHours(1));
map->SetContentSettingDefaultScope(url1, url1, kPlugins, std::string(),
kAllowed);
recent_permissions = GetRecentSitePermissions(profile(), content_types, 3);
{
EXPECT_EQ(3UL, recent_permissions.size());
auto url1_permissions = recent_permissions[0].settings;
EXPECT_EQ(4UL, url1_permissions.size());
EXPECT_EQ(kLocation, url1_permissions[0].content_type);
EXPECT_EQ(kNotifications, url1_permissions[1].content_type);
EXPECT_EQ(kPlugins, url1_permissions[2].content_type);
EXPECT_EQ(kPopups, url1_permissions[3].content_type);
}
// Check that adding a conflicting permission to the regular profile after
// the incognito profile has been created returns correctly for each profile.
const GURL url4("http://example.com:8443");
clock()->Advance(base::TimeDelta::FromHours(1));
incognito_map->SetContentSettingDefaultScope(url4, url4, kLocation,
std::string(), kAllowed);
clock()->Advance(base::TimeDelta::FromHours(1));
map->SetContentSettingDefaultScope(url4, url4, kLocation, std::string(),
kBlocked);
recent_permissions = GetRecentSitePermissions(profile(), content_types, 3);
{
EXPECT_EQ(3UL, recent_permissions.size());
EXPECT_EQ(url4.spec(), recent_permissions[0].origin);
EXPECT_EQ(url4.spec(), recent_permissions[1].origin);
EXPECT_EQ(1UL, recent_permissions[0].settings.size());
EXPECT_EQ(1UL, recent_permissions[1].settings.size());
EXPECT_FALSE(recent_permissions[0].incognito);
EXPECT_TRUE(recent_permissions[1].incognito);
EXPECT_EQ(kLocation, recent_permissions[0].settings[0].content_type);
EXPECT_EQ(kLocation, recent_permissions[1].settings[0].content_type);
EXPECT_EQ(kBlocked, recent_permissions[0].settings[0].content_setting);
EXPECT_EQ(kAllowed, recent_permissions[1].settings[0].content_setting);
}
// Check that resetting the permission to default in the regular profile
// does not affect the permission in the incognito profile.
clock()->Advance(base::TimeDelta::FromHours(1));
map->SetContentSettingDefaultScope(url4, url4, kLocation, std::string(),
kDefault);
recent_permissions = GetRecentSitePermissions(profile(), content_types, 3);
{
EXPECT_EQ(3UL, recent_permissions.size());
EXPECT_EQ(url4.spec(), recent_permissions[0].origin);
EXPECT_EQ(1UL, recent_permissions[0].settings.size());
EXPECT_TRUE(recent_permissions[0].incognito);
EXPECT_EQ(kLocation, recent_permissions[0].settings[0].content_type);
EXPECT_EQ(kAllowed, recent_permissions[0].settings[0].content_setting);
}
}
} // namespace site_settings
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/page_info/page_info_infobar_delegate.h" #include "chrome/browser/ui/page_info/page_info_infobar_delegate.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/recent_site_settings_helper.h"
#include "chrome/browser/ui/webui/site_settings_helper.h" #include "chrome/browser/ui/webui/site_settings_helper.h"
#include "chrome/browser/usb/usb_chooser_context.h" #include "chrome/browser/usb/usb_chooser_context.h"
#include "chrome/browser/usb/usb_chooser_context_factory.h" #include "chrome/browser/usb/usb_chooser_context_factory.h"
...@@ -158,21 +159,6 @@ base::flat_set<web_app::AppId> GetInstalledApps( ...@@ -158,21 +159,6 @@ base::flat_set<web_app::AppId> GetInstalledApps(
return installed; return installed;
} }
// Whether |pattern| applies to a single origin.
bool PatternAppliesToSingleOrigin(const ContentSettingPatternSource& pattern) {
const GURL url(pattern.primary_pattern.ToString());
// Default settings and other patterns apply to multiple origins.
if (url::Origin::Create(url).opaque())
return false;
// Embedded content settings only when |url| is embedded in another origin, so
// ignore non-wildcard secondary patterns that are different to the primary.
if (pattern.primary_pattern != pattern.secondary_pattern &&
pattern.secondary_pattern != ContentSettingsPattern::Wildcard()) {
return false;
}
return true;
}
// Groups |url| into sets of eTLD+1s in |site_group_map|, assuming |url| is an // Groups |url| into sets of eTLD+1s in |site_group_map|, assuming |url| is an
// origin. // origin.
// There are three cases: // There are three cases:
...@@ -367,6 +353,10 @@ void SiteSettingsHandler::RegisterMessages() { ...@@ -367,6 +353,10 @@ void SiteSettingsHandler::RegisterMessages() {
"getAllSites", "getAllSites",
base::BindRepeating(&SiteSettingsHandler::HandleGetAllSites, base::BindRepeating(&SiteSettingsHandler::HandleGetAllSites,
base::Unretained(this))); base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getRecentSitePermissions",
base::BindRepeating(&SiteSettingsHandler::HandleGetRecentSitePermissions,
base::Unretained(this)));
web_ui()->RegisterMessageCallback( web_ui()->RegisterMessageCallback(
"getFormattedBytes", "getFormattedBytes",
base::BindRepeating(&SiteSettingsHandler::HandleGetFormattedBytes, base::BindRepeating(&SiteSettingsHandler::HandleGetFormattedBytes,
...@@ -674,22 +664,14 @@ void SiteSettingsHandler::HandleGetDefaultValueForContentType( ...@@ -674,22 +664,14 @@ void SiteSettingsHandler::HandleGetDefaultValueForContentType(
void SiteSettingsHandler::HandleGetAllSites(const base::ListValue* args) { void SiteSettingsHandler::HandleGetAllSites(const base::ListValue* args) {
AllowJavascript(); AllowJavascript();
CHECK_EQ(2U, args->GetSize()); CHECK_EQ(2U, args->GetList().size());
const base::Value* callback_id; std::string callback_id = args->GetList()[0].GetString();
CHECK(args->Get(0, &callback_id)); auto types = args->GetList()[1].GetList();
const base::ListValue* types;
CHECK(args->GetList(1, &types));
all_sites_map_.clear(); all_sites_map_.clear();
origin_permission_set_.clear(); origin_permission_set_.clear();
// Convert |types| to a list of ContentSettingsTypes.
std::vector<ContentSettingsType> content_types; auto content_types = site_settings::ContentSettingsTypesFromGroupNames(types);
for (size_t i = 0; i < types->GetSize(); ++i) {
std::string type;
types->GetString(i, &type);
content_types.push_back(
site_settings::ContentSettingsTypeFromGroupName(type));
}
// Incognito contains incognito content settings plus non-incognito content // Incognito contains incognito content settings plus non-incognito content
// settings. Thus if it exists, just get exceptions for the incognito profile. // settings. Thus if it exists, just get exceptions for the incognito profile.
...@@ -712,17 +694,14 @@ void SiteSettingsHandler::HandleGetAllSites(const base::ListValue* args) { ...@@ -712,17 +694,14 @@ void SiteSettingsHandler::HandleGetAllSites(const base::ListValue* args) {
origin_permission_set_.insert(url.spec()); origin_permission_set_.insert(url.spec());
} }
// Convert |types| to a list of ContentSettingsTypes. // Get permission exceptions which apply to a single site
for (ContentSettingsType content_type : content_types) { for (auto content_type : content_types) {
ContentSettingsForOneType entries; auto exceptions =
map->GetSettingsForOneType(content_type, std::string(), &entries); site_settings::GetSiteExceptionsForContentType(map, content_type);
for (const ContentSettingPatternSource& e : entries) { for (const auto& e : exceptions) {
if (PatternAppliesToSingleOrigin(e)) { GURL url = GURL(e.primary_pattern.ToString());
CreateOrAppendSiteGroupEntry(&all_sites_map_, CreateOrAppendSiteGroupEntry(&all_sites_map_, url);
GURL(e.primary_pattern.ToString())); origin_permission_set_.insert(url.spec());
origin_permission_set_.insert(
GURL(e.primary_pattern.ToString()).spec());
}
} }
} }
...@@ -744,7 +723,54 @@ void SiteSettingsHandler::HandleGetAllSites(const base::ListValue* args) { ...@@ -744,7 +723,54 @@ void SiteSettingsHandler::HandleGetAllSites(const base::ListValue* args) {
send_sites_list_ = true; send_sites_list_ = true;
ResolveJavascriptCallback(*callback_id, result); ResolveJavascriptCallback(base::Value(callback_id), result);
}
void SiteSettingsHandler::HandleGetRecentSitePermissions(
const base::ListValue* args) {
AllowJavascript();
CHECK_EQ(3U, args->GetList().size());
std::string callback_id = args->GetList()[0].GetString();
auto types = args->GetList()[1].GetList();
size_t max_sources = base::checked_cast<size_t>(args->GetList()[2].GetInt());
auto content_types = site_settings::ContentSettingsTypesFromGroupNames(types);
auto recent_site_permissions = site_settings::GetRecentSitePermissions(
profile_, content_types, max_sources);
// Convert groups of TimestampedPermissions for consumption by JS
base::Value result(base::Value::Type::LIST);
for (const auto& site_permissions : recent_site_permissions) {
DCHECK(!site_permissions.settings.empty());
base::Value recent_site(base::Value::Type::DICTIONARY);
recent_site.SetKey(site_settings::kOrigin,
base::Value(site_permissions.origin.spec()));
recent_site.SetKey(site_settings::kIncognito,
base::Value(site_permissions.incognito));
base::Value permissions_list(base::Value::Type::LIST);
for (const auto& p : site_permissions.settings) {
base::Value recent_permission(base::Value::Type::DICTIONARY);
recent_permission.SetKey(
site_settings::kType,
base::Value(
site_settings::ContentSettingsTypeToGroupName(p.content_type)));
recent_permission.SetKey(
site_settings::kSetting,
base::Value(
content_settings::ContentSettingToString(p.content_setting)));
recent_permission.SetKey(
site_settings::kSource,
base::Value(
site_settings::SiteSettingSourceToString(p.setting_source)));
permissions_list.Append(std::move(recent_permission));
}
recent_site.SetKey(site_settings::kRecentPermissions,
std::move(permissions_list));
result.Append(std::move(recent_site));
}
ResolveJavascriptCallback(base::Value(callback_id), result);
} }
base::Value SiteSettingsHandler::PopulateCookiesAndUsageData(Profile* profile) { base::Value SiteSettingsHandler::PopulateCookiesAndUsageData(Profile* profile) {
...@@ -774,13 +800,14 @@ base::Value SiteSettingsHandler::PopulateCookiesAndUsageData(Profile* profile) { ...@@ -774,13 +800,14 @@ base::Value SiteSettingsHandler::PopulateCookiesAndUsageData(Profile* profile) {
const auto& size_info_it = origin_size_map.find(origin); const auto& size_info_it = origin_size_map.find(origin);
if (size_info_it != origin_size_map.end()) if (size_info_it != origin_size_map.end())
origin_info.SetKey("usage", base::Value(double(size_info_it->second))); origin_info.SetKey("usage", base::Value(double(size_info_it->second)));
GURL origin_url(origin);
const auto& origin_cookie_num_it = const auto& origin_cookie_num_it =
origin_cookie_map.find(GURL(origin).host()); origin_cookie_map.find(origin_url.host());
if (origin_cookie_num_it != origin_cookie_map.end()) { if (origin_cookie_num_it != origin_cookie_map.end()) {
origin_info.SetKey(kNumCookies, origin_info.SetKey(kNumCookies,
base::Value(origin_cookie_num_it->second)); base::Value(origin_cookie_num_it->second));
// Add cookies numbers for origins that isn't an eTLD+1. // Add cookies numbers for origins that isn't an eTLD+1.
if (GURL(origin).host() != etld_plus1) if (origin_url.host() != etld_plus1)
cookie_num += origin_cookie_num_it->second; cookie_num += origin_cookie_num_it->second;
} }
} }
......
...@@ -110,6 +110,7 @@ class SiteSettingsHandler : public SettingsPageUIHandler, ...@@ -110,6 +110,7 @@ class SiteSettingsHandler : public SettingsPageUIHandler,
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExceptionHelpers); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExceptionHelpers);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExtensionDisplayName); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExtensionDisplayName);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAllSites); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAllSites);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetRecentSitePermissions);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, OnStorageFetched); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, OnStorageFetched);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAndSetDefault); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAndSetDefault);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAndSetForInvalidURLs); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAndSetForInvalidURLs);
...@@ -163,6 +164,13 @@ class SiteSettingsHandler : public SettingsPageUIHandler, ...@@ -163,6 +164,13 @@ class SiteSettingsHandler : public SettingsPageUIHandler,
// the front end when fetching finished. // the front end when fetching finished.
void HandleGetAllSites(const base::ListValue* args); void HandleGetAllSites(const base::ListValue* args);
// Returns a list containing the most recent permission changes for the
// provided content types grouped by origin/profile (incognito, regular)
// combinations, limited to N origin/profile pairings. This includes
// permission changes made by embargo, but does not include permissions
// enforced via policy.
void HandleGetRecentSitePermissions(const base::ListValue* args);
// Called when the list of origins using storage has been fetched, and sends // Called when the list of origins using storage has been fetched, and sends
// this list back to the front end. // this list back to the front end.
void OnStorageFetched(); void OnStorageFetched();
......
...@@ -719,6 +719,131 @@ TEST_F(SiteSettingsHandlerTest, MAYBE_GetAllSites) { ...@@ -719,6 +719,131 @@ TEST_F(SiteSettingsHandlerTest, MAYBE_GetAllSites) {
run_loop.RunUntilIdle(); run_loop.RunUntilIdle();
} }
TEST_F(SiteSettingsHandlerTest, GetRecentSitePermissions) {
// Constants used only in this test.
std::string kAllowed = content_settings::ContentSettingToString(
ContentSetting::CONTENT_SETTING_ALLOW);
std::string kBlocked = content_settings::ContentSettingToString(
ContentSetting::CONTENT_SETTING_BLOCK);
std::string kEmbargo =
SiteSettingSourceToString(site_settings::SiteSettingSource::kEmbargo);
std::string kPreference =
SiteSettingSourceToString(site_settings::SiteSettingSource::kPreference);
base::ListValue get_recent_permissions_args;
get_recent_permissions_args.AppendString(kCallbackId);
base::Value category_list(base::Value::Type::LIST);
category_list.Append(kNotifications);
category_list.Append(kFlash);
get_recent_permissions_args.Append(std::move(category_list));
get_recent_permissions_args.Append(3);
// Configure prefs and auto blocker with a controllable clock.
base::SimpleTestClock clock;
clock.SetNow(base::Time::Now());
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile());
map->SetClockForTesting(&clock);
permissions::PermissionDecisionAutoBlocker* auto_blocker =
PermissionDecisionAutoBlockerFactory::GetForProfile(profile());
auto_blocker->SetClockForTesting(&clock);
clock.Advance(base::TimeDelta::FromHours(1));
// Test recent permissions is empty when there are no preferences.
handler()->HandleGetRecentSitePermissions(&get_recent_permissions_args);
EXPECT_EQ(1U, web_ui()->call_data().size());
{
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());
base::Value::ConstListView recent_permissions = data.arg3()->GetList();
EXPECT_EQ(0UL, recent_permissions.size());
}
// Add numerous permissions from different sources and confirm that the recent
// permissions are correctly transformed for usage by JS.
const GURL url1("https://example.com");
const GURL url2("http://example.com");
for (int i = 0; i < 3; ++i)
auto_blocker->RecordDismissAndEmbargo(
url1, ContentSettingsType::NOTIFICATIONS, false);
clock.Advance(base::TimeDelta::FromHours(2));
map->SetContentSettingDefaultScope(url2, url2, ContentSettingsType::PLUGINS,
std::string(), CONTENT_SETTING_ALLOW);
clock.Advance(base::TimeDelta::FromHours(1));
CreateIncognitoProfile();
HostContentSettingsMap* incognito_map =
HostContentSettingsMapFactory::GetForProfile(incognito_profile());
incognito_map->SetClockForTesting(&clock);
incognito_map->SetContentSettingDefaultScope(
url1, url1, ContentSettingsType::PLUGINS, std::string(),
CONTENT_SETTING_ALLOW);
clock.Advance(base::TimeDelta::FromHours(1));
permissions::PermissionDecisionAutoBlocker* incognito_auto_blocker =
PermissionDecisionAutoBlockerFactory::GetForProfile(incognito_profile());
incognito_auto_blocker->SetClockForTesting(&clock);
for (int i = 0; i < 3; ++i)
incognito_auto_blocker->RecordDismissAndEmbargo(
url1, ContentSettingsType::NOTIFICATIONS, false);
handler()->HandleGetRecentSitePermissions(&get_recent_permissions_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());
base::Value::ConstListView recent_permissions = data.arg3()->GetList();
EXPECT_EQ(3UL, recent_permissions.size());
EXPECT_EQ(url1.spec(),
recent_permissions[2].FindKey("origin")->GetString());
EXPECT_EQ(url2.spec(),
recent_permissions[1].FindKey("origin")->GetString());
EXPECT_EQ(url1.spec(),
recent_permissions[0].FindKey("origin")->GetString());
EXPECT_TRUE(recent_permissions[0].FindKey("incognito")->GetBool());
EXPECT_FALSE(recent_permissions[1].FindKey("incognito")->GetBool());
EXPECT_FALSE(recent_permissions[2].FindKey("incognito")->GetBool());
base::Value::ConstListView incognito_url1_permissions =
recent_permissions[0].FindKey("recentPermissions")->GetList();
base::Value::ConstListView url1_permissions =
recent_permissions[2].FindKey("recentPermissions")->GetList();
base::Value::ConstListView url2_permissions =
recent_permissions[1].FindKey("recentPermissions")->GetList();
EXPECT_EQ(2UL, incognito_url1_permissions.size());
EXPECT_EQ(kNotifications,
incognito_url1_permissions[0].FindKey("type")->GetString());
EXPECT_EQ(kBlocked,
incognito_url1_permissions[0].FindKey("setting")->GetString());
EXPECT_EQ(kEmbargo,
incognito_url1_permissions[0].FindKey("source")->GetString());
EXPECT_EQ(kFlash,
incognito_url1_permissions[1].FindKey("type")->GetString());
EXPECT_EQ(kAllowed,
incognito_url1_permissions[1].FindKey("setting")->GetString());
EXPECT_EQ(kPreference,
incognito_url1_permissions[1].FindKey("source")->GetString());
EXPECT_EQ(kNotifications, url1_permissions[0].FindKey("type")->GetString());
EXPECT_EQ(kBlocked, url1_permissions[0].FindKey("setting")->GetString());
EXPECT_EQ(kEmbargo, url1_permissions[0].FindKey("source")->GetString());
EXPECT_EQ(kFlash, url2_permissions[0].FindKey("type")->GetString());
EXPECT_EQ(kAllowed, url2_permissions[0].FindKey("setting")->GetString());
EXPECT_EQ(kPreference, url2_permissions[0].FindKey("source")->GetString());
}
}
TEST_F(SiteSettingsHandlerTest, OnStorageFetched) { TEST_F(SiteSettingsHandlerTest, OnStorageFetched) {
SetUpCookiesTreeModel(); SetUpCookiesTreeModel();
......
...@@ -231,6 +231,21 @@ SiteSettingSource CalculateSiteSettingSource( ...@@ -231,6 +231,21 @@ SiteSettingSource CalculateSiteSettingSource(
return SiteSettingSource::kPreference; return SiteSettingSource::kPreference;
} }
// Whether |pattern| applies to a single origin.
bool PatternAppliesToSingleOrigin(const ContentSettingPatternSource& pattern) {
const GURL url(pattern.primary_pattern.ToString());
// Default settings and other patterns apply to multiple origins.
if (url::Origin::Create(url).opaque())
return false;
// Embedded content settings only when |url| is embedded in another origin, so
// ignore non-wildcard secondary patterns that are different to the primary.
if (pattern.primary_pattern != pattern.secondary_pattern &&
pattern.secondary_pattern != ContentSettingsPattern::Wildcard()) {
return false;
}
return true;
}
// Retrieves the source of a chooser exception as a string. This method uses the // Retrieves the source of a chooser exception as a string. This method uses the
// CalculateSiteSettingSource method above to calculate the correct string to // CalculateSiteSettingSource method above to calculate the correct string to
// use. // use.
...@@ -317,6 +332,18 @@ std::string ContentSettingsTypeToGroupName(ContentSettingsType type) { ...@@ -317,6 +332,18 @@ std::string ContentSettingsTypeToGroupName(ContentSettingsType type) {
return std::string(); return std::string();
} }
std::vector<ContentSettingsType> ContentSettingsTypesFromGroupNames(
const base::Value::ConstListView types) {
std::vector<ContentSettingsType> content_types;
content_types.reserve(types.size());
for (const auto& value : types) {
const auto& type = value.GetString();
content_types.push_back(
site_settings::ContentSettingsTypeFromGroupName(type));
}
return content_types;
}
std::string SiteSettingSourceToString(const SiteSettingSource source) { std::string SiteSettingSourceToString(const SiteSettingSource source) {
return kSiteSettingSourceStringMapping[static_cast<int>(source)].source_str; return kSiteSettingSourceStringMapping[static_cast<int>(source)].source_str;
} }
...@@ -568,6 +595,19 @@ ContentSetting GetContentSettingForOrigin( ...@@ -568,6 +595,19 @@ ContentSetting GetContentSettingForOrigin(
return result.content_setting; return result.content_setting;
} }
std::vector<ContentSettingPatternSource> GetSiteExceptionsForContentType(
HostContentSettingsMap* map,
ContentSettingsType content_type) {
ContentSettingsForOneType entries;
map->GetSettingsForOneType(content_type, std::string(), &entries);
entries.erase(std::remove_if(entries.begin(), entries.end(),
[](const ContentSettingPatternSource& e) {
return !PatternAppliesToSingleOrigin(e);
}),
entries.end());
return entries;
}
void GetPolicyAllowedUrls( void GetPolicyAllowedUrls(
ContentSettingsType type, ContentSettingsType type,
std::vector<std::unique_ptr<base::DictionaryValue>>* exceptions, std::vector<std::unique_ptr<base::DictionaryValue>>* exceptions,
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/values.h"
#include "components/content_settings/core/common/content_settings.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_pattern.h"
#include "components/content_settings/core/common/content_settings_types.h" #include "components/content_settings/core/common/content_settings_types.h"
...@@ -22,12 +23,6 @@ class ChooserContextBase; ...@@ -22,12 +23,6 @@ class ChooserContextBase;
class HostContentSettingsMap; class HostContentSettingsMap;
class Profile; class Profile;
namespace base {
class DictionaryValue;
class ListValue;
class Value;
} // namespace base
namespace extensions { namespace extensions {
class ExtensionRegistry; class ExtensionRegistry;
} }
...@@ -63,9 +58,11 @@ constexpr char kIncognito[] = "incognito"; ...@@ -63,9 +58,11 @@ constexpr char kIncognito[] = "incognito";
constexpr char kObject[] = "object"; constexpr char kObject[] = "object";
constexpr char kOrigin[] = "origin"; constexpr char kOrigin[] = "origin";
constexpr char kOriginForFavicon[] = "originForFavicon"; constexpr char kOriginForFavicon[] = "originForFavicon";
constexpr char kRecentPermissions[] = "recentPermissions";
constexpr char kSetting[] = "setting"; constexpr char kSetting[] = "setting";
constexpr char kSites[] = "sites"; constexpr char kSites[] = "sites";
constexpr char kSource[] = "source"; constexpr char kSource[] = "source";
constexpr char kType[] = "type";
enum class SiteSettingSource { enum class SiteSettingSource {
kAdsFilterBlacklist, kAdsFilterBlacklist,
...@@ -87,6 +84,10 @@ bool HasRegisteredGroupName(ContentSettingsType type); ...@@ -87,6 +84,10 @@ bool HasRegisteredGroupName(ContentSettingsType type);
ContentSettingsType ContentSettingsTypeFromGroupName(const std::string& name); ContentSettingsType ContentSettingsTypeFromGroupName(const std::string& name);
std::string ContentSettingsTypeToGroupName(ContentSettingsType type); std::string ContentSettingsTypeToGroupName(ContentSettingsType type);
// Converts a ListValue of group names to a list of ContentSettingsTypes
std::vector<ContentSettingsType> ContentSettingsTypesFromGroupNames(
const base::Value::ConstListView types);
// Converts a SiteSettingSource to its string identifier. // Converts a SiteSettingSource to its string identifier.
std::string SiteSettingSourceToString(const SiteSettingSource source); std::string SiteSettingSourceToString(const SiteSettingSource source);
...@@ -145,6 +146,11 @@ void GetPolicyAllowedUrls( ...@@ -145,6 +146,11 @@ void GetPolicyAllowedUrls(
content::WebUI* web_ui, content::WebUI* web_ui,
bool incognito); bool incognito);
// Returns all site permission exceptions for a given content type
std::vector<ContentSettingPatternSource> GetSiteExceptionsForContentType(
HostContentSettingsMap* map,
ContentSettingsType content_type);
// This struct facilitates lookup of a chooser context factory function by name // This struct facilitates lookup of a chooser context factory function by name
// for a given content settings type and is declared early so that it can used // for a given content settings type and is declared early so that it can used
// by functions below. // by functions below.
......
...@@ -4157,6 +4157,7 @@ test("unit_tests") { ...@@ -4157,6 +4157,7 @@ test("unit_tests") {
"../browser/ui/webui/history/browsing_history_handler_unittest.cc", "../browser/ui/webui/history/browsing_history_handler_unittest.cc",
"../browser/ui/webui/managed_ui_handler_unittest.cc", "../browser/ui/webui/managed_ui_handler_unittest.cc",
"../browser/ui/webui/management_ui_handler_unittest.cc", "../browser/ui/webui/management_ui_handler_unittest.cc",
"../browser/ui/webui/recent_site_settings_helper_unittest.cc",
"../browser/ui/webui/settings/downloads_handler_unittest.cc", "../browser/ui/webui/settings/downloads_handler_unittest.cc",
"../browser/ui/webui/settings/metrics_reporting_handler_unittest.cc", "../browser/ui/webui/settings/metrics_reporting_handler_unittest.cc",
"../browser/ui/webui/settings/on_startup_handler_unittest.cc", "../browser/ui/webui/settings/on_startup_handler_unittest.cc",
......
...@@ -20,8 +20,13 @@ class GURL; ...@@ -20,8 +20,13 @@ class GURL;
namespace settings { namespace settings {
FORWARD_DECLARE_TEST(SiteSettingsHandlerTest, GetAllSites); FORWARD_DECLARE_TEST(SiteSettingsHandlerTest, GetAllSites);
FORWARD_DECLARE_TEST(SiteSettingsHandlerTest, GetRecentSitePermissions);
} // namespace settings } // namespace settings
namespace site_settings {
FORWARD_DECLARE_TEST(RecentSiteSettingsHelperTest, CheckRecentSitePermissions);
} // namespace site_settings
namespace permissions { namespace permissions {
// The PermissionDecisionAutoBlocker decides whether or not a given origin // The PermissionDecisionAutoBlocker decides whether or not a given origin
...@@ -111,7 +116,11 @@ class PermissionDecisionAutoBlocker : public KeyedService { ...@@ -111,7 +116,11 @@ class PermissionDecisionAutoBlocker : public KeyedService {
private: private:
friend class PermissionDecisionAutoBlockerUnitTest; friend class PermissionDecisionAutoBlockerUnitTest;
FRIEND_TEST_ALL_PREFIXES(site_settings::RecentSiteSettingsHelperTest,
CheckRecentSitePermissions);
FRIEND_TEST_ALL_PREFIXES(settings::SiteSettingsHandlerTest, GetAllSites); FRIEND_TEST_ALL_PREFIXES(settings::SiteSettingsHandlerTest, GetAllSites);
FRIEND_TEST_ALL_PREFIXES(settings::SiteSettingsHandlerTest,
GetRecentSitePermissions);
void PlaceUnderEmbargo(const GURL& request_origin, void PlaceUnderEmbargo(const GURL& request_origin,
ContentSettingsType permission, ContentSettingsType permission,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment