Add ICU message format support
[chromium-blink-merge.git] / extensions / browser / api / web_request / web_request_api_helpers.cc
blobfb6908fd1c7bf1e65864e53e8f9c0252ec252658
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"
7 #include <algorithm>
8 #include <cmath>
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
35 // top of this file.
37 using base::Time;
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 {
46 namespace {
48 static const char* kResourceTypeStrings[] = {
49 "main_frame",
50 "sub_frame",
51 "stylesheet",
52 "script",
53 "image",
54 "object",
55 "xmlhttprequest",
56 "other",
57 "other",
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)) {
94 return true;
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;
106 return false;
109 bool NullableEquals(const int* a, const int* b) {
110 if ((a && !b) || (!a && b))
111 return false;
112 return (!a) || (*a == *b);
115 bool NullableEquals(const bool* a, const bool* b) {
116 if ((a && !b) || (!a && b))
117 return false;
118 return (!a) || (*a == *b);
121 bool NullableEquals(const std::string* a, const std::string* b) {
122 if ((a && !b) || (!a && b))
123 return false;
124 return (!a) || (*a == *b);
127 } // namespace
129 RequestCookie::RequestCookie() {}
130 RequestCookie::~RequestCookie() {}
132 bool NullableEquals(const RequestCookie* a, const RequestCookie* b) {
133 if ((a && !b) || (!a && b))
134 return false;
135 if (!a)
136 return true;
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))
146 return false;
147 if (!a)
148 return true;
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))
165 return false;
166 if (!a)
167 return true;
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))
179 return false;
180 if (!a)
181 return true;
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))
192 return false;
193 if (!a)
194 return true;
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),
204 cancel(false) {
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
213 // callback.
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();
239 ++key) {
240 deleted_headers->Append(new base::StringValue(*key));
242 dict->Set("deleted_headers", deleted_headers);
243 return dict.Pass();
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) {
255 result->Append(
256 new base::FundamentalValue(
257 *reinterpret_cast<const unsigned char*>(&s[i])));
259 return result;
262 bool CharListToString(const base::ListValue* list, std::string* out) {
263 if (!list)
264 return false;
265 const size_t list_length = list->GetSize();
266 out->resize(list_length);
267 int value = 0;
268 for (size_t i = 0; i < list_length; ++i) {
269 if (!list->GetInteger(i, &value) || value < 0 || value > 255)
270 return false;
271 unsigned char tmp = static_cast<unsigned char>(value);
272 (*out)[i] = *reinterpret_cast<char*>(&tmp);
274 return true;
277 EventResponseDelta* CalculateOnBeforeRequestDelta(
278 const std::string& extension_id,
279 const base::Time& extension_install_time,
280 bool cancel,
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;
286 return result;
289 EventResponseDelta* CalculateOnBeforeSendHeadersDelta(
290 const std::string& extension_id,
291 const base::Time& extension_install_time,
292 bool cancel,
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.
301 if (new_headers) {
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()) {
316 std::string value;
317 if (!old_headers->GetHeader(i.name(), &value) || i.value() != value) {
318 result->modified_request_headers.SetHeader(i.name(), i.value());
323 return result;
326 EventResponseDelta* CalculateOnHeadersReceivedDelta(
327 const std::string& extension_id,
328 const base::Time& extension_install_time,
329 bool cancel,
330 const GURL& new_url,
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)
339 return result;
341 // Find deleted headers (header keys are treated case insensitively).
343 void* iter = NULL;
344 std::string name;
345 std::string value;
346 while (old_response_headers->EnumerateHeaderLines(&iter, &name, &value)) {
347 std::string name_lowercase(name);
348 base::StringToLowerASCII(&name_lowercase);
350 bool header_found = false;
351 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
352 i != new_response_headers->end(); ++i) {
353 if (base::LowerCaseEqualsASCII(i->first, name_lowercase.c_str()) &&
354 value == i->second) {
355 header_found = true;
356 break;
359 if (!header_found)
360 result->deleted_response_headers.push_back(ResponseHeader(name, value));
364 // Find added headers (header keys are treated case insensitively).
366 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
367 i != new_response_headers->end(); ++i) {
368 void* iter = NULL;
369 std::string value;
370 bool header_found = false;
371 while (old_response_headers->EnumerateHeader(&iter, i->first, &value) &&
372 !header_found) {
373 header_found = (value == i->second);
375 if (!header_found)
376 result->added_response_headers.push_back(*i);
380 return result;
383 EventResponseDelta* CalculateOnAuthRequiredDelta(
384 const std::string& extension_id,
385 const base::Time& extension_install_time,
386 bool cancel,
387 scoped_ptr<net::AuthCredentials>* auth_credentials) {
388 EventResponseDelta* result =
389 new EventResponseDelta(extension_id, extension_install_time);
390 result->cancel = cancel;
391 result->auth_credentials.swap(*auth_credentials);
392 return result;
395 void MergeCancelOfResponses(
396 const EventResponseDeltas& deltas,
397 bool* canceled,
398 const net::BoundNetLog* net_log) {
399 for (EventResponseDeltas::const_iterator i = deltas.begin();
400 i != deltas.end(); ++i) {
401 if ((*i)->cancel) {
402 *canceled = true;
403 net_log->AddEvent(
404 net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST,
405 CreateNetLogExtensionIdCallback(i->get()));
406 break;
411 // Helper function for MergeRedirectUrlOfResponses() that allows ignoring
412 // all redirects but those to data:// urls and about:blank. This is important
413 // to treat these URLs as "cancel urls", i.e. URLs that extensions redirect
414 // to if they want to express that they want to cancel a request. This reduces
415 // the number of conflicts that we need to flag, as canceling is considered
416 // a higher precedence operation that redirects.
417 // Returns whether a redirect occurred.
418 static bool MergeRedirectUrlOfResponsesHelper(
419 const EventResponseDeltas& deltas,
420 GURL* new_url,
421 extensions::WarningSet* conflicting_extensions,
422 const net::BoundNetLog* net_log,
423 bool consider_only_cancel_scheme_urls) {
424 bool redirected = false;
426 // Extension that determines the |new_url|.
427 std::string winning_extension_id;
428 EventResponseDeltas::const_iterator delta;
429 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
430 if ((*delta)->new_url.is_empty())
431 continue;
432 if (consider_only_cancel_scheme_urls &&
433 !(*delta)->new_url.SchemeIs(url::kDataScheme) &&
434 (*delta)->new_url.spec() != "about:blank") {
435 continue;
438 if (!redirected || *new_url == (*delta)->new_url) {
439 *new_url = (*delta)->new_url;
440 winning_extension_id = (*delta)->extension_id;
441 redirected = true;
442 net_log->AddEvent(
443 net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST,
444 CreateNetLogExtensionIdCallback(delta->get()));
445 } else {
446 conflicting_extensions->insert(
447 extensions::Warning::CreateRedirectConflictWarning(
448 (*delta)->extension_id,
449 winning_extension_id,
450 (*delta)->new_url,
451 *new_url));
452 net_log->AddEvent(
453 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
454 CreateNetLogExtensionIdCallback(delta->get()));
457 return redirected;
460 void MergeRedirectUrlOfResponses(
461 const EventResponseDeltas& deltas,
462 GURL* new_url,
463 extensions::WarningSet* conflicting_extensions,
464 const net::BoundNetLog* net_log) {
466 // First handle only redirects to data:// URLs and about:blank. These are a
467 // special case as they represent a way of cancelling a request.
468 if (MergeRedirectUrlOfResponsesHelper(
469 deltas, new_url, conflicting_extensions, net_log, true)) {
470 // If any extension cancelled a request by redirecting to a data:// URL or
471 // about:blank, we don't consider the other redirects.
472 return;
475 // Handle all other redirects.
476 MergeRedirectUrlOfResponsesHelper(
477 deltas, new_url, conflicting_extensions, net_log, false);
480 void MergeOnBeforeRequestResponses(
481 const EventResponseDeltas& deltas,
482 GURL* new_url,
483 extensions::WarningSet* conflicting_extensions,
484 const net::BoundNetLog* net_log) {
485 MergeRedirectUrlOfResponses(deltas, new_url, conflicting_extensions, net_log);
488 static bool DoesRequestCookieMatchFilter(
489 const ParsedRequestCookie& cookie,
490 RequestCookie* filter) {
491 if (!filter) return true;
492 if (filter->name.get() && cookie.first != *filter->name) return false;
493 if (filter->value.get() && cookie.second != *filter->value) return false;
494 return true;
497 // Applies all CookieModificationType::ADD operations for request cookies of
498 // |deltas| to |cookies|. Returns whether any cookie was added.
499 static bool MergeAddRequestCookieModifications(
500 const EventResponseDeltas& deltas,
501 ParsedRequestCookies* cookies) {
502 bool modified = false;
503 // We assume here that the deltas are sorted in decreasing extension
504 // precedence (i.e. decreasing extension installation time).
505 EventResponseDeltas::const_reverse_iterator delta;
506 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
507 const RequestCookieModifications& modifications =
508 (*delta)->request_cookie_modifications;
509 for (RequestCookieModifications::const_iterator mod = modifications.begin();
510 mod != modifications.end(); ++mod) {
511 if ((*mod)->type != ADD || !(*mod)->modification.get())
512 continue;
513 std::string* new_name = (*mod)->modification->name.get();
514 std::string* new_value = (*mod)->modification->value.get();
515 if (!new_name || !new_value)
516 continue;
518 bool cookie_with_same_name_found = false;
519 for (ParsedRequestCookies::iterator cookie = cookies->begin();
520 cookie != cookies->end() && !cookie_with_same_name_found; ++cookie) {
521 if (cookie->first == *new_name) {
522 if (cookie->second != *new_value) {
523 cookie->second = *new_value;
524 modified = true;
526 cookie_with_same_name_found = true;
529 if (!cookie_with_same_name_found) {
530 cookies->push_back(std::make_pair(base::StringPiece(*new_name),
531 base::StringPiece(*new_value)));
532 modified = true;
536 return modified;
539 // Applies all CookieModificationType::EDIT operations for request cookies of
540 // |deltas| to |cookies|. Returns whether any cookie was modified.
541 static bool MergeEditRequestCookieModifications(
542 const EventResponseDeltas& deltas,
543 ParsedRequestCookies* cookies) {
544 bool modified = false;
545 // We assume here that the deltas are sorted in decreasing extension
546 // precedence (i.e. decreasing extension installation time).
547 EventResponseDeltas::const_reverse_iterator delta;
548 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
549 const RequestCookieModifications& modifications =
550 (*delta)->request_cookie_modifications;
551 for (RequestCookieModifications::const_iterator mod = modifications.begin();
552 mod != modifications.end(); ++mod) {
553 if ((*mod)->type != EDIT || !(*mod)->modification.get())
554 continue;
556 std::string* new_value = (*mod)->modification->value.get();
557 RequestCookie* filter = (*mod)->filter.get();
558 for (ParsedRequestCookies::iterator cookie = cookies->begin();
559 cookie != cookies->end(); ++cookie) {
560 if (!DoesRequestCookieMatchFilter(*cookie, filter))
561 continue;
562 // If the edit operation tries to modify the cookie name, we just ignore
563 // this. We only modify the cookie value.
564 if (new_value && cookie->second != *new_value) {
565 cookie->second = *new_value;
566 modified = true;
571 return modified;
574 // Applies all CookieModificationType::REMOVE operations for request cookies of
575 // |deltas| to |cookies|. Returns whether any cookie was deleted.
576 static bool MergeRemoveRequestCookieModifications(
577 const EventResponseDeltas& deltas,
578 ParsedRequestCookies* cookies) {
579 bool modified = false;
580 // We assume here that the deltas are sorted in decreasing extension
581 // precedence (i.e. decreasing extension installation time).
582 EventResponseDeltas::const_reverse_iterator delta;
583 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
584 const RequestCookieModifications& modifications =
585 (*delta)->request_cookie_modifications;
586 for (RequestCookieModifications::const_iterator mod = modifications.begin();
587 mod != modifications.end(); ++mod) {
588 if ((*mod)->type != REMOVE)
589 continue;
591 RequestCookie* filter = (*mod)->filter.get();
592 ParsedRequestCookies::iterator i = cookies->begin();
593 while (i != cookies->end()) {
594 if (DoesRequestCookieMatchFilter(*i, filter)) {
595 i = cookies->erase(i);
596 modified = true;
597 } else {
598 ++i;
603 return modified;
606 void MergeCookiesInOnBeforeSendHeadersResponses(
607 const EventResponseDeltas& deltas,
608 net::HttpRequestHeaders* request_headers,
609 extensions::WarningSet* conflicting_extensions,
610 const net::BoundNetLog* net_log) {
611 // Skip all work if there are no registered cookie modifications.
612 bool cookie_modifications_exist = false;
613 EventResponseDeltas::const_iterator delta;
614 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
615 cookie_modifications_exist |=
616 !(*delta)->request_cookie_modifications.empty();
618 if (!cookie_modifications_exist)
619 return;
621 // Parse old cookie line.
622 std::string cookie_header;
623 request_headers->GetHeader(net::HttpRequestHeaders::kCookie, &cookie_header);
624 ParsedRequestCookies cookies;
625 net::cookie_util::ParseRequestCookieLine(cookie_header, &cookies);
627 // Modify cookies.
628 bool modified = false;
629 modified |= MergeAddRequestCookieModifications(deltas, &cookies);
630 modified |= MergeEditRequestCookieModifications(deltas, &cookies);
631 modified |= MergeRemoveRequestCookieModifications(deltas, &cookies);
633 // Reassemble and store new cookie line.
634 if (modified) {
635 std::string new_cookie_header =
636 net::cookie_util::SerializeRequestCookieLine(cookies);
637 request_headers->SetHeader(net::HttpRequestHeaders::kCookie,
638 new_cookie_header);
642 // Returns the extension ID of the first extension in |deltas| that sets the
643 // request header identified by |key| to |value|.
644 static std::string FindSetRequestHeader(
645 const EventResponseDeltas& deltas,
646 const std::string& key,
647 const std::string& value) {
648 EventResponseDeltas::const_iterator delta;
649 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
650 net::HttpRequestHeaders::Iterator modification(
651 (*delta)->modified_request_headers);
652 while (modification.GetNext()) {
653 if (key == modification.name() && value == modification.value())
654 return (*delta)->extension_id;
657 return std::string();
660 // Returns the extension ID of the first extension in |deltas| that removes the
661 // request header identified by |key|.
662 static std::string FindRemoveRequestHeader(
663 const EventResponseDeltas& deltas,
664 const std::string& key) {
665 EventResponseDeltas::const_iterator delta;
666 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
667 std::vector<std::string>::iterator i;
668 for (i = (*delta)->deleted_request_headers.begin();
669 i != (*delta)->deleted_request_headers.end();
670 ++i) {
671 if (*i == key)
672 return (*delta)->extension_id;
675 return std::string();
678 void MergeOnBeforeSendHeadersResponses(
679 const EventResponseDeltas& deltas,
680 net::HttpRequestHeaders* request_headers,
681 extensions::WarningSet* conflicting_extensions,
682 const net::BoundNetLog* net_log) {
683 EventResponseDeltas::const_iterator delta;
685 // Here we collect which headers we have removed or set to new values
686 // so far due to extensions of higher precedence.
687 std::set<std::string> removed_headers;
688 std::set<std::string> set_headers;
690 // We assume here that the deltas are sorted in decreasing extension
691 // precedence (i.e. decreasing extension installation time).
692 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
693 if ((*delta)->modified_request_headers.IsEmpty() &&
694 (*delta)->deleted_request_headers.empty()) {
695 continue;
698 // Check whether any modification affects a request header that
699 // has been modified differently before. As deltas is sorted by decreasing
700 // extension installation order, this takes care of precedence.
701 bool extension_conflicts = false;
702 std::string winning_extension_id;
703 std::string conflicting_header;
705 net::HttpRequestHeaders::Iterator modification(
706 (*delta)->modified_request_headers);
707 while (modification.GetNext() && !extension_conflicts) {
708 // This modification sets |key| to |value|.
709 const std::string& key = modification.name();
710 const std::string& value = modification.value();
712 // We must not delete anything that has been modified before.
713 if (removed_headers.find(key) != removed_headers.end() &&
714 !extension_conflicts) {
715 winning_extension_id = FindRemoveRequestHeader(deltas, key);
716 conflicting_header = key;
717 extension_conflicts = true;
720 // We must not modify anything that has been set to a *different*
721 // value before.
722 if (set_headers.find(key) != set_headers.end() &&
723 !extension_conflicts) {
724 std::string current_value;
725 if (!request_headers->GetHeader(key, &current_value) ||
726 current_value != value) {
727 winning_extension_id =
728 FindSetRequestHeader(deltas, key, current_value);
729 conflicting_header = key;
730 extension_conflicts = true;
736 // Check whether any deletion affects a request header that has been
737 // modified before.
739 std::vector<std::string>::iterator key;
740 for (key = (*delta)->deleted_request_headers.begin();
741 key != (*delta)->deleted_request_headers.end() &&
742 !extension_conflicts;
743 ++key) {
744 if (set_headers.find(*key) != set_headers.end()) {
745 std::string current_value;
746 request_headers->GetHeader(*key, &current_value);
747 winning_extension_id =
748 FindSetRequestHeader(deltas, *key, current_value);
749 conflicting_header = *key;
750 extension_conflicts = true;
755 // Now execute the modifications if there were no conflicts.
756 if (!extension_conflicts) {
757 // Copy all modifications into the original headers.
758 request_headers->MergeFrom((*delta)->modified_request_headers);
760 // Record which keys were changed.
761 net::HttpRequestHeaders::Iterator modification(
762 (*delta)->modified_request_headers);
763 while (modification.GetNext())
764 set_headers.insert(modification.name());
767 // Perform all deletions and record which keys were deleted.
769 std::vector<std::string>::iterator key;
770 for (key = (*delta)->deleted_request_headers.begin();
771 key != (*delta)->deleted_request_headers.end();
772 ++key) {
773 request_headers->RemoveHeader(*key);
774 removed_headers.insert(*key);
777 net_log->AddEvent(
778 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
779 base::Bind(&NetLogModificationCallback, delta->get()));
780 } else {
781 conflicting_extensions->insert(
782 extensions::Warning::CreateRequestHeaderConflictWarning(
783 (*delta)->extension_id, winning_extension_id,
784 conflicting_header));
785 net_log->AddEvent(
786 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
787 CreateNetLogExtensionIdCallback(delta->get()));
791 MergeCookiesInOnBeforeSendHeadersResponses(deltas, request_headers,
792 conflicting_extensions, net_log);
795 // Retrives all cookies from |override_response_headers|.
796 static ParsedResponseCookies GetResponseCookies(
797 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
798 ParsedResponseCookies result;
800 void* iter = NULL;
801 std::string value;
802 while (override_response_headers->EnumerateHeader(&iter, "Set-Cookie",
803 &value)) {
804 result.push_back(make_linked_ptr(new net::ParsedCookie(value)));
806 return result;
809 // Stores all |cookies| in |override_response_headers| deleting previously
810 // existing cookie definitions.
811 static void StoreResponseCookies(
812 const ParsedResponseCookies& cookies,
813 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
814 override_response_headers->RemoveHeader("Set-Cookie");
815 for (ParsedResponseCookies::const_iterator i = cookies.begin();
816 i != cookies.end(); ++i) {
817 override_response_headers->AddHeader("Set-Cookie: " + (*i)->ToCookieLine());
821 // Modifies |cookie| according to |modification|. Each value that is set in
822 // |modification| is applied to |cookie|.
823 static bool ApplyResponseCookieModification(ResponseCookie* modification,
824 net::ParsedCookie* cookie) {
825 bool modified = false;
826 if (modification->name.get())
827 modified |= cookie->SetName(*modification->name);
828 if (modification->value.get())
829 modified |= cookie->SetValue(*modification->value);
830 if (modification->expires.get())
831 modified |= cookie->SetExpires(*modification->expires);
832 if (modification->max_age.get())
833 modified |= cookie->SetMaxAge(base::IntToString(*modification->max_age));
834 if (modification->domain.get())
835 modified |= cookie->SetDomain(*modification->domain);
836 if (modification->path.get())
837 modified |= cookie->SetPath(*modification->path);
838 if (modification->secure.get())
839 modified |= cookie->SetIsSecure(*modification->secure);
840 if (modification->http_only.get())
841 modified |= cookie->SetIsHttpOnly(*modification->http_only);
842 return modified;
845 static bool DoesResponseCookieMatchFilter(net::ParsedCookie* cookie,
846 FilterResponseCookie* filter) {
847 if (!cookie->IsValid()) return false;
848 if (!filter) return true;
849 if (filter->name && cookie->Name() != *filter->name)
850 return false;
851 if (filter->value && cookie->Value() != *filter->value)
852 return false;
853 if (filter->expires) {
854 std::string actual_value =
855 cookie->HasExpires() ? cookie->Expires() : std::string();
856 if (actual_value != *filter->expires)
857 return false;
859 if (filter->max_age) {
860 std::string actual_value =
861 cookie->HasMaxAge() ? cookie->MaxAge() : std::string();
862 if (actual_value != base::IntToString(*filter->max_age))
863 return false;
865 if (filter->domain) {
866 std::string actual_value =
867 cookie->HasDomain() ? cookie->Domain() : std::string();
868 if (actual_value != *filter->domain)
869 return false;
871 if (filter->path) {
872 std::string actual_value =
873 cookie->HasPath() ? cookie->Path() : std::string();
874 if (actual_value != *filter->path)
875 return false;
877 if (filter->secure && cookie->IsSecure() != *filter->secure)
878 return false;
879 if (filter->http_only && cookie->IsHttpOnly() != *filter->http_only)
880 return false;
881 if (filter->age_upper_bound || filter->age_lower_bound ||
882 (filter->session_cookie && *filter->session_cookie)) {
883 int64 seconds_to_expiry;
884 bool lifetime_parsed = ParseCookieLifetime(cookie, &seconds_to_expiry);
885 if (filter->age_upper_bound && seconds_to_expiry > *filter->age_upper_bound)
886 return false;
887 if (filter->age_lower_bound && seconds_to_expiry < *filter->age_lower_bound)
888 return false;
889 if (filter->session_cookie && *filter->session_cookie && lifetime_parsed)
890 return false;
892 return true;
895 // Applies all CookieModificationType::ADD operations for response cookies of
896 // |deltas| to |cookies|. Returns whether any cookie was added.
897 static bool MergeAddResponseCookieModifications(
898 const EventResponseDeltas& deltas,
899 ParsedResponseCookies* cookies) {
900 bool modified = false;
901 // We assume here that the deltas are sorted in decreasing extension
902 // precedence (i.e. decreasing extension installation time).
903 EventResponseDeltas::const_reverse_iterator delta;
904 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
905 const ResponseCookieModifications& modifications =
906 (*delta)->response_cookie_modifications;
907 for (ResponseCookieModifications::const_iterator mod =
908 modifications.begin(); mod != modifications.end(); ++mod) {
909 if ((*mod)->type != ADD || !(*mod)->modification.get())
910 continue;
911 // Cookie names are not unique in response cookies so we always append
912 // and never override.
913 linked_ptr<net::ParsedCookie> cookie(
914 new net::ParsedCookie(std::string()));
915 ApplyResponseCookieModification((*mod)->modification.get(), cookie.get());
916 cookies->push_back(cookie);
917 modified = true;
920 return modified;
923 // Applies all CookieModificationType::EDIT operations for response cookies of
924 // |deltas| to |cookies|. Returns whether any cookie was modified.
925 static bool MergeEditResponseCookieModifications(
926 const EventResponseDeltas& deltas,
927 ParsedResponseCookies* cookies) {
928 bool modified = false;
929 // We assume here that the deltas are sorted in decreasing extension
930 // precedence (i.e. decreasing extension installation time).
931 EventResponseDeltas::const_reverse_iterator delta;
932 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
933 const ResponseCookieModifications& modifications =
934 (*delta)->response_cookie_modifications;
935 for (ResponseCookieModifications::const_iterator mod =
936 modifications.begin(); mod != modifications.end(); ++mod) {
937 if ((*mod)->type != EDIT || !(*mod)->modification.get())
938 continue;
940 for (ParsedResponseCookies::iterator cookie = cookies->begin();
941 cookie != cookies->end(); ++cookie) {
942 if (DoesResponseCookieMatchFilter(cookie->get(),
943 (*mod)->filter.get())) {
944 modified |= ApplyResponseCookieModification(
945 (*mod)->modification.get(), cookie->get());
950 return modified;
953 // Applies all CookieModificationType::REMOVE operations for response cookies of
954 // |deltas| to |cookies|. Returns whether any cookie was deleted.
955 static bool MergeRemoveResponseCookieModifications(
956 const EventResponseDeltas& deltas,
957 ParsedResponseCookies* cookies) {
958 bool modified = false;
959 // We assume here that the deltas are sorted in decreasing extension
960 // precedence (i.e. decreasing extension installation time).
961 EventResponseDeltas::const_reverse_iterator delta;
962 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
963 const ResponseCookieModifications& modifications =
964 (*delta)->response_cookie_modifications;
965 for (ResponseCookieModifications::const_iterator mod =
966 modifications.begin(); mod != modifications.end(); ++mod) {
967 if ((*mod)->type != REMOVE)
968 continue;
970 ParsedResponseCookies::iterator i = cookies->begin();
971 while (i != cookies->end()) {
972 if (DoesResponseCookieMatchFilter(i->get(),
973 (*mod)->filter.get())) {
974 i = cookies->erase(i);
975 modified = true;
976 } else {
977 ++i;
982 return modified;
985 void MergeCookiesInOnHeadersReceivedResponses(
986 const EventResponseDeltas& deltas,
987 const net::HttpResponseHeaders* original_response_headers,
988 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
989 extensions::WarningSet* conflicting_extensions,
990 const net::BoundNetLog* net_log) {
991 // Skip all work if there are no registered cookie modifications.
992 bool cookie_modifications_exist = false;
993 EventResponseDeltas::const_reverse_iterator delta;
994 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
995 cookie_modifications_exist |=
996 !(*delta)->response_cookie_modifications.empty();
998 if (!cookie_modifications_exist)
999 return;
1001 // Only create a copy if we really want to modify the response headers.
1002 if (override_response_headers->get() == NULL) {
1003 *override_response_headers = new net::HttpResponseHeaders(
1004 original_response_headers->raw_headers());
1007 ParsedResponseCookies cookies =
1008 GetResponseCookies(*override_response_headers);
1010 bool modified = false;
1011 modified |= MergeAddResponseCookieModifications(deltas, &cookies);
1012 modified |= MergeEditResponseCookieModifications(deltas, &cookies);
1013 modified |= MergeRemoveResponseCookieModifications(deltas, &cookies);
1015 // Store new value.
1016 if (modified)
1017 StoreResponseCookies(cookies, *override_response_headers);
1020 // Converts the key of the (key, value) pair to lower case.
1021 static ResponseHeader ToLowerCase(const ResponseHeader& header) {
1022 std::string lower_key(header.first);
1023 base::StringToLowerASCII(&lower_key);
1024 return ResponseHeader(lower_key, header.second);
1027 // Returns the extension ID of the first extension in |deltas| that removes the
1028 // request header identified by |key|.
1029 static std::string FindRemoveResponseHeader(
1030 const EventResponseDeltas& deltas,
1031 const std::string& key) {
1032 std::string lower_key = base::StringToLowerASCII(key);
1033 EventResponseDeltas::const_iterator delta;
1034 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1035 ResponseHeaders::const_iterator i;
1036 for (i = (*delta)->deleted_response_headers.begin();
1037 i != (*delta)->deleted_response_headers.end(); ++i) {
1038 if (base::StringToLowerASCII(i->first) == lower_key)
1039 return (*delta)->extension_id;
1042 return std::string();
1045 void MergeOnHeadersReceivedResponses(
1046 const EventResponseDeltas& deltas,
1047 const net::HttpResponseHeaders* original_response_headers,
1048 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
1049 GURL* allowed_unsafe_redirect_url,
1050 extensions::WarningSet* conflicting_extensions,
1051 const net::BoundNetLog* net_log) {
1052 EventResponseDeltas::const_iterator delta;
1054 // Here we collect which headers we have removed or added so far due to
1055 // extensions of higher precedence. Header keys are always stored as
1056 // lower case.
1057 std::set<ResponseHeader> removed_headers;
1058 std::set<ResponseHeader> added_headers;
1060 // We assume here that the deltas are sorted in decreasing extension
1061 // precedence (i.e. decreasing extension installation time).
1062 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1063 if ((*delta)->added_response_headers.empty() &&
1064 (*delta)->deleted_response_headers.empty()) {
1065 continue;
1068 // Only create a copy if we really want to modify the response headers.
1069 if (override_response_headers->get() == NULL) {
1070 *override_response_headers = new net::HttpResponseHeaders(
1071 original_response_headers->raw_headers());
1074 // We consider modifications as pairs of (delete, add) operations.
1075 // If a header is deleted twice by different extensions we assume that the
1076 // intention was to modify it to different values and consider this a
1077 // conflict. As deltas is sorted by decreasing extension installation order,
1078 // this takes care of precedence.
1079 bool extension_conflicts = false;
1080 std::string conflicting_header;
1081 std::string winning_extension_id;
1082 ResponseHeaders::const_iterator i;
1083 for (i = (*delta)->deleted_response_headers.begin();
1084 i != (*delta)->deleted_response_headers.end(); ++i) {
1085 if (removed_headers.find(ToLowerCase(*i)) != removed_headers.end()) {
1086 winning_extension_id = FindRemoveResponseHeader(deltas, i->first);
1087 conflicting_header = i->first;
1088 extension_conflicts = true;
1089 break;
1093 // Now execute the modifications if there were no conflicts.
1094 if (!extension_conflicts) {
1095 // Delete headers
1097 for (i = (*delta)->deleted_response_headers.begin();
1098 i != (*delta)->deleted_response_headers.end(); ++i) {
1099 (*override_response_headers)->RemoveHeaderLine(i->first, i->second);
1100 removed_headers.insert(ToLowerCase(*i));
1104 // Add headers.
1106 for (i = (*delta)->added_response_headers.begin();
1107 i != (*delta)->added_response_headers.end(); ++i) {
1108 ResponseHeader lowercase_header(ToLowerCase(*i));
1109 if (added_headers.find(lowercase_header) != added_headers.end())
1110 continue;
1111 added_headers.insert(lowercase_header);
1112 (*override_response_headers)->AddHeader(i->first + ": " + i->second);
1115 net_log->AddEvent(
1116 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
1117 CreateNetLogExtensionIdCallback(delta->get()));
1118 } else {
1119 conflicting_extensions->insert(
1120 extensions::Warning::CreateResponseHeaderConflictWarning(
1121 (*delta)->extension_id, winning_extension_id,
1122 conflicting_header));
1123 net_log->AddEvent(
1124 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1125 CreateNetLogExtensionIdCallback(delta->get()));
1129 MergeCookiesInOnHeadersReceivedResponses(deltas, original_response_headers,
1130 override_response_headers, conflicting_extensions, net_log);
1132 GURL new_url;
1133 MergeRedirectUrlOfResponses(
1134 deltas, &new_url, conflicting_extensions, net_log);
1135 if (new_url.is_valid()) {
1136 // Only create a copy if we really want to modify the response headers.
1137 if (override_response_headers->get() == NULL) {
1138 *override_response_headers = new net::HttpResponseHeaders(
1139 original_response_headers->raw_headers());
1141 (*override_response_headers)->ReplaceStatusLine("HTTP/1.1 302 Found");
1142 (*override_response_headers)->RemoveHeader("location");
1143 (*override_response_headers)->AddHeader("Location: " + new_url.spec());
1144 // Explicitly mark the URL as safe for redirection, to prevent the request
1145 // from being blocked because of net::ERR_UNSAFE_REDIRECT.
1146 *allowed_unsafe_redirect_url = new_url;
1150 bool MergeOnAuthRequiredResponses(
1151 const EventResponseDeltas& deltas,
1152 net::AuthCredentials* auth_credentials,
1153 extensions::WarningSet* conflicting_extensions,
1154 const net::BoundNetLog* net_log) {
1155 CHECK(auth_credentials);
1156 bool credentials_set = false;
1157 std::string winning_extension_id;
1159 for (EventResponseDeltas::const_iterator delta = deltas.begin();
1160 delta != deltas.end();
1161 ++delta) {
1162 if (!(*delta)->auth_credentials.get())
1163 continue;
1164 bool different =
1165 auth_credentials->username() !=
1166 (*delta)->auth_credentials->username() ||
1167 auth_credentials->password() != (*delta)->auth_credentials->password();
1168 if (credentials_set && different) {
1169 conflicting_extensions->insert(
1170 extensions::Warning::CreateCredentialsConflictWarning(
1171 (*delta)->extension_id, winning_extension_id));
1172 net_log->AddEvent(
1173 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1174 CreateNetLogExtensionIdCallback(delta->get()));
1175 } else {
1176 net_log->AddEvent(
1177 net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS,
1178 CreateNetLogExtensionIdCallback(delta->get()));
1179 *auth_credentials = *(*delta)->auth_credentials;
1180 credentials_set = true;
1181 winning_extension_id = (*delta)->extension_id;
1184 return credentials_set;
1187 void ClearCacheOnNavigation() {
1188 if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
1189 ClearCacheOnNavigationOnUI();
1190 } else {
1191 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
1192 base::Bind(&ClearCacheOnNavigationOnUI));
1196 void NotifyWebRequestAPIUsed(void* browser_context_id,
1197 const std::string& extension_id) {
1198 DCHECK(!extension_id.empty());
1199 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1200 content::BrowserContext* browser_context =
1201 reinterpret_cast<content::BrowserContext*>(browser_context_id);
1202 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(
1203 browser_context))
1204 return;
1206 extensions::RuntimeData* runtime_data =
1207 extensions::ExtensionSystem::Get(browser_context)->runtime_data();
1208 if (runtime_data->HasUsedWebRequest(extension_id))
1209 return;
1210 runtime_data->SetHasUsedWebRequest(extension_id, true);
1212 for (content::RenderProcessHost::iterator it =
1213 content::RenderProcessHost::AllHostsIterator();
1214 !it.IsAtEnd(); it.Advance()) {
1215 content::RenderProcessHost* host = it.GetCurrentValue();
1216 if (host->GetBrowserContext() == browser_context)
1217 SendExtensionWebRequestStatusToHost(host);
1221 void SendExtensionWebRequestStatusToHost(content::RenderProcessHost* host) {
1222 content::BrowserContext* browser_context = host->GetBrowserContext();
1223 if (!browser_context)
1224 return;
1226 bool webrequest_used = false;
1227 const extensions::ExtensionSet& extensions =
1228 extensions::ExtensionRegistry::Get(browser_context)->enabled_extensions();
1229 extensions::RuntimeData* runtime_data =
1230 extensions::ExtensionSystem::Get(browser_context)->runtime_data();
1231 for (extensions::ExtensionSet::const_iterator it = extensions.begin();
1232 !webrequest_used && it != extensions.end();
1233 ++it) {
1234 webrequest_used |= runtime_data->HasUsedWebRequest((*it)->id());
1237 host->Send(new ExtensionMsg_UsingWebRequestAPI(webrequest_used));
1240 // Converts the |name|, |value| pair of a http header to a HttpHeaders
1241 // dictionary. Ownership is passed to the caller.
1242 base::DictionaryValue* CreateHeaderDictionary(
1243 const std::string& name, const std::string& value) {
1244 base::DictionaryValue* header = new base::DictionaryValue();
1245 header->SetString(keys::kHeaderNameKey, name);
1246 if (base::IsStringUTF8(value)) {
1247 header->SetString(keys::kHeaderValueKey, value);
1248 } else {
1249 header->Set(keys::kHeaderBinaryValueKey,
1250 StringToCharList(value));
1252 return header;
1255 #define ARRAYEND(array) (array + arraysize(array))
1257 bool IsRelevantResourceType(ResourceType type) {
1258 ResourceType* iter =
1259 std::find(kResourceTypeValues,
1260 kResourceTypeValues + kResourceTypeValuesLength,
1261 type);
1262 return iter != (kResourceTypeValues + kResourceTypeValuesLength);
1265 const char* ResourceTypeToString(ResourceType type) {
1266 ResourceType* iter =
1267 std::find(kResourceTypeValues,
1268 kResourceTypeValues + kResourceTypeValuesLength,
1269 type);
1270 if (iter == (kResourceTypeValues + kResourceTypeValuesLength))
1271 return "other";
1273 return kResourceTypeStrings[iter - kResourceTypeValues];
1276 bool ParseResourceType(const std::string& type_str,
1277 ResourceType* type) {
1278 const char** iter =
1279 std::find(kResourceTypeStrings,
1280 kResourceTypeStrings + kResourceTypeStringsLength,
1281 type_str);
1282 if (iter == (kResourceTypeStrings + kResourceTypeStringsLength))
1283 return false;
1284 *type = kResourceTypeValues[iter - kResourceTypeStrings];
1285 return true;
1288 } // namespace extension_web_request_api_helpers