/* * This file is part of the DOM implementation for KDE. * * Copyright (C) 2003 Lars Knoll (knoll@kde.org) * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. * * 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // #define CSS_DEBUG // #define TOKEN_DEBUG #define YYDEBUG 0 #include #include #include #include "cssparser.h" #include "css_valueimpl.h" #include "css_ruleimpl.h" #include "css_stylesheetimpl.h" #include "cssproperties.h" #include "cssvalues.h" #include "misc/helper.h" #include "csshelper.h" using namespace DOM; #include #include // used to promote background: left to left center #define BACKGROUND_SKIP_CENTER( num ) \ if ( !pos_ok[ num ] && expected != 1 ) { \ pos_ok[num] = true; \ pos[num] = 0; \ skip_next = false; \ } ValueList::~ValueList() { unsigned numValues = m_values.size(); for (unsigned i = 0; i < numValues; i++) if (m_values[i].unit == Value::Function) delete m_values[i].function; } namespace { class ShorthandScope { public: ShorthandScope(CSSParser* parser, int propId) : m_parser(parser) { if (!(m_parser->m_inParseShorthand++)) m_parser->m_currentShorthand = propId; } ~ShorthandScope() { if (!(--m_parser->m_inParseShorthand)) m_parser->m_currentShorthand = 0; } private: CSSParser* m_parser; }; } using namespace DOM; #if YYDEBUG > 0 extern int cssyydebug; #endif extern int cssyyparse( void * parser ); CSSParser *CSSParser::currentParser = 0; CSSParser::CSSParser( bool strictParsing ) { #ifdef CSS_DEBUG kdDebug( 6080 ) << "CSSParser::CSSParser this=" << this << endl; #endif strict = strictParsing; parsedProperties = (CSSProperty **) malloc( 32 * sizeof( CSSProperty * ) ); numParsedProperties = 0; maxParsedProperties = 32; data = 0; valueList = 0; rule = 0; id = 0; important = false; nonCSSHint = false; m_inParseShorthand = 0; m_currentShorthand = 0; m_implicitShorthand = false; yy_start = 1; #if YYDEBUG > 0 cssyydebug = 1; #endif } CSSParser::~CSSParser() { if ( numParsedProperties ) clearProperties(); free( parsedProperties ); delete valueList; #ifdef CSS_DEBUG kdDebug( 6080 ) << "CSSParser::~CSSParser this=" << this << endl; #endif free( data ); } unsigned int CSSParser::defaultNamespace() { if (styleElement && styleElement->isCSSStyleSheet()) return static_cast(styleElement)->defaultNamespace(); else return anyNamespace; } void CSSParser::runParser(int length) { data[length-1] = 0; data[length-2] = 0; data[length-3] = ' '; yyTok = -1; block_nesting = 0; yy_hold_char = 0; yyleng = 0; yytext = yy_c_buf_p = data; yy_hold_char = *yy_c_buf_p; CSSParser *old = currentParser; currentParser = this; cssyyparse( this ); currentParser = old; } void CSSParser::parseSheet( CSSStyleSheetImpl *sheet, const DOMString &string ) { styleElement = sheet; int length = string.length() + 3; data = (unsigned short *)malloc( length *sizeof( unsigned short ) ); memcpy( data, string.unicode(), string.length()*sizeof( unsigned short) ); #ifdef CSS_DEBUG kdDebug( 6080 ) << ">>>>>>> start parsing style sheet" << endl; #endif runParser(length); #ifdef CSS_DEBUG kdDebug( 6080 ) << "<<<<<<< done parsing style sheet" << endl; #endif delete rule; rule = 0; } CSSRuleImpl *CSSParser::parseRule( DOM::CSSStyleSheetImpl *sheet, const DOM::DOMString &string ) { styleElement = sheet; const char khtml_rule[] = "@-khtml-rule{"; int length = string.length() + 4 + strlen(khtml_rule); assert( !data ); data = (unsigned short *)malloc( length *sizeof( unsigned short ) ); for ( unsigned int i = 0; i < strlen(khtml_rule); i++ ) data[i] = khtml_rule[i]; memcpy( data + strlen( khtml_rule ), string.unicode(), string.length()*sizeof( unsigned short) ); // qDebug("parse string = '%s'", TQConstString( (const TQChar *)data, length ).string().latin1() ); data[length-4] = '}'; runParser(length); CSSRuleImpl *result = rule; rule = 0; return result; } bool CSSParser::parseValue( DOM::CSSStyleDeclarationImpl *declaration, int _id, const DOM::DOMString &string, bool _important, bool _nonCSSHint ) { #ifdef CSS_DEBUG kdDebug( 6080 ) << "CSSParser::parseValue: id=" << _id << " important=" << _important << " nonCSSHint=" << _nonCSSHint << " value='" << string.string() << "'" << endl; #endif styleElement = declaration->stylesheet(); const char khtml_value[] = "@-khtml-value{"; int length = string.length() + 4 + strlen(khtml_value); assert( !data ); data = (unsigned short *)malloc( length *sizeof( unsigned short ) ); for ( unsigned int i = 0; i < strlen(khtml_value); i++ ) data[i] = khtml_value[i]; memcpy( data + strlen( khtml_value ), string.unicode(), string.length()*sizeof( unsigned short) ); data[length-4] = '}'; // qDebug("parse string = '%s'", TQConstString( (const TQChar *)data, length ).string().latin1() ); id = _id; important = _important; nonCSSHint = _nonCSSHint; runParser(length); delete rule; rule = 0; bool ok = false; if ( numParsedProperties ) { ok = true; for ( int i = 0; i < numParsedProperties; i++ ) { declaration->removeProperty(parsedProperties[i]->m_id, nonCSSHint); declaration->values()->append( parsedProperties[i] ); } numParsedProperties = 0; } return ok; } bool CSSParser::parseDeclaration( DOM::CSSStyleDeclarationImpl *declaration, const DOM::DOMString &string, bool _nonCSSHint ) { #ifdef CSS_DEBUG kdDebug( 6080 ) << "CSSParser::parseDeclaration: nonCSSHint=" << nonCSSHint << " value='" << string.string() << "'" << endl; #endif styleElement = declaration->stylesheet(); const char khtml_decls[] = "@-khtml-decls{"; int length = string.length() + 4 + strlen(khtml_decls); assert( !data ); data = (unsigned short *)malloc( length *sizeof( unsigned short ) ); for ( unsigned int i = 0; i < strlen(khtml_decls); i++ ) data[i] = khtml_decls[i]; memcpy( data + strlen( khtml_decls ), string.unicode(), string.length()*sizeof( unsigned short) ); data[length-4] = '}'; nonCSSHint = _nonCSSHint; runParser(length); delete rule; rule = 0; bool ok = false; if ( numParsedProperties ) { ok = true; for ( int i = 0; i < numParsedProperties; i++ ) { declaration->removeProperty(parsedProperties[i]->m_id, false); declaration->values()->append( parsedProperties[i] ); } numParsedProperties = 0; } return ok; } void CSSParser::addProperty( int propId, CSSValueImpl *value, bool important ) { CSSProperty *prop = new CSSProperty; prop->m_id = propId; prop->setValue( value ); prop->m_important = important; prop->nonCSSHint = nonCSSHint; if ( numParsedProperties >= maxParsedProperties ) { maxParsedProperties += 32; parsedProperties = (CSSProperty **) realloc( parsedProperties, maxParsedProperties*sizeof( CSSProperty * ) ); } parsedProperties[numParsedProperties++] = prop; } CSSStyleDeclarationImpl *CSSParser::createStyleDeclaration( CSSStyleRuleImpl *rule ) { TQPtrList *propList = new TQPtrList; propList->setAutoDelete( true ); for ( int i = 0; i < numParsedProperties; i++ ) propList->append( parsedProperties[i] ); numParsedProperties = 0; return new CSSStyleDeclarationImpl(rule, propList); } void CSSParser::clearProperties() { for ( int i = 0; i < numParsedProperties; i++ ) delete parsedProperties[i]; numParsedProperties = 0; } DOM::DocumentImpl *CSSParser::document() const { const StyleBaseImpl* root = styleElement; DocumentImpl *doc = 0; while (root->parent()) root = root->parent(); if (root->isCSSStyleSheet()) doc = static_cast(root)->doc(); return doc; } // defines units allowed for a certain property, used in parseUnit enum Units { FUnknown = 0x0000, FInteger = 0x0001, FNumber = 0x0002, // Real Numbers FPercent = 0x0004, FLength = 0x0008, FAngle = 0x0010, FTime = 0x0020, FFrequency = 0x0040, FRelative = 0x0100, FNonNeg = 0x0200 }; static bool validUnit( Value *value, int unitflags, bool strict ) { if ( unitflags & FNonNeg && value->fValue < 0 ) return false; bool b = false; switch( value->unit ) { case CSSPrimitiveValue::CSS_NUMBER: b = (unitflags & FNumber); if ( !b && ( (unitflags & FLength) && (value->fValue == 0 || !strict ) ) ) { value->unit = CSSPrimitiveValue::CSS_PX; b = true; } if (!b && (unitflags & FInteger) && value->isInt) b = true; break; case CSSPrimitiveValue::CSS_PERCENTAGE: b = (unitflags & FPercent); break; case Value::Q_EMS: case CSSPrimitiveValue::CSS_EMS: case CSSPrimitiveValue::CSS_EXS: case CSSPrimitiveValue::CSS_PX: case CSSPrimitiveValue::CSS_CM: case CSSPrimitiveValue::CSS_MM: case CSSPrimitiveValue::CSS_IN: case CSSPrimitiveValue::CSS_PT: case CSSPrimitiveValue::CSS_PC: b = (unitflags & FLength); break; case CSSPrimitiveValue::CSS_MS: case CSSPrimitiveValue::CSS_S: b = (unitflags & FTime); break; case CSSPrimitiveValue::CSS_DEG: case CSSPrimitiveValue::CSS_RAD: case CSSPrimitiveValue::CSS_GRAD: case CSSPrimitiveValue::CSS_HZ: case CSSPrimitiveValue::CSS_KHZ: case CSSPrimitiveValue::CSS_DIMENSION: default: break; } return b; } bool CSSParser::parseValue( int propId, bool important ) { if ( !valueList ) return false; Value *value = valueList->current(); if ( !value ) return false; int id = value->id; int num = inShorthand() ? 1 : valueList->size(); if ( id == CSS_VAL_INHERIT ) { if (num != 1) return false; addProperty( propId, new CSSInheritedValueImpl(), important ); return true; } else if (id == CSS_VAL_INITIAL ) { if (num != 1) return false; addProperty(propId, new CSSInitialValueImpl(), important); return true; } bool valid_primitive = false; CSSValueImpl *parsedValue = 0; 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: // {1,2} | auto | portrait | landscape | inherit // case CSS_PROP_PAGE: // | auto // ### CHECK // ### To be done if (id) valid_primitive = true; break; case CSS_PROP_UNICODE_BIDI: // normal | embed | bidi-override | inherit if ( id == CSS_VAL_NORMAL || id == CSS_VAL_EMBED || id == CSS_VAL_BIDI_OVERRIDE ) valid_primitive = true; break; case CSS_PROP_POSITION: // static | relative | absolute | fixed | inherit if ( id == CSS_VAL_STATIC || id == CSS_VAL_RELATIVE || id == CSS_VAL_ABSOLUTE || id == CSS_VAL_FIXED ) valid_primitive = true; break; case CSS_PROP_PAGE_BREAK_AFTER: // auto | always | avoid | left | right | inherit case CSS_PROP_PAGE_BREAK_BEFORE: // auto | always | avoid | left | right | inherit if ( id == CSS_VAL_AUTO || id == CSS_VAL_ALWAYS || id == CSS_VAL_AVOID || id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT ) valid_primitive = true; break; case CSS_PROP_PAGE_BREAK_INSIDE: // avoid | auto | inherit if ( id == CSS_VAL_AUTO || id == CSS_VAL_AVOID ) valid_primitive = true; break; case CSS_PROP_EMPTY_CELLS: // show | hide | inherit if ( id == CSS_VAL_SHOW || id == CSS_VAL_HIDE ) valid_primitive = true; break; case CSS_PROP_QUOTES: // [ ]+ | none | inherit if (id == CSS_VAL_NONE) { valid_primitive = true; } else { QuotesValueImpl *quotes = new QuotesValueImpl; bool is_valid = true; TQString open, close; Value *val=valueList->current(); while (val) { if (val->unit == CSSPrimitiveValue::CSS_STRING) open = qString(val->string); else { is_valid = false; break; } valueList->next(); val=valueList->current(); if (val && val->unit == CSSPrimitiveValue::CSS_STRING) close = qString(val->string); else { is_valid = false; break; } quotes->addLevel(open, close); valueList->next(); val=valueList->current(); } if (is_valid) parsedValue = quotes; else delete quotes; } break; case CSS_PROP_CONTENT: // normal | none | inherit | // [ | | | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ if ( id == CSS_VAL_NORMAL || id == CSS_VAL_NONE) valid_primitive = true; else return parseContent( propId, important ); break; case CSS_PROP_WHITE_SPACE: // normal | pre | nowrap | pre-wrap | pre-line | inherit if ( id == CSS_VAL_NORMAL || id == CSS_VAL_PRE || id == CSS_VAL_PRE_WRAP || id == CSS_VAL_PRE_LINE || id == CSS_VAL_NOWRAP ) valid_primitive = true; break; case CSS_PROP_CLIP: // | auto | inherit if ( id == CSS_VAL_AUTO ) valid_primitive = true; else if ( value->unit == Value::Function ) return parseShape( propId, important ); 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 // Left and right were deprecated in CSS 2.1 and never supported by KHTML if ( /* id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || */ id == CSS_VAL_TOP || id == CSS_VAL_BOTTOM) valid_primitive = true; break; case CSS_PROP_BORDER_COLLAPSE: // collapse | separate | inherit if ( id == CSS_VAL_COLLAPSE || id == CSS_VAL_SEPARATE ) valid_primitive = true; break; case CSS_PROP_VISIBILITY: // visible | hidden | collapse | inherit if (id == CSS_VAL_VISIBLE || id == CSS_VAL_HIDDEN || id == CSS_VAL_COLLAPSE) valid_primitive = true; break; case CSS_PROP_OVERFLOW: // visible | hidden | scroll | auto | marquee | inherit case CSS_PROP_OVERFLOW_X: case CSS_PROP_OVERFLOW_Y: if (id == CSS_VAL_VISIBLE || id == CSS_VAL_HIDDEN || id == CSS_VAL_SCROLL || id == CSS_VAL_AUTO || id == CSS_VAL_MARQUEE) valid_primitive = true; break; case CSS_PROP_LIST_STYLE_POSITION: // inside | outside | inherit if ( id == CSS_VAL_INSIDE || id == CSS_VAL_OUTSIDE ) valid_primitive = true; 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 ((id >= CSS_VAL_DISC && id <= CSS_VAL__KHTML_CLOSE_QUOTE) || id == CSS_VAL_NONE) valid_primitive = true; break; case CSS_PROP_DISPLAY: // inline | block | list-item | run-in | inline-block | -khtml-ruler | 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 ((id >= CSS_VAL_INLINE && id <= CSS_VAL_TABLE_CAPTION) || id == CSS_VAL_NONE) valid_primitive = true; break; case CSS_PROP_DIRECTION: // ltr | rtl | inherit if ( id == CSS_VAL_LTR || id == CSS_VAL_RTL ) valid_primitive = true; break; case CSS_PROP_TEXT_TRANSFORM: // capitalize | uppercase | lowercase | none | inherit if ((id >= CSS_VAL_CAPITALIZE && id <= CSS_VAL_LOWERCASE) || id == CSS_VAL_NONE) valid_primitive = true; break; case CSS_PROP_FLOAT: // left | right | none | khtml_left | khtml_right | inherit + center for buggy CSS if ( id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || id == CSS_VAL__KHTML_LEFT || id == CSS_VAL__KHTML_RIGHT ||id == CSS_VAL_NONE || id == CSS_VAL_CENTER) valid_primitive = true; break; case CSS_PROP_CLEAR: // none | left | right | both | inherit if ( id == CSS_VAL_NONE || id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT|| id == CSS_VAL_BOTH) valid_primitive = true; break; case CSS_PROP_TEXT_ALIGN: // left | right | center | justify | khtml_left | khtml_right | khtml_center | | inherit if ( ( id >= CSS_VAL__KHTML_AUTO && id <= CSS_VAL__KHTML_CENTER ) || value->unit == CSSPrimitiveValue::CSS_STRING ) valid_primitive = true; break; case CSS_PROP_OUTLINE_STYLE: // | inherit case CSS_PROP_BORDER_TOP_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 | -khtml-native case CSS_PROP_BORDER_LEFT_STYLE: //// if (id >= CSS_VAL__KHTML_NATIVE && id <= CSS_VAL_DOUBLE) valid_primitive = true; break; case CSS_PROP_FONT_WEIGHT: // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | // 500 | 600 | 700 | 800 | 900 | inherit if (id >= CSS_VAL_NORMAL && id <= CSS_VAL_900) { // Allready correct id valid_primitive = true; } else if ( validUnit( value, FInteger|FNonNeg, false ) ) { int weight = (int)value->fValue; if ( (weight % 100) ) break; weight /= 100; if ( weight >= 1 && weight <= 9 ) { id = CSS_VAL_100 + weight - 1; valid_primitive = true; } } break; case CSS_PROP_BORDER_SPACING: { const int properties[2] = { CSS_PROP__KHTML_BORDER_HORIZONTAL_SPACING, CSS_PROP__KHTML_BORDER_VERTICAL_SPACING }; if (num == 1) { ShorthandScope scope(this, CSS_PROP_BORDER_SPACING); if (!parseValue(properties[0], important)) return false; CSSValueImpl* value = parsedProperties[numParsedProperties-1]->value(); addProperty(properties[1], value, important); return true; } else if (num == 2) { ShorthandScope scope(this, CSS_PROP_BORDER_SPACING); if (!parseValue(properties[0], important)) return false; if (!parseValue(properties[1], important)) return false; return true; } return false; } case CSS_PROP__KHTML_BORDER_HORIZONTAL_SPACING: case CSS_PROP__KHTML_BORDER_VERTICAL_SPACING: valid_primitive = validUnit(value, FLength|FNonNeg, strict&(!nonCSSHint)); break; 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 case CSS_PROP_SCROLLBAR_BASE_COLOR: // IE5.5 if ( strict ) break; /* nobreak */ case CSS_PROP_OUTLINE_COLOR: // | invert | inherit // outline has "invert" as additional keyword. if ( propId == CSS_PROP_OUTLINE_COLOR && id == CSS_VAL_INVERT ) { valid_primitive = true; break; } /* nobreak */ case CSS_PROP_BACKGROUND_COLOR: // | inherit case CSS_PROP_BORDER_TOP_COLOR: // | inherit case CSS_PROP_BORDER_RIGHT_COLOR: // | inherit case CSS_PROP_BORDER_BOTTOM_COLOR: // | inherit case CSS_PROP_BORDER_LEFT_COLOR: // | inherit case CSS_PROP_COLOR: // | inherit if ( id == CSS_VAL__KHTML_TEXT || id == CSS_VAL_MENU || (id >= CSS_VAL_AQUA && id <= CSS_VAL_WINDOWTEXT ) || id == CSS_VAL_TRANSPARENT || (id >= CSS_VAL_GREY && id < CSS_VAL__KHTML_TEXT && (nonCSSHint|!strict) ) ) { valid_primitive = true; } else { parsedValue = parseColor(); if ( parsedValue ) valueList->next(); } break; case CSS_PROP_CURSOR: // [ auto | crosshair | default | pointer | progress | move | e-resize | ne-resize | // nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize | text | // wait | help ] ] | inherit // MSIE 5 compatibility :/ if ( !strict && id == CSS_VAL_HAND ) { id = CSS_VAL_POINTER; valid_primitive = true; } else if ( id >= CSS_VAL_AUTO && id <= CSS_VAL_HELP ) valid_primitive = true; break; case CSS_PROP_BACKGROUND_ATTACHMENT: case CSS_PROP__KHTML_BACKGROUND_CLIP: case CSS_PROP_BACKGROUND_IMAGE: case CSS_PROP__KHTML_BACKGROUND_ORIGIN: case CSS_PROP_BACKGROUND_POSITION: case CSS_PROP_BACKGROUND_POSITION_X: case CSS_PROP_BACKGROUND_POSITION_Y: case CSS_PROP__KHTML_BACKGROUND_SIZE: case CSS_PROP_BACKGROUND_REPEAT: { CSSValueImpl *val1 = 0, *val2 = 0; int propId1, propId2; if (parseBackgroundProperty(propId, propId1, propId2, val1, val2)) { addProperty(propId1, val1, important); if (val2) addProperty(propId2, val2, important); return true; } return false; } case CSS_PROP_LIST_STYLE_IMAGE: // | none | inherit if (id == CSS_VAL_NONE) { parsedValue = new CSSImageValueImpl(); valueList->next(); } else if (value->unit == CSSPrimitiveValue::CSS_URI ) { // ### allow string in non strict mode? DOMString uri = khtml::parseURL( domString( value->string ) ); if (!uri.isEmpty()) { parsedValue = new CSSImageValueImpl( DOMString(KURL( styleElement->baseURL(), uri.string()).url()), styleElement ); valueList->next(); } } break; case CSS_PROP_OUTLINE_WIDTH: // | inherit case CSS_PROP_BORDER_TOP_WIDTH: //// | inherit case CSS_PROP_BORDER_RIGHT_WIDTH: // Which is defined as case CSS_PROP_BORDER_BOTTOM_WIDTH: // thin | medium | thick | case CSS_PROP_BORDER_LEFT_WIDTH: //// if (id == CSS_VAL_THIN || id == CSS_VAL_MEDIUM || id == CSS_VAL_THICK) valid_primitive = true; else valid_primitive = ( validUnit( value, FLength, strict&(!nonCSSHint) ) ); break; case CSS_PROP_LETTER_SPACING: // normal | | inherit case CSS_PROP_WORD_SPACING: // normal | | inherit if ( id == CSS_VAL_NORMAL ) valid_primitive = true; else valid_primitive = validUnit( value, FLength, strict&(!nonCSSHint) ); break; case CSS_PROP_TEXT_INDENT: // | | inherit valid_primitive = ( !id && validUnit( value, FLength|FPercent, strict&(!nonCSSHint) ) ); break; case CSS_PROP_PADDING_TOP: // | | inherit case CSS_PROP_PADDING_RIGHT: // | inherit case CSS_PROP_PADDING_BOTTOM: // Which is defined as case CSS_PROP_PADDING_LEFT: // | case CSS_PROP__KHTML_PADDING_START: valid_primitive = ( !id && validUnit( value, FLength|FPercent|FNonNeg, strict&(!nonCSSHint) ) ); break; case CSS_PROP_MAX_HEIGHT: // | | none | inherit case CSS_PROP_MAX_WIDTH: // | | none | inherit if ( id == CSS_VAL_NONE ) { valid_primitive = true; break; } /* nobreak */ case CSS_PROP_MIN_HEIGHT: // | | inherit case CSS_PROP_MIN_WIDTH: // | | inherit valid_primitive = ( !id && validUnit( value, FLength|FPercent|FNonNeg, strict&(!nonCSSHint) ) ); break; case CSS_PROP_FONT_SIZE: // | | | | inherit if (id >= CSS_VAL_XX_SMALL && id <= CSS_VAL_LARGER) valid_primitive = true; else valid_primitive = ( validUnit( value, FLength|FPercent, strict&(!nonCSSHint) ) ); break; case CSS_PROP_FONT_STYLE: // normal | italic | oblique | inherit if ( id == CSS_VAL_NORMAL || id == CSS_VAL_ITALIC || id == CSS_VAL_OBLIQUE) valid_primitive = true; break; case CSS_PROP_FONT_VARIANT: // normal | small-caps | inherit if ( id == CSS_VAL_NORMAL || id == CSS_VAL_SMALL_CAPS) valid_primitive = true; break; case CSS_PROP_VERTICAL_ALIGN: // baseline | sub | super | top | text-top | middle | bottom | text-bottom | // | | inherit if ( id >= CSS_VAL_BASELINE && id <= CSS_VAL__KHTML_BASELINE_MIDDLE ) valid_primitive = true; else valid_primitive = ( !id && validUnit( value, FLength|FPercent, strict&(!nonCSSHint) ) ); break; case CSS_PROP_HEIGHT: // | | auto | inherit case CSS_PROP_WIDTH: // | | auto | inherit if ( id == CSS_VAL_AUTO ) valid_primitive = true; else // ### handle multilength case where we allow relative units valid_primitive = ( !id && validUnit( value, FLength|FPercent|FNonNeg, strict&(!nonCSSHint) ) ); break; case CSS_PROP_BOTTOM: // | | auto | inherit case CSS_PROP_LEFT: // | | auto | inherit case CSS_PROP_RIGHT: // | | auto | inherit case CSS_PROP_TOP: // | | auto | inherit case CSS_PROP_MARGIN_TOP: //// | inherit case CSS_PROP_MARGIN_RIGHT: // Which is defined as case CSS_PROP_MARGIN_BOTTOM: // | | auto | inherit case CSS_PROP_MARGIN_LEFT: //// case CSS_PROP__KHTML_MARGIN_START: if ( id == CSS_VAL_AUTO ) valid_primitive = true; else valid_primitive = ( !id && validUnit( value, FLength|FPercent, strict&(!nonCSSHint) ) ); break; case CSS_PROP_Z_INDEX: // auto | | inherit // qDebug("parsing z-index: id=%d, fValue=%f", id, value->fValue ); if ( id == CSS_VAL_AUTO ) { valid_primitive = true; break; } /* nobreak */ case CSS_PROP_ORPHANS: // | inherit case CSS_PROP_WIDOWS: // | inherit // ### not supported later on valid_primitive = ( !id && validUnit( value, FInteger, false ) ); break; case CSS_PROP_LINE_HEIGHT: // normal | | | | inherit if ( id == CSS_VAL_NORMAL ) valid_primitive = true; else valid_primitive = ( !id && validUnit( value, FNumber|FLength|FPercent, strict&(!nonCSSHint) ) ); break; case CSS_PROP_COUNTER_INCREMENT: // [ ? ]+ | none | inherit if ( id == CSS_VAL_NONE ) valid_primitive = true; else return parseCounter(propId, true, important); break; case CSS_PROP_COUNTER_RESET: // [ ? ]+ | none | inherit if ( id == CSS_VAL_NONE ) valid_primitive = true; else return parseCounter(propId, false, important); break; case CSS_PROP_FONT_FAMILY: // [[ | ],]* [ | ] | inherit { parsedValue = parseFontFamily(); break; } case CSS_PROP_TEXT_DECORATION: // none | [ underline || overline || line-through || blink ] | inherit if (id == CSS_VAL_NONE) { valid_primitive = true; } else { CSSValueListImpl *list = new CSSValueListImpl; bool is_valid = true; while( is_valid && value ) { switch ( value->id ) { case CSS_VAL_BLINK: break; case CSS_VAL_UNDERLINE: case CSS_VAL_OVERLINE: case CSS_VAL_LINE_THROUGH: list->append( new CSSPrimitiveValueImpl( value->id ) ); break; default: is_valid = false; } value = valueList->next(); } //kdDebug( 6080 ) << "got " << list->length() << "d decorations" << endl; if(list->length() && is_valid) { parsedValue = list; valueList->next(); } else { delete list; } } break; case CSS_PROP_TABLE_LAYOUT: // auto | fixed | inherit if ( id == CSS_VAL_AUTO || id == CSS_VAL_FIXED ) valid_primitive = true; break; case CSS_PROP__KHTML_FLOW_MODE: if ( id == CSS_VAL__KHTML_NORMAL || id == CSS_VAL__KHTML_AROUND_FLOATS ) valid_primitive = true; break; /* CSS3 properties */ case CSS_PROP_BOX_SIZING: // border-box | content-box | inherit if ( id == CSS_VAL_BORDER_BOX || id == CSS_VAL_CONTENT_BOX ) valid_primitive = true; break; case CSS_PROP_OUTLINE_OFFSET: valid_primitive = validUnit(value, FLength, strict); break; case CSS_PROP_TEXT_SHADOW: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3 if (id == CSS_VAL_NONE) valid_primitive = true; else return parseShadow(propId, important); break; case CSS_PROP_OPACITY: valid_primitive = validUnit(value, FNumber, strict); break; case CSS_PROP__KHTML_USER_INPUT: // none | enabled | disabled | inherit if ( id == CSS_VAL_NONE || id == CSS_VAL_ENABLED || id == CSS_VAL_DISABLED ) valid_primitive = true; // kdDebug(6080) << "CSS_PROP__KHTML_USER_INPUT: " << valid_primitive << endl; break; case CSS_PROP__KHTML_MARQUEE: { const int properties[5] = { CSS_PROP__KHTML_MARQUEE_DIRECTION, CSS_PROP__KHTML_MARQUEE_INCREMENT, CSS_PROP__KHTML_MARQUEE_REPETITION, CSS_PROP__KHTML_MARQUEE_STYLE, CSS_PROP__KHTML_MARQUEE_SPEED }; return parseShortHand(propId, properties, 5, important); } case CSS_PROP__KHTML_MARQUEE_DIRECTION: if (id == CSS_VAL_FORWARDS || id == CSS_VAL_BACKWARDS || id == CSS_VAL_AHEAD || id == CSS_VAL_REVERSE || id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || id == CSS_VAL_DOWN || id == CSS_VAL_UP || id == CSS_VAL_AUTO) valid_primitive = true; break; case CSS_PROP__KHTML_MARQUEE_INCREMENT: if (id == CSS_VAL_SMALL || id == CSS_VAL_LARGE || id == CSS_VAL_MEDIUM) valid_primitive = true; else valid_primitive = validUnit(value, FLength|FPercent, strict&(!nonCSSHint)); break; case CSS_PROP__KHTML_MARQUEE_STYLE: if (id == CSS_VAL_NONE || id == CSS_VAL_SLIDE || id == CSS_VAL_SCROLL || id == CSS_VAL_ALTERNATE || id == CSS_VAL_UNFURL) valid_primitive = true; break; case CSS_PROP__KHTML_MARQUEE_REPETITION: if (id == CSS_VAL_INFINITE) valid_primitive = true; else valid_primitive = validUnit(value, FInteger|FNonNeg, strict&(!nonCSSHint)); break; case CSS_PROP__KHTML_MARQUEE_SPEED: if (id == CSS_VAL_NORMAL || id == CSS_VAL_SLOW || id == CSS_VAL_FAST) valid_primitive = true; else valid_primitive = validUnit(value, FTime|FInteger|FNonNeg, strict&(!nonCSSHint)); break; case CSS_PROP_TEXT_OVERFLOW: // clip | ellipsis if (id == CSS_VAL_CLIP || id == CSS_VAL_ELLIPSIS) valid_primitive = true; break; // End of CSS3 properties /* shorthand properties */ case CSS_PROP_BACKGROUND: // ['background-color' || 'background-image' ||'background-repeat' || // 'background-attachment' || 'background-position'] | inherit return parseBackgroundShorthand(important); case CSS_PROP_BORDER: // [ 'border-width' || 'border-style' || ] | inherit { const int properties[3] = { CSS_PROP_BORDER_WIDTH, CSS_PROP_BORDER_STYLE, CSS_PROP_BORDER_COLOR }; return parseShortHand(propId, properties, 3, important); } case CSS_PROP_BORDER_TOP: // [ 'border-top-width' || 'border-style' || ] | inherit { const int properties[3] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_TOP_COLOR}; return parseShortHand(propId, properties, 3, important); } case CSS_PROP_BORDER_RIGHT: // [ 'border-right-width' || 'border-style' || ] | inherit { const int properties[3] = { CSS_PROP_BORDER_RIGHT_WIDTH, CSS_PROP_BORDER_RIGHT_STYLE, CSS_PROP_BORDER_RIGHT_COLOR }; return parseShortHand(propId, properties, 3, important); } case CSS_PROP_BORDER_BOTTOM: // [ 'border-bottom-width' || 'border-style' || ] | inherit { const int properties[3] = { CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_BOTTOM_COLOR }; return parseShortHand(propId, properties, 3, important); } case CSS_PROP_BORDER_LEFT: // [ 'border-left-width' || 'border-style' || ] | inherit { const int properties[3] = { CSS_PROP_BORDER_LEFT_WIDTH, CSS_PROP_BORDER_LEFT_STYLE, CSS_PROP_BORDER_LEFT_COLOR }; return parseShortHand(propId, properties, 3, important); } 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(propId, properties, 3, important); } case CSS_PROP_BORDER_COLOR: // {1,4} | inherit { 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(propId, properties, important); } case CSS_PROP_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(propId, properties, important); } case CSS_PROP_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(propId, properties, important); } case CSS_PROP_MARGIN: // {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(propId, properties, important); } case CSS_PROP_PADDING: // {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(propId, properties, important); } 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 if ( id >= CSS_VAL_CAPTION && id <= CSS_VAL_STATUS_BAR ) valid_primitive = true; else return parseFont(important); 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(propId, properties, 3, important); } case CSS_PROP_WORD_WRAP: { // normal | break-word if ( id == CSS_VAL_NORMAL || id == CSS_VAL_BREAK_WORD ) valid_primitive = true; break; } default: // #ifdef CSS_DEBUG // kdDebug( 6080 ) << "illegal or CSS2 Aural property: " << val << endl; // #endif break; } if ( valid_primitive ) { if ( id != 0 ) { parsedValue = new CSSPrimitiveValueImpl( id ); } else if ( value->unit == CSSPrimitiveValue::CSS_STRING ) parsedValue = new CSSPrimitiveValueImpl( domString( value->string ), (CSSPrimitiveValue::UnitTypes) value->unit ); else if ( value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ ) { parsedValue = new CSSPrimitiveValueImpl( value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit ); } else if ( value->unit >= Value::Q_EMS ) { parsedValue = new CSSQuirkPrimitiveValueImpl( value->fValue, CSSPrimitiveValue::CSS_EMS ); } valueList->next(); } if ( parsedValue ) { if (!valueList->current() || inShorthand()) { addProperty( propId, parsedValue, important ); return true; } delete parsedValue; } return false; } void CSSParser::addBackgroundValue(CSSValueImpl*& lval, CSSValueImpl* rval) { if (lval) { if (lval->isValueList()) static_cast(lval)->append(rval); else { CSSValueImpl* oldVal = lval; CSSValueListImpl* list = new CSSValueListImpl(); lval = list; list->append(oldVal); list->append(rval); } } else lval = rval; } bool CSSParser::parseBackgroundShorthand(bool important) { // Position must come before color in this array because a plain old "0" is a legal color // in quirks mode but it's usually the X coordinate of a position. // FIXME: Add CSS_PROP__KHTML_BACKGROUND_SIZE to the shorthand. const int numProperties = 7; const int properties[numProperties] = { CSS_PROP_BACKGROUND_IMAGE, CSS_PROP_BACKGROUND_REPEAT, CSS_PROP_BACKGROUND_ATTACHMENT, CSS_PROP_BACKGROUND_POSITION, CSS_PROP__KHTML_BACKGROUND_CLIP, CSS_PROP__KHTML_BACKGROUND_ORIGIN, CSS_PROP_BACKGROUND_COLOR }; ShorthandScope scope(this, CSS_PROP_BACKGROUND); bool parsedProperty[numProperties] = { false }; // compiler will repeat false as necessary CSSValueImpl* values[numProperties] = { 0 }; // compiler will repeat 0 as necessary CSSValueImpl* positionYValue = 0; int i; while (valueList->current()) { Value* val = valueList->current(); if (val->unit == Value::Operator && val->iValue == ',') { // We hit the end. Fill in all remaining values with the initial value. valueList->next(); for (i = 0; i < numProperties; ++i) { if (properties[i] == CSS_PROP_BACKGROUND_COLOR && parsedProperty[i]) // Color is not allowed except as the last item in a list. Reject the entire // property. goto fail; if (!parsedProperty[i] && properties[i] != CSS_PROP_BACKGROUND_COLOR) { addBackgroundValue(values[i], new CSSInitialValueImpl()); if (properties[i] == CSS_PROP_BACKGROUND_POSITION) addBackgroundValue(positionYValue, new CSSInitialValueImpl()); } parsedProperty[i] = false; } if (!valueList->current()) break; } bool found = false; for (i = 0; !found && i < numProperties; ++i) { if (!parsedProperty[i]) { CSSValueImpl *val1 = 0, *val2 = 0; int propId1, propId2; if (parseBackgroundProperty(properties[i], propId1, propId2, val1, val2)) { parsedProperty[i] = found = true; addBackgroundValue(values[i], val1); if (properties[i] == CSS_PROP_BACKGROUND_POSITION) addBackgroundValue(positionYValue, val2); } } } // if we didn't find at least one match, this is an // invalid shorthand and we have to ignore it if (!found) goto fail; } // Fill in any remaining properties with the initial value. for (i = 0; i < numProperties; ++i) { if (!parsedProperty[i]) { addBackgroundValue(values[i], new CSSInitialValueImpl()); if (properties[i] == CSS_PROP_BACKGROUND_POSITION) addBackgroundValue(positionYValue, new CSSInitialValueImpl()); } } // Now add all of the properties we found. for (i = 0; i < numProperties; i++) { if (properties[i] == CSS_PROP_BACKGROUND_POSITION) { addProperty(CSS_PROP_BACKGROUND_POSITION_X, values[i], important); addProperty(CSS_PROP_BACKGROUND_POSITION_Y, positionYValue, important); } else addProperty(properties[i], values[i], important); } return true; fail: for (int k = 0; k < numProperties; k++) delete values[k]; delete positionYValue; return false; } bool CSSParser::parseShortHand(int propId, const int *properties, int numProperties, bool important ) { /* 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 */ ShorthandScope scope(this, propId); bool found = false; bool fnd[6]; //Trust me ;) for( int i = 0; i < numProperties; i++ ) fnd[i] = false; while ( valueList->current() ) { found = false; for (int propIndex = 0; !found && propIndex < numProperties; ++propIndex) { if (!fnd[propIndex]) { if ( parseValue( properties[propIndex], important ) ) { fnd[propIndex] = found = true; } } } // if we didn't find at least one match, this is an // invalid shorthand and we have to ignore it if (!found) return false; } // Fill in any remaining properties with the initial value. m_implicitShorthand = true; for (int i = 0; i < numProperties; ++i) { if (!fnd[i]) addProperty(properties[i], new CSSInitialValueImpl(), important); } m_implicitShorthand = false; return true; } bool CSSParser::parse4Values(int propId, const int *properties, bool important ) { /* 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. */ int num = inShorthand() ? 1 : valueList->size(); //qDebug("parse4Values: num=%d %d", num, valueList->numValues ); ShorthandScope scope(this, propId); // the order is top, right, bottom, left switch (num) { case 1: { if (!parseValue(properties[0], important)) return false; CSSValueImpl *value = parsedProperties[numParsedProperties-1]->value(); m_implicitShorthand = true; addProperty(properties[1], value, important); addProperty(properties[2], value, important); addProperty(properties[3], value, important); m_implicitShorthand = false; break; } case 2: { if (!parseValue(properties[0], important) || !parseValue(properties[1], important)) return false; CSSValueImpl *value = parsedProperties[numParsedProperties-2]->value(); m_implicitShorthand = true; addProperty(properties[2], value, important); value = parsedProperties[numParsedProperties-2]->value(); addProperty(properties[3], value, important); m_implicitShorthand = false; break; } case 3: { if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || !parseValue(properties[2], important)) return false; CSSValueImpl *value = parsedProperties[numParsedProperties-2]->value(); m_implicitShorthand = true; addProperty(properties[3], value, important); m_implicitShorthand = false; break; } case 4: { if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || !parseValue(properties[2], important) || !parseValue(properties[3], important)) return false; break; } default: { return false; } } return true; } // [ | | | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit // in CSS 2.1 this got somewhat reduced: // [ | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit bool CSSParser::parseContent( int propId, bool important ) { CSSValueListImpl* values = new CSSValueListImpl(); bool isValid = true; Value *val; CSSValueImpl *parsedValue = 0; while ( (val = valueList->current()) ) { parsedValue = 0; if ( val->unit == CSSPrimitiveValue::CSS_URI ) { // url DOMString value = khtml::parseURL(domString(val->string)); parsedValue = new CSSImageValueImpl( DOMString(KURL( styleElement->baseURL(), value.string()).url() ), styleElement ); #ifdef CSS_DEBUG kdDebug( 6080 ) << "content, url=" << value.string() << " base=" << styleElement->baseURL().url( ) << endl; #endif } else if ( val->unit == Value::Function ) { // attr( X ) | counter( X [,Y] ) | counters( X, Y, [,Z] ) ValueList *args = val->function->args; TQString fname = qString( val->function->name ).lower(); if (!args) return false; if (fname == "attr(") { if ( args->size() != 1) return false; Value *a = args->current(); parsedValue = new CSSPrimitiveValueImpl(domString(a->string), CSSPrimitiveValue::CSS_ATTR); } else if (fname == "counter(") { parsedValue = parseCounterContent(args, false); if (!parsedValue) return false; } else if (fname == "counters(") { parsedValue = parseCounterContent(args, true); if (!parsedValue) return false; } else return false; } else if ( val->unit == CSSPrimitiveValue::CSS_IDENT ) { // open-quote | close-quote | no-open-quote | no-close-quote if ( val->id == CSS_VAL_OPEN_QUOTE || val->id == CSS_VAL_CLOSE_QUOTE || val->id == CSS_VAL_NO_OPEN_QUOTE || val->id == CSS_VAL_NO_CLOSE_QUOTE ) { parsedValue = new CSSPrimitiveValueImpl(val->id); } } else if ( val->unit == CSSPrimitiveValue::CSS_STRING ) { parsedValue = new CSSPrimitiveValueImpl(domString(val->string), CSSPrimitiveValue::CSS_STRING); } if (parsedValue) values->append(parsedValue); else { isValid = false; break; } valueList->next(); } if ( isValid && values->length() ) { addProperty( propId, values, important ); valueList->next(); return true; } delete values; // also frees any content by deref return false; } CSSValueImpl* CSSParser::parseCounterContent(ValueList *args, bool counters) { if (counters || (args->size() != 1 && args->size() != 3)) if (!counters || (args->size() != 3 && args->size() != 5)) return 0; CounterImpl *counter = new CounterImpl; Value *i = args->current(); // if (i->unit != CSSPrimitiveValue::CSS_IDENT) goto invalid; counter->m_identifier = domString(i->string); if (counters) { i = args->next(); if (i->unit != Value::Operator || i->iValue != ',') goto invalid; i = args->next(); if (i->unit != CSSPrimitiveValue::CSS_STRING) goto invalid; counter->m_separator = domString(i->string); } counter->m_listStyle = CSS_VAL_DECIMAL - CSS_VAL_DISC; i = args->next(); if (i) { if (i->unit != Value::Operator || i->iValue != ',') goto invalid; i = args->next(); if (i->unit != CSSPrimitiveValue::CSS_IDENT) goto invalid; if (i->id < CSS_VAL_DISC || i->id > CSS_VAL__KHTML_CLOSE_QUOTE) goto invalid; counter->m_listStyle = i->id - CSS_VAL_DISC; } return new CSSPrimitiveValueImpl(counter); invalid: delete counter; return 0; } CSSValueImpl* CSSParser::parseBackgroundColor() { int id = valueList->current()->id; if (id == CSS_VAL__KHTML_TEXT || id == CSS_VAL_TRANSPARENT || (id >= CSS_VAL_AQUA && id <= CSS_VAL_WINDOWTEXT) || id == CSS_VAL_MENU || (id >= CSS_VAL_GREY && id < CSS_VAL__KHTML_TEXT && !strict)) return new CSSPrimitiveValueImpl(id); return parseColor(); } CSSValueImpl* CSSParser::parseBackgroundImage() { if (valueList->current()->id == CSS_VAL_NONE) return new CSSImageValueImpl(); if (valueList->current()->unit == CSSPrimitiveValue::CSS_URI) { DOMString uri = khtml::parseURL(domString(valueList->current()->string)); if (!uri.isEmpty()) return new CSSImageValueImpl(DOMString(KURL(styleElement->baseURL(), uri.string()).url()), styleElement); } return 0; } CSSValueImpl* CSSParser::parseBackgroundPositionXY(bool& xFound, bool& yFound) { int id = valueList->current()->id; if (id == CSS_VAL_LEFT || id == CSS_VAL_TOP || id == CSS_VAL_RIGHT || id == CSS_VAL_BOTTOM || id == CSS_VAL_CENTER) { int percent = 0; if (id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT) { if (xFound) return 0; xFound = true; if (id == CSS_VAL_RIGHT) percent = 100; } else if (id == CSS_VAL_TOP || id == CSS_VAL_BOTTOM) { if (yFound) return 0; yFound = true; if (id == CSS_VAL_BOTTOM) percent = 100; } else if (id == CSS_VAL_CENTER) // Center is ambiguous, so we're not sure which position we've found yet, an x or a y. percent = 50; return new CSSPrimitiveValueImpl(percent, CSSPrimitiveValue::CSS_PERCENTAGE); } if (validUnit(valueList->current(), FPercent|FLength, strict)) return new CSSPrimitiveValueImpl(valueList->current()->fValue, (CSSPrimitiveValue::UnitTypes)valueList->current()->unit); return 0; } void CSSParser::parseBackgroundPosition(CSSValueImpl*& value1, CSSValueImpl*& value2) { value1 = value2 = 0; Value* value = valueList->current(); // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length. bool value1IsX = false, value1IsY = false; value1 = parseBackgroundPositionXY(value1IsX, value1IsY); if (!value1) return; // It only takes one value for background-position to be correctly parsed if it was specified in a shorthand (since we // can assume that any other values belong to the rest of the shorthand). If we're not parsing a shorthand, though, the // value was explicitly specified for our property. value = valueList->next(); // First check for the comma. If so, we are finished parsing this value or value pair. if (value && value->unit == Value::Operator && value->iValue == ',') value = 0; bool value2IsX = false, value2IsY = false; if (value) { value2 = parseBackgroundPositionXY(value2IsX, value2IsY); if (value2) valueList->next(); else { if (!inShorthand()) { delete value1; value1 = 0; return; } } } if (!value2) // Only one value was specified. If that value was not a keyword, then it sets the x position, and the y position // is simply 50%. This is our default. // For keywords, the keyword was either an x-keyword (left/right), a y-keyword (top/bottom), or an ambiguous keyword (center). // For left/right/center, the default of 50% in the y is still correct. value2 = new CSSPrimitiveValueImpl(50, CSSPrimitiveValue::CSS_PERCENTAGE); if (value1IsY || value2IsX) { // Swap our two values. CSSValueImpl* val = value2; value2 = value1; value1 = val; } } CSSValueImpl* CSSParser::parseBackgroundSize() { Value* value = valueList->current(); CSSPrimitiveValueImpl* parsedValue1; if (value->id == CSS_VAL_AUTO) parsedValue1 = new CSSPrimitiveValueImpl(0, CSSPrimitiveValue::CSS_UNKNOWN); else { if (!validUnit(value, FLength|FPercent, strict)) return 0; parsedValue1 = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); } CSSPrimitiveValueImpl* parsedValue2 = parsedValue1; if ((value = valueList->next())) { if (value->id == CSS_VAL_AUTO) parsedValue2 = new CSSPrimitiveValueImpl(0, CSSPrimitiveValue::CSS_UNKNOWN); else { if (!validUnit(value, FLength|FPercent, strict)) { delete parsedValue1; return 0; } parsedValue2 = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); } } PairImpl* pair = new PairImpl(parsedValue1, parsedValue2); return new CSSPrimitiveValueImpl(pair); } bool CSSParser::parseBackgroundProperty(int propId, int& propId1, int& propId2, CSSValueImpl*& retValue1, CSSValueImpl*& retValue2) { #ifdef CSS_DEBUG kdDebug(6080) << "parseBackgroundProperty()" << endl; kdDebug(6080) << "LOOKING FOR: " << getPropertyName(propId).string() << endl; #endif CSSValueListImpl *values = 0, *values2 = 0; Value* val; CSSValueImpl *value = 0, *value2 = 0; bool allowComma = false; retValue1 = retValue2 = 0; propId1 = propId; propId2 = propId; if (propId == CSS_PROP_BACKGROUND_POSITION) { propId1 = CSS_PROP_BACKGROUND_POSITION_X; propId2 = CSS_PROP_BACKGROUND_POSITION_Y; } while ((val = valueList->current())) { CSSValueImpl *currValue = 0, *currValue2 = 0; if (allowComma) { if (val->unit != Value::Operator || val->iValue != ',') goto failed; valueList->next(); allowComma = false; } else { switch (propId) { case CSS_PROP_BACKGROUND_ATTACHMENT: if (val->id == CSS_VAL_SCROLL || val->id == CSS_VAL_FIXED) { currValue = new CSSPrimitiveValueImpl(val->id); valueList->next(); } break; case CSS_PROP_BACKGROUND_COLOR: currValue = parseBackgroundColor(); if (currValue) valueList->next(); break; case CSS_PROP_BACKGROUND_IMAGE: currValue = parseBackgroundImage(); if (currValue) valueList->next(); break; case CSS_PROP__KHTML_BACKGROUND_CLIP: case CSS_PROP__KHTML_BACKGROUND_ORIGIN: if (val->id == CSS_VAL_BORDER || val->id == CSS_VAL_PADDING || val->id == CSS_VAL_CONTENT) { currValue = new CSSPrimitiveValueImpl(val->id); valueList->next(); } break; case CSS_PROP_BACKGROUND_POSITION: parseBackgroundPosition(currValue, currValue2); // unlike the other functions, parseBackgroundPosition advances the valueList pointer break; case CSS_PROP_BACKGROUND_POSITION_X: { bool xFound = false, yFound = true; currValue = parseBackgroundPositionXY(xFound, yFound); if (currValue) valueList->next(); break; } case CSS_PROP_BACKGROUND_POSITION_Y: { bool xFound = true, yFound = false; currValue = parseBackgroundPositionXY(xFound, yFound); if (currValue) valueList->next(); break; } case CSS_PROP_BACKGROUND_REPEAT: if (val->id >= CSS_VAL_REPEAT && val->id <= CSS_VAL_NO_REPEAT) { currValue = new CSSPrimitiveValueImpl(val->id); valueList->next(); } break; case CSS_PROP__KHTML_BACKGROUND_SIZE: currValue = parseBackgroundSize(); if (currValue) valueList->next(); break; } if (!currValue) goto failed; if (value && !values) { values = new CSSValueListImpl(); values->append(value); value = 0; } if (value2 && !values2) { values2 = new CSSValueListImpl(); values2->append(value2); value2 = 0; } if (values) values->append(currValue); else value = currValue; if (currValue2) { if (values2) values2->append(currValue2); else value2 = currValue2; } allowComma = true; } // When parsing the 'background' shorthand property, we let it handle building up the lists for all // properties. if (inShorthand()) break; } if (values && values->length()) { retValue1 = values; if (values2 && values2->length()) retValue2 = values2; return true; } if (value) { retValue1 = value; retValue2 = value2; return true; } failed: delete values; delete values2; delete value; delete value2; return false; } bool CSSParser::parseShape( int propId, bool important ) { Value *value = valueList->current(); ValueList *args = value->function->args; TQString fname = qString( value->function->name ).lower(); //qDebug( "parseShape: fname: %d", fname.latin1() ); if ( fname != "rect(" || !args ) return false; // rect( t, r, b, l ) || rect( t r b l ) if ( args->size() != 4 && args->size() != 7 ) return false; RectImpl *rect = new RectImpl(); bool valid = true; int i = 0; Value *a = args->current(); while ( a ) { valid = validUnit( a, FLength, strict ); if ( !valid ) break; CSSPrimitiveValueImpl *length = new CSSPrimitiveValueImpl( a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit ); if ( i == 0 ) rect->setTop( length ); else if ( i == 1 ) rect->setRight( length ); else if ( i == 2 ) rect->setBottom( length ); else rect->setLeft( length ); a = args->next(); if ( a && args->size() == 7 ) { if ( a->unit == Value::Operator && a->iValue == ',' ) { a = args->next(); } else { valid = false; break; } } i++; } if ( valid ) { addProperty( propId, new CSSPrimitiveValueImpl( rect ), important ); valueList->next(); return true; } delete rect; return false; } // [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 'font-family' bool CSSParser::parseFont( bool important ) { // kdDebug(6080) << "parsing font property current=" << valueList->currentValue << endl; bool valid = true; Value *value = valueList->current(); CSSValueListImpl* family = 0; CSSPrimitiveValueImpl *style = 0, *variant = 0, *weight = 0, *size = 0, *lineHeight = 0; // optional font-style, font-variant and font-weight while ( value ) { // kdDebug( 6080 ) << "got value " << value->id << " / " << (value->unit == CSSPrimitiveValue::CSS_STRING || // value->unit == CSSPrimitiveValue::CSS_IDENT ? qString( value->string ) : TQString::null ) // << endl; int id = value->id; if ( id ) { if ( id == CSS_VAL_NORMAL ) { // do nothing, it's the initial value for all three } /* else if ( id == CSS_VAL_INHERIT ) { // set all non set ones to inherit // This is not that simple as the inherit could also apply to the following font-size. // very ahrd to tell without looking ahead. inherit = true; } */ else if ( id == CSS_VAL_ITALIC || id == CSS_VAL_OBLIQUE ) { if ( style ) goto invalid; style = new CSSPrimitiveValueImpl( id ); } else if ( id == CSS_VAL_SMALL_CAPS ) { if ( variant ) goto invalid; variant = new CSSPrimitiveValueImpl( id ); } else if ( id >= CSS_VAL_BOLD && id <= CSS_VAL_LIGHTER ) { if ( weight ) goto invalid; weight = new CSSPrimitiveValueImpl( id ); } else { valid = false; } } else if ( !weight && validUnit( value, FInteger|FNonNeg, true ) ) { int w = (int)value->fValue; int val = 0; if ( w == 100 ) val = CSS_VAL_100; else if ( w == 200 ) val = CSS_VAL_200; else if ( w == 300 ) val = CSS_VAL_300; else if ( w == 400 ) val = CSS_VAL_400; else if ( w == 500 ) val = CSS_VAL_500; else if ( w == 600 ) val = CSS_VAL_600; else if ( w == 700 ) val = CSS_VAL_700; else if ( w == 800 ) val = CSS_VAL_800; else if ( w == 900 ) val = CSS_VAL_900; if ( val ) weight = new CSSPrimitiveValueImpl( val ); else valid = false; } else { valid = false; } if ( !valid ) break; value = valueList->next(); } if ( !value ) goto invalid; // set undefined values to default if ( !style ) style = new CSSPrimitiveValueImpl( CSS_VAL_NORMAL ); if ( !variant ) variant = new CSSPrimitiveValueImpl( CSS_VAL_NORMAL ); if ( !weight ) weight = new CSSPrimitiveValueImpl( CSS_VAL_NORMAL ); // kdDebug( 6080 ) << " got style, variant and weight current=" << valueList->currentValue << endl; // now a font size _must_ come // | | | | inherit if ( value->id >= CSS_VAL_XX_SMALL && value->id <= CSS_VAL_LARGER ) size = new CSSPrimitiveValueImpl( value->id ); else if ( validUnit( value, FLength|FPercent, strict ) ) { size = new CSSPrimitiveValueImpl( value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit ); } value = valueList->next(); if ( !size || !value ) goto invalid; // kdDebug( 6080 ) << " got size" << endl; if ( value->unit == Value::Operator && value->iValue == '/' ) { // line-height value = valueList->next(); if ( !value ) goto invalid; if ( value->id == CSS_VAL_NORMAL ) { // default value, nothing to do } else if ( validUnit( value, FNumber|FLength|FPercent, strict ) ) { lineHeight = new CSSPrimitiveValueImpl( value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit ); } else { goto invalid; } value = valueList->next(); if ( !value ) goto invalid; } if ( !lineHeight ) lineHeight = new CSSPrimitiveValueImpl( CSS_VAL_NORMAL ); // kdDebug( 6080 ) << " got line height current=" << valueList->currentValue << endl; // font family must come now family = parseFontFamily(); if ( valueList->current() || !family ) goto invalid; //kdDebug( 6080 ) << " got family, parsing ok!" << endl; addProperty( CSS_PROP_FONT_FAMILY, family, important ); addProperty( CSS_PROP_FONT_STYLE, style, important ); addProperty( CSS_PROP_FONT_VARIANT, variant, important ); addProperty( CSS_PROP_FONT_WEIGHT, weight, important ); addProperty( CSS_PROP_FONT_SIZE, size, important ); addProperty( CSS_PROP_LINE_HEIGHT, lineHeight, important ); return true; invalid: //kdDebug(6080) << " -> invalid" << endl; delete family; delete style; delete variant; delete weight; delete size; delete lineHeight; return false; } CSSValueListImpl *CSSParser::parseFontFamily() { // kdDebug( 6080 ) << "CSSParser::parseFontFamily current=" << valueList->currentValue << endl; CSSValueListImpl *list = new CSSValueListImpl; Value *value = valueList->current(); TQString currFace; while ( value ) { // kdDebug( 6080 ) << "got value " << value->id << " / " // << (value->unit == CSSPrimitiveValue::CSS_STRING || // value->unit == CSSPrimitiveValue::CSS_IDENT ? qString( value->string ) : TQString::null ) // << endl; Value* nextValue = valueList->next(); bool nextValBreaksFont = !nextValue || (nextValue->unit == Value::Operator && nextValue->iValue == ','); bool nextValIsFontName = nextValue && ((nextValue->id >= CSS_VAL_SERIF && nextValue->id <= CSS_VAL_MONOSPACE) || (nextValue->unit == CSSPrimitiveValue::CSS_STRING || nextValue->unit == CSSPrimitiveValue::CSS_IDENT)); if (value->id >= CSS_VAL_SERIF && value->id <= CSS_VAL_MONOSPACE) { if (!currFace.isNull()) { currFace += ' '; currFace += qString(value->string); } else if (nextValBreaksFont || !nextValIsFontName) { if ( !currFace.isNull() ) { list->append( new FontFamilyValueImpl( currFace ) ); currFace = TQString::null; } list->append(new CSSPrimitiveValueImpl(value->id)); } else { currFace = qString( value->string ); } } else if (value->unit == CSSPrimitiveValue::CSS_STRING) { // Strings never share in a family name. currFace = TQString::null; list->append(new FontFamilyValueImpl(qString( value->string) ) ); } else if (value->unit == CSSPrimitiveValue::CSS_IDENT) { if (!currFace.isNull()) { currFace += ' '; currFace += qString(value->string); } else if (nextValBreaksFont || !nextValIsFontName) { if ( !currFace.isNull() ) { list->append( new FontFamilyValueImpl( currFace ) ); currFace = TQString::null; } list->append(new FontFamilyValueImpl( qString( value->string ) ) ); } else { currFace = qString( value->string); } } else { //kdDebug( 6080 ) << "invalid family part" << endl; break; } if (!nextValue) break; if (nextValBreaksFont) { value = valueList->next(); if ( !currFace.isNull() ) list->append( new FontFamilyValueImpl( currFace ) ); currFace = TQString::null; } else if (nextValIsFontName) value = nextValue; else break; } if ( !currFace.isNull() ) list->append( new FontFamilyValueImpl( currFace ) ); if ( !list->length() ) { delete list; list = 0; } return list; } bool CSSParser::parseColorParameters(Value* value, int* colorArray, bool parseAlpha) { ValueList* args = value->function->args; Value* v = args->current(); // Get the first value if (!validUnit(v, FInteger | FPercent, true)) return false; colorArray[0] = static_cast(v->fValue * (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 256.0 / 100.0 : 1.0)); for (int i = 1; i < 3; i++) { v = args->next(); if (v->unit != Value::Operator && v->iValue != ',') return false; v = args->next(); if (!validUnit(v, FInteger | FPercent, true)) return false; colorArray[i] = static_cast(v->fValue * (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 256.0 / 100.0 : 1.0)); } if (parseAlpha) { v = args->next(); if (v->unit != Value::Operator && v->iValue != ',') return false; v = args->next(); if (!validUnit(v, FNumber, true)) return false; colorArray[3] = static_cast(kMax(0.0, kMin(1.0, v->fValue)) * 255); } return true; } // CSS3 specification defines the format of a HSL color as // hsl(, , ) // and with alpha, the format is // hsla(, , , ) // The first value, HUE, is in an angle with a value between 0 and 360 bool CSSParser::parseHSLParameters(Value* value, double* colorArray, bool parseAlpha) { ValueList* args = value->function->args; Value* v = args->current(); // Get the first value if (!validUnit(v, FInteger, true)) return false; // normalize the Hue value and change it to be between 0 and 1.0 colorArray[0] = (((static_cast(v->fValue) % 360) + 360) % 360) / 360.0; for (int i = 1; i < 3; i++) { v = args->next(); if (v->unit != Value::Operator && v->iValue != ',') return false; v = args->next(); if (!validUnit(v, FPercent, true)) return false; colorArray[i] = kMax(0.0, kMin(100.0, v->fValue)) / 100.0; // needs to be value between 0 and 1.0 } if (parseAlpha) { v = args->next(); if (v->unit != Value::Operator && v->iValue != ',') return false; v = args->next(); if (!validUnit(v, FNumber, true)) return false; colorArray[3] = kMax(0.0, kMin(1.0, v->fValue)); } return true; } static bool parseColor(int unit, const TQString &name, QRgb& rgb) { int len = name.length(); if ( !len ) return false; bool ok; if ( len == 3 || len == 6 ) { int val = name.toInt(&ok, 16); if ( ok ) { if (len == 6) { rgb = (0xff << 24) | val; return true; } else if ( len == 3 ) { // #abc converts to #aabbcc according to the specs rgb = (0xff << 24) | (val&0xf00)<<12 | (val&0xf00)<<8 | (val&0xf0)<<8 | (val&0xf0)<<4 | (val&0xf)<<4 | (val&0xf); return true; } } } if ( unit == CSSPrimitiveValue::CSS_IDENT ) { // try a little harder TQColor tc; tc.setNamedColor(name.lower()); if ( tc.isValid() ) { rgb = tc.rgb(); return true; } } return false; } CSSPrimitiveValueImpl *CSSParser::parseColor() { return parseColorFromValue(valueList->current()); } CSSPrimitiveValueImpl *CSSParser::parseColorFromValue(Value* value) { QRgb c = khtml::transparentColor; if ( !strict && value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue >= 0. && value->fValue < 1000000. ) { TQString str; str.sprintf( "%06d", (int)(value->fValue+.5) ); if ( !::parseColor( value->unit, str, c ) ) return 0; } else if (value->unit == CSSPrimitiveValue::CSS_RGBCOLOR || value->unit == CSSPrimitiveValue::CSS_IDENT || (!strict && value->unit == CSSPrimitiveValue::CSS_DIMENSION)) { if ( !::parseColor( value->unit, qString( value->string ), c) ) return 0; } else if ( value->unit == Value::Function && value->function->args != 0 && value->function->args->size() == 5 /* rgb + two commas */ && qString( value->function->name ).lower() == "rgb(" ) { int colorValues[3]; if (!parseColorParameters(value, colorValues, false)) return 0; colorValues[0] = kMax( 0, kMin( 255, colorValues[0] ) ); colorValues[1] = kMax( 0, kMin( 255, colorValues[1] ) ); colorValues[2] = kMax( 0, kMin( 255, colorValues[2] ) ); c = qRgb(colorValues[0], colorValues[1], colorValues[2]); } else if (value->unit == Value::Function && value->function->args != 0 && value->function->args->size() == 7 /* rgba + three commas */ && domString(value->function->name).lower() == "rgba(") { int colorValues[4]; if (!parseColorParameters(value, colorValues, true)) return 0; colorValues[0] = kMax( 0, kMin( 255, colorValues[0] ) ); colorValues[1] = kMax( 0, kMin( 255, colorValues[1] ) ); colorValues[2] = kMax( 0, kMin( 255, colorValues[2] ) ); c = qRgba(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); } else if (value->unit == Value::Function && value->function->args != 0 && value->function->args->size() == 5 /* hsl + two commas */ && domString(value->function->name).lower() == "hsl(") { double colorValues[3]; if (!parseHSLParameters(value, colorValues, false)) return 0; c = khtml::qRgbaFromHsla(colorValues[0], colorValues[1], colorValues[2], 1.0); } else if (value->unit == Value::Function && value->function->args != 0 && value->function->args->size() == 7 /* hsla + three commas */ && domString(value->function->name).lower() == "hsla(") { double colorValues[4]; if (!parseHSLParameters(value, colorValues, true)) return 0; c = khtml::qRgbaFromHsla(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); } else return 0; return new CSSPrimitiveValueImpl(c); } // This class tracks parsing state for shadow values. If it goes out of scope (e.g., due to an early return) // without the allowBreak bit being set, then it will clean up all of the objects and destroy them. struct ShadowParseContext { ShadowParseContext() :values(0), x(0), y(0), blur(0), color(0), allowX(true), allowY(false), allowBlur(false), allowColor(true), allowBreak(true) {} ~ShadowParseContext() { if (!allowBreak) { delete values; delete x; delete y; delete blur; delete color; } } bool allowLength() { return allowX || allowY || allowBlur; } bool failed() { return allowBreak = false; } void commitValue() { // Handle the ,, case gracefully by doing nothing. if (x || y || blur || color) { if (!values) values = new CSSValueListImpl(); // Construct the current shadow value and add it to the list. values->append(new ShadowValueImpl(x, y, blur, color)); } // Now reset for the next shadow value. x = y = blur = color = 0; allowX = allowColor = allowBreak = true; allowY = allowBlur = false; } void commitLength(Value* v) { CSSPrimitiveValueImpl* val = new CSSPrimitiveValueImpl(v->fValue, (CSSPrimitiveValue::UnitTypes)v->unit); if (allowX) { x = val; allowX = false; allowY = true; allowColor = false; allowBreak = false; } else if (allowY) { y = val; allowY = false; allowBlur = true; allowColor = true; allowBreak = true; } else if (allowBlur) { blur = val; allowBlur = false; } else delete val; } void commitColor(CSSPrimitiveValueImpl* val) { color = val; allowColor = false; if (allowX) allowBreak = false; else allowBlur = false; } CSSValueListImpl* values; CSSPrimitiveValueImpl* x; CSSPrimitiveValueImpl* y; CSSPrimitiveValueImpl* blur; CSSPrimitiveValueImpl* color; bool allowX; bool allowY; bool allowBlur; bool allowColor; bool allowBreak; }; bool CSSParser::parseShadow(int propId, bool important) { ShadowParseContext context; Value* val; while ((val = valueList->current())) { // Check for a comma break first. if (val->unit == Value::Operator) { if (val->iValue != ',' || !context.allowBreak) // Other operators aren't legal or we aren't done with the current shadow // value. Treat as invalid. return context.failed(); // The value is good. Commit it. context.commitValue(); } // Check to see if we're a length. else if (validUnit(val, FLength, true)) { // We required a length and didn't get one. Invalid. if (!context.allowLength()) return context.failed(); // A length is allowed here. Construct the value and add it. context.commitLength(val); } else { // The only other type of value that's ok is a color value. CSSPrimitiveValueImpl* parsedColor = 0; bool isColor = (val->id >= CSS_VAL_AQUA && val->id <= CSS_VAL_WINDOWTEXT || val->id == CSS_VAL_MENU || (val->id >= CSS_VAL_GREY && val->id <= CSS_VAL__KHTML_TEXT && !strict)); if (!context.allowColor) return context.failed(); if (isColor) parsedColor = new CSSPrimitiveValueImpl(val->id); if (!parsedColor) // It's not built-in. Try to parse it as a color. parsedColor = parseColorFromValue(val); if (!parsedColor) return context.failed(); context.commitColor(parsedColor); } valueList->next(); } if (context.allowBreak) { context.commitValue(); if (context.values->length()) { addProperty(propId, context.values, important); valueList->next(); return true; } } return context.failed(); } bool CSSParser::parseCounter(int propId, bool increment, bool important) { enum { ID, VAL, COMMA } state = ID; CSSValueListImpl *list = new CSSValueListImpl; DOMString c; Value* val; while (true) { val = valueList->current(); switch (state) { // Commas are not allowed according to the standard, but Opera allows them and being the only // other browser with counter support we need to match their behavior to work with current use case COMMA: state = ID; if (val && val->unit == Value::Operator && val->iValue == ',') { valueList->next(); continue; } // no break case ID: if (val && val->unit == CSSPrimitiveValue::CSS_IDENT) { c = qString(val->string); state = VAL; valueList->next(); continue; } break; case VAL: { short i = 0; if (val && val->unit == CSSPrimitiveValue::CSS_NUMBER) { i = (short)val->fValue; valueList->next(); } else i = (increment) ? 1 : 0; CounterActImpl *cv = new CounterActImpl(c,i); list->append(cv); state = COMMA; continue; } } break; } if(list->length() > 0) { addProperty( propId, list, important ); return true; } delete list; return false; } static inline int yyerror( const char *str ) { // assert( 0 ); #ifdef CSS_DEBUG kdDebug( 6080 ) << "CSS parse error " << str << endl; #else Q_UNUSED( str ); #endif return 1; } #define END 0 #include "parser.h" int DOM::CSSParser::lex( void *_yylval ) { YYSTYPE *yylval = (YYSTYPE *)_yylval; int token = lex(); int length; unsigned short *t = text( &length ); #ifdef TOKEN_DEBUG qDebug("CSSTokenizer: got token %d: '%s'", token, token == END ? "" : TQString( (TQChar *)t, length ).latin1() ); #endif switch( token ) { case '{': block_nesting++; break; case '}': if ( block_nesting ) block_nesting--; break; case END: if ( block_nesting ) { block_nesting--; return '}'; } break; case S: case SGML_CD: case INCLUDES: case DASHMATCH: break; case URI: case STRING: case IDENT: case NTH: case HASH: case DIMEN: case UNICODERANGE: case NOTFUNCTION: case FUNCTION: yylval->string.string = t; yylval->string.length = length; break; case IMPORT_SYM: case PAGE_SYM: case MEDIA_SYM: case FONT_FACE_SYM: case CHARSET_SYM: case NAMESPACE_SYM: case IMPORTANT_SYM: break; case QEMS: length--; case GRADS: length--; case DEGS: case RADS: case KHERZ: length--; case MSECS: case HERZ: case EMS: case EXS: case PXS: case CMS: case MMS: case INS: case PTS: case PCS: length--; case SECS: case PERCENTAGE: length--; case FLOAT: case INTEGER: yylval->val = TQString( (TQChar *)t, length ).toDouble(); //qDebug("value = %s, converted=%.2f", TQString( (TQChar *)t, length ).latin1(), yylval->val ); break; default: break; } return token; } static inline int toHex( char c ) { if ( '0' <= c && c <= '9' ) return c - '0'; if ( 'a' <= c && c <= 'f' ) return c - 'a' + 10; if ( 'A' <= c && c<= 'F' ) return c - 'A' + 10; return 0; } unsigned short *DOM::CSSParser::text(int *length) { unsigned short *start = yytext; int l = yyleng; switch( yyTok ) { case STRING: l--; /* nobreak */ case HASH: start++; l--; break; case URI: // "url("{w}{string}{w}")" // "url("{w}{url}{w}")" // strip "url(" and ")" start += 4; l -= 5; // strip {w} while ( l && (*start == ' ' || *start == '\t' || *start == '\r' || *start == '\n' || *start == '\f' ) ) { start++; l--; } if ( *start == '"' || *start == '\'' ) { start++; l--; } while ( l && (start[l-1] == ' ' || start[l-1] == '\t' || start[l-1] == '\r' || start[l-1] == '\n' || start[l-1] == '\f' ) ) { l--; } if ( l && (start[l-1] == '\"' || start[l-1] == '\'' ) ) l--; default: break; } // process escapes unsigned short *out = start; unsigned short *escape = 0; for ( int i = 0; i < l; i++ ) { unsigned short *current = start+i; if ( escape == current - 1 ) { if ( ( *current >= '0' && *current <= '9' ) || ( *current >= 'a' && *current <= 'f' ) || ( *current >= 'A' && *current <= 'F' ) ) continue; if ( yyTok == STRING && ( *current == '\n' || *current == '\r' || *current == '\f' ) ) { // ### handle \r\n case if ( *current != '\r' ) escape = 0; continue; } // in all other cases copy the char to output // ### *out++ = *current; escape = 0; continue; } if ( escape == current - 2 && yyTok == STRING && *(current-1) == '\r' && *current == '\n' ) { escape = 0; continue; } if ( escape > current - 7 && ( ( *current >= '0' && *current <= '9' ) || ( *current >= 'a' && *current <= 'f' ) || ( *current >= 'A' && *current <= 'F' ) ) ) continue; if ( escape ) { // add escaped char int uc = 0; escape++; while ( escape < current ) { // qDebug("toHex( %c = %x", (char)*escape, toHex( *escape ) ); uc *= 16; uc += toHex( *escape ); escape++; } // qDebug(" converting escape: string='%s', value=0x%x", TQString( (TQChar *)e, current-e ).latin1(), uc ); // can't handle chars outside ucs2 if ( uc > 0xffff ) uc = 0xfffd; *(out++) = (unsigned short)uc; escape = 0; if ( *current == ' ' || *current == '\t' || *current == '\r' || *current == '\n' || *current == '\f' ) continue; } if ( !escape && *current == '\\' ) { escape = current; continue; } *(out++) = *current; } if ( escape ) { // add escaped char int uc = 0; escape++; while ( escape < start+l ) { // qDebug("toHex( %c = %x", (char)*escape, toHex( *escape ) ); uc *= 16; uc += toHex( *escape ); escape++; } // qDebug(" converting escape: string='%s', value=0x%x", TQString( (TQChar *)e, current-e ).latin1(), uc ); // can't handle chars outside ucs2 if ( uc > 0xffff ) uc = 0xfffd; *(out++) = (unsigned short)uc; } *length = out - start; return start; } #define YY_DECL int DOM::CSSParser::lex() #define yyconst const typedef int yy_state_type; typedef unsigned int YY_CHAR; // this line makes sure we treat all Unicode chars correctly. #define YY_SC_TO_UI(c) (c > 0xff ? 0xff : c) #define YY_DO_BEFORE_ACTION \ yytext = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ yy_hold_char = *yy_cp; \ *yy_cp = 0; \ yy_c_buf_p = yy_cp; #define YY_BREAK break; #define ECHO qDebug( "%s", TQString( (TQChar *)yytext, yyleng ).latin1() ) #define YY_RULE_SETUP #define INITIAL 0 #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) #define YY_START ((yy_start - 1) / 2) #define yyterminate() yyTok = END; return yyTok #define YY_FATAL_ERROR(a) qFatal(a) #define BEGIN yy_start = 1 + 2 * #define COMMENT 1 #include "tokenizer.cpp"