| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condit
ion_attribute.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/lazy_instance.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/strings/string_util.h" | |
| 12 #include "base/strings/stringprintf.h" | |
| 13 #include "base/values.h" | |
| 14 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condit
ion.h" | |
| 15 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h" | |
| 16 #include "content/public/browser/resource_request_info.h" | |
| 17 #include "extensions/browser/api/declarative/deduping_factory.h" | |
| 18 #include "extensions/browser/api/declarative_webrequest/request_stage.h" | |
| 19 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h" | |
| 20 #include "extensions/common/error_utils.h" | |
| 21 #include "net/base/net_errors.h" | |
| 22 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | |
| 23 #include "net/base/static_cookie_policy.h" | |
| 24 #include "net/http/http_request_headers.h" | |
| 25 #include "net/http/http_util.h" | |
| 26 #include "net/url_request/url_request.h" | |
| 27 | |
| 28 using base::CaseInsensitiveCompareASCII; | |
| 29 using base::DictionaryValue; | |
| 30 using base::ListValue; | |
| 31 using base::StringValue; | |
| 32 using base::Value; | |
| 33 using content::ResourceType; | |
| 34 | |
| 35 namespace helpers = extension_web_request_api_helpers; | |
| 36 namespace keys = extensions::declarative_webrequest_constants; | |
| 37 | |
| 38 namespace extensions { | |
| 39 | |
| 40 namespace { | |
| 41 // Error messages. | |
| 42 const char kInvalidValue[] = "Condition '*' has an invalid value"; | |
| 43 | |
| 44 struct WebRequestConditionAttributeFactory { | |
| 45 DedupingFactory<WebRequestConditionAttribute> factory; | |
| 46 | |
| 47 WebRequestConditionAttributeFactory() : factory(5) { | |
| 48 factory.RegisterFactoryMethod( | |
| 49 keys::kResourceTypeKey, | |
| 50 DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED, | |
| 51 &WebRequestConditionAttributeResourceType::Create); | |
| 52 | |
| 53 factory.RegisterFactoryMethod( | |
| 54 keys::kContentTypeKey, | |
| 55 DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED, | |
| 56 &WebRequestConditionAttributeContentType::Create); | |
| 57 factory.RegisterFactoryMethod( | |
| 58 keys::kExcludeContentTypeKey, | |
| 59 DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED, | |
| 60 &WebRequestConditionAttributeContentType::Create); | |
| 61 | |
| 62 factory.RegisterFactoryMethod( | |
| 63 keys::kRequestHeadersKey, | |
| 64 DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED, | |
| 65 &WebRequestConditionAttributeRequestHeaders::Create); | |
| 66 factory.RegisterFactoryMethod( | |
| 67 keys::kExcludeRequestHeadersKey, | |
| 68 DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED, | |
| 69 &WebRequestConditionAttributeRequestHeaders::Create); | |
| 70 | |
| 71 factory.RegisterFactoryMethod( | |
| 72 keys::kResponseHeadersKey, | |
| 73 DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED, | |
| 74 &WebRequestConditionAttributeResponseHeaders::Create); | |
| 75 factory.RegisterFactoryMethod( | |
| 76 keys::kExcludeResponseHeadersKey, | |
| 77 DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED, | |
| 78 &WebRequestConditionAttributeResponseHeaders::Create); | |
| 79 | |
| 80 factory.RegisterFactoryMethod( | |
| 81 keys::kThirdPartyKey, | |
| 82 DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED, | |
| 83 &WebRequestConditionAttributeThirdParty::Create); | |
| 84 | |
| 85 factory.RegisterFactoryMethod( | |
| 86 keys::kStagesKey, | |
| 87 DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED, | |
| 88 &WebRequestConditionAttributeStages::Create); | |
| 89 } | |
| 90 }; | |
| 91 | |
| 92 base::LazyInstance<WebRequestConditionAttributeFactory>::Leaky | |
| 93 g_web_request_condition_attribute_factory = LAZY_INSTANCE_INITIALIZER; | |
| 94 | |
| 95 } // namespace | |
| 96 | |
| 97 // | |
| 98 // WebRequestConditionAttribute | |
| 99 // | |
| 100 | |
| 101 WebRequestConditionAttribute::WebRequestConditionAttribute() {} | |
| 102 | |
| 103 WebRequestConditionAttribute::~WebRequestConditionAttribute() {} | |
| 104 | |
| 105 bool WebRequestConditionAttribute::Equals( | |
| 106 const WebRequestConditionAttribute* other) const { | |
| 107 return GetType() == other->GetType(); | |
| 108 } | |
| 109 | |
| 110 // static | |
| 111 scoped_refptr<const WebRequestConditionAttribute> | |
| 112 WebRequestConditionAttribute::Create( | |
| 113 const std::string& name, | |
| 114 const base::Value* value, | |
| 115 std::string* error) { | |
| 116 CHECK(value != NULL && error != NULL); | |
| 117 bool bad_message = false; | |
| 118 return g_web_request_condition_attribute_factory.Get().factory.Instantiate( | |
| 119 name, value, error, &bad_message); | |
| 120 } | |
| 121 | |
| 122 // | |
| 123 // WebRequestConditionAttributeResourceType | |
| 124 // | |
| 125 | |
| 126 WebRequestConditionAttributeResourceType:: | |
| 127 WebRequestConditionAttributeResourceType( | |
| 128 const std::vector<ResourceType>& types) | |
| 129 : types_(types) {} | |
| 130 | |
| 131 WebRequestConditionAttributeResourceType:: | |
| 132 ~WebRequestConditionAttributeResourceType() {} | |
| 133 | |
| 134 // static | |
| 135 scoped_refptr<const WebRequestConditionAttribute> | |
| 136 WebRequestConditionAttributeResourceType::Create( | |
| 137 const std::string& instance_type, | |
| 138 const base::Value* value, | |
| 139 std::string* error, | |
| 140 bool* bad_message) { | |
| 141 DCHECK(instance_type == keys::kResourceTypeKey); | |
| 142 const base::ListValue* value_as_list = NULL; | |
| 143 if (!value->GetAsList(&value_as_list)) { | |
| 144 *error = ErrorUtils::FormatErrorMessage(kInvalidValue, | |
| 145 keys::kResourceTypeKey); | |
| 146 return scoped_refptr<const WebRequestConditionAttribute>(NULL); | |
| 147 } | |
| 148 | |
| 149 size_t number_types = value_as_list->GetSize(); | |
| 150 | |
| 151 std::vector<ResourceType> passed_types; | |
| 152 passed_types.reserve(number_types); | |
| 153 for (size_t i = 0; i < number_types; ++i) { | |
| 154 std::string resource_type_string; | |
| 155 ResourceType type = content::RESOURCE_TYPE_LAST_TYPE; | |
| 156 if (!value_as_list->GetString(i, &resource_type_string) || | |
| 157 !helpers::ParseResourceType(resource_type_string, &type)) { | |
| 158 *error = ErrorUtils::FormatErrorMessage(kInvalidValue, | |
| 159 keys::kResourceTypeKey); | |
| 160 return scoped_refptr<const WebRequestConditionAttribute>(NULL); | |
| 161 } | |
| 162 passed_types.push_back(type); | |
| 163 } | |
| 164 | |
| 165 return scoped_refptr<const WebRequestConditionAttribute>( | |
| 166 new WebRequestConditionAttributeResourceType(passed_types)); | |
| 167 } | |
| 168 | |
| 169 int WebRequestConditionAttributeResourceType::GetStages() const { | |
| 170 return ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | ON_SEND_HEADERS | | |
| 171 ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED | ON_BEFORE_REDIRECT | | |
| 172 ON_RESPONSE_STARTED | ON_COMPLETED | ON_ERROR; | |
| 173 } | |
| 174 | |
| 175 bool WebRequestConditionAttributeResourceType::IsFulfilled( | |
| 176 const WebRequestData& request_data) const { | |
| 177 if (!(request_data.stage & GetStages())) | |
| 178 return false; | |
| 179 const content::ResourceRequestInfo* info = | |
| 180 content::ResourceRequestInfo::ForRequest(request_data.request); | |
| 181 if (!info) | |
| 182 return false; | |
| 183 return std::find(types_.begin(), types_.end(), info->GetResourceType()) != | |
| 184 types_.end(); | |
| 185 } | |
| 186 | |
| 187 WebRequestConditionAttribute::Type | |
| 188 WebRequestConditionAttributeResourceType::GetType() const { | |
| 189 return CONDITION_RESOURCE_TYPE; | |
| 190 } | |
| 191 | |
| 192 std::string WebRequestConditionAttributeResourceType::GetName() const { | |
| 193 return keys::kResourceTypeKey; | |
| 194 } | |
| 195 | |
| 196 bool WebRequestConditionAttributeResourceType::Equals( | |
| 197 const WebRequestConditionAttribute* other) const { | |
| 198 if (!WebRequestConditionAttribute::Equals(other)) | |
| 199 return false; | |
| 200 const WebRequestConditionAttributeResourceType* casted_other = | |
| 201 static_cast<const WebRequestConditionAttributeResourceType*>(other); | |
| 202 return types_ == casted_other->types_; | |
| 203 } | |
| 204 | |
| 205 // | |
| 206 // WebRequestConditionAttributeContentType | |
| 207 // | |
| 208 | |
| 209 WebRequestConditionAttributeContentType:: | |
| 210 WebRequestConditionAttributeContentType( | |
| 211 const std::vector<std::string>& content_types, | |
| 212 bool inclusive) | |
| 213 : content_types_(content_types), | |
| 214 inclusive_(inclusive) {} | |
| 215 | |
| 216 WebRequestConditionAttributeContentType:: | |
| 217 ~WebRequestConditionAttributeContentType() {} | |
| 218 | |
| 219 // static | |
| 220 scoped_refptr<const WebRequestConditionAttribute> | |
| 221 WebRequestConditionAttributeContentType::Create( | |
| 222 const std::string& name, | |
| 223 const base::Value* value, | |
| 224 std::string* error, | |
| 225 bool* bad_message) { | |
| 226 DCHECK(name == keys::kContentTypeKey || name == keys::kExcludeContentTypeKey); | |
| 227 | |
| 228 const base::ListValue* value_as_list = NULL; | |
| 229 if (!value->GetAsList(&value_as_list)) { | |
| 230 *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
| 231 return scoped_refptr<const WebRequestConditionAttribute>(NULL); | |
| 232 } | |
| 233 std::vector<std::string> content_types; | |
| 234 for (base::ListValue::const_iterator it = value_as_list->begin(); | |
| 235 it != value_as_list->end(); ++it) { | |
| 236 std::string content_type; | |
| 237 if (!(*it)->GetAsString(&content_type)) { | |
| 238 *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
| 239 return scoped_refptr<const WebRequestConditionAttribute>(NULL); | |
| 240 } | |
| 241 content_types.push_back(content_type); | |
| 242 } | |
| 243 | |
| 244 return scoped_refptr<const WebRequestConditionAttribute>( | |
| 245 new WebRequestConditionAttributeContentType( | |
| 246 content_types, name == keys::kContentTypeKey)); | |
| 247 } | |
| 248 | |
| 249 int WebRequestConditionAttributeContentType::GetStages() const { | |
| 250 return ON_HEADERS_RECEIVED; | |
| 251 } | |
| 252 | |
| 253 bool WebRequestConditionAttributeContentType::IsFulfilled( | |
| 254 const WebRequestData& request_data) const { | |
| 255 if (!(request_data.stage & GetStages())) | |
| 256 return false; | |
| 257 std::string content_type; | |
| 258 request_data.original_response_headers->GetNormalizedHeader( | |
| 259 net::HttpRequestHeaders::kContentType, &content_type); | |
| 260 std::string mime_type; | |
| 261 std::string charset; | |
| 262 bool had_charset = false; | |
| 263 net::HttpUtil::ParseContentType( | |
| 264 content_type, &mime_type, &charset, &had_charset, NULL); | |
| 265 | |
| 266 if (inclusive_) { | |
| 267 return std::find(content_types_.begin(), content_types_.end(), | |
| 268 mime_type) != content_types_.end(); | |
| 269 } else { | |
| 270 return std::find(content_types_.begin(), content_types_.end(), | |
| 271 mime_type) == content_types_.end(); | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 WebRequestConditionAttribute::Type | |
| 276 WebRequestConditionAttributeContentType::GetType() const { | |
| 277 return CONDITION_CONTENT_TYPE; | |
| 278 } | |
| 279 | |
| 280 std::string WebRequestConditionAttributeContentType::GetName() const { | |
| 281 return (inclusive_ ? keys::kContentTypeKey : keys::kExcludeContentTypeKey); | |
| 282 } | |
| 283 | |
| 284 bool WebRequestConditionAttributeContentType::Equals( | |
| 285 const WebRequestConditionAttribute* other) const { | |
| 286 if (!WebRequestConditionAttribute::Equals(other)) | |
| 287 return false; | |
| 288 const WebRequestConditionAttributeContentType* casted_other = | |
| 289 static_cast<const WebRequestConditionAttributeContentType*>(other); | |
| 290 return content_types_ == casted_other->content_types_ && | |
| 291 inclusive_ == casted_other->inclusive_; | |
| 292 } | |
| 293 | |
| 294 // Manages a set of tests to be applied to name-value pairs representing | |
| 295 // headers. This is a helper class to header-related condition attributes. | |
| 296 // It contains a set of test groups. A name-value pair satisfies the whole | |
| 297 // set of test groups iff it passes at least one test group. | |
| 298 class HeaderMatcher { | |
| 299 public: | |
| 300 ~HeaderMatcher(); | |
| 301 | |
| 302 // Creates an instance based on a list |tests| of test groups, encoded as | |
| 303 // dictionaries of the type declarativeWebRequest.HeaderFilter (see | |
| 304 // declarative_web_request.json). | |
| 305 static scoped_ptr<const HeaderMatcher> Create(const base::ListValue* tests); | |
| 306 | |
| 307 // Does |this| match the header "|name|: |value|"? | |
| 308 bool TestNameValue(const std::string& name, const std::string& value) const; | |
| 309 | |
| 310 private: | |
| 311 // Represents a single string-matching test. | |
| 312 class StringMatchTest { | |
| 313 public: | |
| 314 enum MatchType { kPrefix, kSuffix, kEquals, kContains }; | |
| 315 | |
| 316 // |data| is the pattern to be matched in the position given by |type|. | |
| 317 // Note that |data| must point to a StringValue object. | |
| 318 static scoped_ptr<StringMatchTest> Create(const base::Value* data, | |
| 319 MatchType type, | |
| 320 bool case_sensitive); | |
| 321 ~StringMatchTest(); | |
| 322 | |
| 323 // Does |str| pass |this| StringMatchTest? | |
| 324 bool Matches(const std::string& str) const; | |
| 325 | |
| 326 private: | |
| 327 StringMatchTest(const std::string& data, | |
| 328 MatchType type, | |
| 329 bool case_sensitive); | |
| 330 | |
| 331 const std::string data_; | |
| 332 const MatchType type_; | |
| 333 const bool case_sensitive_; | |
| 334 DISALLOW_COPY_AND_ASSIGN(StringMatchTest); | |
| 335 }; | |
| 336 | |
| 337 // Represents a test group -- a set of string matching tests to be applied to | |
| 338 // both the header name and value. | |
| 339 class HeaderMatchTest { | |
| 340 public: | |
| 341 ~HeaderMatchTest(); | |
| 342 | |
| 343 // Gets the test group description in |tests| and creates the corresponding | |
| 344 // HeaderMatchTest. On failure returns NULL. | |
| 345 static scoped_ptr<const HeaderMatchTest> Create( | |
| 346 const base::DictionaryValue* tests); | |
| 347 | |
| 348 // Does the header "|name|: |value|" match all tests in |this|? | |
| 349 bool Matches(const std::string& name, const std::string& value) const; | |
| 350 | |
| 351 private: | |
| 352 // Takes ownership of the content of both |name_match| and |value_match|. | |
| 353 HeaderMatchTest(ScopedVector<const StringMatchTest>* name_match, | |
| 354 ScopedVector<const StringMatchTest>* value_match); | |
| 355 | |
| 356 // Tests to be passed by a header's name. | |
| 357 const ScopedVector<const StringMatchTest> name_match_; | |
| 358 // Tests to be passed by a header's value. | |
| 359 const ScopedVector<const StringMatchTest> value_match_; | |
| 360 DISALLOW_COPY_AND_ASSIGN(HeaderMatchTest); | |
| 361 }; | |
| 362 | |
| 363 explicit HeaderMatcher(ScopedVector<const HeaderMatchTest>* tests); | |
| 364 | |
| 365 const ScopedVector<const HeaderMatchTest> tests_; | |
| 366 | |
| 367 DISALLOW_COPY_AND_ASSIGN(HeaderMatcher); | |
| 368 }; | |
| 369 | |
| 370 // HeaderMatcher implementation. | |
| 371 | |
| 372 HeaderMatcher::~HeaderMatcher() {} | |
| 373 | |
| 374 // static | |
| 375 scoped_ptr<const HeaderMatcher> HeaderMatcher::Create( | |
| 376 const base::ListValue* tests) { | |
| 377 ScopedVector<const HeaderMatchTest> header_tests; | |
| 378 for (base::ListValue::const_iterator it = tests->begin(); | |
| 379 it != tests->end(); ++it) { | |
| 380 const base::DictionaryValue* tests = NULL; | |
| 381 if (!(*it)->GetAsDictionary(&tests)) | |
| 382 return scoped_ptr<const HeaderMatcher>(); | |
| 383 | |
| 384 scoped_ptr<const HeaderMatchTest> header_test( | |
| 385 HeaderMatchTest::Create(tests)); | |
| 386 if (header_test.get() == NULL) | |
| 387 return scoped_ptr<const HeaderMatcher>(); | |
| 388 header_tests.push_back(header_test.release()); | |
| 389 } | |
| 390 | |
| 391 return scoped_ptr<const HeaderMatcher>(new HeaderMatcher(&header_tests)); | |
| 392 } | |
| 393 | |
| 394 bool HeaderMatcher::TestNameValue(const std::string& name, | |
| 395 const std::string& value) const { | |
| 396 for (size_t i = 0; i < tests_.size(); ++i) { | |
| 397 if (tests_[i]->Matches(name, value)) | |
| 398 return true; | |
| 399 } | |
| 400 return false; | |
| 401 } | |
| 402 | |
| 403 HeaderMatcher::HeaderMatcher(ScopedVector<const HeaderMatchTest>* tests) | |
| 404 : tests_(tests->Pass()) {} | |
| 405 | |
| 406 // HeaderMatcher::StringMatchTest implementation. | |
| 407 | |
| 408 // static | |
| 409 scoped_ptr<HeaderMatcher::StringMatchTest> | |
| 410 HeaderMatcher::StringMatchTest::Create(const base::Value* data, | |
| 411 MatchType type, | |
| 412 bool case_sensitive) { | |
| 413 std::string str; | |
| 414 CHECK(data->GetAsString(&str)); | |
| 415 return scoped_ptr<StringMatchTest>( | |
| 416 new StringMatchTest(str, type, case_sensitive)); | |
| 417 } | |
| 418 | |
| 419 HeaderMatcher::StringMatchTest::~StringMatchTest() {} | |
| 420 | |
| 421 bool HeaderMatcher::StringMatchTest::Matches( | |
| 422 const std::string& str) const { | |
| 423 switch (type_) { | |
| 424 case kPrefix: | |
| 425 return StartsWithASCII(str, data_, case_sensitive_); | |
| 426 case kSuffix: | |
| 427 return EndsWith(str, data_, case_sensitive_); | |
| 428 case kEquals: | |
| 429 return str.size() == data_.size() && | |
| 430 StartsWithASCII(str, data_, case_sensitive_); | |
| 431 case kContains: | |
| 432 if (!case_sensitive_) { | |
| 433 return std::search(str.begin(), str.end(), data_.begin(), data_.end(), | |
| 434 CaseInsensitiveCompareASCII<char>()) != str.end(); | |
| 435 } else { | |
| 436 return str.find(data_) != std::string::npos; | |
| 437 } | |
| 438 } | |
| 439 // We never get past the "switch", but the compiler worries about no return. | |
| 440 NOTREACHED(); | |
| 441 return false; | |
| 442 } | |
| 443 | |
| 444 HeaderMatcher::StringMatchTest::StringMatchTest(const std::string& data, | |
| 445 MatchType type, | |
| 446 bool case_sensitive) | |
| 447 : data_(data), | |
| 448 type_(type), | |
| 449 case_sensitive_(case_sensitive) {} | |
| 450 | |
| 451 // HeaderMatcher::HeaderMatchTest implementation. | |
| 452 | |
| 453 HeaderMatcher::HeaderMatchTest::HeaderMatchTest( | |
| 454 ScopedVector<const StringMatchTest>* name_match, | |
| 455 ScopedVector<const StringMatchTest>* value_match) | |
| 456 : name_match_(name_match->Pass()), | |
| 457 value_match_(value_match->Pass()) {} | |
| 458 | |
| 459 HeaderMatcher::HeaderMatchTest::~HeaderMatchTest() {} | |
| 460 | |
| 461 // static | |
| 462 scoped_ptr<const HeaderMatcher::HeaderMatchTest> | |
| 463 HeaderMatcher::HeaderMatchTest::Create(const base::DictionaryValue* tests) { | |
| 464 ScopedVector<const StringMatchTest> name_match; | |
| 465 ScopedVector<const StringMatchTest> value_match; | |
| 466 | |
| 467 for (base::DictionaryValue::Iterator it(*tests); | |
| 468 !it.IsAtEnd(); it.Advance()) { | |
| 469 bool is_name = false; // Is this test for header name? | |
| 470 StringMatchTest::MatchType match_type; | |
| 471 if (it.key() == keys::kNamePrefixKey) { | |
| 472 is_name = true; | |
| 473 match_type = StringMatchTest::kPrefix; | |
| 474 } else if (it.key() == keys::kNameSuffixKey) { | |
| 475 is_name = true; | |
| 476 match_type = StringMatchTest::kSuffix; | |
| 477 } else if (it.key() == keys::kNameContainsKey) { | |
| 478 is_name = true; | |
| 479 match_type = StringMatchTest::kContains; | |
| 480 } else if (it.key() == keys::kNameEqualsKey) { | |
| 481 is_name = true; | |
| 482 match_type = StringMatchTest::kEquals; | |
| 483 } else if (it.key() == keys::kValuePrefixKey) { | |
| 484 match_type = StringMatchTest::kPrefix; | |
| 485 } else if (it.key() == keys::kValueSuffixKey) { | |
| 486 match_type = StringMatchTest::kSuffix; | |
| 487 } else if (it.key() == keys::kValueContainsKey) { | |
| 488 match_type = StringMatchTest::kContains; | |
| 489 } else if (it.key() == keys::kValueEqualsKey) { | |
| 490 match_type = StringMatchTest::kEquals; | |
| 491 } else { | |
| 492 NOTREACHED(); // JSON schema type checking should prevent this. | |
| 493 return scoped_ptr<const HeaderMatchTest>(); | |
| 494 } | |
| 495 const base::Value* content = &it.value(); | |
| 496 | |
| 497 ScopedVector<const StringMatchTest>* tests = | |
| 498 is_name ? &name_match : &value_match; | |
| 499 switch (content->GetType()) { | |
| 500 case base::Value::TYPE_LIST: { | |
| 501 const base::ListValue* list = NULL; | |
| 502 CHECK(content->GetAsList(&list)); | |
| 503 for (base::ListValue::const_iterator it = list->begin(); | |
| 504 it != list->end(); ++it) { | |
| 505 tests->push_back( | |
| 506 StringMatchTest::Create(*it, match_type, !is_name).release()); | |
| 507 } | |
| 508 break; | |
| 509 } | |
| 510 case base::Value::TYPE_STRING: { | |
| 511 tests->push_back( | |
| 512 StringMatchTest::Create(content, match_type, !is_name).release()); | |
| 513 break; | |
| 514 } | |
| 515 default: { | |
| 516 NOTREACHED(); // JSON schema type checking should prevent this. | |
| 517 return scoped_ptr<const HeaderMatchTest>(); | |
| 518 } | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 return scoped_ptr<const HeaderMatchTest>( | |
| 523 new HeaderMatchTest(&name_match, &value_match)); | |
| 524 } | |
| 525 | |
| 526 bool HeaderMatcher::HeaderMatchTest::Matches(const std::string& name, | |
| 527 const std::string& value) const { | |
| 528 for (size_t i = 0; i < name_match_.size(); ++i) { | |
| 529 if (!name_match_[i]->Matches(name)) | |
| 530 return false; | |
| 531 } | |
| 532 | |
| 533 for (size_t i = 0; i < value_match_.size(); ++i) { | |
| 534 if (!value_match_[i]->Matches(value)) | |
| 535 return false; | |
| 536 } | |
| 537 | |
| 538 return true; | |
| 539 } | |
| 540 | |
| 541 // | |
| 542 // WebRequestConditionAttributeRequestHeaders | |
| 543 // | |
| 544 | |
| 545 WebRequestConditionAttributeRequestHeaders:: | |
| 546 WebRequestConditionAttributeRequestHeaders( | |
| 547 scoped_ptr<const HeaderMatcher> header_matcher, | |
| 548 bool positive) | |
| 549 : header_matcher_(header_matcher.Pass()), | |
| 550 positive_(positive) {} | |
| 551 | |
| 552 WebRequestConditionAttributeRequestHeaders:: | |
| 553 ~WebRequestConditionAttributeRequestHeaders() {} | |
| 554 | |
| 555 namespace { | |
| 556 | |
| 557 scoped_ptr<const HeaderMatcher> PrepareHeaderMatcher( | |
| 558 const std::string& name, | |
| 559 const base::Value* value, | |
| 560 std::string* error) { | |
| 561 const base::ListValue* value_as_list = NULL; | |
| 562 if (!value->GetAsList(&value_as_list)) { | |
| 563 *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
| 564 return scoped_ptr<const HeaderMatcher>(); | |
| 565 } | |
| 566 | |
| 567 scoped_ptr<const HeaderMatcher> header_matcher( | |
| 568 HeaderMatcher::Create(value_as_list)); | |
| 569 if (header_matcher.get() == NULL) | |
| 570 *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
| 571 return header_matcher.Pass(); | |
| 572 } | |
| 573 | |
| 574 } // namespace | |
| 575 | |
| 576 // static | |
| 577 scoped_refptr<const WebRequestConditionAttribute> | |
| 578 WebRequestConditionAttributeRequestHeaders::Create( | |
| 579 const std::string& name, | |
| 580 const base::Value* value, | |
| 581 std::string* error, | |
| 582 bool* bad_message) { | |
| 583 DCHECK(name == keys::kRequestHeadersKey || | |
| 584 name == keys::kExcludeRequestHeadersKey); | |
| 585 | |
| 586 scoped_ptr<const HeaderMatcher> header_matcher( | |
| 587 PrepareHeaderMatcher(name, value, error)); | |
| 588 if (header_matcher.get() == NULL) | |
| 589 return scoped_refptr<const WebRequestConditionAttribute>(NULL); | |
| 590 | |
| 591 return scoped_refptr<const WebRequestConditionAttribute>( | |
| 592 new WebRequestConditionAttributeRequestHeaders( | |
| 593 header_matcher.Pass(), name == keys::kRequestHeadersKey)); | |
| 594 } | |
| 595 | |
| 596 int WebRequestConditionAttributeRequestHeaders::GetStages() const { | |
| 597 // Currently we only allow matching against headers in the before-send-headers | |
| 598 // stage. The headers are accessible in other stages as well, but before | |
| 599 // allowing to match against them in further stages, we should consider | |
| 600 // caching the match result. | |
| 601 return ON_BEFORE_SEND_HEADERS; | |
| 602 } | |
| 603 | |
| 604 bool WebRequestConditionAttributeRequestHeaders::IsFulfilled( | |
| 605 const WebRequestData& request_data) const { | |
| 606 if (!(request_data.stage & GetStages())) | |
| 607 return false; | |
| 608 | |
| 609 const net::HttpRequestHeaders& headers = | |
| 610 request_data.request->extra_request_headers(); | |
| 611 | |
| 612 bool passed = false; // Did some header pass TestNameValue? | |
| 613 net::HttpRequestHeaders::Iterator it(headers); | |
| 614 while (!passed && it.GetNext()) | |
| 615 passed |= header_matcher_->TestNameValue(it.name(), it.value()); | |
| 616 | |
| 617 return (positive_ ? passed : !passed); | |
| 618 } | |
| 619 | |
| 620 WebRequestConditionAttribute::Type | |
| 621 WebRequestConditionAttributeRequestHeaders::GetType() const { | |
| 622 return CONDITION_REQUEST_HEADERS; | |
| 623 } | |
| 624 | |
| 625 std::string WebRequestConditionAttributeRequestHeaders::GetName() const { | |
| 626 return (positive_ ? keys::kRequestHeadersKey | |
| 627 : keys::kExcludeRequestHeadersKey); | |
| 628 } | |
| 629 | |
| 630 bool WebRequestConditionAttributeRequestHeaders::Equals( | |
| 631 const WebRequestConditionAttribute* other) const { | |
| 632 // Comparing headers is too heavy, so we skip it entirely. | |
| 633 return false; | |
| 634 } | |
| 635 | |
| 636 // | |
| 637 // WebRequestConditionAttributeResponseHeaders | |
| 638 // | |
| 639 | |
| 640 WebRequestConditionAttributeResponseHeaders:: | |
| 641 WebRequestConditionAttributeResponseHeaders( | |
| 642 scoped_ptr<const HeaderMatcher> header_matcher, | |
| 643 bool positive) | |
| 644 : header_matcher_(header_matcher.Pass()), | |
| 645 positive_(positive) {} | |
| 646 | |
| 647 WebRequestConditionAttributeResponseHeaders:: | |
| 648 ~WebRequestConditionAttributeResponseHeaders() {} | |
| 649 | |
| 650 // static | |
| 651 scoped_refptr<const WebRequestConditionAttribute> | |
| 652 WebRequestConditionAttributeResponseHeaders::Create( | |
| 653 const std::string& name, | |
| 654 const base::Value* value, | |
| 655 std::string* error, | |
| 656 bool* bad_message) { | |
| 657 DCHECK(name == keys::kResponseHeadersKey || | |
| 658 name == keys::kExcludeResponseHeadersKey); | |
| 659 | |
| 660 scoped_ptr<const HeaderMatcher> header_matcher( | |
| 661 PrepareHeaderMatcher(name, value, error)); | |
| 662 if (header_matcher.get() == NULL) | |
| 663 return scoped_refptr<const WebRequestConditionAttribute>(NULL); | |
| 664 | |
| 665 return scoped_refptr<const WebRequestConditionAttribute>( | |
| 666 new WebRequestConditionAttributeResponseHeaders( | |
| 667 header_matcher.Pass(), name == keys::kResponseHeadersKey)); | |
| 668 } | |
| 669 | |
| 670 int WebRequestConditionAttributeResponseHeaders::GetStages() const { | |
| 671 return ON_HEADERS_RECEIVED; | |
| 672 } | |
| 673 | |
| 674 bool WebRequestConditionAttributeResponseHeaders::IsFulfilled( | |
| 675 const WebRequestData& request_data) const { | |
| 676 if (!(request_data.stage & GetStages())) | |
| 677 return false; | |
| 678 | |
| 679 const net::HttpResponseHeaders* headers = | |
| 680 request_data.original_response_headers; | |
| 681 if (headers == NULL) { | |
| 682 // Each header of an empty set satisfies (the negation of) everything; | |
| 683 // OTOH, there is no header to satisfy even the most permissive test. | |
| 684 return !positive_; | |
| 685 } | |
| 686 | |
| 687 bool passed = false; // Did some header pass TestNameValue? | |
| 688 std::string name; | |
| 689 std::string value; | |
| 690 void* iter = NULL; | |
| 691 while (!passed && headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 692 passed |= header_matcher_->TestNameValue(name, value); | |
| 693 } | |
| 694 | |
| 695 return (positive_ ? passed : !passed); | |
| 696 } | |
| 697 | |
| 698 WebRequestConditionAttribute::Type | |
| 699 WebRequestConditionAttributeResponseHeaders::GetType() const { | |
| 700 return CONDITION_RESPONSE_HEADERS; | |
| 701 } | |
| 702 | |
| 703 std::string WebRequestConditionAttributeResponseHeaders::GetName() const { | |
| 704 return (positive_ ? keys::kResponseHeadersKey | |
| 705 : keys::kExcludeResponseHeadersKey); | |
| 706 } | |
| 707 | |
| 708 bool WebRequestConditionAttributeResponseHeaders::Equals( | |
| 709 const WebRequestConditionAttribute* other) const { | |
| 710 return false; | |
| 711 } | |
| 712 | |
| 713 // | |
| 714 // WebRequestConditionAttributeThirdParty | |
| 715 // | |
| 716 | |
| 717 WebRequestConditionAttributeThirdParty:: | |
| 718 WebRequestConditionAttributeThirdParty(bool match_third_party) | |
| 719 : match_third_party_(match_third_party) {} | |
| 720 | |
| 721 WebRequestConditionAttributeThirdParty:: | |
| 722 ~WebRequestConditionAttributeThirdParty() {} | |
| 723 | |
| 724 // static | |
| 725 scoped_refptr<const WebRequestConditionAttribute> | |
| 726 WebRequestConditionAttributeThirdParty::Create( | |
| 727 const std::string& name, | |
| 728 const base::Value* value, | |
| 729 std::string* error, | |
| 730 bool* bad_message) { | |
| 731 DCHECK(name == keys::kThirdPartyKey); | |
| 732 | |
| 733 bool third_party = false; // Dummy value, gets overwritten. | |
| 734 if (!value->GetAsBoolean(&third_party)) { | |
| 735 *error = ErrorUtils::FormatErrorMessage(kInvalidValue, | |
| 736 keys::kThirdPartyKey); | |
| 737 return scoped_refptr<const WebRequestConditionAttribute>(NULL); | |
| 738 } | |
| 739 | |
| 740 return scoped_refptr<const WebRequestConditionAttribute>( | |
| 741 new WebRequestConditionAttributeThirdParty(third_party)); | |
| 742 } | |
| 743 | |
| 744 int WebRequestConditionAttributeThirdParty::GetStages() const { | |
| 745 return ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | ON_SEND_HEADERS | | |
| 746 ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED | ON_BEFORE_REDIRECT | | |
| 747 ON_RESPONSE_STARTED | ON_COMPLETED | ON_ERROR; | |
| 748 } | |
| 749 | |
| 750 bool WebRequestConditionAttributeThirdParty::IsFulfilled( | |
| 751 const WebRequestData& request_data) const { | |
| 752 if (!(request_data.stage & GetStages())) | |
| 753 return false; | |
| 754 | |
| 755 // Request is "1st party" if it gets cookies under 3rd party-blocking policy. | |
| 756 const net::StaticCookiePolicy block_third_party_policy( | |
| 757 net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES); | |
| 758 const int can_get_cookies = block_third_party_policy.CanGetCookies( | |
| 759 request_data.request->url(), | |
| 760 request_data.request->first_party_for_cookies()); | |
| 761 const bool is_first_party = (can_get_cookies == net::OK); | |
| 762 | |
| 763 return match_third_party_ ? !is_first_party : is_first_party; | |
| 764 } | |
| 765 | |
| 766 WebRequestConditionAttribute::Type | |
| 767 WebRequestConditionAttributeThirdParty::GetType() const { | |
| 768 return CONDITION_THIRD_PARTY; | |
| 769 } | |
| 770 | |
| 771 std::string WebRequestConditionAttributeThirdParty::GetName() const { | |
| 772 return keys::kThirdPartyKey; | |
| 773 } | |
| 774 | |
| 775 bool WebRequestConditionAttributeThirdParty::Equals( | |
| 776 const WebRequestConditionAttribute* other) const { | |
| 777 if (!WebRequestConditionAttribute::Equals(other)) | |
| 778 return false; | |
| 779 const WebRequestConditionAttributeThirdParty* casted_other = | |
| 780 static_cast<const WebRequestConditionAttributeThirdParty*>(other); | |
| 781 return match_third_party_ == casted_other->match_third_party_; | |
| 782 } | |
| 783 | |
| 784 // | |
| 785 // WebRequestConditionAttributeStages | |
| 786 // | |
| 787 | |
| 788 WebRequestConditionAttributeStages:: | |
| 789 WebRequestConditionAttributeStages(int allowed_stages) | |
| 790 : allowed_stages_(allowed_stages) {} | |
| 791 | |
| 792 WebRequestConditionAttributeStages:: | |
| 793 ~WebRequestConditionAttributeStages() {} | |
| 794 | |
| 795 namespace { | |
| 796 | |
| 797 // Reads strings stored in |value|, which is expected to be a ListValue, and | |
| 798 // sets corresponding bits (see RequestStage) in |out_stages|. Returns true on | |
| 799 // success, false otherwise. | |
| 800 bool ParseListOfStages(const base::Value& value, int* out_stages) { | |
| 801 const base::ListValue* list = NULL; | |
| 802 if (!value.GetAsList(&list)) | |
| 803 return false; | |
| 804 | |
| 805 int stages = 0; | |
| 806 std::string stage_name; | |
| 807 for (base::ListValue::const_iterator it = list->begin(); | |
| 808 it != list->end(); ++it) { | |
| 809 if (!((*it)->GetAsString(&stage_name))) | |
| 810 return false; | |
| 811 if (stage_name == keys::kOnBeforeRequestEnum) { | |
| 812 stages |= ON_BEFORE_REQUEST; | |
| 813 } else if (stage_name == keys::kOnBeforeSendHeadersEnum) { | |
| 814 stages |= ON_BEFORE_SEND_HEADERS; | |
| 815 } else if (stage_name == keys::kOnHeadersReceivedEnum) { | |
| 816 stages |= ON_HEADERS_RECEIVED; | |
| 817 } else if (stage_name == keys::kOnAuthRequiredEnum) { | |
| 818 stages |= ON_AUTH_REQUIRED; | |
| 819 } else { | |
| 820 NOTREACHED(); // JSON schema checks prevent getting here. | |
| 821 return false; | |
| 822 } | |
| 823 } | |
| 824 | |
| 825 *out_stages = stages; | |
| 826 return true; | |
| 827 } | |
| 828 | |
| 829 } // namespace | |
| 830 | |
| 831 // static | |
| 832 scoped_refptr<const WebRequestConditionAttribute> | |
| 833 WebRequestConditionAttributeStages::Create(const std::string& name, | |
| 834 const base::Value* value, | |
| 835 std::string* error, | |
| 836 bool* bad_message) { | |
| 837 DCHECK(name == keys::kStagesKey); | |
| 838 | |
| 839 int allowed_stages = 0; | |
| 840 if (!ParseListOfStages(*value, &allowed_stages)) { | |
| 841 *error = ErrorUtils::FormatErrorMessage(kInvalidValue, | |
| 842 keys::kStagesKey); | |
| 843 return scoped_refptr<const WebRequestConditionAttribute>(NULL); | |
| 844 } | |
| 845 | |
| 846 return scoped_refptr<const WebRequestConditionAttribute>( | |
| 847 new WebRequestConditionAttributeStages(allowed_stages)); | |
| 848 } | |
| 849 | |
| 850 int WebRequestConditionAttributeStages::GetStages() const { | |
| 851 return allowed_stages_; | |
| 852 } | |
| 853 | |
| 854 bool WebRequestConditionAttributeStages::IsFulfilled( | |
| 855 const WebRequestData& request_data) const { | |
| 856 // Note: removing '!=' triggers warning C4800 on the VS compiler. | |
| 857 return (request_data.stage & GetStages()) != 0; | |
| 858 } | |
| 859 | |
| 860 WebRequestConditionAttribute::Type | |
| 861 WebRequestConditionAttributeStages::GetType() const { | |
| 862 return CONDITION_STAGES; | |
| 863 } | |
| 864 | |
| 865 std::string WebRequestConditionAttributeStages::GetName() const { | |
| 866 return keys::kStagesKey; | |
| 867 } | |
| 868 | |
| 869 bool WebRequestConditionAttributeStages::Equals( | |
| 870 const WebRequestConditionAttribute* other) const { | |
| 871 if (!WebRequestConditionAttribute::Equals(other)) | |
| 872 return false; | |
| 873 const WebRequestConditionAttributeStages* casted_other = | |
| 874 static_cast<const WebRequestConditionAttributeStages*>(other); | |
| 875 return allowed_stages_ == casted_other->allowed_stages_; | |
| 876 } | |
| 877 | |
| 878 } // namespace extensions | |
| OLD | NEW |