| // 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 <string> |
| #include <vector> |
| |
| #include "base/auto_reset.h" |
| #include "base/basictypes.h" |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/prefs/pref_registry.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/prefs/scoped_user_pref_update.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/pref_names.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "url/gurl.h" |
| |
| namespace content_settings { |
| |
| namespace { |
| |
| // Obsolete prefs to be removed from the pref file. |
| // TODO(msramek): Remove this cleanup code after two releases (i.e. in M48). |
| const char kObsoleteDefaultContentSettings[] = |
| "profile.default_content_settings"; |
| const char kObsoleteMigratedDefaultContentSettings[] = |
| "profile.migrated_default_content_settings"; |
| const char kObsoleteMigratedDefaultMediaStreamSetting[] = |
| "profile.migrated_default_media_stream_content_settings"; |
| // TODO(msramek): Remove this cleanup code after two releases (i.e. in M50). |
| const char kObsoleteMetroSwitchToDesktopSetting[] = |
| "profile.default_content_setting_values.metro_switch_to_desktop"; |
| |
| 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_.reset(value->DeepCopy()); |
| } |
| |
| bool HasNext() const override { return value_.get() != NULL; } |
| |
| Rule Next() override { |
| DCHECK(value_.get()); |
| return Rule(ContentSettingsPattern::Wildcard(), |
| ContentSettingsPattern::Wildcard(), |
| value_.release()); |
| } |
| |
| private: |
| scoped_ptr<base::Value> value_; |
| }; |
| |
| } // 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 ------------------------------------------------------- |
| |
| // The deprecated dictionary preference. |
| registry->RegisterDictionaryPref( |
| kObsoleteDefaultContentSettings, |
| new base::DictionaryValue(), |
| user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
| |
| // Whether the deprecated dictionary preference has already been migrated |
| // into the individual preferences in this profile. |
| registry->RegisterBooleanPref( |
| kObsoleteMigratedDefaultContentSettings, |
| false, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
| |
| // Whether the deprecated mediastream default setting has already been |
| // migrated into microphone and camera default settings. |
| registry->RegisterBooleanPref(kObsoleteMigratedDefaultMediaStreamSetting, |
| false); |
| |
| // The removed content settings type METRO_SWITCH_TO_DESKTOP. |
| registry->RegisterIntegerPref( |
| kObsoleteMetroSwitchToDesktopSetting, |
| 0, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
| } |
| |
| 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.DefaultImagesSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_IMAGES))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultJavaScriptSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_JAVASCRIPT))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultPluginsSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_PLUGINS))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultPopupsSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_POPUPS))), |
| 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.DefaultMouseCursorSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_MOUSELOCK))), |
| 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.DefaultPushMessagingSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_PUSH_MESSAGING))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultKeygenSetting", |
| IntToContentSetting(prefs_->GetInteger( |
| GetPrefName(CONTENT_SETTINGS_TYPE_KEYGEN))), |
| CONTENT_SETTING_NUM_SETTINGS); |
| |
| 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; |
| } |
| |
| // 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 false; |
| |
| // Put |in_value| in a scoped pointer to ensure that it gets cleaned up |
| // properly if we don't pass on the ownership. |
| scoped_ptr<base::Value> value(in_value); |
| { |
| 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; |
| } |
| |
| RuleIterator* DefaultProvider::GetRuleIterator( |
| ContentSettingsType content_type, |
| const ResourceIdentifier& resource_identifier, |
| bool incognito) const { |
| base::AutoLock lock(lock_); |
| if (resource_identifier.empty()) { |
| auto it(default_settings_.find(content_type)); |
| if (it != default_settings_.end()) |
| return new DefaultRuleIterator(it->second); |
| NOTREACHED(); |
| } |
| return new EmptyRuleIterator(); |
| } |
| |
| 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_ = NULL; |
| } |
| |
| 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) { |
| if (!value) return true; |
| return ValueToContentSetting(value) == GetDefaultValue(content_type); |
| } |
| |
| void DefaultProvider::ChangeSetting(ContentSettingsType content_type, |
| base::Value* value) { |
| if (!value) { |
| default_settings_.set(content_type, |
| ContentSettingToValue(GetDefaultValue(content_type))); |
| } else { |
| default_settings_.set(content_type, make_scoped_ptr(value->DeepCopy())); |
| } |
| } |
| |
| 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()); |
| } |
| |
| scoped_ptr<base::Value> DefaultProvider::ReadFromPref( |
| ContentSettingsType content_type) { |
| int int_value = prefs_->GetInteger(GetPrefName(content_type)); |
| return ContentSettingToValue(IntToContentSetting(int_value)).Pass(); |
| } |
| |
| void DefaultProvider::DiscardObsoletePreferences() { |
| prefs_->ClearPref(kObsoleteDefaultContentSettings); |
| prefs_->ClearPref(kObsoleteMigratedDefaultContentSettings); |
| prefs_->ClearPref(kObsoleteMigratedDefaultMediaStreamSetting); |
| prefs_->ClearPref(kObsoleteMetroSwitchToDesktopSetting); |
| } |
| |
| } // namespace content_settings |