[SCM] WebKit Debian packaging branch, debian/experimental, updated. debian/1.3.8-1-1049-g2e11a8e

simon.fraser at apple.com simon.fraser at apple.com
Fri Jan 21 14:52:39 UTC 2011


The following commit has been merged in the debian/experimental branch:
commit a21d8b1c03e096172b16e3f723d8e24e4dddbba8
Author: simon.fraser at apple.com <simon.fraser at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Mon Jan 3 19:36:41 2011 +0000

    2011-01-03  Simon Fraser  <simon.fraser at apple.com>
    
            Reviewed by David Hyatt.
    
            Implement -webkit-linear-gradient and -webkit-radial-gradient
            https://bugs.webkit.org/show_bug.cgi?id=28152
    
            Add support for -webkit-radial-gradients.
    
            Tests: fast/gradients/css3-radial-gradients.html
                   fast/gradients/css3-radial-gradients2.html
    
            * css/CSSGradientValue.cpp:
            (WebCore::blend): Used to blend colors, which is necessary when truncating
            the start of radial gradients.
            (WebCore::GradientStop): Small struct to aid gradient stop processing.
            (WebCore::CSSGradientValue::addStops): Rename 'positions' to 'stops', and store
            the color so that we can blend it when truncating radial gradients. Changed
            to handle both linear and radial gradients.
            (WebCore::CSSRadialGradientValue::cssText): Update to follow the spec.
            (WebCore::CSSRadialGradientValue::resolveRadius): Stylistic change.
            (WebCore::distanceToClosestCorner): New utility method.
            (WebCore::distanceToFarthestCorner): Ditto.
            (WebCore::CSSRadialGradientValue::createGradient): New logic to deal with shape and
            fill rules.
    
            * css/CSSGradientValue.h:
            (WebCore::CSSGradientValue::isLinearGradient): Color-stop processing needs to know what
            kind of gradient it's dealing with, so add new isFooGradient methods.
            (WebCore::CSSGradientValue::isRadialGradient): Ditto.
            (WebCore::CSSLinearGradientValue::isLinearGradient):
            (WebCore::CSSRadialGradientValue::setShape): New setters for shape, size etc.
            (WebCore::CSSRadialGradientValue::setSizingBehavior):
            (WebCore::CSSRadialGradientValue::setEndHorizontalSize):
            (WebCore::CSSRadialGradientValue::setEndVerticalSize):
            (WebCore::CSSRadialGradientValue::isRadialGradient):
    
            * css/CSSParser.h:
            * css/CSSParser.h: Pass a CSSParserValueList* into parseFillPositionXY() and parseFillPosition()
            so we can use parseFillPosition() when parsing a gradient function.
            * css/CSSParser.cpp:
            (WebCore::CSSParser::parseFillPositionXY): Pass in a CSSParserValueList*
            (WebCore::CSSParser::parseFillPosition): Ditto
            (WebCore::CSSParser::parseFillProperty): Pass m_valueList to parseFillPosition.
            (WebCore::CSSParser::parseTransformOriginShorthand): Ditto
            (WebCore::CSSParser::parseRadialGradient): Flesh out radial gradient parsing.
            (WebCore::CSSParser::parseTransformOrigin): Pass m_valueList to parseFillPosition.
            (WebCore::CSSParser::parsePerspectiveOrigin): Ditto
    
            * css/CSSValueKeywords.in: New keywords for radial gradient shape and fill behaviors.
    
            * platform/graphics/Gradient.cpp:
            (WebCore::Gradient::Gradient): Pass aspect ratio for elliptical gradients.
            * platform/graphics/Gradient.h: Add aspect ratio for elliptical gradients.
            (WebCore::Gradient::create):
            (WebCore::Gradient::startRadius): Expose radii.
            (WebCore::Gradient::endRadius):
            (WebCore::Gradient::setStartRadius): Setters are required for when we need to scale color stops.
            (WebCore::Gradient::setEndRadius):
            (WebCore::Gradient::aspectRatio):
            * platform/graphics/cg/GradientCG.cpp:
            (WebCore::Gradient::paint): For elliptical gradients, scale the CTM.
            * platform/graphics/wince/GraphicsContextWinCE.cpp:
            (WebCore::GraphicsContext::fillRect): Use start/endRadius() rather than r0() and r1().
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@74915 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 5510929..291669c 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -4,6 +4,27 @@
 
         Implement -webkit-linear-gradient and -webkit-radial-gradient
         https://bugs.webkit.org/show_bug.cgi?id=28152
+
+        Enhance gradient parsing test for radial gradients, and add new tests
+        for various radial gradients.
+
+        * fast/gradients/css3-gradient-parsing-expected.txt:
+        * fast/gradients/css3-gradient-parsing.html:
+        * fast/gradients/css3-radial-gradients-expected.txt: Added.
+        * fast/gradients/css3-radial-gradients.html: Added.
+        * fast/gradients/css3-radial-gradients2-expected.txt: Added.
+        * fast/gradients/css3-radial-gradients2.html: Added.
+        * platform/mac/fast/gradients/css3-radial-gradients-expected.checksum: Added.
+        * platform/mac/fast/gradients/css3-radial-gradients-expected.png: Added.
+        * platform/mac/fast/gradients/css3-radial-gradients2-expected.checksum: Added.
+        * platform/mac/fast/gradients/css3-radial-gradients2-expected.png: Added.
+
+2011-01-03  Simon Fraser  <simon.fraser at apple.com>
+
+        Reviewed by David Hyatt.
+
+        Implement -webkit-linear-gradient and -webkit-radial-gradient
+        https://bugs.webkit.org/show_bug.cgi?id=28152
         
         Testcases for CSS3 linear-gradients.
 
diff --git a/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt b/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt
index 3e60e8a..3e6f187 100644
--- a/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt
+++ b/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt
@@ -1,6 +1,4 @@
-Tests -webkit-linear-gradient and -webkit-radial-gradient
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+-webkit-linear-gradient
 
 
 PASS testGradient("background-image: -webkit-linear-gradient(black 0%", "background-image") is "none"
@@ -19,6 +17,24 @@ PASS testGradient("background-image: -webkit-linear-gradient(black 10px, white 2
 PASS testGradient("background-image: -webkit-linear-gradient(top left, black 0%)", "background-image") is "-webkit-linear-gradient(left top, black 0%)"
 PASS testGradient("background-image: -webkit-linear-gradient(top, black 0%)", "background-image") is "-webkit-linear-gradient(top, black 0%)"
 PASS testGradient("background-image: -webkit-linear-gradient(10deg, black 0%)", "background-image") is "-webkit-linear-gradient(10deg, black 0%)"
+-webkit-radial-gradient
+
+
+PASS testGradient("background-image: -webkit-radial-gradient(white, black)", "background-image") is "-webkit-radial-gradient(center, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(bottom right, white, black)", "background-image") is "-webkit-radial-gradient(100% 100%, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(cover, white, black)", "background-image") is "-webkit-radial-gradient(center, ellipse cover, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(circle, white, black)", "background-image") is "-webkit-radial-gradient(center, circle cover, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(circle contain, white, black)", "background-image") is "-webkit-radial-gradient(center, circle contain, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(top, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(50% 0%, circle contain, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(top left, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(0% 0%, circle contain, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(10px 20%, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(10px 20%, circle contain, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(10px, 20%, circle contain, white, black)", "background-image") is "none"
+PASS testGradient("background-image: -webkit-radial-gradient(circle 10px 20%, circle contain, white, black)", "background-image") is "none"
+PASS testGradient("background-image: -webkit-radial-gradient(circle 10px, circle contain, white, black)", "background-image") is "none"
+PASS testGradient("background-image: -webkit-radial-gradient(center, 10px, white, black)", "background-image") is "none"
+PASS testGradient("background-image: -webkit-radial-gradient(center, 10px 10px, white, black)", "background-image") is "-webkit-radial-gradient(50% 50%, 10px 10px, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(ellipse farthest-corner, white, black)", "background-image") is "-webkit-radial-gradient(center, ellipse farthest-corner, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(circle closest-side, white, black)", "background-image") is "-webkit-radial-gradient(center, circle closest-side, white, black)"
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/LayoutTests/fast/gradients/css3-gradient-parsing.html b/LayoutTests/fast/gradients/css3-gradient-parsing.html
index 3d07f70..75c7f12 100644
--- a/LayoutTests/fast/gradients/css3-gradient-parsing.html
+++ b/LayoutTests/fast/gradients/css3-gradient-parsing.html
@@ -11,7 +11,6 @@
 
 
 <script type="text/javascript">
