Viewport unit values affected by Comand-+ zoom
[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     bool applyZoom = true;
644
645     switch (primitiveType) {
646     case CSS_EMS:
647     case CSS_QUIRKY_EMS:
648         ASSERT(conversionData.style());
649         factor = conversionData.computingFontSize() ? conversionData.style()->fontDescription().specifiedSize() : conversionData.style()->fontDescription().computedSize();
650         break;
651     case CSS_EXS:
652         ASSERT(conversionData.style());
653         // FIXME: We have a bug right now where the zoom will be applied twice to EX units.
654         // We really need to compute EX using fontMetrics for the original specifiedSize and not use
655         // our actual constructed rendering font.
656         if (conversionData.style()->fontMetrics().hasXHeight())
657             factor = conversionData.style()->fontMetrics().xHeight();
658         else
659             factor = (conversionData.computingFontSize() ? conversionData.style()->fontDescription().specifiedSize() : conversionData.style()->fontDescription().computedSize()) / 2.0;
660         break;
661     case CSS_REMS:
662         if (conversionData.rootStyle())
663             factor = conversionData.computingFontSize() ? conversionData.rootStyle()->fontDescription().specifiedSize() : conversionData.rootStyle()->fontDescription().computedSize();
664         else
665             factor = 1.0;
666         break;
667     case CSS_CHS:
668         ASSERT(conversionData.style());
669         factor = conversionData.style()->fontMetrics().zeroWidth();
670         break;
671     case CSS_PX:
672         factor = 1.0;
673         break;
674     case CSS_CM:
675         factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
676         break;
677     case CSS_MM:
678         factor = cssPixelsPerInch / 25.4;
679         break;
680     case CSS_IN:
681         factor = cssPixelsPerInch;
682         break;
683     case CSS_PT:
684         factor = cssPixelsPerInch / 72.0;
685         break;
686     case CSS_PC:
687         // 1 pc == 12 pt
688         factor = cssPixelsPerInch * 12.0 / 72.0;
689         break;
690     case CSS_CALC_PERCENTAGE_WITH_LENGTH:
691     case CSS_CALC_PERCENTAGE_WITH_NUMBER:
692         ASSERT_NOT_REACHED();
693         return -1.0;
694     case CSS_VH:
695         factor = conversionData.viewportHeightFactor();
696         applyZoom = false;
697         break;
698     case CSS_VW:
699         factor = conversionData.viewportWidthFactor();
700         applyZoom = false;
701         break;
702     case CSS_VMAX:
703         factor = conversionData.viewportMaxFactor();
704         applyZoom = false;
705         break;
706     case CSS_VMIN:
707         factor = conversionData.viewportMinFactor();
708         applyZoom = false;
709         break;
710     default:
711         ASSERT_NOT_REACHED();
712         return -1.0;
713     }
714
715     // We do not apply the zoom factor when we are computing the value of the font-size property. The zooming
716     // for font sizes is much more complicated, since we have to worry about enforcing the minimum font size preference
717     // as well as enforcing the implicit "smart minimum."
718     double result = value * factor;
719     if (conversionData.computingFontSize() || isFontRelativeLength(primitiveType))
720         return result;
721
722     if (applyZoom)
723         result *= conversionData.zoom();
724
725     return result;
726 }
727
728 ExceptionOr<void> CSSPrimitiveValue::setFloatValue(unsigned short, double)
729 {
730     // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects.
731     // No other engine supports mutating style through this API. Computed style is always read-only anyway.
732     // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
733     return Exception { NoModificationAllowedError };
734 }
735
736 double CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(UnitType unitType)
737 {
738     double factor = 1.0;
739     // FIXME: the switch can be replaced by an array of scale factors.
740     switch (unitType) {
741     // These are "canonical" units in their respective categories.
742     case CSS_PX:
743     case CSS_DEG:
744     case CSS_MS:
745     case CSS_HZ:
746         break;
747     case CSS_CM:
748         factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
749         break;
750     case CSS_DPCM:
751         factor = 2.54 / cssPixelsPerInch; // (2.54 cm/in)
752         break;
753     case CSS_MM:
754         factor = cssPixelsPerInch / 25.4;
755         break;
756     case CSS_IN:
757         factor = cssPixelsPerInch;
758         break;
759     case CSS_DPI:
760         factor = 1 / cssPixelsPerInch;
761         break;
762     case CSS_PT:
763         factor = cssPixelsPerInch / 72.0;
764         break;
765     case CSS_PC:
766         factor = cssPixelsPerInch * 12.0 / 72.0; // 1 pc == 12 pt
767         break;
768     case CSS_RAD:
769         factor = 180 / piDouble;
770         break;
771     case CSS_GRAD:
772         factor = 0.9;
773         break;
774     case CSS_TURN:
775         factor = 360;
776         break;
777     case CSS_S:
778     case CSS_KHZ:
779         factor = 1000;
780         break;
781     default:
782         break;
783     }
784
785     return factor;
786 }
787
788 ExceptionOr<float> CSSPrimitiveValue::getFloatValue(unsigned short unitType) const
789 {
790     auto result = doubleValueInternal(static_cast<UnitType>(unitType));
791     if (!result)
792         return Exception { InvalidAccessError };
793     return clampTo<float>(result.value());
794 }
795
796 double CSSPrimitiveValue::doubleValue(UnitType unitType) const
797 {
798     return doubleValueInternal(unitType).value_or(0);
799 }
800
801 double CSSPrimitiveValue::doubleValue() const
802 {
803     return m_primitiveUnitType != CSS_CALC ? m_value.num : m_value.calc->doubleValue();
804 }
805
806
807 CSSPrimitiveValue::UnitType CSSPrimitiveValue::canonicalUnitTypeForCategory(UnitCategory category)
808 {
809     // The canonical unit type is chosen according to the way CSSParser::validUnit() chooses the default unit
810     // in each category (based on unitflags).
811     switch (category) {
812     case UNumber:
813         return CSS_NUMBER;
814     case ULength:
815         return CSS_PX;
816     case UPercent:
817         return CSS_UNKNOWN; // Cannot convert between numbers and percent.
818     case UTime:
819         return CSS_MS;
820     case UAngle:
821         return CSS_DEG;
822     case UFrequency:
823         return CSS_HZ;
824 #if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
825     case UResolution:
826         return CSS_DPPX;
827 #endif
828     default:
829         return CSS_UNKNOWN;
830     }
831 }
832
833 std::optional<double> CSSPrimitiveValue::doubleValueInternal(UnitType requestedUnitType) const
834 {
835     if (!isValidCSSUnitTypeForDoubleConversion(static_cast<UnitType>(m_primitiveUnitType)) || !isValidCSSUnitTypeForDoubleConversion(requestedUnitType))
836         return std::nullopt;
837
838     UnitType sourceUnitType = static_cast<UnitType>(primitiveType());
839     if (requestedUnitType == sourceUnitType || requestedUnitType == CSS_DIMENSION)
840         return doubleValue();
841
842     UnitCategory sourceCategory = unitCategory(sourceUnitType);
843     ASSERT(sourceCategory != UOther);
844
845     UnitType targetUnitType = requestedUnitType;
846     UnitCategory targetCategory = unitCategory(targetUnitType);
847     ASSERT(targetCategory != UOther);
848
849     // Cannot convert between unrelated unit categories if one of them is not UNumber.
850     if (sourceCategory != targetCategory && sourceCategory != UNumber && targetCategory != UNumber)
851         return std::nullopt;
852
853     if (targetCategory == UNumber) {
854         // We interpret conversion to CSS_NUMBER as conversion to a canonical unit in this value's category.
855         targetUnitType = canonicalUnitTypeForCategory(sourceCategory);
856         if (targetUnitType == CSS_UNKNOWN)
857             return std::nullopt;
858     }
859
860     if (sourceUnitType == CSS_NUMBER) {
861         // We interpret conversion from CSS_NUMBER in the same way as CSSParser::validUnit() while using non-strict mode.
862         sourceUnitType = canonicalUnitTypeForCategory(targetCategory);
863         if (sourceUnitType == CSS_UNKNOWN)
864             return std::nullopt;
865     }
866
867     double convertedValue = doubleValue();
868
869     // First convert the value from m_primitiveUnitType to canonical type.
870     double factor = conversionToCanonicalUnitsScaleFactor(sourceUnitType);
871     convertedValue *= factor;
872
873     // Now convert from canonical type to the target unitType.
874     factor = conversionToCanonicalUnitsScaleFactor(targetUnitType);
875     convertedValue /= factor;
876
877     return convertedValue;
878 }
879
880 ExceptionOr<void> CSSPrimitiveValue::setStringValue(unsigned short, const String&)
881 {
882     // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects.
883     // No other engine supports mutating style through this API. Computed style is always read-only anyway.
884     // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
885     return Exception { NoModificationAllowedError };
886 }
887
888 ExceptionOr<String> CSSPrimitiveValue::getStringValue() const
889 {
890     switch (m_primitiveUnitType) {
891     case CSS_STRING:
892     case CSS_ATTR:
893     case CSS_URI:
894         return m_value.string;
895     case CSS_FONT_FAMILY:
896         return String { m_value.fontFamily->familyName };
897     case CSS_VALUE_ID:
898         return String { valueName(m_value.valueID).string() };
899     case CSS_PROPERTY_ID:
900         return String { propertyName(m_value.propertyID).string() };
901     default:
902         return Exception { InvalidAccessError };
903     }
904 }
905
906 String CSSPrimitiveValue::stringValue() const
907 {
908     switch (m_primitiveUnitType) {
909     case CSS_STRING:
910     case CSS_ATTR:
911     case CSS_URI:
912         return m_value.string;
913     case CSS_FONT_FAMILY:
914         return m_value.fontFamily->familyName;
915     case CSS_VALUE_ID:
916         return valueName(m_value.valueID);
917     case CSS_PROPERTY_ID:
918         return propertyName(m_value.propertyID);
919     default:
920         return String();
921     }
922 }
923
924 ExceptionOr<Counter&> CSSPrimitiveValue::getCounterValue() const
925 {
926     if (m_primitiveUnitType != CSS_COUNTER)
927         return Exception { InvalidAccessError };
928     return *m_value.counter;
929 }
930
931 ExceptionOr<Rect&> CSSPrimitiveValue::getRectValue() const
932 {
933     if (m_primitiveUnitType != CSS_RECT)
934         return Exception { InvalidAccessError };
935     return *m_value.rect;
936 }
937
938 ExceptionOr<Ref<RGBColor>> CSSPrimitiveValue::getRGBColorValue() const
939 {
940     if (m_primitiveUnitType != CSS_RGBCOLOR)
941         return Exception { InvalidAccessError };
942
943     // FIXME: This should not return a new object for each invocation.
944     return RGBColor::create(m_value.color->rgb());
945 }
946
947 NEVER_INLINE Ref<StringImpl> CSSPrimitiveValue::formatNumberValue(const char* suffix, unsigned suffixLength) const
948 {
949     DecimalNumber decimal(m_value.num);
950
951     unsigned bufferLength = decimal.bufferLengthForStringDecimal() + suffixLength;
952     LChar* buffer;
953     auto string = StringImpl::createUninitialized(bufferLength, buffer);
954
955     unsigned length = decimal.toStringDecimal(buffer, bufferLength);
956
957     for (unsigned i = 0; i < suffixLength; ++i)
958         buffer[length + i] = static_cast<LChar>(suffix[i]);
959
960     return string;
961 }
962
963 template <unsigned characterCount>
964 ALWAYS_INLINE Ref<StringImpl> CSSPrimitiveValue::formatNumberValue(const char (&characters)[characterCount]) const
965 {
966     return formatNumberValue(characters, characterCount - 1);
967 }
968
969 ALWAYS_INLINE String CSSPrimitiveValue::formatNumberForCustomCSSText() const
970 {
971     switch (m_primitiveUnitType) {
972     case CSS_UNKNOWN:
973         return String();
974     case CSS_NUMBER:
975         return formatNumberValue("");
976     case CSS_PERCENTAGE:
977         return formatNumberValue("%");
978     case CSS_EMS:
979     case CSS_QUIRKY_EMS:
980         return formatNumberValue("em");
981     case CSS_EXS:
982         return formatNumberValue("ex");
983     case CSS_REMS:
984         return formatNumberValue("rem");
985     case CSS_CHS:
986         return formatNumberValue("ch");
987     case CSS_PX:
988         return formatNumberValue("px");
989     case CSS_CM:
990         return formatNumberValue("cm");
991 #if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
992     case CSS_DPPX:
993         return formatNumberValue("dppx");
994     case CSS_DPI:
995         return formatNumberValue("dpi");
996     case CSS_DPCM:
997         return formatNumberValue("dpcm");
998 #endif
999     case CSS_MM:
1000         return formatNumberValue("mm");
1001     case CSS_IN:
1002         return formatNumberValue("in");
1003     case CSS_PT:
1004         return formatNumberValue("pt");
1005     case CSS_PC:
1006         return formatNumberValue("pc");
1007     case CSS_DEG:
1008         return formatNumberValue("deg");
1009     case CSS_RAD:
1010         return formatNumberValue("rad");
1011     case CSS_GRAD:
1012         return formatNumberValue("grad");
1013     case CSS_MS:
1014         return formatNumberValue("ms");
1015     case CSS_S:
1016         return formatNumberValue("s");
1017     case CSS_HZ:
1018         return formatNumberValue("hz");
1019     case CSS_KHZ:
1020         return formatNumberValue("khz");
1021     case CSS_TURN:
1022         return formatNumberValue("turn");
1023     case CSS_FR:
1024         return formatNumberValue("fr");
1025     case CSS_DIMENSION:
1026         // FIXME: We currently don't handle CSS_DIMENSION properly as we don't store
1027         // the actual dimension, just the numeric value as a string.
1028     case CSS_STRING:
1029         // FIME-NEWPARSER: Once we have CSSCustomIdentValue hooked up, this can just be
1030         // serializeString, since custom identifiers won't be the same value as strings
1031         // any longer.
1032         return serializeAsStringOrCustomIdent(m_value.string);
1033     case CSS_FONT_FAMILY:
1034         return serializeFontFamily(m_value.fontFamily->familyName);
1035     case CSS_URI:
1036         return serializeURL(m_value.string);
1037     case CSS_VALUE_ID:
1038         return valueName(m_value.valueID);
1039     case CSS_PROPERTY_ID:
1040         return propertyName(m_value.propertyID);
1041     case CSS_ATTR: {
1042         StringBuilder result;
1043         result.reserveCapacity(6 + m_value.string->length());
1044         result.appendLiteral("attr(");
1045         result.append(String(m_value.string));
1046         result.append(')');
1047
1048         return result.toString();
1049     }
1050     case CSS_COUNTER_NAME:
1051         return "counter(" + String(m_value.string) + ')';
1052     case CSS_COUNTER: {
1053         StringBuilder result;
1054         String separator = m_value.counter->separator();
1055         if (separator.isEmpty())
1056             result.appendLiteral("counter(");
1057         else
1058             result.appendLiteral("counters(");
1059
1060         result.append(m_value.counter->identifier());
1061         if (!separator.isEmpty()) {
1062             result.appendLiteral(", ");
1063             serializeString(separator, result);
1064         }
1065         String listStyle = m_value.counter->listStyle();
1066         if (!listStyle.isEmpty()) {
1067             result.appendLiteral(", ");
1068             result.append(listStyle);
1069         }
1070         result.append(')');
1071
1072         return result.toString();
1073     }
1074     case CSS_RECT:
1075         return rectValue()->cssText();
1076     case CSS_QUAD:
1077         return quadValue()->cssText();
1078     case CSS_RGBCOLOR:
1079         return color().cssText();
1080     case CSS_PAIR:
1081         return pairValue()->cssText();
1082 #if ENABLE(DASHBOARD_SUPPORT)
1083     case CSS_DASHBOARD_REGION: {
1084         StringBuilder result;
1085         for (DashboardRegion* region = dashboardRegionValue(); region; region = region->m_next.get()) {
1086             if (!result.isEmpty())
1087                 result.append(' ');
1088             result.appendLiteral("dashboard-region(");
1089             result.append(region->m_label);
1090             if (region->m_isCircle)
1091                 result.appendLiteral(" circle");
1092             else if (region->m_isRectangle)
1093                 result.appendLiteral(" rectangle");
1094             else
1095                 break;
1096             if (region->top()->m_primitiveUnitType == CSS_VALUE_ID && region->top()->valueID() == CSSValueInvalid) {
1097                 ASSERT(region->right()->m_primitiveUnitType == CSS_VALUE_ID);
1098                 ASSERT(region->bottom()->m_primitiveUnitType == CSS_VALUE_ID);
1099                 ASSERT(region->left()->m_primitiveUnitType == CSS_VALUE_ID);
1100                 ASSERT(region->right()->valueID() == CSSValueInvalid);
1101                 ASSERT(region->bottom()->valueID() == CSSValueInvalid);
1102                 ASSERT(region->left()->valueID() == CSSValueInvalid);
1103             } else {
1104                 result.append(' ');
1105                 result.append(region->top()->cssText());
1106                 result.append(' ');
1107                 result.append(region->right()->cssText());
1108                 result.append(' ');
1109                 result.append(region->bottom()->cssText());
1110                 result.append(' ');
1111                 result.append(region->left()->cssText());
1112             }
1113             result.append(')');
1114         }
1115         return result.toString();
1116     }
1117 #endif
1118     case CSS_CALC:
1119         return m_value.calc->cssText();
1120     case CSS_SHAPE:
1121         return m_value.shape->cssText();
1122     case CSS_VW:
1123         return formatNumberValue("vw");
1124     case CSS_VH:
1125         return formatNumberValue("vh");
1126     case CSS_VMIN:
1127         return formatNumberValue("vmin");
1128     case CSS_VMAX:
1129         return formatNumberValue("vmax");
1130     }
1131     return String();
1132 }
1133
1134 String CSSPrimitiveValue::customCSSText() const
1135 {
1136     // FIXME: return the original value instead of a generated one (e.g. color
1137     // name if it was specified) - check what spec says about this
1138
1139     CSSTextCache& cssTextCache = WebCore::cssTextCache();
1140
1141     if (m_hasCachedCSSText) {
1142         ASSERT(cssTextCache.contains(this));
1143         return cssTextCache.get(this);
1144     }
1145
1146     String text = formatNumberForCustomCSSText();
1147
1148     ASSERT(!cssTextCache.contains(this));
1149     m_hasCachedCSSText = true;
1150     cssTextCache.set(this, text);
1151     return text;
1152 }
1153
1154 bool CSSPrimitiveValue::equals(const CSSPrimitiveValue& other) const
1155 {
1156     if (m_primitiveUnitType != other.m_primitiveUnitType)
1157         return false;
1158
1159     switch (m_primitiveUnitType) {
1160     case CSS_UNKNOWN:
1161         return false;
1162     case CSS_NUMBER:
1163     case CSS_PERCENTAGE:
1164     case CSS_EMS:
1165     case CSS_QUIRKY_EMS:
1166     case CSS_EXS:
1167     case CSS_REMS:
1168     case CSS_PX:
1169     case CSS_CM:
1170 #if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
1171     case CSS_DPPX:
1172     case CSS_DPI:
1173     case CSS_DPCM:
1174 #endif
1175     case CSS_MM:
1176     case CSS_IN:
1177     case CSS_PT:
1178     case CSS_PC:
1179     case CSS_DEG:
1180     case CSS_RAD:
1181     case CSS_GRAD:
1182     case CSS_MS:
1183     case CSS_S:
1184     case CSS_HZ:
1185     case CSS_KHZ:
1186     case CSS_TURN:
1187     case CSS_VW:
1188     case CSS_VH:
1189     case CSS_VMIN:
1190     case CSS_FR:
1191         return m_value.num == other.m_value.num;
1192     case CSS_PROPERTY_ID:
1193         return propertyName(m_value.propertyID) == propertyName(other.m_value.propertyID);
1194     case CSS_VALUE_ID:
1195         return valueName(m_value.valueID) == valueName(other.m_value.valueID);
1196     case CSS_DIMENSION:
1197     case CSS_STRING:
1198     case CSS_URI:
1199     case CSS_ATTR:
1200     case CSS_COUNTER_NAME:
1201         return equal(m_value.string, other.m_value.string);
1202     case CSS_COUNTER:
1203         return m_value.counter && other.m_value.counter && m_value.counter->equals(*other.m_value.counter);
1204     case CSS_RECT:
1205         return m_value.rect && other.m_value.rect && m_value.rect->equals(*other.m_value.rect);
1206     case CSS_QUAD:
1207         return m_value.quad && other.m_value.quad && m_value.quad->equals(*other.m_value.quad);
1208     case CSS_RGBCOLOR:
1209         return color() == other.color();
1210     case CSS_PAIR:
1211         return m_value.pair && other.m_value.pair && m_value.pair->equals(*other.m_value.pair);
1212 #if ENABLE(DASHBOARD_SUPPORT)
1213     case CSS_DASHBOARD_REGION:
1214         return m_value.region && other.m_value.region && m_value.region->equals(*other.m_value.region);
1215 #endif
1216     case CSS_CALC:
1217         return m_value.calc && other.m_value.calc && m_value.calc->equals(*other.m_value.calc);
1218     case CSS_SHAPE:
1219         return m_value.shape && other.m_value.shape && m_value.shape->equals(*other.m_value.shape);
1220     case CSS_FONT_FAMILY:
1221         return fontFamily() == other.fontFamily();
1222     }
1223     return false;
1224 }
1225
1226 Ref<DeprecatedCSSOMPrimitiveValue> CSSPrimitiveValue::createDeprecatedCSSOMPrimitiveWrapper(CSSStyleDeclaration& styleDeclaration) const
1227 {
1228     return DeprecatedCSSOMPrimitiveValue::create(*this, styleDeclaration);
1229 }
1230
1231 } // namespace WebCore