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