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