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