From 2e4a107e7e97a2dc36471cbd795ec94096f1a771 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Thu, 4 Dec 2014 11:00:30 +0100 Subject: Ensure that CSS rules are inherited from the parent tags When CSS is set in a head tag then it was not being inherited by the child tags when the CSS specification indicates that the properties are in fact inherited. This ensures that those properties are inherited and the non inheritable ones are unchanged. A test is added to cover the different situations with inheritance and a fix is done for the QTextDocumentFragment test which was passing despite having incorrect defaults. Task-number: QTBUG-28770 Task-number: QTBUG-34153 Change-Id: I55966240845a885852a04ecb82c61926dea9f800 Reviewed-by: Simon Hausmann --- src/gui/text/qcssparser.cpp | 24 ++++ src/gui/text/qcssparser_p.h | 5 +- src/gui/text/qtexthtmlparser.cpp | 270 +++++++++++++++++++++++++++++---------- 3 files changed, 233 insertions(+), 66 deletions(-) (limited to 'src/gui') diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index f9e6a7e335..3c98cb568d 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -364,6 +364,29 @@ static quint64 findKnownValue(const QString &name, const QCssKnownValue *start, return prop->id; } +static inline bool isInheritable(Property propertyId) +{ + switch (propertyId) { + case Font: + case FontFamily: + case FontSize: + case FontStyle: + case FontWeight: + case TextIndent: + case Whitespace: + case ListStyleType: + case ListStyle: + case TextAlignment: + case FontVariant: + case TextTransform: + case LineHeight: + return true; + default: + break; + } + return false; +} + /////////////////////////////////////////////////////////////////////////////// // Value Extractor ValueExtractor::ValueExtractor(const QVector &decls, const QPalette &pal) @@ -2317,6 +2340,7 @@ bool Parser::parseProperty(Declaration *decl) { decl->d->property = lexem(); decl->d->propertyId = static_cast(findKnownValue(decl->d->property, properties, NumProperties)); + decl->d->inheritable = isInheritable(decl->d->propertyId); skipSpace(); return true; } diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index ebd9904762..75802ba36a 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -426,12 +426,13 @@ struct Q_GUI_EXPORT Declaration { struct DeclarationData : public QSharedData { - inline DeclarationData() : propertyId(UnknownProperty), important(false) {} + inline DeclarationData() : propertyId(UnknownProperty), important(false), inheritable(false) {} QString property; Property propertyId; QVector values; QVariant parsed; - bool important; + bool important:1; + bool inheritable:1; }; QExplicitlySharedDataPointer d; inline Declaration() : d(new DeclarationData()) {} diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index edace3d13c..866e0046c0 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -1050,25 +1050,14 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent // set element specific attributes switch (id) { case Html_a: - charFormat.setAnchor(true); for (int i = 0; i < attributes.count(); i += 2) { const QString key = attributes.at(i); if (key.compare(QLatin1String("href"), Qt::CaseInsensitive) == 0 && !attributes.at(i + 1).isEmpty()) { hasHref = true; - charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); - charFormat.setForeground(QGuiApplication::palette().link()); } } - - break; - case Html_em: - case Html_i: - case Html_cite: - case Html_address: - case Html_var: - case Html_dfn: - charFormat.setFontItalic(true); + charFormat.setAnchor(true); break; case Html_big: charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(1)); @@ -1076,36 +1065,27 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent case Html_small: charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(-1)); break; - case Html_strong: - case Html_b: - charFormat.setFontWeight(QFont::Bold); - break; case Html_h1: - charFormat.setFontWeight(QFont::Bold); charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(3)); margin[QTextHtmlParser::MarginTop] = 18; margin[QTextHtmlParser::MarginBottom] = 12; break; case Html_h2: - charFormat.setFontWeight(QFont::Bold); charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(2)); margin[QTextHtmlParser::MarginTop] = 16; margin[QTextHtmlParser::MarginBottom] = 12; break; case Html_h3: - charFormat.setFontWeight(QFont::Bold); charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(1)); margin[QTextHtmlParser::MarginTop] = 14; margin[QTextHtmlParser::MarginBottom] = 12; break; case Html_h4: - charFormat.setFontWeight(QFont::Bold); charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(0)); margin[QTextHtmlParser::MarginTop] = 12; margin[QTextHtmlParser::MarginBottom] = 12; break; case Html_h5: - charFormat.setFontWeight(QFont::Bold); charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(-1)); margin[QTextHtmlParser::MarginTop] = 12; margin[QTextHtmlParser::MarginBottom] = 4; @@ -1114,11 +1094,7 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent margin[QTextHtmlParser::MarginTop] = 12; margin[QTextHtmlParser::MarginBottom] = 12; break; - case Html_center: - blockFormat.setAlignment(Qt::AlignCenter); - break; case Html_ul: - listStyle = QTextListFormat::ListDisc; // nested lists don't have margins, except for the toplevel one if (!isNestedList(parser)) { margin[QTextHtmlParser::MarginTop] = 12; @@ -1127,7 +1103,6 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent // no left margin as we use indenting instead break; case Html_ol: - listStyle = QTextListFormat::ListDecimal; // nested lists don't have margins, except for the toplevel one if (!isNestedList(parser)) { margin[QTextHtmlParser::MarginTop] = 12; @@ -1135,26 +1110,12 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent } // no left margin as we use indenting instead break; - case Html_code: - case Html_tt: - case Html_kbd: - case Html_samp: - charFormat.setFontFamily(QString::fromLatin1("Courier New,courier")); - // uses a fixed font, so set the property - charFormat.setFontFixedPitch(true); - break; case Html_br: text = QChar(QChar::LineSeparator); - wsm = QTextHtmlParserNode::WhiteSpacePre; break; - // ##### sub / sup case Html_pre: - charFormat.setFontFamily(QString::fromLatin1("Courier New,courier")); - wsm = WhiteSpacePre; margin[QTextHtmlParser::MarginTop] = 12; margin[QTextHtmlParser::MarginBottom] = 12; - //
 uses a fixed font
