LayoutTests/ChangeLog

 12011-01-02 Simon Fraser <simon.fraser@apple.com>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 Implement -webkit-linear-gradient and -webkit-radial-gradient
 6 https://bugs.webkit.org/show_bug.cgi?id=28152
 7
 8 Enhance gradient parsing test for radial gradients, and add new tests
 9 for various radial gradients.
 10
 11 * fast/gradients/css3-gradient-parsing-expected.txt:
 12 * fast/gradients/css3-gradient-parsing.html:
 13 * fast/gradients/css3-radial-gradients-expected.txt: Added.
 14 * fast/gradients/css3-radial-gradients.html: Added.
 15 * fast/gradients/css3-radial-gradients2-expected.txt: Added.
 16 * fast/gradients/css3-radial-gradients2.html: Added.
 17 * platform/mac/fast/gradients/css3-radial-gradients-expected.checksum: Added.
 18 * platform/mac/fast/gradients/css3-radial-gradients-expected.png: Added.
 19 * platform/mac/fast/gradients/css3-radial-gradients2-expected.checksum: Added.
 20 * platform/mac/fast/gradients/css3-radial-gradients2-expected.png: Added.
 21
1222011-01-01 Simon Fraser <simon.fraser@apple.com>
223
324 Reviewed by NOBODY (OOPS!).

LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt

1 Tests -webkit-linear-gradient and -webkit-radial-gradient
2 
3 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 1-webkit-linear-gradient
42
53
64PASS testGradient("background-image: -webkit-linear-gradient(black 0%", "background-image") is "none"

