blob: 9ee3a42778732c5ea3db5cb5b64c03a224f9f1c9 [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
* Copyright (C) 2014-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "CSSCursorImageValue.h"
#include "CSSFontFamily.h"
#include "CSSFontValue.h"
#include "CSSGradientValue.h"
#include "CSSGridTemplateAreasValue.h"
#include "CSSPropertyParserHelpers.h"
#include "CSSRegisteredCustomProperty.h"
#include "CSSShadowValue.h"
#include "Counter.h"
#include "CounterContent.h"
#include "CursorList.h"
#include "ElementAncestorIterator.h"
#include "FontVariantBuilder.h"
#include "Frame.h"
#include "HTMLElement.h"
#include "Rect.h"
#include "SVGElement.h"
#include "SVGRenderStyle.h"
#include "StyleBuilderConverter.h"
#include "StyleCachedImage.h"
#include "StyleCursorImage.h"
#include "StyleFontSizeFunctions.h"
#include "StyleGeneratedImage.h"
#include "StyleImageSet.h"
#include "StyleResolver.h"
#include "WillChangeData.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); }
inline Length forwardInheritedValue(const 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 GapLength forwardInheritedValue(const GapLength& 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.
DECLARE_PROPERTY_CUSTOM_HANDLERS(AspectRatio);
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(Clip);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Contain);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Content);
DECLARE_PROPERTY_CUSTOM_HANDLERS(CounterIncrement);
DECLARE_PROPERTY_CUSTOM_HANDLERS(CounterReset);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Cursor);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Fill);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontFamily);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontSize);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontStyle);
#if ENABLE(CSS_IMAGE_RESOLUTION)
DECLARE_PROPERTY_CUSTOM_HANDLERS(ImageResolution);
#endif
DECLARE_PROPERTY_CUSTOM_HANDLERS(LetterSpacing);
#if ENABLE(TEXT_AUTOSIZING)
DECLARE_PROPERTY_CUSTOM_HANDLERS(LineHeight);
#endif
DECLARE_PROPERTY_CUSTOM_HANDLERS(ListStyleType);
DECLARE_PROPERTY_CUSTOM_HANDLERS(OutlineStyle);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Size);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Stroke);
DECLARE_PROPERTY_CUSTOM_HANDLERS(TextIndent);
DECLARE_PROPERTY_CUSTOM_HANDLERS(TextShadow);
DECLARE_PROPERTY_CUSTOM_HANDLERS(WebkitBoxShadow);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontVariantLigatures);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontVariantNumeric);
DECLARE_PROPERTY_CUSTOM_HANDLERS(FontVariantEastAsian);
DECLARE_PROPERTY_CUSTOM_HANDLERS(GridTemplateAreas);
DECLARE_PROPERTY_CUSTOM_HANDLERS(GridTemplateColumns);
DECLARE_PROPERTY_CUSTOM_HANDLERS(GridTemplateRows);
DECLARE_PROPERTY_CUSTOM_HANDLERS(WebkitMaskBoxImageOutset);
DECLARE_PROPERTY_CUSTOM_HANDLERS(WebkitMaskBoxImageRepeat);
DECLARE_PROPERTY_CUSTOM_HANDLERS(WebkitMaskBoxImageSlice);
DECLARE_PROPERTY_CUSTOM_HANDLERS(WebkitMaskBoxImageWidth);
DECLARE_PROPERTY_CUSTOM_HANDLERS(WebkitTextEmphasisStyle);
DECLARE_PROPERTY_CUSTOM_HANDLERS(Zoom);
// Custom handling of initial + inherit value setting only.
static void applyInitialWebkitMaskImage(BuilderState&) { }
static void applyInheritWebkitMaskImage(BuilderState&) { }
static void applyInitialFontFeatureSettings(BuilderState&) { }
static void applyInheritFontFeatureSettings(BuilderState&) { }
static void applyInitialFontVariationSettings(BuilderState&);
static void applyInheritFontVariationSettings(BuilderState&);
// Custom handling of inherit + value setting only.
static void applyInheritDisplay(BuilderState&);
static void applyValueDisplay(BuilderState&, CSSValue&);
// FIXME: <https://webkit.org/b/212506> Teach makeprop.pl to generate setters for hasExplicitlySet* flags
static void applyInheritBorderBottomLeftRadius(BuilderState&);
static void applyValueBorderBottomLeftRadius(BuilderState&, CSSValue&);
static void applyInheritBorderBottomRightRadius(BuilderState&);
static void applyValueBorderBottomRightRadius(BuilderState&, CSSValue&);
static void applyInheritBorderTopLeftRadius(BuilderState&);
static void applyValueBorderTopLeftRadius(BuilderState&, CSSValue&);
static void applyInheritBorderTopRightRadius(BuilderState&);
static void applyValueBorderTopRightRadius(BuilderState&, CSSValue&);
// Custom handling of value setting only.
static void applyValueBaselineShift(BuilderState&, CSSValue&);
static void applyValueDirection(BuilderState&, CSSValue&);
static void applyInheritVerticalAlign(BuilderState&);
static void applyValueVerticalAlign(BuilderState&, CSSValue&);
static void applyInitialTextAlign(BuilderState&);
static void applyValueTextAlign(BuilderState&, CSSValue&);
static void applyValueWebkitLocale(BuilderState&, CSSValue&);
static void applyValueWebkitTextOrientation(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 applyValueAlt(BuilderState&, CSSValue&);
static void applyValueWillChange(BuilderState&, CSSValue&);
#if ENABLE(DARK_MODE_CSS)
static void applyValueColorScheme(BuilderState&, CSSValue&);
#endif
static void applyValueStrokeWidth(BuilderState&, CSSValue&);
static void applyValueStrokeColor(BuilderState&, CSSValue&);
static void applyInitialCustomProperty(BuilderState&, const CSSRegisteredCustomProperty*, const AtomString& name);
static void applyInheritCustomProperty(BuilderState&, const CSSRegisteredCustomProperty*, const AtomString& name);
static void applyValueCustomProperty(BuilderState&, const CSSRegisteredCustomProperty*, CSSCustomPropertyValue&);
private:
static void resetEffectiveZoom(BuilderState&);
static Length mmLength(double mm);
static Length inchLength(double inch);
static bool getPageSizeFromName(CSSPrimitiveValue* pageSizeName, CSSPrimitiveValue* pageOrientation, Length& width, Length& height);
template <CSSPropertyID id>
static void applyTextOrBoxShadowValue(BuilderState&, CSSValue&);
static bool isValidDisplayValue(BuilderState&, DisplayType);
enum CounterBehavior {Increment = 0, Reset};
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(downcast<CSSPrimitiveValue>(value));
builderState.style().setHasExplicitlySetDirection(true);
}
inline void BuilderCustom::applyInitialTextAlign(BuilderState& builderState)
{
builderState.style().setTextAlign(RenderStyle::initialTextAlign());
builderState.style().setHasExplicitlySetTextAlign(true);
}
inline void BuilderCustom::applyValueTextAlign(BuilderState& builderState, CSSValue& value)
{
builderState.style().setTextAlign(BuilderConverter::convertTextAlign(builderState, value));
builderState.style().setHasExplicitlySetTextAlign(true);
}
inline void BuilderCustom::resetEffectiveZoom(BuilderState& builderState)
{
// Reset the zoom in effect. This allows the setZoom method to accurately compute a new zoom in effect.
builderState.setEffectiveZoom(builderState.parentStyle().effectiveZoom());
}
inline void BuilderCustom::applyInitialZoom(BuilderState& builderState)
{
resetEffectiveZoom(builderState);
builderState.setZoom(RenderStyle::initialZoom());
}
inline void BuilderCustom::applyInheritZoom(BuilderState& builderState)
{
resetEffectiveZoom(builderState);
builderState.setZoom(builderState.parentStyle().zoom());
}
inline void BuilderCustom::applyValueZoom(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (primitiveValue.valueID() == CSSValueNormal) {
resetEffectiveZoom(builderState);
builderState.setZoom(RenderStyle::initialZoom());
} else if (primitiveValue.valueID() == CSSValueReset) {
builderState.setEffectiveZoom(RenderStyle::initialZoom());
builderState.setZoom(RenderStyle::initialZoom());
} else if (primitiveValue.valueID() == CSSValueDocument) {
float docZoom = builderState.rootElementStyle() ? builderState.rootElementStyle()->zoom() : RenderStyle::initialZoom();
builderState.setEffectiveZoom(docZoom);
builderState.setZoom(docZoom);
} else if (primitiveValue.isPercentage()) {
resetEffectiveZoom(builderState);
if (float percent = primitiveValue.floatValue())
builderState.setZoom(percent / 100.0f);
} else if (primitiveValue.isNumber()) {
resetEffectiveZoom(builderState);
if (float number = primitiveValue.floatValue())
builderState.setZoom(number);
}
}
inline Length BuilderCustom::mmLength(double mm)
{
Ref<CSSPrimitiveValue> value(CSSPrimitiveValue::create(mm, CSSUnitType::CSS_MM));
return value.get().computeLength<Length>(CSSToLengthConversionData());
}
inline Length BuilderCustom::inchLength(double inch)
{
Ref<CSSPrimitiveValue> value(CSSPrimitiveValue::create(inch, CSSUnitType::CSS_IN));
return value.get().computeLength<Length>(CSSToLengthConversionData());
}
bool BuilderCustom::getPageSizeFromName(CSSPrimitiveValue* pageSizeName, CSSPrimitiveValue* pageOrientation, Length& width, Length& height)
{
static NeverDestroyed<Length> a5Width(mmLength(148));
static NeverDestroyed<Length> a5Height(mmLength(210));
static NeverDestroyed<Length> a4Width(mmLength(210));
static NeverDestroyed<Length> a4Height(mmLength(297));
static NeverDestroyed<Length> a3Width(mmLength(297));
static NeverDestroyed<Length> a3Height(mmLength(420));
static NeverDestroyed<Length> b5Width(mmLength(176));
static NeverDestroyed<Length> b5Height(mmLength(250));
static NeverDestroyed<Length> b4Width(mmLength(250));
static NeverDestroyed<Length> b4Height(mmLength(353));
static NeverDestroyed<Length> letterWidth(inchLength(8.5));
static NeverDestroyed<Length> letterHeight(inchLength(11));
static NeverDestroyed<Length> legalWidth(inchLength(8.5));
static NeverDestroyed<Length> legalHeight(inchLength(14));
static NeverDestroyed<Length> ledgerWidth(inchLength(11));
static NeverDestroyed<Length> ledgerHeight(inchLength(17));
if (!pageSizeName)
return false;
switch (pageSizeName->valueID()) {
case CSSValueA5:
width = a5Width;
height = a5Height;
break;
case CSSValueA4:
width = a4Width;
height = a4Height;
break;
case CSSValueA3:
width = a3Width;
height = a3Height;
break;
case CSSValueB5:
width = b5Width;
height = b5Height;
break;
case CSSValueB4:
width = b4Width;
height = b4Height;
break;
case CSSValueLetter:
width = letterWidth;
height = letterHeight;
break;
case CSSValueLegal:
width = legalWidth;
height = legalHeight;
break;
case CSSValueLedger:
width = ledgerWidth;
height = ledgerHeight;
break;
default:
return false;
}
if (pageOrientation) {
switch (pageOrientation->valueID()) {
case CSSValueLandscape:
std::swap(width, height);
break;
case CSSValuePortrait:
// Nothing to do.
break;
default:
return false;
}
}
return true;
}
inline void BuilderCustom::applyInheritVerticalAlign(BuilderState& builderState)
{
builderState.style().setVerticalAlignLength(forwardInheritedValue(builderState.parentStyle().verticalAlignLength()));
builderState.style().setVerticalAlign(forwardInheritedValue(builderState.parentStyle().verticalAlign()));
}
inline void BuilderCustom::applyValueVerticalAlign(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (primitiveValue.valueID())
builderState.style().setVerticalAlign(primitiveValue);
else
builderState.style().setVerticalAlignLength(primitiveValue.convertToLength<FixedIntegerConversion | PercentConversion | CalculatedConversion>(builderState.cssToLengthConversionData()));
}
#if ENABLE(CSS_IMAGE_RESOLUTION)
inline void BuilderCustom::applyInheritImageResolution(BuilderState& builderState)
{
builderState.style().setImageResolutionSource(builderState.parentStyle().imageResolutionSource());
builderState.style().setImageResolutionSnap(builderState.parentStyle().imageResolutionSnap());
builderState.style().setImageResolution(builderState.parentStyle().imageResolution());
}
inline void BuilderCustom::applyInitialImageResolution(BuilderState& builderState)
{
builderState.style().setImageResolutionSource(RenderStyle::initialImageResolutionSource());
builderState.style().setImageResolutionSnap(RenderStyle::initialImageResolutionSnap());
builderState.style().setImageResolution(RenderStyle::initialImageResolution());
}
inline void BuilderCustom::applyValueImageResolution(BuilderState& builderState, CSSValue& value)
{
ImageResolutionSource source = RenderStyle::initialImageResolutionSource();
ImageResolutionSnap snap = RenderStyle::initialImageResolutionSnap();
double resolution = RenderStyle::initialImageResolution();
for (auto& item : downcast<CSSValueList>(value)) {
CSSPrimitiveValue& primitiveValue = downcast<CSSPrimitiveValue>(item.get());
if (primitiveValue.valueID() == CSSValueFromImage)
source = ImageResolutionSource::FromImage;
else if (primitiveValue.valueID() == CSSValueSnap)
snap = ImageResolutionSnap::Pixels;
else
resolution = primitiveValue.doubleValue(CSSUnitType::CSS_DPPX);
}
builderState.style().setImageResolutionSource(source);
builderState.style().setImageResolutionSnap(snap);
builderState.style().setImageResolution(resolution);
}
#endif // ENABLE(CSS_IMAGE_RESOLUTION)
inline void BuilderCustom::applyInheritSize(BuilderState&) { }
inline void BuilderCustom::applyInitialSize(BuilderState&) { }
inline void BuilderCustom::applyValueSize(BuilderState& builderState, CSSValue& value)
{
builderState.style().resetPageSizeType();
if (!is<CSSValueList>(value))
return;
Length width;
Length height;
PageSizeType pageSizeType = PAGE_SIZE_AUTO;
auto& valueList = downcast<CSSValueList>(value);
switch (valueList.length()) {
case 2: {
auto firstValue = valueList.itemWithoutBoundsCheck(0);
auto secondValue = valueList.itemWithoutBoundsCheck(1);
// <length>{2} | <page-size> <orientation>
if (!is<CSSPrimitiveValue>(*firstValue) || !is<CSSPrimitiveValue>(*secondValue))
return;
auto& firstPrimitiveValue = downcast<CSSPrimitiveValue>(*firstValue);
auto& secondPrimitiveValue = downcast<CSSPrimitiveValue>(*secondValue);
if (firstPrimitiveValue.isLength()) {
// <length>{2}
if (!secondPrimitiveValue.isLength())
return;
CSSToLengthConversionData conversionData = builderState.cssToLengthConversionData().copyWithAdjustedZoom(1.0f);
width = firstPrimitiveValue.computeLength<Length>(conversionData);
height = secondPrimitiveValue.computeLength<Length>(conversionData);
} else {
// <page-size> <orientation>
// The value order is guaranteed. See CSSParser::parseSizeParameter.
if (!getPageSizeFromName(&firstPrimitiveValue, &secondPrimitiveValue, width, height))
return;
}
pageSizeType = PAGE_SIZE_RESOLVED;
break;
}
case 1: {
auto value = valueList.itemWithoutBoundsCheck(0);
// <length> | auto | <page-size> | [ portrait | landscape]
if (!is<CSSPrimitiveValue>(*value))
return;
auto& primitiveValue = downcast<CSSPrimitiveValue>(*value);
if (primitiveValue.isLength()) {
// <length>
pageSizeType = PAGE_SIZE_RESOLVED;
width = height = primitiveValue.computeLength<Length>(builderState.cssToLengthConversionData().copyWithAdjustedZoom(1.0f));
} else {
switch (primitiveValue.valueID()) {
case 0:
return;
case CSSValueAuto:
pageSizeType = PAGE_SIZE_AUTO;
break;
case CSSValuePortrait:
pageSizeType = PAGE_SIZE_AUTO_PORTRAIT;
break;
case CSSValueLandscape:
pageSizeType = PAGE_SIZE_AUTO_LANDSCAPE;
break;
default:
// <page-size>
pageSizeType = PAGE_SIZE_RESOLVED;
if (!getPageSizeFromName(&primitiveValue, nullptr, width, height))
return;
}
}
break;
}
default:
return;
}
builderState.style().setPageSizeType(pageSizeType);
builderState.style().setPageSize({ WTFMove(width), WTFMove(height) });
}
inline void BuilderCustom::applyInheritTextIndent(BuilderState& builderState)
{
builderState.style().setTextIndent(Length { builderState.parentStyle().textIndent() });
builderState.style().setTextIndentLine(builderState.parentStyle().textIndentLine());
builderState.style().setTextIndentType(builderState.parentStyle().textIndentType());
}
inline void BuilderCustom::applyInitialTextIndent(BuilderState& builderState)
{
builderState.style().setTextIndent(RenderStyle::initialTextIndent());
builderState.style().setTextIndentLine(RenderStyle::initialTextIndentLine());
builderState.style().setTextIndentType(RenderStyle::initialTextIndentType());
}
inline void BuilderCustom::applyValueTextIndent(BuilderState& builderState, CSSValue& value)
{
Length lengthOrPercentageValue;
TextIndentLine textIndentLineValue = RenderStyle::initialTextIndentLine();
TextIndentType textIndentTypeValue = RenderStyle::initialTextIndentType();
for (auto& item : downcast<CSSValueList>(value)) {
auto& primitiveValue = downcast<CSSPrimitiveValue>(item.get());
if (!primitiveValue.valueID())
lengthOrPercentageValue = primitiveValue.convertToLength<FixedIntegerConversion | PercentConversion | CalculatedConversion>(builderState.cssToLengthConversionData());
else if (primitiveValue.valueID() == CSSValueEachLine)
textIndentLineValue = TextIndentLine::EachLine;
else if (primitiveValue.valueID() == CSSValueHanging)
textIndentTypeValue = TextIndentType::Hanging;
}
if (lengthOrPercentageValue.isUndefined())
return;
builderState.style().setTextIndent(WTFMove(lengthOrPercentageValue));
builderState.style().setTextIndentLine(textIndentLineValue);
builderState.style().setTextIndentType(textIndentTypeValue);
}
enum BorderImageType { BorderImage, WebkitMaskBoxImage };
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(Length(100, LengthType::Percent), Length(100, LengthType::Percent), Length(100, LengthType::Percent), Length(100, LengthType::Percent)) : LengthBox());
image.setFill(type != BorderImage);
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(Length(1, LengthType::Relative), Length(1, LengthType::Relative), Length(1, LengthType::Relative), 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());
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:
image.setBorderSlices(builderState.styleMap().mapNinePieceImageQuad(value));
break;
}
setValue(builderState.style(), image);
}
private:
static const NinePieceImage& getValue(const RenderStyle& style)
{
return type == BorderImage ? style.borderImage() : style.maskBoxImage();
}
static void setValue(RenderStyle& style, const NinePieceImage& value)
{
return type == BorderImage ? style.setBorderImage(value) : style.setMaskBoxImage(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(WebkitMaskBoxImage, Outset)
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(WebkitMaskBoxImage, Repeat)
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(WebkitMaskBoxImage, Slice)
DEFINE_BORDER_IMAGE_MODIFIER_HANDLER(WebkitMaskBoxImage, Width)
static inline void applyLetterSpacing(BuilderState& builderState, float letterSpacing)
{
// Setting the letter-spacing from a positive value to another positive value shouldn't require fonts to get updated.
bool shouldDisableLigaturesForSpacing = letterSpacing;
if (shouldDisableLigaturesForSpacing != builderState.fontDescription().shouldDisableLigaturesForSpacing()) {
auto fontDescription = builderState.fontDescription();
fontDescription.setShouldDisableLigaturesForSpacing(letterSpacing);
builderState.setFontDescription(WTFMove(fontDescription));
}
builderState.style().setLetterSpacingWithoutUpdatingFontDescription(letterSpacing);
}
inline void BuilderCustom::applyInheritLetterSpacing(BuilderState& builderState)
{
applyLetterSpacing(builderState, builderState.parentStyle().letterSpacing());
}
inline void BuilderCustom::applyInitialLetterSpacing(BuilderState& builderState)
{
applyLetterSpacing(builderState, 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 (is<CSSPrimitiveValue>(value)) {
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (primitiveValue.isFontRelativeLength() || primitiveValue.isCalculated())
builderState.updateFont();
}
}
inline void BuilderCustom::applyValueLetterSpacing(BuilderState& builderState, CSSValue& value)
{
maybeUpdateFontForLetterSpacing(builderState, value);
applyLetterSpacing(builderState, BuilderConverter::convertSpacing(builderState, value));
}
#if ENABLE(TEXT_AUTOSIZING)
inline void BuilderCustom::applyInheritLineHeight(BuilderState& builderState)
{
builderState.style().setLineHeight(Length { builderState.parentStyle().lineHeight() });
builderState.style().setSpecifiedLineHeight(Length { 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.effectiveZoom();
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)
{
std::optional<Length> lineHeight = BuilderConverter::convertLineHeight(builderState, value, 1);
if (!lineHeight)
return;
Length computedLineHeight;
if (lineHeight.value().isNegative())
computedLineHeight = lineHeight.value();
else {
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
auto multiplier = computeLineHeightMultiplierDueToFontSize(builderState.document(), builderState.style(), primitiveValue);
if (multiplier == 1)
computedLineHeight = lineHeight.value();
else
computedLineHeight = BuilderConverter::convertLineHeight(builderState, value, multiplier).value();
}
builderState.style().setLineHeight(WTFMove(computedLineHeight));
builderState.style().setSpecifiedLineHeight(WTFMove(lineHeight.value()));
}
#endif
inline void BuilderCustom::applyInheritListStyleType(BuilderState& builderState)
{
builderState.style().setListStyleType(builderState.parentStyle().listStyleType());
builderState.style().setListStyleStringValue(builderState.parentStyle().listStyleStringValue());
}
inline void BuilderCustom::applyInitialListStyleType(BuilderState& builderState)
{
builderState.style().setListStyleType(RenderStyle::initialListStyleType());
builderState.style().setListStyleStringValue(RenderStyle::initialListStyleStringValue());
}
inline void BuilderCustom::applyValueListStyleType(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (primitiveValue.isValueID()) {
builderState.style().setListStyleType(primitiveValue);
builderState.style().setListStyleStringValue(RenderStyle::initialListStyleStringValue());
return;
}
builderState.style().setListStyleType(ListStyleType::String);
builderState.style().setListStyleStringValue(primitiveValue.stringValue());
}
inline void BuilderCustom::applyInheritOutlineStyle(BuilderState& builderState)
{
builderState.style().setOutlineStyleIsAuto(builderState.parentStyle().outlineStyleIsAuto());
builderState.style().setOutlineStyle(builderState.parentStyle().outlineStyle());
}
inline void BuilderCustom::applyInitialOutlineStyle(BuilderState& builderState)
{
builderState.style().setOutlineStyleIsAuto(RenderStyle::initialOutlineStyleIsAuto());
builderState.style().setOutlineStyle(RenderStyle::initialBorderStyle());
}
inline void BuilderCustom::applyValueOutlineStyle(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
builderState.style().setOutlineStyleIsAuto(primitiveValue);
builderState.style().setOutlineStyle(primitiveValue);
}
inline void BuilderCustom::applyInitialCaretColor(BuilderState& builderState)
{
if (builderState.applyPropertyToRegularStyle())
builderState.style().setHasAutoCaretColor();
if (builderState.applyPropertyToVisitedLinkStyle())
builderState.style().setHasVisitedLinkAutoCaretColor();
}
inline void BuilderCustom::applyInheritCaretColor(BuilderState& builderState)
{
Color color = builderState.parentStyle().caretColor();
if (builderState.applyPropertyToRegularStyle()) {
if (builderState.parentStyle().hasAutoCaretColor())
builderState.style().setHasAutoCaretColor();
else
builderState.style().setCaretColor(color);
}
if (builderState.applyPropertyToVisitedLinkStyle()) {
if (builderState.parentStyle().hasVisitedLinkAutoCaretColor())
builderState.style().setHasVisitedLinkAutoCaretColor();
else
builderState.style().setVisitedLinkCaretColor(color);
}
}
inline void BuilderCustom::applyValueCaretColor(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (builderState.applyPropertyToRegularStyle()) {
if (primitiveValue.valueID() == CSSValueAuto)
builderState.style().setHasAutoCaretColor();
else
builderState.style().setCaretColor(builderState.colorFromPrimitiveValue(primitiveValue, ForVisitedLink::No));
}
if (builderState.applyPropertyToVisitedLinkStyle()) {
if (primitiveValue.valueID() == CSSValueAuto)
builderState.style().setHasVisitedLinkAutoCaretColor();
else
builderState.style().setVisitedLinkCaretColor(builderState.colorFromPrimitiveValue(primitiveValue, ForVisitedLink::Yes));
}
}
inline void BuilderCustom::applyInitialClip(BuilderState& builderState)
{
builderState.style().setClip(Length(), Length(), Length(), Length());
builderState.style().setHasClip(false);
}
inline void BuilderCustom::applyInheritClip(BuilderState& builderState)
{
auto& parentStyle = builderState.parentStyle();
if (!parentStyle.hasClip())
return applyInitialClip(builderState);
builderState.style().setClip(Length { parentStyle.clipTop() }, Length { parentStyle.clipRight() },
Length { parentStyle.clipBottom() }, Length { parentStyle.clipLeft() });
builderState.style().setHasClip(true);
}
inline void BuilderCustom::applyValueClip(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (auto* rect = primitiveValue.rectValue()) {
auto conversionData = builderState.cssToLengthConversionData();
auto top = rect->top()->convertToLength<FixedIntegerConversion | PercentConversion | AutoConversion>(conversionData);
auto right = rect->right()->convertToLength<FixedIntegerConversion | PercentConversion | AutoConversion>(conversionData);
auto bottom = rect->bottom()->convertToLength<FixedIntegerConversion | PercentConversion | AutoConversion>(conversionData);
auto left = rect->left()->convertToLength<FixedIntegerConversion | PercentConversion | AutoConversion>(conversionData);
builderState.style().setClip(WTFMove(top), WTFMove(right), WTFMove(bottom), WTFMove(left));
builderState.style().setHasClip(true);
} else {
ASSERT(primitiveValue.valueID() == CSSValueAuto);
applyInitialClip(builderState);
}
}
inline void BuilderCustom::applyValueWebkitLocale(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
FontCascadeDescription fontDescription = builderState.fontDescription();
if (primitiveValue.valueID() == CSSValueAuto)
fontDescription.setSpecifiedLocale(nullAtom());
else
fontDescription.setSpecifiedLocale(primitiveValue.stringValue());
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyValueWritingMode(BuilderState& builderState, CSSValue& value)
{
builderState.setWritingMode(downcast<CSSPrimitiveValue>(value));
builderState.style().setHasExplicitlySetWritingMode(true);
}
inline void BuilderCustom::applyValueWebkitTextOrientation(BuilderState& builderState, CSSValue& value)
{
builderState.setTextOrientation(downcast<CSSPrimitiveValue>(value));
}
inline void BuilderCustom::applyValueTextOrientation(BuilderState& builderState, CSSValue& value)
{
builderState.setTextOrientation(downcast<CSSPrimitiveValue>(value));
}
#if ENABLE(TEXT_AUTOSIZING)
inline void BuilderCustom::applyValueWebkitTextSizeAdjust(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (primitiveValue.valueID() == CSSValueAuto)
builderState.style().setTextSizeAdjust(TextSizeAdjustment(AutoTextSizeAdjustment));
else if (primitiveValue.valueID() == CSSValueNone)
builderState.style().setTextSizeAdjust(TextSizeAdjustment(NoTextSizeAdjustment));
else
builderState.style().setTextSizeAdjust(TextSizeAdjustment(primitiveValue.floatValue()));
builderState.setFontDirty();
}
#endif
inline void BuilderCustom::applyValueWebkitTextZoom(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
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::convertColorScheme(builderState, value));
builderState.style().setHasExplicitlySetColorScheme(true);
}
#endif
template<CSSPropertyID property>
inline void BuilderCustom::applyTextOrBoxShadowValue(BuilderState& builderState, CSSValue& value)
{
if (is<CSSPrimitiveValue>(value)) {
ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone);
if (property == CSSPropertyTextShadow)
builderState.style().setTextShadow(nullptr);
else
builderState.style().setBoxShadow(nullptr);
return;
}
bool isFirstEntry = true;
for (auto& item : downcast<CSSValueList>(value)) {
auto& shadowValue = downcast<CSSShadowValue>(item.get());
auto conversionData = builderState.cssToLengthConversionData();
auto x = shadowValue.x->computeLength<Length>(conversionData);
auto y = shadowValue.y->computeLength<Length>(conversionData);
auto blur = shadowValue.blur ? shadowValue.blur->computeLength<Length>(conversionData) : Length(0, LengthType::Fixed);
auto spread = shadowValue.spread ? shadowValue.spread->computeLength<Length>(conversionData) : Length(0, LengthType::Fixed);
ShadowStyle shadowStyle = shadowValue.style && shadowValue.style->valueID() == CSSValueInset ? ShadowStyle::Inset : ShadowStyle::Normal;
Color color;
if (shadowValue.color)
color = builderState.colorFromPrimitiveValueWithResolvedCurrentColor(*shadowValue.color);
else
color = builderState.style().color();
auto shadowData = makeUnique<ShadowData>(LengthPoint(x, y), blur, spread, shadowStyle, property == CSSPropertyWebkitBoxShadow, color.isValid() ? color : Color::transparentBlack);
if (property == CSSPropertyTextShadow)
builderState.style().setTextShadow(WTFMove(shadowData), !isFirstEntry); // add to the list if this is not the first entry
else
builderState.style().setBoxShadow(WTFMove(shadowData), !isFirstEntry); // add to the list if this is not the first entry
isFirstEntry = false;
}
}
inline void BuilderCustom::applyInitialTextShadow(BuilderState& builderState)
{
builderState.style().setTextShadow(nullptr);
}
inline void BuilderCustom::applyInheritTextShadow(BuilderState& builderState)
{
builderState.style().setTextShadow(builderState.parentStyle().textShadow() ? makeUnique<ShadowData>(*builderState.parentStyle().textShadow()) : nullptr);
}
inline void BuilderCustom::applyValueTextShadow(BuilderState& builderState, CSSValue& value)
{
applyTextOrBoxShadowValue<CSSPropertyTextShadow>(builderState, value);
}
inline void BuilderCustom::applyInitialBoxShadow(BuilderState& builderState)
{
builderState.style().setBoxShadow(nullptr);
}
inline void BuilderCustom::applyInheritBoxShadow(BuilderState& builderState)
{
builderState.style().setBoxShadow(builderState.parentStyle().boxShadow() ? makeUnique<ShadowData>(*builderState.parentStyle().boxShadow()) : nullptr);
}
inline void BuilderCustom::applyValueBoxShadow(BuilderState& builderState, CSSValue& value)
{
applyTextOrBoxShadowValue<CSSPropertyBoxShadow>(builderState, value);
}
inline void BuilderCustom::applyInitialWebkitBoxShadow(BuilderState& builderState)
{
applyInitialBoxShadow(builderState);
}
inline void BuilderCustom::applyInheritWebkitBoxShadow(BuilderState& builderState)
{
applyInheritBoxShadow(builderState);
}
inline void BuilderCustom::applyValueWebkitBoxShadow(BuilderState& builderState, CSSValue& value)
{
applyTextOrBoxShadowValue<CSSPropertyWebkitBoxShadow>(builderState, value);
}
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.setFontSize(fontDescription, Style::fontSizeForKeyword(sizeIdentifier, false, builderState.document()));
}
if (!initialDesc.firstFamily().isEmpty())
fontDescription.setFamilies(initialDesc.families());
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInheritFontFamily(BuilderState& builderState)
{
auto fontDescription = builderState.fontDescription();
auto parentFontDescription = builderState.parentStyle().fontDescription();
fontDescription.setFamilies(parentFontDescription.families());
fontDescription.setIsSpecifiedFont(parentFontDescription.isSpecifiedFont());
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyValueFontFamily(BuilderState& builderState, CSSValue& value)
{
auto& valueList = downcast<CSSValueList>(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;
families.reserveInitialCapacity(valueList.length());
for (auto& item : valueList) {
auto& contentValue = downcast<CSSPrimitiveValue>(item.get());
AtomString family;
bool isGenericFamily = false;
if (contentValue.isFontFamily()) {
const CSSFontFamily& fontFamily = contentValue.fontFamily();
family = fontFamily.familyName;
// If the family name was resolved by the CSS parser from a system font ID, then it is generic.
isGenericFamily = fontFamily.fromSystemFontID;
} else {
if (contentValue.valueID() == CSSValueWebkitBody)
family = builderState.document().settings().standardFontFamily();
else {
isGenericFamily = true;
family = CSSPropertyParserHelpers::genericFontFamily(contentValue.valueID());
}
}
if (family.isEmpty())
continue;
if (families.isEmpty())
fontDescription.setIsSpecifiedFont(!isGenericFamily);
families.uncheckedAppend(family);
}
if (families.isEmpty())
return;
fontDescription.setFamilies(families);
if (fontDescription.useFixedDefaultSize() != oldFamilyUsedFixedDefaultSize) {
if (CSSValueID sizeIdentifier = fontDescription.keywordSizeAsIdentifier())
builderState.setFontSize(fontDescription, Style::fontSizeForKeyword(sizeIdentifier, !oldFamilyUsedFixedDefaultSize, builderState.document()));
}
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInheritBorderBottomLeftRadius(BuilderState& builderState)
{
builderState.style().setBorderBottomLeftRadius(forwardInheritedValue(builderState.parentStyle().borderBottomLeftRadius()));
builderState.style().setHasExplicitlySetBorderRadius(builderState.parentStyle().hasExplicitlySetBorderRadius());
}
inline void BuilderCustom::applyValueBorderBottomLeftRadius(BuilderState& builderState, CSSValue& value)
{
builderState.style().setBorderBottomLeftRadius(BuilderConverter::convertRadius(builderState, value));
builderState.style().setHasExplicitlySetBorderRadius(true);
}
inline void BuilderCustom::applyInheritBorderBottomRightRadius(BuilderState& builderState)
{
builderState.style().setBorderBottomRightRadius(forwardInheritedValue(builderState.parentStyle().borderBottomRightRadius()));
builderState.style().setHasExplicitlySetBorderRadius(builderState.parentStyle().hasExplicitlySetBorderRadius());
}
inline void BuilderCustom::applyValueBorderBottomRightRadius(BuilderState& builderState, CSSValue& value)
{
builderState.style().setBorderBottomRightRadius(BuilderConverter::convertRadius(builderState, value));
builderState.style().setHasExplicitlySetBorderRadius(true);
}
inline void BuilderCustom::applyInheritBorderTopLeftRadius(BuilderState& builderState)
{
builderState.style().setBorderTopLeftRadius(forwardInheritedValue(builderState.parentStyle().borderTopLeftRadius()));
builderState.style().setHasExplicitlySetBorderRadius(builderState.parentStyle().hasExplicitlySetBorderRadius());
}
inline void BuilderCustom::applyValueBorderTopLeftRadius(BuilderState& builderState, CSSValue& value)
{
builderState.style().setBorderTopLeftRadius(BuilderConverter::convertRadius(builderState, value));
builderState.style().setHasExplicitlySetBorderRadius(true);
}
inline void BuilderCustom::applyInheritBorderTopRightRadius(BuilderState& builderState)
{
builderState.style().setBorderTopRightRadius(forwardInheritedValue(builderState.parentStyle().borderTopRightRadius()));
builderState.style().setHasExplicitlySetBorderRadius(builderState.parentStyle().hasExplicitlySetBorderRadius());
}
inline void BuilderCustom::applyValueBorderTopRightRadius(BuilderState& builderState, CSSValue& value)
{
builderState.style().setBorderTopRightRadius(BuilderConverter::convertRadius(builderState, value));
builderState.style().setHasExplicitlySetBorderRadius(true);
}
inline bool BuilderCustom::isValidDisplayValue(BuilderState& builderState, DisplayType display)
{
if (is<SVGElement>(builderState.element()) && builderState.style().styleType() == PseudoId::None)
return display == DisplayType::Inline || display == DisplayType::Block || display == DisplayType::None;
return true;
}
inline void BuilderCustom::applyInitialFontVariationSettings(BuilderState& builderState)
{
builderState.style().setFontVariationSettings({ });
}
inline void BuilderCustom::applyInheritFontVariationSettings(BuilderState& builderState)
{
builderState.style().setFontVariationSettings(builderState.parentStyle().fontVariationSettings());
}
inline void BuilderCustom::applyInheritDisplay(BuilderState& builderState)
{
DisplayType display = builderState.parentStyle().display();
if (isValidDisplayValue(builderState, display))
builderState.style().setDisplay(display);
}
inline void BuilderCustom::applyValueDisplay(BuilderState& builderState, CSSValue& value)
{
DisplayType display = downcast<CSSPrimitiveValue>(value);
if (isValidDisplayValue(builderState, display))
builderState.style().setDisplay(display);
}
inline void BuilderCustom::applyValueBaselineShift(BuilderState& builderState, CSSValue& value)
{
SVGRenderStyle& svgStyle = builderState.style().accessSVGStyle();
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (primitiveValue.isValueID()) {
switch (primitiveValue.valueID()) {
case CSSValueBaseline:
svgStyle.setBaselineShift(BaselineShift::Baseline);
break;
case CSSValueSub:
svgStyle.setBaselineShift(BaselineShift::Sub);
break;
case CSSValueSuper:
svgStyle.setBaselineShift(BaselineShift::Super);
break;
default:
break;
}
} else {
svgStyle.setBaselineShift(BaselineShift::Length);
svgStyle.setBaselineShiftValue(SVGLengthValue::fromCSSPrimitiveValue(primitiveValue));
}
}
inline void BuilderCustom::applyInitialWebkitTextEmphasisStyle(BuilderState& builderState)
{
builderState.style().setTextEmphasisFill(RenderStyle::initialTextEmphasisFill());
builderState.style().setTextEmphasisMark(RenderStyle::initialTextEmphasisMark());
builderState.style().setTextEmphasisCustomMark(RenderStyle::initialTextEmphasisCustomMark());
}
inline void BuilderCustom::applyInheritWebkitTextEmphasisStyle(BuilderState& builderState)
{
builderState.style().setTextEmphasisFill(builderState.parentStyle().textEmphasisFill());
builderState.style().setTextEmphasisMark(builderState.parentStyle().textEmphasisMark());
builderState.style().setTextEmphasisCustomMark(builderState.parentStyle().textEmphasisCustomMark());
}
inline void BuilderCustom::applyInitialAspectRatio(BuilderState& builderState)
{
builderState.style().setAspectRatioType(RenderStyle::initialAspectRatioType());
builderState.style().setAspectRatio(RenderStyle::initialAspectRatioWidth(), RenderStyle::initialAspectRatioHeight());
}
inline void BuilderCustom::applyInheritAspectRatio(BuilderState&)
{
}
inline void BuilderCustom::applyValueAspectRatio(BuilderState& builderState, CSSValue& value)
{
if (is<CSSPrimitiveValue>(value)) {
ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueAuto);
return builderState.style().setAspectRatioType(AspectRatioType::Auto);
}
if (!is<CSSValueList>(value))
return;
auto& list = downcast<CSSValueList>(value);
if (list.item(1)->isValueList()) {
builderState.style().setAspectRatioType(AspectRatioType::AutoAndRatio);
auto ratioList = downcast<CSSValueList>(list.item(1));
ASSERT(ratioList->length() == 2);
builderState.style().setAspectRatio(downcast<CSSPrimitiveValue>(ratioList->item(0))->doubleValue(), downcast<CSSPrimitiveValue>(ratioList->item(1))->doubleValue());
return;
}
ASSERT(list.length() == 2);
auto width = downcast<CSSPrimitiveValue>(list.item(0))->doubleValue();
auto height = downcast<CSSPrimitiveValue>(list.item(1))->doubleValue();
if (!width || !height)
builderState.style().setAspectRatioType(AspectRatioType::AutoZero);
else
builderState.style().setAspectRatioType(AspectRatioType::Ratio);
builderState.style().setAspectRatio(width, height);
}
inline void BuilderCustom::applyInitialContain(BuilderState& builderState)
{
builderState.style().setContain(RenderStyle::initialContainment());
}
inline void BuilderCustom::applyInheritContain(BuilderState& builderState)
{
builderState.style().setContain(forwardInheritedValue(builderState.parentStyle().contain()));
}
inline void BuilderCustom::applyValueContain(BuilderState& builderState, CSSValue& value)
{
if (is<CSSPrimitiveValue>(value)) {
if (downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone)
return builderState.style().setContain(RenderStyle::initialContainment());
if (downcast<CSSPrimitiveValue>(value).valueID() == CSSValueStrict)
return builderState.style().setContain(RenderStyle::strictContainment());
return builderState.style().setContain(RenderStyle::contentContainment());
}
if (!is<CSSValueList>(value))
return;
OptionSet<Containment> containment;
auto& list = downcast<CSSValueList>(value);
for (auto& item : list) {
auto& value = downcast<CSSPrimitiveValue>(item.get());
switch (value.valueID()) {
case CSSValueSize:
containment.add(Containment::Size);
break;
case CSSValueLayout:
containment.add(Containment::Layout);
break;
case CSSValuePaint:
containment.add(Containment::Paint);
break;
case CSSValueStyle:
containment.add(Containment::Style);
break;
default:
break;
};
}
return builderState.style().setContain(containment);
}
inline void BuilderCustom::applyValueWebkitTextEmphasisStyle(BuilderState& builderState, CSSValue& value)
{
if (is<CSSValueList>(value)) {
auto& list = downcast<CSSValueList>(value);
ASSERT(list.length() == 2);
for (auto& item : list) {
CSSPrimitiveValue& value = downcast<CSSPrimitiveValue>(item.get());
if (value.valueID() == CSSValueFilled || value.valueID() == CSSValueOpen)
builderState.style().setTextEmphasisFill(value);
else
builderState.style().setTextEmphasisMark(value);
}
builderState.style().setTextEmphasisCustomMark(nullAtom());
return;
}
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (primitiveValue.isString()) {
builderState.style().setTextEmphasisFill(TextEmphasisFill::Filled);
builderState.style().setTextEmphasisMark(TextEmphasisMark::Custom);
builderState.style().setTextEmphasisCustomMark(primitiveValue.stringValue());
return;
}
builderState.style().setTextEmphasisCustomMark(nullAtom());
if (primitiveValue.valueID() == CSSValueFilled || primitiveValue.valueID() == CSSValueOpen) {
builderState.style().setTextEmphasisFill(primitiveValue);
builderState.style().setTextEmphasisMark(TextEmphasisMark::Auto);
} else {
builderState.style().setTextEmphasisFill(TextEmphasisFill::Filled);
builderState.style().setTextEmphasisMark(primitiveValue);
}
}
template <BuilderCustom::CounterBehavior counterBehavior>
inline void BuilderCustom::applyInheritCounter(BuilderState& builderState)
{
auto& map = builderState.style().accessCounterDirectives();
for (auto& keyValue : const_cast<RenderStyle&>(builderState.parentStyle()).accessCounterDirectives()) {
auto& directives = map.add(keyValue.key, CounterDirectives { }).iterator->value;
if (counterBehavior == Reset)
directives.resetValue = keyValue.value.resetValue;
else
directives.incrementValue = keyValue.value.incrementValue;
}
}
template <BuilderCustom::CounterBehavior counterBehavior>
inline void BuilderCustom::applyValueCounter(BuilderState& builderState, CSSValue& value)
{
bool setCounterIncrementToNone = counterBehavior == Increment && is<CSSPrimitiveValue>(value) && downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone;
if (!is<CSSValueList>(value) && !setCounterIncrementToNone)
return;
CounterDirectiveMap& map = builderState.style().accessCounterDirectives();
for (auto& keyValue : map) {
if (counterBehavior == Reset)
keyValue.value.resetValue = std::nullopt;
else
keyValue.value.incrementValue = std::nullopt;
}
if (setCounterIncrementToNone)
return;
for (auto& item : downcast<CSSValueList>(value)) {
Pair* pair = downcast<CSSPrimitiveValue>(item.get()).pairValue();
AtomString identifier = pair->first()->stringValue();
int value = pair->second()->intValue();
auto& directives = map.add(identifier, CounterDirectives { }).iterator->value;
if (counterBehavior == Reset)
directives.resetValue = value;
else
directives.incrementValue = saturatedSum<int>(directives.incrementValue.value_or(0), 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::applyInitialCursor(BuilderState& builderState)
{
builderState.style().clearCursorList();
builderState.style().setCursor(RenderStyle::initialCursor());
}
inline void BuilderCustom::applyInheritCursor(BuilderState& builderState)
{
builderState.style().setCursor(builderState.parentStyle().cursor());
builderState.style().setCursorList(builderState.parentStyle().cursors());
}
inline void BuilderCustom::applyValueCursor(BuilderState& builderState, CSSValue& value)
{
builderState.style().clearCursorList();
if (is<CSSPrimitiveValue>(value)) {
CursorType cursor = downcast<CSSPrimitiveValue>(value);
if (builderState.style().cursor() != cursor)
builderState.style().setCursor(cursor);
return;
}
builderState.style().setCursor(CursorType::Auto);
auto& list = downcast<CSSValueList>(value);
for (auto& item : list) {
if (is<CSSCursorImageValue>(item)) {
auto& image = downcast<CSSCursorImageValue>(item.get());
builderState.style().addCursor(builderState.createStyleImage(image), image.hotSpot());
continue;
}
builderState.style().setCursor(downcast<CSSPrimitiveValue>(item.get()));
ASSERT_WITH_MESSAGE(item.ptr() == list.item(list.length() - 1), "Cursor ID fallback should always be last in the list");
return;
}
}
inline void BuilderCustom::applyInitialFill(BuilderState& builderState)
{
auto& svgStyle = builderState.style().accessSVGStyle();
svgStyle.setFillPaint(SVGRenderStyle::initialFillPaintType(), SVGRenderStyle::initialFillPaintColor(), SVGRenderStyle::initialFillPaintUri(), builderState.applyPropertyToRegularStyle(), builderState.applyPropertyToVisitedLinkStyle());
}
inline void BuilderCustom::applyInheritFill(BuilderState& builderState)
{
auto& svgStyle = builderState.style().accessSVGStyle();
auto& svgParentStyle = builderState.parentStyle().svgStyle();
svgStyle.setFillPaint(svgParentStyle.fillPaintType(), svgParentStyle.fillPaintColor(), svgParentStyle.fillPaintUri(), builderState.applyPropertyToRegularStyle(), builderState.applyPropertyToVisitedLinkStyle());
}
inline void BuilderCustom::applyValueFill(BuilderState& builderState, CSSValue& value)
{
auto& svgStyle = builderState.style().accessSVGStyle();
const auto* localValue = value.isPrimitiveValue() ? &downcast<CSSPrimitiveValue>(value) : nullptr;
String url;
if (value.isValueList()) {
const CSSValueList& list = downcast<CSSValueList>(value);
url = downcast<CSSPrimitiveValue>(list.item(0))->stringValue();
localValue = downcast<CSSPrimitiveValue>(list.item(1));
}
if (!localValue)
return;
Color color;
auto paintType = SVGPaintType::RGBColor;
if (localValue->isURI()) {
paintType = SVGPaintType::URI;
url = localValue->stringValue();
} else if (localValue->isValueID() && localValue->valueID() == CSSValueNone)
paintType = url.isEmpty() ? SVGPaintType::None : SVGPaintType::URINone;
else if (localValue->isValueID() && localValue->valueID() == CSSValueCurrentcolor) {
color = builderState.style().color();
paintType = url.isEmpty() ? SVGPaintType::CurrentColor : SVGPaintType::URICurrentColor;
} else {
color = builderState.colorFromPrimitiveValue(*localValue);
paintType = url.isEmpty() ? SVGPaintType::RGBColor : SVGPaintType::URIRGBColor;
}
svgStyle.setFillPaint(paintType, color, url, builderState.applyPropertyToRegularStyle(), builderState.applyPropertyToVisitedLinkStyle());
}
inline void BuilderCustom::applyInitialStroke(BuilderState& builderState)
{
SVGRenderStyle& svgStyle = builderState.style().accessSVGStyle();
svgStyle.setStrokePaint(SVGRenderStyle::initialStrokePaintType(), SVGRenderStyle::initialStrokePaintColor(), SVGRenderStyle::initialStrokePaintUri(), builderState.applyPropertyToRegularStyle(), builderState.applyPropertyToVisitedLinkStyle());
}
inline void BuilderCustom::applyInheritStroke(BuilderState& builderState)
{
auto& svgStyle = builderState.style().accessSVGStyle();
auto& svgParentStyle = builderState.parentStyle().svgStyle();
svgStyle.setStrokePaint(svgParentStyle.strokePaintType(), svgParentStyle.strokePaintColor(), svgParentStyle.strokePaintUri(), builderState.applyPropertyToRegularStyle(), builderState.applyPropertyToVisitedLinkStyle());
}
inline void BuilderCustom::applyValueStroke(BuilderState& builderState, CSSValue& value)
{
auto& svgStyle = builderState.style().accessSVGStyle();
const auto* localValue = value.isPrimitiveValue() ? &downcast<CSSPrimitiveValue>(value) : nullptr;
String url;
if (value.isValueList()) {
const CSSValueList& list = downcast<CSSValueList>(value);
url = downcast<CSSPrimitiveValue>(list.item(0))->stringValue();
localValue = downcast<CSSPrimitiveValue>(list.item(1));
}
if (!localValue)
return;
Color color;
auto paintType = SVGPaintType::RGBColor;
if (localValue->isURI()) {
paintType = SVGPaintType::URI;
url = downcast<CSSPrimitiveValue>(localValue)->stringValue();
} else if (localValue->isValueID() && localValue->valueID() == CSSValueNone)
paintType = url.isEmpty() ? SVGPaintType::None : SVGPaintType::URINone;
else if (localValue->isValueID() && localValue->valueID() == CSSValueCurrentcolor) {
color = builderState.style().color();
paintType = url.isEmpty() ? SVGPaintType::CurrentColor : SVGPaintType::URICurrentColor;
} else {
color = builderState.colorFromPrimitiveValue(*localValue);
paintType = url.isEmpty() ? SVGPaintType::RGBColor : SVGPaintType::URIRGBColor;
}
svgStyle.setStrokePaint(paintType, color, url, builderState.applyPropertyToRegularStyle(), builderState.applyPropertyToVisitedLinkStyle());
}
inline void BuilderCustom::applyInitialContent(BuilderState& builderState)
{
builderState.style().clearContent();
builderState.style().setHasExplicitlyClearedContent(true);
}
inline void BuilderCustom::applyInheritContent(BuilderState&)
{
// FIXME: In CSS3, it will be possible to inherit content. In CSS2 it is not. This
// note is a reminder that eventually "inherit" needs to be supported.
}
inline void BuilderCustom::applyValueContent(BuilderState& builderState, CSSValue& value)
{
if (is<CSSPrimitiveValue>(value)) {
const auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
ASSERT_UNUSED(primitiveValue, primitiveValue.valueID() == CSSValueNormal || primitiveValue.valueID() == CSSValueNone);
builderState.style().clearContent();
builderState.style().setHasExplicitlyClearedContent(true);
return;
}
bool didSet = false;
for (auto& item : downcast<CSSValueList>(value)) {
if (is<CSSImageGeneratorValue>(item)) {
if (is<CSSGradientValue>(item))
builderState.style().setContent(StyleGeneratedImage::create(downcast<CSSGradientValue>(builderState.resolveImageStyles(item.get()).get())), didSet);
else
builderState.style().setContent(StyleGeneratedImage::create(downcast<CSSImageGeneratorValue>(builderState.resolveImageStyles(item.get()).get())), didSet);
didSet = true;
}
if (is<CSSImageSetValue>(item)) {
builderState.style().setContent(StyleImageSet::create(downcast<CSSImageSetValue>(builderState.resolveImageStyles(item.get()).get())), didSet);
didSet = true;
}
if (is<CSSImageValue>(item)) {
builderState.style().setContent(StyleCachedImage::create(downcast<CSSImageValue>(builderState.resolveImageStyles(item.get()).get())), didSet);
didSet = true;
continue;
}
if (!is<CSSPrimitiveValue>(item))
continue;
auto& contentValue = downcast<CSSPrimitiveValue>(item.get());
if (contentValue.isString()) {
builderState.style().setContent(contentValue.stringValue().impl(), didSet);
didSet = true;
} else if (contentValue.isAttr()) {
// FIXME: Can a namespace be specified for an attr(foo)?
if (builderState.style().styleType() == PseudoId::None)
builderState.style().setHasAttrContent();
else
const_cast<RenderStyle&>(builderState.parentStyle()).setHasAttrContent();
QualifiedName attr(nullAtom(), contentValue.stringValue().impl(), nullAtom());
const AtomString& value = builderState.element() ? builderState.element()->getAttribute(attr) : nullAtom();
builderState.style().setContent(value.isNull() ? emptyAtom() : value.impl(), didSet);
didSet = true;
// Register the fact that the attribute value affects the style.
builderState.registerContentAttribute(attr.localName());
} else if (contentValue.isCounter()) {
auto* counterValue = contentValue.counterValue();
ListStyleType listStyleType = ListStyleType::None;
CSSValueID listStyleIdent = counterValue->listStyleIdent();
if (listStyleIdent != CSSValueNone)
listStyleType = static_cast<ListStyleType>(listStyleIdent - CSSValueDisc);
auto counter = makeUnique<CounterContent>(counterValue->identifier(), listStyleType, counterValue->separator());
builderState.style().setContent(WTFMove(counter), didSet);
didSet = true;
} else {
switch (contentValue.valueID()) {
case CSSValueOpenQuote:
builderState.style().setContent(QuoteType::OpenQuote, didSet);
didSet = true;
break;
case CSSValueCloseQuote:
builderState.style().setContent(QuoteType::CloseQuote, didSet);
didSet = true;
break;
case CSSValueNoOpenQuote:
builderState.style().setContent(QuoteType::NoOpenQuote, didSet);
didSet = true;
break;
case CSSValueNoCloseQuote:
builderState.style().setContent(QuoteType::NoCloseQuote, didSet);
didSet = true;
break;
default:
// normal and none do not have any effect.
break;
}
}
}
if (!didSet)
builderState.style().clearContent();
}
inline void BuilderCustom::applyInheritFontVariantLigatures(BuilderState& builderState)
{
auto fontDescription = builderState.fontDescription();
fontDescription.setVariantCommonLigatures(builderState.parentFontDescription().variantCommonLigatures());
fontDescription.setVariantDiscretionaryLigatures(builderState.parentFontDescription().variantDiscretionaryLigatures());
fontDescription.setVariantHistoricalLigatures(builderState.parentFontDescription().variantHistoricalLigatures());
fontDescription.setVariantContextualAlternates(builderState.parentFontDescription().variantContextualAlternates());
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInitialFontVariantLigatures(BuilderState& builderState)
{
auto fontDescription = builderState.fontDescription();
fontDescription.setVariantCommonLigatures(FontVariantLigatures::Normal);
fontDescription.setVariantDiscretionaryLigatures(FontVariantLigatures::Normal);
fontDescription.setVariantHistoricalLigatures(FontVariantLigatures::Normal);
fontDescription.setVariantContextualAlternates(FontVariantLigatures::Normal);
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyValueFontVariantLigatures(BuilderState& builderState, CSSValue& value)
{
auto fontDescription = builderState.fontDescription();
auto variantLigatures = extractFontVariantLigatures(value);
fontDescription.setVariantCommonLigatures(variantLigatures.commonLigatures);
fontDescription.setVariantDiscretionaryLigatures(variantLigatures.discretionaryLigatures);
fontDescription.setVariantHistoricalLigatures(variantLigatures.historicalLigatures);
fontDescription.setVariantContextualAlternates(variantLigatures.contextualAlternates);
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInheritFontVariantNumeric(BuilderState& builderState)
{
auto fontDescription = builderState.fontDescription();
fontDescription.setVariantNumericFigure(builderState.parentFontDescription().variantNumericFigure());
fontDescription.setVariantNumericSpacing(builderState.parentFontDescription().variantNumericSpacing());
fontDescription.setVariantNumericFraction(builderState.parentFontDescription().variantNumericFraction());
fontDescription.setVariantNumericOrdinal(builderState.parentFontDescription().variantNumericOrdinal());
fontDescription.setVariantNumericSlashedZero(builderState.parentFontDescription().variantNumericSlashedZero());
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInitialFontVariantNumeric(BuilderState& builderState)
{
auto fontDescription = builderState.fontDescription();
fontDescription.setVariantNumericFigure(FontVariantNumericFigure::Normal);
fontDescription.setVariantNumericSpacing(FontVariantNumericSpacing::Normal);
fontDescription.setVariantNumericFraction(FontVariantNumericFraction::Normal);
fontDescription.setVariantNumericOrdinal(FontVariantNumericOrdinal::Normal);
fontDescription.setVariantNumericSlashedZero(FontVariantNumericSlashedZero::Normal);
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyValueFontVariantNumeric(BuilderState& builderState, CSSValue& value)
{
auto fontDescription = builderState.fontDescription();
auto variantNumeric = extractFontVariantNumeric(value);
fontDescription.setVariantNumericFigure(variantNumeric.figure);
fontDescription.setVariantNumericSpacing(variantNumeric.spacing);
fontDescription.setVariantNumericFraction(variantNumeric.fraction);
fontDescription.setVariantNumericOrdinal(variantNumeric.ordinal);
fontDescription.setVariantNumericSlashedZero(variantNumeric.slashedZero);
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInheritFontVariantEastAsian(BuilderState& builderState)
{
auto fontDescription = builderState.fontDescription();
fontDescription.setVariantEastAsianVariant(builderState.parentFontDescription().variantEastAsianVariant());
fontDescription.setVariantEastAsianWidth(builderState.parentFontDescription().variantEastAsianWidth());
fontDescription.setVariantEastAsianRuby(builderState.parentFontDescription().variantEastAsianRuby());
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInitialFontVariantEastAsian(BuilderState& builderState)
{
auto fontDescription = builderState.fontDescription();
fontDescription.setVariantEastAsianVariant(FontVariantEastAsianVariant::Normal);
fontDescription.setVariantEastAsianWidth(FontVariantEastAsianWidth::Normal);
fontDescription.setVariantEastAsianRuby(FontVariantEastAsianRuby::Normal);
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyValueFontVariantEastAsian(BuilderState& builderState, CSSValue& value)
{
auto fontDescription = builderState.fontDescription();
auto variantEastAsian = extractFontVariantEastAsian(value);
fontDescription.setVariantEastAsianVariant(variantEastAsian.variant);
fontDescription.setVariantEastAsianWidth(variantEastAsian.width);
fontDescription.setVariantEastAsianRuby(variantEastAsian.ruby);
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;
auto fontDescription = builderState.fontDescription();
fontDescription.setKeywordSize(parentFontDescription.keywordSize());
builderState.setFontSize(fontDescription, size);
builderState.setFontDescription(WTFMove(fontDescription));
}
// 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().rubyPosition() != RubyPosition::InterCharacter)
return 0.5f;
// 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;
}
inline void BuilderCustom::applyInitialFontStyle(BuilderState& builderState)
{
auto fontDescription = builderState.fontDescription();
fontDescription.setItalic(FontCascadeDescription::initialItalic());
fontDescription.setFontStyleAxis(FontCascadeDescription::initialFontStyleAxis());
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInheritFontStyle(BuilderState& builderState)
{
auto fontDescription = builderState.fontDescription();
fontDescription.setItalic(builderState.parentFontDescription().italic());
fontDescription.setFontStyleAxis(builderState.parentFontDescription().fontStyleAxis());
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyValueFontStyle(BuilderState& builderState, CSSValue& value)
{
auto& fontStyleValue = downcast<CSSFontStyleValue>(value);
auto fontDescription = builderState.fontDescription();
fontDescription.setItalic(BuilderConverter::convertFontStyleFromValue(fontStyleValue));
fontDescription.setFontStyleAxis(fontStyleValue.fontStyleValue->valueID() == CSSValueItalic ? FontStyleAxis::ital : FontStyleAxis::slnt);
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyValueFontSize(BuilderState& builderState, CSSValue& value)
{
auto fontDescription = builderState.fontDescription();
fontDescription.setKeywordSizeFromIdentifier(CSSValueInvalid);
float parentSize = builderState.parentStyle().fontDescription().specifiedSize();
bool parentIsAbsoluteSize = builderState.parentStyle().fontDescription().isAbsoluteSize();
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
float size;
if (CSSValueID ident = primitiveValue.valueID()) {
fontDescription.setIsAbsoluteSize(parentIsAbsoluteSize && (ident == CSSValueLarger || ident == CSSValueSmaller || ident == CSSValueWebkitRubyText));
// Keywords are being used.
switch (ident) {
case CSSValueXxSmall:
case CSSValueXSmall:
case CSSValueSmall:
case CSSValueMedium:
case CSSValueLarge:
case CSSValueXLarge:
case CSSValueXxLarge:
case CSSValueWebkitXxxLarge:
size = Style::fontSizeForKeyword(ident, fontDescription.useFixedDefaultSize(), builderState.document());
fontDescription.setKeywordSizeFromIdentifier(ident);
break;
case CSSValueLarger:
size = largerFontSize(parentSize);
break;
case CSSValueSmaller:
size = smallerFontSize(parentSize);
break;
case CSSValueWebkitRubyText:
size = determineRubyTextSizeMultiplier(builderState) * parentSize;
break;
default:
return;
}
} else {
fontDescription.setIsAbsoluteSize(parentIsAbsoluteSize || !(primitiveValue.isPercentage() || primitiveValue.isFontRelativeLength()));
if (primitiveValue.isLength()) {
CSSToLengthConversionData conversionData { &builderState.parentStyle(), builderState.rootElementStyle(), &builderState.parentStyle(), builderState.document().renderView(), 1.0f, CSSPropertyFontSize, &builderState.style() };
size = primitiveValue.computeLength<float>(conversionData);
} else if (primitiveValue.isPercentage())
size = (primitiveValue.floatValue() * parentSize) / 100.0f;
else if (primitiveValue.isCalculatedPercentageWithLength()) {
// FIXME: Why does this need a different root style than the isLength case above?
CSSToLengthConversionData conversionData { &builderState.parentStyle(), builderState.cssToLengthConversionData().rootStyle(), &builderState.parentStyle(), builderState.document().renderView(), 1.0f, CSSPropertyFontSize, &builderState.style() };
size = primitiveValue.cssCalcValue()->createCalculationValue(conversionData)->evaluate(parentSize);
} else
return;
}
if (size < 0)
return;
builderState.setFontSize(fontDescription, std::min(maximumAllowedFontSize, size));
builderState.setFontDescription(WTFMove(fontDescription));
}
inline void BuilderCustom::applyInitialGridTemplateAreas(BuilderState& builderState)
{
builderState.style().setImplicitNamedGridColumnLines(RenderStyle::initialNamedGridColumnLines());
builderState.style().setImplicitNamedGridRowLines(RenderStyle::initialNamedGridRowLines());
builderState.style().setNamedGridArea(RenderStyle::initialNamedGridArea());
builderState.style().setNamedGridAreaRowCount(RenderStyle::initialNamedGridAreaCount());
builderState.style().setNamedGridAreaColumnCount(RenderStyle::initialNamedGridAreaCount());
}
inline void BuilderCustom::applyInheritGridTemplateAreas(BuilderState& builderState)
{
builderState.style().setImplicitNamedGridColumnLines(builderState.parentStyle().implicitNamedGridColumnLines());
builderState.style().setImplicitNamedGridRowLines(builderState.parentStyle().implicitNamedGridRowLines());
builderState.style().setNamedGridArea(builderState.parentStyle().namedGridArea());
builderState.style().setNamedGridAreaRowCount(builderState.parentStyle().namedGridAreaRowCount());
builderState.style().setNamedGridAreaColumnCount(builderState.parentStyle().namedGridAreaColumnCount());
}
inline void BuilderCustom::applyValueGridTemplateAreas(BuilderState& builderState, CSSValue& value)
{
if (is<CSSPrimitiveValue>(value)) {
ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone);
applyInitialGridTemplateAreas(builderState);
return;
}
auto& gridTemplateAreasValue = downcast<CSSGridTemplateAreasValue>(value);
const NamedGridAreaMap& newNamedGridAreas = gridTemplateAreasValue.gridAreaMap();
NamedGridLinesMap implicitNamedGridColumnLines;
NamedGridLinesMap implicitNamedGridRowLines;
BuilderConverter::createImplicitNamedGridLinesFromGridArea(newNamedGridAreas, implicitNamedGridColumnLines, ForColumns);
BuilderConverter::createImplicitNamedGridLinesFromGridArea(newNamedGridAreas, implicitNamedGridRowLines, ForRows);
builderState.style().setImplicitNamedGridColumnLines(implicitNamedGridColumnLines);
builderState.style().setImplicitNamedGridRowLines(implicitNamedGridRowLines);
builderState.style().setNamedGridArea(gridTemplateAreasValue.gridAreaMap());
builderState.style().setNamedGridAreaRowCount(gridTemplateAreasValue.rowCount());
builderState.style().setNamedGridAreaColumnCount(gridTemplateAreasValue.columnCount());
}
#define SET_TRACKS_DATA_INTERNAL(tracksData, style, parentStyle, TrackType) \
ASSERT(tracksData || parentStyle); \
style.setGrid##TrackType##s(tracksData ? tracksData->m_trackSizes : parentStyle->grid##TrackType##s()); \
style.setNamedGrid##TrackType##Lines(tracksData ? tracksData->m_namedGridLines : parentStyle->namedGrid##TrackType##Lines()); \
style.setOrderedNamedGrid##TrackType##Lines(tracksData ? tracksData->m_orderedNamedGridLines : parentStyle->orderedNamedGrid##TrackType##Lines()); \
style.setGridAutoRepeat##TrackType##s(tracksData ? tracksData->m_autoRepeatTrackSizes : parentStyle->gridAutoRepeat##TrackType##s()); \
style.setGridAutoRepeat##TrackType##sInsertionPoint(tracksData ? tracksData->m_autoRepeatInsertionPoint : parentStyle->gridAutoRepeat##TrackType##sInsertionPoint()); \
style.setAutoRepeatNamedGrid##TrackType##Lines(tracksData ? tracksData->m_autoRepeatNamedGridLines : parentStyle->autoRepeatNamedGrid##TrackType##Lines()); \
style.setAutoRepeatOrderedNamedGrid##TrackType##Lines(tracksData ? tracksData->m_autoRepeatOrderedNamedGridLines : parentStyle->autoRepeatOrderedNamedGrid##TrackType##Lines()); \
style.setGridAutoRepeat##TrackType##sType(tracksData ? tracksData->m_autoRepeatType : parentStyle->gridAutoRepeat##TrackType##sType());
#define SET_INHERIT_TRACKS_DATA(style, parentStyle, TrackType) \
BuilderConverter::TracksData* tracksData = nullptr; \
const RenderStyle* parentStylePointer = &parentStyle; \
SET_TRACKS_DATA_INTERNAL(tracksData, style, parentStylePointer, TrackType)
#define SET_TRACKS_DATA(tracksData, style, TrackType) \
BuilderConverter::TracksData* tracksDataPointer = &tracksData; \
const RenderStyle* parentStyle = nullptr; \
SET_TRACKS_DATA_INTERNAL(tracksDataPointer, style, parentStyle, TrackType)
inline void BuilderCustom::applyInitialGridTemplateColumns(BuilderState& builderState)
{
BuilderConverter::TracksData initialTracksData;
SET_TRACKS_DATA(initialTracksData, builderState.style(), Column);
}
inline void BuilderCustom::applyInheritGridTemplateColumns(BuilderState& builderState)
{
SET_INHERIT_TRACKS_DATA(builderState.style(), builderState.parentStyle(), Column);
}
inline void BuilderCustom::applyValueGridTemplateColumns(BuilderState& builderState, CSSValue& value)
{
BuilderConverter::TracksData tracksData;
if (!BuilderConverter::createGridTrackList(value, tracksData, builderState))
return;
const NamedGridAreaMap& namedGridAreas = builderState.style().namedGridArea();
if (!namedGridAreas.isEmpty())
BuilderConverter::createImplicitNamedGridLinesFromGridArea(namedGridAreas, tracksData.m_namedGridLines, ForColumns);
SET_TRACKS_DATA(tracksData, builderState.style(), Column);
}
inline void BuilderCustom::applyInitialGridTemplateRows(BuilderState& builderState)
{
BuilderConverter::TracksData initialTracksData;
SET_TRACKS_DATA(initialTracksData, builderState.style(), Row);
}
inline void BuilderCustom::applyInheritGridTemplateRows(BuilderState& builderState)
{
SET_INHERIT_TRACKS_DATA(builderState.style(), builderState.parentStyle(), Row);
}
inline void BuilderCustom::applyValueGridTemplateRows(BuilderState& builderState, CSSValue& value)
{
BuilderConverter::TracksData tracksData;
if (!BuilderConverter::createGridTrackList(value, tracksData, builderState))
return;
const NamedGridAreaMap& namedGridAreas = builderState.style().namedGridArea();
if (!namedGridAreas.isEmpty())
BuilderConverter::createImplicitNamedGridLinesFromGridArea(namedGridAreas, tracksData.m_namedGridLines, ForRows);
SET_TRACKS_DATA(tracksData, builderState.style(), Row);
}
void BuilderCustom::applyValueAlt(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (primitiveValue.isString())
builderState.style().setContentAltText(primitiveValue.stringValue());
else if (primitiveValue.isAttr()) {
// FIXME: Can a namespace be specified for an attr(foo)?
if (builderState.style().styleType() == PseudoId::None)
builderState.style().setUnique();
else
const_cast<RenderStyle&>(builderState.parentStyle()).setUnique();
QualifiedName attr(nullAtom(), primitiveValue.stringValue(), nullAtom());
const AtomString& value = builderState.element() ? builderState.element()->getAttribute(attr) : nullAtom();
builderState.style().setContentAltText(value.isNull() ? emptyAtom() : value);
// Register the fact that the attribute value affects the style.
builderState.registerContentAttribute(attr.localName());
} else
builderState.style().setContentAltText(emptyAtom());
}
inline void BuilderCustom::applyValueWillChange(BuilderState& builderState, CSSValue& value)
{
if (is<CSSPrimitiveValue>(value)) {
ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueAuto);
builderState.style().setWillChange(nullptr);
return;
}
auto willChange = WillChangeData::create();
for (auto& item : downcast<CSSValueList>(value)) {
if (!is<CSSPrimitiveValue>(item))
continue;
auto& primitiveValue = downcast<CSSPrimitiveValue>(item.get());
switch (primitiveValue.valueID()) {
case CSSValueScrollPosition:
willChange->addFeature(WillChangeData::Feature::ScrollPosition);
break;
case CSSValueContents:
willChange->addFeature(WillChangeData::Feature::Contents);
break;
default:
if (primitiveValue.isPropertyID()) {
if (primitiveValue.propertyID() == CSSPropertyContain && !builderState.document().settings().cssContainmentEnabled())
break;
willChange->addFeature(WillChangeData::Feature::Property, primitiveValue.propertyID());
}
break;
}
}
builderState.style().setWillChange(WTFMove(willChange));
}
inline void BuilderCustom::applyValueStrokeWidth(BuilderState& builderState, CSSValue& value)
{
builderState.style().setStrokeWidth(BuilderConverter::convertLength(builderState, value));
builderState.style().setHasExplicitlySetStrokeWidth(true);
}
inline void BuilderCustom::applyValueStrokeColor(BuilderState& builderState, CSSValue& value)
{
auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
if (builderState.applyPropertyToRegularStyle())
builderState.style().setStrokeColor(builderState.colorFromPrimitiveValue(primitiveValue, ForVisitedLink::No));
if (builderState.applyPropertyToVisitedLinkStyle())
builderState.style().setVisitedLinkStrokeColor(builderState.colorFromPrimitiveValue(primitiveValue, ForVisitedLink::Yes));
builderState.style().setHasExplicitlySetStrokeColor(true);
}
inline void BuilderCustom::applyInitialCustomProperty(BuilderState& builderState, const CSSRegisteredCustomProperty* registered, const AtomString& name)
{
if (registered && registered->initialValue()) {
auto initialValue = registered->initialValueCopy();
applyValueCustomProperty(builderState, registered, *initialValue);
return;
}
auto invalid = CSSCustomPropertyValue::createUnresolved(name, CSSValueInvalid);
applyValueCustomProperty(builderState, registered, invalid.get());
}
inline void BuilderCustom::applyInheritCustomProperty(BuilderState& builderState, const CSSRegisteredCustomProperty* registered, const AtomString& name)
{
auto* parentValue = builderState.parentStyle().inheritedCustomProperties().get(name);
if (parentValue && !(registered && !registered->inherits))
applyValueCustomProperty(builderState, registered, *parentValue);
else
applyInitialCustomProperty(builderState, registered, name);
}
inline void BuilderCustom::applyValueCustomProperty(BuilderState& builderState, const CSSRegisteredCustomProperty* registered, CSSCustomPropertyValue& value)
{
ASSERT(value.isResolved());
const auto& name = value.name();
if (!registered || registered->inherits)
builderState.style().setInheritedCustomPropertyValue(name, value);
else
builderState.style().setNonInheritedCustomPropertyValue(name, value);
}
}
}