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