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