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,
327 const net::HttpResponseHeaders* old_response_headers,
328 ResponseHeaders* new_response_headers) {
329 EventResponseDelta* result =
330 new EventResponseDelta(extension_id, extension_install_time);
331 result->cancel = cancel;
333 if (!new_response_headers)
336 // Find deleted headers (header keys are treated case insensitively).
341 while (old_response_headers->EnumerateHeaderLines(&iter, &name, &value)) {
342 std::string name_lowercase(name);
343 StringToLowerASCII(&name_lowercase);
345 bool header_found = false;
346 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
347 i != new_response_headers->end(); ++i) {
348 if (LowerCaseEqualsASCII(i->first, name_lowercase.c_str()) &&
349 value == i->second) {
355 result->deleted_response_headers.push_back(ResponseHeader(name, value));
359 // Find added headers (header keys are treated case insensitively).
361 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
362 i != new_response_headers->end(); ++i) {
365 bool header_found = false;
366 while (old_response_headers->EnumerateHeader(&iter, i->first, &value) &&
368 header_found = (value == i->second);
371 result->added_response_headers.push_back(*i);
378 EventResponseDelta* CalculateOnAuthRequiredDelta(
379 const std::string& extension_id,
380 const base::Time& extension_install_time,
382 scoped_ptr<net::AuthCredentials>* auth_credentials) {
383 EventResponseDelta* result =
384 new EventResponseDelta(extension_id, extension_install_time);
385 result->cancel = cancel;
386 result->auth_credentials.swap(*auth_credentials);
390 void MergeCancelOfResponses(
391 const EventResponseDeltas& deltas,
393 const net::BoundNetLog* net_log) {
394 for (EventResponseDeltas::const_iterator i = deltas.begin();
395 i != deltas.end(); ++i) {
399 net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST,
400 CreateNetLogExtensionIdCallback(i->get()));
406 // Helper function for MergeOnBeforeRequestResponses() that allows ignoring
407 // all redirects but those to data:// urls and about:blank. This is important
408 // to treat these URLs as "cancel urls", i.e. URLs that extensions redirect
409 // to if they want to express that they want to cancel a request. This reduces
410 // the number of conflicts that we need to flag, as canceling is considered
411 // a higher precedence operation that redirects.
412 // Returns whether a redirect occurred.
413 static bool MergeOnBeforeRequestResponsesHelper(
414 const EventResponseDeltas& deltas,
416 extensions::ExtensionWarningSet* conflicting_extensions,
417 const net::BoundNetLog* net_log,
418 bool consider_only_cancel_scheme_urls) {
419 bool redirected = false;
421 // Extension that determines the |new_url|.
422 std::string winning_extension_id;
423 EventResponseDeltas::const_iterator delta;
424 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
425 if ((*delta)->new_url.is_empty())
427 if (consider_only_cancel_scheme_urls &&
428 !(*delta)->new_url.SchemeIs(content::kDataScheme) &&
429 (*delta)->new_url.spec() != "about:blank") {
433 if (!redirected || *new_url == (*delta)->new_url) {
434 *new_url = (*delta)->new_url;
435 winning_extension_id = (*delta)->extension_id;
438 net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST,
439 CreateNetLogExtensionIdCallback(delta->get()));
441 conflicting_extensions->insert(
442 ExtensionWarning::CreateRedirectConflictWarning(
443 (*delta)->extension_id,
444 winning_extension_id,
448 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
449 CreateNetLogExtensionIdCallback(delta->get()));
455 void MergeOnBeforeRequestResponses(
456 const EventResponseDeltas& deltas,
458 extensions::ExtensionWarningSet* conflicting_extensions,
459 const net::BoundNetLog* net_log) {
461 // First handle only redirects to data:// URLs and about:blank. These are a
462 // special case as they represent a way of cancelling a request.
463 if (MergeOnBeforeRequestResponsesHelper(
464 deltas, new_url, conflicting_extensions, net_log, true)) {
465 // If any extension cancelled a request by redirecting to a data:// URL or
466 // about:blank, we don't consider the other redirects.
470 // Handle all other redirects.
471 MergeOnBeforeRequestResponsesHelper(
472 deltas, new_url, conflicting_extensions, net_log, false);
475 // Assumes that |header_value| is the cookie header value of a HTTP Request
476 // following the cookie-string schema of RFC 6265, section 4.2.1, and returns
477 // cookie name/value pairs. If cookie values are presented in double quotes,
478 // these will appear in |parsed| as well. We can assume that the cookie header
479 // is written by Chromium and therefore, well-formed.
480 static void ParseRequestCookieLine(
481 const std::string& header_value,
482 ParsedRequestCookies* parsed_cookies) {
483 std::string::const_iterator i = header_value.begin();
484 while (i != header_value.end()) {
485 // Here we are at the beginning of a cookie.
488 while (i != header_value.end() && *i == ' ') ++i;
489 if (i == header_value.end()) return;
492 std::string::const_iterator cookie_name_beginning = i;
493 while (i != header_value.end() && *i != '=') ++i;
494 base::StringPiece cookie_name(cookie_name_beginning, i);
496 // Find cookie value.
497 base::StringPiece cookie_value;
498 if (i != header_value.end()) { // Cookies may have no value.
500 std::string::const_iterator cookie_value_beginning = i;
503 while (i != header_value.end() && *i != '"') ++i;
504 if (i == header_value.end()) return;
506 cookie_value = base::StringPiece(cookie_value_beginning, i);
507 // i points to character after '"', potentially a ';'
509 while (i != header_value.end() && *i != ';') ++i;
510 cookie_value = base::StringPiece(cookie_value_beginning, i);
511 // i points to ';' or end of string.
514 parsed_cookies->push_back(make_pair(cookie_name, cookie_value));
516 if (i != header_value.end()) ++i;
520 // Writes all cookies of |parsed_cookies| into a HTTP Request header value
521 // that belongs to the "Cookie" header.
522 static std::string SerializeRequestCookieLine(
523 const ParsedRequestCookies& parsed_cookies) {
525 for (ParsedRequestCookies::const_iterator i = parsed_cookies.begin();
526 i != parsed_cookies.end(); ++i) {
529 buffer += i->first.as_string();
530 if (!i->second.empty())
531 buffer += "=" + i->second.as_string();
536 static bool DoesRequestCookieMatchFilter(
537 const ParsedRequestCookie& cookie,
538 RequestCookie* filter) {
539 if (!filter) return true;
540 if (filter->name.get() && cookie.first != *filter->name) return false;
541 if (filter->value.get() && cookie.second != *filter->value) return false;
545 // Applies all CookieModificationType::ADD operations for request cookies of
546 // |deltas| to |cookies|. Returns whether any cookie was added.
547 static bool MergeAddRequestCookieModifications(
548 const EventResponseDeltas& deltas,
549 ParsedRequestCookies* cookies) {
550 bool modified = false;
551 // We assume here that the deltas are sorted in decreasing extension
552 // precedence (i.e. decreasing extension installation time).
553 EventResponseDeltas::const_reverse_iterator delta;
554 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
555 const RequestCookieModifications& modifications =
556 (*delta)->request_cookie_modifications;
557 for (RequestCookieModifications::const_iterator mod = modifications.begin();
558 mod != modifications.end(); ++mod) {
559 if ((*mod)->type != ADD || !(*mod)->modification.get())
561 std::string* new_name = (*mod)->modification->name.get();
562 std::string* new_value = (*mod)->modification->value.get();
563 if (!new_name || !new_value)
566 bool cookie_with_same_name_found = false;
567 for (ParsedRequestCookies::iterator cookie = cookies->begin();
568 cookie != cookies->end() && !cookie_with_same_name_found; ++cookie) {
569 if (cookie->first == *new_name) {
570 if (cookie->second != *new_value) {
571 cookie->second = *new_value;
574 cookie_with_same_name_found = true;
577 if (!cookie_with_same_name_found) {
578 cookies->push_back(std::make_pair(base::StringPiece(*new_name),
579 base::StringPiece(*new_value)));
587 // Applies all CookieModificationType::EDIT operations for request cookies of
588 // |deltas| to |cookies|. Returns whether any cookie was modified.
589 static bool MergeEditRequestCookieModifications(
590 const EventResponseDeltas& deltas,
591 ParsedRequestCookies* cookies) {
592 bool modified = false;
593 // We assume here that the deltas are sorted in decreasing extension
594 // precedence (i.e. decreasing extension installation time).
595 EventResponseDeltas::const_reverse_iterator delta;
596 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
597 const RequestCookieModifications& modifications =
598 (*delta)->request_cookie_modifications;
599 for (RequestCookieModifications::const_iterator mod = modifications.begin();
600 mod != modifications.end(); ++mod) {
601 if ((*mod)->type != EDIT || !(*mod)->modification.get())
604 std::string* new_value = (*mod)->modification->value.get();
605 RequestCookie* filter = (*mod)->filter.get();
606 for (ParsedRequestCookies::iterator cookie = cookies->begin();
607 cookie != cookies->end(); ++cookie) {
608 if (!DoesRequestCookieMatchFilter(*cookie, filter))
610 // If the edit operation tries to modify the cookie name, we just ignore
611 // this. We only modify the cookie value.
612 if (new_value && cookie->second != *new_value) {
613 cookie->second = *new_value;
622 // Applies all CookieModificationType::REMOVE operations for request cookies of
623 // |deltas| to |cookies|. Returns whether any cookie was deleted.
624 static bool MergeRemoveRequestCookieModifications(
625 const EventResponseDeltas& deltas,
626 ParsedRequestCookies* cookies) {
627 bool modified = false;
628 // We assume here that the deltas are sorted in decreasing extension
629 // precedence (i.e. decreasing extension installation time).
630 EventResponseDeltas::const_reverse_iterator delta;
631 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
632 const RequestCookieModifications& modifications =
633 (*delta)->request_cookie_modifications;
634 for (RequestCookieModifications::const_iterator mod = modifications.begin();
635 mod != modifications.end(); ++mod) {
636 if ((*mod)->type != REMOVE)
639 RequestCookie* filter = (*mod)->filter.get();
640 ParsedRequestCookies::iterator i = cookies->begin();
641 while (i != cookies->end()) {
642 if (DoesRequestCookieMatchFilter(*i, filter)) {
643 i = cookies->erase(i);
654 void MergeCookiesInOnBeforeSendHeadersResponses(
655 const EventResponseDeltas& deltas,
656 net::HttpRequestHeaders* request_headers,
657 extensions::ExtensionWarningSet* conflicting_extensions,
658 const net::BoundNetLog* net_log) {
659 // Skip all work if there are no registered cookie modifications.
660 bool cookie_modifications_exist = false;
661 EventResponseDeltas::const_iterator delta;
662 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
663 cookie_modifications_exist |=
664 !(*delta)->request_cookie_modifications.empty();
666 if (!cookie_modifications_exist)
669 // Parse old cookie line.
670 std::string cookie_header;
671 request_headers->GetHeader(net::HttpRequestHeaders::kCookie, &cookie_header);
672 ParsedRequestCookies cookies;
673 ParseRequestCookieLine(cookie_header, &cookies);
676 bool modified = false;
677 modified |= MergeAddRequestCookieModifications(deltas, &cookies);
678 modified |= MergeEditRequestCookieModifications(deltas, &cookies);
679 modified |= MergeRemoveRequestCookieModifications(deltas, &cookies);
681 // Reassemble and store new cookie line.
683 std::string new_cookie_header = SerializeRequestCookieLine(cookies);
684 request_headers->SetHeader(net::HttpRequestHeaders::kCookie,
689 // Returns the extension ID of the first extension in |deltas| that sets the
690 // request header identified by |key| to |value|.
691 static std::string FindSetRequestHeader(
692 const EventResponseDeltas& deltas,
693 const std::string& key,
694 const std::string& value) {
695 EventResponseDeltas::const_iterator delta;
696 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
697 net::HttpRequestHeaders::Iterator modification(
698 (*delta)->modified_request_headers);
699 while (modification.GetNext()) {
700 if (key == modification.name() && value == modification.value())
701 return (*delta)->extension_id;
704 return std::string();
707 // Returns the extension ID of the first extension in |deltas| that removes the
708 // request header identified by |key|.
709 static std::string FindRemoveRequestHeader(
710 const EventResponseDeltas& deltas,
711 const std::string& key) {
712 EventResponseDeltas::const_iterator delta;
713 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
714 std::vector<std::string>::iterator i;
715 for (i = (*delta)->deleted_request_headers.begin();
716 i != (*delta)->deleted_request_headers.end();
719 return (*delta)->extension_id;
722 return std::string();
725 void MergeOnBeforeSendHeadersResponses(
726 const EventResponseDeltas& deltas,
727 net::HttpRequestHeaders* request_headers,
728 extensions::ExtensionWarningSet* conflicting_extensions,
729 const net::BoundNetLog* net_log) {
730 EventResponseDeltas::const_iterator delta;
732 // Here we collect which headers we have removed or set to new values
733 // so far due to extensions of higher precedence.
734 std::set<std::string> removed_headers;
735 std::set<std::string> set_headers;
737 // We assume here that the deltas are sorted in decreasing extension
738 // precedence (i.e. decreasing extension installation time).
739 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
740 if ((*delta)->modified_request_headers.IsEmpty() &&
741 (*delta)->deleted_request_headers.empty()) {
745 // Check whether any modification affects a request header that
746 // has been modified differently before. As deltas is sorted by decreasing
747 // extension installation order, this takes care of precedence.
748 bool extension_conflicts = false;
749 std::string winning_extension_id;
750 std::string conflicting_header;
752 net::HttpRequestHeaders::Iterator modification(
753 (*delta)->modified_request_headers);
754 while (modification.GetNext() && !extension_conflicts) {
755 // This modification sets |key| to |value|.
756 const std::string& key = modification.name();
757 const std::string& value = modification.value();
759 // We must not delete anything that has been modified before.
760 if (removed_headers.find(key) != removed_headers.end() &&
761 !extension_conflicts) {
762 winning_extension_id = FindRemoveRequestHeader(deltas, key);
763 conflicting_header = key;
764 extension_conflicts = true;
767 // We must not modify anything that has been set to a *different*
769 if (set_headers.find(key) != set_headers.end() &&
770 !extension_conflicts) {
771 std::string current_value;
772 if (!request_headers->GetHeader(key, ¤t_value) ||
773 current_value != value) {
774 winning_extension_id =
775 FindSetRequestHeader(deltas, key, current_value);
776 conflicting_header = key;
777 extension_conflicts = true;
783 // Check whether any deletion affects a request header that has been
786 std::vector<std::string>::iterator key;
787 for (key = (*delta)->deleted_request_headers.begin();
788 key != (*delta)->deleted_request_headers.end() &&
789 !extension_conflicts;
791 if (set_headers.find(*key) != set_headers.end()) {
792 std::string current_value;
793 request_headers->GetHeader(*key, ¤t_value);
794 winning_extension_id =
795 FindSetRequestHeader(deltas, *key, current_value);
796 conflicting_header = *key;
797 extension_conflicts = true;
802 // Now execute the modifications if there were no conflicts.
803 if (!extension_conflicts) {
804 // Copy all modifications into the original headers.
805 request_headers->MergeFrom((*delta)->modified_request_headers);
807 // Record which keys were changed.
808 net::HttpRequestHeaders::Iterator modification(
809 (*delta)->modified_request_headers);
810 while (modification.GetNext())
811 set_headers.insert(modification.name());
814 // Perform all deletions and record which keys were deleted.
816 std::vector<std::string>::iterator key;
817 for (key = (*delta)->deleted_request_headers.begin();
818 key != (*delta)->deleted_request_headers.end();
820 request_headers->RemoveHeader(*key);
821 removed_headers.insert(*key);
825 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
826 base::Bind(&NetLogModificationCallback, delta->get()));
828 conflicting_extensions->insert(
829 ExtensionWarning::CreateRequestHeaderConflictWarning(
830 (*delta)->extension_id, winning_extension_id,
831 conflicting_header));
833 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
834 CreateNetLogExtensionIdCallback(delta->get()));
838 MergeCookiesInOnBeforeSendHeadersResponses(deltas, request_headers,
839 conflicting_extensions, net_log);
842 // Retrives all cookies from |override_response_headers|.
843 static ParsedResponseCookies GetResponseCookies(
844 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
845 ParsedResponseCookies result;
849 while (override_response_headers->EnumerateHeader(&iter, "Set-Cookie",
851 result.push_back(make_linked_ptr(new net::ParsedCookie(value)));
856 // Stores all |cookies| in |override_response_headers| deleting previously
857 // existing cookie definitions.
858 static void StoreResponseCookies(
859 const ParsedResponseCookies& cookies,
860 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
861 override_response_headers->RemoveHeader("Set-Cookie");
862 for (ParsedResponseCookies::const_iterator i = cookies.begin();
863 i != cookies.end(); ++i) {
864 override_response_headers->AddHeader("Set-Cookie: " + (*i)->ToCookieLine());
868 // Modifies |cookie| according to |modification|. Each value that is set in
869 // |modification| is applied to |cookie|.
870 static bool ApplyResponseCookieModification(ResponseCookie* modification,
871 net::ParsedCookie* cookie) {
872 bool modified = false;
873 if (modification->name.get())
874 modified |= cookie->SetName(*modification->name);
875 if (modification->value.get())
876 modified |= cookie->SetValue(*modification->value);
877 if (modification->expires.get())
878 modified |= cookie->SetExpires(*modification->expires);
879 if (modification->max_age.get())
880 modified |= cookie->SetMaxAge(base::IntToString(*modification->max_age));
881 if (modification->domain.get())
882 modified |= cookie->SetDomain(*modification->domain);
883 if (modification->path.get())
884 modified |= cookie->SetPath(*modification->path);
885 if (modification->secure.get())
886 modified |= cookie->SetIsSecure(*modification->secure);
887 if (modification->http_only.get())
888 modified |= cookie->SetIsHttpOnly(*modification->http_only);
892 static bool DoesResponseCookieMatchFilter(net::ParsedCookie* cookie,
893 FilterResponseCookie* filter) {
894 if (!cookie->IsValid()) return false;
895 if (!filter) return true;
896 if (filter->name.get() && cookie->Name() != *filter->name) return false;
897 if (filter->value.get() && cookie->Value() != *filter->value) return false;
898 if (filter->expires.get()) {
899 std::string actual_value =
900 cookie->HasExpires() ? cookie->Expires() : std::string();
901 if (actual_value != *filter->expires)
904 if (filter->max_age.get()) {
905 std::string actual_value =
906 cookie->HasMaxAge() ? cookie->MaxAge() : std::string();
907 if (actual_value != base::IntToString(*filter->max_age))
910 if (filter->domain.get()) {
911 std::string actual_value =
912 cookie->HasDomain() ? cookie->Domain() : std::string();
913 if (actual_value != *filter->domain)
916 if (filter->path.get()) {
917 std::string actual_value =
918 cookie->HasPath() ? cookie->Path() : std::string();
919 if (actual_value != *filter->path)
922 if (filter->secure.get() && cookie->IsSecure() != *filter->secure)
924 if (filter->http_only.get() && cookie->IsHttpOnly() != *filter->http_only)
926 int64 seconds_till_expiry;
927 bool lifetime_parsed = false;
928 if (filter->age_upper_bound.get() ||
929 filter->age_lower_bound.get() ||
930 (filter->session_cookie.get() && *filter->session_cookie)) {
931 lifetime_parsed = ParseCookieLifetime(cookie, &seconds_till_expiry);
933 if (filter->age_upper_bound.get()) {
934 if (seconds_till_expiry > *filter->age_upper_bound)
937 if (filter->age_lower_bound.get()) {
938 if (seconds_till_expiry < *filter->age_lower_bound)
941 if (filter->session_cookie.get() &&
942 *filter->session_cookie &&
949 // Applies all CookieModificationType::ADD operations for response cookies of
950 // |deltas| to |cookies|. Returns whether any cookie was added.
951 static bool MergeAddResponseCookieModifications(
952 const EventResponseDeltas& deltas,
953 ParsedResponseCookies* cookies) {
954 bool modified = false;
955 // We assume here that the deltas are sorted in decreasing extension
956 // precedence (i.e. decreasing extension installation time).
957 EventResponseDeltas::const_reverse_iterator delta;
958 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
959 const ResponseCookieModifications& modifications =
960 (*delta)->response_cookie_modifications;
961 for (ResponseCookieModifications::const_iterator mod =
962 modifications.begin(); mod != modifications.end(); ++mod) {
963 if ((*mod)->type != ADD || !(*mod)->modification.get())
965 // Cookie names are not unique in response cookies so we always append
966 // and never override.
967 linked_ptr<net::ParsedCookie> cookie(
968 new net::ParsedCookie(std::string()));
969 ApplyResponseCookieModification((*mod)->modification.get(), cookie.get());
970 cookies->push_back(cookie);
977 // Applies all CookieModificationType::EDIT operations for response cookies of
978 // |deltas| to |cookies|. Returns whether any cookie was modified.
979 static bool MergeEditResponseCookieModifications(
980 const EventResponseDeltas& deltas,
981 ParsedResponseCookies* cookies) {
982 bool modified = false;
983 // We assume here that the deltas are sorted in decreasing extension
984 // precedence (i.e. decreasing extension installation time).
985 EventResponseDeltas::const_reverse_iterator delta;
986 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
987 const ResponseCookieModifications& modifications =
988 (*delta)->response_cookie_modifications;
989 for (ResponseCookieModifications::const_iterator mod =
990 modifications.begin(); mod != modifications.end(); ++mod) {
991 if ((*mod)->type != EDIT || !(*mod)->modification.get())
994 for (ParsedResponseCookies::iterator cookie = cookies->begin();
995 cookie != cookies->end(); ++cookie) {
996 if (DoesResponseCookieMatchFilter(cookie->get(),
997 (*mod)->filter.get())) {
998 modified |= ApplyResponseCookieModification(
999 (*mod)->modification.get(), cookie->get());
1007 // Applies all CookieModificationType::REMOVE operations for response cookies of
1008 // |deltas| to |cookies|. Returns whether any cookie was deleted.
1009 static bool MergeRemoveResponseCookieModifications(
1010 const EventResponseDeltas& deltas,
1011 ParsedResponseCookies* cookies) {
1012 bool modified = false;
1013 // We assume here that the deltas are sorted in decreasing extension
1014 // precedence (i.e. decreasing extension installation time).
1015 EventResponseDeltas::const_reverse_iterator delta;
1016 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
1017 const ResponseCookieModifications& modifications =
1018 (*delta)->response_cookie_modifications;
1019 for (ResponseCookieModifications::const_iterator mod =
1020 modifications.begin(); mod != modifications.end(); ++mod) {
1021 if ((*mod)->type != REMOVE)
1024 ParsedResponseCookies::iterator i = cookies->begin();
1025 while (i != cookies->end()) {
1026 if (DoesResponseCookieMatchFilter(i->get(),
1027 (*mod)->filter.get())) {
1028 i = cookies->erase(i);
1039 void MergeCookiesInOnHeadersReceivedResponses(
1040 const EventResponseDeltas& deltas,
1041 const net::HttpResponseHeaders* original_response_headers,
1042 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
1043 extensions::ExtensionWarningSet* conflicting_extensions,
1044 const net::BoundNetLog* net_log) {
1045 // Skip all work if there are no registered cookie modifications.
1046 bool cookie_modifications_exist = false;
1047 EventResponseDeltas::const_reverse_iterator delta;
1048 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
1049 cookie_modifications_exist |=
1050 !(*delta)->response_cookie_modifications.empty();
1052 if (!cookie_modifications_exist)
1055 // Only create a copy if we really want to modify the response headers.
1056 if (override_response_headers->get() == NULL) {
1057 *override_response_headers = new net::HttpResponseHeaders(
1058 original_response_headers->raw_headers());
1061 ParsedResponseCookies cookies =
1062 GetResponseCookies(*override_response_headers);
1064 bool modified = false;
1065 modified |= MergeAddResponseCookieModifications(deltas, &cookies);
1066 modified |= MergeEditResponseCookieModifications(deltas, &cookies);
1067 modified |= MergeRemoveResponseCookieModifications(deltas, &cookies);
1071 StoreResponseCookies(cookies, *override_response_headers);
1074 // Converts the key of the (key, value) pair to lower case.
1075 static ResponseHeader ToLowerCase(const ResponseHeader& header) {
1076 std::string lower_key(header.first);
1077 StringToLowerASCII(&lower_key);
1078 return ResponseHeader(lower_key, header.second);
1081 // Returns the extension ID of the first extension in |deltas| that removes the
1082 // request header identified by |key|.
1083 static std::string FindRemoveResponseHeader(
1084 const EventResponseDeltas& deltas,
1085 const std::string& key) {
1086 std::string lower_key = StringToLowerASCII(key);
1087 EventResponseDeltas::const_iterator delta;
1088 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1089 ResponseHeaders::const_iterator i;
1090 for (i = (*delta)->deleted_response_headers.begin();
1091 i != (*delta)->deleted_response_headers.end(); ++i) {
1092 if (StringToLowerASCII(i->first) == lower_key)
1093 return (*delta)->extension_id;
1096 return std::string();
1099 void MergeOnHeadersReceivedResponses(
1100 const EventResponseDeltas& deltas,
1101 const net::HttpResponseHeaders* original_response_headers,
1102 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
1103 extensions::ExtensionWarningSet* conflicting_extensions,
1104 const net::BoundNetLog* net_log) {
1105 EventResponseDeltas::const_iterator delta;
1107 // Here we collect which headers we have removed or added so far due to
1108 // extensions of higher precedence. Header keys are always stored as
1110 std::set<ResponseHeader> removed_headers;
1111 std::set<ResponseHeader> added_headers;
1113 // We assume here that the deltas are sorted in decreasing extension
1114 // precedence (i.e. decreasing extension installation time).
1115 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1116 if ((*delta)->added_response_headers.empty() &&
1117 (*delta)->deleted_response_headers.empty()) {
1121 // Only create a copy if we really want to modify the response headers.
1122 if (override_response_headers->get() == NULL) {
1123 *override_response_headers = new net::HttpResponseHeaders(
1124 original_response_headers->raw_headers());
1127 // We consider modifications as pairs of (delete, add) operations.
1128 // If a header is deleted twice by different extensions we assume that the
1129 // intention was to modify it to different values and consider this a
1130 // conflict. As deltas is sorted by decreasing extension installation order,
1131 // this takes care of precedence.
1132 bool extension_conflicts = false;
1133 std::string conflicting_header;
1134 std::string winning_extension_id;
1135 ResponseHeaders::const_iterator i;
1136 for (i = (*delta)->deleted_response_headers.begin();
1137 i != (*delta)->deleted_response_headers.end(); ++i) {
1138 if (removed_headers.find(ToLowerCase(*i)) != removed_headers.end()) {
1139 winning_extension_id = FindRemoveResponseHeader(deltas, i->first);
1140 conflicting_header = i->first;
1141 extension_conflicts = true;
1146 // Now execute the modifications if there were no conflicts.
1147 if (!extension_conflicts) {
1150 for (i = (*delta)->deleted_response_headers.begin();
1151 i != (*delta)->deleted_response_headers.end(); ++i) {
1152 (*override_response_headers)->RemoveHeaderLine(i->first, i->second);
1153 removed_headers.insert(ToLowerCase(*i));
1159 for (i = (*delta)->added_response_headers.begin();
1160 i != (*delta)->added_response_headers.end(); ++i) {
1161 ResponseHeader lowercase_header(ToLowerCase(*i));
1162 if (added_headers.find(lowercase_header) != added_headers.end())
1164 added_headers.insert(lowercase_header);
1165 (*override_response_headers)->AddHeader(i->first + ": " + i->second);
1169 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
1170 CreateNetLogExtensionIdCallback(delta->get()));
1172 conflicting_extensions->insert(
1173 ExtensionWarning::CreateResponseHeaderConflictWarning(
1174 (*delta)->extension_id, winning_extension_id,
1175 conflicting_header));
1177 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1178 CreateNetLogExtensionIdCallback(delta->get()));
1182 MergeCookiesInOnHeadersReceivedResponses(deltas, original_response_headers,
1183 override_response_headers, conflicting_extensions, net_log);
1186 bool MergeOnAuthRequiredResponses(
1187 const EventResponseDeltas& deltas,
1188 net::AuthCredentials* auth_credentials,
1189 extensions::ExtensionWarningSet* conflicting_extensions,
1190 const net::BoundNetLog* net_log) {
1191 CHECK(auth_credentials);
1192 bool credentials_set = false;
1193 std::string winning_extension_id;
1195 for (EventResponseDeltas::const_iterator delta = deltas.begin();
1196 delta != deltas.end();
1198 if (!(*delta)->auth_credentials.get())
1201 auth_credentials->username() !=
1202 (*delta)->auth_credentials->username() ||
1203 auth_credentials->password() != (*delta)->auth_credentials->password();
1204 if (credentials_set && different) {
1205 conflicting_extensions->insert(
1206 ExtensionWarning::CreateCredentialsConflictWarning(
1207 (*delta)->extension_id, winning_extension_id));
1209 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1210 CreateNetLogExtensionIdCallback(delta->get()));
1213 net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS,
1214 CreateNetLogExtensionIdCallback(delta->get()));
1215 *auth_credentials = *(*delta)->auth_credentials;
1216 credentials_set = true;
1217 winning_extension_id = (*delta)->extension_id;
1220 return credentials_set;
1224 #define ARRAYEND(array) (array + arraysize(array))
1226 bool IsRelevantResourceType(ResourceType::Type type) {
1227 ResourceType::Type* iter =
1228 std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
1229 return iter != ARRAYEND(kResourceTypeValues);
1232 const char* ResourceTypeToString(ResourceType::Type type) {
1233 ResourceType::Type* iter =
1234 std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
1235 if (iter == ARRAYEND(kResourceTypeValues))
1238 return kResourceTypeStrings[iter - kResourceTypeValues];
1241 bool ParseResourceType(const std::string& type_str,
1242 ResourceType::Type* type) {
1244 std::find(kResourceTypeStrings, ARRAYEND(kResourceTypeStrings), type_str);
1245 if (iter == ARRAYEND(kResourceTypeStrings))
1247 *type = kResourceTypeValues[iter - kResourceTypeStrings];
1251 void ClearCacheOnNavigation() {
1252 if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
1253 ClearCacheOnNavigationOnUI();
1255 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
1256 base::Bind(&ClearCacheOnNavigationOnUI));
1260 void NotifyWebRequestAPIUsed(
1262 scoped_refptr<const extensions::Extension> extension) {
1263 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
1264 Profile* profile = reinterpret_cast<Profile*>(profile_id);
1265 if (!g_browser_process->profile_manager()->IsValidProfile(profile))
1268 extensions::RuntimeData* runtime_data =
1269 extensions::ExtensionSystem::Get(profile)->runtime_data();
1270 if (runtime_data->HasUsedWebRequest(extension.get()))
1272 runtime_data->SetHasUsedWebRequest(extension.get(), true);
1274 content::BrowserContext* browser_context = profile;
1275 for (content::RenderProcessHost::iterator it =
1276 content::RenderProcessHost::AllHostsIterator();
1277 !it.IsAtEnd(); it.Advance()) {
1278 content::RenderProcessHost* host = it.GetCurrentValue();
1279 if (host->GetBrowserContext() == browser_context)
1280 SendExtensionWebRequestStatusToHost(host);
1284 } // namespace extension_web_request_api_helpers