File indexing completed on 2024-04-28 15:22:42

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
0005  *           (C) 2004-2008 Apple Computer, Inc.
0006  *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
0007  *           (C) 2009 Germain Garand (germain@ebooksfrance.org)
0008  *
0009  * This library is free software; you can redistribute it and/or
0010  * modify it under the terms of the GNU Library General Public
0011  * License as published by the Free Software Foundation; either
0012  * version 2 of the License, or (at your option) any later version.
0013  *
0014  * This library is distributed in the hope that it will be useful,
0015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017  * Library General Public License for more details.
0018  *
0019  * You should have received a copy of the GNU Library General Public License
0020  * along with this library; see the file COPYING.LIB.  If not, write to
0021  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0022  * Boston, MA 02110-1301, USA.
0023  */
0024 
0025 #include "css_valueimpl.h"
0026 #include "css_ruleimpl.h"
0027 #include "css_stylesheetimpl.h"
0028 #include "css/csshelper.h"
0029 #include "cssparser.h"
0030 #include "cssproperties.h"
0031 #include "cssvalues.h"
0032 
0033 #include <dom/css_value.h>
0034 #include <dom/dom_exception.h>
0035 #include <dom/dom_string.h>
0036 
0037 #include <xml/dom_stringimpl.h>
0038 #include <xml/dom_docimpl.h>
0039 
0040 #include <rendering/font.h>
0041 #include <rendering/render_style.h>
0042 
0043 #include <wtf/ASCIICType.h>
0044 
0045 #include "khtml_debug.h"
0046 #include <QRegExp>
0047 #include <QPaintDevice>
0048 
0049 // Hack for debugging purposes
0050 extern DOM::DOMString getPropertyName(unsigned short id);
0051 
0052 using khtml::FontDef;
0053 
0054 using namespace DOM;
0055 using namespace WTF;
0056 
0057 static int propertyID(const DOMString &s)
0058 {
0059     char buffer[maxCSSPropertyNameLength];
0060 
0061     unsigned len = s.length();
0062     if (len > maxCSSPropertyNameLength) {
0063         return 0;
0064     }
0065 
0066     for (unsigned i = 0; i != len; ++i) {
0067         unsigned short c = s[i].unicode();
0068         if (c == 0 || c >= 0x7F) {
0069             return 0;    // illegal character
0070         }
0071         buffer[i] = s[i].toLower().unicode();
0072     }
0073 
0074     return getPropertyID(buffer, len);
0075 }
0076 
0077 // "ident" from the CSS tokenizer, minus backslash-escape sequences
0078 static bool isCSSTokenizerIdentifier(const DOMString &string)
0079 {
0080     const QChar *p = string.unicode();
0081     const QChar *end = p + string.length();
0082 
0083     // -?
0084     if (p != end && p[0] == '-') {
0085         ++p;
0086     }
0087 
0088     // {nmstart}
0089     if (p == end || !(p[0] == '_' || p[0] >= 128 || isASCIIAlpha(p->unicode()))) {
0090         return false;
0091     }
0092     ++p;
0093 
0094     // {nmchar}*
0095     for (; p != end; ++p) {
0096         if (!(p[0] == '_' || p[0] == '-' || p[0] >= 128 || isASCIIAlphanumeric(p->unicode()))) {
0097             return false;
0098         }
0099     }
0100 
0101     return true;
0102 }
0103 
0104 static DOMString quoteString(const DOMString &string)
0105 {
0106     // FIXME: Also need to transform control characters into \ sequences.
0107     QString s = string.string();
0108     s.replace('\\', "\\\\");
0109     s.replace('\'', "\\'");
0110     return QString('\'' + s + '\'');
0111 }
0112 
0113 // Quotes the string if it needs quoting.
0114 static DOMString quoteStringIfNeeded(const DOMString &string)
0115 {
0116     return isCSSTokenizerIdentifier(string) ? string : quoteString(string);
0117 }
0118 
0119 static inline bool isInitialOrInherit(const CSSValueImpl *value)
0120 {
0121     const unsigned short t = value->cssValueType();
0122     return (t == CSSValue::CSS_INHERIT || t == CSSValue::CSS_INITIAL);
0123 }
0124 
0125 CSSStyleDeclarationImpl::CSSStyleDeclarationImpl(CSSRuleImpl *parent)
0126     : StyleBaseImpl(parent)
0127 {
0128     m_lstValues = nullptr;
0129     m_node = nullptr;
0130 }
0131 
0132 CSSStyleDeclarationImpl::CSSStyleDeclarationImpl(CSSRuleImpl *parent, QList<CSSProperty *> *lstValues)
0133     : StyleBaseImpl(parent)
0134 {
0135     m_lstValues = lstValues;
0136     m_node = nullptr;
0137 }
0138 
0139 CSSStyleDeclarationImpl  &CSSStyleDeclarationImpl::operator= (const CSSStyleDeclarationImpl &o)
0140 {
0141     if (this == &o) {
0142         return *this;
0143     }
0144 
0145     // don't attach it to the same node, just leave the current m_node value
0146     if (m_lstValues) {
0147         qDeleteAll(*m_lstValues);
0148     }
0149     delete m_lstValues;
0150     m_lstValues = nullptr;
0151     if (o.m_lstValues) {
0152         m_lstValues = new QList<CSSProperty *>;
0153         QListIterator<CSSProperty *> lstValuesIt(*o.m_lstValues);
0154         while (lstValuesIt.hasNext()) {
0155             m_lstValues->append(new CSSProperty(*lstValuesIt.next()));
0156         }
0157     }
0158 
0159     return *this;
0160 }
0161 
0162 CSSStyleDeclarationImpl::~CSSStyleDeclarationImpl()
0163 {
0164     if (m_lstValues) {
0165         qDeleteAll(*m_lstValues);
0166     }
0167     delete m_lstValues;
0168     // we don't use refcounting for m_node, to avoid cyclic references (see ElementImpl)
0169 }
0170 
0171 CSSValueImpl *CSSStyleDeclarationImpl::getPropertyCSSValue(const DOMString &propertyName) const
0172 {
0173     int propID = propertyID(propertyName);
0174     if (!propID) {
0175         return nullptr;
0176     }
0177     return getPropertyCSSValue(propID);
0178 }
0179 
0180 DOMString CSSStyleDeclarationImpl::getPropertyValue(const DOMString &propertyName) const
0181 {
0182     int propID = propertyID(propertyName);
0183     if (!propID) {
0184         return DOMString();
0185     }
0186     return getPropertyValue(propID);
0187 }
0188 
0189 DOMString CSSStyleDeclarationImpl::getPropertyPriority(const DOMString &propertyName) const
0190 {
0191     int propID = propertyID(propertyName);
0192     if (!propID) {
0193         return DOMString();
0194     }
0195     return getPropertyPriority(propID) ? "important" : "";
0196 }
0197 
0198 void CSSStyleDeclarationImpl::setProperty(const DOMString &propertyName, const DOMString &value, const DOMString &priority)
0199 {
0200     int propID = propertyID(propertyName);
0201     if (!propID) { // set exception?
0202         return;
0203     }
0204     bool important = priority.string().indexOf("important", 0, Qt::CaseInsensitive) != -1;
0205     setProperty(propID, value, important);
0206 }
0207 
0208 DOMString CSSStyleDeclarationImpl::removeProperty(const DOMString &propertyName)
0209 {
0210     int propID = propertyID(propertyName);
0211     if (!propID) {
0212         return DOMString();
0213     }
0214     DOMString old;
0215     removeProperty(propID, &old);
0216     return old;
0217 }
0218 
0219 DOMString CSSStyleDeclarationImpl::getPropertyValue(int propertyID) const
0220 {
0221     if (!m_lstValues || m_lstValues->isEmpty()) {
0222         return DOMString();
0223     }
0224     CSSValueImpl *value = getPropertyCSSValue(propertyID);
0225     if (value) {
0226         return value->cssText();
0227     }
0228 
0229     // Shorthand and 4-values properties
0230     switch (propertyID) {
0231     case CSS_PROP_BACKGROUND_POSITION: {
0232         // ## Is this correct? The code in cssparser.cpp is confusing
0233         const int properties[2] = { CSS_PROP_BACKGROUND_POSITION_X,
0234                                     CSS_PROP_BACKGROUND_POSITION_Y
0235                                   };
0236         return getLayeredShortHandValue(properties, 2);
0237     }
0238     case CSS_PROP_BACKGROUND: {
0239         // 'clip' must come after 'origin' in this array
0240         const int properties[9] = { CSS_PROP_BACKGROUND_IMAGE, CSS_PROP_BACKGROUND_REPEAT, CSS_PROP_BACKGROUND_ATTACHMENT,
0241                                     CSS_PROP_BACKGROUND_POSITION_X, CSS_PROP_BACKGROUND_POSITION_Y, CSS_PROP_BACKGROUND_SIZE,
0242                                     CSS_PROP_BACKGROUND_ORIGIN, CSS_PROP_BACKGROUND_CLIP, CSS_PROP_BACKGROUND_COLOR
0243                                   };
0244         return getLayeredShortHandValue(properties, 9);
0245     }
0246     case CSS_PROP_BORDER: {
0247         const int properties[3][4] = {{
0248                 CSS_PROP_BORDER_TOP_WIDTH,
0249                 CSS_PROP_BORDER_RIGHT_WIDTH,
0250                 CSS_PROP_BORDER_BOTTOM_WIDTH,
0251                 CSS_PROP_BORDER_LEFT_WIDTH
0252             },
0253             {
0254                 CSS_PROP_BORDER_TOP_STYLE,
0255                 CSS_PROP_BORDER_RIGHT_STYLE,
0256                 CSS_PROP_BORDER_BOTTOM_STYLE,
0257                 CSS_PROP_BORDER_LEFT_STYLE
0258             },
0259             {
0260                 CSS_PROP_BORDER_TOP_COLOR,
0261                 CSS_PROP_BORDER_RIGHT_COLOR,
0262                 CSS_PROP_BORDER_LEFT_COLOR,
0263                 CSS_PROP_BORDER_BOTTOM_COLOR
0264             }
0265         };
0266         DOMString res;
0267         const int nrprops = sizeof(properties) / sizeof(properties[0]);
0268         for (int i = 0; i < nrprops; ++i) {
0269             DOMString value = getCommonValue(properties[i], 4);
0270             if (!value.isNull()) {
0271                 if (!res.isNull()) {
0272                     res += " ";
0273                 }
0274                 res += value;
0275             }
0276         }
0277         return res;
0278 
0279     }
0280     case CSS_PROP_BORDER_TOP: {
0281         const int properties[3] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_TOP_STYLE,
0282                                     CSS_PROP_BORDER_TOP_COLOR
0283                                   };
0284         return getShortHandValue(properties, 3);
0285     }
0286     case CSS_PROP_BORDER_RIGHT: {
0287         const int properties[3] = { CSS_PROP_BORDER_RIGHT_WIDTH, CSS_PROP_BORDER_RIGHT_STYLE,
0288                                     CSS_PROP_BORDER_RIGHT_COLOR
0289                                   };
0290         return getShortHandValue(properties, 3);
0291     }
0292     case CSS_PROP_BORDER_BOTTOM: {
0293         const int properties[3] = { CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_BOTTOM_STYLE,
0294                                     CSS_PROP_BORDER_BOTTOM_COLOR
0295                                   };
0296         return getShortHandValue(properties, 3);
0297     }
0298     case CSS_PROP_BORDER_LEFT: {
0299         const int properties[3] = { CSS_PROP_BORDER_LEFT_WIDTH, CSS_PROP_BORDER_LEFT_STYLE,
0300                                     CSS_PROP_BORDER_LEFT_COLOR
0301                                   };
0302         return getShortHandValue(properties, 3);
0303     }
0304     case CSS_PROP_OUTLINE: {
0305         const int properties[3] = { CSS_PROP_OUTLINE_WIDTH, CSS_PROP_OUTLINE_STYLE,
0306                                     CSS_PROP_OUTLINE_COLOR
0307                                   };
0308         return getShortHandValue(properties, 3);
0309     }
0310     case CSS_PROP_BORDER_COLOR: {
0311         const int properties[4] = { CSS_PROP_BORDER_TOP_COLOR, CSS_PROP_BORDER_RIGHT_COLOR,
0312                                     CSS_PROP_BORDER_BOTTOM_COLOR, CSS_PROP_BORDER_LEFT_COLOR
0313                                   };
0314         return get4Values(properties);
0315     }
0316     case CSS_PROP_BORDER_WIDTH: {
0317         const int properties[4] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_RIGHT_WIDTH,
0318                                     CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_LEFT_WIDTH
0319                                   };
0320         return get4Values(properties);
0321     }
0322     case CSS_PROP_BORDER_STYLE: {
0323         const int properties[4] = { CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_RIGHT_STYLE,
0324                                     CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_LEFT_STYLE
0325                                   };
0326         return get4Values(properties);
0327     }
0328     case CSS_PROP_MARGIN: {
0329         const int properties[4] = { CSS_PROP_MARGIN_TOP, CSS_PROP_MARGIN_RIGHT,
0330                                     CSS_PROP_MARGIN_BOTTOM, CSS_PROP_MARGIN_LEFT
0331                                   };
0332         return get4Values(properties);
0333     }
0334     case CSS_PROP_PADDING: {
0335         const int properties[4] = { CSS_PROP_PADDING_TOP, CSS_PROP_PADDING_RIGHT,
0336                                     CSS_PROP_PADDING_BOTTOM, CSS_PROP_PADDING_LEFT
0337                                   };
0338         return get4Values(properties);
0339     }
0340     case CSS_PROP_LIST_STYLE: {
0341         const int properties[3] = { CSS_PROP_LIST_STYLE_TYPE, CSS_PROP_LIST_STYLE_POSITION,
0342                                     CSS_PROP_LIST_STYLE_IMAGE
0343                                   };
0344         return getShortHandValue(properties, 3);
0345     }
0346     }
0347     //qCDebug(KHTML_LOG) << "property not found:" << propertyID;
0348     return DOMString();
0349 }
0350 
0351 // only returns a non-null value if all properties have the same, non-null value
0352 DOMString CSSStyleDeclarationImpl::getCommonValue(const int *properties, int number) const
0353 {
0354     DOMString res;
0355     for (int i = 0; i < number; ++i) {
0356         CSSValueImpl *value = getPropertyCSSValue(properties[i]);
0357         if (!value) {
0358             return DOMString();
0359         }
0360         DOMString text = value->cssText();
0361         if (text.isNull()) {
0362             return DOMString();
0363         }
0364         if (res.isNull()) {
0365             res = text;
0366         } else if (res != text) {
0367             return DOMString();
0368         }
0369     }
0370     return res;
0371 }
0372 
0373 DOMString CSSStyleDeclarationImpl::get4Values(const int *properties) const
0374 {
0375     // Assume the properties are in the order top, right, bottom, left.
0376     QVector<DOMString> values(4);
0377     for (int i = 0; i < 4; ++i) {
0378         CSSValueImpl *val = getPropertyCSSValue(properties[i]);
0379         // All 4 properties must be specified.
0380         if (!val || isInitialOrInherit(val)) {
0381             return DOMString();
0382         } else {
0383             values[i] = val->cssText();
0384         }
0385     }
0386 
0387     // Reduce shorthand.
0388     if (values.at(1) == values.at(3)) { // right/left
0389         values.remove(3);
0390         if (values.at(0) == values.at(2)) { // top/bottom
0391             values.remove(2);
0392             if (values.at(0) == values.at(1)) {
0393                 values.remove(1);
0394             }
0395         }
0396     }
0397 
0398     DOMString res;
0399     const int valuesSize = values.size();
0400     for (int i = 0; i < valuesSize; ++i) {
0401         if (!res.isNull()) {
0402             res += " ";
0403         }
0404         res += values.at(i);
0405     }
0406 
0407     return res;
0408 }
0409 
0410 static inline DOMString posXYSize_string_helper(DOMString &bPosX, DOMString &bPosY, DOMString &bSize)
0411 {
0412     DOMString res, position;
0413 
0414     if (!bPosX.isEmpty() && !bPosY.isEmpty()) {
0415         position = bPosX + DOMString(" ") + bPosY;
0416     } else if (bPosX.isEmpty() && !bPosY.isEmpty()) {
0417         position = DOMString("0% ") + bPosY;
0418     } else if (!bPosX.isEmpty() && bPosY.isEmpty()) {
0419         position = bPosX + DOMString(" 0%");
0420     }
0421 
0422     if (!bSize.isEmpty()) {
0423         if (position.isEmpty()) {
0424             res = DOMString("0% 0%") + DOMString(" / ") + bSize;
0425         } else {
0426             res = position + DOMString(" / ") + bSize;
0427         }
0428     } else {
0429         res = position;
0430     }
0431 
0432     return res;
0433 }
0434 
0435 DOMString CSSStyleDeclarationImpl::getLayeredShortHandValue(const int *properties, unsigned number) const
0436 {
0437     DOMString res;
0438     unsigned i;
0439     unsigned j;
0440 
0441     // Begin by collecting the properties into an array.
0442     QVector<CSSValueImpl *> values(number);
0443     unsigned numLayers = 0;
0444 
0445     for (i = 0; i < number; ++i) {
0446         values[i] = getPropertyCSSValue(properties[i]);
0447         if (values[i]) {
0448             if (values[i]->isValueList()) {
0449                 CSSValueListImpl *valueList = static_cast<CSSValueListImpl *>(values[i]);
0450                 numLayers = qMax(valueList->length(), (unsigned long)numLayers);
0451             } else {
0452                 numLayers = qMax(1U, numLayers);
0453             }
0454         }
0455     }
0456 
0457     // Now stitch the properties together.
0458     // Implicit initial values are flagged as such and can safely be omitted.
0459     for (i = 0; i < numLayers; i++) {
0460         DOMString layerRes;
0461         DOMString bPosX, bPosY, bSize;
0462         for (j = 0; j < number; j++) {
0463             CSSValueImpl *value = nullptr;
0464             if (values[j]) {
0465                 if (values[j]->isValueList()) {
0466                     value = static_cast<CSSValueListImpl *>(values[j])->item(i);
0467                 } else {
0468                     value = values[j];
0469 
0470                     // Color only belongs in the last layer.
0471                     if (properties[j] == CSS_PROP_BACKGROUND_COLOR) {
0472                         if (i != numLayers - 1) {
0473                             value = nullptr;
0474                         }
0475                     } else if (i != 0) { // Other singletons only belong in the first layer.
0476                         value = nullptr;
0477                     }
0478                 }
0479             }
0480 
0481             if (value && !value->isImplicitInitialValue()) {
0482                 // positionX,positionY,size should be handled separately in order
0483                 // to return a consistent and valid 'background' property string
0484                 if (properties[j] == CSS_PROP_BACKGROUND_POSITION_X) {
0485                     bPosX = value->cssText();
0486                 } else if (properties[j] == CSS_PROP_BACKGROUND_POSITION_Y) {
0487                     bPosY = value->cssText();
0488                 } else if (properties[j] == CSS_PROP_BACKGROUND_SIZE) {
0489                     bSize = value->cssText();
0490                 } else {
0491                     if (!layerRes.isNull()) {
0492                         layerRes += " ";
0493                     }
0494                     layerRes += value->cssText();
0495                 }
0496             }
0497         }
0498 
0499         // now add positionX,positionY,size
0500         DOMString posXYSize = posXYSize_string_helper(bPosX, bPosY, bSize);
0501         if (!posXYSize.isEmpty()) {
0502             if (!layerRes.isNull()) {
0503                 layerRes += " ";
0504             }
0505             layerRes += posXYSize;
0506         }
0507 
0508         if (!layerRes.isNull()) {
0509             if (!res.isNull()) {
0510                 res += ", ";
0511             }
0512             res += layerRes;
0513         }
0514     }
0515 
0516     return res;
0517 }
0518 
0519 DOMString CSSStyleDeclarationImpl::getShortHandValue(const int *properties, int number) const
0520 {
0521     DOMString res;
0522     for (int i = 0; i < number; ++i) {
0523         CSSValueImpl *value = getPropertyCSSValue(properties[i]);
0524         if (value) {   // TODO provide default value if !value
0525             if (!res.isNull()) {
0526                 res += " ";
0527             }
0528             res += value->cssText();
0529         }
0530     }
0531     return res;
0532 }
0533 
0534 CSSValueImpl *CSSStyleDeclarationImpl::getPropertyCSSValue(int propertyID) const
0535 {
0536     if (!m_lstValues || m_lstValues->isEmpty()) {
0537         return nullptr;
0538     }
0539 
0540     QListIterator<CSSProperty *> lstValuesIt(*m_lstValues);
0541     CSSProperty *current;
0542     while (lstValuesIt.hasNext()) {
0543         current = lstValuesIt.next();
0544         if (current->m_id == propertyID) {
0545             return current->value();
0546         }
0547     }
0548     return nullptr;
0549 }
0550 
0551 bool CSSStyleDeclarationImpl::isPropertyImplicit(int propertyID) const
0552 {
0553     QListIterator<CSSProperty *> lstValuesIt(*m_lstValues);
0554     CSSProperty const *current;
0555     while (lstValuesIt.hasNext()) {
0556         current = lstValuesIt.next();
0557         if (current->m_id == propertyID) {
0558             return current->isImplicit();
0559         }
0560     }
0561     return false;
0562 }
0563 
0564 // --------------- Shorthands mapping ----------------
0565 
0566 // In order top be able to remove a shorthand property,
0567 // we need a reverse mapping from the shorthands to their composing properties.
0568 
0569 // ### Warning: keep in sync when introducing new shorthands.
0570 
0571 struct PropertyLonghand {
0572     PropertyLonghand()
0573         : m_properties(nullptr)
0574         , m_length(0)
0575     {
0576     }
0577 
0578     PropertyLonghand(const int *firstProperty, unsigned numProperties)
0579         : m_properties(firstProperty)
0580         , m_length(numProperties)
0581     {
0582     }
0583 
0584     const int *properties() const
0585     {
0586         return m_properties;
0587     }
0588     unsigned length() const
0589     {
0590         return m_length;
0591     }
0592 
0593 private:
0594     const int *m_properties;
0595     unsigned m_length;
0596 };
0597 
0598 static void initShorthandMap(QHash<int, PropertyLonghand> &shorthandMap)
0599 {
0600 #define SET_SHORTHAND_MAP_ENTRY(map, propID, array) \
0601     map.insert(propID, PropertyLonghand(array, sizeof(array) / sizeof(array[0])))
0602 
0603     // Do not change the order of the following four shorthands, and keep them together.
0604     static const int borderProperties[4][3] = {
0605         { CSS_PROP_BORDER_TOP_COLOR, CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_TOP_WIDTH },
0606         { CSS_PROP_BORDER_RIGHT_COLOR, CSS_PROP_BORDER_RIGHT_STYLE, CSS_PROP_BORDER_RIGHT_WIDTH },
0607         { CSS_PROP_BORDER_BOTTOM_COLOR, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_BOTTOM_WIDTH },
0608         { CSS_PROP_BORDER_LEFT_COLOR, CSS_PROP_BORDER_LEFT_STYLE, CSS_PROP_BORDER_LEFT_WIDTH }
0609     };
0610     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_TOP, borderProperties[0]);
0611     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_RIGHT, borderProperties[1]);
0612     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_BOTTOM, borderProperties[2]);
0613     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_LEFT, borderProperties[3]);
0614 
0615     shorthandMap.insert(CSS_PROP_BORDER, PropertyLonghand(borderProperties[0], sizeof(borderProperties) / sizeof(borderProperties[0][0])));
0616 
0617     static const int borderColorProperties[] = {
0618         CSS_PROP_BORDER_TOP_COLOR,
0619         CSS_PROP_BORDER_RIGHT_COLOR,
0620         CSS_PROP_BORDER_BOTTOM_COLOR,
0621         CSS_PROP_BORDER_LEFT_COLOR
0622     };
0623     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_COLOR, borderColorProperties);
0624 
0625     static const int borderStyleProperties[] = {
0626         CSS_PROP_BORDER_TOP_STYLE,
0627         CSS_PROP_BORDER_RIGHT_STYLE,
0628         CSS_PROP_BORDER_BOTTOM_STYLE,
0629         CSS_PROP_BORDER_LEFT_STYLE
0630     };
0631     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_STYLE, borderStyleProperties);
0632 
0633     static const int borderWidthProperties[] = {
0634         CSS_PROP_BORDER_TOP_WIDTH,
0635         CSS_PROP_BORDER_RIGHT_WIDTH,
0636         CSS_PROP_BORDER_BOTTOM_WIDTH,
0637         CSS_PROP_BORDER_LEFT_WIDTH
0638     };
0639     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_WIDTH, borderWidthProperties);
0640 
0641     static const int backgroundPositionProperties[] = { CSS_PROP_BACKGROUND_POSITION_X, CSS_PROP_BACKGROUND_POSITION_Y };
0642     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BACKGROUND_POSITION, backgroundPositionProperties);
0643 
0644     static const int borderSpacingProperties[] = { CSS_PROP__KHTML_BORDER_HORIZONTAL_SPACING, CSS_PROP__KHTML_BORDER_VERTICAL_SPACING };
0645     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_SPACING, borderSpacingProperties);
0646 
0647     static const int listStyleProperties[] = {
0648         CSS_PROP_LIST_STYLE_IMAGE,
0649         CSS_PROP_LIST_STYLE_POSITION,
0650         CSS_PROP_LIST_STYLE_TYPE
0651     };
0652     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_LIST_STYLE, listStyleProperties);
0653 
0654     static const int marginProperties[] = {
0655         CSS_PROP_MARGIN_TOP,
0656         CSS_PROP_MARGIN_RIGHT,
0657         CSS_PROP_MARGIN_BOTTOM,
0658         CSS_PROP_MARGIN_LEFT
0659     };
0660     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_MARGIN, marginProperties);
0661 
0662 #ifdef APPLE_CHANGES
0663     static const int marginCollapseProperties[] = { CSS_PROP__KHTML_MARGIN_TOP_COLLAPSE, CSS_PROP__KHTML_MARGIN_BOTTOM_COLLAPSE };
0664     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__KHTML_MARGIN_COLLAPSE, marginCollapseProperties);
0665 #endif
0666 
0667     static const int marqueeProperties[] = {
0668         CSS_PROP__KHTML_MARQUEE_DIRECTION,
0669         CSS_PROP__KHTML_MARQUEE_INCREMENT,
0670         CSS_PROP__KHTML_MARQUEE_REPETITION,
0671         CSS_PROP__KHTML_MARQUEE_STYLE,
0672         CSS_PROP__KHTML_MARQUEE_SPEED
0673     };
0674     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__KHTML_MARQUEE, marqueeProperties);
0675 
0676     static const int outlineProperties[] = {
0677         CSS_PROP_OUTLINE_COLOR,
0678         CSS_PROP_OUTLINE_OFFSET,
0679         CSS_PROP_OUTLINE_STYLE,
0680         CSS_PROP_OUTLINE_WIDTH
0681     };
0682     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_OUTLINE, outlineProperties);
0683 
0684     static const int paddingProperties[] = {
0685         CSS_PROP_PADDING_TOP,
0686         CSS_PROP_PADDING_RIGHT,
0687         CSS_PROP_PADDING_BOTTOM,
0688         CSS_PROP_PADDING_LEFT
0689     };
0690     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_PADDING, paddingProperties);
0691 
0692 #ifdef APPLE_CHANGES
0693     static const int textStrokeProperties[] = { CSS_PROP__KHTML_TEXT_STROKE_COLOR, CSS_PROP__KHTML_TEXT_STROKE_WIDTH };
0694     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__KHTML_TEXT_STROKE, textStrokeProperties);
0695 #endif
0696 
0697     static const int backgroundProperties[] = {
0698         CSS_PROP_BACKGROUND_ATTACHMENT,
0699         CSS_PROP_BACKGROUND_COLOR,
0700         CSS_PROP_BACKGROUND_IMAGE,
0701         CSS_PROP_BACKGROUND_POSITION_X,
0702         CSS_PROP_BACKGROUND_POSITION_Y,
0703         CSS_PROP_BACKGROUND_REPEAT,
0704         CSS_PROP_BACKGROUND_SIZE,
0705         CSS_PROP_BACKGROUND_ORIGIN,
0706         CSS_PROP_BACKGROUND_CLIP
0707     };
0708     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BACKGROUND, backgroundProperties);
0709 
0710 #ifdef APPLE_CHANGES
0711     static const int columnsProperties[] = { CSS_PROP__KHTML_COLUMN_WIDTH, CSS_PROP__KHTML_COLUMN_COUNT };
0712     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__KHTML_COLUMNS, columnsProperties);
0713 
0714     static const int columnRuleProperties[] = {
0715         CSS_PROP__KHTML_COLUMN_RULE_COLOR,
0716         CSS_PROP__KHTML_COLUMN_RULE_STYLE,
0717         CSS_PROP__KHTML_COLUMN_RULE_WIDTH
0718     };
0719     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__KHTML_COLUMN_RULE, columnRuleProperties);
0720 #endif
0721 
0722     static const int overflowProperties[] = { CSS_PROP_OVERFLOW_X, CSS_PROP_OVERFLOW_Y };
0723     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_OVERFLOW, overflowProperties);
0724 
0725     static const int borderRadiusProperties[] = {
0726         CSS_PROP_BORDER_TOP_RIGHT_RADIUS,
0727         CSS_PROP_BORDER_TOP_LEFT_RADIUS,
0728         CSS_PROP_BORDER_BOTTOM_LEFT_RADIUS,
0729         CSS_PROP_BORDER_BOTTOM_RIGHT_RADIUS
0730     };
0731     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_RADIUS, borderRadiusProperties);
0732 
0733     static const int markerProperties[] = {
0734         CSS_PROP_MARKER_START,
0735         CSS_PROP_MARKER_MID,
0736         CSS_PROP_MARKER_END
0737     };
0738     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_MARKER, markerProperties);
0739 
0740     static const int fontProperties[] = {
0741         CSS_PROP_FONT_STYLE,
0742         CSS_PROP_FONT_VARIANT,
0743         CSS_PROP_FONT_WEIGHT,
0744         CSS_PROP_FONT_SIZE,
0745         CSS_PROP_LINE_HEIGHT,
0746         CSS_PROP_FONT_FAMILY
0747     };
0748     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_FONT, fontProperties);
0749 
0750 #undef SET_SHORTHAND_MAP_ENTRY
0751 }
0752 
0753 // -------------------------------------------
0754 
0755 void CSSStyleDeclarationImpl::removeProperty(int propertyID, DOM::DOMString *old)
0756 {
0757     if (!m_lstValues || m_lstValues->isEmpty()) {
0758         return;
0759     }
0760 
0761     bool changed = false;
0762 
0763     static QHash<int, PropertyLonghand> shorthandMap;
0764     if (shorthandMap.isEmpty()) {
0765         initShorthandMap(shorthandMap);
0766     }
0767 
0768     PropertyLonghand longhand = shorthandMap.value(propertyID);
0769     if (longhand.length()) {
0770         changed = removePropertiesInSet(longhand.properties(), longhand.length());
0771     }
0772 
0773     // FIXME: Return an equivalent shorthand when possible.
0774 
0775     QMutableListIterator<CSSProperty *> lstValuesIt(*m_lstValues);
0776     CSSProperty *current;
0777     lstValuesIt.toBack();
0778     while (lstValuesIt.hasPrevious()) {
0779         current = lstValuesIt.previous();
0780         if (current->m_id == propertyID) {
0781             if (old) {
0782                 *old = current->value()->cssText();
0783             }
0784             delete lstValuesIt.value();
0785             lstValuesIt.remove();
0786             changed = true;
0787             break;
0788         }
0789     }
0790 
0791     if (changed) {
0792         setChanged();
0793     }
0794 }
0795 
0796 bool CSSStyleDeclarationImpl::removePropertiesInSet(const int *set, unsigned length)
0797 {
0798     bool changed = false;
0799     for (unsigned i = 0; i < length; i++) {
0800         QMutableListIterator<CSSProperty *> lstValuesIt(*m_lstValues);
0801         CSSProperty *current;
0802         lstValuesIt.toBack();
0803         while (lstValuesIt.hasPrevious()) {
0804             current = lstValuesIt.previous();
0805             if (current->m_id == set[i]) {
0806                 delete lstValuesIt.value();
0807                 lstValuesIt.remove();
0808                 changed = true;
0809                 break;
0810             }
0811         }
0812     }
0813 
0814     return changed;
0815 }
0816 
0817 void CSSStyleDeclarationImpl::setChanged()
0818 {
0819     if (m_node) {
0820         m_node->setChanged();
0821         return;
0822     }
0823 
0824     // ### quick&dirty hack for KDE 3.0... make this MUCH better! (Dirk)
0825     for (StyleBaseImpl *stylesheet = this; stylesheet; stylesheet = stylesheet->parent())
0826         if (stylesheet->isCSSStyleSheet()) {
0827             static_cast<CSSStyleSheetImpl *>(stylesheet)->doc()->updateStyleSelector();
0828             break;
0829         }
0830 }
0831 
0832 void CSSStyleDeclarationImpl::clear()
0833 {
0834     if (!m_lstValues) {
0835         return;
0836     }
0837 
0838     QMutableListIterator<CSSProperty *> it(*m_lstValues);
0839     while (it.hasNext()) {
0840         delete it.next();
0841         it.remove();
0842     }
0843 }
0844 
0845 bool CSSStyleDeclarationImpl::getPropertyPriority(int propertyID) const
0846 {
0847     if (m_lstValues && !m_lstValues->isEmpty()) {
0848         QListIterator<CSSProperty *> lstValuesIt(*m_lstValues);
0849         CSSProperty *current;
0850         while (lstValuesIt.hasNext()) {
0851             current = lstValuesIt.next();
0852             if (propertyID == current->m_id) {
0853                 return current->m_important;
0854             }
0855         }
0856     }
0857     return false;
0858 }
0859 
0860 bool CSSStyleDeclarationImpl::setProperty(int id, const DOMString &value, bool important, int &ec)
0861 {
0862     ec = 0;
0863 
0864     // Setting the value to an empty string just removes the property in both IE and Gecko.
0865     // Setting it to null seems to produce less consistent results, but we treat it just the same.
0866     if (value.isEmpty()) {
0867         removeProperty(id);
0868         return true;
0869     }
0870 
0871     bool success = setProperty(id, value, important);
0872 #if 0
0873     if (!success) {
0874         // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility,
0875         // see <https://bugs.webkit.org/show_bug.cgi?id=7296>.
0876     }
0877 #endif
0878     return success;
0879 }
0880 
0881 bool CSSStyleDeclarationImpl::setProperty(int id, const DOMString &value, bool important)
0882 {
0883     if (!m_lstValues) {
0884         m_lstValues = new QList<CSSProperty *>;
0885     }
0886 
0887     CSSParser parser(strictParsing);
0888     bool success = parser.parseValue(this, id, value, important);
0889     if (!success) {
0890         // qCDebug(KHTML_LOG) << "CSSStyleDeclarationImpl::setProperty invalid property: [" << getPropertyName(id).string()
0891         //<< "] value: [" << value.string() << "]";
0892     } else {
0893         setChanged();
0894     }
0895     return success;
0896 }
0897 
0898 void CSSStyleDeclarationImpl::setProperty(int id, int value, bool important)
0899 {
0900     if (!m_lstValues) {
0901         m_lstValues = new QList<CSSProperty *>;
0902     }
0903     removeProperty(id);
0904 
0905     CSSValueImpl *cssValue = new CSSPrimitiveValueImpl(value);
0906     setParsedValue(id, cssValue, important, m_lstValues);
0907     setChanged();
0908 }
0909 
0910 void CSSStyleDeclarationImpl::setLengthProperty(int id, const DOM::DOMString &value, bool important, bool _multiLength)
0911 {
0912     bool parseMode = strictParsing;
0913     strictParsing = false;
0914     multiLength = _multiLength;
0915     setProperty(id, value, important);
0916     strictParsing = parseMode;
0917     multiLength = false;
0918 }
0919 
0920 void CSSStyleDeclarationImpl::setProperty(const DOMString &propertyString)
0921 {
0922     if (!m_lstValues) {
0923         m_lstValues = new QList<CSSProperty *>;
0924     }
0925 
0926     CSSParser parser(strictParsing);
0927     parser.parseDeclaration(this, propertyString);
0928     setChanged();
0929 }
0930 
0931 unsigned long CSSStyleDeclarationImpl::length() const
0932 {
0933     return m_lstValues ? m_lstValues->count() : 0;
0934 }
0935 
0936 DOMString CSSStyleDeclarationImpl::item(unsigned long index) const
0937 {
0938     if (m_lstValues && index < (unsigned)m_lstValues->count() && m_lstValues->at(index)) {
0939         return getPropertyName(m_lstValues->at(index)->m_id);
0940     }
0941     return DOMString();
0942 }
0943 
0944 CSSRuleImpl *CSSStyleDeclarationImpl::parentRule() const
0945 {
0946     return (m_parent && m_parent->isRule()) ?
0947            static_cast<CSSRuleImpl *>(m_parent) : nullptr;
0948 }
0949 
0950 DOM::DOMString CSSStyleDeclarationImpl::cssText() const
0951 {
0952     if (!m_lstValues || m_lstValues->isEmpty()) {
0953         return DOMString();
0954     }
0955 
0956     DOMString result;
0957 
0958     const CSSProperty *positionXProp = nullptr;
0959     const CSSProperty *positionYProp = nullptr;
0960 
0961     QListIterator<CSSProperty *> lstValuesIt(*m_lstValues);
0962     while (lstValuesIt.hasNext()) {
0963         const CSSProperty *cur = lstValuesIt.next();
0964         if (cur->id() == CSS_PROP_BACKGROUND_POSITION_X) {
0965             positionXProp = cur;
0966         } else if (cur->id() == CSS_PROP_BACKGROUND_POSITION_Y) {
0967             positionYProp = cur;
0968         } else {
0969             result += cur->cssText();
0970         }
0971     }
0972 
0973     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
0974     // It is required because background-position-x/y are non-standard properties and generated output
0975     // would not work in Firefox
0976     // It would be a better solution if background-position was CSS_PAIR.
0977     if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
0978         DOMString positionValue;
0979         const int properties[2] = { CSS_PROP_BACKGROUND_POSITION_X, CSS_PROP_BACKGROUND_POSITION_Y };
0980         if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList()) {
0981             positionValue = getLayeredShortHandValue(properties, 2);
0982         } else {
0983             positionValue = positionXProp->value()->cssText() + DOMString(" ") + positionYProp->value()->cssText();
0984         }
0985         result += DOMString("background-position: ") + positionValue
0986                   + DOMString((positionXProp->isImportant() ? " !important" : ""))
0987                   + DOMString("; ");
0988     } else {
0989         if (positionXProp) {
0990             result += positionXProp->cssText();
0991         }
0992         if (positionYProp) {
0993             result += positionYProp->cssText();
0994         }
0995     }
0996     return result;
0997 }
0998 
0999 void CSSStyleDeclarationImpl::setCssText(const DOM::DOMString &text)
1000 {
1001     if (m_lstValues) {
1002         qDeleteAll(*m_lstValues);
1003         m_lstValues->clear();
1004     } else {
1005         m_lstValues = new QList<CSSProperty *>;
1006     }
1007 
1008     CSSParser parser(strictParsing);
1009     parser.parseDeclaration(this, text);
1010     setChanged();
1011 }
1012 
1013 bool CSSStyleDeclarationImpl::parseString(const DOMString &/*string*/, bool)
1014 {
1015     // qCDebug(KHTML_LOG) << "WARNING: CSSStyleDeclarationImpl::parseString, unimplemented, was called";
1016     return false;
1017     // ###
1018 }
1019 
1020 // --------------------------------------------------------------------------------------
1021 
1022 void CSSInlineStyleDeclarationImpl::setChanged()
1023 {
1024     if (m_node) {
1025         m_node->setNeedsStyleAttributeUpdate();
1026     }
1027     CSSStyleDeclarationImpl::setChanged();
1028 }
1029 
1030 void CSSInlineStyleDeclarationImpl::updateFromAttribute(const DOMString &value)
1031 {
1032     if (!m_lstValues) {
1033         m_lstValues = new QList<CSSProperty *>;
1034     } else {
1035         clear();
1036     }
1037     CSSParser parser(strictParsing);
1038     parser.parseDeclaration(this, value);
1039     CSSStyleDeclarationImpl::setChanged();
1040 }
1041 
1042 // --------------------------------------------------------------------------------------
1043 
1044 unsigned short CSSInheritedValueImpl::cssValueType() const
1045 {
1046     return CSSValue::CSS_INHERIT;
1047 }
1048 
1049 DOM::DOMString CSSInheritedValueImpl::cssText() const
1050 {
1051     return DOMString("inherit");
1052 }
1053 
1054 unsigned short CSSInitialValueImpl::cssValueType() const
1055 {
1056     return CSSValue::CSS_INITIAL;
1057 }
1058 
1059 DOM::DOMString CSSInitialValueImpl::cssText() const
1060 {
1061     return DOMString("initial");
1062 }
1063 
1064 // ----------------------------------------------------------------------------------------
1065 
1066 CSSValueListImpl::~CSSValueListImpl()
1067 {
1068     for (QListIterator<CSSValueImpl *> iterator(m_values); iterator.hasNext();) {
1069         iterator.next()->deref();
1070     }
1071 }
1072 
1073 unsigned short CSSValueListImpl::cssValueType() const
1074 {
1075     return CSSValue::CSS_VALUE_LIST;
1076 }
1077 
1078 void CSSValueListImpl::append(CSSValueImpl *val)
1079 {
1080     m_values.append(val);
1081     val->ref();
1082 }
1083 
1084 DOM::DOMString CSSValueListImpl::cssText() const
1085 {
1086     DOMString separatorString;
1087     if (m_separator == Comma) {
1088         separatorString = DOMString(", ");
1089     } else { // Space
1090         separatorString = DOMString(" ");
1091     }
1092 
1093     DOMString result = "";
1094 
1095     for (QListIterator<CSSValueImpl *> iterator(m_values); iterator.hasNext();) {
1096         if (!result.isEmpty()) {
1097             result += separatorString;
1098         }
1099         result += iterator.next()->cssText();
1100     }
1101 
1102     return result;
1103 }
1104 
1105 // -------------------------------------------------------------------------------------
1106 
1107 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl()
1108     : CSSValueImpl()
1109 {
1110     m_type = 0;
1111 }
1112 
1113 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(int ident)
1114     : CSSValueImpl()
1115 {
1116     m_value.ident = ident;
1117     m_type = CSSPrimitiveValue::CSS_IDENT;
1118 }
1119 
1120 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(double num, CSSPrimitiveValue::UnitTypes type)
1121 {
1122     m_value.num = num;
1123     m_type = type;
1124 }
1125 
1126 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(const DOMString &str, CSSPrimitiveValue::UnitTypes type)
1127 {
1128     m_value.string = str.implementation();
1129     if (m_value.string) {
1130         m_value.string->ref();
1131     }
1132     m_type = type;
1133 }
1134 
1135 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(CounterImpl *c)
1136 {
1137     m_value.counter = c;
1138     if (m_value.counter) {
1139         m_value.counter->ref();
1140     }
1141     m_type = CSSPrimitiveValue::CSS_COUNTER;
1142 }
1143 
1144 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(RectImpl *r)
1145 {
1146     m_value.rect = r;
1147     if (m_value.rect) {
1148         m_value.rect->ref();
1149     }
1150     m_type = CSSPrimitiveValue::CSS_RECT;
1151 }
1152 
1153 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(QRgb color)
1154 {
1155     m_value.rgbcolor = color;
1156     m_type = CSSPrimitiveValue::CSS_RGBCOLOR;
1157 }
1158 
1159 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(PairImpl *p)
1160 {
1161     m_value.pair = p;
1162     if (m_value.pair) {
1163         m_value.pair->ref();
1164     }
1165     m_type = CSSPrimitiveValue::CSS_PAIR;
1166 }
1167 
1168 CSSPrimitiveValueImpl::~CSSPrimitiveValueImpl()
1169 {
1170     cleanup();
1171 }
1172 
1173 void CSSPrimitiveValueImpl::cleanup()
1174 {
1175     switch (m_type) {
1176     case CSSPrimitiveValue::CSS_STRING:
1177     case CSSPrimitiveValue::CSS_URI:
1178     case CSSPrimitiveValue::CSS_ATTR:
1179         if (m_value.string) {
1180             m_value.string->deref();
1181         }
1182         break;
1183     case CSSPrimitiveValue::CSS_COUNTER:
1184         m_value.counter->deref();
1185         break;
1186     case CSSPrimitiveValue::CSS_RECT:
1187         m_value.rect->deref();
1188         break;
1189     case CSSPrimitiveValue::CSS_PAIR:
1190         m_value.pair->deref();
1191         break;
1192     default:
1193         break;
1194     }
1195 
1196     m_type = 0;
1197 }
1198 
1199 int CSSPrimitiveValueImpl::computeLength(khtml::RenderStyle *style, khtml::RenderStyle *rootStyle, int logicalDpiY)
1200 {
1201     return snapValue(computeLengthFloat(style, rootStyle, logicalDpiY));
1202 }
1203 
1204 double CSSPrimitiveValueImpl::computeLengthFloat(khtml::RenderStyle *style, khtml::RenderStyle *rootStyle, int logicalDpiY)
1205 {
1206     unsigned short type = primitiveType();
1207 
1208     double dpiY = 72.; // fallback
1209     if (logicalDpiY) {
1210         dpiY = logicalDpiY;
1211     }
1212     if (!khtml::printpainter && dpiY < 96) {
1213         dpiY = 96.;
1214     }
1215 
1216     double factor = 1.;
1217     switch (type) {
1218     case CSSPrimitiveValue::CSS_EMS:
1219         factor = style->font().pixelSize();
1220         break;
1221     case CSSPrimitiveValue::CSS_EXS:
1222         factor = style->htmlFont().xHeight();
1223         break;
1224     case CSSPrimitiveValue::CSS_CHS: {
1225         const int zw = style->htmlFont().zeroCharWidth();
1226         if (zw != -1) {
1227             factor = zw;
1228         } else {
1229             // assume 0.5em
1230             return ((double)0.5 * style->font().pixelSize());
1231         }
1232         break;
1233     }
1234     case CSSPrimitiveValue::CSS_REMS:
1235         factor = rootStyle->font().pixelSize();
1236         break;
1237     case CSSPrimitiveValue::CSS_PX:
1238         break;
1239     case CSSPrimitiveValue::CSS_CM:
1240         factor = dpiY / 2.54; //72dpi/(2.54 cm/in)
1241         break;
1242     case CSSPrimitiveValue::CSS_MM:
1243         factor = dpiY / 25.4;
1244         break;
1245     case CSSPrimitiveValue::CSS_IN:
1246         factor = dpiY;
1247         break;
1248     case CSSPrimitiveValue::CSS_PT:
1249         factor = dpiY / 72.;
1250         break;
1251     case CSSPrimitiveValue::CSS_PC:
1252         // 1 pc == 12 pt
1253         factor = dpiY * 12. / 72.;
1254         break;
1255     default:
1256         return -1;
1257     }
1258 
1259     return floatValue(type) * factor;
1260 }
1261 
1262 int CSSPrimitiveValueImpl::getDPIResolution() const
1263 {
1264     unsigned short type = primitiveType();
1265     double factor = 1.;
1266     switch (type) {
1267     case CSSPrimitiveValue::CSS_DPI:
1268         break;
1269     case CSSPrimitiveValue::CSS_DPCM:
1270         factor = 2.54;
1271         break;
1272     default:
1273         return -1;
1274     }
1275 
1276     return (int)(0.01 + floatValue(type) * factor);
1277 }
1278 
1279 void CSSPrimitiveValueImpl::setFloatValue(unsigned short unitType, double floatValue, int &exceptioncode)
1280 {
1281     exceptioncode = 0;
1282     cleanup();
1283     // ### check if property supports this type
1284     if (m_type > CSSPrimitiveValue::CSS_DIMENSION) {
1285         exceptioncode = CSSException::SYNTAX_ERR + CSSException::_EXCEPTION_OFFSET;
1286         return;
1287     }
1288     //if(m_type > CSSPrimitiveValue::CSS_DIMENSION) throw DOMException(DOMException::INVALID_ACCESS_ERR);
1289     m_value.num = floatValue;
1290     m_type = unitType;
1291 }
1292 
1293 void CSSPrimitiveValueImpl::setStringValue(unsigned short stringType, const DOMString &stringValue, int &exceptioncode)
1294 {
1295     exceptioncode = 0;
1296     cleanup();
1297     //if(m_type < CSSPrimitiveValue::CSS_STRING) throw DOMException(DOMException::INVALID_ACCESS_ERR);
1298     //if(m_type > CSSPrimitiveValue::CSS_ATTR) throw DOMException(DOMException::INVALID_ACCESS_ERR);
1299     if (m_type < CSSPrimitiveValue::CSS_STRING || m_type > CSSPrimitiveValue::CSS_ATTR) {
1300         exceptioncode = CSSException::SYNTAX_ERR + CSSException::_EXCEPTION_OFFSET;
1301         return;
1302     }
1303     if (stringType != CSSPrimitiveValue::CSS_IDENT) {
1304         m_value.string = stringValue.implementation();
1305         m_value.string->ref();
1306         m_type = stringType;
1307     }
1308     // ### parse ident
1309 }
1310 
1311 unsigned short CSSPrimitiveValueImpl::cssValueType() const
1312 {
1313     return CSSValue::CSS_PRIMITIVE_VALUE;
1314 }
1315 
1316 bool CSSPrimitiveValueImpl::parseString(const DOMString &/*string*/, bool)
1317 {
1318     // ###
1319     // qCDebug(KHTML_LOG) << "WARNING: CSSPrimitiveValueImpl::parseString, unimplemented, was called";
1320     return false;
1321 }
1322 
1323 int CSSPrimitiveValueImpl::getIdent()
1324 {
1325     if (m_type != CSSPrimitiveValue::CSS_IDENT) {
1326         return 0;
1327     }
1328     return m_value.ident;
1329 }
1330 
1331 DOM::DOMString CSSPrimitiveValueImpl::cssText() const
1332 {
1333     // ### return the original value instead of a generated one (e.g. color
1334     // name if it was specified) - check what spec says about this
1335     DOMString text;
1336     switch (m_type) {
1337     case CSSPrimitiveValue::CSS_UNKNOWN:
1338         // ###
1339         break;
1340     case CSSPrimitiveValue::CSS_NUMBER:
1341         // We want to output integral values w/o a period, but others as-is
1342         if (m_value.num == (int)m_value.num) {
1343             text = DOMString(QString::number((int)m_value.num));
1344         } else {
1345             text = DOMString(QString::number(m_value.num));
1346         }
1347         break;
1348     case CSSPrimitiveValue::CSS_PERCENTAGE:
1349         text = DOMString(QString::number(m_value.num) + "%");
1350         break;
1351     case CSSPrimitiveValue::CSS_EMS:
1352         text = DOMString(QString::number(m_value.num) + "em");
1353         break;
1354     case CSSPrimitiveValue::CSS_EXS:
1355         text = DOMString(QString::number(m_value.num) + "ex");
1356         break;
1357     case CSSPrimitiveValue::CSS_CHS:
1358         text = DOMString(QString::number( m_value.num ) + "ch");
1359         break;
1360     case CSSPrimitiveValue::CSS_REMS:
1361         text = DOMString(QString::number( m_value.num ) + "rem");
1362         break;
1363     case CSSPrimitiveValue::CSS_PX:
1364         text = DOMString(QString::number(m_value.num) + "px");
1365         break;
1366     case CSSPrimitiveValue::CSS_CM:
1367         text = DOMString(QString::number(m_value.num) + "cm");
1368         break;
1369     case CSSPrimitiveValue::CSS_MM:
1370         text = DOMString(QString::number(m_value.num) + "mm");
1371         break;
1372     case CSSPrimitiveValue::CSS_IN:
1373         text = DOMString(QString::number(m_value.num) + "in");
1374         break;
1375     case CSSPrimitiveValue::CSS_PT:
1376         text = DOMString(QString::number(m_value.num) + "pt");
1377         break;
1378     case CSSPrimitiveValue::CSS_PC:
1379         text = DOMString(QString::number(m_value.num) + "pc");
1380         break;
1381     case CSSPrimitiveValue::CSS_DEG:
1382         text = DOMString(QString::number(m_value.num) + "deg");
1383         break;
1384     case CSSPrimitiveValue::CSS_RAD:
1385         text = DOMString(QString::number(m_value.num) + "rad");
1386         break;
1387     case CSSPrimitiveValue::CSS_GRAD:
1388         text = DOMString(QString::number(m_value.num) + "grad");
1389         break;
1390     case CSSPrimitiveValue::CSS_MS:
1391         text = DOMString(QString::number(m_value.num) + "ms");
1392         break;
1393     case CSSPrimitiveValue::CSS_S:
1394         text = DOMString(QString::number(m_value.num) + "s");
1395         break;
1396     case CSSPrimitiveValue::CSS_HZ:
1397         text = DOMString(QString::number(m_value.num) + "hz");
1398         break;
1399     case CSSPrimitiveValue::CSS_KHZ:
1400         text = DOMString(QString::number(m_value.num) + "khz");
1401         break;
1402     case CSSPrimitiveValue::CSS_DIMENSION:
1403         // ###
1404         break;
1405     case CSSPrimitiveValue::CSS_STRING:
1406         text = quoteStringIfNeeded(m_value.string);
1407         break;
1408     case CSSPrimitiveValue::CSS_URI:
1409         text  = "url(";
1410         text += DOMString(m_value.string);
1411         text += ")";
1412         break;
1413     case CSSPrimitiveValue::CSS_IDENT:
1414         text = getValueName(m_value.ident);
1415         break;
1416     case CSSPrimitiveValue::CSS_ATTR:
1417         text = "attr(";
1418         text += DOMString(m_value.string);
1419         text += ")";
1420         break;
1421     case CSSPrimitiveValue::CSS_COUNTER:
1422         text = "counter(";
1423         text += m_value.counter->m_identifier;
1424         text += ")";
1425         // ### add list-style and separator
1426         break;
1427     case CSSPrimitiveValue::CSS_RECT: {
1428         RectImpl *rectVal = getRectValue();
1429         text = "rect(";
1430         text += rectVal->top()->cssText() + DOMString(" ");
1431         text += rectVal->right()->cssText() + DOMString(" ");
1432         text += rectVal->bottom()->cssText() + DOMString(" ");
1433         text += rectVal->left()->cssText() + DOMString(")");
1434         break;
1435     }
1436     case CSSPrimitiveValue::CSS_RGBCOLOR:
1437         if (qAlpha(m_value.rgbcolor) != 0xFF) {
1438             if (m_value.rgbcolor == khtml::transparentColor) {
1439                 text = "transparent";
1440             } else
1441                 text = QString("rgba(" + QString::number(qRed(m_value.rgbcolor)) + ", "
1442                                + QString::number(qGreen(m_value.rgbcolor)) + ", "
1443                                + QString::number(qBlue(m_value.rgbcolor)) + ", "
1444                                + QString::number(qAlpha(m_value.rgbcolor) / 255.0) + ")");
1445         } else {
1446             text = QString("rgb(" + QString::number(qRed(m_value.rgbcolor)) + ", "
1447                            + QString::number(qGreen(m_value.rgbcolor)) + ", "
1448                            + QString::number(qBlue(m_value.rgbcolor)) + ")");
1449         }
1450         break;
1451     case CSSPrimitiveValue::CSS_PAIR:
1452         text = m_value.pair->first()->cssText();
1453         text += " ";
1454         text += m_value.pair->second()->cssText();
1455         break;
1456     default:
1457         break;
1458     }
1459     return text;
1460 }
1461 
1462 // -----------------------------------------------------------------
1463 
1464 RectImpl::RectImpl()
1465 {
1466     m_top = nullptr;
1467     m_right = nullptr;
1468     m_bottom = nullptr;
1469     m_left = nullptr;
1470 }
1471 
1472 RectImpl::~RectImpl()
1473 {
1474     if (m_top) {
1475         m_top->deref();
1476     }
1477     if (m_right) {
1478         m_right->deref();
1479     }
1480     if (m_bottom) {
1481         m_bottom->deref();
1482     }
1483     if (m_left) {
1484         m_left->deref();
1485     }
1486 }
1487 
1488 void RectImpl::setTop(CSSPrimitiveValueImpl *top)
1489 {
1490     if (top) {
1491         top->ref();
1492     }
1493     if (m_top) {
1494         m_top->deref();
1495     }
1496     m_top = top;
1497 }
1498 
1499 void RectImpl::setRight(CSSPrimitiveValueImpl *right)
1500 {
1501     if (right) {
1502         right->ref();
1503     }
1504     if (m_right) {
1505         m_right->deref();
1506     }
1507     m_right = right;
1508 }
1509 
1510 void RectImpl::setBottom(CSSPrimitiveValueImpl *bottom)
1511 {
1512     if (bottom) {
1513         bottom->ref();
1514     }
1515     if (m_bottom) {
1516         m_bottom->deref();
1517     }
1518     m_bottom = bottom;
1519 }
1520 
1521 void RectImpl::setLeft(CSSPrimitiveValueImpl *left)
1522 {
1523     if (left) {
1524         left->ref();
1525     }
1526     if (m_left) {
1527         m_left->deref();
1528     }
1529     m_left = left;
1530 }
1531 
1532 // -----------------------------------------------------------------
1533 
1534 PairImpl::~PairImpl()
1535 {
1536     if (m_first) {
1537         m_first->deref();
1538     } if (m_second) {
1539         m_second->deref();
1540     }
1541 }
1542 
1543 void PairImpl::setFirst(CSSPrimitiveValueImpl *first)
1544 {
1545     if (first == m_first) {
1546         return;
1547     }
1548     if (m_first) {
1549         m_first->deref();
1550     }
1551     m_first = first;
1552     if (m_first) {
1553         m_first->ref();
1554     }
1555 }
1556 
1557 void PairImpl::setSecond(CSSPrimitiveValueImpl *second)
1558 {
1559     if (second == m_second) {
1560         return;
1561     }
1562     if (m_second) {
1563         m_second->deref();
1564     }
1565     m_second = second;
1566     if (m_second) {
1567         m_second->ref();
1568     }
1569 }
1570 
1571 // -----------------------------------------------------------------
1572 
1573 CSSImageValueImpl::CSSImageValueImpl(const DOMString &url, StyleBaseImpl *style)
1574     : CSSPrimitiveValueImpl(url, CSSPrimitiveValue::CSS_URI)
1575 {
1576     m_image = nullptr;
1577     const DOMString imgUrl = url.trimSpaces();
1578     if (!imgUrl.isEmpty()) {
1579         m_fullImageUrl = style->baseURL().resolved(QUrl(imgUrl.string())).toString();
1580     } else {
1581         m_fullImageUrl.clear();
1582     }
1583 }
1584 
1585 CSSImageValueImpl::CSSImageValueImpl()
1586     : CSSPrimitiveValueImpl(CSS_VAL_NONE)
1587 {
1588     m_image = nullptr;
1589     m_fullImageUrl.clear();
1590 }
1591 
1592 CSSImageValueImpl::~CSSImageValueImpl()
1593 {
1594     if (m_image) {
1595         m_image->deref(this);
1596     }
1597 }
1598 
1599 khtml::CachedImage *CSSImageValueImpl::requestCssImage(DocumentImpl *doc)
1600 {
1601     if (!m_image && !m_fullImageUrl.isEmpty()) {
1602         m_image = doc->docLoader()->requestImage(m_fullImageUrl);
1603         if (m_image) {
1604             m_image->ref(this);
1605         }
1606     }
1607     return m_image;
1608 }
1609 
1610 // ------------------------------------------------------------------------
1611 
1612 FontFamilyValueImpl::FontFamilyValueImpl(const QString &string)
1613     : CSSPrimitiveValueImpl(DOMString(string), CSSPrimitiveValue::CSS_STRING)
1614 {
1615     static const QRegExp parenReg(" \\(.*\\)$");
1616 //  static const QRegExp braceReg(" \\[.*\\]$");
1617 
1618     parsedFontName = string;
1619 
1620     // a language tag is often added in braces at the end. Remove it.
1621     parsedFontName.replace(parenReg, QString());
1622 
1623 #if 0
1624     // cannot use such early checks against the font database anymore,
1625     // as the font subsystem might not contain the requested font yet
1626     // (case of downloadable font faces)
1627 
1628     // remove [Xft] qualifiers
1629     parsedFontName.replace(braceReg, QString());
1630 
1631     const QString &available = KHTMLSettings::availableFamilies();
1632 
1633     parsedFontName = parsedFontName.toLower();
1634     // qCDebug(KHTML_LOG) << "searching for face '" << parsedFontName << "'";
1635 
1636     int pos = available.indexOf(',' + parsedFontName + ',', 0, Qt::CaseInsensitive);
1637     if (pos == -1) {
1638         // many pages add extra MSs to make sure it's windows only ;(
1639         if (parsedFontName.startsWith("ms ")) {
1640             parsedFontName = parsedFontName.mid(3);
1641         }
1642         if (parsedFontName.endsWith(" ms")) {
1643             parsedFontName.truncate(parsedFontName.length() - 3);
1644         }
1645         pos = available.indexOf(",ms " + parsedFontName + ',', 0, Qt::CaseInsensitive);
1646         if (pos == -1) {
1647             pos = available.indexOf(',' + parsedFontName + " ms,", 0, Qt::CaseInsensitive);
1648         }
1649     }
1650 
1651     if (pos != -1) {
1652         ++pos;
1653         int p = available.indexOf(',', pos);
1654         assert(p != -1);   // available is supposed to start and end with ,
1655         parsedFontName = available.mid(pos, p - pos);
1656         // qCDebug(KHTML_LOG) << "going for '" << parsedFontName << "'";
1657     }
1658 
1659 #endif // !APPLE_CHANGES
1660 }
1661 
1662 FontValueImpl::FontValueImpl()
1663     : style(nullptr), variant(nullptr), weight(nullptr), size(nullptr), lineHeight(nullptr), family(nullptr)
1664 {
1665 }
1666 
1667 FontValueImpl::~FontValueImpl()
1668 {
1669     delete style;
1670     delete variant;
1671     delete weight;
1672     delete size;
1673     delete lineHeight;
1674     delete family;
1675 }
1676 
1677 DOMString FontValueImpl::cssText() const
1678 {
1679     // font variant weight size / line-height family
1680 
1681     DOMString result("");
1682 
1683     if (style) {
1684         result += style->cssText();
1685     }
1686     if (variant) {
1687         if (result.length() > 0) {
1688             result += " ";
1689         }
1690         result += variant->cssText();
1691     }
1692     if (weight) {
1693         if (result.length() > 0) {
1694             result += " ";
1695         }
1696         result += weight->cssText();
1697     }
1698     if (size) {
1699         if (result.length() > 0) {
1700             result += " ";
1701         }
1702         result += size->cssText();
1703     }
1704     if (lineHeight) {
1705         if (!size) {
1706             result += " ";
1707         }
1708         result += "/";
1709         result += lineHeight->cssText();
1710     }
1711     if (family) {
1712         if (result.length() > 0) {
1713             result += " ";
1714         }
1715         result += family->cssText();
1716     }
1717 
1718     return result;
1719 }
1720 
1721 QuotesValueImpl::QuotesValueImpl()
1722     : levels(0)
1723 {
1724 }
1725 
1726 DOMString QuotesValueImpl::cssText() const
1727 {
1728     return QString("\"" + data.join("\" \"") + "\"");
1729 }
1730 
1731 void QuotesValueImpl::addLevel(const QString &open, const QString &close)
1732 {
1733     data.append(open);
1734     data.append(close);
1735     levels++;
1736 }
1737 
1738 QString QuotesValueImpl::openQuote(int level) const
1739 {
1740     if (levels == 0) {
1741         return "";
1742     }
1743     level--; // increments are calculated before openQuote is called
1744 //     qCDebug(KHTML_LOG) << "Open quote level:" << level;
1745     if (level < 0) {
1746         level = 0;
1747     } else if (level >= (int) levels) {
1748         level = (int)(levels - 1);
1749     }
1750     return data[level * 2];
1751 }
1752 
1753 QString QuotesValueImpl::closeQuote(int level) const
1754 {
1755     if (levels == 0) {
1756         return "";
1757     }
1758 //     qCDebug(KHTML_LOG) << "Close quote level:" << level;
1759     if (level < 0) {
1760         level = 0;
1761     } else if (level >= (int) levels) {
1762         level = (int)(levels - 1);
1763     }
1764     return data[level * 2 + 1];
1765 }
1766 
1767 // Used for text-shadow and box-shadow
1768 ShadowValueImpl::ShadowValueImpl(CSSPrimitiveValueImpl *_x, CSSPrimitiveValueImpl *_y,
1769                                  CSSPrimitiveValueImpl *_blur, CSSPrimitiveValueImpl *_color)
1770     : x(_x), y(_y), blur(_blur), color(_color)
1771 {}
1772 
1773 ShadowValueImpl::~ShadowValueImpl()
1774 {
1775     delete x;
1776     delete y;
1777     delete blur;
1778     delete color;
1779 }
1780 
1781 DOMString ShadowValueImpl::cssText() const
1782 {
1783     DOMString text("");
1784     if (color) {
1785         text += color->cssText();
1786     }
1787     if (x) {
1788         if (text.length() > 0) {
1789             text += " ";
1790         }
1791         text += x->cssText();
1792     }
1793     if (y) {
1794         if (text.length() > 0) {
1795             text += " ";
1796         }
1797         text += y->cssText();
1798     }
1799     if (blur) {
1800         if (text.length() > 0) {
1801             text += " ";
1802         }
1803         text += blur->cssText();
1804     }
1805 
1806     return text;
1807 }
1808 
1809 DOMString CounterActImpl::cssText() const
1810 {
1811     DOMString text(m_counter);
1812     text += DOMString(QString::number(m_value));
1813 
1814     return text;
1815 }
1816 
1817 DOMString CSSProperty::cssText() const
1818 {
1819     return getPropertyName(m_id) + DOMString(": ") + m_value->cssText() + (m_important ? DOMString(" !important") : DOMString()) + DOMString("; ");
1820 }
1821 
1822 // -------------------------------------------------------------------------
1823 
1824 #if 0
1825 // ENABLE(SVG_FONTS)
1826 bool CSSFontFaceSrcValueImpl::isSVGFontFaceSrc() const
1827 {
1828     return !strcasecmp(m_format, "svg");
1829 }
1830 #endif
1831 
1832 bool CSSFontFaceSrcValueImpl::isSupportedFormat() const
1833 {
1834     // Normally we would just check the format, but in order to avoid conflicts with the old WinIE style of font-face,
1835     // we will also check to see if the URL ends with .eot.  If so, we'll go ahead and assume that we shouldn't load it.
1836     if (m_format.isEmpty()) {
1837         // Check for .eot.
1838         if (m_resource.endsWith(".eot") || m_resource.endsWith(".EOT")) {
1839             return false;
1840         }
1841         return true;
1842     }
1843 
1844     return !strcasecmp(m_format, "truetype") || !strcasecmp(m_format, "opentype") || !strcasecmp(m_format, "woff")
1845 #if 0
1846            //ENABLE(SVG_FONTS)
1847            || isSVGFontFaceSrc()
1848 #endif
1849            ;
1850 }
1851 
1852 DOMString CSSFontFaceSrcValueImpl::cssText() const
1853 {
1854     DOMString result;
1855     if (isLocal()) {
1856         result += "local(";
1857     } else {
1858         result += "url(";
1859     }
1860     result += m_resource;
1861     result += ")";
1862     if (!m_format.isEmpty()) {
1863         result += " format(";
1864         result += m_format;
1865         result += ")";
1866     }
1867     return result;
1868 }
1869