Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / browser / api / declarative_webrequest / webrequest_rules_registry.cc
blobef709b28aa2d4ecdba4a26be3b1d2a5aed281270
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 "extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h"
7 #include <algorithm>
8 #include <limits>
9 #include <utility>
11 #include "base/bind.h"
12 #include "base/stl_util.h"
13 #include "extensions/browser/api/declarative_webrequest/webrequest_condition.h"
14 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
15 #include "extensions/browser/api/web_request/web_request_api_helpers.h"
16 #include "extensions/browser/api/web_request/web_request_permissions.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"
23 using url_matcher::URLMatcherConditionSet;
25 namespace {
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.";
31 const char kAllURLsPermissionNeeded[] =
32 "To execute the action '*', you need to request host permission for all "
33 "hosts.";
35 } // namespace
37 namespace extensions {
39 WebRequestRulesRegistry::WebRequestRulesRegistry(
40 content::BrowserContext* browser_context,
41 RulesCacheDelegate* cache_delegate,
42 int rules_registry_id)
43 : RulesRegistry(browser_context,
44 declarative_webrequest_constants::kOnRequest,
45 content::BrowserThread::IO,
46 cache_delegate,
47 rules_registry_id),
48 browser_context_(browser_context) {
49 if (browser_context_)
50 extension_info_map_ = ExtensionSystem::Get(browser_context_)->info_map();
53 std::set<const WebRequestRule*> WebRequestRulesRegistry::GetMatches(
54 const WebRequestData& request_data_without_ids) const {
55 RuleSet result;
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());
63 // 1st phase -- add all rules with some conditions without UrlFilter
64 // attributes.
65 for (const WebRequestRule* rule : rules_with_untriggered_conditions_) {
66 if (rule->conditions().IsFulfilled(-1, request_data))
67 result.insert(rule);
70 // 2nd phase -- add all rules with some conditions triggered by URL matches.
71 AddTriggeredRules(request_data.url_match_ids, request_data, &result);
72 AddTriggeredRules(request_data.first_party_url_match_ids,
73 request_data, &result);
75 return result;
78 std::list<LinkedPtrEventResponseDelta> WebRequestRulesRegistry::CreateDeltas(
79 const InfoMap* extension_info_map,
80 const WebRequestData& request_data,
81 bool crosses_incognito) {
82 if (webrequest_rules_.empty())
83 return std::list<LinkedPtrEventResponseDelta>();
85 std::set<const WebRequestRule*> matches = GetMatches(request_data);
87 // Sort all matching rules by their priority so that they can be processed
88 // in decreasing order.
89 typedef std::pair<WebRequestRule::Priority, WebRequestRule::GlobalRuleId>
90 PriorityRuleIdPair;
91 std::vector<PriorityRuleIdPair> ordered_matches;
92 ordered_matches.reserve(matches.size());
93 for (const WebRequestRule* rule : matches)
94 ordered_matches.push_back(make_pair(rule->priority(), rule->id()));
95 // Sort from rbegin to rend in order to get descending priority order.
96 std::sort(ordered_matches.rbegin(), ordered_matches.rend());
98 // Build a map that maps each extension id to the minimum required priority
99 // for rules of that extension. Initially, this priority is -infinite and
100 // will be increased when the rules are processed and raise the bar via
101 // WebRequestIgnoreRulesActions.
102 typedef std::string ExtensionId;
103 typedef std::map<ExtensionId, WebRequestRule::Priority> MinPriorities;
104 typedef std::map<ExtensionId, std::set<std::string> > IgnoreTags;
105 MinPriorities min_priorities;
106 IgnoreTags ignore_tags;
107 for (const PriorityRuleIdPair& priority_rule_id_pair : ordered_matches) {
108 const WebRequestRule::GlobalRuleId& rule_id = priority_rule_id_pair.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 (const PriorityRuleIdPair& priority_rule_id_pair : ordered_matches) {
116 const WebRequestRule::Priority priority_of_rule =
117 priority_rule_id_pair.first;
118 const WebRequestRule::GlobalRuleId& rule_id = priority_rule_id_pair.second;
119 const ExtensionId& extension_id = rule_id.first;
120 const WebRequestRule* rule =
121 webrequest_rules_[rule_id.first][rule_id.second].get();
122 CHECK(rule);
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)
128 continue;
130 if (!rule->tags().empty() && !ignore_tags[extension_id].empty()) {
131 bool ignore_rule = false;
132 for (const std::string& tag : rule->tags())
133 ignore_rule |= ContainsKey(ignore_tags[extension_id], tag);
134 if (ignore_rule)
135 continue;
138 std::list<LinkedPtrEventResponseDelta> rule_result;
139 WebRequestAction::ApplyInfo apply_info = {
140 extension_info_map, request_data, crosses_incognito, &rule_result,
141 &ignore_tags[extension_id]
143 rule->Apply(&apply_info);
144 result.splice(result.begin(), rule_result);
146 min_priorities[extension_id] = std::max(current_min_priority,
147 rule->GetMinimumPriority());
149 return result;
152 std::string WebRequestRulesRegistry::AddRulesImpl(
153 const std::string& extension_id,
154 const std::vector<linked_ptr<api::events::Rule>>& rules) {
155 typedef std::pair<WebRequestRule::RuleId, linked_ptr<const WebRequestRule>>
156 IdRulePair;
157 typedef std::vector<IdRulePair> RulesVector;
159 base::Time extension_installation_time =
160 GetExtensionInstallationTime(extension_id);
162 std::string error;
163 RulesVector new_webrequest_rules;
164 new_webrequest_rules.reserve(rules.size());
165 const Extension* extension =
166 extension_info_map_->extensions().GetByID(extension_id);
167 RulesMap& registered_rules = webrequest_rules_[extension_id];
169 for (const linked_ptr<api::events::Rule>& rule : rules) {
170 const WebRequestRule::RuleId& rule_id(*rule->id);
171 DCHECK(registered_rules.find(rule_id) == registered_rules.end());
173 scoped_ptr<WebRequestRule> webrequest_rule(WebRequestRule::Create(
174 url_matcher_.condition_factory(), browser_context(), extension,
175 extension_installation_time, rule,
176 base::Bind(&Checker, base::Unretained(extension)), &error));
177 if (!error.empty()) {
178 // We don't return here, because we want to clear temporary
179 // condition sets in the url_matcher_.
180 break;
183 new_webrequest_rules.push_back(
184 IdRulePair(rule_id, make_linked_ptr(webrequest_rule.release())));
187 if (!error.empty()) {
188 // Clean up temporary condition sets created during rule creation.
189 url_matcher_.ClearUnusedConditionSets();
190 return error;
193 // Wohoo, everything worked fine.
194 registered_rules.insert(new_webrequest_rules.begin(),
195 new_webrequest_rules.end());
197 // Create the triggers.
198 for (const IdRulePair& id_rule_pair : new_webrequest_rules) {
199 URLMatcherConditionSet::Vector url_condition_sets;
200 const linked_ptr<const WebRequestRule>& rule = id_rule_pair.second;
201 rule->conditions().GetURLMatcherConditionSets(&url_condition_sets);
202 for (const scoped_refptr<URLMatcherConditionSet>& condition_set :
203 url_condition_sets) {
204 rule_triggers_[condition_set->id()] = rule.get();
208 // Register url patterns in |url_matcher_| and
209 // |rules_with_untriggered_conditions_|.
210 URLMatcherConditionSet::Vector all_new_condition_sets;
211 for (const IdRulePair& id_rule_pair : new_webrequest_rules) {
212 const linked_ptr<const WebRequestRule>& rule = id_rule_pair.second;
213 rule->conditions().GetURLMatcherConditionSets(&all_new_condition_sets);
214 if (rule->conditions().HasConditionsWithoutUrls())
215 rules_with_untriggered_conditions_.insert(rule.get());
217 url_matcher_.AddConditionSets(all_new_condition_sets);
219 ClearCacheOnNavigation();
221 if (browser_context_ && !registered_rules.empty()) {
222 content::BrowserThread::PostTask(
223 content::BrowserThread::UI, FROM_HERE,
224 base::Bind(&extension_web_request_api_helpers::NotifyWebRequestAPIUsed,
225 browser_context_, extension->id()));
228 return std::string();
231 std::string WebRequestRulesRegistry::RemoveRulesImpl(
232 const std::string& extension_id,
233 const std::vector<std::string>& rule_identifiers) {
234 // URLMatcherConditionSet IDs that can be removed from URLMatcher.
235 std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher;
236 RulesMap& registered_rules = webrequest_rules_[extension_id];
238 for (const std::string& identifier : rule_identifiers) {
239 // Skip unknown rules.
240 RulesMap::iterator webrequest_rules_entry =
241 registered_rules.find(identifier);
242 if (webrequest_rules_entry == registered_rules.end())
243 continue;
245 // Remove all triggers but collect their IDs.
246 CleanUpAfterRule(webrequest_rules_entry->second.get(),
247 &remove_from_url_matcher);
249 // Removes the owning references to (and thus deletes) the rule.
250 registered_rules.erase(webrequest_rules_entry);
252 if (registered_rules.empty())
253 webrequest_rules_.erase(extension_id);
255 // Clear URLMatcher based on condition_set_ids that are not needed any more.
256 url_matcher_.RemoveConditionSets(remove_from_url_matcher);
258 ClearCacheOnNavigation();
260 return std::string();
263 std::string WebRequestRulesRegistry::RemoveAllRulesImpl(
264 const std::string& extension_id) {
265 // First we get out all URLMatcherConditionSets and remove the rule references
266 // from |rules_with_untriggered_conditions_|.
267 std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher;
268 for (const std::pair<WebRequestRule::RuleId,
269 linked_ptr<const WebRequestRule>>& rule_id_rule_pair :
270 webrequest_rules_[extension_id]) {
271 CleanUpAfterRule(rule_id_rule_pair.second.get(), &remove_from_url_matcher);
273 url_matcher_.RemoveConditionSets(remove_from_url_matcher);
275 webrequest_rules_.erase(extension_id);
276 ClearCacheOnNavigation();
277 return std::string();
280 void WebRequestRulesRegistry::CleanUpAfterRule(
281 const WebRequestRule* rule,
282 std::vector<URLMatcherConditionSet::ID>* remove_from_url_matcher) {
283 URLMatcherConditionSet::Vector condition_sets;
284 rule->conditions().GetURLMatcherConditionSets(&condition_sets);
285 for (const scoped_refptr<URLMatcherConditionSet>& condition_set :
286 condition_sets) {
287 remove_from_url_matcher->push_back(condition_set->id());
288 rule_triggers_.erase(condition_set->id());
290 rules_with_untriggered_conditions_.erase(rule);
293 bool WebRequestRulesRegistry::IsEmpty() const {
294 // Easy first.
295 if (!rule_triggers_.empty() && url_matcher_.IsEmpty())
296 return false;
298 // Now all the registered rules for each extensions.
299 for (const std::pair<WebRequestRule::ExtensionId, RulesMap>&
300 extension_id_rules_map_pair : webrequest_rules_) {
301 if (!extension_id_rules_map_pair.second.empty())
302 return false;
304 return true;
307 WebRequestRulesRegistry::~WebRequestRulesRegistry() {}
309 base::Time WebRequestRulesRegistry::GetExtensionInstallationTime(
310 const std::string& extension_id) const {
311 return extension_info_map_->GetInstallTime(extension_id);
314 void WebRequestRulesRegistry::ClearCacheOnNavigation() {
315 extension_web_request_api_helpers::ClearCacheOnNavigation();
318 // static
319 bool WebRequestRulesRegistry::Checker(const Extension* extension,
320 const WebRequestConditionSet* conditions,
321 const WebRequestActionSet* actions,
322 std::string* error) {
323 return (StageChecker(conditions, actions, error) &&
324 HostPermissionsChecker(extension, actions, error));
327 // static
328 bool WebRequestRulesRegistry::HostPermissionsChecker(
329 const Extension* extension,
330 const WebRequestActionSet* actions,
331 std::string* error) {
332 if (extension->permissions_data()->HasEffectiveAccessToAllHosts())
333 return true;
335 // Without the permission for all URLs, actions with the STRATEGY_DEFAULT
336 // should not be registered, they would never be able to execute.
337 for (const scoped_refptr<const WebRequestAction>& action :
338 actions->actions()) {
339 if (action->host_permissions_strategy() ==
340 WebRequestAction::STRATEGY_DEFAULT) {
341 *error = ErrorUtils::FormatErrorMessage(kAllURLsPermissionNeeded,
342 action->GetName());
343 return false;
346 return true;
349 // static
350 bool WebRequestRulesRegistry::StageChecker(
351 const WebRequestConditionSet* conditions,
352 const WebRequestActionSet* actions,
353 std::string* error) {
354 // Actions and conditions can be checked and executed in specific stages
355 // of each web request. A rule is inconsistent if there is an action that
356 // can only be triggered in stages in which no condition can be evaluated.
358 // In which stages there are conditions to evaluate.
359 int condition_stages = 0;
360 for (const linked_ptr<const WebRequestCondition>& condition :
361 conditions->conditions()) {
362 condition_stages |= condition->stages();
365 for (const scoped_refptr<const WebRequestAction>& action :
366 actions->actions()) {
367 // Test the intersection of bit masks, this is intentionally & and not &&.
368 if (action->stages() & condition_stages)
369 continue;
370 // We only get here if no matching condition was found.
371 *error = ErrorUtils::FormatErrorMessage(kActionCannotBeExecuted,
372 action->GetName());
373 return false;
375 return true;
377 void WebRequestRulesRegistry::AddTriggeredRules(
378 const URLMatches& url_matches,
379 const WebRequestCondition::MatchData& request_data,
380 RuleSet* result) const {
381 for (url_matcher::URLMatcherConditionSet::ID url_match : url_matches) {
382 RuleTriggers::const_iterator rule_trigger = rule_triggers_.find(url_match);
383 CHECK(rule_trigger != rule_triggers_.end());
384 if (!ContainsKey(*result, rule_trigger->second) &&
385 rule_trigger->second->conditions().IsFulfilled(url_match, request_data))
386 result->insert(rule_trigger->second);
390 } // namespace extensions