Home | History | Annotate | Download | only in declarative_webrequest
      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/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"
     23 
     24 using url_matcher::URLMatcherConditionSet;
     25 
     26 namespace {
     27 
     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.";
     31 
     32 const char kAllURLsPermissionNeeded[] =
     33     "To execute the action '*', you need to request host permission for all "
     34     "hosts.";
     35 
     36 }  // namespace
     37 
     38 namespace extensions {
     39 
     40 WebRequestRulesRegistry::WebRequestRulesRegistry(
     41     Profile* profile,
     42     RulesCacheDelegate* cache_delegate,
     43     const WebViewKey& webview_key)
     44     : RulesRegistry(profile,
     45                     declarative_webrequest_constants::kOnRequest,
     46                     content::BrowserThread::IO,
     47                     cache_delegate,
     48                     webview_key),
     49       profile_id_(profile) {
     50   if (profile)
     51     extension_info_map_ = ExtensionSystem::Get(profile)->info_map();
     52 }
     53 
     54 std::set<const WebRequestRule*> WebRequestRulesRegistry::GetMatches(
     55     const WebRequestData& request_data_without_ids) const {
     56   RuleSet result;
     57 
     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());
     63 
     64   // 1st phase -- add all rules with some conditions without UrlFilter
     65   // attributes.
     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))
     69       result.insert(*it);
     70   }
     71 
     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);
     76 
     77   return result;
     78 }
     79 
     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>();
     86 
     87   std::set<const WebRequestRule*> matches = GetMatches(request_data);
     88 
     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>
     92       PriorityRuleIdPair;
     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()));
     98   }
     99   // Sort from rbegin to rend in order to get descending priority order.
    100   std::sort(ordered_matches.rbegin(), ordered_matches.rend());
    101 
    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();
    116   }
    117 
    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();
    127     CHECK(rule);
    128 
    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)
    133       continue;
    134 
    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();
    140            ++i) {
    141         ignore_rule |= ContainsKey(ignore_tags[extension_id], *i);
    142       }
    143       if (ignore_rule)
    144         continue;
    145     }
    146 
    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]
    151     };
    152     rule->Apply(&apply_info);
    153     result.splice(result.begin(), rule_result);
    154 
    155     min_priorities[extension_id] = std::max(current_min_priority,
    156                                             rule->GetMinimumPriority());
    157   }
    158   return result;
    159 }
    160 
    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> >
    165       IdRulePair;
    166   typedef std::vector<IdRulePair> RulesVector;
    167 
    168   base::Time extension_installation_time =
    169       GetExtensionInstallationTime(extension_id);
    170 
    171   std::string error;
    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];
    177 
    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());
    182 
    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)),
    187         &error));
    188     if (!error.empty()) {
    189       // We don't return here, because we want to clear temporary
    190       // condition sets in the url_matcher_.
    191       break;
    192     }
    193 
    194     new_webrequest_rules.push_back(
    195         IdRulePair(rule_id, make_linked_ptr(webrequest_rule.release())));
    196   }
    197 
    198   if (!error.empty()) {
    199     // Clean up temporary condition sets created during rule creation.
    200     url_matcher_.ClearUnusedConditionSets();
    201     return error;
    202   }
    203 
    204   // Wohoo, everything worked fine.
    205   registered_rules.insert(new_webrequest_rules.begin(),
    206                           new_webrequest_rules.end());
    207 
    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();
    217     }
    218   }
    219 
    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());
    228   }
    229   url_matcher_.AddConditionSets(all_new_condition_sets);
    230 
    231   ClearCacheOnNavigation();
    232 
    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)));
    238   }
    239 
    240   return std::string();
    241 }
    242 
    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];
    249 
    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())
    255       continue;
    256 
    257     // Remove all triggers but collect their IDs.
    258     CleanUpAfterRule(webrequest_rules_entry->second.get(),
    259                      &remove_from_url_matcher);
    260 
    261     // Removes the owning references to (and thus deletes) the rule.
    262     registered_rules.erase(webrequest_rules_entry);
    263   }
    264   if (registered_rules.empty())
    265     webrequest_rules_.erase(extension_id);
    266 
    267   // Clear URLMatcher based on condition_set_ids that are not needed any more.
    268   url_matcher_.RemoveConditionSets(remove_from_url_matcher);
    269 
    270   ClearCacheOnNavigation();
    271 
    272   return std::string();
    273 }
    274 
    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();
    282        ++it) {
    283     CleanUpAfterRule(it->second.get(), &remove_from_url_matcher);
    284   }
    285   url_matcher_.RemoveConditionSets(remove_from_url_matcher);
    286 
    287   webrequest_rules_.erase(extension_id);
    288   ClearCacheOnNavigation();
    289   return std::string();
    290 }
    291 
    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();
    299        ++j) {
    300     remove_from_url_matcher->push_back((*j)->id());
    301     rule_triggers_.erase((*j)->id());
    302   }
    303   rules_with_untriggered_conditions_.erase(rule);
    304 }
    305 
    306 bool WebRequestRulesRegistry::IsEmpty() const {
    307   // Easy first.
    308   if (!rule_triggers_.empty() && url_matcher_.IsEmpty())
    309     return false;
    310 
    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();
    315        ++it) {
    316     if (!it->second.empty())
    317       return false;
    318   }
    319   return true;
    320 }
    321 
    322 WebRequestRulesRegistry::~WebRequestRulesRegistry() {}
    323 
    324 base::Time WebRequestRulesRegistry::GetExtensionInstallationTime(
    325     const std::string& extension_id) const {
    326   return extension_info_map_->GetInstallTime(extension_id);
    327 }
    328 
    329 void WebRequestRulesRegistry::ClearCacheOnNavigation() {
    330   extension_web_request_api_helpers::ClearCacheOnNavigation();
    331 }
    332 
    333 // static
    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));
    340 }
    341 
    342 // static
    343 bool WebRequestRulesRegistry::HostPermissionsChecker(
    344     const Extension* extension,
    345     const WebRequestActionSet* actions,
    346     std::string* error) {
    347   if (extension->permissions_data()->HasEffectiveAccessToAllHosts())
    348     return true;
    349 
    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();
    355        ++action_iter) {
    356     if ((*action_iter)->host_permissions_strategy() ==
    357         WebRequestAction::STRATEGY_DEFAULT) {
    358       *error = ErrorUtils::FormatErrorMessage(kAllURLsPermissionNeeded,
    359                                               (*action_iter)->GetName());
    360       return false;
    361     }
    362   }
    363   return true;
    364 }
    365 
    366 // static
    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.
    374 
    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();
    380        ++condition_iter) {
    381     condition_stages |= (*condition_iter)->stages();
    382   }
    383 
    384   for (WebRequestActionSet::Actions::const_iterator action_iter =
    385            actions->actions().begin();
    386        action_iter != actions->actions().end();
    387        ++action_iter) {
    388     // Test the intersection of bit masks, this is intentionally & and not &&.
    389     if ((*action_iter)->stages() & condition_stages)
    390       continue;
    391     // We only get here if no matching condition was found.
    392     *error = ErrorUtils::FormatErrorMessage(kActionCannotBeExecuted,
    393                                             (*action_iter)->GetName());
    394     return false;
    395   }
    396   return true;
    397 }
    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,
    408                                                        request_data))
    409       result->insert(rule_trigger->second);
    410   }
    411 }
    412 
    413 }  // namespace extensions
    414