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