Add modern API for overriding the page's specified viewport configuration
[WebKit-https.git] / Source / WebCore / 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 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 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 "CSSGradientValue.h"
28
29 #include "CSSCalculationValue.h"
30 #include "CSSToLengthConversionData.h"
31 #include "CSSValueKeywords.h"
32 #include "FloatSize.h"
33 #include "Gradient.h"
34 #include "GradientImage.h"
35 #include "NodeRenderStyle.h"
36 #include "Pair.h"
37 #include "RenderElement.h"
38 #include "RenderView.h"
39 #include "StyleResolver.h"
40 #include <wtf/text/StringBuilder.h>
41
42 namespace WebCore {
43
44 static inline Ref<Gradient> createGradient(CSSGradientValue& value, RenderElement& renderer, FloatSize size)
45 {
46     if (is<CSSLinearGradientValue>(value))
47         return downcast<CSSLinearGradientValue>(value).createGradient(renderer, size);
48     if (is<CSSRadialGradientValue>(value))
49         return downcast<CSSRadialGradientValue>(value).createGradient(renderer, size);
50     return downcast<CSSConicGradientValue>(value).createGradient(renderer, size);
51 }
52
53 RefPtr<Image> CSSGradientValue::image(RenderElement& renderer, const FloatSize& size)
54 {
55     if (size.isEmpty())
56         return nullptr;
57     bool cacheable = isCacheable() && !renderer.style().hasAppleColorFilter();
58     if (cacheable) {
59         if (!clients().contains(&renderer))
60             return nullptr;
61         if (auto* result = cachedImageForSize(size))
62             return result;
63     }
64     auto newImage = GradientImage::create(createGradient(*this, renderer, size), size);
65     if (cacheable)
66         saveCachedImageForSize(size, newImage.get());
67     return newImage;
68 }
69
70 // Should only ever be called for deprecated gradients.
71 static inline bool compareStops(const CSSGradientColorStop& a, const CSSGradientColorStop& b)
72 {
73     double aVal = a.m_position->doubleValue(CSSPrimitiveValue::CSS_NUMBER);
74     double bVal = b.m_position->doubleValue(CSSPrimitiveValue::CSS_NUMBER);
75
76     return aVal < bVal;
77 }
78
79 void CSSGradientValue::sortStopsIfNeeded()
80 {
81     ASSERT(m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDeprecatedRadialGradient);
82     if (!m_stopsSorted) {
83         if (m_stops.size())
84             std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
85         m_stopsSorted = true;
86     }
87 }
88
89 struct GradientStop {
90     Color color;
91     float offset { 0 };
92     bool specified { false };
93     bool isMidpoint { false };
94 };
95
96 static inline Ref<CSSGradientValue> clone(CSSGradientValue& value)
97 {
98     if (is<CSSLinearGradientValue>(value))
99         return downcast<CSSLinearGradientValue>(value).clone();
100     if (is<CSSRadialGradientValue>(value))
101         return downcast<CSSRadialGradientValue>(value).clone();
102     ASSERT(is<CSSConicGradientValue>(value));
103     return downcast<CSSConicGradientValue>(value).clone();
104 }
105
106 Ref<CSSGradientValue> CSSGradientValue::gradientWithStylesResolved(const StyleResolver& styleResolver)
107 {
108     bool colorIsDerivedFromElement = false;
109     for (auto& stop : m_stops) {
110         if (!stop.isMidpoint && styleResolver.colorFromPrimitiveValueIsDerivedFromElement(*stop.m_color)) {
111             stop.m_colorIsDerivedFromElement = true;
112             colorIsDerivedFromElement = true;
113             break;
114         }
115     }
116     auto result = colorIsDerivedFromElement ? clone(*this) : makeRef(*this);
117     for (auto& stop : result->m_stops) {
118         if (!stop.isMidpoint)
119             stop.m_resolvedColor = styleResolver.colorFromPrimitiveValue(*stop.m_color);
120     }
121     return result;
122 }
123
124 class LinearGradientAdapter {
125 public:
126     explicit LinearGradientAdapter(Gradient::LinearData& data)
127         : m_data(data)
128     {
129     }
130     
131     float gradientLength() const
132     {
133         auto gradientSize = m_data.point0 - m_data.point1;
134         return gradientSize.diagonalLength();
135     }
136     float maxExtent(float, float) const { return 1; }
137
138     void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
139     {
140         float firstOffset = stops.first().offset;
141         float lastOffset = stops.last().offset;
142         if (firstOffset != lastOffset) {
143             float scale = lastOffset - firstOffset;
144
145             for (auto& stop : stops)
146                 stop.offset = (stop.offset - firstOffset) / scale;
147
148             auto p0 = m_data.point0;
149             auto p1 = m_data.point1;
150             m_data.point0 = { p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y()) };
151             m_data.point1 = { p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y()) };
152         } else {
153             // There's a single position that is outside the scale, clamp the positions to 1.
154             for (auto& stop : stops)
155                 stop.offset = 1;
156         }
157     }
158
159 private:
160     Gradient::LinearData& m_data;
161 };
162
163 class RadialGradientAdapter {
164 public:
165     explicit RadialGradientAdapter(Gradient::RadialData& data)
166         : m_data(data)
167     {
168     }
169
170     float gradientLength() const { return m_data.endRadius; }
171
172     // Radial gradients may need to extend further than the endpoints, because they have
173     // to repeat out to the corners of the box.
174     float maxExtent(float maxLengthForRepeat, float gradientLength) const
175     {
176         if (maxLengthForRepeat > gradientLength)
177             return gradientLength > 0 ? maxLengthForRepeat / gradientLength : 0;
178         return 1;
179     }
180
181     void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
182     {
183         auto numStops = stops.size();
184
185         // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
186         float firstOffset = 0;
187         float lastOffset = stops.last().offset;
188         float scale = lastOffset - firstOffset;
189
190         // Reset points below 0 to the first visible color.
191         size_t firstZeroOrGreaterIndex = numStops;
192         for (size_t i = 0; i < numStops; ++i) {
193             if (stops[i].offset >= 0) {
194                 firstZeroOrGreaterIndex = i;
195                 break;
196             }
197         }
198
199         if (firstZeroOrGreaterIndex > 0) {
200             if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
201                 float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
202                 float nextOffset = stops[firstZeroOrGreaterIndex].offset;
203
204                 float interStopProportion = -prevOffset / (nextOffset - prevOffset);
205                 // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
206                 Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
207
208                 // Clamp the positions to 0 and set the color.
209                 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
210                     stops[i].offset = 0;
211                     stops[i].color = blendedColor;
212                 }
213             } else {
214                 // All stops are below 0; just clamp them.
215                 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i)
216                     stops[i].offset = 0;
217             }
218         }
219
220         for (auto& stop : stops)
221             stop.offset /= scale;
222
223         m_data.startRadius *= scale;
224         m_data.endRadius *= scale;
225     }
226
227 private:
228     Gradient::RadialData& m_data;
229 };
230
231 class ConicGradientAdapter {
232 public:
233     explicit ConicGradientAdapter() { }
234     float gradientLength() const { return 1; }
235     float maxExtent(float, float) const { return 1; }
236
237     void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
238     {
239         auto numStops = stops.size();
240         
241         size_t firstZeroOrGreaterIndex = numStops;
242         for (size_t i = 0; i < numStops; ++i) {
243             if (stops[i].offset >= 0) {
244                 firstZeroOrGreaterIndex = i;
245                 break;
246             }
247         }
248
249         if (firstZeroOrGreaterIndex > 0) {
250             if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
251                 float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
252                 float nextOffset = stops[firstZeroOrGreaterIndex].offset;
253                 
254                 float interStopProportion = -prevOffset / (nextOffset - prevOffset);
255                 // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
256                 Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
257                 
258                 // Clamp the positions to 0 and set the color.
259                 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
260                     stops[i].offset = 0;
261                     stops[i].color = blendedColor;
262                 }
263             } else {
264                 // All stops are below 0; just clamp them.
265                 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i)
266                     stops[i].offset = 0;
267             }
268         }
269
270         size_t lastOneOrLessIndex = numStops;
271         for (int i = numStops - 1; i >= 0; --i) {
272             if (stops[i].offset <= 1) {
273                 lastOneOrLessIndex = i;
274                 break;
275             }
276         }
277         
278         if (lastOneOrLessIndex < numStops - 1) {
279             if (lastOneOrLessIndex < numStops && stops[lastOneOrLessIndex].offset < 1) {
280                 float prevOffset = stops[lastOneOrLessIndex].offset;
281                 float nextOffset = stops[lastOneOrLessIndex + 1].offset;
282                 
283                 float interStopProportion = (1 - prevOffset) / (nextOffset - prevOffset);
284                 // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
285                 Color blendedColor = blend(stops[lastOneOrLessIndex].color, stops[lastOneOrLessIndex + 1].color, interStopProportion);
286                 
287                 // Clamp the positions to 1 and set the color.
288                 for (size_t i = lastOneOrLessIndex + 1; i < numStops; ++i) {
289                     stops[i].offset = 1;
290                     stops[i].color = blendedColor;
291                 }
292             } else {
293                 // All stops are above 1; just clamp them.
294                 for (size_t i = lastOneOrLessIndex; i < numStops; ++i)
295                     stops[i].offset = 1;
296             }
297         }
298     }
299 };
300
301 template<typename GradientAdapter>
302 Gradient::ColorStopVector CSSGradientValue::computeStops(GradientAdapter& gradientAdapter, const CSSToLengthConversionData& conversionData, const RenderStyle& style, float maxLengthForRepeat)
303 {
304     if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDeprecatedRadialGradient) {
305         sortStopsIfNeeded();
306
307         Gradient::ColorStopVector result;
308         result.reserveInitialCapacity(m_stops.size());
309
310         for (auto& stop : m_stops) {
311             float offset;
312             if (stop.m_position->isPercentage())
313                 offset = stop.m_position->floatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
314             else
315                 offset = stop.m_position->floatValue(CSSPrimitiveValue::CSS_NUMBER);
316
317             Color color = stop.m_resolvedColor;
318             if (style.hasAppleColorFilter())
319                 style.appleColorFilter().transformColor(color);
320             result.uncheckedAppend({ offset, color });
321         }
322
323         return result;
324     }
325
326     size_t numStops = m_stops.size();
327     Vector<GradientStop> stops(numStops);
328
329     float gradientLength = gradientAdapter.gradientLength();
330
331     for (size_t i = 0; i < numStops; ++i) {
332         auto& stop = m_stops[i];
333
334         stops[i].isMidpoint = stop.isMidpoint;
335
336         Color color = stop.m_resolvedColor;
337         if (style.hasAppleColorFilter())
338             style.appleColorFilter().transformColor(color);
339
340         stops[i].color = color;
341
342         if (stop.m_position) {
343             auto& positionValue = *stop.m_position;
344             if (positionValue.isPercentage())
345                 stops[i].offset = positionValue.floatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
346             else if (positionValue.isLength() || positionValue.isViewportPercentageLength() || positionValue.isCalculatedPercentageWithLength()) {
347                 float length;
348                 if (positionValue.isLength())
349                     length = positionValue.computeLength<float>(conversionData);
350                 else {
351                     Ref<CalculationValue> calculationValue { positionValue.cssCalcValue()->createCalculationValue(conversionData) };
352                     length = calculationValue->evaluate(gradientLength);
353                 }
354                 stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0;
355             } else if (positionValue.isAngle())
356                 stops[i].offset = positionValue.floatValue(CSSPrimitiveValue::CSS_DEG) / 360;
357             else {
358                 ASSERT_NOT_REACHED();
359                 stops[i].offset = 0;
360             }
361             stops[i].specified = true;
362         } else {
363             // If the first color-stop does not have a position, its position defaults to 0%.
364             // If the last color-stop does not have a position, its position defaults to 100%.
365             if (!i) {
366                 stops[i].offset = 0;
367                 stops[i].specified = true;
368             } else if (numStops > 1 && i == numStops - 1) {
369                 stops[i].offset = 1;
370                 stops[i].specified = true;
371             }
372         }
373
374         // If a color-stop has a position that is less than the specified position of any
375         // color-stop before it in the list, its position is changed to be equal to the
376         // largest specified position of any color-stop before it.
377         if (stops[i].specified && i > 0) {
378             size_t prevSpecifiedIndex;
379             for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; --prevSpecifiedIndex) {
380                 if (stops[prevSpecifiedIndex].specified)
381                     break;
382             }
383
384             if (stops[i].offset < stops[prevSpecifiedIndex].offset)
385                 stops[i].offset = stops[prevSpecifiedIndex].offset;
386         }
387     }
388
389     ASSERT(stops[0].specified && stops[numStops - 1].specified);
390
391     // If any color-stop still does not have a position, then, for each run of adjacent
392     // color-stops without positions, set their positions so that they are evenly spaced
393     // between the preceding and following color-stops with positions.
394     if (numStops > 2) {
395         size_t unspecifiedRunStart = 0;
396         bool inUnspecifiedRun = false;
397
398         for (size_t i = 0; i < numStops; ++i) {
399             if (!stops[i].specified && !inUnspecifiedRun) {
400                 unspecifiedRunStart = i;
401                 inUnspecifiedRun = true;
402             } else if (stops[i].specified && inUnspecifiedRun) {
403                 size_t unspecifiedRunEnd = i;
404
405                 if (unspecifiedRunStart < unspecifiedRunEnd) {
406                     float lastSpecifiedOffset = stops[unspecifiedRunStart - 1].offset;
407                     float nextSpecifiedOffset = stops[unspecifiedRunEnd].offset;
408                     float delta = (nextSpecifiedOffset - lastSpecifiedOffset) / (unspecifiedRunEnd - unspecifiedRunStart + 1);
409
410                     for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j)
411                         stops[j].offset = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
412                 }
413
414                 inUnspecifiedRun = false;
415             }
416         }
417     }
418
419     // Walk over the color stops, look for midpoints and add stops as needed.
420     // If mid < 50%, add 2 stops to the left and 6 to the right
421     // else add 6 stops to the left and 2 to the right.
422     // Stops on the side with the most stops start midway because the curve approximates
423     // a line in that region. We then add 5 more color stops on that side to minimize the change
424     // how the luminance changes at each of the color stops. We don't have to add as many on the other side
425     // since it becomes small which increases the differentation of luminance which hides the color stops.
426     // Even with 4 extra color stops, it *is* possible to discern the steps when the gradient is large and has
427     // large luminance differences between midpoint and color stop. If this becomes an issue, we can consider
428     // making this algorithm a bit smarter.
429
430     // Midpoints that coincide with color stops are treated specially since they don't require
431     // extra stops and generate hard lines.
432     for (size_t x = 1; x < stops.size() - 1;) {
433         if (!stops[x].isMidpoint) {
434             ++x;
435             continue;
436         }
437
438         // Find previous and next color so we know what to interpolate between.
439         // We already know they have a color since we checked for that earlier.
440         Color color1 = stops[x - 1].color;
441         Color color2 = stops[x + 1].color;
442         // Likewise find the position of previous and next color stop.
443         float offset1 = stops[x - 1].offset;
444         float offset2 = stops[x + 1].offset;
445         float offset = stops[x].offset;
446
447         // Check if everything coincides or the midpoint is exactly in the middle.
448         // If so, ignore the midpoint.
449         if (offset - offset1 == offset2 - offset) {
450             stops.remove(x);
451             continue;
452         }
453
454         // Check if we coincide with the left color stop.
455         if (offset1 == offset) {
456             // Morph the midpoint to a regular stop with the color of the next color stop.
457             stops[x].color = color2;
458             stops[x].isMidpoint = false;
459             continue;
460         }
461
462         // Check if we coincide with the right color stop.
463         if (offset2 == offset) {
464             // Morph the midpoint to a regular stop with the color of the previous color stop.
465             stops[x].color = color1;
466             stops[x].isMidpoint = false;
467             continue;
468         }
469
470         float midpoint = (offset - offset1) / (offset2 - offset1);
471         GradientStop newStops[9];
472         if (midpoint > .5f) {
473             for (size_t y = 0; y < 7; ++y)
474                 newStops[y].offset = offset1 + (offset - offset1) * (7 + y) / 13;
475
476             newStops[7].offset = offset + (offset2 - offset) / 3;
477             newStops[8].offset = offset + (offset2 - offset) * 2 / 3;
478         } else {
479             newStops[0].offset = offset1 + (offset - offset1) / 3;
480             newStops[1].offset = offset1 + (offset - offset1) * 2 / 3;
481
482             for (size_t y = 0; y < 7; ++y)
483                 newStops[y + 2].offset = offset + (offset2 - offset) * y / 13;
484         }
485         // calculate colors
486         for (size_t y = 0; y < 9; ++y) {
487             float relativeOffset = (newStops[y].offset - offset1) / (offset2 - offset1);
488             float multiplier = std::pow(relativeOffset, std::log(.5f) / std::log(midpoint));
489             // FIXME: Why not premultiply here?
490             newStops[y].color = blend(color1, color2, multiplier, false /* do not premultiply */);
491         }
492
493         stops.remove(x);
494         stops.insert(x, newStops, 9);
495         x += 9;
496     }
497
498     numStops = stops.size();
499
500     // If the gradient is repeating, repeat the color stops.
501     // We can't just push this logic down into the platform-specific Gradient code,
502     // because we have to know the extent of the gradient, and possible move the end points.
503     if (m_repeating && numStops > 1) {
504         // If the difference in the positions of the first and last color-stops is 0,
505         // the gradient defines a solid-color image with the color of the last color-stop in the rule.
506         float gradientRange = stops.last().offset - stops.first().offset;
507         if (!gradientRange) {
508             stops.first().offset = 0;
509             stops.first().color = stops.last().color;
510             stops.shrink(1);
511             numStops = 1;
512         } else {
513             float maxExtent = gradientAdapter.maxExtent(maxLengthForRepeat, gradientLength);
514
515             size_t originalNumStops = numStops;
516             size_t originalFirstStopIndex = 0;
517
518             // Work backwards from the first, adding stops until we get one before 0.
519             float firstOffset = stops[0].offset;
520             if (firstOffset > 0) {
521                 float currOffset = firstOffset;
522                 size_t srcStopOrdinal = originalNumStops - 1;
523
524                 while (true) {
525                     GradientStop newStop = stops[originalFirstStopIndex + srcStopOrdinal];
526                     newStop.offset = currOffset;
527                     stops.insert(0, newStop);
528                     ++originalFirstStopIndex;
529                     if (currOffset < 0)
530                         break;
531
532                     if (srcStopOrdinal)
533                         currOffset -= stops[originalFirstStopIndex + srcStopOrdinal].offset - stops[originalFirstStopIndex + srcStopOrdinal - 1].offset;
534                     srcStopOrdinal = (srcStopOrdinal + originalNumStops - 1) % originalNumStops;
535                 }
536             }
537
538             // Work forwards from the end, adding stops until we get one after 1.
539             float lastOffset = stops[stops.size() - 1].offset;
540             if (lastOffset < maxExtent) {
541                 float currOffset = lastOffset;
542                 size_t srcStopOrdinal = 0;
543
544                 while (true) {
545                     size_t srcStopIndex = originalFirstStopIndex + srcStopOrdinal;
546                     GradientStop newStop = stops[srcStopIndex];
547                     newStop.offset = currOffset;
548                     stops.append(newStop);
549                     if (currOffset > maxExtent)
550                         break;
551                     if (srcStopOrdinal < originalNumStops - 1)
552                         currOffset += stops[srcStopIndex + 1].offset - stops[srcStopIndex].offset;
553                     srcStopOrdinal = (srcStopOrdinal + 1) % originalNumStops;
554                 }
555             }
556         }
557     }
558
559     // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and adjusting the stops.
560     if (stops.size() > 1 && (stops.first().offset < 0 || stops.last().offset > 1))
561         gradientAdapter.normalizeStopsAndEndpointsOutsideRange(stops);
562     
563     Gradient::ColorStopVector result;
564     result.reserveInitialCapacity(stops.size());
565     for (auto& stop : stops)
566         result.uncheckedAppend({ stop.offset, stop.color });
567
568     return result;
569 }
570
571 static float positionFromValue(const CSSPrimitiveValue* value, const CSSToLengthConversionData& conversionData, const FloatSize& size, bool isHorizontal)
572 {
573     int origin = 0;
574     int sign = 1;
575     int edgeDistance = isHorizontal ? size.width() : size.height();
576     
577     // In this case the center of the gradient is given relative to an edge in the
578     // form of: [ top | bottom | right | left ] [ <percentage> | <length> ].
579     if (value->isPair()) {
580         CSSValueID originID = value->pairValue()->first()->valueID();
581         value = value->pairValue()->second();
582         
583         if (originID == CSSValueRight || originID == CSSValueBottom) {
584             // For right/bottom, the offset is relative to the far edge.
585             origin = edgeDistance;
586             sign = -1;
587         }
588     }
589     
590     if (value->isNumber())
591         return origin + sign * value->floatValue() * conversionData.zoom();
592     
593     if (value->isPercentage())
594         return origin + sign * value->floatValue() / 100.f * edgeDistance;
595
596     if (value->isCalculatedPercentageWithLength()) {
597         Ref<CalculationValue> calculationValue { value->cssCalcValue()->createCalculationValue(conversionData) };
598         return origin + sign * calculationValue->evaluate(edgeDistance);
599     }
600     
601     switch (value->valueID()) {
602     case CSSValueTop:
603         ASSERT(!isHorizontal);
604         return 0;
605     case CSSValueLeft:
606         ASSERT(isHorizontal);
607         return 0;
608     case CSSValueBottom:
609         ASSERT(!isHorizontal);
610         return size.height();
611     case CSSValueRight:
612         ASSERT(isHorizontal);
613         return size.width();
614     case CSSValueCenter:
615         return origin + sign * .5f * edgeDistance;
616     default:
617         break;
618     }
619
620     return origin + sign * value->computeLength<float>(conversionData);
621 }
622
623 FloatPoint CSSGradientValue::computeEndPoint(CSSPrimitiveValue* horizontal, CSSPrimitiveValue* vertical, const CSSToLengthConversionData& conversionData, const FloatSize& size)
624 {
625     FloatPoint result;
626
627     if (horizontal)
628         result.setX(positionFromValue(horizontal, conversionData, size, true));
629
630     if (vertical)
631         result.setY(positionFromValue(vertical, conversionData, size, false));
632
633     return result;
634 }
635
636 bool CSSGradientValue::isCacheable() const
637 {
638     for (auto& stop : m_stops) {
639         if (stop.m_colorIsDerivedFromElement)
640             return false;
641
642         if (!stop.m_position)
643             continue;
644
645         if (stop.m_position->isFontRelativeLength())
646             return false;
647     }
648
649     return true;
650 }
651
652 bool CSSGradientValue::knownToBeOpaque(const RenderElement& renderer) const
653 {
654     bool hasColorFilter = renderer.style().hasAppleColorFilter();
655
656     for (auto& stop : m_stops) {
657         if (hasColorFilter) {
658             Color stopColor = stop.m_resolvedColor;
659             renderer.style().appleColorFilter().transformColor(stopColor);
660             if (!stopColor.isOpaque())
661                 return false;
662         }
663
664         if (!stop.m_resolvedColor.isOpaque())
665             return false;
666     }
667     return true;
668 }
669
670 String CSSLinearGradientValue::customCSSText() const
671 {
672     StringBuilder result;
673     if (m_gradientType == CSSDeprecatedLinearGradient) {
674         result.appendLiteral("-webkit-gradient(linear, ");
675         result.append(m_firstX->cssText());
676         result.append(' ');
677         result.append(m_firstY->cssText());
678         result.appendLiteral(", ");
679         result.append(m_secondX->cssText());
680         result.append(' ');
681         result.append(m_secondY->cssText());
682
683         for (auto& stop : m_stops) {
684             result.appendLiteral(", ");
685             auto position = stop.m_position->doubleValue(CSSPrimitiveValue::CSS_NUMBER);
686             if (!position) {
687                 result.appendLiteral("from(");
688                 result.append(stop.m_color->cssText());
689                 result.append(')');
690             } else if (position == 1) {
691                 result.appendLiteral("to(");
692                 result.append(stop.m_color->cssText());
693                 result.append(')');
694             } else {
695                 result.appendLiteral("color-stop(");
696                 result.appendFixedPrecisionNumber(position);
697                 result.appendLiteral(", ");
698                 result.append(stop.m_color->cssText());
699                 result.append(')');
700             }
701         }
702     } else if (m_gradientType == CSSPrefixedLinearGradient) {
703         if (m_repeating)
704             result.appendLiteral("-webkit-repeating-linear-gradient(");
705         else
706             result.appendLiteral("-webkit-linear-gradient(");
707
708         if (m_angle)
709             result.append(m_angle->cssText());
710         else {
711             if (m_firstX && m_firstY) {
712                 result.append(m_firstX->cssText());
713                 result.append(' ');
714                 result.append(m_firstY->cssText());
715             } else if (m_firstX || m_firstY) {
716                 if (m_firstX)
717                     result.append(m_firstX->cssText());
718
719                 if (m_firstY)
720                     result.append(m_firstY->cssText());
721             }
722         }
723
724         for (unsigned i = 0; i < m_stops.size(); i++) {
725             auto& stop = m_stops[i];
726             result.appendLiteral(", ");
727             result.append(stop.m_color->cssText());
728             if (stop.m_position) {
729                 result.append(' ');
730                 result.append(stop.m_position->cssText());
731             }
732         }
733     } else {
734         if (m_repeating)
735             result.appendLiteral("repeating-linear-gradient(");
736         else
737             result.appendLiteral("linear-gradient(");
738
739         bool wroteSomething = false;
740
741         if (m_angle && m_angle->computeDegrees() != 180) {
742             result.append(m_angle->cssText());
743             wroteSomething = true;
744         } else if ((m_firstX || m_firstY) && !(!m_firstX && m_firstY && m_firstY->valueID() == CSSValueBottom)) {
745             result.appendLiteral("to ");
746             if (m_firstX && m_firstY) {
747                 result.append(m_firstX->cssText());
748                 result.append(' ');
749                 result.append(m_firstY->cssText());
750             } else if (m_firstX)
751                 result.append(m_firstX->cssText());
752             else
753                 result.append(m_firstY->cssText());
754             wroteSomething = true;
755         }
756
757         if (wroteSomething)
758             result.appendLiteral(", ");
759
760         for (unsigned i = 0; i < m_stops.size(); i++) {
761             const CSSGradientColorStop& stop = m_stops[i];
762             if (i)
763                 result.appendLiteral(", ");
764             if (!stop.isMidpoint)
765                 result.append(stop.m_color->cssText());
766             if (stop.m_position) {
767                 if (!stop.isMidpoint)
768                     result.append(' ');
769                 result.append(stop.m_position->cssText());
770             }
771         }
772         
773     }
774
775     result.append(')');
776     return result.toString();
777 }
778
779 // Compute the endpoints so that a gradient of the given angle covers a box of the given size.
780 static void endPointsFromAngle(float angleDeg, const FloatSize& size, FloatPoint& firstPoint, FloatPoint& secondPoint, CSSGradientType type)
781 {
782     // Prefixed gradients use "polar coordinate" angles, rather than "bearing" angles.
783     if (type == CSSPrefixedLinearGradient)
784         angleDeg = 90 - angleDeg;
785
786     angleDeg = fmodf(angleDeg, 360);
787     if (angleDeg < 0)
788         angleDeg += 360;
789
790     if (!angleDeg) {
791         firstPoint.set(0, size.height());
792         secondPoint.set(0, 0);
793         return;
794     }
795
796     if (angleDeg == 90) {
797         firstPoint.set(0, 0);
798         secondPoint.set(size.width(), 0);
799         return;
800     }
801
802     if (angleDeg == 180) {
803         firstPoint.set(0, 0);
804         secondPoint.set(0, size.height());
805         return;
806     }
807
808     if (angleDeg == 270) {
809         firstPoint.set(size.width(), 0);
810         secondPoint.set(0, 0);
811         return;
812     }
813
814     // angleDeg is a "bearing angle" (0deg = N, 90deg = E),
815     // but tan expects 0deg = E, 90deg = N.
816     float slope = tan(deg2rad(90 - angleDeg));
817
818     // We find the endpoint by computing the intersection of the line formed by the slope,
819     // and a line perpendicular to it that intersects the corner.
820     float perpendicularSlope = -1 / slope;
821
822     // Compute start corner relative to center, in Cartesian space (+y = up).
823     float halfHeight = size.height() / 2;
824     float halfWidth = size.width() / 2;
825     FloatPoint endCorner;
826     if (angleDeg < 90)
827         endCorner.set(halfWidth, halfHeight);
828     else if (angleDeg < 180)
829         endCorner.set(halfWidth, -halfHeight);
830     else if (angleDeg < 270)
831         endCorner.set(-halfWidth, -halfHeight);
832     else
833         endCorner.set(-halfWidth, halfHeight);
834
835     // Compute c (of y = mx + c) using the corner point.
836     float c = endCorner.y() - perpendicularSlope * endCorner.x();
837     float endX = c / (slope - perpendicularSlope);
838     float endY = perpendicularSlope * endX + c;
839
840     // We computed the end point, so set the second point, 
841     // taking into account the moved origin and the fact that we're in drawing space (+y = down).
842     secondPoint.set(halfWidth + endX, halfHeight - endY);
843     // Reflect around the center for the start point.
844     firstPoint.set(halfWidth - endX, halfHeight + endY);
845 }
846
847 Ref<Gradient> CSSLinearGradientValue::createGradient(RenderElement& renderer, const FloatSize& size)
848 {
849     ASSERT(!size.isEmpty());
850
851     CSSToLengthConversionData conversionData(&renderer.style(), renderer.document().documentElement()->renderStyle(), &renderer.view());
852
853     FloatPoint firstPoint;
854     FloatPoint secondPoint;
855     if (m_angle) {
856         float angle = m_angle->floatValue(CSSPrimitiveValue::CSS_DEG);
857         endPointsFromAngle(angle, size, firstPoint, secondPoint, m_gradientType);
858     } else {
859         switch (m_gradientType) {
860         case CSSDeprecatedLinearGradient:
861             firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
862             if (m_secondX || m_secondY)
863                 secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), conversionData, size);
864             else {
865                 if (m_firstX)
866                     secondPoint.setX(size.width() - firstPoint.x());
867                 if (m_firstY)
868                     secondPoint.setY(size.height() - firstPoint.y());
869             }
870             break;
871         case CSSPrefixedLinearGradient:
872             firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
873             if (m_firstX)
874                 secondPoint.setX(size.width() - firstPoint.x());
875             if (m_firstY)
876                 secondPoint.setY(size.height() - firstPoint.y());
877             break;
878         case CSSLinearGradient:
879             if (m_firstX && m_firstY) {
880                 // "Magic" corners, so the 50% line touches two corners.
881                 float rise = size.width();
882                 float run = size.height();
883                 if (m_firstX && m_firstX->valueID() == CSSValueLeft)
884                     run *= -1;
885                 if (m_firstY && m_firstY->valueID() == CSSValueBottom)
886                     rise *= -1;
887                 // Compute angle, and flip it back to "bearing angle" degrees.
888                 float angle = 90 - rad2deg(atan2(rise, run));
889                 endPointsFromAngle(angle, size, firstPoint, secondPoint, m_gradientType);
890             } else if (m_firstX || m_firstY) { 
891                 secondPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
892                 if (m_firstX)
893                     firstPoint.setX(size.width() - secondPoint.x());
894                 if (m_firstY)
895                     firstPoint.setY(size.height() - secondPoint.y());
896             } else
897                 secondPoint.setY(size.height());
898             break;
899         default:
900             ASSERT_NOT_REACHED();
901         }
902     }
903
904     Gradient::LinearData data { firstPoint, secondPoint };
905     LinearGradientAdapter adapter { data };
906     auto stops = computeStops(adapter, conversionData, renderer.style(), 1);
907
908     auto gradient = Gradient::create(WTFMove(data));
909     gradient->setSortedColorStops(WTFMove(stops));
910     return gradient;
911 }
912
913 bool CSSLinearGradientValue::equals(const CSSLinearGradientValue& other) const
914 {
915     if (m_gradientType == CSSDeprecatedLinearGradient)
916         return other.m_gradientType == m_gradientType
917             && compareCSSValuePtr(m_firstX, other.m_firstX)
918             && compareCSSValuePtr(m_firstY, other.m_firstY)
919             && compareCSSValuePtr(m_secondX, other.m_secondX)
920             && compareCSSValuePtr(m_secondY, other.m_secondY)
921             && m_stops == other.m_stops;
922
923     if (m_repeating != other.m_repeating)
924         return false;
925
926     if (m_angle)
927         return compareCSSValuePtr(m_angle, other.m_angle) && m_stops == other.m_stops;
928
929     if (other.m_angle)
930         return false;
931
932     bool equalXandY = false;
933     if (m_firstX && m_firstY)
934         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
935     else if (m_firstX)
936         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
937     else if (m_firstY)
938         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
939     else
940         equalXandY = !other.m_firstX && !other.m_firstY;
941
942     return equalXandY && m_stops == other.m_stops;
943 }
944
945 String CSSRadialGradientValue::customCSSText() const
946 {
947     StringBuilder result;
948
949     if (m_gradientType == CSSDeprecatedRadialGradient) {
950         result.appendLiteral("-webkit-gradient(radial, ");
951         result.append(m_firstX->cssText());
952         result.append(' ');
953         result.append(m_firstY->cssText());
954         result.appendLiteral(", ");
955         result.append(m_firstRadius->cssText());
956         result.appendLiteral(", ");
957         result.append(m_secondX->cssText());
958         result.append(' ');
959         result.append(m_secondY->cssText());
960         result.appendLiteral(", ");
961         result.append(m_secondRadius->cssText());
962
963         // FIXME: share?
964         for (auto& stop : m_stops) {
965             result.appendLiteral(", ");
966             auto position = stop.m_position->doubleValue(CSSPrimitiveValue::CSS_NUMBER);
967             if (!position) {
968                 result.appendLiteral("from(");
969                 result.append(stop.m_color->cssText());
970                 result.append(')');
971             } else if (position == 1) {
972                 result.appendLiteral("to(");
973                 result.append(stop.m_color->cssText());
974                 result.append(')');
975             } else {
976                 result.appendLiteral("color-stop(");
977                 result.appendFixedPrecisionNumber(position);
978                 result.appendLiteral(", ");
979                 result.append(stop.m_color->cssText());
980                 result.append(')');
981             }
982         }
983     } else if (m_gradientType == CSSPrefixedRadialGradient) {
984         if (m_repeating)
985             result.appendLiteral("-webkit-repeating-radial-gradient(");
986         else
987             result.appendLiteral("-webkit-radial-gradient(");
988
989         if (m_firstX && m_firstY) {
990             result.append(m_firstX->cssText());
991             result.append(' ');
992             result.append(m_firstY->cssText());
993         } else if (m_firstX)
994             result.append(m_firstX->cssText());
995          else if (m_firstY)
996             result.append(m_firstY->cssText());
997         else
998             result.appendLiteral("center");
999
1000         if (m_shape || m_sizingBehavior) {
1001             result.appendLiteral(", ");
1002             if (m_shape) {
1003                 result.append(m_shape->cssText());
1004                 result.append(' ');
1005             } else
1006                 result.appendLiteral("ellipse ");
1007
1008             if (m_sizingBehavior)
1009                 result.append(m_sizingBehavior->cssText());
1010             else
1011                 result.appendLiteral("cover");
1012
1013         } else if (m_endHorizontalSize && m_endVerticalSize) {
1014             result.appendLiteral(", ");
1015             result.append(m_endHorizontalSize->cssText());
1016             result.append(' ');
1017             result.append(m_endVerticalSize->cssText());
1018         }
1019
1020         for (unsigned i = 0; i < m_stops.size(); i++) {
1021             const CSSGradientColorStop& stop = m_stops[i];
1022             result.appendLiteral(", ");
1023             result.append(stop.m_color->cssText());
1024             if (stop.m_position) {
1025                 result.append(' ');
1026                 result.append(stop.m_position->cssText());
1027             }
1028         }
1029     } else {
1030         if (m_repeating)
1031             result.appendLiteral("repeating-radial-gradient(");
1032         else
1033             result.appendLiteral("radial-gradient(");
1034
1035         bool wroteSomething = false;
1036
1037         // The only ambiguous case that needs an explicit shape to be provided
1038         // is when a sizing keyword is used (or all sizing is omitted).
1039         if (m_shape && m_shape->valueID() != CSSValueEllipse && (m_sizingBehavior || (!m_sizingBehavior && !m_endHorizontalSize))) {
1040             result.appendLiteral("circle");
1041             wroteSomething = true;
1042         }
1043
1044         if (m_sizingBehavior && m_sizingBehavior->valueID() != CSSValueFarthestCorner) {
1045             if (wroteSomething)
1046                 result.append(' ');
1047             result.append(m_sizingBehavior->cssText());
1048             wroteSomething = true;
1049         } else if (m_endHorizontalSize) {
1050             if (wroteSomething)
1051                 result.append(' ');
1052             result.append(m_endHorizontalSize->cssText());
1053             if (m_endVerticalSize) {
1054                 result.append(' ');
1055                 result.append(m_endVerticalSize->cssText());
1056             }
1057             wroteSomething = true;
1058         }
1059
1060         if (m_firstX || m_firstY) {
1061             if (wroteSomething)
1062                 result.append(' ');
1063             result.appendLiteral("at ");
1064             if (m_firstX && m_firstY) {
1065                 result.append(m_firstX->cssText());
1066                 result.append(' ');
1067                 result.append(m_firstY->cssText());
1068             } else if (m_firstX)
1069                 result.append(m_firstX->cssText());
1070             else
1071                 result.append(m_firstY->cssText());
1072             wroteSomething = true;
1073         }
1074
1075         if (wroteSomething)
1076             result.appendLiteral(", ");
1077
1078         for (unsigned i = 0; i < m_stops.size(); i++) {
1079             const CSSGradientColorStop& stop = m_stops[i];
1080             if (i)
1081                 result.appendLiteral(", ");
1082             if (!stop.isMidpoint)
1083                 result.append(stop.m_color->cssText());
1084             if (stop.m_position) {
1085                 if (!stop.isMidpoint)
1086                     result.append(' ');
1087                 result.append(stop.m_position->cssText());
1088             }
1089         }
1090
1091     }
1092
1093     result.append(')');
1094     return result.toString();
1095 }
1096
1097 float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue& radius, const CSSToLengthConversionData& conversionData, float* widthOrHeight)
1098 {
1099     float result = 0;
1100     if (radius.isNumber()) // Can the radius be a percentage?
1101         result = radius.floatValue() * conversionData.zoom();
1102     else if (widthOrHeight && radius.isPercentage())
1103         result = *widthOrHeight * radius.floatValue() / 100;
1104     else
1105         result = radius.computeLength<float>(conversionData);
1106
1107     return result;
1108 }
1109
1110 static float distanceToClosestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
1111 {
1112     FloatPoint topLeft;
1113     float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
1114
1115     FloatPoint topRight(size.width(), 0);
1116     float topRightDistance = FloatSize(p - topRight).diagonalLength();
1117
1118     FloatPoint bottomLeft(0, size.height());
1119     float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
1120
1121     FloatPoint bottomRight(size.width(), size.height());
1122     float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
1123
1124     corner = topLeft;
1125     float minDistance = topLeftDistance;
1126     if (topRightDistance < minDistance) {
1127         minDistance = topRightDistance;
1128         corner = topRight;
1129     }
1130
1131     if (bottomLeftDistance < minDistance) {
1132         minDistance = bottomLeftDistance;
1133         corner = bottomLeft;
1134     }
1135
1136     if (bottomRightDistance < minDistance) {
1137         minDistance = bottomRightDistance;
1138         corner = bottomRight;
1139     }
1140     return minDistance;
1141 }
1142
1143 static float distanceToFarthestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
1144 {
1145     FloatPoint topLeft;
1146     float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
1147
1148     FloatPoint topRight(size.width(), 0);
1149     float topRightDistance = FloatSize(p - topRight).diagonalLength();
1150
1151     FloatPoint bottomLeft(0, size.height());
1152     float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
1153
1154     FloatPoint bottomRight(size.width(), size.height());
1155     float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
1156
1157     corner = topLeft;
1158     float maxDistance = topLeftDistance;
1159     if (topRightDistance > maxDistance) {
1160         maxDistance = topRightDistance;
1161         corner = topRight;
1162     }
1163
1164     if (bottomLeftDistance > maxDistance) {
1165         maxDistance = bottomLeftDistance;
1166         corner = bottomLeft;
1167     }
1168
1169     if (bottomRightDistance > maxDistance) {
1170         maxDistance = bottomRightDistance;
1171         corner = bottomRight;
1172     }
1173     return maxDistance;
1174 }
1175
1176 // Compute horizontal radius of ellipse with center at 0,0 which passes through p, and has
1177 // width/height given by aspectRatio.
1178 static inline float horizontalEllipseRadius(const FloatSize& p, float aspectRatio)
1179 {
1180     // x^2/a^2 + y^2/b^2 = 1
1181     // a/b = aspectRatio, b = a/aspectRatio
1182     // a = sqrt(x^2 + y^2/(1/r^2))
1183     return sqrtf(p.width() * p.width() + (p.height() * p.height()) / (1 / (aspectRatio * aspectRatio)));
1184 }
1185
1186 // FIXME: share code with the linear version
1187 Ref<Gradient> CSSRadialGradientValue::createGradient(RenderElement& renderer, const FloatSize& size)
1188 {
1189     ASSERT(!size.isEmpty());
1190
1191     CSSToLengthConversionData conversionData(&renderer.style(), renderer.document().documentElement()->renderStyle(), &renderer.view());
1192
1193     FloatPoint firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
1194     if (!m_firstX)
1195         firstPoint.setX(size.width() / 2);
1196     if (!m_firstY)
1197         firstPoint.setY(size.height() / 2);
1198
1199     FloatPoint secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), conversionData, size);
1200     if (!m_secondX)
1201         secondPoint.setX(size.width() / 2);
1202     if (!m_secondY)
1203         secondPoint.setY(size.height() / 2);
1204
1205     float firstRadius = 0;
1206     if (m_firstRadius)
1207         firstRadius = resolveRadius(*m_firstRadius, conversionData);
1208
1209     float secondRadius = 0;
1210     float aspectRatio = 1; // width / height.
1211     if (m_secondRadius)
1212         secondRadius = resolveRadius(*m_secondRadius, conversionData);
1213     else if (m_endHorizontalSize) {
1214         float width = size.width();
1215         float height = size.height();
1216         secondRadius = resolveRadius(*m_endHorizontalSize, conversionData, &width);
1217         if (m_endVerticalSize)
1218             aspectRatio = secondRadius / resolveRadius(*m_endVerticalSize, conversionData, &height);
1219         else
1220             aspectRatio = 1;
1221     } else {
1222         enum GradientShape { Circle, Ellipse };
1223         GradientShape shape = Ellipse;
1224         if ((m_shape && m_shape->valueID() == CSSValueCircle)
1225             || (!m_shape && !m_sizingBehavior && m_endHorizontalSize && !m_endVerticalSize))
1226             shape = Circle;
1227
1228         enum GradientFill { ClosestSide, ClosestCorner, FarthestSide, FarthestCorner };
1229         GradientFill fill = FarthestCorner;
1230
1231         switch (m_sizingBehavior ? m_sizingBehavior->valueID() : 0) {
1232         case CSSValueContain:
1233         case CSSValueClosestSide:
1234             fill = ClosestSide;
1235             break;
1236         case CSSValueClosestCorner:
1237             fill = ClosestCorner;
1238             break;
1239         case CSSValueFarthestSide:
1240             fill = FarthestSide;
1241             break;
1242         case CSSValueCover:
1243         case CSSValueFarthestCorner:
1244             fill = FarthestCorner;
1245             break;
1246         default:
1247             break;
1248         }
1249
1250         // Now compute the end radii based on the second point, shape and fill.
1251
1252         // Horizontal
1253         switch (fill) {
1254         case ClosestSide: {
1255             float xDist = std::min(secondPoint.x(), size.width() - secondPoint.x());
1256             float yDist = std::min(secondPoint.y(), size.height() - secondPoint.y());
1257             if (shape == Circle) {
1258                 float smaller = std::min(xDist, yDist);
1259                 xDist = smaller;
1260                 yDist = smaller;
1261             }
1262             secondRadius = xDist;
1263             aspectRatio = xDist / yDist;
1264             break;
1265         }
1266         case FarthestSide: {
1267             float xDist = std::max(secondPoint.x(), size.width() - secondPoint.x());
1268             float yDist = std::max(secondPoint.y(), size.height() - secondPoint.y());
1269             if (shape == Circle) {
1270                 float larger = std::max(xDist, yDist);
1271                 xDist = larger;
1272                 yDist = larger;
1273             }
1274             secondRadius = xDist;
1275             aspectRatio = xDist / yDist;
1276             break;
1277         }
1278         case ClosestCorner: {
1279             FloatPoint corner;
1280             float distance = distanceToClosestCorner(secondPoint, size, corner);
1281             if (shape == Circle)
1282                 secondRadius = distance;
1283             else {
1284                 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
1285                 // that it would if closest-side or farthest-side were specified, as appropriate.
1286                 float xDist = std::min(secondPoint.x(), size.width() - secondPoint.x());
1287                 float yDist = std::min(secondPoint.y(), size.height() - secondPoint.y());
1288
1289                 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
1290                 aspectRatio = xDist / yDist;
1291             }
1292             break;
1293         }
1294
1295         case FarthestCorner: {
1296             FloatPoint corner;
1297             float distance = distanceToFarthestCorner(secondPoint, size, corner);
1298             if (shape == Circle)
1299                 secondRadius = distance;
1300             else {
1301                 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
1302                 // that it would if closest-side or farthest-side were specified, as appropriate.
1303                 float xDist = std::max(secondPoint.x(), size.width() - secondPoint.x());
1304                 float yDist = std::max(secondPoint.y(), size.height() - secondPoint.y());
1305
1306                 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
1307                 aspectRatio = xDist / yDist;
1308             }
1309             break;
1310         }
1311         }
1312     }
1313
1314     // computeStops() only uses maxExtent for repeating gradients.
1315     float maxExtent = 0;
1316     if (m_repeating) {
1317         FloatPoint corner;
1318         maxExtent = distanceToFarthestCorner(secondPoint, size, corner);
1319     }
1320
1321     Gradient::RadialData data { firstPoint, secondPoint, firstRadius, secondRadius, aspectRatio };
1322     RadialGradientAdapter adapter { data };
1323     auto stops = computeStops(adapter, conversionData, renderer.style(), maxExtent);
1324
1325     auto gradient = Gradient::create(WTFMove(data));
1326     gradient->setSortedColorStops(WTFMove(stops));
1327     return gradient;
1328 }
1329
1330 bool CSSRadialGradientValue::equals(const CSSRadialGradientValue& other) const
1331 {
1332     if (m_gradientType == CSSDeprecatedRadialGradient)
1333         return other.m_gradientType == m_gradientType
1334             && compareCSSValuePtr(m_firstX, other.m_firstX)
1335             && compareCSSValuePtr(m_firstY, other.m_firstY)
1336             && compareCSSValuePtr(m_secondX, other.m_secondX)
1337             && compareCSSValuePtr(m_secondY, other.m_secondY)
1338             && compareCSSValuePtr(m_firstRadius, other.m_firstRadius)
1339             && compareCSSValuePtr(m_secondRadius, other.m_secondRadius)
1340             && m_stops == other.m_stops;
1341
1342     if (m_repeating != other.m_repeating)
1343         return false;
1344
1345     bool equalXandY = false;
1346     if (m_firstX && m_firstY)
1347         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
1348     else if (m_firstX)
1349         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
1350     else if (m_firstY)
1351         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
1352     else
1353         equalXandY = !other.m_firstX && !other.m_firstY;
1354
1355     if (!equalXandY)
1356         return false;
1357
1358     bool equalShape = true;
1359     bool equalSizingBehavior = true;
1360     bool equalHorizontalAndVerticalSize = true;
1361
1362     if (m_shape)
1363         equalShape = compareCSSValuePtr(m_shape, other.m_shape);
1364     else if (m_sizingBehavior)
1365         equalSizingBehavior = compareCSSValuePtr(m_sizingBehavior, other.m_sizingBehavior);
1366     else if (m_endHorizontalSize && m_endVerticalSize)
1367         equalHorizontalAndVerticalSize = compareCSSValuePtr(m_endHorizontalSize, other.m_endHorizontalSize) && compareCSSValuePtr(m_endVerticalSize, other.m_endVerticalSize);
1368     else {
1369         equalShape = !other.m_shape;
1370         equalSizingBehavior = !other.m_sizingBehavior;
1371         equalHorizontalAndVerticalSize = !other.m_endHorizontalSize && !other.m_endVerticalSize;
1372     }
1373     return equalShape && equalSizingBehavior && equalHorizontalAndVerticalSize && m_stops == other.m_stops;
1374 }
1375
1376
1377 String CSSConicGradientValue::customCSSText() const
1378 {
1379     StringBuilder result;
1380
1381     if (m_repeating)
1382         result.appendLiteral("repeating-conic-gradient(");
1383     else
1384         result.appendLiteral("conic-gradient(");
1385
1386     bool wroteSomething = false;
1387
1388     if (m_angle) {
1389         result.appendLiteral("from ");
1390         result.append(m_angle->cssText());
1391         wroteSomething = true;
1392     }
1393
1394     if (m_firstX && m_firstY) {
1395         if (wroteSomething)
1396             result.appendLiteral(" ");
1397         result.appendLiteral("at ");
1398         result.append(m_firstX->cssText());
1399         result.append(' ');
1400         result.append(m_firstY->cssText());
1401         wroteSomething = true;
1402     }
1403
1404     if (wroteSomething)
1405         result.appendLiteral(", ");
1406
1407     bool wroteFirstStop = false;
1408     for (auto& stop : m_stops) {
1409         if (wroteFirstStop)
1410             result.appendLiteral(", ");
1411         wroteFirstStop = true;
1412         if (!stop.isMidpoint)
1413             result.append(stop.m_color->cssText());
1414         if (stop.m_position) {
1415             if (!stop.isMidpoint)
1416                 result.append(' ');
1417             result.append(stop.m_position->cssText());
1418         }
1419     }
1420     
1421     result.append(')');
1422     return result.toString();
1423 }
1424
1425 Ref<Gradient> CSSConicGradientValue::createGradient(RenderElement& renderer, const FloatSize& size)
1426 {
1427     ASSERT(!size.isEmpty());
1428
1429     CSSToLengthConversionData conversionData(&renderer.style(), renderer.document().documentElement()->renderStyle(), &renderer.view());
1430
1431     FloatPoint centerPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
1432     if (!m_firstX)
1433         centerPoint.setX(size.width() / 2);
1434     if (!m_firstY)
1435         centerPoint.setY(size.height() / 2);
1436
1437     float angleRadians = 0;
1438     if (m_angle)
1439         angleRadians = m_angle->floatValue(CSSPrimitiveValue::CSS_RAD);
1440
1441     Gradient::ConicData data { centerPoint, angleRadians };
1442     ConicGradientAdapter adapter;
1443     auto stops = computeStops(adapter, conversionData, renderer.style(), 1);
1444
1445     auto gradient = Gradient::create(WTFMove(data));
1446     gradient->setSortedColorStops(WTFMove(stops));
1447     return gradient;
1448 }
1449
1450 bool CSSConicGradientValue::equals(const CSSConicGradientValue& other) const
1451 {
1452     if (m_repeating != other.m_repeating)
1453         return false;
1454
1455     if (!compareCSSValuePtr(m_angle, other.m_angle))
1456         return false;
1457
1458     bool equalXandY = false;
1459     if (m_firstX && m_firstY)
1460         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
1461     else if (m_firstX)
1462         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
1463     else if (m_firstY)
1464         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
1465     else
1466         equalXandY = !other.m_firstX && !other.m_firstY;
1467
1468     return equalXandY && m_stops == other.m_stops;
1469 }
1470
1471 } // namespace WebCore