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