| /* |
| * This file is part of the DOM implementation for KDE. |
| * |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * 1999 Waldo Bastian (bastian@kde.org) |
| * 2001 Andreas Schlapbach (schlpbch@iam.unibe.ch) |
| * 2001 Dirk Mueller (mueller@kde.org) |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| |
| //#define CSS_DEBUG |
| //#define CSS_AURAL |
| //#define CSS_DEBUG_BCKGR |
| |
| #include <assert.h> |
| |
| #include "css/css_stylesheetimpl.h" |
| #include "css/css_ruleimpl.h" |
| #include "css/css_valueimpl.h" |
| #include "css/csshelper.h" |
| |
| #include "dom/css_stylesheet.h" |
| #include "dom/css_rule.h" |
| #include "dom/dom_string.h" |
| #include "dom/dom_exception.h" |
| |
| #include "xml/dom_nodeimpl.h" |
| #include "html/html_documentimpl.h" |
| #include "khtml_part.h" |
| #include "khtmlview.h" |
| |
| using namespace DOM; |
| |
| #include <kdebug.h> |
| #include <kglobal.h> |
| #include <kglobalsettings.h> // For system fonts |
| #include <kapplication.h> |
| |
| #include "misc/htmlhashes.h" |
| #include "misc/helper.h" |
| |
| // |
| // The following file defines the function |
| // const struct props *findProp(const char *word, int len) |
| // |
| // with 'props->id' a CSS property in the range from CSS_PROP_MIN to |
| // (and including) CSS_PROP_TOTAL-1 |
| #include "cssproperties.c" |
| #include "cssvalues.c" |
| |
| |
| static QPtrList<CSSProperty>* m_propList = 0; |
| static bool m_bImportant = FALSE; |
| static bool m_bnonCSSHint = FALSE; |
| |
| int DOM::getPropertyID(const char *tagStr, int len) |
| { |
| const struct props *propsPtr = findProp(tagStr, len); |
| if (!propsPtr) |
| return 0; |
| |
| return propsPtr->id; |
| } |
| |
| // ------------------------------------------------------------------------------------------------------ |
| |
| bool StyleBaseImpl::deleteMe() |
| { |
| if(!m_parent && _ref <= 0) return true; |
| return false; |
| } |
| |
| void StyleBaseImpl::checkLoaded() |
| { |
| if(m_parent) m_parent->checkLoaded(); |
| } |
| |
| DOMString StyleBaseImpl::baseURL() |
| { |
| // try to find the style sheet. If found look for it's url. |
| // If it has none, look for the parentsheet, or the parentNode and |
| // try to find out about their url |
| StyleBaseImpl *b = this; |
| while(b && !b->isStyleSheet()) |
| b = b->parent(); |
| |
| if(!b) return DOMString(); |
| |
| StyleSheetImpl *sheet = static_cast<StyleSheetImpl *>(b); |
| if(!sheet->href().isNull()) |
| return sheet->href(); |
| |
| // find parent |
| if(sheet->parent()) return sheet->parent()->baseURL(); |
| |
| if(!sheet->ownerNode()) return DOMString(); |
| |
| DocumentImpl *doc = sheet->ownerNode()->getDocument(); |
| |
| return doc->baseURL(); |
| } |
| |
| /* |
| * parsing functions for stylesheets |
| */ |
| |
| const QChar * |
| StyleBaseImpl::parseSpace(const QChar *curP, const QChar *endP) |
| { |
| bool sc = false; // possible start comment? |
| bool ec = false; // possible end comment? |
| bool ic = false; // in comment? |
| |
| while (curP < endP) |
| { |
| if (ic) |
| { |
| if (ec && (*curP == '/')) |
| ic = false; |
| else if (*curP == '*') |
| ec = true; |
| else |
| ec = false; |
| } |
| else if (sc && (*curP == '*')) |
| { |
| ic = true; |
| } |
| else if (*curP == '/') |
| { |
| sc = true; |
| } |
| //else if (!isspace(*curP)) |
| else if (!(curP->isSpace())) |
| { |
| return(curP); |
| } |
| else |
| { |
| sc = false; |
| } |
| curP++; |
| } |
| |
| return(0); |
| } |
| |
| /* |
| * ParseToChar |
| * |
| * Search for an expected character. Deals with escaped characters, |
| * quoted strings, and pairs of braces/parens/brackets. |
| */ |
| const QChar * |
| StyleBaseImpl::parseToChar(const QChar *curP, const QChar *endP, QChar c, bool chkws, bool endAtBlock) |
| { |
| //kdDebug( 6080 ) << "parsetochar: \"" << QString(curP, endP-curP) << "\" searching " << c << " ws=" << chkws << endl; |
| |
| bool sq = false; /* in single quote? */ |
| bool dq = false; /* in double quote? */ |
| bool esc = false; /* escape mode? */ |
| |
| while (curP < endP) |
| { |
| if (esc) |
| esc = false; |
| else if (*curP == '\\') |
| esc = true; |
| else if (!sq && (*curP == '"')) |
| dq = !dq; |
| else if (!dq && (*curP == '\'')) |
| sq = !sq; |
| else if (!sq && !dq && *curP == c) |
| return(curP); |
| else if (!sq && !dq && chkws && curP->isSpace()) //isspace(*curP)) |
| return(curP); |
| else if(!sq && !dq ) { |
| if (*curP == '{') { |
| if(endAtBlock) |
| return curP; |
| curP = parseToChar(curP + 1, endP, '}', false); |
| if (!curP) |
| return(0); |
| } else if (*curP == '(') { |
| curP = parseToChar(curP + 1, endP, ')', false); |
| if (!curP) |
| return(0); |
| } else if (*curP == '[') { |
| curP = parseToChar(curP + 1, endP, ']', false); |
| if (!curP) |
| return(0); |
| } |
| } |
| curP++; |
| } |
| |
| return(0); |
| } |
| |
| CSSRuleImpl * |
| StyleBaseImpl::parseAtRule(const QChar *&curP, const QChar *endP) |
| { |
| curP++; |
| const QChar *startP = curP; |
| while( *curP != ' ' && *curP != '{' && *curP != '\'') |
| curP++; |
| |
| QString rule(startP, curP-startP); |
| rule = rule.lower(); |
| |
| //kdDebug( 6080 ) << "rule = '" << rule << "'" << endl; |
| |
| if(rule == "import") |
| { |
| // load stylesheet and pass it over |
| curP = parseSpace(curP, endP); |
| if(!curP) return 0; |
| startP = curP++; |
| curP = parseToChar(startP, endP, ';', true); |
| // Do not allow @import statements after explicity inlined |
| // declarations. They should simply be ignored per CSS-1 |
| // specification section 3.0. |
| if( !curP || hasInlinedDecl ) return 0; |
| DOMString url = khtml::parseURL(DOMString(startP, curP - startP)); |
| startP = curP; |
| if(*curP != ';') |
| curP = parseToChar(startP, endP, ';', false, true); |
| if(!curP) return 0; |
| |
| DOMString mediaList = DOMString( startP, curP - startP); |
| // ### check if at the beginning of the stylesheet (no style rule |
| // before the import rule) |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "import rule = " << url.string() << ", mediaList = " |
| << mediaList.string() << endl; |
| #endif |
| // ignore block following @import rule |
| if( *curP == '{' ) { |
| curP++; |
| curP = parseToChar(curP, endP, '}', false); |
| if(curP) |
| curP++; |
| } |
| if(!this->isCSSStyleSheet()) return 0; |
| |
| return new CSSImportRuleImpl( this, url, mediaList ); |
| } |
| else if(rule == "charset") |
| { |
| // ### invoke decoder |
| startP = curP++; |
| curP = parseToChar(startP, endP, ';', false); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "charset = " << QString(startP, curP - startP) << endl; |
| #endif |
| } |
| else if(rule == "font-face") |
| { |
| startP = curP++; |
| curP = parseToChar(startP, endP, '{', false); |
| if ( !curP || curP >= endP ) return 0; |
| curP++; |
| curP = parseToChar(curP, endP, '}', false); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "font rule = " << QString(startP, curP - startP) << endl; |
| #endif |
| } |
| else if(rule == "media") |
| { |
| startP = curP++; |
| curP = parseToChar(startP, endP, '{', false); |
| //qDebug("mediaList = '%s'", mediaList.latin1() ); |
| if ( !curP || curP >= endP ) return 0; |
| DOMString mediaList = DOMString( startP, curP - startP); |
| curP++; |
| startP = curP; |
| if ( curP >= endP ) return 0; |
| curP = parseToChar(curP, endP, '}', false); |
| if ( !curP || startP >= curP ) |
| return 0; |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "media rule = " << QString(startP, curP - startP) |
| << ", mediaList = " << mediaList.string() << endl; |
| #endif |
| return new CSSMediaRuleImpl( this, startP, curP, mediaList ); |
| } |
| else if(rule == "page") |
| { |
| startP = curP++; |
| curP = parseToChar(startP, endP, '{', false); |
| if ( !curP || curP >= endP ) return 0; |
| curP++; |
| curP = parseToChar(curP, endP, '}', false); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "page rule = " << QString(startP, curP - startP) << endl; |
| #endif |
| } |
| |
| |
| return 0; |
| } |
| |
| static DOMString getValue( const QChar *curP, const QChar *endP, const QChar *&endVal) |
| { |
| //QString selecString( curP, endP - curP ); |
| //kdDebug( 6080 ) << "getValue = \"" << selecString << "\"" << endl; |
| endVal = curP; |
| endVal++; // ignore first char (could be the ':' form the pseudo classes) |
| while( endVal < endP && *endVal != '.' && *endVal != ':' && *endVal != '[' ) |
| endVal++; |
| const QChar *end = endVal; |
| if(endVal == endP) |
| endVal = 0; |
| return DOMString( curP, end - curP); |
| } |
| |
| CSSSelector * |
| StyleBaseImpl::parseSelector2(const QChar *curP, const QChar *endP, |
| CSSSelector::Relation relation) |
| { |
| CSSSelector *cs = new CSSSelector(); |
| #ifdef CSS_DEBUG |
| QString selecString( curP, endP - curP ); |
| kdDebug( 6080 ) << "selectString = \"" << selecString << "\"" << endl; |
| #endif |
| const QChar *endVal = 0; |
| |
| if (*curP == '#' && (curP < endP && !((*(curP+1)).isDigit()))) |
| { |
| cs->tag = -1; |
| cs->attr = ATTR_ID; |
| cs->match = CSSSelector::Exact; |
| cs->value = getValue( curP+1, endP, endVal); |
| } |
| else if (*curP == '.' && curP < endP && ( !strictParsing || !(*(curP+1)).isDigit() ) ) |
| { |
| cs->tag = -1; |
| cs->attr = ATTR_CLASS; |
| cs->match = CSSSelector::List; |
| cs->value = getValue( curP+1, endP, endVal); |
| } |
| else if (*curP == ':' && (curP < endP && !((*(curP+1)).isDigit()))) |
| { |
| // pseudo attributes (:link, :hover, ...), they are case insensitive. |
| cs->tag = -1; |
| cs->match = CSSSelector::Pseudo; |
| cs->value = getValue(curP+1, endP, endVal); |
| cs->value = cs->value.implementation()->lower(); |
| } |
| else |
| { |
| const QChar *startP = curP; |
| QString tag; |
| while (curP < endP) |
| { |
| if (*curP =='#' && (curP < endP && !((*(curP+1)).isDigit()))) |
| { |
| tag = QString( startP, curP-startP ); |
| cs->attr = ATTR_ID; |
| cs->match = CSSSelector::Exact; |
| cs->value = getValue(curP+1, endP, endVal); |
| break; |
| } |
| else if (*curP == '.' && curP < endP && ( !strictParsing || !(*(curP+1)).isDigit() ) ) |
| { |
| tag = QString( startP, curP - startP ); |
| cs->attr = ATTR_CLASS; |
| cs->match = CSSSelector::List; |
| cs->value = getValue(curP+1, endP, endVal); |
| break; |
| } |
| else if (*curP == ':' && (curP < endP && !((*(curP+1)).isDigit()))) |
| { |
| // pseudo attributes (:link, :hover, ...), they are case insensitive. |
| tag = QString( startP, curP - startP ); |
| cs->match = CSSSelector::Pseudo; |
| cs->value = getValue(curP+1, endP, endVal); |
| cs->value = cs->value.implementation()->lower(); |
| break; |
| } |
| else if (*curP == '[') |
| { |
| tag = QString( startP, curP - startP ); |
| curP++; |
| if ( curP >= endP ) { |
| delete cs; |
| return 0; |
| } |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "tag = " << tag << endl; |
| #endif |
| const QChar *closebracket = parseToChar(curP, endP, ']', false); |
| if (!closebracket) |
| { |
| kdWarning()<<"error in css: closing bracket not found!"<<endl; |
| delete cs; |
| return 0; |
| } |
| QString attr; |
| const QChar *equal = parseToChar(curP, closebracket, '=', false); |
| if(!equal) |
| { |
| attr = QString( curP, closebracket - curP ); |
| attr = attr.stripWhiteSpace(); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "attr = '" << attr << "'" << endl; |
| #endif |
| cs->match = CSSSelector::Set; |
| endVal = closebracket + 1; |
| // ### fixme we ignore everything after [..] |
| if( endVal == endP ) |
| endVal = 0; |
| } |
| else |
| { |
| // check relation: = / ~= / |= |
| // CSS3: ^= / $= / *= |
| if(*(equal-1) == '~') |
| { |
| attr = QString( curP, equal - curP - 1 ); |
| cs->match = CSSSelector::List; |
| } |
| else if(*(equal-1) == '|') |
| { |
| attr = QString( curP, equal - curP - 1 ); |
| cs->match = CSSSelector::Hyphen; |
| } |
| else if(*(equal-1) == '^') |
| { |
| attr = QString( curP, equal - curP - 1); |
| cs->match = CSSSelector::Begin; |
| } |
| else if(*(equal-1) == '$') |
| { |
| attr = QString( curP, equal - curP - 1); |
| cs->match = CSSSelector::End; |
| } |
| else if(*(equal-1) == '*') |
| { |
| attr = QString( curP, equal - curP - 1); |
| cs->match = CSSSelector::Contain; |
| } |
| else |
| { |
| attr = QString(curP, equal - curP ); |
| cs->match = CSSSelector::Exact; |
| } |
| } |
| { |
| attr = attr.stripWhiteSpace(); |
| StyleBaseImpl *root = this; |
| DocumentImpl *doc = 0; |
| while (root->parent()) |
| root = root->parent(); |
| if (root->isCSSStyleSheet()) |
| doc = static_cast<CSSStyleSheetImpl*>(root)->doc(); |
| |
| if ( doc ) { |
| if (doc->isHTMLDocument()) |
| attr = attr.lower(); |
| const DOMString dattr(attr); |
| cs->attr = doc->attrId(0, dattr.implementation(), false); |
| } |
| else { |
| cs->attr = khtml::getAttrID(attr.lower().ascii(), attr.length()); |
| // this case should never happen - only when loading |
| // the default stylesheet - which must not contain unknown attributes |
| assert(cs->attr); |
| } |
| if (!cs->attr) { |
| delete cs; |
| return 0; |
| } |
| } |
| if(equal) |
| { |
| equal++; |
| while(equal < endP && *equal == ' ') |
| equal++; |
| if(equal >= endP ) { |
| delete cs; |
| return 0; |
| } |
| endVal = equal; |
| bool hasQuote = false; |
| if(*equal == '\'') { |
| equal++; |
| endVal++; |
| while(endVal < endP && *endVal != '\'') |
| endVal++; |
| hasQuote = true; |
| } else if(*equal == '\"') { |
| equal++; |
| endVal++; |
| while(endVal < endP && *endVal != '\"') |
| endVal++; |
| hasQuote = true; |
| } else { |
| while(endVal < endP && *endVal != ']') |
| endVal++; |
| } |
| cs->value = DOMString(equal, endVal - equal); |
| if ( hasQuote ) { |
| while( endVal < endP - 1 && *endVal != ']' ) |
| endVal++; |
| } |
| endVal++; |
| // ### fixme we ignore everything after [..] |
| if( endVal == endP ) |
| endVal = 0; |
| } |
| break; |
| } |
| else |
| { |
| curP++; |
| } |
| } |
| if (curP == endP) |
| { |
| tag = QString( startP, curP - startP ); |
| } |
| if(tag == "*") |
| { |
| //kdDebug( 6080 ) << "found '*' selector" << endl; |
| cs->tag = -1; |
| } |
| else { |
| StyleBaseImpl *root = this; |
| DocumentImpl *doc = 0; |
| while (root->parent()) |
| root = root->parent(); |
| if (root->isCSSStyleSheet()) |
| doc = static_cast<CSSStyleSheetImpl*>(root)->doc(); |
| |
| if ( doc ) { |
| if (doc->isHTMLDocument()) |
| tag = tag.lower(); |
| const DOMString dtag(tag); |
| cs->tag = doc->tagId(0, dtag.implementation(), false); |
| } |
| else { |
| cs->tag = khtml::getTagID(tag.lower().ascii(), tag.length()); |
| // this case should never happen - only when loading |
| // the default stylesheet - which must not contain unknown tags |
| assert(cs->tag); |
| } |
| if (!cs->tag) { |
| delete cs; |
| return 0; |
| } |
| } |
| } |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "[Selector: tag=" << cs->tag << " Attribute=" << cs->attr << " match=" << (int)cs->match << " value=" << cs->value.string() << " specificity=" << cs->specificity() << "]" << endl; |
| #endif |
| |
| |
| //stack->print(); |
| if( endVal ) { |
| // lets be recursive |
| relation = CSSSelector::SubSelector; |
| CSSSelector *stack = parseSelector2(endVal, endP, relation); |
| cs->tagHistory = stack; |
| cs->relation = relation; |
| } |
| |
| return cs; |
| } |
| |
| CSSSelector * |
| StyleBaseImpl::parseSelector1(const QChar *curP, const QChar *endP) |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "selector1 is \'" << QString(curP, endP-curP) << "\'" << endl; |
| #endif |
| |
| CSSSelector *selecStack=0; |
| |
| curP = parseSpace(curP, endP); |
| if (!curP) |
| return(0); |
| |
| CSSSelector::Relation relation = CSSSelector::Descendant; |
| |
| const QChar *startP = curP; |
| while (curP && curP <= endP) |
| { |
| if ((curP == endP) || curP->isSpace() || *curP == '+' || *curP == '>') |
| { |
| CSSSelector *newsel = parseSelector2(startP, curP, relation); |
| if (!newsel) { |
| delete selecStack; |
| return 0; |
| } |
| CSSSelector *end = newsel; |
| while( end->tagHistory ) |
| end = end->tagHistory; |
| end->tagHistory = selecStack; |
| end->relation = relation; |
| selecStack = newsel; |
| |
| curP = parseSpace(curP, endP); |
| if (!curP) { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "selector stack is:" << endl; |
| selecStack->print(); |
| kdDebug( 6080 ) << endl; |
| #endif |
| return(selecStack); |
| } |
| relation = CSSSelector::Descendant; |
| if(*curP == '+') |
| { |
| relation = CSSSelector::Sibling; |
| curP++; |
| curP = parseSpace(curP, endP); |
| } |
| else if(*curP == '>') |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "child selector" << endl; |
| #endif |
| relation = CSSSelector::Child; |
| curP++; |
| curP = parseSpace(curP, endP); |
| } |
| //if(selecStack) |
| // selecStack->print(); |
| startP = curP; |
| } |
| else |
| { |
| curP++; |
| } |
| } |
| #ifdef CSS_DEBUG |
| selecStack->print(); |
| #endif |
| return(selecStack); |
| } |
| |
| QPtrList<CSSSelector> * |
| StyleBaseImpl::parseSelector(const QChar *curP, const QChar *endP) |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "selector is \'" << QString(curP, endP-curP) << "\'" << endl; |
| #endif |
| |
| QPtrList<CSSSelector> *slist = 0; |
| const QChar *startP; |
| |
| while (curP < endP) |
| { |
| startP = curP; |
| curP = parseToChar(curP, endP, ',', false); |
| if (!curP) |
| curP = endP; |
| |
| CSSSelector *selector = parseSelector1(startP, curP); |
| if (selector) |
| { |
| if (!slist) |
| { |
| slist = new QPtrList<CSSSelector>; |
| slist->setAutoDelete(true); |
| } |
| slist->append(selector); |
| } |
| else |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "invalid selector" << endl; |
| #endif |
| // invalid selector, delete |
| delete slist; |
| return 0; |
| } |
| curP++; |
| } |
| return slist; |
| } |
| |
| |
| void StyleBaseImpl::parseProperty(const QChar *curP, const QChar *endP) |
| { |
| m_bnonCSSHint = false; |
| m_bImportant = false; |
| // Get rid of space in front of the declaration |
| |
| curP = parseSpace(curP, endP); |
| if (!curP) |
| return; |
| |
| // Search for the required colon or white space |
| const QChar *colon = parseToChar(curP, endP, ':', true); |
| if (!colon) |
| return; |
| |
| const QString propName( curP, colon - curP ); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "Property-name = \"" << propName << "\"" << endl; |
| #endif |
| |
| // May have only reached white space before |
| if (*colon != ':') |
| { |
| // Search for the required colon |
| colon = parseToChar(curP, endP, ':', false); |
| if (!colon) |
| return; |
| } |
| curP = colon+1; |
| // remove space in front of the value |
| while(curP < endP && *curP == ' ') |
| curP++; |
| if ( curP >= endP ) |
| return; |
| |
| // search for !important |
| const QChar *exclam = parseToChar(curP, endP, '!', false); |
| if(exclam) |
| { |
| //const QChar *imp = parseSpace(exclam+1, endP); |
| QString s(exclam+1, endP - exclam - 1); |
| s = s.stripWhiteSpace(); |
| s = s.lower(); |
| if(s != "important") |
| return; |
| m_bImportant = true; |
| endP = exclam; |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "important property!" << endl; |
| #endif |
| } |
| |
| // remove space after the value; |
| while (endP > curP) |
| { |
| //if (!isspace(*(endP-1))) |
| if (!((endP-1)->isSpace())) |
| break; |
| endP--; |
| } |
| |
| #ifdef CSS_DEBUG |
| QString propVal( curP , endP - curP ); |
| kdDebug( 6080 ) << "Property-value = \"" << propVal.latin1() << "\"" << endl; |
| #endif |
| |
| const struct props *propPtr = findProp(propName.lower().ascii(), propName.length()); |
| if (!propPtr) |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "Unknown property" << propName << endl; |
| #endif |
| return; |
| } |
| |
| unsigned int numProps = m_propList->count(); |
| if(!parseValue(curP, endP, propPtr->id)) { |
| #ifdef CSS_DEBUG |
| kdDebug(6080) << "invalid property, removing added properties from m_propList" << endl; |
| #endif |
| while(m_propList->count() > numProps) |
| m_propList->removeLast(); |
| } |
| } |
| |
| QPtrList<CSSProperty> *StyleBaseImpl::parseProperties(const QChar *curP, const QChar *endP) |
| { |
| m_propList = new QPtrList<CSSProperty>; |
| m_propList->setAutoDelete(true); |
| while (curP < endP) |
| { |
| const QChar *startP = curP; |
| curP = parseToChar(curP, endP, ';', false); |
| if (!curP) |
| curP = endP; |
| |
| #ifdef CSS_DEBUG |
| QString propVal( startP , curP - startP ); |
| kdDebug( 6080 ) << "Property = \"" << propVal.latin1() << "\"" << endl; |
| #endif |
| |
| parseProperty(startP, curP); |
| curP++; |
| } |
| if(!m_propList->isEmpty()) { |
| return m_propList; |
| } else { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "empty property list" << endl; |
| #endif |
| delete m_propList; |
| return 0; |
| } |
| } |
| |
| static const QChar *getNext( const QChar *curP, const QChar *endP, bool &last ) |
| { |
| last = false; |
| const QChar *nextP = curP; |
| bool ignoreSpace = false; |
| while(nextP < endP) { |
| if ( *nextP == '(' ) { |
| ignoreSpace = true; |
| } else if ( *nextP == ')' ) { |
| ignoreSpace = false; |
| } |
| if ( *nextP == ' ' && !ignoreSpace ) |
| return nextP; |
| if ( *nextP == ';') |
| break; |
| nextP++; |
| } |
| last = true; |
| return nextP; |
| } |
| // ------------------- begin font property --------------------- |
| /* |
| Parser for the font property of CSS. See |
| http://www.w3.org/TR/REC-CSS2/fonts.html#propdef-font for details. |
| |
| Written by Jasmin Blanchette (jasmin@trolltech.com) on 2000-08-16. |
| */ |
| |
| class FontParser { |
| public: |
| enum { TOK_NONE, TOK_EOI, TOK_SLASH, TOK_COMMA, TOK_STRING, TOK_SYMBOL }; |
| |
| QChar m_yyChar; |
| QString m_yyIn, m_yyStr; |
| unsigned int m_yyPos; |
| int m_yyTok; |
| bool strictParsing; |
| |
| int getChar() { |
| return ( m_yyPos == m_yyIn.length() ) ? QChar('\0') : QChar(m_yyIn[m_yyPos++]); |
| } |
| |
| void startTokenizer( const QString& str, bool _strictParsing ) { |
| m_yyIn = str.simplifyWhiteSpace(); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "startTokenizer: [" << m_yyIn << "]" << endl; |
| #endif |
| m_yyPos = 0; |
| m_yyChar = getChar(); |
| strictParsing = _strictParsing; |
| m_yyTok = TOK_NONE; |
| } |
| |
| int getToken() |
| { |
| m_yyStr = QString::null; |
| |
| if ( m_yyChar == '\0' ) |
| return TOK_EOI; |
| if ( m_yyChar == QChar(' ') ) |
| m_yyChar = getChar(); |
| |
| if ( m_yyChar == QChar('/') ) { |
| m_yyChar = getChar(); |
| return TOK_SLASH; |
| } else if ( m_yyChar == QChar(',') ) { |
| m_yyChar = getChar(); |
| return TOK_COMMA; |
| } else if ( m_yyChar == QChar('"') ) { |
| m_yyChar = getChar(); |
| while ( m_yyChar != QChar('"') && m_yyChar != '\0' ) { |
| m_yyStr += m_yyChar; |
| m_yyChar = getChar(); |
| } |
| m_yyChar = getChar(); |
| return TOK_STRING; |
| } else if ( m_yyChar == QChar('\'') ) { |
| m_yyChar = getChar(); |
| while ( m_yyChar != QChar('\'') && m_yyChar != '\0' ) { |
| m_yyStr += m_yyChar; |
| m_yyChar = getChar(); |
| } |
| m_yyChar = getChar(); |
| return TOK_STRING; |
| } else { |
| while ( m_yyChar != '/' && m_yyChar != ',' && m_yyChar != '\0' && m_yyChar != ' ') { |
| m_yyStr += m_yyChar; |
| m_yyChar = getChar(); |
| } |
| return TOK_SYMBOL; |
| } |
| } |
| |
| bool match( int tok ) |
| { |
| if ( m_yyTok == tok ) { |
| m_yyTok = getToken(); |
| return true; |
| } |
| return false; |
| } |
| |
| bool matchFontStyle( QString *fstyle ) |
| { |
| if ( m_yyTok == TOK_SYMBOL ) { |
| const struct css_value *cssval = findValue(m_yyStr.latin1(), m_yyStr.length()); |
| if (cssval) { |
| int id = cssval->id; |
| if ( id == CSS_VAL_NORMAL || id == CSS_VAL_ITALIC || |
| id == CSS_VAL_OBLIQUE || id == CSS_VAL_INHERIT ) { |
| *fstyle = m_yyStr; |
| m_yyTok = getToken(); |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool matchFontVariant( QString *fvariant ) |
| { |
| if ( m_yyTok == TOK_SYMBOL ) { |
| const struct css_value *cssval = findValue(m_yyStr.latin1(), m_yyStr.length()); |
| if (cssval) { |
| int id = cssval->id; |
| if (id == CSS_VAL_NORMAL || id == CSS_VAL_SMALL_CAPS || id == CSS_VAL_INHERIT) { |
| *fvariant = m_yyStr; |
| m_yyTok = getToken(); |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool matchFontWeight( QString *fweight ) |
| { |
| if ( m_yyTok == TOK_SYMBOL ) { |
| const struct css_value *cssval = findValue(m_yyStr.latin1(), m_yyStr.length()); |
| if (cssval) { |
| int id = cssval->id; |
| if ((id >= CSS_VAL_NORMAL && id <= CSS_VAL_900) || id == CSS_VAL_INHERIT ) { |
| *fweight = m_yyStr; |
| m_yyTok = getToken(); |
| return true; |
| } |
| } |
| } |
| return false ; |
| } |
| |
| bool matchFontSize( QString *fsize ) |
| { |
| if ( m_yyTok == TOK_SYMBOL ) { |
| *fsize = m_yyStr; |
| m_yyTok = getToken(); |
| return true; |
| } |
| return false; |
| } |
| |
| bool matchLineHeight( QString *lheight ) |
| { |
| if ( m_yyTok == TOK_SYMBOL ) { |
| *lheight = m_yyStr; |
| m_yyTok = getToken(); |
| return true; |
| } |
| return false; |
| } |
| |
| bool matchNameFamily( QString *ffamily ) |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "matchNameFamily: [" << *ffamily << "]" << endl; |
| #endif |
| bool matched = false; |
| if ( m_yyTok == TOK_SYMBOL || ( m_yyTok == TOK_STRING && !strictParsing ) ) { |
| // accept quoted "serif" only in non strict mode. |
| *ffamily = m_yyStr; |
| // unquoted courier new should return courier new |
| while( (m_yyTok = getToken()) == TOK_SYMBOL ) { |
| *ffamily += " " + m_yyStr; |
| } |
| matched = true; |
| } else if ( m_yyTok == TOK_STRING ) { |
| // kdDebug( 6080 ) << "[" << m_yyStr << "]" << endl; |
| const struct css_value *cssval = findValue(m_yyStr.latin1(), m_yyStr.length()); |
| if (!cssval || !(cssval->id >= CSS_VAL_SERIF && cssval->id <= CSS_VAL_MONOSPACE)) { |
| *ffamily = m_yyStr; |
| m_yyTok = getToken(); |
| matched = true; |
| } |
| } |
| return matched; |
| } |
| |
| bool matchFontFamily( QString *ffamily ) |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "matchFontFamily: [" << *ffamily << "]" << endl; |
| #endif |
| QStringList t; |
| if ( !matchFontFamily( &t ) ) |
| return false; |
| |
| *ffamily = t.join(", "); |
| return TRUE; |
| } |
| |
| bool matchFontFamily ( QStringList *ffamily ) |
| { |
| if ( m_yyTok == TOK_NONE ) |
| m_yyTok = getToken(); |
| #if 0 |
| // ### |
| if ( m_yyTok == TOK_STRING && m_yyStr == "inherit" ) { |
| ffamily->clear(); |
| m_yyTok = getToken(); |
| return TRUE; |
| } |
| #endif |
| |
| QString name; |
| do { |
| if ( !matchNameFamily(&name) ) |
| return FALSE; |
| ffamily->append( name ); |
| } while ( match(TOK_COMMA) ); |
| |
| return true; |
| } |
| |
| bool matchRealFont( QString *fstyle, QString *fvariant, QString *fweight, |
| QString *fsize, QString *lheight, QString *ffamily ) |
| { |
| bool metFstyle = matchFontStyle( fstyle ); |
| bool metFvariant = matchFontVariant( fvariant ); |
| matchFontWeight( fweight ); |
| if ( !metFstyle ) |
| metFstyle = matchFontStyle( fstyle ); |
| if ( !metFvariant ) |
| matchFontVariant( fvariant ); |
| if ( !metFstyle ) |
| matchFontStyle( fstyle ); |
| |
| if ( !matchFontSize(fsize) ) |
| return FALSE; |
| if ( match(TOK_SLASH) ) { |
| if ( !matchLineHeight(lheight) ) |
| return FALSE; |
| } |
| if ( !matchFontFamily(ffamily) ) |
| return FALSE; |
| return true; |
| } |
| }; |
| |
| bool StyleBaseImpl::parseFont(const QChar *curP, const QChar *endP) |
| { |
| QString str( curP, endP - curP ); |
| QString fstyle, fvariant, fweight, fsize, lheight, ffamily; |
| |
| FontParser fontParser; |
| fontParser.startTokenizer( str, strictParsing ); |
| |
| //kdDebug( 6080 ) << str << endl; |
| const struct css_value *cssval = findValue(fontParser.m_yyIn.latin1(), fontParser.m_yyIn.length()); |
| |
| if (cssval) { |
| //kdDebug( 6080 ) << "System fonts requested: [" << str << "]" << endl; |
| QFont sysFont; |
| switch (cssval->id) { |
| case CSS_VAL_MENU: |
| sysFont = KGlobalSettings::menuFont(); |
| break; |
| case CSS_VAL_CAPTION: |
| sysFont = KGlobalSettings::windowTitleFont(); |
| break; |
| case CSS_VAL_STATUS_BAR: |
| case CSS_VAL_ICON: |
| case CSS_VAL_MESSAGE_BOX: |
| case CSS_VAL_SMALL_CAPTION: |
| default: |
| sysFont = KGlobalSettings::generalFont(); |
| break; |
| } |
| if (sysFont.italic()) { |
| fstyle = "italic"; |
| } else { |
| fstyle = "normal"; |
| } |
| if (sysFont.bold()) { |
| fweight = "bold"; |
| } else { |
| fweight = "normal"; |
| } |
| fsize.sprintf("%dpx", sysFont.pixelSize()); |
| ffamily = sysFont.family(); |
| |
| } else { |
| fontParser.m_yyTok = fontParser.getToken(); |
| if (!(fontParser.matchRealFont(&fstyle, &fvariant, &fweight, &fsize, &lheight, &ffamily))) |
| { |
| return false; |
| } |
| } |
| // kdDebug(6080) << "[" << fstyle << "] [" << fvariant << "] [" << fweight << "] [" |
| // << fsize << "] / [" << lheight << "] [" << ffamily << "]" << endl; |
| |
| if(!fstyle.isNull()) |
| parseValue(fstyle.unicode(), fstyle.unicode()+fstyle.length(), CSS_PROP_FONT_STYLE); |
| if(!fvariant.isNull()) |
| parseValue(fvariant.unicode(), fvariant.unicode()+fvariant.length(), CSS_PROP_FONT_VARIANT); |
| if(!fweight.isNull()) |
| parseValue(fweight.unicode(), fweight.unicode()+fweight.length(), CSS_PROP_FONT_WEIGHT); |
| if(!fsize.isNull()) |
| parseValue(fsize.unicode(), fsize.unicode()+fsize.length(), CSS_PROP_FONT_SIZE); |
| if(!lheight.isNull()) |
| parseValue(lheight.unicode(), lheight.unicode()+lheight.length(), CSS_PROP_LINE_HEIGHT); |
| if(!ffamily.isNull()) |
| parseValue(ffamily.unicode(), ffamily.unicode()+ffamily.length(), CSS_PROP_FONT_FAMILY); |
| return true; |
| } |
| |
| // ---------------- end font property -------------------------- |
| |
| bool StyleBaseImpl::parseValue( const QChar *curP, const QChar *endP, int propId, |
| bool important, bool nonCSSHint, QPtrList<CSSProperty> *propList) |
| { |
| m_bImportant = important; |
| m_bnonCSSHint = nonCSSHint; |
| m_propList = propList; |
| return parseValue(curP, endP, propId); |
| } |
| |
| bool StyleBaseImpl::parseValue( const QChar *curP, const QChar *endP, int propId) |
| { |
| if (curP==endP) {return 0; /* e.g.: width="" */} |
| |
| QString value(curP, endP - curP); |
| value = value.lower().stripWhiteSpace(); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "id [" << getPropertyName(propId).string() << "] parseValue [" << value << "]" << endl; |
| #endif |
| int len = value.length(); |
| const char *val = value.latin1(); |
| |
| CSSValueImpl *parsedValue = 0; |
| |
| // We are using this so often |
| const struct css_value *cssval = findValue(val, len); |
| if (cssval && cssval->id == CSS_VAL_INHERIT) { |
| parsedValue = new CSSInheritedValueImpl(); // inherited value |
| } else { |
| switch(propId) |
| { |
| /* The comment to the left defines all valid value of this properties as defined |
| * in CSS 2, Appendix F. Property index |
| */ |
| |
| /* All the CSS properties are not supported by the renderer at the moment. |
| * Note that all the CSS2 Aural properties are only checked, if CSS_AURAL is defined |
| * (see parseAuralValues). As we don't support them at all this seems reasonable. |
| */ |
| |
| case CSS_PROP_SIZE: // <length>{1,2} | auto | portrait | landscape | inherit |
| case CSS_PROP_QUOTES: // [<string> <string>]+ | none | inherit |
| case CSS_PROP_TEXT_SHADOW: // none | [<color> || <length> <length> <length>? ,]* |
| // [<color> || <length> <length> <length>?] | inherit |
| |
| case CSS_PROP_UNICODE_BIDI: // normal | embed | bidi-override | inherit |
| case CSS_PROP_WHITE_SPACE: // normal | pre | nowrap | inherit |
| case CSS_PROP_FONT_STRETCH: |
| // normal | wider | narrower | ultra-condensed | extra-condensed | condensed | |
| // semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded | |
| // inherit |
| case CSS_PROP_PAGE: // <identifier> | auto // ### CHECK |
| case CSS_PROP_PAGE_BREAK_AFTER: // auto | always | avoid | left | right | inherit |
| case CSS_PROP_PAGE_BREAK_BEFORE: // auto | always | avoid | left | right | inherit |
| case CSS_PROP_PAGE_BREAK_INSIDE: // avoid | auto | inherit |
| case CSS_PROP_POSITION: // static | relative | absolute | fixed | inherit |
| case CSS_PROP_EMPTY_CELLS: // show | hide | inherit |
| case CSS_PROP_TABLE_LAYOUT: // auto | fixed | inherit |
| { |
| const struct css_value *cssval = findValue(val, len); |
| if (cssval) |
| { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } |
| // ### To be done |
| break; |
| } |
| |
| case CSS_PROP_CONTENT: // [ <string> | <uri> | <counter> | attr(X) | open-quote | |
| // close-quote | no-open-quote | no-close-quote ]+ | inherit |
| { |
| parsedValue = parseContent(curP,endP); |
| } |
| |
| case CSS_PROP__KONQ_JS_CLIP: |
| case CSS_PROP_CLIP: // <shape> | auto | inherit |
| { |
| int i; |
| if ( cssval && cssval->id == CSS_VAL_AUTO ) |
| parsedValue = new CSSPrimitiveValueImpl( cssval->id ); |
| else { |
| // only shape in CSS2 is rect( top right bottom left ) |
| QString str = QConstString( const_cast<QChar*>( curP ), endP - curP ).string(); |
| // the CSS specs are not really clear if there should be commas in here or not. We accept both spaces and commas. |
| str.replace( QRegExp( "," ), " " ); |
| str = str.simplifyWhiteSpace(); |
| if ( str.find( "rect", 0, false ) != 0 ) |
| break; |
| int pos = str.find( '(', 4 ); |
| int end = str.findRev( ')' ); |
| if ( end <= pos ) |
| break; |
| str = str.mid( pos + 1, end - pos - 1 ); |
| str = str.simplifyWhiteSpace(); |
| str += " "; |
| //qDebug("rect = '%s'", str.latin1() ); |
| |
| pos = 0; |
| RectImpl *rect = new RectImpl(); |
| for ( i = 0; i < 4; i++ ) { |
| int space; |
| space = str.find( ' ', pos ); |
| const QChar *start = str.unicode() + pos; |
| const QChar *end = str.unicode() + space; |
| //qDebug("part: from=%d, to=%d", pos, space ); |
| if ( start >= end ) |
| goto cleanup; |
| CSSPrimitiveValueImpl *length = 0; |
| if ( str.find( "auto", pos, FALSE ) == pos ) |
| length = new CSSPrimitiveValueImpl( CSS_VAL_AUTO ); |
| else |
| length = parseUnit( start, end, LENGTH ); |
| if ( !length ) |
| goto cleanup; |
| switch ( i ) { |
| case 0: |
| rect->setTop( length ); |
| break; |
| case 1: |
| rect->setRight( length ); |
| break; |
| case 2: |
| rect->setBottom( length ); |
| break; |
| case 3: |
| rect->setLeft( length ); |
| break; |
| } |
| pos = space + 1; |
| } |
| parsedValue = new CSSPrimitiveValueImpl( rect ); |
| //qDebug(" passed rectangle parsing"); |
| break; |
| |
| cleanup: |
| //qDebug(" rectangle parsing failed, i=%d", i); |
| delete rect; |
| } |
| break; |
| } |
| |
| /* Start of supported CSS properties with validation. This is needed for parseShortHand to work |
| * correctly and allows optimization in khtml::applyRule(..) |
| */ |
| case CSS_PROP_CAPTION_SIDE: // top | bottom | left | right | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if (id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || id == CSS_VAL_TOP || id == CSS_VAL_BOTTOM) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_BORDER_COLLAPSE: // collapse | separate | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if ( id == CSS_VAL_COLLAPSE || id == CSS_VAL_SEPARATE ) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_VISIBILITY: // visible | hidden | collapse | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if (id == CSS_VAL_VISIBLE || id == CSS_VAL_HIDDEN || id == CSS_VAL_COLLAPSE) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_OVERFLOW: // visible | hidden | scroll | auto | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if ( id == CSS_VAL_VISIBLE || id == CSS_VAL_HIDDEN || id == CSS_VAL_SCROLL || id == CSS_VAL_AUTO ) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_LIST_STYLE_POSITION: // inside | outside | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if ( id == CSS_VAL_INSIDE || id == CSS_VAL_OUTSIDE ) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_LIST_STYLE_TYPE: |
| // disc | circle | square | decimal | decimal-leading-zero | lower-roman | |
| // upper-roman | lower-greek | lower-alpha | lower-latin | upper-alpha | |
| // upper-latin | hebrew | armenian | georgian | cjk-ideographic | hiragana | |
| // katakana | hiragana-iroha | katakana-iroha | none | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if ((id >= CSS_VAL_DISC && id <= CSS_VAL_KATAKANA_IROHA) || id == CSS_VAL_NONE) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_DISPLAY: |
| // inline | block | list-item | run-in | compact | -konq-ruler | marker | table | |
| // inline-table | table-row-group | table-header-group | table-footer-group | table-row | |
| // table-column-group | table-column | table-cell | table-caption | none | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if ((id >= CSS_VAL_INLINE && id <= CSS_VAL_TABLE_CAPTION) || id == CSS_VAL_NONE) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_DIRECTION: // ltr | rtl | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if ( id == CSS_VAL_LTR || id == CSS_VAL_RTL ) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_TEXT_TRANSFORM: // capitalize | uppercase | lowercase | none | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if ((id >= CSS_VAL_CAPITALIZE && id <= CSS_VAL_LOWERCASE) || id == CSS_VAL_NONE) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_FLOAT: // left | right | none | inherit + center for buggy CSS |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if (id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT |
| || id == CSS_VAL_NONE || id == CSS_VAL_CENTER) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_CLEAR: // none | left | right | both | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if (id == CSS_VAL_NONE || id == CSS_VAL_LEFT |
| || id == CSS_VAL_RIGHT|| id == CSS_VAL_BOTH) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_TEXT_ALIGN: |
| // left | right | center | justify | konq_center | <string> | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if (id >= CSS_VAL__KONQ_AUTO && id <= CSS_VAL__KONQ_CENTER) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| break; |
| } |
| } else { |
| parsedValue = new CSSPrimitiveValueImpl(DOMString(curP, endP - curP), |
| CSSPrimitiveValue::CSS_STRING); |
| } |
| break; |
| } |
| case CSS_PROP_OUTLINE_STYLE: // <border-style> | inherit |
| case CSS_PROP_BORDER_TOP_STYLE: //// <border-style> | inherit |
| case CSS_PROP_BORDER_RIGHT_STYLE: // Defined as: none | hidden | dotted | dashed | |
| case CSS_PROP_BORDER_BOTTOM_STYLE: // solid | double | groove | ridge | inset | outset |
| case CSS_PROP_BORDER_LEFT_STYLE: //// |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if (id >= CSS_VAL_NONE && id <= CSS_VAL_RIDGE) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_FONT_WEIGHT: // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | |
| // 500 | 600 | 700 | 800 | 900 | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if (id) { |
| if (id >= CSS_VAL_NORMAL && id <= CSS_VAL_LIGHTER) { |
| // Allready correct id |
| } else if (id >= CSS_VAL_100 && id <= CSS_VAL_500) { |
| id = CSS_VAL_NORMAL; |
| } else if (id >= CSS_VAL_600 && id <= CSS_VAL_900) { |
| id = CSS_VAL_BOLD; |
| } |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_BACKGROUND_REPEAT: // repeat | repeat-x | repeat-y | no-repeat | inherit |
| { |
| #ifdef CSS_DEBUG_BCKGR |
| kdDebug( 6080 ) << "CSS_PROP_BACKGROUND_REPEAT: " << val << endl; |
| #endif |
| if (cssval) { |
| int id = cssval->id; |
| if ( id >= CSS_VAL_REPEAT && id <= CSS_VAL_NO_REPEAT ) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_BACKGROUND_ATTACHMENT: // scroll | fixed |
| { |
| #ifdef CSS_DEBUG_BCKGR |
| kdDebug( 6080 ) << "CSS_PROP_BACKGROUND_ATTACHEMENT: " << val << endl; |
| #endif |
| if (cssval) { |
| int id = cssval->id; |
| if ( id == CSS_VAL_SCROLL || id == CSS_VAL_FIXED ) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_BACKGROUND_POSITION: |
| { |
| #ifdef CSS_DEBUG_BCKGR |
| kdDebug( 6080 ) << "CSS_PROP_BACKGROUND_POSITION: " << val << endl; |
| #endif |
| /* Problem: center is ambigous |
| * In case of 'center' center defines X and Y coords |
| * In case of 'center top', center defines the Y coord |
| * in case of 'center left', center defines the X coord |
| */ |
| bool isLast; |
| const QChar* nextP = getNext(curP, endP, isLast); |
| QConstString property1(const_cast<QChar*>( curP ), nextP - curP); |
| const struct css_value *cssval1 = findValue( property1.string().ascii(), |
| property1.string().length()); |
| if ( !cssval1 ) { |
| int properties[2] = { CSS_PROP_BACKGROUND_POSITION_X, |
| CSS_PROP_BACKGROUND_POSITION_Y }; |
| return parseShortHand(curP, endP, properties, 2); |
| } |
| const struct css_value *cssval2 = 0; |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "prop 1: [" << property1.string() << "]" << " isLast: " << isLast << endl; |
| #endif |
| if ( !isLast) { |
| curP = nextP+1; |
| nextP = getNext(curP, endP, isLast); |
| QConstString property2(const_cast<QChar*>( curP ), nextP - curP); |
| cssval2 = findValue( property2.string().ascii(), property2.string().length()); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "prop 2: [" << property2.string() << "]" << " isLast: " << isLast << endl; |
| #endif |
| } |
| |
| int valX = -1; |
| int valY = -1; |
| int id1 = cssval1 ? cssval1->id : -1; |
| int id2 = cssval2 ? cssval2->id : CSS_VAL_CENTER; |
| // id1 will influence X and id2 will influence Y |
| if ( id2 == CSS_VAL_LEFT || id2 == CSS_VAL_RIGHT || |
| id1 == CSS_VAL_TOP || id1 == CSS_VAL_BOTTOM) { |
| int h = id1; id1 = id2; id2 = h; |
| } |
| |
| switch( id1 ) { |
| case CSS_VAL_LEFT: valX = 0; break; |
| case CSS_VAL_CENTER: valX = 50; break; |
| case CSS_VAL_RIGHT: valX = 100; break; |
| default: break; |
| } |
| |
| switch ( id2 ) { |
| case CSS_VAL_TOP: valY = 0; break; |
| case CSS_VAL_CENTER: valY = 50; break; |
| case CSS_VAL_BOTTOM: valY = 100; break; |
| default: break; |
| } |
| |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "valX: " << valX << " valY: " << valY << endl; |
| #endif |
| /* CSS 14.2 |
| * Keywords cannot be combined with percentage values or length values. |
| * -> No mix between keywords and other units. |
| */ |
| if (valX !=-1 && valY !=-1) { |
| setParsedValue( CSS_PROP_BACKGROUND_POSITION_X, |
| new CSSPrimitiveValueImpl(valX, CSSPrimitiveValue::CSS_PERCENTAGE)); |
| setParsedValue( CSS_PROP_BACKGROUND_POSITION_Y, |
| new CSSPrimitiveValueImpl(valY, CSSPrimitiveValue::CSS_PERCENTAGE)); |
| return true; |
| } |
| break; |
| } |
| case CSS_PROP_BACKGROUND_POSITION_X: |
| case CSS_PROP_BACKGROUND_POSITION_Y: |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "CSS_PROP_BACKGROUND_POSITION_{X|Y}: " << val << endl; |
| #endif |
| parsedValue = parseUnit(curP, endP, PERCENT | NUMBER | LENGTH); |
| break; |
| } |
| case CSS_PROP_BORDER_SPACING: |
| { |
| // ### should be able to have two values |
| parsedValue = parseUnit(curP, endP, LENGTH | NONNEGATIVE); |
| break; |
| } |
| case CSS_PROP_OUTLINE_COLOR: // <color> | invert | inherit |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "CSS_PROP_OUTLINE_COLOR: " << val << endl; |
| #endif |
| // outline has "invert" as additional keyword. we handle |
| // it as invalid color and add a special case during rendering |
| if (cssval && cssval->id == CSS_VAL_INVERT) { |
| parsedValue = new CSSPrimitiveValueImpl( QColor() ); |
| break; |
| } |
| // Break is explictly missing, looking for <color> |
| } |
| case CSS_PROP_BACKGROUND_COLOR: // <color> | transparent | inherit |
| { |
| #ifdef CSS_DEBUG_BCKGR |
| kdDebug( 6080 ) << "CSS_PROP_BACKGROUND_COLOR: " << val << endl; |
| #endif |
| if (cssval && cssval->id == CSS_VAL_TRANSPARENT) { |
| parsedValue = new CSSPrimitiveValueImpl( QColor() ); |
| break; |
| } |
| // Break is explictly missing, looking for <color> |
| } |
| case CSS_PROP_COLOR: // <color> | inherit |
| case CSS_PROP_BORDER_TOP_COLOR: // <color> | inherit |
| case CSS_PROP_BORDER_RIGHT_COLOR: // <color> | inherit |
| case CSS_PROP_BORDER_BOTTOM_COLOR: // <color> | inherit |
| case CSS_PROP_BORDER_LEFT_COLOR: // <color> | inherit |
| case CSS_PROP_TEXT_DECORATION_COLOR: // |
| case CSS_PROP_SCROLLBAR_FACE_COLOR: // IE5.5 |
| case CSS_PROP_SCROLLBAR_SHADOW_COLOR: // IE5.5 |
| case CSS_PROP_SCROLLBAR_HIGHLIGHT_COLOR: // IE5.5 |
| case CSS_PROP_SCROLLBAR_3DLIGHT_COLOR: // IE5.5 |
| case CSS_PROP_SCROLLBAR_DARKSHADOW_COLOR: // IE5.5 |
| case CSS_PROP_SCROLLBAR_TRACK_COLOR: // IE5.5 |
| case CSS_PROP_SCROLLBAR_ARROW_COLOR: // IE5.5 |
| { |
| const QString val2( value.stripWhiteSpace() ); |
| //kdDebug(6080) << "parsing color " << val2 << endl; |
| QColor c; |
| khtml::setNamedColor(c, val2); |
| if(!c.isValid() && (val2 != "transparent" ) && !val2.isEmpty() ) return false; |
| //kdDebug( 6080 ) << "color is: " << c.red() << ", " << c.green() << ", " << c.blue() << endl; |
| parsedValue = new CSSPrimitiveValueImpl(c); |
| break; |
| } |
| case CSS_PROP_BACKGROUND_IMAGE: // <uri> | none | inherit |
| #ifdef CSS_DEBUG_BCKGR |
| { |
| kdDebug( 6080 ) << "CSS_PROP_BACKGROUND_IMAGE: " << val << endl; |
| } |
| #endif |
| case CSS_PROP_CURSOR: |
| // [ [<uri> ,]* [ auto | crosshair | default | pointer | move | e-resize | ne-resize | |
| // nw-resize | // n-resize | se-resize | sw-resize | s-resize | w-resize | text | |
| // wait | help ] ] | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if (id >= CSS_VAL_AUTO && id <= CSS_VAL_HELP) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| break; |
| } |
| } else { |
| // Break is explictly missing, looking for <uri> |
| // ### Only supports parsing the first uri |
| } |
| } |
| case CSS_PROP_LIST_STYLE_IMAGE: // <uri> | none | inherit |
| { |
| if (cssval && cssval->id == CSS_VAL_NONE) |
| { |
| parsedValue = new CSSImageValueImpl(); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "empty image " << endl; |
| #endif |
| } else { |
| const QString str(value.stripWhiteSpace()); // ### Optimize |
| if (str.left(4).lower() == "url(") { |
| DOMString value(curP, endP - curP); |
| value = khtml::parseURL(value); |
| if (!value.isEmpty()) |
| parsedValue = new CSSImageValueImpl(DOMString(KURL(baseURL().string(), value.string()).url()), this); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "image, url=" << value.string() << " base=" << baseURL().string() << endl; |
| #endif |
| } |
| } |
| break; |
| } |
| case CSS_PROP_OUTLINE_WIDTH: // <border-width> | inherit |
| case CSS_PROP_BORDER_TOP_WIDTH: //// <border-width> | inherit |
| case CSS_PROP_BORDER_RIGHT_WIDTH: // Which is defined as |
| case CSS_PROP_BORDER_BOTTOM_WIDTH: // thin | medium | thick | <length> |
| case CSS_PROP_BORDER_LEFT_WIDTH: //// |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if (id == CSS_VAL_THIN || id == CSS_VAL_MEDIUM || id == CSS_VAL_THICK) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } else { |
| parsedValue = parseUnit(curP, endP, LENGTH|NONNEGATIVE); |
| } |
| break; |
| } |
| case CSS_PROP_MARKER_OFFSET: // <length> | auto | inherit |
| { |
| if (cssval && cssval->id == CSS_VAL_AUTO) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } else { |
| parsedValue = parseUnit(curP, endP, LENGTH); |
| } |
| break; |
| } |
| case CSS_PROP_LETTER_SPACING: // normal | <length> | inherit |
| case CSS_PROP_WORD_SPACING: // normal | <length> | inherit |
| { |
| if (cssval) { |
| if (cssval->id == CSS_VAL_NORMAL) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } |
| } else { |
| parsedValue = parseUnit(curP, endP, LENGTH); |
| } |
| break; |
| } |
| case CSS_PROP_PADDING_TOP: //// <padding-width> | inherit |
| case CSS_PROP_PADDING_RIGHT: // Which is defined as |
| case CSS_PROP_PADDING_BOTTOM: // <length> | <percentage> |
| case CSS_PROP_PADDING_LEFT: //// |
| { |
| parsedValue = parseUnit(curP, endP, LENGTH | PERCENT|NONNEGATIVE); |
| break; |
| } |
| case CSS_PROP_TEXT_INDENT: // <length> | <percentage> | inherit |
| case CSS_PROP_MIN_HEIGHT: // <length> | <percentage> | inherit |
| case CSS_PROP_MIN_WIDTH: // <length> | <percentage> | inherit |
| { |
| parsedValue = parseUnit(curP, endP, LENGTH | PERCENT); |
| break; |
| } |
| case CSS_PROP_FONT_SIZE: |
| // <absolute-size> | <relative-size> | <length> | <percentage> | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if (id >= CSS_VAL_XX_SMALL && id <= CSS_VAL_LARGER) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| break; |
| } |
| } else { |
| parsedValue = parseUnit(curP, endP, LENGTH | PERCENT | NONNEGATIVE); |
| } |
| break; |
| } |
| case CSS_PROP_FONT_STYLE: // normal | italic | oblique | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if ( id == CSS_VAL_NORMAL || id == CSS_VAL_ITALIC || id == CSS_VAL_OBLIQUE) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_FONT_VARIANT: // normal | small-caps | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if ( id == CSS_VAL_NORMAL || id == CSS_VAL_SMALL_CAPS) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } |
| break; |
| } |
| case CSS_PROP_VERTICAL_ALIGN: |
| // baseline | sub | super | top | text-top | middle | bottom | text-bottom | |
| // <percentage> | <length> | inherit |
| { |
| if (cssval) { |
| int id = cssval->id; |
| if ( id >= CSS_VAL_BASELINE && id <= CSS_VAL__KONQ_BASELINE_MIDDLE ) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } else { |
| parsedValue = parseUnit(curP, endP, LENGTH | PERCENT ); |
| } |
| break; |
| } |
| case CSS_PROP_MAX_HEIGHT: // <length> | <percentage> | none | inherit |
| case CSS_PROP_MAX_WIDTH: // <length> | <percentage> | none | inherit |
| { |
| if (cssval && cssval->id == CSS_VAL_NONE) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } else { |
| parsedValue = parseUnit(curP, endP, LENGTH | PERCENT ); |
| } |
| break; |
| } |
| case CSS_PROP_HEIGHT: // <length> | <percentage> | auto | inherit |
| case CSS_PROP_WIDTH: // <length> | <percentage> | auto | inherit |
| { |
| if (cssval && cssval->id == CSS_VAL_AUTO) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } else { |
| parsedValue = parseUnit(curP, endP, LENGTH | PERCENT | NONNEGATIVE ); |
| } |
| break; |
| } |
| case CSS_PROP_BOTTOM: // <length> | <percentage> | auto | inherit |
| case CSS_PROP_LEFT: // <length> | <percentage> | auto | inherit |
| case CSS_PROP_RIGHT: // <length> | <percentage> | auto | inherit |
| case CSS_PROP_TOP: // <length> | <percentage> | auto | inherit |
| case CSS_PROP_MARGIN_TOP: //// <margin-width> | inherit |
| case CSS_PROP_MARGIN_RIGHT: // Which is defined as |
| case CSS_PROP_MARGIN_BOTTOM: // <length> | <percentage> | auto | inherit |
| case CSS_PROP_MARGIN_LEFT: //// |
| { |
| if (cssval && cssval->id == CSS_VAL_AUTO) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } else { |
| parsedValue = parseUnit(curP, endP, LENGTH | PERCENT ); |
| } |
| break; |
| } |
| case CSS_PROP_FONT_SIZE_ADJUST: // <number> | none | inherit |
| // ### not supported later on |
| { |
| if (cssval && cssval->id == CSS_VAL_NONE) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } else { |
| parsedValue = parseUnit(curP, endP, NUMBER); |
| } |
| break; |
| } |
| case CSS_PROP_Z_INDEX: // auto | <integer> | inherit |
| { |
| if (cssval && cssval->id == CSS_VAL_AUTO) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| break; |
| } |
| // break explicitly missing, looking for <number> |
| } |
| case CSS_PROP_ORPHANS: // <integer> | inherit |
| case CSS_PROP_WIDOWS: // <integer> | inherit |
| // ### not supported later on |
| { |
| parsedValue = parseUnit(curP, endP, INTEGER); |
| break; |
| } |
| case CSS_PROP_LINE_HEIGHT: // normal | <number> | <length> | <percentage> | inherit |
| { |
| if (cssval && cssval->id == CSS_VAL_NORMAL) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } else { |
| parsedValue = parseUnit(curP, endP, NUMBER | LENGTH | PERCENT | NONNEGATIVE); |
| } |
| break; |
| } |
| case CSS_PROP_COUNTER_INCREMENT: // [ <identifier> <integer>? ]+ | none | inherit |
| case CSS_PROP_COUNTER_RESET: // [ <identifier> <integer>? ]+ | none | inherit |
| { |
| if (cssval && cssval->id == CSS_VAL_NONE) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } else { |
| CSSValueListImpl *list = new CSSValueListImpl; |
| int pos=0, pos2; |
| while( 1 ) |
| { |
| pos2 = value.find(',', pos); |
| QString face = value.mid(pos, pos2-pos); |
| face = face.stripWhiteSpace(); |
| if(face.length() == 0) break; |
| // ### single quoted is missing... |
| if(face[0] == '\"') face.remove(0, 1); |
| if(face[face.length()-1] == '\"') face = face.left(face.length()-1); |
| //kdDebug( 6080 ) << "found face '" << face << "'" << endl; |
| list->append(new CSSPrimitiveValueImpl(DOMString(face), CSSPrimitiveValue::CSS_STRING)); |
| pos = pos2 + 1; |
| if(pos2 == -1) break; |
| } |
| //kdDebug( 6080 ) << "got " << list->length() << " faces" << endl; |
| if(list->length()) |
| parsedValue = list; |
| else |
| delete list; |
| break; |
| } |
| } |
| case CSS_PROP_FONT_FAMILY: |
| // [[ <family-name> | <generic-family> ],]* [<family-name> | <generic-family>] | inherit |
| { |
| // css2 compatible parsing... |
| FontParser fp; |
| fp.startTokenizer( value, strictParsing ); |
| QStringList families; |
| if ( !fp.matchFontFamily( &families ) ) |
| return false; |
| CSSValueListImpl *list = new CSSValueListImpl; |
| for ( QStringList::Iterator it = families.begin(); it != families.end(); ++it ) { |
| if( *it != QString::null ) { |
| list->append( new FontFamilyValueImpl(*it) ); |
| //kdDebug() << "StyleBaseImpl::parsefont: family='" << *it << "'" << endl; |
| } |
| } |
| //kdDebug( 6080 ) << "got " << list->length() << " faces" << endl; |
| if(list->length()) |
| parsedValue = list; |
| else |
| delete list; |
| break; |
| } |
| case CSS_PROP_TEXT_DECORATION: |
| // none | [ underline || overline || line-through || blink ] | inherit |
| { |
| if (cssval && cssval->id == CSS_VAL_NONE) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } else { |
| CSSValueListImpl *list = new CSSValueListImpl; |
| value.simplifyWhiteSpace(); |
| //kdDebug( 6080 ) << "text-decoration: '" << value << "'" << endl; |
| int pos=0, pos2; |
| while( 1 ) |
| { |
| pos2 = value.find(' ', pos); |
| QString decoration = value.mid(pos, pos2-pos); |
| decoration = decoration.stripWhiteSpace(); |
| //kdDebug( 6080 ) << "found decoration '" << decoration << "'" << endl; |
| const struct css_value *cssval = findValue(decoration.lower().ascii(), |
| decoration.length()); |
| if (cssval) { |
| list->append(new CSSPrimitiveValueImpl(cssval->id)); |
| } |
| pos = pos2 + 1; |
| if(pos2 == -1) break; |
| } |
| //kdDebug( 6080 ) << "got " << list->length() << "d decorations" << endl; |
| if(list->length()) { |
| parsedValue = list; |
| } else { |
| delete list; |
| } |
| } |
| break; |
| } |
| case CSS_PROP__KONQ_FLOW_MODE: |
| { |
| if (cssval->id==CSS_VAL__KONQ_NORMAL || cssval->id==CSS_VAL__KONQ_AROUND_FLOATS) |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| break; |
| } |
| /* shorthand properties */ |
| case CSS_PROP_BACKGROUND: |
| // ['background-color' || 'background-image' ||'background-repeat' || |
| // 'background-attachment' || 'background-position'] | inherit |
| { |
| #ifdef CSS_DEBUG_BCKGR |
| kdDebug(6080) << "CSS_PROP_BACKGROUND" << endl; |
| #endif |
| const int properties[5] = { CSS_PROP_BACKGROUND_IMAGE, CSS_PROP_BACKGROUND_REPEAT, |
| CSS_PROP_BACKGROUND_ATTACHMENT, CSS_PROP_BACKGROUND_POSITION, |
| CSS_PROP_BACKGROUND_COLOR }; |
| return parseShortHand(curP, endP, properties, 5); |
| |
| //return parseBackground(curP, endP); |
| } |
| case CSS_PROP_BORDER: |
| // [ 'border-width' || 'border-style' || <color> ] | inherit |
| { |
| const int properties[3] = { CSS_PROP_BORDER_WIDTH, CSS_PROP_BORDER_STYLE, |
| CSS_PROP_BORDER_COLOR }; |
| return parseShortHand(curP, endP, properties, 3); |
| } |
| case CSS_PROP_BORDER_TOP: |
| // [ 'border-top-width' || 'border-style' || <color> ] | inherit |
| { |
| const int properties[3] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_TOP_STYLE, |
| CSS_PROP_BORDER_TOP_COLOR}; |
| return parseShortHand(curP, endP, properties, 3); |
| } |
| case CSS_PROP_BORDER_RIGHT: |
| // [ 'border-right-width' || 'border-style' || <color> ] | inherit |
| { |
| const int properties[3] = { CSS_PROP_BORDER_RIGHT_WIDTH, CSS_PROP_BORDER_RIGHT_STYLE, |
| CSS_PROP_BORDER_RIGHT_COLOR }; |
| return parseShortHand(curP, endP, properties, 3); |
| } |
| case CSS_PROP_BORDER_BOTTOM: |
| // [ 'border-bottom-width' || 'border-style' || <color> ] | inherit |
| { |
| const int properties[3] = { CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_BOTTOM_STYLE, |
| CSS_PROP_BORDER_BOTTOM_COLOR }; |
| return parseShortHand(curP, endP, properties, 3); |
| } |
| case CSS_PROP_BORDER_LEFT: |
| // [ 'border-left-width' || 'border-style' || <color> ] | inherit |
| { |
| const int properties[3] = { CSS_PROP_BORDER_LEFT_WIDTH, CSS_PROP_BORDER_LEFT_STYLE, |
| CSS_PROP_BORDER_LEFT_COLOR }; |
| return parseShortHand(curP, endP, properties, 3); |
| } |
| case CSS_PROP_OUTLINE: |
| // [ 'outline-color' || 'outline-style' || 'outline-width' ] | inherit |
| { |
| const int properties[3] = { CSS_PROP_OUTLINE_WIDTH, CSS_PROP_OUTLINE_STYLE, |
| CSS_PROP_OUTLINE_COLOR }; |
| return parseShortHand(curP, endP, properties, 3); |
| } |
| case CSS_PROP_BORDER_COLOR: |
| // <color>{1,4} | transparent | inherit |
| { |
| const struct css_value *cssval = findValue(val, len); |
| if (cssval && cssval->id == CSS_VAL_TRANSPARENT) |
| { |
| // set border colors to invalid |
| parsedValue = new CSSPrimitiveValueImpl(CSS_VAL_TRANSPARENT); |
| break; |
| } |
| const int properties[4] = { CSS_PROP_BORDER_TOP_COLOR, CSS_PROP_BORDER_RIGHT_COLOR, |
| CSS_PROP_BORDER_BOTTOM_COLOR, CSS_PROP_BORDER_LEFT_COLOR }; |
| return parse4Values(curP, endP, properties); |
| } |
| case CSS_PROP_BORDER_WIDTH: |
| // <border-width>{1,4} | inherit |
| { |
| const int properties[4] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_RIGHT_WIDTH, |
| CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_LEFT_WIDTH }; |
| return parse4Values(curP, endP, properties); |
| } |
| case CSS_PROP_BORDER_STYLE: |
| // <border-style>{1,4} | inherit |
| { |
| const int properties[4] = { CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_RIGHT_STYLE, |
| CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_LEFT_STYLE }; |
| return parse4Values(curP, endP, properties); |
| } |
| case CSS_PROP_MARGIN: |
| // <margin-width>{1,4} | inherit |
| { |
| const int properties[4] = { CSS_PROP_MARGIN_TOP, CSS_PROP_MARGIN_RIGHT, |
| CSS_PROP_MARGIN_BOTTOM, CSS_PROP_MARGIN_LEFT }; |
| return parse4Values(curP, endP, properties); |
| } |
| case CSS_PROP_PADDING: |
| // <padding-width>{1,4} | inherit |
| { |
| const int properties[4] = { CSS_PROP_PADDING_TOP, CSS_PROP_PADDING_RIGHT, |
| CSS_PROP_PADDING_BOTTOM, CSS_PROP_PADDING_LEFT }; |
| return parse4Values(curP, endP, properties); |
| } |
| case CSS_PROP_FONT: |
| // [ [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? |
| // 'font-family' ] | caption | icon | menu | message-box | small-caption | status-bar | inherit |
| { |
| return parseFont(curP, endP); |
| } |
| case CSS_PROP_LIST_STYLE: |
| { |
| const int properties[3] = { CSS_PROP_LIST_STYLE_TYPE, CSS_PROP_LIST_STYLE_POSITION, |
| CSS_PROP_LIST_STYLE_IMAGE }; |
| return parseShortHand(curP, endP, properties, 3); |
| } |
| |
| default: |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "illegal or CSS2 Aural property: " << val << endl; |
| #endif |
| } |
| } |
| } |
| if ( parsedValue ) { |
| setParsedValue(propId, parsedValue); |
| return true; |
| } else { |
| #ifndef CSS_AURAL |
| return false; |
| #endif |
| #ifdef CSS_AURAL |
| return parseAuralValue(curP, endP, propId); |
| #endif |
| } |
| } |
| |
| #ifdef CSS_AURAL |
| /* parseAuralValue */ |
| bool StyleBaseImpl::parseAuralValue( const QChar *curP, const QChar *endP, int propId ) |
| { |
| QString value(curP, endP - curP); |
| value = value.lower(); |
| const char *val = value.ascii(); |
| |
| CSSValueImpl *parsedValue = 0; |
| kdDebug( 6080 ) << "parseAuralValue: " << value << " val: " << val << endl; |
| |
| /* AURAL Properies */ |
| switch(propId) |
| { |
| case CSS_PROP_AZIMUTH: // <angle> | [[ left-side | far-left | left | center-left | center | |
| // center-right | right | far-right | right-side ] || behind ] | |
| // leftwards | rightwards | inherit |
| case CSS_PROP_PAUSE_AFTER: // <time> | <percentage> | inherit |
| case CSS_PROP_PAUSE_BEFORE: // <time> | <percentage> | inherit |
| case CSS_PROP_PAUSE: // [ [<time> | <percentage>]{1,2} ] | inherit |
| case CSS_PROP_PLAY_DURING: // <uri> mix? repeat? | auto | none | inherit |
| case CSS_PROP_VOICE_FAMILY: // [[<specific-voice> | <generic-voice> ],]* |
| // [<specific-voice> | <generic-voice> ] | inherit |
| { |
| // ### TO BE DONE |
| break; |
| } |
| case CSS_PROP_CUE: // [ 'cue-before' || 'cue-after' ] | inherit |
| { |
| const int properties[2] = { |
| CSS_PROP_CUE_BEFORE, |
| CSS_PROP_CUE_AFTER}; |
| return parse2Values(curP, endP, properties); |
| } |
| case CSS_PROP_CUE_AFTER: // <uri> | none | inherit |
| case CSS_PROP_CUE_BEFORE: // <uri> | none | inherit |
| { |
| const struct css_value *cssval = findValue(val, endP - curP); |
| if (cssval) { |
| if (cssval->id == CSS_VAL_NONE) { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } |
| } else { |
| DOMString value(curP, endP - curP); |
| value = khtml::parseURL(value); |
| parsedValue = new CSSPrimitiveValueImpl( |
| DOMString(KURL(baseURL(), value).url()), |
| CSSPrimitiveValue::CSS_URI); |
| } |
| break; |
| } |
| case CSS_PROP_ELEVATION: // <angle> | below | level | above | higher | lower |
| // | inherit |
| { |
| const struct css_value *cssval = findValue(val, endP - curP); |
| if (cssval) { |
| int id = cssval->id; |
| if (id == CSS_VAL_BELOW || id == CSS_VAL_LEVEL || id == CSS_VAL_ABOVE || |
| id == CSS_VAL_HIGHER || id == CSS_VAL_LOWER) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| break; |
| } |
| } |
| parsedValue = parseUnit(curP, endP, ANGLE ); |
| break; |
| } |
| case CSS_PROP_SPEECH_RATE: // <number> | x-slow | slow | medium | fast | |
| // x-fast | faster | slower | inherit |
| { |
| const struct css_value *cssval = findValue(val, endP - curP); |
| if (cssval) { |
| int id = cssval->id; |
| if (id == CSS_VAL_X_SLOW || id == CSS_VAL_SLOW || id == CSS_VAL_MEDIUM || |
| id == CSS_VAL_FAST || id == CSS_VAL_X_FAST || id == CSS_VAL_FASTER || |
| id == CSS_VAL_SLOWER) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| break; |
| } |
| } else { |
| parsedValue = parseUnit(curP, endP, NUMBER ); |
| } |
| break; |
| } |
| case CSS_PROP_VOLUME: // <number> | <percentage> | silent | x-soft | soft | |
| // medium | loud | x-loud | inherit |
| { |
| const struct css_value *cssval = findValue(val, endP - curP); |
| if (cssval) { |
| int id = cssval->id; |
| if (id == CSS_VAL_SILENT || id == CSS_VAL_X_SOFT || id == CSS_VAL_SOFT || |
| id == CSS_VAL_MEDIUM || id == CSS_VAL_X_LOUD || id == CSS_VAL_X_LOUD) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } else { |
| parsedValue = parseUnit(curP, endP, PERCENT | NUMBER); |
| } |
| break; |
| } |
| case CSS_PROP_PITCH: // <frequency> | x-low | low | medium | high | x-high | inherit |
| { |
| const struct css_value *cssval = findValue(val, endP - curP); |
| if (cssval) { |
| int id = cssval->id; |
| if (id == CSS_VAL_X_LOW || id == CSS_VAL_LOW || id == CSS_VAL_MEDIUM || |
| id == CSS_VAL_HIGH || id == CSS_VAL_X_HIGH ) { |
| parsedValue = new CSSPrimitiveValueImpl(id); |
| } |
| } else { |
| parsedValue = parseUnit(curP, endP, FREQUENCY); |
| } |
| break; |
| } |
| case CSS_PROP_SPEAK: // normal | none | spell-out | inherit |
| case CSS_PROP_SPEAK_HEADER: // once | always | inherit |
| case CSS_PROP_SPEAK_NUMERAL: // digits | continuous | inherit |
| case CSS_PROP_SPEAK_PUNCTUATION: // code | none | inherit |
| { |
| const struct css_value *cssval = findValue(val, endP - curP); |
| if (cssval) |
| { |
| parsedValue = new CSSPrimitiveValueImpl(cssval->id); |
| } |
| break; |
| } |
| case CSS_PROP_PITCH_RANGE: // <number> | inherit |
| case CSS_PROP_RICHNESS: // <number> | inherit |
| case CSS_PROP_STRESS: // <number> | inherit |
| { |
| parsedValue = parseUnit(curP, endP, NUMBER); |
| break; |
| } |
| default: |
| { |
| kdDebug( 6080 ) << "illegal property: " << val << endl; |
| } |
| } |
| if ( parsedValue ) { |
| setParsedValue( propId, parsedValue ); |
| return true; |
| } |
| return false; |
| } |
| #endif |
| |
| void StyleBaseImpl::setParsedValue(int propId, const CSSValueImpl *parsedValue, |
| bool important, bool nonCSSHint, QPtrList<CSSProperty> *propList) |
| { |
| m_bImportant = important; |
| m_bnonCSSHint = nonCSSHint; |
| m_propList = propList; |
| setParsedValue( propId, parsedValue); |
| } |
| |
| void StyleBaseImpl::setParsedValue( int propId, const CSSValueImpl *parsedValue) |
| { |
| QPtrListIterator<CSSProperty> propIt(*m_propList); |
| propIt.toLast(); // just remove the top one - not sure what should happen if we have multiple instances of the property |
| while (propIt.current() && |
| ( propIt.current()->m_id != propId || propIt.current()->nonCSSHint != m_bnonCSSHint || |
| propIt.current()->m_bImportant != m_bImportant) ) |
| --propIt; |
| if (propIt.current()) |
| m_propList->removeRef(propIt.current()); |
| |
| CSSProperty *prop = new CSSProperty(); |
| prop->m_id = propId; |
| prop->setValue((CSSValueImpl *) parsedValue); |
| prop->m_bImportant = m_bImportant; |
| prop->nonCSSHint = m_bnonCSSHint; |
| |
| m_propList->append(prop); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "added property: " << getPropertyName(propId).string() |
| // non implemented yet << ", value: " << parsedValue->cssText().string() |
| << " important: " << prop->m_bImportant |
| << " nonCSS: " << prop->nonCSSHint << endl; |
| #endif |
| } |
| |
| bool StyleBaseImpl::parseShortHand(const QChar *curP, const QChar *endP, const int *properties, int num) |
| { |
| /* We try to match as many properties as possible |
| * We setup an array of booleans to mark which property has been found, |
| * and we try to search for properties until it makes no longer any sense |
| */ |
| |
| bool isLast = false; |
| bool foundAnything = false; |
| bool fnd[6]; //Trust me ;) |
| for( int i = 0; i < num; i++ ) |
| fnd[i] = false; |
| |
| #ifdef CSS_DEBUG |
| kdDebug(6080) << "PSH: parsing \"" << QString(curP, endP - curP) << "\" num=" << num << endl; |
| #endif |
| |
| for (int j = 0; j < num; ++j) { |
| const QChar *nextP = getNext( curP, endP, isLast ); |
| //kdDebug(6080) << "in PSH: \"" << QString(curP, nextP - curP) << "\"" << endl; |
| foundAnything = false; |
| for (int propIndex = 0; propIndex < num; ++propIndex) { |
| if (!fnd[propIndex]) { |
| // We have to tread this seperately |
| //kdDebug(6080) << "LOOKING FOR: " << getPropertyName(properties[propIndex]).string() << endl; |
| bool found = false; |
| if (!isLast && properties[propIndex] == CSS_PROP_BACKGROUND_POSITION) |
| found = parseBackgroundPosition(curP, nextP, endP); |
| else |
| found = parseValue(curP, nextP, properties[propIndex]); |
| |
| if (found) { |
| fnd[propIndex] = foundAnything = true; |
| #ifdef CSS_DEBUG |
| kdDebug(6080) << "FOUND: " << getPropertyName(properties[propIndex]).string() << ": " |
| << QString(curP, nextP - curP) << endl; |
| #endif |
| break; |
| } |
| } |
| } |
| // if we didn't find at least one match, this is an |
| // invalid shorthand and we have to ignore it |
| if (!foundAnything) |
| return foundAnything; |
| |
| do { |
| nextP++; |
| curP = nextP; |
| |
| // oh, less parameteres than we expected |
| if (curP >= endP) |
| return foundAnything; |
| } while (curP->isSpace()); |
| } |
| return foundAnything; |
| } |
| |
| /* |
| * Problem (again): the ambiguity of 'background-position' |
| * from: http://www.w3.org/Style/CSS/Test/current/sec537.htm |
| |
| BODY {background: green url(oransqr.gif) repeat-x center top fixed;} |
| .one {background: lime url(oransqr.gif) repeat-y 100% 0%;} |
| .two {background: lime url(oransqr.gif) repeat-y center top;} |
| .three {background: lime url(oransqr.gif) repeat-x left top;} |
| */ |
| |
| bool StyleBaseImpl::parseBackgroundPosition(const QChar *curP, const QChar *&nextP, const QChar *endP) |
| { |
| // We first need to check if the property has two values. |
| // if this fails we try one value only. |
| |
| const QChar *bckgrNextP = nextP; |
| while (bckgrNextP->isSpace() && bckgrNextP < endP) { bckgrNextP++; } |
| bool dummy; |
| bckgrNextP = getNext(bckgrNextP, endP, dummy); |
| //kdDebug(6080) << "BKCGR: 2: \"" << QString(curP, bckgrNextP - curP) << "\"" << endl; |
| |
| bool found = parseValue(curP, bckgrNextP, CSS_PROP_BACKGROUND_POSITION); |
| if (!found) { |
| // We have not found a pair of Background-Positions, see if we have a single value |
| |
| //kdDebug(6080) << "BKCGR: Single: \"" << QString(curP, nextP - curP) << "\"" << endl; |
| found = parseValue(curP, nextP, CSS_PROP_BACKGROUND_POSITION); |
| } else { |
| // Moving on |
| nextP = bckgrNextP; |
| } |
| //kdDebug(6080) << "found background property!" << endl; |
| return found; |
| } |
| |
| |
| CSSValueImpl* StyleBaseImpl::parseContent(const QChar *curP, const QChar *endP) |
| { |
| CSSValueListImpl* values = new CSSValueListImpl(); |
| |
| QPtrList<QChar> list = splitContent(curP, endP); |
| for(uint n=0; n<list.count(); n+=2) |
| { |
| QString str(list.at(n), list.at(n+1)-list.at(n)); |
| CSSValueImpl* parsedValue=0; |
| |
| if (str.left(4) == "url(") |
| { |
| // url |
| DOMString value(curP, endP - curP); |
| value = khtml::parseURL(value); |
| parsedValue = new CSSImageValueImpl( |
| DOMString(KURL(baseURL().string(), value.string()).url()), this); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "content, url=" << value.string() << " base=" << baseURL().string() << endl; |
| #endif |
| } |
| else if (str.left(5) == "attr(") |
| { |
| // attr |
| } |
| else if (str.left(8) == "counter(") |
| { |
| // counter |
| } |
| else if (str == "open-quote") |
| { |
| // open-quote |
| } |
| else if (str == "close-quote") |
| { |
| // open-quote |
| } |
| else if (str == "no-open-quote") |
| { |
| // no-open-quote |
| } |
| else if (str == "no-close-quote") |
| { |
| // no-close-quote |
| } |
| else |
| { |
| // string |
| QString str = QConstString( list.at(n), list.at(n+1)-list.at(n)).string(); |
| str = str.replace(QRegExp("\\\\a"),"\n"); |
| parsedValue = new CSSPrimitiveValueImpl(DOMString(str), CSSPrimitiveValue::CSS_STRING); |
| } |
| if (parsedValue) |
| values->append(parsedValue); |
| |
| } |
| return values; |
| } |
| |
| |
| |
| QPtrList<QChar> StyleBaseImpl::splitContent(const QChar *curP, const QChar *endP) |
| { |
| bool last = false; |
| |
| QPtrList<QChar> list; |
| while(!last) { |
| const QChar *nextP = curP; |
| bool q = false; |
| bool dq = false; |
| if(*nextP=='\'') |
| q=true; |
| else if (*nextP=='\"') |
| dq=true; |
| while(!(nextP->isSpace()) || q || dq) { |
| nextP++; |
| if(nextP >= endP){ |
| last = true; |
| break; |
| } |
| if((q&&*nextP=='\'') || (dq&&*nextP=='\"')){ |
| nextP++; |
| if(nextP >= endP) last= true; |
| break; |
| } |
| } |
| |
| list.append(curP+((q||dq)?1:0)); |
| list.append(nextP-((q||dq)?1:0)); |
| |
| if ( last ) break; |
| while(nextP->isSpace()) { // skip over WS between tokens |
| nextP++; |
| curP = nextP; |
| if(curP >= endP) { |
| last = true; |
| break; |
| } |
| } |
| } |
| return list; |
| } |
| |
| |
| QPtrList<QChar> StyleBaseImpl::splitShorthandProperties(const QChar *curP, const QChar *endP) |
| { |
| bool last = false; |
| QPtrList<QChar> list; |
| while(!last) { |
| const QChar *nextP = curP; |
| while(!(nextP->isSpace())) { |
| nextP++; |
| if(nextP >= endP) { |
| last = true; |
| break; |
| } |
| } |
| list.append(curP); |
| list.append(nextP); |
| if ( last ) break; |
| while(nextP->isSpace()) { // skip over WS between tokens |
| nextP++; |
| curP = nextP; |
| if(curP >= endP) { |
| last = true; |
| break; |
| } |
| } |
| } |
| return list; |
| } |
| |
| #ifdef CSS_AURAL |
| // used for shorthand properties xxx{1,2} |
| bool StyleBaseImpl::parse2Values( const QChar *curP, const QChar *endP, const int *properties) |
| { |
| QPtrList<QChar> list = splitShorthandProperties(curP, endP); |
| switch(list.count()) |
| { |
| case 2: |
| if(!parseValue(list.at(0), list.at(1), properties[0])) return false; |
| setParsedValue(properties[1], m_propList->last()->value() ); |
| return true; |
| case 4: |
| if(!parseValue(list.at(0), list.at(1), properties[0])) return false; |
| if(!parseValue(list.at(2), list.at(3), properties[1])) return false; |
| return true; |
| default: |
| return false; |
| } |
| } |
| #endif |
| |
| // used for shorthand properties xxx{1,4} |
| bool StyleBaseImpl::parse4Values( const QChar *curP, const QChar *endP, const int *properties) |
| { |
| /* From the CSS 2 specs, 8.3 |
| * If there is only one value, it applies to all sides. If there are two values, the top and |
| * bottom margins are set to the first value and the right and left margins are set to the second. |
| * If there are three values, the top is set to the first value, the left and right are set to the |
| * second, and the bottom is set to the third. If there are four values, they apply to the top, |
| * right, bottom, and left, respectively. |
| */ |
| |
| QPtrList<QChar> list = splitShorthandProperties(curP, endP); |
| switch(list.count()) |
| { |
| case 2: |
| if(!parseValue(list.at(0), list.at(1), properties[0])) return false; |
| setParsedValue(properties[1], m_propList->last()->value()); |
| setParsedValue(properties[2], m_propList->last()->value()); |
| setParsedValue(properties[3], m_propList->last()->value()); |
| return true; |
| case 4: |
| if(!parseValue(list.at(0), list.at(1), properties[0])) return false; |
| setParsedValue(properties[2], m_propList->last()->value()); |
| if(!parseValue(list.at(2), list.at(3), properties[1])) return false; |
| setParsedValue(properties[3], m_propList->last()->value()); |
| return true; |
| case 6: |
| if(!parseValue(list.at(0), list.at(1), properties[0])) return false; |
| if(!parseValue(list.at(2), list.at(3), properties[1])) return false; |
| setParsedValue(properties[3], m_propList->last()->value()); |
| if(!parseValue(list.at(4), list.at(5), properties[2])) return false; |
| return true; |
| case 8: |
| if(!parseValue(list.at(0), list.at(1), properties[0])) return false; |
| if(!parseValue(list.at(2), list.at(3), properties[1])) return false; |
| if(!parseValue(list.at(4), list.at(5), properties[2])) return false; |
| if(!parseValue(list.at(6), list.at(7), properties[3])) return false; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| CSSPrimitiveValueImpl * |
| StyleBaseImpl::parseUnit(const QChar * curP, const QChar *endP, int allowedUnits) |
| { |
| // Everything should be lowercase -> preprocess |
| //assert(QString(curP, endP-curP).lower()==QString(curP,endP-curP)); |
| |
| if (curP==endP || *curP=='"') {return 0; /* e.g.: width="" length="ffffff" */} |
| |
| endP--; |
| while(*endP == ' ' && endP > curP) endP--; |
| const QChar *split = endP; |
| // splt up number and unit |
| while( (*split < '0' || *split > '9') && *split != '.' && split > curP) |
| split--; |
| split++; |
| |
| QString s(curP, split-curP); |
| |
| bool isInt = false; |
| if(s.find('.') == -1) isInt = true; |
| |
| bool ok; |
| float value = s.toFloat(&ok); |
| if ( !ok || (value < 0 && (allowedUnits & NONNEGATIVE)) ) |
| return 0; |
| |
| if(split > endP) // no unit |
| { |
| if(allowedUnits & NUMBER) |
| return new CSSPrimitiveValueImpl(value, CSSPrimitiveValue::CSS_NUMBER); |
| |
| if(allowedUnits & INTEGER && isInt) // ### DOM CSS doesn't seem to define something for integer |
| return new CSSPrimitiveValueImpl(value, CSSPrimitiveValue::CSS_NUMBER); |
| |
| // ### according to the css specs only 0 is allowed without unit. |
| // there are however too many web pages out there using CSS without units |
| // cause ie and ns allow them. We do so if the document is not using a strict dtd |
| if(( allowedUnits & LENGTH ) && (value == 0 || !strictParsing) ) |
| return new CSSPrimitiveValueImpl(value, CSSPrimitiveValue::CSS_PX); |
| |
| return 0; |
| } |
| |
| CSSPrimitiveValue::UnitTypes type = CSSPrimitiveValue::CSS_UNKNOWN; |
| StyleBaseImpl::Units unit = StyleBaseImpl::UNKNOWN; |
| |
| switch(split->latin1()) |
| { |
| case '%': |
| type = CSSPrimitiveValue::CSS_PERCENTAGE; |
| unit =StyleBaseImpl:: PERCENT; |
| break; |
| case 'e': |
| split++; |
| if(split > endP) break; |
| switch(split->latin1()) |
| { |
| case 'm': |
| type = CSSPrimitiveValue::CSS_EMS; |
| unit = StyleBaseImpl::LENGTH; |
| break; |
| case 'x': |
| type = CSSPrimitiveValue::CSS_EXS; |
| unit = StyleBaseImpl::LENGTH; |
| break; |
| } |
| break; |
| case 'p': |
| split++; |
| if(split > endP) break; |
| switch(split->latin1()) |
| { |
| case 'x': |
| type = CSSPrimitiveValue::CSS_PX; |
| unit = StyleBaseImpl::LENGTH; |
| break; |
| case 't': |
| type = CSSPrimitiveValue::CSS_PT; |
| unit = StyleBaseImpl::LENGTH; |
| break; |
| case 'c': |
| type = CSSPrimitiveValue::CSS_PC; |
| unit = StyleBaseImpl::LENGTH; |
| break; |
| } |
| break; |
| case 'c': |
| split++; |
| if(split > endP) break; |
| if(split->latin1() == 'm') |
| { |
| type = CSSPrimitiveValue::CSS_CM; |
| unit = StyleBaseImpl::LENGTH; |
| } |
| break; |
| case 'm': |
| split++; |
| if(split > endP) break; |
| switch(split->latin1()) |
| { |
| case 'm': |
| type = CSSPrimitiveValue::CSS_MM; |
| unit = StyleBaseImpl::LENGTH; |
| break; |
| case 's': |
| type = CSSPrimitiveValue::CSS_MS; |
| unit = StyleBaseImpl::TIME; |
| break; |
| } |
| break; |
| case 'i': |
| split++; |
| if(split > endP) break; |
| if(split->latin1() == 'n') |
| { |
| type = CSSPrimitiveValue::CSS_IN; |
| unit = StyleBaseImpl::LENGTH; |
| } |
| break; |
| case 'd': |
| type = CSSPrimitiveValue::CSS_DEG; |
| unit = StyleBaseImpl::ANGLE; |
| break; |
| case 'r': |
| type = CSSPrimitiveValue::CSS_RAD; |
| unit = StyleBaseImpl::ANGLE; |
| break; |
| case 'g': |
| type = CSSPrimitiveValue::CSS_GRAD; |
| unit = StyleBaseImpl::ANGLE; |
| break; |
| case 's': |
| type = CSSPrimitiveValue::CSS_S; |
| unit = StyleBaseImpl::TIME; |
| break; |
| case 'h': |
| type = CSSPrimitiveValue::CSS_HZ; |
| unit = StyleBaseImpl::FREQUENCY; |
| break; |
| case 'k': |
| type = CSSPrimitiveValue::CSS_KHZ; |
| unit = StyleBaseImpl::FREQUENCY; |
| break; |
| } |
| |
| if(unit & allowedUnits) |
| { |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "found allowed number " << value << ", unit " << type << endl; |
| #endif |
| return new CSSPrimitiveValueImpl(value, type); |
| } |
| |
| return 0; |
| } |
| |
| CSSRuleImpl * |
| StyleBaseImpl::parseStyleRule(const QChar *&curP, const QChar *endP) |
| { |
| //kdDebug( 6080 ) << "style rule is \'" << QString(curP, endP-curP) << "\'" << endl; |
| |
| const QChar *startP; |
| QPtrList<CSSSelector> *slist; |
| QPtrList<CSSProperty> *plist; |
| |
| startP = curP; |
| curP = parseToChar(startP, endP, '{', false); |
| if (!curP) |
| return(0); |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "selector is \'" << QString(startP, curP-startP) << "\'" << endl; |
| #endif |
| |
| slist = parseSelector(startP, curP ); |
| |
| curP++; // need to get past the '{' from above |
| |
| startP = curP; |
| curP = parseToChar(startP, endP, '}', false); |
| |
| if (!curP) |
| { |
| delete slist; |
| return(0); |
| } |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "rules are \'" << QString(startP, curP-startP) << "\'" << endl; |
| #endif |
| |
| plist = parseProperties(startP, curP ); |
| |
| curP++; // need to get past the '}' from above |
| |
| if (!plist || !slist) |
| { |
| // Useless rule |
| delete slist; |
| delete plist; |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "bad style rule" << endl; |
| #endif |
| return 0; |
| } |
| |
| // return the newly created rule |
| CSSStyleRuleImpl *rule = new CSSStyleRuleImpl(this); |
| CSSStyleDeclarationImpl *decl = new CSSStyleDeclarationImpl(rule, plist); |
| |
| rule->setSelector(slist); |
| rule->setDeclaration(decl); |
| // ### set selector and value |
| return rule; |
| } |
| |
| CSSRuleImpl * |
| StyleBaseImpl::parseRule(const QChar *&curP, const QChar *endP) |
| { |
| const QChar *startP; |
| |
| curP = parseSpace( curP, endP ); |
| |
| if (!strictParsing) { |
| // allow ; between rules (not part of spec) |
| while (curP && (curP->isSpace() || *curP == ';')) |
| curP++; |
| } |
| |
| startP = curP; |
| CSSRuleImpl *rule = 0; |
| |
| if(!curP) return 0; |
| #ifdef CSS_DEBUG |
| kdDebug( 6080 ) << "parse rule: current = " << curP->latin1() << endl; |
| #endif |
| |
| if (*curP == '@' && curP != endP && ( (curP+1)->isLetter() || (curP+1)->unicode() > 0xa0 ) ) |
| { |
| rule = parseAtRule(curP, endP); |
| } |
| else |
| { |
| rule = parseStyleRule(curP, endP); |
| if( rule ) |
| hasInlinedDecl = true; // set flag to true iff we have a valid inlined decl. |
| } |
| |
| if(curP) curP++; |
| return rule; |
| } |
| |
| /* Generate a sort of Normal Form for CSS. |
| * Remove comments, it is guaranteed that there will not be more then one space between |
| * tokens and all the tokens within curly braces are lower case (except text |
| * within quotes and url tags). Space is replaced with QChar(' ') and removed where |
| * it's not necessary. |
| * |
| * 4.1.3 Characters and case |
| * |
| * The following rules always hold: |
| * |
| * All CSS style sheets are case-insensitive, except for parts that are not under |
| * the control of CSS. For example, the case-sensitivity of values of the HTML |
| * attributes "id" and "class", of font names, and of URIs lies outside the scope |
| * of this specification. Note in particular that element names are case-insensitive |
| * in HTML, but case-sensitive in XML. |
| */ |
| |
| const QString StyleBaseImpl::preprocess(const QString &str, bool justOneRule) |
| { |
| // ### use DOMString here to avoid coversions |
| QString processed; |
| |
| bool sq = false; // Within single quote |
| bool dq = false; // Within double quote |
| bool bracket = false; // Within brackets, e.g. url(ThisIsStupid) |
| bool comment = false; // Within comment |
| bool skipgarbage = !justOneRule; // skip <!-- and ---> only in specifc places |
| bool firstChar = false; // Beginning of comment either /* or */ |
| bool space = true; // Last token was space |
| int curlyBracket = 0; // Within curlyBrackets -> lower |
| hasInlinedDecl = false; // reset the inlined decl. flag |
| |
| const QChar *ch = str.unicode(); |
| const QChar *last = ch + str.length(); |
| |
| #ifdef CSS_DEBUG |
| kdDebug(6080) << "---Before---" << endl; |
| kdDebug(6080) << str << endl; |
| float orgLength = str.length(); |
| kdDebug(6080) << "Length: " << orgLength << endl; |
| #endif |
| |
| while(ch < last) { |
| // qDebug("current: *%s*, sq=%d dq=%d b=%d c=%d fC=%d space=%d cB=%d sg=%d", |
| // QConstString(ch, kMin(last-ch, 10)).string().latin1(), sq, dq, bracket, comment, firstChar, space, curlyBracket, skipgarbage); |
| if( !comment && !sq && *ch == '"' ) { |
| dq = !dq; |
| processed += *ch; |
| space = skipgarbage = false; |
| } else if ( !comment && !dq && *ch == '\'') { |
| skipgarbage = sq; |
| sq = !sq; |
| processed += *ch; |
| space = false; |
| } else if ( !comment && !dq && !sq && *ch == '(') { |
| bracket = true; |
| processed += *ch; |
| space = true; // Explictly true |
| skipgarbage = false; |
| } else if ( !comment && !dq && !sq && *ch == ')') { |
| bracket = false; |
| processed += *ch; |
| processed += QChar(' '); // Adding a space after this token |
| space = true; |
| skipgarbage = false; |
| } else if ( !comment && !dq && !sq && *ch == '{') { |
| ++curlyBracket; |
| processed += *ch; |
| space = true; // Explictly true |
| skipgarbage = true; |
| } else if ( !comment && !dq && !sq && *ch == '}') { |
| --curlyBracket; |
| processed += *ch; |
| processed += QChar(' '); // Adding a space after this token |
| space = true; |
| skipgarbage = true; |
| } else if ( !comment && skipgarbage && !dq && !sq && (*ch == '-') && ((ch+2) < last) /* SGML Comment */ |
| && (*(ch+1) == '-') && (*(ch+2) == '>')) { |
| ch += 2; // skip --> |
| } else if ( !comment && skipgarbage && !dq && !sq && (*ch == '<') && ((ch+3) < last) /* SGML Comment */ |
| && (*(ch+1) == '!') && (*(ch+2) == '-') && (*(ch+3) == '-')) { |
| ch += 3; // skip <!-- |
| } else if ( comment ) { |
| if ( firstChar && *ch == '/' ) { |
| comment = false; |
| firstChar = false; |
| skipgarbage = true; |
| } else { |
| firstChar = ( *ch == '*' ); |
| } |
| } else if ( !sq && !dq && !bracket ) { |
| // check for comment |
| if ( firstChar ) { |
| if ( *ch == '*' ) { |
| comment = true; |
| } else { |
| processed += '/'; |
| if (curlyBracket > 0) { |
| processed += ch->lower(); |
| } else { |
| processed += *ch; |
| } |
| space = ch->isSpace(); |
| } |
| firstChar = false; |
| } else if ( *ch == '/' ) { |
| firstChar = true; // Slash added only if next is not '*' |
| } else if ( *ch == ',' || *ch == ';') { |
| processed += *ch; |
| processed += QChar(' '); // Adding a space after these tokens |
| space = true; |
| skipgarbage = true; |
| } else { |
| if (!ch->isSpace()) |
| skipgarbage = false; |
| goto addChar; |
| } |
| } else { |
| skipgarbage = ch->isSpace(); |
| goto addChar; |
| } |
| end: |
| ++ch; |
| } |
| |
| #ifdef CSS_DEBUG |
| kdDebug(6080) << "---After ---" << endl; |
| kdDebug(6080) << "[" << processed << "]" << endl; |
| kdDebug(6080) << "------------" << endl; |
| kdDebug(6080) << "Length: " << processed.length() << ", reduced size by: " |
| << 100.0 - (100.0 * (processed.length()/orgLength)) << "%" << endl; |
| kdDebug(6080) << "------------" << endl; |
| #endif |
| |
| return processed; |
| |
| addChar: |
| if ( !sq && !dq && !bracket ) { |
| if (!(space && ch->isSpace())) { // Don't add more than one space |
| if (ch->isSpace()) { |
| processed += QChar(' '); // Normalize whitespace |
| } else { |
| if (curlyBracket > 0 || justOneRule) { |
| processed += ch->lower(); |
| } else { |
| processed += *ch; |
| } |
| } |
| } |
| space = ch->isSpace(); |
| } else { |
| processed += *ch; // We're within quotes or brackets, leave untouched |
| } |
| goto end; |
| } |
| |
| // ------------------------------------------------------------------------------ |
| |
| StyleListImpl::~StyleListImpl() |
| { |
| StyleBaseImpl *n; |
| |
| if(!m_lstChildren) return; |
| |
| for( n = m_lstChildren->first(); n != 0; n = m_lstChildren->next() ) |
| { |
| n->setParent(0); |
| if(n->deleteMe()) delete n; |
| } |
| delete m_lstChildren; |
| } |
| |
| // -------------------------------------------------------------------------------- |
| |
| void CSSSelector::print(void) |
| { |
| kdDebug( 6080 ) << "[Selector: tag = " << tag << ", attr = \"" << attr << "\", match = \"" << match << "\" value = \"" << value.string().latin1() << "\" relation = " << (int)relation << endl; |
| if ( tagHistory ) |
| tagHistory->print(); |
| } |
| |
| unsigned int CSSSelector::specificity() |
| { |
| if ( nonCSSHint ) |
| return 0; |
| |
| int s = 0; |
| if(tag != -1) s = 1; |
| switch(match) |
| { |
| case Exact: |
| if(attr == ATTR_ID) |
| { |
| s += 0x10000; |
| break; |
| } |
| case Set: |
| case List: |
| case Hyphen: |
| case Pseudo: |
| case Contain: |
| case Begin: |
| case End: |
| s += 0x100; |
| case None: |
| break; |
| } |
| if(tagHistory) |
| s += tagHistory->specificity(); |
| // make sure it doesn't overflow |
| return s & 0xffffff; |
| } |
| |
| bool CSSSelector::operator == ( const CSSSelector &other ) |
| { |
| const CSSSelector *sel1 = this; |
| const CSSSelector *sel2 = &other; |
| |
| while ( sel1 && sel2 ) { |
| if ( sel1->tag != sel2->tag || sel1->attr != sel2->attr || |
| sel1->relation != sel2->relation || sel1->match != sel2->match || |
| sel1->nonCSSHint != sel2->nonCSSHint || |
| sel1->value != sel2->value ) |
| return false; |
| sel1 = sel1->tagHistory; |
| sel2 = sel2->tagHistory; |
| } |
| if ( sel1 || sel2 ) |
| return false; |
| return true; |
| } |
| |
| DOMString CSSSelector::selectorText() const |
| { |
| DOMString str; |
| const CSSSelector* cs = this; |
| if ( cs->tag == -1 && cs->attr == ATTR_ID && cs->match == CSSSelector::Exact ) |
| { |
| str = "#"; |
| str += cs->value; |
| } |
| else if ( cs->tag == -1 && cs->attr == ATTR_CLASS && cs->match == CSSSelector::List ) |
| { |
| str = "."; |
| str += cs->value; |
| } |
| else if ( cs->tag == -1 && cs->match == CSSSelector::Pseudo ) |
| { |
| str = ":"; |
| str += cs->value; |
| } |
| else |
| { |
| if ( cs->tag == -1 ) |
| str = "*"; |
| else |
| str = getTagName( cs->tag ); |
| if ( cs->attr == ATTR_ID && cs->match == CSSSelector::Exact ) |
| { |
| str += "#"; |
| str += cs->value; |
| } |
| else if ( cs->attr == ATTR_CLASS && cs->match == CSSSelector::List ) |
| { |
| str += "."; |
| str += cs->value; |
| } |
| else if ( cs->match == CSSSelector::Pseudo ) |
| { |
| str += ":"; |
| str += cs->value; |
| } |
| // optional attribute |
| if ( cs->attr ) { |
| DOMString attrName = getAttrName( cs->attr ); |
| str += "["; |
| str += attrName; |
| switch (cs->match) { |
| case CSSSelector::Exact: |
| str += "="; |
| break; |
| case CSSSelector::Set: |
| str += " "; /// ## correct? |
| break; |
| case CSSSelector::List: |
| str += "~="; |
| break; |
| case CSSSelector::Hyphen: |
| str += "|="; |
| break; |
| case CSSSelector::Begin: |
| str += "^="; |
| break; |
| case CSSSelector::End: |
| str += "$="; |
| break; |
| case CSSSelector::Contain: |
| str += "*="; |
| break; |
| default: |
| kdWarning(6080) << "Unhandled case in CSSStyleRuleImpl::selectorText : match=" << cs->match << endl; |
| } |
| str += "\""; |
| str += cs->value; |
| str += "\"]"; |
| } |
| } |
| if ( cs->tagHistory ) { |
| DOMString tagHistoryText = cs->tagHistory->selectorText(); |
| if ( cs->relation == Sibling ) |
| str = tagHistoryText + " + " + str; |
| else if ( cs->relation == Child ) |
| str = tagHistoryText + " > " + str; |
| else if ( cs->relation == SubSelector ) |
| str += tagHistoryText; // the ":" is provided by selectorText() |
| else // Descendant |
| str = tagHistoryText + " " + str; |
| } |
| return str; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |