• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/content_settings/content_settings_pref_provider.h"
6 
7 #include <map>
8 #include <string>
9 #include <utility>
10 
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/pref_registry/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"
31 
32 using base::UserMetricsAction;
33 using content::BrowserThread;
34 
35 namespace {
36 
37 typedef std::pair<std::string, std::string> StringPair;
38 typedef std::map<std::string, std::string> StringMap;
39 
40 const char kPerPluginPrefName[] = "per_plugin";
41 const char kAudioKey[] = "audio";
42 const char kVideoKey[] = "video";
43 
FixObsoleteCookiePromptMode(ContentSettingsType content_type,ContentSetting setting)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;
49   }
50   return setting;
51 }
52 
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.
GetResourceTypeName(ContentSettingsType content_type,std::string * pref_key)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;
62   }
63   return false;
64 }
65 
66 }  // namespace
67 
68 namespace content_settings {
69 
70 // ////////////////////////////////////////////////////////////////////////////
71 // PrefProvider:
72 //
73 
74 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)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);
84 }
85 
PrefProvider(PrefService * prefs,bool incognito)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);
96   }
97   if (prefs_->GetInteger(prefs::kContentSettingsVersion) >
98       ContentSettingsPattern::kContentSettingsPatternVersion) {
99     return;
100   }
101 
102   // Read content settings exceptions.
103   ReadContentSettingsFromPref(false);
104 
105   if (!is_incognito_) {
106     UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfExceptions",
107                          value_map_.size());
108   }
109 
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();
114 
115   pref_change_registrar_.Init(prefs_);
116   pref_change_registrar_.Add(
117       prefs::kContentSettingsPatternPairs,
118       base::Bind(&PrefProvider::OnContentSettingsPatternPairsChanged,
119                  base::Unretained(this)));
120 }
121 
SetWebsiteSetting(const ContentSettingsPattern & primary_pattern,const ContentSettingsPattern & secondary_pattern,ContentSettingsType content_type,const ResourceIdentifier & resource_identifier,base::Value * in_value)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;
139   }
140 
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_;
147 
148   {
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);
163     }
164   }
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());
172   }
173 
174   NotifyObservers(
175       primary_pattern, secondary_pattern, content_type, resource_identifier);
176 
177   return true;
178 }
179 
ClearAllContentSettingsRules(ContentSettingsType content_type)180 void PrefProvider::ClearAllContentSettingsRules(
181     ContentSettingsType content_type) {
182   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183   DCHECK(prefs_);
184 
185   OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
186   if (!is_incognito_)
187     map_to_modify = &value_map_;
188 
189   std::vector<Rule> rules_to_delete;
190   {
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());
197 
198     map_to_modify->DeleteValues(content_type, std::string());
199   }
200 
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);
208   }
209   NotifyObservers(ContentSettingsPattern(),
210                   ContentSettingsPattern(),
211                   content_type,
212                   std::string());
213 }
214 
~PrefProvider()215 PrefProvider::~PrefProvider() {
216   DCHECK(!prefs_);
217 }
218 
GetRuleIterator(ContentSettingsType content_type,const ResourceIdentifier & resource_identifier,bool incognito) const219 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_);
228 }
229 
230 // ////////////////////////////////////////////////////////////////////////////
231 // Private
232 
UpdatePref(const ContentSettingsPattern & primary_pattern,const ContentSettingsPattern & secondary_pattern,ContentSettingsType content_type,const ResourceIdentifier & resource_identifier,const base::Value * value)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();
242 
243   base::AutoReset<bool> auto_reset(&updating_preferences_, true);
244   {
245     DictionaryPrefUpdate update(prefs_,
246                                 prefs::kContentSettingsPatternPairs);
247     base::DictionaryValue* pattern_pairs_settings = update.Get();
248 
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);
255 
256     if (!found && value) {
257       settings_dictionary = new base::DictionaryValue;
258       pattern_pairs_settings->SetWithoutPathExpansion(
259           pattern_str, settings_dictionary);
260     }
261 
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);
274         }
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);
282           }
283         } else {
284           resource_dictionary->SetWithoutPathExpansion(
285               resource_identifier, value->DeepCopy());
286         }
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());
296         }
297       }
298       // Remove the settings dictionary if it is empty.
299       if (settings_dictionary->empty()) {
300         pattern_pairs_settings->RemoveWithoutPathExpansion(
301             pattern_str, NULL);
302       }
303     }
304   }
305 }
306 
307 
MigrateObsoleteMediaContentSetting()308 void PrefProvider::MigrateObsoleteMediaContentSetting() {
309   std::vector<Rule> rules_to_delete;
310   {
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);
320     }
321   }
322 
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;
328 
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));
339     }
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));
347     }
348 
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);
355   }
356 }
357 
ReadContentSettingsFromPref(bool overwrite)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_);
367 
368   const base::DictionaryValue* all_settings_dictionary =
369       prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
370 
371   if (overwrite)
372     value_map_.clear();
373 
374   // Careful: The returned value could be NULL if the pref has never been set.
375   if (!all_settings_dictionary)
376     return;
377 
378   base::DictionaryValue* mutable_settings;
379   scoped_ptr<base::DictionaryValue> mutable_settings_scope;
380 
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);
387   }
388   // Convert all Unicode patterns into punycode form, then read.
389   CanonicalizeContentSettingsExceptions(mutable_settings);
390 
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;
404     }
405 
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);
411 
412     for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
413       ContentSettingsType content_type = static_cast<ContentSettingsType>(i);
414 
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));
433           }
434         }
435       }
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();
444         }
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);
453         }
454       }
455 
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;
478           }
479         }
480       }
481     }
482   }
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);
489 }
490 
OnContentSettingsPatternPairsChanged()491 void PrefProvider::OnContentSettingsPatternPairsChanged() {
492   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
493 
494   if (updating_preferences_)
495     return;
496 
497   ReadContentSettingsFromPref(true);
498 
499   NotifyObservers(ContentSettingsPattern(),
500                   ContentSettingsPattern(),
501                   CONTENT_SETTINGS_TYPE_DEFAULT,
502                   std::string());
503 }
504 
505 // static
CanonicalizeContentSettingsExceptions(base::DictionaryValue * all_settings_dictionary)506 void PrefProvider::CanonicalizeContentSettingsExceptions(
507     base::DictionaryValue* all_settings_dictionary) {
508   DCHECK(all_settings_dictionary);
509 
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;
522     }
523 
524     const std::string canonicalized_pattern_str = CreatePatternString(
525         pattern_pair.first, pattern_pair.second);
526 
527     if (canonicalized_pattern_str.empty() ||
528         canonicalized_pattern_str == pattern_str) {
529       continue;
530     }
531 
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;
538     }
539 
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));
545     }
546   }
547 
548   for (size_t i = 0; i < remove_items.size(); ++i) {
549     all_settings_dictionary->RemoveWithoutPathExpansion(remove_items[i], NULL);
550   }
551 
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());
558   }
559 }
560 
ShutdownOnUIThread()561 void PrefProvider::ShutdownOnUIThread() {
562   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
563   DCHECK(prefs_);
564   RemoveAllObservers();
565   pref_change_registrar_.RemoveAll();
566   prefs_ = NULL;
567 }
568 
AssertLockNotHeld() const569 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
575 }
576 
577 }  // namespace content_settings
578