-description('Tests -webkit-linear-gradient and -webkit-radial-gradient');
 
 function testGradient(css, queryProp)
 {
@@ -30,6 +29,8 @@ function testSame(dir, prop, altProp, value)
     shouldBeEqualToString('test("' + dir + '", "' + altProp + ': ' + value + '", "' + prop + '")', value);
 }
 
+debug('<p>-webkit-linear-gradient</p>');
+
 shouldBe('testGradient("background-image: -webkit-linear-gradient(black 0%", "background-image")', '"none"');
 shouldBe('testGradient("background-image: -webkit-linear-gradient(top)", "background-image")', '"none"');
 shouldBe('testGradient("background-image: -webkit-linear-gradient(10deg)", "background-image")', '"none"');
@@ -47,6 +48,24 @@ shouldBe('testGradient("background-image: -webkit-linear-gradient(top left, blac
 shouldBe('testGradient("background-image: -webkit-linear-gradient(top, black 0%)", "background-image")', '"-webkit-linear-gradient(top, black 0%)"');
 shouldBe('testGradient("background-image: -webkit-linear-gradient(10deg, black 0%)", "background-image")', '"-webkit-linear-gradient(10deg, black 0%)"');
 
+debug('<p>-webkit-radial-gradient</p>');
+
+shouldBe('testGradient("background-image: -webkit-radial-gradient(white, black)", "background-image")', '"-webkit-radial-gradient(center, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(bottom right, white, black)", "background-image")', '"-webkit-radial-gradient(100% 100%, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(cover, white, black)", "background-image")', '"-webkit-radial-gradient(center, ellipse cover, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(circle, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle cover, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle contain, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(top, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(50% 0%, circle contain, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(top left, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(0% 0%, circle contain, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(10px 20%, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(10px 20%, circle contain, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(10px, 20%, circle contain, white, black)", "background-image")', '"none"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(circle 10px 20%, circle contain, white, black)", "background-image")', '"none"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(circle 10px, circle contain, white, black)", "background-image")', '"none"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(center, 10px, white, black)", "background-image")', '"none"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(center, 10px 10px, white, black)", "background-image")', '"-webkit-radial-gradient(50% 50%, 10px 10px, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(ellipse farthest-corner, white, black)", "background-image")', '"-webkit-radial-gradient(center, ellipse farthest-corner, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(circle closest-side, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle closest-side, white, black)"');
+
 successfullyParsed = true;
   
 </script>
diff --git a/LayoutTests/fast/gradients/css3-color-stops-expected.txt b/LayoutTests/fast/gradients/css3-radial-gradients-expected.txt
similarity index 100%
copy from LayoutTests/fast/gradients/css3-color-stops-expected.txt
copy to LayoutTests/fast/gradients/css3-radial-gradients-expected.txt
diff --git a/LayoutTests/fast/gradients/css3-radial-gradients.html b/LayoutTests/fast/gradients/css3-radial-gradients.html
new file mode 100644
index 0000000..b352419
--- /dev/null
+++ b/LayoutTests/fast/gradients/css3-radial-gradients.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <style type="text/css" media="screen">
+    .box {
+      display: inline-block;
+      height: 120px;
+      width: 200px;
+      margin: 10px;
+      border: 1px solid black;
+      background-repeat: no-repeat;
+    }
+
+    .gradient1 {
+      background-image: -webkit-radial-gradient(ellipse closest-corner, white, black);
+      background-image: -moz-radial-gradient(ellipse closest-corner, white, black);
+    }
+
+    .gradient2 {
+      background-image: -webkit-radial-gradient(50px 50%, circle closest-side, white, black);
+      background-image: -moz-radial-gradient(50px 50%, circle closest-side, white, black);
+    }
+    
+    .gradient3 {
+      background-image: -webkit-radial-gradient(20px 50px, ellipse closest-corner, white, black);
+      background-image: -moz-radial-gradient(20px 50px, ellipse closest-corner, white, black);
+    }
+
+    .gradient4 {
+      background-image: -webkit-radial-gradient(20% 20%, circle closest-corner, white, black);
+      background-image: -moz-radial-gradient(20% 20%, circle closest-corner, white, black);
+    }
+
+    .gradient5 {
+      background-image: -webkit-radial-gradient(circle cover, red, green, blue);
+      background-image: -moz-radial-gradient(circle cover, red, green, blue);
+    }
+
+    .gradient6 {
+      background-image: -webkit-radial-gradient(left, circle cover, red, green 30%, blue);
+      background-image: -moz-radial-gradient(left, circle cover, red, green 30%, blue);
+    }
+
+    .gradient7 {
+      background-image: -webkit-radial-gradient(ellipse cover, red, green 80px, blue);
+      background-image: -moz-radial-gradient(ellipse cover, red, green 80px, blue);
+    }
+
+    .gradient8 {
+      background-image: -webkit-radial-gradient(circle cover, red 20%, green, blue 150%);
+      background-image: -moz-radial-gradient(circle cover, red 20%, green, blue 150%);
+    }
+
+    .gradient9 {
+      background-image: -webkit-radial-gradient(20% 20%, circle closest-corner, red -50%, green, blue 150%);
+      background-image: -moz-radial-gradient(20% 20%, circle closest-corner, red -50%, green, blue 150%);
+    }
+
+  </style>
+  <script type="text/javascript" charset="utf-8">
+    if (window.layoutTestController) {
+      var dumpPixels = true;
+      layoutTestController.dumpAsText(dumpPixels);
+    }
+  </script>
+</head>
+<body>
+
+  <div class="gradient1 box"></div>
+  <div class="gradient2 box"></div>
+  <div class="gradient3 box"></div>
+  <br>
+  <div class="gradient4 box"></div>
+  <div class="gradient5 box"></div>
+  <div class="gradient6 box"></div>
+  <br>
+  <div class="gradient7 box"></div>
+  <div class="gradient8 box"></div>
+  <div class="gradient9 box"></div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/gradients/css3-radial-gradients2-expected.txt b/LayoutTests/fast/gradients/css3-radial-gradients2-expected.txt
new file mode 100644
index 0000000..25447f1
--- /dev/null
+++ b/LayoutTests/fast/gradients/css3-radial-gradients2-expected.txt
@@ -0,0 +1,2 @@
+   
+  
diff --git a/LayoutTests/fast/gradients/css3-radial-gradients2.html b/LayoutTests/fast/gradients/css3-radial-gradients2.html
new file mode 100644
index 0000000..88cf161
--- /dev/null
+++ b/LayoutTests/fast/gradients/css3-radial-gradients2.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <style type="text/css" media="screen">
+    .box {
+      display: inline-block;
+      height: 250px;
+      width: 200px;
+      margin: 10px;
+      border: 1px solid black;
+      background-repeat: no-repeat;
+    }
+
+    .gradient1 {
+      /* Green should coincide with the edge of the box, with blue fill (buggy in CG). */
+      background-image: -webkit-radial-gradient(left, circle closest-corner, red, green 150px, blue);
+      background-image: -moz-radial-gradient(left, circle closest-corner, red, green 150px, blue);
+    }
+
+    .gradient2 {
+      /* Fill should be orange (buggy in CG). */
+      background-image: -webkit-radial-gradient(left, circle closest-corner, red, green 150px, blue, orange 101%);
+      background-image: -moz-radial-gradient(left, circle closest-corner, red, green 150px, blue, orange 101%);
+    }
+
+    .gradient3 {
+      background-image: -webkit-radial-gradient(-100px center, ellipse farthest-corner, black, white);
+      background-image: -moz-radial-gradient(-100px center, ellipse farthest-corner, black, white);
+    }
+    
+    .gradient4 {
+      background-image: -webkit-radial-gradient(-100px center, ellipse closest-corner, black, white);
+      background-image: -moz-radial-gradient(-100px center, ellipse closest-corner, black, white);
+    }
+
+    .gradient5 {
+      background-image: -webkit-radial-gradient(bottom right, black, white);
+      background-image: -moz-radial-gradient(bottom right, black, white);
+    }
+
+    .gradient6 {
+      background-image: -webkit-radial-gradient(50% 20%, ellipse contain, black, white);
+      background-image: -moz-radial-gradient(50% 20%, ellipse contain, black, white);
+    }
+
+  </style>
+  <script type="text/javascript" charset="utf-8">
+    if (window.layoutTestController) {
+      var dumpPixels = true;
+      layoutTestController.dumpAsText(dumpPixels);
+    }
+  </script>
+</head>
+<body>
+
+  <div class="gradient1 box"></div>
+  <div class="gradient2 box"></div>
+  <div class="gradient3 box"></div>
+  <br>
+  <div class="gradient4 box"></div>
+  <div class="gradient5 box"></div>
+  <div class="gradient6 box"></div>
+
+</body>
+</html>
diff --git a/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.checksum b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.checksum
new file mode 100644
index 0000000..713f7cd
--- /dev/null
+++ b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.checksum
@@ -0,0 +1 @@
+af88d9c4da4d2856797d2ab20f7f2927
\ No newline at end of file
diff --git a/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.png b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.png
new file mode 100644
index 0000000..60f9908
Binary files /dev/null and b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.png differ
diff --git a/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.checksum b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.checksum
new file mode 100644
index 0000000..a2008e4
--- /dev/null
+++ b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.checksum
@@ -0,0 +1 @@
+63d36fd71d88bb4bbe7ae42f8ce2f873
\ No newline at end of file
diff --git a/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.png b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.png
new file mode 100644
index 0000000..3b9914f
Binary files /dev/null and b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.png differ
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index 888928a..6cbbeef 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -15,6 +15,71 @@
 
         Implement -webkit-linear-gradient and -webkit-radial-gradient
         https://bugs.webkit.org/show_bug.cgi?id=28152
+
+        Add support for -webkit-radial-gradients.
+
+        Tests: fast/gradients/css3-radial-gradients.html
+               fast/gradients/css3-radial-gradients2.html
+
+        * css/CSSGradientValue.cpp:
+        (WebCore::blend): Used to blend colors, which is necessary when truncating
+        the start of radial gradients.
+        (WebCore::GradientStop): Small struct to aid gradient stop processing.
+        (WebCore::CSSGradientValue::addStops): Rename 'positions' to 'stops', and store
+        the color so that we can blend it when truncating radial gradients. Changed
+        to handle both linear and radial gradients.
+        (WebCore::CSSRadialGradientValue::cssText): Update to follow the spec.
+        (WebCore::CSSRadialGradientValue::resolveRadius): Stylistic change.
+        (WebCore::distanceToClosestCorner): New utility method.
+        (WebCore::distanceToFarthestCorner): Ditto.
+        (WebCore::CSSRadialGradientValue::createGradient): New logic to deal with shape and
+        fill rules.
+
+        * css/CSSGradientValue.h:
+        (WebCore::CSSGradientValue::isLinearGradient): Color-stop processing needs to know what
+        kind of gradient it's dealing with, so add new isFooGradient methods.
+        (WebCore::CSSGradientValue::isRadialGradient): Ditto.
+        (WebCore::CSSLinearGradientValue::isLinearGradient):
+        (WebCore::CSSRadialGradientValue::setShape): New setters for shape, size etc.
+        (WebCore::CSSRadialGradientValue::setSizingBehavior):
+        (WebCore::CSSRadialGradientValue::setEndHorizontalSize):
+        (WebCore::CSSRadialGradientValue::setEndVerticalSize):
+        (WebCore::CSSRadialGradientValue::isRadialGradient):
+
+        * css/CSSParser.h:
+        * css/CSSParser.h: Pass a CSSParserValueList* into parseFillPositionXY() and parseFillPosition()
+        so we can use parseFillPosition() when parsing a gradient function.
+        * css/CSSParser.cpp:
+        (WebCore::CSSParser::parseFillPositionXY): Pass in a CSSParserValueList*
+        (WebCore::CSSParser::parseFillPosition): Ditto
+        (WebCore::CSSParser::parseFillProperty): Pass m_valueList to parseFillPosition.
+        (WebCore::CSSParser::parseTransformOriginShorthand): Ditto
+        (WebCore::CSSParser::parseRadialGradient): Flesh out radial gradient parsing.
+        (WebCore::CSSParser::parseTransformOrigin): Pass m_valueList to parseFillPosition.
+        (WebCore::CSSParser::parsePerspectiveOrigin): Ditto
+
+        * css/CSSValueKeywords.in: New keywords for radial gradient shape and fill behaviors.
+
+        * platform/graphics/Gradient.cpp:
+        (WebCore::Gradient::Gradient): Pass aspect ratio for elliptical gradients.
+        * platform/graphics/Gradient.h: Add aspect ratio for elliptical gradients.
+        (WebCore::Gradient::create):
+        (WebCore::Gradient::startRadius): Expose radii.
+        (WebCore::Gradient::endRadius):
+        (WebCore::Gradient::setStartRadius): Setters are required for when we need to scale color stops.
+        (WebCore::Gradient::setEndRadius):
+        (WebCore::Gradient::aspectRatio):
+        * platform/graphics/cg/GradientCG.cpp:
+        (WebCore::Gradient::paint): For elliptical gradients, scale the CTM.
+        * platform/graphics/wince/GraphicsContextWinCE.cpp:
+        (WebCore::GraphicsContext::fillRect): Use start/endRadius() rather than r0() and r1().
+
+2011-01-03  Simon Fraser  <simon.fraser at apple.com>
+
+        Reviewed by David Hyatt.
+
+        Implement -webkit-linear-gradient and -webkit-radial-gradient
+        https://bugs.webkit.org/show_bug.cgi?id=28152
         
         Add support for the parsing and rendering of non-repeating CSS3 linear gradients,
         according to <http://dev.w3.org/csswg/css3-images/#linear-gradients>.
diff --git a/WebCore/css/CSSGradientValue.cpp b/WebCore/css/CSSGradientValue.cpp
index ca65709..ad0e490 100644
--- a/WebCore/css/CSSGradientValue.cpp
+++ b/WebCore/css/CSSGradientValue.cpp
@@ -82,6 +82,31 @@ void CSSGradientValue::sortStopsIfNeeded()
     }
 }
 
