// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h" #include #include "base/message_loop.h" #include "base/values.h" #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h" #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rule.h" #include "chrome/common/extensions/matcher/url_matcher_constants.h" #include "content/public/browser/resource_request_info.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace extensions { namespace keys = declarative_webrequest_constants; namespace keys2 = url_matcher_constants; TEST(WebRequestConditionTest, CreateCondition) { // Necessary for TestURLRequest. MessageLoop message_loop(MessageLoop::TYPE_IO); URLMatcher matcher; std::string error; scoped_ptr result; DictionaryValue invalid_condition; invalid_condition.SetString("invalid", "foobar"); invalid_condition.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType); DictionaryValue invalid_condition2; invalid_condition2.Set(keys::kUrlKey, new ListValue); invalid_condition2.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType); ListValue* resource_type_list = new ListValue(); resource_type_list->Append(Value::CreateStringValue("main_frame")); DictionaryValue* url_filter = new DictionaryValue(); url_filter->SetString(keys2::kHostSuffixKey, "example.com"); DictionaryValue valid_condition; valid_condition.Set(keys::kResourceTypeKey, resource_type_list); valid_condition.Set(keys::kUrlKey, url_filter); valid_condition.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType); // Test wrong condition name passed. error.clear(); result = WebRequestCondition::Create(matcher.condition_factory(), invalid_condition, &error); EXPECT_FALSE(error.empty()); EXPECT_FALSE(result.get()); // Test wrong datatype in host_suffix. error.clear(); result = WebRequestCondition::Create(matcher.condition_factory(), invalid_condition2, &error); EXPECT_FALSE(error.empty()); EXPECT_FALSE(result.get()); // Test success (can we support multiple criteria?) error.clear(); result = WebRequestCondition::Create(matcher.condition_factory(), valid_condition, &error); EXPECT_EQ("", error); ASSERT_TRUE(result.get()); net::TestURLRequestContext context; net::TestURLRequest match_request( GURL("http://www.example.com"), NULL, &context); content::ResourceRequestInfo::AllocateForTesting(&match_request, ResourceType::MAIN_FRAME, NULL, -1, -1); EXPECT_TRUE(result->IsFulfilled( WebRequestRule::RequestData(&match_request, ON_BEFORE_REQUEST))); net::TestURLRequest wrong_resource_type( GURL("https://www.example.com"), NULL, &context); content::ResourceRequestInfo::AllocateForTesting(&wrong_resource_type, ResourceType::SUB_FRAME, NULL, -1, -1); EXPECT_FALSE(result->IsFulfilled( WebRequestRule::RequestData(&wrong_resource_type, ON_BEFORE_REQUEST))); } TEST(WebRequestConditionTest, CreateConditionSet) { // Necessary for TestURLRequest. MessageLoop message_loop(MessageLoop::TYPE_IO); URLMatcher matcher; ListValue* http_scheme_list = new ListValue(); http_scheme_list->Append(Value::CreateStringValue("http")); DictionaryValue* http_url_filter = new DictionaryValue(); http_url_filter->SetString(keys2::kHostSuffixKey, "example.com"); http_url_filter->Set(keys2::kSchemesKey, http_scheme_list); DictionaryValue http_condition; http_condition.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType); http_condition.Set(keys::kUrlKey, http_url_filter); ListValue* https_scheme_list = new ListValue(); https_scheme_list->Append(Value::CreateStringValue("https")); DictionaryValue* https_url_filter = new DictionaryValue(); https_url_filter->SetString(keys2::kHostSuffixKey, "example.com"); https_url_filter->SetString(keys2::kHostPrefixKey, "www"); https_url_filter->Set(keys2::kSchemesKey, https_scheme_list); DictionaryValue https_condition; https_condition.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType); https_condition.Set(keys::kUrlKey, https_url_filter); WebRequestConditionSet::AnyVector conditions; linked_ptr condition1 = make_linked_ptr( new json_schema_compiler::any::Any); condition1->Init(http_condition); conditions.push_back(condition1); linked_ptr condition2 = make_linked_ptr( new json_schema_compiler::any::Any); condition2->Init(https_condition); conditions.push_back(condition2); // Test insertion std::string error; scoped_ptr result = WebRequestConditionSet::Create(matcher.condition_factory(), conditions, &error); EXPECT_EQ("", error); ASSERT_TRUE(result.get()); EXPECT_EQ(2u, result->conditions().size()); // Tell the URLMatcher about our shiny new patterns. URLMatcherConditionSet::Vector url_matcher_condition_set; result->GetURLMatcherConditionSets(&url_matcher_condition_set); matcher.AddConditionSets(url_matcher_condition_set); std::set url_match_ids; int number_matches = 0; // Test that the result is correct and matches http://www.example.com and // https://www.example.com GURL http_url("http://www.example.com"); net::TestURLRequestContext context; net::TestURLRequest http_request(http_url, NULL, &context); url_match_ids = matcher.MatchURL(http_url); for (std::set::iterator i = url_match_ids.begin(); i != url_match_ids.end(); ++i) { if (result->IsFulfilled( *i, WebRequestRule::RequestData(&http_request, ON_BEFORE_REQUEST))) ++number_matches; } EXPECT_EQ(1, number_matches); GURL https_url("https://www.example.com"); url_match_ids = matcher.MatchURL(https_url); net::TestURLRequest https_request(https_url, NULL, &context); number_matches = 0; for (std::set::iterator i = url_match_ids.begin(); i != url_match_ids.end(); ++i) { if (result->IsFulfilled( *i, WebRequestRule::RequestData(&https_request, ON_BEFORE_REQUEST))) ++number_matches; } EXPECT_EQ(1, number_matches); // Check that both, hostPrefix and hostSuffix are evaluated. GURL https_foo_url("https://foo.example.com"); url_match_ids = matcher.MatchURL(https_foo_url); net::TestURLRequest https_foo_request(https_foo_url, NULL, &context); number_matches = 0; for (std::set::iterator i = url_match_ids.begin(); i != url_match_ids.end(); ++i) { if (result->IsFulfilled( *i, WebRequestRule::RequestData( &https_foo_request, ON_BEFORE_REQUEST))) ++number_matches; } EXPECT_EQ(0, number_matches); } TEST(WebRequestConditionTest, TestPortFilter) { // Necessary for TestURLRequest. MessageLoop message_loop(MessageLoop::TYPE_IO); URLMatcher matcher; // Allow 80;1000-1010. ListValue* port_range = new ListValue(); port_range->Append(Value::CreateIntegerValue(1000)); port_range->Append(Value::CreateIntegerValue(1010)); ListValue* port_ranges = new ListValue(); port_ranges->Append(Value::CreateIntegerValue(80)); port_ranges->Append(port_range); DictionaryValue* url_filter = new DictionaryValue(); url_filter->Set(keys2::kPortsKey, port_ranges); url_filter->SetString(keys2::kHostSuffixKey, "example.com"); DictionaryValue condition; condition.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType); condition.Set(keys::kUrlKey, url_filter); linked_ptr any_condition = make_linked_ptr(new json_schema_compiler::any::Any); any_condition->Init(condition); WebRequestConditionSet::AnyVector conditions; conditions.push_back(any_condition); // Test insertion std::string error; scoped_ptr result = WebRequestConditionSet::Create(matcher.condition_factory(), conditions, &error); EXPECT_EQ("", error); ASSERT_TRUE(result.get()); EXPECT_EQ(1u, result->conditions().size()); // Tell the URLMatcher about our shiny new patterns. URLMatcherConditionSet::Vector url_matcher_condition_set; result->GetURLMatcherConditionSets(&url_matcher_condition_set); matcher.AddConditionSets(url_matcher_condition_set); std::set url_match_ids; // Test various URLs. GURL http_url("http://www.example.com"); net::TestURLRequestContext context; net::TestURLRequest http_request(http_url, NULL, &context); url_match_ids = matcher.MatchURL(http_url); ASSERT_EQ(1u, url_match_ids.size()); GURL http_url_80("http://www.example.com:80"); net::TestURLRequest http_request_80(http_url_80, NULL, &context); url_match_ids = matcher.MatchURL(http_url_80); ASSERT_EQ(1u, url_match_ids.size()); GURL http_url_1000("http://www.example.com:1000"); net::TestURLRequest http_request_1000(http_url_1000, NULL, &context); url_match_ids = matcher.MatchURL(http_url_1000); ASSERT_EQ(1u, url_match_ids.size()); GURL http_url_2000("http://www.example.com:2000"); net::TestURLRequest http_request_2000(http_url_2000, NULL, &context); url_match_ids = matcher.MatchURL(http_url_2000); ASSERT_EQ(0u, url_match_ids.size()); } // Create a condition with two attributes: one on the request header and one on // the response header. The Create() method should fail and complain that it is // impossible that both conditions are fulfilled at the same time. TEST(WebRequestConditionTest, ConditionsWithConflictingStages) { // Necessary for TestURLRequest. MessageLoop message_loop(MessageLoop::TYPE_IO); URLMatcher matcher; std::string error; scoped_ptr result; DictionaryValue condition_value; condition_value.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType); // Create two JS arrays, each with one empty object... scoped_ptr request_header_filters(new ListValue()); request_header_filters->Append(new DictionaryValue()); scoped_ptr response_header_filters(new ListValue()); response_header_filters->Append(new DictionaryValue()); // ...and pass them as the header filters to the request matcher. condition_value.Set(keys::kRequestHeadersKey, request_header_filters.release()); condition_value.Set(keys::kResponseHeadersKey, response_header_filters.release()); // Test error on incompatible application stages for involved attributes. error.clear(); result = WebRequestCondition::Create(matcher.condition_factory(), condition_value, &error); EXPECT_FALSE(error.empty()); EXPECT_FALSE(result.get()); } } // namespace extensions