Chromium Code Reviews| Index: components/content_settings/core/browser/content_settings_pref_provider.cc |
| 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 6c855d3e2ff94fe6482e74449017add60ec36ff9..49939f2e58992e1f1f17d8fe65e9aee841d47df5 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,12 @@ |
| #include <string> |
| #include <utility> |
| +#include "base/auto_reset.h" |
| #include "base/bind.h" |
| #include "base/memory/scoped_ptr.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 +28,66 @@ |
| namespace { |
| +const char kPerPluginPrefName[] = "per_plugin"; |
| + |
| +// If the given content type supports resource identifiers in user preferences, |
| +// 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; |
| +} |
| + |
| +ContentSetting FixObsoleteCookiePromptMode(ContentSettingsType content_type, |
| + ContentSetting setting) { |
| + if (content_type == CONTENT_SETTINGS_TYPE_COOKIES && |
| + setting == CONTENT_SETTING_ASK) { |
| + return CONTENT_SETTING_BLOCK; |
| + } |
| + return setting; |
| +} |
| + |
| const char kAudioKey[] = "audio"; |
| const char kVideoKey[] = "video"; |
| +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 +106,24 @@ void PrefProvider::RegisterProfilePrefs( |
| registry->RegisterDictionaryPref( |
| prefs::kContentSettingsPatternPairs, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
| + registry->RegisterBooleanPref( |
| + prefs::kMigratedContentSettingsPatternPairs, |
| + false, |
| + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
| + |
| + for (size_t 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()), |
| + updating_old_preferences_(false) { |
| DCHECK(prefs_); |
| // Verify preferences version. |
| if (!prefs_->HasPrefPath(prefs::kContentSettingsVersion)) { |
| @@ -64,18 +136,39 @@ 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)))); |
| + |
| + pref_change_registrar_.Add(prefs::kContentSettingsPatternPairs, base::Bind( |
| + &PrefProvider::OnOldContentSettingsPatternPairsChanged, |
| + base::Unretained(this))); |
| + |
| + content_settings_prefs_.resize(CONTENT_SETTINGS_NUM_TYPES); |
| + for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { |
| + content_settings_prefs_[i].reset(new ContentSettingsPref( |
| + ContentSettingsType(i), prefs_, &pref_change_registrar_, |
| + kContentSettingsExceptionsPrefs[i], incognito, |
| + &updating_old_preferences_, base::Bind(&PrefProvider::Notify, |
| + base::Unretained(this)))); |
| + } |
| + |
| + ReadContentSettingsFromOldPref(NULL); |
| if (!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 all the exceptions from the aggregate dictionary preference |
| + // to the separate dictionaries. |
| + MigrateAllExceptions(); |
| + |
| // Migrate the obsolete media content setting exceptions to the new |
| - // settings. This needs to be done after ReadContentSettingsFromPref(). |
| + // settings. |
| MigrateObsoleteMediaContentSetting(); |
| } |
| + |
| } |
| PrefProvider::~PrefProvider() { |
| @@ -86,9 +179,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 +193,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 +216,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 +231,35 @@ 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(primary_pattern), |
| + secondary_pattern(secondary_pattern), |
| + resource_identifier(resource_identifier), |
| + value(value) { |
| +}; |
| + |
| +PrefProvider::ContentSettingsPrefEntry::~ContentSettingsPrefEntry() {} |
| + |
| void PrefProvider::MigrateObsoleteMediaContentSetting() { |
| std::vector<Rule> rules_to_delete; |
| { |
| @@ -205,9 +321,170 @@ void PrefProvider::Notify( |
| resource_identifier); |
| } |
| +void PrefProvider::ReadContentSettingsFromOldPref( |
|
msramek
2015/03/26 18:27:39
So this whole method is basically duplicated from
|
| + PrefProvider::ContentSettingsPrefEntryMap* entry_map) { |
| + // |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); |
| + base::AutoLock auto_lock(old_lock_); |
| + |
| + 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); |
| + |
| + if (entry_map) { |
| + (*entry_map)[content_type].push_back(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); |
| + } |
| + } |
| + |
| + // |entry_map| will take the ownership of |value|. |
| + if (value != NULL && entry_map) { |
| + (*entry_map)[content_type].push_back(ContentSettingsPrefEntry( |
| + pattern_pair.first, |
| + pattern_pair.second, |
| + ResourceIdentifier(), |
| + value)); |
| + } |
| + } |
| + } |
| +} |
| + |
| +void PrefProvider::WriteSettingsToNewPreferences(bool syncable_only) { |
| + if (updating_old_preferences_) |
| + return; |
| + |
| + ContentSettingsPrefEntryMap old_values; |
| + ReadContentSettingsFromOldPref(&old_values); |
| + |
| + base::AutoReset<bool> auto_reset(&updating_old_preferences_, true); |
| + base::AutoLock auto_lock(old_lock_); |
|
msramek
2015/03/26 18:27:39
I readded the lock here to make sure that the migr
|
| + |
| + for (ContentSettingsPrefEntryMap::iterator it = old_values.begin(); |
| + it != old_values.end(); ++it) { |
| + ContentSettingsType content_type = it->first; |
| + if (syncable_only && !IsContentSettingsTypeSyncable(content_type)) |
| + return; |
| + |
| + std::vector<ContentSettingsPrefEntry>& entries = it->second; |
| + |
| + content_settings_prefs_[content_type]->ClearAllContentSettingsRules(); |
| + |
| + for (size_t i = 0; i < entries.size(); ++i) { |
| + content_settings_prefs_[content_type]->SetWebsiteSetting( |
| + entries[i].primary_pattern, |
| + entries[i].secondary_pattern, |
| + entries[i].resource_identifier, |
| + entries[i].value->DeepCopy()); |
| + } |
| + } |
| +} |
| + |
| +void PrefProvider::OnOldContentSettingsPatternPairsChanged() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + WriteSettingsToNewPreferences(true); |
| +} |
| + |
| +void PrefProvider::MigrateAllExceptions() { |
| + if (prefs_->GetBoolean(prefs::kMigratedContentSettingsPatternPairs)) |
| + return; |
| + |
| + WriteSettingsToNewPreferences(false); |
| + |
| + prefs_->SetBoolean(prefs::kMigratedContentSettingsPatternPairs, 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 |