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