Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcssparser.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qcssparser_p.h"
6
7#include <QtCore/qmap.h>
8#include <qdebug.h>
9#include <qicon.h>
10#include <qcolor.h>
11#include <qfont.h>
12#include <qfileinfo.h>
13#include <qfontmetrics.h>
14#include <qbrush.h>
15#include <qimagereader.h>
16#include <qtextformat.h>
17
18#include <QtCore/q20algorithm.h>
19
20#ifndef QT_NO_CSSPARSER
21
23
24using namespace Qt::StringLiterals;
25
26QT_IMPL_METATYPE_EXTERN_TAGGED(QCss::BackgroundData, QCss__BackgroundData)
27QT_IMPL_METATYPE_EXTERN_TAGGED(QCss::LengthData, QCss__LengthData)
28QT_IMPL_METATYPE_EXTERN_TAGGED(QCss::BorderData, QCss__BorderData)
29
30#include "qcssscanner.cpp"
31
32using namespace QCss;
33
35{
36 const char name[28];
38
39 struct ByName;
40};
41
43{
44 constexpr bool operator()(const QCssKnownValue &lhs, const QCssKnownValue &rhs) const noexcept
45 { return std::string_view{lhs.name} < std::string_view{rhs.name}; }
46};
47#if !defined(Q_CC_GNU_ONLY) || Q_CC_GNU >= 1000
48# define NOT_OLD_GCCs(...) __VA_ARGS__
49#else
50# define NOT_OLD_GCCs(...) /* nothing */
51#endif
52#define CHECK_ARRAY_IS_SORTED(array, Num)
53 static_assert(std::size(array) == Num);
54 NOT_OLD_GCCs(
55 static_assert(q20::is_sorted(std::begin(array), std::end(array),
56 QCssKnownValue::ByName{}));
57 ) /* NOT_OLD_GCCs */
58 /* end */
59
60// This array is sorted alphabetically.
61static constexpr QCssKnownValue properties[] = {
62 { "-qt-background-role", QtBackgroundRole },
63 { "-qt-block-indent", QtBlockIndent },
64 { "-qt-fg-texture-cachekey", QtForegroundTextureCacheKey },
65 { "-qt-foreground", QtForeground },
66 { "-qt-line-height-type", QtLineHeightType },
67 { "-qt-list-indent", QtListIndent },
68 { "-qt-list-number-prefix", QtListNumberPrefix },
69 { "-qt-list-number-suffix", QtListNumberSuffix },
70 { "-qt-paragraph-type", QtParagraphType },
71 { "-qt-stroke-color", QtStrokeColor },
72 { "-qt-stroke-dasharray", QtStrokeDashArray },
73 { "-qt-stroke-dashoffset", QtStrokeDashOffset },
74 { "-qt-stroke-linecap", QtStrokeLineCap },
75 { "-qt-stroke-linejoin", QtStrokeLineJoin },
76 { "-qt-stroke-miterlimit", QtStrokeMiterLimit },
77 { "-qt-stroke-width", QtStrokeWidth },
78 { "-qt-style-features", QtStyleFeatures },
79 { "-qt-table-type", QtTableType },
80 { "-qt-user-state", QtUserState },
81 { "accent-color", QtAccent },
82 { "alternate-background-color", QtAlternateBackground },
83 { "background", Background },
84 { "background-attachment", BackgroundAttachment },
85 { "background-clip", BackgroundClip },
86 { "background-color", BackgroundColor },
87 { "background-image", BackgroundImage },
88 { "background-origin", BackgroundOrigin },
89 { "background-position", BackgroundPosition },
90 { "background-repeat", BackgroundRepeat },
91 { "border", Border },
92 { "border-bottom", BorderBottom },
93 { "border-bottom-color", BorderBottomColor },
94 { "border-bottom-left-radius", BorderBottomLeftRadius },
95 { "border-bottom-right-radius", BorderBottomRightRadius },
96 { "border-bottom-style", BorderBottomStyle },
97 { "border-bottom-width", BorderBottomWidth },
98 { "border-collapse", BorderCollapse },
99 { "border-color", BorderColor },
100 { "border-image", BorderImage },
101 { "border-left", BorderLeft },
102 { "border-left-color", BorderLeftColor },
103 { "border-left-style", BorderLeftStyle },
104 { "border-left-width", BorderLeftWidth },
105 { "border-radius", BorderRadius },
106 { "border-right", BorderRight },
107 { "border-right-color", BorderRightColor },
108 { "border-right-style", BorderRightStyle },
109 { "border-right-width", BorderRightWidth },
110 { "border-style", BorderStyles },
111 { "border-top", BorderTop },
112 { "border-top-color", BorderTopColor },
113 { "border-top-left-radius", BorderTopLeftRadius },
114 { "border-top-right-radius", BorderTopRightRadius },
115 { "border-top-style", BorderTopStyle },
116 { "border-top-width", BorderTopWidth },
117 { "border-width", BorderWidth },
118 { "bottom", Bottom },
119 { "color", Color },
120 { "float", Float },
121 { "font", Font },
122 { "font-family", FontFamily },
123 { "font-kerning", FontKerning },
124 { "font-size", FontSize },
125 { "font-style", FontStyle },
126 { "font-variant", FontVariant },
127 { "font-weight", FontWeight },
128 { "height", Height },
129 { "icon", QtIcon },
130 { "image", QtImage },
131 { "image-position", QtImageAlignment },
132 { "left", Left },
133 { "letter-spacing", LetterSpacing },
134 { "line-height", LineHeight },
135 { "list-style", ListStyle },
136 { "list-style-type", ListStyleType },
137 { "margin" , Margin },
138 { "margin-bottom", MarginBottom },
139 { "margin-left", MarginLeft },
140 { "margin-right", MarginRight },
141 { "margin-top", MarginTop },
142 { "max-height", MaximumHeight },
143 { "max-width", MaximumWidth },
144 { "min-height", MinimumHeight },
145 { "min-width", MinimumWidth },
146 { "outline", Outline },
147 { "outline-bottom-left-radius", OutlineBottomLeftRadius },
148 { "outline-bottom-right-radius", OutlineBottomRightRadius },
149 { "outline-color", OutlineColor },
150 { "outline-offset", OutlineOffset },
151 { "outline-radius", OutlineRadius },
152 { "outline-style", OutlineStyle },
153 { "outline-top-left-radius", OutlineTopLeftRadius },
154 { "outline-top-right-radius", OutlineTopRightRadius },
155 { "outline-width", OutlineWidth },
156 { "padding", Padding },
157 { "padding-bottom", PaddingBottom },
158 { "padding-left", PaddingLeft },
159 { "padding-right", PaddingRight },
160 { "padding-top", PaddingTop },
161 { "page-break-after", PageBreakAfter },
162 { "page-break-before", PageBreakBefore },
163 { "placeholder-text-color", QtPlaceHolderTextColor },
164 { "position", Position },
165 { "right", Right },
166 { "selection-background-color", QtSelectionBackground },
167 { "selection-color", QtSelectionForeground },
168 { "spacing", QtSpacing },
169 { "subcontrol-origin", QtOrigin },
170 { "subcontrol-position", QtPosition },
171 { "text-align", TextAlignment },
172 { "text-decoration", TextDecoration },
173 { "text-decoration-color", TextDecorationColor },
174 { "text-indent", TextIndent },
175 { "text-transform", TextTransform },
176 { "text-underline-style", TextUnderlineStyle },
177 { "top", Top },
178 { "vertical-align", VerticalAlignment },
179 { "white-space", Whitespace },
180 { "width", Width },
181 { "word-spacing", WordSpacing }
182};
183CHECK_ARRAY_IS_SORTED(properties, size_t(NumProperties) - 1)
184
185static constexpr QCssKnownValue values[] = {
186 { "accent", Value_Accent },
187 { "active", Value_Active },
188 { "alternate-base", Value_AlternateBase },
189 { "always", Value_Always },
190 { "auto", Value_Auto },
191 { "base", Value_Base },
192 { "beveljoin", Value_BevelJoin},
193 { "bold", Value_Bold },
194 { "bottom", Value_Bottom },
195 { "bright-text", Value_BrightText },
196 { "button", Value_Button },
197 { "button-text", Value_ButtonText },
198 { "center", Value_Center },
199 { "circle", Value_Circle },
200 { "dark", Value_Dark },
201 { "dashed", Value_Dashed },
202 { "decimal", Value_Decimal },
203 { "disabled", Value_Disabled },
204 { "disc", Value_Disc },
205 { "dot-dash", Value_DotDash },
206 { "dot-dot-dash", Value_DotDotDash },
207 { "dotted", Value_Dotted },
208 { "double", Value_Double },
209 { "flatcap", Value_FlatCap},
210 { "groove", Value_Groove },
211 { "highlight", Value_Highlight },
212 { "highlighted-text", Value_HighlightedText },
213 { "inset", Value_Inset },
214 { "italic", Value_Italic },
215 { "large", Value_Large },
216 { "left", Value_Left },
217 { "light", Value_Light },
218 { "line-through", Value_LineThrough },
219 { "link", Value_Link },
220 { "link-visited", Value_LinkVisited },
221 { "lower-alpha", Value_LowerAlpha },
222 { "lower-roman", Value_LowerRoman },
223 { "lowercase", Value_Lowercase },
224 { "medium", Value_Medium },
225 { "mid", Value_Mid },
226 { "middle", Value_Middle },
227 { "midlight", Value_Midlight },
228 { "miterjoin", Value_MiterJoin},
229 { "native", Value_Native },
230 { "no-role", Value_NoRole },
231 { "none", Value_None },
232 { "normal", Value_Normal },
233 { "nowrap", Value_NoWrap },
234 { "oblique", Value_Oblique },
235 { "off", Value_Off },
236 { "on", Value_On },
237 { "outset", Value_Outset },
238 { "overline", Value_Overline },
239 { "placeholder-text", Value_PlaceholderText },
240 { "pre", Value_Pre },
241 { "pre-line", Value_PreLine },
242 { "pre-wrap", Value_PreWrap },
243 { "ridge", Value_Ridge },
244 { "right", Value_Right },
245 { "roundcap", Value_RoundCap},
246 { "roundjoin", Value_RoundJoin},
247 { "selected", Value_Selected },
248 { "shadow", Value_Shadow },
249 { "small" , Value_Small },
250 { "small-caps", Value_SmallCaps },
251 { "solid", Value_Solid },
252 { "square", Value_Square },
253 { "squarecap", Value_SquareCap},
254 { "sub", Value_Sub },
255 { "super", Value_Super },
256 { "svgmiterjoin", Value_SvgMiterJoin},
257 { "text", Value_Text },
258 { "tooltip-base", Value_ToolTipBase },
259 { "tooltip-text", Value_ToolTipText },
260 { "top", Value_Top },
261 { "transparent", Value_Transparent },
262 { "underline", Value_Underline },
263 { "upper-alpha", Value_UpperAlpha },
264 { "upper-roman", Value_UpperRoman },
265 { "uppercase", Value_Uppercase },
266 { "wave", Value_Wave },
267 { "window", Value_Window },
268 { "window-text", Value_WindowText },
269 { "x-large", Value_XLarge },
270 { "xx-large", Value_XXLarge }
271};
272CHECK_ARRAY_IS_SORTED(values, size_t(NumKnownValues) - 1)
273
274//Map id to strings as they appears in the 'values' array above
275static constexpr uchar indexOfId[] = {
276 0, 46, 54, 47, 55, 56, 63, 38, 29, 83, 84, 28, 48, 7, 76, 52,
277 32, 68, 69, 30, 58, 74, 8, 12, 43, 65, 21, 15, 19, 20, 22, 24, 57, 27, 51, 80, 40, 4, 3, 45, 75, 18, 13,
278 66, 16, 35, 77, 36, 78, 64, 79, 37, 67, 23, 59, 42, 6, 60, 70, 82, 10, 31, 41, 14, 39, 71, 9, 11, 5, 81,
279 62, 25, 26, 33, 34, 2, 44, 72, 73, 53, 0, 17, 1, 61, 50, 49 };
280
281static_assert(std::size(indexOfId) == size_t(NumKnownValues));
282
283QString Value::toString() const
284{
285 if (type == KnownIdentifier) {
286 return QLatin1StringView(values[indexOfId[variant.toInt()]].name);
287 } else {
288 return variant.toString();
289 }
290}
291
292static constexpr QCssKnownValue pseudos[] = {
293 { "active", PseudoClass_Active },
294 { "adjoins-item", PseudoClass_Item },
295 { "alternate", PseudoClass_Alternate },
296 { "bottom", PseudoClass_Bottom },
297 { "checked", PseudoClass_Checked },
298 { "closable", PseudoClass_Closable },
299 { "closed", PseudoClass_Closed },
300 { "default", PseudoClass_Default },
301 { "disabled", PseudoClass_Disabled },
302 { "edit-focus", PseudoClass_EditFocus },
303 { "editable", PseudoClass_Editable },
304 { "enabled", PseudoClass_Enabled },
305 { "exclusive", PseudoClass_Exclusive },
306 { "first", PseudoClass_First },
307 { "flat", PseudoClass_Flat },
308 { "floatable", PseudoClass_Floatable },
309 { "focus", PseudoClass_Focus },
310 { "has-children", PseudoClass_Children },
311 { "has-siblings", PseudoClass_Sibling },
312 { "horizontal", PseudoClass_Horizontal },
313 { "hover", PseudoClass_Hover },
314 { "indeterminate" , PseudoClass_Indeterminate },
315 { "last", PseudoClass_Last },
316 { "left", PseudoClass_Left },
317 { "maximized", PseudoClass_Maximized },
318 { "middle", PseudoClass_Middle },
319 { "minimized", PseudoClass_Minimized },
320 { "movable", PseudoClass_Movable },
321 { "next-selected", PseudoClass_NextSelected },
322 { "no-frame", PseudoClass_Frameless },
323 { "non-exclusive", PseudoClass_NonExclusive },
324 { "off", PseudoClass_Unchecked },
325 { "on", PseudoClass_Checked },
326 { "only-one", PseudoClass_OnlyOne },
327 { "open", PseudoClass_Open },
328 { "pressed", PseudoClass_Pressed },
329 { "previous-selected", PseudoClass_PreviousSelected },
330 { "read-only", PseudoClass_ReadOnly },
331 { "right", PseudoClass_Right },
332 { "selected", PseudoClass_Selected },
333 { "top", PseudoClass_Top },
334 { "unchecked" , PseudoClass_Unchecked },
335 { "vertical", PseudoClass_Vertical },
336 { "window", PseudoClass_Window }
337};
338CHECK_ARRAY_IS_SORTED(pseudos, size_t(NumPseudos) - 1)
339
340static constexpr QCssKnownValue origins[] = {
341 { "border", Origin_Border },
342 { "content", Origin_Content },
343 { "margin", Origin_Margin }, // not in css
344 { "padding", Origin_Padding }
345};
346CHECK_ARRAY_IS_SORTED(origins, size_t(NumKnownOrigins) - 1)
347
348static constexpr QCssKnownValue repeats[] = {
349 { "no-repeat", Repeat_None },
350 { "repeat-x", Repeat_X },
351 { "repeat-xy", Repeat_XY },
352 { "repeat-y", Repeat_Y }
353};
354CHECK_ARRAY_IS_SORTED(repeats, size_t(NumKnownRepeats) - 1)
355
356static constexpr QCssKnownValue tileModes[] = {
357 { "repeat", TileMode_Repeat },
358 { "round", TileMode_Round },
359 { "stretch", TileMode_Stretch },
360};
361CHECK_ARRAY_IS_SORTED(tileModes, size_t(NumKnownTileModes) - 1)
362
363static constexpr QCssKnownValue positions[] = {
364 { "absolute", PositionMode_Absolute },
365 { "fixed", PositionMode_Fixed },
366 { "relative", PositionMode_Relative },
367 { "static", PositionMode_Static }
368};
369CHECK_ARRAY_IS_SORTED(positions, size_t(NumKnownPositionModes) - 1)
370
371static constexpr QCssKnownValue attachments[] = {
372 { "fixed", Attachment_Fixed },
373 { "scroll", Attachment_Scroll }
374};
375CHECK_ARRAY_IS_SORTED(attachments, size_t(NumKnownAttachments) - 1)
376
377static constexpr QCssKnownValue styleFeatures[] = {
378 { "background-color", StyleFeature_BackgroundColor },
379 { "background-gradient", StyleFeature_BackgroundGradient },
380 { "none", StyleFeature_None }
381};
382CHECK_ARRAY_IS_SORTED(styleFeatures, size_t(NumKnownStyleFeatures) - 1)
383
384static bool operator<(const QString &name, const QCssKnownValue &prop)
385{
386 return QString::compare(name, QLatin1StringView(prop.name), Qt::CaseInsensitive) < 0;
387}
388
389static bool operator<(const QCssKnownValue &prop, const QString &name)
390{
391 return QString::compare(QLatin1StringView(prop.name), name, Qt::CaseInsensitive) < 0;
392}
393
394#undef CHECK_ARRAY_IS_SORTED
395#undef NOT_OLD_GCCs
396
397static quint64 findKnownValue(const QString &name, const QCssKnownValue *start, int numValues)
398{
399 const QCssKnownValue *end = start + (numValues - 1);
400 const QCssKnownValue *prop = std::lower_bound(start, end, name);
401 if ((prop == end) || (name < *prop))
402 return 0;
403 return prop->id;
404}
405
406static inline bool isInheritable(Property propertyId)
407{
408 switch (propertyId) {
409 case Font:
410 case FontKerning:
411 case FontFamily:
412 case FontSize:
413 case FontStyle:
414 case FontWeight:
415 case TextIndent:
416 case Whitespace:
417 case ListStyleType:
418 case ListStyle:
419 case TextAlignment:
420 case FontVariant:
421 case TextTransform:
422 case LineHeight:
423 case LetterSpacing:
424 case WordSpacing:
425 return true;
426 default:
427 break;
428 }
429 return false;
430}
431
432///////////////////////////////////////////////////////////////////////////////
433// Value Extractor
434ValueExtractor::ValueExtractor(const QList<Declaration> &decls, const QPalette &pal)
435: declarations(decls), adjustment(0), fontExtracted(false), pal(pal)
436{
437}
438
439LengthData ValueExtractor::lengthValue(const Value& v)
440{
441 const QString str = v.variant.toString();
442 QStringView s(str);
443 LengthData data;
444 data.unit = LengthData::None;
445 if (s.endsWith(u"px", Qt::CaseInsensitive))
446 data.unit = LengthData::Px;
447 else if (s.endsWith(u"ex", Qt::CaseInsensitive))
448 data.unit = LengthData::Ex;
449 else if (s.endsWith(u"em", Qt::CaseInsensitive))
450 data.unit = LengthData::Em;
451
452 if (data.unit != LengthData::None)
453 s.chop(2);
454 else if (v.type == Value::Percentage)
455 data.unit = LengthData::Percent;
456
457 data.number = s.toDouble();
458 return data;
459}
460
461static int lengthValueFromData(const LengthData& data, const QFont& f)
462{
463 const int scale = (data.unit == LengthData::Ex ? QFontMetrics(f).xHeight()
464 : data.unit == LengthData::Em ? QFontMetrics(f).height() : 1);
465 // raised lower limit due to the implementation of qRound()
466 return qRound(qBound(double(INT_MIN) + 0.1, scale * data.number, double(INT_MAX)));
467}
468
469QTextLength ValueExtractor::textLength(const Declaration &decl)
470{
471 const LengthData data = lengthValue(decl.d->values.at(0));
472 if (data.unit == LengthData::Percent)
473 return QTextLength(QTextLength::PercentageLength, data.number);
474
475 return QTextLength(QTextLength::FixedLength, lengthValueFromData(data, f));
476}
477
478int ValueExtractor::lengthValue(const Declaration &decl)
479{
480 if (decl.d->parsed.isValid())
481 return lengthValueFromData(qvariant_cast<LengthData>(decl.d->parsed), f);
482 if (decl.d->values.size() < 1)
483 return 0;
484 LengthData data = lengthValue(decl.d->values.at(0));
485 decl.d->parsed = QVariant::fromValue<LengthData>(data);
486 return lengthValueFromData(data,f);
487}
488
489void ValueExtractor::lengthValues(const Declaration &decl, int *m)
490{
491 if (decl.d->parsed.isValid()) {
492 QList<QVariant> v = decl.d->parsed.toList();
493 Q_ASSERT(v.size() == 4);
494 for (int i = 0; i < 4; i++)
495 m[i] = lengthValueFromData(qvariant_cast<LengthData>(v.at(i)), f);
496 return;
497 }
498
499 LengthData datas[4];
500 int i;
501 for (i = 0; i < qMin(decl.d->values.size(), 4); i++)
502 datas[i] = lengthValue(decl.d->values[i]);
503
504 if (i == 0) {
505 LengthData zero = {0.0, LengthData::None};
506 datas[0] = datas[1] = datas[2] = datas[3] = zero;
507 } else if (i == 1) {
508 datas[3] = datas[2] = datas[1] = datas[0];
509 } else if (i == 2) {
510 datas[2] = datas[0];
511 datas[3] = datas[1];
512 } else if (i == 3) {
513 datas[3] = datas[1];
514 }
515
516 QList<QVariant> v;
517 v.reserve(4);
518 for (i = 0; i < 4; i++) {
519 v += QVariant::fromValue<LengthData>(datas[i]);
520 m[i] = lengthValueFromData(datas[i], f);
521 }
522 decl.d->parsed = v;
523}
524
525bool ValueExtractor::extractGeometry(int *w, int *h, int *minw, int *minh, int *maxw, int *maxh)
526{
527 extractFont();
528 bool hit = false;
529 for (int i = 0; i < declarations.size(); i++) {
530 const Declaration &decl = declarations.at(i);
531 switch (decl.d->propertyId) {
532 case Width: *w = lengthValue(decl); break;
533 case Height: *h = lengthValue(decl); break;
534 case MinimumWidth: *minw = lengthValue(decl); break;
535 case MinimumHeight: *minh = lengthValue(decl); break;
536 case MaximumWidth: *maxw = lengthValue(decl); break;
537 case MaximumHeight: *maxh = lengthValue(decl); break;
538 default: continue;
539 }
540 hit = true;
541 }
542
543 return hit;
544}
545
546bool ValueExtractor::extractPosition(int *left, int *top, int *right, int *bottom, QCss::Origin *origin,
547 Qt::Alignment *position, QCss::PositionMode *mode, Qt::Alignment *textAlignment)
548{
549 extractFont();
550 bool hit = false;
551 for (int i = 0; i < declarations.size(); i++) {
552 const Declaration &decl = declarations.at(i);
553 switch (decl.d->propertyId) {
554 case Left: *left = lengthValue(decl); break;
555 case Top: *top = lengthValue(decl); break;
556 case Right: *right = lengthValue(decl); break;
557 case Bottom: *bottom = lengthValue(decl); break;
558 case QtOrigin: *origin = decl.originValue(); break;
559 case QtPosition: *position = decl.alignmentValue(); break;
560 case TextAlignment: *textAlignment = decl.alignmentValue(); break;
561 case Position: *mode = decl.positionValue(); break;
562 default: continue;
563 }
564 hit = true;
565 }
566
567 return hit;
568}
569
570bool ValueExtractor::extractBox(int *margins, int *paddings, int *spacing)
571{
572 extractFont();
573 bool hit = false;
574 for (int i = 0; i < declarations.size(); i++) {
575 const Declaration &decl = declarations.at(i);
576 switch (decl.d->propertyId) {
577 case PaddingLeft: paddings[LeftEdge] = lengthValue(decl); break;
578 case PaddingRight: paddings[RightEdge] = lengthValue(decl); break;
579 case PaddingTop: paddings[TopEdge] = lengthValue(decl); break;
580 case PaddingBottom: paddings[BottomEdge] = lengthValue(decl); break;
581 case Padding: lengthValues(decl, paddings); break;
582
583 case MarginLeft: margins[LeftEdge] = lengthValue(decl); break;
584 case MarginRight: margins[RightEdge] = lengthValue(decl); break;
585 case MarginTop: margins[TopEdge] = lengthValue(decl); break;
586 case MarginBottom: margins[BottomEdge] = lengthValue(decl); break;
587 case Margin: lengthValues(decl, margins); break;
588 case QtSpacing: if (spacing) *spacing = lengthValue(decl); break;
589
590 default: continue;
591 }
592 hit = true;
593 }
594
595 return hit;
596}
597
598int ValueExtractor::extractStyleFeatures()
599{
600 int features = StyleFeature_None;
601 for (int i = 0; i < declarations.size(); i++) {
602 const Declaration &decl = declarations.at(i);
603 if (decl.d->propertyId == QtStyleFeatures)
604 features = decl.styleFeaturesValue();
605 }
606 return features;
607}
608
609QSize ValueExtractor::sizeValue(const Declaration &decl)
610{
611 if (decl.d->parsed.isValid()) {
612 QList<QVariant> v = decl.d->parsed.toList();
613 return QSize(lengthValueFromData(qvariant_cast<LengthData>(v.at(0)), f),
614 lengthValueFromData(qvariant_cast<LengthData>(v.at(1)), f));
615 }
616
617 LengthData x[2] = { {0, LengthData::None }, {0, LengthData::None} };
618 if (decl.d->values.size() > 0)
619 x[0] = lengthValue(decl.d->values.at(0));
620 if (decl.d->values.size() > 1)
621 x[1] = lengthValue(decl.d->values.at(1));
622 else
623 x[1] = x[0];
624 QList<QVariant> v;
625 v << QVariant::fromValue<LengthData>(x[0]) << QVariant::fromValue<LengthData>(x[1]);
626 decl.d->parsed = v;
627 return QSize(lengthValueFromData(x[0], f), lengthValueFromData(x[1], f));
628}
629
630void ValueExtractor::sizeValues(const Declaration &decl, QSize *radii)
631{
632 radii[0] = sizeValue(decl);
633 for (int i = 1; i < 4; i++)
634 radii[i] = radii[0];
635}
636
637bool ValueExtractor::extractBorder(int *borders, QBrush *colors, BorderStyle *styles,
638 QSize *radii)
639{
640 extractFont();
641 bool hit = false;
642 for (int i = 0; i < declarations.size(); i++) {
643 const Declaration &decl = declarations.at(i);
644 switch (decl.d->propertyId) {
645 case BorderLeftWidth: borders[LeftEdge] = lengthValue(decl); break;
646 case BorderRightWidth: borders[RightEdge] = lengthValue(decl); break;
647 case BorderTopWidth: borders[TopEdge] = lengthValue(decl); break;
648 case BorderBottomWidth: borders[BottomEdge] = lengthValue(decl); break;
649 case BorderWidth: lengthValues(decl, borders); break;
650
651 case BorderLeftColor: colors[LeftEdge] = decl.brushValue(pal); break;
652 case BorderRightColor: colors[RightEdge] = decl.brushValue(pal); break;
653 case BorderTopColor: colors[TopEdge] = decl.brushValue(pal); break;
654 case BorderBottomColor: colors[BottomEdge] = decl.brushValue(pal); break;
655 case BorderColor: decl.brushValues(colors, pal); break;
656
657 case BorderTopStyle: styles[TopEdge] = decl.styleValue(); break;
658 case BorderBottomStyle: styles[BottomEdge] = decl.styleValue(); break;
659 case BorderLeftStyle: styles[LeftEdge] = decl.styleValue(); break;
660 case BorderRightStyle: styles[RightEdge] = decl.styleValue(); break;
661 case BorderStyles: decl.styleValues(styles); break;
662
663 case BorderTopLeftRadius: radii[0] = sizeValue(decl); break;
664 case BorderTopRightRadius: radii[1] = sizeValue(decl); break;
665 case BorderBottomLeftRadius: radii[2] = sizeValue(decl); break;
666 case BorderBottomRightRadius: radii[3] = sizeValue(decl); break;
667 case BorderRadius: sizeValues(decl, radii); break;
668
669 case BorderLeft:
670 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
671 break;
672 case BorderTop:
673 borderValue(decl, &borders[TopEdge], &styles[TopEdge], &colors[TopEdge]);
674 break;
675 case BorderRight:
676 borderValue(decl, &borders[RightEdge], &styles[RightEdge], &colors[RightEdge]);
677 break;
678 case BorderBottom:
679 borderValue(decl, &borders[BottomEdge], &styles[BottomEdge], &colors[BottomEdge]);
680 break;
681 case Border:
682 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
683 borders[TopEdge] = borders[RightEdge] = borders[BottomEdge] = borders[LeftEdge];
684 styles[TopEdge] = styles[RightEdge] = styles[BottomEdge] = styles[LeftEdge];
685 colors[TopEdge] = colors[RightEdge] = colors[BottomEdge] = colors[LeftEdge];
686 break;
687
688 default: continue;
689 }
690 hit = true;
691 }
692
693 return hit;
694}
695
696bool ValueExtractor::extractOutline(int *borders, QBrush *colors, BorderStyle *styles,
697 QSize *radii, int *offsets)
698{
699 extractFont();
700 bool hit = false;
701 for (int i = 0; i < declarations.size(); i++) {
702 const Declaration &decl = declarations.at(i);
703 switch (decl.d->propertyId) {
704 case OutlineWidth: lengthValues(decl, borders); break;
705 case OutlineColor: decl.brushValues(colors, pal); break;
706 case OutlineStyle: decl.styleValues(styles); break;
707
708 case OutlineTopLeftRadius: radii[0] = sizeValue(decl); break;
709 case OutlineTopRightRadius: radii[1] = sizeValue(decl); break;
710 case OutlineBottomLeftRadius: radii[2] = sizeValue(decl); break;
711 case OutlineBottomRightRadius: radii[3] = sizeValue(decl); break;
712 case OutlineRadius: sizeValues(decl, radii); break;
713 case OutlineOffset: lengthValues(decl, offsets); break;
714
715 case Outline:
716 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
717 borders[TopEdge] = borders[RightEdge] = borders[BottomEdge] = borders[LeftEdge];
718 styles[TopEdge] = styles[RightEdge] = styles[BottomEdge] = styles[LeftEdge];
719 colors[TopEdge] = colors[RightEdge] = colors[BottomEdge] = colors[LeftEdge];
720 break;
721
722 default: continue;
723 }
724 hit = true;
725 }
726
727 return hit;
728}
729
730static Qt::Alignment parseAlignment(const QCss::Value *values, int count)
731{
732 Qt::Alignment a[2] = { { }, { } };
733 for (int i = 0; i < qMin(2, count); i++) {
734 if (values[i].type != Value::KnownIdentifier)
735 break;
736 switch (values[i].variant.toInt()) {
737 case Value_Left: a[i] = Qt::AlignLeft; break;
738 case Value_Right: a[i] = Qt::AlignRight; break;
739 case Value_Top: a[i] = Qt::AlignTop; break;
740 case Value_Bottom: a[i] = Qt::AlignBottom; break;
741 case Value_Center: a[i] = Qt::AlignCenter; break;
742 default: break;
743 }
744 }
745
746 if (a[0] == Qt::AlignCenter && a[1] != 0 && a[1] != Qt::AlignCenter)
747 a[0] = (a[1] == Qt::AlignLeft || a[1] == Qt::AlignRight) ? Qt::AlignVCenter : Qt::AlignHCenter;
748 if ((a[1] == 0 || a[1] == Qt::AlignCenter) && a[0] != Qt::AlignCenter)
749 a[1] = (a[0] == Qt::AlignLeft || a[0] == Qt::AlignRight) ? Qt::AlignVCenter : Qt::AlignHCenter;
750 return a[0] | a[1];
751}
752
754{
756 v.variant.convert(QMetaType::fromType<QColor>());
758 }
759
760 if (v.type == Value::Color)
761 return qvariant_cast<QColor>(v.variant);
762
763 if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent)
764 return QColor(Qt::transparent);
765
766 if (v.type != Value::Function)
767 return ColorData();
768
769 QStringList lst = v.variant.toStringList();
770 if (lst.size() != 2)
771 return ColorData();
772
773 const QString &identifier = lst.at(0);
774 if ((identifier.compare("palette"_L1, Qt::CaseInsensitive)) == 0) {
775 static_assert((Value_LastColorRole - Value_FirstColorRole + 1) == QPalette::ColorRole::NColorRoles);
776
777 int role = findKnownValue(lst.at(1).trimmed(), values, NumKnownValues);
778 if (role >= Value_FirstColorRole && role <= Value_LastColorRole)
779 return (QPalette::ColorRole)(role-Value_FirstColorRole);
780
781 return ColorData();
782 }
783
784 const bool rgb = identifier.startsWith("rgb"_L1);
785 const bool hsv = !rgb && identifier.startsWith("hsv"_L1);
786 const bool hsl = !rgb && !hsv && identifier.startsWith("hsl"_L1);
787
788 if (!rgb && !hsv && !hsl)
789 return ColorData();
790
791 const bool hasAlpha = identifier.size() == 4 && identifier.at(3) == u'a';
792 if (identifier.size() > 3 && !hasAlpha)
793 return ColorData();
794
795 Parser p(lst.at(1));
796 if (!p.testExpr())
797 return ColorData();
798
799 QList<QCss::Value> colorDigits;
800 if (!p.parseExpr(&colorDigits))
801 return ColorData();
802 const int tokenCount = colorDigits.size();
803
804 for (int i = 0; i < qMin(tokenCount, 7); i += 2) {
805 if (colorDigits.at(i).type == Value::Percentage) {
806 const qreal maxRange = (rgb || i != 0) ? 255. : 359.;
807 colorDigits[i].variant = colorDigits.at(i).variant.toReal() * (maxRange / 100.);
808 colorDigits[i].type = Value::Number;
809 } else if (colorDigits.at(i).type != Value::Number) {
810 return ColorData();
811 }
812 }
813
814
815 if (tokenCount < 5)
816 return ColorData();
817
818 if (hasAlpha && tokenCount != 7) {
819 qWarning("QCssParser::parseColorValue: Specified color with alpha value but no alpha given: '%s'", qPrintable(lst.join(u' ')));
820 return ColorData();
821 }
822 if (!hasAlpha && tokenCount != 5) {
823 qWarning("QCssParser::parseColorValue: Specified color without alpha value but alpha given: '%s'", qPrintable(lst.join(u' ')));
824 return ColorData();
825 }
826
827 int v1 = colorDigits.at(0).variant.toInt();
828 int v2 = colorDigits.at(2).variant.toInt();
829 int v3 = colorDigits.at(4).variant.toInt();
830 int alpha = 255;
831 if (tokenCount == 7) {
832 int alphaValue = colorDigits.at(6).variant.toInt();
833 if (alphaValue <= 1)
834 alpha = colorDigits.at(6).variant.toReal() * 255.;
835 else
836 alpha = alphaValue;
837 }
838
839 if (rgb)
840 return QColor::fromRgb(v1, v2, v3, alpha);
841 if (hsv)
842 return QColor::fromHsv(v1, v2, v3, alpha);
843 return QColor::fromHsl(v1, v2, v3, alpha);
844}
845
846static QColor colorFromData(const ColorData& c, const QPalette &pal)
847{
848 if (c.type == ColorData::Color) {
849 return c.color;
850 } else if (c.type == ColorData::Role) {
851 return pal.color(c.role);
852 }
853 return QColor();
854}
855
856static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
857{
859 if (c.type == ColorData::Color) {
860 return QBrush(c.color);
861 } else if (c.type == ColorData::Role) {
862 return c.role;
863 }
864
865 if (v.type != Value::Function)
866 return BrushData();
867
868 QStringList lst = v.variant.toStringList();
869 if (lst.size() != 2)
870 return BrushData();
871
872 QStringList gradFuncs;
873 gradFuncs << "qlineargradient"_L1 << "qradialgradient"_L1 << "qconicalgradient"_L1 << "qgradient"_L1;
874 int gradType = -1;
875
876 if ((gradType = gradFuncs.indexOf(lst.at(0).toLower())) == -1)
877 return BrushData();
878
879 QHash<QString, qreal> vars;
880 QList<QGradientStop> stops;
881
882 int spread = -1;
883 QStringList spreads;
884 spreads << "pad"_L1 << "reflect"_L1 << "repeat"_L1;
885
886 int coordinateMode = -1;
887 QStringList coordinateModes;
888 coordinateModes << "logical"_L1 << "stretchtodevice"_L1 << "objectbounding"_L1 << "object"_L1;
889
890 bool dependsOnThePalette = false;
891 Parser parser(lst.at(1));
892 while (parser.hasNext()) {
893 parser.skipSpace();
894 if (!parser.test(IDENT))
895 return BrushData();
896 QString attr = parser.lexem();
897 parser.skipSpace();
898 if (!parser.test(COLON))
899 return BrushData();
900 parser.skipSpace();
901 if (attr.compare("stop"_L1, Qt::CaseInsensitive) == 0) {
902 QCss::Value stop, color;
903 parser.next();
904 if (!parser.parseTerm(&stop)) return BrushData();
905 parser.skipSpace();
906 parser.next();
907 if (!parser.parseTerm(&color)) return BrushData();
909 if (cd.type == ColorData::Role)
910 dependsOnThePalette = true;
911 stops.append(QGradientStop(stop.variant.toReal(), colorFromData(cd, pal)));
912 } else {
913 parser.next();
914 QCss::Value value;
915 (void)parser.parseTerm(&value);
916 if (attr.compare("spread"_L1, Qt::CaseInsensitive) == 0)
917 spread = spreads.indexOf(value.variant.toString());
918 else if (attr.compare("coordinatemode"_L1, Qt::CaseInsensitive) == 0)
919 coordinateMode = coordinateModes.indexOf(value.variant.toString());
920 else
921 vars[attr] = value.variant.toReal();
922 }
923 parser.skipSpace();
924 (void)parser.test(COMMA);
925 }
926
927 if (gradType == 0) {
928 QLinearGradient lg(vars.value("x1"_L1), vars.value("y1"_L1),
929 vars.value("x2"_L1), vars.value("y2"_L1));
930 lg.setCoordinateMode(coordinateMode < 0 ? QGradient::ObjectBoundingMode : QGradient::CoordinateMode(coordinateMode));
931 lg.setStops(stops);
932 if (spread != -1)
933 lg.setSpread(QGradient::Spread(spread));
934 BrushData bd = QBrush(lg);
935 if (dependsOnThePalette)
937 return bd;
938 }
939
940 if (gradType == 1) {
941 QRadialGradient rg(vars.value("cx"_L1), vars.value("cy"_L1),
942 vars.value("radius"_L1), vars.value("fx"_L1),
943 vars.value("fy"_L1));
944 rg.setCoordinateMode(coordinateMode < 0 ? QGradient::ObjectBoundingMode : QGradient::CoordinateMode(coordinateMode));
945 rg.setStops(stops);
946 if (spread != -1)
947 rg.setSpread(QGradient::Spread(spread));
948 BrushData bd = QBrush(rg);
949 if (dependsOnThePalette)
951 return bd;
952 }
953
954 if (gradType == 2) {
955 QConicalGradient cg(vars.value("cx"_L1), vars.value("cy"_L1), vars.value("angle"_L1));
956 cg.setCoordinateMode(coordinateMode < 0 ? QGradient::ObjectBoundingMode : QGradient::CoordinateMode(coordinateMode));
957 cg.setStops(stops);
958 if (spread != -1)
959 cg.setSpread(QGradient::Spread(spread));
960 BrushData bd = QBrush(cg);
961 if (dependsOnThePalette)
963 return bd;
964 }
965
966 return BrushData();
967}
968
969static QBrush brushFromData(const BrushData& c, const QPalette &pal)
970{
971 if (c.type == BrushData::Role) {
972 return pal.color(c.role);
973 } else {
974 return c.brush;
975 }
976}
977
979{
981 switch (v.variant.toInt()) {
982 case Value_None:
983 return BorderStyle_None;
984 case Value_Dotted:
985 return BorderStyle_Dotted;
986 case Value_Dashed:
987 return BorderStyle_Dashed;
988 case Value_Solid:
989 return BorderStyle_Solid;
990 case Value_Double:
991 return BorderStyle_Double;
992 case Value_DotDash:
993 return BorderStyle_DotDash;
994 case Value_DotDotDash:
996 case Value_Groove:
997 return BorderStyle_Groove;
998 case Value_Ridge:
999 return BorderStyle_Ridge;
1000 case Value_Inset:
1001 return BorderStyle_Inset;
1002 case Value_Outset:
1003 return BorderStyle_Outset;
1004 case Value_Native:
1005 return BorderStyle_Native;
1006 default:
1007 break;
1008 }
1009 }
1010
1011 return BorderStyle_Unknown;
1012}
1013
1014void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::BorderStyle *style, QBrush *color)
1015{
1016 if (decl.d->parsed.isValid()) {
1017 BorderData data = qvariant_cast<BorderData>(decl.d->parsed);
1018 *width = lengthValueFromData(data.width, f);
1019 *style = data.style;
1020 *color = data.color.type != BrushData::Invalid ? brushFromData(data.color, pal) : QBrush(QColor());
1021 return;
1022 }
1023
1024 *width = 0;
1025 *style = BorderStyle_None;
1026 *color = QColor();
1027
1028 if (decl.d->values.isEmpty())
1029 return;
1030
1031 BorderData data;
1032 data.width.number = 0;
1033 data.width.unit = LengthData::None;
1034 data.style = BorderStyle_None;
1035
1036 int i = 0;
1037 if (decl.d->values.at(i).type == Value::Length || decl.d->values.at(i).type == Value::Number) {
1038 data.width = lengthValue(decl.d->values.at(i));
1039 *width = lengthValueFromData(data.width, f);
1040 if (++i >= decl.d->values.size()) {
1041 decl.d->parsed = QVariant::fromValue<BorderData>(data);
1042 return;
1043 }
1044 }
1045
1046 data.style = parseStyleValue(decl.d->values.at(i));
1047 if (data.style != BorderStyle_Unknown) {
1048 *style = data.style;
1049 if (++i >= decl.d->values.size()) {
1050 decl.d->parsed = QVariant::fromValue<BorderData>(data);
1051 return;
1052 }
1053 } else {
1054 data.style = BorderStyle_None;
1055 }
1056
1057 data.color = parseBrushValue(decl.d->values.at(i), pal);
1058 if (data.color.type != BrushData::Invalid) {
1059 *color = brushFromData(data.color, pal);
1060 if (data.color.type != BrushData::DependsOnThePalette)
1061 decl.d->parsed = QVariant::fromValue<BorderData>(data);
1062 }
1063}
1064
1065static void parseShorthandBackgroundProperty(const QList<QCss::Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal)
1066{
1067 *brush = BrushData();
1068 *image = QString();
1069 *repeat = Repeat_XY;
1070 *alignment = Qt::AlignTop | Qt::AlignLeft;
1071
1072 for (int i = 0; i < values.size(); ++i) {
1073 const QCss::Value &v = values.at(i);
1074 if (v.type == Value::Uri) {
1075 *image = v.variant.toString();
1076 continue;
1077 } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_None) {
1078 *image = QString();
1079 continue;
1080 } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent) {
1081 *brush = QBrush(Qt::transparent);
1082 }
1083
1084 Repeat repeatAttempt = static_cast<Repeat>(findKnownValue(v.variant.toString(),
1085 repeats, NumKnownRepeats));
1086 if (repeatAttempt != Repeat_Unknown) {
1087 *repeat = repeatAttempt;
1088 continue;
1089 }
1090
1091 if (v.type == Value::KnownIdentifier) {
1092 const int start = i;
1093 int count = 1;
1094 if (i < values.size() - 1
1095 && values.at(i + 1).type == Value::KnownIdentifier) {
1096 ++i;
1097 ++count;
1098 }
1099 Qt::Alignment a = parseAlignment(values.constData() + start, count);
1100 if (int(a) != 0) {
1101 *alignment = a;
1102 continue;
1103 }
1104 i -= count - 1;
1105 }
1106
1107 *brush = parseBrushValue(v, pal);
1108 }
1109}
1110
1111bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *repeat,
1112 Qt::Alignment *alignment, Origin *origin, Attachment *attachment,
1113 Origin *clip)
1114{
1115 bool hit = false;
1116 for (int i = 0; i < declarations.size(); ++i) {
1117 const Declaration &decl = declarations.at(i);
1118 if (decl.d->values.isEmpty())
1119 continue;
1120 const QCss::Value &val = decl.d->values.at(0);
1121 switch (decl.d->propertyId) {
1122 case BackgroundColor:
1123 *brush = decl.brushValue();
1124 break;
1125 case BackgroundImage:
1126 if (val.type == Value::Uri)
1127 *image = val.variant.toString();
1128 break;
1129 case BackgroundRepeat:
1130 if (decl.d->parsed.isValid()) {
1131 *repeat = static_cast<Repeat>(decl.d->parsed.toInt());
1132 } else {
1133 *repeat = static_cast<Repeat>(findKnownValue(val.variant.toString(),
1134 repeats, NumKnownRepeats));
1135 decl.d->parsed = *repeat;
1136 }
1137 break;
1138 case BackgroundPosition:
1139 *alignment = decl.alignmentValue();
1140 break;
1141 case BackgroundOrigin:
1142 *origin = decl.originValue();
1143 break;
1144 case BackgroundClip:
1145 *clip = decl.originValue();
1146 break;
1147 case Background:
1148 if (decl.d->parsed.isValid()) {
1149 BackgroundData data = qvariant_cast<BackgroundData>(decl.d->parsed);
1150 *brush = brushFromData(data.brush, pal);
1151 *image = data.image;
1152 *repeat = data.repeat;
1153 *alignment = data.alignment;
1154 } else {
1155 BrushData brushData;
1156 parseShorthandBackgroundProperty(decl.d->values, &brushData, image, repeat, alignment, pal);
1157 *brush = brushFromData(brushData, pal);
1158 if (brushData.type != BrushData::DependsOnThePalette) {
1159 BackgroundData data = { brushData, *image, *repeat, *alignment };
1160 decl.d->parsed = QVariant::fromValue<BackgroundData>(data);
1161 }
1162 }
1163 break;
1164 case BackgroundAttachment:
1165 *attachment = decl.attachmentValue();
1166 break;
1167 default: continue;
1168 }
1169 hit = true;
1170 }
1171 return hit;
1172}
1173
1174static bool setFontSizeFromValue(QCss::Value value, QFont *font, int *fontSizeAdjustment)
1175{
1176 if (value.type == Value::KnownIdentifier) {
1177 bool valid = true;
1178 switch (value.variant.toInt()) {
1179 case Value_Small: *fontSizeAdjustment = -1; break;
1180 case Value_Medium: *fontSizeAdjustment = 0; break;
1181 case Value_Large: *fontSizeAdjustment = 1; break;
1182 case Value_XLarge: *fontSizeAdjustment = 2; break;
1183 case Value_XXLarge: *fontSizeAdjustment = 3; break;
1184 default: valid = false; break;
1185 }
1186 return valid;
1187 }
1188 if (value.type != Value::Length)
1189 return false;
1190
1191 bool valid = false;
1192 QString s = value.variant.toString();
1193 if (s.endsWith("pt"_L1, Qt::CaseInsensitive)) {
1194 s.chop(2);
1195 value.variant = s;
1196 if (value.variant.convert(QMetaType::fromType<qreal>())) {
1197 font->setPointSizeF(qBound(qreal(0), value.variant.toReal(), qreal(1 << 24) - 1));
1198 valid = true;
1199 }
1200 } else if (s.endsWith("px"_L1, Qt::CaseInsensitive)) {
1201 s.chop(2);
1202 value.variant = s;
1203 if (value.variant.convert(QMetaType::fromType<qreal>())) {
1204 font->setPixelSize(qBound(0, value.variant.toInt(), (1 << 24) - 1));
1205 valid = true;
1206 }
1207 }
1208 return valid;
1209}
1210
1211static bool setFontStyleFromValue(const QCss::Value &value, QFont *font)
1212{
1213 if (value.type != Value::KnownIdentifier)
1214 return false ;
1215 switch (value.variant.toInt()) {
1216 case Value_Normal: font->setStyle(QFont::StyleNormal); return true;
1217 case Value_Italic: font->setStyle(QFont::StyleItalic); return true;
1218 case Value_Oblique: font->setStyle(QFont::StyleOblique); return true;
1219 default: break;
1220 }
1221 return false;
1222}
1223
1224static bool setFontKerningFromValue(const QCss::Value &value, QFont *font)
1225{
1226 if (value.type != Value::KnownIdentifier)
1227 return false ;
1228 switch (value.variant.toInt()) {
1229 case Value_Normal: font->setKerning(true); return true;
1230 case Value_None: font->setKerning(false); return true;
1231 case Value_Auto: return true;
1232 default: break;
1233 }
1234 return false;
1235}
1236
1237static bool setFontWeightFromValue(const QCss::Value &value, QFont *font)
1238{
1239 if (value.type == Value::KnownIdentifier) {
1240 switch (value.variant.toInt()) {
1241 case Value_Normal: font->setWeight(QFont::Normal); return true;
1242 case Value_Bold: font->setWeight(QFont::Bold); return true;
1243 default: break;
1244 }
1245 return false;
1246 }
1247 if (value.type != Value::Number)
1248 return false;
1249 // .toInt() would call qRound64() and might overflow the long long there
1250 font->setWeight(QFont::Weight(qRound(qBound(0.0, value.variant.toDouble(), 1001.0))));
1251 return true;
1252}
1253
1254/** \internal
1255 * parse the font family from the values (starting from index \a start)
1256 * and set it the \a font
1257 * The function returns \c true if a family was extracted.
1258 */
1259static bool setFontFamilyFromValues(const QList<QCss::Value> &values, QFont *font, int start = 0)
1260{
1261 QString family;
1262 QStringList families;
1263 bool shouldAddSpace = false;
1264 for (int i = start; i < values.size(); ++i) {
1265 const QCss::Value &v = values.at(i);
1267 families << family;
1268 family.clear();
1269 shouldAddSpace = false;
1270 continue;
1271 }
1272 const QString str = v.variant.toString();
1273 if (str.isEmpty())
1274 break;
1275 if (shouldAddSpace)
1276 family += u' ';
1277 family += str;
1278 shouldAddSpace = true;
1279 }
1280 if (!family.isEmpty())
1281 families << family;
1282 if (families.isEmpty())
1283 return false;
1284 font->setFamilies(families);
1285 return true;
1286}
1287
1288static void setTextDecorationFromValues(const QList<QCss::Value> &values, QFont *font)
1289{
1290 for (int i = 0; i < values.size(); ++i) {
1291 if (values.at(i).type != Value::KnownIdentifier)
1292 continue;
1293 switch (values.at(i).variant.toInt()) {
1294 case Value_Underline: font->setUnderline(true); break;
1295 case Value_Overline: font->setOverline(true); break;
1296 case Value_LineThrough: font->setStrikeOut(true); break;
1297 case Value_None:
1298 font->setUnderline(false);
1299 font->setOverline(false);
1300 font->setStrikeOut(false);
1301 break;
1302 default: break;
1303 }
1304 }
1305}
1306
1307static void setLetterSpacingFromValue(const QCss::Value &value, QFont *font)
1308{
1309 QString s = value.variant.toString();
1310 qreal val;
1311 bool ok = false;
1312 if (s.endsWith("em"_L1, Qt::CaseInsensitive)) {
1313 s.chop(2);
1314 val = s.toDouble(&ok);
1315 if (ok)
1316 font->setLetterSpacing(QFont::PercentageSpacing, (val + 1.0) * 100);
1317 } else if (s.endsWith("px"_L1, Qt::CaseInsensitive)) {
1318 s.chop(2);
1319 val = s.toDouble(&ok);
1320 if (ok)
1321 font->setLetterSpacing(QFont::AbsoluteSpacing, val);
1322 }
1323}
1324
1325static void setWordSpacingFromValue(const QCss::Value &value, QFont *font)
1326{
1327 QString s = value.variant.toString();
1328 if (s.endsWith("px"_L1, Qt::CaseInsensitive)) {
1329 s.chop(2);
1330 qreal val;
1331 bool ok = false;
1332 val = s.toDouble(&ok);
1333 if (ok)
1334 font->setWordSpacing(val);
1335 }
1336}
1337
1338static void parseShorthandFontProperty(const QList<QCss::Value> &values, QFont *font, int *fontSizeAdjustment)
1339{
1340 font->setStyle(QFont::StyleNormal);
1341 font->setWeight(QFont::Normal);
1342 *fontSizeAdjustment = -255;
1343
1344 int i = 0;
1345 while (i < values.size()) {
1346 if (setFontStyleFromValue(values.at(i), font)
1347 || setFontWeightFromValue(values.at(i), font))
1348 ++i;
1349 else
1350 break;
1351 }
1352
1353 if (i < values.size()) {
1354 setFontSizeFromValue(values.at(i), font, fontSizeAdjustment);
1355 ++i;
1356 }
1357
1358 if (i < values.size()) {
1359 setFontFamilyFromValues(values, font, i);
1360 }
1361}
1362
1363static void setFontVariantFromValue(const QCss::Value &value, QFont *font)
1364{
1365 if (value.type == Value::KnownIdentifier) {
1366 switch (value.variant.toInt()) {
1367 case Value_Normal: font->setCapitalization(QFont::MixedCase); break;
1368 case Value_SmallCaps: font->setCapitalization(QFont::SmallCaps); break;
1369 default: break;
1370 }
1371 }
1372}
1373
1374static void setTextTransformFromValue(const QCss::Value &value, QFont *font)
1375{
1376 if (value.type == Value::KnownIdentifier) {
1377 switch (value.variant.toInt()) {
1378 case Value_None: font->setCapitalization(QFont::MixedCase); break;
1379 case Value_Uppercase: font->setCapitalization(QFont::AllUppercase); break;
1380 case Value_Lowercase: font->setCapitalization(QFont::AllLowercase); break;
1381 default: break;
1382 }
1383 }
1384}
1385
1386bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment)
1387{
1388 if (fontExtracted) {
1389 *font = f;
1390 *fontSizeAdjustment = adjustment;
1391 return fontExtracted == 1;
1392 }
1393
1394 bool hit = false;
1395 for (int i = 0; i < declarations.size(); ++i) {
1396 const Declaration &decl = declarations.at(i);
1397 if (decl.d->values.isEmpty())
1398 continue;
1399 const QCss::Value &val = decl.d->values.at(0);
1400 switch (decl.d->propertyId) {
1401 case FontSize: setFontSizeFromValue(val, font, fontSizeAdjustment); break;
1402 case FontStyle: setFontStyleFromValue(val, font); break;
1403 case FontWeight: setFontWeightFromValue(val, font); break;
1404 case FontFamily: setFontFamilyFromValues(decl.d->values, font); break;
1405 case FontKerning: setFontKerningFromValue(val, font); break;
1406 case TextDecoration: setTextDecorationFromValues(decl.d->values, font); break;
1407 case Font: parseShorthandFontProperty(decl.d->values, font, fontSizeAdjustment); break;
1408 case FontVariant: setFontVariantFromValue(val, font); break;
1409 case TextTransform: setTextTransformFromValue(val, font); break;
1410 case LetterSpacing: setLetterSpacingFromValue(val, font); break;
1411 case WordSpacing: setWordSpacingFromValue(val, font); break;
1412 default: continue;
1413 }
1414 hit = true;
1415 }
1416
1417 f = *font;
1418 adjustment = *fontSizeAdjustment;
1419 fontExtracted = hit ? 1 : 2;
1420 return hit;
1421}
1422
1423bool ValueExtractor::extractPalette(QBrush *foreground,
1424 QBrush *selectedForeground,
1425 QBrush *selectedBackground,
1426 QBrush *alternateBackground,
1427 QBrush *placeHolderTextForeground,
1428 QBrush *accent)
1429{
1430 bool hit = false;
1431 for (int i = 0; i < declarations.size(); ++i) {
1432 const Declaration &decl = declarations.at(i);
1433 switch (decl.d->propertyId) {
1434 case Color: *foreground = decl.brushValue(pal); break;
1435 case QtSelectionForeground: *selectedForeground = decl.brushValue(pal); break;
1436 case QtSelectionBackground: *selectedBackground = decl.brushValue(pal); break;
1437 case QtAlternateBackground: *alternateBackground = decl.brushValue(pal); break;
1438 case QtPlaceHolderTextColor: *placeHolderTextForeground = decl.brushValue(pal); break;
1439 case QtAccent: *accent = decl.brushValue(pal); break;
1440 default: continue;
1441 }
1442 hit = true;
1443 }
1444 return hit;
1445}
1446
1447void ValueExtractor::extractFont()
1448{
1449 if (fontExtracted)
1450 return;
1451 int dummy = -255;
1452 extractFont(&f, &dummy);
1453}
1454
1455bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size)
1456{
1457 bool hit = false;
1458 for (int i = 0; i < declarations.size(); ++i) {
1459 const Declaration &decl = declarations.at(i);
1460 switch (decl.d->propertyId) {
1461 case QtImage:
1462 *icon = decl.iconValue();
1463 if (decl.d->values.size() > 0 && decl.d->values.at(0).type == Value::Uri) {
1464 // try to pull just the size from the image...
1465 QImageReader imageReader(decl.d->values.at(0).variant.toString());
1466 if ((*size = imageReader.size()).isNull()) {
1467 // but we'll have to load the whole image if the
1468 // format doesn't support just reading the size
1469 *size = imageReader.read().size();
1470 }
1471 }
1472 break;
1473 case QtImageAlignment: *a = decl.alignmentValue(); break;
1474 default: continue;
1475 }
1476 hit = true;
1477 }
1478 return hit;
1479}
1480
1481bool ValueExtractor::extractIcon(QIcon *icon, QSize *size)
1482{
1483 // Find last declaration that specifies an icon
1484 const auto declaration = std::find_if(
1485 declarations.rbegin(), declarations.rend(),
1486 [](const Declaration &decl) { return decl.d->propertyId == QtIcon; });
1487 if (declaration == declarations.rend())
1488 return false;
1489
1490 *icon = declaration->iconValue();
1491
1492 // If the value contains a URI, try to get the size of the icon
1493 if (declaration->d->values.isEmpty())
1494 return true;
1495
1496 const auto &propertyValue = declaration->d->values.constFirst();
1497 if (propertyValue.type != Value::Uri)
1498 return true;
1499
1500 // First try to read just the size from the image without loading it
1501 const QString url(propertyValue.variant.toString());
1502 QImageReader imageReader(url);
1503 *size = imageReader.size();
1504 if (!size->isNull())
1505 return true;
1506
1507 // Get the size by loading the image instead
1508 *size = imageReader.read().size();
1509 return true;
1510}
1511
1512///////////////////////////////////////////////////////////////////////////////
1513// Declaration
1514QColor Declaration::colorValue(const QPalette &pal) const
1515{
1516 if (d->values.size() != 1)
1517 return QColor();
1518
1519 if (d->parsed.isValid()) {
1520 switch (d->parsed.typeId()) {
1521 case qMetaTypeId<QColor>():
1522 return qvariant_cast<QColor>(d->parsed);
1523 case qMetaTypeId<int>():
1524 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1525 case qMetaTypeId<QList<QVariant>>():
1526 if (d->parsed.toList().size() == 1) {
1527 auto parsedList = d->parsed.toList();
1528 const auto &value = parsedList.at(0);
1529 return qvariant_cast<QColor>(value);
1530 }
1531 break;
1532 }
1533 }
1534
1535 ColorData color = parseColorValue(d->values.at(0));
1536 if (color.type == ColorData::Role) {
1537 d->parsed = QVariant::fromValue<int>(color.role);
1538 return pal.color((QPalette::ColorRole)(color.role));
1539 } else {
1540 d->parsed = QVariant::fromValue<QColor>(color.color);
1541 return color.color;
1542 }
1543}
1544
1545QBrush Declaration::brushValue(const QPalette &pal) const
1546{
1547 if (d->values.size() != 1)
1548 return QBrush();
1549
1550 if (d->parsed.isValid()) {
1551 if (d->parsed.userType() == QMetaType::QBrush)
1552 return qvariant_cast<QBrush>(d->parsed);
1553 if (d->parsed.userType() == QMetaType::Int)
1554 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1555 }
1556
1557 BrushData data = parseBrushValue(d->values.at(0), pal);
1558
1559 if (data.type == BrushData::Role) {
1560 d->parsed = QVariant::fromValue<int>(data.role);
1561 return pal.color((QPalette::ColorRole)(data.role));
1562 } else {
1563 if (data.type != BrushData::DependsOnThePalette)
1564 d->parsed = QVariant::fromValue<QBrush>(data.brush);
1565 return data.brush;
1566 }
1567}
1568
1569void Declaration::brushValues(QBrush *c, const QPalette &pal) const
1570{
1571 int needParse = 0x1f; // bits 0..3 say if we should parse the corresponding value.
1572 // the bit 4 say we need to update d->parsed
1573 int i = 0;
1574 if (d->parsed.isValid()) {
1575 needParse = 0;
1576 Q_ASSERT(d->parsed.metaType() == QMetaType::fromType<QList<QVariant>>());
1577 QList<QVariant> v = d->parsed.toList();
1578 for (i = 0; i < qMin(v.size(), 4); i++) {
1579 if (v.at(i).userType() == QMetaType::QBrush) {
1580 c[i] = qvariant_cast<QBrush>(v.at(i));
1581 } else if (v.at(i).userType() == QMetaType::Int) {
1582 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1583 } else {
1584 needParse |= (1<<i);
1585 }
1586 }
1587 }
1588 if (needParse != 0) {
1589 QList<QVariant> v;
1590 for (i = 0; i < qMin(d->values.size(), 4); i++) {
1591 if (!(needParse & (1<<i)))
1592 continue;
1593 BrushData data = parseBrushValue(d->values.at(i), pal);
1594 if (data.type == BrushData::Role) {
1595 v += QVariant::fromValue<int>(data.role);
1596 c[i] = pal.color((QPalette::ColorRole)(data.role));
1597 } else {
1598 if (data.type != BrushData::DependsOnThePalette) {
1599 v += QVariant::fromValue<QBrush>(data.brush);
1600 } else {
1601 v += QVariant();
1602 }
1603 c[i] = data.brush;
1604 }
1605 }
1606 if (needParse & 0x10)
1607 d->parsed = v;
1608 }
1609 if (i == 0) c[0] = c[1] = c[2] = c[3] = QBrush();
1610 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1611 else if (i == 2) c[2] = c[0], c[3] = c[1];
1612 else if (i == 3) c[3] = c[1];
1613}
1614
1615bool Declaration::realValue(qreal *real, const char *unit) const
1616{
1617 if (d->values.size() != 1)
1618 return false;
1619 const Value &v = d->values.at(0);
1620 if (unit && v.type != Value::Length)
1621 return false;
1622 const QString str = v.variant.toString();
1623 QStringView s(str);
1624 if (unit) {
1625 const QLatin1StringView unitStr(unit);
1626 if (!s.endsWith(unitStr, Qt::CaseInsensitive))
1627 return false;
1628 s.chop(unitStr.size());
1629 }
1630 bool ok = false;
1631 qreal val = s.toDouble(&ok);
1632 if (ok)
1633 *real = val;
1634 return ok;
1635}
1636
1637static bool intValueHelper(const QCss::Value &v, int *i, const char *unit)
1638{
1639 if (unit && v.type != Value::Length)
1640 return false;
1641 const QString str = v.variant.toString();
1642 QStringView s(str);
1643 if (unit) {
1644 const QLatin1StringView unitStr(unit);
1645 if (!s.endsWith(unitStr, Qt::CaseInsensitive))
1646 return false;
1647 s.chop(unitStr.size());
1648 }
1649 bool ok = false;
1650 int val = s.toInt(&ok);
1651 if (ok)
1652 *i = val;
1653 return ok;
1654}
1655
1656bool Declaration::intValue(int *i, const char *unit) const
1657{
1658 if (d->values.size() != 1)
1659 return false;
1660 return intValueHelper(d->values.at(0), i, unit);
1661}
1662
1663QSize Declaration::sizeValue() const
1664{
1665 if (d->parsed.isValid())
1666 return qvariant_cast<QSize>(d->parsed);
1667
1668 int x[2] = { 0, 0 };
1669 const int count = d->values.size();
1670 for (int i = 0; i < count; ++i) {
1671 if (i > 1) {
1672 qWarning("QCssParser::sizeValue: Too many values provided");
1673 break;
1674 }
1675 const auto &value = d->values.at(i);
1676 const QString valueString = value.variant.toString();
1677 if (valueString.endsWith(u"pt", Qt::CaseInsensitive)) {
1678 intValueHelper(value, &x[i], "pt");
1679 // according to https://www.w3.org/TR/css3-values/#absolute-lengths
1680 // 1pt = 1/72th of 1 inch, and 1px = 1/96th of 1 inch
1681 x[i] = (x[i] * 72) / 96;
1682 } else {
1683 // by default we use 'px'
1684 intValueHelper(value, &x[i], "px");
1685 }
1686 }
1687 if (count == 1)
1688 x[1] = x[0];
1689 QSize size(x[0], x[1]);
1690 d->parsed = QVariant::fromValue<QSize>(size);
1691 return size;
1692}
1693
1694QRect Declaration::rectValue() const
1695{
1696 if (d->values.size() != 1)
1697 return QRect();
1698
1699 if (d->parsed.isValid())
1700 return qvariant_cast<QRect>(d->parsed);
1701
1702 const QCss::Value &v = d->values.at(0);
1703 if (v.type != Value::Function)
1704 return QRect();
1705 const QStringList func = v.variant.toStringList();
1706 if (func.size() != 2 || func.at(0).compare("rect"_L1) != 0)
1707 return QRect();
1708 const auto args = QStringView{func[1]}.split(u' ', Qt::SkipEmptyParts);
1709 if (args.size() != 4)
1710 return QRect();
1711 QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt());
1712 d->parsed = QVariant::fromValue<QRect>(rect);
1713 return rect;
1714}
1715
1716void Declaration::colorValues(QColor *c, const QPalette &pal) const
1717{
1718 int i;
1719 if (d->parsed.isValid()) {
1720 QList<QVariant> v = d->parsed.toList();
1721 for (i = 0; i < qMin(d->values.size(), 4); i++) {
1722 if (v.at(i).userType() == QMetaType::QColor) {
1723 c[i] = qvariant_cast<QColor>(v.at(i));
1724 } else {
1725 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1726 }
1727 }
1728 } else {
1729 QList<QVariant> v;
1730 for (i = 0; i < qMin(d->values.size(), 4); i++) {
1731 ColorData color = parseColorValue(d->values.at(i));
1732 if (color.type == ColorData::Role) {
1733 v += QVariant::fromValue<int>(color.role);
1734 c[i] = pal.color((QPalette::ColorRole)(color.role));
1735 } else {
1736 v += QVariant::fromValue<QColor>(color.color);
1737 c[i] = color.color;
1738 }
1739 }
1740 d->parsed = v;
1741 }
1742
1743 if (i == 0) c[0] = c[1] = c[2] = c[3] = QColor();
1744 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1745 else if (i == 2) c[2] = c[0], c[3] = c[1];
1746 else if (i == 3) c[3] = c[1];
1747}
1748
1749BorderStyle Declaration::styleValue() const
1750{
1751 if (d->values.size() != 1)
1752 return BorderStyle_None;
1753 return parseStyleValue(d->values.at(0));
1754}
1755
1756void Declaration::styleValues(BorderStyle *s) const
1757{
1758 int i;
1759 for (i = 0; i < qMin(d->values.size(), 4); i++)
1760 s[i] = parseStyleValue(d->values.at(i));
1761 if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None;
1762 else if (i == 1) s[3] = s[2] = s[1] = s[0];
1763 else if (i == 2) s[2] = s[0], s[3] = s[1];
1764 else if (i == 3) s[3] = s[1];
1765}
1766
1767Repeat Declaration::repeatValue() const
1768{
1769 if (d->parsed.isValid())
1770 return static_cast<Repeat>(d->parsed.toInt());
1771 if (d->values.size() != 1)
1772 return Repeat_Unknown;
1773 int v = findKnownValue(d->values.at(0).variant.toString(),
1774 repeats, NumKnownRepeats);
1775 d->parsed = v;
1776 return static_cast<Repeat>(v);
1777}
1778
1779Origin Declaration::originValue() const
1780{
1781 if (d->parsed.isValid())
1782 return static_cast<Origin>(d->parsed.toInt());
1783 if (d->values.size() != 1)
1784 return Origin_Unknown;
1785 int v = findKnownValue(d->values.at(0).variant.toString(),
1786 origins, NumKnownOrigins);
1787 d->parsed = v;
1788 return static_cast<Origin>(v);
1789}
1790
1791PositionMode Declaration::positionValue() const
1792{
1793 if (d->parsed.isValid())
1794 return static_cast<PositionMode>(d->parsed.toInt());
1795 if (d->values.size() != 1)
1796 return PositionMode_Unknown;
1797 int v = findKnownValue(d->values.at(0).variant.toString(),
1798 positions, NumKnownPositionModes);
1799 d->parsed = v;
1800 return static_cast<PositionMode>(v);
1801}
1802
1803Attachment Declaration::attachmentValue() const
1804{
1805 if (d->parsed.isValid())
1806 return static_cast<Attachment>(d->parsed.toInt());
1807 if (d->values.size() != 1)
1808 return Attachment_Unknown;
1809 int v = findKnownValue(d->values.at(0).variant.toString(),
1810 attachments, NumKnownAttachments);
1811 d->parsed = v;
1812 return static_cast<Attachment>(v);
1813}
1814
1815int Declaration::styleFeaturesValue() const
1816{
1817 Q_ASSERT(d->propertyId == QtStyleFeatures);
1818 if (d->parsed.isValid())
1819 return d->parsed.toInt();
1820 int features = StyleFeature_None;
1821 for (int i = 0; i < d->values.size(); i++) {
1822 features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(),
1823 styleFeatures, NumKnownStyleFeatures));
1824 }
1825 d->parsed = features;
1826 return features;
1827}
1828
1829QString Declaration::uriValue() const
1830{
1831 if (d->values.isEmpty() || d->values.at(0).type != Value::Uri)
1832 return QString();
1833 return d->values.at(0).variant.toString();
1834}
1835
1836Qt::Alignment Declaration::alignmentValue() const
1837{
1838 if (d->parsed.isValid())
1839 return Qt::Alignment(d->parsed.toInt());
1840 if (d->values.isEmpty() || d->values.size() > 2)
1841 return Qt::AlignLeft | Qt::AlignTop;
1842
1843 Qt::Alignment v = parseAlignment(d->values.constData(), d->values.size());
1844 d->parsed = int(v);
1845 return v;
1846}
1847
1848void Declaration::borderImageValue(QString *image, int *cuts,
1849 TileMode *h, TileMode *v) const
1850{
1851 const DeclarationData *d = this->d.data(); // make it const and shadow d
1852 *image = uriValue();
1853 for (int i = 0; i < 4; i++)
1854 cuts[i] = -1;
1855 *h = *v = TileMode_Stretch;
1856
1857 if (d->values.size() < 2)
1858 return;
1859
1860 if (d->values.at(1).type == Value::Number) { // cuts!
1861 int i;
1862 for (i = 0; i < qMin(d->values.size()-1, 4); i++) {
1863 const Value& v = d->values.at(i+1);
1864 if (v.type != Value::Number)
1865 break;
1866 cuts[i] = v.variant.toString().toInt();
1867 if (cuts[i] < 0) {
1868 qWarning("Declaration::borderImageValue: Invalid cut value %d at position %d",
1869 cuts[i], i);
1870 cuts[0] = cuts[1] = cuts[2] = cuts[3] = -1;
1871 i = 4;
1872 break;
1873 }
1874 }
1875 if (i == 0) cuts[0] = cuts[1] = cuts[2] = cuts[3] = 0;
1876 else if (i == 1) cuts[3] = cuts[2] = cuts[1] = cuts[0];
1877 else if (i == 2) cuts[2] = cuts[0], cuts[3] = cuts[1];
1878 else if (i == 3) cuts[3] = cuts[1];
1879 }
1880
1881 if (d->values.last().type == Value::Identifier) {
1882 *v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(),
1883 tileModes, NumKnownTileModes));
1884 }
1885 if (d->values[d->values.size() - 2].type == Value::Identifier) {
1886 *h = static_cast<TileMode>
1887 (findKnownValue(d->values[d->values.size()-2].variant.toString(),
1888 tileModes, NumKnownTileModes));
1889 } else
1890 *h = *v;
1891}
1892
1893bool Declaration::borderCollapseValue() const
1894{
1895 if (d->values.size() != 1)
1896 return false;
1897 else
1898 return d->values.at(0).toString() == "collapse"_L1;
1899}
1900
1901QList<qreal> Declaration::dashArray() const
1902{
1903 if (d->propertyId != Property::QtStrokeDashArray || d->values.empty())
1904 return QList<qreal>();
1905
1906 bool isValid = true;
1907 QList<qreal> dashes;
1908 for (int i = 0; i < d->values.size(); i++) {
1909 Value v = d->values[i];
1910 // Separators must be at odd indices and Numbers at even indices.
1911 bool isValidSeparator = (i & 1) && v.type == Value::TermOperatorComma;
1912 bool isValidNumber = !(i & 1) && v.type == Value::Number;
1913 if (!isValidNumber && !isValidSeparator) {
1914 isValid = false;
1915 break;
1916 } else if (isValidNumber) {
1917 bool ok;
1918 dashes.append(v.variant.toReal(&ok));
1919 if (!ok) {
1920 isValid = false;
1921 break;
1922 }
1923 }
1924 }
1925
1926 isValid &= !(dashes.size() & 1);
1927 return isValid ? dashes : QList<qreal>();
1928}
1929
1930QIcon Declaration::iconValue() const
1931{
1932 if (d->parsed.isValid())
1933 return qvariant_cast<QIcon>(d->parsed);
1934
1935 QIcon icon;
1936 for (int i = 0; i < d->values.size();) {
1937 const Value &value = d->values.at(i++);
1938 if (value.type != Value::Uri)
1939 break;
1940 QString uri = value.variant.toString();
1941 QIcon::Mode mode = QIcon::Normal;
1942 QIcon::State state = QIcon::Off;
1943 for (int j = 0; j < 2; j++) {
1944 if (i != d->values.size() && d->values.at(i).type == Value::KnownIdentifier) {
1945 switch (d->values.at(i).variant.toInt()) {
1946 case Value_Disabled: mode = QIcon::Disabled; break;
1947 case Value_Active: mode = QIcon::Active; break;
1948 case Value_Selected: mode = QIcon::Selected; break;
1949 case Value_Normal: mode = QIcon::Normal; break;
1950 case Value_On: state = QIcon::On; break;
1951 case Value_Off: state = QIcon::Off; break;
1952 default: break;
1953 }
1954 ++i;
1955 } else {
1956 break;
1957 }
1958 }
1959
1960 // QIcon is soo broken
1961 if (icon.isNull())
1962 icon = QIcon(uri);
1963 else
1964 icon.addPixmap(uri, mode, state);
1965
1966 if (i == d->values.size())
1967 break;
1968
1969 if (d->values.at(i).type == Value::TermOperatorComma)
1970 i++;
1971 }
1972
1973 d->parsed = QVariant::fromValue<QIcon>(icon);
1974 return icon;
1975}
1976
1977///////////////////////////////////////////////////////////////////////////////
1978// Selector
1979int Selector::specificity() const
1980{
1981 int val = 0;
1982 for (int i = 0; i < basicSelectors.size(); ++i) {
1983 const BasicSelector &sel = basicSelectors.at(i);
1984 if (!sel.elementName.isEmpty())
1985 val += 1;
1986
1987 val += (sel.pseudos.size() + sel.attributeSelectors.size()) * 0x10;
1988 val += sel.ids.size() * 0x100;
1989 }
1990 return val;
1991}
1992
1993QString Selector::pseudoElement() const
1994{
1995 const BasicSelector& bs = basicSelectors.last();
1996 if (!bs.pseudos.isEmpty() && bs.pseudos.at(0).type == PseudoClass_Unknown)
1997 return bs.pseudos.at(0).name;
1998 return QString();
1999}
2000
2001quint64 Selector::pseudoClass(quint64 *negated) const
2002{
2003 const BasicSelector& bs = basicSelectors.last();
2004 if (bs.pseudos.isEmpty())
2005 return PseudoClass_Unspecified;
2006 quint64 pc = PseudoClass_Unknown;
2007 for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.size(); i++) {
2008 const Pseudo &pseudo = bs.pseudos.at(i);
2009 if (pseudo.type == PseudoClass_Unknown)
2010 return PseudoClass_Unknown;
2011 if (!pseudo.negated)
2012 pc |= pseudo.type;
2013 else if (negated)
2014 *negated |= pseudo.type;
2015 }
2016 return pc;
2017}
2018
2019///////////////////////////////////////////////////////////////////////////////
2020// StyleSheet
2021void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity)
2022{
2023 QList<StyleRule> universals;
2024 for (int i = 0; i < styleRules.size(); ++i) {
2025 const StyleRule &rule = styleRules.at(i);
2026 QList<Selector> universalsSelectors;
2027 for (int j = 0; j < rule.selectors.size(); ++j) {
2028 const Selector& selector = rule.selectors.at(j);
2029
2030 if (selector.basicSelectors.isEmpty())
2031 continue;
2032
2033 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
2034 if (selector.basicSelectors.size() != 1)
2035 continue;
2036 } else if (selector.basicSelectors.size() <= 1) {
2037 continue;
2038 }
2039
2040 const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.size() - 1);
2041
2042 if (!sel.ids.isEmpty()) {
2043 StyleRule nr;
2044 nr.selectors += selector;
2045 nr.declarations = rule.declarations;
2046 nr.order = i;
2047 idIndex.insert(sel.ids.at(0), nr);
2048 } else if (!sel.elementName.isEmpty()) {
2049 StyleRule nr;
2050 nr.selectors += selector;
2051 nr.declarations = rule.declarations;
2052 nr.order = i;
2053 QString name = sel.elementName;
2054 if (nameCaseSensitivity == Qt::CaseInsensitive)
2055 name = std::move(name).toLower();
2056 nameIndex.insert(name, nr);
2057 } else {
2058 universalsSelectors += selector;
2059 }
2060 }
2061 if (!universalsSelectors.isEmpty()) {
2062 StyleRule nr;
2063 nr.selectors = universalsSelectors;
2064 nr.declarations = rule.declarations;
2065 nr.order = i;
2066 universals << nr;
2067 }
2068 }
2069 styleRules = universals;
2070}
2071
2072///////////////////////////////////////////////////////////////////////////////
2073// StyleSelector
2074StyleSelector::~StyleSelector()
2075{
2076}
2077
2078bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const
2079{
2080 return nodeNames(node).contains(nodeName, nameCaseSensitivity);
2081}
2082
2083QStringList StyleSelector::nodeIds(NodePtr node) const
2084{
2085 return QStringList(attributeValue(node, QCss::AttributeSelector{"id"_L1, {}, AttributeSelector::NoMatch}));
2086}
2087
2088bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
2089{
2090 if (selector.basicSelectors.isEmpty())
2091 return false;
2092
2093 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
2094 if (selector.basicSelectors.size() != 1)
2095 return false;
2096 return basicSelectorMatches(selector.basicSelectors.at(0), node);
2097 }
2098 if (selector.basicSelectors.size() <= 1)
2099 return false;
2100
2101 int i = selector.basicSelectors.size() - 1;
2102 node = duplicateNode(node);
2103 bool match = true;
2104
2105 BasicSelector sel = selector.basicSelectors.at(i);
2106 do {
2107 match = basicSelectorMatches(sel, node);
2108 if (!match) {
2109 if (i == selector.basicSelectors.size() - 1) // first element must always match!
2110 break;
2111 if (sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor &&
2112 sel.relationToNext != BasicSelector::MatchNextSelectorIfIndirectAdjecent)
2113 break;
2114 }
2115
2116 if (match || (sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor &&
2117 sel.relationToNext != BasicSelector::MatchNextSelectorIfIndirectAdjecent))
2118 --i;
2119
2120 if (i < 0)
2121 break;
2122
2123 sel = selector.basicSelectors.at(i);
2124 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor
2125 || sel.relationToNext == BasicSelector::MatchNextSelectorIfParent) {
2126
2127 NodePtr nextParent = parentNode(node);
2128 freeNode(node);
2129 node = nextParent;
2130 } else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfDirectAdjecent
2131 || sel.relationToNext == BasicSelector::MatchNextSelectorIfIndirectAdjecent) {
2132 NodePtr previousSibling = previousSiblingNode(node);
2133 freeNode(node);
2134 node = previousSibling;
2135 }
2136 if (isNullNode(node)) {
2137 match = false;
2138 break;
2139 }
2140 } while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor
2141 || sel.relationToNext == BasicSelector::MatchNextSelectorIfIndirectAdjecent));
2142
2143 freeNode(node);
2144
2145 return match;
2146}
2147
2148bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
2149{
2150 if (!sel.attributeSelectors.isEmpty()) {
2151 if (!hasAttributes(node))
2152 return false;
2153
2154 for (int i = 0; i < sel.attributeSelectors.size(); ++i) {
2155 const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
2156
2157 const QString attrValue = attributeValue(node, a);
2158 if (attrValue.isNull())
2159 return false;
2160
2161 switch (a.valueMatchCriterium) {
2162 case QCss::AttributeSelector::NoMatch:
2163 break;
2164 case QCss::AttributeSelector::MatchEqual:
2165 if (attrValue != a.value)
2166 return false;
2167 break;
2168 case QCss::AttributeSelector::MatchIncludes: {
2169 const auto lst = QStringView{attrValue}.tokenize(u' ');
2170 bool found = false;
2171 for (auto s : lst) {
2172 if (s == a.value) {
2173 found = true;
2174 break;
2175 }
2176 }
2177 if (!found)
2178 return false;
2179 break;
2180 }
2181 case QCss::AttributeSelector::MatchDashMatch: {
2182 const QString dashPrefix = a.value + u'-';
2183 if (attrValue != a.value && !attrValue.startsWith(dashPrefix))
2184 return false;
2185 break;
2186 }
2187 case QCss::AttributeSelector::MatchBeginsWith:
2188 if (!attrValue.startsWith(a.value))
2189 return false;
2190 break;
2191 case QCss::AttributeSelector::MatchEndsWith:
2192 if (!attrValue.endsWith(a.value))
2193 return false;
2194 break;
2195 case QCss::AttributeSelector::MatchContains:
2196 if (!attrValue.contains(a.value))
2197 return false;
2198 break;
2199 }
2200 }
2201 }
2202
2203 if (!sel.elementName.isEmpty()
2204 && !nodeNameEquals(node, sel.elementName))
2205 return false;
2206
2207 if (!sel.ids.isEmpty()
2208 && sel.ids != nodeIds(node))
2209 return false;
2210
2211 return true;
2212}
2213
2214void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin,
2215 int depth, QMultiMap<uint, StyleRule> *weightedRules)
2216{
2217 for (int j = 0; j < rule.selectors.size(); ++j) {
2218 const Selector& selector = rule.selectors.at(j);
2219 if (selectorMatches(selector, node)) {
2220 uint weight = rule.order
2221 + selector.specificity() *0x100
2222 + (uint(origin) + depth)*0x100000;
2223 StyleRule newRule = rule;
2224 if (rule.selectors.size() > 1) {
2225 newRule.selectors.resize(1);
2226 newRule.selectors[0] = selector;
2227 }
2228 //We might have rules with the same weight if they came from a rule with several selectors
2229 weightedRules->insert(weight, newRule);
2230 }
2231 }
2232}
2233
2234// Returns style rules that are in ascending order of specificity
2235// Each of the StyleRule returned will contain exactly one Selector
2236QList<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
2237{
2238 QList<StyleRule> rules;
2239 if (styleSheets.isEmpty())
2240 return rules;
2241
2242 QMultiMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below
2243
2244 //prune using indexed stylesheet
2245 for (int sheetIdx = 0; sheetIdx < styleSheets.size(); ++sheetIdx) {
2246 const StyleSheet &styleSheet = styleSheets.at(sheetIdx);
2247 for (int i = 0; i < styleSheet.styleRules.size(); ++i) {
2248 matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);
2249 }
2250
2251 if (!styleSheet.idIndex.isEmpty()) {
2252 QStringList ids = nodeIds(node);
2253 for (int i = 0; i < ids.size(); i++) {
2254 const QString &key = ids.at(i);
2255 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key);
2256 while (it != styleSheet.idIndex.constEnd() && it.key() == key) {
2257 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
2258 ++it;
2259 }
2260 }
2261 }
2262 if (!styleSheet.nameIndex.isEmpty()) {
2263 QStringList names = nodeNames(node);
2264 for (int i = 0; i < names.size(); i++) {
2265 QString name = names.at(i);
2266 if (nameCaseSensitivity == Qt::CaseInsensitive)
2267 name = std::move(name).toLower();
2268 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.nameIndex.constFind(name);
2269 while (it != styleSheet.nameIndex.constEnd() && it.key() == name) {
2270 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
2271 ++it;
2272 }
2273 }
2274 }
2275 if (!medium.isEmpty()) {
2276 for (int i = 0; i < styleSheet.mediaRules.size(); ++i) {
2277 if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {
2278 for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.size(); ++j) {
2279 matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,
2280 styleSheet.depth, &weightedRules);
2281 }
2282 }
2283 }
2284 }
2285 }
2286
2287 rules.reserve(weightedRules.size());
2288 QMultiMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin();
2289 for ( ; it != weightedRules.constEnd() ; ++it)
2290 rules += *it;
2291
2292 return rules;
2293}
2294
2295// for qtexthtmlparser which requires just the declarations with Enabled state
2296// and without pseudo elements
2297QList<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *extraPseudo)
2298{
2299 QList<Declaration> decls;
2300 QList<StyleRule> rules = styleRulesForNode(node);
2301 for (int i = 0; i < rules.size(); i++) {
2302 const Selector& selector = rules.at(i).selectors.at(0);
2303 const QString pseudoElement = selector.pseudoElement();
2304
2305 if (extraPseudo && pseudoElement == QLatin1StringView(extraPseudo)) {
2306 decls += rules.at(i).declarations;
2307 continue;
2308 }
2309
2310 if (!pseudoElement.isEmpty()) // skip rules with pseudo elements
2311 continue;
2312 quint64 pseudoClass = selector.pseudoClass();
2313 if (pseudoClass == PseudoClass_Enabled || pseudoClass == PseudoClass_Unspecified)
2314 decls += rules.at(i).declarations;
2315 }
2316 return decls;
2317}
2318
2319static inline bool isHexDigit(const char c)
2320{
2321 return (c >= '0' && c <= '9')
2322 || (c >= 'a' && c <= 'f')
2323 || (c >= 'A' && c <= 'F')
2324 ;
2325}
2326
2327QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences)
2328{
2329 QString output = input;
2330
2331 if (hasEscapeSequences)
2332 *hasEscapeSequences = false;
2333
2334 int i = 0;
2335 while (i < output.size()) {
2336 if (output.at(i) == u'\\') {
2337
2338 ++i;
2339 // test for unicode hex escape
2340 int hexCount = 0;
2341 const int hexStart = i;
2342 while (i < output.size()
2343 && isHexDigit(output.at(i).toLatin1())
2344 && hexCount < 7) {
2345 ++hexCount;
2346 ++i;
2347 }
2348 if (hexCount == 0) {
2349 if (hasEscapeSequences)
2350 *hasEscapeSequences = true;
2351 continue;
2352 }
2353
2354 hexCount = qMin(hexCount, 6);
2355 bool ok = false;
2356 const char16_t code = QStringView{output}.mid(hexStart, hexCount).toUShort(&ok, 16);
2357 if (ok) {
2358 output.replace(hexStart - 1, hexCount + 1, code);
2359 i = hexStart;
2360 } else {
2361 i = hexStart;
2362 }
2363 } else {
2364 ++i;
2365 }
2366 }
2367 return output;
2368}
2369
2370int QCssScanner_Generated::handleCommentStart()
2371{
2372 while (pos < input.size() - 1) {
2373 if (input.at(pos) == u'*' && input.at(pos + 1) == u'/') {
2374 pos += 2;
2375 break;
2376 }
2377 ++pos;
2378 }
2379 return S;
2380}
2381
2382void Scanner::scan(const QString &preprocessedInput, QList<Symbol> *symbols)
2383{
2384 QCssScanner_Generated scanner(preprocessedInput);
2385 Symbol sym;
2386 int tok = scanner.lex();
2387 while (tok != -1) {
2388 sym.token = static_cast<QCss::TokenType>(tok);
2389 sym.text = scanner.input;
2390 sym.start = scanner.lexemStart;
2391 sym.len = scanner.lexemLength;
2392 symbols->append(sym);
2393 tok = scanner.lex();
2394 }
2395}
2396
2397QString Symbol::lexem() const
2398{
2399 QString result;
2400 if (len > 0)
2401 result.reserve(len);
2402 for (int i = 0; i < len; ++i) {
2403 if (text.at(start + i) == u'\\' && i < len - 1)
2404 ++i;
2405 result += text.at(start + i);
2406 }
2407 return result;
2408}
2409
2410Parser::Parser(const QString &css, bool isFile)
2411{
2412 init(css, isFile);
2413}
2414
2415Parser::Parser()
2416{
2417 index = 0;
2418 errorIndex = -1;
2419 hasEscapeSequences = false;
2420}
2421
2422void Parser::init(const QString &css, bool isFile)
2423{
2424 QString styleSheet = css;
2425 if (isFile) {
2426 QFile file(css);
2427 if (file.open(QFile::ReadOnly)) {
2428 sourcePath = QFileInfo(styleSheet).absolutePath() + u'/';
2429 QTextStream stream(&file);
2430 styleSheet = stream.readAll();
2431 } else {
2432 qWarning() << "QCss::Parser - Failed to load file " << css;
2433 styleSheet.clear();
2434 }
2435 } else {
2436 sourcePath.clear();
2437 }
2438
2439 hasEscapeSequences = false;
2440 symbols.clear();
2441 symbols.reserve(8);
2442 Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols);
2443 index = 0;
2444 errorIndex = -1;
2445}
2446
2447bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity)
2448{
2449 if (testTokenAndEndsWith(ATKEYWORD_SYM, "charset"_L1)) {
2450 while (test(S) || test(CDO) || test(CDC)) {}
2451 if (!next(STRING)) return false;
2452 if (!next(SEMICOLON)) return false;
2453 }
2454
2455 while (test(S) || test(CDO) || test(CDC)) {}
2456
2457 while (testImport()) {
2458 ImportRule rule;
2459 if (!parseImport(&rule)) return false;
2460 styleSheet->importRules.append(rule);
2461 while (test(S) || test(CDO) || test(CDC)) {}
2462 }
2463
2464 do {
2465 if (testMedia()) {
2466 MediaRule rule;
2467 if (!parseMedia(&rule)) return false;
2468 styleSheet->mediaRules.append(rule);
2469 } else if (testPage()) {
2470 PageRule rule;
2471 if (!parsePage(&rule)) return false;
2472 styleSheet->pageRules.append(rule);
2473 } else if (testAnimation()) {
2474 AnimationRule rule;
2475 if (!parseAnimation(&rule)) return false;
2476 styleSheet->animationRules.append(rule);
2477 } else if (testRuleset()) {
2478 StyleRule rule;
2479 if (!parseRuleset(&rule)) return false;
2480 styleSheet->styleRules.append(rule);
2481 } else if (test(ATKEYWORD_SYM)) {
2482 if (!until(RBRACE)) return false;
2483 } else if (hasNext()) {
2484 return false;
2485 }
2486 while (test(S) || test(CDO) || test(CDC)) {}
2487 } while (hasNext());
2488 styleSheet->buildIndexes(nameCaseSensitivity);
2489 return true;
2490}
2491
2492Symbol Parser::errorSymbol()
2493{
2494 if (errorIndex == -1) return Symbol();
2495 return symbols.at(errorIndex);
2496}
2497
2498static inline void removeOptionalQuotes(QString *str)
2499{
2500 if (!str->startsWith(u'\'') && !str->startsWith(u'\"'))
2501 return;
2502 str->remove(0, 1);
2503 str->chop(1);
2504}
2505
2506bool Parser::parseImport(ImportRule *importRule)
2507{
2508 skipSpace();
2509
2510 if (test(STRING)) {
2511 importRule->href = lexem();
2512 } else {
2513 if (!testAndParseUri(&importRule->href)) return false;
2514 }
2515 removeOptionalQuotes(&importRule->href);
2516
2517 skipSpace();
2518
2519 if (testMedium()) {
2520 if (!parseMedium(&importRule->media)) return false;
2521
2522 while (test(COMMA)) {
2523 skipSpace();
2524 if (!parseNextMedium(&importRule->media)) return false;
2525 }
2526 }
2527
2528 if (!next(SEMICOLON)) return false;
2529
2530 skipSpace();
2531 return true;
2532}
2533
2534bool Parser::parseMedia(MediaRule *mediaRule)
2535{
2536 do {
2537 skipSpace();
2538 if (!parseNextMedium(&mediaRule->media)) return false;
2539 } while (test(COMMA));
2540
2541 if (!next(LBRACE)) return false;
2542 skipSpace();
2543
2544 while (testRuleset()) {
2545 StyleRule rule;
2546 if (!parseRuleset(&rule)) return false;
2547 mediaRule->styleRules.append(rule);
2548 }
2549
2550 if (!next(RBRACE)) return false;
2551 skipSpace();
2552 return true;
2553}
2554
2555bool Parser::parseMedium(QStringList *media)
2556{
2557 media->append(lexem());
2558 skipSpace();
2559 return true;
2560}
2561
2562bool Parser::parsePage(PageRule *pageRule)
2563{
2564 skipSpace();
2565
2566 if (testPseudoPage())
2567 if (!parsePseudoPage(&pageRule->selector)) return false;
2568
2569 skipSpace();
2570 if (!next(LBRACE)) return false;
2571
2572 do {
2573 skipSpace();
2574 Declaration decl;
2575 if (!parseNextDeclaration(&decl)) return false;
2576 if (!decl.isEmpty())
2577 pageRule->declarations.append(decl);
2578 } while (test(SEMICOLON));
2579
2580 if (!next(RBRACE)) return false;
2581 skipSpace();
2582 return true;
2583}
2584
2585bool Parser::parsePseudoPage(QString *selector)
2586{
2587 if (!next(IDENT)) return false;
2588 *selector = lexem();
2589 return true;
2590}
2591
2592bool Parser::parseNextOperator(Value *value)
2593{
2594 if (!hasNext()) return true;
2595 switch (next()) {
2596 case SLASH: value->type = Value::TermOperatorSlash; skipSpace(); break;
2597 case COMMA: value->type = Value::TermOperatorComma; skipSpace(); break;
2598 default: prev(); break;
2599 }
2600 return true;
2601}
2602
2603bool Parser::parseAnimation(AnimationRule *animationRule)
2604{
2605 skipSpace();
2606 if (!test(IDENT)) return false;
2607
2608 animationRule->animName = lexem();
2609
2610 if (!next(LBRACE)) return false;
2611 skipSpace();
2612
2613 while (test(PERCENTAGE) || test(IDENT)) {
2614 AnimationRule::AnimationRuleSet set;
2615 if (lookup() == PERCENTAGE) {
2616 QString name = lexem();
2617 name.removeLast();
2618 float keyFrame = name.toFloat() / 100;
2619 set.keyFrame = keyFrame;
2620 } else if (lookup() == IDENT) {
2621 QString name;
2622 if (parseElementName(&name)) {
2623 if (name == QStringLiteral("from"))
2624 set.keyFrame = 0;
2625 else if (name == QStringLiteral("to"))
2626 set.keyFrame = 1;
2627 }
2628 }
2629
2630 skipSpace();
2631 if (!next(LBRACE)) return false;
2632 const int declarationStart = index;
2633
2634 do {
2635 skipSpace();
2636 Declaration decl;
2637 const int rewind = index;
2638 if (!parseNextDeclaration(&decl)) {
2639 index = rewind;
2640 const bool foundSemicolon = until(SEMICOLON);
2641 const int semicolonIndex = index;
2642
2643 index = declarationStart;
2644 const bool foundRBrace = until(RBRACE);
2645
2646 if (foundSemicolon && semicolonIndex < index) {
2647 decl = Declaration();
2648 index = semicolonIndex - 1;
2649 } else {
2650 skipSpace();
2651 return foundRBrace;
2652 }
2653 }
2654 if (!decl.isEmpty())
2655 set.declarations.append(decl);
2656 } while (test(SEMICOLON));
2657
2658 if (!next(RBRACE)) return false;
2659 skipSpace();
2660 animationRule->ruleSets.append(set);
2661 }
2662
2663 if (!next(RBRACE)) return false;
2664 skipSpace();
2665
2666 return true;
2667}
2668
2669bool Parser::parseCombinator(BasicSelector::Relation *relation)
2670{
2671 *relation = BasicSelector::NoRelation;
2672 if (lookup() == S) {
2673 *relation = BasicSelector::MatchNextSelectorIfAncestor;
2674 skipSpace();
2675 } else {
2676 prev();
2677 }
2678 if (test(PLUS)) {
2679 *relation = BasicSelector::MatchNextSelectorIfDirectAdjecent;
2680 } else if (test(GREATER)) {
2681 *relation = BasicSelector::MatchNextSelectorIfParent;
2682 } else if (test(TILDE)) {
2683 *relation = BasicSelector::MatchNextSelectorIfIndirectAdjecent;
2684 }
2685 skipSpace();
2686 return true;
2687}
2688
2689bool Parser::parseProperty(Declaration *decl)
2690{
2691 decl->d->property = lexem();
2692 decl->d->propertyId = static_cast<Property>(findKnownValue(decl->d->property, properties, NumProperties));
2693 decl->d->inheritable = isInheritable(decl->d->propertyId);
2694 skipSpace();
2695 return true;
2696}
2697
2698bool Parser::parseRuleset(StyleRule *styleRule)
2699{
2700 Selector sel;
2701 if (!parseSelector(&sel)) return false;
2702 styleRule->selectors.append(sel);
2703
2704 while (test(COMMA)) {
2705 skipSpace();
2706 Selector sel;
2707 if (!parseNextSelector(&sel)) return false;
2708 styleRule->selectors.append(sel);
2709 }
2710
2711 skipSpace();
2712 if (!next(LBRACE)) return false;
2713 const int declarationStart = index;
2714
2715 do {
2716 skipSpace();
2717 Declaration decl;
2718 const int rewind = index;
2719 if (!parseNextDeclaration(&decl)) {
2720 index = rewind;
2721 const bool foundSemicolon = until(SEMICOLON);
2722 const int semicolonIndex = index;
2723
2724 index = declarationStart;
2725 const bool foundRBrace = until(RBRACE);
2726
2727 if (foundSemicolon && semicolonIndex < index) {
2728 decl = Declaration();
2729 index = semicolonIndex - 1;
2730 } else {
2731 skipSpace();
2732 return foundRBrace;
2733 }
2734 }
2735 if (!decl.isEmpty())
2736 styleRule->declarations.append(decl);
2737 } while (test(SEMICOLON));
2738
2739 if (!next(RBRACE)) return false;
2740 skipSpace();
2741 return true;
2742}
2743
2744bool Parser::parseSelector(Selector *sel)
2745{
2746 BasicSelector basicSel;
2747 if (!parseSimpleSelector(&basicSel)) return false;
2748 while (testCombinator()) {
2749 if (!parseCombinator(&basicSel.relationToNext)) return false;
2750
2751 if (!testSimpleSelector()) break;
2752 sel->basicSelectors.append(basicSel);
2753
2754 basicSel = BasicSelector();
2755 if (!parseSimpleSelector(&basicSel)) return false;
2756 }
2757 sel->basicSelectors.append(basicSel);
2758 return true;
2759}
2760
2761bool Parser::parseSimpleSelector(BasicSelector *basicSel)
2762{
2763 int minCount = 0;
2764 if (lookupElementName()) {
2765 if (!parseElementName(&basicSel->elementName)) return false;
2766 } else {
2767 prev();
2768 minCount = 1;
2769 }
2770 bool onceMore;
2771 int count = 0;
2772 do {
2773 onceMore = false;
2774 if (test(HASH)) {
2775 QString theid = lexem();
2776 // chop off leading #
2777 theid.remove(0, 1);
2778 basicSel->ids.append(theid);
2779 onceMore = true;
2780 } else if (testClass()) {
2781 onceMore = true;
2782 AttributeSelector a;
2783 a.name = "class"_L1;
2784 a.valueMatchCriterium = AttributeSelector::MatchIncludes;
2785 if (!parseClass(&a.value)) return false;
2786 basicSel->attributeSelectors.append(a);
2787 } else if (testAttrib()) {
2788 onceMore = true;
2789 AttributeSelector a;
2790 if (!parseAttrib(&a)) return false;
2791 basicSel->attributeSelectors.append(a);
2792 } else if (testPseudo()) {
2793 onceMore = true;
2794 Pseudo ps;
2795 if (!parsePseudo(&ps)) return false;
2796 basicSel->pseudos.append(ps);
2797 }
2798 if (onceMore) ++count;
2799 } while (onceMore);
2800 return count >= minCount;
2801}
2802
2803bool Parser::parseClass(QString *name)
2804{
2805 if (!next(IDENT)) return false;
2806 *name = lexem();
2807 return true;
2808}
2809
2810bool Parser::parseElementName(QString *name)
2811{
2812 switch (lookup()) {
2813 case STAR: name->clear(); break;
2814 case IDENT: *name = lexem(); break;
2815 default: return false;
2816 }
2817 return true;
2818}
2819
2820bool Parser::parseAttrib(AttributeSelector *attr)
2821{
2822 skipSpace();
2823 if (!next(IDENT)) return false;
2824 attr->name = lexem();
2825 skipSpace();
2826
2827 if (test(EQUAL)) {
2828 attr->valueMatchCriterium = AttributeSelector::MatchEqual;
2829 } else if (test(INCLUDES)) {
2830 attr->valueMatchCriterium = AttributeSelector::MatchIncludes;
2831 } else if (test(DASHMATCH)) {
2832 attr->valueMatchCriterium = AttributeSelector::MatchDashMatch;
2833 } else if (test(BEGINSWITH)) {
2834 attr->valueMatchCriterium = AttributeSelector::MatchBeginsWith;
2835 } else if (test(ENDSWITH)) {
2836 attr->valueMatchCriterium = AttributeSelector::MatchEndsWith;
2837 } else if (test(CONTAINS)) {
2838 attr->valueMatchCriterium = AttributeSelector::MatchContains;
2839 } else {
2840 return next(RBRACKET);
2841 }
2842
2843 skipSpace();
2844
2845 if (!test(IDENT) && !test(STRING)) return false;
2846 attr->value = unquotedLexem();
2847
2848 skipSpace();
2849 return next(RBRACKET);
2850}
2851
2852bool Parser::parsePseudo(Pseudo *pseudo)
2853{
2854 (void)test(COLON);
2855 pseudo->negated = test(EXCLAMATION_SYM);
2856 if (test(IDENT)) {
2857 pseudo->name = lexem();
2858 pseudo->type = static_cast<quint64>(findKnownValue(pseudo->name, pseudos, NumPseudos));
2859 return true;
2860 }
2861 if (!next(FUNCTION)) return false;
2862 pseudo->function = lexem();
2863 // chop off trailing parenthesis
2864 pseudo->function.chop(1);
2865 skipSpace();
2866 if (!test(IDENT)) return false;
2867 pseudo->name = lexem();
2868 skipSpace();
2869 return next(RPAREN);
2870}
2871
2872bool Parser::parseNextDeclaration(Declaration *decl)
2873{
2874 if (!testProperty())
2875 return true; // not an error!
2876 if (!parseProperty(decl)) return false;
2877 if (!next(COLON)) return false;
2878 skipSpace();
2879 if (!parseNextExpr(&decl->d->values)) return false;
2880 if (testPrio())
2881 if (!parsePrio(decl)) return false;
2882 return true;
2883}
2884
2885bool Parser::testPrio()
2886{
2887 const int rewind = index;
2888 if (!test(EXCLAMATION_SYM)) return false;
2889 skipSpace();
2890 if (!test(IDENT)) {
2891 index = rewind;
2892 return false;
2893 }
2894 if (lexem().compare("important"_L1, Qt::CaseInsensitive) != 0) {
2895 index = rewind;
2896 return false;
2897 }
2898 return true;
2899}
2900
2901bool Parser::parsePrio(Declaration *declaration)
2902{
2903 declaration->d->important = true;
2904 skipSpace();
2905 return true;
2906}
2907
2908bool Parser::parseExpr(QList<Value> *values)
2909{
2910 Value val;
2911 if (!parseTerm(&val)) return false;
2912 values->append(val);
2913 bool onceMore;
2914 do {
2915 onceMore = false;
2916 val = Value();
2917 if (!parseNextOperator(&val)) return false;
2918 if (val.type != QCss::Value::Unknown)
2919 values->append(val);
2920 if (testTerm()) {
2921 onceMore = true;
2922 val = Value();
2923 if (!parseTerm(&val)) return false;
2924 values->append(val);
2925 }
2926 } while (onceMore);
2927 return true;
2928}
2929
2930bool Parser::testTerm()
2931{
2932 return test(PLUS) || test(MINUS)
2933 || test(NUMBER)
2934 || test(PERCENTAGE)
2935 || test(LENGTH)
2936 || test(STRING)
2937 || test(IDENT)
2938 || testHexColor()
2939 || testFunction();
2940}
2941
2942bool Parser::parseTerm(Value *value)
2943{
2944 QString str = lexem();
2945 bool haveUnary = false;
2946 if (lookup() == PLUS || lookup() == MINUS) {
2947 haveUnary = true;
2948 if (!hasNext()) return false;
2949 next();
2950 str += lexem();
2951 }
2952
2953 value->variant = str;
2954 value->type = QCss::Value::String;
2955 switch (lookup()) {
2956 case NUMBER:
2957 value->type = Value::Number;
2958 value->variant.convert(QMetaType::fromType<double>());
2959 break;
2960 case PERCENTAGE:
2961 value->type = Value::Percentage;
2962 str.chop(1); // strip off %
2963 value->variant = str;
2964 break;
2965 case LENGTH:
2966 value->type = Value::Length;
2967 break;
2968
2969 case STRING:
2970 if (haveUnary) return false;
2971 value->type = Value::String;
2972 str.chop(1);
2973 str.remove(0, 1);
2974 value->variant = str;
2975 break;
2976 case IDENT: {
2977 if (haveUnary) return false;
2978 value->type = Value::Identifier;
2979 const int theid = findKnownValue(str, values, NumKnownValues);
2980 if (theid != 0) {
2981 value->type = Value::KnownIdentifier;
2982 value->variant = theid;
2983 }
2984 break;
2985 }
2986 default: {
2987 if (haveUnary) return false;
2988 prev();
2989 if (testHexColor()) {
2990 QColor col;
2991 if (!parseHexColor(&col)) return false;
2992 value->type = Value::Color;
2993 value->variant = col;
2994 } else if (testFunction()) {
2995 QString name, args;
2996 if (!parseFunction(&name, &args)) return false;
2997 if (name == "url"_L1) {
2998 value->type = Value::Uri;
2999 removeOptionalQuotes(&args);
3000 if (QFileInfo(args).isRelative() && !sourcePath.isEmpty()) {
3001 args.prepend(sourcePath);
3002 }
3003 value->variant = args;
3004 } else {
3005 value->type = Value::Function;
3006 value->variant = QStringList() << name << args;
3007 }
3008 } else {
3009 return recordError();
3010 }
3011 return true;
3012 }
3013 }
3014 skipSpace();
3015 return true;
3016}
3017
3018bool Parser::parseFunction(QString *name, QString *args)
3019{
3020 *name = lexem();
3021 name->chop(1);
3022 // until(RPAREN) needs FUNCTION token at index-1 to work properly
3023 int start = index;
3024 skipSpace();
3025 std::swap(start, index);
3026 if (!until(RPAREN)) return false;
3027 for (int i = start; i < index - 1; ++i)
3028 args->append(symbols.at(i).lexem());
3029 /*
3030 if (!nextExpr(&arguments)) return false;
3031 if (!next(RPAREN)) return false;
3032 */
3033 skipSpace();
3034 return true;
3035}
3036
3037bool Parser::parseHexColor(QColor *col)
3038{
3039 *col = QColor::fromString(lexem());
3040 if (!col->isValid()) {
3041 qWarning("QCssParser::parseHexColor: Unknown color name '%s'",lexem().toLatin1().constData());
3042 return false;
3043 }
3044 skipSpace();
3045 return true;
3046}
3047
3048bool Parser::testAndParseUri(QString *uri)
3049{
3050 const int rewind = index;
3051 if (!testFunction()) return false;
3052
3053 QString name, args;
3054 if (!parseFunction(&name, &args)) {
3055 index = rewind;
3056 return false;
3057 }
3058 if (name.compare("url"_L1, Qt::CaseInsensitive) != 0) {
3059 index = rewind;
3060 return false;
3061 }
3062 *uri = args;
3063 removeOptionalQuotes(uri);
3064 return true;
3065}
3066
3067bool Parser::testSimpleSelector()
3068{
3069 return testElementName()
3070 || (test(HASH))
3071 || testClass()
3072 || testAttrib()
3073 || testPseudo();
3074}
3075
3076bool Parser::next(QCss::TokenType t)
3077{
3078 if (hasNext() && next() == t)
3079 return true;
3080 return recordError();
3081}
3082
3083bool Parser::test(QCss::TokenType t)
3084{
3085 if (index >= symbols.size())
3086 return false;
3087 if (symbols.at(index).token == t) {
3088 ++index;
3089 return true;
3090 }
3091 return false;
3092}
3093
3094QString Parser::unquotedLexem() const
3095{
3096 QString s = lexem();
3097 if (lookup() == STRING) {
3098 s.chop(1);
3099 s.remove(0, 1);
3100 }
3101 return s;
3102}
3103
3104QString Parser::lexemUntil(QCss::TokenType t)
3105{
3106 QString lexem;
3107 while (hasNext() && next() != t)
3108 lexem += symbol().lexem();
3109 return lexem;
3110}
3111
3112bool Parser::until(QCss::TokenType target, QCss::TokenType target2)
3113{
3114 int braceCount = 0;
3115 int brackCount = 0;
3116 int parenCount = 0;
3117 if (index) {
3118 switch(symbols.at(index-1).token) {
3119 case LBRACE: ++braceCount; break;
3120 case LBRACKET: ++brackCount; break;
3121 case FUNCTION:
3122 case LPAREN: ++parenCount; break;
3123 default: ;
3124 }
3125 }
3126 while (index < symbols.size()) {
3127 QCss::TokenType t = symbols.at(index++).token;
3128 switch (t) {
3129 case LBRACE: ++braceCount; break;
3130 case RBRACE: --braceCount; break;
3131 case LBRACKET: ++brackCount; break;
3132 case RBRACKET: --brackCount; break;
3133 case FUNCTION:
3134 case LPAREN: ++parenCount; break;
3135 case RPAREN: --parenCount; break;
3136 default: break;
3137 }
3138 if ((t == target || (target2 != NONE && t == target2))
3139 && braceCount <= 0
3140 && brackCount <= 0
3141 && parenCount <= 0)
3142 return true;
3143
3144 if (braceCount < 0 || brackCount < 0 || parenCount < 0) {
3145 --index;
3146 break;
3147 }
3148 }
3149 return false;
3150}
3151
3152bool Parser::testTokenAndEndsWith(QCss::TokenType t, QLatin1StringView str)
3153{
3154 if (!test(t)) return false;
3155 if (!lexem().endsWith(str, Qt::CaseInsensitive)) {
3156 prev();
3157 return false;
3158 }
3159 return true;
3160}
3161
3162QT_END_NAMESPACE
3163#endif // QT_NO_CSSPARSER
\inmodule QtGui
Definition qbrush.h:468
\inmodule QtGui
Definition qbrush.h:416
\inmodule QtGui
Definition qbrush.h:434
@ Repeat_XY
@ Repeat_Unknown
@ Repeat_None
@ Origin_Padding
@ Origin_Content
@ Origin_Margin
@ Origin_Border
@ StyleFeature_BackgroundGradient
@ StyleFeature_BackgroundColor
@ StyleFeature_None
@ Attachment_Fixed
@ Attachment_Scroll
@ Value_Disc
@ Value_Bottom
@ Value_Wave
@ Value_UpperRoman
@ Value_Oblique
@ Value_Selected
@ Value_XXLarge
@ Value_Decimal
@ Value_PreWrap
@ Value_Link
@ Value_Active
@ Value_Button
@ Value_Left
@ Value_Circle
@ Value_Medium
@ Value_Midlight
@ Value_SvgMiterJoin
@ Value_Pre
@ Value_SquareCap
@ Value_Normal
@ Value_LineThrough
@ Value_DotDotDash
@ Value_Square
@ Value_Small
@ Value_Groove
@ Value_Transparent
@ Value_LowerAlpha
@ Value_Off
@ Value_Solid
@ Value_Disabled
@ Value_Light
@ Value_Super
@ Value_NoWrap
@ Value_Base
@ Value_Outset
@ Value_Accent
@ Value_Shadow
@ Value_Native
@ Value_ButtonText
@ Value_RoundJoin
@ Value_LowerRoman
@ Value_Top
@ Value_None
@ Value_Highlight
@ Value_Bold
@ Value_ToolTipText
@ Value_Dark
@ Value_Center
@ Value_Right
@ Value_Sub
@ Value_Text
@ Value_PreLine
@ Value_HighlightedText
@ Value_WindowText
@ Value_FlatCap
@ Value_Underline
@ Value_ToolTipBase
@ Value_DotDash
@ Value_Uppercase
@ Value_Auto
@ Value_Inset
@ Value_Large
@ Value_XLarge
@ Value_MiterJoin
@ Value_SmallCaps
@ Value_RoundCap
@ Value_Double
@ Value_AlternateBase
@ Value_BevelJoin
@ Value_Mid
@ Value_Overline
@ Value_Ridge
@ Value_UpperAlpha
@ Value_PlaceholderText
@ Value_Dashed
@ Value_BrightText
@ Value_Always
@ Value_LinkVisited
@ Value_Dotted
@ Value_Middle
@ Value_Window
@ Value_NoRole
@ Value_Lowercase
@ Value_Italic
@ BorderImage
@ BorderTopLeftRadius
@ BackgroundColor
@ Whitespace
@ OutlineTopLeftRadius
@ MarginLeft
@ QtAlternateBackground
@ QtStrokeMiterLimit
@ BorderTopColor
@ QtPlaceHolderTextColor
@ MarginRight
@ BorderBottomLeftRadius
@ QtStrokeDashArray
@ QtTableType
@ BorderBottom
@ Padding
@ QtStyleFeatures
@ BorderTopStyle
@ OutlineBottomRightRadius
@ FontKerning
@ BorderRightWidth
@ QtStrokeColor
@ PaddingRight
@ QtStrokeDashOffset
@ BackgroundOrigin
@ BorderBottomColor
@ MarginTop
@ TextUnderlineStyle
@ QtListIndent
@ PaddingLeft
@ PaddingTop
@ QtForeground
@ QtSpacing
@ BorderLeftColor
@ OutlineTopRightRadius
@ BorderRightColor
@ QtSelectionForeground
@ PaddingBottom
@ LetterSpacing
@ BorderBottomRightRadius
@ QtListNumberPrefix
@ OutlineWidth
@ PageBreakAfter
@ BorderRight
@ OutlineColor
@ OutlineBottomLeftRadius
@ FontWeight
@ QtBackgroundRole
@ QtForegroundTextureCacheKey
@ TextIndent
@ QtPosition
@ BorderColor
@ MarginBottom
@ MaximumWidth
@ QtUserState
@ OutlineStyle
@ BorderLeftWidth
@ BorderCollapse
@ QtBlockIndent
@ BackgroundPosition
@ WordSpacing
@ QtStrokeWidth
@ BackgroundImage
@ BorderRightStyle
@ FontStyle
@ TextTransform
@ FontFamily
@ FontVariant
@ BorderWidth
@ VerticalAlignment
@ LineHeight
@ QtParagraphType
@ QtSelectionBackground
@ BorderStyles
@ BorderLeft
@ TextDecoration
@ BorderTopRightRadius
@ ListStyleType
@ OutlineOffset
@ BorderRadius
@ ListStyle
@ QtStrokeLineJoin
@ BorderBottomWidth
@ Background
@ BorderTopWidth
@ OutlineRadius
@ MinimumWidth
@ BackgroundAttachment
@ TextAlignment
@ BackgroundClip
@ TextDecorationColor
@ QtListNumberSuffix
@ QtImageAlignment
@ BorderBottomStyle
@ BorderTop
@ FontSize
@ PageBreakBefore
@ QtLineHeightType
@ BackgroundRepeat
@ BorderLeftStyle
@ MinimumHeight
@ MaximumHeight
@ QtStrokeLineCap
@ TileMode_Repeat
@ TileMode_Round
@ TileMode_Stretch
@ PositionMode_Absolute
@ PositionMode_Relative
@ PositionMode_Static
@ PositionMode_Fixed
@ BorderStyle_Dotted
@ BorderStyle_Solid
@ BorderStyle_Double
@ BorderStyle_DotDash
@ BorderStyle_Ridge
@ BorderStyle_Unknown
@ BorderStyle_Dashed
@ BorderStyle_Outset
@ BorderStyle_Groove
@ BorderStyle_Native
@ BorderStyle_None
@ BorderStyle_DotDotDash
@ BorderStyle_Inset
static void setTextDecorationFromValues(const QList< QCss::Value > &values, QFont *font)
static bool operator<(const QString &name, const QCssKnownValue &prop)
static constexpr QCssKnownValue positions[]
static void removeOptionalQuotes(QString *str)
static constexpr QCssKnownValue repeats[]
static bool setFontFamilyFromValues(const QList< QCss::Value > &values, QFont *font, int start=0)
static bool setFontWeightFromValue(const QCss::Value &value, QFont *font)
static int lengthValueFromData(const LengthData &data, const QFont &f)
static bool operator<(const QCssKnownValue &prop, const QString &name)
static void setTextTransformFromValue(const QCss::Value &value, QFont *font)
static bool setFontSizeFromValue(QCss::Value value, QFont *font, int *fontSizeAdjustment)
static constexpr QCssKnownValue tileModes[]
static bool setFontStyleFromValue(const QCss::Value &value, QFont *font)
static ColorData parseColorValue(QCss::Value v)
static BorderStyle parseStyleValue(const QCss::Value &v)
static void setFontVariantFromValue(const QCss::Value &value, QFont *font)
static bool intValueHelper(const QCss::Value &v, int *i, const char *unit)
static quint64 findKnownValue(const QString &name, const QCssKnownValue *start, int numValues)
static Qt::Alignment parseAlignment(const QCss::Value *values, int count)
static constexpr QCssKnownValue styleFeatures[]
static constexpr QCssKnownValue properties[]
static void setLetterSpacingFromValue(const QCss::Value &value, QFont *font)
static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
static bool isHexDigit(const char c)
static bool isInheritable(Property propertyId)
static constexpr uchar indexOfId[]
static constexpr QCssKnownValue pseudos[]
static bool setFontKerningFromValue(const QCss::Value &value, QFont *font)
static void parseShorthandBackgroundProperty(const QList< QCss::Value > &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal)
static constexpr QCssKnownValue attachments[]
#define CHECK_ARRAY_IS_SORTED(array, Num)
static void parseShorthandFontProperty(const QList< QCss::Value > &values, QFont *font, int *fontSizeAdjustment)
static QBrush brushFromData(const BrushData &c, const QPalette &pal)
static void setWordSpacingFromValue(const QCss::Value &value, QFont *font)
static constexpr QCssKnownValue values[]
static constexpr QCssKnownValue origins[]
static QColor colorFromData(const ColorData &c, const QPalette &pal)
constexpr bool operator()(const QCssKnownValue &lhs, const QCssKnownValue &rhs) const noexcept
const char name[28]
Q_GUI_EXPORT void buildIndexes(Qt::CaseSensitivity nameCaseSensitivity=Qt::CaseSensitive)