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