1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
6
7#include <cmath>
8
9#include "base/bind.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_util.h"
12#include "base/strings/stringprintf.h"
13#include "base/time/time.h"
14#include "base/values.h"
15#include "chrome/browser/browser_process.h"
16#include "chrome/browser/extensions/api/web_request/web_request_api.h"
17#include "chrome/browser/extensions/extension_warning_set.h"
18#include "chrome/browser/profiles/profile_manager.h"
19#include "chrome/browser/renderer_host/web_cache_manager.h"
20#include "content/public/browser/browser_thread.h"
21#include "content/public/browser/render_process_host.h"
22#include "extensions/browser/extension_system.h"
23#include "extensions/browser/runtime_data.h"
24#include "net/base/net_log.h"
25#include "net/cookies/cookie_util.h"
26#include "net/cookies/parsed_cookie.h"
27#include "net/http/http_util.h"
28#include "net/url_request/url_request.h"
29#include "url/url_constants.h"
30
31// TODO(battre): move all static functions into an anonymous namespace at the
32// top of this file.
33
34using base::Time;
35using extensions::ExtensionWarning;
36
37namespace extension_web_request_api_helpers {
38
39namespace {
40
41// A ParsedRequestCookie consists of the key and value of the cookie.
42typedef std::pair<base::StringPiece, base::StringPiece> ParsedRequestCookie;
43typedef std::vector<ParsedRequestCookie> ParsedRequestCookies;
44typedef std::vector<linked_ptr<net::ParsedCookie> > ParsedResponseCookies;
45
46static 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
58static 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
75COMPILE_ASSERT(
76    arraysize(kResourceTypeStrings) == arraysize(kResourceTypeValues),
77    keep_resource_types_in_sync);
78
79void ClearCacheOnNavigationOnUI() {
80  WebCacheManager::GetInstance()->ClearCacheOnNavigation();
81}
82
83bool 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
105bool NullableEquals(const int* a, const int* b) {
106  if ((a && !b) || (!a && b))
107    return false;
108  return (!a) || (*a == *b);
109}
110
111bool NullableEquals(const bool* a, const bool* b) {
112  if ((a && !b) || (!a && b))
113    return false;
114  return (!a) || (*a == *b);
115}
116
117bool 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
125RequestCookie::RequestCookie() {}
126RequestCookie::~RequestCookie() {}
127
128bool 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
137ResponseCookie::ResponseCookie() {}
138ResponseCookie::~ResponseCookie() {}
139
140bool 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
155FilterResponseCookie::FilterResponseCookie() {}
156FilterResponseCookie::~FilterResponseCookie() {}
157
158bool 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
169RequestCookieModification::RequestCookieModification() {}
170RequestCookieModification::~RequestCookieModification() {}
171
172bool 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
182ResponseCookieModification::ResponseCookieModification() : type(ADD) {}
183ResponseCookieModification::~ResponseCookieModification() {}
184
185bool 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
196EventResponseDelta::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
203EventResponseDelta::~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.
210net::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.
217base::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
243bool 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
249base::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
259bool 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
274EventResponseDelta* 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
286EventResponseDelta* 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
323EventResponseDelta* 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
380EventResponseDelta* 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
392void 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.
415static bool MergeRedirectUrlOfResponsesHelper(
416    const EventResponseDeltas& deltas,
417    GURL* new_url,
418    extensions::ExtensionWarningSet* conflicting_extensions,
419    const net::BoundNetLog* net_log,
420    bool consider_only_cancel_scheme_urls) {
421  bool redirected = false;
422
423  // Extension that determines the |new_url|.
424  std::string winning_extension_id;
425  EventResponseDeltas::const_iterator delta;
426  for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
427    if ((*delta)->new_url.is_empty())
428      continue;
429    if (consider_only_cancel_scheme_urls &&
430        !(*delta)->new_url.SchemeIs(url::kDataScheme) &&
431        (*delta)->new_url.spec() != "about:blank") {
432      continue;
433    }
434
435    if (!redirected || *new_url == (*delta)->new_url) {
436      *new_url = (*delta)->new_url;
437      winning_extension_id = (*delta)->extension_id;
438      redirected = true;
439      net_log->AddEvent(
440          net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST,
441          CreateNetLogExtensionIdCallback(delta->get()));
442    } else {
443      conflicting_extensions->insert(
444          ExtensionWarning::CreateRedirectConflictWarning(
445              (*delta)->extension_id,
446              winning_extension_id,
447              (*delta)->new_url,
448              *new_url));
449      net_log->AddEvent(
450          net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
451          CreateNetLogExtensionIdCallback(delta->get()));
452    }
453  }
454  return redirected;
455}
456
457void 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
477void 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.
490static 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.
532static 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
546static 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.
557static 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.
599static 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.
634static 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
664void 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|.
701static 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|.
719static 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
735void 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|.
853static 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.
868static 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|.
880static 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
902static 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.
961static 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.
989static 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.
1019static 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
1049void 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.
1085static 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|.
1093static 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
1109void 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
1214bool 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
1254bool IsRelevantResourceType(ResourceType::Type type) {
1255  ResourceType::Type* iter =
1256      std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
1257  return iter != ARRAYEND(kResourceTypeValues);
1258}
1259
1260const 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
1269bool 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
1279void 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
1288void 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
1312bool IsValidHeaderName(const std::string& name) {
1313  // Check whether the header name is RFC 2616-compliant.
1314  return net::HttpUtil::IsToken(name);
1315}
1316
1317bool IsValidHeaderValue(const std::string& value) {
1318  // Just a sanity check: disallow NUL and CRLF.
1319  return value.find('\0') == std::string::npos &&
1320      value.find("\r\n") == std::string::npos;
1321}
1322
1323}  // namespace extension_web_request_api_helpers
1324