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