- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / declarative_webrequest / webrequest_rules_registry.cc
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 "chrome/common/extensions/extension.h"
19 #include "chrome/common/extensions/permissions/permissions_data.h"
20 #include "extensions/common/error_utils.h"
21 #include "net/url_request/url_request.h"
22
23 namespace {
24
25 const char kActionCannotBeExecuted[] = "The action '*' can never be executed "
26     "because there are is no time in the request life-cycle during which the "
27     "conditions can be checked and the action can possibly be executed.";
28
29 const char kAllURLsPermissionNeeded[] =
30     "To execute the action '*', you need to request host permission for all "
31     "hosts.";
32
33 }  // namespace
34
35 namespace extensions {
36
37 WebRequestRulesRegistry::WebRequestRulesRegistry(
38     Profile* profile,
39     RulesCacheDelegate* cache_delegate)
40     : RulesRegistry(profile,
41                     declarative_webrequest_constants::kOnRequest,
42                     content::BrowserThread::IO,
43                     cache_delegate),
44       profile_id_(profile) {
45   if (profile)
46     extension_info_map_ = ExtensionSystem::Get(profile)->info_map();
47 }
48
49 std::set<const WebRequestRule*> WebRequestRulesRegistry::GetMatches(
50     const WebRequestData& request_data_without_ids) const {
51   RuleSet result;
52
53   WebRequestDataWithMatchIds request_data(&request_data_without_ids);
54   request_data.url_match_ids = url_matcher_.MatchURL(
55       request_data.data->request->url());
56   request_data.first_party_url_match_ids = url_matcher_.MatchURL(
57       request_data.data->request->first_party_for_cookies());
58
59   // 1st phase -- add all rules with some conditions without UrlFilter
60   // attributes.
61   for (RuleSet::const_iterator it = rules_with_untriggered_conditions_.begin();
62        it != rules_with_untriggered_conditions_.end(); ++it) {
63     if ((*it)->conditions().IsFulfilled(-1, request_data))
64       result.insert(*it);
65   }
66
67   // 2nd phase -- add all rules with some conditions triggered by URL matches.
68   AddTriggeredRules(request_data.url_match_ids, request_data, &result);
69   AddTriggeredRules(request_data.first_party_url_match_ids,
70                     request_data, &result);
71
72   return result;
73 }
74
75 std::list<LinkedPtrEventResponseDelta> WebRequestRulesRegistry::CreateDeltas(
76     const ExtensionInfoMap* extension_info_map,
77     const WebRequestData& request_data,
78     bool crosses_incognito) {
79   if (webrequest_rules_.empty())
80     return std::list<LinkedPtrEventResponseDelta>();
81
82   std::set<const WebRequestRule*> matches = GetMatches(request_data);
83
84   // Sort all matching rules by their priority so that they can be processed
85   // in decreasing order.
86   typedef std::pair<WebRequestRule::Priority, WebRequestRule::GlobalRuleId>
87       PriorityRuleIdPair;
88   std::vector<PriorityRuleIdPair> ordered_matches;
89   ordered_matches.reserve(matches.size());
90   for (std::set<const WebRequestRule*>::iterator i = matches.begin();
91        i != matches.end(); ++i) {
92     ordered_matches.push_back(make_pair((*i)->priority(), (*i)->id()));
93   }
94   // Sort from rbegin to rend in order to get descending priority order.
95   std::sort(ordered_matches.rbegin(), ordered_matches.rend());
96
97   // Build a map that maps each extension id to the minimum required priority
98   // for rules of that extension. Initially, this priority is -infinite and
99   // will be increased when the rules are processed and raise the bar via
100   // WebRequestIgnoreRulesActions.
101   typedef std::string ExtensionId;
102   typedef std::map<ExtensionId, WebRequestRule::Priority> MinPriorities;
103   typedef std::map<ExtensionId, std::set<std::string> > IgnoreTags;
104   MinPriorities min_priorities;
105   IgnoreTags ignore_tags;
106   for (std::vector<PriorityRuleIdPair>::iterator i = ordered_matches.begin();
107        i != ordered_matches.end(); ++i) {
108     const WebRequestRule::GlobalRuleId& rule_id = i->second;
109     const ExtensionId& extension_id = rule_id.first;
110     min_priorities[extension_id] = std::numeric_limits<int>::min();
111   }
112
113   // Create deltas until we have passed the minimum priority.
114   std::list<LinkedPtrEventResponseDelta> result;
115   for (std::vector<PriorityRuleIdPair>::iterator i = ordered_matches.begin();
116        i != ordered_matches.end(); ++i) {
117     const WebRequestRule::Priority priority_of_rule = i->first;
118     const WebRequestRule::GlobalRuleId& rule_id = i->second;
119     const ExtensionId& extension_id = rule_id.first;
120     const WebRequestRule* rule =
121         webrequest_rules_[rule_id.first][rule_id.second].get();
122     CHECK(rule);
123
124     // Skip rule if a previous rule of this extension instructed to ignore
125     // all rules with a lower priority than min_priorities[extension_id].
126     int current_min_priority = min_priorities[extension_id];
127     if (priority_of_rule < current_min_priority)
128       continue;
129
130     if (!rule->tags().empty() && !ignore_tags[extension_id].empty()) {
131       bool ignore_rule = false;
132       const WebRequestRule::Tags& tags = rule->tags();
133       for (WebRequestRule::Tags::const_iterator i = tags.begin();
134            !ignore_rule && i != tags.end();
135            ++i) {
136         ignore_rule |= ContainsKey(ignore_tags[extension_id], *i);
137       }
138       if (ignore_rule)
139         continue;
140     }
141
142     std::list<LinkedPtrEventResponseDelta> rule_result;
143     WebRequestAction::ApplyInfo apply_info = {
144       extension_info_map, request_data, crosses_incognito, &rule_result,
145       &ignore_tags[extension_id]
146     };
147     rule->Apply(&apply_info);
148     result.splice(result.begin(), rule_result);
149
150     min_priorities[extension_id] = std::max(current_min_priority,
151                                             rule->GetMinimumPriority());
152   }
153   return result;
154 }
155
156 std::string WebRequestRulesRegistry::AddRulesImpl(
157     const std::string& extension_id,
158     const std::vector<linked_ptr<RulesRegistry::Rule> >& rules) {
159   typedef std::pair<WebRequestRule::RuleId, linked_ptr<WebRequestRule> >
160       IdRulePair;
161   typedef std::vector<IdRulePair> RulesVector;
162
163   base::Time extension_installation_time =
164       GetExtensionInstallationTime(extension_id);
165
166   std::string error;
167   RulesVector new_webrequest_rules;
168   new_webrequest_rules.reserve(rules.size());
169   const Extension* extension =
170       extension_info_map_->extensions().GetByID(extension_id);
171   RulesMap& registered_rules = webrequest_rules_[extension_id];
172
173   for (std::vector<linked_ptr<RulesRegistry::Rule> >::const_iterator rule =
174        rules.begin(); rule != rules.end(); ++rule) {
175     const WebRequestRule::RuleId& rule_id(*(*rule)->id);
176     DCHECK(registered_rules.find(rule_id) == registered_rules.end());
177
178     scoped_ptr<WebRequestRule> webrequest_rule(WebRequestRule::Create(
179         url_matcher_.condition_factory(),
180         extension, extension_installation_time, *rule,
181         base::Bind(&Checker, base::Unretained(extension)),
182         &error));
183     if (!error.empty()) {
184       // We don't return here, because we want to clear temporary
185       // condition sets in the url_matcher_.
186       break;
187     }
188
189     new_webrequest_rules.push_back(
190         IdRulePair(rule_id, make_linked_ptr(webrequest_rule.release())));
191   }
192
193   if (!error.empty()) {
194     // Clean up temporary condition sets created during rule creation.
195     url_matcher_.ClearUnusedConditionSets();
196     return error;
197   }
198
199   // Wohoo, everything worked fine.
200   registered_rules.insert(new_webrequest_rules.begin(),
201                           new_webrequest_rules.end());
202
203   // Create the triggers.
204   for (RulesVector::const_iterator i = new_webrequest_rules.begin();
205        i != new_webrequest_rules.end(); ++i) {
206     URLMatcherConditionSet::Vector url_condition_sets;
207     const WebRequestConditionSet& conditions = i->second->conditions();
208     conditions.GetURLMatcherConditionSets(&url_condition_sets);
209     for (URLMatcherConditionSet::Vector::iterator j =
210          url_condition_sets.begin(); j != url_condition_sets.end(); ++j) {
211       rule_triggers_[(*j)->id()] = i->second.get();
212     }
213   }
214
215   // Register url patterns in |url_matcher_| and
216   // |rules_with_untriggered_conditions_|.
217   URLMatcherConditionSet::Vector all_new_condition_sets;
218   for (RulesVector::const_iterator i = new_webrequest_rules.begin();
219        i != new_webrequest_rules.end(); ++i) {
220     i->second->conditions().GetURLMatcherConditionSets(&all_new_condition_sets);
221     if (i->second->conditions().HasConditionsWithoutUrls())
222       rules_with_untriggered_conditions_.insert(i->second.get());
223   }
224   url_matcher_.AddConditionSets(all_new_condition_sets);
225
226   ClearCacheOnNavigation();
227
228   if (profile_id_ && !registered_rules.empty()) {
229     content::BrowserThread::PostTask(
230         content::BrowserThread::UI, FROM_HERE,
231         base::Bind(&extension_web_request_api_helpers::NotifyWebRequestAPIUsed,
232                    profile_id_, make_scoped_refptr(extension)));
233   }
234
235   return std::string();
236 }
237
238 std::string WebRequestRulesRegistry::RemoveRulesImpl(
239     const std::string& extension_id,
240     const std::vector<std::string>& rule_identifiers) {
241   // URLMatcherConditionSet IDs that can be removed from URLMatcher.
242   std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher;
243   RulesMap& registered_rules = webrequest_rules_[extension_id];
244
245   for (std::vector<std::string>::const_iterator i = rule_identifiers.begin();
246        i != rule_identifiers.end(); ++i) {
247     // Skip unknown rules.
248     RulesMap::iterator webrequest_rules_entry = registered_rules.find(*i);
249     if (webrequest_rules_entry == registered_rules.end())
250       continue;
251
252     // Remove all triggers but collect their IDs.
253     CleanUpAfterRule(webrequest_rules_entry->second.get(),
254                      &remove_from_url_matcher);
255
256     // Removes the owning references to (and thus deletes) the rule.
257     registered_rules.erase(webrequest_rules_entry);
258   }
259   if (registered_rules.empty())
260     webrequest_rules_.erase(extension_id);
261
262   // Clear URLMatcher based on condition_set_ids that are not needed any more.
263   url_matcher_.RemoveConditionSets(remove_from_url_matcher);
264
265   ClearCacheOnNavigation();
266
267   return std::string();
268 }
269
270 std::string WebRequestRulesRegistry::RemoveAllRulesImpl(
271     const std::string& extension_id) {
272   // First we get out all URLMatcherConditionSets and remove the rule references
273   // from |rules_with_untriggered_conditions_|.
274   std::vector<URLMatcherConditionSet::ID> remove_from_url_matcher;
275   for (RulesMap::const_iterator it = webrequest_rules_[extension_id].begin();
276        it != webrequest_rules_[extension_id].end();
277        ++it) {
278     CleanUpAfterRule(it->second.get(), &remove_from_url_matcher);
279   }
280   url_matcher_.RemoveConditionSets(remove_from_url_matcher);
281
282   webrequest_rules_.erase(extension_id);
283   ClearCacheOnNavigation();
284   return std::string();
285 }
286
287 void WebRequestRulesRegistry::CleanUpAfterRule(
288     const WebRequestRule* rule,
289     std::vector<URLMatcherConditionSet::ID>* remove_from_url_matcher) {
290   URLMatcherConditionSet::Vector condition_sets;
291   rule->conditions().GetURLMatcherConditionSets(&condition_sets);
292   for (URLMatcherConditionSet::Vector::iterator j = condition_sets.begin();
293        j != condition_sets.end();
294        ++j) {
295     remove_from_url_matcher->push_back((*j)->id());
296     rule_triggers_.erase((*j)->id());
297   }
298   rules_with_untriggered_conditions_.erase(rule);
299 }
300
301 bool WebRequestRulesRegistry::IsEmpty() const {
302   // Easy first.
303   if (!rule_triggers_.empty() && url_matcher_.IsEmpty())
304     return false;
305
306   // Now all the registered rules for each extensions.
307   for (std::map<WebRequestRule::ExtensionId, RulesMap>::const_iterator it =
308            webrequest_rules_.begin();
309        it != webrequest_rules_.end();
310        ++it) {
311     if (!it->second.empty())
312       return false;
313   }
314   return true;
315 }
316
317 WebRequestRulesRegistry::~WebRequestRulesRegistry() {}
318
319 base::Time WebRequestRulesRegistry::GetExtensionInstallationTime(
320     const std::string& extension_id) const {
321   return extension_info_map_->GetInstallTime(extension_id);
322 }
323
324 void WebRequestRulesRegistry::ClearCacheOnNavigation() {
325   extension_web_request_api_helpers::ClearCacheOnNavigation();
326 }
327
328 // static
329 bool WebRequestRulesRegistry::Checker(const Extension* extension,
330                                       const WebRequestConditionSet* conditions,
331                                       const WebRequestActionSet* actions,
332                                       std::string* error) {
333   return (StageChecker(conditions, actions, error) &&
334           HostPermissionsChecker(extension, actions, error));
335 }
336
337 // static
338 bool WebRequestRulesRegistry::HostPermissionsChecker(
339     const Extension* extension,
340     const WebRequestActionSet* actions,
341     std::string* error) {
342   if (PermissionsData::HasEffectiveAccessToAllHosts(extension))
343     return true;
344
345   // Without the permission for all URLs, actions with the STRATEGY_DEFAULT
346   // should not be registered, they would never be able to execute.
347   for (WebRequestActionSet::Actions::const_iterator action_iter =
348            actions->actions().begin();
349        action_iter != actions->actions().end();
350        ++action_iter) {
351     if ((*action_iter)->host_permissions_strategy() ==
352         WebRequestAction::STRATEGY_DEFAULT) {
353       *error = ErrorUtils::FormatErrorMessage(kAllURLsPermissionNeeded,
354                                               (*action_iter)->GetName());
355       return false;
356     }
357   }
358   return true;
359 }
360
361 // static
362 bool WebRequestRulesRegistry::StageChecker(
363     const WebRequestConditionSet* conditions,
364     const WebRequestActionSet* actions,
365     std::string* error) {
366   // Actions and conditions can be checked and executed in specific stages
367   // of each web request. A rule is inconsistent if there is an action that
368   // can only be triggered in stages in which no condition can be evaluated.
369
370   // In which stages there are conditions to evaluate.
371   int condition_stages = 0;
372   for (WebRequestConditionSet::Conditions::const_iterator condition_iter =
373            conditions->conditions().begin();
374        condition_iter != conditions->conditions().end();
375        ++condition_iter) {
376     condition_stages |= (*condition_iter)->stages();
377   }
378
379   for (WebRequestActionSet::Actions::const_iterator action_iter =
380            actions->actions().begin();
381        action_iter != actions->actions().end();
382        ++action_iter) {
383     // Test the intersection of bit masks, this is intentionally & and not &&.
384     if ((*action_iter)->stages() & condition_stages)
385       continue;
386     // We only get here if no matching condition was found.
387     *error = ErrorUtils::FormatErrorMessage(kActionCannotBeExecuted,
388                                             (*action_iter)->GetName());
389     return false;
390   }
391   return true;
392 }
393 void WebRequestRulesRegistry::AddTriggeredRules(
394     const URLMatches& url_matches,
395     const WebRequestCondition::MatchData& request_data,
396     RuleSet* result) const {
397   for (URLMatches::const_iterator url_match = url_matches.begin();
398        url_match != url_matches.end(); ++url_match) {
399     RuleTriggers::const_iterator rule_trigger = rule_triggers_.find(*url_match);
400     CHECK(rule_trigger != rule_triggers_.end());
401     if (!ContainsKey(*result, rule_trigger->second) &&
402         rule_trigger->second->conditions().IsFulfilled(*url_match,
403                                                        request_data))
404       result->insert(rule_trigger->second);
405   }
406 }
407
408 }  // namespace extensions