This patch reworks the WebCore memory cache to significantly reduce the amoun...
authorhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 7 Mar 2007 07:42:45 +0000 (07:42 +0000)
committerhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 7 Mar 2007 07:42:45 +0000 (07:42 +0000)
        images in the cache and to enhance the accuracy of the cache size as an absolute bound for the objects
        contained within it.  WebCore's memory use over time should significantly improve as a result of these
        changes.

        Cached resources now have both an encoded size (the original data stream) and a decoded size (an estimate of
        the amount of memory consumed by an expanded version of that resource, e.g., the decoded frames of an image).
        Both sizes now count towards the total size of the object and towards the allowed memory cache total.

        By including both totals the reported size of resources will now be larger, and the cache will therefore become
        much more aggressive about flushing.

        Objects are stored in size-adjusted and popularity-aware LRU lists as before, but encoded size is now always
        used when determining the correct LRU list.

        The flush algorithm for the memory cache has been rewritten to first destroy decoded data before evicting
        resources.  By being able to compact its resources without evicting them, the memory cache can now hold many more
        unique resources (encoded) in the same amount of space.  Depending on how much of a hit we want to take from
        re-decoding images, the memory cache could in theory have its size significantly reduced now while still holding
        more resources than it did at the larger size!

        Reviewed by mjs

        * WebCore.xcodeproj/project.pbxproj:
        * loader/Cache.cpp:
        (WebCore::Cache::requestResource):
        (WebCore::Cache::prune):
        (WebCore::Cache::remove):
        (WebCore::Cache::lruListFor):
        (WebCore::Cache::adjustSize):
        * loader/Cache.h:
        * loader/CachedCSSStyleSheet.cpp:
        (WebCore::CachedCSSStyleSheet::data):
        * loader/CachedImage.cpp:
        (WebCore::CachedImage::CachedImage):
        (WebCore::CachedImage::allReferencesRemoved):
        (WebCore::CachedImage::clear):
        (WebCore::CachedImage::data):
        (WebCore::CachedImage::destroyDecodedData):
        (WebCore::CachedImage::decodedSize):
        (WebCore::CachedImage::decodedSizeChanged):
        (WebCore::CachedImage::shouldPauseAnimation):
        * loader/CachedImage.h:
        * loader/CachedResource.cpp:
        (WebCore::CachedResource::CachedResource):
        (WebCore::CachedResource::deref):
        (WebCore::CachedResource::setEncodedSize):
        * loader/CachedResource.h:
        (WebCore::CachedResource::allReferencesRemoved):
        (WebCore::CachedResource::size):
        (WebCore::CachedResource::encodedSize):
        (WebCore::CachedResource::decodedSize):
        (WebCore::CachedResource::destroyDecodedData):
        * loader/CachedScript.cpp:
        (WebCore::CachedScript::data):
        * loader/CachedXSLStyleSheet.cpp:
        (WebCore::CachedXSLStyleSheet::data):
        * platform/graphics/BitmapImage.cpp:
        (WebCore::BitmapImage::BitmapImage):
        (WebCore::BitmapImage::~BitmapImage):
        (WebCore::BitmapImage::destroyDecodedData):
        (WebCore::BitmapImage::pruneDecodedDataIfNeeded):
        (WebCore::BitmapImage::cacheFrame):
        (WebCore::BitmapImage::setNativeData):
        (WebCore::BitmapImage::shouldAnimate):
        (WebCore::BitmapImage::advanceAnimation):
        * platform/graphics/BitmapImage.h:
        (WebCore::BitmapImage::decodedSize):
        * platform/graphics/Image.cpp:
        (WebCore::Image::Image):
        * platform/graphics/Image.h:
        (WebCore::Image::destroyDecodedData):
        (WebCore::Image::decodedSize):
        (WebCore::Image::imageObserver):
        * platform/graphics/ImageAnimationObserver.h: Removed.
        * platform/graphics/ImageObserver.h: Added.
        (WebCore::ImageObserver::~ImageObserver):
        * platform/graphics/svg/SVGImage.cpp:
        (WebCore::SVGImage::SVGImage):
        * platform/graphics/svg/SVGImage.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@20009 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
WebCore/ChangeLog
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/loader/Cache.cpp
WebCore/loader/Cache.h
WebCore/loader/CachedCSSStyleSheet.cpp
WebCore/loader/CachedImage.cpp
WebCore/loader/CachedImage.h
WebCore/loader/CachedResource.cpp
WebCore/loader/CachedResource.h
WebCore/loader/CachedScript.cpp
WebCore/loader/CachedXSLStyleSheet.cpp
WebCore/platform/graphics/BitmapImage.cpp
WebCore/platform/graphics/BitmapImage.h
WebCore/platform/graphics/Image.cpp
WebCore/platform/graphics/Image.h
WebCore/platform/graphics/ImageObserver.h [moved from WebCore/platform/graphics/ImageAnimationObserver.h with 87% similarity]
WebCore/platform/graphics/svg/SVGImage.cpp
WebCore/platform/graphics/svg/SVGImage.h