@@PASS testGradient("background-image: -webkit-linear-gradient(black 10px, white 2
1917PASS testGradient("background-image: -webkit-linear-gradient(top left, black 0%)", "background-image") is "-webkit-linear-gradient(left top, black 0%)"
2018PASS testGradient("background-image: -webkit-linear-gradient(top, black 0%)", "background-image") is "-webkit-linear-gradient(top, black 0%)"
2119PASS testGradient("background-image: -webkit-linear-gradient(10deg, black 0%)", "background-image") is "-webkit-linear-gradient(10deg, black 0%)"
 20-webkit-radial-gradient
 21
 22
 23PASS testGradient("background-image: -webkit-radial-gradient(white, black)", "background-image") is "-webkit-radial-gradient(center, white, black)"
 24PASS testGradient("background-image: -webkit-radial-gradient(bottom right, white, black)", "background-image") is "-webkit-radial-gradient(100% 100%, white, black)"
 25PASS testGradient("background-image: -webkit-radial-gradient(cover, white, black)", "background-image") is "-webkit-radial-gradient(center, ellipse cover, white, black)"
 26PASS testGradient("background-image: -webkit-radial-gradient(circle, white, black)", "background-image") is "-webkit-radial-gradient(center, circle cover, white, black)"
 27PASS testGradient("background-image: -webkit-radial-gradient(circle contain, white, black)", "background-image") is "-webkit-radial-gradient(center, circle contain, white, black)"
 28PASS testGradient("background-image: -webkit-radial-gradient(top, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(50% 0%, circle contain, white, black)"
 29PASS testGradient("background-image: -webkit-radial-gradient(top left, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(0% 0%, circle contain, white, black)"
 30PASS testGradient("background-image: -webkit-radial-gradient(10px 20%, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(10px 20%, circle contain, white, black)"
 31PASS testGradient("background-image: -webkit-radial-gradient(10px, 20%, circle contain, white, black)", "background-image") is "none"
 32PASS testGradient("background-image: -webkit-radial-gradient(circle 10px 20%, circle contain, white, black)", "background-image") is "none"
 33PASS testGradient("background-image: -webkit-radial-gradient(circle 10px, circle contain, white, black)", "background-image") is "none"
 34PASS testGradient("background-image: -webkit-radial-gradient(center, 10px, white, black)", "background-image") is "none"
 35PASS testGradient("background-image: -webkit-radial-gradient(center, 10px 10px, white, black)", "background-image") is "-webkit-radial-gradient(50% 50%, 10px 10px, white, black)"
 36PASS testGradient("background-image: -webkit-radial-gradient(ellipse farthest-corner, white, black)", "background-image") is "-webkit-radial-gradient(center, ellipse farthest-corner, white, black)"
 37PASS testGradient("background-image: -webkit-radial-gradient(circle closest-side, white, black)", "background-image") is "-webkit-radial-gradient(center, circle closest-side, white, black)"
2238PASS successfullyParsed is true
2339
2440TEST COMPLETE

LayoutTests/fast/gradients/css3-gradient-parsing.html

1111
1212
1313<script type="text/javascript">
14 description('Tests -webkit-linear-gradient and -webkit-radial-gradient');
1514
1615function testGradient(css, queryProp)
1716{

@@function testSame(dir, prop, altProp, value)
3029 shouldBeEqualToString('test("' + dir + '", "' + altProp + ': ' + value + '", "' + prop + '")', value);
3130}
3231
 32debug('<p>-webkit-linear-gradient</p>');
 33
3334shouldBe('testGradient("background-image: -webkit-linear-gradient(black 0%", "background-image")', '"none"');
3435shouldBe('testGradient("background-image: -webkit-linear-gradient(top)", "background-image")', '"none"');
3536shouldBe('testGradient("background-image: -webkit-linear-gradient(10deg)", "background-image")', '"none"');

@@shouldBe('testGradient("background-image: -webkit-linear-gradient(top left, blac
4748shouldBe('testGradient("background-image: -webkit-linear-gradient(top, black 0%)", "background-image")', '"-webkit-linear-gradient(top, black 0%)"');
4849shouldBe('testGradient("background-image: -webkit-linear-gradient(10deg, black 0%)", "background-image")', '"-webkit-linear-gradient(10deg, black 0%)"');
4950
 51debug('<p>-webkit-radial-gradient</p>');
 52
 53shouldBe('testGradient("background-image: -webkit-radial-gradient(white, black)", "background-image")', '"-webkit-radial-gradient(center, white, black)"');
 54shouldBe('testGradient("background-image: -webkit-radial-gradient(bottom right, white, black)", "background-image")', '"-webkit-radial-gradient(100% 100%, white, black)"');
 55shouldBe('testGradient("background-image: -webkit-radial-gradient(cover, white, black)", "background-image")', '"-webkit-radial-gradient(center, ellipse cover, white, black)"');
 56shouldBe('testGradient("background-image: -webkit-radial-gradient(circle, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle cover, white, black)"');
 57shouldBe('testGradient("background-image: -webkit-radial-gradient(circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle contain, white, black)"');
 58shouldBe('testGradient("background-image: -webkit-radial-gradient(top, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(50% 0%, circle contain, white, black)"');
 59shouldBe('testGradient("background-image: -webkit-radial-gradient(top left, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(0% 0%, circle contain, white, black)"');
 60shouldBe('testGradient("background-image: -webkit-radial-gradient(10px 20%, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(10px 20%, circle contain, white, black)"');
 61shouldBe('testGradient("background-image: -webkit-radial-gradient(10px, 20%, circle contain, white, black)", "background-image")', '"none"');
 62shouldBe('testGradient("background-image: -webkit-radial-gradient(circle 10px 20%, circle contain, white, black)", "background-image")', '"none"');
 63shouldBe('testGradient("background-image: -webkit-radial-gradient(circle 10px, circle contain, white, black)", "background-image")', '"none"');
 64shouldBe('testGradient("background-image: -webkit-radial-gradient(center, 10px, white, black)", "background-image")', '"none"');
 65shouldBe('testGradient("background-image: -webkit-radial-gradient(center, 10px 10px, white, black)", "background-image")', '"-webkit-radial-gradient(50% 50%, 10px 10px, white, black)"');
 66shouldBe('testGradient("background-image: -webkit-radial-gradient(ellipse farthest-corner, white, black)", "background-image")', '"-webkit-radial-gradient(center, ellipse farthest-corner, white, black)"');
 67shouldBe('testGradient("background-image: -webkit-radial-gradient(circle closest-side, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle closest-side, white, black)"');
 68
5069successfullyParsed = true;
5170
5271</script>

LayoutTests/fast/gradients/css3-radial-gradients-expected.txt

 1
 2
 3

LayoutTests/fast/gradients/css3-radial-gradients.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5 <style type="text/css" media="screen">
 6 .box {
 7 display: inline-block;
 8 height: 120px;
 9 width: 200px;
 10 margin: 10px;
 11 border: 1px solid black;
 12 background-repeat: no-repeat;
 13 }
 14
 15 .gradient1 {
 16 background-image: -webkit-radial-gradient(ellipse closest-corner, white, black);
 17 background-image: -moz-radial-gradient(ellipse closest-corner, white, black);
 18 }
 19
 20 .gradient2 {
 21 background-image: -webkit-radial-gradient(50px 50%, circle closest-side, white, black);
 22 background-image: -moz-radial-gradient(50px 50%, circle closest-side, white, black);
 23 }
 24
 25 .gradient3 {
 26 background-image: -webkit-radial-gradient(20px 50px, ellipse closest-corner, white, black);
 27 background-image: -moz-radial-gradient(20px 50px, ellipse closest-corner, white, black);
 28 }
 29
 30 .gradient4 {
 31 background-image: -webkit-radial-gradient(20% 20%, circle closest-corner, white, black);
 32 background-image: -moz-radial-gradient(20% 20%, circle closest-corner, white, black);
 33 }
 34
 35 .gradient5 {
 36 background-image: -webkit-radial-gradient(circle cover, red, green, blue);
 37 background-image: -moz-radial-gradient(circle cover, red, green, blue);
 38 }
 39
 40 .gradient6 {
 41 background-image: -webkit-radial-gradient(left, circle cover, red, green 30%, blue);
 42 background-image: -moz-radial-gradient(left, circle cover, red, green 30%, blue);
 43 }
 44
 45 .gradient7 {
 46 background-image: -webkit-radial-gradient(ellipse cover, red, green 80px, blue);
 47 background-image: -moz-radial-gradient(ellipse cover, red, green 80px, blue);
 48 }
 49
 50 .gradient8 {
 51 background-image: -webkit-radial-gradient(circle cover, red 20%, green, blue 150%);
 52 background-image: -moz-radial-gradient(circle cover, red 20%, green, blue 150%);
 53 }
 54
 55 .gradient9 {
 56 background-image: -webkit-radial-gradient(20% 20%, circle closest-corner, red -50%, green, blue 150%);
 57 background-image: -moz-radial-gradient(20% 20%, circle closest-corner, red -50%, green, blue 150%);
 58 }
 59
 60 </style>
 61 <script type="text/javascript" charset="utf-8">
 62 if (window.layoutTestController) {
 63 var dumpPixels = true;
 64 layoutTestController.dumpAsText(dumpPixels);
 65 }
 66 </script>
 67</head>
 68<body>
 69
 70 <div class="gradient1 box"></div>
 71 <div class="gradient2 box"></div>
 72 <div class="gradient3 box"></div>
 73 <br>
 74 <div class="gradient4 box"></div>
 75 <div class="gradient5 box"></div>
 76 <div class="gradient6 box"></div>
 77 <br>
 78 <div class="gradient7 box"></div>
 79 <div class="gradient8 box"></div>
 80 <div class="gradient9 box"></div>
 81
 82</body>
 83</html>

LayoutTests/fast/gradients/css3-radial-gradients2-expected.txt

 1
 2

LayoutTests/fast/gradients/css3-radial-gradients2.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5 <style type="text/css" media="screen">
 6 .box {
 7 display: inline-block;
 8 height: 250px;
 9 width: 200px;
 10 margin: 10px;
 11 border: 1px solid black;
 12 background-repeat: no-repeat;
 13 }
 14
 15 .gradient1 {
 16 /* Green should coincide with the edge of the box, with blue fill (buggy in CG). */
 17 background-image: -webkit-radial-gradient(left, circle closest-corner, red, green 150px, blue);
 18 background-image: -moz-radial-gradient(left, circle closest-corner, red, green 150px, blue);
 19 }
 20
 21 .gradient2 {
 22 /* Fill should be orange (buggy in CG). */
 23 background-image: -webkit-radial-gradient(left, circle closest-corner, red, green 150px, blue, orange 101%);
 24 background-image: -moz-radial-gradient(left, circle closest-corner, red, green 150px, blue, orange 101%);
 25 }
 26
 27 .gradient3 {
 28 background-image: -webkit-radial-gradient(-100px center, ellipse farthest-corner, black, white);
 29 background-image: -moz-radial-gradient(-100px center, ellipse farthest-corner, black, white);
 30 }
 31
 32 .gradient4 {
 33 background-image: -webkit-radial-gradient(-100px center, ellipse closest-corner, black, white);
 34 background-image: -moz-radial-gradient(-100px center, ellipse closest-corner, black, white);
 35 }
 36
 37 .gradient5 {
 38 background-image: -webkit-radial-gradient(bottom right, black, white);
 39 background-image: -moz-radial-gradient(bottom right, black, white);
 40 }
 41
 42 .gradient6 {
 43 background-image: -webkit-radial-gradient(50% 20%, ellipse contain, black, white);
 44 background-image: -moz-radial-gradient(50% 20%, ellipse contain, black, white);
 45 }
 46
 47 </style>
 48 <script type="text/javascript" charset="utf-8">
 49 if (window.layoutTestController) {
 50 var dumpPixels = true;
 51 layoutTestController.dumpAsText(dumpPixels);
 52 }
 53 </script>
 54</head>
 55<body>
 56
 57 <div class="gradient1 box"></div>
 58 <div class="gradient2 box"></div>
 59 <div class="gradient3 box"></div>
 60 <br>
 61 <div class="gradient4 box"></div>
 62 <div class="gradient5 box"></div>
 63 <div class="gradient6 box"></div>
 64
 65</body>
 66</html>

LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.checksum

 1af88d9c4da4d2856797d2ab20f7f2927
02\ No newline at end of file

LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.png


Added

INVALID: Image lacks a checksum. This will fail with a MISSING error in run-webkit-tests. Always generate new png files using run-webkit-tests.

LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.checksum

 163d36fd71d88bb4bbe7ae42f8ce2f873
02\ No newline at end of file

LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.png


Added

INVALID: Image lacks a checksum. This will fail with a MISSING error in run-webkit-tests. Always generate new png files using run-webkit-tests.

WebCore/ChangeLog

 12011-01-02 Simon Fraser <simon.fraser@apple.com>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 Implement -webkit-linear-gradient and -webkit-radial-gradient
 6 https://bugs.webkit.org/show_bug.cgi?id=28152
 7
 8 Add support for -webkit-radial-gradients.
 9
 10 Tests: fast/gradients/css3-radial-gradients.html
 11 fast/gradients/css3-radial-gradients2.html
 12
 13 * css/CSSGradientValue.cpp:
 14 (WebCore::blend): Used to blend colors, which is necessary when truncating
 15 the start of radial gradients.
 16 (WebCore::GradientStop): Small struct to aid gradient stop processing.
 17 (WebCore::CSSGradientValue::addStops): Rename 'positions' to 'stops', and store
 18 the color so that we can blend it when truncating radial gradients. Changed
 19 to handle both linear and radial gradients.
 20 (WebCore::CSSRadialGradientValue::cssText): Update to follow the spec.
 21 (WebCore::CSSRadialGradientValue::resolveRadius): Stylistic change.
 22 (WebCore::distanceToClosestCorner): New utility method.
 23 (WebCore::distanceToFarthestCorner): Ditto.
 24 (WebCore::CSSRadialGradientValue::createGradient): New logic to deal with shape and
 25 fill rules.
 26
 27 * css/CSSGradientValue.h:
 28 (WebCore::CSSGradientValue::isLinearGradient): Color-stop processing needs to know what
 29 kind of gradient it's dealing with, so add new isFooGradient methods.
 30 (WebCore::CSSGradientValue::isRadialGradient): Ditto.
 31 (WebCore::CSSLinearGradientValue::isLinearGradient):
 32 (WebCore::CSSRadialGradientValue::setShape): New setters for shape, size etc.
 33 (WebCore::CSSRadialGradientValue::setSizingBehavior):
 34 (WebCore::CSSRadialGradientValue::setEndHorizontalSize):
 35 (WebCore::CSSRadialGradientValue::setEndVerticalSize):
 36 (WebCore::CSSRadialGradientValue::isRadialGradient):
 37
 38 * css/CSSParser.h:
 39 * css/CSSParser.h: Pass a CSSParserValueList* into parseFillPositionXY() and parseFillPosition()
 40 so we can use parseFillPosition() when parsing a gradient function.
 41 * css/CSSParser.cpp:
 42 (WebCore::CSSParser::parseFillPositionXY): Pass in a CSSParserValueList*
 43 (WebCore::CSSParser::parseFillPosition): Ditto
 44 (WebCore::CSSParser::parseFillProperty): Pass m_valueList to parseFillPosition.
 45 (WebCore::CSSParser::parseTransformOriginShorthand): Ditto
 46 (WebCore::CSSParser::parseRadialGradient): Flesh out radial gradient parsing.
 47 (WebCore::CSSParser::parseTransformOrigin): Pass m_valueList to parseFillPosition.
 48 (WebCore::CSSParser::parsePerspectiveOrigin): Ditto
 49
 50 * css/CSSValueKeywords.in: New keywords for radial gradient shape and fill behaviors.
 51
 52 * platform/graphics/Gradient.cpp:
 53 (WebCore::Gradient::Gradient): Pass aspect ratio for elliptical gradients.
 54 * platform/graphics/Gradient.h: Add aspect ratio for elliptical gradients.
 55 (WebCore::Gradient::create):
 56 (WebCore::Gradient::startRadius): Expose radii.
 57 (WebCore::Gradient::endRadius):
 58 (WebCore::Gradient::setStartRadius): Setters are required for when we need to scale color stops.
 59 (WebCore::Gradient::setEndRadius):
 60 (WebCore::Gradient::aspectRatio):
 61 * platform/graphics/cg/GradientCG.cpp:
 62 (WebCore::Gradient::paint): For elliptical gradients, scale the CTM.
 63 * platform/graphics/wince/GraphicsContextWinCE.cpp:
 64 (WebCore::GraphicsContext::fillRect): Use start/endRadius() rather than r0() and r1().
 65
1662011-01-01 Simon Fraser <simon.fraser@apple.com>
267
368 Reviewed by NOBODY (OOPS!).

WebCore/css/CSSGradientValue.cpp

@@void CSSGradientValue::sortStopsIfNeeded()
8282 }
8383}
8484
 85static inline int blend(int from, int to, float progress)
 86{
 87 return int(from + (to - from) * progress);
 88}
 89
 90static inline Color blend(const Color& from, const Color& to, float progress)
 91{
 92 // FIXME: when we interpolate gradients using premultiplied colors, this should also do premultiplication.
 93 return Color(blend(from.red(), to.red(), progress),
 94 blend(from.green(), to.green(), progress),
 95 blend(from.blue(), to.blue(), progress),
 96 blend(from.alpha(), to.alpha(), progress));
 97}
 98
 99struct GradientStop {
 100 Color color;
 101 float offset;
 102 bool specified;
 103
 104 GradientStop()
 105 : offset(0)
 106 , specified(false)
 107 { }
 108};
 109
85110void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, RenderStyle* rootStyle)
86111{
87112 RenderStyle* style = renderer->style();

@@void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, Rend
109134 }
110135
111136 size_t numStops = m_stops.size();
112  // Pair of <offset, specified>.
113  Vector<pair<float, bool> > positions(numStops);
 137
 138 Vector<GradientStop> stops(numStops);
114139
115140 float gradientLength = 0;
116141 bool computedGradientLength = false;
117142
 143 FloatPoint gradientStart = gradient->p0();
 144 FloatPoint gradientEnd;
 145 if (isLinearGradient())
 146 gradientEnd = gradient->p1();
 147 else if (isRadialGradient())
 148 gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0);
 149
118150 for (size_t i = 0; i < numStops; ++i) {
119151 const CSSGradientColorStop& stop = m_stops[i];
120152
121  positions[i].first = 0;
122  positions[i].second = false;
 153 stops[i].color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(stop.m_color.get());
123154
124155 if (stop.m_position) {
125156 int type = stop.m_position->primitiveType();
126157 if (type == CSSPrimitiveValue::CSS_PERCENTAGE)
127  positions[i].first = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
 158 stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
128159 else if (CSSPrimitiveValue::isUnitTypeLength(type)) {
129160 float length = stop.m_position->computeLengthFloat(style, rootStyle, style->effectiveZoom());
130161 if (!computedGradientLength) {
131  float xDelta = gradient->p0().x() - gradient->p1().x();
132  float yDelta = gradient->p0().y() - gradient->p1().y();
133  gradientLength = sqrtf(xDelta * xDelta + yDelta * yDelta);
 162 FloatSize gradientSize(gradientStart - gradientEnd);
 163 gradientLength = gradientSize.diagonalLength();
134164 }
135  positions[i].first = (gradientLength > 0) ? length / gradientLength : 0;
 165 stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0;
136166 } else {
137167 ASSERT_NOT_REACHED();
138  positions[i].first = 0;
 168 stops[i].offset = 0;
139169 }
140  positions[i].second = true;
 170 stops[i].specified = true;
141171 } else {
142172 // If the first color-stop does not have a position, its position defaults to 0%.
143173 // If the last color-stop does not have a position, its position defaults to 100%.
144174 if (!i) {
145  positions[i].first = 0;
146  positions[i].second = true;
 175 stops[i].offset = 0;
 176 stops[i].specified = true;
147177 } else if (numStops > 1 && i == numStops - 1) {
148  positions[i].first = 1;
149  positions[i].second = true;
 178 stops[i].offset = 1;
 179 stops[i].specified = true;
150180 }
151181 }
152182
153183 // If a color-stop has a position that is less than the specified position of any
154184 // color-stop before it in the list, its position is changed to be equal to the
155185 // largest specified position of any color-stop before it.
156  if (positions[i].second && i > 0) {
 186 if (stops[i].specified && i > 0) {
157187 size_t prevSpecifiedIndex;
158188 for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; --prevSpecifiedIndex) {
159  if (positions[prevSpecifiedIndex].second)
 189 if (stops[prevSpecifiedIndex].specified)
160190 break;
161191 }
162192
163  if (positions[i].first < positions[prevSpecifiedIndex].first)
164  positions[i].first = positions[prevSpecifiedIndex].first;
 193 if (stops[i].offset < stops[prevSpecifiedIndex].offset)
 194 stops[i].offset = stops[prevSpecifiedIndex].offset;
165195 }
166196 }
167197
168  ASSERT(positions[0].second && positions[numStops - 1].second);
 198 ASSERT(stops[0].specified && stops[numStops - 1].specified);
169199
170200 // If any color-stop still does not have a position, then, for each run of adjacent
171201 // color-stops without positions, set their positions so that they are evenly spaced

@@void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, Rend
175205 bool inUnspecifiedRun = false;
176206
177207 for (size_t i = 0; i < numStops; ++i) {
178  if (!positions[i].second && !inUnspecifiedRun) {
 208 if (!stops[i].specified && !inUnspecifiedRun) {
179209 unspecifiedRunStart = i;
180210 inUnspecifiedRun = true;
181  } else if (positions[i].second && inUnspecifiedRun) {
 211 } else if (stops[i].specified && inUnspecifiedRun) {
182212 size_t unspecifiedRunEnd = i;
183213
184214 if (unspecifiedRunStart < unspecifiedRunEnd) {
185  float lastSpecifiedOffset = positions[unspecifiedRunStart - 1].first;
186  float nextSpecifiedOffset = positions[unspecifiedRunEnd].first;
 215 float lastSpecifiedOffset = stops[unspecifiedRunStart - 1].offset;
 216 float nextSpecifiedOffset = stops[unspecifiedRunEnd].offset;
187217 float delta = (nextSpecifiedOffset - lastSpecifiedOffset) / (unspecifiedRunEnd - unspecifiedRunStart + 1);
188218
189219 for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j)
190  positions[j].first = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
 220 stops[j].offset = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
191221 }
192222
193223 inUnspecifiedRun = false;

@@void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, Rend
196226 }
197227
198228 // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and ajusting the stops.
199  if (numStops > 1 && (positions[0].first < 0 || positions[numStops - 1].first > 1)) {
200  float firstOffset = positions[0].first;
201  float lastOffset = positions[numStops - 1].first;
202  float scale = lastOffset - firstOffset;
203 
204  for (size_t i = 0; i < numStops; ++i)
205  positions[i].first = (positions[i].first - firstOffset) / scale;
206 
207  FloatPoint p0 = gradient->p0();
208  FloatPoint p1 = gradient->p1();
209  gradient->setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
210  gradient->setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
 229 if (numStops > 1 && (stops[0].offset < 0 || stops[numStops - 1].offset > 1)) {
 230
 231 if (isLinearGradient()) {
 232 float firstOffset = stops[0].offset;
 233 float lastOffset = stops[numStops - 1].offset;
 234 float scale = lastOffset - firstOffset;
 235
 236 for (size_t i = 0; i < numStops; ++i)
 237 stops[i].offset = (stops[i].offset - firstOffset) / scale;
 238
 239 FloatPoint p0 = gradient->p0();
 240 FloatPoint p1 = gradient->p1();
 241 gradient->setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
 242 gradient->setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
 243 } else if (isRadialGradient()) {
 244 // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
 245 float firstOffset = 0;
 246 float lastOffset = stops[numStops - 1].offset;
 247 float scale = lastOffset - firstOffset;
 248
 249 // Reset points below 0 to the first visible color.
 250 size_t firstZeroOrGreaterIndex = 0;
 251 for (size_t i = 0; i < numStops; ++i) {
 252 if (stops[i].offset >= 0) {
 253 firstZeroOrGreaterIndex = i;
 254 break;
 255 }
 256 }
 257
 258 if (firstZeroOrGreaterIndex > 0 && stops[firstZeroOrGreaterIndex].offset > 0) {
 259 float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
 260 float nextOffset = stops[firstZeroOrGreaterIndex].offset;
 261
 262 float interStopProportion = -prevOffset / (nextOffset - prevOffset);
 263 Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
 264
 265 // Clamp the positions to 0 and set the color.
 266 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
 267 stops[i].offset = 0;
 268 stops[i].color = blendedColor;
 269 }
 270 }
 271
 272 for (size_t i = 0; i < numStops; ++i)
 273 stops[i].offset /= scale;
 274
 275 gradient->setStartRadius(gradient->startRadius() * scale);
 276 gradient->setEndRadius(gradient->endRadius() * scale);
 277 }
211278 }
212279
213  for (unsigned i = 0; i < numStops; i++) {
214  const CSSGradientColorStop& stop = m_stops[i];
215  Color color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(stop.m_color.get());
216  gradient->addColorStop(positions[i].first, color);
217  }
 280 for (unsigned i = 0; i < numStops; i++)
 281 gradient->addColorStop(stops[i].offset, stops[i].color);
218282
219283 gradient->setStopsSorted(true);
220284}

@@String CSSRadialGradientValue::cssText() const
404468{
405469 String result;
406470
407  if (m_deprecatedType)
 471 if (m_deprecatedType) {
408472 result = "-webkit-gradient(radial, ";
409  else
410  result = "-webkit-radial-gradient(";
411473
412  result += m_firstX->cssText() + " ";
413  result += m_firstY->cssText() + ", ";
414  result += m_firstRadius->cssText() + ", ";
415  result += m_secondX->cssText() + " ";
416  result += m_secondY->cssText();
417  result += ", ";
418  result += m_secondRadius->cssText();
419 
420  for (unsigned i = 0; i < m_stops.size(); i++) {
 474 result += m_firstX->cssText() + " ";
 475 result += m_firstY->cssText() + ", ";
 476 result += m_firstRadius->cssText() + ", ";
 477 result += m_secondX->cssText() + " ";
 478 result += m_secondY->cssText();
421479 result += ", ";
422  if (m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
423  result += "from(" + m_stops[i].m_color->cssText() + ")";
424  else if (m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
425  result += "to(" + m_stops[i].m_color->cssText() + ")";
 480 result += m_secondRadius->cssText();
 481
 482 // FIXME: share?
 483 for (unsigned i = 0; i < m_stops.size(); i++) {
 484 const CSSGradientColorStop& stop = m_stops[i];
 485 result += ", ";
 486 if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
 487 result += "from(" + stop.m_color->cssText() + ")";
 488 else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
 489 result += "to(" + stop.m_color->cssText() + ")";
 490 else
 491 result += "color-stop(" + String::number(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + stop.m_color->cssText() + ")";
 492 }
 493 } else {
 494
 495 result = "-webkit-radial-gradient(";
 496 if (m_firstX && m_firstY) {
 497 result += m_firstX->cssText() + " " + m_firstY->cssText();
 498 } else if (m_firstX)
 499 result += m_firstX->cssText();
 500 else if (m_firstY)
 501 result += m_firstY->cssText();
426502 else
427  result += "color-stop(" + String::number(m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + m_stops[i].m_color->cssText() + ")";
 503 result += "center";
 504
 505
 506 if (m_shape || m_sizingBehavior) {
 507 result += ", ";
 508 if (m_shape)
 509 result += m_shape->cssText() + " ";
 510 else
 511 result += "ellipse ";
 512
 513 if (m_sizingBehavior)
 514 result += m_sizingBehavior->cssText();
 515 else
 516 result += "cover";
 517
 518 } else if (m_endHorizontalSize && m_endVerticalSize) {
 519 result += ", ";
 520 result += m_endHorizontalSize->cssText() + " " + m_endVerticalSize->cssText();
 521 }
 522
 523 for (unsigned i = 0; i < m_stops.size(); i++) {
 524 const CSSGradientColorStop& stop = m_stops[i];
 525 result += ", ";
 526 result += stop.m_color->cssText();
 527 if (stop.m_position)
 528 result += " " + stop.m_position->cssText();
 529 }
428530 }
 531
429532 result += ")";
430533 return result;
431534}

@@float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, RenderSty
434537{
435538 float zoomFactor = style->effectiveZoom();
436539
437  float result = 0.f;
 540 float result = 0;
438541 if (radius->primitiveType() == CSSPrimitiveValue::CSS_NUMBER) // Can the radius be a percentage?
439542 result = radius->getFloatValue() * zoomFactor;
440543 else

@@float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, RenderSty
443546 return result;
444547}
445548
 549static float distanceToClosestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
 550{
 551 FloatPoint topLeft;
 552 float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
 553
 554 FloatPoint topRight(size.width(), 0);
 555 float topRightDistance = FloatSize(p - topRight).diagonalLength();
 556
 557 FloatPoint bottomLeft(0, size.height());
 558 float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
 559
 560 FloatPoint bottomRight(size.width(), size.height());
 561 float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
 562
 563 corner = topLeft;
 564 float minDistance = topLeftDistance;
 565 if (topRightDistance < minDistance) {
 566 minDistance = topRightDistance;
 567 corner = topRight;
 568 }
 569
 570 if (bottomLeftDistance < minDistance) {
 571 minDistance = bottomLeftDistance;
 572 corner = bottomLeft;
 573 }
 574
 575 if (bottomRightDistance < minDistance) {
 576 minDistance = bottomRightDistance;
 577 corner = bottomRight;
 578 }
 579 return minDistance;
 580}
 581
 582static float distanceToFarthestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
 583{
 584 FloatPoint topLeft;
 585 float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
 586
 587 FloatPoint topRight(size.width(), 0);
 588 float topRightDistance = FloatSize(p - topRight).diagonalLength();
 589
 590 FloatPoint bottomLeft(0, size.height());
 591 float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
 592
 593 FloatPoint bottomRight(size.width(), size.height());
 594 float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
 595
 596 corner = topLeft;
 597 float maxDistance = topLeftDistance;
 598 if (topRightDistance > maxDistance) {
 599 maxDistance = topRightDistance;
 600 corner = topRight;
 601 }
 602
 603 if (bottomLeftDistance > maxDistance) {
 604 maxDistance = bottomLeftDistance;
 605 corner = bottomLeft;
 606 }
 607
 608 if (bottomRightDistance > maxDistance) {
 609 maxDistance = bottomRightDistance;
 610 corner = bottomRight;
 611 }
 612 return maxDistance;
 613}
 614
 615// Compute horizontal radius of ellipse with center at 0,0 which passes through p, and has
 616// width/height given by aspectRatio.
 617static inline float horizontalEllipseRadius(const FloatSize& p, float aspectRatio)
 618{
 619 // x^2/a^2 + y^2/b^2 = 1
 620 // a/b = aspectRatio, b = a/aspectRatio
 621 // a = sqrt(x^2 + y^2/(1/r^2))
 622 return sqrtf(p.width() * p.width() + (p.height() * p.height()) / (1 / (aspectRatio * aspectRatio)));
 623}
 624
446625// FIXME: share code with the linear version
447626PassRefPtr<Gradient> CSSRadialGradientValue::createGradient(RenderObject* renderer, const IntSize& size)
448627{

@@PassRefPtr<Gradient> CSSRadialGradientValue::createGradient(RenderObject* render
451630 RenderStyle* rootStyle = renderer->document()->documentElement()->renderStyle();
452631
453632 FloatPoint firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), renderer->style(), rootStyle, size);
 633 if (!m_firstX)
 634 firstPoint.setX(size.width() / 2);
 635 if (!m_firstY)
 636 firstPoint.setY(size.height() / 2);
 637
454638 FloatPoint secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), renderer->style(), rootStyle, size);
 639 if (!m_secondX)
 640 secondPoint.setX(size.width() / 2);
 641 if (!m_secondY)
 642 secondPoint.setY(size.height() / 2);
455643
456  float firstRadius = resolveRadius(m_firstRadius.get(), renderer->style(), rootStyle);
457  float secondRadius = resolveRadius(m_secondRadius.get(), renderer->style(), rootStyle);
458  RefPtr<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius);
459 
460  // Now add the stops.
461  sortStopsIfNeeded();
 644 float firstRadius = 0;
 645 if (m_firstRadius)
 646 firstRadius = resolveRadius(m_firstRadius.get(), renderer->style(), rootStyle);
 647
 648 float secondRadius = 0;
 649 float aspectRatio = 1; // width / height.
 650 if (m_secondRadius)
 651 secondRadius = resolveRadius(m_secondRadius.get(), renderer->style(), rootStyle);
 652 else if (m_endHorizontalSize || m_endVerticalSize) {
 653 secondRadius = resolveRadius(m_endHorizontalSize.get(), renderer->style(), rootStyle);
 654 aspectRatio = secondRadius / resolveRadius(m_endVerticalSize.get(), renderer->style(), rootStyle);
 655 } else {
 656 enum GradientShape { Circle, Ellipse };
 657 GradientShape shape = Ellipse;
 658 if (m_shape && m_shape->primitiveType() == CSSPrimitiveValue::CSS_IDENT && m_shape->getIdent() == CSSValueCircle)
 659 shape = Circle;
 660
 661 enum GradientFill { ClosestSide, ClosestCorner, FarthestSide, FarthestCorner };
 662 GradientFill fill = FarthestCorner;
 663
 664 if (m_sizingBehavior && m_sizingBehavior->primitiveType() == CSSPrimitiveValue::CSS_IDENT) {
 665 switch (m_sizingBehavior->getIdent()) {
 666 case CSSValueContain:
 667 case CSSValueClosestSide:
 668 fill = ClosestSide;
 669 break;
 670 case CSSValueClosestCorner:
 671 fill = ClosestCorner;
 672 break;
 673 case CSSValueFarthestSide:
 674 fill = FarthestSide;
 675 break;
 676 case CSSValueCover:
 677 case CSSValueFarthestCorner:
 678 fill = FarthestCorner;
 679 break;
 680 }
 681 }
 682
 683 // Now compute the end radii based on the second point, shape and fill.
 684
 685 // Horizontal
 686 switch (fill) {
 687 case ClosestSide: {
 688 float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
 689 float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
 690 if (shape == Circle) {
 691 float smaller = min(xDist, yDist);
 692 xDist = smaller;
 693 yDist = smaller;
 694 }
 695 secondRadius = xDist;
 696 aspectRatio = xDist / yDist;
 697 break;
 698 }
 699 case FarthestSide: {
 700 float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
 701 float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
 702 if (shape == Circle) {
 703 float larger = max(xDist, yDist);
 704 xDist = larger;
 705 yDist = larger;
 706 }
 707 secondRadius = xDist;
 708 aspectRatio = xDist / yDist;
 709 break;
 710 }
 711 case ClosestCorner: {
 712 FloatPoint corner;
 713 float distance = distanceToClosestCorner(secondPoint, size, corner);
 714 if (shape == Circle)
 715 secondRadius = distance;
 716 else {
 717 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
 718 // that it would if closest-side or farthest-side were specified, as appropriate.
 719 float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
 720 float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
 721
 722 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
 723 aspectRatio = xDist / yDist;
 724 }
 725 break;
 726 }
462727
463  // We have to resolve colors.
464  for (unsigned i = 0; i < m_stops.size(); i++) {
465  Color color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(m_stops[i].m_color.get());
466  gradient->addColorStop(m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER), color);
 728 case FarthestCorner: {
 729 FloatPoint corner;
 730 float distance = distanceToFarthestCorner(secondPoint, size, corner);
 731 if (shape == Circle)
 732 secondRadius = distance;
 733 else {
 734 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
 735 // that it would if closest-side or farthest-side were specified, as appropriate.
 736 float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
 737 float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
 738
 739 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
 740 aspectRatio = xDist / yDist;
 741 }
 742 break;
 743 }
 744 }
467745 }
468746
469  // The back end already sorted the stops.
470  gradient->setStopsSorted(true);
 747 RefPtr<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius, aspectRatio);
 748
 749 // Now add the stops.
 750 addStops(gradient.get(), renderer, rootStyle);
471751
472752 return gradient.release();
473753}

WebCore/css/CSSGradientValue.h

@@public:
5757 Vector<CSSGradientColorStop>& stops() { return m_stops; }
5858
5959 void sortStopsIfNeeded();
 60
 61 virtual bool isLinearGradient() const { return false; }
 62 virtual bool isRadialGradient() const { return false; }
6063
6164 bool deprecatedType() const { return m_deprecatedType; } // came from -webkit-gradient
6265

@@protected:
7578 // Resolve points/radii to front end values.
7679 FloatPoint computeEndPoint(CSSPrimitiveValue*, CSSPrimitiveValue*, RenderStyle*, RenderStyle* rootStyle, const IntSize&);
7780
78  // Points. Some of these may be nil for linear gradients.
 81 // Points. Some of these may be null for linear gradients.
7982 RefPtr<CSSPrimitiveValue> m_firstX;
8083 RefPtr<CSSPrimitiveValue> m_firstY;
8184

@@private:
106109 {
107110 }
108111
 112 virtual bool isLinearGradient() const { return true; }
 113
109114 // Create the gradient for a given size.
110115 virtual PassRefPtr<Gradient> createGradient(RenderObject*, const IntSize&);
111116

@@public:
124129 void setFirstRadius(PassRefPtr<CSSPrimitiveValue> val) { m_firstRadius = val; }
125130 void setSecondRadius(PassRefPtr<CSSPrimitiveValue> val) { m_secondRadius = val; }
126131
 132 void setShape(PassRefPtr<CSSPrimitiveValue> val) { m_shape = val; }
 133 void setSizingBehavior(PassRefPtr<CSSPrimitiveValue> val) { m_sizingBehavior = val; }
 134
 135 void setEndHorizontalSize(PassRefPtr<CSSPrimitiveValue> val) { m_endHorizontalSize = val; }
 136 void setEndVerticalSize(PassRefPtr<CSSPrimitiveValue> val) { m_endVerticalSize = val; }
 137
127138private:
128139 CSSRadialGradientValue(bool deprecatedType = false)
129140 : CSSGradientValue(deprecatedType)
130141 {
131142 }
132143
 144 virtual bool isRadialGradient() const { return true; }
 145
133146 // Create the gradient for a given size.
134147 virtual PassRefPtr<Gradient> createGradient(RenderObject*, const IntSize&);
135148
136149 // Resolve points/radii to front end values.
137150 float resolveRadius(CSSPrimitiveValue*, RenderStyle*, RenderStyle* rootStyle);
138151
 152 // These may be null for non-deprecated gradients.
139153 RefPtr<CSSPrimitiveValue> m_firstRadius;
140154 RefPtr<CSSPrimitiveValue> m_secondRadius;
 155
 156 // The below are only used for non-deprecated gradients.
 157 RefPtr<CSSPrimitiveValue> m_shape;
 158 RefPtr<CSSPrimitiveValue> m_sizingBehavior;
 159
 160 RefPtr<CSSPrimitiveValue> m_endHorizontalSize;
 161 RefPtr<CSSPrimitiveValue> m_endVerticalSize;
141162};
142163
143164} // namespace WebCore