+static inline int blend(int from, int to, float progress)
+{  
+    return int(from + (to - from) * progress);
+}
+
+static inline Color blend(const Color& from, const Color& to, float progress)
+{
+    // FIXME: when we interpolate gradients using premultiplied colors, this should also do premultiplication.
+    return Color(blend(from.red(), to.red(), progress),
+        blend(from.green(), to.green(), progress),
+        blend(from.blue(), to.blue(), progress),
+        blend(from.alpha(), to.alpha(), progress));
+}
+
+struct GradientStop {
+    Color color;
+    float offset;
+    bool specified;
+    
+    GradientStop()
+        : offset(0)
+        , specified(false)
+    { }
+};
+
 void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, RenderStyle* rootStyle)
 {
     RenderStyle* style = renderer->style();
@@ -109,63 +134,68 @@ void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, Rend
     }
 
     size_t numStops = m_stops.size();
-    // Pair of <offset, specified>.
-    Vector<pair<float, bool> > positions(numStops);
+    
+    Vector<GradientStop> stops(numStops);
     
     float gradientLength = 0;
     bool computedGradientLength = false;
     
+    FloatPoint gradientStart = gradient->p0();
+    FloatPoint gradientEnd;
+    if (isLinearGradient())
+        gradientEnd = gradient->p1();
+    else if (isRadialGradient())
+        gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0);
+        
     for (size_t i = 0; i < numStops; ++i) {
         const CSSGradientColorStop& stop = m_stops[i];
 
-        positions[i].first = 0;
-        positions[i].second = false;
+        stops[i].color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(stop.m_color.get());
 
         if (stop.m_position) {
             int type = stop.m_position->primitiveType();
             if (type == CSSPrimitiveValue::CSS_PERCENTAGE)
-                positions[i].first = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
+                stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
             else if (CSSPrimitiveValue::isUnitTypeLength(type)) {
                 float length = stop.m_position->computeLengthFloat(style, rootStyle, style->effectiveZoom());
                 if (!computedGradientLength) {
-                    float xDelta = gradient->p0().x() - gradient->p1().x();
-                    float yDelta = gradient->p0().y() - gradient->p1().y();
-                    gradientLength = sqrtf(xDelta * xDelta + yDelta * yDelta);
+                    FloatSize gradientSize(gradientStart - gradientEnd);
+                    gradientLength = gradientSize.diagonalLength();
                 }
-                positions[i].first = (gradientLength > 0) ? length / gradientLength : 0;
+                stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0;
             } else {
                 ASSERT_NOT_REACHED();
-                positions[i].first = 0;
+                stops[i].offset = 0;
             }
-            positions[i].second = true;
+            stops[i].specified = true;
         } else {
             // If the first color-stop does not have a position, its position defaults to 0%.
             // If the last color-stop does not have a position, its position defaults to 100%.
             if (!i) {
-                positions[i].first = 0;
-                positions[i].second = true;
+                stops[i].offset = 0;
+                stops[i].specified = true;
             } else if (numStops > 1 && i == numStops - 1) {
-                positions[i].first = 1;
-                positions[i].second = true;
+                stops[i].offset = 1;
+                stops[i].specified = true;
             }
         }
 
         // If a color-stop has a position that is less than the specified position of any
         // color-stop before it in the list, its position is changed to be equal to the
         // largest specified position of any color-stop before it.
-        if (positions[i].second && i > 0) {
+        if (stops[i].specified && i > 0) {
             size_t prevSpecifiedIndex;
             for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; --prevSpecifiedIndex) {
-                if (positions[prevSpecifiedIndex].second)
+                if (stops[prevSpecifiedIndex].specified)
                     break;
             }
             
-            if (positions[i].first < positions[prevSpecifiedIndex].first)
-                positions[i].first = positions[prevSpecifiedIndex].first;
+            if (stops[i].offset < stops[prevSpecifiedIndex].offset)
+                stops[i].offset = stops[prevSpecifiedIndex].offset;
         }
     }
 
