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.
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__
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"
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
41 // ConditionT should be immutable after creation. It must define the following
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
{
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
,
74 const Conditions
& conditions() const {
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();
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
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
141 template<typename ActionT
>
142 class DeclarativeActionSet
{
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
,
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_
; }
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
{
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
,
216 base::Time extension_installation_time
,
217 scoped_ptr
<ConditionSet
> conditions
,
218 scoped_ptr
<ActionSet
> actions
,
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
,
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
254 Priority
GetMinimumPriority() const;
259 base::Time extension_installation_time_
; // For precedences of rules.
260 scoped_ptr
<ConditionSet
> conditions_
;
261 scoped_ptr
<ActionSet
> actions_
;
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
))
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
);
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
) {
310 for (const linked_ptr
<base::Value
>& value
: condition_values
) {
312 scoped_ptr
<ConditionT
> condition
= ConditionT::Create(
313 extension
, url_matcher_condition_factory
, *value
, error
);
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());
329 for (const scoped_refptr
<url_matcher::URLMatcherConditionSet
>& match_set
:
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
) {}
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
,
365 *bad_message
= false;
368 for (const linked_ptr
<base::Value
>& value
: action_values
) {
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
;
421 template<typename ConditionT
, typename ActionT
>
422 DeclarativeRule
<ConditionT
, ActionT
>::DeclarativeRule(
423 const GlobalRuleId
& id
,
425 base::Time extension_installation_time
,
426 scoped_ptr
<ConditionSet
> conditions
,
427 scoped_ptr
<ActionSet
> actions
,
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());
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
);
455 return error_result
.Pass();
456 CHECK(conditions
.get());
458 bool bad_message
= false;
459 scoped_ptr
<ActionSet
> actions
=
461 browser_context
, extension
, rule
->actions
, error
, &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_
,
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__