index 1088f15..c2c58ed 100644 (file)
@@ -1,3 +1,86 @@
+2007-03-06  David Hyatt  <hyatt@apple.com>
+
+        This patch reworks the WebCore memory cache to significantly reduce the amount of memory consumed by
+        images in the cache and to enhance the accuracy of the cache size as an absolute bound for the objects
+        contained within it.  WebCore's memory use over time should significantly improve as a result of these
+        changes.
+
+        Cached resources now have both an encoded size (the original data stream) and a decoded size (an estimate of
+        the amount of memory consumed by an expanded version of that resource, e.g., the decoded frames of an image).
+        Both sizes now count towards the total size of the object and towards the allowed memory cache total.
+
+        By including both totals the reported size of resources will now be larger, and the cache will therefore become
+        much more aggressive about flushing.
+
+        Objects are stored in size-adjusted and popularity-aware LRU lists as before, but encoded size is now always
+        used when determining the correct LRU list.
+
+        The flush algorithm for the memory cache has been rewritten to first destroy decoded data before evicting
+        resources.  By being able to compact its resources without evicting them, the memory cache can now hold many more
+        unique resources (encoded) in the same amount of space.  Depending on how much of a hit we want to take from
+        re-decoding images, the memory cache could in theory have its size significantly reduced now while still holding
+        more resources than it did at the larger size!
+
+        Reviewed by mjs
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * loader/Cache.cpp:
+        (WebCore::Cache::requestResource):
+        (WebCore::Cache::prune):
+        (WebCore::Cache::remove):
+        (WebCore::Cache::lruListFor):
+        (WebCore::Cache::adjustSize):
+        * loader/Cache.h:
+        * loader/CachedCSSStyleSheet.cpp:
+        (WebCore::CachedCSSStyleSheet::data):
+        * loader/CachedImage.cpp:
+        (WebCore::CachedImage::CachedImage):
+        (WebCore::CachedImage::allReferencesRemoved):
+        (WebCore::CachedImage::clear):
+        (WebCore::CachedImage::data):
+        (WebCore::CachedImage::destroyDecodedData):
+        (WebCore::CachedImage::decodedSize):
+        (WebCore::CachedImage::decodedSizeChanged):
+        (WebCore::CachedImage::shouldPauseAnimation):
+        * loader/CachedImage.h:
+        * loader/CachedResource.cpp:
+        (WebCore::CachedResource::CachedResource):
+        (WebCore::CachedResource::deref):
+        (WebCore::CachedResource::setEncodedSize):
+        * loader/CachedResource.h:
+        (WebCore::CachedResource::allReferencesRemoved):
+        (WebCore::CachedResource::size):
+        (WebCore::CachedResource::encodedSize):
+        (WebCore::CachedResource::decodedSize):
+        (WebCore::CachedResource::destroyDecodedData):
+        * loader/CachedScript.cpp:
+        (WebCore::CachedScript::data):
+        * loader/CachedXSLStyleSheet.cpp:
+        (WebCore::CachedXSLStyleSheet::data):
+        * platform/graphics/BitmapImage.cpp:
+        (WebCore::BitmapImage::BitmapImage):
+        (WebCore::BitmapImage::~BitmapImage):
+        (WebCore::BitmapImage::destroyDecodedData):
+        (WebCore::BitmapImage::pruneDecodedDataIfNeeded):
+        (WebCore::BitmapImage::cacheFrame):
+        (WebCore::BitmapImage::setNativeData):
+        (WebCore::BitmapImage::shouldAnimate):
+        (WebCore::BitmapImage::advanceAnimation):
+        * platform/graphics/BitmapImage.h:
+        (WebCore::BitmapImage::decodedSize):
+        * platform/graphics/Image.cpp:
+        (WebCore::Image::Image):
+        * platform/graphics/Image.h:
+        (WebCore::Image::destroyDecodedData):
+        (WebCore::Image::decodedSize):
+        (WebCore::Image::imageObserver):
+        * platform/graphics/ImageAnimationObserver.h: Removed.
+        * platform/graphics/ImageObserver.h: Added.
+        (WebCore::ImageObserver::~ImageObserver):
+        * platform/graphics/svg/SVGImage.cpp:
+        (WebCore::SVGImage::SVGImage):
+        * platform/graphics/svg/SVGImage.h:
+
 2007-03-06  Alexey Proskuryakov  <ap@webkit.org>
 
         Reviewed by Sam Weinig.
index cf3dc05..f01be69 100644 (file)
                B2A015BA0AF6CD53006BCE0E /* SVGResourceMasker.h in Headers */ = {isa = PBXBuildFile; fileRef = B2A015A70AF6CD53006BCE0E /* SVGResourceMasker.h */; };
                B2A10B920B3818BD00099AA4 /* ImageBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = B2A10B910B3818BD00099AA4 /* ImageBuffer.h */; };
                B2A10B940B3818D700099AA4 /* ImageBufferCG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2A10B930B3818D700099AA4 /* ImageBufferCG.cpp */; };
