Fix uses of qRound on non-floating-point types.
[profile/ivi/qtbase.git] / src / gui / text / qtexthtmlparser.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qtexthtmlparser_p.h"
43
44 #include <qbytearray.h>
45 #include <qtextcodec.h>
46 #include <qapplication.h>
47 #include <qstack.h>
48 #include <qdebug.h>
49 #include <qthread.h>
50
51 #include "qtextdocument.h"
52 #include "qtextformat_p.h"
53 #include "qtextdocument_p.h"
54 #include "qtextcursor.h"
55 #include "qfont_p.h"
56 #include "private/qfunctions_p.h"
57
58 #ifndef QT_NO_TEXTHTMLPARSER
59
60 QT_BEGIN_NAMESPACE
61
62 // see also tst_qtextdocumentfragment.cpp
63 #define MAX_ENTITY 258
64 static const struct QTextHtmlEntity { const char *name; quint16 code; } entities[MAX_ENTITY]= {
65     { "AElig", 0x00c6 },
66     { "AMP", 38 },
67     { "Aacute", 0x00c1 },
68     { "Acirc", 0x00c2 },
69     { "Agrave", 0x00c0 },
70     { "Alpha", 0x0391 },
71     { "Aring", 0x00c5 },
72     { "Atilde", 0x00c3 },
73     { "Auml", 0x00c4 },
74     { "Beta", 0x0392 },
75     { "Ccedil", 0x00c7 },
76     { "Chi", 0x03a7 },
77     { "Dagger", 0x2021 },
78     { "Delta", 0x0394 },
79     { "ETH", 0x00d0 },
80     { "Eacute", 0x00c9 },
81     { "Ecirc", 0x00ca },
82     { "Egrave", 0x00c8 },
83     { "Epsilon", 0x0395 },
84     { "Eta", 0x0397 },
85     { "Euml", 0x00cb },
86     { "GT", 62 },
87     { "Gamma", 0x0393 },
88     { "Iacute", 0x00cd },
89     { "Icirc", 0x00ce },
90     { "Igrave", 0x00cc },
91     { "Iota", 0x0399 },
92     { "Iuml", 0x00cf },
93     { "Kappa", 0x039a },
94     { "LT", 60 },
95     { "Lambda", 0x039b },
96     { "Mu", 0x039c },
97     { "Ntilde", 0x00d1 },
98     { "Nu", 0x039d },
99     { "OElig", 0x0152 },
100     { "Oacute", 0x00d3 },
101     { "Ocirc", 0x00d4 },
102     { "Ograve", 0x00d2 },
103     { "Omega", 0x03a9 },
104     { "Omicron", 0x039f },
105     { "Oslash", 0x00d8 },
106     { "Otilde", 0x00d5 },
107     { "Ouml", 0x00d6 },
108     { "Phi", 0x03a6 },
109     { "Pi", 0x03a0 },
110     { "Prime", 0x2033 },
111     { "Psi", 0x03a8 },
112     { "QUOT", 34 },
113     { "Rho", 0x03a1 },
114     { "Scaron", 0x0160 },
115     { "Sigma", 0x03a3 },
116     { "THORN", 0x00de },
117     { "Tau", 0x03a4 },
118     { "Theta", 0x0398 },
119     { "Uacute", 0x00da },
120     { "Ucirc", 0x00db },
121     { "Ugrave", 0x00d9 },
122     { "Upsilon", 0x03a5 },
123     { "Uuml", 0x00dc },
124     { "Xi", 0x039e },
125     { "Yacute", 0x00dd },
126     { "Yuml", 0x0178 },
127     { "Zeta", 0x0396 },
128     { "aacute", 0x00e1 },
129     { "acirc", 0x00e2 },
130     { "acute", 0x00b4 },
131     { "aelig", 0x00e6 },
132     { "agrave", 0x00e0 },
133     { "alefsym", 0x2135 },
134     { "alpha", 0x03b1 },
135     { "amp", 38 },
136     { "and", 0x22a5 },
137     { "ang", 0x2220 },
138     { "apos", 0x0027 },
139     { "aring", 0x00e5 },
140     { "asymp", 0x2248 },
141     { "atilde", 0x00e3 },
142     { "auml", 0x00e4 },
143     { "bdquo", 0x201e },
144     { "beta", 0x03b2 },
145     { "brvbar", 0x00a6 },
146     { "bull", 0x2022 },
147     { "cap", 0x2229 },
148     { "ccedil", 0x00e7 },
149     { "cedil", 0x00b8 },
150     { "cent", 0x00a2 },
151     { "chi", 0x03c7 },
152     { "circ", 0x02c6 },
153     { "clubs", 0x2663 },
154     { "cong", 0x2245 },
155     { "copy", 0x00a9 },
156     { "crarr", 0x21b5 },
157     { "cup", 0x222a },
158     { "curren", 0x00a4 },
159     { "dArr", 0x21d3 },
160     { "dagger", 0x2020 },
161     { "darr", 0x2193 },
162     { "deg", 0x00b0 },
163     { "delta", 0x03b4 },
164     { "diams", 0x2666 },
165     { "divide", 0x00f7 },
166     { "eacute", 0x00e9 },
167     { "ecirc", 0x00ea },
168     { "egrave", 0x00e8 },
169     { "empty", 0x2205 },
170     { "emsp", 0x2003 },
171     { "ensp", 0x2002 },
172     { "epsilon", 0x03b5 },
173     { "equiv", 0x2261 },
174     { "eta", 0x03b7 },
175     { "eth", 0x00f0 },
176     { "euml", 0x00eb },
177     { "euro", 0x20ac },
178     { "exist", 0x2203 },
179     { "fnof", 0x0192 },
180     { "forall", 0x2200 },
181     { "frac12", 0x00bd },
182     { "frac14", 0x00bc },
183     { "frac34", 0x00be },
184     { "frasl", 0x2044 },
185     { "gamma", 0x03b3 },
186     { "ge", 0x2265 },
187     { "gt", 62 },
188     { "hArr", 0x21d4 },
189     { "harr", 0x2194 },
190     { "hearts", 0x2665 },
191     { "hellip", 0x2026 },
192     { "iacute", 0x00ed },
193     { "icirc", 0x00ee },
194     { "iexcl", 0x00a1 },
195     { "igrave", 0x00ec },
196     { "image", 0x2111 },
197     { "infin", 0x221e },
198     { "int", 0x222b },
199     { "iota", 0x03b9 },
200     { "iquest", 0x00bf },
201     { "isin", 0x2208 },
202     { "iuml", 0x00ef },
203     { "kappa", 0x03ba },
204     { "lArr", 0x21d0 },
205     { "lambda", 0x03bb },
206     { "lang", 0x2329 },
207     { "laquo", 0x00ab },
208     { "larr", 0x2190 },
209     { "lceil", 0x2308 },
210     { "ldquo", 0x201c },
211     { "le", 0x2264 },
212     { "lfloor", 0x230a },
213     { "lowast", 0x2217 },
214     { "loz", 0x25ca },
215     { "lrm", 0x200e },
216     { "lsaquo", 0x2039 },
217     { "lsquo", 0x2018 },
218     { "lt", 60 },
219     { "macr", 0x00af },
220     { "mdash", 0x2014 },
221     { "micro", 0x00b5 },
222     { "middot", 0x00b7 },
223     { "minus", 0x2212 },
224     { "mu", 0x03bc },
225     { "nabla", 0x2207 },
226     { "nbsp", 0x00a0 },
227     { "ndash", 0x2013 },
228     { "ne", 0x2260 },
229     { "ni", 0x220b },
230     { "not", 0x00ac },
231     { "notin", 0x2209 },
232     { "nsub", 0x2284 },
233     { "ntilde", 0x00f1 },
234     { "nu", 0x03bd },
235     { "oacute", 0x00f3 },
236     { "ocirc", 0x00f4 },
237     { "oelig", 0x0153 },
238     { "ograve", 0x00f2 },
239     { "oline", 0x203e },
240     { "omega", 0x03c9 },
241     { "omicron", 0x03bf },
242     { "oplus", 0x2295 },
243     { "or", 0x22a6 },
244     { "ordf", 0x00aa },
245     { "ordm", 0x00ba },
246     { "oslash", 0x00f8 },
247     { "otilde", 0x00f5 },
248     { "otimes", 0x2297 },
249     { "ouml", 0x00f6 },
250     { "para", 0x00b6 },
251     { "part", 0x2202 },
252     { "percnt", 0x0025 },
253     { "permil", 0x2030 },
254     { "perp", 0x22a5 },
255     { "phi", 0x03c6 },
256     { "pi", 0x03c0 },
257     { "piv", 0x03d6 },
258     { "plusmn", 0x00b1 },
259     { "pound", 0x00a3 },
260     { "prime", 0x2032 },
261     { "prod", 0x220f },
262     { "prop", 0x221d },
263     { "psi", 0x03c8 },
264     { "quot", 34 },
265     { "rArr", 0x21d2 },
266     { "radic", 0x221a },
267     { "rang", 0x232a },
268     { "raquo", 0x00bb },
269     { "rarr", 0x2192 },
270     { "rceil", 0x2309 },
271     { "rdquo", 0x201d },
272     { "real", 0x211c },
273     { "reg", 0x00ae },
274     { "rfloor", 0x230b },
275     { "rho", 0x03c1 },
276     { "rlm", 0x200f },
277     { "rsaquo", 0x203a },
278     { "rsquo", 0x2019 },
279     { "sbquo", 0x201a },
280     { "scaron", 0x0161 },
281     { "sdot", 0x22c5 },
282     { "sect", 0x00a7 },
283     { "shy", 0x00ad },
284     { "sigma", 0x03c3 },
285     { "sigmaf", 0x03c2 },
286     { "sim", 0x223c },
287     { "spades", 0x2660 },
288     { "sub", 0x2282 },
289     { "sube", 0x2286 },
290     { "sum", 0x2211 },
291     { "sup", 0x2283 },
292     { "sup1", 0x00b9 },
293     { "sup2", 0x00b2 },
294     { "sup3", 0x00b3 },
295     { "supe", 0x2287 },
296     { "szlig", 0x00df },
297     { "tau", 0x03c4 },
298     { "there4", 0x2234 },
299     { "theta", 0x03b8 },
300     { "thetasym", 0x03d1 },
301     { "thinsp", 0x2009 },
302     { "thorn", 0x00fe },
303     { "tilde", 0x02dc },
304     { "times", 0x00d7 },
305     { "trade", 0x2122 },
306     { "uArr", 0x21d1 },
307     { "uacute", 0x00fa },
308     { "uarr", 0x2191 },
309     { "ucirc", 0x00fb },
310     { "ugrave", 0x00f9 },
311     { "uml", 0x00a8 },
312     { "upsih", 0x03d2 },
313     { "upsilon", 0x03c5 },
314     { "uuml", 0x00fc },
315     { "weierp", 0x2118 },
316     { "xi", 0x03be },
317     { "yacute", 0x00fd },
318     { "yen", 0x00a5 },
319     { "yuml", 0x00ff },
320     { "zeta", 0x03b6 },
321     { "zwj", 0x200d },
322     { "zwnj", 0x200c }
323 };
324
325 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QString &entityStr, const QTextHtmlEntity &entity)
326 {
327     return entityStr < QLatin1String(entity.name);
328 }
329
330 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QTextHtmlEntity &entity, const QString &entityStr)
331 {
332     return QLatin1String(entity.name) < entityStr;
333 }
334
335 static QChar resolveEntity(const QString &entity)
336 {
337     const QTextHtmlEntity *start = &entities[0];
338     const QTextHtmlEntity *end = &entities[MAX_ENTITY];
339     const QTextHtmlEntity *e = qBinaryFind(start, end, entity);
340     if (e == end)
341         return QChar();
342     return e->code;
343 }
344
345 static const ushort windowsLatin1ExtendedCharacters[0xA0 - 0x80] = {
346     0x20ac, // 0x80
347     0x0081, // 0x81 direct mapping
348     0x201a, // 0x82
349     0x0192, // 0x83
350     0x201e, // 0x84
351     0x2026, // 0x85
352     0x2020, // 0x86
353     0x2021, // 0x87
354     0x02C6, // 0x88
355     0x2030, // 0x89
356     0x0160, // 0x8A
357     0x2039, // 0x8B
358     0x0152, // 0x8C
359     0x008D, // 0x8D direct mapping
360     0x017D, // 0x8E
361     0x008F, // 0x8F directmapping
362     0x0090, // 0x90 directmapping
363     0x2018, // 0x91
364     0x2019, // 0x92
365     0x201C, // 0x93
366     0X201D, // 0x94
367     0x2022, // 0x95
368     0x2013, // 0x96
369     0x2014, // 0x97
370     0x02DC, // 0x98
371     0x2122, // 0x99
372     0x0161, // 0x9A
373     0x203A, // 0x9B
374     0x0153, // 0x9C
375     0x009D, // 0x9D direct mapping
376     0x017E, // 0x9E
377     0x0178  // 0x9F
378 };
379
380 // the displayMode value is according to the what are blocks in the piecetable, not
381 // what the w3c defines.
382 static const QTextHtmlElement elements[Html_NumElements]= {
383     { "a",          Html_a,          QTextHtmlElement::DisplayInline },
384     { "address",    Html_address,    QTextHtmlElement::DisplayInline },
385     { "b",          Html_b,          QTextHtmlElement::DisplayInline },
386     { "big",        Html_big,        QTextHtmlElement::DisplayInline },
387     { "blockquote", Html_blockquote, QTextHtmlElement::DisplayBlock },
388     { "body",       Html_body,       QTextHtmlElement::DisplayBlock },
389     { "br",         Html_br,         QTextHtmlElement::DisplayInline },
390     { "caption",    Html_caption,    QTextHtmlElement::DisplayBlock },
391     { "center",     Html_center,     QTextHtmlElement::DisplayBlock },
392     { "cite",       Html_cite,       QTextHtmlElement::DisplayInline },
393     { "code",       Html_code,       QTextHtmlElement::DisplayInline },
394     { "dd",         Html_dd,         QTextHtmlElement::DisplayBlock },
395     { "dfn",        Html_dfn,        QTextHtmlElement::DisplayInline },
396     { "div",        Html_div,        QTextHtmlElement::DisplayBlock },
397     { "dl",         Html_dl,         QTextHtmlElement::DisplayBlock },
398     { "dt",         Html_dt,         QTextHtmlElement::DisplayBlock },
399     { "em",         Html_em,         QTextHtmlElement::DisplayInline },
400     { "font",       Html_font,       QTextHtmlElement::DisplayInline },
401     { "h1",         Html_h1,         QTextHtmlElement::DisplayBlock },
402     { "h2",         Html_h2,         QTextHtmlElement::DisplayBlock },
403     { "h3",         Html_h3,         QTextHtmlElement::DisplayBlock },
404     { "h4",         Html_h4,         QTextHtmlElement::DisplayBlock },
405     { "h5",         Html_h5,         QTextHtmlElement::DisplayBlock },
406     { "h6",         Html_h6,         QTextHtmlElement::DisplayBlock },
407     { "head",       Html_head,       QTextHtmlElement::DisplayNone },
408     { "hr",         Html_hr,         QTextHtmlElement::DisplayBlock },
409     { "html",       Html_html,       QTextHtmlElement::DisplayInline },
410     { "i",          Html_i,          QTextHtmlElement::DisplayInline },
411     { "img",        Html_img,        QTextHtmlElement::DisplayInline },
412     { "kbd",        Html_kbd,        QTextHtmlElement::DisplayInline },
413     { "li",         Html_li,         QTextHtmlElement::DisplayBlock },
414     { "link",       Html_link,       QTextHtmlElement::DisplayNone },
415     { "meta",       Html_meta,       QTextHtmlElement::DisplayNone },
416     { "nobr",       Html_nobr,       QTextHtmlElement::DisplayInline },
417     { "ol",         Html_ol,         QTextHtmlElement::DisplayBlock },
418     { "p",          Html_p,          QTextHtmlElement::DisplayBlock },
419     { "pre",        Html_pre,        QTextHtmlElement::DisplayBlock },
420     { "qt",         Html_body /*deliberate mapping*/, QTextHtmlElement::DisplayBlock },
421     { "s",          Html_s,          QTextHtmlElement::DisplayInline },
422     { "samp",       Html_samp,       QTextHtmlElement::DisplayInline },
423     { "script",     Html_script,     QTextHtmlElement::DisplayNone },
424     { "small",      Html_small,      QTextHtmlElement::DisplayInline },
425     { "span",       Html_span,       QTextHtmlElement::DisplayInline },
426     { "strong",     Html_strong,     QTextHtmlElement::DisplayInline },
427     { "style",      Html_style,      QTextHtmlElement::DisplayNone },
428     { "sub",        Html_sub,        QTextHtmlElement::DisplayInline },
429     { "sup",        Html_sup,        QTextHtmlElement::DisplayInline },
430     { "table",      Html_table,      QTextHtmlElement::DisplayTable },
431     { "tbody",      Html_tbody,      QTextHtmlElement::DisplayTable },
432     { "td",         Html_td,         QTextHtmlElement::DisplayBlock },
433     { "tfoot",      Html_tfoot,      QTextHtmlElement::DisplayTable },
434     { "th",         Html_th,         QTextHtmlElement::DisplayBlock },
435     { "thead",      Html_thead,      QTextHtmlElement::DisplayTable },
436     { "title",      Html_title,      QTextHtmlElement::DisplayNone },
437     { "tr",         Html_tr,         QTextHtmlElement::DisplayTable },
438     { "tt",         Html_tt,         QTextHtmlElement::DisplayInline },
439     { "u",          Html_u,          QTextHtmlElement::DisplayInline },
440     { "ul",         Html_ul,         QTextHtmlElement::DisplayBlock },
441     { "var",        Html_var,        QTextHtmlElement::DisplayInline },
442 };
443
444
445 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QString &str, const QTextHtmlElement &e)
446 {
447     return str < QLatin1String(e.name);
448 }
449
450 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QTextHtmlElement &e, const QString &str)
451 {
452     return QLatin1String(e.name) < str;
453 }
454
455 static const QTextHtmlElement *lookupElementHelper(const QString &element)
456 {
457     const QTextHtmlElement *start = &elements[0];
458     const QTextHtmlElement *end = &elements[Html_NumElements];
459     const QTextHtmlElement *e = qBinaryFind(start, end, element);
460     if (e == end)
461         return 0;
462     return e;
463 }
464
465 int QTextHtmlParser::lookupElement(const QString &element)
466 {
467     const QTextHtmlElement *e = lookupElementHelper(element);
468     if (!e)
469         return -1;
470     return e->id;
471 }
472
473 // quotes newlines as "\\n"
474 static QString quoteNewline(const QString &s)
475 {
476     QString n = s;
477     n.replace(QLatin1Char('\n'), QLatin1String("\\n"));
478     return n;
479 }
480
481 QTextHtmlParserNode::QTextHtmlParserNode()
482     : parent(0), id(Html_unknown),
483       cssFloat(QTextFrameFormat::InFlow), hasOwnListStyle(false),
484       hasCssListIndent(false), isEmptyParagraph(false), isTextFrame(false), isRootFrame(false),
485       displayMode(QTextHtmlElement::DisplayInline), hasHref(false),
486       listStyle(QTextListFormat::ListStyleUndefined), imageWidth(-1), imageHeight(-1), tableBorder(0),
487       tableCellRowSpan(1), tableCellColSpan(1), tableCellSpacing(2), tableCellPadding(0),
488       borderBrush(Qt::darkGray), borderStyle(QTextFrameFormat::BorderStyle_Outset),
489       userState(-1), cssListIndent(0), wsm(WhiteSpaceModeUndefined)
490 {
491     margin[QTextHtmlParser::MarginLeft] = 0;
492     margin[QTextHtmlParser::MarginRight] = 0;
493     margin[QTextHtmlParser::MarginTop] = 0;
494     margin[QTextHtmlParser::MarginBottom] = 0;
495 }
496
497 void QTextHtmlParser::dumpHtml()
498 {
499     for (int i = 0; i < count(); ++i) {
500         qDebug().nospace() << qPrintable(QString(depth(i)*4, QLatin1Char(' ')))
501                            << qPrintable(at(i).tag) << ':'
502                            << quoteNewline(at(i).text);
503             ;
504     }
505 }
506
507 QTextHtmlParserNode *QTextHtmlParser::newNode(int parent)
508 {
509     QTextHtmlParserNode *lastNode = &nodes.last();
510     QTextHtmlParserNode *newNode = 0;
511
512     bool reuseLastNode = true;
513
514     if (nodes.count() == 1) {
515         reuseLastNode = false;
516     } else if (lastNode->tag.isEmpty()) {
517
518         if (lastNode->text.isEmpty()) {
519             reuseLastNode = true;
520         } else { // last node is a text node (empty tag) with some text
521
522             if (lastNode->text.length() == 1 && lastNode->text.at(0).isSpace()) {
523
524                 int lastSibling = count() - 2;
525                 while (lastSibling
526                        && at(lastSibling).parent != lastNode->parent
527                        && at(lastSibling).displayMode == QTextHtmlElement::DisplayInline) {
528                     lastSibling = at(lastSibling).parent;
529                 }
530
531                 if (at(lastSibling).displayMode == QTextHtmlElement::DisplayInline) {
532                     reuseLastNode = false;
533                 } else {
534                     reuseLastNode = true;
535                 }
536             } else {
537                 // text node with real (non-whitespace) text -> nothing to re-use
538                 reuseLastNode = false;
539             }
540
541         }
542
543     } else {
544         // last node had a proper tag -> nothing to re-use
545         reuseLastNode = false;
546     }
547
548     if (reuseLastNode) {
549         newNode = lastNode;
550         newNode->tag.clear();
551         newNode->text.clear();
552         newNode->id = Html_unknown;
553     } else {
554         nodes.resize(nodes.size() + 1);
555         newNode = &nodes.last();
556     }
557
558     newNode->parent = parent;
559     return newNode;
560 }
561
562 void QTextHtmlParser::parse(const QString &text, const QTextDocument *_resourceProvider)
563 {
564     nodes.clear();
565     nodes.resize(1);
566     txt = text;
567     pos = 0;
568     len = txt.length();
569     textEditMode = false;
570     resourceProvider = _resourceProvider;
571     parse();
572     //dumpHtml();
573 }
574
575 int QTextHtmlParser::depth(int i) const
576 {
577     int depth = 0;
578     while (i) {
579         i = at(i).parent;
580         ++depth;
581     }
582     return depth;
583 }
584
585 int QTextHtmlParser::margin(int i, int mar) const {
586     int m = 0;
587     const QTextHtmlParserNode *node;
588     if (mar == MarginLeft
589         || mar == MarginRight) {
590         while (i) {
591             node = &at(i);
592             if (!node->isBlock() && node->id != Html_table)
593                 break;
594             if (node->isTableCell())
595                 break;
596             m += node->margin[mar];
597             i = node->parent;
598         }
599     }
600     return m;
601 }
602
603 int QTextHtmlParser::topMargin(int i) const
604 {
605     if (!i)
606         return 0;
607     return at(i).margin[MarginTop];
608 }
609
610 int QTextHtmlParser::bottomMargin(int i) const
611 {
612     if (!i)
613         return 0;
614     return at(i).margin[MarginBottom];
615 }
616
617 void QTextHtmlParser::eatSpace()
618 {
619     while (pos < len && txt.at(pos).isSpace() && txt.at(pos) != QChar::ParagraphSeparator)
620         pos++;
621 }
622
623 void QTextHtmlParser::parse()
624 {
625     while (pos < len) {
626         QChar c = txt.at(pos++);
627         if (c == QLatin1Char('<')) {
628             parseTag();
629         } else if (c == QLatin1Char('&')) {
630             nodes.last().text += parseEntity();
631         } else {
632             nodes.last().text += c;
633         }
634     }
635 }
636
637 // parses a tag after "<"
638 void QTextHtmlParser::parseTag()
639 {
640     eatSpace();
641
642     // handle comments and other exclamation mark declarations
643     if (hasPrefix(QLatin1Char('!'))) {
644         parseExclamationTag();
645         if (nodes.last().wsm != QTextHtmlParserNode::WhiteSpacePre
646             && nodes.last().wsm != QTextHtmlParserNode::WhiteSpacePreWrap
647             && !textEditMode)
648             eatSpace();
649         return;
650     }
651
652     // if close tag just close
653     if (hasPrefix(QLatin1Char('/'))) {
654         if (nodes.last().id == Html_style) {
655 #ifndef QT_NO_CSSPARSER
656             QCss::Parser parser(nodes.last().text);
657             QCss::StyleSheet sheet;
658             sheet.origin = QCss::StyleSheetOrigin_Author;
659             parser.parse(&sheet, Qt::CaseInsensitive);
660             inlineStyleSheets.append(sheet);
661             resolveStyleSheetImports(sheet);
662 #endif
663         }
664         parseCloseTag();
665         return;
666     }
667
668     int p = last();
669     while (p && at(p).tag.size() == 0)
670         p = at(p).parent;
671
672     QTextHtmlParserNode *node = newNode(p);
673
674     // parse tag name
675     node->tag = parseWord().toLower();
676
677     const QTextHtmlElement *elem = lookupElementHelper(node->tag);
678     if (elem) {
679         node->id = elem->id;
680         node->displayMode = elem->displayMode;
681     } else {
682         node->id = Html_unknown;
683     }
684
685     node->attributes.clear();
686     // _need_ at least one space after the tag name, otherwise there can't be attributes
687     if (pos < len && txt.at(pos).isSpace())
688         node->attributes = parseAttributes();
689
690     // resolveParent() may have to change the order in the tree and
691     // insert intermediate nodes for buggy HTML, so re-initialize the 'node'
692     // pointer through the return value
693     node = resolveParent();
694     resolveNode();
695
696     const int nodeIndex = nodes.count() - 1; // this new node is always the last
697 #ifndef QT_NO_CSSPARSER
698     node->applyCssDeclarations(declarationsForNode(nodeIndex), resourceProvider);
699 #endif
700     applyAttributes(node->attributes);
701
702     // finish tag
703     bool tagClosed = false;
704     while (pos < len && txt.at(pos) != QLatin1Char('>')) {
705         if (txt.at(pos) == QLatin1Char('/'))
706             tagClosed = true;
707
708         pos++;
709     }
710     pos++;
711
712     // in a white-space preserving environment strip off a initial newline
713     // since the element itself already generates a newline
714     if ((node->wsm == QTextHtmlParserNode::WhiteSpacePre
715          || node->wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
716         && node->isBlock()) {
717         if (pos < len - 1 && txt.at(pos) == QLatin1Char('\n'))
718             ++pos;
719     }
720
721     if (node->mayNotHaveChildren() || tagClosed) {
722         newNode(node->parent);
723         resolveNode();
724     }
725 }
726
727 // parses a tag beginning with "/"
728 void QTextHtmlParser::parseCloseTag()
729 {
730     ++pos;
731     QString tag = parseWord().toLower().trimmed();
732     while (pos < len) {
733         QChar c = txt.at(pos++);
734         if (c == QLatin1Char('>'))
735             break;
736     }
737
738     // find corresponding open node
739     int p = last();
740     if (p > 0
741         && at(p - 1).tag == tag
742         && at(p - 1).mayNotHaveChildren())
743         p--;
744
745     while (p && at(p).tag != tag)
746         p = at(p).parent;
747
748     // simply ignore the tag if we can't find
749     // a corresponding open node, for broken
750     // html such as <font>blah</font></font>
751     if (!p)
752         return;
753
754     // in a white-space preserving environment strip off a trailing newline
755     // since the closing of the opening block element will automatically result
756     // in a new block for elements following the <pre>
757     // ...foo\n</pre><p>blah -> foo</pre><p>blah
758     if ((at(p).wsm == QTextHtmlParserNode::WhiteSpacePre
759          || at(p).wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
760         && at(p).isBlock()) {
761         if (at(last()).text.endsWith(QLatin1Char('\n')))
762             nodes[last()].text.chop(1);
763     }
764
765     newNode(at(p).parent);
766     resolveNode();
767 }
768
769 // parses a tag beginning with "!"
770 void QTextHtmlParser::parseExclamationTag()
771 {
772     ++pos;
773     if (hasPrefix(QLatin1Char('-'),1) && hasPrefix(QLatin1Char('-'),2)) {
774         pos += 3;
775         // eat comments
776         int end = txt.indexOf(QLatin1String("-->"), pos);
777         pos = (end >= 0 ? end + 3 : len);
778     } else {
779         // eat internal tags
780         while (pos < len) {
781             QChar c = txt.at(pos++);
782             if (c == QLatin1Char('>'))
783                 break;
784         }
785     }
786 }
787
788 // parses an entity after "&", and returns it
789 QString QTextHtmlParser::parseEntity()
790 {
791     int recover = pos;
792     QString entity;
793     while (pos < len) {
794         QChar c = txt.at(pos++);
795         if (c.isSpace() || pos - recover > 9) {
796             goto error;
797         }
798         if (c == QLatin1Char(';'))
799             break;
800         entity += c;
801     }
802     {
803         QChar resolved = resolveEntity(entity);
804         if (!resolved.isNull())
805             return QString(resolved);
806     }
807     if (entity.length() > 1 && entity.at(0) == QLatin1Char('#')) {
808         entity.remove(0, 1); // removing leading #
809
810         int base = 10;
811         bool ok = false;
812
813         if (entity.at(0).toLower() == QLatin1Char('x')) { // hex entity?
814             entity.remove(0, 1);
815             base = 16;
816         }
817
818         uint uc = entity.toUInt(&ok, base);
819         if (ok) {
820             if (uc >= 0x80 && uc < 0x80 + (sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0])))
821                 uc = windowsLatin1ExtendedCharacters[uc - 0x80];
822             QString str;
823             if (uc > 0xffff) {
824                 // surrogate pair
825                 uc -= 0x10000;
826                 ushort high = uc/0x400 + 0xd800;
827                 ushort low = uc%0x400 + 0xdc00;
828                 str.append(QChar(high));
829                 str.append(QChar(low));
830             } else {
831                 str.append(QChar(uc));
832             }
833             return str;
834         }
835     }
836 error:
837     pos = recover;
838     return QLatin1String("&");
839 }
840
841 // parses one word, possibly quoted, and returns it
842 QString QTextHtmlParser::parseWord()
843 {
844     QString word;
845     if (hasPrefix(QLatin1Char('\"'))) { // double quotes
846         ++pos;
847         while (pos < len) {
848             QChar c = txt.at(pos++);
849             if (c == QLatin1Char('\"'))
850                 break;
851             else if (c == QLatin1Char('&'))
852                 word += parseEntity();
853             else
854                 word += c;
855         }
856     } else if (hasPrefix(QLatin1Char('\''))) { // single quotes
857         ++pos;
858         while (pos < len) {
859             QChar c = txt.at(pos++);
860             if (c == QLatin1Char('\''))
861                 break;
862             else
863                 word += c;
864         }
865     } else { // normal text
866         while (pos < len) {
867             QChar c = txt.at(pos++);
868             if (c == QLatin1Char('>')
869                 || (c == QLatin1Char('/') && hasPrefix(QLatin1Char('>'), 1))
870                 || c == QLatin1Char('<')
871                 || c == QLatin1Char('=')
872                 || c.isSpace()) {
873                 --pos;
874                 break;
875             }
876             if (c == QLatin1Char('&'))
877                 word += parseEntity();
878             else
879                 word += c;
880         }
881     }
882     return word;
883 }
884
885 // gives the new node the right parent
886 QTextHtmlParserNode *QTextHtmlParser::resolveParent()
887 {
888     QTextHtmlParserNode *node = &nodes.last();
889
890     int p = node->parent;
891
892     // Excel gives us buggy HTML with just tr without surrounding table tags
893     // or with just td tags
894
895     if (node->id == Html_td) {
896         int n = p;
897         while (n && at(n).id != Html_tr)
898             n = at(n).parent;
899
900         if (!n) {
901             nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
902             nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
903
904             QTextHtmlParserNode *table = &nodes[nodes.count() - 3];
905             table->parent = p;
906             table->id = Html_table;
907             table->tag = QLatin1String("table");
908             table->children.append(nodes.count() - 2); // add row as child
909
910             QTextHtmlParserNode *row = &nodes[nodes.count() - 2];
911             row->parent = nodes.count() - 3; // table as parent
912             row->id = Html_tr;
913             row->tag = QLatin1String("tr");
914
915             p = nodes.count() - 2;
916             node = &nodes.last(); // re-initialize pointer
917         }
918     }
919
920     if (node->id == Html_tr) {
921         int n = p;
922         while (n && at(n).id != Html_table)
923             n = at(n).parent;
924
925         if (!n) {
926             nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
927             QTextHtmlParserNode *table = &nodes[nodes.count() - 2];
928             table->parent = p;
929             table->id = Html_table;
930             table->tag = QLatin1String("table");
931             p = nodes.count() - 2;
932             node = &nodes.last(); // re-initialize pointer
933         }
934     }
935
936     // permit invalid html by letting block elements be children
937     // of inline elements with the exception of paragraphs:
938     //
939     // a new paragraph closes parent inline elements (while loop),
940     // unless they themselves are children of a non-paragraph block
941     // element (if statement)
942     //
943     // For example:
944     //
945     // <body><p><b>Foo<p>Bar <-- second <p> implicitly closes <b> that
946     //                           belongs to the first <p>. The self-nesting
947     //                           check further down prevents the second <p>
948     //                           from nesting into the first one then.
949     //                           so Bar is not bold.
950     //
951     // <body><b><p>Foo <-- Foo should be bold.
952     //
953     // <body><b><p>Foo<p>Bar <-- Foo and Bar should be bold.
954     //
955     if (node->id == Html_p) {
956         while (p && !at(p).isBlock())
957             p = at(p).parent;
958
959         if (!p || at(p).id != Html_p)
960             p = node->parent;
961     }
962
963     // some elements are not self nesting
964     if (node->id == at(p).id
965         && node->isNotSelfNesting())
966         p = at(p).parent;
967
968     // some elements are not allowed in certain contexts
969     while ((p && !node->allowedInContext(at(p).id))
970            // ### make new styles aware of empty tags
971            || at(p).mayNotHaveChildren()
972        ) {
973         p = at(p).parent;
974     }
975
976     node->parent = p;
977
978     // makes it easier to traverse the tree, later
979     nodes[p].children.append(nodes.count() - 1);
980     return node;
981 }
982
983 // sets all properties on the new node
984 void QTextHtmlParser::resolveNode()
985 {
986     QTextHtmlParserNode *node = &nodes.last();
987     const QTextHtmlParserNode *parent = &nodes.at(node->parent);
988     node->initializeProperties(parent, this);
989 }
990
991 bool QTextHtmlParserNode::isNestedList(const QTextHtmlParser *parser) const
992 {
993     if (!isListStart())
994         return false;
995
996     int p = parent;
997     while (p) {
998         if (parser->at(p).isListStart())
999             return true;
1000         p = parser->at(p).parent;
1001     }
1002     return false;
1003 }
1004
1005 void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent, const QTextHtmlParser *parser)
1006 {
1007     // inherit properties from parent element
1008     charFormat = parent->charFormat;
1009
1010     if (id == Html_html)
1011         blockFormat.setLayoutDirection(Qt::LeftToRight); // HTML default
1012     else if (parent->blockFormat.hasProperty(QTextFormat::LayoutDirection))
1013         blockFormat.setLayoutDirection(parent->blockFormat.layoutDirection());
1014
1015     if (parent->displayMode == QTextHtmlElement::DisplayNone)
1016         displayMode = QTextHtmlElement::DisplayNone;
1017
1018     if (parent->id != Html_table || id == Html_caption) {
1019         if (parent->blockFormat.hasProperty(QTextFormat::BlockAlignment))
1020             blockFormat.setAlignment(parent->blockFormat.alignment());
1021         else
1022             blockFormat.clearProperty(QTextFormat::BlockAlignment);
1023     }
1024     // we don't paint per-row background colors, yet. so as an
1025     // exception inherit the background color here
1026     // we also inherit the background between inline elements
1027     if ((parent->id != Html_tr || !isTableCell())
1028         && (displayMode != QTextHtmlElement::DisplayInline || parent->displayMode != QTextHtmlElement::DisplayInline)) {
1029         charFormat.clearProperty(QTextFormat::BackgroundBrush);
1030     }
1031
1032     listStyle = parent->listStyle;
1033     // makes no sense to inherit that property, a named anchor is a single point
1034     // in the document, which is set by the DocumentFragment
1035     charFormat.clearProperty(QTextFormat::AnchorName);
1036     wsm = parent->wsm;
1037
1038     // initialize remaining properties
1039     margin[QTextHtmlParser::MarginLeft] = 0;
1040     margin[QTextHtmlParser::MarginRight] = 0;
1041     margin[QTextHtmlParser::MarginTop] = 0;
1042     margin[QTextHtmlParser::MarginBottom] = 0;
1043     cssFloat = QTextFrameFormat::InFlow;
1044
1045     for (int i = 0; i < 4; ++i)
1046         padding[i] = -1;
1047
1048     // set element specific attributes
1049     switch (id) {
1050         case Html_a:
1051             charFormat.setAnchor(true);
1052             for (int i = 0; i < attributes.count(); i += 2) {
1053                 const QString key = attributes.at(i);
1054                 if (key.compare(QLatin1String("href"), Qt::CaseInsensitive) == 0
1055                     && !attributes.at(i + 1).isEmpty()) {
1056                     hasHref = true;
1057                     charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
1058                     charFormat.setForeground(QApplication::palette().link());
1059                 }
1060             }
1061
1062             break;
1063         case Html_em:
1064         case Html_i:
1065         case Html_cite:
1066         case Html_address:
1067         case Html_var:
1068         case Html_dfn:
1069             charFormat.setFontItalic(true);
1070             break;
1071         case Html_big:
1072             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(1));
1073             break;
1074         case Html_small:
1075             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(-1));
1076             break;
1077         case Html_strong:
1078         case Html_b:
1079             charFormat.setFontWeight(QFont::Bold);
1080             break;
1081         case Html_h1:
1082             charFormat.setFontWeight(QFont::Bold);
1083             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(3));
1084             margin[QTextHtmlParser::MarginTop] = 18;
1085             margin[QTextHtmlParser::MarginBottom] = 12;
1086             break;
1087         case Html_h2:
1088             charFormat.setFontWeight(QFont::Bold);
1089             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(2));
1090             margin[QTextHtmlParser::MarginTop] = 16;
1091             margin[QTextHtmlParser::MarginBottom] = 12;
1092             break;
1093         case Html_h3:
1094             charFormat.setFontWeight(QFont::Bold);
1095             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(1));
1096             margin[QTextHtmlParser::MarginTop] = 14;
1097             margin[QTextHtmlParser::MarginBottom] = 12;
1098             break;
1099         case Html_h4:
1100             charFormat.setFontWeight(QFont::Bold);
1101             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(0));
1102             margin[QTextHtmlParser::MarginTop] = 12;
1103             margin[QTextHtmlParser::MarginBottom] = 12;
1104             break;
1105         case Html_h5:
1106             charFormat.setFontWeight(QFont::Bold);
1107             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(-1));
1108             margin[QTextHtmlParser::MarginTop] = 12;
1109             margin[QTextHtmlParser::MarginBottom] = 4;
1110             break;
1111         case Html_p:
1112             margin[QTextHtmlParser::MarginTop] = 12;
1113             margin[QTextHtmlParser::MarginBottom] = 12;
1114             break;
1115         case Html_center:
1116             blockFormat.setAlignment(Qt::AlignCenter);
1117             break;
1118         case Html_ul:
1119             listStyle = QTextListFormat::ListDisc;
1120             // nested lists don't have margins, except for the toplevel one
1121             if (!isNestedList(parser)) {
1122                 margin[QTextHtmlParser::MarginTop] = 12;
1123                 margin[QTextHtmlParser::MarginBottom] = 12;
1124             }
1125             // no left margin as we use indenting instead
1126             break;
1127         case Html_ol:
1128             listStyle = QTextListFormat::ListDecimal;
1129             // nested lists don't have margins, except for the toplevel one
1130             if (!isNestedList(parser)) {
1131                 margin[QTextHtmlParser::MarginTop] = 12;
1132                 margin[QTextHtmlParser::MarginBottom] = 12;
1133             }
1134             // no left margin as we use indenting instead
1135             break;
1136         case Html_code:
1137         case Html_tt:
1138         case Html_kbd:
1139         case Html_samp:
1140             charFormat.setFontFamily(QString::fromLatin1("Courier New,courier"));
1141             // <tt> uses a fixed font, so set the property
1142             charFormat.setFontFixedPitch(true);
1143             break;
1144         case Html_br:
1145             text = QChar(QChar::LineSeparator);
1146             wsm = QTextHtmlParserNode::WhiteSpacePre;
1147             break;
1148         // ##### sub / sup
1149         case Html_pre:
1150             charFormat.setFontFamily(QString::fromLatin1("Courier New,courier"));
1151             wsm = WhiteSpacePre;
1152             margin[QTextHtmlParser::MarginTop] = 12;
1153             margin[QTextHtmlParser::MarginBottom] = 12;
1154             // <pre> uses a fixed font
1155             charFormat.setFontFixedPitch(true);
1156             break;
1157         case Html_blockquote:
1158             margin[QTextHtmlParser::MarginTop] = 12;
1159             margin[QTextHtmlParser::MarginBottom] = 12;
1160             margin[QTextHtmlParser::MarginLeft] = 40;
1161             margin[QTextHtmlParser::MarginRight] = 40;
1162             break;
1163         case Html_dl:
1164             margin[QTextHtmlParser::MarginTop] = 8;
1165             margin[QTextHtmlParser::MarginBottom] = 8;
1166             break;
1167         case Html_dd:
1168             margin[QTextHtmlParser::MarginLeft] = 30;
1169             break;
1170         case Html_u:
1171             charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
1172             break;
1173         case Html_s:
1174             charFormat.setFontStrikeOut(true);
1175             break;
1176         case Html_nobr:
1177             wsm = WhiteSpaceNoWrap;
1178             break;
1179         case Html_th:
1180             charFormat.setFontWeight(QFont::Bold);
1181             blockFormat.setAlignment(Qt::AlignCenter);
1182             break;
1183         case Html_td:
1184             blockFormat.setAlignment(Qt::AlignLeft);
1185             break;
1186         case Html_sub:
1187             charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript);
1188             break;
1189         case Html_sup:
1190             charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
1191             break;
1192         default: break;
1193     }
1194 }
1195
1196 #ifndef QT_NO_CSSPARSER
1197 void QTextHtmlParserNode::setListStyle(const QVector<QCss::Value> &cssValues)
1198 {
1199     for (int i = 0; i < cssValues.count(); ++i) {
1200         if (cssValues.at(i).type == QCss::Value::KnownIdentifier) {
1201             switch (static_cast<QCss::KnownValue>(cssValues.at(i).variant.toInt())) {
1202                 case QCss::Value_Disc: hasOwnListStyle = true; listStyle = QTextListFormat::ListDisc; break;
1203                 case QCss::Value_Square: hasOwnListStyle = true; listStyle = QTextListFormat::ListSquare; break;
1204                 case QCss::Value_Circle: hasOwnListStyle = true; listStyle = QTextListFormat::ListCircle; break;
1205                 case QCss::Value_Decimal: hasOwnListStyle = true; listStyle = QTextListFormat::ListDecimal; break;
1206                 case QCss::Value_LowerAlpha: hasOwnListStyle = true; listStyle = QTextListFormat::ListLowerAlpha; break;
1207                 case QCss::Value_UpperAlpha: hasOwnListStyle = true; listStyle = QTextListFormat::ListUpperAlpha; break;
1208                 case QCss::Value_LowerRoman: hasOwnListStyle = true; listStyle = QTextListFormat::ListLowerRoman; break;
1209                 case QCss::Value_UpperRoman: hasOwnListStyle = true; listStyle = QTextListFormat::ListUpperRoman; break;
1210                 default: break;
1211             }
1212         }
1213     }
1214     // allow individual list items to override the style
1215     if (id == Html_li && hasOwnListStyle)
1216         blockFormat.setProperty(QTextFormat::ListStyle, listStyle);
1217 }
1218
1219 void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration> &declarations, const QTextDocument *resourceProvider)
1220 {
1221     QCss::ValueExtractor extractor(declarations);
1222     extractor.extractBox(margin, padding);
1223
1224     for (int i = 0; i < declarations.count(); ++i) {
1225         const QCss::Declaration &decl = declarations.at(i);
1226         if (decl.d->values.isEmpty()) continue;
1227
1228         QCss::KnownValue identifier = QCss::UnknownValue;
1229         if (decl.d->values.first().type == QCss::Value::KnownIdentifier)
1230             identifier = static_cast<QCss::KnownValue>(decl.d->values.first().variant.toInt());
1231
1232         switch (decl.d->propertyId) {
1233         case QCss::BorderColor: borderBrush = QBrush(decl.colorValue()); break;
1234         case QCss::BorderStyles:
1235             if (decl.styleValue() != QCss::BorderStyle_Unknown && decl.styleValue() != QCss::BorderStyle_Native)
1236                 borderStyle = static_cast<QTextFrameFormat::BorderStyle>(decl.styleValue() - 1);
1237             break;
1238         case QCss::BorderWidth:
1239             tableBorder = extractor.lengthValue(decl);
1240             break;
1241         case QCss::Color: charFormat.setForeground(decl.colorValue()); break;
1242         case QCss::Float:
1243             cssFloat = QTextFrameFormat::InFlow;
1244             switch (identifier) {
1245             case QCss::Value_Left: cssFloat = QTextFrameFormat::FloatLeft; break;
1246             case QCss::Value_Right: cssFloat = QTextFrameFormat::FloatRight; break;
1247             default: break;
1248             }
1249             break;
1250         case QCss::QtBlockIndent:
1251             blockFormat.setIndent(decl.d->values.first().variant.toInt());
1252             break;
1253        case QCss::LineHeight: {
1254             qreal lineHeight;
1255             if (decl.realValue(&lineHeight, "px")) {
1256                 blockFormat.setLineHeight(lineHeight, QTextBlockFormat::FixedHeight);
1257             } else {
1258                 bool ok;
1259                 QString value = decl.d->values.first().toString();
1260                 lineHeight = value.toDouble(&ok);
1261                 if (ok)
1262                     blockFormat.setLineHeight(lineHeight, QTextBlockFormat::ProportionalHeight);
1263                 else
1264                     blockFormat.setLineHeight(0, QTextBlockFormat::SingleHeight);
1265             }
1266             break; }
1267         case QCss::TextIndent: {
1268             qreal indent = 0;
1269             if (decl.realValue(&indent, "px"))
1270                 blockFormat.setTextIndent(indent);
1271             break; }
1272         case QCss::QtListIndent:
1273             if (decl.intValue(&cssListIndent))
1274                 hasCssListIndent = true;
1275             break;
1276         case QCss::QtParagraphType:
1277             if (decl.d->values.first().variant.toString().compare(QLatin1String("empty"), Qt::CaseInsensitive) == 0)
1278                 isEmptyParagraph = true;
1279             break;
1280         case QCss::QtTableType:
1281             if (decl.d->values.first().variant.toString().compare(QLatin1String("frame"), Qt::CaseInsensitive) == 0)
1282                 isTextFrame = true;
1283             else if (decl.d->values.first().variant.toString().compare(QLatin1String("root"), Qt::CaseInsensitive) == 0) {
1284                 isTextFrame = true;
1285                 isRootFrame = true;
1286             }
1287             break;
1288         case QCss::QtUserState:
1289             userState = decl.d->values.first().variant.toInt();
1290             break;
1291         case QCss::Whitespace:
1292             switch (identifier) {
1293             case QCss::Value_Normal: wsm = QTextHtmlParserNode::WhiteSpaceNormal; break;
1294             case QCss::Value_Pre: wsm = QTextHtmlParserNode::WhiteSpacePre; break;
1295             case QCss::Value_NoWrap: wsm = QTextHtmlParserNode::WhiteSpaceNoWrap; break;
1296             case QCss::Value_PreWrap: wsm = QTextHtmlParserNode::WhiteSpacePreWrap; break;
1297             default: break;
1298             }
1299             break;
1300         case QCss::VerticalAlignment:
1301             switch (identifier) {
1302             case QCss::Value_Sub: charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript); break;
1303             case QCss::Value_Super: charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript); break;
1304             case QCss::Value_Middle: charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle); break;
1305             case QCss::Value_Top: charFormat.setVerticalAlignment(QTextCharFormat::AlignTop); break;
1306             case QCss::Value_Bottom: charFormat.setVerticalAlignment(QTextCharFormat::AlignBottom); break;
1307             default: charFormat.setVerticalAlignment(QTextCharFormat::AlignNormal); break;
1308             }
1309             break;
1310         case QCss::PageBreakBefore:
1311             switch (identifier) {
1312             case QCss::Value_Always: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() | QTextFormat::PageBreak_AlwaysBefore); break;
1313             case QCss::Value_Auto: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() & ~QTextFormat::PageBreak_AlwaysBefore); break;
1314             default: break;
1315             }
1316             break;
1317         case QCss::PageBreakAfter:
1318             switch (identifier) {
1319             case QCss::Value_Always: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() | QTextFormat::PageBreak_AlwaysAfter); break;
1320             case QCss::Value_Auto: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() & ~QTextFormat::PageBreak_AlwaysAfter); break;
1321             default: break;
1322             }
1323             break;
1324         case QCss::TextUnderlineStyle:
1325             switch (identifier) {
1326             case QCss::Value_None: charFormat.setUnderlineStyle(QTextCharFormat::NoUnderline); break;
1327             case QCss::Value_Solid: charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); break;
1328             case QCss::Value_Dashed: charFormat.setUnderlineStyle(QTextCharFormat::DashUnderline); break;
1329             case QCss::Value_Dotted: charFormat.setUnderlineStyle(QTextCharFormat::DotLine); break;
1330             case QCss::Value_DotDash: charFormat.setUnderlineStyle(QTextCharFormat::DashDotLine); break;
1331             case QCss::Value_DotDotDash: charFormat.setUnderlineStyle(QTextCharFormat::DashDotDotLine); break;
1332             case QCss::Value_Wave: charFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); break;
1333             default: break;
1334             }
1335             break;
1336         case QCss::ListStyleType:
1337         case QCss::ListStyle:
1338             setListStyle(decl.d->values);
1339             break;
1340         case QCss::QtListNumberPrefix:
1341             textListNumberPrefix = decl.d->values.first().variant.toString();
1342             break;
1343         case QCss::QtListNumberSuffix:
1344             textListNumberSuffix = decl.d->values.first().variant.toString();
1345             break;
1346         default: break;
1347         }
1348     }
1349
1350     QFont f;
1351     int adjustment = -255;
1352     extractor.extractFont(&f, &adjustment);
1353     if (f.resolve() & QFont::SizeResolved) {
1354         if (f.pointSize() > 0) {
1355             charFormat.setFontPointSize(f.pointSize());
1356         } else if (f.pixelSize() > 0) {
1357             charFormat.setProperty(QTextFormat::FontPixelSize, f.pixelSize());
1358         }
1359     }
1360     if (f.resolve() & QFont::StyleResolved)
1361         charFormat.setFontItalic(f.style() != QFont::StyleNormal);
1362
1363     if (f.resolve() & QFont::WeightResolved)
1364         charFormat.setFontWeight(f.weight());
1365
1366     if (f.resolve() & QFont::FamilyResolved)
1367         charFormat.setFontFamily(f.family());
1368
1369     if (f.resolve() & QFont::UnderlineResolved)
1370         charFormat.setUnderlineStyle(f.underline() ? QTextCharFormat::SingleUnderline : QTextCharFormat::NoUnderline);
1371
1372     if (f.resolve() & QFont::OverlineResolved)
1373         charFormat.setFontOverline(f.overline());
1374
1375     if (f.resolve() & QFont::StrikeOutResolved)
1376         charFormat.setFontStrikeOut(f.strikeOut());
1377
1378     if (f.resolve() & QFont::CapitalizationResolved)
1379         charFormat.setFontCapitalization(f.capitalization());
1380
1381     if (adjustment >= -1)
1382         charFormat.setProperty(QTextFormat::FontSizeAdjustment, adjustment);
1383
1384     {
1385         Qt::Alignment ignoredAlignment;
1386         QCss::Repeat ignoredRepeat;
1387         QString bgImage;
1388         QBrush bgBrush;
1389         QCss::Origin ignoredOrigin, ignoredClip;
1390         QCss::Attachment ignoredAttachment;
1391         extractor.extractBackground(&bgBrush, &bgImage, &ignoredRepeat, &ignoredAlignment,
1392                                     &ignoredOrigin, &ignoredAttachment, &ignoredClip);
1393
1394         if (!bgImage.isEmpty() && resourceProvider) {
1395             applyBackgroundImage(bgImage, resourceProvider);
1396         } else if (bgBrush.style() != Qt::NoBrush) {
1397             charFormat.setBackground(bgBrush);
1398         }
1399     }
1400 }
1401
1402 #endif // QT_NO_CSSPARSER
1403
1404 void QTextHtmlParserNode::applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider)
1405 {
1406     if (!url.isEmpty() && resourceProvider) {
1407         QVariant val = resourceProvider->resource(QTextDocument::ImageResource, url);
1408
1409         if (qApp->thread() != QThread::currentThread()) {
1410             // must use images in non-GUI threads
1411             if (val.type() == QVariant::Image) {
1412                 QImage image = qvariant_cast<QImage>(val);
1413                 charFormat.setBackground(image);
1414             } else if (val.type() == QVariant::ByteArray) {
1415                 QImage image;
1416                 if (image.loadFromData(val.toByteArray())) {
1417                     charFormat.setBackground(image);
1418                 }
1419             }
1420         } else {
1421             if (val.type() == QVariant::Image || val.type() == QVariant::Pixmap) {
1422                 charFormat.setBackground(qvariant_cast<QPixmap>(val));
1423             } else if (val.type() == QVariant::ByteArray) {
1424                 QPixmap pm;
1425                 if (pm.loadFromData(val.toByteArray())) {
1426                     charFormat.setBackground(pm);
1427                 }
1428             }
1429         }
1430     }
1431     if (!url.isEmpty())
1432         charFormat.setProperty(QTextFormat::BackgroundImageUrl, url);
1433 }
1434
1435 bool QTextHtmlParserNode::hasOnlyWhitespace() const
1436 {
1437     for (int i = 0; i < text.count(); ++i)
1438         if (!text.at(i).isSpace() || text.at(i) == QChar::LineSeparator)
1439             return false;
1440     return true;
1441 }
1442
1443 static bool setIntAttribute(int *destination, const QString &value)
1444 {
1445     bool ok = false;
1446     int val = value.toInt(&ok);
1447     if (ok)
1448         *destination = val;
1449
1450     return ok;
1451 }
1452
1453 static bool setFloatAttribute(qreal *destination, const QString &value)
1454 {
1455     bool ok = false;
1456     qreal val = value.toDouble(&ok);
1457     if (ok)
1458         *destination = val;
1459
1460     return ok;
1461 }
1462
1463 static void setWidthAttribute(QTextLength *width, QString value)
1464 {
1465     bool ok = false;
1466     qreal realVal = value.toDouble(&ok);
1467     if (ok) {
1468         *width = QTextLength(QTextLength::FixedLength, realVal);
1469     } else {
1470         value = value.trimmed();
1471         if (!value.isEmpty() && value.endsWith(QLatin1Char('%'))) {
1472             value.chop(1);
1473             realVal = value.toDouble(&ok);
1474             if (ok)
1475                 *width = QTextLength(QTextLength::PercentageLength, realVal);
1476         }
1477     }
1478 }
1479
1480 #ifndef QT_NO_CSSPARSER
1481 void QTextHtmlParserNode::parseStyleAttribute(const QString &value, const QTextDocument *resourceProvider)
1482 {
1483     QString css = value;
1484     css.prepend(QLatin1String("* {"));
1485     css.append(QLatin1Char('}'));
1486     QCss::Parser parser(css);
1487     QCss::StyleSheet sheet;
1488     parser.parse(&sheet, Qt::CaseInsensitive);
1489     if (sheet.styleRules.count() != 1) return;
1490     applyCssDeclarations(sheet.styleRules.at(0).declarations, resourceProvider);
1491 }
1492 #endif
1493
1494 QStringList QTextHtmlParser::parseAttributes()
1495 {
1496     QStringList attrs;
1497
1498     while (pos < len) {
1499         eatSpace();
1500         if (hasPrefix(QLatin1Char('>')) || hasPrefix(QLatin1Char('/')))
1501             break;
1502         QString key = parseWord().toLower();
1503         QString value = QLatin1String("1");
1504         if (key.size() == 0)
1505             break;
1506         eatSpace();
1507         if (hasPrefix(QLatin1Char('='))){
1508             pos++;
1509             eatSpace();
1510             value = parseWord();
1511         }
1512         if (value.size() == 0)
1513             continue;
1514         attrs << key << value;
1515     }
1516
1517     return attrs;
1518 }
1519
1520 void QTextHtmlParser::applyAttributes(const QStringList &attributes)
1521 {
1522     // local state variable for qt3 textedit mode
1523     bool seenQt3Richtext = false;
1524     QString linkHref;
1525     QString linkType;
1526
1527     if (attributes.count() % 2 == 1)
1528         return;
1529
1530     QTextHtmlParserNode *node = &nodes.last();
1531
1532     for (int i = 0; i < attributes.count(); i += 2) {
1533         QString key = attributes.at(i);
1534         QString value = attributes.at(i + 1);
1535
1536         switch (node->id) {
1537             case Html_font:
1538                 // the infamous font tag
1539                 if (key == QLatin1String("size") && value.size()) {
1540                     int n = value.toInt();
1541                     if (value.at(0) != QLatin1Char('+') && value.at(0) != QLatin1Char('-'))
1542                         n -= 3;
1543                     node->charFormat.setProperty(QTextFormat::FontSizeAdjustment, n);
1544                 } else if (key == QLatin1String("face")) {
1545                     node->charFormat.setFontFamily(value);
1546                 } else if (key == QLatin1String("color")) {
1547                     QColor c; c.setNamedColor(value);
1548                     if (!c.isValid())
1549                         qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
1550                     node->charFormat.setForeground(c);
1551                 }
1552                 break;
1553             case Html_ol:
1554             case Html_ul:
1555                 if (key == QLatin1String("type")) {
1556                     node->hasOwnListStyle = true;
1557                     if (value == QLatin1String("1")) {
1558                         node->listStyle = QTextListFormat::ListDecimal;
1559                     } else if (value == QLatin1String("a")) {
1560                         node->listStyle = QTextListFormat::ListLowerAlpha;
1561                     } else if (value == QLatin1String("A")) {
1562                         node->listStyle = QTextListFormat::ListUpperAlpha;
1563                     } else if (value == QLatin1String("i")) {
1564                         node->listStyle = QTextListFormat::ListLowerRoman;
1565                     } else if (value == QLatin1String("I")) {
1566                         node->listStyle = QTextListFormat::ListUpperRoman;
1567                     } else {
1568                         value = value.toLower();
1569                         if (value == QLatin1String("square"))
1570                             node->listStyle = QTextListFormat::ListSquare;
1571                         else if (value == QLatin1String("disc"))
1572                             node->listStyle = QTextListFormat::ListDisc;
1573                         else if (value == QLatin1String("circle"))
1574                             node->listStyle = QTextListFormat::ListCircle;
1575                     }
1576                 }
1577                 break;
1578             case Html_a:
1579                 if (key == QLatin1String("href"))
1580                     node->charFormat.setAnchorHref(value);
1581                 else if (key == QLatin1String("name"))
1582                     node->charFormat.setAnchorName(value);
1583                 break;
1584             case Html_img:
1585                 if (key == QLatin1String("src") || key == QLatin1String("source")) {
1586                     node->imageName = value;
1587                 } else if (key == QLatin1String("width")) {
1588                     node->imageWidth = -2; // register that there is a value for it.
1589                     setFloatAttribute(&node->imageWidth, value);
1590                 } else if (key == QLatin1String("height")) {
1591                     node->imageHeight = -2; // register that there is a value for it.
1592                     setFloatAttribute(&node->imageHeight, value);
1593                 }
1594                 break;
1595             case Html_tr:
1596             case Html_body:
1597                 if (key == QLatin1String("bgcolor")) {
1598                     QColor c; c.setNamedColor(value);
1599                     if (!c.isValid())
1600                         qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
1601                     node->charFormat.setBackground(c);
1602                 } else if (key == QLatin1String("background")) {
1603                     node->applyBackgroundImage(value, resourceProvider);
1604                 }
1605                 break;
1606             case Html_th:
1607             case Html_td:
1608                 if (key == QLatin1String("width")) {
1609                     setWidthAttribute(&node->width, value);
1610                 } else if (key == QLatin1String("bgcolor")) {
1611                     QColor c; c.setNamedColor(value);
1612                     if (!c.isValid())
1613                         qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
1614                     node->charFormat.setBackground(c);
1615                 } else if (key == QLatin1String("background")) {
1616                     node->applyBackgroundImage(value, resourceProvider);
1617                 } else if (key == QLatin1String("rowspan")) {
1618                     if (setIntAttribute(&node->tableCellRowSpan, value))
1619                         node->tableCellRowSpan = qMax(1, node->tableCellRowSpan);
1620                 } else if (key == QLatin1String("colspan")) {
1621                     if (setIntAttribute(&node->tableCellColSpan, value))
1622                         node->tableCellColSpan = qMax(1, node->tableCellColSpan);
1623                 }
1624                 break;
1625             case Html_table:
1626                 if (key == QLatin1String("border")) {
1627                     setFloatAttribute(&node->tableBorder, value);
1628                 } else if (key == QLatin1String("bgcolor")) {
1629                     QColor c; c.setNamedColor(value);
1630                     if (!c.isValid())
1631                         qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
1632                     node->charFormat.setBackground(c);
1633                 } else if (key == QLatin1String("background")) {
1634                     node->applyBackgroundImage(value, resourceProvider);
1635                 } else if (key == QLatin1String("cellspacing")) {
1636                     setFloatAttribute(&node->tableCellSpacing, value);
1637                 } else if (key == QLatin1String("cellpadding")) {
1638                     setFloatAttribute(&node->tableCellPadding, value);
1639                 } else if (key == QLatin1String("width")) {
1640                     setWidthAttribute(&node->width, value);
1641                 } else if (key == QLatin1String("height")) {
1642                     setWidthAttribute(&node->height, value);
1643                 }
1644                 break;
1645             case Html_meta:
1646                 if (key == QLatin1String("name")
1647                     && value == QLatin1String("qrichtext")) {
1648                     seenQt3Richtext = true;
1649                 }
1650
1651                 if (key == QLatin1String("content")
1652                     && value == QLatin1String("1")
1653                     && seenQt3Richtext) {
1654
1655                     textEditMode = true;
1656                 }
1657                 break;
1658             case Html_hr:
1659                 if (key == QLatin1String("width"))
1660                     setWidthAttribute(&node->width, value);
1661                 break;
1662             case Html_link:
1663                 if (key == QLatin1String("href"))
1664                     linkHref = value;
1665                 else if (key == QLatin1String("type"))
1666                     linkType = value;
1667                 break;
1668             default:
1669                 break;
1670         }
1671
1672         if (key == QLatin1String("style")) {
1673 #ifndef QT_NO_CSSPARSER
1674             node->parseStyleAttribute(value, resourceProvider);
1675 #endif
1676         } else if (key == QLatin1String("align")) {
1677             value = value.toLower();
1678             bool alignmentSet = true;
1679
1680             if (value == QLatin1String("left"))
1681                 node->blockFormat.setAlignment(Qt::AlignLeft|Qt::AlignAbsolute);
1682             else if (value == QLatin1String("right"))
1683                 node->blockFormat.setAlignment(Qt::AlignRight|Qt::AlignAbsolute);
1684             else if (value == QLatin1String("center"))
1685                 node->blockFormat.setAlignment(Qt::AlignHCenter);
1686             else if (value == QLatin1String("justify"))
1687                 node->blockFormat.setAlignment(Qt::AlignJustify);
1688             else
1689                 alignmentSet = false;
1690
1691             if (node->id == Html_img) {
1692                 // HTML4 compat
1693                 if (alignmentSet) {
1694                     if (node->blockFormat.alignment() & Qt::AlignLeft)
1695                         node->cssFloat = QTextFrameFormat::FloatLeft;
1696                     else if (node->blockFormat.alignment() & Qt::AlignRight)
1697                         node->cssFloat = QTextFrameFormat::FloatRight;
1698                 } else if (value == QLatin1String("middle")) {
1699                     node->charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle);
1700                 } else if (value == QLatin1String("top")) {
1701                     node->charFormat.setVerticalAlignment(QTextCharFormat::AlignTop);
1702                 }
1703             }
1704         } else if (key == QLatin1String("valign")) {
1705             value = value.toLower();
1706             if (value == QLatin1String("top"))
1707                 node->charFormat.setVerticalAlignment(QTextCharFormat::AlignTop);
1708             else if (value == QLatin1String("middle"))
1709                 node->charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle);
1710             else if (value == QLatin1String("bottom"))
1711                 node->charFormat.setVerticalAlignment(QTextCharFormat::AlignBottom);
1712         } else if (key == QLatin1String("dir")) {
1713             value = value.toLower();
1714             if (value == QLatin1String("ltr"))
1715                 node->blockFormat.setLayoutDirection(Qt::LeftToRight);
1716             else if (value == QLatin1String("rtl"))
1717                 node->blockFormat.setLayoutDirection(Qt::RightToLeft);
1718         } else if (key == QLatin1String("title")) {
1719             node->charFormat.setToolTip(value);
1720         } else if (key == QLatin1String("id")) {
1721             node->charFormat.setAnchor(true);
1722             node->charFormat.setAnchorName(value);
1723         }
1724     }
1725
1726 #ifndef QT_NO_CSSPARSER
1727     if (resourceProvider && !linkHref.isEmpty() && linkType == QLatin1String("text/css"))
1728         importStyleSheet(linkHref);
1729 #endif
1730 }
1731
1732 #ifndef QT_NO_CSSPARSER
1733 class QTextHtmlStyleSelector : public QCss::StyleSelector
1734 {
1735 public:
1736     inline QTextHtmlStyleSelector(const QTextHtmlParser *parser)
1737         : parser(parser) { nameCaseSensitivity = Qt::CaseInsensitive; }
1738
1739     virtual QStringList nodeNames(NodePtr node) const;
1740     virtual QString attribute(NodePtr node, const QString &name) const;
1741     virtual bool hasAttributes(NodePtr node) const;
1742     virtual bool isNullNode(NodePtr node) const;
1743     virtual NodePtr parentNode(NodePtr node) const;
1744     virtual NodePtr previousSiblingNode(NodePtr node) const;
1745     virtual NodePtr duplicateNode(NodePtr node) const;
1746     virtual void freeNode(NodePtr node) const;
1747
1748 private:
1749     const QTextHtmlParser *parser;
1750 };
1751
1752 QStringList QTextHtmlStyleSelector::nodeNames(NodePtr node) const
1753 {
1754     return QStringList(parser->at(node.id).tag.toLower());
1755 }
1756
1757 #endif // QT_NO_CSSPARSER
1758
1759 static inline int findAttribute(const QStringList &attributes, const QString &name)
1760 {
1761     int idx = -1;
1762     do {
1763         idx = attributes.indexOf(name, idx + 1);
1764     } while (idx != -1 && (idx % 2 == 1));
1765     return idx;
1766 }
1767
1768 #ifndef QT_NO_CSSPARSER
1769
1770 QString QTextHtmlStyleSelector::attribute(NodePtr node, const QString &name) const
1771 {
1772     const QStringList &attributes = parser->at(node.id).attributes;
1773     const int idx = findAttribute(attributes, name);
1774     if (idx == -1)
1775         return QString();
1776     return attributes.at(idx + 1);
1777 }
1778
1779 bool QTextHtmlStyleSelector::hasAttributes(NodePtr node) const
1780 {
1781    const QStringList &attributes = parser->at(node.id).attributes;
1782    return !attributes.isEmpty();
1783 }
1784
1785 bool QTextHtmlStyleSelector::isNullNode(NodePtr node) const
1786 {
1787     return node.id == 0;
1788 }
1789
1790 QCss::StyleSelector::NodePtr QTextHtmlStyleSelector::parentNode(NodePtr node) const
1791 {
1792     NodePtr parent;
1793     parent.id = 0;
1794     if (node.id) {
1795         parent.id = parser->at(node.id).parent;
1796     }
1797     return parent;
1798 }
1799
1800 QCss::StyleSelector::NodePtr QTextHtmlStyleSelector::duplicateNode(NodePtr node) const
1801 {
1802     return node;
1803 }
1804
1805 QCss::StyleSelector::NodePtr QTextHtmlStyleSelector::previousSiblingNode(NodePtr node) const
1806 {
1807     NodePtr sibling;
1808     sibling.id = 0;
1809     if (!node.id)
1810         return sibling;
1811     int parent = parser->at(node.id).parent;
1812     if (!parent)
1813         return sibling;
1814     const int childIdx = parser->at(parent).children.indexOf(node.id);
1815     if (childIdx <= 0)
1816         return sibling;
1817     sibling.id = parser->at(parent).children.at(childIdx - 1);
1818     return sibling;
1819 }
1820
1821 void QTextHtmlStyleSelector::freeNode(NodePtr) const
1822 {
1823 }
1824
1825 void QTextHtmlParser::resolveStyleSheetImports(const QCss::StyleSheet &sheet)
1826 {
1827     for (int i = 0; i < sheet.importRules.count(); ++i) {
1828         const QCss::ImportRule &rule = sheet.importRules.at(i);
1829         if (rule.media.isEmpty()
1830             || rule.media.contains(QLatin1String("screen"), Qt::CaseInsensitive))
1831             importStyleSheet(rule.href);
1832     }
1833 }
1834
1835 void QTextHtmlParser::importStyleSheet(const QString &href)
1836 {
1837     if (!resourceProvider)
1838         return;
1839     for (int i = 0; i < externalStyleSheets.count(); ++i)
1840         if (externalStyleSheets.at(i).url == href)
1841             return;
1842
1843     QVariant res = resourceProvider->resource(QTextDocument::StyleSheetResource, href);
1844     QString css;
1845     if (res.type() == QVariant::String) {
1846         css = res.toString();
1847     } else if (res.type() == QVariant::ByteArray) {
1848         // #### detect @charset
1849         css = QString::fromUtf8(res.toByteArray());
1850     }
1851     if (!css.isEmpty()) {
1852         QCss::Parser parser(css);
1853         QCss::StyleSheet sheet;
1854         parser.parse(&sheet, Qt::CaseInsensitive);
1855         externalStyleSheets.append(ExternalStyleSheet(href, sheet));
1856         resolveStyleSheetImports(sheet);
1857     }
1858 }
1859
1860 QVector<QCss::Declaration> QTextHtmlParser::declarationsForNode(int node) const
1861 {
1862     QVector<QCss::Declaration> decls;
1863
1864     QTextHtmlStyleSelector selector(this);
1865
1866     int idx = 0;
1867     selector.styleSheets.resize((resourceProvider ? 1 : 0)
1868                                 + externalStyleSheets.count()
1869                                 + inlineStyleSheets.count());
1870     if (resourceProvider)
1871         selector.styleSheets[idx++] = resourceProvider->docHandle()->parsedDefaultStyleSheet;
1872
1873     for (int i = 0; i < externalStyleSheets.count(); ++i, ++idx)
1874         selector.styleSheets[idx] = externalStyleSheets.at(i).sheet;
1875
1876     for (int i = 0; i < inlineStyleSheets.count(); ++i, ++idx)
1877         selector.styleSheets[idx] = inlineStyleSheets.at(i);
1878
1879     selector.medium = QLatin1String("screen");
1880
1881     QCss::StyleSelector::NodePtr n;
1882     n.id = node;
1883
1884     const char *extraPseudo = 0;
1885     if (nodes.at(node).id == Html_a && nodes.at(node).hasHref)
1886         extraPseudo = "link";
1887     decls = selector.declarationsForNode(n, extraPseudo);
1888
1889     return decls;
1890 }
1891
1892 bool QTextHtmlParser::nodeIsChildOf(int i, QTextHTMLElements id) const
1893 {
1894     while (i) {
1895         if (at(i).id == id)
1896             return true;
1897         i = at(i).parent;
1898     }
1899     return false;
1900 }
1901
1902 QT_END_NAMESPACE
1903 #endif // QT_NO_CSSPARSER
1904
1905 #endif // QT_NO_TEXTHTMLPARSER