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