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