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 base::Value::CreateIntegerValue(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 if (i != entries_.end()) { 127 delete i->second; 128 entries_.erase(i); 129 } 130 131 ExtensionEntry* entry = new ExtensionEntry; 132 entry->id = ext_id; 133 entry->enabled = is_enabled; 134 entries_.insert(std::make_pair(install_time, entry)); 135 } 136 137 void ContentSettingsStore::UnregisterExtension( 138 const std::string& ext_id) { 139 bool notify = false; 140 bool notify_incognito = false; 141 { 142 base::AutoLock lock(lock_); 143 ExtensionEntryMap::iterator i = FindEntry(ext_id); 144 if (i == entries_.end()) 145 return; 146 notify = !i->second->settings.empty(); 147 notify_incognito = !i->second->incognito_persistent_settings.empty() || 148 !i->second->incognito_session_only_settings.empty(); 149 150 delete i->second; 151 entries_.erase(i); 152 } 153 if (notify) 154 NotifyOfContentSettingChanged(ext_id, false); 155 if (notify_incognito) 156 NotifyOfContentSettingChanged(ext_id, true); 157 } 158 159 void ContentSettingsStore::SetExtensionState( 160 const std::string& ext_id, bool is_enabled) { 161 bool notify = false; 162 bool notify_incognito = false; 163 { 164 base::AutoLock lock(lock_); 165 ExtensionEntryMap::const_iterator i = FindEntry(ext_id); 166 if (i == entries_.end()) 167 return; 168 notify = !i->second->settings.empty(); 169 notify_incognito = !i->second->incognito_persistent_settings.empty() || 170 !i->second->incognito_session_only_settings.empty(); 171 172 i->second->enabled = is_enabled; 173 } 174 if (notify) 175 NotifyOfContentSettingChanged(ext_id, false); 176 if (notify_incognito) 177 NotifyOfContentSettingChanged(ext_id, true); 178 } 179 180 OriginIdentifierValueMap* ContentSettingsStore::GetValueMap( 181 const std::string& ext_id, 182 ExtensionPrefsScope scope) { 183 ExtensionEntryMap::const_iterator i = FindEntry(ext_id); 184 if (i != entries_.end()) { 185 switch (scope) { 186 case kExtensionPrefsScopeRegular: 187 return &(i->second->settings); 188 case kExtensionPrefsScopeRegularOnly: 189 // TODO(bauerb): Implement regular-only content settings. 190 NOTREACHED(); 191 return NULL; 192 case kExtensionPrefsScopeIncognitoPersistent: 193 return &(i->second->incognito_persistent_settings); 194 case kExtensionPrefsScopeIncognitoSessionOnly: 195 return &(i->second->incognito_session_only_settings); 196 } 197 } 198 return NULL; 199 } 200 201 const OriginIdentifierValueMap* ContentSettingsStore::GetValueMap( 202 const std::string& ext_id, 203 ExtensionPrefsScope scope) const { 204 ExtensionEntryMap::const_iterator i = FindEntry(ext_id); 205 if (i == entries_.end()) 206 return NULL; 207 208 switch (scope) { 209 case kExtensionPrefsScopeRegular: 210 return &(i->second->settings); 211 case kExtensionPrefsScopeRegularOnly: 212 // TODO(bauerb): Implement regular-only content settings. 213 NOTREACHED(); 214 return NULL; 215 case kExtensionPrefsScopeIncognitoPersistent: 216 return &(i->second->incognito_persistent_settings); 217 case kExtensionPrefsScopeIncognitoSessionOnly: 218 return &(i->second->incognito_session_only_settings); 219 } 220 221 NOTREACHED(); 222 return NULL; 223 } 224 225 void ContentSettingsStore::ClearContentSettingsForExtension( 226 const std::string& ext_id, 227 ExtensionPrefsScope scope) { 228 bool notify = false; 229 { 230 base::AutoLock lock(lock_); 231 OriginIdentifierValueMap* map = GetValueMap(ext_id, scope); 232 if (!map) { 233 // Fail gracefully in Release builds. 234 NOTREACHED(); 235 return; 236 } 237 notify = !map->empty(); 238 map->clear(); 239 } 240 if (notify) { 241 NotifyOfContentSettingChanged(ext_id, scope != kExtensionPrefsScopeRegular); 242 } 243 } 244 245 base::ListValue* ContentSettingsStore::GetSettingsForExtension( 246 const std::string& extension_id, 247 ExtensionPrefsScope scope) const { 248 base::AutoLock lock(lock_); 249 const OriginIdentifierValueMap* map = GetValueMap(extension_id, scope); 250 if (!map) 251 return NULL; 252 base::ListValue* settings = new base::ListValue(); 253 OriginIdentifierValueMap::EntryMap::const_iterator it; 254 for (it = map->begin(); it != map->end(); ++it) { 255 scoped_ptr<RuleIterator> rule_iterator( 256 map->GetRuleIterator(it->first.content_type, 257 it->first.resource_identifier, 258 NULL)); // We already hold the lock. 259 while (rule_iterator->HasNext()) { 260 const Rule& rule = rule_iterator->Next(); 261 base::DictionaryValue* setting_dict = new base::DictionaryValue(); 262 setting_dict->SetString(keys::kPrimaryPatternKey, 263 rule.primary_pattern.ToString()); 264 setting_dict->SetString(keys::kSecondaryPatternKey, 265 rule.secondary_pattern.ToString()); 266 setting_dict->SetString( 267 keys::kContentSettingsTypeKey, 268 helpers::ContentSettingsTypeToString(it->first.content_type)); 269 setting_dict->SetString(keys::kResourceIdentifierKey, 270 it->first.resource_identifier); 271 ContentSetting content_setting = ValueToContentSetting(rule.value.get()); 272 DCHECK_NE(CONTENT_SETTING_DEFAULT, content_setting); 273 setting_dict->SetString( 274 keys::kContentSettingKey, 275 helpers::ContentSettingToString(content_setting)); 276 settings->Append(setting_dict); 277 } 278 } 279 return settings; 280 } 281 282 void ContentSettingsStore::SetExtensionContentSettingFromList( 283 const std::string& extension_id, 284 const base::ListValue* list, 285 ExtensionPrefsScope scope) { 286 for (base::ListValue::const_iterator it = list->begin(); 287 it != list->end(); ++it) { 288 if ((*it)->GetType() != Value::TYPE_DICTIONARY) { 289 NOTREACHED(); 290 continue; 291 } 292 base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(*it); 293 std::string primary_pattern_str; 294 dict->GetString(keys::kPrimaryPatternKey, &primary_pattern_str); 295 ContentSettingsPattern primary_pattern = 296 ContentSettingsPattern::FromString(primary_pattern_str); 297 DCHECK(primary_pattern.IsValid()); 298 299 std::string secondary_pattern_str; 300 dict->GetString(keys::kSecondaryPatternKey, &secondary_pattern_str); 301 ContentSettingsPattern secondary_pattern = 302 ContentSettingsPattern::FromString(secondary_pattern_str); 303 DCHECK(secondary_pattern.IsValid()); 304 305 std::string content_settings_type_str; 306 dict->GetString(keys::kContentSettingsTypeKey, &content_settings_type_str); 307 ContentSettingsType content_settings_type = 308 helpers::StringToContentSettingsType(content_settings_type_str); 309 DCHECK_NE(CONTENT_SETTINGS_TYPE_DEFAULT, content_settings_type); 310 311 std::string resource_identifier; 312 dict->GetString(keys::kResourceIdentifierKey, &resource_identifier); 313 314 std::string content_setting_string; 315 dict->GetString(keys::kContentSettingKey, &content_setting_string); 316 ContentSetting setting = CONTENT_SETTING_DEFAULT; 317 bool result = 318 helpers::StringToContentSetting(content_setting_string, &setting); 319 DCHECK(result); 320 321 SetExtensionContentSetting(extension_id, 322 primary_pattern, 323 secondary_pattern, 324 content_settings_type, 325 resource_identifier, 326 setting, 327 scope); 328 } 329 } 330 331 void ContentSettingsStore::AddObserver(Observer* observer) { 332 DCHECK(OnCorrectThread()); 333 observers_.AddObserver(observer); 334 } 335 336 void ContentSettingsStore::RemoveObserver(Observer* observer) { 337 DCHECK(OnCorrectThread()); 338 observers_.RemoveObserver(observer); 339 } 340 341 void ContentSettingsStore::NotifyOfContentSettingChanged( 342 const std::string& extension_id, 343 bool incognito) { 344 FOR_EACH_OBSERVER( 345 ContentSettingsStore::Observer, 346 observers_, 347 OnContentSettingChanged(extension_id, incognito)); 348 } 349 350 bool ContentSettingsStore::OnCorrectThread() { 351 // If there is no UI thread, we're most likely in a unit test. 352 return !BrowserThread::IsThreadInitialized(BrowserThread::UI) || 353 BrowserThread::CurrentlyOn(BrowserThread::UI); 354 } 355 356 ContentSettingsStore::ExtensionEntryMap::iterator 357 ContentSettingsStore::FindEntry(const std::string& ext_id) { 358 ExtensionEntryMap::iterator i; 359 for (i = entries_.begin(); i != entries_.end(); ++i) { 360 if (i->second->id == ext_id) 361 return i; 362 } 363 return entries_.end(); 364 } 365 366 ContentSettingsStore::ExtensionEntryMap::const_iterator 367 ContentSettingsStore::FindEntry(const std::string& ext_id) const { 368 ExtensionEntryMap::const_iterator i; 369 for (i = entries_.begin(); i != entries_.end(); ++i) { 370 if (i->second->id == ext_id) 371 return i; 372 } 373 return entries_.end(); 374 } 375 376 } // namespace extensions 377