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 222ffc0..8a95492 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 a41407f..6a701b8 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 da6be1a..afab7ab 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 84c8f54..58ec4ab 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;