/* * (C) 1999-2003 Lars Knoll (knoll@kde.org) * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012, 2013 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "CSSPrimitiveValue.h" #include "CSSBasicShapes.h" #include "CSSCalcValue.h" #include "CSSFontFamily.h" #include "CSSHelper.h" #include "CSSMarkup.h" #include "CSSPrimitiveValueMappings.h" #include "CSSPropertyNames.h" #include "CSSToLengthConversionData.h" #include "CSSValueKeywords.h" #include "CalculationCategory.h" #include "CalculationValue.h" #include "Color.h" #include "ColorSerialization.h" #include "Counter.h" #include "DeprecatedCSSOMPrimitiveValue.h" #include "FontCascade.h" #include "Node.h" #include "Pair.h" #include "Rect.h" #include "RenderStyle.h" #include "RenderView.h" #include #include #include #include namespace WebCore { static inline bool isValidCSSUnitTypeForDoubleConversion(CSSUnitType unitType) { switch (unitType) { case CSSUnitType::CSS_CALC: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER: case CSSUnitType::CSS_CHS: case CSSUnitType::CSS_CM: case CSSUnitType::CSS_DEG: case CSSUnitType::CSS_DIMENSION: case CSSUnitType::CSS_EMS: case CSSUnitType::CSS_EXS: case CSSUnitType::CSS_FR: case CSSUnitType::CSS_GRAD: case CSSUnitType::CSS_HZ: case CSSUnitType::CSS_IN: case CSSUnitType::CSS_KHZ: case CSSUnitType::CSS_MM: case CSSUnitType::CSS_MS: case CSSUnitType::CSS_NUMBER: case CSSUnitType::CSS_PC: case CSSUnitType::CSS_PERCENTAGE: case CSSUnitType::CSS_PT: case CSSUnitType::CSS_PX: case CSSUnitType::CSS_Q: case CSSUnitType::CSS_LHS: case CSSUnitType::CSS_RLHS: case CSSUnitType::CSS_QUIRKY_EMS: case CSSUnitType::CSS_RAD: case CSSUnitType::CSS_REMS: case CSSUnitType::CSS_S: case CSSUnitType::CSS_TURN: case CSSUnitType::CSS_VH: case CSSUnitType::CSS_VMAX: case CSSUnitType::CSS_VMIN: case CSSUnitType::CSS_VW: case CSSUnitType::CSS_DPCM: case CSSUnitType::CSS_DPI: case CSSUnitType::CSS_DPPX: return true; case CSSUnitType::CSS_ATTR: case CSSUnitType::CSS_COUNTER: case CSSUnitType::CSS_COUNTER_NAME: case CSSUnitType::CSS_FONT_FAMILY: case CSSUnitType::CustomIdent: case CSSUnitType::CSS_PAIR: case CSSUnitType::CSS_PROPERTY_ID: case CSSUnitType::CSS_QUAD: case CSSUnitType::CSS_RECT: case CSSUnitType::CSS_RGBCOLOR: case CSSUnitType::CSS_SHAPE: case CSSUnitType::CSS_STRING: case CSSUnitType::CSS_UNICODE_RANGE: case CSSUnitType::CSS_UNKNOWN: case CSSUnitType::CSS_URI: case CSSUnitType::CSS_VALUE_ID: return false; case CSSUnitType::CSS_IDENT: break; } ASSERT_NOT_REACHED(); return false; } #if ASSERT_ENABLED static inline bool isStringType(CSSUnitType type) { switch (type) { case CSSUnitType::CSS_STRING: case CSSUnitType::CustomIdent: case CSSUnitType::CSS_URI: case CSSUnitType::CSS_ATTR: case CSSUnitType::CSS_COUNTER_NAME: return true; case CSSUnitType::CSS_CALC: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER: case CSSUnitType::CSS_CHS: case CSSUnitType::CSS_CM: case CSSUnitType::CSS_COUNTER: case CSSUnitType::CSS_DEG: case CSSUnitType::CSS_DIMENSION: case CSSUnitType::CSS_DPCM: case CSSUnitType::CSS_DPI: case CSSUnitType::CSS_DPPX: case CSSUnitType::CSS_EMS: case CSSUnitType::CSS_EXS: case CSSUnitType::CSS_FONT_FAMILY: case CSSUnitType::CSS_FR: case CSSUnitType::CSS_GRAD: case CSSUnitType::CSS_HZ: case CSSUnitType::CSS_IDENT: case CSSUnitType::CSS_IN: case CSSUnitType::CSS_KHZ: case CSSUnitType::CSS_MM: case CSSUnitType::CSS_MS: case CSSUnitType::CSS_NUMBER: case CSSUnitType::CSS_PAIR: case CSSUnitType::CSS_PC: case CSSUnitType::CSS_PERCENTAGE: case CSSUnitType::CSS_PROPERTY_ID: case CSSUnitType::CSS_PT: case CSSUnitType::CSS_PX: case CSSUnitType::CSS_Q: case CSSUnitType::CSS_LHS: case CSSUnitType::CSS_RLHS: case CSSUnitType::CSS_QUAD: case CSSUnitType::CSS_QUIRKY_EMS: case CSSUnitType::CSS_RAD: case CSSUnitType::CSS_RECT: case CSSUnitType::CSS_REMS: case CSSUnitType::CSS_RGBCOLOR: case CSSUnitType::CSS_S: case CSSUnitType::CSS_SHAPE: case CSSUnitType::CSS_TURN: case CSSUnitType::CSS_UNICODE_RANGE: case CSSUnitType::CSS_UNKNOWN: case CSSUnitType::CSS_VALUE_ID: case CSSUnitType::CSS_VH: case CSSUnitType::CSS_VMAX: case CSSUnitType::CSS_VMIN: case CSSUnitType::CSS_VW: return false; } ASSERT_NOT_REACHED(); return false; } #endif // ASSERT_ENABLED typedef HashMap CSSTextCache; static CSSTextCache& cssTextCache() { static NeverDestroyed cache; return cache; } CSSUnitType CSSPrimitiveValue::primitiveType() const { // FIXME: Use a switch statement here. if (primitiveUnitType() == CSSUnitType::CSS_PROPERTY_ID || primitiveUnitType() == CSSUnitType::CSS_VALUE_ID || primitiveUnitType() == CSSUnitType::CustomIdent) return CSSUnitType::CSS_IDENT; // Web-exposed content expects font family values to have CSSUnitType::CSS_STRING primitive type // so we need to map our internal CSSUnitType::CSS_FONT_FAMILY type here. if (primitiveUnitType() == CSSUnitType::CSS_FONT_FAMILY) return CSSUnitType::CSS_STRING; if (primitiveUnitType() != CSSUnitType::CSS_CALC) return primitiveUnitType(); switch (m_value.calc->category()) { case CalculationCategory::Number: return CSSUnitType::CSS_NUMBER; case CalculationCategory::Percent: return CSSUnitType::CSS_PERCENTAGE; case CalculationCategory::PercentNumber: return CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER; case CalculationCategory::PercentLength: return CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH; case CalculationCategory::Length: case CalculationCategory::Angle: case CalculationCategory::Time: case CalculationCategory::Frequency: return m_value.calc->primitiveType(); case CalculationCategory::Other: return CSSUnitType::CSS_UNKNOWN; } return CSSUnitType::CSS_UNKNOWN; } static const AtomString& propertyName(CSSPropertyID propertyID) { ASSERT_ARG(propertyID, (propertyID >= firstCSSProperty && propertyID < firstCSSProperty + numCSSProperties)); return getPropertyNameAtomString(propertyID); } static const AtomString& valueName(CSSValueID valueID) { ASSERT_ARG(valueID, (valueID >= firstCSSValueKeyword && valueID <= lastCSSValueKeyword)); return getValueNameAtomString(valueID); } CSSPrimitiveValue::CSSPrimitiveValue(CSSValueID valueID) : CSSValue(PrimitiveClass) { setPrimitiveUnitType(CSSUnitType::CSS_VALUE_ID); m_value.valueID = valueID; } CSSPrimitiveValue::CSSPrimitiveValue(CSSPropertyID propertyID) : CSSValue(PrimitiveClass) { setPrimitiveUnitType(CSSUnitType::CSS_PROPERTY_ID); m_value.propertyID = propertyID; } CSSPrimitiveValue::CSSPrimitiveValue(double num, CSSUnitType type) : CSSValue(PrimitiveClass) { setPrimitiveUnitType(type); ASSERT(std::isfinite(num)); m_value.num = num; } CSSPrimitiveValue::CSSPrimitiveValue(const String& string, CSSUnitType type) : CSSValue(PrimitiveClass) { ASSERT(isStringType(type)); setPrimitiveUnitType(type); if ((m_value.string = string.impl())) m_value.string->ref(); } CSSPrimitiveValue::CSSPrimitiveValue(const Color& color) : CSSValue(PrimitiveClass) { setPrimitiveUnitType(CSSUnitType::CSS_RGBCOLOR); m_value.color = new Color(color); } CSSPrimitiveValue::CSSPrimitiveValue(const Length& length) : CSSValue(PrimitiveClass) { init(length); } CSSPrimitiveValue::CSSPrimitiveValue(const Length& length, const RenderStyle& style) : CSSValue(PrimitiveClass) { switch (length.type()) { case LengthType::Auto: case LengthType::Intrinsic: case LengthType::MinIntrinsic: case LengthType::MinContent: case LengthType::MaxContent: case LengthType::FillAvailable: case LengthType::FitContent: case LengthType::Percent: init(length); return; case LengthType::Fixed: setPrimitiveUnitType(CSSUnitType::CSS_PX); m_value.num = adjustFloatForAbsoluteZoom(length.value(), style); return; case LengthType::Calculated: init(CSSCalcValue::create(length.calculationValue(), style)); return; case LengthType::Relative: case LengthType::Undefined: ASSERT_NOT_REACHED(); return; } ASSERT_NOT_REACHED(); } CSSPrimitiveValue::CSSPrimitiveValue(const LengthSize& lengthSize, const RenderStyle& style) : CSSValue(PrimitiveClass) { init(lengthSize, style); } CSSPrimitiveValue::CSSPrimitiveValue(StaticCSSValueTag, CSSValueID valueID) : CSSPrimitiveValue(valueID) { makeStatic(); } CSSPrimitiveValue::CSSPrimitiveValue(StaticCSSValueTag, const Color& color) : CSSPrimitiveValue(color) { makeStatic(); } CSSPrimitiveValue::CSSPrimitiveValue(StaticCSSValueTag, double num, CSSUnitType type) : CSSPrimitiveValue(num, type) { makeStatic(); } void CSSPrimitiveValue::init(const Length& length) { switch (length.type()) { case LengthType::Auto: setPrimitiveUnitType(CSSUnitType::CSS_VALUE_ID); m_value.valueID = CSSValueAuto; return; case LengthType::Fixed: setPrimitiveUnitType(CSSUnitType::CSS_PX); m_value.num = length.value(); return; case LengthType::Intrinsic: setPrimitiveUnitType(CSSUnitType::CSS_VALUE_ID); m_value.valueID = CSSValueIntrinsic; return; case LengthType::MinIntrinsic: setPrimitiveUnitType(CSSUnitType::CSS_VALUE_ID); m_value.valueID = CSSValueMinIntrinsic; return; case LengthType::MinContent: setPrimitiveUnitType(CSSUnitType::CSS_VALUE_ID); m_value.valueID = CSSValueMinContent; return; case LengthType::MaxContent: setPrimitiveUnitType(CSSUnitType::CSS_VALUE_ID); m_value.valueID = CSSValueMaxContent; return; case LengthType::FillAvailable: setPrimitiveUnitType(CSSUnitType::CSS_VALUE_ID); m_value.valueID = CSSValueWebkitFillAvailable; return; case LengthType::FitContent: setPrimitiveUnitType(CSSUnitType::CSS_VALUE_ID); m_value.valueID = CSSValueFitContent; return; case LengthType::Percent: setPrimitiveUnitType(CSSUnitType::CSS_PERCENTAGE); ASSERT(std::isfinite(length.percent())); m_value.num = length.percent(); return; case LengthType::Calculated: case LengthType::Relative: case LengthType::Undefined: ASSERT_NOT_REACHED(); return; } ASSERT_NOT_REACHED(); } void CSSPrimitiveValue::init(const LengthSize& lengthSize, const RenderStyle& style) { setPrimitiveUnitType(CSSUnitType::CSS_PAIR); m_hasCachedCSSText = false; m_value.pair = &Pair::create(create(lengthSize.width, style), create(lengthSize.height, style)).leakRef(); } void CSSPrimitiveValue::init(Ref&& counter) { setPrimitiveUnitType(CSSUnitType::CSS_COUNTER); m_hasCachedCSSText = false; m_value.counter = &counter.leakRef(); } void CSSPrimitiveValue::init(Ref&& r) { setPrimitiveUnitType(CSSUnitType::CSS_RECT); m_hasCachedCSSText = false; m_value.rect = &r.leakRef(); } void CSSPrimitiveValue::init(Ref&& quad) { setPrimitiveUnitType(CSSUnitType::CSS_QUAD); m_hasCachedCSSText = false; m_value.quad = &quad.leakRef(); } void CSSPrimitiveValue::init(Ref&& p) { setPrimitiveUnitType(CSSUnitType::CSS_PAIR); m_hasCachedCSSText = false; m_value.pair = &p.leakRef(); } void CSSPrimitiveValue::init(Ref&& shape) { setPrimitiveUnitType(CSSUnitType::CSS_SHAPE); m_hasCachedCSSText = false; m_value.shape = &shape.leakRef(); } void CSSPrimitiveValue::init(RefPtr&& c) { setPrimitiveUnitType(CSSUnitType::CSS_CALC); m_hasCachedCSSText = false; m_value.calc = c.leakRef(); } CSSPrimitiveValue::~CSSPrimitiveValue() { cleanup(); } void CSSPrimitiveValue::cleanup() { auto type = primitiveUnitType(); switch (type) { case CSSUnitType::CSS_STRING: case CSSUnitType::CustomIdent: case CSSUnitType::CSS_URI: case CSSUnitType::CSS_ATTR: case CSSUnitType::CSS_COUNTER_NAME: if (m_value.string) m_value.string->deref(); break; case CSSUnitType::CSS_COUNTER: m_value.counter->deref(); break; case CSSUnitType::CSS_RECT: m_value.rect->deref(); break; case CSSUnitType::CSS_QUAD: m_value.quad->deref(); break; case CSSUnitType::CSS_PAIR: m_value.pair->deref(); break; case CSSUnitType::CSS_CALC: if (m_value.calc) m_value.calc->deref(); break; case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH: ASSERT_NOT_REACHED(); break; case CSSUnitType::CSS_SHAPE: m_value.shape->deref(); break; case CSSUnitType::CSS_FONT_FAMILY: ASSERT(m_value.fontFamily); delete m_value.fontFamily; m_value.fontFamily = nullptr; break; case CSSUnitType::CSS_RGBCOLOR: ASSERT(m_value.color); delete m_value.color; m_value.color = nullptr; break; case CSSUnitType::CSS_DIMENSION: case CSSUnitType::CSS_NUMBER: case CSSUnitType::CSS_PERCENTAGE: case CSSUnitType::CSS_EMS: case CSSUnitType::CSS_QUIRKY_EMS: case CSSUnitType::CSS_EXS: case CSSUnitType::CSS_REMS: case CSSUnitType::CSS_CHS: case CSSUnitType::CSS_PX: case CSSUnitType::CSS_CM: case CSSUnitType::CSS_MM: case CSSUnitType::CSS_IN: case CSSUnitType::CSS_PT: case CSSUnitType::CSS_PC: case CSSUnitType::CSS_DEG: case CSSUnitType::CSS_RAD: case CSSUnitType::CSS_GRAD: case CSSUnitType::CSS_MS: case CSSUnitType::CSS_S: case CSSUnitType::CSS_HZ: case CSSUnitType::CSS_KHZ: case CSSUnitType::CSS_TURN: case CSSUnitType::CSS_VW: case CSSUnitType::CSS_VH: case CSSUnitType::CSS_VMIN: case CSSUnitType::CSS_VMAX: case CSSUnitType::CSS_DPPX: case CSSUnitType::CSS_DPI: case CSSUnitType::CSS_DPCM: case CSSUnitType::CSS_FR: case CSSUnitType::CSS_Q: case CSSUnitType::CSS_LHS: case CSSUnitType::CSS_RLHS: case CSSUnitType::CSS_IDENT: case CSSUnitType::CSS_UNKNOWN: case CSSUnitType::CSS_UNICODE_RANGE: case CSSUnitType::CSS_PROPERTY_ID: case CSSUnitType::CSS_VALUE_ID: ASSERT(!isStringType(type)); break; } setPrimitiveUnitType(CSSUnitType::CSS_UNKNOWN); if (m_hasCachedCSSText) { cssTextCache().remove(this); m_hasCachedCSSText = false; } } double CSSPrimitiveValue::computeDegrees() const { return computeDegrees(primitiveType(), doubleValue()); } template<> int CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const { return roundForImpreciseConversion(computeLengthDouble(conversionData)); } template<> unsigned CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const { return roundForImpreciseConversion(computeLengthDouble(conversionData)); } template<> Length CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const { return Length(clampTo(computeLengthDouble(conversionData), minValueForCssLength, maxValueForCssLength), LengthType::Fixed); } template<> short CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const { return roundForImpreciseConversion(computeLengthDouble(conversionData)); } template<> unsigned short CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const { return roundForImpreciseConversion(computeLengthDouble(conversionData)); } template<> float CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const { return static_cast(computeLengthDouble(conversionData)); } template<> double CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const { return computeLengthDouble(conversionData); } template<> LayoutUnit CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const { return LayoutUnit(computeLengthDouble(conversionData)); } double CSSPrimitiveValue::computeLengthDouble(const CSSToLengthConversionData& conversionData) const { if (primitiveUnitType() == CSSUnitType::CSS_CALC) { // The multiplier and factor is applied to each value in the calc expression individually return m_value.calc->computeLengthPx(conversionData); } return computeNonCalcLengthDouble(conversionData, primitiveType(), m_value.num); } static constexpr double mmPerInch = 25.4; static constexpr double cmPerInch = 2.54; static constexpr double QPerInch = 25.4 * 4.0; double CSSPrimitiveValue::computeUnzoomedNonCalcLengthDouble(CSSUnitType primitiveType, double value, CSSPropertyID propertyToCompute, const FontMetrics* fontMetrics, const FontCascadeDescription* fontDescription, const FontCascadeDescription* rootFontDescription, const RenderView* renderView) { switch (primitiveType) { case CSSUnitType::CSS_EMS: case CSSUnitType::CSS_QUIRKY_EMS: ASSERT(fontDescription); return ((propertyToCompute == CSSPropertyFontSize) ? fontDescription->specifiedSize() : fontDescription->computedSize()) * value; case CSSUnitType::CSS_EXS: ASSERT(fontMetrics); if (fontMetrics->hasXHeight()) return fontMetrics->xHeight() * value; ASSERT(fontDescription); return ((propertyToCompute == CSSPropertyFontSize) ? fontDescription->specifiedSize() : fontDescription->computedSize()) / 2.0 * value; case CSSUnitType::CSS_REMS: if (!rootFontDescription) return value; return ((propertyToCompute == CSSPropertyFontSize) ? rootFontDescription->specifiedSize() : rootFontDescription->computedSize()) * value; case CSSUnitType::CSS_CHS: ASSERT(fontMetrics); return fontMetrics->zeroWidth() * value; case CSSUnitType::CSS_PX: return value; case CSSUnitType::CSS_CM: return cssPixelsPerInch / cmPerInch * value; case CSSUnitType::CSS_MM: return cssPixelsPerInch / mmPerInch * value; case CSSUnitType::CSS_Q: return cssPixelsPerInch / QPerInch * value; case CSSUnitType::CSS_LHS: case CSSUnitType::CSS_RLHS: ASSERT_NOT_REACHED(); return -1.0; case CSSUnitType::CSS_IN: return cssPixelsPerInch * value; case CSSUnitType::CSS_PT: return cssPixelsPerInch / 72.0 * value; case CSSUnitType::CSS_PC: // 1 pc == 12 pt return cssPixelsPerInch * 12.0 / 72.0 * value; case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER: ASSERT_NOT_REACHED(); return -1.0; case CSSUnitType::CSS_VH: return renderView ? renderView->viewportSizeForCSSViewportUnits().height() / 100.0 * value : 0; case CSSUnitType::CSS_VW: return renderView ? renderView->viewportSizeForCSSViewportUnits().width() / 100.0 * value : 0; case CSSUnitType::CSS_VMAX: if (renderView) { IntSize viewportSizeForCSSViewportUnits = renderView->viewportSizeForCSSViewportUnits(); return std::max(viewportSizeForCSSViewportUnits.width(), viewportSizeForCSSViewportUnits.height()) / 100.0 * value; } return value; case CSSUnitType::CSS_VMIN: if (renderView) { IntSize viewportSizeForCSSViewportUnits = renderView->viewportSizeForCSSViewportUnits(); return std::min(viewportSizeForCSSViewportUnits.width(), viewportSizeForCSSViewportUnits.height()) / 100.0 * value; } return value; default: ASSERT_NOT_REACHED(); return -1.0; } } double CSSPrimitiveValue::computeNonCalcLengthDouble(const CSSToLengthConversionData& conversionData, CSSUnitType primitiveType, double value) { switch (primitiveType) { case CSSUnitType::CSS_EMS: case CSSUnitType::CSS_QUIRKY_EMS: ASSERT(conversionData.style()); value = computeUnzoomedNonCalcLengthDouble(primitiveType, value, conversionData.propertyToCompute(), nullptr, &conversionData.style()->fontDescription()); break; case CSSUnitType::CSS_EXS: // FIXME: We have a bug right now where the zoom will be applied twice to EX units. // We really need to compute EX using fontMetrics for the original specifiedSize and not use // our actual constructed rendering font. ASSERT(conversionData.style()); value = computeUnzoomedNonCalcLengthDouble(primitiveType, value, conversionData.propertyToCompute(), &conversionData.style()->fontMetrics(), &conversionData.style()->fontDescription()); break; case CSSUnitType::CSS_REMS: value = computeUnzoomedNonCalcLengthDouble(primitiveType, value, conversionData.propertyToCompute(), nullptr, nullptr, conversionData.rootStyle() ? &conversionData.rootStyle()->fontDescription() : nullptr); break; case CSSUnitType::CSS_CHS: ASSERT(conversionData.style()); value = computeUnzoomedNonCalcLengthDouble(primitiveType, value, conversionData.propertyToCompute(), &conversionData.style()->fontMetrics()); break; case CSSUnitType::CSS_PX: case CSSUnitType::CSS_CM: case CSSUnitType::CSS_MM: case CSSUnitType::CSS_Q: case CSSUnitType::CSS_IN: case CSSUnitType::CSS_PT: case CSSUnitType::CSS_PC: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER: value = computeUnzoomedNonCalcLengthDouble(primitiveType, value, conversionData.propertyToCompute()); break; case CSSUnitType::CSS_VH: return value * conversionData.viewportHeightFactor(); case CSSUnitType::CSS_VW: return value * conversionData.viewportWidthFactor(); case CSSUnitType::CSS_VMAX: return value * conversionData.viewportMaxFactor(); case CSSUnitType::CSS_VMIN: return value * conversionData.viewportMinFactor(); case CSSUnitType::CSS_LHS: ASSERT(conversionData.style()); if (conversionData.computingLineHeight() || conversionData.computingFontSize()) { // Try to get the parent's computed line-height, or fall back to the initial line-height of this element's font spacing. value *= conversionData.parentStyle() ? conversionData.parentStyle()->computedLineHeight() : conversionData.style()->fontMetrics().lineSpacing(); } else value *= conversionData.style()->computedLineHeight(); break; case CSSUnitType::CSS_RLHS: if (conversionData.rootStyle()) { if (conversionData.computingLineHeight() || conversionData.computingFontSize()) value *= conversionData.rootStyle()->computeLineHeight(conversionData.rootStyle()->specifiedLineHeight()); else value *= conversionData.rootStyle()->computedLineHeight(); } break; default: ASSERT_NOT_REACHED(); return -1.0; } // We do not apply the zoom factor when we are computing the value of the font-size property. The zooming // for font sizes is much more complicated, since we have to worry about enforcing the minimum font size preference // as well as enforcing the implicit "smart minimum." if (conversionData.computingFontSize() || isFontRelativeLength(primitiveType)) return value; return value * conversionData.zoom(); } bool CSSPrimitiveValue::equalForLengthResolution(const RenderStyle& styleA, const RenderStyle& styleB) { // These properties affect results of computeNonCalcLengthDouble above. if (styleA.fontDescription().computedSize() != styleB.fontDescription().computedSize()) return false; if (styleA.fontDescription().specifiedSize() != styleB.fontDescription().specifiedSize()) return false; if (styleA.fontMetrics().xHeight() != styleB.fontMetrics().xHeight()) return false; if (styleA.fontMetrics().zeroWidth() != styleB.fontMetrics().zeroWidth()) return false; if (styleA.zoom() != styleB.zoom()) return false; return true; } double CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(CSSUnitType unitType) { double factor = 1.0; // FIXME: the switch can be replaced by an array of scale factors. switch (unitType) { // These are "canonical" units in their respective categories. case CSSUnitType::CSS_PX: case CSSUnitType::CSS_DEG: case CSSUnitType::CSS_MS: case CSSUnitType::CSS_HZ: case CSSUnitType::CSS_DPPX: break; case CSSUnitType::CSS_CM: factor = cssPixelsPerInch / cmPerInch; break; case CSSUnitType::CSS_DPCM: factor = cmPerInch / cssPixelsPerInch; // (2.54 cm/in) break; case CSSUnitType::CSS_MM: factor = cssPixelsPerInch / mmPerInch; break; case CSSUnitType::CSS_Q: factor = cssPixelsPerInch / QPerInch; break; case CSSUnitType::CSS_IN: factor = cssPixelsPerInch; break; case CSSUnitType::CSS_DPI: factor = 1 / cssPixelsPerInch; break; case CSSUnitType::CSS_PT: factor = cssPixelsPerInch / 72.0; break; case CSSUnitType::CSS_PC: factor = cssPixelsPerInch * 12.0 / 72.0; // 1 pc == 12 pt break; case CSSUnitType::CSS_RAD: factor = degreesPerRadianDouble; break; case CSSUnitType::CSS_GRAD: factor = degreesPerGradientDouble; break; case CSSUnitType::CSS_TURN: factor = degreesPerTurnDouble; break; case CSSUnitType::CSS_S: case CSSUnitType::CSS_KHZ: factor = 1000; break; default: break; } return factor; } ExceptionOr CSSPrimitiveValue::getFloatValue(CSSUnitType unitType) const { auto result = doubleValueInternal(unitType); if (!result) return Exception { InvalidAccessError }; return clampTo(result.value()); } double CSSPrimitiveValue::doubleValue(CSSUnitType unitType) const { return doubleValueInternal(unitType).value_or(0); } double CSSPrimitiveValue::doubleValue() const { return primitiveUnitType() != CSSUnitType::CSS_CALC ? m_value.num : m_value.calc->doubleValue(); } std::optional CSSPrimitiveValue::isZero() const { if (primitiveUnitType() == CSSUnitType::CSS_CALC) return std::nullopt; return !m_value.num; } std::optional CSSPrimitiveValue::isPositive() const { if (primitiveUnitType() == CSSUnitType::CSS_CALC) return std::nullopt; return m_value.num > 0; } std::optional CSSPrimitiveValue::isNegative() const { if (primitiveUnitType() == CSSUnitType::CSS_CALC) return std::nullopt; return m_value.num < 0; } bool CSSPrimitiveValue::isCenterPosition() const { return valueID() == CSSValueCenter || doubleValue(CSSUnitType::CSS_PERCENTAGE) == 50; } std::optional CSSPrimitiveValue::doubleValueInternal(CSSUnitType requestedUnitType) const { if (!isValidCSSUnitTypeForDoubleConversion(primitiveUnitType()) || !isValidCSSUnitTypeForDoubleConversion(requestedUnitType)) return std::nullopt; CSSUnitType sourceUnitType = primitiveType(); if (requestedUnitType == sourceUnitType || requestedUnitType == CSSUnitType::CSS_DIMENSION) return doubleValue(); CSSUnitCategory sourceCategory = unitCategory(sourceUnitType); ASSERT(sourceCategory != CSSUnitCategory::Other); CSSUnitType targetUnitType = requestedUnitType; CSSUnitCategory targetCategory = unitCategory(targetUnitType); ASSERT(targetCategory != CSSUnitCategory::Other); // Cannot convert between unrelated unit categories if one of them is not CSSUnitCategory::Number. if (sourceCategory != targetCategory && sourceCategory != CSSUnitCategory::Number && targetCategory != CSSUnitCategory::Number) return std::nullopt; if (targetCategory == CSSUnitCategory::Number) { // We interpret conversion to CSSUnitType::CSS_NUMBER as conversion to a canonical unit in this value's category. targetUnitType = canonicalUnitTypeForCategory(sourceCategory); if (targetUnitType == CSSUnitType::CSS_UNKNOWN) return std::nullopt; } if (sourceUnitType == CSSUnitType::CSS_NUMBER) { // We interpret conversion from CSSUnitType::CSS_NUMBER in the same way as CSSParser::validUnit() while using non-strict mode. sourceUnitType = canonicalUnitTypeForCategory(targetCategory); if (sourceUnitType == CSSUnitType::CSS_UNKNOWN) return std::nullopt; } double convertedValue = doubleValue(); // First convert the value from primitiveUnitType() to canonical type. double factor = conversionToCanonicalUnitsScaleFactor(sourceUnitType); convertedValue *= factor; // Now convert from canonical type to the target unitType. factor = conversionToCanonicalUnitsScaleFactor(targetUnitType); convertedValue /= factor; return convertedValue; } String CSSPrimitiveValue::stringValue() const { switch (primitiveUnitType()) { case CSSUnitType::CSS_STRING: case CSSUnitType::CustomIdent: case CSSUnitType::CSS_ATTR: case CSSUnitType::CSS_URI: return m_value.string; case CSSUnitType::CSS_FONT_FAMILY: return m_value.fontFamily->familyName; case CSSUnitType::CSS_VALUE_ID: return valueName(m_value.valueID); case CSSUnitType::CSS_PROPERTY_ID: return propertyName(m_value.propertyID); default: return String(); } } NEVER_INLINE String CSSPrimitiveValue::formatNumberValue(StringView suffix) const { return makeString(m_value.num, suffix); } // FIXME: Should return const char*. String CSSPrimitiveValue::unitTypeString(CSSUnitType unitType) { switch (unitType) { case CSSUnitType::CSS_PERCENTAGE: return "%"; case CSSUnitType::CSS_EMS: return "em"; case CSSUnitType::CSS_EXS: return "ex"; case CSSUnitType::CSS_PX: return "px"; case CSSUnitType::CSS_CM: return "cm"; case CSSUnitType::CSS_MM: return "mm"; case CSSUnitType::CSS_IN: return "in"; case CSSUnitType::CSS_PT: return "pt"; case CSSUnitType::CSS_PC: return "pc"; case CSSUnitType::CSS_DEG: return "deg"; case CSSUnitType::CSS_RAD: return "rad"; case CSSUnitType::CSS_GRAD: return "grad"; case CSSUnitType::CSS_MS: return "ms"; case CSSUnitType::CSS_S: return "s"; case CSSUnitType::CSS_HZ: return "hz"; case CSSUnitType::CSS_KHZ: return "khz"; case CSSUnitType::CSS_VW: return "vw"; case CSSUnitType::CSS_VH: return "vh"; case CSSUnitType::CSS_VMIN: return "vmin"; case CSSUnitType::CSS_VMAX: return "vmax"; case CSSUnitType::CSS_DPPX: return "dppx"; case CSSUnitType::CSS_DPI: return "dpi"; case CSSUnitType::CSS_DPCM: return "dpcm"; case CSSUnitType::CSS_FR: return "fr"; case CSSUnitType::CSS_Q: return "q"; case CSSUnitType::CSS_LHS: return "lh"; case CSSUnitType::CSS_RLHS: return "rlh"; case CSSUnitType::CSS_TURN: return "turn"; case CSSUnitType::CSS_REMS: return "rem"; case CSSUnitType::CSS_CHS: return "ch"; case CSSUnitType::CSS_UNKNOWN: case CSSUnitType::CSS_NUMBER: case CSSUnitType::CSS_DIMENSION: case CSSUnitType::CSS_STRING: case CSSUnitType::CSS_URI: case CSSUnitType::CSS_IDENT: case CSSUnitType::CustomIdent: case CSSUnitType::CSS_ATTR: case CSSUnitType::CSS_COUNTER: case CSSUnitType::CSS_RECT: case CSSUnitType::CSS_RGBCOLOR: case CSSUnitType::CSS_PAIR: case CSSUnitType::CSS_UNICODE_RANGE: case CSSUnitType::CSS_COUNTER_NAME: case CSSUnitType::CSS_SHAPE: case CSSUnitType::CSS_QUAD: case CSSUnitType::CSS_CALC: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH: case CSSUnitType::CSS_FONT_FAMILY: case CSSUnitType::CSS_PROPERTY_ID: case CSSUnitType::CSS_VALUE_ID: case CSSUnitType::CSS_QUIRKY_EMS: return emptyString(); } ASSERT_NOT_REACHED(); return emptyString(); } ALWAYS_INLINE String CSSPrimitiveValue::formatNumberForCustomCSSText() const { switch (primitiveUnitType()) { case CSSUnitType::CSS_UNKNOWN: return String(); case CSSUnitType::CSS_NUMBER: return formatNumberValue(""); case CSSUnitType::CSS_PERCENTAGE: return formatNumberValue("%"); case CSSUnitType::CSS_EMS: case CSSUnitType::CSS_QUIRKY_EMS: return formatNumberValue("em"); case CSSUnitType::CSS_EXS: return formatNumberValue("ex"); case CSSUnitType::CSS_REMS: return formatNumberValue("rem"); case CSSUnitType::CSS_CHS: return formatNumberValue("ch"); case CSSUnitType::CSS_PX: return formatNumberValue("px"); case CSSUnitType::CSS_CM: return formatNumberValue("cm"); case CSSUnitType::CSS_DPPX: return formatNumberValue("dppx"); case CSSUnitType::CSS_DPI: return formatNumberValue("dpi"); case CSSUnitType::CSS_DPCM: return formatNumberValue("dpcm"); case CSSUnitType::CSS_MM: return formatNumberValue("mm"); case CSSUnitType::CSS_IN: return formatNumberValue("in"); case CSSUnitType::CSS_PT: return formatNumberValue("pt"); case CSSUnitType::CSS_PC: return formatNumberValue("pc"); case CSSUnitType::CSS_DEG: return formatNumberValue("deg"); case CSSUnitType::CSS_RAD: return formatNumberValue("rad"); case CSSUnitType::CSS_GRAD: return formatNumberValue("grad"); case CSSUnitType::CSS_MS: return formatNumberValue("ms"); case CSSUnitType::CSS_S: return formatNumberValue("s"); case CSSUnitType::CSS_HZ: return formatNumberValue("hz"); case CSSUnitType::CSS_KHZ: return formatNumberValue("khz"); case CSSUnitType::CSS_TURN: return formatNumberValue("turn"); case CSSUnitType::CSS_FR: return formatNumberValue("fr"); case CSSUnitType::CSS_Q: return formatNumberValue("q"); case CSSUnitType::CSS_LHS: return formatNumberValue("lh"); case CSSUnitType::CSS_RLHS: return formatNumberValue("rlh"); case CSSUnitType::CSS_DIMENSION: // FIXME: This isn't correct. return formatNumberValue(""); case CSSUnitType::CSS_STRING: return serializeString(m_value.string); case CSSUnitType::CustomIdent: { StringBuilder builder; serializeIdentifier(m_value.string, builder); return builder.toString(); } case CSSUnitType::CSS_FONT_FAMILY: return serializeFontFamily(m_value.fontFamily->familyName); case CSSUnitType::CSS_URI: return serializeURL(m_value.string); case CSSUnitType::CSS_VALUE_ID: return valueName(m_value.valueID); case CSSUnitType::CSS_PROPERTY_ID: return propertyName(m_value.propertyID); case CSSUnitType::CSS_ATTR: return "attr(" + String(m_value.string) + ')'; case CSSUnitType::CSS_COUNTER_NAME: return "counter(" + String(m_value.string) + ')'; case CSSUnitType::CSS_COUNTER: { StringBuilder result; auto separator = m_value.counter->separator(); auto listStyle = m_value.counter->listStyle(); result.append(separator.isEmpty() ? "counter(" : "counters(", m_value.counter->identifier(), separator.isEmpty() ? "" : ", "); if (!separator.isEmpty()) serializeString(separator, result); if (!(listStyle.isEmpty() || listStyle == "decimal")) result.append(", ", listStyle); result.append(')'); return result.toString(); } case CSSUnitType::CSS_RECT: return rectValue()->cssText(); case CSSUnitType::CSS_QUAD: return quadValue()->cssText(); case CSSUnitType::CSS_RGBCOLOR: return serializationForCSS(color()); case CSSUnitType::CSS_PAIR: return pairValue()->cssText(); case CSSUnitType::CSS_CALC: if (!m_value.calc) break; return m_value.calc->cssText(); case CSSUnitType::CSS_SHAPE: return m_value.shape->cssText(); case CSSUnitType::CSS_VW: return formatNumberValue("vw"); case CSSUnitType::CSS_VH: return formatNumberValue("vh"); case CSSUnitType::CSS_VMIN: return formatNumberValue("vmin"); case CSSUnitType::CSS_VMAX: return formatNumberValue("vmax"); case CSSUnitType::CSS_IDENT: case CSSUnitType::CSS_UNICODE_RANGE: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH: ASSERT_NOT_REACHED(); } return String(); } String CSSPrimitiveValue::customCSSText() const { // FIXME: return the original value instead of a generated one (e.g. color // name if it was specified) - check what spec says about this CSSTextCache& cssTextCache = WebCore::cssTextCache(); if (m_hasCachedCSSText) { ASSERT(cssTextCache.contains(this)); return cssTextCache.get(this); } String text = formatNumberForCustomCSSText(); ASSERT(!cssTextCache.contains(this)); m_hasCachedCSSText = true; cssTextCache.set(this, text); return text; } bool CSSPrimitiveValue::equals(const CSSPrimitiveValue& other) const { if (primitiveUnitType() != other.primitiveUnitType()) return false; switch (primitiveUnitType()) { case CSSUnitType::CSS_UNKNOWN: return false; case CSSUnitType::CSS_NUMBER: case CSSUnitType::CSS_PERCENTAGE: case CSSUnitType::CSS_EMS: case CSSUnitType::CSS_QUIRKY_EMS: case CSSUnitType::CSS_EXS: case CSSUnitType::CSS_REMS: case CSSUnitType::CSS_CHS: case CSSUnitType::CSS_PX: case CSSUnitType::CSS_CM: case CSSUnitType::CSS_DPPX: case CSSUnitType::CSS_DPI: case CSSUnitType::CSS_DPCM: case CSSUnitType::CSS_MM: case CSSUnitType::CSS_IN: case CSSUnitType::CSS_PT: case CSSUnitType::CSS_PC: case CSSUnitType::CSS_DEG: case CSSUnitType::CSS_RAD: case CSSUnitType::CSS_GRAD: case CSSUnitType::CSS_MS: case CSSUnitType::CSS_S: case CSSUnitType::CSS_HZ: case CSSUnitType::CSS_KHZ: case CSSUnitType::CSS_TURN: case CSSUnitType::CSS_VW: case CSSUnitType::CSS_VH: case CSSUnitType::CSS_VMIN: case CSSUnitType::CSS_VMAX: case CSSUnitType::CSS_FR: case CSSUnitType::CSS_Q: case CSSUnitType::CSS_LHS: case CSSUnitType::CSS_RLHS: case CSSUnitType::CSS_DIMENSION: return m_value.num == other.m_value.num; case CSSUnitType::CSS_PROPERTY_ID: return propertyName(m_value.propertyID) == propertyName(other.m_value.propertyID); case CSSUnitType::CSS_VALUE_ID: return valueName(m_value.valueID) == valueName(other.m_value.valueID); case CSSUnitType::CSS_STRING: case CSSUnitType::CustomIdent: case CSSUnitType::CSS_URI: case CSSUnitType::CSS_ATTR: case CSSUnitType::CSS_COUNTER_NAME: return equal(m_value.string, other.m_value.string); case CSSUnitType::CSS_COUNTER: return m_value.counter && other.m_value.counter && m_value.counter->equals(*other.m_value.counter); case CSSUnitType::CSS_RECT: return m_value.rect && other.m_value.rect && m_value.rect->equals(*other.m_value.rect); case CSSUnitType::CSS_QUAD: return m_value.quad && other.m_value.quad && m_value.quad->equals(*other.m_value.quad); case CSSUnitType::CSS_RGBCOLOR: return color() == other.color(); case CSSUnitType::CSS_PAIR: return m_value.pair && other.m_value.pair && m_value.pair->equals(*other.m_value.pair); case CSSUnitType::CSS_CALC: return m_value.calc && other.m_value.calc && m_value.calc->equals(*other.m_value.calc); case CSSUnitType::CSS_SHAPE: return m_value.shape && other.m_value.shape && m_value.shape->equals(*other.m_value.shape); case CSSUnitType::CSS_FONT_FAMILY: return fontFamily() == other.fontFamily(); case CSSUnitType::CSS_IDENT: case CSSUnitType::CSS_UNICODE_RANGE: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER: case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH: // FIXME: seems like these should be handled. ASSERT_NOT_REACHED(); break; } return false; } Ref CSSPrimitiveValue::createDeprecatedCSSOMPrimitiveWrapper(CSSStyleDeclaration& styleDeclaration) const { return DeprecatedCSSOMPrimitiveValue::create(*this, styleDeclaration); } // https://drafts.css-houdini.org/css-properties-values-api/#dependency-cycles void CSSPrimitiveValue::collectDirectComputationalDependencies(HashSet& values) const { switch (primitiveUnitType()) { case CSSUnitType::CSS_EMS: case CSSUnitType::CSS_QUIRKY_EMS: case CSSUnitType::CSS_EXS: case CSSUnitType::CSS_CHS: values.add(CSSPropertyFontSize); break; case CSSUnitType::CSS_LHS: values.add(CSSPropertyFontSize); values.add(CSSPropertyLineHeight); break; case CSSUnitType::CSS_CALC: m_value.calc->collectDirectComputationalDependencies(values); break; default: break; } } void CSSPrimitiveValue::collectDirectRootComputationalDependencies(HashSet& values) const { switch (primitiveUnitType()) { case CSSUnitType::CSS_REMS: values.add(CSSPropertyFontSize); break; case CSSUnitType::CSS_RLHS: values.add(CSSPropertyFontSize); values.add(CSSPropertyLineHeight); break; case CSSUnitType::CSS_CALC: m_value.calc->collectDirectRootComputationalDependencies(values); break; default: break; } } } // namespace WebCore