Home | History | Annotate | Download | only in content_settings
      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 
     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.
     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
     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 
     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 
    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 
    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 
    215 PrefProvider::~PrefProvider() {
    216   DCHECK(!prefs_);
    217 }
    218 
    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_);
    228 }
    229 
    230 // ////////////////////////////////////////////////////////////////////////////
    231 // Private
    232 
    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 
    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 
    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 
    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
    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 
    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 
    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
    575 }
    576 
    577 }  // namespace content_settings
    578