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.
5 #include "chrome/browser/extensions/api/declarative/rules_registry.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/time/time.h"
13 #include "base/values.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/extensions/api/declarative/rules_cache_delegate.h"
16 #include "chrome/browser/extensions/extension_info_map.h"
17 #include "chrome/browser/extensions/extension_prefs.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/extensions/extension_system.h"
20 #include "chrome/browser/extensions/extension_util.h"
21 #include "chrome/browser/extensions/state_store.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/common/extensions/extension.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/notification_details.h"
26 #include "content/public/browser/notification_source.h"
30 const char kSuccess[] = "";
31 const char kDuplicateRuleId[] = "Duplicate rule ID: %s";
33 scoped_ptr<base::Value> RulesToValue(
34 const std::vector<linked_ptr<extensions::RulesRegistry::Rule> >& rules) {
35 scoped_ptr<base::ListValue> list(new base::ListValue());
36 for (size_t i = 0; i < rules.size(); ++i)
37 list->Append(rules[i]->ToValue().release());
38 return list.PassAs<base::Value>();
41 std::vector<linked_ptr<extensions::RulesRegistry::Rule> > RulesFromValue(
42 const base::Value* value) {
43 std::vector<linked_ptr<extensions::RulesRegistry::Rule> > rules;
45 const base::ListValue* list = NULL;
46 if (!value || !value->GetAsList(&list))
49 rules.reserve(list->GetSize());
50 for (size_t i = 0; i < list->GetSize(); ++i) {
51 const base::DictionaryValue* dict = NULL;
52 if (!list->GetDictionary(i, &dict))
54 linked_ptr<extensions::RulesRegistry::Rule> rule(
55 new extensions::RulesRegistry::Rule());
56 if (extensions::RulesRegistry::Rule::Populate(*dict, rule.get()))
57 rules.push_back(rule);
63 std::string ToId(int identifier) {
64 return base::StringPrintf("_%d_", identifier);
70 namespace extensions {
74 RulesRegistry::RulesRegistry(
76 const std::string& event_name,
77 content::BrowserThread::ID owner_thread,
78 RulesCacheDelegate* cache_delegate)
80 owner_thread_(owner_thread),
81 event_name_(event_name),
82 weak_ptr_factory_(profile ? this : NULL),
83 process_changed_rules_requested_(profile ? NOT_SCHEDULED_FOR_PROCESSING
85 last_generated_rule_identifier_id_(0) {
87 cache_delegate_ = cache_delegate->GetWeakPtr();
88 cache_delegate->Init(this);
94 std::string RulesRegistry::AddRules(
95 const std::string& extension_id,
96 const std::vector<linked_ptr<Rule> >& rules) {
97 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
99 std::string error = CheckAndFillInOptionalRules(extension_id, rules);
102 FillInOptionalPriorities(rules);
104 // Verify that all rule IDs are new.
105 for (std::vector<linked_ptr<Rule> >::const_iterator i =
106 rules.begin(); i != rules.end(); ++i) {
107 const RuleId& rule_id = *((*i)->id);
108 RulesDictionaryKey key(extension_id, rule_id);
109 if (rules_.find(key) != rules_.end())
110 return base::StringPrintf(kDuplicateRuleId, rule_id.c_str());
113 error = AddRulesImpl(extension_id, rules);
118 // Commit all rules into |rules_| on success.
119 for (std::vector<linked_ptr<Rule> >::const_iterator i =
120 rules.begin(); i != rules.end(); ++i) {
121 const RuleId& rule_id = *((*i)->id);
122 RulesDictionaryKey key(extension_id, rule_id);
126 MaybeProcessChangedRules(extension_id);
130 std::string RulesRegistry::RemoveRules(
131 const std::string& extension_id,
132 const std::vector<std::string>& rule_identifiers) {
133 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
135 std::string error = RemoveRulesImpl(extension_id, rule_identifiers);
140 // Commit removal of rules from |rules_| on success.
141 for (std::vector<std::string>::const_iterator i =
142 rule_identifiers.begin(); i != rule_identifiers.end(); ++i) {
143 RulesDictionaryKey lookup_key(extension_id, *i);
144 rules_.erase(lookup_key);
147 MaybeProcessChangedRules(extension_id);
148 RemoveUsedRuleIdentifiers(extension_id, rule_identifiers);
152 std::string RulesRegistry::RemoveAllRules(const std::string& extension_id) {
153 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
155 std::string error = RemoveAllRulesImpl(extension_id);
160 // Commit removal of rules from |rules_| on success.
161 for (RulesDictionary::const_iterator i = rules_.begin();
162 i != rules_.end();) {
163 const RulesDictionaryKey& key = i->first;
165 if (key.first == extension_id)
169 MaybeProcessChangedRules(extension_id);
170 RemoveAllUsedRuleIdentifiers(extension_id);
174 std::string RulesRegistry::GetRules(
175 const std::string& extension_id,
176 const std::vector<std::string>& rule_identifiers,
177 std::vector<linked_ptr<RulesRegistry::Rule> >* out) {
178 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
180 for (std::vector<std::string>::const_iterator i = rule_identifiers.begin();
181 i != rule_identifiers.end(); ++i) {
182 RulesDictionaryKey lookup_key(extension_id, *i);
183 RulesDictionary::iterator entry = rules_.find(lookup_key);
184 if (entry != rules_.end())
185 out->push_back(entry->second);
190 std::string RulesRegistry::GetAllRules(
191 const std::string& extension_id,
192 std::vector<linked_ptr<RulesRegistry::Rule> >* out) {
193 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
195 for (RulesDictionary::const_iterator i = rules_.begin();
196 i != rules_.end(); ++i) {
197 const RulesDictionaryKey& key = i->first;
198 if (key.first == extension_id)
199 out->push_back(i->second);
204 void RulesRegistry::OnExtensionUnloaded(const std::string& extension_id) {
205 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
206 std::string error = RemoveAllRules(extension_id);
209 used_rule_identifiers_.erase(extension_id);
212 size_t RulesRegistry::GetNumberOfUsedRuleIdentifiersForTesting() const {
213 size_t entry_count = 0u;
214 for (RuleIdentifiersMap::const_iterator extension =
215 used_rule_identifiers_.begin();
216 extension != used_rule_identifiers_.end();
218 // Each extension is counted as 1 just for being there. Otherwise we miss
219 // keys with empty values.
220 entry_count += 1u + extension->second.size();
225 void RulesRegistry::DeserializeAndAddRules(
226 const std::string& extension_id,
227 scoped_ptr<base::Value> rules) {
228 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
230 AddRules(extension_id, RulesFromValue(rules.get()));
233 RulesRegistry::~RulesRegistry() {
236 void RulesRegistry::MarkReady(base::Time storage_init_time) {
237 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
239 if (!storage_init_time.is_null()) {
240 UMA_HISTOGRAM_TIMES("Extensions.DeclarativeRulesStorageInitialization",
241 base::Time::Now() - storage_init_time);
247 void RulesRegistry::ProcessChangedRules(const std::string& extension_id) {
248 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
250 process_changed_rules_requested_ = NOT_SCHEDULED_FOR_PROCESSING;
252 std::vector<linked_ptr<RulesRegistry::Rule> > new_rules;
253 std::string error = GetAllRules(extension_id, &new_rules);
254 DCHECK_EQ(std::string(), error);
255 content::BrowserThread::PostTask(
256 content::BrowserThread::UI,
258 base::Bind(&RulesCacheDelegate::WriteToStorage,
261 base::Passed(RulesToValue(new_rules))));
264 void RulesRegistry::MaybeProcessChangedRules(const std::string& extension_id) {
265 if (process_changed_rules_requested_ != NOT_SCHEDULED_FOR_PROCESSING)
268 process_changed_rules_requested_ = SCHEDULED_FOR_PROCESSING;
269 ready_.Post(FROM_HERE,
270 base::Bind(&RulesRegistry::ProcessChangedRules,
271 weak_ptr_factory_.GetWeakPtr(),
275 bool RulesRegistry::IsUniqueId(const std::string& extension_id,
276 const std::string& rule_id) const {
277 RuleIdentifiersMap::const_iterator identifiers =
278 used_rule_identifiers_.find(extension_id);
279 if (identifiers == used_rule_identifiers_.end())
281 return identifiers->second.find(rule_id) == identifiers->second.end();
284 std::string RulesRegistry::GenerateUniqueId(const std::string& extension_id) {
285 while (!IsUniqueId(extension_id, ToId(last_generated_rule_identifier_id_)))
286 ++last_generated_rule_identifier_id_;
287 return ToId(last_generated_rule_identifier_id_);
290 std::string RulesRegistry::CheckAndFillInOptionalRules(
291 const std::string& extension_id,
292 const std::vector<linked_ptr<RulesRegistry::Rule> >& rules) {
293 // IDs we have inserted, in case we need to rollback this operation.
294 std::vector<std::string> rollback_log;
296 // First we insert all rules with existing identifier, so that generated
297 // identifiers cannot collide with identifiers passed by the caller.
298 for (std::vector<linked_ptr<RulesRegistry::Rule> >::const_iterator i =
299 rules.begin(); i != rules.end(); ++i) {
300 RulesRegistry::Rule* rule = i->get();
301 if (rule->id.get()) {
302 std::string id = *(rule->id);
303 if (!IsUniqueId(extension_id, id)) {
304 RemoveUsedRuleIdentifiers(extension_id, rollback_log);
305 return "Id " + id + " was used multiple times.";
307 used_rule_identifiers_[extension_id].insert(id);
310 // Now we generate IDs in case they were not specified in the rules. This
311 // cannot fail so we do not need to keep track of a rollback log.
312 for (std::vector<linked_ptr<RulesRegistry::Rule> >::const_iterator i =
313 rules.begin(); i != rules.end(); ++i) {
314 RulesRegistry::Rule* rule = i->get();
315 if (!rule->id.get()) {
316 rule->id.reset(new std::string(GenerateUniqueId(extension_id)));
317 used_rule_identifiers_[extension_id].insert(*(rule->id));
320 return std::string();
323 void RulesRegistry::FillInOptionalPriorities(
324 const std::vector<linked_ptr<RulesRegistry::Rule> >& rules) {
325 std::vector<linked_ptr<RulesRegistry::Rule> >::const_iterator i;
326 for (i = rules.begin(); i != rules.end(); ++i) {
327 if (!(*i)->priority.get())
328 (*i)->priority.reset(new int(DEFAULT_PRIORITY));
332 void RulesRegistry::RemoveUsedRuleIdentifiers(
333 const std::string& extension_id,
334 const std::vector<std::string>& identifiers) {
335 std::vector<std::string>::const_iterator i;
336 for (i = identifiers.begin(); i != identifiers.end(); ++i)
337 used_rule_identifiers_[extension_id].erase(*i);
340 void RulesRegistry::RemoveAllUsedRuleIdentifiers(
341 const std::string& extension_id) {
342 used_rule_identifiers_.erase(extension_id);
345 } // namespace extensions