Home | History | Annotate | Download | only in content_settings
      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() != base::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