WebCore/css/CSSParser.cpp

@@bool CSSParser::parseFillImage(RefPtr<CSSValue>& value)
24552455 return false;
24562456}
24572457
2458 PassRefPtr<CSSValue> CSSParser::parseFillPositionXY(bool& xFound, bool& yFound)
 2458PassRefPtr<CSSValue> CSSParser::parseFillPositionXY(CSSParserValueList* valueList, bool& xFound, bool& yFound)
24592459{
2460  int id = m_valueList->current()->id;
 2460 int id = valueList->current()->id;
24612461 if (id == CSSValueLeft || id == CSSValueTop || id == CSSValueRight || id == CSSValueBottom || id == CSSValueCenter) {
24622462 int percent = 0;
24632463 if (id == CSSValueLeft || id == CSSValueRight) {

@@PassRefPtr<CSSValue> CSSParser::parseFillPositionXY(bool& xFound, bool& yFound)
24792479 percent = 50;
24802480 return CSSPrimitiveValue::create(percent, CSSPrimitiveValue::CSS_PERCENTAGE);
24812481 }
2482  if (validUnit(m_valueList->current(), FPercent | FLength, m_strict))
2483  return CSSPrimitiveValue::create(m_valueList->current()->fValue,
2484  (CSSPrimitiveValue::UnitTypes)m_valueList->current()->unit);
 2482 if (validUnit(valueList->current(), FPercent | FLength, m_strict))
 2483 return CSSPrimitiveValue::create(valueList->current()->fValue,
 2484 (CSSPrimitiveValue::UnitTypes)valueList->current()->unit);
24852485
24862486 return 0;
24872487}
24882488
2489 void CSSParser::parseFillPosition(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2)
 2489void CSSParser::parseFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2)
