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

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: Minor cleanup 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 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/string_util.h"
10 #include "base/stringprintf.h" 11 #include "base/stringprintf.h"
11 #include "base/values.h" 12 #include "base/values.h"
12 #include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h" 13 #include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
13 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_consta nts.h" 14 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_consta nts.h"
14 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h" 15 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
15 #include "chrome/common/extensions/extension_error_utils.h" 16 #include "chrome/common/extensions/extension_error_utils.h"
16 #include "content/public/browser/resource_request_info.h" 17 #include "content/public/browser/resource_request_info.h"
17 #include "net/http/http_util.h" 18 #include "net/http/http_util.h"
18 #include "net/http/http_request_headers.h" 19 #include "net/http/http_request_headers.h"
19 #include "net/url_request/url_request.h" 20 #include "net/url_request/url_request.h"
20 21
22 using base::DictionaryValue;
23 using base::ListValue;
24 using base::StringValue;
25 using base::Value;
26
21 namespace { 27 namespace {
22 // Error messages. 28 // Error messages.
23 const char kUnknownConditionAttribute[] = "Unknown matching condition: '*'"; 29 const char kUnknownConditionAttribute[] = "Unknown matching condition: '*'";
24 const char kInvalidValue[] = "Condition '*' has an invalid value"; 30 const char kInvalidValue[] = "Condition '*' has an invalid value";
25 } 31 }
26 32
27 namespace helpers = extension_web_request_api_helpers; 33 namespace helpers = extension_web_request_api_helpers;
28 34
29 namespace extensions { 35 namespace extensions {
30 36
31 namespace keys = declarative_webrequest_constants; 37 namespace keys = declarative_webrequest_constants;
32 38
33 // 39 //
34 // WebRequestConditionAttribute 40 // WebRequestConditionAttribute
35 // 41 //
36 42
37 WebRequestConditionAttribute::WebRequestConditionAttribute() {} 43 WebRequestConditionAttribute::WebRequestConditionAttribute() {}
38 44
39 WebRequestConditionAttribute::~WebRequestConditionAttribute() {} 45 WebRequestConditionAttribute::~WebRequestConditionAttribute() {}
40 46
41 // static 47 // static
42 bool WebRequestConditionAttribute::IsKnownType( 48 bool WebRequestConditionAttribute::IsKnownType(
43 const std::string& instance_type) { 49 const std::string& instance_type) {
44 return 50 return
45 WebRequestConditionAttributeResourceType::IsMatchingType(instance_type) || 51 WebRequestConditionAttributeResourceType::IsMatchingType(instance_type) ||
46 WebRequestConditionAttributeContentType::IsMatchingType(instance_type); 52 WebRequestConditionAttributeContentType::IsMatchingType(instance_type) ||
53 WebRequestConditionAttributeResponseHeaders::IsMatchingType(
54 instance_type);
47 } 55 }
48 56
49 // static 57 // static
50 scoped_ptr<WebRequestConditionAttribute> 58 scoped_ptr<WebRequestConditionAttribute>
51 WebRequestConditionAttribute::Create( 59 WebRequestConditionAttribute::Create(
52 const std::string& name, 60 const std::string& name,
53 const base::Value* value, 61 const base::Value* value,
54 std::string* error) { 62 std::string* error) {
63 CHECK(value != NULL && error != NULL);
55 if (WebRequestConditionAttributeResourceType::IsMatchingType(name)) { 64 if (WebRequestConditionAttributeResourceType::IsMatchingType(name)) {
56 return WebRequestConditionAttributeResourceType::Create(name, value, error); 65 return WebRequestConditionAttributeResourceType::Create(name, value, error);
57 } else if (WebRequestConditionAttributeContentType::IsMatchingType(name)) { 66 } else if (WebRequestConditionAttributeContentType::IsMatchingType(name)) {
58 return WebRequestConditionAttributeContentType::Create(name, value, error); 67 return WebRequestConditionAttributeContentType::Create(name, value, error);
68 } else if (WebRequestConditionAttributeResponseHeaders::IsMatchingType(
69 name)) {
70 return WebRequestConditionAttributeResponseHeaders::Create(
71 name, value, error);
59 } 72 }
60 73
61 *error = ExtensionErrorUtils::FormatErrorMessage(kUnknownConditionAttribute, 74 *error = ExtensionErrorUtils::FormatErrorMessage(kUnknownConditionAttribute,
62 name); 75 name);
63 return scoped_ptr<WebRequestConditionAttribute>(NULL); 76 return scoped_ptr<WebRequestConditionAttribute>(NULL);
64 } 77 }
65 78
66 // 79 //
67 // WebRequestConditionAttributeResourceType 80 // WebRequestConditionAttributeResourceType
68 // 81 //
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 new WebRequestConditionAttributeResourceType(passed_types)); 128 new WebRequestConditionAttributeResourceType(passed_types));
116 } 129 }
117 130
118 int WebRequestConditionAttributeResourceType::GetStages() const { 131 int WebRequestConditionAttributeResourceType::GetStages() const {
119 return ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | ON_SEND_HEADERS | 132 return ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | ON_SEND_HEADERS |
120 ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED | ON_BEFORE_REDIRECT | 133 ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED | ON_BEFORE_REDIRECT |
121 ON_RESPONSE_STARTED | ON_COMPLETED | ON_ERROR; 134 ON_RESPONSE_STARTED | ON_COMPLETED | ON_ERROR;
122 } 135 }
123 136
124 bool WebRequestConditionAttributeResourceType::IsFulfilled( 137 bool WebRequestConditionAttributeResourceType::IsFulfilled(
125 const WebRequestRule::RequestData& request_data) { 138 const WebRequestRule::RequestData& request_data) const {
126 if (!(request_data.stage & GetStages())) 139 if (!(request_data.stage & GetStages()))
127 return false; 140 return false;
128 const content::ResourceRequestInfo* info = 141 const content::ResourceRequestInfo* info =
129 content::ResourceRequestInfo::ForRequest(request_data.request); 142 content::ResourceRequestInfo::ForRequest(request_data.request);
130 if (!info) 143 if (!info)
131 return false; 144 return false;
132 return std::find(types_.begin(), types_.end(), info->GetResourceType()) != 145 return std::find(types_.begin(), types_.end(), info->GetResourceType()) !=
133 types_.end(); 146 types_.end();
134 } 147 }
135 148
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
186 return scoped_ptr<WebRequestConditionAttribute>( 199 return scoped_ptr<WebRequestConditionAttribute>(
187 new WebRequestConditionAttributeContentType( 200 new WebRequestConditionAttributeContentType(
188 content_types, name == keys::kContentTypeKey)); 201 content_types, name == keys::kContentTypeKey));
189 } 202 }
190 203
191 int WebRequestConditionAttributeContentType::GetStages() const { 204 int WebRequestConditionAttributeContentType::GetStages() const {
192 return ON_HEADERS_RECEIVED; 205 return ON_HEADERS_RECEIVED;
193 } 206 }
194 207
195 bool WebRequestConditionAttributeContentType::IsFulfilled( 208 bool WebRequestConditionAttributeContentType::IsFulfilled(
196 const WebRequestRule::RequestData& request_data) { 209 const WebRequestRule::RequestData& request_data) const {
197 if (!(request_data.stage & GetStages())) 210 if (!(request_data.stage & GetStages()))
198 return false; 211 return false;
199 std::string content_type; 212 std::string content_type;
200 request_data.original_response_headers->GetNormalizedHeader( 213 request_data.original_response_headers->GetNormalizedHeader(
201 net::HttpRequestHeaders::kContentType, &content_type); 214 net::HttpRequestHeaders::kContentType, &content_type);
202 std::string mime_type; 215 std::string mime_type;
203 std::string charset; 216 std::string charset;
204 bool had_charset = false; 217 bool had_charset = false;
205 net::HttpUtil::ParseContentType( 218 net::HttpUtil::ParseContentType(
206 content_type, &mime_type, &charset, &had_charset, NULL); 219 content_type, &mime_type, &charset, &had_charset, NULL);
207 220
208 if (inclusive_) { 221 if (inclusive_) {
209 return std::find(content_types_.begin(), content_types_.end(), 222 return std::find(content_types_.begin(), content_types_.end(),
210 mime_type) != content_types_.end(); 223 mime_type) != content_types_.end();
211 } else { 224 } else {
212 return std::find(content_types_.begin(), content_types_.end(), 225 return std::find(content_types_.begin(), content_types_.end(),
213 mime_type) == content_types_.end(); 226 mime_type) == content_types_.end();
214 } 227 }
215 } 228 }
216 229
217 WebRequestConditionAttribute::Type 230 WebRequestConditionAttribute::Type
218 WebRequestConditionAttributeContentType::GetType() const { 231 WebRequestConditionAttributeContentType::GetType() const {
219 return CONDITION_CONTENT_TYPE; 232 return CONDITION_CONTENT_TYPE;
220 } 233 }
221 234
235 //
236 // WebRequestConditionAttributeResponseHeaders
237 //
238
239 WebRequestConditionAttributeResponseHeaders::MatchTest::MatchTest(
240 scoped_ptr<const std::string> data,
241 MatchType type)
242 : data_(data.Pass()),
243 type_(type) {
244 CHECK(data_.get() != NULL);
245 }
246
247 WebRequestConditionAttributeResponseHeaders::MatchTest::~MatchTest() {}
248
249 WebRequestConditionAttributeResponseHeaders::TestGroup::TestGroup(
250 ScopedVector<const MatchTest>* name,
251 ScopedVector<const MatchTest>* value)
252 : name_(name->Pass()),
253 value_(value->Pass()) {}
254
255 WebRequestConditionAttributeResponseHeaders::TestGroup::~TestGroup() {}
256
257 WebRequestConditionAttributeResponseHeaders::
258 WebRequestConditionAttributeResponseHeaders(bool positive_test)
259 : positive_test_(positive_test) {}
260
261 WebRequestConditionAttributeResponseHeaders::
262 ~WebRequestConditionAttributeResponseHeaders() {}
263
264 // static
265 bool WebRequestConditionAttributeResponseHeaders::IsMatchingType(
266 const std::string& instance_type) {
267 return instance_type == keys::kResponseHeadersKey ||
268 instance_type == keys::kExcludeResponseHeadersKey;
269 }
270
271 // static
272 scoped_ptr<WebRequestConditionAttribute>
273 WebRequestConditionAttributeResponseHeaders::Create(
274 const std::string& name,
275 const base::Value* value,
276 std::string* error) {
277 DCHECK(IsMatchingType(name));
278
279 const ListValue* value_as_list = NULL;
280 if (!value->GetAsList(&value_as_list)) {
281 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name);
282 return scoped_ptr<WebRequestConditionAttribute>(NULL);
283 }
284
285 ScopedVector<const TestGroup> groups;
286 for (ListValue::const_iterator it = value_as_list->begin();
287 it != value_as_list->end(); ++it) {
288 const DictionaryValue* tests = NULL;
289 if (!(*it)->GetAsDictionary(&tests)) {
290 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name);
291 return scoped_ptr<WebRequestConditionAttribute>(NULL);
292 }
293
294 scoped_ptr<const TestGroup> group(GetTests(tests, error).Pass());
battre 2012/08/24 16:24:41 Is this Pass necessary?
vabr (Chromium) 2012/08/27 12:11:50 I don't understand why, but it is not (at least wi
295 if (group.get() == NULL)
296 return scoped_ptr<WebRequestConditionAttribute>(NULL);
297 groups.push_back(group.release());
298 }
299
300 scoped_ptr<WebRequestConditionAttributeResponseHeaders> result;
301 result.reset(new WebRequestConditionAttributeResponseHeaders(
302 name == keys::kResponseHeadersKey));
303 result->tests_.swap(groups);
battre 2012/08/24 16:24:41 nit: Why don't you pass a pointer to groups to the
vabr (Chromium) 2012/08/27 12:11:50 Done.
304
305 return result.PassAs<WebRequestConditionAttribute>();
306 }
307
308 int WebRequestConditionAttributeResponseHeaders::GetStages() const {
309 return ON_HEADERS_RECEIVED;
310 }
311
312 bool WebRequestConditionAttributeResponseHeaders::IsFulfilled(
313 const WebRequestRule::RequestData& request_data) const {
314 if (!(request_data.stage & GetStages()))
315 return false;
316
317 const net::HttpResponseHeaders* headers =
318 request_data.original_response_headers;
319 if (headers == NULL) {
320 // Each header of an empty set satisfies (the negation of) everything;
321 // OTOH, there is no header to satisfy even the most permissive test.
322 return !positive_test_;
323 }
324
325 // |success| == for some |i|, has there been a name-value header pair
326 // satisfying all tests from |tests_[i]|?
327 bool success = false;
328
329 for (size_t i = 0; !success && i < tests_.size(); ++i) {
330 std::string name;
331 std::string value;
332 // Has some header already passed the current test group?
333 bool header_found = false;
battre 2012/08/24 16:24:41 move this to line 327 and remove success?
vabr (Chromium) 2012/08/27 12:11:50 Done.
334
335 for (void* iter = NULL;
336 !header_found && headers->EnumerateHeaderLines(&iter, &name, &value);
337 /* No increment here, this is done inside EnumerateHeaderLines. */) {
338 StringToLowerASCII(&name); // Header names are case-insensitive.
339 header_found |= tests_[i]->Matches(name, value);
vabr (Chromium) 2012/08/24 15:10:07 If one header occurs on multiple lines, then we te
340 }
341
342 success |= header_found;
343 }
344
345 return (positive_test_ ? success : !success);
346 }
347
348 WebRequestConditionAttribute::Type
349 WebRequestConditionAttributeResponseHeaders::GetType() const {
350 return CONDITION_REQUEST_HEADERS;
351 }
352
353 bool WebRequestConditionAttributeResponseHeaders::MatchTest::Matches(
354 const std::string& str) const {
355 switch (type_) {
356 case kPrefix:
357 return StartsWithASCII(str, data(), true /*case_sensitive*/);
battre 2012/08/24 16:24:41 you can replace data() with data_ and remove data(
vabr (Chromium) 2012/08/27 12:11:50 Done.
358 case kSuffix:
359 return EndsWith(str, data(), true /*case_sensitive*/);
360 case kEquals:
361 return data() == str;
362 case kContains:
363 return str.find(data()) != std::string::npos;
364 }
365 // We never get past the "switch", but the compiler worries about no return.
366 NOTREACHED();
367 return false;
368 }
369
370 bool WebRequestConditionAttributeResponseHeaders::TestGroup::Matches(
371 const std::string& name,
372 const std::string& value) const {
373 // |header_success| == have all tests for this header passed so far?
374 bool header_success = true;
375 for (size_t j = 0; header_success && j < name_.size(); ++j) {
battre 2012/08/24 16:24:41 nit: use i as iterator? also in line 383
vabr (Chromium) 2012/08/27 12:11:50 Done.
376 header_success &= name_[j]->Matches(name);
377 }
378 if (!header_success)
379 return false;
380
381 // |value_success| == have all tests for this value passed so far?
382 bool value_success = true;
383 for (size_t j = 0; value_success && j < value_.size(); ++j) {
384 value_success &= value_[j]->Matches(value);
385 }
386
387 // Here we already know that header_success == true, just check values.
388 return value_success;
battre 2012/08/24 16:24:41 you could make this function slightly shorter: for
vabr (Chromium) 2012/08/27 12:11:50 Done (except for making the last return true :)).
389 }
390
391
392 // static
393 scoped_ptr<const WebRequestConditionAttributeResponseHeaders::TestGroup>
394 WebRequestConditionAttributeResponseHeaders::GetTests(
395 const DictionaryValue* tests,
396 std::string* error) {
397 ScopedVector<const MatchTest> name;
398 ScopedVector<const MatchTest> value;
399
400 for (DictionaryValue::key_iterator key = tests->begin_keys();
401 key != tests->end_keys();
402 ++key) {
403 bool is_name = false; // Is this test for header name?
404 MatchType match_type;
405 if (*key == keys::kNamePrefixKey) {
406 is_name = true;
407 match_type = kPrefix;
408 } else if (*key == keys::kNameSuffixKey) {
409 is_name = true;
410 match_type = kSuffix;
411 } else if (*key == keys::kNameContainsKey) {
412 is_name = true;
413 match_type = kContains;
414 } else if (*key == keys::kNameEqualsKey) {
415 is_name = true;
416 match_type = kEquals;
417 } else if (*key == keys::kValuePrefixKey) {
418 match_type = kPrefix;
419 } else if (*key == keys::kValueSuffixKey) {
420 match_type = kSuffix;
421 } else if (*key == keys::kValueContainsKey) {
422 match_type = kContains;
423 } else if (*key == keys::kValueEqualsKey) {
424 match_type = kEquals;
425 } else {
426 NOTREACHED(); // JSON schema type checking should prevent this.
427 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key);
428 return scoped_ptr<const TestGroup>(NULL);
429 }
430 const Value* content = NULL;
431 // This should not fire, we already checked that |key| is there.
432 CHECK(tests->Get(*key, &content));
433
434 switch (content->GetType()) {
435 case Value::TYPE_LIST: {
436 const ListValue* list = NULL;
437 CHECK(content->GetAsList(&list));
438 for (ListValue::const_iterator it = list->begin();
439 it != list->end(); ++it) {
440 ScopedVector<const MatchTest>* tests = is_name ? &name : &value;
441 tests->push_back(GetMatchTest(*it, is_name, match_type).release());
442 }
443 break;
444 }
445 case Value::TYPE_STRING: {
446 ScopedVector<const MatchTest>* tests = is_name ? &name : &value;
447 tests->push_back(GetMatchTest(content, is_name, match_type).release());
448 break;
449 }
450 default: {
451 NOTREACHED(); // JSON schema type checking should prevent this.
452 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key);
453 return scoped_ptr<const TestGroup>(NULL);
454 }
455 }
456 }
457
458 return scoped_ptr<const TestGroup>(new TestGroup(&name, &value));
battre 2012/08/24 16:24:41 FYI: we can now use make_scoped_ptr as well, i.e.
vabr (Chromium) 2012/08/27 12:11:50 Thanks, I did not know that. As you only wrote "FY
459 }
460
461 // static
462 scoped_ptr<const WebRequestConditionAttributeResponseHeaders::MatchTest>
463 WebRequestConditionAttributeResponseHeaders::GetMatchTest(
464 const Value* content, bool is_name_test, MatchType match_type) {
465 scoped_ptr<std::string> str(new std::string);
466
467 CHECK(content->GetAsString(str.get()));
468 if (is_name_test) // Header names are case-insensitive.
469 StringToLowerASCII(str.get());
470
471 return scoped_ptr<const MatchTest>(
472 new MatchTest(str.PassAs<const std::string>(), match_type));
473 }
474
222 } // namespace extensions 475 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698