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