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