Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / web_request / web_request_api_helpers.cc
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 "chrome/common/url_constants.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/render_process_host.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/browser/runtime_data.h"
25 #include "net/base/net_log.h"
26 #include "net/cookies/cookie_util.h"
27 #include "net/cookies/parsed_cookie.h"
28 #include "net/http/http_util.h"
29 #include "net/url_request/url_request.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
79 void ClearCacheOnNavigationOnUI() {
80   WebCacheManager::GetInstance()->ClearCacheOnNavigation();
81 }
82
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
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
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
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
125 RequestCookie::RequestCookie() {}
126 RequestCookie::~RequestCookie() {}
127
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
137 ResponseCookie::ResponseCookie() {}
138 ResponseCookie::~ResponseCookie() {}
139
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
155 FilterResponseCookie::FilterResponseCookie() {}
156 FilterResponseCookie::~FilterResponseCookie() {}
157
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
169 RequestCookieModification::RequestCookieModification() {}
170 RequestCookieModification::~RequestCookieModification() {}
171
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
182 ResponseCookieModification::ResponseCookieModification() : type(ADD) {}
183 ResponseCookieModification::~ResponseCookieModification() {}
184
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
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
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.
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.
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
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
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
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
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
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
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
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
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.
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(content::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
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
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.
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.
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
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.
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.
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.
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
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|.
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|.
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
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, &current_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, &current_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|.
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.
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|.
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
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.
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.
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.
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
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.
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|.
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
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
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
1254 bool IsRelevantResourceType(ResourceType::Type type) {
1255   ResourceType::Type* iter =
1256       std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
1257   return iter != ARRAYEND(kResourceTypeValues);
1258 }
1259
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
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
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
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
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
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