[Extensions] Fix IsBeingUpgraded runtime data bug and make RuntimeData take ids
[chromium-blink-merge.git] / extensions / browser / api / declarative_webrequest / webrequest_rules_registry.cc
blob21f7a0a00368fda4616a219533cf3b034a23bc4c
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 "extensions/browser/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 "extensions/browser/api/declarative_webrequest/webrequest_condition.h"
14 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
15 #include "extensions/browser/api/web_request/web_request_api_helpers.h"
16 #include "extensions/browser/api/web_request/web_request_permissions.h"
17 #include "extensions/browser/extension_system.h"
18 #include "extensions/common/error_utils.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/permissions/permissions_data.h"
21 #include "net/url_request/url_request.h"
23 using url_matcher::URLMatcherConditionSet;
25 namespace {
27 const 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.";
31 const char kAllURLsPermissionNeeded[] =
32 "To execute the action '*', you need to request host permission for all "
33 "hosts.";
35 } // namespace
37 namespace extensions {
39 WebRequestRulesRegistry::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();
53 std::set<const WebRequestRule*> WebRequestRulesRegistry::GetMatches(
54 const WebRequestData& request_data_without_ids) const {
55 RuleSet result;
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());
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);
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);
76 return result;
79 std::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>();
86 std::set<const WebRequestRule*> matches = GetMatches(request_data);
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()));
98 // Sort from rbegin to rend in order to get descending priority order.
99 std::sort(ordered_matches.rbegin(), ordered_matches.rend());
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();
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);
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;
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);
142 if (ignore_rule)
143 continue;
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]
151 rule->Apply(&apply_info);
152 result.splice(result.begin(), rule_result);
154 min_priorities[extension_id] = std::max(current_min_priority,
155 rule->GetMinimumPriority());
157 return result;
160 std::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;
167 base::Time extension_installation_time =
168 GetExtensionInstallationTime(extension_id);
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];
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());
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;
193 new_webrequest_rules.push_back(
194 IdRulePair(rule_id, make_linked_ptr(webrequest_rule.release())));
197 if (!error.empty()) {
198 // Clean up temporary condition sets created during rule creation.
199 url_matcher_.ClearUnusedConditionSets();
200 return error;
203 // Wohoo, everything worked fine.
204 registered_rules.insert(new_webrequest_rules.begin(),
205 new_webrequest_rules.end());
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();
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());
228 url_matcher_.AddConditionSets(all_new_condition_sets);
230 ClearCacheOnNavigation();
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_, extension->id()));
239 return std::string();
242 std::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];
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;
256 // Remove all triggers but collect their IDs.
257 CleanUpAfterRule(webrequest_rules_entry->second.get(),
258 &remove_from_url_matcher);
260 // Removes the owning references to (and thus deletes) the rule.
261 registered_rules.erase(webrequest_rules_entry);
263 if (registered_rules.empty())
264 webrequest_rules_.erase(extension_id);
266 // Clear URLMatcher based on condition_set_ids that are not needed any more.
267 url_matcher_.RemoveConditionSets(remove_from_url_matcher);
269 ClearCacheOnNavigation();
271 return std::string();
274 std::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);
284 url_matcher_.RemoveConditionSets(remove_from_url_matcher);
286 webrequest_rules_.erase(extension_id);
287 ClearCacheOnNavigation();
288 return std::string();
291 void 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());
302 rules_with_untriggered_conditions_.erase(rule);
305 bool WebRequestRulesRegistry::IsEmpty() const {
306 // Easy first.
307 if (!rule_triggers_.empty() && url_matcher_.IsEmpty())
308 return false;
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;
318 return true;
321 WebRequestRulesRegistry::~WebRequestRulesRegistry() {}
323 base::Time WebRequestRulesRegistry::GetExtensionInstallationTime(
324 const std::string& extension_id) const {
325 return extension_info_map_->GetInstallTime(extension_id);
328 void WebRequestRulesRegistry::ClearCacheOnNavigation() {
329 extension_web_request_api_helpers::ClearCacheOnNavigation();
332 // static
333 bool 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));
341 // static
342 bool WebRequestRulesRegistry::HostPermissionsChecker(
343 const Extension* extension,
344 const WebRequestActionSet* actions,
345 std::string* error) {
346 if (extension->permissions_data()->HasEffectiveAccessToAllHosts())
347 return true;
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;
362 return true;
365 // static
366 bool 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.
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();
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;
395 return true;
397 void 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);
412 } // namespace extensions