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 |