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. }
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); } }
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; }
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); } }
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); }
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(); }
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); } }
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(); }
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); } }
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; }
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; }