Source/WebCore/ChangeLog

 12012-06-11 Xianzhu Wang <wangxianzhu@chromium.org>
 2
 3 SVGImageCache leaks image data
 4 https://bugs.webkit.org/show_bug.cgi?id=87792
 5
 6 There are two functions to remove a client from a CachedImage:
 7 - CachedResource::removeClient()
 8 - CachedImage::removeClientForRenderer().
 9 It's easy to make error to call the former which will leak the cached
 10 image buffers in SVGImageCache.
 11
 12 This change combined the two by adding the virtual
 13 CachedResource::didRemoveClient(). CachedImage will do SVGImageCache
 14 cleanup in the function.
 15
 16 Reviewed by Nikolas Zimmermann.
 17
 18 Test: svg/as-image/svg-image-leak-cached-data.html
 19
 20 * loader/cache/CachedFont.h:
 21 (WebCore::CachedFontClient::resourceClientType): Added 'const'.
 22 * loader/cache/CachedImage.cpp:
 23 (WebCore):
 24 (WebCore::CachedImage::didRemoveClient): Removes the client from SVGImageCache.
 25 (WebCore::CachedImage::lookupOrCreateImageForRenderer):
 26 * loader/cache/CachedImage.h:
 27 (CachedImage):
 28 (WebCore::CachedImageClient::resourceClientType): Added 'const'.
 29 * loader/cache/CachedRawResource.h:
 30 (WebCore::CachedRawResourceClient::resourceClientType): Added 'const'.
 31 * loader/cache/CachedResource.cpp:
 32 (WebCore::CachedResource::removeClient): Added invocation of didRemoveClient().
 33 * loader/cache/CachedResource.h:
 34 (WebCore::CachedResource::didRemoveClient): Added for subclasses to do additional works.
 35 * loader/cache/CachedResourceClient.h:
 36 (WebCore::CachedResourceClient::resourceClientType): Added 'const'.
 37 * loader/cache/CachedSVGDocument.h:
 38 (WebCore::CachedSVGDocumentClient::resourceClientType): Added 'const'.
 39 * loader/cache/CachedStyleSheetClient.h:
 40 (WebCore::CachedStyleSheetClient::resourceClientType): Added 'const'.
 41 * rendering/style/StyleCachedImage.cpp:
 42 (WebCore::StyleCachedImage::removeClient):
 43 * rendering/style/StyleCachedImageSet.cpp:
 44 (WebCore::StyleCachedImageSet::removeClient):
 45 * svg/graphics/SVGImageCache.cpp:
 46 (WebCore::SVGImageCache::~SVGImageCache): Added checking for leaks.
 47 (WebCore::SVGImageCache::removeClientFromCache):
 48 (WebCore::SVGImageCache::setRequestedSizeAndScales):
 49 (WebCore::SVGImageCache::requestedSizeAndScales):
 50 (WebCore::SVGImageCache::lookupOrCreateBitmapImageForClient):
 51 * svg/graphics/SVGImageCache.h:
 52 (WebCore):
 53 (SVGImageCache):
 54
1552012-06-11 David Barr <davidbarr@chromium.org>
256
357 Add css3-images image-resolution (dppx only)
119986

Source/WebCore/loader/cache/CachedFont.h

@@class CachedFontClient : public CachedRe
8585public:
8686 virtual ~CachedFontClient() { }
8787 static CachedResourceClientType expectedType() { return FontType; }
88  virtual CachedResourceClientType resourceClientType() { return expectedType(); }
 88 virtual CachedResourceClientType resourceClientType() const { return expectedType(); }
8989 virtual void fontLoaded(CachedFont*) { }
9090};
9191
119986

Source/WebCore/loader/cache/CachedImage.cpp

