Home | History | Annotate | Download | only in cache
      1 /*
      2     Copyright (C) 1998 Lars Knoll (knoll (at) mpi-hd.mpg.de)
      3     Copyright (C) 2001 Dirk Mueller (mueller (at) kde.org)
      4     Copyright (C) 2002 Waldo Bastian (bastian (at) kde.org)
      5     Copyright (C) 2006 Samuel Weinig (sam.weinig (at) gmail.com)
      6     Copyright (C) 2004, 2005, 2006, 2007, 2008 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 "MemoryCache.h"
     28 #include "CachedMetadata.h"
     29 #include "CachedResourceClient.h"
     30 #include "CachedResourceClientWalker.h"
     31 #include "CachedResourceHandle.h"
     32 #include "CachedResourceLoader.h"
     33 #include "CachedResourceRequest.h"
     34 #include "Frame.h"
     35 #include "FrameLoaderClient.h"
     36 #include "KURL.h"
     37 #include "Logging.h"
     38 #include "PurgeableBuffer.h"
     39 #include "ResourceHandle.h"
     40 #include "SharedBuffer.h"
     41 #include <wtf/CurrentTime.h>
     42 #include <wtf/MathExtras.h>
     43 #include <wtf/RefCountedLeakCounter.h>
     44 #include <wtf/StdLibExtras.h>
     45 #include <wtf/Vector.h>
     46 
     47 using namespace WTF;
     48 
     49 namespace WebCore {
     50 
     51 static ResourceLoadPriority defaultPriorityForResourceType(CachedResource::Type type)
     52 {
     53     switch (type) {
     54         case CachedResource::CSSStyleSheet:
     55 #if ENABLE(XSLT)
     56         case CachedResource::XSLStyleSheet:
     57 #endif
     58             return ResourceLoadPriorityHigh;
     59         case CachedResource::Script:
     60         case CachedResource::FontResource:
     61             return ResourceLoadPriorityMedium;
     62         case CachedResource::ImageResource:
     63             return ResourceLoadPriorityLow;
     64 #if ENABLE(LINK_PREFETCH)
     65         case CachedResource::LinkResource:
     66             return ResourceLoadPriorityVeryLow;
     67 #endif
     68     }
     69     ASSERT_NOT_REACHED();
     70     return ResourceLoadPriorityLow;
     71 }
     72 
     73 #ifndef NDEBUG
     74 static RefCountedLeakCounter cachedResourceLeakCounter("CachedResource");
     75 #endif
     76 
     77 CachedResource::CachedResource(const String& url, Type type)
     78     : m_url(url)
     79     , m_request(0)
     80     , m_loadPriority(defaultPriorityForResourceType(type))
     81     , m_responseTimestamp(currentTime())
     82     , m_lastDecodedAccessTime(0)
     83     , m_encodedSize(0)
     84     , m_decodedSize(0)
     85     , m_accessCount(0)
     86     , m_handleCount(0)
     87     , m_preloadCount(0)
     88     , m_preloadResult(PreloadNotReferenced)
     89     , m_inLiveDecodedResourcesList(false)
     90     , m_requestedFromNetworkingLayer(false)
     91     , m_sendResourceLoadCallbacks(true)
     92     , m_inCache(false)
     93     , m_loading(false)
     94     , m_type(type)
     95     , m_status(Pending)
     96 #ifndef NDEBUG
     97     , m_deleted(false)
     98     , m_lruIndex(0)
     99 #endif
    100     , m_nextInAllResourcesList(0)
    101     , m_prevInAllResourcesList(0)
    102     , m_nextInLiveResourcesList(0)
    103     , m_prevInLiveResourcesList(0)
    104     , m_owningCachedResourceLoader(0)
    105     , m_resourceToRevalidate(0)
    106     , m_proxyResource(0)
    107 {
    108 #ifndef NDEBUG
    109     cachedResourceLeakCounter.increment();
    110 #endif
    111 }
    112 
    113 CachedResource::~CachedResource()
    114 {
    115     ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this.
    116     ASSERT(canDelete());
    117     ASSERT(!inCache());
    118     ASSERT(!m_deleted);
    119     ASSERT(url().isNull() || memoryCache()->resourceForURL(KURL(ParsedURLString, url())) != this);
    120 
    121 #ifndef NDEBUG
    122     m_deleted = true;
    123     cachedResourceLeakCounter.decrement();
    124 #endif
    125 
    126     if (m_owningCachedResourceLoader)
    127         m_owningCachedResourceLoader->removeCachedResource(this);
    128 }
    129 
    130 void CachedResource::load(CachedResourceLoader* cachedResourceLoader, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
    131 {
    132     m_sendResourceLoadCallbacks = sendResourceLoadCallbacks;
    133     cachedResourceLoader->load(this, incremental, securityCheck, sendResourceLoadCallbacks);
    134     m_loading = true;
    135 }
    136 
    137 void CachedResource::checkNotify()
    138 {
    139     if (isLoading())
    140         return;
    141 
    142     CachedResourceClientWalker w(m_clients);
    143     while (CachedResourceClient* c = w.next())
    144         c->notifyFinished(this);
    145 }
    146 
    147 void CachedResource::data(PassRefPtr<SharedBuffer>, bool allDataReceived)
    148 {
    149     if (!allDataReceived)
    150         return;
    151 
    152     setLoading(false);
    153     checkNotify();
    154 }
    155 
    156 void CachedResource::error(CachedResource::Status status)
    157 {
    158     setStatus(status);
    159     ASSERT(errorOccurred());
    160     m_data.clear();
    161 
    162     setLoading(false);
    163     checkNotify();
    164 }
    165 
    166 void CachedResource::finish()
    167 {
    168     m_status = Cached;
    169 }
    170 
    171 bool CachedResource::isExpired() const
    172 {
    173     if (m_response.isNull())
    174         return false;
    175 
    176     return currentAge() > freshnessLifetime();
    177 }
    178 
    179 double CachedResource::currentAge() const
    180 {
    181     // RFC2616 13.2.3
    182     // No compensation for latency as that is not terribly important in practice
    183     double dateValue = m_response.date();
    184     double apparentAge = isfinite(dateValue) ? max(0., m_responseTimestamp - dateValue) : 0;
    185     double ageValue = m_response.age();
    186     double correctedReceivedAge = isfinite(ageValue) ? max(apparentAge, ageValue) : apparentAge;
    187     double residentTime = currentTime() - m_responseTimestamp;
    188     return correctedReceivedAge + residentTime;
    189 }
    190 
    191 double CachedResource::freshnessLifetime() const
    192 {
    193     // Cache non-http resources liberally
    194     if (!m_response.url().protocolInHTTPFamily())
    195         return std::numeric_limits<double>::max();
    196 
    197     // RFC2616 13.2.4
    198     double maxAgeValue = m_response.cacheControlMaxAge();
    199     if (isfinite(maxAgeValue))
    200         return maxAgeValue;
    201     double expiresValue = m_response.expires();
    202     double dateValue = m_response.date();
    203     double creationTime = isfinite(dateValue) ? dateValue : m_responseTimestamp;
    204     if (isfinite(expiresValue))
    205         return expiresValue - creationTime;
    206     double lastModifiedValue = m_response.lastModified();
    207     if (isfinite(lastModifiedValue))
    208         return (creationTime - lastModifiedValue) * 0.1;
    209     // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0.
    210     return 0;
    211 }
    212 
    213 void CachedResource::setResponse(const ResourceResponse& response)
    214 {
    215     m_response = response;
    216     m_responseTimestamp = currentTime();
    217 }
    218 
    219 void CachedResource::setSerializedCachedMetadata(const char* data, size_t size)
    220 {
    221     // We only expect to receive cached metadata from the platform once.
    222     // If this triggers, it indicates an efficiency problem which is most
    223     // likely unexpected in code designed to improve performance.
    224     ASSERT(!m_cachedMetadata);
    225 
    226     m_cachedMetadata = CachedMetadata::deserialize(data, size);
    227 }
    228 
    229 void CachedResource::setCachedMetadata(unsigned dataTypeID, const char* data, size_t size)
    230 {
    231     // Currently, only one type of cached metadata per resource is supported.
    232     // If the need arises for multiple types of metadata per resource this could
    233     // be enhanced to store types of metadata in a map.
    234     ASSERT(!m_cachedMetadata);
    235 
    236     m_cachedMetadata = CachedMetadata::create(dataTypeID, data, size);
    237     ResourceHandle::cacheMetadata(m_response, m_cachedMetadata->serialize());
    238 }
    239 
    240 CachedMetadata* CachedResource::cachedMetadata(unsigned dataTypeID) const
    241 {
    242     if (!m_cachedMetadata || m_cachedMetadata->dataTypeID() != dataTypeID)
    243         return 0;
    244     return m_cachedMetadata.get();
    245 }
    246 
    247 void CachedResource::setRequest(CachedResourceRequest* request)
    248 {
    249     if (request && !m_request)
    250         m_status = Pending;
    251     m_request = request;
    252     if (canDelete() && !inCache())
    253         delete this;
    254 }
    255 
    256 void CachedResource::addClient(CachedResourceClient* client)
    257 {
    258     addClientToSet(client);
    259     didAddClient(client);
    260 }
    261 
    262 void CachedResource::didAddClient(CachedResourceClient* c)
    263 {
    264     if (!isLoading() && !stillNeedsLoad())
    265         c->notifyFinished(this);
    266 }
    267 
    268 void CachedResource::addClientToSet(CachedResourceClient* client)
    269 {
    270     ASSERT(!isPurgeable());
    271 
    272     if (m_preloadResult == PreloadNotReferenced) {
    273         if (isLoaded())
    274             m_preloadResult = PreloadReferencedWhileComplete;
    275         else if (m_requestedFromNetworkingLayer)
    276             m_preloadResult = PreloadReferencedWhileLoading;
    277         else
    278             m_preloadResult = PreloadReferenced;
    279     }
    280     if (!hasClients() && inCache())
    281         memoryCache()->addToLiveResourcesSize(this);
    282     m_clients.add(client);
    283 }
    284 
    285 void CachedResource::removeClient(CachedResourceClient* client)
    286 {
    287     ASSERT(m_clients.contains(client));
    288     m_clients.remove(client);
    289 
    290     if (canDelete() && !inCache())
    291         delete this;
    292     else if (!hasClients() && inCache()) {
    293         memoryCache()->removeFromLiveResourcesSize(this);
    294         memoryCache()->removeFromLiveDecodedResourcesList(this);
    295         allClientsRemoved();
    296         if (response().cacheControlContainsNoStore()) {
    297             // RFC2616 14.9.2:
    298             // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
    299             // "... History buffers MAY store such responses as part of their normal operation."
    300             // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
    301             if (protocolIs(url(), "https"))
    302                 memoryCache()->remove(this);
    303         } else
    304             memoryCache()->prune();
    305     }
    306     // This object may be dead here.
    307 }
    308 
    309 void CachedResource::deleteIfPossible()
    310 {
    311     if (canDelete() && !inCache())
    312         delete this;
    313 }
    314 
    315 void CachedResource::setDecodedSize(unsigned size)
    316 {
    317     if (size == m_decodedSize)
    318         return;
    319 
    320     int delta = size - m_decodedSize;
    321 
    322     // The object must now be moved to a different queue, since its size has been changed.
    323     // We have to remove explicitly before updating m_decodedSize, so that we find the correct previous
    324     // queue.
    325     if (inCache())
    326         memoryCache()->removeFromLRUList(this);
    327 
    328     m_decodedSize = size;
    329 
    330     if (inCache()) {
    331         // Now insert into the new LRU list.
    332         memoryCache()->insertInLRUList(this);
    333 
    334         // Insert into or remove from the live decoded list if necessary.
    335         // When inserting into the LiveDecodedResourcesList it is possible
    336         // that the m_lastDecodedAccessTime is still zero or smaller than
    337         // the m_lastDecodedAccessTime of the current list head. This is a
    338         // violation of the invariant that the list is to be kept sorted
    339         // by access time. The weakening of the invariant does not pose
    340         // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209
    341         if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients())
    342             memoryCache()->insertInLiveDecodedResourcesList(this);
    343         else if (!m_decodedSize && m_inLiveDecodedResourcesList)
    344             memoryCache()->removeFromLiveDecodedResourcesList(this);
    345 
    346         // Update the cache's size totals.
    347         memoryCache()->adjustSize(hasClients(), delta);
    348     }
    349 }
    350 
    351 void CachedResource::setEncodedSize(unsigned size)
    352 {
    353     if (size == m_encodedSize)
    354         return;
    355 
    356     // The size cannot ever shrink (unless it is being nulled out because of an error).  If it ever does, assert.
    357     ASSERT(size == 0 || size >= m_encodedSize);
    358 
    359     int delta = size - m_encodedSize;
    360 
    361     // The object must now be moved to a different queue, since its size has been changed.
    362     // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous
    363     // queue.
    364     if (inCache())
    365         memoryCache()->removeFromLRUList(this);
    366 
    367     m_encodedSize = size;
    368 
    369     if (inCache()) {
    370         // Now insert into the new LRU list.
    371         memoryCache()->insertInLRUList(this);
    372 
    373         // Update the cache's size totals.
    374         memoryCache()->adjustSize(hasClients(), delta);
    375     }
    376 }
    377 
    378 void CachedResource::didAccessDecodedData(double timeStamp)
    379 {
    380     m_lastDecodedAccessTime = timeStamp;
    381 
    382     if (inCache()) {
    383         if (m_inLiveDecodedResourcesList) {
    384             memoryCache()->removeFromLiveDecodedResourcesList(this);
    385             memoryCache()->insertInLiveDecodedResourcesList(this);
    386         }
    387         memoryCache()->prune();
    388     }
    389 }
    390 
    391 void CachedResource::setResourceToRevalidate(CachedResource* resource)
    392 {
    393     ASSERT(resource);
    394     ASSERT(!m_resourceToRevalidate);
    395     ASSERT(resource != this);
    396     ASSERT(m_handlesToRevalidate.isEmpty());
    397     ASSERT(resource->type() == type());
    398 
    399     LOG(ResourceLoading, "CachedResource %p setResourceToRevalidate %p", this, resource);
    400 
    401     // The following assert should be investigated whenever it occurs. Although it should never fire, it currently does in rare circumstances.
    402     // https://bugs.webkit.org/show_bug.cgi?id=28604.
    403     // So the code needs to be robust to this assert failing thus the "if (m_resourceToRevalidate->m_proxyResource == this)" in CachedResource::clearResourceToRevalidate.
    404     ASSERT(!resource->m_proxyResource);
    405 
    406     resource->m_proxyResource = this;
    407     m_resourceToRevalidate = resource;
    408 }
    409 
    410 void CachedResource::clearResourceToRevalidate()
    411 {
    412     ASSERT(m_resourceToRevalidate);
    413     // 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.
    414     if (m_resourceToRevalidate->m_proxyResource == this) {
    415         m_resourceToRevalidate->m_proxyResource = 0;
    416         m_resourceToRevalidate->deleteIfPossible();
    417     }
    418     m_handlesToRevalidate.clear();
    419     m_resourceToRevalidate = 0;
    420     deleteIfPossible();
    421 }
    422 
    423 void CachedResource::switchClientsToRevalidatedResource()
    424 {
    425     ASSERT(m_resourceToRevalidate);
    426     ASSERT(m_resourceToRevalidate->inCache());
    427     ASSERT(!inCache());
    428 
    429     LOG(ResourceLoading, "CachedResource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate);
    430 
    431     HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end();
    432     for (HashSet<CachedResourceHandleBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) {
    433         CachedResourceHandleBase* handle = *it;
    434         handle->m_resource = m_resourceToRevalidate;
    435         m_resourceToRevalidate->registerHandle(handle);
    436         --m_handleCount;
    437     }
    438     ASSERT(!m_handleCount);
    439     m_handlesToRevalidate.clear();
    440 
    441     Vector<CachedResourceClient*> clientsToMove;
    442     HashCountedSet<CachedResourceClient*>::iterator end2 = m_clients.end();
    443     for (HashCountedSet<CachedResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) {
    444         CachedResourceClient* client = it->first;
    445         unsigned count = it->second;
    446         while (count) {
    447             clientsToMove.append(client);
    448             --count;
    449         }
    450     }
    451     // Equivalent of calling removeClient() for all clients
    452     m_clients.clear();
    453 
    454     unsigned moveCount = clientsToMove.size();
    455     for (unsigned n = 0; n < moveCount; ++n)
    456         m_resourceToRevalidate->addClientToSet(clientsToMove[n]);
    457     for (unsigned n = 0; n < moveCount; ++n) {
    458         // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore.
    459         if (m_resourceToRevalidate->m_clients.contains(clientsToMove[n]))
    460             m_resourceToRevalidate->didAddClient(clientsToMove[n]);
    461     }
    462 }
    463 
    464 void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse)
    465 {
    466     m_responseTimestamp = currentTime();
    467 
    468     DEFINE_STATIC_LOCAL(const AtomicString, contentHeaderPrefix, ("content-"));
    469     // RFC2616 10.3.5
    470     // Update cached headers from the 304 response
    471     const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields();
    472     HTTPHeaderMap::const_iterator end = newHeaders.end();
    473     for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) {
    474         // Don't allow 304 response to update content headers, these can't change but some servers send wrong values.
    475         if (it->first.startsWith(contentHeaderPrefix, false))
    476             continue;
    477         m_response.setHTTPHeaderField(it->first, it->second);
    478     }
    479 }
    480 
    481 void CachedResource::registerHandle(CachedResourceHandleBase* h)
    482 {
    483     ++m_handleCount;
    484     if (m_resourceToRevalidate)
    485         m_handlesToRevalidate.add(h);
    486 }
    487 
    488 void CachedResource::unregisterHandle(CachedResourceHandleBase* h)
    489 {
    490     ASSERT(m_handleCount > 0);
    491     --m_handleCount;
    492 
    493     if (m_resourceToRevalidate)
    494          m_handlesToRevalidate.remove(h);
    495 
    496     if (!m_handleCount)
    497         deleteIfPossible();
    498 }
    499 
    500 bool CachedResource::canUseCacheValidator() const
    501 {
    502     if (m_loading || errorOccurred())
    503         return false;
    504 
    505     if (m_response.cacheControlContainsNoStore())
    506         return false;
    507     return m_response.hasCacheValidatorFields();
    508 }
    509 
    510 bool CachedResource::mustRevalidateDueToCacheHeaders(CachePolicy cachePolicy) const
    511 {
    512     ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyCache || cachePolicy == CachePolicyVerify);
    513 
    514     if (cachePolicy == CachePolicyRevalidate)
    515         return true;
    516 
    517     if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) {
    518         LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this);
    519         return true;
    520     }
    521 
    522     if (cachePolicy == CachePolicyCache) {
    523         if (m_response.cacheControlContainsMustRevalidate() && isExpired()) {
    524             LOG(ResourceLoading, "CachedResource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this);
    525             return true;
    526         }
    527         return false;
    528     }
    529 
    530     // CachePolicyVerify
    531     if (isExpired()) {
    532         LOG(ResourceLoading, "CachedResource %p mustRevalidate because of isExpired()\n", this);
    533         return true;
    534     }
    535 
    536     return false;
    537 }
    538 
    539 bool CachedResource::isSafeToMakePurgeable() const
    540 {
    541     return !hasClients() && !m_proxyResource && !m_resourceToRevalidate;
    542 }
    543 
    544 bool CachedResource::makePurgeable(bool purgeable)
    545 {
    546     if (purgeable) {
    547         ASSERT(isSafeToMakePurgeable());
    548 
    549         if (m_purgeableData) {
    550             ASSERT(!m_data);
    551             return true;
    552         }
    553         if (!m_data)
    554             return false;
    555 
    556         // Should not make buffer purgeable if it has refs other than this since we don't want two copies.
    557         if (!m_data->hasOneRef())
    558             return false;
    559 
    560         if (m_data->hasPurgeableBuffer()) {
    561             m_purgeableData = m_data->releasePurgeableBuffer();
    562         } else {
    563             m_purgeableData = PurgeableBuffer::create(m_data->data(), m_data->size());
    564             if (!m_purgeableData)
    565                 return false;
    566             m_purgeableData->setPurgePriority(purgePriority());
    567         }
    568 
    569         m_purgeableData->makePurgeable(true);
    570         m_data.clear();
    571         return true;
    572     }
    573 
    574     if (!m_purgeableData)
    575         return true;
    576     ASSERT(!m_data);
    577     ASSERT(!hasClients());
    578 
    579     if (!m_purgeableData->makePurgeable(false))
    580         return false;
    581 
    582     m_data = SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release());
    583     return true;
    584 }
    585 
    586 bool CachedResource::isPurgeable() const
    587 {
    588     return m_purgeableData && m_purgeableData->isPurgeable();
    589 }
    590 
    591 bool CachedResource::wasPurged() const
    592 {
    593     return m_purgeableData && m_purgeableData->wasPurged();
    594 }
    595 
    596 unsigned CachedResource::overheadSize() const
    597 {
    598     return sizeof(CachedResource) + m_response.memoryUsage() + 576;
    599     /*
    600         576 = 192 +                   // average size of m_url
    601               384;                    // average size of m_clients hash map
    602     */
    603 }
    604 
    605 void CachedResource::setLoadPriority(ResourceLoadPriority loadPriority)
    606 {
    607     if (loadPriority == ResourceLoadPriorityUnresolved)
    608         return;
    609     m_loadPriority = loadPriority;
    610 }
    611 
    612 }
    613