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/cookies/cookie_util.h"
28 #include "net/cookies/parsed_cookie.h"
29 #include "net/http/http_util.h"
30 #include "net/log/net_log.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 scoped_ptr
<base::Value
> NetLogModificationCallback(
221 const EventResponseDelta
* delta
,
222 net::NetLogCaptureMode capture_mode
) {
223 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
224 dict
->SetString("extension_id", delta
->extension_id
);
226 base::ListValue
* modified_headers
= new base::ListValue();
227 net::HttpRequestHeaders::Iterator
modification(
228 delta
->modified_request_headers
);
229 while (modification
.GetNext()) {
230 std::string line
= modification
.name() + ": " + modification
.value();
231 modified_headers
->Append(new base::StringValue(line
));
233 dict
->Set("modified_headers", modified_headers
);
235 base::ListValue
* deleted_headers
= new base::ListValue();
236 for (std::vector
<std::string
>::const_iterator key
=
237 delta
->deleted_request_headers
.begin();
238 key
!= delta
->deleted_request_headers
.end();
240 deleted_headers
->Append(new base::StringValue(*key
));
242 dict
->Set("deleted_headers", deleted_headers
);
246 bool InDecreasingExtensionInstallationTimeOrder(
247 const linked_ptr
<EventResponseDelta
>& a
,
248 const linked_ptr
<EventResponseDelta
>& b
) {
249 return a
->extension_install_time
> b
->extension_install_time
;
252 base::ListValue
* StringToCharList(const std::string
& s
) {
253 base::ListValue
* result
= new base::ListValue
;
254 for (size_t i
= 0, n
= s
.size(); i
< n
; ++i
) {
256 new base::FundamentalValue(
257 *reinterpret_cast<const unsigned char*>(&s
[i
])));
262 bool CharListToString(const base::ListValue
* list
, std::string
* out
) {
265 const size_t list_length
= list
->GetSize();
266 out
->resize(list_length
);
268 for (size_t i
= 0; i
< list_length
; ++i
) {
269 if (!list
->GetInteger(i
, &value
) || value
< 0 || value
> 255)
271 unsigned char tmp
= static_cast<unsigned char>(value
);
272 (*out
)[i
] = *reinterpret_cast<char*>(&tmp
);
277 EventResponseDelta
* CalculateOnBeforeRequestDelta(
278 const std::string
& extension_id
,
279 const base::Time
& extension_install_time
,
281 const GURL
& new_url
) {
282 EventResponseDelta
* result
=
283 new EventResponseDelta(extension_id
, extension_install_time
);
284 result
->cancel
= cancel
;
285 result
->new_url
= new_url
;
289 EventResponseDelta
* CalculateOnBeforeSendHeadersDelta(
290 const std::string
& extension_id
,
291 const base::Time
& extension_install_time
,
293 net::HttpRequestHeaders
* old_headers
,
294 net::HttpRequestHeaders
* new_headers
) {
295 EventResponseDelta
* result
=
296 new EventResponseDelta(extension_id
, extension_install_time
);
297 result
->cancel
= cancel
;
299 // The event listener might not have passed any new headers if he
300 // just wanted to cancel the request.
302 // Find deleted headers.
304 net::HttpRequestHeaders::Iterator
i(*old_headers
);
305 while (i
.GetNext()) {
306 if (!new_headers
->HasHeader(i
.name())) {
307 result
->deleted_request_headers
.push_back(i
.name());
312 // Find modified headers.
314 net::HttpRequestHeaders::Iterator
i(*new_headers
);
315 while (i
.GetNext()) {
317 if (!old_headers
->GetHeader(i
.name(), &value
) || i
.value() != value
) {
318 result
->modified_request_headers
.SetHeader(i
.name(), i
.value());
326 EventResponseDelta
* CalculateOnHeadersReceivedDelta(
327 const std::string
& extension_id
,
328 const base::Time
& extension_install_time
,
331 const net::HttpResponseHeaders
* old_response_headers
,
332 ResponseHeaders
* new_response_headers
) {
333 EventResponseDelta
* result
=
334 new EventResponseDelta(extension_id
, extension_install_time
);
335 result
->cancel
= cancel
;
336 result
->new_url
= new_url
;
338 if (!new_response_headers
)
341 // Find deleted headers (header keys are treated case insensitively).
346 while (old_response_headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
347 std::string
name_lowercase(name
);
348 base::StringToLowerASCII(&name_lowercase
);
350 bool header_found
= false;
351 for (ResponseHeaders::const_iterator i
= new_response_headers
->begin();
352 i
!= new_response_headers
->end(); ++i
) {
353 if (base::LowerCaseEqualsASCII(i
->first
, name_lowercase
.c_str()) &&
354 value
== i
->second
) {
360 result
->deleted_response_headers
.push_back(ResponseHeader(name
, value
));
364 // Find added headers (header keys are treated case insensitively).
366 for (ResponseHeaders::const_iterator i
= new_response_headers
->begin();
367 i
!= new_response_headers
->end(); ++i
) {
370 bool header_found
= false;
371 while (old_response_headers
->EnumerateHeader(&iter
, i
->first
, &value
) &&
373 header_found
= (value
== i
->second
);
376 result
->added_response_headers
.push_back(*i
);
383 EventResponseDelta
* CalculateOnAuthRequiredDelta(
384 const std::string
& extension_id
,
385 const base::Time
& extension_install_time
,
387 scoped_ptr
<net::AuthCredentials
>* auth_credentials
) {
388 EventResponseDelta
* result
=
389 new EventResponseDelta(extension_id
, extension_install_time
);
390 result
->cancel
= cancel
;
391 result
->auth_credentials
.swap(*auth_credentials
);
395 void MergeCancelOfResponses(
396 const EventResponseDeltas
& deltas
,
398 const net::BoundNetLog
* net_log
) {
399 for (EventResponseDeltas::const_iterator i
= deltas
.begin();
400 i
!= deltas
.end(); ++i
) {
404 net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST
,
405 CreateNetLogExtensionIdCallback(i
->get()));
411 // Helper function for MergeRedirectUrlOfResponses() that allows ignoring
412 // all redirects but those to data:// urls and about:blank. This is important
413 // to treat these URLs as "cancel urls", i.e. URLs that extensions redirect
414 // to if they want to express that they want to cancel a request. This reduces
415 // the number of conflicts that we need to flag, as canceling is considered
416 // a higher precedence operation that redirects.
417 // Returns whether a redirect occurred.
418 static bool MergeRedirectUrlOfResponsesHelper(
419 const EventResponseDeltas
& deltas
,
421 extensions::WarningSet
* conflicting_extensions
,
422 const net::BoundNetLog
* net_log
,
423 bool consider_only_cancel_scheme_urls
) {
424 bool redirected
= false;
426 // Extension that determines the |new_url|.
427 std::string winning_extension_id
;
428 EventResponseDeltas::const_iterator delta
;
429 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
430 if ((*delta
)->new_url
.is_empty())
432 if (consider_only_cancel_scheme_urls
&&
433 !(*delta
)->new_url
.SchemeIs(url::kDataScheme
) &&
434 (*delta
)->new_url
.spec() != "about:blank") {
438 if (!redirected
|| *new_url
== (*delta
)->new_url
) {
439 *new_url
= (*delta
)->new_url
;
440 winning_extension_id
= (*delta
)->extension_id
;
443 net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST
,
444 CreateNetLogExtensionIdCallback(delta
->get()));
446 conflicting_extensions
->insert(
447 extensions::Warning::CreateRedirectConflictWarning(
448 (*delta
)->extension_id
,
449 winning_extension_id
,
453 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
454 CreateNetLogExtensionIdCallback(delta
->get()));
460 void MergeRedirectUrlOfResponses(
461 const EventResponseDeltas
& deltas
,
463 extensions::WarningSet
* conflicting_extensions
,
464 const net::BoundNetLog
* net_log
) {
466 // First handle only redirects to data:// URLs and about:blank. These are a
467 // special case as they represent a way of cancelling a request.
468 if (MergeRedirectUrlOfResponsesHelper(
469 deltas
, new_url
, conflicting_extensions
, net_log
, true)) {
470 // If any extension cancelled a request by redirecting to a data:// URL or
471 // about:blank, we don't consider the other redirects.
475 // Handle all other redirects.
476 MergeRedirectUrlOfResponsesHelper(
477 deltas
, new_url
, conflicting_extensions
, net_log
, false);
480 void MergeOnBeforeRequestResponses(
481 const EventResponseDeltas
& deltas
,
483 extensions::WarningSet
* conflicting_extensions
,
484 const net::BoundNetLog
* net_log
) {
485 MergeRedirectUrlOfResponses(deltas
, new_url
, conflicting_extensions
, net_log
);
488 static bool DoesRequestCookieMatchFilter(
489 const ParsedRequestCookie
& cookie
,
490 RequestCookie
* filter
) {
491 if (!filter
) return true;
492 if (filter
->name
.get() && cookie
.first
!= *filter
->name
) return false;
493 if (filter
->value
.get() && cookie
.second
!= *filter
->value
) return false;
497 // Applies all CookieModificationType::ADD operations for request cookies of
498 // |deltas| to |cookies|. Returns whether any cookie was added.
499 static bool MergeAddRequestCookieModifications(
500 const EventResponseDeltas
& deltas
,
501 ParsedRequestCookies
* cookies
) {
502 bool modified
= false;
503 // We assume here that the deltas are sorted in decreasing extension
504 // precedence (i.e. decreasing extension installation time).
505 EventResponseDeltas::const_reverse_iterator delta
;
506 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
507 const RequestCookieModifications
& modifications
=
508 (*delta
)->request_cookie_modifications
;
509 for (RequestCookieModifications::const_iterator mod
= modifications
.begin();
510 mod
!= modifications
.end(); ++mod
) {
511 if ((*mod
)->type
!= ADD
|| !(*mod
)->modification
.get())
513 std::string
* new_name
= (*mod
)->modification
->name
.get();
514 std::string
* new_value
= (*mod
)->modification
->value
.get();
515 if (!new_name
|| !new_value
)
518 bool cookie_with_same_name_found
= false;
519 for (ParsedRequestCookies::iterator cookie
= cookies
->begin();
520 cookie
!= cookies
->end() && !cookie_with_same_name_found
; ++cookie
) {
521 if (cookie
->first
== *new_name
) {
522 if (cookie
->second
!= *new_value
) {
523 cookie
->second
= *new_value
;
526 cookie_with_same_name_found
= true;
529 if (!cookie_with_same_name_found
) {
530 cookies
->push_back(std::make_pair(base::StringPiece(*new_name
),
531 base::StringPiece(*new_value
)));
539 // Applies all CookieModificationType::EDIT operations for request cookies of
540 // |deltas| to |cookies|. Returns whether any cookie was modified.
541 static bool MergeEditRequestCookieModifications(
542 const EventResponseDeltas
& deltas
,
543 ParsedRequestCookies
* cookies
) {
544 bool modified
= false;
545 // We assume here that the deltas are sorted in decreasing extension
546 // precedence (i.e. decreasing extension installation time).
547 EventResponseDeltas::const_reverse_iterator delta
;
548 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
549 const RequestCookieModifications
& modifications
=
550 (*delta
)->request_cookie_modifications
;
551 for (RequestCookieModifications::const_iterator mod
= modifications
.begin();
552 mod
!= modifications
.end(); ++mod
) {
553 if ((*mod
)->type
!= EDIT
|| !(*mod
)->modification
.get())
556 std::string
* new_value
= (*mod
)->modification
->value
.get();
557 RequestCookie
* filter
= (*mod
)->filter
.get();
558 for (ParsedRequestCookies::iterator cookie
= cookies
->begin();
559 cookie
!= cookies
->end(); ++cookie
) {
560 if (!DoesRequestCookieMatchFilter(*cookie
, filter
))
562 // If the edit operation tries to modify the cookie name, we just ignore
563 // this. We only modify the cookie value.
564 if (new_value
&& cookie
->second
!= *new_value
) {
565 cookie
->second
= *new_value
;
574 // Applies all CookieModificationType::REMOVE operations for request cookies of
575 // |deltas| to |cookies|. Returns whether any cookie was deleted.
576 static bool MergeRemoveRequestCookieModifications(
577 const EventResponseDeltas
& deltas
,
578 ParsedRequestCookies
* cookies
) {
579 bool modified
= false;
580 // We assume here that the deltas are sorted in decreasing extension
581 // precedence (i.e. decreasing extension installation time).
582 EventResponseDeltas::const_reverse_iterator delta
;
583 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
584 const RequestCookieModifications
& modifications
=
585 (*delta
)->request_cookie_modifications
;
586 for (RequestCookieModifications::const_iterator mod
= modifications
.begin();
587 mod
!= modifications
.end(); ++mod
) {
588 if ((*mod
)->type
!= REMOVE
)
591 RequestCookie
* filter
= (*mod
)->filter
.get();
592 ParsedRequestCookies::iterator i
= cookies
->begin();
593 while (i
!= cookies
->end()) {
594 if (DoesRequestCookieMatchFilter(*i
, filter
)) {
595 i
= cookies
->erase(i
);
606 void MergeCookiesInOnBeforeSendHeadersResponses(
607 const EventResponseDeltas
& deltas
,
608 net::HttpRequestHeaders
* request_headers
,
609 extensions::WarningSet
* conflicting_extensions
,
610 const net::BoundNetLog
* net_log
) {
611 // Skip all work if there are no registered cookie modifications.
612 bool cookie_modifications_exist
= false;
613 EventResponseDeltas::const_iterator delta
;
614 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
615 cookie_modifications_exist
|=
616 !(*delta
)->request_cookie_modifications
.empty();
618 if (!cookie_modifications_exist
)
621 // Parse old cookie line.
622 std::string cookie_header
;
623 request_headers
->GetHeader(net::HttpRequestHeaders::kCookie
, &cookie_header
);
624 ParsedRequestCookies cookies
;
625 net::cookie_util::ParseRequestCookieLine(cookie_header
, &cookies
);
628 bool modified
= false;
629 modified
|= MergeAddRequestCookieModifications(deltas
, &cookies
);
630 modified
|= MergeEditRequestCookieModifications(deltas
, &cookies
);
631 modified
|= MergeRemoveRequestCookieModifications(deltas
, &cookies
);
633 // Reassemble and store new cookie line.
635 std::string new_cookie_header
=
636 net::cookie_util::SerializeRequestCookieLine(cookies
);
637 request_headers
->SetHeader(net::HttpRequestHeaders::kCookie
,
642 // Returns the extension ID of the first extension in |deltas| that sets the
643 // request header identified by |key| to |value|.
644 static std::string
FindSetRequestHeader(
645 const EventResponseDeltas
& deltas
,
646 const std::string
& key
,
647 const std::string
& value
) {
648 EventResponseDeltas::const_iterator delta
;
649 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
650 net::HttpRequestHeaders::Iterator
modification(
651 (*delta
)->modified_request_headers
);
652 while (modification
.GetNext()) {
653 if (key
== modification
.name() && value
== modification
.value())
654 return (*delta
)->extension_id
;
657 return std::string();
660 // Returns the extension ID of the first extension in |deltas| that removes the
661 // request header identified by |key|.
662 static std::string
FindRemoveRequestHeader(
663 const EventResponseDeltas
& deltas
,
664 const std::string
& key
) {
665 EventResponseDeltas::const_iterator delta
;
666 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
667 std::vector
<std::string
>::iterator i
;
668 for (i
= (*delta
)->deleted_request_headers
.begin();
669 i
!= (*delta
)->deleted_request_headers
.end();
672 return (*delta
)->extension_id
;
675 return std::string();
678 void MergeOnBeforeSendHeadersResponses(
679 const EventResponseDeltas
& deltas
,
680 net::HttpRequestHeaders
* request_headers
,
681 extensions::WarningSet
* conflicting_extensions
,
682 const net::BoundNetLog
* net_log
) {
683 EventResponseDeltas::const_iterator delta
;
685 // Here we collect which headers we have removed or set to new values
686 // so far due to extensions of higher precedence.
687 std::set
<std::string
> removed_headers
;
688 std::set
<std::string
> set_headers
;
690 // We assume here that the deltas are sorted in decreasing extension
691 // precedence (i.e. decreasing extension installation time).
692 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
693 if ((*delta
)->modified_request_headers
.IsEmpty() &&
694 (*delta
)->deleted_request_headers
.empty()) {
698 // Check whether any modification affects a request header that
699 // has been modified differently before. As deltas is sorted by decreasing
700 // extension installation order, this takes care of precedence.
701 bool extension_conflicts
= false;
702 std::string winning_extension_id
;
703 std::string conflicting_header
;
705 net::HttpRequestHeaders::Iterator
modification(
706 (*delta
)->modified_request_headers
);
707 while (modification
.GetNext() && !extension_conflicts
) {
708 // This modification sets |key| to |value|.
709 const std::string
& key
= modification
.name();
710 const std::string
& value
= modification
.value();
712 // We must not delete anything that has been modified before.
713 if (removed_headers
.find(key
) != removed_headers
.end() &&
714 !extension_conflicts
) {
715 winning_extension_id
= FindRemoveRequestHeader(deltas
, key
);
716 conflicting_header
= key
;
717 extension_conflicts
= true;
720 // We must not modify anything that has been set to a *different*
722 if (set_headers
.find(key
) != set_headers
.end() &&
723 !extension_conflicts
) {
724 std::string current_value
;
725 if (!request_headers
->GetHeader(key
, ¤t_value
) ||
726 current_value
!= value
) {
727 winning_extension_id
=
728 FindSetRequestHeader(deltas
, key
, current_value
);
729 conflicting_header
= key
;
730 extension_conflicts
= true;
736 // Check whether any deletion affects a request header that has been
739 std::vector
<std::string
>::iterator key
;
740 for (key
= (*delta
)->deleted_request_headers
.begin();
741 key
!= (*delta
)->deleted_request_headers
.end() &&
742 !extension_conflicts
;
744 if (set_headers
.find(*key
) != set_headers
.end()) {
745 std::string current_value
;
746 request_headers
->GetHeader(*key
, ¤t_value
);
747 winning_extension_id
=
748 FindSetRequestHeader(deltas
, *key
, current_value
);
749 conflicting_header
= *key
;
750 extension_conflicts
= true;
755 // Now execute the modifications if there were no conflicts.
756 if (!extension_conflicts
) {
757 // Copy all modifications into the original headers.
758 request_headers
->MergeFrom((*delta
)->modified_request_headers
);
760 // Record which keys were changed.
761 net::HttpRequestHeaders::Iterator
modification(
762 (*delta
)->modified_request_headers
);
763 while (modification
.GetNext())
764 set_headers
.insert(modification
.name());
767 // Perform all deletions and record which keys were deleted.
769 std::vector
<std::string
>::iterator key
;
770 for (key
= (*delta
)->deleted_request_headers
.begin();
771 key
!= (*delta
)->deleted_request_headers
.end();
773 request_headers
->RemoveHeader(*key
);
774 removed_headers
.insert(*key
);
778 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS
,
779 base::Bind(&NetLogModificationCallback
, delta
->get()));
781 conflicting_extensions
->insert(
782 extensions::Warning::CreateRequestHeaderConflictWarning(
783 (*delta
)->extension_id
, winning_extension_id
,
784 conflicting_header
));
786 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
787 CreateNetLogExtensionIdCallback(delta
->get()));
791 MergeCookiesInOnBeforeSendHeadersResponses(deltas
, request_headers
,
792 conflicting_extensions
, net_log
);
795 // Retrives all cookies from |override_response_headers|.
796 static ParsedResponseCookies
GetResponseCookies(
797 scoped_refptr
<net::HttpResponseHeaders
> override_response_headers
) {
798 ParsedResponseCookies result
;
802 while (override_response_headers
->EnumerateHeader(&iter
, "Set-Cookie",
804 result
.push_back(make_linked_ptr(new net::ParsedCookie(value
)));
809 // Stores all |cookies| in |override_response_headers| deleting previously
810 // existing cookie definitions.
811 static void StoreResponseCookies(
812 const ParsedResponseCookies
& cookies
,
813 scoped_refptr
<net::HttpResponseHeaders
> override_response_headers
) {
814 override_response_headers
->RemoveHeader("Set-Cookie");
815 for (ParsedResponseCookies::const_iterator i
= cookies
.begin();
816 i
!= cookies
.end(); ++i
) {
817 override_response_headers
->AddHeader("Set-Cookie: " + (*i
)->ToCookieLine());
821 // Modifies |cookie| according to |modification|. Each value that is set in
822 // |modification| is applied to |cookie|.
823 static bool ApplyResponseCookieModification(ResponseCookie
* modification
,
824 net::ParsedCookie
* cookie
) {
825 bool modified
= false;
826 if (modification
->name
.get())
827 modified
|= cookie
->SetName(*modification
->name
);
828 if (modification
->value
.get())
829 modified
|= cookie
->SetValue(*modification
->value
);
830 if (modification
->expires
.get())
831 modified
|= cookie
->SetExpires(*modification
->expires
);
832 if (modification
->max_age
.get())
833 modified
|= cookie
->SetMaxAge(base::IntToString(*modification
->max_age
));
834 if (modification
->domain
.get())
835 modified
|= cookie
->SetDomain(*modification
->domain
);
836 if (modification
->path
.get())
837 modified
|= cookie
->SetPath(*modification
->path
);
838 if (modification
->secure
.get())
839 modified
|= cookie
->SetIsSecure(*modification
->secure
);
840 if (modification
->http_only
.get())
841 modified
|= cookie
->SetIsHttpOnly(*modification
->http_only
);
845 static bool DoesResponseCookieMatchFilter(net::ParsedCookie
* cookie
,
846 FilterResponseCookie
* filter
) {
847 if (!cookie
->IsValid()) return false;
848 if (!filter
) return true;
849 if (filter
->name
&& cookie
->Name() != *filter
->name
)
851 if (filter
->value
&& cookie
->Value() != *filter
->value
)
853 if (filter
->expires
) {
854 std::string actual_value
=
855 cookie
->HasExpires() ? cookie
->Expires() : std::string();
856 if (actual_value
!= *filter
->expires
)
859 if (filter
->max_age
) {
860 std::string actual_value
=
861 cookie
->HasMaxAge() ? cookie
->MaxAge() : std::string();
862 if (actual_value
!= base::IntToString(*filter
->max_age
))
865 if (filter
->domain
) {
866 std::string actual_value
=
867 cookie
->HasDomain() ? cookie
->Domain() : std::string();
868 if (actual_value
!= *filter
->domain
)
872 std::string actual_value
=
873 cookie
->HasPath() ? cookie
->Path() : std::string();
874 if (actual_value
!= *filter
->path
)
877 if (filter
->secure
&& cookie
->IsSecure() != *filter
->secure
)
879 if (filter
->http_only
&& cookie
->IsHttpOnly() != *filter
->http_only
)
881 if (filter
->age_upper_bound
|| filter
->age_lower_bound
||
882 (filter
->session_cookie
&& *filter
->session_cookie
)) {
883 int64 seconds_to_expiry
;
884 bool lifetime_parsed
= ParseCookieLifetime(cookie
, &seconds_to_expiry
);
885 if (filter
->age_upper_bound
&& seconds_to_expiry
> *filter
->age_upper_bound
)
887 if (filter
->age_lower_bound
&& seconds_to_expiry
< *filter
->age_lower_bound
)
889 if (filter
->session_cookie
&& *filter
->session_cookie
&& lifetime_parsed
)
895 // Applies all CookieModificationType::ADD operations for response cookies of
896 // |deltas| to |cookies|. Returns whether any cookie was added.
897 static bool MergeAddResponseCookieModifications(
898 const EventResponseDeltas
& deltas
,
899 ParsedResponseCookies
* cookies
) {
900 bool modified
= false;
901 // We assume here that the deltas are sorted in decreasing extension
902 // precedence (i.e. decreasing extension installation time).
903 EventResponseDeltas::const_reverse_iterator delta
;
904 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
905 const ResponseCookieModifications
& modifications
=
906 (*delta
)->response_cookie_modifications
;
907 for (ResponseCookieModifications::const_iterator mod
=
908 modifications
.begin(); mod
!= modifications
.end(); ++mod
) {
909 if ((*mod
)->type
!= ADD
|| !(*mod
)->modification
.get())
911 // Cookie names are not unique in response cookies so we always append
912 // and never override.
913 linked_ptr
<net::ParsedCookie
> cookie(
914 new net::ParsedCookie(std::string()));
915 ApplyResponseCookieModification((*mod
)->modification
.get(), cookie
.get());
916 cookies
->push_back(cookie
);
923 // Applies all CookieModificationType::EDIT operations for response cookies of
924 // |deltas| to |cookies|. Returns whether any cookie was modified.
925 static bool MergeEditResponseCookieModifications(
926 const EventResponseDeltas
& deltas
,
927 ParsedResponseCookies
* cookies
) {
928 bool modified
= false;
929 // We assume here that the deltas are sorted in decreasing extension
930 // precedence (i.e. decreasing extension installation time).
931 EventResponseDeltas::const_reverse_iterator delta
;
932 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
933 const ResponseCookieModifications
& modifications
=
934 (*delta
)->response_cookie_modifications
;
935 for (ResponseCookieModifications::const_iterator mod
=
936 modifications
.begin(); mod
!= modifications
.end(); ++mod
) {
937 if ((*mod
)->type
!= EDIT
|| !(*mod
)->modification
.get())
940 for (ParsedResponseCookies::iterator cookie
= cookies
->begin();
941 cookie
!= cookies
->end(); ++cookie
) {
942 if (DoesResponseCookieMatchFilter(cookie
->get(),
943 (*mod
)->filter
.get())) {
944 modified
|= ApplyResponseCookieModification(
945 (*mod
)->modification
.get(), cookie
->get());
953 // Applies all CookieModificationType::REMOVE operations for response cookies of
954 // |deltas| to |cookies|. Returns whether any cookie was deleted.
955 static bool MergeRemoveResponseCookieModifications(
956 const EventResponseDeltas
& deltas
,
957 ParsedResponseCookies
* cookies
) {
958 bool modified
= false;
959 // We assume here that the deltas are sorted in decreasing extension
960 // precedence (i.e. decreasing extension installation time).
961 EventResponseDeltas::const_reverse_iterator delta
;
962 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
963 const ResponseCookieModifications
& modifications
=
964 (*delta
)->response_cookie_modifications
;
965 for (ResponseCookieModifications::const_iterator mod
=
966 modifications
.begin(); mod
!= modifications
.end(); ++mod
) {
967 if ((*mod
)->type
!= REMOVE
)
970 ParsedResponseCookies::iterator i
= cookies
->begin();
971 while (i
!= cookies
->end()) {
972 if (DoesResponseCookieMatchFilter(i
->get(),
973 (*mod
)->filter
.get())) {
974 i
= cookies
->erase(i
);
985 void MergeCookiesInOnHeadersReceivedResponses(
986 const EventResponseDeltas
& deltas
,
987 const net::HttpResponseHeaders
* original_response_headers
,
988 scoped_refptr
<net::HttpResponseHeaders
>* override_response_headers
,
989 extensions::WarningSet
* conflicting_extensions
,
990 const net::BoundNetLog
* net_log
) {
991 // Skip all work if there are no registered cookie modifications.
992 bool cookie_modifications_exist
= false;
993 EventResponseDeltas::const_reverse_iterator delta
;
994 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
995 cookie_modifications_exist
|=
996 !(*delta
)->response_cookie_modifications
.empty();
998 if (!cookie_modifications_exist
)
1001 // Only create a copy if we really want to modify the response headers.
1002 if (override_response_headers
->get() == NULL
) {
1003 *override_response_headers
= new net::HttpResponseHeaders(
1004 original_response_headers
->raw_headers());
1007 ParsedResponseCookies cookies
=
1008 GetResponseCookies(*override_response_headers
);
1010 bool modified
= false;
1011 modified
|= MergeAddResponseCookieModifications(deltas
, &cookies
);
1012 modified
|= MergeEditResponseCookieModifications(deltas
, &cookies
);
1013 modified
|= MergeRemoveResponseCookieModifications(deltas
, &cookies
);
1017 StoreResponseCookies(cookies
, *override_response_headers
);
1020 // Converts the key of the (key, value) pair to lower case.
1021 static ResponseHeader
ToLowerCase(const ResponseHeader
& header
) {
1022 std::string
lower_key(header
.first
);
1023 base::StringToLowerASCII(&lower_key
);
1024 return ResponseHeader(lower_key
, header
.second
);
1027 // Returns the extension ID of the first extension in |deltas| that removes the
1028 // request header identified by |key|.
1029 static std::string
FindRemoveResponseHeader(
1030 const EventResponseDeltas
& deltas
,
1031 const std::string
& key
) {
1032 std::string lower_key
= base::StringToLowerASCII(key
);
1033 EventResponseDeltas::const_iterator delta
;
1034 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
1035 ResponseHeaders::const_iterator i
;
1036 for (i
= (*delta
)->deleted_response_headers
.begin();
1037 i
!= (*delta
)->deleted_response_headers
.end(); ++i
) {
1038 if (base::StringToLowerASCII(i
->first
) == lower_key
)
1039 return (*delta
)->extension_id
;
1042 return std::string();
1045 void MergeOnHeadersReceivedResponses(
1046 const EventResponseDeltas
& deltas
,
1047 const net::HttpResponseHeaders
* original_response_headers
,
1048 scoped_refptr
<net::HttpResponseHeaders
>* override_response_headers
,
1049 GURL
* allowed_unsafe_redirect_url
,
1050 extensions::WarningSet
* conflicting_extensions
,
1051 const net::BoundNetLog
* net_log
) {
1052 EventResponseDeltas::const_iterator delta
;
1054 // Here we collect which headers we have removed or added so far due to
1055 // extensions of higher precedence. Header keys are always stored as
1057 std::set
<ResponseHeader
> removed_headers
;
1058 std::set
<ResponseHeader
> added_headers
;
1060 // We assume here that the deltas are sorted in decreasing extension
1061 // precedence (i.e. decreasing extension installation time).
1062 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
1063 if ((*delta
)->added_response_headers
.empty() &&
1064 (*delta
)->deleted_response_headers
.empty()) {
1068 // Only create a copy if we really want to modify the response headers.
1069 if (override_response_headers
->get() == NULL
) {
1070 *override_response_headers
= new net::HttpResponseHeaders(
1071 original_response_headers
->raw_headers());
1074 // We consider modifications as pairs of (delete, add) operations.
1075 // If a header is deleted twice by different extensions we assume that the
1076 // intention was to modify it to different values and consider this a
1077 // conflict. As deltas is sorted by decreasing extension installation order,
1078 // this takes care of precedence.
1079 bool extension_conflicts
= false;
1080 std::string conflicting_header
;
1081 std::string winning_extension_id
;
1082 ResponseHeaders::const_iterator i
;
1083 for (i
= (*delta
)->deleted_response_headers
.begin();
1084 i
!= (*delta
)->deleted_response_headers
.end(); ++i
) {
1085 if (removed_headers
.find(ToLowerCase(*i
)) != removed_headers
.end()) {
1086 winning_extension_id
= FindRemoveResponseHeader(deltas
, i
->first
);
1087 conflicting_header
= i
->first
;
1088 extension_conflicts
= true;
1093 // Now execute the modifications if there were no conflicts.
1094 if (!extension_conflicts
) {
1097 for (i
= (*delta
)->deleted_response_headers
.begin();
1098 i
!= (*delta
)->deleted_response_headers
.end(); ++i
) {
1099 (*override_response_headers
)->RemoveHeaderLine(i
->first
, i
->second
);
1100 removed_headers
.insert(ToLowerCase(*i
));
1106 for (i
= (*delta
)->added_response_headers
.begin();
1107 i
!= (*delta
)->added_response_headers
.end(); ++i
) {
1108 ResponseHeader
lowercase_header(ToLowerCase(*i
));
1109 if (added_headers
.find(lowercase_header
) != added_headers
.end())
1111 added_headers
.insert(lowercase_header
);
1112 (*override_response_headers
)->AddHeader(i
->first
+ ": " + i
->second
);
1116 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS
,
1117 CreateNetLogExtensionIdCallback(delta
->get()));
1119 conflicting_extensions
->insert(
1120 extensions::Warning::CreateResponseHeaderConflictWarning(
1121 (*delta
)->extension_id
, winning_extension_id
,
1122 conflicting_header
));
1124 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
1125 CreateNetLogExtensionIdCallback(delta
->get()));
1129 MergeCookiesInOnHeadersReceivedResponses(deltas
, original_response_headers
,
1130 override_response_headers
, conflicting_extensions
, net_log
);
1133 MergeRedirectUrlOfResponses(
1134 deltas
, &new_url
, conflicting_extensions
, net_log
);
1135 if (new_url
.is_valid()) {
1136 // Only create a copy if we really want to modify the response headers.
1137 if (override_response_headers
->get() == NULL
) {
1138 *override_response_headers
= new net::HttpResponseHeaders(
1139 original_response_headers
->raw_headers());
1141 (*override_response_headers
)->ReplaceStatusLine("HTTP/1.1 302 Found");
1142 (*override_response_headers
)->RemoveHeader("location");
1143 (*override_response_headers
)->AddHeader("Location: " + new_url
.spec());
1144 // Explicitly mark the URL as safe for redirection, to prevent the request
1145 // from being blocked because of net::ERR_UNSAFE_REDIRECT.
1146 *allowed_unsafe_redirect_url
= new_url
;
1150 bool MergeOnAuthRequiredResponses(
1151 const EventResponseDeltas
& deltas
,
1152 net::AuthCredentials
* auth_credentials
,
1153 extensions::WarningSet
* conflicting_extensions
,
1154 const net::BoundNetLog
* net_log
) {
1155 CHECK(auth_credentials
);
1156 bool credentials_set
= false;
1157 std::string winning_extension_id
;
1159 for (EventResponseDeltas::const_iterator delta
= deltas
.begin();
1160 delta
!= deltas
.end();
1162 if (!(*delta
)->auth_credentials
.get())
1165 auth_credentials
->username() !=
1166 (*delta
)->auth_credentials
->username() ||
1167 auth_credentials
->password() != (*delta
)->auth_credentials
->password();
1168 if (credentials_set
&& different
) {
1169 conflicting_extensions
->insert(
1170 extensions::Warning::CreateCredentialsConflictWarning(
1171 (*delta
)->extension_id
, winning_extension_id
));
1173 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
1174 CreateNetLogExtensionIdCallback(delta
->get()));
1177 net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS
,
1178 CreateNetLogExtensionIdCallback(delta
->get()));
1179 *auth_credentials
= *(*delta
)->auth_credentials
;
1180 credentials_set
= true;
1181 winning_extension_id
= (*delta
)->extension_id
;
1184 return credentials_set
;
1187 void ClearCacheOnNavigation() {
1188 if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
)) {
1189 ClearCacheOnNavigationOnUI();
1191 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
1192 base::Bind(&ClearCacheOnNavigationOnUI
));
1196 void NotifyWebRequestAPIUsed(void* browser_context_id
,
1197 const std::string
& extension_id
) {
1198 DCHECK(!extension_id
.empty());
1199 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
1200 content::BrowserContext
* browser_context
=
1201 reinterpret_cast<content::BrowserContext
*>(browser_context_id
);
1202 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(
1206 extensions::RuntimeData
* runtime_data
=
1207 extensions::ExtensionSystem::Get(browser_context
)->runtime_data();
1208 if (runtime_data
->HasUsedWebRequest(extension_id
))
1210 runtime_data
->SetHasUsedWebRequest(extension_id
, true);
1212 for (content::RenderProcessHost::iterator it
=
1213 content::RenderProcessHost::AllHostsIterator();
1214 !it
.IsAtEnd(); it
.Advance()) {
1215 content::RenderProcessHost
* host
= it
.GetCurrentValue();
1216 if (host
->GetBrowserContext() == browser_context
)
1217 SendExtensionWebRequestStatusToHost(host
);
1221 void SendExtensionWebRequestStatusToHost(content::RenderProcessHost
* host
) {
1222 content::BrowserContext
* browser_context
= host
->GetBrowserContext();
1223 if (!browser_context
)
1226 bool webrequest_used
= false;
1227 const extensions::ExtensionSet
& extensions
=
1228 extensions::ExtensionRegistry::Get(browser_context
)->enabled_extensions();
1229 extensions::RuntimeData
* runtime_data
=
1230 extensions::ExtensionSystem::Get(browser_context
)->runtime_data();
1231 for (extensions::ExtensionSet::const_iterator it
= extensions
.begin();
1232 !webrequest_used
&& it
!= extensions
.end();
1234 webrequest_used
|= runtime_data
->HasUsedWebRequest((*it
)->id());
1237 host
->Send(new ExtensionMsg_UsingWebRequestAPI(webrequest_used
));
1240 // Converts the |name|, |value| pair of a http header to a HttpHeaders
1241 // dictionary. Ownership is passed to the caller.
1242 base::DictionaryValue
* CreateHeaderDictionary(
1243 const std::string
& name
, const std::string
& value
) {
1244 base::DictionaryValue
* header
= new base::DictionaryValue();
1245 header
->SetString(keys::kHeaderNameKey
, name
);
1246 if (base::IsStringUTF8(value
)) {
1247 header
->SetString(keys::kHeaderValueKey
, value
);
1249 header
->Set(keys::kHeaderBinaryValueKey
,
1250 StringToCharList(value
));
1255 #define ARRAYEND(array) (array + arraysize(array))
1257 bool IsRelevantResourceType(ResourceType type
) {
1258 ResourceType
* iter
=
1259 std::find(kResourceTypeValues
,
1260 kResourceTypeValues
+ kResourceTypeValuesLength
,
1262 return iter
!= (kResourceTypeValues
+ kResourceTypeValuesLength
);
1265 const char* ResourceTypeToString(ResourceType type
) {
1266 ResourceType
* iter
=
1267 std::find(kResourceTypeValues
,
1268 kResourceTypeValues
+ kResourceTypeValuesLength
,
1270 if (iter
== (kResourceTypeValues
+ kResourceTypeValuesLength
))
1273 return kResourceTypeStrings
[iter
- kResourceTypeValues
];
1276 bool ParseResourceType(const std::string
& type_str
,
1277 ResourceType
* type
) {
1279 std::find(kResourceTypeStrings
,
1280 kResourceTypeStrings
+ kResourceTypeStringsLength
,
1282 if (iter
== (kResourceTypeStrings
+ kResourceTypeStringsLength
))
1284 *type
= kResourceTypeValues
[iter
- kResourceTypeStrings
];
1288 } // namespace extension_web_request_api_helpers