@@void CachedImage::load(CachedResourceLoa
9292 setLoading(false);
9393}
9494
95 void CachedImage::removeClientForRenderer(RenderObject* renderer)
96 {
97 #if ENABLE(SVG)
98  if (m_svgImageCache)
99  m_svgImageCache->removeRendererFromCache(renderer);
100 #endif
101  removeClient(renderer);
102 }
103 
10495void CachedImage::didAddClient(CachedResourceClient* c)
10596{
10697 if (m_decodedDataDeletionTimer.isActive())

@@void CachedImage::didAddClient(CachedRes
118109 CachedResource::didAddClient(c);
119110}
120111
 112void CachedImage::didRemoveClient(CachedResourceClient* c)
 113{
 114 ASSERT(c->resourceClientType() == CachedImageClient::expectedType());
 115#if ENABLE(SVG)
 116 if (m_svgImageCache)
 117 m_svgImageCache->removeClientFromCache(static_cast<CachedImageClient*>(c));
 118#endif
 119
 120 CachedResource::didRemoveClient(c);
 121}
 122
121123void CachedImage::allClientsRemoved()
122124{
123125 if (m_image && !errorOccurred())

@@inline Image* CachedImage::lookupOrCreat
149151 return 0;
150152 if (!m_image->isSVGImage())
151153 return m_image.get();
152  Image* useImage = m_svgImageCache->lookupOrCreateBitmapImageForRenderer(renderer);
 154 Image* useImage = m_svgImageCache->lookupOrCreateBitmapImageForClient(renderer);
153155 if (useImage == Image::nullImage())
154156 return m_image.get();
155157 return useImage;
119986

Source/WebCore/loader/cache/CachedImage.h

@@public:
6767 IntSize imageSizeForRenderer(const RenderObject*, float multiplier); // returns the size of the complete image.
6868 void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio);
6969
70  void removeClientForRenderer(RenderObject*);
7170 virtual void didAddClient(CachedResourceClient*);
72 
 71 virtual void didRemoveClient(CachedResourceClient*);
 72
7373 virtual void allClientsRemoved();
7474 virtual void destroyDecodedData();
7575

@@class CachedImageClient : public CachedR
118118public:
119119 virtual ~CachedImageClient() { }
120120 static CachedResourceClientType expectedType() { return ImageType; }
121  virtual CachedResourceClientType resourceClientType() { return expectedType(); }
 121 virtual CachedResourceClientType resourceClientType() const { return expectedType(); }
122122
123123 // Called whenever a frame of an image changes, either because we got more data from the network or
124124 // because we are animating. If not null, the IntRect is the changed rect of the image.
119986

Source/WebCore/loader/cache/CachedRawResource.h

@@class CachedRawResourceClient : public C
6666public:
6767 virtual ~CachedRawResourceClient() { }
6868 static CachedResourceClientType expectedType() { return RawResourceType; }
69  virtual CachedResourceClientType resourceClientType() { return expectedType(); }
 69 virtual CachedResourceClientType resourceClientType() const { return expectedType(); }
7070
7171 virtual void dataSent(CachedResource*, unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { }
7272 virtual void responseReceived(CachedResource*, const ResourceResponse&) { }
119986

Source/WebCore/loader/cache/CachedResource.cpp

@@void CachedResource::removeClient(Cached
426426 } else {
427427 ASSERT(m_clients.contains(client));
428428 m_clients.remove(client);
 429 didRemoveClient(client);
429430 }
430431
431432 if (canDelete() && !inCache())
119986

Source/WebCore/loader/cache/CachedResource.h

@@public:
126126 PreloadResult preloadResult() const { return static_cast<PreloadResult>(m_preloadResult); }
127127
128128 virtual void didAddClient(CachedResourceClient*);
 129 virtual void didRemoveClient(CachedResourceClient*) { }
129130 virtual void allClientsRemoved() { }
130131
131132 unsigned count() const { return m_clients.size(); }
119986

Source/WebCore/loader/cache/CachedResourceClient.h

@@public:
5050 virtual void didReceiveData(CachedResource*) { };
5151
5252 static CachedResourceClientType expectedType() { return BaseResourceType; }
53  virtual CachedResourceClientType resourceClientType() { return expectedType(); }
 53 virtual CachedResourceClientType resourceClientType() const { return expectedType(); }
5454
5555protected:
5656 CachedResourceClient() { }
119986

Source/WebCore/loader/cache/CachedSVGDocument.h

@@class CachedSVGDocumentClient : public C
5252public:
5353 virtual ~CachedSVGDocumentClient() { }
5454 static CachedResourceClientType expectedType() { return SVGDocumentType; }
55  virtual CachedResourceClientType resourceClientType() { return expectedType(); }
 55 virtual CachedResourceClientType resourceClientType() const { return expectedType(); }
5656};
5757
5858}
119986

Source/WebCore/loader/cache/CachedStyleSheetClient.h

@@class CachedStyleSheetClient : public Ca
3636public:
3737 virtual ~CachedStyleSheetClient() { }
3838 static CachedResourceClientType expectedType() { return StyleSheetType; }
39  virtual CachedResourceClientType resourceClientType() { return expectedType(); }
 39 virtual CachedResourceClientType resourceClientType() const { return expectedType(); }
4040 virtual void setCSSStyleSheet(const String& /* href */, const KURL& /* baseURL */, const String& /* charset */, const CachedCSSStyleSheet*) { }
4141 virtual void setXSLStyleSheet(const String& /* href */, const KURL& /* baseURL */, const String& /* sheet */) { }
4242};
119986

Source/WebCore/rendering/style/StyleCachedImage.cpp