-    ASSERT(positions[0].second && positions[numStops - 1].second);
+    ASSERT(stops[0].specified && stops[numStops - 1].specified);
     
     // If any color-stop still does not have a position, then, for each run of adjacent
     // color-stops without positions, set their positions so that they are evenly spaced
@@ -175,19 +205,19 @@ void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, Rend
         bool inUnspecifiedRun = false;
 
         for (size_t i = 0; i < numStops; ++i) {
-            if (!positions[i].second && !inUnspecifiedRun) {
+            if (!stops[i].specified && !inUnspecifiedRun) {
                 unspecifiedRunStart = i;
                 inUnspecifiedRun = true;
-            } else if (positions[i].second && inUnspecifiedRun) {
+            } else if (stops[i].specified && inUnspecifiedRun) {
                 size_t unspecifiedRunEnd = i;
 
                 if (unspecifiedRunStart < unspecifiedRunEnd) {
-                    float lastSpecifiedOffset = positions[unspecifiedRunStart - 1].first;
-                    float nextSpecifiedOffset = positions[unspecifiedRunEnd].first;
+                    float lastSpecifiedOffset = stops[unspecifiedRunStart - 1].offset;
+                    float nextSpecifiedOffset = stops[unspecifiedRunEnd].offset;
                     float delta = (nextSpecifiedOffset - lastSpecifiedOffset) / (unspecifiedRunEnd - unspecifiedRunStart + 1);
                     
                     for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j)
-                        positions[j].first = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
+                        stops[j].offset = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
                 }
 
                 inUnspecifiedRun = false;
@@ -196,25 +226,58 @@ void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, Rend
     }
     
     // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and ajusting the stops.
-    if (numStops > 1 && (positions[0].first < 0 || positions[numStops - 1].first > 1)) {
-        float firstOffset = positions[0].first;
-        float lastOffset = positions[numStops - 1].first;
-        float scale = lastOffset - firstOffset;
-        
-        for (size_t i = 0; i < numStops; ++i)
-            positions[i].first = (positions[i].first - firstOffset) / scale;
-
-        FloatPoint p0 = gradient->p0();
-        FloatPoint p1 = gradient->p1();
-        gradient->setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
-        gradient->setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
+    if (numStops > 1 && (stops[0].offset < 0 || stops[numStops - 1].offset > 1)) {
+        if (isLinearGradient()) {
+            float firstOffset = stops[0].offset;
+            float lastOffset = stops[numStops - 1].offset;
+            float scale = lastOffset - firstOffset;
+
+            for (size_t i = 0; i < numStops; ++i)
+                stops[i].offset = (stops[i].offset - firstOffset) / scale;
+
+            FloatPoint p0 = gradient->p0();
+            FloatPoint p1 = gradient->p1();
+            gradient->setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
+            gradient->setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
+        } else if (isRadialGradient()) {
+            // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
+            float firstOffset = 0;
+            float lastOffset = stops[numStops - 1].offset;
+            float scale = lastOffset - firstOffset;
+
+            // Reset points below 0 to the first visible color.
+            size_t firstZeroOrGreaterIndex = 0;
+            for (size_t i = 0; i < numStops; ++i) {
+                if (stops[i].offset >= 0) {
+                    firstZeroOrGreaterIndex = i;
+                    break;
+                }
+            }
+            
+            if (firstZeroOrGreaterIndex > 0 && stops[firstZeroOrGreaterIndex].offset > 0) {
+                float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
+                float nextOffset = stops[firstZeroOrGreaterIndex].offset;
+                
+                float interStopProportion = -prevOffset / (nextOffset - prevOffset);
+                Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
+
+                // Clamp the positions to 0 and set the color.
+                for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
+                    stops[i].offset = 0;
+                    stops[i].color = blendedColor;
+                }
+            }
+            
+            for (size_t i = 0; i < numStops; ++i)
+                stops[i].offset /= scale;
+            
+            gradient->setStartRadius(gradient->startRadius() * scale);
+            gradient->setEndRadius(gradient->endRadius() * scale);
+        }
     }
     
-    for (unsigned i = 0; i < numStops; i++) {
-        const CSSGradientColorStop& stop = m_stops[i];
-        Color color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(stop.m_color.get());
-        gradient->addColorStop(positions[i].first, color);
-    }
+    for (unsigned i = 0; i < numStops; i++)
+        gradient->addColorStop(stops[i].offset, stops[i].color);
 
     gradient->setStopsSorted(true);
 }
