1/*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#pragma once
28
29#include "BasicShapeFunctions.h"
30#include "CSSCalculationValue.h"
31#include "CSSContentDistributionValue.h"
32#include "CSSFontFeatureValue.h"
33#include "CSSFontStyleValue.h"
34#include "CSSFontVariationValue.h"
35#include "CSSFunctionValue.h"
36#include "CSSGridAutoRepeatValue.h"
37#include "CSSGridLineNamesValue.h"
38#include "CSSImageGeneratorValue.h"
39#include "CSSImageSetValue.h"
40#include "CSSImageValue.h"
41#include "CSSPrimitiveValue.h"
42#include "CSSPrimitiveValueMappings.h"
43#include "CSSReflectValue.h"
44#include "FontSelectionValueInlines.h"
45#include "Frame.h"
46#include "GridPositionsResolver.h"
47#include "Length.h"
48#include "Pair.h"
49#include "QuotesData.h"
50#include "RuntimeEnabledFeatures.h"
51#include "SVGURIReference.h"
52#include "Settings.h"
53#include "StyleResolver.h"
54#include "StyleScrollSnapPoints.h"
55#include "TouchAction.h"
56#include "TransformFunctions.h"
57#include <wtf/Optional.h>
58
59namespace WebCore {
60
61// Note that we assume the CSS parser only allows valid CSSValue types.
62class StyleBuilderConverter {
63public:
64 static Length convertLength(const StyleResolver&, const CSSValue&);
65 static Length convertLengthOrAuto(const StyleResolver&, const CSSValue&);
66 static Length convertLengthSizing(const StyleResolver&, const CSSValue&);
67 static Length convertLengthMaxSizing(const StyleResolver&, const CSSValue&);
68 template<typename T> static T convertComputedLength(StyleResolver&, const CSSValue&);
69 template<typename T> static T convertLineWidth(StyleResolver&, const CSSValue&);
70 static float convertSpacing(StyleResolver&, const CSSValue&);
71 static LengthSize convertRadius(StyleResolver&, const CSSValue&);
72 static LengthPoint convertObjectPosition(StyleResolver&, const CSSValue&);
73 static OptionSet<TextDecoration> convertTextDecoration(StyleResolver&, const CSSValue&);
74 template<typename T> static T convertNumber(StyleResolver&, const CSSValue&);
75 template<typename T> static T convertNumberOrAuto(StyleResolver&, const CSSValue&);
76 static short convertWebkitHyphenateLimitLines(StyleResolver&, const CSSValue&);
77 template<CSSPropertyID> static NinePieceImage convertBorderImage(StyleResolver&, CSSValue&);
78 template<CSSPropertyID> static NinePieceImage convertBorderMask(StyleResolver&, CSSValue&);
79 template<CSSPropertyID> static RefPtr<StyleImage> convertStyleImage(StyleResolver&, CSSValue&);
80 static TransformOperations convertTransform(StyleResolver&, const CSSValue&);
81#if ENABLE(DARK_MODE_CSS)
82 static StyleColorScheme convertColorScheme(StyleResolver&, const CSSValue&);
83#endif
84 static String convertString(StyleResolver&, const CSSValue&);
85 static String convertStringOrAuto(StyleResolver&, const CSSValue&);
86 static String convertStringOrNone(StyleResolver&, const CSSValue&);
87 static OptionSet<TextEmphasisPosition> convertTextEmphasisPosition(StyleResolver&, const CSSValue&);
88 static TextAlignMode convertTextAlign(StyleResolver&, const CSSValue&);
89 static RefPtr<ClipPathOperation> convertClipPath(StyleResolver&, const CSSValue&);
90 static Resize convertResize(StyleResolver&, const CSSValue&);
91 static int convertMarqueeRepetition(StyleResolver&, const CSSValue&);
92 static int convertMarqueeSpeed(StyleResolver&, const CSSValue&);
93 static Ref<QuotesData> convertQuotes(StyleResolver&, const CSSValue&);
94 static TextUnderlinePosition convertTextUnderlinePosition(StyleResolver&, const CSSValue&);
95 static TextUnderlineOffset convertTextUnderlineOffset(StyleResolver&, const CSSValue&);
96 static TextDecorationThickness convertTextDecorationThickness(StyleResolver&, const CSSValue&);
97 static RefPtr<StyleReflection> convertReflection(StyleResolver&, const CSSValue&);
98 static IntSize convertInitialLetter(StyleResolver&, const CSSValue&);
99 static float convertTextStrokeWidth(StyleResolver&, const CSSValue&);
100 static LineBoxContain convertLineBoxContain(StyleResolver&, const CSSValue&);
101 static OptionSet<TextDecorationSkip> convertTextDecorationSkip(StyleResolver&, const CSSValue&);
102 static RefPtr<ShapeValue> convertShapeValue(StyleResolver&, CSSValue&);
103#if ENABLE(CSS_SCROLL_SNAP)
104 static ScrollSnapType convertScrollSnapType(StyleResolver&, const CSSValue&);
105 static ScrollSnapAlign convertScrollSnapAlign(StyleResolver&, const CSSValue&);
106#endif
107 static GridTrackSize convertGridTrackSize(StyleResolver&, const CSSValue&);
108 static Vector<GridTrackSize> convertGridTrackSizeList(StyleResolver&, const CSSValue&);
109 static Optional<GridPosition> convertGridPosition(StyleResolver&, const CSSValue&);
110 static GridAutoFlow convertGridAutoFlow(StyleResolver&, const CSSValue&);
111 static Optional<Length> convertWordSpacing(StyleResolver&, const CSSValue&);
112 static Optional<float> convertPerspective(StyleResolver&, const CSSValue&);
113 static Optional<Length> convertMarqueeIncrement(StyleResolver&, const CSSValue&);
114 static Optional<FilterOperations> convertFilterOperations(StyleResolver&, const CSSValue&);
115#if PLATFORM(IOS_FAMILY)
116 static bool convertTouchCallout(StyleResolver&, const CSSValue&);
117#endif
118#if ENABLE(TOUCH_EVENTS)
119 static Color convertTapHighlightColor(StyleResolver&, const CSSValue&);
120#endif
121#if ENABLE(POINTER_EVENTS)
122 static OptionSet<TouchAction> convertTouchAction(StyleResolver&, const CSSValue&);
123#endif
124#if ENABLE(OVERFLOW_SCROLLING_TOUCH)
125 static bool convertOverflowScrolling(StyleResolver&, const CSSValue&);
126#endif
127 static FontFeatureSettings convertFontFeatureSettings(StyleResolver&, const CSSValue&);
128 static FontSelectionValue convertFontWeightFromValue(const CSSValue&);
129 static FontSelectionValue convertFontStretchFromValue(const CSSValue&);
130 static Optional<FontSelectionValue> convertFontStyleFromValue(const CSSValue&);
131 static FontSelectionValue convertFontWeight(StyleResolver&, const CSSValue&);
132 static FontSelectionValue convertFontStretch(StyleResolver&, const CSSValue&);
133 static FontSelectionValue convertFontStyle(StyleResolver&, const CSSValue&);
134#if ENABLE(VARIATION_FONTS)
135 static FontVariationSettings convertFontVariationSettings(StyleResolver&, const CSSValue&);
136#endif
137 static SVGLengthValue convertSVGLengthValue(StyleResolver&, const CSSValue&);
138 static Vector<SVGLengthValue> convertSVGLengthVector(StyleResolver&, const CSSValue&);
139 static Vector<SVGLengthValue> convertStrokeDashArray(StyleResolver&, const CSSValue&);
140 static PaintOrder convertPaintOrder(StyleResolver&, const CSSValue&);
141 static float convertOpacity(StyleResolver&, const CSSValue&);
142 static String convertSVGURIReference(StyleResolver&, const CSSValue&);
143 static Color convertSVGColor(StyleResolver&, const CSSValue&);
144 static StyleSelfAlignmentData convertSelfOrDefaultAlignmentData(StyleResolver&, const CSSValue&);
145 static StyleContentAlignmentData convertContentAlignmentData(StyleResolver&, const CSSValue&);
146 static GlyphOrientation convertGlyphOrientation(StyleResolver&, const CSSValue&);
147 static GlyphOrientation convertGlyphOrientationOrAuto(StyleResolver&, const CSSValue&);
148 static Optional<Length> convertLineHeight(StyleResolver&, const CSSValue&, float multiplier = 1.f);
149 static FontSynthesis convertFontSynthesis(StyleResolver&, const CSSValue&);
150
151 static BreakBetween convertPageBreakBetween(StyleResolver&, const CSSValue&);
152 static BreakInside convertPageBreakInside(StyleResolver&, const CSSValue&);
153 static BreakBetween convertColumnBreakBetween(StyleResolver&, const CSSValue&);
154 static BreakInside convertColumnBreakInside(StyleResolver&, const CSSValue&);
155
156 static OptionSet<HangingPunctuation> convertHangingPunctuation(StyleResolver&, const CSSValue&);
157
158 static OptionSet<SpeakAs> convertSpeakAs(StyleResolver&, const CSSValue&);
159
160 static Length convertPositionComponentX(StyleResolver&, const CSSValue&);
161 static Length convertPositionComponentY(StyleResolver&, const CSSValue&);
162
163 static GapLength convertGapLength(StyleResolver&, const CSSValue&);
164
165private:
166 friend class StyleBuilderCustom;
167
168 static Length convertToRadiusLength(CSSToLengthConversionData&, const CSSPrimitiveValue&);
169 static OptionSet<TextEmphasisPosition> valueToEmphasisPosition(const CSSPrimitiveValue&);
170 static OptionSet<TextDecorationSkip> valueToDecorationSkip(const CSSPrimitiveValue&);
171#if ENABLE(CSS_SCROLL_SNAP)
172 static Length parseSnapCoordinate(StyleResolver&, const CSSValue&);
173#endif
174
175#if ENABLE(DARK_MODE_CSS)
176 static void updateColorScheme(const CSSPrimitiveValue&, StyleColorScheme&);
177#endif
178
179 static Length convertTo100PercentMinusLength(const Length&);
180 template<CSSValueID, CSSValueID> static Length convertPositionComponent(StyleResolver&, const CSSPrimitiveValue&);
181
182 static GridLength createGridTrackBreadth(const CSSPrimitiveValue&, StyleResolver&);
183 static GridTrackSize createGridTrackSize(const CSSValue&, StyleResolver&);
184 struct TracksData;
185 static bool createGridTrackList(const CSSValue&, TracksData&, StyleResolver&);
186 static bool createGridPosition(const CSSValue&, GridPosition&);
187 static void createImplicitNamedGridLinesFromGridArea(const NamedGridAreaMap&, NamedGridLinesMap&, GridTrackSizingDirection);
188 static CSSToLengthConversionData csstoLengthConversionDataWithTextZoomFactor(StyleResolver&);
189};
190
191inline Length StyleBuilderConverter::convertLength(const StyleResolver& styleResolver, const CSSValue& value)
192{
193 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
194 CSSToLengthConversionData conversionData = styleResolver.useSVGZoomRulesForLength() ?
195 styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f)
196 : styleResolver.state().cssToLengthConversionData();
197
198 if (primitiveValue.isLength()) {
199 Length length = primitiveValue.computeLength<Length>(conversionData);
200 length.setHasQuirk(primitiveValue.isQuirkValue());
201 return length;
202 }
203
204 if (primitiveValue.isPercentage())
205 return Length(primitiveValue.doubleValue(), Percent);
206
207 if (primitiveValue.isCalculatedPercentageWithLength())
208 return Length(primitiveValue.cssCalcValue()->createCalculationValue(conversionData));
209
210 ASSERT_NOT_REACHED();
211 return Length(0, Fixed);
212}
213
214inline Length StyleBuilderConverter::convertLengthOrAuto(const StyleResolver& styleResolver, const CSSValue& value)
215{
216 if (downcast<CSSPrimitiveValue>(value).valueID() == CSSValueAuto)
217 return Length(Auto);
218 return convertLength(styleResolver, value);
219}
220
221inline Length StyleBuilderConverter::convertLengthSizing(const StyleResolver& styleResolver, const CSSValue& value)
222{
223 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
224 switch (primitiveValue.valueID()) {
225 case CSSValueInvalid:
226 return convertLength(styleResolver, value);
227 case CSSValueIntrinsic:
228 return Length(Intrinsic);
229 case CSSValueMinIntrinsic:
230 return Length(MinIntrinsic);
231 case CSSValueMinContent:
232 case CSSValueWebkitMinContent:
233 return Length(MinContent);
234 case CSSValueMaxContent:
235 case CSSValueWebkitMaxContent:
236 return Length(MaxContent);
237 case CSSValueWebkitFillAvailable:
238 return Length(FillAvailable);
239 case CSSValueFitContent:
240 case CSSValueWebkitFitContent:
241 return Length(FitContent);
242 case CSSValueAuto:
243 return Length(Auto);
244 default:
245 ASSERT_NOT_REACHED();
246 return Length();
247 }
248}
249
250inline Length StyleBuilderConverter::convertLengthMaxSizing(const StyleResolver& styleResolver, const CSSValue& value)
251{
252 if (downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone)
253 return Length(Undefined);
254 return convertLengthSizing(styleResolver, value);
255}
256
257template<typename T>
258inline T StyleBuilderConverter::convertComputedLength(StyleResolver& styleResolver, const CSSValue& value)
259{
260 return downcast<CSSPrimitiveValue>(value).computeLength<T>(styleResolver.state().cssToLengthConversionData());
261}
262
263template<typename T>
264inline T StyleBuilderConverter::convertLineWidth(StyleResolver& styleResolver, const CSSValue& value)
265{
266 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
267 switch (primitiveValue.valueID()) {
268 case CSSValueThin:
269 return 1;
270 case CSSValueMedium:
271 return 3;
272 case CSSValueThick:
273 return 5;
274 case CSSValueInvalid: {
275 // Any original result that was >= 1 should not be allowed to fall below 1.
276 // This keeps border lines from vanishing.
277 T result = convertComputedLength<T>(styleResolver, value);
278 if (styleResolver.state().style()->effectiveZoom() < 1.0f && result < 1.0) {
279 T originalLength = primitiveValue.computeLength<T>(styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0));
280 if (originalLength >= 1.0)
281 return 1;
282 }
283 float minimumLineWidth = 1 / styleResolver.document().deviceScaleFactor();
284 if (result > 0 && result < minimumLineWidth)
285 return minimumLineWidth;
286 return floorToDevicePixel(result, styleResolver.document().deviceScaleFactor());
287 }
288 default:
289 ASSERT_NOT_REACHED();
290 return 0;
291 }
292}
293
294inline float StyleBuilderConverter::convertSpacing(StyleResolver& styleResolver, const CSSValue& value)
295{
296 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
297 if (primitiveValue.valueID() == CSSValueNormal)
298 return 0.f;
299
300 CSSToLengthConversionData conversionData = styleResolver.useSVGZoomRulesForLength() ?
301 styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f)
302 : styleResolver.state().cssToLengthConversionData();
303 return primitiveValue.computeLength<float>(conversionData);
304}
305
306inline Length StyleBuilderConverter::convertToRadiusLength(CSSToLengthConversionData& conversionData, const CSSPrimitiveValue& value)
307{
308 if (value.isPercentage())
309 return Length(value.doubleValue(), Percent);
310 if (value.isCalculatedPercentageWithLength())
311 return Length(value.cssCalcValue()->createCalculationValue(conversionData));
312 return value.computeLength<Length>(conversionData);
313}
314
315inline LengthSize StyleBuilderConverter::convertRadius(StyleResolver& styleResolver, const CSSValue& value)
316{
317 auto* pair = downcast<CSSPrimitiveValue>(value).pairValue();
318 if (!pair || !pair->first() || !pair->second())
319 return { { 0, Fixed }, { 0, Fixed } };
320
321 CSSToLengthConversionData conversionData = styleResolver.state().cssToLengthConversionData();
322 LengthSize radius { convertToRadiusLength(conversionData, *pair->first()), convertToRadiusLength(conversionData, *pair->second()) };
323
324 ASSERT(!radius.width.isNegative());
325 ASSERT(!radius.height.isNegative());
326 if (radius.width.isZero() || radius.height.isZero())
327 return { { 0, Fixed }, { 0, Fixed } };
328
329 return radius;
330}
331
332inline Length StyleBuilderConverter::convertTo100PercentMinusLength(const Length& length)
333{
334 if (length.isPercent())
335 return Length(100 - length.value(), Percent);
336
337 // Turn this into a calc expression: calc(100% - length)
338 Vector<std::unique_ptr<CalcExpressionNode>> lengths;
339 lengths.reserveInitialCapacity(2);
340 lengths.uncheckedAppend(std::make_unique<CalcExpressionLength>(Length(100, Percent)));
341 lengths.uncheckedAppend(std::make_unique<CalcExpressionLength>(length));
342 auto op = std::make_unique<CalcExpressionOperation>(WTFMove(lengths), CalcOperator::Subtract);
343 return Length(CalculationValue::create(WTFMove(op), ValueRangeAll));
344}
345
346inline Length StyleBuilderConverter::convertPositionComponentX(StyleResolver& styleResolver, const CSSValue& value)
347{
348 return convertPositionComponent<CSSValueLeft, CSSValueRight>(styleResolver, downcast<CSSPrimitiveValue>(value));
349}
350
351inline Length StyleBuilderConverter::convertPositionComponentY(StyleResolver& styleResolver, const CSSValue& value)
352{
353 return convertPositionComponent<CSSValueTop, CSSValueBottom>(styleResolver, downcast<CSSPrimitiveValue>(value));
354}
355
356template<CSSValueID cssValueFor0, CSSValueID cssValueFor100>
357inline Length StyleBuilderConverter::convertPositionComponent(StyleResolver& styleResolver, const CSSPrimitiveValue& value)
358{
359 Length length;
360
361 auto* lengthValue = &value;
362 bool relativeToTrailingEdge = false;
363
364 if (value.isPair()) {
365 auto& first = *value.pairValue()->first();
366 if (first.valueID() == CSSValueRight || first.valueID() == CSSValueBottom)
367 relativeToTrailingEdge = true;
368 lengthValue = value.pairValue()->second();
369 }
370
371 if (value.isValueID()) {
372 switch (value.valueID()) {
373 case cssValueFor0:
374 return Length(0, Percent);
375 case cssValueFor100:
376 return Length(100, Percent);
377 case CSSValueCenter:
378 return Length(50, Percent);
379 default:
380 ASSERT_NOT_REACHED();
381 }
382 }
383
384 length = convertLength(styleResolver, *lengthValue);
385
386 if (relativeToTrailingEdge)
387 length = convertTo100PercentMinusLength(length);
388
389 return length;
390}
391
392inline LengthPoint StyleBuilderConverter::convertObjectPosition(StyleResolver& styleResolver, const CSSValue& value)
393{
394 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
395 Pair* pair = primitiveValue.pairValue();
396 if (!pair || !pair->first() || !pair->second())
397 return RenderStyle::initialObjectPosition();
398
399 Length lengthX = convertPositionComponent<CSSValueLeft, CSSValueRight>(styleResolver, *pair->first());
400 Length lengthY = convertPositionComponent<CSSValueTop, CSSValueBottom>(styleResolver, *pair->second());
401
402 return LengthPoint(lengthX, lengthY);
403}
404
405inline OptionSet<TextDecoration> StyleBuilderConverter::convertTextDecoration(StyleResolver&, const CSSValue& value)
406{
407 auto result = RenderStyle::initialTextDecoration();
408 if (is<CSSValueList>(value)) {
409 for (auto& currentValue : downcast<CSSValueList>(value))
410 result.add(downcast<CSSPrimitiveValue>(currentValue.get()));
411 }
412 return result;
413}
414
415template<typename T>
416inline T StyleBuilderConverter::convertNumber(StyleResolver&, const CSSValue& value)
417{
418 return downcast<CSSPrimitiveValue>(value).value<T>(CSSPrimitiveValue::CSS_NUMBER);
419}
420
421template<typename T>
422inline T StyleBuilderConverter::convertNumberOrAuto(StyleResolver& styleResolver, const CSSValue& value)
423{
424 if (downcast<CSSPrimitiveValue>(value).valueID() == CSSValueAuto)
425 return -1;
426 return convertNumber<T>(styleResolver, value);
427}
428
429inline short StyleBuilderConverter::convertWebkitHyphenateLimitLines(StyleResolver&, const CSSValue& value)
430{
431 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
432 if (primitiveValue.valueID() == CSSValueNoLimit)
433 return -1;
434 return primitiveValue.value<short>(CSSPrimitiveValue::CSS_NUMBER);
435}
436
437template<CSSPropertyID property>
438inline NinePieceImage StyleBuilderConverter::convertBorderImage(StyleResolver& styleResolver, CSSValue& value)
439{
440 NinePieceImage image;
441 styleResolver.styleMap()->mapNinePieceImage(property, &value, image);
442 return image;
443}
444
445template<CSSPropertyID property>
446inline NinePieceImage StyleBuilderConverter::convertBorderMask(StyleResolver& styleResolver, CSSValue& value)
447{
448 NinePieceImage image;
449 image.setMaskDefaults();
450 styleResolver.styleMap()->mapNinePieceImage(property, &value, image);
451 return image;
452}
453
454template<CSSPropertyID>
455inline RefPtr<StyleImage> StyleBuilderConverter::convertStyleImage(StyleResolver& styleResolver, CSSValue& value)
456{
457 return styleResolver.styleImage(value);
458}
459
460inline TransformOperations StyleBuilderConverter::convertTransform(StyleResolver& styleResolver, const CSSValue& value)
461{
462 TransformOperations operations;
463 transformsForValue(value, styleResolver.state().cssToLengthConversionData(), operations);
464 return operations;
465}
466
467#if ENABLE(DARK_MODE_CSS)
468inline void StyleBuilderConverter::updateColorScheme(const CSSPrimitiveValue& primitiveValue, StyleColorScheme& colorScheme)
469{
470 ASSERT(primitiveValue.isValueID());
471
472 switch (primitiveValue.valueID()) {
473 case CSSValueAuto:
474 colorScheme = StyleColorScheme();
475 break;
476 case CSSValueOnly:
477 colorScheme.setAllowsTransformations(false);
478 break;
479 case CSSValueLight:
480 colorScheme.add(ColorScheme::Light);
481 break;
482 case CSSValueDark:
483 colorScheme.add(ColorScheme::Dark);
484 break;
485 default:
486 // Unknown identifiers are allowed and ignored.
487 break;
488 }
489}
490
491inline StyleColorScheme StyleBuilderConverter::convertColorScheme(StyleResolver&, const CSSValue& value)
492{
493 StyleColorScheme colorScheme;
494
495 if (is<CSSValueList>(value)) {
496 for (auto& currentValue : downcast<CSSValueList>(value))
497 updateColorScheme(downcast<CSSPrimitiveValue>(currentValue.get()), colorScheme);
498 } else if (is<CSSPrimitiveValue>(value))
499 updateColorScheme(downcast<CSSPrimitiveValue>(value), colorScheme);
500
501 // If the value was just "only", that is synonymous for "only light".
502 if (colorScheme.isOnly())
503 colorScheme.add(ColorScheme::Light);
504
505 return colorScheme;
506}
507#endif
508
509inline String StyleBuilderConverter::convertString(StyleResolver&, const CSSValue& value)
510{
511 return downcast<CSSPrimitiveValue>(value).stringValue();
512}
513
514inline String StyleBuilderConverter::convertStringOrAuto(StyleResolver& styleResolver, const CSSValue& value)
515{
516 if (downcast<CSSPrimitiveValue>(value).valueID() == CSSValueAuto)
517 return nullAtom();
518 return convertString(styleResolver, value);
519}
520
521inline String StyleBuilderConverter::convertStringOrNone(StyleResolver& styleResolver, const CSSValue& value)
522{
523 if (downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone)
524 return nullAtom();
525 return convertString(styleResolver, value);
526}
527
528inline OptionSet<TextEmphasisPosition> StyleBuilderConverter::valueToEmphasisPosition(const CSSPrimitiveValue& primitiveValue)
529{
530 ASSERT(primitiveValue.isValueID());
531
532 switch (primitiveValue.valueID()) {
533 case CSSValueOver:
534 return TextEmphasisPosition::Over;
535 case CSSValueUnder:
536 return TextEmphasisPosition::Under;
537 case CSSValueLeft:
538 return TextEmphasisPosition::Left;
539 case CSSValueRight:
540 return TextEmphasisPosition::Right;
541 default:
542 break;
543 }
544
545 ASSERT_NOT_REACHED();
546 return RenderStyle::initialTextEmphasisPosition();
547}
548
549inline OptionSet<TextEmphasisPosition> StyleBuilderConverter::convertTextEmphasisPosition(StyleResolver&, const CSSValue& value)
550{
551 if (is<CSSPrimitiveValue>(value))
552 return valueToEmphasisPosition(downcast<CSSPrimitiveValue>(value));
553
554 OptionSet<TextEmphasisPosition> position;
555 for (auto& currentValue : downcast<CSSValueList>(value))
556 position.add(valueToEmphasisPosition(downcast<CSSPrimitiveValue>(currentValue.get())));
557 return position;
558}
559
560inline TextAlignMode StyleBuilderConverter::convertTextAlign(StyleResolver& styleResolver, const CSSValue& value)
561{
562 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
563 ASSERT(primitiveValue.isValueID());
564
565 if (primitiveValue.valueID() != CSSValueWebkitMatchParent)
566 return primitiveValue;
567
568 auto* parentStyle = styleResolver.parentStyle();
569 if (parentStyle->textAlign() == TextAlignMode::Start)
570 return parentStyle->isLeftToRightDirection() ? TextAlignMode::Left : TextAlignMode::Right;
571 if (parentStyle->textAlign() == TextAlignMode::End)
572 return parentStyle->isLeftToRightDirection() ? TextAlignMode::Right : TextAlignMode::Left;
573 return parentStyle->textAlign();
574}
575
576inline RefPtr<ClipPathOperation> StyleBuilderConverter::convertClipPath(StyleResolver& styleResolver, const CSSValue& value)
577{
578 if (is<CSSPrimitiveValue>(value)) {
579 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
580 if (primitiveValue.primitiveType() == CSSPrimitiveValue::CSS_URI) {
581 String cssURLValue = primitiveValue.stringValue();
582 URL url = styleResolver.document().completeURL(cssURLValue);
583 // FIXME: It doesn't work with external SVG references (see https://bugs.webkit.org/show_bug.cgi?id=126133)
584 return ReferenceClipPathOperation::create(cssURLValue, url.fragmentIdentifier());
585 }
586 ASSERT(primitiveValue.valueID() == CSSValueNone);
587 return nullptr;
588 }
589
590 CSSBoxType referenceBox = CSSBoxType::BoxMissing;
591 RefPtr<ClipPathOperation> operation;
592
593 for (auto& currentValue : downcast<CSSValueList>(value)) {
594 auto& primitiveValue = downcast<CSSPrimitiveValue>(currentValue.get());
595 if (primitiveValue.isShape()) {
596 ASSERT(!operation);
597 operation = ShapeClipPathOperation::create(basicShapeForValue(styleResolver.state().cssToLengthConversionData(), *primitiveValue.shapeValue()));
598 } else {
599 ASSERT(primitiveValue.valueID() == CSSValueContentBox
600 || primitiveValue.valueID() == CSSValueBorderBox
601 || primitiveValue.valueID() == CSSValuePaddingBox
602 || primitiveValue.valueID() == CSSValueMarginBox
603 || primitiveValue.valueID() == CSSValueFillBox
604 || primitiveValue.valueID() == CSSValueStrokeBox
605 || primitiveValue.valueID() == CSSValueViewBox);
606 ASSERT(referenceBox == CSSBoxType::BoxMissing);
607 referenceBox = primitiveValue;
608 }
609 }
610 if (operation)
611 downcast<ShapeClipPathOperation>(*operation).setReferenceBox(referenceBox);
612 else {
613 ASSERT(referenceBox != CSSBoxType::BoxMissing);
614 operation = BoxClipPathOperation::create(referenceBox);
615 }
616
617 return operation;
618}
619
620inline Resize StyleBuilderConverter::convertResize(StyleResolver& styleResolver, const CSSValue& value)
621{
622 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
623
624 Resize resize = Resize::None;
625 if (primitiveValue.valueID() == CSSValueAuto)
626 resize = styleResolver.settings().textAreasAreResizable() ? Resize::Both : Resize::None;
627 else
628 resize = primitiveValue;
629
630 return resize;
631}
632
633inline int StyleBuilderConverter::convertMarqueeRepetition(StyleResolver&, const CSSValue& value)
634{
635 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
636 if (primitiveValue.valueID() == CSSValueInfinite)
637 return -1; // -1 means repeat forever.
638
639 ASSERT(primitiveValue.isNumber());
640 return primitiveValue.intValue();
641}
642
643inline int StyleBuilderConverter::convertMarqueeSpeed(StyleResolver&, const CSSValue& value)
644{
645 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
646 int speed = 85;
647 if (CSSValueID ident = primitiveValue.valueID()) {
648 switch (ident) {
649 case CSSValueSlow:
650 speed = 500; // 500 msec.
651 break;
652 case CSSValueNormal:
653 speed = 85; // 85msec. The WinIE default.
654 break;
655 case CSSValueFast:
656 speed = 10; // 10msec. Super fast.
657 break;
658 default:
659 ASSERT_NOT_REACHED();
660 break;
661 }
662 } else if (primitiveValue.isTime())
663 speed = primitiveValue.computeTime<int, CSSPrimitiveValue::Milliseconds>();
664 else {
665 // For scrollamount support.
666 ASSERT(primitiveValue.isNumber());
667 speed = primitiveValue.intValue();
668 }
669 return speed;
670}
671
672inline Ref<QuotesData> StyleBuilderConverter::convertQuotes(StyleResolver&, const CSSValue& value)
673{
674 if (is<CSSPrimitiveValue>(value)) {
675 ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone);
676 return QuotesData::create(Vector<std::pair<String, String>>());
677 }
678
679 auto& list = downcast<CSSValueList>(value);
680 Vector<std::pair<String, String>> quotes;
681 quotes.reserveInitialCapacity(list.length() / 2);
682 for (unsigned i = 0; i < list.length(); i += 2) {
683 const CSSValue* first = list.itemWithoutBoundsCheck(i);
684 // item() returns null if out of bounds so this is safe.
685 const CSSValue* second = list.item(i + 1);
686 if (!second)
687 break;
688 String startQuote = downcast<CSSPrimitiveValue>(*first).stringValue();
689 String endQuote = downcast<CSSPrimitiveValue>(*second).stringValue();
690 quotes.append(std::make_pair(startQuote, endQuote));
691 }
692 return QuotesData::create(quotes);
693}
694
695inline TextUnderlinePosition StyleBuilderConverter::convertTextUnderlinePosition(StyleResolver&, const CSSValue& value)
696{
697 ASSERT(is<CSSPrimitiveValue>(value));
698 return downcast<CSSPrimitiveValue>(value);
699}
700
701inline TextUnderlineOffset StyleBuilderConverter::convertTextUnderlineOffset(StyleResolver& styleResolver, const CSSValue& value)
702{
703 ASSERT(is<CSSPrimitiveValue>(value));
704 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
705 switch (primitiveValue.valueID()) {
706 case CSSValueAuto:
707 return TextUnderlineOffset::createWithAuto();
708 default:
709 ASSERT(primitiveValue.isLength());
710 auto computedLength = convertComputedLength<float>(styleResolver, primitiveValue);
711 return TextUnderlineOffset::createWithLength(computedLength);
712 }
713}
714
715inline TextDecorationThickness StyleBuilderConverter::convertTextDecorationThickness(StyleResolver& styleResolver, const CSSValue& value)
716{
717 ASSERT(is<CSSPrimitiveValue>(value));
718 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
719 switch (primitiveValue.valueID()) {
720 case CSSValueAuto:
721 return TextDecorationThickness::createWithAuto();
722 case CSSValueFromFont:
723 return TextDecorationThickness::createFromFont();
724 default:
725 ASSERT(primitiveValue.isLength());
726 auto computedLength = convertComputedLength<float>(styleResolver, primitiveValue);
727 return TextDecorationThickness::createWithLength(computedLength);
728 }
729}
730
731inline RefPtr<StyleReflection> StyleBuilderConverter::convertReflection(StyleResolver& styleResolver, const CSSValue& value)
732{
733 if (is<CSSPrimitiveValue>(value)) {
734 ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone);
735 return nullptr;
736 }
737
738 auto& reflectValue = downcast<CSSReflectValue>(value);
739
740 auto reflection = StyleReflection::create();
741 reflection->setDirection(reflectValue.direction());
742 reflection->setOffset(reflectValue.offset().convertToLength<FixedIntegerConversion | PercentConversion | CalculatedConversion>(styleResolver.state().cssToLengthConversionData()));
743
744 NinePieceImage mask;
745 mask.setMaskDefaults();
746 styleResolver.styleMap()->mapNinePieceImage(CSSPropertyWebkitBoxReflect, reflectValue.mask(), mask);
747 reflection->setMask(mask);
748
749 return reflection;
750}
751
752inline IntSize StyleBuilderConverter::convertInitialLetter(StyleResolver&, const CSSValue& value)
753{
754 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
755
756 if (primitiveValue.valueID() == CSSValueNormal)
757 return IntSize();
758
759 Pair* pair = primitiveValue.pairValue();
760 ASSERT(pair);
761 ASSERT(pair->first());
762 ASSERT(pair->second());
763
764 return IntSize(pair->first()->intValue(), pair->second()->intValue());
765}
766
767inline float StyleBuilderConverter::convertTextStrokeWidth(StyleResolver& styleResolver, const CSSValue& value)
768{
769 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
770
771 float width = 0;
772 switch (primitiveValue.valueID()) {
773 case CSSValueThin:
774 case CSSValueMedium:
775 case CSSValueThick: {
776 double result = 1.0 / 48;
777 if (primitiveValue.valueID() == CSSValueMedium)
778 result *= 3;
779 else if (primitiveValue.valueID() == CSSValueThick)
780 result *= 5;
781 Ref<CSSPrimitiveValue> emsValue(CSSPrimitiveValue::create(result, CSSPrimitiveValue::CSS_EMS));
782 width = convertComputedLength<float>(styleResolver, emsValue);
783 break;
784 }
785 case CSSValueInvalid: {
786 width = convertComputedLength<float>(styleResolver, primitiveValue);
787 break;
788 }
789 default:
790 ASSERT_NOT_REACHED();
791 return 0;
792 }
793
794 return width;
795}
796
797inline LineBoxContain StyleBuilderConverter::convertLineBoxContain(StyleResolver&, const CSSValue& value)
798{
799 if (is<CSSPrimitiveValue>(value)) {
800 ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone);
801 return LineBoxContainNone;
802 }
803
804 return downcast<CSSLineBoxContainValue>(value).value();
805}
806
807inline OptionSet<TextDecorationSkip> StyleBuilderConverter::valueToDecorationSkip(const CSSPrimitiveValue& primitiveValue)
808{
809 ASSERT(primitiveValue.isValueID());
810
811 switch (primitiveValue.valueID()) {
812 case CSSValueAuto:
813 return TextDecorationSkip::Auto;
814 case CSSValueNone:
815 return OptionSet<TextDecorationSkip> { };
816 case CSSValueInk:
817 return TextDecorationSkip::Ink;
818 case CSSValueObjects:
819 return TextDecorationSkip::Objects;
820 default:
821 break;
822 }
823
824 ASSERT_NOT_REACHED();
825 return OptionSet<TextDecorationSkip> { };
826}
827
828inline OptionSet<TextDecorationSkip> StyleBuilderConverter::convertTextDecorationSkip(StyleResolver&, const CSSValue& value)
829{
830 if (is<CSSPrimitiveValue>(value))
831 return valueToDecorationSkip(downcast<CSSPrimitiveValue>(value));
832
833 OptionSet<TextDecorationSkip> skip;
834 for (auto& currentValue : downcast<CSSValueList>(value))
835 skip.add(valueToDecorationSkip(downcast<CSSPrimitiveValue>(currentValue.get())));
836 return skip;
837}
838
839static inline bool isImageShape(const CSSValue& value)
840{
841 return is<CSSImageValue>(value) || is<CSSImageSetValue>(value) || is<CSSImageGeneratorValue>(value);
842}
843
844inline RefPtr<ShapeValue> StyleBuilderConverter::convertShapeValue(StyleResolver& styleResolver, CSSValue& value)
845{
846 if (is<CSSPrimitiveValue>(value)) {
847 ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone);
848 return nullptr;
849 }
850
851 if (isImageShape(value))
852 return ShapeValue::create(styleResolver.styleImage(value).releaseNonNull());
853
854 RefPtr<BasicShape> shape;
855 CSSBoxType referenceBox = CSSBoxType::BoxMissing;
856 for (auto& currentValue : downcast<CSSValueList>(value)) {
857 auto& primitiveValue = downcast<CSSPrimitiveValue>(currentValue.get());
858 if (primitiveValue.isShape())
859 shape = basicShapeForValue(styleResolver.state().cssToLengthConversionData(), *primitiveValue.shapeValue());
860 else if (primitiveValue.valueID() == CSSValueContentBox
861 || primitiveValue.valueID() == CSSValueBorderBox
862 || primitiveValue.valueID() == CSSValuePaddingBox
863 || primitiveValue.valueID() == CSSValueMarginBox)
864 referenceBox = primitiveValue;
865 else {
866 ASSERT_NOT_REACHED();
867 return nullptr;
868 }
869 }
870
871 if (shape)
872 return ShapeValue::create(shape.releaseNonNull(), referenceBox);
873
874 if (referenceBox != CSSBoxType::BoxMissing)
875 return ShapeValue::create(referenceBox);
876
877 ASSERT_NOT_REACHED();
878 return nullptr;
879}
880
881#if ENABLE(CSS_SCROLL_SNAP)
882
883inline ScrollSnapType StyleBuilderConverter::convertScrollSnapType(StyleResolver&, const CSSValue& value)
884{
885 ScrollSnapType type;
886 auto& values = downcast<CSSValueList>(value);
887 auto& firstValue = downcast<CSSPrimitiveValue>(*values.item(0));
888 if (values.length() == 2) {
889 type.axis = firstValue;
890 type.strictness = downcast<CSSPrimitiveValue>(*values.item(1));
891 return type;
892 }
893
894 switch (firstValue.valueID()) {
895 case CSSValueNone:
896 case CSSValueMandatory:
897 case CSSValueProximity:
898 type.strictness = firstValue;
899 break;
900 default:
901 type.axis = firstValue;
902 type.strictness = ScrollSnapStrictness::Proximity;
903 break;
904 }
905 return type;
906}
907
908inline ScrollSnapAlign StyleBuilderConverter::convertScrollSnapAlign(StyleResolver&, const CSSValue& value)
909{
910 auto& values = downcast<CSSValueList>(value);
911 ScrollSnapAlign alignment;
912 alignment.x = downcast<CSSPrimitiveValue>(*values.item(0));
913 if (values.length() == 1)
914 alignment.y = alignment.x;
915 else
916 alignment.y = downcast<CSSPrimitiveValue>(*values.item(1));
917 return alignment;
918}
919
920#endif
921
922inline GridLength StyleBuilderConverter::createGridTrackBreadth(const CSSPrimitiveValue& primitiveValue, StyleResolver& styleResolver)
923{
924 if (primitiveValue.valueID() == CSSValueMinContent || primitiveValue.valueID() == CSSValueWebkitMinContent)
925 return Length(MinContent);
926
927 if (primitiveValue.valueID() == CSSValueMaxContent || primitiveValue.valueID() == CSSValueWebkitMaxContent)
928 return Length(MaxContent);
929
930 // Fractional unit.
931 if (primitiveValue.isFlex())
932 return GridLength(primitiveValue.doubleValue());
933
934 return primitiveValue.convertToLength<FixedIntegerConversion | PercentConversion | CalculatedConversion | AutoConversion>(styleResolver.state().cssToLengthConversionData());
935}
936
937inline GridTrackSize StyleBuilderConverter::createGridTrackSize(const CSSValue& value, StyleResolver& styleResolver)
938{
939 if (is<CSSPrimitiveValue>(value))
940 return GridTrackSize(createGridTrackBreadth(downcast<CSSPrimitiveValue>(value), styleResolver));
941
942 ASSERT(is<CSSFunctionValue>(value));
943 const auto& function = downcast<CSSFunctionValue>(value);
944
945 if (function.length() == 1)
946 return GridTrackSize(createGridTrackBreadth(downcast<CSSPrimitiveValue>(*function.itemWithoutBoundsCheck(0)), styleResolver), FitContentTrackSizing);
947
948 ASSERT_WITH_SECURITY_IMPLICATION(function.length() == 2);
949 GridLength minTrackBreadth(createGridTrackBreadth(downcast<CSSPrimitiveValue>(*function.itemWithoutBoundsCheck(0)), styleResolver));
950 GridLength maxTrackBreadth(createGridTrackBreadth(downcast<CSSPrimitiveValue>(*function.itemWithoutBoundsCheck(1)), styleResolver));
951 return GridTrackSize(minTrackBreadth, maxTrackBreadth);
952}
953
954static void createGridLineNamesList(const CSSValue& value, unsigned currentNamedGridLine, NamedGridLinesMap& namedGridLines, OrderedNamedGridLinesMap& orderedNamedGridLines)
955{
956 ASSERT(value.isGridLineNamesValue());
957
958 for (auto& namedGridLineValue : downcast<CSSGridLineNamesValue>(value)) {
959 String namedGridLine = downcast<CSSPrimitiveValue>(namedGridLineValue.get()).stringValue();
960 auto result = namedGridLines.add(namedGridLine, Vector<unsigned>());
961 result.iterator->value.append(currentNamedGridLine);
962 auto orderedResult = orderedNamedGridLines.add(currentNamedGridLine, Vector<String>());
963 orderedResult.iterator->value.append(namedGridLine);
964 }
965}
966
967struct StyleBuilderConverter::TracksData {
968 WTF_MAKE_NONCOPYABLE(TracksData); WTF_MAKE_FAST_ALLOCATED;
969public:
970 TracksData() = default;
971
972 Vector<GridTrackSize> m_trackSizes;
973 NamedGridLinesMap m_namedGridLines;
974 OrderedNamedGridLinesMap m_orderedNamedGridLines;
975 Vector<GridTrackSize> m_autoRepeatTrackSizes;
976 NamedGridLinesMap m_autoRepeatNamedGridLines;
977 OrderedNamedGridLinesMap m_autoRepeatOrderedNamedGridLines;
978 unsigned m_autoRepeatInsertionPoint { RenderStyle::initialGridAutoRepeatInsertionPoint() };
979 AutoRepeatType m_autoRepeatType { RenderStyle::initialGridAutoRepeatType() };
980};
981
982inline bool StyleBuilderConverter::createGridTrackList(const CSSValue& value, TracksData& tracksData, StyleResolver& styleResolver)
983{
984 // Handle 'none'.
985 if (is<CSSPrimitiveValue>(value))
986 return downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone;
987
988 if (!is<CSSValueList>(value))
989 return false;
990
991 unsigned currentNamedGridLine = 0;
992 for (auto& currentValue : downcast<CSSValueList>(value)) {
993 if (is<CSSGridLineNamesValue>(currentValue)) {
994 createGridLineNamesList(currentValue.get(), currentNamedGridLine, tracksData.m_namedGridLines, tracksData.m_orderedNamedGridLines);
995 continue;
996 }
997
998 if (is<CSSGridAutoRepeatValue>(currentValue)) {
999 ASSERT(tracksData.m_autoRepeatTrackSizes.isEmpty());
1000 unsigned autoRepeatIndex = 0;
1001 CSSValueID autoRepeatID = downcast<CSSGridAutoRepeatValue>(currentValue.get()).autoRepeatID();
1002 ASSERT(autoRepeatID == CSSValueAutoFill || autoRepeatID == CSSValueAutoFit);
1003 tracksData.m_autoRepeatType = autoRepeatID == CSSValueAutoFill ? AutoRepeatType::Fill : AutoRepeatType::Fit;
1004 for (auto& autoRepeatValue : downcast<CSSValueList>(currentValue.get())) {
1005 if (is<CSSGridLineNamesValue>(autoRepeatValue)) {
1006 createGridLineNamesList(autoRepeatValue.get(), autoRepeatIndex, tracksData.m_autoRepeatNamedGridLines, tracksData.m_autoRepeatOrderedNamedGridLines);
1007 continue;
1008 }
1009 ++autoRepeatIndex;
1010 tracksData.m_autoRepeatTrackSizes.append(createGridTrackSize(autoRepeatValue.get(), styleResolver));
1011 }
1012 tracksData.m_autoRepeatInsertionPoint = currentNamedGridLine++;
1013 continue;
1014 }
1015
1016 ++currentNamedGridLine;
1017 tracksData.m_trackSizes.append(createGridTrackSize(currentValue, styleResolver));
1018 }
1019
1020 // The parser should have rejected any <track-list> without any <track-size> as
1021 // this is not conformant to the syntax.
1022 ASSERT(!tracksData.m_trackSizes.isEmpty() || !tracksData.m_autoRepeatTrackSizes.isEmpty());
1023 return true;
1024}
1025
1026inline bool StyleBuilderConverter::createGridPosition(const CSSValue& value, GridPosition& position)
1027{
1028 // We accept the specification's grammar:
1029 // auto | <custom-ident> | [ <integer> && <custom-ident>? ] | [ span && [ <integer> || <custom-ident> ] ]
1030 if (is<CSSPrimitiveValue>(value)) {
1031 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1032 // We translate <ident> to <string> during parsing as it makes handling it simpler.
1033 if (primitiveValue.isString()) {
1034 position.setNamedGridArea(primitiveValue.stringValue());
1035 return true;
1036 }
1037
1038 ASSERT(primitiveValue.valueID() == CSSValueAuto);
1039 return true;
1040 }
1041
1042 auto& values = downcast<CSSValueList>(value);
1043 ASSERT(values.length());
1044
1045 auto it = values.begin();
1046 const CSSPrimitiveValue* currentValue = &downcast<CSSPrimitiveValue>(it->get());
1047 bool isSpanPosition = false;
1048 if (currentValue->valueID() == CSSValueSpan) {
1049 isSpanPosition = true;
1050 ++it;
1051 currentValue = it != values.end() ? &downcast<CSSPrimitiveValue>(it->get()) : nullptr;
1052 }
1053
1054 int gridLineNumber = 0;
1055 if (currentValue && currentValue->isNumber()) {
1056 gridLineNumber = currentValue->intValue();
1057 ++it;
1058 currentValue = it != values.end() ? &downcast<CSSPrimitiveValue>(it->get()) : nullptr;
1059 }
1060
1061 String gridLineName;
1062 if (currentValue && currentValue->isString()) {
1063 gridLineName = currentValue->stringValue();
1064 ++it;
1065 }
1066
1067 ASSERT(it == values.end());
1068 if (isSpanPosition)
1069 position.setSpanPosition(gridLineNumber ? gridLineNumber : 1, gridLineName);
1070 else
1071 position.setExplicitPosition(gridLineNumber, gridLineName);
1072
1073 return true;
1074}
1075
1076inline void StyleBuilderConverter::createImplicitNamedGridLinesFromGridArea(const NamedGridAreaMap& namedGridAreas, NamedGridLinesMap& namedGridLines, GridTrackSizingDirection direction)
1077{
1078 for (auto& area : namedGridAreas) {
1079 GridSpan areaSpan = direction == ForRows ? area.value.rows : area.value.columns;
1080 {
1081 auto& startVector = namedGridLines.add(area.key + "-start", Vector<unsigned>()).iterator->value;
1082 startVector.append(areaSpan.startLine());
1083 std::sort(startVector.begin(), startVector.end());
1084 }
1085 {
1086 auto& endVector = namedGridLines.add(area.key + "-end", Vector<unsigned>()).iterator->value;
1087 endVector.append(areaSpan.endLine());
1088 std::sort(endVector.begin(), endVector.end());
1089 }
1090 }
1091}
1092
1093inline Vector<GridTrackSize> StyleBuilderConverter::convertGridTrackSizeList(StyleResolver& styleResolver, const CSSValue& value)
1094{
1095 ASSERT(value.isValueList());
1096 auto& valueList = downcast<CSSValueList>(value);
1097 Vector<GridTrackSize> trackSizes;
1098 trackSizes.reserveInitialCapacity(valueList.length());
1099 for (auto& currValue : valueList) {
1100 ASSERT(!currValue->isGridLineNamesValue());
1101 ASSERT(!currValue->isGridAutoRepeatValue());
1102 trackSizes.uncheckedAppend(convertGridTrackSize(styleResolver, currValue));
1103 }
1104 return trackSizes;
1105}
1106
1107inline GridTrackSize StyleBuilderConverter::convertGridTrackSize(StyleResolver& styleResolver, const CSSValue& value)
1108{
1109 return createGridTrackSize(value, styleResolver);
1110}
1111
1112inline Optional<GridPosition> StyleBuilderConverter::convertGridPosition(StyleResolver&, const CSSValue& value)
1113{
1114 GridPosition gridPosition;
1115 if (createGridPosition(value, gridPosition))
1116 return gridPosition;
1117 return WTF::nullopt;
1118}
1119
1120inline GridAutoFlow StyleBuilderConverter::convertGridAutoFlow(StyleResolver&, const CSSValue& value)
1121{
1122 auto& list = downcast<CSSValueList>(value);
1123 if (!list.length())
1124 return RenderStyle::initialGridAutoFlow();
1125
1126 auto& first = downcast<CSSPrimitiveValue>(*list.item(0));
1127 auto* second = downcast<CSSPrimitiveValue>(list.item(1));
1128
1129 GridAutoFlow autoFlow;
1130 switch (first.valueID()) {
1131 case CSSValueRow:
1132 if (second && second->valueID() == CSSValueDense)
1133 autoFlow = AutoFlowRowDense;
1134 else
1135 autoFlow = AutoFlowRow;
1136 break;
1137 case CSSValueColumn:
1138 if (second && second->valueID() == CSSValueDense)
1139 autoFlow = AutoFlowColumnDense;
1140 else
1141 autoFlow = AutoFlowColumn;
1142 break;
1143 case CSSValueDense:
1144 if (second && second->valueID() == CSSValueColumn)
1145 autoFlow = AutoFlowColumnDense;
1146 else
1147 autoFlow = AutoFlowRowDense;
1148 break;
1149 default:
1150 ASSERT_NOT_REACHED();
1151 autoFlow = RenderStyle::initialGridAutoFlow();
1152 break;
1153 }
1154
1155 return autoFlow;
1156}
1157
1158inline CSSToLengthConversionData StyleBuilderConverter::csstoLengthConversionDataWithTextZoomFactor(StyleResolver& styleResolver)
1159{
1160 if (auto* frame = styleResolver.document().frame()) {
1161 float textZoomFactor = styleResolver.style()->textZoom() != TextZoom::Reset ? frame->textZoomFactor() : 1.0f;
1162 return styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(styleResolver.style()->effectiveZoom() * textZoomFactor);
1163 }
1164 return styleResolver.state().cssToLengthConversionData();
1165}
1166
1167inline Optional<Length> StyleBuilderConverter::convertWordSpacing(StyleResolver& styleResolver, const CSSValue& value)
1168{
1169 Optional<Length> wordSpacing;
1170 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1171 if (primitiveValue.valueID() == CSSValueNormal)
1172 wordSpacing = RenderStyle::initialWordSpacing();
1173 else if (primitiveValue.isLength())
1174 wordSpacing = primitiveValue.computeLength<Length>(csstoLengthConversionDataWithTextZoomFactor(styleResolver));
1175 else if (primitiveValue.isPercentage())
1176 wordSpacing = Length(clampTo<float>(primitiveValue.doubleValue(), minValueForCssLength, maxValueForCssLength), Percent);
1177 else if (primitiveValue.isNumber())
1178 wordSpacing = Length(primitiveValue.doubleValue(), Fixed);
1179
1180 return wordSpacing;
1181}
1182
1183inline Optional<float> StyleBuilderConverter::convertPerspective(StyleResolver& styleResolver, const CSSValue& value)
1184{
1185 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1186 if (primitiveValue.valueID() == CSSValueNone)
1187 return 0.f;
1188
1189 float perspective = -1;
1190 if (primitiveValue.isLength())
1191 perspective = primitiveValue.computeLength<float>(styleResolver.state().cssToLengthConversionData());
1192 else if (primitiveValue.isNumber())
1193 perspective = primitiveValue.doubleValue() * styleResolver.state().cssToLengthConversionData().zoom();
1194 else
1195 ASSERT_NOT_REACHED();
1196
1197 return perspective < 0 ? Optional<float>(WTF::nullopt) : Optional<float>(perspective);
1198}
1199
1200inline Optional<Length> StyleBuilderConverter::convertMarqueeIncrement(StyleResolver& styleResolver, const CSSValue& value)
1201{
1202 Optional<Length> marqueeLength;
1203 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1204 switch (primitiveValue.valueID()) {
1205 case CSSValueSmall:
1206 marqueeLength = Length(1, Fixed); // 1px.
1207 break;
1208 case CSSValueNormal:
1209 marqueeLength = Length(6, Fixed); // 6px. The WinIE default.
1210 break;
1211 case CSSValueLarge:
1212 marqueeLength = Length(36, Fixed); // 36px.
1213 break;
1214 case CSSValueInvalid: {
1215 Length length = primitiveValue.convertToLength<FixedIntegerConversion | PercentConversion | CalculatedConversion>(styleResolver.state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f));
1216 if (!length.isUndefined())
1217 marqueeLength = length;
1218 break;
1219 }
1220 default:
1221 break;
1222 }
1223 return marqueeLength;
1224}
1225
1226inline Optional<FilterOperations> StyleBuilderConverter::convertFilterOperations(StyleResolver& styleResolver, const CSSValue& value)
1227{
1228 FilterOperations operations;
1229 if (styleResolver.createFilterOperations(value, operations))
1230 return operations;
1231 return WTF::nullopt;
1232}
1233
1234inline FontFeatureSettings StyleBuilderConverter::convertFontFeatureSettings(StyleResolver&, const CSSValue& value)
1235{
1236 if (is<CSSPrimitiveValue>(value)) {
1237 ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNormal);
1238 return { };
1239 }
1240
1241 FontFeatureSettings settings;
1242 for (auto& item : downcast<CSSValueList>(value)) {
1243 auto& feature = downcast<CSSFontFeatureValue>(item.get());
1244 settings.insert(FontFeature(feature.tag(), feature.value()));
1245 }
1246 return settings;
1247}
1248
1249inline FontSelectionValue StyleBuilderConverter::convertFontWeightFromValue(const CSSValue& value)
1250{
1251 ASSERT(is<CSSPrimitiveValue>(value));
1252 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1253
1254 if (primitiveValue.isNumber())
1255 return FontSelectionValue::clampFloat(primitiveValue.floatValue());
1256
1257 ASSERT(primitiveValue.isValueID());
1258 switch (primitiveValue.valueID()) {
1259 case CSSValueNormal:
1260 return normalWeightValue();
1261 case CSSValueBold:
1262 case CSSValueBolder:
1263 return boldWeightValue();
1264 case CSSValueLighter:
1265 return lightWeightValue();
1266 default:
1267 ASSERT_NOT_REACHED();
1268 return normalWeightValue();
1269 }
1270}
1271
1272inline FontSelectionValue StyleBuilderConverter::convertFontStretchFromValue(const CSSValue& value)
1273{
1274 ASSERT(is<CSSPrimitiveValue>(value));
1275 const auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1276
1277 if (primitiveValue.isPercentage())
1278 return FontSelectionValue::clampFloat(primitiveValue.floatValue());
1279
1280 ASSERT(primitiveValue.isValueID());
1281 if (auto value = fontStretchValue(primitiveValue.valueID()))
1282 return value.value();
1283 ASSERT_NOT_REACHED();
1284 return normalStretchValue();
1285}
1286
1287// The input value needs to parsed and valid, this function returns WTF::nullopt if the input was "normal".
1288inline Optional<FontSelectionValue> StyleBuilderConverter::convertFontStyleFromValue(const CSSValue& value)
1289{
1290 ASSERT(is<CSSFontStyleValue>(value));
1291 const auto& fontStyleValue = downcast<CSSFontStyleValue>(value);
1292
1293 auto valueID = fontStyleValue.fontStyleValue->valueID();
1294 if (valueID == CSSValueNormal)
1295 return WTF::nullopt;
1296 if (valueID == CSSValueItalic)
1297 return italicValue();
1298 ASSERT(valueID == CSSValueOblique);
1299 if (auto* obliqueValue = fontStyleValue.obliqueValue.get())
1300 return FontSelectionValue(obliqueValue->value<float>(CSSPrimitiveValue::CSS_DEG));
1301 return italicValue();
1302}
1303
1304inline FontSelectionValue StyleBuilderConverter::convertFontWeight(StyleResolver& styleResolver, const CSSValue& value)
1305{
1306 ASSERT(is<CSSPrimitiveValue>(value));
1307 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1308 if (primitiveValue.isValueID()) {
1309 auto valueID = primitiveValue.valueID();
1310 if (valueID == CSSValueBolder)
1311 return FontCascadeDescription::bolderWeight(styleResolver.parentStyle()->fontDescription().weight());
1312 if (valueID == CSSValueLighter)
1313 return FontCascadeDescription::lighterWeight(styleResolver.parentStyle()->fontDescription().weight());
1314 }
1315 return convertFontWeightFromValue(value);
1316}
1317
1318inline FontSelectionValue StyleBuilderConverter::convertFontStretch(StyleResolver&, const CSSValue& value)
1319{
1320 return convertFontStretchFromValue(value);
1321}
1322
1323#if ENABLE(VARIATION_FONTS)
1324inline FontVariationSettings StyleBuilderConverter::convertFontVariationSettings(StyleResolver&, const CSSValue& value)
1325{
1326 if (is<CSSPrimitiveValue>(value)) {
1327 ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNormal);
1328 return { };
1329 }
1330
1331 FontVariationSettings settings;
1332 for (auto& item : downcast<CSSValueList>(value)) {
1333 auto& feature = downcast<CSSFontVariationValue>(item.get());
1334 settings.insert({ feature.tag(), feature.value() });
1335 }
1336 return settings;
1337}
1338#endif
1339
1340#if PLATFORM(IOS_FAMILY)
1341inline bool StyleBuilderConverter::convertTouchCallout(StyleResolver&, const CSSValue& value)
1342{
1343 return !equalLettersIgnoringASCIICase(downcast<CSSPrimitiveValue>(value).stringValue(), "none");
1344}
1345#endif
1346
1347#if ENABLE(TOUCH_EVENTS)
1348inline Color StyleBuilderConverter::convertTapHighlightColor(StyleResolver& styleResolver, const CSSValue& value)
1349{
1350 return styleResolver.colorFromPrimitiveValue(downcast<CSSPrimitiveValue>(value));
1351}
1352#endif
1353
1354#if ENABLE(POINTER_EVENTS)
1355inline OptionSet<TouchAction> StyleBuilderConverter::convertTouchAction(StyleResolver&, const CSSValue& value)
1356{
1357 if (is<CSSPrimitiveValue>(value))
1358 return downcast<CSSPrimitiveValue>(value);
1359
1360 if (is<CSSValueList>(value)) {
1361 OptionSet<TouchAction> touchActions;
1362 for (auto& currentValue : downcast<CSSValueList>(value)) {
1363 auto& primitiveValue = downcast<CSSPrimitiveValue>(currentValue.get());
1364 auto primitiveValueID = primitiveValue.valueID();
1365 if (primitiveValueID != CSSValuePanX && primitiveValueID != CSSValuePanY && primitiveValueID != CSSValuePinchZoom)
1366 return RenderStyle::initialTouchActions();
1367 touchActions.add(primitiveValue);
1368 }
1369 return touchActions;
1370 }
1371
1372 return RenderStyle::initialTouchActions();
1373}
1374#endif
1375
1376#if ENABLE(OVERFLOW_SCROLLING_TOUCH)
1377inline bool StyleBuilderConverter::convertOverflowScrolling(StyleResolver&, const CSSValue& value)
1378{
1379 return downcast<CSSPrimitiveValue>(value).valueID() == CSSValueTouch;
1380}
1381#endif
1382
1383inline SVGLengthValue StyleBuilderConverter::convertSVGLengthValue(StyleResolver&, const CSSValue& value)
1384{
1385 return SVGLengthValue::fromCSSPrimitiveValue(downcast<CSSPrimitiveValue>(value));
1386}
1387
1388inline Vector<SVGLengthValue> StyleBuilderConverter::convertSVGLengthVector(StyleResolver& styleResolver, const CSSValue& value)
1389{
1390 auto& valueList = downcast<CSSValueList>(value);
1391
1392 Vector<SVGLengthValue> svgLengths;
1393 svgLengths.reserveInitialCapacity(valueList.length());
1394 for (auto& item : valueList)
1395 svgLengths.uncheckedAppend(convertSVGLengthValue(styleResolver, item));
1396
1397 return svgLengths;
1398}
1399
1400inline Vector<SVGLengthValue> StyleBuilderConverter::convertStrokeDashArray(StyleResolver& styleResolver, const CSSValue& value)
1401{
1402 if (is<CSSPrimitiveValue>(value)) {
1403 ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone);
1404 return SVGRenderStyle::initialStrokeDashArray();
1405 }
1406
1407 return convertSVGLengthVector(styleResolver, value);
1408}
1409
1410inline PaintOrder StyleBuilderConverter::convertPaintOrder(StyleResolver&, const CSSValue& value)
1411{
1412 if (is<CSSPrimitiveValue>(value)) {
1413 ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNormal);
1414 return PaintOrder::Normal;
1415 }
1416
1417 auto& orderTypeList = downcast<CSSValueList>(value);
1418 switch (downcast<CSSPrimitiveValue>(*orderTypeList.itemWithoutBoundsCheck(0)).valueID()) {
1419 case CSSValueFill:
1420 return orderTypeList.length() > 1 ? PaintOrder::FillMarkers : PaintOrder::Fill;
1421 case CSSValueStroke:
1422 return orderTypeList.length() > 1 ? PaintOrder::StrokeMarkers : PaintOrder::Stroke;
1423 case CSSValueMarkers:
1424 return orderTypeList.length() > 1 ? PaintOrder::MarkersStroke : PaintOrder::Markers;
1425 default:
1426 ASSERT_NOT_REACHED();
1427 return PaintOrder::Normal;
1428 }
1429}
1430
1431inline float StyleBuilderConverter::convertOpacity(StyleResolver&, const CSSValue& value)
1432{
1433 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1434 float opacity = primitiveValue.floatValue();
1435 if (primitiveValue.isPercentage())
1436 opacity /= 100.0f;
1437 return opacity;
1438}
1439
1440inline String StyleBuilderConverter::convertSVGURIReference(StyleResolver& styleResolver, const CSSValue& value)
1441{
1442 String s;
1443 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1444 if (primitiveValue.isURI())
1445 s = primitiveValue.stringValue();
1446
1447 return SVGURIReference::fragmentIdentifierFromIRIString(s, styleResolver.document());
1448}
1449
1450inline Color StyleBuilderConverter::convertSVGColor(StyleResolver& styleResolver, const CSSValue& value)
1451{
1452 return styleResolver.colorFromPrimitiveValue(downcast<CSSPrimitiveValue>(value));
1453}
1454
1455inline StyleSelfAlignmentData StyleBuilderConverter::convertSelfOrDefaultAlignmentData(StyleResolver&, const CSSValue& value)
1456{
1457 StyleSelfAlignmentData alignmentData = RenderStyle::initialSelfAlignment();
1458 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1459 if (Pair* pairValue = primitiveValue.pairValue()) {
1460 if (pairValue->first()->valueID() == CSSValueLegacy) {
1461 alignmentData.setPositionType(ItemPositionType::Legacy);
1462 alignmentData.setPosition(*pairValue->second());
1463 } else if (pairValue->first()->valueID() == CSSValueFirst) {
1464 alignmentData.setPosition(ItemPosition::Baseline);
1465 } else if (pairValue->first()->valueID() == CSSValueLast) {
1466 alignmentData.setPosition(ItemPosition::LastBaseline);
1467 } else {
1468 alignmentData.setOverflow(*pairValue->first());
1469 alignmentData.setPosition(*pairValue->second());
1470 }
1471 } else
1472 alignmentData.setPosition(primitiveValue);
1473 return alignmentData;
1474}
1475
1476inline StyleContentAlignmentData StyleBuilderConverter::convertContentAlignmentData(StyleResolver&, const CSSValue& value)
1477{
1478 StyleContentAlignmentData alignmentData = RenderStyle::initialContentAlignment();
1479 if (!is<CSSContentDistributionValue>(value))
1480 return alignmentData;
1481 auto& contentValue = downcast<CSSContentDistributionValue>(value);
1482 if (contentValue.distribution()->valueID() != CSSValueInvalid)
1483 alignmentData.setDistribution(contentValue.distribution().get());
1484 if (contentValue.position()->valueID() != CSSValueInvalid)
1485 alignmentData.setPosition(contentValue.position().get());
1486 if (contentValue.overflow()->valueID() != CSSValueInvalid)
1487 alignmentData.setOverflow(contentValue.overflow().get());
1488 return alignmentData;
1489}
1490
1491inline GlyphOrientation StyleBuilderConverter::convertGlyphOrientation(StyleResolver&, const CSSValue& value)
1492{
1493 float angle = fabsf(fmodf(downcast<CSSPrimitiveValue>(value).floatValue(), 360.0f));
1494 if (angle <= 45.0f || angle > 315.0f)
1495 return GlyphOrientation::Degrees0;
1496 if (angle > 45.0f && angle <= 135.0f)
1497 return GlyphOrientation::Degrees90;
1498 if (angle > 135.0f && angle <= 225.0f)
1499 return GlyphOrientation::Degrees180;
1500 return GlyphOrientation::Degrees270;
1501}
1502
1503inline GlyphOrientation StyleBuilderConverter::convertGlyphOrientationOrAuto(StyleResolver& styleResolver, const CSSValue& value)
1504{
1505 if (downcast<CSSPrimitiveValue>(value).valueID() == CSSValueAuto)
1506 return GlyphOrientation::Auto;
1507 return convertGlyphOrientation(styleResolver, value);
1508}
1509
1510inline Optional<Length> StyleBuilderConverter::convertLineHeight(StyleResolver& styleResolver, const CSSValue& value, float multiplier)
1511{
1512 auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
1513 if (primitiveValue.valueID() == CSSValueNormal)
1514 return RenderStyle::initialLineHeight();
1515
1516 if (primitiveValue.isLength()) {
1517 Length length = primitiveValue.computeLength<Length>(StyleBuilderConverter::csstoLengthConversionDataWithTextZoomFactor(styleResolver));
1518 if (multiplier != 1.f)
1519 length = Length(length.value() * multiplier, Fixed);
1520 return length;
1521 }
1522
1523 // Line-height percentages need to inherit as if they were Fixed pixel values. In the example:
1524 // <div style="font-size: 10px; line-height: 150%;"><div style="font-size: 100px;"></div></div>
1525 // the inner element should have line-height of 15px. However, in this example:
1526 // <div style="font-size: 10px; line-height: 1.5;"><div style="font-size: 100px;"></div></div>
1527 // the inner element should have a line-height of 150px. Therefore, we map percentages to Fixed
1528 // values and raw numbers to percentages.
1529 if (primitiveValue.isPercentage()) {
1530 // FIXME: percentage should not be restricted to an integer here.
1531 return Length((styleResolver.style()->computedFontSize() * primitiveValue.intValue()) / 100, Fixed);
1532 }
1533 if (primitiveValue.isNumber())
1534 return Length(primitiveValue.doubleValue() * 100.0, Percent);
1535
1536 // FIXME: The parser should only emit the above types, so this should never be reached. We should change the
1537 // type of this function to return just a Length (and not an Optional).
1538 return WTF::nullopt;
1539}
1540
1541inline FontSynthesis StyleBuilderConverter::convertFontSynthesis(StyleResolver&, const CSSValue& value)
1542{
1543 if (is<CSSPrimitiveValue>(value)) {
1544 ASSERT(downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone);
1545 return FontSynthesisNone;
1546 }
1547
1548 FontSynthesis result = FontSynthesisNone;
1549 ASSERT(is<CSSValueList>(value));
1550 for (const CSSValue& v : downcast<CSSValueList>(value)) {
1551 switch (downcast<CSSPrimitiveValue>(v).valueID()) {
1552 case CSSValueWeight:
1553 result |= FontSynthesisWeight;
1554 break;
1555 case CSSValueStyle:
1556 result |= FontSynthesisStyle;
1557 break;
1558 case CSSValueSmallCaps:
1559 result |= FontSynthesisSmallCaps;
1560 break;
1561 default:
1562 ASSERT_NOT_REACHED();
1563 break;
1564 }
1565 }
1566
1567 return result;
1568}
1569
1570inline OptionSet<SpeakAs> StyleBuilderConverter::convertSpeakAs(StyleResolver&, const CSSValue& value)
1571{
1572 auto result = RenderStyle::initialSpeakAs();
1573 if (is<CSSValueList>(value)) {
1574 for (auto& currentValue : downcast<CSSValueList>(value))
1575 result.add(downcast<CSSPrimitiveValue>(currentValue.get()));
1576 }
1577 return result;
1578}
1579
1580inline OptionSet<HangingPunctuation> StyleBuilderConverter::convertHangingPunctuation(StyleResolver&, const CSSValue& value)
1581{
1582 auto result = RenderStyle::initialHangingPunctuation();
1583 if (is<CSSValueList>(value)) {
1584 for (auto& currentValue : downcast<CSSValueList>(value))
1585 result.add(downcast<CSSPrimitiveValue>(currentValue.get()));
1586 }
1587 return result;
1588}
1589
1590inline GapLength StyleBuilderConverter::convertGapLength(StyleResolver& styleResolver, const CSSValue& value)
1591{
1592 return (downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNormal) ? GapLength() : GapLength(convertLength(styleResolver, value));
1593}
1594
1595} // namespace WebCore
1596