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