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/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/extensions/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
WebRequestRulesRegistry(Profile * profile,RulesCacheDelegate * cache_delegate,const WebViewKey & webview_key)39 WebRequestRulesRegistry::WebRequestRulesRegistry(
40 Profile* profile,
41 RulesCacheDelegate* cache_delegate,
42 const WebViewKey& webview_key)
43 : RulesRegistry(profile,
44 declarative_webrequest_constants::kOnRequest,
45 content::BrowserThread::IO,
46 cache_delegate,
47 webview_key),
48 profile_id_(profile) {
49 if (profile)
50 extension_info_map_ = ExtensionSystem::Get(profile)->info_map();
51 }
52
GetMatches(const WebRequestData & request_data_without_ids) const53 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
CreateDeltas(const InfoMap * extension_info_map,const WebRequestData & request_data,bool crosses_incognito)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
AddRulesImpl(const std::string & extension_id,const std::vector<linked_ptr<RulesRegistry::Rule>> & rules)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 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 (profile_id_ && !registered_rules.empty()) {
233 content::BrowserThread::PostTask(
234 content::BrowserThread::UI, FROM_HERE,
235 base::Bind(&extension_web_request_api_helpers::NotifyWebRequestAPIUsed,
236 profile_id_, make_scoped_refptr(extension)));
237 }
238
239 return std::string();
240 }
241
RemoveRulesImpl(const std::string & extension_id,const std::vector<std::string> & rule_identifiers)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
RemoveAllRulesImpl(const std::string & extension_id)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
CleanUpAfterRule(const WebRequestRule * rule,std::vector<URLMatcherConditionSet::ID> * remove_from_url_matcher)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
IsEmpty() const305 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
~WebRequestRulesRegistry()321 WebRequestRulesRegistry::~WebRequestRulesRegistry() {}
322
GetExtensionInstallationTime(const std::string & extension_id) const323 base::Time WebRequestRulesRegistry::GetExtensionInstallationTime(
324 const std::string& extension_id) const {
325 return extension_info_map_->GetInstallTime(extension_id);
326 }
327
ClearCacheOnNavigation()328 void WebRequestRulesRegistry::ClearCacheOnNavigation() {
329 extension_web_request_api_helpers::ClearCacheOnNavigation();
330 }
331
332 // static
Checker(const Extension * extension,const WebRequestConditionSet * conditions,const WebRequestActionSet * actions,std::string * error)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
HostPermissionsChecker(const Extension * extension,const WebRequestActionSet * actions,std::string * error)342 bool WebRequestRulesRegistry::HostPermissionsChecker(
343 const Extension* extension,
344 const WebRequestActionSet* actions,
345 std::string* error) {
346 if (PermissionsData::HasEffectiveAccessToAllHosts(extension))
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
StageChecker(const WebRequestConditionSet * conditions,const WebRequestActionSet * actions,std::string * error)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 }
AddTriggeredRules(const URLMatches & url_matches,const WebRequestCondition::MatchData & request_data,RuleSet * result) const397 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
413