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 <algorithm>
8 #include <memory>
9 #include <set>
10 #include <utility>
11
12 #include "base/debug/alias.h"
13 #include "base/feature_list.h"
14 #include "base/logging.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/strings/string_util.h"
17 #include "base/values.h"
18 #include "chrome/browser/extensions/api/content_settings/content_settings_api_constants.h"
19 #include "chrome/browser/extensions/api/content_settings/content_settings_helpers.h"
20 #include "components/content_settings/core/browser/content_settings_info.h"
21 #include "components/content_settings/core/browser/content_settings_origin_identifier_value_map.h"
22 #include "components/content_settings/core/browser/content_settings_registry.h"
23 #include "components/content_settings/core/browser/content_settings_rule.h"
24 #include "components/content_settings/core/browser/content_settings_utils.h"
25 #include "components/content_settings/core/browser/website_settings_info.h"
26 #include "components/content_settings/core/common/content_settings_utils.h"
27 #include "components/permissions/features.h"
28 #include "content/public/browser/browser_thread.h"
29
30 using content::BrowserThread;
31 using content_settings::ConcatenationIterator;
32 using content_settings::Rule;
33 using content_settings::RuleIterator;
34 using content_settings::OriginIdentifierValueMap;
35 using content_settings::ResourceIdentifier;
36
37 namespace extensions {
38
39 struct ContentSettingsStore::ExtensionEntry {
40 // Extension id.
41 std::string id;
42 // Installation time.
43 base::Time install_time;
44 // Whether extension is enabled in the profile.
45 bool enabled;
46 // Content settings.
47 OriginIdentifierValueMap settings;
48 // Persistent incognito content settings.
49 OriginIdentifierValueMap incognito_persistent_settings;
50 // Session-only incognito content settings.
51 OriginIdentifierValueMap incognito_session_only_settings;
52 };
53
ContentSettingsStore()54 ContentSettingsStore::ContentSettingsStore() {
55 DCHECK(OnCorrectThread());
56 }
57
~ContentSettingsStore()58 ContentSettingsStore::~ContentSettingsStore() {
59 }
60
GetRuleIterator(ContentSettingsType type,const content_settings::ResourceIdentifier & identifier,bool incognito) const61 std::unique_ptr<RuleIterator> ContentSettingsStore::GetRuleIterator(
62 ContentSettingsType type,
63 const content_settings::ResourceIdentifier& identifier,
64 bool incognito) const {
65 std::vector<std::unique_ptr<RuleIterator>> iterators;
66
67 // The individual |RuleIterators| shouldn't lock; pass |lock_| to the
68 // |ConcatenationIterator| in a locked state.
69 std::unique_ptr<base::AutoLock> auto_lock(new base::AutoLock(lock_));
70
71 // Iterate the extensions based on install time (most-recently installed
72 // items first).
73 for (const auto& entry : entries_) {
74 if (!entry->enabled)
75 continue;
76
77 std::unique_ptr<RuleIterator> rule_it;
78 if (incognito) {
79 rule_it = entry->incognito_session_only_settings.GetRuleIterator(
80 type, identifier, nullptr);
81 if (rule_it)
82 iterators.push_back(std::move(rule_it));
83 rule_it = entry->incognito_persistent_settings.GetRuleIterator(
84 type, identifier, nullptr);
85 if (rule_it)
86 iterators.push_back(std::move(rule_it));
87 } else {
88 rule_it = entry->settings.GetRuleIterator(type, identifier, nullptr);
89 if (rule_it)
90 iterators.push_back(std::move(rule_it));
91 }
92 }
93 if (iterators.empty())
94 return nullptr;
95
96 return std::make_unique<ConcatenationIterator>(std::move(iterators),
97 auto_lock.release());
98 }
99
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)100 void ContentSettingsStore::SetExtensionContentSetting(
101 const std::string& ext_id,
102 const ContentSettingsPattern& primary_pattern,
103 const ContentSettingsPattern& secondary_pattern,
104 ContentSettingsType type,
105 const content_settings::ResourceIdentifier& identifier,
106 ContentSetting setting,
107 ExtensionPrefsScope scope) {
108 {
109 base::AutoLock lock(lock_);
110 OriginIdentifierValueMap* map = GetValueMap(ext_id, scope);
111 if (setting == CONTENT_SETTING_DEFAULT) {
112 map->DeleteValue(primary_pattern, secondary_pattern, type, identifier);
113 } else {
114 // Do not set a timestamp for extension settings.
115 map->SetValue(primary_pattern, secondary_pattern, type, identifier,
116 base::Time(), base::Value(setting));
117 }
118 }
119
120 // Send notification that content settings changed. (Note: This is responsible
121 // for updating the pref store, so cannot be skipped even if the setting would
122 // be masked by another extension.)
123 NotifyOfContentSettingChanged(ext_id,
124 scope != kExtensionPrefsScopeRegular);
125 }
126
RegisterExtension(const std::string & ext_id,const base::Time & install_time,bool is_enabled)127 void ContentSettingsStore::RegisterExtension(
128 const std::string& ext_id,
129 const base::Time& install_time,
130 bool is_enabled) {
131 base::AutoLock lock(lock_);
132 auto i = FindIterator(ext_id);
133 ExtensionEntry* entry = nullptr;
134 if (i != entries_.end()) {
135 entry = i->get();
136 } else {
137 entry = new ExtensionEntry;
138 entry->install_time = install_time;
139
140 // Insert in reverse-chronological order to maintain the invariant.
141 auto unique_entry = base::WrapUnique(entry);
142 auto location =
143 std::upper_bound(entries_.begin(), entries_.end(), unique_entry,
144 [](const std::unique_ptr<ExtensionEntry>& a,
145 const std::unique_ptr<ExtensionEntry>& b) {
146 return a->install_time > b->install_time;
147 });
148 entries_.insert(location, std::move(unique_entry));
149 }
150
151 entry->id = ext_id;
152 entry->enabled = is_enabled;
153 }
154
UnregisterExtension(const std::string & ext_id)155 void ContentSettingsStore::UnregisterExtension(
156 const std::string& ext_id) {
157 bool notify = false;
158 bool notify_incognito = false;
159 {
160 base::AutoLock lock(lock_);
161 auto i = FindIterator(ext_id);
162 if (i == entries_.end())
163 return;
164 notify = !(*i)->settings.empty();
165 notify_incognito = !(*i)->incognito_persistent_settings.empty() ||
166 !(*i)->incognito_session_only_settings.empty();
167
168 entries_.erase(i);
169 }
170 if (notify)
171 NotifyOfContentSettingChanged(ext_id, false);
172 if (notify_incognito)
173 NotifyOfContentSettingChanged(ext_id, true);
174 }
175
SetExtensionState(const std::string & ext_id,bool is_enabled)176 void ContentSettingsStore::SetExtensionState(
177 const std::string& ext_id, bool is_enabled) {
178 bool notify = false;
179 bool notify_incognito = false;
180 {
181 base::AutoLock lock(lock_);
182 ExtensionEntry* entry = FindEntry(ext_id);
183 if (!entry)
184 return;
185
186 notify = !entry->settings.empty();
187 notify_incognito = !entry->incognito_persistent_settings.empty() ||
188 !entry->incognito_session_only_settings.empty();
189
190 entry->enabled = is_enabled;
191 }
192 if (notify)
193 NotifyOfContentSettingChanged(ext_id, false);
194 if (notify_incognito)
195 NotifyOfContentSettingChanged(ext_id, true);
196 }
197
GetValueMap(const std::string & ext_id,ExtensionPrefsScope scope)198 OriginIdentifierValueMap* ContentSettingsStore::GetValueMap(
199 const std::string& ext_id,
200 ExtensionPrefsScope scope) {
201 const OriginIdentifierValueMap* result =
202 static_cast<const ContentSettingsStore*>(this)->GetValueMap(ext_id,
203 scope);
204 return const_cast<OriginIdentifierValueMap*>(result);
205 }
206
GetValueMap(const std::string & ext_id,ExtensionPrefsScope scope) const207 const OriginIdentifierValueMap* ContentSettingsStore::GetValueMap(
208 const std::string& ext_id,
209 ExtensionPrefsScope scope) const {
210 ExtensionEntry* entry = FindEntry(ext_id);
211 if (!entry)
212 return nullptr;
213
214 switch (scope) {
215 case kExtensionPrefsScopeRegular:
216 return &(entry->settings);
217 case kExtensionPrefsScopeRegularOnly:
218 // TODO(bauerb): Implement regular-only content settings.
219 NOTREACHED();
220 return nullptr;
221 case kExtensionPrefsScopeIncognitoPersistent:
222 return &(entry->incognito_persistent_settings);
223 case kExtensionPrefsScopeIncognitoSessionOnly:
224 return &(entry->incognito_session_only_settings);
225 }
226
227 NOTREACHED();
228 return nullptr;
229 }
230
ClearContentSettingsForExtension(const std::string & ext_id,ExtensionPrefsScope scope)231 void ContentSettingsStore::ClearContentSettingsForExtension(
232 const std::string& ext_id,
233 ExtensionPrefsScope scope) {
234 bool notify = false;
235 {
236 base::AutoLock lock(lock_);
237 OriginIdentifierValueMap* map = GetValueMap(ext_id, scope);
238 DCHECK(map);
239 notify = !map->empty();
240 map->clear();
241 }
242 if (notify) {
243 NotifyOfContentSettingChanged(ext_id, scope != kExtensionPrefsScopeRegular);
244 }
245 }
246
ClearContentSettingsForExtensionAndContentType(const std::string & ext_id,ExtensionPrefsScope scope,ContentSettingsType content_type)247 void ContentSettingsStore::ClearContentSettingsForExtensionAndContentType(
248 const std::string& ext_id,
249 ExtensionPrefsScope scope,
250 ContentSettingsType content_type) {
251 bool notify = false;
252 {
253 base::AutoLock lock(lock_);
254 OriginIdentifierValueMap* map = GetValueMap(ext_id, scope);
255 DCHECK(map);
256
257 // Get all of the resource identifiers for this |content_type|.
258 std::set<ResourceIdentifier> resource_identifiers;
259 for (const auto& entry : *map) {
260 if (entry.first.content_type == content_type)
261 resource_identifiers.insert(entry.first.resource_identifier);
262 }
263
264 notify = !resource_identifiers.empty();
265
266 for (const ResourceIdentifier& resource_identifier : resource_identifiers)
267 map->DeleteValues(content_type, resource_identifier);
268 }
269 if (notify) {
270 NotifyOfContentSettingChanged(ext_id, scope != kExtensionPrefsScopeRegular);
271 }
272 }
273
GetSettingsForExtension(const std::string & extension_id,ExtensionPrefsScope scope) const274 std::unique_ptr<base::ListValue> ContentSettingsStore::GetSettingsForExtension(
275 const std::string& extension_id,
276 ExtensionPrefsScope scope) const {
277 base::AutoLock lock(lock_);
278 const OriginIdentifierValueMap* map = GetValueMap(extension_id, scope);
279 if (!map)
280 return nullptr;
281
282 auto settings = std::make_unique<base::ListValue>();
283 for (const auto& it : *map) {
284 const auto& key = it.first;
285 std::unique_ptr<RuleIterator> rule_iterator(
286 map->GetRuleIterator(key.content_type, key.resource_identifier,
287 nullptr)); // We already hold the lock.
288 if (!rule_iterator)
289 continue;
290
291 while (rule_iterator->HasNext()) {
292 const Rule& rule = rule_iterator->Next();
293 std::unique_ptr<base::DictionaryValue> setting_dict(
294 new base::DictionaryValue());
295 setting_dict->SetString(
296 content_settings_api_constants::kPrimaryPatternKey,
297 rule.primary_pattern.ToString());
298 setting_dict->SetString(
299 content_settings_api_constants::kSecondaryPatternKey,
300 rule.secondary_pattern.ToString());
301 setting_dict->SetString(
302 content_settings_api_constants::kContentSettingsTypeKey,
303 content_settings_helpers::ContentSettingsTypeToString(
304 key.content_type));
305 setting_dict->SetString(
306 content_settings_api_constants::kResourceIdentifierKey,
307 key.resource_identifier);
308 ContentSetting content_setting =
309 content_settings::ValueToContentSetting(&rule.value);
310 DCHECK_NE(CONTENT_SETTING_DEFAULT, content_setting);
311
312 std::string setting_string =
313 content_settings::ContentSettingToString(content_setting);
314 DCHECK(!setting_string.empty());
315
316 setting_dict->SetString(
317 content_settings_api_constants::kContentSettingKey, setting_string);
318 settings->Append(std::move(setting_dict));
319 }
320 }
321 return settings;
322 }
323
SetExtensionContentSettingFromList(const std::string & extension_id,const base::ListValue * list,ExtensionPrefsScope scope)324 void ContentSettingsStore::SetExtensionContentSettingFromList(
325 const std::string& extension_id,
326 const base::ListValue* list,
327 ExtensionPrefsScope scope) {
328 for (const auto& value : *list) {
329 const base::DictionaryValue* dict = nullptr;
330 if (!value.GetAsDictionary(&dict)) {
331 NOTREACHED();
332 continue;
333 }
334 std::string primary_pattern_str;
335 dict->GetString(content_settings_api_constants::kPrimaryPatternKey,
336 &primary_pattern_str);
337 ContentSettingsPattern primary_pattern =
338 ContentSettingsPattern::FromString(primary_pattern_str);
339 DCHECK(primary_pattern.IsValid());
340
341 std::string secondary_pattern_str;
342 dict->GetString(content_settings_api_constants::kSecondaryPatternKey,
343 &secondary_pattern_str);
344 ContentSettingsPattern secondary_pattern =
345 ContentSettingsPattern::FromString(secondary_pattern_str);
346 DCHECK(secondary_pattern.IsValid());
347
348 std::string content_settings_type_str;
349 dict->GetString(content_settings_api_constants::kContentSettingsTypeKey,
350 &content_settings_type_str);
351 ContentSettingsType content_settings_type =
352 content_settings_helpers::StringToContentSettingsType(
353 content_settings_type_str);
354 if (content_settings_type == ContentSettingsType::DEFAULT) {
355 // We'll end up with DEFAULT here if the type string isn't recognised.
356 // This could be if it's a string from an old settings type that has been
357 // deleted. DCHECK to make sure this is the case (not some random string).
358 DCHECK(content_settings_type_str == "fullscreen" ||
359 content_settings_type_str == "mouselock");
360
361 // In this case, we just skip over that setting, effectively deleting it
362 // from the in-memory model. This will implicitly delete these old
363 // settings from the pref store when it is written back.
364 continue;
365 }
366
367 const content_settings::ContentSettingsInfo* info =
368 content_settings::ContentSettingsRegistry::GetInstance()->Get(
369 content_settings_type);
370 if (primary_pattern != secondary_pattern &&
371 secondary_pattern != ContentSettingsPattern::Wildcard() &&
372 !info->website_settings_info()->SupportsEmbeddedExceptions() &&
373 base::FeatureList::IsEnabled(
374 permissions::features::kPermissionDelegation)) {
375 // Some types may have had embedded exceptions written even though they
376 // aren't supported. This will implicitly delete these old settings from
377 // the pref store when it is written back.
378 continue;
379 }
380
381 std::string resource_identifier;
382 dict->GetString(content_settings_api_constants::kResourceIdentifierKey,
383 &resource_identifier);
384
385 std::string content_setting_string;
386 dict->GetString(content_settings_api_constants::kContentSettingKey,
387 &content_setting_string);
388 ContentSetting setting;
389 bool result = content_settings::ContentSettingFromString(
390 content_setting_string, &setting);
391 DCHECK(result);
392 // The content settings extensions API does not support setting any content
393 // settings to |CONTENT_SETTING_DEFAULT|.
394 DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
395
396 SetExtensionContentSetting(extension_id,
397 primary_pattern,
398 secondary_pattern,
399 content_settings_type,
400 resource_identifier,
401 setting,
402 scope);
403 }
404 }
405
AddObserver(Observer * observer)406 void ContentSettingsStore::AddObserver(Observer* observer) {
407 DCHECK(OnCorrectThread());
408 observers_.AddObserver(observer);
409 }
410
RemoveObserver(Observer * observer)411 void ContentSettingsStore::RemoveObserver(Observer* observer) {
412 DCHECK(OnCorrectThread());
413 observers_.RemoveObserver(observer);
414 }
415
NotifyOfContentSettingChanged(const std::string & extension_id,bool incognito)416 void ContentSettingsStore::NotifyOfContentSettingChanged(
417 const std::string& extension_id,
418 bool incognito) {
419 for (auto& observer : observers_)
420 observer.OnContentSettingChanged(extension_id, incognito);
421 }
422
OnCorrectThread()423 bool ContentSettingsStore::OnCorrectThread() {
424 // If there is no UI thread, we're most likely in a unit test.
425 return !BrowserThread::IsThreadInitialized(BrowserThread::UI) ||
426 BrowserThread::CurrentlyOn(BrowserThread::UI);
427 }
428
FindEntry(const std::string & ext_id) const429 ContentSettingsStore::ExtensionEntry* ContentSettingsStore::FindEntry(
430 const std::string& ext_id) const {
431 auto iter =
432 std::find_if(entries_.begin(), entries_.end(),
433 [ext_id](const std::unique_ptr<ExtensionEntry>& entry) {
434 return entry->id == ext_id;
435 });
436 return iter == entries_.end() ? nullptr : iter->get();
437 }
438
439 ContentSettingsStore::ExtensionEntries::iterator
FindIterator(const std::string & ext_id)440 ContentSettingsStore::FindIterator(const std::string& ext_id) {
441 return std::find_if(entries_.begin(), entries_.end(),
442 [ext_id](const std::unique_ptr<ExtensionEntry>& entry) {
443 return entry->id == ext_id;
444 });
445 }
446
447 } // namespace extensions
448