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 "base/strings/string_split.h"
18 #include "base/time/clock.h"
19 #include "base/time/default_clock.h"
20 #include "chrome/browser/content_settings/content_settings_utils.h"
21 #include "chrome/browser/content_settings/host_content_settings_map.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/pref_names.h"
24 #include "components/content_settings/core/browser/content_settings_rule.h"
25 #include "components/content_settings/core/common/content_settings.h"
26 #include "components/content_settings/core/common/content_settings_pattern.h"
27 #include "components/pref_registry/pref_registry_syncable.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/user_metrics.h"
32 using base::UserMetricsAction;
33 using content::BrowserThread;
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";
43 const char kLastUsed[] = "last_used";
45 ContentSetting FixObsoleteCookiePromptMode(ContentSettingsType content_type,
46 ContentSetting setting) {
47 if (content_type == CONTENT_SETTINGS_TYPE_COOKIES &&
48 setting == CONTENT_SETTING_ASK) {
49 return CONTENT_SETTING_BLOCK;
54 // If the given content type supports resource identifiers in user preferences,
55 // returns true and sets |pref_key| to the key in the content settings
56 // dictionary under which per-resource content settings are stored.
57 // Otherwise, returns false.
58 bool GetResourceTypeName(ContentSettingsType content_type,
59 std::string* pref_key) {
60 if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) {
61 *pref_key = kPerPluginPrefName;
69 namespace content_settings {
71 // ////////////////////////////////////////////////////////////////////////////
76 void PrefProvider::RegisterProfilePrefs(
77 user_prefs::PrefRegistrySyncable* registry) {
78 registry->RegisterIntegerPref(
79 prefs::kContentSettingsVersion,
80 ContentSettingsPattern::kContentSettingsPatternVersion,
81 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
82 registry->RegisterDictionaryPref(
83 prefs::kContentSettingsPatternPairs,
84 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
87 PrefProvider::PrefProvider(PrefService* prefs, bool incognito)
89 clock_(new base::DefaultClock()),
90 is_incognito_(incognito),
91 updating_preferences_(false) {
93 // Verify preferences version.
94 if (!prefs_->HasPrefPath(prefs::kContentSettingsVersion)) {
95 prefs_->SetInteger(prefs::kContentSettingsVersion,
96 ContentSettingsPattern::kContentSettingsPatternVersion);
98 if (prefs_->GetInteger(prefs::kContentSettingsVersion) >
99 ContentSettingsPattern::kContentSettingsPatternVersion) {
103 // Read content settings exceptions.
104 ReadContentSettingsFromPref(false);
106 if (!is_incognito_) {
107 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfExceptions",
111 // Migrate the obsolete media content setting exceptions to the new settings.
112 // This needs to be done after ReadContentSettingsFromPref().
114 MigrateObsoleteMediaContentSetting();
116 pref_change_registrar_.Init(prefs_);
117 pref_change_registrar_.Add(
118 prefs::kContentSettingsPatternPairs,
119 base::Bind(&PrefProvider::OnContentSettingsPatternPairsChanged,
120 base::Unretained(this)));
123 bool PrefProvider::SetWebsiteSetting(
124 const ContentSettingsPattern& primary_pattern,
125 const ContentSettingsPattern& secondary_pattern,
126 ContentSettingsType content_type,
127 const ResourceIdentifier& resource_identifier,
128 base::Value* in_value) {
129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
131 // Default settings are set using a wildcard pattern for both
132 // |primary_pattern| and |secondary_pattern|. Don't store default settings in
133 // the |PrefProvider|. The |PrefProvider| handles settings for specific
134 // sites/origins defined by the |primary_pattern| and the |secondary_pattern|.
135 // Default settings are handled by the |DefaultProvider|.
136 if (primary_pattern == ContentSettingsPattern::Wildcard() &&
137 secondary_pattern == ContentSettingsPattern::Wildcard() &&
138 resource_identifier.empty()) {
142 // At this point take the ownership of the |in_value|.
143 scoped_ptr<base::Value> value(in_value);
144 // Update in memory value map.
145 OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
147 map_to_modify = &value_map_;
150 base::AutoLock auto_lock(lock_);
152 map_to_modify->SetValue(
159 map_to_modify->DeleteValue(
163 resource_identifier);
166 // Update the content settings preference.
167 if (!is_incognito_) {
168 UpdatePref(primary_pattern,
176 primary_pattern, secondary_pattern, content_type, resource_identifier);
181 void PrefProvider::ClearAllContentSettingsRules(
182 ContentSettingsType content_type) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
186 OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
188 map_to_modify = &value_map_;
190 std::vector<Rule> rules_to_delete;
192 base::AutoLock auto_lock(lock_);
193 scoped_ptr<RuleIterator> rule_iterator(
194 map_to_modify->GetRuleIterator(content_type, std::string(), NULL));
195 // Copy the rules; we cannot call |UpdatePref| while holding |lock_|.
196 while (rule_iterator->HasNext())
197 rules_to_delete.push_back(rule_iterator->Next());
199 map_to_modify->DeleteValues(content_type, std::string());
202 for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
203 it != rules_to_delete.end(); ++it) {
204 UpdatePref(it->primary_pattern,
205 it->secondary_pattern,
210 NotifyObservers(ContentSettingsPattern(),
211 ContentSettingsPattern(),
216 PrefProvider::~PrefProvider() {
220 RuleIterator* PrefProvider::GetRuleIterator(
221 ContentSettingsType content_type,
222 const ResourceIdentifier& resource_identifier,
223 bool incognito) const {
225 return incognito_value_map_.GetRuleIterator(content_type,
228 return value_map_.GetRuleIterator(content_type, resource_identifier, &lock_);
231 // ////////////////////////////////////////////////////////////////////////////
234 void PrefProvider::UpdatePref(
235 const ContentSettingsPattern& primary_pattern,
236 const ContentSettingsPattern& secondary_pattern,
237 ContentSettingsType content_type,
238 const ResourceIdentifier& resource_identifier,
239 const base::Value* value) {
240 // Ensure that |lock_| is not held by this thread, since this function will
241 // send out notifications (by |~DictionaryPrefUpdate|).
244 base::AutoReset<bool> auto_reset(&updating_preferences_, true);
246 DictionaryPrefUpdate update(prefs_,
247 prefs::kContentSettingsPatternPairs);
248 base::DictionaryValue* pattern_pairs_settings = update.Get();
250 // Get settings dictionary for the given patterns.
251 std::string pattern_str(CreatePatternString(primary_pattern,
253 base::DictionaryValue* settings_dictionary = NULL;
254 bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
255 pattern_str, &settings_dictionary);
257 if (!found && value) {
258 settings_dictionary = new base::DictionaryValue;
259 pattern_pairs_settings->SetWithoutPathExpansion(
260 pattern_str, settings_dictionary);
263 if (settings_dictionary) {
264 std::string res_dictionary_path;
265 if (GetResourceTypeName(content_type, &res_dictionary_path) &&
266 !resource_identifier.empty()) {
267 base::DictionaryValue* resource_dictionary = NULL;
268 found = settings_dictionary->GetDictionary(
269 res_dictionary_path, &resource_dictionary);
272 return; // Nothing to remove. Exit early.
273 resource_dictionary = new base::DictionaryValue;
274 settings_dictionary->Set(res_dictionary_path, resource_dictionary);
276 // Update resource dictionary.
278 resource_dictionary->RemoveWithoutPathExpansion(resource_identifier,
280 if (resource_dictionary->empty()) {
281 settings_dictionary->RemoveWithoutPathExpansion(
282 res_dictionary_path, NULL);
285 resource_dictionary->SetWithoutPathExpansion(
286 resource_identifier, value->DeepCopy());
289 // Update settings dictionary.
290 std::string setting_path = GetTypeName(content_type);
292 settings_dictionary->RemoveWithoutPathExpansion(setting_path,
294 settings_dictionary->RemoveWithoutPathExpansion(kLastUsed, NULL);
296 settings_dictionary->SetWithoutPathExpansion(
297 setting_path, value->DeepCopy());
300 // Remove the settings dictionary if it is empty.
301 if (settings_dictionary->empty()) {
302 pattern_pairs_settings->RemoveWithoutPathExpansion(
310 void PrefProvider::MigrateObsoleteMediaContentSetting() {
311 std::vector<Rule> rules_to_delete;
313 scoped_ptr<RuleIterator> rule_iterator(GetRuleIterator(
314 CONTENT_SETTINGS_TYPE_MEDIASTREAM, std::string(), false));
315 while (rule_iterator->HasNext()) {
316 // Skip default setting and rules without a value.
317 const content_settings::Rule& rule = rule_iterator->Next();
318 DCHECK(rule.primary_pattern != ContentSettingsPattern::Wildcard());
319 if (!rule.value.get())
321 rules_to_delete.push_back(rule);
325 for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
326 it != rules_to_delete.end(); ++it) {
327 const base::DictionaryValue* value_dict = NULL;
328 if (!it->value->GetAsDictionary(&value_dict) || value_dict->empty())
331 std::string audio_device, video_device;
332 value_dict->GetString(kAudioKey, &audio_device);
333 value_dict->GetString(kVideoKey, &video_device);
334 // Add the exception to the new microphone content setting.
335 if (!audio_device.empty()) {
336 SetWebsiteSetting(it->primary_pattern,
337 it->secondary_pattern,
338 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
340 new base::FundamentalValue(CONTENT_SETTING_ALLOW));
342 // Add the exception to the new camera content setting.
343 if (!video_device.empty()) {
344 SetWebsiteSetting(it->primary_pattern,
345 it->secondary_pattern,
346 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
348 new base::FundamentalValue(CONTENT_SETTING_ALLOW));
351 // Remove the old exception in CONTENT_SETTINGS_TYPE_MEDIASTREAM.
352 SetWebsiteSetting(it->primary_pattern,
353 it->secondary_pattern,
354 CONTENT_SETTINGS_TYPE_MEDIASTREAM,
360 void PrefProvider::ReadContentSettingsFromPref(bool overwrite) {
361 // |DictionaryPrefUpdate| sends out notifications when destructed. This
362 // construction order ensures |AutoLock| gets destroyed first and |lock_| is
363 // not held when the notifications are sent. Also, |auto_reset| must be still
364 // valid when the notifications are sent, so that |Observe| skips the
366 base::AutoReset<bool> auto_reset(&updating_preferences_, true);
367 DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs);
368 base::AutoLock auto_lock(lock_);
370 const base::DictionaryValue* all_settings_dictionary =
371 prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
376 // Careful: The returned value could be NULL if the pref has never been set.
377 if (!all_settings_dictionary)
380 base::DictionaryValue* mutable_settings;
381 scoped_ptr<base::DictionaryValue> mutable_settings_scope;
383 if (!is_incognito_) {
384 mutable_settings = update.Get();
386 // Create copy as we do not want to persist anything in OTR prefs.
387 mutable_settings = all_settings_dictionary->DeepCopy();
388 mutable_settings_scope.reset(mutable_settings);
390 // Convert all Unicode patterns into punycode form, then read.
391 CanonicalizeContentSettingsExceptions(mutable_settings);
393 size_t cookies_block_exception_count = 0;
394 size_t cookies_allow_exception_count = 0;
395 size_t cookies_session_only_exception_count = 0;
396 for (base::DictionaryValue::Iterator i(*mutable_settings); !i.IsAtEnd();
398 const std::string& pattern_str(i.key());
399 std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
400 ParsePatternString(pattern_str);
401 if (!pattern_pair.first.IsValid() ||
402 !pattern_pair.second.IsValid()) {
403 // TODO: Change this to DFATAL when crbug.com/132659 is fixed.
404 LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
408 // Get settings dictionary for the current pattern string, and read
409 // settings from the dictionary.
410 const base::DictionaryValue* settings_dictionary = NULL;
411 bool is_dictionary = i.value().GetAsDictionary(&settings_dictionary);
412 DCHECK(is_dictionary);
414 for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
415 ContentSettingsType content_type = static_cast<ContentSettingsType>(i);
417 std::string res_dictionary_path;
418 if (GetResourceTypeName(content_type, &res_dictionary_path)) {
419 const base::DictionaryValue* resource_dictionary = NULL;
420 if (settings_dictionary->GetDictionary(
421 res_dictionary_path, &resource_dictionary)) {
422 for (base::DictionaryValue::Iterator j(*resource_dictionary);
425 const std::string& resource_identifier(j.key());
426 int setting = CONTENT_SETTING_DEFAULT;
427 bool is_integer = j.value().GetAsInteger(&setting);
429 DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
430 value_map_.SetValue(pattern_pair.first,
434 new base::FundamentalValue(setting));
438 base::Value* value = NULL;
439 if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type)) {
440 const base::DictionaryValue* setting = NULL;
441 // TODO(xians): Handle the non-dictionary types.
442 if (settings_dictionary->GetDictionaryWithoutPathExpansion(
443 GetTypeName(ContentSettingsType(i)), &setting)) {
444 DCHECK(!setting->empty());
445 value = setting->DeepCopy();
448 int setting = CONTENT_SETTING_DEFAULT;
449 if (settings_dictionary->GetIntegerWithoutPathExpansion(
450 GetTypeName(ContentSettingsType(i)), &setting)) {
451 DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
452 setting = FixObsoleteCookiePromptMode(content_type,
453 ContentSetting(setting));
454 value = new base::FundamentalValue(setting);
458 // |value_map_| will take the ownership of |value|.
460 value_map_.SetValue(pattern_pair.first,
463 ResourceIdentifier(),
465 if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
466 ContentSetting s = ValueToContentSetting(value);
468 case CONTENT_SETTING_ALLOW :
469 ++cookies_allow_exception_count;
471 case CONTENT_SETTING_BLOCK :
472 ++cookies_block_exception_count;
474 case CONTENT_SETTING_SESSION_ONLY :
475 ++cookies_session_only_exception_count;
485 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfBlockCookiesExceptions",
486 cookies_block_exception_count);
487 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfAllowCookiesExceptions",
488 cookies_allow_exception_count);
489 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfSessionOnlyCookiesExceptions",
490 cookies_session_only_exception_count);
493 void PrefProvider::OnContentSettingsPatternPairsChanged() {
494 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
496 if (updating_preferences_)
499 ReadContentSettingsFromPref(true);
501 NotifyObservers(ContentSettingsPattern(),
502 ContentSettingsPattern(),
503 CONTENT_SETTINGS_TYPE_DEFAULT,
508 void PrefProvider::CanonicalizeContentSettingsExceptions(
509 base::DictionaryValue* all_settings_dictionary) {
510 DCHECK(all_settings_dictionary);
512 std::vector<std::string> remove_items;
513 base::StringPairs move_items;
514 for (base::DictionaryValue::Iterator i(*all_settings_dictionary);
517 const std::string& pattern_str(i.key());
518 std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
519 ParsePatternString(pattern_str);
520 if (!pattern_pair.first.IsValid() ||
521 !pattern_pair.second.IsValid()) {
522 LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
526 const std::string canonicalized_pattern_str = CreatePatternString(
527 pattern_pair.first, pattern_pair.second);
529 if (canonicalized_pattern_str.empty() ||
530 canonicalized_pattern_str == pattern_str) {
534 // Clear old pattern if prefs already have canonicalized pattern.
535 const base::DictionaryValue* new_pattern_settings_dictionary = NULL;
536 if (all_settings_dictionary->GetDictionaryWithoutPathExpansion(
537 canonicalized_pattern_str, &new_pattern_settings_dictionary)) {
538 remove_items.push_back(pattern_str);
542 // Move old pattern to canonicalized pattern.
543 const base::DictionaryValue* old_pattern_settings_dictionary = NULL;
544 if (i.value().GetAsDictionary(&old_pattern_settings_dictionary)) {
545 move_items.push_back(
546 std::make_pair(pattern_str, canonicalized_pattern_str));
550 for (size_t i = 0; i < remove_items.size(); ++i) {
551 all_settings_dictionary->RemoveWithoutPathExpansion(remove_items[i], NULL);
554 for (size_t i = 0; i < move_items.size(); ++i) {
555 scoped_ptr<base::Value> pattern_settings_dictionary;
556 all_settings_dictionary->RemoveWithoutPathExpansion(
557 move_items[i].first, &pattern_settings_dictionary);
558 all_settings_dictionary->SetWithoutPathExpansion(
559 move_items[i].second, pattern_settings_dictionary.release());
563 void PrefProvider::ShutdownOnUIThread() {
564 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
566 RemoveAllObservers();
567 pref_change_registrar_.RemoveAll();
571 void PrefProvider::UpdateLastUsage(
572 const ContentSettingsPattern& primary_pattern,
573 const ContentSettingsPattern& secondary_pattern,
574 ContentSettingsType content_type) {
575 // Don't write if in incognito.
580 // Ensure that |lock_| is not held by this thread, since this function will
581 // send out notifications (by |~DictionaryPrefUpdate|).
584 base::AutoReset<bool> auto_reset(&updating_preferences_, true);
586 DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs);
587 base::DictionaryValue* pattern_pairs_settings = update.Get();
589 std::string pattern_str(
590 CreatePatternString(primary_pattern, secondary_pattern));
591 base::DictionaryValue* settings_dictionary = NULL;
592 bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
593 pattern_str, &settings_dictionary);
596 settings_dictionary = new base::DictionaryValue;
597 pattern_pairs_settings->SetWithoutPathExpansion(pattern_str,
598 settings_dictionary);
601 base::DictionaryValue* last_used_dictionary = NULL;
602 found = settings_dictionary->GetDictionaryWithoutPathExpansion(
603 kLastUsed, &last_used_dictionary);
606 last_used_dictionary = new base::DictionaryValue;
607 settings_dictionary->SetWithoutPathExpansion(kLastUsed,
608 last_used_dictionary);
611 std::string settings_path = GetTypeName(content_type);
612 last_used_dictionary->Set(
613 settings_path, new base::FundamentalValue(clock_->Now().ToDoubleT()));
617 base::Time PrefProvider::GetLastUsage(
618 const ContentSettingsPattern& primary_pattern,
619 const ContentSettingsPattern& secondary_pattern,
620 ContentSettingsType content_type) {
621 const base::DictionaryValue* pattern_pairs_settings =
622 prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
623 std::string pattern_str(
624 CreatePatternString(primary_pattern, secondary_pattern));
626 const base::DictionaryValue* settings_dictionary = NULL;
627 bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
628 pattern_str, &settings_dictionary);
633 const base::DictionaryValue* last_used_dictionary = NULL;
634 found = settings_dictionary->GetDictionaryWithoutPathExpansion(
635 kLastUsed, &last_used_dictionary);
640 double last_used_time;
641 found = last_used_dictionary->GetDoubleWithoutPathExpansion(
642 GetTypeName(content_type), &last_used_time);
647 return base::Time::FromDoubleT(last_used_time);
650 void PrefProvider::AssertLockNotHeld() const {
652 // |Lock::Acquire()| will assert if the lock is held by this thread.
658 void PrefProvider::SetClockForTesting(scoped_ptr<base::Clock> clock) {
659 clock_ = clock.Pass();
662 } // namespace content_settings