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 "base/strings/string_split.h"
     18 #include "base/time/clock.h"
     19 #include "base/time/default_clock.h"
     20 #include "chrome/browser/content_settings/content_settings_utils.h"
     21 #include "chrome/browser/content_settings/host_content_settings_map.h"
     22 #include "chrome/common/chrome_switches.h"
     23 #include "chrome/common/pref_names.h"
     24 #include "components/content_settings/core/browser/content_settings_rule.h"
     25 #include "components/content_settings/core/common/content_settings.h"
     26 #include "components/content_settings/core/common/content_settings_pattern.h"
     27 #include "components/pref_registry/pref_registry_syncable.h"
     28 #include "content/public/browser/browser_thread.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 const char kLastUsed[] = "last_used";
     44 
     45 ContentSetting FixObsoleteCookiePromptMode(ContentSettingsType content_type,
     46                                            ContentSetting setting) {
     47   if (content_type == CONTENT_SETTINGS_TYPE_COOKIES &&
     48       setting == CONTENT_SETTING_ASK) {
     49     return CONTENT_SETTING_BLOCK;
     50   }
     51   return setting;
     52 }
     53 
     54 // If the given content type supports resource identifiers in user preferences,
     55 // returns true and sets |pref_key| to the key in the content settings
     56 // dictionary under which per-resource content settings are stored.
     57 // Otherwise, returns false.
     58 bool GetResourceTypeName(ContentSettingsType content_type,
     59                          std::string* pref_key) {
     60   if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) {
     61     *pref_key = kPerPluginPrefName;
     62     return true;
     63   }
     64   return false;
     65 }
     66 
     67 }  // namespace
     68 
     69 namespace content_settings {
     70 
     71 // ////////////////////////////////////////////////////////////////////////////
     72 // PrefProvider:
     73 //
     74 
     75 // static
     76 void PrefProvider::RegisterProfilePrefs(
     77     user_prefs::PrefRegistrySyncable* registry) {
     78   registry->RegisterIntegerPref(
     79       prefs::kContentSettingsVersion,
     80       ContentSettingsPattern::kContentSettingsPatternVersion,
     81       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
     82   registry->RegisterDictionaryPref(
     83       prefs::kContentSettingsPatternPairs,
     84       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
     85 }
     86 
     87 PrefProvider::PrefProvider(PrefService* prefs, bool incognito)
     88     : prefs_(prefs),
     89       clock_(new base::DefaultClock()),
     90       is_incognito_(incognito),
     91       updating_preferences_(false) {
     92   DCHECK(prefs_);
     93   // Verify preferences version.
     94   if (!prefs_->HasPrefPath(prefs::kContentSettingsVersion)) {
     95     prefs_->SetInteger(prefs::kContentSettingsVersion,
     96                       ContentSettingsPattern::kContentSettingsPatternVersion);
     97   }
     98   if (prefs_->GetInteger(prefs::kContentSettingsVersion) >
     99       ContentSettingsPattern::kContentSettingsPatternVersion) {
    100     return;
    101   }
    102 
    103   // Read content settings exceptions.
    104   ReadContentSettingsFromPref(false);
    105 
    106   if (!is_incognito_) {
    107     UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfExceptions",
    108                          value_map_.size());
    109   }
    110 
    111   // Migrate the obsolete media content setting exceptions to the new settings.
    112   // This needs to be done after ReadContentSettingsFromPref().
    113   if (!is_incognito_)
    114     MigrateObsoleteMediaContentSetting();
    115 
    116   pref_change_registrar_.Init(prefs_);
    117   pref_change_registrar_.Add(
    118       prefs::kContentSettingsPatternPairs,
    119       base::Bind(&PrefProvider::OnContentSettingsPatternPairsChanged,
    120                  base::Unretained(this)));
    121 }
    122 
    123 bool PrefProvider::SetWebsiteSetting(
    124     const ContentSettingsPattern& primary_pattern,
    125     const ContentSettingsPattern& secondary_pattern,
    126     ContentSettingsType content_type,
    127     const ResourceIdentifier& resource_identifier,
    128     base::Value* in_value) {
    129   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    130   DCHECK(prefs_);
    131   // Default settings are set using a wildcard pattern for both
    132   // |primary_pattern| and |secondary_pattern|. Don't store default settings in
    133   // the |PrefProvider|. The |PrefProvider| handles settings for specific
    134   // sites/origins defined by the |primary_pattern| and the |secondary_pattern|.
    135   // Default settings are handled by the |DefaultProvider|.
    136   if (primary_pattern == ContentSettingsPattern::Wildcard() &&
    137       secondary_pattern == ContentSettingsPattern::Wildcard() &&
    138       resource_identifier.empty()) {
    139     return false;
    140   }
    141 
    142   // At this point take the ownership of the |in_value|.
    143   scoped_ptr<base::Value> value(in_value);
    144   // Update in memory value map.
    145   OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
    146   if (!is_incognito_)
    147     map_to_modify = &value_map_;
    148 
    149   {
    150     base::AutoLock auto_lock(lock_);
    151     if (value.get()) {
    152       map_to_modify->SetValue(
    153           primary_pattern,
    154           secondary_pattern,
    155           content_type,
    156           resource_identifier,
    157           value->DeepCopy());
    158     } else {
    159       map_to_modify->DeleteValue(
    160           primary_pattern,
    161           secondary_pattern,
    162           content_type,
    163           resource_identifier);
    164     }
    165   }
    166   // Update the content settings preference.
    167   if (!is_incognito_) {
    168     UpdatePref(primary_pattern,
    169                secondary_pattern,
    170                content_type,
    171                resource_identifier,
    172                value.get());
    173   }
    174 
    175   NotifyObservers(
    176       primary_pattern, secondary_pattern, content_type, resource_identifier);
    177 
    178   return true;
    179 }
    180 
    181 void PrefProvider::ClearAllContentSettingsRules(
    182     ContentSettingsType content_type) {
    183   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    184   DCHECK(prefs_);
    185 
    186   OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
    187   if (!is_incognito_)
    188     map_to_modify = &value_map_;
    189 
    190   std::vector<Rule> rules_to_delete;
    191   {
    192     base::AutoLock auto_lock(lock_);
    193     scoped_ptr<RuleIterator> rule_iterator(
    194         map_to_modify->GetRuleIterator(content_type, std::string(), NULL));
    195     // Copy the rules; we cannot call |UpdatePref| while holding |lock_|.
    196     while (rule_iterator->HasNext())
    197       rules_to_delete.push_back(rule_iterator->Next());
    198 
    199     map_to_modify->DeleteValues(content_type, std::string());
    200   }
    201 
    202   for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
    203        it != rules_to_delete.end(); ++it) {
    204     UpdatePref(it->primary_pattern,
    205                it->secondary_pattern,
    206                content_type,
    207                std::string(),
    208                NULL);
    209   }
    210   NotifyObservers(ContentSettingsPattern(),
    211                   ContentSettingsPattern(),
    212                   content_type,
    213                   std::string());
    214 }
    215 
    216 PrefProvider::~PrefProvider() {
    217   DCHECK(!prefs_);
    218 }
    219 
    220 RuleIterator* PrefProvider::GetRuleIterator(
    221     ContentSettingsType content_type,
    222     const ResourceIdentifier& resource_identifier,
    223     bool incognito) const {
    224   if (incognito)
    225     return incognito_value_map_.GetRuleIterator(content_type,
    226                                                 resource_identifier,
    227                                                 &lock_);
    228   return value_map_.GetRuleIterator(content_type, resource_identifier, &lock_);
    229 }
    230 
    231 // ////////////////////////////////////////////////////////////////////////////
    232 // Private
    233 
    234 void PrefProvider::UpdatePref(
    235     const ContentSettingsPattern& primary_pattern,
    236     const ContentSettingsPattern& secondary_pattern,
    237     ContentSettingsType content_type,
    238     const ResourceIdentifier& resource_identifier,
    239     const base::Value* value) {
    240   // Ensure that |lock_| is not held by this thread, since this function will
    241   // send out notifications (by |~DictionaryPrefUpdate|).
    242   AssertLockNotHeld();
    243 
    244   base::AutoReset<bool> auto_reset(&updating_preferences_, true);
    245   {
    246     DictionaryPrefUpdate update(prefs_,
    247                                 prefs::kContentSettingsPatternPairs);
    248     base::DictionaryValue* pattern_pairs_settings = update.Get();
    249 
    250     // Get settings dictionary for the given patterns.
    251     std::string pattern_str(CreatePatternString(primary_pattern,
    252                                                 secondary_pattern));
    253     base::DictionaryValue* settings_dictionary = NULL;
    254     bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
    255         pattern_str, &settings_dictionary);
    256 
    257     if (!found && value) {
    258       settings_dictionary = new base::DictionaryValue;
    259       pattern_pairs_settings->SetWithoutPathExpansion(
    260           pattern_str, settings_dictionary);
    261     }
    262 
    263     if (settings_dictionary) {
    264       std::string res_dictionary_path;
    265       if (GetResourceTypeName(content_type, &res_dictionary_path) &&
    266           !resource_identifier.empty()) {
    267         base::DictionaryValue* resource_dictionary = NULL;
    268         found = settings_dictionary->GetDictionary(
    269             res_dictionary_path, &resource_dictionary);
    270         if (!found) {
    271           if (value == NULL)
    272             return;  // Nothing to remove. Exit early.
    273           resource_dictionary = new base::DictionaryValue;
    274           settings_dictionary->Set(res_dictionary_path, resource_dictionary);
    275         }
    276         // Update resource dictionary.
    277         if (value == NULL) {
    278           resource_dictionary->RemoveWithoutPathExpansion(resource_identifier,
    279                                                           NULL);
    280           if (resource_dictionary->empty()) {
    281             settings_dictionary->RemoveWithoutPathExpansion(
    282                 res_dictionary_path, NULL);
    283           }
    284         } else {
    285           resource_dictionary->SetWithoutPathExpansion(
    286               resource_identifier, value->DeepCopy());
    287         }
    288       } else {
    289         // Update settings dictionary.
    290         std::string setting_path = GetTypeName(content_type);
    291         if (value == NULL) {
    292           settings_dictionary->RemoveWithoutPathExpansion(setting_path,
    293                                                           NULL);
    294           settings_dictionary->RemoveWithoutPathExpansion(kLastUsed, NULL);
    295         } else {
    296           settings_dictionary->SetWithoutPathExpansion(
    297               setting_path, value->DeepCopy());
    298         }
    299       }
    300       // Remove the settings dictionary if it is empty.
    301       if (settings_dictionary->empty()) {
    302         pattern_pairs_settings->RemoveWithoutPathExpansion(
    303             pattern_str, NULL);
    304       }
    305     }
    306   }
    307 }
    308 
    309 
    310 void PrefProvider::MigrateObsoleteMediaContentSetting() {
    311   std::vector<Rule> rules_to_delete;
    312   {
    313     scoped_ptr<RuleIterator> rule_iterator(GetRuleIterator(
    314         CONTENT_SETTINGS_TYPE_MEDIASTREAM, std::string(), false));
    315     while (rule_iterator->HasNext()) {
    316       // Skip default setting and rules without a value.
    317       const content_settings::Rule& rule = rule_iterator->Next();
    318       DCHECK(rule.primary_pattern != ContentSettingsPattern::Wildcard());
    319       if (!rule.value.get())
    320         continue;
    321       rules_to_delete.push_back(rule);
    322     }
    323   }
    324 
    325   for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
    326        it != rules_to_delete.end(); ++it) {
    327     const base::DictionaryValue* value_dict = NULL;
    328     if (!it->value->GetAsDictionary(&value_dict) || value_dict->empty())
    329       return;
    330 
    331     std::string audio_device, video_device;
    332     value_dict->GetString(kAudioKey, &audio_device);
    333     value_dict->GetString(kVideoKey, &video_device);
    334     // Add the exception to the new microphone content setting.
    335     if (!audio_device.empty()) {
    336       SetWebsiteSetting(it->primary_pattern,
    337                         it->secondary_pattern,
    338                         CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
    339                         std::string(),
    340                         new base::FundamentalValue(CONTENT_SETTING_ALLOW));
    341     }
    342     // Add the exception to the new camera content setting.
    343     if (!video_device.empty()) {
    344       SetWebsiteSetting(it->primary_pattern,
    345                         it->secondary_pattern,
    346                         CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
    347                         std::string(),
    348                         new base::FundamentalValue(CONTENT_SETTING_ALLOW));
    349     }
    350 
    351     // Remove the old exception in CONTENT_SETTINGS_TYPE_MEDIASTREAM.
    352     SetWebsiteSetting(it->primary_pattern,
    353                       it->secondary_pattern,
    354                       CONTENT_SETTINGS_TYPE_MEDIASTREAM,
    355                       std::string(),
    356                       NULL);
    357   }
    358 }
    359 
    360 void PrefProvider::ReadContentSettingsFromPref(bool overwrite) {
    361   // |DictionaryPrefUpdate| sends out notifications when destructed. This
    362   // construction order ensures |AutoLock| gets destroyed first and |lock_| is
    363   // not held when the notifications are sent. Also, |auto_reset| must be still
    364   // valid when the notifications are sent, so that |Observe| skips the
    365   // notification.
    366   base::AutoReset<bool> auto_reset(&updating_preferences_, true);
    367   DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs);
    368   base::AutoLock auto_lock(lock_);
    369 
    370   const base::DictionaryValue* all_settings_dictionary =
    371       prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
    372 
    373   if (overwrite)
    374     value_map_.clear();
    375 
    376   // Careful: The returned value could be NULL if the pref has never been set.
    377   if (!all_settings_dictionary)
    378     return;
    379 
    380   base::DictionaryValue* mutable_settings;
    381   scoped_ptr<base::DictionaryValue> mutable_settings_scope;
    382 
    383   if (!is_incognito_) {
    384     mutable_settings = update.Get();
    385   } else {
    386     // Create copy as we do not want to persist anything in OTR prefs.
    387     mutable_settings = all_settings_dictionary->DeepCopy();
    388     mutable_settings_scope.reset(mutable_settings);
    389   }
    390   // Convert all Unicode patterns into punycode form, then read.
    391   CanonicalizeContentSettingsExceptions(mutable_settings);
    392 
    393   size_t cookies_block_exception_count = 0;
    394   size_t cookies_allow_exception_count = 0;
    395   size_t cookies_session_only_exception_count = 0;
    396   for (base::DictionaryValue::Iterator i(*mutable_settings); !i.IsAtEnd();
    397        i.Advance()) {
    398     const std::string& pattern_str(i.key());
    399     std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
    400         ParsePatternString(pattern_str);
    401     if (!pattern_pair.first.IsValid() ||
    402         !pattern_pair.second.IsValid()) {
    403       // TODO: Change this to DFATAL when crbug.com/132659 is fixed.
    404       LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
    405       continue;
    406     }
    407 
    408     // Get settings dictionary for the current pattern string, and read
    409     // settings from the dictionary.
    410     const base::DictionaryValue* settings_dictionary = NULL;
    411     bool is_dictionary = i.value().GetAsDictionary(&settings_dictionary);
    412     DCHECK(is_dictionary);
    413 
    414     for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
    415       ContentSettingsType content_type = static_cast<ContentSettingsType>(i);
    416 
    417       std::string res_dictionary_path;
    418       if (GetResourceTypeName(content_type, &res_dictionary_path)) {
    419         const base::DictionaryValue* resource_dictionary = NULL;
    420         if (settings_dictionary->GetDictionary(
    421                 res_dictionary_path, &resource_dictionary)) {
    422           for (base::DictionaryValue::Iterator j(*resource_dictionary);
    423                !j.IsAtEnd();
    424                j.Advance()) {
    425             const std::string& resource_identifier(j.key());
    426             int setting = CONTENT_SETTING_DEFAULT;
    427             bool is_integer = j.value().GetAsInteger(&setting);
    428             DCHECK(is_integer);
    429             DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
    430             value_map_.SetValue(pattern_pair.first,
    431                                 pattern_pair.second,
    432                                 content_type,
    433                                 resource_identifier,
    434                                 new base::FundamentalValue(setting));
    435           }
    436         }
    437       }
    438       base::Value* value = NULL;
    439       if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type)) {
    440         const base::DictionaryValue* setting = NULL;
    441         // TODO(xians): Handle the non-dictionary types.
    442         if (settings_dictionary->GetDictionaryWithoutPathExpansion(
    443             GetTypeName(ContentSettingsType(i)), &setting)) {
    444           DCHECK(!setting->empty());
    445           value = setting->DeepCopy();
    446         }
    447       } else {
    448         int setting = CONTENT_SETTING_DEFAULT;
    449         if (settings_dictionary->GetIntegerWithoutPathExpansion(
    450                 GetTypeName(ContentSettingsType(i)), &setting)) {
    451           DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
    452           setting = FixObsoleteCookiePromptMode(content_type,
    453                                                 ContentSetting(setting));
    454           value = new base::FundamentalValue(setting);
    455         }
    456       }
    457 
    458       // |value_map_| will take the ownership of |value|.
    459       if (value != NULL) {
    460         value_map_.SetValue(pattern_pair.first,
    461                             pattern_pair.second,
    462                             content_type,
    463                             ResourceIdentifier(),
    464                             value);
    465         if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
    466           ContentSetting s = ValueToContentSetting(value);
    467           switch (s) {
    468             case CONTENT_SETTING_ALLOW :
    469               ++cookies_allow_exception_count;
    470               break;
    471             case CONTENT_SETTING_BLOCK :
    472               ++cookies_block_exception_count;
    473               break;
    474             case CONTENT_SETTING_SESSION_ONLY :
    475               ++cookies_session_only_exception_count;
    476               break;
    477             default:
    478               NOTREACHED();
    479               break;
    480           }
    481         }
    482       }
    483     }
    484   }
    485   UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfBlockCookiesExceptions",
    486                        cookies_block_exception_count);
    487   UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfAllowCookiesExceptions",
    488                        cookies_allow_exception_count);
    489   UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfSessionOnlyCookiesExceptions",
    490                        cookies_session_only_exception_count);
    491 }
    492 
    493 void PrefProvider::OnContentSettingsPatternPairsChanged() {
    494   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    495 
    496   if (updating_preferences_)
    497     return;
    498 
    499   ReadContentSettingsFromPref(true);
    500 
    501   NotifyObservers(ContentSettingsPattern(),
    502                   ContentSettingsPattern(),
    503                   CONTENT_SETTINGS_TYPE_DEFAULT,
    504                   std::string());
    505 }
    506 
    507 // static
    508 void PrefProvider::CanonicalizeContentSettingsExceptions(
    509     base::DictionaryValue* all_settings_dictionary) {
    510   DCHECK(all_settings_dictionary);
    511 
    512   std::vector<std::string> remove_items;
    513   base::StringPairs move_items;
    514   for (base::DictionaryValue::Iterator i(*all_settings_dictionary);
    515        !i.IsAtEnd();
    516        i.Advance()) {
    517     const std::string& pattern_str(i.key());
    518     std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
    519          ParsePatternString(pattern_str);
    520     if (!pattern_pair.first.IsValid() ||
    521         !pattern_pair.second.IsValid()) {
    522       LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
    523       continue;
    524     }
    525 
    526     const std::string canonicalized_pattern_str = CreatePatternString(
    527         pattern_pair.first, pattern_pair.second);
    528 
    529     if (canonicalized_pattern_str.empty() ||
    530         canonicalized_pattern_str == pattern_str) {
    531       continue;
    532     }
    533 
    534     // Clear old pattern if prefs already have canonicalized pattern.
    535     const base::DictionaryValue* new_pattern_settings_dictionary = NULL;
    536     if (all_settings_dictionary->GetDictionaryWithoutPathExpansion(
    537             canonicalized_pattern_str, &new_pattern_settings_dictionary)) {
    538       remove_items.push_back(pattern_str);
    539       continue;
    540     }
    541 
    542     // Move old pattern to canonicalized pattern.
    543     const base::DictionaryValue* old_pattern_settings_dictionary = NULL;
    544     if (i.value().GetAsDictionary(&old_pattern_settings_dictionary)) {
    545       move_items.push_back(
    546           std::make_pair(pattern_str, canonicalized_pattern_str));
    547     }
    548   }
    549 
    550   for (size_t i = 0; i < remove_items.size(); ++i) {
    551     all_settings_dictionary->RemoveWithoutPathExpansion(remove_items[i], NULL);
    552   }
    553 
    554   for (size_t i = 0; i < move_items.size(); ++i) {
    555     scoped_ptr<base::Value> pattern_settings_dictionary;
    556     all_settings_dictionary->RemoveWithoutPathExpansion(
    557         move_items[i].first, &pattern_settings_dictionary);
    558     all_settings_dictionary->SetWithoutPathExpansion(
    559         move_items[i].second, pattern_settings_dictionary.release());
    560   }
    561 }
    562 
    563 void PrefProvider::ShutdownOnUIThread() {
    564   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    565   DCHECK(prefs_);
    566   RemoveAllObservers();
    567   pref_change_registrar_.RemoveAll();
    568   prefs_ = NULL;
    569 }
    570 
    571 void PrefProvider::UpdateLastUsage(
    572     const ContentSettingsPattern& primary_pattern,
    573     const ContentSettingsPattern& secondary_pattern,
    574     ContentSettingsType content_type) {
    575   // Don't write if in incognito.
    576   if (is_incognito_) {
    577     return;
    578   }
    579 
    580   // Ensure that |lock_| is not held by this thread, since this function will
    581   // send out notifications (by |~DictionaryPrefUpdate|).
    582   AssertLockNotHeld();
    583 
    584   base::AutoReset<bool> auto_reset(&updating_preferences_, true);
    585   {
    586     DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs);
    587     base::DictionaryValue* pattern_pairs_settings = update.Get();
    588 
    589     std::string pattern_str(
    590         CreatePatternString(primary_pattern, secondary_pattern));
    591     base::DictionaryValue* settings_dictionary = NULL;
    592     bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
    593         pattern_str, &settings_dictionary);
    594 
    595     if (!found) {
    596       settings_dictionary = new base::DictionaryValue;
    597       pattern_pairs_settings->SetWithoutPathExpansion(pattern_str,
    598                                                       settings_dictionary);
    599     }
    600 
    601     base::DictionaryValue* last_used_dictionary = NULL;
    602     found = settings_dictionary->GetDictionaryWithoutPathExpansion(
    603         kLastUsed, &last_used_dictionary);
    604 
    605     if (!found) {
    606       last_used_dictionary = new base::DictionaryValue;
    607       settings_dictionary->SetWithoutPathExpansion(kLastUsed,
    608                                                    last_used_dictionary);
    609     }
    610 
    611     std::string settings_path = GetTypeName(content_type);
    612     last_used_dictionary->Set(
    613         settings_path, new base::FundamentalValue(clock_->Now().ToDoubleT()));
    614   }
    615 }
    616 
    617 base::Time PrefProvider::GetLastUsage(
    618     const ContentSettingsPattern& primary_pattern,
    619     const ContentSettingsPattern& secondary_pattern,
    620     ContentSettingsType content_type) {
    621   const base::DictionaryValue* pattern_pairs_settings =
    622       prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
    623   std::string pattern_str(
    624       CreatePatternString(primary_pattern, secondary_pattern));
    625 
    626   const base::DictionaryValue* settings_dictionary = NULL;
    627   bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
    628       pattern_str, &settings_dictionary);
    629 
    630   if (!found)
    631     return base::Time();
    632 
    633   const base::DictionaryValue* last_used_dictionary = NULL;
    634   found = settings_dictionary->GetDictionaryWithoutPathExpansion(
    635       kLastUsed, &last_used_dictionary);
    636 
    637   if (!found)
    638     return base::Time();
    639 
    640   double last_used_time;
    641   found = last_used_dictionary->GetDoubleWithoutPathExpansion(
    642       GetTypeName(content_type), &last_used_time);
    643 
    644   if (!found)
    645     return base::Time();
    646 
    647   return base::Time::FromDoubleT(last_used_time);
    648 }
    649 
    650 void PrefProvider::AssertLockNotHeld() const {
    651 #if !defined(NDEBUG)
    652   // |Lock::Acquire()| will assert if the lock is held by this thread.
    653   lock_.Acquire();
    654   lock_.Release();
    655 #endif
    656 }
    657 
    658 void PrefProvider::SetClockForTesting(scoped_ptr<base::Clock> clock) {
    659   clock_ = clock.Pass();
    660 }
    661 
    662 }  // namespace content_settings
    663