Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / content_settings / content_settings_pref_provider.cc
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