• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "CachedCSSStyleSheet.h"
27 #include "CachedFont.h"
28 #include "CachedImage.h"
29 #include "CachedScript.h"
30 #include "CachedXSLStyleSheet.h"
31 #include "CachedResourceLoader.h"
32 #include "Document.h"
33 #include "FrameLoader.h"
34 #include "FrameLoaderTypes.h"
35 #include "FrameView.h"
36 #include "Image.h"
37 #include "Logging.h"
38 #include "ResourceHandle.h"
39 #include "SecurityOrigin.h"
40 #include "SecurityOriginHash.h"
41 #include <stdio.h>
42 #include <wtf/CurrentTime.h>
43 #include <wtf/text/CString.h>
44 
45 using namespace std;
46 
47 namespace WebCore {
48 
49 static const int cDefaultCacheCapacity = 8192 * 1024;
50 static const double cMinDelayBeforeLiveDecodedPrune = 1; // Seconds.
51 static const float cTargetPrunePercentage = .95f; // Percentage of capacity toward which we prune, to avoid immediately pruning again.
52 static const double cDefaultDecodedDataDeletionInterval = 0;
53 
memoryCache()54 MemoryCache* memoryCache()
55 {
56     static MemoryCache* staticCache = new MemoryCache;
57     return staticCache;
58 }
59 
MemoryCache()60 MemoryCache::MemoryCache()
61     : m_disabled(false)
62     , m_pruneEnabled(true)
63     , m_inPruneDeadResources(false)
64     , m_capacity(cDefaultCacheCapacity)
65     , m_minDeadCapacity(0)
66     , m_maxDeadCapacity(cDefaultCacheCapacity)
67     , m_deadDecodedDataDeletionInterval(cDefaultDecodedDataDeletionInterval)
68     , m_liveSize(0)
69     , m_deadSize(0)
70 {
71 }
72 
removeFragmentIdentifierIfNeeded(const KURL & originalURL)73 KURL MemoryCache::removeFragmentIdentifierIfNeeded(const KURL& originalURL)
74 {
75     if (!originalURL.hasFragmentIdentifier())
76         return originalURL;
77     // Strip away fragment identifier from HTTP and file urls.
78     // Data urls must be unmodified and it is also safer to keep them for custom protocols.
79     if (!(originalURL.protocolInHTTPFamily() || originalURL.isLocalFile()))
80         return originalURL;
81     KURL url = originalURL;
82     url.removeFragmentIdentifier();
83     return url;
84 }
85 
add(CachedResource * resource)86 bool MemoryCache::add(CachedResource* resource)
87 {
88     if (disabled())
89         return false;
90 
91     m_resources.set(resource->url(), resource);
92     resource->setInCache(true);
93 
94     resourceAccessed(resource);
95 
96     LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource->url().latin1().data(), resource);
97     return true;
98 }
99 
revalidationSucceeded(CachedResource * revalidatingResource,const ResourceResponse & response)100 void MemoryCache::revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse& response)
101 {
102     CachedResource* resource = revalidatingResource->resourceToRevalidate();
103     ASSERT(resource);
104     ASSERT(!resource->inCache());
105     ASSERT(resource->isLoaded());
106     ASSERT(revalidatingResource->inCache());
107 
108     evict(revalidatingResource);
109 
110     ASSERT(!m_resources.get(resource->url()));
111     m_resources.set(resource->url(), resource);
112     resource->setInCache(true);
113     resource->updateResponseAfterRevalidation(response);
114     insertInLRUList(resource);
115     int delta = resource->size();
116     if (resource->decodedSize() && resource->hasClients())
117         insertInLiveDecodedResourcesList(resource);
118     if (delta)
119         adjustSize(resource->hasClients(), delta);
120 
121     revalidatingResource->switchClientsToRevalidatedResource();
122     // this deletes the revalidating resource
123     revalidatingResource->clearResourceToRevalidate();
124 }
125 
revalidationFailed(CachedResource * revalidatingResource)126 void MemoryCache::revalidationFailed(CachedResource* revalidatingResource)
127 {
128     LOG(ResourceLoading, "Revalidation failed for %p", revalidatingResource);
129     ASSERT(revalidatingResource->resourceToRevalidate());
130     revalidatingResource->clearResourceToRevalidate();
131 }
132 
resourceForURL(const KURL & resourceURL)133 CachedResource* MemoryCache::resourceForURL(const KURL& resourceURL)
134 {
135     KURL url = removeFragmentIdentifierIfNeeded(resourceURL);
136     CachedResource* resource = m_resources.get(url);
137     bool wasPurgeable = MemoryCache::shouldMakeResourcePurgeableOnEviction() && resource && resource->isPurgeable();
138     if (resource && !resource->makePurgeable(false)) {
139         ASSERT(!resource->hasClients());
140         evict(resource);
141         return 0;
142     }
143     // Add the size back since we had subtracted it when we marked the memory as purgeable.
144     if (wasPurgeable)
145         adjustSize(resource->hasClients(), resource->size());
146     return resource;
147 }
148 
deadCapacity() const149 unsigned MemoryCache::deadCapacity() const
150 {
151     // Dead resource capacity is whatever space is not occupied by live resources, bounded by an independent minimum and maximum.
152     unsigned capacity = m_capacity - min(m_liveSize, m_capacity); // Start with available capacity.
153     capacity = max(capacity, m_minDeadCapacity); // Make sure it's above the minimum.
154     capacity = min(capacity, m_maxDeadCapacity); // Make sure it's below the maximum.
155     return capacity;
156 }
157 
liveCapacity() const158 unsigned MemoryCache::liveCapacity() const
159 {
160     // Live resource capacity is whatever is left over after calculating dead resource capacity.
161     return m_capacity - deadCapacity();
162 }
163 
pruneLiveResources()164 void MemoryCache::pruneLiveResources()
165 {
166     if (!m_pruneEnabled)
167         return;
168 
169     unsigned capacity = liveCapacity();
170     if (capacity && m_liveSize <= capacity)
171         return;
172 
173     unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again.
174     double currentTime = FrameView::currentPaintTimeStamp();
175     if (!currentTime) // In case prune is called directly, outside of a Frame paint.
176         currentTime = WTF::currentTime();
177 
178     // Destroy any decoded data in live objects that we can.
179     // Start from the tail, since this is the least recently accessed of the objects.
180 
181     // The list might not be sorted by the m_lastDecodedAccessTime. The impact
182     // of this weaker invariant is minor as the below if statement to check the
183     // elapsedTime will evaluate to false as the currentTime will be a lot
184     // greater than the current->m_lastDecodedAccessTime.
185     // For more details see: https://bugs.webkit.org/show_bug.cgi?id=30209
186     CachedResource* current = m_liveDecodedResources.m_tail;
187     while (current) {
188         CachedResource* prev = current->m_prevInLiveResourcesList;
189         ASSERT(current->hasClients());
190         if (current->isLoaded() && current->decodedSize()) {
191             // Check to see if the remaining resources are too new to prune.
192             double elapsedTime = currentTime - current->m_lastDecodedAccessTime;
193             if (elapsedTime < cMinDelayBeforeLiveDecodedPrune)
194                 return;
195 
196             // Destroy our decoded data. This will remove us from
197             // m_liveDecodedResources, and possibly move us to a different LRU
198             // list in m_allResources.
199             current->destroyDecodedData();
200 
201             if (targetSize && m_liveSize <= targetSize)
202                 return;
203         }
204         current = prev;
205     }
206 }
207 
pruneDeadResources()208 void MemoryCache::pruneDeadResources()
209 {
210     if (!m_pruneEnabled)
211         return;
212 
213     unsigned capacity = deadCapacity();
214     if (capacity && m_deadSize <= capacity)
215         return;
216 
217     unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again.
218     int size = m_allResources.size();
219 
220     if (!m_inPruneDeadResources) {
221         // See if we have any purged resources we can evict.
222         for (int i = 0; i < size; i++) {
223             CachedResource* current = m_allResources[i].m_tail;
224             while (current) {
225                 CachedResource* prev = current->m_prevInAllResourcesList;
226                 if (current->wasPurged()) {
227                     ASSERT(!current->hasClients());
228                     ASSERT(!current->isPreloaded());
229                     evict(current);
230                 }
231                 current = prev;
232             }
233         }
234         if (targetSize && m_deadSize <= targetSize)
235             return;
236     }
237 
238     bool canShrinkLRULists = true;
239     m_inPruneDeadResources = true;
240     for (int i = size - 1; i >= 0; i--) {
241         // Remove from the tail, since this is the least frequently accessed of the objects.
242         CachedResource* current = m_allResources[i].m_tail;
243 
244         // First flush all the decoded data in this queue.
245         while (current) {
246             CachedResource* prev = current->m_prevInAllResourcesList;
247             if (!current->hasClients() && !current->isPreloaded() && current->isLoaded()) {
248                 // Destroy our decoded data. This will remove us from
249                 // m_liveDecodedResources, and possibly move us to a different
250                 // LRU list in m_allResources.
251                 current->destroyDecodedData();
252 
253                 if (targetSize && m_deadSize <= targetSize) {
254                     m_inPruneDeadResources = false;
255                     return;
256                 }
257             }
258             current = prev;
259         }
260 
261         // Now evict objects from this queue.
262         current = m_allResources[i].m_tail;
263         while (current) {
264             CachedResource* prev = current->m_prevInAllResourcesList;
265             if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) {
266                 if (!makeResourcePurgeable(current))
267                     evict(current);
268 
269                 // If evict() caused pruneDeadResources() to be re-entered, bail out. This can happen when removing an
270                 // SVG CachedImage that has subresources.
271                 if (!m_inPruneDeadResources)
272                     return;
273 
274                 if (targetSize && m_deadSize <= targetSize) {
275                     m_inPruneDeadResources = false;
276                     return;
277                 }
278             }
279             current = prev;
280         }
281 
282         // Shrink the vector back down so we don't waste time inspecting
283         // empty LRU lists on future prunes.
284         if (m_allResources[i].m_head)
285             canShrinkLRULists = false;
286         else if (canShrinkLRULists)
287             m_allResources.resize(i);
288     }
289     m_inPruneDeadResources = false;
290 }
291 
setCapacities(unsigned minDeadBytes,unsigned maxDeadBytes,unsigned totalBytes)292 void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes)
293 {
294     ASSERT(minDeadBytes <= maxDeadBytes);
295     ASSERT(maxDeadBytes <= totalBytes);
296     m_minDeadCapacity = minDeadBytes;
297     m_maxDeadCapacity = maxDeadBytes;
298     m_capacity = totalBytes;
299     prune();
300 }
301 
makeResourcePurgeable(CachedResource * resource)302 bool MemoryCache::makeResourcePurgeable(CachedResource* resource)
303 {
304     if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
305         return false;
306 
307     if (!resource->inCache())
308         return false;
309 
310     if (resource->isPurgeable())
311         return true;
312 
313     if (!resource->isSafeToMakePurgeable())
314         return false;
315 
316     if (!resource->makePurgeable(true))
317         return false;
318 
319     adjustSize(resource->hasClients(), -static_cast<int>(resource->size()));
320 
321     return true;
322 }
323 
evict(CachedResource * resource)324 void MemoryCache::evict(CachedResource* resource)
325 {
326     LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", resource, resource->url().latin1().data());
327     // The resource may have already been removed by someone other than our caller,
328     // who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>.
329     if (resource->inCache()) {
330         // Remove from the resource map.
331         m_resources.remove(resource->url());
332         resource->setInCache(false);
333 
334         // Remove from the appropriate LRU list.
335         removeFromLRUList(resource);
336         removeFromLiveDecodedResourcesList(resource);
337 
338         // If the resource was purged, it means we had already decremented the size when we made the
339         // resource purgeable in makeResourcePurgeable(). So adjust the size if we are evicting a
340         // resource that was not marked as purgeable.
341         if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() || !resource->isPurgeable())
342             adjustSize(resource->hasClients(), -static_cast<int>(resource->size()));
343     } else
344         ASSERT(m_resources.get(resource->url()) != resource);
345 
346     if (resource->canDelete())
347         delete resource;
348 }
349 
fastLog2(unsigned i)350 static inline unsigned fastLog2(unsigned i)
351 {
352     unsigned log2 = 0;
353     if (i & (i - 1))
354         log2 += 1;
355     if (i >> 16)
356         log2 += 16, i >>= 16;
357     if (i >> 8)
358         log2 += 8, i >>= 8;
359     if (i >> 4)
360         log2 += 4, i >>= 4;
361     if (i >> 2)
362         log2 += 2, i >>= 2;
363     if (i >> 1)
364         log2 += 1;
365     return log2;
366 }
367 
lruListFor(CachedResource * resource)368 MemoryCache::LRUList* MemoryCache::lruListFor(CachedResource* resource)
369 {
370     unsigned accessCount = max(resource->accessCount(), 1U);
371     unsigned queueIndex = fastLog2(resource->size() / accessCount);
372 #ifndef NDEBUG
373     resource->m_lruIndex = queueIndex;
374 #endif
375     if (m_allResources.size() <= queueIndex)
376         m_allResources.grow(queueIndex + 1);
377     return &m_allResources[queueIndex];
378 }
379 
removeFromLRUList(CachedResource * resource)380 void MemoryCache::removeFromLRUList(CachedResource* resource)
381 {
382     // If we've never been accessed, then we're brand new and not in any list.
383     if (resource->accessCount() == 0)
384         return;
385 
386 #if !ASSERT_DISABLED
387     unsigned oldListIndex = resource->m_lruIndex;
388 #endif
389 
390     LRUList* list = lruListFor(resource);
391 
392 #if !ASSERT_DISABLED
393     // Verify that the list we got is the list we want.
394     ASSERT(resource->m_lruIndex == oldListIndex);
395 
396     // Verify that we are in fact in this list.
397     bool found = false;
398     for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) {
399         if (current == resource) {
400             found = true;
401             break;
402         }
403     }
404     ASSERT(found);
405 #endif
406 
407     CachedResource* next = resource->m_nextInAllResourcesList;
408     CachedResource* prev = resource->m_prevInAllResourcesList;
409 
410     if (next == 0 && prev == 0 && list->m_head != resource)
411         return;
412 
413     resource->m_nextInAllResourcesList = 0;
414     resource->m_prevInAllResourcesList = 0;
415 
416     if (next)
417         next->m_prevInAllResourcesList = prev;
418     else if (list->m_tail == resource)
419         list->m_tail = prev;
420 
421     if (prev)
422         prev->m_nextInAllResourcesList = next;
423     else if (list->m_head == resource)
424         list->m_head = next;
425 }
426 
insertInLRUList(CachedResource * resource)427 void MemoryCache::insertInLRUList(CachedResource* resource)
428 {
429     // Make sure we aren't in some list already.
430     ASSERT(!resource->m_nextInAllResourcesList && !resource->m_prevInAllResourcesList);
431     ASSERT(resource->inCache());
432     ASSERT(resource->accessCount() > 0);
433 
434     LRUList* list = lruListFor(resource);
435 
436     resource->m_nextInAllResourcesList = list->m_head;
437     if (list->m_head)
438         list->m_head->m_prevInAllResourcesList = resource;
439     list->m_head = resource;
440 
441     if (!resource->m_nextInAllResourcesList)
442         list->m_tail = resource;
443 
444 #ifndef NDEBUG
445     // Verify that we are in now in the list like we should be.
446     list = lruListFor(resource);
447     bool found = false;
448     for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) {
449         if (current == resource) {
450             found = true;
451             break;
452         }
453     }
454     ASSERT(found);
455 #endif
456 
457 }
458 
resourceAccessed(CachedResource * resource)459 void MemoryCache::resourceAccessed(CachedResource* resource)
460 {
461     ASSERT(resource->inCache());
462 
463     // Need to make sure to remove before we increase the access count, since
464     // the queue will possibly change.
465     removeFromLRUList(resource);
466 
467     // If this is the first time the resource has been accessed, adjust the size of the cache to account for its initial size.
468     if (!resource->accessCount())
469         adjustSize(resource->hasClients(), resource->size());
470 
471     // Add to our access count.
472     resource->increaseAccessCount();
473 
474     // Now insert into the new queue.
475     insertInLRUList(resource);
476 }
477 
removeResourcesWithOrigin(SecurityOrigin * origin)478 void MemoryCache::removeResourcesWithOrigin(SecurityOrigin* origin)
479 {
480     Vector<CachedResource*> resourcesWithOrigin;
481 
482     CachedResourceMap::iterator e = m_resources.end();
483     for (CachedResourceMap::iterator it = m_resources.begin(); it != e; ++it) {
484         CachedResource* resource = it->second;
485         RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::createFromString(resource->url());
486         if (!resourceOrigin)
487             continue;
488         if (resourceOrigin->equal(origin))
489             resourcesWithOrigin.append(resource);
490     }
491 
492     for (size_t i = 0; i < resourcesWithOrigin.size(); ++i)
493         remove(resourcesWithOrigin[i]);
494 }
495 
getOriginsWithCache(SecurityOriginSet & origins)496 void MemoryCache::getOriginsWithCache(SecurityOriginSet& origins)
497 {
498     CachedResourceMap::iterator e = m_resources.end();
499     for (CachedResourceMap::iterator it = m_resources.begin(); it != e; ++it)
500         origins.add(SecurityOrigin::create(KURL(KURL(), it->second->url())));
501 }
502 
removeFromLiveDecodedResourcesList(CachedResource * resource)503 void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource* resource)
504 {
505     // If we've never been accessed, then we're brand new and not in any list.
506     if (!resource->m_inLiveDecodedResourcesList)
507         return;
508     resource->m_inLiveDecodedResourcesList = false;
509 
510 #ifndef NDEBUG
511     // Verify that we are in fact in this list.
512     bool found = false;
513     for (CachedResource* current = m_liveDecodedResources.m_head; current; current = current->m_nextInLiveResourcesList) {
514         if (current == resource) {
515             found = true;
516             break;
517         }
518     }
519     ASSERT(found);
520 #endif
521 
522     CachedResource* next = resource->m_nextInLiveResourcesList;
523     CachedResource* prev = resource->m_prevInLiveResourcesList;
524 
525     if (next == 0 && prev == 0 && m_liveDecodedResources.m_head != resource)
526         return;
527 
528     resource->m_nextInLiveResourcesList = 0;
529     resource->m_prevInLiveResourcesList = 0;
530 
531     if (next)
532         next->m_prevInLiveResourcesList = prev;
533     else if (m_liveDecodedResources.m_tail == resource)
534         m_liveDecodedResources.m_tail = prev;
535 
536     if (prev)
537         prev->m_nextInLiveResourcesList = next;
538     else if (m_liveDecodedResources.m_head == resource)
539         m_liveDecodedResources.m_head = next;
540 }
541 
insertInLiveDecodedResourcesList(CachedResource * resource)542 void MemoryCache::insertInLiveDecodedResourcesList(CachedResource* resource)
543 {
544     // Make sure we aren't in the list already.
545     ASSERT(!resource->m_nextInLiveResourcesList && !resource->m_prevInLiveResourcesList && !resource->m_inLiveDecodedResourcesList);
546     resource->m_inLiveDecodedResourcesList = true;
547 
548     resource->m_nextInLiveResourcesList = m_liveDecodedResources.m_head;
549     if (m_liveDecodedResources.m_head)
550         m_liveDecodedResources.m_head->m_prevInLiveResourcesList = resource;
551     m_liveDecodedResources.m_head = resource;
552 
553     if (!resource->m_nextInLiveResourcesList)
554         m_liveDecodedResources.m_tail = resource;
555 
556 #ifndef NDEBUG
557     // Verify that we are in now in the list like we should be.
558     bool found = false;
559     for (CachedResource* current = m_liveDecodedResources.m_head; current; current = current->m_nextInLiveResourcesList) {
560         if (current == resource) {
561             found = true;
562             break;
563         }
564     }
565     ASSERT(found);
566 #endif
567 
568 }
569 
addToLiveResourcesSize(CachedResource * resource)570 void MemoryCache::addToLiveResourcesSize(CachedResource* resource)
571 {
572     m_liveSize += resource->size();
573     m_deadSize -= resource->size();
574 }
575 
removeFromLiveResourcesSize(CachedResource * resource)576 void MemoryCache::removeFromLiveResourcesSize(CachedResource* resource)
577 {
578     m_liveSize -= resource->size();
579     m_deadSize += resource->size();
580 }
581 
adjustSize(bool live,int delta)582 void MemoryCache::adjustSize(bool live, int delta)
583 {
584     if (live) {
585         ASSERT(delta >= 0 || ((int)m_liveSize + delta >= 0));
586         m_liveSize += delta;
587     } else {
588         ASSERT(delta >= 0 || ((int)m_deadSize + delta >= 0));
589         m_deadSize += delta;
590     }
591 }
592 
addResource(CachedResource * o)593 void MemoryCache::TypeStatistic::addResource(CachedResource* o)
594 {
595     bool purged = o->wasPurged();
596     bool purgeable = o->isPurgeable() && !purged;
597     int pageSize = (o->encodedSize() + o->overheadSize() + 4095) & ~4095;
598     count++;
599     size += purged ? 0 : o->size();
600     liveSize += o->hasClients() ? o->size() : 0;
601     decodedSize += o->decodedSize();
602     purgeableSize += purgeable ? pageSize : 0;
603     purgedSize += purged ? pageSize : 0;
604 }
605 
getStatistics()606 MemoryCache::Statistics MemoryCache::getStatistics()
607 {
608     Statistics stats;
609     CachedResourceMap::iterator e = m_resources.end();
610     for (CachedResourceMap::iterator i = m_resources.begin(); i != e; ++i) {
611         CachedResource* resource = i->second;
612         switch (resource->type()) {
613         case CachedResource::ImageResource:
614             stats.images.addResource(resource);
615             break;
616         case CachedResource::CSSStyleSheet:
617             stats.cssStyleSheets.addResource(resource);
618             break;
619         case CachedResource::Script:
620             stats.scripts.addResource(resource);
621             break;
622 #if ENABLE(XSLT)
623         case CachedResource::XSLStyleSheet:
624             stats.xslStyleSheets.addResource(resource);
625             break;
626 #endif
627         case CachedResource::FontResource:
628             stats.fonts.addResource(resource);
629             break;
630         default:
631             break;
632         }
633     }
634     return stats;
635 }
636 
setDisabled(bool disabled)637 void MemoryCache::setDisabled(bool disabled)
638 {
639     m_disabled = disabled;
640     if (!m_disabled)
641         return;
642 
643     for (;;) {
644         CachedResourceMap::iterator i = m_resources.begin();
645         if (i == m_resources.end())
646             break;
647         evict(i->second);
648     }
649 }
650 
evictResources()651 void MemoryCache::evictResources()
652 {
653     if (disabled())
654         return;
655 
656     setDisabled(true);
657     setDisabled(false);
658 }
659 
660 #ifndef NDEBUG
dumpStats()661 void MemoryCache::dumpStats()
662 {
663     Statistics s = getStatistics();
664     printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize");
665     printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------");
666     printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize, s.images.purgeableSize, s.images.purgedSize);
667     printf("%-13s %13d %13d %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize, s.cssStyleSheets.purgeableSize, s.cssStyleSheets.purgedSize);
668 #if ENABLE(XSLT)
669     printf("%-13s %13d %13d %13d %13d %13d %13d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize, s.xslStyleSheets.purgeableSize, s.xslStyleSheets.purgedSize);
670 #endif
671     printf("%-13s %13d %13d %13d %13d %13d %13d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize, s.scripts.purgeableSize, s.scripts.purgedSize);
672     printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize, s.fonts.purgeableSize, s.fonts.purgedSize);
673     printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------");
674 }
675 
dumpLRULists(bool includeLive) const676 void MemoryCache::dumpLRULists(bool includeLive) const
677 {
678     printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged):\n");
679 
680     int size = m_allResources.size();
681     for (int i = size - 1; i >= 0; i--) {
682         printf("\n\nList %d: ", i);
683         CachedResource* current = m_allResources[i].m_tail;
684         while (current) {
685             CachedResource* prev = current->m_prevInAllResourcesList;
686             if (includeLive || !current->hasClients())
687                 printf("(%.1fK, %.1fK, %uA, %dR, %d, %d); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients(), current->isPurgeable(), current->wasPurged());
688 
689             current = prev;
690         }
691     }
692 }
693 #endif
694 
695 } // namespace WebCore
696