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.
5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry.h"
11 #include "base/bind.h"
12 #include "base/stl_util.h"
13 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h"
14 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
15 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
16 #include "chrome/browser/extensions/api/web_request/web_request_permissions.h"
17 #include "chrome/browser/extensions/extension_system.h"
18 #include "chrome/common/extensions/extension.h"
19 #include "chrome/common/extensions/permissions/permissions_data.h"
20 #include "extensions/common/error_utils.h"
21 #include "net/url_request/url_request.h"
25 const char kActionCannotBeExecuted[] = "The action '*' can never be executed "
26 "because there are is no time in the request life-cycle during which the "
27 "conditions can be checked and the action can possibly be executed.";
29 const char kAllURLsPermissionNeeded[] =
30 "To execute the action '*', you need to request host permission for all "
35 namespace extensions {
37 WebRequestRulesRegistry::WebRequestRulesRegistry(
39 RulesCacheDelegate* cache_delegate)
40 : RulesRegistry(profile,
41 declarative_webrequest_constants::kOnRequest,
42 content::BrowserThread::IO,
44 profile_id_(profile) {
46 extension_info_map_ = ExtensionSystem::Get(profile)->info_map();
49 std::set<const WebRequestRule*> WebRequestRulesRegistry::GetMatches(
50 const WebRequestData& request_data_without_ids) const {
53 WebRequestDataWithMatchIds request_data(&request_data_without_ids);
54 request_data.url_match_ids = url_matcher_.MatchURL(
55 request_data.data->request->url());
56 request_data.first_party_url_match_ids = url_matcher_.MatchURL(
57 request_data.data->request->first_party_for_cookies());
59 // 1st phase -- add all rules with some conditions without UrlFilter
61 for (RuleSet::const_iterator it = rules_with_untriggered_conditions_.begin();
62 it != rules_with_untriggered_conditions_.end(); ++it) {
63 if ((*it)->conditions().IsFulfilled(-1, request_data))
67 // 2nd phase -- add all rules with some conditions triggered by URL matches.
68 AddTriggeredRules(request_data.url_match_ids, request_data, &result);
69 AddTriggeredRules(request_data.first_party_url_match_ids,
70 request_data, &result);
75 std::list<LinkedPtrEventResponseDelta> WebRequestRulesRegistry::CreateDeltas(
76 const ExtensionInfoMap* extension_info_map,
77 const WebRequestData& request_data,
78 bool crosses_incognito) {
79 if (webrequest_rules_.empty())
80 return std::list<LinkedPtrEventResponseDelta>();
82 std::set<const WebRequestRule*> matches = GetMatches(request_data);
84 // Sort all matching rules by their priority so that they can be processed
85 // in decreasing order.
86 typedef std::pair<WebRequestRule::Priority, WebRequestRule::GlobalRuleId>
88 std::vector<PriorityRuleIdPair> ordered_matches;
89 ordered_matches.reserve(matches.size());
90 for (std::set<const WebRequestRule*>::iterator i = matches.begin();
91 i != matches.end(); ++i) {
92 ordered_matches.push_back(make_pair((*i)->priority(), (*i)->id()));
94 // Sort from rbegin to rend in order to get descending priority order.
95 std::sort(ordered_matches.rbegin(), ordered_matches.rend());
97 // Build a map that maps each extension id to the minimum required priority
98 // for rules of that extension. Initially, this priority is -infinite and
99 // will be increased when the rules are processed and raise the bar via
100 // WebRequestIgnoreRulesActions.
101 typedef std::string ExtensionId;
102 typedef std::map<ExtensionId, WebRequestRule::Priority> MinPriorities;
103 typedef std::map<ExtensionId, std::set<std::string> > IgnoreTags;
104 MinPriorities min_priorities;
105 IgnoreTags ignore_tags;
106 for (std::vector<PriorityRuleIdPair>::iterator i = ordered_matches.begin();
107 i != ordered_matches.end(); ++i) {
108 const WebRequestRule::GlobalRuleId& rule_id = i->second;
109 const ExtensionId& extension_id = rule_id.first;
110 min_priorities[extension_id] = std::numeric_limits<int>::min();
113 // Create deltas until we have passed the minimum priority.
114 std::list<LinkedPtrEventResponseDelta> result;
115 for (std::vector<PriorityRuleIdPair>::iterator i = ordered_matches.begin();
116 i != ordered_matches.end(); ++i) {
117 const WebRequestRule::Priority priority_of_rule = i->first;
118 const WebRequestRule::GlobalRuleId& rule_id = i->second;
119 const ExtensionId& extension_id = rule_id.first;
120 const WebRequestRule* rule =
121 webrequest_rules_[rule_id.first][rule_id.second].get();
124 // Skip rule if a previous rule of this extension instructed to ignore
125 // all rules with a lower priority than min_priorities[extension_id].
126 int current_min_priority = min_priorities[extension_id];
127 if (priority_of_rule < current_min_priority)
130 if (!rule->tags().empty() && !ignore_tags[extension_id].empty()) {
131 bool ignore_rule = false;
132 const WebRequestRule::Tags& tags = rule->tags();
133 for (WebRequestRule::Tags::const_iterator i = tags.begin();
134 !ignore_rule && i != tags.end();
136 ignore_rule |= ContainsKey(ignore_tags[extension_id], *i);
142 std::list<LinkedPtrEventResponseDelta> rule_result;
143 WebRequestAction::ApplyInfo apply_info = {
144 extension_info_map, request_data, crosses_incognito, &rule_result,
145 &ignore_tags[extension_id]
147 rule->Apply(&apply_info);
148 result.splice(result.begin(), rule_result);
150 min_priorities[extension_id] = std::max(current_min_priority,
151 rule->GetMinimumPriority());
156 std::string WebRequestRulesRegistry::AddRulesImpl(
157 const std::string& extension_id,
158 const std::vector<linked_ptr<RulesRegistry::Rule> >& rules) {
159 typedef std::pair<WebRequestRule::RuleId, linked_ptr<WebRequestRule> >
161 typedef std::vector<IdRulePair> RulesVector;
163 base::Time extension_installation_time =
164 GetExtensionInstallationTime(extension_id);
167 RulesVector new_webrequest_rules;
168 new_webrequest_rules.reserve(rules.size());
169 const Extension* extension =
170 extension_info_map_->extensions().GetByID(extension_id);
171 RulesMap& registered_rules = webrequest_rules_[extension_id];
173 for (std::vector<linked_ptr<RulesRegistry::Rule> >::const_iterator rule =
174 rules.begin(); rule != rules.end(); ++rule) {
175 const WebRequestRule::RuleId& rule_id(*(*rule)->id);
176 DCHECK(registered_rules.find(rule_id) == registered_rules.end());
178 scoped_ptr<WebRequestRule> webrequest_rule(WebRequestRule::Create(
179 url_matcher_.condition_factory(),
180 extension, extension_installation_time, *rule,
181 base::Bind(&Checker, base::Unretained(extension)),
183 if (!error.empty()) {
184 // We don't return here, because we want to clear temporary
185 // condition sets in the url_matcher_.
189 new_webrequest_rules.push_back(
190 IdRulePair(rule_id, make_linked_ptr(webrequest_rule.release())));
193 if (!error.empty()) {
194 // Clean up temporary condition sets created during rule creation.
195 url_matcher_.ClearUnusedConditionSets();
199 // Wohoo, everything worked fine.
200 registered_rules.insert(new_webrequest_rules.begin(),
201 new_webrequest_rules.end());
203 // Create the triggers.
204 for (RulesVector::const_iterator i = new_webrequest_rules.begin();
205 i != new_webrequest_rules.end(); ++i) {
206 URLMatcherConditionSet::Vector url_condition_sets;
207 const WebRequestConditionSet& conditions = i->second->conditions();
208 conditions.GetURLMatcherConditionSets(&url_condition_sets);
209 for (URLMatcherConditionSet::Vector::iterator j =
210 url_condition_sets.begin(); j != url_condition_sets.end(); ++j) {
211 rule_triggers_[(*j)->id()] = i->second.get();
215 // Register url patterns in |url_matcher_| and
216 // |rules_with_untriggered_conditions_|.
217 URLMatcherConditionSet::Vector all_new_condition_sets;
218 for (RulesVector::const_iterator i = new_webrequest_rules.begin();
219 i != new_webrequest_rules.end(); ++i) {
220 i->second->conditions().GetURLMatcherConditionSets(&all_new_condition_sets);
221 if (i->second->conditions().HasConditionsWithoutUrls())
222 rules_with_untriggered_conditions_.insert(i->second.get());
224 url_matcher_.AddConditionSets(all_new_condition_sets);
226 ClearCacheOnNavigation();
228 if (profile_id_ && !registered_rules.empty()) {
229 content::BrowserThread::PostTask(
230 content::BrowserThread::UI, FROM_HERE,
231 base::Bind(&extension_web_request_api_helpers::NotifyWebRequestAPIUsed,
232 profile_id_, make_scoped_refptr(extension)));
235 return std::string();
238 std::string WebRequestRulesRegistry::RemoveRulesImpl(
239 const std::string& extension_id,
240 const std::vector<std::string>& rule_identifiers) {
241 // URLMatcherConditionSet IDs that can be removed from URLMatcher.
242 std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher;
243 RulesMap& registered_rules = webrequest_rules_[extension_id];
245 for (std::vector<std::string>::const_iterator i = rule_identifiers.begin();
246 i != rule_identifiers.end(); ++i) {
247 // Skip unknown rules.
248 RulesMap::iterator webrequest_rules_entry = registered_rules.find(*i);
249 if (webrequest_rules_entry == registered_rules.end())
252 // Remove all triggers but collect their IDs.
253 CleanUpAfterRule(webrequest_rules_entry->second.get(),
254 &remove_from_url_matcher);
256 // Removes the owning references to (and thus deletes) the rule.
257 registered_rules.erase(webrequest_rules_entry);
259 if (registered_rules.empty())
260 webrequest_rules_.erase(extension_id);
262 // Clear URLMatcher based on condition_set_ids that are not needed any more.
263 url_matcher_.RemoveConditionSets(remove_from_url_matcher);
265 ClearCacheOnNavigation();
267 return std::string();
270 std::string WebRequestRulesRegistry::RemoveAllRulesImpl(
271 const std::string& extension_id) {
272 // First we get out all URLMatcherConditionSets and remove the rule references
273 // from |rules_with_untriggered_conditions_|.
274 std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher;
275 for (RulesMap::const_iterator it = webrequest_rules_[extension_id].begin();
276 it != webrequest_rules_[extension_id].end();
278 CleanUpAfterRule(it->second.get(), &remove_from_url_matcher);
280 url_matcher_.RemoveConditionSets(remove_from_url_matcher);
282 webrequest_rules_.erase(extension_id);
283 ClearCacheOnNavigation();
284 return std::string();
287 void WebRequestRulesRegistry::CleanUpAfterRule(
288 const WebRequestRule* rule,
289 std::vector<URLMatcherConditionSet::ID>* remove_from_url_matcher) {
290 URLMatcherConditionSet::Vector condition_sets;
291 rule->conditions().GetURLMatcherConditionSets(&condition_sets);
292 for (URLMatcherConditionSet::Vector::iterator j = condition_sets.begin();
293 j != condition_sets.end();
295 remove_from_url_matcher->push_back((*j)->id());
296 rule_triggers_.erase((*j)->id());
298 rules_with_untriggered_conditions_.erase(rule);
301 bool WebRequestRulesRegistry::IsEmpty() const {
303 if (!rule_triggers_.empty() && url_matcher_.IsEmpty())
306 // Now all the registered rules for each extensions.
307 for (std::map<WebRequestRule::ExtensionId, RulesMap>::const_iterator it =
308 webrequest_rules_.begin();
309 it != webrequest_rules_.end();
311 if (!it->second.empty())
317 WebRequestRulesRegistry::~WebRequestRulesRegistry() {}
319 base::Time WebRequestRulesRegistry::GetExtensionInstallationTime(
320 const std::string& extension_id) const {
321 return extension_info_map_->GetInstallTime(extension_id);
324 void WebRequestRulesRegistry::ClearCacheOnNavigation() {
325 extension_web_request_api_helpers::ClearCacheOnNavigation();
329 bool WebRequestRulesRegistry::Checker(const Extension* extension,
330 const WebRequestConditionSet* conditions,
331 const WebRequestActionSet* actions,
332 std::string* error) {
333 return (StageChecker(conditions, actions, error) &&
334 HostPermissionsChecker(extension, actions, error));
338 bool WebRequestRulesRegistry::HostPermissionsChecker(
339 const Extension* extension,
340 const WebRequestActionSet* actions,
341 std::string* error) {
342 if (PermissionsData::HasEffectiveAccessToAllHosts(extension))
345 // Without the permission for all URLs, actions with the STRATEGY_DEFAULT
346 // should not be registered, they would never be able to execute.
347 for (WebRequestActionSet::Actions::const_iterator action_iter =
348 actions->actions().begin();
349 action_iter != actions->actions().end();
351 if ((*action_iter)->host_permissions_strategy() ==
352 WebRequestAction::STRATEGY_DEFAULT) {
353 *error = ErrorUtils::FormatErrorMessage(kAllURLsPermissionNeeded,
354 (*action_iter)->GetName());
362 bool WebRequestRulesRegistry::StageChecker(
363 const WebRequestConditionSet* conditions,
364 const WebRequestActionSet* actions,
365 std::string* error) {
366 // Actions and conditions can be checked and executed in specific stages
367 // of each web request. A rule is inconsistent if there is an action that
368 // can only be triggered in stages in which no condition can be evaluated.
370 // In which stages there are conditions to evaluate.
371 int condition_stages = 0;
372 for (WebRequestConditionSet::Conditions::const_iterator condition_iter =
373 conditions->conditions().begin();
374 condition_iter != conditions->conditions().end();
376 condition_stages |= (*condition_iter)->stages();
379 for (WebRequestActionSet::Actions::const_iterator action_iter =
380 actions->actions().begin();
381 action_iter != actions->actions().end();
383 // Test the intersection of bit masks, this is intentionally & and not &&.
384 if ((*action_iter)->stages() & condition_stages)
386 // We only get here if no matching condition was found.
387 *error = ErrorUtils::FormatErrorMessage(kActionCannotBeExecuted,
388 (*action_iter)->GetName());
393 void WebRequestRulesRegistry::AddTriggeredRules(
394 const URLMatches& url_matches,
395 const WebRequestCondition::MatchData& request_data,
396 RuleSet* result) const {
397 for (URLMatches::const_iterator url_match = url_matches.begin();
398 url_match != url_matches.end(); ++url_match) {
399 RuleTriggers::const_iterator rule_trigger = rule_triggers_.find(*url_match);
400 CHECK(rule_trigger != rule_triggers_.end());
401 if (!ContainsKey(*result, rule_trigger->second) &&
402 rule_trigger->second->conditions().IsFulfilled(*url_match,
404 result->insert(rule_trigger->second);
408 } // namespace extensions