@@ -404,28 +467,67 @@ String CSSRadialGradientValue::cssText() const
 {
     String result;
 
-    if (m_deprecatedType)
+    if (m_deprecatedType) {
         result = "-webkit-gradient(radial, ";
-    else
-        result = "-webkit-radial-gradient(";
 
-    result += m_firstX->cssText() + " ";
-    result += m_firstY->cssText() + ", ";
-    result += m_firstRadius->cssText() + ", ";
-    result += m_secondX->cssText() + " ";
-    result += m_secondY->cssText();
-    result += ", ";
-    result += m_secondRadius->cssText();
-
-    for (unsigned i = 0; i < m_stops.size(); i++) {
+        result += m_firstX->cssText() + " ";
+        result += m_firstY->cssText() + ", ";
+        result += m_firstRadius->cssText() + ", ";
+        result += m_secondX->cssText() + " ";
+        result += m_secondY->cssText();
         result += ", ";
-        if (m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
-            result += "from(" + m_stops[i].m_color->cssText() + ")";
-        else if (m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
-            result += "to(" + m_stops[i].m_color->cssText() + ")";
+        result += m_secondRadius->cssText();
+
+        // FIXME: share?
+        for (unsigned i = 0; i < m_stops.size(); i++) {
+            const CSSGradientColorStop& stop = m_stops[i];
+            result += ", ";
+            if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
+                result += "from(" + stop.m_color->cssText() + ")";
+            else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
+                result += "to(" + stop.m_color->cssText() + ")";
+            else
+                result += "color-stop(" + String::number(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + stop.m_color->cssText() + ")";
+        }
+    } else {
+
+        result = "-webkit-radial-gradient(";
+        if (m_firstX && m_firstY) {
+            result += m_firstX->cssText() + " " + m_firstY->cssText();
+        } else if (m_firstX)
+            result += m_firstX->cssText();
+         else if (m_firstY)
+            result += m_firstY->cssText();
         else
-            result += "color-stop(" + String::number(m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + m_stops[i].m_color->cssText() + ")";
+            result += "center";
+
+
+        if (m_shape || m_sizingBehavior) {
+            result += ", ";
+            if (m_shape)
+                result += m_shape->cssText() + " ";
+            else
+                result += "ellipse ";
+
+            if (m_sizingBehavior)
+                result += m_sizingBehavior->cssText();
+            else
+                result += "cover";
+
+        } else if (m_endHorizontalSize && m_endVerticalSize) {
+            result += ", ";
+            result += m_endHorizontalSize->cssText() + " " + m_endVerticalSize->cssText();
+        }
+
+        for (unsigned i = 0; i < m_stops.size(); i++) {
+            const CSSGradientColorStop& stop = m_stops[i];
+            result += ", ";
+            result += stop.m_color->cssText();
+            if (stop.m_position)
+                result += " " + stop.m_position->cssText();
+        }
     }
+
     result += ")";
     return result;
 }
@@ -434,7 +536,7 @@ float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, RenderSty
 {
     float zoomFactor = style->effectiveZoom();
 
-    float result = 0.f;
+    float result = 0;
     if (radius->primitiveType() == CSSPrimitiveValue::CSS_NUMBER)  // Can the radius be a percentage?
         result = radius->getFloatValue() * zoomFactor;
     else
@@ -443,6 +545,82 @@ float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, RenderSty
     return result;
 }
 
+static float distanceToClosestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
+{
+    FloatPoint topLeft;
+    float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
+
+    FloatPoint topRight(size.width(), 0);
+    float topRightDistance = FloatSize(p - topRight).diagonalLength();
+
+    FloatPoint bottomLeft(0, size.height());
+    float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
+
+    FloatPoint bottomRight(size.width(), size.height());
+    float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
+
+    corner = topLeft;
+    float minDistance = topLeftDistance;
+    if (topRightDistance < minDistance) {
+        minDistance = topRightDistance;
+        corner = topRight;
+    }
+
+    if (bottomLeftDistance < minDistance) {
+        minDistance = bottomLeftDistance;
+        corner = bottomLeft;
+    }
+
+    if (bottomRightDistance < minDistance) {
+        minDistance = bottomRightDistance;
+        corner = bottomRight;
+    }
+    return minDistance;
+}
+
+static float distanceToFarthestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
+{
+    FloatPoint topLeft;
+    float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
+
+    FloatPoint topRight(size.width(), 0);
+    float topRightDistance = FloatSize(p - topRight).diagonalLength();
+
+    FloatPoint bottomLeft(0, size.height());
+    float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
+
+    FloatPoint bottomRight(size.width(), size.height());
+    float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
+
+    corner = topLeft;
+    float maxDistance = topLeftDistance;
+    if (topRightDistance > maxDistance) {
+        maxDistance = topRightDistance;
+        corner = topRight;
+    }
+
+    if (bottomLeftDistance > maxDistance) {
+        maxDistance = bottomLeftDistance;
+        corner = bottomLeft;
+    }
+
+    if (bottomRightDistance > maxDistance) {
+        maxDistance = bottomRightDistance;
+        corner = bottomRight;
+    }
+    return maxDistance;
+}
+
+// Compute horizontal radius of ellipse with center at 0,0 which passes through p, and has
+// width/height given by aspectRatio.
+static inline float horizontalEllipseRadius(const FloatSize& p, float aspectRatio)
+{
+    // x^2/a^2 + y^2/b^2 = 1
+    // a/b = aspectRatio, b = a/aspectRatio
+    // a = sqrt(x^2 + y^2/(1/r^2))
+    return sqrtf(p.width() * p.width() + (p.height() * p.height()) / (1 / (aspectRatio * aspectRatio)));
+}
+
 // FIXME: share code with the linear version
 PassRefPtr<Gradient> CSSRadialGradientValue::createGradient(RenderObject* renderer, const IntSize& size)
 {
@@ -451,23 +629,124 @@ PassRefPtr<Gradient> CSSRadialGradientValue::createGradient(RenderObject* render
     RenderStyle* rootStyle = renderer->document()->documentElement()->renderStyle();
 
     FloatPoint firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), renderer->style(), rootStyle, size);
+    if (!m_firstX)
+        firstPoint.setX(size.width() / 2);
+    if (!m_firstY)
+        firstPoint.setY(size.height() / 2);
+
     FloatPoint secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), renderer->style(), rootStyle, size);
+    if (!m_secondX)
+        secondPoint.setX(size.width() / 2);
+    if (!m_secondY)
+        secondPoint.setY(size.height() / 2);
     
-    float firstRadius = resolveRadius(m_firstRadius.get(), renderer->style(), rootStyle);
-    float secondRadius = resolveRadius(m_secondRadius.get(), renderer->style(), rootStyle);
-    RefPtr<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius);
-
-    // Now add the stops.
-    sortStopsIfNeeded();
+    float firstRadius = 0;
+    if (m_firstRadius)
+        firstRadius = resolveRadius(m_firstRadius.get(), renderer->style(), rootStyle);
+
+    float secondRadius = 0;
+    float aspectRatio = 1; // width / height.
+    if (m_secondRadius)
+        secondRadius = resolveRadius(m_secondRadius.get(), renderer->style(), rootStyle);
+    else if (m_endHorizontalSize || m_endVerticalSize) {
+        secondRadius = resolveRadius(m_endHorizontalSize.get(), renderer->style(), rootStyle);
+        aspectRatio = secondRadius / resolveRadius(m_endVerticalSize.get(), renderer->style(), rootStyle);
+    } else {
+        enum GradientShape { Circle, Ellipse };
+        GradientShape shape = Ellipse;
+        if (m_shape && m_shape->primitiveType() == CSSPrimitiveValue::CSS_IDENT && m_shape->getIdent() == CSSValueCircle)
+            shape = Circle;
+        
+        enum GradientFill { ClosestSide, ClosestCorner, FarthestSide, FarthestCorner };
+        GradientFill fill = FarthestCorner;
+        
+        if (m_sizingBehavior && m_sizingBehavior->primitiveType() == CSSPrimitiveValue::CSS_IDENT) {
+            switch (m_sizingBehavior->getIdent()) {
+            case CSSValueContain:
+            case CSSValueClosestSide:
+                fill = ClosestSide;
+                break;
+            case CSSValueClosestCorner:
+                fill = ClosestCorner;
+                break;
+            case CSSValueFarthestSide:
+                fill = FarthestSide;
+                break;
+            case CSSValueCover:
+            case CSSValueFarthestCorner:
+                fill = FarthestCorner;
+                break;
+            }
+        }
+        
+        // Now compute the end radii based on the second point, shape and fill.
+        
+        // Horizontal
+        switch (fill) {
+        case ClosestSide: {
+            float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
+            float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
+            if (shape == Circle) {
+                float smaller = min(xDist, yDist);
+                xDist = smaller;
+                yDist = smaller;
+            }
+            secondRadius = xDist;
+            aspectRatio = xDist / yDist;
+            break;
+        }
+        case FarthestSide: {
+            float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
+            float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
+            if (shape == Circle) {
+                float larger = max(xDist, yDist);
+                xDist = larger;
+                yDist = larger;
+            }
+            secondRadius = xDist;
+            aspectRatio = xDist / yDist;
+            break;
+        }
+        case ClosestCorner: {
+            FloatPoint corner;
+            float distance = distanceToClosestCorner(secondPoint, size, corner);
+            if (shape == Circle)
+                secondRadius = distance;
+            else {
+                // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
+                // that it would if closest-side or farthest-side were specified, as appropriate.
+                float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
+                float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
+                
+                secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
+                aspectRatio = xDist / yDist;
+            }
+            break;
+        }
 
-    // We have to resolve colors.
-    for (unsigned i = 0; i < m_stops.size(); i++) {
-        Color color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(m_stops[i].m_color.get());
-        gradient->addColorStop(m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER), color);
+        case FarthestCorner: {
+            FloatPoint corner;
+            float distance = distanceToFarthestCorner(secondPoint, size, corner);
+            if (shape == Circle)
+                secondRadius = distance;
+            else {
+                // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
+                // that it would if closest-side or farthest-side were specified, as appropriate.
+                float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
+                float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
+                
+                secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
+                aspectRatio = xDist / yDist;
+            }
+            break;
+        }
+        }
     }
 
-    // The back end already sorted the stops.
-    gradient->setStopsSorted(true);
+    RefPtr<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius, aspectRatio);
+
+    // Now add the stops.
+    addStops(gradient.get(), renderer, rootStyle);
 
     return gradient.release();
 }
