File indexing completed on 2024-04-28 11:37:32
0001 /* 0002 * This file is part of the DOM implementation for KDE. 0003 * 0004 * Copyright 2003 Lars Knoll (knoll@kde.org) 0005 * Copyright 2005 Allan Sandfeld Jensen (kde@carewolf.com) 0006 * Copyright (C) 2004, 2005, 2006, 2007 Apple Computer, Inc. 0007 * Copyright (C) 2008 Maksim Orlovich <maksim@kde.org> 0008 * 0009 * This library is free software; you can redistribute it and/or 0010 * modify it under the terms of the GNU Library General Public 0011 * License as published by the Free Software Foundation; either 0012 * version 2 of the License, or (at your option) any later version. 0013 * 0014 * This library is distributed in the hope that it will be useful, 0015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0017 * Library General Public License for more details. 0018 * 0019 * You should have received a copy of the GNU Library General Public License 0020 * along with this library; see the file COPYING.LIB. If not, write to 0021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0022 * Boston, MA 02110-1301, USA. 0023 */ 0024 0025 // #define CSS_DEBUG 0026 // #define TOKEN_DEBUG 0027 #define YYDEBUG 0 0028 0029 #include "cssparser.h" 0030 0031 #include "khtml_debug.h" 0032 #include <QUrl> 0033 #include <QScopedPointer> 0034 0035 #include "css_valueimpl.h" 0036 #include "css_ruleimpl.h" 0037 #include "css_stylesheetimpl.h" 0038 #include "css_mediaquery.h" 0039 #include "cssproperties.h" 0040 #include "cssvalues.h" 0041 0042 #include <stdlib.h> 0043 #include <assert.h> 0044 0045 using namespace DOM; 0046 0047 // used to promote background: left to left center 0048 #define BACKGROUND_SKIP_CENTER( num ) \ 0049 if ( !pos_ok[ num ] && expected != 1 ) { \ 0050 pos_ok[num] = true; \ 0051 pos[num] = 0; \ 0052 skip_next = false; \ 0053 } 0054 0055 ValueList::~ValueList() 0056 { 0057 const int numValues = m_values.size(); 0058 for (int i = 0; i < numValues; i++) 0059 if (m_values[i].unit == Value::Function) { 0060 delete m_values[i].function; 0061 } 0062 } 0063 0064 namespace 0065 { 0066 class ShorthandScope 0067 { 0068 public: 0069 ShorthandScope(CSSParser *parser, int propId) : m_parser(parser) 0070 { 0071 if (!(m_parser->m_inParseShorthand++)) { 0072 m_parser->m_currentShorthand = propId; 0073 } 0074 } 0075 ~ShorthandScope() 0076 { 0077 if (!(--m_parser->m_inParseShorthand)) { 0078 m_parser->m_currentShorthand = 0; 0079 } 0080 } 0081 0082 private: 0083 CSSParser *m_parser; 0084 }; 0085 } 0086 0087 using namespace DOM; 0088 0089 #if YYDEBUG > 0 0090 extern int cssyydebug; 0091 #endif 0092 0093 extern int cssyyparse(void *parser); 0094 0095 CSSParser *CSSParser::currentParser = nullptr; 0096 0097 CSSParser::CSSParser(bool strictParsing) 0098 { 0099 #ifdef CSS_DEBUG 0100 qCDebug(KHTML_LOG) << "CSSParser::CSSParser this=" << this; 0101 #endif 0102 strict = strictParsing; 0103 0104 parsedProperties = (CSSProperty **) malloc(32 * sizeof(CSSProperty *)); 0105 numParsedProperties = 0; 0106 maxParsedProperties = 32; 0107 0108 data = nullptr; 0109 valueList = nullptr; 0110 rule = nullptr; 0111 id = 0; 0112 important = false; 0113 0114 m_inParseShorthand = 0; 0115 m_currentShorthand = 0; 0116 m_implicitShorthand = false; 0117 0118 yy_start = 1; 0119 0120 #if YYDEBUG > 0 0121 cssyydebug = 1; 0122 #endif 0123 0124 } 0125 0126 CSSParser::~CSSParser() 0127 { 0128 if (numParsedProperties) { 0129 clearProperties(); 0130 } 0131 free(parsedProperties); 0132 0133 delete valueList; 0134 0135 #ifdef CSS_DEBUG 0136 qCDebug(KHTML_LOG) << "CSSParser::~CSSParser this=" << this; 0137 #endif 0138 0139 free(data); 0140 0141 } 0142 0143 unsigned int CSSParser::defaultNamespace() 0144 { 0145 if (styleElement && styleElement->isCSSStyleSheet()) { 0146 return static_cast<CSSStyleSheetImpl *>(styleElement)->defaultNamespace(); 0147 } else { 0148 return anyNamespace; 0149 } 0150 } 0151 0152 void CSSParser::runParser() 0153 { 0154 CSSParser *old = currentParser; 0155 currentParser = this; 0156 cssyyparse(this); 0157 currentParser = old; 0158 boundLocalNames.clear(); 0159 } 0160 0161 void CSSParser::setupParser(const char *prefix, const DOMString &string, const char *suffix) 0162 { 0163 unsigned preflen = strlen(prefix); 0164 unsigned sufflen = strlen(suffix); 0165 int length = string.length() + preflen + sufflen + 8; 0166 0167 free(data); 0168 0169 data = (unsigned short *)malloc(length * sizeof(unsigned short)); 0170 for (unsigned i = 0; i < preflen; i++) { 0171 data[i] = prefix[i]; 0172 } 0173 0174 memcpy(data + preflen, string.unicode(), string.length()*sizeof(unsigned short)); 0175 0176 unsigned start = preflen + string.length(); 0177 unsigned end = start + sufflen; 0178 for (unsigned i = start; i < end; i++) { 0179 data[i] = suffix[i - start]; 0180 } 0181 0182 // the flex scanner sometimes give invalid reads for any 0183 // smaller padding - try e.g. css/invalid-rules-005.html or see #167318 0184 data[length - 1] = 0; 0185 data[length - 2] = 0; 0186 data[length - 3] = 0; 0187 data[length - 4] = 0; 0188 data[length - 5] = 0; 0189 data[length - 6] = 0; 0190 data[length - 7] = 0; 0191 data[length - 8] = 0; 0192 0193 yyTok = -1; 0194 block_nesting = 0; 0195 yy_hold_char = 0; 0196 yyleng = 0; 0197 yytext = yy_c_buf_p = data; 0198 yy_hold_char = *yy_c_buf_p; 0199 } 0200 0201 void CSSParser::parseSheet(CSSStyleSheetImpl *sheet, const DOMString &string) 0202 { 0203 styleElement = sheet; 0204 styleDocument = nullptr; 0205 0206 setupParser("", string, ""); 0207 0208 #ifdef CSS_DEBUG 0209 qCDebug(KHTML_LOG) << ">>>>>>> start parsing style sheet"; 0210 #endif 0211 runParser(); 0212 #ifdef CSS_DEBUG 0213 qCDebug(KHTML_LOG) << "<<<<<<< done parsing style sheet"; 0214 #endif 0215 0216 delete rule; 0217 rule = nullptr; 0218 } 0219 0220 CSSRuleImpl *CSSParser::parseRule(DOM::CSSStyleSheetImpl *sheet, const DOM::DOMString &string) 0221 { 0222 styleElement = sheet; 0223 styleDocument = nullptr; 0224 0225 setupParser("@-khtml-rule{", string, "} "); 0226 runParser(); 0227 0228 CSSRuleImpl *result = rule; 0229 rule = nullptr; 0230 0231 return result; 0232 } 0233 0234 static void addParsedProperties(DOM::CSSStyleDeclarationImpl *declaration, CSSProperty **parsedProperties, 0235 int numProperties) 0236 { 0237 for (int i = 0; i < numProperties; i++) { 0238 // Only add properties that have no !important counterpart present 0239 if (!declaration->getPropertyPriority(parsedProperties[i]->id()) || parsedProperties[i]->isImportant()) { 0240 declaration->removeProperty(parsedProperties[i]->m_id); 0241 declaration->values()->append(parsedProperties[i]); 0242 } 0243 } 0244 } 0245 0246 bool CSSParser::parseValue(DOM::CSSStyleDeclarationImpl *declaration, int _id, const DOM::DOMString &string, 0247 bool _important) 0248 { 0249 #ifdef CSS_DEBUG 0250 qCDebug(KHTML_LOG) << "CSSParser::parseValue: id=" << _id << " important=" << _important 0251 << " value='" << string.string() << "'"; 0252 #endif 0253 0254 styleElement = declaration->stylesheet(); 0255 styleDocument = nullptr; 0256 0257 setupParser("@-khtml-value{", string, "} "); 0258 0259 id = _id; 0260 important = _important; 0261 0262 runParser(); 0263 0264 delete rule; 0265 rule = nullptr; 0266 0267 bool ok = false; 0268 if (numParsedProperties) { 0269 ok = true; 0270 addParsedProperties(declaration, parsedProperties, numParsedProperties); 0271 numParsedProperties = 0; 0272 } 0273 0274 return ok; 0275 } 0276 0277 bool CSSParser::parseDeclaration(DOM::CSSStyleDeclarationImpl *declaration, const DOM::DOMString &string) 0278 { 0279 #ifdef CSS_DEBUG 0280 qCDebug(KHTML_LOG) << "CSSParser::parseDeclaration:" 0281 << " value='" << string.string() << "'"; 0282 #endif 0283 0284 styleElement = declaration->stylesheet(); 0285 styleDocument = nullptr; 0286 0287 setupParser("@-khtml-decls{", string, "} "); 0288 runParser(); 0289 0290 delete rule; 0291 rule = nullptr; 0292 0293 bool ok = false; 0294 if (numParsedProperties) { 0295 ok = true; 0296 addParsedProperties(declaration, parsedProperties, numParsedProperties); 0297 numParsedProperties = 0; 0298 } 0299 0300 return ok; 0301 } 0302 0303 bool CSSParser::parseMediaQuery(DOM::MediaListImpl *queries, const DOM::DOMString &string) 0304 { 0305 if (string.isEmpty() || string.isNull()) { 0306 return true; 0307 } 0308 0309 mediaQuery = nullptr; 0310 // can't use { because tokenizer state switches from mediaquery to initial state when it sees { token. 0311 // instead insert one " " (which is S in parser.y) 0312 setupParser("@-khtml-mediaquery ", string, "} "); 0313 runParser(); 0314 0315 bool ok = false; 0316 if (mediaQuery) { 0317 ok = true; 0318 queries->appendMediaQuery(mediaQuery); 0319 mediaQuery = nullptr; 0320 } 0321 0322 return ok; 0323 } 0324 0325 QList<DOM::CSSSelector *> CSSParser::parseSelectorList(DOM::DocumentImpl *doc, const DOM::DOMString &string) 0326 { 0327 styleElement = nullptr; 0328 styleDocument = doc; 0329 selectors.clear(); 0330 setupParser("@-khtml-selectors{", string, "} "); 0331 runParser(); 0332 0333 // Make sure to detect problems with pseudos, too 0334 bool ok = true; 0335 for (int i = 0; i < selectors.size(); ++i) { 0336 // we need to check not only us, but also other things we're connected to via 0337 // combinators 0338 for (DOM::CSSSelector *sel = selectors[i]; sel; sel = sel->tagHistory) { 0339 if (sel->match == CSSSelector::PseudoClass || sel->match == CSSSelector::PseudoElement) { 0340 if (sel->pseudoType() == CSSSelector::PseudoOther) { 0341 ok = false; 0342 break; 0343 } 0344 } 0345 } 0346 } 0347 0348 if (!ok) { 0349 qDeleteAll(selectors); 0350 selectors.clear(); 0351 } 0352 0353 return selectors; 0354 } 0355 0356 void CSSParser::addProperty(int propId, CSSValueImpl *value, bool important) 0357 { 0358 CSSProperty *prop = new CSSProperty; 0359 prop->m_id = propId; 0360 prop->setValue(value); 0361 prop->m_important = important; 0362 prop->m_implicit = m_implicitShorthand; 0363 0364 if (numParsedProperties >= maxParsedProperties) { 0365 maxParsedProperties += 32; 0366 parsedProperties = (CSSProperty **) realloc(parsedProperties, 0367 maxParsedProperties * sizeof(CSSProperty *)); 0368 } 0369 parsedProperties[numParsedProperties++] = prop; 0370 } 0371 0372 void CSSParser::rollbackParsedProperties(int toNumParsedProperties) 0373 { 0374 while (numParsedProperties > toNumParsedProperties) { 0375 --numParsedProperties; 0376 delete parsedProperties[numParsedProperties]; 0377 } 0378 } 0379 0380 CSSStyleDeclarationImpl *CSSParser::createStyleDeclaration(CSSStyleRuleImpl *rule) 0381 { 0382 QList<CSSProperty *> *propList = new QList<CSSProperty *>; 0383 for (int i = 0; i < numParsedProperties; i++) { 0384 propList->append(parsedProperties[i]); 0385 } 0386 0387 numParsedProperties = 0; 0388 return new CSSStyleDeclarationImpl(rule, propList); 0389 } 0390 0391 CSSStyleDeclarationImpl *CSSParser::createFontFaceStyleDeclaration(CSSFontFaceRuleImpl *rule) 0392 { 0393 QList<CSSProperty *> *propList = new QList<CSSProperty *>; 0394 CSSProperty *overriddenSrcProperty = nullptr; 0395 for (int i = 0; i < numParsedProperties; i++) { 0396 CSSProperty *property = parsedProperties[i]; 0397 int id = property->id(); 0398 if ((id == CSS_PROP_FONT_WEIGHT || id == CSS_PROP_FONT_STYLE || id == CSS_PROP_FONT_VARIANT) && property->value()->isPrimitiveValue()) { 0399 // change those to a list of values containing a single value, so that we may always cast to a list in the CSSFontSelector. 0400 CSSValueImpl *value = property->value(); 0401 value->ref(); 0402 property->setValue(new CSSValueListImpl(CSSValueListImpl::Comma)); 0403 static_cast<CSSValueListImpl *>(property->value())->append(value); 0404 value->deref(); 0405 } else if (id == CSS_PROP_SRC) { 0406 overriddenSrcProperty = property; 0407 continue; 0408 } 0409 0410 propList->append(property); 0411 } 0412 0413 if (overriddenSrcProperty) { 0414 propList->append(overriddenSrcProperty); 0415 } 0416 0417 numParsedProperties = 0; 0418 return new CSSStyleDeclarationImpl(rule, propList); 0419 } 0420 0421 void CSSParser::clearProperties() 0422 { 0423 for (int i = 0; i < numParsedProperties; i++) { 0424 delete parsedProperties[i]; 0425 } 0426 numParsedProperties = 0; 0427 } 0428 0429 DOM::DocumentImpl *CSSParser::document() const 0430 { 0431 if (!styleDocument) { 0432 const StyleBaseImpl *root = styleElement; 0433 while (root->parent()) { 0434 root = root->parent(); 0435 } 0436 if (root->isCSSStyleSheet()) { 0437 styleDocument = static_cast<const CSSStyleSheetImpl *>(root)->doc(); 0438 } 0439 } 0440 return styleDocument; 0441 } 0442 0443 bool CSSParser::validUnit(Value *value, int unitflags, bool strict) 0444 { 0445 if (unitflags & FNonNeg && value->fValue < 0) { 0446 return false; 0447 } 0448 0449 bool b = false; 0450 switch (value->unit) { 0451 case CSSPrimitiveValue::CSS_NUMBER: 0452 b = (unitflags & FNumber); 0453 if (!b && ((unitflags & FLength) && (value->fValue == 0 || !strict))) { 0454 value->unit = CSSPrimitiveValue::CSS_PX; 0455 b = true; 0456 } 0457 if (!b && (unitflags & FInteger) && value->isInt) { 0458 b = true; 0459 } 0460 break; 0461 case CSSPrimitiveValue::CSS_PERCENTAGE: 0462 b = (unitflags & FPercent); 0463 break; 0464 case Value::Q_EMS: 0465 case CSSPrimitiveValue::CSS_EMS: 0466 case CSSPrimitiveValue::CSS_EXS: 0467 case CSSPrimitiveValue::CSS_CHS: 0468 case CSSPrimitiveValue::CSS_REMS: 0469 case CSSPrimitiveValue::CSS_PX: 0470 case CSSPrimitiveValue::CSS_CM: 0471 case CSSPrimitiveValue::CSS_MM: 0472 case CSSPrimitiveValue::CSS_IN: 0473 case CSSPrimitiveValue::CSS_PT: 0474 case CSSPrimitiveValue::CSS_PC: 0475 b = (unitflags & FLength); 0476 break; 0477 case CSSPrimitiveValue::CSS_MS: 0478 case CSSPrimitiveValue::CSS_S: 0479 b = (unitflags & FTime); 0480 break; 0481 case CSSPrimitiveValue::CSS_DEG: 0482 case CSSPrimitiveValue::CSS_RAD: 0483 case CSSPrimitiveValue::CSS_GRAD: 0484 case CSSPrimitiveValue::CSS_HZ: 0485 case CSSPrimitiveValue::CSS_KHZ: 0486 case CSSPrimitiveValue::CSS_DPI: 0487 case CSSPrimitiveValue::CSS_DPCM: 0488 case CSSPrimitiveValue::CSS_DIMENSION: 0489 default: 0490 break; 0491 } 0492 return b; 0493 } 0494 0495 bool CSSParser::parseValue(int propId, bool important) 0496 { 0497 if (!valueList) { 0498 return false; 0499 } 0500 0501 Value *value = valueList->current(); 0502 0503 if (!value) { 0504 return false; 0505 } 0506 0507 int id = value->id; 0508 0509 const int num = inShorthand() ? 1 : valueList->size(); 0510 0511 if (id == CSS_VAL_INHERIT) { 0512 if (num != 1) { 0513 return false; 0514 } 0515 addProperty(propId, new CSSInheritedValueImpl(), important); 0516 return true; 0517 } else if (id == CSS_VAL_INITIAL) { 0518 if (num != 1) { 0519 return false; 0520 } 0521 addProperty(propId, new CSSInitialValueImpl(false/*implicit initial*/), important); 0522 return true; 0523 } 0524 0525 bool valid_primitive = false; 0526 CSSValueImpl *parsedValue = nullptr; 0527 0528 switch (propId) { 0529 /* The comment to the left defines all valid value of this properties as defined 0530 * in CSS 2, Appendix F. Property index 0531 */ 0532 0533 /* All the CSS properties are not supported by the renderer at the moment. 0534 * Note that all the CSS2 Aural properties are only checked, if CSS_AURAL is defined 0535 * (see parseAuralValues). As we don't support them at all this seems reasonable. 0536 */ 0537 0538 case CSS_PROP_SIZE: // <length>{1,2} | auto | portrait | landscape | inherit 0539 // case CSS_PROP_PAGE: // <identifier> | auto // ### CHECK 0540 // ### To be done 0541 if (id) { 0542 valid_primitive = true; 0543 } 0544 break; 0545 case CSS_PROP_UNICODE_BIDI: // normal | embed | bidi-override | inherit 0546 if (id == CSS_VAL_NORMAL || 0547 id == CSS_VAL_EMBED || 0548 id == CSS_VAL_BIDI_OVERRIDE) { 0549 valid_primitive = true; 0550 } 0551 break; 0552 0553 case CSS_PROP_POSITION: // static | relative | absolute | fixed | inherit 0554 if (id == CSS_VAL_STATIC || 0555 id == CSS_VAL_RELATIVE || 0556 id == CSS_VAL_ABSOLUTE || 0557 id == CSS_VAL_FIXED) { 0558 valid_primitive = true; 0559 } 0560 break; 0561 0562 case CSS_PROP_PAGE_BREAK_AFTER: // auto | always | avoid | left | right | inherit 0563 case CSS_PROP_PAGE_BREAK_BEFORE: // auto | always | avoid | left | right | inherit 0564 if (id == CSS_VAL_AUTO || 0565 id == CSS_VAL_ALWAYS || 0566 id == CSS_VAL_AVOID || 0567 id == CSS_VAL_LEFT || 0568 id == CSS_VAL_RIGHT) { 0569 valid_primitive = true; 0570 } 0571 break; 0572 0573 case CSS_PROP_PAGE_BREAK_INSIDE: // avoid | auto | inherit 0574 if (id == CSS_VAL_AUTO || 0575 id == CSS_VAL_AVOID) { 0576 valid_primitive = true; 0577 } 0578 break; 0579 0580 case CSS_PROP_EMPTY_CELLS: // show | hide | inherit 0581 if (id == CSS_VAL_SHOW || 0582 id == CSS_VAL_HIDE) { 0583 valid_primitive = true; 0584 } 0585 break; 0586 0587 case CSS_PROP_QUOTES: // [<string> <string>]+ | none | inherit 0588 if (id == CSS_VAL_NONE) { 0589 valid_primitive = true; 0590 } else { 0591 QuotesValueImpl *quotes = new QuotesValueImpl; 0592 bool is_valid = true; 0593 QString open, close; 0594 Value *val = valueList->current(); 0595 while (val) { 0596 if (val->unit == CSSPrimitiveValue::CSS_STRING) { 0597 open = qString(val->string); 0598 } else { 0599 is_valid = false; 0600 break; 0601 } 0602 valueList->next(); 0603 val = valueList->current(); 0604 if (val && val->unit == CSSPrimitiveValue::CSS_STRING) { 0605 close = qString(val->string); 0606 } else { 0607 is_valid = false; 0608 break; 0609 } 0610 quotes->addLevel(open, close); 0611 valueList->next(); 0612 val = valueList->current(); 0613 } 0614 if (is_valid) { 0615 parsedValue = quotes; 0616 } else { 0617 delete quotes; 0618 } 0619 } 0620 break; 0621 0622 case CSS_PROP_CONTENT: // normal | none | inherit | 0623 // [ <string> | <uri> | <counter> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ 0624 if (id == CSS_VAL_NORMAL || id == CSS_VAL_NONE) { 0625 valid_primitive = true; 0626 } else { 0627 return parseContent(propId, important); 0628 } 0629 break; 0630 0631 case CSS_PROP_WHITE_SPACE: // normal | pre | nowrap | pre-wrap | pre-line | inherit 0632 if (id == CSS_VAL_NORMAL || 0633 id == CSS_VAL_PRE || 0634 id == CSS_VAL_PRE_WRAP || 0635 id == CSS_VAL_PRE_LINE || 0636 id == CSS_VAL_NOWRAP) { 0637 valid_primitive = true; 0638 } 0639 break; 0640 0641 case CSS_PROP_CLIP: // <shape> | auto | inherit 0642 if (id == CSS_VAL_AUTO) { 0643 valid_primitive = true; 0644 } else if (value->unit == Value::Function) { 0645 return parseShape(propId, important); 0646 } 0647 break; 0648 0649 /* Start of supported CSS properties with validation. This is needed for parseShortHand to work 0650 * correctly and allows optimization in khtml::applyRule(..) 0651 */ 0652 case CSS_PROP_CAPTION_SIDE: // top | bottom | left | right | inherit 0653 // Left and right were deprecated in CSS 2.1 and never supported by KHTML 0654 if ( /* id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || */ 0655 id == CSS_VAL_TOP || id == CSS_VAL_BOTTOM) { 0656 valid_primitive = true; 0657 } 0658 break; 0659 0660 case CSS_PROP_BORDER_COLLAPSE: // collapse | separate | inherit 0661 if (id == CSS_VAL_COLLAPSE || id == CSS_VAL_SEPARATE) { 0662 valid_primitive = true; 0663 } 0664 break; 0665 0666 case CSS_PROP_VISIBILITY: // visible | hidden | collapse | inherit 0667 if (id == CSS_VAL_VISIBLE || id == CSS_VAL_HIDDEN || id == CSS_VAL_COLLAPSE) { 0668 valid_primitive = true; 0669 } 0670 break; 0671 0672 case CSS_PROP_OVERFLOW: // visible | hidden | scroll | auto | marquee | inherit 0673 case CSS_PROP_OVERFLOW_X: 0674 case CSS_PROP_OVERFLOW_Y: 0675 if (id == CSS_VAL_VISIBLE || id == CSS_VAL_HIDDEN || id == CSS_VAL_SCROLL || id == CSS_VAL_AUTO || 0676 id == CSS_VAL_MARQUEE) { 0677 valid_primitive = true; 0678 } 0679 break; 0680 0681 case CSS_PROP_LIST_STYLE_POSITION: // inside | outside | inherit 0682 if (id == CSS_VAL_INSIDE || id == CSS_VAL_OUTSIDE) { 0683 valid_primitive = true; 0684 } 0685 break; 0686 0687 case CSS_PROP_LIST_STYLE_TYPE: 0688 // disc | circle | square | decimal | decimal-leading-zero | lower-roman | 0689 // upper-roman | lower-greek | lower-alpha | lower-latin | upper-alpha | 0690 // upper-latin | hebrew | armenian | georgian | cjk-ideographic | hiragana | 0691 // katakana | hiragana-iroha | katakana-iroha | none | inherit 0692 if ((id >= CSS_VAL_DISC && id <= CSS_VAL__KHTML_CLOSE_QUOTE) || id == CSS_VAL_NONE) { 0693 valid_primitive = true; 0694 } 0695 break; 0696 0697 case CSS_PROP_DISPLAY: 0698 // inline | block | list-item | run-in | inline-block | -khtml-ruler | table | 0699 // inline-table | table-row-group | table-header-group | table-footer-group | table-row | 0700 // table-column-group | table-column | table-cell | table-caption | none | inherit 0701 if ((id >= CSS_VAL_INLINE && id <= CSS_VAL_TABLE_CAPTION) || id == CSS_VAL_NONE) { 0702 valid_primitive = true; 0703 } 0704 break; 0705 0706 case CSS_PROP_DIRECTION: // ltr | rtl | inherit 0707 if (id == CSS_VAL_LTR || id == CSS_VAL_RTL) { 0708 valid_primitive = true; 0709 } 0710 break; 0711 0712 case CSS_PROP_TEXT_TRANSFORM: // capitalize | uppercase | lowercase | none | inherit 0713 if ((id >= CSS_VAL_CAPITALIZE && id <= CSS_VAL_LOWERCASE) || id == CSS_VAL_NONE) { 0714 valid_primitive = true; 0715 } 0716 break; 0717 0718 case CSS_PROP_FLOAT: // left | right | none | khtml_left | khtml_right | inherit + center for buggy CSS 0719 if (id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || id == CSS_VAL__KHTML_LEFT || 0720 id == CSS_VAL__KHTML_RIGHT || id == CSS_VAL_NONE || id == CSS_VAL_CENTER) { 0721 valid_primitive = true; 0722 } 0723 break; 0724 0725 case CSS_PROP_CLEAR: // none | left | right | both | inherit 0726 if (id == CSS_VAL_NONE || id == CSS_VAL_LEFT || 0727 id == CSS_VAL_RIGHT || id == CSS_VAL_BOTH) { 0728 valid_primitive = true; 0729 } 0730 break; 0731 0732 case CSS_PROP_TEXT_ALIGN: 0733 // left | right | center | justify | khtml_left | khtml_right | khtml_center | <string> | inherit 0734 if ((id >= CSS_VAL__KHTML_AUTO && id <= CSS_VAL__KHTML_CENTER) || 0735 value->unit == CSSPrimitiveValue::CSS_STRING) { 0736 valid_primitive = true; 0737 } 0738 break; 0739 0740 case CSS_PROP_OUTLINE_STYLE: // <border-style> | inherit 0741 case CSS_PROP_BORDER_TOP_STYLE: //// <border-style> | inherit 0742 case CSS_PROP_BORDER_RIGHT_STYLE: // Defined as: none | hidden | dotted | dashed | 0743 case CSS_PROP_BORDER_BOTTOM_STYLE: // solid | double | groove | ridge | inset | outset | -khtml-native 0744 case CSS_PROP_BORDER_LEFT_STYLE: //// 0745 if (id >= CSS_VAL__KHTML_NATIVE && id <= CSS_VAL_DOUBLE) { 0746 valid_primitive = true; 0747 } 0748 break; 0749 0750 case CSS_PROP_FONT_WEIGHT: // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit 0751 id = parseFontWeight(value, false); 0752 if (id) { 0753 valid_primitive = true; 0754 } 0755 break; 0756 case CSS_PROP_BORDER_TOP_RIGHT_RADIUS: 0757 case CSS_PROP_BORDER_BOTTOM_RIGHT_RADIUS: 0758 case CSS_PROP_BORDER_BOTTOM_LEFT_RADIUS: 0759 case CSS_PROP_BORDER_TOP_LEFT_RADIUS: { 0760 //<length> <length>? 0761 if (num < 1 || num > 2) { 0762 return false; 0763 } 0764 0765 if (!validUnit(value, FLength | FPercent | FNonNeg, strict)) { 0766 return false; 0767 } 0768 0769 CSSPrimitiveValueImpl *horiz = new CSSPrimitiveValueImpl(value->fValue, 0770 (CSSPrimitiveValue::UnitTypes) value->unit); 0771 CSSPrimitiveValueImpl *vert; 0772 0773 if (num == 2) { 0774 value = valueList->next(); 0775 if (!validUnit(value, FLength | FPercent | FNonNeg, strict)) { 0776 delete horiz; 0777 return false; 0778 } 0779 vert = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); 0780 } else { 0781 vert = horiz; 0782 } 0783 0784 addProperty(propId, new CSSPrimitiveValueImpl(new PairImpl(horiz, vert)), important); 0785 return true; 0786 } 0787 0788 case CSS_PROP_BORDER_RADIUS: 0789 return parseBorderRadius(important); 0790 0791 case CSS_PROP_BORDER_SPACING: { 0792 const int properties[2] = { CSS_PROP__KHTML_BORDER_HORIZONTAL_SPACING, 0793 CSS_PROP__KHTML_BORDER_VERTICAL_SPACING 0794 }; 0795 if (num == 1) { 0796 ShorthandScope scope(this, CSS_PROP_BORDER_SPACING); 0797 if (!parseValue(properties[0], important)) { 0798 return false; 0799 } 0800 CSSValueImpl *value = parsedProperties[numParsedProperties - 1]->value(); 0801 addProperty(properties[1], value, important); 0802 return true; 0803 } else if (num == 2) { 0804 ShorthandScope scope(this, CSS_PROP_BORDER_SPACING); 0805 const int oldNumParsedProperties = numParsedProperties; 0806 if (!parseValue(properties[0], important)) { 0807 return false; 0808 } 0809 if (!parseValue(properties[1], important)) { 0810 rollbackParsedProperties(oldNumParsedProperties); 0811 return false; 0812 } 0813 return true; 0814 } 0815 return false; 0816 } 0817 case CSS_PROP__KHTML_BORDER_HORIZONTAL_SPACING: 0818 case CSS_PROP__KHTML_BORDER_VERTICAL_SPACING: 0819 valid_primitive = validUnit(value, FLength | FNonNeg, strict); 0820 break; 0821 0822 case CSS_PROP_SCROLLBAR_FACE_COLOR: // IE5.5 0823 case CSS_PROP_SCROLLBAR_SHADOW_COLOR: // IE5.5 0824 case CSS_PROP_SCROLLBAR_HIGHLIGHT_COLOR: // IE5.5 0825 case CSS_PROP_SCROLLBAR_3DLIGHT_COLOR: // IE5.5 0826 case CSS_PROP_SCROLLBAR_DARKSHADOW_COLOR: // IE5.5 0827 case CSS_PROP_SCROLLBAR_TRACK_COLOR: // IE5.5 0828 case CSS_PROP_SCROLLBAR_ARROW_COLOR: // IE5.5 0829 case CSS_PROP_SCROLLBAR_BASE_COLOR: // IE5.5 0830 if (strict) { 0831 break; 0832 } 0833 /* nobreak */ 0834 case CSS_PROP_OUTLINE_COLOR: // <color> | invert | inherit 0835 // outline has "invert" as additional keyword. 0836 if (propId == CSS_PROP_OUTLINE_COLOR && id == CSS_VAL_INVERT) { 0837 valid_primitive = true; 0838 break; 0839 } 0840 /* nobreak */ 0841 case CSS_PROP_BACKGROUND_COLOR: // <color> | inherit 0842 case CSS_PROP_BORDER_TOP_COLOR: // <color> | inherit 0843 case CSS_PROP_BORDER_RIGHT_COLOR: // <color> | inherit 0844 case CSS_PROP_BORDER_BOTTOM_COLOR: // <color> | inherit 0845 case CSS_PROP_BORDER_LEFT_COLOR: // <color> | inherit 0846 case CSS_PROP_COLOR: // <color> | inherit 0847 if (id == CSS_VAL__KHTML_TEXT || id == CSS_VAL_MENU || 0848 (id >= CSS_VAL_AQUA && id <= CSS_VAL_WINDOWTEXT) || 0849 id == CSS_VAL_TRANSPARENT || 0850 id == CSS_VAL_CURRENTCOLOR || 0851 (id >= CSS_VAL_GREY && id < CSS_VAL__KHTML_TEXT && !strict)) { 0852 valid_primitive = true; 0853 } else { 0854 parsedValue = parseColor(); 0855 if (parsedValue) { 0856 valueList->next(); 0857 } 0858 } 0859 break; 0860 0861 case CSS_PROP_CURSOR: 0862 // [ auto | default | none | 0863 // context-menu | help | pointer | progress | wait | 0864 // cell | crosshair | text | vertical-text | 0865 // alias | copy | move | no-drop | not-allowed | 0866 // e-resize | ne-resize | nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize | 0867 // ew-resize | ns-resize | nesw-resize | nwse-resize | 0868 // col-resize | row-resize | all-scroll 0869 // ] ] | inherit 0870 // MSIE 5 compatibility :/ 0871 if (!strict && id == CSS_VAL_HAND) { 0872 id = CSS_VAL_POINTER; 0873 valid_primitive = true; 0874 } else if ((id >= CSS_VAL_AUTO && id <= CSS_VAL_ALL_SCROLL) || id == CSS_VAL_NONE) { 0875 valid_primitive = true; 0876 } 0877 break; 0878 0879 case CSS_PROP_BACKGROUND_ATTACHMENT: 0880 case CSS_PROP_BACKGROUND_CLIP: 0881 case CSS_PROP_BACKGROUND_IMAGE: 0882 case CSS_PROP_BACKGROUND_ORIGIN: 0883 case CSS_PROP_BACKGROUND_POSITION: 0884 case CSS_PROP_BACKGROUND_POSITION_X: 0885 case CSS_PROP_BACKGROUND_POSITION_Y: 0886 case CSS_PROP_BACKGROUND_SIZE: 0887 case CSS_PROP_BACKGROUND_REPEAT: { 0888 CSSValueImpl *val1 = nullptr, *val2 = nullptr; 0889 int propId1, propId2; 0890 if (parseBackgroundProperty(propId, propId1, propId2, val1, val2)) { 0891 addProperty(propId1, val1, important); 0892 if (val2) { 0893 addProperty(propId2, val2, important); 0894 } 0895 return true; 0896 } 0897 return false; 0898 } 0899 case CSS_PROP_LIST_STYLE_IMAGE: // <uri> | none | inherit 0900 if (id == CSS_VAL_NONE) { 0901 parsedValue = new CSSImageValueImpl(); 0902 valueList->next(); 0903 } else if (value->unit == CSSPrimitiveValue::CSS_URI) { 0904 // ### allow string in non strict mode? 0905 if (styleElement) { 0906 const DOMString uri = domString(value->string); 0907 parsedValue = new CSSImageValueImpl(uri, styleElement); 0908 valueList->next(); 0909 } 0910 } 0911 break; 0912 0913 case CSS_PROP_OUTLINE_WIDTH: // <border-width> | inherit 0914 case CSS_PROP_BORDER_TOP_WIDTH: //// <border-width> | inherit 0915 case CSS_PROP_BORDER_RIGHT_WIDTH: // Which is defined as 0916 case CSS_PROP_BORDER_BOTTOM_WIDTH: // thin | medium | thick | <length> 0917 case CSS_PROP_BORDER_LEFT_WIDTH: //// 0918 if (id == CSS_VAL_THIN || id == CSS_VAL_MEDIUM || id == CSS_VAL_THICK) { 0919 valid_primitive = true; 0920 } else { 0921 valid_primitive = validUnit(value, FLength|FNonNeg, strict); 0922 } 0923 break; 0924 0925 case CSS_PROP_LETTER_SPACING: // normal | <length> | inherit 0926 case CSS_PROP_WORD_SPACING: // normal | <length> | inherit 0927 if (id == CSS_VAL_NORMAL) { 0928 valid_primitive = true; 0929 } else { 0930 valid_primitive = validUnit(value, FLength, strict); 0931 } 0932 break; 0933 0934 case CSS_PROP_TEXT_INDENT: // <length> | <percentage> | inherit 0935 valid_primitive = (!id && validUnit(value, FLength | FPercent, strict)); 0936 break; 0937 0938 case CSS_PROP_PADDING_TOP: // <length> | <percentage> | inherit 0939 case CSS_PROP_PADDING_RIGHT: // <padding-width> | inherit 0940 case CSS_PROP_PADDING_BOTTOM: // Which is defined as 0941 case CSS_PROP_PADDING_LEFT: // <length> | <percentage> 0942 case CSS_PROP__KHTML_PADDING_START: 0943 valid_primitive = (!id && validUnit(value, FLength | FPercent | FNonNeg, strict)); 0944 break; 0945 0946 case CSS_PROP_MAX_HEIGHT: // <length> | <percentage> | none | inherit 0947 case CSS_PROP_MAX_WIDTH: // <length> | <percentage> | none | inherit 0948 if (id == CSS_VAL_NONE) { 0949 valid_primitive = true; 0950 break; 0951 } 0952 /* nobreak */ 0953 case CSS_PROP_MIN_HEIGHT: // <length> | <percentage> | inherit 0954 case CSS_PROP_MIN_WIDTH: // <length> | <percentage> | inherit 0955 valid_primitive = (!id && validUnit(value, FLength | FPercent | FNonNeg, strict)); 0956 break; 0957 0958 case CSS_PROP_FONT_SIZE: 0959 // <absolute-size> | <relative-size> | <length> | <percentage> | inherit 0960 if (id >= CSS_VAL_XX_SMALL && id <= CSS_VAL_LARGER) { 0961 valid_primitive = true; 0962 } else { 0963 valid_primitive = validUnit(value, FLength | FPercent | FNonNeg, strict); 0964 } 0965 break; 0966 0967 case CSS_PROP_FONT_STYLE: // normal | italic | oblique | inherit 0968 if (id == CSS_VAL_NORMAL || id == CSS_VAL_ITALIC || id == CSS_VAL_OBLIQUE) { 0969 valid_primitive = true; 0970 } 0971 break; 0972 0973 case CSS_PROP_FONT_VARIANT: // normal | small-caps | inherit 0974 if (id == CSS_VAL_NORMAL || id == CSS_VAL_SMALL_CAPS) { 0975 valid_primitive = true; 0976 } 0977 break; 0978 0979 case CSS_PROP_VERTICAL_ALIGN: 0980 // baseline | sub | super | top | text-top | middle | bottom | text-bottom | 0981 // <percentage> | <length> | inherit 0982 0983 if (id >= CSS_VAL_BASELINE && id <= CSS_VAL__KHTML_BASELINE_MIDDLE) { 0984 valid_primitive = true; 0985 } else { 0986 valid_primitive = (!id && validUnit(value, FLength | FPercent, strict)); 0987 } 0988 break; 0989 0990 case CSS_PROP_HEIGHT: // <length> | <percentage> | auto | inherit 0991 case CSS_PROP_WIDTH: // <length> | <percentage> | auto | inherit 0992 if (id == CSS_VAL_AUTO) { 0993 valid_primitive = true; 0994 } else 0995 // ### handle multilength case where we allow relative units 0996 { 0997 valid_primitive = (!id && validUnit(value, FLength | FPercent | FNonNeg, strict)); 0998 } 0999 break; 1000 1001 case CSS_PROP_BOTTOM: // <length> | <percentage> | auto | inherit 1002 case CSS_PROP_LEFT: // <length> | <percentage> | auto | inherit 1003 case CSS_PROP_RIGHT: // <length> | <percentage> | auto | inherit 1004 case CSS_PROP_TOP: // <length> | <percentage> | auto | inherit 1005 case CSS_PROP_MARGIN_TOP: //// <margin-width> | inherit 1006 case CSS_PROP_MARGIN_RIGHT: // Which is defined as 1007 case CSS_PROP_MARGIN_BOTTOM: // <length> | <percentage> | auto | inherit 1008 case CSS_PROP_MARGIN_LEFT: //// 1009 case CSS_PROP__KHTML_MARGIN_START: 1010 if (id == CSS_VAL_AUTO) { 1011 valid_primitive = true; 1012 } else { 1013 valid_primitive = (!id && validUnit(value, FLength | FPercent, strict)); 1014 } 1015 break; 1016 1017 case CSS_PROP_Z_INDEX: // auto | <integer> | inherit 1018 // qDebug("parsing z-index: id=%d, fValue=%f", id, value->fValue ); 1019 if (id == CSS_VAL_AUTO) { 1020 valid_primitive = true; 1021 break; 1022 } 1023 /* nobreak */ 1024 case CSS_PROP_ORPHANS: // <integer> | inherit 1025 case CSS_PROP_WIDOWS: // <integer> | inherit 1026 // ### not supported later on 1027 valid_primitive = (!id && validUnit(value, FInteger, false)); 1028 break; 1029 1030 case CSS_PROP_LINE_HEIGHT: // normal | <number> | <length> | <percentage> | inherit 1031 if (id == CSS_VAL_NORMAL) { 1032 valid_primitive = true; 1033 } else { 1034 valid_primitive = (!id && validUnit(value, FNumber | FLength | FPercent | FNonNeg, strict)); 1035 } 1036 break; 1037 case CSS_PROP_COUNTER_INCREMENT: // [ <identifier> <integer>? ]+ | none | inherit 1038 if (id == CSS_VAL_NONE) { 1039 valid_primitive = true; 1040 } else { 1041 return parseCounter(propId, true, important); 1042 } 1043 break; 1044 case CSS_PROP_COUNTER_RESET: // [ <identifier> <integer>? ]+ | none | inherit 1045 if (id == CSS_VAL_NONE) { 1046 valid_primitive = true; 1047 } else { 1048 return parseCounter(propId, false, important); 1049 } 1050 break; 1051 1052 case CSS_PROP_FONT_FAMILY: 1053 // [[ <family-name> | <generic-family> ],]* [<family-name> | <generic-family>] | inherit 1054 { 1055 parsedValue = parseFontFamily(); 1056 break; 1057 } 1058 1059 case CSS_PROP_TEXT_DECORATION: 1060 // none | [ underline || overline || line-through || blink ] | inherit 1061 if (id == CSS_VAL_NONE) { 1062 valid_primitive = true; 1063 } else { 1064 CSSValueListImpl *list = new CSSValueListImpl; 1065 bool is_valid = true; 1066 while (is_valid && value) { 1067 switch (value->id) { 1068 case CSS_VAL_BLINK: 1069 break; 1070 case CSS_VAL_UNDERLINE: 1071 case CSS_VAL_OVERLINE: 1072 case CSS_VAL_LINE_THROUGH: 1073 list->append(new CSSPrimitiveValueImpl(value->id)); 1074 break; 1075 default: 1076 is_valid = false; 1077 } 1078 value = valueList->next(); 1079 } 1080 //qCDebug(KHTML_LOG) << "got " << list->length() << "d decorations"; 1081 if (list->length() && is_valid) { 1082 parsedValue = list; 1083 valueList->next(); 1084 } else { 1085 delete list; 1086 } 1087 } 1088 break; 1089 1090 case CSS_PROP_TABLE_LAYOUT: // auto | fixed | inherit 1091 if (id == CSS_VAL_AUTO || id == CSS_VAL_FIXED) { 1092 valid_primitive = true; 1093 } 1094 break; 1095 1096 case CSS_PROP_SRC: // Only used within @font-face, so cannot use inherit | initial or be !important. This is a list of urls or local references. 1097 return parseFontFaceSrc(); 1098 1099 case CSS_PROP_UNICODE_RANGE: 1100 // return parseFontFaceUnicodeRange(); 1101 break; 1102 1103 case CSS_PROP__KHTML_FLOW_MODE: 1104 if (id == CSS_VAL__KHTML_NORMAL || id == CSS_VAL__KHTML_AROUND_FLOATS) { 1105 valid_primitive = true; 1106 } 1107 break; 1108 1109 /* CSS3 properties */ 1110 case CSS_PROP_BOX_SIZING: // border-box | content-box | inherit 1111 if (id == CSS_VAL_BORDER_BOX || id == CSS_VAL_CONTENT_BOX) { 1112 valid_primitive = true; 1113 } 1114 break; 1115 case CSS_PROP_OUTLINE_OFFSET: 1116 valid_primitive = validUnit(value, FLength, strict); 1117 break; 1118 case CSS_PROP_TEXT_SHADOW: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3 1119 if (id == CSS_VAL_NONE) { 1120 valid_primitive = true; 1121 } else { 1122 return parseShadow(propId, important); 1123 } 1124 break; 1125 case CSS_PROP_OPACITY: 1126 valid_primitive = validUnit(value, FNumber, strict); 1127 break; 1128 case CSS_PROP__KHTML_USER_INPUT: // none | enabled | disabled | inherit 1129 if (id == CSS_VAL_NONE || id == CSS_VAL_ENABLED || id == CSS_VAL_DISABLED) { 1130 valid_primitive = true; 1131 } 1132 // qCDebug(KHTML_LOG) << "CSS_PROP__KHTML_USER_INPUT: " << valid_primitive; 1133 break; 1134 case CSS_PROP__KHTML_MARQUEE: { 1135 const int properties[5] = { CSS_PROP__KHTML_MARQUEE_DIRECTION, CSS_PROP__KHTML_MARQUEE_INCREMENT, 1136 CSS_PROP__KHTML_MARQUEE_REPETITION, 1137 CSS_PROP__KHTML_MARQUEE_STYLE, CSS_PROP__KHTML_MARQUEE_SPEED 1138 }; 1139 return parseShortHand(propId, properties, 5, important); 1140 } 1141 case CSS_PROP__KHTML_MARQUEE_DIRECTION: 1142 if (id == CSS_VAL_FORWARDS || id == CSS_VAL_BACKWARDS || id == CSS_VAL_AHEAD || 1143 id == CSS_VAL_REVERSE || id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || id == CSS_VAL_DOWN || 1144 id == CSS_VAL_UP || id == CSS_VAL_AUTO) { 1145 valid_primitive = true; 1146 } 1147 break; 1148 case CSS_PROP__KHTML_MARQUEE_INCREMENT: 1149 if (id == CSS_VAL_SMALL || id == CSS_VAL_LARGE || id == CSS_VAL_MEDIUM) { 1150 valid_primitive = true; 1151 } else { 1152 valid_primitive = validUnit(value, FLength | FPercent, strict); 1153 } 1154 break; 1155 case CSS_PROP__KHTML_MARQUEE_STYLE: 1156 if (id == CSS_VAL_NONE || id == CSS_VAL_SLIDE || id == CSS_VAL_SCROLL || id == CSS_VAL_ALTERNATE || 1157 id == CSS_VAL_UNFURL) { 1158 valid_primitive = true; 1159 } 1160 break; 1161 case CSS_PROP__KHTML_MARQUEE_REPETITION: 1162 if (id == CSS_VAL_INFINITE) { 1163 valid_primitive = true; 1164 } else { 1165 valid_primitive = validUnit(value, FInteger | FNonNeg, strict); 1166 } 1167 break; 1168 case CSS_PROP__KHTML_MARQUEE_SPEED: 1169 if (id == CSS_VAL_NORMAL || id == CSS_VAL_SLOW || id == CSS_VAL_FAST) { 1170 valid_primitive = true; 1171 } else { 1172 valid_primitive = validUnit(value, FTime | FInteger | FNonNeg, strict); 1173 } 1174 break; 1175 case CSS_PROP_TEXT_OVERFLOW: // clip | ellipsis 1176 if (id == CSS_VAL_CLIP || id == CSS_VAL_ELLIPSIS) { 1177 valid_primitive = true; 1178 } 1179 break; 1180 // End of CSS3 properties 1181 1182 /* shorthand properties */ 1183 case CSS_PROP_BACKGROUND: 1184 // ['background-color' || 'background-image' ||'background-repeat' || 1185 // 'background-attachment' || 'background-position'] | inherit 1186 return parseBackgroundShorthand(important); 1187 case CSS_PROP_BORDER: 1188 // [ 'border-width' || 'border-style' || <color> ] | inherit 1189 { 1190 const int properties[3] = { CSS_PROP_BORDER_WIDTH, CSS_PROP_BORDER_STYLE, 1191 CSS_PROP_BORDER_COLOR 1192 }; 1193 return parseShortHand(propId, properties, 3, important); 1194 } 1195 case CSS_PROP_BORDER_TOP: 1196 // [ 'border-top-width' || 'border-style' || <color> ] | inherit 1197 { 1198 const int properties[3] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_TOP_STYLE, 1199 CSS_PROP_BORDER_TOP_COLOR 1200 }; 1201 return parseShortHand(propId, properties, 3, important); 1202 } 1203 case CSS_PROP_BORDER_RIGHT: 1204 // [ 'border-right-width' || 'border-style' || <color> ] | inherit 1205 { 1206 const int properties[3] = { CSS_PROP_BORDER_RIGHT_WIDTH, CSS_PROP_BORDER_RIGHT_STYLE, 1207 CSS_PROP_BORDER_RIGHT_COLOR 1208 }; 1209 return parseShortHand(propId, properties, 3, important); 1210 } 1211 case CSS_PROP_BORDER_BOTTOM: 1212 // [ 'border-bottom-width' || 'border-style' || <color> ] | inherit 1213 { 1214 const int properties[3] = { CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_BOTTOM_STYLE, 1215 CSS_PROP_BORDER_BOTTOM_COLOR 1216 }; 1217 return parseShortHand(propId, properties, 3, important); 1218 } 1219 case CSS_PROP_BORDER_LEFT: 1220 // [ 'border-left-width' || 'border-style' || <color> ] | inherit 1221 { 1222 const int properties[3] = { CSS_PROP_BORDER_LEFT_WIDTH, CSS_PROP_BORDER_LEFT_STYLE, 1223 CSS_PROP_BORDER_LEFT_COLOR 1224 }; 1225 return parseShortHand(propId, properties, 3, important); 1226 } 1227 case CSS_PROP_OUTLINE: 1228 // [ 'outline-color' || 'outline-style' || 'outline-width' ] | inherit 1229 { 1230 const int properties[3] = { CSS_PROP_OUTLINE_WIDTH, CSS_PROP_OUTLINE_STYLE, 1231 CSS_PROP_OUTLINE_COLOR 1232 }; 1233 return parseShortHand(propId, properties, 3, important); 1234 } 1235 case CSS_PROP_BORDER_COLOR: 1236 // <color>{1,4} | inherit 1237 { 1238 const int properties[4] = { CSS_PROP_BORDER_TOP_COLOR, CSS_PROP_BORDER_RIGHT_COLOR, 1239 CSS_PROP_BORDER_BOTTOM_COLOR, CSS_PROP_BORDER_LEFT_COLOR 1240 }; 1241 return parse4Values(propId, properties, important); 1242 } 1243 case CSS_PROP_BORDER_WIDTH: 1244 // <border-width>{1,4} | inherit 1245 { 1246 const int properties[4] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_RIGHT_WIDTH, 1247 CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_LEFT_WIDTH 1248 }; 1249 return parse4Values(propId, properties, important); 1250 } 1251 case CSS_PROP_BORDER_STYLE: 1252 // <border-style>{1,4} | inherit 1253 { 1254 const int properties[4] = { CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_RIGHT_STYLE, 1255 CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_LEFT_STYLE 1256 }; 1257 return parse4Values(propId, properties, important); 1258 } 1259 case CSS_PROP_MARGIN: 1260 // <margin-width>{1,4} | inherit 1261 { 1262 const int properties[4] = { CSS_PROP_MARGIN_TOP, CSS_PROP_MARGIN_RIGHT, 1263 CSS_PROP_MARGIN_BOTTOM, CSS_PROP_MARGIN_LEFT 1264 }; 1265 return parse4Values(propId, properties, important); 1266 } 1267 case CSS_PROP_PADDING: 1268 // <padding-width>{1,4} | inherit 1269 { 1270 const int properties[4] = { CSS_PROP_PADDING_TOP, CSS_PROP_PADDING_RIGHT, 1271 CSS_PROP_PADDING_BOTTOM, CSS_PROP_PADDING_LEFT 1272 }; 1273 return parse4Values(propId, properties, important); 1274 } 1275 case CSS_PROP_FONT: { 1276 return parseFontShorthand(important); 1277 } 1278 case CSS_PROP_LIST_STYLE: { 1279 return parseListStyleShorthand(important); 1280 } 1281 case CSS_PROP_WORD_WRAP: 1282 // normal | break-word 1283 if (id == CSS_VAL_NORMAL || id == CSS_VAL_BREAK_WORD) { 1284 valid_primitive = true; 1285 } 1286 break; 1287 1288 default: 1289 return parseSVGValue(propId, important); 1290 // #ifdef CSS_DEBUG 1291 // qCDebug(KHTML_LOG) << "illegal or CSS2 Aural property: " << val; 1292 // #endif 1293 //break; 1294 } 1295 1296 if (valid_primitive) { 1297 1298 if (id != 0) { 1299 parsedValue = new CSSPrimitiveValueImpl(id); 1300 } else if (value->unit == CSSPrimitiveValue::CSS_STRING) 1301 parsedValue = new CSSPrimitiveValueImpl(domString(value->string), 1302 (CSSPrimitiveValue::UnitTypes) value->unit); 1303 else if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && 1304 value->unit <= CSSPrimitiveValue::CSS_KHZ) { 1305 parsedValue = new CSSPrimitiveValueImpl(value->fValue, 1306 (CSSPrimitiveValue::UnitTypes) value->unit); 1307 } else if (value->unit >= Value::Q_EMS) { 1308 parsedValue = new CSSQuirkPrimitiveValueImpl(value->fValue, CSSPrimitiveValue::CSS_EMS); 1309 } 1310 valueList->next(); 1311 } 1312 if (parsedValue) { 1313 if (!valueList->current() || inShorthand()) { 1314 addProperty(propId, parsedValue, important); 1315 return true; 1316 } 1317 delete parsedValue; 1318 } 1319 return false; 1320 } 1321 1322 void CSSParser::addBackgroundValue(CSSValueImpl *&lval, CSSValueImpl *rval) 1323 { 1324 if (lval) { 1325 if (lval->isValueList()) { 1326 static_cast<CSSValueListImpl *>(lval)->append(rval); 1327 } else { 1328 CSSValueImpl *oldVal = lval; 1329 CSSValueListImpl *list = new CSSValueListImpl(CSSValueListImpl::Comma); 1330 lval = list; 1331 list->append(oldVal); 1332 list->append(rval); 1333 } 1334 } else { 1335 lval = rval; 1336 } 1337 } 1338 1339 bool CSSParser::parseBackgroundShorthand(bool important) 1340 { 1341 // Order is important in this array: 1342 // 'position' must come before color because a plain old "0" is a legal color in quirks mode 1343 // but it's usually the X coordinate of a position. 1344 // 'size' must be the next property after 'position' in order to correctly parse '/size'. 1345 // 'origin' must come before 'clip' because the first <box> value found belongs to 'origin', 1346 // the second (if any) to 'clip'. 1347 const int numProperties = 8; 1348 const int properties[numProperties] = { CSS_PROP_BACKGROUND_IMAGE, CSS_PROP_BACKGROUND_REPEAT, 1349 CSS_PROP_BACKGROUND_ATTACHMENT, CSS_PROP_BACKGROUND_POSITION, CSS_PROP_BACKGROUND_SIZE, 1350 CSS_PROP_BACKGROUND_ORIGIN, CSS_PROP_BACKGROUND_CLIP, CSS_PROP_BACKGROUND_COLOR 1351 }; 1352 1353 ShorthandScope scope(this, CSS_PROP_BACKGROUND); 1354 1355 bool parsedProperty[numProperties] = { false }; // compiler will repeat false as necessary 1356 CSSValueImpl *values[numProperties] = { nullptr }; // compiler will repeat 0 as necessary 1357 CSSValueImpl *positionYValue = nullptr; 1358 int parsedOriginIdent = 0; 1359 int i; 1360 1361 while (valueList->current()) { 1362 Value *val = valueList->current(); 1363 if (val->unit == Value::Operator && val->iValue == ',') { 1364 // We hit the end. Fill in all remaining values with the initial value. 1365 valueList->next(); 1366 for (i = 0; i < numProperties; ++i) { 1367 if (properties[i] == CSS_PROP_BACKGROUND_COLOR && parsedProperty[i]) 1368 // Color is not allowed except as the last item in a list. Reject the entire 1369 // property. 1370 { 1371 goto fail; 1372 } 1373 1374 if (!parsedProperty[i] && properties[i] != CSS_PROP_BACKGROUND_COLOR) { 1375 if (properties[i] == CSS_PROP_BACKGROUND_CLIP && parsedOriginIdent != 0) { 1376 addBackgroundValue(values[i], new CSSPrimitiveValueImpl(parsedOriginIdent)); 1377 } else { 1378 addBackgroundValue(values[i], new CSSInitialValueImpl(true/*implicit initial*/)); 1379 if (properties[i] == CSS_PROP_BACKGROUND_POSITION) { 1380 addBackgroundValue(positionYValue, new CSSInitialValueImpl(true/*implicit initial*/)); 1381 } 1382 } 1383 } 1384 parsedProperty[i] = false; 1385 } 1386 parsedOriginIdent = 0; 1387 if (!valueList->current()) { 1388 break; 1389 } 1390 } 1391 1392 bool found = false; 1393 for (i = 0; !found && i < numProperties; ++i) { 1394 if (!parsedProperty[i]) { 1395 CSSValueImpl *val1 = nullptr, *val2 = nullptr; 1396 int propId1, propId2; 1397 if (parseBackgroundProperty(properties[i], propId1, propId2, val1, val2)) { 1398 parsedProperty[i] = found = true; 1399 addBackgroundValue(values[i], val1); 1400 if (properties[i] == CSS_PROP_BACKGROUND_POSITION) { 1401 addBackgroundValue(positionYValue, val2); 1402 // after 'position' there could be '/size', check for it 1403 const Value *v = valueList->current(); 1404 if (v && v->unit == Value::Operator && v->iValue == '/') { 1405 // next property _must_ be 'size' 1406 valueList->next(); 1407 ++i; // 'size' is at the next position in properties[] array 1408 CSSValueImpl *retVal1 = nullptr, *retVal2 = nullptr; 1409 if (parseBackgroundProperty(properties[i], propId1, propId2, retVal1, retVal2)) { 1410 parsedProperty[i] = true; 1411 addBackgroundValue(values[i], retVal1); 1412 } else { 1413 goto fail; 1414 } 1415 } 1416 } else if (properties[i] == CSS_PROP_BACKGROUND_ORIGIN) { 1417 parsedOriginIdent = static_cast<CSSPrimitiveValueImpl *>(val1)->getIdent(); 1418 } else if (properties[i] == CSS_PROP_BACKGROUND_SIZE) { 1419 // we caught an invalid length|percent as background-size 1420 goto fail; 1421 } 1422 } 1423 } 1424 } 1425 1426 // if we didn't find at least one match, this is an 1427 // invalid shorthand and we have to ignore it 1428 if (!found) { 1429 goto fail; 1430 } 1431 1432 } // end of while loop 1433 1434 // Fill in any remaining properties with the initial value. 1435 for (i = 0; i < numProperties; ++i) { 1436 if (!parsedProperty[i]) { 1437 if (properties[i] == CSS_PROP_BACKGROUND_CLIP && parsedOriginIdent != 0) { 1438 addBackgroundValue(values[i], new CSSPrimitiveValueImpl(parsedOriginIdent)); 1439 } else { 1440 addBackgroundValue(values[i], new CSSInitialValueImpl(true/*implicit initial*/)); 1441 if (properties[i] == CSS_PROP_BACKGROUND_POSITION) { 1442 addBackgroundValue(positionYValue, new CSSInitialValueImpl(true/*implicit initial*/)); 1443 } 1444 } 1445 } 1446 } 1447 1448 // Now add all of the properties we found. 1449 for (i = 0; i < numProperties; i++) { 1450 if (properties[i] == CSS_PROP_BACKGROUND_POSITION) { 1451 addProperty(CSS_PROP_BACKGROUND_POSITION_X, values[i], important); 1452 addProperty(CSS_PROP_BACKGROUND_POSITION_Y, positionYValue, important); 1453 } else { 1454 addProperty(properties[i], values[i], important); 1455 } 1456 } 1457 1458 return true; 1459 1460 fail: 1461 for (int k = 0; k < numProperties; k++) { 1462 delete values[k]; 1463 } 1464 delete positionYValue; 1465 return false; 1466 } 1467 1468 static void completeMissingRadii(SharedPtr<CSSPrimitiveValueImpl> *array) 1469 { 1470 if (!array[1]) { 1471 array[1] = array[0]; // top-left => top-right 1472 } 1473 if (!array[2]) { 1474 array[2] = array[0]; // top-left => bottom-right 1475 } 1476 if (!array[3]) { 1477 array[3] = array[1]; // top-left => bottom-right 1478 } 1479 } 1480 1481 bool CSSParser::parseBorderRadius(bool important) 1482 { 1483 const int properties[4] = { CSS_PROP_BORDER_TOP_LEFT_RADIUS, 1484 CSS_PROP_BORDER_TOP_RIGHT_RADIUS, 1485 CSS_PROP_BORDER_BOTTOM_RIGHT_RADIUS, 1486 CSS_PROP_BORDER_BOTTOM_LEFT_RADIUS 1487 }; 1488 SharedPtr<CSSPrimitiveValueImpl> horiz[4], vert[4]; 1489 1490 for (int c = 0; c < 4; ++c) { 1491 horiz[c] = nullptr; 1492 vert [c] = nullptr; 1493 } 1494 1495 Value *value; 1496 1497 // Parse horizontal ones until / or done. 1498 for (int c = 0; c < 4; ++c) { 1499 value = valueList->current(); 1500 if (!value || (value->unit == Value::Operator && value->iValue == '/')) { 1501 break; //Saw slash -- exit w/o consuming as we'll use it below. 1502 } 1503 1504 if (!validUnit(value, FLength | FPercent | FNonNeg, strict)) { 1505 return false; 1506 } 1507 1508 horiz[c] = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); 1509 value = valueList->next(); 1510 } 1511 1512 if (!horiz[0]) { 1513 return false; 1514 } 1515 1516 completeMissingRadii(horiz); 1517 1518 // Do we have vertical radii afterwards? 1519 if (value && value->unit == Value::Operator && value->iValue == '/') { 1520 valueList->next(); 1521 1522 for (int c = 0; c < 4; ++c) { 1523 // qCDebug(KHTML_LOG) << c; 1524 value = valueList->current(); 1525 if (!value) { 1526 break; 1527 } 1528 1529 if (!validUnit(value, FLength | FPercent | FNonNeg, strict)) { 1530 return false; 1531 } 1532 1533 vert[c] = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); 1534 value = valueList->next(); 1535 } 1536 1537 // If we didn't parse anything, or there is stuff remaining, this is malformed 1538 if (!vert[0] || valueList->current()) { 1539 return false; 1540 } 1541 1542 completeMissingRadii(vert); 1543 } else { 1544 // Nope -- we better be at the end. 1545 if (valueList->current()) { 1546 return false; 1547 } 1548 1549 for (int c = 0; c < 4; ++c) { 1550 vert[c] = horiz[c]; 1551 } 1552 } 1553 1554 // All OK parsing, add properties 1555 for (int c = 0; c < 4; ++c) 1556 addProperty(properties[c], new CSSPrimitiveValueImpl( 1557 new PairImpl(horiz[c].get(), vert[c].get())), important); 1558 return true; 1559 } 1560 1561 bool CSSParser::parseShortHand(int propId, const int *properties, const int numProperties, bool important) 1562 { 1563 if (valueList->size() > numProperties) { 1564 // discard 1565 return false; 1566 } 1567 1568 ShorthandScope scope(this, propId); 1569 1570 // Store current numParsedProperties, we need it in case we should rollback later 1571 const int oldNumParsedProperties = numParsedProperties; 1572 1573 // Setup an array of booleans to mark which property has been found 1574 bool fnd[6]; //Trust me ;) 1575 for (int i = 0; i < numProperties; i++) { 1576 fnd[i] = false; 1577 } 1578 1579 bool discard = false; 1580 unsigned short numValidProperties = 0; 1581 bool foundValid = false; 1582 while (valueList->current()) { 1583 foundValid = false; 1584 for (int propIndex = 0; propIndex < numProperties; ++propIndex) { 1585 if (parseValue(properties[propIndex], important)) { 1586 foundValid = true; 1587 ++numValidProperties; 1588 if (fnd[propIndex]) { // found a duplicate 1589 discard = true; 1590 } else { 1591 fnd[propIndex] = true; 1592 } 1593 1594 break; 1595 } 1596 } 1597 1598 // if we didn't find at least one match, this is an 1599 // invalid shorthand and we have to ignore it 1600 if (!foundValid) { 1601 discard = true; 1602 } 1603 1604 if (discard) { 1605 break; 1606 } 1607 } 1608 1609 if (discard) { 1610 // Remove valid properties previously added by parseValue(), if any 1611 rollbackParsedProperties(oldNumParsedProperties); 1612 return false; 1613 } 1614 1615 if (numValidProperties == numProperties) { 1616 return true; 1617 } 1618 1619 // Fill in any remaining properties with the initial value. 1620 m_implicitShorthand = true; 1621 for (int i = 0; i < numProperties; ++i) { 1622 if (!fnd[i]) { 1623 addProperty(properties[i], new CSSInitialValueImpl(true/*implicit initial*/), important); 1624 } 1625 } 1626 m_implicitShorthand = false; 1627 1628 return true; 1629 } 1630 1631 bool CSSParser::parse4Values(int propId, const int *properties, bool important) 1632 { 1633 /* From the CSS 2 specs, 8.3 1634 * If there is only one value, it applies to all sides. If there are two values, the top and 1635 * bottom margins are set to the first value and the right and left margins are set to the second. 1636 * If there are three values, the top is set to the first value, the left and right are set to the 1637 * second, and the bottom is set to the third. If there are four values, they apply to the top, 1638 * right, bottom, and left, respectively. 1639 */ 1640 1641 const int num = inShorthand() ? 1 : valueList->size(); 1642 //qDebug("parse4Values: num=%d %d", num, valueList->numValues ); 1643 1644 ShorthandScope scope(this, propId); 1645 1646 const int oldNumParsedProperties = numParsedProperties; 1647 // the order is top, right, bottom, left 1648 switch (num) { 1649 case 1: { 1650 if (!parseValue(properties[0], important)) { 1651 return false; 1652 } 1653 CSSValueImpl *value = parsedProperties[numParsedProperties - 1]->value(); 1654 m_implicitShorthand = true; 1655 addProperty(properties[1], value, important); 1656 addProperty(properties[2], value, important); 1657 addProperty(properties[3], value, important); 1658 m_implicitShorthand = false; 1659 break; 1660 } 1661 case 2: { 1662 if (!parseValue(properties[0], important) || !parseValue(properties[1], important)) { 1663 rollbackParsedProperties(oldNumParsedProperties); 1664 return false; 1665 } 1666 CSSValueImpl *value = parsedProperties[numParsedProperties - 2]->value(); 1667 m_implicitShorthand = true; 1668 addProperty(properties[2], value, important); 1669 value = parsedProperties[numParsedProperties - 2]->value(); 1670 addProperty(properties[3], value, important); 1671 m_implicitShorthand = false; 1672 break; 1673 } 1674 case 3: { 1675 if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || !parseValue(properties[2], important)) { 1676 rollbackParsedProperties(oldNumParsedProperties); 1677 return false; 1678 } 1679 CSSValueImpl *value = parsedProperties[numParsedProperties - 2]->value(); 1680 m_implicitShorthand = true; 1681 addProperty(properties[3], value, important); 1682 m_implicitShorthand = false; 1683 break; 1684 } 1685 case 4: { 1686 if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || 1687 !parseValue(properties[2], important) || !parseValue(properties[3], important)) { 1688 rollbackParsedProperties(oldNumParsedProperties); 1689 return false; 1690 } 1691 break; 1692 } 1693 default: { 1694 return false; 1695 } 1696 } 1697 1698 return true; 1699 } 1700 1701 // [ <string> | <uri> | <counter> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit 1702 // in CSS 2.1 this got somewhat reduced: 1703 // [ <string> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit 1704 bool CSSParser::parseContent(int propId, bool important) 1705 { 1706 QScopedPointer<CSSValueListImpl> values( 1707 new CSSValueListImpl(CSSValueListImpl::Comma)); 1708 1709 bool isValid = true; 1710 Value *val; 1711 CSSValueImpl *parsedValue = nullptr; 1712 while ((val = valueList->current())) { 1713 parsedValue = nullptr; 1714 if (val->unit == CSSPrimitiveValue::CSS_URI) { 1715 if (styleElement) { 1716 const DOMString uri = domString(val->string); 1717 parsedValue = new CSSImageValueImpl(uri, styleElement); 1718 #ifdef CSS_DEBUG 1719 qCDebug(KHTML_LOG) << "content, url=" << uri.string() << " base=" << styleElement->baseURL().url(); 1720 #endif 1721 } 1722 } else if (val->unit == Value::Function) { 1723 // attr( X ) | counter( X [,Y] ) | counters( X, Y, [,Z] ) 1724 ValueList *args = val->function->args; 1725 QString fname = qString(val->function->name).toLower(); 1726 if (!args) { 1727 return false; 1728 } 1729 if (fname == "attr(") { 1730 if (args->size() != 1) { 1731 return false; 1732 } 1733 Value *a = args->current(); 1734 if (a->unit != CSSPrimitiveValue::CSS_IDENT) { 1735 isValid = false; 1736 break; 1737 } 1738 if (qString(a->string)[0] == '-') { 1739 isValid = false; 1740 break; 1741 } 1742 parsedValue = new CSSPrimitiveValueImpl(domString(a->string), CSSPrimitiveValue::CSS_ATTR); 1743 } else if (fname == "counter(") { 1744 parsedValue = parseCounterContent(args, false); 1745 if (!parsedValue) { 1746 return false; 1747 } 1748 } else if (fname == "counters(") { 1749 parsedValue = parseCounterContent(args, true); 1750 if (!parsedValue) { 1751 return false; 1752 } 1753 } else { 1754 return false; 1755 } 1756 1757 } else if (val->unit == CSSPrimitiveValue::CSS_IDENT) { 1758 // open-quote | close-quote | no-open-quote | no-close-quote 1759 if (val->id == CSS_VAL_OPEN_QUOTE || 1760 val->id == CSS_VAL_CLOSE_QUOTE || 1761 val->id == CSS_VAL_NO_OPEN_QUOTE || 1762 val->id == CSS_VAL_NO_CLOSE_QUOTE) { 1763 parsedValue = new CSSPrimitiveValueImpl(val->id); 1764 } 1765 } else if (val->unit == CSSPrimitiveValue::CSS_STRING) { 1766 parsedValue = new CSSPrimitiveValueImpl(domString(val->string), CSSPrimitiveValue::CSS_STRING); 1767 } 1768 1769 if (parsedValue) { 1770 values->append(parsedValue); 1771 } else { 1772 isValid = false; 1773 break; 1774 } 1775 valueList->next(); 1776 } 1777 if (isValid && values->length()) { 1778 addProperty(propId, values.take(), important); 1779 valueList->next(); 1780 return true; 1781 } 1782 1783 return false; 1784 } 1785 1786 CSSValueImpl *CSSParser::parseCounterContent(ValueList *args, bool counters) 1787 { 1788 const int argsSize = args->size(); 1789 if (counters || (argsSize != 1 && argsSize != 3)) 1790 if (!counters || (argsSize != 3 && argsSize != 5)) { 1791 return nullptr; 1792 } 1793 1794 CounterImpl *counter = new CounterImpl; 1795 Value *i = args->current(); 1796 if (i->unit != CSSPrimitiveValue::CSS_IDENT) { 1797 goto invalid; 1798 } 1799 if (qString(i->string)[0] == '-') { 1800 goto invalid; 1801 } 1802 counter->m_identifier = domString(i->string); 1803 if (counters) { 1804 i = args->next(); 1805 if (i->unit != Value::Operator || i->iValue != ',') { 1806 goto invalid; 1807 } 1808 i = args->next(); 1809 if (i->unit != CSSPrimitiveValue::CSS_STRING) { 1810 goto invalid; 1811 } 1812 counter->m_separator = domString(i->string); 1813 } 1814 counter->m_listStyle = CSS_VAL_DECIMAL - CSS_VAL_DISC; 1815 i = args->next(); 1816 if (i) { 1817 if (i->unit != Value::Operator || i->iValue != ',') { 1818 goto invalid; 1819 } 1820 i = args->next(); 1821 if (i->unit != CSSPrimitiveValue::CSS_IDENT) { 1822 goto invalid; 1823 } 1824 if (i->id < CSS_VAL_DISC || i->id > CSS_VAL__KHTML_CLOSE_QUOTE) { 1825 goto invalid; 1826 } 1827 counter->m_listStyle = i->id - CSS_VAL_DISC; 1828 } 1829 return new CSSPrimitiveValueImpl(counter); 1830 invalid: 1831 delete counter; 1832 return nullptr; 1833 } 1834 1835 CSSValueImpl *CSSParser::parseBackgroundColor() 1836 { 1837 int id = valueList->current()->id; 1838 if (id == CSS_VAL__KHTML_TEXT || id == CSS_VAL_TRANSPARENT || id == CSS_VAL_CURRENTCOLOR || 1839 (id >= CSS_VAL_AQUA && id <= CSS_VAL_WINDOWTEXT) || id == CSS_VAL_MENU || 1840 (id >= CSS_VAL_GREY && id < CSS_VAL__KHTML_TEXT && !strict)) { 1841 return new CSSPrimitiveValueImpl(id); 1842 } 1843 return parseColor(); 1844 } 1845 1846 CSSValueImpl *CSSParser::parseBackgroundImage(bool &didParse) 1847 { 1848 const Value *v = valueList->current(); 1849 if (v->id == CSS_VAL_NONE) { 1850 didParse = true; 1851 return new CSSImageValueImpl(); 1852 } else if (v->unit == CSSPrimitiveValue::CSS_URI) { 1853 didParse = true; 1854 if (styleElement) { 1855 const DOMString uri = domString(v->string); 1856 return new CSSImageValueImpl(uri, styleElement); 1857 } else { 1858 return nullptr; 1859 } 1860 } else { 1861 didParse = false; 1862 return nullptr; 1863 } 1864 } 1865 1866 CSSValueImpl *CSSParser::parseBackgroundPositionXY(BackgroundPosKind &kindOut) 1867 { 1868 int id = valueList->current()->id; 1869 if (id == CSS_VAL_LEFT || id == CSS_VAL_TOP || id == CSS_VAL_RIGHT || id == CSS_VAL_BOTTOM || id == CSS_VAL_CENTER) { 1870 int percent = 0; 1871 if (id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT) { 1872 kindOut = BgPos_X; 1873 if (id == CSS_VAL_RIGHT) { 1874 percent = 100; 1875 } 1876 } else if (id == CSS_VAL_TOP || id == CSS_VAL_BOTTOM) { 1877 kindOut = BgPos_Y; 1878 if (id == CSS_VAL_BOTTOM) { 1879 percent = 100; 1880 } 1881 } else if (id == CSS_VAL_CENTER) { 1882 // Center is ambiguous, so we're not sure which position we've found yet, an x or a y. 1883 kindOut = BgPos_Center; 1884 percent = 50; 1885 } 1886 return new CSSPrimitiveValueImpl(percent, CSSPrimitiveValue::CSS_PERCENTAGE); 1887 } 1888 1889 if (validUnit(valueList->current(), FPercent | FLength, strict)) { 1890 kindOut = BgPos_NonKW; 1891 return new CSSPrimitiveValueImpl(valueList->current()->fValue, 1892 (CSSPrimitiveValue::UnitTypes)valueList->current()->unit); 1893 } 1894 1895 return nullptr; 1896 } 1897 1898 void CSSParser::parseBackgroundPosition(CSSValueImpl *&value1, CSSValueImpl *&value2) 1899 { 1900 value1 = value2 = nullptr; 1901 1902 // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length. 1903 BackgroundPosKind value1pos; 1904 value1 = parseBackgroundPositionXY(value1pos); 1905 if (!value1) { 1906 return; 1907 } 1908 1909 // Parse the second value, if any. 1910 Value *value = valueList->next(); 1911 1912 // First check for the comma. If so, we are finished parsing this value or value pair. 1913 if (value && value->unit == Value::Operator && value->iValue == ',') { 1914 value = nullptr; 1915 } 1916 1917 bool secondValueSpecifiedAndValid = false; 1918 BackgroundPosKind value2pos = BgPos_Center; // true if not specified. 1919 if (value) { 1920 value2 = parseBackgroundPositionXY(value2pos); 1921 if (value2) { 1922 secondValueSpecifiedAndValid = true; 1923 } else { 1924 if (!inShorthand()) { 1925 delete value1; 1926 value1 = nullptr; 1927 return; 1928 } 1929 } 1930 } 1931 1932 if (!value2) 1933 // Only one value was specified. The other direction is always 50%. 1934 // If the one given was not a keyword, it should be viewed as 'x', 1935 // and so setting value2 would set y, as desired. 1936 // If the one value was a keyword, the swap below would put things in order 1937 // if needed. 1938 { 1939 value2 = new CSSPrimitiveValueImpl(50, CSSPrimitiveValue::CSS_PERCENTAGE); 1940 } 1941 1942 // Check for various failures 1943 bool ok = true; 1944 1945 // Two keywords on the same axis. 1946 if (value1pos == BgPos_X && value2pos == BgPos_X) { 1947 ok = false; 1948 } 1949 if (value1pos == BgPos_Y && value2pos == BgPos_Y) { 1950 ok = false; 1951 } 1952 1953 // Will we need to swap them? 1954 bool swap = (value1pos == BgPos_Y || value2pos == BgPos_X); 1955 1956 // If we had a non-KW value and a keyword value that's in the "wrong" position, 1957 // this is malformed (#169612) 1958 if (swap && (value1pos == BgPos_NonKW || value2pos == BgPos_NonKW)) { 1959 ok = false; 1960 } 1961 1962 if (!ok) { 1963 delete value1; 1964 delete value2; 1965 value1 = nullptr; 1966 value2 = nullptr; 1967 return; 1968 } 1969 1970 if (swap) { 1971 // Swap our two values. 1972 CSSValueImpl *val = value2; 1973 value2 = value1; 1974 value1 = val; 1975 } 1976 1977 if (secondValueSpecifiedAndValid) { 1978 valueList->next(); 1979 } 1980 } 1981 1982 CSSValueImpl *CSSParser::parseBackgroundSize() 1983 { 1984 Value *value = valueList->current(); 1985 1986 // Parse the first value. 1987 CSSPrimitiveValueImpl *parsedValue1; 1988 1989 if (value->id == CSS_VAL_COVER || value->id == CSS_VAL_CONTAIN) { 1990 valueList->next(); 1991 return new CSSPrimitiveValueImpl(value->id); 1992 } 1993 1994 if (value->id == CSS_VAL_AUTO) { 1995 parsedValue1 = new CSSPrimitiveValueImpl(CSS_VAL_AUTO); 1996 } else if (validUnit(value, FLength | FPercent | FNonNeg, strict)) { 1997 parsedValue1 = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); 1998 } else { 1999 return nullptr; 2000 } 2001 2002 // Parse the second value, if any. 2003 value = valueList->next(); 2004 2005 // First check for the comma. If so, we are finished parsing this value or value pair. 2006 if (value && value->unit == Value::Operator && value->iValue == ',') { 2007 value = nullptr; 2008 } 2009 2010 CSSPrimitiveValueImpl *parsedValue2 = nullptr; 2011 if (value) { 2012 if (value->id == CSS_VAL_AUTO) { 2013 parsedValue2 = new CSSPrimitiveValueImpl(CSS_VAL_AUTO); 2014 } else if (validUnit(value, FLength | FPercent | FNonNeg, strict)) { 2015 parsedValue2 = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); 2016 } else if (!inShorthand()) { 2017 delete parsedValue1; 2018 return nullptr; 2019 } 2020 } 2021 2022 if (parsedValue2) { 2023 valueList->next(); 2024 } else { 2025 // If only one value is given the second is assumed to be ‘auto’ 2026 parsedValue2 = new CSSPrimitiveValueImpl(CSS_VAL_AUTO); 2027 } 2028 2029 PairImpl *pair = new PairImpl(parsedValue1, parsedValue2); 2030 return new CSSPrimitiveValueImpl(pair); 2031 } 2032 2033 bool CSSParser::parseBackgroundProperty(int propId, int &propId1, int &propId2, 2034 CSSValueImpl *&retValue1, CSSValueImpl *&retValue2) 2035 { 2036 #ifdef CSS_DEBUG 2037 qCDebug(KHTML_LOG) << "parseBackgroundProperty()"; 2038 qCDebug(KHTML_LOG) << "LOOKING FOR: " << getPropertyName(propId).string(); 2039 #endif 2040 Value *val; 2041 CSSValueListImpl *value = new CSSValueListImpl(CSSValueListImpl::Comma); 2042 CSSValueListImpl *value2 = new CSSValueListImpl(CSSValueListImpl::Comma); 2043 bool expectComma = false; 2044 2045 retValue1 = retValue2 = nullptr; 2046 propId1 = propId; 2047 propId2 = propId; 2048 if (propId == CSS_PROP_BACKGROUND_POSITION) { 2049 propId1 = CSS_PROP_BACKGROUND_POSITION_X; 2050 propId2 = CSS_PROP_BACKGROUND_POSITION_Y; 2051 } 2052 2053 while ((val = valueList->current())) { 2054 CSSValueImpl *currValue = nullptr, *currValue2 = nullptr; 2055 if (expectComma) { 2056 if (val->unit != Value::Operator || val->iValue != ',') { 2057 goto failed; 2058 } 2059 valueList->next(); 2060 expectComma = false; 2061 } else { 2062 switch (propId) { 2063 case CSS_PROP_BACKGROUND_ATTACHMENT: 2064 if (val->id == CSS_VAL_SCROLL || val->id == CSS_VAL_FIXED || val->id == CSS_VAL_LOCAL) { 2065 currValue = new CSSPrimitiveValueImpl(val->id); 2066 valueList->next(); 2067 } 2068 break; 2069 case CSS_PROP_BACKGROUND_COLOR: 2070 currValue = parseBackgroundColor(); 2071 if (currValue) { 2072 valueList->next(); 2073 } 2074 break; 2075 case CSS_PROP_BACKGROUND_IMAGE: { 2076 bool didParse = false; 2077 currValue = parseBackgroundImage(didParse); 2078 if (didParse) { 2079 valueList->next(); 2080 } 2081 break; 2082 } 2083 case CSS_PROP_BACKGROUND_CLIP: 2084 case CSS_PROP_BACKGROUND_ORIGIN: 2085 if (val->id == CSS_VAL_BORDER_BOX || val->id == CSS_VAL_PADDING_BOX || val->id == CSS_VAL_CONTENT_BOX) { 2086 currValue = new CSSPrimitiveValueImpl(val->id); 2087 valueList->next(); 2088 } 2089 break; 2090 case CSS_PROP_BACKGROUND_POSITION: 2091 parseBackgroundPosition(currValue, currValue2); 2092 // parseBackgroundPosition advances the valueList pointer 2093 break; 2094 case CSS_PROP_BACKGROUND_POSITION_X: { 2095 BackgroundPosKind pos; 2096 currValue = parseBackgroundPositionXY(pos); 2097 if (currValue) { 2098 if (pos == BgPos_Y) { 2099 delete currValue; 2100 currValue = nullptr; 2101 } else { 2102 valueList->next(); 2103 } 2104 } 2105 break; 2106 } 2107 case CSS_PROP_BACKGROUND_POSITION_Y: { 2108 BackgroundPosKind pos; 2109 currValue = parseBackgroundPositionXY(pos); 2110 if (currValue) { 2111 if (pos == BgPos_X) { 2112 delete currValue; 2113 currValue = nullptr; 2114 } else { 2115 valueList->next(); 2116 } 2117 } 2118 break; 2119 } 2120 case CSS_PROP_BACKGROUND_REPEAT: 2121 if (val->id >= CSS_VAL_REPEAT && val->id <= CSS_VAL_NO_REPEAT) { 2122 currValue = new CSSPrimitiveValueImpl(val->id); 2123 valueList->next(); 2124 } 2125 break; 2126 case CSS_PROP_BACKGROUND_SIZE: 2127 currValue = parseBackgroundSize(); 2128 // parseBackgroundSize advances the valueList pointer 2129 break; 2130 } 2131 2132 if (!currValue) { 2133 goto failed; 2134 } 2135 2136 // When parsing the 'background' shorthand property return the parsed value... 2137 if (inShorthand()) { 2138 retValue1 = currValue; 2139 if (currValue2) { 2140 retValue2 = currValue2; 2141 } 2142 delete value; delete value2; 2143 return true; 2144 } 2145 2146 // ...if not in shorthand, append to the list of value for the property 2147 // and expect a comma for the next value (if any) 2148 value->append(currValue); 2149 if (currValue2) { 2150 value2->append(currValue2); 2151 } 2152 expectComma = true; 2153 } 2154 } 2155 2156 // Now return the value list 2157 if (value->length() > 0) { 2158 retValue1 = value; 2159 if (value2->length() > 0) { 2160 retValue2 = value2; 2161 } else { 2162 delete value2; 2163 } 2164 return true; 2165 } 2166 2167 failed: 2168 delete value; delete value2; 2169 return false; 2170 } 2171 2172 bool CSSParser::parseShape(int propId, bool important) 2173 { 2174 Value *value = valueList->current(); 2175 ValueList *args = value->function->args; 2176 QString fname = qString(value->function->name).toLower(); 2177 //qDebug( "parseShape: fname: %d", fname.toLatin1().constData() ); 2178 if (fname != "rect(" || !args) { 2179 return false; 2180 } 2181 2182 const int argsSize = args->size(); 2183 // rect( t, r, b, l ) || rect( t r b l ) 2184 if (argsSize != 4 && argsSize != 7) { 2185 return false; 2186 } 2187 RectImpl *rect = new RectImpl(); 2188 bool valid = true; 2189 int i = 0; 2190 Value *a = args->current(); 2191 while (a) { 2192 CSSPrimitiveValueImpl *length; 2193 if (a->id == CSS_VAL_AUTO) { 2194 length = new CSSPrimitiveValueImpl(CSS_VAL_AUTO); 2195 } else { 2196 valid = validUnit(a, FLength, strict); 2197 if (!valid) { 2198 break; 2199 } 2200 length = new CSSPrimitiveValueImpl(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit); 2201 } 2202 2203 if (i == 0) { 2204 rect->setTop(length); 2205 } else if (i == 1) { 2206 rect->setRight(length); 2207 } else if (i == 2) { 2208 rect->setBottom(length); 2209 } else { 2210 rect->setLeft(length); 2211 } 2212 a = args->next(); 2213 if (a && argsSize == 7) { 2214 if (a->unit == Value::Operator && a->iValue == ',') { 2215 a = args->next(); 2216 } else { 2217 valid = false; 2218 break; 2219 } 2220 } 2221 i++; 2222 } 2223 if (valid) { 2224 addProperty(propId, new CSSPrimitiveValueImpl(rect), important); 2225 valueList->next(); 2226 return true; 2227 } 2228 delete rect; 2229 return false; 2230 } 2231 2232 // [ [ <'font-style'> || <'font-variant'> || <'font-weight'> ]? <'font-size'> [ / <'line-height'> ]? <'font-family'> ] | 2233 // caption | icon | menu | message-box | small-caption | status-bar 2234 bool CSSParser::parseFontShorthand(bool important) 2235 { 2236 Value *value = valueList->current(); 2237 if (valueList->size() == 1) { 2238 // Must be a system font identifier 2239 if (value->id >= CSS_VAL_CAPTION && value->id <= CSS_VAL_STATUS_BAR) { 2240 addProperty(CSS_PROP_FONT, new CSSPrimitiveValueImpl(value->id), important); 2241 return true; 2242 } 2243 return false; 2244 } 2245 CSSValueListImpl *family = nullptr; 2246 CSSPrimitiveValueImpl *style = nullptr, *variant = nullptr, *weight = nullptr, *size = nullptr, *lineHeight = nullptr; 2247 2248 ShorthandScope scope(this, CSS_PROP_FONT); 2249 2250 // optional font-style, font-variant and font-weight 2251 while (value) { 2252 //qCWarning(KHTML_LOG) << "got value" << value->id << "/" << 2253 //(value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT ? qString(value->string) : QString()); 2254 const int id = value->id; 2255 2256 if (id == CSS_VAL_NORMAL) { 2257 // do nothing, it's the initial value for all three 2258 } else if (id == CSS_VAL_ITALIC || id == CSS_VAL_OBLIQUE) { 2259 if (style) { 2260 goto invalid; 2261 } 2262 style = new CSSPrimitiveValueImpl(id); 2263 } else if (id == CSS_VAL_SMALL_CAPS) { 2264 if (variant) { 2265 goto invalid; 2266 } 2267 variant = new CSSPrimitiveValueImpl(id); 2268 } else if (int weightValueId = parseFontWeight(value, true)) { 2269 if (weight) { 2270 goto invalid; 2271 } 2272 weight = new CSSPrimitiveValueImpl(weightValueId); 2273 } else { 2274 break; 2275 } 2276 2277 value = valueList->next(); 2278 } 2279 2280 if (!value) { 2281 goto invalid; 2282 } 2283 2284 // set undefined values to default 2285 if (!style) { 2286 style = new CSSPrimitiveValueImpl(CSS_VAL_NORMAL); 2287 } 2288 if (!variant) { 2289 variant = new CSSPrimitiveValueImpl(CSS_VAL_NORMAL); 2290 } 2291 if (!weight) { 2292 weight = new CSSPrimitiveValueImpl(CSS_VAL_NORMAL); 2293 } 2294 2295 //qCWarning(KHTML_LOG) << "parsed style, variant, weight:" << style->cssText() << variant->cssText() << weight->cssText(); 2296 2297 // now a font-size _must_ come 2298 // <absolute-size> | <relative-size> | <length> | <percentage> | inherit 2299 if (value->id >= CSS_VAL_XX_SMALL && value->id <= CSS_VAL_LARGER) { 2300 size = new CSSPrimitiveValueImpl(value->id); 2301 } else if (validUnit(value, FLength | FPercent | FNonNeg, strict)) { 2302 size = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); 2303 } 2304 if (!size) { 2305 goto invalid; 2306 } 2307 //qCWarning(KHTML_LOG) << "parsed size:" << size->cssText(); 2308 2309 // now /line-height could come, next font-family _must_ come 2310 value = valueList->next(); 2311 if (!value) { 2312 goto invalid; 2313 } 2314 2315 if (value->unit == Value::Operator && value->iValue == '/') { 2316 // line-height 2317 value = valueList->next(); 2318 if (!value) { 2319 goto invalid; 2320 } 2321 if (value->id == CSS_VAL_NORMAL) { 2322 // default value, nothing to do 2323 } else if (validUnit(value, FNumber | FLength | FPercent | FNonNeg, strict)) { 2324 lineHeight = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); 2325 } else { 2326 goto invalid; 2327 } 2328 value = valueList->next(); 2329 if (!value) { 2330 goto invalid; 2331 } 2332 } 2333 // if undefined set to default 2334 if (!lineHeight) { 2335 lineHeight = new CSSPrimitiveValueImpl(CSS_VAL_NORMAL); 2336 } 2337 //qCWarning(KHTML_LOG) << "parsed line-height:" << lineHeight->cssText(); 2338 2339 // font-family _must_ come now 2340 family = parseFontFamily(); 2341 2342 if (valueList->current() || !family) { 2343 goto invalid; 2344 } 2345 //qCWarning(KHTML_LOG) << "parsed family:" << family->cssText(); 2346 2347 addProperty(CSS_PROP_FONT_FAMILY, family, important); 2348 addProperty(CSS_PROP_FONT_STYLE, style, important); 2349 addProperty(CSS_PROP_FONT_VARIANT, variant, important); 2350 addProperty(CSS_PROP_FONT_WEIGHT, weight, important); 2351 addProperty(CSS_PROP_FONT_SIZE, size, important); 2352 addProperty(CSS_PROP_LINE_HEIGHT, lineHeight, important); 2353 return true; 2354 2355 invalid: 2356 //qCWarning(KHTML_LOG) << " -> invalid"; 2357 delete family; 2358 delete style; 2359 delete variant; 2360 delete weight; 2361 delete size; 2362 delete lineHeight; 2363 2364 return false; 2365 } 2366 2367 int CSSParser::parseFontWeight(Value *val, bool strict) 2368 { 2369 const int valId = val->id; 2370 if (valId >= CSS_VAL_NORMAL && valId <= CSS_VAL_900) { 2371 // Valid primitive 2372 return valId; 2373 } 2374 if (validUnit(val, FInteger | FNonNeg, strict)) { 2375 int weight = static_cast<int>(val->fValue); 2376 if ((weight % 100)) { 2377 // Invalid 2378 return 0; 2379 } 2380 weight /= 100; 2381 if (weight >= 1 && weight <= 9) { 2382 return (CSS_VAL_100 + weight - 1); 2383 } 2384 } 2385 return 0; 2386 } 2387 2388 CSSValueListImpl *CSSParser::parseFontFamily() 2389 { 2390 // qCDebug(KHTML_LOG) << "CSSParser::parseFontFamily current=" << valueList->currentValue; 2391 CSSValueListImpl *list = new CSSValueListImpl(CSSValueListImpl::Comma); 2392 Value *value = valueList->current(); 2393 QString currFace; 2394 2395 while (value) { 2396 // qCDebug(KHTML_LOG) << "got value " << value->id << " / " 2397 // << (value->unit == CSSPrimitiveValue::CSS_STRING || 2398 // value->unit == CSSPrimitiveValue::CSS_IDENT ? qString( value->string ) : QString() ); 2399 Value *nextValue = valueList->next(); 2400 bool nextValBreaksFont = !nextValue || (nextValue->unit == Value::Operator && nextValue->iValue == ','); 2401 bool nextValIsFontName = nextValue && 2402 ((nextValue->id >= CSS_VAL_SERIF && nextValue->id <= CSS_VAL_MONOSPACE) || 2403 (nextValue->unit == CSSPrimitiveValue::CSS_STRING || 2404 nextValue->unit == CSSPrimitiveValue::CSS_IDENT)); 2405 2406 if (value->id == CSS_VAL_INHERIT && inShorthand() && currFace.isNull() && nextValBreaksFont) { 2407 // fail (#169610) 2408 delete list; 2409 return nullptr; 2410 } 2411 2412 if (value->id >= CSS_VAL_SERIF && value->id <= CSS_VAL_MONOSPACE) { 2413 if (!currFace.isNull()) { 2414 currFace += ' '; 2415 currFace += qString(value->string); 2416 } else if (nextValBreaksFont || !nextValIsFontName) { 2417 if (!currFace.isNull()) { 2418 list->append(new FontFamilyValueImpl(currFace)); 2419 currFace.clear(); 2420 } 2421 list->append(new CSSPrimitiveValueImpl(value->id)); 2422 } else { 2423 currFace = qString(value->string); 2424 } 2425 } else if (value->unit == CSSPrimitiveValue::CSS_STRING) { 2426 // Strings never share in a family name. 2427 currFace.clear(); 2428 list->append(new FontFamilyValueImpl(qString(value->string))); 2429 } else if (value->unit == CSSPrimitiveValue::CSS_IDENT) { 2430 if (!currFace.isNull()) { 2431 currFace += ' '; 2432 currFace += qString(value->string); 2433 } else if (nextValBreaksFont || !nextValIsFontName) { 2434 if (!currFace.isNull()) { 2435 list->append(new FontFamilyValueImpl(currFace)); 2436 currFace.clear(); 2437 } 2438 list->append(new FontFamilyValueImpl(qString(value->string))); 2439 } else { 2440 currFace = qString(value->string); 2441 } 2442 } else { 2443 //qCDebug(KHTML_LOG) << "invalid family part"; 2444 break; 2445 } 2446 2447 if (!nextValue) { 2448 break; 2449 } 2450 2451 if (nextValBreaksFont) { 2452 value = valueList->next(); 2453 if (!currFace.isNull()) { 2454 list->append(new FontFamilyValueImpl(currFace)); 2455 } 2456 currFace.clear(); 2457 } else if (nextValIsFontName) { 2458 value = nextValue; 2459 } else { 2460 break; 2461 } 2462 } 2463 2464 if (!currFace.isNull()) { 2465 list->append(new FontFamilyValueImpl(currFace)); 2466 } 2467 2468 if (!list->length()) { 2469 delete list; 2470 list = nullptr; 2471 } 2472 return list; 2473 } 2474 2475 bool CSSParser::parseFontFaceSrc() 2476 { 2477 CSSValueListImpl *values = new CSSValueListImpl(CSSValueListImpl::Comma); 2478 Value *val; 2479 bool expectComma = false; 2480 bool allowFormat = false; 2481 bool failed = false; 2482 CSSFontFaceSrcValueImpl *uriValue = nullptr; 2483 while ((val = valueList->current())) { 2484 CSSFontFaceSrcValueImpl *parsedValue = nullptr; 2485 if (val->unit == CSSPrimitiveValue::CSS_URI && !expectComma && styleElement) { 2486 const DOMString uri = domString(val->string).trimSpaces(); 2487 parsedValue = new CSSFontFaceSrcValueImpl(DOMString(QUrl(styleElement->baseURL()).resolved(QUrl(uri.string())).toString()), false /*local*/); 2488 uriValue = parsedValue; 2489 allowFormat = true; 2490 expectComma = true; 2491 } else if (val->unit == Value::Function) { 2492 // There are two allowed functions: local() and format(). 2493 // For both we expect a string argument 2494 ValueList *args = val->function->args; 2495 if (args && args->size() == 1 && 2496 (args->current()->unit == CSSPrimitiveValue::CSS_STRING || 2497 args->current()->unit == CSSPrimitiveValue::CSS_IDENT)) { 2498 if (!strcasecmp(domString(val->function->name), "local(") && !expectComma) { 2499 expectComma = true; 2500 allowFormat = false; 2501 Value *a = args->current(); 2502 uriValue = nullptr; 2503 parsedValue = new CSSFontFaceSrcValueImpl(domString(a->string), true /*local src*/); 2504 } else if (!strcasecmp(domString(val->function->name), "format(") && allowFormat && uriValue) { 2505 expectComma = true; 2506 allowFormat = false; 2507 uriValue->setFormat(domString(args->current()->string)); 2508 uriValue = nullptr; 2509 valueList->next(); 2510 continue; 2511 } 2512 } 2513 } else if (val->unit == Value::Operator && val->iValue == ',' && expectComma) { 2514 expectComma = false; 2515 allowFormat = false; 2516 uriValue = nullptr; 2517 valueList->next(); 2518 continue; 2519 } 2520 2521 if (parsedValue) { 2522 values->append(parsedValue); 2523 } else { 2524 failed = true; 2525 break; 2526 } 2527 valueList->next(); 2528 } 2529 2530 if (values->length() && !failed) { 2531 addProperty(CSS_PROP_SRC, values, important); 2532 valueList->next(); 2533 return true; 2534 } else { 2535 delete values; 2536 } 2537 2538 return false; 2539 } 2540 2541 // [ <list-style-type> || <list-style-position> || <list-style-image> ] 2542 bool CSSParser::parseListStyleShorthand(bool important) 2543 { 2544 if (valueList->size() > 3) { 2545 // discard 2546 return false; 2547 } 2548 2549 CSSValueImpl *type = nullptr; 2550 CSSValueImpl *position = nullptr; 2551 CSSValueImpl *image = nullptr; 2552 2553 int numberOfNone = 0; 2554 Value *value = valueList->current(); 2555 while (value) { 2556 const int valId = value->id; 2557 if (valId == CSS_VAL_NONE) { 2558 // just count 2559 ++numberOfNone; 2560 } else if (valId >= CSS_VAL_DISC && valId <= CSS_VAL__KHTML_CLOSE_QUOTE) { 2561 if (!type) { 2562 type = new CSSPrimitiveValueImpl(valId); 2563 } else { 2564 goto invalid; 2565 } 2566 } else if (valId == CSS_VAL_INSIDE || valId == CSS_VAL_OUTSIDE) { 2567 if (!position) { 2568 position = new CSSPrimitiveValueImpl(valId); 2569 } else { 2570 goto invalid; 2571 } 2572 } else if (value->unit == CSSPrimitiveValue::CSS_URI) { 2573 if (!image) { 2574 // ### allow string in non strict mode? 2575 if (styleElement) { 2576 const DOMString uri = domString(value->string); 2577 image = new CSSImageValueImpl(uri, styleElement); 2578 } 2579 } else { 2580 goto invalid; 2581 } 2582 } else { 2583 goto invalid; 2584 } 2585 value = valueList->next(); 2586 } 2587 2588 // Set whichever of 'list-style-type' and 'list-style-image' are not otherwise specified, to 'none' 2589 switch (numberOfNone) { 2590 case 0: { 2591 break; 2592 } 2593 case 1: { 2594 if (image && type) { 2595 goto invalid; 2596 } 2597 if (!image) { 2598 image = new CSSImageValueImpl(); 2599 } 2600 if (!type) { 2601 type = new CSSPrimitiveValueImpl(CSS_VAL_NONE); 2602 } 2603 break; 2604 } 2605 case 2: { 2606 if (image || type) { 2607 goto invalid; 2608 } else { 2609 image = new CSSImageValueImpl(); 2610 type = new CSSPrimitiveValueImpl(CSS_VAL_NONE); 2611 } 2612 break; 2613 } 2614 default: // numberOfNone == 3 2615 goto invalid; 2616 } 2617 2618 // The shorthand is valid: fill-in any remaining properties with default value 2619 if (!type) { 2620 type = new CSSPrimitiveValueImpl(CSS_VAL_DISC); 2621 } 2622 if (!position) { 2623 position = new CSSPrimitiveValueImpl(CSS_VAL_OUTSIDE); 2624 } 2625 if (!image) { 2626 image = new CSSImageValueImpl(); 2627 } 2628 addProperty(CSS_PROP_LIST_STYLE_TYPE, type, important); 2629 addProperty(CSS_PROP_LIST_STYLE_POSITION, position, important); 2630 addProperty(CSS_PROP_LIST_STYLE_IMAGE, image, important); 2631 2632 return true; 2633 2634 invalid: 2635 delete type; 2636 delete position; 2637 delete image; 2638 2639 return false; 2640 } 2641 2642 bool CSSParser::parseColorParameters(Value *value, int *colorArray, bool parseAlpha) 2643 { 2644 ValueList *args = value->function->args; 2645 Value *v = args->current(); 2646 2647 // Get the first value 2648 if (!validUnit(v, FInteger | FPercent, true)) { 2649 return false; 2650 } 2651 bool isPercent = (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE); 2652 colorArray[0] = static_cast<int>(v->fValue * (isPercent ? 256.0 / 100.0 : 1.0)); 2653 for (int i = 1; i < 3; i++) { 2654 v = args->next(); 2655 if (v->unit != Value::Operator && v->iValue != ',') { 2656 return false; 2657 } 2658 v = args->next(); 2659 if (!validUnit(v, (isPercent ? FPercent : FInteger), true)) { 2660 return false; 2661 } 2662 colorArray[i] = static_cast<int>(v->fValue * (isPercent ? 256.0 / 100.0 : 1.0)); 2663 } 2664 if (parseAlpha) { 2665 v = args->next(); 2666 if (v->unit != Value::Operator && v->iValue != ',') { 2667 return false; 2668 } 2669 v = args->next(); 2670 if (!validUnit(v, FNumber, true)) { 2671 return false; 2672 } 2673 colorArray[3] = static_cast<int>(qMax(0.0, qMin(1.0, v->fValue)) * 255); //krazy:exclude=qminmax 2674 } 2675 return true; 2676 } 2677 2678 // CSS3 specification defines the format of a HSL color as 2679 // hsl(<number>, <percent>, <percent>) 2680 // and with alpha, the format is 2681 // hsla(<number>, <percent>, <percent>, <number>) 2682 // The first value, HUE, is in an angle with a value between 0 and 360 2683 bool CSSParser::parseHSLParameters(Value *value, double *colorArray, bool parseAlpha) 2684 { 2685 ValueList *args = value->function->args; 2686 Value *v = args->current(); 2687 // Get the first value 2688 if (!validUnit(v, FInteger, true)) { 2689 return false; 2690 } 2691 // normalize the Hue value and change it to be between 0 and 1.0 2692 colorArray[0] = (((static_cast<int>(v->fValue) % 360) + 360) % 360) / 360.0; 2693 for (int i = 1; i < 3; i++) { 2694 v = args->next(); 2695 if (v->unit != Value::Operator && v->iValue != ',') { 2696 return false; 2697 } 2698 v = args->next(); 2699 if (!validUnit(v, FPercent, true)) { 2700 return false; 2701 } 2702 colorArray[i] = qMax(0.0, qMin(100.0, v->fValue)) / 100.0; // needs to be value between 0 and 1.0, krazy:exclude=qminmax 2703 } 2704 if (parseAlpha) { 2705 v = args->next(); 2706 if (v->unit != Value::Operator && v->iValue != ',') { 2707 return false; 2708 } 2709 v = args->next(); 2710 if (!validUnit(v, FNumber, true)) { 2711 return false; 2712 } 2713 colorArray[3] = qMax(0.0, qMin(1.0, v->fValue)); //krazy:exclude=qminmax 2714 } 2715 return true; 2716 } 2717 2718 static int hex2int(unsigned short c, bool *error) 2719 { 2720 if (c >= '0' && c <= '9') { 2721 return c - '0'; 2722 } else if (c >= 'A' && c <= 'F') { 2723 return 10 + c - 'A'; 2724 } else if (c >= 'a' && c <= 'f') { 2725 return 10 + c - 'a'; 2726 } else { 2727 *error = true; 2728 return -1; 2729 } 2730 } 2731 2732 static bool parseColor(int unit, const QString &name, QRgb &rgb, bool strict) 2733 { 2734 int len = name.length(); 2735 2736 if (!len) { 2737 return false; 2738 } 2739 2740 if (unit == CSSPrimitiveValue::CSS_RGBCOLOR || !strict) { 2741 const unsigned short *c = 2742 reinterpret_cast<const unsigned short *>(name.unicode()); 2743 2744 rgb = 0xff; // fixed alpha 2745 if (len == 6) { 2746 // RRGGBB 2747 bool error = false; 2748 for (int i = 0; i < 6; ++i, ++c) { 2749 rgb = rgb << 4 | hex2int(*c, &error); 2750 } 2751 if (!error) { 2752 return true; 2753 } 2754 } else if (len == 3) { 2755 // RGB, shortcut for RRGGBB 2756 bool error = false; 2757 for (int i = 0; i < 3; ++i, ++c) { 2758 rgb = rgb << 8 | 0x11 * hex2int(*c, &error); 2759 } 2760 if (!error) { 2761 return true; 2762 } 2763 } 2764 } 2765 2766 if (unit == CSSPrimitiveValue::CSS_IDENT) { 2767 // try a little harder 2768 QColor tc; 2769 tc.setNamedColor(name.toLower()); 2770 if (tc.isValid()) { 2771 rgb = tc.rgba(); 2772 return true; 2773 } 2774 } 2775 2776 return false; 2777 } 2778 2779 CSSPrimitiveValueImpl *CSSParser::parseColor() 2780 { 2781 return parseColorFromValue(valueList->current()); 2782 } 2783 2784 CSSPrimitiveValueImpl *CSSParser::parseColorFromValue(Value *value) 2785 { 2786 QRgb c = khtml::transparentColor; 2787 if (!strict && value->unit == CSSPrimitiveValue::CSS_NUMBER && // color: 000000 (quirk) 2788 value->fValue >= 0. && value->fValue < 1000000.) { 2789 QString str; 2790 str.sprintf("%06d", (int)(value->fValue + .5)); 2791 if (!::parseColor(CSSPrimitiveValue::CSS_RGBCOLOR, str, c, strict)) { 2792 return nullptr; 2793 } 2794 } else if (value->unit == CSSPrimitiveValue::CSS_RGBCOLOR || // color: #ff0000 2795 value->unit == CSSPrimitiveValue::CSS_IDENT || // color: red || color: ff0000 (quirk) 2796 (!strict && value->unit == CSSPrimitiveValue::CSS_DIMENSION)) { // color: 00ffff (quirk) 2797 if (!::parseColor(value->unit, qString(value->string), c, strict)) { 2798 return nullptr; 2799 } 2800 } else if (value->unit == Value::Function && 2801 value->function->args != nullptr && 2802 value->function->args->size() == 5 /* rgb + two commas */ && 2803 qString(value->function->name).toLower() == "rgb(") { 2804 int colorValues[3]; 2805 if (!parseColorParameters(value, colorValues, false)) { 2806 return nullptr; 2807 } 2808 colorValues[0] = qMax(0, qMin(255, colorValues[0])); 2809 colorValues[1] = qMax(0, qMin(255, colorValues[1])); 2810 colorValues[2] = qMax(0, qMin(255, colorValues[2])); 2811 c = qRgb(colorValues[0], colorValues[1], colorValues[2]); 2812 } else if (value->unit == Value::Function && 2813 value->function->args != nullptr && 2814 value->function->args->size() == 7 /* rgba + three commas */ && 2815 domString(value->function->name).lower() == "rgba(") { 2816 int colorValues[4]; 2817 if (!parseColorParameters(value, colorValues, true)) { 2818 return nullptr; 2819 } 2820 colorValues[0] = qMax(0, qMin(255, colorValues[0])); 2821 colorValues[1] = qMax(0, qMin(255, colorValues[1])); 2822 colorValues[2] = qMax(0, qMin(255, colorValues[2])); 2823 c = qRgba(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); 2824 } else if (value->unit == Value::Function && 2825 value->function->args != nullptr && 2826 value->function->args->size() == 5 /* hsl + two commas */ && 2827 domString(value->function->name).lower() == "hsl(") { 2828 double colorValues[3]; 2829 if (!parseHSLParameters(value, colorValues, false)) { 2830 return nullptr; 2831 } 2832 c = khtml::qRgbaFromHsla(colorValues[0], colorValues[1], colorValues[2], 1.0); 2833 } else if (value->unit == Value::Function && 2834 value->function->args != nullptr && 2835 value->function->args->size() == 7 /* hsla + three commas */ && 2836 domString(value->function->name).lower() == "hsla(") { 2837 double colorValues[4]; 2838 if (!parseHSLParameters(value, colorValues, true)) { 2839 return nullptr; 2840 } 2841 c = khtml::qRgbaFromHsla(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); 2842 } else { 2843 return nullptr; 2844 } 2845 2846 return new CSSPrimitiveValueImpl(c); 2847 } 2848 2849 // This class tracks parsing state for shadow values. If it goes out of scope (e.g., due to an early return) 2850 // without the allowBreak bit being set, then it will clean up all of the objects and destroy them. 2851 struct ShadowParseContext { 2852 ShadowParseContext() 2853 : values(nullptr), x(nullptr), y(nullptr), blur(nullptr), color(nullptr), 2854 allowX(true), allowY(false), allowBlur(false), allowColor(true), 2855 allowBreak(true) 2856 {} 2857 2858 ~ShadowParseContext() 2859 { 2860 if (!allowBreak) { 2861 delete values; 2862 delete x; 2863 delete y; 2864 delete blur; 2865 delete color; 2866 } 2867 } 2868 2869 bool allowLength() 2870 { 2871 return allowX || allowY || allowBlur; 2872 } 2873 2874 bool failed() 2875 { 2876 return allowBreak = false; 2877 } 2878 2879 void commitValue() 2880 { 2881 // Handle the ,, case gracefully by doing nothing. 2882 if (x || y || blur || color) { 2883 if (!values) { 2884 values = new CSSValueListImpl(CSSValueListImpl::Comma); 2885 } 2886 2887 // Construct the current shadow value and add it to the list. 2888 values->append(new ShadowValueImpl(x, y, blur, color)); 2889 } 2890 2891 // Now reset for the next shadow value. 2892 x = y = blur = color = nullptr; 2893 allowX = allowColor = allowBreak = true; 2894 allowY = allowBlur = false; 2895 } 2896 2897 void commitLength(Value *v) 2898 { 2899 CSSPrimitiveValueImpl *val = new CSSPrimitiveValueImpl(v->fValue, 2900 (CSSPrimitiveValue::UnitTypes)v->unit); 2901 if (allowX) { 2902 x = val; 2903 allowX = false; allowY = true; allowColor = false; allowBreak = false; 2904 } else if (allowY) { 2905 y = val; 2906 allowY = false; allowBlur = true; allowColor = true; allowBreak = true; 2907 } else if (allowBlur) { 2908 blur = val; 2909 allowBlur = false; 2910 } else { 2911 delete val; 2912 } 2913 } 2914 2915 void commitColor(CSSPrimitiveValueImpl *val) 2916 { 2917 color = val; 2918 allowColor = false; 2919 if (allowX) { 2920 allowBreak = false; 2921 } else { 2922 allowBlur = false; 2923 } 2924 } 2925 2926 CSSValueListImpl *values; 2927 CSSPrimitiveValueImpl *x; 2928 CSSPrimitiveValueImpl *y; 2929 CSSPrimitiveValueImpl *blur; 2930 CSSPrimitiveValueImpl *color; 2931 2932 bool allowX; 2933 bool allowY; 2934 bool allowBlur; 2935 bool allowColor; 2936 bool allowBreak; 2937 }; 2938 2939 bool CSSParser::parseShadow(int propId, bool important) 2940 { 2941 ShadowParseContext context; 2942 Value *val; 2943 while ((val = valueList->current())) { 2944 // Check for a comma break first. 2945 if (val->unit == Value::Operator) { 2946 if (val->iValue != ',' || !context.allowBreak) 2947 // Other operators aren't legal or we aren't done with the current shadow 2948 // value. Treat as invalid. 2949 { 2950 return context.failed(); 2951 } 2952 2953 // The value is good. Commit it. 2954 context.commitValue(); 2955 } 2956 // Check to see if we're a length. 2957 else if (validUnit(val, FLength, true)) { 2958 // We required a length and didn't get one. Invalid. 2959 if (!context.allowLength()) { 2960 return context.failed(); 2961 } 2962 2963 // A length is allowed here. Construct the value and add it. 2964 context.commitLength(val); 2965 } else { 2966 // The only other type of value that's ok is a color value. 2967 CSSPrimitiveValueImpl *parsedColor = nullptr; 2968 bool isColor = ((val->id >= CSS_VAL_AQUA && val->id <= CSS_VAL_WINDOWTEXT) || 2969 val->id == CSS_VAL_MENU || 2970 (val->id >= CSS_VAL_GREY && val->id <= CSS_VAL__KHTML_TEXT && !strict)); 2971 if (!context.allowColor) { 2972 return context.failed(); 2973 } 2974 2975 if (isColor) { 2976 parsedColor = new CSSPrimitiveValueImpl(val->id); 2977 } 2978 2979 if (!parsedColor) 2980 // It's not built-in. Try to parse it as a color. 2981 { 2982 parsedColor = parseColorFromValue(val); 2983 } 2984 2985 if (!parsedColor) { 2986 return context.failed(); 2987 } 2988 2989 context.commitColor(parsedColor); 2990 } 2991 2992 valueList->next(); 2993 } 2994 2995 if (context.allowBreak) { 2996 context.commitValue(); 2997 if (context.values->length()) { 2998 addProperty(propId, context.values, important); 2999 valueList->next(); 3000 return true; 3001 } 3002 } 3003 3004 return context.failed(); 3005 } 3006 3007 bool CSSParser::parseCounter(int propId, bool increment, bool important) 3008 { 3009 enum { ID, VAL, COMMA } state = ID; 3010 3011 CSSValueListImpl *list = new CSSValueListImpl; 3012 DOMString c; 3013 Value *val; 3014 while (true) { 3015 val = valueList->current(); 3016 switch (state) { 3017 // Commas are not allowed according to the standard, but Opera allows them and being the only 3018 // other browser with counter support we need to match their behavior to work with current use 3019 case COMMA: 3020 state = ID; 3021 if (val && val->unit == Value::Operator && val->iValue == ',') { 3022 valueList->next(); 3023 continue; 3024 } 3025 // no break 3026 case ID: 3027 if (val && val->unit == CSSPrimitiveValue::CSS_IDENT) { 3028 c = qString(val->string); 3029 state = VAL; 3030 valueList->next(); 3031 continue; 3032 } 3033 break; 3034 case VAL: { 3035 short i = 0; 3036 if (val && val->unit == CSSPrimitiveValue::CSS_NUMBER) { 3037 i = (short)val->fValue; 3038 valueList->next(); 3039 } else { 3040 i = (increment) ? 1 : 0; 3041 } 3042 3043 CounterActImpl *cv = new CounterActImpl(c, i); 3044 list->append(cv); 3045 state = COMMA; 3046 continue; 3047 } 3048 } 3049 break; 3050 } 3051 if (list->length() > 0) { 3052 addProperty(propId, list, important); 3053 return true; 3054 } 3055 delete list; 3056 return false; 3057 } 3058 3059 static inline int yyerror(const char *str) 3060 { 3061 // assert( 0 ); 3062 #ifdef CSS_DEBUG 3063 qCDebug(KHTML_LOG) << "CSS parse error " << str; 3064 #else 3065 Q_UNUSED(str); 3066 #endif 3067 return 1; 3068 } 3069 3070 static const double dIntMax = INT_MAX; 3071 #define END 0 3072 3073 #include "parser.h" 3074 3075 int DOM::CSSParser::lex(void *_yylval) 3076 { 3077 YYSTYPE *yylval = (YYSTYPE *)_yylval; 3078 int token = lex(); 3079 int length; 3080 unsigned short *t = text(&length); 3081 3082 #ifdef TOKEN_DEBUG 3083 qDebug("CSSTokenizer: got token %d: '%s'", token, token == END ? "" : QString((QChar *)t, length).toLatin1().constData()); 3084 #endif 3085 switch (token) { 3086 case '{': 3087 block_nesting++; 3088 break; 3089 case '}': 3090 if (block_nesting) { 3091 block_nesting--; 3092 } 3093 break; 3094 case END: 3095 if (block_nesting) { 3096 block_nesting--; 3097 return '}'; 3098 } 3099 break; 3100 case S: 3101 case SGML_CD: 3102 case INCLUDES: 3103 case DASHMATCH: 3104 break; 3105 3106 case URI: 3107 case STRING: 3108 case IDENT: 3109 case NTH: 3110 case HASH: 3111 case HEXCOLOR: 3112 case DIMEN: 3113 case UNICODERANGE: 3114 case NOTFUNCTION: 3115 case FUNCTION: 3116 yylval->string.string = t; 3117 yylval->string.length = length; 3118 break; 3119 3120 case IMPORT_SYM: 3121 case PAGE_SYM: 3122 case MEDIA_SYM: 3123 case FONT_FACE_SYM: 3124 case CHARSET_SYM: 3125 case NAMESPACE_SYM: 3126 3127 case IMPORTANT_SYM: 3128 break; 3129 3130 case QEMS: 3131 length--; 3132 case GRADS: 3133 case DPCM: 3134 length--; 3135 case DEGS: 3136 case RADS: 3137 case KHERZ: 3138 case DPI: 3139 case REMS: 3140 length--; 3141 case MSECS: 3142 case HERZ: 3143 case EMS: 3144 case EXS: 3145 case CHS: 3146 case PXS: 3147 case CMS: 3148 case MMS: 3149 case INS: 3150 case PTS: 3151 case PCS: 3152 length--; 3153 case SECS: 3154 case PERCENTAGE: 3155 length--; 3156 case FLOAT: 3157 case INTEGER: 3158 yylval->val = qMin(QString((QChar *)t, length).toDouble(), dIntMax); 3159 //qDebug("value = %s, converted=%.2f", QString((QChar *)t, length).toLatin1().constData(), yylval->val); 3160 break; 3161 3162 default: 3163 break; 3164 } 3165 3166 return token; 3167 } 3168 3169 static inline int toHex(char c) 3170 { 3171 if ('0' <= c && c <= '9') { 3172 return c - '0'; 3173 } 3174 if ('a' <= c && c <= 'f') { 3175 return c - 'a' + 10; 3176 } 3177 if ('A' <= c && c <= 'F') { 3178 return c - 'A' + 10; 3179 } 3180 return 0; 3181 } 3182 3183 unsigned short *DOM::CSSParser::text(int *length) 3184 { 3185 unsigned short *start = yytext; 3186 int l = yyleng; 3187 switch (yyTok) { 3188 case STRING: 3189 l--; 3190 /* nobreak */ 3191 case HASH: 3192 case HEXCOLOR: 3193 start++; 3194 l--; 3195 break; 3196 case URI: 3197 // "url("{w}{string}{w}")" 3198 // "url("{w}{url}{w}")" 3199 3200 // strip "url(" and ")" 3201 start += 4; 3202 l -= 5; 3203 // strip {w} 3204 while (l && 3205 (*start == ' ' || *start == '\t' || *start == '\r' || 3206 *start == '\n' || *start == '\f')) { 3207 start++; l--; 3208 } 3209 if (*start == '"' || *start == '\'') { 3210 start++; l--; 3211 } 3212 while (l && 3213 (start[l - 1] == ' ' || start[l - 1] == '\t' || start[l - 1] == '\r' || 3214 start[l - 1] == '\n' || start[l - 1] == '\f')) { 3215 l--; 3216 } 3217 if (l && (start[l - 1] == '\"' || start[l - 1] == '\'')) { 3218 l--; 3219 } 3220 3221 default: 3222 break; 3223 } 3224 3225 // process escapes 3226 unsigned short *out = start; 3227 unsigned short *escape = nullptr; 3228 3229 for (int i = 0; i < l; i++) { 3230 unsigned short *current = start + i; 3231 if (escape == current - 1) { 3232 if ((*current >= '0' && *current <= '9') || 3233 (*current >= 'a' && *current <= 'f') || 3234 (*current >= 'A' && *current <= 'F')) { 3235 continue; 3236 } 3237 if (yyTok == STRING && 3238 (*current == '\n' || *current == '\r' || *current == '\f')) { 3239 // ### handle \r\n case 3240 if (*current != '\r') { 3241 escape = nullptr; 3242 } 3243 continue; 3244 } 3245 // in all other cases copy the char to output 3246 // ### 3247 *out++ = *current; 3248 escape = nullptr; 3249 continue; 3250 } 3251 if (escape == current - 2 && yyTok == STRING && 3252 *(current - 1) == '\r' && *current == '\n') { 3253 escape = nullptr; 3254 continue; 3255 } 3256 if (escape > current - 7 && 3257 ((*current >= '0' && *current <= '9') || 3258 (*current >= 'a' && *current <= 'f') || 3259 (*current >= 'A' && *current <= 'F'))) { 3260 continue; 3261 } 3262 if (escape) { 3263 // add escaped char 3264 int uc = 0; 3265 escape++; 3266 while (escape < current) { 3267 // qDebug("toHex( %c = %x", (char)*escape, toHex( *escape ) ); 3268 uc *= 16; 3269 uc += toHex(*escape); 3270 escape++; 3271 } 3272 // qDebug(" converting escape: string='%s', value=0x%x", QString( (QChar *)e, current-e ).toLatin1().constData(), uc ); 3273 // can't handle chars outside utf16 3274 if (uc > 0xffff) { 3275 uc = 0xfffd; 3276 } 3277 *(out++) = (unsigned short)uc; 3278 escape = nullptr; 3279 if (*current == ' ' || 3280 *current == '\t' || 3281 *current == '\r' || 3282 *current == '\n' || 3283 *current == '\f') { 3284 continue; 3285 } 3286 } 3287 if (!escape && *current == '\\') { 3288 escape = current; 3289 continue; 3290 } 3291 *(out++) = *current; 3292 } 3293 if (escape) { 3294 // add escaped char 3295 int uc = 0; 3296 escape++; 3297 while (escape < start + l) { 3298 // qDebug("toHex( %c = %x", (char)*escape, toHex( *escape ) ); 3299 uc *= 16; 3300 uc += toHex(*escape); 3301 escape++; 3302 } 3303 // qDebug(" converting escape: string='%s', value=0x%x", QString( (QChar *)e, current-e ).toLatin1().constData(), uc ); 3304 // can't handle chars outside utf16 3305 if (uc > 0xffff) { 3306 uc = 0xfffd; 3307 } 3308 *(out++) = (unsigned short)uc; 3309 } 3310 3311 *length = out - start; 3312 return start; 3313 } 3314 3315 // When we reach the end of the input we switch over 3316 // the lexer to this alternative buffer and keep it stuck here. 3317 // (and as it contains nulls, flex will keep on reporting 3318 // end of buffer, and we will keep reseting the input 3319 // pointer to the beginning of this). 3320 static unsigned short postEofBuf[2]; 3321 3322 #define YY_DECL int DOM::CSSParser::lex() 3323 #define yyconst const 3324 typedef int yy_state_type; 3325 typedef unsigned int YY_CHAR; 3326 // this line makes sure we treat all Unicode chars correctly. 3327 #define YY_SC_TO_UI(c) (c > 0xff ? 0xff : c) 3328 #define YY_DO_BEFORE_ACTION \ 3329 yytext = yy_bp; \ 3330 yyleng = (int) (yy_cp - yy_bp); \ 3331 yy_hold_char = *yy_cp; \ 3332 *yy_cp = 0; \ 3333 yy_c_buf_p = yy_cp; 3334 #define YY_BREAK break; 3335 #define ECHO qDebug( "%s", QString( (QChar *)yytext, yyleng ).toLatin1().constData() ) 3336 #define YY_RULE_SETUP 3337 #define INITIAL 0 3338 #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) 3339 #define YY_START ((yy_start - 1) / 2) 3340 #define yyterminate()\ 3341 do { \ 3342 if (yy_act == YY_END_OF_BUFFER) { \ 3343 yy_c_buf_p = postEofBuf; \ 3344 yy_hold_char = 0; /* first char of the postEndOf to 'restore' */ \ 3345 } \ 3346 yyTok = END; return yyTok; \ 3347 } while (0) 3348 #define YY_FATAL_ERROR(a) qFatal(a) 3349 #define BEGIN yy_start = 1 + 2 * 3350 #define COMMENT 1 3351 3352 #include "tokenizer.cpp"