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"
10 #include "base/basictypes.h"
11 #include "base/json/json_reader.h"
12 #include "base/memory/linked_ptr.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/stl_util.h"
16 #include "base/test/values_test_util.h"
17 #include "base/values.h"
18 #include "chrome/common/extensions/extension_test_util.h"
19 #include "components/url_matcher/url_matcher_constants.h"
20 #include "content/public/test/test_browser_thread.h"
21 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
22 #include "extensions/browser/api/web_request/web_request_api_helpers.h"
23 #include "net/base/request_priority.h"
24 #include "net/url_request/url_request.h"
25 #include "net/url_request/url_request_test_util.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest-message.h"
28 #include "testing/gtest/include/gtest/gtest.h"
31 using extension_test_util::LoadManifest;
32 using extension_test_util::LoadManifestUnchecked;
33 using testing::HasSubstr;
34 using url_matcher::URLMatcher;
37 const char kExtensionId[] = "ext1";
38 const char kExtensionId2[] = "ext2";
39 const char kRuleId1[] = "rule1";
40 const char kRuleId2[] = "rule2";
41 const char kRuleId3[] = "rule3";
42 const char kRuleId4[] = "rule4";
45 namespace extensions {
47 namespace helpers = extension_web_request_api_helpers;
48 namespace keys = declarative_webrequest_constants;
49 namespace keys2 = url_matcher::url_matcher_constants;
51 class TestWebRequestRulesRegistry : public WebRequestRulesRegistry {
53 TestWebRequestRulesRegistry(
54 scoped_refptr<InfoMap> extension_info_map)
55 : WebRequestRulesRegistry(NULL /*profile*/,
56 NULL /* cache_delegate */,
58 num_clear_cache_calls_(0) {
59 SetExtensionInfoMapForTesting(extension_info_map);
62 // Returns how often the in-memory caches of the renderers were instructed
64 int num_clear_cache_calls() const { return num_clear_cache_calls_; }
66 // How many rules are there which have some conditions not triggered by URL
68 size_t RulesWithoutTriggers() const {
69 return rules_with_untriggered_conditions_for_test().size();
73 virtual ~TestWebRequestRulesRegistry() {}
75 virtual void ClearCacheOnNavigation() OVERRIDE {
76 ++num_clear_cache_calls_;
80 int num_clear_cache_calls_;
83 class WebRequestRulesRegistryTest : public testing::Test {
85 WebRequestRulesRegistryTest()
86 : ui_(content::BrowserThread::UI, &message_loop_),
87 io_(content::BrowserThread::IO, &message_loop_) {}
89 virtual ~WebRequestRulesRegistryTest() {}
91 virtual void SetUp() OVERRIDE;
93 virtual void TearDown() OVERRIDE {
94 // Make sure that deletion traits of all registries are executed.
95 message_loop_.RunUntilIdle();
98 // Returns a rule that roughly matches http://*.example.com and
99 // https://www.example.com and cancels it
100 linked_ptr<RulesRegistry::Rule> CreateRule1() {
101 base::ListValue* scheme_http = new base::ListValue();
102 scheme_http->Append(new base::StringValue("http"));
103 base::DictionaryValue* http_condition_dict = new base::DictionaryValue();
104 http_condition_dict->Set(keys2::kSchemesKey, scheme_http);
105 http_condition_dict->SetString(keys2::kHostSuffixKey, "example.com");
106 base::DictionaryValue http_condition_url_filter;
107 http_condition_url_filter.Set(keys::kUrlKey, http_condition_dict);
108 http_condition_url_filter.SetString(keys::kInstanceTypeKey,
109 keys::kRequestMatcherType);
111 base::ListValue* scheme_https = new base::ListValue();
112 scheme_http->Append(new base::StringValue("https"));
113 base::DictionaryValue* https_condition_dict = new base::DictionaryValue();
114 https_condition_dict->Set(keys2::kSchemesKey, scheme_https);
115 https_condition_dict->SetString(keys2::kHostSuffixKey, "example.com");
116 https_condition_dict->SetString(keys2::kHostPrefixKey, "www");
117 base::DictionaryValue https_condition_url_filter;
118 https_condition_url_filter.Set(keys::kUrlKey, https_condition_dict);
119 https_condition_url_filter.SetString(keys::kInstanceTypeKey,
120 keys::kRequestMatcherType);
122 base::DictionaryValue action_dict;
123 action_dict.SetString(keys::kInstanceTypeKey, keys::kCancelRequestType);
125 linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
126 rule->id.reset(new std::string(kRuleId1));
127 rule->priority.reset(new int(100));
128 rule->actions.push_back(linked_ptr<base::Value>(action_dict.DeepCopy()));
129 rule->conditions.push_back(
130 linked_ptr<base::Value>(http_condition_url_filter.DeepCopy()));
131 rule->conditions.push_back(
132 linked_ptr<base::Value>(https_condition_url_filter.DeepCopy()));
136 // Returns a rule that matches anything and cancels it.
137 linked_ptr<RulesRegistry::Rule> CreateRule2() {
138 base::DictionaryValue condition_dict;
139 condition_dict.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType);
141 base::DictionaryValue action_dict;
142 action_dict.SetString(keys::kInstanceTypeKey, keys::kCancelRequestType);
144 linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
145 rule->id.reset(new std::string(kRuleId2));
146 rule->priority.reset(new int(100));
147 rule->actions.push_back(linked_ptr<base::Value>(action_dict.DeepCopy()));
148 rule->conditions.push_back(
149 linked_ptr<base::Value>(condition_dict.DeepCopy()));
153 linked_ptr<RulesRegistry::Rule> CreateRedirectRule(
154 const std::string& destination) {
155 base::DictionaryValue condition_dict;
156 condition_dict.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType);
158 base::DictionaryValue action_dict;
159 action_dict.SetString(keys::kInstanceTypeKey, keys::kRedirectRequestType);
160 action_dict.SetString(keys::kRedirectUrlKey, destination);
162 linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
163 rule->id.reset(new std::string(kRuleId3));
164 rule->priority.reset(new int(100));
165 rule->actions.push_back(linked_ptr<base::Value>(action_dict.DeepCopy()));
166 rule->conditions.push_back(
167 linked_ptr<base::Value>(condition_dict.DeepCopy()));
171 // Create a rule to ignore all other rules for a destination that
172 // contains index.html.
173 linked_ptr<RulesRegistry::Rule> CreateIgnoreRule() {
174 base::DictionaryValue condition_dict;
175 base::DictionaryValue* http_condition_dict = new base::DictionaryValue();
176 http_condition_dict->SetString(keys2::kPathContainsKey, "index.html");
177 condition_dict.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType);
178 condition_dict.Set(keys::kUrlKey, http_condition_dict);
180 base::DictionaryValue action_dict;
181 action_dict.SetString(keys::kInstanceTypeKey, keys::kIgnoreRulesType);
182 action_dict.SetInteger(keys::kLowerPriorityThanKey, 150);
184 linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
185 rule->id.reset(new std::string(kRuleId4));
186 rule->priority.reset(new int(200));
187 rule->actions.push_back(linked_ptr<base::Value>(action_dict.DeepCopy()));
188 rule->conditions.push_back(
189 linked_ptr<base::Value>(condition_dict.DeepCopy()));
193 // Create a condition with the attributes specified. An example value of
194 // |attributes| is: "\"resourceType\": [\"stylesheet\"], \n".
195 linked_ptr<base::Value> CreateCondition(const std::string& attributes) {
196 std::string json_description =
198 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n";
199 json_description += attributes;
200 json_description += "}";
202 return linked_ptr<base::Value>(
203 base::test::ParseJson(json_description).release());
206 // Create a rule with the ID |rule_id| and with conditions created from the
207 // |attributes| specified (one entry one condition). An example value of a
208 // string from |attributes| is: "\"resourceType\": [\"stylesheet\"], \n".
209 linked_ptr<RulesRegistry::Rule> CreateCancellingRule(
211 const std::vector<const std::string*>& attributes) {
212 base::DictionaryValue action_dict;
213 action_dict.SetString(keys::kInstanceTypeKey, keys::kCancelRequestType);
215 linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
216 rule->id.reset(new std::string(rule_id));
217 rule->priority.reset(new int(1));
218 rule->actions.push_back(linked_ptr<base::Value>(action_dict.DeepCopy()));
219 for (std::vector<const std::string*>::const_iterator it =
221 it != attributes.end(); ++it)
222 rule->conditions.push_back(CreateCondition(**it));
227 base::MessageLoopForIO message_loop_;
228 content::TestBrowserThread ui_;
229 content::TestBrowserThread io_;
230 // Two extensions with host permissions for all URLs and the DWR permission.
231 // Installation times will be so that |extension_| is older than
233 scoped_refptr<Extension> extension_;
234 scoped_refptr<Extension> extension2_;
235 scoped_refptr<InfoMap> extension_info_map_;
238 void WebRequestRulesRegistryTest::SetUp() {
239 testing::Test::SetUp();
242 extension_ = LoadManifestUnchecked("permissions",
243 "web_request_all_host_permissions.json",
244 Manifest::INVALID_LOCATION,
248 ASSERT_TRUE(extension_.get()) << error;
249 extension2_ = LoadManifestUnchecked("permissions",
250 "web_request_all_host_permissions.json",
251 Manifest::INVALID_LOCATION,
255 ASSERT_TRUE(extension2_.get()) << error;
256 extension_info_map_ = new InfoMap;
257 ASSERT_TRUE(extension_info_map_.get());
258 extension_info_map_->AddExtension(extension_.get(),
259 base::Time() + base::TimeDelta::FromDays(1),
260 false /*incognito_enabled*/,
261 false /*notifications_disabled*/);
262 extension_info_map_->AddExtension(extension2_.get(),
263 base::Time() + base::TimeDelta::FromDays(2),
264 false /*incognito_enabled*/,
265 false /*notifications_disabled*/);
269 TEST_F(WebRequestRulesRegistryTest, AddRulesImpl) {
270 scoped_refptr<TestWebRequestRulesRegistry> registry(
271 new TestWebRequestRulesRegistry(extension_info_map_));
274 std::vector<linked_ptr<RulesRegistry::Rule> > rules;
275 rules.push_back(CreateRule1());
276 rules.push_back(CreateRule2());
278 error = registry->AddRules(kExtensionId, rules);
279 EXPECT_EQ("", error);
280 EXPECT_EQ(1, registry->num_clear_cache_calls());
282 std::set<const WebRequestRule*> matches;
284 GURL http_url("http://www.example.com");
285 net::TestURLRequestContext context;
286 scoped_ptr<net::URLRequest> http_request(context.CreateRequest(
287 http_url, net::DEFAULT_PRIORITY, NULL, NULL));
288 WebRequestData request_data(http_request.get(), ON_BEFORE_REQUEST);
289 matches = registry->GetMatches(request_data);
290 EXPECT_EQ(2u, matches.size());
292 std::set<WebRequestRule::GlobalRuleId> matches_ids;
293 for (std::set<const WebRequestRule*>::const_iterator it = matches.begin();
294 it != matches.end(); ++it)
295 matches_ids.insert((*it)->id());
296 EXPECT_TRUE(ContainsKey(matches_ids, std::make_pair(kExtensionId, kRuleId1)));
297 EXPECT_TRUE(ContainsKey(matches_ids, std::make_pair(kExtensionId, kRuleId2)));
299 GURL foobar_url("http://www.foobar.com");
300 scoped_ptr<net::URLRequest> foobar_request(context.CreateRequest(
301 foobar_url, net::DEFAULT_PRIORITY, NULL, NULL));
302 request_data.request = foobar_request.get();
303 matches = registry->GetMatches(request_data);
304 EXPECT_EQ(1u, matches.size());
305 WebRequestRule::GlobalRuleId expected_pair =
306 std::make_pair(kExtensionId, kRuleId2);
307 EXPECT_EQ(expected_pair, (*matches.begin())->id());
310 TEST_F(WebRequestRulesRegistryTest, RemoveRulesImpl) {
311 scoped_refptr<TestWebRequestRulesRegistry> registry(
312 new TestWebRequestRulesRegistry(extension_info_map_));
315 // Setup RulesRegistry to contain two rules.
316 std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add;
317 rules_to_add.push_back(CreateRule1());
318 rules_to_add.push_back(CreateRule2());
319 error = registry->AddRules(kExtensionId, rules_to_add);
320 EXPECT_EQ("", error);
321 EXPECT_EQ(1, registry->num_clear_cache_calls());
323 // Verify initial state.
324 std::vector<linked_ptr<RulesRegistry::Rule> > registered_rules;
325 registry->GetAllRules(kExtensionId, ®istered_rules);
326 EXPECT_EQ(2u, registered_rules.size());
327 EXPECT_EQ(1u, registry->RulesWithoutTriggers());
329 // Remove first rule.
330 std::vector<std::string> rules_to_remove;
331 rules_to_remove.push_back(kRuleId1);
332 error = registry->RemoveRules(kExtensionId, rules_to_remove);
333 EXPECT_EQ("", error);
334 EXPECT_EQ(2, registry->num_clear_cache_calls());
336 // Verify that only one rule is left.
337 registered_rules.clear();
338 registry->GetAllRules(kExtensionId, ®istered_rules);
339 EXPECT_EQ(1u, registered_rules.size());
340 EXPECT_EQ(1u, registry->RulesWithoutTriggers());
342 // Now rules_to_remove contains both rules, i.e. one that does not exist in
343 // the rules registry anymore. Effectively we only remove the second rule.
344 rules_to_remove.push_back(kRuleId2);
345 error = registry->RemoveRules(kExtensionId, rules_to_remove);
346 EXPECT_EQ("", error);
347 EXPECT_EQ(3, registry->num_clear_cache_calls());
349 // Verify that everything is gone.
350 registered_rules.clear();
351 registry->GetAllRules(kExtensionId, ®istered_rules);
352 EXPECT_EQ(0u, registered_rules.size());
353 EXPECT_EQ(0u, registry->RulesWithoutTriggers());
355 EXPECT_TRUE(registry->IsEmpty());
358 TEST_F(WebRequestRulesRegistryTest, RemoveAllRulesImpl) {
359 scoped_refptr<TestWebRequestRulesRegistry> registry(
360 new TestWebRequestRulesRegistry(extension_info_map_));
363 // Setup RulesRegistry to contain two rules, one for each extension.
364 std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add(1);
365 rules_to_add[0] = CreateRule1();
366 error = registry->AddRules(kExtensionId, rules_to_add);
367 EXPECT_EQ("", error);
368 EXPECT_EQ(1, registry->num_clear_cache_calls());
370 rules_to_add[0] = CreateRule2();
371 error = registry->AddRules(kExtensionId2, rules_to_add);
372 EXPECT_EQ("", error);
373 EXPECT_EQ(2, registry->num_clear_cache_calls());
375 // Verify initial state.
376 std::vector<linked_ptr<RulesRegistry::Rule> > registered_rules;
377 registry->GetAllRules(kExtensionId, ®istered_rules);
378 EXPECT_EQ(1u, registered_rules.size());
379 registered_rules.clear();
380 registry->GetAllRules(kExtensionId2, ®istered_rules);
381 EXPECT_EQ(1u, registered_rules.size());
383 // Remove rule of first extension.
384 error = registry->RemoveAllRules(kExtensionId);
385 EXPECT_EQ("", error);
386 EXPECT_EQ(3, registry->num_clear_cache_calls());
388 // Verify that only the first rule is deleted.
389 registered_rules.clear();
390 registry->GetAllRules(kExtensionId, ®istered_rules);
391 EXPECT_EQ(0u, registered_rules.size());
392 registered_rules.clear();
393 registry->GetAllRules(kExtensionId2, ®istered_rules);
394 EXPECT_EQ(1u, registered_rules.size());
396 // Test removing rules if none exist.
397 error = registry->RemoveAllRules(kExtensionId);
398 EXPECT_EQ("", error);
399 EXPECT_EQ(4, registry->num_clear_cache_calls());
401 // Remove rule from second extension.
402 error = registry->RemoveAllRules(kExtensionId2);
403 EXPECT_EQ("", error);
404 EXPECT_EQ(5, registry->num_clear_cache_calls());
406 EXPECT_TRUE(registry->IsEmpty());
409 // Test precedences between extensions.
410 TEST_F(WebRequestRulesRegistryTest, Precedences) {
411 scoped_refptr<WebRequestRulesRegistry> registry(
412 new TestWebRequestRulesRegistry(extension_info_map_));
415 std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add_1(1);
416 rules_to_add_1[0] = CreateRedirectRule("http://www.foo.com");
417 error = registry->AddRules(kExtensionId, rules_to_add_1);
418 EXPECT_EQ("", error);
420 std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add_2(1);
421 rules_to_add_2[0] = CreateRedirectRule("http://www.bar.com");
422 error = registry->AddRules(kExtensionId2, rules_to_add_2);
423 EXPECT_EQ("", error);
425 GURL url("http://www.google.com");
426 net::TestURLRequestContext context;
427 scoped_ptr<net::URLRequest> request(context.CreateRequest(
428 url, net::DEFAULT_PRIORITY, NULL, NULL));
429 WebRequestData request_data(request.get(), ON_BEFORE_REQUEST);
430 std::list<LinkedPtrEventResponseDelta> deltas =
431 registry->CreateDeltas(NULL, request_data, false);
433 // The second extension is installed later and will win for this reason
434 // in conflict resolution.
435 ASSERT_EQ(2u, deltas.size());
436 deltas.sort(&helpers::InDecreasingExtensionInstallationTimeOrder);
438 std::list<LinkedPtrEventResponseDelta>::iterator i = deltas.begin();
439 LinkedPtrEventResponseDelta winner = *i++;
440 LinkedPtrEventResponseDelta loser = *i;
442 EXPECT_EQ(kExtensionId2, winner->extension_id);
443 EXPECT_EQ(base::Time() + base::TimeDelta::FromDays(2),
444 winner->extension_install_time);
445 EXPECT_EQ(GURL("http://www.bar.com"), winner->new_url);
447 EXPECT_EQ(kExtensionId, loser->extension_id);
448 EXPECT_EQ(base::Time() + base::TimeDelta::FromDays(1),
449 loser->extension_install_time);
450 EXPECT_EQ(GURL("http://www.foo.com"), loser->new_url);
453 // Test priorities of rules within one extension.
454 TEST_F(WebRequestRulesRegistryTest, Priorities) {
455 scoped_refptr<WebRequestRulesRegistry> registry(
456 new TestWebRequestRulesRegistry(extension_info_map_));
459 std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add_1(1);
460 rules_to_add_1[0] = CreateRedirectRule("http://www.foo.com");
461 error = registry->AddRules(kExtensionId, rules_to_add_1);
462 EXPECT_EQ("", error);
464 std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add_2(1);
465 rules_to_add_2[0] = CreateRedirectRule("http://www.bar.com");
466 error = registry->AddRules(kExtensionId2, rules_to_add_2);
467 EXPECT_EQ("", error);
469 std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add_3(1);
470 rules_to_add_3[0] = CreateIgnoreRule();
471 error = registry->AddRules(kExtensionId, rules_to_add_3);
472 EXPECT_EQ("", error);
474 GURL url("http://www.google.com/index.html");
475 net::TestURLRequestContext context;
476 scoped_ptr<net::URLRequest> request(context.CreateRequest(
477 url, net::DEFAULT_PRIORITY, NULL, NULL));
478 WebRequestData request_data(request.get(), ON_BEFORE_REQUEST);
479 std::list<LinkedPtrEventResponseDelta> deltas =
480 registry->CreateDeltas(NULL, request_data, false);
482 // The redirect by the first extension is ignored due to the ignore rule.
483 ASSERT_EQ(1u, deltas.size());
484 LinkedPtrEventResponseDelta effective_rule = *(deltas.begin());
486 EXPECT_EQ(kExtensionId2, effective_rule->extension_id);
487 EXPECT_EQ(base::Time() + base::TimeDelta::FromDays(2),
488 effective_rule->extension_install_time);
489 EXPECT_EQ(GURL("http://www.bar.com"), effective_rule->new_url);
492 // Test ignoring of rules by tag.
493 TEST_F(WebRequestRulesRegistryTest, IgnoreRulesByTag) {
494 const char kRule1[] =
496 " \"id\": \"rule1\", \n"
497 " \"tags\": [\"non_matching_tag\", \"ignore_tag\"], \n"
498 " \"conditions\": [ \n"
500 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
501 " \"url\": {\"hostSuffix\": \"foo.com\"} \n"
506 " \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
507 " \"redirectUrl\": \"http://bar.com\" \n"
510 " \"priority\": 200 \n"
513 const char kRule2[] =
515 " \"id\": \"rule2\", \n"
516 " \"conditions\": [ \n"
518 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
519 " \"url\": {\"pathPrefix\": \"/test\"} \n"
524 " \"instanceType\": \"declarativeWebRequest.IgnoreRules\", \n"
525 " \"hasTag\": \"ignore_tag\" \n"
528 " \"priority\": 300 \n"
531 scoped_ptr<base::Value> value1(base::JSONReader::Read(kRule1));
532 ASSERT_TRUE(value1.get());
533 scoped_ptr<base::Value> value2(base::JSONReader::Read(kRule2));
534 ASSERT_TRUE(value2.get());
536 std::vector<linked_ptr<RulesRegistry::Rule> > rules;
537 rules.push_back(make_linked_ptr(new RulesRegistry::Rule));
538 rules.push_back(make_linked_ptr(new RulesRegistry::Rule));
539 ASSERT_TRUE(RulesRegistry::Rule::Populate(*value1, rules[0].get()));
540 ASSERT_TRUE(RulesRegistry::Rule::Populate(*value2, rules[1].get()));
542 scoped_refptr<WebRequestRulesRegistry> registry(
543 new TestWebRequestRulesRegistry(extension_info_map_));
544 std::string error = registry->AddRulesImpl(kExtensionId, rules);
545 EXPECT_EQ("", error);
546 EXPECT_FALSE(registry->IsEmpty());
548 GURL url("http://www.foo.com/test");
549 net::TestURLRequestContext context;
550 scoped_ptr<net::URLRequest> request(context.CreateRequest(
551 url, net::DEFAULT_PRIORITY, NULL, NULL));
552 WebRequestData request_data(request.get(), ON_BEFORE_REQUEST);
553 std::list<LinkedPtrEventResponseDelta> deltas =
554 registry->CreateDeltas(NULL, request_data, false);
556 // The redirect by the redirect rule is ignored due to the ignore rule.
557 std::set<const WebRequestRule*> matches = registry->GetMatches(request_data);
558 EXPECT_EQ(2u, matches.size());
559 ASSERT_EQ(0u, deltas.size());
562 // Test that rules failing IsFulfilled on their conditions are never returned by
564 TEST_F(WebRequestRulesRegistryTest, GetMatchesCheckFulfilled) {
565 scoped_refptr<TestWebRequestRulesRegistry> registry(
566 new TestWebRequestRulesRegistry(extension_info_map_));
567 const std::string kMatchingUrlAttribute(
568 "\"url\": { \"pathContains\": \"\" }, \n");
569 const std::string kNonMatchingNonUrlAttribute(
570 "\"resourceType\": [\"stylesheet\"], \n");
571 const std::string kBothAttributes(kMatchingUrlAttribute +
572 kNonMatchingNonUrlAttribute);
574 std::vector<const std::string*> attributes;
575 std::vector<linked_ptr<RulesRegistry::Rule> > rules;
577 // Rules 1 and 2 have one condition, neither of them should fire.
578 attributes.push_back(&kNonMatchingNonUrlAttribute);
579 rules.push_back(CreateCancellingRule(kRuleId1, attributes));
582 attributes.push_back(&kBothAttributes);
583 rules.push_back(CreateCancellingRule(kRuleId2, attributes));
585 // Rule 3 has two conditions, one with a matching URL attribute, and one
586 // with a non-matching non-URL attribute.
588 attributes.push_back(&kMatchingUrlAttribute);
589 attributes.push_back(&kNonMatchingNonUrlAttribute);
590 rules.push_back(CreateCancellingRule(kRuleId3, attributes));
592 error = registry->AddRules(kExtensionId, rules);
593 EXPECT_EQ("", error);
594 EXPECT_EQ(1, registry->num_clear_cache_calls());
596 std::set<const WebRequestRule*> matches;
598 GURL http_url("http://www.example.com");
599 net::TestURLRequestContext context;
600 scoped_ptr<net::URLRequest> http_request(context.CreateRequest(
601 http_url, net::DEFAULT_PRIORITY, NULL, NULL));
602 WebRequestData request_data(http_request.get(), ON_BEFORE_REQUEST);
603 matches = registry->GetMatches(request_data);
604 EXPECT_EQ(1u, matches.size());
605 WebRequestRule::GlobalRuleId expected_pair = std::make_pair(kExtensionId,
607 EXPECT_EQ(expected_pair, (*matches.begin())->id());
610 // Test that the url and firstPartyForCookiesUrl attributes are evaluated
611 // against corresponding URLs. Tested on requests where these URLs actually
613 TEST_F(WebRequestRulesRegistryTest, GetMatchesDifferentUrls) {
614 scoped_refptr<TestWebRequestRulesRegistry> registry(
615 new TestWebRequestRulesRegistry(extension_info_map_));
616 const std::string kUrlAttribute(
617 "\"url\": { \"hostContains\": \"url\" }, \n");
618 const std::string kFirstPartyUrlAttribute(
619 "\"firstPartyForCookiesUrl\": { \"hostContains\": \"fpfc\" }, \n");
621 std::vector<const std::string*> attributes;
622 std::vector<linked_ptr<RulesRegistry::Rule> > rules;
624 // Rule 1 has one condition, with a url attribute
625 attributes.push_back(&kUrlAttribute);
626 rules.push_back(CreateCancellingRule(kRuleId1, attributes));
628 // Rule 2 has one condition, with a firstPartyForCookiesUrl attribute
630 attributes.push_back(&kFirstPartyUrlAttribute);
631 rules.push_back(CreateCancellingRule(kRuleId2, attributes));
633 error = registry->AddRules(kExtensionId, rules);
634 EXPECT_EQ("", error);
635 EXPECT_EQ(1, registry->num_clear_cache_calls());
637 std::set<const WebRequestRule*> matches;
639 const GURL urls[] = {
640 GURL("http://url.example.com"), // matching
641 GURL("http://www.example.com") // non-matching
643 const GURL firstPartyUrls[] = {
644 GURL("http://www.example.com"), // non-matching
645 GURL("http://fpfc.example.com") // matching
647 // Which rules should match in subsequent test iterations.
648 const char* matchingRuleIds[] = { kRuleId1, kRuleId2 };
649 COMPILE_ASSERT(arraysize(urls) == arraysize(firstPartyUrls),
650 urls_and_firstPartyUrls_need_to_have_the_same_size);
651 COMPILE_ASSERT(arraysize(urls) == arraysize(matchingRuleIds),
652 urls_and_matchingRuleIds_need_to_have_the_same_size);
653 net::TestURLRequestContext context;
655 for (size_t i = 0; i < arraysize(matchingRuleIds); ++i) {
656 // Construct the inputs.
657 scoped_ptr<net::URLRequest> http_request(context.CreateRequest(
658 urls[i], net::DEFAULT_PRIORITY, NULL, NULL));
659 WebRequestData request_data(http_request.get(), ON_BEFORE_REQUEST);
660 http_request->set_first_party_for_cookies(firstPartyUrls[i]);
661 // Now run both rules on the input.
662 matches = registry->GetMatches(request_data);
663 SCOPED_TRACE(testing::Message("i = ") << i << ", rule id = "
664 << matchingRuleIds[i]);
665 // Make sure that the right rule succeeded.
666 EXPECT_EQ(1u, matches.size());
667 EXPECT_EQ(WebRequestRule::GlobalRuleId(std::make_pair(kExtensionId,
668 matchingRuleIds[i])),
669 (*matches.begin())->id());
673 TEST(WebRequestRulesRegistrySimpleTest, StageChecker) {
674 // The contentType condition can only be evaluated during ON_HEADERS_RECEIVED
675 // but the SetRequestHeader action can only be executed during
676 // ON_BEFORE_SEND_HEADERS.
677 // Therefore, this is an inconsistent rule that needs to be flagged.
680 " \"id\": \"rule1\", \n"
681 " \"conditions\": [ \n"
683 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
684 " \"url\": {\"hostSuffix\": \"foo.com\"}, \n"
685 " \"contentType\": [\"image/jpeg\"] \n"
690 " \"instanceType\": \"declarativeWebRequest.SetRequestHeader\",\n"
691 " \"name\": \"Content-Type\", \n"
692 " \"value\": \"text/plain\" \n"
695 " \"priority\": 200 \n"
698 scoped_ptr<base::Value> value(base::JSONReader::Read(kRule));
701 RulesRegistry::Rule rule;
702 ASSERT_TRUE(RulesRegistry::Rule::Populate(*value, &rule));
706 scoped_ptr<WebRequestConditionSet> conditions =
707 WebRequestConditionSet::Create(
708 NULL, matcher.condition_factory(), rule.conditions, &error);
709 ASSERT_TRUE(error.empty()) << error;
710 ASSERT_TRUE(conditions);
712 bool bad_message = false;
713 scoped_ptr<WebRequestActionSet> actions =
714 WebRequestActionSet::Create(
715 NULL, NULL, rule.actions, &error, &bad_message);
716 ASSERT_TRUE(error.empty()) << error;
717 ASSERT_FALSE(bad_message);
718 ASSERT_TRUE(actions);
720 EXPECT_FALSE(WebRequestRulesRegistry::StageChecker(
721 conditions.get(), actions.get(), &error));
722 EXPECT_THAT(error, HasSubstr("no time in the request life-cycle"));
723 EXPECT_THAT(error, HasSubstr(actions->actions().back()->GetName()));
726 TEST(WebRequestRulesRegistrySimpleTest, HostPermissionsChecker) {
727 const char kAction[] = // This action requires all URLs host permission.
729 " \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
730 " \"redirectUrl\": \"http://bar.com\" \n"
732 scoped_ptr<base::Value> action_value(base::JSONReader::Read(kAction));
733 ASSERT_TRUE(action_value);
735 WebRequestActionSet::AnyVector actions;
736 actions.push_back(linked_ptr<base::Value>(action_value.release()));
737 ASSERT_TRUE(actions.back().get());
740 bool bad_message = false;
741 scoped_ptr<WebRequestActionSet> action_set(
742 WebRequestActionSet::Create(NULL, NULL, actions, &error, &bad_message));
743 ASSERT_TRUE(error.empty()) << error;
744 ASSERT_FALSE(bad_message);
745 ASSERT_TRUE(action_set);
747 scoped_refptr<Extension> extension_no_url(
748 LoadManifest("permissions", "web_request_no_host.json"));
749 scoped_refptr<Extension> extension_some_urls(
750 LoadManifest("permissions", "web_request_com_host_permissions.json"));
751 scoped_refptr<Extension> extension_all_urls(
752 LoadManifest("permissions", "web_request_all_host_permissions.json"));
754 EXPECT_TRUE(WebRequestRulesRegistry::HostPermissionsChecker(
755 extension_all_urls.get(), action_set.get(), &error));
756 EXPECT_TRUE(error.empty()) << error;
758 EXPECT_FALSE(WebRequestRulesRegistry::HostPermissionsChecker(
759 extension_some_urls.get(), action_set.get(), &error));
760 EXPECT_THAT(error, HasSubstr("permission for all"));
761 EXPECT_THAT(error, HasSubstr(action_set->actions().back()->GetName()));
763 EXPECT_FALSE(WebRequestRulesRegistry::HostPermissionsChecker(
764 extension_no_url.get(), action_set.get(), &error));
765 EXPECT_THAT(error, HasSubstr("permission for all"));
766 EXPECT_THAT(error, HasSubstr(action_set->actions().back()->GetName()));
769 TEST_F(WebRequestRulesRegistryTest, CheckOriginAndPathRegEx) {
772 " \"id\": \"rule1\", \n"
773 " \"conditions\": [ \n"
775 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
776 " \"url\": {\"originAndPathMatches\": \"fo+.com\"} \n"
781 " \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
782 " \"redirectUrl\": \"http://bar.com\" \n"
785 " \"priority\": 200 \n"
788 scoped_ptr<base::Value> value(base::JSONReader::Read(kRule));
789 ASSERT_TRUE(value.get());
791 std::vector<linked_ptr<RulesRegistry::Rule> > rules;
792 rules.push_back(make_linked_ptr(new RulesRegistry::Rule));
793 ASSERT_TRUE(RulesRegistry::Rule::Populate(*value, rules.back().get()));
795 scoped_refptr<WebRequestRulesRegistry> registry(
796 new TestWebRequestRulesRegistry(extension_info_map_));
799 std::string error = registry->AddRulesImpl(kExtensionId, rules);
800 EXPECT_EQ("", error);
802 net::TestURLRequestContext context;
803 std::list<LinkedPtrEventResponseDelta> deltas;
805 // No match because match is in the query parameter.
806 GURL url1("http://bar.com/index.html?foo.com");
807 scoped_ptr<net::URLRequest> request1(context.CreateRequest(
808 url1, net::DEFAULT_PRIORITY, NULL, NULL));
809 WebRequestData request_data1(request1.get(), ON_BEFORE_REQUEST);
810 deltas = registry->CreateDeltas(NULL, request_data1, false);
811 EXPECT_EQ(0u, deltas.size());
813 // This is a correct match.
814 GURL url2("http://foo.com/index.html");
815 scoped_ptr<net::URLRequest> request2(context.CreateRequest(
816 url2, net::DEFAULT_PRIORITY, NULL, NULL));
817 WebRequestData request_data2(request2.get(), ON_BEFORE_REQUEST);
818 deltas = registry->CreateDeltas(NULL, request_data2, false);
819 EXPECT_EQ(1u, deltas.size());
822 } // namespace extensions