1 // Copyright (c) 2011 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 <string> 8 #include <utility> 9 #include <vector> 10 11 #include "base/command_line.h" 12 #include "chrome/browser/content_settings/content_settings_details.h" 13 #include "chrome/browser/content_settings/content_settings_pattern.h" 14 #include "chrome/browser/metrics/user_metrics.h" 15 #include "chrome/browser/prefs/pref_service.h" 16 #include "chrome/browser/prefs/scoped_user_pref_update.h" 17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/common/chrome_switches.h" 19 #include "chrome/common/content_settings.h" 20 #include "chrome/common/pref_names.h" 21 #include "content/browser/browser_thread.h" 22 #include "content/common/notification_details.h" 23 #include "content/common/notification_service.h" 24 #include "content/common/notification_source.h" 25 #include "googleurl/src/gurl.h" 26 #include "net/base/net_util.h" 27 28 namespace { 29 30 // The preference keys where resource identifiers are stored for 31 // ContentSettingsType values that support resource identifiers. 32 const char* kResourceTypeNames[] = { 33 NULL, 34 NULL, 35 NULL, 36 "per_plugin", 37 NULL, 38 NULL, // Not used for Geolocation 39 NULL, // Not used for Notifications 40 NULL, // Not used for Prerender. 41 }; 42 COMPILE_ASSERT(arraysize(kResourceTypeNames) == CONTENT_SETTINGS_NUM_TYPES, 43 resource_type_names_incorrect_size); 44 45 // The default setting for each content type. 46 const ContentSetting kDefaultSettings[] = { 47 CONTENT_SETTING_ALLOW, // CONTENT_SETTINGS_TYPE_COOKIES 48 CONTENT_SETTING_ALLOW, // CONTENT_SETTINGS_TYPE_IMAGES 49 CONTENT_SETTING_ALLOW, // CONTENT_SETTINGS_TYPE_JAVASCRIPT 50 CONTENT_SETTING_ALLOW, // CONTENT_SETTINGS_TYPE_PLUGINS 51 CONTENT_SETTING_BLOCK, // CONTENT_SETTINGS_TYPE_POPUPS 52 CONTENT_SETTING_ASK, // Not used for Geolocation 53 CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_NOTIFICATIONS 54 CONTENT_SETTING_ALLOW, // CONTENT_SETTINGS_TYPE_PRERENDER 55 }; 56 COMPILE_ASSERT(arraysize(kDefaultSettings) == CONTENT_SETTINGS_NUM_TYPES, 57 default_settings_incorrect_size); 58 59 // The names of the ContentSettingsType values, for use with dictionary prefs. 60 const char* kTypeNames[] = { 61 "cookies", 62 "images", 63 "javascript", 64 "plugins", 65 "popups", 66 NULL, // Not used for Geolocation 67 // TODO(markusheintz): Refactoring in progress. Content settings exceptions 68 // for notifications will be added next. 69 "notifications", // Only used for default Notifications settings. 70 NULL, // Not used for Prerender 71 }; 72 COMPILE_ASSERT(arraysize(kTypeNames) == CONTENT_SETTINGS_NUM_TYPES, 73 type_names_incorrect_size); 74 75 void SetDefaultContentSettings(DictionaryValue* default_settings) { 76 default_settings->Clear(); 77 78 for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { 79 if (kTypeNames[i] != NULL) { 80 default_settings->SetInteger(kTypeNames[i], 81 kDefaultSettings[i]); 82 } 83 } 84 } 85 86 } // namespace 87 88 namespace content_settings { 89 90 PrefDefaultProvider::PrefDefaultProvider(Profile* profile) 91 : profile_(profile), 92 is_incognito_(profile_->IsOffTheRecord()), 93 updating_preferences_(false) { 94 initializing_ = true; 95 PrefService* prefs = profile->GetPrefs(); 96 97 MigrateObsoleteNotificationPref(prefs); 98 99 // Read global defaults. 100 DCHECK_EQ(arraysize(kTypeNames), 101 static_cast<size_t>(CONTENT_SETTINGS_NUM_TYPES)); 102 ReadDefaultSettings(true); 103 if (default_content_settings_.settings[CONTENT_SETTINGS_TYPE_COOKIES] == 104 CONTENT_SETTING_BLOCK) { 105 UserMetrics::RecordAction( 106 UserMetricsAction("CookieBlockingEnabledPerDefault")); 107 } else { 108 UserMetrics::RecordAction( 109 UserMetricsAction("CookieBlockingDisabledPerDefault")); 110 } 111 112 pref_change_registrar_.Init(prefs); 113 pref_change_registrar_.Add(prefs::kDefaultContentSettings, this); 114 notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED, 115 Source<Profile>(profile_)); 116 initializing_ = false; 117 } 118 119 PrefDefaultProvider::~PrefDefaultProvider() { 120 UnregisterObservers(); 121 } 122 123 ContentSetting PrefDefaultProvider::ProvideDefaultSetting( 124 ContentSettingsType content_type) const { 125 base::AutoLock lock(lock_); 126 return default_content_settings_.settings[content_type]; 127 } 128 129 void PrefDefaultProvider::UpdateDefaultSetting( 130 ContentSettingsType content_type, 131 ContentSetting setting) { 132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 133 DCHECK(kTypeNames[content_type] != NULL); // Don't call this for Geolocation. 134 DCHECK(content_type != CONTENT_SETTINGS_TYPE_PLUGINS || 135 setting != CONTENT_SETTING_ASK || 136 CommandLine::ForCurrentProcess()->HasSwitch( 137 switches::kEnableClickToPlay)); 138 139 // The default settings may not be directly modified for OTR sessions. 140 // Instead, they are synced to the main profile's setting. 141 if (is_incognito_) 142 return; 143 144 PrefService* prefs = profile_->GetPrefs(); 145 146 std::string dictionary_path(kTypeNames[content_type]); 147 updating_preferences_ = true; 148 { 149 base::AutoLock lock(lock_); 150 DictionaryPrefUpdate update(prefs, prefs::kDefaultContentSettings); 151 DictionaryValue* default_settings_dictionary = update.Get(); 152 if ((setting == CONTENT_SETTING_DEFAULT) || 153 (setting == kDefaultSettings[content_type])) { 154 default_content_settings_.settings[content_type] = 155 kDefaultSettings[content_type]; 156 default_settings_dictionary->RemoveWithoutPathExpansion(dictionary_path, 157 NULL); 158 } else { 159 default_content_settings_.settings[content_type] = setting; 160 default_settings_dictionary->SetWithoutPathExpansion( 161 dictionary_path, Value::CreateIntegerValue(setting)); 162 } 163 } 164 updating_preferences_ = false; 165 166 NotifyObservers( 167 ContentSettingsDetails(ContentSettingsPattern(), content_type, "")); 168 } 169 170 bool PrefDefaultProvider::DefaultSettingIsManaged( 171 ContentSettingsType content_type) const { 172 return false; 173 } 174 175 void PrefDefaultProvider::ResetToDefaults() { 176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 177 base::AutoLock lock(lock_); 178 default_content_settings_ = ContentSettings(); 179 ForceDefaultsToBeExplicit(); 180 181 if (!is_incognito_) { 182 PrefService* prefs = profile_->GetPrefs(); 183 updating_preferences_ = true; 184 prefs->ClearPref(prefs::kDefaultContentSettings); 185 updating_preferences_ = false; 186 } 187 } 188 189 void PrefDefaultProvider::Observe(NotificationType type, 190 const NotificationSource& source, 191 const NotificationDetails& details) { 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 193 194 if (type == NotificationType::PREF_CHANGED) { 195 DCHECK_EQ(profile_->GetPrefs(), Source<PrefService>(source).ptr()); 196 if (updating_preferences_) 197 return; 198 199 std::string* name = Details<std::string>(details).ptr(); 200 if (*name == prefs::kDefaultContentSettings) { 201 ReadDefaultSettings(true); 202 } else { 203 NOTREACHED() << "Unexpected preference observed"; 204 return; 205 } 206 207 if (!is_incognito_) { 208 NotifyObservers(ContentSettingsDetails( 209 ContentSettingsPattern(), CONTENT_SETTINGS_TYPE_DEFAULT, "")); 210 } 211 } else if (type == NotificationType::PROFILE_DESTROYED) { 212 DCHECK_EQ(profile_, Source<Profile>(source).ptr()); 213 UnregisterObservers(); 214 } else { 215 NOTREACHED() << "Unexpected notification"; 216 } 217 } 218 219 void PrefDefaultProvider::UnregisterObservers() { 220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 221 if (!profile_) 222 return; 223 pref_change_registrar_.RemoveAll(); 224 notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED, 225 Source<Profile>(profile_)); 226 profile_ = NULL; 227 } 228 229 void PrefDefaultProvider::ReadDefaultSettings(bool overwrite) { 230 PrefService* prefs = profile_->GetPrefs(); 231 const DictionaryValue* default_settings_dictionary = 232 prefs->GetDictionary(prefs::kDefaultContentSettings); 233 234 base::AutoLock lock(lock_); 235 236 if (overwrite) 237 default_content_settings_ = ContentSettings(); 238 239 // Careful: The returned value could be NULL if the pref has never been set. 240 if (default_settings_dictionary != NULL) { 241 GetSettingsFromDictionary(default_settings_dictionary, 242 &default_content_settings_); 243 } 244 ForceDefaultsToBeExplicit(); 245 } 246 247 void PrefDefaultProvider::ForceDefaultsToBeExplicit() { 248 DCHECK_EQ(arraysize(kDefaultSettings), 249 static_cast<size_t>(CONTENT_SETTINGS_NUM_TYPES)); 250 251 for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { 252 if (default_content_settings_.settings[i] == CONTENT_SETTING_DEFAULT) 253 default_content_settings_.settings[i] = kDefaultSettings[i]; 254 } 255 } 256 257 void PrefDefaultProvider::GetSettingsFromDictionary( 258 const DictionaryValue* dictionary, 259 ContentSettings* settings) { 260 for (DictionaryValue::key_iterator i(dictionary->begin_keys()); 261 i != dictionary->end_keys(); ++i) { 262 const std::string& content_type(*i); 263 for (size_t type = 0; type < arraysize(kTypeNames); ++type) { 264 if ((kTypeNames[type] != NULL) && (kTypeNames[type] == content_type)) { 265 int setting = CONTENT_SETTING_DEFAULT; 266 bool found = dictionary->GetIntegerWithoutPathExpansion(content_type, 267 &setting); 268 DCHECK(found); 269 settings->settings[type] = IntToContentSetting(setting); 270 break; 271 } 272 } 273 } 274 // Migrate obsolete cookie prompt mode. 275 if (settings->settings[CONTENT_SETTINGS_TYPE_COOKIES] == 276 CONTENT_SETTING_ASK) 277 settings->settings[CONTENT_SETTINGS_TYPE_COOKIES] = CONTENT_SETTING_BLOCK; 278 279 settings->settings[CONTENT_SETTINGS_TYPE_PLUGINS] = 280 BaseProvider::ClickToPlayFixup( 281 CONTENT_SETTINGS_TYPE_PLUGINS, 282 settings->settings[CONTENT_SETTINGS_TYPE_PLUGINS]); 283 } 284 285 void PrefDefaultProvider::NotifyObservers( 286 const ContentSettingsDetails& details) { 287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 288 if (initializing_ || profile_ == NULL) 289 return; 290 NotificationService::current()->Notify( 291 NotificationType::CONTENT_SETTINGS_CHANGED, 292 Source<HostContentSettingsMap>(profile_->GetHostContentSettingsMap()), 293 Details<const ContentSettingsDetails>(&details)); 294 } 295 296 void PrefDefaultProvider::MigrateObsoleteNotificationPref(PrefService* prefs) { 297 if (prefs->HasPrefPath(prefs::kDesktopNotificationDefaultContentSetting)) { 298 ContentSetting setting = IntToContentSetting( 299 prefs->GetInteger(prefs::kDesktopNotificationDefaultContentSetting)); 300 UpdateDefaultSetting(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, setting); 301 prefs->ClearPref(prefs::kDesktopNotificationDefaultContentSetting); 302 } 303 } 304 305 // static 306 void PrefDefaultProvider::RegisterUserPrefs(PrefService* prefs) { 307 // The registration of the preference prefs::kDefaultContentSettings should 308 // also include the default values for default content settings. This allows 309 // functional tests to get default content settings by reading the preference 310 // prefs::kDefaultContentSettings via pyauto. 311 // TODO(markusheintz): Write pyauto hooks for the content settings map as 312 // content settings should be read from the host content settings map. 313 DictionaryValue* default_content_settings = new DictionaryValue(); 314 SetDefaultContentSettings(default_content_settings); 315 prefs->RegisterDictionaryPref(prefs::kDefaultContentSettings, 316 default_content_settings); 317 318 // Obsolete prefs, for migrations: 319 prefs->RegisterIntegerPref( 320 prefs::kDesktopNotificationDefaultContentSetting, 321 kDefaultSettings[CONTENT_SETTINGS_TYPE_NOTIFICATIONS]); 322 } 323 324 // //////////////////////////////////////////////////////////////////////////// 325 // PrefProvider: 326 // 327 328 // static 329 void PrefProvider::RegisterUserPrefs(PrefService* prefs) { 330 prefs->RegisterIntegerPref(prefs::kContentSettingsVersion, 331 ContentSettingsPattern::kContentSettingsPatternVersion); 332 prefs->RegisterDictionaryPref(prefs::kContentSettingsPatterns); 333 334 // Obsolete prefs, for migration: 335 prefs->RegisterListPref(prefs::kPopupWhitelistedHosts); 336 prefs->RegisterDictionaryPref(prefs::kPerHostContentSettings); 337 } 338 339 PrefProvider::PrefProvider(Profile* profile) 340 : BaseProvider(profile->IsOffTheRecord()), 341 profile_(profile), 342 updating_preferences_(false) { 343 Init(); 344 } 345 346 void PrefProvider::Init() { 347 initializing_ = true; 348 PrefService* prefs = profile_->GetPrefs(); 349 350 // Migrate obsolete preferences. 351 MigrateObsoletePerhostPref(prefs); 352 MigrateObsoletePopupsPref(prefs); 353 354 // Verify preferences version. 355 if (!prefs->HasPrefPath(prefs::kContentSettingsVersion)) { 356 prefs->SetInteger(prefs::kContentSettingsVersion, 357 ContentSettingsPattern::kContentSettingsPatternVersion); 358 } 359 if (prefs->GetInteger(prefs::kContentSettingsVersion) > 360 ContentSettingsPattern::kContentSettingsPatternVersion) { 361 LOG(ERROR) << "Unknown content settings version in preferences."; 362 return; 363 } 364 365 // Read exceptions. 366 ReadExceptions(false); 367 368 pref_change_registrar_.Init(prefs); 369 pref_change_registrar_.Add(prefs::kContentSettingsPatterns, this); 370 371 notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED, 372 Source<Profile>(profile_)); 373 initializing_ = false; 374 } 375 376 bool PrefProvider::ContentSettingsTypeIsManaged( 377 ContentSettingsType content_type) { 378 return false; 379 } 380 381 void PrefProvider::SetContentSetting( 382 const ContentSettingsPattern& requesting_pattern, 383 const ContentSettingsPattern& embedding_pattern, 384 ContentSettingsType content_type, 385 const ResourceIdentifier& resource_identifier, 386 ContentSetting setting) { 387 // Support for embedding_patterns is not implemented yet. 388 DCHECK(requesting_pattern == embedding_pattern); 389 390 DCHECK(kTypeNames[content_type] != NULL); // Don't call this for Geolocation. 391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 392 DCHECK_NE(RequiresResourceIdentifier(content_type), 393 resource_identifier.empty()); 394 DCHECK(content_type != CONTENT_SETTINGS_TYPE_PLUGINS || 395 setting != CONTENT_SETTING_ASK || 396 CommandLine::ForCurrentProcess()->HasSwitch( 397 switches::kEnableClickToPlay)); 398 399 const ContentSettingsPattern pattern( 400 requesting_pattern.CanonicalizePattern()); 401 402 bool early_exit = false; 403 std::string pattern_str(pattern.AsString()); 404 DictionaryValue* all_settings_dictionary = NULL; 405 406 updating_preferences_ = true; 407 { // Begin scope of update. 408 // profile_ may be NULL in unit tests. 409 DictionaryPrefUpdate update(profile_ ? profile_->GetPrefs() : NULL, 410 prefs::kContentSettingsPatterns); 411 412 // Select content-settings-map to write to. 413 HostContentSettings* map_to_modify = incognito_settings(); 414 if (!is_incognito()) { 415 all_settings_dictionary = update.Get(); 416 417 map_to_modify = host_content_settings(); 418 } 419 420 // Update content-settings-map. 421 { 422 base::AutoLock auto_lock(lock()); 423 if (!map_to_modify->count(pattern_str)) 424 (*map_to_modify)[pattern_str].content_settings = ContentSettings(); 425 HostContentSettings::iterator i(map_to_modify->find(pattern_str)); 426 ContentSettings& settings = i->second.content_settings; 427 if (RequiresResourceIdentifier(content_type)) { 428 settings.settings[content_type] = CONTENT_SETTING_DEFAULT; 429 if (setting != CONTENT_SETTING_DEFAULT) { 430 i->second.content_settings_for_resources[ 431 ContentSettingsTypeResourceIdentifierPair(content_type, 432 resource_identifier)] = setting; 433 } else { 434 i->second.content_settings_for_resources.erase( 435 ContentSettingsTypeResourceIdentifierPair(content_type, 436 resource_identifier)); 437 } 438 } else { 439 settings.settings[content_type] = setting; 440 } 441 if (AllDefault(i->second)) { 442 map_to_modify->erase(i); 443 if (all_settings_dictionary) 444 all_settings_dictionary->RemoveWithoutPathExpansion( 445 pattern_str, NULL); 446 447 // We can't just return because |NotifyObservers()| needs to be called, 448 // without lock() being held. 449 early_exit = true; 450 } 451 } 452 453 // Update the content_settings preference. 454 if (!early_exit && all_settings_dictionary) { 455 DictionaryValue* host_settings_dictionary = NULL; 456 bool found = all_settings_dictionary->GetDictionaryWithoutPathExpansion( 457 pattern_str, &host_settings_dictionary); 458 if (!found) { 459 host_settings_dictionary = new DictionaryValue; 460 all_settings_dictionary->SetWithoutPathExpansion( 461 pattern_str, host_settings_dictionary); 462 DCHECK_NE(setting, CONTENT_SETTING_DEFAULT); 463 } 464 if (RequiresResourceIdentifier(content_type)) { 465 std::string dictionary_path(kResourceTypeNames[content_type]); 466 DictionaryValue* resource_dictionary = NULL; 467 found = host_settings_dictionary->GetDictionary( 468 dictionary_path, &resource_dictionary); 469 if (!found) { 470 resource_dictionary = new DictionaryValue; 471 host_settings_dictionary->Set(dictionary_path, resource_dictionary); 472 } 473 if (setting == CONTENT_SETTING_DEFAULT) { 474 resource_dictionary->RemoveWithoutPathExpansion(resource_identifier, 475 NULL); 476 } else { 477 resource_dictionary->SetWithoutPathExpansion( 478 resource_identifier, Value::CreateIntegerValue(setting)); 479 } 480 } else { 481 std::string dictionary_path(kTypeNames[content_type]); 482 if (setting == CONTENT_SETTING_DEFAULT) { 483 host_settings_dictionary->RemoveWithoutPathExpansion(dictionary_path, 484 NULL); 485 } else { 486 host_settings_dictionary->SetWithoutPathExpansion( 487 dictionary_path, Value::CreateIntegerValue(setting)); 488 } 489 } 490 } 491 } // End scope of update. 492 updating_preferences_ = false; 493 494 NotifyObservers(ContentSettingsDetails(pattern, content_type, "")); 495 } 496 497 void PrefProvider::ResetToDefaults() { 498 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 499 500 { 501 base::AutoLock auto_lock(lock()); 502 host_content_settings()->clear(); 503 incognito_settings()->clear(); 504 } 505 506 if (!is_incognito()) { 507 PrefService* prefs = profile_->GetPrefs(); 508 updating_preferences_ = true; 509 prefs->ClearPref(prefs::kContentSettingsPatterns); 510 updating_preferences_ = false; 511 } 512 } 513 514 void PrefProvider::ClearAllContentSettingsRules( 515 ContentSettingsType content_type) { 516 DCHECK(kTypeNames[content_type] != NULL); // Don't call this for Geolocation. 517 518 DictionaryValue* all_settings_dictionary = NULL; 519 HostContentSettings* map_to_modify = incognito_settings(); 520 521 updating_preferences_ = true; 522 { // Begin scope of update. 523 DictionaryPrefUpdate update(profile_->GetPrefs(), 524 prefs::kContentSettingsPatterns); 525 526 if (!is_incognito()) { 527 all_settings_dictionary = update.Get(); 528 map_to_modify = host_content_settings(); 529 } 530 531 { 532 base::AutoLock auto_lock(lock()); 533 for (HostContentSettings::iterator i(map_to_modify->begin()); 534 i != map_to_modify->end(); ) { 535 if (RequiresResourceIdentifier(content_type) || 536 i->second.content_settings.settings[content_type] != 537 CONTENT_SETTING_DEFAULT) { 538 if (RequiresResourceIdentifier(content_type)) 539 i->second.content_settings_for_resources.clear(); 540 i->second.content_settings.settings[content_type] = 541 CONTENT_SETTING_DEFAULT; 542 std::string host(i->first); 543 if (AllDefault(i->second)) { 544 if (all_settings_dictionary) 545 all_settings_dictionary->RemoveWithoutPathExpansion(host, NULL); 546 map_to_modify->erase(i++); 547 } else if (all_settings_dictionary) { 548 DictionaryValue* host_settings_dictionary; 549 bool found = 550 all_settings_dictionary->GetDictionaryWithoutPathExpansion( 551 host, &host_settings_dictionary); 552 DCHECK(found); 553 host_settings_dictionary->RemoveWithoutPathExpansion( 554 kTypeNames[content_type], NULL); 555 ++i; 556 } 557 } else { 558 ++i; 559 } 560 } 561 } 562 } // End scope of update. 563 updating_preferences_ = false; 564 565 NotifyObservers( 566 ContentSettingsDetails(ContentSettingsPattern(), content_type, "")); 567 } 568 569 void PrefProvider::Observe( 570 NotificationType type, 571 const NotificationSource& source, 572 const NotificationDetails& details) { 573 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 574 575 if (type == NotificationType::PREF_CHANGED) { 576 DCHECK_EQ(profile_->GetPrefs(), Source<PrefService>(source).ptr()); 577 if (updating_preferences_) 578 return; 579 580 std::string* name = Details<std::string>(details).ptr(); 581 if (*name == prefs::kContentSettingsPatterns) { 582 ReadExceptions(true); 583 } else { 584 NOTREACHED() << "Unexpected preference observed"; 585 return; 586 } 587 588 if (!is_incognito()) { 589 NotifyObservers(ContentSettingsDetails(ContentSettingsPattern(), 590 CONTENT_SETTINGS_TYPE_DEFAULT, 591 "")); 592 } 593 } else if (type == NotificationType::PROFILE_DESTROYED) { 594 DCHECK_EQ(profile_, Source<Profile>(source).ptr()); 595 UnregisterObservers(); 596 } else { 597 NOTREACHED() << "Unexpected notification"; 598 } 599 } 600 601 PrefProvider::~PrefProvider() { 602 UnregisterObservers(); 603 } 604 605 // //////////////////////////////////////////////////////////////////////////// 606 // Private 607 608 void PrefProvider::ReadExceptions(bool overwrite) { 609 base::AutoLock auto_lock(lock()); 610 611 PrefService* prefs = profile_->GetPrefs(); 612 const DictionaryValue* all_settings_dictionary = 613 prefs->GetDictionary(prefs::kContentSettingsPatterns); 614 615 if (overwrite) 616 host_content_settings()->clear(); 617 618 updating_preferences_ = true; 619 620 // Careful: The returned value could be NULL if the pref has never been set. 621 if (all_settings_dictionary != NULL) { 622 DictionaryPrefUpdate update(prefs, prefs::kContentSettingsPatterns); 623 DictionaryValue* mutable_settings; 624 scoped_ptr<DictionaryValue> mutable_settings_scope; 625 626 if (!is_incognito()) { 627 mutable_settings = update.Get(); 628 } else { 629 // Create copy as we do not want to persist anything in OTR prefs. 630 mutable_settings = all_settings_dictionary->DeepCopy(); 631 mutable_settings_scope.reset(mutable_settings); 632 } 633 634 // Convert all Unicode patterns into punycode form, then read. 635 CanonicalizeContentSettingsExceptions(mutable_settings); 636 637 for (DictionaryValue::key_iterator i(mutable_settings->begin_keys()); 638 i != mutable_settings->end_keys(); ++i) { 639 const std::string& pattern(*i); 640 if (!ContentSettingsPattern(pattern).IsValid()) 641 LOG(WARNING) << "Invalid pattern stored in content settings"; 642 DictionaryValue* pattern_settings_dictionary = NULL; 643 bool found = mutable_settings->GetDictionaryWithoutPathExpansion( 644 pattern, &pattern_settings_dictionary); 645 DCHECK(found); 646 647 ExtendedContentSettings extended_settings; 648 GetSettingsFromDictionary(pattern_settings_dictionary, 649 &extended_settings.content_settings); 650 GetResourceSettingsFromDictionary( 651 pattern_settings_dictionary, 652 &extended_settings.content_settings_for_resources); 653 654 (*host_content_settings())[pattern] = extended_settings; 655 } 656 } 657 updating_preferences_ = false; 658 } 659 660 void PrefProvider::CanonicalizeContentSettingsExceptions( 661 DictionaryValue* all_settings_dictionary) { 662 DCHECK(all_settings_dictionary); 663 664 std::vector<std::string> remove_items; 665 std::vector<std::pair<std::string, std::string> > move_items; 666 for (DictionaryValue::key_iterator i(all_settings_dictionary->begin_keys()); 667 i != all_settings_dictionary->end_keys(); ++i) { 668 const std::string& pattern(*i); 669 const std::string canonicalized_pattern = 670 ContentSettingsPattern(pattern).CanonicalizePattern(); 671 672 if (canonicalized_pattern.empty() || canonicalized_pattern == pattern) 673 continue; 674 675 // Clear old pattern if prefs already have canonicalized pattern. 676 DictionaryValue* new_pattern_settings_dictionary = NULL; 677 if (all_settings_dictionary->GetDictionaryWithoutPathExpansion( 678 canonicalized_pattern, &new_pattern_settings_dictionary)) { 679 remove_items.push_back(pattern); 680 continue; 681 } 682 683 // Move old pattern to canonicalized pattern. 684 DictionaryValue* old_pattern_settings_dictionary = NULL; 685 if (all_settings_dictionary->GetDictionaryWithoutPathExpansion( 686 pattern, &old_pattern_settings_dictionary)) { 687 move_items.push_back(std::make_pair(pattern, canonicalized_pattern)); 688 } 689 } 690 691 for (size_t i = 0; i < remove_items.size(); ++i) { 692 all_settings_dictionary->RemoveWithoutPathExpansion(remove_items[i], NULL); 693 } 694 695 for (size_t i = 0; i < move_items.size(); ++i) { 696 Value* pattern_settings_dictionary = NULL; 697 all_settings_dictionary->RemoveWithoutPathExpansion( 698 move_items[i].first, &pattern_settings_dictionary); 699 all_settings_dictionary->SetWithoutPathExpansion( 700 move_items[i].second, pattern_settings_dictionary); 701 } 702 } 703 704 void PrefProvider::GetSettingsFromDictionary( 705 const DictionaryValue* dictionary, 706 ContentSettings* settings) { 707 for (DictionaryValue::key_iterator i(dictionary->begin_keys()); 708 i != dictionary->end_keys(); ++i) { 709 const std::string& content_type(*i); 710 for (size_t type = 0; type < arraysize(kTypeNames); ++type) { 711 if ((kTypeNames[type] != NULL) && (kTypeNames[type] == content_type)) { 712 int setting = CONTENT_SETTING_DEFAULT; 713 bool found = dictionary->GetIntegerWithoutPathExpansion(content_type, 714 &setting); 715 DCHECK(found); 716 settings->settings[type] = IntToContentSetting(setting); 717 break; 718 } 719 } 720 } 721 // Migrate obsolete cookie prompt mode. 722 if (settings->settings[CONTENT_SETTINGS_TYPE_COOKIES] == 723 CONTENT_SETTING_ASK) 724 settings->settings[CONTENT_SETTINGS_TYPE_COOKIES] = CONTENT_SETTING_BLOCK; 725 726 settings->settings[CONTENT_SETTINGS_TYPE_PLUGINS] = 727 ClickToPlayFixup(CONTENT_SETTINGS_TYPE_PLUGINS, 728 settings->settings[CONTENT_SETTINGS_TYPE_PLUGINS]); 729 } 730 731 void PrefProvider::GetResourceSettingsFromDictionary( 732 const DictionaryValue* dictionary, 733 ResourceContentSettings* settings) { 734 for (DictionaryValue::key_iterator i(dictionary->begin_keys()); 735 i != dictionary->end_keys(); ++i) { 736 const std::string& content_type(*i); 737 for (size_t type = 0; type < arraysize(kResourceTypeNames); ++type) { 738 if ((kResourceTypeNames[type] != NULL) && 739 (kResourceTypeNames[type] == content_type)) { 740 DictionaryValue* resource_dictionary = NULL; 741 bool found = dictionary->GetDictionary(content_type, 742 &resource_dictionary); 743 DCHECK(found); 744 for (DictionaryValue::key_iterator j(resource_dictionary->begin_keys()); 745 j != resource_dictionary->end_keys(); ++j) { 746 const std::string& resource_identifier(*j); 747 int setting = CONTENT_SETTING_DEFAULT; 748 bool found = resource_dictionary->GetIntegerWithoutPathExpansion( 749 resource_identifier, &setting); 750 DCHECK(found); 751 (*settings)[ContentSettingsTypeResourceIdentifierPair( 752 ContentSettingsType(type), resource_identifier)] = 753 ClickToPlayFixup(ContentSettingsType(type), 754 ContentSetting(setting)); 755 } 756 757 break; 758 } 759 } 760 } 761 } 762 763 void PrefProvider::NotifyObservers( 764 const ContentSettingsDetails& details) { 765 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 766 if (initializing_ || profile_ == NULL) 767 return; 768 NotificationService::current()->Notify( 769 NotificationType::CONTENT_SETTINGS_CHANGED, 770 Source<HostContentSettingsMap>( 771 profile_->GetHostContentSettingsMap()), 772 Details<const ContentSettingsDetails>(&details)); 773 } 774 775 void PrefProvider::UnregisterObservers() { 776 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 777 if (!profile_) 778 return; 779 pref_change_registrar_.RemoveAll(); 780 notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED, 781 Source<Profile>(profile_)); 782 profile_ = NULL; 783 } 784 785 void PrefProvider::MigrateObsoletePerhostPref(PrefService* prefs) { 786 if (prefs->HasPrefPath(prefs::kPerHostContentSettings)) { 787 const DictionaryValue* all_settings_dictionary = 788 prefs->GetDictionary(prefs::kPerHostContentSettings); 789 DCHECK(all_settings_dictionary); 790 for (DictionaryValue::key_iterator 791 i(all_settings_dictionary->begin_keys()); 792 i != all_settings_dictionary->end_keys(); ++i) { 793 const std::string& host(*i); 794 ContentSettingsPattern pattern( 795 std::string(ContentSettingsPattern::kDomainWildcard) + host); 796 DictionaryValue* host_settings_dictionary = NULL; 797 bool found = all_settings_dictionary->GetDictionaryWithoutPathExpansion( 798 host, &host_settings_dictionary); 799 DCHECK(found); 800 ContentSettings settings; 801 GetSettingsFromDictionary(host_settings_dictionary, &settings); 802 for (int j = 0; j < CONTENT_SETTINGS_NUM_TYPES; ++j) { 803 if (settings.settings[j] != CONTENT_SETTING_DEFAULT && 804 !RequiresResourceIdentifier(ContentSettingsType(j))) { 805 SetContentSetting( 806 pattern, 807 pattern, 808 ContentSettingsType(j), 809 "", 810 settings.settings[j]); 811 } 812 } 813 } 814 prefs->ClearPref(prefs::kPerHostContentSettings); 815 } 816 } 817 818 void PrefProvider::MigrateObsoletePopupsPref(PrefService* prefs) { 819 if (prefs->HasPrefPath(prefs::kPopupWhitelistedHosts)) { 820 const ListValue* whitelist_pref = 821 prefs->GetList(prefs::kPopupWhitelistedHosts); 822 for (ListValue::const_iterator i(whitelist_pref->begin()); 823 i != whitelist_pref->end(); ++i) { 824 std::string host; 825 (*i)->GetAsString(&host); 826 SetContentSetting(ContentSettingsPattern(host), 827 ContentSettingsPattern(host), 828 CONTENT_SETTINGS_TYPE_POPUPS, 829 "", 830 CONTENT_SETTING_ALLOW); 831 } 832 prefs->ClearPref(prefs::kPopupWhitelistedHosts); 833 } 834 } 835 836 } // namespace content_settings 837