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