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