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/extensions/api/content_settings/content_settings_store.h" 6 7 #include <set> 8 9 #include "base/debug/alias.h" 10 #include "base/logging.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_vector.h" 13 #include "base/stl_util.h" 14 #include "base/strings/string_util.h" 15 #include "base/values.h" 16 #include "chrome/browser/content_settings/content_settings_origin_identifier_value_map.h" 17 #include "chrome/browser/content_settings/content_settings_rule.h" 18 #include "chrome/browser/content_settings/content_settings_utils.h" 19 #include "chrome/browser/extensions/api/content_settings/content_settings_api_constants.h" 20 #include "chrome/browser/extensions/api/content_settings/content_settings_helpers.h" 21 #include "content/public/browser/browser_thread.h" 22 23 using content::BrowserThread; 24 using content_settings::ConcatenationIterator; 25 using content_settings::Rule; 26 using content_settings::RuleIterator; 27 using content_settings::OriginIdentifierValueMap; 28 using content_settings::ResourceIdentifier; 29 using content_settings::ValueToContentSetting; 30 31 namespace extensions { 32 33 namespace helpers = content_settings_helpers; 34 namespace keys = content_settings_api_constants; 35 36 struct ContentSettingsStore::ExtensionEntry { 37 // Extension id 38 std::string id; 39 // Whether extension is enabled in the profile. 40 bool enabled; 41 // Content settings. 42 OriginIdentifierValueMap settings; 43 // Persistent incognito content settings. 44 OriginIdentifierValueMap incognito_persistent_settings; 45 // Session-only incognito content settings. 46 OriginIdentifierValueMap incognito_session_only_settings; 47 }; 48 49 ContentSettingsStore::ContentSettingsStore() { 50 DCHECK(OnCorrectThread()); 51 } 52 53 ContentSettingsStore::~ContentSettingsStore() { 54 STLDeleteValues(&entries_); 55 } 56 57 RuleIterator* ContentSettingsStore::GetRuleIterator( 58 ContentSettingsType type, 59 const content_settings::ResourceIdentifier& identifier, 60 bool incognito) const { 61 ScopedVector<RuleIterator> iterators; 62 // Iterate the extensions based on install time (last installed extensions 63 // first). 64 ExtensionEntryMap::const_reverse_iterator entry; 65 66 // The individual |RuleIterators| shouldn't lock; pass |lock_| to the 67 // |ConcatenationIterator| in a locked state. 68 scoped_ptr<base::AutoLock> auto_lock(new base::AutoLock(lock_)); 69 70 for (entry = entries_.rbegin(); entry != entries_.rend(); ++entry) { 71 if (!entry->second->enabled) 72 continue; 73 74 if (incognito) { 75 iterators.push_back( 76 entry->second->incognito_session_only_settings.GetRuleIterator( 77 type, 78 identifier, 79 NULL)); 80 iterators.push_back( 81 entry->second->incognito_persistent_settings.GetRuleIterator( 82 type, 83 identifier, 84 NULL)); 85 } else { 86 iterators.push_back( 87 entry->second->settings.GetRuleIterator(type, identifier, NULL)); 88 } 89 } 90 return new ConcatenationIterator(&iterators, auto_lock.release()); 91 } 92 93 void ContentSettingsStore::SetExtensionContentSetting( 94 const std::string& ext_id, 95 const ContentSettingsPattern& primary_pattern, 96 const ContentSettingsPattern& secondary_pattern, 97 ContentSettingsType type, 98 const content_settings::ResourceIdentifier& identifier, 99 ContentSetting setting, 100 ExtensionPrefsScope scope) { 101 { 102 base::AutoLock lock(lock_); 103 OriginIdentifierValueMap* map = GetValueMap(ext_id, scope); 104 if (setting == CONTENT_SETTING_DEFAULT) { 105 map->DeleteValue(primary_pattern, secondary_pattern, type, identifier); 106 } else { 107 map->SetValue(primary_pattern, secondary_pattern, type, identifier, 108 new base::FundamentalValue(setting)); 109 } 110 } 111 112 // Send notification that content settings changed. 113 // TODO(markusheintz): Notifications should only be sent if the set content 114 // setting is effective and not hidden by another setting of another 115 // extension installed more recently. 116 NotifyOfContentSettingChanged(ext_id, 117 scope != kExtensionPrefsScopeRegular); 118 } 119 120 void ContentSettingsStore::RegisterExtension( 121 const std::string& ext_id, 122 const base::Time& install_time, 123 bool is_enabled) { 124 base::AutoLock lock(lock_); 125 ExtensionEntryMap::iterator i = FindEntry(ext_id); 126 ExtensionEntry* entry; 127 if (i != entries_.end()) { 128 entry = i->second; 129 } else { 130 entry = new ExtensionEntry; 131 entries_.insert(std::make_pair(install_time, entry)); 132 } 133 134 entry->id = ext_id; 135 entry->enabled = is_enabled; 136 } 137 138 void ContentSettingsStore::UnregisterExtension( 139 const std::string& ext_id) { 140 bool notify = false; 141 bool notify_incognito = false; 142 { 143 base::AutoLock lock(lock_); 144 ExtensionEntryMap::iterator i = FindEntry(ext_id); 145 if (i == entries_.end()) 146 return; 147 notify = !i->second->settings.empty(); 148 notify_incognito = !i->second->incognito_persistent_settings.empty() || 149 !i->second->incognito_session_only_settings.empty(); 150 151 delete i->second; 152 entries_.erase(i); 153 } 154 if (notify) 155 NotifyOfContentSettingChanged(ext_id, false); 156 if (notify_incognito) 157 NotifyOfContentSettingChanged(ext_id, true); 158 } 159 160 void ContentSettingsStore::SetExtensionState( 161 const std::string& ext_id, bool is_enabled) { 162 bool notify = false; 163 bool notify_incognito = false; 164 { 165 base::AutoLock lock(lock_); 166 ExtensionEntryMap::const_iterator i = FindEntry(ext_id); 167 if (i == entries_.end()) 168 return; 169 notify = !i->second->settings.empty(); 170 notify_incognito = !i->second->incognito_persistent_settings.empty() || 171 !i->second->incognito_session_only_settings.empty(); 172 173 i->second->enabled = is_enabled; 174 } 175 if (notify) 176 NotifyOfContentSettingChanged(ext_id, false); 177 if (notify_incognito) 178 NotifyOfContentSettingChanged(ext_id, true); 179 } 180 181 OriginIdentifierValueMap* ContentSettingsStore::GetValueMap( 182 const std::string& ext_id, 183 ExtensionPrefsScope scope) { 184 ExtensionEntryMap::const_iterator i = FindEntry(ext_id); 185 if (i != entries_.end()) { 186 switch (scope) { 187 case kExtensionPrefsScopeRegular: 188 return &(i->second->settings); 189 case kExtensionPrefsScopeRegularOnly: 190 // TODO(bauerb): Implement regular-only content settings. 191 NOTREACHED(); 192 return NULL; 193 case kExtensionPrefsScopeIncognitoPersistent: 194 return &(i->second->incognito_persistent_settings); 195 case kExtensionPrefsScopeIncognitoSessionOnly: 196 return &(i->second->incognito_session_only_settings); 197 } 198 } 199 return NULL; 200 } 201 202 const OriginIdentifierValueMap* ContentSettingsStore::GetValueMap( 203 const std::string& ext_id, 204 ExtensionPrefsScope scope) const { 205 ExtensionEntryMap::const_iterator i = FindEntry(ext_id); 206 if (i == entries_.end()) 207 return NULL; 208 209 switch (scope) { 210 case kExtensionPrefsScopeRegular: 211 return &(i->second->settings); 212 case kExtensionPrefsScopeRegularOnly: 213 // TODO(bauerb): Implement regular-only content settings. 214 NOTREACHED(); 215 return NULL; 216 case kExtensionPrefsScopeIncognitoPersistent: 217 return &(i->second->incognito_persistent_settings); 218 case kExtensionPrefsScopeIncognitoSessionOnly: 219 return &(i->second->incognito_session_only_settings); 220 } 221 222 NOTREACHED(); 223 return NULL; 224 } 225 226 void ContentSettingsStore::ClearContentSettingsForExtension( 227 const std::string& ext_id, 228 ExtensionPrefsScope scope) { 229 bool notify = false; 230 { 231 base::AutoLock lock(lock_); 232 OriginIdentifierValueMap* map = GetValueMap(ext_id, scope); 233 if (!map) { 234 // Fail gracefully in Release builds. 235 NOTREACHED(); 236 return; 237 } 238 notify = !map->empty(); 239 map->clear(); 240 } 241 if (notify) { 242 NotifyOfContentSettingChanged(ext_id, scope != kExtensionPrefsScopeRegular); 243 } 244 } 245 246 base::ListValue* ContentSettingsStore::GetSettingsForExtension( 247 const std::string& extension_id, 248 ExtensionPrefsScope scope) const { 249 base::AutoLock lock(lock_); 250 const OriginIdentifierValueMap* map = GetValueMap(extension_id, scope); 251 if (!map) 252 return NULL; 253 base::ListValue* settings = new base::ListValue(); 254 OriginIdentifierValueMap::EntryMap::const_iterator it; 255 for (it = map->begin(); it != map->end(); ++it) { 256 scoped_ptr<RuleIterator> rule_iterator( 257 map->GetRuleIterator(it->first.content_type, 258 it->first.resource_identifier, 259 NULL)); // We already hold the lock. 260 while (rule_iterator->HasNext()) { 261 const Rule& rule = rule_iterator->Next(); 262 base::DictionaryValue* setting_dict = new base::DictionaryValue(); 263 setting_dict->SetString(keys::kPrimaryPatternKey, 264 rule.primary_pattern.ToString()); 265 setting_dict->SetString(keys::kSecondaryPatternKey, 266 rule.secondary_pattern.ToString()); 267 setting_dict->SetString( 268 keys::kContentSettingsTypeKey, 269 helpers::ContentSettingsTypeToString(it->first.content_type)); 270 setting_dict->SetString(keys::kResourceIdentifierKey, 271 it->first.resource_identifier); 272 ContentSetting content_setting = ValueToContentSetting(rule.value.get()); 273 DCHECK_NE(CONTENT_SETTING_DEFAULT, content_setting); 274 setting_dict->SetString( 275 keys::kContentSettingKey, 276 helpers::ContentSettingToString(content_setting)); 277 settings->Append(setting_dict); 278 } 279 } 280 return settings; 281 } 282 283 void ContentSettingsStore::SetExtensionContentSettingFromList( 284 const std::string& extension_id, 285 const base::ListValue* list, 286 ExtensionPrefsScope scope) { 287 for (base::ListValue::const_iterator it = list->begin(); 288 it != list->end(); ++it) { 289 if ((*it)->GetType() != Value::TYPE_DICTIONARY) { 290 NOTREACHED(); 291 continue; 292 } 293 base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(*it); 294 std::string primary_pattern_str; 295 dict->GetString(keys::kPrimaryPatternKey, &primary_pattern_str); 296 ContentSettingsPattern primary_pattern = 297 ContentSettingsPattern::FromString(primary_pattern_str); 298 DCHECK(primary_pattern.IsValid()); 299 300 std::string secondary_pattern_str; 301 dict->GetString(keys::kSecondaryPatternKey, &secondary_pattern_str); 302 ContentSettingsPattern secondary_pattern = 303 ContentSettingsPattern::FromString(secondary_pattern_str); 304 DCHECK(secondary_pattern.IsValid()); 305 306 std::string content_settings_type_str; 307 dict->GetString(keys::kContentSettingsTypeKey, &content_settings_type_str); 308 ContentSettingsType content_settings_type = 309 helpers::StringToContentSettingsType(content_settings_type_str); 310 DCHECK_NE(CONTENT_SETTINGS_TYPE_DEFAULT, content_settings_type); 311 312 std::string resource_identifier; 313 dict->GetString(keys::kResourceIdentifierKey, &resource_identifier); 314 315 std::string content_setting_string; 316 dict->GetString(keys::kContentSettingKey, &content_setting_string); 317 ContentSetting setting = CONTENT_SETTING_DEFAULT; 318 bool result = 319 helpers::StringToContentSetting(content_setting_string, &setting); 320 DCHECK(result); 321 322 SetExtensionContentSetting(extension_id, 323 primary_pattern, 324 secondary_pattern, 325 content_settings_type, 326 resource_identifier, 327 setting, 328 scope); 329 } 330 } 331 332 void ContentSettingsStore::AddObserver(Observer* observer) { 333 DCHECK(OnCorrectThread()); 334 observers_.AddObserver(observer); 335 } 336 337 void ContentSettingsStore::RemoveObserver(Observer* observer) { 338 DCHECK(OnCorrectThread()); 339 observers_.RemoveObserver(observer); 340 } 341 342 void ContentSettingsStore::NotifyOfContentSettingChanged( 343 const std::string& extension_id, 344 bool incognito) { 345 FOR_EACH_OBSERVER( 346 ContentSettingsStore::Observer, 347 observers_, 348 OnContentSettingChanged(extension_id, incognito)); 349 } 350 351 bool ContentSettingsStore::OnCorrectThread() { 352 // If there is no UI thread, we're most likely in a unit test. 353 return !BrowserThread::IsThreadInitialized(BrowserThread::UI) || 354 BrowserThread::CurrentlyOn(BrowserThread::UI); 355 } 356 357 ContentSettingsStore::ExtensionEntryMap::iterator 358 ContentSettingsStore::FindEntry(const std::string& ext_id) { 359 ExtensionEntryMap::iterator i; 360 for (i = entries_.begin(); i != entries_.end(); ++i) { 361 if (i->second->id == ext_id) 362 return i; 363 } 364 return entries_.end(); 365 } 366 367 ContentSettingsStore::ExtensionEntryMap::const_iterator 368 ContentSettingsStore::FindEntry(const std::string& ext_id) const { 369 ExtensionEntryMap::const_iterator i; 370 for (i = entries_.begin(); i != entries_.end(); ++i) { 371 if (i->second->id == ext_id) 372 return i; 373 } 374 return entries_.end(); 375 } 376 377 } // namespace extensions 378