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/profiles/profile.h"
18 #include "extensions/browser/extension_system.h"
19 #include "extensions/common/error_utils.h"
20 #include "extensions/common/extension.h"
21 #include "extensions/common/permissions/permissions_data.h"
22 #include "net/url_request/url_request.h"
24 using url_matcher::URLMatcherConditionSet
;
28 const char kActionCannotBeExecuted
[] = "The action '*' can never be executed "
29 "because there are is no time in the request life-cycle during which the "
30 "conditions can be checked and the action can possibly be executed.";
32 const char kAllURLsPermissionNeeded
[] =
33 "To execute the action '*', you need to request host permission for all "
38 namespace extensions
{
40 WebRequestRulesRegistry::WebRequestRulesRegistry(
42 RulesCacheDelegate
* cache_delegate
,
43 const WebViewKey
& webview_key
)
44 : RulesRegistry(profile
,
45 declarative_webrequest_constants::kOnRequest
,
46 content::BrowserThread::IO
,
49 profile_id_(profile
) {
51 extension_info_map_
= ExtensionSystem::Get(profile
)->info_map();
54 std::set
<const WebRequestRule
*> WebRequestRulesRegistry::GetMatches(
55 const WebRequestData
& request_data_without_ids
) const {
58 WebRequestDataWithMatchIds
request_data(&request_data_without_ids
);
59 request_data
.url_match_ids
= url_matcher_
.MatchURL(
60 request_data
.data
->request
->url());
61 request_data
.first_party_url_match_ids
= url_matcher_
.MatchURL(
62 request_data
.data
->request
->first_party_for_cookies());
64 // 1st phase -- add all rules with some conditions without UrlFilter
66 for (RuleSet::const_iterator it
= rules_with_untriggered_conditions_
.begin();
67 it
!= rules_with_untriggered_conditions_
.end(); ++it
) {
68 if ((*it
)->conditions().IsFulfilled(-1, request_data
))
72 // 2nd phase -- add all rules with some conditions triggered by URL matches.
73 AddTriggeredRules(request_data
.url_match_ids
, request_data
, &result
);
74 AddTriggeredRules(request_data
.first_party_url_match_ids
,
75 request_data
, &result
);
80 std::list
<LinkedPtrEventResponseDelta
> WebRequestRulesRegistry::CreateDeltas(
81 const InfoMap
* extension_info_map
,
82 const WebRequestData
& request_data
,
83 bool crosses_incognito
) {
84 if (webrequest_rules_
.empty())
85 return std::list
<LinkedPtrEventResponseDelta
>();
87 std::set
<const WebRequestRule
*> matches
= GetMatches(request_data
);
89 // Sort all matching rules by their priority so that they can be processed
90 // in decreasing order.
91 typedef std::pair
<WebRequestRule::Priority
, WebRequestRule::GlobalRuleId
>
93 std::vector
<PriorityRuleIdPair
> ordered_matches
;
94 ordered_matches
.reserve(matches
.size());
95 for (std::set
<const WebRequestRule
*>::iterator i
= matches
.begin();
96 i
!= matches
.end(); ++i
) {
97 ordered_matches
.push_back(make_pair((*i
)->priority(), (*i
)->id()));
99 // Sort from rbegin to rend in order to get descending priority order.
100 std::sort(ordered_matches
.rbegin(), ordered_matches
.rend());
102 // Build a map that maps each extension id to the minimum required priority
103 // for rules of that extension. Initially, this priority is -infinite and
104 // will be increased when the rules are processed and raise the bar via
105 // WebRequestIgnoreRulesActions.
106 typedef std::string ExtensionId
;
107 typedef std::map
<ExtensionId
, WebRequestRule::Priority
> MinPriorities
;
108 typedef std::map
<ExtensionId
, std::set
<std::string
> > IgnoreTags
;
109 MinPriorities min_priorities
;
110 IgnoreTags ignore_tags
;
111 for (std::vector
<PriorityRuleIdPair
>::iterator i
= ordered_matches
.begin();
112 i
!= ordered_matches
.end(); ++i
) {
113 const WebRequestRule::GlobalRuleId
& rule_id
= i
->second
;
114 const ExtensionId
& extension_id
= rule_id
.first
;
115 min_priorities
[extension_id
] = std::numeric_limits
<int>::min();
118 // Create deltas until we have passed the minimum priority.
119 std::list
<LinkedPtrEventResponseDelta
> result
;
120 for (std::vector
<PriorityRuleIdPair
>::iterator i
= ordered_matches
.begin();
121 i
!= ordered_matches
.end(); ++i
) {
122 const WebRequestRule::Priority priority_of_rule
= i
->first
;
123 const WebRequestRule::GlobalRuleId
& rule_id
= i
->second
;
124 const ExtensionId
& extension_id
= rule_id
.first
;
125 const WebRequestRule
* rule
=
126 webrequest_rules_
[rule_id
.first
][rule_id
.second
].get();
129 // Skip rule if a previous rule of this extension instructed to ignore
130 // all rules with a lower priority than min_priorities[extension_id].
131 int current_min_priority
= min_priorities
[extension_id
];
132 if (priority_of_rule
< current_min_priority
)
135 if (!rule
->tags().empty() && !ignore_tags
[extension_id
].empty()) {
136 bool ignore_rule
= false;
137 const WebRequestRule::Tags
& tags
= rule
->tags();
138 for (WebRequestRule::Tags::const_iterator i
= tags
.begin();
139 !ignore_rule
&& i
!= tags
.end();
141 ignore_rule
|= ContainsKey(ignore_tags
[extension_id
], *i
);
147 std::list
<LinkedPtrEventResponseDelta
> rule_result
;
148 WebRequestAction::ApplyInfo apply_info
= {
149 extension_info_map
, request_data
, crosses_incognito
, &rule_result
,
150 &ignore_tags
[extension_id
]
152 rule
->Apply(&apply_info
);
153 result
.splice(result
.begin(), rule_result
);
155 min_priorities
[extension_id
] = std::max(current_min_priority
,
156 rule
->GetMinimumPriority());
161 std::string
WebRequestRulesRegistry::AddRulesImpl(
162 const std::string
& extension_id
,
163 const std::vector
<linked_ptr
<RulesRegistry::Rule
> >& rules
) {
164 typedef std::pair
<WebRequestRule::RuleId
, linked_ptr
<WebRequestRule
> >
166 typedef std::vector
<IdRulePair
> RulesVector
;
168 base::Time extension_installation_time
=
169 GetExtensionInstallationTime(extension_id
);
172 RulesVector new_webrequest_rules
;
173 new_webrequest_rules
.reserve(rules
.size());
174 const Extension
* extension
=
175 extension_info_map_
->extensions().GetByID(extension_id
);
176 RulesMap
& registered_rules
= webrequest_rules_
[extension_id
];
178 for (std::vector
<linked_ptr
<RulesRegistry::Rule
> >::const_iterator rule
=
179 rules
.begin(); rule
!= rules
.end(); ++rule
) {
180 const WebRequestRule::RuleId
& rule_id(*(*rule
)->id
);
181 DCHECK(registered_rules
.find(rule_id
) == registered_rules
.end());
183 scoped_ptr
<WebRequestRule
> webrequest_rule(WebRequestRule::Create(
184 url_matcher_
.condition_factory(),
185 extension
, extension_installation_time
, *rule
,
186 base::Bind(&Checker
, base::Unretained(extension
)),
188 if (!error
.empty()) {
189 // We don't return here, because we want to clear temporary
190 // condition sets in the url_matcher_.
194 new_webrequest_rules
.push_back(
195 IdRulePair(rule_id
, make_linked_ptr(webrequest_rule
.release())));
198 if (!error
.empty()) {
199 // Clean up temporary condition sets created during rule creation.
200 url_matcher_
.ClearUnusedConditionSets();
204 // Wohoo, everything worked fine.
205 registered_rules
.insert(new_webrequest_rules
.begin(),
206 new_webrequest_rules
.end());
208 // Create the triggers.
209 for (RulesVector::const_iterator i
= new_webrequest_rules
.begin();
210 i
!= new_webrequest_rules
.end(); ++i
) {
211 URLMatcherConditionSet::Vector url_condition_sets
;
212 const WebRequestConditionSet
& conditions
= i
->second
->conditions();
213 conditions
.GetURLMatcherConditionSets(&url_condition_sets
);
214 for (URLMatcherConditionSet::Vector::iterator j
=
215 url_condition_sets
.begin(); j
!= url_condition_sets
.end(); ++j
) {
216 rule_triggers_
[(*j
)->id()] = i
->second
.get();
220 // Register url patterns in |url_matcher_| and
221 // |rules_with_untriggered_conditions_|.
222 URLMatcherConditionSet::Vector all_new_condition_sets
;
223 for (RulesVector::const_iterator i
= new_webrequest_rules
.begin();
224 i
!= new_webrequest_rules
.end(); ++i
) {
225 i
->second
->conditions().GetURLMatcherConditionSets(&all_new_condition_sets
);
226 if (i
->second
->conditions().HasConditionsWithoutUrls())
227 rules_with_untriggered_conditions_
.insert(i
->second
.get());
229 url_matcher_
.AddConditionSets(all_new_condition_sets
);
231 ClearCacheOnNavigation();
233 if (profile_id_
&& !registered_rules
.empty()) {
234 content::BrowserThread::PostTask(
235 content::BrowserThread::UI
, FROM_HERE
,
236 base::Bind(&extension_web_request_api_helpers::NotifyWebRequestAPIUsed
,
237 profile_id_
, make_scoped_refptr(extension
)));
240 return std::string();
243 std::string
WebRequestRulesRegistry::RemoveRulesImpl(
244 const std::string
& extension_id
,
245 const std::vector
<std::string
>& rule_identifiers
) {
246 // URLMatcherConditionSet IDs that can be removed from URLMatcher.
247 std::vector
<URLMatcherConditionSet::ID
> remove_from_url_matcher
;
248 RulesMap
& registered_rules
= webrequest_rules_
[extension_id
];
250 for (std::vector
<std::string
>::const_iterator i
= rule_identifiers
.begin();
251 i
!= rule_identifiers
.end(); ++i
) {
252 // Skip unknown rules.
253 RulesMap::iterator webrequest_rules_entry
= registered_rules
.find(*i
);
254 if (webrequest_rules_entry
== registered_rules
.end())
257 // Remove all triggers but collect their IDs.
258 CleanUpAfterRule(webrequest_rules_entry
->second
.get(),
259 &remove_from_url_matcher
);
261 // Removes the owning references to (and thus deletes) the rule.
262 registered_rules
.erase(webrequest_rules_entry
);
264 if (registered_rules
.empty())
265 webrequest_rules_
.erase(extension_id
);
267 // Clear URLMatcher based on condition_set_ids that are not needed any more.
268 url_matcher_
.RemoveConditionSets(remove_from_url_matcher
);
270 ClearCacheOnNavigation();
272 return std::string();
275 std::string
WebRequestRulesRegistry::RemoveAllRulesImpl(
276 const std::string
& extension_id
) {
277 // First we get out all URLMatcherConditionSets and remove the rule references
278 // from |rules_with_untriggered_conditions_|.
279 std::vector
<URLMatcherConditionSet::ID
> remove_from_url_matcher
;
280 for (RulesMap::const_iterator it
= webrequest_rules_
[extension_id
].begin();
281 it
!= webrequest_rules_
[extension_id
].end();
283 CleanUpAfterRule(it
->second
.get(), &remove_from_url_matcher
);
285 url_matcher_
.RemoveConditionSets(remove_from_url_matcher
);
287 webrequest_rules_
.erase(extension_id
);
288 ClearCacheOnNavigation();
289 return std::string();
292 void WebRequestRulesRegistry::CleanUpAfterRule(
293 const WebRequestRule
* rule
,
294 std::vector
<URLMatcherConditionSet::ID
>* remove_from_url_matcher
) {
295 URLMatcherConditionSet::Vector condition_sets
;
296 rule
->conditions().GetURLMatcherConditionSets(&condition_sets
);
297 for (URLMatcherConditionSet::Vector::iterator j
= condition_sets
.begin();
298 j
!= condition_sets
.end();
300 remove_from_url_matcher
->push_back((*j
)->id());
301 rule_triggers_
.erase((*j
)->id());
303 rules_with_untriggered_conditions_
.erase(rule
);
306 bool WebRequestRulesRegistry::IsEmpty() const {
308 if (!rule_triggers_
.empty() && url_matcher_
.IsEmpty())
311 // Now all the registered rules for each extensions.
312 for (std::map
<WebRequestRule::ExtensionId
, RulesMap
>::const_iterator it
=
313 webrequest_rules_
.begin();
314 it
!= webrequest_rules_
.end();
316 if (!it
->second
.empty())
322 WebRequestRulesRegistry::~WebRequestRulesRegistry() {}
324 base::Time
WebRequestRulesRegistry::GetExtensionInstallationTime(
325 const std::string
& extension_id
) const {
326 return extension_info_map_
->GetInstallTime(extension_id
);
329 void WebRequestRulesRegistry::ClearCacheOnNavigation() {
330 extension_web_request_api_helpers::ClearCacheOnNavigation();
334 bool WebRequestRulesRegistry::Checker(const Extension
* extension
,
335 const WebRequestConditionSet
* conditions
,
336 const WebRequestActionSet
* actions
,
337 std::string
* error
) {
338 return (StageChecker(conditions
, actions
, error
) &&
339 HostPermissionsChecker(extension
, actions
, error
));
343 bool WebRequestRulesRegistry::HostPermissionsChecker(
344 const Extension
* extension
,
345 const WebRequestActionSet
* actions
,
346 std::string
* error
) {
347 if (PermissionsData::HasEffectiveAccessToAllHosts(extension
))
350 // Without the permission for all URLs, actions with the STRATEGY_DEFAULT
351 // should not be registered, they would never be able to execute.
352 for (WebRequestActionSet::Actions::const_iterator action_iter
=
353 actions
->actions().begin();
354 action_iter
!= actions
->actions().end();
356 if ((*action_iter
)->host_permissions_strategy() ==
357 WebRequestAction::STRATEGY_DEFAULT
) {
358 *error
= ErrorUtils::FormatErrorMessage(kAllURLsPermissionNeeded
,
359 (*action_iter
)->GetName());
367 bool WebRequestRulesRegistry::StageChecker(
368 const WebRequestConditionSet
* conditions
,
369 const WebRequestActionSet
* actions
,
370 std::string
* error
) {
371 // Actions and conditions can be checked and executed in specific stages
372 // of each web request. A rule is inconsistent if there is an action that
373 // can only be triggered in stages in which no condition can be evaluated.
375 // In which stages there are conditions to evaluate.
376 int condition_stages
= 0;
377 for (WebRequestConditionSet::Conditions::const_iterator condition_iter
=
378 conditions
->conditions().begin();
379 condition_iter
!= conditions
->conditions().end();
381 condition_stages
|= (*condition_iter
)->stages();
384 for (WebRequestActionSet::Actions::const_iterator action_iter
=
385 actions
->actions().begin();
386 action_iter
!= actions
->actions().end();
388 // Test the intersection of bit masks, this is intentionally & and not &&.
389 if ((*action_iter
)->stages() & condition_stages
)
391 // We only get here if no matching condition was found.
392 *error
= ErrorUtils::FormatErrorMessage(kActionCannotBeExecuted
,
393 (*action_iter
)->GetName());
398 void WebRequestRulesRegistry::AddTriggeredRules(
399 const URLMatches
& url_matches
,
400 const WebRequestCondition::MatchData
& request_data
,
401 RuleSet
* result
) const {
402 for (URLMatches::const_iterator url_match
= url_matches
.begin();
403 url_match
!= url_matches
.end(); ++url_match
) {
404 RuleTriggers::const_iterator rule_trigger
= rule_triggers_
.find(*url_match
);
405 CHECK(rule_trigger
!= rule_triggers_
.end());
406 if (!ContainsKey(*result
, rule_trigger
->second
) &&
407 rule_trigger
->second
->conditions().IsFulfilled(*url_match
,
409 result
->insert(rule_trigger
->second
);
413 } // namespace extensions