summaryrefslogtreecommitdiffstats
path: root/extensions/browser/api/declarative/declarative_rule.h
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/browser/api/declarative/declarative_rule.h')
-rw-r--r--extensions/browser/api/declarative/declarative_rule.h517
1 files changed, 517 insertions, 0 deletions
diff --git a/extensions/browser/api/declarative/declarative_rule.h b/extensions/browser/api/declarative/declarative_rule.h
new file mode 100644
index 0000000..b612f27
--- /dev/null
+++ b/extensions/browser/api/declarative/declarative_rule.h
@@ -0,0 +1,517 @@
+// Copyright (c) 2013 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 EXTENSIONS_BROWSER_API_DECLARATIVE_DECLARATIVE_RULE_H__
+#define EXTENSIONS_BROWSER_API_DECLARATIVE_DECLARATIVE_RULE_H__
+
+#include <limits>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/stl_util.h"
+#include "base/time/time.h"
+#include "components/url_matcher/url_matcher.h"
+#include "extensions/common/api/events.h"
+#include "extensions/common/extension.h"
+
+namespace base {
+class Time;
+class Value;
+}
+
+namespace content {
+class BrowserContext;
+}
+
+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.
+//
+// ConditionT should be immutable after creation. It must define the following
+// members:
+//
+// // Arguments passed through from DeclarativeConditionSet::Create.
+// static scoped_ptr<ConditionT> Create(
+// const Extension* extension,
+// URLMatcherConditionFactory* url_matcher_condition_factory,
+// // Except this argument gets elements of the AnyVector.
+// const base::Value& definition,
+// std::string* error);
+// // If the Condition needs to be filtered by some URLMatcherConditionSets,
+// // append them to |condition_sets|.
+// // DeclarativeConditionSet::GetURLMatcherConditionSets forwards here.
+// void GetURLMatcherConditionSets(
+// URLMatcherConditionSet::Vector* condition_sets);
+// // |match_data| passed through from DeclarativeConditionSet::IsFulfilled.
+// bool IsFulfilled(const ConditionT::MatchData& match_data);
+template<typename ConditionT>
+class DeclarativeConditionSet {
+ public:
+ typedef std::vector<linked_ptr<base::Value> > AnyVector;
+ typedef std::vector<linked_ptr<const ConditionT> > Conditions;
+ typedef typename Conditions::const_iterator const_iterator;
+
+ // Factory method that creates a DeclarativeConditionSet for |extension|
+ // 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(
+ const Extension* extension,
+ url_matcher::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(); }
+
+ // If |url_match_trigger| is not -1, this function looks for a condition
+ // with this URLMatcherConditionSet, and forwards to that condition's
+ // IsFulfilled(|match_data|). If there is no such condition, then false is
+ // returned. If |url_match_trigger| is -1, this function returns whether any
+ // of the conditions without URL attributes is satisfied.
+ bool IsFulfilled(url_matcher::URLMatcherConditionSet::ID url_match_trigger,
+ const typename ConditionT::MatchData& match_data) const;
+
+ // Appends the URLMatcherConditionSet from all conditions to |condition_sets|.
+ void GetURLMatcherConditionSets(
+ url_matcher::URLMatcherConditionSet::Vector* condition_sets) const;
+
+ // Returns whether there are some conditions without UrlFilter attributes.
+ bool HasConditionsWithoutUrls() const {
+ return !conditions_without_urls_.empty();
+ }
+
+ private:
+ typedef std::map<url_matcher::URLMatcherConditionSet::ID, const ConditionT*>
+ URLMatcherIdToCondition;
+
+ DeclarativeConditionSet(
+ const Conditions& conditions,
+ const URLMatcherIdToCondition& match_id_to_condition,
+ const std::vector<const ConditionT*>& conditions_without_urls);
+
+ const URLMatcherIdToCondition match_id_to_condition_;
+ const Conditions conditions_;
+ const std::vector<const ConditionT*> conditions_without_urls_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeclarativeConditionSet);
+};
+
+// Immutable container for multiple actions.
+//
+// ActionT should be immutable after creation. It must define the following
+// members:
+//
+// // Arguments passed through from ActionSet::Create.
+// static scoped_ptr<ActionT> Create(
+// const Extension* extension,
+// // Except this argument gets elements of the AnyVector.
+// const base::Value& definition,
+// std::string* error, bool* bad_message);
+// void Apply(const std::string& extension_id,
+// const base::Time& extension_install_time,
+// // Contains action-type-specific in/out parameters.
+// typename ActionT::ApplyInfo* apply_info) const;
+// // Only needed if the RulesRegistry calls DeclarativeActionSet::Revert().
+// void Revert(const std::string& extension_id,
+// const base::Time& extension_install_time,
+// // Contains action-type-specific in/out parameters.
+// typename ActionT::ApplyInfo* apply_info) const;
+// // Return the minimum priority of rules that can be evaluated after this
+// // action runs. A suitable default value is MIN_INT.
+// int minimum_priority() const;
+//
+// 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<base::Value> > AnyVector;
+ typedef std::vector<scoped_refptr<const ActionT> > Actions;
+
+ explicit DeclarativeActionSet(const Actions& actions);
+
+ // Factory method that instantiates a DeclarativeActionSet for |extension|
+ // according to |actions| which represents the array of actions received from
+ // the extension API.
+ static scoped_ptr<DeclarativeActionSet> Create(
+ content::BrowserContext* browser_context,
+ const Extension* extension,
+ 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,
+ typename ActionT::ApplyInfo* apply_info) const;
+
+ // Rules call this method when their conditions are fulfilled, but Apply has
+ // already been called.
+ void Reapply(const std::string& extension_id,
+ const base::Time& extension_install_time,
+ 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,
+ 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:
+ const Actions actions_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeclarativeActionSet);
+};
+
+// Representation of a rule of a declarative API:
+// https://developer.chrome.com/beta/extensions/events.html#declarative.
+// Generally a RulesRegistry will hold a collection of Rules for a given
+// declarative API and contain the logic for matching and applying them.
+//
+// See DeclarativeConditionSet and DeclarativeActionSet for the requirements on
+// ConditionT and ActionT.
+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::core_api::events::Rule JsonRule;
+ typedef std::vector<std::string> Tags;
+
+ // Checks whether the set of |conditions| and |actions| are consistent.
+ // Returns true in case of consistency and MUST set |error| otherwise.
+ typedef base::Callback<bool(const ConditionSet* conditions,
+ const ActionSet* actions,
+ std::string* error)> ConsistencyChecker;
+
+ DeclarativeRule(const GlobalRuleId& id,
+ const Tags& tags,
+ base::Time extension_installation_time,
+ scoped_ptr<ConditionSet> conditions,
+ scoped_ptr<ActionSet> actions,
+ Priority priority);
+
+ // Creates a DeclarativeRule for |extension| given a json definition. The
+ // format of each condition and action's json is up to the specific ConditionT
+ // and ActionT. |extension| may be NULL in tests.
+ //
+ // Before constructing the final rule, 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(
+ url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory,
+ content::BrowserContext* browser_context,
+ const Extension* extension,
+ base::Time extension_installation_time,
+ linked_ptr<JsonRule> rule,
+ ConsistencyChecker check_consistency,
+ std::string* error);
+
+ const GlobalRuleId& id() const { return id_; }
+ const Tags& tags() const { return tags_; }
+ 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_; }
+
+ // Calls actions().Apply(extension_id(), extension_installation_time_,
+ // apply_info). 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).
+ void Apply(typename ActionT::ApplyInfo* apply_info) const;
+
+ // Returns the minimum priority of rules that may be evaluated after
+ // this rule. Defaults to MIN_INT. Only valid if the conditions of this rule
+ // are fulfilled.
+ Priority GetMinimumPriority() const;
+
+ private:
+ GlobalRuleId id_;
+ Tags tags_;
+ 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>
+bool DeclarativeConditionSet<ConditionT>::IsFulfilled(
+ url_matcher::URLMatcherConditionSet::ID url_match_trigger,
+ const typename ConditionT::MatchData& match_data) const {
+ if (url_match_trigger == -1) {
+ // Invalid trigger -- indication that we should only check conditions
+ // without URL attributes.
+ for (typename std::vector<const ConditionT*>::const_iterator it =
+ conditions_without_urls_.begin();
+ it != conditions_without_urls_.end(); ++it) {
+ if ((*it)->IsFulfilled(match_data))
+ return true;
+ }
+ return false;
+ }
+
+ typename URLMatcherIdToCondition::const_iterator triggered =
+ match_id_to_condition_.find(url_match_trigger);
+ return (triggered != match_id_to_condition_.end() &&
+ triggered->second->IsFulfilled(match_data));
+}
+
+template<typename ConditionT>
+void DeclarativeConditionSet<ConditionT>::GetURLMatcherConditionSets(
+ url_matcher::URLMatcherConditionSet::Vector* condition_sets) const {
+ for (typename Conditions::const_iterator i = conditions_.begin();
+ i != conditions_.end(); ++i) {
+ (*i)->GetURLMatcherConditionSets(condition_sets);
+ }
+}
+
+// static
+template<typename ConditionT>
+scoped_ptr<DeclarativeConditionSet<ConditionT> >
+DeclarativeConditionSet<ConditionT>::Create(
+ const Extension* extension,
+ url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory,
+ const AnyVector& conditions,
+ std::string* error) {
+ Conditions result;
+
+ for (AnyVector::const_iterator i = conditions.begin();
+ i != conditions.end(); ++i) {
+ CHECK(i->get());
+ scoped_ptr<ConditionT> condition = ConditionT::Create(
+ extension, url_matcher_condition_factory, **i, error);
+ if (!error->empty())
+ return scoped_ptr<DeclarativeConditionSet>();
+ result.push_back(make_linked_ptr(condition.release()));
+ }
+
+ URLMatcherIdToCondition match_id_to_condition;
+ std::vector<const ConditionT*> conditions_without_urls;
+ url_matcher::URLMatcherConditionSet::Vector condition_sets;
+
+ for (typename Conditions::const_iterator i = result.begin();
+ i != result.end(); ++i) {
+ condition_sets.clear();
+ (*i)->GetURLMatcherConditionSets(&condition_sets);
+ if (condition_sets.empty()) {
+ conditions_without_urls.push_back(i->get());
+ } else {
+ for (url_matcher::URLMatcherConditionSet::Vector::const_iterator
+ match_set = condition_sets.begin();
+ match_set != condition_sets.end(); ++match_set)
+ match_id_to_condition[(*match_set)->id()] = i->get();
+ }
+ }
+
+ return make_scoped_ptr(new DeclarativeConditionSet(
+ result, match_id_to_condition, conditions_without_urls));
+}
+
+template<typename ConditionT>
+DeclarativeConditionSet<ConditionT>::DeclarativeConditionSet(
+ const Conditions& conditions,
+ const URLMatcherIdToCondition& match_id_to_condition,
+ const std::vector<const ConditionT*>& conditions_without_urls)
+ : match_id_to_condition_(match_id_to_condition),
+ conditions_(conditions),
+ conditions_without_urls_(conditions_without_urls) {}
+
+//
+// DeclarativeActionSet
+//
+
+template<typename ActionT>
+DeclarativeActionSet<ActionT>::DeclarativeActionSet(const Actions& actions)
+ : actions_(actions) {}
+
+// static
+template<typename ActionT>
+scoped_ptr<DeclarativeActionSet<ActionT> >
+DeclarativeActionSet<ActionT>::Create(
+ content::BrowserContext* browser_context,
+ const Extension* extension,
+ 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_refptr<const ActionT> action =
+ ActionT::Create(browser_context, extension, **i, error, bad_message);
+ if (!error->empty() || *bad_message)
+ return scoped_ptr<DeclarativeActionSet>();
+ result.push_back(action);
+ }
+
+ 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,
+ 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>::Reapply(
+ const std::string& extension_id,
+ const base::Time& extension_install_time,
+ typename ActionT::ApplyInfo* apply_info) const {
+ for (typename Actions::const_iterator i = actions_.begin();
+ i != actions_.end(); ++i)
+ (*i)->Reapply(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,
+ 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)->minimum_priority());
+ }
+ return minimum_priority;
+}
+
+//
+// DeclarativeRule
+//
+
+template<typename ConditionT, typename ActionT>
+DeclarativeRule<ConditionT, ActionT>::DeclarativeRule(
+ const GlobalRuleId& id,
+ const Tags& tags,
+ base::Time extension_installation_time,
+ scoped_ptr<ConditionSet> conditions,
+ scoped_ptr<ActionSet> actions,
+ Priority priority)
+ : id_(id),
+ tags_(tags),
+ 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(
+ url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory,
+ content::BrowserContext* browser_context,
+ const Extension* extension,
+ 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(
+ extension, 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(
+ browser_context, extension, 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.is_null() &&
+ !check_consistency.Run(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));
+ Tags tags = rule->tags ? *rule->tags : Tags();
+ return scoped_ptr<DeclarativeRule>(
+ new DeclarativeRule(rule_id, tags, extension_installation_time,
+ conditions.Pass(), actions.Pass(), priority));
+}
+
+template<typename ConditionT, typename ActionT>
+void DeclarativeRule<ConditionT, ActionT>::Apply(
+ 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 // EXTENSIONS_BROWSER_API_DECLARATIVE_DECLARATIVE_RULE_H__