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
32using base::UserMetricsAction;
33using content::BrowserThread;
34
35namespace {
36
37typedef std::pair<std::string, std::string> StringPair;
38typedef std::map<std::string, std::string> StringMap;
39
40const char kPerPluginPrefName[] = "per_plugin";
41const char kAudioKey[] = "audio";
42const char kVideoKey[] = "video";
43const char kLastUsed[] = "last_used";
44
45ContentSetting 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.
58bool 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
69namespace content_settings {
70
71// ////////////////////////////////////////////////////////////////////////////
72// PrefProvider:
73//
74
75// static
76void 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
87PrefProvider::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
123bool 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
181void 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
216PrefProvider::~PrefProvider() {
217  DCHECK(!prefs_);
218}
219
220RuleIterator* 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
234void 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
310void 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
360void 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
493void 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
508void 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
563void PrefProvider::ShutdownOnUIThread() {
564  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
565  DCHECK(prefs_);
566  RemoveAllObservers();
567  pref_change_registrar_.RemoveAll();
568  prefs_ = NULL;
569}
570
571void 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
617base::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
650void 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
658void PrefProvider::SetClockForTesting(scoped_ptr<base::Clock> clock) {
659  clock_ = clock.Pass();
660}
661
662}  // namespace content_settings
663