diff --git a/WebCore/css/CSSGradientValue.h b/WebCore/css/CSSGradientValue.h
index 1db626a..0988cb6 100644
--- a/WebCore/css/CSSGradientValue.h
+++ b/WebCore/css/CSSGradientValue.h
@@ -57,6 +57,9 @@ public:
     Vector<CSSGradientColorStop>& stops() { return m_stops; }
 
     void sortStopsIfNeeded();
+    
+    virtual bool isLinearGradient() const { return false; }
+    virtual bool isRadialGradient() const { return false; }
 
     bool deprecatedType() const { return m_deprecatedType; } // came from -webkit-gradient
 
@@ -75,7 +78,7 @@ protected:
     // Resolve points/radii to front end values.
     FloatPoint computeEndPoint(CSSPrimitiveValue*, CSSPrimitiveValue*, RenderStyle*, RenderStyle* rootStyle, const IntSize&);
 
-    // Points. Some of these may be nil for linear gradients.
+    // Points. Some of these may be null for linear gradients.
     RefPtr<CSSPrimitiveValue> m_firstX;
     RefPtr<CSSPrimitiveValue> m_firstY;
     
@@ -106,6 +109,8 @@ private:
     {
     }
 
+    virtual bool isLinearGradient() const { return true; }
+
     // Create the gradient for a given size.
     virtual PassRefPtr<Gradient> createGradient(RenderObject*, const IntSize&);
 
@@ -124,20 +129,36 @@ public:
     void setFirstRadius(PassRefPtr<CSSPrimitiveValue> val) { m_firstRadius = val; }
     void setSecondRadius(PassRefPtr<CSSPrimitiveValue> val) { m_secondRadius = val; }
 
+    void setShape(PassRefPtr<CSSPrimitiveValue> val) { m_shape = val; }
+    void setSizingBehavior(PassRefPtr<CSSPrimitiveValue> val) { m_sizingBehavior = val; }
+
+    void setEndHorizontalSize(PassRefPtr<CSSPrimitiveValue> val) { m_endHorizontalSize = val; }
+    void setEndVerticalSize(PassRefPtr<CSSPrimitiveValue> val) { m_endVerticalSize = val; }
+
 private:
     CSSRadialGradientValue(bool deprecatedType = false)
         : CSSGradientValue(deprecatedType)
     {
     }
 
+    virtual bool isRadialGradient() const { return true; }
+
     // Create the gradient for a given size.
     virtual PassRefPtr<Gradient> createGradient(RenderObject*, const IntSize&);
     
     // Resolve points/radii to front end values.
     float resolveRadius(CSSPrimitiveValue*, RenderStyle*, RenderStyle* rootStyle);
     
+    // These may be null for non-deprecated gradients.
     RefPtr<CSSPrimitiveValue> m_firstRadius;
     RefPtr<CSSPrimitiveValue> m_secondRadius;
+
+    // The below are only used for non-deprecated gradients.
+    RefPtr<CSSPrimitiveValue> m_shape;
+    RefPtr<CSSPrimitiveValue> m_sizingBehavior;
+
+    RefPtr<CSSPrimitiveValue> m_endHorizontalSize;
+    RefPtr<CSSPrimitiveValue> m_endVerticalSize;
 };
 
 } // namespace WebCore
diff --git a/WebCore/css/CSSParser.cpp b/WebCore/css/CSSParser.cpp
index 441597a..bf2143c 100644
--- a/WebCore/css/CSSParser.cpp
+++ b/WebCore/css/CSSParser.cpp
@@ -2455,9 +2455,9 @@ bool CSSParser::parseFillImage(RefPtr<CSSValue>& value)
     return false;
 }
 
