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
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
ContentSettingsStore()49 ContentSettingsStore::ContentSettingsStore() {
50 DCHECK(OnCorrectThread());
51 }
52
~ContentSettingsStore()53 ContentSettingsStore::~ContentSettingsStore() {
54 STLDeleteValues(&entries_);
55 }
56
GetRuleIterator(ContentSettingsType type,const content_settings::ResourceIdentifier & identifier,bool incognito) const57 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
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)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
RegisterExtension(const std::string & ext_id,const base::Time & install_time,bool is_enabled)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
UnregisterExtension(const std::string & ext_id)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
SetExtensionState(const std::string & ext_id,bool is_enabled)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
GetValueMap(const std::string & ext_id,ExtensionPrefsScope scope)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
GetValueMap(const std::string & ext_id,ExtensionPrefsScope scope) const202 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
ClearContentSettingsForExtension(const std::string & ext_id,ExtensionPrefsScope scope)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
GetSettingsForExtension(const std::string & extension_id,ExtensionPrefsScope scope) const246 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
SetExtensionContentSettingFromList(const std::string & extension_id,const base::ListValue * list,ExtensionPrefsScope scope)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
AddObserver(Observer * observer)332 void ContentSettingsStore::AddObserver(Observer* observer) {
333 DCHECK(OnCorrectThread());
334 observers_.AddObserver(observer);
335 }
336
RemoveObserver(Observer * observer)337 void ContentSettingsStore::RemoveObserver(Observer* observer) {
338 DCHECK(OnCorrectThread());
339 observers_.RemoveObserver(observer);
340 }
341
NotifyOfContentSettingChanged(const std::string & extension_id,bool incognito)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
OnCorrectThread()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
FindEntry(const std::string & ext_id)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
FindEntry(const std::string & ext_id) const368 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