Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(310)

Unified Diff: components/content_settings/core/browser/content_settings_pref_provider.cc

Issue 1005303003: Split the aggregate dictionary of content settings exceptions into per-type dictionaries (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed tests. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..c5469c79ff2cc08fb57a8e92eac48e91ce5b41ad 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,74 @@
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
markusheintz_ 2015/04/01 10:02:51 nit: move this "Returns true and set ..." part to
msramek 2015/04/01 10:53:35 Done. (This text is actually just moved here from
+// dictionary under which per-resource content settings are stored.
+// Otherwise, returns false.
markusheintz_ 2015/04/01 10:02:51 nit: you can remove this line
msramek 2015/04/01 10:53:35 Done.
+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,
markusheintz_ 2015/04/01 10:02:51 I wonder if we still need this? If you are unsure
msramek 2015/04/01 10:53:35 It looks like a very old migration code. I believe
+ 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";
+const char* kContentSettingsExceptionsPrefs[] = {
markusheintz_ 2015/04/01 10:02:51 Maybe add a comment that the order matters and has
msramek 2015/04/01 10:53:35 Done.
+ 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 +114,24 @@ 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)
markusheintz_ 2015/04/01 10:02:51 Please add { }. I know for loop header is followed
msramek 2015/04/01 10:53:35 Done.
+ 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 +144,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);
+
+ // Migrate all the exceptions from the aggregate dictionary preference
+ // to the separate dictionaries.
+ MigrateAllExceptions();
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 the obsolete media content setting exceptions to the new
- // settings. This needs to be done after ReadContentSettingsFromPref().
+ // settings.
MigrateObsoleteMediaContentSetting();
}
+
}
PrefProvider::~PrefProvider() {
@@ -86,9 +187,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,
markusheintz_ 2015/04/01 10:02:51 bad indentation
msramek 2015/04/01 10:53:35 Done.
+ incognito);
}
bool PrefProvider::SetWebsiteSetting(
@@ -100,11 +201,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,
markusheintz_ 2015/04/01 10:02:51 bad indentation.
msramek 2015/04/01 10:53:35 Done.
+ secondary_pattern,
+ resource_identifier,
+ in_value);
}
void PrefProvider::ClearAllContentSettingsRules(
@@ -112,7 +224,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 +239,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 +348,183 @@ void PrefProvider::Notify(
resource_identifier);
}
+void PrefProvider::ReadContentSettingsFromOldPref(
+ 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);
+
+ 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) {
+ ContentSettingsPrefEntry entry(
+ pattern_pair.first,
+ pattern_pair.second,
+ resource_identifier,
+ new base::FundamentalValue(setting));
+ (*entry_map)[content_type].push_back(entry);
+ }
+ }
+ }
+ }
+ 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) {
+ ContentSettingsPrefEntry entry(
+ pattern_pair.first,
+ pattern_pair.second,
+ ResourceIdentifier(),
+ value);
+ (*entry_map)[content_type].push_back(entry);
+ }
+ }
+ }
+}
+
+void PrefProvider::WriteSettingsToNewPreferences(bool syncable_only) {
+ if (updating_old_preferences_)
+ return;
+
+ // 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;
+
+ base::AutoReset<bool> auto_reset(&updating_old_preferences_, true);
+ base::AutoLock auto_lock(old_lock_);
+
+ ContentSettingsPrefEntryMap old_values;
+ ReadContentSettingsFromOldPref(&old_values);
+
+ for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
+ ContentSettingsType content_type = ContentSettingsType(i);
+ if (syncable_only && !IsContentSettingsTypeSyncable(content_type))
+ return;
+
+ content_settings_prefs_[content_type]->ClearAllContentSettingsRules();
+
+ for (size_t i = 0; i < old_values[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 &&
+ old_values[content_type][i].value->Equals(&allow_value)) {
+ continue;
+ }
+#endif
+
+ content_settings_prefs_[content_type]->SetWebsiteSetting(
+ old_values[content_type][i].primary_pattern,
+ old_values[content_type][i].secondary_pattern,
+ old_values[content_type][i].resource_identifier,
+ old_values[content_type][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

Powered by Google App Engine
This is Rietveld 408576698