-               B2BFB5A00B22F76200567E80 /* ImageAnimationObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = B2BFB59F0B22F76200567E80 /* ImageAnimationObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
                B2C96D8D0B3AF2B7005E80EC /* JSSVGPathSegCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2C96D8C0B3AF2B7005E80EC /* JSSVGPathSegCustom.cpp */; };
                B2CB41930AB75904004D9C45 /* SVGRenderingIntent.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CB414C0AB75904004D9C45 /* SVGRenderingIntent.h */; };
                B2CB41A60AB75904004D9C45 /* SVGUnitTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CB415F0AB75904004D9C45 /* SVGUnitTypes.h */; };
                BC6DB3690A1A7CB700E5CD14 /* GlyphPageTreeNode.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6DB3680A1A7CB700E5CD14 /* GlyphPageTreeNode.h */; settings = {ATTRIBUTES = (Private, ); }; };
                BC6DB4740A1A90FB00E5CD14 /* GlyphPageTreeNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BC6DB4730A1A90FB00E5CD14 /* GlyphPageTreeNode.cpp */; };
                BC6DB4D40A1AFEEF00E5CD14 /* GlyphPageTreeNodeMac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BC6DB4D30A1AFEEF00E5CD14 /* GlyphPageTreeNodeMac.cpp */; };
+               BC7F44A80B9E324E00A9D081 /* ImageObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = BC7F44A70B9E324E00A9D081 /* ImageObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
                BCAA90C30A7EBA60008B1229 /* ScrollBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCAA90C20A7EBA60008B1229 /* ScrollBar.cpp */; };
                BCB16B8B0979B01400467741 /* DeprecatedArray.h in Headers */ = {isa = PBXBuildFile; fileRef = BCB16B880979B01400467741 /* DeprecatedArray.h */; settings = {ATTRIBUTES = (Private, ); }; };
                BCB16B8C0979B01400467741 /* ArrayImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCB16B890979B01400467741 /* ArrayImpl.cpp */; };
                B2A015A70AF6CD53006BCE0E /* SVGResourceMasker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SVGResourceMasker.h; sourceTree = "<group>"; };
                B2A10B910B3818BD00099AA4 /* ImageBuffer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ImageBuffer.h; sourceTree = "<group>"; };
                B2A10B930B3818D700099AA4 /* ImageBufferCG.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = ImageBufferCG.cpp; sourceTree = "<group>"; };
-               B2BFB59F0B22F76200567E80 /* ImageAnimationObserver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ImageAnimationObserver.h; sourceTree = "<group>"; };
                B2C96D8C0B3AF2B7005E80EC /* JSSVGPathSegCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSSVGPathSegCustom.cpp; sourceTree = "<group>"; };
                B2CB41010AB758E6004D9C45 /* SVGAElement.idl */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SVGAElement.idl; sourceTree = "<group>"; };
                B2CB41020AB758E6004D9C45 /* SVGAnimateColorElement.idl */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SVGAnimateColorElement.idl; sourceTree = "<group>"; };
                BC6DB4D30A1AFEEF00E5CD14 /* GlyphPageTreeNodeMac.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = GlyphPageTreeNodeMac.cpp; sourceTree = "<group>"; };
                BC7B2AF80450824100A8000F /* ScrollBar.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = ScrollBar.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                BC7B2AF90450824100A8000F /* PlatformScrollBarMac.mm */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformScrollBarMac.mm; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
+               BC7F44A70B9E324E00A9D081 /* ImageObserver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ImageObserver.h; sourceTree = "<group>"; };
                BCAA90C20A7EBA60008B1229 /* ScrollBar.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ScrollBar.cpp; path = platform/ScrollBar.cpp; sourceTree = SOURCE_ROOT; };
                BCB16B880979B01400467741 /* DeprecatedArray.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DeprecatedArray.h; sourceTree = "<group>"; };
                BCB16B890979B01400467741 /* ArrayImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayImpl.cpp; sourceTree = "<group>"; };
                                B27535400B053814002CE64F /* Icon.h */,
                                B27535410B053814002CE64F /* Image.cpp */,
                                B27535420B053814002CE64F /* Image.h */,
-                               B2BFB59F0B22F76200567E80 /* ImageAnimationObserver.h */,
                                B23BCCE40B3829C9005B2415 /* ImageBuffer.cpp */,
                                B2A10B910B3818BD00099AA4 /* ImageBuffer.h */,
+                               BC7F44A70B9E324E00A9D081 /* ImageObserver.h */,
                                B27535430B053814002CE64F /* ImageSource.h */,
                                B27535440B053814002CE64F /* IntPoint.h */,
                                B27535450B053814002CE64F /* IntRect.cpp */,
                                06027CAD0B1CBFC000884B2D /* ContextMenuItem.h in Headers */,
                                B2310B770B1F46A200D55D87 /* CgSupport.h in Headers */,
                                932871C00B20DEB70049035A /* PlatformMenuDescription.h in Headers */,
