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 | 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 Loading... | |
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 Loading... | |
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 const std::string& data, | |
241 MatchType type) | |
242 : data_(data), | |
243 type_(type) {} | |
244 | |
245 WebRequestConditionAttributeResponseHeaders::MatchTest::~MatchTest() {} | |
246 | |
247 WebRequestConditionAttributeResponseHeaders::TestGroup::TestGroup( | |
248 ScopedVector<const MatchTest>* name, | |
249 ScopedVector<const MatchTest>* value) | |
250 : name_(name->Pass()), | |
251 value_(value->Pass()) {} | |
252 | |
253 WebRequestConditionAttributeResponseHeaders::TestGroup::~TestGroup() {} | |
254 | |
255 WebRequestConditionAttributeResponseHeaders:: | |
256 WebRequestConditionAttributeResponseHeaders( | |
257 bool positive_test, ScopedVector<const TestGroup>* tests) | |
258 : tests_(tests->Pass()), | |
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(CreateTests(tests, error)); | |
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, &groups)); | |
303 | |
304 return result.PassAs<WebRequestConditionAttribute>(); | |
305 } | |
306 | |
307 int WebRequestConditionAttributeResponseHeaders::GetStages() const { | |
308 return ON_HEADERS_RECEIVED; | |
309 } | |
310 | |
311 bool WebRequestConditionAttributeResponseHeaders::IsFulfilled( | |
312 const WebRequestRule::RequestData& request_data) const { | |
313 if (!(request_data.stage & GetStages())) | |
314 return false; | |
315 | |
316 const net::HttpResponseHeaders* headers = | |
317 request_data.original_response_headers; | |
318 if (headers == NULL) { | |
319 // Each header of an empty set satisfies (the negation of) everything; | |
320 // OTOH, there is no header to satisfy even the most permissive test. | |
321 return !positive_test_; | |
322 } | |
323 | |
324 // Has some header already passed some test group? | |
325 bool header_found = false; | |
326 | |
327 for (size_t i = 0; !header_found && i < tests_.size(); ++i) { | |
328 std::string name; | |
329 std::string value; | |
330 | |
331 for (void* iter = NULL; | |
332 !header_found && headers->EnumerateHeaderLines(&iter, &name, &value); | |
333 /* No increment here, this is done inside EnumerateHeaderLines. */) { | |
battre
2012/08/27 17:16:25
opt:
void* iter = NULL;
while (!header_found && he
vabr (Chromium)
2012/08/28 17:59:09
Done.
| |
334 StringToLowerASCII(&name); // Header names are case-insensitive. | |
335 header_found |= tests_[i]->Matches(name, value); | |
336 } | |
337 } | |
338 | |
339 return (positive_test_ ? header_found : !header_found); | |
340 } | |
341 | |
342 WebRequestConditionAttribute::Type | |
343 WebRequestConditionAttributeResponseHeaders::GetType() const { | |
344 return CONDITION_REQUEST_HEADERS; | |
345 } | |
346 | |
347 bool WebRequestConditionAttributeResponseHeaders::MatchTest::Matches( | |
348 const std::string& str) const { | |
349 switch (type_) { | |
350 case kPrefix: | |
351 return StartsWithASCII(str, data_, true /*case_sensitive*/); | |
352 case kSuffix: | |
353 return EndsWith(str, data_, true /*case_sensitive*/); | |
354 case kEquals: | |
355 return data_ == str; | |
356 case kContains: | |
357 return str.find(data_) != std::string::npos; | |
358 } | |
359 // We never get past the "switch", but the compiler worries about no return. | |
360 NOTREACHED(); | |
361 return false; | |
362 } | |
363 | |
364 bool WebRequestConditionAttributeResponseHeaders::TestGroup::Matches( | |
365 const std::string& name, | |
366 const std::string& value) const { | |
367 for (size_t i = 0; i < name_.size(); ++i) { | |
368 if (!name_[i]->Matches(name)) | |
369 return false; | |
370 } | |
371 | |
372 for (size_t i = 0; i < value_.size(); ++i) { | |
373 if (!value_[i]->Matches(value)) | |
374 return false; | |
375 } | |
376 | |
377 return true; | |
378 } | |
379 | |
380 | |
381 // static | |
382 scoped_ptr<const WebRequestConditionAttributeResponseHeaders::TestGroup> | |
383 WebRequestConditionAttributeResponseHeaders::CreateTests( | |
384 const DictionaryValue* tests, | |
385 std::string* error) { | |
386 ScopedVector<const MatchTest> name; | |
387 ScopedVector<const MatchTest> value; | |
388 | |
389 for (DictionaryValue::key_iterator key = tests->begin_keys(); | |
390 key != tests->end_keys(); | |
391 ++key) { | |
392 bool is_name = false; // Is this test for header name? | |
393 MatchType match_type; | |
394 if (*key == keys::kNamePrefixKey) { | |
395 is_name = true; | |
396 match_type = kPrefix; | |
397 } else if (*key == keys::kNameSuffixKey) { | |
398 is_name = true; | |
399 match_type = kSuffix; | |
400 } else if (*key == keys::kNameContainsKey) { | |
401 is_name = true; | |
402 match_type = kContains; | |
403 } else if (*key == keys::kNameEqualsKey) { | |
404 is_name = true; | |
405 match_type = kEquals; | |
406 } else if (*key == keys::kValuePrefixKey) { | |
407 match_type = kPrefix; | |
408 } else if (*key == keys::kValueSuffixKey) { | |
409 match_type = kSuffix; | |
410 } else if (*key == keys::kValueContainsKey) { | |
411 match_type = kContains; | |
412 } else if (*key == keys::kValueEqualsKey) { | |
413 match_type = kEquals; | |
414 } else { | |
415 NOTREACHED(); // JSON schema type checking should prevent this. | |
416 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); | |
417 return scoped_ptr<const TestGroup>(NULL); | |
418 } | |
419 const Value* content = NULL; | |
420 // This should not fire, we already checked that |key| is there. | |
421 CHECK(tests->Get(*key, &content)); | |
422 | |
423 switch (content->GetType()) { | |
424 case Value::TYPE_LIST: { | |
425 const ListValue* list = NULL; | |
426 CHECK(content->GetAsList(&list)); | |
427 for (ListValue::const_iterator it = list->begin(); | |
428 it != list->end(); ++it) { | |
429 ScopedVector<const MatchTest>* tests = is_name ? &name : &value; | |
430 tests->push_back(CreateMatchTest(*it, is_name, match_type).release()); | |
431 } | |
432 break; | |
433 } | |
434 case Value::TYPE_STRING: { | |
435 ScopedVector<const MatchTest>* tests = is_name ? &name : &value; | |
436 tests->push_back( | |
437 CreateMatchTest(content, is_name, match_type).release()); | |
438 break; | |
439 } | |
440 default: { | |
441 NOTREACHED(); // JSON schema type checking should prevent this. | |
442 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); | |
443 return scoped_ptr<const TestGroup>(NULL); | |
444 } | |
445 } | |
446 } | |
447 | |
448 return scoped_ptr<const TestGroup>(new TestGroup(&name, &value)); | |
449 } | |
450 | |
451 // static | |
452 scoped_ptr<const WebRequestConditionAttributeResponseHeaders::MatchTest> | |
453 WebRequestConditionAttributeResponseHeaders::CreateMatchTest( | |
454 const Value* content, bool is_name_test, MatchType match_type) { | |
455 std::string str; | |
456 | |
457 CHECK(content->GetAsString(&str)); | |
458 if (is_name_test) // Header names are case-insensitive. | |
459 StringToLowerASCII(&str); | |
460 | |
461 return scoped_ptr<const MatchTest>( | |
462 new MatchTest(str, match_type)); | |
battre
2012/08/27 17:16:25
nit: single line
vabr (Chromium)
2012/08/28 17:59:09
Does not fit any more after renaming MatchTest to
| |
463 } | |
464 | |
222 } // namespace extensions | 465 } // namespace extensions |
OLD | NEW |