Ignore:
Timestamp:
Mar 5, 2020, 8:11:39 PM (5 years ago)
Author:
Darin Adler
Message:

Simplify gradient parsing
https://bugs.webkit.org/show_bug.cgi?id=208417

Reviewed by Anders Carlsson.

  • Use Optional<> and invalid Color to represent unspecified positions and colors. This is simpler and easier to get right than separate booleans.
  • Simplified sorting of stops in legacy gradients to remove extra CSS value evaluation and unnecessary "sort in place" technique.
  • Rewrote equals functions for CSS gradient value classes. The new pattern is to compare all the data members that hold parsed CSS data, handling null correctly, since the parser won't set inappropriate ones. The old code had complex logic to only compare certain data members, which was unnecessary and hard to read to tell if it was correct.
  • Added some more use of WTFMove to cut down on reference count churn.
  • css/CSSGradientValue.cpp:

(WebCore::CSSGradientValue::image): Removed unneeded call to get().
(WebCore::compareStops): Deleted.
(WebCore::CSSGradientValue::sortStopsIfNeeded): Deleted.
(WebCore::resolveStopColors): Take advantage of the fact that we know because
of parsing rules that the only stops without colors are midpoints to drastically
simplify this function to a trivial loop.
(WebCore::CSSGradientValue::hasColorDerivedFromElement const): Added.
Checks to see if any of the stop colors is derived from the element. The old
code confusingly would store the answer to this in the stop, but only in the
first stop with this property. Computing it without modifying the stop, and
memoizing it in the gradient preserves the same performance characteristics
as before without requiring a boolean in each stop in the stops vector.
(WebCore::CSSGradientValue::gradientWithStylesResolved): Call the new
hasColorDerivedFromElement function instead of having the logic here.
(WebCore::LinearGradientAdapter::normalizeStopsAndEndpointsOutsideRange):
Update since GradientStop now has optional offsets. By the time this
function is called they are all guaranteed to be filled in, so we can
just use the * operator.
(WebCore::RadialGradientAdapter::normalizeStopsAndEndpointsOutsideRange):
Ditto.
(WebCore::ConicGradientAdapter::normalizeStopsAndEndpointsOutsideRange):
Ditto.
(WebCore::CSSGradientValue::computeStops): Moved the sorting of stops for
the deprecated gradients here. Also updated since Gradient::ColorStop
no longer uses "m_" prefixes on its public struct data members. Some
simplification because we no longer need to explicitly set "specified"
to true since it's no longer a separate boolean.
(WebCore::positionFromValue): Handle a null pointer for value by returning
0, which is what the caller was doing explicitly before. Use float
instead of int for some internal computations that were mixing the two
for no good reason.
(WebCore::computeEndPoint): Removed null checks now that positionFromValue
does them for us, turning this into a one-liner.
(WebCore::CSSGradientValue::isCacheable const): Use hasColorDerivedFromElement.
(WebCore::CSSGradientValue::knownToBeOpaque const): Removed unnnecessary
checking the color both before and after when a color filter is involved.
(WebCore::CSSGradientValue::equals const): Added. Shared by all the equals
functions for derived classes.
(WebCore::appendGradientStops): Updated for changes to CSSGradientColorStop.
(WebCore::appendSpaceSeparatedOptionalCSSPtrText): Added template helper
for writing two optional CSS values with a space between.
(WebCore::writeColorStop): Ditto. Also converted to non-member function,
removed unneeded isMidpoint check, use appendSpaceSeparatedOptionalCSSPtrText.
(WebCore::CSSLinearGradientValue::customCSSText const): Call function
members so we don't have to expose CSSGradientValue data members as
protected things that can be accessed by derived classes. Some other
small refactoring, such as getting rid of extra boolean wroteFirstStop.
(WebCore::CSSLinearGradientValue::createGradient): Updated to use
function members instead of protected data members.
(WebCore::CSSLinearGradientValue::equals const): Compare all data
members and use CSSGradientValue::equals, makes this a 1-liner.
(WebCore::CSSRadialGradientValue::customCSSText const): Call function
members as described above and use appendSpaceSeparatedOptionalCSSPtrText.
(WebCore::CSSRadialGradientValue::createGradient): Ditto.
(WebCore::CSSRadialGradientValue::equals const): Compare all data
members and use CSSGradientValue::equals.
(WebCore::CSSConicGradientValue::customCSSText const): Call function
members as described above and use appendSpaceSeparatedOptionalCSSPtrText.
(WebCore::CSSConicGradientValue::createGradient): Ditto.
(WebCore::CSSConicGradientValue::equals const): Compare all data
members and use CSSGradientValue::equals, makes this a 1-liner.

  • css/CSSGradientValue.h: Removed unneeded includes and forward declarations.

Renamed CSSGradientColorStop data members to not use m_ prefix since this is
a struct with public data members, and WebKit style says not to do that here.
Removed m_colorIsDerivedFromElement and isMidpoint from CSSGradientColorStop,
m_colorIsDerivedFromElement is now stored in the gradient, not the color stop,
and midpoints are any color stop with null color. Replaced the
CSSGradientValue::stopCount function, which mixed size_t and unsigned types,
with a hasTwoStops function, which is all the caller needs. Converted the
isFixedSize, fixedSize, isPending, and loadSubimages into static member
functions: they don't do any work and so don't need an instance. Removed
the unneeded gradient type argument to the cloning constructors. Removed
m_stopsSorted and added m_hasColorDerivedFromElement and
hasColorDerivedFromElement. Added getter functions that are protected so
the data members themselves can be private. Removed sortStopsIfNeeded
and writeColorStop.

  • css/parser/CSSPropertyParserHelpers.cpp:

