/* * 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. */ #pragma once #include "BasicShapeFunctions.h" #include "CSSCalcValue.h" #include "CSSContentDistributionValue.h" #include "CSSFontFeatureValue.h" #include "CSSFontStyleValue.h" #include "CSSFontVariationValue.h" #include "CSSFunctionValue.h" #include "CSSGridAutoRepeatValue.h" #include "CSSGridIntegerRepeatValue.h" #include "CSSGridLineNamesValue.h" #include "CSSImageGeneratorValue.h" #include "CSSImageSetValue.h" #include "CSSImageValue.h" #include "CSSPrimitiveValue.h" #include "CSSPrimitiveValueMappings.h" #include "CSSReflectValue.h" #include "CalcExpressionLength.h" #include "CalcExpressionOperation.h" #include "FontSelectionValueInlines.h" #include "Frame.h" #include "GridPositionsResolver.h" #include "Length.h" #include "Pair.h" #include "QuotesData.h" #include "RuntimeEnabledFeatures.h" #include "SVGURIReference.h" #include "Settings.h" #include "StyleBuilderState.h" #include "StyleScrollSnapPoints.h" #include "TabSize.h" #include "TouchAction.h" #include "TransformFunctions.h" namespace WebCore { namespace Style { // Note that we assume the CSS parser only allows valid CSSValue types. class BuilderConverter { public: static Length convertLength(const BuilderState&, const CSSValue&); static Length convertLengthOrAuto(const BuilderState&, const CSSValue&); static Length convertLengthSizing(const BuilderState&, const CSSValue&); static Length convertLengthMaxSizing(const BuilderState&, const CSSValue&); static TabSize convertTabSize(const BuilderState&, const CSSValue&); template static T convertComputedLength(BuilderState&, const CSSValue&); template static T convertLineWidth(BuilderState&, const CSSValue&); static float convertSpacing(BuilderState&, const CSSValue&); static LengthSize convertRadius(BuilderState&, const CSSValue&); static LengthPoint convertObjectPosition(BuilderState&, const CSSValue&); static OptionSet convertTextDecoration(BuilderState&, const CSSValue&); template static T convertNumber(BuilderState&, const CSSValue&); template static T convertNumberOrAuto(BuilderState&, const CSSValue&); static short convertWebkitHyphenateLimitLines(BuilderState&, const CSSValue&); template static NinePieceImage convertBorderImage(BuilderState&, CSSValue&); template static NinePieceImage convertBorderMask(BuilderState&, CSSValue&); template static RefPtr convertStyleImage(BuilderState&, CSSValue&); static ImageOrientation convertImageOrientation(BuilderState&, const CSSValue&); static TransformOperations convertTransform(BuilderState&, const CSSValue&); static RefPtr convertRotate(BuilderState&, const CSSValue&); static RefPtr convertScale(BuilderState&, const CSSValue&); static RefPtr convertTranslate(BuilderState&, const CSSValue&); #if ENABLE(DARK_MODE_CSS) static StyleColorScheme convertColorScheme(BuilderState&, const CSSValue&); #endif static String convertString(BuilderState&, const CSSValue&); static String convertStringOrAuto(BuilderState&, const CSSValue&); static String convertStringOrNone(BuilderState&, const CSSValue&); static OptionSet convertTextEmphasisPosition(BuilderState&, const CSSValue&); static TextAlignMode convertTextAlign(BuilderState&, const CSSValue&); static RefPtr convertClipPath(BuilderState&, const CSSValue&); static Resize convertResize(BuilderState&, const CSSValue&); static int convertMarqueeRepetition(BuilderState&, const CSSValue&); static int convertMarqueeSpeed(BuilderState&, const CSSValue&); static RefPtr convertQuotes(BuilderState&, const CSSValue&); static TextUnderlinePosition convertTextUnderlinePosition(BuilderState&, const CSSValue&); static TextUnderlineOffset convertTextUnderlineOffset(BuilderState&, const CSSValue&); static TextDecorationThickness convertTextDecorationThickness(BuilderState&, const CSSValue&); static RefPtr convertReflection(BuilderState&, const CSSValue&); static IntSize convertInitialLetter(BuilderState&, const CSSValue&); static float convertTextStrokeWidth(BuilderState&, const CSSValue&); static OptionSet convertLineBoxContain(BuilderState&, const CSSValue&); static OptionSet convertTextDecorationSkip(BuilderState&, const CSSValue&); static RefPtr convertShapeValue(BuilderState&, CSSValue&); static ScrollSnapType convertScrollSnapType(BuilderState&, const CSSValue&); static ScrollSnapAlign convertScrollSnapAlign(BuilderState&, const CSSValue&); static ScrollSnapStop convertScrollSnapStop(BuilderState&, const CSSValue&); static GridTrackSize convertGridTrackSize(BuilderState&, const CSSValue&); static Vector convertGridTrackSizeList(BuilderState&, const CSSValue&); static std::optional convertGridPosition(BuilderState&, const CSSValue&); static GridAutoFlow convertGridAutoFlow(BuilderState&, const CSSValue&); static std::optional convertWordSpacing(BuilderState&, const CSSValue&); static std::optional convertPerspective(BuilderState&, const CSSValue&); static std::optional convertMarqueeIncrement(BuilderState&, const CSSValue&); static std::optional convertFilterOperations(BuilderState&, const CSSValue&); #if PLATFORM(IOS_FAMILY) static bool convertTouchCallout(BuilderState&, const CSSValue&); #endif #if ENABLE(TOUCH_EVENTS) static Color convertTapHighlightColor(BuilderState&, const CSSValue&); #endif static OptionSet convertTouchAction(BuilderState&, const CSSValue&); #if ENABLE(OVERFLOW_SCROLLING_TOUCH) static bool convertOverflowScrolling(BuilderState&, const CSSValue&); #endif static FontFeatureSettings convertFontFeatureSettings(BuilderState&, const CSSValue&); static bool convertSmoothScrolling(BuilderState&, const CSSValue&); static FontSelectionValue convertFontWeightFromValue(const CSSValue&); static FontSelectionValue convertFontStretchFromValue(const CSSValue&); static std::optional convertFontStyleFromValue(const CSSValue&); static FontSelectionValue convertFontWeight(BuilderState&, const CSSValue&); static FontSelectionValue convertFontStretch(BuilderState&, const CSSValue&); static FontSelectionValue convertFontStyle(BuilderState&, const CSSValue&); static FontVariationSettings convertFontVariationSettings(BuilderState&, const CSSValue&); static SVGLengthValue convertSVGLengthValue(BuilderState&, const CSSValue&); static Vector convertSVGLengthVector(BuilderState&, const CSSValue&); static Vector convertStrokeDashArray(BuilderState&, const CSSValue&); static PaintOrder convertPaintOrder(BuilderState&, const CSSValue&); static float convertOpacity(BuilderState&, const CSSValue&); static String convertSVGURIReference(BuilderState&, const CSSValue&); static Color convertSVGColor(BuilderState&, const CSSValue&); static StyleSelfAlignmentData convertSelfOrDefaultAlignmentData(BuilderState&, const CSSValue&); static StyleContentAlignmentData convertContentAlignmentData(BuilderState&, const CSSValue&); static GlyphOrientation convertGlyphOrientation(BuilderState&, const CSSValue&); static GlyphOrientation convertGlyphOrientationOrAuto(BuilderState&, const CSSValue&); static std::optional convertLineHeight(BuilderState&, const CSSValue&, float multiplier = 1.f); static FontSynthesis convertFontSynthesis(BuilderState&, const CSSValue&); static BreakBetween convertPageBreakBetween(BuilderState&, const CSSValue&); static BreakInside convertPageBreakInside(BuilderState&, const CSSValue&); static BreakBetween convertColumnBreakBetween(BuilderState&, const CSSValue&); static BreakInside convertColumnBreakInside(BuilderState&, const CSSValue&); static OptionSet convertHangingPunctuation(BuilderState&, const CSSValue&); static OptionSet convertSpeakAs(BuilderState&, const CSSValue&); static Length convertPositionComponentX(BuilderState&, const CSSValue&); static Length convertPositionComponentY(BuilderState&, const CSSValue&); static GapLength convertGapLength(BuilderState&, const CSSValue&); private: friend class BuilderCustom; static Length convertToRadiusLength(CSSToLengthConversionData&, const CSSPrimitiveValue&); static OptionSet valueToEmphasisPosition(const CSSPrimitiveValue&); static OptionSet valueToDecorationSkip(const CSSPrimitiveValue&); static Length parseSnapCoordinate(BuilderState&, const CSSValue&); #if ENABLE(DARK_MODE_CSS) static void updateColorScheme(const CSSPrimitiveValue&, StyleColorScheme&); #endif static Length convertTo100PercentMinusLength(const Length&); template static Length convertPositionComponent(BuilderState&, const CSSPrimitiveValue&); static GridLength createGridTrackBreadth(const CSSPrimitiveValue&, BuilderState&); static GridTrackSize createGridTrackSize(const CSSValue&, BuilderState&); struct TracksData; static bool createGridTrackList(const CSSValue&, TracksData&, BuilderState&); static bool createGridPosition(const CSSValue&, GridPosition&); static void createImplicitNamedGridLinesFromGridArea(const NamedGridAreaMap&, NamedGridLinesMap&, GridTrackSizingDirection); static CSSToLengthConversionData csstoLengthConversionDataWithTextZoomFactor(BuilderState&); }; inline Length BuilderConverter::convertLength(const BuilderState& builderState, const CSSValue& value) { auto& primitiveValue = downcast(value); CSSToLengthConversionData conversionData = builderState.useSVGZoomRulesForLength() ? builderState.cssToLengthConversionData().copyWithAdjustedZoom(1.0f) : builderState.cssToLengthConversionData(); if (primitiveValue.isLength()) { Length length = primitiveValue.computeLength(conversionData); length.setHasQuirk(primitiveValue.primitiveType() == CSSUnitType::CSS_QUIRKY_EMS); return length; } if (primitiveValue.isPercentage()) return Length(primitiveValue.doubleValue(), LengthType::Percent); if (primitiveValue.isCalculatedPercentageWithLength()) return Length(primitiveValue.cssCalcValue()->createCalculationValue(conversionData)); ASSERT_NOT_REACHED(); return Length(0, LengthType::Fixed); } inline Length BuilderConverter::convertLengthOrAuto(const BuilderState& builderState, const CSSValue& value) { if (downcast(value).valueID() == CSSValueAuto) return Length(LengthType::Auto); return convertLength(builderState, value); } inline Length BuilderConverter::convertLengthSizing(const BuilderState& builderState, const CSSValue& value) { auto& primitiveValue = downcast(value); switch (primitiveValue.valueID()) { case CSSValueInvalid: return convertLength(builderState, value); case CSSValueIntrinsic: return Length(LengthType::Intrinsic); case CSSValueMinIntrinsic: return Length(LengthType::MinIntrinsic); case CSSValueMinContent: case CSSValueWebkitMinContent: return Length(LengthType::MinContent); case CSSValueMaxContent: case CSSValueWebkitMaxContent: return Length(LengthType::MaxContent); case CSSValueWebkitFillAvailable: return Length(LengthType::FillAvailable); case CSSValueFitContent: case CSSValueWebkitFitContent: return Length(LengthType::FitContent); case CSSValueAuto: return Length(LengthType::Auto); default: ASSERT_NOT_REACHED(); return Length(); } } inline Length BuilderConverter::convertLengthMaxSizing(const BuilderState& builderState, const CSSValue& value) { if (downcast(value).valueID() == CSSValueNone) return Length(LengthType::Undefined); return convertLengthSizing(builderState, value); } inline TabSize BuilderConverter::convertTabSize(const BuilderState& builderState, const CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.isNumber()) return TabSize(primitiveValue.floatValue(), SpaceValueType); return TabSize(primitiveValue.computeLength(builderState.cssToLengthConversionData()), LengthValueType); } template inline T BuilderConverter::convertComputedLength(BuilderState& builderState, const CSSValue& value) { return downcast(value).computeLength(builderState.cssToLengthConversionData()); } template inline T BuilderConverter::convertLineWidth(BuilderState& builderState, const CSSValue& value) { auto& primitiveValue = downcast(value); switch (primitiveValue.valueID()) { 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(builderState, value); if (builderState.style().effectiveZoom() < 1.0f && result < 1.0) { T originalLength = primitiveValue.computeLength(builderState.cssToLengthConversionData().copyWithAdjustedZoom(1.0)); if (originalLength >= 1.0) return 1; } float minimumLineWidth = 1 / builderState.document().deviceScaleFactor(); if (result > 0 && result < minimumLineWidth) return minimumLineWidth; return floorToDevicePixel(result, builderState.document().deviceScaleFactor()); } default: ASSERT_NOT_REACHED(); return 0; } } inline float BuilderConverter::convertSpacing(BuilderState& builderState, const CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.valueID() == CSSValueNormal) return 0.f; CSSToLengthConversionData conversionData = builderState.useSVGZoomRulesForLength() ? builderState.cssToLengthConversionData().copyWithAdjustedZoom(1.0f) : builderState.cssToLengthConversionData(); return primitiveValue.computeLength(conversionData); } inline Length BuilderConverter::convertToRadiusLength(CSSToLengthConversionData& conversionData, const CSSPrimitiveValue& value) { if (value.isPercentage()) return Length(value.doubleValue(), LengthType::Percent); if (value.isCalculatedPercentageWithLength()) return Length(value.cssCalcValue()->createCalculationValue(conversionData)); return value.computeLength(conversionData); } inline LengthSize BuilderConverter::convertRadius(BuilderState& builderState, const CSSValue& value) { auto* pair = downcast(value).pairValue(); if (!pair || !pair->first() || !pair->second()) return { { 0, LengthType::Fixed }, { 0, LengthType::Fixed } }; CSSToLengthConversionData conversionData = builderState.cssToLengthConversionData(); LengthSize radius { convertToRadiusLength(conversionData, *pair->first()), convertToRadiusLength(conversionData, *pair->second()) }; ASSERT(!radius.width.isNegative()); ASSERT(!radius.height.isNegative()); return radius; } inline Length BuilderConverter::convertTo100PercentMinusLength(const Length& length) { if (length.isPercent()) return Length(100 - length.value(), LengthType::Percent); // Turn this into a calc expression: calc(100% - length) Vector> lengths; lengths.reserveInitialCapacity(2); lengths.uncheckedAppend(makeUnique(Length(100, LengthType::Percent))); lengths.uncheckedAppend(makeUnique(length)); auto op = makeUnique(WTFMove(lengths), CalcOperator::Subtract); return Length(CalculationValue::create(WTFMove(op), ValueRange::All)); } inline Length BuilderConverter::convertPositionComponentX(BuilderState& builderState, const CSSValue& value) { return convertPositionComponent(builderState, downcast(value)); } inline Length BuilderConverter::convertPositionComponentY(BuilderState& builderState, const CSSValue& value) { return convertPositionComponent(builderState, downcast(value)); } template inline Length BuilderConverter::convertPositionComponent(BuilderState& builderState, const CSSPrimitiveValue& value) { Length length; auto* lengthValue = &value; bool relativeToTrailingEdge = false; if (value.isPair()) { auto& first = *value.pairValue()->first(); if (first.valueID() == CSSValueRight || first.valueID() == CSSValueBottom) relativeToTrailingEdge = true; lengthValue = value.pairValue()->second(); } if (value.isValueID()) { switch (value.valueID()) { case cssValueFor0: return Length(0, LengthType::Percent); case cssValueFor100: return Length(100, LengthType::Percent); case CSSValueCenter: return Length(50, LengthType::Percent); default: ASSERT_NOT_REACHED(); } } length = convertLength(builderState, *lengthValue); if (relativeToTrailingEdge) length = convertTo100PercentMinusLength(length); return length; } inline LengthPoint BuilderConverter::convertObjectPosition(BuilderState& builderState, const CSSValue& value) { auto& primitiveValue = downcast(value); Pair* pair = primitiveValue.pairValue(); if (!pair || !pair->first() || !pair->second()) return RenderStyle::initialObjectPosition(); Length lengthX = convertPositionComponent(builderState, *pair->first()); Length lengthY = convertPositionComponent(builderState, *pair->second()); return LengthPoint(lengthX, lengthY); } inline OptionSet BuilderConverter::convertTextDecoration(BuilderState&, const CSSValue& value) { auto result = RenderStyle::initialTextDecoration(); if (is(value)) { for (auto& currentValue : downcast(value)) result.add(downcast(currentValue.get())); } return result; } template inline T BuilderConverter::convertNumber(BuilderState&, const CSSValue& value) { return downcast(value).value(CSSUnitType::CSS_NUMBER); } template inline T BuilderConverter::convertNumberOrAuto(BuilderState& builderState, const CSSValue& value) { if (downcast(value).valueID() == CSSValueAuto) return -1; return convertNumber(builderState, value); } inline short BuilderConverter::convertWebkitHyphenateLimitLines(BuilderState&, const CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.valueID() == CSSValueNoLimit) return -1; return primitiveValue.value(CSSUnitType::CSS_NUMBER); } template inline NinePieceImage BuilderConverter::convertBorderImage(BuilderState& builderState, CSSValue& value) { NinePieceImage image; builderState.styleMap().mapNinePieceImage(property, &value, image); return image; } template inline NinePieceImage BuilderConverter::convertBorderMask(BuilderState& builderState, CSSValue& value) { NinePieceImage image(NinePieceImage::Type::Mask); builderState.styleMap().mapNinePieceImage(property, &value, image); return image; } template inline RefPtr BuilderConverter::convertStyleImage(BuilderState& builderState, CSSValue& value) { return builderState.createStyleImage(value); } inline ImageOrientation BuilderConverter::convertImageOrientation(BuilderState&, const CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.valueID() == CSSValueFromImage) return ImageOrientation::FromImage; return ImageOrientation::None; } inline TransformOperations BuilderConverter::convertTransform(BuilderState& builderState, const CSSValue& value) { TransformOperations operations; transformsForValue(value, builderState.cssToLengthConversionData(), operations); return operations; } inline RefPtr BuilderConverter::convertTranslate(BuilderState& builderState, const CSSValue& value) { return translateForValue(value, builderState.cssToLengthConversionData()); } inline RefPtr BuilderConverter::convertRotate(BuilderState&, const CSSValue& value) { return rotateForValue(value); } inline RefPtr BuilderConverter::convertScale(BuilderState&, const CSSValue& value) { return scaleForValue(value); } #if ENABLE(DARK_MODE_CSS) inline void BuilderConverter::updateColorScheme(const CSSPrimitiveValue& primitiveValue, StyleColorScheme& colorScheme) { ASSERT(primitiveValue.isValueID()); switch (primitiveValue.valueID()) { case CSSValueAuto: colorScheme = StyleColorScheme(); break; case CSSValueOnly: colorScheme.setAllowsTransformations(false); break; case CSSValueLight: colorScheme.add(ColorScheme::Light); break; case CSSValueDark: colorScheme.add(ColorScheme::Dark); break; default: // Unknown identifiers are allowed and ignored. break; } } inline StyleColorScheme BuilderConverter::convertColorScheme(BuilderState&, const CSSValue& value) { StyleColorScheme colorScheme; if (is(value)) { for (auto& currentValue : downcast(value)) updateColorScheme(downcast(currentValue.get()), colorScheme); } else if (is(value)) updateColorScheme(downcast(value), colorScheme); // If the value was just "only", that is synonymous for "only light". if (colorScheme.isOnly()) colorScheme.add(ColorScheme::Light); return colorScheme; } #endif inline String BuilderConverter::convertString(BuilderState&, const CSSValue& value) { return downcast(value).stringValue(); } inline String BuilderConverter::convertStringOrAuto(BuilderState& builderState, const CSSValue& value) { if (downcast(value).valueID() == CSSValueAuto) return nullAtom(); return convertString(builderState, value); } inline String BuilderConverter::convertStringOrNone(BuilderState& builderState, const CSSValue& value) { if (downcast(value).valueID() == CSSValueNone) return nullAtom(); return convertString(builderState, value); } inline OptionSet BuilderConverter::valueToEmphasisPosition(const CSSPrimitiveValue& primitiveValue) { ASSERT(primitiveValue.isValueID()); switch (primitiveValue.valueID()) { case CSSValueOver: return TextEmphasisPosition::Over; case CSSValueUnder: return TextEmphasisPosition::Under; case CSSValueLeft: return TextEmphasisPosition::Left; case CSSValueRight: return TextEmphasisPosition::Right; default: break; } ASSERT_NOT_REACHED(); return RenderStyle::initialTextEmphasisPosition(); } inline OptionSet BuilderConverter::convertTextEmphasisPosition(BuilderState&, const CSSValue& value) { if (is(value)) return valueToEmphasisPosition(downcast(value)); OptionSet position; for (auto& currentValue : downcast(value)) position.add(valueToEmphasisPosition(downcast(currentValue.get()))); return position; } inline TextAlignMode BuilderConverter::convertTextAlign(BuilderState& builderState, const CSSValue& value) { auto& primitiveValue = downcast(value); ASSERT(primitiveValue.isValueID()); if (primitiveValue.valueID() != CSSValueWebkitMatchParent) return primitiveValue; auto& parentStyle = builderState.parentStyle(); if (parentStyle.textAlign() == TextAlignMode::Start) return parentStyle.isLeftToRightDirection() ? TextAlignMode::Left : TextAlignMode::Right; if (parentStyle.textAlign() == TextAlignMode::End) return parentStyle.isLeftToRightDirection() ? TextAlignMode::Right : TextAlignMode::Left; return parentStyle.textAlign(); } inline RefPtr BuilderConverter::convertClipPath(BuilderState& builderState, const CSSValue& value) { if (is(value)) { auto& primitiveValue = downcast(value); if (primitiveValue.primitiveType() == CSSUnitType::CSS_URI) { String cssURLValue = primitiveValue.stringValue(); String fragment = SVGURIReference::fragmentIdentifierFromIRIString(cssURLValue, builderState.document()); // FIXME: It doesn't work with external SVG references (see https://bugs.webkit.org/show_bug.cgi?id=126133) return ReferenceClipPathOperation::create(cssURLValue, fragment); } ASSERT(primitiveValue.valueID() == CSSValueNone); return nullptr; } CSSBoxType referenceBox = CSSBoxType::BoxMissing; RefPtr operation; for (auto& currentValue : downcast(value)) { auto& primitiveValue = downcast(currentValue.get()); if (primitiveValue.isShape()) { ASSERT(!operation); operation = ShapeClipPathOperation::create( basicShapeForValue(builderState.cssToLengthConversionData(), *primitiveValue.shapeValue(), builderState.style().effectiveZoom())); } else { ASSERT(primitiveValue.valueID() == CSSValueContentBox || primitiveValue.valueID() == CSSValueBorderBox || primitiveValue.valueID() == CSSValuePaddingBox || primitiveValue.valueID() == CSSValueMarginBox || primitiveValue.valueID() == CSSValueFillBox || primitiveValue.valueID() == CSSValueStrokeBox || primitiveValue.valueID() == CSSValueViewBox); ASSERT(referenceBox == CSSBoxType::BoxMissing); referenceBox = primitiveValue; } } if (operation) downcast(*operation).setReferenceBox(referenceBox); else { ASSERT(referenceBox != CSSBoxType::BoxMissing); operation = BoxClipPathOperation::create(referenceBox); } return operation; } inline Resize BuilderConverter::convertResize(BuilderState& builderState, const CSSValue& value) { auto& primitiveValue = downcast(value); Resize resize = Resize::None; if (primitiveValue.valueID() == CSSValueAuto) resize = builderState.document().settings().textAreasAreResizable() ? Resize::Both : Resize::None; else resize = primitiveValue; return resize; } inline int BuilderConverter::convertMarqueeRepetition(BuilderState&, const CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.valueID() == CSSValueInfinite) return -1; // -1 means repeat forever. ASSERT(primitiveValue.isNumber()); return primitiveValue.intValue(); } inline int BuilderConverter::convertMarqueeSpeed(BuilderState&, const CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.isTime()) return primitiveValue.computeTime(); // For scrollamount support. ASSERT(primitiveValue.isNumber()); return primitiveValue.intValue(); } inline RefPtr BuilderConverter::convertQuotes(BuilderState&, const CSSValue& value) { if (is(value)) { auto& primitiveValue = downcast(value); if (primitiveValue.valueID() == CSSValueNone) return QuotesData::create({ }); ASSERT(primitiveValue.valueID() == CSSValueAuto); return nullptr; } auto& list = downcast(value); Vector> quotes; quotes.reserveInitialCapacity(list.length() / 2); for (unsigned i = 0; i < list.length(); i += 2) { const CSSValue* first = list.itemWithoutBoundsCheck(i); // item() returns null if out of bounds so this is safe. const CSSValue* second = list.item(i + 1); if (!second) break; String startQuote = downcast(*first).stringValue(); String endQuote = downcast(*second).stringValue(); quotes.append(std::make_pair(startQuote, endQuote)); } return QuotesData::create(quotes); } inline TextUnderlinePosition BuilderConverter::convertTextUnderlinePosition(BuilderState&, const CSSValue& value) { ASSERT(is(value)); return downcast(value); } inline TextUnderlineOffset BuilderConverter::convertTextUnderlineOffset(BuilderState& builderState, const CSSValue& value) { ASSERT(is(value)); auto& primitiveValue = downcast(value); switch (primitiveValue.valueID()) { case CSSValueAuto: return TextUnderlineOffset::createWithAuto(); default: ASSERT(primitiveValue.isLength()); auto computedLength = convertComputedLength(builderState, primitiveValue); return TextUnderlineOffset::createWithLength(computedLength); } } inline TextDecorationThickness BuilderConverter::convertTextDecorationThickness(BuilderState& builderState, const CSSValue& value) { ASSERT(is(value)); auto& primitiveValue = downcast(value); switch (primitiveValue.valueID()) { case CSSValueAuto: return TextDecorationThickness::createWithAuto(); case CSSValueFromFont: return TextDecorationThickness::createFromFont(); default: ASSERT(primitiveValue.isLength()); auto computedLength = convertComputedLength(builderState, primitiveValue); return TextDecorationThickness::createWithLength(computedLength); } } inline RefPtr BuilderConverter::convertReflection(BuilderState& builderState, const CSSValue& value) { if (is(value)) { ASSERT(downcast(value).valueID() == CSSValueNone); return nullptr; } auto& reflectValue = downcast(value); auto reflection = StyleReflection::create(); reflection->setDirection(reflectValue.direction()); reflection->setOffset(reflectValue.offset().convertToLength(builderState.cssToLengthConversionData())); NinePieceImage mask(NinePieceImage::Type::Mask); builderState.styleMap().mapNinePieceImage(CSSPropertyWebkitBoxReflect, reflectValue.mask(), mask); reflection->setMask(mask); return reflection; } inline IntSize BuilderConverter::convertInitialLetter(BuilderState&, const CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.valueID() == CSSValueNormal) return IntSize(); Pair* pair = primitiveValue.pairValue(); ASSERT(pair); ASSERT(pair->first()); ASSERT(pair->second()); return IntSize(pair->first()->intValue(), pair->second()->intValue()); } inline float BuilderConverter::convertTextStrokeWidth(BuilderState& builderState, const CSSValue& value) { auto& primitiveValue = downcast(value); float width = 0; switch (primitiveValue.valueID()) { case CSSValueThin: case CSSValueMedium: case CSSValueThick: { double result = 1.0 / 48; if (primitiveValue.valueID() == CSSValueMedium) result *= 3; else if (primitiveValue.valueID() == CSSValueThick) result *= 5; Ref emsValue(CSSPrimitiveValue::create(result, CSSUnitType::CSS_EMS)); width = convertComputedLength(builderState, emsValue); break; } case CSSValueInvalid: { width = convertComputedLength(builderState, primitiveValue); break; } default: ASSERT_NOT_REACHED(); return 0; } return width; } inline OptionSet BuilderConverter::convertLineBoxContain(BuilderState&, const CSSValue& value) { if (is(value)) { ASSERT(downcast(value).valueID() == CSSValueNone); return { }; } return downcast(value).value(); } inline OptionSet BuilderConverter::valueToDecorationSkip(const CSSPrimitiveValue& primitiveValue) { ASSERT(primitiveValue.isValueID()); switch (primitiveValue.valueID()) { case CSSValueAuto: return TextDecorationSkip::Auto; case CSSValueNone: return OptionSet { }; case CSSValueInk: return TextDecorationSkip::Ink; case CSSValueObjects: return TextDecorationSkip::Objects; default: break; } ASSERT_NOT_REACHED(); return OptionSet { }; } inline OptionSet BuilderConverter::convertTextDecorationSkip(BuilderState&, const CSSValue& value) { if (is(value)) return valueToDecorationSkip(downcast(value)); OptionSet skip; for (auto& currentValue : downcast(value)) skip.add(valueToDecorationSkip(downcast(currentValue.get()))); return skip; } static inline bool isImageShape(const CSSValue& value) { return is(value) || is(value) || is(value); } inline RefPtr BuilderConverter::convertShapeValue(BuilderState& builderState, CSSValue& value) { if (is(value)) { ASSERT(downcast(value).valueID() == CSSValueNone); return nullptr; } if (isImageShape(value)) return ShapeValue::create(builderState.createStyleImage(value).releaseNonNull()); RefPtr shape; CSSBoxType referenceBox = CSSBoxType::BoxMissing; for (auto& currentValue : downcast(value)) { auto& primitiveValue = downcast(currentValue.get()); if (primitiveValue.isShape()) shape = basicShapeForValue(builderState.cssToLengthConversionData(), *primitiveValue.shapeValue()); else if (primitiveValue.valueID() == CSSValueContentBox || primitiveValue.valueID() == CSSValueBorderBox || primitiveValue.valueID() == CSSValuePaddingBox || primitiveValue.valueID() == CSSValueMarginBox) referenceBox = primitiveValue; else { ASSERT_NOT_REACHED(); return nullptr; } } if (shape) return ShapeValue::create(shape.releaseNonNull(), referenceBox); if (referenceBox != CSSBoxType::BoxMissing) return ShapeValue::create(referenceBox); ASSERT_NOT_REACHED(); return nullptr; } inline ScrollSnapType BuilderConverter::convertScrollSnapType(BuilderState&, const CSSValue& value) { ScrollSnapType type; auto& values = downcast(value); auto& firstValue = downcast(*values.item(0)); if (firstValue.valueID() == CSSValueNone) return type; type.axis = firstValue; if (values.length() == 2) type.strictness = downcast(*values.item(1)); else type.strictness = ScrollSnapStrictness::Proximity; return type; } inline ScrollSnapAlign BuilderConverter::convertScrollSnapAlign(BuilderState&, const CSSValue& value) { auto& values = downcast(value); ScrollSnapAlign alignment; alignment.blockAlign = downcast(*values.item(0)); if (values.length() == 1) alignment.inlineAlign = alignment.blockAlign; else alignment.inlineAlign = downcast(*values.item(1)); return alignment; } inline ScrollSnapStop BuilderConverter::convertScrollSnapStop(BuilderState&, const CSSValue& value) { ASSERT(is(value)); return downcast(value); } inline GridLength BuilderConverter::createGridTrackBreadth(const CSSPrimitiveValue& primitiveValue, BuilderState& builderState) { if (primitiveValue.valueID() == CSSValueMinContent || primitiveValue.valueID() == CSSValueWebkitMinContent) return Length(LengthType::MinContent); if (primitiveValue.valueID() == CSSValueMaxContent || primitiveValue.valueID() == CSSValueWebkitMaxContent) return Length(LengthType::MaxContent); // Fractional unit. if (primitiveValue.isFlex()) return GridLength(primitiveValue.doubleValue()); return primitiveValue.convertToLength(builderState.cssToLengthConversionData()); } inline GridTrackSize BuilderConverter::createGridTrackSize(const CSSValue& value, BuilderState& builderState) { if (is(value)) return GridTrackSize(createGridTrackBreadth(downcast(value), builderState)); ASSERT(is(value)); const auto& function = downcast(value); if (function.length() == 1) return GridTrackSize(createGridTrackBreadth(downcast(*function.itemWithoutBoundsCheck(0)), builderState), FitContentTrackSizing); RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(function.length() == 2); GridLength minTrackBreadth(createGridTrackBreadth(downcast(*function.itemWithoutBoundsCheck(0)), builderState)); GridLength maxTrackBreadth(createGridTrackBreadth(downcast(*function.itemWithoutBoundsCheck(1)), builderState)); return GridTrackSize(minTrackBreadth, maxTrackBreadth); } static void createGridLineNamesList(const CSSValue& value, unsigned currentNamedGridLine, NamedGridLinesMap& namedGridLines, OrderedNamedGridLinesMap& orderedNamedGridLines) { ASSERT(value.isGridLineNamesValue()); for (auto& namedGridLineValue : downcast(value)) { String namedGridLine = downcast(namedGridLineValue.get()).stringValue(); auto result = namedGridLines.add(namedGridLine, Vector()); result.iterator->value.append(currentNamedGridLine); auto orderedResult = orderedNamedGridLines.add(currentNamedGridLine, Vector()); orderedResult.iterator->value.append(namedGridLine); } } struct BuilderConverter::TracksData { WTF_MAKE_NONCOPYABLE(TracksData); WTF_MAKE_FAST_ALLOCATED; public: TracksData() = default; Vector m_trackSizes; NamedGridLinesMap m_namedGridLines; OrderedNamedGridLinesMap m_orderedNamedGridLines; Vector m_autoRepeatTrackSizes; NamedGridLinesMap m_autoRepeatNamedGridLines; OrderedNamedGridLinesMap m_autoRepeatOrderedNamedGridLines; unsigned m_autoRepeatInsertionPoint { RenderStyle::initialGridAutoRepeatInsertionPoint() }; AutoRepeatType m_autoRepeatType { RenderStyle::initialGridAutoRepeatType() }; }; inline bool BuilderConverter::createGridTrackList(const CSSValue& value, TracksData& tracksData, BuilderState& builderState) { // Handle 'none'. if (is(value)) return downcast(value).valueID() == CSSValueNone; if (!is(value)) return false; unsigned currentNamedGridLine = 0; auto handleLineNameOrTrackSize = [&](const CSSValue& currentValue) { if (is(currentValue)) createGridLineNamesList(currentValue, currentNamedGridLine, tracksData.m_namedGridLines, tracksData.m_orderedNamedGridLines); else { ++currentNamedGridLine; tracksData.m_trackSizes.append(createGridTrackSize(currentValue, builderState)); } }; for (auto& currentValue : downcast(value)) { if (is(currentValue)) { ASSERT(tracksData.m_autoRepeatTrackSizes.isEmpty()); unsigned autoRepeatIndex = 0; CSSValueID autoRepeatID = downcast(currentValue.get()).autoRepeatID(); ASSERT(autoRepeatID == CSSValueAutoFill || autoRepeatID == CSSValueAutoFit); tracksData.m_autoRepeatType = autoRepeatID == CSSValueAutoFill ? AutoRepeatType::Fill : AutoRepeatType::Fit; for (auto& autoRepeatValue : downcast(currentValue.get())) { if (is(autoRepeatValue)) { createGridLineNamesList(autoRepeatValue.get(), autoRepeatIndex, tracksData.m_autoRepeatNamedGridLines, tracksData.m_autoRepeatOrderedNamedGridLines); continue; } ++autoRepeatIndex; tracksData.m_autoRepeatTrackSizes.append(createGridTrackSize(autoRepeatValue.get(), builderState)); } tracksData.m_autoRepeatInsertionPoint = currentNamedGridLine++; continue; } if (is(currentValue)) { size_t repetitions = downcast(currentValue.get()).repetitions(); for (size_t i = 0; i < repetitions; ++i) { for (auto& integerRepeatValue : downcast(currentValue.get())) handleLineNameOrTrackSize(integerRepeatValue); } continue; } handleLineNameOrTrackSize(currentValue); } // The parser should have rejected any without any as // this is not conformant to the syntax. ASSERT(!tracksData.m_trackSizes.isEmpty() || !tracksData.m_autoRepeatTrackSizes.isEmpty()); return true; } inline bool BuilderConverter::createGridPosition(const CSSValue& value, GridPosition& position) { // We accept the specification's grammar: // auto | | [ && ? ] | [ span && [ || ] ] if (is(value)) { auto& primitiveValue = downcast(value); if (primitiveValue.isCustomIdent()) { position.setNamedGridArea(primitiveValue.stringValue()); return true; } ASSERT(primitiveValue.valueID() == CSSValueAuto); return true; } auto& values = downcast(value); ASSERT(values.length()); auto it = values.begin(); const CSSPrimitiveValue* currentValue = &downcast(it->get()); bool isSpanPosition = false; if (currentValue->valueID() == CSSValueSpan) { isSpanPosition = true; ++it; currentValue = it != values.end() ? &downcast(it->get()) : nullptr; } int gridLineNumber = 0; if (currentValue && currentValue->isNumber()) { gridLineNumber = currentValue->intValue(); ++it; currentValue = it != values.end() ? &downcast(it->get()) : nullptr; } String gridLineName; if (currentValue && currentValue->isCustomIdent()) { gridLineName = currentValue->stringValue(); ++it; } ASSERT(it == values.end()); if (isSpanPosition) position.setSpanPosition(gridLineNumber ? gridLineNumber : 1, gridLineName); else position.setExplicitPosition(gridLineNumber, gridLineName); return true; } inline void BuilderConverter::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.startLine()); std::sort(startVector.begin(), startVector.end()); } { auto& endVector = namedGridLines.add(area.key + "-end", Vector()).iterator->value; endVector.append(areaSpan.endLine()); std::sort(endVector.begin(), endVector.end()); } } } inline Vector BuilderConverter::convertGridTrackSizeList(BuilderState& builderState, const CSSValue& value) { ASSERT(value.isValueList()); auto& valueList = downcast(value); Vector trackSizes; trackSizes.reserveInitialCapacity(valueList.length()); for (auto& currValue : valueList) { ASSERT(!currValue->isGridLineNamesValue()); ASSERT(!currValue->isGridAutoRepeatValue()); ASSERT(!currValue->isGridIntegerRepeatValue()); trackSizes.uncheckedAppend(convertGridTrackSize(builderState, currValue)); } return trackSizes; } inline GridTrackSize BuilderConverter::convertGridTrackSize(BuilderState& builderState, const CSSValue& value) { return createGridTrackSize(value, builderState); } inline std::optional BuilderConverter::convertGridPosition(BuilderState&, const CSSValue& value) { GridPosition gridPosition; if (createGridPosition(value, gridPosition)) return gridPosition; return std::nullopt; } inline GridAutoFlow BuilderConverter::convertGridAutoFlow(BuilderState&, const 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; switch (first.valueID()) { case CSSValueRow: if (second && second->valueID() == CSSValueDense) autoFlow = AutoFlowRowDense; else autoFlow = AutoFlowRow; break; case CSSValueColumn: if (second && second->valueID() == CSSValueDense) autoFlow = AutoFlowColumnDense; else autoFlow = AutoFlowColumn; break; case CSSValueDense: if (second && second->valueID() == CSSValueColumn) autoFlow = AutoFlowColumnDense; else autoFlow = AutoFlowRowDense; break; default: ASSERT_NOT_REACHED(); autoFlow = RenderStyle::initialGridAutoFlow(); break; } return autoFlow; } inline float zoomWithTextZoomFactor(BuilderState& builderState) { if (auto* frame = builderState.document().frame()) { float textZoomFactor = builderState.style().textZoom() != TextZoom::Reset ? frame->textZoomFactor() : 1.0f; return builderState.style().effectiveZoom() * textZoomFactor; } return builderState.cssToLengthConversionData().zoom(); } inline CSSToLengthConversionData BuilderConverter::csstoLengthConversionDataWithTextZoomFactor(BuilderState& builderState) { float zoom = zoomWithTextZoomFactor(builderState); if (zoom == builderState.cssToLengthConversionData().zoom()) return builderState.cssToLengthConversionData(); return builderState.cssToLengthConversionData().copyWithAdjustedZoom(zoom); } inline std::optional BuilderConverter::convertWordSpacing(BuilderState& builderState, const CSSValue& value) { std::optional wordSpacing; auto& primitiveValue = downcast(value); if (primitiveValue.valueID() == CSSValueNormal) wordSpacing = RenderStyle::initialWordSpacing(); else if (primitiveValue.isLength()) wordSpacing = primitiveValue.computeLength(csstoLengthConversionDataWithTextZoomFactor(builderState)); else if (primitiveValue.isPercentage()) wordSpacing = Length(clampTo(primitiveValue.doubleValue(), minValueForCssLength, maxValueForCssLength), LengthType::Percent); else if (primitiveValue.isNumber()) wordSpacing = Length(primitiveValue.doubleValue(), LengthType::Fixed); return wordSpacing; } inline std::optional BuilderConverter::convertPerspective(BuilderState& builderState, const CSSValue& value) { auto& primitiveValue = downcast(value); if (primitiveValue.valueID() == CSSValueNone) return RenderStyle::initialPerspective(); float perspective = -1; if (primitiveValue.isLength()) perspective = primitiveValue.computeLength(builderState.cssToLengthConversionData()); else if (primitiveValue.isNumber()) perspective = primitiveValue.doubleValue() * builderState.cssToLengthConversionData().zoom(); else ASSERT_NOT_REACHED(); return perspective < 0 ? std::optional(std::nullopt) : std::optional(perspective); } inline std::optional BuilderConverter::convertMarqueeIncrement(BuilderState& builderState, const CSSValue& value) { Length length = downcast(value).convertToLength(builderState.cssToLengthConversionData().copyWithAdjustedZoom(1.0f)); if (length.isUndefined()) return std::nullopt; return length; } inline std::optional BuilderConverter::convertFilterOperations(BuilderState& builderState, const CSSValue& value) { FilterOperations operations; if (builderState.createFilterOperations(value, operations)) return operations; return std::nullopt; } inline FontFeatureSettings BuilderConverter::convertFontFeatureSettings(BuilderState&, const CSSValue& value) { if (is(value)) { ASSERT(downcast(value).valueID() == CSSValueNormal); return { }; } FontFeatureSettings settings; for (auto& item : downcast(value)) { auto& feature = downcast(item.get()); settings.insert(FontFeature(feature.tag(), feature.value())); } return settings; } inline FontSelectionValue BuilderConverter::convertFontWeightFromValue(const CSSValue& value) { ASSERT(is(value)); auto& primitiveValue = downcast(value); if (primitiveValue.isNumber()) return FontSelectionValue::clampFloat(primitiveValue.floatValue()); ASSERT(primitiveValue.isValueID()); switch (primitiveValue.valueID()) { case CSSValueNormal: return normalWeightValue(); case CSSValueBold: case CSSValueBolder: return boldWeightValue(); case CSSValueLighter: return lightWeightValue(); default: ASSERT_NOT_REACHED(); return normalWeightValue(); } } inline FontSelectionValue BuilderConverter::convertFontStretchFromValue(const CSSValue& value) { ASSERT(is(value)); const auto& primitiveValue = downcast(value); if (primitiveValue.isPercentage()) return FontSelectionValue::clampFloat(primitiveValue.floatValue()); ASSERT(primitiveValue.isValueID()); if (auto value = fontStretchValue(primitiveValue.valueID())) return value.value(); ASSERT_NOT_REACHED(); return normalStretchValue(); } // The input value needs to parsed and valid, this function returns std::nullopt if the input was "normal". inline std::optional BuilderConverter::convertFontStyleFromValue(const CSSValue& value) { ASSERT(is(value)); const auto& fontStyleValue = downcast(value); auto valueID = fontStyleValue.fontStyleValue->valueID(); if (valueID == CSSValueNormal) return std::nullopt; if (valueID == CSSValueItalic) return italicValue(); ASSERT(valueID == CSSValueOblique); if (auto* obliqueValue = fontStyleValue.obliqueValue.get()) return FontSelectionValue(obliqueValue->value(CSSUnitType::CSS_DEG)); return italicValue(); } inline FontSelectionValue BuilderConverter::convertFontWeight(BuilderState& builderState, const CSSValue& value) { ASSERT(is(value)); auto& primitiveValue = downcast(value); if (primitiveValue.isValueID()) { auto valueID = primitiveValue.valueID(); if (valueID == CSSValueBolder) return FontCascadeDescription::bolderWeight(builderState.parentStyle().fontDescription().weight()); if (valueID == CSSValueLighter) return FontCascadeDescription::lighterWeight(builderState.parentStyle().fontDescription().weight()); } return convertFontWeightFromValue(value); } inline FontSelectionValue BuilderConverter::convertFontStretch(BuilderState&, const CSSValue& value) { return convertFontStretchFromValue(value); } inline FontVariationSettings BuilderConverter::convertFontVariationSettings(BuilderState&, const CSSValue& value) { if (is(value)) { ASSERT(downcast(value).valueID() == CSSValueNormal); return { }; } FontVariationSettings settings; for (auto& item : downcast(value)) { auto& feature = downcast(item.get()); settings.insert({ feature.tag(), feature.value() }); } return settings; } #if PLATFORM(IOS_FAMILY) inline bool BuilderConverter::convertTouchCallout(BuilderState&, const CSSValue& value) { return !equalLettersIgnoringASCIICase(downcast(value).stringValue(), "none"); } #endif #if ENABLE(TOUCH_EVENTS) inline Color BuilderConverter::convertTapHighlightColor(BuilderState& builderState, const CSSValue& value) { return builderState.colorFromPrimitiveValue(downcast(value)); } #endif inline OptionSet BuilderConverter::convertTouchAction(BuilderState&, const CSSValue& value) { if (is(value)) return downcast(value); if (is(value)) { OptionSet touchActions; for (auto& currentValue : downcast(value)) { auto& primitiveValue = downcast(currentValue.get()); auto primitiveValueID = primitiveValue.valueID(); if (primitiveValueID != CSSValuePanX && primitiveValueID != CSSValuePanY && primitiveValueID != CSSValuePinchZoom) return RenderStyle::initialTouchActions(); touchActions.add(primitiveValue); } return touchActions; } return RenderStyle::initialTouchActions(); } #if ENABLE(OVERFLOW_SCROLLING_TOUCH) inline bool BuilderConverter::convertOverflowScrolling(BuilderState&, const CSSValue& value) { return downcast(value).valueID() == CSSValueTouch; } #endif inline bool BuilderConverter::convertSmoothScrolling(BuilderState&, const CSSValue& value) { return downcast(value).valueID() == CSSValueSmooth; } inline SVGLengthValue BuilderConverter::convertSVGLengthValue(BuilderState&, const CSSValue& value) { return SVGLengthValue::fromCSSPrimitiveValue(downcast(value)); } inline Vector BuilderConverter::convertSVGLengthVector(BuilderState& builderState, const CSSValue& value) { auto& valueList = downcast(value); Vector svgLengths; svgLengths.reserveInitialCapacity(valueList.length()); for (auto& item : valueList) svgLengths.uncheckedAppend(convertSVGLengthValue(builderState, item)); return svgLengths; } inline Vector BuilderConverter::convertStrokeDashArray(BuilderState& builderState, const CSSValue& value) { if (is(value)) { ASSERT(downcast(value).valueID() == CSSValueNone); return SVGRenderStyle::initialStrokeDashArray(); } return convertSVGLengthVector(builderState, value); } inline PaintOrder BuilderConverter::convertPaintOrder(BuilderState&, const CSSValue& value) { if (is(value)) { ASSERT(downcast(value).valueID() == CSSValueNormal); return PaintOrder::Normal; } auto& orderTypeList = downcast(value); switch (downcast(*orderTypeList.itemWithoutBoundsCheck(0)).valueID()) { case CSSValueFill: return orderTypeList.length() > 1 ? PaintOrder::FillMarkers : PaintOrder::Fill; case CSSValueStroke: return orderTypeList.length() > 1 ? PaintOrder::StrokeMarkers : PaintOrder::Stroke; case CSSValueMarkers: return orderTypeList.length() > 1 ? PaintOrder::MarkersStroke : PaintOrder::Markers; default: ASSERT_NOT_REACHED(); return PaintOrder::Normal; } } inline float BuilderConverter::convertOpacity(BuilderState&, const CSSValue& value) { auto& primitiveValue = downcast(value); float opacity = primitiveValue.floatValue(); if (primitiveValue.isPercentage()) opacity /= 100.0f; return std::max(0.0f, std::min(1.0f, opacity)); } inline String BuilderConverter::convertSVGURIReference(BuilderState& builderState, const CSSValue& value) { String s; auto& primitiveValue = downcast(value); if (primitiveValue.isURI()) s = primitiveValue.stringValue(); return SVGURIReference::fragmentIdentifierFromIRIString(s, builderState.document()); } inline Color BuilderConverter::convertSVGColor(BuilderState& builderState, const CSSValue& value) { return builderState.colorFromPrimitiveValue(downcast(value)); } inline StyleSelfAlignmentData BuilderConverter::convertSelfOrDefaultAlignmentData(BuilderState&, const CSSValue& value) { StyleSelfAlignmentData alignmentData = RenderStyle::initialSelfAlignment(); auto& primitiveValue = downcast(value); if (Pair* pairValue = primitiveValue.pairValue()) { if (pairValue->first()->valueID() == CSSValueLegacy) { alignmentData.setPositionType(ItemPositionType::Legacy); alignmentData.setPosition(*pairValue->second()); } else if (pairValue->first()->valueID() == CSSValueFirst) alignmentData.setPosition(ItemPosition::Baseline); else if (pairValue->first()->valueID() == CSSValueLast) alignmentData.setPosition(ItemPosition::LastBaseline); else { alignmentData.setOverflow(*pairValue->first()); alignmentData.setPosition(*pairValue->second()); } } else alignmentData.setPosition(primitiveValue); return alignmentData; } inline StyleContentAlignmentData BuilderConverter::convertContentAlignmentData(BuilderState&, const CSSValue& value) { StyleContentAlignmentData alignmentData = RenderStyle::initialContentAlignment(); if (!is(value)) return alignmentData; auto& contentValue = downcast(value); if (contentValue.distribution()->valueID() != CSSValueInvalid) alignmentData.setDistribution(contentValue.distribution().get()); if (contentValue.position()->valueID() != CSSValueInvalid) alignmentData.setPosition(contentValue.position().get()); if (contentValue.overflow()->valueID() != CSSValueInvalid) alignmentData.setOverflow(contentValue.overflow().get()); return alignmentData; } inline GlyphOrientation BuilderConverter::convertGlyphOrientation(BuilderState&, const CSSValue& value) { float angle = fabsf(fmodf(downcast(value).floatValue(), 360.0f)); if (angle <= 45.0f || angle > 315.0f) return GlyphOrientation::Degrees0; if (angle > 45.0f && angle <= 135.0f) return GlyphOrientation::Degrees90; if (angle > 135.0f && angle <= 225.0f) return GlyphOrientation::Degrees180; return GlyphOrientation::Degrees270; } inline GlyphOrientation BuilderConverter::convertGlyphOrientationOrAuto(BuilderState& builderState, const CSSValue& value) { if (downcast(value).valueID() == CSSValueAuto) return GlyphOrientation::Auto; return convertGlyphOrientation(builderState, value); } inline std::optional BuilderConverter::convertLineHeight(BuilderState& builderState, const CSSValue& value, float multiplier) { auto& primitiveValue = downcast(value); if (primitiveValue.valueID() == CSSValueNormal) return RenderStyle::initialLineHeight(); if (primitiveValue.isLength()) { auto conversionData = builderState.cssToLengthConversionData().copyWithAdjustedZoomAndPropertyToCompute(zoomWithTextZoomFactor(builderState), CSSPropertyLineHeight); Length length = primitiveValue.computeLength(conversionData); if (multiplier != 1.f) length = Length(length.value() * multiplier, LengthType::Fixed); return length; } // Line-height percentages need to inherit as if they were Fixed pixel values. In the example: //
// the inner element should have line-height of 15px. However, in this example: //
// the inner element should have a line-height of 150px. Therefore, we map percentages to Fixed // values and raw numbers to percentages. if (primitiveValue.isPercentage()) { // FIXME: percentage should not be restricted to an integer here. return Length((builderState.style().computedFontSize() * primitiveValue.intValue()) / 100, LengthType::Fixed); } if (primitiveValue.isNumber()) return Length(primitiveValue.doubleValue() * 100.0, LengthType::Percent); // FIXME: The parser should only emit the above types, so this should never be reached. We should change the // type of this function to return just a Length (and not an Optional). return std::nullopt; } inline FontSynthesis BuilderConverter::convertFontSynthesis(BuilderState&, const CSSValue& value) { if (is(value)) { ASSERT(downcast(value).valueID() == CSSValueNone); return FontSynthesisNone; } FontSynthesis result = FontSynthesisNone; ASSERT(is(value)); for (const CSSValue& v : downcast(value)) { switch (downcast(v).valueID()) { case CSSValueWeight: result |= FontSynthesisWeight; break; case CSSValueStyle: result |= FontSynthesisStyle; break; case CSSValueSmallCaps: result |= FontSynthesisSmallCaps; break; default: ASSERT_NOT_REACHED(); break; } } return result; } inline OptionSet BuilderConverter::convertSpeakAs(BuilderState&, const CSSValue& value) { auto result = RenderStyle::initialSpeakAs(); if (is(value)) { for (auto& currentValue : downcast(value)) result.add(downcast(currentValue.get())); } return result; } inline OptionSet BuilderConverter::convertHangingPunctuation(BuilderState&, const CSSValue& value) { auto result = RenderStyle::initialHangingPunctuation(); if (is(value)) { for (auto& currentValue : downcast(value)) result.add(downcast(currentValue.get())); } return result; } inline GapLength BuilderConverter::convertGapLength(BuilderState& builderState, const CSSValue& value) { return (downcast(value).valueID() == CSSValueNormal) ? GapLength() : GapLength(convertLength(builderState, value)); } } }