Export QTextImageHandler and add accessor for image
[profile/ivi/qtbase.git] / src / gui / text / qcssparser.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
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
253 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
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
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
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
368 ValueExtractor::ValueExtractor(const QVector<Declaration> &decls, const QPalette &pal)
369 : declarations(decls), adjustment(0), fontExtracted(false), pal(pal)
370 {
371 }
372
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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  */
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
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
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
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
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
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
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
1267 void ValueExtractor::extractFont()
1268 {
1269     if (fontExtracted)
1270         return;
1271     int dummy = -255;
1272     extractFont(&f, &dummy);
1273 }
1274
1275 bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size)
1276 {
1277     bool hit = false;
1278 #if 0
1279     // ### Qt5
1280     for (int i = 0; i < declarations.count(); ++i) {
1281         const Declaration &decl = declarations.at(i);
1282         switch (decl.d->propertyId) {
1283         case QtImage:
1284             *icon = decl.iconValue();
1285             if (decl.d->values.count() > 0 && decl.d->values.at(0).type == Value::Uri) {
1286                 // try to pull just the size from the image...
1287                 QImageReader imageReader(decl.d->values.at(0).variant.toString());
1288                 if ((*size = imageReader.size()).isNull()) {
1289                     // but we'll have to load the whole image if the
1290                     // format doesn't support just reading the size
1291                     *size = imageReader.read().size();
1292                 }
1293             }
1294             break;
1295         case QtImageAlignment: *a = decl.alignmentValue();  break;
1296         default: continue;
1297         }
1298         hit = true;
1299     }
1300 #endif
1301     return hit;
1302 }
1303
1304 ///////////////////////////////////////////////////////////////////////////////
1305 // Declaration
1306 QColor Declaration::colorValue(const QPalette &pal) const
1307 {
1308     if (d->values.count() != 1)
1309         return QColor();
1310
1311     if (d->parsed.isValid()) {
1312         if (d->parsed.type() == QVariant::Color)
1313             return qvariant_cast<QColor>(d->parsed);
1314         if (d->parsed.type() == QVariant::Int)
1315             return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1316     }
1317
1318     ColorData color = parseColorValue(d->values.at(0));
1319     if(color.type == ColorData::Role) {
1320         d->parsed = QVariant::fromValue<int>(color.role);
1321         return pal.color((QPalette::ColorRole)(color.role));
1322     } else {
1323         d->parsed = QVariant::fromValue<QColor>(color.color);
1324         return color.color;
1325     }
1326 }
1327
1328 QBrush Declaration::brushValue(const QPalette &pal) const
1329 {
1330     if (d->values.count() != 1)
1331         return QBrush();
1332
1333     if (d->parsed.isValid()) {
1334         if (d->parsed.type() == QVariant::Brush)
1335             return qvariant_cast<QBrush>(d->parsed);
1336         if (d->parsed.type() == QVariant::Int)
1337             return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1338     }
1339
1340     BrushData data = parseBrushValue(d->values.at(0), pal);
1341
1342     if(data.type == BrushData::Role) {
1343         d->parsed = QVariant::fromValue<int>(data.role);
1344         return pal.color((QPalette::ColorRole)(data.role));
1345     } else {
1346         if (data.type != BrushData::DependsOnThePalette)
1347             d->parsed = QVariant::fromValue<QBrush>(data.brush);
1348         return data.brush;
1349     }
1350 }
1351
1352 void Declaration::brushValues(QBrush *c, const QPalette &pal) const
1353 {
1354     int needParse = 0x1f; // bits 0..3 say if we should parse the corresponding value.
1355                           // the bit 4 say we need to update d->parsed
1356     int i = 0;
1357     if (d->parsed.isValid()) {
1358         needParse = 0;
1359         QList<QVariant> v = d->parsed.toList();
1360         for (i = 0; i < qMin(v.count(), 4); i++) {
1361             if (v.at(i).type() == QVariant::Brush) {
1362                 c[i] = qvariant_cast<QBrush>(v.at(i));
1363             } else if (v.at(i).type() == QVariant::Int) {
1364                 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1365             } else {
1366                 needParse |= (1<<i);
1367             }
1368         }
1369     }
1370     if (needParse != 0) {
1371         QList<QVariant> v;
1372         for (i = 0; i < qMin(d->values.count(), 4); i++) {
1373             if (!(needParse & (1<<i)))
1374                 continue;
1375             BrushData data = parseBrushValue(d->values.at(i), pal);
1376             if(data.type == BrushData::Role) {
1377                 v += QVariant::fromValue<int>(data.role);
1378                 c[i] = pal.color((QPalette::ColorRole)(data.role));
1379             } else {
1380                 if (data.type != BrushData::DependsOnThePalette) {
1381                     v += QVariant::fromValue<QBrush>(data.brush);
1382                 } else {
1383                     v += QVariant();
1384                 }
1385                 c[i] = data.brush;
1386             }
1387         }
1388         if (needParse & 0x10)
1389             d->parsed = v;
1390     }
1391     if (i == 0) c[0] = c[1] = c[2] = c[3] = QBrush();
1392     else if (i == 1) c[3] = c[2] = c[1] = c[0];
1393     else if (i == 2) c[2] = c[0], c[3] = c[1];
1394     else if (i == 3) c[3] = c[1];
1395 }
1396
1397 bool Declaration::realValue(qreal *real, const char *unit) const
1398 {
1399     if (d->values.count() != 1)
1400         return false;
1401     const Value &v = d->values.at(0);
1402     if (unit && v.type != Value::Length)
1403         return false;
1404     QString s = v.variant.toString();
1405     if (unit) {
1406         if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1407             return false;
1408         s.chop(qstrlen(unit));
1409     }
1410     bool ok = false;
1411     qreal val = s.toDouble(&ok);
1412     if (ok)
1413         *real = val;
1414     return ok;
1415 }
1416
1417 static bool intValueHelper(const QCss::Value &v, int *i, const char *unit)
1418 {
1419     if (unit && v.type != Value::Length)
1420         return false;
1421     QString s = v.variant.toString();
1422     if (unit) {
1423         if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1424             return false;
1425         s.chop(qstrlen(unit));
1426     }
1427     bool ok = false;
1428     int val = s.toInt(&ok);
1429     if (ok)
1430         *i = val;
1431     return ok;
1432 }
1433
1434 bool Declaration::intValue(int *i, const char *unit) const
1435 {
1436     if (d->values.count() != 1)
1437         return false;
1438     return intValueHelper(d->values.at(0), i, unit);
1439 }
1440
1441 QSize Declaration::sizeValue() const
1442 {
1443     if (d->parsed.isValid())
1444         return qvariant_cast<QSize>(d->parsed);
1445
1446     int x[2] = { 0, 0 };
1447     if (d->values.count() > 0)
1448         intValueHelper(d->values.at(0), &x[0], "px");
1449     if (d->values.count() > 1)
1450         intValueHelper(d->values.at(1), &x[1], "px");
1451     else
1452         x[1] = x[0];
1453     QSize size(x[0], x[1]);
1454     d->parsed = QVariant::fromValue<QSize>(size);
1455     return size;
1456 }
1457
1458 QRect Declaration::rectValue() const
1459 {
1460     if (d->values.count() != 1)
1461         return QRect();
1462     
1463     if (d->parsed.isValid())
1464         return qvariant_cast<QRect>(d->parsed);
1465
1466     const QCss::Value &v = d->values.at(0);
1467     if (v.type != Value::Function)
1468         return QRect();
1469     QStringList func = v.variant.toStringList();
1470     if (func.count() != 2 || func.at(0).compare(QLatin1String("rect")) != 0)
1471         return QRect();
1472     QStringList args = func[1].split(QLatin1Char(' '), QString::SkipEmptyParts);
1473     if (args.count() != 4)
1474         return QRect();
1475     QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt());
1476     d->parsed = QVariant::fromValue<QRect>(rect);
1477     return rect;
1478 }
1479
1480 void Declaration::colorValues(QColor *c, const QPalette &pal) const
1481 {
1482     int i;
1483     if (d->parsed.isValid()) {
1484         QList<QVariant> v = d->parsed.toList();
1485         for (i = 0; i < qMin(d->values.count(), 4); i++) {
1486             if (v.at(i).type() == QVariant::Color) {
1487                 c[i] = qvariant_cast<QColor>(v.at(i));
1488             } else {
1489                 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1490             }
1491         }
1492     } else {
1493         QList<QVariant> v;
1494         for (i = 0; i < qMin(d->values.count(), 4); i++) {
1495             ColorData color = parseColorValue(d->values.at(i));
1496             if(color.type == ColorData::Role) {
1497                 v += QVariant::fromValue<int>(color.role);
1498                 c[i] = pal.color((QPalette::ColorRole)(color.role));
1499             } else {
1500                 v += QVariant::fromValue<QColor>(color.color);
1501                 c[i] = color.color;
1502             }
1503         }
1504         d->parsed = v;
1505     }
1506
1507     if (i == 0) c[0] = c[1] = c[2] = c[3] = QColor();
1508     else if (i == 1) c[3] = c[2] = c[1] = c[0];
1509     else if (i == 2) c[2] = c[0], c[3] = c[1];
1510     else if (i == 3) c[3] = c[1];
1511 }
1512
1513 BorderStyle Declaration::styleValue() const
1514 {
1515     if (d->values.count() != 1)
1516         return BorderStyle_None;
1517     return parseStyleValue(d->values.at(0));
1518 }
1519
1520 void Declaration::styleValues(BorderStyle *s) const
1521 {
1522     int i;
1523     for (i = 0; i < qMin(d->values.count(), 4); i++)
1524         s[i] = parseStyleValue(d->values.at(i));
1525     if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None;
1526     else if (i == 1) s[3] = s[2] = s[1] = s[0];
1527     else if (i == 2) s[2] = s[0], s[3] = s[1];
1528     else if (i == 3) s[3] = s[1];
1529 }
1530
1531 Repeat Declaration::repeatValue() const
1532 {
1533     if (d->parsed.isValid())
1534         return static_cast<Repeat>(d->parsed.toInt());
1535     if (d->values.count() != 1)
1536         return Repeat_Unknown;
1537     int v = findKnownValue(d->values.at(0).variant.toString(),
1538                    repeats, NumKnownRepeats);
1539     d->parsed = v;
1540     return static_cast<Repeat>(v);
1541 }
1542
1543 Origin Declaration::originValue() const
1544 {
1545     if (d->parsed.isValid())
1546         return static_cast<Origin>(d->parsed.toInt());
1547     if (d->values.count() != 1)
1548         return Origin_Unknown;
1549     int v = findKnownValue(d->values.at(0).variant.toString(),
1550                                origins, NumKnownOrigins);
1551     d->parsed = v;
1552     return static_cast<Origin>(v);
1553 }
1554
1555 PositionMode Declaration::positionValue() const
1556 {
1557     if (d->parsed.isValid())
1558         return static_cast<PositionMode>(d->parsed.toInt());
1559     if (d->values.count() != 1)
1560         return PositionMode_Unknown;
1561     int v = findKnownValue(d->values.at(0).variant.toString(),
1562                            positions, NumKnownPositionModes);
1563     d->parsed = v;
1564     return static_cast<PositionMode>(v);
1565 }
1566
1567 Attachment Declaration::attachmentValue() const
1568 {
1569     if (d->parsed.isValid())
1570         return static_cast<Attachment>(d->parsed.toInt());
1571     if (d->values.count() != 1)
1572         return Attachment_Unknown;
1573     int v = findKnownValue(d->values.at(0).variant.toString(),
1574                            attachments, NumKnownAttachments);
1575     d->parsed = v;
1576     return static_cast<Attachment>(v);
1577 }
1578
1579 int Declaration::styleFeaturesValue() const
1580 {
1581     Q_ASSERT(d->propertyId == QtStyleFeatures);
1582     if (d->parsed.isValid())
1583         return d->parsed.toInt();
1584     int features = StyleFeature_None;
1585     for (int i = 0; i < d->values.count(); i++) {
1586         features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(),
1587                                      styleFeatures, NumKnownStyleFeatures));
1588     }
1589     d->parsed = features;
1590     return features;
1591 }
1592
1593 QString Declaration::uriValue() const
1594 {
1595     if (d->values.isEmpty() || d->values.at(0).type != Value::Uri)
1596         return QString();
1597     return d->values.at(0).variant.toString();
1598 }
1599
1600 Qt::Alignment Declaration::alignmentValue() const
1601 {
1602     if (d->parsed.isValid())
1603         return Qt::Alignment(d->parsed.toInt());
1604     if (d->values.isEmpty() || d->values.count() > 2)
1605         return Qt::AlignLeft | Qt::AlignTop;
1606
1607     Qt::Alignment v = parseAlignment(d->values.constData(), d->values.count());
1608     d->parsed = int(v);
1609     return v;
1610 }
1611
1612 void Declaration::borderImageValue(QString *image, int *cuts,
1613                                    TileMode *h, TileMode *v) const
1614 {
1615     *image = uriValue();
1616     for (int i = 0; i < 4; i++)
1617         cuts[i] = -1;
1618     *h = *v = TileMode_Stretch;
1619
1620     if (d->values.count() < 2)
1621         return;
1622
1623     if (d->values.at(1).type == Value::Number) { // cuts!
1624         int i;
1625         for (i = 0; i < qMin(d->values.count()-1, 4); i++) {
1626             const Value& v = d->values.at(i+1);
1627             if (v.type != Value::Number)
1628                 break;
1629             cuts[i] = v.variant.toString().toInt();
1630         }
1631         if (i == 0) cuts[0] = cuts[1] = cuts[2] = cuts[3] = 0;
1632         else if (i == 1) cuts[3] = cuts[2] = cuts[1] = cuts[0];
1633         else if (i == 2) cuts[2] = cuts[0], cuts[3] = cuts[1];
1634         else if (i == 3) cuts[3] = cuts[1];
1635     }
1636
1637     if (d->values.last().type == Value::Identifier) {
1638         *v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(),
1639                                       tileModes, NumKnownTileModes));
1640     }
1641     if (d->values[d->values.count() - 2].type == Value::Identifier) {
1642         *h = static_cast<TileMode>
1643                 (findKnownValue(d->values[d->values.count()-2].variant.toString(),
1644                                         tileModes, NumKnownTileModes));
1645     } else
1646         *h = *v;
1647 }
1648
1649 #if 0
1650 // ### Qt 5
1651 QIcon Declaration::iconValue() const
1652 {
1653     if (d->parsed.isValid())
1654         return qvariant_cast<QIcon>(d->parsed);
1655
1656     QIcon icon;
1657     for (int i = 0; i < d->values.count();) {
1658         const Value &value = d->values.at(i++);
1659         if (value.type != Value::Uri)
1660             break;
1661         QString uri = value.variant.toString();
1662         QIcon::Mode mode = QIcon::Normal;
1663         QIcon::State state = QIcon::Off;
1664         for (int j = 0; j < 2; j++) {
1665             if (i != d->values.count() && d->values.at(i).type == Value::KnownIdentifier) {
1666                 switch (d->values.at(i).variant.toInt()) {
1667                 case Value_Disabled: mode = QIcon::Disabled; break;
1668                 case Value_Active: mode = QIcon::Active; break;
1669                 case Value_Selected: mode = QIcon::Selected; break;
1670                 case Value_Normal: mode = QIcon::Normal; break;
1671                 case Value_On: state = QIcon::On; break;
1672                 case Value_Off: state = QIcon::Off; break;
1673                 default: break;
1674                 }
1675                 ++i;
1676             } else {
1677                 break;
1678             }
1679         }
1680
1681         // QIcon is soo broken
1682         if (icon.isNull())
1683             icon = QIcon(uri);
1684         else
1685             icon.addPixmap(uri, mode, state);
1686
1687         if (i == d->values.count())
1688             break;
1689
1690         if (d->values.at(i).type == Value::TermOperatorComma)
1691             i++;
1692     }
1693
1694     d->parsed = QVariant::fromValue<QIcon>(icon);
1695     return icon;
1696 }
1697 #endif
1698
1699 ///////////////////////////////////////////////////////////////////////////////
1700 // Selector
1701 int Selector::specificity() const
1702 {
1703     int val = 0;
1704     for (int i = 0; i < basicSelectors.count(); ++i) {
1705         const BasicSelector &sel = basicSelectors.at(i);
1706         if (!sel.elementName.isEmpty())
1707             val += 1;
1708
1709         val += (sel.pseudos.count() + sel.attributeSelectors.count()) * 0x10;
1710         val += sel.ids.count() * 0x100;
1711     }
1712     return val;
1713 }
1714
1715 QString Selector::pseudoElement() const
1716 {
1717     const BasicSelector& bs = basicSelectors.last();
1718     if (!bs.pseudos.isEmpty() && bs.pseudos.at(0).type == PseudoClass_Unknown)
1719         return bs.pseudos.at(0).name;
1720     return QString();
1721 }
1722
1723 quint64 Selector::pseudoClass(quint64 *negated) const
1724 {
1725     const BasicSelector& bs = basicSelectors.last();
1726     if (bs.pseudos.isEmpty())
1727         return PseudoClass_Unspecified;
1728     quint64 pc = PseudoClass_Unknown;
1729     for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.count(); i++) {
1730         const Pseudo &pseudo = bs.pseudos.at(i);
1731         if (pseudo.type == PseudoClass_Unknown)
1732             return PseudoClass_Unknown;
1733         if (!pseudo.negated)
1734             pc |= pseudo.type;
1735         else if (negated)
1736             *negated |= pseudo.type;
1737     }
1738     return pc;
1739 }
1740
1741 ///////////////////////////////////////////////////////////////////////////////
1742 // StyleSheet
1743 void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity)
1744 {
1745     QVector<StyleRule> universals;
1746     for (int i = 0; i < styleRules.count(); ++i) {
1747         const StyleRule &rule = styleRules.at(i);
1748         QVector<Selector> universalsSelectors;
1749         for (int j = 0; j < rule.selectors.count(); ++j) {
1750             const Selector& selector = rule.selectors.at(j);
1751
1752             if (selector.basicSelectors.isEmpty())
1753                 continue;
1754
1755             if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1756                 if (selector.basicSelectors.count() != 1)
1757                     continue;
1758             } else if (selector.basicSelectors.count() <= 1) {
1759                 continue;
1760             }
1761
1762             const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.count() - 1);
1763
1764             if (!sel.ids.isEmpty()) {
1765                 StyleRule nr;
1766                 nr.selectors += selector;
1767                 nr.declarations = rule.declarations;
1768                 nr.order = i;
1769                 idIndex.insert(sel.ids.at(0), nr);
1770             } else if (!sel.elementName.isEmpty()) {
1771                 StyleRule nr;
1772                 nr.selectors += selector;
1773                 nr.declarations = rule.declarations;
1774                 nr.order = i;
1775                 QString name = sel.elementName;
1776                 if (nameCaseSensitivity == Qt::CaseInsensitive)
1777                     name=name.toLower();
1778                 nameIndex.insert(name, nr);
1779             } else {
1780                 universalsSelectors += selector;
1781             }
1782         }
1783         if (!universalsSelectors.isEmpty()) {
1784             StyleRule nr;
1785             nr.selectors = universalsSelectors;
1786             nr.declarations = rule.declarations;
1787             nr.order = i;
1788             universals << nr;
1789         }
1790     }
1791     styleRules = universals;
1792 }
1793
1794 ///////////////////////////////////////////////////////////////////////////////
1795 // StyleSelector
1796 StyleSelector::~StyleSelector()
1797 {
1798 }
1799
1800 bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const
1801 {
1802     return nodeNames(node).contains(nodeName, nameCaseSensitivity);
1803 }
1804
1805 QStringList StyleSelector::nodeIds(NodePtr node) const
1806 {
1807     return QStringList(attribute(node, QLatin1String("id")));
1808 }
1809
1810 bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
1811 {
1812     if (selector.basicSelectors.isEmpty())
1813         return false;
1814
1815     if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1816         if (selector.basicSelectors.count() != 1)
1817             return false;
1818         return basicSelectorMatches(selector.basicSelectors.at(0), node);
1819     }
1820     if (selector.basicSelectors.count() <= 1)
1821         return false;
1822
1823     int i = selector.basicSelectors.count() - 1;
1824     node = duplicateNode(node);
1825     bool match = true;
1826
1827     BasicSelector sel = selector.basicSelectors.at(i);
1828     do {
1829         match = basicSelectorMatches(sel, node);
1830         if (!match) {
1831             if (sel.relationToNext == BasicSelector::MatchNextSelectorIfParent
1832                 || i == selector.basicSelectors.count() - 1) // first element must always match!
1833                 break;
1834         }
1835
1836         if (match || sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor)
1837             --i;
1838
1839         if (i < 0)
1840             break;
1841
1842         sel = selector.basicSelectors.at(i);
1843         if (sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor
1844             || sel.relationToNext == BasicSelector::MatchNextSelectorIfParent) {
1845
1846             NodePtr nextParent = parentNode(node);
1847             freeNode(node);
1848             node = nextParent;
1849        } else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfPreceeds) {
1850             NodePtr previousSibling = previousSiblingNode(node);
1851             freeNode(node);
1852             node = previousSibling;
1853        }
1854         if (isNullNode(node)) {
1855             match = false;
1856             break;
1857         }
1858    } while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor));
1859
1860     freeNode(node);
1861
1862     return match;
1863 }
1864
1865 bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
1866 {
1867     if (!sel.attributeSelectors.isEmpty()) {
1868         if (!hasAttributes(node))
1869             return false;
1870
1871         for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
1872             const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
1873
1874             const QString attrValue = attribute(node, a.name);
1875             if (attrValue.isNull())
1876                 return false;
1877
1878             if (a.valueMatchCriterium == QCss::AttributeSelector::MatchContains) {
1879
1880                 QStringList lst = attrValue.split(QLatin1Char(' '));
1881                 if (!lst.contains(a.value))
1882                     return false;
1883             } else if (
1884                 (a.valueMatchCriterium == QCss::AttributeSelector::MatchEqual
1885                  && attrValue != a.value)
1886                 ||
1887                 (a.valueMatchCriterium == QCss::AttributeSelector::MatchBeginsWith
1888                  && !attrValue.startsWith(a.value))
1889                )
1890                 return false;
1891         }
1892     }
1893
1894     if (!sel.elementName.isEmpty()
1895         && !nodeNameEquals(node, sel.elementName))
1896             return false;
1897
1898     if (!sel.ids.isEmpty()
1899         && sel.ids != nodeIds(node))
1900             return false;
1901
1902     return true;
1903 }
1904
1905 void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin,
1906                                int depth, QMap<uint, StyleRule> *weightedRules)
1907 {
1908     for (int j = 0; j < rule.selectors.count(); ++j) {
1909         const Selector& selector = rule.selectors.at(j);
1910         if (selectorMatches(selector, node)) {
1911             uint weight = rule.order
1912                         + selector.specificity() *0x100
1913                         + (uint(origin) + depth)*0x100000;
1914             StyleRule newRule = rule;
1915             if(rule.selectors.count() > 1) {
1916                 newRule.selectors.resize(1);
1917                 newRule.selectors[0] = selector;
1918             }
1919             //We might have rules with the same weight if they came from a rule with several selectors
1920             weightedRules->insertMulti(weight, newRule);
1921         }
1922     }
1923 }
1924
1925 // Returns style rules that are in ascending order of specificity
1926 // Each of the StyleRule returned will contain exactly one Selector
1927 QVector<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
1928 {
1929     QVector<StyleRule> rules;
1930     if (styleSheets.isEmpty())
1931         return rules;
1932
1933     QMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below
1934     
1935     //prune using indexed stylesheet
1936     for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) {
1937         const StyleSheet &styleSheet = styleSheets.at(sheetIdx);
1938         for (int i = 0; i < styleSheet.styleRules.count(); ++i) {
1939             matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);
1940         }
1941
1942         if (!styleSheet.idIndex.isEmpty()) {
1943             QStringList ids = nodeIds(node);
1944             for (int i = 0; i < ids.count(); i++) {
1945                 const QString &key = ids.at(i);
1946                 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key);
1947                 while (it != styleSheet.idIndex.constEnd() && it.key() == key) {
1948                     matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1949                     ++it;
1950                 }
1951             }
1952         }
1953         if (!styleSheet.nameIndex.isEmpty()) {
1954             QStringList names = nodeNames(node);
1955             for (int i = 0; i < names.count(); i++) {
1956                 QString name = names.at(i);
1957                 if (nameCaseSensitivity == Qt::CaseInsensitive)
1958                     name = name.toLower();
1959                 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.nameIndex.constFind(name);
1960                 while (it != styleSheet.nameIndex.constEnd() && it.key() == name) {
1961                     matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1962                     ++it;
1963                 }
1964             }
1965         }
1966         if (!medium.isEmpty()) {
1967             for (int i = 0; i < styleSheet.mediaRules.count(); ++i) {
1968                 if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {
1969                     for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) {
1970                         matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,
1971                                styleSheet.depth, &weightedRules);
1972                     }
1973                 }
1974             }
1975         }
1976     }
1977
1978     rules.reserve(weightedRules.count());
1979     QMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin();
1980     for ( ; it != weightedRules.constEnd() ; ++it)
1981         rules += *it;
1982
1983     return rules;
1984 }
1985
1986 // for qtexthtmlparser which requires just the declarations with Enabled state
1987 // and without pseudo elements
1988 QVector<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *extraPseudo)
1989 {
1990     QVector<Declaration> decls;
1991     QVector<StyleRule> rules = styleRulesForNode(node);
1992     for (int i = 0; i < rules.count(); i++) {
1993         const Selector& selector = rules.at(i).selectors.at(0);
1994         const QString pseudoElement = selector.pseudoElement();
1995
1996         if (extraPseudo && pseudoElement == QLatin1String(extraPseudo)) {
1997             decls += rules.at(i).declarations;
1998             continue;
1999         }
2000
2001         if (!pseudoElement.isEmpty()) // skip rules with pseudo elements
2002             continue;
2003         quint64 pseudoClass = selector.pseudoClass();
2004         if (pseudoClass == PseudoClass_Enabled || pseudoClass == PseudoClass_Unspecified)
2005             decls += rules.at(i).declarations;
2006     }
2007     return decls;
2008 }
2009
2010 static inline bool isHexDigit(const char c)
2011 {
2012     return (c >= '0' && c <= '9')
2013            || (c >= 'a' && c <= 'f')
2014            || (c >= 'A' && c <= 'F')
2015            ;
2016 }
2017
2018 QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences)
2019 {
2020     QString output = input;
2021
2022     if (hasEscapeSequences)
2023         *hasEscapeSequences = false;
2024
2025     int i = 0;
2026     while (i < output.size()) {
2027         if (output.at(i) == QLatin1Char('\\')) {
2028
2029             ++i;
2030             // test for unicode hex escape
2031             int hexCount = 0;
2032             const int hexStart = i;
2033             while (i < output.size()
2034                    && isHexDigit(output.at(i).toLatin1())
2035                    && hexCount < 7) {
2036                 ++hexCount;
2037                 ++i;
2038             }
2039             if (hexCount == 0) {
2040                 if (hasEscapeSequences)
2041                     *hasEscapeSequences = true;
2042                 continue;
2043             }
2044
2045             hexCount = qMin(hexCount, 6);
2046             bool ok = false;
2047             ushort code = output.mid(hexStart, hexCount).toUShort(&ok, 16);
2048             if (ok) {
2049                 output.replace(hexStart - 1, hexCount + 1, QChar(code));
2050                 i = hexStart;
2051             } else {
2052                 i = hexStart;
2053             }
2054         } else {
2055             ++i;
2056         }
2057     }
2058     return output;
2059 }
2060
2061 int QCssScanner_Generated::handleCommentStart()
2062 {
2063     while (pos < input.size() - 1) {
2064         if (input.at(pos) == QLatin1Char('*')
2065             && input.at(pos + 1) == QLatin1Char('/')) {
2066             pos += 2;
2067             break;
2068         }
2069         ++pos;
2070     }
2071     return S;
2072 }
2073
2074 void Scanner::scan(const QString &preprocessedInput, QVector<Symbol> *symbols)
2075 {
2076     QCssScanner_Generated scanner(preprocessedInput);
2077     Symbol sym;
2078     int tok = scanner.lex();
2079     while (tok != -1) {
2080         sym.token = static_cast<QCss::TokenType>(tok);
2081         sym.text = scanner.input;
2082         sym.start = scanner.lexemStart;
2083         sym.len = scanner.lexemLength;
2084         symbols->append(sym);
2085         tok = scanner.lex();
2086     }
2087 }
2088
2089 QString Symbol::lexem() const
2090 {
2091     QString result;
2092     if (len > 0)
2093         result.reserve(len);
2094     for (int i = 0; i < len; ++i) {
2095         if (text.at(start + i) == QLatin1Char('\\') && i < len - 1)
2096             ++i;
2097         result += text.at(start + i);
2098     }
2099     return result;
2100 }
2101
2102 Parser::Parser(const QString &css, bool isFile)
2103 {
2104     init(css, isFile);
2105 }
2106
2107 Parser::Parser()
2108 {
2109     index = 0;
2110     errorIndex = -1;
2111     hasEscapeSequences = false;
2112 }
2113
2114 void Parser::init(const QString &css, bool isFile)
2115 {
2116     QString styleSheet = css;
2117     if (isFile) {
2118         QFile file(css);
2119         if (file.open(QFile::ReadOnly)) {
2120             sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1Char('/');
2121             QTextStream stream(&file);
2122             styleSheet = stream.readAll();
2123         } else {
2124             qWarning() << "QCss::Parser - Failed to load file " << css;
2125             styleSheet.clear();
2126         }
2127     } else {
2128         sourcePath.clear();
2129     }
2130
2131     hasEscapeSequences = false;
2132     symbols.resize(0);
2133     symbols.reserve(8);
2134     Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols);
2135     index = 0;
2136     errorIndex = -1;
2137 }
2138
2139 bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity)
2140 {
2141     if (testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("charset"))) {
2142         if (!next(STRING)) return false;
2143         if (!next(SEMICOLON)) return false;
2144     }
2145
2146     while (test(S) || test(CDO) || test(CDC)) {}
2147
2148     while (testImport()) {
2149         ImportRule rule;
2150         if (!parseImport(&rule)) return false;
2151         styleSheet->importRules.append(rule);
2152         while (test(S) || test(CDO) || test(CDC)) {}
2153     }
2154
2155     do {
2156         if (testMedia()) {
2157             MediaRule rule;
2158             if (!parseMedia(&rule)) return false;
2159             styleSheet->mediaRules.append(rule);
2160         } else if (testPage()) {
2161             PageRule rule;
2162             if (!parsePage(&rule)) return false;
2163             styleSheet->pageRules.append(rule);
2164         } else if (testRuleset()) {
2165             StyleRule rule;
2166             if (!parseRuleset(&rule)) return false;
2167             styleSheet->styleRules.append(rule);
2168         } else if (test(ATKEYWORD_SYM)) {
2169             if (!until(RBRACE)) return false;
2170         } else if (hasNext()) {
2171             return false;
2172         }
2173         while (test(S) || test(CDO) || test(CDC)) {}
2174     } while (hasNext());
2175     styleSheet->buildIndexes(nameCaseSensitivity);
2176     return true;
2177 }
2178
2179 Symbol Parser::errorSymbol()
2180 {
2181     if (errorIndex == -1) return Symbol();
2182     return symbols.at(errorIndex);
2183 }
2184
2185 static inline void removeOptionalQuotes(QString *str)
2186 {
2187     if (!str->startsWith(QLatin1Char('\''))
2188         && !str->startsWith(QLatin1Char('\"')))
2189         return;
2190     str->remove(0, 1);
2191     str->chop(1);
2192 }
2193
2194 bool Parser::parseImport(ImportRule *importRule)
2195 {
2196     skipSpace();
2197
2198     if (test(STRING)) {
2199         importRule->href = lexem();
2200     } else {
2201         if (!testAndParseUri(&importRule->href)) return false;
2202     }
2203     removeOptionalQuotes(&importRule->href);
2204
2205     skipSpace();
2206
2207     if (testMedium()) {
2208         if (!parseMedium(&importRule->media)) return false;
2209
2210         while (test(COMMA)) {
2211             skipSpace();
2212             if (!parseNextMedium(&importRule->media)) return false;
2213         }
2214     }
2215
2216     if (!next(SEMICOLON)) return false;
2217
2218     skipSpace();
2219     return true;
2220 }
2221
2222 bool Parser::parseMedia(MediaRule *mediaRule)
2223 {
2224     do {
2225         skipSpace();
2226         if (!parseNextMedium(&mediaRule->media)) return false;
2227     } while (test(COMMA));
2228
2229     if (!next(LBRACE)) return false;
2230     skipSpace();
2231
2232     while (testRuleset()) {
2233         StyleRule rule;
2234         if (!parseRuleset(&rule)) return false;
2235         mediaRule->styleRules.append(rule);
2236     }
2237
2238     if (!next(RBRACE)) return false;
2239     skipSpace();
2240     return true;
2241 }
2242
2243 bool Parser::parseMedium(QStringList *media)
2244 {
2245     media->append(lexem());
2246     skipSpace();
2247     return true;
2248 }
2249
2250 bool Parser::parsePage(PageRule *pageRule)
2251 {
2252     skipSpace();
2253
2254     if (testPseudoPage())
2255         if (!parsePseudoPage(&pageRule->selector)) return false;
2256
2257     skipSpace();
2258     if (!next(LBRACE)) return false;
2259
2260     do {
2261         skipSpace();
2262         Declaration decl;
2263         if (!parseNextDeclaration(&decl)) return false;
2264         if (!decl.isEmpty())
2265             pageRule->declarations.append(decl);
2266     } while (test(SEMICOLON));
2267
2268     if (!next(RBRACE)) return false;
2269     skipSpace();
2270     return true;
2271 }
2272
2273 bool Parser::parsePseudoPage(QString *selector)
2274 {
2275     if (!next(IDENT)) return false;
2276     *selector = lexem();
2277     return true;
2278 }
2279
2280 bool Parser::parseNextOperator(Value *value)
2281 {
2282     if (!hasNext()) return true;
2283     switch (next()) {
2284         case SLASH: value->type = Value::TermOperatorSlash; skipSpace(); break;
2285         case COMMA: value->type = Value::TermOperatorComma; skipSpace(); break;
2286         default: prev(); break;
2287     }
2288     return true;
2289 }
2290
2291 bool Parser::parseCombinator(BasicSelector::Relation *relation)
2292 {
2293     *relation = BasicSelector::NoRelation;
2294     if (lookup() == S) {
2295         *relation = BasicSelector::MatchNextSelectorIfAncestor;
2296         skipSpace();
2297     } else {
2298         prev();
2299     }
2300     if (test(PLUS)) {
2301         *relation = BasicSelector::MatchNextSelectorIfPreceeds;
2302     } else if (test(GREATER)) {
2303         *relation = BasicSelector::MatchNextSelectorIfParent;
2304     }
2305     skipSpace();
2306     return true;
2307 }
2308
2309 bool Parser::parseProperty(Declaration *decl)
2310 {
2311     decl->d->property = lexem();
2312     decl->d->propertyId = static_cast<Property>(findKnownValue(decl->d->property, properties, NumProperties));
2313     skipSpace();
2314     return true;
2315 }
2316
2317 bool Parser::parseRuleset(StyleRule *styleRule)
2318 {
2319     Selector sel;
2320     if (!parseSelector(&sel)) return false;
2321     styleRule->selectors.append(sel);
2322
2323     while (test(COMMA)) {
2324         skipSpace();
2325         Selector sel;
2326         if (!parseNextSelector(&sel)) return false;
2327         styleRule->selectors.append(sel);
2328     }
2329
2330     skipSpace();
2331     if (!next(LBRACE)) return false;
2332     const int declarationStart = index;
2333
2334     do {
2335         skipSpace();
2336         Declaration decl;
2337         const int rewind = index;
2338         if (!parseNextDeclaration(&decl)) {
2339             index = rewind;
2340             const bool foundSemicolon = until(SEMICOLON);
2341             const int semicolonIndex = index;
2342
2343             index = declarationStart;
2344             const bool foundRBrace = until(RBRACE);
2345
2346             if (foundSemicolon && semicolonIndex < index) {
2347                 decl = Declaration();
2348                 index = semicolonIndex - 1;
2349             } else {
2350                 skipSpace();
2351                 return foundRBrace;
2352             }
2353         }
2354         if (!decl.isEmpty())
2355             styleRule->declarations.append(decl);
2356     } while (test(SEMICOLON));
2357
2358     if (!next(RBRACE)) return false;
2359     skipSpace();
2360     return true;
2361 }
2362
2363 bool Parser::parseSelector(Selector *sel)
2364 {
2365     BasicSelector basicSel;
2366     if (!parseSimpleSelector(&basicSel)) return false;
2367     while (testCombinator()) {
2368         if (!parseCombinator(&basicSel.relationToNext)) return false;
2369
2370         if (!testSimpleSelector()) break;
2371         sel->basicSelectors.append(basicSel);
2372
2373         basicSel = BasicSelector();
2374         if (!parseSimpleSelector(&basicSel)) return false;
2375     }
2376     sel->basicSelectors.append(basicSel);
2377     return true;
2378 }
2379
2380 bool Parser::parseSimpleSelector(BasicSelector *basicSel)
2381 {
2382     int minCount = 0;
2383     if (lookupElementName()) {
2384         if (!parseElementName(&basicSel->elementName)) return false;
2385     } else {
2386         prev();
2387         minCount = 1;
2388     }
2389     bool onceMore;
2390     int count = 0;
2391     do {
2392         onceMore = false;
2393         if (test(HASH)) {
2394             QString theid = lexem();
2395             // chop off leading #
2396             theid.remove(0, 1);
2397             basicSel->ids.append(theid);
2398             onceMore = true;
2399         } else if (testClass()) {
2400             onceMore = true;
2401             AttributeSelector a;
2402             a.name = QLatin1String("class");
2403             a.valueMatchCriterium = AttributeSelector::MatchContains;
2404             if (!parseClass(&a.value)) return false;
2405             basicSel->attributeSelectors.append(a);
2406         } else if (testAttrib()) {
2407             onceMore = true;
2408             AttributeSelector a;
2409             if (!parseAttrib(&a)) return false;
2410             basicSel->attributeSelectors.append(a);
2411         } else if (testPseudo()) {
2412             onceMore = true;
2413             Pseudo ps;
2414             if (!parsePseudo(&ps)) return false;
2415             basicSel->pseudos.append(ps);
2416         }
2417         if (onceMore) ++count;
2418     } while (onceMore);
2419     return count >= minCount;
2420 }
2421
2422 bool Parser::parseClass(QString *name)
2423 {
2424     if (!next(IDENT)) return false;
2425     *name = lexem();
2426     return true;
2427 }
2428
2429 bool Parser::parseElementName(QString *name)
2430 {
2431     switch (lookup()) {
2432         case STAR: name->clear(); break;
2433         case IDENT: *name = lexem(); break;
2434         default: return false;
2435     }
2436     return true;
2437 }
2438
2439 bool Parser::parseAttrib(AttributeSelector *attr)
2440 {
2441     skipSpace();
2442     if (!next(IDENT)) return false;
2443     attr->name = lexem();
2444     skipSpace();
2445
2446     if (test(EQUAL)) {
2447         attr->valueMatchCriterium = AttributeSelector::MatchEqual;
2448     } else if (test(INCLUDES)) {
2449         attr->valueMatchCriterium = AttributeSelector::MatchContains;
2450     } else if (test(DASHMATCH)) {
2451         attr->valueMatchCriterium = AttributeSelector::MatchBeginsWith;
2452     } else {
2453         return next(RBRACKET);
2454     }
2455
2456     skipSpace();
2457
2458     if (!test(IDENT) && !test(STRING)) return false;
2459     attr->value = unquotedLexem();
2460
2461     skipSpace();
2462     return next(RBRACKET);
2463 }
2464
2465 bool Parser::parsePseudo(Pseudo *pseudo)
2466 {
2467     (void)test(COLON);
2468     pseudo->negated = test(EXCLAMATION_SYM);
2469     if (test(IDENT)) {
2470         pseudo->name = lexem();
2471         pseudo->type = static_cast<quint64>(findKnownValue(pseudo->name, pseudos, NumPseudos));
2472         return true;
2473     }
2474     if (!next(FUNCTION)) return false;
2475     pseudo->function = lexem();
2476     // chop off trailing parenthesis
2477     pseudo->function.chop(1);
2478     skipSpace();
2479     if (!test(IDENT)) return false;
2480     pseudo->name = lexem();
2481     skipSpace();
2482     return next(RPAREN);
2483 }
2484
2485 bool Parser::parseNextDeclaration(Declaration *decl)
2486 {
2487     if (!testProperty())
2488         return true; // not an error!
2489     if (!parseProperty(decl)) return false;
2490     if (!next(COLON)) return false;
2491     skipSpace();
2492     if (!parseNextExpr(&decl->d->values)) return false;
2493     if (testPrio())
2494         if (!parsePrio(decl)) return false;
2495     return true;
2496 }
2497
2498 bool Parser::testPrio()
2499 {
2500     const int rewind = index;
2501     if (!test(EXCLAMATION_SYM)) return false;
2502     skipSpace();
2503     if (!test(IDENT)) {
2504         index = rewind;
2505         return false;
2506     }
2507     if (lexem().compare(QLatin1String("important"), Qt::CaseInsensitive) != 0) {
2508         index = rewind;
2509         return false;
2510     }
2511     return true;
2512 }
2513
2514 bool Parser::parsePrio(Declaration *declaration)
2515 {
2516     declaration->d->important = true;
2517     skipSpace();
2518     return true;
2519 }
2520
2521 bool Parser::parseExpr(QVector<Value> *values)
2522 {
2523     Value val;
2524     if (!parseTerm(&val)) return false;
2525     values->append(val);
2526     bool onceMore;
2527     do {
2528         onceMore = false;
2529         val = Value();
2530         if (!parseNextOperator(&val)) return false;
2531         if (val.type != QCss::Value::Unknown)
2532             values->append(val);
2533         if (testTerm()) {
2534             onceMore = true;
2535             val = Value();
2536             if (!parseTerm(&val)) return false;
2537             values->append(val);
2538         }
2539     } while (onceMore);
2540     return true;
2541 }
2542
2543 bool Parser::testTerm()
2544 {
2545     return test(PLUS) || test(MINUS)
2546            || test(NUMBER)
2547            || test(PERCENTAGE)
2548            || test(LENGTH)
2549            || test(STRING)
2550            || test(IDENT)
2551            || testHexColor()
2552            || testFunction();
2553 }
2554
2555 bool Parser::parseTerm(Value *value)
2556 {
2557     QString str = lexem();
2558     bool haveUnary = false;
2559     if (lookup() == PLUS || lookup() == MINUS) {
2560         haveUnary = true;
2561         if (!hasNext()) return false;
2562         next();
2563         str += lexem();
2564     }
2565
2566     value->variant = str;
2567     value->type = QCss::Value::String;
2568     switch (lookup()) {
2569         case NUMBER:
2570             value->type = Value::Number;
2571             value->variant.convert(QVariant::Double);
2572             break;
2573         case PERCENTAGE:
2574             value->type = Value::Percentage;
2575             str.chop(1); // strip off %
2576             value->variant = str;
2577             break;
2578         case LENGTH:
2579             value->type = Value::Length;
2580             break;
2581
2582         case STRING:
2583             if (haveUnary) return false;
2584             value->type = Value::String;
2585             str.chop(1);
2586             str.remove(0, 1);
2587             value->variant = str;
2588             break;
2589         case IDENT: {
2590             if (haveUnary) return false;
2591             value->type = Value::Identifier;
2592             const int theid = findKnownValue(str, values, NumKnownValues);
2593             if (theid != 0) {
2594                 value->type = Value::KnownIdentifier;
2595                 value->variant = theid;
2596             }
2597             break;
2598         }
2599         default: {
2600             if (haveUnary) return false;
2601             prev();
2602             if (testHexColor()) {
2603                 QColor col;
2604                 if (!parseHexColor(&col)) return false;
2605                 value->type = Value::Color;
2606                 value->variant = col;
2607             } else if (testFunction()) {
2608                 QString name, args;
2609                 if (!parseFunction(&name, &args)) return false;
2610                 if (name == QLatin1String("url")) {
2611                     value->type = Value::Uri;
2612                     removeOptionalQuotes(&args);
2613                     if (QFileInfo(args).isRelative() && !sourcePath.isEmpty()) {
2614                         args.prepend(sourcePath);
2615                     }
2616                     value->variant = args;
2617                 } else {
2618                     value->type = Value::Function;
2619                     value->variant = QStringList() << name << args;
2620                 }
2621             } else {
2622                 return recordError();
2623             }
2624             return true;
2625         }
2626     }
2627     skipSpace();
2628     return true;
2629 }
2630
2631 bool Parser::parseFunction(QString *name, QString *args)
2632 {
2633     *name = lexem();
2634     name->chop(1);
2635     skipSpace();
2636     const int start = index;
2637     if (!until(RPAREN)) return false;
2638     for (int i = start; i < index - 1; ++i)
2639         args->append(symbols.at(i).lexem());
2640     /*
2641     if (!nextExpr(&arguments)) return false;
2642     if (!next(RPAREN)) return false;
2643     */
2644     skipSpace();
2645     return true;
2646 }
2647
2648 bool Parser::parseHexColor(QColor *col)
2649 {
2650     col->setNamedColor(lexem());
2651     if (!col->isValid()) {
2652         qWarning("QCssParser::parseHexColor: Unknown color name '%s'",lexem().toLatin1().constData());
2653         return false;
2654     }
2655     skipSpace();
2656     return true;
2657 }
2658
2659 bool Parser::testAndParseUri(QString *uri)
2660 {
2661     const int rewind = index;
2662     if (!testFunction()) return false;
2663
2664     QString name, args;
2665     if (!parseFunction(&name, &args)) {
2666         index = rewind;
2667         return false;
2668     }
2669     if (name.toLower() != QLatin1String("url")) {
2670         index = rewind;
2671         return false;
2672     }
2673     *uri = args;
2674     removeOptionalQuotes(uri);
2675     return true;
2676 }
2677
2678 bool Parser::testSimpleSelector()
2679 {
2680     return testElementName()
2681            || (test(HASH))
2682            || testClass()
2683            || testAttrib()
2684            || testPseudo();
2685 }
2686
2687 bool Parser::next(QCss::TokenType t)
2688 {
2689     if (hasNext() && next() == t)
2690         return true;
2691     return recordError();
2692 }
2693
2694 bool Parser::test(QCss::TokenType t)
2695 {
2696     if (index >= symbols.count())
2697         return false;
2698     if (symbols.at(index).token == t) {
2699         ++index;
2700         return true;
2701     }
2702     return false;
2703 }
2704
2705 QString Parser::unquotedLexem() const
2706 {
2707     QString s = lexem();
2708     if (lookup() == STRING) {
2709         s.chop(1);
2710         s.remove(0, 1);
2711     }
2712     return s;
2713 }
2714
2715 QString Parser::lexemUntil(QCss::TokenType t)
2716 {
2717     QString lexem;
2718     while (hasNext() && next() != t)
2719         lexem += symbol().lexem();
2720     return lexem;
2721 }
2722
2723 bool Parser::until(QCss::TokenType target, QCss::TokenType target2)
2724 {
2725     int braceCount = 0;
2726     int brackCount = 0;
2727     int parenCount = 0;
2728     if (index) {
2729         switch(symbols.at(index-1).token) {
2730         case LBRACE: ++braceCount; break;
2731         case LBRACKET: ++brackCount; break;
2732         case FUNCTION:
2733         case LPAREN: ++parenCount; break;
2734         default: ;
2735         }
2736     }
2737     while (index < symbols.size()) {
2738         QCss::TokenType t = symbols.at(index++).token;
2739         switch (t) {
2740         case LBRACE: ++braceCount; break;
2741         case RBRACE: --braceCount; break;
2742         case LBRACKET: ++brackCount; break;
2743         case RBRACKET: --brackCount; break;
2744         case FUNCTION:
2745         case LPAREN: ++parenCount; break;
2746         case RPAREN: --parenCount; break;
2747         default: break;
2748         }
2749         if ((t == target || (target2 != NONE && t == target2))
2750             && braceCount <= 0
2751             && brackCount <= 0
2752             && parenCount <= 0)
2753             return true;
2754
2755         if (braceCount < 0 || brackCount < 0 || parenCount < 0) {
2756             --index;
2757             break;
2758         }
2759     }
2760     return false;
2761 }
2762
2763 bool Parser::testTokenAndEndsWith(QCss::TokenType t, const QLatin1String &str)
2764 {
2765     if (!test(t)) return false;
2766     if (!lexem().endsWith(str, Qt::CaseInsensitive)) {
2767         prev();
2768         return false;
2769     }
2770     return true;
2771 }
2772
2773 QT_END_NAMESPACE
2774 #endif // QT_NO_CSSPARSER