24902490{
2491  CSSParserValue* value = m_valueList->current();
 2491 CSSParserValue* value = valueList->current();
24922492
24932493 // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length.
24942494 bool value1IsX = false, value1IsY = false;
2495  value1 = parseFillPositionXY(value1IsX, value1IsY);
 2495 value1 = parseFillPositionXY(valueList, value1IsX, value1IsY);
24962496 if (!value1)
24972497 return;
24982498
24992499 // It only takes one value for background-position to be correctly parsed if it was specified in a shorthand (since we
25002500 // can assume that any other values belong to the rest of the shorthand). If we're not parsing a shorthand, though, the
25012501 // value was explicitly specified for our property.
2502  value = m_valueList->next();
 2502 value = valueList->next();
25032503
25042504 // First check for the comma. If so, we are finished parsing this value or value pair.
25052505 if (value && value->unit == CSSParserValue::Operator && value->iValue == ',')

@@void CSSParser::parseFillPosition(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& va
25072507
25082508 bool value2IsX = false, value2IsY = false;
25092509 if (value) {
2510  value2 = parseFillPositionXY(value2IsX, value2IsY);
 2510 value2 = parseFillPositionXY(valueList, value2IsX, value2IsY);
25112511 if (value2)
2512  m_valueList->next();
 2512 valueList->next();
25132513 else {
25142514 if (!inShorthand()) {
25152515 value1.clear();

@@bool CSSParser::parseFillProperty(int propId, int& propId1, int& propId2,
26982698 break;
26992699 case CSSPropertyBackgroundPosition:
27002700 case CSSPropertyWebkitMaskPosition:
2701  parseFillPosition(currValue, currValue2);
 2701 parseFillPosition(m_valueList, currValue, currValue2);
27022702 // parseFillPosition advances the m_valueList pointer
27032703 break;
27042704 case CSSPropertyBackgroundPositionX:
27052705 case CSSPropertyWebkitMaskPositionX: {
27062706 bool xFound = false, yFound = true;
2707  currValue = parseFillPositionXY(xFound, yFound);
 2707 currValue = parseFillPositionXY(m_valueList, xFound, yFound);
27082708 if (currValue)
27092709 m_valueList->next();
27102710 break;

@@bool CSSParser::parseFillProperty(int propId, int& propId1, int& propId2,
27122712 case CSSPropertyBackgroundPositionY:
27132713 case CSSPropertyWebkitMaskPositionY: {
27142714 bool xFound = true, yFound = false;
2715  currValue = parseFillPositionXY(xFound, yFound);
 2715 currValue = parseFillPositionXY(m_valueList, xFound, yFound);
27162716 if (currValue)
27172717 m_valueList->next();
27182718 break;

@@PassRefPtr<CSSValue> CSSParser::parseAnimationProperty()
28632863
28642864bool CSSParser::parseTransformOriginShorthand(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3)
28652865{
2866  parseFillPosition(value1, value2);
 2866 parseFillPosition(m_valueList, value1, value2);
28672867
28682868 // now get z
28692869 if (m_valueList->current()) {

@@bool CSSParser::parseLinearGradient(RefPtr<CSSValue>& gradient)
49884988 return true;
49894989}
49904990
4991 bool CSSParser::parseRadialGradient(RefPtr<CSSValue>&)
 4991bool CSSParser::parseRadialGradient(RefPtr<CSSValue>& gradient)
49924992{
4993  // FIXME: implement.
4994  return false;
 4993 RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create();
 4994
 4995 // Walk the arguments.
 4996 CSSParserValueList* args = m_valueList->current()->function->args.get();
 4997 if (!args || !args->size())
 4998 return false;
 4999
 5000 CSSParserValue* a = args->current();
 5001 if (!a)
 5002 return false;
 5003
 5004 bool expectComma = false;
 5005
 5006 // Optional background-position
 5007 RefPtr<CSSValue> centerX;
 5008 RefPtr<CSSValue> centerY;
 5009 // parseFillPosition advances the args next pointer.
 5010 parseFillPosition(args, centerX, centerY);
 5011 a = args->current();
 5012
 5013 if (centerX || centerY) {
 5014 // Comma
 5015 if (a->unit != CSSParserValue::Operator || a->iValue != ',')
 5016 return false;
 5017
 5018 a = args->next();
 5019 if (!a)
 5020 return false;
 5021 }
 5022
 5023 ASSERT(!centerX || centerX->isPrimitiveValue());
 5024 ASSERT(!centerY || centerY->isPrimitiveValue());
 5025
 5026 result->setFirstX(static_cast<CSSPrimitiveValue*>(centerX.get()));
 5027 result->setSecondX(static_cast<CSSPrimitiveValue*>(centerX.get()));
 5028 // CSS3 radial gradients always share the same start and end point.
 5029 result->setFirstY(static_cast<CSSPrimitiveValue*>(centerY.get()));
 5030 result->setSecondY(static_cast<CSSPrimitiveValue*>(centerY.get()));
 5031
 5032 RefPtr<CSSPrimitiveValue> shapeValue;
 5033 RefPtr<CSSPrimitiveValue> sizeValue;
 5034
 5035 // Optional shape and/or size in any order.
 5036 for (int i = 0; i < 2; ++i) {
 5037 if (a->unit != CSSPrimitiveValue::CSS_IDENT)
 5038 break;
 5039
 5040 bool foundValue = false;
 5041 switch (a->id) {
 5042 case CSSValueCircle:
 5043 case CSSValueEllipse:
 5044 shapeValue = CSSPrimitiveValue::createIdentifier(a->id);
 5045 foundValue = true;
 5046 break;
 5047 case CSSValueClosestSide:
 5048 case CSSValueClosestCorner:
 5049 case CSSValueFarthestSide:
 5050 case CSSValueFarthestCorner:
 5051 case CSSValueContain:
 5052 case CSSValueCover:
 5053 sizeValue = CSSPrimitiveValue::createIdentifier(a->id);
 5054 foundValue = true;
 5055 break;
 5056 }
 5057
 5058 if (foundValue) {
 5059 a = args->next();
 5060 if (!a)
 5061 return false;
 5062
 5063 expectComma = true;
 5064 }
 5065 }
 5066
 5067 result->setShape(shapeValue);
 5068 result->setSizingBehavior(sizeValue);
 5069
 5070 // Or, two lengths or percentages
 5071 RefPtr<CSSPrimitiveValue> horizontalSize;
 5072 RefPtr<CSSPrimitiveValue> verticalSize;
 5073
 5074 if (!shapeValue && !sizeValue) {
 5075 if (validUnit(a, FLength | FPercent, m_strict)) {
 5076 horizontalSize = CSSPrimitiveValue::create(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit);
 5077 a = args->next();
 5078 if (!a)
 5079 return false;
 5080
 5081 expectComma = true;
 5082 }
 5083
 5084 if (validUnit(a, FLength | FPercent, m_strict)) {
 5085 verticalSize = CSSPrimitiveValue::create(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit);
 5086
 5087 a = args->next();
 5088 if (!a)
 5089 return false;
 5090 expectComma = true;
 5091 }
 5092 }
 5093
 5094 // Must have neither or both.
 5095 if (!horizontalSize != !verticalSize)
 5096 return false;
 5097
 5098 result->setEndHorizontalSize(horizontalSize);
 5099 result->setEndVerticalSize(verticalSize);
 5100
 5101 // Now look for 0 or more color stops.
 5102 while (a) {
 5103 // Look for the comma before the next stop.
 5104 if (expectComma) {
 5105 if (a->unit != CSSParserValue::Operator || a->iValue != ',')
 5106 return false;
 5107
 5108 a = args->next();
 5109 if (!a)
 5110 return false;
 5111 }
 5112
 5113 // <color-stop> = <color> [ <percentage> | <length> ]?
 5114 CSSGradientColorStop stop;
 5115 stop.m_color = parseGradientColorOrKeyword(this, a);
 5116 if (!stop.m_color)
 5117 return false;
 5118
 5119 a = args->next();
 5120 if (a) {
 5121 if (validUnit(a, FLength | FPercent, m_strict)) {
 5122 stop.m_position = CSSPrimitiveValue::create(a->fValue, (CSSPrimitiveValue::UnitTypes)a->unit);
 5123 a = args->next();
 5124 }
 5125 }
 5126
 5127 result->addStop(stop);
 5128 expectComma = true;
 5129 }
 5130
 5131 Vector<CSSGradientColorStop>& stops = result->stops();
 5132 if (stops.isEmpty())
 5133 return false;
 5134
 5135 gradient = result.release();
 5136 return true;
49955137}
49965138
49975139bool CSSParser::isGeneratedImageValue(CSSParserValue* val) const

@@bool CSSParser::parseTransformOrigin(int propId, int& propId1, int& propId2, int
52255367 break;
52265368 case CSSPropertyWebkitTransformOriginX: {
52275369 bool xFound = false, yFound = true;
5228  value = parseFillPositionXY(xFound, yFound);
 5370 value = parseFillPositionXY(m_valueList, xFound, yFound);
52295371 if (value)
52305372 m_valueList->next();
52315373 break;
52325374 }
52335375 case CSSPropertyWebkitTransformOriginY: {
52345376 bool xFound = true, yFound = false;
5235  value = parseFillPositionXY(xFound, yFound);
 5377 value = parseFillPositionXY(m_valueList, xFound, yFound);
52365378 if (value)
52375379 m_valueList->next();
52385380 break;

@@bool CSSParser::parsePerspectiveOrigin(int propId, int& propId1, int& propId2, R
52605402
52615403 switch (propId) {
52625404 case CSSPropertyWebkitPerspectiveOrigin:
5263  parseFillPosition(value, value2);
 5405 parseFillPosition(m_valueList, value, value2);
52645406 break;
52655407 case CSSPropertyWebkitPerspectiveOriginX: {
52665408 bool xFound = false, yFound = true;
5267  value = parseFillPositionXY(xFound, yFound);
 5409 value = parseFillPositionXY(m_valueList, xFound, yFound);
52685410 if (value)
52695411 m_valueList->next();
52705412 break;
52715413 }
52725414 case CSSPropertyWebkitPerspectiveOriginY: {
52735415 bool xFound = true, yFound = false;
5274  value = parseFillPositionXY(xFound, yFound);
 5416 value = parseFillPositionXY(m_valueList, xFound, yFound);
52755417 if (value)
52765418 m_valueList->next();
52775419 break;

WebCore/css/CSSParser.h

@@namespace WebCore {
8484 PassRefPtr<CSSValue> parseBackgroundColor();
8585
8686 bool parseFillImage(RefPtr<CSSValue>&);
87  PassRefPtr<CSSValue> parseFillPositionXY(bool& xFound, bool& yFound);
88  void parseFillPosition(RefPtr<CSSValue>&, RefPtr<CSSValue>&);
 87 PassRefPtr<CSSValue> parseFillPositionXY(CSSParserValueList*, bool& xFound, bool& yFound);
 88 void parseFillPosition(CSSParserValueList*, RefPtr<CSSValue>&, RefPtr<CSSValue>&);
8989 void parseFillRepeat(RefPtr<CSSValue>&, RefPtr<CSSValue>&);
9090 PassRefPtr<CSSValue> parseFillSize(int propId, bool &allowComma);
9191

WebCore/css/CSSValueKeywords.in

@@dot
780780double-circle
781781triangle
782782sesame
 783
 784# -webkit-radial-gradient
 785# circle
 786ellipse
 787closest-side
 788closest-corner
 789farthest-side
 790farthest-corner
 791# contain
 792# cover

WebCore/platform/graphics/Gradient.cpp

@@Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1)
3939 , m_p1(p1)
4040 , m_r0(0)
4141 , m_r1(0)
 42 , m_aspectRatio(1)
4243 , m_stopsSorted(false)
4344 , m_lastStop(0)
4445 , m_spreadMethod(SpreadMethodPad)

@@Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1)
4647 platformInit();
4748}
4849
49 Gradient::Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1)
 50Gradient::Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio)
5051 : m_radial(true)
5152 , m_p0(p0)
5253 , m_p1(p1)
5354 , m_r0(r0)
5455 , m_r1(r1)
 56 , m_aspectRatio(aspectRatio)
5557 , m_stopsSorted(false)
5658 , m_lastStop(0)
5759 , m_spreadMethod(SpreadMethodPad)

WebCore/platform/graphics/Gradient.h

@@namespace WebCore {
7676 {
7777 return adoptRef(new Gradient(p0, p1));
7878 }
79  static PassRefPtr<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1)
 79 static PassRefPtr<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio = 1)
8080 {
81  return adoptRef(new Gradient(p0, r0, p1, r1));
 81 return adoptRef(new Gradient(p0, r0, p1, r1, aspectRatio));
8282 }
8383 virtual ~Gradient();
8484

@@namespace WebCore {
9898 void setP0(const FloatPoint& p) { m_p0 = p; }
9999 void setP1(const FloatPoint& p) { m_p1 = p; }
100100
 101 float startRadius() const { return m_r0; }
 102 float endRadius() const { return m_r1; }
 103
 104 void setStartRadius(float r) { m_r0 = r; }
 105 void setEndRadius(float r) { m_r1 = r; }
 106
 107 float aspectRatio() const { return m_aspectRatio; }
 108
101109#if OS(WINCE) && !PLATFORM(QT)
102  float r0() const { return m_r0; }
103  float r1() const { return m_r1; }
104110 const Vector<ColorStop, 2>& getStops() const;
105111#else
106112 PlatformGradient platformGradient();

@@namespace WebCore {
134140#endif
135141 private:
136142 Gradient(const FloatPoint& p0, const FloatPoint& p1);
137  Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1);
 143 Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio);
138144
139145 void platformInit() { m_gradient = 0; }
140146 void platformDestroy();

@@namespace WebCore {
147153 FloatPoint m_p1;
148154 float m_r0;
149155 float m_r1;
 156 float m_aspectRatio; // For elliptical gradient, width / height.
150157 mutable Vector<ColorStop, 2> m_stops;
151158 mutable bool m_stopsSorted;
152159 mutable int m_lastStop;

WebCore/platform/graphics/cg/GradientCG.cpp

@@void Gradient::fill(GraphicsContext* context, const FloatRect& rect)
112112
113113void Gradient::paint(GraphicsContext* context)
114114{
 115 CGContextRef ctx = context->platformContext();
115116#if USE_CG_SHADING
116  CGContextDrawShading(context->platformContext(), platformGradient());
 117 CGContextDrawShading(ctx, platformGradient());
117118#else
118119 CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
119  if (m_radial)
120  CGContextDrawRadialGradient(context->platformContext(), platformGradient(), m_p0, m_r0, m_p1, m_r1, extendOptions);
121  else
122  CGContextDrawLinearGradient(context->platformContext(), platformGradient(), m_p0, m_p1, extendOptions);
 120 if (m_radial) {
 121 bool needScaling = aspectRatio() != 1;
 122 if (needScaling) {
 123 CGContextSaveGState(ctx);
 124 // Scale from the center of the gradient. We only ever scale non-deprecated gradients,
 125 // for which m_p0 == m_p1.
 126 ASSERT(m_p0 == m_p1);
 127 CGContextTranslateCTM(ctx, m_p0.x(), m_p0.y());
 128 CGContextScaleCTM(ctx, 1, 1 / aspectRatio());
 129 CGContextTranslateCTM(ctx, -m_p0.x(), -m_p0.y());
 130 }
 131
 132 CGContextDrawRadialGradient(ctx, platformGradient(), m_p0, m_r0, m_p1, m_r1, extendOptions);
 133
 134 if (needScaling)
 135 CGContextRestoreGState(ctx);
 136 } else
 137 CGContextDrawLinearGradient(ctx, platformGradient(), m_p0, m_p1, extendOptions);
123138#endif
124139}
125140

WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp

@@void GraphicsContext::fillRect(const FloatRect& r, const Gradient* gradient)
14251425 if (g_radialGradientFiller) {
14261426 // FIXME: don't support 2D scaling at this time
14271427 double scale = (m_data->m_transform.a() + m_data->m_transform.d()) * 0.5;
1428  float r0 = gradient->r0() * scale;
1429  float r1 = gradient->r1() * scale;
 1428 float r0 = gradient->startRadius() * scale;
 1429 float r1 = gradient->endRadius() * scale;
14301430 g_radialGradientFiller(dc, rect, p0, p1, r0, r1, gradient->getStops());
14311431 return;
14321432 }