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