Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(133)

Side by Side Diff: chrome/browser/extensions/api/declarative/declarative_rule.h

Issue 11572061: Create DeclarativeConditionSet, DeclarativeActionSet, and DeclarativeRule templates (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix 2 simple bugs Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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__
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698