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::StringMatchTest::StringMatchTest( |
| 240 const std::string& data, |
| 241 MatchType type) |
| 242 : data_(data), |
| 243 type_(type) {} |
| 244 |
| 245 WebRequestConditionAttributeResponseHeaders::StringMatchTest::~StringMatchTest() |
| 246 {} |
| 247 |
| 248 WebRequestConditionAttributeResponseHeaders::HeaderMatchTest::HeaderMatchTest( |
| 249 ScopedVector<const StringMatchTest>* name, |
| 250 ScopedVector<const StringMatchTest>* value) |
| 251 : name_(name->Pass()), |
| 252 value_(value->Pass()) {} |
| 253 |
| 254 WebRequestConditionAttributeResponseHeaders::HeaderMatchTest::~HeaderMatchTest() |
| 255 {} |
| 256 |
| 257 WebRequestConditionAttributeResponseHeaders:: |
| 258 WebRequestConditionAttributeResponseHeaders( |
| 259 bool positive_test, ScopedVector<const HeaderMatchTest>* tests) |
| 260 : tests_(tests->Pass()), |
| 261 positive_test_(positive_test) {} |
| 262 |
| 263 WebRequestConditionAttributeResponseHeaders:: |
| 264 ~WebRequestConditionAttributeResponseHeaders() {} |
| 265 |
| 266 // static |
| 267 bool WebRequestConditionAttributeResponseHeaders::IsMatchingType( |
| 268 const std::string& instance_type) { |
| 269 return instance_type == keys::kResponseHeadersKey || |
| 270 instance_type == keys::kExcludeResponseHeadersKey; |
| 271 } |
| 272 |
| 273 // static |
| 274 scoped_ptr<WebRequestConditionAttribute> |
| 275 WebRequestConditionAttributeResponseHeaders::Create( |
| 276 const std::string& name, |
| 277 const base::Value* value, |
| 278 std::string* error) { |
| 279 DCHECK(IsMatchingType(name)); |
| 280 |
| 281 const ListValue* value_as_list = NULL; |
| 282 if (!value->GetAsList(&value_as_list)) { |
| 283 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); |
| 284 return scoped_ptr<WebRequestConditionAttribute>(NULL); |
| 285 } |
| 286 |
| 287 ScopedVector<const HeaderMatchTest> header_tests; |
| 288 for (ListValue::const_iterator it = value_as_list->begin(); |
| 289 it != value_as_list->end(); ++it) { |
| 290 const DictionaryValue* tests = NULL; |
| 291 if (!(*it)->GetAsDictionary(&tests)) { |
| 292 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); |
| 293 return scoped_ptr<WebRequestConditionAttribute>(NULL); |
| 294 } |
| 295 |
| 296 scoped_ptr<const HeaderMatchTest> header_test(CreateTests(tests, error)); |
| 297 if (header_test.get() == NULL) |
| 298 return scoped_ptr<WebRequestConditionAttribute>(NULL); |
| 299 header_tests.push_back(header_test.release()); |
| 300 } |
| 301 |
| 302 scoped_ptr<WebRequestConditionAttributeResponseHeaders> result; |
| 303 result.reset(new WebRequestConditionAttributeResponseHeaders( |
| 304 name == keys::kResponseHeadersKey, &header_tests)); |
| 305 |
| 306 return result.PassAs<WebRequestConditionAttribute>(); |
| 307 } |
| 308 |
| 309 int WebRequestConditionAttributeResponseHeaders::GetStages() const { |
| 310 return ON_HEADERS_RECEIVED; |
| 311 } |
| 312 |
| 313 bool WebRequestConditionAttributeResponseHeaders::IsFulfilled( |
| 314 const WebRequestRule::RequestData& request_data) const { |
| 315 if (!(request_data.stage & GetStages())) |
| 316 return false; |
| 317 |
| 318 const net::HttpResponseHeaders* headers = |
| 319 request_data.original_response_headers; |
| 320 if (headers == NULL) { |
| 321 // Each header of an empty set satisfies (the negation of) everything; |
| 322 // OTOH, there is no header to satisfy even the most permissive test. |
| 323 return !positive_test_; |
| 324 } |
| 325 |
| 326 // Has some header already passed some header test? |
| 327 bool header_found = false; |
| 328 |
| 329 for (size_t i = 0; !header_found && i < tests_.size(); ++i) { |
| 330 std::string name; |
| 331 std::string value; |
| 332 |
| 333 void* iter = NULL; |
| 334 while (!header_found && |
| 335 headers->EnumerateHeaderLines(&iter, &name, &value)) { |
| 336 StringToLowerASCII(&name); // Header names are case-insensitive. |
| 337 header_found |= tests_[i]->Matches(name, value); |
| 338 } |
| 339 } |
| 340 |
| 341 return (positive_test_ ? header_found : !header_found); |
| 342 } |
| 343 |
| 344 WebRequestConditionAttribute::Type |
| 345 WebRequestConditionAttributeResponseHeaders::GetType() const { |
| 346 return CONDITION_REQUEST_HEADERS; |
| 347 } |
| 348 |
| 349 bool WebRequestConditionAttributeResponseHeaders::StringMatchTest::Matches( |
| 350 const std::string& str) const { |
| 351 switch (type_) { |
| 352 case kPrefix: |
| 353 return StartsWithASCII(str, data_, true /*case_sensitive*/); |
| 354 case kSuffix: |
| 355 return EndsWith(str, data_, true /*case_sensitive*/); |
| 356 case kEquals: |
| 357 return data_ == str; |
| 358 case kContains: |
| 359 return str.find(data_) != std::string::npos; |
| 360 } |
| 361 // We never get past the "switch", but the compiler worries about no return. |
| 362 NOTREACHED(); |
| 363 return false; |
| 364 } |
| 365 |
| 366 bool WebRequestConditionAttributeResponseHeaders::HeaderMatchTest::Matches( |
| 367 const std::string& name, |
| 368 const std::string& value) const { |
| 369 for (size_t i = 0; i < name_.size(); ++i) { |
| 370 if (!name_[i]->Matches(name)) |
| 371 return false; |
| 372 } |
| 373 |
| 374 for (size_t i = 0; i < value_.size(); ++i) { |
| 375 if (!value_[i]->Matches(value)) |
| 376 return false; |
| 377 } |
| 378 |
| 379 return true; |
| 380 } |
| 381 |
| 382 |
| 383 // static |
| 384 scoped_ptr<const WebRequestConditionAttributeResponseHeaders::HeaderMatchTest> |
| 385 WebRequestConditionAttributeResponseHeaders::CreateTests( |
| 386 const DictionaryValue* tests, |
| 387 std::string* error) { |
| 388 ScopedVector<const StringMatchTest> name; |
| 389 ScopedVector<const StringMatchTest> value; |
| 390 |
| 391 for (DictionaryValue::key_iterator key = tests->begin_keys(); |
| 392 key != tests->end_keys(); |
| 393 ++key) { |
| 394 bool is_name = false; // Is this test for header name? |
| 395 MatchType match_type; |
| 396 if (*key == keys::kNamePrefixKey) { |
| 397 is_name = true; |
| 398 match_type = kPrefix; |
| 399 } else if (*key == keys::kNameSuffixKey) { |
| 400 is_name = true; |
| 401 match_type = kSuffix; |
| 402 } else if (*key == keys::kNameContainsKey) { |
| 403 is_name = true; |
| 404 match_type = kContains; |
| 405 } else if (*key == keys::kNameEqualsKey) { |
| 406 is_name = true; |
| 407 match_type = kEquals; |
| 408 } else if (*key == keys::kValuePrefixKey) { |
| 409 match_type = kPrefix; |
| 410 } else if (*key == keys::kValueSuffixKey) { |
| 411 match_type = kSuffix; |
| 412 } else if (*key == keys::kValueContainsKey) { |
| 413 match_type = kContains; |
| 414 } else if (*key == keys::kValueEqualsKey) { |
| 415 match_type = kEquals; |
| 416 } else { |
| 417 NOTREACHED(); // JSON schema type checking should prevent this. |
| 418 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); |
| 419 return scoped_ptr<const HeaderMatchTest>(NULL); |
| 420 } |
| 421 const Value* content = NULL; |
| 422 // This should not fire, we already checked that |key| is there. |
| 423 CHECK(tests->Get(*key, &content)); |
| 424 |
| 425 switch (content->GetType()) { |
| 426 case Value::TYPE_LIST: { |
| 427 const ListValue* list = NULL; |
| 428 CHECK(content->GetAsList(&list)); |
| 429 for (ListValue::const_iterator it = list->begin(); |
| 430 it != list->end(); ++it) { |
| 431 ScopedVector<const StringMatchTest>* tests = is_name ? &name : &value; |
| 432 tests->push_back(CreateMatchTest(*it, is_name, match_type).release()); |
| 433 } |
| 434 break; |
| 435 } |
| 436 case Value::TYPE_STRING: { |
| 437 ScopedVector<const StringMatchTest>* tests = is_name ? &name : &value; |
| 438 tests->push_back( |
| 439 CreateMatchTest(content, is_name, match_type).release()); |
| 440 break; |
| 441 } |
| 442 default: { |
| 443 NOTREACHED(); // JSON schema type checking should prevent this. |
| 444 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); |
| 445 return scoped_ptr<const HeaderMatchTest>(NULL); |
| 446 } |
| 447 } |
| 448 } |
| 449 |
| 450 return scoped_ptr<const HeaderMatchTest>(new HeaderMatchTest(&name, &value)); |
| 451 } |
| 452 |
| 453 // static |
| 454 scoped_ptr<const WebRequestConditionAttributeResponseHeaders::StringMatchTest> |
| 455 WebRequestConditionAttributeResponseHeaders::CreateMatchTest( |
| 456 const Value* content, bool is_name_test, MatchType match_type) { |
| 457 std::string str; |
| 458 |
| 459 CHECK(content->GetAsString(&str)); |
| 460 if (is_name_test) // Header names are case-insensitive. |
| 461 StringToLowerASCII(&str); |
| 462 |
| 463 return scoped_ptr<const StringMatchTest>( |
| 464 new StringMatchTest(str, match_type)); |
| 465 } |
| 466 |
222 } // namespace extensions | 467 } // namespace extensions |
OLD | NEW |