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