1bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant// Use of this source code is governed by a BSD-style license that can be
3f5256e16dfc425c1d466f6308d4026d529ce9e0bHoward Hinnant// found in the LICENSE file.
4bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
5b64f8b07c104c6cc986570ac8ee0ed16a9f23976Howard Hinnant#include "extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h"
6b64f8b07c104c6cc986570ac8ee0ed16a9f23976Howard Hinnant
7bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include <algorithm>
8bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include <limits>
9bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include <utility>
10bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
11bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "base/bind.h"
12bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "base/stl_util.h"
13bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/browser/api/declarative_webrequest/webrequest_condition.h"
14bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
15bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/browser/api/web_request/web_request_api_helpers.h"
16bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/browser/api/web_request/web_request_permissions.h"
17bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/browser/extension_system.h"
18bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/common/error_utils.h"
19bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/common/extension.h"
20bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "extensions/common/permissions/permissions_data.h"
21bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "net/url_request/url_request.h"
22bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
23bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnantusing url_matcher::URLMatcherConditionSet;
24bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
25bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnantnamespace {
26bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
27const 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
31const char kAllURLsPermissionNeeded[] =
32    "To execute the action '*', you need to request host permission for all "
33    "hosts.";
34
35}  // namespace
36
37namespace extensions {
38
39WebRequestRulesRegistry::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
53std::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
79std::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
160std::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
242std::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
274std::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
291void 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
305bool 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
321WebRequestRulesRegistry::~WebRequestRulesRegistry() {}
322
323base::Time WebRequestRulesRegistry::GetExtensionInstallationTime(
324    const std::string& extension_id) const {
325  return extension_info_map_->GetInstallTime(extension_id);
326}
327
328void WebRequestRulesRegistry::ClearCacheOnNavigation() {
329  extension_web_request_api_helpers::ClearCacheOnNavigation();
330}
331
332// static
333bool 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
342bool 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
366bool 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}
397void 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