Linux: Depend on liberation-fonts package for RPMs.
[chromium-blink-merge.git] / components / content_settings / core / browser / content_settings_pref.cc
blob2443e274884a5d20d0885f4c553dc5730dceb2e9
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"
8 #include "base/bind.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"
20 #include "url/gurl.h"
22 namespace {
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;
34 return setting;
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;
45 } // namespace
47 namespace content_settings {
49 ContentSettingsPref::ContentSettingsPref(
50 ContentSettingsType content_type,
51 PrefService* prefs,
52 PrefChangeRegistrar* registrar,
53 const std::string& pref_name,
54 bool incognito,
55 NotifyObserversCallback notify_callback)
56 : content_type_(content_type),
57 prefs_(prefs),
58 registrar_(registrar),
59 pref_name_(pref_name),
60 is_incognito_(incognito),
61 updating_preferences_(false),
62 notify_callback_(notify_callback) {
63 DCHECK(prefs_);
65 ReadContentSettingsFromPref();
67 registrar_->Add(
68 pref_name_,
69 base::Bind(&ContentSettingsPref::OnPrefChanged, base::Unretained(this)));
72 ContentSettingsPref::~ContentSettingsPref() {
75 RuleIterator* ContentSettingsPref::GetRuleIterator(
76 const ResourceIdentifier& resource_identifier,
77 bool incognito) const {
78 if (incognito)
79 return incognito_value_map_.GetRuleIterator(content_type_,
80 resource_identifier,
81 &lock_);
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());
91 DCHECK(prefs_);
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_;
101 if (!is_incognito_)
102 map_to_modify = &value_map_;
105 base::AutoLock auto_lock(lock_);
106 if (value.get()) {
107 map_to_modify->SetValue(
108 primary_pattern,
109 secondary_pattern,
110 content_type_,
111 resource_identifier,
112 value->DeepCopy());
113 } else {
114 map_to_modify->DeleteValue(
115 primary_pattern,
116 secondary_pattern,
117 content_type_,
118 resource_identifier);
121 // Update the content settings preference.
122 if (!is_incognito_) {
123 UpdatePref(primary_pattern,
124 secondary_pattern,
125 resource_identifier,
126 value.get());
129 notify_callback_.Run(
130 primary_pattern, secondary_pattern, content_type_, resource_identifier);
132 return true;
135 void ContentSettingsPref::ClearAllContentSettingsRules() {
136 DCHECK(thread_checker_.CalledOnValidThread());
137 DCHECK(prefs_);
139 OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
140 if (!is_incognito_)
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(),
160 content_type_,
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.
169 if (is_incognito_) {
170 return;
173 // Ensure that |lock_| is not held by this thread, since this function will
174 // send out notifications (by |~DictionaryPrefUpdate|).
175 AssertLockNotHeld();
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);
188 if (!found) {
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);
211 if (!found)
212 return base::Time();
214 double last_used_time;
215 found = settings_dictionary->GetDoubleWithoutPathExpansion(
216 kLastUsed, &last_used_time);
218 if (!found)
219 return base::Time();
221 return base::Time::FromDoubleT(last_used_time);
224 size_t ContentSettingsPref::GetNumExceptions() {
225 return value_map_.size();
228 bool ContentSettingsPref::TryLockForTesting() const {
229 if (!lock_.Try())
230 return false;
231 lock_.Release();
232 return true;
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
240 // notification.
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_);
248 value_map_.clear();
250 // Careful: The returned value could be NULL if the pref has never been set.
251 if (!all_settings_dictionary)
252 return;
254 base::DictionaryValue* mutable_settings;
255 scoped_ptr<base::DictionaryValue> mutable_settings_scope;
257 if (!is_incognito_) {
258 mutable_settings = update.Get();
259 } else {
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();
271 i.Advance()) {
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;
279 continue;
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);
293 !j.IsAtEnd();
294 j.Advance()) {
295 const std::string& resource_identifier(j.key());
296 int setting = CONTENT_SETTING_DEFAULT;
297 bool is_integer = j.value().GetAsInteger(&setting);
298 DCHECK(is_integer);
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,
303 pattern_pair.second,
304 content_type_,
305 resource_identifier,
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();
319 } else {
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);
330 if (value != NULL) {
331 scoped_ptr<base::Value> value_ptr(value);
332 value_map_.SetValue(pattern_pair.first,
333 pattern_pair.second,
334 content_type_,
335 ResourceIdentifier(),
336 value->DeepCopy());
337 if (content_type_ == CONTENT_SETTINGS_TYPE_COOKIES) {
338 ContentSetting s = ValueToContentSetting(value);
339 switch (s) {
340 case CONTENT_SETTING_ALLOW :
341 ++cookies_allow_exception_count;
342 break;
343 case CONTENT_SETTING_BLOCK :
344 ++cookies_block_exception_count;
345 break;
346 case CONTENT_SETTING_SESSION_ONLY :
347 ++cookies_session_only_exception_count;
348 break;
349 default:
350 NOTREACHED();
351 break;
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_)
372 return;
374 ReadContentSettingsFromPref();
376 notify_callback_.Run(ContentSettingsPattern(),
377 ContentSettingsPattern(),
378 content_type_,
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|).
389 AssertLockNotHeld();
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,
398 secondary_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);
415 if (!found) {
416 if (value == NULL)
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.
423 if (value == NULL) {
424 resource_dictionary->RemoveWithoutPathExpansion(resource_identifier,
425 NULL);
426 if (resource_dictionary->empty()) {
427 settings_dictionary->RemoveWithoutPathExpansion(
428 kPerResourceIdentifierPrefName, NULL);
430 } else {
431 resource_dictionary->SetWithoutPathExpansion(
432 resource_identifier, value->DeepCopy());
434 } else {
435 // Update settings dictionary.
436 if (value == NULL) {
437 settings_dictionary->RemoveWithoutPathExpansion(kSettingPath, NULL);
438 settings_dictionary->RemoveWithoutPathExpansion(kLastUsed, NULL);
439 } else {
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(
447 pattern_str, NULL);
453 // static
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);
461 !i.IsAtEnd();
462 i.Advance()) {
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;
469 continue;
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) {
477 continue;
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);
485 continue;
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 {
510 #if !defined(NDEBUG)
511 // |Lock::Acquire()| will assert if the lock is held by this thread.
512 lock_.Acquire();
513 lock_.Release();
514 #endif
517 } // namespace content_settings