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

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

Issue 976243002: Split content settings pref related logic into a separate class (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: 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.cc
diff --git a/components/content_settings/core/browser/content_settings_pref.cc b/components/content_settings/core/browser/content_settings_pref.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a643697cbdee3410ebd42d58d4010f9bda8ecc9f
--- /dev/null
+++ b/components/content_settings/core/browser/content_settings_pref.cc
@@ -0,0 +1,558 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/content_settings/core/browser/content_settings_pref.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/prefs/scoped_user_pref_update.h"
+#include "base/strings/string_split.h"
+#include "base/time/clock.h"
+#include "components/content_settings/core/browser/content_settings_rule.h"
+#include "components/content_settings/core/browser/content_settings_utils.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_pattern.h"
+#include "components/content_settings/core/common/pref_names.h"
+#include "url/gurl.h"
+
+namespace {
+
+typedef std::pair<std::string, std::string> StringPair;
+
+const char kPerPluginPrefName[] = "per_plugin";
+const char kLastUsed[] = "last_used";
+
+ContentSetting FixObsoleteCookiePromptMode(ContentSettingsType content_type,
+ ContentSetting setting) {
+ if (content_type == CONTENT_SETTINGS_TYPE_COOKIES &&
+ setting == CONTENT_SETTING_ASK) {
+ return CONTENT_SETTING_BLOCK;
+ }
+ return setting;
+}
+
+// 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;
+}
+
+} // namespace
+
+namespace content_settings {
+
+ContentSettingsPref::ContentSettingsPref(
+ PrefService* prefs,
+ PrefChangeRegistrar* registrar,
+ base::Clock* clock,
+ bool incognito,
+ NotifyObserversCallback notify_callback)
+ : prefs_(prefs),
+ clock_(clock),
+ registrar_(registrar),
+ is_incognito_(incognito),
+ updating_preferences_(false),
+ notify_callback_(notify_callback) {
+ DCHECK(prefs_);
+
+ // Read content settings exceptions.
+ ReadContentSettingsFromPref();
+
+ registrar->Add(
+ prefs::kContentSettingsPatternPairs,
+ base::Bind(&ContentSettingsPref::OnContentSettingsPatternPairsChanged,
+ 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,
+ 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;
+ }
+
+ // 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_)
+ map_to_modify = &value_map_;
+
+ {
+ base::AutoLock auto_lock(lock_);
+ if (value.get()) {
+ map_to_modify->SetValue(
+ primary_pattern,
+ secondary_pattern,
+ content_type,
+ resource_identifier,
+ value->DeepCopy());
+ } else {
+ map_to_modify->DeleteValue(
+ primary_pattern,
+ secondary_pattern,
+ content_type,
+ resource_identifier);
+ }
+ }
+ // Update the content settings preference.
+ if (!is_incognito_) {
+ UpdatePref(primary_pattern,
+ secondary_pattern,
+ content_type,
+ resource_identifier,
+ value.get());
+ }
+
+ notify_callback_.Run(
+ primary_pattern, secondary_pattern, content_type, resource_identifier);
+
+ return true;
+}
+
+void ContentSettingsPref::ClearAllContentSettingsRules(
+ ContentSettingsType content_type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(prefs_);
+
+ OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
+ if (!is_incognito_)
+ map_to_modify = &value_map_;
+
+ std::vector<Rule> rules_to_delete;
+ {
+ base::AutoLock auto_lock(lock_);
+ scoped_ptr<RuleIterator> rule_iterator(
+ 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());
+ }
+
+ 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);
+ }
+ notify_callback_.Run(ContentSettingsPattern(),
+ ContentSettingsPattern(),
+ content_type,
+ std::string());
+}
+
+void ContentSettingsPref::UpdateLastUsage(
+ const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type) {
+ // Don't write if in incognito.
+ if (is_incognito_) {
+ return;
+ }
+
+ // Ensure that |lock_| is not held by this thread, since this function will
+ // send out notifications (by |~DictionaryPrefUpdate|).
+ AssertLockNotHeld();
+
+ base::AutoReset<bool> auto_reset(&updating_preferences_, true);
+ {
+ DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs);
+ base::DictionaryValue* pattern_pairs_settings = update.Get();
+
+ 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) {
+ settings_dictionary = new base::DictionaryValue;
+ pattern_pairs_settings->SetWithoutPathExpansion(pattern_str,
+ 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()));
+ }
+}
+
+base::Time ContentSettingsPref::GetLastUsage(
+ const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type) {
+ const base::DictionaryValue* pattern_pairs_settings =
+ prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
+ std::string pattern_str(
+ CreatePatternString(primary_pattern, secondary_pattern));
+
+ const base::DictionaryValue* settings_dictionary = NULL;
+ bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
+ pattern_str, &settings_dictionary);
+
+ 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);
+
+ if (!found)
+ return base::Time();
+
+ return base::Time::FromDoubleT(last_used_time);
+}
+
+size_t ContentSettingsPref::GetNumExceptions() {
+ return value_map_.size();
+}
+
+void ContentSettingsPref::SetClockForTesting(base::Clock* clock) {
+ clock_ = clock;
+}
+
+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);
+ base::AutoLock auto_lock(lock_);
+
+ const base::DictionaryValue* all_settings_dictionary =
+ prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
+
+ value_map_.clear();
+
+ // 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.
+ CanonicalizeContentSettingsExceptions(mutable_settings);
+
+ size_t cookies_block_exception_count = 0;
+ size_t cookies_allow_exception_count = 0;
+ size_t cookies_session_only_exception_count = 0;
+ 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);
+ value_map_.SetValue(pattern_pair.first,
+ pattern_pair.second,
+ content_type,
+ 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);
+ }
+ }
+
+ // |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;
+ }
+ }
+ }
+ }
+ }
+ 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() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (updating_preferences_)
+ return;
+
+ ReadContentSettingsFromPref();
+
+ notify_callback_.Run(ContentSettingsPattern(),
+ ContentSettingsPattern(),
+ CONTENT_SETTINGS_TYPE_DEFAULT,
+ 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
+ // send out notifications (by |~DictionaryPrefUpdate|).
+ AssertLockNotHeld();
+
+ base::AutoReset<bool> auto_reset(&updating_preferences_, true);
+ {
+ DictionaryPrefUpdate update(prefs_,
+ prefs::kContentSettingsPatternPairs);
+ 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) {
+ std::string res_dictionary_path;
+ if (GetResourceTypeName(content_type, &res_dictionary_path) &&
+ !resource_identifier.empty()) {
+ base::DictionaryValue* resource_dictionary = NULL;
+ found = settings_dictionary->GetDictionary(
+ res_dictionary_path, &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);
+ }
+ // Update resource dictionary.
+ if (value == NULL) {
+ resource_dictionary->RemoveWithoutPathExpansion(resource_identifier,
+ NULL);
+ if (resource_dictionary->empty()) {
+ settings_dictionary->RemoveWithoutPathExpansion(
+ res_dictionary_path, NULL);
+ }
+ } else {
+ resource_dictionary->SetWithoutPathExpansion(
+ resource_identifier, value->DeepCopy());
+ }
+ } else {
+ // Update settings dictionary.
+ std::string setting_path = GetTypeName(content_type);
+ if (value == NULL) {
+ settings_dictionary->RemoveWithoutPathExpansion(setting_path,
+ NULL);
+ settings_dictionary->RemoveWithoutPathExpansion(kLastUsed, NULL);
+ } else {
+ settings_dictionary->SetWithoutPathExpansion(
+ setting_path, value->DeepCopy());
+ }
+ }
+ // Remove the settings dictionary if it is empty.
+ if (settings_dictionary->empty()) {
+ pattern_pairs_settings->RemoveWithoutPathExpansion(
+ pattern_str, NULL);
+ }
+ }
+ }
+}
+
+// static
+void ContentSettingsPref::CanonicalizeContentSettingsExceptions(
+ base::DictionaryValue* all_settings_dictionary) {
+ DCHECK(all_settings_dictionary);
+
+ std::vector<std::string> remove_items;
+ base::StringPairs move_items;
+ for (base::DictionaryValue::Iterator i(*all_settings_dictionary);
+ !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()) {
+ LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
+ continue;
+ }
+
+ const std::string canonicalized_pattern_str = CreatePatternString(
+ pattern_pair.first, pattern_pair.second);
+
+ if (canonicalized_pattern_str.empty() ||
+ canonicalized_pattern_str == pattern_str) {
+ continue;
+ }
+
+ // Clear old pattern if prefs already have canonicalized pattern.
+ const base::DictionaryValue* new_pattern_settings_dictionary = NULL;
+ if (all_settings_dictionary->GetDictionaryWithoutPathExpansion(
+ canonicalized_pattern_str, &new_pattern_settings_dictionary)) {
+ remove_items.push_back(pattern_str);
+ continue;
+ }
+
+ // Move old pattern to canonicalized pattern.
+ const base::DictionaryValue* old_pattern_settings_dictionary = NULL;
+ if (i.value().GetAsDictionary(&old_pattern_settings_dictionary)) {
+ move_items.push_back(
+ std::make_pair(pattern_str, canonicalized_pattern_str));
+ }
+ }
+
+ for (size_t i = 0; i < remove_items.size(); ++i) {
+ all_settings_dictionary->RemoveWithoutPathExpansion(remove_items[i], NULL);
+ }
+
+ for (size_t i = 0; i < move_items.size(); ++i) {
+ scoped_ptr<base::Value> pattern_settings_dictionary;
+ all_settings_dictionary->RemoveWithoutPathExpansion(
+ move_items[i].first, &pattern_settings_dictionary);
+ all_settings_dictionary->SetWithoutPathExpansion(
+ move_items[i].second, pattern_settings_dictionary.release());
+ }
+}
+
+void ContentSettingsPref::AssertLockNotHeld() const {
+#if !defined(NDEBUG)
+ // |Lock::Acquire()| will assert if the lock is held by this thread.
+ lock_.Acquire();
+ lock_.Release();
+#endif
+}
+
+} // namespace content_settings

Powered by Google App Engine
This is Rietveld 408576698