-            charFormat.setFontFixedPitch(true);
             break;
         case Html_blockquote:
             margin[QTextHtmlParser::MarginTop] = 12;
@@ -1169,28 +1130,6 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent
         case Html_dd:
             margin[QTextHtmlParser::MarginLeft] = 30;
             break;
-        case Html_u:
-            charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
-            break;
-        case Html_s:
-            charFormat.setFontStrikeOut(true);
-            break;
-        case Html_nobr:
-            wsm = WhiteSpaceNoWrap;
-            break;
-        case Html_th:
-            charFormat.setFontWeight(QFont::Bold);
-            blockFormat.setAlignment(Qt::AlignCenter);
-            break;
-        case Html_td:
-            blockFormat.setAlignment(Qt::AlignLeft);
-            break;
-        case Html_sub:
-            charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript);
-            break;
-        case Html_sup:
-            charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
-            break;
         default: break;
     }
 }
@@ -1345,6 +1284,14 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector
         case QCss::QtListNumberSuffix:
             textListNumberSuffix = decl.d->values.first().variant.toString();
             break;
+        case QCss::TextAlignment:
+            switch (identifier) {
+            case QCss::Value_Left: blockFormat.setAlignment(Qt::AlignLeft); break;
+            case QCss::Value_Center: blockFormat.setAlignment(Qt::AlignCenter); break;
+            case QCss::Value_Right: blockFormat.setAlignment(Qt::AlignRight); break;
+            default: break;
+            }
+            break;
         default: break;
         }
     }
@@ -1833,6 +1780,189 @@ void QTextHtmlParser::importStyleSheet(const QString &href)
     }
 }
 
