void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize) { if (m_inPruneResources) return; TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); if (targetSize && m_deadSize <= targetSize) return; bool canShrinkLRULists = true; for (int i = m_allResources.size() - 1; i >= 0; i--) { LRUList& list = *m_allResources[i]; // First flush all the decoded data in this queue. // Remove from the head, since this is the least frequently accessed of the objects. auto it = list.begin(); while (it != list.end()) { CachedResource& current = **it; // Increment the iterator now as the call to destroyDecodedData() below may // invalidate the current iterator. ++it; // Protect 'next' so it can't get deleted during destroyDecodedData(). CachedResourceHandle<CachedResource> next = it != list.end() ? *it : nullptr; ASSERT(!next || next->inCache()); if (!current.hasClients() && !current.isPreloaded() && current.isLoaded()) { // Destroy our decoded data. This will remove us from // m_liveDecodedResources, and possibly move us to a different // LRU list in m_allResources. current.destroyDecodedData(); if (targetSize && m_deadSize <= targetSize) return; } // Decoded data may reference other resources. Stop iterating if 'next' somehow got // kicked out of cache during destroyDecodedData(). if (next && !next->inCache()) break; } // Now evict objects from this list. // Remove from the head, since this is the least frequently accessed of the objects. it = list.begin(); while (it != list.end()) { CachedResource& current = **it; // Increment the iterator now as the call to remove() below will // invalidate the current iterator. ++it; CachedResourceHandle<CachedResource> next = it != list.end() ? *it : nullptr; ASSERT(!next || next->inCache()); if (!current.hasClients() && !current.isPreloaded() && !current.isCacheValidator()) { remove(current); if (targetSize && m_deadSize <= targetSize) return; } if (next && !next->inCache()) break; } // Shrink the vector back down so we don't waste time inspecting // empty LRU lists on future prunes. if (!m_allResources[i]->isEmpty()) canShrinkLRULists = false; else if (canShrinkLRULists) m_allResources.shrink(i); } }
void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize) { if (m_inPruneResources) return; TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); int size = m_allResources.size(); // See if we have any purged resources we can evict. for (int i = 0; i < size; i++) { CachedResource* current = m_allResources[i].m_tail; while (current) { CachedResource* prev = current->m_prevInAllResourcesList; if (current->wasPurged()) { ASSERT(!current->hasClients()); ASSERT(!current->isPreloaded()); evict(current); } current = prev; } } if (targetSize && m_deadSize <= targetSize) return; bool canShrinkLRULists = true; for (int i = size - 1; i >= 0; i--) { // Remove from the tail, since this is the least frequently accessed of the objects. CachedResource* current = m_allResources[i].m_tail; // First flush all the decoded data in this queue. while (current) { // Protect 'previous' so it can't get deleted during destroyDecodedData(). CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList; ASSERT(!previous || previous->inCache()); if (!current->hasClients() && !current->isPreloaded() && current->isLoaded()) { // Destroy our decoded data. This will remove us from // m_liveDecodedResources, and possibly move us to a different // LRU list in m_allResources. current->destroyDecodedData(); if (targetSize && m_deadSize <= targetSize) return; } // Decoded data may reference other resources. Stop iterating if 'previous' somehow got // kicked out of cache during destroyDecodedData(). if (previous && !previous->inCache()) break; current = previous.get(); } // Now evict objects from this queue. current = m_allResources[i].m_tail; while (current) { CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList; ASSERT(!previous || previous->inCache()); if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) { if (!makeResourcePurgeable(current)) evict(current); if (targetSize && m_deadSize <= targetSize) return; } if (previous && !previous->inCache()) break; current = previous.get(); } // Shrink the vector back down so we don't waste time inspecting // empty LRU lists on future prunes. if (m_allResources[i].m_head) canShrinkLRULists = false; else if (canShrinkLRULists) m_allResources.resize(i); } }
CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request) { KURL url = request.resourceRequest().url(); LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload()); // If only the fragment identifiers differ, it is the same resource. url = MemoryCache::removeFragmentIdentifierIfNeeded(url); if (!url.isValid()) return 0; if (!canRequest(type, url, request.forPreload())) return 0; if (Frame* f = frame()) f->loader()->client()->dispatchWillRequestResource(&request); if (memoryCache()->disabled()) { DocumentResourceMap::iterator it = m_documentResources.find(url.string()); if (it != m_documentResources.end()) { it->value->setOwningCachedResourceLoader(0); m_documentResources.remove(it); } } // See if we can use an existing resource from the cache. CachedResourceHandle<CachedResource> resource; #if ENABLE(CACHE_PARTITIONING) if (document()) request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition()); #endif resource = memoryCache()->resourceForRequest(request.resourceRequest()); const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer()); switch (policy) { case Reload: memoryCache()->remove(resource.get()); // Fall through case Load: resource = loadResource(type, request, request.charset()); break; case Revalidate: resource = revalidateResource(request, resource.get()); break; case Use: memoryCache()->resourceAccessed(resource.get()); notifyLoadedFromMemoryCache(resource.get()); break; } if (!resource) return 0; if (!request.forPreload() || policy != Use) resource->setLoadPriority(request.priority()); if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) { resource->load(this, request.options()); // We don't support immediate loads, but we do support immediate failure. if (resource->errorOccurred()) { if (resource->inCache()) memoryCache()->remove(resource.get()); return 0; } } #if PLATFORM(CHROMIUM) // FIXME: Temporarily leave main resource caching disabled for chromium, see https://bugs.webkit.org/show_bug.cgi?id=107962 // Ensure main resources aren't preloaded, and other main resource loads are removed from cache to prevent reuse. if (type == CachedResource::MainResource) { ASSERT(policy != Use); ASSERT(policy != Revalidate); memoryCache()->remove(resource.get()); if (request.forPreload()) return 0; } #endif if (!request.resourceRequest().url().protocolIsData()) m_validatedURLs.add(request.resourceRequest().url()); ASSERT(resource->url() == url.string()); m_documentResources.set(resource->url(), resource); return resource; }