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