Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / CSSGradientValue.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "core/css/CSSGradientValue.h"
28
29 #include "core/CSSValueKeywords.h"
30 #include "core/css/CSSCalculationValue.h"
31 #include "core/css/CSSToLengthConversionData.h"
32 #include "core/css/Pair.h"
33 #include "core/dom/NodeRenderStyle.h"
34 #include "core/dom/TextLinkColors.h"
35 #include "core/rendering/RenderObject.h"
36 #include "platform/geometry/IntSize.h"
37 #include "platform/graphics/Gradient.h"
38 #include "platform/graphics/GradientGeneratedImage.h"
39 #include "platform/graphics/Image.h"
40 #include "platform/graphics/skia/SkiaUtils.h"
41 #include "wtf/text/StringBuilder.h"
42 #include "wtf/text/WTFString.h"
43
44 namespace blink {
45
46 void CSSGradientColorStop::trace(Visitor* visitor)
47 {
48     visitor->trace(m_position);
49     visitor->trace(m_color);
50 }
51
52 PassRefPtr<Image> CSSGradientValue::image(RenderObject* renderer, const IntSize& size)
53 {
54     if (size.isEmpty())
55         return nullptr;
56
57     bool cacheable = isCacheable();
58     if (cacheable) {
59         if (!clients().contains(renderer))
60             return nullptr;
61
62         // Need to look up our size.  Create a string of width*height to use as a hash key.
63         Image* result = getImage(renderer, size);
64         if (result)
65             return result;
66     }
67
68     // We need to create an image.
69     RefPtr<Gradient> gradient;
70
71     RenderStyle* rootStyle = renderer->document().documentElement()->renderStyle();
72     CSSToLengthConversionData conversionData(renderer->style(), rootStyle, renderer->view());
73     if (isLinearGradientValue())
74         gradient = toCSSLinearGradientValue(this)->createGradient(conversionData, size);
75     else
76         gradient = toCSSRadialGradientValue(this)->createGradient(conversionData, size);
77
78     RefPtr<Image> newImage = GradientGeneratedImage::create(gradient, size);
79     if (cacheable)
80         putImage(size, newImage);
81
82     return newImage.release();
83 }
84
85 // Should only ever be called for deprecated gradients.
86 static inline bool compareStops(const CSSGradientColorStop& a, const CSSGradientColorStop& b)
87 {
88     double aVal = a.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER);
89     double bVal = b.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER);
90
91     return aVal < bVal;
92 }
93
94 void CSSGradientValue::sortStopsIfNeeded()
95 {
96     ASSERT(m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDeprecatedRadialGradient);
97     if (!m_stopsSorted) {
98         if (m_stops.size())
99             std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
100         m_stopsSorted = true;
101     }
102 }
103
104 class GradientStop {
105     ALLOW_ONLY_INLINE_ALLOCATION();
106 public:
107     Color color;
108     float offset;
109     bool specified;
110
111     GradientStop()
112         : offset(0)
113         , specified(false)
114     { }
115 };
116
117 PassRefPtrWillBeRawPtr<CSSGradientValue> CSSGradientValue::gradientWithStylesResolved(const TextLinkColors& textLinkColors, Color currentColor)
118 {
119     bool derived = false;
120     for (auto& stop : m_stops) {
121         if (!stop.isHint() && stop.m_color->colorIsDerivedFromElement()) {
122             stop.m_colorIsDerivedFromElement = true;
123             derived = true;
124             break;
125         }
126     }
127
128     RefPtrWillBeRawPtr<CSSGradientValue> result = nullptr;
129     if (!derived)
130         result = this;
131     else if (isLinearGradientValue())
132         result = toCSSLinearGradientValue(this)->clone();
133     else if (isRadialGradientValue())
134         result = toCSSRadialGradientValue(this)->clone();
135     else {
136         ASSERT_NOT_REACHED();
137         return nullptr;
138     }
139
140     for (auto& stop : result->m_stops) {
141         if (!stop.isHint())
142             stop.m_resolvedColor = textLinkColors.colorFromPrimitiveValue(stop.m_color.get(), currentColor);
143     }
144
145     return result.release();
146 }
147
148 static void replaceColorHintsWithColorStops(WillBeHeapVector<GradientStop>& stops, const WillBeHeapVector<CSSGradientColorStop, 2>& cssGradientStops)
149 {
150     // This algorithm will replace each color interpolation hint with 9 regular
151     // color stops. The color values for the new color stops will be calculated
152     // using the color weighting formula defined in the spec. The new color
153     // stops will be positioned in such a way that all the pixels between the two
154     // user defined color stops have color values close to the interpolation curve.
155     // If the hint is closer to the left color stop, add 2 stops to the left and
156     // 6 to the right, else add 6 stops to the left and 2 to the right.
157     // The color stops on the side with more space start midway because
158     // the curve approximates a line in that region.
159     // Using this aproximation, it is possible to discern the color steps when
160     // the gradient is large. If this becomes an issue, we can consider improving
161     // the algorithm, or adding support for color interpolation hints to skia shaders.
162
163     int indexOffset = 0;
164
165     // The first and the last color stops cannot be color hints.
166     for (size_t i = 1; i < cssGradientStops.size() - 1; ++i) {
167         if (!cssGradientStops[i].isHint())
168             continue;
169
170         // The current index of the stops vector.
171         size_t x = i + indexOffset;
172         ASSERT(x >= 1);
173
174         // offsetLeft          offset                            offsetRight
175         //   |-------------------|---------------------------------|
176         //          leftDist                 rightDist
177
178         float offsetLeft = stops[x - 1].offset;
179         float offsetRight = stops[x + 1].offset;
180         float offset = stops[x].offset;
181         float leftDist = offset - offsetLeft;
182         float rightDist = offsetRight - offset;
183         float totalDist = offsetRight - offsetLeft;
184
185         Color leftColor = stops[x - 1].color;
186         Color rightColor = stops[x + 1].color;
187
188         ASSERT(offsetLeft <= offset && offset <= offsetRight);
189
190         if (WebCoreFloatNearlyEqual(leftDist, rightDist)) {
191             stops.remove(x);
192             --indexOffset;
193             continue;
194         }
195
196         if (WebCoreFloatNearlyEqual(leftDist, .0f)) {
197             stops[x].color = rightColor;
198             continue;
199         }
200
201         if (WebCoreFloatNearlyEqual(rightDist, .0f)) {
202             stops[x].color = leftColor;
203             continue;
204         }
205
206         GradientStop newStops[9];
207         // Position the new color stops.
208         if (leftDist > rightDist) {
209             for (size_t y = 0; y < 7; ++y)
210                 newStops[y].offset = offsetLeft + leftDist * (7 + y) / 13;
211             newStops[7].offset = offset + rightDist / 3;
212             newStops[8].offset = offset + rightDist * 2 / 3;
213         } else {
214             newStops[0].offset = offsetLeft + leftDist / 3;
215             newStops[1].offset = offsetLeft + leftDist * 2 / 3;
216             for (size_t y = 0; y < 7; ++y)
217                 newStops[y + 2].offset = offset + rightDist * y / 13;
218         }
219
220         // calculate colors for the new color hints.
221         // The color weighting for the new color stops will be pointRelativeOffset^(ln(0.5)/ln(hintRelativeOffset)).
222         float hintRelativeOffset = leftDist / totalDist;
223         for (size_t y = 0; y < 9; ++y) {
224             float pointRelativeOffset = (newStops[y].offset - offsetLeft) / totalDist;
225             float weighting = powf(pointRelativeOffset, logf(.5f) / logf(hintRelativeOffset));
226             newStops[y].color = blend(leftColor, rightColor, weighting);
227         }
228
229         // Replace the color hint with the new color stops.
230         stops.remove(x);
231         stops.insert(x, newStops, 9);
232         indexOffset += 8;
233     }
234 }
235
236 void CSSGradientValue::addStops(Gradient* gradient, const CSSToLengthConversionData& conversionData, float maxLengthForRepeat)
237 {
238     if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDeprecatedRadialGradient) {
239         sortStopsIfNeeded();
240
241         for (unsigned i = 0; i < m_stops.size(); i++) {
242             const CSSGradientColorStop& stop = m_stops[i];
243
244             float offset;
245             if (stop.m_position->isPercentage())
246                 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
247             else
248                 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_NUMBER);
249
250             gradient->addColorStop(offset, stop.m_resolvedColor);
251         }
252
253         return;
254     }
255
256     size_t numStops = m_stops.size();
257
258     WillBeHeapVector<GradientStop> stops(numStops);
259
260     float gradientLength = 0;
261     bool computedGradientLength = false;
262
263     bool hasHints = false;
264
265     FloatPoint gradientStart = gradient->p0();
266     FloatPoint gradientEnd;
267     if (isLinearGradientValue())
268         gradientEnd = gradient->p1();
269     else if (isRadialGradientValue())
270         gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0);
271
272     for (size_t i = 0; i < numStops; ++i) {
273         const CSSGradientColorStop& stop = m_stops[i];
274
275         if (stop.isHint())
276             hasHints = true;
277         else
278             stops[i].color = stop.m_resolvedColor;
279
280         if (stop.m_position) {
281             if (stop.m_position->isPercentage())
282                 stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
283             else if (stop.m_position->isLength() || stop.m_position->isCalculatedPercentageWithLength()) {
284                 if (!computedGradientLength) {
285                     FloatSize gradientSize(gradientStart - gradientEnd);
286                     gradientLength = gradientSize.diagonalLength();
287                 }
288                 float length;
289                 if (stop.m_position->isLength())
290                     length = stop.m_position->computeLength<float>(conversionData);
291                 else
292                     length = stop.m_position->cssCalcValue()->toCalcValue(conversionData)->evaluate(gradientLength);
293                 stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0;
294             } else {
295                 ASSERT_NOT_REACHED();
296                 stops[i].offset = 0;
297             }
298             stops[i].specified = true;
299         } else {
300             // If the first color-stop does not have a position, its position defaults to 0%.
301             // If the last color-stop does not have a position, its position defaults to 100%.
302             if (!i) {
303                 stops[i].offset = 0;
304                 stops[i].specified = true;
305             } else if (numStops > 1 && i == numStops - 1) {
306                 stops[i].offset = 1;
307                 stops[i].specified = true;
308             }
309         }
310
311         // If a color-stop has a position that is less than the specified position of any
312         // color-stop before it in the list, its position is changed to be equal to the
313         // largest specified position of any color-stop before it.
314         if (stops[i].specified && i > 0) {
315             size_t prevSpecifiedIndex;
316             for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; --prevSpecifiedIndex) {
317                 if (stops[prevSpecifiedIndex].specified)
318                     break;
319             }
320
321             if (stops[i].offset < stops[prevSpecifiedIndex].offset)
322                 stops[i].offset = stops[prevSpecifiedIndex].offset;
323         }
324     }
325
326     ASSERT(stops[0].specified && stops[numStops - 1].specified);
327
328     // If any color-stop still does not have a position, then, for each run of adjacent
329     // color-stops without positions, set their positions so that they are evenly spaced
330     // between the preceding and following color-stops with positions.
331     if (numStops > 2) {
332         size_t unspecifiedRunStart = 0;
333         bool inUnspecifiedRun = false;
334
335         for (size_t i = 0; i < numStops; ++i) {
336             if (!stops[i].specified && !inUnspecifiedRun) {
337                 unspecifiedRunStart = i;
338                 inUnspecifiedRun = true;
339             } else if (stops[i].specified && inUnspecifiedRun) {
340                 size_t unspecifiedRunEnd = i;
341
342                 if (unspecifiedRunStart < unspecifiedRunEnd) {
343                     float lastSpecifiedOffset = stops[unspecifiedRunStart - 1].offset;
344                     float nextSpecifiedOffset = stops[unspecifiedRunEnd].offset;
345                     float delta = (nextSpecifiedOffset - lastSpecifiedOffset) / (unspecifiedRunEnd - unspecifiedRunStart + 1);
346
347                     for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j)
348                         stops[j].offset = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
349                 }
350
351                 inUnspecifiedRun = false;
352             }
353         }
354     }
355
356     ASSERT(stops.size() == m_stops.size());
357     if (hasHints) {
358         replaceColorHintsWithColorStops(stops, m_stops);
359         numStops = stops.size();
360     }
361
362     // If the gradient is repeating, repeat the color stops.
363     // We can't just push this logic down into the platform-specific Gradient code,
364     // because we have to know the extent of the gradient, and possible move the end points.
365     if (m_repeating && numStops > 1) {
366         // If the difference in the positions of the first and last color-stops is 0,
367         // the gradient defines a solid-color image with the color of the last color-stop in the rule.
368         float gradientRange = stops[numStops - 1].offset - stops[0].offset;
369         if (!gradientRange) {
370             stops.first().offset = 0;
371             stops.first().color = stops.last().color;
372             stops.shrink(1);
373         } else {
374             float maxExtent = 1;
375
376             // Radial gradients may need to extend further than the endpoints, because they have
377             // to repeat out to the corners of the box.
378             if (isRadialGradientValue()) {
379                 if (!computedGradientLength) {
380                     FloatSize gradientSize(gradientStart - gradientEnd);
381                     gradientLength = gradientSize.diagonalLength();
382                 }
383
384                 if (maxLengthForRepeat > gradientLength)
385                     maxExtent = gradientLength > 0 ? maxLengthForRepeat / gradientLength : 0;
386             }
387
388             size_t originalNumStops = numStops;
389             size_t originalFirstStopIndex = 0;
390
391             // Work backwards from the first, adding stops until we get one before 0.
392             float firstOffset = stops[0].offset;
393             if (firstOffset > 0) {
394                 float currOffset = firstOffset;
395                 size_t srcStopOrdinal = originalNumStops - 1;
396
397                 while (true) {
398                     GradientStop newStop = stops[originalFirstStopIndex + srcStopOrdinal];
399                     newStop.offset = currOffset;
400                     stops.prepend(newStop);
401                     ++originalFirstStopIndex;
402                     if (currOffset < 0)
403                         break;
404
405                     if (srcStopOrdinal)
406                         currOffset -= stops[originalFirstStopIndex + srcStopOrdinal].offset - stops[originalFirstStopIndex + srcStopOrdinal - 1].offset;
407                     srcStopOrdinal = (srcStopOrdinal + originalNumStops - 1) % originalNumStops;
408                 }
409             }
410
411             // Work forwards from the end, adding stops until we get one after 1.
412             float lastOffset = stops[stops.size() - 1].offset;
413             if (lastOffset < maxExtent) {
414                 float currOffset = lastOffset;
415                 size_t srcStopOrdinal = 0;
416
417                 while (true) {
418                     size_t srcStopIndex = originalFirstStopIndex + srcStopOrdinal;
419                     GradientStop newStop = stops[srcStopIndex];
420                     newStop.offset = currOffset;
421                     stops.append(newStop);
422                     if (currOffset > maxExtent)
423                         break;
424                     if (srcStopOrdinal < originalNumStops - 1)
425                         currOffset += stops[srcStopIndex + 1].offset - stops[srcStopIndex].offset;
426                     srcStopOrdinal = (srcStopOrdinal + 1) % originalNumStops;
427                 }
428             }
429         }
430     }
431
432     numStops = stops.size();
433
434     // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and adjusting the stops.
435     if (numStops > 1 && (stops[0].offset < 0 || stops[numStops - 1].offset > 1)) {
436         if (isLinearGradientValue()) {
437             float firstOffset = stops[0].offset;
438             float lastOffset = stops[numStops - 1].offset;
439             float scale = lastOffset - firstOffset;
440
441             for (size_t i = 0; i < numStops; ++i)
442                 stops[i].offset = (stops[i].offset - firstOffset) / scale;
443
444             FloatPoint p0 = gradient->p0();
445             FloatPoint p1 = gradient->p1();
446             gradient->setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
447             gradient->setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
448         } else if (isRadialGradientValue()) {
449             // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
450             float firstOffset = 0;
451             float lastOffset = stops[numStops - 1].offset;
452             float scale = lastOffset - firstOffset;
453
454             // Reset points below 0 to the first visible color.
455             size_t firstZeroOrGreaterIndex = numStops;
456             for (size_t i = 0; i < numStops; ++i) {
457                 if (stops[i].offset >= 0) {
458                     firstZeroOrGreaterIndex = i;
459                     break;
460                 }
461             }
462
463             if (firstZeroOrGreaterIndex > 0) {
464                 if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
465                     float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
466                     float nextOffset = stops[firstZeroOrGreaterIndex].offset;
467
468                     float interStopProportion = -prevOffset / (nextOffset - prevOffset);
469                     // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
470                     Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
471
472                     // Clamp the positions to 0 and set the color.
473                     for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
474                         stops[i].offset = 0;
475                         stops[i].color = blendedColor;
476                     }
477                 } else {
478                     // All stops are below 0; just clamp them.
479                     for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i)
480                         stops[i].offset = 0;
481                 }
482             }
483
484             for (size_t i = 0; i < numStops; ++i)
485                 stops[i].offset /= scale;
486
487             gradient->setStartRadius(gradient->startRadius() * scale);
488             gradient->setEndRadius(gradient->endRadius() * scale);
489         }
490     }
491
492     for (unsigned i = 0; i < numStops; i++)
493         gradient->addColorStop(stops[i].offset, stops[i].color);
494 }
495
496 static float positionFromValue(CSSPrimitiveValue* value, const CSSToLengthConversionData& conversionData, const IntSize& size, bool isHorizontal)
497 {
498     int origin = 0;
499     int sign = 1;
500     int edgeDistance = isHorizontal ? size.width() : size.height();
501
502     // In this case the center of the gradient is given relative to an edge in the form of:
503     // [ top | bottom | right | left ] [ <percentage> | <length> ].
504     if (Pair* pair = value->getPairValue()) {
505         CSSValueID originID = pair->first()->getValueID();
506         value = pair->second();
507
508         if (originID == CSSValueRight || originID == CSSValueBottom) {
509             // For right/bottom, the offset is relative to the far edge.
510             origin = edgeDistance;
511             sign = -1;
512         }
513     }
514
515     if (value->isNumber())
516         return origin + sign * value->getFloatValue() * conversionData.zoom();
517
518     if (value->isPercentage())
519         return origin + sign * value->getFloatValue() / 100.f * edgeDistance;
520
521     if (value->isCalculatedPercentageWithLength())
522         return origin + sign * value->cssCalcValue()->toCalcValue(conversionData)->evaluate(edgeDistance);
523
524     switch (value->getValueID()) {
525     case CSSValueTop:
526         ASSERT(!isHorizontal);
527         return 0;
528     case CSSValueLeft:
529         ASSERT(isHorizontal);
530         return 0;
531     case CSSValueBottom:
532         ASSERT(!isHorizontal);
533         return size.height();
534     case CSSValueRight:
535         ASSERT(isHorizontal);
536         return size.width();
537     default:
538         break;
539     }
540
541     return origin + sign * value->computeLength<float>(conversionData);
542 }
543
544 FloatPoint CSSGradientValue::computeEndPoint(CSSPrimitiveValue* horizontal, CSSPrimitiveValue* vertical, const CSSToLengthConversionData& conversionData, const IntSize& size)
545 {
546     FloatPoint result;
547
548     if (horizontal)
549         result.setX(positionFromValue(horizontal, conversionData, size, true));
550
551     if (vertical)
552         result.setY(positionFromValue(vertical, conversionData, size, false));
553
554     return result;
555 }
556
557 bool CSSGradientValue::isCacheable() const
558 {
559     for (size_t i = 0; i < m_stops.size(); ++i) {
560         const CSSGradientColorStop& stop = m_stops[i];
561
562         if (stop.m_colorIsDerivedFromElement)
563             return false;
564
565         if (!stop.m_position)
566             continue;
567
568         if (stop.m_position->isFontRelativeLength())
569             return false;
570     }
571
572     return true;
573 }
574
575 bool CSSGradientValue::knownToBeOpaque(const RenderObject*) const
576 {
577     for (auto& stop : m_stops) {
578         if (!stop.isHint() && stop.m_resolvedColor.hasAlpha())
579             return false;
580     }
581     return true;
582 }
583
584 void CSSGradientValue::traceAfterDispatch(Visitor* visitor)
585 {
586     visitor->trace(m_firstX);
587     visitor->trace(m_firstY);
588     visitor->trace(m_secondX);
589     visitor->trace(m_secondY);
590     visitor->trace(m_stops);
591     CSSImageGeneratorValue::traceAfterDispatch(visitor);
592 }
593
594 String CSSLinearGradientValue::customCSSText() const
595 {
596     StringBuilder result;
597     if (m_gradientType == CSSDeprecatedLinearGradient) {
598         result.appendLiteral("-webkit-gradient(linear, ");
599         result.append(m_firstX->cssText());
600         result.append(' ');
601         result.append(m_firstY->cssText());
602         result.appendLiteral(", ");
603         result.append(m_secondX->cssText());
604         result.append(' ');
605         result.append(m_secondY->cssText());
606
607         for (unsigned i = 0; i < m_stops.size(); i++) {
608             const CSSGradientColorStop& stop = m_stops[i];
609             result.appendLiteral(", ");
610             if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0) {
611                 result.appendLiteral("from(");
612                 result.append(stop.m_color->cssText());
613                 result.append(')');
614             } else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1) {
615                 result.appendLiteral("to(");
616                 result.append(stop.m_color->cssText());
617                 result.append(')');
618             } else {
619                 result.appendLiteral("color-stop(");
620                 result.appendNumber(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER));
621                 result.appendLiteral(", ");
622                 result.append(stop.m_color->cssText());
623                 result.append(')');
624             }
625         }
626     } else if (m_gradientType == CSSPrefixedLinearGradient) {
627         if (m_repeating)
628             result.appendLiteral("-webkit-repeating-linear-gradient(");
629         else
630             result.appendLiteral("-webkit-linear-gradient(");
631
632         if (m_angle)
633             result.append(m_angle->cssText());
634         else {
635             if (m_firstX && m_firstY) {
636                 result.append(m_firstX->cssText());
637                 result.append(' ');
638                 result.append(m_firstY->cssText());
639             } else if (m_firstX || m_firstY) {
640                 if (m_firstX)
641                     result.append(m_firstX->cssText());
642
643                 if (m_firstY)
644                     result.append(m_firstY->cssText());
645             }
646         }
647
648         for (unsigned i = 0; i < m_stops.size(); i++) {
649             const CSSGradientColorStop& stop = m_stops[i];
650             result.appendLiteral(", ");
651             result.append(stop.m_color->cssText());
652             if (stop.m_position) {
653                 result.append(' ');
654                 result.append(stop.m_position->cssText());
655             }
656         }
657     } else {
658         if (m_repeating)
659             result.appendLiteral("repeating-linear-gradient(");
660         else
661             result.appendLiteral("linear-gradient(");
662
663         bool wroteSomething = false;
664
665         if (m_angle && m_angle->computeDegrees() != 180) {
666             result.append(m_angle->cssText());
667             wroteSomething = true;
668         } else if ((m_firstX || m_firstY) && !(!m_firstX && m_firstY && m_firstY->getValueID() == CSSValueBottom)) {
669             result.appendLiteral("to ");
670             if (m_firstX && m_firstY) {
671                 result.append(m_firstX->cssText());
672                 result.append(' ');
673                 result.append(m_firstY->cssText());
674             } else if (m_firstX)
675                 result.append(m_firstX->cssText());
676             else
677                 result.append(m_firstY->cssText());
678             wroteSomething = true;
679         }
680
681         if (wroteSomething)
682             result.appendLiteral(", ");
683
684         for (unsigned i = 0; i < m_stops.size(); i++) {
685             const CSSGradientColorStop& stop = m_stops[i];
686             if (i)
687                 result.appendLiteral(", ");
688             if (stop.m_color)
689                 result.append(stop.m_color->cssText());
690             if (stop.m_color && stop.m_position)
691                 result.append(' ');
692             if (stop.m_position)
693                 result.append(stop.m_position->cssText());
694         }
695
696     }
697
698     result.append(')');
699     return result.toString();
700 }
701
702 // Compute the endpoints so that a gradient of the given angle covers a box of the given size.
703 static void endPointsFromAngle(float angleDeg, const IntSize& size, FloatPoint& firstPoint, FloatPoint& secondPoint, CSSGradientType type)
704 {
705     // Prefixed gradients use "polar coordinate" angles, rather than "bearing" angles.
706     if (type == CSSPrefixedLinearGradient)
707         angleDeg = 90 - angleDeg;
708
709     angleDeg = fmodf(angleDeg, 360);
710     if (angleDeg < 0)
711         angleDeg += 360;
712
713     if (!angleDeg) {
714         firstPoint.set(0, size.height());
715         secondPoint.set(0, 0);
716         return;
717     }
718
719     if (angleDeg == 90) {
720         firstPoint.set(0, 0);
721         secondPoint.set(size.width(), 0);
722         return;
723     }
724
725     if (angleDeg == 180) {
726         firstPoint.set(0, 0);
727         secondPoint.set(0, size.height());
728         return;
729     }
730
731     if (angleDeg == 270) {
732         firstPoint.set(size.width(), 0);
733         secondPoint.set(0, 0);
734         return;
735     }
736
737     // angleDeg is a "bearing angle" (0deg = N, 90deg = E),
738     // but tan expects 0deg = E, 90deg = N.
739     float slope = tan(deg2rad(90 - angleDeg));
740
741     // We find the endpoint by computing the intersection of the line formed by the slope,
742     // and a line perpendicular to it that intersects the corner.
743     float perpendicularSlope = -1 / slope;
744
745     // Compute start corner relative to center, in Cartesian space (+y = up).
746     float halfHeight = size.height() / 2;
747     float halfWidth = size.width() / 2;
748     FloatPoint endCorner;
749     if (angleDeg < 90)
750         endCorner.set(halfWidth, halfHeight);
751     else if (angleDeg < 180)
752         endCorner.set(halfWidth, -halfHeight);
753     else if (angleDeg < 270)
754         endCorner.set(-halfWidth, -halfHeight);
755     else
756         endCorner.set(-halfWidth, halfHeight);
757
758     // Compute c (of y = mx + c) using the corner point.
759     float c = endCorner.y() - perpendicularSlope * endCorner.x();
760     float endX = c / (slope - perpendicularSlope);
761     float endY = perpendicularSlope * endX + c;
762
763     // We computed the end point, so set the second point,
764     // taking into account the moved origin and the fact that we're in drawing space (+y = down).
765     secondPoint.set(halfWidth + endX, halfHeight - endY);
766     // Reflect around the center for the start point.
767     firstPoint.set(halfWidth - endX, halfHeight + endY);
768 }
769
770 PassRefPtr<Gradient> CSSLinearGradientValue::createGradient(const CSSToLengthConversionData& conversionData, const IntSize& size)
771 {
772     ASSERT(!size.isEmpty());
773
774     FloatPoint firstPoint;
775     FloatPoint secondPoint;
776     if (m_angle) {
777         float angle = m_angle->getFloatValue(CSSPrimitiveValue::CSS_DEG);
778         endPointsFromAngle(angle, size, firstPoint, secondPoint, m_gradientType);
779     } else {
780         switch (m_gradientType) {
781         case CSSDeprecatedLinearGradient:
782             firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
783             if (m_secondX || m_secondY)
784                 secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), conversionData, size);
785             else {
786                 if (m_firstX)
787                     secondPoint.setX(size.width() - firstPoint.x());
788                 if (m_firstY)
789                     secondPoint.setY(size.height() - firstPoint.y());
790             }
791             break;
792         case CSSPrefixedLinearGradient:
793             firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
794             if (m_firstX)
795                 secondPoint.setX(size.width() - firstPoint.x());
796             if (m_firstY)
797                 secondPoint.setY(size.height() - firstPoint.y());
798             break;
799         case CSSLinearGradient:
800             if (m_firstX && m_firstY) {
801                 // "Magic" corners, so the 50% line touches two corners.
802                 float rise = size.width();
803                 float run = size.height();
804                 if (m_firstX && m_firstX->getValueID() == CSSValueLeft)
805                     run *= -1;
806                 if (m_firstY && m_firstY->getValueID() == CSSValueBottom)
807                     rise *= -1;
808                 // Compute angle, and flip it back to "bearing angle" degrees.
809                 float angle = 90 - rad2deg(atan2(rise, run));
810                 endPointsFromAngle(angle, size, firstPoint, secondPoint, m_gradientType);
811             } else if (m_firstX || m_firstY) {
812                 secondPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
813                 if (m_firstX)
814                     firstPoint.setX(size.width() - secondPoint.x());
815                 if (m_firstY)
816                     firstPoint.setY(size.height() - secondPoint.y());
817             } else
818                 secondPoint.setY(size.height());
819             break;
820         default:
821             ASSERT_NOT_REACHED();
822         }
823
824     }
825
826     RefPtr<Gradient> gradient = Gradient::create(firstPoint, secondPoint);
827
828     gradient->setDrawsInPMColorSpace(true);
829
830     // Now add the stops.
831     addStops(gradient.get(), conversionData, 1);
832
833     return gradient.release();
834 }
835
836 bool CSSLinearGradientValue::equals(const CSSLinearGradientValue& other) const
837 {
838     if (m_gradientType == CSSDeprecatedLinearGradient)
839         return other.m_gradientType == m_gradientType
840             && compareCSSValuePtr(m_firstX, other.m_firstX)
841             && compareCSSValuePtr(m_firstY, other.m_firstY)
842             && compareCSSValuePtr(m_secondX, other.m_secondX)
843             && compareCSSValuePtr(m_secondY, other.m_secondY)
844             && m_stops == other.m_stops;
845
846     if (m_repeating != other.m_repeating)
847         return false;
848
849     if (m_angle)
850         return compareCSSValuePtr(m_angle, other.m_angle) && m_stops == other.m_stops;
851
852     if (other.m_angle)
853         return false;
854
855     bool equalXandY = false;
856     if (m_firstX && m_firstY)
857         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
858     else if (m_firstX)
859         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
860     else if (m_firstY)
861         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
862     else
863         equalXandY = !other.m_firstX && !other.m_firstY;
864
865     return equalXandY && m_stops == other.m_stops;
866 }
867
868 void CSSLinearGradientValue::traceAfterDispatch(Visitor* visitor)
869 {
870     visitor->trace(m_angle);
871     CSSGradientValue::traceAfterDispatch(visitor);
872 }
873
874 String CSSRadialGradientValue::customCSSText() const
875 {
876     StringBuilder result;
877
878     if (m_gradientType == CSSDeprecatedRadialGradient) {
879         result.appendLiteral("-webkit-gradient(radial, ");
880         result.append(m_firstX->cssText());
881         result.append(' ');
882         result.append(m_firstY->cssText());
883         result.appendLiteral(", ");
884         result.append(m_firstRadius->cssText());
885         result.appendLiteral(", ");
886         result.append(m_secondX->cssText());
887         result.append(' ');
888         result.append(m_secondY->cssText());
889         result.appendLiteral(", ");
890         result.append(m_secondRadius->cssText());
891
892         // FIXME: share?
893         for (unsigned i = 0; i < m_stops.size(); i++) {
894             const CSSGradientColorStop& stop = m_stops[i];
895             result.appendLiteral(", ");
896             if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0) {
897                 result.appendLiteral("from(");
898                 result.append(stop.m_color->cssText());
899                 result.append(')');
900             } else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1) {
901                 result.appendLiteral("to(");
902                 result.append(stop.m_color->cssText());
903                 result.append(')');
904             } else {
905                 result.appendLiteral("color-stop(");
906                 result.appendNumber(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER));
907                 result.appendLiteral(", ");
908                 result.append(stop.m_color->cssText());
909                 result.append(')');
910             }
911         }
912     } else if (m_gradientType == CSSPrefixedRadialGradient) {
913         if (m_repeating)
914             result.appendLiteral("-webkit-repeating-radial-gradient(");
915         else
916             result.appendLiteral("-webkit-radial-gradient(");
917
918         if (m_firstX && m_firstY) {
919             result.append(m_firstX->cssText());
920             result.append(' ');
921             result.append(m_firstY->cssText());
922         } else if (m_firstX)
923             result.append(m_firstX->cssText());
924          else if (m_firstY)
925             result.append(m_firstY->cssText());
926         else
927             result.appendLiteral("center");
928
929         if (m_shape || m_sizingBehavior) {
930             result.appendLiteral(", ");
931             if (m_shape) {
932                 result.append(m_shape->cssText());
933                 result.append(' ');
934             } else
935                 result.appendLiteral("ellipse ");
936
937             if (m_sizingBehavior)
938                 result.append(m_sizingBehavior->cssText());
939             else
940                 result.appendLiteral("cover");
941
942         } else if (m_endHorizontalSize && m_endVerticalSize) {
943             result.appendLiteral(", ");
944             result.append(m_endHorizontalSize->cssText());
945             result.append(' ');
946             result.append(m_endVerticalSize->cssText());
947         }
948
949         for (unsigned i = 0; i < m_stops.size(); i++) {
950             const CSSGradientColorStop& stop = m_stops[i];
951             result.appendLiteral(", ");
952             result.append(stop.m_color->cssText());
953             if (stop.m_position) {
954                 result.append(' ');
955                 result.append(stop.m_position->cssText());
956             }
957         }
958     } else {
959         if (m_repeating)
960             result.appendLiteral("repeating-radial-gradient(");
961         else
962             result.appendLiteral("radial-gradient(");
963
964         bool wroteSomething = false;
965
966         // The only ambiguous case that needs an explicit shape to be provided
967         // is when a sizing keyword is used (or all sizing is omitted).
968         if (m_shape && m_shape->getValueID() != CSSValueEllipse && (m_sizingBehavior || (!m_sizingBehavior && !m_endHorizontalSize))) {
969             result.appendLiteral("circle");
970             wroteSomething = true;
971         }
972
973         if (m_sizingBehavior && m_sizingBehavior->getValueID() != CSSValueFarthestCorner) {
974             if (wroteSomething)
975                 result.append(' ');
976             result.append(m_sizingBehavior->cssText());
977             wroteSomething = true;
978         } else if (m_endHorizontalSize) {
979             if (wroteSomething)
980                 result.append(' ');
981             result.append(m_endHorizontalSize->cssText());
982             if (m_endVerticalSize) {
983                 result.append(' ');
984                 result.append(m_endVerticalSize->cssText());
985             }
986             wroteSomething = true;
987         }
988
989         if (m_firstX || m_firstY) {
990             if (wroteSomething)
991                 result.append(' ');
992             result.appendLiteral("at ");
993             if (m_firstX && m_firstY) {
994                 result.append(m_firstX->cssText());
995                 result.append(' ');
996                 result.append(m_firstY->cssText());
997             } else if (m_firstX)
998                 result.append(m_firstX->cssText());
999             else
1000                 result.append(m_firstY->cssText());
1001             wroteSomething = true;
1002         }
1003
1004         if (wroteSomething)
1005             result.appendLiteral(", ");
1006
1007         for (unsigned i = 0; i < m_stops.size(); i++) {
1008             const CSSGradientColorStop& stop = m_stops[i];
1009             if (i)
1010                 result.appendLiteral(", ");
1011             if (stop.m_color)
1012                 result.append(stop.m_color->cssText());
1013             if (stop.m_color && stop.m_position)
1014                 result.append(' ');
1015             if (stop.m_position)
1016                 result.append(stop.m_position->cssText());
1017         }
1018
1019     }
1020
1021     result.append(')');
1022     return result.toString();
1023 }
1024
1025 float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, const CSSToLengthConversionData& conversionData, float* widthOrHeight)
1026 {
1027     float result = 0;
1028     if (radius->isNumber()) // Can the radius be a percentage?
1029         result = radius->getFloatValue() * conversionData.zoom();
1030     else if (widthOrHeight && radius->isPercentage())
1031         result = *widthOrHeight * radius->getFloatValue() / 100;
1032     else
1033         result = radius->computeLength<float>(conversionData);
1034
1035     return result;
1036 }
1037
1038 static float distanceToClosestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
1039 {
1040     FloatPoint topLeft;
1041     float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
1042
1043     FloatPoint topRight(size.width(), 0);
1044     float topRightDistance = FloatSize(p - topRight).diagonalLength();
1045
1046     FloatPoint bottomLeft(0, size.height());
1047     float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
1048
1049     FloatPoint bottomRight(size.width(), size.height());
1050     float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
1051
1052     corner = topLeft;
1053     float minDistance = topLeftDistance;
1054     if (topRightDistance < minDistance) {
1055         minDistance = topRightDistance;
1056         corner = topRight;
1057     }
1058
1059     if (bottomLeftDistance < minDistance) {
1060         minDistance = bottomLeftDistance;
1061         corner = bottomLeft;
1062     }
1063
1064     if (bottomRightDistance < minDistance) {
1065         minDistance = bottomRightDistance;
1066         corner = bottomRight;
1067     }
1068     return minDistance;
1069 }
1070
1071 static float distanceToFarthestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
1072 {
1073     FloatPoint topLeft;
1074     float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
1075
1076     FloatPoint topRight(size.width(), 0);
1077     float topRightDistance = FloatSize(p - topRight).diagonalLength();
1078
1079     FloatPoint bottomLeft(0, size.height());
1080     float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
1081
1082     FloatPoint bottomRight(size.width(), size.height());
1083     float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
1084
1085     corner = topLeft;
1086     float maxDistance = topLeftDistance;
1087     if (topRightDistance > maxDistance) {
1088         maxDistance = topRightDistance;
1089         corner = topRight;
1090     }
1091
1092     if (bottomLeftDistance > maxDistance) {
1093         maxDistance = bottomLeftDistance;
1094         corner = bottomLeft;
1095     }
1096
1097     if (bottomRightDistance > maxDistance) {
1098         maxDistance = bottomRightDistance;
1099         corner = bottomRight;
1100     }
1101     return maxDistance;
1102 }
1103
1104 // Compute horizontal radius of ellipse with center at 0,0 which passes through p, and has
1105 // width/height given by aspectRatio.
1106 static inline float horizontalEllipseRadius(const FloatSize& p, float aspectRatio)
1107 {
1108     // x^2/a^2 + y^2/b^2 = 1
1109     // a/b = aspectRatio, b = a/aspectRatio
1110     // a = sqrt(x^2 + y^2/(1/r^2))
1111     return sqrtf(p.width() * p.width() + (p.height() * p.height()) / (1 / (aspectRatio * aspectRatio)));
1112 }
1113
1114 // FIXME: share code with the linear version
1115 PassRefPtr<Gradient> CSSRadialGradientValue::createGradient(const CSSToLengthConversionData& conversionData, const IntSize& size)
1116 {
1117     ASSERT(!size.isEmpty());
1118
1119     FloatPoint firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
1120     if (!m_firstX)
1121         firstPoint.setX(size.width() / 2);
1122     if (!m_firstY)
1123         firstPoint.setY(size.height() / 2);
1124
1125     FloatPoint secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), conversionData, size);
1126     if (!m_secondX)
1127         secondPoint.setX(size.width() / 2);
1128     if (!m_secondY)
1129         secondPoint.setY(size.height() / 2);
1130
1131     float firstRadius = 0;
1132     if (m_firstRadius)
1133         firstRadius = resolveRadius(m_firstRadius.get(), conversionData);
1134
1135     float secondRadius = 0;
1136     float aspectRatio = 1; // width / height.
1137     if (m_secondRadius)
1138         secondRadius = resolveRadius(m_secondRadius.get(), conversionData);
1139     else if (m_endHorizontalSize) {
1140         float width = size.width();
1141         float height = size.height();
1142         secondRadius = resolveRadius(m_endHorizontalSize.get(), conversionData, &width);
1143         if (m_endVerticalSize)
1144             aspectRatio = secondRadius / resolveRadius(m_endVerticalSize.get(), conversionData, &height);
1145         else
1146             aspectRatio = 1;
1147     } else {
1148         enum GradientShape { Circle, Ellipse };
1149         GradientShape shape = Ellipse;
1150         if ((m_shape && m_shape->getValueID() == CSSValueCircle)
1151             || (!m_shape && !m_sizingBehavior && m_endHorizontalSize && !m_endVerticalSize))
1152             shape = Circle;
1153
1154         enum GradientFill { ClosestSide, ClosestCorner, FarthestSide, FarthestCorner };
1155         GradientFill fill = FarthestCorner;
1156
1157         switch (m_sizingBehavior ? m_sizingBehavior->getValueID() : 0) {
1158         case CSSValueContain:
1159         case CSSValueClosestSide:
1160             fill = ClosestSide;
1161             break;
1162         case CSSValueClosestCorner:
1163             fill = ClosestCorner;
1164             break;
1165         case CSSValueFarthestSide:
1166             fill = FarthestSide;
1167             break;
1168         case CSSValueCover:
1169         case CSSValueFarthestCorner:
1170             fill = FarthestCorner;
1171             break;
1172         default:
1173             break;
1174         }
1175
1176         // Now compute the end radii based on the second point, shape and fill.
1177
1178         // Horizontal
1179         switch (fill) {
1180         case ClosestSide: {
1181             float xDist = std::min(secondPoint.x(), size.width() - secondPoint.x());
1182             float yDist = std::min(secondPoint.y(), size.height() - secondPoint.y());
1183             if (shape == Circle) {
1184                 float smaller = std::min(xDist, yDist);
1185                 xDist = smaller;
1186                 yDist = smaller;
1187             }
1188             secondRadius = xDist;
1189             aspectRatio = xDist / yDist;
1190             break;
1191         }
1192         case FarthestSide: {
1193             float xDist = std::max(secondPoint.x(), size.width() - secondPoint.x());
1194             float yDist = std::max(secondPoint.y(), size.height() - secondPoint.y());
1195             if (shape == Circle) {
1196                 float larger = std::max(xDist, yDist);
1197                 xDist = larger;
1198                 yDist = larger;
1199             }
1200             secondRadius = xDist;
1201             aspectRatio = xDist / yDist;
1202             break;
1203         }
1204         case ClosestCorner: {
1205             FloatPoint corner;
1206             float distance = distanceToClosestCorner(secondPoint, size, corner);
1207             if (shape == Circle)
1208                 secondRadius = distance;
1209             else {
1210                 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
1211                 // that it would if closest-side or farthest-side were specified, as appropriate.
1212                 float xDist = std::min(secondPoint.x(), size.width() - secondPoint.x());
1213                 float yDist = std::min(secondPoint.y(), size.height() - secondPoint.y());
1214
1215                 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
1216                 aspectRatio = xDist / yDist;
1217             }
1218             break;
1219         }
1220
1221         case FarthestCorner: {
1222             FloatPoint corner;
1223             float distance = distanceToFarthestCorner(secondPoint, size, corner);
1224             if (shape == Circle)
1225                 secondRadius = distance;
1226             else {
1227                 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
1228                 // that it would if closest-side or farthest-side were specified, as appropriate.
1229                 float xDist = std::max(secondPoint.x(), size.width() - secondPoint.x());
1230                 float yDist = std::max(secondPoint.y(), size.height() - secondPoint.y());
1231
1232                 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
1233                 aspectRatio = xDist / yDist;
1234             }
1235             break;
1236         }
1237         }
1238     }
1239
1240     RefPtr<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius, aspectRatio);
1241
1242     gradient->setDrawsInPMColorSpace(true);
1243
1244     // addStops() only uses maxExtent for repeating gradients.
1245     float maxExtent = 0;
1246     if (m_repeating) {
1247         FloatPoint corner;
1248         maxExtent = distanceToFarthestCorner(secondPoint, size, corner);
1249     }
1250
1251     // Now add the stops.
1252     addStops(gradient.get(), conversionData, maxExtent);
1253
1254     return gradient.release();
1255 }
1256
1257 bool CSSRadialGradientValue::equals(const CSSRadialGradientValue& other) const
1258 {
1259     if (m_gradientType == CSSDeprecatedRadialGradient)
1260         return other.m_gradientType == m_gradientType
1261             && compareCSSValuePtr(m_firstX, other.m_firstX)
1262             && compareCSSValuePtr(m_firstY, other.m_firstY)
1263             && compareCSSValuePtr(m_secondX, other.m_secondX)
1264             && compareCSSValuePtr(m_secondY, other.m_secondY)
1265             && compareCSSValuePtr(m_firstRadius, other.m_firstRadius)
1266             && compareCSSValuePtr(m_secondRadius, other.m_secondRadius)
1267             && m_stops == other.m_stops;
1268
1269     if (m_repeating != other.m_repeating)
1270         return false;
1271
1272     bool equalXandY = false;
1273     if (m_firstX && m_firstY)
1274         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
1275     else if (m_firstX)
1276         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
1277     else if (m_firstY)
1278         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
1279     else
1280         equalXandY = !other.m_firstX && !other.m_firstY;
1281
1282     if (!equalXandY)
1283         return false;
1284
1285     bool equalShape = true;
1286     bool equalSizingBehavior = true;
1287     bool equalHorizontalAndVerticalSize = true;
1288
1289     if (m_shape)
1290         equalShape = compareCSSValuePtr(m_shape, other.m_shape);
1291     else if (m_sizingBehavior)
1292         equalSizingBehavior = compareCSSValuePtr(m_sizingBehavior, other.m_sizingBehavior);
1293     else if (m_endHorizontalSize && m_endVerticalSize)
1294         equalHorizontalAndVerticalSize = compareCSSValuePtr(m_endHorizontalSize, other.m_endHorizontalSize) && compareCSSValuePtr(m_endVerticalSize, other.m_endVerticalSize);
1295     else {
1296         equalShape = !other.m_shape;
1297         equalSizingBehavior = !other.m_sizingBehavior;
1298         equalHorizontalAndVerticalSize = !other.m_endHorizontalSize && !other.m_endVerticalSize;
1299     }
1300     return equalShape && equalSizingBehavior && equalHorizontalAndVerticalSize && m_stops == other.m_stops;
1301 }
1302
1303 void CSSRadialGradientValue::traceAfterDispatch(Visitor* visitor)
1304 {
1305     visitor->trace(m_firstRadius);
1306     visitor->trace(m_secondRadius);
1307     visitor->trace(m_shape);
1308     visitor->trace(m_sizingBehavior);
1309     visitor->trace(m_endHorizontalSize);
1310     visitor->trace(m_endVerticalSize);
1311     CSSGradientValue::traceAfterDispatch(visitor);
1312 }
1313
1314 } // namespace blink