/* * (C) 1999-2003 Lars Knoll (knoll@kde.org) * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. * Copyright (C) 2011 Research In Motion Limited. All rights reserved. * Copyright (C) 2013 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 "config.h" #include "StyleProperties.h" #include "CSSComputedStyleDeclaration.h" #include "CSSCustomPropertyValue.h" #include "CSSDeferredParser.h" #include "CSSParser.h" #include "CSSPendingSubstitutionValue.h" #include "CSSPropertyParser.h" #include "CSSTokenizer.h" #include "CSSValueKeywords.h" #include "CSSValueList.h" #include "CSSValuePool.h" #include "Color.h" #include "Document.h" #include "PropertySetCSSStyleDeclaration.h" #include "StylePropertyShorthand.h" #include "StylePropertyShorthandFunctions.h" #include "StyleSheetContents.h" #include #include #ifndef NDEBUG #include #include #endif namespace WebCore { DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(StyleProperties); DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(ImmutableStyleProperties); DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(MutableStyleProperties); static size_t sizeForImmutableStylePropertiesWithPropertyCount(unsigned count) { return sizeof(ImmutableStyleProperties) - sizeof(void*) + sizeof(StylePropertyMetadata) * count + sizeof(PackedPtr) * count; } static bool isCSSWideValueKeyword(StringView value) { return value == "initial" || value == "inherit" || value == "unset" || value == "revert"; } Ref ImmutableStyleProperties::create(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode) { void* slot = ImmutableStylePropertiesMalloc::malloc(sizeForImmutableStylePropertiesWithPropertyCount(count)); return adoptRef(*new (NotNull, slot) ImmutableStyleProperties(properties, count, cssParserMode)); } Ref StyleProperties::immutableCopyIfNeeded() const { if (is(*this)) return downcast(const_cast(*this)); const MutableStyleProperties& mutableThis = downcast(*this); return ImmutableStyleProperties::create(mutableThis.m_propertyVector.data(), mutableThis.m_propertyVector.size(), cssParserMode()); } MutableStyleProperties::MutableStyleProperties(CSSParserMode cssParserMode) : StyleProperties(cssParserMode, MutablePropertiesType) { } MutableStyleProperties::MutableStyleProperties(Vector&& properties) : StyleProperties(HTMLStandardMode, MutablePropertiesType) , m_propertyVector(WTFMove(properties)) { } MutableStyleProperties::~MutableStyleProperties() = default; ImmutableStyleProperties::ImmutableStyleProperties(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode) : StyleProperties(cssParserMode, length) { StylePropertyMetadata* metadataArray = const_cast(this->metadataArray()); PackedPtr* valueArray = bitwise_cast*>(this->valueArray()); for (unsigned i = 0; i < length; ++i) { metadataArray[i] = properties[i].metadata(); auto* value = properties[i].value(); valueArray[i] = value; value->ref(); } } ImmutableStyleProperties::~ImmutableStyleProperties() { PackedPtr* valueArray = bitwise_cast*>(this->valueArray()); for (unsigned i = 0; i < m_arraySize; ++i) valueArray[i]->deref(); } MutableStyleProperties::MutableStyleProperties(const StyleProperties& other) : StyleProperties(other.cssParserMode(), MutablePropertiesType) { ASSERT(other.type() != DeferredPropertiesType); if (is(other)) m_propertyVector = downcast(other).m_propertyVector; else { const auto& immutableOther = downcast(other); unsigned propertyCount = immutableOther.propertyCount(); m_propertyVector.reserveInitialCapacity(propertyCount); for (unsigned i = 0; i < propertyCount; ++i) m_propertyVector.uncheckedAppend(immutableOther.propertyAt(i).toCSSProperty()); } } String StyleProperties::getPropertyValue(CSSPropertyID propertyID) const { if (auto value = getPropertyCSSValue(propertyID)) { switch (propertyID) { case CSSPropertyFillOpacity: case CSSPropertyFloodOpacity: case CSSPropertyOpacity: case CSSPropertyStopOpacity: case CSSPropertyStrokeOpacity: // Opacity percentage values serialize as a fraction in the range 0-1, no "%". if (is(*value) && downcast(*value).isPercentage()) return makeString(downcast(*value).doubleValue() / 100); FALLTHROUGH; default: return value->cssText(); } } { auto shorthand = shorthandForProperty(propertyID); if (shorthand.length() && is(getPropertyCSSValue(shorthand.properties()[0]))) return String(); } // Shorthand and 4-values properties switch (propertyID) { case CSSPropertyAll: return getCommonValue(allShorthand()); case CSSPropertyAnimation: return getLayeredShorthandValue(animationShorthand()); case CSSPropertyBorderSpacing: return borderSpacingValue(borderSpacingShorthand()); case CSSPropertyBackgroundPosition: return getLayeredShorthandValue(backgroundPositionShorthand()); case CSSPropertyBackgroundRepeat: return getLayeredShorthandValue(backgroundRepeatShorthand()); case CSSPropertyBackground: return getLayeredShorthandValue(backgroundShorthand()); case CSSPropertyBorder: return borderPropertyValue(borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand()); case CSSPropertyBorderTop: return getShorthandValue(borderTopShorthand()); case CSSPropertyBorderRight: return getShorthandValue(borderRightShorthand()); case CSSPropertyBorderBottom: return getShorthandValue(borderBottomShorthand()); case CSSPropertyBorderLeft: return getShorthandValue(borderLeftShorthand()); case CSSPropertyBorderBlock: return borderPropertyValue(borderBlockWidthShorthand(), borderBlockStyleShorthand(), borderBlockColorShorthand()); case CSSPropertyBorderBlockColor: return get2Values(borderBlockColorShorthand()); case CSSPropertyBorderBlockStyle: return get2Values(borderBlockStyleShorthand()); case CSSPropertyBorderBlockWidth: return get2Values(borderBlockWidthShorthand()); case CSSPropertyBorderBlockStart: return getShorthandValue(borderBlockStartShorthand()); case CSSPropertyBorderBlockEnd: return getShorthandValue(borderBlockEndShorthand()); case CSSPropertyBorderInline: return borderPropertyValue(borderInlineWidthShorthand(), borderInlineStyleShorthand(), borderInlineColorShorthand()); case CSSPropertyBorderInlineColor: return get2Values(borderInlineColorShorthand()); case CSSPropertyBorderInlineStyle: return get2Values(borderInlineStyleShorthand()); case CSSPropertyBorderInlineWidth: return get2Values(borderInlineWidthShorthand()); case CSSPropertyBorderInlineStart: return getShorthandValue(borderInlineStartShorthand()); case CSSPropertyBorderInlineEnd: return getShorthandValue(borderInlineEndShorthand()); case CSSPropertyOutline: return getShorthandValue(outlineShorthand()); case CSSPropertyBorderColor: return get4Values(borderColorShorthand()); case CSSPropertyBorderWidth: return get4Values(borderWidthShorthand()); case CSSPropertyBorderStyle: return get4Values(borderStyleShorthand()); case CSSPropertyColumnRule: return getShorthandValue(columnRuleShorthand()); case CSSPropertyColumns: return getShorthandValue(columnsShorthand()); case CSSPropertyFlex: return getShorthandValue(flexShorthand()); case CSSPropertyFlexFlow: return getShorthandValue(flexFlowShorthand()); case CSSPropertyGridArea: return getGridShorthandValue(gridAreaShorthand()); case CSSPropertyGridTemplate: return getGridShorthandValue(gridTemplateShorthand()); case CSSPropertyGrid: return getGridShorthandValue(gridShorthand()); case CSSPropertyGridColumn: return getGridShorthandValue(gridColumnShorthand()); case CSSPropertyGridRow: return getGridShorthandValue(gridRowShorthand()); case CSSPropertyPageBreakAfter: return pageBreakPropertyValue(pageBreakAfterShorthand()); case CSSPropertyPageBreakBefore: return pageBreakPropertyValue(pageBreakBeforeShorthand()); case CSSPropertyPageBreakInside: return pageBreakPropertyValue(pageBreakInsideShorthand()); case CSSPropertyPlaceContent: return getAlignmentShorthandValue(placeContentShorthand()); case CSSPropertyPlaceItems: return getAlignmentShorthandValue(placeItemsShorthand()); case CSSPropertyPlaceSelf: return getAlignmentShorthandValue(placeSelfShorthand()); case CSSPropertyFont: return fontValue(); case CSSPropertyFontVariant: return fontVariantValue(); case CSSPropertyInset: return get4Values(insetShorthand()); case CSSPropertyInsetBlock: return get2Values(insetBlockShorthand()); case CSSPropertyInsetInline: return get2Values(insetInlineShorthand()); case CSSPropertyMargin: return get4Values(marginShorthand()); case CSSPropertyMarginBlock: return get2Values(marginBlockShorthand()); case CSSPropertyMarginInline: return get2Values(marginInlineShorthand()); case CSSPropertyWebkitMarginCollapse: return getShorthandValue(webkitMarginCollapseShorthand()); case CSSPropertyOverflow: return get2Values(overflowShorthand()); case CSSPropertyOverscrollBehavior: return get2Values(overscrollBehaviorShorthand()); case CSSPropertyPadding: return get4Values(paddingShorthand()); case CSSPropertyPaddingBlock: return get2Values(paddingBlockShorthand()); case CSSPropertyPaddingInline: return get2Values(paddingInlineShorthand()); case CSSPropertyTransition: return getLayeredShorthandValue(transitionShorthand()); case CSSPropertyListStyle: return getShorthandValue(listStyleShorthand()); case CSSPropertyWebkitMaskPosition: return getLayeredShorthandValue(webkitMaskPositionShorthand()); case CSSPropertyWebkitMaskRepeat: return getLayeredShorthandValue(webkitMaskRepeatShorthand()); case CSSPropertyWebkitMask: return getLayeredShorthandValue(webkitMaskShorthand()); case CSSPropertyWebkitTextEmphasis: return getShorthandValue(webkitTextEmphasisShorthand()); case CSSPropertyWebkitTextStroke: return getShorthandValue(webkitTextStrokeShorthand()); case CSSPropertyPerspectiveOrigin: return getShorthandValue(perspectiveOriginShorthand()); case CSSPropertyTransformOrigin: return getShorthandValue(transformOriginShorthand()); case CSSPropertyMarker: if (auto value = getPropertyCSSValue(CSSPropertyMarkerStart)) return value->cssText(); return String(); case CSSPropertyBorderRadius: return get4Values(borderRadiusShorthand()); case CSSPropertyGap: return get2Values(gapShorthand()); case CSSPropertyScrollMargin: return get4Values(scrollMarginShorthand()); case CSSPropertyScrollMarginBlock: return get2Values(scrollMarginBlockShorthand()); case CSSPropertyScrollMarginInline: return get2Values(scrollMarginInlineShorthand()); case CSSPropertyScrollPadding: return get4Values(scrollPaddingShorthand()); case CSSPropertyScrollPaddingBlock: return get2Values(scrollPaddingBlockShorthand()); case CSSPropertyScrollPaddingInline: return get2Values(scrollPaddingInlineShorthand()); default: return String(); } } std::optional StyleProperties::propertyAsColor(CSSPropertyID property) const { auto colorValue = getPropertyCSSValue(property); if (!is(colorValue)) return std::nullopt; auto& primitiveColor = downcast(*colorValue); return primitiveColor.isRGBColor() ? primitiveColor.color() : CSSParser::parseColor(colorValue->cssText()); } CSSValueID StyleProperties::propertyAsValueID(CSSPropertyID property) const { auto cssValue = getPropertyCSSValue(property); return is(cssValue) ? downcast(*cssValue).valueID() : CSSValueInvalid; } String StyleProperties::getCustomPropertyValue(const String& propertyName) const { RefPtr value = getCustomPropertyCSSValue(propertyName); if (value) return value->cssText(); return String(); } String StyleProperties::borderSpacingValue(const StylePropertyShorthand& shorthand) const { auto horizontalValue = getPropertyCSSValue(shorthand.properties()[0]); auto verticalValue = getPropertyCSSValue(shorthand.properties()[1]); // While standard border-spacing property does not allow specifying border-spacing-vertical without // specifying border-spacing-horizontal , // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal. if (!horizontalValue || !verticalValue) return String(); String horizontalValueCSSText = horizontalValue->cssText(); String verticalValueCSSText = verticalValue->cssText(); if (horizontalValueCSSText == verticalValueCSSText) return horizontalValueCSSText; return horizontalValueCSSText + ' ' + verticalValueCSSText; } void StyleProperties::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const { int foundPropertyIndex = findPropertyIndex(propertyID); if (foundPropertyIndex == -1) return; // All longhands must have at least implicit values if "font" is specified. if (propertyAt(foundPropertyIndex).isImplicit()) { commonValue = String(); return; } char prefix = '\0'; switch (propertyID) { case CSSPropertyFontStyle: break; // No prefix. case CSSPropertyFontFamily: case CSSPropertyFontVariantAlternates: case CSSPropertyFontVariantCaps: case CSSPropertyFontVariantLigatures: case CSSPropertyFontVariantNumeric: case CSSPropertyFontVariantPosition: case CSSPropertyFontVariantEastAsian: case CSSPropertyFontWeight: case CSSPropertyFontStretch: prefix = ' '; break; case CSSPropertyLineHeight: prefix = '/'; break; default: ASSERT_NOT_REACHED(); } if (prefix && !result.isEmpty()) result.append(prefix); String value = propertyAt(foundPropertyIndex).value()->cssText(); result.append(value); if (!commonValue.isNull() && commonValue != value) commonValue = String(); } String StyleProperties::fontValue() const { int fontSizePropertyIndex = findPropertyIndex(CSSPropertyFontSize); int fontFamilyPropertyIndex = findPropertyIndex(CSSPropertyFontFamily); if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1) return emptyString(); PropertyReference fontSizeProperty = propertyAt(fontSizePropertyIndex); PropertyReference fontFamilyProperty = propertyAt(fontFamilyPropertyIndex); if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit()) return emptyString(); String commonValue = fontSizeProperty.value()->cssText(); StringBuilder result; appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue); appendFontLonghandValueIfExplicit(CSSPropertyFontVariantCaps, result, commonValue); appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue); appendFontLonghandValueIfExplicit(CSSPropertyFontStretch, result, commonValue); if (!result.isEmpty()) result.append(' '); result.append(fontSizeProperty.value()->cssText()); appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue); if (!result.isEmpty()) result.append(' '); result.append(fontFamilyProperty.value()->cssText()); if (isCSSWideValueKeyword(commonValue)) return commonValue; return result.toString(); } String StyleProperties::fontVariantValue() const { String commonValue; StringBuilder result; appendFontLonghandValueIfExplicit(CSSPropertyFontVariantLigatures, result, commonValue); if (isCSSWideValueKeyword(result.toString())) return result.toString(); appendFontLonghandValueIfExplicit(CSSPropertyFontVariantAlternates, result, commonValue); appendFontLonghandValueIfExplicit(CSSPropertyFontVariantCaps, result, commonValue); appendFontLonghandValueIfExplicit(CSSPropertyFontVariantEastAsian, result, commonValue); appendFontLonghandValueIfExplicit(CSSPropertyFontVariantNumeric, result, commonValue); appendFontLonghandValueIfExplicit(CSSPropertyFontVariantPosition, result, commonValue); return result.toString(); } String StyleProperties::get2Values(const StylePropertyShorthand& shorthand) const { // Assume the properties are in the usual order start, end. int startValueIndex = findPropertyIndex(shorthand.properties()[0]); int endValueIndex = findPropertyIndex(shorthand.properties()[1]); if (startValueIndex == -1 || endValueIndex == -1) return { }; auto start = propertyAt(startValueIndex); auto end = propertyAt(endValueIndex); // All 2 properties must be specified. if (!start.value() || !end.value()) return { }; // Important flags must be the same if (start.isImportant() != end.isImportant()) return { }; if (start.isInherited() && end.isInherited()) return getValueName(CSSValueInherit); if (start.value()->isInitialValue() || end.value()->isInitialValue()) { if (start.value()->isInitialValue() && end.value()->isInitialValue() && !start.isImplicit()) return getValueName(CSSValueInitial); return { }; } StringBuilder result; result.append(start.value()->cssText()); if (!start.value()->equals(*end.value())) { result.append(' '); result.append(end.value()->cssText()); } return result.toString(); } String StyleProperties::get4Values(const StylePropertyShorthand& shorthand) const { // Assume the properties are in the usual order top, right, bottom, left. int topValueIndex = findPropertyIndex(shorthand.properties()[0]); int rightValueIndex = findPropertyIndex(shorthand.properties()[1]); int bottomValueIndex = findPropertyIndex(shorthand.properties()[2]); int leftValueIndex = findPropertyIndex(shorthand.properties()[3]); if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1) return String(); PropertyReference top = propertyAt(topValueIndex); PropertyReference right = propertyAt(rightValueIndex); PropertyReference bottom = propertyAt(bottomValueIndex); PropertyReference left = propertyAt(leftValueIndex); // All 4 properties must be specified. if (!top.value() || !right.value() || !bottom.value() || !left.value()) return String(); // Important flags must be the same if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant()) return String(); if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited()) return getValueName(CSSValueInherit); if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) { if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) { // All components are "initial" and "top" is not implicit. return getValueName(CSSValueInitial); } return String(); } bool showLeft = !right.value()->equals(*left.value()); bool showBottom = !top.value()->equals(*bottom.value()) || showLeft; bool showRight = !top.value()->equals(*right.value()) || showBottom; StringBuilder result; result.append(top.value()->cssText()); if (showRight) { result.append(' '); result.append(right.value()->cssText()); } if (showBottom) { result.append(' '); result.append(bottom.value()->cssText()); } if (showLeft) { result.append(' '); result.append(left.value()->cssText()); } return result.toString(); } String StyleProperties::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const { StringBuilder result; const unsigned size = shorthand.length(); Vector> values(size); size_t numLayers = 0; for (unsigned i = 0; i < size; ++i) { values[i] = getPropertyCSSValue(shorthand.properties()[i]); if (!values[i]) { // We don't have all longhand properties defined as required for the shorthand // property and thus should not serialize to a shorthand value. See spec at // https://www.w3.org/TR/cssom-1/#serialize-a-css-declaration-block return String(); } if (values[i]->isBaseValueList()) numLayers = std::max(downcast(*values[i]).length(), numLayers); else numLayers = std::max(1U, numLayers); } String commonValue; // Now stitch the properties together. // Implicit initial values are flagged as such and can safely be omitted. for (size_t i = 0; i < numLayers; i++) { StringBuilder layerResult; bool useRepeatXShorthand = false; bool useRepeatYShorthand = false; bool useSingleWordShorthand = false; bool foundPositionYCSSProperty = false; for (unsigned j = 0; j < size; j++) { auto property = shorthand.properties()[j]; auto value = values[j]; if (value) { if (value->isBaseValueList()) value = downcast(*value).item(i); else { // Color only belongs in the last layer. if (property == CSSPropertyBackgroundColor) { if (i != numLayers - 1) value = nullptr; } else if (i) // Other singletons only belong in the first layer. value = nullptr; } } // We need to report background-repeat as it was written in the CSS. // If the property is implicit, then it was written with only one value. Here we figure out which value that was so we can report back correctly. if (value && j < size - 1 && (property == CSSPropertyBackgroundRepeatX || property == CSSPropertyWebkitMaskRepeatX) && isPropertyImplicit(property)) { // Make sure the value was not reset in the layer check just above. auto nextProperty = shorthand.properties()[j + 1]; if (nextProperty == CSSPropertyBackgroundRepeatY || nextProperty == CSSPropertyWebkitMaskRepeatY) { if (auto yValue = values[j + 1]) { if (is(*yValue)) yValue = downcast(*yValue).itemWithoutBoundsCheck(i); if (!is(*value) || !is(*yValue)) continue; auto xId = downcast(*value).valueID(); auto yId = downcast(*yValue).valueID(); if (xId != yId) { if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) { useRepeatXShorthand = true; ++j; } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) { useRepeatYShorthand = true; continue; } } else { useSingleWordShorthand = true; ++j; } } } } String valueText; if (value && !value->isImplicitInitialValue()) { if (!layerResult.isEmpty()) layerResult.append(' '); if (property == CSSPropertyBackgroundSize || property == CSSPropertyWebkitMaskSize) { if (!foundPositionYCSSProperty) continue; layerResult.append("/ "); } if (useRepeatXShorthand) { useRepeatXShorthand = false; layerResult.append(getValueName(CSSValueRepeatX)); } else if (useRepeatYShorthand) { useRepeatYShorthand = false; layerResult.append(getValueName(CSSValueRepeatY)); } else { if (useSingleWordShorthand) useSingleWordShorthand = false; valueText = value->cssText(); layerResult.append(valueText); } if (property == CSSPropertyBackgroundPositionY || property == CSSPropertyWebkitMaskPositionY) foundPositionYCSSProperty = true; } if (commonValue.isNull()) commonValue = valueText; else if (commonValue != valueText) commonValue = emptyString(); // Could use value here other than a CSS-wide value keyword or the null string. } if (!layerResult.isEmpty()) result.append(result.isEmpty() ? "" : ", ", layerResult.toString()); } if (isCSSWideValueKeyword(commonValue)) return commonValue; return result.isEmpty() ? String() : result.toString(); } String StyleProperties::getGridShorthandValue(const StylePropertyShorthand& shorthand) const { return getShorthandValue(shorthand, " / "); } String StyleProperties::getShorthandValue(const StylePropertyShorthand& shorthand, const char* separator) const { String commonValue; StringBuilder result; for (unsigned i = 0; i < shorthand.length(); ++i) { if (!isPropertyImplicit(shorthand.properties()[i])) { auto value = getPropertyCSSValue(shorthand.properties()[i]); if (!value) return String(); String valueText = value->cssText(); if (!i) commonValue = valueText; else if (!commonValue.isNull() && commonValue != valueText) commonValue = String(); if (value->isInitialValue()) continue; if (!result.isEmpty()) result.append(separator); result.append(valueText); } else commonValue = String(); } if (isCSSWideValueKeyword(commonValue)) return commonValue; if (result.isEmpty()) return String(); return result.toString(); } // Returns a non-null value if all properties have the same value. String StyleProperties::getCommonValue(const StylePropertyShorthand& shorthand) const { String result; bool lastPropertyWasImportant = false; for (unsigned i = 0; i < shorthand.length(); ++i) { auto value = getPropertyCSSValue(shorthand.properties()[i]); if (!value) return String(); // FIXME: CSSInitialValue::cssText should generate the right value. String text = value->cssText(); if (text.isNull()) return String(); if (result.isNull()) result = text; else if (result != text) return String(); bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]); if (i && lastPropertyWasImportant != currentPropertyIsImportant) return String(); lastPropertyWasImportant = currentPropertyIsImportant; } return result; } String StyleProperties::getAlignmentShorthandValue(const StylePropertyShorthand& shorthand) const { String value = getCommonValue(shorthand); if (value.isNull() || value.isEmpty()) return getShorthandValue(shorthand); return value; } String StyleProperties::borderPropertyValue(const StylePropertyShorthand& width, const StylePropertyShorthand& style, const StylePropertyShorthand& color) const { const StylePropertyShorthand properties[3] = { width, style, color }; String commonValue; StringBuilder result; for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) { String value = getCommonValue(properties[i]); if (value.isNull()) return String(); if (!i) commonValue = value; else if (commonValue != value) commonValue = String(); if (value == "initial") continue; if (!result.isEmpty()) result.append(' '); result.append(value); } if (isCSSWideValueKeyword(commonValue)) return commonValue; return result.toString(); } String StyleProperties::pageBreakPropertyValue(const StylePropertyShorthand& shorthand) const { auto value = getPropertyCSSValue(shorthand.properties()[0]); if (!value) return String(); // FIXME: Remove this isGlobalKeyword check after we do this consistently for all shorthands in getPropertyValue. if (value->isGlobalKeyword()) return value->cssText(); if (!is(*value)) return String(); CSSValueID valueId = downcast(*value).valueID(); switch (valueId) { case CSSValuePage: return "always"_s; case CSSValueAuto: case CSSValueAvoid: case CSSValueLeft: case CSSValueRight: return value->cssText(); default: return String(); } } RefPtr StyleProperties::getPropertyCSSValue(CSSPropertyID propertyID) const { int foundPropertyIndex = findPropertyIndex(propertyID); if (foundPropertyIndex == -1) return nullptr; return propertyAt(foundPropertyIndex).value(); } RefPtr StyleProperties::getCustomPropertyCSSValue(const String& propertyName) const { int foundPropertyIndex = findCustomPropertyIndex(propertyName); if (foundPropertyIndex == -1) return nullptr; return propertyAt(foundPropertyIndex).value(); } bool MutableStyleProperties::removeShorthandProperty(CSSPropertyID propertyID) { StylePropertyShorthand shorthand = shorthandForProperty(propertyID); if (!shorthand.length()) return false; return removePropertiesInSet(shorthand.properties(), shorthand.length()); } bool MutableStyleProperties::removeProperty(CSSPropertyID propertyID, String* returnText) { if (removeShorthandProperty(propertyID)) { // FIXME: Return an equivalent shorthand when possible. if (returnText) *returnText = emptyString(); return true; } int foundPropertyIndex = findPropertyIndex(propertyID); if (foundPropertyIndex == -1) { if (returnText) *returnText = emptyString(); return false; } if (returnText) *returnText = propertyAt(foundPropertyIndex).value()->cssText(); // A more efficient removal strategy would involve marking entries as empty // and sweeping them when the vector grows too big. m_propertyVector.remove(foundPropertyIndex); return true; } bool MutableStyleProperties::removeCustomProperty(const String& propertyName, String* returnText) { int foundPropertyIndex = findCustomPropertyIndex(propertyName); if (foundPropertyIndex == -1) { if (returnText) *returnText = emptyString(); return false; } if (returnText) *returnText = propertyAt(foundPropertyIndex).value()->cssText(); // A more efficient removal strategy would involve marking entries as empty // and sweeping them when the vector grows too big. m_propertyVector.remove(foundPropertyIndex); return true; } bool StyleProperties::propertyIsImportant(CSSPropertyID propertyID) const { int foundPropertyIndex = findPropertyIndex(propertyID); if (foundPropertyIndex != -1) return propertyAt(foundPropertyIndex).isImportant(); auto shorthand = shorthandForProperty(propertyID); if (!shorthand.length()) return false; for (auto longhand : shorthand) { if (!propertyIsImportant(longhand)) return false; } return true; } bool StyleProperties::customPropertyIsImportant(const String& propertyName) const { int foundPropertyIndex = findCustomPropertyIndex(propertyName); if (foundPropertyIndex != -1) return propertyAt(foundPropertyIndex).isImportant(); return false; } String StyleProperties::getPropertyShorthand(CSSPropertyID propertyID) const { int foundPropertyIndex = findPropertyIndex(propertyID); if (foundPropertyIndex == -1) return String(); return getPropertyNameString(propertyAt(foundPropertyIndex).shorthandID()); } bool StyleProperties::isPropertyImplicit(CSSPropertyID propertyID) const { int foundPropertyIndex = findPropertyIndex(propertyID); if (foundPropertyIndex == -1) return false; return propertyAt(foundPropertyIndex).isImplicit(); } bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important, CSSParserContext parserContext) { if (!isEnabledCSSProperty(propertyID)) return false; // Setting the value to an empty string just removes the property in both IE and Gecko. // Setting it to null seems to produce less consistent results, but we treat it just the same. if (value.isEmpty()) return removeProperty(propertyID); parserContext.mode = cssParserMode(); // When replacing an existing property value, this moves the property to the end of the list. // Firefox preserves the position, and MSIE moves the property to the beginning. return CSSParser::parseValue(*this, propertyID, value, important, parserContext) == CSSParser::ParseResult::Changed; } bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important) { CSSParserContext parserContext(cssParserMode()); return setProperty(propertyID, value, important, parserContext); } bool MutableStyleProperties::setCustomProperty(const Document* document, const String& propertyName, const String& value, bool important, CSSParserContext parserContext) { // Setting the value to an empty string just removes the property in both IE and Gecko. // Setting it to null seems to produce less consistent results, but we treat it just the same. if (value.isEmpty()) return removeCustomProperty(propertyName); parserContext.mode = cssParserMode(); String syntax = "*"; auto* registered = document ? document->getCSSRegisteredCustomPropertySet().get(propertyName) : nullptr; if (registered) syntax = registered->syntax; CSSTokenizer tokenizer(value); if (!CSSPropertyParser::canParseTypedCustomPropertyValue(syntax, tokenizer.tokenRange(), parserContext)) return false; // When replacing an existing property value, this moves the property to the end of the list. // Firefox preserves the position, and MSIE moves the property to the beginning. return CSSParser::parseCustomPropertyValue(*this, propertyName, value, important, parserContext) == CSSParser::ParseResult::Changed; } void MutableStyleProperties::setProperty(CSSPropertyID propertyID, RefPtr&& value, bool important) { StylePropertyShorthand shorthand = shorthandForProperty(propertyID); if (!shorthand.length()) { setProperty(CSSProperty(propertyID, WTFMove(value), important)); return; } removePropertiesInSet(shorthand.properties(), shorthand.length()); for (auto longhand : shorthand) m_propertyVector.append(CSSProperty(longhand, value.copyRef(), important)); } bool MutableStyleProperties::canUpdateInPlace(const CSSProperty& property, CSSProperty* toReplace) const { // If the property is in a logical property group, we can't just update the value in-place, // because afterwards there might be another property of the same group but different mapping logic. // In that case the latter might override the former, so setProperty would have no effect. CSSPropertyID id = property.id(); if (CSSProperty::isInLogicalPropertyGroup(id)) { ASSERT(toReplace >= m_propertyVector.begin()); ASSERT(toReplace < m_propertyVector.end()); for (CSSProperty* it = toReplace + 1; it != m_propertyVector.end(); ++it) { if (CSSProperty::areInSameLogicalPropertyGroupWithDifferentMappingLogic(id, it->id())) return false; } } return true; } bool MutableStyleProperties::setProperty(const CSSProperty& property, CSSProperty* slot) { if (!removeShorthandProperty(property.id())) { CSSProperty* toReplace = slot; if (!slot) { if (property.id() == CSSPropertyCustom) { if (property.value()) toReplace = findCustomCSSPropertyWithName(downcast(*property.value()).name()); } else toReplace = findCSSPropertyWithID(property.id()); } if (toReplace) { if (canUpdateInPlace(property, toReplace)) { if (*toReplace == property) return false; *toReplace = property; return true; } m_propertyVector.remove(toReplace - m_propertyVector.begin()); toReplace = nullptr; } } m_propertyVector.append(property); return true; } bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important) { return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important)); } bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important) { return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important)); } bool MutableStyleProperties::parseDeclaration(const String& styleDeclaration, CSSParserContext context) { auto oldProperties = WTFMove(m_propertyVector); m_propertyVector.clear(); context.mode = cssParserMode(); CSSParser parser(context); parser.parseDeclaration(*this, styleDeclaration); // We could do better. Just changing property order does not require style invalidation. return oldProperties != m_propertyVector; } bool MutableStyleProperties::addParsedProperties(const ParsedPropertyVector& properties) { bool anyChanged = false; m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size()); for (const auto& property : properties) { if (addParsedProperty(property)) anyChanged = true; } return anyChanged; } bool MutableStyleProperties::addParsedProperty(const CSSProperty& property) { if (property.id() == CSSPropertyCustom) { if ((property.value() && !customPropertyIsImportant(downcast(*property.value()).name())) || property.isImportant()) return setProperty(property); return false; } return setProperty(property); } String StyleProperties::asText() const { StringBuilder result; int positionXPropertyIndex = -1; int positionYPropertyIndex = -1; int repeatXPropertyIndex = -1; int repeatYPropertyIndex = -1; std::bitset shorthandPropertyUsed; std::bitset shorthandPropertyAppeared; unsigned size = propertyCount(); unsigned numDecls = 0; for (unsigned n = 0; n < size; ++n) { PropertyReference property = propertyAt(n); CSSPropertyID propertyID = property.id(); CSSPropertyID shorthandPropertyID = CSSPropertyInvalid; CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid; CSSPropertyID borderBlockFallbackShorthandProperty = CSSPropertyInvalid; CSSPropertyID borderInlineFallbackShorthandProperty = CSSPropertyInvalid; String value; auto serializeBorderShorthand = [&] (const CSSPropertyID borderProperty, const CSSPropertyID fallbackProperty) { // FIXME: Deal with cases where only some of border sides are specified. ASSERT(borderProperty - firstCSSProperty < static_cast(shorthandPropertyAppeared.size())); if (!shorthandPropertyAppeared[borderProperty - firstCSSProperty] && isEnabledCSSProperty(borderProperty)) { value = getPropertyValue(borderProperty); if (value.isNull()) shorthandPropertyAppeared.set(borderProperty - firstCSSProperty); else shorthandPropertyID = borderProperty; } else if (shorthandPropertyUsed[borderProperty - firstCSSProperty]) shorthandPropertyID = borderProperty; if (!shorthandPropertyID) shorthandPropertyID = fallbackProperty; }; if (is(property.value())) { auto& substitutionValue = downcast(*property.value()); shorthandPropertyID = substitutionValue.shorthandPropertyId(); value = substitutionValue.shorthandValue().cssText(); } else { switch (propertyID) { case CSSPropertyAnimationName: case CSSPropertyAnimationDuration: case CSSPropertyAnimationTimingFunction: case CSSPropertyAnimationDelay: case CSSPropertyAnimationIterationCount: case CSSPropertyAnimationDirection: case CSSPropertyAnimationFillMode: case CSSPropertyAnimationPlayState: shorthandPropertyID = CSSPropertyAnimation; break; case CSSPropertyBackgroundPositionX: positionXPropertyIndex = n; continue; case CSSPropertyBackgroundPositionY: positionYPropertyIndex = n; continue; case CSSPropertyBackgroundRepeatX: repeatXPropertyIndex = n; continue; case CSSPropertyBackgroundRepeatY: repeatYPropertyIndex = n; continue; case CSSPropertyBorderTopWidth: case CSSPropertyBorderRightWidth: case CSSPropertyBorderBottomWidth: case CSSPropertyBorderLeftWidth: if (!borderFallbackShorthandProperty) borderFallbackShorthandProperty = CSSPropertyBorderWidth; FALLTHROUGH; case CSSPropertyBorderTopStyle: case CSSPropertyBorderRightStyle: case CSSPropertyBorderBottomStyle: case CSSPropertyBorderLeftStyle: if (!borderFallbackShorthandProperty) borderFallbackShorthandProperty = CSSPropertyBorderStyle; FALLTHROUGH; case CSSPropertyBorderTopColor: case CSSPropertyBorderRightColor: case CSSPropertyBorderBottomColor: case CSSPropertyBorderLeftColor: if (!borderFallbackShorthandProperty) borderFallbackShorthandProperty = CSSPropertyBorderColor; serializeBorderShorthand(CSSPropertyBorder, borderFallbackShorthandProperty); break; case CSSPropertyBorderBlockStartWidth: case CSSPropertyBorderBlockEndWidth: if (!borderBlockFallbackShorthandProperty) borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockWidth; FALLTHROUGH; case CSSPropertyBorderBlockStartStyle: case CSSPropertyBorderBlockEndStyle: if (!borderBlockFallbackShorthandProperty) borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockStyle; FALLTHROUGH; case CSSPropertyBorderBlockStartColor: case CSSPropertyBorderBlockEndColor: if (!borderBlockFallbackShorthandProperty) borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockColor; serializeBorderShorthand(CSSPropertyBorderBlock, borderBlockFallbackShorthandProperty); break; case CSSPropertyBorderInlineStartWidth: case CSSPropertyBorderInlineEndWidth: if (!borderInlineFallbackShorthandProperty) borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineWidth; FALLTHROUGH; case CSSPropertyBorderInlineStartStyle: case CSSPropertyBorderInlineEndStyle: if (!borderInlineFallbackShorthandProperty) borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineStyle; FALLTHROUGH; case CSSPropertyBorderInlineStartColor: case CSSPropertyBorderInlineEndColor: if (!borderInlineFallbackShorthandProperty) borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineColor; serializeBorderShorthand(CSSPropertyBorderInline, borderInlineFallbackShorthandProperty); break; case CSSPropertyWebkitBorderHorizontalSpacing: case CSSPropertyWebkitBorderVerticalSpacing: shorthandPropertyID = CSSPropertyBorderSpacing; break; case CSSPropertyFontFamily: case CSSPropertyLineHeight: case CSSPropertyFontSize: case CSSPropertyFontStyle: case CSSPropertyFontVariantCaps: case CSSPropertyFontWeight: // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing. break; case CSSPropertyTop: case CSSPropertyRight: case CSSPropertyBottom: case CSSPropertyLeft: shorthandPropertyID = CSSPropertyInset; break; case CSSPropertyInsetBlockStart: case CSSPropertyInsetBlockEnd: shorthandPropertyID = CSSPropertyInsetBlock; break; case CSSPropertyInsetInlineStart: case CSSPropertyInsetInlineEnd: shorthandPropertyID = CSSPropertyInsetInline; break; case CSSPropertyListStyleType: case CSSPropertyListStylePosition: case CSSPropertyListStyleImage: shorthandPropertyID = CSSPropertyListStyle; break; case CSSPropertyMarginTop: case CSSPropertyMarginRight: case CSSPropertyMarginBottom: case CSSPropertyMarginLeft: shorthandPropertyID = CSSPropertyMargin; break; case CSSPropertyMarginBlockStart: case CSSPropertyMarginBlockEnd: shorthandPropertyID = CSSPropertyMarginBlock; break; case CSSPropertyMarginInlineStart: case CSSPropertyMarginInlineEnd: shorthandPropertyID = CSSPropertyMarginInline; break; case CSSPropertyOutlineWidth: case CSSPropertyOutlineStyle: case CSSPropertyOutlineColor: shorthandPropertyID = CSSPropertyOutline; break; case CSSPropertyOverflowX: case CSSPropertyOverflowY: shorthandPropertyID = CSSPropertyOverflow; break; case CSSPropertyOverscrollBehaviorX: case CSSPropertyOverscrollBehaviorY: shorthandPropertyID = CSSPropertyOverscrollBehavior; break; case CSSPropertyPaddingTop: case CSSPropertyPaddingRight: case CSSPropertyPaddingBottom: case CSSPropertyPaddingLeft: shorthandPropertyID = CSSPropertyPadding; break; case CSSPropertyPaddingBlockStart: case CSSPropertyPaddingBlockEnd: shorthandPropertyID = CSSPropertyPaddingBlock; break; case CSSPropertyPaddingInlineStart: case CSSPropertyPaddingInlineEnd: shorthandPropertyID = CSSPropertyPaddingInline; break; case CSSPropertyScrollMarginTop: case CSSPropertyScrollMarginRight: case CSSPropertyScrollMarginBottom: case CSSPropertyScrollMarginLeft: shorthandPropertyID = CSSPropertyScrollMargin; break; case CSSPropertyScrollMarginBlockStart: case CSSPropertyScrollMarginBlockEnd: shorthandPropertyID = CSSPropertyScrollMarginBlock; break; case CSSPropertyScrollMarginInlineStart: case CSSPropertyScrollMarginInlineEnd: shorthandPropertyID = CSSPropertyScrollMarginInline; break; case CSSPropertyScrollPaddingTop: case CSSPropertyScrollPaddingRight: case CSSPropertyScrollPaddingBottom: case CSSPropertyScrollPaddingLeft: shorthandPropertyID = CSSPropertyScrollPadding; break; case CSSPropertyScrollPaddingBlockStart: case CSSPropertyScrollPaddingBlockEnd: shorthandPropertyID = CSSPropertyScrollPaddingBlock; break; case CSSPropertyScrollPaddingInlineStart: case CSSPropertyScrollPaddingInlineEnd: shorthandPropertyID = CSSPropertyScrollPaddingInline; break; case CSSPropertyTransitionProperty: case CSSPropertyTransitionDuration: case CSSPropertyTransitionTimingFunction: case CSSPropertyTransitionDelay: shorthandPropertyID = CSSPropertyTransition; break; case CSSPropertyFlexDirection: case CSSPropertyFlexWrap: shorthandPropertyID = CSSPropertyFlexFlow; break; case CSSPropertyFlexBasis: case CSSPropertyFlexGrow: case CSSPropertyFlexShrink: shorthandPropertyID = CSSPropertyFlex; break; case CSSPropertyWebkitMaskPositionX: case CSSPropertyWebkitMaskPositionY: case CSSPropertyWebkitMaskRepeatX: case CSSPropertyWebkitMaskRepeatY: case CSSPropertyWebkitMaskImage: case CSSPropertyWebkitMaskRepeat: case CSSPropertyWebkitMaskPosition: case CSSPropertyWebkitMaskClip: case CSSPropertyWebkitMaskOrigin: shorthandPropertyID = CSSPropertyWebkitMask; break; case CSSPropertyPerspectiveOriginX: case CSSPropertyPerspectiveOriginY: shorthandPropertyID = CSSPropertyPerspectiveOrigin; break; case CSSPropertyTransformOriginX: case CSSPropertyTransformOriginY: case CSSPropertyTransformOriginZ: shorthandPropertyID = CSSPropertyTransformOrigin; break; default: break; } } unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty; if (shorthandPropertyID && isEnabledCSSProperty(shorthandPropertyID)) { ASSERT(shortPropertyIndex < shorthandPropertyUsed.size()); if (shorthandPropertyUsed[shortPropertyIndex]) continue; if (!shorthandPropertyAppeared[shortPropertyIndex] && value.isNull()) value = getPropertyValue(shorthandPropertyID); shorthandPropertyAppeared.set(shortPropertyIndex); } if (!value.isNull()) { propertyID = shorthandPropertyID; shorthandPropertyUsed.set(shortPropertyIndex); } else value = property.value()->cssText(); if (propertyID != CSSPropertyCustom && value == "initial" && !CSSProperty::isInheritedProperty(propertyID)) continue; if (numDecls++) result.append(' '); if (propertyID == CSSPropertyCustom) result.append(downcast(*property.value()).name()); else result.append(getPropertyName(propertyID)); result.append(": ", value, property.isImportant() ? " !important" : "", ';'); } // FIXME: This is a not-so-nice way to turn x/y positions into single background-position/repeat in output. // In 2007 we decided this was required because background-position/repeat-x/y are non-standard properties and WebKit generated output would not work in Firefox (). auto appendPositionOrProperty = [&] (int xIndex, int yIndex, const char* name, const StylePropertyShorthand& shorthand) { if (xIndex != -1 && yIndex != -1 && propertyAt(xIndex).isImportant() == propertyAt(yIndex).isImportant()) { String value; auto xProperty = propertyAt(xIndex); auto yProperty = propertyAt(yIndex); if (xProperty.value()->isValueList() || yProperty.value()->isValueList()) value = getLayeredShorthandValue(shorthand); else { auto x = xProperty.value()->cssText(); auto y = yProperty.value()->cssText(); if (x == y && isCSSWideValueKeyword(x)) value = x; else value = makeString(x, ' ', y); } if (value != "initial") { result.append(numDecls ? " " : "", name, ": ", value, xProperty.isImportant() ? " !important" : "", ';'); ++numDecls; } } else { if (xIndex != -1) { if (numDecls++) result.append(' '); result.append(propertyAt(xIndex).cssText()); } if (yIndex != -1) { if (numDecls++) result.append(' '); result.append(propertyAt(yIndex).cssText()); } } }; appendPositionOrProperty(positionXPropertyIndex, positionYPropertyIndex, "background-position", backgroundPositionShorthand()); appendPositionOrProperty(repeatXPropertyIndex, repeatYPropertyIndex, "background-repeat", backgroundRepeatShorthand()); ASSERT(!numDecls ^ !result.isEmpty()); return result.toString(); } bool StyleProperties::hasCSSOMWrapper() const { return is(*this) && downcast(*this).m_cssomWrapper; } void MutableStyleProperties::mergeAndOverrideOnConflict(const StyleProperties& other) { unsigned size = other.propertyCount(); for (unsigned i = 0; i < size; ++i) addParsedProperty(other.propertyAt(i).toCSSProperty()); } bool StyleProperties::traverseSubresources(const WTF::Function& handler) const { unsigned size = propertyCount(); for (unsigned i = 0; i < size; ++i) { if (propertyAt(i).value()->traverseSubresources(handler)) return true; } return false; } // This is the list of properties we want to copy in the copyBlockProperties() function. // It is the list of CSS properties that apply specially to block-level elements. static const CSSPropertyID blockProperties[] = { CSSPropertyOrphans, CSSPropertyOverflow, // This can be also be applied to replaced elements CSSPropertyColumnCount, CSSPropertyColumnGap, CSSPropertyRowGap, CSSPropertyColumnRuleColor, CSSPropertyColumnRuleStyle, CSSPropertyColumnRuleWidth, CSSPropertyWebkitColumnBreakBefore, CSSPropertyWebkitColumnBreakAfter, CSSPropertyWebkitColumnBreakInside, CSSPropertyColumnWidth, CSSPropertyPageBreakAfter, CSSPropertyPageBreakBefore, CSSPropertyPageBreakInside, CSSPropertyTextAlign, #if ENABLE(CSS3_TEXT) CSSPropertyWebkitTextAlignLast, CSSPropertyWebkitTextJustify, #endif // CSS3_TEXT CSSPropertyTextIndent, CSSPropertyWidows }; void MutableStyleProperties::clear() { m_propertyVector.clear(); } const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties); Ref StyleProperties::copyBlockProperties() const { return copyPropertiesInSet(blockProperties, numBlockProperties); } void MutableStyleProperties::removeBlockProperties() { removePropertiesInSet(blockProperties, numBlockProperties); } bool MutableStyleProperties::removePropertiesInSet(const CSSPropertyID* set, unsigned length) { if (m_propertyVector.isEmpty()) return false; // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless. HashSet toRemove; for (unsigned i = 0; i < length; ++i) toRemove.add(set[i]); return m_propertyVector.removeAllMatching([&toRemove] (const CSSProperty& property) { return toRemove.contains(property.id()); }) > 0; } int ImmutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const { // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid // the compiler converting it to an int multiple times in the loop. uint16_t id = static_cast(propertyID); for (int n = m_arraySize - 1 ; n >= 0; --n) { if (metadataArray()[n].m_propertyID == id) return n; } return -1; } int MutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const { // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid // the compiler converting it to an int multiple times in the loop. uint16_t id = static_cast(propertyID); for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) { if (m_propertyVector.at(n).metadata().m_propertyID == id) return n; } return -1; } int ImmutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const { for (int n = m_arraySize - 1 ; n >= 0; --n) { if (metadataArray()[n].m_propertyID == CSSPropertyCustom) { // We found a custom property. See if the name matches. auto* value = valueArray()[n].get(); if (!value) continue; if (downcast(*value).name() == propertyName) return n; } } return -1; } int MutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const { for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) { if (m_propertyVector.at(n).metadata().m_propertyID == CSSPropertyCustom) { // We found a custom property. See if the name matches. if (!m_propertyVector.at(n).value()) continue; if (downcast(*m_propertyVector.at(n).value()).name() == propertyName) return n; } } return -1; } CSSProperty* MutableStyleProperties::findCSSPropertyWithID(CSSPropertyID propertyID) { int foundPropertyIndex = findPropertyIndex(propertyID); if (foundPropertyIndex == -1) return 0; return &m_propertyVector.at(foundPropertyIndex); } CSSProperty* MutableStyleProperties::findCustomCSSPropertyWithName(const String& propertyName) { int foundPropertyIndex = findCustomPropertyIndex(propertyName); if (foundPropertyIndex == -1) return 0; return &m_propertyVector.at(foundPropertyIndex); } bool StyleProperties::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const { int foundPropertyIndex = findPropertyIndex(propertyID); if (foundPropertyIndex == -1) return false; return propertyAt(foundPropertyIndex).value()->equals(*propertyValue); } Ref StyleProperties::mutableCopy() const { return adoptRef(*new MutableStyleProperties(*this)); } Ref StyleProperties::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const { Vector list; list.reserveInitialCapacity(length); for (unsigned i = 0; i < length; ++i) { if (auto value = getPropertyCSSValue(set[i])) list.uncheckedAppend(CSSProperty(set[i], WTFMove(value), false)); } return MutableStyleProperties::create(WTFMove(list)); } PropertySetCSSStyleDeclaration* MutableStyleProperties::cssStyleDeclaration() { return m_cssomWrapper.get(); } CSSStyleDeclaration& MutableStyleProperties::ensureCSSStyleDeclaration() { if (m_cssomWrapper) { ASSERT(!static_cast(m_cssomWrapper.get())->parentRule()); ASSERT(!m_cssomWrapper->parentElement()); return *m_cssomWrapper; } m_cssomWrapper = makeUnique(*this); return *m_cssomWrapper; } CSSStyleDeclaration& MutableStyleProperties::ensureInlineCSSStyleDeclaration(StyledElement& parentElement) { if (m_cssomWrapper) { ASSERT(m_cssomWrapper->parentElement() == &parentElement); return *m_cssomWrapper; } m_cssomWrapper = makeUnique(*this, parentElement); return *m_cssomWrapper; } unsigned StyleProperties::averageSizeInBytes() { // Please update this if the storage scheme changes so that this longer reflects the actual size. return sizeForImmutableStylePropertiesWithPropertyCount(4); } // See the function above if you need to update this. struct SameSizeAsStyleProperties : public RefCounted { unsigned bitfield; }; COMPILE_ASSERT(sizeof(StyleProperties) == sizeof(SameSizeAsStyleProperties), style_property_set_should_stay_small); #ifndef NDEBUG void StyleProperties::showStyle() { fprintf(stderr, "%s\n", asText().ascii().data()); } #endif Ref MutableStyleProperties::create(CSSParserMode cssParserMode) { return adoptRef(*new MutableStyleProperties(cssParserMode)); } Ref MutableStyleProperties::create(Vector&& properties) { return adoptRef(*new MutableStyleProperties(WTFMove(properties))); } String StyleProperties::PropertyReference::cssName() const { if (id() == CSSPropertyCustom) return downcast(*value()).name(); return getPropertyNameString(id()); } String StyleProperties::PropertyReference::cssText() const { return makeString(cssName(), ": ", m_value->cssText(), isImportant() ? " !important" : "", ';'); } Ref DeferredStyleProperties::create(const CSSParserTokenRange& tokenRange, CSSDeferredParser& parser) { return adoptRef(*new DeferredStyleProperties(tokenRange, parser)); } DeferredStyleProperties::DeferredStyleProperties(const CSSParserTokenRange& range, CSSDeferredParser& parser) : StylePropertiesBase(parser.mode(), DeferredPropertiesType) , m_parser(parser) { size_t length = range.end() - range.begin(); m_tokens.reserveCapacity(length); m_tokens.append(range.begin(), length); } DeferredStyleProperties::~DeferredStyleProperties() = default; Ref DeferredStyleProperties::parseDeferredProperties() { return m_parser->parseDeclaration(m_tokens); } } // namespace WebCore