Move Webstore URL concepts to //extensions and out
[chromium-blink-merge.git] / chrome / browser / content_settings / content_settings_pref_provider.cc
blobc0a67216ab89c953da273a45d060dae73ae31d43
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.
5 #include "chrome/browser/content_settings/content_settings_pref_provider.h"
7 #include <map>
8 #include <string>
9 #include <utility>
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"
32 using base::UserMetricsAction;
33 using content::BrowserThread;
35 namespace {
37 typedef std::pair<std::string, std::string> StringPair;
38 typedef std::map<std::string, std::string> StringMap;
40 const char kPerPluginPrefName[] = "per_plugin";
41 const char kAudioKey[] = "audio";
42 const char kVideoKey[] = "video";
43 const char kLastUsed[] = "last_used";
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;
51 return setting;
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;
64 return false;
67 } // namespace
69 namespace content_settings {
71 // ////////////////////////////////////////////////////////////////////////////
72 // PrefProvider:
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);
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);
98 if (prefs_->GetInteger(prefs::kContentSettingsVersion) >
99 ContentSettingsPattern::kContentSettingsPatternVersion) {
100 return;
103 // Read content settings exceptions.
104 ReadContentSettingsFromPref(false);
106 if (!is_incognito_) {
107 UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfExceptions",
108 value_map_.size());
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();
116 pref_change_registrar_.Init(prefs_);
117 pref_change_registrar_.Add(
118 prefs::kContentSettingsPatternPairs,
119 base::Bind(&PrefProvider::OnContentSettingsPatternPairsChanged,
120 base::Unretained(this)));
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;
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_;
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);
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());
175 NotifyObservers(
176 primary_pattern, secondary_pattern, content_type, resource_identifier);
178 return true;
181 void PrefProvider::ClearAllContentSettingsRules(
182 ContentSettingsType content_type) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
184 DCHECK(prefs_);
186 OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
187 if (!is_incognito_)
188 map_to_modify = &value_map_;
190 std::vector<Rule> rules_to_delete;
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());
199 map_to_modify->DeleteValues(content_type, std::string());
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);
210 NotifyObservers(ContentSettingsPattern(),
211 ContentSettingsPattern(),
212 content_type,
213 std::string());
216 PrefProvider::~PrefProvider() {
217 DCHECK(!prefs_);
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_);
231 // ////////////////////////////////////////////////////////////////////////////
232 // Private
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();
244 base::AutoReset<bool> auto_reset(&updating_preferences_, true);
246 DictionaryPrefUpdate update(prefs_,
247 prefs::kContentSettingsPatternPairs);
248 base::DictionaryValue* pattern_pairs_settings = update.Get();
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);
257 if (!found && value) {
258 settings_dictionary = new base::DictionaryValue;
259 pattern_pairs_settings->SetWithoutPathExpansion(
260 pattern_str, settings_dictionary);
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);
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);
284 } else {
285 resource_dictionary->SetWithoutPathExpansion(
286 resource_identifier, value->DeepCopy());
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());
300 // Remove the settings dictionary if it is empty.
301 if (settings_dictionary->empty()) {
302 pattern_pairs_settings->RemoveWithoutPathExpansion(
303 pattern_str, NULL);
310 void PrefProvider::MigrateObsoleteMediaContentSetting() {
311 std::vector<Rule> rules_to_delete;
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);
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;
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));
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));
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);
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_);
370 const base::DictionaryValue* all_settings_dictionary =
371 prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
373 if (overwrite)
374 value_map_.clear();
376 // Careful: The returned value could be NULL if the pref has never been set.
377 if (!all_settings_dictionary)
378 return;
380 base::DictionaryValue* mutable_settings;
381 scoped_ptr<base::DictionaryValue> mutable_settings_scope;
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);
390 // Convert all Unicode patterns into punycode form, then read.
391 CanonicalizeContentSettingsExceptions(mutable_settings);
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;
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);
414 for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
415 ContentSettingsType content_type = static_cast<ContentSettingsType>(i);
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));
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();
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);
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;
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);
493 void PrefProvider::OnContentSettingsPatternPairsChanged() {
494 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
496 if (updating_preferences_)
497 return;
499 ReadContentSettingsFromPref(true);
501 NotifyObservers(ContentSettingsPattern(),
502 ContentSettingsPattern(),
503 CONTENT_SETTINGS_TYPE_DEFAULT,
504 std::string());
507 // static
508 void PrefProvider::CanonicalizeContentSettingsExceptions(
509 base::DictionaryValue* all_settings_dictionary) {
510 DCHECK(all_settings_dictionary);
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;
526 const std::string canonicalized_pattern_str = CreatePatternString(
527 pattern_pair.first, pattern_pair.second);
529 if (canonicalized_pattern_str.empty() ||
530 canonicalized_pattern_str == pattern_str) {
531 continue;
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;
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));
550 for (size_t i = 0; i < remove_items.size(); ++i) {
551 all_settings_dictionary->RemoveWithoutPathExpansion(remove_items[i], NULL);
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());
563 void PrefProvider::ShutdownOnUIThread() {
564 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
565 DCHECK(prefs_);
566 RemoveAllObservers();
567 pref_change_registrar_.RemoveAll();
568 prefs_ = NULL;
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;
580 // Ensure that |lock_| is not held by this thread, since this function will
581 // send out notifications (by |~DictionaryPrefUpdate|).
582 AssertLockNotHeld();
584 base::AutoReset<bool> auto_reset(&updating_preferences_, true);
586 DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs);
587 base::DictionaryValue* pattern_pairs_settings = update.Get();
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);
595 if (!found) {
596 settings_dictionary = new base::DictionaryValue;
597 pattern_pairs_settings->SetWithoutPathExpansion(pattern_str,
598 settings_dictionary);
601 base::DictionaryValue* last_used_dictionary = NULL;
602 found = settings_dictionary->GetDictionaryWithoutPathExpansion(
603 kLastUsed, &last_used_dictionary);
605 if (!found) {
606 last_used_dictionary = new base::DictionaryValue;
607 settings_dictionary->SetWithoutPathExpansion(kLastUsed,
608 last_used_dictionary);
611 std::string settings_path = GetTypeName(content_type);
612 last_used_dictionary->Set(
613 settings_path, new base::FundamentalValue(clock_->Now().ToDoubleT()));
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));
626 const base::DictionaryValue* settings_dictionary = NULL;
627 bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
628 pattern_str, &settings_dictionary);
630 if (!found)
631 return base::Time();
633 const base::DictionaryValue* last_used_dictionary = NULL;
634 found = settings_dictionary->GetDictionaryWithoutPathExpansion(
635 kLastUsed, &last_used_dictionary);
637 if (!found)
638 return base::Time();
640 double last_used_time;
641 found = last_used_dictionary->GetDoubleWithoutPathExpansion(
642 GetTypeName(content_type), &last_used_time);
644 if (!found)
645 return base::Time();
647 return base::Time::FromDoubleT(last_used_time);
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
658 void PrefProvider::SetClockForTesting(scoped_ptr<base::Clock> clock) {
659 clock_ = clock.Pass();
662 } // namespace content_settings