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                     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