1bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant// Use of this source code is governed by a BSD-style license that can be 3f5256e16dfc425c1d466f6308d4026d529ce9e0bHoward Hinnant// found in the LICENSE file. 4bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant 5b64f8b07c104c6cc986570ac8ee0ed16a9f23976Howard Hinnant#include "extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h" 6b64f8b07c104c6cc986570ac8ee0ed16a9f23976Howard Hinnant 7bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include <algorithm> 8bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include <limits> 9bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include <utility> 10bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant 11bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "base/bind.h" 12bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "base/stl_util.h" 13bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/browser/api/declarative_webrequest/webrequest_condition.h" 14bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/browser/api/declarative_webrequest/webrequest_constants.h" 15bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/browser/api/web_request/web_request_api_helpers.h" 16bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/browser/api/web_request/web_request_permissions.h" 17bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/browser/extension_system.h" 18bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/common/error_utils.h" 19bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/common/extension.h" 20bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/common/permissions/permissions_data.h" 21bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "net/url_request/url_request.h" 22bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant 23bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnantusing url_matcher::URLMatcherConditionSet; 24bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant 25bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnantnamespace { 26bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant 27const 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 31const char kAllURLsPermissionNeeded[] = 32 "To execute the action '*', you need to request host permission for all " 33 "hosts."; 34 35} // namespace 36 37namespace extensions { 38 39WebRequestRulesRegistry::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 53std::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 79std::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 160std::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 242std::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 274std::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 291void 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 305bool 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 321WebRequestRulesRegistry::~WebRequestRulesRegistry() {} 322 323base::Time WebRequestRulesRegistry::GetExtensionInstallationTime( 324 const std::string& extension_id) const { 325 return extension_info_map_->GetInstallTime(extension_id); 326} 327 328void WebRequestRulesRegistry::ClearCacheOnNavigation() { 329 extension_web_request_api_helpers::ClearCacheOnNavigation(); 330} 331 332// static 333bool 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 342bool 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 366bool 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} 397void 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 413