Fix <rdar://problem/5365030> calling dataWithPDFInsideRect on an SVG with a gradient...
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 14 Nov 2007 03:48:44 +0000 (03:48 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 14 Nov 2007 03:48:44 +0000 (03:48 +0000)
Reviewed by Anders.

When drawing directly to PDF CG may delay the use of the gradient function until outside our
standard drawing path, which in turn could let us invalidate the caches before they were used.

To work around this we now store the cached stops in a RefCounted object, so that we can ensure
that cache exists as long as required.

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

WebCore/ChangeLog
WebCore/platform/graphics/svg/SVGPaintServerGradient.cpp
WebCore/platform/graphics/svg/SVGPaintServerGradient.h
WebCore/platform/graphics/svg/cg/SVGPaintServerGradientCg.cpp

index 222ffc096ea10f14b400a581a788f42c38dbfffd..8a95492f2c87ae1a79c6da47435912510338a580 100644 (file)
@@ -1,3 +1,24 @@
+2007-11-13  Oliver Hunt  <oliver@apple.com>
+
+        Reviewed by Anders.
+
+        <rdar://problem/5365030> calling dataWithPDFInsideRect on an SVG with a gradient crashes (14780)
+
+        When drawing directly to PDF CG may delay the use of the gradient function until outside our
+        standard drawing path, which in turn could let us invalidate the caches before they were used.
+
+        To work around this we now store the cached stops in a RefCounted object, so that we can ensure
+        that cache exists as long as required.
+        
+        * platform/graphics/svg/SVGPaintServerGradient.cpp:
+        (WebCore::SVGPaintServerGradient::SVGPaintServerGradient):
+        * platform/graphics/svg/SVGPaintServerGradient.h:
+        * platform/graphics/svg/cg/SVGPaintServerGradientCg.cpp:
+        (WebCore::cgGradientCallback):
+        (WebCore::CGShadingRefForLinearGradient):
+        (WebCore::CGShadingRefForRadialGradient):
+        (WebCore::SVGPaintServerGradient::updateQuartzGradientStopsCache):
+
 2007-11-13  Anders Carlsson  <andersca@apple.com>
 
         Fix Windows build.
index a41407fb2b7e63fd3cfdaa986265f25968691985..6a701b8445e24b912dfe3d0184404946ac2b71ee 100644 (file)
@@ -66,7 +66,6 @@ SVGPaintServerGradient::SVGPaintServerGradient(const SVGGradientElement* owner)
 
 #if PLATFORM(CG)
     , m_stopsCache(0)
-    , m_stopsCount(0)
     , m_shadingCache(0)
     , m_savedContext(0)
     , m_imageBuffer(0)
@@ -78,7 +77,6 @@ SVGPaintServerGradient::SVGPaintServerGradient(const SVGGradientElement* owner)
 SVGPaintServerGradient::~SVGPaintServerGradient()
 {
 #if PLATFORM(CG)
-    delete m_stopsCache;
     CGShadingRelease(m_shadingCache);
 #endif
 }
index da6be1abab376dbfd04029ad4edef4a89f5adac3..afab7aba55c789118de9e5454634cb3bc6405566 100644 (file)
@@ -32,6 +32,9 @@
 #include "Color.h"
 #include "SVGPaintServer.h"
 
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
 #if PLATFORM(QT)
 class QGradient;
 #endif
@@ -110,9 +113,12 @@ namespace WebCore {
             CGFloat offset;
             CGFloat previousDeltaInverse;
         } QuartzGradientStop;
+        
+        struct SharedStopCache : public RefCounted<SharedStopCache> {
+            Vector<QuartzGradientStop> m_stops;
+        };
 
-        QuartzGradientStop* m_stopsCache;
-        int m_stopsCount;
+        RefPtr<SharedStopCache> m_stopsCache;
 
         CGShadingRef m_shadingCache;
         mutable GraphicsContext* m_savedContext;
index 84c8f549f3aa8f1e97db49f9d5290ca518fd8632..58ec4abf3a214c386e40333121c836c03cbf09b5 100644 (file)
@@ -40,11 +40,18 @@ using namespace std;
 
 namespace WebCore {
 
+static void releaseCachedStops(void* info)
+{
+    reinterpret_cast<SVGPaintServerGradient::SharedStopCache*>(info)->deref();
+}
+
 static void cgGradientCallback(void* info, const CGFloat* inValues, CGFloat* outColor)
 {
-    const SVGPaintServerGradient* server = reinterpret_cast<const SVGPaintServerGradient*>(info);
-    SVGPaintServerGradient::QuartzGradientStop* stops = server->m_stopsCache;
-    int stopsCount = server->m_stopsCount;
+    SVGPaintServerGradient::SharedStopCache* stopsCache = reinterpret_cast<SVGPaintServerGradient::SharedStopCache*>(info);
+    
+    SVGPaintServerGradient::QuartzGradientStop* stops = stopsCache->m_stops.data();
+        
+    int stopsCount = stopsCache->m_stops.size();
 
     CGFloat inValue = inValues[0];
 
@@ -86,10 +93,11 @@ static CGShadingRef CGShadingRefForLinearGradient(const SVGPaintServerLinearGrad
     CGPoint start = CGPoint(server->gradientStart());
     CGPoint end = CGPoint(server->gradientEnd());
 
-    CGFunctionCallbacks callbacks = {0, cgGradientCallback, NULL};
+    CGFunctionCallbacks callbacks = {0, cgGradientCallback, releaseCachedStops};
     CGFloat domainLimits[2] = {0, 1};
     CGFloat rangeLimits[8] = {0, 1, 0, 1, 0, 1, 0, 1};
-    CGFunctionRef shadingFunction = CGFunctionCreate((void *)server, 1, domainLimits, 4, rangeLimits, &callbacks);
+    server->m_stopsCache->ref();
+    CGFunctionRef shadingFunction = CGFunctionCreate(server->m_stopsCache.get(), 1, domainLimits, 4, rangeLimits, &callbacks);
 
     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
     CGShadingRef shading = CGShadingCreateAxial(colorSpace, start, end, shadingFunction, true, true);
@@ -115,10 +123,11 @@ static CGShadingRef CGShadingRefForRadialGradient(const SVGPaintServerRadialGrad
         focus.y = narrowPrecisionToCGFloat(sin(angle) * radius);
     }
 
-    CGFunctionCallbacks callbacks = {0, cgGradientCallback, NULL};
+    CGFunctionCallbacks callbacks = {0, cgGradientCallback, releaseCachedStops};
     CGFloat domainLimits[2] = {0, 1};
     CGFloat rangeLimits[8] = {0, 1, 0, 1, 0, 1, 0, 1};
-    CGFunctionRef shadingFunction = CGFunctionCreate((void *)server, 1, domainLimits, 4, rangeLimits, &callbacks);
+    server->m_stopsCache->ref();
+    CGFunctionRef shadingFunction = CGFunctionCreate(server->m_stopsCache.get(), 1, domainLimits, 4, rangeLimits, &callbacks);
 
     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
     CGShadingRef shading = CGShadingCreateRadial(colorSpace, focus, 0, center, narrowPrecisionToCGFloat(radius), shadingFunction, true, true);
@@ -129,18 +138,16 @@ static CGShadingRef CGShadingRefForRadialGradient(const SVGPaintServerRadialGrad
 
 void SVGPaintServerGradient::updateQuartzGradientStopsCache(const Vector<SVGGradientStop>& stops)
 {
-    delete m_stopsCache;
-
-    m_stopsCount = stops.size();
-    m_stopsCache = new SVGPaintServerGradient::QuartzGradientStop[m_stopsCount];
-
+    m_stopsCache = new SharedStopCache;
+    Vector<QuartzGradientStop>& stopsCache = m_stopsCache->m_stops;
+    stopsCache.resize(stops.size());
     CGFloat previousOffset = 0.0f;
     for (unsigned i = 0; i < stops.size(); ++i) {
         CGFloat currOffset = min(max(stops[i].first, previousOffset), static_cast<CGFloat>(1.0));
-        m_stopsCache[i].offset = currOffset;
-        m_stopsCache[i].previousDeltaInverse = 1.0f / (currOffset - previousOffset);
+        stopsCache[i].offset = currOffset;
+        stopsCache[i].previousDeltaInverse = 1.0f / (currOffset - previousOffset);
         previousOffset = currOffset;
-        CGFloat* ca = m_stopsCache[i].colorArray;
+        CGFloat* ca = stopsCache[i].colorArray;
         stops[i].second.getRGBA(ca[0], ca[1], ca[2], ca[3]);
     }
 }
@@ -321,7 +328,6 @@ bool SVGPaintServerGradient::setup(GraphicsContext*& context, const RenderObject
 void SVGPaintServerGradient::invalidate()
 {
     // Invalidate caches
-    delete m_stopsCache;
     CGShadingRelease(m_shadingCache);
 
     m_stopsCache = 0;