Add testing/scripts/OWNERS
[chromium-blink-merge.git] / extensions / browser / api / web_request / web_request_api_helpers.cc
blob825b0bd631199d2f35f71fbc3168a1f214e60036
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/base/net_log.h"
28 #include "net/cookies/cookie_util.h"
29 #include "net/cookies/parsed_cookie.h"
30 #include "net/http/http_util.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 // Caller takes ownership of returned value.
221 base::Value* NetLogModificationCallback(
222 const EventResponseDelta* delta,
223 net::NetLog::LogLevel log_level) {
224 base::DictionaryValue* dict = new base::DictionaryValue();
225 dict->SetString("extension_id", delta->extension_id);
227 base::ListValue* modified_headers = new base::ListValue();
228 net::HttpRequestHeaders::Iterator modification(
229 delta->modified_request_headers);
230 while (modification.GetNext()) {
231 std::string line = modification.name() + ": " + modification.value();
232 modified_headers->Append(new base::StringValue(line));
234 dict->Set("modified_headers", modified_headers);
236 base::ListValue* deleted_headers = new base::ListValue();
237 for (std::vector<std::string>::const_iterator key =
238 delta->deleted_request_headers.begin();
239 key != delta->deleted_request_headers.end();
240 ++key) {
241 deleted_headers->Append(new base::StringValue(*key));
243 dict->Set("deleted_headers", deleted_headers);
244 return dict;
247 bool InDecreasingExtensionInstallationTimeOrder(
248 const linked_ptr<EventResponseDelta>& a,
249 const linked_ptr<EventResponseDelta>& b) {
250 return a->extension_install_time > b->extension_install_time;
253 base::ListValue* StringToCharList(const std::string& s) {
254 base::ListValue* result = new base::ListValue;
255 for (size_t i = 0, n = s.size(); i < n; ++i) {
256 result->Append(
257 new base::FundamentalValue(
258 *reinterpret_cast<const unsigned char*>(&s[i])));
260 return result;
263 bool CharListToString(const base::ListValue* list, std::string* out) {
264 if (!list)
265 return false;
266 const size_t list_length = list->GetSize();
267 out->resize(list_length);
268 int value = 0;
269 for (size_t i = 0; i < list_length; ++i) {
270 if (!list->GetInteger(i, &value) || value < 0 || value > 255)
271 return false;
272 unsigned char tmp = static_cast<unsigned char>(value);
273 (*out)[i] = *reinterpret_cast<char*>(&tmp);
275 return true;
278 EventResponseDelta* CalculateOnBeforeRequestDelta(
279 const std::string& extension_id,
280 const base::Time& extension_install_time,
281 bool cancel,
282 const GURL& new_url) {
283 EventResponseDelta* result =
284 new EventResponseDelta(extension_id, extension_install_time);
285 result->cancel = cancel;
286 result->new_url = new_url;
287 return result;
290 EventResponseDelta* CalculateOnBeforeSendHeadersDelta(
291 const std::string& extension_id,
292 const base::Time& extension_install_time,
293 bool cancel,
294 net::HttpRequestHeaders* old_headers,
295 net::HttpRequestHeaders* new_headers) {
296 EventResponseDelta* result =
297 new EventResponseDelta(extension_id, extension_install_time);
298 result->cancel = cancel;
300 // The event listener might not have passed any new headers if he
301 // just wanted to cancel the request.
302 if (new_headers) {
303 // Find deleted headers.
305 net::HttpRequestHeaders::Iterator i(*old_headers);
306 while (i.GetNext()) {
307 if (!new_headers->HasHeader(i.name())) {
308 result->deleted_request_headers.push_back(i.name());
313 // Find modified headers.
315 net::HttpRequestHeaders::Iterator i(*new_headers);
316 while (i.GetNext()) {
317 std::string value;
318 if (!old_headers->GetHeader(i.name(), &value) || i.value() != value) {
319 result->modified_request_headers.SetHeader(i.name(), i.value());
324 return result;
327 EventResponseDelta* CalculateOnHeadersReceivedDelta(
328 const std::string& extension_id,
329 const base::Time& extension_install_time,
330 bool cancel,
331 const GURL& new_url,
332 const net::HttpResponseHeaders* old_response_headers,
333 ResponseHeaders* new_response_headers) {
334 EventResponseDelta* result =
335 new EventResponseDelta(extension_id, extension_install_time);
336 result->cancel = cancel;
337 result->new_url = new_url;
339 if (!new_response_headers)
340 return result;
342 // Find deleted headers (header keys are treated case insensitively).
344 void* iter = NULL;
345 std::string name;
346 std::string value;
347 while (old_response_headers->EnumerateHeaderLines(&iter, &name, &value)) {
348 std::string name_lowercase(name);
349 base::StringToLowerASCII(&name_lowercase);
351 bool header_found = false;
352 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
353 i != new_response_headers->end(); ++i) {
354 if (LowerCaseEqualsASCII(i->first, name_lowercase.c_str()) &&
355 value == i->second) {
356 header_found = true;
357 break;
360 if (!header_found)
361 result->deleted_response_headers.push_back(ResponseHeader(name, value));
365 // Find added headers (header keys are treated case insensitively).
367 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
368 i != new_response_headers->end(); ++i) {
369 void* iter = NULL;
370 std::string value;
371 bool header_found = false;
372 while (old_response_headers->EnumerateHeader(&iter, i->first, &value) &&
373 !header_found) {
374 header_found = (value == i->second);
376 if (!header_found)
377 result->added_response_headers.push_back(*i);
381 return result;
384 EventResponseDelta* CalculateOnAuthRequiredDelta(
385 const std::string& extension_id,
386 const base::Time& extension_install_time,
387 bool cancel,
388 scoped_ptr<net::AuthCredentials>* auth_credentials) {
389 EventResponseDelta* result =
390 new EventResponseDelta(extension_id, extension_install_time);
391 result->cancel = cancel;
392 result->auth_credentials.swap(*auth_credentials);
393 return result;
396 void MergeCancelOfResponses(
397 const EventResponseDeltas& deltas,
398 bool* canceled,
399 const net::BoundNetLog* net_log) {
400 for (EventResponseDeltas::const_iterator i = deltas.begin();
401 i != deltas.end(); ++i) {
402 if ((*i)->cancel) {
403 *canceled = true;
404 net_log->AddEvent(
405 net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST,
406 CreateNetLogExtensionIdCallback(i->get()));
407 break;
412 // Helper function for MergeRedirectUrlOfResponses() that allows ignoring
413 // all redirects but those to data:// urls and about:blank. This is important
414 // to treat these URLs as "cancel urls", i.e. URLs that extensions redirect
415 // to if they want to express that they want to cancel a request. This reduces
416 // the number of conflicts that we need to flag, as canceling is considered
417 // a higher precedence operation that redirects.
418 // Returns whether a redirect occurred.
419 static bool MergeRedirectUrlOfResponsesHelper(
420 const EventResponseDeltas& deltas,
421 GURL* new_url,
422 extensions::WarningSet* conflicting_extensions,
423 const net::BoundNetLog* net_log,
424 bool consider_only_cancel_scheme_urls) {
425 bool redirected = false;
427 // Extension that determines the |new_url|.
428 std::string winning_extension_id;
429 EventResponseDeltas::const_iterator delta;
430 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
431 if ((*delta)->new_url.is_empty())
432 continue;
433 if (consider_only_cancel_scheme_urls &&
434 !(*delta)->new_url.SchemeIs(url::kDataScheme) &&
435 (*delta)->new_url.spec() != "about:blank") {
436 continue;
439 if (!redirected || *new_url == (*delta)->new_url) {
440 *new_url = (*delta)->new_url;
441 winning_extension_id = (*delta)->extension_id;
442 redirected = true;
443 net_log->AddEvent(
444 net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST,
445 CreateNetLogExtensionIdCallback(delta->get()));
446 } else {
447 conflicting_extensions->insert(
448 extensions::Warning::CreateRedirectConflictWarning(
449 (*delta)->extension_id,
450 winning_extension_id,
451 (*delta)->new_url,
452 *new_url));
453 net_log->AddEvent(
454 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
455 CreateNetLogExtensionIdCallback(delta->get()));
458 return redirected;
461 void MergeRedirectUrlOfResponses(
462 const EventResponseDeltas& deltas,
463 GURL* new_url,
464 extensions::WarningSet* conflicting_extensions,
465 const net::BoundNetLog* net_log) {
467 // First handle only redirects to data:// URLs and about:blank. These are a
468 // special case as they represent a way of cancelling a request.
469 if (MergeRedirectUrlOfResponsesHelper(
470 deltas, new_url, conflicting_extensions, net_log, true)) {
471 // If any extension cancelled a request by redirecting to a data:// URL or
472 // about:blank, we don't consider the other redirects.
473 return;
476 // Handle all other redirects.
477 MergeRedirectUrlOfResponsesHelper(
478 deltas, new_url, conflicting_extensions, net_log, false);
481 void MergeOnBeforeRequestResponses(
482 const EventResponseDeltas& deltas,
483 GURL* new_url,
484 extensions::WarningSet* conflicting_extensions,
485 const net::BoundNetLog* net_log) {
486 MergeRedirectUrlOfResponses(deltas, new_url, conflicting_extensions, net_log);
489 static bool DoesRequestCookieMatchFilter(
490 const ParsedRequestCookie& cookie,
491 RequestCookie* filter) {
492 if (!filter) return true;
493 if (filter->name.get() && cookie.first != *filter->name) return false;
494 if (filter->value.get() && cookie.second != *filter->value) return false;
495 return true;
498 // Applies all CookieModificationType::ADD operations for request cookies of
499 // |deltas| to |cookies|. Returns whether any cookie was added.
500 static bool MergeAddRequestCookieModifications(
501 const EventResponseDeltas& deltas,
502 ParsedRequestCookies* cookies) {
503 bool modified = false;
504 // We assume here that the deltas are sorted in decreasing extension
505 // precedence (i.e. decreasing extension installation time).
506 EventResponseDeltas::const_reverse_iterator delta;
507 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
508 const RequestCookieModifications& modifications =
509 (*delta)->request_cookie_modifications;
510 for (RequestCookieModifications::const_iterator mod = modifications.begin();
511 mod != modifications.end(); ++mod) {
512 if ((*mod)->type != ADD || !(*mod)->modification.get())
513 continue;
514 std::string* new_name = (*mod)->modification->name.get();
515 std::string* new_value = (*mod)->modification->value.get();
516 if (!new_name || !new_value)
517 continue;
519 bool cookie_with_same_name_found = false;
520 for (ParsedRequestCookies::iterator cookie = cookies->begin();
521 cookie != cookies->end() && !cookie_with_same_name_found; ++cookie) {
522 if (cookie->first == *new_name) {
523 if (cookie->second != *new_value) {
524 cookie->second = *new_value;
525 modified = true;
527 cookie_with_same_name_found = true;
530 if (!cookie_with_same_name_found) {
531 cookies->push_back(std::make_pair(base::StringPiece(*new_name),
532 base::StringPiece(*new_value)));
533 modified = true;
537 return modified;
540 // Applies all CookieModificationType::EDIT operations for request cookies of
541 // |deltas| to |cookies|. Returns whether any cookie was modified.
542 static bool MergeEditRequestCookieModifications(
543 const EventResponseDeltas& deltas,
544 ParsedRequestCookies* cookies) {
545 bool modified = false;
546 // We assume here that the deltas are sorted in decreasing extension
547 // precedence (i.e. decreasing extension installation time).
548 EventResponseDeltas::const_reverse_iterator delta;
549 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
550 const RequestCookieModifications& modifications =
551 (*delta)->request_cookie_modifications;
552 for (RequestCookieModifications::const_iterator mod = modifications.begin();
553 mod != modifications.end(); ++mod) {
554 if ((*mod)->type != EDIT || !(*mod)->modification.get())
555 continue;
557 std::string* new_value = (*mod)->modification->value.get();
558 RequestCookie* filter = (*mod)->filter.get();
559 for (ParsedRequestCookies::iterator cookie = cookies->begin();
560 cookie != cookies->end(); ++cookie) {
561 if (!DoesRequestCookieMatchFilter(*cookie, filter))
562 continue;
563 // If the edit operation tries to modify the cookie name, we just ignore
564 // this. We only modify the cookie value.
565 if (new_value && cookie->second != *new_value) {
566 cookie->second = *new_value;
567 modified = true;
572 return modified;
575 // Applies all CookieModificationType::REMOVE operations for request cookies of
576 // |deltas| to |cookies|. Returns whether any cookie was deleted.
577 static bool MergeRemoveRequestCookieModifications(
578 const EventResponseDeltas& deltas,
579 ParsedRequestCookies* cookies) {
580 bool modified = false;
581 // We assume here that the deltas are sorted in decreasing extension
582 // precedence (i.e. decreasing extension installation time).
583 EventResponseDeltas::const_reverse_iterator delta;
584 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
585 const RequestCookieModifications& modifications =
586 (*delta)->request_cookie_modifications;
587 for (RequestCookieModifications::const_iterator mod = modifications.begin();
588 mod != modifications.end(); ++mod) {
589 if ((*mod)->type != REMOVE)
590 continue;
592 RequestCookie* filter = (*mod)->filter.get();
593 ParsedRequestCookies::iterator i = cookies->begin();
594 while (i != cookies->end()) {
595 if (DoesRequestCookieMatchFilter(*i, filter)) {
596 i = cookies->erase(i);
597 modified = true;
598 } else {
599 ++i;
604 return modified;
607 void MergeCookiesInOnBeforeSendHeadersResponses(
608 const EventResponseDeltas& deltas,
609 net::HttpRequestHeaders* request_headers,
610 extensions::WarningSet* conflicting_extensions,
611 const net::BoundNetLog* net_log) {
612 // Skip all work if there are no registered cookie modifications.
613 bool cookie_modifications_exist = false;
614 EventResponseDeltas::const_iterator delta;
615 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
616 cookie_modifications_exist |=
617 !(*delta)->request_cookie_modifications.empty();
619 if (!cookie_modifications_exist)
620 return;
622 // Parse old cookie line.
623 std::string cookie_header;
624 request_headers->GetHeader(net::HttpRequestHeaders::kCookie, &cookie_header);
625 ParsedRequestCookies cookies;
626 net::cookie_util::ParseRequestCookieLine(cookie_header, &cookies);
628 // Modify cookies.
629 bool modified = false;
630 modified |= MergeAddRequestCookieModifications(deltas, &cookies);
631 modified |= MergeEditRequestCookieModifications(deltas, &cookies);
632 modified |= MergeRemoveRequestCookieModifications(deltas, &cookies);
634 // Reassemble and store new cookie line.
635 if (modified) {
636 std::string new_cookie_header =
637 net::cookie_util::SerializeRequestCookieLine(cookies);
638 request_headers->SetHeader(net::HttpRequestHeaders::kCookie,
639 new_cookie_header);
643 // Returns the extension ID of the first extension in |deltas| that sets the
644 // request header identified by |key| to |value|.
645 static std::string FindSetRequestHeader(
646 const EventResponseDeltas& deltas,
647 const std::string& key,
648 const std::string& value) {
649 EventResponseDeltas::const_iterator delta;
650 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
651 net::HttpRequestHeaders::Iterator modification(
652 (*delta)->modified_request_headers);
653 while (modification.GetNext()) {
654 if (key == modification.name() && value == modification.value())
655 return (*delta)->extension_id;
658 return std::string();
661 // Returns the extension ID of the first extension in |deltas| that removes the
662 // request header identified by |key|.
663 static std::string FindRemoveRequestHeader(
664 const EventResponseDeltas& deltas,
665 const std::string& key) {
666 EventResponseDeltas::const_iterator delta;
667 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
668 std::vector<std::string>::iterator i;
669 for (i = (*delta)->deleted_request_headers.begin();
670 i != (*delta)->deleted_request_headers.end();
671 ++i) {
672 if (*i == key)
673 return (*delta)->extension_id;
676 return std::string();
679 void MergeOnBeforeSendHeadersResponses(
680 const EventResponseDeltas& deltas,
681 net::HttpRequestHeaders* request_headers,
682 extensions::WarningSet* conflicting_extensions,
683 const net::BoundNetLog* net_log) {
684 EventResponseDeltas::const_iterator delta;
686 // Here we collect which headers we have removed or set to new values
687 // so far due to extensions of higher precedence.
688 std::set<std::string> removed_headers;
689 std::set<std::string> set_headers;
691 // We assume here that the deltas are sorted in decreasing extension
692 // precedence (i.e. decreasing extension installation time).
693 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
694 if ((*delta)->modified_request_headers.IsEmpty() &&
695 (*delta)->deleted_request_headers.empty()) {
696 continue;
699 // Check whether any modification affects a request header that
700 // has been modified differently before. As deltas is sorted by decreasing
701 // extension installation order, this takes care of precedence.
702 bool extension_conflicts = false;
703 std::string winning_extension_id;
704 std::string conflicting_header;
706 net::HttpRequestHeaders::Iterator modification(
707 (*delta)->modified_request_headers);
708 while (modification.GetNext() && !extension_conflicts) {
709 // This modification sets |key| to |value|.
710 const std::string& key = modification.name();
711 const std::string& value = modification.value();
713 // We must not delete anything that has been modified before.
714 if (removed_headers.find(key) != removed_headers.end() &&
715 !extension_conflicts) {
716 winning_extension_id = FindRemoveRequestHeader(deltas, key);
717 conflicting_header = key;
718 extension_conflicts = true;
721 // We must not modify anything that has been set to a *different*
722 // value before.
723 if (set_headers.find(key) != set_headers.end() &&
724 !extension_conflicts) {
725 std::string current_value;
726 if (!request_headers->GetHeader(key, &current_value) ||
727 current_value != value) {
728 winning_extension_id =
729 FindSetRequestHeader(deltas, key, current_value);
730 conflicting_header = key;
731 extension_conflicts = true;
737 // Check whether any deletion affects a request header that has been
738 // modified before.
740 std::vector<std::string>::iterator key;
741 for (key = (*delta)->deleted_request_headers.begin();
742 key != (*delta)->deleted_request_headers.end() &&
743 !extension_conflicts;
744 ++key) {
745 if (set_headers.find(*key) != set_headers.end()) {
746 std::string current_value;
747 request_headers->GetHeader(*key, &current_value);
748 winning_extension_id =
749 FindSetRequestHeader(deltas, *key, current_value);
750 conflicting_header = *key;
751 extension_conflicts = true;
756 // Now execute the modifications if there were no conflicts.
757 if (!extension_conflicts) {
758 // Copy all modifications into the original headers.
759 request_headers->MergeFrom((*delta)->modified_request_headers);
761 // Record which keys were changed.
762 net::HttpRequestHeaders::Iterator modification(
763 (*delta)->modified_request_headers);
764 while (modification.GetNext())
765 set_headers.insert(modification.name());
768 // Perform all deletions and record which keys were deleted.
770 std::vector<std::string>::iterator key;
771 for (key = (*delta)->deleted_request_headers.begin();
772 key != (*delta)->deleted_request_headers.end();
773 ++key) {
774 request_headers->RemoveHeader(*key);
775 removed_headers.insert(*key);
778 net_log->AddEvent(
779 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
780 base::Bind(&NetLogModificationCallback, delta->get()));
781 } else {
782 conflicting_extensions->insert(
783 extensions::Warning::CreateRequestHeaderConflictWarning(
784 (*delta)->extension_id, winning_extension_id,
785 conflicting_header));
786 net_log->AddEvent(
787 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
788 CreateNetLogExtensionIdCallback(delta->get()));
792 MergeCookiesInOnBeforeSendHeadersResponses(deltas, request_headers,
793 conflicting_extensions, net_log);
796 // Retrives all cookies from |override_response_headers|.
797 static ParsedResponseCookies GetResponseCookies(
798 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
799 ParsedResponseCookies result;
801 void* iter = NULL;
802 std::string value;
803 while (override_response_headers->EnumerateHeader(&iter, "Set-Cookie",
804 &value)) {
805 result.push_back(make_linked_ptr(new net::ParsedCookie(value)));
807 return result;
810 // Stores all |cookies| in |override_response_headers| deleting previously
811 // existing cookie definitions.
812 static void StoreResponseCookies(
813 const ParsedResponseCookies& cookies,
814 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
815 override_response_headers->RemoveHeader("Set-Cookie");
816 for (ParsedResponseCookies::const_iterator i = cookies.begin();
817 i != cookies.end(); ++i) {
818 override_response_headers->AddHeader("Set-Cookie: " + (*i)->ToCookieLine());
822 // Modifies |cookie| according to |modification|. Each value that is set in
823 // |modification| is applied to |cookie|.
824 static bool ApplyResponseCookieModification(ResponseCookie* modification,
825 net::ParsedCookie* cookie) {
826 bool modified = false;
827 if (modification->name.get())
828 modified |= cookie->SetName(*modification->name);
829 if (modification->value.get())
830 modified |= cookie->SetValue(*modification->value);
831 if (modification->expires.get())
832 modified |= cookie->SetExpires(*modification->expires);
833 if (modification->max_age.get())
834 modified |= cookie->SetMaxAge(base::IntToString(*modification->max_age));
835 if (modification->domain.get())
836 modified |= cookie->SetDomain(*modification->domain);
837 if (modification->path.get())
838 modified |= cookie->SetPath(*modification->path);
839 if (modification->secure.get())
840 modified |= cookie->SetIsSecure(*modification->secure);
841 if (modification->http_only.get())
842 modified |= cookie->SetIsHttpOnly(*modification->http_only);
843 return modified;
846 static bool DoesResponseCookieMatchFilter(net::ParsedCookie* cookie,
847 FilterResponseCookie* filter) {
848 if (!cookie->IsValid()) return false;
849 if (!filter) return true;
850 if (filter->name && cookie->Name() != *filter->name)
851 return false;
852 if (filter->value && cookie->Value() != *filter->value)
853 return false;
854 if (filter->expires) {
855 std::string actual_value =
856 cookie->HasExpires() ? cookie->Expires() : std::string();
857 if (actual_value != *filter->expires)
858 return false;
860 if (filter->max_age) {
861 std::string actual_value =
862 cookie->HasMaxAge() ? cookie->MaxAge() : std::string();
863 if (actual_value != base::IntToString(*filter->max_age))
864 return false;
866 if (filter->domain) {
867 std::string actual_value =
868 cookie->HasDomain() ? cookie->Domain() : std::string();
869 if (actual_value != *filter->domain)
870 return false;
872 if (filter->path) {
873 std::string actual_value =
874 cookie->HasPath() ? cookie->Path() : std::string();
875 if (actual_value != *filter->path)
876 return false;
878 if (filter->secure && cookie->IsSecure() != *filter->secure)
879 return false;
880 if (filter->http_only && cookie->IsHttpOnly() != *filter->http_only)
881 return false;
882 if (filter->age_upper_bound || filter->age_lower_bound ||
883 (filter->session_cookie && *filter->session_cookie)) {
884 int64 seconds_to_expiry;
885 bool lifetime_parsed = ParseCookieLifetime(cookie, &seconds_to_expiry);
886 if (filter->age_upper_bound && seconds_to_expiry > *filter->age_upper_bound)
887 return false;
888 if (filter->age_lower_bound && seconds_to_expiry < *filter->age_lower_bound)
889 return false;
890 if (filter->session_cookie && *filter->session_cookie && lifetime_parsed)
891 return false;
893 return true;
896 // Applies all CookieModificationType::ADD operations for response cookies of
897 // |deltas| to |cookies|. Returns whether any cookie was added.
898 static bool MergeAddResponseCookieModifications(
899 const EventResponseDeltas& deltas,
900 ParsedResponseCookies* cookies) {
901 bool modified = false;
902 // We assume here that the deltas are sorted in decreasing extension
903 // precedence (i.e. decreasing extension installation time).
904 EventResponseDeltas::const_reverse_iterator delta;
905 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
906 const ResponseCookieModifications& modifications =
907 (*delta)->response_cookie_modifications;
908 for (ResponseCookieModifications::const_iterator mod =
909 modifications.begin(); mod != modifications.end(); ++mod) {
910 if ((*mod)->type != ADD || !(*mod)->modification.get())
911 continue;
912 // Cookie names are not unique in response cookies so we always append
913 // and never override.
914 linked_ptr<net::ParsedCookie> cookie(
915 new net::ParsedCookie(std::string()));
916 ApplyResponseCookieModification((*mod)->modification.get(), cookie.get());
917 cookies->push_back(cookie);
918 modified = true;
921 return modified;
924 // Applies all CookieModificationType::EDIT operations for response cookies of
925 // |deltas| to |cookies|. Returns whether any cookie was modified.
926 static bool MergeEditResponseCookieModifications(
927 const EventResponseDeltas& deltas,
928 ParsedResponseCookies* cookies) {
929 bool modified = false;
930 // We assume here that the deltas are sorted in decreasing extension
931 // precedence (i.e. decreasing extension installation time).
932 EventResponseDeltas::const_reverse_iterator delta;
933 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
934 const ResponseCookieModifications& modifications =
935 (*delta)->response_cookie_modifications;
936 for (ResponseCookieModifications::const_iterator mod =
937 modifications.begin(); mod != modifications.end(); ++mod) {
938 if ((*mod)->type != EDIT || !(*mod)->modification.get())
939 continue;
941 for (ParsedResponseCookies::iterator cookie = cookies->begin();
942 cookie != cookies->end(); ++cookie) {
943 if (DoesResponseCookieMatchFilter(cookie->get(),
944 (*mod)->filter.get())) {
945 modified |= ApplyResponseCookieModification(
946 (*mod)->modification.get(), cookie->get());
951 return modified;
954 // Applies all CookieModificationType::REMOVE operations for response cookies of
955 // |deltas| to |cookies|. Returns whether any cookie was deleted.
956 static bool MergeRemoveResponseCookieModifications(
957 const EventResponseDeltas& deltas,
958 ParsedResponseCookies* cookies) {
959 bool modified = false;
960 // We assume here that the deltas are sorted in decreasing extension
961 // precedence (i.e. decreasing extension installation time).
962 EventResponseDeltas::const_reverse_iterator delta;
963 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
964 const ResponseCookieModifications& modifications =
965 (*delta)->response_cookie_modifications;
966 for (ResponseCookieModifications::const_iterator mod =
967 modifications.begin(); mod != modifications.end(); ++mod) {
968 if ((*mod)->type != REMOVE)
969 continue;
971 ParsedResponseCookies::iterator i = cookies->begin();
972 while (i != cookies->end()) {
973 if (DoesResponseCookieMatchFilter(i->get(),
974 (*mod)->filter.get())) {
975 i = cookies->erase(i);
976 modified = true;
977 } else {
978 ++i;
983 return modified;
986 void MergeCookiesInOnHeadersReceivedResponses(
987 const EventResponseDeltas& deltas,
988 const net::HttpResponseHeaders* original_response_headers,
989 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
990 extensions::WarningSet* conflicting_extensions,
991 const net::BoundNetLog* net_log) {
992 // Skip all work if there are no registered cookie modifications.
993 bool cookie_modifications_exist = false;
994 EventResponseDeltas::const_reverse_iterator delta;
995 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
996 cookie_modifications_exist |=
997 !(*delta)->response_cookie_modifications.empty();
999 if (!cookie_modifications_exist)
1000 return;
1002 // Only create a copy if we really want to modify the response headers.
1003 if (override_response_headers->get() == NULL) {
1004 *override_response_headers = new net::HttpResponseHeaders(
1005 original_response_headers->raw_headers());
1008 ParsedResponseCookies cookies =
1009 GetResponseCookies(*override_response_headers);
1011 bool modified = false;
1012 modified |= MergeAddResponseCookieModifications(deltas, &cookies);
1013 modified |= MergeEditResponseCookieModifications(deltas, &cookies);
1014 modified |= MergeRemoveResponseCookieModifications(deltas, &cookies);
1016 // Store new value.
1017 if (modified)
1018 StoreResponseCookies(cookies, *override_response_headers);
1021 // Converts the key of the (key, value) pair to lower case.
1022 static ResponseHeader ToLowerCase(const ResponseHeader& header) {
1023 std::string lower_key(header.first);
1024 base::StringToLowerASCII(&lower_key);
1025 return ResponseHeader(lower_key, header.second);
1028 // Returns the extension ID of the first extension in |deltas| that removes the
1029 // request header identified by |key|.
1030 static std::string FindRemoveResponseHeader(
1031 const EventResponseDeltas& deltas,
1032 const std::string& key) {
1033 std::string lower_key = base::StringToLowerASCII(key);
1034 EventResponseDeltas::const_iterator delta;
1035 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1036 ResponseHeaders::const_iterator i;
1037 for (i = (*delta)->deleted_response_headers.begin();
1038 i != (*delta)->deleted_response_headers.end(); ++i) {
1039 if (base::StringToLowerASCII(i->first) == lower_key)
1040 return (*delta)->extension_id;
1043 return std::string();
1046 void MergeOnHeadersReceivedResponses(
1047 const EventResponseDeltas& deltas,
1048 const net::HttpResponseHeaders* original_response_headers,
1049 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
1050 GURL* allowed_unsafe_redirect_url,
1051 extensions::WarningSet* conflicting_extensions,
1052 const net::BoundNetLog* net_log) {
1053 EventResponseDeltas::const_iterator delta;
1055 // Here we collect which headers we have removed or added so far due to
1056 // extensions of higher precedence. Header keys are always stored as
1057 // lower case.
1058 std::set<ResponseHeader> removed_headers;
1059 std::set<ResponseHeader> added_headers;
1061 // We assume here that the deltas are sorted in decreasing extension
1062 // precedence (i.e. decreasing extension installation time).
1063 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1064 if ((*delta)->added_response_headers.empty() &&
1065 (*delta)->deleted_response_headers.empty()) {
1066 continue;
1069 // Only create a copy if we really want to modify the response headers.
1070 if (override_response_headers->get() == NULL) {
1071 *override_response_headers = new net::HttpResponseHeaders(
1072 original_response_headers->raw_headers());
1075 // We consider modifications as pairs of (delete, add) operations.
1076 // If a header is deleted twice by different extensions we assume that the
1077 // intention was to modify it to different values and consider this a
1078 // conflict. As deltas is sorted by decreasing extension installation order,
1079 // this takes care of precedence.
1080 bool extension_conflicts = false;
1081 std::string conflicting_header;
1082 std::string winning_extension_id;
1083 ResponseHeaders::const_iterator i;
1084 for (i = (*delta)->deleted_response_headers.begin();
1085 i != (*delta)->deleted_response_headers.end(); ++i) {
1086 if (removed_headers.find(ToLowerCase(*i)) != removed_headers.end()) {
1087 winning_extension_id = FindRemoveResponseHeader(deltas, i->first);
1088 conflicting_header = i->first;
1089 extension_conflicts = true;
1090 break;
1094 // Now execute the modifications if there were no conflicts.
1095 if (!extension_conflicts) {
1096 // Delete headers
1098 for (i = (*delta)->deleted_response_headers.begin();
1099 i != (*delta)->deleted_response_headers.end(); ++i) {
1100 (*override_response_headers)->RemoveHeaderLine(i->first, i->second);
1101 removed_headers.insert(ToLowerCase(*i));
1105 // Add headers.
1107 for (i = (*delta)->added_response_headers.begin();
1108 i != (*delta)->added_response_headers.end(); ++i) {
1109 ResponseHeader lowercase_header(ToLowerCase(*i));
1110 if (added_headers.find(lowercase_header) != added_headers.end())
1111 continue;
1112 added_headers.insert(lowercase_header);
1113 (*override_response_headers)->AddHeader(i->first + ": " + i->second);
1116 net_log->AddEvent(
1117 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
1118 CreateNetLogExtensionIdCallback(delta->get()));
1119 } else {
1120 conflicting_extensions->insert(
1121 extensions::Warning::CreateResponseHeaderConflictWarning(
1122 (*delta)->extension_id, winning_extension_id,
1123 conflicting_header));
1124 net_log->AddEvent(
1125 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1126 CreateNetLogExtensionIdCallback(delta->get()));
1130 MergeCookiesInOnHeadersReceivedResponses(deltas, original_response_headers,
1131 override_response_headers, conflicting_extensions, net_log);
1133 GURL new_url;
1134 MergeRedirectUrlOfResponses(
1135 deltas, &new_url, conflicting_extensions, net_log);
1136 if (new_url.is_valid()) {
1137 // Only create a copy if we really want to modify the response headers.
1138 if (override_response_headers->get() == NULL) {
1139 *override_response_headers = new net::HttpResponseHeaders(
1140 original_response_headers->raw_headers());
1142 (*override_response_headers)->ReplaceStatusLine("HTTP/1.1 302 Found");
1143 (*override_response_headers)->RemoveHeader("location");
1144 (*override_response_headers)->AddHeader("Location: " + new_url.spec());
1145 // Explicitly mark the URL as safe for redirection, to prevent the request
1146 // from being blocked because of net::ERR_UNSAFE_REDIRECT.
1147 *allowed_unsafe_redirect_url = new_url;
1151 bool MergeOnAuthRequiredResponses(
1152 const EventResponseDeltas& deltas,
1153 net::AuthCredentials* auth_credentials,
1154 extensions::WarningSet* conflicting_extensions,
1155 const net::BoundNetLog* net_log) {
1156 CHECK(auth_credentials);
1157 bool credentials_set = false;
1158 std::string winning_extension_id;
1160 for (EventResponseDeltas::const_iterator delta = deltas.begin();
1161 delta != deltas.end();
1162 ++delta) {
1163 if (!(*delta)->auth_credentials.get())
1164 continue;
1165 bool different =
1166 auth_credentials->username() !=
1167 (*delta)->auth_credentials->username() ||
1168 auth_credentials->password() != (*delta)->auth_credentials->password();
1169 if (credentials_set && different) {
1170 conflicting_extensions->insert(
1171 extensions::Warning::CreateCredentialsConflictWarning(
1172 (*delta)->extension_id, winning_extension_id));
1173 net_log->AddEvent(
1174 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1175 CreateNetLogExtensionIdCallback(delta->get()));
1176 } else {
1177 net_log->AddEvent(
1178 net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS,
1179 CreateNetLogExtensionIdCallback(delta->get()));
1180 *auth_credentials = *(*delta)->auth_credentials;
1181 credentials_set = true;
1182 winning_extension_id = (*delta)->extension_id;
1185 return credentials_set;
1188 void ClearCacheOnNavigation() {
1189 if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
1190 ClearCacheOnNavigationOnUI();
1191 } else {
1192 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
1193 base::Bind(&ClearCacheOnNavigationOnUI));
1197 void NotifyWebRequestAPIUsed(
1198 void* browser_context_id,
1199 scoped_refptr<const extensions::Extension> extension) {
1200 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1201 content::BrowserContext* browser_context =
1202 reinterpret_cast<content::BrowserContext*>(browser_context_id);
1203 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(
1204 browser_context))
1205 return;
1207 extensions::RuntimeData* runtime_data =
1208 extensions::ExtensionSystem::Get(browser_context)->runtime_data();
1209 if (extension.get()) {
1210 if (runtime_data->HasUsedWebRequest(extension.get()))
1211 return;
1212 runtime_data->SetHasUsedWebRequest(extension.get(), true);
1215 for (content::RenderProcessHost::iterator it =
1216 content::RenderProcessHost::AllHostsIterator();
1217 !it.IsAtEnd(); it.Advance()) {
1218 content::RenderProcessHost* host = it.GetCurrentValue();
1219 if (host->GetBrowserContext() == browser_context)
1220 SendExtensionWebRequestStatusToHost(host);
1224 void SendExtensionWebRequestStatusToHost(content::RenderProcessHost* host) {
1225 content::BrowserContext* browser_context = host->GetBrowserContext();
1226 if (!browser_context)
1227 return;
1229 bool webrequest_used = false;
1230 const extensions::ExtensionSet& extensions =
1231 extensions::ExtensionRegistry::Get(browser_context)->enabled_extensions();
1232 extensions::RuntimeData* runtime_data =
1233 extensions::ExtensionSystem::Get(browser_context)->runtime_data();
1234 for (extensions::ExtensionSet::const_iterator it = extensions.begin();
1235 !webrequest_used && it != extensions.end();
1236 ++it) {
1237 webrequest_used |= runtime_data->HasUsedWebRequest(it->get());
1240 host->Send(new ExtensionMsg_UsingWebRequestAPI(webrequest_used));
1243 // Converts the |name|, |value| pair of a http header to a HttpHeaders
1244 // dictionary. Ownership is passed to the caller.
1245 base::DictionaryValue* CreateHeaderDictionary(
1246 const std::string& name, const std::string& value) {
1247 base::DictionaryValue* header = new base::DictionaryValue();
1248 header->SetString(keys::kHeaderNameKey, name);
1249 if (base::IsStringUTF8(value)) {
1250 header->SetString(keys::kHeaderValueKey, value);
1251 } else {
1252 header->Set(keys::kHeaderBinaryValueKey,
1253 StringToCharList(value));
1255 return header;
1258 #define ARRAYEND(array) (array + arraysize(array))
1260 bool IsRelevantResourceType(ResourceType type) {
1261 ResourceType* iter =
1262 std::find(kResourceTypeValues,
1263 kResourceTypeValues + kResourceTypeValuesLength,
1264 type);
1265 return iter != (kResourceTypeValues + kResourceTypeValuesLength);
1268 const char* ResourceTypeToString(ResourceType type) {
1269 ResourceType* iter =
1270 std::find(kResourceTypeValues,
1271 kResourceTypeValues + kResourceTypeValuesLength,
1272 type);
1273 if (iter == (kResourceTypeValues + kResourceTypeValuesLength))
1274 return "other";
1276 return kResourceTypeStrings[iter - kResourceTypeValues];
1279 bool ParseResourceType(const std::string& type_str,
1280 ResourceType* type) {
1281 const char** iter =
1282 std::find(kResourceTypeStrings,
1283 kResourceTypeStrings + kResourceTypeStringsLength,
1284 type_str);
1285 if (iter == (kResourceTypeStrings + kResourceTypeStringsLength))
1286 return false;
1287 *type = kResourceTypeValues[iter - kResourceTypeStrings];
1288 return true;
1291 } // namespace extension_web_request_api_helpers