OLD | NEW |
(Empty) | |
| 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. |
| 9 |
| 10 #ifndef CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_RULE_H__ |
| 11 #define CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_RULE_H__ |
| 12 |
| 13 #include <limits> |
| 14 |
| 15 #include "base/memory/linked_ptr.h" |
| 16 #include "base/memory/scoped_vector.h" |
| 17 #include "base/time.h" |
| 18 #include "chrome/common/extensions/api/events.h" |
| 19 #include "chrome/common/extensions/matcher/url_matcher.h" |
| 20 |
| 21 namespace base { |
| 22 class Time; |
| 23 } |
| 24 |
| 25 namespace extensions { |
| 26 |
| 27 // This class stores a set of conditions that may be part of a DeclarativeRule. |
| 28 // If any condition is fulfilled, the Actions of the DeclarativeRule can be |
| 29 // triggered. |
| 30 // |
| 31 // ConditionT should be immutable after creation. It must define the following |
| 32 // members: |
| 33 // |
| 34 // // Arguments passed through from ConditionSet::Create. |
| 35 // static scoped_ptr<ConditionT> Create( |
| 36 // URLMatcherConditionFactory*, |
| 37 // // Except this argument gets elements of the AnyVector. |
| 38 // const base::Value& definition, |
| 39 // std::string* error); |
| 40 // // If the Condition needs to be filtered by some |
| 41 // // URLMatcherConditionSets, append them to this argument. |
| 42 // // DeclarativeConditionSet::GetURLMatcherConditionSets forwards here. |
| 43 // void GetURLMatcherConditionSets( |
| 44 // URLMatcherConditionSet::Vector* condition_sets) |
| 45 // // True if GetURLMatcherConditionSets would append anything to its |
| 46 // // argument. |
| 47 // bool has_url_matcher_condition_set(); |
| 48 // // |url_matches| and |match_data| passed through from |
| 49 // // ConditionSet::IsFulfilled. |
| 50 // bool IsFulfilled( |
| 51 // const std::set<URLMatcherConditionSet::ID>& url_matches, |
| 52 // const ConditionT::MatchData&); |
| 53 template<typename ConditionT> |
| 54 class DeclarativeConditionSet { |
| 55 public: |
| 56 typedef std::vector<linked_ptr<json_schema_compiler::any::Any> > AnyVector; |
| 57 typedef std::vector<linked_ptr<const ConditionT> > Conditions; |
| 58 typedef typename Conditions::const_iterator const_iterator; |
| 59 |
| 60 // Factory method that creates an WebRequestConditionSet according to the JSON |
| 61 // array |conditions| passed by the extension API. |
| 62 // Sets |error| and returns NULL in case of an error. |
| 63 static scoped_ptr<DeclarativeConditionSet> Create( |
| 64 URLMatcherConditionFactory* url_matcher_condition_factory, |
| 65 const AnyVector& conditions, |
| 66 std::string* error); |
| 67 |
| 68 const Conditions& conditions() const { |
| 69 return conditions_; |
| 70 } |
| 71 |
| 72 const_iterator begin() const { return conditions_.begin(); } |
| 73 const_iterator end() const { return conditions_.end(); } |
| 74 |
| 75 // If |url_match_trigger| is a member of |url_matches|, then this |
| 76 // returns whether the corresponding condition is fulfilled |
| 77 // wrt. |request_data|. If |url_match_trigger| is -1, this function |
| 78 // returns whether any of the conditions without URL attributes is |
| 79 // satisfied. |
| 80 // |
| 81 // Conditions for which has_url_matcher_condition_set() is false are always |
| 82 // checked (aside from short-circuiting when an earlier condition already |
| 83 // matched.) |
| 84 // |
| 85 // Conditions for which has_url_matcher_condition_set() is true are only |
| 86 // checked if one of the URLMatcherConditionSets returned by |
| 87 // GetURLMatcherConditionSets() has an id listed in url_matches. That means |
| 88 // that if |match_data| contains URL matches for two pieces of a request, |
| 89 // their union should appear in |url_matches|. For kinds of MatchData that |
| 90 // only have one type of URL, |url_matches| is forwarded on to |
| 91 // ConditionT::IsFulfilled(), so it doesn't need to appear in |match_data|. |
| 92 bool IsFulfilled(URLMatcherConditionSet::ID url_match_trigger, |
| 93 const std::set<URLMatcherConditionSet::ID>& url_matches, |
| 94 const typename ConditionT::MatchData& match_data) const; |
| 95 |
| 96 // Appends the URLMatcherConditionSet from all conditions to |condition_sets|. |
| 97 void GetURLMatcherConditionSets( |
| 98 URLMatcherConditionSet::Vector* condition_sets) const; |
| 99 |
| 100 // Returns whether there are some conditions without UrlFilter attributes. |
| 101 bool HasConditionsWithoutUrls() const { |
| 102 return !conditions_without_urls_.empty(); |
| 103 } |
| 104 |
| 105 private: |
| 106 typedef std::map<URLMatcherConditionSet::ID, const ConditionT*> |
| 107 URLMatcherIdToCondition; |
| 108 |
| 109 DeclarativeConditionSet( |
| 110 const Conditions& conditions, |
| 111 const URLMatcherIdToCondition& match_id_to_condition, |
| 112 const std::vector<const ConditionT*>& conditions_without_urls); |
| 113 |
| 114 const URLMatcherIdToCondition match_id_to_condition_; |
| 115 const Conditions conditions_; |
| 116 const std::vector<const ConditionT*> conditions_without_urls_; |
| 117 |
| 118 DISALLOW_COPY_AND_ASSIGN(DeclarativeConditionSet); |
| 119 }; |
| 120 |
| 121 // Immutable container for multiple actions. |
| 122 // |
| 123 // ActionT should be immutable after creation. It must define the following |
| 124 // members: |
| 125 // |
| 126 // // Arguments passed through from ActionSet::Create. |
| 127 // static scoped_ptr<ActionT> Create( |
| 128 // // Except this argument gets elements of the AnyVector. |
| 129 // const base::Value& definition, |
| 130 // std::string* error, bool* bad_message); |
| 131 // void Apply(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 // // Only needed if the RulesRegistry calls DeclarativeActionSet::Revert(). |
| 136 // void Revert(const std::string& extension_id, |
| 137 // const base::Time& extension_install_time, |
| 138 // // Contains action-type-specific in/out parameters. |
| 139 // typename ActionT::ApplyInfo* apply_info) const; |
| 140 // // Return the minimum priority of rules that can be evaluated after this |
| 141 // // action runs. Return MIN_INT by default. |
| 142 // int GetMinimumPriority() const; |
| 143 // |
| 144 // TODO(battre): As DeclarativeActionSet can become the single owner of all |
| 145 // actions, we can optimize here by making some of them singletons (e.g. Cancel |
| 146 // actions). |
| 147 template<typename ActionT> |
| 148 class DeclarativeActionSet { |
| 149 public: |
| 150 typedef std::vector<linked_ptr<json_schema_compiler::any::Any> > AnyVector; |
| 151 typedef std::vector<linked_ptr<const ActionT> > Actions; |
| 152 |
| 153 explicit DeclarativeActionSet(const Actions& actions); |
| 154 |
| 155 // Factory method that instantiates a DeclarativeActionSet according to |
| 156 // |actions| which represents the array of actions received from the |
| 157 // extension API. |
| 158 static scoped_ptr<DeclarativeActionSet> Create(const AnyVector& actions, |
| 159 std::string* error, |
| 160 bool* bad_message); |
| 161 |
| 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; |
| 166 |
| 167 // Rules call this method when they have stateful conditions, and those |
| 168 // conditions stop being fulfilled. Rules with event-based conditions (e.g. a |
| 169 // network request happened) will never Revert() an action. |
| 170 void Revert(const std::string& extension_id, |
| 171 const base::Time& extension_install_time, |
| 172 typename ActionT::ApplyInfo* apply_info) const; |
| 173 |
| 174 // Returns the minimum priority of rules that may be evaluated after |
| 175 // this rule. Defaults to MIN_INT. |
| 176 int GetMinimumPriority() const; |
| 177 |
| 178 const Actions& actions() const { return actions_; } |
| 179 |
| 180 private: |
| 181 const Actions actions_; |
| 182 |
| 183 DISALLOW_COPY_AND_ASSIGN(DeclarativeActionSet); |
| 184 }; |
| 185 |
| 186 // Representation of a rule of a declarative API: |
| 187 // https://developer.chrome.com/beta/extensions/events.html#declarative. |
| 188 // Generally a RulesRegistry will hold a collection of Rules for a given |
| 189 // declarative API and contain the logic for matching and applying them. |
| 190 // |
| 191 // See DeclarativeConditionSet and DeclarativeActionSet for the requirements on |
| 192 // ConditionT and ActionT. |
| 193 template<typename ConditionT, typename ActionT> |
| 194 class DeclarativeRule { |
| 195 public: |
| 196 typedef std::string ExtensionId; |
| 197 typedef std::string RuleId; |
| 198 typedef std::pair<ExtensionId, RuleId> GlobalRuleId; |
| 199 typedef int Priority; |
| 200 typedef DeclarativeConditionSet<ConditionT> ConditionSet; |
| 201 typedef DeclarativeActionSet<ActionT> ActionSet; |
| 202 typedef extensions::api::events::Rule JsonRule; |
| 203 |
| 204 // Checks whether the set of |conditions| and |actions| are consistent. |
| 205 // Returns true in case of consistency and MUST set |error| otherwise. |
| 206 typedef bool (*ConsistencyChecker)(const ConditionSet* conditions, |
| 207 const ActionSet* actions, |
| 208 std::string* error); |
| 209 |
| 210 DeclarativeRule(const GlobalRuleId& id, |
| 211 base::Time extension_installation_time, |
| 212 scoped_ptr<ConditionSet> conditions, |
| 213 scoped_ptr<ActionSet> actions, |
| 214 Priority priority); |
| 215 |
| 216 // Creates a DeclarativeRule for an extension given a json definition. The |
| 217 // format of each condition and action's json is up to the specific ConditionT |
| 218 // and ActionT. |
| 219 // |
| 220 // Before constructing the final rule, calls check_consistency(conditions, |
| 221 // actions, error) and returns NULL if it fails. Pass NULL if no consistency |
| 222 // check is needed. If |error| is empty, the translation was successful and |
| 223 // the returned rule is internally consistent. |
| 224 static scoped_ptr<DeclarativeRule> Create( |
| 225 URLMatcherConditionFactory* url_matcher_condition_factory, |
| 226 const std::string& extension_id, |
| 227 base::Time extension_installation_time, |
| 228 linked_ptr<JsonRule> rule, |
| 229 ConsistencyChecker check_consistency, |
| 230 std::string* error); |
| 231 |
| 232 const GlobalRuleId& id() const { return id_; } |
| 233 const std::string& extension_id() const { return id_.first; } |
| 234 const ConditionSet& conditions() const { return *conditions_; } |
| 235 const ActionSet& actions() const { return *actions_; } |
| 236 Priority priority() const { return priority_; } |
| 237 |
| 238 // Calls actions().Apply(extension_id(), extension_installation_time_, |
| 239 // apply_info). This function should only be called when the conditions_ are |
| 240 // fulfilled (from a semantic point of view; no harm is done if this function |
| 241 // is called at other times for testing purposes). |
| 242 void Apply(typename ActionT::ApplyInfo* apply_info) const; |
| 243 |
| 244 // Returns the minimum priority of rules that may be evaluated after |
| 245 // this rule. Defaults to MIN_INT. Only valid if the conditions of this rule |
| 246 // are fulfilled. |
| 247 Priority GetMinimumPriority() const; |
| 248 |
| 249 private: |
| 250 GlobalRuleId id_; |
| 251 base::Time extension_installation_time_; // For precedences of rules. |
| 252 scoped_ptr<ConditionSet> conditions_; |
| 253 scoped_ptr<ActionSet> actions_; |
| 254 Priority priority_; |
| 255 |
| 256 DISALLOW_COPY_AND_ASSIGN(DeclarativeRule); |
| 257 }; |
| 258 |
| 259 // Implementation details below here. |
| 260 |
| 261 // |
| 262 // DeclarativeConditionSet |
| 263 // |
| 264 |
| 265 template<typename ConditionT> |
| 266 bool DeclarativeConditionSet<ConditionT>::IsFulfilled( |
| 267 URLMatcherConditionSet::ID url_match_trigger, |
| 268 const std::set<URLMatcherConditionSet::ID>& url_matches, |
| 269 const typename ConditionT::MatchData& match_data) const { |
| 270 if (url_match_trigger == -1) { |
| 271 // Invalid trigger -- indication that we should only check conditions |
| 272 // without URL attributes. |
| 273 for (typename std::vector<const ConditionT*>::const_iterator it = |
| 274 conditions_without_urls_.begin(); |
| 275 it != conditions_without_urls_.end(); ++it) { |
| 276 if ((*it)->IsFulfilled(url_matches, match_data)) |
| 277 return true; |
| 278 } |
| 279 return false; |
| 280 } |
| 281 |
| 282 typename URLMatcherIdToCondition::const_iterator triggered = |
| 283 match_id_to_condition_.find(url_match_trigger); |
| 284 return (triggered != match_id_to_condition_.end() && |
| 285 triggered->second->IsFulfilled(url_matches, match_data)); |
| 286 } |
| 287 |
| 288 template<typename ConditionT> |
| 289 void DeclarativeConditionSet<ConditionT>::GetURLMatcherConditionSets( |
| 290 URLMatcherConditionSet::Vector* condition_sets) const { |
| 291 for (typename Conditions::const_iterator i = conditions_.begin(); |
| 292 i != conditions_.end(); ++i) { |
| 293 (*i)->GetURLMatcherConditionSets(condition_sets); |
| 294 } |
| 295 } |
| 296 |
| 297 // static |
| 298 template<typename ConditionT> |
| 299 scoped_ptr<DeclarativeConditionSet<ConditionT> > |
| 300 DeclarativeConditionSet<ConditionT>::Create( |
| 301 URLMatcherConditionFactory* url_matcher_condition_factory, |
| 302 const AnyVector& conditions, |
| 303 std::string* error) { |
| 304 Conditions result; |
| 305 |
| 306 for (AnyVector::const_iterator i = conditions.begin(); |
| 307 i != conditions.end(); ++i) { |
| 308 CHECK(i->get()); |
| 309 scoped_ptr<ConditionT> condition = |
| 310 ConditionT::Create(url_matcher_condition_factory, |
| 311 (*i)->value(), error); |
| 312 if (!error->empty()) |
| 313 return scoped_ptr<DeclarativeConditionSet>(NULL); |
| 314 result.push_back(make_linked_ptr(condition.release())); |
| 315 } |
| 316 |
| 317 URLMatcherIdToCondition match_id_to_condition; |
| 318 std::vector<const ConditionT*> conditions_without_urls; |
| 319 URLMatcherConditionSet::Vector condition_sets; |
| 320 |
| 321 for (typename Conditions::const_iterator i = result.begin(); |
| 322 i != result.end(); ++i) { |
| 323 condition_sets.clear(); |
| 324 (*i)->GetURLMatcherConditionSets(&condition_sets); |
| 325 if (condition_sets.empty()) { |
| 326 conditions_without_urls.push_back(i->get()); |
| 327 } else { |
| 328 for (URLMatcherConditionSet::Vector::const_iterator |
| 329 match_set = condition_sets.begin(); |
| 330 match_set != condition_sets.end(); ++match_set) |
| 331 match_id_to_condition[(*match_set)->id()] = i->get(); |
| 332 } |
| 333 } |
| 334 |
| 335 return make_scoped_ptr(new DeclarativeConditionSet( |
| 336 result, match_id_to_condition, conditions_without_urls)); |
| 337 } |
| 338 |
| 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) {} |
| 347 |
| 348 // |
| 349 // DeclarativeActionSet |
| 350 // |
| 351 |
| 352 template<typename ActionT> |
| 353 DeclarativeActionSet<ActionT>::DeclarativeActionSet(const Actions& actions) |
| 354 : actions_(actions) {} |
| 355 |
| 356 // static |
| 357 template<typename ActionT> |
| 358 scoped_ptr<DeclarativeActionSet<ActionT> > |
| 359 DeclarativeActionSet<ActionT>::Create( |
| 360 const AnyVector& actions, |
| 361 std::string* error, |
| 362 bool* bad_message) { |
| 363 *error = ""; |
| 364 *bad_message = false; |
| 365 Actions result; |
| 366 |
| 367 for (AnyVector::const_iterator i = actions.begin(); |
| 368 i != actions.end(); ++i) { |
| 369 CHECK(i->get()); |
| 370 scoped_ptr<ActionT> action = |
| 371 ActionT::Create((*i)->value(), error, bad_message); |
| 372 if (!error->empty() || *bad_message) |
| 373 return scoped_ptr<DeclarativeActionSet>(NULL); |
| 374 result.push_back(make_linked_ptr(action.release())); |
| 375 } |
| 376 |
| 377 return scoped_ptr<DeclarativeActionSet>(new DeclarativeActionSet(result)); |
| 378 } |
| 379 |
| 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 (typename Actions::const_iterator i = actions_.begin(); |
| 386 i != actions_.end(); ++i) |
| 387 (*i)->Apply(extension_id, extension_install_time, apply_info); |
| 388 } |
| 389 |
| 390 template<typename ActionT> |
| 391 void DeclarativeActionSet<ActionT>::Revert( |
| 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)->Revert(extension_id, extension_install_time, apply_info); |
| 398 } |
| 399 |
| 400 template<typename ActionT> |
| 401 int DeclarativeActionSet<ActionT>::GetMinimumPriority() const { |
| 402 int minimum_priority = std::numeric_limits<int>::min(); |
| 403 for (typename Actions::const_iterator i = actions_.begin(); |
| 404 i != actions_.end(); ++i) { |
| 405 minimum_priority = std::max(minimum_priority, (*i)->GetMinimumPriority()); |
| 406 } |
| 407 return minimum_priority; |
| 408 } |
| 409 |
| 410 // |
| 411 // DeclarativeRule |
| 412 // |
| 413 |
| 414 template<typename ConditionT, typename ActionT> |
| 415 DeclarativeRule<ConditionT, ActionT>::DeclarativeRule( |
| 416 const GlobalRuleId& id, |
| 417 base::Time extension_installation_time, |
| 418 scoped_ptr<ConditionSet> conditions, |
| 419 scoped_ptr<ActionSet> actions, |
| 420 Priority priority) |
| 421 : id_(id), |
| 422 extension_installation_time_(extension_installation_time), |
| 423 conditions_(conditions.release()), |
| 424 actions_(actions.release()), |
| 425 priority_(priority) { |
| 426 CHECK(conditions_.get()); |
| 427 CHECK(actions_.get()); |
| 428 } |
| 429 |
| 430 // static |
| 431 template<typename ConditionT, typename ActionT> |
| 432 scoped_ptr<DeclarativeRule<ConditionT, ActionT> > |
| 433 DeclarativeRule<ConditionT, ActionT>::Create( |
| 434 URLMatcherConditionFactory* url_matcher_condition_factory, |
| 435 const std::string& extension_id, |
| 436 base::Time extension_installation_time, |
| 437 linked_ptr<JsonRule> rule, |
| 438 ConsistencyChecker check_consistency, |
| 439 std::string* error) { |
| 440 scoped_ptr<DeclarativeRule> error_result; |
| 441 |
| 442 scoped_ptr<ConditionSet> conditions = ConditionSet::Create( |
| 443 url_matcher_condition_factory, rule->conditions, error); |
| 444 if (!error->empty()) |
| 445 return error_result.Pass(); |
| 446 CHECK(conditions.get()); |
| 447 |
| 448 bool bad_message = false; |
| 449 scoped_ptr<ActionSet> actions = |
| 450 ActionSet::Create(rule->actions, error, &bad_message); |
| 451 if (bad_message) { |
| 452 // TODO(battre) Export concept of bad_message to caller, the extension |
| 453 // should be killed in case it is true. |
| 454 *error = "An action of a rule set had an invalid " |
| 455 "structure that should have been caught by the JSON validator."; |
| 456 return error_result.Pass(); |
| 457 } |
| 458 if (!error->empty() || bad_message) |
| 459 return error_result.Pass(); |
| 460 CHECK(actions.get()); |
| 461 |
| 462 if (check_consistency && |
| 463 !check_consistency(conditions.get(), actions.get(), error)) { |
| 464 DCHECK(!error->empty()); |
| 465 return error_result.Pass(); |
| 466 } |
| 467 |
| 468 CHECK(rule->priority.get()); |
| 469 int priority = *(rule->priority); |
| 470 |
| 471 GlobalRuleId rule_id(extension_id, *(rule->id)); |
| 472 return scoped_ptr<DeclarativeRule>( |
| 473 new DeclarativeRule(rule_id, extension_installation_time, |
| 474 conditions.Pass(), actions.Pass(), priority)); |
| 475 } |
| 476 |
| 477 template<typename ConditionT, typename ActionT> |
| 478 void DeclarativeRule<ConditionT, ActionT>::Apply( |
| 479 typename ActionT::ApplyInfo* apply_info) const { |
| 480 return actions_->Apply(extension_id(), |
| 481 extension_installation_time_, |
| 482 apply_info); |
| 483 } |
| 484 |
| 485 template<typename ConditionT, typename ActionT> |
| 486 int DeclarativeRule<ConditionT, ActionT>::GetMinimumPriority() const { |
| 487 return actions_->GetMinimumPriority(); |
| 488 } |
| 489 |
| 490 } // namespace extensions |
| 491 |
| 492 #endif // CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_DECLARATIVE_RULE_H__ |
OLD | NEW |