Enable Enterprise enrollment on desktop builds.
[chromium-blink-merge.git] / chrome / browser / extensions / api / declarative_webrequest / webrequest_rules_registry.cc
blob189a441b16891670e90929cd75b85a9440fb4eab
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.
5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry.h"
7 #include <algorithm>
8 #include <limits>
9 #include <utility>
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"
24 using url_matcher::URLMatcherConditionSet;
26 namespace {
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.";
32 const char kAllURLsPermissionNeeded[] =
33 "To execute the action '*', you need to request host permission for all "
34 "hosts.";
36 } // namespace
38 namespace extensions {
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();
54 std::set<const WebRequestRule*> WebRequestRulesRegistry::GetMatches(
55 const WebRequestData& request_data_without_ids) const {
56 RuleSet result;
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());
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);
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);
77 return result;
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>();
87 std::set<const WebRequestRule*> matches = GetMatches(request_data);
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()));
99 // Sort from rbegin to rend in order to get descending priority order.
100 std::sort(ordered_matches.rbegin(), ordered_matches.rend());
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();
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);
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;
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);
143 if (ignore_rule)
144 continue;
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]
152 rule->Apply(&apply_info);
153 result.splice(result.begin(), rule_result);
155 min_priorities[extension_id] = std::max(current_min_priority,
156 rule->GetMinimumPriority());
158 return result;
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;
168 base::Time extension_installation_time =
169 GetExtensionInstallationTime(extension_id);
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];
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());
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;
194 new_webrequest_rules.push_back(
195 IdRulePair(rule_id, make_linked_ptr(webrequest_rule.release())));
198 if (!error.empty()) {
199 // Clean up temporary condition sets created during rule creation.
200 url_matcher_.ClearUnusedConditionSets();
201 return error;
204 // Wohoo, everything worked fine.
205 registered_rules.insert(new_webrequest_rules.begin(),
206 new_webrequest_rules.end());
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();
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());
229 url_matcher_.AddConditionSets(all_new_condition_sets);
231 ClearCacheOnNavigation();
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)));
240 return std::string();
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];
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;
257 // Remove all triggers but collect their IDs.
258 CleanUpAfterRule(webrequest_rules_entry->second.get(),
259 &remove_from_url_matcher);
261 // Removes the owning references to (and thus deletes) the rule.
262 registered_rules.erase(webrequest_rules_entry);
264 if (registered_rules.empty())
265 webrequest_rules_.erase(extension_id);
267 // Clear URLMatcher based on condition_set_ids that are not needed any more.
268 url_matcher_.RemoveConditionSets(remove_from_url_matcher);
270 ClearCacheOnNavigation();
272 return std::string();
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);
285 url_matcher_.RemoveConditionSets(remove_from_url_matcher);
287 webrequest_rules_.erase(extension_id);
288 ClearCacheOnNavigation();
289 return std::string();
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());
303 rules_with_untriggered_conditions_.erase(rule);
306 bool WebRequestRulesRegistry::IsEmpty() const {
307 // Easy first.
308 if (!rule_triggers_.empty() && url_matcher_.IsEmpty())
309 return false;
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;
319 return true;
322 WebRequestRulesRegistry::~WebRequestRulesRegistry() {}
324 base::Time WebRequestRulesRegistry::GetExtensionInstallationTime(
325 const std::string& extension_id) const {
326 return extension_info_map_->GetInstallTime(extension_id);
329 void WebRequestRulesRegistry::ClearCacheOnNavigation() {
330 extension_web_request_api_helpers::ClearCacheOnNavigation();
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));
342 // static
343 bool WebRequestRulesRegistry::HostPermissionsChecker(
344 const Extension* extension,
345 const WebRequestActionSet* actions,
346 std::string* error) {
347 if (PermissionsData::HasEffectiveAccessToAllHosts(extension))
348 return true;
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;
363 return true;
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.
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();
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;
396 return true;
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);
413 } // namespace extensions