1/*
2 * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012, 2013 Apple Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "CSSPrimitiveValue.h"
23
24#include "CSSBasicShapes.h"
25#include "CSSCalculationValue.h"
26#include "CSSFontFamily.h"
27#include "CSSHelper.h"
28#include "CSSMarkup.h"
29#include "CSSPrimitiveValueMappings.h"
30#include "CSSPropertyNames.h"
31#include "CSSToLengthConversionData.h"
32#include "CSSValueKeywords.h"
33#include "CalculationValue.h"
34#include "Color.h"
35#include "Counter.h"
36#include "DeprecatedCSSOMPrimitiveValue.h"
37#include "FontCascade.h"
38#include "Node.h"
39#include "Pair.h"
40#include "RGBColor.h"
41#include "Rect.h"
42#include "RenderStyle.h"
43#include <wtf/NeverDestroyed.h>
44#include <wtf/StdLibExtras.h>
45#include <wtf/text/StringBuilder.h>
46#include <wtf/text/StringConcatenateNumbers.h>
47
48#if ENABLE(DASHBOARD_SUPPORT)
49#include "DashboardRegion.h"
50#endif
51
52
53namespace WebCore {
54
55static inline bool isValidCSSUnitTypeForDoubleConversion(CSSPrimitiveValue::UnitType unitType)
56{
57 switch (unitType) {
58 case CSSPrimitiveValue::CSS_CALC:
59 case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH:
60 case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER:
61 case CSSPrimitiveValue::CSS_CHS:
62 case CSSPrimitiveValue::CSS_CM:
63 case CSSPrimitiveValue::CSS_DEG:
64 case CSSPrimitiveValue::CSS_DIMENSION:
65 case CSSPrimitiveValue::CSS_EMS:
66 case CSSPrimitiveValue::CSS_QUIRKY_EMS:
67 case CSSPrimitiveValue::CSS_EXS:
68 case CSSPrimitiveValue::CSS_FR:
69 case CSSPrimitiveValue::CSS_GRAD:
70 case CSSPrimitiveValue::CSS_HZ:
71 case CSSPrimitiveValue::CSS_IN:
72 case CSSPrimitiveValue::CSS_KHZ:
73 case CSSPrimitiveValue::CSS_MM:
74 case CSSPrimitiveValue::CSS_MS:
75 case CSSPrimitiveValue::CSS_NUMBER:
76 case CSSPrimitiveValue::CSS_PC:
77 case CSSPrimitiveValue::CSS_PERCENTAGE:
78 case CSSPrimitiveValue::CSS_PT:
79 case CSSPrimitiveValue::CSS_PX:
80 case CSSPrimitiveValue::CSS_RAD:
81 case CSSPrimitiveValue::CSS_REMS:
82 case CSSPrimitiveValue::CSS_S:
83 case CSSPrimitiveValue::CSS_TURN:
84 case CSSPrimitiveValue::CSS_VH:
85 case CSSPrimitiveValue::CSS_VMAX:
86 case CSSPrimitiveValue::CSS_VMIN:
87 case CSSPrimitiveValue::CSS_VW:
88 return true;
89 case CSSPrimitiveValue::CSS_DPCM:
90 case CSSPrimitiveValue::CSS_DPI:
91 case CSSPrimitiveValue::CSS_DPPX:
92#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
93 return true;
94#else
95 return false;
96#endif
97 case CSSPrimitiveValue::CSS_ATTR:
98 case CSSPrimitiveValue::CSS_COUNTER:
99 case CSSPrimitiveValue::CSS_COUNTER_NAME:
100 case CSSPrimitiveValue::CSS_FONT_FAMILY:
101 case CSSPrimitiveValue::CSS_IDENT:
102 case CSSPrimitiveValue::CSS_PAIR:
103 case CSSPrimitiveValue::CSS_PROPERTY_ID:
104 case CSSPrimitiveValue::CSS_QUAD:
105 case CSSPrimitiveValue::CSS_RECT:
106 case CSSPrimitiveValue::CSS_RGBCOLOR:
107 case CSSPrimitiveValue::CSS_SHAPE:
108 case CSSPrimitiveValue::CSS_STRING:
109 case CSSPrimitiveValue::CSS_UNICODE_RANGE:
110 case CSSPrimitiveValue::CSS_UNKNOWN:
111 case CSSPrimitiveValue::CSS_URI:
112 case CSSPrimitiveValue::CSS_VALUE_ID:
113#if ENABLE(DASHBOARD_SUPPORT)
114 case CSSPrimitiveValue::CSS_DASHBOARD_REGION:
115#endif
116 return false;
117 }
118
119 ASSERT_NOT_REACHED();
120 return false;
121}
122
123#if !ASSERT_DISABLED
124
125static inline bool isStringType(CSSPrimitiveValue::UnitType type)
126{
127 switch (type) {
128 case CSSPrimitiveValue::CSS_STRING:
129 case CSSPrimitiveValue::CSS_URI:
130 case CSSPrimitiveValue::CSS_ATTR:
131 case CSSPrimitiveValue::CSS_COUNTER_NAME:
132 case CSSPrimitiveValue::CSS_DIMENSION:
133 return true;
134 case CSSPrimitiveValue::CSS_CALC:
135 case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH:
136 case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER:
137 case CSSPrimitiveValue::CSS_CHS:
138 case CSSPrimitiveValue::CSS_CM:
139 case CSSPrimitiveValue::CSS_COUNTER:
140 case CSSPrimitiveValue::CSS_DEG:
141 case CSSPrimitiveValue::CSS_DPCM:
142 case CSSPrimitiveValue::CSS_DPI:
143 case CSSPrimitiveValue::CSS_DPPX:
144 case CSSPrimitiveValue::CSS_EMS:
145 case CSSPrimitiveValue::CSS_QUIRKY_EMS:
146 case CSSPrimitiveValue::CSS_EXS:
147 case CSSPrimitiveValue::CSS_FONT_FAMILY:
148 case CSSPrimitiveValue::CSS_FR:
149 case CSSPrimitiveValue::CSS_GRAD:
150 case CSSPrimitiveValue::CSS_HZ:
151 case CSSPrimitiveValue::CSS_IDENT:
152 case CSSPrimitiveValue::CSS_IN:
153 case CSSPrimitiveValue::CSS_KHZ:
154 case CSSPrimitiveValue::CSS_MM:
155 case CSSPrimitiveValue::CSS_MS:
156 case CSSPrimitiveValue::CSS_NUMBER:
157 case CSSPrimitiveValue::CSS_PAIR:
158 case CSSPrimitiveValue::CSS_PC:
159 case CSSPrimitiveValue::CSS_PERCENTAGE:
160 case CSSPrimitiveValue::CSS_PROPERTY_ID:
161 case CSSPrimitiveValue::CSS_PT:
162 case CSSPrimitiveValue::CSS_PX:
163 case CSSPrimitiveValue::CSS_QUAD:
164 case CSSPrimitiveValue::CSS_RAD:
165 case CSSPrimitiveValue::CSS_RECT:
166 case CSSPrimitiveValue::CSS_REMS:
167 case CSSPrimitiveValue::CSS_RGBCOLOR:
168 case CSSPrimitiveValue::CSS_S:
169 case CSSPrimitiveValue::CSS_SHAPE:
170 case CSSPrimitiveValue::CSS_TURN:
171 case CSSPrimitiveValue::CSS_UNICODE_RANGE:
172 case CSSPrimitiveValue::CSS_UNKNOWN:
173 case CSSPrimitiveValue::CSS_VALUE_ID:
174 case CSSPrimitiveValue::CSS_VH:
175 case CSSPrimitiveValue::CSS_VMAX:
176 case CSSPrimitiveValue::CSS_VMIN:
177 case CSSPrimitiveValue::CSS_VW:
178#if ENABLE(DASHBOARD_SUPPORT)
179 case CSSPrimitiveValue::CSS_DASHBOARD_REGION:
180#endif
181 return false;
182 }
183
184 ASSERT_NOT_REACHED();
185 return false;
186}
187
188#endif // !ASSERT_DISABLED
189
190CSSPrimitiveValue::UnitCategory CSSPrimitiveValue::unitCategory(CSSPrimitiveValue::UnitType type)
191{
192 // Here we violate the spec (http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue) and allow conversions
193 // between CSS_PX and relative lengths (see cssPixelsPerInch comment in CSSHelper.h for the topic treatment).
194 switch (type) {
195 case CSS_NUMBER:
196 return UNumber;
197 case CSS_PERCENTAGE:
198 return UPercent;
199 case CSS_PX:
200 case CSS_CM:
201 case CSS_MM:
202 case CSS_IN:
203 case CSS_PT:
204 case CSS_PC:
205 return ULength;
206 case CSS_MS:
207 case CSS_S:
208 return UTime;
209 case CSS_DEG:
210 case CSS_RAD:
211 case CSS_GRAD:
212 case CSS_TURN:
213 return UAngle;
214 case CSS_HZ:
215 case CSS_KHZ:
216 return UFrequency;
217#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
218 case CSS_DPPX:
219 case CSS_DPI:
220 case CSS_DPCM:
221 return UResolution;
222#endif
223 default:
224 return UOther;
225 }
226}
227
228typedef HashMap<const CSSPrimitiveValue*, String> CSSTextCache;
229static CSSTextCache& cssTextCache()
230{
231 static NeverDestroyed<CSSTextCache> cache;
232 return cache;
233}
234
235unsigned short CSSPrimitiveValue::primitiveType() const
236{
237 if (m_primitiveUnitType == CSS_PROPERTY_ID || m_primitiveUnitType == CSS_VALUE_ID)
238 return CSS_IDENT;
239
240 // Web-exposed content expects font family values to have CSS_STRING primitive type
241 // so we need to map our internal CSS_FONT_FAMILY type here.
242 if (m_primitiveUnitType == CSS_FONT_FAMILY)
243 return CSS_STRING;
244
245 if (m_primitiveUnitType != CSSPrimitiveValue::CSS_CALC)
246 return m_primitiveUnitType;
247
248 switch (m_value.calc->category()) {
249 case CalculationCategory::Number:
250 return CSSPrimitiveValue::CSS_NUMBER;
251 case CalculationCategory::Length:
252 return CSSPrimitiveValue::CSS_PX;
253 case CalculationCategory::Percent:
254 return CSSPrimitiveValue::CSS_PERCENTAGE;
255 case CalculationCategory::PercentNumber:
256 return CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER;
257 case CalculationCategory::PercentLength:
258 return CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH;
259 case CalculationCategory::Angle:
260 case CalculationCategory::Time:
261 case CalculationCategory::Frequency:
262 return m_value.calc->primitiveType();
263 case CalculationCategory::Other:
264 return CSSPrimitiveValue::CSS_UNKNOWN;
265 }
266 return CSSPrimitiveValue::CSS_UNKNOWN;
267}
268
269static const AtomicString& propertyName(CSSPropertyID propertyID)
270{
271 ASSERT_ARG(propertyID, (propertyID >= firstCSSProperty && propertyID < firstCSSProperty + numCSSProperties));
272
273 return getPropertyNameAtomicString(propertyID);
274}
275
276static const AtomicString& valueName(CSSValueID valueID)
277{
278 ASSERT_ARG(valueID, (valueID >= firstCSSValueKeyword && valueID <= lastCSSValueKeyword));
279
280 return getValueNameAtomicString(valueID);
281}
282
283CSSPrimitiveValue::CSSPrimitiveValue(CSSValueID valueID)
284 : CSSValue(PrimitiveClass)
285{
286 m_primitiveUnitType = CSS_VALUE_ID;
287 m_value.valueID = valueID;
288}
289
290CSSPrimitiveValue::CSSPrimitiveValue(CSSPropertyID propertyID)
291 : CSSValue(PrimitiveClass)
292{
293 m_primitiveUnitType = CSS_PROPERTY_ID;
294 m_value.propertyID = propertyID;
295}
296
297CSSPrimitiveValue::CSSPrimitiveValue(double num, UnitType type)
298 : CSSValue(PrimitiveClass)
299{
300 m_primitiveUnitType = type;
301 ASSERT(std::isfinite(num));
302 m_value.num = num;
303}
304
305CSSPrimitiveValue::CSSPrimitiveValue(const String& string, UnitType type)
306 : CSSValue(PrimitiveClass)
307{
308 ASSERT(isStringType(type));
309 m_primitiveUnitType = type;
310 if ((m_value.string = string.impl()))
311 m_value.string->ref();
312}
313
314CSSPrimitiveValue::CSSPrimitiveValue(const Color& color)
315 : CSSValue(PrimitiveClass)
316{
317 m_primitiveUnitType = CSS_RGBCOLOR;
318 m_value.color = new Color(color);
319}
320
321CSSPrimitiveValue::CSSPrimitiveValue(const Length& length)
322 : CSSValue(PrimitiveClass)
323{
324 init(length);
325}
326
327CSSPrimitiveValue::CSSPrimitiveValue(const Length& length, const RenderStyle& style)
328 : CSSValue(PrimitiveClass)
329{
330 switch (length.type()) {
331 case Auto:
332 case Intrinsic:
333 case MinIntrinsic:
334 case MinContent:
335 case MaxContent:
336 case FillAvailable:
337 case FitContent:
338 case Percent:
339 init(length);
340 return;
341 case Fixed:
342 m_primitiveUnitType = CSS_PX;
343 m_value.num = adjustFloatForAbsoluteZoom(length.value(), style);
344 return;
345 case Calculated: {
346 init(CSSCalcValue::create(length.calculationValue(), style));
347 return;
348 }
349 case Relative:
350 case Undefined:
351 ASSERT_NOT_REACHED();
352 return;
353 }
354 ASSERT_NOT_REACHED();
355}
356
357CSSPrimitiveValue::CSSPrimitiveValue(const LengthSize& lengthSize, const RenderStyle& style)
358 : CSSValue(PrimitiveClass)
359{
360 init(lengthSize, style);
361}
362
363void CSSPrimitiveValue::init(const Length& length)
364{
365 switch (length.type()) {
366 case Auto:
367 m_primitiveUnitType = CSS_VALUE_ID;
368 m_value.valueID = CSSValueAuto;
369 return;
370 case WebCore::Fixed:
371 m_primitiveUnitType = CSS_PX;
372 m_value.num = length.value();
373 return;
374 case Intrinsic:
375 m_primitiveUnitType = CSS_VALUE_ID;
376 m_value.valueID = CSSValueIntrinsic;
377 return;
378 case MinIntrinsic:
379 m_primitiveUnitType = CSS_VALUE_ID;
380 m_value.valueID = CSSValueMinIntrinsic;
381 return;
382 case MinContent:
383 m_primitiveUnitType = CSS_VALUE_ID;
384 m_value.valueID = CSSValueMinContent;
385 return;
386 case MaxContent:
387 m_primitiveUnitType = CSS_VALUE_ID;
388 m_value.valueID = CSSValueMaxContent;
389 return;
390 case FillAvailable:
391 m_primitiveUnitType = CSS_VALUE_ID;
392 m_value.valueID = CSSValueWebkitFillAvailable;
393 return;
394 case FitContent:
395 m_primitiveUnitType = CSS_VALUE_ID;
396 m_value.valueID = CSSValueFitContent;
397 return;
398 case Percent:
399 m_primitiveUnitType = CSS_PERCENTAGE;
400 ASSERT(std::isfinite(length.percent()));
401 m_value.num = length.percent();
402 return;
403 case Calculated:
404 case Relative:
405 case Undefined:
406 ASSERT_NOT_REACHED();
407 return;
408 }
409 ASSERT_NOT_REACHED();
410}
411
412void CSSPrimitiveValue::init(const LengthSize& lengthSize, const RenderStyle& style)
413{
414 m_primitiveUnitType = CSS_PAIR;
415 m_hasCachedCSSText = false;
416 m_value.pair = &Pair::create(create(lengthSize.width, style), create(lengthSize.height, style)).leakRef();
417}
418
419void CSSPrimitiveValue::init(Ref<Counter>&& counter)
420{
421 m_primitiveUnitType = CSS_COUNTER;
422 m_hasCachedCSSText = false;
423 m_value.counter = &counter.leakRef();
424}
425
426void CSSPrimitiveValue::init(Ref<Rect>&& r)
427{
428 m_primitiveUnitType = CSS_RECT;
429 m_hasCachedCSSText = false;
430 m_value.rect = &r.leakRef();
431}
432
433void CSSPrimitiveValue::init(Ref<Quad>&& quad)
434{
435 m_primitiveUnitType = CSS_QUAD;
436 m_hasCachedCSSText = false;
437 m_value.quad = &quad.leakRef();
438}
439
440#if ENABLE(DASHBOARD_SUPPORT)
441void CSSPrimitiveValue::init(RefPtr<DashboardRegion>&& r)
442{
443 m_primitiveUnitType = CSS_DASHBOARD_REGION;
444 m_hasCachedCSSText = false;
445 m_value.region = r.leakRef();
446}
447#endif
448
449void CSSPrimitiveValue::init(Ref<Pair>&& p)
450{
451 m_primitiveUnitType = CSS_PAIR;
452 m_hasCachedCSSText = false;
453 m_value.pair = &p.leakRef();
454}
455
456void CSSPrimitiveValue::init(Ref<CSSBasicShape>&& shape)
457{
458 m_primitiveUnitType = CSS_SHAPE;
459 m_hasCachedCSSText = false;
460 m_value.shape = &shape.leakRef();
461}
462
463void CSSPrimitiveValue::init(RefPtr<CSSCalcValue>&& c)
464{
465 m_primitiveUnitType = CSS_CALC;
466 m_hasCachedCSSText = false;
467 m_value.calc = c.leakRef();
468}
469
470CSSPrimitiveValue::~CSSPrimitiveValue()
471{
472 cleanup();
473}
474
475void CSSPrimitiveValue::cleanup()
476{
477 auto type = static_cast<UnitType>(m_primitiveUnitType);
478 switch (type) {
479 case CSS_STRING:
480 case CSS_URI:
481 case CSS_ATTR:
482 case CSS_COUNTER_NAME:
483 if (m_value.string)
484 m_value.string->deref();
485 break;
486 case CSS_DIMENSION:
487 case CSS_COUNTER:
488 m_value.counter->deref();
489 break;
490 case CSS_RECT:
491 m_value.rect->deref();
492 break;
493 case CSS_QUAD:
494 m_value.quad->deref();
495 break;
496 case CSS_PAIR:
497 m_value.pair->deref();
498 break;
499#if ENABLE(DASHBOARD_SUPPORT)
500 case CSS_DASHBOARD_REGION:
501 if (m_value.region)
502 m_value.region->deref();
503 break;
504#endif
505 case CSS_CALC:
506 m_value.calc->deref();
507 break;
508 case CSS_CALC_PERCENTAGE_WITH_NUMBER:
509 case CSS_CALC_PERCENTAGE_WITH_LENGTH:
510 ASSERT_NOT_REACHED();
511 break;
512 case CSS_SHAPE:
513 m_value.shape->deref();
514 break;
515 case CSS_FONT_FAMILY:
516 ASSERT(m_value.fontFamily);
517 delete m_value.fontFamily;
518 m_value.fontFamily = nullptr;
519 break;
520 case CSS_RGBCOLOR:
521 ASSERT(m_value.color);
522 delete m_value.color;
523 m_value.color = nullptr;
524 break;
525 case CSS_NUMBER:
526 case CSS_PERCENTAGE:
527 case CSS_EMS:
528 case CSS_QUIRKY_EMS:
529 case CSS_EXS:
530 case CSS_REMS:
531 case CSS_CHS:
532 case CSS_PX:
533 case CSS_CM:
534 case CSS_MM:
535 case CSS_IN:
536 case CSS_PT:
537 case CSS_PC:
538 case CSS_DEG:
539 case CSS_RAD:
540 case CSS_GRAD:
541 case CSS_MS:
542 case CSS_S:
543 case CSS_HZ:
544 case CSS_KHZ:
545 case CSS_TURN:
546 case CSS_VW:
547 case CSS_VH:
548 case CSS_VMIN:
549 case CSS_VMAX:
550 case CSS_DPPX:
551 case CSS_DPI:
552 case CSS_DPCM:
553 case CSS_FR:
554 case CSS_IDENT:
555 case CSS_UNKNOWN:
556 case CSS_UNICODE_RANGE:
557 case CSS_PROPERTY_ID:
558 case CSS_VALUE_ID:
559 ASSERT(!isStringType(type));
560 break;
561 }
562 m_primitiveUnitType = 0;
563 if (m_hasCachedCSSText) {
564 cssTextCache().remove(this);
565 m_hasCachedCSSText = false;
566 }
567}
568
569double CSSPrimitiveValue::computeDegrees() const
570{
571 switch (primitiveType()) {
572 case CSS_DEG:
573 return doubleValue();
574 case CSS_RAD:
575 return rad2deg(doubleValue());
576 case CSS_GRAD:
577 return grad2deg(doubleValue());
578 case CSS_TURN:
579 return turn2deg(doubleValue());
580 default:
581 ASSERT_NOT_REACHED();
582 return 0;
583 }
584}
585
586template<> int CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
587{
588 return roundForImpreciseConversion<int>(computeLengthDouble(conversionData));
589}
590
591template<> unsigned CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
592{
593 return roundForImpreciseConversion<unsigned>(computeLengthDouble(conversionData));
594}
595
596template<> Length CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
597{
598 return Length(clampTo<float>(computeLengthDouble(conversionData), minValueForCssLength, maxValueForCssLength), Fixed);
599}
600
601template<> short CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
602{
603 return roundForImpreciseConversion<short>(computeLengthDouble(conversionData));
604}
605
606template<> unsigned short CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
607{
608 return roundForImpreciseConversion<unsigned short>(computeLengthDouble(conversionData));
609}
610
611template<> float CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
612{
613 return static_cast<float>(computeLengthDouble(conversionData));
614}
615
616template<> double CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
617{
618 return computeLengthDouble(conversionData);
619}
620
621double CSSPrimitiveValue::computeLengthDouble(const CSSToLengthConversionData& conversionData) const
622{
623 if (m_primitiveUnitType == CSS_CALC)
624 // The multiplier and factor is applied to each value in the calc expression individually
625 return m_value.calc->computeLengthPx(conversionData);
626
627 return computeNonCalcLengthDouble(conversionData, static_cast<UnitType>(primitiveType()), m_value.num);
628}
629
630double CSSPrimitiveValue::computeNonCalcLengthDouble(const CSSToLengthConversionData& conversionData, UnitType primitiveType, double value)
631{
632 double factor;
633 bool applyZoom = true;
634
635 switch (primitiveType) {
636 case CSS_EMS:
637 case CSS_QUIRKY_EMS:
638 ASSERT(conversionData.style());
639 factor = conversionData.computingFontSize() ? conversionData.style()->fontDescription().specifiedSize() : conversionData.style()->fontDescription().computedSize();
640 break;
641 case CSS_EXS:
642 ASSERT(conversionData.style());
643 // FIXME: We have a bug right now where the zoom will be applied twice to EX units.
644 // We really need to compute EX using fontMetrics for the original specifiedSize and not use
645 // our actual constructed rendering font.
646 if (conversionData.style()->fontMetrics().hasXHeight())
647 factor = conversionData.style()->fontMetrics().xHeight();
648 else
649 factor = (conversionData.computingFontSize() ? conversionData.style()->fontDescription().specifiedSize() : conversionData.style()->fontDescription().computedSize()) / 2.0;
650 break;
651 case CSS_REMS:
652 if (conversionData.rootStyle())
653 factor = conversionData.computingFontSize() ? conversionData.rootStyle()->fontDescription().specifiedSize() : conversionData.rootStyle()->fontDescription().computedSize();
654 else
655 factor = 1.0;
656 break;
657 case CSS_CHS:
658 ASSERT(conversionData.style());
659 factor = conversionData.style()->fontMetrics().zeroWidth();
660 break;
661 case CSS_PX:
662 factor = 1.0;
663 break;
664 case CSS_CM:
665 factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
666 break;
667 case CSS_MM:
668 factor = cssPixelsPerInch / 25.4;
669 break;
670 case CSS_IN:
671 factor = cssPixelsPerInch;
672 break;
673 case CSS_PT:
674 factor = cssPixelsPerInch / 72.0;
675 break;
676 case CSS_PC:
677 // 1 pc == 12 pt
678 factor = cssPixelsPerInch * 12.0 / 72.0;
679 break;
680 case CSS_CALC_PERCENTAGE_WITH_LENGTH:
681 case CSS_CALC_PERCENTAGE_WITH_NUMBER:
682 ASSERT_NOT_REACHED();
683 return -1.0;
684 case CSS_VH:
685 factor = conversionData.viewportHeightFactor();
686 applyZoom = false;
687 break;
688 case CSS_VW:
689 factor = conversionData.viewportWidthFactor();
690 applyZoom = false;
691 break;
692 case CSS_VMAX:
693 factor = conversionData.viewportMaxFactor();
694 applyZoom = false;
695 break;
696 case CSS_VMIN:
697 factor = conversionData.viewportMinFactor();
698 applyZoom = false;
699 break;
700 default:
701 ASSERT_NOT_REACHED();
702 return -1.0;
703 }
704
705 // We do not apply the zoom factor when we are computing the value of the font-size property. The zooming
706 // for font sizes is much more complicated, since we have to worry about enforcing the minimum font size preference
707 // as well as enforcing the implicit "smart minimum."
708 double result = value * factor;
709 if (conversionData.computingFontSize() || isFontRelativeLength(primitiveType))
710 return result;
711
712 if (applyZoom)
713 result *= conversionData.zoom();
714
715 return result;
716}
717
718ExceptionOr<void> CSSPrimitiveValue::setFloatValue(unsigned short, double)
719{
720 // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects.
721 // No other engine supports mutating style through this API. Computed style is always read-only anyway.
722 // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
723 return Exception { NoModificationAllowedError };
724}
725
726double CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(UnitType unitType)
727{
728 double factor = 1.0;
729 // FIXME: the switch can be replaced by an array of scale factors.
730 switch (unitType) {
731 // These are "canonical" units in their respective categories.
732 case CSS_PX:
733 case CSS_DEG:
734 case CSS_MS:
735 case CSS_HZ:
736 break;
737 case CSS_CM:
738 factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
739 break;
740 case CSS_DPCM:
741 factor = 2.54 / cssPixelsPerInch; // (2.54 cm/in)
742 break;
743 case CSS_MM:
744 factor = cssPixelsPerInch / 25.4;
745 break;
746 case CSS_IN:
747 factor = cssPixelsPerInch;
748 break;
749 case CSS_DPI:
750 factor = 1 / cssPixelsPerInch;
751 break;
752 case CSS_PT:
753 factor = cssPixelsPerInch / 72.0;
754 break;
755 case CSS_PC:
756 factor = cssPixelsPerInch * 12.0 / 72.0; // 1 pc == 12 pt
757 break;
758 case CSS_RAD:
759 factor = 180 / piDouble;
760 break;
761 case CSS_GRAD:
762 factor = 0.9;
763 break;
764 case CSS_TURN:
765 factor = 360;
766 break;
767 case CSS_S:
768 case CSS_KHZ:
769 factor = 1000;
770 break;
771 default:
772 break;
773 }
774
775 return factor;
776}
777
778ExceptionOr<float> CSSPrimitiveValue::getFloatValue(unsigned short unitType) const
779{
780 auto result = doubleValueInternal(static_cast<UnitType>(unitType));
781 if (!result)
782 return Exception { InvalidAccessError };
783 return clampTo<float>(result.value());
784}
785
786double CSSPrimitiveValue::doubleValue(UnitType unitType) const
787{
788 return doubleValueInternal(unitType).valueOr(0);
789}
790
791double CSSPrimitiveValue::doubleValue() const
792{
793 return m_primitiveUnitType != CSS_CALC ? m_value.num : m_value.calc->doubleValue();
794}
795
796
797CSSPrimitiveValue::UnitType CSSPrimitiveValue::canonicalUnitTypeForCategory(UnitCategory category)
798{
799 // The canonical unit type is chosen according to the way CSSParser::validUnit() chooses the default unit
800 // in each category (based on unitflags).
801 switch (category) {
802 case UNumber:
803 return CSS_NUMBER;
804 case ULength:
805 return CSS_PX;
806 case UPercent:
807 return CSS_UNKNOWN; // Cannot convert between numbers and percent.
808 case UTime:
809 return CSS_MS;
810 case UAngle:
811 return CSS_DEG;
812 case UFrequency:
813 return CSS_HZ;
814#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
815 case UResolution:
816 return CSS_DPPX;
817#endif
818 default:
819 return CSS_UNKNOWN;
820 }
821}
822
823Optional<double> CSSPrimitiveValue::doubleValueInternal(UnitType requestedUnitType) const
824{
825 if (!isValidCSSUnitTypeForDoubleConversion(static_cast<UnitType>(m_primitiveUnitType)) || !isValidCSSUnitTypeForDoubleConversion(requestedUnitType))
826 return WTF::nullopt;
827
828 UnitType sourceUnitType = static_cast<UnitType>(primitiveType());
829 if (requestedUnitType == sourceUnitType || requestedUnitType == CSS_DIMENSION)
830 return doubleValue();
831
832 UnitCategory sourceCategory = unitCategory(sourceUnitType);
833 ASSERT(sourceCategory != UOther);
834
835 UnitType targetUnitType = requestedUnitType;
836 UnitCategory targetCategory = unitCategory(targetUnitType);
837 ASSERT(targetCategory != UOther);
838
839 // Cannot convert between unrelated unit categories if one of them is not UNumber.
840 if (sourceCategory != targetCategory && sourceCategory != UNumber && targetCategory != UNumber)
841 return WTF::nullopt;
842
843 if (targetCategory == UNumber) {
844 // We interpret conversion to CSS_NUMBER as conversion to a canonical unit in this value's category.
845 targetUnitType = canonicalUnitTypeForCategory(sourceCategory);
846 if (targetUnitType == CSS_UNKNOWN)
847 return WTF::nullopt;
848 }
849
850 if (sourceUnitType == CSS_NUMBER) {
851 // We interpret conversion from CSS_NUMBER in the same way as CSSParser::validUnit() while using non-strict mode.
852 sourceUnitType = canonicalUnitTypeForCategory(targetCategory);
853 if (sourceUnitType == CSS_UNKNOWN)
854 return WTF::nullopt;
855 }
856
857 double convertedValue = doubleValue();
858
859 // First convert the value from m_primitiveUnitType to canonical type.
860 double factor = conversionToCanonicalUnitsScaleFactor(sourceUnitType);
861 convertedValue *= factor;
862
863 // Now convert from canonical type to the target unitType.
864 factor = conversionToCanonicalUnitsScaleFactor(targetUnitType);
865 convertedValue /= factor;
866
867 return convertedValue;
868}
869
870ExceptionOr<void> CSSPrimitiveValue::setStringValue(unsigned short, const String&)
871{
872 // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects.
873 // No other engine supports mutating style through this API. Computed style is always read-only anyway.
874 // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
875 return Exception { NoModificationAllowedError };
876}
877
878ExceptionOr<String> CSSPrimitiveValue::getStringValue() const
879{
880 switch (m_primitiveUnitType) {
881 case CSS_STRING:
882 case CSS_ATTR:
883 case CSS_URI:
884 return m_value.string;
885 case CSS_FONT_FAMILY:
886 return String { m_value.fontFamily->familyName };
887 case CSS_VALUE_ID:
888 return String { valueName(m_value.valueID).string() };
889 case CSS_PROPERTY_ID:
890 return String { propertyName(m_value.propertyID).string() };
891 default:
892 return Exception { InvalidAccessError };
893 }
894}
895
896String CSSPrimitiveValue::stringValue() const
897{
898 switch (m_primitiveUnitType) {
899 case CSS_STRING:
900 case CSS_ATTR:
901 case CSS_URI:
902 return m_value.string;
903 case CSS_FONT_FAMILY:
904 return m_value.fontFamily->familyName;
905 case CSS_VALUE_ID:
906 return valueName(m_value.valueID);
907 case CSS_PROPERTY_ID:
908 return propertyName(m_value.propertyID);
909 default:
910 return String();
911 }
912}
913
914ExceptionOr<Counter&> CSSPrimitiveValue::getCounterValue() const
915{
916 if (m_primitiveUnitType != CSS_COUNTER)
917 return Exception { InvalidAccessError };
918 return *m_value.counter;
919}
920
921ExceptionOr<Rect&> CSSPrimitiveValue::getRectValue() const
922{
923 if (m_primitiveUnitType != CSS_RECT)
924 return Exception { InvalidAccessError };
925 return *m_value.rect;
926}
927
928ExceptionOr<Ref<RGBColor>> CSSPrimitiveValue::getRGBColorValue() const
929{
930 if (m_primitiveUnitType != CSS_RGBCOLOR)
931 return Exception { InvalidAccessError };
932
933 // FIXME: This should not return a new object for each invocation.
934 return RGBColor::create(m_value.color->rgb());
935}
936
937NEVER_INLINE String CSSPrimitiveValue::formatNumberValue(StringView suffix) const
938{
939 return makeString(m_value.num, suffix);
940}
941
942ALWAYS_INLINE String CSSPrimitiveValue::formatNumberForCustomCSSText() const
943{
944 switch (m_primitiveUnitType) {
945 case CSS_UNKNOWN:
946 return String();
947 case CSS_NUMBER:
948 return formatNumberValue("");
949 case CSS_PERCENTAGE:
950 return formatNumberValue("%");
951 case CSS_EMS:
952 case CSS_QUIRKY_EMS:
953 return formatNumberValue("em");
954 case CSS_EXS:
955 return formatNumberValue("ex");
956 case CSS_REMS:
957 return formatNumberValue("rem");
958 case CSS_CHS:
959 return formatNumberValue("ch");
960 case CSS_PX:
961 return formatNumberValue("px");
962 case CSS_CM:
963 return formatNumberValue("cm");
964#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
965 case CSS_DPPX:
966 return formatNumberValue("dppx");
967 case CSS_DPI:
968 return formatNumberValue("dpi");
969 case CSS_DPCM:
970 return formatNumberValue("dpcm");
971#endif
972 case CSS_MM:
973 return formatNumberValue("mm");
974 case CSS_IN:
975 return formatNumberValue("in");
976 case CSS_PT:
977 return formatNumberValue("pt");
978 case CSS_PC:
979 return formatNumberValue("pc");
980 case CSS_DEG:
981 return formatNumberValue("deg");
982 case CSS_RAD:
983 return formatNumberValue("rad");
984 case CSS_GRAD:
985 return formatNumberValue("grad");
986 case CSS_MS:
987 return formatNumberValue("ms");
988 case CSS_S:
989 return formatNumberValue("s");
990 case CSS_HZ:
991 return formatNumberValue("hz");
992 case CSS_KHZ:
993 return formatNumberValue("khz");
994 case CSS_TURN:
995 return formatNumberValue("turn");
996 case CSS_FR:
997 return formatNumberValue("fr");
998 case CSS_DIMENSION:
999 // FIXME: We currently don't handle CSS_DIMENSION properly as we don't store
1000 // the actual dimension, just the numeric value as a string.
1001 case CSS_STRING:
1002 // FIME-NEWPARSER: Once we have CSSCustomIdentValue hooked up, this can just be
1003 // serializeString, since custom identifiers won't be the same value as strings
1004 // any longer.
1005 return serializeAsStringOrCustomIdent(m_value.string);
1006 case CSS_FONT_FAMILY:
1007 return serializeFontFamily(m_value.fontFamily->familyName);
1008 case CSS_URI:
1009 return serializeURL(m_value.string);
1010 case CSS_VALUE_ID:
1011 return valueName(m_value.valueID);
1012 case CSS_PROPERTY_ID:
1013 return propertyName(m_value.propertyID);
1014 case CSS_ATTR:
1015 return "attr(" + String(m_value.string) + ')';
1016 case CSS_COUNTER_NAME:
1017 return "counter(" + String(m_value.string) + ')';
1018 case CSS_COUNTER: {
1019 StringBuilder result;
1020 String separator = m_value.counter->separator();
1021 if (separator.isEmpty())
1022 result.appendLiteral("counter(");
1023 else
1024 result.appendLiteral("counters(");
1025
1026 result.append(m_value.counter->identifier());
1027 if (!separator.isEmpty()) {
1028 result.appendLiteral(", ");
1029 serializeString(separator, result);
1030 }
1031 String listStyle = m_value.counter->listStyle();
1032 if (!listStyle.isEmpty()) {
1033 result.appendLiteral(", ");
1034 result.append(listStyle);
1035 }
1036 result.append(')');
1037
1038 return result.toString();
1039 }
1040 case CSS_RECT:
1041 return rectValue()->cssText();
1042 case CSS_QUAD:
1043 return quadValue()->cssText();
1044 case CSS_RGBCOLOR:
1045 return color().cssText();
1046 case CSS_PAIR:
1047 return pairValue()->cssText();
1048#if ENABLE(DASHBOARD_SUPPORT)
1049 case CSS_DASHBOARD_REGION: {
1050 StringBuilder result;
1051 for (DashboardRegion* region = dashboardRegionValue(); region; region = region->m_next.get()) {
1052 if (!result.isEmpty())
1053 result.append(' ');
1054 result.appendLiteral("dashboard-region(");
1055 result.append(region->m_label);
1056 if (region->m_isCircle)
1057 result.appendLiteral(" circle");
1058 else if (region->m_isRectangle)
1059 result.appendLiteral(" rectangle");
1060 else
1061 break;
1062 if (region->top()->m_primitiveUnitType == CSS_VALUE_ID && region->top()->valueID() == CSSValueInvalid) {
1063 ASSERT(region->right()->m_primitiveUnitType == CSS_VALUE_ID);
1064 ASSERT(region->bottom()->m_primitiveUnitType == CSS_VALUE_ID);
1065 ASSERT(region->left()->m_primitiveUnitType == CSS_VALUE_ID);
1066 ASSERT(region->right()->valueID() == CSSValueInvalid);
1067 ASSERT(region->bottom()->valueID() == CSSValueInvalid);
1068 ASSERT(region->left()->valueID() == CSSValueInvalid);
1069 } else {
1070 result.append(' ');
1071 result.append(region->top()->cssText());
1072 result.append(' ');
1073 result.append(region->right()->cssText());
1074 result.append(' ');
1075 result.append(region->bottom()->cssText());
1076 result.append(' ');
1077 result.append(region->left()->cssText());
1078 }
1079 result.append(')');
1080 }
1081 return result.toString();
1082 }
1083#endif
1084 case CSS_CALC:
1085 return m_value.calc->cssText();
1086 case CSS_SHAPE:
1087 return m_value.shape->cssText();
1088 case CSS_VW:
1089 return formatNumberValue("vw");
1090 case CSS_VH:
1091 return formatNumberValue("vh");
1092 case CSS_VMIN:
1093 return formatNumberValue("vmin");
1094 case CSS_VMAX:
1095 return formatNumberValue("vmax");
1096 }
1097 return String();
1098}
1099
1100String CSSPrimitiveValue::customCSSText() const
1101{
1102 // FIXME: return the original value instead of a generated one (e.g. color
1103 // name if it was specified) - check what spec says about this
1104
1105 CSSTextCache& cssTextCache = WebCore::cssTextCache();
1106
1107 if (m_hasCachedCSSText) {
1108 ASSERT(cssTextCache.contains(this));
1109 return cssTextCache.get(this);
1110 }
1111
1112 String text = formatNumberForCustomCSSText();
1113
1114 ASSERT(!cssTextCache.contains(this));
1115 m_hasCachedCSSText = true;
1116 cssTextCache.set(this, text);
1117 return text;
1118}
1119
1120bool CSSPrimitiveValue::equals(const CSSPrimitiveValue& other) const
1121{
1122 if (m_primitiveUnitType != other.m_primitiveUnitType)
1123 return false;
1124
1125 switch (m_primitiveUnitType) {
1126 case CSS_UNKNOWN:
1127 return false;
1128 case CSS_NUMBER:
1129 case CSS_PERCENTAGE:
1130 case CSS_EMS:
1131 case CSS_QUIRKY_EMS:
1132 case CSS_EXS:
1133 case CSS_REMS:
1134 case CSS_PX:
1135 case CSS_CM:
1136#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
1137 case CSS_DPPX:
1138 case CSS_DPI:
1139 case CSS_DPCM:
1140#endif
1141 case CSS_MM:
1142 case CSS_IN:
1143 case CSS_PT:
1144 case CSS_PC:
1145 case CSS_DEG:
1146 case CSS_RAD:
1147 case CSS_GRAD:
1148 case CSS_MS:
1149 case CSS_S:
1150 case CSS_HZ:
1151 case CSS_KHZ:
1152 case CSS_TURN:
1153 case CSS_VW:
1154 case CSS_VH:
1155 case CSS_VMIN:
1156 case CSS_FR:
1157 return m_value.num == other.m_value.num;
1158 case CSS_PROPERTY_ID:
1159 return propertyName(m_value.propertyID) == propertyName(other.m_value.propertyID);
1160 case CSS_VALUE_ID:
1161 return valueName(m_value.valueID) == valueName(other.m_value.valueID);
1162 case CSS_DIMENSION:
1163 case CSS_STRING:
1164 case CSS_URI:
1165 case CSS_ATTR:
1166 case CSS_COUNTER_NAME:
1167 return equal(m_value.string, other.m_value.string);
1168 case CSS_COUNTER:
1169 return m_value.counter && other.m_value.counter && m_value.counter->equals(*other.m_value.counter);
1170 case CSS_RECT:
1171 return m_value.rect && other.m_value.rect && m_value.rect->equals(*other.m_value.rect);
1172 case CSS_QUAD:
1173 return m_value.quad && other.m_value.quad && m_value.quad->equals(*other.m_value.quad);
1174 case CSS_RGBCOLOR:
1175 return color() == other.color();
1176 case CSS_PAIR:
1177 return m_value.pair && other.m_value.pair && m_value.pair->equals(*other.m_value.pair);
1178#if ENABLE(DASHBOARD_SUPPORT)
1179 case CSS_DASHBOARD_REGION:
1180 return m_value.region && other.m_value.region && m_value.region->equals(*other.m_value.region);
1181#endif
1182 case CSS_CALC:
1183 return m_value.calc && other.m_value.calc && m_value.calc->equals(*other.m_value.calc);
1184 case CSS_SHAPE:
1185 return m_value.shape && other.m_value.shape && m_value.shape->equals(*other.m_value.shape);
1186 case CSS_FONT_FAMILY:
1187 return fontFamily() == other.fontFamily();
1188 }
1189 return false;
1190}
1191
1192Ref<DeprecatedCSSOMPrimitiveValue> CSSPrimitiveValue::createDeprecatedCSSOMPrimitiveWrapper(CSSStyleDeclaration& styleDeclaration) const
1193{
1194 return DeprecatedCSSOMPrimitiveValue::create(*this, styleDeclaration);
1195}
1196
1197// https://drafts.css-houdini.org/css-properties-values-api/#dependency-cycles-via-relative-units
1198void CSSPrimitiveValue::collectDirectComputationalDependencies(HashSet<CSSPropertyID>& values) const
1199{
1200 switch (m_primitiveUnitType) {
1201 case CSS_EMS:
1202 case CSS_QUIRKY_EMS:
1203 case CSS_EXS:
1204 case CSS_CHS:
1205 values.add(CSSPropertyFontSize);
1206 break;
1207 case CSS_CALC:
1208 m_value.calc->collectDirectComputationalDependencies(values);
1209 break;
1210 }
1211}
1212
1213void CSSPrimitiveValue::collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& values) const
1214{
1215 switch (m_primitiveUnitType) {
1216 case CSS_REMS:
1217 values.add(CSSPropertyFontSize);
1218 break;
1219 case CSS_CALC:
1220 m_value.calc->collectDirectRootComputationalDependencies(values);
1221 break;
1222 }
1223}
1224
1225} // namespace WebCore
1226