blob: 6b11de46fbecda147118dfb652f6abf653f6c725 [file] [log] [blame]
// Copyright 2016 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/settings/site_settings_helper.h"
#include "base/bind_helpers.h"
#include "base/guid.h"
#include "base/json/json_reader.h"
#include "base/strings/utf_string_conversions.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/usb/usb_chooser_context.h"
#include "chrome/browser/usb/usb_chooser_context_factory.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_utils.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/permissions/chooser_context_base.h"
#include "components/permissions/permission_decision_auto_blocker.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/extension_registry.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/device/public/cpp/test/fake_usb_device_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace site_settings {
namespace {
constexpr ContentSettingsType kContentType = ContentSettingsType::GEOLOCATION;
constexpr ContentSettingsType kContentTypeNotifications =
ContentSettingsType::NOTIFICATIONS;
constexpr ContentSettingsType kContentTypeCookies =
ContentSettingsType::COOKIES;
}
class SiteSettingsHelperTest : public testing::Test {
public:
void VerifySetting(const base::ListValue& exceptions,
int index,
const std::string& pattern,
const std::string& pattern_display_name,
const ContentSetting setting) {
const base::DictionaryValue* dict;
exceptions.GetDictionary(index, &dict);
std::string actual_pattern;
dict->GetString("origin", &actual_pattern);
EXPECT_EQ(pattern, actual_pattern);
std::string actual_display_name;
dict->GetString(kDisplayName, &actual_display_name);
EXPECT_EQ(pattern_display_name, actual_display_name);
std::string actual_setting;
dict->GetString(kSetting, &actual_setting);
EXPECT_EQ(content_settings::ContentSettingToString(setting),
actual_setting);
}
void AddSetting(HostContentSettingsMap* map,
const std::string& pattern,
ContentSetting setting) {
map->SetContentSettingCustomScope(
ContentSettingsPattern::FromString(pattern),
ContentSettingsPattern::Wildcard(), kContentType, std::string(),
setting);
}
private:
content::BrowserTaskEnvironment task_environment_;
};
TEST_F(SiteSettingsHelperTest, ExceptionListWithEmbargoedAndBlockedOrigins) {
TestingProfile profile;
constexpr char kOriginToEmbargo[] = "https://embargoed.co.uk:443";
auto* auto_blocker =
PermissionDecisionAutoBlockerFactory::GetForProfile(&profile);
for (size_t i = 0; i < 3; ++i) {
auto_blocker->RecordDismissAndEmbargo(GURL(kOriginToEmbargo),
kContentTypeNotifications, false);
}
constexpr char kOriginToBlock[] = "https://www.blocked.com:443";
auto* map = HostContentSettingsMapFactory::GetForProfile(&profile);
map->SetContentSettingDefaultScope(GURL(kOriginToBlock), GURL(kOriginToBlock),
kContentTypeNotifications, std::string(),
CONTENT_SETTING_BLOCK);
base::ListValue exceptions;
site_settings::GetExceptionsForContentType(kContentTypeNotifications,
&profile,
/*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/false, &exceptions);
// |exceptions| size should be 2. One blocked and one embargoed origins.
ASSERT_EQ(2U, exceptions.GetSize());
base::Value* value = nullptr;
// Get last added origin.
exceptions.Get(0, &value);
base::Value* is_embargoed = value->FindKey(site_settings::kIsEmbargoed);
ASSERT_NE(nullptr, is_embargoed);
// Last added origin is blocked, |embargo| key should be false.
EXPECT_FALSE(is_embargoed->GetBool());
// Get embargoed origin.
exceptions.Get(1, &value);
is_embargoed = value->FindKey(site_settings::kIsEmbargoed);
ASSERT_NE(nullptr, is_embargoed);
EXPECT_TRUE(is_embargoed->GetBool());
}
TEST_F(SiteSettingsHelperTest, ExceptionListShowsIncognitoEmbargoed) {
TestingProfile profile;
constexpr char kOriginToBlock[] = "https://www.blocked.com:443";
constexpr char kOriginToEmbargo[] = "https://embargoed.co.uk:443";
constexpr char kOriginToEmbargoIncognito[] =
"https://embargoed.incognito.co.uk:443";
// Add an origin under embargo for non incognito profile.
{
auto* auto_blocker =
PermissionDecisionAutoBlockerFactory::GetForProfile(&profile);
for (size_t i = 0; i < 3; ++i) {
auto_blocker->RecordDismissAndEmbargo(GURL(kOriginToEmbargo),
kContentTypeNotifications, false);
}
// Check that origin is under embargo.
ASSERT_EQ(CONTENT_SETTING_BLOCK,
auto_blocker
->GetEmbargoResult(GURL(kOriginToEmbargo),
kContentTypeNotifications)
.content_setting);
}
// Check there is 1 embargoed origin for a non-incognito profile.
{
base::ListValue exceptions;
site_settings::GetExceptionsForContentType(
kContentTypeNotifications, &profile, /*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/false, &exceptions);
ASSERT_EQ(1U, exceptions.GetSize());
}
TestingProfile* incognito_profile =
TestingProfile::Builder().BuildIncognito(&profile);
// Check there are no blocked origins for an incognito profile.
{
base::ListValue exceptions;
site_settings::GetExceptionsForContentType(kContentTypeNotifications,
incognito_profile,
/*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/true, &exceptions);
ASSERT_EQ(0U, exceptions.GetSize());
}
{
auto* incognito_map =
HostContentSettingsMapFactory::GetForProfile(incognito_profile);
incognito_map->SetContentSettingDefaultScope(
GURL(kOriginToBlock), GURL(kOriginToBlock), kContentTypeNotifications,
std::string(), CONTENT_SETTING_BLOCK);
}
// Check there is only 1 blocked origin for an incognito profile.
{
base::ListValue exceptions;
site_settings::GetExceptionsForContentType(kContentTypeNotifications,
incognito_profile,
/*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/true, &exceptions);
// The exceptions size should be 1 because previously embargoed origin
// was for a non-incognito profile.
ASSERT_EQ(1U, exceptions.GetSize());
}
// Add an origin under embargo for incognito profile.
{
auto* incognito_auto_blocker =
PermissionDecisionAutoBlockerFactory::GetForProfile(incognito_profile);
for (size_t i = 0; i < 3; ++i) {
incognito_auto_blocker->RecordDismissAndEmbargo(
GURL(kOriginToEmbargoIncognito), kContentTypeNotifications, false);
}
EXPECT_EQ(CONTENT_SETTING_BLOCK,
incognito_auto_blocker
->GetEmbargoResult(GURL(kOriginToEmbargoIncognito),
kContentTypeNotifications)
.content_setting);
}
// Check there are 2 blocked or embargoed origins for an incognito profile.
{
base::ListValue exceptions;
site_settings::GetExceptionsForContentType(kContentTypeNotifications,
incognito_profile,
/*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/true, &exceptions);
ASSERT_EQ(2U, exceptions.GetSize());
}
}
TEST_F(SiteSettingsHelperTest, ExceptionListShowsEmbargoed) {
TestingProfile profile;
constexpr char kOriginToBlock[] = "https://www.blocked.com:443";
constexpr char kOriginToEmbargo[] = "https://embargoed.co.uk:443";
// Check there is no blocked origins.
{
base::ListValue exceptions;
site_settings::GetExceptionsForContentType(
kContentTypeNotifications, &profile, /*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/false, &exceptions);
ASSERT_EQ(0U, exceptions.GetSize());
}
auto* map = HostContentSettingsMapFactory::GetForProfile(&profile);
map->SetContentSettingDefaultScope(GURL(kOriginToBlock), GURL(kOriginToBlock),
kContentTypeNotifications, std::string(),
CONTENT_SETTING_BLOCK);
{
// Check there is 1 blocked origin.
base::ListValue exceptions;
site_settings::GetExceptionsForContentType(
kContentTypeNotifications, &profile, /*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/false, &exceptions);
ASSERT_EQ(1U, exceptions.GetSize());
}
// Add an origin under embargo.
auto* auto_blocker =
PermissionDecisionAutoBlockerFactory::GetForProfile(&profile);
const GURL origin_to_embargo(kOriginToEmbargo);
for (size_t i = 0; i < 3; ++i) {
auto_blocker->RecordDismissAndEmbargo(origin_to_embargo,
kContentTypeNotifications, false);
}
// Check that origin is under embargo.
EXPECT_EQ(CONTENT_SETTING_BLOCK,
auto_blocker
->GetEmbargoResult(origin_to_embargo, kContentTypeNotifications)
.content_setting);
// Check there are 2 blocked origins.
{
base::ListValue exceptions;
site_settings::GetExceptionsForContentType(
kContentTypeNotifications, &profile, /*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/false, &exceptions);
// The size should be 2, 1st is blocked origin, 2nd is embargoed origin.
ASSERT_EQ(2U, exceptions.GetSize());
// Fetch and check the first origin.
const base::DictionaryValue* dictionary;
std::string primary_pattern, display_name;
ASSERT_TRUE(exceptions.GetDictionary(0, &dictionary));
ASSERT_TRUE(
dictionary->GetString(site_settings::kOrigin, &primary_pattern));
ASSERT_TRUE(
dictionary->GetString(site_settings::kDisplayName, &display_name));
EXPECT_EQ(kOriginToBlock, primary_pattern);
EXPECT_EQ(kOriginToBlock, display_name);
// Fetch and check the second origin.
ASSERT_TRUE(exceptions.GetDictionary(1, &dictionary));
ASSERT_TRUE(
dictionary->GetString(site_settings::kOrigin, &primary_pattern));
ASSERT_TRUE(
dictionary->GetString(site_settings::kDisplayName, &display_name));
EXPECT_EQ(kOriginToEmbargo, primary_pattern);
EXPECT_EQ(kOriginToEmbargo, display_name);
}
{
// Non-permission types should not DCHECK when there is autoblocker data
// present.
base::ListValue exceptions;
site_settings::GetExceptionsForContentType(
kContentTypeCookies, &profile, /*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/false, &exceptions);
ASSERT_EQ(0U, exceptions.GetSize());
}
}
TEST_F(SiteSettingsHelperTest, CheckExceptionOrder) {
TestingProfile profile;
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(&profile);
base::ListValue exceptions;
// Check that the initial state of the map is empty.
GetExceptionsForContentType(kContentType, &profile,
/*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/false, &exceptions);
EXPECT_EQ(0u, exceptions.GetSize());
map->SetDefaultContentSetting(kContentType, CONTENT_SETTING_ALLOW);
// Add a policy exception.
std::string star_google_com = "http://[*.]google.com";
auto policy_provider = std::make_unique<content_settings::MockProvider>();
policy_provider->SetWebsiteSetting(
ContentSettingsPattern::FromString(star_google_com),
ContentSettingsPattern::Wildcard(), kContentType, "",
std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
policy_provider->set_read_only(true);
content_settings::TestUtils::OverrideProvider(
map, std::move(policy_provider), HostContentSettingsMap::POLICY_PROVIDER);
// Add user preferences.
std::string http_star = "http://*";
std::string maps_google_com = "http://maps.google.com";
AddSetting(map, http_star, CONTENT_SETTING_BLOCK);
AddSetting(map, maps_google_com, CONTENT_SETTING_BLOCK);
AddSetting(map, star_google_com, CONTENT_SETTING_ALLOW);
// Add an extension exception.
std::string drive_google_com = "http://drive.google.com";
auto extension_provider = std::make_unique<content_settings::MockProvider>();
extension_provider->SetWebsiteSetting(
ContentSettingsPattern::FromString(drive_google_com),
ContentSettingsPattern::Wildcard(), kContentType, "",
std::make_unique<base::Value>(CONTENT_SETTING_ASK));
extension_provider->set_read_only(true);
content_settings::TestUtils::OverrideProvider(
map, std::move(extension_provider),
HostContentSettingsMap::CUSTOM_EXTENSION_PROVIDER);
exceptions.Clear();
GetExceptionsForContentType(kContentType, &profile,
/*extension_registry=*/nullptr,
/*web_ui=*/nullptr,
/*incognito=*/false, &exceptions);
EXPECT_EQ(5u, exceptions.GetSize());
// The policy exception should be returned first, the extension exception
// second and pref exceptions afterwards.
// The default content setting should not be returned.
int i = 0;
// From policy provider:
VerifySetting(exceptions, i++, star_google_com, star_google_com,
CONTENT_SETTING_BLOCK);
// From extension provider:
VerifySetting(exceptions, i++, drive_google_com, drive_google_com,
CONTENT_SETTING_ASK);
// From user preferences:
VerifySetting(exceptions, i++, maps_google_com, maps_google_com,
CONTENT_SETTING_BLOCK);
VerifySetting(exceptions, i++, star_google_com, star_google_com,
CONTENT_SETTING_ALLOW);
VerifySetting(exceptions, i++, http_star, "http://*", CONTENT_SETTING_BLOCK);
}
// Tests the following content setting sources: Chrome default, user-set global
// default, user-set pattern, user-set origin setting, extension, and policy.
TEST_F(SiteSettingsHelperTest, ContentSettingSource) {
TestingProfile profile;
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(&profile);
GURL origin("https://www.example.com/");
auto* extension_registry = extensions::ExtensionRegistry::Get(&profile);
std::string source;
std::string display_name;
ContentSetting content_setting;
// Built in Chrome default.
content_setting =
GetContentSettingForOrigin(&profile, map, origin, kContentType, &source,
extension_registry, &display_name);
EXPECT_EQ(SiteSettingSourceToString(SiteSettingSource::kDefault), source);
EXPECT_EQ(CONTENT_SETTING_ASK, content_setting);
// User-set global default.
map->SetDefaultContentSetting(kContentType, CONTENT_SETTING_ALLOW);
content_setting =
GetContentSettingForOrigin(&profile, map, origin, kContentType, &source,
extension_registry, &display_name);
EXPECT_EQ(SiteSettingSourceToString(SiteSettingSource::kDefault), source);
EXPECT_EQ(CONTENT_SETTING_ALLOW, content_setting);
// User-set pattern.
AddSetting(map, "https://*", CONTENT_SETTING_BLOCK);
content_setting =
GetContentSettingForOrigin(&profile, map, origin, kContentType, &source,
extension_registry, &display_name);
EXPECT_EQ(SiteSettingSourceToString(SiteSettingSource::kPreference), source);
EXPECT_EQ(CONTENT_SETTING_BLOCK, content_setting);
// User-set origin setting.
map->SetContentSettingDefaultScope(origin, origin, kContentType,
std::string(), CONTENT_SETTING_ALLOW);
content_setting =
GetContentSettingForOrigin(&profile, map, origin, kContentType, &source,
extension_registry, &display_name);
EXPECT_EQ(SiteSettingSourceToString(SiteSettingSource::kPreference), source);
EXPECT_EQ(CONTENT_SETTING_ALLOW, content_setting);
// ChromeOS - DRM disabled.
#if defined(OS_CHROMEOS)
profile.GetPrefs()->SetBoolean(prefs::kEnableDRM, false);
// Note this is not testing |kContentType|, because this setting is only valid
// for protected content.
content_setting = GetContentSettingForOrigin(
&profile, map, origin, ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
&source, extension_registry, &display_name);
EXPECT_EQ(SiteSettingSourceToString(SiteSettingSource::kDrmDisabled), source);
EXPECT_EQ(CONTENT_SETTING_BLOCK, content_setting);
#endif
// Extension.
auto extension_provider = std::make_unique<content_settings::MockProvider>();
extension_provider->SetWebsiteSetting(
ContentSettingsPattern::FromURL(origin),
ContentSettingsPattern::FromURL(origin), kContentType, "",
std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
extension_provider->set_read_only(true);
content_settings::TestUtils::OverrideProvider(
map, std::move(extension_provider),
HostContentSettingsMap::CUSTOM_EXTENSION_PROVIDER);
content_setting =
GetContentSettingForOrigin(&profile, map, origin, kContentType, &source,
extension_registry, &display_name);
EXPECT_EQ(SiteSettingSourceToString(SiteSettingSource::kExtension), source);
EXPECT_EQ(CONTENT_SETTING_BLOCK, content_setting);
// Enterprise policy.
auto policy_provider = std::make_unique<content_settings::MockProvider>();
policy_provider->SetWebsiteSetting(
ContentSettingsPattern::FromURL(origin),
ContentSettingsPattern::FromURL(origin), kContentType, "",
std::make_unique<base::Value>(CONTENT_SETTING_ALLOW));
policy_provider->set_read_only(true);
content_settings::TestUtils::OverrideProvider(
map, std::move(policy_provider), HostContentSettingsMap::POLICY_PROVIDER);
content_setting =
GetContentSettingForOrigin(&profile, map, origin, kContentType, &source,
extension_registry, &display_name);
EXPECT_EQ(SiteSettingSourceToString(SiteSettingSource::kPolicy), source);
EXPECT_EQ(CONTENT_SETTING_ALLOW, content_setting);
// Insecure origins.
content_setting = GetContentSettingForOrigin(
&profile, map, GURL("http://www.insecure_http_site.com/"), kContentType,
&source, extension_registry, &display_name);
EXPECT_EQ(SiteSettingSourceToString(SiteSettingSource::kInsecureOrigin),
source);
EXPECT_EQ(CONTENT_SETTING_BLOCK, content_setting);
}
namespace {
// Test GURLs
// TODO(https://crbug.com/1042727): Fix test GURL scoping and remove this getter
// function.
GURL GoogleUrl() {
return GURL("https://google.com");
}
GURL ChromiumUrl() {
return GURL("https://chromium.org");
}
GURL AndroidUrl() {
return GURL("https://android.com");
}
void ExpectValidChooserExceptionObject(
const base::Value& actual_exception_object,
const std::string& chooser_type,
const base::string16& display_name,
const base::Value& chooser_object) {
const base::Value* chooser_type_value = actual_exception_object.FindKeyOfType(
kChooserType, base::Value::Type::STRING);
ASSERT_TRUE(chooser_type_value);
EXPECT_EQ(chooser_type_value->GetString(), chooser_type);
const base::Value* display_name_value = actual_exception_object.FindKeyOfType(
kDisplayName, base::Value::Type::STRING);
ASSERT_TRUE(display_name_value);
EXPECT_EQ(base::UTF8ToUTF16(display_name_value->GetString()), display_name);
const base::Value* object_value = actual_exception_object.FindKeyOfType(
kObject, base::Value::Type::DICTIONARY);
ASSERT_TRUE(object_value);
EXPECT_EQ(*object_value, chooser_object);
const base::Value* sites_value =
actual_exception_object.FindKeyOfType(kSites, base::Value::Type::LIST);
ASSERT_TRUE(sites_value);
}
void ExpectValidSiteExceptionObject(const base::Value& actual_site_object,
const GURL& origin,
const GURL& embedding_origin,
const std::string source,
bool incognito) {
ASSERT_TRUE(actual_site_object.is_dict());
const base::Value* display_name_value =
actual_site_object.FindKeyOfType(kDisplayName, base::Value::Type::STRING);
ASSERT_TRUE(display_name_value);
EXPECT_EQ(display_name_value->GetString(), origin.GetOrigin().spec());
const base::Value* origin_value =
actual_site_object.FindKeyOfType(kOrigin, base::Value::Type::STRING);
ASSERT_TRUE(origin_value);
EXPECT_EQ(origin_value->GetString(), origin.GetOrigin().spec());
const base::Value* embedding_origin_value = actual_site_object.FindKeyOfType(
kEmbeddingOrigin, base::Value::Type::STRING);
ASSERT_TRUE(embedding_origin_value);
EXPECT_EQ(embedding_origin_value->GetString(),
embedding_origin.GetOrigin().spec());
const base::Value* setting_value =
actual_site_object.FindKeyOfType(kSetting, base::Value::Type::STRING);
ASSERT_TRUE(setting_value);
EXPECT_EQ(setting_value->GetString(),
content_settings::ContentSettingToString(CONTENT_SETTING_DEFAULT));
const base::Value* source_value =
actual_site_object.FindKeyOfType(kSource, base::Value::Type::STRING);
ASSERT_TRUE(source_value);
EXPECT_EQ(source_value->GetString(), source);
const base::Value* incognito_value =
actual_site_object.FindKeyOfType(kIncognito, base::Value::Type::BOOLEAN);
ASSERT_TRUE(incognito_value);
EXPECT_EQ(incognito_value->GetBool(), incognito);
}
void ExpectValidSiteExceptionObject(const base::Value& actual_site_object,
const GURL& origin,
const std::string source,
bool incognito) {
ExpectValidSiteExceptionObject(actual_site_object, origin, GURL::EmptyGURL(),
source, incognito);
}
} // namespace
TEST_F(SiteSettingsHelperTest, CreateChooserExceptionObject) {
const std::string kUsbChooserGroupName =
ContentSettingsTypeToGroupName(ContentSettingsType::USB_CHOOSER_DATA);
const std::string& kPolicySource =
SiteSettingSourceToString(SiteSettingSource::kPolicy);
const std::string& kPreferenceSource =
SiteSettingSourceToString(SiteSettingSource::kPreference);
const base::string16& kObjectName = base::ASCIIToUTF16("Gadget");
ChooserExceptionDetails exception_details;
// Create a chooser object for testing.
auto chooser_object = std::make_unique<base::DictionaryValue>();
chooser_object->SetKey("name", base::Value(kObjectName));
// Add a user permission for a requesting origin of |kGoogleOrigin| and an
// embedding origin of |kChromiumOrigin|.
exception_details[std::make_pair(GoogleUrl().GetOrigin(), kPreferenceSource)]
.insert(std::make_pair(ChromiumUrl().GetOrigin(), /*incognito=*/false));
{
auto exception = CreateChooserExceptionObject(
/*display_name=*/kObjectName,
/*object=*/*chooser_object,
/*chooser_type=*/kUsbChooserGroupName,
/*chooser_exception_details=*/exception_details);
ExpectValidChooserExceptionObject(
exception, /*chooser_type=*/kUsbChooserGroupName,
/*display_name=*/kObjectName, *chooser_object);
const auto& sites_list = exception.FindKey(kSites)->GetList();
ExpectValidSiteExceptionObject(/*actual_site_object=*/sites_list[0],
/*origin=*/GoogleUrl(),
/*embedding_origin=*/ChromiumUrl(),
/*source=*/kPreferenceSource,
/*incognito=*/false);
}
// Add a user permissions for a requesting and embedding origin pair of
// |kAndroidOrigin| granted in an off the record profile.
exception_details[std::make_pair(AndroidUrl().GetOrigin(), kPreferenceSource)]
.insert(std::make_pair(AndroidUrl().GetOrigin(), /*incognito=*/true));
{
auto exception = CreateChooserExceptionObject(
/*display_name=*/kObjectName,
/*object=*/*chooser_object,
/*chooser_type=*/kUsbChooserGroupName,
/*chooser_exception_details=*/exception_details);
ExpectValidChooserExceptionObject(exception,
/*chooser_type=*/kUsbChooserGroupName,
/*display_name=*/kObjectName,
*chooser_object);
// The map sorts the sites by requesting origin, so |kAndroidOrigin| should
// be first, followed by the origin pair (kGoogleOrigin, kChromiumOrigin).
const auto& sites_list = exception.FindKey(kSites)->GetList();
ExpectValidSiteExceptionObject(/*actual_site_object=*/sites_list[0],
/*origin=*/AndroidUrl(),
/*embedding_origin=*/AndroidUrl(),
/*source=*/kPreferenceSource,
/*incognito=*/true);
ExpectValidSiteExceptionObject(/*actual_site_object=*/sites_list[1],
/*origin=*/GoogleUrl(),
/*embedding_origin=*/ChromiumUrl(),
/*source=*/kPreferenceSource,
/*incognito=*/false);
}
// Add a policy permission for a requesting origin of |kGoogleOrigin| with a
// wildcard embedding origin.
exception_details[std::make_pair(GoogleUrl().GetOrigin(), kPolicySource)]
.insert(std::make_pair(GURL::EmptyGURL(), /*incognito=*/false));
{
auto exception = CreateChooserExceptionObject(
/*display_name=*/kObjectName,
/*object=*/*chooser_object,
/*chooser_type=*/kUsbChooserGroupName,
/*chooser_exception_details=*/exception_details);
ExpectValidChooserExceptionObject(exception,
/*chooser_type=*/kUsbChooserGroupName,
/*display_name=*/kObjectName,
*chooser_object);
// The map sorts the sites by requesting origin, but the
// CreateChooserExceptionObject method sorts the sites further by the
// source. Therefore, policy granted sites are listed before user granted
// sites.
const auto& sites_list = exception.FindKey(kSites)->GetList();
ExpectValidSiteExceptionObject(/*actual_site_object=*/sites_list[0],
/*origin=*/GoogleUrl(),
/*embedding_origin=*/GURL::EmptyGURL(),
/*source=*/kPolicySource,
/*incognito=*/false);
ExpectValidSiteExceptionObject(/*actual_site_object=*/sites_list[1],
/*origin=*/AndroidUrl(),
/*embedding_origin=*/AndroidUrl(),
/*source=*/kPreferenceSource,
/*incognito=*/true);
ExpectValidSiteExceptionObject(/*actual_site_object=*/sites_list[2],
/*origin=*/GoogleUrl(),
/*embedding_origin=*/ChromiumUrl(),
/*source=*/kPreferenceSource,
/*incognito=*/false);
}
}
namespace {
constexpr char kUsbPolicySetting[] = R"(
[
{
"devices": [{ "vendor_id": 6353, "product_id": 5678 }],
"urls": ["https://chromium.org"]
}, {
"devices": [{ "vendor_id": 6353 }],
"urls": ["https://google.com,https://android.com"]
}, {
"devices": [{ "vendor_id": 6354 }],
"urls": ["https://android.com,"]
}, {
"devices": [{}],
"urls": ["https://google.com,https://google.com"]
}
])";
class SiteSettingsHelperChooserExceptionTest : public testing::Test {
protected:
Profile* profile() { return &profile_; }
void SetUp() override { SetUpUsbChooserContext(); }
// Sets up the UsbChooserContext with two devices and permissions for these
// devices. It also adds three policy defined permissions. The two devices
// represent the two types of USB devices, persistent and ephemeral, that can
// be granted permission.
void SetUpUsbChooserContext() {
device::mojom::UsbDeviceInfoPtr persistent_device_info =
device_manager_.CreateAndAddDevice(6353, 5678, "Google", "Gizmo",
"123ABC");
device::mojom::UsbDeviceInfoPtr ephemeral_device_info =
device_manager_.CreateAndAddDevice(6354, 0, "Google", "Gadget", "");
auto* chooser_context = UsbChooserContextFactory::GetForProfile(profile());
mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
device_manager_.AddReceiver(
device_manager.InitWithNewPipeAndPassReceiver());
chooser_context->SetDeviceManagerForTesting(std::move(device_manager));
chooser_context->GetDevices(
base::DoNothing::Once<std::vector<device::mojom::UsbDeviceInfoPtr>>());
base::RunLoop().RunUntilIdle();
const auto kAndroidOrigin = url::Origin::Create(AndroidUrl());
const auto kChromiumOrigin = url::Origin::Create(ChromiumUrl());
const auto kGoogleOrigin = url::Origin::Create(GoogleUrl());
// Add the user granted permissions for testing.
// These two persistent device permissions should be lumped together with
// the policy permissions, since they apply to the same device and URL.
chooser_context->GrantDevicePermission(kChromiumOrigin, kChromiumOrigin,
*persistent_device_info);
chooser_context->GrantDevicePermission(kChromiumOrigin, kGoogleOrigin,
*persistent_device_info);
chooser_context->GrantDevicePermission(kAndroidOrigin, kChromiumOrigin,
*persistent_device_info);
chooser_context->GrantDevicePermission(kAndroidOrigin, kAndroidOrigin,
*ephemeral_device_info);
// Add the policy granted permissions for testing.
auto policy_value = base::JSONReader::ReadDeprecated(kUsbPolicySetting);
DCHECK(policy_value);
profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls,
*policy_value);
}
device::FakeUsbDeviceManager device_manager_;
private:
content::BrowserTaskEnvironment task_environment_;
TestingProfile profile_;
};
void ExpectDisplayNameEq(const base::Value& actual_exception_object,
const std::string& display_name) {
const std::string* actual_display_name =
actual_exception_object.FindStringKey(kDisplayName);
ASSERT_TRUE(actual_display_name);
EXPECT_EQ(*actual_display_name, display_name);
}
} // namespace
TEST_F(SiteSettingsHelperChooserExceptionTest,
GetChooserExceptionListFromProfile) {
const std::string kUsbChooserGroupName =
ContentSettingsTypeToGroupName(ContentSettingsType::USB_CHOOSER_DATA);
const ChooserTypeNameEntry* chooser_type =
ChooserTypeFromGroupName(kUsbChooserGroupName);
const std::string& kPolicySource =
SiteSettingSourceToString(SiteSettingSource::kPolicy);
const std::string& kPreferenceSource =
SiteSettingSourceToString(SiteSettingSource::kPreference);
// The chooser exceptions are ordered by display name. Their corresponding
// sites are ordered by permission source precedence, then by the requesting
// origin and the embedding origin. User granted permissions that are also
// granted by policy are combined with the policy so that duplicate
// permissions are not displayed.
base::Value exceptions =
GetChooserExceptionListFromProfile(profile(), *chooser_type);
base::Value::ConstListView exceptions_list = exceptions.GetList();
ASSERT_EQ(exceptions_list.size(), 4u);
// This exception should describe the permissions for any device with the
// vendor ID corresponding to "Google Inc.". There are no user granted
// permissions that intersect with this permission, and this policy only
// grants one permission to the following site pair:
// * ("https://google.com", "https://android.com")
{
const auto& exception = exceptions_list[0];
ExpectDisplayNameEq(exception,
/*display_name=*/"Devices from Google Inc.");
const auto& sites_list = exception.FindKey(kSites)->GetList();
ASSERT_EQ(sites_list.size(), 1u);
ExpectValidSiteExceptionObject(sites_list[0],
/*origin=*/GoogleUrl(),
/*embedding_origin=*/AndroidUrl(),
/*source=*/kPolicySource,
/*incognito=*/false);
}
// This exception should describe the permissions for any device.
// There are no user granted permissions that intersect with this permission,
// and this policy only grants one permission to the following site pair:
// * ("https://google.com", "https://google.com")
{
const auto& exception = exceptions_list[1];
ExpectDisplayNameEq(exception,
/*display_name=*/"Devices from any vendor");
const auto& sites_list = exception.FindKey(kSites)->GetList();
ASSERT_EQ(sites_list.size(), 1u);
ExpectValidSiteExceptionObject(sites_list[0],
/*origin=*/GoogleUrl(),
/*embedding_origin=*/GoogleUrl(),
/*source=*/kPolicySource,
/*incognito=*/false);
}
// This exception should describe the permissions for any device with the
// vendor ID 6354. There is a user granted permission for a device with that
// vendor ID, so the site list for this exception will only have the policy
// granted permission, which is the following:
// * ("https://android.com", "")
{
const auto& exception = exceptions_list[2];
ExpectDisplayNameEq(exception,
/*display_name=*/"Devices from vendor 0x18D2");
const auto& sites_list = exception.FindKey(kSites)->GetList();
ASSERT_EQ(sites_list.size(), 1u);
ExpectValidSiteExceptionObject(sites_list[0],
/*origin=*/AndroidUrl(),
/*source=*/kPolicySource,
/*incognito=*/false);
}
// This exception should describe the permissions for the "Gizmo" device.
// The user granted permissions are the following:
// * ("https://chromium.org", "https://chromium.org")
// * ("https://chromium.org", "https://google.com")
// * ("https://android.com", "https://chromium.org")
// The policy granted permission is the following:
// * ("https://chromium.org", "")
// The embedding origin is a wildcard, so the policy granted permission covers
// any user granted permissions that contain a requesting origin of
// "https://chromium.org", so the site list for this exception will only have
// the following permissions:
// * ("https://chromium.org", "")
// * ("https://android.com", "https://chromium.org")
{
const auto& exception = exceptions_list[3];
ExpectDisplayNameEq(exception, /*display_name=*/"Gizmo");
const auto& sites_list = exception.FindKey(kSites)->GetList();
ASSERT_EQ(sites_list.size(), 2u);
ExpectValidSiteExceptionObject(sites_list[0],
/*origin=*/ChromiumUrl(),
/*source=*/kPolicySource,
/*incognito=*/false);
ExpectValidSiteExceptionObject(sites_list[1],
/*origin=*/AndroidUrl(),
/*embedding_origin=*/ChromiumUrl(),
/*source=*/kPreferenceSource,
/*incognito=*/false);
}
}
} // namespace site_settings