• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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