/* * Copyright (C) 2003 Lars Knoll (knoll@kde.org) * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. * Copyright (C) 2007 Nicholas Shanks * Copyright (C) 2008 Eric Seidel * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "sky/engine/core/css/parser/BisonCSSParser.h" #include #include "gen/sky/core/CSSValueKeywords.h" #include "gen/sky/core/StylePropertyShorthand.h" #include "gen/sky/platform/RuntimeEnabledFeatures.h" #include "sky/engine/core/css/CSSAspectRatioValue.h" #include "sky/engine/core/css/CSSBasicShapes.h" #include "sky/engine/core/css/CSSBorderImage.h" #include "sky/engine/core/css/CSSFontFeatureValue.h" #include "sky/engine/core/css/CSSFunctionValue.h" #include "sky/engine/core/css/CSSGradientValue.h" #include "sky/engine/core/css/CSSInheritedValue.h" #include "sky/engine/core/css/CSSInitialValue.h" #include "sky/engine/core/css/CSSLineBoxContainValue.h" #include "sky/engine/core/css/CSSPrimitiveValue.h" #include "sky/engine/core/css/CSSPropertySourceData.h" #include "sky/engine/core/css/CSSSelector.h" #include "sky/engine/core/css/CSSShadowValue.h" #include "sky/engine/core/css/CSSStyleSheet.h" #include "sky/engine/core/css/CSSTransformValue.h" #include "sky/engine/core/css/CSSUnicodeRangeValue.h" #include "sky/engine/core/css/CSSValueList.h" #include "sky/engine/core/css/CSSValuePool.h" #include "sky/engine/core/css/HashTools.h" #include "sky/engine/core/css/Pair.h" #include "sky/engine/core/css/Rect.h" #include "sky/engine/core/css/StylePropertySet.h" #include "sky/engine/core/css/StyleRule.h" #include "sky/engine/core/css/StyleSheetContents.h" #include "sky/engine/core/css/parser/CSSParserIdioms.h" #include "sky/engine/core/dom/Document.h" #include "sky/engine/core/frame/FrameConsole.h" #include "sky/engine/core/frame/FrameHost.h" #include "sky/engine/core/frame/LocalFrame.h" #include "sky/engine/core/frame/Settings.h" #include "sky/engine/core/html/parser/HTMLParserIdioms.h" #include "sky/engine/core/inspector/ConsoleMessage.h" #include "sky/engine/core/rendering/RenderTheme.h" #include "sky/engine/platform/FloatConversion.h" #include "sky/engine/wtf/BitArray.h" #include "sky/engine/wtf/HexNumber.h" #include "sky/engine/wtf/text/StringBuffer.h" #include "sky/engine/wtf/text/StringBuilder.h" #include "sky/engine/wtf/text/StringImpl.h" #include "sky/engine/wtf/text/TextEncoding.h" #define YYDEBUG 0 #if YYDEBUG > 0 extern int cssyydebug; #endif int cssyyparse(blink::BisonCSSParser*); using namespace WTF; namespace blink { static const unsigned INVALID_NUM_PARSED_PROPERTIES = UINT_MAX; BisonCSSParser::BisonCSSParser(const CSSParserContext& context) : m_context(context) , m_id(CSSPropertyInvalid) , m_styleSheet(nullptr) , m_supportsCondition(false) , m_selectorListForParseSelector(0) , m_numParsedPropertiesBeforeMarginBox(INVALID_NUM_PARSED_PROPERTIES) , m_hadSyntacticallyValidCSSRule(false) , m_ignoreErrors(false) , m_defaultNamespace(starAtom) , m_observer(0) , m_source(0) , m_ruleHeaderType(CSSRuleSourceData::UNKNOWN_RULE) , m_allowImportRules(true) , m_allowNamespaceDeclarations(true) , m_inViewport(false) , m_tokenizer(*this) { #if YYDEBUG > 0 cssyydebug = 1; #endif } BisonCSSParser::~BisonCSSParser() { clearProperties(); deleteAllValues(m_floatingSelectors); deleteAllValues(m_floatingSelectorVectors); deleteAllValues(m_floatingValueLists); deleteAllValues(m_floatingFunctions); } void BisonCSSParser::setupParser(const char* prefix, unsigned prefixLength, const String& string, const char* suffix, unsigned suffixLength) { m_tokenizer.setupTokenizer(prefix, prefixLength, string, suffix, suffixLength); m_ruleHasHeader = true; } void BisonCSSParser::parseSheet(StyleSheetContents* sheet, const String& string) { setStyleSheet(sheet); m_defaultNamespace = starAtom; // Reset the default namespace. m_ignoreErrors = false; m_tokenizer.m_lineNumber = 0; m_source = &string; m_tokenizer.m_internal = false; setupParser("", string, ""); cssyyparse(this); sheet->shrinkToFit(); m_source = 0; m_rule = nullptr; m_lineEndings.clear(); m_ignoreErrors = false; m_tokenizer.m_internal = true; } PassRefPtr BisonCSSParser::parseRule(StyleSheetContents* sheet, const String& string) { setStyleSheet(sheet); m_allowNamespaceDeclarations = false; setupParser("@-internal-rule ", string, ""); cssyyparse(this); return m_rule.release(); } bool BisonCSSParser::parseSupportsCondition(const String& string) { m_supportsCondition = false; setupParser("@-internal-supports-condition ", string, ""); cssyyparse(this); return m_supportsCondition; } static inline bool isColorPropertyID(CSSPropertyID propertyId) { switch (propertyId) { case CSSPropertyColor: case CSSPropertyBackgroundColor: case CSSPropertyBorderBottomColor: case CSSPropertyBorderLeftColor: case CSSPropertyBorderRightColor: case CSSPropertyBorderTopColor: case CSSPropertyOutlineColor: case CSSPropertyWebkitBorderAfterColor: case CSSPropertyWebkitBorderBeforeColor: case CSSPropertyWebkitBorderEndColor: case CSSPropertyWebkitBorderStartColor: case CSSPropertyWebkitTextEmphasisColor: case CSSPropertyWebkitTextFillColor: case CSSPropertyWebkitTextStrokeColor: case CSSPropertyTextDecorationColor: return true; default: return false; } } static bool parseColorValue(MutableStylePropertySet* declaration, CSSPropertyID propertyId, const String& string, CSSParserMode cssParserMode) { ASSERT(!string.isEmpty()); bool quirksMode = isQuirksModeBehavior(cssParserMode); if (!isColorPropertyID(propertyId)) return false; CSSParserString cssString; cssString.init(string); CSSValueID valueID = cssValueKeywordID(cssString); bool validPrimitive = false; if (valueID == CSSValueCurrentcolor) validPrimitive = true; if (validPrimitive) { RefPtr value = cssValuePool().createIdentifierValue(valueID); declaration->addParsedProperty(CSSProperty(propertyId, value.release())); return true; } RGBA32 color; if (!CSSPropertyParser::fastParseColor(color, string, !quirksMode && string[0] != '#')) return false; RefPtr value = cssValuePool().createColorValue(color); declaration->addParsedProperty(CSSProperty(propertyId, value.release())); return true; } static inline bool isSimpleLengthPropertyID(CSSPropertyID propertyId, bool& acceptsNegativeNumbers) { switch (propertyId) { case CSSPropertyFontSize: case CSSPropertyHeight: case CSSPropertyWidth: case CSSPropertyMinHeight: case CSSPropertyMinWidth: case CSSPropertyPaddingBottom: case CSSPropertyPaddingLeft: case CSSPropertyPaddingRight: case CSSPropertyPaddingTop: case CSSPropertyWebkitLogicalWidth: case CSSPropertyWebkitLogicalHeight: case CSSPropertyWebkitMinLogicalWidth: case CSSPropertyWebkitMinLogicalHeight: case CSSPropertyWebkitPaddingAfter: case CSSPropertyWebkitPaddingBefore: case CSSPropertyWebkitPaddingEnd: case CSSPropertyWebkitPaddingStart: acceptsNegativeNumbers = false; return true; case CSSPropertyBottom: case CSSPropertyLeft: case CSSPropertyMarginBottom: case CSSPropertyMarginLeft: case CSSPropertyMarginRight: case CSSPropertyMarginTop: case CSSPropertyRight: case CSSPropertyTop: case CSSPropertyWebkitMarginAfter: case CSSPropertyWebkitMarginBefore: case CSSPropertyWebkitMarginEnd: case CSSPropertyWebkitMarginStart: acceptsNegativeNumbers = true; return true; default: return false; } } template static inline bool parseSimpleLength(const CharacterType* characters, unsigned length, CSSPrimitiveValue::UnitType& unit, double& number) { if (length > 2 && (characters[length - 2] | 0x20) == 'p' && (characters[length - 1] | 0x20) == 'x') { length -= 2; unit = CSSPrimitiveValue::CSS_PX; } else if (length > 1 && characters[length - 1] == '%') { length -= 1; unit = CSSPrimitiveValue::CSS_PERCENTAGE; } // We rely on charactersToDouble for validation as well. The function // will set "ok" to "false" if the entire passed-in character range does // not represent a double. bool ok; number = charactersToDouble(characters, length, &ok); return ok; } static bool parseSimpleLengthValue(MutableStylePropertySet* declaration, CSSPropertyID propertyId, const String& string, CSSParserMode cssParserMode) { ASSERT(!string.isEmpty()); bool acceptsNegativeNumbers = false; // In @viewport, width and height are shorthands, not simple length values. if (isCSSViewportParsingEnabledForMode(cssParserMode) || !isSimpleLengthPropertyID(propertyId, acceptsNegativeNumbers)) return false; unsigned length = string.length(); double number; CSSPrimitiveValue::UnitType unit = CSSPrimitiveValue::CSS_NUMBER; if (string.is8Bit()) { if (!parseSimpleLength(string.characters8(), length, unit, number)) return false; } else { if (!parseSimpleLength(string.characters16(), length, unit, number)) return false; } if (unit == CSSPrimitiveValue::CSS_NUMBER) { bool quirksMode = isQuirksModeBehavior(cssParserMode); if (number && !quirksMode) return false; unit = CSSPrimitiveValue::CSS_PX; } if (number < 0 && !acceptsNegativeNumbers) return false; RefPtr value = cssValuePool().createValue(number, unit); declaration->addParsedProperty(CSSProperty(propertyId, value.release())); return true; } bool isValidKeywordPropertyAndValue(CSSPropertyID propertyId, CSSValueID valueID, const CSSParserContext& parserContext) { if (valueID == CSSValueInvalid) return false; switch (propertyId) { case CSSPropertyBackgroundRepeatX: // repeat | no-repeat case CSSPropertyBackgroundRepeatY: // repeat | no-repeat return valueID == CSSValueRepeat || valueID == CSSValueNoRepeat; case CSSPropertyBorderTopStyle: // case CSSPropertyBorderRightStyle: // Defined as: none | hidden | dotted | dashed | case CSSPropertyBorderBottomStyle: // solid | double | groove | ridge | inset | outset case CSSPropertyBorderLeftStyle: case CSSPropertyWebkitBorderAfterStyle: case CSSPropertyWebkitBorderBeforeStyle: case CSSPropertyWebkitBorderEndStyle: case CSSPropertyWebkitBorderStartStyle: return valueID >= CSSValueNone && valueID <= CSSValueDouble; case CSSPropertyBoxSizing: return valueID == CSSValueBorderBox || valueID == CSSValueContentBox; case CSSPropertyDirection: // ltr | rtl return valueID == CSSValueLtr || valueID == CSSValueRtl; case CSSPropertyDisplay: // inline | block | list-item | inline-block | table | // inline-table | table-row-group | table-header-group | table-footer-group | table-row | // table-column-group | table-column | table-cell | table-caption | -webkit-box | -webkit-inline-box | none // flex | inline-flex return (valueID >= CSSValueInline && valueID <= CSSValueInlineFlex) || valueID == CSSValueNone; case CSSPropertyFontStyle: // normal | italic | oblique return valueID == CSSValueNormal || valueID == CSSValueItalic || valueID == CSSValueOblique; case CSSPropertyFontStretch: // normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded return valueID == CSSValueNormal || (valueID >= CSSValueUltraCondensed && valueID <= CSSValueUltraExpanded); case CSSPropertyImageRendering: // auto | optimizeContrast | pixelated return valueID == CSSValueAuto || valueID == CSSValueWebkitOptimizeContrast || (RuntimeEnabledFeatures::imageRenderingPixelatedEnabled() && valueID == CSSValuePixelated); case CSSPropertyObjectFit: ASSERT(RuntimeEnabledFeatures::objectFitPositionEnabled()); return valueID == CSSValueFill || valueID == CSSValueContain || valueID == CSSValueCover || valueID == CSSValueNone || valueID == CSSValueScaleDown; case CSSPropertyOutlineStyle: // ( except hidden) | auto return valueID == CSSValueAuto || valueID == CSSValueNone || (valueID >= CSSValueInset && valueID <= CSSValueDouble); case CSSPropertyOverflowWrap: // normal | break-word case CSSPropertyWordWrap: return valueID == CSSValueNormal || valueID == CSSValueBreakWord; case CSSPropertyOverflowX: // visible | hidden | auto | overlay return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueAuto || valueID == CSSValueOverlay; case CSSPropertyOverflowY: // visible | hidden | auto | overlay | -webkit-paged-x | -webkit-paged-y return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueAuto || valueID == CSSValueOverlay || valueID == CSSValueWebkitPagedX || valueID == CSSValueWebkitPagedY; case CSSPropertyPointerEvents: // none | visiblePainted | visibleFill | visibleStroke | visible | // painted | fill | stroke | auto | all | bounding-box return valueID == CSSValueVisible || valueID == CSSValueNone || valueID == CSSValueAll || valueID == CSSValueAuto || (valueID >= CSSValueVisiblepainted && valueID <= CSSValueBoundingBox); case CSSPropertyPosition: // static | absolute return valueID == CSSValueStatic || valueID == CSSValueAbsolute; case CSSPropertyTextAlignLast: // auto | start | end | left | right | center | justify ASSERT(RuntimeEnabledFeatures::css3TextEnabled()); return (valueID >= CSSValueLeft && valueID <= CSSValueJustify) || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueAuto; case CSSPropertyTextDecorationStyle: // solid | double | dotted | dashed | wavy ASSERT(RuntimeEnabledFeatures::css3TextDecorationsEnabled()); return valueID == CSSValueSolid || valueID == CSSValueDouble || valueID == CSSValueDotted || valueID == CSSValueDashed || valueID == CSSValueWavy; case CSSPropertyTextJustify: // auto | none | inter-word | distribute ASSERT(RuntimeEnabledFeatures::css3TextEnabled()); return valueID == CSSValueInterWord || valueID == CSSValueDistribute || valueID == CSSValueAuto || valueID == CSSValueNone; case CSSPropertyTextOverflow: // clip | ellipsis return valueID == CSSValueClip || valueID == CSSValueEllipsis; case CSSPropertyTextRendering: // auto | optimizeSpeed | optimizeLegibility | geometricPrecision return valueID == CSSValueAuto || valueID == CSSValueOptimizespeed || valueID == CSSValueOptimizelegibility || valueID == CSSValueGeometricprecision; case CSSPropertyUnicodeBidi: return valueID == CSSValueNormal || valueID == CSSValueEmbed || valueID == CSSValueBidiOverride || valueID == CSSValueWebkitIsolate || valueID == CSSValueWebkitIsolateOverride || valueID == CSSValueWebkitPlaintext; case CSSPropertyTouchActionDelay: // none | script ASSERT(RuntimeEnabledFeatures::cssTouchActionDelayEnabled()); return valueID == CSSValueScript || valueID == CSSValueNone; case CSSPropertyWebkitBoxDecorationBreak: return valueID == CSSValueClone || valueID == CSSValueSlice; case CSSPropertyAlignContent: // FIXME: Per CSS alignment, this property should accept an optional . We should share this parsing code with 'justify-self'. return valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueSpaceBetween || valueID == CSSValueSpaceAround || valueID == CSSValueStretch; case CSSPropertyAlignItems: // FIXME: Per CSS alignment, this property should accept the same arguments as 'justify-self' so we should share its parsing code. return valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch; case CSSPropertyAlignSelf: // FIXME: Per CSS alignment, this property should accept the same arguments as 'justify-self' so we should share its parsing code. return valueID == CSSValueAuto || valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch; case CSSPropertyFlexDirection: return valueID == CSSValueRow || valueID == CSSValueRowReverse || valueID == CSSValueColumn || valueID == CSSValueColumnReverse; case CSSPropertyFlexWrap: return valueID == CSSValueNowrap || valueID == CSSValueWrap || valueID == CSSValueWrapReverse; case CSSPropertyJustifyContent: // FIXME: Per CSS alignment, this property should accept an optional . We should share this parsing code with 'justify-self'. return valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueSpaceBetween || valueID == CSSValueSpaceAround; case CSSPropertyFontKerning: return valueID == CSSValueAuto || valueID == CSSValueNormal || valueID == CSSValueNone; case CSSPropertyWebkitFontSmoothing: return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueAntialiased || valueID == CSSValueSubpixelAntialiased; case CSSPropertyWebkitLineBreak: // auto | loose | normal | strict | after-white-space return valueID == CSSValueAuto || valueID == CSSValueLoose || valueID == CSSValueNormal || valueID == CSSValueStrict || valueID == CSSValueAfterWhiteSpace; case CSSPropertyWebkitRtlOrdering: return valueID == CSSValueLogical || valueID == CSSValueVisual; case CSSPropertyWebkitTextEmphasisPosition: return valueID == CSSValueOver || valueID == CSSValueUnder; case CSSPropertyTransformStyle: case CSSPropertyWebkitTransformStyle: return valueID == CSSValueFlat || valueID == CSSValuePreserve3d; case CSSPropertyWebkitUserModify: // read-only | read-write return valueID == CSSValueReadOnly || valueID == CSSValueReadWrite || valueID == CSSValueReadWritePlaintextOnly; case CSSPropertyWebkitUserSelect: // auto | none | text | all return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueText || valueID == CSSValueAll; case CSSPropertyWhiteSpace: // normal | pre | nowrap return valueID == CSSValueNormal || valueID == CSSValuePre || valueID == CSSValuePreWrap || valueID == CSSValuePreLine || valueID == CSSValueNowrap; case CSSPropertyWordBreak: // normal | break-all | break-word (this is a custom extension) return valueID == CSSValueNormal || valueID == CSSValueBreakAll || valueID == CSSValueBreakWord; default: ASSERT_NOT_REACHED(); return false; } return false; } bool isKeywordPropertyID(CSSPropertyID propertyId) { switch (propertyId) { case CSSPropertyAlignItems: case CSSPropertyAlignSelf: case CSSPropertyBackgroundRepeatX: case CSSPropertyBackgroundRepeatY: case CSSPropertyBorderBottomStyle: case CSSPropertyBorderLeftStyle: case CSSPropertyBorderRightStyle: case CSSPropertyBorderTopStyle: case CSSPropertyBoxSizing: case CSSPropertyDirection: case CSSPropertyDisplay: case CSSPropertyFontStyle: case CSSPropertyFontStretch: case CSSPropertyImageRendering: case CSSPropertyObjectFit: case CSSPropertyOutlineStyle: case CSSPropertyOverflowWrap: case CSSPropertyOverflowX: case CSSPropertyOverflowY: case CSSPropertyPointerEvents: case CSSPropertyPosition: case CSSPropertyTextAlignLast: case CSSPropertyTextDecorationStyle: case CSSPropertyTextJustify: case CSSPropertyTextOverflow: case CSSPropertyTextRendering: case CSSPropertyTouchActionDelay: case CSSPropertyUnicodeBidi: case CSSPropertyWebkitBorderAfterStyle: case CSSPropertyWebkitBorderBeforeStyle: case CSSPropertyWebkitBorderEndStyle: case CSSPropertyWebkitBorderStartStyle: case CSSPropertyWebkitBoxDecorationBreak: case CSSPropertyAlignContent: case CSSPropertyFlexDirection: case CSSPropertyFlexWrap: case CSSPropertyJustifyContent: case CSSPropertyFontKerning: case CSSPropertyWebkitFontSmoothing: case CSSPropertyWebkitLineBreak: case CSSPropertyWebkitRtlOrdering: case CSSPropertyWebkitTextEmphasisPosition: case CSSPropertyTransformStyle: case CSSPropertyWebkitTransformStyle: case CSSPropertyWebkitUserModify: case CSSPropertyWebkitUserSelect: case CSSPropertyWhiteSpace: case CSSPropertyWordBreak: case CSSPropertyWordWrap: return true; default: return false; } } static bool parseKeywordValue(MutableStylePropertySet* declaration, CSSPropertyID propertyId, const String& string, const CSSParserContext& parserContext) { ASSERT(!string.isEmpty()); if (!isKeywordPropertyID(propertyId)) { // All properties accept the values of "initial" and "inherit". String lowerCaseString = string.lower(); if (lowerCaseString != "initial" && lowerCaseString != "inherit") return false; // Parse initial/inherit shorthands using the BisonCSSParser. if (shorthandForProperty(propertyId).length()) return false; } CSSParserString cssString; cssString.init(string); CSSValueID valueID = cssValueKeywordID(cssString); if (!valueID) return false; RefPtr value = nullptr; if (valueID == CSSValueInherit) value = cssValuePool().createInheritedValue(); else if (valueID == CSSValueInitial) value = cssValuePool().createExplicitInitialValue(); else if (isValidKeywordPropertyAndValue(propertyId, valueID, parserContext)) value = cssValuePool().createIdentifierValue(valueID); else return false; declaration->addParsedProperty(CSSProperty(propertyId, value.release())); return true; } template static bool parseTransformTranslateArguments(CharType*& pos, CharType* end, unsigned expectedCount, CSSTransformValue* transformValue) { while (expectedCount) { size_t delimiter = WTF::find(pos, end - pos, expectedCount == 1 ? ')' : ','); if (delimiter == kNotFound) return false; unsigned argumentLength = static_cast(delimiter); CSSPrimitiveValue::UnitType unit = CSSPrimitiveValue::CSS_NUMBER; double number; if (!parseSimpleLength(pos, argumentLength, unit, number)) return false; if (unit != CSSPrimitiveValue::CSS_PX && (number || unit != CSSPrimitiveValue::CSS_NUMBER)) return false; transformValue->append(cssValuePool().createValue(number, CSSPrimitiveValue::CSS_PX)); pos += argumentLength + 1; --expectedCount; } return true; } template static bool parseTransformNumberArguments(CharType*& pos, CharType* end, unsigned expectedCount, CSSTransformValue* transformValue) { while (expectedCount) { size_t delimiter = WTF::find(pos, end - pos, expectedCount == 1 ? ')' : ','); if (delimiter == kNotFound) return false; unsigned argumentLength = static_cast(delimiter); bool ok; double number = charactersToDouble(pos, argumentLength, &ok); if (!ok) return false; transformValue->append(cssValuePool().createValue(number, CSSPrimitiveValue::CSS_NUMBER)); pos += argumentLength + 1; --expectedCount; } return true; } template static PassRefPtr parseSimpleTransformValue(CharType*& pos, CharType* end) { static const int shortestValidTransformStringLength = 12; if (end - pos < shortestValidTransformStringLength) return nullptr; const bool isTranslate = toASCIILower(pos[0]) == 't' && toASCIILower(pos[1]) == 'r' && toASCIILower(pos[2]) == 'a' && toASCIILower(pos[3]) == 'n' && toASCIILower(pos[4]) == 's' && toASCIILower(pos[5]) == 'l' && toASCIILower(pos[6]) == 'a' && toASCIILower(pos[7]) == 't' && toASCIILower(pos[8]) == 'e'; if (isTranslate) { CSSTransformValue::TransformOperationType transformType; unsigned expectedArgumentCount = 1; unsigned argumentStart = 11; CharType c9 = toASCIILower(pos[9]); if (c9 == 'x' && pos[10] == '(') { transformType = CSSTransformValue::TranslateXTransformOperation; } else if (c9 == 'y' && pos[10] == '(') { transformType = CSSTransformValue::TranslateYTransformOperation; } else if (c9 == 'z' && pos[10] == '(') { transformType = CSSTransformValue::TranslateZTransformOperation; } else if (c9 == '(') { transformType = CSSTransformValue::TranslateTransformOperation; expectedArgumentCount = 2; argumentStart = 10; } else if (c9 == '3' && toASCIILower(pos[10]) == 'd' && pos[11] == '(') { transformType = CSSTransformValue::Translate3DTransformOperation; expectedArgumentCount = 3; argumentStart = 12; } else { return nullptr; } pos += argumentStart; RefPtr transformValue = CSSTransformValue::create(transformType); if (!parseTransformTranslateArguments(pos, end, expectedArgumentCount, transformValue.get())) return nullptr; return transformValue.release(); } const bool isMatrix3d = toASCIILower(pos[0]) == 'm' && toASCIILower(pos[1]) == 'a' && toASCIILower(pos[2]) == 't' && toASCIILower(pos[3]) == 'r' && toASCIILower(pos[4]) == 'i' && toASCIILower(pos[5]) == 'x' && pos[6] == '3' && toASCIILower(pos[7]) == 'd' && pos[8] == '('; if (isMatrix3d) { pos += 9; RefPtr transformValue = CSSTransformValue::create(CSSTransformValue::Matrix3DTransformOperation); if (!parseTransformNumberArguments(pos, end, 16, transformValue.get())) return nullptr; return transformValue.release(); } const bool isScale3d = toASCIILower(pos[0]) == 's' && toASCIILower(pos[1]) == 'c' && toASCIILower(pos[2]) == 'a' && toASCIILower(pos[3]) == 'l' && toASCIILower(pos[4]) == 'e' && pos[5] == '3' && toASCIILower(pos[6]) == 'd' && pos[7] == '('; if (isScale3d) { pos += 8; RefPtr transformValue = CSSTransformValue::create(CSSTransformValue::Scale3DTransformOperation); if (!parseTransformNumberArguments(pos, end, 3, transformValue.get())) return nullptr; return transformValue.release(); } return nullptr; } template static PassRefPtr parseSimpleTransformList(CharType*& pos, CharType* end) { RefPtr transformList = nullptr; while (pos < end) { while (pos < end && isCSSSpace(*pos)) ++pos; RefPtr transformValue = parseSimpleTransformValue(pos, end); if (!transformValue) return nullptr; if (!transformList) transformList = CSSValueList::createSpaceSeparated(); transformList->append(transformValue.release()); if (pos < end) { if (isCSSSpace(*pos)) return nullptr; } } return transformList.release(); } static bool parseSimpleTransform(MutableStylePropertySet* properties, CSSPropertyID propertyID, const String& string) { if (propertyID != CSSPropertyTransform && propertyID != CSSPropertyWebkitTransform) return false; if (string.isEmpty()) return false; RefPtr transformList = nullptr; if (string.is8Bit()) { const LChar* pos = string.characters8(); const LChar* end = pos + string.length(); transformList = parseSimpleTransformList(pos, end); if (!transformList) return false; } else { const UChar* pos = string.characters16(); const UChar* end = pos + string.length(); transformList = parseSimpleTransformList(pos, end); if (!transformList) return false; } properties->addParsedProperty(CSSProperty(propertyID, transformList.release())); return true; } PassRefPtr BisonCSSParser::parseFontFaceValue(const AtomicString& string) { if (string.isEmpty()) return nullptr; RefPtr dummyStyle = MutableStylePropertySet::create(); if (!parseValue(dummyStyle.get(), CSSPropertyFontFamily, string, HTMLStandardMode, 0)) return nullptr; RefPtr fontFamily = dummyStyle->getPropertyCSSValue(CSSPropertyFontFamily); if (!fontFamily->isValueList()) return nullptr; return toCSSValueList(dummyStyle->getPropertyCSSValue(CSSPropertyFontFamily).get()); } bool BisonCSSParser::parseValue(MutableStylePropertySet* declaration, CSSPropertyID propertyID, const String& string, const Document& document) { ASSERT(!string.isEmpty()); CSSParserContext context(document); if (parseSimpleLengthValue(declaration, propertyID, string, context.mode())) return true; if (parseColorValue(declaration, propertyID, string, context.mode())) return true; if (parseKeywordValue(declaration, propertyID, string, context)) return true; BisonCSSParser parser(context); return parser.parseValue(declaration, propertyID, string, static_cast(0)); } bool BisonCSSParser::parseValue(MutableStylePropertySet* declaration, CSSPropertyID propertyID, const String& string, CSSParserMode cssParserMode, StyleSheetContents* contextStyleSheet) { ASSERT(!string.isEmpty()); if (parseSimpleLengthValue(declaration, propertyID, string, cssParserMode)) return true; if (parseColorValue(declaration, propertyID, string, cssParserMode)) return true; CSSParserContext context; if (contextStyleSheet) context = contextStyleSheet->parserContext(); if (parseKeywordValue(declaration, propertyID, string, context)) return true; if (parseSimpleTransform(declaration, propertyID, string)) return true; BisonCSSParser parser(context); return parser.parseValue(declaration, propertyID, string, contextStyleSheet); } bool BisonCSSParser::parseValue(MutableStylePropertySet* declaration, CSSPropertyID propertyID, const String& string, StyleSheetContents* contextStyleSheet) { setStyleSheet(contextStyleSheet); setupParser("@-internal-value ", string, ""); m_id = propertyID; cssyyparse(this); m_rule = nullptr; m_id = CSSPropertyInvalid; bool ok = false; if (!m_parsedProperties.isEmpty()) { ok = true; declaration->addParsedProperties(m_parsedProperties); clearProperties(); } return ok; } // The color will only be changed when string contains a valid CSS color, so callers // can set it to a default color and ignore the boolean result. bool BisonCSSParser::parseColor(RGBA32& color, const String& string, bool strict) { // First try creating a color specified by name, rgba(), rgb() or "#" syntax. if (CSSPropertyParser::fastParseColor(color, string, strict)) return true; BisonCSSParser parser(strictCSSParserContext()); // In case the fast-path parser didn't understand the color, try the full parser. if (!parser.parseColor(string)) return false; CSSValue* value = parser.m_parsedProperties.first().value(); if (!value->isPrimitiveValue()) return false; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (!primitiveValue->isRGBColor()) return false; color = primitiveValue->getRGBA32Value(); return true; } StyleColor BisonCSSParser::colorFromRGBColorString(const String& colorString) { // FIXME: Rework css parser so it is more SVG aware. RGBA32 color; if (parseColor(color, colorString.stripWhiteSpace())) return StyleColor(color); // FIXME: This branch catches the string currentColor, but we should error if we have an illegal color value. return StyleColor::currentColor(); } bool BisonCSSParser::parseColor(const String& string) { setupParser("@-internal-decls color:", string, ""); cssyyparse(this); m_rule = nullptr; return !m_parsedProperties.isEmpty() && m_parsedProperties.first().id() == CSSPropertyColor; } bool BisonCSSParser::parseSystemColor(RGBA32& color, const String& string) { CSSParserString cssColor; cssColor.init(string); CSSValueID id = cssValueKeywordID(cssColor); if (!CSSPropertyParser::isSystemColor(id)) return false; Color parsedColor = RenderTheme::theme().systemColor(id); color = parsedColor.rgb(); return true; } void BisonCSSParser::parseSelector(const String& string, CSSSelectorList& selectorList) { m_selectorListForParseSelector = &selectorList; setupParser("@-internal-selector ", string, ""); cssyyparse(this); m_selectorListForParseSelector = 0; } PassRefPtr BisonCSSParser::parseInlineStyleDeclaration(const String& string, Element* element) { Document& document = element->document(); CSSParserContext context = CSSParserContext(document.elementSheet().contents()->parserContext()); return BisonCSSParser(context).parseDeclaration(string, document.elementSheet().contents()); } PassRefPtr BisonCSSParser::parseDeclaration(const String& string, StyleSheetContents* contextStyleSheet) { setStyleSheet(contextStyleSheet); setupParser("@-internal-decls ", string, ""); cssyyparse(this); m_rule = nullptr; RefPtr style = createStylePropertySet(); clearProperties(); return style.release(); } bool BisonCSSParser::parseDeclaration(MutableStylePropertySet* declaration, const String& string, CSSParserObserver* observer, StyleSheetContents* contextStyleSheet) { setStyleSheet(contextStyleSheet); TemporaryChange scopedObsever(m_observer, observer); setupParser("@-internal-decls ", string, ""); if (m_observer) { m_observer->startRuleHeader(CSSRuleSourceData::STYLE_RULE, 0); m_observer->endRuleHeader(1); m_observer->startRuleBody(0); } cssyyparse(this); m_rule = nullptr; bool ok = false; if (!m_parsedProperties.isEmpty()) { ok = true; declaration->addParsedProperties(m_parsedProperties); clearProperties(); } if (m_observer) m_observer->endRuleBody(string.length(), false); return ok; } bool BisonCSSParser::parseAttributeMatchType(CSSSelector::AttributeMatchType& matchType, const String& string) { if (!RuntimeEnabledFeatures::cssAttributeCaseSensitivityEnabled() && !isUASheetBehavior(m_context.mode())) return false; if (string == "i") { matchType = CSSSelector::CaseInsensitive; return true; } return false; } static inline void filterProperties(const Vector& input, Vector& output, size_t& unusedEntries, BitArray& seenProperties) { // Add properties in reverse order so that highest priority definitions are reached first. Duplicate definitions can then be ignored when found. for (int i = input.size() - 1; i >= 0; --i) { const CSSProperty& property = input[i]; const unsigned propertyIDIndex = property.id() - firstCSSProperty; if (seenProperties.get(propertyIDIndex)) continue; seenProperties.set(propertyIDIndex); output[--unusedEntries] = property; } } PassRefPtr BisonCSSParser::createStylePropertySet() { BitArray seenProperties; size_t unusedEntries = m_parsedProperties.size(); Vector results(unusedEntries); filterProperties(m_parsedProperties, results, unusedEntries, seenProperties); if (unusedEntries) results.remove(0, unusedEntries); return ImmutableStylePropertySet::create(results.data(), results.size(), HTMLStandardMode); } void BisonCSSParser::rollbackLastProperties(int num) { ASSERT(num >= 0); ASSERT(m_parsedProperties.size() >= static_cast(num)); m_parsedProperties.shrink(m_parsedProperties.size() - num); } void BisonCSSParser::clearProperties() { m_parsedProperties.clear(); m_numParsedPropertiesBeforeMarginBox = INVALID_NUM_PARSED_PROPERTIES; } void BisonCSSParser::setCurrentProperty(CSSPropertyID propId) { m_id = propId; } bool BisonCSSParser::parseValue(CSSPropertyID propId) { return CSSPropertyParser::parseValue(propId, m_valueList.get(), m_context, m_inViewport, m_parsedProperties, m_ruleHeaderType); } class TransformOperationInfo { public: TransformOperationInfo(const CSSParserString& name) : m_type(CSSTransformValue::UnknownTransformOperation) , m_argCount(1) , m_allowSingleArgument(false) , m_unit(CSSPropertyParser::FUnknown) { const UChar* characters; unsigned nameLength = name.length(); const unsigned longestNameLength = 12; UChar characterBuffer[longestNameLength]; if (name.is8Bit()) { unsigned length = std::min(longestNameLength, nameLength); const LChar* characters8 = name.characters8(); for (unsigned i = 0; i < length; ++i) characterBuffer[i] = characters8[i]; characters = characterBuffer; } else characters = name.characters16(); SWITCH(characters, nameLength) { CASE("skew(") { m_unit = CSSPropertyParser::FAngle; m_type = CSSTransformValue::SkewTransformOperation; m_allowSingleArgument = true; m_argCount = 3; } CASE("scale(") { m_unit = CSSPropertyParser::FNumber; m_type = CSSTransformValue::ScaleTransformOperation; m_allowSingleArgument = true; m_argCount = 3; } CASE("skewx(") { m_unit = CSSPropertyParser::FAngle; m_type = CSSTransformValue::SkewXTransformOperation; } CASE("skewy(") { m_unit = CSSPropertyParser::FAngle; m_type = CSSTransformValue::SkewYTransformOperation; } CASE("matrix(") { m_unit = CSSPropertyParser::FNumber; m_type = CSSTransformValue::MatrixTransformOperation; m_argCount = 11; } CASE("rotate(") { m_unit = CSSPropertyParser::FAngle; m_type = CSSTransformValue::RotateTransformOperation; } CASE("scalex(") { m_unit = CSSPropertyParser::FNumber; m_type = CSSTransformValue::ScaleXTransformOperation; } CASE("scaley(") { m_unit = CSSPropertyParser::FNumber; m_type = CSSTransformValue::ScaleYTransformOperation; } CASE("scalez(") { m_unit = CSSPropertyParser::FNumber; m_type = CSSTransformValue::ScaleZTransformOperation; } CASE("scale3d(") { m_unit = CSSPropertyParser::FNumber; m_type = CSSTransformValue::Scale3DTransformOperation; m_argCount = 5; } CASE("rotatex(") { m_unit = CSSPropertyParser::FAngle; m_type = CSSTransformValue::RotateXTransformOperation; } CASE("rotatey(") { m_unit = CSSPropertyParser::FAngle; m_type = CSSTransformValue::RotateYTransformOperation; } CASE("rotatez(") { m_unit = CSSPropertyParser::FAngle; m_type = CSSTransformValue::RotateZTransformOperation; } CASE("matrix3d(") { m_unit = CSSPropertyParser::FNumber; m_type = CSSTransformValue::Matrix3DTransformOperation; m_argCount = 31; } CASE("rotate3d(") { m_unit = CSSPropertyParser::FNumber; m_type = CSSTransformValue::Rotate3DTransformOperation; m_argCount = 7; } CASE("translate(") { m_unit = CSSPropertyParser::FLength | CSSPropertyParser::FPercent; m_type = CSSTransformValue::TranslateTransformOperation; m_allowSingleArgument = true; m_argCount = 3; } CASE("translatex(") { m_unit = CSSPropertyParser::FLength | CSSPropertyParser::FPercent; m_type = CSSTransformValue::TranslateXTransformOperation; } CASE("translatey(") { m_unit = CSSPropertyParser::FLength | CSSPropertyParser::FPercent; m_type = CSSTransformValue::TranslateYTransformOperation; } CASE("translatez(") { m_unit = CSSPropertyParser::FLength | CSSPropertyParser::FPercent; m_type = CSSTransformValue::TranslateZTransformOperation; } CASE("perspective(") { m_unit = CSSPropertyParser::FNumber; m_type = CSSTransformValue::PerspectiveTransformOperation; } CASE("translate3d(") { m_unit = CSSPropertyParser::FLength | CSSPropertyParser::FPercent; m_type = CSSTransformValue::Translate3DTransformOperation; m_argCount = 5; } } } CSSTransformValue::TransformOperationType type() const { return m_type; } unsigned argCount() const { return m_argCount; } CSSPropertyParser::Units unit() const { return m_unit; } bool unknown() const { return m_type == CSSTransformValue::UnknownTransformOperation; } bool hasCorrectArgCount(unsigned argCount) { return m_argCount == argCount || (m_allowSingleArgument && argCount == 1); } private: CSSTransformValue::TransformOperationType m_type; unsigned m_argCount; bool m_allowSingleArgument; CSSPropertyParser::Units m_unit; }; PassRefPtr CSSPropertyParser::parseTransform(CSSPropertyID propId) { if (!m_valueList) return nullptr; RefPtr list = CSSValueList::createSpaceSeparated(); for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { RefPtr parsedTransformValue = parseTransformValue(propId, value); if (!parsedTransformValue) return nullptr; list->append(parsedTransformValue.release()); } return list.release(); } PassRefPtr CSSPropertyParser::parseTransformValue(CSSPropertyID propId, CSSParserValue *value) { if (value->unit != CSSParserValue::Function || !value->function) return nullptr; // Every primitive requires at least one argument. CSSParserValueList* args = value->function->args.get(); if (!args) return nullptr; // See if the specified primitive is one we understand. TransformOperationInfo info(value->function->name); if (info.unknown()) return nullptr; if (!info.hasCorrectArgCount(args->size())) return nullptr; // The transform is a list of functional primitives that specify transform operations. // We collect a list of CSSTransformValues, where each value specifies a single operation. // Create the new CSSTransformValue for this operation and add it to our list. RefPtr transformValue = CSSTransformValue::create(info.type()); // Snag our values. CSSParserValue* a = args->current(); unsigned argNumber = 0; while (a) { CSSPropertyParser::Units unit = info.unit(); if (info.type() == CSSTransformValue::Rotate3DTransformOperation && argNumber == 3) { // 4th param of rotate3d() is an angle rather than a bare number, validate it as such if (!validUnit(a, FAngle, HTMLStandardMode)) return nullptr; } else if (info.type() == CSSTransformValue::Translate3DTransformOperation && argNumber == 2) { // 3rd param of translate3d() cannot be a percentage if (!validUnit(a, FLength, HTMLStandardMode)) return nullptr; } else if (info.type() == CSSTransformValue::TranslateZTransformOperation && !argNumber) { // 1st param of translateZ() cannot be a percentage if (!validUnit(a, FLength, HTMLStandardMode)) return nullptr; } else if (info.type() == CSSTransformValue::PerspectiveTransformOperation && !argNumber) { // 1st param of perspective() must be a non-negative number (deprecated) or length. if ((propId == CSSPropertyWebkitTransform && !validUnit(a, FNumber | FLength | FNonNeg, HTMLStandardMode)) || (propId == CSSPropertyTransform && !validUnit(a, FLength | FNonNeg, HTMLStandardMode))) return nullptr; } else if (!validUnit(a, unit, HTMLStandardMode)) { return nullptr; } // Add the value to the current transform operation. transformValue->append(createPrimitiveNumericValue(a)); a = args->next(); if (!a) break; if (a->unit != CSSParserValue::Operator || a->iValue != ',') return nullptr; a = args->next(); argNumber++; } return transformValue.release(); } void BisonCSSParser::ensureLineEndings() { if (!m_lineEndings) m_lineEndings = lineEndings(*m_source); } CSSParserSelector* BisonCSSParser::createFloatingSelectorWithTagName(const QualifiedName& tagQName) { CSSParserSelector* selector = new CSSParserSelector(tagQName); m_floatingSelectors.append(selector); return selector; } CSSParserSelector* BisonCSSParser::createFloatingSelector() { CSSParserSelector* selector = new CSSParserSelector; m_floatingSelectors.append(selector); return selector; } PassOwnPtr BisonCSSParser::sinkFloatingSelector(CSSParserSelector* selector) { if (selector) { size_t index = m_floatingSelectors.reverseFind(selector); ASSERT(index != kNotFound); m_floatingSelectors.remove(index); } return adoptPtr(selector); } Vector >* BisonCSSParser::createFloatingSelectorVector() { Vector >* selectorVector = new Vector >; m_floatingSelectorVectors.append(selectorVector); return selectorVector; } PassOwnPtr > > BisonCSSParser::sinkFloatingSelectorVector(Vector >* selectorVector) { if (selectorVector) { size_t index = m_floatingSelectorVectors.reverseFind(selectorVector); ASSERT(index != kNotFound); m_floatingSelectorVectors.remove(index); } return adoptPtr(selectorVector); } CSSParserValueList* BisonCSSParser::createFloatingValueList() { CSSParserValueList* list = new CSSParserValueList; m_floatingValueLists.append(list); return list; } PassOwnPtr BisonCSSParser::sinkFloatingValueList(CSSParserValueList* list) { if (list) { size_t index = m_floatingValueLists.reverseFind(list); ASSERT(index != kNotFound); m_floatingValueLists.remove(index); } return adoptPtr(list); } CSSParserFunction* BisonCSSParser::createFloatingFunction() { CSSParserFunction* function = new CSSParserFunction; m_floatingFunctions.append(function); return function; } CSSParserFunction* BisonCSSParser::createFloatingFunction(const CSSParserString& name, PassOwnPtr args) { CSSParserFunction* function = createFloatingFunction(); function->name = name; function->args = args; return function; } PassOwnPtr BisonCSSParser::sinkFloatingFunction(CSSParserFunction* function) { if (function) { size_t index = m_floatingFunctions.reverseFind(function); ASSERT(index != kNotFound); m_floatingFunctions.remove(index); } return adoptPtr(function); } CSSParserValue& BisonCSSParser::sinkFloatingValue(CSSParserValue& value) { if (value.unit == CSSParserValue::Function) { size_t index = m_floatingFunctions.reverseFind(value.function); ASSERT(index != kNotFound); m_floatingFunctions.remove(index); } return value; } StyleRuleBase* BisonCSSParser::createSupportsRule(bool conditionIsSupported, RuleList* rules) { m_allowImportRules = m_allowNamespaceDeclarations = false; RefPtr data = popSupportsRuleData(); RefPtr rule = nullptr; String conditionText; unsigned conditionOffset = data->ruleHeaderRange.start + 9; unsigned conditionLength = data->ruleHeaderRange.length() - 9; if (m_tokenizer.is8BitSource()) conditionText = String(m_tokenizer.m_dataStart8.get() + conditionOffset, conditionLength).stripWhiteSpace(); else conditionText = String(m_tokenizer.m_dataStart16.get() + conditionOffset, conditionLength).stripWhiteSpace(); if (rules) { rule = StyleRuleSupports::create(conditionText, conditionIsSupported, *rules); } else { RuleList emptyRules; rule = StyleRuleSupports::create(conditionText, conditionIsSupported, emptyRules); } StyleRuleSupports* result = rule.get(); m_parsedRules.append(rule.release()); return result; } void BisonCSSParser::markSupportsRuleHeaderStart() { if (!m_supportsRuleDataStack) m_supportsRuleDataStack = adoptPtr(new RuleSourceDataList()); RefPtr data = CSSRuleSourceData::create(CSSRuleSourceData::SUPPORTS_RULE); data->ruleHeaderRange.start = m_tokenizer.tokenStartOffset(); m_supportsRuleDataStack->append(data); } void BisonCSSParser::markSupportsRuleHeaderEnd() { ASSERT(m_supportsRuleDataStack && !m_supportsRuleDataStack->isEmpty()); if (m_tokenizer.is8BitSource()) m_supportsRuleDataStack->last()->ruleHeaderRange.end = m_tokenizer.tokenStart() - m_tokenizer.m_dataStart8.get(); else m_supportsRuleDataStack->last()->ruleHeaderRange.end = m_tokenizer.tokenStart() - m_tokenizer.m_dataStart16.get(); } PassRefPtr BisonCSSParser::popSupportsRuleData() { ASSERT(m_supportsRuleDataStack && !m_supportsRuleDataStack->isEmpty()); RefPtr data = m_supportsRuleDataStack->last(); m_supportsRuleDataStack->removeLast(); return data.release(); } BisonCSSParser::RuleList* BisonCSSParser::createRuleList() { OwnPtr list = adoptPtr(new RuleList); RuleList* listPtr = list.get(); m_parsedRuleLists.append(list.release()); return listPtr; } BisonCSSParser::RuleList* BisonCSSParser::appendRule(RuleList* ruleList, StyleRuleBase* rule) { if (rule) { if (!ruleList) ruleList = createRuleList(); ruleList->append(rule); } return ruleList; } template ALWAYS_INLINE static void makeLower(const CharacterType* input, CharacterType* output, unsigned length) { // FIXME: If we need Unicode lowercasing here, then we probably want the real kind // that can potentially change the length of the string rather than the character // by character kind. If we don't need Unicode lowercasing, it would be good to // simplify this function. if (charactersAreAllASCII(input, length)) { // Fast case for all-ASCII. for (unsigned i = 0; i < length; i++) output[i] = toASCIILower(input[i]); } else { for (unsigned i = 0; i < length; i++) output[i] = Unicode::toLower(input[i]); } } void BisonCSSParser::tokenToLowerCase(CSSParserString& token) { // Since it's our internal token, we know that we created it out // of our writable work buffers. Therefore the const_cast is just // ugly and not a potential crash. size_t length = token.length(); if (token.is8Bit()) { makeLower(token.characters8(), const_cast(token.characters8()), length); } else { makeLower(token.characters16(), const_cast(token.characters16()), length); } } void BisonCSSParser::endInvalidRuleHeader() { if (m_ruleHeaderType == CSSRuleSourceData::UNKNOWN_RULE) return; CSSParserLocation location; location.lineNumber = m_tokenizer.m_lineNumber; location.offset = m_ruleHeaderStartOffset; if (m_tokenizer.is8BitSource()) location.token.init(m_tokenizer.m_dataStart8.get() + m_ruleHeaderStartOffset, 0); else location.token.init(m_tokenizer.m_dataStart16.get() + m_ruleHeaderStartOffset, 0); reportError(location, m_ruleHeaderType == CSSRuleSourceData::STYLE_RULE ? InvalidSelectorCSSError : InvalidRuleCSSError); endRuleHeader(); } StyleRuleBase* BisonCSSParser::createStyleRule(Vector >* selectors) { StyleRule* result = 0; if (selectors) { m_allowImportRules = m_allowNamespaceDeclarations = false; RefPtr rule = StyleRule::create(); rule->parserAdoptSelectorVector(*selectors); rule->setProperties(createStylePropertySet()); result = rule.get(); m_parsedRules.append(rule.release()); } clearProperties(); return result; } StyleRuleBase* BisonCSSParser::createFontFaceRule() { m_allowImportRules = m_allowNamespaceDeclarations = false; for (unsigned i = 0; i < m_parsedProperties.size(); ++i) { CSSProperty& property = m_parsedProperties[i]; if (property.id() == CSSPropertyFontVariant && property.value()->isPrimitiveValue()) property.wrapValueInCommaSeparatedList(); else if (property.id() == CSSPropertyFontFamily && (!property.value()->isValueList() || toCSSValueList(property.value())->length() != 1)) { // Unlike font-family property, font-family descriptor in @font-face rule // has to be a value list with exactly one family name. It cannot have a // have 'initial' value and cannot 'inherit' from parent. // See http://dev.w3.org/csswg/css3-fonts/#font-family-desc clearProperties(); return 0; } } RefPtr rule = StyleRuleFontFace::create(); rule->setProperties(createStylePropertySet()); clearProperties(); StyleRuleFontFace* result = rule.get(); m_parsedRules.append(rule.release()); return result; } CSSParserSelector* BisonCSSParser::rewriteSpecifiersWithNamespaceIfNeeded(CSSParserSelector* specifiers) { if (m_defaultNamespace != starAtom) return rewriteSpecifiersWithElementName(nullAtom, starAtom, specifiers, /*tagIsForNamespaceRule*/true); return specifiers; } CSSParserSelector* BisonCSSParser::rewriteSpecifiersWithElementName(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector* specifiers, bool tagIsForNamespaceRule) { QualifiedName tag(elementName); // *:host never matches, so we can't discard the * otherwise we can't tell the // difference between *:host and just :host. if (tag == anyName && !specifiers->hasHostPseudoSelector()) return specifiers; specifiers->prependTagSelector(tag, tagIsForNamespaceRule); return specifiers; } CSSParserSelector* BisonCSSParser::rewriteSpecifiers(CSSParserSelector* specifiers, CSSParserSelector* newSpecifier) { specifiers->appendTagHistory(sinkFloatingSelector(newSpecifier)); return specifiers; } void BisonCSSParser::startDeclarationsForMarginBox() { m_numParsedPropertiesBeforeMarginBox = m_parsedProperties.size(); } void BisonCSSParser::endDeclarationsForMarginBox() { rollbackLastProperties(m_parsedProperties.size() - m_numParsedPropertiesBeforeMarginBox); m_numParsedPropertiesBeforeMarginBox = INVALID_NUM_PARSED_PROPERTIES; } void BisonCSSParser::startRule() { if (!m_observer) return; ASSERT(m_ruleHasHeader); m_ruleHasHeader = false; } void BisonCSSParser::endRule(bool valid) { if (!m_observer) return; if (m_ruleHasHeader) m_observer->endRuleBody(m_tokenizer.safeUserStringTokenOffset(), !valid); m_ruleHasHeader = true; } void BisonCSSParser::startRuleHeader(CSSRuleSourceData::Type ruleType) { m_ruleHeaderType = ruleType; m_ruleHeaderStartOffset = m_tokenizer.safeUserStringTokenOffset(); m_ruleHeaderStartLineNumber = m_tokenizer.m_tokenStartLineNumber; if (m_observer) { ASSERT(!m_ruleHasHeader); m_observer->startRuleHeader(ruleType, m_ruleHeaderStartOffset); m_ruleHasHeader = true; } } void BisonCSSParser::endRuleHeader() { ASSERT(m_ruleHeaderType != CSSRuleSourceData::UNKNOWN_RULE); m_ruleHeaderType = CSSRuleSourceData::UNKNOWN_RULE; if (m_observer) { ASSERT(m_ruleHasHeader); m_observer->endRuleHeader(m_tokenizer.safeUserStringTokenOffset()); } } void BisonCSSParser::startSelector() { if (m_observer) m_observer->startSelector(m_tokenizer.safeUserStringTokenOffset()); } void BisonCSSParser::endSelector() { if (m_observer) m_observer->endSelector(m_tokenizer.safeUserStringTokenOffset()); } void BisonCSSParser::startRuleBody() { if (m_observer) m_observer->startRuleBody(m_tokenizer.safeUserStringTokenOffset()); } void BisonCSSParser::startProperty() { if (m_observer) m_observer->startProperty(m_tokenizer.safeUserStringTokenOffset()); } void BisonCSSParser::endProperty(bool isPropertyParsed, CSSParserError errorType) { m_id = CSSPropertyInvalid; if (m_observer) m_observer->endProperty(isPropertyParsed, m_tokenizer.safeUserStringTokenOffset(), errorType); } void BisonCSSParser::startEndUnknownRule() { if (m_observer) m_observer->startEndUnknownRule(); } }