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
33using base::Time;
34using extensions::ExtensionWarning;
35
36namespace extension_web_request_api_helpers {
37
38namespace {
39
40// A ParsedRequestCookie consists of the key and value of the cookie.
41typedef std::pair<base::StringPiece, base::StringPiece> ParsedRequestCookie;
42typedef std::vector<ParsedRequestCookie> ParsedRequestCookies;
43typedef std::vector<linked_ptr<net::ParsedCookie> > ParsedResponseCookies;
44
45static 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
57static 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
74COMPILE_ASSERT(
75    arraysize(kResourceTypeStrings) == arraysize(kResourceTypeValues),
76    keep_resource_types_in_sync);
77
78void ClearCacheOnNavigationOnUI() {
79  WebCacheManager::GetInstance()->ClearCacheOnNavigation();
80}
81
82bool 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
104bool NullableEquals(const int* a, const int* b) {
105  if ((a && !b) || (!a && b))
106    return false;
107  return (!a) || (*a == *b);
108}
109
110bool NullableEquals(const bool* a, const bool* b) {
111  if ((a && !b) || (!a && b))
112    return false;
113  return (!a) || (*a == *b);
114}
115
116bool 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
124RequestCookie::RequestCookie() {}
125RequestCookie::~RequestCookie() {}
126
127bool 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
136ResponseCookie::ResponseCookie() {}
137ResponseCookie::~ResponseCookie() {}
138
139bool 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
154FilterResponseCookie::FilterResponseCookie() {}
155FilterResponseCookie::~FilterResponseCookie() {}
156
157bool 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
168RequestCookieModification::RequestCookieModification() {}
169RequestCookieModification::~RequestCookieModification() {}
170
171bool 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
181ResponseCookieModification::ResponseCookieModification() : type(ADD) {}
182ResponseCookieModification::~ResponseCookieModification() {}
183
184bool 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
195EventResponseDelta::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
202EventResponseDelta::~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.
209net::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.
216Value* 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
242bool 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
248base::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
258bool 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
273EventResponseDelta* 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
285EventResponseDelta* 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
322EventResponseDelta* 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
377EventResponseDelta* 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
389void 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.
412static 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
454void 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.
479static 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.
521static 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
535static 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.
546static 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.
588static 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.
623static 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
653void 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|.
690static 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|.
708static 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
724void 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|.
842static 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.
857static 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|.
869static 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
891static 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.
950static 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.
978static 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.
1008static 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
1038void 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.
1074static 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|.
1082static 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
1098void 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
1185bool 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
1225bool IsRelevantResourceType(ResourceType::Type type) {
1226  ResourceType::Type* iter =
1227      std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
1228  return iter != ARRAYEND(kResourceTypeValues);
1229}
1230
1231const 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
1240bool 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
1250void 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
1259void 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