(WebCore::CSSPropertyParserHelpers::consumeDeprecatedGradientColorStop):
Updated for CSSGradientColorStop member renaming.
(WebCore::CSSPropertyParserHelpers::consumeDeprecatedGradient): Use
more WTFMove to save a little bit of reference count churn; in some cases
that means moving the setter calls to the end of the function after all
the error checking.
(WebCore::CSSPropertyParserHelpers::consumeGradientColorStops): Ditto.
Removed code to set isMidpoint and the FIXME-NEWPARSER comment that said
it could be removed. Used lambda to cut down on repeated code. Changed
parsing of stops with a second position to repeat the color instead of
relying on later computation to repeat it; this is required so we can
always treat an omitted color as a midpoint.
(WebCore::CSSPropertyParserHelpers::consumeDeprecatedRadialGradient): Ditto.
(WebCore::CSSPropertyParserHelpers::consumeRadialGradient): Ditto.
(WebCore::CSSPropertyParserHelpers::consumeLinearGradient): Ditto.
(WebCore::CSSPropertyParserHelpers::consumeConicGradient): Ditto.

  • html/HTMLInputElement.cpp:

(WebCore::autoFillStrongPasswordMaskImage): Updated for the renamed
CSSGradientColorStop members, added a missing call to doneAddingStops,
and use some WTFMove to cut down on reference count churn.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/css/CSSGradientValue.cpp

    r255559 r257966  
    6464    auto newImage = GradientImage::create(createGradient(*this, renderer, size), size);
    6565    if (cacheable)
    66         saveCachedImageForSize(size, newImage.get());
     66        saveCachedImageForSize(size, newImage);
    6767    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(CSSUnitType::CSS_NUMBER);
    74     double bVal = b.m_position->doubleValue(CSSUnitType::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     }
    8768}
    8869
    8970struct GradientStop {
    9071    Color color;
    91     float offset { 0 };
    92     bool specified { false };
    93     bool isMidpoint { false };
     72    Optional<float> offset;
     73
     74    bool isSpecified() const { return offset.hasValue(); }
     75    bool isMidpoint() const { return !color.isValid(); }
    9476};
    9577
     
    10486}
    10587
    106 template<typename Function>
    107 void resolveStopColors(Vector<CSSGradientColorStop, 2>& stops, Function&& colorResolveFunction)
    108 {
    109     for (size_t i = 0; i < stops.size(); ++i) {
    110         auto& stop = stops[i];
    111         if (stop.isMidpoint)
    112             continue;
    113         if (stop.m_color)
    114             stop.m_resolvedColor = colorResolveFunction(*stop.m_color);
    115         else if (i) {
    116             auto& previousStop = stops[i - 1];
    117             ASSERT(previousStop.m_color);
    118             stop.m_color = previousStop.m_color;
    119             stop.m_resolvedColor = previousStop.m_resolvedColor;
    120         }
    121     }
     88template<typename Function> void resolveStopColors(Vector<CSSGradientColorStop, 2>& stops, Function&& colorResolveFunction)
     89{
     90    for (auto& stop : stops) {
     91        if (stop.color)
     92            stop.resolvedColor = colorResolveFunction(*stop.color);
     93    }
     94}
     95
     96bool CSSGradientValue::hasColorDerivedFromElement() const
     97{
     98    if (!m_hasColorDerivedFromElement) {
     99        m_hasColorDerivedFromElement = false;
     100        for (auto& stop : m_stops) {
     101            if (stop.color && Style::BuilderState::isColorFromPrimitiveValueDerivedFromElement(*stop.color)) {
     102                m_hasColorDerivedFromElement = true;
     103                break;
     104            }
     105        }
     106    }
     107    return *m_hasColorDerivedFromElement;
    122108}
    123109
    124110Ref<CSSGradientValue> CSSGradientValue::gradientWithStylesResolved(Style::BuilderState& builderState)
    125111{
    126     bool colorIsDerivedFromElement = false;
    127     for (auto& stop : m_stops) {
    128         if (!stop.isMidpoint && stop.m_color && Style::BuilderState::isColorFromPrimitiveValueDerivedFromElement(*stop.m_color)) {
    129             stop.m_colorIsDerivedFromElement = true;
    130             colorIsDerivedFromElement = true;
    131             break;
    132         }
    133     }
    134     auto result = colorIsDerivedFromElement ? clone(*this) : makeRef(*this);
     112    auto result = hasColorDerivedFromElement() ? clone(*this) : makeRef(*this);
    135113    resolveStopColors(result->m_stops, [&](const CSSPrimitiveValue& colorValue) {
    136114        return builderState.colorFromPrimitiveValue(colorValue);
     
    153131    {
    154132    }
    155    
     133
    156134    float gradientLength() const
    157135    {
     
    163141    void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
    164142    {
    165         float firstOffset = stops.first().offset;
    166         float lastOffset = stops.last().offset;
     143        float firstOffset = *stops.first().offset;
     144        float lastOffset = *stops.last().offset;
    167145        if (firstOffset != lastOffset) {
    168146            float scale = lastOffset - firstOffset;
    169147
    170148            for (auto& stop : stops)
    171                 stop.offset = (stop.offset - firstOffset) / scale;
     149                stop.offset = (*stop.offset - firstOffset) / scale;
    172150
    173151            auto p0 = m_data.point0;
     
    210188        // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
    211189        float firstOffset = 0;
    212         float lastOffset = stops.last().offset;
     190        float lastOffset = *stops.last().offset;
    213191        float scale = lastOffset - firstOffset;
    214192
     
    216194        size_t firstZeroOrGreaterIndex = numStops;
    217195        for (size_t i = 0; i < numStops; ++i) {
    218             if (stops[i].offset >= 0) {
     196            if (*stops[i].offset >= 0) {
    219197                firstZeroOrGreaterIndex = i;
    220198                break;
     
    223201
    224202        if (firstZeroOrGreaterIndex > 0) {
    225             if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
    226                 float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
    227                 float nextOffset = stops[firstZeroOrGreaterIndex].offset;
     203            if (firstZeroOrGreaterIndex < numStops && *stops[firstZeroOrGreaterIndex].offset > 0) {
     204                float prevOffset = *stops[firstZeroOrGreaterIndex - 1].offset;
     205                float nextOffset = *stops[firstZeroOrGreaterIndex].offset;
    228206
    229207                float interStopProportion = -prevOffset / (nextOffset - prevOffset);
     
    244222
    245223        for (auto& stop : stops)
    246             stop.offset /= scale;
     224            *stop.offset /= scale;
    247225
    248226        m_data.startRadius *= scale;
     
    265243        size_t firstZeroOrGreaterIndex = numStops;
    266244        for (size_t i = 0; i < numStops; ++i) {
    267             if (stops[i].offset >= 0) {
     245            if (*stops[i].offset >= 0) {
    268246                firstZeroOrGreaterIndex = i;
    269247                break;
     
    272250
    273251        if (firstZeroOrGreaterIndex > 0) {
    274             if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
    275                 float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
    276                 float nextOffset = stops[firstZeroOrGreaterIndex].offset;
     252            if (firstZeroOrGreaterIndex < numStops && *stops[firstZeroOrGreaterIndex].offset > 0) {
     253                float prevOffset = *stops[firstZeroOrGreaterIndex - 1].offset;
     254                float nextOffset = *stops[firstZeroOrGreaterIndex].offset;
    277255               
    278256                float interStopProportion = -prevOffset / (nextOffset - prevOffset);
     
    294272        size_t lastOneOrLessIndex = numStops;
    295273        for (int i = numStops - 1; i >= 0; --i) {
    296             if (stops[i].offset <= 1) {
     274            if (*stops[i].offset <= 1) {
    297275                lastOneOrLessIndex = i;
    298276                break;
     
    301279       
    302280        if (lastOneOrLessIndex < numStops - 1) {
    303             if (lastOneOrLessIndex < numStops && stops[lastOneOrLessIndex].offset < 1) {
    304                 float prevOffset = stops[lastOneOrLessIndex].offset;
    305                 float nextOffset = stops[lastOneOrLessIndex + 1].offset;
     281            if (lastOneOrLessIndex < numStops && *stops[lastOneOrLessIndex].offset < 1) {
     282                float prevOffset = *stops[lastOneOrLessIndex].offset;
     283                float nextOffset = *stops[lastOneOrLessIndex + 1].offset;
    306284               
    307285                float interStopProportion = (1 - prevOffset) / (nextOffset - prevOffset);
     
    327305{
    328306    if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDeprecatedRadialGradient) {
    329         sortStopsIfNeeded();
    330 
    331307        Gradient::ColorStopVector result;
    332308        result.reserveInitialCapacity(m_stops.size());
     
    334310        for (auto& stop : m_stops) {
    335311            float offset;
    336             if (stop.m_position->isPercentage())
    337                 offset = stop.m_position->floatValue(CSSUnitType::CSS_PERCENTAGE) / 100;
     312            if (stop.position->isPercentage())
     313                offset = stop.position->floatValue(CSSUnitType::CSS_PERCENTAGE) / 100;
    338314            else
    339                 offset = stop.m_position->floatValue(CSSUnitType::CSS_NUMBER);
    340 
    341             Color color = stop.m_resolvedColor;
     315                offset = stop.position->floatValue(CSSUnitType::CSS_NUMBER);
     316
     317            Color color = stop.resolvedColor;
    342318            if (style.hasAppleColorFilter())
    343319                style.appleColorFilter().transformColor(color);
     
    345321        }
    346322
     323        std::stable_sort(result.begin(), result.end(), [] (const Gradient::ColorStop& a, const Gradient::ColorStop& b) {
     324            return a.offset < b.offset;
     325        });
     326
    347327        return result;
    348328    }
     
    356336        auto& stop = m_stops[i];
    357337
    358         stops[i].isMidpoint = stop.isMidpoint;
    359 
    360         Color color = stop.m_resolvedColor;
     338        Color color = stop.resolvedColor;
    361339        if (style.hasAppleColorFilter())
    362340            style.appleColorFilter().transformColor(color);
     
    364342        stops[i].color = color;
    365343
    366         if (stop.m_position) {
    367             auto& positionValue = *stop.m_position;
     344        if (stop.position) {
     345            auto& positionValue = *stop.position;
    368346            if (positionValue.isPercentage())
    369347                stops[i].offset = positionValue.floatValue(CSSUnitType::CSS_PERCENTAGE) / 100;
     
    383361                stops[i].offset = 0;
    384362            }
    385             stops[i].specified = true;
    386363        } else {
    387364            // If the first color-stop does not have a position, its position defaults to 0%.
    388365            // If the last color-stop does not have a position, its position defaults to 100%.
    389             if (!i) {
     366            if (!i)
    390367                stops[i].offset = 0;
    391                 stops[i].specified = true;
    392             } else if (numStops > 1 && i == numStops - 1) {
     368            else if (numStops > 1 && i == numStops - 1)
    393369                stops[i].offset = 1;
    394                 stops[i].specified = true;
    395             }
    396370        }
    397371
     
    399373        // color-stop before it in the list, its position is changed to be equal to the
    400374        // largest specified position of any color-stop before it.
    401         if (stops[i].specified && i > 0) {
     375        if (stops[i].isSpecified() && i > 0) {
    402376            size_t prevSpecifiedIndex;
    403377            for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; --prevSpecifiedIndex) {
    404                 if (stops[prevSpecifiedIndex].specified)
     378                if (stops[prevSpecifiedIndex].isSpecified())
    405379                    break;
    406380            }
    407381
    408             if (stops[i].offset < stops[prevSpecifiedIndex].offset)
     382            if (*stops[i].offset < *stops[prevSpecifiedIndex].offset)
    409383                stops[i].offset = stops[prevSpecifiedIndex].offset;
    410384        }
    411385    }
    412386
    413     ASSERT(stops[0].specified && stops[numStops - 1].specified);
     387    ASSERT(stops[0].isSpecified() && stops[numStops - 1].isSpecified());
    414388
    415389    // If any color-stop still does not have a position, then, for each run of adjacent
     
    421395
    422396        for (size_t i = 0; i < numStops; ++i) {
    423             if (!stops[i].specified && !inUnspecifiedRun) {
     397            if (!stops[i].isSpecified() && !inUnspecifiedRun) {
    424398                unspecifiedRunStart = i;
    425399                inUnspecifiedRun = true;
    426             } else if (stops[i].specified && inUnspecifiedRun) {
     400            } else if (stops[i].isSpecified() && inUnspecifiedRun) {
    427401                size_t unspecifiedRunEnd = i;
    428402
    429403                if (unspecifiedRunStart < unspecifiedRunEnd) {
    430                     float lastSpecifiedOffset = stops[unspecifiedRunStart - 1].offset;
    431                     float nextSpecifiedOffset = stops[unspecifiedRunEnd].offset;
     404                    float lastSpecifiedOffset = *stops[unspecifiedRunStart - 1].offset;
     405                    float nextSpecifiedOffset = *stops[unspecifiedRunEnd].offset;
    432406                    float delta = (nextSpecifiedOffset - lastSpecifiedOffset) / (unspecifiedRunEnd - unspecifiedRunStart + 1);
    433407
     
    455429    // extra stops and generate hard lines.
    456430    for (size_t x = 1; x < stops.size() - 1;) {
    457         if (!stops[x].isMidpoint) {
     431        if (!stops[x].isMidpoint()) {
    458432            ++x;
    459433            continue;
     
    465439        Color color2 = stops[x + 1].color;
    466440        // Likewise find the position of previous and next color stop.
    467         float offset1 = stops[x - 1].offset;
    468         float offset2 = stops[x + 1].offset;
    469         float offset = stops[x].offset;
     441        float offset1 = *stops[x - 1].offset;
     442        float offset2 = *stops[x + 1].offset;
     443        float offset = *stops[x].offset;
    470444
    471445        // Check if everything coincides or the midpoint is exactly in the middle.
     
    480454            // Morph the midpoint to a regular stop with the color of the next color stop.
    481455            stops[x].color = color2;
    482             stops[x].isMidpoint = false;
    483456            continue;
    484457        }
     
    488461            // Morph the midpoint to a regular stop with the color of the previous color stop.
    489462            stops[x].color = color1;
    490             stops[x].isMidpoint = false;
    491463            continue;
    492464        }
     
    509481        // calculate colors
    510482        for (size_t y = 0; y < 9; ++y) {
    511             float relativeOffset = (newStops[y].offset - offset1) / (offset2 - offset1);
     483            float relativeOffset = (*newStops[y].offset - offset1) / (offset2 - offset1);
    512484            float multiplier = std::pow(relativeOffset, std::log(.5f) / std::log(midpoint));
    513485            // FIXME: Why not premultiply here?
     
    528500        // If the difference in the positions of the first and last color-stops is 0,
    529501        // the gradient defines a solid-color image with the color of the last color-stop in the rule.
    530         float gradientRange = stops.last().offset - stops.first().offset;
     502        float gradientRange = *stops.last().offset - *stops.first().offset;
    531503        if (!gradientRange) {
    532504            stops.first().offset = 0;
     
    541513
    542514            // Work backwards from the first, adding stops until we get one before 0.
    543             float firstOffset = stops[0].offset;
     515            float firstOffset = *stops[0].offset;
    544516            if (firstOffset > 0) {
    545517                float currOffset = firstOffset;
     
    555527
    556528                    if (srcStopOrdinal)
    557                         currOffset -= stops[originalFirstStopIndex + srcStopOrdinal].offset - stops[originalFirstStopIndex + srcStopOrdinal - 1].offset;
     529                        currOffset -= *stops[originalFirstStopIndex + srcStopOrdinal].offset - *stops[originalFirstStopIndex + srcStopOrdinal - 1].offset;
    558530                    srcStopOrdinal = (srcStopOrdinal + originalNumStops - 1) % originalNumStops;
    559531                }
     
    561533
    562534            // Work forwards from the end, adding stops until we get one after 1.
    563             float lastOffset = stops[stops.size() - 1].offset;
     535            float lastOffset = *stops[stops.size() - 1].offset;
    564536            if (lastOffset < maxExtent) {
    565537                float currOffset = lastOffset;
     
    574546                        break;
    575547                    if (srcStopOrdinal < originalNumStops - 1)
    576                         currOffset += stops[srcStopIndex + 1].offset - stops[srcStopIndex].offset;
     548                        currOffset += *stops[srcStopIndex + 1].offset - *stops[srcStopIndex].offset;
    577549                    srcStopOrdinal = (srcStopOrdinal + 1) % originalNumStops;
    578550                }
     
    582554
    583555    // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and adjusting the stops.
    584     if (stops.size() > 1 && (stops.first().offset < 0 || stops.last().offset > 1))
     556    if (stops.size() > 1 && (*stops.first().offset < 0 || *stops.last().offset > 1))
    585557        gradientAdapter.normalizeStopsAndEndpointsOutsideRange(stops);
    586558   
     
    588560    result.reserveInitialCapacity(stops.size());
    589561    for (auto& stop : stops)
    590         result.uncheckedAppend({ stop.offset, stop.color });
     562        result.uncheckedAppend({ *stop.offset, stop.color });
    591563
    592564    return result;
     
    595567static float positionFromValue(const CSSPrimitiveValue* value, const CSSToLengthConversionData& conversionData, const FloatSize& size, bool isHorizontal)
    596568{
    597     int origin = 0;
    598     int sign = 1;
    599     int edgeDistance = isHorizontal ? size.width() : size.height();
    600    
     569    if (!value)
     570        return 0;
     571
     572    float origin = 0;
     573    float sign = 1;
     574    float edgeDistance = isHorizontal ? size.width() : size.height();
     575
    601576    // In this case the center of the gradient is given relative to an edge in the
    602577    // form of: [ top | bottom | right | left ] [ <percentage> | <length> ].
    603578    if (value->isPair()) {
    604579        CSSValueID originID = value->pairValue()->first()->valueID();
    605         value = value->pairValue()->second();
    606        
    607580        if (originID == CSSValueRight || originID == CSSValueBottom) {
    608581            // For right/bottom, the offset is relative to the far edge.
     
    610583            sign = -1;
    611584        }
    612     }
    613    
     585        value = value->pairValue()->second();
     586    }
     587
    614588    if (value->isNumber())
    615589        return origin + sign * value->floatValue() * conversionData.zoom();
    616    
     590
    617591    if (value->isPercentage())
    618         return origin + sign * value->floatValue() / 100.f * edgeDistance;
    619 
    620     if (value->isCalculatedPercentageWithLength()) {
    621         Ref<CalculationValue> calculationValue { value->cssCalcValue()->createCalculationValue(conversionData) };
    622         return origin + sign * calculationValue->evaluate(edgeDistance);
    623     }
    624    
     592        return origin + sign * value->floatValue() / 100 * edgeDistance;
     593
     594    if (value->isCalculatedPercentageWithLength())
     595        return origin + sign * value->cssCalcValue()->createCalculationValue(conversionData)->evaluate(edgeDistance);
     596
    625597    switch (value->valueID()) {
    626598    case CSSValueTop:
     
    632604    case CSSValueBottom:
    633605        ASSERT(!isHorizontal);
    634         return size.height();
     606        return edgeDistance;
    635607    case CSSValueRight:
    636608        ASSERT(isHorizontal);
    637         return size.width();
     609        return edgeDistance;
    638610    case CSSValueCenter:
    639611        return origin + sign * .5f * edgeDistance;
     
    645617}
    646618
    647 FloatPoint CSSGradientValue::computeEndPoint(CSSPrimitiveValue* horizontal, CSSPrimitiveValue* vertical, const CSSToLengthConversionData& conversionData, const FloatSize& size)
    648 {
    649     FloatPoint result;
    650 
    651     if (horizontal)
    652         result.setX(positionFromValue(horizontal, conversionData, size, true));
    653 
    654     if (vertical)
    655         result.setY(positionFromValue(vertical, conversionData, size, false));
    656 
    657     return result;
     619// Resolve points/radii to front end values.
     620static FloatPoint computeEndPoint(const CSSPrimitiveValue* horizontal, const CSSPrimitiveValue* vertical, const CSSToLengthConversionData& conversionData, const FloatSize& size)
     621{
     622    return { positionFromValue(horizontal, conversionData, size, true), positionFromValue(vertical, conversionData, size, false) };
    658623}
    659624
    660625bool CSSGradientValue::isCacheable() const
    661626{
     627    if (hasColorDerivedFromElement())
     628        return false;
    662629    for (auto& stop : m_stops) {
    663         if (stop.m_colorIsDerivedFromElement)
     630        if (stop.position && stop.position->isFontRelativeLength())
    664631            return false;
    665 
    666         if (!stop.m_position)
    667             continue;
    668 
    669         if (stop.m_position->isFontRelativeLength())
     632    }
     633    return true;
     634}
     635
     636bool CSSGradientValue::knownToBeOpaque(const RenderElement& renderer) const
     637{
     638    bool hasColorFilter = renderer.style().hasAppleColorFilter();
     639    for (auto& stop : m_stops) {
     640        Color color = stop.resolvedColor;
     641        if (hasColorFilter)
     642            renderer.style().appleColorFilter().transformColor(color);
     643        if (!color.isOpaque())
    670644            return false;
    671645    }
    672 
    673646    return true;
    674647}
    675648
    676 bool CSSGradientValue::knownToBeOpaque(const RenderElement& renderer) const
    677 {
    678     bool hasColorFilter = renderer.style().hasAppleColorFilter();
    679 
    680     for (auto& stop : m_stops) {
    681         if (hasColorFilter) {
    682             Color stopColor = stop.m_resolvedColor;
    683             renderer.style().appleColorFilter().transformColor(stopColor);
    684             if (!stopColor.isOpaque())
    685                 return false;
    686         }
    687 
    688         if (!stop.m_resolvedColor.isOpaque())
    689             return false;
    690     }
    691     return true;
     649bool CSSGradientValue::equals(const CSSGradientValue& other) const
     650{
     651    return compareCSSValuePtr(m_firstX, other.m_firstX)
     652        && compareCSSValuePtr(m_firstY, other.m_firstY)
     653        && compareCSSValuePtr(m_secondX, other.m_secondX)
     654        && compareCSSValuePtr(m_secondY, other.m_secondY)
     655        && m_stops == other.m_stops
     656        && m_gradientType == other.m_gradientType
     657        && m_repeating == other.m_repeating;
    692658}
    693659
     
    695661{
    696662    for (auto& stop : stops) {
    697         double position = stop.m_position->doubleValue(CSSUnitType::CSS_NUMBER);
     663        double position = stop.position->doubleValue(CSSUnitType::CSS_NUMBER);
    698664        if (!position)
    699             builder.append(", from(", stop.m_color->cssText(), ')');
     665            builder.append(", from(", stop.color->cssText(), ')');
    700666        else if (position == 1)
    701             builder.append(", to(", stop.m_color->cssText(), ')');
     667            builder.append(", to(", stop.color->cssText(), ')');
    702668        else
    703             builder.append(", color-stop(", position, ", ", stop.m_color->cssText(), ')');
    704     }
    705 }
    706 
    707 void CSSGradientValue::writeColorStop(StringBuilder& builder, const CSSGradientColorStop& stop) const
    708 {
    709     if (!stop.isMidpoint && stop.m_color)
    710         builder.append(stop.m_color->cssText());
    711 
    712     if (stop.m_position) {
    713         if (!stop.isMidpoint)
    714             builder.append(' ');
    715         builder.append(stop.m_position->cssText());
    716     }
     669            builder.append(", color-stop(", position, ", ", stop.color->cssText(), ')');
     670    }
     671}
     672
     673template<typename T, typename U> static void appendSpaceSeparatedOptionalCSSPtrText(StringBuilder& builder, const T& a, const U& b)
     674{
     675    if (a && b)
     676        builder.append(a->cssText(), ' ', b->cssText());
     677    else if (a)
     678        builder.append(a->cssText());
     679    else if (b)
     680        builder.append(b->cssText());
     681}
     682
     683static void writeColorStop(StringBuilder& builder, const CSSGradientColorStop& stop)
     684{
     685    appendSpaceSeparatedOptionalCSSPtrText(builder, stop.color, stop.position);
    717686}
    718687
     
    720689{
    721690    StringBuilder result;
    722     if (m_gradientType == CSSDeprecatedLinearGradient) {
    723         result.append("-webkit-gradient(linear, ", m_firstX->cssText(), ' ', m_firstY->cssText(), ", ", m_secondX->cssText(), ' ', m_secondY->cssText());
    724         appendGradientStops(result, m_stops);
    725     } else if (m_gradientType == CSSPrefixedLinearGradient) {
    726         if (m_repeating)
     691    if (gradientType() == CSSDeprecatedLinearGradient) {
     692        result.append("-webkit-gradient(linear, ", firstX()->cssText(), ' ', firstY()->cssText(), ", ", secondX()->cssText(), ' ', secondY()->cssText());
     693        appendGradientStops(result, stops());
     694    } else if (gradientType() == CSSPrefixedLinearGradient) {
     695        if (isRepeating())
    727696            result.appendLiteral("-webkit-repeating-linear-gradient(");
    728697        else
     
    731700        if (m_angle)
    732701            result.append(m_angle->cssText());
    733         else {
    734             if (m_firstX && m_firstY)
    735                 result.append(m_firstX->cssText(), ' ', m_firstY->cssText());
    736             else if (m_firstX)
    737                 result.append(m_firstX->cssText());
    738             else if (m_firstY)
    739                 result.append(m_firstY->cssText());
    740         }
    741 
    742         for (auto& stop : m_stops) {
     702        else
     703            appendSpaceSeparatedOptionalCSSPtrText(result, firstX(), firstY());
     704
     705        for (auto& stop : stops()) {
    743706            result.appendLiteral(", ");
    744707            writeColorStop(result, stop);
    745708        }
    746709    } else {
    747         if (m_repeating)
     710        if (isRepeating())
    748711            result.appendLiteral("repeating-linear-gradient(");
    749712        else
     
    755718            result.append(m_angle->cssText());
    756719            wroteSomething = true;
    757         } else if ((m_firstX || m_firstY) && !(!m_firstX && m_firstY && m_firstY->valueID() == CSSValueBottom)) {
     720        } else if ((firstX() || firstY()) && !(!firstX() && firstY() && firstY()->valueID() == CSSValueBottom)) {
    758721            result.appendLiteral("to ");
    759             if (m_firstX && m_firstY)
    760                 result.append(m_firstX->cssText(), ' ', m_firstY->cssText());
    761             else if (m_firstX)
    762                 result.append(m_firstX->cssText());
    763             else
    764                 result.append(m_firstY->cssText());
     722            appendSpaceSeparatedOptionalCSSPtrText(result, firstX(), firstY());
    765723            wroteSomething = true;
    766724        }
    767725
    768         if (wroteSomething)
    769             result.appendLiteral(", ");
    770 
    771         bool wroteFirstStop = false;
    772         for (auto& stop : m_stops) {
    773             if (wroteFirstStop)
     726        for (auto& stop : stops()) {
     727            if (wroteSomething)
    774728                result.appendLiteral(", ");
    775             wroteFirstStop = true;
     729            wroteSomething = true;
    776730            writeColorStop(result, stop);
    777731        }
     
    860814    if (m_angle) {
    861815        float angle = m_angle->floatValue(CSSUnitType::CSS_DEG);
    862         endPointsFromAngle(angle, size, firstPoint, secondPoint, m_gradientType);
     816        endPointsFromAngle(angle, size, firstPoint, secondPoint, gradientType());
    863817    } else {
    864         switch (m_gradientType) {
     818        switch (gradientType()) {
    865819        case CSSDeprecatedLinearGradient:
    866             firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
    867             if (m_secondX || m_secondY)
    868                 secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), conversionData, size);
     820            firstPoint = computeEndPoint(firstX(), firstY(), conversionData, size);
     821            if (secondX() || secondY())
     822                secondPoint = computeEndPoint(secondX(), secondY(), conversionData, size);
    869823            else {
    870                 if (m_firstX)
     824                if (firstX())
    871825                    secondPoint.setX(size.width() - firstPoint.x());
    872                 if (m_firstY)
     826                if (firstY())
    873827                    secondPoint.setY(size.height() - firstPoint.y());
    874828            }
    875829            break;
    876830        case CSSPrefixedLinearGradient:
    877             firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
    878             if (m_firstX)
     831            firstPoint = computeEndPoint(firstX(), firstY(), conversionData, size);
     832            if (firstX())
    879833                secondPoint.setX(size.width() - firstPoint.x());
    880             if (m_firstY)
     834            if (firstY())
    881835                secondPoint.setY(size.height() - firstPoint.y());
    882836            break;
    883837        case CSSLinearGradient:
    884             if (m_firstX && m_firstY) {
     838            if (firstX() && firstY()) {
    885839                // "Magic" corners, so the 50% line touches two corners.
    886840                float rise = size.width();
    887841                float run = size.height();
    888                 if (m_firstX && m_firstX->valueID() == CSSValueLeft)
     842                if (firstX() && firstX()->valueID() == CSSValueLeft)
    889843                    run *= -1;
    890                 if (m_firstY && m_firstY->valueID() == CSSValueBottom)
     844                if (firstY() && firstY()->valueID() == CSSValueBottom)
    891845                    rise *= -1;
    892846                // Compute angle, and flip it back to "bearing angle" degrees.
    893847                float angle = 90 - rad2deg(atan2(rise, run));
    894                 endPointsFromAngle(angle, size, firstPoint, secondPoint, m_gradientType);
    895             } else if (m_firstX || m_firstY) {
    896                 secondPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
    897                 if (m_firstX)
     848                endPointsFromAngle(angle, size, firstPoint, secondPoint, gradientType());
     849            } else if (firstX() || firstY()) {
     850                secondPoint = computeEndPoint(firstX(), firstY(), conversionData, size);
     851                if (firstX())
    898852                    firstPoint.setX(size.width() - secondPoint.x());
    899                 if (m_firstY)
     853                if (firstY())
    900854                    firstPoint.setY(size.height() - secondPoint.y());
    901855            } else
     
    918872bool CSSLinearGradientValue::equals(const CSSLinearGradientValue& other) const
    919873{
    920     if (m_gradientType == CSSDeprecatedLinearGradient)
    921         return other.m_gradientType == m_gradientType
    922             && compareCSSValuePtr(m_firstX, other.m_firstX)
    923             && compareCSSValuePtr(m_firstY, other.m_firstY)
    924             && compareCSSValuePtr(m_secondX, other.m_secondX)
    925             && compareCSSValuePtr(m_secondY, other.m_secondY)
    926             && m_stops == other.m_stops;
    927 
    928     if (m_gradientType != other.m_gradientType)
    929         return false;
    930 
    931     if (m_repeating != other.m_repeating)
    932         return false;
    933 
    934     if (m_angle)
    935         return compareCSSValuePtr(m_angle, other.m_angle) && m_stops == other.m_stops;
    936 
    937     if (other.m_angle)
    938         return false;
    939 
    940     bool equalXandY = false;
    941     if (m_firstX && m_firstY)
    942         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
    943     else if (m_firstX)
    944         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
    945     else if (m_firstY)
    946         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
    947     else
    948         equalXandY = !other.m_firstX && !other.m_firstY;
    949 
    950     return equalXandY && m_stops == other.m_stops;
     874    return CSSGradientValue::equals(other) && compareCSSValuePtr(m_angle, other.m_angle);
    951875}
    952876
     
    955879    StringBuilder result;
    956880
    957     if (m_gradientType == CSSDeprecatedRadialGradient) {
    958         result.append("-webkit-gradient(radial, ", m_firstX->cssText(), ' ', m_firstY->cssText(), ", ", m_firstRadius->cssText(),
    959             ", ", m_secondX->cssText(), ' ', m_secondY->cssText(), ", ", m_secondRadius->cssText());
    960         appendGradientStops(result, m_stops);
    961     } else if (m_gradientType == CSSPrefixedRadialGradient) {
    962         if (m_repeating)
     881    if (gradientType() == CSSDeprecatedRadialGradient) {
     882        result.append("-webkit-gradient(radial, ", firstX()->cssText(), ' ', firstY()->cssText(), ", ", m_firstRadius->cssText(),
     883            ", ", secondX()->cssText(), ' ', secondY()->cssText(), ", ", m_secondRadius->cssText());
     884        appendGradientStops(result, stops());
     885    } else if (gradientType() == CSSPrefixedRadialGradient) {
     886        if (isRepeating())
    963887            result.appendLiteral("-webkit-repeating-radial-gradient(");
    964888        else
    965889            result.appendLiteral("-webkit-radial-gradient(");
    966890
    967         if (m_firstX && m_firstY)
    968             result.append(m_firstX->cssText(), ' ', m_firstY->cssText());
    969         else if (m_firstX)
    970             result.append(m_firstX->cssText());
    971         else if (m_firstY)
    972             result.append(m_firstY->cssText());
     891        if (firstX() || firstY())
     892            appendSpaceSeparatedOptionalCSSPtrText(result, firstX(), firstY());
    973893        else
    974894            result.appendLiteral("center");
     
    987907            result.append(", ", m_endHorizontalSize->cssText(), ' ', m_endVerticalSize->cssText());
    988908
    989         for (auto& stop : m_stops) {
     909        for (auto& stop : stops()) {
    990910            result.appendLiteral(", ");
    991911            writeColorStop(result, stop);
    992912        }
    993913    } else {
    994         if (m_repeating)
     914        if (isRepeating())
    995915            result.appendLiteral("repeating-radial-gradient(");
    996916        else
     
    1020940        }
    1021941
    1022         if (m_firstX || m_firstY) {
     942        if (firstX() || firstY()) {
    1023943            if (wroteSomething)
    1024944                result.append(' ');
    1025945            result.appendLiteral("at ");
    1026             if (m_firstX && m_firstY)
    1027                 result.append(m_firstX->cssText(), ' ', m_firstY->cssText());
    1028             else if (m_firstX)
    1029                 result.append(m_firstX->cssText());
    1030             else
    1031                 result.append(m_firstY->cssText());
     946            appendSpaceSeparatedOptionalCSSPtrText(result, firstX(), firstY());
    1032947            wroteSomething = true;
    1033948        }
     
    1037952
    1038953        bool wroteFirstStop = false;
    1039         for (auto& stop : m_stops) {
     954        for (auto& stop : stops()) {
    1040955            if (wroteFirstStop)
    1041956                result.appendLiteral(", ");
     
    11441059    CSSToLengthConversionData conversionData(&renderer.style(), renderer.document().documentElement()->renderStyle(), &renderer.view());
    11451060
    1146     FloatPoint firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
    1147     if (!m_firstX)
     1061    FloatPoint firstPoint = computeEndPoint(firstX(), firstY(), conversionData, size);
     1062    if (!firstX())
    11481063        firstPoint.setX(size.width() / 2);
    1149     if (!m_firstY)
     1064    if (!firstY())
    11501065        firstPoint.setY(size.height() / 2);
    11511066
    1152     FloatPoint secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), conversionData, size);
    1153     if (!m_secondX)
     1067    FloatPoint secondPoint = computeEndPoint(secondX(), secondY(), conversionData, size);
     1068    if (!secondX())
    11541069        secondPoint.setX(size.width() / 2);
    1155     if (!m_secondY)
     1070    if (!secondY())
    11561071        secondPoint.setY(size.height() / 2);
    11571072
     
    12671182    // computeStops() only uses maxExtent for repeating gradients.
    12681183    float maxExtent = 0;
    1269     if (m_repeating) {
     1184    if (isRepeating()) {
    12701185        FloatPoint corner;
    12711186        maxExtent = distanceToFarthestCorner(secondPoint, size, corner);
     
    12831198bool CSSRadialGradientValue::equals(const CSSRadialGradientValue& other) const
    12841199{
    1285     if (m_gradientType == CSSDeprecatedRadialGradient)
    1286         return other.m_gradientType == m_gradientType
    1287             && compareCSSValuePtr(m_firstX, other.m_firstX)
    1288             && compareCSSValuePtr(m_firstY, other.m_firstY)
    1289             && compareCSSValuePtr(m_secondX, other.m_secondX)
    1290             && compareCSSValuePtr(m_secondY, other.m_secondY)
    1291             && compareCSSValuePtr(m_firstRadius, other.m_firstRadius)
    1292             && compareCSSValuePtr(m_secondRadius, other.m_secondRadius)
    1293             && m_stops == other.m_stops;
    1294 
    1295     if (m_gradientType != other.m_gradientType)
    1296         return false;
    1297 
    1298     if (m_repeating != other.m_repeating)
    1299         return false;
    1300 
    1301     bool equalXandY = false;
    1302     if (m_firstX && m_firstY)
    1303         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
    1304     else if (m_firstX)
    1305         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
    1306     else if (m_firstY)
    1307         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
    1308     else
    1309         equalXandY = !other.m_firstX && !other.m_firstY;
    1310 
    1311     if (!equalXandY)
    1312         return false;
    1313 
    1314     bool equalShape = true;
    1315     bool equalSizingBehavior = true;
    1316     bool equalHorizontalAndVerticalSize = true;
    1317 
    1318     if (m_shape)
    1319         equalShape = compareCSSValuePtr(m_shape, other.m_shape);
    1320     else if (m_sizingBehavior)
    1321         equalSizingBehavior = compareCSSValuePtr(m_sizingBehavior, other.m_sizingBehavior);
    1322     else if (m_endHorizontalSize && m_endVerticalSize)
    1323         equalHorizontalAndVerticalSize = compareCSSValuePtr(m_endHorizontalSize, other.m_endHorizontalSize) && compareCSSValuePtr(m_endVerticalSize, other.m_endVerticalSize);
    1324     else {
    1325         equalShape = !other.m_shape;
    1326         equalSizingBehavior = !other.m_sizingBehavior;
    1327         equalHorizontalAndVerticalSize = !other.m_endHorizontalSize && !other.m_endVerticalSize;
    1328     }
    1329     return equalShape && equalSizingBehavior && equalHorizontalAndVerticalSize && m_stops == other.m_stops;
    1330 }
    1331 
     1200    return CSSGradientValue::equals(other)
     1201        && compareCSSValuePtr(m_shape, other.m_shape)
     1202        && compareCSSValuePtr(m_sizingBehavior, other.m_sizingBehavior)
     1203        && compareCSSValuePtr(m_endHorizontalSize, other.m_endHorizontalSize)
     1204        && compareCSSValuePtr(m_endVerticalSize, other.m_endVerticalSize);
     1205}
    13321206
    13331207String CSSConicGradientValue::customCSSText() const
     
    13351209    StringBuilder result;
    13361210
    1337     if (m_repeating)
     1211    if (isRepeating())
    13381212        result.appendLiteral("repeating-conic-gradient(");
    13391213    else
     
    13471221    }
    13481222
    1349     if (m_firstX && m_firstY) {
     1223    if (firstX() && firstY()) {
    13501224        if (wroteSomething)
    13511225            result.append(' ');
    1352         result.append("at ", m_firstX->cssText(), ' ', m_firstY->cssText());
     1226        result.appendLiteral("at ");
     1227        appendSpaceSeparatedOptionalCSSPtrText(result, firstX(), firstY());
    13531228        wroteSomething = true;
    13541229    }
     
    13581233
    13591234    bool wroteFirstStop = false;
    1360     for (auto& stop : m_stops) {
     1235    for (auto& stop : stops()) {
    13611236        if (wroteFirstStop)
    13621237            result.appendLiteral(", ");
     
    13751250    CSSToLengthConversionData conversionData(&renderer.style(), renderer.document().documentElement()->renderStyle(), &renderer.view());
    13761251
    1377     FloatPoint centerPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
    1378     if (!m_firstX)
     1252    FloatPoint centerPoint = computeEndPoint(firstX(), firstY(), conversionData, size);
     1253    if (!firstX())
    13791254        centerPoint.setX(size.width() / 2);
    1380     if (!m_firstY)
     1255    if (!firstY())
    13811256        centerPoint.setY(size.height() / 2);
    13821257
     
    13961271bool CSSConicGradientValue::equals(const CSSConicGradientValue& other) const
    13971272{
    1398     if (m_repeating != other.m_repeating)
    1399         return false;
    1400 
    1401     if (!compareCSSValuePtr(m_angle, other.m_angle))
    1402         return false;
    1403 
    1404     bool equalXandY = false;
    1405     if (m_firstX && m_firstY)
    1406         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
    1407     else if (m_firstX)
    1408         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
    1409     else if (m_firstY)
    1410         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
    1411     else
    1412         equalXandY = !other.m_firstX && !other.m_firstY;
    1413 
    1414     return equalXandY && m_stops == other.m_stops;
     1273    return CSSGradientValue::equals(other) && compareCSSValuePtr(m_angle, other.m_angle);
    14151274}
    14161275
Note: See TracChangeset for help on using the changeset viewer.