Beispiel #1
0
void CachedResource::setDecodedSize(unsigned size)
{
    if (size == m_decodedSize)
        return;

    int delta = size - m_decodedSize;

    // The object must now be moved to a different queue, since its size has been changed.
    // We have to remove explicitly before updating m_decodedSize, so that we find the correct previous
    // queue.
    if (inCache())
        memoryCache()->removeFromLRUList(this);
    
    m_decodedSize = size;
   
    if (inCache()) { 
        // Now insert into the new LRU list.
        memoryCache()->insertInLRUList(this);
        
        // Insert into or remove from the live decoded list if necessary.
        // When inserting into the LiveDecodedResourcesList it is possible
        // that the m_lastDecodedAccessTime is still zero or smaller than
        // the m_lastDecodedAccessTime of the current list head. This is a
        // violation of the invariant that the list is to be kept sorted
        // by access time. The weakening of the invariant does not pose
        // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209
        if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients())
            memoryCache()->insertInLiveDecodedResourcesList(this);
        else if (!m_decodedSize && m_inLiveDecodedResourcesList)
            memoryCache()->removeFromLiveDecodedResourcesList(this);

        // Update the cache's size totals.
        memoryCache()->adjustSize(hasClients(), delta);
    }
}
void Resource::removeClient(ResourceClient* client)
{
    ASSERT(hasClient(client));
    if (m_finishedClients.contains(client))
        m_finishedClients.remove(client);
    else if (m_clientsAwaitingCallback.contains(client))
        m_clientsAwaitingCallback.remove(client);
    else
        m_clients.remove(client);

    didRemoveClient(client);

    if (m_clientsAwaitingCallback.isEmpty())
        ResourceCallback::callbackHandler()->cancel(this);

    bool deleted = deleteIfPossible();
    if (!deleted && !hasClients()) {
        memoryCache()->makeDead(this);
        if (!m_switchingClientsToRevalidatedResource)
            allClientsRemoved();

        // RFC2616 14.9.2:
        // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
        // "... History buffers MAY store such responses as part of their normal operation."
        // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
        if (hasCacheControlNoStoreHeader() && url().protocolIs("https")) {
            memoryCache()->remove(this);
            memoryCache()->prune();
        } else {
            memoryCache()->prune(this);
        }
    }
    // This object may be dead here.
}
Beispiel #3
0
void CachedResource::setEncodedSize(unsigned size)
{
    if (size == m_encodedSize)
        return;

    // The size cannot ever shrink (unless it is being nulled out because of an error).  If it ever does, assert.
    ASSERT(size == 0 || size >= m_encodedSize);
    
    int delta = size - m_encodedSize;

    // The object must now be moved to a different queue, since its size has been changed.
    // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous
    // queue.
    if (inCache())
        memoryCache()->removeFromLRUList(this);
    
    m_encodedSize = size;
   
    if (inCache()) { 
        // Now insert into the new LRU list.
        memoryCache()->insertInLRUList(this);
        
        // Update the cache's size totals.
        memoryCache()->adjustSize(hasClients(), delta);
    }
}
Beispiel #4
0
void CachedResource::removeClient(CachedResourceClient* client)
{
    OwnPtr<CachedResourceCallback> callback = m_clientsAwaitingCallback.take(client);
    if (callback) {
        ASSERT(!m_clients.contains(client));
        callback->cancel();
        callback.clear();
    } else {
        ASSERT(m_clients.contains(client));
        m_clients.remove(client);
        didRemoveClient(client);
    }

    bool deleted = deleteIfPossible();
    if (!deleted && !hasClients()) {
        if (inCache()) {
            memoryCache()->removeFromLiveResourcesSize(this);
            memoryCache()->removeFromLiveDecodedResourcesList(this);
        }
        if (!m_switchingClientsToRevalidatedResource)
            allClientsRemoved();
        destroyDecodedDataIfNeeded();
        if (response().cacheControlContainsNoStore() && url().protocolIs("https")) {
            // RFC2616 14.9.2:
            // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
            // "... History buffers MAY store such responses as part of their normal operation."
            // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
            memoryCache()->remove(this);
        }
        memoryCache()->prune();
    }
    // This object may be dead here.
}
void Resource::addClient(ResourceClient* client)
{
    ASSERT(!isPurgeable());

    if (m_preloadResult == PreloadNotReferenced) {
        if (isLoaded())
            m_preloadResult = PreloadReferencedWhileComplete;
        else if (m_requestedFromNetworkingLayer)
            m_preloadResult = PreloadReferencedWhileLoading;
        else
            m_preloadResult = PreloadReferenced;
    }
    if (!hasClients())
        memoryCache()->makeLive(this);

    if (!m_revalidatingRequest.isNull()) {
        m_clients.add(client);
        return;
    }

    // If we have existing data to send to the new client and the resource type supprts it, send it asynchronously.
    if (!m_response.isNull() && !shouldSendCachedDataSynchronouslyForType(type()) && !m_needsSynchronousCacheHit) {
        m_clientsAwaitingCallback.add(client);
        ResourceCallback::callbackHandler()->schedule(this);
        return;
    }

    m_clients.add(client);
    didAddClient(client);
    return;
}
Beispiel #6
0
bool CachedResource::addClientToSet(CachedResourceClient* client)
{
    ASSERT(!isPurgeable());

    if (m_preloadResult == PreloadNotReferenced) {
        if (isLoaded())
            m_preloadResult = PreloadReferencedWhileComplete;
        else if (m_requestedFromNetworkingLayer)
            m_preloadResult = PreloadReferencedWhileLoading;
        else
            m_preloadResult = PreloadReferenced;
    }
    if (!hasClients() && inCache())
        memoryCache()->addToLiveResourcesSize(this);

    if ((m_type == RawResource || m_type == MainResource) && !m_response.isNull() && !m_proxyResource) {
        // Certain resources (especially XHRs and main resources) do crazy things if an asynchronous load returns
        // synchronously (e.g., scripts may not have set all the state they need to handle the load).
        // Therefore, rather than immediately sending callbacks on a cache hit like other CachedResources,
        // we schedule the callbacks and ensure we never finish synchronously.
        ASSERT(!m_clientsAwaitingCallback.contains(client));
        m_clientsAwaitingCallback.add(client, CachedResourceCallback::schedule(this, client));
        return false;
    }

    m_clients.add(client);
    return true;
}
void ImageResource::destroyDecodedDataIfPossible()
{
    if (!hasClients() && !isLoading() && (!m_image || (m_image->hasOneRef() && m_image->isBitmapImage()))) {
        m_image = nullptr;
        setDecodedSize(0);
    } else if (m_image && !errorOccurred()) {
        m_image->destroyDecodedData(true);
    }
}
Beispiel #8
0
void CachedImage::destroyDecodedData()
{
    bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
    if (canDeleteImage && !isLoading() && !hasClients()) {
        m_image = nullptr;
        setDecodedSize(0);
    } else if (m_image && !errorOccurred())
        m_image->destroyDecodedData();
}
void Resource::cancelTimerFired(Timer<Resource>* timer)
{
    ASSERT_UNUSED(timer, timer == &m_cancelTimer);
    if (hasClients() || !m_loader)
        return;
    ResourcePtr<Resource> protect(this);
    m_loader->cancelIfNotFinishing();
    if (m_status != Cached)
        memoryCache()->remove(this);
}
Beispiel #10
0
bool CachedResource::isSafeToMakePurgeable() const
{ 
#if ENABLE(DISK_IMAGE_CACHE)
    // It does not make sense to have a resource in the disk image cache
    // (memory mapped on disk) and purgeable (in memory). So do not allow
    // disk image cached resources to be purgeable.
    if (isUsingDiskImageCache())
        return false;
#endif

    return !hasClients() && !m_proxyResource && !m_resourceToRevalidate;
}
bool Resource::unlock()
{
    if (!m_data)
        return false;

    if (!m_data->isLocked())
        return true;

    if (!memoryCache()->contains(this) || hasClients() || m_handleCount > 1 || !m_revalidatingRequest.isNull() || !m_loadFinishTime || !isSafeToUnlock())
        return false;

    m_data->unlock();
    return true;
}
String Resource::reasonNotDeletable() const
{
    StringBuilder builder;
    if (hasClients()) {
        builder.append("hasClients(");
        builder.appendNumber(m_clients.size());
        if (!m_clientsAwaitingCallback.isEmpty()) {
            builder.append(", AwaitingCallback=");
            builder.appendNumber(m_clientsAwaitingCallback.size());
        }
        if (!m_finishedClients.isEmpty()) {
            builder.append(", Finished=");
            builder.appendNumber(m_finishedClients.size());
        }
        builder.append(")");
    }
    if (m_loader) {
        if (!builder.isEmpty())
            builder.append(' ');
        builder.append("m_loader");
    }
    if (m_preloadCount) {
        if (!builder.isEmpty())
            builder.append(' ');
        builder.append("m_preloadCount(");
        builder.appendNumber(m_preloadCount);
        builder.append(")");
    }
    if (!hasRightHandleCountApartFromCache(0)) {
        if (!builder.isEmpty())
            builder.append(' ');
        builder.append("m_handleCount(");
        builder.appendNumber(m_handleCount);
        builder.append(")");
    }
    if (m_protectorCount) {
        if (!builder.isEmpty())
            builder.append(' ');
        builder.append("m_protectorCount(");
        builder.appendNumber(m_protectorCount);
        builder.append(")");
    }
    if (memoryCache()->contains(this)) {
        if (!builder.isEmpty())
            builder.append(' ');
        builder.append("in_memory_cache");
    }
    return builder.toString();
}
Beispiel #13
0
void CachedResource::setDecodedSize(unsigned size)
{
    if (size == m_decodedSize)
        return;

    long long delta = static_cast<long long>(size) - m_decodedSize;

    // The object must be moved to a different queue, since its size has been changed.
    // Remove before updating m_decodedSize, so we find the resource in the correct LRU list.
    if (allowsCaching() && inCache())
        MemoryCache::singleton().removeFromLRUList(*this);

    m_decodedSize = size;
   
    if (allowsCaching() && inCache()) {
        auto& memoryCache = MemoryCache::singleton();
        // Now insert into the new LRU list.
        memoryCache.insertInLRUList(*this);
        
        // Insert into or remove from the live decoded list if necessary.
        // When inserting into the LiveDecodedResourcesList it is possible
        // that the m_lastDecodedAccessTime is still zero or smaller than
        // the m_lastDecodedAccessTime of the current list head. This is a
        // violation of the invariant that the list is to be kept sorted
        // by access time. The weakening of the invariant does not pose
        // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209
        bool inLiveDecodedResourcesList = memoryCache.inLiveDecodedResourcesList(*this);
        if (m_decodedSize && !inLiveDecodedResourcesList && hasClients())
            memoryCache.insertInLiveDecodedResourcesList(*this);
        else if (!m_decodedSize && inLiveDecodedResourcesList)
            memoryCache.removeFromLiveDecodedResourcesList(*this);

        // Update the cache's size totals.
        memoryCache.adjustSize(hasClients(), delta);
    }
}
bool Resource::lock()
{
    if (!m_data)
        return true;
    if (m_data->isLocked())
        return true;

    ASSERT(!hasClients());

    if (!m_data->lock()) {
        m_wasPurged = true;
        return false;
    }
    return true;
}
void Resource::unregisterHandle(ResourcePtrBase* h)
{
    assertAlive();
    ASSERT(m_handleCount > 0);
    --m_handleCount;

    if (!m_handleCount) {
        if (deleteIfPossible())
            return;
        unlock();
    } else if (m_handleCount == 1 && memoryCache()->contains(this)) {
        unlock();
        if (!hasClients())
            memoryCache()->prune(this);
    }
}
Beispiel #16
0
void CachedResource::removeClient(CachedResourceClient& client)
{
    auto callback = m_clientsAwaitingCallback.take(&client);
    if (callback) {
        ASSERT(!m_clients.contains(&client));
        callback->cancel();
        callback = nullptr;
    } else {
        ASSERT(m_clients.contains(&client));
        m_clients.remove(&client);
        didRemoveClient(client);
    }

    if (deleteIfPossible()) {
        // `this` object is dead here.
        return;
    }

    if (hasClients())
        return;

    auto& memoryCache = MemoryCache::singleton();
    if (allowsCaching() && inCache()) {
        memoryCache.removeFromLiveResourcesSize(*this);
        memoryCache.removeFromLiveDecodedResourcesList(*this);
    }
    if (!m_switchingClientsToRevalidatedResource)
        allClientsRemoved();
    destroyDecodedDataIfNeeded();

    if (!allowsCaching())
        return;

    if (response().cacheControlContainsNoStore() && url().protocolIs("https")) {
        // RFC2616 14.9.2:
        // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
        // "... History buffers MAY store such responses as part of their normal operation."
        // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
        memoryCache.remove(*this);
    }
    memoryCache.pruneSoon();
}
Beispiel #17
0
void CachedResource::setEncodedSize(unsigned size)
{
    if (size == m_encodedSize)
        return;

    long long delta = static_cast<long long>(size) - m_encodedSize;

    // The object must be moved to a different queue, since its size has been changed.
    // Remove before updating m_encodedSize, so we find the resource in the correct LRU list.
    if (allowsCaching() && inCache())
        MemoryCache::singleton().removeFromLRUList(*this);

    m_encodedSize = size;

    if (allowsCaching() && inCache()) {
        auto& memoryCache = MemoryCache::singleton();
        memoryCache.insertInLRUList(*this);
        memoryCache.adjustSize(hasClients(), delta);
    }
}
Beispiel #18
0
bool CachedResource::makePurgeable(bool purgeable) 
{ 
    if (purgeable) {
        ASSERT(isSafeToMakePurgeable());

        if (m_purgeableData) {
            ASSERT(!m_data);
            return true;
        }
        if (!m_data)
            return false;
        
        // Should not make buffer purgeable if it has refs other than this since we don't want two copies.
        if (!m_data->hasOneRef())
            return false;

        m_data->createPurgeableBuffer();
        if (!m_data->hasPurgeableBuffer())
            return false;

        m_purgeableData = m_data->releasePurgeableBuffer();
        m_purgeableData->setPurgePriority(purgePriority());
        m_purgeableData->makePurgeable(true);
        m_data.clear();
        return true;
    }

    if (!m_purgeableData)
        return true;
    ASSERT(!m_data);
    ASSERT(!hasClients());

    if (!m_purgeableData->makePurgeable(false))
        return false; 

    m_data = ResourceBuffer::adoptSharedBuffer(SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release()));
    return true;
}
Beispiel #19
0
void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*)
{
    ASSERT(!hasClients());
    destroyDecodedData();
}
bool CachedResource::isSafeToMakePurgeable() const
{ 
    return !hasClients() && !m_proxyResource && !m_resourceToRevalidate;
}
bool Resource::canDelete() const
{
    return !hasClients() && !m_loader && !m_preloadCount && hasRightHandleCountApartFromCache(0)
           && !m_protectorCount;
}