| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_
registry.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <limits> | |
| 9 #include <utility> | |
| 10 | |
| 11 #include "base/bind.h" | |
| 12 #include "base/stl_util.h" | |
| 13 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h" | |
| 14 #include "chrome/browser/extensions/api/web_request/web_request_permissions.h" | |
| 15 #include "extensions/browser/api/declarative_webrequest/webrequest_condition.h" | |
| 16 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h" | |
| 17 #include "extensions/browser/extension_system.h" | |
| 18 #include "extensions/common/error_utils.h" | |
| 19 #include "extensions/common/extension.h" | |
| 20 #include "extensions/common/permissions/permissions_data.h" | |
| 21 #include "net/url_request/url_request.h" | |
| 22 | |
| 23 using url_matcher::URLMatcherConditionSet; | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 const char kActionCannotBeExecuted[] = "The action '*' can never be executed " | |
| 28 "because there are is no time in the request life-cycle during which the " | |
| 29 "conditions can be checked and the action can possibly be executed."; | |
| 30 | |
| 31 const char kAllURLsPermissionNeeded[] = | |
| 32 "To execute the action '*', you need to request host permission for all " | |
| 33 "hosts."; | |
| 34 | |
| 35 } // namespace | |
| 36 | |
| 37 namespace extensions { | |
| 38 | |
| 39 WebRequestRulesRegistry::WebRequestRulesRegistry( | |
| 40 content::BrowserContext* browser_context, | |
| 41 RulesCacheDelegate* cache_delegate, | |
| 42 const WebViewKey& webview_key) | |
| 43 : RulesRegistry(browser_context, | |
| 44 declarative_webrequest_constants::kOnRequest, | |
| 45 content::BrowserThread::IO, | |
| 46 cache_delegate, | |
| 47 webview_key), | |
| 48 browser_context_(browser_context) { | |
| 49 if (browser_context_) | |
| 50 extension_info_map_ = ExtensionSystem::Get(browser_context_)->info_map(); | |
| 51 } | |
| 52 | |
| 53 std::set<const WebRequestRule*> WebRequestRulesRegistry::GetMatches( | |
| 54 const WebRequestData& request_data_without_ids) const { | |
| 55 RuleSet result; | |
| 56 | |
| 57 WebRequestDataWithMatchIds request_data(&request_data_without_ids); | |
| 58 request_data.url_match_ids = url_matcher_.MatchURL( | |
| 59 request_data.data->request->url()); | |
| 60 request_data.first_party_url_match_ids = url_matcher_.MatchURL( | |
| 61 request_data.data->request->first_party_for_cookies()); | |
| 62 | |
| 63 // 1st phase -- add all rules with some conditions without UrlFilter | |
| 64 // attributes. | |
| 65 for (RuleSet::const_iterator it = rules_with_untriggered_conditions_.begin(); | |
| 66 it != rules_with_untriggered_conditions_.end(); ++it) { | |
| 67 if ((*it)->conditions().IsFulfilled(-1, request_data)) | |
| 68 result.insert(*it); | |
| 69 } | |
| 70 | |
| 71 // 2nd phase -- add all rules with some conditions triggered by URL matches. | |
| 72 AddTriggeredRules(request_data.url_match_ids, request_data, &result); | |
| 73 AddTriggeredRules(request_data.first_party_url_match_ids, | |
| 74 request_data, &result); | |
| 75 | |
| 76 return result; | |
| 77 } | |
| 78 | |
| 79 std::list<LinkedPtrEventResponseDelta> WebRequestRulesRegistry::CreateDeltas( | |
| 80 const InfoMap* extension_info_map, | |
| 81 const WebRequestData& request_data, | |
| 82 bool crosses_incognito) { | |
| 83 if (webrequest_rules_.empty()) | |
| 84 return std::list<LinkedPtrEventResponseDelta>(); | |
| 85 | |
| 86 std::set<const WebRequestRule*> matches = GetMatches(request_data); | |
| 87 | |
| 88 // Sort all matching rules by their priority so that they can be processed | |
| 89 // in decreasing order. | |
| 90 typedef std::pair<WebRequestRule::Priority, WebRequestRule::GlobalRuleId> | |
| 91 PriorityRuleIdPair; | |
| 92 std::vector<PriorityRuleIdPair> ordered_matches; | |
| 93 ordered_matches.reserve(matches.size()); | |
| 94 for (std::set<const WebRequestRule*>::iterator i = matches.begin(); | |
| 95 i != matches.end(); ++i) { | |
| 96 ordered_matches.push_back(make_pair((*i)->priority(), (*i)->id())); | |
| 97 } | |
| 98 // Sort from rbegin to rend in order to get descending priority order. | |
| 99 std::sort(ordered_matches.rbegin(), ordered_matches.rend()); | |
| 100 | |
| 101 // Build a map that maps each extension id to the minimum required priority | |
| 102 // for rules of that extension. Initially, this priority is -infinite and | |
| 103 // will be increased when the rules are processed and raise the bar via | |
| 104 // WebRequestIgnoreRulesActions. | |
| 105 typedef std::string ExtensionId; | |
| 106 typedef std::map<ExtensionId, WebRequestRule::Priority> MinPriorities; | |
| 107 typedef std::map<ExtensionId, std::set<std::string> > IgnoreTags; | |
| 108 MinPriorities min_priorities; | |
| 109 IgnoreTags ignore_tags; | |
| 110 for (std::vector<PriorityRuleIdPair>::iterator i = ordered_matches.begin(); | |
| 111 i != ordered_matches.end(); ++i) { | |
| 112 const WebRequestRule::GlobalRuleId& rule_id = i->second; | |
| 113 const ExtensionId& extension_id = rule_id.first; | |
| 114 min_priorities[extension_id] = std::numeric_limits<int>::min(); | |
| 115 } | |
| 116 | |
| 117 // Create deltas until we have passed the minimum priority. | |
| 118 std::list<LinkedPtrEventResponseDelta> result; | |
| 119 for (std::vector<PriorityRuleIdPair>::iterator i = ordered_matches.begin(); | |
| 120 i != ordered_matches.end(); ++i) { | |
| 121 const WebRequestRule::Priority priority_of_rule = i->first; | |
| 122 const WebRequestRule::GlobalRuleId& rule_id = i->second; | |
| 123 const ExtensionId& extension_id = rule_id.first; | |
| 124 const WebRequestRule* rule = | |
| 125 webrequest_rules_[rule_id.first][rule_id.second].get(); | |
| 126 CHECK(rule); | |
| 127 | |
| 128 // Skip rule if a previous rule of this extension instructed to ignore | |
| 129 // all rules with a lower priority than min_priorities[extension_id]. | |
| 130 int current_min_priority = min_priorities[extension_id]; | |
| 131 if (priority_of_rule < current_min_priority) | |
| 132 continue; | |
| 133 | |
| 134 if (!rule->tags().empty() && !ignore_tags[extension_id].empty()) { | |
| 135 bool ignore_rule = false; | |
| 136 const WebRequestRule::Tags& tags = rule->tags(); | |
| 137 for (WebRequestRule::Tags::const_iterator i = tags.begin(); | |
| 138 !ignore_rule && i != tags.end(); | |
| 139 ++i) { | |
| 140 ignore_rule |= ContainsKey(ignore_tags[extension_id], *i); | |
| 141 } | |
| 142 if (ignore_rule) | |
| 143 continue; | |
| 144 } | |
| 145 | |
| 146 std::list<LinkedPtrEventResponseDelta> rule_result; | |
| 147 WebRequestAction::ApplyInfo apply_info = { | |
| 148 extension_info_map, request_data, crosses_incognito, &rule_result, | |
| 149 &ignore_tags[extension_id] | |
| 150 }; | |
| 151 rule->Apply(&apply_info); | |
| 152 result.splice(result.begin(), rule_result); | |
| 153 | |
| 154 min_priorities[extension_id] = std::max(current_min_priority, | |
| 155 rule->GetMinimumPriority()); | |
| 156 } | |
| 157 return result; | |
| 158 } | |
| 159 | |
| 160 std::string WebRequestRulesRegistry::AddRulesImpl( | |
| 161 const std::string& extension_id, | |
| 162 const std::vector<linked_ptr<RulesRegistry::Rule> >& rules) { | |
| 163 typedef std::pair<WebRequestRule::RuleId, linked_ptr<WebRequestRule> > | |
| 164 IdRulePair; | |
| 165 typedef std::vector<IdRulePair> RulesVector; | |
| 166 | |
| 167 base::Time extension_installation_time = | |
| 168 GetExtensionInstallationTime(extension_id); | |
| 169 | |
| 170 std::string error; | |
| 171 RulesVector new_webrequest_rules; | |
| 172 new_webrequest_rules.reserve(rules.size()); | |
| 173 const Extension* extension = | |
| 174 extension_info_map_->extensions().GetByID(extension_id); | |
| 175 RulesMap& registered_rules = webrequest_rules_[extension_id]; | |
| 176 | |
| 177 for (std::vector<linked_ptr<RulesRegistry::Rule> >::const_iterator rule = | |
| 178 rules.begin(); rule != rules.end(); ++rule) { | |
| 179 const WebRequestRule::RuleId& rule_id(*(*rule)->id); | |
| 180 DCHECK(registered_rules.find(rule_id) == registered_rules.end()); | |
| 181 | |
| 182 scoped_ptr<WebRequestRule> webrequest_rule(WebRequestRule::Create( | |
| 183 url_matcher_.condition_factory(), | |
| 184 browser_context(), extension, extension_installation_time, *rule, | |
| 185 base::Bind(&Checker, base::Unretained(extension)), | |
| 186 &error)); | |
| 187 if (!error.empty()) { | |
| 188 // We don't return here, because we want to clear temporary | |
| 189 // condition sets in the url_matcher_. | |
| 190 break; | |
| 191 } | |
| 192 | |
| 193 new_webrequest_rules.push_back( | |
| 194 IdRulePair(rule_id, make_linked_ptr(webrequest_rule.release()))); | |
| 195 } | |
| 196 | |
| 197 if (!error.empty()) { | |
| 198 // Clean up temporary condition sets created during rule creation. | |
| 199 url_matcher_.ClearUnusedConditionSets(); | |
| 200 return error; | |
| 201 } | |
| 202 | |
| 203 // Wohoo, everything worked fine. | |
| 204 registered_rules.insert(new_webrequest_rules.begin(), | |
| 205 new_webrequest_rules.end()); | |
| 206 | |
| 207 // Create the triggers. | |
| 208 for (RulesVector::const_iterator i = new_webrequest_rules.begin(); | |
| 209 i != new_webrequest_rules.end(); ++i) { | |
| 210 URLMatcherConditionSet::Vector url_condition_sets; | |
| 211 const WebRequestConditionSet& conditions = i->second->conditions(); | |
| 212 conditions.GetURLMatcherConditionSets(&url_condition_sets); | |
| 213 for (URLMatcherConditionSet::Vector::iterator j = | |
| 214 url_condition_sets.begin(); j != url_condition_sets.end(); ++j) { | |
| 215 rule_triggers_[(*j)->id()] = i->second.get(); | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 // Register url patterns in |url_matcher_| and | |
| 220 // |rules_with_untriggered_conditions_|. | |
| 221 URLMatcherConditionSet::Vector all_new_condition_sets; | |
| 222 for (RulesVector::const_iterator i = new_webrequest_rules.begin(); | |
| 223 i != new_webrequest_rules.end(); ++i) { | |
| 224 i->second->conditions().GetURLMatcherConditionSets(&all_new_condition_sets); | |
| 225 if (i->second->conditions().HasConditionsWithoutUrls()) | |
| 226 rules_with_untriggered_conditions_.insert(i->second.get()); | |
| 227 } | |
| 228 url_matcher_.AddConditionSets(all_new_condition_sets); | |
| 229 | |
| 230 ClearCacheOnNavigation(); | |
| 231 | |
| 232 if (browser_context_ && !registered_rules.empty()) { | |
| 233 content::BrowserThread::PostTask( | |
| 234 content::BrowserThread::UI, FROM_HERE, | |
| 235 base::Bind(&extension_web_request_api_helpers::NotifyWebRequestAPIUsed, | |
| 236 browser_context_, make_scoped_refptr(extension))); | |
| 237 } | |
| 238 | |
| 239 return std::string(); | |
| 240 } | |
| 241 | |
| 242 std::string WebRequestRulesRegistry::RemoveRulesImpl( | |
| 243 const std::string& extension_id, | |
| 244 const std::vector<std::string>& rule_identifiers) { | |
| 245 // URLMatcherConditionSet IDs that can be removed from URLMatcher. | |
| 246 std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher; | |
| 247 RulesMap& registered_rules = webrequest_rules_[extension_id]; | |
| 248 | |
| 249 for (std::vector<std::string>::const_iterator i = rule_identifiers.begin(); | |
| 250 i != rule_identifiers.end(); ++i) { | |
| 251 // Skip unknown rules. | |
| 252 RulesMap::iterator webrequest_rules_entry = registered_rules.find(*i); | |
| 253 if (webrequest_rules_entry == registered_rules.end()) | |
| 254 continue; | |
| 255 | |
| 256 // Remove all triggers but collect their IDs. | |
| 257 CleanUpAfterRule(webrequest_rules_entry->second.get(), | |
| 258 &remove_from_url_matcher); | |
| 259 | |
| 260 // Removes the owning references to (and thus deletes) the rule. | |
| 261 registered_rules.erase(webrequest_rules_entry); | |
| 262 } | |
| 263 if (registered_rules.empty()) | |
| 264 webrequest_rules_.erase(extension_id); | |
| 265 | |
| 266 // Clear URLMatcher based on condition_set_ids that are not needed any more. | |
| 267 url_matcher_.RemoveConditionSets(remove_from_url_matcher); | |
| 268 | |
| 269 ClearCacheOnNavigation(); | |
| 270 | |
| 271 return std::string(); | |
| 272 } | |
| 273 | |
| 274 std::string WebRequestRulesRegistry::RemoveAllRulesImpl( | |
| 275 const std::string& extension_id) { | |
| 276 // First we get out all URLMatcherConditionSets and remove the rule references | |
| 277 // from |rules_with_untriggered_conditions_|. | |
| 278 std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher; | |
| 279 for (RulesMap::const_iterator it = webrequest_rules_[extension_id].begin(); | |
| 280 it != webrequest_rules_[extension_id].end(); | |
| 281 ++it) { | |
| 282 CleanUpAfterRule(it->second.get(), &remove_from_url_matcher); | |
| 283 } | |
| 284 url_matcher_.RemoveConditionSets(remove_from_url_matcher); | |
| 285 | |
| 286 webrequest_rules_.erase(extension_id); | |
| 287 ClearCacheOnNavigation(); | |
| 288 return std::string(); | |
| 289 } | |
| 290 | |
| 291 void WebRequestRulesRegistry::CleanUpAfterRule( | |
| 292 const WebRequestRule* rule, | |
| 293 std::vector<URLMatcherConditionSet::ID>* remove_from_url_matcher) { | |
| 294 URLMatcherConditionSet::Vector condition_sets; | |
| 295 rule->conditions().GetURLMatcherConditionSets(&condition_sets); | |
| 296 for (URLMatcherConditionSet::Vector::iterator j = condition_sets.begin(); | |
| 297 j != condition_sets.end(); | |
| 298 ++j) { | |
| 299 remove_from_url_matcher->push_back((*j)->id()); | |
| 300 rule_triggers_.erase((*j)->id()); | |
| 301 } | |
| 302 rules_with_untriggered_conditions_.erase(rule); | |
| 303 } | |
| 304 | |
| 305 bool WebRequestRulesRegistry::IsEmpty() const { | |
| 306 // Easy first. | |
| 307 if (!rule_triggers_.empty() && url_matcher_.IsEmpty()) | |
| 308 return false; | |
| 309 | |
| 310 // Now all the registered rules for each extensions. | |
| 311 for (std::map<WebRequestRule::ExtensionId, RulesMap>::const_iterator it = | |
| 312 webrequest_rules_.begin(); | |
| 313 it != webrequest_rules_.end(); | |
| 314 ++it) { | |
| 315 if (!it->second.empty()) | |
| 316 return false; | |
| 317 } | |
| 318 return true; | |
| 319 } | |
| 320 | |
| 321 WebRequestRulesRegistry::~WebRequestRulesRegistry() {} | |
| 322 | |
| 323 base::Time WebRequestRulesRegistry::GetExtensionInstallationTime( | |
| 324 const std::string& extension_id) const { | |
| 325 return extension_info_map_->GetInstallTime(extension_id); | |
| 326 } | |
| 327 | |
| 328 void WebRequestRulesRegistry::ClearCacheOnNavigation() { | |
| 329 extension_web_request_api_helpers::ClearCacheOnNavigation(); | |
| 330 } | |
| 331 | |
| 332 // static | |
| 333 bool WebRequestRulesRegistry::Checker(const Extension* extension, | |
| 334 const WebRequestConditionSet* conditions, | |
| 335 const WebRequestActionSet* actions, | |
| 336 std::string* error) { | |
| 337 return (StageChecker(conditions, actions, error) && | |
| 338 HostPermissionsChecker(extension, actions, error)); | |
| 339 } | |
| 340 | |
| 341 // static | |
| 342 bool WebRequestRulesRegistry::HostPermissionsChecker( | |
| 343 const Extension* extension, | |
| 344 const WebRequestActionSet* actions, | |
| 345 std::string* error) { | |
| 346 if (extension->permissions_data()->HasEffectiveAccessToAllHosts()) | |
| 347 return true; | |
| 348 | |
| 349 // Without the permission for all URLs, actions with the STRATEGY_DEFAULT | |
| 350 // should not be registered, they would never be able to execute. | |
| 351 for (WebRequestActionSet::Actions::const_iterator action_iter = | |
| 352 actions->actions().begin(); | |
| 353 action_iter != actions->actions().end(); | |
| 354 ++action_iter) { | |
| 355 if ((*action_iter)->host_permissions_strategy() == | |
| 356 WebRequestAction::STRATEGY_DEFAULT) { | |
| 357 *error = ErrorUtils::FormatErrorMessage(kAllURLsPermissionNeeded, | |
| 358 (*action_iter)->GetName()); | |
| 359 return false; | |
| 360 } | |
| 361 } | |
| 362 return true; | |
| 363 } | |
| 364 | |
| 365 // static | |
| 366 bool WebRequestRulesRegistry::StageChecker( | |
| 367 const WebRequestConditionSet* conditions, | |
| 368 const WebRequestActionSet* actions, | |
| 369 std::string* error) { | |
| 370 // Actions and conditions can be checked and executed in specific stages | |
| 371 // of each web request. A rule is inconsistent if there is an action that | |
| 372 // can only be triggered in stages in which no condition can be evaluated. | |
| 373 | |
| 374 // In which stages there are conditions to evaluate. | |
| 375 int condition_stages = 0; | |
| 376 for (WebRequestConditionSet::Conditions::const_iterator condition_iter = | |
| 377 conditions->conditions().begin(); | |
| 378 condition_iter != conditions->conditions().end(); | |
| 379 ++condition_iter) { | |
| 380 condition_stages |= (*condition_iter)->stages(); | |
| 381 } | |
| 382 | |
| 383 for (WebRequestActionSet::Actions::const_iterator action_iter = | |
| 384 actions->actions().begin(); | |
| 385 action_iter != actions->actions().end(); | |
| 386 ++action_iter) { | |
| 387 // Test the intersection of bit masks, this is intentionally & and not &&. | |
| 388 if ((*action_iter)->stages() & condition_stages) | |
| 389 continue; | |
| 390 // We only get here if no matching condition was found. | |
| 391 *error = ErrorUtils::FormatErrorMessage(kActionCannotBeExecuted, | |
| 392 (*action_iter)->GetName()); | |
| 393 return false; | |
| 394 } | |
| 395 return true; | |
| 396 } | |
| 397 void WebRequestRulesRegistry::AddTriggeredRules( | |
| 398 const URLMatches& url_matches, | |
| 399 const WebRequestCondition::MatchData& request_data, | |
| 400 RuleSet* result) const { | |
| 401 for (URLMatches::const_iterator url_match = url_matches.begin(); | |
| 402 url_match != url_matches.end(); ++url_match) { | |
| 403 RuleTriggers::const_iterator rule_trigger = rule_triggers_.find(*url_match); | |
| 404 CHECK(rule_trigger != rule_triggers_.end()); | |
| 405 if (!ContainsKey(*result, rule_trigger->second) && | |
| 406 rule_trigger->second->conditions().IsFulfilled(*url_match, | |
| 407 request_data)) | |
| 408 result->insert(rule_trigger->second); | |
| 409 } | |
| 410 } | |
| 411 | |
| 412 } // namespace extensions | |
| OLD | NEW |