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 | |
59 | namespace WebCore { |
60 | |
61 | // Note that we assume the CSS parser only allows valid CSSValue types. |
62 | class StyleBuilderConverter { |
63 | public: |
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 | |
165 | private: |
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 | |
191 | inline 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 | |
214 | inline 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 | |
221 | inline 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 | |
250 | inline 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 | |
257 | template<typename T> |
258 | inline T StyleBuilderConverter::convertComputedLength(StyleResolver& styleResolver, const CSSValue& value) |
259 | { |
260 | return downcast<CSSPrimitiveValue>(value).computeLength<T>(styleResolver.state().cssToLengthConversionData()); |
261 | } |
262 | |
263 | template<typename T> |
264 | inline 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 | |
294 | inline 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 | |
306 | inline 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 | |
315 | inline 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 | |
332 | inline 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 | |
346 | inline Length StyleBuilderConverter::convertPositionComponentX(StyleResolver& styleResolver, const CSSValue& value) |
347 | { |
348 | return convertPositionComponent<CSSValueLeft, CSSValueRight>(styleResolver, downcast<CSSPrimitiveValue>(value)); |
349 | } |
350 | |
351 | inline Length StyleBuilderConverter::convertPositionComponentY(StyleResolver& styleResolver, const CSSValue& value) |
352 | { |
353 | return convertPositionComponent<CSSValueTop, CSSValueBottom>(styleResolver, downcast<CSSPrimitiveValue>(value)); |
354 | } |
355 | |
356 | template<CSSValueID cssValueFor0, CSSValueID cssValueFor100> |
357 | inline 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 | |
392 | inline 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 | |
405 | inline 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 | |
415 | template<typename T> |
416 | inline T StyleBuilderConverter::convertNumber(StyleResolver&, const CSSValue& value) |
417 | { |
418 | return downcast<CSSPrimitiveValue>(value).value<T>(CSSPrimitiveValue::CSS_NUMBER); |
419 | } |
420 | |
421 | template<typename T> |
422 | inline 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 | |
429 | inline 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 | |
437 | template<CSSPropertyID property> |
438 | inline NinePieceImage StyleBuilderConverter::convertBorderImage(StyleResolver& styleResolver, CSSValue& value) |
439 | { |
440 | NinePieceImage image; |
441 | styleResolver.styleMap()->mapNinePieceImage(property, &value, image); |
442 | return image; |
443 | } |
444 | |
445 | template<CSSPropertyID property> |
446 | inline 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 | |
454 | template<CSSPropertyID> |
455 | inline RefPtr<StyleImage> StyleBuilderConverter::convertStyleImage(StyleResolver& styleResolver, CSSValue& value) |
456 | { |
457 | return styleResolver.styleImage(value); |
458 | } |
459 | |
460 | inline 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) |
468 | inline 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 | |
491 | inline 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 | |
509 | inline String StyleBuilderConverter::convertString(StyleResolver&, const CSSValue& value) |
510 | { |
511 | return downcast<CSSPrimitiveValue>(value).stringValue(); |
512 | } |
513 | |
514 | inline 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 | |
521 | inline 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 | |
528 | inline 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 | |
549 | inline 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 | |
560 | inline 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 | |
576 | inline 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 | |
620 | inline 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 | |
633 | inline 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 | |
643 | inline 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 | |
672 | inline 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 | |
695 | inline TextUnderlinePosition StyleBuilderConverter::convertTextUnderlinePosition(StyleResolver&, const CSSValue& value) |
696 | { |
697 | ASSERT(is<CSSPrimitiveValue>(value)); |
698 | return downcast<CSSPrimitiveValue>(value); |
699 | } |
700 | |
701 | inline 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 | |
715 | inline 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 | |
731 | inline 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 | |
752 | inline 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 | |
767 | inline 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 | |
797 | inline 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 | |
807 | inline 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 | |
828 | inline 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 | |
839 | static inline bool isImageShape(const CSSValue& value) |
840 | { |
841 | return is<CSSImageValue>(value) || is<CSSImageSetValue>(value) || is<CSSImageGeneratorValue>(value); |
842 | } |
843 | |
844 | inline 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 | |
883 | inline 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 | |
908 | inline 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 | |
922 | inline 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 | |
937 | inline 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 | |
954 | static 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 | |
967 | struct StyleBuilderConverter::TracksData { |
968 | WTF_MAKE_NONCOPYABLE(TracksData); WTF_MAKE_FAST_ALLOCATED; |
969 | public: |
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 | |
982 | inline 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 | |
1026 | inline 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 | |
1076 | inline 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 | |
1093 | inline 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 | |
1107 | inline GridTrackSize StyleBuilderConverter::convertGridTrackSize(StyleResolver& styleResolver, const CSSValue& value) |
1108 | { |
1109 | return createGridTrackSize(value, styleResolver); |
1110 | } |
1111 | |
1112 | inline 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 | |
1120 | inline 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 | |
1158 | inline 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 | |
1167 | inline 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 | |
1183 | inline 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 | |
1200 | inline 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 | |
1226 | inline 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 | |
1234 | inline 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 | |
1249 | inline 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 | |
1272 | inline 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". |
1288 | inline 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 | |
1304 | inline 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 | |
1318 | inline FontSelectionValue StyleBuilderConverter::convertFontStretch(StyleResolver&, const CSSValue& value) |
1319 | { |
1320 | return convertFontStretchFromValue(value); |
1321 | } |
1322 | |
1323 | #if ENABLE(VARIATION_FONTS) |
1324 | inline 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) |
1341 | inline 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) |
1348 | inline 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) |
1355 | inline 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) |
1377 | inline bool StyleBuilderConverter::convertOverflowScrolling(StyleResolver&, const CSSValue& value) |
1378 | { |
1379 | return downcast<CSSPrimitiveValue>(value).valueID() == CSSValueTouch; |
1380 | } |
1381 | #endif |
1382 | |
1383 | inline SVGLengthValue StyleBuilderConverter::convertSVGLengthValue(StyleResolver&, const CSSValue& value) |
1384 | { |
1385 | return SVGLengthValue::fromCSSPrimitiveValue(downcast<CSSPrimitiveValue>(value)); |
1386 | } |
1387 | |
1388 | inline 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 | |
1400 | inline 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 | |
1410 | inline 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 | |
1431 | inline 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 | |
1440 | inline 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 | |
1450 | inline Color StyleBuilderConverter::convertSVGColor(StyleResolver& styleResolver, const CSSValue& value) |
1451 | { |
1452 | return styleResolver.colorFromPrimitiveValue(downcast<CSSPrimitiveValue>(value)); |
1453 | } |
1454 | |
1455 | inline 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 | |
1476 | inline 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 | |
1491 | inline 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 | |
1503 | inline 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 | |
1510 | inline 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 | |
1541 | inline 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 | |
1570 | inline 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 | |
1580 | inline 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 | |
1590 | inline 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 | |