root/chrome/browser/extensions/api/content_settings/content_settings_store.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetRuleIterator
  2. SetExtensionContentSetting
  3. RegisterExtension
  4. UnregisterExtension
  5. SetExtensionState
  6. GetValueMap
  7. GetValueMap
  8. ClearContentSettingsForExtension
  9. GetSettingsForExtension
  10. SetExtensionContentSettingFromList
  11. AddObserver
  12. RemoveObserver
  13. NotifyOfContentSettingChanged
  14. OnCorrectThread
  15. FindEntry
  16. FindEntry

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/api/content_settings/content_settings_store.h"

#include <set>

#include "base/debug/alias.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chrome/browser/content_settings/content_settings_origin_identifier_value_map.h"
#include "chrome/browser/content_settings/content_settings_rule.h"
#include "chrome/browser/content_settings/content_settings_utils.h"
#include "chrome/browser/extensions/api/content_settings/content_settings_api_constants.h"
#include "chrome/browser/extensions/api/content_settings/content_settings_helpers.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;
using content_settings::ConcatenationIterator;
using content_settings::Rule;
using content_settings::RuleIterator;
using content_settings::OriginIdentifierValueMap;
using content_settings::ResourceIdentifier;
using content_settings::ValueToContentSetting;

namespace extensions {

namespace helpers = content_settings_helpers;
namespace keys = content_settings_api_constants;

struct ContentSettingsStore::ExtensionEntry {
  // Extension id
  std::string id;
  // Whether extension is enabled in the profile.
  bool enabled;
  // Content settings.
  OriginIdentifierValueMap settings;
  // Persistent incognito content settings.
  OriginIdentifierValueMap incognito_persistent_settings;
  // Session-only incognito content settings.
  OriginIdentifierValueMap incognito_session_only_settings;
};

ContentSettingsStore::ContentSettingsStore() {
  DCHECK(OnCorrectThread());
}

ContentSettingsStore::~ContentSettingsStore() {
  STLDeleteValues(&entries_);
}

RuleIterator* ContentSettingsStore::GetRuleIterator(
    ContentSettingsType type,
    const content_settings::ResourceIdentifier& identifier,
    bool incognito) const {
  ScopedVector<RuleIterator> iterators;
  // Iterate the extensions based on install time (last installed extensions
  // first).
  ExtensionEntryMap::const_reverse_iterator entry;

  // The individual |RuleIterators| shouldn't lock; pass |lock_| to the
  // |ConcatenationIterator| in a locked state.
  scoped_ptr<base::AutoLock> auto_lock(new base::AutoLock(lock_));

  for (entry = entries_.rbegin(); entry != entries_.rend(); ++entry) {
    if (!entry->second->enabled)
      continue;

    if (incognito) {
      iterators.push_back(
          entry->second->incognito_session_only_settings.GetRuleIterator(
              type,
              identifier,
              NULL));
      iterators.push_back(
          entry->second->incognito_persistent_settings.GetRuleIterator(
              type,
              identifier,
              NULL));
    } else {
      iterators.push_back(
          entry->second->settings.GetRuleIterator(type, identifier, NULL));
    }
  }
  return new ConcatenationIterator(&iterators, auto_lock.release());
}

void ContentSettingsStore::SetExtensionContentSetting(
    const std::string& ext_id,
    const ContentSettingsPattern& primary_pattern,
    const ContentSettingsPattern& secondary_pattern,
    ContentSettingsType type,
    const content_settings::ResourceIdentifier& identifier,
    ContentSetting setting,
    ExtensionPrefsScope scope) {
  {
    base::AutoLock lock(lock_);
    OriginIdentifierValueMap* map = GetValueMap(ext_id, scope);
    if (setting == CONTENT_SETTING_DEFAULT) {
      map->DeleteValue(primary_pattern, secondary_pattern, type, identifier);
    } else {
      map->SetValue(primary_pattern, secondary_pattern, type, identifier,
                    new base::FundamentalValue(setting));
    }
  }

  // Send notification that content settings changed.
  // TODO(markusheintz): Notifications should only be sent if the set content
  // setting is effective and not hidden by another setting of another
  // extension installed more recently.
  NotifyOfContentSettingChanged(ext_id,
                                scope != kExtensionPrefsScopeRegular);
}

void ContentSettingsStore::RegisterExtension(
    const std::string& ext_id,
    const base::Time& install_time,
    bool is_enabled) {
  base::AutoLock lock(lock_);
  ExtensionEntryMap::iterator i = FindEntry(ext_id);
  ExtensionEntry* entry;
  if (i != entries_.end()) {
    entry = i->second;
  } else {
    entry = new ExtensionEntry;
    entries_.insert(std::make_pair(install_time, entry));
  }

  entry->id = ext_id;
  entry->enabled = is_enabled;
}

void ContentSettingsStore::UnregisterExtension(
    const std::string& ext_id) {
  bool notify = false;
  bool notify_incognito = false;
  {
    base::AutoLock lock(lock_);
    ExtensionEntryMap::iterator i = FindEntry(ext_id);
    if (i == entries_.end())
      return;
    notify = !i->second->settings.empty();
    notify_incognito = !i->second->incognito_persistent_settings.empty() ||
                       !i->second->incognito_session_only_settings.empty();

    delete i->second;
    entries_.erase(i);
  }
  if (notify)
    NotifyOfContentSettingChanged(ext_id, false);
  if (notify_incognito)
    NotifyOfContentSettingChanged(ext_id, true);
}

void ContentSettingsStore::SetExtensionState(
    const std::string& ext_id, bool is_enabled) {
  bool notify = false;
  bool notify_incognito = false;
  {
    base::AutoLock lock(lock_);
    ExtensionEntryMap::const_iterator i = FindEntry(ext_id);
    if (i == entries_.end())
      return;
    notify = !i->second->settings.empty();
    notify_incognito = !i->second->incognito_persistent_settings.empty() ||
                       !i->second->incognito_session_only_settings.empty();

    i->second->enabled = is_enabled;
  }
  if (notify)
    NotifyOfContentSettingChanged(ext_id, false);
  if (notify_incognito)
    NotifyOfContentSettingChanged(ext_id, true);
}

OriginIdentifierValueMap* ContentSettingsStore::GetValueMap(
    const std::string& ext_id,
    ExtensionPrefsScope scope) {
  ExtensionEntryMap::const_iterator i = FindEntry(ext_id);
  if (i != entries_.end()) {
    switch (scope) {
      case kExtensionPrefsScopeRegular:
        return &(i->second->settings);
      case kExtensionPrefsScopeRegularOnly:
        // TODO(bauerb): Implement regular-only content settings.
        NOTREACHED();
        return NULL;
      case kExtensionPrefsScopeIncognitoPersistent:
        return &(i->second->incognito_persistent_settings);
      case kExtensionPrefsScopeIncognitoSessionOnly:
        return &(i->second->incognito_session_only_settings);
    }
  }
  return NULL;
}

const OriginIdentifierValueMap* ContentSettingsStore::GetValueMap(
    const std::string& ext_id,
    ExtensionPrefsScope scope) const {
  ExtensionEntryMap::const_iterator i = FindEntry(ext_id);
  if (i == entries_.end())
    return NULL;

  switch (scope) {
    case kExtensionPrefsScopeRegular:
      return &(i->second->settings);
    case kExtensionPrefsScopeRegularOnly:
      // TODO(bauerb): Implement regular-only content settings.
      NOTREACHED();
      return NULL;
    case kExtensionPrefsScopeIncognitoPersistent:
      return &(i->second->incognito_persistent_settings);
    case kExtensionPrefsScopeIncognitoSessionOnly:
      return &(i->second->incognito_session_only_settings);
  }

  NOTREACHED();
  return NULL;
}

void ContentSettingsStore::ClearContentSettingsForExtension(
    const std::string& ext_id,
    ExtensionPrefsScope scope) {
  bool notify = false;
  {
    base::AutoLock lock(lock_);
    OriginIdentifierValueMap* map = GetValueMap(ext_id, scope);
    if (!map) {
      // Fail gracefully in Release builds.
      NOTREACHED();
      return;
    }
    notify = !map->empty();
    map->clear();
  }
  if (notify) {
    NotifyOfContentSettingChanged(ext_id, scope != kExtensionPrefsScopeRegular);
  }
}

base::ListValue* ContentSettingsStore::GetSettingsForExtension(
    const std::string& extension_id,
    ExtensionPrefsScope scope) const {
  base::AutoLock lock(lock_);
  const OriginIdentifierValueMap* map = GetValueMap(extension_id, scope);
  if (!map)
    return NULL;
  base::ListValue* settings = new base::ListValue();
  OriginIdentifierValueMap::EntryMap::const_iterator it;
  for (it = map->begin(); it != map->end(); ++it) {
    scoped_ptr<RuleIterator> rule_iterator(
        map->GetRuleIterator(it->first.content_type,
                             it->first.resource_identifier,
                             NULL));  // We already hold the lock.
    while (rule_iterator->HasNext()) {
      const Rule& rule = rule_iterator->Next();
      base::DictionaryValue* setting_dict = new base::DictionaryValue();
      setting_dict->SetString(keys::kPrimaryPatternKey,
                              rule.primary_pattern.ToString());
      setting_dict->SetString(keys::kSecondaryPatternKey,
                              rule.secondary_pattern.ToString());
      setting_dict->SetString(
          keys::kContentSettingsTypeKey,
          helpers::ContentSettingsTypeToString(it->first.content_type));
      setting_dict->SetString(keys::kResourceIdentifierKey,
                              it->first.resource_identifier);
      ContentSetting content_setting = ValueToContentSetting(rule.value.get());
      DCHECK_NE(CONTENT_SETTING_DEFAULT, content_setting);
      setting_dict->SetString(
          keys::kContentSettingKey,
          helpers::ContentSettingToString(content_setting));
      settings->Append(setting_dict);
    }
  }
  return settings;
}

void ContentSettingsStore::SetExtensionContentSettingFromList(
    const std::string& extension_id,
    const base::ListValue* list,
    ExtensionPrefsScope scope) {
  for (base::ListValue::const_iterator it = list->begin();
       it != list->end(); ++it) {
    if ((*it)->GetType() != base::Value::TYPE_DICTIONARY) {
      NOTREACHED();
      continue;
    }
    base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(*it);
    std::string primary_pattern_str;
    dict->GetString(keys::kPrimaryPatternKey, &primary_pattern_str);
    ContentSettingsPattern primary_pattern =
        ContentSettingsPattern::FromString(primary_pattern_str);
    DCHECK(primary_pattern.IsValid());

    std::string secondary_pattern_str;
    dict->GetString(keys::kSecondaryPatternKey, &secondary_pattern_str);
    ContentSettingsPattern secondary_pattern =
        ContentSettingsPattern::FromString(secondary_pattern_str);
    DCHECK(secondary_pattern.IsValid());

    std::string content_settings_type_str;
    dict->GetString(keys::kContentSettingsTypeKey, &content_settings_type_str);
    ContentSettingsType content_settings_type =
        helpers::StringToContentSettingsType(content_settings_type_str);
    DCHECK_NE(CONTENT_SETTINGS_TYPE_DEFAULT, content_settings_type);

    std::string resource_identifier;
    dict->GetString(keys::kResourceIdentifierKey, &resource_identifier);

    std::string content_setting_string;
    dict->GetString(keys::kContentSettingKey, &content_setting_string);
    ContentSetting setting = CONTENT_SETTING_DEFAULT;
    bool result =
        helpers::StringToContentSetting(content_setting_string, &setting);
    DCHECK(result);

    SetExtensionContentSetting(extension_id,
                               primary_pattern,
                               secondary_pattern,
                               content_settings_type,
                               resource_identifier,
                               setting,
                               scope);
  }
}

void ContentSettingsStore::AddObserver(Observer* observer) {
  DCHECK(OnCorrectThread());
  observers_.AddObserver(observer);
}

void ContentSettingsStore::RemoveObserver(Observer* observer) {
  DCHECK(OnCorrectThread());
  observers_.RemoveObserver(observer);
}

void ContentSettingsStore::NotifyOfContentSettingChanged(
    const std::string& extension_id,
    bool incognito) {
  FOR_EACH_OBSERVER(
      ContentSettingsStore::Observer,
      observers_,
      OnContentSettingChanged(extension_id, incognito));
}

bool ContentSettingsStore::OnCorrectThread() {
  // If there is no UI thread, we're most likely in a unit test.
  return !BrowserThread::IsThreadInitialized(BrowserThread::UI) ||
         BrowserThread::CurrentlyOn(BrowserThread::UI);
}

ContentSettingsStore::ExtensionEntryMap::iterator
ContentSettingsStore::FindEntry(const std::string& ext_id) {
  ExtensionEntryMap::iterator i;
  for (i = entries_.begin(); i != entries_.end(); ++i) {
    if (i->second->id == ext_id)
      return i;
  }
  return entries_.end();
}

ContentSettingsStore::ExtensionEntryMap::const_iterator
ContentSettingsStore::FindEntry(const std::string& ext_id) const {
  ExtensionEntryMap::const_iterator i;
  for (i = entries_.begin(); i != entries_.end(); ++i) {
    if (i->second->id == ext_id)
      return i;
  }
  return entries_.end();
}

}  // namespace extensions

/* [<][>][^][v][top][bottom][index][help] */