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
= base::ToLowerASCII(name
);
349 bool header_found
= false;
350 for (const auto& i
: *new_response_headers
) {
351 if (base::LowerCaseEqualsASCII(i
.first
, name_lowercase
) &&
358 result
->deleted_response_headers
.push_back(ResponseHeader(name
, value
));
362 // Find added headers (header keys are treated case insensitively).
364 for (const auto& i
: *new_response_headers
) {
365 std::string name_lowercase
= base::ToLowerASCII(i
.first
);
366 void* iter
= nullptr;
369 bool header_found
= false;
370 while (old_response_headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
371 if (base::LowerCaseEqualsASCII(name
, name_lowercase
) &&
378 result
->added_response_headers
.push_back(i
);
385 EventResponseDelta
* CalculateOnAuthRequiredDelta(
386 const std::string
& extension_id
,
387 const base::Time
& extension_install_time
,
389 scoped_ptr
<net::AuthCredentials
>* auth_credentials
) {
390 EventResponseDelta
* result
=
391 new EventResponseDelta(extension_id
, extension_install_time
);
392 result
->cancel
= cancel
;
393 result
->auth_credentials
.swap(*auth_credentials
);
397 void MergeCancelOfResponses(
398 const EventResponseDeltas
& deltas
,
400 const net::BoundNetLog
* net_log
) {
401 for (EventResponseDeltas::const_iterator i
= deltas
.begin();
402 i
!= deltas
.end(); ++i
) {
406 net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST
,
407 CreateNetLogExtensionIdCallback(i
->get()));
413 // Helper function for MergeRedirectUrlOfResponses() that allows ignoring
414 // all redirects but those to data:// urls and about:blank. This is important
415 // to treat these URLs as "cancel urls", i.e. URLs that extensions redirect
416 // to if they want to express that they want to cancel a request. This reduces
417 // the number of conflicts that we need to flag, as canceling is considered
418 // a higher precedence operation that redirects.
419 // Returns whether a redirect occurred.
420 static bool MergeRedirectUrlOfResponsesHelper(
421 const EventResponseDeltas
& deltas
,
423 extensions::WarningSet
* conflicting_extensions
,
424 const net::BoundNetLog
* net_log
,
425 bool consider_only_cancel_scheme_urls
) {
426 bool redirected
= false;
428 // Extension that determines the |new_url|.
429 std::string winning_extension_id
;
430 EventResponseDeltas::const_iterator delta
;
431 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
432 if ((*delta
)->new_url
.is_empty())
434 if (consider_only_cancel_scheme_urls
&&
435 !(*delta
)->new_url
.SchemeIs(url::kDataScheme
) &&
436 (*delta
)->new_url
.spec() != "about:blank") {
440 if (!redirected
|| *new_url
== (*delta
)->new_url
) {
441 *new_url
= (*delta
)->new_url
;
442 winning_extension_id
= (*delta
)->extension_id
;
445 net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST
,
446 CreateNetLogExtensionIdCallback(delta
->get()));
448 conflicting_extensions
->insert(
449 extensions::Warning::CreateRedirectConflictWarning(
450 (*delta
)->extension_id
,
451 winning_extension_id
,
455 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
456 CreateNetLogExtensionIdCallback(delta
->get()));
462 void MergeRedirectUrlOfResponses(
463 const EventResponseDeltas
& deltas
,
465 extensions::WarningSet
* conflicting_extensions
,
466 const net::BoundNetLog
* net_log
) {
468 // First handle only redirects to data:// URLs and about:blank. These are a
469 // special case as they represent a way of cancelling a request.
470 if (MergeRedirectUrlOfResponsesHelper(
471 deltas
, new_url
, conflicting_extensions
, net_log
, true)) {
472 // If any extension cancelled a request by redirecting to a data:// URL or
473 // about:blank, we don't consider the other redirects.
477 // Handle all other redirects.
478 MergeRedirectUrlOfResponsesHelper(
479 deltas
, new_url
, conflicting_extensions
, net_log
, false);
482 void MergeOnBeforeRequestResponses(
483 const EventResponseDeltas
& deltas
,
485 extensions::WarningSet
* conflicting_extensions
,
486 const net::BoundNetLog
* net_log
) {
487 MergeRedirectUrlOfResponses(deltas
, new_url
, conflicting_extensions
, net_log
);
490 static bool DoesRequestCookieMatchFilter(
491 const ParsedRequestCookie
& cookie
,
492 RequestCookie
* filter
) {
493 if (!filter
) return true;
494 if (filter
->name
.get() && cookie
.first
!= *filter
->name
) return false;
495 if (filter
->value
.get() && cookie
.second
!= *filter
->value
) return false;
499 // Applies all CookieModificationType::ADD operations for request cookies of
500 // |deltas| to |cookies|. Returns whether any cookie was added.
501 static bool MergeAddRequestCookieModifications(
502 const EventResponseDeltas
& deltas
,
503 ParsedRequestCookies
* cookies
) {
504 bool modified
= false;
505 // We assume here that the deltas are sorted in decreasing extension
506 // precedence (i.e. decreasing extension installation time).
507 EventResponseDeltas::const_reverse_iterator delta
;
508 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
509 const RequestCookieModifications
& modifications
=
510 (*delta
)->request_cookie_modifications
;
511 for (RequestCookieModifications::const_iterator mod
= modifications
.begin();
512 mod
!= modifications
.end(); ++mod
) {
513 if ((*mod
)->type
!= ADD
|| !(*mod
)->modification
.get())
515 std::string
* new_name
= (*mod
)->modification
->name
.get();
516 std::string
* new_value
= (*mod
)->modification
->value
.get();
517 if (!new_name
|| !new_value
)
520 bool cookie_with_same_name_found
= false;
521 for (ParsedRequestCookies::iterator cookie
= cookies
->begin();
522 cookie
!= cookies
->end() && !cookie_with_same_name_found
; ++cookie
) {
523 if (cookie
->first
== *new_name
) {
524 if (cookie
->second
!= *new_value
) {
525 cookie
->second
= *new_value
;
528 cookie_with_same_name_found
= true;
531 if (!cookie_with_same_name_found
) {
532 cookies
->push_back(std::make_pair(base::StringPiece(*new_name
),
533 base::StringPiece(*new_value
)));
541 // Applies all CookieModificationType::EDIT operations for request cookies of
542 // |deltas| to |cookies|. Returns whether any cookie was modified.
543 static bool MergeEditRequestCookieModifications(
544 const EventResponseDeltas
& deltas
,
545 ParsedRequestCookies
* cookies
) {
546 bool modified
= false;
547 // We assume here that the deltas are sorted in decreasing extension
548 // precedence (i.e. decreasing extension installation time).
549 EventResponseDeltas::const_reverse_iterator delta
;
550 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
551 const RequestCookieModifications
& modifications
=
552 (*delta
)->request_cookie_modifications
;
553 for (RequestCookieModifications::const_iterator mod
= modifications
.begin();
554 mod
!= modifications
.end(); ++mod
) {
555 if ((*mod
)->type
!= EDIT
|| !(*mod
)->modification
.get())
558 std::string
* new_value
= (*mod
)->modification
->value
.get();
559 RequestCookie
* filter
= (*mod
)->filter
.get();
560 for (ParsedRequestCookies::iterator cookie
= cookies
->begin();
561 cookie
!= cookies
->end(); ++cookie
) {
562 if (!DoesRequestCookieMatchFilter(*cookie
, filter
))
564 // If the edit operation tries to modify the cookie name, we just ignore
565 // this. We only modify the cookie value.
566 if (new_value
&& cookie
->second
!= *new_value
) {
567 cookie
->second
= *new_value
;
576 // Applies all CookieModificationType::REMOVE operations for request cookies of
577 // |deltas| to |cookies|. Returns whether any cookie was deleted.
578 static bool MergeRemoveRequestCookieModifications(
579 const EventResponseDeltas
& deltas
,
580 ParsedRequestCookies
* cookies
) {
581 bool modified
= false;
582 // We assume here that the deltas are sorted in decreasing extension
583 // precedence (i.e. decreasing extension installation time).
584 EventResponseDeltas::const_reverse_iterator delta
;
585 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
586 const RequestCookieModifications
& modifications
=
587 (*delta
)->request_cookie_modifications
;
588 for (RequestCookieModifications::const_iterator mod
= modifications
.begin();
589 mod
!= modifications
.end(); ++mod
) {
590 if ((*mod
)->type
!= REMOVE
)
593 RequestCookie
* filter
= (*mod
)->filter
.get();
594 ParsedRequestCookies::iterator i
= cookies
->begin();
595 while (i
!= cookies
->end()) {
596 if (DoesRequestCookieMatchFilter(*i
, filter
)) {
597 i
= cookies
->erase(i
);
608 void MergeCookiesInOnBeforeSendHeadersResponses(
609 const EventResponseDeltas
& deltas
,
610 net::HttpRequestHeaders
* request_headers
,
611 extensions::WarningSet
* conflicting_extensions
,
612 const net::BoundNetLog
* net_log
) {
613 // Skip all work if there are no registered cookie modifications.
614 bool cookie_modifications_exist
= false;
615 EventResponseDeltas::const_iterator delta
;
616 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
617 cookie_modifications_exist
|=
618 !(*delta
)->request_cookie_modifications
.empty();
620 if (!cookie_modifications_exist
)
623 // Parse old cookie line.
624 std::string cookie_header
;
625 request_headers
->GetHeader(net::HttpRequestHeaders::kCookie
, &cookie_header
);
626 ParsedRequestCookies cookies
;
627 net::cookie_util::ParseRequestCookieLine(cookie_header
, &cookies
);
630 bool modified
= false;
631 modified
|= MergeAddRequestCookieModifications(deltas
, &cookies
);
632 modified
|= MergeEditRequestCookieModifications(deltas
, &cookies
);
633 modified
|= MergeRemoveRequestCookieModifications(deltas
, &cookies
);
635 // Reassemble and store new cookie line.
637 std::string new_cookie_header
=
638 net::cookie_util::SerializeRequestCookieLine(cookies
);
639 request_headers
->SetHeader(net::HttpRequestHeaders::kCookie
,
644 // Returns the extension ID of the first extension in |deltas| that sets the
645 // request header identified by |key| to |value|.
646 static std::string
FindSetRequestHeader(
647 const EventResponseDeltas
& deltas
,
648 const std::string
& key
,
649 const std::string
& value
) {
650 EventResponseDeltas::const_iterator delta
;
651 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
652 net::HttpRequestHeaders::Iterator
modification(
653 (*delta
)->modified_request_headers
);
654 while (modification
.GetNext()) {
655 if (key
== modification
.name() && value
== modification
.value())
656 return (*delta
)->extension_id
;
659 return std::string();
662 // Returns the extension ID of the first extension in |deltas| that removes the
663 // request header identified by |key|.
664 static std::string
FindRemoveRequestHeader(
665 const EventResponseDeltas
& deltas
,
666 const std::string
& key
) {
667 EventResponseDeltas::const_iterator delta
;
668 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
669 std::vector
<std::string
>::iterator i
;
670 for (i
= (*delta
)->deleted_request_headers
.begin();
671 i
!= (*delta
)->deleted_request_headers
.end();
674 return (*delta
)->extension_id
;
677 return std::string();
680 void MergeOnBeforeSendHeadersResponses(
681 const EventResponseDeltas
& deltas
,
682 net::HttpRequestHeaders
* request_headers
,
683 extensions::WarningSet
* conflicting_extensions
,
684 const net::BoundNetLog
* net_log
) {
685 EventResponseDeltas::const_iterator delta
;
687 // Here we collect which headers we have removed or set to new values
688 // so far due to extensions of higher precedence.
689 std::set
<std::string
> removed_headers
;
690 std::set
<std::string
> set_headers
;
692 // We assume here that the deltas are sorted in decreasing extension
693 // precedence (i.e. decreasing extension installation time).
694 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
695 if ((*delta
)->modified_request_headers
.IsEmpty() &&
696 (*delta
)->deleted_request_headers
.empty()) {
700 // Check whether any modification affects a request header that
701 // has been modified differently before. As deltas is sorted by decreasing
702 // extension installation order, this takes care of precedence.
703 bool extension_conflicts
= false;
704 std::string winning_extension_id
;
705 std::string conflicting_header
;
707 net::HttpRequestHeaders::Iterator
modification(
708 (*delta
)->modified_request_headers
);
709 while (modification
.GetNext() && !extension_conflicts
) {
710 // This modification sets |key| to |value|.
711 const std::string
& key
= modification
.name();
712 const std::string
& value
= modification
.value();
714 // We must not delete anything that has been modified before.
715 if (removed_headers
.find(key
) != removed_headers
.end() &&
716 !extension_conflicts
) {
717 winning_extension_id
= FindRemoveRequestHeader(deltas
, key
);
718 conflicting_header
= key
;
719 extension_conflicts
= true;
722 // We must not modify anything that has been set to a *different*
724 if (set_headers
.find(key
) != set_headers
.end() &&
725 !extension_conflicts
) {
726 std::string current_value
;
727 if (!request_headers
->GetHeader(key
, ¤t_value
) ||
728 current_value
!= value
) {
729 winning_extension_id
=
730 FindSetRequestHeader(deltas
, key
, current_value
);
731 conflicting_header
= key
;
732 extension_conflicts
= true;
738 // Check whether any deletion affects a request header that has been
741 std::vector
<std::string
>::iterator key
;
742 for (key
= (*delta
)->deleted_request_headers
.begin();
743 key
!= (*delta
)->deleted_request_headers
.end() &&
744 !extension_conflicts
;
746 if (set_headers
.find(*key
) != set_headers
.end()) {
747 std::string current_value
;
748 request_headers
->GetHeader(*key
, ¤t_value
);
749 winning_extension_id
=
750 FindSetRequestHeader(deltas
, *key
, current_value
);
751 conflicting_header
= *key
;
752 extension_conflicts
= true;
757 // Now execute the modifications if there were no conflicts.
758 if (!extension_conflicts
) {
759 // Copy all modifications into the original headers.
760 request_headers
->MergeFrom((*delta
)->modified_request_headers
);
762 // Record which keys were changed.
763 net::HttpRequestHeaders::Iterator
modification(
764 (*delta
)->modified_request_headers
);
765 while (modification
.GetNext())
766 set_headers
.insert(modification
.name());
769 // Perform all deletions and record which keys were deleted.
771 std::vector
<std::string
>::iterator key
;
772 for (key
= (*delta
)->deleted_request_headers
.begin();
773 key
!= (*delta
)->deleted_request_headers
.end();
775 request_headers
->RemoveHeader(*key
);
776 removed_headers
.insert(*key
);
780 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS
,
781 base::Bind(&NetLogModificationCallback
, delta
->get()));
783 conflicting_extensions
->insert(
784 extensions::Warning::CreateRequestHeaderConflictWarning(
785 (*delta
)->extension_id
, winning_extension_id
,
786 conflicting_header
));
788 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
789 CreateNetLogExtensionIdCallback(delta
->get()));
793 MergeCookiesInOnBeforeSendHeadersResponses(deltas
, request_headers
,
794 conflicting_extensions
, net_log
);
797 // Retrives all cookies from |override_response_headers|.
798 static ParsedResponseCookies
GetResponseCookies(
799 scoped_refptr
<net::HttpResponseHeaders
> override_response_headers
) {
800 ParsedResponseCookies result
;
804 while (override_response_headers
->EnumerateHeader(&iter
, "Set-Cookie",
806 result
.push_back(make_linked_ptr(new net::ParsedCookie(value
)));
811 // Stores all |cookies| in |override_response_headers| deleting previously
812 // existing cookie definitions.
813 static void StoreResponseCookies(
814 const ParsedResponseCookies
& cookies
,
815 scoped_refptr
<net::HttpResponseHeaders
> override_response_headers
) {
816 override_response_headers
->RemoveHeader("Set-Cookie");
817 for (ParsedResponseCookies::const_iterator i
= cookies
.begin();
818 i
!= cookies
.end(); ++i
) {
819 override_response_headers
->AddHeader("Set-Cookie: " + (*i
)->ToCookieLine());
823 // Modifies |cookie| according to |modification|. Each value that is set in
824 // |modification| is applied to |cookie|.
825 static bool ApplyResponseCookieModification(ResponseCookie
* modification
,
826 net::ParsedCookie
* cookie
) {
827 bool modified
= false;
828 if (modification
->name
.get())
829 modified
|= cookie
->SetName(*modification
->name
);
830 if (modification
->value
.get())
831 modified
|= cookie
->SetValue(*modification
->value
);
832 if (modification
->expires
.get())
833 modified
|= cookie
->SetExpires(*modification
->expires
);
834 if (modification
->max_age
.get())
835 modified
|= cookie
->SetMaxAge(base::IntToString(*modification
->max_age
));
836 if (modification
->domain
.get())
837 modified
|= cookie
->SetDomain(*modification
->domain
);
838 if (modification
->path
.get())
839 modified
|= cookie
->SetPath(*modification
->path
);
840 if (modification
->secure
.get())
841 modified
|= cookie
->SetIsSecure(*modification
->secure
);
842 if (modification
->http_only
.get())
843 modified
|= cookie
->SetIsHttpOnly(*modification
->http_only
);
847 static bool DoesResponseCookieMatchFilter(net::ParsedCookie
* cookie
,
848 FilterResponseCookie
* filter
) {
849 if (!cookie
->IsValid()) return false;
850 if (!filter
) return true;
851 if (filter
->name
&& cookie
->Name() != *filter
->name
)
853 if (filter
->value
&& cookie
->Value() != *filter
->value
)
855 if (filter
->expires
) {
856 std::string actual_value
=
857 cookie
->HasExpires() ? cookie
->Expires() : std::string();
858 if (actual_value
!= *filter
->expires
)
861 if (filter
->max_age
) {
862 std::string actual_value
=
863 cookie
->HasMaxAge() ? cookie
->MaxAge() : std::string();
864 if (actual_value
!= base::IntToString(*filter
->max_age
))
867 if (filter
->domain
) {
868 std::string actual_value
=
869 cookie
->HasDomain() ? cookie
->Domain() : std::string();
870 if (actual_value
!= *filter
->domain
)
874 std::string actual_value
=
875 cookie
->HasPath() ? cookie
->Path() : std::string();
876 if (actual_value
!= *filter
->path
)
879 if (filter
->secure
&& cookie
->IsSecure() != *filter
->secure
)
881 if (filter
->http_only
&& cookie
->IsHttpOnly() != *filter
->http_only
)
883 if (filter
->age_upper_bound
|| filter
->age_lower_bound
||
884 (filter
->session_cookie
&& *filter
->session_cookie
)) {
885 int64 seconds_to_expiry
;
886 bool lifetime_parsed
= ParseCookieLifetime(cookie
, &seconds_to_expiry
);
887 if (filter
->age_upper_bound
&& seconds_to_expiry
> *filter
->age_upper_bound
)
889 if (filter
->age_lower_bound
&& seconds_to_expiry
< *filter
->age_lower_bound
)
891 if (filter
->session_cookie
&& *filter
->session_cookie
&& lifetime_parsed
)
897 // Applies all CookieModificationType::ADD operations for response cookies of
898 // |deltas| to |cookies|. Returns whether any cookie was added.
899 static bool MergeAddResponseCookieModifications(
900 const EventResponseDeltas
& deltas
,
901 ParsedResponseCookies
* cookies
) {
902 bool modified
= false;
903 // We assume here that the deltas are sorted in decreasing extension
904 // precedence (i.e. decreasing extension installation time).
905 EventResponseDeltas::const_reverse_iterator delta
;
906 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
907 const ResponseCookieModifications
& modifications
=
908 (*delta
)->response_cookie_modifications
;
909 for (ResponseCookieModifications::const_iterator mod
=
910 modifications
.begin(); mod
!= modifications
.end(); ++mod
) {
911 if ((*mod
)->type
!= ADD
|| !(*mod
)->modification
.get())
913 // Cookie names are not unique in response cookies so we always append
914 // and never override.
915 linked_ptr
<net::ParsedCookie
> cookie(
916 new net::ParsedCookie(std::string()));
917 ApplyResponseCookieModification((*mod
)->modification
.get(), cookie
.get());
918 cookies
->push_back(cookie
);
925 // Applies all CookieModificationType::EDIT operations for response cookies of
926 // |deltas| to |cookies|. Returns whether any cookie was modified.
927 static bool MergeEditResponseCookieModifications(
928 const EventResponseDeltas
& deltas
,
929 ParsedResponseCookies
* cookies
) {
930 bool modified
= false;
931 // We assume here that the deltas are sorted in decreasing extension
932 // precedence (i.e. decreasing extension installation time).
933 EventResponseDeltas::const_reverse_iterator delta
;
934 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
935 const ResponseCookieModifications
& modifications
=
936 (*delta
)->response_cookie_modifications
;
937 for (ResponseCookieModifications::const_iterator mod
=
938 modifications
.begin(); mod
!= modifications
.end(); ++mod
) {
939 if ((*mod
)->type
!= EDIT
|| !(*mod
)->modification
.get())
942 for (ParsedResponseCookies::iterator cookie
= cookies
->begin();
943 cookie
!= cookies
->end(); ++cookie
) {
944 if (DoesResponseCookieMatchFilter(cookie
->get(),
945 (*mod
)->filter
.get())) {
946 modified
|= ApplyResponseCookieModification(
947 (*mod
)->modification
.get(), cookie
->get());
955 // Applies all CookieModificationType::REMOVE operations for response cookies of
956 // |deltas| to |cookies|. Returns whether any cookie was deleted.
957 static bool MergeRemoveResponseCookieModifications(
958 const EventResponseDeltas
& deltas
,
959 ParsedResponseCookies
* cookies
) {
960 bool modified
= false;
961 // We assume here that the deltas are sorted in decreasing extension
962 // precedence (i.e. decreasing extension installation time).
963 EventResponseDeltas::const_reverse_iterator delta
;
964 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
965 const ResponseCookieModifications
& modifications
=
966 (*delta
)->response_cookie_modifications
;
967 for (ResponseCookieModifications::const_iterator mod
=
968 modifications
.begin(); mod
!= modifications
.end(); ++mod
) {
969 if ((*mod
)->type
!= REMOVE
)
972 ParsedResponseCookies::iterator i
= cookies
->begin();
973 while (i
!= cookies
->end()) {
974 if (DoesResponseCookieMatchFilter(i
->get(),
975 (*mod
)->filter
.get())) {
976 i
= cookies
->erase(i
);
987 void MergeCookiesInOnHeadersReceivedResponses(
988 const EventResponseDeltas
& deltas
,
989 const net::HttpResponseHeaders
* original_response_headers
,
990 scoped_refptr
<net::HttpResponseHeaders
>* override_response_headers
,
991 extensions::WarningSet
* conflicting_extensions
,
992 const net::BoundNetLog
* net_log
) {
993 // Skip all work if there are no registered cookie modifications.
994 bool cookie_modifications_exist
= false;
995 EventResponseDeltas::const_reverse_iterator delta
;
996 for (delta
= deltas
.rbegin(); delta
!= deltas
.rend(); ++delta
) {
997 cookie_modifications_exist
|=
998 !(*delta
)->response_cookie_modifications
.empty();
1000 if (!cookie_modifications_exist
)
1003 // Only create a copy if we really want to modify the response headers.
1004 if (override_response_headers
->get() == NULL
) {
1005 *override_response_headers
= new net::HttpResponseHeaders(
1006 original_response_headers
->raw_headers());
1009 ParsedResponseCookies cookies
=
1010 GetResponseCookies(*override_response_headers
);
1012 bool modified
= false;
1013 modified
|= MergeAddResponseCookieModifications(deltas
, &cookies
);
1014 modified
|= MergeEditResponseCookieModifications(deltas
, &cookies
);
1015 modified
|= MergeRemoveResponseCookieModifications(deltas
, &cookies
);
1019 StoreResponseCookies(cookies
, *override_response_headers
);
1022 // Converts the key of the (key, value) pair to lower case.
1023 static ResponseHeader
ToLowerCase(const ResponseHeader
& header
) {
1024 return ResponseHeader(base::ToLowerASCII(header
.first
), 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::ToLowerASCII(key
);
1033 for (const auto& delta
: deltas
) {
1034 for (const auto& deleted_hdr
: delta
->deleted_response_headers
) {
1035 if (base::ToLowerASCII(deleted_hdr
.first
) == lower_key
)
1036 return delta
->extension_id
;
1039 return std::string();
1042 void MergeOnHeadersReceivedResponses(
1043 const EventResponseDeltas
& deltas
,
1044 const net::HttpResponseHeaders
* original_response_headers
,
1045 scoped_refptr
<net::HttpResponseHeaders
>* override_response_headers
,
1046 GURL
* allowed_unsafe_redirect_url
,
1047 extensions::WarningSet
* conflicting_extensions
,
1048 const net::BoundNetLog
* net_log
) {
1049 EventResponseDeltas::const_iterator delta
;
1051 // Here we collect which headers we have removed or added so far due to
1052 // extensions of higher precedence. Header keys are always stored as
1054 std::set
<ResponseHeader
> removed_headers
;
1055 std::set
<ResponseHeader
> added_headers
;
1057 // We assume here that the deltas are sorted in decreasing extension
1058 // precedence (i.e. decreasing extension installation time).
1059 for (delta
= deltas
.begin(); delta
!= deltas
.end(); ++delta
) {
1060 if ((*delta
)->added_response_headers
.empty() &&
1061 (*delta
)->deleted_response_headers
.empty()) {
1065 // Only create a copy if we really want to modify the response headers.
1066 if (override_response_headers
->get() == NULL
) {
1067 *override_response_headers
= new net::HttpResponseHeaders(
1068 original_response_headers
->raw_headers());
1071 // We consider modifications as pairs of (delete, add) operations.
1072 // If a header is deleted twice by different extensions we assume that the
1073 // intention was to modify it to different values and consider this a
1074 // conflict. As deltas is sorted by decreasing extension installation order,
1075 // this takes care of precedence.
1076 bool extension_conflicts
= false;
1077 std::string conflicting_header
;
1078 std::string winning_extension_id
;
1079 ResponseHeaders::const_iterator i
;
1080 for (i
= (*delta
)->deleted_response_headers
.begin();
1081 i
!= (*delta
)->deleted_response_headers
.end(); ++i
) {
1082 if (removed_headers
.find(ToLowerCase(*i
)) != removed_headers
.end()) {
1083 winning_extension_id
= FindRemoveResponseHeader(deltas
, i
->first
);
1084 conflicting_header
= i
->first
;
1085 extension_conflicts
= true;
1090 // Now execute the modifications if there were no conflicts.
1091 if (!extension_conflicts
) {
1094 for (i
= (*delta
)->deleted_response_headers
.begin();
1095 i
!= (*delta
)->deleted_response_headers
.end(); ++i
) {
1096 (*override_response_headers
)->RemoveHeaderLine(i
->first
, i
->second
);
1097 removed_headers
.insert(ToLowerCase(*i
));
1103 for (i
= (*delta
)->added_response_headers
.begin();
1104 i
!= (*delta
)->added_response_headers
.end(); ++i
) {
1105 ResponseHeader
lowercase_header(ToLowerCase(*i
));
1106 if (added_headers
.find(lowercase_header
) != added_headers
.end())
1108 added_headers
.insert(lowercase_header
);
1109 (*override_response_headers
)->AddHeader(i
->first
+ ": " + i
->second
);
1113 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS
,
1114 CreateNetLogExtensionIdCallback(delta
->get()));
1116 conflicting_extensions
->insert(
1117 extensions::Warning::CreateResponseHeaderConflictWarning(
1118 (*delta
)->extension_id
, winning_extension_id
,
1119 conflicting_header
));
1121 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
1122 CreateNetLogExtensionIdCallback(delta
->get()));
1126 MergeCookiesInOnHeadersReceivedResponses(deltas
, original_response_headers
,
1127 override_response_headers
, conflicting_extensions
, net_log
);
1130 MergeRedirectUrlOfResponses(
1131 deltas
, &new_url
, conflicting_extensions
, net_log
);
1132 if (new_url
.is_valid()) {
1133 // Only create a copy if we really want to modify the response headers.
1134 if (override_response_headers
->get() == NULL
) {
1135 *override_response_headers
= new net::HttpResponseHeaders(
1136 original_response_headers
->raw_headers());
1138 (*override_response_headers
)->ReplaceStatusLine("HTTP/1.1 302 Found");
1139 (*override_response_headers
)->RemoveHeader("location");
1140 (*override_response_headers
)->AddHeader("Location: " + new_url
.spec());
1141 // Explicitly mark the URL as safe for redirection, to prevent the request
1142 // from being blocked because of net::ERR_UNSAFE_REDIRECT.
1143 *allowed_unsafe_redirect_url
= new_url
;
1147 bool MergeOnAuthRequiredResponses(
1148 const EventResponseDeltas
& deltas
,
1149 net::AuthCredentials
* auth_credentials
,
1150 extensions::WarningSet
* conflicting_extensions
,
1151 const net::BoundNetLog
* net_log
) {
1152 CHECK(auth_credentials
);
1153 bool credentials_set
= false;
1154 std::string winning_extension_id
;
1156 for (EventResponseDeltas::const_iterator delta
= deltas
.begin();
1157 delta
!= deltas
.end();
1159 if (!(*delta
)->auth_credentials
.get())
1162 auth_credentials
->username() !=
1163 (*delta
)->auth_credentials
->username() ||
1164 auth_credentials
->password() != (*delta
)->auth_credentials
->password();
1165 if (credentials_set
&& different
) {
1166 conflicting_extensions
->insert(
1167 extensions::Warning::CreateCredentialsConflictWarning(
1168 (*delta
)->extension_id
, winning_extension_id
));
1170 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT
,
1171 CreateNetLogExtensionIdCallback(delta
->get()));
1174 net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS
,
1175 CreateNetLogExtensionIdCallback(delta
->get()));
1176 *auth_credentials
= *(*delta
)->auth_credentials
;
1177 credentials_set
= true;
1178 winning_extension_id
= (*delta
)->extension_id
;
1181 return credentials_set
;
1184 void ClearCacheOnNavigation() {
1185 if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
)) {
1186 ClearCacheOnNavigationOnUI();
1188 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
1189 base::Bind(&ClearCacheOnNavigationOnUI
));
1193 void NotifyWebRequestAPIUsed(void* browser_context_id
,
1194 const std::string
& extension_id
) {
1195 DCHECK(!extension_id
.empty());
1196 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
1197 content::BrowserContext
* browser_context
=
1198 reinterpret_cast<content::BrowserContext
*>(browser_context_id
);
1199 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(
1203 extensions::RuntimeData
* runtime_data
=
1204 extensions::ExtensionSystem::Get(browser_context
)->runtime_data();
1205 if (runtime_data
->HasUsedWebRequest(extension_id
))
1207 runtime_data
->SetHasUsedWebRequest(extension_id
, true);
1209 for (content::RenderProcessHost::iterator it
=
1210 content::RenderProcessHost::AllHostsIterator();
1211 !it
.IsAtEnd(); it
.Advance()) {
1212 content::RenderProcessHost
* host
= it
.GetCurrentValue();
1213 if (host
->GetBrowserContext() == browser_context
)
1214 SendExtensionWebRequestStatusToHost(host
);
1218 void SendExtensionWebRequestStatusToHost(content::RenderProcessHost
* host
) {
1219 content::BrowserContext
* browser_context
= host
->GetBrowserContext();
1220 if (!browser_context
)
1223 bool webrequest_used
= false;
1224 const extensions::ExtensionSet
& extensions
=
1225 extensions::ExtensionRegistry::Get(browser_context
)->enabled_extensions();
1226 extensions::RuntimeData
* runtime_data
=
1227 extensions::ExtensionSystem::Get(browser_context
)->runtime_data();
1228 for (extensions::ExtensionSet::const_iterator it
= extensions
.begin();
1229 !webrequest_used
&& it
!= extensions
.end();
1231 webrequest_used
|= runtime_data
->HasUsedWebRequest((*it
)->id());
1234 host
->Send(new ExtensionMsg_UsingWebRequestAPI(webrequest_used
));
1237 // Converts the |name|, |value| pair of a http header to a HttpHeaders
1238 // dictionary. Ownership is passed to the caller.
1239 base::DictionaryValue
* CreateHeaderDictionary(
1240 const std::string
& name
, const std::string
& value
) {
1241 base::DictionaryValue
* header
= new base::DictionaryValue();
1242 header
->SetString(keys::kHeaderNameKey
, name
);
1243 if (base::IsStringUTF8(value
)) {
1244 header
->SetString(keys::kHeaderValueKey
, value
);
1246 header
->Set(keys::kHeaderBinaryValueKey
,
1247 StringToCharList(value
));
1252 #define ARRAYEND(array) (array + arraysize(array))
1254 bool IsRelevantResourceType(ResourceType type
) {
1255 ResourceType
* iter
=
1256 std::find(kResourceTypeValues
,
1257 kResourceTypeValues
+ kResourceTypeValuesLength
,
1259 return iter
!= (kResourceTypeValues
+ kResourceTypeValuesLength
);
1262 const char* ResourceTypeToString(ResourceType type
) {
1263 ResourceType
* iter
=
1264 std::find(kResourceTypeValues
,
1265 kResourceTypeValues
+ kResourceTypeValuesLength
,
1267 if (iter
== (kResourceTypeValues
+ kResourceTypeValuesLength
))
1270 return kResourceTypeStrings
[iter
- kResourceTypeValues
];
1273 bool ParseResourceType(const std::string
& type_str
,
1274 ResourceType
* type
) {
1276 std::find(kResourceTypeStrings
,
1277 kResourceTypeStrings
+ kResourceTypeStringsLength
,
1279 if (iter
== (kResourceTypeStrings
+ kResourceTypeStringsLength
))
1281 *type
= kResourceTypeValues
[iter
- kResourceTypeStrings
];
1285 } // namespace extension_web_request_api_helpers