1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/content_settings/content_settings_pref_provider.h"
11 #include "base/auto_reset.h"
12 #include "base/command_line.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/metrics/histogram.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/prefs/scoped_user_pref_update.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/content_settings/content_settings_rule.h"
19 #include "chrome/browser/content_settings/content_settings_utils.h"
20 #include "chrome/browser/content_settings/host_content_settings_map.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/content_settings.h"
23 #include "chrome/common/content_settings_pattern.h"
24 #include "chrome/common/pref_names.h"
25 #include "components/user_prefs/pref_registry_syncable.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/notification_details.h"
28 #include "content/public/browser/notification_source.h"
29 #include "content/public/browser/user_metrics.h"
32 using content::BrowserThread;
33 using content::UserMetricsAction;
37 typedef std::pair<std::string, std::string> StringPair;
38 typedef std::map<std::string, std::string> StringMap;
40 const char kPerPluginPrefName[] = "per_plugin";
41 const char kAudioKey[] = "audio";
42 const char kVideoKey[] = "video";
44 ContentSetting FixObsoleteCookiePromptMode(ContentSettingsType content_type,
45 ContentSetting setting) {
46 if (content_type == CONTENT_SETTINGS_TYPE_COOKIES &&
47 setting == CONTENT_SETTING_ASK) {
48 return CONTENT_SETTING_BLOCK;
53 // If the given content type supports resource identifiers in user preferences,
54 // returns true and sets |pref_key| to the key in the content settings
55 // dictionary under which per-resource content settings are stored.
56 // Otherwise, returns false.
57 bool GetResourceTypeName(ContentSettingsType content_type,
58 std::string* pref_key) {
59 if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) {
60 *pref_key = kPerPluginPrefName;
68 namespace content_settings {
70 // ////////////////////////////////////////////////////////////////////////////
75 void PrefProvider::RegisterProfilePrefs(
76 user_prefs::PrefRegistrySyncable* registry) {
77 registry->RegisterIntegerPref(
78 prefs::kContentSettingsVersion,
79 ContentSettingsPattern::kContentSettingsPatternVersion,
80 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
81 registry->RegisterDictionaryPref(
82 prefs::kContentSettingsPatternPairs,
83 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
86 PrefProvider::PrefProvider(PrefService* prefs,
89 is_incognito_(incognito),
90 updating_preferences_(false) {
92 // Verify preferences version.
93 if (!prefs_->HasPrefPath(prefs::kContentSettingsVersion)) {
94 prefs_->SetInteger(prefs::kContentSettingsVersion,
95 ContentSettingsPattern::kContentSettingsPatternVersion);
97 if (prefs_->GetInteger(prefs::kContentSettingsVersion) >
98 ContentSettingsPattern::kContentSettingsPatternVersion) {
102 // Read content settings exceptions.
103 ReadContentSettingsFromPref(false);
105 if (!is_incognito_) {
106 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfExceptions",
110 // Migrate the obsolete media content setting exceptions to the new settings.
111 // This needs to be done after ReadContentSettingsFromPref().
113 MigrateObsoleteMediaContentSetting();
115 pref_change_registrar_.Init(prefs_);
116 pref_change_registrar_.Add(
117 prefs::kContentSettingsPatternPairs,
118 base::Bind(&PrefProvider::OnContentSettingsPatternPairsChanged,
119 base::Unretained(this)));
122 bool PrefProvider::SetWebsiteSetting(
123 const ContentSettingsPattern& primary_pattern,
124 const ContentSettingsPattern& secondary_pattern,
125 ContentSettingsType content_type,
126 const ResourceIdentifier& resource_identifier,
128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
130 // Default settings are set using a wildcard pattern for both
131 // |primary_pattern| and |secondary_pattern|. Don't store default settings in
132 // the |PrefProvider|. The |PrefProvider| handles settings for specific
133 // sites/origins defined by the |primary_pattern| and the |secondary_pattern|.
134 // Default settings are handled by the |DefaultProvider|.
135 if (primary_pattern == ContentSettingsPattern::Wildcard() &&
136 secondary_pattern == ContentSettingsPattern::Wildcard() &&
137 resource_identifier.empty()) {
141 // At this point take the ownership of the |in_value|.
142 scoped_ptr<base::Value> value(in_value);
143 // Update in memory value map.
144 OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
146 map_to_modify = &value_map_;
149 base::AutoLock auto_lock(lock_);
151 map_to_modify->SetValue(
158 map_to_modify->DeleteValue(
162 resource_identifier);
165 // Update the content settings preference.
166 if (!is_incognito_) {
167 UpdatePref(primary_pattern,
175 primary_pattern, secondary_pattern, content_type, resource_identifier);
180 void PrefProvider::ClearAllContentSettingsRules(
181 ContentSettingsType content_type) {
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
185 OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
187 map_to_modify = &value_map_;
189 std::vector<Rule> rules_to_delete;
191 base::AutoLock auto_lock(lock_);
192 scoped_ptr<RuleIterator> rule_iterator(
193 map_to_modify->GetRuleIterator(content_type, std::string(), NULL));
194 // Copy the rules; we cannot call |UpdatePref| while holding |lock_|.
195 while (rule_iterator->HasNext())
196 rules_to_delete.push_back(rule_iterator->Next());
198 map_to_modify->DeleteValues(content_type, std::string());
201 for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
202 it != rules_to_delete.end(); ++it) {
203 UpdatePref(it->primary_pattern,
204 it->secondary_pattern,
209 NotifyObservers(ContentSettingsPattern(),
210 ContentSettingsPattern(),
215 PrefProvider::~PrefProvider() {
219 RuleIterator* PrefProvider::GetRuleIterator(
220 ContentSettingsType content_type,
221 const ResourceIdentifier& resource_identifier,
222 bool incognito) const {
224 return incognito_value_map_.GetRuleIterator(content_type,
227 return value_map_.GetRuleIterator(content_type, resource_identifier, &lock_);
230 // ////////////////////////////////////////////////////////////////////////////
233 void PrefProvider::UpdatePref(
234 const ContentSettingsPattern& primary_pattern,
235 const ContentSettingsPattern& secondary_pattern,
236 ContentSettingsType content_type,
237 const ResourceIdentifier& resource_identifier,
238 const base::Value* value) {
239 // Ensure that |lock_| is not held by this thread, since this function will
240 // send out notifications (by |~DictionaryPrefUpdate|).
243 base::AutoReset<bool> auto_reset(&updating_preferences_, true);
245 DictionaryPrefUpdate update(prefs_,
246 prefs::kContentSettingsPatternPairs);
247 DictionaryValue* pattern_pairs_settings = update.Get();
249 // Get settings dictionary for the given patterns.
250 std::string pattern_str(CreatePatternString(primary_pattern,
252 DictionaryValue* settings_dictionary = NULL;
253 bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
254 pattern_str, &settings_dictionary);
256 if (!found && value) {
257 settings_dictionary = new DictionaryValue;
258 pattern_pairs_settings->SetWithoutPathExpansion(
259 pattern_str, settings_dictionary);
262 if (settings_dictionary) {
263 std::string res_dictionary_path;
264 if (GetResourceTypeName(content_type, &res_dictionary_path) &&
265 !resource_identifier.empty()) {
266 DictionaryValue* resource_dictionary = NULL;
267 found = settings_dictionary->GetDictionary(
268 res_dictionary_path, &resource_dictionary);
271 return; // Nothing to remove. Exit early.
272 resource_dictionary = new DictionaryValue;
273 settings_dictionary->Set(res_dictionary_path, resource_dictionary);
275 // Update resource dictionary.
277 resource_dictionary->RemoveWithoutPathExpansion(resource_identifier,
279 if (resource_dictionary->empty()) {
280 settings_dictionary->RemoveWithoutPathExpansion(
281 res_dictionary_path, NULL);
284 resource_dictionary->SetWithoutPathExpansion(
285 resource_identifier, value->DeepCopy());
288 // Update settings dictionary.
289 std::string setting_path = GetTypeName(content_type);
291 settings_dictionary->RemoveWithoutPathExpansion(setting_path,
294 settings_dictionary->SetWithoutPathExpansion(
295 setting_path, value->DeepCopy());
298 // Remove the settings dictionary if it is empty.
299 if (settings_dictionary->empty()) {
300 pattern_pairs_settings->RemoveWithoutPathExpansion(
308 void PrefProvider::MigrateObsoleteMediaContentSetting() {
309 std::vector<Rule> rules_to_delete;
311 scoped_ptr<RuleIterator> rule_iterator(GetRuleIterator(
312 CONTENT_SETTINGS_TYPE_MEDIASTREAM, std::string(), false));
313 while (rule_iterator->HasNext()) {
314 // Skip default setting and rules without a value.
315 const content_settings::Rule& rule = rule_iterator->Next();
316 DCHECK(rule.primary_pattern != ContentSettingsPattern::Wildcard());
317 if (!rule.value.get())
319 rules_to_delete.push_back(rule);
323 for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
324 it != rules_to_delete.end(); ++it) {
325 const DictionaryValue* value_dict = NULL;
326 if (!it->value->GetAsDictionary(&value_dict) || value_dict->empty())
329 std::string audio_device, video_device;
330 value_dict->GetString(kAudioKey, &audio_device);
331 value_dict->GetString(kVideoKey, &video_device);
332 // Add the exception to the new microphone content setting.
333 if (!audio_device.empty()) {
334 SetWebsiteSetting(it->primary_pattern,
335 it->secondary_pattern,
336 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
338 Value::CreateIntegerValue(CONTENT_SETTING_ALLOW));
340 // Add the exception to the new camera content setting.
341 if (!video_device.empty()) {
342 SetWebsiteSetting(it->primary_pattern,
343 it->secondary_pattern,
344 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
346 Value::CreateIntegerValue(CONTENT_SETTING_ALLOW));
349 // Remove the old exception in CONTENT_SETTINGS_TYPE_MEDIASTREAM.
350 SetWebsiteSetting(it->primary_pattern,
351 it->secondary_pattern,
352 CONTENT_SETTINGS_TYPE_MEDIASTREAM,
358 void PrefProvider::ReadContentSettingsFromPref(bool overwrite) {
359 // |DictionaryPrefUpdate| sends out notifications when destructed. This
360 // construction order ensures |AutoLock| gets destroyed first and |lock_| is
361 // not held when the notifications are sent. Also, |auto_reset| must be still
362 // valid when the notifications are sent, so that |Observe| skips the
364 base::AutoReset<bool> auto_reset(&updating_preferences_, true);
365 DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs);
366 base::AutoLock auto_lock(lock_);
368 const DictionaryValue* all_settings_dictionary =
369 prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
374 // Careful: The returned value could be NULL if the pref has never been set.
375 if (!all_settings_dictionary)
378 DictionaryValue* mutable_settings;
379 scoped_ptr<DictionaryValue> mutable_settings_scope;
381 if (!is_incognito_) {
382 mutable_settings = update.Get();
384 // Create copy as we do not want to persist anything in OTR prefs.
385 mutable_settings = all_settings_dictionary->DeepCopy();
386 mutable_settings_scope.reset(mutable_settings);
388 // Convert all Unicode patterns into punycode form, then read.
389 CanonicalizeContentSettingsExceptions(mutable_settings);
391 size_t cookies_block_exception_count = 0;
392 size_t cookies_allow_exception_count = 0;
393 size_t cookies_session_only_exception_count = 0;
394 for (DictionaryValue::Iterator i(*mutable_settings); !i.IsAtEnd();
396 const std::string& pattern_str(i.key());
397 std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
398 ParsePatternString(pattern_str);
399 if (!pattern_pair.first.IsValid() ||
400 !pattern_pair.second.IsValid()) {
401 // TODO: Change this to DFATAL when crbug.com/132659 is fixed.
402 LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
406 // Get settings dictionary for the current pattern string, and read
407 // settings from the dictionary.
408 const DictionaryValue* settings_dictionary = NULL;
409 bool is_dictionary = i.value().GetAsDictionary(&settings_dictionary);
410 DCHECK(is_dictionary);
412 for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
413 ContentSettingsType content_type = static_cast<ContentSettingsType>(i);
415 std::string res_dictionary_path;
416 if (GetResourceTypeName(content_type, &res_dictionary_path)) {
417 const DictionaryValue* resource_dictionary = NULL;
418 if (settings_dictionary->GetDictionary(
419 res_dictionary_path, &resource_dictionary)) {
420 for (DictionaryValue::Iterator j(*resource_dictionary); !j.IsAtEnd();
422 const std::string& resource_identifier(j.key());
423 int setting = CONTENT_SETTING_DEFAULT;
424 bool is_integer = j.value().GetAsInteger(&setting);
426 DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
427 value_map_.SetValue(pattern_pair.first,
431 Value::CreateIntegerValue(setting));
436 if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type)) {
437 const DictionaryValue* setting = NULL;
438 // TODO(xians): Handle the non-dictionary types.
439 if (settings_dictionary->GetDictionaryWithoutPathExpansion(
440 GetTypeName(ContentSettingsType(i)), &setting)) {
441 DCHECK(!setting->empty());
442 value = setting->DeepCopy();
445 int setting = CONTENT_SETTING_DEFAULT;
446 if (settings_dictionary->GetIntegerWithoutPathExpansion(
447 GetTypeName(ContentSettingsType(i)), &setting)) {
448 DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
449 setting = FixObsoleteCookiePromptMode(content_type,
450 ContentSetting(setting));
451 value = Value::CreateIntegerValue(setting);
455 // |value_map_| will take the ownership of |value|.
457 value_map_.SetValue(pattern_pair.first,
460 ResourceIdentifier(),
462 if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
463 ContentSetting s = ValueToContentSetting(value);
465 case CONTENT_SETTING_ALLOW :
466 ++cookies_allow_exception_count;
468 case CONTENT_SETTING_BLOCK :
469 ++cookies_block_exception_count;
471 case CONTENT_SETTING_SESSION_ONLY :
472 ++cookies_session_only_exception_count;
482 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfBlockCookiesExceptions",
483 cookies_block_exception_count);
484 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfAllowCookiesExceptions",
485 cookies_allow_exception_count);
486 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfSessionOnlyCookiesExceptions",
487 cookies_session_only_exception_count);
490 void PrefProvider::OnContentSettingsPatternPairsChanged() {
491 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
493 if (updating_preferences_)
496 ReadContentSettingsFromPref(true);
498 NotifyObservers(ContentSettingsPattern(),
499 ContentSettingsPattern(),
500 CONTENT_SETTINGS_TYPE_DEFAULT,
505 void PrefProvider::CanonicalizeContentSettingsExceptions(
506 DictionaryValue* all_settings_dictionary) {
507 DCHECK(all_settings_dictionary);
509 std::vector<std::string> remove_items;
510 std::vector<std::pair<std::string, std::string> > move_items;
511 for (DictionaryValue::Iterator i(*all_settings_dictionary); !i.IsAtEnd();
513 const std::string& pattern_str(i.key());
514 std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
515 ParsePatternString(pattern_str);
516 if (!pattern_pair.first.IsValid() ||
517 !pattern_pair.second.IsValid()) {
518 LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
522 const std::string canonicalized_pattern_str = CreatePatternString(
523 pattern_pair.first, pattern_pair.second);
525 if (canonicalized_pattern_str.empty() ||
526 canonicalized_pattern_str == pattern_str) {
530 // Clear old pattern if prefs already have canonicalized pattern.
531 const DictionaryValue* new_pattern_settings_dictionary = NULL;
532 if (all_settings_dictionary->GetDictionaryWithoutPathExpansion(
533 canonicalized_pattern_str, &new_pattern_settings_dictionary)) {
534 remove_items.push_back(pattern_str);
538 // Move old pattern to canonicalized pattern.
539 const DictionaryValue* old_pattern_settings_dictionary = NULL;
540 if (i.value().GetAsDictionary(&old_pattern_settings_dictionary)) {
541 move_items.push_back(
542 std::make_pair(pattern_str, canonicalized_pattern_str));
546 for (size_t i = 0; i < remove_items.size(); ++i) {
547 all_settings_dictionary->RemoveWithoutPathExpansion(remove_items[i], NULL);
550 for (size_t i = 0; i < move_items.size(); ++i) {
551 scoped_ptr<Value> pattern_settings_dictionary;
552 all_settings_dictionary->RemoveWithoutPathExpansion(
553 move_items[i].first, &pattern_settings_dictionary);
554 all_settings_dictionary->SetWithoutPathExpansion(
555 move_items[i].second, pattern_settings_dictionary.release());
559 void PrefProvider::ShutdownOnUIThread() {
560 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
562 RemoveAllObservers();
563 pref_change_registrar_.RemoveAll();
567 void PrefProvider::AssertLockNotHeld() const {
569 // |Lock::Acquire()| will assert if the lock is held by this thread.
575 } // namespace content_settings