OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 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 // DeclarativeRule<>, DeclarativeConditionSet<>, and DeclarativeActionSet<> | |
6 // templates usable with multiple different declarativeFoo systems. These are | |
7 // templated on the Condition and Action types that define the behavior of a | |
8 // particular declarative event. | |
9 | |
10 #ifndef CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_RULE_H__ | |
11 #define CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_RULE_H__ | |
12 | |
13 #include <limits> | |
14 #include <set> | |
15 #include <string> | |
16 #include <vector> | |
17 | |
18 #include "base/callback.h" | |
19 #include "base/memory/linked_ptr.h" | |
20 #include "base/memory/scoped_vector.h" | |
21 #include "base/stl_util.h" | |
22 #include "base/time/time.h" | |
23 #include "chrome/common/extensions/api/events.h" | |
24 #include "components/url_matcher/url_matcher.h" | |
25 #include "extensions/common/extension.h" | |
26 | |
27 namespace base { | |
28 class Time; | |
29 class Value; | |
30 } | |
31 | |
32 namespace content { | |
33 class BrowserContext; | |
34 } | |
35 | |
36 namespace extensions { | |
37 | |
38 // This class stores a set of conditions that may be part of a DeclarativeRule. | |
39 // If any condition is fulfilled, the Actions of the DeclarativeRule can be | |
40 // triggered. | |
41 // | |
42 // ConditionT should be immutable after creation. It must define the following | |
43 // members: | |
44 // | |
45 // // Arguments passed through from DeclarativeConditionSet::Create. | |
46 // static scoped_ptr<ConditionT> Create( | |
47 // const Extension* extension, | |
48 // URLMatcherConditionFactory* url_matcher_condition_factory, | |
49 // // Except this argument gets elements of the AnyVector. | |
50 // const base::Value& definition, | |
51 // std::string* error); | |
52 // // If the Condition needs to be filtered by some URLMatcherConditionSets, | |
53 // // append them to |condition_sets|. | |
54 // // DeclarativeConditionSet::GetURLMatcherConditionSets forwards here. | |
55 // void GetURLMatcherConditionSets( | |
56 // URLMatcherConditionSet::Vector* condition_sets); | |
57 // // |match_data| passed through from DeclarativeConditionSet::IsFulfilled. | |
58 // bool IsFulfilled(const ConditionT::MatchData& match_data); | |
59 template<typename ConditionT> | |
60 class DeclarativeConditionSet { | |
61 public: | |
62 typedef std::vector<linked_ptr<base::Value> > AnyVector; | |
63 typedef std::vector<linked_ptr<const ConditionT> > Conditions; | |
64 typedef typename Conditions::const_iterator const_iterator; | |
65 | |
66 // Factory method that creates a DeclarativeConditionSet for |extension| | |
67 // according to the JSON array |conditions| passed by the extension API. Sets | |
68 // |error| and returns NULL in case of an error. | |
69 static scoped_ptr<DeclarativeConditionSet> Create( | |
70 const Extension* extension, | |
71 url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory, | |
72 const AnyVector& conditions, | |
73 std::string* error); | |
74 | |
75 const Conditions& conditions() const { | |
76 return conditions_; | |
77 } | |
78 | |
79 const_iterator begin() const { return conditions_.begin(); } | |
80 const_iterator end() const { return conditions_.end(); } | |
81 | |
82 // If |url_match_trigger| is not -1, this function looks for a condition | |
83 // with this URLMatcherConditionSet, and forwards to that condition's | |
84 // IsFulfilled(|match_data|). If there is no such condition, then false is | |
85 // returned. If |url_match_trigger| is -1, this function returns whether any | |
86 // of the conditions without URL attributes is satisfied. | |
87 bool IsFulfilled(url_matcher::URLMatcherConditionSet::ID url_match_trigger, | |
88 const typename ConditionT::MatchData& match_data) const; | |
89 | |
90 // Appends the URLMatcherConditionSet from all conditions to |condition_sets|. | |
91 void GetURLMatcherConditionSets( | |
92 url_matcher::URLMatcherConditionSet::Vector* condition_sets) const; | |
93 | |
94 // Returns whether there are some conditions without UrlFilter attributes. | |
95 bool HasConditionsWithoutUrls() const { | |
96 return !conditions_without_urls_.empty(); | |
97 } | |
98 | |
99 private: | |
100 typedef std::map<url_matcher::URLMatcherConditionSet::ID, const ConditionT*> | |
101 URLMatcherIdToCondition; | |
102 | |
103 DeclarativeConditionSet( | |
104 const Conditions& conditions, | |
105 const URLMatcherIdToCondition& match_id_to_condition, | |
106 const std::vector<const ConditionT*>& conditions_without_urls); | |
107 | |
108 const URLMatcherIdToCondition match_id_to_condition_; | |
109 const Conditions conditions_; | |
110 const std::vector<const ConditionT*> conditions_without_urls_; | |
111 | |
112 DISALLOW_COPY_AND_ASSIGN(DeclarativeConditionSet); | |
113 }; | |
114 | |
115 // Immutable container for multiple actions. | |
116 // | |
117 // ActionT should be immutable after creation. It must define the following | |
118 // members: | |
119 // | |
120 // // Arguments passed through from ActionSet::Create. | |
121 // static scoped_ptr<ActionT> Create( | |
122 // const Extension* extension, | |
123 // // Except this argument gets elements of the AnyVector. | |
124 // const base::Value& definition, | |
125 // std::string* error, bool* bad_message); | |
126 // void Apply(const std::string& extension_id, | |
127 // const base::Time& extension_install_time, | |
128 // // Contains action-type-specific in/out parameters. | |
129 // typename ActionT::ApplyInfo* apply_info) const; | |
130 // // Only needed if the RulesRegistry calls DeclarativeActionSet::Revert(). | |
131 // void Revert(const std::string& extension_id, | |
132 // const base::Time& extension_install_time, | |
133 // // Contains action-type-specific in/out parameters. | |
134 // typename ActionT::ApplyInfo* apply_info) const; | |
135 // // Return the minimum priority of rules that can be evaluated after this | |
136 // // action runs. A suitable default value is MIN_INT. | |
137 // int minimum_priority() const; | |
138 // | |
139 // TODO(battre): As DeclarativeActionSet can become the single owner of all | |
140 // actions, we can optimize here by making some of them singletons (e.g. Cancel | |
141 // actions). | |
142 template<typename ActionT> | |
143 class DeclarativeActionSet { | |
144 public: | |
145 typedef std::vector<linked_ptr<base::Value> > AnyVector; | |
146 typedef std::vector<scoped_refptr<const ActionT> > Actions; | |
147 | |
148 explicit DeclarativeActionSet(const Actions& actions); | |
149 | |
150 // Factory method that instantiates a DeclarativeActionSet for |extension| | |
151 // according to |actions| which represents the array of actions received from | |
152 // the extension API. | |
153 static scoped_ptr<DeclarativeActionSet> Create( | |
154 content::BrowserContext* browser_context, | |
155 const Extension* extension, | |
156 const AnyVector& actions, | |
157 std::string* error, | |
158 bool* bad_message); | |
159 | |
160 // Rules call this method when their conditions are fulfilled. | |
161 void Apply(const std::string& extension_id, | |
162 const base::Time& extension_install_time, | |
163 typename ActionT::ApplyInfo* apply_info) const; | |
164 | |
165 // Rules call this method when their conditions are fulfilled, but Apply has | |
166 // already been called. | |
167 void Reapply(const std::string& extension_id, | |
168 const base::Time& extension_install_time, | |
169 typename ActionT::ApplyInfo* apply_info) const; | |
170 | |
171 // Rules call this method when they have stateful conditions, and those | |
172 // conditions stop being fulfilled. Rules with event-based conditions (e.g. a | |
173 // network request happened) will never Revert() an action. | |
174 void Revert(const std::string& extension_id, | |
175 const base::Time& extension_install_time, | |
176 typename ActionT::ApplyInfo* apply_info) const; | |
177 | |
178 // Returns the minimum priority of rules that may be evaluated after | |
179 // this rule. Defaults to MIN_INT. | |
180 int GetMinimumPriority() const; | |
181 | |
182 const Actions& actions() const { return actions_; } | |
183 | |
184 private: | |
185 const Actions actions_; | |
186 | |
187 DISALLOW_COPY_AND_ASSIGN(DeclarativeActionSet); | |
188 }; | |
189 | |
190 // Representation of a rule of a declarative API: | |
191 // https://developer.chrome.com/beta/extensions/events.html#declarative. | |
192 // Generally a RulesRegistry will hold a collection of Rules for a given | |
193 // declarative API and contain the logic for matching and applying them. | |
194 // | |
195 // See DeclarativeConditionSet and DeclarativeActionSet for the requirements on | |
196 // ConditionT and ActionT. | |
197 template<typename ConditionT, typename ActionT> | |
198 class DeclarativeRule { | |
199 public: | |
200 typedef std::string ExtensionId; | |
201 typedef std::string RuleId; | |
202 typedef std::pair<ExtensionId, RuleId> GlobalRuleId; | |
203 typedef int Priority; | |
204 typedef DeclarativeConditionSet<ConditionT> ConditionSet; | |
205 typedef DeclarativeActionSet<ActionT> ActionSet; | |
206 typedef extensions::api::events::Rule JsonRule; | |
207 typedef std::vector<std::string> Tags; | |
208 | |
209 // Checks whether the set of |conditions| and |actions| are consistent. | |
210 // Returns true in case of consistency and MUST set |error| otherwise. | |
211 typedef base::Callback<bool(const ConditionSet* conditions, | |
212 const ActionSet* actions, | |
213 std::string* error)> ConsistencyChecker; | |
214 | |
215 DeclarativeRule(const GlobalRuleId& id, | |
216 const Tags& tags, | |
217 base::Time extension_installation_time, | |
218 scoped_ptr<ConditionSet> conditions, | |
219 scoped_ptr<ActionSet> actions, | |
220 Priority priority); | |
221 | |
222 // Creates a DeclarativeRule for |extension| given a json definition. The | |
223 // format of each condition and action's json is up to the specific ConditionT | |
224 // and ActionT. |extension| may be NULL in tests. | |
225 // | |
226 // Before constructing the final rule, calls check_consistency(conditions, | |
227 // actions, error) and returns NULL if it fails. Pass NULL if no consistency | |
228 // check is needed. If |error| is empty, the translation was successful and | |
229 // the returned rule is internally consistent. | |
230 static scoped_ptr<DeclarativeRule> Create( | |
231 url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory, | |
232 content::BrowserContext* browser_context, | |
233 const Extension* extension, | |
234 base::Time extension_installation_time, | |
235 linked_ptr<JsonRule> rule, | |
236 ConsistencyChecker check_consistency, | |
237 std::string* error); | |
238 | |
239 const GlobalRuleId& id() const { return id_; } | |
240 const Tags& tags() const { return tags_; } | |
241 const std::string& extension_id() const { return id_.first; } | |
242 const ConditionSet& conditions() const { return *conditions_; } | |
243 const ActionSet& actions() const { return *actions_; } | |
244 Priority priority() const { return priority_; } | |
245 | |
246 // Calls actions().Apply(extension_id(), extension_installation_time_, | |
247 // apply_info). This function should only be called when the conditions_ are | |
248 // fulfilled (from a semantic point of view; no harm is done if this function | |
249 // is called at other times for testing purposes). | |
250 void Apply(typename ActionT::ApplyInfo* apply_info) const; | |
251 | |
252 // Returns the minimum priority of rules that may be evaluated after | |
253 // this rule. Defaults to MIN_INT. Only valid if the conditions of this rule | |
254 // are fulfilled. | |
255 Priority GetMinimumPriority() const; | |
256 | |
257 private: | |
258 GlobalRuleId id_; | |
259 Tags tags_; | |
260 base::Time extension_installation_time_; // For precedences of rules. | |
261 scoped_ptr<ConditionSet> conditions_; | |
262 scoped_ptr<ActionSet> actions_; | |
263 Priority priority_; | |
264 | |
265 DISALLOW_COPY_AND_ASSIGN(DeclarativeRule); | |
266 }; | |
267 | |
268 // Implementation details below here. | |
269 | |
270 // | |
271 // DeclarativeConditionSet | |
272 // | |
273 | |
274 template<typename ConditionT> | |
275 bool DeclarativeConditionSet<ConditionT>::IsFulfilled( | |
276 url_matcher::URLMatcherConditionSet::ID url_match_trigger, | |
277 const typename ConditionT::MatchData& match_data) const { | |
278 if (url_match_trigger == -1) { | |
279 // Invalid trigger -- indication that we should only check conditions | |
280 // without URL attributes. | |
281 for (typename std::vector<const ConditionT*>::const_iterator it = | |
282 conditions_without_urls_.begin(); | |
283 it != conditions_without_urls_.end(); ++it) { | |
284 if ((*it)->IsFulfilled(match_data)) | |
285 return true; | |
286 } | |
287 return false; | |
288 } | |
289 | |
290 typename URLMatcherIdToCondition::const_iterator triggered = | |
291 match_id_to_condition_.find(url_match_trigger); | |
292 return (triggered != match_id_to_condition_.end() && | |
293 triggered->second->IsFulfilled(match_data)); | |
294 } | |
295 | |
296 template<typename ConditionT> | |
297 void DeclarativeConditionSet<ConditionT>::GetURLMatcherConditionSets( | |
298 url_matcher::URLMatcherConditionSet::Vector* condition_sets) const { | |
299 for (typename Conditions::const_iterator i = conditions_.begin(); | |
300 i != conditions_.end(); ++i) { | |
301 (*i)->GetURLMatcherConditionSets(condition_sets); | |
302 } | |
303 } | |
304 | |
305 // static | |
306 template<typename ConditionT> | |
307 scoped_ptr<DeclarativeConditionSet<ConditionT> > | |
308 DeclarativeConditionSet<ConditionT>::Create( | |
309 const Extension* extension, | |
310 url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory, | |
311 const AnyVector& conditions, | |
312 std::string* error) { | |
313 Conditions result; | |
314 | |
315 for (AnyVector::const_iterator i = conditions.begin(); | |
316 i != conditions.end(); ++i) { | |
317 CHECK(i->get()); | |
318 scoped_ptr<ConditionT> condition = ConditionT::Create( | |
319 extension, url_matcher_condition_factory, **i, error); | |
320 if (!error->empty()) | |
321 return scoped_ptr<DeclarativeConditionSet>(); | |
322 result.push_back(make_linked_ptr(condition.release())); | |
323 } | |
324 | |
325 URLMatcherIdToCondition match_id_to_condition; | |
326 std::vector<const ConditionT*> conditions_without_urls; | |
327 url_matcher::URLMatcherConditionSet::Vector condition_sets; | |
328 | |
329 for (typename Conditions::const_iterator i = result.begin(); | |
330 i != result.end(); ++i) { | |
331 condition_sets.clear(); | |
332 (*i)->GetURLMatcherConditionSets(&condition_sets); | |
333 if (condition_sets.empty()) { | |
334 conditions_without_urls.push_back(i->get()); | |
335 } else { | |
336 for (url_matcher::URLMatcherConditionSet::Vector::const_iterator | |
337 match_set = condition_sets.begin(); | |
338 match_set != condition_sets.end(); ++match_set) | |
339 match_id_to_condition[(*match_set)->id()] = i->get(); | |
340 } | |
341 } | |
342 | |
343 return make_scoped_ptr(new DeclarativeConditionSet( | |
344 result, match_id_to_condition, conditions_without_urls)); | |
345 } | |
346 | |
347 template<typename ConditionT> | |
348 DeclarativeConditionSet<ConditionT>::DeclarativeConditionSet( | |
349 const Conditions& conditions, | |
350 const URLMatcherIdToCondition& match_id_to_condition, | |
351 const std::vector<const ConditionT*>& conditions_without_urls) | |
352 : match_id_to_condition_(match_id_to_condition), | |
353 conditions_(conditions), | |
354 conditions_without_urls_(conditions_without_urls) {} | |
355 | |
356 // | |
357 // DeclarativeActionSet | |
358 // | |
359 | |
360 template<typename ActionT> | |
361 DeclarativeActionSet<ActionT>::DeclarativeActionSet(const Actions& actions) | |
362 : actions_(actions) {} | |
363 | |
364 // static | |
365 template<typename ActionT> | |
366 scoped_ptr<DeclarativeActionSet<ActionT> > | |
367 DeclarativeActionSet<ActionT>::Create( | |
368 content::BrowserContext* browser_context, | |
369 const Extension* extension, | |
370 const AnyVector& actions, | |
371 std::string* error, | |
372 bool* bad_message) { | |
373 *error = ""; | |
374 *bad_message = false; | |
375 Actions result; | |
376 | |
377 for (AnyVector::const_iterator i = actions.begin(); | |
378 i != actions.end(); ++i) { | |
379 CHECK(i->get()); | |
380 scoped_refptr<const ActionT> action = | |
381 ActionT::Create(browser_context, extension, **i, error, bad_message); | |
382 if (!error->empty() || *bad_message) | |
383 return scoped_ptr<DeclarativeActionSet>(); | |
384 result.push_back(action); | |
385 } | |
386 | |
387 return scoped_ptr<DeclarativeActionSet>(new DeclarativeActionSet(result)); | |
388 } | |
389 | |
390 template<typename ActionT> | |
391 void DeclarativeActionSet<ActionT>::Apply( | |
392 const std::string& extension_id, | |
393 const base::Time& extension_install_time, | |
394 typename ActionT::ApplyInfo* apply_info) const { | |
395 for (typename Actions::const_iterator i = actions_.begin(); | |
396 i != actions_.end(); ++i) | |
397 (*i)->Apply(extension_id, extension_install_time, apply_info); | |
398 } | |
399 | |
400 template<typename ActionT> | |
401 void DeclarativeActionSet<ActionT>::Reapply( | |
402 const std::string& extension_id, | |
403 const base::Time& extension_install_time, | |
404 typename ActionT::ApplyInfo* apply_info) const { | |
405 for (typename Actions::const_iterator i = actions_.begin(); | |
406 i != actions_.end(); ++i) | |
407 (*i)->Reapply(extension_id, extension_install_time, apply_info); | |
408 } | |
409 | |
410 template<typename ActionT> | |
411 void DeclarativeActionSet<ActionT>::Revert( | |
412 const std::string& extension_id, | |
413 const base::Time& extension_install_time, | |
414 typename ActionT::ApplyInfo* apply_info) const { | |
415 for (typename Actions::const_iterator i = actions_.begin(); | |
416 i != actions_.end(); ++i) | |
417 (*i)->Revert(extension_id, extension_install_time, apply_info); | |
418 } | |
419 | |
420 template<typename ActionT> | |
421 int DeclarativeActionSet<ActionT>::GetMinimumPriority() const { | |
422 int minimum_priority = std::numeric_limits<int>::min(); | |
423 for (typename Actions::const_iterator i = actions_.begin(); | |
424 i != actions_.end(); ++i) { | |
425 minimum_priority = std::max(minimum_priority, (*i)->minimum_priority()); | |
426 } | |
427 return minimum_priority; | |
428 } | |
429 | |
430 // | |
431 // DeclarativeRule | |
432 // | |
433 | |
434 template<typename ConditionT, typename ActionT> | |
435 DeclarativeRule<ConditionT, ActionT>::DeclarativeRule( | |
436 const GlobalRuleId& id, | |
437 const Tags& tags, | |
438 base::Time extension_installation_time, | |
439 scoped_ptr<ConditionSet> conditions, | |
440 scoped_ptr<ActionSet> actions, | |
441 Priority priority) | |
442 : id_(id), | |
443 tags_(tags), | |
444 extension_installation_time_(extension_installation_time), | |
445 conditions_(conditions.release()), | |
446 actions_(actions.release()), | |
447 priority_(priority) { | |
448 CHECK(conditions_.get()); | |
449 CHECK(actions_.get()); | |
450 } | |
451 | |
452 // static | |
453 template<typename ConditionT, typename ActionT> | |
454 scoped_ptr<DeclarativeRule<ConditionT, ActionT> > | |
455 DeclarativeRule<ConditionT, ActionT>::Create( | |
456 url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory, | |
457 content::BrowserContext* browser_context, | |
458 const Extension* extension, | |
459 base::Time extension_installation_time, | |
460 linked_ptr<JsonRule> rule, | |
461 ConsistencyChecker check_consistency, | |
462 std::string* error) { | |
463 scoped_ptr<DeclarativeRule> error_result; | |
464 | |
465 scoped_ptr<ConditionSet> conditions = ConditionSet::Create( | |
466 extension, url_matcher_condition_factory, rule->conditions, error); | |
467 if (!error->empty()) | |
468 return error_result.Pass(); | |
469 CHECK(conditions.get()); | |
470 | |
471 bool bad_message = false; | |
472 scoped_ptr<ActionSet> actions = | |
473 ActionSet::Create( | |
474 browser_context, extension, rule->actions, error, &bad_message); | |
475 if (bad_message) { | |
476 // TODO(battre) Export concept of bad_message to caller, the extension | |
477 // should be killed in case it is true. | |
478 *error = "An action of a rule set had an invalid " | |
479 "structure that should have been caught by the JSON validator."; | |
480 return error_result.Pass(); | |
481 } | |
482 if (!error->empty() || bad_message) | |
483 return error_result.Pass(); | |
484 CHECK(actions.get()); | |
485 | |
486 if (!check_consistency.is_null() && | |
487 !check_consistency.Run(conditions.get(), actions.get(), error)) { | |
488 DCHECK(!error->empty()); | |
489 return error_result.Pass(); | |
490 } | |
491 | |
492 CHECK(rule->priority.get()); | |
493 int priority = *(rule->priority); | |
494 | |
495 GlobalRuleId rule_id(extension->id(), *(rule->id)); | |
496 Tags tags = rule->tags ? *rule->tags : Tags(); | |
497 return scoped_ptr<DeclarativeRule>( | |
498 new DeclarativeRule(rule_id, tags, extension_installation_time, | |
499 conditions.Pass(), actions.Pass(), priority)); | |
500 } | |
501 | |
502 template<typename ConditionT, typename ActionT> | |
503 void DeclarativeRule<ConditionT, ActionT>::Apply( | |
504 typename ActionT::ApplyInfo* apply_info) const { | |
505 return actions_->Apply(extension_id(), | |
506 extension_installation_time_, | |
507 apply_info); | |
508 } | |
509 | |
510 template<typename ConditionT, typename ActionT> | |
511 int DeclarativeRule<ConditionT, ActionT>::GetMinimumPriority() const { | |
512 return actions_->GetMinimumPriority(); | |
513 } | |
514 | |
515 } // namespace extensions | |
516 | |
517 #endif // CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_RULE_H__ | |
OLD | NEW |