@@void StyleCachedImage::addClient(RenderO
9797
9898void StyleCachedImage::removeClient(RenderObject* renderer)
9999{
100  m_image->removeClientForRenderer(renderer);
 100 m_image->removeClient(renderer);
101101}
102102
103103PassRefPtr<Image> StyleCachedImage::image(RenderObject* renderer, const IntSize&) const
119986

Source/WebCore/rendering/style/StyleCachedImageSet.cpp

@@void StyleCachedImageSet::addClient(Rend
108108
109109void StyleCachedImageSet::removeClient(RenderObject* renderer)
110110{
111  m_bestFitImage->removeClientForRenderer(renderer);
 111 m_bestFitImage->removeClient(renderer);
112112}
113113
114114PassRefPtr<Image> StyleCachedImageSet::image(RenderObject* renderer, const IntSize&) const
119986

Source/WebCore/svg/graphics/SVGImageCache.cpp

2121#include "SVGImageCache.h"
2222
2323#if ENABLE(SVG)
 24#include "CachedImage.h"
2425#include "FrameView.h"
2526#include "GraphicsContext.h"
2627#include "ImageBuffer.h"

@@SVGImageCache::~SVGImageCache()
4142 m_sizeAndScalesMap.clear();
4243
4344 ImageDataMap::iterator end = m_imageDataMap.end();
44  for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it)
 45 for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it) {
 46 // Checks if the client (it->first) is still valid. The client should remove itself from this
 47 // cache before its end of life, otherwise the following ASSERT will crash on pure virtual
 48 // function call or a general crash.
 49 ASSERT(it->first->resourceClientType() == CachedImageClient::expectedType());
4550 delete it->second.buffer;
 51 }
4652
4753 m_imageDataMap.clear();
4854}
4955
50 void SVGImageCache::removeRendererFromCache(const RenderObject* renderer)
 56void SVGImageCache::removeClientFromCache(const CachedImageClient* client)
5157{
52  ASSERT(renderer);
53  m_sizeAndScalesMap.remove(renderer);
 58 ASSERT(client);
 59 m_sizeAndScalesMap.remove(client);
5460
55  ImageDataMap::iterator it = m_imageDataMap.find(renderer);
 61 ImageDataMap::iterator it = m_imageDataMap.find(client);
5662 if (it == m_imageDataMap.end())
5763 return;
5864

@@void SVGImageCache::removeRendererFromCa
6066 m_imageDataMap.remove(it);
6167}
6268
63 void SVGImageCache::setRequestedSizeAndScales(const RenderObject* renderer, const SizeAndScales& sizeAndScales)
 69void SVGImageCache::setRequestedSizeAndScales(const CachedImageClient* client, const SizeAndScales& sizeAndScales)
6470{
65  ASSERT(renderer);
 71 ASSERT(client);
6672 ASSERT(!sizeAndScales.size.isEmpty());
67  m_sizeAndScalesMap.set(renderer, sizeAndScales);
 73 m_sizeAndScalesMap.set(client, sizeAndScales);
6874}
6975
70 SVGImageCache::SizeAndScales SVGImageCache::requestedSizeAndScales(const RenderObject* renderer) const
 76SVGImageCache::SizeAndScales SVGImageCache::requestedSizeAndScales(const CachedImageClient* client) const