-PassRefPtr<CSSValue> CSSParser::parseFillPositionXY(bool& xFound, bool& yFound)
+PassRefPtr<CSSValue> CSSParser::parseFillPositionXY(CSSParserValueList* valueList, bool& xFound, bool& yFound)
 {
-    int id = m_valueList->current()->id;
+    int id = valueList->current()->id;
     if (id == CSSValueLeft || id == CSSValueTop || id == CSSValueRight || id == CSSValueBottom || id == CSSValueCenter) {
         int percent = 0;
         if (id == CSSValueLeft || id == CSSValueRight) {
@@ -2479,27 +2479,27 @@ PassRefPtr<CSSValue> CSSParser::parseFillPositionXY(bool& xFound, bool& yFound)
             percent = 50;
         return CSSPrimitiveValue::create(percent, CSSPrimitiveValue::CSS_PERCENTAGE);
     }
-    if (validUnit(m_valueList->current(), FPercent | FLength, m_strict))
-        return CSSPrimitiveValue::create(m_valueList->current()->fValue,
-                                         (CSSPrimitiveValue::UnitTypes)m_valueList->current()->unit);
+    if (validUnit(valueList->current(), FPercent | FLength, m_strict))
+        return CSSPrimitiveValue::create(valueList->current()->fValue,
+                                         (CSSPrimitiveValue::UnitTypes)valueList->current()->unit);
 
     return 0;
 }
 
-void CSSParser::parseFillPosition(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2)
+void CSSParser::parseFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2)
 {
-    CSSParserValue* value = m_valueList->current();
+    CSSParserValue* value = valueList->current();
 
     // Parse the first value.  We're just making sure that it is one of the valid keywords or a percentage/length.
     bool value1IsX = false, value1IsY = false;
-    value1 = parseFillPositionXY(value1IsX, value1IsY);
+    value1 = parseFillPositionXY(valueList, value1IsX, value1IsY);
     if (!value1)
         return;
 
     // It only takes one value for background-position to be correctly parsed if it was specified in a shorthand (since we
     // can assume that any other values belong to the rest of the shorthand).  If we're not parsing a shorthand, though, the
     // value was explicitly specified for our property.
-    value = m_valueList->next();
+    value = valueList->next();
 
     // First check for the comma.  If so, we are finished parsing this value or value pair.
     if (value && value->unit == CSSParserValue::Operator && value->iValue == ',')
@@ -2507,9 +2507,9 @@ void CSSParser::parseFillPosition(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& va
 
     bool value2IsX = false, value2IsY = false;
     if (value) {
-        value2 = parseFillPositionXY(value2IsX, value2IsY);
+        value2 = parseFillPositionXY(valueList, value2IsX, value2IsY);
         if (value2)
-            m_valueList->next();
+            valueList->next();
         else {
             if (!inShorthand()) {
                 value1.clear();
@@ -2698,13 +2698,13 @@ bool CSSParser::parseFillProperty(int propId, int& propId1, int& propId2,
                     break;
                 case CSSPropertyBackgroundPosition:
                 case CSSPropertyWebkitMaskPosition:
-                    parseFillPosition(currValue, currValue2);
+                    parseFillPosition(m_valueList, currValue, currValue2);
                     // parseFillPosition advances the m_valueList pointer
                     break;
                 case CSSPropertyBackgroundPositionX:
                 case CSSPropertyWebkitMaskPositionX: {
                     bool xFound = false, yFound = true;
-                    currValue = parseFillPositionXY(xFound, yFound);
+                    currValue = parseFillPositionXY(m_valueList, xFound, yFound);
                     if (currValue)
                         m_valueList->next();
                     break;
@@ -2712,7 +2712,7 @@ bool CSSParser::parseFillProperty(int propId, int& propId1, int& propId2,
                 case CSSPropertyBackgroundPositionY:
                 case CSSPropertyWebkitMaskPositionY: {
                     bool xFound = true, yFound = false;
-                    currValue = parseFillPositionXY(xFound, yFound);
+                    currValue = parseFillPositionXY(m_valueList, xFound, yFound);
                     if (currValue)
                         m_valueList->next();
                     break;
@@ -2863,7 +2863,7 @@ PassRefPtr<CSSValue> CSSParser::parseAnimationProperty()
 
 bool CSSParser::parseTransformOriginShorthand(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3)
 {
-    parseFillPosition(value1, value2);
+    parseFillPosition(m_valueList, value1, value2);
 
     // now get z
     if (m_valueList->current()) {
@@ -4988,10 +4988,152 @@ bool CSSParser::parseLinearGradient(RefPtr<CSSValue>& gradient)
     return true;
 }
 
-bool CSSParser::parseRadialGradient(RefPtr<CSSValue>&)
+bool CSSParser::parseRadialGradient(RefPtr<CSSValue>& gradient)
 {
-    // FIXME: implement.
-    return false;
+    RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create();
+    
+    // Walk the arguments.
+    CSSParserValueList* args = m_valueList->current()->function->args.get();
+    if (!args || !args->size())
+        return false;
+
+    CSSParserValue* a = args->current();
+    if (!a)
+        return false;
+
+    bool expectComma = false;
+
+    // Optional background-position
+    RefPtr<CSSValue> centerX;
+    RefPtr<CSSValue> centerY;
+    // parseFillPosition advances the args next pointer.
+    parseFillPosition(args, centerX, centerY);
+    a = args->current();
+    
+    if (centerX || centerY) {
+        // Comma
+        if (a->unit != CSSParserValue::Operator || a->iValue != ',')
+            return false;
+
+        a = args->next();
+        if (!a)
+            return false;
+    }
+    
+    ASSERT(!centerX || centerX->isPrimitiveValue());
+    ASSERT(!centerY || centerY->isPrimitiveValue());
+
+    result->setFirstX(static_cast<CSSPrimitiveValue*>(centerX.get()));
+    result->setSecondX(static_cast<CSSPrimitiveValue*>(centerX.get()));
+    // CSS3 radial gradients always share the same start and end point.
+    result->setFirstY(static_cast<CSSPrimitiveValue*>(centerY.get()));
+    result->setSecondY(static_cast<CSSPrimitiveValue*>(centerY.get()));
+    
+    RefPtr<CSSPrimitiveValue> shapeValue;
+    RefPtr<CSSPrimitiveValue> sizeValue;
+
+    // Optional shape and/or size in any order.
+    for (int i = 0; i < 2; ++i) {
+        if (a->unit != CSSPrimitiveValue::CSS_IDENT)
+            break;
+    
+        bool foundValue = false;
+        switch (a->id) {
+        case CSSValueCircle:
+        case CSSValueEllipse:
+            shapeValue = CSSPrimitiveValue::createIdentifier(a->id);
+            foundValue = true;
+            break;
+        case CSSValueClosestSide:
+        case CSSValueClosestCorner:
+        case CSSValueFarthestSide:
+        case CSSValueFarthestCorner:
+        case CSSValueContain:
+        case CSSValueCover:
+            sizeValue = CSSPrimitiveValue::createIdentifier(a->id);
+            foundValue = true;
+            break;
+        }
+
+        if (foundValue) {
+            a = args->next();
+            if (!a)
+                return false;
+
+            expectComma = true;
+        }
+    }
+    
+    result->setShape(shapeValue);
+    result->setSizingBehavior(sizeValue);
+    
+    // Or, two lengths or percentages
+    RefPtr<CSSPrimitiveValue> horizontalSize;
+    RefPtr<CSSPrimitiveValue> verticalSize;
+
+    if (!shapeValue && !sizeValue) {
+        if (validUnit(a, FLength | FPercent, m_strict)) {
+            horizontalSize = CSSPrimitiveValue::create(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit);
+            a = args->next();
+            if (!a)
+                return false;
+
+            expectComma = true;
+        }
+
+        if (validUnit(a, FLength | FPercent, m_strict)) {
+            verticalSize = CSSPrimitiveValue::create(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit);
+
+            a = args->next();
+            if (!a)
+                return false;
+            expectComma = true;
+        }
+    }
+
+    // Must have neither or both.
+    if (!horizontalSize != !verticalSize)
+        return false;
+    
+    result->setEndHorizontalSize(horizontalSize);
+    result->setEndVerticalSize(verticalSize);
+
+    // Now look for 0 or more color stops.
+    while (a) {
+        // Look for the comma before the next stop.
+        if (expectComma) {
+            if (a->unit != CSSParserValue::Operator || a->iValue != ',')
+                return false;
+
+            a = args->next();
+            if (!a)
+                return false;
+        }
+
+        // <color-stop> = <color> [ <percentage> | <length> ]?
+        CSSGradientColorStop stop;
+        stop.m_color = parseGradientColorOrKeyword(this, a);
+        if (!stop.m_color)
+            return false;
+
+        a = args->next();
+        if (a) {
+            if (validUnit(a, FLength | FPercent, m_strict)) {
+                stop.m_position = CSSPrimitiveValue::create(a->fValue, (CSSPrimitiveValue::UnitTypes)a->unit);
+                a = args->next();
+            }
+        }
+        
+        result->addStop(stop);
+        expectComma = true;
+    }
+
+    Vector<CSSGradientColorStop>& stops = result->stops();
+    if (stops.isEmpty())
+        return false;
+
+    gradient = result.release();
+    return true;
 }
 
 bool CSSParser::isGeneratedImageValue(CSSParserValue* val) const
@@ -5225,14 +5367,14 @@ bool CSSParser::parseTransformOrigin(int propId, int& propId1, int& propId2, int
             break;
         case CSSPropertyWebkitTransformOriginX: {
             bool xFound = false, yFound = true;
-            value = parseFillPositionXY(xFound, yFound);
+            value = parseFillPositionXY(m_valueList, xFound, yFound);
             if (value)
                 m_valueList->next();
             break;
         }
         case CSSPropertyWebkitTransformOriginY: {
             bool xFound = true, yFound = false;
-            value = parseFillPositionXY(xFound, yFound);
+            value = parseFillPositionXY(m_valueList, xFound, yFound);
             if (value)
                 m_valueList->next();
             break;
@@ -5260,18 +5402,18 @@ bool CSSParser::parsePerspectiveOrigin(int propId, int& propId1, int& propId2, R
 
     switch (propId) {
         case CSSPropertyWebkitPerspectiveOrigin:
-            parseFillPosition(value, value2);
+            parseFillPosition(m_valueList, value, value2);
             break;
         case CSSPropertyWebkitPerspectiveOriginX: {
             bool xFound = false, yFound = true;
-            value = parseFillPositionXY(xFound, yFound);
+            value = parseFillPositionXY(m_valueList, xFound, yFound);
             if (value)
                 m_valueList->next();
             break;
         }
         case CSSPropertyWebkitPerspectiveOriginY: {
             bool xFound = true, yFound = false;
-            value = parseFillPositionXY(xFound, yFound);
+            value = parseFillPositionXY(m_valueList, xFound, yFound);
             if (value)
                 m_valueList->next();
             break;
diff --git a/WebCore/css/CSSParser.h b/WebCore/css/CSSParser.h
index 2432e79..6633072 100644
--- a/WebCore/css/CSSParser.h
+++ b/WebCore/css/CSSParser.h
@@ -84,8 +84,8 @@ namespace WebCore {
         PassRefPtr<CSSValue> parseBackgroundColor();
 
         bool parseFillImage(RefPtr<CSSValue>&);
-        PassRefPtr<CSSValue> parseFillPositionXY(bool& xFound, bool& yFound);
-        void parseFillPosition(RefPtr<CSSValue>&, RefPtr<CSSValue>&);
+        PassRefPtr<CSSValue> parseFillPositionXY(CSSParserValueList*, bool& xFound, bool& yFound);
+        void parseFillPosition(CSSParserValueList*, RefPtr<CSSValue>&, RefPtr<CSSValue>&);
         void parseFillRepeat(RefPtr<CSSValue>&, RefPtr<CSSValue>&);
         PassRefPtr<CSSValue> parseFillSize(int propId, bool &allowComma);
 
diff --git a/WebCore/css/CSSValueKeywords.in b/WebCore/css/CSSValueKeywords.in
index c3ca1b5..4fce4f3 100644
--- a/WebCore/css/CSSValueKeywords.in
+++ b/WebCore/css/CSSValueKeywords.in
@@ -780,3 +780,13 @@ dot
 double-circle
 triangle
 sesame
+
+# -webkit-radial-gradient
+# circle
+ellipse
+closest-side
+closest-corner
+farthest-side
+farthest-corner
+# contain
+# cover
diff --git a/WebCore/platform/graphics/Gradient.cpp b/WebCore/platform/graphics/Gradient.cpp
index da60f3b..079dcd5 100644
--- a/WebCore/platform/graphics/Gradient.cpp
+++ b/WebCore/platform/graphics/Gradient.cpp
@@ -39,6 +39,7 @@ Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1)
     , m_p1(p1)
     , m_r0(0)
     , m_r1(0)
+    , m_aspectRatio(1)
     , m_stopsSorted(false)
     , m_lastStop(0)
     , m_spreadMethod(SpreadMethodPad)
@@ -46,12 +47,13 @@ Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1)
     platformInit();
 }
 
-Gradient::Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1)
+Gradient::Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio)
     : m_radial(true)
     , m_p0(p0)
     , m_p1(p1)
     , m_r0(r0)
     , m_r1(r1)
+    , m_aspectRatio(aspectRatio)
     , m_stopsSorted(false)
     , m_lastStop(0)
     , m_spreadMethod(SpreadMethodPad)
diff --git a/WebCore/platform/graphics/Gradient.h b/WebCore/platform/graphics/Gradient.h
index 5180a9b..19b9c6a 100644
--- a/WebCore/platform/graphics/Gradient.h
+++ b/WebCore/platform/graphics/Gradient.h
@@ -76,9 +76,9 @@ namespace WebCore {
         {
             return adoptRef(new Gradient(p0, p1));
         }
-        static PassRefPtr<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1)
+        static PassRefPtr<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio = 1)
         {
-            return adoptRef(new Gradient(p0, r0, p1, r1));
+            return adoptRef(new Gradient(p0, r0, p1, r1, aspectRatio));
         }
         virtual ~Gradient();
 
@@ -98,9 +98,15 @@ namespace WebCore {
         void setP0(const FloatPoint& p) { m_p0 = p; }
         void setP1(const FloatPoint& p) { m_p1 = p; }
 
+        float startRadius() const { return m_r0; }
+        float endRadius() const { return m_r1; }
+
+        void setStartRadius(float r) { m_r0 = r; }
+        void setEndRadius(float r) { m_r1 = r; }
+        
+        float aspectRatio() const { return m_aspectRatio; }
+
 #if OS(WINCE) && !PLATFORM(QT)
-        float r0() const { return m_r0; }
-        float r1() const { return m_r1; }
         const Vector<ColorStop, 2>& getStops() const;
 #else
         PlatformGradient platformGradient();
@@ -134,7 +140,7 @@ namespace WebCore {
 #endif
     private:
         Gradient(const FloatPoint& p0, const FloatPoint& p1);
-        Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1);
+        Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio);
 
         void platformInit() { m_gradient = 0; }
         void platformDestroy();
@@ -147,6 +153,7 @@ namespace WebCore {
         FloatPoint m_p1;
         float m_r0;
         float m_r1;
+        float m_aspectRatio; // For elliptical gradient, width / height.
         mutable Vector<ColorStop, 2> m_stops;
         mutable bool m_stopsSorted;
         mutable int m_lastStop;
diff --git a/WebCore/platform/graphics/cg/GradientCG.cpp b/WebCore/platform/graphics/cg/GradientCG.cpp
index 85037e2..0503400 100644
--- a/WebCore/platform/graphics/cg/GradientCG.cpp
+++ b/WebCore/platform/graphics/cg/GradientCG.cpp
@@ -112,14 +112,29 @@ void Gradient::fill(GraphicsContext* context, const FloatRect& rect)
 
 void Gradient::paint(GraphicsContext* context)
 {
+    CGContextRef ctx = context->platformContext();
 #if USE_CG_SHADING
-    CGContextDrawShading(context->platformContext(), platformGradient());
+    CGContextDrawShading(ctx, platformGradient());
 #else
     CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
-    if (m_radial)
-        CGContextDrawRadialGradient(context->platformContext(), platformGradient(), m_p0, m_r0, m_p1, m_r1, extendOptions);
-    else
-        CGContextDrawLinearGradient(context->platformContext(), platformGradient(), m_p0, m_p1, extendOptions);
+    if (m_radial) {
+        bool needScaling = aspectRatio() != 1;
+        if (needScaling) {
+            CGContextSaveGState(ctx);
+            // Scale from the center of the gradient. We only ever scale non-deprecated gradients,
+            // for which m_p0 == m_p1.
+            ASSERT(m_p0 == m_p1);
+            CGContextTranslateCTM(ctx, m_p0.x(), m_p0.y());
+            CGContextScaleCTM(ctx, 1, 1 / aspectRatio());
+            CGContextTranslateCTM(ctx, -m_p0.x(), -m_p0.y());
+        }
+
+        CGContextDrawRadialGradient(ctx, platformGradient(), m_p0, m_r0, m_p1, m_r1, extendOptions);
+        
+        if (needScaling)
+            CGContextRestoreGState(ctx);
+    } else
+        CGContextDrawLinearGradient(ctx, platformGradient(), m_p0, m_p1, extendOptions);
 #endif
 }
 
diff --git a/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp b/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp
index 1ea1a64..2def6ab 100644
--- a/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp
+++ b/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp
@@ -1425,8 +1425,8 @@ void GraphicsContext::fillRect(const FloatRect& r, const Gradient* gradient)
         if (g_radialGradientFiller) {
             // FIXME: don't support 2D scaling at this time
             double scale = (m_data->m_transform.a() + m_data->m_transform.d()) * 0.5;
-            float r0 = gradient->r0() * scale;
-            float r1 = gradient->r1() * scale;
+            float r0 = gradient->startRadius() * scale;
+            float r1 = gradient->endRadius() * scale;
             g_radialGradientFiller(dc, rect, p0, p1, r0, r1, gradient->getStops());
             return;
         }

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list