OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_
registry.h" | |
6 | |
7 #include <algorithm> | |
8 #include <limits> | |
9 #include <utility> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/stl_util.h" | |
13 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h" | |
14 #include "chrome/browser/extensions/api/web_request/web_request_permissions.h" | |
15 #include "extensions/browser/api/declarative_webrequest/webrequest_condition.h" | |
16 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.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" | |
22 | |
23 using url_matcher::URLMatcherConditionSet; | |
24 | |
25 namespace { | |
26 | |
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."; | |
30 | |
31 const char kAllURLsPermissionNeeded[] = | |
32 "To execute the action '*', you need to request host permission for all " | |
33 "hosts."; | |
34 | |
35 } // namespace | |
36 | |
37 namespace extensions { | |
38 | |
39 WebRequestRulesRegistry::WebRequestRulesRegistry( | |
40 content::BrowserContext* browser_context, | |
41 RulesCacheDelegate* cache_delegate, | |
42 const WebViewKey& webview_key) | |
43 : RulesRegistry(browser_context, | |
44 declarative_webrequest_constants::kOnRequest, | |
45 content::BrowserThread::IO, | |
46 cache_delegate, | |
47 webview_key), | |
48 browser_context_(browser_context) { | |
49 if (browser_context_) | |
50 extension_info_map_ = ExtensionSystem::Get(browser_context_)->info_map(); | |
51 } | |
52 | |
53 std::set<const WebRequestRule*> WebRequestRulesRegistry::GetMatches( | |
54 const WebRequestData& request_data_without_ids) const { | |
55 RuleSet result; | |
56 | |
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()); | |
62 | |
63 // 1st phase -- add all rules with some conditions without UrlFilter | |
64 // attributes. | |
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)) | |
68 result.insert(*it); | |
69 } | |
70 | |
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); | |
75 | |
76 return result; | |
77 } | |
78 | |
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>(); | |
85 | |
86 std::set<const WebRequestRule*> matches = GetMatches(request_data); | |
87 | |
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> | |
91 PriorityRuleIdPair; | |
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())); | |
97 } | |
98 // Sort from rbegin to rend in order to get descending priority order. | |
99 std::sort(ordered_matches.rbegin(), ordered_matches.rend()); | |
100 | |
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(); | |
115 } | |
116 | |
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(); | |
126 CHECK(rule); | |
127 | |
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) | |
132 continue; | |
133 | |
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(); | |
139 ++i) { | |
140 ignore_rule |= ContainsKey(ignore_tags[extension_id], *i); | |
141 } | |
142 if (ignore_rule) | |
143 continue; | |
144 } | |
145 | |
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] | |
150 }; | |
151 rule->Apply(&apply_info); | |
152 result.splice(result.begin(), rule_result); | |
153 | |
154 min_priorities[extension_id] = std::max(current_min_priority, | |
155 rule->GetMinimumPriority()); | |
156 } | |
157 return result; | |
158 } | |
159 | |
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> > | |
164 IdRulePair; | |
165 typedef std::vector<IdRulePair> RulesVector; | |
166 | |
167 base::Time extension_installation_time = | |
168 GetExtensionInstallationTime(extension_id); | |
169 | |
170 std::string error; | |
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]; | |
176 | |
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()); | |
181 | |
182 scoped_ptr<WebRequestRule> webrequest_rule(WebRequestRule::Create( | |
183 url_matcher_.condition_factory(), | |
184 browser_context(), extension, extension_installation_time, *rule, | |
185 base::Bind(&Checker, base::Unretained(extension)), | |
186 &error)); | |
187 if (!error.empty()) { | |
188 // We don't return here, because we want to clear temporary | |
189 // condition sets in the url_matcher_. | |
190 break; | |
191 } | |
192 | |
193 new_webrequest_rules.push_back( | |
194 IdRulePair(rule_id, make_linked_ptr(webrequest_rule.release()))); | |
195 } | |
196 | |
197 if (!error.empty()) { | |
198 // Clean up temporary condition sets created during rule creation. | |
199 url_matcher_.ClearUnusedConditionSets(); | |
200 return error; | |
201 } | |
202 | |
203 // Wohoo, everything worked fine. | |
204 registered_rules.insert(new_webrequest_rules.begin(), | |
205 new_webrequest_rules.end()); | |
206 | |
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(); | |
216 } | |
217 } | |
218 | |
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()); | |
227 } | |
228 url_matcher_.AddConditionSets(all_new_condition_sets); | |
229 | |
230 ClearCacheOnNavigation(); | |
231 | |
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_, make_scoped_refptr(extension))); | |
237 } | |
238 | |
239 return std::string(); | |
240 } | |
241 | |
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]; | |
248 | |
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()) | |
254 continue; | |
255 | |
256 // Remove all triggers but collect their IDs. | |
257 CleanUpAfterRule(webrequest_rules_entry->second.get(), | |
258 &remove_from_url_matcher); | |
259 | |
260 // Removes the owning references to (and thus deletes) the rule. | |
261 registered_rules.erase(webrequest_rules_entry); | |
262 } | |
263 if (registered_rules.empty()) | |
264 webrequest_rules_.erase(extension_id); | |
265 | |
266 // Clear URLMatcher based on condition_set_ids that are not needed any more. | |
267 url_matcher_.RemoveConditionSets(remove_from_url_matcher); | |
268 | |
269 ClearCacheOnNavigation(); | |
270 | |
271 return std::string(); | |
272 } | |
273 | |
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(); | |
281 ++it) { | |
282 CleanUpAfterRule(it->second.get(), &remove_from_url_matcher); | |
283 } | |
284 url_matcher_.RemoveConditionSets(remove_from_url_matcher); | |
285 | |
286 webrequest_rules_.erase(extension_id); | |
287 ClearCacheOnNavigation(); | |
288 return std::string(); | |
289 } | |
290 | |
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(); | |
298 ++j) { | |
299 remove_from_url_matcher->push_back((*j)->id()); | |
300 rule_triggers_.erase((*j)->id()); | |
301 } | |
302 rules_with_untriggered_conditions_.erase(rule); | |
303 } | |
304 | |
305 bool WebRequestRulesRegistry::IsEmpty() const { | |
306 // Easy first. | |
307 if (!rule_triggers_.empty() && url_matcher_.IsEmpty()) | |
308 return false; | |
309 | |
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(); | |
314 ++it) { | |
315 if (!it->second.empty()) | |
316 return false; | |
317 } | |
318 return true; | |
319 } | |
320 | |
321 WebRequestRulesRegistry::~WebRequestRulesRegistry() {} | |
322 | |
323 base::Time WebRequestRulesRegistry::GetExtensionInstallationTime( | |
324 const std::string& extension_id) const { | |
325 return extension_info_map_->GetInstallTime(extension_id); | |
326 } | |
327 | |
328 void WebRequestRulesRegistry::ClearCacheOnNavigation() { | |
329 extension_web_request_api_helpers::ClearCacheOnNavigation(); | |
330 } | |
331 | |
332 // static | |
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)); | |
339 } | |
340 | |
341 // static | |
342 bool WebRequestRulesRegistry::HostPermissionsChecker( | |
343 const Extension* extension, | |
344 const WebRequestActionSet* actions, | |
345 std::string* error) { | |
346 if (extension->permissions_data()->HasEffectiveAccessToAllHosts()) | |
347 return true; | |
348 | |
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(); | |
354 ++action_iter) { | |
355 if ((*action_iter)->host_permissions_strategy() == | |
356 WebRequestAction::STRATEGY_DEFAULT) { | |
357 *error = ErrorUtils::FormatErrorMessage(kAllURLsPermissionNeeded, | |
358 (*action_iter)->GetName()); | |
359 return false; | |
360 } | |
361 } | |
362 return true; | |
363 } | |
364 | |
365 // static | |
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. | |
373 | |
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(); | |
379 ++condition_iter) { | |
380 condition_stages |= (*condition_iter)->stages(); | |
381 } | |
382 | |
383 for (WebRequestActionSet::Actions::const_iterator action_iter = | |
384 actions->actions().begin(); | |
385 action_iter != actions->actions().end(); | |
386 ++action_iter) { | |
387 // Test the intersection of bit masks, this is intentionally & and not &&. | |
388 if ((*action_iter)->stages() & condition_stages) | |
389 continue; | |
390 // We only get here if no matching condition was found. | |
391 *error = ErrorUtils::FormatErrorMessage(kActionCannotBeExecuted, | |
392 (*action_iter)->GetName()); | |
393 return false; | |
394 } | |
395 return true; | |
396 } | |
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, | |
407 request_data)) | |
408 result->insert(rule_trigger->second); | |
409 } | |
410 } | |
411 | |
412 } // namespace extensions | |
OLD | NEW |