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