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