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