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