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 <algorithm> | 7 #include <algorithm> |
8 #include <set> | |
9 #include <string.h> | |
8 | 10 |
9 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/string_util.h" | |
10 #include "base/stringprintf.h" | 13 #include "base/stringprintf.h" |
11 #include "base/values.h" | 14 #include "base/values.h" |
12 #include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h" | 15 #include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h" |
13 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_consta nts.h" | 16 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_consta nts.h" |
14 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h" | 17 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h" |
15 #include "chrome/common/extensions/extension_error_utils.h" | 18 #include "chrome/common/extensions/extension_error_utils.h" |
16 #include "content/public/browser/resource_request_info.h" | 19 #include "content/public/browser/resource_request_info.h" |
17 #include "net/http/http_util.h" | 20 #include "net/http/http_util.h" |
18 #include "net/http/http_request_headers.h" | 21 #include "net/http/http_request_headers.h" |
19 #include "net/url_request/url_request.h" | 22 #include "net/url_request/url_request.h" |
20 | 23 |
24 using base::DictionaryValue; | |
25 using base::ListValue; | |
26 using base::StringValue; | |
27 using base::Value; | |
28 | |
21 namespace { | 29 namespace { |
22 // Error messages. | 30 // Error messages. |
23 const char kUnknownConditionAttribute[] = "Unknown matching condition: '*'"; | 31 const char kUnknownConditionAttribute[] = "Unknown matching condition: '*'"; |
24 const char kInvalidValue[] = "Condition '*' has an invalid value"; | 32 const char kInvalidValue[] = "Condition '*' has an invalid value"; |
25 } | 33 } |
26 | 34 |
27 namespace helpers = extension_web_request_api_helpers; | 35 namespace helpers = extension_web_request_api_helpers; |
28 | 36 |
29 namespace extensions { | 37 namespace extensions { |
30 | 38 |
31 namespace keys = declarative_webrequest_constants; | 39 namespace keys = declarative_webrequest_constants; |
32 | 40 |
33 // | 41 // |
34 // WebRequestConditionAttribute | 42 // WebRequestConditionAttribute |
35 // | 43 // |
36 | 44 |
37 WebRequestConditionAttribute::WebRequestConditionAttribute() {} | 45 WebRequestConditionAttribute::WebRequestConditionAttribute() {} |
38 | 46 |
39 WebRequestConditionAttribute::~WebRequestConditionAttribute() {} | 47 WebRequestConditionAttribute::~WebRequestConditionAttribute() {} |
40 | 48 |
41 // static | 49 // static |
42 bool WebRequestConditionAttribute::IsKnownType( | 50 bool WebRequestConditionAttribute::IsKnownType( |
43 const std::string& instance_type) { | 51 const std::string& instance_type) { |
44 return | 52 return |
45 WebRequestConditionAttributeResourceType::IsMatchingType(instance_type) || | 53 WebRequestConditionAttributeResourceType::IsMatchingType(instance_type) || |
46 WebRequestConditionAttributeContentType::IsMatchingType(instance_type); | 54 WebRequestConditionAttributeContentType::IsMatchingType(instance_type) || |
55 WebRequestConditionAttributeContainsHeaders::IsMatchingType( | |
56 instance_type); | |
47 } | 57 } |
48 | 58 |
49 // static | 59 // static |
50 scoped_ptr<WebRequestConditionAttribute> | 60 scoped_ptr<WebRequestConditionAttribute> |
51 WebRequestConditionAttribute::Create( | 61 WebRequestConditionAttribute::Create( |
52 const std::string& name, | 62 const std::string& name, |
53 const base::Value* value, | 63 const base::Value* value, |
54 std::string* error) { | 64 std::string* error) { |
65 CHECK(value != NULL && error != NULL); | |
55 if (WebRequestConditionAttributeResourceType::IsMatchingType(name)) { | 66 if (WebRequestConditionAttributeResourceType::IsMatchingType(name)) { |
56 return WebRequestConditionAttributeResourceType::Create(name, value, error); | 67 return WebRequestConditionAttributeResourceType::Create(name, value, error); |
57 } else if (WebRequestConditionAttributeContentType::IsMatchingType(name)) { | 68 } else if (WebRequestConditionAttributeContentType::IsMatchingType(name)) { |
58 return WebRequestConditionAttributeContentType::Create(name, value, error); | 69 return WebRequestConditionAttributeContentType::Create(name, value, error); |
70 } else if (WebRequestConditionAttributeContainsHeaders::IsMatchingType( | |
71 name)) { | |
72 return WebRequestConditionAttributeContainsHeaders::Create( | |
73 name, value, error); | |
59 } | 74 } |
60 | 75 |
61 *error = ExtensionErrorUtils::FormatErrorMessage(kUnknownConditionAttribute, | 76 *error = ExtensionErrorUtils::FormatErrorMessage(kUnknownConditionAttribute, |
62 name); | 77 name); |
63 return scoped_ptr<WebRequestConditionAttribute>(NULL); | 78 return scoped_ptr<WebRequestConditionAttribute>(NULL); |
64 } | 79 } |
65 | 80 |
66 // | 81 // |
67 // WebRequestConditionAttributeResourceType | 82 // WebRequestConditionAttributeResourceType |
68 // | 83 // |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
212 return std::find(content_types_.begin(), content_types_.end(), | 227 return std::find(content_types_.begin(), content_types_.end(), |
213 mime_type) == content_types_.end(); | 228 mime_type) == content_types_.end(); |
214 } | 229 } |
215 } | 230 } |
216 | 231 |
217 WebRequestConditionAttribute::Type | 232 WebRequestConditionAttribute::Type |
218 WebRequestConditionAttributeContentType::GetType() const { | 233 WebRequestConditionAttributeContentType::GetType() const { |
219 return CONDITION_CONTENT_TYPE; | 234 return CONDITION_CONTENT_TYPE; |
220 } | 235 } |
221 | 236 |
237 // | |
238 // WebRequestConditionAttributeContainsHeaders | |
239 // | |
240 | |
241 WebRequestConditionAttributeContainsHeaders::TestGroup::TestGroup() {} | |
242 WebRequestConditionAttributeContainsHeaders::TestGroup::~TestGroup() {} | |
243 | |
244 WebRequestConditionAttributeContainsHeaders:: | |
245 WebRequestConditionAttributeContainsHeaders(bool positive_test) | |
246 : positive_test_(positive_test) {} | |
247 | |
248 WebRequestConditionAttributeContainsHeaders:: | |
249 ~WebRequestConditionAttributeContainsHeaders() {} | |
250 | |
251 // static | |
252 bool WebRequestConditionAttributeContainsHeaders::IsMatchingType( | |
253 const std::string& instance_type) { | |
254 return instance_type == keys::kContainsHeadersKey || | |
255 instance_type == keys::kDoesNotContainHeadersKey; | |
256 } | |
257 | |
258 // static | |
259 scoped_ptr<WebRequestConditionAttribute> | |
260 WebRequestConditionAttributeContainsHeaders::Create( | |
261 const std::string& name, | |
262 const base::Value* value, | |
263 std::string* error) { | |
264 DCHECK(IsMatchingType(name)); | |
265 | |
266 enum { kList, kDictionary, kNone } obtained_value = kNone; | |
267 | |
268 const DictionaryValue* value_as_dictionary = NULL; | |
269 const ListValue* value_as_list = NULL; | |
270 if (value->GetAsList(&value_as_list)) | |
271 obtained_value = kList; | |
272 else if (value->GetAsDictionary(&value_as_dictionary)) | |
273 obtained_value = kDictionary; | |
274 | |
275 scoped_ptr<WebRequestConditionAttributeContainsHeaders> result; | |
276 if (obtained_value != kNone) { | |
277 // Only construct the condition after we know we have a valid |value|. | |
278 result.reset(new WebRequestConditionAttributeContainsHeaders( | |
279 name == keys::kContainsHeadersKey)); | |
280 } | |
281 switch (obtained_value) { | |
282 case kList: | |
283 for (ListValue::const_iterator it = value_as_list->begin(); | |
284 it != value_as_list->end(); ++it) { | |
285 const DictionaryValue* tests = NULL; | |
286 if (!(*it)->GetAsDictionary(&tests)) { | |
287 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
288 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
289 } | |
290 | |
291 if (!result->PushMatchTests(tests, error)) { | |
292 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
293 } | |
294 } | |
295 break; | |
296 case kDictionary: | |
297 if (!result->PushMatchTests(value_as_dictionary, error)) { | |
298 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
299 } | |
battre
2012/08/23 12:32:11
nit: no {}, also in 291
vabr (Chromium)
2012/08/24 14:48:59
Done, and subsequently deleted altogether. Case kD
| |
300 break; | |
301 case kNone: | |
302 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
303 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
304 } | |
305 | |
306 | |
307 return result.PassAs<WebRequestConditionAttribute>(); | |
308 } | |
309 | |
310 int WebRequestConditionAttributeContainsHeaders::GetStages() const { | |
311 return ON_HEADERS_RECEIVED; | |
312 } | |
313 | |
314 bool WebRequestConditionAttributeContainsHeaders::IsFulfilled( | |
315 const WebRequestRule::RequestData& request_data) { | |
316 if (!(request_data.stage & GetStages())) | |
317 return false; | |
318 | |
319 const net::HttpResponseHeaders* | |
320 headers = request_data.original_response_headers; | |
battre
2012/08/23 12:32:11
"headers =" goes into previous line.
vabr (Chromium)
2012/08/24 14:48:59
Done.
| |
321 if (headers == NULL) { | |
322 // Each header of an empty set satisfies (the negation of) everything; | |
323 // OTOH, there is no header to satisfy even the most permissive test. | |
324 return !positive_test_; | |
325 } | |
326 | |
327 // |success| == for some |i|, has there been a name-value header pair | |
328 // satisfying all tests from |tests_[i]|? | |
329 bool success = false; | |
330 | |
331 // First we get all header names. Unfortunately, this is unnecessarily | |
332 // costly, because the values are copied along anyway. But we need to know | |
333 // the header names so that we can call EnumerateHeader instead of | |
334 // EnumerateHeaderLines. | |
335 // TODO(vabr) -- check whether you could implement this header enumeration | |
336 // directly in HttpResponseHeaders instead. | |
337 std::set<std::string> header_names; | |
338 std::string name; | |
339 std::string value; | |
340 for (void* iter = NULL; | |
341 headers->EnumerateHeaderLines(&iter, &name, &value); | |
342 /* No increment here, this is done inside EnumerateHeaderLines. */) { | |
343 StringToLowerASCII(&name); // Header names are case-insensitive. | |
344 header_names.insert(name); | |
345 } | |
346 | |
347 for (std::set<std::string>::const_iterator it = header_names.begin(); | |
348 !success && it != header_names.end(); | |
349 ++it) { | |
350 for (size_t i = 0; !success && i < tests_.size(); ++i) { | |
battre
2012/08/23 12:32:11
How about moving this loop to become the outer loo
vabr (Chromium)
2012/08/24 14:48:59
Done.
| |
351 // |header_success| == have all tests for this header passed so far? | |
352 bool header_success = true; | |
353 for (size_t j = 0; header_success && j < tests_[i].name.size(); ++j) { | |
354 header_success &= tests_[i].name[j].Matches(*it); | |
355 } | |
356 if (!header_success) | |
357 continue; | |
358 | |
359 // Is there a value of this header such that all tests pass for it? | |
360 bool found_value = false; | |
361 for (void* iter = NULL; | |
362 !found_value && headers->EnumerateHeader(&iter, *it, &value); | |
363 /* No increment here, done inside EnumerateHeader. */) { | |
364 // |value_success| == have all tests for this value passed so far? | |
365 bool value_success = true; | |
366 for (size_t j = 0; value_success && j < tests_[i].value.size(); ++j) { | |
367 value_success &= tests_[i].value[j].Matches(value); | |
368 } | |
369 found_value |= value_success; | |
370 } | |
371 | |
372 // Here we already know that header_success == true, just check values. | |
373 success |= found_value; | |
374 } | |
375 } | |
376 | |
377 return (positive_test_ ? success : !success); | |
378 } | |
379 | |
380 WebRequestConditionAttribute::Type | |
381 WebRequestConditionAttributeContainsHeaders::GetType() const { | |
382 return CONDITION_CONTAINS_HEADERS; | |
383 } | |
384 | |
385 bool WebRequestConditionAttributeContainsHeaders::MatchTest::Matches( | |
386 const std::string& str) { | |
387 switch (type_) { | |
388 case kPrefix: { | |
battre
2012/08/23 12:32:11
You can use StartsWithASCII and EndsWith for these
vabr (Chromium)
2012/08/24 14:48:59
Done. Thanks for making me aware of those function
| |
389 if (data_.size() > str.size()) | |
390 return false; | |
391 return strncmp(data_.data(), str.data(), data_.size()) == 0; | |
392 } | |
393 case kSuffix: { | |
394 if (data_.size() > str.size()) | |
395 return false; | |
396 const char* start = str.data() + str.size() - data_.size(); | |
397 return strncmp(data_.data(), start, data_.size()) == 0; | |
398 } | |
399 case kEqual: { | |
400 return data_ == str; | |
401 } | |
402 case kContains: { | |
403 return str.find(data_) != std::string::npos; | |
404 } | |
405 } | |
406 // We never get past the "switch", but the compiler worries about no return. | |
407 NOTREACHED(); | |
408 return false; | |
409 } | |
410 | |
411 bool WebRequestConditionAttributeContainsHeaders::PushMatchTests( | |
412 const DictionaryValue* tests, std::string* error) { | |
battre
2012/08/23 12:32:11
How about a functional style of programming? I.e.
vabr (Chromium)
2012/08/24 14:48:59
Done.
| |
413 tests_.push_back(TestGroup()); | |
414 | |
415 for (DictionaryValue::key_iterator key = tests->begin_keys(); | |
416 key != tests->end_keys(); | |
417 ++key) { | |
418 bool is_name = false; // Is this test for header name? | |
419 MatchType match_type; | |
420 if (*key == keys::kNamePrefixKey) { | |
421 is_name = true; | |
422 match_type = kPrefix; | |
423 } else if (*key == keys::kNameSuffixKey) { | |
424 is_name = true; | |
425 match_type = kSuffix; | |
426 } else if (*key == keys::kNameContainsKey) { | |
427 is_name = true; | |
428 match_type = kContains; | |
429 } else if (*key == keys::kNameEqualsKey) { | |
430 is_name = true; | |
431 match_type = kEqual; | |
432 } else if (*key == keys::kValuePrefixKey) { | |
433 match_type = kPrefix; | |
434 } else if (*key == keys::kValueSuffixKey) { | |
435 match_type = kSuffix; | |
436 } else if (*key == keys::kValueContainsKey) { | |
437 match_type = kContains; | |
438 } else if (*key == keys::kValueEqualsKey) { | |
439 match_type = kEqual; | |
440 } else { | |
441 NOTREACHED(); // JSON schema type checking should prevent this. | |
442 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); | |
443 return false; | |
444 } | |
445 const Value* content; | |
battre
2012/08/23 12:32:11
nit: = NULL
vabr (Chromium)
2012/08/24 14:48:59
Done.
| |
446 tests->Get(*key, &content); // We already checked that |key| is there. | |
battre
2012/08/23 12:32:11
CHECK?
vabr (Chromium)
2012/08/24 14:48:59
Done.
| |
447 switch (content->GetType()) { | |
448 case Value::TYPE_LIST: { | |
449 const ListValue* list = NULL; | |
450 CHECK(content->GetAsList(&list)); | |
451 for (ListValue::const_iterator it = list->begin(); | |
452 it != list->end(); ++it) { | |
453 PushOneMatchTest(*it, is_name, match_type); | |
454 } | |
455 break; | |
456 } | |
457 case Value::TYPE_STRING: { | |
458 PushOneMatchTest(content, is_name, match_type); | |
459 break; | |
460 } | |
461 default: { | |
462 NOTREACHED(); // JSON schema type checking should prevent this. | |
463 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); | |
464 return false; | |
465 } | |
466 } | |
467 } | |
468 return true; | |
469 } | |
470 | |
471 void WebRequestConditionAttributeContainsHeaders::PushOneMatchTest( | |
472 const Value* content, bool is_name_test, MatchType match_type) { | |
473 std::string* data = NULL; | |
474 if (is_name_test) { | |
475 std::vector<MatchTest>* current_name_tests = &(tests_.back().name); | |
476 current_name_tests->push_back(MatchTest(match_type)); | |
477 data = &(current_name_tests->back().data()); | |
478 } else { | |
479 std::vector<MatchTest>* current_value_tests = &(tests_.back().value); | |
480 current_value_tests->push_back(MatchTest(match_type)); | |
481 data = &(current_value_tests->back().data()); | |
482 } | |
483 CHECK(content->GetAsString(data)); | |
484 if (is_name_test) // Header names are case-insensitive. | |
485 StringToLowerASCII(data); | |
486 } | |
487 | |
222 } // namespace extensions | 488 } // namespace extensions |
OLD | NEW |