-                               B2BFB5A00B22F76200567E80 /* ImageAnimationObserver.h in Headers */,
                                E1D8E3160B29E39C00F4BAF6 /* HTTPParsers.h in Headers */,
                                BC18C5D00B2A886F0018461D /* TextBreakIterator.h in Headers */,
                                AA4C3A770B2B1679002334A2 /* StyleElement.h in Headers */,
                                06A6A73D0B8BA44800DF1703 /* StringTruncator.h in Headers */,
                                934D9BA70B8C1175007B42A9 /* WebCoreNSStringExtras.h in Headers */,
                                51DF6D7E0B92A16D00C2DC85 /* ThreadCheck.h in Headers */,
+                               BC7F44A80B9E324E00A9D081 /* ImageObserver.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 88d4bf1..fa34fd8 100644 (file)
@@ -89,14 +89,10 @@ CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Typ
     CachedResource* resource = m_resources.get(url.url());
 
     if (resource) {
-        if (!skipCanLoadCheck
-         && FrameLoader::restrictAccessToLocal()
-         && !FrameLoader::canLoad(*resource, docLoader->doc()))
+        if (!skipCanLoadCheck && FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(*resource, docLoader->doc()))
             return 0;
     } else {
-        if (!skipCanLoadCheck
-         && FrameLoader::restrictAccessToLocal()
-         && !FrameLoader::canLoad(url, docLoader->doc()))
+        if (!skipCanLoadCheck && FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(url, docLoader->doc()))
             return 0;
 
         // The resource does not exist.  Create it.
@@ -135,9 +131,30 @@ void Cache::prune()
     if (unreferencedResourcesSize < m_maximumSize)
         return;
 
-    bool canShrinkLRULists = true;
+    // Our first pass over the objects in the cache will destroy any decoded data in unreferenced objects.
     unsigned size = m_lruLists.size();
     for (int i = size - 1; i >= 0; i--) {
+        // Start from the tail, since this is the least frequently accessed of the objects.
+        CachedResource* current = m_lruLists[i].m_tail;
+        while (current) {
+            CachedResource* prev = current->m_prevInLRUList;
+            if (!current->referenced()) {
+                // Go ahead and destroy our decoded data.
+                current->destroyDecodedData();
+                
+                // Stop pruning if our total cache size is back under the maximum or if every
+                // remaining object in the cache is live (meaning there is nothing left we are able
+                // to prune).
+                if (m_currentSize <= m_maximumSize || m_currentSize == m_liveResourcesSize)
+                    return;
+            }
+            current = prev;
+        }
+    }
+
+    // Our second pass over the objects in the cache will actually evict objects from the cache.
+    bool canShrinkLRULists = true;
+    for (int i = size - 1; i >= 0; i--) {
         // Remove from the tail, since this is the least frequently accessed of the objects.
         CachedResource* current = m_lruLists[i].m_tail;
         while (current) {
@@ -188,9 +205,9 @@ void Cache::remove(CachedResource* resource)
             (*itr)->removeCachedResource(resource);
 
         // Subtract from our size totals.
-        m_currentSize -= resource->size();
-        if (resource->referenced())
-            m_liveResourcesSize -= resource->size();
+        int delta = -resource->size();
+        if (delta)
+            adjustSize(resource->referenced(), delta);
     }
 
     if (resource->canDelete())
@@ -228,7 +245,7 @@ static inline unsigned fastLog2(unsigned i)
 LRUList* Cache::lruListFor(CachedResource* resource)
 {
     unsigned accessCount = max(resource->accessCount(), 1U);
-    unsigned queueIndex = fastLog2(resource->size() / accessCount);
+    unsigned queueIndex = fastLog2(resource->encodedSize() / accessCount);
 #ifndef NDEBUG
     resource->m_lruIndex = queueIndex;
 #endif
@@ -327,15 +344,11 @@ void Cache::resourceAccessed(CachedResource* resource)
     insertInLRUList(resource);
 }
 
-void Cache::adjustSize(bool live, unsigned oldResourceSize, unsigned newResourceSize)
+void Cache::adjustSize(bool live, int delta)
 {
-    m_currentSize -= oldResourceSize;
-    if (live)
-        m_liveResourcesSize -= oldResourceSize;
-        
-    m_currentSize += newResourceSize;
+    m_currentSize += delta;
     if (live)
-        m_liveResourcesSize += newResourceSize;
+        m_liveResourcesSize += delta;
 }
 
 Cache::Statistics Cache::getStatistics()
index 805d9b6..fbcef3b 100644 (file)
@@ -93,7 +93,7 @@ public:
     void removeFromLRUList(CachedResource*);
 
     // Called to adjust the cache totals when a resource changes size.
-    void adjustSize(bool live, unsigned oldResourceSize, unsigned newResourceSize);
+    void adjustSize(bool live, int delta);
 
     // Track the size of all resources that are in the cache and still referenced by a Web page. 
     void addToLiveObjectSize(unsigned s) { m_liveResourcesSize += s; }
index a359670..95d5e93 100644 (file)
@@ -72,8 +72,8 @@ void CachedCSSStyleSheet::data(Vector<char>& data, bool allDataReceived)
     if (!allDataReceived)
         return;
 
-    setSize(data.size());
-    m_sheet = m_decoder->decode(data.data(), size());
+    setEncodedSize(data.size());
+    m_sheet = m_decoder->decode(data.data(), encodedSize());
     m_sheet += m_decoder->flush();
     m_loading = false;
     checkNotify();
index 4db0ef1..37d9ae4 100644 (file)
@@ -53,7 +53,6 @@ namespace WebCore {
 
 CachedImage::CachedImage(DocLoader* docLoader, const String& url, CachePolicy cachePolicy, time_t _expireDate)
     : CachedResource(url, ImageResource, cachePolicy, _expireDate)
-    , m_dataSize(0)
 {
     m_image = 0;
     m_status = Unknown;
@@ -66,7 +65,6 @@ CachedImage::CachedImage(DocLoader* docLoader, const String& url, CachePolicy ca
 
 CachedImage::CachedImage(Image* image)
     : CachedResource(String(), ImageResource, CachePolicyCache, 0)
-    , m_dataSize(0)
 {
     m_image = image;
     m_status = Cached;
@@ -89,6 +87,12 @@ void CachedImage::ref(CachedResourceClient* c)
         c->notifyFinished(this);
 }
 
+void CachedImage::allReferencesRemoved()
+{
+    if (m_image && !m_errorOccurred)
+        m_image->resetAnimation();
+}
+
 static Image* brokenImage()
 {
     static Image* brokenImage;
@@ -135,7 +139,7 @@ void CachedImage::clear()
 {
     delete m_image;
     m_image = 0;
-    setSize(0);
+    setEncodedSize(0);
 }
 
 inline void CachedImage::createImage()
@@ -201,8 +205,6 @@ void CachedImage::data(Vector<char>& data, bool allDataReceived)
 
     bool sizeAvailable = false;
 
-    m_dataSize = data.size();
-
     // Have the image update its data from its internal buffer.
     // It will not do anything now, but will delay decoding until 
     // queried for info (like size or specific image frames).
@@ -221,12 +223,10 @@ void CachedImage::data(Vector<char>& data, bool allDataReceived)
         } else
             notifyObservers();
 
-        // FIXME: An animated GIF with a huge frame count can't have its size properly estimated.  The reason is that we don't
-        // want to decode the image to determine the frame count, so what we do instead is max the projected size of a single
-        // RGBA32 buffer (width*height*4) with the data size.  This will help ensure that large animated GIFs with thousands of
-        // frames are at least given a reasonably large size.
-        IntSize s = imageSize();
-        setSize(max(s.width() * s.height() * 4, m_dataSize));
+        if (m_image) {
+            Vector<char>& imageBuffer = m_image->dataBuffer();
+            setEncodedSize(imageBuffer.size());
+        }
     }
     
     if (allDataReceived) {
@@ -254,7 +254,28 @@ void CachedImage::checkNotify()
         c->notifyFinished(this);
 }
 
-bool CachedImage::shouldStopAnimation(const Image* image)
+void CachedImage::destroyDecodedData()
+{
+    if (m_image && !m_errorOccurred)
+        m_image->destroyDecodedData();
+}
+
+unsigned CachedImage::decodedSize() const
+{
+    if (m_image && !m_errorOccurred)
+        return m_image->decodedSize();
+    return 0;
+}
+
+void CachedImage::decodedSizeChanged(const Image* image, int delta)
+{
+    if (image != m_image)
+        return;
+    
+    cache()->adjustSize(referenced(), delta);
+}
+
+bool CachedImage::shouldPauseAnimation(const Image* image)
 {
     if (image != m_image)
         return false;
index 07e5363..74249b7 100644 (file)
@@ -29,7 +29,7 @@
 #define CachedImage_h
 
 #include "CachedResource.h"
-#include "ImageAnimationObserver.h"
+#include "ImageObserver.h"
 #include "IntRect.h"
 #include <wtf/Vector.h>
 
@@ -39,7 +39,7 @@ class DocLoader;
 class Cache;
 class Image;
 
-class CachedImage : public CachedResource, public ImageAnimationObserver {
+class CachedImage : public CachedResource, public ImageObserver {
 public:
     CachedImage(DocLoader*, const String& url, CachePolicy, time_t expireDate);
     CachedImage(Image*);
@@ -53,6 +53,9 @@ public:
     IntRect imageRect() const;  // The size of the image.
 
     virtual void ref(CachedResourceClient*);
+    
+    virtual void allReferencesRemoved();
+    virtual void destroyDecodedData();
 
     virtual Vector<char>& bufferData(const char* bytes, int addedSize, Request*);
     virtual void data(Vector<char>&, bool allDataReceived);
@@ -66,7 +69,10 @@ public:
 
     void clear();
     
-    virtual bool shouldStopAnimation(const Image* image);
+    virtual unsigned decodedSize() const;
+
+    virtual void decodedSizeChanged(const Image* image, int delta);
+    virtual bool shouldPauseAnimation(const Image* image);
     virtual void animationAdvanced(const Image* image);
 
     bool stillNeedsLoad() const { return !m_errorOccurred && m_status == Unknown && m_loading == false; }
index 6a2b51b..33a269b 100644 (file)
@@ -42,7 +42,7 @@ CachedResource::CachedResource(const String& URL, Type type, CachePolicy cachePo
     m_url = URL;
     m_type = type;
     m_status = Pending;
-    m_size = size;
+    m_encodedSize = size;
     m_inCache = false;
     m_cachePolicy = cachePolicy;
     m_request = 0;
@@ -119,32 +119,33 @@ void CachedResource::deref(CachedResourceClient *c)
     if (canDelete() && !inCache())
         delete this;
     else if (!referenced() && inCache()) {
+        allReferencesRemoved();
         cache()->removeFromLiveObjectSize(size());
         cache()->prune();
     }
 }
 
-void CachedResource::setSize(unsigned size)
+void CachedResource::setEncodedSize(unsigned size)
 {
-    if (size == m_size)
+    if (size == m_encodedSize)
         return;
 
-    unsigned oldSize = m_size;
+    unsigned oldSize = m_encodedSize;
 
     // The object must now be moved to a different queue, since its size has been changed.
-    // We have to remove explicitly before updating m_size, so that we find the correct previous
+    // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous
     // queue.
     if (inCache())
         cache()->removeFromLRUList(this);
     
-    m_size = size;
+    m_encodedSize = size;
    
     if (inCache()) { 
         // Now insert into the new LRU list.
         cache()->insertInLRUList(this);
         
         // Update the cache's size totals.
-        cache()->adjustSize(referenced(), oldSize, size);
+        cache()->adjustSize(referenced(), size - oldSize);
     }
 }
 
index 4bae871..234ee6f 100644 (file)
@@ -67,7 +67,7 @@ public:
         Cached       // regular case
     };
 
-    CachedResource(const String& URL, Type type, CachePolicy cachePolicy, unsigned size = 0);
+    CachedResource(const String& URL, Type type, CachePolicy cachePolicy, unsigned encodedSize = 0);
     virtual ~CachedResource();
 
     virtual void setEncoding(const String&) { }
@@ -81,13 +81,16 @@ public:
     virtual void ref(CachedResourceClient*);
     void deref(CachedResourceClient*);
     bool referenced() const { return !m_clients.isEmpty(); }
+    virtual void allReferencesRemoved() {};
 
     unsigned count() const { return m_clients.size(); }
 
     Status status() const { return m_status; }
 
-    unsigned size() const { return m_size; }
-
+    unsigned size() const { return encodedSize() + decodedSize(); }
+    unsigned encodedSize() const { return m_encodedSize; }
+    virtual unsigned decodedSize() const { return 0; }
+    
     bool isLoaded() const { return !m_loading; }
     void setLoading(bool b) { m_loading = b; }
 
@@ -130,8 +133,10 @@ public:
     bool errorOccurred() const { return m_errorOccurred; }
     bool treatAsLocal() const { return m_shouldTreatAsLocal; }
 
+    virtual void destroyDecodedData() {};
+
 protected:
-    void setSize(unsigned size);
+    void setEncodedSize(unsigned);
 
     HashSet<CachedResourceClient*> m_clients;
 
@@ -148,7 +153,7 @@ protected:
     bool m_errorOccurred;
 
 private:
-    unsigned m_size;
+    unsigned m_encodedSize;
     unsigned m_accessCount;
 
 protected:
index 1be29ad..89637fc 100644 (file)
@@ -75,8 +75,8 @@ void CachedScript::data(Vector<char>& data, bool allDataReceived)
     if (!allDataReceived)
         return;
 
-    setSize(data.size());
-    m_script = m_encoding.decode(data.data(), size());
+    setEncodedSize(data.size());
+    m_script = m_encoding.decode(data.data(), encodedSize());
     m_loading = false;
     checkNotify();
 }
index bdd88e4..f12806b 100644 (file)
@@ -71,8 +71,8 @@ void CachedXSLStyleSheet::data(Vector<char>& data, bool allDataReceived)
     if (!allDataReceived)
         return;
 
-    setSize(data.size());
-    m_sheet = String(m_decoder->decode(data.data(), size()));
+    setEncodedSize(data.size());
+    m_sheet = String(m_decoder->decode(data.data(), encodedSize()));
     m_sheet += m_decoder->flush();
     m_loading = false;
     checkNotify();
index 6582984..3550f31 100644 (file)
@@ -28,7 +28,7 @@
 #include "BitmapImage.h"
 
 #include "FloatRect.h"
-#include "ImageAnimationObserver.h"
+#include "ImageObserver.h"
 #include "IntRect.h"
 #include "PlatformString.h"
 #include "Timer.h"
@@ -37,7 +37,7 @@
 
 namespace WebCore {
 
-BitmapImage::BitmapImage(ImageAnimationObserver* observer)
+BitmapImage::BitmapImage(ImageObserver* observer)
     : Image(observer)
     , m_currentFrame(0)
     , m_frames(0)
@@ -49,23 +49,40 @@ BitmapImage::BitmapImage(ImageAnimationObserver* observer)
     , m_animationFinished(false)
     , m_haveSize(false)
     , m_sizeAvailable(false)
+    , m_decodedSize(0)
 {
     initPlatformData();
 }
 
 BitmapImage::~BitmapImage()
 {
-    invalidateData();
+    destroyDecodedData();
     stopAnimation();
 }
 
-void BitmapImage::invalidateData()
+void BitmapImage::destroyDecodedData(bool incremental)
 {
     // Destroy the cached images and release them.
     if (m_frames.size()) {
-        m_frames.last().clear();
+        int sizeChange = 0;
+        int frameSize = m_size.width() * m_size.height() * 4;
+        for (unsigned i = incremental ? m_frames.size() - 1 : 0; i < m_frames.size(); i++) {
+            if (m_frames[i].m_frame) {
+                sizeChange -= frameSize;
+                m_frames[i].clear();
+            }
+        }
+
+        // We just always invalidate our platform data, even in the incremental case.
+        // This could be better, but it's not a big deal.
         m_isSolidColor = false;
         invalidatePlatformData();
+        
+        if (sizeChange) {
+            m_decodedSize += sizeChange;
+            if (imageObserver())
+                imageObserver()->decodedSizeChanged(this, sizeChange);
+        }
     }
 }
 
@@ -83,12 +100,19 @@ void BitmapImage::cacheFrame(size_t index)
         m_frames.resize(numFrames);
 
     m_frames[index].m_frame = m_source.createFrameAtIndex(index);
-    if (m_frames[index].m_frame)
+    if (numFrames == 1 && m_frames[index].m_frame)
         checkForSolidColor();
 
     if (shouldAnimate())
         m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
     m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
+    
+    int sizeChange = m_size.width() * m_size.height() * 4;
+    if (sizeChange) {
+        m_decodedSize += sizeChange;
+        if (imageObserver())
+            imageObserver()->decodedSizeChanged(this, sizeChange);
+    }
 }
 
 IntSize BitmapImage::size() const
@@ -102,7 +126,7 @@ IntSize BitmapImage::size() const
 
 bool BitmapImage::setNativeData(NativeBytePtr data, bool allDataReceived)
 {
-    invalidateData();
+    destroyDecodedData(true);
     
     // Feed all the data we've seen so far to the image decoder.
     m_source.setData(data, allDataReceived);
@@ -162,7 +186,7 @@ bool BitmapImage::frameHasAlphaAtIndex(size_t index)
 
 bool BitmapImage::shouldAnimate()
 {
-    return (m_animatingImageType && !m_animationFinished && animationObserver());
+    return (m_animatingImageType && !m_animationFinished && imageObserver());
 }
 
 void BitmapImage::startAnimation()
@@ -190,22 +214,21 @@ void BitmapImage::resetAnimation()
     m_animationFinished = false;
 }
 
-
 void BitmapImage::advanceAnimation(Timer<BitmapImage>* timer)
 {
     // Stop the animation.
     stopAnimation();
     
     // See if anyone is still paying attention to this animation.  If not, we don't
-    // advance and will simply pause the animation.
-    if (animationObserver()->shouldStopAnimation(this))
+    // advance and will remain suspended at the current frame until the animation is resumed.
+    if (imageObserver()->shouldPauseAnimation(this))
         return;
 
     m_currentFrame++;
     if (m_currentFrame >= frameCount()) {
         m_repetitionsComplete += 1;
         if (m_repetitionCount && m_repetitionsComplete >= m_repetitionCount) {
-            m_animationFinished = false;
+            m_animationFinished = true;
             m_currentFrame--;
             return;
         }
@@ -213,7 +236,7 @@ void BitmapImage::advanceAnimation(Timer<BitmapImage>* timer)
     }
 
     // Notify our observer that the animation has advanced.
-    animationObserver()->animationAdvanced(this);
+    imageObserver()->animationAdvanced(this);
         
     // Kick off a timer to move to the next frame.
     m_frameTimer = new Timer<BitmapImage>(this, &BitmapImage::advanceAnimation);
index 204455f..45852f4 100644 (file)
@@ -89,7 +89,7 @@ struct FrameData : Noncopyable {
 class BitmapImage : public Image {
     friend class GraphicsContext;
 public:
-    BitmapImage(ImageAnimationObserver* = 0);
+    BitmapImage(ImageObserver* = 0);
     ~BitmapImage();
     
     virtual IntSize size() const;
@@ -102,6 +102,8 @@ public:
     virtual void stopAnimation();
     virtual void resetAnimation();
     
+    virtual unsigned decodedSize() const { return m_decodedSize; }
+
 #if PLATFORM(MAC)
     // Accessors for native image formats.
     virtual NSImage* getNSImage();
@@ -137,8 +139,9 @@ private:
     // Decodes and caches a frame. Never accessed except internally.
     void cacheFrame(size_t index);
 
-    // Called to invalidate all our cached data when more bytes are available.
-    void invalidateData();
+    // Called to invalidate all our cached data.  If an image is loading incrementally, we only
+    // invalidate the last cached frame.
+    virtual void destroyDecodedData(bool incremental = false);
 
     // Whether or not size is available yet.    
     bool isSizeAvailable();
@@ -151,7 +154,7 @@ private:
     // Handle platform-specific data
     void initPlatformData();
     void invalidatePlatformData();
-
+    
     // Checks to see if the image is a 1x1 solid color.  We optimize these images and just do a fill rect instead.
     void checkForSolidColor();
     
@@ -181,6 +184,7 @@ private:
 
     mutable bool m_haveSize; // Whether or not our |m_size| member variable has the final overall image size yet.
     bool m_sizeAvailable; // Whether or not we can obtain the size of the first image frame yet from ImageIO.
+    unsigned m_decodedSize; // The current size of all decoded frames.
 };
 
 }
index 9e2c21b..9cfafdc 100644 (file)
@@ -40,8 +40,8 @@
 
 namespace WebCore {
 
-Image::Image(ImageAnimationObserver* observer)
-    : m_animationObserver(observer)
+Image::Image(ImageObserver* observer)
+    : m_imageObserver(observer)
 {
 }
 
index f452ce1..0f4b5a6 100644 (file)
@@ -62,13 +62,13 @@ class IntRect;
 class IntSize;
 class String;
 
-// This class gets notified when an image advances animation frames.
-class ImageAnimationObserver;
+// This class gets notified when an image creates or destroys decoded frames and when it advances animation frames.
+class ImageObserver;
 
 class Image : Noncopyable {
     friend class GraphicsContext;
 public:
-    Image(ImageAnimationObserver* = 0);
+    Image(ImageObserver* = 0);
     virtual ~Image();
     
     static Image* loadPlatformResource(const char* name);
@@ -84,6 +84,11 @@ public:
     virtual bool setData(bool allDataReceived);
     virtual bool setNativeData(NativeBytePtr, bool allDataReceived) { return false; }
     
+    // FIXME: PDF/SVG will be underreporting decoded sizes and will be unable to prune because these functions are not
+    // implemented yet for those image types.
+    virtual void destroyDecodedData(bool incremental = false) {};
+    virtual unsigned decodedSize() const { return 0; }
+
     Vector<char>& dataBuffer() { return m_data; }
 
     // It may look unusual that there is no start animation call as public API.  This is because
@@ -93,7 +98,7 @@ public:
     virtual void resetAnimation() {}
     
     // Typically the CachedImage that owns us.
-    ImageAnimationObserver* animationObserver() const { return m_animationObserver; }
+    ImageObserver* imageObserver() const { return m_imageObserver; }
 
     enum TileRule { StretchTile, RepeatTile };
     
@@ -140,7 +145,7 @@ private:
 #endif
     
     Vector<char> m_data; // The encoded raw data for the image. 
-    ImageAnimationObserver* m_animationObserver;
+    ImageObserver* m_imageObserver;
 };
 
 }
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#ifndef ImageAnimationObserver_h
-#define ImageAnimationObserver_h
+#ifndef ImageObserver_h
+#define ImageObserver_h
 
 namespace WebCore {
 
 class Image;
 
 // This class gets notified when an image advances animation frames.
-class ImageAnimationObserver {
+class ImageObserver {
 protected:
-    virtual ~ImageAnimationObserver() {}
+    virtual ~ImageObserver() {}
 public:
-    virtual bool shouldStopAnimation(const Image*) = 0;
+    virtual void decodedSizeChanged(const Image*, int delta) = 0;
+
+    virtual bool shouldPauseAnimation(const Image*) = 0;
     virtual void animationAdvanced(const Image*) = 0;
 };
 
index 3ce820d..5dca240 100644 (file)
@@ -45,7 +45,7 @@
 
 namespace WebCore {
 
-SVGImage::SVGImage(ImageAnimationObserver* observer)
+SVGImage::SVGImage(ImageObserver* observer)
     : Image(observer)
     , m_document(0)
     , m_page(0)
index efda801..c390492 100644 (file)
@@ -42,7 +42,7 @@ namespace WebCore {
     
     class SVGImage : public Image {
     public:
-        SVGImage(ImageAnimationObserver*);
+        SVGImage(ImageObserver*);
         ~SVGImage();
         
         virtual IntSize size() const;