| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/containers/flat_map.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/clock.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| #include "components/content_settings/core/browser/content_settings_default_provider.h" |
| #include "components/content_settings/core/browser/content_settings_details.h" |
| #include "components/content_settings/core/browser/content_settings_ephemeral_provider.h" |
| #include "components/content_settings/core/browser/content_settings_info.h" |
| #include "components/content_settings/core/browser/content_settings_observable_provider.h" |
| #include "components/content_settings/core/browser/content_settings_policy_provider.h" |
| #include "components/content_settings/core/browser/content_settings_pref_provider.h" |
| #include "components/content_settings/core/browser/content_settings_provider.h" |
| #include "components/content_settings/core/browser/content_settings_registry.h" |
| #include "components/content_settings/core/browser/content_settings_rule.h" |
| #include "components/content_settings/core/browser/content_settings_utils.h" |
| #include "components/content_settings/core/browser/user_modifiable_provider.h" |
| #include "components/content_settings/core/browser/website_settings_registry.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/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_service.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/static_cookie_policy.h" |
| #include "url/gurl.h" |
| |
| using content_settings::WebsiteSettingsInfo; |
| using content_settings::ContentSettingsInfo; |
| |
| namespace { |
| |
| typedef std::vector<content_settings::Rule> Rules; |
| |
| typedef std::pair<std::string, std::string> StringPair; |
| |
| struct ProviderNamesSourceMapEntry { |
| const char* provider_name; |
| content_settings::SettingSource provider_source; |
| }; |
| |
| const HostContentSettingsMap::ProviderType kFirstProvider = |
| HostContentSettingsMap::POLICY_PROVIDER; |
| const HostContentSettingsMap::ProviderType kFirstUserModifiableProvider = |
| HostContentSettingsMap::NOTIFICATION_ANDROID_PROVIDER; |
| |
| constexpr ProviderNamesSourceMapEntry kProviderNamesSourceMap[] = { |
| {"policy", content_settings::SETTING_SOURCE_POLICY}, |
| {"supervised_user", content_settings::SETTING_SOURCE_SUPERVISED}, |
| {"extension", content_settings::SETTING_SOURCE_EXTENSION}, |
| {"notification_android", content_settings::SETTING_SOURCE_USER}, |
| {"ephemeral", content_settings::SETTING_SOURCE_USER}, |
| {"preference", content_settings::SETTING_SOURCE_USER}, |
| {"default", content_settings::SETTING_SOURCE_USER}, |
| {"tests", content_settings::SETTING_SOURCE_USER}, |
| {"tests_other", content_settings::SETTING_SOURCE_USER}, |
| }; |
| |
| static_assert( |
| base::size(kProviderNamesSourceMap) == |
| HostContentSettingsMap::NUM_PROVIDER_TYPES, |
| "kProviderNamesSourceMap should have NUM_PROVIDER_TYPES elements"); |
| |
| // Ensure that kFirstUserModifiableProvider is actually the highest precedence |
| // user modifiable provider. |
| constexpr bool FirstUserModifiableProviderIsHighestPrecedence() { |
| for (size_t i = 0; i < kFirstUserModifiableProvider; ++i) { |
| if (kProviderNamesSourceMap[i].provider_source == |
| content_settings::SETTING_SOURCE_USER) { |
| return false; |
| } |
| } |
| return kProviderNamesSourceMap[kFirstUserModifiableProvider] |
| .provider_source == content_settings::SETTING_SOURCE_USER; |
| } |
| static_assert(FirstUserModifiableProviderIsHighestPrecedence(), |
| "kFirstUserModifiableProvider is not the highest precedence user " |
| "modifiable provider."); |
| |
| // Returns true if the |content_type| supports a resource identifier. |
| // Resource identifiers are supported (but not required) for plugins. |
| bool SupportsResourceIdentifier(ContentSettingsType content_type) { |
| return content_type == CONTENT_SETTINGS_TYPE_PLUGINS; |
| } |
| |
| bool SchemeCanBeWhitelisted(const std::string& scheme) { |
| return scheme == content_settings::kChromeDevToolsScheme || |
| scheme == content_settings::kExtensionScheme || |
| scheme == content_settings::kChromeUIScheme; |
| } |
| |
| // Handles inheritance of settings from the regular profile into the incognito |
| // profile. |
| std::unique_ptr<base::Value> ProcessIncognitoInheritanceBehavior( |
| ContentSettingsType content_type, |
| std::unique_ptr<base::Value> value) { |
| // Website setting inheritance can be completely disallowed. |
| const WebsiteSettingsInfo* website_settings_info = |
| content_settings::WebsiteSettingsRegistry::GetInstance()->Get( |
| content_type); |
| if (website_settings_info && |
| website_settings_info->incognito_behavior() == |
| WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO) { |
| return nullptr; |
| } |
| |
| // Content setting inheritance can be for settings, that are more permissive |
| // than the initial value of a content setting. |
| const ContentSettingsInfo* content_settings_info = |
| content_settings::ContentSettingsRegistry::GetInstance()->Get( |
| content_type); |
| if (content_settings_info) { |
| ContentSettingsInfo::IncognitoBehavior behaviour = |
| content_settings_info->incognito_behavior(); |
| switch (behaviour) { |
| case ContentSettingsInfo::INHERIT_IN_INCOGNITO: |
| return value; |
| case ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE: |
| ContentSetting setting = |
| content_settings::ValueToContentSetting(value.get()); |
| const base::Value* initial_value = content_settings_info |
| ->website_settings_info()->initial_default_value(); |
| ContentSetting initial_setting = |
| content_settings::ValueToContentSetting(initial_value); |
| if (content_settings::IsMorePermissive(setting, initial_setting)) |
| return content_settings::ContentSettingToValue(initial_setting); |
| return value; |
| } |
| } |
| |
| return value; |
| } |
| |
| content_settings::PatternPair GetPatternsFromScopingType( |
| WebsiteSettingsInfo::ScopingType scoping_type, |
| const GURL& primary_url, |
| const GURL& secondary_url) { |
| DCHECK(!primary_url.is_empty()); |
| content_settings::PatternPair patterns; |
| |
| switch (scoping_type) { |
| case WebsiteSettingsInfo::COOKIES_SCOPE: |
| patterns.first = ContentSettingsPattern::FromURL(primary_url); |
| patterns.second = ContentSettingsPattern::Wildcard(); |
| break; |
| case WebsiteSettingsInfo::SINGLE_ORIGIN_ONLY_SCOPE: |
| case WebsiteSettingsInfo::SINGLE_ORIGIN_WITH_EMBEDDED_EXCEPTIONS_SCOPE: |
| patterns.first = ContentSettingsPattern::FromURLNoWildcard(primary_url); |
| patterns.second = ContentSettingsPattern::Wildcard(); |
| break; |
| case WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE: |
| DCHECK(!secondary_url.is_empty()); |
| patterns.first = ContentSettingsPattern::FromURLNoWildcard(primary_url); |
| patterns.second = |
| ContentSettingsPattern::FromURLNoWildcard(secondary_url); |
| break; |
| } |
| return patterns; |
| } |
| |
| content_settings::PatternPair GetPatternsForContentSettingsType( |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType type) { |
| const WebsiteSettingsInfo* website_settings_info = |
| content_settings::WebsiteSettingsRegistry::GetInstance()->Get(type); |
| DCHECK(website_settings_info); |
| content_settings::PatternPair patterns = GetPatternsFromScopingType( |
| website_settings_info->scoping_type(), primary_url, secondary_url); |
| return patterns; |
| } |
| |
| // This enum is used to collect Flash permission data. |
| enum class FlashPermissions { |
| kFirstTime = 0, |
| kRepeated = 1, |
| kMaxValue = kRepeated, |
| }; |
| |
| // Returns whether per-content setting exception information should be |
| // collected. All content settings for which this method returns true here be |
| // content settings, not website settings (i.e. their value should be a |
| // ContentSetting). |
| // |
| // This method should be kept in sync with histograms.xml, as every type here |
| // is an affected histogram under the "ContentSetting" suffix. |
| bool ShouldCollectFineGrainedExceptionHistograms(ContentSettingsType type) { |
| switch (type) { |
| case CONTENT_SETTINGS_TYPE_COOKIES: |
| case CONTENT_SETTINGS_TYPE_POPUPS: |
| case CONTENT_SETTINGS_TYPE_ADS: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| const char* ContentSettingToString(ContentSetting setting) { |
| switch (setting) { |
| case CONTENT_SETTING_ALLOW: |
| return "Allow"; |
| case CONTENT_SETTING_BLOCK: |
| return "Block"; |
| case CONTENT_SETTING_ASK: |
| return "Ask"; |
| case CONTENT_SETTING_SESSION_ONLY: |
| return "SessionOnly"; |
| case CONTENT_SETTING_DETECT_IMPORTANT_CONTENT: |
| return "DetectImportantContent"; |
| case CONTENT_SETTING_DEFAULT: |
| case CONTENT_SETTING_NUM_SETTINGS: |
| NOTREACHED(); |
| return nullptr; |
| } |
| } |
| |
| } // namespace |
| |
| HostContentSettingsMap::HostContentSettingsMap( |
| PrefService* prefs, |
| bool is_incognito_profile, |
| bool is_guest_profile, |
| bool store_last_modified, |
| bool migrate_requesting_and_top_level_origin_settings) |
| : RefcountedKeyedService(base::ThreadTaskRunnerHandle::Get()), |
| #ifndef NDEBUG |
| used_from_thread_id_(base::PlatformThread::CurrentId()), |
| #endif |
| prefs_(prefs), |
| is_incognito_(is_incognito_profile || is_guest_profile), |
| store_last_modified_(store_last_modified), |
| weak_ptr_factory_(this) { |
| TRACE_EVENT0("startup", "HostContentSettingsMap::HostContentSettingsMap"); |
| DCHECK(!(is_incognito_profile && is_guest_profile)); |
| |
| content_settings::PolicyProvider* policy_provider = |
| new content_settings::PolicyProvider(prefs_); |
| content_settings_providers_[POLICY_PROVIDER] = |
| base::WrapUnique(policy_provider); |
| policy_provider->AddObserver(this); |
| |
| pref_provider_ = new content_settings::PrefProvider(prefs_, is_incognito_, |
| store_last_modified_); |
| content_settings_providers_[PREF_PROVIDER] = base::WrapUnique(pref_provider_); |
| user_modifiable_providers_.push_back(pref_provider_); |
| pref_provider_->AddObserver(this); |
| |
| // This ensures that content settings are cleared for the guest profile. This |
| // wouldn't be needed except that we used to allow settings to be stored for |
| // the guest profile and so we need to ensure those get cleared. |
| if (is_guest_profile) |
| pref_provider_->ClearPrefs(); |
| |
| content_settings::EphemeralProvider* ephemeral_provider = |
| new content_settings::EphemeralProvider(store_last_modified_); |
| content_settings_providers_[EPHEMERAL_PROVIDER] = |
| base::WrapUnique(ephemeral_provider); |
| user_modifiable_providers_.push_back(ephemeral_provider); |
| ephemeral_provider->AddObserver(this); |
| |
| auto default_provider = std::make_unique<content_settings::DefaultProvider>( |
| prefs_, is_incognito_); |
| default_provider->AddObserver(this); |
| content_settings_providers_[DEFAULT_PROVIDER] = std::move(default_provider); |
| |
| InitializePluginsDataSettings(); |
| if (migrate_requesting_and_top_level_origin_settings) |
| MigrateRequestingAndTopLevelOriginSettings(); |
| RecordExceptionMetrics(); |
| } |
| |
| // static |
| void HostContentSettingsMap::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| // Ensure the content settings are all registered. |
| content_settings::ContentSettingsRegistry::GetInstance(); |
| |
| registry->RegisterIntegerPref(prefs::kContentSettingsWindowLastTabIndex, 0); |
| |
| // Register the prefs for the content settings providers. |
| content_settings::DefaultProvider::RegisterProfilePrefs(registry); |
| content_settings::PrefProvider::RegisterProfilePrefs(registry); |
| content_settings::PolicyProvider::RegisterProfilePrefs(registry); |
| } |
| |
| void HostContentSettingsMap::RegisterUserModifiableProvider( |
| ProviderType type, |
| std::unique_ptr<content_settings::UserModifiableProvider> provider) { |
| user_modifiable_providers_.push_back(provider.get()); |
| RegisterProvider(type, std::move(provider)); |
| } |
| |
| void HostContentSettingsMap::RegisterProvider( |
| ProviderType type, |
| std::unique_ptr<content_settings::ObservableProvider> provider) { |
| DCHECK(!content_settings_providers_[type]); |
| provider->AddObserver(this); |
| content_settings_providers_[type] = std::move(provider); |
| |
| #ifndef NDEBUG |
| DCHECK_NE(used_from_thread_id_, base::kInvalidThreadId) |
| << "Used from multiple threads before initialization complete."; |
| #endif |
| |
| OnContentSettingChanged(ContentSettingsPattern(), |
| ContentSettingsPattern(), |
| CONTENT_SETTINGS_TYPE_DEFAULT, |
| std::string()); |
| } |
| |
| ContentSetting HostContentSettingsMap::GetDefaultContentSettingFromProvider( |
| ContentSettingsType content_type, |
| content_settings::ProviderInterface* provider) const { |
| std::unique_ptr<content_settings::RuleIterator> rule_iterator( |
| provider->GetRuleIterator(content_type, std::string(), false)); |
| |
| if (rule_iterator) { |
| ContentSettingsPattern wildcard = ContentSettingsPattern::Wildcard(); |
| while (rule_iterator->HasNext()) { |
| content_settings::Rule rule = rule_iterator->Next(); |
| if (rule.primary_pattern == wildcard && |
| rule.secondary_pattern == wildcard) { |
| return content_settings::ValueToContentSetting(&rule.value); |
| } |
| } |
| } |
| return CONTENT_SETTING_DEFAULT; |
| } |
| |
| ContentSetting HostContentSettingsMap::GetDefaultContentSettingInternal( |
| ContentSettingsType content_type, |
| ProviderType* provider_type) const { |
| DCHECK(provider_type); |
| UsedContentSettingsProviders(); |
| |
| // Iterate through the list of providers and return the first non-NULL value |
| // that matches |primary_url| and |secondary_url|. |
| for (const auto& provider_pair : content_settings_providers_) { |
| if (provider_pair.first == PREF_PROVIDER) |
| continue; |
| ContentSetting default_setting = GetDefaultContentSettingFromProvider( |
| content_type, provider_pair.second.get()); |
| if (is_incognito_) { |
| default_setting = content_settings::ValueToContentSetting( |
| ProcessIncognitoInheritanceBehavior( |
| content_type, |
| content_settings::ContentSettingToValue(default_setting)) |
| .get()); |
| } |
| if (default_setting != CONTENT_SETTING_DEFAULT) { |
| *provider_type = provider_pair.first; |
| return default_setting; |
| } |
| } |
| |
| return CONTENT_SETTING_DEFAULT; |
| } |
| |
| ContentSetting HostContentSettingsMap::GetDefaultContentSetting( |
| ContentSettingsType content_type, |
| std::string* provider_id) const { |
| ProviderType provider_type = NUM_PROVIDER_TYPES; |
| ContentSetting content_setting = |
| GetDefaultContentSettingInternal(content_type, &provider_type); |
| if (content_setting != CONTENT_SETTING_DEFAULT && provider_id) |
| *provider_id = kProviderNamesSourceMap[provider_type].provider_name; |
| return content_setting; |
| } |
| |
| ContentSetting HostContentSettingsMap::GetContentSetting( |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier) const { |
| DCHECK(content_settings::ContentSettingsRegistry::GetInstance()->Get( |
| content_type)); |
| std::unique_ptr<base::Value> value = GetWebsiteSetting( |
| primary_url, secondary_url, content_type, resource_identifier, nullptr); |
| return content_settings::ValueToContentSetting(value.get()); |
| } |
| |
| ContentSetting HostContentSettingsMap::GetUserModifiableContentSetting( |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier) const { |
| DCHECK(content_settings::ContentSettingsRegistry::GetInstance()->Get( |
| content_type)); |
| std::unique_ptr<base::Value> value = GetWebsiteSettingInternal( |
| primary_url, secondary_url, content_type, resource_identifier, |
| kFirstUserModifiableProvider, nullptr); |
| return content_settings::ValueToContentSetting(value.get()); |
| } |
| |
| void HostContentSettingsMap::GetSettingsForOneType( |
| ContentSettingsType content_type, |
| const std::string& resource_identifier, |
| ContentSettingsForOneType* settings) const { |
| DCHECK(SupportsResourceIdentifier(content_type) || |
| resource_identifier.empty()); |
| DCHECK(settings); |
| UsedContentSettingsProviders(); |
| |
| settings->clear(); |
| for (const auto& provider_pair : content_settings_providers_) { |
| // For each provider, iterate first the incognito-specific rules, then the |
| // normal rules. |
| if (is_incognito_) { |
| AddSettingsForOneType(provider_pair.second.get(), provider_pair.first, |
| content_type, resource_identifier, settings, true); |
| } |
| AddSettingsForOneType(provider_pair.second.get(), provider_pair.first, |
| content_type, resource_identifier, settings, false); |
| } |
| } |
| |
| void HostContentSettingsMap::SetDefaultContentSetting( |
| ContentSettingsType content_type, |
| ContentSetting setting) { |
| std::unique_ptr<base::Value> value; |
| // A value of CONTENT_SETTING_DEFAULT implies deleting the content setting. |
| if (setting != CONTENT_SETTING_DEFAULT) { |
| DCHECK(content_settings::ContentSettingsRegistry::GetInstance() |
| ->Get(content_type) |
| ->IsDefaultSettingValid(setting)); |
| value.reset(new base::Value(setting)); |
| } |
| SetWebsiteSettingCustomScope(ContentSettingsPattern::Wildcard(), |
| ContentSettingsPattern::Wildcard(), content_type, |
| std::string(), std::move(value)); |
| } |
| |
| void HostContentSettingsMap::SetWebsiteSettingDefaultScope( |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier, |
| std::unique_ptr<base::Value> value) { |
| content_settings::PatternPair patterns = GetPatternsForContentSettingsType( |
| primary_url, secondary_url, content_type); |
| ContentSettingsPattern primary_pattern = patterns.first; |
| ContentSettingsPattern secondary_pattern = patterns.second; |
| if (!primary_pattern.IsValid() || !secondary_pattern.IsValid()) |
| return; |
| |
| SetWebsiteSettingCustomScope(primary_pattern, secondary_pattern, content_type, |
| resource_identifier, std::move(value)); |
| } |
| |
| void HostContentSettingsMap::SetWebsiteSettingCustomScope( |
| const ContentSettingsPattern& primary_pattern, |
| const ContentSettingsPattern& secondary_pattern, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier, |
| std::unique_ptr<base::Value> value) { |
| DCHECK(primary_pattern == secondary_pattern || |
| secondary_pattern == ContentSettingsPattern::Wildcard() || |
| content_settings::WebsiteSettingsRegistry::GetInstance() |
| ->Get(content_type) |
| ->SupportsEmbeddedExceptions() || |
| content_settings::WebsiteSettingsRegistry::GetInstance() |
| ->Get(content_type) |
| ->scoping_type() == |
| WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE); |
| DCHECK(SupportsResourceIdentifier(content_type) || |
| resource_identifier.empty()); |
| // TODO(crbug.com/731126): Verify that assumptions for notification content |
| // settings are met. |
| UsedContentSettingsProviders(); |
| |
| for (const auto& provider_pair : content_settings_providers_) { |
| if (provider_pair.second->SetWebsiteSetting( |
| primary_pattern, secondary_pattern, content_type, |
| resource_identifier, value.get())) { |
| // If successful then ownership is passed to the provider. |
| ignore_result(value.release()); |
| return; |
| } |
| } |
| NOTREACHED(); |
| } |
| |
| bool HostContentSettingsMap::CanSetNarrowestContentSetting( |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType type) const { |
| content_settings::PatternPair patterns = |
| GetNarrowestPatterns(primary_url, secondary_url, type); |
| return patterns.first.IsValid() && patterns.second.IsValid(); |
| } |
| |
| bool HostContentSettingsMap::IsRestrictedToSecureOrigins( |
| ContentSettingsType type) const { |
| const ContentSettingsInfo* content_settings_info = |
| content_settings::ContentSettingsRegistry::GetInstance()->Get(type); |
| DCHECK(content_settings_info); |
| |
| return content_settings_info->origin_restriction() == |
| ContentSettingsInfo::EXCEPTIONS_ON_SECURE_ORIGINS_ONLY; |
| } |
| |
| void HostContentSettingsMap::SetNarrowestContentSetting( |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType type, |
| ContentSetting setting) { |
| content_settings::PatternPair patterns = |
| GetNarrowestPatterns(primary_url, secondary_url, type); |
| |
| if (!patterns.first.IsValid() || !patterns.second.IsValid()) |
| return; |
| |
| SetContentSettingCustomScope(patterns.first, patterns.second, type, |
| std::string(), setting); |
| } |
| |
| content_settings::PatternPair HostContentSettingsMap::GetNarrowestPatterns ( |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType type) const { |
| // Permission settings are specified via rules. There exists always at least |
| // one rule for the default setting. Get the rule that currently defines |
| // the permission for the given permission |type|. Then test whether the |
| // existing rule is more specific than the rule we are about to create. If |
| // the existing rule is more specific, than change the existing rule instead |
| // of creating a new rule that would be hidden behind the existing rule. |
| content_settings::SettingInfo info; |
| std::unique_ptr<base::Value> v = GetWebsiteSettingInternal( |
| primary_url, secondary_url, type, std::string(), kFirstProvider, &info); |
| if (info.source != content_settings::SETTING_SOURCE_USER) { |
| // Return an invalid pattern if the current setting is not a user setting |
| // and thus can't be changed. |
| return content_settings::PatternPair(); |
| } |
| |
| content_settings::PatternPair patterns = GetPatternsForContentSettingsType( |
| primary_url, secondary_url, type); |
| |
| ContentSettingsPattern::Relation r1 = |
| info.primary_pattern.Compare(patterns.first); |
| if (r1 == ContentSettingsPattern::PREDECESSOR) { |
| patterns.first = info.primary_pattern; |
| } else if (r1 == ContentSettingsPattern::IDENTITY) { |
| ContentSettingsPattern::Relation r2 = |
| info.secondary_pattern.Compare(patterns.second); |
| DCHECK(r2 != ContentSettingsPattern::DISJOINT_ORDER_POST && |
| r2 != ContentSettingsPattern::DISJOINT_ORDER_PRE); |
| if (r2 == ContentSettingsPattern::PREDECESSOR) |
| patterns.second = info.secondary_pattern; |
| } |
| |
| return patterns; |
| } |
| |
| void HostContentSettingsMap::SetContentSettingCustomScope( |
| const ContentSettingsPattern& primary_pattern, |
| const ContentSettingsPattern& secondary_pattern, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier, |
| ContentSetting setting) { |
| DCHECK(content_settings::ContentSettingsRegistry::GetInstance()->Get( |
| content_type)); |
| |
| // Record stats on Flash permission grants with ephemeral storage. |
| if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS && |
| setting == CONTENT_SETTING_ALLOW) { |
| GURL url(primary_pattern.ToString()); |
| ContentSettingsPattern temp_patterns[2]; |
| std::unique_ptr<base::Value> value(GetContentSettingValueAndPatterns( |
| content_settings_providers_[PREF_PROVIDER].get(), url, url, |
| CONTENT_SETTINGS_TYPE_PLUGINS_DATA, resource_identifier, is_incognito_, |
| temp_patterns, temp_patterns + 1)); |
| |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.EphemeralFlashPermission", |
| value ? FlashPermissions::kRepeated : FlashPermissions::kFirstTime); |
| } |
| |
| std::unique_ptr<base::Value> value; |
| // A value of CONTENT_SETTING_DEFAULT implies deleting the content setting. |
| if (setting != CONTENT_SETTING_DEFAULT) { |
| DCHECK(content_settings::ContentSettingsRegistry::GetInstance() |
| ->Get(content_type) |
| ->IsSettingValid(setting)); |
| value.reset(new base::Value(setting)); |
| } |
| SetWebsiteSettingCustomScope(primary_pattern, secondary_pattern, content_type, |
| resource_identifier, std::move(value)); |
| } |
| |
| void HostContentSettingsMap::SetContentSettingDefaultScope( |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier, |
| ContentSetting setting) { |
| content_settings::PatternPair patterns = GetPatternsForContentSettingsType( |
| primary_url, secondary_url, content_type); |
| |
| ContentSettingsPattern primary_pattern = patterns.first; |
| ContentSettingsPattern secondary_pattern = patterns.second; |
| if (!primary_pattern.IsValid() || !secondary_pattern.IsValid()) |
| return; |
| |
| SetContentSettingCustomScope(primary_pattern, secondary_pattern, content_type, |
| resource_identifier, setting); |
| } |
| |
| base::WeakPtr<HostContentSettingsMap> HostContentSettingsMap::GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| void HostContentSettingsMap::SetClockForTesting(base::Clock* clock) { |
| pref_provider_->SetClockForTesting(clock); |
| } |
| |
| void HostContentSettingsMap::RecordExceptionMetrics() { |
| auto* content_setting_registry = |
| content_settings::ContentSettingsRegistry::GetInstance(); |
| for (const content_settings::WebsiteSettingsInfo* info : |
| *content_settings::WebsiteSettingsRegistry::GetInstance()) { |
| ContentSettingsType content_type = info->type(); |
| const std::string type_name = info->name(); |
| |
| ContentSettingsForOneType settings; |
| GetSettingsForOneType(content_type, std::string(), &settings); |
| size_t num_exceptions = 0; |
| base::flat_map<ContentSetting, size_t> num_exceptions_with_setting; |
| const content_settings::ContentSettingsInfo* content_info = |
| content_setting_registry->Get(content_type); |
| for (const ContentSettingPatternSource& setting_entry : settings) { |
| // Skip default settings. |
| if (setting_entry.primary_pattern == ContentSettingsPattern::Wildcard() && |
| setting_entry.secondary_pattern == |
| ContentSettingsPattern::Wildcard()) { |
| continue; |
| } |
| |
| ContentSettingsPattern::SchemeType scheme = |
| setting_entry.primary_pattern.GetScheme(); |
| UMA_HISTOGRAM_ENUMERATION("ContentSettings.ExceptionScheme", scheme, |
| ContentSettingsPattern::SCHEME_MAX); |
| |
| if (scheme == ContentSettingsPattern::SCHEME_FILE) { |
| UMA_HISTOGRAM_BOOLEAN("ContentSettings.ExceptionSchemeFile.HasPath", |
| setting_entry.primary_pattern.HasPath()); |
| size_t num_values; |
| int histogram_value = |
| ContentSettingTypeToHistogramValue(content_type, &num_values); |
| if (setting_entry.primary_pattern.HasPath()) { |
| UMA_HISTOGRAM_EXACT_LINEAR( |
| "ContentSettings.ExceptionSchemeFile.Type.WithPath", |
| histogram_value, num_values); |
| } else { |
| UMA_HISTOGRAM_EXACT_LINEAR( |
| "ContentSettings.ExceptionSchemeFile.Type.WithoutPath", |
| histogram_value, num_values); |
| } |
| } |
| |
| if (setting_entry.source == "preference") { |
| // |content_info| will be non-nullptr iff |content_type| is a content |
| // setting rather than a website setting. |
| if (content_info) |
| ++num_exceptions_with_setting[setting_entry.GetContentSetting()]; |
| ++num_exceptions; |
| } |
| } |
| |
| std::string histogram_name = |
| "ContentSettings.Exceptions." + type_name; |
| base::UmaHistogramCustomCounts(histogram_name, num_exceptions, 1, 1000, 30); |
| |
| // For some ContentSettingTypes, collect exception histograms broken out by |
| // ContentSetting. |
| if (ShouldCollectFineGrainedExceptionHistograms(content_type)) { |
| DCHECK(content_info); |
| for (int setting = 0; setting < CONTENT_SETTING_NUM_SETTINGS; ++setting) { |
| ContentSetting content_setting = IntToContentSetting(setting); |
| if (!content_info->IsSettingValid(content_setting)) |
| continue; |
| std::string histogram_with_suffix = |
| histogram_name + "." + ContentSettingToString(content_setting); |
| base::UmaHistogramCustomCounts( |
| histogram_with_suffix, num_exceptions_with_setting[content_setting], |
| 1, 1000, 30); |
| } |
| } |
| } |
| } |
| |
| void HostContentSettingsMap::AddObserver(content_settings::Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void HostContentSettingsMap::RemoveObserver( |
| content_settings::Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void HostContentSettingsMap::FlushLossyWebsiteSettings() { |
| prefs_->SchedulePendingLossyWrites(); |
| } |
| |
| void HostContentSettingsMap::ClearSettingsForOneType( |
| ContentSettingsType content_type) { |
| UsedContentSettingsProviders(); |
| for (const auto& provider_pair : content_settings_providers_) |
| provider_pair.second->ClearAllContentSettingsRules(content_type); |
| FlushLossyWebsiteSettings(); |
| } |
| |
| base::Time HostContentSettingsMap::GetSettingLastModifiedDate( |
| const ContentSettingsPattern& primary_pattern, |
| const ContentSettingsPattern& secondary_pattern, |
| ContentSettingsType content_type) const { |
| base::Time most_recent_time; |
| for (auto* provider : user_modifiable_providers_) { |
| base::Time time = provider->GetWebsiteSettingLastModified( |
| primary_pattern, secondary_pattern, content_type, std::string()); |
| most_recent_time = std::max(time, most_recent_time); |
| } |
| return most_recent_time; |
| } |
| |
| void HostContentSettingsMap::ClearSettingsForOneTypeWithPredicate( |
| ContentSettingsType content_type, |
| base::Time begin_time, |
| base::Time end_time, |
| const PatternSourcePredicate& pattern_predicate) { |
| if (pattern_predicate.is_null() && begin_time.is_null() && |
| (end_time.is_null() || end_time.is_max())) { |
| ClearSettingsForOneType(content_type); |
| return; |
| } |
| UsedContentSettingsProviders(); |
| ContentSettingsForOneType settings; |
| GetSettingsForOneType(content_type, std::string(), &settings); |
| for (const ContentSettingPatternSource& setting : settings) { |
| if (pattern_predicate.is_null() || |
| pattern_predicate.Run(setting.primary_pattern, |
| setting.secondary_pattern)) { |
| for (auto* provider : user_modifiable_providers_) { |
| base::Time last_modified = provider->GetWebsiteSettingLastModified( |
| setting.primary_pattern, setting.secondary_pattern, content_type, |
| std::string()); |
| if (last_modified >= begin_time && |
| (last_modified < end_time || end_time.is_null())) { |
| provider->SetWebsiteSetting(setting.primary_pattern, |
| setting.secondary_pattern, content_type, |
| std::string(), nullptr); |
| } |
| } |
| } |
| } |
| } |
| |
| void HostContentSettingsMap::OnContentSettingChanged( |
| const ContentSettingsPattern& primary_pattern, |
| const ContentSettingsPattern& secondary_pattern, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier) { |
| for (content_settings::Observer& observer : observers_) { |
| observer.OnContentSettingChanged(primary_pattern, secondary_pattern, |
| content_type, resource_identifier); |
| } |
| } |
| |
| HostContentSettingsMap::~HostContentSettingsMap() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!prefs_); |
| } |
| |
| void HostContentSettingsMap::ShutdownOnUIThread() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(prefs_); |
| prefs_ = nullptr; |
| for (const auto& provider_pair : content_settings_providers_) |
| provider_pair.second->ShutdownOnUIThread(); |
| } |
| |
| void HostContentSettingsMap::AddSettingsForOneType( |
| const content_settings::ProviderInterface* provider, |
| ProviderType provider_type, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier, |
| ContentSettingsForOneType* settings, |
| bool incognito) const { |
| std::unique_ptr<content_settings::RuleIterator> rule_iterator( |
| provider->GetRuleIterator(content_type, resource_identifier, incognito)); |
| if (!rule_iterator) |
| return; |
| |
| while (rule_iterator->HasNext()) { |
| content_settings::Rule rule = rule_iterator->Next(); |
| settings->emplace_back( |
| rule.primary_pattern, rule.secondary_pattern, std::move(rule.value), |
| kProviderNamesSourceMap[provider_type].provider_name, incognito); |
| } |
| } |
| |
| void HostContentSettingsMap::UsedContentSettingsProviders() const { |
| #ifndef NDEBUG |
| if (used_from_thread_id_ == base::kInvalidThreadId) |
| return; |
| |
| if (base::PlatformThread::CurrentId() != used_from_thread_id_) |
| used_from_thread_id_ = base::kInvalidThreadId; |
| #endif |
| } |
| |
| std::unique_ptr<base::Value> HostContentSettingsMap::GetWebsiteSetting( |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier, |
| content_settings::SettingInfo* info) const { |
| DCHECK(SupportsResourceIdentifier(content_type) || |
| resource_identifier.empty()); |
| |
| // Check if the requested setting is whitelisted. |
| // TODO(raymes): Move this into GetContentSetting. This has nothing to do with |
| // website settings |
| const content_settings::ContentSettingsInfo* content_settings_info = |
| content_settings::ContentSettingsRegistry::GetInstance()->Get( |
| content_type); |
| if (content_settings_info) { |
| for (const std::string& scheme : |
| content_settings_info->whitelisted_schemes()) { |
| DCHECK(SchemeCanBeWhitelisted(scheme)); |
| |
| if (primary_url.SchemeIs(scheme)) { |
| if (info) { |
| info->source = content_settings::SETTING_SOURCE_WHITELIST; |
| info->primary_pattern = ContentSettingsPattern::Wildcard(); |
| info->secondary_pattern = ContentSettingsPattern::Wildcard(); |
| } |
| return std::unique_ptr<base::Value>( |
| new base::Value(CONTENT_SETTING_ALLOW)); |
| } |
| } |
| } |
| |
| return GetWebsiteSettingInternal(primary_url, secondary_url, content_type, |
| resource_identifier, kFirstProvider, info); |
| } |
| |
| // static |
| HostContentSettingsMap::ProviderType |
| HostContentSettingsMap::GetProviderTypeFromSource(const std::string& source) { |
| for (size_t i = 0; i < base::size(kProviderNamesSourceMap); ++i) { |
| if (source == kProviderNamesSourceMap[i].provider_name) |
| return static_cast<ProviderType>(i); |
| } |
| |
| NOTREACHED(); |
| return DEFAULT_PROVIDER; |
| } |
| |
| std::unique_ptr<base::Value> HostContentSettingsMap::GetWebsiteSettingInternal( |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier, |
| ProviderType first_provider_to_search, |
| content_settings::SettingInfo* info) const { |
| UsedContentSettingsProviders(); |
| ContentSettingsPattern* primary_pattern = nullptr; |
| ContentSettingsPattern* secondary_pattern = nullptr; |
| if (info) { |
| primary_pattern = &info->primary_pattern; |
| secondary_pattern = &info->secondary_pattern; |
| } |
| |
| // The list of |content_settings_providers_| is ordered according to their |
| // precedence. |
| auto it = content_settings_providers_.lower_bound(first_provider_to_search); |
| for (; it != content_settings_providers_.end(); ++it) { |
| std::unique_ptr<base::Value> value = GetContentSettingValueAndPatterns( |
| it->second.get(), primary_url, secondary_url, content_type, |
| resource_identifier, is_incognito_, primary_pattern, secondary_pattern); |
| if (value) { |
| if (info) |
| info->source = kProviderNamesSourceMap[it->first].provider_source; |
| return value; |
| } |
| } |
| |
| if (info) { |
| info->source = content_settings::SETTING_SOURCE_NONE; |
| info->primary_pattern = ContentSettingsPattern(); |
| info->secondary_pattern = ContentSettingsPattern(); |
| } |
| return std::unique_ptr<base::Value>(); |
| } |
| |
| // static |
| std::unique_ptr<base::Value> |
| HostContentSettingsMap::GetContentSettingValueAndPatterns( |
| const content_settings::ProviderInterface* provider, |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier, |
| bool include_incognito, |
| ContentSettingsPattern* primary_pattern, |
| ContentSettingsPattern* secondary_pattern) { |
| if (include_incognito) { |
| // Check incognito-only specific settings. It's essential that the |
| // |RuleIterator| gets out of scope before we get a rule iterator for the |
| // normal mode. |
| std::unique_ptr<content_settings::RuleIterator> incognito_rule_iterator( |
| provider->GetRuleIterator(content_type, resource_identifier, |
| true /* incognito */)); |
| std::unique_ptr<base::Value> value = GetContentSettingValueAndPatterns( |
| incognito_rule_iterator.get(), primary_url, secondary_url, |
| primary_pattern, secondary_pattern); |
| if (value) |
| return value; |
| } |
| // No settings from the incognito; use the normal mode. |
| std::unique_ptr<content_settings::RuleIterator> rule_iterator( |
| provider->GetRuleIterator(content_type, resource_identifier, |
| false /* incognito */)); |
| std::unique_ptr<base::Value> value = GetContentSettingValueAndPatterns( |
| rule_iterator.get(), primary_url, secondary_url, primary_pattern, |
| secondary_pattern); |
| if (value && include_incognito) |
| value = ProcessIncognitoInheritanceBehavior(content_type, std::move(value)); |
| return value; |
| } |
| |
| // static |
| std::unique_ptr<base::Value> |
| HostContentSettingsMap::GetContentSettingValueAndPatterns( |
| content_settings::RuleIterator* rule_iterator, |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsPattern* primary_pattern, |
| ContentSettingsPattern* secondary_pattern) { |
| if (rule_iterator) { |
| while (rule_iterator->HasNext()) { |
| const content_settings::Rule& rule = rule_iterator->Next(); |
| if (rule.primary_pattern.Matches(primary_url) && |
| rule.secondary_pattern.Matches(secondary_url)) { |
| if (primary_pattern) |
| *primary_pattern = rule.primary_pattern; |
| if (secondary_pattern) |
| *secondary_pattern = rule.secondary_pattern; |
| return rule.value.CreateDeepCopy(); |
| } |
| } |
| } |
| return std::unique_ptr<base::Value>(); |
| } |
| |
| void HostContentSettingsMap::InitializePluginsDataSettings() { |
| if (!content_settings::WebsiteSettingsRegistry::GetInstance()->Get( |
| CONTENT_SETTINGS_TYPE_PLUGINS_DATA)) { |
| return; |
| } |
| ContentSettingsForOneType host_settings; |
| GetSettingsForOneType(CONTENT_SETTINGS_TYPE_PLUGINS_DATA, std::string(), |
| &host_settings); |
| if (host_settings.empty()) { |
| GetSettingsForOneType(CONTENT_SETTINGS_TYPE_PLUGINS, std::string(), |
| &host_settings); |
| for (ContentSettingPatternSource pattern : host_settings) { |
| if (pattern.source != "preference") |
| continue; |
| const GURL primary(pattern.primary_pattern.ToString()); |
| if (!primary.is_valid()) |
| continue; |
| DCHECK_EQ(ContentSettingsPattern::Relation::IDENTITY, |
| ContentSettingsPattern::Wildcard().Compare( |
| pattern.secondary_pattern)); |
| auto dict = std::make_unique<base::DictionaryValue>(); |
| constexpr char kFlagKey[] = "flashPreviouslyChanged"; |
| dict->SetKey(kFlagKey, base::Value(true)); |
| SetWebsiteSettingDefaultScope(primary, primary, |
| CONTENT_SETTINGS_TYPE_PLUGINS_DATA, |
| std::string(), std::move(dict)); |
| } |
| } |
| } |
| |
| void HostContentSettingsMap::MigrateRequestingAndTopLevelOriginSettings() { |
| content_settings::ContentSettingsRegistry* registry = |
| content_settings::ContentSettingsRegistry::GetInstance(); |
| for (const content_settings::ContentSettingsInfo* info : *registry) { |
| // Only 3 types should be migrated. |
| ContentSettingsType type = info->website_settings_info()->type(); |
| if (type != CONTENT_SETTINGS_TYPE_GEOLOCATION && |
| type != CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER && |
| type != CONTENT_SETTINGS_TYPE_MIDI_SYSEX) { |
| continue; |
| } |
| |
| ContentSettingsForOneType host_settings; |
| GetSettingsForOneType(type, std::string(), &host_settings); |
| for (ContentSettingPatternSource pattern : host_settings) { |
| if (pattern.source != "preference") |
| continue; |
| |
| // Users were never allowed to add user-specified patterns for these types |
| // so we can assume they are all origin scoped. |
| DCHECK(GURL(pattern.primary_pattern.ToString()).is_valid()); |
| DCHECK(GURL(pattern.secondary_pattern.ToString()).is_valid()); |
| |
| if (pattern.secondary_pattern.IsValid() && |
| pattern.secondary_pattern != pattern.primary_pattern && |
| pattern.secondary_pattern != ContentSettingsPattern::Wildcard()) { |
| SetContentSettingCustomScope(pattern.primary_pattern, |
| pattern.secondary_pattern, type, |
| std::string(), CONTENT_SETTING_DEFAULT); |
| // Also clear the setting for the top level origin so that the user |
| // receives another prompt. This is necessary in case they have allowed |
| // the top level origin but blocked an embedded origin in which case |
| // they should have another opportunity to block a request from an |
| // embedded origin. |
| SetContentSettingCustomScope(pattern.secondary_pattern, |
| pattern.secondary_pattern, type, |
| std::string(), CONTENT_SETTING_DEFAULT); |
| SetContentSettingCustomScope(pattern.secondary_pattern, |
| ContentSettingsPattern::Wildcard(), type, |
| std::string(), CONTENT_SETTING_DEFAULT); |
| } |
| } |
| } |
| } |