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