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