Chromium Code Reviews| Index: chrome/browser/extensions/api/declarative/declarative_rule.h |
| diff --git a/chrome/browser/extensions/api/declarative/declarative_rule.h b/chrome/browser/extensions/api/declarative/declarative_rule.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..4b073fc512e0ad052e23b47a76f3e061e8a7f093 |
| --- /dev/null |
| +++ b/chrome/browser/extensions/api/declarative/declarative_rule.h |
| @@ -0,0 +1,392 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| +// |
| +// DeclarativeRule<>, DeclarativeConditionSet<>, and DeclarativeActionSet<> |
| +// templates usable with multiple different declarativeFoo systems. These are |
| +// templated on the Condition and Action types that define the behavior of a |
| +// particular declarative event. |
| + |
| +#ifndef CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_RULE_H__ |
| +#define CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_RULE_H__ |
| + |
| +#include <limits> |
| + |
| +#include "base/memory/linked_ptr.h" |
| +#include "base/memory/scoped_vector.h" |
| +#include "base/time.h" |
| +#include "chrome/common/extensions/api/events.h" |
| +#include "chrome/common/extensions/matcher/url_matcher.h" |
| + |
| +namespace base { |
| +class Time; |
| +} |
| + |
| +namespace extensions { |
| + |
| +// This class stores a set of conditions that may be part of a DeclarativeRule. |
| +// If any condition is fulfilled, the Actions of the DeclarativeRule can be |
| +// triggered. |
| +template<typename ConditionT> |
|
Matt Perry
2012/12/13 20:27:48
Did you consider defining Condition and Action int
Jeffrey Yasskin
2012/12/14 00:38:20
Yes. It would be more verbose to implement, and bo
Matt Perry
2012/12/14 19:49:32
Fair enough :)
|
| +class DeclarativeConditionSet { |
| + public: |
| + typedef std::vector<linked_ptr<json_schema_compiler::any::Any> > AnyVector; |
| + typedef std::vector<linked_ptr<ConditionT> > Conditions; |
| + typedef typename Conditions::const_iterator const_iterator; |
| + |
| + explicit DeclarativeConditionSet(const Conditions& conditions); |
| + |
| + // Factory method that creates an WebRequestConditionSet according to the JSON |
| + // array |conditions| passed by the extension API. |
| + // Sets |error| and returns NULL in case of an error. |
| + static scoped_ptr<DeclarativeConditionSet> Create( |
| + URLMatcherConditionFactory* url_matcher_condition_factory, |
| + const AnyVector& conditions, |
| + std::string* error); |
| + |
| + const Conditions& conditions() const { |
| + return conditions_; |
| + } |
| + |
| + const_iterator begin() const { return conditions_.begin(); } |
| + const_iterator end() const { return conditions_.end(); } |
| + |
| + // Returns whether any condition in the condition set is fulfilled |
| + // based on a match |url_match| and the value of |request_data.request|. |
| + // This function should be called for each URLMatcherConditionSet::ID |
| + // that was found by the URLMatcher to ensure that the each trigger in |
| + // |match_triggers_| is found. |
| + bool IsFulfilled( |
| + URLMatcherConditionSet::ID url_match, |
| + const typename ConditionT::MatchData& match_data) const; |
| + |
| + // Appends the URLMatcherConditionSet from all conditions to |condition_sets|. |
| + void GetURLMatcherConditionSets( |
| + URLMatcherConditionSet::Vector* condition_sets) const; |
| + |
| + private: |
| + Conditions conditions_; |
| + |
| + typedef std::map<URLMatcherConditionSet::ID, ConditionT*> |
| + MatchTriggers; |
| + MatchTriggers match_triggers_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DeclarativeConditionSet); |
| +}; |
| + |
| +// Immutable container for multiple actions. |
| +// |
| +// TODO(battre): As DeclarativeActionSet can become the single owner of all |
| +// actions, we can optimize here by making some of them singletons (e.g. Cancel |
| +// actions). |
| +template<typename ActionT> |
| +class DeclarativeActionSet { |
| + public: |
| + typedef std::vector<linked_ptr<json_schema_compiler::any::Any> > AnyVector; |
| + typedef std::vector<linked_ptr<ActionT> > Actions; |
| + |
| + explicit DeclarativeActionSet(const Actions& actions); |
| + |
| + // Factory method that instantiates a DeclarativeActionSet according to |
| + // |actions| which represents the array of actions received from the |
| + // extension API. |
| + static scoped_ptr<DeclarativeActionSet> Create(const AnyVector& actions, |
| + std::string* error, |
| + bool* bad_message); |
| + |
| + // Rules call this method when their conditions are fulfilled. |
| + void Apply(const std::string& extension_id, |
| + const base::Time& extension_install_time, |
| + const typename ActionT::ApplyInfo& apply_info) const; |
| + |
| + // Rules call this method when they have stateful conditions, and those |
| + // conditions stop being fulfilled. Rules with event-based conditions (e.g. a |
| + // network request happened) will never Revert() an action. |
| + void Revert(const std::string& extension_id, |
| + const base::Time& extension_install_time, |
| + const typename ActionT::ApplyInfo& apply_info) const; |
| + |
| + // Returns the minimum priority of rules that may be evaluated after |
| + // this rule. Defaults to MIN_INT. |
| + int GetMinimumPriority() const; |
| + |
| + const Actions& actions() const { return actions_; } |
| + |
| + private: |
| + Actions actions_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DeclarativeActionSet); |
| +}; |
| + |
| +template<typename ConditionT, typename ActionT> |
| +class DeclarativeRule { |
| + public: |
| + typedef std::string ExtensionId; |
| + typedef std::string RuleId; |
| + typedef std::pair<ExtensionId, RuleId> GlobalRuleId; |
| + typedef int Priority; |
| + typedef DeclarativeConditionSet<ConditionT> ConditionSet; |
| + typedef DeclarativeActionSet<ActionT> ActionSet; |
| + typedef extensions::api::events::Rule JsonRule; |
| + |
| + // Checks whether the set of |conditions| and |actions| are consistent. |
| + // Returns true in case of consistency and MUST set |error| otherwise. |
| + typedef bool (*ConsistencyChecker)(ConditionSet* conditions, |
| + ActionSet* actions, |
| + std::string* error); |
| + |
| + DeclarativeRule(const GlobalRuleId& id, |
| + base::Time extension_installation_time, |
| + scoped_ptr<ConditionSet> conditions, |
| + scoped_ptr<ActionSet> actions, |
| + Priority priority); |
| + |
| + // Calls check_consistency(conditions, actions, error) and returns |
| + // NULL if it fails. Pass NULL if no consistency check is needed. |
| + // If |error| is empty, the translation was successful and the |
| + // returned rule is internally consistent. |
| + static scoped_ptr<DeclarativeRule> Create( |
| + URLMatcherConditionFactory* url_matcher_condition_factory, |
| + const std::string& extension_id, |
| + base::Time extension_installation_time, |
| + linked_ptr<JsonRule> rule, |
| + ConsistencyChecker check_consistency, |
| + std::string* error); |
| + |
| + const GlobalRuleId& id() const { return id_; } |
| + const std::string& extension_id() const { return id_.first; } |
| + const ConditionSet& conditions() const { return *conditions_; } |
| + const ActionSet& actions() const { return *actions_; } |
| + Priority priority() const { return priority_; } |
| + |
| + // Creates all deltas resulting from the ActionSet and fills them into |
| + // apply_info.deltas. This function should only be called when the conditions_ |
| + // are fulfilled (from a semantic point of view; no harm is done if this |
| + // function is called at other times for testing purposes). |
| + // |
| + // If |extension| is set, deltas are suppressed if the |extension| does not |
| + // have have sufficient permissions to modify the request. The returned list |
| + // may be empty in this case. |
| + void Apply(const typename ActionT::ApplyInfo& apply_info) const; |
| + |
| + // Returns the minimum priority of rules that may be evaluated after |
| + // this rule. Defaults to MAX_INT. Only valid if the conditions of this rule |
| + // are fulfilled. |
| + Priority GetMinimumPriority() const; |
| + |
| + private: |
| + GlobalRuleId id_; |
| + base::Time extension_installation_time_; // For precedences of rules. |
| + scoped_ptr<ConditionSet> conditions_; |
| + scoped_ptr<ActionSet> actions_; |
| + Priority priority_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DeclarativeRule); |
| +}; |
| + |
| +// Implementation details below here. |
| + |
| +// |
| +// DeclarativeConditionSet |
| +// |
| + |
| +template<typename ConditionT> |
| +DeclarativeConditionSet<ConditionT>::DeclarativeConditionSet( |
| + const DeclarativeConditionSet::Conditions& conditions) |
| + : conditions_(conditions) { |
| + for (typename Conditions::iterator i = conditions_.begin(); |
| + i != conditions_.end(); ++i) { |
| + URLMatcherConditionSet::ID trigger_id = |
| + (*i)->url_matcher_condition_set_id(); |
| + match_triggers_[trigger_id] = i->get(); |
| + } |
| +} |
| + |
| +template<typename ConditionT> |
| +bool DeclarativeConditionSet<ConditionT>::IsFulfilled( |
| + URLMatcherConditionSet::ID url_match, |
| + const typename ConditionT::MatchData& match_data) const { |
| + typename MatchTriggers::const_iterator trigger = |
| + match_triggers_.find(url_match); |
| + DCHECK(trigger != match_triggers_.end()); |
| + DCHECK_EQ(url_match, trigger->second->url_matcher_condition_set_id()); |
| + return trigger->second->IsFulfilled(match_data); |
| +} |
| + |
| +template<typename ConditionT> |
| +void DeclarativeConditionSet<ConditionT>::GetURLMatcherConditionSets( |
| + URLMatcherConditionSet::Vector* condition_sets) const { |
| + for (typename Conditions::const_iterator i = conditions_.begin(); |
| + i != conditions_.end(); ++i) { |
| + condition_sets->push_back((*i)->url_matcher_condition_set()); |
| + } |
| +} |
| + |
| +// static |
| +template<typename ConditionT> |
| +scoped_ptr<DeclarativeConditionSet<ConditionT> > |
| +DeclarativeConditionSet<ConditionT>::Create( |
| + URLMatcherConditionFactory* url_matcher_condition_factory, |
| + const AnyVector& conditions, |
| + std::string* error) { |
| + DeclarativeConditionSet::Conditions result; |
| + |
| + for (AnyVector::const_iterator i = conditions.begin(); |
| + i != conditions.end(); ++i) { |
| + CHECK(i->get()); |
| + scoped_ptr<ConditionT> condition = |
| + ConditionT::Create(url_matcher_condition_factory, |
| + (*i)->value(), error); |
| + if (!error->empty()) |
| + return scoped_ptr<DeclarativeConditionSet>(NULL); |
| + result.push_back(make_linked_ptr(condition.release())); |
| + } |
| + |
| + return make_scoped_ptr(new DeclarativeConditionSet(result)); |
| +} |
| + |
| +// |
| +// DeclarativeActionSet |
| +// |
| + |
| +template<typename ActionT> |
| +DeclarativeActionSet<ActionT>::DeclarativeActionSet(const Actions& actions) |
| + : actions_(actions) {} |
| + |
| +// static |
| +template<typename ActionT> |
| +scoped_ptr<DeclarativeActionSet<ActionT> > |
| +DeclarativeActionSet<ActionT>::Create( |
| + const AnyVector& actions, |
| + std::string* error, |
| + bool* bad_message) { |
| + *error = ""; |
| + *bad_message = false; |
| + Actions result; |
| + |
| + for (AnyVector::const_iterator i = actions.begin(); |
| + i != actions.end(); ++i) { |
| + CHECK(i->get()); |
| + scoped_ptr<ActionT> action = |
| + ActionT::Create((*i)->value(), error, bad_message); |
| + if (!error->empty() || *bad_message) |
| + return scoped_ptr<DeclarativeActionSet>(NULL); |
| + result.push_back(make_linked_ptr(action.release())); |
| + } |
| + |
| + return scoped_ptr<DeclarativeActionSet>(new DeclarativeActionSet(result)); |
| +} |
| + |
| +template<typename ActionT> |
| +void DeclarativeActionSet<ActionT>::Apply( |
| + const std::string& extension_id, |
| + const base::Time& extension_install_time, |
| + const typename ActionT::ApplyInfo& apply_info) const { |
| + for (typename Actions::const_iterator i = actions_.begin(); |
| + i != actions_.end(); ++i) |
| + (*i)->Apply(extension_id, extension_install_time, apply_info); |
| +} |
| + |
| +template<typename ActionT> |
| +void DeclarativeActionSet<ActionT>::Revert( |
| + const std::string& extension_id, |
| + const base::Time& extension_install_time, |
| + const typename ActionT::ApplyInfo& apply_info) const { |
| + for (typename Actions::const_iterator i = actions_.begin(); |
| + i != actions_.end(); ++i) |
| + (*i)->Revert(extension_id, extension_install_time, apply_info); |
| +} |
| + |
| +template<typename ActionT> |
| +int DeclarativeActionSet<ActionT>::GetMinimumPriority() const { |
| + int minimum_priority = std::numeric_limits<int>::min(); |
| + for (typename Actions::const_iterator i = actions_.begin(); |
| + i != actions_.end(); ++i) { |
| + minimum_priority = std::max(minimum_priority, (*i)->GetMinimumPriority()); |
| + } |
| + return minimum_priority; |
| +} |
| + |
| +// |
| +// DeclarativeRule |
| +// |
| + |
| +template<typename ConditionT, typename ActionT> |
| +DeclarativeRule<ConditionT, ActionT>::DeclarativeRule( |
| + const GlobalRuleId& id, |
| + base::Time extension_installation_time, |
| + scoped_ptr<ConditionSet> conditions, |
| + scoped_ptr<ActionSet> actions, |
| + Priority priority) |
| + : id_(id), |
| + extension_installation_time_(extension_installation_time), |
| + conditions_(conditions.release()), |
| + actions_(actions.release()), |
| + priority_(priority) { |
| + CHECK(conditions_.get()); |
| + CHECK(actions_.get()); |
| +} |
| + |
| +// static |
| +template<typename ConditionT, typename ActionT> |
| +scoped_ptr<DeclarativeRule<ConditionT, ActionT> > |
| +DeclarativeRule<ConditionT, ActionT>::Create( |
| + URLMatcherConditionFactory* url_matcher_condition_factory, |
| + const std::string& extension_id, |
| + base::Time extension_installation_time, |
| + linked_ptr<JsonRule> rule, |
| + ConsistencyChecker check_consistency, |
| + std::string* error) { |
| + scoped_ptr<DeclarativeRule> error_result; |
| + |
| + scoped_ptr<ConditionSet> conditions = ConditionSet::Create( |
| + url_matcher_condition_factory, rule->conditions, error); |
| + if (!error->empty()) |
| + return error_result.Pass(); |
| + CHECK(conditions.get()); |
| + |
| + bool bad_message = false; |
| + scoped_ptr<ActionSet> actions = |
| + ActionSet::Create(rule->actions, error, &bad_message); |
| + if (bad_message) { |
| + // TODO(battre) Export concept of bad_message to caller, the extension |
| + // should be killed in case it is true. |
| + *error = "An action of a rule set had an invalid " |
| + "structure that should have been caught by the JSON validator."; |
| + return error_result.Pass(); |
| + } |
| + if (!error->empty() || bad_message) |
| + return error_result.Pass(); |
| + CHECK(actions.get()); |
| + |
| + if (check_consistency && |
| + !check_consistency(conditions.get(), actions.get(), error)) { |
| + DCHECK(!error->empty()); |
| + return error_result.Pass(); |
| + } |
| + |
| + CHECK(rule->priority.get()); |
| + int priority = *(rule->priority); |
| + |
| + GlobalRuleId rule_id(extension_id, *(rule->id)); |
| + return scoped_ptr<DeclarativeRule>( |
| + new DeclarativeRule(rule_id, extension_installation_time, |
| + conditions.Pass(), actions.Pass(), priority)); |
| +} |
| + |
| +template<typename ConditionT, typename ActionT> |
| +void DeclarativeRule<ConditionT, ActionT>::Apply( |
| + const typename ActionT::ApplyInfo& apply_info) const { |
| + return actions_->Apply(extension_id(), |
| + extension_installation_time_, |
| + apply_info); |
| +} |
| + |
| +template<typename ConditionT, typename ActionT> |
| +int DeclarativeRule<ConditionT, ActionT>::GetMinimumPriority() const { |
| + return actions_->GetMinimumPriority(); |
| +} |
| + |
| +} // namespace extensions |
| + |
| +#endif // CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_RULE_H__ |