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