Implement the HTML5 canvas tainting rules to prevent potential data leakage
[WebKit-https.git] / WebCore / html / CanvasPattern.cpp
index 2a4fd345726183d14b87a45d6e27b0d27de957c9..2cda91783103a484649b9d4cb9cb889b2cfb2d00 100644 (file)
 #include "CanvasPattern.h"
 
 #include "CachedImage.h"
+#include "ExceptionCode.h"
 #include "FloatRect.h"
+#include "GraphicsContext.h"
 #include "Image.h"
 
 namespace WebCore {
 
-CanvasPattern::CanvasPattern(CachedImage* cachedImage, const String& repetitionType)
-    : m_cachedImage(cachedImage)
-    , m_repeatX(!(equalIgnoringCase(repetitionType, "repeat-y") || equalIgnoringCase(repetitionType, "no-repeat")))
-    , m_repeatY(!(equalIgnoringCase(repetitionType, "repeat-x") || equalIgnoringCase(repetitionType, "no-repeat")))
+void CanvasPattern::parseRepetitionType(const String& type, bool& repeatX, bool& repeatY, ExceptionCode& ec)
+{
+    if (type.isEmpty() || type == "repeat") {
+        repeatX = true;
+        repeatY = true;
+        ec = 0;
+        return;
+    }
+    if (type == "no-repeat") {
+        repeatX = false;
+        repeatY = false;
+        ec = 0;
+        return;
+    }
+    if (type == "repeat-x") {
+        repeatX = true;
+        repeatY = false;
+        ec = 0;
+        return;
+    }
+    if (type == "repeat-y") {
+        repeatX = false;
+        repeatY = true;
+        ec = 0;
+        return;
+    }
+    ec = SYNTAX_ERR;
+}
+
+#if PLATFORM(CG)
+
+CanvasPattern::CanvasPattern(CGImageRef image, bool repeatX, bool repeatY, bool originClean)
+    : RefCounted<CanvasPattern>(0)
+    , m_platformImage(image)
+    , m_cachedImage(0)
+    , m_repeatX(repeatX)
+    , m_repeatY(repeatY)
+    , m_originClean(originClean)
+{
+}
+
+#elif PLATFORM(CAIRO)
+
+CanvasPattern::CanvasPattern(cairo_surface_t* surface, bool repeatX, bool repeatY, bool originClean)
+    : RefCounted<CanvasPattern>(0)
+    , m_platformImage(cairo_surface_reference(surface))
+    , m_cachedImage(0)
+    , m_repeatX(repeatX)
+    , m_repeatY(repeatY)
+    , m_originClean(originClean)
+{
+}
+
+#endif
+
+CanvasPattern::CanvasPattern(CachedImage* cachedImage, bool repeatX, bool repeatY, bool originClean)
+    : RefCounted<CanvasPattern>(0)
+#if PLATFORM(CG) || PLATFORM(CAIRO)
+    , m_platformImage(0)
+#endif
+    , m_cachedImage(cachedImage)
+    , m_repeatX(repeatX)
+    , m_repeatY(repeatY)
+    , m_originClean(originClean)
 {
     if (cachedImage)
         cachedImage->ref(this);
@@ -43,14 +105,26 @@ CanvasPattern::CanvasPattern(CachedImage* cachedImage, const String& repetitionT
 
 CanvasPattern::~CanvasPattern()
 {
+#if PLATFORM(CAIRO)
+    if (m_platformImage)
+        cairo_surface_destroy(m_platformImage);
+#endif
     if (m_cachedImage)
         m_cachedImage->deref(this);
 }
 
-#if __APPLE__
+#if PLATFORM(CG)
 
 static void patternCallback(void* info, CGContextRef context)
 {
+    CGImageRef platformImage = static_cast<CanvasPattern*>(info)->platformImage();
+    if (platformImage) {
+        CGRect rect = GraphicsContext(context).roundToDevicePixels(
+            FloatRect(0, 0, CGImageGetWidth(platformImage), CGImageGetHeight(platformImage)));
+        CGContextDrawImage(context, rect, platformImage);
+        return;
+    }
+
     CachedImage* cachedImage = static_cast<CanvasPattern*>(info)->cachedImage();
     if (!cachedImage)
         return;
@@ -58,15 +132,18 @@ static void patternCallback(void* info, CGContextRef context)
     if (!image)
         return;
 
-    FloatRect rect = image->rect();
-    
-    // FIXME: Is using CGImageRef directly really superior to asking the image to draw?
+    FloatRect rect = GraphicsContext(context).roundToDevicePixels(image->rect());
+
     if (image->getCGImageRef()) {
         CGContextDrawImage(context, rect, image->getCGImageRef());
+        // FIXME: We should refactor this code to use the platform-independent 
+        // drawing API in all cases. Then, this didDraw call will happen 
+        // automatically, and we can remove it.
+        cachedImage->didDraw(image);
         return;
     }
 
-    image->drawInRect(rect, rect, Image::CompositeSourceOver, context);
+    GraphicsContext(context).drawImage(image, rect);
 }
 
 static void patternReleaseCallback(void* info)
@@ -76,29 +153,65 @@ static void patternReleaseCallback(void* info)
 
 CGPatternRef CanvasPattern::createPattern(const CGAffineTransform& transform)
 {
-    if (!m_cachedImage)
-        return 0;
-    Image* image = m_cachedImage->image();
-    if (!image)
-        return 0;
+    CGRect rect;
+    rect.origin.x = 0;
+    rect.origin.y = 0;
+    if (m_platformImage) {
+        rect.size.width = CGImageGetWidth(m_platformImage.get());
+        rect.size.height = CGImageGetHeight(m_platformImage.get());
+    } else {
+        if (!m_cachedImage)
+            return 0;
+        Image* image = m_cachedImage->image();
+        if (!image)
+            return 0;
+        rect.size.width = image->width();
+        rect.size.height = image->height();
+    }
 
     CGAffineTransform patternTransform =
-        CGAffineTransformTranslate(CGAffineTransformScale(transform, 1, -1), 0, -image->height());
+        CGAffineTransformTranslate(CGAffineTransformScale(transform, 1, -1), 0, -rect.size.height);
 
-    float xStep = m_repeatX ? image->width() : FLT_MAX;
+    float xStep = m_repeatX ? rect.size.width : FLT_MAX;
     // If FLT_MAX should also be used for yStep, nothing is rendered. Using fractions of FLT_MAX also
     // result in nothing being rendered. This is not a problem with xStep.
     // INT_MAX is almost correct, but there seems to be some number wrapping occuring making the fill
     // pattern is not filled correctly. 
     // So, just pick a really large number that works. 
-    float yStep = m_repeatY ? image->height() : (100000000.0);
+    float yStep = m_repeatY ? rect.size.height : (100000000.0f);
 
     const CGPatternCallbacks patternCallbacks = { 0, patternCallback, patternReleaseCallback };
     ref();
-    return CGPatternCreate(this, image->rect(), patternTransform, xStep, yStep,
+    return CGPatternCreate(this, rect, patternTransform, xStep, yStep,
         kCGPatternTilingConstantSpacing, TRUE, &patternCallbacks);
 }
 
+#elif PLATFORM(CAIRO)
+
+cairo_pattern_t* CanvasPattern::createPattern(const cairo_matrix_t& m)
+{
+    cairo_surface_t* surface = 0;
+    if (m_platformImage) {
+        surface = m_platformImage;
+    } else {
+        if (!m_cachedImage)
+            return 0;
+        Image* image = m_cachedImage->image();
+        if (!image)
+            return 0;
+        surface = image->nativeImageForCurrentFrame();
+    }
+
+    if (!surface)
+        return 0;
+
+    cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface);
+    cairo_pattern_set_matrix(pattern, &m);
+    if (m_repeatX || m_repeatY)
+        cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+    return pattern;
+}
+
 #endif
 
 }