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