Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / declarative / rules_registry.cc
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 <utility>
8
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/time/time.h"
16 #include "base/values.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/extensions/api/declarative/rules_cache_delegate.h"
19 #include "chrome/browser/extensions/extension_service.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 "content/public/browser/browser_thread.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_source.h"
26 #include "extensions/browser/extension_prefs.h"
27 #include "extensions/browser/extension_system.h"
28 #include "extensions/common/extension.h"
29
30 namespace {
31
32 const char kSuccess[] = "";
33 const char kDuplicateRuleId[] = "Duplicate rule ID: %s";
34
35 scoped_ptr<base::Value> RulesToValue(
36     const std::vector<linked_ptr<extensions::RulesRegistry::Rule> >& rules) {
37   scoped_ptr<base::ListValue> list(new base::ListValue());
38   for (size_t i = 0; i < rules.size(); ++i)
39     list->Append(rules[i]->ToValue().release());
40   return list.PassAs<base::Value>();
41 }
42
43 std::vector<linked_ptr<extensions::RulesRegistry::Rule> > RulesFromValue(
44     const base::Value* value) {
45   std::vector<linked_ptr<extensions::RulesRegistry::Rule> > rules;
46
47   const base::ListValue* list = NULL;
48   if (!value || !value->GetAsList(&list))
49     return rules;
50
51   rules.reserve(list->GetSize());
52   for (size_t i = 0; i < list->GetSize(); ++i) {
53     const base::DictionaryValue* dict = NULL;
54     if (!list->GetDictionary(i, &dict))
55       continue;
56     linked_ptr<extensions::RulesRegistry::Rule> rule(
57         new extensions::RulesRegistry::Rule());
58     if (extensions::RulesRegistry::Rule::Populate(*dict, rule.get()))
59       rules.push_back(rule);
60   }
61
62   return rules;
63 }
64
65 std::string ToId(int identifier) {
66   return base::StringPrintf("_%d_", identifier);
67 }
68
69 }  // namespace
70
71
72 namespace extensions {
73
74 // RulesRegistry
75
76 RulesRegistry::RulesRegistry(Profile* profile,
77                              const std::string& event_name,
78                              content::BrowserThread::ID owner_thread,
79                              RulesCacheDelegate* cache_delegate,
80                              const WebViewKey& webview_key)
81     : profile_(profile),
82       owner_thread_(owner_thread),
83       event_name_(event_name),
84       webview_key_(webview_key),
85       ready_(/*signaled=*/!cache_delegate),  // Immediately ready if no cache
86                                              // delegate to wait for.
87       weak_ptr_factory_(profile ? this : NULL),
88       last_generated_rule_identifier_id_(0) {
89   if (cache_delegate) {
90     cache_delegate_ = cache_delegate->GetWeakPtr();
91     cache_delegate->Init(this);
92   }
93 }
94
95 std::string RulesRegistry::AddRulesNoFill(
96     const std::string& extension_id,
97     const std::vector<linked_ptr<Rule> >& rules) {
98   DCHECK_CURRENTLY_ON(owner_thread());
99
100   // Verify that all rule IDs are new.
101   for (std::vector<linked_ptr<Rule> >::const_iterator i =
102       rules.begin(); i != rules.end(); ++i) {
103     const RuleId& rule_id = *((*i)->id);
104     // Every rule should have a priority assigned.
105     DCHECK((*i)->priority);
106     RulesDictionaryKey key(extension_id, rule_id);
107     if (rules_.find(key) != rules_.end())
108       return base::StringPrintf(kDuplicateRuleId, rule_id.c_str());
109   }
110
111   std::string error = AddRulesImpl(extension_id, rules);
112
113   if (!error.empty())
114     return error;
115
116   // Commit all rules into |rules_| on success.
117   for (std::vector<linked_ptr<Rule> >::const_iterator i =
118       rules.begin(); i != rules.end(); ++i) {
119     const RuleId& rule_id = *((*i)->id);
120     RulesDictionaryKey key(extension_id, rule_id);
121     rules_[key] = *i;
122   }
123
124   MaybeProcessChangedRules(extension_id);
125   return kSuccess;
126 }
127
128 std::string RulesRegistry::AddRules(
129     const std::string& extension_id,
130     const std::vector<linked_ptr<Rule> >& rules) {
131   DCHECK_CURRENTLY_ON(owner_thread());
132
133   std::string error = CheckAndFillInOptionalRules(extension_id, rules);
134   if (!error.empty())
135     return error;
136   FillInOptionalPriorities(rules);
137
138   return AddRulesNoFill(extension_id, rules);
139 }
140
141 std::string RulesRegistry::RemoveRules(
142     const std::string& extension_id,
143     const std::vector<std::string>& rule_identifiers) {
144   DCHECK_CURRENTLY_ON(owner_thread());
145
146   std::string error = RemoveRulesImpl(extension_id, rule_identifiers);
147
148   if (!error.empty())
149     return error;
150
151   for (std::vector<std::string>::const_iterator i = rule_identifiers.begin();
152        i != rule_identifiers.end();
153        ++i) {
154     RulesDictionaryKey lookup_key(extension_id, *i);
155     rules_.erase(lookup_key);
156   }
157
158   MaybeProcessChangedRules(extension_id);
159   RemoveUsedRuleIdentifiers(extension_id, rule_identifiers);
160   return kSuccess;
161 }
162
163 std::string RulesRegistry::RemoveAllRules(const std::string& extension_id) {
164   std::string result = RulesRegistry::RemoveAllRulesNoStoreUpdate(extension_id);
165   MaybeProcessChangedRules(extension_id);  // Now update the prefs and store.
166   return result;
167 }
168
169 std::string RulesRegistry::RemoveAllRulesNoStoreUpdate(
170     const std::string& extension_id) {
171   DCHECK_CURRENTLY_ON(owner_thread());
172
173   std::string error = RemoveAllRulesImpl(extension_id);
174
175   if (!error.empty())
176     return error;
177
178   for (RulesDictionary::const_iterator i = rules_.begin();
179       i != rules_.end();) {
180     const RulesDictionaryKey& key = i->first;
181     ++i;
182     if (key.first == extension_id)
183       rules_.erase(key);
184   }
185
186   RemoveAllUsedRuleIdentifiers(extension_id);
187   return kSuccess;
188 }
189
190 void RulesRegistry::GetRules(const std::string& extension_id,
191                              const std::vector<std::string>& rule_identifiers,
192                              std::vector<linked_ptr<Rule> >* out) {
193   DCHECK_CURRENTLY_ON(owner_thread());
194
195   for (std::vector<std::string>::const_iterator i = rule_identifiers.begin();
196       i != rule_identifiers.end(); ++i) {
197     RulesDictionaryKey lookup_key(extension_id, *i);
198     RulesDictionary::iterator entry = rules_.find(lookup_key);
199     if (entry != rules_.end())
200       out->push_back(entry->second);
201   }
202 }
203
204 void RulesRegistry::GetAllRules(const std::string& extension_id,
205                                 std::vector<linked_ptr<Rule> >* out) {
206   DCHECK_CURRENTLY_ON(owner_thread());
207
208   for (RulesDictionary::const_iterator i = rules_.begin();
209       i != rules_.end(); ++i) {
210     const RulesDictionaryKey& key = i->first;
211     if (key.first == extension_id)
212       out->push_back(i->second);
213   }
214 }
215
216 void RulesRegistry::OnExtensionUnloaded(const std::string& extension_id) {
217   DCHECK_CURRENTLY_ON(owner_thread());
218   std::string error = RemoveAllRulesImpl(extension_id);
219   if (!error.empty())
220     LOG(ERROR) << error;
221 }
222
223 void RulesRegistry::OnExtensionUninstalled(const std::string& extension_id) {
224   DCHECK_CURRENTLY_ON(owner_thread());
225   std::string error = RemoveAllRulesNoStoreUpdate(extension_id);
226   if (!error.empty())
227     LOG(ERROR) << error;
228 }
229
230 void RulesRegistry::OnExtensionLoaded(const std::string& extension_id) {
231   DCHECK_CURRENTLY_ON(owner_thread());
232   std::vector<linked_ptr<Rule> > rules;
233   GetAllRules(extension_id, &rules);
234   std::string error = AddRulesImpl(extension_id, rules);
235   if (!error.empty())
236     LOG(ERROR) << error;
237 }
238
239 size_t RulesRegistry::GetNumberOfUsedRuleIdentifiersForTesting() const {
240   size_t entry_count = 0u;
241   for (RuleIdentifiersMap::const_iterator extension =
242            used_rule_identifiers_.begin();
243        extension != used_rule_identifiers_.end();
244        ++extension) {
245     // Each extension is counted as 1 just for being there. Otherwise we miss
246     // keys with empty values.
247     entry_count += 1u + extension->second.size();
248   }
249   return entry_count;
250 }
251
252 void RulesRegistry::DeserializeAndAddRules(
253     const std::string& extension_id,
254     scoped_ptr<base::Value> rules) {
255   DCHECK_CURRENTLY_ON(owner_thread());
256
257   AddRulesNoFill(extension_id, RulesFromValue(rules.get()));
258 }
259
260 RulesRegistry::~RulesRegistry() {
261 }
262
263 void RulesRegistry::MarkReady(base::Time storage_init_time) {
264   DCHECK_CURRENTLY_ON(owner_thread());
265
266   if (!storage_init_time.is_null()) {
267     UMA_HISTOGRAM_TIMES("Extensions.DeclarativeRulesStorageInitialization",
268                         base::Time::Now() - storage_init_time);
269   }
270
271   ready_.Signal();
272 }
273
274 void RulesRegistry::ProcessChangedRules(const std::string& extension_id) {
275   DCHECK_CURRENTLY_ON(owner_thread());
276
277   DCHECK(ContainsKey(process_changed_rules_requested_, extension_id));
278   process_changed_rules_requested_[extension_id] = NOT_SCHEDULED_FOR_PROCESSING;
279
280   std::vector<linked_ptr<Rule> > new_rules;
281   GetAllRules(extension_id, &new_rules);
282   content::BrowserThread::PostTask(
283       content::BrowserThread::UI,
284       FROM_HERE,
285       base::Bind(&RulesCacheDelegate::WriteToStorage,
286                  cache_delegate_,
287                  extension_id,
288                  base::Passed(RulesToValue(new_rules))));
289 }
290
291 void RulesRegistry::MaybeProcessChangedRules(const std::string& extension_id) {
292   // Read and initialize |process_changed_rules_requested_[extension_id]| if
293   // necessary. (Note that the insertion below will not overwrite
294   // |process_changed_rules_requested_[extension_id]| if that already exists.
295   std::pair<ProcessStateMap::iterator, bool> insertion =
296       process_changed_rules_requested_.insert(std::make_pair(
297           extension_id,
298           profile_ ? NOT_SCHEDULED_FOR_PROCESSING : NEVER_PROCESS));
299   if (insertion.first->second != NOT_SCHEDULED_FOR_PROCESSING)
300     return;
301
302   process_changed_rules_requested_[extension_id] = SCHEDULED_FOR_PROCESSING;
303   ready_.Post(FROM_HERE,
304               base::Bind(&RulesRegistry::ProcessChangedRules,
305                          weak_ptr_factory_.GetWeakPtr(),
306                          extension_id));
307 }
308
309 bool RulesRegistry::IsUniqueId(const std::string& extension_id,
310                                const std::string& rule_id) const {
311   RuleIdentifiersMap::const_iterator identifiers =
312       used_rule_identifiers_.find(extension_id);
313   if (identifiers == used_rule_identifiers_.end())
314     return true;
315   return identifiers->second.find(rule_id) == identifiers->second.end();
316 }
317
318 std::string RulesRegistry::GenerateUniqueId(const std::string& extension_id) {
319   while (!IsUniqueId(extension_id, ToId(last_generated_rule_identifier_id_)))
320     ++last_generated_rule_identifier_id_;
321   return ToId(last_generated_rule_identifier_id_);
322 }
323
324 std::string RulesRegistry::CheckAndFillInOptionalRules(
325     const std::string& extension_id,
326     const std::vector<linked_ptr<Rule> >& rules) {
327   // IDs we have inserted, in case we need to rollback this operation.
328   std::vector<std::string> rollback_log;
329
330   // First we insert all rules with existing identifier, so that generated
331   // identifiers cannot collide with identifiers passed by the caller.
332   for (std::vector<linked_ptr<Rule> >::const_iterator i = rules.begin();
333        i != rules.end();
334        ++i) {
335     Rule* rule = i->get();
336     if (rule->id.get()) {
337       std::string id = *(rule->id);
338       if (!IsUniqueId(extension_id, id)) {
339         RemoveUsedRuleIdentifiers(extension_id, rollback_log);
340         return "Id " + id + " was used multiple times.";
341       }
342       used_rule_identifiers_[extension_id].insert(id);
343     }
344   }
345   // Now we generate IDs in case they were not specified in the rules. This
346   // cannot fail so we do not need to keep track of a rollback log.
347   for (std::vector<linked_ptr<Rule> >::const_iterator i = rules.begin();
348        i != rules.end();
349        ++i) {
350     Rule* rule = i->get();
351     if (!rule->id.get()) {
352       rule->id.reset(new std::string(GenerateUniqueId(extension_id)));
353       used_rule_identifiers_[extension_id].insert(*(rule->id));
354     }
355   }
356   return std::string();
357 }
358
359 void RulesRegistry::FillInOptionalPriorities(
360     const std::vector<linked_ptr<Rule> >& rules) {
361   std::vector<linked_ptr<Rule> >::const_iterator i;
362   for (i = rules.begin(); i != rules.end(); ++i) {
363     if (!(*i)->priority.get())
364       (*i)->priority.reset(new int(DEFAULT_PRIORITY));
365   }
366 }
367
368 void RulesRegistry::RemoveUsedRuleIdentifiers(
369     const std::string& extension_id,
370     const std::vector<std::string>& identifiers) {
371   std::vector<std::string>::const_iterator i;
372   for (i = identifiers.begin(); i != identifiers.end(); ++i)
373     used_rule_identifiers_[extension_id].erase(*i);
374 }
375
376 void RulesRegistry::RemoveAllUsedRuleIdentifiers(
377     const std::string& extension_id) {
378   used_rule_identifiers_.erase(extension_id);
379 }
380
381 }  // namespace extensions