7177{
72  ASSERT(renderer);
73  SizeAndScalesMap::const_iterator it = m_sizeAndScalesMap.find(renderer);
 78 ASSERT(client);
 79 SizeAndScalesMap::const_iterator it = m_sizeAndScalesMap.find(client);
7480 if (it == m_sizeAndScalesMap.end())
7581 return SizeAndScales();
7682 return it->second;

@@void SVGImageCache::redrawTimerFired(Tim
122128 redraw();
123129}
124130
125 Image* SVGImageCache::lookupOrCreateBitmapImageForRenderer(const RenderObject* renderer)
 131Image* SVGImageCache::lookupOrCreateBitmapImageForClient(const CachedImageClient* client)
126132{
127  ASSERT(renderer);
 133 ASSERT(client);
128134
129  // The cache needs to know the size of the renderer before querying an image for it.
130  SizeAndScalesMap::iterator sizeIt = m_sizeAndScalesMap.find(renderer);
 135 // The cache needs to know the size of the client before querying an image for it.
 136 SizeAndScalesMap::iterator sizeIt = m_sizeAndScalesMap.find(client);
131137 if (sizeIt == m_sizeAndScalesMap.end())
132138 return Image::nullImage();
133139

@@Image* SVGImageCache::lookupOrCreateBitm
136142 float scale = sizeIt->second.scale;
137143 ASSERT(!size.isEmpty());
138144
139  // Lookup image for renderer in cache and eventually update it.
140  ImageDataMap::iterator it = m_imageDataMap.find(renderer);
 145 // Lookup image for client in cache and eventually update it.
 146 ImageDataMap::iterator it = m_imageDataMap.find(client);
141147 if (it != m_imageDataMap.end()) {
142148 ImageData& data = it->second;
143149

@@Image* SVGImageCache::lookupOrCreateBitm
145151 if (data.sizeAndScales.size == size && data.sizeAndScales.zoom == zoom && data.sizeAndScales.scale == scale)
146152 return data.image.get();
147153
148  // If the image size for the renderer changed, we have to delete the buffer, remove the item from the cache and recreate it.
 154 // If the image size for the client changed, we have to delete the buffer, remove the item from the cache and recreate it.
149155 delete data.buffer;
150156 m_imageDataMap.remove(it);
151157 }

@@Image* SVGImageCache::lookupOrCreateBitm
164170 Image* newImagePtr = newImage.get();
165171 ASSERT(newImagePtr);
166172
167  m_imageDataMap.add(renderer, ImageData(newBuffer.leakPtr(), newImage.release(), sizeIt->second));
 173 m_imageDataMap.add(client, ImageData(newBuffer.leakPtr(), newImage.release(), sizeIt->second));
168174 return newImagePtr;
169175}
170176
119986

Source/WebCore/svg/graphics/SVGImageCache.h

3131namespace WebCore {
3232
3333class CachedImage;
 34class CachedImageClient;
3435class ImageBuffer;
35 class RenderObject;
3636class SVGImage;
3737
3838class SVGImageCache {

@@public:
6363 float scale;
6464 };
6565
66  void removeRendererFromCache(const RenderObject*);
 66 void removeClientFromCache(const CachedImageClient*);
6767
68  void setRequestedSizeAndScales(const RenderObject*, const SizeAndScales&);
69  SizeAndScales requestedSizeAndScales(const RenderObject*) const;
 68 void setRequestedSizeAndScales(const CachedImageClient*, const SizeAndScales&);
 69 SizeAndScales requestedSizeAndScales(const CachedImageClient*) const;
7070
71  Image* lookupOrCreateBitmapImageForRenderer(const RenderObject*);
 71 Image* lookupOrCreateBitmapImageForClient(const CachedImageClient*);
7272 void imageContentChanged();
7373
7474private:

@@private:
9898 RefPtr<Image> image;
9999 };
100100
101  typedef HashMap<const RenderObject*, SizeAndScales> SizeAndScalesMap;
102  typedef HashMap<const RenderObject*, ImageData> ImageDataMap;
 101 typedef HashMap<const CachedImageClient*, SizeAndScales> SizeAndScalesMap;
 102 typedef HashMap<const CachedImageClient*, ImageData> ImageDataMap;
103103
104104 SVGImage* m_svgImage;
105105 SizeAndScalesMap m_sizeAndScalesMap;
119986

LayoutTests/ChangeLog

 12012-06-11 Xianzhu Wang <wangxianzhu@chromium.org>
 2
 3 SVGImageCache leaks image data
 4 https://bugs.webkit.org/show_bug.cgi?id=87792
 5
 6 Reviewed by Nikolas Zimmermann.
 7
 8 New test case.
 9
 10 * svg/as-image/svg-image-leak-cached-data-expected.txt: Added.
 11 * svg/as-image/svg-image-leak-cached-data.html: Added.
 12
1132012-06-11 Ryosuke Niwa <rniwa@webkit.org>
214
315 Use testRunner instead of layoutTestController in animations tests
119986

LayoutTests/svg/as-image/svg-image-leak-cached-data-expected.txt

 1This test checks if SVGImageCache leaks SVG image data as reported in https://bugs.webkit.org/show_bug.cgi?id=87792. Its layout has no particular meaning. The test will cause crash of debug version when leaks of SVG image data is detected.
 2
 3Note: the code detects leaks of SVG image data on destruction of SVGImageCache, which doesn't work on platforms that DumpRenderTree leaks the cache itself.
 4
 5
0

LayoutTests/svg/as-image/svg-image-leak-cached-data.html

 1<html>
 2<head>
 3<script>
 4if (window.layoutTestController) {
 5 layoutTestController.dumpAsText();
 6 layoutTestController.waitUntilDone();
 7}
 8
 9var count = 0;
 10function test() {
 11 var img = document.getElementById('img');
 12 document.body.replaceChild(img.cloneNode(), img);
 13 if (++count < 10)
 14 setTimeout(test, 0);
 15 else if (window.layoutTestController)
 16 layoutTestController.notifyDone();
 17}
 18</script>
 19</head>
 20
 21<body onload='test()'>
 22 <p>This test checks if SVGImageCache leaks SVG image data as reported in
 23 https://bugs.webkit.org/show_bug.cgi?id=87792. Its layout has no particular meaning.
 24 The test will cause crash of debug version when leaks of SVG image data is detected.</p>
 25 <p>Note: the code detects leaks of SVG image data on destruction of SVGImageCache,
 26 which doesn't work on platforms that DumpRenderTree leaks the cache itself.</p>
 27 <img id='img' src='resources/circle.svg'>
 28</body>
 29</html>
0