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 "CachedResource.h" 26 27#include "CachedResourceClient.h" 28#include "CachedResourceClientWalker.h" 29#include "CachedResourceHandle.h" 30#include "CachedResourceLoader.h" 31#include "CrossOriginAccessControl.h" 32#include "Document.h" 33#include "DocumentLoader.h" 34#include "FrameLoader.h" 35#include "FrameLoaderClient.h" 36#include "InspectorInstrumentation.h" 37#include "KURL.h" 38#include "LoaderStrategy.h" 39#include "Logging.h" 40#include "MemoryCache.h" 41#include "PlatformStrategies.h" 42#include "PurgeableBuffer.h" 43#include "ResourceBuffer.h" 44#include "ResourceHandle.h" 45#include "ResourceLoadScheduler.h" 46#include "SchemeRegistry.h" 47#include "SecurityOrigin.h" 48#include "SecurityPolicy.h" 49#include "SubresourceLoader.h" 50#include <wtf/CurrentTime.h> 51#include <wtf/MathExtras.h> 52#include <wtf/RefCountedLeakCounter.h> 53#include <wtf/StdLibExtras.h> 54#include <wtf/text/CString.h> 55#include <wtf/Vector.h> 56 57using namespace WTF; 58 59namespace WebCore { 60 61// These response headers are not copied from a revalidated response to the 62// cached response headers. For compatibility, this list is based on Chromium's 63// net/http/http_response_headers.cc. 64const char* const headersToIgnoreAfterRevalidation[] = { 65 "allow", 66 "connection", 67 "etag", 68 "expires", 69 "keep-alive", 70 "last-modified" 71 "proxy-authenticate", 72 "proxy-connection", 73 "trailer", 74 "transfer-encoding", 75 "upgrade", 76 "www-authenticate", 77 "x-frame-options", 78 "x-xss-protection", 79}; 80 81// Some header prefixes mean "Don't copy this header from a 304 response.". 82// Rather than listing all the relevant headers, we can consolidate them into 83// this list, also grabbed from Chromium's net/http/http_response_headers.cc. 84const char* const headerPrefixesToIgnoreAfterRevalidation[] = { 85 "content-", 86 "x-content-", 87 "x-webkit-" 88}; 89 90static inline bool shouldUpdateHeaderAfterRevalidation(const AtomicString& header) 91{ 92 for (size_t i = 0; i < WTF_ARRAY_LENGTH(headersToIgnoreAfterRevalidation); i++) { 93 if (equalIgnoringCase(header, headersToIgnoreAfterRevalidation[i])) 94 return false; 95 } 96 for (size_t i = 0; i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) { 97 if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i], false)) 98 return false; 99 } 100 return true; 101} 102 103static ResourceLoadPriority defaultPriorityForResourceType(CachedResource::Type type) 104{ 105 switch (type) { 106 case CachedResource::MainResource: 107 return ResourceLoadPriorityVeryHigh; 108 case CachedResource::CSSStyleSheet: 109 return ResourceLoadPriorityHigh; 110 case CachedResource::Script: 111 case CachedResource::FontResource: 112 case CachedResource::RawResource: 113 return ResourceLoadPriorityMedium; 114 case CachedResource::ImageResource: 115 return ResourceLoadPriorityLow; 116#if ENABLE(XSLT) 117 case CachedResource::XSLStyleSheet: 118 return ResourceLoadPriorityHigh; 119#endif 120#if ENABLE(SVG) 121 case CachedResource::SVGDocumentResource: 122 return ResourceLoadPriorityLow; 123#endif 124#if ENABLE(LINK_PREFETCH) 125 case CachedResource::LinkPrefetch: 126 return ResourceLoadPriorityVeryLow; 127 case CachedResource::LinkSubresource: 128 return ResourceLoadPriorityVeryLow; 129#endif 130#if ENABLE(VIDEO_TRACK) 131 case CachedResource::TextTrackResource: 132 return ResourceLoadPriorityLow; 133#endif 134#if ENABLE(CSS_SHADERS) 135 case CachedResource::ShaderResource: 136 return ResourceLoadPriorityMedium; 137#endif 138 } 139 ASSERT_NOT_REACHED(); 140 return ResourceLoadPriorityLow; 141} 142 143#if PLATFORM(BLACKBERRY) 144static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type) 145{ 146 switch (type) { 147 case CachedResource::MainResource: 148 return ResourceRequest::TargetIsMainFrame; 149 case CachedResource::CSSStyleSheet: 150#if ENABLE(XSLT) 151 case CachedResource::XSLStyleSheet: 152#endif 153 return ResourceRequest::TargetIsStyleSheet; 154 case CachedResource::Script: 155 return ResourceRequest::TargetIsScript; 156 case CachedResource::FontResource: 157 return ResourceRequest::TargetIsFontResource; 158 case CachedResource::ImageResource: 159 return ResourceRequest::TargetIsImage; 160#if ENABLE(CSS_SHADERS) 161 case CachedResource::ShaderResource: 162#endif 163 case CachedResource::RawResource: 164 return ResourceRequest::TargetIsSubresource; 165#if ENABLE(LINK_PREFETCH) 166 case CachedResource::LinkPrefetch: 167 return ResourceRequest::TargetIsPrefetch; 168 case CachedResource::LinkSubresource: 169 return ResourceRequest::TargetIsSubresource; 170#endif 171#if ENABLE(VIDEO_TRACK) 172 case CachedResource::TextTrackResource: 173 return ResourceRequest::TargetIsTextTrack; 174#endif 175#if ENABLE(SVG) 176 case CachedResource::SVGDocumentResource: 177 return ResourceRequest::TargetIsImage; 178#endif 179 } 180 ASSERT_NOT_REACHED(); 181 return ResourceRequest::TargetIsSubresource; 182} 183#endif 184 185DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("CachedResource")); 186 187CachedResource::CachedResource(const ResourceRequest& request, Type type) 188 : m_resourceRequest(request) 189 , m_loadPriority(defaultPriorityForResourceType(type)) 190 , m_responseTimestamp(currentTime()) 191 , m_decodedDataDeletionTimer(this, &CachedResource::decodedDataDeletionTimerFired) 192 , m_lastDecodedAccessTime(0) 193 , m_loadFinishTime(0) 194 , m_encodedSize(0) 195 , m_decodedSize(0) 196 , m_accessCount(0) 197 , m_handleCount(0) 198 , m_preloadCount(0) 199 , m_preloadResult(PreloadNotReferenced) 200 , m_inLiveDecodedResourcesList(false) 201 , m_requestedFromNetworkingLayer(false) 202 , m_inCache(false) 203 , m_loading(false) 204 , m_switchingClientsToRevalidatedResource(false) 205 , m_type(type) 206 , m_status(Pending) 207#ifndef NDEBUG 208 , m_deleted(false) 209 , m_lruIndex(0) 210#endif 211 , m_nextInAllResourcesList(0) 212 , m_prevInAllResourcesList(0) 213 , m_nextInLiveResourcesList(0) 214 , m_prevInLiveResourcesList(0) 215 , m_owningCachedResourceLoader(0) 216 , m_resourceToRevalidate(0) 217 , m_proxyResource(0) 218{ 219 ASSERT(m_type == unsigned(type)); // m_type is a bitfield, so this tests careless updates of the enum. 220#ifndef NDEBUG 221 cachedResourceLeakCounter.increment(); 222#endif 223 224 if (!m_resourceRequest.url().hasFragmentIdentifier()) 225 return; 226 KURL urlForCache = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url()); 227 if (urlForCache.hasFragmentIdentifier()) 228 return; 229 m_fragmentIdentifierForRequest = m_resourceRequest.url().fragmentIdentifier(); 230 m_resourceRequest.setURL(urlForCache); 231} 232 233CachedResource::~CachedResource() 234{ 235 ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this. 236 ASSERT(canDelete()); 237 ASSERT(!inCache()); 238 ASSERT(!m_deleted); 239 ASSERT(url().isNull() || memoryCache()->resourceForRequest(resourceRequest()) != this); 240 241#ifndef NDEBUG 242 m_deleted = true; 243 cachedResourceLeakCounter.decrement(); 244#endif 245 246 if (m_owningCachedResourceLoader) 247 m_owningCachedResourceLoader->removeCachedResource(this); 248} 249 250void CachedResource::failBeforeStarting() 251{ 252 // FIXME: What if resources in other frames were waiting for this revalidation? 253 LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data()); 254 if (m_resourceToRevalidate) 255 memoryCache()->revalidationFailed(this); 256 error(CachedResource::LoadError); 257} 258 259void CachedResource::addAdditionalRequestHeaders(CachedResourceLoader* cachedResourceLoader) 260{ 261 // Note: We skip the Content-Security-Policy check here because we check 262 // the Content-Security-Policy at the CachedResourceLoader layer so we can 263 // handle different resource types differently. 264 265 FrameLoader* frameLoader = cachedResourceLoader->frame()->loader(); 266 String outgoingReferrer; 267 String outgoingOrigin; 268 if (m_resourceRequest.httpReferrer().isNull()) { 269 outgoingReferrer = frameLoader->outgoingReferrer(); 270 outgoingOrigin = frameLoader->outgoingOrigin(); 271 } else { 272 outgoingReferrer = m_resourceRequest.httpReferrer(); 273 outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString(); 274 } 275 276 outgoingReferrer = SecurityPolicy::generateReferrerHeader(cachedResourceLoader->document()->referrerPolicy(), m_resourceRequest.url(), outgoingReferrer); 277 if (outgoingReferrer.isEmpty()) 278 m_resourceRequest.clearHTTPReferrer(); 279 else if (!m_resourceRequest.httpReferrer()) 280 m_resourceRequest.setHTTPReferrer(outgoingReferrer); 281 FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin); 282 283 frameLoader->addExtraFieldsToSubresourceRequest(m_resourceRequest); 284} 285 286void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options) 287{ 288 if (!cachedResourceLoader->frame()) { 289 failBeforeStarting(); 290 return; 291 } 292 293 FrameLoader* frameLoader = cachedResourceLoader->frame()->loader(); 294 if (options.securityCheck == DoSecurityCheck && (frameLoader->state() == FrameStateProvisional || !frameLoader->activeDocumentLoader() || frameLoader->activeDocumentLoader()->isStopping())) { 295 failBeforeStarting(); 296 return; 297 } 298 299 m_options = options; 300 m_loading = true; 301 302#if PLATFORM(BLACKBERRY) 303 if (m_resourceRequest.targetType() == ResourceRequest::TargetIsUnspecified) 304 m_resourceRequest.setTargetType(cachedResourceTypeToTargetType(type())); 305#endif 306 307 if (!accept().isEmpty()) 308 m_resourceRequest.setHTTPAccept(accept()); 309 310 if (isCacheValidator()) { 311 CachedResource* resourceToRevalidate = m_resourceToRevalidate; 312 ASSERT(resourceToRevalidate->canUseCacheValidator()); 313 ASSERT(resourceToRevalidate->isLoaded()); 314 const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified"); 315 const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag"); 316 if (!lastModified.isEmpty() || !eTag.isEmpty()) { 317 ASSERT(cachedResourceLoader->cachePolicy(type()) != CachePolicyReload); 318 if (cachedResourceLoader->cachePolicy(type()) == CachePolicyRevalidate) 319 m_resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); 320 if (!lastModified.isEmpty()) 321 m_resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified); 322 if (!eTag.isEmpty()) 323 m_resourceRequest.setHTTPHeaderField("If-None-Match", eTag); 324 } 325 } 326 327#if ENABLE(LINK_PREFETCH) 328 if (type() == CachedResource::LinkPrefetch || type() == CachedResource::LinkSubresource) 329 m_resourceRequest.setHTTPHeaderField("Purpose", "prefetch"); 330#endif 331 m_resourceRequest.setPriority(loadPriority()); 332 333 if (type() != MainResource) 334 addAdditionalRequestHeaders(cachedResourceLoader); 335 336 // FIXME: It's unfortunate that the cache layer and below get to know anything about fragment identifiers. 337 // We should look into removing the expectation of that knowledge from the platform network stacks. 338 ResourceRequest request(m_resourceRequest); 339 if (!m_fragmentIdentifierForRequest.isNull()) { 340 KURL url = request.url(); 341 url.setFragmentIdentifier(m_fragmentIdentifierForRequest); 342 request.setURL(url); 343 m_fragmentIdentifierForRequest = String(); 344 } 345 346 m_loader = platformStrategies()->loaderStrategy()->resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->frame(), this, request, request.priority(), options); 347 if (!m_loader) { 348 failBeforeStarting(); 349 return; 350 } 351 352 m_status = Pending; 353} 354 355void CachedResource::checkNotify() 356{ 357 if (isLoading()) 358 return; 359 360 CachedResourceClientWalker<CachedResourceClient> w(m_clients); 361 while (CachedResourceClient* c = w.next()) 362 c->notifyFinished(this); 363} 364 365void CachedResource::addDataBuffer(ResourceBuffer*) 366{ 367 ASSERT(m_options.dataBufferingPolicy == BufferData); 368} 369 370void CachedResource::addData(const char*, unsigned) 371{ 372 ASSERT(m_options.dataBufferingPolicy == DoNotBufferData); 373} 374 375void CachedResource::finishLoading(ResourceBuffer*) 376{ 377 setLoading(false); 378 checkNotify(); 379} 380 381void CachedResource::error(CachedResource::Status status) 382{ 383 setStatus(status); 384 ASSERT(errorOccurred()); 385 m_data.clear(); 386 387 setLoading(false); 388 checkNotify(); 389} 390 391void CachedResource::cancelLoad() 392{ 393 if (!isLoading()) 394 return; 395 396 setStatus(LoadError); 397 setLoading(false); 398 checkNotify(); 399} 400 401void CachedResource::finish() 402{ 403 if (!errorOccurred()) 404 m_status = Cached; 405} 406 407bool CachedResource::passesAccessControlCheck(SecurityOrigin* securityOrigin) 408{ 409 String errorDescription; 410 return WebCore::passesAccessControlCheck(m_response, resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription); 411} 412 413bool CachedResource::isExpired() const 414{ 415 if (m_response.isNull()) 416 return false; 417 418 return currentAge() > freshnessLifetime(); 419} 420 421double CachedResource::currentAge() const 422{ 423 // RFC2616 13.2.3 424 // No compensation for latency as that is not terribly important in practice 425 double dateValue = m_response.date(); 426 double apparentAge = std::isfinite(dateValue) ? std::max(0., m_responseTimestamp - dateValue) : 0; 427 double ageValue = m_response.age(); 428 double correctedReceivedAge = std::isfinite(ageValue) ? std::max(apparentAge, ageValue) : apparentAge; 429 double residentTime = currentTime() - m_responseTimestamp; 430 return correctedReceivedAge + residentTime; 431} 432 433double CachedResource::freshnessLifetime() const 434{ 435 if (!m_response.url().protocolIsInHTTPFamily()) { 436 // Don't cache non-HTTP main resources since we can't check for freshness. 437 // FIXME: We should not cache subresources either, but when we tried this 438 // it caused performance and flakiness issues in our test infrastructure. 439 if (m_type == MainResource && !SchemeRegistry::shouldCacheResponsesFromURLSchemeIndefinitely(m_response.url().protocol())) 440 return 0; 441 442 return std::numeric_limits<double>::max(); 443 } 444 445 // RFC2616 13.2.4 446 double maxAgeValue = m_response.cacheControlMaxAge(); 447 if (std::isfinite(maxAgeValue)) 448 return maxAgeValue; 449 double expiresValue = m_response.expires(); 450 double dateValue = m_response.date(); 451 double creationTime = std::isfinite(dateValue) ? dateValue : m_responseTimestamp; 452 if (std::isfinite(expiresValue)) 453 return expiresValue - creationTime; 454 double lastModifiedValue = m_response.lastModified(); 455 if (std::isfinite(lastModifiedValue)) 456 return (creationTime - lastModifiedValue) * 0.1; 457 // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0. 458 return 0; 459} 460 461void CachedResource::responseReceived(const ResourceResponse& response) 462{ 463 setResponse(response); 464 m_responseTimestamp = currentTime(); 465 String encoding = response.textEncodingName(); 466 if (!encoding.isNull()) 467 setEncoding(encoding); 468} 469 470void CachedResource::clearLoader() 471{ 472 ASSERT(m_loader); 473 m_loader = 0; 474} 475 476void CachedResource::addClient(CachedResourceClient* client) 477{ 478 if (addClientToSet(client)) 479 didAddClient(client); 480} 481 482void CachedResource::didAddClient(CachedResourceClient* c) 483{ 484 if (m_decodedDataDeletionTimer.isActive()) 485 m_decodedDataDeletionTimer.stop(); 486 487 if (m_clientsAwaitingCallback.contains(c)) { 488 m_clients.add(c); 489 m_clientsAwaitingCallback.remove(c); 490 } 491 if (!isLoading() && !stillNeedsLoad()) 492 c->notifyFinished(this); 493} 494 495bool CachedResource::addClientToSet(CachedResourceClient* client) 496{ 497 ASSERT(!isPurgeable()); 498 499 if (m_preloadResult == PreloadNotReferenced) { 500 if (isLoaded()) 501 m_preloadResult = PreloadReferencedWhileComplete; 502 else if (m_requestedFromNetworkingLayer) 503 m_preloadResult = PreloadReferencedWhileLoading; 504 else 505 m_preloadResult = PreloadReferenced; 506 } 507 if (!hasClients() && inCache()) 508 memoryCache()->addToLiveResourcesSize(this); 509 510 if ((m_type == RawResource || m_type == MainResource) && !m_response.isNull() && !m_proxyResource) { 511 // Certain resources (especially XHRs and main resources) do crazy things if an asynchronous load returns 512 // synchronously (e.g., scripts may not have set all the state they need to handle the load). 513 // Therefore, rather than immediately sending callbacks on a cache hit like other CachedResources, 514 // we schedule the callbacks and ensure we never finish synchronously. 515 ASSERT(!m_clientsAwaitingCallback.contains(client)); 516 m_clientsAwaitingCallback.add(client, CachedResourceCallback::schedule(this, client)); 517 return false; 518 } 519 520 m_clients.add(client); 521 return true; 522} 523 524void CachedResource::removeClient(CachedResourceClient* client) 525{ 526 OwnPtr<CachedResourceCallback> callback = m_clientsAwaitingCallback.take(client); 527 if (callback) { 528 ASSERT(!m_clients.contains(client)); 529 callback->cancel(); 530 callback.clear(); 531 } else { 532 ASSERT(m_clients.contains(client)); 533 m_clients.remove(client); 534 didRemoveClient(client); 535 } 536 537 bool deleted = deleteIfPossible(); 538 if (!deleted && !hasClients()) { 539 if (inCache()) { 540 memoryCache()->removeFromLiveResourcesSize(this); 541 memoryCache()->removeFromLiveDecodedResourcesList(this); 542 } 543 if (!m_switchingClientsToRevalidatedResource) 544 allClientsRemoved(); 545 destroyDecodedDataIfNeeded(); 546 if (response().cacheControlContainsNoStore()) { 547 // RFC2616 14.9.2: 548 // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible" 549 // "... History buffers MAY store such responses as part of their normal operation." 550 // We allow non-secure content to be reused in history, but we do not allow secure content to be reused. 551 if (url().protocolIs("https")) 552 memoryCache()->remove(this); 553 } else 554 memoryCache()->prune(); 555 } 556 // This object may be dead here. 557} 558 559void CachedResource::destroyDecodedDataIfNeeded() 560{ 561 if (!m_decodedSize) 562 return; 563 564 if (double interval = memoryCache()->deadDecodedDataDeletionInterval()) 565 m_decodedDataDeletionTimer.startOneShot(interval); 566} 567 568void CachedResource::decodedDataDeletionTimerFired(Timer<CachedResource>*) 569{ 570 destroyDecodedData(); 571} 572 573bool CachedResource::deleteIfPossible() 574{ 575 if (canDelete() && !inCache()) { 576 InspectorInstrumentation::willDestroyCachedResource(this); 577 delete this; 578 return true; 579 } 580 return false; 581} 582 583void CachedResource::setDecodedSize(unsigned size) 584{ 585 if (size == m_decodedSize) 586 return; 587 588 int delta = size - m_decodedSize; 589 590 // The object must now be moved to a different queue, since its size has been changed. 591 // We have to remove explicitly before updating m_decodedSize, so that we find the correct previous 592 // queue. 593 if (inCache()) 594 memoryCache()->removeFromLRUList(this); 595 596 m_decodedSize = size; 597 598 if (inCache()) { 599 // Now insert into the new LRU list. 600 memoryCache()->insertInLRUList(this); 601 602 // Insert into or remove from the live decoded list if necessary. 603 // When inserting into the LiveDecodedResourcesList it is possible 604 // that the m_lastDecodedAccessTime is still zero or smaller than 605 // the m_lastDecodedAccessTime of the current list head. This is a 606 // violation of the invariant that the list is to be kept sorted 607 // by access time. The weakening of the invariant does not pose 608 // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209 609 if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients()) 610 memoryCache()->insertInLiveDecodedResourcesList(this); 611 else if (!m_decodedSize && m_inLiveDecodedResourcesList) 612 memoryCache()->removeFromLiveDecodedResourcesList(this); 613 614 // Update the cache's size totals. 615 memoryCache()->adjustSize(hasClients(), delta); 616 } 617} 618 619void CachedResource::setEncodedSize(unsigned size) 620{ 621 if (size == m_encodedSize) 622 return; 623 624 // The size cannot ever shrink (unless it is being nulled out because of an error). If it ever does, assert. 625 ASSERT(size == 0 || size >= m_encodedSize); 626 627 int delta = size - m_encodedSize; 628 629 // The object must now be moved to a different queue, since its size has been changed. 630 // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous 631 // queue. 632 if (inCache()) 633 memoryCache()->removeFromLRUList(this); 634 635 m_encodedSize = size; 636 637 if (inCache()) { 638 // Now insert into the new LRU list. 639 memoryCache()->insertInLRUList(this); 640 641 // Update the cache's size totals. 642 memoryCache()->adjustSize(hasClients(), delta); 643 } 644} 645 646void CachedResource::didAccessDecodedData(double timeStamp) 647{ 648 m_lastDecodedAccessTime = timeStamp; 649 650 if (inCache()) { 651 if (m_inLiveDecodedResourcesList) { 652 memoryCache()->removeFromLiveDecodedResourcesList(this); 653 memoryCache()->insertInLiveDecodedResourcesList(this); 654 } 655 memoryCache()->prune(); 656 } 657} 658 659void CachedResource::setResourceToRevalidate(CachedResource* resource) 660{ 661 ASSERT(resource); 662 ASSERT(!m_resourceToRevalidate); 663 ASSERT(resource != this); 664 ASSERT(m_handlesToRevalidate.isEmpty()); 665 ASSERT(resource->type() == type()); 666 667 LOG(ResourceLoading, "CachedResource %p setResourceToRevalidate %p", this, resource); 668 669 // The following assert should be investigated whenever it occurs. Although it should never fire, it currently does in rare circumstances. 670 // https://bugs.webkit.org/show_bug.cgi?id=28604. 671 // So the code needs to be robust to this assert failing thus the "if (m_resourceToRevalidate->m_proxyResource == this)" in CachedResource::clearResourceToRevalidate. 672 ASSERT(!resource->m_proxyResource); 673 674 resource->m_proxyResource = this; 675 m_resourceToRevalidate = resource; 676} 677 678void CachedResource::clearResourceToRevalidate() 679{ 680 ASSERT(m_resourceToRevalidate); 681 if (m_switchingClientsToRevalidatedResource) 682 return; 683 684 // 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. 685 if (m_resourceToRevalidate->m_proxyResource == this) { 686 m_resourceToRevalidate->m_proxyResource = 0; 687 m_resourceToRevalidate->deleteIfPossible(); 688 } 689 m_handlesToRevalidate.clear(); 690 m_resourceToRevalidate = 0; 691 deleteIfPossible(); 692} 693 694void CachedResource::switchClientsToRevalidatedResource() 695{ 696 ASSERT(m_resourceToRevalidate); 697 ASSERT(m_resourceToRevalidate->inCache()); 698 ASSERT(!inCache()); 699 700 LOG(ResourceLoading, "CachedResource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate); 701 702 m_switchingClientsToRevalidatedResource = true; 703 HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end(); 704 for (HashSet<CachedResourceHandleBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) { 705 CachedResourceHandleBase* handle = *it; 706 handle->m_resource = m_resourceToRevalidate; 707 m_resourceToRevalidate->registerHandle(handle); 708 --m_handleCount; 709 } 710 ASSERT(!m_handleCount); 711 m_handlesToRevalidate.clear(); 712 713 Vector<CachedResourceClient*> clientsToMove; 714 HashCountedSet<CachedResourceClient*>::iterator end2 = m_clients.end(); 715 for (HashCountedSet<CachedResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) { 716 CachedResourceClient* client = it->key; 717 unsigned count = it->value; 718 while (count) { 719 clientsToMove.append(client); 720 --count; 721 } 722 } 723 724 unsigned moveCount = clientsToMove.size(); 725 for (unsigned n = 0; n < moveCount; ++n) 726 removeClient(clientsToMove[n]); 727 ASSERT(m_clients.isEmpty()); 728 729 for (unsigned n = 0; n < moveCount; ++n) 730 m_resourceToRevalidate->addClientToSet(clientsToMove[n]); 731 for (unsigned n = 0; n < moveCount; ++n) { 732 // Calling didAddClient may do anything, including trying to cancel revalidation. 733 // Assert that it didn't succeed. 734 ASSERT(m_resourceToRevalidate); 735 // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore. 736 if (m_resourceToRevalidate->m_clients.contains(clientsToMove[n])) 737 m_resourceToRevalidate->didAddClient(clientsToMove[n]); 738 } 739 m_switchingClientsToRevalidatedResource = false; 740} 741 742void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse) 743{ 744 m_responseTimestamp = currentTime(); 745 746 // RFC2616 10.3.5 747 // Update cached headers from the 304 response 748 const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields(); 749 HTTPHeaderMap::const_iterator end = newHeaders.end(); 750 for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) { 751 // Entity headers should not be sent by servers when generating a 304 752 // response; misconfigured servers send them anyway. We shouldn't allow 753 // such headers to update the original request. We'll base this on the 754 // list defined by RFC2616 7.1, with a few additions for extension headers 755 // we care about. 756 if (!shouldUpdateHeaderAfterRevalidation(it->key)) 757 continue; 758 m_response.setHTTPHeaderField(it->key, it->value); 759 } 760} 761 762void CachedResource::registerHandle(CachedResourceHandleBase* h) 763{ 764 ++m_handleCount; 765 if (m_resourceToRevalidate) 766 m_handlesToRevalidate.add(h); 767} 768 769void CachedResource::unregisterHandle(CachedResourceHandleBase* h) 770{ 771 ASSERT(m_handleCount > 0); 772 --m_handleCount; 773 774 if (m_resourceToRevalidate) 775 m_handlesToRevalidate.remove(h); 776 777 if (!m_handleCount) 778 deleteIfPossible(); 779} 780 781bool CachedResource::canUseCacheValidator() const 782{ 783 if (m_loading || errorOccurred()) 784 return false; 785 786 if (m_response.cacheControlContainsNoStore()) 787 return false; 788 return m_response.hasCacheValidatorFields(); 789} 790 791bool CachedResource::mustRevalidateDueToCacheHeaders(CachePolicy cachePolicy) const 792{ 793 ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyCache || cachePolicy == CachePolicyVerify); 794 795 if (cachePolicy == CachePolicyRevalidate) 796 return true; 797 798 if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) { 799 LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this); 800 return true; 801 } 802 803 if (cachePolicy == CachePolicyCache) { 804 if (m_response.cacheControlContainsMustRevalidate() && isExpired()) { 805 LOG(ResourceLoading, "CachedResource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this); 806 return true; 807 } 808 return false; 809 } 810 811 // CachePolicyVerify 812 if (isExpired()) { 813 LOG(ResourceLoading, "CachedResource %p mustRevalidate because of isExpired()\n", this); 814 return true; 815 } 816 817 return false; 818} 819 820bool CachedResource::isSafeToMakePurgeable() const 821{ 822 return !hasClients() && !m_proxyResource && !m_resourceToRevalidate; 823} 824 825bool CachedResource::makePurgeable(bool purgeable) 826{ 827 if (purgeable) { 828 ASSERT(isSafeToMakePurgeable()); 829 830 if (m_purgeableData) { 831 ASSERT(!m_data); 832 return true; 833 } 834 if (!m_data) 835 return false; 836 837 // Should not make buffer purgeable if it has refs other than this since we don't want two copies. 838 if (!m_data->hasOneRef()) 839 return false; 840 841 m_data->createPurgeableBuffer(); 842 if (!m_data->hasPurgeableBuffer()) 843 return false; 844 845 m_purgeableData = m_data->releasePurgeableBuffer(); 846 m_purgeableData->setPurgePriority(purgePriority()); 847 m_purgeableData->makePurgeable(true); 848 m_data.clear(); 849 return true; 850 } 851 852 if (!m_purgeableData) 853 return true; 854 ASSERT(!m_data); 855 ASSERT(!hasClients()); 856 857 if (!m_purgeableData->makePurgeable(false)) 858 return false; 859 860 m_data = ResourceBuffer::adoptSharedBuffer(SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release())); 861 return true; 862} 863 864bool CachedResource::isPurgeable() const 865{ 866 return m_purgeableData && m_purgeableData->isPurgeable(); 867} 868 869bool CachedResource::wasPurged() const 870{ 871 return m_purgeableData && m_purgeableData->wasPurged(); 872} 873 874unsigned CachedResource::overheadSize() const 875{ 876 static const int kAverageClientsHashMapSize = 384; 877 return sizeof(CachedResource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2; 878} 879 880void CachedResource::setLoadPriority(ResourceLoadPriority loadPriority) 881{ 882 if (loadPriority == ResourceLoadPriorityUnresolved) 883 loadPriority = defaultPriorityForResourceType(type()); 884 if (loadPriority == m_loadPriority) 885 return; 886 m_loadPriority = loadPriority; 887 if (m_loader) 888 m_loader->didChangePriority(loadPriority); 889} 890 891 892CachedResource::CachedResourceCallback::CachedResourceCallback(CachedResource* resource, CachedResourceClient* client) 893 : m_resource(resource) 894 , m_client(client) 895 , m_callbackTimer(this, &CachedResourceCallback::timerFired) 896{ 897 m_callbackTimer.startOneShot(0); 898} 899 900void CachedResource::CachedResourceCallback::cancel() 901{ 902 if (m_callbackTimer.isActive()) 903 m_callbackTimer.stop(); 904} 905 906void CachedResource::CachedResourceCallback::timerFired(Timer<CachedResourceCallback>*) 907{ 908 m_resource->didAddClient(m_client); 909} 910 911#if PLATFORM(MAC) 912void CachedResource::tryReplaceEncodedData(PassRefPtr<SharedBuffer> newBuffer) 913{ 914 if (!m_data) 915 return; 916 917 if (!mayTryReplaceEncodedData()) 918 return; 919 920 // Because the disk cache is asynchronous and racey with regards to the data we might be asked to replace, 921 // we need to verify that the new buffer has the same contents as our old buffer. 922 if (m_data->size() != newBuffer->size() || memcmp(m_data->data(), newBuffer->data(), m_data->size())) 923 return; 924 925 m_data->tryReplaceSharedBufferContents(newBuffer.get()); 926} 927#endif 928 929} 930