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