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"
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
;
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 "
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
,
48 browser_context_(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 {
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
65 for (const WebRequestRule
* rule
: rules_with_untriggered_conditions_
) {
66 if (rule
->conditions().IsFulfilled(-1, request_data
))
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
);
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
>
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();
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 for (const std::string
& tag
: rule
->tags())
133 ignore_rule
|= ContainsKey(ignore_tags
[extension_id
], tag
);
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());
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
>>
157 typedef std::vector
<IdRulePair
> RulesVector
;
159 base::Time extension_installation_time
=
160 GetExtensionInstallationTime(extension_id
);
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_.
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();
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())
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
:
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 {
295 if (!rule_triggers_
.empty() && url_matcher_
.IsEmpty())
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())
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();
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
));
328 bool WebRequestRulesRegistry::HostPermissionsChecker(
329 const Extension
* extension
,
330 const WebRequestActionSet
* actions
,
331 std::string
* error
) {
332 if (extension
->permissions_data()->HasEffectiveAccessToAllHosts())
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
,
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
)
370 // We only get here if no matching condition was found.
371 *error
= ErrorUtils::FormatErrorMessage(kActionCannotBeExecuted
,
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