Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / content_settings / content_settings_pref_provider.cc
blob4e46cca1efa56097ea159d8ff0dcd6e35a1d51d0
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"
7 #include <map>
8 #include <string>
9 #include <utility>
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"
30 #include "url/gurl.h"
32 using base::UserMetricsAction;
33 using content::BrowserThread;
35 namespace {
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;
50 return setting;
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;
61 return true;
63 return false;
66 } // namespace
68 namespace content_settings {
70 // ////////////////////////////////////////////////////////////////////////////
71 // PrefProvider:
74 // static
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,
87 bool incognito)
88 : prefs_(prefs),
89 is_incognito_(incognito),
90 updating_preferences_(false) {
91 DCHECK(prefs_);
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) {
99 return;
102 // Read content settings exceptions.
103 ReadContentSettingsFromPref(false);
105 if (!is_incognito_) {
106 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfExceptions",
107 value_map_.size());
110 // Migrate the obsolete media content setting exceptions to the new settings.
111 // This needs to be done after ReadContentSettingsFromPref().
112 if (!is_incognito_)
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));
129 DCHECK(prefs_);
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()) {
138 return false;
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_;
145 if (!is_incognito_)
146 map_to_modify = &value_map_;
149 base::AutoLock auto_lock(lock_);
150 if (value.get()) {
151 map_to_modify->SetValue(
152 primary_pattern,
153 secondary_pattern,
154 content_type,
155 resource_identifier,
156 value->DeepCopy());
157 } else {
158 map_to_modify->DeleteValue(
159 primary_pattern,
160 secondary_pattern,
161 content_type,
162 resource_identifier);
165 // Update the content settings preference.
166 if (!is_incognito_) {
167 UpdatePref(primary_pattern,
168 secondary_pattern,
169 content_type,
170 resource_identifier,
171 value.get());
174 NotifyObservers(
175 primary_pattern, secondary_pattern, content_type, resource_identifier);
177 return true;
180 void PrefProvider::ClearAllContentSettingsRules(
181 ContentSettingsType content_type) {
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183 DCHECK(prefs_);
185 OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
186 if (!is_incognito_)
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,
205 content_type,
206 std::string(),
207 NULL);
209 NotifyObservers(ContentSettingsPattern(),
210 ContentSettingsPattern(),
211 content_type,
212 std::string());
215 PrefProvider::~PrefProvider() {
216 DCHECK(!prefs_);
219 RuleIterator* PrefProvider::GetRuleIterator(
220 ContentSettingsType content_type,
221 const ResourceIdentifier& resource_identifier,
222 bool incognito) const {
223 if (incognito)
224 return incognito_value_map_.GetRuleIterator(content_type,
225 resource_identifier,
226 &lock_);
227 return value_map_.GetRuleIterator(content_type, resource_identifier, &lock_);
230 // ////////////////////////////////////////////////////////////////////////////
231 // Private
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|).
241 AssertLockNotHeld();
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,
251 secondary_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);
269 if (!found) {
270 if (value == NULL)
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.
276 if (value == NULL) {
277 resource_dictionary->RemoveWithoutPathExpansion(resource_identifier,
278 NULL);
279 if (resource_dictionary->empty()) {
280 settings_dictionary->RemoveWithoutPathExpansion(
281 res_dictionary_path, NULL);
283 } else {
284 resource_dictionary->SetWithoutPathExpansion(
285 resource_identifier, value->DeepCopy());
287 } else {
288 // Update settings dictionary.
289 std::string setting_path = GetTypeName(content_type);
290 if (value == NULL) {
291 settings_dictionary->RemoveWithoutPathExpansion(setting_path,
292 NULL);
293 } else {
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(
301 pattern_str, NULL);
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())
318 continue;
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())
327 return;
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,
337 std::string(),
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,
345 std::string(),
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,
353 std::string(),
354 NULL);
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
363 // notification.
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);
371 if (overwrite)
372 value_map_.clear();
374 // Careful: The returned value could be NULL if the pref has never been set.
375 if (!all_settings_dictionary)
376 return;
378 base::DictionaryValue* mutable_settings;
379 scoped_ptr<base::DictionaryValue> mutable_settings_scope;
381 if (!is_incognito_) {
382 mutable_settings = update.Get();
383 } else {
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();
395 i.Advance()) {
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;
403 continue;
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);
421 !j.IsAtEnd();
422 j.Advance()) {
423 const std::string& resource_identifier(j.key());
424 int setting = CONTENT_SETTING_DEFAULT;
425 bool is_integer = j.value().GetAsInteger(&setting);
426 DCHECK(is_integer);
427 DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
428 value_map_.SetValue(pattern_pair.first,
429 pattern_pair.second,
430 content_type,
431 resource_identifier,
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();
445 } else {
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|.
457 if (value != NULL) {
458 value_map_.SetValue(pattern_pair.first,
459 pattern_pair.second,
460 content_type,
461 ResourceIdentifier(),
462 value);
463 if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
464 ContentSetting s = ValueToContentSetting(value);
465 switch (s) {
466 case CONTENT_SETTING_ALLOW :
467 ++cookies_allow_exception_count;
468 break;
469 case CONTENT_SETTING_BLOCK :
470 ++cookies_block_exception_count;
471 break;
472 case CONTENT_SETTING_SESSION_ONLY :
473 ++cookies_session_only_exception_count;
474 break;
475 default:
476 NOTREACHED();
477 break;
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_)
495 return;
497 ReadContentSettingsFromPref(true);
499 NotifyObservers(ContentSettingsPattern(),
500 ContentSettingsPattern(),
501 CONTENT_SETTINGS_TYPE_DEFAULT,
502 std::string());
505 // static
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);
513 !i.IsAtEnd();
514 i.Advance()) {
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;
521 continue;
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) {
529 continue;
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);
537 continue;
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));
563 DCHECK(prefs_);
564 RemoveAllObservers();
565 pref_change_registrar_.RemoveAll();
566 prefs_ = NULL;
569 void PrefProvider::AssertLockNotHeld() const {
570 #if !defined(NDEBUG)
571 // |Lock::Acquire()| will assert if the lock is held by this thread.
572 lock_.Acquire();
573 lock_.Release();
574 #endif
577 } // namespace content_settings