Coverage Report

Created: 2026-02-14 07:09

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