#ifndef EXTENSIONS_BROWSER_API_DECLARATIVE_DECLARATIVE_RULE_H__
#define EXTENSIONS_BROWSER_API_DECLARATIVE_DECLARATIVE_RULE_H__
#include <limits>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/url_matcher/url_matcher.h"
#include "extensions/common/api/events.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_id.h"
namespace base {
class Time;
}
namespace content {
class BrowserContext;
}
namespace extensions {
template<typename ConditionT>
class DeclarativeConditionSet {
public:
using Conditions = std::vector<std::unique_ptr<const ConditionT>>;
using const_iterator = typename Conditions::const_iterator;
DeclarativeConditionSet(const DeclarativeConditionSet&) = delete;
DeclarativeConditionSet& operator=(const DeclarativeConditionSet&) = delete;
static std::unique_ptr<DeclarativeConditionSet> Create(
const Extension* extension,
url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory,
const base::Value::List& condition_values,
std::string* error);
const Conditions& conditions() const {
return conditions_;
}
const_iterator begin() const { return conditions_.begin(); }
const_iterator end() const { return conditions_.end(); }
bool IsFulfilled(base::MatcherStringPattern::ID url_match_trigger,
const typename ConditionT::MatchData& match_data) const;
void GetURLMatcherConditionSets(
url_matcher::URLMatcherConditionSet::Vector* condition_sets) const;
bool HasConditionsWithoutUrls() const {
return !conditions_without_urls_.empty();
}
private:
using URLMatcherIdToCondition =
std::map<base::MatcherStringPattern::ID, const ConditionT*>;
DeclarativeConditionSet(
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_;
};
template<typename ActionT>
class DeclarativeActionSet {
public:
using Actions = std::vector<scoped_refptr<const ActionT>>;
explicit DeclarativeActionSet(const Actions& actions);
DeclarativeActionSet(const DeclarativeActionSet&) = delete;
DeclarativeActionSet& operator=(const DeclarativeActionSet&) = delete;
static std::unique_ptr<DeclarativeActionSet> Create(
content::BrowserContext* browser_context,
const Extension* extension,
const base::Value::List& action_values,
std::string* error,
bool* bad_message);
void Apply(const ExtensionId& extension_id,
const base::Time& extension_install_time,
typename ActionT::ApplyInfo* apply_info) const;
void Reapply(const ExtensionId& extension_id,
const base::Time& extension_install_time,
typename ActionT::ApplyInfo* apply_info) const;
void Revert(const ExtensionId& extension_id,
const base::Time& extension_install_time,
typename ActionT::ApplyInfo* apply_info) const;
int GetMinimumPriority() const;
const Actions& actions() const { return actions_; }
private:
const Actions actions_;
};
template<typename ConditionT, typename ActionT>
class DeclarativeRule {
public:
using RuleId = std::string;
using GlobalRuleId = std::pair<ExtensionId, RuleId>;
using Priority = int;
using ConditionSet = DeclarativeConditionSet<ConditionT>;
using ActionSet = DeclarativeActionSet<ActionT>;
using JsonRule = extensions::api::events::Rule;
using Tags = std::vector<std::string>;
using ConsistencyChecker =
base::OnceCallback<bool(const ConditionSet* conditions,
const ActionSet* actions,
std::string* error)>;
DeclarativeRule(const GlobalRuleId& id,
const Tags& tags,
base::Time extension_installation_time,
std::unique_ptr<ConditionSet> conditions,
std::unique_ptr<ActionSet> actions,
Priority priority);
DeclarativeRule(const DeclarativeRule&) = delete;
DeclarativeRule& operator=(const DeclarativeRule&) = delete;
static std::unique_ptr<DeclarativeRule> Create(
url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory,
content::BrowserContext* browser_context,
const Extension* extension,
base::Time extension_installation_time,
const JsonRule& rule,
ConsistencyChecker check_consistency,
std::string* error);
const GlobalRuleId& id() const { return id_; }
const Tags& tags() const { return tags_; }
const ExtensionId& extension_id() const { return id_.first; }
const ConditionSet& conditions() const { return *conditions_; }
const ActionSet& actions() const { return *actions_; }
Priority priority() const { return priority_; }
void Apply(typename ActionT::ApplyInfo* apply_info) const;
Priority GetMinimumPriority() const;
private:
GlobalRuleId id_;
Tags tags_;
base::Time extension_installation_time_;
std::unique_ptr<ConditionSet> conditions_;
std::unique_ptr<ActionSet> actions_;
Priority priority_;
};
template <typename ConditionT>
bool DeclarativeConditionSet<ConditionT>::IsFulfilled(
base::MatcherStringPattern::ID url_match_trigger,
const typename ConditionT::MatchData& match_data) const {
if (url_match_trigger == base::MatcherStringPattern::kInvalidId) {
for (const ConditionT* condition : conditions_without_urls_) {
if (condition->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 (const auto& condition : conditions_)
condition->GetURLMatcherConditionSets(condition_sets);
}
template <typename ConditionT>
std::unique_ptr<DeclarativeConditionSet<ConditionT>>
DeclarativeConditionSet<ConditionT>::Create(
const Extension* extension,
url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory,
const base::Value::List& condition_values,
std::string* error) {
Conditions result;
for (const auto& value : condition_values) {
std::unique_ptr<ConditionT> condition = ConditionT::Create(
extension, url_matcher_condition_factory, value, error);
if (!error->empty())
return nullptr;
result.push_back(std::move(condition));
}
URLMatcherIdToCondition match_id_to_condition;
std::vector<const ConditionT*> conditions_without_urls;
url_matcher::URLMatcherConditionSet::Vector condition_sets;
for (const auto& condition : result) {
condition_sets.clear();
condition->GetURLMatcherConditionSets(&condition_sets);
if (condition_sets.empty()) {
conditions_without_urls.push_back(condition.get());
} else {
for (const scoped_refptr<url_matcher::URLMatcherConditionSet>& match_set :
condition_sets)
match_id_to_condition[match_set->id()] = condition.get();
}
}
return base::WrapUnique(new DeclarativeConditionSet(
std::move(result), match_id_to_condition, conditions_without_urls));
}
template <typename ConditionT>
DeclarativeConditionSet<ConditionT>::DeclarativeConditionSet(
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_(std::move(conditions)),
conditions_without_urls_(conditions_without_urls) {}
template<typename ActionT>
DeclarativeActionSet<ActionT>::DeclarativeActionSet(const Actions& actions)
: actions_(actions) {}
template <typename ActionT>
std::unique_ptr<DeclarativeActionSet<ActionT>>
DeclarativeActionSet<ActionT>::Create(content::BrowserContext* browser_context,
const Extension* extension,
const base::Value::List& action_values,
std::string* error,
bool* bad_message) {
*error = "";
*bad_message = false;
Actions result;
for (const auto& value : action_values) {
if (!value.is_dict()) {
*bad_message = true;
*error = "Action must be an object.";
return nullptr;
}
scoped_refptr<const ActionT> action = ActionT::Create(
browser_context, extension, value.GetDict(), error, bad_message);
if (!error->empty() || *bad_message)
return nullptr;
result.push_back(action);
}
return std::make_unique<DeclarativeActionSet>(result);
}
template <typename ActionT>
void DeclarativeActionSet<ActionT>::Apply(
const ExtensionId& extension_id,
const base::Time& extension_install_time,
typename ActionT::ApplyInfo* apply_info) const {
for (const scoped_refptr<const ActionT>& action : actions_)
action->Apply(extension_id, extension_install_time, apply_info);
}
template <typename ActionT>
void DeclarativeActionSet<ActionT>::Reapply(
const ExtensionId& extension_id,
const base::Time& extension_install_time,
typename ActionT::ApplyInfo* apply_info) const {
for (const scoped_refptr<const ActionT>& action : actions_)
action->Reapply(extension_id, extension_install_time, apply_info);
}
template <typename ActionT>
void DeclarativeActionSet<ActionT>::Revert(
const ExtensionId& extension_id,
const base::Time& extension_install_time,
typename ActionT::ApplyInfo* apply_info) const {
for (const scoped_refptr<const ActionT>& action : actions_)
action->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;
}
template <typename ConditionT, typename ActionT>
DeclarativeRule<ConditionT, ActionT>::DeclarativeRule(
const GlobalRuleId& id,
const Tags& tags,
base::Time extension_installation_time,
std::unique_ptr<ConditionSet> conditions,
std::unique_ptr<ActionSet> actions,
Priority priority)
: id_(id),
tags_(tags),
extension_installation_time_(extension_installation_time),
conditions_(std::move(conditions)),
actions_(std::move(actions)),
priority_(priority) {
CHECK(conditions_.get());
CHECK(actions_.get());
}
template <typename ConditionT, typename ActionT>
std::unique_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,
const JsonRule& rule,
ConsistencyChecker check_consistency,
std::string* error) {
std::unique_ptr<DeclarativeRule> error_result;
std::unique_ptr<ConditionSet> conditions = ConditionSet::Create(
extension, url_matcher_condition_factory, rule.conditions, error);
if (!error->empty())
return std::move(error_result);
CHECK(conditions.get());
bool bad_message = false;
std::unique_ptr<ActionSet> actions = ActionSet::Create(
browser_context, extension, rule.actions, error, &bad_message);
if (bad_message) {
*error = "An action of a rule set had an invalid "
"structure that should have been caught by the JSON validator.";
return std::move(error_result);
}
if (!error->empty() || bad_message)
return std::move(error_result);
CHECK(actions.get());
if (!check_consistency.is_null() &&
!std::move(check_consistency)
.Run(conditions.get(), actions.get(), error)) {
DCHECK(!error->empty());
return std::move(error_result);
}
CHECK(rule.priority);
int priority = *(rule.priority);
GlobalRuleId rule_id(extension->id(), *(rule.id));
Tags tags = rule.tags ? *rule.tags : Tags();
return std::make_unique<DeclarativeRule>(
rule_id, tags, extension_installation_time, std::move(conditions),
std::move(actions), 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();
}
}
#endif