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.
5 #include "extensions/browser/api/web_request/web_request_api_helpers.h"
10 #include "base/bind.h"
11 #include "base/macros.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/time/time.h"
16 #include "base/values.h"
17 #include "components/web_cache/browser/web_cache_manager.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "extensions/browser/api/web_request/web_request_api_constants.h"
21 #include "extensions/browser/extension_registry.h"
22 #include "extensions/browser/extension_system.h"
23 #include "extensions/browser/extensions_browser_client.h"
24 #include "extensions/browser/runtime_data.h"
25 #include "extensions/browser/warning_set.h"
26 #include "extensions/common/extension_messages.h"
27 #include "net/base/net_log.h"
28 #include "net/cookies/cookie_util.h"
29 #include "net/cookies/parsed_cookie.h"
30 #include "net/http/http_util.h"
31 #include "net/url_request/url_request.h"
32 #include "url/url_constants.h"
34 // TODO(battre): move all static functions into an anonymous namespace at the
38 using content::ResourceType
;
39 using net::cookie_util::ParsedRequestCookie
;
40 using net::cookie_util::ParsedRequestCookies
;
42 namespace keys
= extension_web_request_api_constants
;
44 namespace extension_web_request_api_helpers
{
48 static const char* kResourceTypeStrings
[] = {
60 const size_t kResourceTypeStringsLength
= arraysize(kResourceTypeStrings
);
62 static ResourceType kResourceTypeValues
[] = {
63 content::RESOURCE_TYPE_MAIN_FRAME
,
64 content::RESOURCE_TYPE_SUB_FRAME
,
65 content::RESOURCE_TYPE_STYLESHEET
,
66 content::RESOURCE_TYPE_SCRIPT
,
67 content::RESOURCE_TYPE_IMAGE
,
68 content::RESOURCE_TYPE_OBJECT
,
69 content::RESOURCE_TYPE_XHR
,
70 content::RESOURCE_TYPE_LAST_TYPE
, // represents "other"
71 // TODO(jochen): We duplicate the last entry, so the array's size is not a
72 // power of two. If it is, this triggers a bug in gcc 4.4 in Release builds
73 // (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949). Once we use a version
74 // of gcc with this bug fixed, or the array is changed so this duplicate
75 // entry is no longer required, this should be removed.
76 content::RESOURCE_TYPE_LAST_TYPE
,
79 const size_t kResourceTypeValuesLength
= arraysize(kResourceTypeValues
);
81 typedef std::vector
<linked_ptr
<net::ParsedCookie
> > ParsedResponseCookies
;
83 void ClearCacheOnNavigationOnUI() {
84 web_cache::WebCacheManager::GetInstance()->ClearCacheOnNavigation();
87 bool ParseCookieLifetime(net::ParsedCookie
* cookie
,
88 int64
* seconds_till_expiry
) {
89 // 'Max-Age' is processed first because according to:
90 // http://tools.ietf.org/html/rfc6265#section-5.3 'Max-Age' attribute
91 // overrides 'Expires' attribute.
92 if (cookie
->HasMaxAge() &&
93 base::StringToInt64(cookie
->MaxAge(), seconds_till_expiry
)) {
97 Time parsed_expiry_time
;
98 if (cookie
->HasExpires())
99 parsed_expiry_time
= net::cookie_util::ParseCookieTime(cookie
->Expires());
101 if (!parsed_expiry_time
.is_null()) {
102 *seconds_till_expiry
=
103 ceil((parsed_expiry_time
- Time::Now()).InSecondsF());
104 return *seconds_till_expiry
>= 0;
109 bool NullableEquals(const int* a
, const int* b
) {
110 if ((a
&& !b
) || (!a
&& b
))
112 return (!a
) || (*a
== *b
);
115 bool NullableEquals(const bool* a
, const bool* b
) {
116 if ((a
&& !b
) || (!a
&& b
))
118 return (!a
) || (*a
== *b
);
121 bool NullableEquals(const std::string
* a
, const std::string
* b
) {
122 if ((a
&& !b
) || (!a
&& b
))
124 return (!a
) || (*a
== *b
);
129 RequestCookie::RequestCookie() {}
130 RequestCookie::~RequestCookie() {}
132 bool NullableEquals(const RequestCookie
* a
, const RequestCookie
* b
) {
133 if ((a
&& !b
) || (!a
&& b
))
137 return NullableEquals(a
->name
.get(), b
->name
.get()) &&
138 NullableEquals(a
->value
.get(), b
->value
.get());
141 ResponseCookie::ResponseCookie() {}
142 ResponseCookie::~ResponseCookie() {}
144 bool NullableEquals(const ResponseCookie
* a
, const ResponseCookie
* b
) {
145 if ((a
&& !b
) || (!a
&& b
))
149 return NullableEquals(a
->name
.get(), b
->name
.get()) &&
150 NullableEquals(a
->value
.get(), b
->value
.get()) &&
151 NullableEquals(a
->expires
.get(), b
->expires
.get()) &&
152 NullableEquals(a
->max_age
.get(), b
->max_age
.get()) &&
153 NullableEquals(a
->domain
.get(), b
->domain
.get()) &&
154 NullableEquals(a
->path
.get(), b
->path
.get()) &&
155 NullableEquals(a
->secure
.get(), b
->secure
.get()) &&
156 NullableEquals(a
->http_only
.get(), b
->http_only
.get());
159 FilterResponseCookie::FilterResponseCookie() {}
160 FilterResponseCookie::~FilterResponseCookie() {}
162 bool NullableEquals(const FilterResponseCookie
* a
,
163 const FilterResponseCookie
* b
) {
164 if ((a
&& !b
) || (!a
&& b
))
168 return NullableEquals(a
->age_lower_bound
.get(), b
->age_lower_bound
.get()) &&
169 NullableEquals(a
->age_upper_bound
.get(), b
->age_upper_bound
.get()) &&
170 NullableEquals(a
->session_cookie
.get(), b
->session_cookie
.get());
173 RequestCookieModification::RequestCookieModification() {}
174 RequestCookieModification::~RequestCookieModification() {}
176 bool NullableEquals(const RequestCookieModification
* a
,
177 const RequestCookieModification
* b
) {
178 if ((a
&& !b
) || (!a
&& b
))
182 return NullableEquals(a
->filter
.get(), b
->filter
.get()) &&
183 NullableEquals(a
->modification
.get(), b
->modification
.get());
186 ResponseCookieModification::ResponseCookieModification() : type(ADD
) {}
187 ResponseCookieModification::~ResponseCookieModification() {}
189 bool NullableEquals(const ResponseCookieModification
* a
,
190 const ResponseCookieModification
* b
) {
191 if ((a
&& !b
) || (!a
&& b
))
195 return a
->type
== b
->type
&&
196 NullableEquals(a
->filter
.get(), b
->filter
.get()) &&
197 NullableEquals(a
->modification
.get(), b
->modification
.get());
200 EventResponseDelta::EventResponseDelta(
201 const std::string
& extension_id
, const base::Time
& extension_install_time
)
202 : extension_id(extension_id
),
203 extension_install_time(extension_install_time
),
207 EventResponseDelta::~EventResponseDelta() {
211 // Creates a NetLog callback the returns a Value with the ID of the extension
212 // that caused an event. |delta| must remain valid for the lifetime of the
214 net::NetLog::ParametersCallback
CreateNetLogExtensionIdCallback(
215 const EventResponseDelta
* delta
) {
216 return net::NetLog::StringCallback("extension_id", &delta
->extension_id
);
219 // Creates NetLog parameters to indicate that an extension modified a request.
220 // Caller takes ownership of returned value.
221 base::Value
* NetLogModificationCallback(
222 const EventResponseDelta
* delta
,
223 net::NetLog::LogLevel log_level
) {
224 base::DictionaryValue
* dict
= new base::DictionaryValue();
225 dict
->SetString("extension_id", delta
->extension_id
);
227 base::ListValue
* modified_headers
= new base::ListValue();
228 net::HttpRequestHeaders::Iterator
modification(
229 delta
->modified_request_headers
);
230 while (modification
.GetNext()) {
231 std::string line
= modification
.name() + ": " + modification
.value();
232 modified_headers
->Append(new base::StringValue(line
));
234 dict
->Set("modified_headers", modified_headers
);
236 base::ListValue
* deleted_headers
= new base::ListValue();
237 for (std::vector
<std::string
>::const_iterator key
=
238 delta
->deleted_request_headers
.begin();
239 key
!= delta
->deleted_request_headers
.end();
241 deleted_headers
->Append(new base::StringValue(*key
));
243 dict
->Set("deleted_headers", deleted_headers
);
247 bool InDecreasingExtensionInstallationTimeOrder(
248 const linked_ptr
<EventResponseDelta
>& a
,
249 const linked_ptr
<EventResponseDelta
>& b
) {
250 return a
->extension_install_time
> b
->extension_install_time
;
253 base::ListValue
* StringToCharList(const std::string
& s
) {
254 base::ListValue
* result
= new base::ListValue
;
255 for (size_t i
= 0, n
= s
.size(); i
< n
; ++i
) {
257 new base::FundamentalValue(
258 *reinterpret_cast<const unsigned char*>(&s
[i
])));
263 bool CharListToString(const base::ListValue
* list
, std::string
* out
) {
266 const size_t list_length
= list
->GetSize();
267 out
->resize(list_length
);
269 for (size_t i
= 0; i
< list_length
; ++i
) {
270 if (!list
->GetInteger(i
, &value
) || value
< 0 || value
> 255)
272 unsigned char tmp
= static_cast<unsigned char>(value
);
273 (*out
)[i
] = *reinterpret_cast<char*>(&tmp
);
278 EventResponseDelta
* CalculateOnBeforeRequestDelta(
279 const std::string
& extension_id
,
280 const base::Time
& extension_install_time
,
282 const GURL
& new_url
) {
283 EventResponseDelta
* result
=
284 new EventResponseDelta(extension_id
, extension_install_time
);
285 result
->cancel
= cancel
;
286 result
->new_url
= new_url
;
290 EventResponseDelta
* CalculateOnBeforeSendHeadersDelta(
291 const std::string
& extension_id
,
292 const base::Time
& extension_install_time
,
294 net::HttpRequestHeaders
* old_headers
,
295 net::HttpRequestHeaders
* new_headers
) {
296 EventResponseDelta
* result
=
297 new EventResponseDelta(extension_id
, extension_install_time
);
298 result
->cancel
= cancel
;
300 // The event listener might not have passed any new headers if he
301 // just wanted to cancel the request.
303 // Find deleted headers.
305 net::HttpRequestHeaders::Iterator
i(*old_headers
);
306 while (i
.GetNext()) {
307 if (!new_headers
->HasHeader(i
.name())) {
308 result
->deleted_request_headers
.push_back(i
.name());
313 // Find modified headers.
315 net::HttpRequestHeaders::Iterator
i(*new_headers
);
316 while (i
.GetNext()) {
318 if (!old_headers
->GetHeader(i
.name(), &value
) || i
.value() != value
) {
319 result
->modified_request_headers
.SetHeader(i
.name(), i
.value());
327 EventResponseDelta
* CalculateOnHeadersReceivedDelta(
328 const std::string
& extension_id
,
329 const base::Time
& extension_install_time
,
332 const net::HttpResponseHeaders
* old_response_headers
,
333 ResponseHeaders
* new_response_headers
) {
334 EventResponseDelta
* result
=
335 new EventResponseDelta(extension_id
, extension_install_time
);
336 result
->cancel
= cancel
;
337 result
->new_url
= new_url
;
339 if (!new_response_headers
)
342 // Find deleted headers (header keys are treated case insensitively).
347 while (old_response_headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
348 std::string
name_lowercase(name
);
349 base::StringToLowerASCII(&name_lowercase
);
351 bool header_found
= false;
352 for (ResponseHeaders::const_iterator i
= new_response_headers
->begin();
353 i
!= new_response_headers
->end(); ++i
) {
354 if (LowerCaseEqualsASCII(i
->first
, name_lowercase
.c_str()) &&
355 value
== i
->second
) {
361 result
->deleted_response_headers
.push_back(ResponseHeader(name
, value
));
365 // Find added headers (header keys are treated case insensitively).
367 for (ResponseHeaders::const_iterator i
= new_response_headers
->begin();
368 i
!= new_response_headers
->end(); ++i
) {
371 bool header_found
= false;
372 while (old_response_headers
->EnumerateHeader(&iter
, i
->first
, &value
) &&
374 header_found
= (value
== i
->second
);
377 result
->added_response_headers
.push_back(*i
);
384 EventResponseDelta
* CalculateOnAuthRequiredDelta(
385 const std::string
& extension_id
,
386 const base::Time
& extension_install_time
,
388 scoped_ptr
<net::AuthCredentials
>* auth_credentials
) {
389 EventResponseDelta
* result
=
390 new EventResponseDelta(extension_id
, extension_install_time
);
391 result
->cancel
= cancel
;
392 result
->auth_credentials
.swap(*auth_credentials
);
396 void MergeCancelOfResponses(
397 const EventResponseDeltas
& deltas
,
399 const net::BoundNetLog
* net_log
) {
400 for (EventResponseDeltas::const_iterator i
= deltas
.begin();
401 i
!= deltas
.end(); ++i
) {
405 net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST
,
406 CreateNetLogExtensionIdCallback(i
->get()));
412 // Helper function for MergeRedirectUrlOfResponses() that allows ignoring
413 // all redirects but those to data:// urls and about:blank. This is important
414 // to treat these URLs as "cancel urls", i.e. URLs that extensions redirect
415 // to if they want to express that they want to cancel a request. This reduces
416 // the number of conflicts that we need to flag, as canceling is considered
417 // a higher precedence operation that redirects.
418 // Returns whether a redirect occurred.
419 static bool MergeRedirectUrlOfResponsesHelper(
420 const EventResponseDeltas
& deltas
,
422 extensions::WarningSet
* conflicting_extensions
,
423 const net::BoundNetLog
* net_log
,
424 bool consider_only_cancel_scheme_urls
) {
425 bool redirected
= false;
427 // Extension that determines the |new_url|.
428 std::string winning_extension_id
;
429 EventResponseDeltas::const_iterator delta
;
430 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
431 if ((*delta
)->new_url
.is_empty())
433 if (consider_only_cancel_scheme_urls
&&
434 !(*delta
)->new_url
.SchemeIs(url::kDataScheme
) &&
435 (*delta
)->new_url
.spec() != "about:blank") {
439 if (!redirected
|| *new_url
== (*delta
)->new_url
) {
440 *new_url
= (*delta
)->new_url
;
441 winning_extension_id
= (*delta
)->extension_id
;
444 net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST
,
445 CreateNetLogExtensionIdCallback(delta
->get()));
447 conflicting_extensions
->insert(
448 extensions::Warning::CreateRedirectConflictWarning(
449 (*delta
)->extension_id
,
450 winning_extension_id
,
454 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
455 CreateNetLogExtensionIdCallback(delta
->get()));
461 void MergeRedirectUrlOfResponses(
462 const EventResponseDeltas
& deltas
,
464 extensions::WarningSet
* conflicting_extensions
,
465 const net::BoundNetLog
* net_log
) {
467 // First handle only redirects to data:// URLs and about:blank. These are a
468 // special case as they represent a way of cancelling a request.
469 if (MergeRedirectUrlOfResponsesHelper(
470 deltas
, new_url
, conflicting_extensions
, net_log
, true)) {
471 // If any extension cancelled a request by redirecting to a data:// URL or
472 // about:blank, we don't consider the other redirects.
476 // Handle all other redirects.
477 MergeRedirectUrlOfResponsesHelper(
478 deltas
, new_url
, conflicting_extensions
, net_log
, false);
481 void MergeOnBeforeRequestResponses(
482 const EventResponseDeltas
& deltas
,
484 extensions::WarningSet
* conflicting_extensions
,
485 const net::BoundNetLog
* net_log
) {
486 MergeRedirectUrlOfResponses(deltas
, new_url
, conflicting_extensions
, net_log
);
489 static bool DoesRequestCookieMatchFilter(
490 const ParsedRequestCookie
& cookie
,
491 RequestCookie
* filter
) {
492 if (!filter
) return true;
493 if (filter
->name
.get() && cookie
.first
!= *filter
->name
) return false;
494 if (filter
->value
.get() && cookie
.second
!= *filter
->value
) return false;
498 // Applies all CookieModificationType::ADD operations for request cookies of
499 // |deltas| to |cookies|. Returns whether any cookie was added.
500 static bool MergeAddRequestCookieModifications(
501 const EventResponseDeltas
& deltas
,
502 ParsedRequestCookies
* cookies
) {
503 bool modified
= false;
504 // We assume here that the deltas are sorted in decreasing extension
505 // precedence (i.e. decreasing extension installation time).
506 EventResponseDeltas::const_reverse_iterator delta
;
507 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
508 const RequestCookieModifications
& modifications
=
509 (*delta
)->request_cookie_modifications
;
510 for (RequestCookieModifications::const_iterator mod
= modifications
.begin();
511 mod
!= modifications
.end(); ++mod
) {
512 if ((*mod
)->type
!= ADD
|| !(*mod
)->modification
.get())
514 std::string
* new_name
= (*mod
)->modification
->name
.get();
515 std::string
* new_value
= (*mod
)->modification
->value
.get();
516 if (!new_name
|| !new_value
)
519 bool cookie_with_same_name_found
= false;
520 for (ParsedRequestCookies::iterator cookie
= cookies
->begin();
521 cookie
!= cookies
->end() && !cookie_with_same_name_found
; ++cookie
) {
522 if (cookie
->first
== *new_name
) {
523 if (cookie
->second
!= *new_value
) {
524 cookie
->second
= *new_value
;
527 cookie_with_same_name_found
= true;
530 if (!cookie_with_same_name_found
) {
531 cookies
->push_back(std::make_pair(base::StringPiece(*new_name
),
532 base::StringPiece(*new_value
)));
540 // Applies all CookieModificationType::EDIT operations for request cookies of
541 // |deltas| to |cookies|. Returns whether any cookie was modified.
542 static bool MergeEditRequestCookieModifications(
543 const EventResponseDeltas
& deltas
,
544 ParsedRequestCookies
* cookies
) {
545 bool modified
= false;
546 // We assume here that the deltas are sorted in decreasing extension
547 // precedence (i.e. decreasing extension installation time).
548 EventResponseDeltas::const_reverse_iterator delta
;
549 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
550 const RequestCookieModifications
& modifications
=
551 (*delta
)->request_cookie_modifications
;
552 for (RequestCookieModifications::const_iterator mod
= modifications
.begin();
553 mod
!= modifications
.end(); ++mod
) {
554 if ((*mod
)->type
!= EDIT
|| !(*mod
)->modification
.get())
557 std::string
* new_value
= (*mod
)->modification
->value
.get();
558 RequestCookie
* filter
= (*mod
)->filter
.get();
559 for (ParsedRequestCookies::iterator cookie
= cookies
->begin();
560 cookie
!= cookies
->end(); ++cookie
) {
561 if (!DoesRequestCookieMatchFilter(*cookie
, filter
))
563 // If the edit operation tries to modify the cookie name, we just ignore
564 // this. We only modify the cookie value.
565 if (new_value
&& cookie
->second
!= *new_value
) {
566 cookie
->second
= *new_value
;
575 // Applies all CookieModificationType::REMOVE operations for request cookies of
576 // |deltas| to |cookies|. Returns whether any cookie was deleted.
577 static bool MergeRemoveRequestCookieModifications(
578 const EventResponseDeltas
& deltas
,
579 ParsedRequestCookies
* cookies
) {
580 bool modified
= false;
581 // We assume here that the deltas are sorted in decreasing extension
582 // precedence (i.e. decreasing extension installation time).
583 EventResponseDeltas::const_reverse_iterator delta
;
584 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
585 const RequestCookieModifications
& modifications
=
586 (*delta
)->request_cookie_modifications
;
587 for (RequestCookieModifications::const_iterator mod
= modifications
.begin();
588 mod
!= modifications
.end(); ++mod
) {
589 if ((*mod
)->type
!= REMOVE
)
592 RequestCookie
* filter
= (*mod
)->filter
.get();
593 ParsedRequestCookies::iterator i
= cookies
->begin();
594 while (i
!= cookies
->end()) {
595 if (DoesRequestCookieMatchFilter(*i
, filter
)) {
596 i
= cookies
->erase(i
);
607 void MergeCookiesInOnBeforeSendHeadersResponses(
608 const EventResponseDeltas
& deltas
,
609 net::HttpRequestHeaders
* request_headers
,
610 extensions::WarningSet
* conflicting_extensions
,
611 const net::BoundNetLog
* net_log
) {
612 // Skip all work if there are no registered cookie modifications.
613 bool cookie_modifications_exist
= false;
614 EventResponseDeltas::const_iterator delta
;
615 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
616 cookie_modifications_exist
|=
617 !(*delta
)->request_cookie_modifications
.empty();
619 if (!cookie_modifications_exist
)
622 // Parse old cookie line.
623 std::string cookie_header
;
624 request_headers
->GetHeader(net::HttpRequestHeaders::kCookie
, &cookie_header
);
625 ParsedRequestCookies cookies
;
626 net::cookie_util::ParseRequestCookieLine(cookie_header
, &cookies
);
629 bool modified
= false;
630 modified
|= MergeAddRequestCookieModifications(deltas
, &cookies
);
631 modified
|= MergeEditRequestCookieModifications(deltas
, &cookies
);
632 modified
|= MergeRemoveRequestCookieModifications(deltas
, &cookies
);
634 // Reassemble and store new cookie line.
636 std::string new_cookie_header
=
637 net::cookie_util::SerializeRequestCookieLine(cookies
);
638 request_headers
->SetHeader(net::HttpRequestHeaders::kCookie
,
643 // Returns the extension ID of the first extension in |deltas| that sets the
644 // request header identified by |key| to |value|.
645 static std::string
FindSetRequestHeader(
646 const EventResponseDeltas
& deltas
,
647 const std::string
& key
,
648 const std::string
& value
) {
649 EventResponseDeltas::const_iterator delta
;
650 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
651 net::HttpRequestHeaders::Iterator
modification(
652 (*delta
)->modified_request_headers
);
653 while (modification
.GetNext()) {
654 if (key
== modification
.name() && value
== modification
.value())
655 return (*delta
)->extension_id
;
658 return std::string();
661 // Returns the extension ID of the first extension in |deltas| that removes the
662 // request header identified by |key|.
663 static std::string
FindRemoveRequestHeader(
664 const EventResponseDeltas
& deltas
,
665 const std::string
& key
) {
666 EventResponseDeltas::const_iterator delta
;
667 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
668 std::vector
<std::string
>::iterator i
;
669 for (i
= (*delta
)->deleted_request_headers
.begin();
670 i
!= (*delta
)->deleted_request_headers
.end();
673 return (*delta
)->extension_id
;
676 return std::string();
679 void MergeOnBeforeSendHeadersResponses(
680 const EventResponseDeltas
& deltas
,
681 net::HttpRequestHeaders
* request_headers
,
682 extensions::WarningSet
* conflicting_extensions
,
683 const net::BoundNetLog
* net_log
) {
684 EventResponseDeltas::const_iterator delta
;
686 // Here we collect which headers we have removed or set to new values
687 // so far due to extensions of higher precedence.
688 std::set
<std::string
> removed_headers
;
689 std::set
<std::string
> set_headers
;
691 // We assume here that the deltas are sorted in decreasing extension
692 // precedence (i.e. decreasing extension installation time).
693 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
694 if ((*delta
)->modified_request_headers
.IsEmpty() &&
695 (*delta
)->deleted_request_headers
.empty()) {
699 // Check whether any modification affects a request header that
700 // has been modified differently before. As deltas is sorted by decreasing
701 // extension installation order, this takes care of precedence.
702 bool extension_conflicts
= false;
703 std::string winning_extension_id
;
704 std::string conflicting_header
;
706 net::HttpRequestHeaders::Iterator
modification(
707 (*delta
)->modified_request_headers
);
708 while (modification
.GetNext() && !extension_conflicts
) {
709 // This modification sets |key| to |value|.
710 const std::string
& key
= modification
.name();
711 const std::string
& value
= modification
.value();
713 // We must not delete anything that has been modified before.
714 if (removed_headers
.find(key
) != removed_headers
.end() &&
715 !extension_conflicts
) {
716 winning_extension_id
= FindRemoveRequestHeader(deltas
, key
);
717 conflicting_header
= key
;
718 extension_conflicts
= true;
721 // We must not modify anything that has been set to a *different*
723 if (set_headers
.find(key
) != set_headers
.end() &&
724 !extension_conflicts
) {
725 std::string current_value
;
726 if (!request_headers
->GetHeader(key
, ¤t_value
) ||
727 current_value
!= value
) {
728 winning_extension_id
=
729 FindSetRequestHeader(deltas
, key
, current_value
);
730 conflicting_header
= key
;
731 extension_conflicts
= true;
737 // Check whether any deletion affects a request header that has been
740 std::vector
<std::string
>::iterator key
;
741 for (key
= (*delta
)->deleted_request_headers
.begin();
742 key
!= (*delta
)->deleted_request_headers
.end() &&
743 !extension_conflicts
;
745 if (set_headers
.find(*key
) != set_headers
.end()) {
746 std::string current_value
;
747 request_headers
->GetHeader(*key
, ¤t_value
);
748 winning_extension_id
=
749 FindSetRequestHeader(deltas
, *key
, current_value
);
750 conflicting_header
= *key
;
751 extension_conflicts
= true;
756 // Now execute the modifications if there were no conflicts.
757 if (!extension_conflicts
) {
758 // Copy all modifications into the original headers.
759 request_headers
->MergeFrom((*delta
)->modified_request_headers
);
761 // Record which keys were changed.
762 net::HttpRequestHeaders::Iterator
modification(
763 (*delta
)->modified_request_headers
);
764 while (modification
.GetNext())
765 set_headers
.insert(modification
.name());
768 // Perform all deletions and record which keys were deleted.
770 std::vector
<std::string
>::iterator key
;
771 for (key
= (*delta
)->deleted_request_headers
.begin();
772 key
!= (*delta
)->deleted_request_headers
.end();
774 request_headers
->RemoveHeader(*key
);
775 removed_headers
.insert(*key
);
779 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS
,
780 base::Bind(&NetLogModificationCallback
, delta
->get()));
782 conflicting_extensions
->insert(
783 extensions::Warning::CreateRequestHeaderConflictWarning(
784 (*delta
)->extension_id
, winning_extension_id
,
785 conflicting_header
));
787 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
788 CreateNetLogExtensionIdCallback(delta
->get()));
792 MergeCookiesInOnBeforeSendHeadersResponses(deltas
, request_headers
,
793 conflicting_extensions
, net_log
);
796 // Retrives all cookies from |override_response_headers|.
797 static ParsedResponseCookies
GetResponseCookies(
798 scoped_refptr
<net::HttpResponseHeaders
> override_response_headers
) {
799 ParsedResponseCookies result
;
803 while (override_response_headers
->EnumerateHeader(&iter
, "Set-Cookie",
805 result
.push_back(make_linked_ptr(new net::ParsedCookie(value
)));
810 // Stores all |cookies| in |override_response_headers| deleting previously
811 // existing cookie definitions.
812 static void StoreResponseCookies(
813 const ParsedResponseCookies
& cookies
,
814 scoped_refptr
<net::HttpResponseHeaders
> override_response_headers
) {
815 override_response_headers
->RemoveHeader("Set-Cookie");
816 for (ParsedResponseCookies::const_iterator i
= cookies
.begin();
817 i
!= cookies
.end(); ++i
) {
818 override_response_headers
->AddHeader("Set-Cookie: " + (*i
)->ToCookieLine());
822 // Modifies |cookie| according to |modification|. Each value that is set in
823 // |modification| is applied to |cookie|.
824 static bool ApplyResponseCookieModification(ResponseCookie
* modification
,
825 net::ParsedCookie
* cookie
) {
826 bool modified
= false;
827 if (modification
->name
.get())
828 modified
|= cookie
->SetName(*modification
->name
);
829 if (modification
->value
.get())
830 modified
|= cookie
->SetValue(*modification
->value
);
831 if (modification
->expires
.get())
832 modified
|= cookie
->SetExpires(*modification
->expires
);
833 if (modification
->max_age
.get())
834 modified
|= cookie
->SetMaxAge(base::IntToString(*modification
->max_age
));
835 if (modification
->domain
.get())
836 modified
|= cookie
->SetDomain(*modification
->domain
);
837 if (modification
->path
.get())
838 modified
|= cookie
->SetPath(*modification
->path
);
839 if (modification
->secure
.get())
840 modified
|= cookie
->SetIsSecure(*modification
->secure
);
841 if (modification
->http_only
.get())
842 modified
|= cookie
->SetIsHttpOnly(*modification
->http_only
);
846 static bool DoesResponseCookieMatchFilter(net::ParsedCookie
* cookie
,
847 FilterResponseCookie
* filter
) {
848 if (!cookie
->IsValid()) return false;
849 if (!filter
) return true;
850 if (filter
->name
&& cookie
->Name() != *filter
->name
)
852 if (filter
->value
&& cookie
->Value() != *filter
->value
)
854 if (filter
->expires
) {
855 std::string actual_value
=
856 cookie
->HasExpires() ? cookie
->Expires() : std::string();
857 if (actual_value
!= *filter
->expires
)
860 if (filter
->max_age
) {
861 std::string actual_value
=
862 cookie
->HasMaxAge() ? cookie
->MaxAge() : std::string();
863 if (actual_value
!= base::IntToString(*filter
->max_age
))
866 if (filter
->domain
) {
867 std::string actual_value
=
868 cookie
->HasDomain() ? cookie
->Domain() : std::string();
869 if (actual_value
!= *filter
->domain
)
873 std::string actual_value
=
874 cookie
->HasPath() ? cookie
->Path() : std::string();
875 if (actual_value
!= *filter
->path
)
878 if (filter
->secure
&& cookie
->IsSecure() != *filter
->secure
)
880 if (filter
->http_only
&& cookie
->IsHttpOnly() != *filter
->http_only
)
882 if (filter
->age_upper_bound
|| filter
->age_lower_bound
||
883 (filter
->session_cookie
&& *filter
->session_cookie
)) {
884 int64 seconds_to_expiry
;
885 bool lifetime_parsed
= ParseCookieLifetime(cookie
, &seconds_to_expiry
);
886 if (filter
->age_upper_bound
&& seconds_to_expiry
> *filter
->age_upper_bound
)
888 if (filter
->age_lower_bound
&& seconds_to_expiry
< *filter
->age_lower_bound
)
890 if (filter
->session_cookie
&& *filter
->session_cookie
&& lifetime_parsed
)
896 // Applies all CookieModificationType::ADD operations for response cookies of
897 // |deltas| to |cookies|. Returns whether any cookie was added.
898 static bool MergeAddResponseCookieModifications(
899 const EventResponseDeltas
& deltas
,
900 ParsedResponseCookies
* cookies
) {
901 bool modified
= false;
902 // We assume here that the deltas are sorted in decreasing extension
903 // precedence (i.e. decreasing extension installation time).
904 EventResponseDeltas::const_reverse_iterator delta
;
905 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
906 const ResponseCookieModifications
& modifications
=
907 (*delta
)->response_cookie_modifications
;
908 for (ResponseCookieModifications::const_iterator mod
=
909 modifications
.begin(); mod
!= modifications
.end(); ++mod
) {
910 if ((*mod
)->type
!= ADD
|| !(*mod
)->modification
.get())
912 // Cookie names are not unique in response cookies so we always append
913 // and never override.
914 linked_ptr
<net::ParsedCookie
> cookie(
915 new net::ParsedCookie(std::string()));
916 ApplyResponseCookieModification((*mod
)->modification
.get(), cookie
.get());
917 cookies
->push_back(cookie
);
924 // Applies all CookieModificationType::EDIT operations for response cookies of
925 // |deltas| to |cookies|. Returns whether any cookie was modified.
926 static bool MergeEditResponseCookieModifications(
927 const EventResponseDeltas
& deltas
,
928 ParsedResponseCookies
* cookies
) {
929 bool modified
= false;
930 // We assume here that the deltas are sorted in decreasing extension
931 // precedence (i.e. decreasing extension installation time).
932 EventResponseDeltas::const_reverse_iterator delta
;
933 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
934 const ResponseCookieModifications
& modifications
=
935 (*delta
)->response_cookie_modifications
;
936 for (ResponseCookieModifications::const_iterator mod
=
937 modifications
.begin(); mod
!= modifications
.end(); ++mod
) {
938 if ((*mod
)->type
!= EDIT
|| !(*mod
)->modification
.get())
941 for (ParsedResponseCookies::iterator cookie
= cookies
->begin();
942 cookie
!= cookies
->end(); ++cookie
) {
943 if (DoesResponseCookieMatchFilter(cookie
->get(),
944 (*mod
)->filter
.get())) {
945 modified
|= ApplyResponseCookieModification(
946 (*mod
)->modification
.get(), cookie
->get());
954 // Applies all CookieModificationType::REMOVE operations for response cookies of
955 // |deltas| to |cookies|. Returns whether any cookie was deleted.
956 static bool MergeRemoveResponseCookieModifications(
957 const EventResponseDeltas
& deltas
,
958 ParsedResponseCookies
* cookies
) {
959 bool modified
= false;
960 // We assume here that the deltas are sorted in decreasing extension
961 // precedence (i.e. decreasing extension installation time).
962 EventResponseDeltas::const_reverse_iterator delta
;
963 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
964 const ResponseCookieModifications
& modifications
=
965 (*delta
)->response_cookie_modifications
;
966 for (ResponseCookieModifications::const_iterator mod
=
967 modifications
.begin(); mod
!= modifications
.end(); ++mod
) {
968 if ((*mod
)->type
!= REMOVE
)
971 ParsedResponseCookies::iterator i
= cookies
->begin();
972 while (i
!= cookies
->end()) {
973 if (DoesResponseCookieMatchFilter(i
->get(),
974 (*mod
)->filter
.get())) {
975 i
= cookies
->erase(i
);
986 void MergeCookiesInOnHeadersReceivedResponses(
987 const EventResponseDeltas
& deltas
,
988 const net::HttpResponseHeaders
* original_response_headers
,
989 scoped_refptr
<net::HttpResponseHeaders
>* override_response_headers
,
990 extensions::WarningSet
* conflicting_extensions
,
991 const net::BoundNetLog
* net_log
) {
992 // Skip all work if there are no registered cookie modifications.
993 bool cookie_modifications_exist
= false;
994 EventResponseDeltas::const_reverse_iterator delta
;
995 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
996 cookie_modifications_exist
|=
997 !(*delta
)->response_cookie_modifications
.empty();
999 if (!cookie_modifications_exist
)
1002 // Only create a copy if we really want to modify the response headers.
1003 if (override_response_headers
->get() == NULL
) {
1004 *override_response_headers
= new net::HttpResponseHeaders(
1005 original_response_headers
->raw_headers());
1008 ParsedResponseCookies cookies
=
1009 GetResponseCookies(*override_response_headers
);
1011 bool modified
= false;
1012 modified
|= MergeAddResponseCookieModifications(deltas
, &cookies
);
1013 modified
|= MergeEditResponseCookieModifications(deltas
, &cookies
);
1014 modified
|= MergeRemoveResponseCookieModifications(deltas
, &cookies
);
1018 StoreResponseCookies(cookies
, *override_response_headers
);
1021 // Converts the key of the (key, value) pair to lower case.
1022 static ResponseHeader
ToLowerCase(const ResponseHeader
& header
) {
1023 std::string
lower_key(header
.first
);
1024 base::StringToLowerASCII(&lower_key
);
1025 return ResponseHeader(lower_key
, header
.second
);
1028 // Returns the extension ID of the first extension in |deltas| that removes the
1029 // request header identified by |key|.
1030 static std::string
FindRemoveResponseHeader(
1031 const EventResponseDeltas
& deltas
,
1032 const std::string
& key
) {
1033 std::string lower_key
= base::StringToLowerASCII(key
);
1034 EventResponseDeltas::const_iterator delta
;
1035 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
1036 ResponseHeaders::const_iterator i
;
1037 for (i
= (*delta
)->deleted_response_headers
.begin();
1038 i
!= (*delta
)->deleted_response_headers
.end(); ++i
) {
1039 if (base::StringToLowerASCII(i
->first
) == lower_key
)
1040 return (*delta
)->extension_id
;
1043 return std::string();
1046 void MergeOnHeadersReceivedResponses(
1047 const EventResponseDeltas
& deltas
,
1048 const net::HttpResponseHeaders
* original_response_headers
,
1049 scoped_refptr
<net::HttpResponseHeaders
>* override_response_headers
,
1050 GURL
* allowed_unsafe_redirect_url
,
1051 extensions::WarningSet
* conflicting_extensions
,
1052 const net::BoundNetLog
* net_log
) {
1053 EventResponseDeltas::const_iterator delta
;
1055 // Here we collect which headers we have removed or added so far due to
1056 // extensions of higher precedence. Header keys are always stored as
1058 std::set
<ResponseHeader
> removed_headers
;
1059 std::set
<ResponseHeader
> added_headers
;
1061 // We assume here that the deltas are sorted in decreasing extension
1062 // precedence (i.e. decreasing extension installation time).
1063 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
1064 if ((*delta
)->added_response_headers
.empty() &&
1065 (*delta
)->deleted_response_headers
.empty()) {
1069 // Only create a copy if we really want to modify the response headers.
1070 if (override_response_headers
->get() == NULL
) {
1071 *override_response_headers
= new net::HttpResponseHeaders(
1072 original_response_headers
->raw_headers());
1075 // We consider modifications as pairs of (delete, add) operations.
1076 // If a header is deleted twice by different extensions we assume that the
1077 // intention was to modify it to different values and consider this a
1078 // conflict. As deltas is sorted by decreasing extension installation order,
1079 // this takes care of precedence.
1080 bool extension_conflicts
= false;
1081 std::string conflicting_header
;
1082 std::string winning_extension_id
;
1083 ResponseHeaders::const_iterator i
;
1084 for (i
= (*delta
)->deleted_response_headers
.begin();
1085 i
!= (*delta
)->deleted_response_headers
.end(); ++i
) {
1086 if (removed_headers
.find(ToLowerCase(*i
)) != removed_headers
.end()) {
1087 winning_extension_id
= FindRemoveResponseHeader(deltas
, i
->first
);
1088 conflicting_header
= i
->first
;
1089 extension_conflicts
= true;
1094 // Now execute the modifications if there were no conflicts.
1095 if (!extension_conflicts
) {
1098 for (i
= (*delta
)->deleted_response_headers
.begin();
1099 i
!= (*delta
)->deleted_response_headers
.end(); ++i
) {
1100 (*override_response_headers
)->RemoveHeaderLine(i
->first
, i
->second
);
1101 removed_headers
.insert(ToLowerCase(*i
));
1107 for (i
= (*delta
)->added_response_headers
.begin();
1108 i
!= (*delta
)->added_response_headers
.end(); ++i
) {
1109 ResponseHeader
lowercase_header(ToLowerCase(*i
));
1110 if (added_headers
.find(lowercase_header
) != added_headers
.end())
1112 added_headers
.insert(lowercase_header
);
1113 (*override_response_headers
)->AddHeader(i
->first
+ ": " + i
->second
);
1117 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS
,
1118 CreateNetLogExtensionIdCallback(delta
->get()));
1120 conflicting_extensions
->insert(
1121 extensions::Warning::CreateResponseHeaderConflictWarning(
1122 (*delta
)->extension_id
, winning_extension_id
,
1123 conflicting_header
));
1125 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
1126 CreateNetLogExtensionIdCallback(delta
->get()));
1130 MergeCookiesInOnHeadersReceivedResponses(deltas
, original_response_headers
,
1131 override_response_headers
, conflicting_extensions
, net_log
);
1134 MergeRedirectUrlOfResponses(
1135 deltas
, &new_url
, conflicting_extensions
, net_log
);
1136 if (new_url
.is_valid()) {
1137 // Only create a copy if we really want to modify the response headers.
1138 if (override_response_headers
->get() == NULL
) {
1139 *override_response_headers
= new net::HttpResponseHeaders(
1140 original_response_headers
->raw_headers());
1142 (*override_response_headers
)->ReplaceStatusLine("HTTP/1.1 302 Found");
1143 (*override_response_headers
)->RemoveHeader("location");
1144 (*override_response_headers
)->AddHeader("Location: " + new_url
.spec());
1145 // Explicitly mark the URL as safe for redirection, to prevent the request
1146 // from being blocked because of net::ERR_UNSAFE_REDIRECT.
1147 *allowed_unsafe_redirect_url
= new_url
;
1151 bool MergeOnAuthRequiredResponses(
1152 const EventResponseDeltas
& deltas
,
1153 net::AuthCredentials
* auth_credentials
,
1154 extensions::WarningSet
* conflicting_extensions
,
1155 const net::BoundNetLog
* net_log
) {
1156 CHECK(auth_credentials
);
1157 bool credentials_set
= false;
1158 std::string winning_extension_id
;
1160 for (EventResponseDeltas::const_iterator delta
= deltas
.begin();
1161 delta
!= deltas
.end();
1163 if (!(*delta
)->auth_credentials
.get())
1166 auth_credentials
->username() !=
1167 (*delta
)->auth_credentials
->username() ||
1168 auth_credentials
->password() != (*delta
)->auth_credentials
->password();
1169 if (credentials_set
&& different
) {
1170 conflicting_extensions
->insert(
1171 extensions::Warning::CreateCredentialsConflictWarning(
1172 (*delta
)->extension_id
, winning_extension_id
));
1174 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
1175 CreateNetLogExtensionIdCallback(delta
->get()));
1178 net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS
,
1179 CreateNetLogExtensionIdCallback(delta
->get()));
1180 *auth_credentials
= *(*delta
)->auth_credentials
;
1181 credentials_set
= true;
1182 winning_extension_id
= (*delta
)->extension_id
;
1185 return credentials_set
;
1188 void ClearCacheOnNavigation() {
1189 if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
)) {
1190 ClearCacheOnNavigationOnUI();
1192 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
1193 base::Bind(&ClearCacheOnNavigationOnUI
));
1197 void NotifyWebRequestAPIUsed(
1198 void* browser_context_id
,
1199 scoped_refptr
<const extensions::Extension
> extension
) {
1200 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
1201 content::BrowserContext
* browser_context
=
1202 reinterpret_cast<content::BrowserContext
*>(browser_context_id
);
1203 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(
1207 extensions::RuntimeData
* runtime_data
=
1208 extensions::ExtensionSystem::Get(browser_context
)->runtime_data();
1209 if (extension
.get()) {
1210 if (runtime_data
->HasUsedWebRequest(extension
.get()))
1212 runtime_data
->SetHasUsedWebRequest(extension
.get(), true);
1215 for (content::RenderProcessHost::iterator it
=
1216 content::RenderProcessHost::AllHostsIterator();
1217 !it
.IsAtEnd(); it
.Advance()) {
1218 content::RenderProcessHost
* host
= it
.GetCurrentValue();
1219 if (host
->GetBrowserContext() == browser_context
)
1220 SendExtensionWebRequestStatusToHost(host
);
1224 void SendExtensionWebRequestStatusToHost(content::RenderProcessHost
* host
) {
1225 content::BrowserContext
* browser_context
= host
->GetBrowserContext();
1226 if (!browser_context
)
1229 bool webrequest_used
= false;
1230 const extensions::ExtensionSet
& extensions
=
1231 extensions::ExtensionRegistry::Get(browser_context
)->enabled_extensions();
1232 extensions::RuntimeData
* runtime_data
=
1233 extensions::ExtensionSystem::Get(browser_context
)->runtime_data();
1234 for (extensions::ExtensionSet::const_iterator it
= extensions
.begin();
1235 !webrequest_used
&& it
!= extensions
.end();
1237 webrequest_used
|= runtime_data
->HasUsedWebRequest(it
->get());
1240 host
->Send(new ExtensionMsg_UsingWebRequestAPI(webrequest_used
));
1243 // Converts the |name|, |value| pair of a http header to a HttpHeaders
1244 // dictionary. Ownership is passed to the caller.
1245 base::DictionaryValue
* CreateHeaderDictionary(
1246 const std::string
& name
, const std::string
& value
) {
1247 base::DictionaryValue
* header
= new base::DictionaryValue();
1248 header
->SetString(keys::kHeaderNameKey
, name
);
1249 if (base::IsStringUTF8(value
)) {
1250 header
->SetString(keys::kHeaderValueKey
, value
);
1252 header
->Set(keys::kHeaderBinaryValueKey
,
1253 StringToCharList(value
));
1258 #define ARRAYEND(array) (array + arraysize(array))
1260 bool IsRelevantResourceType(ResourceType type
) {
1261 ResourceType
* iter
=
1262 std::find(kResourceTypeValues
,
1263 kResourceTypeValues
+ kResourceTypeValuesLength
,
1265 return iter
!= (kResourceTypeValues
+ kResourceTypeValuesLength
);
1268 const char* ResourceTypeToString(ResourceType type
) {
1269 ResourceType
* iter
=
1270 std::find(kResourceTypeValues
,
1271 kResourceTypeValues
+ kResourceTypeValuesLength
,
1273 if (iter
== (kResourceTypeValues
+ kResourceTypeValuesLength
))
1276 return kResourceTypeStrings
[iter
- kResourceTypeValues
];
1279 bool ParseResourceType(const std::string
& type_str
,
1280 ResourceType
* type
) {
1282 std::find(kResourceTypeStrings
,
1283 kResourceTypeStrings
+ kResourceTypeStringsLength
,
1285 if (iter
== (kResourceTypeStrings
+ kResourceTypeStringsLength
))
1287 *type
= kResourceTypeValues
[iter
- kResourceTypeStrings
];
1291 } // namespace extension_web_request_api_helpers