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