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