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