summaryrefslogtreecommitdiff
path: root/Source/WebCore/css/StyleProperties.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/css/StyleProperties.cpp')
-rw-r--r--Source/WebCore/css/StyleProperties.cpp1438
1 files changed, 1438 insertions, 0 deletions
diff --git a/Source/WebCore/css/StyleProperties.cpp b/Source/WebCore/css/StyleProperties.cpp
new file mode 100644
index 000000000..10bb2849d
--- /dev/null
+++ b/Source/WebCore/css/StyleProperties.cpp
@@ -0,0 +1,1438 @@
+/*
+ * (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 "CSSParser.h"
+#include "CSSValueKeywords.h"
+#include "CSSValueList.h"
+#include "CSSValuePool.h"
+#include "CSSVariableDependentValue.h"
+#include "Document.h"
+#include "PropertySetCSSStyleDeclaration.h"
+#include "StylePropertyShorthand.h"
+#include "StyleSheetContents.h"
+#include <bitset>
+#include <wtf/text/StringBuilder.h>
+
+#ifndef NDEBUG
+#include <stdio.h>
+#include <wtf/ASCIICType.h>
+#include <wtf/text/CString.h>
+#endif
+
+namespace WebCore {
+
+static size_t sizeForImmutableStylePropertiesWithPropertyCount(unsigned count)
+{
+ return sizeof(ImmutableStyleProperties) - sizeof(void*) + sizeof(CSSValue*) * count + sizeof(StylePropertyMetadata) * count;
+}
+
+static bool isInitialOrInherit(const String& value)
+{
+ return value.length() == 7 && (value == "initial" || value == "inherit");
+}
+
+Ref<ImmutableStyleProperties> ImmutableStyleProperties::create(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
+{
+ void* slot = WTF::fastMalloc(sizeForImmutableStylePropertiesWithPropertyCount(count));
+ return adoptRef(*new (NotNull, slot) ImmutableStyleProperties(properties, count, cssParserMode));
+}
+
+Ref<ImmutableStyleProperties> StyleProperties::immutableCopyIfNeeded() const
+{
+ if (is<ImmutableStyleProperties>(*this))
+ return downcast<ImmutableStyleProperties>(const_cast<StyleProperties&>(*this));
+ const MutableStyleProperties& mutableThis = downcast<MutableStyleProperties>(*this);
+ return ImmutableStyleProperties::create(mutableThis.m_propertyVector.data(), mutableThis.m_propertyVector.size(), cssParserMode());
+}
+
+MutableStyleProperties::MutableStyleProperties(CSSParserMode cssParserMode)
+ : StyleProperties(cssParserMode)
+{
+}
+
+MutableStyleProperties::MutableStyleProperties(const CSSProperty* properties, unsigned length)
+ : StyleProperties(CSSStrictMode)
+{
+ m_propertyVector.reserveInitialCapacity(length);
+ for (unsigned i = 0; i < length; ++i)
+ m_propertyVector.uncheckedAppend(properties[i]);
+}
+
+MutableStyleProperties::~MutableStyleProperties()
+{
+}
+
+ImmutableStyleProperties::ImmutableStyleProperties(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode)
+ : StyleProperties(cssParserMode, length)
+{
+ StylePropertyMetadata* metadataArray = const_cast<StylePropertyMetadata*>(this->metadataArray());
+ CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
+ for (unsigned i = 0; i < length; ++i) {
+ metadataArray[i] = properties[i].metadata();
+ valueArray[i] = properties[i].value();
+ valueArray[i]->ref();
+ }
+}
+
+ImmutableStyleProperties::~ImmutableStyleProperties()
+{
+ CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
+ for (unsigned i = 0; i < m_arraySize; ++i)
+ valueArray[i]->deref();
+}
+
+MutableStyleProperties::MutableStyleProperties(const StyleProperties& other)
+ : StyleProperties(other.cssParserMode())
+{
+ if (is<MutableStyleProperties>(other))
+ m_propertyVector = downcast<MutableStyleProperties>(other).m_propertyVector;
+ else {
+ const auto& immutableOther = downcast<ImmutableStyleProperties>(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
+{
+ RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
+ if (value)
+ return value->cssText();
+
+ // 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(OmitUncommonValues);
+ case CSSPropertyBorderTop:
+ return getShorthandValue(borderTopShorthand());
+ case CSSPropertyBorderRight:
+ return getShorthandValue(borderRightShorthand());
+ case CSSPropertyBorderBottom:
+ return getShorthandValue(borderBottomShorthand());
+ case CSSPropertyBorderLeft:
+ return getShorthandValue(borderLeftShorthand());
+ 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());
+#if ENABLE(CSS_GRID_LAYOUT)
+ case CSSPropertyWebkitGridArea:
+ return getShorthandValue(webkitGridAreaShorthand());
+ case CSSPropertyWebkitGridTemplate:
+ return getShorthandValue(webkitGridTemplateShorthand());
+ case CSSPropertyWebkitGrid:
+ return getShorthandValue(webkitGridShorthand());
+ case CSSPropertyWebkitGridColumn:
+ return getShorthandValue(webkitGridColumnShorthand());
+ case CSSPropertyWebkitGridRow:
+ return getShorthandValue(webkitGridRowShorthand());
+#endif
+ case CSSPropertyFont:
+ return fontValue();
+ case CSSPropertyMargin:
+ return get4Values(marginShorthand());
+ case CSSPropertyWebkitMarginCollapse:
+ return getShorthandValue(webkitMarginCollapseShorthand());
+ case CSSPropertyOverflow:
+ return getCommonValue(overflowShorthand());
+ case CSSPropertyPadding:
+ return get4Values(paddingShorthand());
+ case CSSPropertyTransition:
+ return getLayeredShorthandValue(transitionShorthand());
+ case CSSPropertyListStyle:
+ return getShorthandValue(listStyleShorthand());
+ case CSSPropertyWebkitMarquee:
+ return getShorthandValue(webkitMarqueeShorthand());
+ 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 CSSPropertyWebkitTransition:
+ return getLayeredShorthandValue(webkitTransitionShorthand());
+ case CSSPropertyWebkitAnimation:
+ return getLayeredShorthandValue(webkitAnimationShorthand());
+ case CSSPropertyMarker: {
+ RefPtr<CSSValue> value = getPropertyCSSValueInternal(CSSPropertyMarkerStart);
+ if (value)
+ return value->cssText();
+ return String();
+ }
+ case CSSPropertyBorderRadius:
+ return get4Values(borderRadiusShorthand());
+ default:
+ return String();
+ }
+}
+
+String StyleProperties::getCustomPropertyValue(const String& propertyName) const
+{
+ RefPtr<CSSValue> value = getCustomPropertyCSSValue(propertyName);
+ if (value)
+ return value->cssText();
+ return String();
+}
+
+String StyleProperties::borderSpacingValue(const StylePropertyShorthand& shorthand) const
+{
+ RefPtr<CSSValue> horizontalValue = getPropertyCSSValueInternal(shorthand.properties()[0]);
+ if (horizontalValue && horizontalValue->isVariableDependentValue())
+ return horizontalValue->cssText();
+
+ RefPtr<CSSValue> verticalValue = getPropertyCSSValueInternal(shorthand.properties()[1]);
+
+ // While standard border-spacing property does not allow specifying border-spacing-vertical without
+ // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
+ // -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 CSSPropertyFontVariantCaps:
+ case CSSPropertyFontWeight:
+ 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);
+ 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 (isInitialOrInherit(commonValue))
+ return commonValue;
+ 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();
+
+ 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();
+ }
+ if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
+ 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();
+ // Begin by collecting the properties into an array.
+ Vector< RefPtr<CSSValue>> values(size);
+ size_t numLayers = 0;
+
+ for (unsigned i = 0; i < size; ++i) {
+ values[i] = getPropertyCSSValueInternal(shorthand.properties()[i]);
+ if (values[i]) {
+ if (values[i]->isVariableDependentValue())
+ return values[i]->cssText();
+ if (values[i]->isBaseValueList())
+ numLayers = std::max(downcast<CSSValueList>(*values[i]).length(), numLayers);
+ else
+ numLayers = std::max<size_t>(1U, numLayers);
+ }
+ }
+
+ String commonValue;
+ bool commonValueInitialized = false;
+
+ // 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++) {
+ RefPtr<CSSValue> value;
+ if (values[j]) {
+ if (values[j]->isBaseValueList())
+ value = downcast<CSSValueList>(*values[j]).item(i);
+ else {
+ value = values[j];
+
+ // Color only belongs in the last layer.
+ if (shorthand.properties()[j] == 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 ((shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(shorthand.properties()[j]))
+ || (shorthand.properties()[j] == CSSPropertyWebkitMaskRepeatX && isPropertyImplicit(shorthand.properties()[j]))) {
+
+ // BUG 49055: make sure the value was not reset in the layer check just above.
+ if ((j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value)
+ || (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyWebkitMaskRepeatY && value)) {
+ RefPtr<CSSValue> yValue;
+ RefPtr<CSSValue> nextValue = values[j + 1];
+ if (nextValue) {
+ if (is<CSSValueList>(*nextValue))
+ yValue = downcast<CSSValueList>(*nextValue).itemWithoutBoundsCheck(i);
+ else
+ yValue = nextValue;
+
+ if (!is<CSSPrimitiveValue>(*value) || !is<CSSPrimitiveValue>(*yValue))
+ continue;
+
+ CSSValueID xId = downcast<CSSPrimitiveValue>(*value).getValueID();
+ CSSValueID yId = downcast<CSSPrimitiveValue>(*yValue).getValueID();
+ 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 (foundPositionYCSSProperty
+ && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
+ layerResult.appendLiteral("/ ");
+ if (!foundPositionYCSSProperty
+ && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
+ continue;
+
+ 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 (shorthand.properties()[j] == CSSPropertyBackgroundPositionY
+ || shorthand.properties()[j] == CSSPropertyWebkitMaskPositionY) {
+ foundPositionYCSSProperty = true;
+
+ // background-position is a special case: if only the first offset is specified,
+ // the second one defaults to "center", not the same value.
+ if (commonValueInitialized && commonValue != "initial" && commonValue != "inherit")
+ commonValue = String();
+ }
+ }
+
+ if (!commonValueInitialized) {
+ commonValue = valueText;
+ commonValueInitialized = true;
+ } else if (!commonValue.isNull() && commonValue != valueText)
+ commonValue = String();
+ }
+
+ if (!layerResult.isEmpty()) {
+ if (!result.isEmpty())
+ result.appendLiteral(", ");
+ result.append(layerResult);
+ }
+ }
+
+ if (isInitialOrInherit(commonValue))
+ return commonValue;
+
+ if (result.isEmpty())
+ return String();
+ return result.toString();
+}
+
+String StyleProperties::getShorthandValue(const StylePropertyShorthand& shorthand) const
+{
+ String commonValue;
+ StringBuilder result;
+ for (unsigned i = 0; i < shorthand.length(); ++i) {
+ if (!isPropertyImplicit(shorthand.properties()[i])) {
+ RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[i]);
+ if (!value)
+ return String();
+ if (value->isVariableDependentValue())
+ return value->cssText();
+ 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(' ');
+ result.append(valueText);
+ } else
+ commonValue = String();
+ }
+ if (isInitialOrInherit(commonValue))
+ return commonValue;
+ if (result.isEmpty())
+ return String();
+ return result.toString();
+}
+
+// only returns a non-null value if all properties have the same, non-null value
+String StyleProperties::getCommonValue(const StylePropertyShorthand& shorthand) const
+{
+ String res;
+ bool lastPropertyWasImportant = false;
+ for (unsigned i = 0; i < shorthand.length(); ++i) {
+ RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[i]);
+ // FIXME: CSSInitialValue::cssText should generate the right value.
+ if (!value)
+ return String();
+ if (value->isVariableDependentValue())
+ return value->cssText();
+ String text = value->cssText();
+ if (text.isNull())
+ return String();
+ if (res.isNull())
+ res = text;
+ else if (res != text)
+ return String();
+
+ bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]);
+ if (i && lastPropertyWasImportant != currentPropertyIsImportant)
+ return String();
+ lastPropertyWasImportant = currentPropertyIsImportant;
+ }
+ return res;
+}
+
+String StyleProperties::borderPropertyValue(CommonValueMode valueMode) const
+{
+ const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
+ String commonValue;
+ StringBuilder result;
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
+ String value = getCommonValue(properties[i]);
+ if (value.isNull()) {
+ if (valueMode == ReturnNullOnUncommonValues)
+ return String();
+ ASSERT(valueMode == OmitUncommonValues);
+ continue;
+ }
+ if (!i)
+ commonValue = value;
+ else if (!commonValue.isNull() && commonValue != value)
+ commonValue = String();
+ if (value == "initial")
+ continue;
+ if (!result.isEmpty())
+ result.append(' ');
+ result.append(value);
+ }
+ if (isInitialOrInherit(commonValue))
+ return commonValue;
+ return result.isEmpty() ? String() : result.toString();
+}
+
+PassRefPtr<CSSValue> StyleProperties::getPropertyCSSValue(CSSPropertyID propertyID) const
+{
+ PassRefPtr<CSSValue> value = getPropertyCSSValueInternal(propertyID);
+ if (value && value->isVariableDependentValue()) {
+ auto& dependentValue = downcast<CSSVariableDependentValue>(*value);
+ if (dependentValue.propertyID() != propertyID)
+ return CSSCustomPropertyValue::createInvalid(); // Have to return special "pending-substitution" value.
+ }
+ return value;
+}
+
+PassRefPtr<CSSValue> StyleProperties::getPropertyCSSValueInternal(CSSPropertyID propertyID) const
+{
+ int foundPropertyIndex = findPropertyIndex(propertyID);
+ if (foundPropertyIndex == -1)
+ return nullptr;
+ return propertyAt(foundPropertyIndex).value();
+}
+
+RefPtr<CSSValue> 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;
+
+ bool ret = removePropertiesInSet(shorthand.properties(), shorthand.length());
+
+ CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(propertyID);
+ if (prefixingVariant == propertyID)
+ return ret;
+
+ StylePropertyShorthand shorthandPrefixingVariant = shorthandForProperty(prefixingVariant);
+ return removePropertiesInSet(shorthandPrefixingVariant.properties(), shorthandPrefixingVariant.length());
+}
+
+bool MutableStyleProperties::removeProperty(CSSPropertyID propertyID, String* returnText)
+{
+ if (removeShorthandProperty(propertyID)) {
+ // FIXME: Return an equivalent shorthand when possible.
+ if (returnText)
+ *returnText = "";
+ return true;
+ }
+
+ int foundPropertyIndex = findPropertyIndex(propertyID);
+ if (foundPropertyIndex == -1) {
+ if (returnText)
+ *returnText = "";
+ 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);
+
+ removePrefixedOrUnprefixedProperty(propertyID);
+
+ return true;
+}
+
+bool MutableStyleProperties::removeCustomProperty(const String& propertyName, String* returnText)
+{
+ int foundPropertyIndex = findCustomPropertyIndex(propertyName);
+ if (foundPropertyIndex == -1) {
+ if (returnText)
+ *returnText = "";
+ 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;
+}
+
+void MutableStyleProperties::removePrefixedOrUnprefixedProperty(CSSPropertyID propertyID)
+{
+ int foundPropertyIndex = findPropertyIndex(prefixingVariantForPropertyId(propertyID));
+ if (foundPropertyIndex == -1)
+ return;
+ m_propertyVector.remove(foundPropertyIndex);
+}
+
+bool StyleProperties::propertyIsImportant(CSSPropertyID propertyID) const
+{
+ int foundPropertyIndex = findPropertyIndex(propertyID);
+ if (foundPropertyIndex != -1)
+ return propertyAt(foundPropertyIndex).isImportant();
+
+ StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
+ if (!shorthand.length())
+ return false;
+
+ for (unsigned i = 0; i < shorthand.length(); ++i) {
+ if (!propertyIsImportant(shorthand.properties()[i]))
+ 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, StyleSheetContents* contextStyleSheet)
+{
+ // 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);
+
+ // 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, cssParserMode(), contextStyleSheet) == CSSParser::ParseResult::Changed;
+}
+
+bool MutableStyleProperties::setCustomProperty(const String& propertyName, const String& value, bool important, StyleSheetContents* contextStyleSheet)
+{
+ // 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);
+
+ // 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, cssParserMode(), contextStyleSheet) == CSSParser::ParseResult::Changed;
+}
+
+void MutableStyleProperties::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
+{
+ StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
+ if (!shorthand.length()) {
+ setProperty(CSSProperty(propertyID, prpValue, important));
+ return;
+ }
+
+ removePropertiesInSet(shorthand.properties(), shorthand.length());
+
+ RefPtr<CSSValue> value = prpValue;
+ for (unsigned i = 0; i < shorthand.length(); ++i)
+ m_propertyVector.append(CSSProperty(shorthand.properties()[i], value, important));
+}
+
+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<CSSCustomPropertyValue>(*property.value()).name());
+ } else
+ toReplace = findCSSPropertyWithID(property.id());
+ }
+
+ if (toReplace) {
+ if (*toReplace == property)
+ return false;
+
+ *toReplace = property;
+ setPrefixingVariantProperty(property);
+ return true;
+ }
+ }
+
+ return appendPrefixingVariantProperty(property);
+}
+
+static unsigned getIndexInShorthandVectorForPrefixingVariant(const CSSProperty& property, CSSPropertyID prefixingVariant)
+{
+ if (!property.isSetFromShorthand())
+ return 0;
+
+ CSSPropertyID prefixedShorthand = prefixingVariantForPropertyId(property.shorthandID());
+ return indexOfShorthandForLonghand(prefixedShorthand, matchingShorthandsForLonghand(prefixingVariant));
+}
+
+bool MutableStyleProperties::appendPrefixingVariantProperty(const CSSProperty& property)
+{
+ m_propertyVector.append(property);
+ CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(property.id());
+ if (prefixingVariant == property.id())
+ return true;
+
+ m_propertyVector.append(CSSProperty(prefixingVariant, property.value(), property.isImportant(), property.isSetFromShorthand(), getIndexInShorthandVectorForPrefixingVariant(property, prefixingVariant), property.metadata().m_implicit));
+ return true;
+}
+
+void MutableStyleProperties::setPrefixingVariantProperty(const CSSProperty& property)
+{
+ CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(property.id());
+ CSSProperty* toReplace = findCSSPropertyWithID(prefixingVariant);
+ if (toReplace && prefixingVariant != property.id())
+ *toReplace = CSSProperty(prefixingVariant, property.value(), property.isImportant(), property.isSetFromShorthand(), getIndexInShorthandVectorForPrefixingVariant(property, prefixingVariant), property.metadata().m_implicit);
+}
+
+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, StyleSheetContents* contextStyleSheet)
+{
+ auto oldProperties = WTFMove(m_propertyVector);
+ m_propertyVector.clear();
+
+ CSSParserContext context(cssParserMode());
+ if (contextStyleSheet) {
+ context = contextStyleSheet->parserContext();
+ context.mode = cssParserMode();
+ }
+ CSSParser parser(context);
+ parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
+
+ // We could do better. Just changing property order does not require style invalidation.
+ return oldProperties != m_propertyVector;
+}
+
+bool MutableStyleProperties::addParsedProperties(const CSSParser::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<CSSCustomPropertyValue>(*property.value()).name())) || property.isImportant())
+ return setProperty(property);
+ return false;
+ }
+
+ // Only add properties that have no !important counterpart present
+ if (!propertyIsImportant(property.id()) || property.isImportant())
+ return setProperty(property);
+
+ return false;
+}
+
+String StyleProperties::asText() const
+{
+ StringBuilder result;
+
+ int positionXPropertyIndex = -1;
+ int positionYPropertyIndex = -1;
+ int repeatXPropertyIndex = -1;
+ int repeatYPropertyIndex = -1;
+
+ std::bitset<numCSSProperties> shorthandPropertyUsed;
+ std::bitset<numCSSProperties> 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;
+ String value;
+
+ if (property.value() && property.value()->isVariableDependentValue()) {
+ auto& dependentValue = downcast<CSSVariableDependentValue>(*property.value());
+ if (dependentValue.propertyID() != propertyID)
+ shorthandPropertyID = dependentValue.propertyID();
+ } else {
+ switch (propertyID) {
+ case CSSPropertyAnimationName:
+ case CSSPropertyAnimationDuration:
+ case CSSPropertyAnimationTimingFunction:
+ case CSSPropertyAnimationDelay:
+ case CSSPropertyAnimationIterationCount:
+ case CSSPropertyAnimationDirection:
+ case CSSPropertyAnimationFillMode:
+ 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;
+
+ // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
+ ASSERT(CSSPropertyBorder - firstCSSProperty < shorthandPropertyAppeared.size());
+ if (!shorthandPropertyAppeared[CSSPropertyBorder - firstCSSProperty]) {
+ value = borderPropertyValue(ReturnNullOnUncommonValues);
+ if (value.isNull())
+ shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
+ else
+ shorthandPropertyID = CSSPropertyBorder;
+ } else if (shorthandPropertyUsed[CSSPropertyBorder - firstCSSProperty])
+ shorthandPropertyID = CSSPropertyBorder;
+ if (!shorthandPropertyID)
+ shorthandPropertyID = borderFallbackShorthandProperty;
+ 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 CSSPropertyListStyleType:
+ case CSSPropertyListStylePosition:
+ case CSSPropertyListStyleImage:
+ shorthandPropertyID = CSSPropertyListStyle;
+ break;
+ case CSSPropertyMarginTop:
+ case CSSPropertyMarginRight:
+ case CSSPropertyMarginBottom:
+ case CSSPropertyMarginLeft:
+ shorthandPropertyID = CSSPropertyMargin;
+ break;
+ case CSSPropertyOutlineWidth:
+ case CSSPropertyOutlineStyle:
+ case CSSPropertyOutlineColor:
+ shorthandPropertyID = CSSPropertyOutline;
+ break;
+ case CSSPropertyOverflowX:
+ case CSSPropertyOverflowY:
+ shorthandPropertyID = CSSPropertyOverflow;
+ break;
+ case CSSPropertyPaddingTop:
+ case CSSPropertyPaddingRight:
+ case CSSPropertyPaddingBottom:
+ case CSSPropertyPaddingLeft:
+ shorthandPropertyID = CSSPropertyPadding;
+ break;
+ case CSSPropertyTransitionProperty:
+ case CSSPropertyTransitionDuration:
+ case CSSPropertyTransitionTimingFunction:
+ case CSSPropertyTransitionDelay:
+ shorthandPropertyID = CSSPropertyTransition;
+ break;
+ case CSSPropertyWebkitAnimationName:
+ case CSSPropertyWebkitAnimationDuration:
+ case CSSPropertyWebkitAnimationTimingFunction:
+ case CSSPropertyWebkitAnimationDelay:
+ case CSSPropertyWebkitAnimationIterationCount:
+ case CSSPropertyWebkitAnimationDirection:
+ case CSSPropertyWebkitAnimationFillMode:
+ shorthandPropertyID = CSSPropertyWebkitAnimation;
+ 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;
+ case CSSPropertyWebkitTransitionProperty:
+ case CSSPropertyWebkitTransitionDuration:
+ case CSSPropertyWebkitTransitionTimingFunction:
+ case CSSPropertyWebkitTransitionDelay:
+ shorthandPropertyID = CSSPropertyWebkitTransition;
+ break;
+ default:
+ break;
+ }
+ }
+
+ unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
+ if (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<CSSCustomPropertyValue>(*property.value()).name());
+ else
+ result.append(getPropertyName(propertyID));
+
+ result.appendLiteral(": ");
+ result.append(value);
+ if (property.isImportant())
+ result.appendLiteral(" !important");
+ result.append(';');
+ }
+
+ // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
+ // It is required because background-position-x/y are non-standard properties and WebKit generated output
+ // would not work in Firefox (<rdar://problem/5143183>)
+ // It would be a better solution if background-position was CSS_PAIR.
+ if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && propertyAt(positionXPropertyIndex).isImportant() == propertyAt(positionYPropertyIndex).isImportant()) {
+ PropertyReference positionXProperty = propertyAt(positionXPropertyIndex);
+ PropertyReference positionYProperty = propertyAt(positionYPropertyIndex);
+
+ if (numDecls++)
+ result.append(' ');
+ result.appendLiteral("background-position: ");
+ if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList())
+ result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
+ else {
+ result.append(positionXProperty.value()->cssText());
+ result.append(' ');
+ result.append(positionYProperty.value()->cssText());
+ }
+ if (positionXProperty.isImportant())
+ result.appendLiteral(" !important");
+ result.append(';');
+ } else {
+ if (positionXPropertyIndex != -1) {
+ if (numDecls++)
+ result.append(' ');
+ result.append(propertyAt(positionXPropertyIndex).cssText());
+ }
+ if (positionYPropertyIndex != -1) {
+ if (numDecls++)
+ result.append(' ');
+ result.append(propertyAt(positionYPropertyIndex).cssText());
+ }
+ }
+
+ // FIXME: We need to do the same for background-repeat.
+ if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && propertyAt(repeatXPropertyIndex).isImportant() == propertyAt(repeatYPropertyIndex).isImportant()) {
+ PropertyReference repeatXProperty = propertyAt(repeatXPropertyIndex);
+ PropertyReference repeatYProperty = propertyAt(repeatYPropertyIndex);
+
+ if (numDecls++)
+ result.append(' ');
+ result.appendLiteral("background-repeat: ");
+ if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList())
+ result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
+ else {
+ result.append(repeatXProperty.value()->cssText());
+ result.append(' ');
+ result.append(repeatYProperty.value()->cssText());
+ }
+ if (repeatXProperty.isImportant())
+ result.appendLiteral(" !important");
+ result.append(';');
+ } else {
+ if (repeatXPropertyIndex != -1) {
+ if (numDecls++)
+ result.append(' ');
+ result.append(propertyAt(repeatXPropertyIndex).cssText());
+ }
+ if (repeatYPropertyIndex != -1) {
+ if (numDecls++)
+ result.append(' ');
+ result.append(propertyAt(repeatYPropertyIndex).cssText());
+ }
+ }
+
+ ASSERT(!numDecls ^ !result.isEmpty());
+ return result.toString();
+}
+
+bool StyleProperties::hasCSSOMWrapper() const
+{
+ return is<MutableStyleProperties>(*this) && downcast<MutableStyleProperties>(*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());
+}
+
+void StyleProperties::addSubresourceStyleURLs(ListHashSet<URL>& urls, StyleSheetContents* contextStyleSheet) const
+{
+ unsigned size = propertyCount();
+ for (unsigned i = 0; i < size; ++i)
+ propertyAt(i).value()->addSubresourceStyleURLs(urls, contextStyleSheet);
+}
+
+bool StyleProperties::traverseSubresources(const std::function<bool (const CachedResource&)>& 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
+ CSSPropertyWebkitAspectRatio,
+ CSSPropertyColumnCount,
+ CSSPropertyColumnGap,
+ CSSPropertyColumnRuleColor,
+ CSSPropertyColumnRuleStyle,
+ CSSPropertyColumnRuleWidth,
+ CSSPropertyWebkitColumnBreakBefore,
+ CSSPropertyWebkitColumnBreakAfter,
+ CSSPropertyWebkitColumnBreakInside,
+ CSSPropertyColumnWidth,
+ CSSPropertyPageBreakAfter,
+ CSSPropertyPageBreakBefore,
+ CSSPropertyPageBreakInside,
+#if ENABLE(CSS_REGIONS)
+ CSSPropertyWebkitRegionBreakAfter,
+ CSSPropertyWebkitRegionBreakBefore,
+ CSSPropertyWebkitRegionBreakInside,
+#endif
+ 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<MutableStyleProperties> 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<CSSPropertyID> toRemove;
+ for (unsigned i = 0; i < length; ++i)
+ toRemove.add(set[i]);
+
+ return m_propertyVector.removeAllMatching([&toRemove] (const CSSProperty& property) {
+ // Not quite sure if the isImportant test is needed but it matches the existing behavior.
+ return !property.isImportant() && 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<uint16_t>(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<uint16_t>(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
+{
+ // Convert the 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.
+ 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.
+ if (!valueArray()[n])
+ continue;
+ if (downcast<CSSCustomPropertyValue>(*valueArray()[n]).name() == propertyName)
+ return n;
+ }
+ }
+
+ return -1;
+}
+
+int MutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const
+{
+ // Convert the 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.
+ 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<CSSCustomPropertyValue>(*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<MutableStyleProperties> StyleProperties::mutableCopy() const
+{
+ return adoptRef(*new MutableStyleProperties(*this));
+}
+
+Ref<MutableStyleProperties> StyleProperties::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
+{
+ Vector<CSSProperty, 256> list;
+ list.reserveInitialCapacity(length);
+ for (unsigned i = 0; i < length; ++i) {
+ RefPtr<CSSValue> value = getPropertyCSSValueInternal(set[i]);
+ if (value)
+ list.append(CSSProperty(set[i], value.release(), false));
+ }
+ return MutableStyleProperties::create(list.data(), list.size());
+}
+
+PropertySetCSSStyleDeclaration* MutableStyleProperties::cssStyleDeclaration()
+{
+ return m_cssomWrapper.get();
+}
+
+CSSStyleDeclaration* MutableStyleProperties::ensureCSSStyleDeclaration()
+{
+ if (m_cssomWrapper) {
+ ASSERT(!static_cast<CSSStyleDeclaration*>(m_cssomWrapper.get())->parentRule());
+ ASSERT(!m_cssomWrapper->parentElement());
+ return m_cssomWrapper.get();
+ }
+ m_cssomWrapper = std::make_unique<PropertySetCSSStyleDeclaration>(this);
+ return m_cssomWrapper.get();
+}
+
+CSSStyleDeclaration* MutableStyleProperties::ensureInlineCSSStyleDeclaration(StyledElement* parentElement)
+{
+ if (m_cssomWrapper) {
+ ASSERT(m_cssomWrapper->parentElement() == parentElement);
+ return m_cssomWrapper.get();
+ }
+ m_cssomWrapper = std::make_unique<InlineCSSStyleDeclaration>(this, parentElement);
+ return m_cssomWrapper.get();
+}
+
+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<SameSizeAsStyleProperties> {
+ 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> MutableStyleProperties::create(CSSParserMode cssParserMode)
+{
+ return adoptRef(*new MutableStyleProperties(cssParserMode));
+}
+
+Ref<MutableStyleProperties> MutableStyleProperties::create(const CSSProperty* properties, unsigned count)
+{
+ return adoptRef(*new MutableStyleProperties(properties, count));
+}
+
+String StyleProperties::PropertyReference::cssName() const
+{
+ if (id() == CSSPropertyCustom)
+ return downcast<CSSCustomPropertyValue>(*value()).name();
+ return getPropertyNameString(id());
+}
+
+String StyleProperties::PropertyReference::cssText() const
+{
+ StringBuilder result;
+ result.append(cssName());
+ result.appendLiteral(": ");
+ result.append(m_value->cssText());
+ if (isImportant())
+ result.appendLiteral(" !important");
+ result.append(';');
+ return result.toString();
+}
+
+
+} // namespace WebCore