1/* 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) 3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) 4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) 5 Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) 6 Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Library General Public 10 License as published by the Free Software Foundation; either 11 version 2 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Library General Public License for more details. 17 18 You should have received a copy of the GNU Library General Public License 19 along with this library; see the file COPYING.LIB. If not, write to 20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 Boston, MA 02110-1301, USA. 22*/ 23 24#include "config.h" 25#include "core/fetch/Resource.h" 26 27#include "core/FetchInitiatorTypeNames.h" 28#include "core/fetch/CachedMetadata.h" 29#include "core/fetch/CrossOriginAccessControl.h" 30#include "core/fetch/MemoryCache.h" 31#include "core/fetch/ResourceClient.h" 32#include "core/fetch/ResourceClientWalker.h" 33#include "core/fetch/ResourceFetcher.h" 34#include "core/fetch/ResourceLoader.h" 35#include "core/fetch/ResourcePtr.h" 36#include "core/inspector/InspectorInstrumentation.h" 37#include "platform/Logging.h" 38#include "platform/SharedBuffer.h" 39#include "platform/TraceEvent.h" 40#include "platform/weborigin/KURL.h" 41#include "public/platform/Platform.h" 42#include "wtf/CurrentTime.h" 43#include "wtf/MathExtras.h" 44#include "wtf/RefCountedLeakCounter.h" 45#include "wtf/StdLibExtras.h" 46#include "wtf/Vector.h" 47#include "wtf/text/CString.h" 48 49using namespace WTF; 50 51namespace blink { 52 53// These response headers are not copied from a revalidated response to the 54// cached response headers. For compatibility, this list is based on Chromium's 55// net/http/http_response_headers.cc. 56const char* const headersToIgnoreAfterRevalidation[] = { 57 "allow", 58 "connection", 59 "etag", 60 "expires", 61 "keep-alive", 62 "last-modified" 63 "proxy-authenticate", 64 "proxy-connection", 65 "trailer", 66 "transfer-encoding", 67 "upgrade", 68 "www-authenticate", 69 "x-frame-options", 70 "x-xss-protection", 71}; 72 73// Some header prefixes mean "Don't copy this header from a 304 response.". 74// Rather than listing all the relevant headers, we can consolidate them into 75// this list, also grabbed from Chromium's net/http/http_response_headers.cc. 76const char* const headerPrefixesToIgnoreAfterRevalidation[] = { 77 "content-", 78 "x-content-", 79 "x-webkit-" 80}; 81 82static inline bool shouldUpdateHeaderAfterRevalidation(const AtomicString& header) 83{ 84 for (size_t i = 0; i < WTF_ARRAY_LENGTH(headersToIgnoreAfterRevalidation); i++) { 85 if (equalIgnoringCase(header, headersToIgnoreAfterRevalidation[i])) 86 return false; 87 } 88 for (size_t i = 0; i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) { 89 if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i], false)) 90 return false; 91 } 92 return true; 93} 94 95DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("Resource")); 96unsigned Resource::s_instanceCount = 0; 97 98Resource::Resource(const ResourceRequest& request, Type type) 99 : m_resourceRequest(request) 100 , m_responseTimestamp(currentTime()) 101 , m_cancelTimer(this, &Resource::cancelTimerFired) 102 , m_loadFinishTime(0) 103 , m_identifier(0) 104 , m_encodedSize(0) 105 , m_decodedSize(0) 106 , m_handleCount(0) 107 , m_preloadCount(0) 108 , m_protectorCount(0) 109 , m_preloadResult(PreloadNotReferenced) 110 , m_requestedFromNetworkingLayer(false) 111 , m_loading(false) 112 , m_switchingClientsToRevalidatedResource(false) 113 , m_type(type) 114 , m_status(Pending) 115 , m_wasPurged(false) 116 , m_needsSynchronousCacheHit(false) 117#ifdef ENABLE_RESOURCE_IS_DELETED_CHECK 118 , m_deleted(false) 119#endif 120 , m_resourceToRevalidate(nullptr) 121 , m_proxyResource(nullptr) 122{ 123 ASSERT(m_type == unsigned(type)); // m_type is a bitfield, so this tests careless updates of the enum. 124 ++s_instanceCount; 125#ifndef NDEBUG 126 cachedResourceLeakCounter.increment(); 127#endif 128 memoryCache()->registerLiveResource(*this); 129 130 if (!m_resourceRequest.url().hasFragmentIdentifier()) 131 return; 132 KURL urlForCache = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url()); 133 if (urlForCache.hasFragmentIdentifier()) 134 return; 135 m_fragmentIdentifierForRequest = m_resourceRequest.url().fragmentIdentifier(); 136 m_resourceRequest.setURL(urlForCache); 137} 138 139Resource::~Resource() 140{ 141 ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this. 142 ASSERT(canDelete()); 143 RELEASE_ASSERT(!memoryCache()->contains(this)); 144 RELEASE_ASSERT(!ResourceCallback::callbackHandler()->isScheduled(this)); 145 ASSERT(url().isNull() || memoryCache()->resourceForURL(KURL(ParsedURLString, url())) != this); 146 assertAlive(); 147 148#ifdef ENABLE_RESOURCE_IS_DELETED_CHECK 149 m_deleted = true; 150#endif 151#ifndef NDEBUG 152 cachedResourceLeakCounter.decrement(); 153#endif 154 --s_instanceCount; 155} 156 157void Resource::dispose() 158{ 159} 160 161void Resource::trace(Visitor* visitor) 162{ 163 visitor->trace(m_loader); 164 visitor->trace(m_resourceToRevalidate); 165 visitor->trace(m_proxyResource); 166} 167 168void Resource::failBeforeStarting() 169{ 170 WTF_LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data()); 171 error(Resource::LoadError); 172} 173 174void Resource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options) 175{ 176 if (!fetcher->frame()) { 177 failBeforeStarting(); 178 return; 179 } 180 181 m_options = options; 182 m_loading = true; 183 184 if (!accept().isEmpty()) 185 m_resourceRequest.setHTTPAccept(accept()); 186 187 // FIXME: It's unfortunate that the cache layer and below get to know anything about fragment identifiers. 188 // We should look into removing the expectation of that knowledge from the platform network stacks. 189 ResourceRequest request(m_resourceRequest); 190 if (!m_fragmentIdentifierForRequest.isNull()) { 191 KURL url = request.url(); 192 url.setFragmentIdentifier(m_fragmentIdentifierForRequest); 193 request.setURL(url); 194 m_fragmentIdentifierForRequest = String(); 195 } 196 m_status = Pending; 197 if (m_loader) { 198 RELEASE_ASSERT(m_options.synchronousPolicy == RequestSynchronously); 199 m_loader->changeToSynchronous(); 200 return; 201 } 202 m_loader = ResourceLoader::create(fetcher, this, request, options); 203 m_loader->start(); 204} 205 206void Resource::checkNotify() 207{ 208 if (isLoading()) 209 return; 210 211 ResourceClientWalker<ResourceClient> w(m_clients); 212 while (ResourceClient* c = w.next()) 213 c->notifyFinished(this); 214} 215 216void Resource::appendData(const char* data, int length) 217{ 218 TRACE_EVENT0("blink", "Resource::appendData"); 219 ASSERT(!m_resourceToRevalidate); 220 ASSERT(!errorOccurred()); 221 if (m_options.dataBufferingPolicy == DoNotBufferData) 222 return; 223 if (m_data) 224 m_data->append(data, length); 225 else 226 m_data = SharedBuffer::createPurgeable(data, length); 227 setEncodedSize(m_data->size()); 228} 229 230void Resource::setResourceBuffer(PassRefPtr<SharedBuffer> resourceBuffer) 231{ 232 ASSERT(!m_resourceToRevalidate); 233 ASSERT(!errorOccurred()); 234 ASSERT(m_options.dataBufferingPolicy == BufferData); 235 m_data = resourceBuffer; 236 setEncodedSize(m_data->size()); 237} 238 239void Resource::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy) 240{ 241 m_options.dataBufferingPolicy = dataBufferingPolicy; 242 m_data.clear(); 243 setEncodedSize(0); 244} 245 246void Resource::error(Resource::Status status) 247{ 248 if (m_resourceToRevalidate) 249 revalidationFailed(); 250 251 if (!m_error.isNull() && (m_error.isCancellation() || !isPreloaded())) 252 memoryCache()->remove(this); 253 254 setStatus(status); 255 ASSERT(errorOccurred()); 256 m_data.clear(); 257 258 setLoading(false); 259 checkNotify(); 260} 261 262void Resource::finishOnePart() 263{ 264 setLoading(false); 265 checkNotify(); 266} 267 268void Resource::finish(double finishTime) 269{ 270 ASSERT(!m_resourceToRevalidate); 271 ASSERT(!errorOccurred()); 272 m_loadFinishTime = finishTime; 273 finishOnePart(); 274 if (!errorOccurred()) 275 m_status = Cached; 276} 277 278bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin) 279{ 280 String ignoredErrorDescription; 281 return passesAccessControlCheck(securityOrigin, ignoredErrorDescription); 282} 283 284bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin, String& errorDescription) 285{ 286 return blink::passesAccessControlCheck(m_response, resourceRequest().allowStoredCredentials() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription); 287} 288 289static double currentAge(const ResourceResponse& response, double responseTimestamp) 290{ 291 // RFC2616 13.2.3 292 // No compensation for latency as that is not terribly important in practice 293 double dateValue = response.date(); 294 double apparentAge = std::isfinite(dateValue) ? std::max(0., responseTimestamp - dateValue) : 0; 295 double ageValue = response.age(); 296 double correctedReceivedAge = std::isfinite(ageValue) ? std::max(apparentAge, ageValue) : apparentAge; 297 double residentTime = currentTime() - responseTimestamp; 298 return correctedReceivedAge + residentTime; 299} 300 301static double freshnessLifetime(ResourceResponse& response, double responseTimestamp) 302{ 303#if !OS(ANDROID) 304 // On desktop, local files should be reloaded in case they change. 305 if (response.url().isLocalFile()) 306 return 0; 307#endif 308 309 // Cache other non-http / non-filesystem resources liberally. 310 if (!response.url().protocolIsInHTTPFamily() 311 && !response.url().protocolIs("filesystem")) 312 return std::numeric_limits<double>::max(); 313 314 // RFC2616 13.2.4 315 double maxAgeValue = response.cacheControlMaxAge(); 316 if (std::isfinite(maxAgeValue)) 317 return maxAgeValue; 318 double expiresValue = response.expires(); 319 double dateValue = response.date(); 320 double creationTime = std::isfinite(dateValue) ? dateValue : responseTimestamp; 321 if (std::isfinite(expiresValue)) 322 return expiresValue - creationTime; 323 double lastModifiedValue = response.lastModified(); 324 if (std::isfinite(lastModifiedValue)) 325 return (creationTime - lastModifiedValue) * 0.1; 326 // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0. 327 return 0; 328} 329 330static bool canUseResponse(ResourceResponse& response, double responseTimestamp) 331{ 332 if (response.isNull()) 333 return false; 334 335 // FIXME: Why isn't must-revalidate considered a reason we can't use the response? 336 if (response.cacheControlContainsNoCache() || response.cacheControlContainsNoStore()) 337 return false; 338 339 if (response.httpStatusCode() == 303) { 340 // Must not be cached. 341 return false; 342 } 343 344 if (response.httpStatusCode() == 302 || response.httpStatusCode() == 307) { 345 // Default to not cacheable unless explicitly allowed. 346 bool hasMaxAge = std::isfinite(response.cacheControlMaxAge()); 347 bool hasExpires = std::isfinite(response.expires()); 348 // TODO: consider catching Cache-Control "private" and "public" here. 349 if (!hasMaxAge && !hasExpires) 350 return false; 351 } 352 353 return currentAge(response, responseTimestamp) <= freshnessLifetime(response, responseTimestamp); 354} 355 356const ResourceRequest& Resource::lastResourceRequest() const 357{ 358 if (!m_redirectChain.size()) 359 return m_resourceRequest; 360 return m_redirectChain.last().m_request; 361} 362 363void Resource::willSendRequest(ResourceRequest& request, const ResourceResponse& response) 364{ 365 m_redirectChain.append(RedirectPair(request, response)); 366 m_requestedFromNetworkingLayer = true; 367} 368 369bool Resource::unlock() 370{ 371 if (!m_data) 372 return false; 373 374 if (!m_data->isLocked()) 375 return true; 376 377 if (!memoryCache()->contains(this) || hasClients() || m_handleCount > 1 || m_proxyResource || m_resourceToRevalidate || !m_loadFinishTime || !isSafeToUnlock()) 378 return false; 379 380 m_data->unlock(); 381 return true; 382} 383 384bool Resource::hasRightHandleCountApartFromCache(unsigned targetCount) const 385{ 386 return m_handleCount == targetCount + (memoryCache()->contains(this) ? 1 : 0); 387} 388 389void Resource::responseReceived(const ResourceResponse& response) 390{ 391 setResponse(response); 392 m_responseTimestamp = currentTime(); 393 String encoding = response.textEncodingName(); 394 if (!encoding.isNull()) 395 setEncoding(encoding); 396 397 if (!m_resourceToRevalidate) 398 return; 399 if (response.httpStatusCode() == 304) 400 revalidationSucceeded(response); 401 else 402 revalidationFailed(); 403} 404 405void Resource::setSerializedCachedMetadata(const char* data, size_t size) 406{ 407 // We only expect to receive cached metadata from the platform once. 408 // If this triggers, it indicates an efficiency problem which is most 409 // likely unexpected in code designed to improve performance. 410 ASSERT(!m_cachedMetadata); 411 ASSERT(!m_resourceToRevalidate); 412 413 m_cachedMetadata = CachedMetadata::deserialize(data, size); 414} 415 416void Resource::setCachedMetadata(unsigned dataTypeID, const char* data, size_t size, MetadataCacheType cacheType) 417{ 418 // Currently, only one type of cached metadata per resource is supported. 419 // If the need arises for multiple types of metadata per resource this could 420 // be enhanced to store types of metadata in a map. 421 ASSERT(!m_cachedMetadata); 422 423 m_cachedMetadata = CachedMetadata::create(dataTypeID, data, size); 424 425 if (cacheType == SendToPlatform) { 426 const Vector<char>& serializedData = m_cachedMetadata->serialize(); 427 blink::Platform::current()->cacheMetadata(m_response.url(), m_response.responseTime(), serializedData.data(), serializedData.size()); 428 } 429} 430 431void Resource::clearCachedMetadata() 432{ 433 m_cachedMetadata.clear(); 434} 435 436bool Resource::canDelete() const 437{ 438 return !hasClients() && !m_loader && !m_preloadCount && hasRightHandleCountApartFromCache(0) 439 && !m_protectorCount && !m_resourceToRevalidate && !m_proxyResource; 440} 441 442bool Resource::hasOneHandle() const 443{ 444 return hasRightHandleCountApartFromCache(1); 445} 446 447CachedMetadata* Resource::cachedMetadata(unsigned dataTypeID) const 448{ 449 if (!m_cachedMetadata || m_cachedMetadata->dataTypeID() != dataTypeID) 450 return 0; 451 return m_cachedMetadata.get(); 452} 453 454void Resource::clearLoader() 455{ 456 m_loader = nullptr; 457} 458 459void Resource::addClient(ResourceClient* client) 460{ 461 if (addClientToSet(client)) 462 didAddClient(client); 463} 464 465void Resource::didAddClient(ResourceClient* c) 466{ 467 if (!isLoading() && !stillNeedsLoad()) 468 c->notifyFinished(this); 469} 470 471static bool shouldSendCachedDataSynchronouslyForType(Resource::Type type) 472{ 473 // Some resources types default to return data synchronously. 474 // For most of these, it's because there are layout tests that 475 // expect data to return synchronously in case of cache hit. In 476 // the case of fonts, there was a performance regression. 477 // FIXME: Get to the point where we don't need to special-case sync/async 478 // behavior for different resource types. 479 if (type == Resource::Image) 480 return true; 481 if (type == Resource::CSSStyleSheet) 482 return true; 483 if (type == Resource::Script) 484 return true; 485 if (type == Resource::Font) 486 return true; 487 return false; 488} 489 490bool Resource::addClientToSet(ResourceClient* client) 491{ 492 ASSERT(!isPurgeable()); 493 494 if (m_preloadResult == PreloadNotReferenced) { 495 if (isLoaded()) 496 m_preloadResult = PreloadReferencedWhileComplete; 497 else if (m_requestedFromNetworkingLayer) 498 m_preloadResult = PreloadReferencedWhileLoading; 499 else 500 m_preloadResult = PreloadReferenced; 501 } 502 if (!hasClients()) 503 memoryCache()->makeLive(this); 504 505 // If we have existing data to send to the new client and the resource type supprts it, send it asynchronously. 506 if (!m_response.isNull() && !m_proxyResource && !shouldSendCachedDataSynchronouslyForType(type()) && !m_needsSynchronousCacheHit) { 507 m_clientsAwaitingCallback.add(client); 508 ResourceCallback::callbackHandler()->schedule(this); 509 return false; 510 } 511 512 m_clients.add(client); 513 return true; 514} 515 516void Resource::removeClient(ResourceClient* client) 517{ 518 if (m_clientsAwaitingCallback.contains(client)) { 519 ASSERT(!m_clients.contains(client)); 520 m_clientsAwaitingCallback.remove(client); 521 } else { 522 ASSERT(m_clients.contains(client)); 523 m_clients.remove(client); 524 didRemoveClient(client); 525 } 526 527 if (m_clientsAwaitingCallback.isEmpty()) 528 ResourceCallback::callbackHandler()->cancel(this); 529 530 bool deleted = deleteIfPossible(); 531 if (!deleted && !hasClients()) { 532 memoryCache()->makeDead(this); 533 if (!m_switchingClientsToRevalidatedResource) 534 allClientsRemoved(); 535 536 // RFC2616 14.9.2: 537 // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible" 538 // "... History buffers MAY store such responses as part of their normal operation." 539 // We allow non-secure content to be reused in history, but we do not allow secure content to be reused. 540 if (hasCacheControlNoStoreHeader() && url().protocolIs("https")) { 541 memoryCache()->remove(this); 542 memoryCache()->prune(); 543 } else { 544 memoryCache()->prune(this); 545 } 546 } 547 // This object may be dead here. 548} 549 550void Resource::allClientsRemoved() 551{ 552 if (!m_loader) 553 return; 554 if (m_type == MainResource || m_type == Raw) 555 cancelTimerFired(&m_cancelTimer); 556 else if (!m_cancelTimer.isActive()) 557 m_cancelTimer.startOneShot(0, FROM_HERE); 558 559 unlock(); 560} 561 562void Resource::cancelTimerFired(Timer<Resource>* timer) 563{ 564 ASSERT_UNUSED(timer, timer == &m_cancelTimer); 565 if (hasClients() || !m_loader) 566 return; 567 ResourcePtr<Resource> protect(this); 568 m_loader->cancelIfNotFinishing(); 569 if (m_status != Cached) 570 memoryCache()->remove(this); 571} 572 573bool Resource::deleteIfPossible() 574{ 575 if (canDelete() && !memoryCache()->contains(this)) { 576 InspectorInstrumentation::willDestroyResource(this); 577 dispose(); 578 memoryCache()->unregisterLiveResource(*this); 579#if !ENABLE(OILPAN) 580 delete this; 581#endif 582 return true; 583 } 584 return false; 585} 586 587void Resource::setDecodedSize(size_t decodedSize) 588{ 589 if (decodedSize == m_decodedSize) 590 return; 591 size_t oldSize = size(); 592 m_decodedSize = decodedSize; 593 memoryCache()->update(this, oldSize, size()); 594 memoryCache()->updateDecodedResource(this, UpdateForPropertyChange); 595} 596 597void Resource::setEncodedSize(size_t encodedSize) 598{ 599 if (encodedSize == m_encodedSize) 600 return; 601 size_t oldSize = size(); 602 m_encodedSize = encodedSize; 603 memoryCache()->update(this, oldSize, size()); 604} 605 606void Resource::didAccessDecodedData() 607{ 608 memoryCache()->updateDecodedResource(this, UpdateForAccess); 609 memoryCache()->prune(); 610} 611 612void Resource::finishPendingClients() 613{ 614 // We're going to notify clients one by one. It is simple if the client does nothing. 615 // However there are a couple other things that can happen. 616 // 617 // 1. Clients can be added during the loop. Make sure they are not processed. 618 // 2. Clients can be removed during the loop. Make sure they are always available to be 619 // removed. Also don't call removed clients or add them back. 620 621 // Handle case (1) by saving a list of clients to notify. A separate list also ensure 622 // a client is either in m_clients or m_clientsAwaitingCallback. 623 Vector<ResourceClient*> clientsToNotify; 624 copyToVector(m_clientsAwaitingCallback, clientsToNotify); 625 626 for (size_t i = 0; i < clientsToNotify.size(); ++i) { 627 ResourceClient* client = clientsToNotify[i]; 628 629 // Handle case (2) to skip removed clients. 630 if (!m_clientsAwaitingCallback.remove(client)) 631 continue; 632 m_clients.add(client); 633 didAddClient(client); 634 } 635 636 // It is still possible for the above loop to finish a new client synchronously. 637 // If there's no client waiting we should deschedule. 638 bool scheduled = ResourceCallback::callbackHandler()->isScheduled(this); 639 if (scheduled && m_clientsAwaitingCallback.isEmpty()) 640 ResourceCallback::callbackHandler()->cancel(this); 641 642 // Prevent the case when there are clients waiting but no callback scheduled. 643 ASSERT(m_clientsAwaitingCallback.isEmpty() || scheduled); 644} 645 646void Resource::prune() 647{ 648 destroyDecodedDataIfPossible(); 649 unlock(); 650} 651 652void Resource::setResourceToRevalidate(Resource* resource) 653{ 654 ASSERT(resource); 655 ASSERT(!m_resourceToRevalidate); 656 ASSERT(resource != this); 657 ASSERT(m_handlesToRevalidate.isEmpty()); 658 ASSERT(resource->type() == type()); 659 660 WTF_LOG(ResourceLoading, "Resource %p setResourceToRevalidate %p", this, resource); 661 662 // The following assert should be investigated whenever it occurs. Although it should never fire, it currently does in rare circumstances. 663 // https://bugs.webkit.org/show_bug.cgi?id=28604. 664 // So the code needs to be robust to this assert failing thus the "if (m_resourceToRevalidate->m_proxyResource == this)" in Resource::clearResourceToRevalidate. 665 ASSERT(!resource->m_proxyResource); 666 667 resource->m_proxyResource = this; 668 m_resourceToRevalidate = resource; 669} 670 671void Resource::clearResourceToRevalidate() 672{ 673 ASSERT(m_resourceToRevalidate); 674 if (m_switchingClientsToRevalidatedResource) 675 return; 676 677 // A resource may start revalidation before this method has been called, so check that this resource is still the proxy resource before clearing it out. 678 if (m_resourceToRevalidate->m_proxyResource == this) { 679 m_resourceToRevalidate->m_proxyResource = nullptr; 680 m_resourceToRevalidate->deleteIfPossible(); 681 } 682 m_handlesToRevalidate.clear(); 683 m_resourceToRevalidate = nullptr; 684 deleteIfPossible(); 685} 686 687void Resource::switchClientsToRevalidatedResource() 688{ 689 ASSERT(m_resourceToRevalidate); 690 ASSERT(memoryCache()->contains(m_resourceToRevalidate)); 691 ASSERT(!memoryCache()->contains(this)); 692 693 WTF_LOG(ResourceLoading, "Resource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate.get()); 694 695 m_resourceToRevalidate->m_identifier = m_identifier; 696 697 m_switchingClientsToRevalidatedResource = true; 698 HashSet<ResourcePtrBase*>::iterator end = m_handlesToRevalidate.end(); 699 for (HashSet<ResourcePtrBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) { 700 ResourcePtrBase* handle = *it; 701 handle->m_resource = m_resourceToRevalidate; 702 m_resourceToRevalidate->registerHandle(handle); 703 --m_handleCount; 704 } 705 ASSERT(!m_handleCount); 706 m_handlesToRevalidate.clear(); 707 708 Vector<ResourceClient*> clientsToMove; 709 HashCountedSet<ResourceClient*>::iterator end2 = m_clients.end(); 710 for (HashCountedSet<ResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) { 711 ResourceClient* client = it->key; 712 unsigned count = it->value; 713 while (count) { 714 clientsToMove.append(client); 715 --count; 716 } 717 } 718 719 unsigned moveCount = clientsToMove.size(); 720 for (unsigned n = 0; n < moveCount; ++n) 721 removeClient(clientsToMove[n]); 722 ASSERT(m_clients.isEmpty()); 723 724 for (unsigned n = 0; n < moveCount; ++n) 725 m_resourceToRevalidate->addClientToSet(clientsToMove[n]); 726 for (unsigned n = 0; n < moveCount; ++n) { 727 // Calling didAddClient may do anything, including trying to cancel revalidation. 728 // Assert that it didn't succeed. 729 ASSERT(m_resourceToRevalidate); 730 // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore. 731 if (m_resourceToRevalidate->m_clients.contains(clientsToMove[n])) 732 m_resourceToRevalidate->didAddClient(clientsToMove[n]); 733 } 734 m_switchingClientsToRevalidatedResource = false; 735} 736 737void Resource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse) 738{ 739 m_responseTimestamp = currentTime(); 740 741 // RFC2616 10.3.5 742 // Update cached headers from the 304 response 743 const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields(); 744 HTTPHeaderMap::const_iterator end = newHeaders.end(); 745 for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) { 746 // Entity headers should not be sent by servers when generating a 304 747 // response; misconfigured servers send them anyway. We shouldn't allow 748 // such headers to update the original request. We'll base this on the 749 // list defined by RFC2616 7.1, with a few additions for extension headers 750 // we care about. 751 if (!shouldUpdateHeaderAfterRevalidation(it->key)) 752 continue; 753 m_response.setHTTPHeaderField(it->key, it->value); 754 } 755} 756 757void Resource::revalidationSucceeded(const ResourceResponse& response) 758{ 759 ASSERT(m_resourceToRevalidate); 760 ASSERT(!memoryCache()->contains(m_resourceToRevalidate)); 761 ASSERT(m_resourceToRevalidate->isLoaded()); 762 763 // Calling evict() can potentially delete revalidatingResource, which we use 764 // below. This mustn't be the case since revalidation means it is loaded 765 // and so canDelete() is false. 766 ASSERT(!canDelete()); 767 768 m_resourceToRevalidate->updateResponseAfterRevalidation(response); 769 memoryCache()->replace(m_resourceToRevalidate, this); 770 771 switchClientsToRevalidatedResource(); 772 assertAlive(); 773 // clearResourceToRevalidate deletes this. 774 clearResourceToRevalidate(); 775} 776 777void Resource::revalidationFailed() 778{ 779 ASSERT(WTF::isMainThread()); 780 WTF_LOG(ResourceLoading, "Revalidation failed for %p", this); 781 ASSERT(resourceToRevalidate()); 782 clearResourceToRevalidate(); 783} 784 785void Resource::registerHandle(ResourcePtrBase* h) 786{ 787 assertAlive(); 788 ++m_handleCount; 789 if (m_resourceToRevalidate) 790 m_handlesToRevalidate.add(h); 791} 792 793void Resource::unregisterHandle(ResourcePtrBase* h) 794{ 795 assertAlive(); 796 ASSERT(m_handleCount > 0); 797 --m_handleCount; 798 799 if (m_resourceToRevalidate) 800 m_handlesToRevalidate.remove(h); 801 802 if (!m_handleCount) { 803 if (deleteIfPossible()) 804 return; 805 unlock(); 806 } else if (m_handleCount == 1 && memoryCache()->contains(this)) { 807 unlock(); 808 if (!hasClients()) 809 memoryCache()->prune(this); 810 } 811} 812 813bool Resource::canReuseRedirectChain() 814{ 815 for (size_t i = 0; i < m_redirectChain.size(); ++i) { 816 if (!canUseResponse(m_redirectChain[i].m_redirectResponse, m_responseTimestamp)) 817 return false; 818 if (m_redirectChain[i].m_request.cacheControlContainsNoCache() || m_redirectChain[i].m_request.cacheControlContainsNoStore()) 819 return false; 820 } 821 return true; 822} 823 824bool Resource::hasCacheControlNoStoreHeader() 825{ 826 return m_response.cacheControlContainsNoStore() || m_resourceRequest.cacheControlContainsNoStore(); 827} 828 829bool Resource::mustRevalidateDueToCacheHeaders() 830{ 831 return !canUseResponse(m_response, m_responseTimestamp) || m_resourceRequest.cacheControlContainsNoCache() || m_resourceRequest.cacheControlContainsNoStore(); 832} 833 834bool Resource::canUseCacheValidator() 835{ 836 if (m_loading || errorOccurred()) 837 return false; 838 839 if (hasCacheControlNoStoreHeader()) 840 return false; 841 return m_response.hasCacheValidatorFields() || m_resourceRequest.hasCacheValidatorFields(); 842} 843 844bool Resource::isPurgeable() const 845{ 846 return m_data && !m_data->isLocked(); 847} 848 849bool Resource::wasPurged() const 850{ 851 return m_wasPurged; 852} 853 854bool Resource::lock() 855{ 856 if (!m_data) 857 return true; 858 if (m_data->isLocked()) 859 return true; 860 861 ASSERT(!hasClients()); 862 863 if (!m_data->lock()) { 864 m_wasPurged = true; 865 return false; 866 } 867 return true; 868} 869 870size_t Resource::overheadSize() const 871{ 872 static const int kAverageClientsHashMapSize = 384; 873 return sizeof(Resource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2; 874} 875 876void Resource::didChangePriority(ResourceLoadPriority loadPriority, int intraPriorityValue) 877{ 878 if (m_loader) 879 m_loader->didChangePriority(loadPriority, intraPriorityValue); 880} 881 882Resource::ResourceCallback* Resource::ResourceCallback::callbackHandler() 883{ 884 DEFINE_STATIC_LOCAL(ResourceCallback, callbackHandler, ()); 885 return &callbackHandler; 886} 887 888Resource::ResourceCallback::ResourceCallback() 889 : m_callbackTimer(this, &ResourceCallback::timerFired) 890{ 891} 892 893void Resource::ResourceCallback::schedule(Resource* resource) 894{ 895 if (!m_callbackTimer.isActive()) 896 m_callbackTimer.startOneShot(0, FROM_HERE); 897 resource->assertAlive(); 898 m_resourcesWithPendingClients.add(resource); 899} 900 901void Resource::ResourceCallback::cancel(Resource* resource) 902{ 903 resource->assertAlive(); 904 m_resourcesWithPendingClients.remove(resource); 905 if (m_callbackTimer.isActive() && m_resourcesWithPendingClients.isEmpty()) 906 m_callbackTimer.stop(); 907} 908 909bool Resource::ResourceCallback::isScheduled(Resource* resource) const 910{ 911 return m_resourcesWithPendingClients.contains(resource); 912} 913 914void Resource::ResourceCallback::timerFired(Timer<ResourceCallback>*) 915{ 916 HashSet<Resource*>::iterator end = m_resourcesWithPendingClients.end(); 917 Vector<ResourcePtr<Resource> > resources; 918 for (HashSet<Resource*>::iterator it = m_resourcesWithPendingClients.begin(); it != end; ++it) 919 resources.append(*it); 920 m_resourcesWithPendingClients.clear(); 921 922 for (size_t i = 0; i < resources.size(); i++) { 923 resources[i]->assertAlive(); 924 resources[i]->finishPendingClients(); 925 resources[i]->assertAlive(); 926 } 927 928 for (size_t i = 0; i < resources.size(); i++) 929 resources[i]->assertAlive(); 930} 931 932static const char* initatorTypeNameToString(const AtomicString& initiatorTypeName) 933{ 934 if (initiatorTypeName == FetchInitiatorTypeNames::css) 935 return "CSS resource"; 936 if (initiatorTypeName == FetchInitiatorTypeNames::document) 937 return "Document"; 938 if (initiatorTypeName == FetchInitiatorTypeNames::icon) 939 return "Icon"; 940 if (initiatorTypeName == FetchInitiatorTypeNames::internal) 941 return "Internal resource"; 942 if (initiatorTypeName == FetchInitiatorTypeNames::link) 943 return "Link element resource"; 944 if (initiatorTypeName == FetchInitiatorTypeNames::processinginstruction) 945 return "Processing instruction"; 946 if (initiatorTypeName == FetchInitiatorTypeNames::texttrack) 947 return "Text track"; 948 if (initiatorTypeName == FetchInitiatorTypeNames::xml) 949 return "XML resource"; 950 if (initiatorTypeName == FetchInitiatorTypeNames::xmlhttprequest) 951 return "XMLHttpRequest"; 952 953 return "Resource"; 954} 955 956const char* Resource::resourceTypeToString(Type type, const FetchInitiatorInfo& initiatorInfo) 957{ 958 switch (type) { 959 case Resource::MainResource: 960 return "Main resource"; 961 case Resource::Image: 962 return "Image"; 963 case Resource::CSSStyleSheet: 964 return "CSS stylesheet"; 965 case Resource::Script: 966 return "Script"; 967 case Resource::Font: 968 return "Font"; 969 case Resource::Raw: 970 return initatorTypeNameToString(initiatorInfo.name); 971 case Resource::SVGDocument: 972 return "SVG document"; 973 case Resource::XSLStyleSheet: 974 return "XSL stylesheet"; 975 case Resource::LinkPrefetch: 976 return "Link prefetch resource"; 977 case Resource::LinkSubresource: 978 return "Link subresource"; 979 case Resource::TextTrack: 980 return "Text track"; 981 case Resource::ImportResource: 982 return "Imported resource"; 983 case Resource::Media: 984 return "Media"; 985 } 986 ASSERT_NOT_REACHED(); 987 return initatorTypeNameToString(initiatorInfo.name); 988} 989 990#if !LOG_DISABLED 991const char* ResourceTypeName(Resource::Type type) 992{ 993 switch (type) { 994 case Resource::MainResource: 995 return "MainResource"; 996 case Resource::Image: 997 return "Image"; 998 case Resource::CSSStyleSheet: 999 return "CSSStyleSheet"; 1000 case Resource::Script: 1001 return "Script"; 1002 case Resource::Font: 1003 return "Font"; 1004 case Resource::Raw: 1005 return "Raw"; 1006 case Resource::SVGDocument: 1007 return "SVGDocument"; 1008 case Resource::XSLStyleSheet: 1009 return "XSLStyleSheet"; 1010 case Resource::LinkPrefetch: 1011 return "LinkPrefetch"; 1012 case Resource::LinkSubresource: 1013 return "LinkSubresource"; 1014 case Resource::TextTrack: 1015 return "TextTrack"; 1016 case Resource::ImportResource: 1017 return "ImportResource"; 1018 case Resource::Media: 1019 return "Media"; 1020 } 1021 ASSERT_NOT_REACHED(); 1022 return "Unknown"; 1023} 1024#endif // !LOG_DISABLED 1025 1026} 1027