Upstream version 10.38.222.0
[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/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