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 (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
))
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
);
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
>();
86 std::set
<const WebRequestRule
*> matches
= GetMatches(request_data
);
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
>
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()));
98 // Sort from rbegin to rend in order to get descending priority order.
99 std::sort(ordered_matches
.rbegin(), ordered_matches
.rend());
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();
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();
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
)
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();
140 ignore_rule
|= ContainsKey(ignore_tags
[extension_id
], *i
);
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
]
151 rule
->Apply(&apply_info
);
152 result
.splice(result
.begin(), rule_result
);
154 min_priorities
[extension_id
] = std::max(current_min_priority
,
155 rule
->GetMinimumPriority());
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
> >
165 typedef std::vector
<IdRulePair
> RulesVector
;
167 base::Time extension_installation_time
=
168 GetExtensionInstallationTime(extension_id
);
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
];
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());
182 scoped_ptr
<WebRequestRule
> webrequest_rule(WebRequestRule::Create(
183 url_matcher_
.condition_factory(), browser_context(),
184 HostID(HostID::EXTENSIONS
, extension
->id()), extension
,
185 extension_installation_time
, *rule
,
186 base::Bind(&Checker
, base::Unretained(extension
)), &error
));
187 if (!error
.empty()) {
188 // We don't return here, because we want to clear temporary
189 // condition sets in the url_matcher_.
193 new_webrequest_rules
.push_back(
194 IdRulePair(rule_id
, make_linked_ptr(webrequest_rule
.release())));
197 if (!error
.empty()) {
198 // Clean up temporary condition sets created during rule creation.
199 url_matcher_
.ClearUnusedConditionSets();
203 // Wohoo, everything worked fine.
204 registered_rules
.insert(new_webrequest_rules
.begin(),
205 new_webrequest_rules
.end());
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();
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());
228 url_matcher_
.AddConditionSets(all_new_condition_sets
);
230 ClearCacheOnNavigation();
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_
, extension
->id()));
239 return std::string();
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
];
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())
256 // Remove all triggers but collect their IDs.
257 CleanUpAfterRule(webrequest_rules_entry
->second
.get(),
258 &remove_from_url_matcher
);
260 // Removes the owning references to (and thus deletes) the rule.
261 registered_rules
.erase(webrequest_rules_entry
);
263 if (registered_rules
.empty())
264 webrequest_rules_
.erase(extension_id
);
266 // Clear URLMatcher based on condition_set_ids that are not needed any more.
267 url_matcher_
.RemoveConditionSets(remove_from_url_matcher
);
269 ClearCacheOnNavigation();
271 return std::string();
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();
282 CleanUpAfterRule(it
->second
.get(), &remove_from_url_matcher
);
284 url_matcher_
.RemoveConditionSets(remove_from_url_matcher
);
286 webrequest_rules_
.erase(extension_id
);
287 ClearCacheOnNavigation();
288 return std::string();
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();
299 remove_from_url_matcher
->push_back((*j
)->id());
300 rule_triggers_
.erase((*j
)->id());
302 rules_with_untriggered_conditions_
.erase(rule
);
305 bool WebRequestRulesRegistry::IsEmpty() const {
307 if (!rule_triggers_
.empty() && url_matcher_
.IsEmpty())
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();
315 if (!it
->second
.empty())
321 WebRequestRulesRegistry::~WebRequestRulesRegistry() {}
323 base::Time
WebRequestRulesRegistry::GetExtensionInstallationTime(
324 const std::string
& extension_id
) const {
325 return extension_info_map_
->GetInstallTime(extension_id
);
328 void WebRequestRulesRegistry::ClearCacheOnNavigation() {
329 extension_web_request_api_helpers::ClearCacheOnNavigation();
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
));
342 bool WebRequestRulesRegistry::HostPermissionsChecker(
343 const Extension
* extension
,
344 const WebRequestActionSet
* actions
,
345 std::string
* error
) {
346 if (extension
->permissions_data()->HasEffectiveAccessToAllHosts())
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();
355 if ((*action_iter
)->host_permissions_strategy() ==
356 WebRequestAction::STRATEGY_DEFAULT
) {
357 *error
= ErrorUtils::FormatErrorMessage(kAllURLsPermissionNeeded
,
358 (*action_iter
)->GetName());
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.
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();
380 condition_stages
|= (*condition_iter
)->stages();
383 for (WebRequestActionSet::Actions::const_iterator action_iter
=
384 actions
->actions().begin();
385 action_iter
!= actions
->actions().end();
387 // Test the intersection of bit masks, this is intentionally & and not &&.
388 if ((*action_iter
)->stages() & condition_stages
)
390 // We only get here if no matching condition was found.
391 *error
= ErrorUtils::FormatErrorMessage(kActionCannotBeExecuted
,
392 (*action_iter
)->GetName());
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
,
408 result
->insert(rule_trigger
->second
);
412 } // namespace extensions