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.
4
5 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
6
7 #include <cmath>
8
9 #include "base/bind.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/extensions/api/web_request/web_request_api.h"
17 #include "chrome/browser/extensions/extension_warning_set.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "chrome/browser/renderer_host/web_cache_manager.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "extensions/browser/extension_system.h"
23 #include "extensions/browser/runtime_data.h"
24 #include "net/base/net_log.h"
25 #include "net/cookies/cookie_util.h"
26 #include "net/cookies/parsed_cookie.h"
27 #include "net/http/http_util.h"
28 #include "net/url_request/url_request.h"
29 #include "url/url_constants.h"
30
31 // TODO(battre): move all static functions into an anonymous namespace at the
32 // top of this file.
33
34 using base::Time;
35 using extensions::ExtensionWarning;
36
37 namespace extension_web_request_api_helpers {
38
39 namespace {
40
41 // A ParsedRequestCookie consists of the key and value of the cookie.
42 typedef std::pair<base::StringPiece, base::StringPiece> ParsedRequestCookie;
43 typedef std::vector<ParsedRequestCookie> ParsedRequestCookies;
44 typedef std::vector<linked_ptr<net::ParsedCookie> > ParsedResponseCookies;
45
46 static const char* kResourceTypeStrings[] = {
47 "main_frame",
48 "sub_frame",
49 "stylesheet",
50 "script",
51 "image",
52 "object",
53 "xmlhttprequest",
54 "other",
55 "other",
56 };
57
58 static ResourceType::Type kResourceTypeValues[] = {
59 ResourceType::MAIN_FRAME,
60 ResourceType::SUB_FRAME,
61 ResourceType::STYLESHEET,
62 ResourceType::SCRIPT,
63 ResourceType::IMAGE,
64 ResourceType::OBJECT,
65 ResourceType::XHR,
66 ResourceType::LAST_TYPE, // represents "other"
67 // TODO(jochen): We duplicate the last entry, so the array's size is not a
68 // power of two. If it is, this triggers a bug in gcc 4.4 in Release builds
69 // (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949). Once we use a version
70 // of gcc with this bug fixed, or the array is changed so this duplicate
71 // entry is no longer required, this should be removed.
72 ResourceType::LAST_TYPE,
73 };
74
75 COMPILE_ASSERT(
76 arraysize(kResourceTypeStrings) == arraysize(kResourceTypeValues),
77 keep_resource_types_in_sync);
78
ClearCacheOnNavigationOnUI()79 void ClearCacheOnNavigationOnUI() {
80 WebCacheManager::GetInstance()->ClearCacheOnNavigation();
81 }
82
ParseCookieLifetime(net::ParsedCookie * cookie,int64 * seconds_till_expiry)83 bool ParseCookieLifetime(net::ParsedCookie* cookie,
84 int64* seconds_till_expiry) {
85 // 'Max-Age' is processed first because according to:
86 // http://tools.ietf.org/html/rfc6265#section-5.3 'Max-Age' attribute
87 // overrides 'Expires' attribute.
88 if (cookie->HasMaxAge() &&
89 base::StringToInt64(cookie->MaxAge(), seconds_till_expiry)) {
90 return true;
91 }
92
93 Time parsed_expiry_time;
94 if (cookie->HasExpires())
95 parsed_expiry_time = net::cookie_util::ParseCookieTime(cookie->Expires());
96
97 if (!parsed_expiry_time.is_null()) {
98 *seconds_till_expiry =
99 ceil((parsed_expiry_time - Time::Now()).InSecondsF());
100 return *seconds_till_expiry >= 0;
101 }
102 return false;
103 }
104
NullableEquals(const int * a,const int * b)105 bool NullableEquals(const int* a, const int* b) {
106 if ((a && !b) || (!a && b))
107 return false;
108 return (!a) || (*a == *b);
109 }
110
NullableEquals(const bool * a,const bool * b)111 bool NullableEquals(const bool* a, const bool* b) {
112 if ((a && !b) || (!a && b))
113 return false;
114 return (!a) || (*a == *b);
115 }
116
NullableEquals(const std::string * a,const std::string * b)117 bool NullableEquals(const std::string* a, const std::string* b) {
118 if ((a && !b) || (!a && b))
119 return false;
120 return (!a) || (*a == *b);
121 }
122
123 } // namespace
124
RequestCookie()125 RequestCookie::RequestCookie() {}
~RequestCookie()126 RequestCookie::~RequestCookie() {}
127
NullableEquals(const RequestCookie * a,const RequestCookie * b)128 bool NullableEquals(const RequestCookie* a, const RequestCookie* b) {
129 if ((a && !b) || (!a && b))
130 return false;
131 if (!a)
132 return true;
133 return NullableEquals(a->name.get(), b->name.get()) &&
134 NullableEquals(a->value.get(), b->value.get());
135 }
136
ResponseCookie()137 ResponseCookie::ResponseCookie() {}
~ResponseCookie()138 ResponseCookie::~ResponseCookie() {}
139
NullableEquals(const ResponseCookie * a,const ResponseCookie * b)140 bool NullableEquals(const ResponseCookie* a, const ResponseCookie* b) {
141 if ((a && !b) || (!a && b))
142 return false;
143 if (!a)
144 return true;
145 return NullableEquals(a->name.get(), b->name.get()) &&
146 NullableEquals(a->value.get(), b->value.get()) &&
147 NullableEquals(a->expires.get(), b->expires.get()) &&
148 NullableEquals(a->max_age.get(), b->max_age.get()) &&
149 NullableEquals(a->domain.get(), b->domain.get()) &&
150 NullableEquals(a->path.get(), b->path.get()) &&
151 NullableEquals(a->secure.get(), b->secure.get()) &&
152 NullableEquals(a->http_only.get(), b->http_only.get());
153 }
154
FilterResponseCookie()155 FilterResponseCookie::FilterResponseCookie() {}
~FilterResponseCookie()156 FilterResponseCookie::~FilterResponseCookie() {}
157
NullableEquals(const FilterResponseCookie * a,const FilterResponseCookie * b)158 bool NullableEquals(const FilterResponseCookie* a,
159 const FilterResponseCookie* b) {
160 if ((a && !b) || (!a && b))
161 return false;
162 if (!a)
163 return true;
164 return NullableEquals(a->age_lower_bound.get(), b->age_lower_bound.get()) &&
165 NullableEquals(a->age_upper_bound.get(), b->age_upper_bound.get()) &&
166 NullableEquals(a->session_cookie.get(), b->session_cookie.get());
167 }
168
RequestCookieModification()169 RequestCookieModification::RequestCookieModification() {}
~RequestCookieModification()170 RequestCookieModification::~RequestCookieModification() {}
171
NullableEquals(const RequestCookieModification * a,const RequestCookieModification * b)172 bool NullableEquals(const RequestCookieModification* a,
173 const RequestCookieModification* b) {
174 if ((a && !b) || (!a && b))
175 return false;
176 if (!a)
177 return true;
178 return NullableEquals(a->filter.get(), b->filter.get()) &&
179 NullableEquals(a->modification.get(), b->modification.get());
180 }
181
ResponseCookieModification()182 ResponseCookieModification::ResponseCookieModification() : type(ADD) {}
~ResponseCookieModification()183 ResponseCookieModification::~ResponseCookieModification() {}
184
NullableEquals(const ResponseCookieModification * a,const ResponseCookieModification * b)185 bool NullableEquals(const ResponseCookieModification* a,
186 const ResponseCookieModification* b) {
187 if ((a && !b) || (!a && b))
188 return false;
189 if (!a)
190 return true;
191 return a->type == b->type &&
192 NullableEquals(a->filter.get(), b->filter.get()) &&
193 NullableEquals(a->modification.get(), b->modification.get());
194 }
195
EventResponseDelta(const std::string & extension_id,const base::Time & extension_install_time)196 EventResponseDelta::EventResponseDelta(
197 const std::string& extension_id, const base::Time& extension_install_time)
198 : extension_id(extension_id),
199 extension_install_time(extension_install_time),
200 cancel(false) {
201 }
202
~EventResponseDelta()203 EventResponseDelta::~EventResponseDelta() {
204 }
205
206
207 // Creates a NetLog callback the returns a Value with the ID of the extension
208 // that caused an event. |delta| must remain valid for the lifetime of the
209 // callback.
CreateNetLogExtensionIdCallback(const EventResponseDelta * delta)210 net::NetLog::ParametersCallback CreateNetLogExtensionIdCallback(
211 const EventResponseDelta* delta) {
212 return net::NetLog::StringCallback("extension_id", &delta->extension_id);
213 }
214
215 // Creates NetLog parameters to indicate that an extension modified a request.
216 // Caller takes ownership of returned value.
NetLogModificationCallback(const EventResponseDelta * delta,net::NetLog::LogLevel log_level)217 base::Value* NetLogModificationCallback(
218 const EventResponseDelta* delta,
219 net::NetLog::LogLevel log_level) {
220 base::DictionaryValue* dict = new base::DictionaryValue();
221 dict->SetString("extension_id", delta->extension_id);
222
223 base::ListValue* modified_headers = new base::ListValue();
224 net::HttpRequestHeaders::Iterator modification(
225 delta->modified_request_headers);
226 while (modification.GetNext()) {
227 std::string line = modification.name() + ": " + modification.value();
228 modified_headers->Append(new base::StringValue(line));
229 }
230 dict->Set("modified_headers", modified_headers);
231
232 base::ListValue* deleted_headers = new base::ListValue();
233 for (std::vector<std::string>::const_iterator key =
234 delta->deleted_request_headers.begin();
235 key != delta->deleted_request_headers.end();
236 ++key) {
237 deleted_headers->Append(new base::StringValue(*key));
238 }
239 dict->Set("deleted_headers", deleted_headers);
240 return dict;
241 }
242
InDecreasingExtensionInstallationTimeOrder(const linked_ptr<EventResponseDelta> & a,const linked_ptr<EventResponseDelta> & b)243 bool InDecreasingExtensionInstallationTimeOrder(
244 const linked_ptr<EventResponseDelta>& a,
245 const linked_ptr<EventResponseDelta>& b) {
246 return a->extension_install_time > b->extension_install_time;
247 }
248
StringToCharList(const std::string & s)249 base::ListValue* StringToCharList(const std::string& s) {
250 base::ListValue* result = new base::ListValue;
251 for (size_t i = 0, n = s.size(); i < n; ++i) {
252 result->Append(
253 new base::FundamentalValue(
254 *reinterpret_cast<const unsigned char*>(&s[i])));
255 }
256 return result;
257 }
258
CharListToString(const base::ListValue * list,std::string * out)259 bool CharListToString(const base::ListValue* list, std::string* out) {
260 if (!list)
261 return false;
262 const size_t list_length = list->GetSize();
263 out->resize(list_length);
264 int value = 0;
265 for (size_t i = 0; i < list_length; ++i) {
266 if (!list->GetInteger(i, &value) || value < 0 || value > 255)
267 return false;
268 unsigned char tmp = static_cast<unsigned char>(value);
269 (*out)[i] = *reinterpret_cast<char*>(&tmp);
270 }
271 return true;
272 }
273
CalculateOnBeforeRequestDelta(const std::string & extension_id,const base::Time & extension_install_time,bool cancel,const GURL & new_url)274 EventResponseDelta* CalculateOnBeforeRequestDelta(
275 const std::string& extension_id,
276 const base::Time& extension_install_time,
277 bool cancel,
278 const GURL& new_url) {
279 EventResponseDelta* result =
280 new EventResponseDelta(extension_id, extension_install_time);
281 result->cancel = cancel;
282 result->new_url = new_url;
283 return result;
284 }
285
CalculateOnBeforeSendHeadersDelta(const std::string & extension_id,const base::Time & extension_install_time,bool cancel,net::HttpRequestHeaders * old_headers,net::HttpRequestHeaders * new_headers)286 EventResponseDelta* CalculateOnBeforeSendHeadersDelta(
287 const std::string& extension_id,
288 const base::Time& extension_install_time,
289 bool cancel,
290 net::HttpRequestHeaders* old_headers,
291 net::HttpRequestHeaders* new_headers) {
292 EventResponseDelta* result =
293 new EventResponseDelta(extension_id, extension_install_time);
294 result->cancel = cancel;
295
296 // The event listener might not have passed any new headers if he
297 // just wanted to cancel the request.
298 if (new_headers) {
299 // Find deleted headers.
300 {
301 net::HttpRequestHeaders::Iterator i(*old_headers);
302 while (i.GetNext()) {
303 if (!new_headers->HasHeader(i.name())) {
304 result->deleted_request_headers.push_back(i.name());
305 }
306 }
307 }
308
309 // Find modified headers.
310 {
311 net::HttpRequestHeaders::Iterator i(*new_headers);
312 while (i.GetNext()) {
313 std::string value;
314 if (!old_headers->GetHeader(i.name(), &value) || i.value() != value) {
315 result->modified_request_headers.SetHeader(i.name(), i.value());
316 }
317 }
318 }
319 }
320 return result;
321 }
322
CalculateOnHeadersReceivedDelta(const std::string & extension_id,const base::Time & extension_install_time,bool cancel,const GURL & new_url,const net::HttpResponseHeaders * old_response_headers,ResponseHeaders * new_response_headers)323 EventResponseDelta* CalculateOnHeadersReceivedDelta(
324 const std::string& extension_id,
325 const base::Time& extension_install_time,
326 bool cancel,
327 const GURL& new_url,
328 const net::HttpResponseHeaders* old_response_headers,
329 ResponseHeaders* new_response_headers) {
330 EventResponseDelta* result =
331 new EventResponseDelta(extension_id, extension_install_time);
332 result->cancel = cancel;
333 result->new_url = new_url;
334
335 if (!new_response_headers)
336 return result;
337
338 // Find deleted headers (header keys are treated case insensitively).
339 {
340 void* iter = NULL;
341 std::string name;
342 std::string value;
343 while (old_response_headers->EnumerateHeaderLines(&iter, &name, &value)) {
344 std::string name_lowercase(name);
345 StringToLowerASCII(&name_lowercase);
346
347 bool header_found = false;
348 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
349 i != new_response_headers->end(); ++i) {
350 if (LowerCaseEqualsASCII(i->first, name_lowercase.c_str()) &&
351 value == i->second) {
352 header_found = true;
353 break;
354 }
355 }
356 if (!header_found)
357 result->deleted_response_headers.push_back(ResponseHeader(name, value));
358 }
359 }
360
361 // Find added headers (header keys are treated case insensitively).
362 {
363 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
364 i != new_response_headers->end(); ++i) {
365 void* iter = NULL;
366 std::string value;
367 bool header_found = false;
368 while (old_response_headers->EnumerateHeader(&iter, i->first, &value) &&
369 !header_found) {
370 header_found = (value == i->second);
371 }
372 if (!header_found)
373 result->added_response_headers.push_back(*i);
374 }
375 }
376
377 return result;
378 }
379
CalculateOnAuthRequiredDelta(const std::string & extension_id,const base::Time & extension_install_time,bool cancel,scoped_ptr<net::AuthCredentials> * auth_credentials)380 EventResponseDelta* CalculateOnAuthRequiredDelta(
381 const std::string& extension_id,
382 const base::Time& extension_install_time,
383 bool cancel,
384 scoped_ptr<net::AuthCredentials>* auth_credentials) {
385 EventResponseDelta* result =
386 new EventResponseDelta(extension_id, extension_install_time);
387 result->cancel = cancel;
388 result->auth_credentials.swap(*auth_credentials);
389 return result;
390 }
391
MergeCancelOfResponses(const EventResponseDeltas & deltas,bool * canceled,const net::BoundNetLog * net_log)392 void MergeCancelOfResponses(
393 const EventResponseDeltas& deltas,
394 bool* canceled,
395 const net::BoundNetLog* net_log) {
396 for (EventResponseDeltas::const_iterator i = deltas.begin();
397 i != deltas.end(); ++i) {
398 if ((*i)->cancel) {
399 *canceled = true;
400 net_log->AddEvent(
401 net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST,
402 CreateNetLogExtensionIdCallback(i->get()));
403 break;
404 }
405 }
406 }
407
408 // Helper function for MergeRedirectUrlOfResponses() that allows ignoring
409 // all redirects but those to data:// urls and about:blank. This is important
410 // to treat these URLs as "cancel urls", i.e. URLs that extensions redirect
411 // to if they want to express that they want to cancel a request. This reduces
412 // the number of conflicts that we need to flag, as canceling is considered
413 // a higher precedence operation that redirects.
414 // Returns whether a redirect occurred.
MergeRedirectUrlOfResponsesHelper(const EventResponseDeltas & deltas,GURL * new_url,extensions::ExtensionWarningSet * conflicting_extensions,const net::BoundNetLog * net_log,bool consider_only_cancel_scheme_urls)415 static bool MergeRedirectUrlOfResponsesHelper(
416 const EventResponseDeltas& deltas,
417 GURL* new_url,
418 extensions::ExtensionWarningSet* conflicting_extensions,
419 const net::BoundNetLog* net_log,
420 bool consider_only_cancel_scheme_urls) {
421 bool redirected = false;
422
423 // Extension that determines the |new_url|.
424 std::string winning_extension_id;
425 EventResponseDeltas::const_iterator delta;
426 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
427 if ((*delta)->new_url.is_empty())
428 continue;
429 if (consider_only_cancel_scheme_urls &&
430 !(*delta)->new_url.SchemeIs(url::kDataScheme) &&
431 (*delta)->new_url.spec() != "about:blank") {
432 continue;
433 }
434
435 if (!redirected || *new_url == (*delta)->new_url) {
436 *new_url = (*delta)->new_url;
437 winning_extension_id = (*delta)->extension_id;
438 redirected = true;
439 net_log->AddEvent(
440 net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST,
441 CreateNetLogExtensionIdCallback(delta->get()));
442 } else {
443 conflicting_extensions->insert(
444 ExtensionWarning::CreateRedirectConflictWarning(
445 (*delta)->extension_id,
446 winning_extension_id,
447 (*delta)->new_url,
448 *new_url));
449 net_log->AddEvent(
450 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
451 CreateNetLogExtensionIdCallback(delta->get()));
452 }
453 }
454 return redirected;
455 }
456
MergeRedirectUrlOfResponses(const EventResponseDeltas & deltas,GURL * new_url,extensions::ExtensionWarningSet * conflicting_extensions,const net::BoundNetLog * net_log)457 void MergeRedirectUrlOfResponses(
458 const EventResponseDeltas& deltas,
459 GURL* new_url,
460 extensions::ExtensionWarningSet* conflicting_extensions,
461 const net::BoundNetLog* net_log) {
462
463 // First handle only redirects to data:// URLs and about:blank. These are a
464 // special case as they represent a way of cancelling a request.
465 if (MergeRedirectUrlOfResponsesHelper(
466 deltas, new_url, conflicting_extensions, net_log, true)) {
467 // If any extension cancelled a request by redirecting to a data:// URL or
468 // about:blank, we don't consider the other redirects.
469 return;
470 }
471
472 // Handle all other redirects.
473 MergeRedirectUrlOfResponsesHelper(
474 deltas, new_url, conflicting_extensions, net_log, false);
475 }
476
MergeOnBeforeRequestResponses(const EventResponseDeltas & deltas,GURL * new_url,extensions::ExtensionWarningSet * conflicting_extensions,const net::BoundNetLog * net_log)477 void MergeOnBeforeRequestResponses(
478 const EventResponseDeltas& deltas,
479 GURL* new_url,
480 extensions::ExtensionWarningSet* conflicting_extensions,
481 const net::BoundNetLog* net_log) {
482 MergeRedirectUrlOfResponses(deltas, new_url, conflicting_extensions, net_log);
483 }
484
485 // Assumes that |header_value| is the cookie header value of a HTTP Request
486 // following the cookie-string schema of RFC 6265, section 4.2.1, and returns
487 // cookie name/value pairs. If cookie values are presented in double quotes,
488 // these will appear in |parsed| as well. We can assume that the cookie header
489 // is written by Chromium and therefore, well-formed.
ParseRequestCookieLine(const std::string & header_value,ParsedRequestCookies * parsed_cookies)490 static void ParseRequestCookieLine(
491 const std::string& header_value,
492 ParsedRequestCookies* parsed_cookies) {
493 std::string::const_iterator i = header_value.begin();
494 while (i != header_value.end()) {
495 // Here we are at the beginning of a cookie.
496
497 // Eat whitespace.
498 while (i != header_value.end() && *i == ' ') ++i;
499 if (i == header_value.end()) return;
500
501 // Find cookie name.
502 std::string::const_iterator cookie_name_beginning = i;
503 while (i != header_value.end() && *i != '=') ++i;
504 base::StringPiece cookie_name(cookie_name_beginning, i);
505
506 // Find cookie value.
507 base::StringPiece cookie_value;
508 if (i != header_value.end()) { // Cookies may have no value.
509 ++i; // Skip '='.
510 std::string::const_iterator cookie_value_beginning = i;
511 if (*i == '"') {
512 ++i; // Skip '"'.
513 while (i != header_value.end() && *i != '"') ++i;
514 if (i == header_value.end()) return;
515 ++i; // Skip '"'.
516 cookie_value = base::StringPiece(cookie_value_beginning, i);
517 // i points to character after '"', potentially a ';'
518 } else {
519 while (i != header_value.end() && *i != ';') ++i;
520 cookie_value = base::StringPiece(cookie_value_beginning, i);
521 // i points to ';' or end of string.
522 }
523 }
524 parsed_cookies->push_back(make_pair(cookie_name, cookie_value));
525 // Eat ';'
526 if (i != header_value.end()) ++i;
527 }
528 }
529
530 // Writes all cookies of |parsed_cookies| into a HTTP Request header value
531 // that belongs to the "Cookie" header.
SerializeRequestCookieLine(const ParsedRequestCookies & parsed_cookies)532 static std::string SerializeRequestCookieLine(
533 const ParsedRequestCookies& parsed_cookies) {
534 std::string buffer;
535 for (ParsedRequestCookies::const_iterator i = parsed_cookies.begin();
536 i != parsed_cookies.end(); ++i) {
537 if (!buffer.empty())
538 buffer += "; ";
539 buffer += i->first.as_string();
540 if (!i->second.empty())
541 buffer += "=" + i->second.as_string();
542 }
543 return buffer;
544 }
545
DoesRequestCookieMatchFilter(const ParsedRequestCookie & cookie,RequestCookie * filter)546 static bool DoesRequestCookieMatchFilter(
547 const ParsedRequestCookie& cookie,
548 RequestCookie* filter) {
549 if (!filter) return true;
550 if (filter->name.get() && cookie.first != *filter->name) return false;
551 if (filter->value.get() && cookie.second != *filter->value) return false;
552 return true;
553 }
554
555 // Applies all CookieModificationType::ADD operations for request cookies of
556 // |deltas| to |cookies|. Returns whether any cookie was added.
MergeAddRequestCookieModifications(const EventResponseDeltas & deltas,ParsedRequestCookies * cookies)557 static bool MergeAddRequestCookieModifications(
558 const EventResponseDeltas& deltas,
559 ParsedRequestCookies* cookies) {
560 bool modified = false;
561 // We assume here that the deltas are sorted in decreasing extension
562 // precedence (i.e. decreasing extension installation time).
563 EventResponseDeltas::const_reverse_iterator delta;
564 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
565 const RequestCookieModifications& modifications =
566 (*delta)->request_cookie_modifications;
567 for (RequestCookieModifications::const_iterator mod = modifications.begin();
568 mod != modifications.end(); ++mod) {
569 if ((*mod)->type != ADD || !(*mod)->modification.get())
570 continue;
571 std::string* new_name = (*mod)->modification->name.get();
572 std::string* new_value = (*mod)->modification->value.get();
573 if (!new_name || !new_value)
574 continue;
575
576 bool cookie_with_same_name_found = false;
577 for (ParsedRequestCookies::iterator cookie = cookies->begin();
578 cookie != cookies->end() && !cookie_with_same_name_found; ++cookie) {
579 if (cookie->first == *new_name) {
580 if (cookie->second != *new_value) {
581 cookie->second = *new_value;
582 modified = true;
583 }
584 cookie_with_same_name_found = true;
585 }
586 }
587 if (!cookie_with_same_name_found) {
588 cookies->push_back(std::make_pair(base::StringPiece(*new_name),
589 base::StringPiece(*new_value)));
590 modified = true;
591 }
592 }
593 }
594 return modified;
595 }
596
597 // Applies all CookieModificationType::EDIT operations for request cookies of
598 // |deltas| to |cookies|. Returns whether any cookie was modified.
MergeEditRequestCookieModifications(const EventResponseDeltas & deltas,ParsedRequestCookies * cookies)599 static bool MergeEditRequestCookieModifications(
600 const EventResponseDeltas& deltas,
601 ParsedRequestCookies* cookies) {
602 bool modified = false;
603 // We assume here that the deltas are sorted in decreasing extension
604 // precedence (i.e. decreasing extension installation time).
605 EventResponseDeltas::const_reverse_iterator delta;
606 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
607 const RequestCookieModifications& modifications =
608 (*delta)->request_cookie_modifications;
609 for (RequestCookieModifications::const_iterator mod = modifications.begin();
610 mod != modifications.end(); ++mod) {
611 if ((*mod)->type != EDIT || !(*mod)->modification.get())
612 continue;
613
614 std::string* new_value = (*mod)->modification->value.get();
615 RequestCookie* filter = (*mod)->filter.get();
616 for (ParsedRequestCookies::iterator cookie = cookies->begin();
617 cookie != cookies->end(); ++cookie) {
618 if (!DoesRequestCookieMatchFilter(*cookie, filter))
619 continue;
620 // If the edit operation tries to modify the cookie name, we just ignore
621 // this. We only modify the cookie value.
622 if (new_value && cookie->second != *new_value) {
623 cookie->second = *new_value;
624 modified = true;
625 }
626 }
627 }
628 }
629 return modified;
630 }
631
632 // Applies all CookieModificationType::REMOVE operations for request cookies of
633 // |deltas| to |cookies|. Returns whether any cookie was deleted.
MergeRemoveRequestCookieModifications(const EventResponseDeltas & deltas,ParsedRequestCookies * cookies)634 static bool MergeRemoveRequestCookieModifications(
635 const EventResponseDeltas& deltas,
636 ParsedRequestCookies* cookies) {
637 bool modified = false;
638 // We assume here that the deltas are sorted in decreasing extension
639 // precedence (i.e. decreasing extension installation time).
640 EventResponseDeltas::const_reverse_iterator delta;
641 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
642 const RequestCookieModifications& modifications =
643 (*delta)->request_cookie_modifications;
644 for (RequestCookieModifications::const_iterator mod = modifications.begin();
645 mod != modifications.end(); ++mod) {
646 if ((*mod)->type != REMOVE)
647 continue;
648
649 RequestCookie* filter = (*mod)->filter.get();
650 ParsedRequestCookies::iterator i = cookies->begin();
651 while (i != cookies->end()) {
652 if (DoesRequestCookieMatchFilter(*i, filter)) {
653 i = cookies->erase(i);
654 modified = true;
655 } else {
656 ++i;
657 }
658 }
659 }
660 }
661 return modified;
662 }
663
MergeCookiesInOnBeforeSendHeadersResponses(const EventResponseDeltas & deltas,net::HttpRequestHeaders * request_headers,extensions::ExtensionWarningSet * conflicting_extensions,const net::BoundNetLog * net_log)664 void MergeCookiesInOnBeforeSendHeadersResponses(
665 const EventResponseDeltas& deltas,
666 net::HttpRequestHeaders* request_headers,
667 extensions::ExtensionWarningSet* conflicting_extensions,
668 const net::BoundNetLog* net_log) {
669 // Skip all work if there are no registered cookie modifications.
670 bool cookie_modifications_exist = false;
671 EventResponseDeltas::const_iterator delta;
672 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
673 cookie_modifications_exist |=
674 !(*delta)->request_cookie_modifications.empty();
675 }
676 if (!cookie_modifications_exist)
677 return;
678
679 // Parse old cookie line.
680 std::string cookie_header;
681 request_headers->GetHeader(net::HttpRequestHeaders::kCookie, &cookie_header);
682 ParsedRequestCookies cookies;
683 ParseRequestCookieLine(cookie_header, &cookies);
684
685 // Modify cookies.
686 bool modified = false;
687 modified |= MergeAddRequestCookieModifications(deltas, &cookies);
688 modified |= MergeEditRequestCookieModifications(deltas, &cookies);
689 modified |= MergeRemoveRequestCookieModifications(deltas, &cookies);
690
691 // Reassemble and store new cookie line.
692 if (modified) {
693 std::string new_cookie_header = SerializeRequestCookieLine(cookies);
694 request_headers->SetHeader(net::HttpRequestHeaders::kCookie,
695 new_cookie_header);
696 }
697 }
698
699 // Returns the extension ID of the first extension in |deltas| that sets the
700 // request header identified by |key| to |value|.
FindSetRequestHeader(const EventResponseDeltas & deltas,const std::string & key,const std::string & value)701 static std::string FindSetRequestHeader(
702 const EventResponseDeltas& deltas,
703 const std::string& key,
704 const std::string& value) {
705 EventResponseDeltas::const_iterator delta;
706 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
707 net::HttpRequestHeaders::Iterator modification(
708 (*delta)->modified_request_headers);
709 while (modification.GetNext()) {
710 if (key == modification.name() && value == modification.value())
711 return (*delta)->extension_id;
712 }
713 }
714 return std::string();
715 }
716
717 // Returns the extension ID of the first extension in |deltas| that removes the
718 // request header identified by |key|.
FindRemoveRequestHeader(const EventResponseDeltas & deltas,const std::string & key)719 static std::string FindRemoveRequestHeader(
720 const EventResponseDeltas& deltas,
721 const std::string& key) {
722 EventResponseDeltas::const_iterator delta;
723 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
724 std::vector<std::string>::iterator i;
725 for (i = (*delta)->deleted_request_headers.begin();
726 i != (*delta)->deleted_request_headers.end();
727 ++i) {
728 if (*i == key)
729 return (*delta)->extension_id;
730 }
731 }
732 return std::string();
733 }
734
MergeOnBeforeSendHeadersResponses(const EventResponseDeltas & deltas,net::HttpRequestHeaders * request_headers,extensions::ExtensionWarningSet * conflicting_extensions,const net::BoundNetLog * net_log)735 void MergeOnBeforeSendHeadersResponses(
736 const EventResponseDeltas& deltas,
737 net::HttpRequestHeaders* request_headers,
738 extensions::ExtensionWarningSet* conflicting_extensions,
739 const net::BoundNetLog* net_log) {
740 EventResponseDeltas::const_iterator delta;
741
742 // Here we collect which headers we have removed or set to new values
743 // so far due to extensions of higher precedence.
744 std::set<std::string> removed_headers;
745 std::set<std::string> set_headers;
746
747 // We assume here that the deltas are sorted in decreasing extension
748 // precedence (i.e. decreasing extension installation time).
749 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
750 if ((*delta)->modified_request_headers.IsEmpty() &&
751 (*delta)->deleted_request_headers.empty()) {
752 continue;
753 }
754
755 // Check whether any modification affects a request header that
756 // has been modified differently before. As deltas is sorted by decreasing
757 // extension installation order, this takes care of precedence.
758 bool extension_conflicts = false;
759 std::string winning_extension_id;
760 std::string conflicting_header;
761 {
762 net::HttpRequestHeaders::Iterator modification(
763 (*delta)->modified_request_headers);
764 while (modification.GetNext() && !extension_conflicts) {
765 // This modification sets |key| to |value|.
766 const std::string& key = modification.name();
767 const std::string& value = modification.value();
768
769 // We must not delete anything that has been modified before.
770 if (removed_headers.find(key) != removed_headers.end() &&
771 !extension_conflicts) {
772 winning_extension_id = FindRemoveRequestHeader(deltas, key);
773 conflicting_header = key;
774 extension_conflicts = true;
775 }
776
777 // We must not modify anything that has been set to a *different*
778 // value before.
779 if (set_headers.find(key) != set_headers.end() &&
780 !extension_conflicts) {
781 std::string current_value;
782 if (!request_headers->GetHeader(key, ¤t_value) ||
783 current_value != value) {
784 winning_extension_id =
785 FindSetRequestHeader(deltas, key, current_value);
786 conflicting_header = key;
787 extension_conflicts = true;
788 }
789 }
790 }
791 }
792
793 // Check whether any deletion affects a request header that has been
794 // modified before.
795 {
796 std::vector<std::string>::iterator key;
797 for (key = (*delta)->deleted_request_headers.begin();
798 key != (*delta)->deleted_request_headers.end() &&
799 !extension_conflicts;
800 ++key) {
801 if (set_headers.find(*key) != set_headers.end()) {
802 std::string current_value;
803 request_headers->GetHeader(*key, ¤t_value);
804 winning_extension_id =
805 FindSetRequestHeader(deltas, *key, current_value);
806 conflicting_header = *key;
807 extension_conflicts = true;
808 }
809 }
810 }
811
812 // Now execute the modifications if there were no conflicts.
813 if (!extension_conflicts) {
814 // Copy all modifications into the original headers.
815 request_headers->MergeFrom((*delta)->modified_request_headers);
816 {
817 // Record which keys were changed.
818 net::HttpRequestHeaders::Iterator modification(
819 (*delta)->modified_request_headers);
820 while (modification.GetNext())
821 set_headers.insert(modification.name());
822 }
823
824 // Perform all deletions and record which keys were deleted.
825 {
826 std::vector<std::string>::iterator key;
827 for (key = (*delta)->deleted_request_headers.begin();
828 key != (*delta)->deleted_request_headers.end();
829 ++key) {
830 request_headers->RemoveHeader(*key);
831 removed_headers.insert(*key);
832 }
833 }
834 net_log->AddEvent(
835 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
836 base::Bind(&NetLogModificationCallback, delta->get()));
837 } else {
838 conflicting_extensions->insert(
839 ExtensionWarning::CreateRequestHeaderConflictWarning(
840 (*delta)->extension_id, winning_extension_id,
841 conflicting_header));
842 net_log->AddEvent(
843 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
844 CreateNetLogExtensionIdCallback(delta->get()));
845 }
846 }
847
848 MergeCookiesInOnBeforeSendHeadersResponses(deltas, request_headers,
849 conflicting_extensions, net_log);
850 }
851
852 // Retrives all cookies from |override_response_headers|.
GetResponseCookies(scoped_refptr<net::HttpResponseHeaders> override_response_headers)853 static ParsedResponseCookies GetResponseCookies(
854 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
855 ParsedResponseCookies result;
856
857 void* iter = NULL;
858 std::string value;
859 while (override_response_headers->EnumerateHeader(&iter, "Set-Cookie",
860 &value)) {
861 result.push_back(make_linked_ptr(new net::ParsedCookie(value)));
862 }
863 return result;
864 }
865
866 // Stores all |cookies| in |override_response_headers| deleting previously
867 // existing cookie definitions.
StoreResponseCookies(const ParsedResponseCookies & cookies,scoped_refptr<net::HttpResponseHeaders> override_response_headers)868 static void StoreResponseCookies(
869 const ParsedResponseCookies& cookies,
870 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
871 override_response_headers->RemoveHeader("Set-Cookie");
872 for (ParsedResponseCookies::const_iterator i = cookies.begin();
873 i != cookies.end(); ++i) {
874 override_response_headers->AddHeader("Set-Cookie: " + (*i)->ToCookieLine());
875 }
876 }
877
878 // Modifies |cookie| according to |modification|. Each value that is set in
879 // |modification| is applied to |cookie|.
ApplyResponseCookieModification(ResponseCookie * modification,net::ParsedCookie * cookie)880 static bool ApplyResponseCookieModification(ResponseCookie* modification,
881 net::ParsedCookie* cookie) {
882 bool modified = false;
883 if (modification->name.get())
884 modified |= cookie->SetName(*modification->name);
885 if (modification->value.get())
886 modified |= cookie->SetValue(*modification->value);
887 if (modification->expires.get())
888 modified |= cookie->SetExpires(*modification->expires);
889 if (modification->max_age.get())
890 modified |= cookie->SetMaxAge(base::IntToString(*modification->max_age));
891 if (modification->domain.get())
892 modified |= cookie->SetDomain(*modification->domain);
893 if (modification->path.get())
894 modified |= cookie->SetPath(*modification->path);
895 if (modification->secure.get())
896 modified |= cookie->SetIsSecure(*modification->secure);
897 if (modification->http_only.get())
898 modified |= cookie->SetIsHttpOnly(*modification->http_only);
899 return modified;
900 }
901
DoesResponseCookieMatchFilter(net::ParsedCookie * cookie,FilterResponseCookie * filter)902 static bool DoesResponseCookieMatchFilter(net::ParsedCookie* cookie,
903 FilterResponseCookie* filter) {
904 if (!cookie->IsValid()) return false;
905 if (!filter) return true;
906 if (filter->name.get() && cookie->Name() != *filter->name) return false;
907 if (filter->value.get() && cookie->Value() != *filter->value) return false;
908 if (filter->expires.get()) {
909 std::string actual_value =
910 cookie->HasExpires() ? cookie->Expires() : std::string();
911 if (actual_value != *filter->expires)
912 return false;
913 }
914 if (filter->max_age.get()) {
915 std::string actual_value =
916 cookie->HasMaxAge() ? cookie->MaxAge() : std::string();
917 if (actual_value != base::IntToString(*filter->max_age))
918 return false;
919 }
920 if (filter->domain.get()) {
921 std::string actual_value =
922 cookie->HasDomain() ? cookie->Domain() : std::string();
923 if (actual_value != *filter->domain)
924 return false;
925 }
926 if (filter->path.get()) {
927 std::string actual_value =
928 cookie->HasPath() ? cookie->Path() : std::string();
929 if (actual_value != *filter->path)
930 return false;
931 }
932 if (filter->secure.get() && cookie->IsSecure() != *filter->secure)
933 return false;
934 if (filter->http_only.get() && cookie->IsHttpOnly() != *filter->http_only)
935 return false;
936 int64 seconds_till_expiry;
937 bool lifetime_parsed = false;
938 if (filter->age_upper_bound.get() ||
939 filter->age_lower_bound.get() ||
940 (filter->session_cookie.get() && *filter->session_cookie)) {
941 lifetime_parsed = ParseCookieLifetime(cookie, &seconds_till_expiry);
942 }
943 if (filter->age_upper_bound.get()) {
944 if (seconds_till_expiry > *filter->age_upper_bound)
945 return false;
946 }
947 if (filter->age_lower_bound.get()) {
948 if (seconds_till_expiry < *filter->age_lower_bound)
949 return false;
950 }
951 if (filter->session_cookie.get() &&
952 *filter->session_cookie &&
953 lifetime_parsed) {
954 return false;
955 }
956 return true;
957 }
958
959 // Applies all CookieModificationType::ADD operations for response cookies of
960 // |deltas| to |cookies|. Returns whether any cookie was added.
MergeAddResponseCookieModifications(const EventResponseDeltas & deltas,ParsedResponseCookies * cookies)961 static bool MergeAddResponseCookieModifications(
962 const EventResponseDeltas& deltas,
963 ParsedResponseCookies* cookies) {
964 bool modified = false;
965 // We assume here that the deltas are sorted in decreasing extension
966 // precedence (i.e. decreasing extension installation time).
967 EventResponseDeltas::const_reverse_iterator delta;
968 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
969 const ResponseCookieModifications& modifications =
970 (*delta)->response_cookie_modifications;
971 for (ResponseCookieModifications::const_iterator mod =
972 modifications.begin(); mod != modifications.end(); ++mod) {
973 if ((*mod)->type != ADD || !(*mod)->modification.get())
974 continue;
975 // Cookie names are not unique in response cookies so we always append
976 // and never override.
977 linked_ptr<net::ParsedCookie> cookie(
978 new net::ParsedCookie(std::string()));
979 ApplyResponseCookieModification((*mod)->modification.get(), cookie.get());
980 cookies->push_back(cookie);
981 modified = true;
982 }
983 }
984 return modified;
985 }
986
987 // Applies all CookieModificationType::EDIT operations for response cookies of
988 // |deltas| to |cookies|. Returns whether any cookie was modified.
MergeEditResponseCookieModifications(const EventResponseDeltas & deltas,ParsedResponseCookies * cookies)989 static bool MergeEditResponseCookieModifications(
990 const EventResponseDeltas& deltas,
991 ParsedResponseCookies* cookies) {
992 bool modified = false;
993 // We assume here that the deltas are sorted in decreasing extension
994 // precedence (i.e. decreasing extension installation time).
995 EventResponseDeltas::const_reverse_iterator delta;
996 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
997 const ResponseCookieModifications& modifications =
998 (*delta)->response_cookie_modifications;
999 for (ResponseCookieModifications::const_iterator mod =
1000 modifications.begin(); mod != modifications.end(); ++mod) {
1001 if ((*mod)->type != EDIT || !(*mod)->modification.get())
1002 continue;
1003
1004 for (ParsedResponseCookies::iterator cookie = cookies->begin();
1005 cookie != cookies->end(); ++cookie) {
1006 if (DoesResponseCookieMatchFilter(cookie->get(),
1007 (*mod)->filter.get())) {
1008 modified |= ApplyResponseCookieModification(
1009 (*mod)->modification.get(), cookie->get());
1010 }
1011 }
1012 }
1013 }
1014 return modified;
1015 }
1016
1017 // Applies all CookieModificationType::REMOVE operations for response cookies of
1018 // |deltas| to |cookies|. Returns whether any cookie was deleted.
MergeRemoveResponseCookieModifications(const EventResponseDeltas & deltas,ParsedResponseCookies * cookies)1019 static bool MergeRemoveResponseCookieModifications(
1020 const EventResponseDeltas& deltas,
1021 ParsedResponseCookies* cookies) {
1022 bool modified = false;
1023 // We assume here that the deltas are sorted in decreasing extension
1024 // precedence (i.e. decreasing extension installation time).
1025 EventResponseDeltas::const_reverse_iterator delta;
1026 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
1027 const ResponseCookieModifications& modifications =
1028 (*delta)->response_cookie_modifications;
1029 for (ResponseCookieModifications::const_iterator mod =
1030 modifications.begin(); mod != modifications.end(); ++mod) {
1031 if ((*mod)->type != REMOVE)
1032 continue;
1033
1034 ParsedResponseCookies::iterator i = cookies->begin();
1035 while (i != cookies->end()) {
1036 if (DoesResponseCookieMatchFilter(i->get(),
1037 (*mod)->filter.get())) {
1038 i = cookies->erase(i);
1039 modified = true;
1040 } else {
1041 ++i;
1042 }
1043 }
1044 }
1045 }
1046 return modified;
1047 }
1048
MergeCookiesInOnHeadersReceivedResponses(const EventResponseDeltas & deltas,const net::HttpResponseHeaders * original_response_headers,scoped_refptr<net::HttpResponseHeaders> * override_response_headers,extensions::ExtensionWarningSet * conflicting_extensions,const net::BoundNetLog * net_log)1049 void MergeCookiesInOnHeadersReceivedResponses(
1050 const EventResponseDeltas& deltas,
1051 const net::HttpResponseHeaders* original_response_headers,
1052 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
1053 extensions::ExtensionWarningSet* conflicting_extensions,
1054 const net::BoundNetLog* net_log) {
1055 // Skip all work if there are no registered cookie modifications.
1056 bool cookie_modifications_exist = false;
1057 EventResponseDeltas::const_reverse_iterator delta;
1058 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
1059 cookie_modifications_exist |=
1060 !(*delta)->response_cookie_modifications.empty();
1061 }
1062 if (!cookie_modifications_exist)
1063 return;
1064
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());
1069 }
1070
1071 ParsedResponseCookies cookies =
1072 GetResponseCookies(*override_response_headers);
1073
1074 bool modified = false;
1075 modified |= MergeAddResponseCookieModifications(deltas, &cookies);
1076 modified |= MergeEditResponseCookieModifications(deltas, &cookies);
1077 modified |= MergeRemoveResponseCookieModifications(deltas, &cookies);
1078
1079 // Store new value.
1080 if (modified)
1081 StoreResponseCookies(cookies, *override_response_headers);
1082 }
1083
1084 // Converts the key of the (key, value) pair to lower case.
ToLowerCase(const ResponseHeader & header)1085 static ResponseHeader ToLowerCase(const ResponseHeader& header) {
1086 std::string lower_key(header.first);
1087 StringToLowerASCII(&lower_key);
1088 return ResponseHeader(lower_key, header.second);
1089 }
1090
1091 // Returns the extension ID of the first extension in |deltas| that removes the
1092 // request header identified by |key|.
FindRemoveResponseHeader(const EventResponseDeltas & deltas,const std::string & key)1093 static std::string FindRemoveResponseHeader(
1094 const EventResponseDeltas& deltas,
1095 const std::string& key) {
1096 std::string lower_key = StringToLowerASCII(key);
1097 EventResponseDeltas::const_iterator delta;
1098 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1099 ResponseHeaders::const_iterator i;
1100 for (i = (*delta)->deleted_response_headers.begin();
1101 i != (*delta)->deleted_response_headers.end(); ++i) {
1102 if (StringToLowerASCII(i->first) == lower_key)
1103 return (*delta)->extension_id;
1104 }
1105 }
1106 return std::string();
1107 }
1108
MergeOnHeadersReceivedResponses(const EventResponseDeltas & deltas,const net::HttpResponseHeaders * original_response_headers,scoped_refptr<net::HttpResponseHeaders> * override_response_headers,GURL * allowed_unsafe_redirect_url,extensions::ExtensionWarningSet * conflicting_extensions,const net::BoundNetLog * net_log)1109 void MergeOnHeadersReceivedResponses(
1110 const EventResponseDeltas& deltas,
1111 const net::HttpResponseHeaders* original_response_headers,
1112 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
1113 GURL* allowed_unsafe_redirect_url,
1114 extensions::ExtensionWarningSet* conflicting_extensions,
1115 const net::BoundNetLog* net_log) {
1116 EventResponseDeltas::const_iterator delta;
1117
1118 // Here we collect which headers we have removed or added so far due to
1119 // extensions of higher precedence. Header keys are always stored as
1120 // lower case.
1121 std::set<ResponseHeader> removed_headers;
1122 std::set<ResponseHeader> added_headers;
1123
1124 // We assume here that the deltas are sorted in decreasing extension
1125 // precedence (i.e. decreasing extension installation time).
1126 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1127 if ((*delta)->added_response_headers.empty() &&
1128 (*delta)->deleted_response_headers.empty()) {
1129 continue;
1130 }
1131
1132 // Only create a copy if we really want to modify the response headers.
1133 if (override_response_headers->get() == NULL) {
1134 *override_response_headers = new net::HttpResponseHeaders(
1135 original_response_headers->raw_headers());
1136 }
1137
1138 // We consider modifications as pairs of (delete, add) operations.
1139 // If a header is deleted twice by different extensions we assume that the
1140 // intention was to modify it to different values and consider this a
1141 // conflict. As deltas is sorted by decreasing extension installation order,
1142 // this takes care of precedence.
1143 bool extension_conflicts = false;
1144 std::string conflicting_header;
1145 std::string winning_extension_id;
1146 ResponseHeaders::const_iterator i;
1147 for (i = (*delta)->deleted_response_headers.begin();
1148 i != (*delta)->deleted_response_headers.end(); ++i) {
1149 if (removed_headers.find(ToLowerCase(*i)) != removed_headers.end()) {
1150 winning_extension_id = FindRemoveResponseHeader(deltas, i->first);
1151 conflicting_header = i->first;
1152 extension_conflicts = true;
1153 break;
1154 }
1155 }
1156
1157 // Now execute the modifications if there were no conflicts.
1158 if (!extension_conflicts) {
1159 // Delete headers
1160 {
1161 for (i = (*delta)->deleted_response_headers.begin();
1162 i != (*delta)->deleted_response_headers.end(); ++i) {
1163 (*override_response_headers)->RemoveHeaderLine(i->first, i->second);
1164 removed_headers.insert(ToLowerCase(*i));
1165 }
1166 }
1167
1168 // Add headers.
1169 {
1170 for (i = (*delta)->added_response_headers.begin();
1171 i != (*delta)->added_response_headers.end(); ++i) {
1172 ResponseHeader lowercase_header(ToLowerCase(*i));
1173 if (added_headers.find(lowercase_header) != added_headers.end())
1174 continue;
1175 added_headers.insert(lowercase_header);
1176 (*override_response_headers)->AddHeader(i->first + ": " + i->second);
1177 }
1178 }
1179 net_log->AddEvent(
1180 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
1181 CreateNetLogExtensionIdCallback(delta->get()));
1182 } else {
1183 conflicting_extensions->insert(
1184 ExtensionWarning::CreateResponseHeaderConflictWarning(
1185 (*delta)->extension_id, winning_extension_id,
1186 conflicting_header));
1187 net_log->AddEvent(
1188 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1189 CreateNetLogExtensionIdCallback(delta->get()));
1190 }
1191 }
1192
1193 MergeCookiesInOnHeadersReceivedResponses(deltas, original_response_headers,
1194 override_response_headers, conflicting_extensions, net_log);
1195
1196 GURL new_url;
1197 MergeRedirectUrlOfResponses(
1198 deltas, &new_url, conflicting_extensions, net_log);
1199 if (new_url.is_valid()) {
1200 // Only create a copy if we really want to modify the response headers.
1201 if (override_response_headers->get() == NULL) {
1202 *override_response_headers = new net::HttpResponseHeaders(
1203 original_response_headers->raw_headers());
1204 }
1205 (*override_response_headers)->ReplaceStatusLine("HTTP/1.1 302 Found");
1206 (*override_response_headers)->RemoveHeader("location");
1207 (*override_response_headers)->AddHeader("Location: " + new_url.spec());
1208 // Explicitly mark the URL as safe for redirection, to prevent the request
1209 // from being blocked because of net::ERR_UNSAFE_REDIRECT.
1210 *allowed_unsafe_redirect_url = new_url;
1211 }
1212 }
1213
MergeOnAuthRequiredResponses(const EventResponseDeltas & deltas,net::AuthCredentials * auth_credentials,extensions::ExtensionWarningSet * conflicting_extensions,const net::BoundNetLog * net_log)1214 bool MergeOnAuthRequiredResponses(
1215 const EventResponseDeltas& deltas,
1216 net::AuthCredentials* auth_credentials,
1217 extensions::ExtensionWarningSet* conflicting_extensions,
1218 const net::BoundNetLog* net_log) {
1219 CHECK(auth_credentials);
1220 bool credentials_set = false;
1221 std::string winning_extension_id;
1222
1223 for (EventResponseDeltas::const_iterator delta = deltas.begin();
1224 delta != deltas.end();
1225 ++delta) {
1226 if (!(*delta)->auth_credentials.get())
1227 continue;
1228 bool different =
1229 auth_credentials->username() !=
1230 (*delta)->auth_credentials->username() ||
1231 auth_credentials->password() != (*delta)->auth_credentials->password();
1232 if (credentials_set && different) {
1233 conflicting_extensions->insert(
1234 ExtensionWarning::CreateCredentialsConflictWarning(
1235 (*delta)->extension_id, winning_extension_id));
1236 net_log->AddEvent(
1237 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1238 CreateNetLogExtensionIdCallback(delta->get()));
1239 } else {
1240 net_log->AddEvent(
1241 net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS,
1242 CreateNetLogExtensionIdCallback(delta->get()));
1243 *auth_credentials = *(*delta)->auth_credentials;
1244 credentials_set = true;
1245 winning_extension_id = (*delta)->extension_id;
1246 }
1247 }
1248 return credentials_set;
1249 }
1250
1251
1252 #define ARRAYEND(array) (array + arraysize(array))
1253
IsRelevantResourceType(ResourceType::Type type)1254 bool IsRelevantResourceType(ResourceType::Type type) {
1255 ResourceType::Type* iter =
1256 std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
1257 return iter != ARRAYEND(kResourceTypeValues);
1258 }
1259
ResourceTypeToString(ResourceType::Type type)1260 const char* ResourceTypeToString(ResourceType::Type type) {
1261 ResourceType::Type* iter =
1262 std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
1263 if (iter == ARRAYEND(kResourceTypeValues))
1264 return "other";
1265
1266 return kResourceTypeStrings[iter - kResourceTypeValues];
1267 }
1268
ParseResourceType(const std::string & type_str,ResourceType::Type * type)1269 bool ParseResourceType(const std::string& type_str,
1270 ResourceType::Type* type) {
1271 const char** iter =
1272 std::find(kResourceTypeStrings, ARRAYEND(kResourceTypeStrings), type_str);
1273 if (iter == ARRAYEND(kResourceTypeStrings))
1274 return false;
1275 *type = kResourceTypeValues[iter - kResourceTypeStrings];
1276 return true;
1277 }
1278
ClearCacheOnNavigation()1279 void ClearCacheOnNavigation() {
1280 if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
1281 ClearCacheOnNavigationOnUI();
1282 } else {
1283 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
1284 base::Bind(&ClearCacheOnNavigationOnUI));
1285 }
1286 }
1287
NotifyWebRequestAPIUsed(void * profile_id,scoped_refptr<const extensions::Extension> extension)1288 void NotifyWebRequestAPIUsed(
1289 void* profile_id,
1290 scoped_refptr<const extensions::Extension> extension) {
1291 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1292 Profile* profile = reinterpret_cast<Profile*>(profile_id);
1293 if (!g_browser_process->profile_manager()->IsValidProfile(profile))
1294 return;
1295
1296 extensions::RuntimeData* runtime_data =
1297 extensions::ExtensionSystem::Get(profile)->runtime_data();
1298 if (runtime_data->HasUsedWebRequest(extension.get()))
1299 return;
1300 runtime_data->SetHasUsedWebRequest(extension.get(), true);
1301
1302 content::BrowserContext* browser_context = profile;
1303 for (content::RenderProcessHost::iterator it =
1304 content::RenderProcessHost::AllHostsIterator();
1305 !it.IsAtEnd(); it.Advance()) {
1306 content::RenderProcessHost* host = it.GetCurrentValue();
1307 if (host->GetBrowserContext() == browser_context)
1308 SendExtensionWebRequestStatusToHost(host);
1309 }
1310 }
1311
IsValidHeaderName(const std::string & name)1312 bool IsValidHeaderName(const std::string& name) {
1313 // Check whether the header name is RFC 2616-compliant.
1314 return net::HttpUtil::IsToken(name);
1315 }
1316
IsValidHeaderValue(const std::string & value)1317 bool IsValidHeaderValue(const std::string& value) {
1318 // Just a sanity check: disallow NUL and CRLF.
1319 return value.find('\0') == std::string::npos &&
1320 value.find("\r\n") == std::string::npos;
1321 }
1322
1323 } // namespace extension_web_request_api_helpers
1324