Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(368)

Side by Side Diff: chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc

Issue 10874029: Adding condition attributes for response headers to Declarative WebRequest (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Some typos corrected Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698