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 "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/extensions/api/web_request/web_request_api.h"
17 #include "chrome/browser/extensions/extension_warning_set.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "chrome/browser/renderer_host/web_cache_manager.h"
20 #include "chrome/common/url_constants.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/render_process_host.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/browser/runtime_data.h"
25 #include "net/base/net_log.h"
26 #include "net/cookies/cookie_util.h"
27 #include "net/cookies/parsed_cookie.h"
28 #include "net/http/http_util.h"
29 #include "net/url_request/url_request.h"
31 // TODO(battre): move all static functions into an anonymous namespace at the
35 using extensions::ExtensionWarning;
37 namespace extension_web_request_api_helpers {
41 // A ParsedRequestCookie consists of the key and value of the cookie.
42 typedef std::pair<base::StringPiece, base::StringPiece> ParsedRequestCookie;
43 typedef std::vector<ParsedRequestCookie> ParsedRequestCookies;
44 typedef std::vector<linked_ptr<net::ParsedCookie> > ParsedResponseCookies;
46 static const char* kResourceTypeStrings[] = {
58 static ResourceType::Type kResourceTypeValues[] = {
59 ResourceType::MAIN_FRAME,
60 ResourceType::SUB_FRAME,
61 ResourceType::STYLESHEET,
66 ResourceType::LAST_TYPE, // represents "other"
67 // TODO(jochen): We duplicate the last entry, so the array's size is not a
68 // power of two. If it is, this triggers a bug in gcc 4.4 in Release builds
69 // (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949). Once we use a version
70 // of gcc with this bug fixed, or the array is changed so this duplicate
71 // entry is no longer required, this should be removed.
72 ResourceType::LAST_TYPE,
76 arraysize(kResourceTypeStrings) == arraysize(kResourceTypeValues),
77 keep_resource_types_in_sync);
79 void ClearCacheOnNavigationOnUI() {
80 WebCacheManager::GetInstance()->ClearCacheOnNavigation();
83 bool ParseCookieLifetime(net::ParsedCookie* cookie,
84 int64* seconds_till_expiry) {
85 // 'Max-Age' is processed first because according to:
86 // http://tools.ietf.org/html/rfc6265#section-5.3 'Max-Age' attribute
87 // overrides 'Expires' attribute.
88 if (cookie->HasMaxAge() &&
89 base::StringToInt64(cookie->MaxAge(), seconds_till_expiry)) {
93 Time parsed_expiry_time;
94 if (cookie->HasExpires())
95 parsed_expiry_time = net::cookie_util::ParseCookieTime(cookie->Expires());
97 if (!parsed_expiry_time.is_null()) {
98 *seconds_till_expiry =
99 ceil((parsed_expiry_time - Time::Now()).InSecondsF());
100 return *seconds_till_expiry >= 0;
105 bool NullableEquals(const int* a, const int* b) {
106 if ((a && !b) || (!a && b))
108 return (!a) || (*a == *b);
111 bool NullableEquals(const bool* a, const bool* b) {
112 if ((a && !b) || (!a && b))
114 return (!a) || (*a == *b);
117 bool NullableEquals(const std::string* a, const std::string* b) {
118 if ((a && !b) || (!a && b))
120 return (!a) || (*a == *b);
125 RequestCookie::RequestCookie() {}
126 RequestCookie::~RequestCookie() {}
128 bool NullableEquals(const RequestCookie* a, const RequestCookie* b) {
129 if ((a && !b) || (!a && b))
133 return NullableEquals(a->name.get(), b->name.get()) &&
134 NullableEquals(a->value.get(), b->value.get());
137 ResponseCookie::ResponseCookie() {}
138 ResponseCookie::~ResponseCookie() {}
140 bool NullableEquals(const ResponseCookie* a, const ResponseCookie* b) {
141 if ((a && !b) || (!a && b))
145 return NullableEquals(a->name.get(), b->name.get()) &&
146 NullableEquals(a->value.get(), b->value.get()) &&
147 NullableEquals(a->expires.get(), b->expires.get()) &&
148 NullableEquals(a->max_age.get(), b->max_age.get()) &&
149 NullableEquals(a->domain.get(), b->domain.get()) &&
150 NullableEquals(a->path.get(), b->path.get()) &&
151 NullableEquals(a->secure.get(), b->secure.get()) &&
152 NullableEquals(a->http_only.get(), b->http_only.get());
155 FilterResponseCookie::FilterResponseCookie() {}
156 FilterResponseCookie::~FilterResponseCookie() {}
158 bool NullableEquals(const FilterResponseCookie* a,
159 const FilterResponseCookie* b) {
160 if ((a && !b) || (!a && b))
164 return NullableEquals(a->age_lower_bound.get(), b->age_lower_bound.get()) &&
165 NullableEquals(a->age_upper_bound.get(), b->age_upper_bound.get()) &&
166 NullableEquals(a->session_cookie.get(), b->session_cookie.get());
169 RequestCookieModification::RequestCookieModification() {}
170 RequestCookieModification::~RequestCookieModification() {}
172 bool NullableEquals(const RequestCookieModification* a,
173 const RequestCookieModification* b) {
174 if ((a && !b) || (!a && b))
178 return NullableEquals(a->filter.get(), b->filter.get()) &&
179 NullableEquals(a->modification.get(), b->modification.get());
182 ResponseCookieModification::ResponseCookieModification() : type(ADD) {}
183 ResponseCookieModification::~ResponseCookieModification() {}
185 bool NullableEquals(const ResponseCookieModification* a,
186 const ResponseCookieModification* b) {
187 if ((a && !b) || (!a && b))
191 return a->type == b->type &&
192 NullableEquals(a->filter.get(), b->filter.get()) &&
193 NullableEquals(a->modification.get(), b->modification.get());
196 EventResponseDelta::EventResponseDelta(
197 const std::string& extension_id, const base::Time& extension_install_time)
198 : extension_id(extension_id),
199 extension_install_time(extension_install_time),
203 EventResponseDelta::~EventResponseDelta() {
207 // Creates a NetLog callback the returns a Value with the ID of the extension
208 // that caused an event. |delta| must remain valid for the lifetime of the
210 net::NetLog::ParametersCallback CreateNetLogExtensionIdCallback(
211 const EventResponseDelta* delta) {
212 return net::NetLog::StringCallback("extension_id", &delta->extension_id);
215 // Creates NetLog parameters to indicate that an extension modified a request.
216 // Caller takes ownership of returned value.
217 base::Value* NetLogModificationCallback(
218 const EventResponseDelta* delta,
219 net::NetLog::LogLevel log_level) {
220 base::DictionaryValue* dict = new base::DictionaryValue();
221 dict->SetString("extension_id", delta->extension_id);
223 base::ListValue* modified_headers = new base::ListValue();
224 net::HttpRequestHeaders::Iterator modification(
225 delta->modified_request_headers);
226 while (modification.GetNext()) {
227 std::string line = modification.name() + ": " + modification.value();
228 modified_headers->Append(new base::StringValue(line));
230 dict->Set("modified_headers", modified_headers);
232 base::ListValue* deleted_headers = new base::ListValue();
233 for (std::vector<std::string>::const_iterator key =
234 delta->deleted_request_headers.begin();
235 key != delta->deleted_request_headers.end();
237 deleted_headers->Append(new base::StringValue(*key));
239 dict->Set("deleted_headers", deleted_headers);
243 bool InDecreasingExtensionInstallationTimeOrder(
244 const linked_ptr<EventResponseDelta>& a,
245 const linked_ptr<EventResponseDelta>& b) {
246 return a->extension_install_time > b->extension_install_time;
249 base::ListValue* StringToCharList(const std::string& s) {
250 base::ListValue* result = new base::ListValue;
251 for (size_t i = 0, n = s.size(); i < n; ++i) {
253 new base::FundamentalValue(
254 *reinterpret_cast<const unsigned char*>(&s[i])));
259 bool CharListToString(const base::ListValue* list, std::string* out) {
262 const size_t list_length = list->GetSize();
263 out->resize(list_length);
265 for (size_t i = 0; i < list_length; ++i) {
266 if (!list->GetInteger(i, &value) || value < 0 || value > 255)
268 unsigned char tmp = static_cast<unsigned char>(value);
269 (*out)[i] = *reinterpret_cast<char*>(&tmp);
274 EventResponseDelta* CalculateOnBeforeRequestDelta(
275 const std::string& extension_id,
276 const base::Time& extension_install_time,
278 const GURL& new_url) {
279 EventResponseDelta* result =
280 new EventResponseDelta(extension_id, extension_install_time);
281 result->cancel = cancel;
282 result->new_url = new_url;
286 EventResponseDelta* CalculateOnBeforeSendHeadersDelta(
287 const std::string& extension_id,
288 const base::Time& extension_install_time,
290 net::HttpRequestHeaders* old_headers,
291 net::HttpRequestHeaders* new_headers) {
292 EventResponseDelta* result =
293 new EventResponseDelta(extension_id, extension_install_time);
294 result->cancel = cancel;
296 // The event listener might not have passed any new headers if he
297 // just wanted to cancel the request.
299 // Find deleted headers.
301 net::HttpRequestHeaders::Iterator i(*old_headers);
302 while (i.GetNext()) {
303 if (!new_headers->HasHeader(i.name())) {
304 result->deleted_request_headers.push_back(i.name());
309 // Find modified headers.
311 net::HttpRequestHeaders::Iterator i(*new_headers);
312 while (i.GetNext()) {
314 if (!old_headers->GetHeader(i.name(), &value) || i.value() != value) {
315 result->modified_request_headers.SetHeader(i.name(), i.value());
323 EventResponseDelta* CalculateOnHeadersReceivedDelta(
324 const std::string& extension_id,
325 const base::Time& extension_install_time,
328 const net::HttpResponseHeaders* old_response_headers,
329 ResponseHeaders* new_response_headers) {
330 EventResponseDelta* result =
331 new EventResponseDelta(extension_id, extension_install_time);
332 result->cancel = cancel;
333 result->new_url = new_url;
335 if (!new_response_headers)
338 // Find deleted headers (header keys are treated case insensitively).
343 while (old_response_headers->EnumerateHeaderLines(&iter, &name, &value)) {
344 std::string name_lowercase(name);
345 StringToLowerASCII(&name_lowercase);
347 bool header_found = false;
348 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
349 i != new_response_headers->end(); ++i) {
350 if (LowerCaseEqualsASCII(i->first, name_lowercase.c_str()) &&
351 value == i->second) {
357 result->deleted_response_headers.push_back(ResponseHeader(name, value));
361 // Find added headers (header keys are treated case insensitively).
363 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
364 i != new_response_headers->end(); ++i) {
367 bool header_found = false;
368 while (old_response_headers->EnumerateHeader(&iter, i->first, &value) &&
370 header_found = (value == i->second);
373 result->added_response_headers.push_back(*i);
380 EventResponseDelta* CalculateOnAuthRequiredDelta(
381 const std::string& extension_id,
382 const base::Time& extension_install_time,
384 scoped_ptr<net::AuthCredentials>* auth_credentials) {
385 EventResponseDelta* result =
386 new EventResponseDelta(extension_id, extension_install_time);
387 result->cancel = cancel;
388 result->auth_credentials.swap(*auth_credentials);
392 void MergeCancelOfResponses(
393 const EventResponseDeltas& deltas,
395 const net::BoundNetLog* net_log) {
396 for (EventResponseDeltas::const_iterator i = deltas.begin();
397 i != deltas.end(); ++i) {
401 net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST,
402 CreateNetLogExtensionIdCallback(i->get()));
408 // Helper function for MergeRedirectUrlOfResponses() that allows ignoring
409 // all redirects but those to data:// urls and about:blank. This is important
410 // to treat these URLs as "cancel urls", i.e. URLs that extensions redirect
411 // to if they want to express that they want to cancel a request. This reduces
412 // the number of conflicts that we need to flag, as canceling is considered
413 // a higher precedence operation that redirects.
414 // Returns whether a redirect occurred.
415 static bool MergeRedirectUrlOfResponsesHelper(
416 const EventResponseDeltas& deltas,
418 extensions::ExtensionWarningSet* conflicting_extensions,
419 const net::BoundNetLog* net_log,
420 bool consider_only_cancel_scheme_urls) {
421 bool redirected = false;
423 // Extension that determines the |new_url|.
424 std::string winning_extension_id;
425 EventResponseDeltas::const_iterator delta;
426 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
427 if ((*delta)->new_url.is_empty())
429 if (consider_only_cancel_scheme_urls &&
430 !(*delta)->new_url.SchemeIs(content::kDataScheme) &&
431 (*delta)->new_url.spec() != "about:blank") {
435 if (!redirected || *new_url == (*delta)->new_url) {
436 *new_url = (*delta)->new_url;
437 winning_extension_id = (*delta)->extension_id;
440 net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST,
441 CreateNetLogExtensionIdCallback(delta->get()));
443 conflicting_extensions->insert(
444 ExtensionWarning::CreateRedirectConflictWarning(
445 (*delta)->extension_id,
446 winning_extension_id,
450 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
451 CreateNetLogExtensionIdCallback(delta->get()));
457 void MergeRedirectUrlOfResponses(
458 const EventResponseDeltas& deltas,
460 extensions::ExtensionWarningSet* conflicting_extensions,
461 const net::BoundNetLog* net_log) {
463 // First handle only redirects to data:// URLs and about:blank. These are a
464 // special case as they represent a way of cancelling a request.
465 if (MergeRedirectUrlOfResponsesHelper(
466 deltas, new_url, conflicting_extensions, net_log, true)) {
467 // If any extension cancelled a request by redirecting to a data:// URL or
468 // about:blank, we don't consider the other redirects.
472 // Handle all other redirects.
473 MergeRedirectUrlOfResponsesHelper(
474 deltas, new_url, conflicting_extensions, net_log, false);
477 void MergeOnBeforeRequestResponses(
478 const EventResponseDeltas& deltas,
480 extensions::ExtensionWarningSet* conflicting_extensions,
481 const net::BoundNetLog* net_log) {
482 MergeRedirectUrlOfResponses(deltas, new_url, conflicting_extensions, net_log);
485 // Assumes that |header_value| is the cookie header value of a HTTP Request
486 // following the cookie-string schema of RFC 6265, section 4.2.1, and returns
487 // cookie name/value pairs. If cookie values are presented in double quotes,
488 // these will appear in |parsed| as well. We can assume that the cookie header
489 // is written by Chromium and therefore, well-formed.
490 static void ParseRequestCookieLine(
491 const std::string& header_value,
492 ParsedRequestCookies* parsed_cookies) {
493 std::string::const_iterator i = header_value.begin();
494 while (i != header_value.end()) {
495 // Here we are at the beginning of a cookie.
498 while (i != header_value.end() && *i == ' ') ++i;
499 if (i == header_value.end()) return;
502 std::string::const_iterator cookie_name_beginning = i;
503 while (i != header_value.end() && *i != '=') ++i;
504 base::StringPiece cookie_name(cookie_name_beginning, i);
506 // Find cookie value.
507 base::StringPiece cookie_value;
508 if (i != header_value.end()) { // Cookies may have no value.
510 std::string::const_iterator cookie_value_beginning = i;
513 while (i != header_value.end() && *i != '"') ++i;
514 if (i == header_value.end()) return;
516 cookie_value = base::StringPiece(cookie_value_beginning, i);
517 // i points to character after '"', potentially a ';'
519 while (i != header_value.end() && *i != ';') ++i;
520 cookie_value = base::StringPiece(cookie_value_beginning, i);
521 // i points to ';' or end of string.
524 parsed_cookies->push_back(make_pair(cookie_name, cookie_value));
526 if (i != header_value.end()) ++i;
530 // Writes all cookies of |parsed_cookies| into a HTTP Request header value
531 // that belongs to the "Cookie" header.
532 static std::string SerializeRequestCookieLine(
533 const ParsedRequestCookies& parsed_cookies) {
535 for (ParsedRequestCookies::const_iterator i = parsed_cookies.begin();
536 i != parsed_cookies.end(); ++i) {
539 buffer += i->first.as_string();
540 if (!i->second.empty())
541 buffer += "=" + i->second.as_string();
546 static bool DoesRequestCookieMatchFilter(
547 const ParsedRequestCookie& cookie,
548 RequestCookie* filter) {
549 if (!filter) return true;
550 if (filter->name.get() && cookie.first != *filter->name) return false;
551 if (filter->value.get() && cookie.second != *filter->value) return false;
555 // Applies all CookieModificationType::ADD operations for request cookies of
556 // |deltas| to |cookies|. Returns whether any cookie was added.
557 static bool MergeAddRequestCookieModifications(
558 const EventResponseDeltas& deltas,
559 ParsedRequestCookies* cookies) {
560 bool modified = false;
561 // We assume here that the deltas are sorted in decreasing extension
562 // precedence (i.e. decreasing extension installation time).
563 EventResponseDeltas::const_reverse_iterator delta;
564 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
565 const RequestCookieModifications& modifications =
566 (*delta)->request_cookie_modifications;
567 for (RequestCookieModifications::const_iterator mod = modifications.begin();
568 mod != modifications.end(); ++mod) {
569 if ((*mod)->type != ADD || !(*mod)->modification.get())
571 std::string* new_name = (*mod)->modification->name.get();
572 std::string* new_value = (*mod)->modification->value.get();
573 if (!new_name || !new_value)
576 bool cookie_with_same_name_found = false;
577 for (ParsedRequestCookies::iterator cookie = cookies->begin();
578 cookie != cookies->end() && !cookie_with_same_name_found; ++cookie) {
579 if (cookie->first == *new_name) {
580 if (cookie->second != *new_value) {
581 cookie->second = *new_value;
584 cookie_with_same_name_found = true;
587 if (!cookie_with_same_name_found) {
588 cookies->push_back(std::make_pair(base::StringPiece(*new_name),
589 base::StringPiece(*new_value)));
597 // Applies all CookieModificationType::EDIT operations for request cookies of
598 // |deltas| to |cookies|. Returns whether any cookie was modified.
599 static bool MergeEditRequestCookieModifications(
600 const EventResponseDeltas& deltas,
601 ParsedRequestCookies* cookies) {
602 bool modified = false;
603 // We assume here that the deltas are sorted in decreasing extension
604 // precedence (i.e. decreasing extension installation time).
605 EventResponseDeltas::const_reverse_iterator delta;
606 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
607 const RequestCookieModifications& modifications =
608 (*delta)->request_cookie_modifications;
609 for (RequestCookieModifications::const_iterator mod = modifications.begin();
610 mod != modifications.end(); ++mod) {
611 if ((*mod)->type != EDIT || !(*mod)->modification.get())
614 std::string* new_value = (*mod)->modification->value.get();
615 RequestCookie* filter = (*mod)->filter.get();
616 for (ParsedRequestCookies::iterator cookie = cookies->begin();
617 cookie != cookies->end(); ++cookie) {
618 if (!DoesRequestCookieMatchFilter(*cookie, filter))
620 // If the edit operation tries to modify the cookie name, we just ignore
621 // this. We only modify the cookie value.
622 if (new_value && cookie->second != *new_value) {
623 cookie->second = *new_value;
632 // Applies all CookieModificationType::REMOVE operations for request cookies of
633 // |deltas| to |cookies|. Returns whether any cookie was deleted.
634 static bool MergeRemoveRequestCookieModifications(
635 const EventResponseDeltas& deltas,
636 ParsedRequestCookies* cookies) {
637 bool modified = false;
638 // We assume here that the deltas are sorted in decreasing extension
639 // precedence (i.e. decreasing extension installation time).
640 EventResponseDeltas::const_reverse_iterator delta;
641 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
642 const RequestCookieModifications& modifications =
643 (*delta)->request_cookie_modifications;
644 for (RequestCookieModifications::const_iterator mod = modifications.begin();
645 mod != modifications.end(); ++mod) {
646 if ((*mod)->type != REMOVE)
649 RequestCookie* filter = (*mod)->filter.get();
650 ParsedRequestCookies::iterator i = cookies->begin();
651 while (i != cookies->end()) {
652 if (DoesRequestCookieMatchFilter(*i, filter)) {
653 i = cookies->erase(i);
664 void MergeCookiesInOnBeforeSendHeadersResponses(
665 const EventResponseDeltas& deltas,
666 net::HttpRequestHeaders* request_headers,
667 extensions::ExtensionWarningSet* conflicting_extensions,
668 const net::BoundNetLog* net_log) {
669 // Skip all work if there are no registered cookie modifications.
670 bool cookie_modifications_exist = false;
671 EventResponseDeltas::const_iterator delta;
672 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
673 cookie_modifications_exist |=
674 !(*delta)->request_cookie_modifications.empty();
676 if (!cookie_modifications_exist)
679 // Parse old cookie line.
680 std::string cookie_header;
681 request_headers->GetHeader(net::HttpRequestHeaders::kCookie, &cookie_header);
682 ParsedRequestCookies cookies;
683 ParseRequestCookieLine(cookie_header, &cookies);
686 bool modified = false;
687 modified |= MergeAddRequestCookieModifications(deltas, &cookies);
688 modified |= MergeEditRequestCookieModifications(deltas, &cookies);
689 modified |= MergeRemoveRequestCookieModifications(deltas, &cookies);
691 // Reassemble and store new cookie line.
693 std::string new_cookie_header = SerializeRequestCookieLine(cookies);
694 request_headers->SetHeader(net::HttpRequestHeaders::kCookie,
699 // Returns the extension ID of the first extension in |deltas| that sets the
700 // request header identified by |key| to |value|.
701 static std::string FindSetRequestHeader(
702 const EventResponseDeltas& deltas,
703 const std::string& key,
704 const std::string& value) {
705 EventResponseDeltas::const_iterator delta;
706 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
707 net::HttpRequestHeaders::Iterator modification(
708 (*delta)->modified_request_headers);
709 while (modification.GetNext()) {
710 if (key == modification.name() && value == modification.value())
711 return (*delta)->extension_id;
714 return std::string();
717 // Returns the extension ID of the first extension in |deltas| that removes the
718 // request header identified by |key|.
719 static std::string FindRemoveRequestHeader(
720 const EventResponseDeltas& deltas,
721 const std::string& key) {
722 EventResponseDeltas::const_iterator delta;
723 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
724 std::vector<std::string>::iterator i;
725 for (i = (*delta)->deleted_request_headers.begin();
726 i != (*delta)->deleted_request_headers.end();
729 return (*delta)->extension_id;
732 return std::string();
735 void MergeOnBeforeSendHeadersResponses(
736 const EventResponseDeltas& deltas,
737 net::HttpRequestHeaders* request_headers,
738 extensions::ExtensionWarningSet* conflicting_extensions,
739 const net::BoundNetLog* net_log) {
740 EventResponseDeltas::const_iterator delta;
742 // Here we collect which headers we have removed or set to new values
743 // so far due to extensions of higher precedence.
744 std::set<std::string> removed_headers;
745 std::set<std::string> set_headers;
747 // We assume here that the deltas are sorted in decreasing extension
748 // precedence (i.e. decreasing extension installation time).
749 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
750 if ((*delta)->modified_request_headers.IsEmpty() &&
751 (*delta)->deleted_request_headers.empty()) {
755 // Check whether any modification affects a request header that
756 // has been modified differently before. As deltas is sorted by decreasing
757 // extension installation order, this takes care of precedence.
758 bool extension_conflicts = false;
759 std::string winning_extension_id;
760 std::string conflicting_header;
762 net::HttpRequestHeaders::Iterator modification(
763 (*delta)->modified_request_headers);
764 while (modification.GetNext() && !extension_conflicts) {
765 // This modification sets |key| to |value|.
766 const std::string& key = modification.name();
767 const std::string& value = modification.value();
769 // We must not delete anything that has been modified before.
770 if (removed_headers.find(key) != removed_headers.end() &&
771 !extension_conflicts) {
772 winning_extension_id = FindRemoveRequestHeader(deltas, key);
773 conflicting_header = key;
774 extension_conflicts = true;
777 // We must not modify anything that has been set to a *different*
779 if (set_headers.find(key) != set_headers.end() &&
780 !extension_conflicts) {
781 std::string current_value;
782 if (!request_headers->GetHeader(key, ¤t_value) ||
783 current_value != value) {
784 winning_extension_id =
785 FindSetRequestHeader(deltas, key, current_value);
786 conflicting_header = key;
787 extension_conflicts = true;
793 // Check whether any deletion affects a request header that has been
796 std::vector<std::string>::iterator key;
797 for (key = (*delta)->deleted_request_headers.begin();
798 key != (*delta)->deleted_request_headers.end() &&
799 !extension_conflicts;
801 if (set_headers.find(*key) != set_headers.end()) {
802 std::string current_value;
803 request_headers->GetHeader(*key, ¤t_value);
804 winning_extension_id =
805 FindSetRequestHeader(deltas, *key, current_value);
806 conflicting_header = *key;
807 extension_conflicts = true;
812 // Now execute the modifications if there were no conflicts.
813 if (!extension_conflicts) {
814 // Copy all modifications into the original headers.
815 request_headers->MergeFrom((*delta)->modified_request_headers);
817 // Record which keys were changed.
818 net::HttpRequestHeaders::Iterator modification(
819 (*delta)->modified_request_headers);
820 while (modification.GetNext())
821 set_headers.insert(modification.name());
824 // Perform all deletions and record which keys were deleted.
826 std::vector<std::string>::iterator key;
827 for (key = (*delta)->deleted_request_headers.begin();
828 key != (*delta)->deleted_request_headers.end();
830 request_headers->RemoveHeader(*key);
831 removed_headers.insert(*key);
835 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
836 base::Bind(&NetLogModificationCallback, delta->get()));
838 conflicting_extensions->insert(
839 ExtensionWarning::CreateRequestHeaderConflictWarning(
840 (*delta)->extension_id, winning_extension_id,
841 conflicting_header));
843 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
844 CreateNetLogExtensionIdCallback(delta->get()));
848 MergeCookiesInOnBeforeSendHeadersResponses(deltas, request_headers,
849 conflicting_extensions, net_log);
852 // Retrives all cookies from |override_response_headers|.
853 static ParsedResponseCookies GetResponseCookies(
854 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
855 ParsedResponseCookies result;
859 while (override_response_headers->EnumerateHeader(&iter, "Set-Cookie",
861 result.push_back(make_linked_ptr(new net::ParsedCookie(value)));
866 // Stores all |cookies| in |override_response_headers| deleting previously
867 // existing cookie definitions.
868 static void StoreResponseCookies(
869 const ParsedResponseCookies& cookies,
870 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
871 override_response_headers->RemoveHeader("Set-Cookie");
872 for (ParsedResponseCookies::const_iterator i = cookies.begin();
873 i != cookies.end(); ++i) {
874 override_response_headers->AddHeader("Set-Cookie: " + (*i)->ToCookieLine());
878 // Modifies |cookie| according to |modification|. Each value that is set in
879 // |modification| is applied to |cookie|.
880 static bool ApplyResponseCookieModification(ResponseCookie* modification,
881 net::ParsedCookie* cookie) {
882 bool modified = false;
883 if (modification->name.get())
884 modified |= cookie->SetName(*modification->name);
885 if (modification->value.get())
886 modified |= cookie->SetValue(*modification->value);
887 if (modification->expires.get())
888 modified |= cookie->SetExpires(*modification->expires);
889 if (modification->max_age.get())
890 modified |= cookie->SetMaxAge(base::IntToString(*modification->max_age));
891 if (modification->domain.get())
892 modified |= cookie->SetDomain(*modification->domain);
893 if (modification->path.get())
894 modified |= cookie->SetPath(*modification->path);
895 if (modification->secure.get())
896 modified |= cookie->SetIsSecure(*modification->secure);
897 if (modification->http_only.get())
898 modified |= cookie->SetIsHttpOnly(*modification->http_only);
902 static bool DoesResponseCookieMatchFilter(net::ParsedCookie* cookie,
903 FilterResponseCookie* filter) {
904 if (!cookie->IsValid()) return false;
905 if (!filter) return true;
906 if (filter->name.get() && cookie->Name() != *filter->name) return false;
907 if (filter->value.get() && cookie->Value() != *filter->value) return false;
908 if (filter->expires.get()) {
909 std::string actual_value =
910 cookie->HasExpires() ? cookie->Expires() : std::string();
911 if (actual_value != *filter->expires)
914 if (filter->max_age.get()) {
915 std::string actual_value =
916 cookie->HasMaxAge() ? cookie->MaxAge() : std::string();
917 if (actual_value != base::IntToString(*filter->max_age))
920 if (filter->domain.get()) {
921 std::string actual_value =
922 cookie->HasDomain() ? cookie->Domain() : std::string();
923 if (actual_value != *filter->domain)
926 if (filter->path.get()) {
927 std::string actual_value =
928 cookie->HasPath() ? cookie->Path() : std::string();
929 if (actual_value != *filter->path)
932 if (filter->secure.get() && cookie->IsSecure() != *filter->secure)
934 if (filter->http_only.get() && cookie->IsHttpOnly() != *filter->http_only)
936 int64 seconds_till_expiry;
937 bool lifetime_parsed = false;
938 if (filter->age_upper_bound.get() ||
939 filter->age_lower_bound.get() ||
940 (filter->session_cookie.get() && *filter->session_cookie)) {
941 lifetime_parsed = ParseCookieLifetime(cookie, &seconds_till_expiry);
943 if (filter->age_upper_bound.get()) {
944 if (seconds_till_expiry > *filter->age_upper_bound)
947 if (filter->age_lower_bound.get()) {
948 if (seconds_till_expiry < *filter->age_lower_bound)
951 if (filter->session_cookie.get() &&
952 *filter->session_cookie &&
959 // Applies all CookieModificationType::ADD operations for response cookies of
960 // |deltas| to |cookies|. Returns whether any cookie was added.
961 static bool MergeAddResponseCookieModifications(
962 const EventResponseDeltas& deltas,
963 ParsedResponseCookies* cookies) {
964 bool modified = false;
965 // We assume here that the deltas are sorted in decreasing extension
966 // precedence (i.e. decreasing extension installation time).
967 EventResponseDeltas::const_reverse_iterator delta;
968 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
969 const ResponseCookieModifications& modifications =
970 (*delta)->response_cookie_modifications;
971 for (ResponseCookieModifications::const_iterator mod =
972 modifications.begin(); mod != modifications.end(); ++mod) {
973 if ((*mod)->type != ADD || !(*mod)->modification.get())
975 // Cookie names are not unique in response cookies so we always append
976 // and never override.
977 linked_ptr<net::ParsedCookie> cookie(
978 new net::ParsedCookie(std::string()));
979 ApplyResponseCookieModification((*mod)->modification.get(), cookie.get());
980 cookies->push_back(cookie);
987 // Applies all CookieModificationType::EDIT operations for response cookies of
988 // |deltas| to |cookies|. Returns whether any cookie was modified.
989 static bool MergeEditResponseCookieModifications(
990 const EventResponseDeltas& deltas,
991 ParsedResponseCookies* cookies) {
992 bool modified = false;
993 // We assume here that the deltas are sorted in decreasing extension
994 // precedence (i.e. decreasing extension installation time).
995 EventResponseDeltas::const_reverse_iterator delta;
996 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
997 const ResponseCookieModifications& modifications =
998 (*delta)->response_cookie_modifications;
999 for (ResponseCookieModifications::const_iterator mod =
1000 modifications.begin(); mod != modifications.end(); ++mod) {
1001 if ((*mod)->type != EDIT || !(*mod)->modification.get())
1004 for (ParsedResponseCookies::iterator cookie = cookies->begin();
1005 cookie != cookies->end(); ++cookie) {
1006 if (DoesResponseCookieMatchFilter(cookie->get(),
1007 (*mod)->filter.get())) {
1008 modified |= ApplyResponseCookieModification(
1009 (*mod)->modification.get(), cookie->get());
1017 // Applies all CookieModificationType::REMOVE operations for response cookies of
1018 // |deltas| to |cookies|. Returns whether any cookie was deleted.
1019 static bool MergeRemoveResponseCookieModifications(
1020 const EventResponseDeltas& deltas,
1021 ParsedResponseCookies* cookies) {
1022 bool modified = false;
1023 // We assume here that the deltas are sorted in decreasing extension
1024 // precedence (i.e. decreasing extension installation time).
1025 EventResponseDeltas::const_reverse_iterator delta;
1026 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
1027 const ResponseCookieModifications& modifications =
1028 (*delta)->response_cookie_modifications;
1029 for (ResponseCookieModifications::const_iterator mod =
1030 modifications.begin(); mod != modifications.end(); ++mod) {
1031 if ((*mod)->type != REMOVE)
1034 ParsedResponseCookies::iterator i = cookies->begin();
1035 while (i != cookies->end()) {
1036 if (DoesResponseCookieMatchFilter(i->get(),
1037 (*mod)->filter.get())) {
1038 i = cookies->erase(i);
1049 void MergeCookiesInOnHeadersReceivedResponses(
1050 const EventResponseDeltas& deltas,
1051 const net::HttpResponseHeaders* original_response_headers,
1052 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
1053 extensions::ExtensionWarningSet* conflicting_extensions,
1054 const net::BoundNetLog* net_log) {
1055 // Skip all work if there are no registered cookie modifications.
1056 bool cookie_modifications_exist = false;
1057 EventResponseDeltas::const_reverse_iterator delta;
1058 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
1059 cookie_modifications_exist |=
1060 !(*delta)->response_cookie_modifications.empty();
1062 if (!cookie_modifications_exist)
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 ParsedResponseCookies cookies =
1072 GetResponseCookies(*override_response_headers);
1074 bool modified = false;
1075 modified |= MergeAddResponseCookieModifications(deltas, &cookies);
1076 modified |= MergeEditResponseCookieModifications(deltas, &cookies);
1077 modified |= MergeRemoveResponseCookieModifications(deltas, &cookies);
1081 StoreResponseCookies(cookies, *override_response_headers);
1084 // Converts the key of the (key, value) pair to lower case.
1085 static ResponseHeader ToLowerCase(const ResponseHeader& header) {
1086 std::string lower_key(header.first);
1087 StringToLowerASCII(&lower_key);
1088 return ResponseHeader(lower_key, header.second);
1091 // Returns the extension ID of the first extension in |deltas| that removes the
1092 // request header identified by |key|.
1093 static std::string FindRemoveResponseHeader(
1094 const EventResponseDeltas& deltas,
1095 const std::string& key) {
1096 std::string lower_key = StringToLowerASCII(key);
1097 EventResponseDeltas::const_iterator delta;
1098 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1099 ResponseHeaders::const_iterator i;
1100 for (i = (*delta)->deleted_response_headers.begin();
1101 i != (*delta)->deleted_response_headers.end(); ++i) {
1102 if (StringToLowerASCII(i->first) == lower_key)
1103 return (*delta)->extension_id;
1106 return std::string();
1109 void MergeOnHeadersReceivedResponses(
1110 const EventResponseDeltas& deltas,
1111 const net::HttpResponseHeaders* original_response_headers,
1112 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
1113 GURL* allowed_unsafe_redirect_url,
1114 extensions::ExtensionWarningSet* conflicting_extensions,
1115 const net::BoundNetLog* net_log) {
1116 EventResponseDeltas::const_iterator delta;
1118 // Here we collect which headers we have removed or added so far due to
1119 // extensions of higher precedence. Header keys are always stored as
1121 std::set<ResponseHeader> removed_headers;
1122 std::set<ResponseHeader> added_headers;
1124 // We assume here that the deltas are sorted in decreasing extension
1125 // precedence (i.e. decreasing extension installation time).
1126 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1127 if ((*delta)->added_response_headers.empty() &&
1128 (*delta)->deleted_response_headers.empty()) {
1132 // Only create a copy if we really want to modify the response headers.
1133 if (override_response_headers->get() == NULL) {
1134 *override_response_headers = new net::HttpResponseHeaders(
1135 original_response_headers->raw_headers());
1138 // We consider modifications as pairs of (delete, add) operations.
1139 // If a header is deleted twice by different extensions we assume that the
1140 // intention was to modify it to different values and consider this a
1141 // conflict. As deltas is sorted by decreasing extension installation order,
1142 // this takes care of precedence.
1143 bool extension_conflicts = false;
1144 std::string conflicting_header;
1145 std::string winning_extension_id;
1146 ResponseHeaders::const_iterator i;
1147 for (i = (*delta)->deleted_response_headers.begin();
1148 i != (*delta)->deleted_response_headers.end(); ++i) {
1149 if (removed_headers.find(ToLowerCase(*i)) != removed_headers.end()) {
1150 winning_extension_id = FindRemoveResponseHeader(deltas, i->first);
1151 conflicting_header = i->first;
1152 extension_conflicts = true;
1157 // Now execute the modifications if there were no conflicts.
1158 if (!extension_conflicts) {
1161 for (i = (*delta)->deleted_response_headers.begin();
1162 i != (*delta)->deleted_response_headers.end(); ++i) {
1163 (*override_response_headers)->RemoveHeaderLine(i->first, i->second);
1164 removed_headers.insert(ToLowerCase(*i));
1170 for (i = (*delta)->added_response_headers.begin();
1171 i != (*delta)->added_response_headers.end(); ++i) {
1172 ResponseHeader lowercase_header(ToLowerCase(*i));
1173 if (added_headers.find(lowercase_header) != added_headers.end())
1175 added_headers.insert(lowercase_header);
1176 (*override_response_headers)->AddHeader(i->first + ": " + i->second);
1180 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
1181 CreateNetLogExtensionIdCallback(delta->get()));
1183 conflicting_extensions->insert(
1184 ExtensionWarning::CreateResponseHeaderConflictWarning(
1185 (*delta)->extension_id, winning_extension_id,
1186 conflicting_header));
1188 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1189 CreateNetLogExtensionIdCallback(delta->get()));
1193 MergeCookiesInOnHeadersReceivedResponses(deltas, original_response_headers,
1194 override_response_headers, conflicting_extensions, net_log);
1197 MergeRedirectUrlOfResponses(
1198 deltas, &new_url, conflicting_extensions, net_log);
1199 if (new_url.is_valid()) {
1200 // Only create a copy if we really want to modify the response headers.
1201 if (override_response_headers->get() == NULL) {
1202 *override_response_headers = new net::HttpResponseHeaders(
1203 original_response_headers->raw_headers());
1205 (*override_response_headers)->ReplaceStatusLine("HTTP/1.1 302 Found");
1206 (*override_response_headers)->RemoveHeader("location");
1207 (*override_response_headers)->AddHeader("Location: " + new_url.spec());
1208 // Explicitly mark the URL as safe for redirection, to prevent the request
1209 // from being blocked because of net::ERR_UNSAFE_REDIRECT.
1210 *allowed_unsafe_redirect_url = new_url;
1214 bool MergeOnAuthRequiredResponses(
1215 const EventResponseDeltas& deltas,
1216 net::AuthCredentials* auth_credentials,
1217 extensions::ExtensionWarningSet* conflicting_extensions,
1218 const net::BoundNetLog* net_log) {
1219 CHECK(auth_credentials);
1220 bool credentials_set = false;
1221 std::string winning_extension_id;
1223 for (EventResponseDeltas::const_iterator delta = deltas.begin();
1224 delta != deltas.end();
1226 if (!(*delta)->auth_credentials.get())
1229 auth_credentials->username() !=
1230 (*delta)->auth_credentials->username() ||
1231 auth_credentials->password() != (*delta)->auth_credentials->password();
1232 if (credentials_set && different) {
1233 conflicting_extensions->insert(
1234 ExtensionWarning::CreateCredentialsConflictWarning(
1235 (*delta)->extension_id, winning_extension_id));
1237 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1238 CreateNetLogExtensionIdCallback(delta->get()));
1241 net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS,
1242 CreateNetLogExtensionIdCallback(delta->get()));
1243 *auth_credentials = *(*delta)->auth_credentials;
1244 credentials_set = true;
1245 winning_extension_id = (*delta)->extension_id;
1248 return credentials_set;
1252 #define ARRAYEND(array) (array + arraysize(array))
1254 bool IsRelevantResourceType(ResourceType::Type type) {
1255 ResourceType::Type* iter =
1256 std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
1257 return iter != ARRAYEND(kResourceTypeValues);
1260 const char* ResourceTypeToString(ResourceType::Type type) {
1261 ResourceType::Type* iter =
1262 std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
1263 if (iter == ARRAYEND(kResourceTypeValues))
1266 return kResourceTypeStrings[iter - kResourceTypeValues];
1269 bool ParseResourceType(const std::string& type_str,
1270 ResourceType::Type* type) {
1272 std::find(kResourceTypeStrings, ARRAYEND(kResourceTypeStrings), type_str);
1273 if (iter == ARRAYEND(kResourceTypeStrings))
1275 *type = kResourceTypeValues[iter - kResourceTypeStrings];
1279 void ClearCacheOnNavigation() {
1280 if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
1281 ClearCacheOnNavigationOnUI();
1283 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
1284 base::Bind(&ClearCacheOnNavigationOnUI));
1288 void NotifyWebRequestAPIUsed(
1290 scoped_refptr<const extensions::Extension> extension) {
1291 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1292 Profile* profile = reinterpret_cast<Profile*>(profile_id);
1293 if (!g_browser_process->profile_manager()->IsValidProfile(profile))
1296 extensions::RuntimeData* runtime_data =
1297 extensions::ExtensionSystem::Get(profile)->runtime_data();
1298 if (runtime_data->HasUsedWebRequest(extension.get()))
1300 runtime_data->SetHasUsedWebRequest(extension.get(), true);
1302 content::BrowserContext* browser_context = profile;
1303 for (content::RenderProcessHost::iterator it =
1304 content::RenderProcessHost::AllHostsIterator();
1305 !it.IsAtEnd(); it.Advance()) {
1306 content::RenderProcessHost* host = it.GetCurrentValue();
1307 if (host->GetBrowserContext() == browser_context)
1308 SendExtensionWebRequestStatusToHost(host);
1312 bool IsValidHeaderName(const std::string& name) {
1313 // Check whether the header name is RFC 2616-compliant.
1314 return net::HttpUtil::IsToken(name);
1317 bool IsValidHeaderValue(const std::string& value) {
1318 // Just a sanity check: disallow NUL and CRLF.
1319 return value.find('\0') == std::string::npos &&
1320 value.find("\r\n") == std::string::npos;
1323 } // namespace extension_web_request_api_helpers