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/time/clock.h"
18 #include "base/time/default_clock.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/content_settings/content_settings_rule.h"
21 #include "chrome/browser/content_settings/content_settings_utils.h"
22 #include "chrome/browser/content_settings/host_content_settings_map.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/common/content_settings.h"
25 #include "chrome/common/content_settings_pattern.h"
26 #include "chrome/common/pref_names.h"
27 #include "components/pref_registry/pref_registry_syncable.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/notification_details.h"
30 #include "content/public/browser/notification_source.h"
31 #include "content/public/browser/user_metrics.h"
34 using base::UserMetricsAction;
35 using content::BrowserThread;
39 typedef std::pair<std::string, std::string> StringPair;
40 typedef std::map<std::string, std::string> StringMap;
42 const char kPerPluginPrefName[] = "per_plugin";
43 const char kAudioKey[] = "audio";
44 const char kVideoKey[] = "video";
45 const char kLastUsed[] = "last_used";
47 ContentSetting FixObsoleteCookiePromptMode(ContentSettingsType content_type,
48 ContentSetting setting) {
49 if (content_type == CONTENT_SETTINGS_TYPE_COOKIES &&
50 setting == CONTENT_SETTING_ASK) {
51 return CONTENT_SETTING_BLOCK;
56 // If the given content type supports resource identifiers in user preferences,
57 // returns true and sets |pref_key| to the key in the content settings
58 // dictionary under which per-resource content settings are stored.
59 // Otherwise, returns false.
60 bool GetResourceTypeName(ContentSettingsType content_type,
61 std::string* pref_key) {
62 if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) {
63 *pref_key = kPerPluginPrefName;
71 namespace content_settings {
73 // ////////////////////////////////////////////////////////////////////////////
78 void PrefProvider::RegisterProfilePrefs(
79 user_prefs::PrefRegistrySyncable* registry) {
80 registry->RegisterIntegerPref(
81 prefs::kContentSettingsVersion,
82 ContentSettingsPattern::kContentSettingsPatternVersion,
83 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
84 registry->RegisterDictionaryPref(
85 prefs::kContentSettingsPatternPairs,
86 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
89 PrefProvider::PrefProvider(PrefService* prefs, bool incognito)
91 clock_(new base::DefaultClock()),
92 is_incognito_(incognito),
93 updating_preferences_(false) {
95 // Verify preferences version.
96 if (!prefs_->HasPrefPath(prefs::kContentSettingsVersion)) {
97 prefs_->SetInteger(prefs::kContentSettingsVersion,
98 ContentSettingsPattern::kContentSettingsPatternVersion);
100 if (prefs_->GetInteger(prefs::kContentSettingsVersion) >
101 ContentSettingsPattern::kContentSettingsPatternVersion) {
105 // Read content settings exceptions.
106 ReadContentSettingsFromPref(false);
108 if (!is_incognito_) {
109 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfExceptions",
113 // Migrate the obsolete media content setting exceptions to the new settings.
114 // This needs to be done after ReadContentSettingsFromPref().
116 MigrateObsoleteMediaContentSetting();
118 pref_change_registrar_.Init(prefs_);
119 pref_change_registrar_.Add(
120 prefs::kContentSettingsPatternPairs,
121 base::Bind(&PrefProvider::OnContentSettingsPatternPairsChanged,
122 base::Unretained(this)));
125 bool PrefProvider::SetWebsiteSetting(
126 const ContentSettingsPattern& primary_pattern,
127 const ContentSettingsPattern& secondary_pattern,
128 ContentSettingsType content_type,
129 const ResourceIdentifier& resource_identifier,
130 base::Value* in_value) {
131 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
133 // Default settings are set using a wildcard pattern for both
134 // |primary_pattern| and |secondary_pattern|. Don't store default settings in
135 // the |PrefProvider|. The |PrefProvider| handles settings for specific
136 // sites/origins defined by the |primary_pattern| and the |secondary_pattern|.
137 // Default settings are handled by the |DefaultProvider|.
138 if (primary_pattern == ContentSettingsPattern::Wildcard() &&
139 secondary_pattern == ContentSettingsPattern::Wildcard() &&
140 resource_identifier.empty()) {
144 // At this point take the ownership of the |in_value|.
145 scoped_ptr<base::Value> value(in_value);
146 // Update in memory value map.
147 OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
149 map_to_modify = &value_map_;
152 base::AutoLock auto_lock(lock_);
154 map_to_modify->SetValue(
161 map_to_modify->DeleteValue(
165 resource_identifier);
168 // Update the content settings preference.
169 if (!is_incognito_) {
170 UpdatePref(primary_pattern,
178 primary_pattern, secondary_pattern, content_type, resource_identifier);
183 void PrefProvider::ClearAllContentSettingsRules(
184 ContentSettingsType content_type) {
185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
188 OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
190 map_to_modify = &value_map_;
192 std::vector<Rule> rules_to_delete;
194 base::AutoLock auto_lock(lock_);
195 scoped_ptr<RuleIterator> rule_iterator(
196 map_to_modify->GetRuleIterator(content_type, std::string(), NULL));
197 // Copy the rules; we cannot call |UpdatePref| while holding |lock_|.
198 while (rule_iterator->HasNext())
199 rules_to_delete.push_back(rule_iterator->Next());
201 map_to_modify->DeleteValues(content_type, std::string());
204 for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
205 it != rules_to_delete.end(); ++it) {
206 UpdatePref(it->primary_pattern,
207 it->secondary_pattern,
212 NotifyObservers(ContentSettingsPattern(),
213 ContentSettingsPattern(),
218 PrefProvider::~PrefProvider() {
222 RuleIterator* PrefProvider::GetRuleIterator(
223 ContentSettingsType content_type,
224 const ResourceIdentifier& resource_identifier,
225 bool incognito) const {
227 return incognito_value_map_.GetRuleIterator(content_type,
230 return value_map_.GetRuleIterator(content_type, resource_identifier, &lock_);
233 // ////////////////////////////////////////////////////////////////////////////
236 void PrefProvider::UpdatePref(
237 const ContentSettingsPattern& primary_pattern,
238 const ContentSettingsPattern& secondary_pattern,
239 ContentSettingsType content_type,
240 const ResourceIdentifier& resource_identifier,
241 const base::Value* value) {
242 // Ensure that |lock_| is not held by this thread, since this function will
243 // send out notifications (by |~DictionaryPrefUpdate|).
246 base::AutoReset<bool> auto_reset(&updating_preferences_, true);
248 DictionaryPrefUpdate update(prefs_,
249 prefs::kContentSettingsPatternPairs);
250 base::DictionaryValue* pattern_pairs_settings = update.Get();
252 // Get settings dictionary for the given patterns.
253 std::string pattern_str(CreatePatternString(primary_pattern,
255 base::DictionaryValue* settings_dictionary = NULL;
256 bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
257 pattern_str, &settings_dictionary);
259 if (!found && value) {
260 settings_dictionary = new base::DictionaryValue;
261 pattern_pairs_settings->SetWithoutPathExpansion(
262 pattern_str, settings_dictionary);
265 if (settings_dictionary) {
266 std::string res_dictionary_path;
267 if (GetResourceTypeName(content_type, &res_dictionary_path) &&
268 !resource_identifier.empty()) {
269 base::DictionaryValue* resource_dictionary = NULL;
270 found = settings_dictionary->GetDictionary(
271 res_dictionary_path, &resource_dictionary);
274 return; // Nothing to remove. Exit early.
275 resource_dictionary = new base::DictionaryValue;
276 settings_dictionary->Set(res_dictionary_path, resource_dictionary);
278 // Update resource dictionary.
280 resource_dictionary->RemoveWithoutPathExpansion(resource_identifier,
282 if (resource_dictionary->empty()) {
283 settings_dictionary->RemoveWithoutPathExpansion(
284 res_dictionary_path, NULL);
287 resource_dictionary->SetWithoutPathExpansion(
288 resource_identifier, value->DeepCopy());
291 // Update settings dictionary.
292 std::string setting_path = GetTypeName(content_type);
294 settings_dictionary->RemoveWithoutPathExpansion(setting_path,
296 settings_dictionary->RemoveWithoutPathExpansion(kLastUsed, NULL);
298 settings_dictionary->SetWithoutPathExpansion(
299 setting_path, value->DeepCopy());
302 // Remove the settings dictionary if it is empty.
303 if (settings_dictionary->empty()) {
304 pattern_pairs_settings->RemoveWithoutPathExpansion(
312 void PrefProvider::MigrateObsoleteMediaContentSetting() {
313 std::vector<Rule> rules_to_delete;
315 scoped_ptr<RuleIterator> rule_iterator(GetRuleIterator(
316 CONTENT_SETTINGS_TYPE_MEDIASTREAM, std::string(), false));
317 while (rule_iterator->HasNext()) {
318 // Skip default setting and rules without a value.
319 const content_settings::Rule& rule = rule_iterator->Next();
320 DCHECK(rule.primary_pattern != ContentSettingsPattern::Wildcard());
321 if (!rule.value.get())
323 rules_to_delete.push_back(rule);
327 for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
328 it != rules_to_delete.end(); ++it) {
329 const base::DictionaryValue* value_dict = NULL;
330 if (!it->value->GetAsDictionary(&value_dict) || value_dict->empty())
333 std::string audio_device, video_device;
334 value_dict->GetString(kAudioKey, &audio_device);
335 value_dict->GetString(kVideoKey, &video_device);
336 // Add the exception to the new microphone content setting.
337 if (!audio_device.empty()) {
338 SetWebsiteSetting(it->primary_pattern,
339 it->secondary_pattern,
340 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
342 new base::FundamentalValue(CONTENT_SETTING_ALLOW));
344 // Add the exception to the new camera content setting.
345 if (!video_device.empty()) {
346 SetWebsiteSetting(it->primary_pattern,
347 it->secondary_pattern,
348 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
350 new base::FundamentalValue(CONTENT_SETTING_ALLOW));
353 // Remove the old exception in CONTENT_SETTINGS_TYPE_MEDIASTREAM.
354 SetWebsiteSetting(it->primary_pattern,
355 it->secondary_pattern,
356 CONTENT_SETTINGS_TYPE_MEDIASTREAM,
362 void PrefProvider::ReadContentSettingsFromPref(bool overwrite) {
363 // |DictionaryPrefUpdate| sends out notifications when destructed. This
364 // construction order ensures |AutoLock| gets destroyed first and |lock_| is
365 // not held when the notifications are sent. Also, |auto_reset| must be still
366 // valid when the notifications are sent, so that |Observe| skips the
368 base::AutoReset<bool> auto_reset(&updating_preferences_, true);
369 DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs);
370 base::AutoLock auto_lock(lock_);
372 const base::DictionaryValue* all_settings_dictionary =
373 prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
378 // Careful: The returned value could be NULL if the pref has never been set.
379 if (!all_settings_dictionary)
382 base::DictionaryValue* mutable_settings;
383 scoped_ptr<base::DictionaryValue> mutable_settings_scope;
385 if (!is_incognito_) {
386 mutable_settings = update.Get();
388 // Create copy as we do not want to persist anything in OTR prefs.
389 mutable_settings = all_settings_dictionary->DeepCopy();
390 mutable_settings_scope.reset(mutable_settings);
392 // Convert all Unicode patterns into punycode form, then read.
393 CanonicalizeContentSettingsExceptions(mutable_settings);
395 size_t cookies_block_exception_count = 0;
396 size_t cookies_allow_exception_count = 0;
397 size_t cookies_session_only_exception_count = 0;
398 for (base::DictionaryValue::Iterator i(*mutable_settings); !i.IsAtEnd();
400 const std::string& pattern_str(i.key());
401 std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
402 ParsePatternString(pattern_str);
403 if (!pattern_pair.first.IsValid() ||
404 !pattern_pair.second.IsValid()) {
405 // TODO: Change this to DFATAL when crbug.com/132659 is fixed.
406 LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
410 // Get settings dictionary for the current pattern string, and read
411 // settings from the dictionary.
412 const base::DictionaryValue* settings_dictionary = NULL;
413 bool is_dictionary = i.value().GetAsDictionary(&settings_dictionary);
414 DCHECK(is_dictionary);
416 for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
417 ContentSettingsType content_type = static_cast<ContentSettingsType>(i);
419 std::string res_dictionary_path;
420 if (GetResourceTypeName(content_type, &res_dictionary_path)) {
421 const base::DictionaryValue* resource_dictionary = NULL;
422 if (settings_dictionary->GetDictionary(
423 res_dictionary_path, &resource_dictionary)) {
424 for (base::DictionaryValue::Iterator j(*resource_dictionary);
427 const std::string& resource_identifier(j.key());
428 int setting = CONTENT_SETTING_DEFAULT;
429 bool is_integer = j.value().GetAsInteger(&setting);
431 DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
432 value_map_.SetValue(pattern_pair.first,
436 new base::FundamentalValue(setting));
440 base::Value* value = NULL;
441 if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type)) {
442 const base::DictionaryValue* setting = NULL;
443 // TODO(xians): Handle the non-dictionary types.
444 if (settings_dictionary->GetDictionaryWithoutPathExpansion(
445 GetTypeName(ContentSettingsType(i)), &setting)) {
446 DCHECK(!setting->empty());
447 value = setting->DeepCopy();
450 int setting = CONTENT_SETTING_DEFAULT;
451 if (settings_dictionary->GetIntegerWithoutPathExpansion(
452 GetTypeName(ContentSettingsType(i)), &setting)) {
453 DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
454 setting = FixObsoleteCookiePromptMode(content_type,
455 ContentSetting(setting));
456 value = new base::FundamentalValue(setting);
460 // |value_map_| will take the ownership of |value|.
462 value_map_.SetValue(pattern_pair.first,
465 ResourceIdentifier(),
467 if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
468 ContentSetting s = ValueToContentSetting(value);
470 case CONTENT_SETTING_ALLOW :
471 ++cookies_allow_exception_count;
473 case CONTENT_SETTING_BLOCK :
474 ++cookies_block_exception_count;
476 case CONTENT_SETTING_SESSION_ONLY :
477 ++cookies_session_only_exception_count;
487 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfBlockCookiesExceptions",
488 cookies_block_exception_count);
489 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfAllowCookiesExceptions",
490 cookies_allow_exception_count);
491 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfSessionOnlyCookiesExceptions",
492 cookies_session_only_exception_count);
495 void PrefProvider::OnContentSettingsPatternPairsChanged() {
496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
498 if (updating_preferences_)
501 ReadContentSettingsFromPref(true);
503 NotifyObservers(ContentSettingsPattern(),
504 ContentSettingsPattern(),
505 CONTENT_SETTINGS_TYPE_DEFAULT,
510 void PrefProvider::CanonicalizeContentSettingsExceptions(
511 base::DictionaryValue* all_settings_dictionary) {
512 DCHECK(all_settings_dictionary);
514 std::vector<std::string> remove_items;
515 std::vector<std::pair<std::string, std::string> > move_items;
516 for (base::DictionaryValue::Iterator i(*all_settings_dictionary);
519 const std::string& pattern_str(i.key());
520 std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
521 ParsePatternString(pattern_str);
522 if (!pattern_pair.first.IsValid() ||
523 !pattern_pair.second.IsValid()) {
524 LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
528 const std::string canonicalized_pattern_str = CreatePatternString(
529 pattern_pair.first, pattern_pair.second);
531 if (canonicalized_pattern_str.empty() ||
532 canonicalized_pattern_str == pattern_str) {
536 // Clear old pattern if prefs already have canonicalized pattern.
537 const base::DictionaryValue* new_pattern_settings_dictionary = NULL;
538 if (all_settings_dictionary->GetDictionaryWithoutPathExpansion(
539 canonicalized_pattern_str, &new_pattern_settings_dictionary)) {
540 remove_items.push_back(pattern_str);
544 // Move old pattern to canonicalized pattern.
545 const base::DictionaryValue* old_pattern_settings_dictionary = NULL;
546 if (i.value().GetAsDictionary(&old_pattern_settings_dictionary)) {
547 move_items.push_back(
548 std::make_pair(pattern_str, canonicalized_pattern_str));
552 for (size_t i = 0; i < remove_items.size(); ++i) {
553 all_settings_dictionary->RemoveWithoutPathExpansion(remove_items[i], NULL);
556 for (size_t i = 0; i < move_items.size(); ++i) {
557 scoped_ptr<base::Value> pattern_settings_dictionary;
558 all_settings_dictionary->RemoveWithoutPathExpansion(
559 move_items[i].first, &pattern_settings_dictionary);
560 all_settings_dictionary->SetWithoutPathExpansion(
561 move_items[i].second, pattern_settings_dictionary.release());
565 void PrefProvider::ShutdownOnUIThread() {
566 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
568 RemoveAllObservers();
569 pref_change_registrar_.RemoveAll();
573 void PrefProvider::UpdateLastUsage(
574 const ContentSettingsPattern& primary_pattern,
575 const ContentSettingsPattern& secondary_pattern,
576 ContentSettingsType content_type) {
577 // Don't write if in incognito.
582 // Ensure that |lock_| is not held by this thread, since this function will
583 // send out notifications (by |~DictionaryPrefUpdate|).
586 base::AutoReset<bool> auto_reset(&updating_preferences_, true);
588 DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs);
589 base::DictionaryValue* pattern_pairs_settings = update.Get();
591 std::string pattern_str(
592 CreatePatternString(primary_pattern, secondary_pattern));
593 base::DictionaryValue* settings_dictionary = NULL;
594 bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
595 pattern_str, &settings_dictionary);
598 settings_dictionary = new base::DictionaryValue;
599 pattern_pairs_settings->SetWithoutPathExpansion(pattern_str,
600 settings_dictionary);
603 base::DictionaryValue* last_used_dictionary = NULL;
604 found = settings_dictionary->GetDictionaryWithoutPathExpansion(
605 kLastUsed, &last_used_dictionary);
608 last_used_dictionary = new base::DictionaryValue;
609 settings_dictionary->SetWithoutPathExpansion(kLastUsed,
610 last_used_dictionary);
613 std::string settings_path = GetTypeName(content_type);
614 last_used_dictionary->Set(
615 settings_path, new base::FundamentalValue(clock_->Now().ToDoubleT()));
619 base::Time PrefProvider::GetLastUsage(
620 const ContentSettingsPattern& primary_pattern,
621 const ContentSettingsPattern& secondary_pattern,
622 ContentSettingsType content_type) {
623 const base::DictionaryValue* pattern_pairs_settings =
624 prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
625 std::string pattern_str(
626 CreatePatternString(primary_pattern, secondary_pattern));
628 const base::DictionaryValue* settings_dictionary = NULL;
629 bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
630 pattern_str, &settings_dictionary);
635 const base::DictionaryValue* last_used_dictionary = NULL;
636 found = settings_dictionary->GetDictionaryWithoutPathExpansion(
637 kLastUsed, &last_used_dictionary);
642 double last_used_time;
643 found = last_used_dictionary->GetDoubleWithoutPathExpansion(
644 GetTypeName(content_type), &last_used_time);
649 return base::Time::FromDoubleT(last_used_time);
652 void PrefProvider::AssertLockNotHeld() const {
654 // |Lock::Acquire()| will assert if the lock is held by this thread.
660 void PrefProvider::SetClockForTesting(scoped_ptr<base::Clock> clock) {
661 clock_ = clock.Pass();
664 } // namespace content_settings