diff options
author | msramek <msramek@chromium.org> | 2015-04-02 21:57:21 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-03 04:58:07 +0000 |
commit | c2bda48205f8246990caf262f87f52dd971c78a5 (patch) | |
tree | b5676d5a12c3828c77af04759fcc2d50ff139b6a /components/content_settings | |
parent | b7f527b44458aa70b8b7c52f4de229f6f243e190 (diff) | |
download | chromium_src-c2bda48205f8246990caf262f87f52dd971c78a5.zip chromium_src-c2bda48205f8246990caf262f87f52dd971c78a5.tar.gz chromium_src-c2bda48205f8246990caf262f87f52dd971c78a5.tar.bz2 |
Split the aggregate dictionary that contains content settings exceptions for all content types into separate dictionary holding one content type each.
Old dictionary entry example:
"www.google.com,*": {"cookies": <value>, "plugins": <value>, last_used: {"cookies": <timestamp>, "plugins": <timestamp>}, "per_plugin": {<resource_id>: <value>}}
Split into separate dictionaries:
cookies:
"www.google.com,*": {"setting": <value>, "last_used": <timestamp>}
plugins:
"www.google.com,*": {"setting": <value>, "last_used": <timestamp>, "per_resource": {<resource_id>: <value>}}
For migration between versions of Chrome, we use a similar logic as when we migrated the default preferences in https://codereview.chromium.org/1004733003/ .
1. On the first run, migrate all settings (PrefProvider::MigrateAllExceptions()).
2. Whenever the old dictionary preference changes, propagate the syncable entries into the new preferences (PrefProvider::OnOldContentSettingsPatternPairsChanged()).
3. Whenever one of the new preferences changes, and it is syncable, we write it back to the old dictionary preference as well (ContentSettingsPref::OnPrefChanged()).
Note that PrefProvider manages its old dictionary preference the same way as ContentSettingsPref does a new preference, and ContentSettingsPref needs access to the old preference as well, which leads to some duplication in the code (UpdateOldPref() is just a modified version of UpdatePref(); both use some of the same constructs). We use the convention of referring to attributes and methods managing the old dictionary preference as "old" in the name.
BUG=452388
Review URL: https://codereview.chromium.org/1005303003
Cr-Commit-Position: refs/heads/master@{#323585}
Diffstat (limited to 'components/content_settings')
8 files changed, 856 insertions, 208 deletions
diff --git a/components/content_settings/core/browser/content_settings_pref.cc b/components/content_settings/core/browser/content_settings_pref.cc index a643697..7cfd31a 100644 --- a/components/content_settings/core/browser/content_settings_pref.cc +++ b/components/content_settings/core/browser/content_settings_pref.cc @@ -21,8 +21,8 @@ namespace { -typedef std::pair<std::string, std::string> StringPair; - +const char kSettingPath[] = "setting"; +const char kPerResourceIdentifierPrefName[] = "per_resource"; const char kPerPluginPrefName[] = "per_plugin"; const char kLastUsed[] = "last_used"; @@ -39,13 +39,8 @@ ContentSetting FixObsoleteCookiePromptMode(ContentSettingsType content_type, // returns true and sets |pref_key| to the key in the content settings // dictionary under which per-resource content settings are stored. // Otherwise, returns false. -bool GetResourceTypeName(ContentSettingsType content_type, - std::string* pref_key) { - if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) { - *pref_key = kPerPluginPrefName; - return true; - } - return false; +bool SupportsResourceIdentifiers(ContentSettingsType content_type) { + return content_type == CONTENT_SETTINGS_TYPE_PLUGINS; } } // namespace @@ -53,64 +48,66 @@ bool GetResourceTypeName(ContentSettingsType content_type, namespace content_settings { ContentSettingsPref::ContentSettingsPref( + ContentSettingsType content_type, PrefService* prefs, PrefChangeRegistrar* registrar, - base::Clock* clock, + const char* pref_name, bool incognito, + bool* updating_old_preferences_flag, NotifyObserversCallback notify_callback) - : prefs_(prefs), - clock_(clock), + : content_type_(content_type), + prefs_(prefs), registrar_(registrar), + pref_name_(pref_name), is_incognito_(incognito), updating_preferences_(false), + updating_old_preferences_(updating_old_preferences_flag), notify_callback_(notify_callback) { DCHECK(prefs_); - // Read content settings exceptions. - ReadContentSettingsFromPref(); + // If the migration hasn't happened yet, or if this content setting + // is syncable, the parent |PrefProvider| is going to copy the contents + // of the old preference to this new preference. There is no need + // to initialize this preference separately (in fact, in the case + // of migration, we would be writing the empty new preference back to the + // old one, erasing it). + if (prefs_->GetBoolean(prefs::kMigratedContentSettingsPatternPairs) && + !IsContentSettingsTypeSyncable(content_type_)) { + ReadContentSettingsFromPrefAndWriteToOldPref(); + } - registrar->Add( - prefs::kContentSettingsPatternPairs, - base::Bind(&ContentSettingsPref::OnContentSettingsPatternPairsChanged, - base::Unretained(this))); + registrar_->Add( + pref_name_, + base::Bind(&ContentSettingsPref::OnPrefChanged, base::Unretained(this))); } ContentSettingsPref::~ContentSettingsPref() { } RuleIterator* ContentSettingsPref::GetRuleIterator( - ContentSettingsType content_type, const ResourceIdentifier& resource_identifier, bool incognito) const { if (incognito) - return incognito_value_map_.GetRuleIterator(content_type, + return incognito_value_map_.GetRuleIterator(content_type_, resource_identifier, &lock_); - return value_map_.GetRuleIterator(content_type, resource_identifier, &lock_); + return value_map_.GetRuleIterator(content_type_, resource_identifier, &lock_); } bool ContentSettingsPref::SetWebsiteSetting( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, - ContentSettingsType content_type, const ResourceIdentifier& resource_identifier, base::Value* in_value) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(prefs_); - - // Default settings are set using a wildcard pattern for both - // |primary_pattern| and |secondary_pattern|. Don't store default settings in - // the |PrefProvider|. The |PrefProvider| handles settings for specific - // sites/origins defined by the |primary_pattern| and the |secondary_pattern|. - // Default settings are handled by the |DefaultProvider|. - if (primary_pattern == ContentSettingsPattern::Wildcard() && - secondary_pattern == ContentSettingsPattern::Wildcard() && - resource_identifier.empty()) { - return false; - } + DCHECK(primary_pattern != ContentSettingsPattern::Wildcard() || + secondary_pattern != ContentSettingsPattern::Wildcard() || + !resource_identifier.empty()); // At this point take the ownership of the |in_value|. scoped_ptr<base::Value> value(in_value); + // Update in memory value map. OriginIdentifierValueMap* map_to_modify = &incognito_value_map_; if (!is_incognito_) @@ -122,14 +119,14 @@ bool ContentSettingsPref::SetWebsiteSetting( map_to_modify->SetValue( primary_pattern, secondary_pattern, - content_type, + content_type_, resource_identifier, value->DeepCopy()); } else { map_to_modify->DeleteValue( primary_pattern, secondary_pattern, - content_type, + content_type_, resource_identifier); } } @@ -137,19 +134,23 @@ bool ContentSettingsPref::SetWebsiteSetting( if (!is_incognito_) { UpdatePref(primary_pattern, secondary_pattern, - content_type, resource_identifier, value.get()); + if (IsContentSettingsTypeSyncable(content_type_)) { + UpdateOldPref(primary_pattern, + secondary_pattern, + resource_identifier, + value.get()); + } } notify_callback_.Run( - primary_pattern, secondary_pattern, content_type, resource_identifier); + primary_pattern, secondary_pattern, content_type_, resource_identifier); return true; } -void ContentSettingsPref::ClearAllContentSettingsRules( - ContentSettingsType content_type) { +void ContentSettingsPref::ClearAllContentSettingsRules() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(prefs_); @@ -161,32 +162,37 @@ void ContentSettingsPref::ClearAllContentSettingsRules( { base::AutoLock auto_lock(lock_); scoped_ptr<RuleIterator> rule_iterator( - map_to_modify->GetRuleIterator(content_type, std::string(), NULL)); + map_to_modify->GetRuleIterator(content_type_, std::string(), NULL)); // Copy the rules; we cannot call |UpdatePref| while holding |lock_|. while (rule_iterator->HasNext()) rules_to_delete.push_back(rule_iterator->Next()); - map_to_modify->DeleteValues(content_type, std::string()); + map_to_modify->DeleteValues(content_type_, std::string()); } for (std::vector<Rule>::const_iterator it = rules_to_delete.begin(); it != rules_to_delete.end(); ++it) { UpdatePref(it->primary_pattern, it->secondary_pattern, - content_type, std::string(), NULL); + if (IsContentSettingsTypeSyncable(content_type_)) { + UpdateOldPref(it->primary_pattern, + it->secondary_pattern, + std::string(), + NULL); + } } notify_callback_.Run(ContentSettingsPattern(), ContentSettingsPattern(), - content_type, + content_type_, std::string()); } void ContentSettingsPref::UpdateLastUsage( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, - ContentSettingsType content_type) { + base::Clock* clock) { // Don't write if in incognito. if (is_incognito_) { return; @@ -198,7 +204,7 @@ void ContentSettingsPref::UpdateLastUsage( base::AutoReset<bool> auto_reset(&updating_preferences_, true); { - DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs); + DictionaryPrefUpdate update(prefs_, pref_name_); base::DictionaryValue* pattern_pairs_settings = update.Get(); std::string pattern_str( @@ -213,28 +219,16 @@ void ContentSettingsPref::UpdateLastUsage( settings_dictionary); } - base::DictionaryValue* last_used_dictionary = NULL; - found = settings_dictionary->GetDictionaryWithoutPathExpansion( - kLastUsed, &last_used_dictionary); - - if (!found) { - last_used_dictionary = new base::DictionaryValue; - settings_dictionary->SetWithoutPathExpansion(kLastUsed, - last_used_dictionary); - } - - std::string settings_path = GetTypeName(content_type); - last_used_dictionary->Set( - settings_path, new base::FundamentalValue(clock_->Now().ToDoubleT())); + settings_dictionary->SetWithoutPathExpansion( + kLastUsed, new base::FundamentalValue(clock->Now().ToDoubleT())); } } base::Time ContentSettingsPref::GetLastUsage( const ContentSettingsPattern& primary_pattern, - const ContentSettingsPattern& secondary_pattern, - ContentSettingsType content_type) { + const ContentSettingsPattern& secondary_pattern) { const base::DictionaryValue* pattern_pairs_settings = - prefs_->GetDictionary(prefs::kContentSettingsPatternPairs); + prefs_->GetDictionary(pref_name_); std::string pattern_str( CreatePatternString(primary_pattern, secondary_pattern)); @@ -245,16 +239,9 @@ base::Time ContentSettingsPref::GetLastUsage( if (!found) return base::Time(); - const base::DictionaryValue* last_used_dictionary = NULL; - found = settings_dictionary->GetDictionaryWithoutPathExpansion( - kLastUsed, &last_used_dictionary); - - if (!found) - return base::Time(); - double last_used_time; - found = last_used_dictionary->GetDoubleWithoutPathExpansion( - GetTypeName(content_type), &last_used_time); + found = settings_dictionary->GetDoubleWithoutPathExpansion( + kLastUsed, &last_used_time); if (!found) return base::Time(); @@ -266,22 +253,25 @@ size_t ContentSettingsPref::GetNumExceptions() { return value_map_.size(); } -void ContentSettingsPref::SetClockForTesting(base::Clock* clock) { - clock_ = clock; -} +void ContentSettingsPref::ReadContentSettingsFromPrefAndWriteToOldPref() { + // Clear the old preference, so we can copy the exceptions from the new + // preference into it. Note that copying in this direction is disallowed + // in incognito, to avoid the echo effect: New preference -> PrefProvider -> + // Old preference -> Incognito PrefProvider -> New preference -> etc. + if (!is_incognito_ && IsContentSettingsTypeSyncable(content_type_)) + ClearOldPreference(); -void ContentSettingsPref::ReadContentSettingsFromPref() { // |DictionaryPrefUpdate| sends out notifications when destructed. This // construction order ensures |AutoLock| gets destroyed first and |lock_| is // not held when the notifications are sent. Also, |auto_reset| must be still // valid when the notifications are sent, so that |Observe| skips the // notification. base::AutoReset<bool> auto_reset(&updating_preferences_, true); - DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs); + DictionaryPrefUpdate update(prefs_, pref_name_); base::AutoLock auto_lock(lock_); const base::DictionaryValue* all_settings_dictionary = - prefs_->GetDictionary(prefs::kContentSettingsPatternPairs); + prefs_->GetDictionary(pref_name_); value_map_.clear(); @@ -323,103 +313,115 @@ void ContentSettingsPref::ReadContentSettingsFromPref() { bool is_dictionary = i.value().GetAsDictionary(&settings_dictionary); DCHECK(is_dictionary); - for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { - ContentSettingsType content_type = static_cast<ContentSettingsType>(i); - - std::string res_dictionary_path; - if (GetResourceTypeName(content_type, &res_dictionary_path)) { - const base::DictionaryValue* resource_dictionary = NULL; - if (settings_dictionary->GetDictionary( - res_dictionary_path, &resource_dictionary)) { - for (base::DictionaryValue::Iterator j(*resource_dictionary); - !j.IsAtEnd(); - j.Advance()) { - const std::string& resource_identifier(j.key()); - int setting = CONTENT_SETTING_DEFAULT; - bool is_integer = j.value().GetAsInteger(&setting); - DCHECK(is_integer); - DCHECK_NE(CONTENT_SETTING_DEFAULT, setting); - value_map_.SetValue(pattern_pair.first, - pattern_pair.second, - content_type, - resource_identifier, - new base::FundamentalValue(setting)); + if (SupportsResourceIdentifiers(content_type_)) { + const base::DictionaryValue* resource_dictionary = NULL; + if (settings_dictionary->GetDictionary( + kPerResourceIdentifierPrefName, &resource_dictionary)) { + for (base::DictionaryValue::Iterator j(*resource_dictionary); + !j.IsAtEnd(); + j.Advance()) { + const std::string& resource_identifier(j.key()); + int setting = CONTENT_SETTING_DEFAULT; + bool is_integer = j.value().GetAsInteger(&setting); + DCHECK(is_integer); + DCHECK_NE(CONTENT_SETTING_DEFAULT, setting); + scoped_ptr<base::Value> setting_ptr( + new base::FundamentalValue(setting)); + value_map_.SetValue(pattern_pair.first, + pattern_pair.second, + content_type_, + resource_identifier, + setting_ptr->DeepCopy()); + if (!is_incognito_ && IsContentSettingsTypeSyncable(content_type_)) { + UpdateOldPref(pattern_pair.first, + pattern_pair.second, + resource_identifier, + setting_ptr.get()); } } } - base::Value* value = NULL; - if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type)) { - const base::DictionaryValue* setting = NULL; - // TODO(xians): Handle the non-dictionary types. - if (settings_dictionary->GetDictionaryWithoutPathExpansion( - GetTypeName(ContentSettingsType(i)), &setting)) { - DCHECK(!setting->empty()); - value = setting->DeepCopy(); - } - } else { - int setting = CONTENT_SETTING_DEFAULT; - if (settings_dictionary->GetIntegerWithoutPathExpansion( - GetTypeName(ContentSettingsType(i)), &setting)) { - DCHECK_NE(CONTENT_SETTING_DEFAULT, setting); - setting = FixObsoleteCookiePromptMode(content_type, - ContentSetting(setting)); - value = new base::FundamentalValue(setting); - } + } + base::Value* value = NULL; + if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type_)) { + const base::DictionaryValue* setting = NULL; + // TODO(xians): Handle the non-dictionary types. + if (settings_dictionary->GetDictionaryWithoutPathExpansion( + kSettingPath, &setting)) { + DCHECK(!setting->empty()); + value = setting->DeepCopy(); + } + } else { + int setting = CONTENT_SETTING_DEFAULT; + if (settings_dictionary->GetIntegerWithoutPathExpansion( + kSettingPath, &setting)) { + DCHECK_NE(CONTENT_SETTING_DEFAULT, setting); + setting = FixObsoleteCookiePromptMode(content_type_, + ContentSetting(setting)); + value = new base::FundamentalValue(setting); } + } - // |value_map_| will take the ownership of |value|. - if (value != NULL) { - value_map_.SetValue(pattern_pair.first, - pattern_pair.second, - content_type, - ResourceIdentifier(), - value); - if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) { - ContentSetting s = ValueToContentSetting(value); - switch (s) { - case CONTENT_SETTING_ALLOW : - ++cookies_allow_exception_count; - break; - case CONTENT_SETTING_BLOCK : - ++cookies_block_exception_count; - break; - case CONTENT_SETTING_SESSION_ONLY : - ++cookies_session_only_exception_count; - break; - default: - NOTREACHED(); - break; - } + if (value != NULL) { + scoped_ptr<base::Value> value_ptr(value); + value_map_.SetValue(pattern_pair.first, + pattern_pair.second, + content_type_, + ResourceIdentifier(), + value->DeepCopy()); + if (!is_incognito_ && IsContentSettingsTypeSyncable(content_type_)) { + UpdateOldPref(pattern_pair.first, + pattern_pair.second, + ResourceIdentifier(), + value_ptr.get()); + } + if (content_type_ == CONTENT_SETTINGS_TYPE_COOKIES) { + ContentSetting s = ValueToContentSetting(value); + switch (s) { + case CONTENT_SETTING_ALLOW : + ++cookies_allow_exception_count; + break; + case CONTENT_SETTING_BLOCK : + ++cookies_block_exception_count; + break; + case CONTENT_SETTING_SESSION_ONLY : + ++cookies_session_only_exception_count; + break; + default: + NOTREACHED(); + break; } } } + + } + + if (content_type_ == CONTENT_SETTINGS_TYPE_COOKIES) { + UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfBlockCookiesExceptions", + cookies_block_exception_count); + UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfAllowCookiesExceptions", + cookies_allow_exception_count); + UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfSessionOnlyCookiesExceptions", + cookies_session_only_exception_count); } - UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfBlockCookiesExceptions", - cookies_block_exception_count); - UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfAllowCookiesExceptions", - cookies_allow_exception_count); - UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfSessionOnlyCookiesExceptions", - cookies_session_only_exception_count); } -void ContentSettingsPref::OnContentSettingsPatternPairsChanged() { +void ContentSettingsPref::OnPrefChanged() { DCHECK(thread_checker_.CalledOnValidThread()); if (updating_preferences_) return; - ReadContentSettingsFromPref(); + ReadContentSettingsFromPrefAndWriteToOldPref(); notify_callback_.Run(ContentSettingsPattern(), ContentSettingsPattern(), - CONTENT_SETTINGS_TYPE_DEFAULT, + content_type_, std::string()); } void ContentSettingsPref::UpdatePref( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, - ContentSettingsType content_type, const ResourceIdentifier& resource_identifier, const base::Value* value) { // Ensure that |lock_| is not held by this thread, since this function will @@ -428,6 +430,83 @@ void ContentSettingsPref::UpdatePref( base::AutoReset<bool> auto_reset(&updating_preferences_, true); { + DictionaryPrefUpdate update(prefs_, pref_name_); + base::DictionaryValue* pattern_pairs_settings = update.Get(); + + // Get settings dictionary for the given patterns. + std::string pattern_str(CreatePatternString(primary_pattern, + secondary_pattern)); + base::DictionaryValue* settings_dictionary = NULL; + bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion( + pattern_str, &settings_dictionary); + + if (!found && value) { + settings_dictionary = new base::DictionaryValue; + pattern_pairs_settings->SetWithoutPathExpansion( + pattern_str, settings_dictionary); + } + + if (settings_dictionary) { + if (SupportsResourceIdentifiers(content_type_) && + !resource_identifier.empty()) { + base::DictionaryValue* resource_dictionary = NULL; + found = settings_dictionary->GetDictionary( + kPerResourceIdentifierPrefName, &resource_dictionary); + if (!found) { + if (value == NULL) + return; // Nothing to remove. Exit early. + resource_dictionary = new base::DictionaryValue; + settings_dictionary->Set( + kPerResourceIdentifierPrefName, resource_dictionary); + } + // Update resource dictionary. + if (value == NULL) { + resource_dictionary->RemoveWithoutPathExpansion(resource_identifier, + NULL); + if (resource_dictionary->empty()) { + settings_dictionary->RemoveWithoutPathExpansion( + kPerResourceIdentifierPrefName, NULL); + } + } else { + resource_dictionary->SetWithoutPathExpansion( + resource_identifier, value->DeepCopy()); + } + } else { + // Update settings dictionary. + if (value == NULL) { + settings_dictionary->RemoveWithoutPathExpansion(kSettingPath, NULL); + settings_dictionary->RemoveWithoutPathExpansion(kLastUsed, NULL); + } else { + settings_dictionary->SetWithoutPathExpansion( + kSettingPath, value->DeepCopy()); + } + } + // Remove the settings dictionary if it is empty. + if (settings_dictionary->empty()) { + pattern_pairs_settings->RemoveWithoutPathExpansion( + pattern_str, NULL); + } + } + } +} + +void ContentSettingsPref::UpdateOldPref( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + const ResourceIdentifier& resource_identifier, + const base::Value* value) { + DCHECK(IsContentSettingsTypeSyncable(content_type_)); + + // The incognito provider cannot write the settings to avoid echo effect: + // New preference -> PrefProvider -> Old preference -> + // -> Incognito PrefProvider -> New preference -> etc. + DCHECK(!is_incognito_); + + if (*updating_old_preferences_) + return; + + base::AutoReset<bool> auto_reset(updating_old_preferences_, true); + { DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs); base::DictionaryValue* pattern_pairs_settings = update.Get(); @@ -446,17 +525,16 @@ void ContentSettingsPref::UpdatePref( } if (settings_dictionary) { - std::string res_dictionary_path; - if (GetResourceTypeName(content_type, &res_dictionary_path) && + if (content_type_ == CONTENT_SETTINGS_TYPE_PLUGINS && !resource_identifier.empty()) { base::DictionaryValue* resource_dictionary = NULL; found = settings_dictionary->GetDictionary( - res_dictionary_path, &resource_dictionary); + kPerPluginPrefName, &resource_dictionary); if (!found) { if (value == NULL) return; // Nothing to remove. Exit early. resource_dictionary = new base::DictionaryValue; - settings_dictionary->Set(res_dictionary_path, resource_dictionary); + settings_dictionary->Set(kPerPluginPrefName, resource_dictionary); } // Update resource dictionary. if (value == NULL) { @@ -464,7 +542,7 @@ void ContentSettingsPref::UpdatePref( NULL); if (resource_dictionary->empty()) { settings_dictionary->RemoveWithoutPathExpansion( - res_dictionary_path, NULL); + kPerPluginPrefName, NULL); } } else { resource_dictionary->SetWithoutPathExpansion( @@ -472,7 +550,7 @@ void ContentSettingsPref::UpdatePref( } } else { // Update settings dictionary. - std::string setting_path = GetTypeName(content_type); + std::string setting_path = GetTypeName(content_type_); if (value == NULL) { settings_dictionary->RemoveWithoutPathExpansion(setting_path, NULL); @@ -491,6 +569,48 @@ void ContentSettingsPref::UpdatePref( } } +void ContentSettingsPref::ClearOldPreference() { + DCHECK(IsContentSettingsTypeSyncable(content_type_)); + + if (*updating_old_preferences_) + return; + + std::vector<std::string> keys; + + base::AutoReset<bool> auto_reset(updating_old_preferences_, true); + DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs); + base::DictionaryValue* old_dictionary = update.Get(); + + for (base::DictionaryValue::Iterator it(*old_dictionary); + !it.IsAtEnd(); it.Advance()) { + keys.push_back(it.key()); + } + + for (const std::string& key : keys) { + base::DictionaryValue* exception; + bool is_dictionary = + old_dictionary->GetDictionaryWithoutPathExpansion(key, &exception); + DCHECK(is_dictionary); + + exception->RemoveWithoutPathExpansion(GetTypeName(content_type_), NULL); + + base::DictionaryValue* last_used; + if (exception->GetDictionaryWithoutPathExpansion(kLastUsed, &last_used)) { + last_used->RemoveWithoutPathExpansion(GetTypeName(content_type_), NULL); + + if (last_used->empty()) + exception->RemoveWithoutPathExpansion(kLastUsed, NULL); + } + + if (content_type_ == CONTENT_SETTINGS_TYPE_PLUGINS) + exception->RemoveWithoutPathExpansion(kPerPluginPrefName, NULL); + + if (exception->empty()) + old_dictionary->RemoveWithoutPathExpansion(key, NULL); + } + +} + // static void ContentSettingsPref::CanonicalizeContentSettingsExceptions( base::DictionaryValue* all_settings_dictionary) { diff --git a/components/content_settings/core/browser/content_settings_pref.h b/components/content_settings/core/browser/content_settings_pref.h index a571a2a..1314e16 100644 --- a/components/content_settings/core/browser/content_settings_pref.h +++ b/components/content_settings/core/browser/content_settings_pref.h @@ -32,10 +32,7 @@ namespace content_settings { class RuleIterator; -// Represents a single pref for reading/writing content settings. -// TODO(raymes): Currently all content settings types are stored in a single -// instance of one of these. But the intention is that there will be one -// instance of this class per content settings type. +// Represents a single pref for reading/writing content settings of one type. class ContentSettingsPref { public: typedef base::Callback<void(const ContentSettingsPattern&, @@ -43,45 +40,46 @@ class ContentSettingsPref { ContentSettingsType, const std::string&)> NotifyObserversCallback; - ContentSettingsPref(PrefService* prefs, + ContentSettingsPref(ContentSettingsType content_type, + PrefService* prefs, PrefChangeRegistrar* registrar, - base::Clock* clock, + const char* pref_name, bool incognito, + bool* updating_old_preferences_flag, NotifyObserversCallback notify_callback); ~ContentSettingsPref(); - RuleIterator* GetRuleIterator(ContentSettingsType content_type, - const ResourceIdentifier& resource_identifier, + RuleIterator* GetRuleIterator(const ResourceIdentifier& resource_identifier, bool incognito) const; bool SetWebsiteSetting(const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, - ContentSettingsType content_type, const ResourceIdentifier& resource_identifier, base::Value* value); - void ClearAllContentSettingsRules(ContentSettingsType content_type); + void ClearAllContentSettingsRules(); void UpdateLastUsage(const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, - ContentSettingsType content_type); + base::Clock* clock); base::Time GetLastUsage(const ContentSettingsPattern& primary_pattern, - const ContentSettingsPattern& secondary_pattern, - ContentSettingsType content_type); + const ContentSettingsPattern& secondary_pattern); size_t GetNumExceptions(); - void SetClockForTesting(base::Clock* clock); private: - friend class DeadlockCheckerThread; // For testing. + // Only to access static method CanonicalizeContentSettingsExceptions, + // so that we reduce duplicity between the two. + // TODO(msramek): Remove this after the migration is over. + friend class PrefProvider; // Reads all content settings exceptions from the preference and load them // into the |value_map_|. The |value_map_| is cleared first. - void ReadContentSettingsFromPref(); + void ReadContentSettingsFromPrefAndWriteToOldPref(); // Callback for changes in the pref with the same name. - void OnContentSettingsPatternPairsChanged(); + void OnPrefChanged(); // Update the preference that stores content settings exceptions and syncs the // value to the obsolete preference. When calling this function, |lock_| @@ -90,7 +88,6 @@ class ContentSettingsPref { void UpdatePref( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, - ContentSettingsType content_type, const ResourceIdentifier& resource_identifier, const base::Value* value); @@ -102,21 +99,43 @@ class ContentSettingsPref { // release it. void AssertLockNotHeld() const; + // Update the old aggregate preference, so that the settings can be synced + // to old versions of Chrome. + // TODO(msramek): Remove after the migration is over. + void UpdateOldPref( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + const ResourceIdentifier& resource_identifier, + const base::Value* value); + + // Remove all exceptions of |content_type_| from the old aggregate dictionary + // preference. + // TODO(msramek): Remove after the migration is over. + void ClearOldPreference(); + + // The type of content settings stored in this pref. + ContentSettingsType content_type_; + // Weak; owned by the Profile and reset in ShutdownOnUIThread. PrefService* prefs_; // Owned by the PrefProvider. - base::Clock* clock_; - - // Owned by the PrefProvider. PrefChangeRegistrar* registrar_; + // Name of the dictionary preference managed by this class. + const char* pref_name_; + bool is_incognito_; // Whether we are currently updating preferences, this is used to ignore // notifications from the preferences service that we triggered ourself. bool updating_preferences_; + // Whether we are currently updating the old aggregate dictionary preference. + // Owned by the parent |PrefProvider| and shared by all its children + // |ContentSettingsPref|s. + bool* updating_old_preferences_; + OriginIdentifierValueMap value_map_; OriginIdentifierValueMap incognito_value_map_; diff --git a/components/content_settings/core/browser/content_settings_pref_provider.cc b/components/content_settings/core/browser/content_settings_pref_provider.cc index 6c855d3..f53acc3 100644 --- a/components/content_settings/core/browser/content_settings_pref_provider.cc +++ b/components/content_settings/core/browser/content_settings_pref_provider.cc @@ -8,10 +8,13 @@ #include <string> #include <utility> +#include "base/auto_reset.h" #include "base/bind.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "base/metrics/histogram.h" #include "base/prefs/pref_service.h" +#include "base/prefs/scoped_user_pref_update.h" #include "base/strings/string_split.h" #include "base/time/clock.h" #include "base/time/default_clock.h" @@ -26,9 +29,76 @@ namespace { +const char kPerPluginPrefName[] = "per_plugin"; + +// Returns true and sets |pref_key| to the key in the content settings +// dictionary under which per-resource content settings are stored, +// if the given content type supports resource identifiers in user preferences. +bool GetResourceTypeName(ContentSettingsType content_type, + std::string* pref_key) { + if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) { + *pref_key = kPerPluginPrefName; + return true; + } + return false; +} + +// TODO(msramek): Check if we still need this migration code. If not, remove it. +ContentSetting FixObsoleteCookiePromptMode(ContentSettingsType content_type, + ContentSetting setting) { + if (content_type == CONTENT_SETTINGS_TYPE_COOKIES && + setting == CONTENT_SETTING_ASK) { + return CONTENT_SETTING_BLOCK; + } + return setting; +} + +// A helper function to duplicate |ContentSettingsPattern|, so that +// |ReadContentSettingsFromOldPref| can export them in a vector. We cannot pass +// them by pointer, because the original values will go out of scope when +// the vector is used in |WriteSettingsToNewPreferences|. +ContentSettingsPattern CopyPattern(const ContentSettingsPattern& pattern) { + return ContentSettingsPattern::FromString(pattern.ToString()); +} + const char kAudioKey[] = "audio"; const char kVideoKey[] = "video"; +// A list of exception preferences corresponding to individual content settings +// types. Must be kept in sync with the enum |ContentSettingsType|. +const char* kContentSettingsExceptionsPrefs[] = { + prefs::kContentSettingsCookiesPatternPairs, + prefs::kContentSettingsImagesPatternPairs, + prefs::kContentSettingsJavaScriptPatternPairs, + prefs::kContentSettingsPluginsPatternPairs, + prefs::kContentSettingsPopupsPatternPairs, + prefs::kContentSettingsGeolocationPatternPairs, + prefs::kContentSettingsNotificationsPatternPairs, + prefs::kContentSettingsAutoSelectCertificatePatternPairs, + prefs::kContentSettingsFullScreenPatternPairs, + prefs::kContentSettingsMouseLockPatternPairs, + prefs::kContentSettingsMixedScriptPatternPairs, + prefs::kContentSettingsMediaStreamPatternPairs, + prefs::kContentSettingsMediaStreamMicPatternPairs, + prefs::kContentSettingsMediaStreamCameraPatternPairs, + prefs::kContentSettingsProtocolHandlersPatternPairs, + prefs::kContentSettingsPpapiBrokerPatternPairs, + prefs::kContentSettingsAutomaticDownloadsPatternPairs, + prefs::kContentSettingsMidiSysexPatternPairs, + prefs::kContentSettingsPushMessagingPatternPairs, + prefs::kContentSettingsSSLCertDecisionsPatternPairs, +#if defined(OS_WIN) + prefs::kContentSettingsMetroSwitchToDesktopPatternPairs, +#elif defined(OS_ANDROID) || defined(OS_CHROMEOS) + prefs::kContentSettingsProtectedMediaIdentifierPatternPairs, +#endif + prefs::kContentSettingsAppBannerPatternPairs +}; +static_assert(arraysize(kContentSettingsExceptionsPrefs) + == CONTENT_SETTINGS_NUM_TYPES, + "kContentSettingsExceptionsPrefs should have " + "CONTENT_SETTINGS_NUM_TYPES elements"); + } // namespace namespace content_settings { @@ -47,11 +117,26 @@ void PrefProvider::RegisterProfilePrefs( registry->RegisterDictionaryPref( prefs::kContentSettingsPatternPairs, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); + registry->RegisterBooleanPref( + prefs::kMigratedContentSettingsPatternPairs, + false, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + + for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { + registry->RegisterDictionaryPref( + kContentSettingsExceptionsPrefs[i], + IsContentSettingsTypeSyncable(ContentSettingsType(i)) + ? user_prefs::PrefRegistrySyncable::SYNCABLE_PREF + : user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF + ); + } } PrefProvider::PrefProvider(PrefService* prefs, bool incognito) : prefs_(prefs), - clock_(new base::DefaultClock()) { + clock_(new base::DefaultClock()), + is_incognito_(incognito), + updating_old_preferences_(false) { DCHECK(prefs_); // Verify preferences version. if (!prefs_->HasPrefPath(prefs::kContentSettingsVersion)) { @@ -64,18 +149,43 @@ PrefProvider::PrefProvider(PrefService* prefs, bool incognito) } pref_change_registrar_.Init(prefs_); - content_settings_pref_.reset(new ContentSettingsPref( - prefs_, &pref_change_registrar_, clock_.get(), incognito, - base::Bind(&PrefProvider::Notify, - base::Unretained(this)))); - if (!incognito) { + pref_change_registrar_.Add(prefs::kContentSettingsPatternPairs, base::Bind( + &PrefProvider::OnOldContentSettingsPatternPairsChanged, + base::Unretained(this))); + + for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { + content_settings_prefs_.push_back(new ContentSettingsPref( + ContentSettingsType(i), prefs_, &pref_change_registrar_, + kContentSettingsExceptionsPrefs[i], is_incognito_, + &updating_old_preferences_, base::Bind(&PrefProvider::Notify, + base::Unretained(this)))); + } + + // Migrate all the exceptions from the aggregate dictionary preference + // to the separate dictionaries, if this hasn't been done before. + if (!prefs_->GetBoolean(prefs::kMigratedContentSettingsPatternPairs)) { + WriteSettingsToNewPreferences(false); + prefs_->SetBoolean(prefs::kMigratedContentSettingsPatternPairs, true); + } else { + // Trigger the update of old preference, and as a result, + // the new preferences as well. + OnOldContentSettingsPatternPairsChanged(); + } + + if (!is_incognito_) { + size_t num_exceptions = 0; + for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) + num_exceptions += content_settings_prefs_[i]->GetNumExceptions(); + UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfExceptions", - content_settings_pref_->GetNumExceptions()); + num_exceptions); + // Migrate the obsolete media content setting exceptions to the new - // settings. This needs to be done after ReadContentSettingsFromPref(). + // settings. MigrateObsoleteMediaContentSetting(); } + } PrefProvider::~PrefProvider() { @@ -86,9 +196,9 @@ RuleIterator* PrefProvider::GetRuleIterator( ContentSettingsType content_type, const ResourceIdentifier& resource_identifier, bool incognito) const { - return content_settings_pref_->GetRuleIterator(content_type, - resource_identifier, - incognito); + return content_settings_prefs_[content_type]->GetRuleIterator( + resource_identifier, + incognito); } bool PrefProvider::SetWebsiteSetting( @@ -100,11 +210,22 @@ bool PrefProvider::SetWebsiteSetting( DCHECK(CalledOnValidThread()); DCHECK(prefs_); - return content_settings_pref_->SetWebsiteSetting(primary_pattern, - secondary_pattern, - content_type, - resource_identifier, - in_value); + // Default settings are set using a wildcard pattern for both + // |primary_pattern| and |secondary_pattern|. Don't store default settings in + // the |PrefProvider|. The |PrefProvider| handles settings for specific + // sites/origins defined by the |primary_pattern| and the |secondary_pattern|. + // Default settings are handled by the |DefaultProvider|. + if (primary_pattern == ContentSettingsPattern::Wildcard() && + secondary_pattern == ContentSettingsPattern::Wildcard() && + resource_identifier.empty()) { + return false; + } + + return content_settings_prefs_[content_type]->SetWebsiteSetting( + primary_pattern, + secondary_pattern, + resource_identifier, + in_value); } void PrefProvider::ClearAllContentSettingsRules( @@ -112,7 +233,7 @@ void PrefProvider::ClearAllContentSettingsRules( DCHECK(CalledOnValidThread()); DCHECK(prefs_); - content_settings_pref_->ClearAllContentSettingsRules(content_type); + content_settings_prefs_[content_type]->ClearAllContentSettingsRules(); } void PrefProvider::ShutdownOnUIThread() { @@ -127,23 +248,54 @@ void PrefProvider::UpdateLastUsage( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, ContentSettingsType content_type) { - content_settings_pref_->UpdateLastUsage(primary_pattern, - secondary_pattern, - content_type); + content_settings_prefs_[content_type]->UpdateLastUsage(primary_pattern, + secondary_pattern, + clock_.get()); } base::Time PrefProvider::GetLastUsage( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, ContentSettingsType content_type) { - return content_settings_pref_->GetLastUsage(primary_pattern, - secondary_pattern, - content_type); + return content_settings_prefs_[content_type]->GetLastUsage(primary_pattern, + secondary_pattern); } // //////////////////////////////////////////////////////////////////////////// // Private +PrefProvider::ContentSettingsPrefEntry::ContentSettingsPrefEntry( + const ContentSettingsPattern primary_pattern, + const ContentSettingsPattern secondary_pattern, + const ResourceIdentifier resource_identifier, + base::Value* value) + : primary_pattern(CopyPattern(primary_pattern)), + secondary_pattern(CopyPattern(secondary_pattern)), + resource_identifier(resource_identifier), + value(value) { +} + +PrefProvider::ContentSettingsPrefEntry::ContentSettingsPrefEntry( + const ContentSettingsPrefEntry& entry) + : primary_pattern(CopyPattern(entry.primary_pattern)), + secondary_pattern(CopyPattern(entry.secondary_pattern)), + resource_identifier(entry.resource_identifier), + value(entry.value->DeepCopy()) { +} + +PrefProvider::ContentSettingsPrefEntry& + PrefProvider::ContentSettingsPrefEntry::operator=( + const ContentSettingsPrefEntry& entry) { + this->primary_pattern = CopyPattern(entry.primary_pattern); + this->secondary_pattern = CopyPattern(entry.secondary_pattern); + this->resource_identifier = entry.resource_identifier; + this->value.reset(entry.value->DeepCopy()); + + return *this; +} + +PrefProvider::ContentSettingsPrefEntry::~ContentSettingsPrefEntry() {} + void PrefProvider::MigrateObsoleteMediaContentSetting() { std::vector<Rule> rules_to_delete; { @@ -205,9 +357,180 @@ void PrefProvider::Notify( resource_identifier); } +void PrefProvider::ReadContentSettingsFromOldPref() { + // |DictionaryPrefUpdate| sends out notifications when destructed. This + // construction order ensures |AutoLock| gets destroyed first and |old_lock_| + // is not held when the notifications are sent. Also, |auto_reset| must be + // still valid when the notifications are sent, so that |Observe| skips the + // notification. + base::AutoReset<bool> auto_reset(&updating_old_preferences_, true); + DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs); + + ClearPrefEntryMap(); + + const base::DictionaryValue* all_settings_dictionary = + prefs_->GetDictionary(prefs::kContentSettingsPatternPairs); + + // Careful: The returned value could be NULL if the pref has never been set. + if (!all_settings_dictionary) + return; + + base::DictionaryValue* mutable_settings; + scoped_ptr<base::DictionaryValue> mutable_settings_scope; + + if (!is_incognito_) { + mutable_settings = update.Get(); + } else { + // Create copy as we do not want to persist anything in OTR prefs. + mutable_settings = all_settings_dictionary->DeepCopy(); + mutable_settings_scope.reset(mutable_settings); + } + // Convert all Unicode patterns into punycode form, then read. + ContentSettingsPref::CanonicalizeContentSettingsExceptions(mutable_settings); + + for (base::DictionaryValue::Iterator i(*mutable_settings); !i.IsAtEnd(); + i.Advance()) { + const std::string& pattern_str(i.key()); + std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair = + ParsePatternString(pattern_str); + if (!pattern_pair.first.IsValid() || + !pattern_pair.second.IsValid()) { + // TODO: Change this to DFATAL when crbug.com/132659 is fixed. + LOG(ERROR) << "Invalid pattern strings: " << pattern_str; + continue; + } + + // Get settings dictionary for the current pattern string, and read + // settings from the dictionary. + const base::DictionaryValue* settings_dictionary = NULL; + bool is_dictionary = i.value().GetAsDictionary(&settings_dictionary); + DCHECK(is_dictionary); + + for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { + ContentSettingsType content_type = static_cast<ContentSettingsType>(i); + + std::string res_dictionary_path; + if (GetResourceTypeName(content_type, &res_dictionary_path)) { + const base::DictionaryValue* resource_dictionary = NULL; + if (settings_dictionary->GetDictionary( + res_dictionary_path, &resource_dictionary)) { + for (base::DictionaryValue::Iterator j(*resource_dictionary); + !j.IsAtEnd(); + j.Advance()) { + const std::string& resource_identifier(j.key()); + int setting = CONTENT_SETTING_DEFAULT; + bool is_integer = j.value().GetAsInteger(&setting); + DCHECK(is_integer); + DCHECK_NE(CONTENT_SETTING_DEFAULT, setting); + + pref_entry_map_[content_type].push_back( + new ContentSettingsPrefEntry( + pattern_pair.first, + pattern_pair.second, + resource_identifier, + new base::FundamentalValue(setting))); + } + } + } + base::Value* value = NULL; + if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type)) { + const base::DictionaryValue* setting = NULL; + // TODO(xians): Handle the non-dictionary types. + if (settings_dictionary->GetDictionaryWithoutPathExpansion( + GetTypeName(ContentSettingsType(i)), &setting)) { + DCHECK(!setting->empty()); + value = setting->DeepCopy(); + } + } else { + int setting = CONTENT_SETTING_DEFAULT; + if (settings_dictionary->GetIntegerWithoutPathExpansion( + GetTypeName(ContentSettingsType(i)), &setting)) { + DCHECK_NE(CONTENT_SETTING_DEFAULT, setting); + setting = FixObsoleteCookiePromptMode(content_type, + ContentSetting(setting)); + value = new base::FundamentalValue(setting); + } + } + + // |pref_entry_map_| will take the ownership of |value|. + if (value != NULL) { + pref_entry_map_[content_type].push_back( + new ContentSettingsPrefEntry( + pattern_pair.first, + pattern_pair.second, + ResourceIdentifier(), + value)); + } + } + } +} + +void PrefProvider::WriteSettingsToNewPreferences(bool syncable_only) { + // The incognito provider cannot write the settings to avoid echo effect: + // New preference -> PrefProvider -> Old preference -> + // -> Incognito PrefProvider -> New preference -> etc. + if (is_incognito_) + return; + + if (updating_old_preferences_) + return; + + base::AutoReset<bool> auto_reset(&updating_old_preferences_, true); + base::AutoLock auto_lock(old_lock_); + + ReadContentSettingsFromOldPref(); + + for (int k = 0; k < CONTENT_SETTINGS_NUM_TYPES; ++k) { + ContentSettingsType content_type = ContentSettingsType(k); + + if (syncable_only && !IsContentSettingsTypeSyncable(content_type)) + continue; + + content_settings_prefs_[content_type]->ClearAllContentSettingsRules(); + + for (size_t i = 0; i < pref_entry_map_[content_type].size(); ++i) { +#if defined(OS_CHROMEOS) || defined(OS_ANDROID) + // Protected Media Identifier "Allow" exceptions can not be migrated. + const base::FundamentalValue allow_value(CONTENT_SETTING_ALLOW); + if (content_type == CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER && + pref_entry_map_[content_type][i]->value->Equals(&allow_value)) { + continue; + } +#endif + + content_settings_prefs_[content_type]->SetWebsiteSetting( + pref_entry_map_[content_type][i]->primary_pattern, + pref_entry_map_[content_type][i]->secondary_pattern, + pref_entry_map_[content_type][i]->resource_identifier, + pref_entry_map_[content_type][i]->value.release()); + } + } + + ClearPrefEntryMap(); +} + +void PrefProvider::ClearPrefEntryMap() { + for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) + pref_entry_map_[i].clear(); +} + +void PrefProvider::OnOldContentSettingsPatternPairsChanged() { + DCHECK(thread_checker_.CalledOnValidThread()); + + WriteSettingsToNewPreferences(true); +} + void PrefProvider::SetClockForTesting(scoped_ptr<base::Clock> clock) { clock_ = clock.Pass(); - content_settings_pref_->SetClockForTesting(clock_.get()); +} + +bool PrefProvider::TestAllLocks() const { + for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { + if (!content_settings_prefs_[i]->lock_.Try()) + return false; + content_settings_prefs_[i]->lock_.Release(); + } + return true; } } // namespace content_settings diff --git a/components/content_settings/core/browser/content_settings_pref_provider.h b/components/content_settings/core/browser/content_settings_pref_provider.h index 8c26c4d..ec235a9 100644 --- a/components/content_settings/core/browser/content_settings_pref_provider.h +++ b/components/content_settings/core/browser/content_settings_pref_provider.h @@ -10,6 +10,7 @@ #include <vector> #include "base/basictypes.h" +#include "base/memory/scoped_vector.h" #include "base/prefs/pref_change_registrar.h" #include "components/content_settings/core/browser/content_settings_observable_provider.h" #include "components/content_settings/core/browser/content_settings_utils.h" @@ -70,14 +71,20 @@ class PrefProvider : public ObservableProvider { // Gains ownership of |clock|. void SetClockForTesting(scoped_ptr<base::Clock> clock); - ContentSettingsPref* content_settings_pref() { - return content_settings_pref_.get(); - } - private: + friend class DeadlockCheckerThread; // For testing. + // Migrate the old media setting into new mic/camera content settings. void MigrateObsoleteMediaContentSetting(); + // Migrate the settings from the old aggregate dictionary into the new format. + void MigrateAllExceptions(); + + // Writes the contents of the old aggregate dictionary preferences into + // separate dictionaries for content types. If |syncable_only| is true, + // only syncable content types will be written. + void WriteSettingsToNewPreferences(bool syncable_only); + // Weak; owned by the Profile and reset in ShutdownOnUIThread. PrefService* prefs_; @@ -88,9 +95,56 @@ class PrefProvider : public ObservableProvider { PrefChangeRegistrar pref_change_registrar_; - scoped_ptr<ContentSettingsPref> content_settings_pref_; + ScopedVector<ContentSettingsPref> content_settings_prefs_; DISALLOW_COPY_AND_ASSIGN(PrefProvider); + + bool TestAllLocks() const; + + // All functionality regarding reading and writing of preferences has been + // moved to |ContentSettingsPref|, which manages one content type per + // instance. However, for backward compatibility, we need to be able to write + // to the old and deprecated aggregate dictionary preference which maintains + // all content types. Therefore, |ContentSettingsPrefProvider| must still + // retain some of the functionality of |ContentSettingsPref|. The following + // attributes and methods serve this purpose. + // TODO(msramek): Remove this migration code after two stable releases. + struct ContentSettingsPrefEntry { + ContentSettingsPrefEntry(const ContentSettingsPattern primary_pattern, + const ContentSettingsPattern secondary_pattern, + const ResourceIdentifier resource_identifier, + base::Value* value); + ContentSettingsPrefEntry(const ContentSettingsPrefEntry& entry); + ContentSettingsPrefEntry& operator=(const ContentSettingsPrefEntry& entry); + ~ContentSettingsPrefEntry(); + + ContentSettingsPattern primary_pattern; + ContentSettingsPattern secondary_pattern; + ResourceIdentifier resource_identifier; + scoped_ptr<base::Value> value; + }; + + // Stores exceptions read from the old preference before writing them + // to the new one. + ScopedVector<ContentSettingsPrefEntry> + pref_entry_map_[CONTENT_SETTINGS_NUM_TYPES]; + + // Clears |pref_entry_map_|. + void ClearPrefEntryMap(); + + // Guards access to |pref_entry_map_|. + mutable base::Lock old_lock_; + + // Indicates whether the old preferences are updated. + bool updating_old_preferences_; + + // Called when the old preference changes. + void OnOldContentSettingsPatternPairsChanged(); + + // Reads the old preference and writes it to |pref_entry_map_|. + void ReadContentSettingsFromOldPref(); + + base::ThreadChecker thread_checker_; }; } // namespace content_settings diff --git a/components/content_settings/core/common/content_settings.cc b/components/content_settings/core/common/content_settings.cc index 00403e9..491f973 100644 --- a/components/content_settings/core/common/content_settings.cc +++ b/components/content_settings/core/common/content_settings.cc @@ -73,6 +73,48 @@ ContentSettingsTypeHistogram ContentSettingTypeToHistogramValue( return CONTENT_SETTINGS_TYPE_HISTOGRAM_INVALID; } +bool IsContentSettingsTypeSyncable(ContentSettingsType content_setting) { + switch (content_setting) { + case CONTENT_SETTINGS_TYPE_COOKIES: + case CONTENT_SETTINGS_TYPE_IMAGES: + case CONTENT_SETTINGS_TYPE_JAVASCRIPT: + case CONTENT_SETTINGS_TYPE_PLUGINS: + case CONTENT_SETTINGS_TYPE_POPUPS: + case CONTENT_SETTINGS_TYPE_FULLSCREEN: + case CONTENT_SETTINGS_TYPE_MOUSELOCK: + case CONTENT_SETTINGS_TYPE_MIXEDSCRIPT: + case CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS: + case CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS: + case CONTENT_SETTINGS_TYPE_MIDI_SYSEX: + case CONTENT_SETTINGS_TYPE_PUSH_MESSAGING: +#if defined(OS_WIN) + case CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP: +#endif + return true; + + case CONTENT_SETTINGS_TYPE_GEOLOCATION: + case CONTENT_SETTINGS_TYPE_NOTIFICATIONS: + case CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE: + case CONTENT_SETTINGS_TYPE_MEDIASTREAM: + case CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC: + case CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA: + case CONTENT_SETTINGS_TYPE_PPAPI_BROKER: + case CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS: +#if defined(OS_ANDROID) || defined(OS_CHROMEOS) + case CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER: +#endif + case CONTENT_SETTINGS_TYPE_APP_BANNER: + return false; + + case CONTENT_SETTINGS_TYPE_DEFAULT: + case CONTENT_SETTINGS_NUM_TYPES: + NOTREACHED(); + return false; + } + NOTREACHED(); + return false; +} + ContentSettingPatternSource::ContentSettingPatternSource( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, diff --git a/components/content_settings/core/common/content_settings.h b/components/content_settings/core/common/content_settings.h index 7f49a8c..7745ac1 100644 --- a/components/content_settings/core/common/content_settings.h +++ b/components/content_settings/core/common/content_settings.h @@ -34,6 +34,9 @@ ContentSetting IntToContentSetting(int content_setting); ContentSettingsTypeHistogram ContentSettingTypeToHistogramValue( ContentSettingsType content_setting); +// Whether this content setting should be synced. +bool IsContentSettingsTypeSyncable(ContentSettingsType content_setting); + struct ContentSettingPatternSource { ContentSettingPatternSource(const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_patttern, diff --git a/components/content_settings/core/common/pref_names.cc b/components/content_settings/core/common/pref_names.cc index da7bd8b..bffdc49 100644 --- a/components/content_settings/core/common/pref_names.cc +++ b/components/content_settings/core/common/pref_names.cc @@ -80,15 +80,72 @@ const char kDefaultProtectedMediaIdentifierSetting[] = const char kDefaultAppBannerSetting[] = "profile.default_content_setting_values.app_banner"; +// Boolean indicating whether the media stream default setting had been +// migrated into two separate microphone and camera settings. +const char kMigratedDefaultMediaStreamSetting[] = + "profile.migrated_default_media_stream_content_settings"; + // Dictionary of content settings that can globally disallow all hosts by // default. If a value is set, it means the setting is globally disallowed. // If a value is not set, it means the setting is allowed. const char kOverrideContentSettings[] = "profile.override_content_settings"; -// Boolean indicating whether the media stream default setting had been -// migrated into two separate microphone and camera settings. -const char kMigratedDefaultMediaStreamSetting[] = - "profile.migrated_default_media_stream_content_settings"; +// Preferences storing the content settings exceptions. +const char kContentSettingsCookiesPatternPairs[] = + "profile.content_settings.exceptions.cookies"; +const char kContentSettingsImagesPatternPairs[] = + "profile.content_settings.exceptions.images"; +const char kContentSettingsJavaScriptPatternPairs[] = + "profile.content_settings.exceptions.javascript"; +const char kContentSettingsPluginsPatternPairs[] = + "profile.content_settings.exceptions.plugins"; +const char kContentSettingsPopupsPatternPairs[] = + "profile.content_settings.exceptions.popups"; +const char kContentSettingsGeolocationPatternPairs[] = + "profile.content_settings.exceptions.geolocation"; +const char kContentSettingsNotificationsPatternPairs[] = + "profile.content_settings.exceptions.notifications"; +const char kContentSettingsAutoSelectCertificatePatternPairs[] = + "profile.content_settings.exceptions.auto_select_certificate"; +const char kContentSettingsFullScreenPatternPairs[] = + "profile.content_settings.exceptions.fullscreen"; +const char kContentSettingsMouseLockPatternPairs[] = + "profile.content_settings.exceptions.mouselock"; +const char kContentSettingsMixedScriptPatternPairs[] = + "profile.content_settings.exceptions.mixed_script"; +const char kContentSettingsMediaStreamPatternPairs[] = + "profile.content_settings.exceptions.media_stream"; +const char kContentSettingsMediaStreamMicPatternPairs[] = + "profile.content_settings.exceptions.media_stream_mic"; +const char kContentSettingsMediaStreamCameraPatternPairs[] = + "profile.content_settings.exceptions.media_stream_camera"; +const char kContentSettingsProtocolHandlersPatternPairs[] = + "profile.content_settings.exceptions.protocol_handlers"; +const char kContentSettingsPpapiBrokerPatternPairs[] = + "profile.content_settings.exceptions.ppapi_broker"; +const char kContentSettingsAutomaticDownloadsPatternPairs[] = + "profile.content_settings.exceptions.automatic_downloads"; +const char kContentSettingsMidiSysexPatternPairs[] = + "profile.content_settings.exceptions.midi_sysex"; +const char kContentSettingsPushMessagingPatternPairs[] = + "profile.content_settings.exceptions.push_messaging"; +const char kContentSettingsSSLCertDecisionsPatternPairs[] = + "profile.content_settings.exceptions.ssl_cert_decisions"; +#if defined(OS_WIN) +const char kContentSettingsMetroSwitchToDesktopPatternPairs[] = + "profile.content_settings.exceptions.metro_switch_to_desktop"; +#elif defined(OS_ANDROID) || defined(OS_CHROMEOS) +const char kContentSettingsProtectedMediaIdentifierPatternPairs[] = + "profile.content_settings.exceptions.protected_media_identifier"; +#endif +const char kContentSettingsAppBannerPatternPairs[] = + "profile.content_settings.exceptions.app_banner"; + +// Whether the patern pairs have been migrated from the deprecated aggregate +// preference |kContentSettingsPatternPairs| to the separate preferences +// |kContentSettings<type>PatternPairs|. +const char kMigratedContentSettingsPatternPairs[] = + "profile.migrated_content_settings_exceptions"; // Preferences that are exclusively used to store managed values for default // content settings. diff --git a/components/content_settings/core/common/pref_names.h b/components/content_settings/core/common/pref_names.h index 01620a3..69591d5 100644 --- a/components/content_settings/core/common/pref_names.h +++ b/components/content_settings/core/common/pref_names.h @@ -45,6 +45,36 @@ extern const char kDefaultAppBannerSetting[]; extern const char kMigratedDefaultMediaStreamSetting[]; +// Preferences storing the default values for individual content settings. +extern const char kContentSettingsCookiesPatternPairs[]; +extern const char kContentSettingsImagesPatternPairs[]; +extern const char kContentSettingsJavaScriptPatternPairs[]; +extern const char kContentSettingsPluginsPatternPairs[]; +extern const char kContentSettingsPopupsPatternPairs[]; +extern const char kContentSettingsGeolocationPatternPairs[]; +extern const char kContentSettingsNotificationsPatternPairs[]; +extern const char kContentSettingsAutoSelectCertificatePatternPairs[]; +extern const char kContentSettingsFullScreenPatternPairs[]; +extern const char kContentSettingsMouseLockPatternPairs[]; +extern const char kContentSettingsMixedScriptPatternPairs[]; +extern const char kContentSettingsMediaStreamPatternPairs[]; +extern const char kContentSettingsMediaStreamMicPatternPairs[]; +extern const char kContentSettingsMediaStreamCameraPatternPairs[]; +extern const char kContentSettingsProtocolHandlersPatternPairs[]; +extern const char kContentSettingsPpapiBrokerPatternPairs[]; +extern const char kContentSettingsAutomaticDownloadsPatternPairs[]; +extern const char kContentSettingsMidiSysexPatternPairs[]; +extern const char kContentSettingsPushMessagingPatternPairs[]; +extern const char kContentSettingsSSLCertDecisionsPatternPairs[]; +#if defined(OS_WIN) +extern const char kContentSettingsMetroSwitchToDesktopPatternPairs[]; +#elif defined(OS_ANDROID) || defined(OS_CHROMEOS) +extern const char kContentSettingsProtectedMediaIdentifierPatternPairs[]; +#endif +extern const char kContentSettingsAppBannerPatternPairs[]; + +extern const char kMigratedContentSettingsPatternPairs[]; + extern const char kManagedDefaultCookiesSetting[]; extern const char kManagedDefaultImagesSetting[]; extern const char kManagedDefaultJavaScriptSetting[]; |