/* * (C) 1999-2003 Lars Knoll (knoll@kde.org) * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. * Copyright (C) 2007 Alexey Proskuryakov * * 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. */ #pragma once #include "CSSPropertyNames.h" #include "CSSUnits.h" #include "CSSValue.h" #include "CSSValueKeywords.h" #include "Color.h" #include "ExceptionOr.h" #include "LayoutUnit.h" #include #include #include namespace WebCore { class CSSBasicShape; class CSSCalcValue; class CSSToLengthConversionData; class Counter; class DeprecatedCSSOMPrimitiveValue; class FontCascadeDescription; class FontMetrics; class Pair; class Quad; class RGBColor; class Rect; class RenderStyle; class RenderView; struct CSSFontFamily; struct Length; struct LengthSize; // Max/min values for CSS, needs to slightly smaller/larger than the true max/min values to allow for rounding without overflowing. // Subtract two (rather than one) to allow for values to be converted to float and back without exceeding the LayoutUnit::max. const int maxValueForCssLength = intMaxForLayoutUnit - 2; const int minValueForCssLength = intMinForLayoutUnit + 2; // Dimension calculations are imprecise, often resulting in values of e.g. // 44.99998. We need to round if we're really close to the next integer value. template inline T roundForImpreciseConversion(double value) { value += (value < 0) ? -0.01 : +0.01; return ((value > std::numeric_limits::max()) || (value < std::numeric_limits::min())) ? 0 : static_cast(value); } template<> inline float roundForImpreciseConversion(double value) { double ceiledValue = ceil(value); double proximityToNextInt = ceiledValue - value; if (proximityToNextInt <= 0.01 && value > 0) return static_cast(ceiledValue); if (proximityToNextInt >= 0.99 && value < 0) return static_cast(floor(value)); return static_cast(value); } class CSSPrimitiveValue final : public CSSValue { public: static constexpr bool isLength(CSSUnitType); static double computeDegrees(CSSUnitType, double angle); // FIXME: Some of these use primitiveUnitType() and some use primitiveType(). Many that use primitiveUnitType() are likely broken with calc(). bool isAngle() const { return unitCategory(primitiveType()) == CSSUnitCategory::Angle; } bool isAttr() const { return primitiveUnitType() == CSSUnitType::CSS_ATTR; } bool isCounter() const { return primitiveUnitType() == CSSUnitType::CSS_COUNTER; } bool isFontIndependentLength() const { return primitiveUnitType() >= CSSUnitType::CSS_PX && primitiveUnitType() <= CSSUnitType::CSS_PC; } bool isFontRelativeLength() const { return isFontRelativeLength(primitiveUnitType()); } bool isQuirkyEms() const { return primitiveType() == CSSUnitType::CSS_QUIRKY_EMS; } bool isLength() const { return isLength(static_cast(primitiveType())); } bool isNumber() const { return primitiveType() == CSSUnitType::CSS_NUMBER; } bool isPercentage() const { return primitiveType() == CSSUnitType::CSS_PERCENTAGE; } bool isPx() const { return primitiveType() == CSSUnitType::CSS_PX; } bool isRect() const { return primitiveUnitType() == CSSUnitType::CSS_RECT; } bool isPair() const { return primitiveUnitType() == CSSUnitType::CSS_PAIR; } bool isPropertyID() const { return primitiveUnitType() == CSSUnitType::CSS_PROPERTY_ID; } bool isRGBColor() const { return primitiveUnitType() == CSSUnitType::CSS_RGBCOLOR; } bool isShape() const { return primitiveUnitType() == CSSUnitType::CSS_SHAPE; } bool isString() const { return primitiveUnitType() == CSSUnitType::CSS_STRING; } bool isFontFamily() const { return primitiveUnitType() == CSSUnitType::CSS_FONT_FAMILY; } bool isTime() const { return unitCategory(primitiveUnitType()) == CSSUnitCategory::Time; } bool isFrequency() const { return unitCategory(primitiveType()) == CSSUnitCategory::Frequency; } bool isURI() const { return primitiveUnitType() == CSSUnitType::CSS_URI; } bool isCalculated() const { return primitiveUnitType() == CSSUnitType::CSS_CALC; } bool isCalculatedPercentageWithNumber() const { return primitiveType() == CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER; } bool isCalculatedPercentageWithLength() const { return primitiveType() == CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH; } bool isDotsPerInch() const { return primitiveType() == CSSUnitType::CSS_DPI; } bool isDotsPerPixel() const { return primitiveType() == CSSUnitType::CSS_DPPX; } bool isDotsPerCentimeter() const { return primitiveType() == CSSUnitType::CSS_DPCM; } bool isResolution() const { return unitCategory(primitiveType()) == CSSUnitCategory::Resolution; } bool isViewportPercentageLength() const { return isViewportPercentageLength(primitiveUnitType()); } bool isViewportPercentageWidth() const { return primitiveUnitType() == CSSUnitType::CSS_VW; } bool isViewportPercentageHeight() const { return primitiveUnitType() == CSSUnitType::CSS_VH; } bool isViewportPercentageMax() const { return primitiveUnitType() == CSSUnitType::CSS_VMAX; } bool isViewportPercentageMin() const { return primitiveUnitType() == CSSUnitType::CSS_VMIN; } bool isValueID() const { return primitiveUnitType() == CSSUnitType::CSS_VALUE_ID; } bool isFlex() const { return primitiveType() == CSSUnitType::CSS_FR; } bool isCustomIdent() const { return primitiveUnitType() == CSSUnitType::CustomIdent; } static Ref createIdentifier(CSSValueID valueID) { return adoptRef(*new CSSPrimitiveValue(valueID)); } static Ref createIdentifier(CSSPropertyID propertyID) { return adoptRef(*new CSSPrimitiveValue(propertyID)); } static Ref create(double value, CSSUnitType type) { return adoptRef(*new CSSPrimitiveValue(value, type)); } static Ref create(const String& value, CSSUnitType type) { return adoptRef(*new CSSPrimitiveValue(value, type)); } static Ref create(const Length& value, const RenderStyle& style) { return adoptRef(*new CSSPrimitiveValue(value, style)); } static Ref create(const LengthSize& value, const RenderStyle& style) { return adoptRef(*new CSSPrimitiveValue(value, style)); } template static Ref create(T&&); ~CSSPrimitiveValue(); void cleanup(); CSSUnitType primitiveType() const; ExceptionOr getFloatValue(CSSUnitType) const; double computeDegrees() const; enum TimeUnit { Seconds, Milliseconds }; template T computeTime() const; template T computeLength(const CSSToLengthConversionData&) const; template Length convertToLength(const CSSToLengthConversionData&) const; bool convertingToLengthRequiresNonNullStyle(int lengthConversion) const; double doubleValue(CSSUnitType) const; // It's usually wrong to call this; it can trigger type conversion in calc without sufficient context to resolve relative length units. double doubleValue() const; // These return nullopt for calc, for which range checking is not done at parse time: . std::optional isZero() const; std::optional isPositive() const; std::optional isNegative() const; bool isCenterPosition() const; template inline T value(CSSUnitType type) const { return clampTo(doubleValue(type)); } template inline T value() const { return clampTo(doubleValue()); } float floatValue(CSSUnitType type) const { return value(type); } float floatValue() const { return value(); } int intValue(CSSUnitType type) const { return value(type); } int intValue() const { return value(); } WEBCORE_EXPORT String stringValue() const; const Color& color() const { ASSERT(primitiveUnitType() == CSSUnitType::CSS_RGBCOLOR); return *m_value.color; } Counter* counterValue() const { return primitiveUnitType() != CSSUnitType::CSS_COUNTER ? nullptr : m_value.counter; } CSSCalcValue* cssCalcValue() const { return primitiveUnitType() != CSSUnitType::CSS_CALC ? nullptr : m_value.calc; } const CSSFontFamily& fontFamily() const { ASSERT(primitiveUnitType() == CSSUnitType::CSS_FONT_FAMILY); return *m_value.fontFamily; } Pair* pairValue() const { return primitiveUnitType() != CSSUnitType::CSS_PAIR ? nullptr : m_value.pair; } CSSPropertyID propertyID() const { return primitiveUnitType() == CSSUnitType::CSS_PROPERTY_ID ? m_value.propertyID : CSSPropertyInvalid; } Quad* quadValue() const { return primitiveUnitType() != CSSUnitType::CSS_QUAD ? nullptr : m_value.quad; } Rect* rectValue() const { return primitiveUnitType() != CSSUnitType::CSS_RECT ? nullptr : m_value.rect; } CSSBasicShape* shapeValue() const { return primitiveUnitType() != CSSUnitType::CSS_SHAPE ? nullptr : m_value.shape; } CSSValueID valueID() const { return primitiveUnitType() == CSSUnitType::CSS_VALUE_ID ? m_value.valueID : CSSValueInvalid; } template inline operator T() const; // Defined in CSSPrimitiveValueMappings.h String customCSSText() const; bool equals(const CSSPrimitiveValue&) const; static double conversionToCanonicalUnitsScaleFactor(CSSUnitType); static String unitTypeString(CSSUnitType); static double computeUnzoomedNonCalcLengthDouble(CSSUnitType, double value, CSSPropertyID, const FontMetrics* = nullptr, const FontCascadeDescription* = nullptr, const FontCascadeDescription* rootFontDescription = nullptr, const RenderView* = nullptr); static double computeNonCalcLengthDouble(const CSSToLengthConversionData&, CSSUnitType, double value); // True if computeNonCalcLengthDouble would produce identical results when resolved against both these styles. static bool equalForLengthResolution(const RenderStyle&, const RenderStyle&); Ref createDeprecatedCSSOMPrimitiveWrapper(CSSStyleDeclaration&) const; void collectDirectComputationalDependencies(HashSet&) const; void collectDirectRootComputationalDependencies(HashSet&) const; private: friend class CSSValuePool; friend LazyNeverDestroyed; CSSPrimitiveValue(CSSValueID); CSSPrimitiveValue(CSSPropertyID); CSSPrimitiveValue(const Color&); CSSPrimitiveValue(const Length&); CSSPrimitiveValue(const Length&, const RenderStyle&); CSSPrimitiveValue(const LengthSize&, const RenderStyle&); CSSPrimitiveValue(const String&, CSSUnitType); CSSPrimitiveValue(double, CSSUnitType); CSSPrimitiveValue(StaticCSSValueTag, CSSValueID); CSSPrimitiveValue(StaticCSSValueTag, const Color&); CSSPrimitiveValue(StaticCSSValueTag, double, CSSUnitType); template CSSPrimitiveValue(T); // Defined in CSSPrimitiveValueMappings.h template CSSPrimitiveValue(RefPtr&&); template CSSPrimitiveValue(Ref&&); static void create(int); // compile-time guard static void create(unsigned); // compile-time guard template operator T*(); // compile-time guard void init(const Length&); void init(const LengthSize&, const RenderStyle&); void init(Ref&&); void init(RefPtr&&); void init(Ref&&); void init(Ref&&); void init(Ref&&); void init(Ref&&); CSSUnitType primitiveUnitType() const { return static_cast(m_primitiveUnitType); } void setPrimitiveUnitType(CSSUnitType type) { m_primitiveUnitType = static_cast(type); } std::optional doubleValueInternal(CSSUnitType targetUnitType) const; double computeLengthDouble(const CSSToLengthConversionData&) const; ALWAYS_INLINE String formatNumberForCustomCSSText() const; NEVER_INLINE String formatNumberValue(StringView) const; static constexpr bool isFontRelativeLength(CSSUnitType); static constexpr bool isResolution(CSSUnitType); static constexpr bool isViewportPercentageLength(CSSUnitType type) { return type >= CSSUnitType::CSS_VW && type <= CSSUnitType::CSS_VMAX; } union { CSSPropertyID propertyID; CSSValueID valueID; double num; StringImpl* string; Counter* counter; Rect* rect; Quad* quad; const Color* color; Pair* pair; CSSBasicShape* shape; CSSCalcValue* calc; const CSSFontFamily* fontFamily; } m_value; }; constexpr bool CSSPrimitiveValue::isFontRelativeLength(CSSUnitType type) { return type == CSSUnitType::CSS_EMS || type == CSSUnitType::CSS_EXS || type == CSSUnitType::CSS_LHS || type == CSSUnitType::CSS_RLHS || type == CSSUnitType::CSS_REMS || type == CSSUnitType::CSS_CHS || type == CSSUnitType::CSS_QUIRKY_EMS; } constexpr bool CSSPrimitiveValue::isLength(CSSUnitType type) { return (type >= CSSUnitType::CSS_EMS && type <= CSSUnitType::CSS_PC) || type == CSSUnitType::CSS_REMS || type == CSSUnitType::CSS_CHS || type == CSSUnitType::CSS_Q || type == CSSUnitType::CSS_LHS || type == CSSUnitType::CSS_RLHS || isViewportPercentageLength(type) || type == CSSUnitType::CSS_QUIRKY_EMS; } constexpr bool CSSPrimitiveValue::isResolution(CSSUnitType type) { return type >= CSSUnitType::CSS_DPPX && type <= CSSUnitType::CSS_DPCM; } template inline Ref CSSPrimitiveValue::create(T&& value) { return adoptRef(*new CSSPrimitiveValue(std::forward(value))); } template inline T CSSPrimitiveValue::computeTime() const { if (timeUnit == Seconds && primitiveType() == CSSUnitType::CSS_S) return value(); if (timeUnit == Seconds && primitiveType() == CSSUnitType::CSS_MS) return value() / 1000; if (timeUnit == Milliseconds && primitiveType() == CSSUnitType::CSS_MS) return value(); if (timeUnit == Milliseconds && primitiveType() == CSSUnitType::CSS_S) return value() * 1000; ASSERT_NOT_REACHED(); return 0; } template inline CSSPrimitiveValue::CSSPrimitiveValue(RefPtr&& value) : CSSValue(PrimitiveClass) { init(WTFMove(value)); } template inline CSSPrimitiveValue::CSSPrimitiveValue(Ref&& value) : CSSValue(PrimitiveClass) { init(WTFMove(value)); } inline double CSSPrimitiveValue::computeDegrees(CSSUnitType type, double angle) { switch (type) { case CSSUnitType::CSS_DEG: return angle; case CSSUnitType::CSS_RAD: return rad2deg(angle); case CSSUnitType::CSS_GRAD: return grad2deg(angle); case CSSUnitType::CSS_TURN: return turn2deg(angle); default: ASSERT_NOT_REACHED(); return 0; } } } // namespace WebCore SPECIALIZE_TYPE_TRAITS_CSS_VALUE(CSSPrimitiveValue, isPrimitiveValue())