OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condit
ion_attribute.h" | 5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condit
ion_attribute.h" |
6 | 6 |
| 7 #include "base/basictypes.h" |
7 #include "base/file_path.h" | 8 #include "base/file_path.h" |
8 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
9 #include "base/values.h" | 10 #include "base/values.h" |
10 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_consta
nts.h" | 11 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_consta
nts.h" |
11 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rule.h
" | 12 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rule.h
" |
12 #include "content/public/browser/resource_request_info.h" | 13 #include "content/public/browser/resource_request_info.h" |
13 #include "net/url_request/url_request_test_util.h" | 14 #include "net/url_request/url_request_test_util.h" |
14 #include "net/test/test_server.h" | 15 #include "net/test/test_server.h" |
15 #include "testing/gtest/include/gtest/gtest.h" | 16 #include "testing/gtest/include/gtest/gtest.h" |
16 | 17 |
| 18 using base::DictionaryValue; |
| 19 using base::ListValue; |
| 20 using base::StringValue; |
| 21 using base::Value; |
| 22 |
17 namespace { | 23 namespace { |
18 const char kUnknownConditionName[] = "unknownType"; | 24 const char kUnknownConditionName[] = "unknownType"; |
19 } // namespace | 25 } // namespace |
20 | 26 |
21 namespace extensions { | 27 namespace extensions { |
22 | 28 |
23 namespace keys = declarative_webrequest_constants; | 29 namespace keys = declarative_webrequest_constants; |
24 | 30 |
25 TEST(WebRequestConditionAttributeTest, CreateConditionAttribute) { | 31 TEST(WebRequestConditionAttributeTest, CreateConditionAttribute) { |
26 // Necessary for TestURLRequest. | 32 // Necessary for TestURLRequest. |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 scoped_ptr<WebRequestConditionAttribute> attribute_unexcluded = | 155 scoped_ptr<WebRequestConditionAttribute> attribute_unexcluded = |
150 WebRequestConditionAttribute::Create( | 156 WebRequestConditionAttribute::Create( |
151 keys::kExcludeContentTypeKey, &content_types, &error); | 157 keys::kExcludeContentTypeKey, &content_types, &error); |
152 EXPECT_EQ("", error); | 158 EXPECT_EQ("", error); |
153 ASSERT_TRUE(attribute_unexcluded.get()); | 159 ASSERT_TRUE(attribute_unexcluded.get()); |
154 EXPECT_TRUE(attribute_unexcluded->IsFulfilled( | 160 EXPECT_TRUE(attribute_unexcluded->IsFulfilled( |
155 WebRequestRule::RequestData(&url_request, ON_HEADERS_RECEIVED, | 161 WebRequestRule::RequestData(&url_request, ON_HEADERS_RECEIVED, |
156 url_request.response_headers()))); | 162 url_request.response_headers()))); |
157 } | 163 } |
158 | 164 |
| 165 namespace { |
| 166 |
| 167 // Builds a vector of vectors of string pointers from an array of strings. |
| 168 // |array| is in fact a sequence of arrays. The array |sizes| captures the sizes |
| 169 // of all parts of |array|, and |size| is the length of |sizes| itself. |
| 170 // Example (this is pseudo-code, not C++): |
| 171 // array = { "a", "b", "c", "d", "e", "f" } |
| 172 // sizes = { 2, 0, 4 } |
| 173 // size = 3 |
| 174 // results in out == { {&"a", &"b"}, {}, {&"c", &"d", &"e", &"f"} } |
| 175 void GetArrayAsVector(const std::string array[], |
| 176 const size_t sizes[], |
| 177 const size_t size, |
| 178 std::vector< std::vector<const std::string*> >* out) { |
| 179 out->clear(); |
| 180 size_t next = 0; |
| 181 for (size_t i = 0; i < size; ++i) { |
| 182 out->push_back(std::vector<const std::string*>()); |
| 183 for (size_t j = next; j < next + sizes[i]; ++j) { |
| 184 out->back().push_back(&(array[j])); |
| 185 } |
| 186 next += sizes[i]; |
| 187 } |
| 188 } |
| 189 |
| 190 // Builds a DictionaryValue from an array of the form {name1, value1, name2, |
| 191 // value2, ...}. Values for the same key are grouped in a ListValue. |
| 192 scoped_ptr<DictionaryValue> GetDictionaryFromArray( |
| 193 const std::vector<const std::string*>& array) { |
| 194 const size_t length = array.size(); |
| 195 CHECK(length % 2 == 0); |
| 196 |
| 197 scoped_ptr<DictionaryValue> dictionary(new DictionaryValue); |
| 198 for (size_t i = 0; i < length; i += 2) { |
| 199 const std::string* name = array[i]; |
| 200 const std::string* value = array[i+1]; |
| 201 if (dictionary->HasKey(*name)) { |
| 202 Value* entry = NULL; |
| 203 ListValue* list = NULL; |
| 204 if (!dictionary->GetWithoutPathExpansion(*name, &entry)) |
| 205 return scoped_ptr<DictionaryValue>(NULL); |
| 206 switch (entry->GetType()) { |
| 207 case Value::TYPE_STRING: // Replace the present string with a list. |
| 208 list = new ListValue; |
| 209 // Ignoring return value, we already verified the entry is there. |
| 210 dictionary->RemoveWithoutPathExpansion(*name, &entry); |
| 211 list->Append(entry); |
| 212 list->Append(Value::CreateStringValue(*value)); |
| 213 dictionary->SetWithoutPathExpansion(*name, list); |
| 214 break; |
| 215 case Value::TYPE_LIST: // Just append to the list. |
| 216 CHECK(entry->GetAsList(&list)); |
| 217 list->Append(Value::CreateStringValue(*value)); |
| 218 break; |
| 219 default: |
| 220 NOTREACHED(); // We never put other Values here. |
| 221 return scoped_ptr<DictionaryValue>(NULL); |
| 222 } |
| 223 } else { |
| 224 dictionary->SetString(*name, *value); |
| 225 } |
| 226 } |
| 227 return dictionary.Pass(); |
| 228 } |
| 229 |
| 230 // Returns whether the response headers from |url_request| satisfy the match |
| 231 // criteria given in |tests|. For at least one |i| all tests from |tests[i]| |
| 232 // must pass. If |positive_test| is true, the dictionary is interpreted as the |
| 233 // containsHeaders property of a RequestMatcher, otherwise as |
| 234 // doesNotContainHeaders. |
| 235 void MatchAndCheck(const std::vector< std::vector<const std::string*> >& tests, |
| 236 const std::string& key, |
| 237 net::URLRequest* url_request, |
| 238 bool* result) { |
| 239 ListValue contains_headers; |
| 240 for (size_t i = 0; i < tests.size(); ++i) { |
| 241 scoped_ptr<DictionaryValue> temp(GetDictionaryFromArray(tests[i])); |
| 242 ASSERT_TRUE(temp.get() != NULL); |
| 243 contains_headers.Append(temp.release()); |
| 244 } |
| 245 |
| 246 std::string error; |
| 247 scoped_ptr<WebRequestConditionAttribute> attribute = |
| 248 WebRequestConditionAttribute::Create(key, &contains_headers, &error); |
| 249 ASSERT_EQ("", error); |
| 250 ASSERT_TRUE(attribute.get() != NULL); |
| 251 |
| 252 *result = attribute->IsFulfilled(WebRequestRule::RequestData( |
| 253 url_request, ON_HEADERS_RECEIVED, url_request->response_headers())); |
| 254 } |
| 255 |
| 256 } // namespace |
| 257 |
| 258 // Here we test WebRequestConditionAttributeResponseHeaders for: |
| 259 // 1. Correct implementation of prefix/suffix/contains/equals matching. |
| 260 // 2. Performing logical disjunction (||) between multiple specifications. |
| 261 // 3. Negating the match in case of 'doesNotContainHeaders'. |
| 262 TEST(WebRequestConditionAttributeTest, Headers) { |
| 263 // Necessary for TestURLRequest. |
| 264 MessageLoop message_loop(MessageLoop::TYPE_IO); |
| 265 |
| 266 net::TestServer test_server( |
| 267 net::TestServer::TYPE_HTTP, |
| 268 net::TestServer::kLocalhost, |
| 269 FilePath(FILE_PATH_LITERAL( |
| 270 "chrome/test/data/extensions/api_test/webrequest/declarative"))); |
| 271 ASSERT_TRUE(test_server.Start()); |
| 272 |
| 273 TestURLRequestContext context; |
| 274 TestDelegate delegate; |
| 275 TestURLRequest url_request(test_server.GetURL("files/headers.html"), |
| 276 &delegate, &context); |
| 277 url_request.Start(); |
| 278 MessageLoop::current()->Run(); |
| 279 |
| 280 // In all the tests below we assume that the server includes the headers |
| 281 // Custom-Header: custom/value |
| 282 // Custom-Header-B: valueA |
| 283 // Custom-Header-B: valueB |
| 284 // Custom-Header-C: valueC, valueD |
| 285 // Custom-Header-D: |
| 286 // in the response, but does not include "Non-existing: void". |
| 287 |
| 288 std::vector< std::vector<const std::string*> > tests; |
| 289 bool result; |
| 290 |
| 291 // 1.a. -- All these tests should pass. |
| 292 const std::string kPassingCondition[] = { |
| 293 keys::kNamePrefixKey, "Custom", |
| 294 keys::kNameSuffixKey, "m-header", // Header names are case insensitive. |
| 295 keys::kValueContainsKey, "alu", |
| 296 keys::kValueEqualsKey, "custom/value" |
| 297 }; |
| 298 const size_t kPassingConditionSizes[] = { arraysize(kPassingCondition) }; |
| 299 GetArrayAsVector(kPassingCondition, kPassingConditionSizes, 1u, &tests); |
| 300 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 301 EXPECT_TRUE(result); |
| 302 |
| 303 // 1.b. -- None of the following tests in the discjunction should pass. |
| 304 const std::string kFailCondition[] = { |
| 305 keys::kNamePrefixKey, " Custom", // Test 1. |
| 306 keys::kNameContainsKey, " -", // Test 2. |
| 307 keys::kValueSuffixKey, "alu", // Test 3. |
| 308 keys::kValueEqualsKey, "custom" // Test 4. |
| 309 }; |
| 310 const size_t kFailConditionSizes[] = { 2u, 2u, 2u, 2u }; |
| 311 GetArrayAsVector(kFailCondition, kFailConditionSizes, 4u, &tests); |
| 312 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 313 EXPECT_FALSE(result); |
| 314 |
| 315 // 1.c. -- This should fail (mixing name and value from different headers) |
| 316 const std::string kMixingCondition[] = { |
| 317 keys::kNameSuffixKey, "Header-B", |
| 318 keys::kValueEqualsKey, "custom/value" |
| 319 }; |
| 320 const size_t kMixingConditionSizes[] = { arraysize(kMixingCondition) }; |
| 321 GetArrayAsVector(kMixingCondition, kMixingConditionSizes, 1u, &tests); |
| 322 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 323 EXPECT_FALSE(result); |
| 324 |
| 325 // 1.d. -- Test handling multiple values for one header (both should pass). |
| 326 const std::string kMoreValues1[] = { |
| 327 keys::kNameEqualsKey, "Custom-header-b", |
| 328 keys::kValueEqualsKey, "valueA" |
| 329 }; |
| 330 const size_t kMoreValues1Sizes[] = { arraysize(kMoreValues1) }; |
| 331 GetArrayAsVector(kMoreValues1, kMoreValues1Sizes, 1u, &tests); |
| 332 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 333 EXPECT_TRUE(result); |
| 334 const std::string kMoreValues2[] = { |
| 335 keys::kNameEqualsKey, "Custom-header-b", |
| 336 keys::kValueEqualsKey, "valueB" |
| 337 }; |
| 338 const size_t kMoreValues2Sizes[] = { arraysize(kMoreValues2) }; |
| 339 GetArrayAsVector(kMoreValues2, kMoreValues2Sizes, 1u, &tests); |
| 340 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 341 EXPECT_TRUE(result); |
| 342 |
| 343 // 1.e. -- This should fail as conjunction but pass as disjunction. |
| 344 const std::string kConflict[] = { |
| 345 keys::kNameSuffixKey, "Header", // True for some header. |
| 346 keys::kNameContainsKey, "Header-B" // True for a different header. |
| 347 }; |
| 348 // First disjunction, no conflict. |
| 349 const size_t kNoConflictSizes[] = { 2u, 2u }; |
| 350 GetArrayAsVector(kConflict, kNoConflictSizes, 2u, &tests); |
| 351 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 352 EXPECT_TRUE(result); |
| 353 // Then conjunction, conflict. |
| 354 const size_t kConflictSizes[] = { arraysize(kConflict) }; |
| 355 GetArrayAsVector(kConflict, kConflictSizes, 1u, &tests); |
| 356 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 357 EXPECT_FALSE(result); |
| 358 |
| 359 // 1.f. -- This should pass, checking for correct treatment of ',' in values. |
| 360 const std::string kComma[] = { |
| 361 keys::kNameSuffixKey, "Header-C", |
| 362 keys::kValueEqualsKey, "valueC, valueD" |
| 363 }; |
| 364 const size_t kCommaSizes[] = { arraysize(kComma) }; |
| 365 GetArrayAsVector(kComma, kCommaSizes, 1u, &tests); |
| 366 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 367 EXPECT_TRUE(result); |
| 368 |
| 369 // 1.g. -- This should pass, empty values are values as well. |
| 370 const std::string kEmpty[] = { |
| 371 keys::kNameEqualsKey, "custom-header-d", |
| 372 keys::kValueEqualsKey, "" |
| 373 }; |
| 374 const size_t kEmptySizes[] = { arraysize(kEmpty) }; |
| 375 GetArrayAsVector(kEmpty, kEmptySizes, 1u, &tests); |
| 376 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 377 EXPECT_TRUE(result); |
| 378 |
| 379 // 1.h. -- Values are case-sensitive, this should fail |
| 380 const std::string kLowercase[] = { |
| 381 keys::kNameEqualsKey, "Custom-header-b", |
| 382 keys::kValueEqualsKey, "valuea" // valuea != valueA |
| 383 }; |
| 384 const size_t kLowercaseSizes[] = { arraysize(kLowercase) }; |
| 385 GetArrayAsVector(kLowercase, kLowercaseSizes, 1u, &tests); |
| 386 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 387 EXPECT_FALSE(result); |
| 388 |
| 389 // 2.a. -- This should pass as disjunction, because one of the tests passes. |
| 390 const std::string kDisjunction[] = { |
| 391 keys::kNamePrefixKey, "Non-existing", // This one fails. |
| 392 keys::kNameSuffixKey, "Non-existing", // This one fails. |
| 393 keys::kValueEqualsKey, "void", // This one fails. |
| 394 keys::kValueContainsKey, "alu" // This passes. |
| 395 }; |
| 396 const size_t kDisjunctionSizes[] = { 2u, 2u, 2u, 2u }; |
| 397 GetArrayAsVector(kDisjunction, kDisjunctionSizes, 4u, &tests); |
| 398 MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result); |
| 399 EXPECT_TRUE(result); |
| 400 |
| 401 // 3.a. -- This should pass. |
| 402 const std::string kNonExistent[] = { |
| 403 keys::kNameEqualsKey, "Non-existing", |
| 404 keys::kValueEqualsKey, "void" |
| 405 }; |
| 406 const size_t kNonExistentSizes[] = { arraysize(kNonExistent) }; |
| 407 GetArrayAsVector(kNonExistent, kNonExistentSizes, 1u, &tests); |
| 408 MatchAndCheck(tests, keys::kExcludeResponseHeadersKey, &url_request, &result); |
| 409 EXPECT_TRUE(result); |
| 410 |
| 411 // 3.b. -- This should fail. |
| 412 const std::string kExisting[] = { |
| 413 keys::kNameEqualsKey, "custom-header-b", |
| 414 keys::kValueEqualsKey, "valueB" |
| 415 }; |
| 416 const size_t kExistingSize[] = { arraysize(kExisting) }; |
| 417 GetArrayAsVector(kExisting, kExistingSize, 1u, &tests); |
| 418 MatchAndCheck(tests, keys::kExcludeResponseHeadersKey, &url_request, &result); |
| 419 EXPECT_FALSE(result); |
| 420 } |
| 421 |
159 } // namespace extensions | 422 } // namespace extensions |
OLD | NEW |