blob: 941a440a732ed95e8a4c3f8a3ed20db94456b109 [file] [log] [blame]
/*
* Copyright (C) 2013-2014 Google Inc. All rights reserved.
* Copyright (C) 2014-2022 Apple Inc. All rights reserved.
* Copyright (C) 2025 Samuel Weinig <sam@webkit.org>
*
* 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 "CSSBoxShadowPropertyValue.h"
#include "CSSCalcSymbolTable.h"
#include "CSSColorValue.h"
#include "CSSCounterStyleRegistry.h"
#include "CSSCounterStyleRule.h"
#include "CSSCounterValue.h"
#include "CSSCursorImageValue.h"
#include "CSSFontValue.h"
#include "CSSGradientValue.h"
#include "CSSPropertyParserConsumer+Font.h"
#include "CSSRatioValue.h"
#include "CSSRectValue.h"
#include "CSSRegisteredCustomProperty.h"
#include "CSSTextShadowPropertyValue.h"
#include "CSSURLValue.h"
#include "ElementAncestorIteratorInlines.h"
#include "FontVariantBuilder.h"
#include "HTMLElement.h"
#include "LocalFrame.h"
#include "SVGElement.h"
#include "StyleBoxShadow.h"
#include "StyleBuilderConverter.h"
#include "StyleBuilderStateInlines.h"
#include "StyleCachedImage.h"
#include "StyleCursorImage.h"
#include "StyleCustomPropertyData.h"
#include "StyleFontSizeFunctions.h"
#include "StyleGeneratedImage.h"
#include "StyleImageSet.h"
#include "StyleRatio.h"
#include "StyleResolver.h"
#include "StyleScope.h"
#include "StyleTextShadow.h"
namespace WebCore {
namespace Style {
#define DECLARE_PROPERTY_CUSTOM_HANDLERS(property) \
static void applyInherit##property(BuilderState&); \
static void applyInitial##property(BuilderState&); \
static void applyValue##property(BuilderState&, CSSValue&)
template<typename T> inline T forwardInheritedValue(T&& value) { return std::forward<T>(value); }
template<auto R, typename V> inline Length<R, V> forwardInheritedValue(const Length<R, V>& value) { auto copy = value; return copy; }
inline AnchorNames forwardInheritedValue(const AnchorNames& value) { auto copy = value; return copy; }
inline AspectRatio forwardInheritedValue(const AspectRatio& value) { auto copy = value; return copy; }
inline BlockEllipsis forwardInheritedValue(const BlockEllipsis& value) { auto copy = value; return copy; }
inline BlockStepSize forwardInheritedValue(const BlockStepSize& value) { auto copy = value; return copy; }
inline BorderRadiusValue forwardInheritedValue(const BorderRadiusValue& value) { auto copy = value; return copy; }
inline BoxShadows forwardInheritedValue(const BoxShadows& value) { auto copy = value; return copy; }
inline ContainIntrinsicSize forwardInheritedValue(const ContainIntrinsicSize& value) { auto copy = value; return copy; }
inline ContainerNames forwardInheritedValue(const ContainerNames& value) { auto copy = value; return copy; }
inline Content forwardInheritedValue(const Content& value) { auto copy = value; return copy; }
inline WebCore::Color forwardInheritedValue(const WebCore::Color& value) { auto copy = value; return copy; }
inline Color forwardInheritedValue(const Color& value) { auto copy = value; return copy; }
inline WebCore::Length forwardInheritedValue(const WebCore::Length& value) { auto copy = value; return copy; }
inline LengthSize forwardInheritedValue(const LengthSize& value) { auto copy = value; return copy; }
inline LengthBox forwardInheritedValue(const LengthBox& value) { auto copy = value; return copy; }
inline GapGutter forwardInheritedValue(const GapGutter& value) { auto copy = value; return copy; }
inline FilterOperations forwardInheritedValue(const FilterOperations& value) { auto copy = value; return copy; }
inline TransformOperations forwardInheritedValue(const TransformOperations& value) { auto copy = value; return copy; }
inline ScrollMarginEdge forwardInheritedValue(const ScrollMarginEdge& value) { auto copy = value; return copy; }
inline ScrollPaddingEdge forwardInheritedValue(const ScrollPaddingEdge& value) { auto copy = value; return copy; }
inline MarginEdge forwardInheritedValue(const MarginEdge& value) { auto copy = value; return copy; }
inline PaddingEdge forwardInheritedValue(const PaddingEdge& value) { auto copy = value; return copy; }
inline InsetEdge forwardInheritedValue(const InsetEdge& value) { auto copy = value; return copy; }
inline Perspective forwardInheritedValue(const Perspective& value) { auto copy = value; return copy; }
inline Quotes forwardInheritedValue(const Quotes& value) { auto copy = value; return copy; }
inline Rotate forwardInheritedValue(const Rotate& value) { auto copy = value; return copy; }
inline Scale forwardInheritedValue(const Scale& value) { auto copy = value; return copy; }
inline Translate forwardInheritedValue(const Translate& value) { auto copy = value; return copy; }
inline PreferredSize forwardInheritedValue(const PreferredSize& value) { auto copy = value; return copy; }
inline MinimumSize forwardInheritedValue(const MinimumSize& value) { auto copy = value; return copy; }
inline MaximumSize forwardInheritedValue(const MaximumSize& value) { auto copy = value; return copy; }
inline FlexBasis forwardInheritedValue(const FlexBasis& value) { auto copy = value; return copy; }
inline DynamicRangeLimit forwardInheritedValue(const DynamicRangeLimit& value) { auto copy = value; return copy; }
inline Clip forwardInheritedValue(const Clip& value) { auto copy = value; return copy; }
inline ClipPath forwardInheritedValue(const ClipPath& value) { auto copy = value; return copy; }
inline CornerShapeValue forwardInheritedValue(const CornerShapeValue& value) { auto copy = value; return copy; }
inline GridPosition forwardInheritedValue(const GridPosition& value) { auto copy = value; return copy; }
inline GridTemplateAreas forwardInheritedValue(const GridTemplateAreas& value) { auto copy = value; return copy; }
inline GridTemplateList forwardInheritedValue(const GridTemplateList& value) { auto copy = value; return copy; }
inline GridTrackSizes forwardInheritedValue(const GridTrackSizes& value) { auto copy = value; return copy; }
inline HyphenateCharacter forwardInheritedValue(const HyphenateCharacter& value) { auto copy = value; return copy; }
inline ListStyleType forwardInheritedValue(const ListStyleType& value) { auto copy = value; return copy; }
inline OffsetAnchor forwardInheritedValue(const OffsetAnchor& value) { auto copy = value; return copy; }
inline OffsetDistance forwardInheritedValue(const OffsetDistance& value) { auto copy = value; return copy; }
inline OffsetPath forwardInheritedValue(const OffsetPath& value) { auto copy = value; return copy; }
inline OffsetPosition forwardInheritedValue(const OffsetPosition& value) { auto copy = value; return copy; }
inline OffsetRotate forwardInheritedValue(const OffsetRotate& value) { auto copy = value; return copy; }
inline Position forwardInheritedValue(const Position& value) { auto copy = value; return copy; }
inline PositionX forwardInheritedValue(const PositionX& value) { auto copy = value; return copy; }
inline PositionY forwardInheritedValue(const PositionY& value) { auto copy = value; return copy; }
inline SVGBaselineShift forwardInheritedValue(const SVGBaselineShift& value) { auto copy = value; return copy; }
inline SVGCenterCoordinateComponent forwardInheritedValue(const SVGCenterCoordinateComponent& value) { auto copy = value; return copy; }
inline SVGCoordinateComponent forwardInheritedValue(const SVGCoordinateComponent& value) { auto copy = value; return copy; }
inline SVGPaint forwardInheritedValue(const SVGPaint& value) { auto copy = value; return copy; }
inline SVGRadius forwardInheritedValue(const SVGRadius& value) { auto copy = value; return copy; }
inline SVGRadiusComponent forwardInheritedValue(const SVGRadiusComponent& value) { auto copy = value; return copy; }
inline SVGStrokeDasharray forwardInheritedValue(const SVGStrokeDasharray& value) { auto copy = value; return copy; }
inline SVGStrokeDashoffset forwardInheritedValue(const SVGStrokeDashoffset& value) { auto copy = value; return copy; }
inline ScrollbarColor forwardInheritedValue(const ScrollbarColor& value) { auto copy = value; return copy; }
inline ScrollbarGutter forwardInheritedValue(const ScrollbarGutter& value) { auto copy = value; return copy; }
inline ShapeMargin forwardInheritedValue(const ShapeMargin& value) { auto copy = value; return copy; }
inline ShapeOutside forwardInheritedValue(const ShapeOutside& value) { auto copy = value; return copy; }
inline StrokeWidth forwardInheritedValue(const StrokeWidth& value) { auto copy = value; return copy; }
inline TextDecorationThickness forwardInheritedValue(const TextDecorationThickness& value) { auto copy = value; return copy; }
inline TextEmphasisStyle forwardInheritedValue(const TextEmphasisStyle& value) { auto copy = value; return copy; }
inline TextIndent forwardInheritedValue(const TextIndent& value) { auto copy = value; return copy; }
inline TextShadows forwardInheritedValue(const TextShadows& value) { auto copy = value; return copy; }
inline TextUnderlineOffset forwardInheritedValue(const TextUnderlineOffset& value) { auto copy = value; return copy; }
inline URL forwardInheritedValue(const URL& value) { auto copy = value; return copy; }
inline FixedVector<WebCore::Length> forwardInheritedValue(const FixedVector<WebCore::Length>& value) { auto copy = value; return copy; }
inline FixedVector<PositionTryFallback> forwardInheritedValue(const FixedVector<PositionTryFallback>& value) { auto copy = value; return copy; }
inline ProgressTimelineAxes forwardInheritedValue(const ProgressTimelineAxes& value) { auto copy = value; return copy; }
inline ProgressTimelineNames forwardInheritedValue(const ProgressTimelineNames& value) { auto copy = value; return copy; }
inline ScrollTimelines forwardInheritedValue(const ScrollTimelines& value) { auto copy = value; return copy; }
inline VerticalAlign forwardInheritedValue(const VerticalAlign& value) { auto copy = value; return copy; }
inline ViewTimelineInsets forwardInheritedValue(const ViewTimelineInsets& value) { auto copy = value; return copy; }
inline ViewTimelines forwardInheritedValue(const ViewTimelines& value) { auto copy = value; return copy; }
inline ViewTransitionClasses forwardInheritedValue(const ViewTransitionClasses& value) { auto copy = value; return copy; }
inline ViewTransitionName forwardInheritedValue(const ViewTransitionName& value) { auto copy = value; return copy; }
inline Vector<GridTrackSize> forwardInheritedValue(const Vector<GridTrackSize>& value) { auto copy = value; return copy; }
inline WebkitLineGrid forwardInheritedValue(const WebkitLineGrid& value) { auto copy = value; return copy; }
// Note that we assume the CSS parser only allows valid CSSValue types.
class BuilderCustom {
public:
// Custom handling of inherit, initial and value setting.
// FIXME: <https://webkit.org/b/212506> Teach makeprop.pl to generate setters for hasExplicitlySet* flags
DECLARE_PROPERTY_CUSTOM_HANDLERS(BorderBottomLeftRadius);
DECLARE_PROPERTY_CUSTOM_HANDLERS(BorderBottomRightRadius);
DECLARE_PROPERTY_CUSTOM_HANDLERS(BorderTopLeftRadius);
DECLARE_PROPERTY_CUSTOM_HANDLERS(BorderTopRightRadius);
DECLARE_PROPERTY_CUSTOM_HANDLERS(BorderImageOutset);
DECLARE_PROPERTY_CUSTOM_HANDLERS(BorderImageRepeat);
DECLARE_PROPERTY_CUSTOM_HANDLERS(BorderImageSlice);
DECLARE_PROPERTY_CUSTOM_HANDLERS(BorderImageWidth);
DECLARE_PROPERTY_CUSTOM_HANDLERS(BoxShadow);
DECLARE_PROPERTY_CUSTOM_HANDLERS(CaretColor);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Color);
DECLARE_PROPERTY_CUSTOM_HANDLERS(CounterIncrement);
DECLARE_PROPERTY_CUSTOM_HANDLERS(CounterReset);
DECLARE_PROPERTY_CUSTOM_HANDLERS(CounterSet);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Fill);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontFamily);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontSize);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontStyle);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontVariantAlternates);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontVariantLigatures);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontVariantNumeric);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontVariantEastAsian);
DECLARE_PROPERTY_CUSTOM_HANDLERS(LetterSpacing);
#if ENABLE(TEXT_AUTOSIZING)
DECLARE_PROPERTY_CUSTOM_HANDLERS(LineHeight);
#endif
DECLARE_PROPERTY_CUSTOM_HANDLERS(MaskBorderOutset);
DECLARE_PROPERTY_CUSTOM_HANDLERS(MaskBorderRepeat);
DECLARE_PROPERTY_CUSTOM_HANDLERS(MaskBorderSlice);
DECLARE_PROPERTY_CUSTOM_HANDLERS(MaskBorderWidth);
DECLARE_PROPERTY_CUSTOM_HANDLERS(PaddingBottom);
DECLARE_PROPERTY_CUSTOM_HANDLERS(PaddingLeft);
DECLARE_PROPERTY_CUSTOM_HANDLERS(PaddingRight);
DECLARE_PROPERTY_CUSTOM_HANDLERS(PaddingTop);
DECLARE_PROPERTY_CUSTOM_HANDLERS(OutlineStyle);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Stroke);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Zoom);
// Custom handling of inherit setting only.
static void applyInheritWordSpacing(BuilderState&);
// Custom handling of value setting only.
static void applyValueDirection(BuilderState&, CSSValue&);
static void applyValueWebkitLocale(BuilderState&, CSSValue&);
static void applyValueTextOrientation(BuilderState&, CSSValue&);
#if ENABLE(TEXT_AUTOSIZING)
static void applyValueWebkitTextSizeAdjust(BuilderState&, CSSValue&);
#endif
static void applyValueWebkitTextZoom(BuilderState&, CSSValue&);
static void applyValueWritingMode(BuilderState&, CSSValue&);
static void applyValueFontSizeAdjust(BuilderState&, CSSValue&);
#if ENABLE(DARK_MODE_CSS)
static void applyValueColorScheme(BuilderState&, CSSValue&);
#endif
static void applyValueStrokeWidth(BuilderState&, CSSValue&);
static void applyValueStrokeColor(BuilderState&, CSSValue&);
private:
static void resetUsedZoom(BuilderState&);
enum CounterBehavior { Increment, Reset, Set };
template <CounterBehavior counterBehavior>
static void applyInheritCounter(BuilderState&);
template <CounterBehavior counterBehavior>
static void applyValueCounter(BuilderState&, CSSValue&);
static float largerFontSize(float size);
static float smallerFontSize(float size);
static float determineRubyTextSizeMultiplier(BuilderState&);
};
inline void BuilderCustom::applyValueDirection(BuilderState& builderState, CSSValue& value)
{
builderState.style().setDirection(fromCSSValue<TextDirection>(value));
builderState.style().setHasExplicitlySetDirection();
}
inline void BuilderCustom::resetUsedZoom(BuilderState& builderState)
{
// Reset the zoom in effect. This allows the setZoom method to accurately compute a new zoom in effect.
builderState.setUsedZoom(builderState.parentStyle().usedZoom());
}
inline void BuilderCustom::applyInitialZoom(BuilderState& builderState)
{
resetUsedZoom(builderState);
builderState.setZoom(RenderStyle::initialZoom());
}
inline void BuilderCustom::applyInheritZoom(BuilderState& builderState)
{
resetUsedZoom(builderState);
builderState.setZoom(forwardInheritedValue(builderState.parentStyle().zoom()));
}
inline void BuilderCustom::applyValueZoom(BuilderState& builderState, CSSValue& value)
{
auto primitiveValue = requiredDowncast<CSSPrimitiveValue>(builderState, value);
if (!primitiveValue)
return;
if (primitiveValue->valueID() == CSSValueNormal) {
resetUsedZoom(builderState);
builderState.setZoom(RenderStyle::initialZoom());
} else if (primitiveValue->isPercentage()) {
resetUsedZoom(builderState);
if (float percent = primitiveValue->resolveAsPercentage<float>(builderState.cssToLengthConversionData()))
builderState.setZoom(percent / 100.0f);
} else if (primitiveValue->isNumber()) {
resetUsedZoom(builderState);
if (float number = primitiveValue->resolveAsNumber<float>(builderState.cssToLengthConversionData()))
builderState.setZoom(number);
}
}
enum BorderImageType { BorderImage, MaskBorder };
enum BorderImageModifierType { Outset, Repeat, Slice, Width };
template<BorderImageType type, BorderImageModifierType modifier>
class ApplyPropertyBorderImageModifier {
public:
static void applyInheritValue(BuilderState& builderState)
{
NinePieceImage image(getValue(builderState.style()));
switch (modifier) {
case Outset:
image.copyOutsetFrom(getValue(builderState.parentStyle()));
break;
case Repeat:
image.copyRepeatFrom(getValue(builderState.parentStyle()));
break;
case Slice:
image.copyImageSlicesFrom(getValue(builderState.parentStyle()));
break;
case Width:
image.copyBorderSlicesFrom(getValue(builderState.parentStyle()));
break;
}
setValue(builderState.style(), image);
}
static void applyInitialValue(BuilderState& builderState)
{
NinePieceImage image(getValue(builderState.style()));
switch (modifier) {
case Outset:
image.setOutset(LengthBox(LengthType::Relative));
break;
case Repeat:
image.setHorizontalRule(NinePieceImageRule::Stretch);
image.setVerticalRule(NinePieceImageRule::Stretch);
break;
case Slice:
// Masks have a different initial value for slices. Preserve the value of "0 fill" for backwards compatibility.
image.setImageSlices(type == BorderImage ? LengthBox(WebCore::Length(100, LengthType::Percent), WebCore::Length(100, LengthType::Percent), WebCore::Length(100, LengthType::Percent), WebCore::Length(100, LengthType::Percent)) : LengthBox(LengthType::Fixed));
image.setFill(false);
break;
case Width:
// FIXME: This is a local variable to work around a bug in the GCC 8.1 Address Sanitizer.
// Might be slightly less efficient when the type is not BorderImage since this is unused in that case.
// Should be switched back to a temporary when possible. See https://webkit.org/b/186980
LengthBox lengthBox(WebCore::Length(1, LengthType::Relative), WebCore::Length(1, LengthType::Relative), WebCore::Length(1, LengthType::Relative), WebCore::Length(1, LengthType::Relative));
// Masks have a different initial value for widths. They use an 'auto' value rather than trying to fit to the border.
image.setBorderSlices(type == BorderImage ? lengthBox : LengthBox());
image.setOverridesBorderWidths(false);
break;
}
setValue(builderState.style(), image);
}
static void applyValue(BuilderState& builderState, CSSValue& value)
{
NinePieceImage image(getValue(builderState.style()));
switch (modifier) {
case Outset:
image.setOutset(builderState.styleMap().mapNinePieceImageQuad(value));
break;
case Repeat:
builderState.styleMap().mapNinePieceImageRepeat(value, image);
break;
case Slice:
builderState.styleMap().mapNinePieceImageSlice(value, image);
break;
case Width:
builderState.styleMap().mapNinePieceImageWidth(value, image);
break;
}
setValue(builderState.style(), image);
}
private:
static const NinePieceImage& getValue(const RenderStyle& style)
{
return type == BorderImage ? style.borderImage() : style.maskBorder();
}
static void setValue(RenderStyle& style, const NinePieceImage& value)
{
return type == BorderImage ? style.setBorderImage(value) : style.setMaskBorder(value);
}
};
#define DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(type, modifier) \
inline void BuilderCustom::applyInherit##type##modifier(BuilderState& builderState) \
{ \
ApplyPropertyBorderImageModifier<type, modifier>::applyInheritValue(builderState); \
} \
inline void BuilderCustom::applyInitial##type##modifier(BuilderState& builderState) \
{ \
ApplyPropertyBorderImageModifier<type, modifier>::applyInitialValue(builderState); \
} \
inline void BuilderCustom::applyValue##type##modifier(BuilderState& builderState, CSSValue& value) \
{ \
ApplyPropertyBorderImageModifier<type, modifier>::applyValue(builderState, value); \
}
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(BorderImage, Outset)
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(BorderImage, Repeat)
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(BorderImage, Slice)
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(BorderImage, Width)
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(MaskBorder, Outset)
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(MaskBorder, Repeat)
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(MaskBorder, Slice)
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(MaskBorder, Width)
inline void BuilderCustom::applyInheritWordSpacing(BuilderState& builderState)
{
builderState.style().setWordSpacing(forwardInheritedValue(builderState.parentStyle().computedWordSpacing()));
}
inline void BuilderCustom::applyInheritLetterSpacing(BuilderState& builderState)
{
builderState.style().setLetterSpacing(forwardInheritedValue(builderState.parentStyle().computedLetterSpacing()));
}
inline void BuilderCustom::applyInitialLetterSpacing(BuilderState& builderState)
{
builderState.style().setLetterSpacing(RenderStyle::initialLetterSpacing());
}
void maybeUpdateFontForLetterSpacing(BuilderState& builderState, CSSValue& value)
{
// This is unfortunate. It's related to https://github.com/w3c/csswg-drafts/issues/5498.
//
// From StyleBuilder's point of view, there's a dependency cycle:
// letter-spacing accepts an arbitrary <length>, which must be resolved against a font, which must
// be selected after all the properties that affect font selection are processed, but letter-spacing
// itself affects font selection because it can disable font features. StyleBuilder has some (valid)
// ASSERT()s which would fire because of this cycle.
//
// There isn't *actually* a dependency cycle, though, as none of the font-relative units are
// actually sensitive to font features (luckily). The problem is that our StyleBuilder is only
// smart enough to consider fonts as one indivisible thing, rather than having the deeper
// understanding that different parts of fonts may or may not depend on each other.
//
// So, we update the font early here, so that if there is a font-relative unit inside the CSSValue,
// its font is updated and ready to go. In the worst case there might be a second call to
// updateFont() later, but that isn't bad for perf because 1. It only happens twice if there is
// actually a font-relative unit passed to letter-spacing, and 2. updateFont() internally has logic
// to only do work if the font is actually dirty.
if (auto* primitiveValue = dynamicDowncast<CSSPrimitiveValue>(value)) {
if (primitiveValue->isFontRelativeLength() || primitiveValue->isCalculated())
builderState.updateFont();
}
}
inline void BuilderCustom::applyValueLetterSpacing(BuilderState& builderState, CSSValue& value)
{
maybeUpdateFontForLetterSpacing(builderState, value);
builderState.style().setLetterSpacing(BuilderConverter::convertTextLengthOrNormal(builderState, value));
}
#if ENABLE(TEXT_AUTOSIZING)
inline void BuilderCustom::applyInheritLineHeight(BuilderState& builderState)
{
builderState.style().setLineHeight(forwardInheritedValue(builderState.parentStyle().lineHeight()));
builderState.style().setSpecifiedLineHeight(forwardInheritedValue(builderState.parentStyle().specifiedLineHeight()));
}
inline void BuilderCustom::applyInitialLineHeight(BuilderState& builderState)
{
builderState.style().setLineHeight(RenderStyle::initialLineHeight());
builderState.style().setSpecifiedLineHeight(RenderStyle::initialSpecifiedLineHeight());
}
static inline float computeBaseSpecifiedFontSize(const Document& document, const RenderStyle& style, bool percentageAutosizingEnabled)
{
float result = style.specifiedFontSize();
auto* frame = document.frame();
if (frame && style.textZoom() != TextZoom::Reset)
result *= frame->textZoomFactor();
result *= style.usedZoom();
if (percentageAutosizingEnabled
&& (!document.settings().textAutosizingUsesIdempotentMode() || document.settings().idempotentModeAutosizingOnlyHonorsPercentages()))
result *= style.textSizeAdjust().multiplier();
return result;
}
static inline float computeLineHeightMultiplierDueToFontSize(const Document& document, const RenderStyle& style, const CSSPrimitiveValue& value)
{
bool percentageAutosizingEnabled = document.settings().textAutosizingEnabled() && style.textSizeAdjust().isPercentage();
if (value.isLength()) {
auto minimumFontSize = document.settings().minimumFontSize();
if (minimumFontSize > 0) {
auto specifiedFontSize = computeBaseSpecifiedFontSize(document, style, percentageAutosizingEnabled);
// Small font sizes cause a preposterously large (near infinity) line-height. Add a fuzz-factor of 1px which opts out of
// boosted line-height.
if (specifiedFontSize < minimumFontSize && specifiedFontSize >= 1) {
// FIXME: There are two settings which are relevant here: minimum font size, and minimum logical font size (as
// well as things like the zoom property, text zoom on the page, and text autosizing). The minimum logical font
// size is nonzero by default, and already incorporated into the computed font size, so if we just use the ratio
// of the computed : specified font size, it will be > 1 in the cases where the minimum logical font size kicks
// in. In general, this is the right thing to do, however, this kind of blanket change is too risky to perform
// right now. https://bugs.webkit.org/show_bug.cgi?id=174570 tracks turning this on. For now, we can just pretend
// that the minimum font size is the only thing affecting the computed font size.
// This calculation matches the line-height computed size calculation in
// TextAutoSizing::Value::adjustTextNodeSizes().
auto scaleChange = minimumFontSize / specifiedFontSize;
return scaleChange;
}
}
}
if (percentageAutosizingEnabled && !document.settings().textAutosizingUsesIdempotentMode())
return style.textSizeAdjust().multiplier();
return 1;
}
inline void BuilderCustom::applyValueLineHeight(BuilderState& builderState, CSSValue& value)
{
if (CSSPropertyParserHelpers::isSystemFontShorthand(value.valueID())) {
applyInitialLineHeight(builderState);
return;
}
auto lineHeight = BuilderConverter::convertLineHeight(builderState, value, 1);
WebCore::Length computedLineHeight;
if (lineHeight.isNormal())
computedLineHeight = lineHeight;
else {
auto primitiveValue = requiredDowncast<CSSPrimitiveValue>(builderState, value);
if (!primitiveValue)
return;
auto multiplier = computeLineHeightMultiplierDueToFontSize(builderState.document(), builderState.style(), *primitiveValue);
if (multiplier == 1)
computedLineHeight = lineHeight;
else
computedLineHeight = BuilderConverter::convertLineHeight(builderState, value, multiplier);
}
builderState.style().setLineHeight(WTFMove(computedLineHeight));
builderState.style().setSpecifiedLineHeight(WTFMove(lineHeight));
}
#endif
inline void BuilderCustom::applyInitialCaretColor(BuilderState& builderState)
{
if (builderState.applyPropertyToRegularStyle())
builderState.style().setHasAutoCaretColor();
if (builderState.applyPropertyToVisitedLinkStyle())
builderState.style().setHasVisitedLinkAutoCaretColor();
}
inline void BuilderCustom::applyInheritCaretColor(BuilderState& builderState)
{
auto& color = builderState.parentStyle().caretColor();
if (builderState.applyPropertyToRegularStyle()) {
if (builderState.parentStyle().hasAutoCaretColor())
builderState.style().setHasAutoCaretColor();
else
builderState.style().setCaretColor(forwardInheritedValue(color));
}
if (builderState.applyPropertyToVisitedLinkStyle()) {
if (builderState.parentStyle().hasVisitedLinkAutoCaretColor())
builderState.style().setHasVisitedLinkAutoCaretColor();
else
builderState.style().setVisitedLinkCaretColor(forwardInheritedValue(color));
}
}
inline void BuilderCustom::applyValueCaretColor(BuilderState& builderState, CSSValue& value)
{
if (builderState.applyPropertyToRegularStyle()) {
if (value.valueID() == CSSValueAuto)
builderState.style().setHasAutoCaretColor();
else
builderState.style().setCaretColor(toStyleFromCSSValue<Color>(builderState, value, ForVisitedLink::No));
}
if (builderState.applyPropertyToVisitedLinkStyle()) {
if (value.valueID() == CSSValueAuto)
builderState.style().setHasVisitedLinkAutoCaretColor();
else
builderState.style().setVisitedLinkCaretColor(toStyleFromCSSValue<Color>(builderState, value, ForVisitedLink::Yes));
}
}
inline void BuilderCustom::applyValueWebkitLocale(BuilderState& builderState, CSSValue& value)
{
auto primitiveValue = requiredDowncast<CSSPrimitiveValue>(builderState, value);
if (!primitiveValue)
return;
if (primitiveValue->valueID() == CSSValueAuto)
builderState.setFontDescriptionSpecifiedLocale(nullAtom());
else
builderState.setFontDescriptionSpecifiedLocale(AtomString { primitiveValue->stringValue() });
}
inline void BuilderCustom::applyValueWritingMode(BuilderState& builderState, CSSValue& value)
{
builderState.setWritingMode(fromCSSValue<StyleWritingMode>(value));
builderState.style().setHasExplicitlySetWritingMode();
}
inline void BuilderCustom::applyValueTextOrientation(BuilderState& builderState, CSSValue& value)
{
builderState.setTextOrientation(fromCSSValue<TextOrientation>(value));
}
#if ENABLE(TEXT_AUTOSIZING)
inline void BuilderCustom::applyValueWebkitTextSizeAdjust(BuilderState& builderState, CSSValue& value)
{
builderState.style().setTextSizeAdjust(toStyleFromCSSValue<TextSizeAdjust>(builderState, value));
builderState.setFontDirty();
}
#endif
inline void BuilderCustom::applyValueWebkitTextZoom(BuilderState& builderState, CSSValue& value)
{
auto primitiveValue = requiredDowncast<CSSPrimitiveValue>(builderState, value);
if (!primitiveValue)
return;
if (primitiveValue->valueID() == CSSValueNormal)
builderState.style().setTextZoom(TextZoom::Normal);
else if (primitiveValue->valueID() == CSSValueReset)
builderState.style().setTextZoom(TextZoom::Reset);
builderState.setFontDirty();
}
#if ENABLE(DARK_MODE_CSS)
inline void BuilderCustom::applyValueColorScheme(BuilderState& builderState, CSSValue& value)
{
builderState.style().setColorScheme(BuilderConverter::convertStyleType<ColorScheme>(builderState, value));
builderState.style().setHasExplicitlySetColorScheme();
}
#endif
inline void BuilderCustom::applyInitialFontFamily(BuilderState& builderState)
{
auto& fontDescription = builderState.fontDescription();
auto initialDesc = FontCascadeDescription();
// We need to adjust the size to account for the generic family change from monospace to non-monospace.
if (fontDescription.useFixedDefaultSize()) {
if (CSSValueID sizeIdentifier = fontDescription.keywordSizeAsIdentifier())
builderState.setFontDescriptionFontSize(Style::fontSizeForKeyword(sizeIdentifier, false, builderState.document()));
}
if (!initialDesc.firstFamily().isEmpty())
builderState.setFontDescriptionFamilies(initialDesc.families());
}
inline void BuilderCustom::applyInheritFontFamily(BuilderState& builderState)
{
auto parentFontDescription = builderState.parentStyle().fontDescription();
builderState.setFontDescriptionFamilies(parentFontDescription.families());
builderState.setFontDescriptionIsSpecifiedFont(parentFontDescription.isSpecifiedFont());
}
inline void BuilderCustom::applyValueFontFamily(BuilderState& builderState, CSSValue& value)
{
auto& fontDescription = builderState.fontDescription();
// Before mapping in a new font-family property, we should reset the generic family.
bool oldFamilyUsedFixedDefaultSize = fontDescription.useFixedDefaultSize();
Vector<AtomString> families;
if (is<CSSPrimitiveValue>(value)) {
auto valueID = value.valueID();
if (!CSSPropertyParserHelpers::isSystemFontShorthand(valueID)) {
// Early return if the invalid CSSValueID is set while using CSSOM API.
return;
}
AtomString family = SystemFontDatabase::singleton().systemFontShorthandFamily(CSSPropertyParserHelpers::lowerFontShorthand(valueID));
ASSERT(!family.isEmpty());
builderState.setFontDescriptionIsSpecifiedFont(false);
families = Vector<AtomString>::from(WTFMove(family));
} else {
auto valueList = requiredListDowncast<CSSValueList, CSSPrimitiveValue>(builderState, value);
if (!valueList)
return;
bool isFirstFont = true;
families = WTF::compactMap(*valueList, [&](auto& contentValue) -> std::optional<AtomString> {
AtomString family;
bool isGenericFamily = false;
if (contentValue.isFontFamily())
family = AtomString { contentValue.stringValue() };
else if (contentValue.valueID() == CSSValueWebkitBody)
family = AtomString { builderState.document().settings().standardFontFamily() };
else {
isGenericFamily = true;
family = CSSPropertyParserHelpers::genericFontFamily(contentValue.valueID());
ASSERT(!family.isEmpty());
}
if (family.isNull())
return std::nullopt;
if (isFirstFont) {
builderState.setFontDescriptionIsSpecifiedFont(!isGenericFamily);
isFirstFont = false;
}
return family;
});
if (families.isEmpty())
return;
}
builderState.setFontDescriptionFamilies(families);
if (fontDescription.useFixedDefaultSize() != oldFamilyUsedFixedDefaultSize) {
if (CSSValueID sizeIdentifier = fontDescription.keywordSizeAsIdentifier())
builderState.setFontDescriptionFontSize(Style::fontSizeForKeyword(sizeIdentifier, !oldFamilyUsedFixedDefaultSize, builderState.document()));
}
}
inline void BuilderCustom::applyInitialBorderBottomLeftRadius(BuilderState& builderState)
{
builderState.style().setBorderBottomLeftRadius(RenderStyle::initialBorderRadius());
builderState.style().setHasExplicitlySetBorderBottomLeftRadius(false);
}
inline void BuilderCustom::applyInheritBorderBottomLeftRadius(BuilderState& builderState)
{
builderState.style().setBorderBottomLeftRadius(forwardInheritedValue(builderState.parentStyle().borderBottomLeftRadius()));
builderState.style().setHasExplicitlySetBorderBottomLeftRadius(builderState.parentStyle().hasExplicitlySetBorderBottomLeftRadius());
}
inline void BuilderCustom::applyValueBorderBottomLeftRadius(BuilderState& builderState, CSSValue& value)
{
builderState.style().setBorderBottomLeftRadius(BuilderConverter::convertStyleType<BorderRadiusValue>(builderState, value));
builderState.style().setHasExplicitlySetBorderBottomLeftRadius(true);
}
inline void BuilderCustom::applyInitialBorderBottomRightRadius(BuilderState& builderState)
{
builderState.style().setBorderBottomRightRadius(RenderStyle::initialBorderRadius());
builderState.style().setHasExplicitlySetBorderBottomRightRadius(false);
}
inline void BuilderCustom::applyInheritBorderBottomRightRadius(BuilderState& builderState)
{
builderState.style().setBorderBottomRightRadius(forwardInheritedValue(builderState.parentStyle().borderBottomRightRadius()));
builderState.style().setHasExplicitlySetBorderBottomRightRadius(builderState.parentStyle().hasExplicitlySetBorderBottomRightRadius());
}
inline void BuilderCustom::applyValueBorderBottomRightRadius(BuilderState& builderState, CSSValue& value)
{
builderState.style().setBorderBottomRightRadius(BuilderConverter::convertStyleType<BorderRadiusValue>(builderState, value));
builderState.style().setHasExplicitlySetBorderBottomRightRadius(true);
}
inline void BuilderCustom::applyInitialBorderTopLeftRadius(BuilderState& builderState)
{
builderState.style().setBorderTopLeftRadius(RenderStyle::initialBorderRadius());
builderState.style().setHasExplicitlySetBorderTopLeftRadius(false);
}
inline void BuilderCustom::applyInheritBorderTopLeftRadius(BuilderState& builderState)
{
builderState.style().setBorderTopLeftRadius(forwardInheritedValue(builderState.parentStyle().borderTopLeftRadius()));
builderState.style().setHasExplicitlySetBorderTopLeftRadius(builderState.parentStyle().hasExplicitlySetBorderTopLeftRadius());
}
inline void BuilderCustom::applyValueBorderTopLeftRadius(BuilderState& builderState, CSSValue& value)
{
builderState.style().setBorderTopLeftRadius(BuilderConverter::convertStyleType<BorderRadiusValue>(builderState, value));
builderState.style().setHasExplicitlySetBorderTopLeftRadius(true);
}
inline void BuilderCustom::applyInitialBorderTopRightRadius(BuilderState& builderState)
{
builderState.style().setBorderTopRightRadius(RenderStyle::initialBorderRadius());
builderState.style().setHasExplicitlySetBorderTopRightRadius(false);
}
inline void BuilderCustom::applyInheritBorderTopRightRadius(BuilderState& builderState)
{
builderState.style().setBorderTopRightRadius(forwardInheritedValue(builderState.parentStyle().borderTopRightRadius()));
builderState.style().setHasExplicitlySetBorderTopRightRadius(builderState.parentStyle().hasExplicitlySetBorderTopRightRadius());
}
inline void BuilderCustom::applyValueBorderTopRightRadius(BuilderState& builderState, CSSValue& value)
{
builderState.style().setBorderTopRightRadius(BuilderConverter::convertStyleType<BorderRadiusValue>(builderState, value));
builderState.style().setHasExplicitlySetBorderTopRightRadius(true);
}
template<BuilderCustom::CounterBehavior counterBehavior>
inline void BuilderCustom::applyInheritCounter(BuilderState& builderState)
{
auto& map = builderState.style().accessCounterDirectives().map;
for (auto& keyValue : builderState.parentStyle().counterDirectives().map) {
auto& directives = map.add(keyValue.key, CounterDirectives { }).iterator->value;
if (counterBehavior == Reset)
directives.resetValue = keyValue.value.resetValue;
else if (counterBehavior == Increment)
directives.incrementValue = keyValue.value.incrementValue;
else
directives.setValue = keyValue.value.setValue;
}
}
template<BuilderCustom::CounterBehavior counterBehavior>
inline void BuilderCustom::applyValueCounter(BuilderState& builderState, CSSValue& value)
{
bool setCounterIncrementToNone = counterBehavior == Increment && value.valueID() == CSSValueNone;
if (!is<CSSValueList>(value) && !setCounterIncrementToNone)
return;
auto& map = builderState.style().accessCounterDirectives().map;
for (auto& keyValue : map) {
if (counterBehavior == Reset)
keyValue.value.resetValue = std::nullopt;
else if (counterBehavior == Increment)
keyValue.value.incrementValue = std::nullopt;
else
keyValue.value.setValue = std::nullopt;
}
if (setCounterIncrementToNone)
return;
auto& conversionData = builderState.cssToLengthConversionData();
auto list = requiredListDowncast<CSSValueList, CSSValuePair>(builderState, value);
if (!list)
return;
for (auto& pairValue : *list) {
auto pair = requiredPairDowncast<CSSPrimitiveValue>(builderState, pairValue);
if (!pair)
return;
AtomString identifier { pair->first->stringValue() };
int value = pair->second->resolveAsNumber<int>(conversionData);
auto& directives = map.add(identifier, CounterDirectives { }).iterator->value;
if (counterBehavior == Reset)
directives.resetValue = value;
else if (counterBehavior == Increment)
directives.incrementValue = saturatedSum(directives.incrementValue.value_or(0), value);
else
directives.setValue = value;
}
}
inline void BuilderCustom::applyInitialCounterIncrement(BuilderState&)
{
}
inline void BuilderCustom::applyInheritCounterIncrement(BuilderState& builderState)
{
applyInheritCounter<Increment>(builderState);
}
inline void BuilderCustom::applyValueCounterIncrement(BuilderState& builderState, CSSValue& value)
{
applyValueCounter<Increment>(builderState, value);
}
inline void BuilderCustom::applyInitialCounterReset(BuilderState&)
{
}
inline void BuilderCustom::applyInheritCounterReset(BuilderState& builderState)
{
applyInheritCounter<Reset>(builderState);
}
inline void BuilderCustom::applyValueCounterReset(BuilderState& builderState, CSSValue& value)
{
applyValueCounter<Reset>(builderState, value);
}
inline void BuilderCustom::applyInitialCounterSet(BuilderState&)
{
}
inline void BuilderCustom::applyInheritCounterSet(BuilderState& builderState)
{
applyInheritCounter<Set>(builderState);
}
inline void BuilderCustom::applyValueCounterSet(BuilderState& builderState, CSSValue& value)
{
applyValueCounter<Set>(builderState, value);
}
inline void BuilderCustom::applyInitialFill(BuilderState& builderState)
{
auto& svgStyle = builderState.style().accessSVGStyle();
if (builderState.applyPropertyToRegularStyle())
svgStyle.setFill(SVGRenderStyle::initialFill());
if (builderState.applyPropertyToVisitedLinkStyle())
svgStyle.setVisitedLinkFill(SVGRenderStyle::initialFill());
}
inline void BuilderCustom::applyInheritFill(BuilderState& builderState)
{
auto& svgStyle = builderState.style().accessSVGStyle();
auto& svgParentStyle = builderState.parentStyle().svgStyle();
if (builderState.applyPropertyToRegularStyle())
svgStyle.setFill(forwardInheritedValue(svgParentStyle.fill()));
if (builderState.applyPropertyToVisitedLinkStyle())
svgStyle.setVisitedLinkFill(forwardInheritedValue(svgParentStyle.fill()));
}
inline void BuilderCustom::applyValueFill(BuilderState& builderState, CSSValue& value)
{
auto& svgStyle = builderState.style().accessSVGStyle();
if (builderState.applyPropertyToRegularStyle())
svgStyle.setFill(BuilderConverter::convertStyleType<SVGPaint>(builderState, value, ForVisitedLink::No));
if (builderState.applyPropertyToVisitedLinkStyle())
svgStyle.setVisitedLinkFill(BuilderConverter::convertStyleType<SVGPaint>(builderState, value, ForVisitedLink::Yes));
}
inline void BuilderCustom::applyInitialStroke(BuilderState& builderState)
{
auto& svgStyle = builderState.style().accessSVGStyle();
if (builderState.applyPropertyToRegularStyle())
svgStyle.setStroke(SVGRenderStyle::initialStroke());
if (builderState.applyPropertyToVisitedLinkStyle())
svgStyle.setVisitedLinkStroke(SVGRenderStyle::initialStroke());
}
inline void BuilderCustom::applyInheritStroke(BuilderState& builderState)
{
auto& svgStyle = builderState.style().accessSVGStyle();
auto& svgParentStyle = builderState.parentStyle().svgStyle();
if (builderState.applyPropertyToRegularStyle())
svgStyle.setStroke(forwardInheritedValue(svgParentStyle.stroke()));
if (builderState.applyPropertyToVisitedLinkStyle())
svgStyle.setVisitedLinkStroke(forwardInheritedValue(svgParentStyle.stroke()));
}
inline void BuilderCustom::applyValueStroke(BuilderState& builderState, CSSValue& value)
{
auto& svgStyle = builderState.style().accessSVGStyle();
if (builderState.applyPropertyToRegularStyle())
svgStyle.setStroke(BuilderConverter::convertStyleType<SVGPaint>(builderState, value, ForVisitedLink::No));
if (builderState.applyPropertyToVisitedLinkStyle())
svgStyle.setVisitedLinkStroke(BuilderConverter::convertStyleType<SVGPaint>(builderState, value, ForVisitedLink::Yes));
}
inline void BuilderCustom::applyInheritFontVariantLigatures(BuilderState& builderState)
{
builderState.setFontDescriptionVariantCommonLigatures(builderState.parentFontDescription().variantCommonLigatures());
builderState.setFontDescriptionVariantDiscretionaryLigatures(builderState.parentFontDescription().variantDiscretionaryLigatures());
builderState.setFontDescriptionVariantHistoricalLigatures(builderState.parentFontDescription().variantHistoricalLigatures());
builderState.setFontDescriptionVariantContextualAlternates(builderState.parentFontDescription().variantContextualAlternates());
}
inline void BuilderCustom::applyInitialFontVariantLigatures(BuilderState& builderState)
{
builderState.setFontDescriptionVariantCommonLigatures(FontVariantLigatures::Normal);
builderState.setFontDescriptionVariantDiscretionaryLigatures(FontVariantLigatures::Normal);
builderState.setFontDescriptionVariantHistoricalLigatures(FontVariantLigatures::Normal);
builderState.setFontDescriptionVariantContextualAlternates(FontVariantLigatures::Normal);
}
inline void BuilderCustom::applyValueFontVariantLigatures(BuilderState& builderState, CSSValue& value)
{
if (CSSPropertyParserHelpers::isSystemFontShorthand(value.valueID())) {
applyInitialFontVariantLigatures(builderState);
return;
}
auto variantLigatures = extractFontVariantLigatures(value);
builderState.setFontDescriptionVariantCommonLigatures(variantLigatures.commonLigatures);
builderState.setFontDescriptionVariantDiscretionaryLigatures(variantLigatures.discretionaryLigatures);
builderState.setFontDescriptionVariantHistoricalLigatures(variantLigatures.historicalLigatures);
builderState.setFontDescriptionVariantContextualAlternates(variantLigatures.contextualAlternates);
}
inline void BuilderCustom::applyInheritFontVariantNumeric(BuilderState& builderState)
{
builderState.setFontDescriptionVariantNumericFigure(builderState.parentFontDescription().variantNumericFigure());
builderState.setFontDescriptionVariantNumericSpacing(builderState.parentFontDescription().variantNumericSpacing());
builderState.setFontDescriptionVariantNumericFraction(builderState.parentFontDescription().variantNumericFraction());
builderState.setFontDescriptionVariantNumericOrdinal(builderState.parentFontDescription().variantNumericOrdinal());
builderState.setFontDescriptionVariantNumericSlashedZero(builderState.parentFontDescription().variantNumericSlashedZero());
}
inline void BuilderCustom::applyInitialFontVariantNumeric(BuilderState& builderState)
{
builderState.setFontDescriptionVariantNumericFigure(FontVariantNumericFigure::Normal);
builderState.setFontDescriptionVariantNumericSpacing(FontVariantNumericSpacing::Normal);
builderState.setFontDescriptionVariantNumericFraction(FontVariantNumericFraction::Normal);
builderState.setFontDescriptionVariantNumericOrdinal(FontVariantNumericOrdinal::Normal);
builderState.setFontDescriptionVariantNumericSlashedZero(FontVariantNumericSlashedZero::Normal);
}
inline void BuilderCustom::applyValueFontVariantNumeric(BuilderState& builderState, CSSValue& value)
{
if (CSSPropertyParserHelpers::isSystemFontShorthand(value.valueID())) {
applyInitialFontVariantNumeric(builderState);
return;
}
auto variantNumeric = extractFontVariantNumeric(value);
builderState.setFontDescriptionVariantNumericFigure(variantNumeric.figure);
builderState.setFontDescriptionVariantNumericSpacing(variantNumeric.spacing);
builderState.setFontDescriptionVariantNumericFraction(variantNumeric.fraction);
builderState.setFontDescriptionVariantNumericOrdinal(variantNumeric.ordinal);
builderState.setFontDescriptionVariantNumericSlashedZero(variantNumeric.slashedZero);
}
inline void BuilderCustom::applyInheritFontVariantEastAsian(BuilderState& builderState)
{
builderState.setFontDescriptionVariantEastAsianVariant(builderState.parentFontDescription().variantEastAsianVariant());
builderState.setFontDescriptionVariantEastAsianWidth(builderState.parentFontDescription().variantEastAsianWidth());
builderState.setFontDescriptionVariantEastAsianRuby(builderState.parentFontDescription().variantEastAsianRuby());
}
inline void BuilderCustom::applyInitialFontVariantEastAsian(BuilderState& builderState)
{
builderState.setFontDescriptionVariantEastAsianVariant(FontVariantEastAsianVariant::Normal);
builderState.setFontDescriptionVariantEastAsianWidth(FontVariantEastAsianWidth::Normal);
builderState.setFontDescriptionVariantEastAsianRuby(FontVariantEastAsianRuby::Normal);
}
inline void BuilderCustom::applyValueFontVariantEastAsian(BuilderState& builderState, CSSValue& value)
{
if (CSSPropertyParserHelpers::isSystemFontShorthand(value.valueID())) {
applyInitialFontVariantEastAsian(builderState);
return;
}
auto variantEastAsian = extractFontVariantEastAsian(value);
builderState.setFontDescriptionVariantEastAsianVariant(variantEastAsian.variant);
builderState.setFontDescriptionVariantEastAsianWidth(variantEastAsian.width);
builderState.setFontDescriptionVariantEastAsianRuby(variantEastAsian.ruby);
}
inline void BuilderCustom::applyInheritFontVariantAlternates(BuilderState& builderState)
{
builderState.setFontDescriptionVariantAlternates(builderState.parentFontDescription().variantAlternates());
}
inline void BuilderCustom::applyInitialFontVariantAlternates(BuilderState& builderState)
{
builderState.setFontDescriptionVariantAlternates(FontVariantAlternates::Normal());
}
inline void BuilderCustom::applyValueFontVariantAlternates(BuilderState& builderState, CSSValue& value)
{
if (CSSPropertyParserHelpers::isSystemFontShorthand(value.valueID())) {
applyInitialFontVariantAlternates(builderState);
return;
}
auto fontDescription = builderState.fontDescription();
fontDescription.setVariantAlternates(extractFontVariantAlternates(value, builderState));
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInitialFontSize(BuilderState& builderState)
{
auto fontDescription = builderState.fontDescription();
float size = Style::fontSizeForKeyword(CSSValueMedium, fontDescription.useFixedDefaultSize(), builderState.document());
if (size < 0)
return;
fontDescription.setKeywordSizeFromIdentifier(CSSValueMedium);
builderState.setFontSize(fontDescription, size);
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInheritFontSize(BuilderState& builderState)
{
const auto& parentFontDescription = builderState.parentStyle().fontDescription();
float size = parentFontDescription.specifiedSize();
if (size < 0)
return;
builderState.setFontDescriptionKeywordSize(parentFontDescription.keywordSize());
builderState.setFontDescriptionFontSize(size);
}
// When the CSS keyword "larger" is used, this function will attempt to match within the keyword
// table, and failing that, will simply multiply by 1.2.
inline float BuilderCustom::largerFontSize(float size)
{
// FIXME: Figure out where we fall in the size ranges (xx-small to xxx-large) and scale up to
// the next size level.
return size * 1.2f;
}
// Like the previous function, but for the keyword "smaller".
inline float BuilderCustom::smallerFontSize(float size)
{
// FIXME: Figure out where we fall in the size ranges (xx-small to xxx-large) and scale down to
// the next size level.
return size / 1.2f;
}
inline float BuilderCustom::determineRubyTextSizeMultiplier(BuilderState& builderState)
{
if (!builderState.style().isInterCharacterRubyPosition())
return 0.5f;
auto rubyPosition = builderState.style().rubyPosition();
if (rubyPosition == RubyPosition::InterCharacter) {
// If the writing mode of the enclosing ruby container is vertical, 'inter-character' value has the same effect as over.
return !builderState.parentStyle().writingMode().isVerticalTypographic() ? 0.3f : 0.5f;
}
// Legacy inter-character behavior.
// FIXME: This hack is to ensure tone marks are the same size as
// the bopomofo. This code will go away if we make a special renderer
// for the tone marks eventually.
if (auto* element = builderState.element()) {
for (auto& ancestor : ancestorsOfType<HTMLElement>(*element)) {
if (ancestor.hasTagName(HTMLNames::rtTag))
return 1.0f;
}
}
return 0.25f;
}
static inline void applyFontStyle(BuilderState& state, std::optional<FontSelectionValue> slope, FontStyleAxis axis)
{
auto& description = state.fontDescription();
if (description.italic() == slope && description.fontStyleAxis() == axis)
return;
auto copy = description;
copy.setItalic(slope);
copy.setFontStyleAxis(axis);
state.setFontDescription(WTFMove(copy));
}
inline void BuilderCustom::applyInitialFontStyle(BuilderState& state)
{
applyFontStyle(state, FontCascadeDescription::initialItalic(), FontCascadeDescription::initialFontStyleAxis());
}
inline void BuilderCustom::applyInheritFontStyle(BuilderState& state)
{
applyFontStyle(state, state.parentFontDescription().italic(), state.parentFontDescription().fontStyleAxis());
}
inline void BuilderCustom::applyValueFontStyle(BuilderState& state, CSSValue& value)
{
auto* primitiveValue = dynamicDowncast<CSSPrimitiveValue>(value);
auto keyword = primitiveValue ? primitiveValue->valueID() : CSSValueOblique;
std::optional<FontSelectionValue> slope;
if (!CSSPropertyParserHelpers::isSystemFontShorthand(keyword))
slope = BuilderConverter::convertFontStyleFromValue(state, value);
applyFontStyle(state, slope, keyword == CSSValueItalic ? FontStyleAxis::ital : FontStyleAxis::slnt);
}
inline void BuilderCustom::applyValueFontSize(BuilderState& builderState, CSSValue& value)
{
auto& fontDescription = builderState.fontDescription();
builderState.setFontDescriptionKeywordSizeFromIdentifier(CSSValueInvalid);
float parentSize = builderState.parentStyle().fontDescription().specifiedSize();
bool parentIsAbsoluteSize = builderState.parentStyle().fontDescription().isAbsoluteSize();
auto primitiveValue = requiredDowncast<CSSPrimitiveValue>(builderState, value);
if (!primitiveValue)
return;
float size = 0;
if (CSSValueID ident = primitiveValue->valueID()) {
builderState.setFontDescriptionIsAbsoluteSize((parentIsAbsoluteSize && (ident == CSSValueLarger || ident == CSSValueSmaller || ident == CSSValueWebkitRubyText)) || CSSPropertyParserHelpers::isSystemFontShorthand(ident));
if (CSSPropertyParserHelpers::isSystemFontShorthand(ident))
size = SystemFontDatabase::singleton().systemFontShorthandSize(CSSPropertyParserHelpers::lowerFontShorthand(ident));
switch (ident) {
case CSSValueXxSmall:
case CSSValueXSmall:
case CSSValueSmall:
case CSSValueMedium:
case CSSValueLarge:
case CSSValueXLarge:
case CSSValueXxLarge:
case CSSValueXxxLarge:
size = Style::fontSizeForKeyword(ident, fontDescription.useFixedDefaultSize(), builderState.document());
builderState.setFontDescriptionKeywordSizeFromIdentifier(ident);
break;
case CSSValueLarger:
size = largerFontSize(parentSize);
break;
case CSSValueSmaller:
size = smallerFontSize(parentSize);
break;
case CSSValueWebkitRubyText:
size = determineRubyTextSizeMultiplier(builderState) * parentSize;
break;
default:
break;
}
} else {
builderState.setFontDescriptionIsAbsoluteSize(parentIsAbsoluteSize || !primitiveValue->isParentFontRelativeLength());
auto conversionData = builderState.cssToLengthConversionData().copyForFontSize();
if (primitiveValue->isLength())
size = primitiveValue->resolveAsLength<float>(conversionData);
else if (primitiveValue->isPercentage())
size = (primitiveValue->resolveAsPercentage<float>(conversionData) * parentSize) / 100.0f;
else if (primitiveValue->isCalculatedPercentageWithLength())
size = primitiveValue->cssCalcValue()->createCalculationValue(conversionData, CSSCalcSymbolTable { })->evaluate(parentSize);
else
return;
}
if (size < 0)
return;
builderState.setFontDescriptionFontSize(std::min(maximumAllowedFontSize, size));
}
inline void BuilderCustom::applyValueFontSizeAdjust(BuilderState& builderState, CSSValue& value)
{
builderState.setFontDescriptionFontSizeAdjust(BuilderConverter::convertFontSizeAdjust(builderState, value));
}
inline void BuilderCustom::applyValueStrokeWidth(BuilderState& builderState, CSSValue& value)
{
builderState.style().setStrokeWidth(toStyleFromCSSValue<StrokeWidth>(builderState, value));
builderState.style().setHasExplicitlySetStrokeWidth(true);
}
inline void BuilderCustom::applyValueStrokeColor(BuilderState& builderState, CSSValue& value)
{
if (builderState.applyPropertyToRegularStyle())
builderState.style().setStrokeColor(toStyleFromCSSValue<Color>(builderState, value, ForVisitedLink::No));
if (builderState.applyPropertyToVisitedLinkStyle())
builderState.style().setVisitedLinkStrokeColor(toStyleFromCSSValue<Color>(builderState, value, ForVisitedLink::Yes));
builderState.style().setHasExplicitlySetStrokeColor(true);
}
// For the color property, "currentcolor" is actually the inherited computed color.
inline void BuilderCustom::applyValueColor(BuilderState& builderState, CSSValue& value)
{
if (builderState.applyPropertyToRegularStyle()) {
auto color = toStyleFromCSSValue<Color>(builderState, value, ForVisitedLink::No);
builderState.style().setColor(color.resolveColor(builderState.parentStyle().color()));
}
if (builderState.applyPropertyToVisitedLinkStyle()) {
auto color = toStyleFromCSSValue<Color>(builderState, value, ForVisitedLink::Yes);
builderState.style().setVisitedLinkColor(color.resolveColor(builderState.parentStyle().visitedLinkColor()));
}
builderState.style().setDisallowsFastPathInheritance();
builderState.style().setHasExplicitlySetColor(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyInitialColor(BuilderState& builderState)
{
if (builderState.applyPropertyToRegularStyle())
builderState.style().setColor(RenderStyle::initialColor());
if (builderState.applyPropertyToVisitedLinkStyle())
builderState.style().setVisitedLinkColor(RenderStyle::initialColor());
builderState.style().setDisallowsFastPathInheritance();
builderState.style().setHasExplicitlySetColor(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyInheritColor(BuilderState& builderState)
{
if (builderState.applyPropertyToRegularStyle())
builderState.style().setColor(forwardInheritedValue(builderState.parentStyle().color()));
if (builderState.applyPropertyToVisitedLinkStyle())
builderState.style().setVisitedLinkColor(forwardInheritedValue(builderState.parentStyle().color()));
builderState.style().setDisallowsFastPathInheritance();
builderState.style().setHasExplicitlySetColor(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyInitialPaddingBottom(BuilderState& builderState)
{
builderState.style().setPaddingBottom(RenderStyle::initialPadding());
builderState.style().setHasExplicitlySetPaddingBottom(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyInheritPaddingBottom(BuilderState& builderState)
{
builderState.style().setPaddingBottom(forwardInheritedValue(builderState.parentStyle().paddingBottom()));
builderState.style().setHasExplicitlySetPaddingBottom(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyValuePaddingBottom(BuilderState& builderState, CSSValue& value)
{
builderState.style().setPaddingBottom(BuilderConverter::convertStyleType<PaddingEdge>(builderState, value));
builderState.style().setHasExplicitlySetPaddingBottom(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyInitialPaddingLeft(BuilderState& builderState)
{
builderState.style().setPaddingLeft(RenderStyle::initialPadding());
builderState.style().setHasExplicitlySetPaddingLeft(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyInheritPaddingLeft(BuilderState& builderState)
{
builderState.style().setPaddingLeft(forwardInheritedValue(builderState.parentStyle().paddingLeft()));
builderState.style().setHasExplicitlySetPaddingLeft(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyValuePaddingLeft(BuilderState& builderState, CSSValue& value)
{
builderState.style().setPaddingLeft(BuilderConverter::convertStyleType<PaddingEdge>(builderState, value));
builderState.style().setHasExplicitlySetPaddingLeft(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyInitialPaddingRight(BuilderState& builderState)
{
builderState.style().setPaddingRight(RenderStyle::initialPadding());
builderState.style().setHasExplicitlySetPaddingRight(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyInheritPaddingRight(BuilderState& builderState)
{
builderState.style().setPaddingRight(forwardInheritedValue(builderState.parentStyle().paddingRight()));
builderState.style().setHasExplicitlySetPaddingRight(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyValuePaddingRight(BuilderState& builderState, CSSValue& value)
{
builderState.style().setPaddingRight(BuilderConverter::convertStyleType<PaddingEdge>(builderState, value));
builderState.style().setHasExplicitlySetPaddingRight(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyInitialPaddingTop(BuilderState& builderState)
{
builderState.style().setPaddingTop(RenderStyle::initialPadding());
builderState.style().setHasExplicitlySetPaddingTop(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyInheritPaddingTop(BuilderState& builderState)
{
builderState.style().setPaddingTop(forwardInheritedValue(builderState.parentStyle().paddingTop()));
builderState.style().setHasExplicitlySetPaddingTop(builderState.isAuthorOrigin());
}
inline void BuilderCustom::applyValuePaddingTop(BuilderState& builderState, CSSValue& value)
{
builderState.style().setPaddingTop(BuilderConverter::convertStyleType<PaddingEdge>(builderState, value));
builderState.style().setHasExplicitlySetPaddingTop(builderState.isAuthorOrigin());
}
} // namespace Style
} // namespace WebCore