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