OLD | NEW |
---|---|
(Empty) | |
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/declarative/rules_registry.h" | |
6 | |
7 #include "base/bind.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" | |
27 | |
28 namespace { | |
29 | |
30 const char kSuccess[] = ""; | |
31 const char kDuplicateRuleId[] = "Duplicate rule ID: %s"; | |
32 | |
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>(); | |
39 } | |
40 | |
41 std::vector<linked_ptr<extensions::RulesRegistry::Rule> > RulesFromValue( | |
42 const base::Value* value) { | |
43 std::vector<linked_ptr<extensions::RulesRegistry::Rule> > rules; | |
44 | |
45 const base::ListValue* list = NULL; | |
46 if (!value || !value->GetAsList(&list)) | |
47 return rules; | |
48 | |
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)) | |
53 continue; | |
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); | |
58 } | |
59 | |
60 return rules; | |
61 } | |
62 | |
63 std::string ToId(int identifier) { | |
64 return base::StringPrintf("_%d_", identifier); | |
65 } | |
66 | |
67 } // namespace | |
68 | |
69 | |
70 namespace extensions { | |
71 | |
72 // RulesRegistry | |
73 | |
74 RulesRegistry::RulesRegistry( | |
75 Profile* profile, | |
76 const std::string& event_name, | |
77 content::BrowserThread::ID owner_thread, | |
78 bool log_storage_init_delay, | |
79 scoped_ptr<RulesCacheDelegate>* ui_part) | |
80 : owner_thread_(owner_thread), | |
81 event_name_(event_name), | |
82 weak_ptr_factory_(profile ? this : NULL), | |
83 cache_delegate_( | |
84 (profile ? (new RulesCacheDelegate(profile, | |
85 event_name, | |
86 owner_thread, | |
87 weak_ptr_factory_.GetWeakPtr(), | |
88 log_storage_init_delay))->GetWeakPtr() | |
89 : base::WeakPtr<RulesCacheDelegate>())), | |
90 process_changed_rules_requested_(profile ? NOT_SCHEDULED_FOR_PROCESSING | |
91 : NEVER_PROCESS), | |
92 last_generated_rule_identifier_id_(0) { | |
93 if (!profile) { | |
94 CHECK(!ui_part); | |
95 return; | |
96 } | |
97 | |
98 ui_part->reset(cache_delegate_.get()); | |
99 | |
100 cache_delegate_->Init(); | |
101 } | |
102 | |
103 std::string RulesRegistry::AddRules( | |
104 const std::string& extension_id, | |
105 const std::vector<linked_ptr<Rule> >& rules) { | |
106 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); | |
107 | |
108 std::string error = CheckAndFillInOptionalRules(extension_id, rules); | |
109 if (!error.empty()) | |
110 return error; | |
111 FillInOptionalPriorities(rules); | |
112 | |
113 // Verify that all rule IDs are new. | |
114 for (std::vector<linked_ptr<Rule> >::const_iterator i = | |
115 rules.begin(); i != rules.end(); ++i) { | |
116 const RuleId& rule_id = *((*i)->id); | |
117 RulesDictionaryKey key(extension_id, rule_id); | |
118 if (rules_.find(key) != rules_.end()) | |
119 return base::StringPrintf(kDuplicateRuleId, rule_id.c_str()); | |
120 } | |
121 | |
122 error = AddRulesImpl(extension_id, rules); | |
123 | |
124 if (!error.empty()) | |
125 return error; | |
126 | |
127 // Commit all rules into |rules_| on success. | |
vabr (Chromium)
2013/10/29 16:05:54
This is one of the parts when the rules caching co
| |
128 for (std::vector<linked_ptr<Rule> >::const_iterator i = | |
129 rules.begin(); i != rules.end(); ++i) { | |
130 const RuleId& rule_id = *((*i)->id); | |
131 RulesDictionaryKey key(extension_id, rule_id); | |
132 rules_[key] = *i; | |
133 } | |
134 | |
135 MaybeProcessChangedRules(extension_id); | |
136 return kSuccess; | |
137 } | |
138 | |
139 std::string RulesRegistry::RemoveRules( | |
140 const std::string& extension_id, | |
141 const std::vector<std::string>& rule_identifiers) { | |
142 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); | |
143 | |
144 std::string error = RemoveRulesImpl(extension_id, rule_identifiers); | |
145 | |
146 if (!error.empty()) | |
147 return error; | |
148 | |
149 // Commit removal of rules from |rules_| on success. | |
150 for (std::vector<std::string>::const_iterator i = | |
151 rule_identifiers.begin(); i != rule_identifiers.end(); ++i) { | |
152 RulesDictionaryKey lookup_key(extension_id, *i); | |
153 rules_.erase(lookup_key); | |
154 } | |
155 | |
156 MaybeProcessChangedRules(extension_id); | |
157 RemoveUsedRuleIdentifiers(extension_id, rule_identifiers); | |
158 return kSuccess; | |
159 } | |
160 | |
161 std::string RulesRegistry::RemoveAllRules(const std::string& extension_id) { | |
162 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); | |
163 | |
164 std::string error = RemoveAllRulesImpl(extension_id); | |
165 | |
166 if (!error.empty()) | |
167 return error; | |
168 | |
169 // Commit removal of rules from |rules_| on success. | |
170 for (RulesDictionary::const_iterator i = rules_.begin(); | |
171 i != rules_.end();) { | |
172 const RulesDictionaryKey& key = i->first; | |
173 ++i; | |
174 if (key.first == extension_id) | |
175 rules_.erase(key); | |
176 } | |
177 | |
178 MaybeProcessChangedRules(extension_id); | |
179 RemoveAllUsedRuleIdentifiers(extension_id); | |
180 return kSuccess; | |
181 } | |
182 | |
183 std::string RulesRegistry::GetRules( | |
184 const std::string& extension_id, | |
185 const std::vector<std::string>& rule_identifiers, | |
186 std::vector<linked_ptr<RulesRegistry::Rule> >* out) { | |
187 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); | |
188 | |
189 for (std::vector<std::string>::const_iterator i = rule_identifiers.begin(); | |
190 i != rule_identifiers.end(); ++i) { | |
191 RulesDictionaryKey lookup_key(extension_id, *i); | |
192 RulesDictionary::iterator entry = rules_.find(lookup_key); | |
193 if (entry != rules_.end()) | |
194 out->push_back(entry->second); | |
195 } | |
196 return kSuccess; | |
197 } | |
198 | |
199 std::string RulesRegistry::GetAllRules( | |
200 const std::string& extension_id, | |
201 std::vector<linked_ptr<RulesRegistry::Rule> >* out) { | |
202 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); | |
203 | |
204 for (RulesDictionary::const_iterator i = rules_.begin(); | |
205 i != rules_.end(); ++i) { | |
206 const RulesDictionaryKey& key = i->first; | |
207 if (key.first == extension_id) | |
208 out->push_back(i->second); | |
209 } | |
210 return kSuccess; | |
211 } | |
212 | |
213 void RulesRegistry::OnExtensionUnloaded(const std::string& extension_id) { | |
214 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); | |
215 std::string error = RemoveAllRules(extension_id); | |
216 if (!error.empty()) | |
217 LOG(ERROR) << error; | |
218 used_rule_identifiers_.erase(extension_id); | |
219 } | |
220 | |
221 size_t RulesRegistry::GetNumberOfUsedRuleIdentifiersForTesting() const { | |
222 size_t entry_count = 0u; | |
223 for (RuleIdentifiersMap::const_iterator extension = | |
224 used_rule_identifiers_.begin(); | |
225 extension != used_rule_identifiers_.end(); | |
226 ++extension) { | |
227 // Each extension is counted as 1 just for being there. Otherwise we miss | |
228 // keys with empty values. | |
229 entry_count += 1u + extension->second.size(); | |
230 } | |
231 return entry_count; | |
232 } | |
233 | |
234 RulesRegistry::~RulesRegistry() { | |
235 } | |
236 | |
237 void RulesRegistry::MarkReady(base::Time storage_init_time) { | |
238 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); | |
239 | |
240 if (!storage_init_time.is_null()) { | |
241 UMA_HISTOGRAM_TIMES("Extensions.DeclarativeRulesStorageInitialization", | |
242 base::Time::Now() - storage_init_time); | |
243 } | |
244 | |
245 ready_.Signal(); | |
246 } | |
247 | |
248 void RulesRegistry::DeserializeAndAddRules( | |
249 const std::string& extension_id, | |
250 scoped_ptr<base::Value> rules) { | |
251 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); | |
252 | |
253 AddRules(extension_id, RulesFromValue(rules.get())); | |
254 } | |
255 | |
256 void RulesRegistry::ProcessChangedRules(const std::string& extension_id) { | |
257 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); | |
258 | |
259 process_changed_rules_requested_ = NOT_SCHEDULED_FOR_PROCESSING; | |
260 | |
261 std::vector<linked_ptr<RulesRegistry::Rule> > new_rules; | |
262 std::string error = GetAllRules(extension_id, &new_rules); | |
263 DCHECK_EQ(std::string(), error); | |
264 content::BrowserThread::PostTask( | |
265 content::BrowserThread::UI, | |
266 FROM_HERE, | |
267 base::Bind(&RulesCacheDelegate::WriteToStorage, | |
268 cache_delegate_, | |
269 extension_id, | |
270 base::Passed(RulesToValue(new_rules)))); | |
271 } | |
272 | |
273 void RulesRegistry::MaybeProcessChangedRules(const std::string& extension_id) { | |
274 if (process_changed_rules_requested_ != NOT_SCHEDULED_FOR_PROCESSING) | |
275 return; | |
276 | |
277 process_changed_rules_requested_ = SCHEDULED_FOR_PROCESSING; | |
278 ready_.Post(FROM_HERE, | |
279 base::Bind(&RulesRegistry::ProcessChangedRules, | |
280 weak_ptr_factory_.GetWeakPtr(), | |
281 extension_id)); | |
282 } | |
283 | |
284 bool RulesRegistry::IsUniqueId(const std::string& extension_id, | |
285 const std::string& rule_id) const { | |
286 RuleIdentifiersMap::const_iterator identifiers = | |
287 used_rule_identifiers_.find(extension_id); | |
288 if (identifiers == used_rule_identifiers_.end()) | |
289 return true; | |
290 return identifiers->second.find(rule_id) == identifiers->second.end(); | |
291 } | |
292 | |
293 std::string RulesRegistry::GenerateUniqueId(const std::string& extension_id) { | |
294 while (!IsUniqueId(extension_id, ToId(last_generated_rule_identifier_id_))) | |
295 ++last_generated_rule_identifier_id_; | |
296 return ToId(last_generated_rule_identifier_id_); | |
297 } | |
298 | |
299 std::string RulesRegistry::CheckAndFillInOptionalRules( | |
300 const std::string& extension_id, | |
301 const std::vector<linked_ptr<RulesRegistry::Rule> >& rules) { | |
302 // IDs we have inserted, in case we need to rollback this operation. | |
303 std::vector<std::string> rollback_log; | |
304 | |
305 // First we insert all rules with existing identifier, so that generated | |
306 // identifiers cannot collide with identifiers passed by the caller. | |
307 for (std::vector<linked_ptr<RulesRegistry::Rule> >::const_iterator i = | |
308 rules.begin(); i != rules.end(); ++i) { | |
309 RulesRegistry::Rule* rule = i->get(); | |
310 if (rule->id.get()) { | |
311 std::string id = *(rule->id); | |
312 if (!IsUniqueId(extension_id, id)) { | |
313 RemoveUsedRuleIdentifiers(extension_id, rollback_log); | |
314 return "Id " + id + " was used multiple times."; | |
315 } | |
316 used_rule_identifiers_[extension_id].insert(id); | |
317 } | |
318 } | |
319 // Now we generate IDs in case they were not specificed in the rules. This | |
320 // cannot fail so we do not need to keep track of a rollback log. | |
321 for (std::vector<linked_ptr<RulesRegistry::Rule> >::const_iterator i = | |
322 rules.begin(); i != rules.end(); ++i) { | |
323 RulesRegistry::Rule* rule = i->get(); | |
324 if (!rule->id.get()) { | |
325 rule->id.reset(new std::string(GenerateUniqueId(extension_id))); | |
326 used_rule_identifiers_[extension_id].insert(*(rule->id)); | |
327 } | |
328 } | |
329 return std::string(); | |
330 } | |
331 | |
332 void RulesRegistry::FillInOptionalPriorities( | |
333 const std::vector<linked_ptr<RulesRegistry::Rule> >& rules) { | |
334 std::vector<linked_ptr<RulesRegistry::Rule> >::const_iterator i; | |
335 for (i = rules.begin(); i != rules.end(); ++i) { | |
336 if (!(*i)->priority.get()) | |
337 (*i)->priority.reset(new int(DEFAULT_PRIORITY)); | |
338 } | |
339 } | |
340 | |
341 void RulesRegistry::RemoveUsedRuleIdentifiers( | |
342 const std::string& extension_id, | |
343 const std::vector<std::string>& identifiers) { | |
344 std::vector<std::string>::const_iterator i; | |
345 for (i = identifiers.begin(); i != identifiers.end(); ++i) | |
346 used_rule_identifiers_[extension_id].erase(*i); | |
347 } | |
348 | |
349 void RulesRegistry::RemoveAllUsedRuleIdentifiers( | |
350 const std::string& extension_id) { | |
351 used_rule_identifiers_.erase(extension_id); | |
352 } | |
353 | |
354 } // namespace extensions | |
OLD | NEW |