/* * Copyright (C) 2013 Google Inc. All rights reserved. * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef StyleBuilderConverter_h #define StyleBuilderConverter_h #include "BasicShapeFunctions.h" #include "CSSCalculationValue.h" #include "CSSContentDistributionValue.h" #include "CSSFontFeatureValue.h" #include "CSSFunctionValue.h" #include "CSSGridLineNamesValue.h" #include "CSSGridTemplateAreasValue.h" #include "CSSImageGeneratorValue.h" #include "CSSImageSetValue.h" #include "CSSImageValue.h" #include "CSSPrimitiveValue.h" #include "CSSReflectValue.h" #include "Frame.h" #include "LayoutUnit.h" #include "Length.h" #include "LengthRepeat.h" #include "Pair.h" #include "QuotesData.h" #include "SVGURIReference.h" #include "Settings.h" #include "StyleResolver.h" #include "StyleScrollSnapPoints.h" #include "TransformFunctions.h" #include namespace WebCore { // Note that we assume the CSS parser only allows valid CSSValue types. class StyleBuilderConverter { public: static Length convertLength(StyleResolver&, const CSSValue&); static Length convertLengthOrAuto(StyleResolver&, CSSValue&); static Length convertLengthSizing(StyleResolver&, CSSValue&); static Length convertLengthMaxSizing(StyleResolver&, CSSValue&); template static T convertComputedLength(StyleResolver&, CSSValue&); template static T convertLineWidth(StyleResolver&, CSSValue&); static float convertSpacing(StyleResolver&, CSSValue&); static LengthSize convertRadius(StyleResolver&, CSSValue&); static LengthPoint convertObjectPosition(StyleResolver&, CSSValue&); static TextDecoration convertTextDecoration(StyleResolver&, CSSValue&); template static T convertNumber(StyleResolver&, CSSValue&); template static T convertNumberOrAuto(StyleResolver&, CSSValue&); static short convertWebkitHyphenateLimitLines(StyleResolver&, CSSValue&); template static NinePieceImage convertBorderImage(StyleResolver&, CSSValue&); template static NinePieceImage convertBorderMask(StyleResolver&, CSSValue&); template static PassRefPtr convertStyleImage(StyleResolver&, CSSValue&); static TransformOperations convertTransform(StyleResolver&, CSSValue&); static String convertString(StyleResolver&, CSSValue&); static String convertStringOrAuto(StyleResolver&, CSSValue&); static String convertStringOrNone(StyleResolver&, CSSValue&); static TextEmphasisPosition convertTextEmphasisPosition(StyleResolver&, CSSValue&); static ETextAlign convertTextAlign(StyleResolver&, CSSValue&); static PassRefPtr convertClipPath(StyleResolver&, CSSValue&); static EResize convertResize(StyleResolver&, CSSValue&); static int convertMarqueeRepetition(StyleResolver&, CSSValue&); static int convertMarqueeSpeed(StyleResolver&, CSSValue&); static PassRefPtr convertQuotes(StyleResolver&, CSSValue&); static TextUnderlinePosition convertTextUnderlinePosition(StyleResolver&, CSSValue&); static PassRefPtr convertReflection(StyleResolver&, CSSValue&); static IntSize convertInitialLetter(StyleResolver&, CSSValue&); static float convertTextStrokeWidth(StyleResolver&, CSSValue&); static LineBoxContain convertLineBoxContain(StyleResolver&, CSSValue&); static TextDecorationSkip convertTextDecorationSkip(StyleResolver&, CSSValue&); static PassRefPtr convertShapeValue(StyleResolver&, CSSValue&); #if ENABLE(CSS_SCROLL_SNAP) static std::unique_ptr convertScrollSnapPoints(StyleResolver&, CSSValue&); static LengthSize convertSnapCoordinatePair(StyleResolver&, CSSValue&, size_t offset = 0); static Vector convertScrollSnapCoordinates(StyleResolver&, CSSValue&); #endif #if ENABLE(CSS_GRID_LAYOUT) static GridTrackSize convertGridTrackSize(StyleResolver&, CSSValue&); static Optional convertGridPosition(StyleResolver&, CSSValue&); static GridAutoFlow convertGridAutoFlow(StyleResolver&, CSSValue&); #endif // ENABLE(CSS_GRID_LAYOUT) static Optional convertWordSpacing(StyleResolver&, CSSValue&); static Optional convertPerspective(StyleResolver&, CSSValue&); static Optional convertMarqueeIncrement(StyleResolver&, CSSValue&); static Optional convertFilterOperations(StyleResolver&, CSSValue&); #if PLATFORM(IOS) static bool convertTouchCallout(StyleResolver&, CSSValue&); #endif #if ENABLE(TOUCH_EVENTS) static Color convertTapHighlightColor(StyleResolver&, CSSValue&); #endif #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) static bool convertOverflowScrolling(StyleResolver&, CSSValue&); #endif static FontFeatureSettings convertFontFeatureSettings(StyleResolver&, CSSValue&); static SVGLength convertSVGLength(StyleResolver&, CSSValue&); static Vector convertSVGLengthVector(StyleResolver&, CSSValue&); static Vector convertStrokeDashArray(StyleResolver&, CSSValue&); static PaintOrder convertPaintOrder(StyleResolver&, CSSValue&); static float convertOpacity(StyleResolver&, CSSValue&); static String convertSVGURIReference(StyleResolver&, CSSValue&); static Color convertSVGColor(StyleResolver&, CSSValue&); static StyleSelfAlignmentData convertSelfOrDefaultAlignmentData(StyleResolver&, CSSValue&); static StyleContentAlignmentData convertContentAlignmentData(StyleResolver&, CSSValue&); static EGlyphOrientation convertGlyphOrientation(StyleResolver&, CSSValue&); static EGlyphOrientation convertGlyphOrientationOrAuto(StyleResolver&, CSSValue&); static Optional convertLineHeight(StyleResolver&, CSSValue&, float multiplier = 1.f); static FontSynthesis convertFontSynthesis(StyleResolver&, CSSValue&); static BreakBetween convertPageBreakBetween(StyleResolver&, CSSValue&); static BreakInside convertPageBreakInside(StyleResolver&, CSSValue&); static BreakBetween convertColumnBreakBetween(StyleResolver&, CSSValue&); static BreakInside convertColumnBreakInside(StyleResolver&, CSSValue&); #if ENABLE(CSS_REGIONS) static BreakBetween convertRegionBreakBetween(StyleResolver&, CSSValue&); static BreakInside convertRegionBreakInside(StyleResolver&, CSSValue&); #endif static HangingPunctuation convertHangingPunctuation(StyleResolver&, CSSValue&); private: friend class StyleBuilderCustom; static Length convertToRadiusLength(CSSToLengthConversionData&, CSSPrimitiveValue&); static TextEmphasisPosition valueToEmphasisPosition(CSSPrimitiveValue&); static TextDecorationSkip valueToDecorationSkip(const CSSPrimitiveValue&); #if ENABLE(CSS_SCROLL_SNAP) static Length parseSnapCoordinate(StyleResolver&, const CSSValue&); #endif static Length convertTo100PercentMinusLength(const Length&); static Length convertPositionComponent(StyleResolver&, const CSSPrimitiveValue&); #if ENABLE(CSS_GRID_LAYOUT) static GridLength createGridTrackBreadth(CSSPrimitiveValue&, StyleResolver&); static GridTrackSize createGridTrackSize(CSSValue&, StyleResolver&); static bool createGridTrackList(CSSValue&, Vector& trackSizes, NamedGridLinesMap&, OrderedNamedGridLinesMap&, StyleResolver&); static bool createGridPosition(CSSValue&, GridPosition&); static void createImplicitNamedGridLinesFromGridArea(const NamedGridAreaMap&, NamedGridLinesMap&, GridTrackSizingDirection); #endif // ENABLE(CSS_GRID_LAYOUT) static CSSToLengthConversionData csstoLengthConversionDataWithTextZoomFactor(StyleResolver&); }; inline Length StyleBuilderConverter::convertLength(StyleResolver& styleResolver, const CSSValue& value) { auto& primitiveValue = downcast(value); CSSToLengthConversionData conversionData = styleResolver.useSVGZoomRulesForLength() ? styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f) : styleResolver.state().cssToLengthConversionData(); if (primitiveValue.isLength()) { Length length = primitiveValue.computeLength(conversionData); length.setHasQuirk(primitiveValue.isQuirkValue()); return length; } if (primitiveValue.isPercentage()) return Length(primitiveValue.getDoubleValue(), Percent); if (primitiveValue.isCalculatedPercentageWithLength()) return Length(primitiveValue.cssCalcValue()->createCalculationValue(conversionData)); ASSERT_NOT_REACHED(); return Length(0, Fixed); } inline Length StyleBuilderConverter::convertLengthOrAuto(StyleResolver& styleResolver, CSSValue& value) { if (downcast(value).getValueID() == CSSValueAuto) return Length(Auto); return convertLength(styleResolver, value); } inline Length StyleBuilderConverter::convertLengthSizing(StyleResolver& styleResolver, CSSValue& value) { auto& primitiveValue = downcast(value); switch (primitiveValue.getValueID()) { case CSSValueInvalid: return convertLength(styleResolver, value); case CSSValueIntrinsic: return Length(Intrinsic); case CSSValueMinIntrinsic: return Length(MinIntrinsic); case CSSValueWebkitMinContent: return Length(MinContent); case CSSValueWebkitMaxContent: return Length(MaxContent); case CSSValueWebkitFillAvailable: return Length(FillAvailable); case CSSValueWebkitFitContent: return Length(FitContent); case CSSValueAuto: return Length(Auto); default: ASSERT_NOT_REACHED(); return Length(); } } inline Length StyleBuilderConverter::convertLengthMaxSizing(StyleResolver& styleResolver, CSSValue& value) { if (downcast(value).getValueID() == CSSValueNone) return Length(Undefined); return convertLengthSizing(styleResolver, value); } template inline T StyleBuilderConverter::convertComputedLength(StyleResolver& styleResolver, CSSValue& value) { return downcast(value).computeLength(styleResolver.state().cssToLengthConversionData()); } template inline T StyleBuilderConverter::convertLineWidth(StyleResolver& styleResolver, CSSValue& value) { auto& primitiveValue = downcast(value); switch (primitiveValue.getValueID()) { case CSSValueThin: return 1; case CSSValueMedium: return 3; case CSSValueThick: return 5; case CSSValueInvalid: { // Any original result that was >= 1 should not be allowed to fall below 1. // This keeps border lines from vanishing. T result = convertComputedLength(styleResolver, value); if (styleResolver.state().style()->effectiveZoom() < 1.0f && result < 1.0) { T originalLength = primitiveValue.computeLength(styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0)); if (originalLength >= 1.0) return 1; } float minimumLineWidth = 1 / styleResolver.document().deviceScaleFactor(); if (result > 0 && result < minimumLineWidth) return minimumLineWidth; return floorToDevicePixel(result, styleResolver.document().deviceScaleFactor()); } default: ASSERT_NOT_REACHED(); return 0; } } inline float StyleBuilderConverter::convertSpacing(StyleResolver& styleResolver, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueNormal) return 0.f; CSSToLengthConversionData conversionData = styleResolver.useSVGZoomRulesForLength() ? styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f) : styleResolver.state().cssToLengthConversionData(); return primitiveValue.computeLength(conversionData); } inline Length StyleBuilderConverter::convertToRadiusLength(CSSToLengthConversionData& conversionData, CSSPrimitiveValue& value) { if (value.isPercentage()) return Length(value.getDoubleValue(), Percent); if (value.isCalculatedPercentageWithLength()) return Length(value.cssCalcValue()->createCalculationValue(conversionData)); return value.computeLength(conversionData); } inline LengthSize StyleBuilderConverter::convertRadius(StyleResolver& styleResolver, CSSValue& value) { auto& primitiveValue = downcast(value); Pair* pair = primitiveValue.getPairValue(); if (!pair || !pair->first() || !pair->second()) return LengthSize(Length(0, Fixed), Length(0, Fixed)); CSSToLengthConversionData conversionData = styleResolver.state().cssToLengthConversionData(); Length radiusWidth = convertToRadiusLength(conversionData, *pair->first()); Length radiusHeight = convertToRadiusLength(conversionData, *pair->second()); ASSERT(!radiusWidth.isNegative()); ASSERT(!radiusHeight.isNegative()); if (radiusWidth.isZero() || radiusHeight.isZero()) return LengthSize(Length(0, Fixed), Length(0, Fixed)); return LengthSize(radiusWidth, radiusHeight); } inline Length StyleBuilderConverter::convertTo100PercentMinusLength(const Length& length) { if (length.isPercent()) return Length(100 - length.value(), Percent); // Turn this into a calc expression: calc(100% - length) auto lhs = std::make_unique(Length(100, Percent)); auto rhs = std::make_unique(length); auto op = std::make_unique(WTFMove(lhs), WTFMove(rhs), CalcSubtract); return Length(CalculationValue::create(WTFMove(op), CalculationRangeAll)); } inline Length StyleBuilderConverter::convertPositionComponent(StyleResolver& styleResolver, const CSSPrimitiveValue& value) { Length length; auto* lengthValue = &value; bool relativeToTrailingEdge = false; if (value.isPair()) { auto& first = *value.getPairValue()->first(); if (first.getValueID() == CSSValueRight || first.getValueID() == CSSValueBottom) relativeToTrailingEdge = true; lengthValue = value.getPairValue()->second(); } length = convertLength(styleResolver, *lengthValue); if (relativeToTrailingEdge) length = convertTo100PercentMinusLength(length); return length; } inline LengthPoint StyleBuilderConverter::convertObjectPosition(StyleResolver& styleResolver, CSSValue& value) { auto& primitiveValue = downcast(value); Pair* pair = primitiveValue.getPairValue(); if (!pair || !pair->first() || !pair->second()) return RenderStyle::initialObjectPosition(); Length lengthX = convertPositionComponent(styleResolver, *pair->first()); Length lengthY = convertPositionComponent(styleResolver, *pair->second()); return LengthPoint(lengthX, lengthY); } inline TextDecoration StyleBuilderConverter::convertTextDecoration(StyleResolver&, CSSValue& value) { TextDecoration result = RenderStyle::initialTextDecoration(); if (is(value)) { for (auto& currentValue : downcast(value)) result |= downcast(currentValue.get()); } return result; } template inline T StyleBuilderConverter::convertNumber(StyleResolver&, CSSValue& value) { return downcast(value).getValue(CSSPrimitiveValue::CSS_NUMBER); } template inline T StyleBuilderConverter::convertNumberOrAuto(StyleResolver& styleResolver, CSSValue& value) { if (downcast(value).getValueID() == CSSValueAuto) return -1; return convertNumber(styleResolver, value); } inline short StyleBuilderConverter::convertWebkitHyphenateLimitLines(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueNoLimit) return -1; return primitiveValue.getValue(CSSPrimitiveValue::CSS_NUMBER); } template inline NinePieceImage StyleBuilderConverter::convertBorderImage(StyleResolver& styleResolver, CSSValue& value) { NinePieceImage image; styleResolver.styleMap()->mapNinePieceImage(property, &value, image); return image; } template inline NinePieceImage StyleBuilderConverter::convertBorderMask(StyleResolver& styleResolver, CSSValue& value) { NinePieceImage image; image.setMaskDefaults(); styleResolver.styleMap()->mapNinePieceImage(property, &value, image); return image; } template inline PassRefPtr StyleBuilderConverter::convertStyleImage(StyleResolver& styleResolver, CSSValue& value) { return styleResolver.styleImage(property, value); } inline TransformOperations StyleBuilderConverter::convertTransform(StyleResolver& styleResolver, CSSValue& value) { TransformOperations operations; transformsForValue(value, styleResolver.state().cssToLengthConversionData(), operations); return operations; } inline String StyleBuilderConverter::convertString(StyleResolver&, CSSValue& value) { return downcast(value).getStringValue(); } inline String StyleBuilderConverter::convertStringOrAuto(StyleResolver& styleResolver, CSSValue& value) { if (downcast(value).getValueID() == CSSValueAuto) return nullAtom; return convertString(styleResolver, value); } inline String StyleBuilderConverter::convertStringOrNone(StyleResolver& styleResolver, CSSValue& value) { if (downcast(value).getValueID() == CSSValueNone) return nullAtom; return convertString(styleResolver, value); } inline TextEmphasisPosition StyleBuilderConverter::valueToEmphasisPosition(CSSPrimitiveValue& primitiveValue) { ASSERT(primitiveValue.isValueID()); switch (primitiveValue.getValueID()) { case CSSValueOver: return TextEmphasisPositionOver; case CSSValueUnder: return TextEmphasisPositionUnder; case CSSValueLeft: return TextEmphasisPositionLeft; case CSSValueRight: return TextEmphasisPositionRight; default: break; } ASSERT_NOT_REACHED(); return RenderStyle::initialTextEmphasisPosition(); } inline TextEmphasisPosition StyleBuilderConverter::convertTextEmphasisPosition(StyleResolver&, CSSValue& value) { if (is(value)) return valueToEmphasisPosition(downcast(value)); TextEmphasisPosition position = 0; for (auto& currentValue : downcast(value)) position |= valueToEmphasisPosition(downcast(currentValue.get())); return position; } inline ETextAlign StyleBuilderConverter::convertTextAlign(StyleResolver& styleResolver, CSSValue& value) { auto& primitiveValue = downcast(value); ASSERT(primitiveValue.isValueID()); if (primitiveValue.getValueID() != CSSValueWebkitMatchParent) return primitiveValue; auto* parentStyle = styleResolver.parentStyle(); if (parentStyle->textAlign() == TASTART) return parentStyle->isLeftToRightDirection() ? LEFT : RIGHT; if (parentStyle->textAlign() == TAEND) return parentStyle->isLeftToRightDirection() ? RIGHT : LEFT; return parentStyle->textAlign(); } inline PassRefPtr StyleBuilderConverter::convertClipPath(StyleResolver& styleResolver, CSSValue& value) { if (is(value)) { auto& primitiveValue = downcast(value); if (primitiveValue.primitiveType() == CSSPrimitiveValue::CSS_URI) { String cssURLValue = primitiveValue.getStringValue(); URL url = styleResolver.document().completeURL(cssURLValue); // FIXME: It doesn't work with external SVG references (see https://bugs.webkit.org/show_bug.cgi?id=126133) return ReferenceClipPathOperation::create(cssURLValue, url.fragmentIdentifier()); } ASSERT(primitiveValue.getValueID() == CSSValueNone); return nullptr; } CSSBoxType referenceBox = BoxMissing; RefPtr operation; for (auto& currentValue : downcast(value)) { auto& primitiveValue = downcast(currentValue.get()); if (primitiveValue.isShape()) { ASSERT(!operation); operation = ShapeClipPathOperation::create(basicShapeForValue(styleResolver.state().cssToLengthConversionData(), primitiveValue.getShapeValue())); } else { ASSERT(primitiveValue.getValueID() == CSSValueContentBox || primitiveValue.getValueID() == CSSValueBorderBox || primitiveValue.getValueID() == CSSValuePaddingBox || primitiveValue.getValueID() == CSSValueMarginBox || primitiveValue.getValueID() == CSSValueFill || primitiveValue.getValueID() == CSSValueStroke || primitiveValue.getValueID() == CSSValueViewBox); ASSERT(referenceBox == BoxMissing); referenceBox = primitiveValue; } } if (operation) downcast(*operation).setReferenceBox(referenceBox); else { ASSERT(referenceBox != BoxMissing); operation = BoxClipPathOperation::create(referenceBox); } return operation.release(); } inline EResize StyleBuilderConverter::convertResize(StyleResolver& styleResolver, CSSValue& value) { auto& primitiveValue = downcast(value); EResize resize = RESIZE_NONE; if (primitiveValue.getValueID() == CSSValueAuto) { if (Settings* settings = styleResolver.document().settings()) resize = settings->textAreasAreResizable() ? RESIZE_BOTH : RESIZE_NONE; } else resize = primitiveValue; return resize; } inline int StyleBuilderConverter::convertMarqueeRepetition(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueInfinite) return -1; // -1 means repeat forever. ASSERT(primitiveValue.isNumber()); return primitiveValue.getIntValue(); } inline int StyleBuilderConverter::convertMarqueeSpeed(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); int speed = 85; if (CSSValueID ident = primitiveValue.getValueID()) { switch (ident) { case CSSValueSlow: speed = 500; // 500 msec. break; case CSSValueNormal: speed = 85; // 85msec. The WinIE default. break; case CSSValueFast: speed = 10; // 10msec. Super fast. break; default: ASSERT_NOT_REACHED(); break; } } else if (primitiveValue.isTime()) speed = primitiveValue.computeTime(); else { // For scrollamount support. ASSERT(primitiveValue.isNumber()); speed = primitiveValue.getIntValue(); } return speed; } inline PassRefPtr StyleBuilderConverter::convertQuotes(StyleResolver&, CSSValue& value) { if (is(value)) { ASSERT(downcast(value).getValueID() == CSSValueNone); return QuotesData::create(Vector>()); } CSSValueList& list = downcast(value); Vector> quotes; quotes.reserveInitialCapacity(list.length() / 2); for (unsigned i = 0; i < list.length(); i += 2) { CSSValue* first = list.itemWithoutBoundsCheck(i); // item() returns null if out of bounds so this is safe. CSSValue* second = list.item(i + 1); if (!second) break; String startQuote = downcast(*first).getStringValue(); String endQuote = downcast(*second).getStringValue(); quotes.append(std::make_pair(startQuote, endQuote)); } return QuotesData::create(quotes); } inline TextUnderlinePosition StyleBuilderConverter::convertTextUnderlinePosition(StyleResolver&, CSSValue& value) { // This is true if value is 'auto' or 'alphabetic'. if (is(value)) return downcast(value); unsigned combinedPosition = 0; for (auto& currentValue : downcast(value)) { TextUnderlinePosition position = downcast(currentValue.get()); combinedPosition |= position; } return static_cast(combinedPosition); } inline PassRefPtr StyleBuilderConverter::convertReflection(StyleResolver& styleResolver, CSSValue& value) { if (is(value)) { ASSERT(downcast(value).getValueID() == CSSValueNone); return nullptr; } auto& reflectValue = downcast(value); RefPtr reflection = StyleReflection::create(); reflection->setDirection(*reflectValue.direction()); if (reflectValue.offset()) reflection->setOffset(reflectValue.offset()->convertToLength(styleResolver.state().cssToLengthConversionData())); NinePieceImage mask; mask.setMaskDefaults(); styleResolver.styleMap()->mapNinePieceImage(CSSPropertyWebkitBoxReflect, reflectValue.mask(), mask); reflection->setMask(mask); return reflection.release(); } inline IntSize StyleBuilderConverter::convertInitialLetter(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueNormal) return IntSize(); Pair* pair = primitiveValue.getPairValue(); ASSERT(pair); ASSERT(pair->first()); ASSERT(pair->second()); return IntSize(pair->first()->getIntValue(), pair->second()->getIntValue()); } inline float StyleBuilderConverter::convertTextStrokeWidth(StyleResolver& styleResolver, CSSValue& value) { auto& primitiveValue = downcast(value); float width = 0; switch (primitiveValue.getValueID()) { case CSSValueThin: case CSSValueMedium: case CSSValueThick: { double result = 1.0 / 48; if (primitiveValue.getValueID() == CSSValueMedium) result *= 3; else if (primitiveValue.getValueID() == CSSValueThick) result *= 5; Ref emsValue(CSSPrimitiveValue::create(result, CSSPrimitiveValue::CSS_EMS)); width = convertComputedLength(styleResolver, emsValue); break; } case CSSValueInvalid: { width = convertComputedLength(styleResolver, primitiveValue); break; } default: ASSERT_NOT_REACHED(); return 0; } return width; } inline LineBoxContain StyleBuilderConverter::convertLineBoxContain(StyleResolver&, CSSValue& value) { if (is(value)) { ASSERT(downcast(value).getValueID() == CSSValueNone); return LineBoxContainNone; } return downcast(value).value(); } inline TextDecorationSkip StyleBuilderConverter::valueToDecorationSkip(const CSSPrimitiveValue& primitiveValue) { ASSERT(primitiveValue.isValueID()); switch (primitiveValue.getValueID()) { case CSSValueAuto: return TextDecorationSkipAuto; case CSSValueNone: return TextDecorationSkipNone; case CSSValueInk: return TextDecorationSkipInk; case CSSValueObjects: return TextDecorationSkipObjects; default: break; } ASSERT_NOT_REACHED(); return TextDecorationSkipNone; } inline TextDecorationSkip StyleBuilderConverter::convertTextDecorationSkip(StyleResolver&, CSSValue& value) { if (is(value)) return valueToDecorationSkip(downcast(value)); TextDecorationSkip skip = RenderStyle::initialTextDecorationSkip(); for (auto& currentValue : downcast(value)) skip |= valueToDecorationSkip(downcast(currentValue.get())); return skip; } #if ENABLE(CSS_SHAPES) static inline bool isImageShape(const CSSValue& value) { return is(value) #if ENABLE(CSS_IMAGE_SET) || is(value) #endif || is(value); } inline PassRefPtr StyleBuilderConverter::convertShapeValue(StyleResolver& styleResolver, CSSValue& value) { if (is(value)) { ASSERT(downcast(value).getValueID() == CSSValueNone); return nullptr; } if (isImageShape(value)) return ShapeValue::createImageValue(styleResolver.styleImage(CSSPropertyWebkitShapeOutside, value)); RefPtr shape; CSSBoxType referenceBox = BoxMissing; for (auto& currentValue : downcast(value)) { CSSPrimitiveValue& primitiveValue = downcast(currentValue.get()); if (primitiveValue.isShape()) shape = basicShapeForValue(styleResolver.state().cssToLengthConversionData(), primitiveValue.getShapeValue()); else if (primitiveValue.getValueID() == CSSValueContentBox || primitiveValue.getValueID() == CSSValueBorderBox || primitiveValue.getValueID() == CSSValuePaddingBox || primitiveValue.getValueID() == CSSValueMarginBox) referenceBox = primitiveValue; else { ASSERT_NOT_REACHED(); return nullptr; } } if (shape) return ShapeValue::createShapeValue(shape.release(), referenceBox); if (referenceBox != BoxMissing) return ShapeValue::createBoxShapeValue(referenceBox); ASSERT_NOT_REACHED(); return nullptr; } #endif // ENABLE(CSS_SHAPES) #if ENABLE(CSS_SCROLL_SNAP) inline Length StyleBuilderConverter::parseSnapCoordinate(StyleResolver& styleResolver, const CSSValue& value) { return downcast(value).convertToLength(styleResolver.state().cssToLengthConversionData()); } inline std::unique_ptr StyleBuilderConverter::convertScrollSnapPoints(StyleResolver& styleResolver, CSSValue& value) { auto points = std::make_unique(); if (is(value)) { ASSERT(downcast(value).getValueID() == CSSValueElements); points->usesElements = true; return points; } for (auto& currentValue : downcast(value)) { auto& itemValue = downcast(currentValue.get()); if (auto* lengthRepeat = itemValue.getLengthRepeatValue()) { if (auto* interval = lengthRepeat->interval()) { points->repeatOffset = parseSnapCoordinate(styleResolver, *interval); points->hasRepeat = true; break; } } points->offsets.append(parseSnapCoordinate(styleResolver, itemValue)); } return points; } inline LengthSize StyleBuilderConverter::convertSnapCoordinatePair(StyleResolver& styleResolver, CSSValue& value, size_t offset) { auto& valueList = downcast(value); return LengthSize(parseSnapCoordinate(styleResolver, *valueList.item(offset)), parseSnapCoordinate(styleResolver, *valueList.item(offset + 1))); } inline Vector StyleBuilderConverter::convertScrollSnapCoordinates(StyleResolver& styleResolver, CSSValue& value) { auto& valueList = downcast(value); ASSERT(!(valueList.length() % 2)); size_t pointCount = valueList.length() / 2; Vector coordinates; coordinates.reserveInitialCapacity(pointCount); for (size_t i = 0; i < pointCount; ++i) coordinates.uncheckedAppend(convertSnapCoordinatePair(styleResolver, valueList, i * 2)); return coordinates; } #endif #if ENABLE(CSS_GRID_LAYOUT) inline GridLength StyleBuilderConverter::createGridTrackBreadth(CSSPrimitiveValue& primitiveValue, StyleResolver& styleResolver) { if (primitiveValue.getValueID() == CSSValueWebkitMinContent) return Length(MinContent); if (primitiveValue.getValueID() == CSSValueWebkitMaxContent) return Length(MaxContent); // Fractional unit. if (primitiveValue.isFlex()) return GridLength(primitiveValue.getDoubleValue()); return primitiveValue.convertToLength(styleResolver.state().cssToLengthConversionData()); } inline GridTrackSize StyleBuilderConverter::createGridTrackSize(CSSValue& value, StyleResolver& styleResolver) { if (is(value)) return GridTrackSize(createGridTrackBreadth(downcast(value), styleResolver)); CSSValueList& arguments = *downcast(value).arguments(); ASSERT_WITH_SECURITY_IMPLICATION(arguments.length() == 2); GridLength minTrackBreadth(createGridTrackBreadth(downcast(*arguments.itemWithoutBoundsCheck(0)), styleResolver)); GridLength maxTrackBreadth(createGridTrackBreadth(downcast(*arguments.itemWithoutBoundsCheck(1)), styleResolver)); return GridTrackSize(minTrackBreadth, maxTrackBreadth); } inline bool StyleBuilderConverter::createGridTrackList(CSSValue& value, Vector& trackSizes, NamedGridLinesMap& namedGridLines, OrderedNamedGridLinesMap& orderedNamedGridLines, StyleResolver& styleResolver) { // Handle 'none'. if (is(value)) return downcast(value).getValueID() == CSSValueNone; if (!is(value)) return false; unsigned currentNamedGridLine = 0; for (auto& currentValue : downcast(value)) { if (is(currentValue.get())) { for (auto& currentGridLineName : downcast(currentValue.get())) { String namedGridLine = downcast(currentGridLineName.get()).getStringValue(); NamedGridLinesMap::AddResult result = namedGridLines.add(namedGridLine, Vector()); result.iterator->value.append(currentNamedGridLine); OrderedNamedGridLinesMap::AddResult orderedResult = orderedNamedGridLines.add(currentNamedGridLine, Vector()); orderedResult.iterator->value.append(namedGridLine); } continue; } ++currentNamedGridLine; trackSizes.append(createGridTrackSize(currentValue, styleResolver)); } // The parser should have rejected any without any as // this is not conformant to the syntax. ASSERT(!trackSizes.isEmpty()); return true; } inline bool StyleBuilderConverter::createGridPosition(CSSValue& value, GridPosition& position) { // We accept the specification's grammar: // auto | | [ && ? ] | [ span && [ || ] ] if (is(value)) { auto& primitiveValue = downcast(value); // We translate to during parsing as it makes handling it simpler. if (primitiveValue.isString()) { position.setNamedGridArea(primitiveValue.getStringValue()); return true; } ASSERT(primitiveValue.getValueID() == CSSValueAuto); return true; } auto& values = downcast(value); ASSERT(values.length()); auto it = values.begin(); CSSPrimitiveValue* currentValue = &downcast(it->get()); bool isSpanPosition = false; if (currentValue->getValueID() == CSSValueSpan) { isSpanPosition = true; ++it; currentValue = it != values.end() ? &downcast(it->get()) : nullptr; } int gridLineNumber = 0; if (currentValue && currentValue->isNumber()) { gridLineNumber = currentValue->getIntValue(); ++it; currentValue = it != values.end() ? &downcast(it->get()) : nullptr; } String gridLineName; if (currentValue && currentValue->isString()) { gridLineName = currentValue->getStringValue(); ++it; } ASSERT(it == values.end()); if (isSpanPosition) position.setSpanPosition(gridLineNumber ? gridLineNumber : 1, gridLineName); else position.setExplicitPosition(gridLineNumber, gridLineName); return true; } inline void StyleBuilderConverter::createImplicitNamedGridLinesFromGridArea(const NamedGridAreaMap& namedGridAreas, NamedGridLinesMap& namedGridLines, GridTrackSizingDirection direction) { for (auto& area : namedGridAreas) { GridSpan areaSpan = direction == ForRows ? area.value.rows : area.value.columns; { auto& startVector = namedGridLines.add(area.key + "-start", Vector()).iterator->value; startVector.append(areaSpan.resolvedInitialPosition().toInt()); std::sort(startVector.begin(), startVector.end()); } { auto& endVector = namedGridLines.add(area.key + "-end", Vector()).iterator->value; endVector.append(areaSpan.resolvedFinalPosition().toInt()); std::sort(endVector.begin(), endVector.end()); } } } inline GridTrackSize StyleBuilderConverter::convertGridTrackSize(StyleResolver& styleResolver, CSSValue& value) { return createGridTrackSize(value, styleResolver); } inline Optional StyleBuilderConverter::convertGridPosition(StyleResolver&, CSSValue& value) { GridPosition gridPosition; if (createGridPosition(value, gridPosition)) return gridPosition; return Nullopt; } inline GridAutoFlow StyleBuilderConverter::convertGridAutoFlow(StyleResolver&, CSSValue& value) { auto& list = downcast(value); if (!list.length()) return RenderStyle::initialGridAutoFlow(); auto& first = downcast(*list.item(0)); auto* second = downcast(list.item(1)); GridAutoFlow autoFlow = RenderStyle::initialGridAutoFlow(); switch (first.getValueID()) { case CSSValueRow: if (second && second->getValueID() == CSSValueDense) autoFlow = AutoFlowRowDense; else autoFlow = AutoFlowRow; break; case CSSValueColumn: if (second && second->getValueID() == CSSValueDense) autoFlow = AutoFlowColumnDense; else autoFlow = AutoFlowColumn; break; default: ASSERT_NOT_REACHED(); break; } return autoFlow; } #endif // ENABLE(CSS_GRID_LAYOUT) inline CSSToLengthConversionData StyleBuilderConverter::csstoLengthConversionDataWithTextZoomFactor(StyleResolver& styleResolver) { if (auto* frame = styleResolver.document().frame()) { float textZoomFactor = styleResolver.style()->textZoom() != TextZoomReset ? frame->textZoomFactor() : 1.0f; return styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(styleResolver.style()->effectiveZoom() * textZoomFactor); } return styleResolver.state().cssToLengthConversionData(); } inline Optional StyleBuilderConverter::convertWordSpacing(StyleResolver& styleResolver, CSSValue& value) { Optional wordSpacing; auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueNormal) wordSpacing = RenderStyle::initialWordSpacing(); else if (primitiveValue.isLength()) wordSpacing = primitiveValue.computeLength(csstoLengthConversionDataWithTextZoomFactor(styleResolver)); else if (primitiveValue.isPercentage()) wordSpacing = Length(clampTo(primitiveValue.getDoubleValue(), minValueForCssLength, maxValueForCssLength), Percent); else if (primitiveValue.isNumber()) wordSpacing = Length(primitiveValue.getDoubleValue(), Fixed); return wordSpacing; } inline Optional StyleBuilderConverter::convertPerspective(StyleResolver& styleResolver, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueNone) return 0.f; float perspective = -1; if (primitiveValue.isLength()) perspective = primitiveValue.computeLength(styleResolver.state().cssToLengthConversionData()); else if (primitiveValue.isNumber()) perspective = primitiveValue.getDoubleValue() * styleResolver.state().cssToLengthConversionData().zoom(); else ASSERT_NOT_REACHED(); return perspective < 0 ? Optional(Nullopt) : Optional(perspective); } inline Optional StyleBuilderConverter::convertMarqueeIncrement(StyleResolver& styleResolver, CSSValue& value) { Optional marqueeLength; auto& primitiveValue = downcast(value); switch (primitiveValue.getValueID()) { case CSSValueSmall: marqueeLength = Length(1, Fixed); // 1px. break; case CSSValueNormal: marqueeLength = Length(6, Fixed); // 6px. The WinIE default. break; case CSSValueLarge: marqueeLength = Length(36, Fixed); // 36px. break; case CSSValueInvalid: { Length length = primitiveValue.convertToLength(styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f)); if (!length.isUndefined()) marqueeLength = length; break; } default: break; } return marqueeLength; } inline Optional StyleBuilderConverter::convertFilterOperations(StyleResolver& styleResolver, CSSValue& value) { FilterOperations operations; if (styleResolver.createFilterOperations(value, operations)) return operations; return Nullopt; } inline FontFeatureSettings StyleBuilderConverter::convertFontFeatureSettings(StyleResolver&, CSSValue& value) { if (is(value)) { ASSERT(downcast(value).getValueID() == CSSValueNormal); return { }; } FontFeatureSettings settings; for (auto& item : downcast(value)) { auto& feature = downcast(item.get()); settings.insert(FontFeature(feature.tag(), feature.value())); } return settings; } #if PLATFORM(IOS) inline bool StyleBuilderConverter::convertTouchCallout(StyleResolver&, CSSValue& value) { return !equalLettersIgnoringASCIICase(downcast(value).getStringValue(), "none"); } #endif #if ENABLE(TOUCH_EVENTS) inline Color StyleBuilderConverter::convertTapHighlightColor(StyleResolver& styleResolver, CSSValue& value) { return styleResolver.colorFromPrimitiveValue(downcast(value)); } #endif #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) inline bool StyleBuilderConverter::convertOverflowScrolling(StyleResolver&, CSSValue& value) { return downcast(value).getValueID() == CSSValueTouch; } #endif inline SVGLength StyleBuilderConverter::convertSVGLength(StyleResolver&, CSSValue& value) { return SVGLength::fromCSSPrimitiveValue(downcast(value)); } inline Vector StyleBuilderConverter::convertSVGLengthVector(StyleResolver& styleResolver, CSSValue& value) { auto& valueList = downcast(value); Vector svgLengths; svgLengths.reserveInitialCapacity(valueList.length()); for (auto& item : valueList) svgLengths.uncheckedAppend(convertSVGLength(styleResolver, item)); return svgLengths; } inline Vector StyleBuilderConverter::convertStrokeDashArray(StyleResolver& styleResolver, CSSValue& value) { if (is(value)) { ASSERT(downcast(value).getValueID() == CSSValueNone); return SVGRenderStyle::initialStrokeDashArray(); } return convertSVGLengthVector(styleResolver, value); } inline PaintOrder StyleBuilderConverter::convertPaintOrder(StyleResolver&, CSSValue& value) { if (is(value)) { ASSERT(downcast(value).getValueID() == CSSValueNormal); return PaintOrderNormal; } auto& orderTypeList = downcast(value); switch (downcast(*orderTypeList.itemWithoutBoundsCheck(0)).getValueID()) { case CSSValueFill: return orderTypeList.length() > 1 ? PaintOrderFillMarkers : PaintOrderFill; case CSSValueStroke: return orderTypeList.length() > 1 ? PaintOrderStrokeMarkers : PaintOrderStroke; case CSSValueMarkers: return orderTypeList.length() > 1 ? PaintOrderMarkersStroke : PaintOrderMarkers; default: ASSERT_NOT_REACHED(); return PaintOrderNormal; } } inline float StyleBuilderConverter::convertOpacity(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); float opacity = primitiveValue.getFloatValue(); if (primitiveValue.isPercentage()) opacity /= 100.0f; return opacity; } inline String StyleBuilderConverter::convertSVGURIReference(StyleResolver& styleResolver, CSSValue& value) { String s; auto& primitiveValue = downcast(value); if (primitiveValue.isURI()) s = primitiveValue.getStringValue(); return SVGURIReference::fragmentIdentifierFromIRIString(s, styleResolver.document()); } inline Color StyleBuilderConverter::convertSVGColor(StyleResolver& styleResolver, CSSValue& value) { auto& svgColor = downcast(value); return svgColor.colorType() == SVGColor::SVG_COLORTYPE_CURRENTCOLOR ? styleResolver.style()->color() : svgColor.color(); } inline StyleSelfAlignmentData StyleBuilderConverter::convertSelfOrDefaultAlignmentData(StyleResolver&, CSSValue& value) { StyleSelfAlignmentData alignmentData = RenderStyle::initialSelfAlignment(); auto& primitiveValue = downcast(value); if (Pair* pairValue = primitiveValue.getPairValue()) { if (pairValue->first()->getValueID() == CSSValueLegacy) { alignmentData.setPositionType(LegacyPosition); alignmentData.setPosition(*pairValue->second()); } else { alignmentData.setPosition(*pairValue->first()); alignmentData.setOverflow(*pairValue->second()); } } else alignmentData.setPosition(primitiveValue); return alignmentData; } inline StyleContentAlignmentData StyleBuilderConverter::convertContentAlignmentData(StyleResolver&, CSSValue& value) { StyleContentAlignmentData alignmentData = RenderStyle::initialContentAlignment(); auto& contentValue = downcast(value); if (contentValue.distribution()->getValueID() != CSSValueInvalid) alignmentData.setDistribution(contentValue.distribution().get()); if (contentValue.position()->getValueID() != CSSValueInvalid) alignmentData.setPosition(contentValue.position().get()); if (contentValue.overflow()->getValueID() != CSSValueInvalid) alignmentData.setOverflow(contentValue.overflow().get()); return alignmentData; } inline EGlyphOrientation StyleBuilderConverter::convertGlyphOrientation(StyleResolver&, CSSValue& value) { float angle = fabsf(fmodf(downcast(value).getFloatValue(), 360.0f)); if (angle <= 45.0f || angle > 315.0f) return GO_0DEG; if (angle > 45.0f && angle <= 135.0f) return GO_90DEG; if (angle > 135.0f && angle <= 225.0f) return GO_180DEG; return GO_270DEG; } inline EGlyphOrientation StyleBuilderConverter::convertGlyphOrientationOrAuto(StyleResolver& styleResolver, CSSValue& value) { if (downcast(value).getValueID() == CSSValueAuto) return GO_AUTO; return convertGlyphOrientation(styleResolver, value); } inline Optional StyleBuilderConverter::convertLineHeight(StyleResolver& styleResolver, CSSValue& value, float multiplier) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueNormal) return RenderStyle::initialLineHeight(); if (primitiveValue.isLength()) { Length length = primitiveValue.computeLength(StyleBuilderConverter::csstoLengthConversionDataWithTextZoomFactor(styleResolver)); if (multiplier != 1.f) length = Length(length.value() * multiplier, Fixed); return length; } if (primitiveValue.isPercentage()) { // FIXME: percentage should not be restricted to an integer here. return Length((styleResolver.style()->computedFontSize() * primitiveValue.getIntValue()) / 100, Fixed); } if (primitiveValue.isNumber()) { // FIXME: number and percentage values should produce the same type of Length (ie. Fixed or Percent). return Length(primitiveValue.getDoubleValue() * multiplier * 100.0, Percent); } return Nullopt; } FontSynthesis StyleBuilderConverter::convertFontSynthesis(StyleResolver&, CSSValue& value) { if (is(value)) { ASSERT(downcast(value).getValueID() == CSSValueNone); return FontSynthesisNone; } FontSynthesis result = FontSynthesisNone; ASSERT(is(value)); for (CSSValue& v : downcast(value)) { switch (downcast(v).getValueID()) { case CSSValueWeight: result |= FontSynthesisWeight; break; case CSSValueStyle: result |= FontSynthesisStyle; break; default: ASSERT_NOT_REACHED(); break; } } return result; } inline BreakBetween StyleBuilderConverter::convertPageBreakBetween(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueAlways) return PageBreakBetween; if (primitiveValue.getValueID() == CSSValueAvoid) return AvoidPageBreakBetween; return primitiveValue; } inline BreakInside StyleBuilderConverter::convertPageBreakInside(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueAvoid) return AvoidPageBreakInside; return primitiveValue; } inline BreakBetween StyleBuilderConverter::convertColumnBreakBetween(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueAlways) return ColumnBreakBetween; if (primitiveValue.getValueID() == CSSValueAvoid) return AvoidColumnBreakBetween; return primitiveValue; } inline BreakInside StyleBuilderConverter::convertColumnBreakInside(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueAvoid) return AvoidColumnBreakInside; return primitiveValue; } #if ENABLE(CSS_REGIONS) inline BreakBetween StyleBuilderConverter::convertRegionBreakBetween(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueAlways) return RegionBreakBetween; if (primitiveValue.getValueID() == CSSValueAvoid) return AvoidRegionBreakBetween; return primitiveValue; } inline BreakInside StyleBuilderConverter::convertRegionBreakInside(StyleResolver&, CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.getValueID() == CSSValueAvoid) return AvoidRegionBreakInside; return primitiveValue; } #endif inline HangingPunctuation StyleBuilderConverter::convertHangingPunctuation(StyleResolver&, CSSValue& value) { HangingPunctuation result = RenderStyle::initialHangingPunctuation(); if (is(value)) { for (auto& currentValue : downcast(value)) result |= downcast(currentValue.get()); } return result; } } // namespace WebCore #endif // StyleBuilderConverter_h