Home | History | Annotate | Download | only in declarative
      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 
     71   const Conditions& conditions() const {
     72     return conditions_;
     73   }
     74 
     75   const_iterator begin() const { return conditions_.begin(); }
     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.
     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 
    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 
    226   const GlobalRuleId& id() const { return id_; }
    227   const Tags& tags() const { return tags_; }
    228   const std::string& extension_id() const { return id_.first; }
    229   const ConditionSet& conditions() const { return *conditions_; }
    230   const ActionSet& actions() const { return *actions_; }
    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>
    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>
    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> >
    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>
    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>
    348 DeclarativeActionSet<ActionT>::DeclarativeActionSet(const Actions& actions)
    349     : actions_(actions) {}
    350 
    351 // static
    352 template<typename ActionT>
    353 scoped_ptr<DeclarativeActionSet<ActionT> >
    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>
    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>
    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>
    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>
    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> >
    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>
    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>
    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