6ae62671983eb70f7139b9be5c176a091bfcd0a7
[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 CalculationCategory::Number:
252         return CSSPrimitiveValue::CSS_NUMBER;
253     case CalculationCategory::Length:
254         return CSSPrimitiveValue::CSS_PX;
255     case CalculationCategory::Percent:
256         return CSSPrimitiveValue::CSS_PERCENTAGE;
257     case CalculationCategory::PercentNumber:
258         return CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER;
259     case CalculationCategory::PercentLength:
260         return CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH;
261     case CalculationCategory::Angle:
262     case CalculationCategory::Time:
263     case CalculationCategory::Frequency:
264         return m_value.calc->primitiveType();
265     case CalculationCategory::Other:
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 >= firstCSSValueKeyword && valueID <= lastCSSValueKeyword));
281
282     return getValueNameAtomicString(valueID);
283 }
284
285 CSSPrimitiveValue::CSSPrimitiveValue(CSSValueID valueID)
286     : CSSValue(PrimitiveClass)
287 {
288     m_primitiveUnitType = CSS_VALUE_ID;
289     m_value.valueID = valueID;
290 }
291
292 CSSPrimitiveValue::CSSPrimitiveValue(CSSPropertyID propertyID)
293     : CSSValue(PrimitiveClass)
294 {
295     m_primitiveUnitType = CSS_PROPERTY_ID;
296     m_value.propertyID = propertyID;
297 }
298
299 CSSPrimitiveValue::CSSPrimitiveValue(double num, UnitType type)
300     : CSSValue(PrimitiveClass)
301 {
302     m_primitiveUnitType = type;
303     ASSERT(std::isfinite(num));
304     m_value.num = num;
305 }
306
307 CSSPrimitiveValue::CSSPrimitiveValue(const String& string, UnitType type)
308     : CSSValue(PrimitiveClass)
309 {
310     ASSERT(isStringType(type));
311     m_primitiveUnitType = type;
312     if ((m_value.string = string.impl()))
313         m_value.string->ref();
314 }
315
316 CSSPrimitiveValue::CSSPrimitiveValue(const Color& color)
317     : CSSValue(PrimitiveClass)
318 {
319     m_primitiveUnitType = CSS_RGBCOLOR;
320     m_value.color = new Color(color);
321 }
322
323 CSSPrimitiveValue::CSSPrimitiveValue(const Length& length)
324     : CSSValue(PrimitiveClass)
325 {
326     init(length);
327 }
328
329 CSSPrimitiveValue::CSSPrimitiveValue(const Length& length, const RenderStyle& style)
330     : CSSValue(PrimitiveClass)
331 {
332     switch (length.type()) {
333     case Auto:
334     case Intrinsic:
335     case MinIntrinsic:
336     case MinContent:
337     case MaxContent:
338     case FillAvailable:
339     case FitContent:
340     case Percent:
341         init(length);
342         return;
343     case Fixed:
344         m_primitiveUnitType = CSS_PX;
345         m_value.num = adjustFloatForAbsoluteZoom(length.value(), style);
346         return;
347     case Calculated: {
348         init(CSSCalcValue::create(length.calculationValue(), style));
349         return;
350     }
351     case Relative:
352     case Undefined:
353         ASSERT_NOT_REACHED();
354         return;
355     }
356     ASSERT_NOT_REACHED();
357 }
358
359 CSSPrimitiveValue::CSSPrimitiveValue(const LengthSize& lengthSize, const RenderStyle& style)
360     : CSSValue(PrimitiveClass)
361 {
362     init(lengthSize, style);
363 }
364
365 void CSSPrimitiveValue::init(const Length& length)
366 {
367     switch (length.type()) {
368     case Auto:
369         m_primitiveUnitType = CSS_VALUE_ID;
370         m_value.valueID = CSSValueAuto;
371         return;
372     case WebCore::Fixed:
373         m_primitiveUnitType = CSS_PX;
374         m_value.num = length.value();
375         return;
376     case Intrinsic:
377         m_primitiveUnitType = CSS_VALUE_ID;
378         m_value.valueID = CSSValueIntrinsic;
379         return;
380     case MinIntrinsic:
381         m_primitiveUnitType = CSS_VALUE_ID;
382         m_value.valueID = CSSValueMinIntrinsic;
383         return;
384     case MinContent:
385         m_primitiveUnitType = CSS_VALUE_ID;
386         m_value.valueID = CSSValueMinContent;
387         return;
388     case MaxContent:
389         m_primitiveUnitType = CSS_VALUE_ID;
390         m_value.valueID = CSSValueMaxContent;
391         return;
392     case FillAvailable:
393         m_primitiveUnitType = CSS_VALUE_ID;
394         m_value.valueID = CSSValueWebkitFillAvailable;
395         return;
396     case FitContent:
397         m_primitiveUnitType = CSS_VALUE_ID;
398         m_value.valueID = CSSValueFitContent;
399         return;
400     case Percent:
401         m_primitiveUnitType = CSS_PERCENTAGE;
402         ASSERT(std::isfinite(length.percent()));
403         m_value.num = length.percent();
404         return;
405     case Calculated:
406     case Relative:
407     case Undefined:
408         ASSERT_NOT_REACHED();
409         return;
410     }
411     ASSERT_NOT_REACHED();
412 }
413
414 void CSSPrimitiveValue::init(const LengthSize& lengthSize, const RenderStyle& style)
415 {
416     m_primitiveUnitType = CSS_PAIR;
417     m_hasCachedCSSText = false;
418     m_value.pair = &Pair::create(create(lengthSize.width, style), create(lengthSize.height, style)).leakRef();
419 }
420
421 void CSSPrimitiveValue::init(Ref<Counter>&& counter)
422 {
423     m_primitiveUnitType = CSS_COUNTER;
424     m_hasCachedCSSText = false;
425     m_value.counter = &counter.leakRef();
426 }
427
428 void CSSPrimitiveValue::init(Ref<Rect>&& r)
429 {
430     m_primitiveUnitType = CSS_RECT;
431     m_hasCachedCSSText = false;
432     m_value.rect = &r.leakRef();
433 }
434
435 void CSSPrimitiveValue::init(Ref<Quad>&& quad)
436 {
437     m_primitiveUnitType = CSS_QUAD;
438     m_hasCachedCSSText = false;
439     m_value.quad = &quad.leakRef();
440 }
441
442 #if ENABLE(DASHBOARD_SUPPORT)
443 void CSSPrimitiveValue::init(RefPtr<DashboardRegion>&& r)
444 {
445     m_primitiveUnitType = CSS_DASHBOARD_REGION;
446     m_hasCachedCSSText = false;
447     m_value.region = r.leakRef();
448 }
449 #endif
450
451 void CSSPrimitiveValue::init(Ref<Pair>&& p)
452 {
453     m_primitiveUnitType = CSS_PAIR;
454     m_hasCachedCSSText = false;
455     m_value.pair = &p.leakRef();
456 }
457
458 void CSSPrimitiveValue::init(Ref<CSSBasicShape>&& shape)
459 {
460     m_primitiveUnitType = CSS_SHAPE;
461     m_hasCachedCSSText = false;
462     m_value.shape = &shape.leakRef();
463 }
464
465 void CSSPrimitiveValue::init(RefPtr<CSSCalcValue>&& c)
466 {
467     m_primitiveUnitType = CSS_CALC;
468     m_hasCachedCSSText = false;
469     m_value.calc = c.leakRef();
470 }
471
472 CSSPrimitiveValue::~CSSPrimitiveValue()
473 {
474     cleanup();
475 }
476
477 void CSSPrimitiveValue::cleanup()
478 {
479     auto type = static_cast<UnitType>(m_primitiveUnitType);
480     switch (type) {
481     case CSS_STRING:
482     case CSS_URI:
483     case CSS_ATTR:
484     case CSS_COUNTER_NAME:
485         if (m_value.string)
486             m_value.string->deref();
487         break;
488     case CSS_DIMENSION:
489     case CSS_COUNTER:
490         m_value.counter->deref();
491         break;
492     case CSS_RECT:
493         m_value.rect->deref();
494         break;
495     case CSS_QUAD:
496         m_value.quad->deref();
497         break;
498     case CSS_PAIR:
499         m_value.pair->deref();
500         break;
501 #if ENABLE(DASHBOARD_SUPPORT)
502     case CSS_DASHBOARD_REGION:
503         if (m_value.region)
504             m_value.region->deref();
505         break;
506 #endif
507     case CSS_CALC:
508         m_value.calc->deref();
509         break;
510     case CSS_CALC_PERCENTAGE_WITH_NUMBER:
511     case CSS_CALC_PERCENTAGE_WITH_LENGTH:
512         ASSERT_NOT_REACHED();
513         break;
514     case CSS_SHAPE:
515         m_value.shape->deref();
516         break;
517     case CSS_FONT_FAMILY:
518         ASSERT(m_value.fontFamily);
519         delete m_value.fontFamily;
520         m_value.fontFamily = nullptr;
521         break;
522     case CSS_RGBCOLOR:
523         ASSERT(m_value.color);
524         delete m_value.color;
525         m_value.color = nullptr;
526         break;
527     case CSS_NUMBER:
528     case CSS_PERCENTAGE:
529     case CSS_EMS:
530     case CSS_QUIRKY_EMS:
531     case CSS_EXS:
532     case CSS_REMS:
533     case CSS_CHS:
534     case CSS_PX:
535     case CSS_CM:
536     case CSS_MM:
537     case CSS_IN:
538     case CSS_PT:
539     case CSS_PC:
540     case CSS_DEG:
541     case CSS_RAD:
542     case CSS_GRAD:
543     case CSS_MS:
544     case CSS_S:
545     case CSS_HZ:
546     case CSS_KHZ:
547     case CSS_TURN:
548     case CSS_VW:
549     case CSS_VH:
550     case CSS_VMIN:
551     case CSS_VMAX:
552     case CSS_DPPX:
553     case CSS_DPI:
554     case CSS_DPCM:
555     case CSS_FR:
556     case CSS_IDENT:
557     case CSS_UNKNOWN:
558     case CSS_UNICODE_RANGE:
559     case CSS_PROPERTY_ID:
560     case CSS_VALUE_ID:
561         ASSERT(!isStringType(type));
562         break;
563     }
564     m_primitiveUnitType = 0;
565     if (m_hasCachedCSSText) {
566         cssTextCache().remove(this);
567         m_hasCachedCSSText = false;
568     }
569 }
570
571 double CSSPrimitiveValue::computeDegrees() const
572 {
573     switch (primitiveType()) {
574     case CSS_DEG:
575         return doubleValue();
576     case CSS_RAD:
577         return rad2deg(doubleValue());
578     case CSS_GRAD:
579         return grad2deg(doubleValue());
580     case CSS_TURN:
581         return turn2deg(doubleValue());
582     default:
583         ASSERT_NOT_REACHED();
584         return 0;
585     }
586 }
587
588 template<> int CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
589 {
590     return roundForImpreciseConversion<int>(computeLengthDouble(conversionData));
591 }
592
593 template<> unsigned CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
594 {
595     return roundForImpreciseConversion<unsigned>(computeLengthDouble(conversionData));
596 }
597
598 template<> Length CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
599 {
600     return Length(clampTo<float>(computeLengthDouble(conversionData), minValueForCssLength, maxValueForCssLength), Fixed);
601 }
602
603 template<> short CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
604 {
605     return roundForImpreciseConversion<short>(computeLengthDouble(conversionData));
606 }
607
608 template<> unsigned short CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
609 {
610     return roundForImpreciseConversion<unsigned short>(computeLengthDouble(conversionData));
611 }
612
613 template<> float CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
614 {
615     return static_cast<float>(computeLengthDouble(conversionData));
616 }
617
618 template<> double CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
619 {
620     return computeLengthDouble(conversionData);
621 }
622
623 double CSSPrimitiveValue::computeLengthDouble(const CSSToLengthConversionData& conversionData) const
624 {
625     if (m_primitiveUnitType == CSS_CALC)
626         // The multiplier and factor is applied to each value in the calc expression individually
627         return m_value.calc->computeLengthPx(conversionData);
628
629     return computeNonCalcLengthDouble(conversionData, static_cast<UnitType>(primitiveType()), m_value.num);
630 }
631
632 double CSSPrimitiveValue::computeNonCalcLengthDouble(const CSSToLengthConversionData& conversionData, UnitType primitiveType, double value)
633 {
634     double factor;
635     bool applyZoom = true;
636
637     switch (primitiveType) {
638     case CSS_EMS:
639     case CSS_QUIRKY_EMS:
640         ASSERT(conversionData.style());
641         factor = conversionData.computingFontSize() ? conversionData.style()->fontDescription().specifiedSize() : conversionData.style()->fontDescription().computedSize();
642         break;
643     case CSS_EXS:
644         ASSERT(conversionData.style());
645         // FIXME: We have a bug right now where the zoom will be applied twice to EX units.
646         // We really need to compute EX using fontMetrics for the original specifiedSize and not use
647         // our actual constructed rendering font.
648         if (conversionData.style()->fontMetrics().hasXHeight())
649             factor = conversionData.style()->fontMetrics().xHeight();
650         else
651             factor = (conversionData.computingFontSize() ? conversionData.style()->fontDescription().specifiedSize() : conversionData.style()->fontDescription().computedSize()) / 2.0;
652         break;
653     case CSS_REMS:
654         if (conversionData.rootStyle())
655             factor = conversionData.computingFontSize() ? conversionData.rootStyle()->fontDescription().specifiedSize() : conversionData.rootStyle()->fontDescription().computedSize();
656         else
657             factor = 1.0;
658         break;
659     case CSS_CHS:
660         ASSERT(conversionData.style());
661         factor = conversionData.style()->fontMetrics().zeroWidth();
662         break;
663     case CSS_PX:
664         factor = 1.0;
665         break;
666     case CSS_CM:
667         factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
668         break;
669     case CSS_MM:
670         factor = cssPixelsPerInch / 25.4;
671         break;
672     case CSS_IN:
673         factor = cssPixelsPerInch;
674         break;
675     case CSS_PT:
676         factor = cssPixelsPerInch / 72.0;
677         break;
678     case CSS_PC:
679         // 1 pc == 12 pt
680         factor = cssPixelsPerInch * 12.0 / 72.0;
681         break;
682     case CSS_CALC_PERCENTAGE_WITH_LENGTH:
683     case CSS_CALC_PERCENTAGE_WITH_NUMBER:
684         ASSERT_NOT_REACHED();
685         return -1.0;
686     case CSS_VH:
687         factor = conversionData.viewportHeightFactor();
688         applyZoom = false;
689         break;
690     case CSS_VW:
691         factor = conversionData.viewportWidthFactor();
692         applyZoom = false;
693         break;
694     case CSS_VMAX:
695         factor = conversionData.viewportMaxFactor();
696         applyZoom = false;
697         break;
698     case CSS_VMIN:
699         factor = conversionData.viewportMinFactor();
700         applyZoom = false;
701         break;
702     default:
703         ASSERT_NOT_REACHED();
704         return -1.0;
705     }
706
707     // We do not apply the zoom factor when we are computing the value of the font-size property. The zooming
708     // for font sizes is much more complicated, since we have to worry about enforcing the minimum font size preference
709     // as well as enforcing the implicit "smart minimum."
710     double result = value * factor;
711     if (conversionData.computingFontSize() || isFontRelativeLength(primitiveType))
712         return result;
713
714     if (applyZoom)
715         result *= conversionData.zoom();
716
717     return result;
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 Optional<double> CSSPrimitiveValue::doubleValueInternal(UnitType requestedUnitType) const
826 {
827     if (!isValidCSSUnitTypeForDoubleConversion(static_cast<UnitType>(m_primitiveUnitType)) || !isValidCSSUnitTypeForDoubleConversion(requestedUnitType))
828         return WTF::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 WTF::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 WTF::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 WTF::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 // https://drafts.css-houdini.org/css-properties-values-api/#dependency-cycles-via-relative-units
1224 void CSSPrimitiveValue::collectDirectComputationalDependencies(HashSet<CSSPropertyID>& values) const
1225 {
1226     switch (m_primitiveUnitType) {
1227     case CSS_EMS:
1228     case CSS_QUIRKY_EMS:
1229     case CSS_EXS:
1230     case CSS_CHS:
1231         values.add(CSSPropertyFontSize);
1232         break;
1233     case CSS_CALC:
1234         m_value.calc->collectDirectComputationalDependencies(values);
1235         break;
1236     }
1237 }
1238
1239 void CSSPrimitiveValue::collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& values) const
1240 {
1241     switch (m_primitiveUnitType) {
1242     case CSS_REMS:
1243         values.add(CSSPropertyFontSize);
1244         break;
1245     case CSS_CALC:
1246         m_value.calc->collectDirectRootComputationalDependencies(values);
1247         break;
1248     }
1249 }
1250
1251 } // namespace WebCore