1 // Copyright 2015 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 "components/content_settings/core/browser/content_settings_pref.h"
7 #include "base/auto_reset.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/prefs/scoped_user_pref_update.h"
12 #include "base/strings/string_split.h"
13 #include "base/time/clock.h"
14 #include "components/content_settings/core/browser/content_settings_rule.h"
15 #include "components/content_settings/core/browser/content_settings_utils.h"
16 #include "components/content_settings/core/browser/host_content_settings_map.h"
17 #include "components/content_settings/core/common/content_settings.h"
18 #include "components/content_settings/core/common/content_settings_pattern.h"
19 #include "components/content_settings/core/common/pref_names.h"
24 const char kSettingPath
[] = "setting";
25 const char kPerResourceIdentifierPrefName
[] = "per_resource";
26 const char kLastUsed
[] = "last_used";
28 ContentSetting
FixObsoleteCookiePromptMode(ContentSettingsType content_type
,
29 ContentSetting setting
) {
30 if (content_type
== CONTENT_SETTINGS_TYPE_COOKIES
&&
31 setting
== CONTENT_SETTING_ASK
) {
32 return CONTENT_SETTING_BLOCK
;
37 // If the given content type supports resource identifiers in user preferences,
38 // returns true and sets |pref_key| to the key in the content settings
39 // dictionary under which per-resource content settings are stored.
40 // Otherwise, returns false.
41 bool SupportsResourceIdentifiers(ContentSettingsType content_type
) {
42 return content_type
== CONTENT_SETTINGS_TYPE_PLUGINS
;
47 namespace content_settings
{
49 ContentSettingsPref::ContentSettingsPref(
50 ContentSettingsType content_type
,
52 PrefChangeRegistrar
* registrar
,
53 const std::string
& pref_name
,
55 NotifyObserversCallback notify_callback
)
56 : content_type_(content_type
),
58 registrar_(registrar
),
59 pref_name_(pref_name
),
60 is_incognito_(incognito
),
61 updating_preferences_(false),
62 notify_callback_(notify_callback
) {
65 ReadContentSettingsFromPref();
69 base::Bind(&ContentSettingsPref::OnPrefChanged
, base::Unretained(this)));
72 ContentSettingsPref::~ContentSettingsPref() {
75 RuleIterator
* ContentSettingsPref::GetRuleIterator(
76 const ResourceIdentifier
& resource_identifier
,
77 bool incognito
) const {
79 return incognito_value_map_
.GetRuleIterator(content_type_
,
82 return value_map_
.GetRuleIterator(content_type_
, resource_identifier
, &lock_
);
85 bool ContentSettingsPref::SetWebsiteSetting(
86 const ContentSettingsPattern
& primary_pattern
,
87 const ContentSettingsPattern
& secondary_pattern
,
88 const ResourceIdentifier
& resource_identifier
,
89 base::Value
* in_value
) {
90 DCHECK(thread_checker_
.CalledOnValidThread());
92 DCHECK(primary_pattern
!= ContentSettingsPattern::Wildcard() ||
93 secondary_pattern
!= ContentSettingsPattern::Wildcard() ||
94 !resource_identifier
.empty());
96 // At this point take the ownership of the |in_value|.
97 scoped_ptr
<base::Value
> value(in_value
);
99 // Update in memory value map.
100 OriginIdentifierValueMap
* map_to_modify
= &incognito_value_map_
;
102 map_to_modify
= &value_map_
;
105 base::AutoLock
auto_lock(lock_
);
107 map_to_modify
->SetValue(
114 map_to_modify
->DeleteValue(
118 resource_identifier
);
121 // Update the content settings preference.
122 if (!is_incognito_
) {
123 UpdatePref(primary_pattern
,
129 notify_callback_
.Run(
130 primary_pattern
, secondary_pattern
, content_type_
, resource_identifier
);
135 void ContentSettingsPref::ClearAllContentSettingsRules() {
136 DCHECK(thread_checker_
.CalledOnValidThread());
139 OriginIdentifierValueMap
* map_to_modify
= &incognito_value_map_
;
141 map_to_modify
= &value_map_
;
144 base::AutoLock
auto_lock(lock_
);
145 map_to_modify
->clear();
148 if (!is_incognito_
) {
149 // Clear the preference.
151 base::AutoReset
<bool> auto_reset(&updating_preferences_
, true);
152 DictionaryPrefUpdate
update(prefs_
, pref_name_
);
153 base::DictionaryValue
* pattern_pairs_settings
= update
.Get();
154 pattern_pairs_settings
->Clear();
158 notify_callback_
.Run(ContentSettingsPattern(),
159 ContentSettingsPattern(),
161 ResourceIdentifier());
164 void ContentSettingsPref::UpdateLastUsage(
165 const ContentSettingsPattern
& primary_pattern
,
166 const ContentSettingsPattern
& secondary_pattern
,
167 base::Clock
* clock
) {
168 // Don't write if in incognito.
173 // Ensure that |lock_| is not held by this thread, since this function will
174 // send out notifications (by |~DictionaryPrefUpdate|).
177 base::AutoReset
<bool> auto_reset(&updating_preferences_
, true);
179 DictionaryPrefUpdate
update(prefs_
, pref_name_
);
180 base::DictionaryValue
* pattern_pairs_settings
= update
.Get();
182 std::string
pattern_str(
183 CreatePatternString(primary_pattern
, secondary_pattern
));
184 base::DictionaryValue
* settings_dictionary
= NULL
;
185 bool found
= pattern_pairs_settings
->GetDictionaryWithoutPathExpansion(
186 pattern_str
, &settings_dictionary
);
189 settings_dictionary
= new base::DictionaryValue
;
190 pattern_pairs_settings
->SetWithoutPathExpansion(pattern_str
,
191 settings_dictionary
);
194 settings_dictionary
->SetWithoutPathExpansion(
195 kLastUsed
, new base::FundamentalValue(clock
->Now().ToDoubleT()));
199 base::Time
ContentSettingsPref::GetLastUsage(
200 const ContentSettingsPattern
& primary_pattern
,
201 const ContentSettingsPattern
& secondary_pattern
) {
202 const base::DictionaryValue
* pattern_pairs_settings
=
203 prefs_
->GetDictionary(pref_name_
);
204 std::string
pattern_str(
205 CreatePatternString(primary_pattern
, secondary_pattern
));
207 const base::DictionaryValue
* settings_dictionary
= NULL
;
208 bool found
= pattern_pairs_settings
->GetDictionaryWithoutPathExpansion(
209 pattern_str
, &settings_dictionary
);
214 double last_used_time
;
215 found
= settings_dictionary
->GetDoubleWithoutPathExpansion(
216 kLastUsed
, &last_used_time
);
221 return base::Time::FromDoubleT(last_used_time
);
224 size_t ContentSettingsPref::GetNumExceptions() {
225 return value_map_
.size();
228 bool ContentSettingsPref::TryLockForTesting() const {
235 void ContentSettingsPref::ReadContentSettingsFromPref() {
236 // |DictionaryPrefUpdate| sends out notifications when destructed. This
237 // construction order ensures |AutoLock| gets destroyed first and |lock_| is
238 // not held when the notifications are sent. Also, |auto_reset| must be still
239 // valid when the notifications are sent, so that |Observe| skips the
241 base::AutoReset
<bool> auto_reset(&updating_preferences_
, true);
242 DictionaryPrefUpdate
update(prefs_
, pref_name_
);
243 base::AutoLock
auto_lock(lock_
);
245 const base::DictionaryValue
* all_settings_dictionary
=
246 prefs_
->GetDictionary(pref_name_
);
250 // Careful: The returned value could be NULL if the pref has never been set.
251 if (!all_settings_dictionary
)
254 base::DictionaryValue
* mutable_settings
;
255 scoped_ptr
<base::DictionaryValue
> mutable_settings_scope
;
257 if (!is_incognito_
) {
258 mutable_settings
= update
.Get();
260 // Create copy as we do not want to persist anything in OTR prefs.
261 mutable_settings
= all_settings_dictionary
->DeepCopy();
262 mutable_settings_scope
.reset(mutable_settings
);
264 // Convert all Unicode patterns into punycode form, then read.
265 CanonicalizeContentSettingsExceptions(mutable_settings
);
267 size_t cookies_block_exception_count
= 0;
268 size_t cookies_allow_exception_count
= 0;
269 size_t cookies_session_only_exception_count
= 0;
270 for (base::DictionaryValue::Iterator
i(*mutable_settings
); !i
.IsAtEnd();
272 const std::string
& pattern_str(i
.key());
273 std::pair
<ContentSettingsPattern
, ContentSettingsPattern
> pattern_pair
=
274 ParsePatternString(pattern_str
);
275 if (!pattern_pair
.first
.IsValid() ||
276 !pattern_pair
.second
.IsValid()) {
277 // TODO: Change this to DFATAL when crbug.com/132659 is fixed.
278 LOG(ERROR
) << "Invalid pattern strings: " << pattern_str
;
282 // Get settings dictionary for the current pattern string, and read
283 // settings from the dictionary.
284 const base::DictionaryValue
* settings_dictionary
= NULL
;
285 bool is_dictionary
= i
.value().GetAsDictionary(&settings_dictionary
);
286 DCHECK(is_dictionary
);
288 if (SupportsResourceIdentifiers(content_type_
)) {
289 const base::DictionaryValue
* resource_dictionary
= NULL
;
290 if (settings_dictionary
->GetDictionary(
291 kPerResourceIdentifierPrefName
, &resource_dictionary
)) {
292 for (base::DictionaryValue::Iterator
j(*resource_dictionary
);
295 const std::string
& resource_identifier(j
.key());
296 int setting
= CONTENT_SETTING_DEFAULT
;
297 bool is_integer
= j
.value().GetAsInteger(&setting
);
299 DCHECK_NE(CONTENT_SETTING_DEFAULT
, setting
);
300 scoped_ptr
<base::Value
> setting_ptr(
301 new base::FundamentalValue(setting
));
302 value_map_
.SetValue(pattern_pair
.first
,
306 setting_ptr
->DeepCopy());
310 base::Value
* value
= NULL
;
311 if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type_
)) {
312 const base::DictionaryValue
* setting
= NULL
;
313 // TODO(xians): Handle the non-dictionary types.
314 if (settings_dictionary
->GetDictionaryWithoutPathExpansion(
315 kSettingPath
, &setting
)) {
316 DCHECK(!setting
->empty());
317 value
= setting
->DeepCopy();
320 int setting
= CONTENT_SETTING_DEFAULT
;
321 if (settings_dictionary
->GetIntegerWithoutPathExpansion(
322 kSettingPath
, &setting
)) {
323 DCHECK_NE(CONTENT_SETTING_DEFAULT
, setting
);
324 setting
= FixObsoleteCookiePromptMode(content_type_
,
325 ContentSetting(setting
));
326 value
= new base::FundamentalValue(setting
);
331 scoped_ptr
<base::Value
> value_ptr(value
);
332 value_map_
.SetValue(pattern_pair
.first
,
335 ResourceIdentifier(),
337 if (content_type_
== CONTENT_SETTINGS_TYPE_COOKIES
) {
338 ContentSetting s
= ValueToContentSetting(value
);
340 case CONTENT_SETTING_ALLOW
:
341 ++cookies_allow_exception_count
;
343 case CONTENT_SETTING_BLOCK
:
344 ++cookies_block_exception_count
;
346 case CONTENT_SETTING_SESSION_ONLY
:
347 ++cookies_session_only_exception_count
;
358 if (content_type_
== CONTENT_SETTINGS_TYPE_COOKIES
) {
359 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfBlockCookiesExceptions",
360 cookies_block_exception_count
);
361 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfAllowCookiesExceptions",
362 cookies_allow_exception_count
);
363 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfSessionOnlyCookiesExceptions",
364 cookies_session_only_exception_count
);
368 void ContentSettingsPref::OnPrefChanged() {
369 DCHECK(thread_checker_
.CalledOnValidThread());
371 if (updating_preferences_
)
374 ReadContentSettingsFromPref();
376 notify_callback_
.Run(ContentSettingsPattern(),
377 ContentSettingsPattern(),
379 ResourceIdentifier());
382 void ContentSettingsPref::UpdatePref(
383 const ContentSettingsPattern
& primary_pattern
,
384 const ContentSettingsPattern
& secondary_pattern
,
385 const ResourceIdentifier
& resource_identifier
,
386 const base::Value
* value
) {
387 // Ensure that |lock_| is not held by this thread, since this function will
388 // send out notifications (by |~DictionaryPrefUpdate|).
391 base::AutoReset
<bool> auto_reset(&updating_preferences_
, true);
393 DictionaryPrefUpdate
update(prefs_
, pref_name_
);
394 base::DictionaryValue
* pattern_pairs_settings
= update
.Get();
396 // Get settings dictionary for the given patterns.
397 std::string
pattern_str(CreatePatternString(primary_pattern
,
399 base::DictionaryValue
* settings_dictionary
= NULL
;
400 bool found
= pattern_pairs_settings
->GetDictionaryWithoutPathExpansion(
401 pattern_str
, &settings_dictionary
);
403 if (!found
&& value
) {
404 settings_dictionary
= new base::DictionaryValue
;
405 pattern_pairs_settings
->SetWithoutPathExpansion(
406 pattern_str
, settings_dictionary
);
409 if (settings_dictionary
) {
410 if (SupportsResourceIdentifiers(content_type_
) &&
411 !resource_identifier
.empty()) {
412 base::DictionaryValue
* resource_dictionary
= NULL
;
413 found
= settings_dictionary
->GetDictionary(
414 kPerResourceIdentifierPrefName
, &resource_dictionary
);
417 return; // Nothing to remove. Exit early.
418 resource_dictionary
= new base::DictionaryValue
;
419 settings_dictionary
->Set(
420 kPerResourceIdentifierPrefName
, resource_dictionary
);
422 // Update resource dictionary.
424 resource_dictionary
->RemoveWithoutPathExpansion(resource_identifier
,
426 if (resource_dictionary
->empty()) {
427 settings_dictionary
->RemoveWithoutPathExpansion(
428 kPerResourceIdentifierPrefName
, NULL
);
431 resource_dictionary
->SetWithoutPathExpansion(
432 resource_identifier
, value
->DeepCopy());
435 // Update settings dictionary.
437 settings_dictionary
->RemoveWithoutPathExpansion(kSettingPath
, NULL
);
438 settings_dictionary
->RemoveWithoutPathExpansion(kLastUsed
, NULL
);
440 settings_dictionary
->SetWithoutPathExpansion(
441 kSettingPath
, value
->DeepCopy());
444 // Remove the settings dictionary if it is empty.
445 if (settings_dictionary
->empty()) {
446 pattern_pairs_settings
->RemoveWithoutPathExpansion(
454 void ContentSettingsPref::CanonicalizeContentSettingsExceptions(
455 base::DictionaryValue
* all_settings_dictionary
) {
456 DCHECK(all_settings_dictionary
);
458 std::vector
<std::string
> remove_items
;
459 base::StringPairs move_items
;
460 for (base::DictionaryValue::Iterator
i(*all_settings_dictionary
);
463 const std::string
& pattern_str(i
.key());
464 std::pair
<ContentSettingsPattern
, ContentSettingsPattern
> pattern_pair
=
465 ParsePatternString(pattern_str
);
466 if (!pattern_pair
.first
.IsValid() ||
467 !pattern_pair
.second
.IsValid()) {
468 LOG(ERROR
) << "Invalid pattern strings: " << pattern_str
;
472 const std::string canonicalized_pattern_str
= CreatePatternString(
473 pattern_pair
.first
, pattern_pair
.second
);
475 if (canonicalized_pattern_str
.empty() ||
476 canonicalized_pattern_str
== pattern_str
) {
480 // Clear old pattern if prefs already have canonicalized pattern.
481 const base::DictionaryValue
* new_pattern_settings_dictionary
= NULL
;
482 if (all_settings_dictionary
->GetDictionaryWithoutPathExpansion(
483 canonicalized_pattern_str
, &new_pattern_settings_dictionary
)) {
484 remove_items
.push_back(pattern_str
);
488 // Move old pattern to canonicalized pattern.
489 const base::DictionaryValue
* old_pattern_settings_dictionary
= NULL
;
490 if (i
.value().GetAsDictionary(&old_pattern_settings_dictionary
)) {
491 move_items
.push_back(
492 std::make_pair(pattern_str
, canonicalized_pattern_str
));
496 for (size_t i
= 0; i
< remove_items
.size(); ++i
) {
497 all_settings_dictionary
->RemoveWithoutPathExpansion(remove_items
[i
], NULL
);
500 for (size_t i
= 0; i
< move_items
.size(); ++i
) {
501 scoped_ptr
<base::Value
> pattern_settings_dictionary
;
502 all_settings_dictionary
->RemoveWithoutPathExpansion(
503 move_items
[i
].first
, &pattern_settings_dictionary
);
504 all_settings_dictionary
->SetWithoutPathExpansion(
505 move_items
[i
].second
, pattern_settings_dictionary
.release());
509 void ContentSettingsPref::AssertLockNotHeld() const {
511 // |Lock::Acquire()| will assert if the lock is held by this thread.
517 } // namespace content_settings