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