Chromium Code Reviews| 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 |