[WTF] Move currentCPUTime and sleep(Seconds) to CPUTime.h and Seconds.h respectively
[WebKit-https.git] / Source / WebCore / loader / cache / MemoryCache.cpp
1 /*
2     Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3     Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4     Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5     Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Library General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Library General Public License for more details.
16
17     You should have received a copy of the GNU Library General Public License
18     along with this library; see the file COPYING.LIB.  If not, write to
19     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20     Boston, MA 02110-1301, USA.
21 */
22
23 #include "config.h"
24 #include "MemoryCache.h"
25
26 #include "BitmapImage.h"
27 #include "CachedImage.h"
28 #include "CachedImageClient.h"
29 #include "CachedResource.h"
30 #include "CachedResourceHandle.h"
31 #include "Document.h"
32 #include "FrameLoader.h"
33 #include "FrameLoaderTypes.h"
34 #include "FrameView.h"
35 #include "Image.h"
36 #include "Logging.h"
37 #include "PublicSuffix.h"
38 #include "SharedBuffer.h"
39 #include "WorkerGlobalScope.h"
40 #include "WorkerLoaderProxy.h"
41 #include "WorkerThread.h"
42 #include <stdio.h>
43 #include <wtf/MathExtras.h>
44 #include <wtf/NeverDestroyed.h>
45 #include <wtf/SetForScope.h>
46 #include <wtf/text/CString.h>
47
48 namespace WebCore {
49
50 static const int cDefaultCacheCapacity = 8192 * 1024;
51 static const Seconds cMinDelayBeforeLiveDecodedPrune { 1_s };
52 static const float cTargetPrunePercentage = .95f; // Percentage of capacity toward which we prune, to avoid immediately pruning again.
53
54 MemoryCache& MemoryCache::singleton()
55 {
56     ASSERT(WTF::isMainThread());
57     static NeverDestroyed<MemoryCache> memoryCache;
58     return memoryCache;
59 }
60
61 MemoryCache::MemoryCache()
62     : m_disabled(false)
63     , m_inPruneResources(false)
64     , m_capacity(cDefaultCacheCapacity)
65     , m_minDeadCapacity(0)
66     , m_maxDeadCapacity(cDefaultCacheCapacity)
67     , m_liveSize(0)
68     , m_deadSize(0)
69     , m_pruneTimer(*this, &MemoryCache::prune)
70 {
71     static_assert(sizeof(long long) > sizeof(unsigned), "Numerical overflow can happen when adjusting the size of the cached memory.");
72 }
73
74 auto MemoryCache::sessionResourceMap(PAL::SessionID sessionID) const -> CachedResourceMap*
75 {
76     ASSERT(sessionID.isValid());
77     return m_sessionResources.get(sessionID);
78 }
79
80 auto MemoryCache::ensureSessionResourceMap(PAL::SessionID sessionID) -> CachedResourceMap&
81 {
82     ASSERT(sessionID.isValid());
83     auto& map = m_sessionResources.add(sessionID, nullptr).iterator->value;
84     if (!map)
85         map = std::make_unique<CachedResourceMap>();
86     return *map;
87 }
88
89 bool MemoryCache::shouldRemoveFragmentIdentifier(const URL& originalURL)
90 {
91     if (!originalURL.hasFragmentIdentifier())
92         return false;
93     // Strip away fragment identifier from HTTP URLs.
94     // Data URLs must be unmodified. For file and custom URLs clients may expect resources
95     // to be unique even when they differ by the fragment identifier only.
96     return originalURL.protocolIsInHTTPFamily();
97 }
98
99 URL MemoryCache::removeFragmentIdentifierIfNeeded(const URL& originalURL)
100 {
101     if (!shouldRemoveFragmentIdentifier(originalURL))
102         return originalURL;
103     URL url = originalURL;
104     url.removeFragmentIdentifier();
105     return url;
106 }
107
108 bool MemoryCache::add(CachedResource& resource)
109 {
110     if (disabled())
111         return false;
112
113     ASSERT(WTF::isMainThread());
114
115     auto key = std::make_pair(resource.url(), resource.cachePartition());
116
117     ensureSessionResourceMap(resource.sessionID()).set(key, &resource);
118     resource.setInCache(true);
119     
120     resourceAccessed(resource);
121     
122     LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource.url().string().latin1().data(), &resource);
123     return true;
124 }
125
126 void MemoryCache::revalidationSucceeded(CachedResource& revalidatingResource, const ResourceResponse& response)
127 {
128     ASSERT(response.source() == ResourceResponse::Source::MemoryCacheAfterValidation);
129     ASSERT(revalidatingResource.resourceToRevalidate());
130     CachedResource& resource = *revalidatingResource.resourceToRevalidate();
131     ASSERT(!resource.inCache());
132     ASSERT(resource.isLoaded());
133
134     // Calling remove() can potentially delete revalidatingResource, which we use
135     // below. This mustn't be the case since revalidation means it is loaded
136     // and so canDelete() is false.
137     ASSERT(!revalidatingResource.canDelete());
138
139     remove(revalidatingResource);
140
141     auto& resources = ensureSessionResourceMap(resource.sessionID());
142     auto key = std::make_pair(resource.url(), resource.cachePartition());
143
144     ASSERT(!resources.get(key));
145     resources.set(key, &resource);
146     resource.setInCache(true);
147     resource.updateResponseAfterRevalidation(response);
148     insertInLRUList(resource);
149     long long delta = resource.size();
150     if (resource.decodedSize() && resource.hasClients())
151         insertInLiveDecodedResourcesList(resource);
152     if (delta)
153         adjustSize(resource.hasClients(), delta);
154
155     revalidatingResource.switchClientsToRevalidatedResource();
156     ASSERT(!revalidatingResource.m_deleted);
157     // this deletes the revalidating resource
158     revalidatingResource.clearResourceToRevalidate();
159 }
160
161 void MemoryCache::revalidationFailed(CachedResource& revalidatingResource)
162 {
163     ASSERT(WTF::isMainThread());
164     LOG(ResourceLoading, "Revalidation failed for %p", &revalidatingResource);
165     ASSERT(revalidatingResource.resourceToRevalidate());
166     revalidatingResource.clearResourceToRevalidate();
167 }
168
169 CachedResource* MemoryCache::resourceForRequest(const ResourceRequest& request, PAL::SessionID sessionID)
170 {
171     // FIXME: Change all clients to make sure HTTP(s) URLs have no fragment identifiers before calling here.
172     // CachedResourceLoader is now doing this. Add an assertion once all other clients are doing it too.
173     auto* resources = sessionResourceMap(sessionID);
174     if (!resources)
175         return nullptr;
176     return resourceForRequestImpl(request, *resources);
177 }
178
179 CachedResource* MemoryCache::resourceForRequestImpl(const ResourceRequest& request, CachedResourceMap& resources)
180 {
181     ASSERT(WTF::isMainThread());
182     URL url = removeFragmentIdentifierIfNeeded(request.url());
183
184     auto key = std::make_pair(url, request.cachePartition());
185     return resources.get(key);
186 }
187
188 unsigned MemoryCache::deadCapacity() const 
189 {
190     // Dead resource capacity is whatever space is not occupied by live resources, bounded by an independent minimum and maximum.
191     unsigned capacity = m_capacity - std::min(m_liveSize, m_capacity); // Start with available capacity.
192     capacity = std::max(capacity, m_minDeadCapacity); // Make sure it's above the minimum.
193     capacity = std::min(capacity, m_maxDeadCapacity); // Make sure it's below the maximum.
194     return capacity;
195 }
196
197 unsigned MemoryCache::liveCapacity() const 
198
199     // Live resource capacity is whatever is left over after calculating dead resource capacity.
200     return m_capacity - deadCapacity();
201 }
202
203 static CachedImageClient& dummyCachedImageClient()
204 {
205     static NeverDestroyed<CachedImageClient> client;
206     return client;
207 }
208
209 bool MemoryCache::addImageToCache(NativeImagePtr&& image, const URL& url, const String& domainForCachePartition)
210 {
211     ASSERT(image);
212     PAL::SessionID sessionID = PAL::SessionID::defaultSessionID();
213     removeImageFromCache(url, domainForCachePartition); // Remove cache entry if it already exists.
214
215     RefPtr<BitmapImage> bitmapImage = BitmapImage::create(WTFMove(image), nullptr);
216     if (!bitmapImage)
217         return false;
218
219     auto cachedImage = std::make_unique<CachedImage>(url, bitmapImage.get(), sessionID, domainForCachePartition);
220
221     cachedImage->addClient(dummyCachedImageClient());
222     cachedImage->setDecodedSize(bitmapImage->decodedSize());
223
224     return add(*cachedImage.release());
225 }
226
227 void MemoryCache::removeImageFromCache(const URL& url, const String& domainForCachePartition)
228 {
229     auto* resources = sessionResourceMap(PAL::SessionID::defaultSessionID());
230     if (!resources)
231         return;
232
233     auto key = std::make_pair(url, ResourceRequest::partitionName(domainForCachePartition));
234
235     CachedResource* resource = resources->get(key);
236     if (!resource)
237         return;
238
239     // A resource exists and is not a manually cached image, so just remove it.
240     if (!is<CachedImage>(*resource) || !downcast<CachedImage>(*resource).isManuallyCached()) {
241         remove(*resource);
242         return;
243     }
244
245     // Removing the last client of a CachedImage turns the resource
246     // into a dead resource which will eventually be evicted when
247     // dead resources are pruned. That might be immediately since
248     // removing the last client triggers a MemoryCache::prune, so the
249     // resource may be deleted after this call.
250     downcast<CachedImage>(*resource).removeClient(dummyCachedImageClient());
251 }
252
253 void MemoryCache::pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveResources)
254 {
255     unsigned capacity = shouldDestroyDecodedDataForAllLiveResources ? 0 : liveCapacity();
256     if (capacity && m_liveSize <= capacity)
257         return;
258
259     unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again.
260
261     pruneLiveResourcesToSize(targetSize, shouldDestroyDecodedDataForAllLiveResources);
262 }
263
264 void MemoryCache::forEachResource(const WTF::Function<void(CachedResource&)>& function)
265 {
266     for (auto& unprotectedLRUList : m_allResources) {
267         for (auto& resource : copyToVector(*unprotectedLRUList))
268             function(*resource);
269     }
270 }
271
272 void MemoryCache::forEachSessionResource(PAL::SessionID sessionID, const WTF::Function<void (CachedResource&)>& function)
273 {
274     auto it = m_sessionResources.find(sessionID);
275     if (it == m_sessionResources.end())
276         return;
277
278     for (auto& resource : copyToVector(it->value->values()))
279         function(*resource);
280 }
281
282 void MemoryCache::destroyDecodedDataForAllImages()
283 {
284     MemoryCache::singleton().forEachResource([](CachedResource& resource) {
285         if (!resource.isImage())
286             return;
287
288         if (auto image = downcast<CachedImage>(resource).image())
289             image->destroyDecodedData();
290     });
291 }
292
293 void MemoryCache::pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources)
294 {
295     if (m_inPruneResources)
296         return;
297     SetForScope<bool> reentrancyProtector(m_inPruneResources, true);
298
299     MonotonicTime currentTime = FrameView::currentPaintTimeStamp();
300     if (!currentTime) // In case prune is called directly, outside of a Frame paint.
301         currentTime = MonotonicTime::now();
302     
303     // Destroy any decoded data in live objects that we can.
304     // Start from the head, since this is the least recently accessed of the objects.
305
306     // The list might not be sorted by the m_lastDecodedAccessTime. The impact
307     // of this weaker invariant is minor as the below if statement to check the
308     // elapsedTime will evaluate to false as the currentTime will be a lot
309     // greater than the current->m_lastDecodedAccessTime.
310     // For more details see: https://bugs.webkit.org/show_bug.cgi?id=30209
311     auto it = m_liveDecodedResources.begin();
312     while (it != m_liveDecodedResources.end()) {
313         auto* current = *it;
314
315         // Increment the iterator now because the call to destroyDecodedData() below
316         // may cause a call to ListHashSet::remove() and invalidate the current
317         // iterator. Note that this is safe because unlike iteration of most
318         // WTF Hash data structures, iteration is guaranteed safe against mutation
319         // of the ListHashSet, except for removal of the item currently pointed to
320         // by a given iterator.
321         ++it;
322
323         ASSERT(current->hasClients());
324         if (current->isLoaded() && current->decodedSize()) {
325             // Check to see if the remaining resources are too new to prune.
326             Seconds elapsedTime = currentTime - current->m_lastDecodedAccessTime;
327             if (!shouldDestroyDecodedDataForAllLiveResources && elapsedTime < cMinDelayBeforeLiveDecodedPrune)
328                 return;
329
330             // Destroy our decoded data. This will remove us from m_liveDecodedResources, and possibly move us
331             // to a different LRU list in m_allResources.
332             current->destroyDecodedData();
333
334             if (targetSize && m_liveSize <= targetSize)
335                 return;
336         }
337     }
338 }
339
340 void MemoryCache::pruneDeadResources()
341 {
342     unsigned capacity = deadCapacity();
343     if (capacity && m_deadSize <= capacity)
344         return;
345
346     unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again.
347     pruneDeadResourcesToSize(targetSize);
348 }
349
350 void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize)
351 {
352     if (m_inPruneResources)
353         return;
354     SetForScope<bool> reentrancyProtector(m_inPruneResources, true);
355  
356     if (targetSize && m_deadSize <= targetSize)
357         return;
358
359     bool canShrinkLRULists = true;
360     for (int i = m_allResources.size() - 1; i >= 0; i--) {
361         // Make a copy of the LRUList first (and ref the resources) as calling
362         // destroyDecodedData() can alter the LRUList.
363         auto lruList = copyToVector(*m_allResources[i]);
364
365         // First flush all the decoded data in this queue.
366         // Remove from the head, since this is the least frequently accessed of the objects.
367         for (auto& resource : lruList) {
368             if (!resource->inCache())
369                 continue;
370
371             if (!resource->hasClients() && !resource->isPreloaded() && resource->isLoaded()) {
372                 // Destroy our decoded data. This will remove us from 
373                 // m_liveDecodedResources, and possibly move us to a different 
374                 // LRU list in m_allResources.
375                 resource->destroyDecodedData();
376
377                 if (targetSize && m_deadSize <= targetSize)
378                     return;
379             }
380         }
381
382         // Now evict objects from this list.
383         // Remove from the head, since this is the least frequently accessed of the objects.
384         for (auto& resource : lruList) {
385             if (!resource->inCache())
386                 continue;
387
388             if (!resource->hasClients() && !resource->isPreloaded() && !resource->isCacheValidator()) {
389                 remove(*resource);
390                 if (targetSize && m_deadSize <= targetSize)
391                     return;
392             }
393         }
394             
395         // Shrink the vector back down so we don't waste time inspecting
396         // empty LRU lists on future prunes.
397         if (!m_allResources[i]->isEmpty())
398             canShrinkLRULists = false;
399         else if (canShrinkLRULists)
400             m_allResources.shrink(i);
401     }
402 }
403
404 void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes)
405 {
406     ASSERT(minDeadBytes <= maxDeadBytes);
407     ASSERT(maxDeadBytes <= totalBytes);
408     m_minDeadCapacity = minDeadBytes;
409     m_maxDeadCapacity = maxDeadBytes;
410     m_capacity = totalBytes;
411     prune();
412 }
413
414 void MemoryCache::remove(CachedResource& resource)
415 {
416     ASSERT(WTF::isMainThread());
417     LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", &resource, resource.url().string().latin1().data());
418     // The resource may have already been removed by someone other than our caller,
419     // who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>.
420     if (auto* resources = sessionResourceMap(resource.sessionID())) {
421         auto key = std::make_pair(resource.url(), resource.cachePartition());
422
423         if (resource.inCache()) {
424             // Remove resource from the resource map.
425             resources->remove(key);
426             resource.setInCache(false);
427
428             // If the resource map is now empty, remove it from m_sessionResources.
429             if (resources->isEmpty())
430                 m_sessionResources.remove(resource.sessionID());
431
432             // Remove from the appropriate LRU list.
433             removeFromLRUList(resource);
434             removeFromLiveDecodedResourcesList(resource);
435             adjustSize(resource.hasClients(), -static_cast<long long>(resource.size()));
436         } else
437             ASSERT(resources->get(key) != &resource);
438     }
439
440     resource.deleteIfPossible();
441 }
442
443 auto MemoryCache::lruListFor(CachedResource& resource) -> LRUList&
444 {
445     unsigned accessCount = std::max(resource.accessCount(), 1U);
446     unsigned queueIndex = WTF::fastLog2(resource.size() / accessCount);
447 #ifndef NDEBUG
448     resource.m_lruIndex = queueIndex;
449 #endif
450
451     m_allResources.reserveCapacity(queueIndex + 1);
452     while (m_allResources.size() <= queueIndex)
453         m_allResources.uncheckedAppend(std::make_unique<LRUList>());
454     return *m_allResources[queueIndex];
455 }
456
457 void MemoryCache::removeFromLRUList(CachedResource& resource)
458 {
459     // If we've never been accessed, then we're brand new and not in any list.
460     if (!resource.accessCount())
461         return;
462
463 #if !ASSERT_DISABLED
464     unsigned oldListIndex = resource.m_lruIndex;
465 #endif
466
467     LRUList& list = lruListFor(resource);
468
469     // Verify that the list we got is the list we want.
470     ASSERT(resource.m_lruIndex == oldListIndex);
471
472     bool removed = list.remove(&resource);
473     ASSERT_UNUSED(removed, removed);
474 }
475
476 void MemoryCache::insertInLRUList(CachedResource& resource)
477 {
478     ASSERT(resource.inCache());
479     ASSERT(resource.accessCount() > 0);
480     
481     auto addResult = lruListFor(resource).add(&resource);
482     ASSERT_UNUSED(addResult, addResult.isNewEntry);
483 }
484
485 void MemoryCache::resourceAccessed(CachedResource& resource)
486 {
487     ASSERT(resource.inCache());
488     
489     // Need to make sure to remove before we increase the access count, since
490     // the queue will possibly change.
491     removeFromLRUList(resource);
492     
493     // If this is the first time the resource has been accessed, adjust the size of the cache to account for its initial size.
494     if (!resource.accessCount())
495         adjustSize(resource.hasClients(), resource.size());
496     
497     // Add to our access count.
498     resource.increaseAccessCount();
499     
500     // Now insert into the new queue.
501     insertInLRUList(resource);
502 }
503
504 void MemoryCache::removeResourcesWithOrigin(SecurityOrigin& origin)
505 {
506     String originPartition = ResourceRequest::partitionName(origin.host());
507
508     Vector<CachedResource*> resourcesWithOrigin;
509     for (auto& resources : m_sessionResources.values()) {
510         for (auto& keyValue : *resources) {
511             auto& resource = *keyValue.value;
512             auto& partitionName = keyValue.key.second;
513             if (partitionName == originPartition) {
514                 resourcesWithOrigin.append(&resource);
515                 continue;
516             }
517             RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::create(resource.url());
518             if (resourceOrigin->equal(&origin))
519                 resourcesWithOrigin.append(&resource);
520         }
521     }
522
523     for (auto* resource : resourcesWithOrigin)
524         remove(*resource);
525 }
526
527 void MemoryCache::removeResourcesWithOrigins(PAL::SessionID sessionID, const HashSet<RefPtr<SecurityOrigin>>& origins)
528 {
529     auto* resourceMap = sessionResourceMap(sessionID);
530     if (!resourceMap)
531         return;
532
533     HashSet<String> originPartitions;
534
535     for (auto& origin : origins)
536         originPartitions.add(ResourceRequest::partitionName(origin->host()));
537
538     Vector<CachedResource*> resourcesToRemove;
539     for (auto& keyValuePair : *resourceMap) {
540         auto& resource = *keyValuePair.value;
541         auto& partitionName = keyValuePair.key.second;
542         if (originPartitions.contains(partitionName)) {
543             resourcesToRemove.append(&resource);
544             continue;
545         }
546         if (origins.contains(SecurityOrigin::create(resource.url()).ptr()))
547             resourcesToRemove.append(&resource);
548     }
549
550     for (auto& resource : resourcesToRemove)
551         remove(*resource);
552 }
553
554 void MemoryCache::getOriginsWithCache(SecurityOriginSet& origins)
555 {
556     for (auto& resources : m_sessionResources.values()) {
557         for (auto& keyValue : *resources) {
558             auto& resource = *keyValue.value;
559             auto& partitionName = keyValue.key.second;
560             if (!partitionName.isEmpty())
561                 origins.add(SecurityOrigin::create(ASCIILiteral("http"), partitionName, 0));
562             else
563                 origins.add(SecurityOrigin::create(resource.url()));
564         }
565     }
566 }
567
568 HashSet<RefPtr<SecurityOrigin>> MemoryCache::originsWithCache(PAL::SessionID sessionID) const
569 {
570     HashSet<RefPtr<SecurityOrigin>> origins;
571
572     auto it = m_sessionResources.find(sessionID);
573     if (it != m_sessionResources.end()) {
574         for (auto& keyValue : *it->value) {
575             auto& resource = *keyValue.value;
576             auto& partitionName = keyValue.key.second;
577             if (!partitionName.isEmpty())
578                 origins.add(SecurityOrigin::create("http", partitionName, 0));
579             else
580                 origins.add(SecurityOrigin::create(resource.url()));
581         }
582     }
583
584     return origins;
585 }
586
587 void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource& resource)
588 {
589     m_liveDecodedResources.remove(&resource);
590 }
591
592 void MemoryCache::insertInLiveDecodedResourcesList(CachedResource& resource)
593 {
594     // Make sure we aren't in the list already.
595     ASSERT(!m_liveDecodedResources.contains(&resource));
596     m_liveDecodedResources.add(&resource);
597 }
598
599 void MemoryCache::addToLiveResourcesSize(CachedResource& resource)
600 {
601     m_liveSize += resource.size();
602     m_deadSize -= resource.size();
603 }
604
605 void MemoryCache::removeFromLiveResourcesSize(CachedResource& resource)
606 {
607     m_liveSize -= resource.size();
608     m_deadSize += resource.size();
609 }
610
611 void MemoryCache::adjustSize(bool live, long long delta)
612 {
613     if (live) {
614         ASSERT(delta >= 0 || (static_cast<long long>(m_liveSize) + delta >= 0));
615         m_liveSize += delta;
616     } else {
617         ASSERT(delta >= 0 || (static_cast<long long>(m_deadSize) + delta >= 0));
618         m_deadSize += delta;
619     }
620 }
621
622 void MemoryCache::removeRequestFromSessionCaches(ScriptExecutionContext& context, const ResourceRequest& request)
623 {
624     if (is<WorkerGlobalScope>(context)) {
625         downcast<WorkerGlobalScope>(context).thread().workerLoaderProxy().postTaskToLoader([request = request.isolatedCopy()] (ScriptExecutionContext& context) {
626             MemoryCache::removeRequestFromSessionCaches(context, request);
627         });
628         return;
629     }
630
631     auto& memoryCache = MemoryCache::singleton();
632     for (auto& resources : memoryCache.m_sessionResources) {
633         if (CachedResource* resource = memoryCache.resourceForRequestImpl(request, *resources.value))
634             memoryCache.remove(*resource);
635     }
636 }
637
638 void MemoryCache::TypeStatistic::addResource(CachedResource& resource)
639 {
640     count++;
641     size += resource.size();
642     liveSize += resource.hasClients() ? resource.size() : 0;
643     decodedSize += resource.decodedSize();
644 }
645
646 MemoryCache::Statistics MemoryCache::getStatistics()
647 {
648     Statistics stats;
649
650     for (auto& resources : m_sessionResources.values()) {
651         for (auto* resource : resources->values()) {
652             switch (resource->type()) {
653             case CachedResource::ImageResource:
654                 stats.images.addResource(*resource);
655                 break;
656             case CachedResource::CSSStyleSheet:
657                 stats.cssStyleSheets.addResource(*resource);
658                 break;
659             case CachedResource::Script:
660                 stats.scripts.addResource(*resource);
661                 break;
662 #if ENABLE(XSLT)
663             case CachedResource::XSLStyleSheet:
664                 stats.xslStyleSheets.addResource(*resource);
665                 break;
666 #endif
667 #if ENABLE(SVG_FONTS)
668             case CachedResource::SVGFontResource:
669 #endif
670             case CachedResource::FontResource:
671                 stats.fonts.addResource(*resource);
672                 break;
673             default:
674                 break;
675             }
676         }
677     }
678     return stats;
679 }
680
681 void MemoryCache::setDisabled(bool disabled)
682 {
683     m_disabled = disabled;
684     if (!m_disabled)
685         return;
686
687     while (!m_sessionResources.isEmpty()) {
688         auto& resources = *m_sessionResources.begin()->value;
689         ASSERT(!resources.isEmpty());
690         remove(*resources.begin()->value);
691     }
692 }
693
694 void MemoryCache::evictResources()
695 {
696     if (disabled())
697         return;
698
699     setDisabled(true);
700     setDisabled(false);
701 }
702
703 void MemoryCache::evictResources(PAL::SessionID sessionID)
704 {
705     if (disabled())
706         return;
707
708     forEachSessionResource(sessionID, [this] (CachedResource& resource) { remove(resource); });
709
710     ASSERT(!m_sessionResources.contains(sessionID));
711 }
712
713 bool MemoryCache::needsPruning() const
714 {
715     return m_liveSize + m_deadSize > m_capacity || m_deadSize > m_maxDeadCapacity;
716 }
717
718 void MemoryCache::prune()
719 {
720     if (!needsPruning())
721         return;
722         
723     pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live.
724     pruneLiveResources();
725 }
726
727 void MemoryCache::pruneSoon()
728 {
729     if (m_pruneTimer.isActive())
730         return;
731     if (!needsPruning())
732         return;
733     m_pruneTimer.startOneShot(0_s);
734 }
735
736 #ifndef NDEBUG
737 void MemoryCache::dumpStats()
738 {
739     Statistics s = getStatistics();
740     printf("%-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize");
741     printf("%-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------");
742     printf("%-13s %13d %13d %13d %13d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize);
743     printf("%-13s %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize);
744 #if ENABLE(XSLT)
745     printf("%-13s %13d %13d %13d %13d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize);
746 #endif
747     printf("%-13s %13d %13d %13d %13d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize);
748     printf("%-13s %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize);
749     printf("%-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------");
750 }
751
752 void MemoryCache::dumpLRULists(bool includeLive) const
753 {
754     printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced):\n");
755
756     int size = m_allResources.size();
757     for (int i = size - 1; i >= 0; i--) {
758         printf("\n\nList %d: ", i);
759         for (auto* resource : *m_allResources[i]) {
760             if (includeLive || !resource->hasClients())
761                 printf("(%.1fK, %.1fK, %uA, %dR); ", resource->decodedSize() / 1024.0f, (resource->encodedSize() + resource->overheadSize()) / 1024.0f, resource->accessCount(), resource->hasClients());
762         }
763     }
764 }
765 #endif
766
767 } // namespace WebCore