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_utils.h"
17#include "chrome/browser/extensions/api/content_settings/content_settings_api_constants.h"
18#include "chrome/browser/extensions/api/content_settings/content_settings_helpers.h"
19#include "components/content_settings/core/browser/content_settings_origin_identifier_value_map.h"
20#include "components/content_settings/core/browser/content_settings_rule.h"
21#include "content/public/browser/browser_thread.h"
22
23using content::BrowserThread;
24using content_settings::ConcatenationIterator;
25using content_settings::Rule;
26using content_settings::RuleIterator;
27using content_settings::OriginIdentifierValueMap;
28using content_settings::ResourceIdentifier;
29using content_settings::ValueToContentSetting;
30
31namespace extensions {
32
33namespace helpers = content_settings_helpers;
34namespace keys = content_settings_api_constants;
35
36struct 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
49ContentSettingsStore::ContentSettingsStore() {
50  DCHECK(OnCorrectThread());
51}
52
53ContentSettingsStore::~ContentSettingsStore() {
54  STLDeleteValues(&entries_);
55}
56
57RuleIterator* 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
93void 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
120void 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
138void 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
160void 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
181OriginIdentifierValueMap* 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
202const 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
226void 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
246base::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
283void 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
332void ContentSettingsStore::AddObserver(Observer* observer) {
333  DCHECK(OnCorrectThread());
334  observers_.AddObserver(observer);
335}
336
337void ContentSettingsStore::RemoveObserver(Observer* observer) {
338  DCHECK(OnCorrectThread());
339  observers_.RemoveObserver(observer);
340}
341
342void 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
351bool 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
357ContentSettingsStore::ExtensionEntryMap::iterator
358ContentSettingsStore::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
367ContentSettingsStore::ExtensionEntryMap::const_iterator
368ContentSettingsStore::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