| // 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/content_settings_default_provider.h" |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/auto_reset.h" |
| #include "base/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "components/content_settings/core/browser/content_settings_info.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/website_settings_info.h" |
| #include "components/content_settings/core/browser/website_settings_registry.h" |
| #include "components/content_settings/core/common/content_settings.h" |
| #include "components/content_settings/core/common/content_settings_pattern.h" |
| #include "components/content_settings/core/common/content_settings_utils.h" |
| #include "components/content_settings/core/common/pref_names.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_registry.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/prefs/scoped_user_pref_update.h" |
| #include "url/gurl.h" |
| |
| namespace content_settings { |
| |
| namespace { |
| |
| // These settings are no longer used, and should be deleted on profile startup. |
| #if !defined(OS_IOS) |
| const char kObsoleteFullscreenDefaultPref[] = |
| "profile.default_content_setting_values.fullscreen"; |
| #if !defined(OS_ANDROID) |
| const char kObsoleteMouseLockDefaultPref[] = |
| "profile.default_content_setting_values.mouselock"; |
| #endif // !defined(OS_ANDROID) |
| #endif // !defined(OS_IOS) |
| |
| ContentSetting GetDefaultValue(const WebsiteSettingsInfo* info) { |
| const base::Value* initial_default = info->initial_default_value(); |
| if (!initial_default) |
| return CONTENT_SETTING_DEFAULT; |
| int result = 0; |
| bool success = initial_default->GetAsInteger(&result); |
| DCHECK(success); |
| return static_cast<ContentSetting>(result); |
| } |
| |
| ContentSetting GetDefaultValue(ContentSettingsType type) { |
| return GetDefaultValue(WebsiteSettingsRegistry::GetInstance()->Get(type)); |
| } |
| |
| const std::string& GetPrefName(ContentSettingsType type) { |
| return WebsiteSettingsRegistry::GetInstance() |
| ->Get(type) |
| ->default_value_pref_name(); |
| } |
| |
| class DefaultRuleIterator : public RuleIterator { |
| public: |
| explicit DefaultRuleIterator(const base::Value* value) { |
| if (value) |
| value_ = value->Clone(); |
| else |
| is_done_ = true; |
| } |
| |
| bool HasNext() const override { return !is_done_; } |
| |
| Rule Next() override { |
| DCHECK(HasNext()); |
| is_done_ = true; |
| return Rule(ContentSettingsPattern::Wildcard(), |
| ContentSettingsPattern::Wildcard(), std::move(value_)); |
| } |
| |
| private: |
| bool is_done_ = false; |
| base::Value value_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DefaultRuleIterator); |
| }; |
| |
| } // namespace |
| |
| // static |
| void DefaultProvider::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| // Register the default settings' preferences. |
| WebsiteSettingsRegistry* website_settings = |
| WebsiteSettingsRegistry::GetInstance(); |
| for (const WebsiteSettingsInfo* info : *website_settings) { |
| registry->RegisterIntegerPref(info->default_value_pref_name(), |
| GetDefaultValue(info), |
| info->GetPrefRegistrationFlags()); |
| } |
| |
| // Obsolete prefs ------------------------------------------------------- |
| |
| // These prefs have been removed, but need to be registered so they can |
| // be deleted on startup. |
| #if !defined(OS_IOS) |
| registry->RegisterIntegerPref( |
| kObsoleteFullscreenDefaultPref, 0, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
| #if !defined(OS_ANDROID) |
| registry->RegisterIntegerPref( |
| kObsoleteMouseLockDefaultPref, 0, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
| #endif // !defined(OS_ANDROID) |
| #endif // !defined(OS_IOS) |
| } |
| |
| DefaultProvider::DefaultProvider(PrefService* prefs, bool incognito) |
| : prefs_(prefs), |
| is_incognito_(incognito), |
| updating_preferences_(false) { |
| DCHECK(prefs_); |
| |
| // Remove the obsolete preferences from the pref file. |
| DiscardObsoletePreferences(); |
| |
| // Read global defaults. |
| ReadDefaultSettings(); |
| |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultCookiesSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_COOKIES))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultPopupsSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_POPUPS))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| #if !defined(OS_IOS) && !defined(OS_ANDROID) |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultImagesSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_IMAGES))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultPluginsSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_PLUGINS))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| #endif |
| |
| #if !defined(OS_IOS) |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultJavaScriptSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_JAVASCRIPT))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultLocationSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_GEOLOCATION))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultNotificationsSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_NOTIFICATIONS))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultMediaStreamMicSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultMediaStreamCameraSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultMIDISysExSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_MIDI_SYSEX))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultWebBluetoothGuardSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_BLUETOOTH_GUARD))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultAutoplaySetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_AUTOPLAY))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION("ContentSettings.DefaultSubresourceFilterSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_ADS))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION("ContentSettings.DefaultSoundSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_SOUND))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION("ContentSettings.DefaultUsbGuardSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_USB_GUARD))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| #endif |
| pref_change_registrar_.Init(prefs_); |
| PrefChangeRegistrar::NamedChangeCallback callback = base::Bind( |
| &DefaultProvider::OnPreferenceChanged, base::Unretained(this)); |
| WebsiteSettingsRegistry* website_settings = |
| WebsiteSettingsRegistry::GetInstance(); |
| for (const WebsiteSettingsInfo* info : *website_settings) |
| pref_change_registrar_.Add(info->default_value_pref_name(), callback); |
| } |
| |
| DefaultProvider::~DefaultProvider() { |
| } |
| |
| bool DefaultProvider::SetWebsiteSetting( |
| const ContentSettingsPattern& primary_pattern, |
| const ContentSettingsPattern& secondary_pattern, |
| ContentSettingsType content_type, |
| const ResourceIdentifier& resource_identifier, |
| base::Value* in_value) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(prefs_); |
| |
| // Ignore non default settings |
| if (primary_pattern != ContentSettingsPattern::Wildcard() || |
| secondary_pattern != ContentSettingsPattern::Wildcard()) { |
| return false; |
| } |
| |
| // Put |in_value| in a scoped pointer to ensure that it gets cleaned up |
| // properly if we don't pass on the ownership. |
| std::unique_ptr<base::Value> value(in_value); |
| |
| // The default settings may not be directly modified for OTR sessions. |
| // Instead, they are synced to the main profile's setting. |
| if (is_incognito_) |
| return true; |
| |
| { |
| base::AutoReset<bool> auto_reset(&updating_preferences_, true); |
| // Lock the memory map access, so that values are not read by |
| // |GetRuleIterator| at the same time as they are written here. Do not lock |
| // the preference access though; preference updates send out notifications |
| // whose callbacks may try to reacquire the lock on the same thread. |
| { |
| base::AutoLock lock(lock_); |
| ChangeSetting(content_type, value.get()); |
| } |
| WriteToPref(content_type, value.get()); |
| } |
| |
| NotifyObservers(ContentSettingsPattern(), |
| ContentSettingsPattern(), |
| content_type, |
| ResourceIdentifier()); |
| |
| return true; |
| } |
| |
| std::unique_ptr<RuleIterator> DefaultProvider::GetRuleIterator( |
| ContentSettingsType content_type, |
| const ResourceIdentifier& resource_identifier, |
| bool incognito) const { |
| // The default provider never has incognito-specific settings. |
| if (incognito) |
| return nullptr; |
| |
| base::AutoLock lock(lock_); |
| if (!resource_identifier.empty()) |
| return nullptr; |
| |
| auto it = default_settings_.find(content_type); |
| if (it == default_settings_.end()) { |
| NOTREACHED(); |
| return nullptr; |
| } |
| return std::make_unique<DefaultRuleIterator>(it->second.get()); |
| } |
| |
| void DefaultProvider::ClearAllContentSettingsRules( |
| ContentSettingsType content_type) { |
| // TODO(markusheintz): This method is only called when the |
| // |DesktopNotificationService| calls |ClearAllSettingsForType| method on the |
| // |HostContentSettingsMap|. Don't implement this method yet, otherwise the |
| // default notification settings will be cleared as well. |
| } |
| |
| void DefaultProvider::ShutdownOnUIThread() { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(prefs_); |
| RemoveAllObservers(); |
| pref_change_registrar_.RemoveAll(); |
| prefs_ = nullptr; |
| } |
| |
| void DefaultProvider::ReadDefaultSettings() { |
| base::AutoLock lock(lock_); |
| WebsiteSettingsRegistry* website_settings = |
| WebsiteSettingsRegistry::GetInstance(); |
| for (const WebsiteSettingsInfo* info : *website_settings) |
| ChangeSetting(info->type(), ReadFromPref(info->type()).get()); |
| } |
| |
| bool DefaultProvider::IsValueEmptyOrDefault(ContentSettingsType content_type, |
| base::Value* value) { |
| return !value || |
| ValueToContentSetting(value) == GetDefaultValue(content_type); |
| } |
| |
| void DefaultProvider::ChangeSetting(ContentSettingsType content_type, |
| base::Value* value) { |
| const ContentSettingsInfo* info = |
| ContentSettingsRegistry::GetInstance()->Get(content_type); |
| DCHECK(!info || !value || |
| info->IsDefaultSettingValid(ValueToContentSetting(value))); |
| default_settings_[content_type] = |
| value ? base::WrapUnique(value->DeepCopy()) |
| : ContentSettingToValue(GetDefaultValue(content_type)); |
| } |
| |
| void DefaultProvider::WriteToPref(ContentSettingsType content_type, |
| base::Value* value) { |
| if (IsValueEmptyOrDefault(content_type, value)) { |
| prefs_->ClearPref(GetPrefName(content_type)); |
| return; |
| } |
| |
| int int_value = GetDefaultValue(content_type); |
| bool is_integer = value->GetAsInteger(&int_value); |
| DCHECK(is_integer); |
| prefs_->SetInteger(GetPrefName(content_type), int_value); |
| } |
| |
| void DefaultProvider::OnPreferenceChanged(const std::string& name) { |
| DCHECK(CalledOnValidThread()); |
| if (updating_preferences_) |
| return; |
| |
| // Find out which content setting the preference corresponds to. |
| ContentSettingsType content_type = CONTENT_SETTINGS_TYPE_DEFAULT; |
| |
| WebsiteSettingsRegistry* website_settings = |
| WebsiteSettingsRegistry::GetInstance(); |
| for (const WebsiteSettingsInfo* info : *website_settings) { |
| if (info->default_value_pref_name() == name) { |
| content_type = info->type(); |
| break; |
| } |
| } |
| |
| if (content_type == CONTENT_SETTINGS_TYPE_DEFAULT) { |
| NOTREACHED() << "A change of the preference " << name << " was observed, " |
| "but the preference could not be mapped to a content " |
| "settings type."; |
| return; |
| } |
| |
| { |
| base::AutoReset<bool> auto_reset(&updating_preferences_, true); |
| // Lock the memory map access, so that values are not read by |
| // |GetRuleIterator| at the same time as they are written here. Do not lock |
| // the preference access though; preference updates send out notifications |
| // whose callbacks may try to reacquire the lock on the same thread. |
| { |
| base::AutoLock lock(lock_); |
| ChangeSetting(content_type, ReadFromPref(content_type).get()); |
| } |
| } |
| |
| NotifyObservers(ContentSettingsPattern(), |
| ContentSettingsPattern(), |
| content_type, |
| ResourceIdentifier()); |
| } |
| |
| std::unique_ptr<base::Value> DefaultProvider::ReadFromPref( |
| ContentSettingsType content_type) { |
| int int_value = prefs_->GetInteger(GetPrefName(content_type)); |
| return ContentSettingToValue(IntToContentSetting(int_value)); |
| } |
| |
| void DefaultProvider::DiscardObsoletePreferences() { |
| if (is_incognito_) |
| return; |
| // These prefs were never stored on iOS/Android so they don't need to be |
| // deleted. |
| #if !defined(OS_IOS) |
| prefs_->ClearPref(kObsoleteFullscreenDefaultPref); |
| #if !defined(OS_ANDROID) |
| prefs_->ClearPref(kObsoleteMouseLockDefaultPref); |
| |
| // ALLOW-by-default is an obsolete pref value for plugins (Flash). Erase that |
| // pref and fall back to the default behavior - but preserve other values. |
| const std::string& plugins_pref = GetPrefName(CONTENT_SETTINGS_TYPE_PLUGINS); |
| if (IntToContentSetting(prefs_->GetInteger(plugins_pref)) == |
| ContentSetting::CONTENT_SETTING_ALLOW) { |
| prefs_->ClearPref(plugins_pref); |
| } |
| #endif // !defined(OS_ANDROID) |
| #endif // !defined(OS_IOS) |
| } |
| |
| } // namespace content_settings |