+QVector standardDeclarationForNode(const QTextHtmlParserNode &node)
+{
+    QVector decls;
+    QCss::Declaration decl;
+    QCss::Value val;
+    switch (node.id) {
+    case Html_a:
+    case Html_u: {
+        bool needsUnderline = (node.id == Html_u) ? true : false;
+        if (node.id == Html_a) {
+            for (int i = 0; i < node.attributes.count(); i += 2) {
+                const QString key = node.attributes.at(i);
+                if (key.compare(QLatin1String("href"), Qt::CaseInsensitive) == 0
+                    && !node.attributes.at(i + 1).isEmpty()) {
+                    needsUnderline = true;
+                    decl.d->property = QLatin1String("color");
+                    decl.d->propertyId = QCss::Property::Color;
+                    val.type = QCss::Value::Color;
+                    val.variant = QVariant(QGuiApplication::palette().link());
+                    decl.d->values = QVector() << val;
+                    decl.d->inheritable = true;
+                    decls << decl;
+                    break;
+                }
+            }
+        }
+        if (needsUnderline) {
+            decl = QCss::Declaration();
+            decl.d->property = QLatin1String("text-decoration");
+            decl.d->propertyId = QCss::Property::TextDecoration;
+            val.type = QCss::Value::KnownIdentifier;
+            val.variant = QVariant(QCss::Value_Underline);
+            decl.d->values = QVector() << val;
+            decl.d->inheritable = true;
+            decls << decl;
+        }
+        break;
+    }
+    case Html_b:
+    case Html_strong:
+    case Html_h1:
+    case Html_h2:
+    case Html_h3:
+    case Html_h4:
+    case Html_h5:
+    case Html_th:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("font-weight");
+        decl.d->propertyId = QCss::Property::FontWeight;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = QVariant(QCss::Value_Bold);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        if (node.id == Html_b || node.id == Html_strong)
+            break;
+        // Delibrate fall through
+    case Html_big:
+    case Html_small:
+        if (node.id != Html_th) {
+            decl = QCss::Declaration();
+            decl.d->property = QLatin1String("font-size");
+            decl.d->propertyId = QCss::Property::FontSize;
+            decl.d->inheritable = false;
+            val.type = QCss::Value::KnownIdentifier;
+            switch (node.id) {
+            case Html_h1: val.variant = QVariant(QCss::Value_XXLarge); break;
+            case Html_h2: val.variant = QVariant(QCss::Value_XLarge); break;
+            case Html_h3: case Html_big: val.variant = QVariant(QCss::Value_Large); break;
+            case Html_h4: val.variant = QVariant(QCss::Value_Medium); break;
+            case Html_h5: case Html_small: val.variant = QVariant(QCss::Value_Small); break;
+            default: break;
+            }
+            decl.d->values = QVector() << val;
+            decls << decl;
+            break;
+        }
+        // Delibrate fall through
+    case Html_center:
+    case Html_td:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("text-align");
+        decl.d->propertyId = QCss::Property::TextAlignment;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = (node.id == Html_td) ? QVariant(QCss::Value_Left) : QVariant(QCss::Value_Center);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    case Html_s:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("text-decoration");
+        decl.d->propertyId = QCss::Property::TextDecoration;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = QVariant(QCss::Value_LineThrough);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    case Html_em:
+    case Html_i:
+    case Html_cite:
+    case Html_address:
+    case Html_var:
+    case Html_dfn:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("font-style");
+        decl.d->propertyId = QCss::Property::FontStyle;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = QVariant(QCss::Value_Italic);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    case Html_sub:
+    case Html_sup:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("vertical-align");
+        decl.d->propertyId = QCss::Property::VerticalAlignment;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = (node.id == Html_sub) ? QVariant(QCss::Value_Sub) : QVariant(QCss::Value_Super);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    case Html_ul:
+    case Html_ol:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("list-style");
+        decl.d->propertyId = QCss::Property::ListStyle;
+        val.type = QCss::Value::KnownIdentifier;
+        val.variant = (node.id == Html_ul) ? QVariant(QCss::Value_Disc) : QVariant(QCss::Value_Decimal);
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    case Html_code:
+    case Html_tt:
+    case Html_kbd:
+    case Html_samp:
+    case Html_pre: {
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("font-family");
+        decl.d->propertyId = QCss::Property::FontFamily;
+        QVector values;
+        val.type = QCss::Value::String;
+        val.variant = QLatin1String("Courier New");
+        values << val;
+        val.type = QCss::Value::TermOperatorComma;
+        val.variant = QVariant();
+        values << val;
+        val.type = QCss::Value::String;
+        val.variant = QLatin1String("courier");
+        values << val;
+        decl.d->values = values;
+        decl.d->inheritable = true;
+        decls << decl;
+        }
+        if (node.id != Html_pre)
+            break;
+        // Delibrate fall through
+    case Html_br:
+    case Html_nobr:
+        decl = QCss::Declaration();
+        decl.d->property = QLatin1String("whitespace");
+        decl.d->propertyId = QCss::Property::Whitespace;
+        val.type = QCss::Value::KnownIdentifier;
+        switch (node.id) {
+        case Html_br: val.variant = QVariant(QCss::Value_PreWrap); break;
+        case Html_nobr: val.variant = QVariant(QCss::Value_NoWrap); break;
+        case Html_pre: val.variant = QVariant(QCss::Value_Pre); break;
+        default: break;
+        }
+        decl.d->values = QVector() << val;
+        decl.d->inheritable = true;
+        decls << decl;
+        break;
+    default:
+        break;
+    }
+    return decls;
+}
+
 QVector QTextHtmlParser::declarationsForNode(int node) const
 {
     QVector decls;
@@ -1860,8 +1990,20 @@ QVector QTextHtmlParser::declarationsForNode(int node) const
     const char *extraPseudo = 0;
     if (nodes.at(node).id == Html_a && nodes.at(node).hasHref)
         extraPseudo = "link";
-    decls = selector.declarationsForNode(n, extraPseudo);
-
+    // Ensure that our own style is taken into consideration
+    decls = standardDeclarationForNode(nodes.at(node));
+    decls += selector.declarationsForNode(n, extraPseudo);
+    n = selector.parentNode(n);
+    while (!selector.isNullNode(n)) {
+        QVector inheritedDecls;
+        inheritedDecls = selector.declarationsForNode(n, extraPseudo);
+        for (int i = 0; i < inheritedDecls.size(); ++i) {
+            const QCss::Declaration &decl = inheritedDecls.at(i);
+            if (decl.d->inheritable)
+                decls.prepend(decl);
+        }
+        n = selector.parentNode(n);
+    }
     return decls;
 }
 
-- 
cgit v1.2.3