Unregister from GCM when the only GCM app is removed
[chromium-blink-merge.git] / extensions / browser / api / declarative / declarative_rule.h
blobb612f271d6407864c99400abb174e6b551bf8971
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"
27 namespace base {
28 class Time;
29 class Value;
32 namespace content {
33 class BrowserContext;
36 namespace extensions {
38 // This class stores a set of conditions that may be part of a DeclarativeRule.
39 // If any condition is fulfilled, the Actions of the DeclarativeRule can be
40 // triggered.
42 // ConditionT should be immutable after creation. It must define the following
43 // members:
45 // // Arguments passed through from DeclarativeConditionSet::Create.
46 // static scoped_ptr<ConditionT> Create(
47 // const Extension* extension,
48 // URLMatcherConditionFactory* url_matcher_condition_factory,
49 // // Except this argument gets elements of the AnyVector.
50 // const base::Value& definition,
51 // std::string* error);
52 // // If the Condition needs to be filtered by some URLMatcherConditionSets,
53 // // append them to |condition_sets|.
54 // // DeclarativeConditionSet::GetURLMatcherConditionSets forwards here.
55 // void GetURLMatcherConditionSets(
56 // URLMatcherConditionSet::Vector* condition_sets);
57 // // |match_data| passed through from DeclarativeConditionSet::IsFulfilled.
58 // bool IsFulfilled(const ConditionT::MatchData& match_data);
59 template<typename ConditionT>
60 class DeclarativeConditionSet {
61 public:
62 typedef std::vector<linked_ptr<base::Value> > AnyVector;
63 typedef std::vector<linked_ptr<const ConditionT> > Conditions;
64 typedef typename Conditions::const_iterator const_iterator;
66 // Factory method that creates a DeclarativeConditionSet for |extension|
67 // according to the JSON array |conditions| passed by the extension API. Sets
68 // |error| and returns NULL in case of an error.
69 static scoped_ptr<DeclarativeConditionSet> Create(
70 const Extension* extension,
71 url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory,
72 const AnyVector& conditions,
73 std::string* error);
75 const Conditions& conditions() const {
76 return conditions_;
79 const_iterator begin() const { return conditions_.begin(); }
80 const_iterator end() const { return conditions_.end(); }
82 // If |url_match_trigger| is not -1, this function looks for a condition
83 // with this URLMatcherConditionSet, and forwards to that condition's
84 // IsFulfilled(|match_data|). If there is no such condition, then false is
85 // returned. If |url_match_trigger| is -1, this function returns whether any
86 // of the conditions without URL attributes is satisfied.
87 bool IsFulfilled(url_matcher::URLMatcherConditionSet::ID url_match_trigger,
88 const typename ConditionT::MatchData& match_data) const;
90 // Appends the URLMatcherConditionSet from all conditions to |condition_sets|.
91 void GetURLMatcherConditionSets(
92 url_matcher::URLMatcherConditionSet::Vector* condition_sets) const;
94 // Returns whether there are some conditions without UrlFilter attributes.
95 bool HasConditionsWithoutUrls() const {
96 return !conditions_without_urls_.empty();
99 private:
100 typedef std::map<url_matcher::URLMatcherConditionSet::ID, const ConditionT*>
101 URLMatcherIdToCondition;
103 DeclarativeConditionSet(
104 const Conditions& conditions,
105 const URLMatcherIdToCondition& match_id_to_condition,
106 const std::vector<const ConditionT*>& conditions_without_urls);
108 const URLMatcherIdToCondition match_id_to_condition_;
109 const Conditions conditions_;
110 const std::vector<const ConditionT*> conditions_without_urls_;
112 DISALLOW_COPY_AND_ASSIGN(DeclarativeConditionSet);
115 // Immutable container for multiple actions.
117 // ActionT should be immutable after creation. It must define the following
118 // members:
120 // // Arguments passed through from ActionSet::Create.
121 // static scoped_ptr<ActionT> Create(
122 // const Extension* extension,
123 // // Except this argument gets elements of the AnyVector.
124 // const base::Value& definition,
125 // std::string* error, bool* bad_message);
126 // void Apply(const std::string& extension_id,
127 // const base::Time& extension_install_time,
128 // // Contains action-type-specific in/out parameters.
129 // typename ActionT::ApplyInfo* apply_info) const;
130 // // Only needed if the RulesRegistry calls DeclarativeActionSet::Revert().
131 // void Revert(const std::string& extension_id,
132 // const base::Time& extension_install_time,
133 // // Contains action-type-specific in/out parameters.
134 // typename ActionT::ApplyInfo* apply_info) const;
135 // // Return the minimum priority of rules that can be evaluated after this
136 // // action runs. A suitable default value is MIN_INT.
137 // int minimum_priority() const;
139 // TODO(battre): As DeclarativeActionSet can become the single owner of all
140 // actions, we can optimize here by making some of them singletons (e.g. Cancel
141 // actions).
142 template<typename ActionT>
143 class DeclarativeActionSet {
144 public:
145 typedef std::vector<linked_ptr<base::Value> > AnyVector;
146 typedef std::vector<scoped_refptr<const ActionT> > Actions;
148 explicit DeclarativeActionSet(const Actions& actions);
150 // Factory method that instantiates a DeclarativeActionSet for |extension|
151 // according to |actions| which represents the array of actions received from
152 // the extension API.
153 static scoped_ptr<DeclarativeActionSet> Create(
154 content::BrowserContext* browser_context,
155 const Extension* extension,
156 const AnyVector& actions,
157 std::string* error,
158 bool* bad_message);
160 // Rules call this method when their conditions are fulfilled.
161 void Apply(const std::string& extension_id,
162 const base::Time& extension_install_time,
163 typename ActionT::ApplyInfo* apply_info) const;
165 // Rules call this method when their conditions are fulfilled, but Apply has
166 // already been called.
167 void Reapply(const std::string& extension_id,
168 const base::Time& extension_install_time,
169 typename ActionT::ApplyInfo* apply_info) const;
171 // Rules call this method when they have stateful conditions, and those
172 // conditions stop being fulfilled. Rules with event-based conditions (e.g. a
173 // network request happened) will never Revert() an action.
174 void Revert(const std::string& extension_id,
175 const base::Time& extension_install_time,
176 typename ActionT::ApplyInfo* apply_info) const;
178 // Returns the minimum priority of rules that may be evaluated after
179 // this rule. Defaults to MIN_INT.
180 int GetMinimumPriority() const;
182 const Actions& actions() const { return actions_; }
184 private:
185 const Actions actions_;
187 DISALLOW_COPY_AND_ASSIGN(DeclarativeActionSet);
190 // Representation of a rule of a declarative API:
191 // https://developer.chrome.com/beta/extensions/events.html#declarative.
192 // Generally a RulesRegistry will hold a collection of Rules for a given
193 // declarative API and contain the logic for matching and applying them.
195 // See DeclarativeConditionSet and DeclarativeActionSet for the requirements on
196 // ConditionT and ActionT.
197 template<typename ConditionT, typename ActionT>
198 class DeclarativeRule {
199 public:
200 typedef std::string ExtensionId;
201 typedef std::string RuleId;
202 typedef std::pair<ExtensionId, RuleId> GlobalRuleId;
203 typedef int Priority;
204 typedef DeclarativeConditionSet<ConditionT> ConditionSet;
205 typedef DeclarativeActionSet<ActionT> ActionSet;
206 typedef extensions::core_api::events::Rule JsonRule;
207 typedef std::vector<std::string> Tags;
209 // Checks whether the set of |conditions| and |actions| are consistent.
210 // Returns true in case of consistency and MUST set |error| otherwise.
211 typedef base::Callback<bool(const ConditionSet* conditions,
212 const ActionSet* actions,
213 std::string* error)> ConsistencyChecker;
215 DeclarativeRule(const GlobalRuleId& id,
216 const Tags& tags,
217 base::Time extension_installation_time,
218 scoped_ptr<ConditionSet> conditions,
219 scoped_ptr<ActionSet> actions,
220 Priority priority);
222 // Creates a DeclarativeRule for |extension| given a json definition. The
223 // format of each condition and action's json is up to the specific ConditionT
224 // and ActionT. |extension| may be NULL in tests.
226 // Before constructing the final rule, calls check_consistency(conditions,
227 // actions, error) and returns NULL if it fails. Pass NULL if no consistency
228 // check is needed. If |error| is empty, the translation was successful and
229 // the returned rule is internally consistent.
230 static scoped_ptr<DeclarativeRule> Create(
231 url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory,
232 content::BrowserContext* browser_context,
233 const Extension* extension,
234 base::Time extension_installation_time,
235 linked_ptr<JsonRule> rule,
236 ConsistencyChecker check_consistency,
237 std::string* error);
239 const GlobalRuleId& id() const { return id_; }
240 const Tags& tags() const { return tags_; }
241 const std::string& extension_id() const { return id_.first; }
242 const ConditionSet& conditions() const { return *conditions_; }
243 const ActionSet& actions() const { return *actions_; }
244 Priority priority() const { return priority_; }
246 // Calls actions().Apply(extension_id(), extension_installation_time_,
247 // apply_info). This function should only be called when the conditions_ are
248 // fulfilled (from a semantic point of view; no harm is done if this function
249 // is called at other times for testing purposes).
250 void Apply(typename ActionT::ApplyInfo* apply_info) const;
252 // Returns the minimum priority of rules that may be evaluated after
253 // this rule. Defaults to MIN_INT. Only valid if the conditions of this rule
254 // are fulfilled.
255 Priority GetMinimumPriority() const;
257 private:
258 GlobalRuleId id_;
259 Tags tags_;
260 base::Time extension_installation_time_; // For precedences of rules.
261 scoped_ptr<ConditionSet> conditions_;
262 scoped_ptr<ActionSet> actions_;
263 Priority priority_;
265 DISALLOW_COPY_AND_ASSIGN(DeclarativeRule);
268 // Implementation details below here.
271 // DeclarativeConditionSet
274 template<typename ConditionT>
275 bool DeclarativeConditionSet<ConditionT>::IsFulfilled(
276 url_matcher::URLMatcherConditionSet::ID url_match_trigger,
277 const typename ConditionT::MatchData& match_data) const {
278 if (url_match_trigger == -1) {
279 // Invalid trigger -- indication that we should only check conditions
280 // without URL attributes.
281 for (typename std::vector<const ConditionT*>::const_iterator it =
282 conditions_without_urls_.begin();
283 it != conditions_without_urls_.end(); ++it) {
284 if ((*it)->IsFulfilled(match_data))
285 return true;
287 return false;
290 typename URLMatcherIdToCondition::const_iterator triggered =
291 match_id_to_condition_.find(url_match_trigger);
292 return (triggered != match_id_to_condition_.end() &&
293 triggered->second->IsFulfilled(match_data));
296 template<typename ConditionT>
297 void DeclarativeConditionSet<ConditionT>::GetURLMatcherConditionSets(
298 url_matcher::URLMatcherConditionSet::Vector* condition_sets) const {
299 for (typename Conditions::const_iterator i = conditions_.begin();
300 i != conditions_.end(); ++i) {
301 (*i)->GetURLMatcherConditionSets(condition_sets);
305 // static
306 template<typename ConditionT>
307 scoped_ptr<DeclarativeConditionSet<ConditionT> >
308 DeclarativeConditionSet<ConditionT>::Create(
309 const Extension* extension,
310 url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory,
311 const AnyVector& conditions,
312 std::string* error) {
313 Conditions result;
315 for (AnyVector::const_iterator i = conditions.begin();
316 i != conditions.end(); ++i) {
317 CHECK(i->get());
318 scoped_ptr<ConditionT> condition = ConditionT::Create(
319 extension, url_matcher_condition_factory, **i, error);
320 if (!error->empty())
321 return scoped_ptr<DeclarativeConditionSet>();
322 result.push_back(make_linked_ptr(condition.release()));
325 URLMatcherIdToCondition match_id_to_condition;
326 std::vector<const ConditionT*> conditions_without_urls;
327 url_matcher::URLMatcherConditionSet::Vector condition_sets;
329 for (typename Conditions::const_iterator i = result.begin();
330 i != result.end(); ++i) {
331 condition_sets.clear();
332 (*i)->GetURLMatcherConditionSets(&condition_sets);
333 if (condition_sets.empty()) {
334 conditions_without_urls.push_back(i->get());
335 } else {
336 for (url_matcher::URLMatcherConditionSet::Vector::const_iterator
337 match_set = condition_sets.begin();
338 match_set != condition_sets.end(); ++match_set)
339 match_id_to_condition[(*match_set)->id()] = i->get();
343 return make_scoped_ptr(new DeclarativeConditionSet(
344 result, match_id_to_condition, conditions_without_urls));
347 template<typename ConditionT>
348 DeclarativeConditionSet<ConditionT>::DeclarativeConditionSet(
349 const Conditions& conditions,
350 const URLMatcherIdToCondition& match_id_to_condition,
351 const std::vector<const ConditionT*>& conditions_without_urls)
352 : match_id_to_condition_(match_id_to_condition),
353 conditions_(conditions),
354 conditions_without_urls_(conditions_without_urls) {}
357 // DeclarativeActionSet
360 template<typename ActionT>
361 DeclarativeActionSet<ActionT>::DeclarativeActionSet(const Actions& actions)
362 : actions_(actions) {}
364 // static
365 template<typename ActionT>
366 scoped_ptr<DeclarativeActionSet<ActionT> >
367 DeclarativeActionSet<ActionT>::Create(
368 content::BrowserContext* browser_context,
369 const Extension* extension,
370 const AnyVector& actions,
371 std::string* error,
372 bool* bad_message) {
373 *error = "";
374 *bad_message = false;
375 Actions result;
377 for (AnyVector::const_iterator i = actions.begin();
378 i != actions.end(); ++i) {
379 CHECK(i->get());
380 scoped_refptr<const ActionT> action =
381 ActionT::Create(browser_context, extension, **i, error, bad_message);
382 if (!error->empty() || *bad_message)
383 return scoped_ptr<DeclarativeActionSet>();
384 result.push_back(action);
387 return scoped_ptr<DeclarativeActionSet>(new DeclarativeActionSet(result));
390 template<typename ActionT>
391 void DeclarativeActionSet<ActionT>::Apply(
392 const std::string& extension_id,
393 const base::Time& extension_install_time,
394 typename ActionT::ApplyInfo* apply_info) const {
395 for (typename Actions::const_iterator i = actions_.begin();
396 i != actions_.end(); ++i)
397 (*i)->Apply(extension_id, extension_install_time, apply_info);
400 template<typename ActionT>
401 void DeclarativeActionSet<ActionT>::Reapply(
402 const std::string& extension_id,
403 const base::Time& extension_install_time,
404 typename ActionT::ApplyInfo* apply_info) const {
405 for (typename Actions::const_iterator i = actions_.begin();
406 i != actions_.end(); ++i)
407 (*i)->Reapply(extension_id, extension_install_time, apply_info);
410 template<typename ActionT>
411 void DeclarativeActionSet<ActionT>::Revert(
412 const std::string& extension_id,
413 const base::Time& extension_install_time,
414 typename ActionT::ApplyInfo* apply_info) const {
415 for (typename Actions::const_iterator i = actions_.begin();
416 i != actions_.end(); ++i)
417 (*i)->Revert(extension_id, extension_install_time, apply_info);
420 template<typename ActionT>
421 int DeclarativeActionSet<ActionT>::GetMinimumPriority() const {
422 int minimum_priority = std::numeric_limits<int>::min();
423 for (typename Actions::const_iterator i = actions_.begin();
424 i != actions_.end(); ++i) {
425 minimum_priority = std::max(minimum_priority, (*i)->minimum_priority());
427 return minimum_priority;
431 // DeclarativeRule
434 template<typename ConditionT, typename ActionT>
435 DeclarativeRule<ConditionT, ActionT>::DeclarativeRule(
436 const GlobalRuleId& id,
437 const Tags& tags,
438 base::Time extension_installation_time,
439 scoped_ptr<ConditionSet> conditions,
440 scoped_ptr<ActionSet> actions,
441 Priority priority)
442 : id_(id),
443 tags_(tags),
444 extension_installation_time_(extension_installation_time),
445 conditions_(conditions.release()),
446 actions_(actions.release()),
447 priority_(priority) {
448 CHECK(conditions_.get());
449 CHECK(actions_.get());
452 // static
453 template<typename ConditionT, typename ActionT>
454 scoped_ptr<DeclarativeRule<ConditionT, ActionT> >
455 DeclarativeRule<ConditionT, ActionT>::Create(
456 url_matcher::URLMatcherConditionFactory* url_matcher_condition_factory,
457 content::BrowserContext* browser_context,
458 const Extension* extension,
459 base::Time extension_installation_time,
460 linked_ptr<JsonRule> rule,
461 ConsistencyChecker check_consistency,
462 std::string* error) {
463 scoped_ptr<DeclarativeRule> error_result;
465 scoped_ptr<ConditionSet> conditions = ConditionSet::Create(
466 extension, url_matcher_condition_factory, rule->conditions, error);
467 if (!error->empty())
468 return error_result.Pass();
469 CHECK(conditions.get());
471 bool bad_message = false;
472 scoped_ptr<ActionSet> actions =
473 ActionSet::Create(
474 browser_context, extension, rule->actions, error, &bad_message);
475 if (bad_message) {
476 // TODO(battre) Export concept of bad_message to caller, the extension
477 // should be killed in case it is true.
478 *error = "An action of a rule set had an invalid "
479 "structure that should have been caught by the JSON validator.";
480 return error_result.Pass();
482 if (!error->empty() || bad_message)
483 return error_result.Pass();
484 CHECK(actions.get());
486 if (!check_consistency.is_null() &&
487 !check_consistency.Run(conditions.get(), actions.get(), error)) {
488 DCHECK(!error->empty());
489 return error_result.Pass();
492 CHECK(rule->priority.get());
493 int priority = *(rule->priority);
495 GlobalRuleId rule_id(extension->id(), *(rule->id));
496 Tags tags = rule->tags ? *rule->tags : Tags();
497 return scoped_ptr<DeclarativeRule>(
498 new DeclarativeRule(rule_id, tags, extension_installation_time,
499 conditions.Pass(), actions.Pass(), priority));
502 template<typename ConditionT, typename ActionT>
503 void DeclarativeRule<ConditionT, ActionT>::Apply(
504 typename ActionT::ApplyInfo* apply_info) const {
505 return actions_->Apply(extension_id(),
506 extension_installation_time_,
507 apply_info);
510 template<typename ConditionT, typename ActionT>
511 int DeclarativeRule<ConditionT, ActionT>::GetMinimumPriority() const {
512 return actions_->GetMinimumPriority();
515 } // namespace extensions
517 #endif // EXTENSIONS_BROWSER_API_DECLARATIVE_DECLARATIVE_RULE_H__