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