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 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";
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
,
127 base::Value
* in_value
) {
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 base::DictionaryValue
* pattern_pairs_settings
= update
.Get();
249 // Get settings dictionary for the given patterns.
250 std::string
pattern_str(CreatePatternString(primary_pattern
,
252 base::DictionaryValue
* settings_dictionary
= NULL
;
253 bool found
= pattern_pairs_settings
->GetDictionaryWithoutPathExpansion(
254 pattern_str
, &settings_dictionary
);
256 if (!found
&& value
) {
257 settings_dictionary
= new base::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 base::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 base::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 base::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 base::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 base::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 base::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 base::DictionaryValue
* mutable_settings
;
379 scoped_ptr
<base::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 (base::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 base::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 base::DictionaryValue
* resource_dictionary
= NULL
;
418 if (settings_dictionary
->GetDictionary(
419 res_dictionary_path
, &resource_dictionary
)) {
420 for (base::DictionaryValue::Iterator
j(*resource_dictionary
);
423 const std::string
& resource_identifier(j
.key());
424 int setting
= CONTENT_SETTING_DEFAULT
;
425 bool is_integer
= j
.value().GetAsInteger(&setting
);
427 DCHECK_NE(CONTENT_SETTING_DEFAULT
, setting
);
428 value_map_
.SetValue(pattern_pair
.first
,
432 base::Value::CreateIntegerValue(setting
));
436 base::Value
* value
= NULL
;
437 if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type
)) {
438 const base::DictionaryValue
* setting
= NULL
;
439 // TODO(xians): Handle the non-dictionary types.
440 if (settings_dictionary
->GetDictionaryWithoutPathExpansion(
441 GetTypeName(ContentSettingsType(i
)), &setting
)) {
442 DCHECK(!setting
->empty());
443 value
= setting
->DeepCopy();
446 int setting
= CONTENT_SETTING_DEFAULT
;
447 if (settings_dictionary
->GetIntegerWithoutPathExpansion(
448 GetTypeName(ContentSettingsType(i
)), &setting
)) {
449 DCHECK_NE(CONTENT_SETTING_DEFAULT
, setting
);
450 setting
= FixObsoleteCookiePromptMode(content_type
,
451 ContentSetting(setting
));
452 value
= base::Value::CreateIntegerValue(setting
);
456 // |value_map_| will take the ownership of |value|.
458 value_map_
.SetValue(pattern_pair
.first
,
461 ResourceIdentifier(),
463 if (content_type
== CONTENT_SETTINGS_TYPE_COOKIES
) {
464 ContentSetting s
= ValueToContentSetting(value
);
466 case CONTENT_SETTING_ALLOW
:
467 ++cookies_allow_exception_count
;
469 case CONTENT_SETTING_BLOCK
:
470 ++cookies_block_exception_count
;
472 case CONTENT_SETTING_SESSION_ONLY
:
473 ++cookies_session_only_exception_count
;
483 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfBlockCookiesExceptions",
484 cookies_block_exception_count
);
485 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfAllowCookiesExceptions",
486 cookies_allow_exception_count
);
487 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfSessionOnlyCookiesExceptions",
488 cookies_session_only_exception_count
);
491 void PrefProvider::OnContentSettingsPatternPairsChanged() {
492 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
494 if (updating_preferences_
)
497 ReadContentSettingsFromPref(true);
499 NotifyObservers(ContentSettingsPattern(),
500 ContentSettingsPattern(),
501 CONTENT_SETTINGS_TYPE_DEFAULT
,
506 void PrefProvider::CanonicalizeContentSettingsExceptions(
507 base::DictionaryValue
* all_settings_dictionary
) {
508 DCHECK(all_settings_dictionary
);
510 std::vector
<std::string
> remove_items
;
511 std::vector
<std::pair
<std::string
, std::string
> > move_items
;
512 for (base::DictionaryValue::Iterator
i(*all_settings_dictionary
);
515 const std::string
& pattern_str(i
.key());
516 std::pair
<ContentSettingsPattern
, ContentSettingsPattern
> pattern_pair
=
517 ParsePatternString(pattern_str
);
518 if (!pattern_pair
.first
.IsValid() ||
519 !pattern_pair
.second
.IsValid()) {
520 LOG(ERROR
) << "Invalid pattern strings: " << pattern_str
;
524 const std::string canonicalized_pattern_str
= CreatePatternString(
525 pattern_pair
.first
, pattern_pair
.second
);
527 if (canonicalized_pattern_str
.empty() ||
528 canonicalized_pattern_str
== pattern_str
) {
532 // Clear old pattern if prefs already have canonicalized pattern.
533 const base::DictionaryValue
* new_pattern_settings_dictionary
= NULL
;
534 if (all_settings_dictionary
->GetDictionaryWithoutPathExpansion(
535 canonicalized_pattern_str
, &new_pattern_settings_dictionary
)) {
536 remove_items
.push_back(pattern_str
);
540 // Move old pattern to canonicalized pattern.
541 const base::DictionaryValue
* old_pattern_settings_dictionary
= NULL
;
542 if (i
.value().GetAsDictionary(&old_pattern_settings_dictionary
)) {
543 move_items
.push_back(
544 std::make_pair(pattern_str
, canonicalized_pattern_str
));
548 for (size_t i
= 0; i
< remove_items
.size(); ++i
) {
549 all_settings_dictionary
->RemoveWithoutPathExpansion(remove_items
[i
], NULL
);
552 for (size_t i
= 0; i
< move_items
.size(); ++i
) {
553 scoped_ptr
<base::Value
> pattern_settings_dictionary
;
554 all_settings_dictionary
->RemoveWithoutPathExpansion(
555 move_items
[i
].first
, &pattern_settings_dictionary
);
556 all_settings_dictionary
->SetWithoutPathExpansion(
557 move_items
[i
].second
, pattern_settings_dictionary
.release());
561 void PrefProvider::ShutdownOnUIThread() {
562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
564 RemoveAllObservers();
565 pref_change_registrar_
.RemoveAll();
569 void PrefProvider::AssertLockNotHeld() const {
571 // |Lock::Acquire()| will assert if the lock is held by this thread.
577 } // namespace content_settings