/* * Copyright (C) 2004 Zack Rusin * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. * Copyright (C) 2007 Alexey Proskuryakov * Copyright (C) 2007 Nicholas Shanks * Copyright (C) 2011 Sencha, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "sky/engine/core/css/CSSComputedStyleDeclaration.h" #include "gen/sky/core/CSSPropertyNames.h" #include "gen/sky/core/StylePropertyShorthand.h" #include "gen/sky/platform/FontFamilyNames.h" #include "gen/sky/platform/RuntimeEnabledFeatures.h" #include "sky/engine/bindings/exception_state.h" #include "sky/engine/core/css/BasicShapeFunctions.h" #include "sky/engine/core/css/CSSAspectRatioValue.h" #include "sky/engine/core/css/CSSFilterValue.h" #include "sky/engine/core/css/CSSFontFeatureValue.h" #include "sky/engine/core/css/CSSFontValue.h" #include "sky/engine/core/css/CSSFunctionValue.h" #include "sky/engine/core/css/CSSLineBoxContainValue.h" #include "sky/engine/core/css/CSSPrimitiveValue.h" #include "sky/engine/core/css/CSSPrimitiveValueMappings.h" #include "sky/engine/core/css/CSSPropertyMetadata.h" #include "sky/engine/core/css/CSSSelector.h" #include "sky/engine/core/css/CSSShadowValue.h" #include "sky/engine/core/css/CSSTransformValue.h" #include "sky/engine/core/css/CSSValueList.h" #include "sky/engine/core/css/CSSValuePool.h" #include "sky/engine/core/css/Pair.h" #include "sky/engine/core/css/StylePropertySet.h" #include "sky/engine/core/css/parser/BisonCSSParser.h" #include "sky/engine/core/css/resolver/StyleResolver.h" #include "sky/engine/core/dom/Document.h" #include "sky/engine/core/dom/ExceptionCode.h" #include "sky/engine/core/rendering/RenderBox.h" #include "sky/engine/core/rendering/style/RenderStyle.h" #include "sky/engine/core/rendering/style/ShadowList.h" #include "sky/engine/core/rendering/style/ShapeValue.h" #include "sky/engine/platform/fonts/FontFeatureSettings.h" #include "sky/engine/wtf/text/StringBuilder.h" namespace blink { // List of all properties we know how to compute, omitting shorthands. // NOTE: Do not use this list, use computableProperties() instead // to respect runtime enabling of CSS properties. static const CSSPropertyID staticComputableProperties[] = { CSSPropertyBackgroundAttachment, CSSPropertyBackgroundClip, CSSPropertyBackgroundColor, CSSPropertyBackgroundImage, CSSPropertyBackgroundOrigin, CSSPropertyBackgroundPosition, // more-specific background-position-x/y are non-standard CSSPropertyBackgroundRepeat, CSSPropertyBackgroundSize, CSSPropertyBorderBottomColor, CSSPropertyBorderBottomLeftRadius, CSSPropertyBorderBottomRightRadius, CSSPropertyBorderBottomStyle, CSSPropertyBorderBottomWidth, CSSPropertyBorderLeftColor, CSSPropertyBorderLeftStyle, CSSPropertyBorderLeftWidth, CSSPropertyBorderRightColor, CSSPropertyBorderRightStyle, CSSPropertyBorderRightWidth, CSSPropertyBorderTopColor, CSSPropertyBorderTopLeftRadius, CSSPropertyBorderTopRightRadius, CSSPropertyBorderTopStyle, CSSPropertyBorderTopWidth, CSSPropertyBottom, CSSPropertyBoxShadow, CSSPropertyBoxSizing, CSSPropertyColor, CSSPropertyDirection, CSSPropertyDisplay, CSSPropertyFontFamily, CSSPropertyFontKerning, CSSPropertyFontSize, CSSPropertyFontStretch, CSSPropertyFontStyle, CSSPropertyFontVariant, CSSPropertyFontVariantLigatures, CSSPropertyFontWeight, CSSPropertyHeight, CSSPropertyImageRendering, CSSPropertyLeft, CSSPropertyLetterSpacing, CSSPropertyLineHeight, CSSPropertyMarginBottom, CSSPropertyMarginLeft, CSSPropertyMarginRight, CSSPropertyMarginTop, CSSPropertyMaxHeight, CSSPropertyMaxWidth, CSSPropertyMinHeight, CSSPropertyMinWidth, CSSPropertyObjectFit, CSSPropertyObjectPosition, CSSPropertyOpacity, CSSPropertyOutlineColor, CSSPropertyOutlineOffset, CSSPropertyOutlineStyle, CSSPropertyOutlineWidth, CSSPropertyOverflowWrap, CSSPropertyOverflowX, CSSPropertyOverflowY, CSSPropertyPaddingBottom, CSSPropertyPaddingLeft, CSSPropertyPaddingRight, CSSPropertyPaddingTop, CSSPropertyPointerEvents, CSSPropertyPosition, CSSPropertyRight, CSSPropertyTabSize, CSSPropertyTextAlign, CSSPropertyTextAlignLast, CSSPropertyTextDecoration, CSSPropertyTextDecorationLine, CSSPropertyTextDecorationStyle, CSSPropertyTextDecorationColor, CSSPropertyTextJustify, CSSPropertyTextUnderlinePosition, CSSPropertyTextIndent, CSSPropertyTextRendering, CSSPropertyTextShadow, CSSPropertyTextOverflow, CSSPropertyTop, CSSPropertyTouchAction, CSSPropertyTouchActionDelay, CSSPropertyUnicodeBidi, CSSPropertyVerticalAlign, CSSPropertyWhiteSpace, CSSPropertyWidth, CSSPropertyWordBreak, CSSPropertyWordSpacing, CSSPropertyWordWrap, CSSPropertyZIndex, CSSPropertyWebkitBackgroundClip, CSSPropertyWebkitBackgroundComposite, CSSPropertyWebkitBackgroundOrigin, CSSPropertyWebkitBackgroundSize, CSSPropertyWebkitBorderHorizontalSpacing, CSSPropertyWebkitBorderVerticalSpacing, CSSPropertyWebkitBoxDecorationBreak, CSSPropertyWebkitBoxShadow, CSSPropertyWebkitClipPath, CSSPropertyFilter, CSSPropertyAlignContent, CSSPropertyAlignItems, CSSPropertyAlignSelf, CSSPropertyFlexBasis, CSSPropertyFlexGrow, CSSPropertyFlexShrink, CSSPropertyFlexDirection, CSSPropertyFlexWrap, CSSPropertyJustifyContent, CSSPropertyWebkitFontSmoothing, CSSPropertyWebkitHighlight, CSSPropertyWebkitHyphenateCharacter, CSSPropertyWebkitLineBoxContain, CSSPropertyWebkitLineBreak, CSSPropertyWebkitLocale, CSSPropertyOrder, CSSPropertyPerspective, CSSPropertyWebkitPerspective, CSSPropertyPerspectiveOrigin, CSSPropertyWebkitPerspectiveOrigin, CSSPropertyWebkitRtlOrdering, CSSPropertyWebkitTapHighlightColor, CSSPropertyWebkitTextDecorationsInEffect, CSSPropertyWebkitTextEmphasisColor, CSSPropertyWebkitTextEmphasisPosition, CSSPropertyWebkitTextEmphasisStyle, CSSPropertyWebkitTextFillColor, CSSPropertyWebkitTextOrientation, CSSPropertyWebkitTextStrokeColor, CSSPropertyWebkitTextStrokeWidth, CSSPropertyTransform, CSSPropertyWebkitTransform, CSSPropertyTransformOrigin, CSSPropertyWebkitTransformOrigin, CSSPropertyTransformStyle, CSSPropertyWebkitTransformStyle, CSSPropertyWebkitUserModify, CSSPropertyWebkitUserSelect, }; static const Vector& computableProperties() { DEFINE_STATIC_LOCAL(Vector, properties, ()); if (properties.isEmpty()) CSSPropertyMetadata::filterEnabledCSSPropertiesIntoVector(staticComputableProperties, WTF_ARRAY_LENGTH(staticComputableProperties), properties); return properties; } inline static PassRefPtr pixelValue(double value, const RenderStyle&) { return cssValuePool().createValue(value, CSSPrimitiveValue::CSS_PX); } inline static PassRefPtr numberValue(double value, const RenderStyle&) { return cssValuePool().createValue(value, CSSPrimitiveValue::CSS_NUMBER); } static PassRefPtr pixelValueForLength(const Length& length, const RenderStyle& style) { if (length.isFixed()) return pixelValue(length.value(), style); return cssValuePool().createValue(length, style); } static PassRefPtr createPositionListForLayer(CSSPropertyID propertyID, const FillLayer& layer, const RenderStyle& style) { RefPtr positionList = CSSValueList::createSpaceSeparated(); if (layer.isBackgroundXOriginSet()) { ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPosition); positionList->append(cssValuePool().createValue(layer.backgroundXOrigin())); } positionList->append(pixelValueForLength(layer.xPosition(), style)); if (layer.isBackgroundYOriginSet()) { ASSERT(propertyID == CSSPropertyBackgroundPosition); positionList->append(cssValuePool().createValue(layer.backgroundYOrigin())); } positionList->append(pixelValueForLength(layer.yPosition(), style)); return positionList.release(); } static PassRefPtr valueForPositionOffset(RenderStyle& style, CSSPropertyID propertyID, const RenderObject* renderer) { Length l; switch (propertyID) { case CSSPropertyLeft: l = style.left(); break; case CSSPropertyRight: l = style.right(); break; case CSSPropertyTop: l = style.top(); break; case CSSPropertyBottom: l = style.bottom(); break; default: return nullptr; } if (l.isPercent() && renderer && renderer->isBox()) { LayoutUnit containingBlockSize = (propertyID == CSSPropertyLeft || propertyID == CSSPropertyRight) ? toRenderBox(renderer)->containingBlockLogicalWidthForContent() : toRenderBox(renderer)->containingBlockLogicalHeightForContent(ExcludeMarginBorderPadding); return pixelValue(valueForLength(l, containingBlockSize), style); } if (l.isAuto()) { // FIXME: It's not enough to simply return "auto" values for one offset if the other side is defined. // In other words if left is auto and right is not auto, then left's computed value is negative right(). // So we should get the opposite length unit and see if it is auto. return cssValuePool().createIdentifierValue(CSSValueAuto); } return pixelValueForLength(l, style); } PassRefPtr CSSComputedStyleDeclaration::currentColorOrValidColor(const RenderStyle& style, const StyleColor& color) const { // This function does NOT look at visited information, so that computed style doesn't expose that. return cssValuePool().createColorValue(color.resolve(style.color()).rgb()); } static PassRefPtr valuesForBorderRadiusCorner(LengthSize radius, const RenderStyle& style) { RefPtr list = CSSValueList::createSpaceSeparated(); if (radius.width().type() == Percent) list->append(cssValuePool().createValue(radius.width().percent(), CSSPrimitiveValue::CSS_PERCENTAGE)); else list->append(pixelValueForLength(radius.width(), style)); if (radius.height().type() == Percent) list->append(cssValuePool().createValue(radius.height().percent(), CSSPrimitiveValue::CSS_PERCENTAGE)); else list->append(pixelValueForLength(radius.height(), style)); return list.release(); } static PassRefPtr valueForBorderRadiusCorner(LengthSize radius, const RenderStyle& style) { RefPtr list = valuesForBorderRadiusCorner(radius, style); if (list->item(0)->equals(*list->item(1))) return list->item(0); return list.release(); } static PassRefPtr valueForBorderRadiusShorthand(const RenderStyle& style) { RefPtr list = CSSValueList::createSlashSeparated(); bool showHorizontalBottomLeft = style.borderTopRightRadius().width() != style.borderBottomLeftRadius().width(); bool showHorizontalBottomRight = showHorizontalBottomLeft || (style.borderBottomRightRadius().width() != style.borderTopLeftRadius().width()); bool showHorizontalTopRight = showHorizontalBottomRight || (style.borderTopRightRadius().width() != style.borderTopLeftRadius().width()); bool showVerticalBottomLeft = style.borderTopRightRadius().height() != style.borderBottomLeftRadius().height(); bool showVerticalBottomRight = showVerticalBottomLeft || (style.borderBottomRightRadius().height() != style.borderTopLeftRadius().height()); bool showVerticalTopRight = showVerticalBottomRight || (style.borderTopRightRadius().height() != style.borderTopLeftRadius().height()); RefPtr topLeftRadius = valuesForBorderRadiusCorner(style.borderTopLeftRadius(), style); RefPtr topRightRadius = valuesForBorderRadiusCorner(style.borderTopRightRadius(), style); RefPtr bottomRightRadius = valuesForBorderRadiusCorner(style.borderBottomRightRadius(), style); RefPtr bottomLeftRadius = valuesForBorderRadiusCorner(style.borderBottomLeftRadius(), style); RefPtr horizontalRadii = CSSValueList::createSpaceSeparated(); horizontalRadii->append(topLeftRadius->item(0)); if (showHorizontalTopRight) horizontalRadii->append(topRightRadius->item(0)); if (showHorizontalBottomRight) horizontalRadii->append(bottomRightRadius->item(0)); if (showHorizontalBottomLeft) horizontalRadii->append(bottomLeftRadius->item(0)); list->append(horizontalRadii.release()); RefPtr verticalRadii = CSSValueList::createSpaceSeparated(); verticalRadii->append(topLeftRadius->item(1)); if (showVerticalTopRight) verticalRadii->append(topRightRadius->item(1)); if (showVerticalBottomRight) verticalRadii->append(bottomRightRadius->item(1)); if (showVerticalBottomLeft) verticalRadii->append(bottomLeftRadius->item(1)); if (!verticalRadii->equals(*toCSSValueList(list->item(0)))) list->append(verticalRadii.release()); return list.release(); } static LayoutRect sizingBox(RenderObject* renderer) { if (!renderer->isBox()) return LayoutRect(); RenderBox* box = toRenderBox(renderer); return box->style()->boxSizing() == BORDER_BOX ? box->borderBoxRect() : box->computedCSSContentBoxRect(); } static PassRefPtr valueForMatrixTransform(const TransformationMatrix& transform, const RenderStyle& style) { RefPtr transformValue = nullptr; if (transform.isAffine()) { transformValue = CSSTransformValue::create(CSSTransformValue::MatrixTransformOperation); transformValue->append(cssValuePool().createValue(transform.a(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.b(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.c(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.d(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(numberValue(transform.e(), style)); transformValue->append(numberValue(transform.f(), style)); } else { transformValue = CSSTransformValue::create(CSSTransformValue::Matrix3DTransformOperation); transformValue->append(cssValuePool().createValue(transform.m11(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m12(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m13(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m14(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m21(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m22(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m23(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m24(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m31(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m32(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m33(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(cssValuePool().createValue(transform.m34(), CSSPrimitiveValue::CSS_NUMBER)); transformValue->append(numberValue(transform.m41(), style)); transformValue->append(numberValue(transform.m42(), style)); transformValue->append(numberValue(transform.m43(), style)); transformValue->append(cssValuePool().createValue(transform.m44(), CSSPrimitiveValue::CSS_NUMBER)); } return transformValue.release(); } static PassRefPtr computedTransform(RenderObject* renderer, const RenderStyle& style) { if (!renderer || !renderer->hasTransform() || !style.hasTransform()) return cssValuePool().createIdentifierValue(CSSValueNone); IntRect box; if (renderer->isBox()) box = pixelSnappedIntRect(toRenderBox(renderer)->borderBoxRect()); TransformationMatrix transform; style.applyTransform(transform, box.size(), RenderStyle::ExcludeTransformOrigin); // FIXME: Need to print out individual functions (https://bugs.webkit.org/show_bug.cgi?id=23924) RefPtr list = CSSValueList::createSpaceSeparated(); list->append(valueForMatrixTransform(transform, style)); return list.release(); } PassRefPtr CSSComputedStyleDeclaration::valueForFilter(const RenderObject* renderer, const RenderStyle& style) const { if (style.filter().operations().isEmpty()) return cssValuePool().createIdentifierValue(CSSValueNone); RefPtr list = CSSValueList::createSpaceSeparated(); RefPtr filterValue = nullptr; Vector >::const_iterator end = style.filter().operations().end(); for (Vector >::const_iterator it = style.filter().operations().begin(); it != end; ++it) { FilterOperation* filterOperation = it->get(); switch (filterOperation->type()) { case FilterOperation::GRAYSCALE: filterValue = CSSFilterValue::create(CSSFilterValue::GrayscaleFilterOperation); filterValue->append(cssValuePool().createValue(toBasicColorMatrixFilterOperation(filterOperation)->amount(), CSSPrimitiveValue::CSS_NUMBER)); break; case FilterOperation::SEPIA: filterValue = CSSFilterValue::create(CSSFilterValue::SepiaFilterOperation); filterValue->append(cssValuePool().createValue(toBasicColorMatrixFilterOperation(filterOperation)->amount(), CSSPrimitiveValue::CSS_NUMBER)); break; case FilterOperation::SATURATE: filterValue = CSSFilterValue::create(CSSFilterValue::SaturateFilterOperation); filterValue->append(cssValuePool().createValue(toBasicColorMatrixFilterOperation(filterOperation)->amount(), CSSPrimitiveValue::CSS_NUMBER)); break; case FilterOperation::HUE_ROTATE: filterValue = CSSFilterValue::create(CSSFilterValue::HueRotateFilterOperation); filterValue->append(cssValuePool().createValue(toBasicColorMatrixFilterOperation(filterOperation)->amount(), CSSPrimitiveValue::CSS_DEG)); break; case FilterOperation::INVERT: filterValue = CSSFilterValue::create(CSSFilterValue::InvertFilterOperation); filterValue->append(cssValuePool().createValue(toBasicComponentTransferFilterOperation(filterOperation)->amount(), CSSPrimitiveValue::CSS_NUMBER)); break; case FilterOperation::OPACITY: filterValue = CSSFilterValue::create(CSSFilterValue::OpacityFilterOperation); filterValue->append(cssValuePool().createValue(toBasicComponentTransferFilterOperation(filterOperation)->amount(), CSSPrimitiveValue::CSS_NUMBER)); break; case FilterOperation::BRIGHTNESS: filterValue = CSSFilterValue::create(CSSFilterValue::BrightnessFilterOperation); filterValue->append(cssValuePool().createValue(toBasicComponentTransferFilterOperation(filterOperation)->amount(), CSSPrimitiveValue::CSS_NUMBER)); break; case FilterOperation::CONTRAST: filterValue = CSSFilterValue::create(CSSFilterValue::ContrastFilterOperation); filterValue->append(cssValuePool().createValue(toBasicComponentTransferFilterOperation(filterOperation)->amount(), CSSPrimitiveValue::CSS_NUMBER)); break; case FilterOperation::BLUR: filterValue = CSSFilterValue::create(CSSFilterValue::BlurFilterOperation); filterValue->append(pixelValue(toBlurFilterOperation(filterOperation)->stdDeviation().value(), style)); break; case FilterOperation::DROP_SHADOW: { DropShadowFilterOperation* dropShadowOperation = toDropShadowFilterOperation(filterOperation); filterValue = CSSFilterValue::create(CSSFilterValue::DropShadowFilterOperation); // We want our computed style to look like that of a text shadow (has neither spread nor inset style). ShadowData shadow(dropShadowOperation->location(), dropShadowOperation->stdDeviation(), 0, Normal, dropShadowOperation->color()); filterValue->append(valueForShadowData(shadow, style, false)); break; } default: filterValue = CSSFilterValue::create(CSSFilterValue::UnknownFilterOperation); break; } list->append(filterValue.release()); } return list.release(); } static PassRefPtr createLineBoxContainValue(unsigned lineBoxContain) { if (!lineBoxContain) return cssValuePool().createIdentifierValue(CSSValueNone); return CSSLineBoxContainValue::create(lineBoxContain); } CSSComputedStyleDeclaration::CSSComputedStyleDeclaration(PassRefPtr n, bool allowVisitedStyle) : m_node(n) , m_allowVisitedStyle(allowVisitedStyle) #if !ENABLE(OILPAN) , m_refCount(1) #endif { } CSSComputedStyleDeclaration::~CSSComputedStyleDeclaration() { } #if !ENABLE(OILPAN) void CSSComputedStyleDeclaration::ref() { ++m_refCount; } void CSSComputedStyleDeclaration::deref() { ASSERT(m_refCount); if (!--m_refCount) delete this; } #endif String CSSComputedStyleDeclaration::cssText() const { StringBuilder result; const Vector& properties = computableProperties(); for (unsigned i = 0; i < properties.size(); i++) { if (i) result.append(' '); result.append(getPropertyName(properties[i])); result.appendLiteral(": "); result.append(getPropertyValue(properties[i])); result.append(';'); } return result.toString(); } void CSSComputedStyleDeclaration::setCSSText(const String&, ExceptionState& exceptionState) { exceptionState.ThrowDOMException(NoModificationAllowedError, "These styles are computed, and therefore read-only."); } static CSSValueID cssIdentifierForFontSizeKeyword(int keywordSize) { ASSERT_ARG(keywordSize, keywordSize); ASSERT_ARG(keywordSize, keywordSize <= 8); return static_cast(CSSValueXxSmall + keywordSize - 1); } PassRefPtr CSSComputedStyleDeclaration::getFontSizeCSSValuePreferringKeyword() const { if (!m_node) return nullptr; m_node->document().updateLayout(); RefPtr style = m_node->computedStyle(); if (!style) return nullptr; if (int keywordSize = style->fontDescription().keywordSize()) return cssValuePool().createIdentifierValue(cssIdentifierForFontSizeKeyword(keywordSize)); return pixelValue(style->fontDescription().computedPixelSize(), *style); } FixedPitchFontType CSSComputedStyleDeclaration::fixedPitchFontType() const { if (!m_node) return NonFixedPitchFont; RefPtr style = m_node->computedStyle(); if (!style) return NonFixedPitchFont; return style->fontDescription().fixedPitchFontType(); } PassRefPtr CSSComputedStyleDeclaration::valueForShadowData(const ShadowData& shadow, const RenderStyle& style, bool useSpread) const { RefPtr x = pixelValue(shadow.x(), style); RefPtr y = pixelValue(shadow.y(), style); RefPtr blur = pixelValue(shadow.blur(), style); RefPtr spread = useSpread ? pixelValue(shadow.spread(), style) : PassRefPtr(nullptr); RefPtr shadowStyle = shadow.style() == Normal ? PassRefPtr(nullptr) : cssValuePool().createIdentifierValue(CSSValueInset); RefPtr color = currentColorOrValidColor(style, shadow.color()); return CSSShadowValue::create(x.release(), y.release(), blur.release(), spread.release(), shadowStyle.release(), color.release()); } PassRefPtr CSSComputedStyleDeclaration::valueForShadowList(const ShadowList* shadowList, const RenderStyle& style, bool useSpread) const { if (!shadowList) return cssValuePool().createIdentifierValue(CSSValueNone); RefPtr list = CSSValueList::createCommaSeparated(); size_t shadowCount = shadowList->shadows().size(); for (size_t i = 0; i < shadowCount; ++i) list->append(valueForShadowData(shadowList->shadows()[i], style, useSpread)); return list.release(); } static CSSValueID identifierForFamily(const AtomicString& family) { if (family == FontFamilyNames::webkit_cursive) return CSSValueCursive; if (family == FontFamilyNames::webkit_fantasy) return CSSValueFantasy; if (family == FontFamilyNames::webkit_monospace) return CSSValueMonospace; if (family == FontFamilyNames::webkit_pictograph) return CSSValueWebkitPictograph; if (family == FontFamilyNames::webkit_sans_serif) return CSSValueSansSerif; if (family == FontFamilyNames::webkit_serif) return CSSValueSerif; return CSSValueInvalid; } static PassRefPtr valueForFamily(const AtomicString& family) { if (CSSValueID familyIdentifier = identifierForFamily(family)) return cssValuePool().createIdentifierValue(familyIdentifier); return cssValuePool().createValue(family.string(), CSSPrimitiveValue::CSS_STRING); } static PassRefPtr renderTextDecorationFlagsToCSSValue(int textDecoration) { // Blink value is ignored. RefPtr list = CSSValueList::createSpaceSeparated(); if (textDecoration & TextDecorationUnderline) list->append(cssValuePool().createIdentifierValue(CSSValueUnderline)); if (textDecoration & TextDecorationOverline) list->append(cssValuePool().createIdentifierValue(CSSValueOverline)); if (textDecoration & TextDecorationLineThrough) list->append(cssValuePool().createIdentifierValue(CSSValueLineThrough)); if (!list->length()) return cssValuePool().createIdentifierValue(CSSValueNone); return list.release(); } static PassRefPtr valueForTextDecorationStyle(TextDecorationStyle textDecorationStyle) { switch (textDecorationStyle) { case TextDecorationStyleSolid: return cssValuePool().createIdentifierValue(CSSValueSolid); case TextDecorationStyleDouble: return cssValuePool().createIdentifierValue(CSSValueDouble); case TextDecorationStyleDotted: return cssValuePool().createIdentifierValue(CSSValueDotted); case TextDecorationStyleDashed: return cssValuePool().createIdentifierValue(CSSValueDashed); case TextDecorationStyleWavy: return cssValuePool().createIdentifierValue(CSSValueWavy); } ASSERT_NOT_REACHED(); return cssValuePool().createExplicitInitialValue(); } static PassRefPtr valueForFillRepeat(EFillRepeat xRepeat, EFillRepeat yRepeat) { // For backwards compatibility, if both values are equal, just return one of them. And // if the two values are equivalent to repeat-x or repeat-y, just return the shorthand. if (xRepeat == yRepeat) return cssValuePool().createValue(xRepeat); if (xRepeat == RepeatFill && yRepeat == NoRepeatFill) return cssValuePool().createIdentifierValue(CSSValueRepeatX); if (xRepeat == NoRepeatFill && yRepeat == RepeatFill) return cssValuePool().createIdentifierValue(CSSValueRepeatY); RefPtr list = CSSValueList::createSpaceSeparated(); list->append(cssValuePool().createValue(xRepeat)); list->append(cssValuePool().createValue(yRepeat)); return list.release(); } static PassRefPtr valueForFillSize(const FillSize& fillSize, const RenderStyle& style) { if (fillSize.type == Contain) return cssValuePool().createIdentifierValue(CSSValueContain); if (fillSize.type == Cover) return cssValuePool().createIdentifierValue(CSSValueCover); if (fillSize.size.height().isAuto()) return pixelValueForLength(fillSize.size.width(), style); RefPtr list = CSSValueList::createSpaceSeparated(); list->append(pixelValueForLength(fillSize.size.width(), style)); list->append(pixelValueForLength(fillSize.size.height(), style)); return list.release(); } static void logUnimplementedPropertyID(CSSPropertyID propertyID) { DEFINE_STATIC_LOCAL(HashSet, propertyIDSet, ()); if (!propertyIDSet.add(propertyID).isNewEntry) return; WTF_LOG_ERROR("WebKit does not yet implement getComputedStyle for '%s'.", getPropertyName(propertyID)); } static PassRefPtr valueForFontFamily(RenderStyle& style) { const FontFamily& firstFamily = style.fontDescription().family(); RefPtr list = CSSValueList::createCommaSeparated(); for (const FontFamily* family = &firstFamily; family; family = family->next()) list->append(valueForFamily(family->family())); return list.release(); } static PassRefPtr valueForLineHeight(RenderStyle& style) { Length length = style.lineHeight(); if (length.isNegative()) return cssValuePool().createIdentifierValue(CSSValueNormal); return pixelValue(floatValueForLength(length, style.fontDescription().specifiedSize()), style); } static PassRefPtr valueForFontSize(RenderStyle& style) { return pixelValue(style.fontDescription().computedPixelSize(), style); } static PassRefPtr valueForFontStretch(RenderStyle& style) { return cssValuePool().createValue(style.fontDescription().stretch()); } static PassRefPtr valueForFontStyle(RenderStyle& style) { return cssValuePool().createValue(style.fontDescription().style()); } static PassRefPtr valueForFontVariant(RenderStyle& style) { return cssValuePool().createValue(style.fontDescription().variant()); } static PassRefPtr valueForFontWeight(RenderStyle& style) { return cssValuePool().createValue(style.fontDescription().weight()); } static PassRefPtr touchActionFlagsToCSSValue(TouchAction touchAction) { RefPtr list = CSSValueList::createSpaceSeparated(); if (touchAction == TouchActionAuto) list->append(cssValuePool().createIdentifierValue(CSSValueAuto)); if (touchAction & TouchActionNone) { ASSERT(touchAction == TouchActionNone); list->append(cssValuePool().createIdentifierValue(CSSValueNone)); } if (touchAction == (TouchActionPanX | TouchActionPanY | TouchActionPinchZoom)) { list->append(cssValuePool().createIdentifierValue(CSSValueManipulation)); } else { if (touchAction & TouchActionPanX) list->append(cssValuePool().createIdentifierValue(CSSValuePanX)); if (touchAction & TouchActionPanY) list->append(cssValuePool().createIdentifierValue(CSSValuePanY)); } ASSERT(list->length()); return list.release(); } static bool isLayoutDependent(CSSPropertyID propertyID, PassRefPtr style, RenderObject* renderer) { // Some properties only depend on layout in certain conditions which // are specified in the main switch statement below. So we can avoid // forcing layout in those conditions. The conditions in this switch // statement must remain in sync with the conditions in the main switch. // FIXME: Some of these cases could be narrowed down or optimized better. switch (propertyID) { case CSSPropertyBottom: case CSSPropertyHeight: case CSSPropertyLeft: case CSSPropertyRight: case CSSPropertyTop: case CSSPropertyPerspectiveOrigin: case CSSPropertyWebkitPerspectiveOrigin: case CSSPropertyTransform: case CSSPropertyWebkitTransform: case CSSPropertyTransformOrigin: case CSSPropertyWebkitTransformOrigin: case CSSPropertyWidth: case CSSPropertyFilter: return true; case CSSPropertyMargin: return renderer && renderer->isBox() && (!style || !style->marginBottom().isFixed() || !style->marginTop().isFixed() || !style->marginLeft().isFixed() || !style->marginRight().isFixed()); case CSSPropertyMarginLeft: return renderer && renderer->isBox() && (!style || !style->marginLeft().isFixed()); case CSSPropertyMarginRight: return renderer && renderer->isBox() && (!style || !style->marginRight().isFixed()); case CSSPropertyMarginTop: return renderer && renderer->isBox() && (!style || !style->marginTop().isFixed()); case CSSPropertyMarginBottom: return renderer && renderer->isBox() && (!style || !style->marginBottom().isFixed()); case CSSPropertyPadding: return renderer && renderer->isBox() && (!style || !style->paddingBottom().isFixed() || !style->paddingTop().isFixed() || !style->paddingLeft().isFixed() || !style->paddingRight().isFixed()); case CSSPropertyPaddingBottom: return renderer && renderer->isBox() && (!style || !style->paddingBottom().isFixed()); case CSSPropertyPaddingLeft: return renderer && renderer->isBox() && (!style || !style->paddingLeft().isFixed()); case CSSPropertyPaddingRight: return renderer && renderer->isBox() && (!style || !style->paddingRight().isFixed()); case CSSPropertyPaddingTop: return renderer && renderer->isBox() && (!style || !style->paddingTop().isFixed()); default: return false; } } PassRefPtr CSSComputedStyleDeclaration::computeRenderStyle(CSSPropertyID propertyID) const { return m_node->computedStyle(); } static ItemPosition resolveAlignmentAuto(ItemPosition position, Node* element) { if (position != ItemPositionAuto) return position; bool isFlex = element && element->computedStyle() && element->computedStyle()->isDisplayFlexibleBox(); return isFlex ? ItemPositionStretch : ItemPositionStart; } static PassRefPtr valueForItemPositionWithOverflowAlignment(ItemPosition itemPosition, OverflowAlignment overflowAlignment, ItemPositionType positionType) { RefPtr result = CSSValueList::createSpaceSeparated(); if (positionType == LegacyPosition) result->append(CSSPrimitiveValue::createIdentifier(CSSValueLegacy)); result->append(CSSPrimitiveValue::create(itemPosition)); if (itemPosition >= ItemPositionCenter && overflowAlignment != OverflowAlignmentDefault) result->append(CSSPrimitiveValue::create(overflowAlignment)); ASSERT(result->length() <= 2); return result.release(); } PassRefPtr CSSComputedStyleDeclaration::getPropertyCSSValue(CSSPropertyID propertyID, EUpdateLayout updateLayout) const { if (!m_node) return nullptr; RenderObject* renderer = m_node->renderer(); RefPtr style; if (updateLayout) { Document& document = m_node->document(); document.updateRenderTreeForNodeIfNeeded(m_node.get()); renderer = m_node->renderer(); style = computeRenderStyle(propertyID); bool forceFullLayout = isLayoutDependent(propertyID, style, renderer); if (forceFullLayout) { document.updateLayout(); style = computeRenderStyle(propertyID); renderer = m_node->renderer(); } } else { style = computeRenderStyle(propertyID); } if (!style) return nullptr; propertyID = CSSProperty::resolveDirectionAwareProperty(propertyID, style->direction()); switch (propertyID) { case CSSPropertyInvalid: break; case CSSPropertyBackgroundColor: return m_allowVisitedStyle ? cssValuePool().createColorValue(style->colorIncludingFallback(CSSPropertyBackgroundColor).rgb()) : currentColorOrValidColor(*style, style->backgroundColor()); case CSSPropertyBackgroundImage: { RefPtr list = CSSValueList::createCommaSeparated(); for (const FillLayer* currLayer = &style->backgroundLayers(); currLayer; currLayer = currLayer->next()) { if (currLayer->image()) list->append(currLayer->image()->cssValue()); else list->append(cssValuePool().createIdentifierValue(CSSValueNone)); } return list.release(); } case CSSPropertyBackgroundSize: case CSSPropertyWebkitBackgroundSize: { RefPtr list = CSSValueList::createCommaSeparated(); for (const FillLayer* currLayer = &style->backgroundLayers(); currLayer; currLayer = currLayer->next()) list->append(valueForFillSize(currLayer->size(), *style)); return list.release(); } case CSSPropertyBackgroundRepeat: { RefPtr list = CSSValueList::createCommaSeparated(); for (const FillLayer* currLayer = &style->backgroundLayers(); currLayer; currLayer = currLayer->next()) list->append(valueForFillRepeat(currLayer->repeatX(), currLayer->repeatY())); return list.release(); } case CSSPropertyWebkitBackgroundComposite: { RefPtr list = CSSValueList::createCommaSeparated(); for (const FillLayer* currLayer = &style->backgroundLayers(); currLayer; currLayer = currLayer->next()) list->append(cssValuePool().createValue(currLayer->composite())); return list.release(); } case CSSPropertyBackgroundAttachment: { RefPtr list = CSSValueList::createCommaSeparated(); for (const FillLayer* currLayer = &style->backgroundLayers(); currLayer; currLayer = currLayer->next()) list->append(cssValuePool().createValue(currLayer->attachment())); return list.release(); } case CSSPropertyBackgroundClip: case CSSPropertyBackgroundOrigin: case CSSPropertyWebkitBackgroundClip: case CSSPropertyWebkitBackgroundOrigin: { bool isClip = propertyID == CSSPropertyBackgroundClip || propertyID == CSSPropertyWebkitBackgroundClip; RefPtr list = CSSValueList::createCommaSeparated(); for (const FillLayer* currLayer = &style->backgroundLayers(); currLayer; currLayer = currLayer->next()) { EFillBox box = isClip ? currLayer->clip() : currLayer->origin(); list->append(cssValuePool().createValue(box)); } return list.release(); } case CSSPropertyBackgroundPosition: { RefPtr list = CSSValueList::createCommaSeparated(); for (const FillLayer* currLayer = &style->backgroundLayers(); currLayer; currLayer = currLayer->next()) list->append(createPositionListForLayer(propertyID, *currLayer, *style)); return list.release(); } case CSSPropertyBackgroundPositionX: { RefPtr list = CSSValueList::createCommaSeparated(); for (const FillLayer* currLayer = &style->backgroundLayers(); currLayer; currLayer = currLayer->next()) list->append(pixelValueForLength(currLayer->xPosition(), *style)); return list.release(); } case CSSPropertyBackgroundPositionY: { RefPtr list = CSSValueList::createCommaSeparated(); for (const FillLayer* currLayer = &style->backgroundLayers(); currLayer; currLayer = currLayer->next()) list->append(pixelValueForLength(currLayer->yPosition(), *style)); return list.release(); } case CSSPropertyBorderSpacing: { RefPtr list = CSSValueList::createSpaceSeparated(); list->append(pixelValue(style->horizontalBorderSpacing(), *style)); list->append(pixelValue(style->verticalBorderSpacing(), *style)); return list.release(); } case CSSPropertyWebkitBorderHorizontalSpacing: return pixelValue(style->horizontalBorderSpacing(), *style); case CSSPropertyWebkitBorderVerticalSpacing: return pixelValue(style->verticalBorderSpacing(), *style); case CSSPropertyBorderTopColor: return m_allowVisitedStyle ? cssValuePool().createColorValue(style->colorIncludingFallback(CSSPropertyBorderTopColor).rgb()) : currentColorOrValidColor(*style, style->borderTopColor()); case CSSPropertyBorderRightColor: return m_allowVisitedStyle ? cssValuePool().createColorValue(style->colorIncludingFallback(CSSPropertyBorderRightColor).rgb()) : currentColorOrValidColor(*style, style->borderRightColor()); case CSSPropertyBorderBottomColor: return m_allowVisitedStyle ? cssValuePool().createColorValue(style->colorIncludingFallback(CSSPropertyBorderBottomColor).rgb()) : currentColorOrValidColor(*style, style->borderBottomColor()); case CSSPropertyBorderLeftColor: return m_allowVisitedStyle ? cssValuePool().createColorValue(style->colorIncludingFallback(CSSPropertyBorderLeftColor).rgb()) : currentColorOrValidColor(*style, style->borderLeftColor()); case CSSPropertyBorderTopStyle: return cssValuePool().createValue(style->borderTopStyle()); case CSSPropertyBorderRightStyle: return cssValuePool().createValue(style->borderRightStyle()); case CSSPropertyBorderBottomStyle: return cssValuePool().createValue(style->borderBottomStyle()); case CSSPropertyBorderLeftStyle: return cssValuePool().createValue(style->borderLeftStyle()); case CSSPropertyBorderTopWidth: return pixelValue(style->borderTopWidth(), *style); case CSSPropertyBorderRightWidth: return pixelValue(style->borderRightWidth(), *style); case CSSPropertyBorderBottomWidth: return pixelValue(style->borderBottomWidth(), *style); case CSSPropertyBorderLeftWidth: return pixelValue(style->borderLeftWidth(), *style); case CSSPropertyBottom: return valueForPositionOffset(*style, CSSPropertyBottom, renderer); case CSSPropertyWebkitBoxDecorationBreak: if (style->boxDecorationBreak() == DSLICE) return cssValuePool().createIdentifierValue(CSSValueSlice); return cssValuePool().createIdentifierValue(CSSValueClone); case CSSPropertyBoxShadow: case CSSPropertyWebkitBoxShadow: return valueForShadowList(style->boxShadow(), *style, true); case CSSPropertyColor: return cssValuePool().createColorValue(m_allowVisitedStyle ? style->colorIncludingFallback(CSSPropertyColor).rgb() : style->color().rgb()); case CSSPropertyTabSize: return cssValuePool().createValue(style->tabSize(), CSSPrimitiveValue::CSS_NUMBER); case CSSPropertyDirection: return cssValuePool().createValue(style->direction()); case CSSPropertyDisplay: return cssValuePool().createValue(style->display()); case CSSPropertyAlignContent: return cssValuePool().createValue(style->alignContent()); case CSSPropertyAlignItems: return valueForItemPositionWithOverflowAlignment(resolveAlignmentAuto(style->alignItems(), m_node.get()), style->alignItemsOverflowAlignment(), NonLegacyPosition); case CSSPropertyAlignSelf: return valueForItemPositionWithOverflowAlignment(resolveAlignmentAuto(style->alignSelf(), m_node->parentNode()), style->alignSelfOverflowAlignment(), NonLegacyPosition); case CSSPropertyFlex: return valuesForShorthandProperty(flexShorthand()); case CSSPropertyFlexBasis: return pixelValueForLength(style->flexBasis(), *style); case CSSPropertyFlexDirection: return cssValuePool().createValue(style->flexDirection()); case CSSPropertyFlexFlow: return valuesForShorthandProperty(flexFlowShorthand()); case CSSPropertyFlexGrow: return cssValuePool().createValue(style->flexGrow()); case CSSPropertyFlexShrink: return cssValuePool().createValue(style->flexShrink()); case CSSPropertyFlexWrap: return cssValuePool().createValue(style->flexWrap()); case CSSPropertyJustifyContent: return cssValuePool().createValue(style->justifyContent()); case CSSPropertyOrder: return cssValuePool().createValue(style->order(), CSSPrimitiveValue::CSS_NUMBER); case CSSPropertyFont: { RefPtr computedFont = CSSFontValue::create(); computedFont->style = valueForFontStyle(*style); computedFont->variant = valueForFontVariant(*style); computedFont->weight = valueForFontWeight(*style); computedFont->stretch = valueForFontStretch(*style); computedFont->size = valueForFontSize(*style); computedFont->lineHeight = valueForLineHeight(*style); computedFont->family = valueForFontFamily(*style); return computedFont.release(); } case CSSPropertyFontFamily: { RefPtr fontFamilyList = valueForFontFamily(*style); // If there's only a single family, return that as a CSSPrimitiveValue. // NOTE: Gecko always returns this as a comma-separated CSSPrimitiveValue string. if (fontFamilyList->length() == 1) return fontFamilyList->item(0); return fontFamilyList.release(); } case CSSPropertyFontSize: return valueForFontSize(*style); case CSSPropertyFontStretch: return valueForFontStretch(*style); case CSSPropertyFontStyle: return valueForFontStyle(*style); case CSSPropertyFontVariant: return valueForFontVariant(*style); case CSSPropertyFontWeight: return valueForFontWeight(*style); case CSSPropertyWebkitFontFeatureSettings: { const FontFeatureSettings* featureSettings = style->fontDescription().featureSettings(); if (!featureSettings || !featureSettings->size()) return cssValuePool().createIdentifierValue(CSSValueNormal); RefPtr list = CSSValueList::createCommaSeparated(); for (unsigned i = 0; i < featureSettings->size(); ++i) { const FontFeature& feature = featureSettings->at(i); RefPtr featureValue = CSSFontFeatureValue::create(feature.tag(), feature.value()); list->append(featureValue.release()); } return list.release(); } case CSSPropertyHeight: if (renderer) { // According to http://www.w3.org/TR/CSS2/visudet.html#the-height-property, // the "height" property does not apply for non-replaced inline elements. if (!renderer->isReplaced() && renderer->isInline()) return cssValuePool().createIdentifierValue(CSSValueAuto); return pixelValue(sizingBox(renderer).height(), *style); } return pixelValueForLength(style->height(), *style); case CSSPropertyWebkitHighlight: if (style->highlight() == nullAtom) return cssValuePool().createIdentifierValue(CSSValueNone); return cssValuePool().createValue(style->highlight(), CSSPrimitiveValue::CSS_STRING); case CSSPropertyWebkitHyphenateCharacter: if (style->hyphenationString().isNull()) return cssValuePool().createIdentifierValue(CSSValueAuto); return cssValuePool().createValue(style->hyphenationString(), CSSPrimitiveValue::CSS_STRING); case CSSPropertyImageRendering: return CSSPrimitiveValue::create(style->imageRendering()); case CSSPropertyLeft: return valueForPositionOffset(*style, CSSPropertyLeft, renderer); case CSSPropertyLetterSpacing: if (!style->letterSpacing()) return cssValuePool().createIdentifierValue(CSSValueNormal); return pixelValue(style->letterSpacing(), *style); case CSSPropertyLineHeight: return valueForLineHeight(*style); case CSSPropertyWebkitLocale: if (style->locale().isNull()) return cssValuePool().createIdentifierValue(CSSValueAuto); return cssValuePool().createValue(style->locale(), CSSPrimitiveValue::CSS_STRING); case CSSPropertyMarginTop: { Length marginTop = style->marginTop(); if (marginTop.isFixed() || !renderer || !renderer->isBox()) return pixelValueForLength(marginTop, *style); return pixelValue(toRenderBox(renderer)->marginTop(), *style); } case CSSPropertyMarginRight: { Length marginRight = style->marginRight(); if (marginRight.isFixed() || !renderer || !renderer->isBox()) return pixelValueForLength(marginRight, *style); float value; if (marginRight.isPercent()) { // RenderBox gives a marginRight() that is the distance between the right-edge of the child box // and the right-edge of the containing box, when display == BLOCK. Let's calculate the absolute // value of the specified margin-right % instead of relying on RenderBox's marginRight() value. value = minimumValueForLength(marginRight, toRenderBox(renderer)->containingBlockLogicalWidthForContent()).toFloat(); } else { value = toRenderBox(renderer)->marginRight().toFloat(); } return pixelValue(value, *style); } case CSSPropertyMarginBottom: { Length marginBottom = style->marginBottom(); if (marginBottom.isFixed() || !renderer || !renderer->isBox()) return pixelValueForLength(marginBottom, *style); return pixelValue(toRenderBox(renderer)->marginBottom(), *style); } case CSSPropertyMarginLeft: { Length marginLeft = style->marginLeft(); if (marginLeft.isFixed() || !renderer || !renderer->isBox()) return pixelValueForLength(marginLeft, *style); return pixelValue(toRenderBox(renderer)->marginLeft(), *style); } case CSSPropertyWebkitUserModify: return cssValuePool().createValue(style->userModify()); case CSSPropertyMaxHeight: { const Length& maxHeight = style->maxHeight(); if (maxHeight.isMaxSizeNone()) return cssValuePool().createIdentifierValue(CSSValueNone); return pixelValueForLength(maxHeight, *style); } case CSSPropertyMaxWidth: { const Length& maxWidth = style->maxWidth(); if (maxWidth.isMaxSizeNone()) return cssValuePool().createIdentifierValue(CSSValueNone); return pixelValueForLength(maxWidth, *style); } case CSSPropertyMinHeight: // FIXME: For flex-items, min-height:auto should compute to min-content. if (style->minHeight().isAuto()) return pixelValue(0, *style); return pixelValueForLength(style->minHeight(), *style); case CSSPropertyMinWidth: // FIXME: For flex-items, min-width:auto should compute to min-content. if (style->minWidth().isAuto()) return pixelValue(0, *style); return pixelValueForLength(style->minWidth(), *style); case CSSPropertyObjectFit: return cssValuePool().createValue(style->objectFit()); case CSSPropertyObjectPosition: return cssValuePool().createValue( Pair::create( pixelValueForLength(style->objectPosition().x(), *style), pixelValueForLength(style->objectPosition().y(), *style), Pair::KeepIdenticalValues)); case CSSPropertyOpacity: return cssValuePool().createValue(style->opacity(), CSSPrimitiveValue::CSS_NUMBER); case CSSPropertyOutlineColor: return m_allowVisitedStyle ? cssValuePool().createColorValue(style->colorIncludingFallback(CSSPropertyOutlineColor).rgb()) : currentColorOrValidColor(*style, style->outlineColor()); case CSSPropertyOutlineOffset: return pixelValue(style->outlineOffset(), *style); case CSSPropertyOutlineStyle: if (style->outlineStyleIsAuto()) return cssValuePool().createIdentifierValue(CSSValueAuto); return cssValuePool().createValue(style->outlineStyle()); case CSSPropertyOutlineWidth: return pixelValue(style->outlineWidth(), *style); case CSSPropertyOverflow: return cssValuePool().createValue(max(style->overflowX(), style->overflowY())); case CSSPropertyOverflowWrap: return cssValuePool().createValue(style->overflowWrap()); case CSSPropertyOverflowX: return cssValuePool().createValue(style->overflowX()); case CSSPropertyOverflowY: return cssValuePool().createValue(style->overflowY()); case CSSPropertyPaddingTop: { Length paddingTop = style->paddingTop(); if (paddingTop.isFixed() || !renderer || !renderer->isBox()) return pixelValueForLength(paddingTop, *style); return pixelValue(toRenderBox(renderer)->computedCSSPaddingTop(), *style); } case CSSPropertyPaddingRight: { Length paddingRight = style->paddingRight(); if (paddingRight.isFixed() || !renderer || !renderer->isBox()) return pixelValueForLength(paddingRight, *style); return pixelValue(toRenderBox(renderer)->computedCSSPaddingRight(), *style); } case CSSPropertyPaddingBottom: { Length paddingBottom = style->paddingBottom(); if (paddingBottom.isFixed() || !renderer || !renderer->isBox()) return pixelValueForLength(paddingBottom, *style); return pixelValue(toRenderBox(renderer)->computedCSSPaddingBottom(), *style); } case CSSPropertyPaddingLeft: { Length paddingLeft = style->paddingLeft(); if (paddingLeft.isFixed() || !renderer || !renderer->isBox()) return pixelValueForLength(paddingLeft, *style); return pixelValue(toRenderBox(renderer)->computedCSSPaddingLeft(), *style); } case CSSPropertyPosition: return cssValuePool().createValue(style->position()); case CSSPropertyRight: return valueForPositionOffset(*style, CSSPropertyRight, renderer); case CSSPropertyTextAlign: return cssValuePool().createValue(style->textAlign()); case CSSPropertyTextAlignLast: return cssValuePool().createValue(style->textAlignLast()); case CSSPropertyTextDecoration: if (RuntimeEnabledFeatures::css3TextDecorationsEnabled()) return valuesForShorthandProperty(textDecorationShorthand()); // Fall through. case CSSPropertyTextDecorationLine: return renderTextDecorationFlagsToCSSValue(style->textDecoration()); case CSSPropertyTextDecorationStyle: return valueForTextDecorationStyle(style->textDecorationStyle()); case CSSPropertyTextDecorationColor: return currentColorOrValidColor(*style, style->textDecorationColor()); case CSSPropertyTextJustify: return cssValuePool().createValue(style->textJustify()); case CSSPropertyTextUnderlinePosition: return cssValuePool().createValue(style->textUnderlinePosition()); case CSSPropertyWebkitTextDecorationsInEffect: return renderTextDecorationFlagsToCSSValue(style->textDecorationsInEffect()); case CSSPropertyWebkitTextFillColor: return currentColorOrValidColor(*style, style->textFillColor()); case CSSPropertyWebkitTextEmphasisColor: return currentColorOrValidColor(*style, style->textEmphasisColor()); case CSSPropertyWebkitTextEmphasisPosition: return cssValuePool().createValue(style->textEmphasisPosition()); case CSSPropertyWebkitTextEmphasisStyle: switch (style->textEmphasisMark()) { case TextEmphasisMarkNone: return cssValuePool().createIdentifierValue(CSSValueNone); case TextEmphasisMarkCustom: return cssValuePool().createValue(style->textEmphasisCustomMark(), CSSPrimitiveValue::CSS_STRING); case TextEmphasisMarkAuto: ASSERT_NOT_REACHED(); // Fall through case TextEmphasisMarkDot: case TextEmphasisMarkCircle: case TextEmphasisMarkDoubleCircle: case TextEmphasisMarkTriangle: case TextEmphasisMarkSesame: { RefPtr list = CSSValueList::createSpaceSeparated(); list->append(cssValuePool().createValue(style->textEmphasisFill())); list->append(cssValuePool().createValue(style->textEmphasisMark())); return list.release(); } } case CSSPropertyTextIndent: { // If RuntimeEnabledFeatures::css3TextEnabled() returns false or text-indent has only one value( | ), // getPropertyCSSValue() returns CSSValue. // If RuntimeEnabledFeatures::css3TextEnabled() returns true and text-indent has each-line or hanging, // getPropertyCSSValue() returns CSSValueList. RefPtr textIndent = pixelValueForLength(style->textIndent(), *style); if (RuntimeEnabledFeatures::css3TextEnabled() && (style->textIndentLine() == TextIndentEachLine || style->textIndentType() == TextIndentHanging)) { RefPtr list = CSSValueList::createSpaceSeparated(); list->append(textIndent.release()); if (style->textIndentLine() == TextIndentEachLine) list->append(cssValuePool().createIdentifierValue(CSSValueEachLine)); if (style->textIndentType() == TextIndentHanging) list->append(cssValuePool().createIdentifierValue(CSSValueHanging)); return list.release(); } return textIndent.release(); } case CSSPropertyTextShadow: return valueForShadowList(style->textShadow(), *style, false); case CSSPropertyTextRendering: return cssValuePool().createValue(style->fontDescription().textRendering()); case CSSPropertyTextOverflow: if (style->textOverflow()) return cssValuePool().createIdentifierValue(CSSValueEllipsis); return cssValuePool().createIdentifierValue(CSSValueClip); case CSSPropertyWebkitTextStrokeColor: return currentColorOrValidColor(*style, style->textStrokeColor()); case CSSPropertyWebkitTextStrokeWidth: return pixelValue(style->textStrokeWidth(), *style); case CSSPropertyTop: return valueForPositionOffset(*style, CSSPropertyTop, renderer); case CSSPropertyTouchAction: return touchActionFlagsToCSSValue(style->touchAction()); case CSSPropertyTouchActionDelay: return cssValuePool().createValue(style->touchActionDelay()); case CSSPropertyUnicodeBidi: return cssValuePool().createValue(style->unicodeBidi()); case CSSPropertyVerticalAlign: switch (style->verticalAlign()) { case BASELINE: return cssValuePool().createIdentifierValue(CSSValueBaseline); case MIDDLE: return cssValuePool().createIdentifierValue(CSSValueMiddle); case SUB: return cssValuePool().createIdentifierValue(CSSValueSub); case SUPER: return cssValuePool().createIdentifierValue(CSSValueSuper); case TEXT_TOP: return cssValuePool().createIdentifierValue(CSSValueTextTop); case TEXT_BOTTOM: return cssValuePool().createIdentifierValue(CSSValueTextBottom); case TOP: return cssValuePool().createIdentifierValue(CSSValueTop); case BOTTOM: return cssValuePool().createIdentifierValue(CSSValueBottom); case BASELINE_MIDDLE: return cssValuePool().createIdentifierValue(CSSValueWebkitBaselineMiddle); case LENGTH: return pixelValueForLength(style->verticalAlignLength(), *style); } ASSERT_NOT_REACHED(); return nullptr; case CSSPropertyWhiteSpace: return cssValuePool().createValue(style->whiteSpace()); case CSSPropertyWidth: if (renderer) { // According to http://www.w3.org/TR/CSS2/visudet.html#the-width-property, // the "width" property does not apply for non-replaced inline elements. if (!renderer->isReplaced() && renderer->isInline()) return cssValuePool().createIdentifierValue(CSSValueAuto); return pixelValue(sizingBox(renderer).width(), *style); } return pixelValueForLength(style->width(), *style); case CSSPropertyWordBreak: return cssValuePool().createValue(style->wordBreak()); case CSSPropertyWordSpacing: return pixelValue(style->wordSpacing(), *style); case CSSPropertyWordWrap: return cssValuePool().createValue(style->overflowWrap()); case CSSPropertyWebkitLineBreak: return cssValuePool().createValue(style->lineBreak()); case CSSPropertyFontKerning: return cssValuePool().createValue(style->fontDescription().kerning()); case CSSPropertyWebkitFontSmoothing: return cssValuePool().createValue(style->fontDescription().fontSmoothing()); case CSSPropertyFontVariantLigatures: { FontDescription::LigaturesState commonLigaturesState = style->fontDescription().commonLigaturesState(); FontDescription::LigaturesState discretionaryLigaturesState = style->fontDescription().discretionaryLigaturesState(); FontDescription::LigaturesState historicalLigaturesState = style->fontDescription().historicalLigaturesState(); FontDescription::LigaturesState contextualLigaturesState = style->fontDescription().contextualLigaturesState(); if (commonLigaturesState == FontDescription::NormalLigaturesState && discretionaryLigaturesState == FontDescription::NormalLigaturesState && historicalLigaturesState == FontDescription::NormalLigaturesState && contextualLigaturesState == FontDescription::NormalLigaturesState) return cssValuePool().createIdentifierValue(CSSValueNormal); RefPtr valueList = CSSValueList::createSpaceSeparated(); if (commonLigaturesState != FontDescription::NormalLigaturesState) valueList->append(cssValuePool().createIdentifierValue(commonLigaturesState == FontDescription::DisabledLigaturesState ? CSSValueNoCommonLigatures : CSSValueCommonLigatures)); if (discretionaryLigaturesState != FontDescription::NormalLigaturesState) valueList->append(cssValuePool().createIdentifierValue(discretionaryLigaturesState == FontDescription::DisabledLigaturesState ? CSSValueNoDiscretionaryLigatures : CSSValueDiscretionaryLigatures)); if (historicalLigaturesState != FontDescription::NormalLigaturesState) valueList->append(cssValuePool().createIdentifierValue(historicalLigaturesState == FontDescription::DisabledLigaturesState ? CSSValueNoHistoricalLigatures : CSSValueHistoricalLigatures)); if (contextualLigaturesState != FontDescription::NormalLigaturesState) valueList->append(cssValuePool().createIdentifierValue(contextualLigaturesState == FontDescription::DisabledLigaturesState ? CSSValueNoContextual : CSSValueContextual)); return valueList; } case CSSPropertyZIndex: if (style->hasAutoZIndex()) return cssValuePool().createIdentifierValue(CSSValueAuto); return cssValuePool().createValue(style->zIndex(), CSSPrimitiveValue::CSS_NUMBER); case CSSPropertyBoxSizing: if (style->boxSizing() == CONTENT_BOX) return cssValuePool().createIdentifierValue(CSSValueContentBox); return cssValuePool().createIdentifierValue(CSSValueBorderBox); case CSSPropertyWebkitAspectRatio: if (!style->hasAspectRatio()) return cssValuePool().createIdentifierValue(CSSValueNone); return CSSAspectRatioValue::create(style->aspectRatioNumerator(), style->aspectRatioDenominator()); case CSSPropertyWebkitFontSizeDelta: // Not a real style property -- used by the editing engine -- so has no computed value. break; case CSSPropertyPerspective: case CSSPropertyWebkitPerspective: if (!style->hasPerspective()) return cssValuePool().createIdentifierValue(CSSValueNone); return pixelValue(style->perspective(), *style); case CSSPropertyPerspectiveOrigin: case CSSPropertyWebkitPerspectiveOrigin: { RefPtr list = CSSValueList::createSpaceSeparated(); if (renderer) { LayoutRect box; if (renderer->isBox()) box = toRenderBox(renderer)->borderBoxRect(); list->append(pixelValue(minimumValueForLength(style->perspectiveOriginX(), box.width()), *style)); list->append(pixelValue(minimumValueForLength(style->perspectiveOriginY(), box.height()), *style)); } else { list->append(pixelValueForLength(style->perspectiveOriginX(), *style)); list->append(pixelValueForLength(style->perspectiveOriginY(), *style)); } return list.release(); } case CSSPropertyWebkitRtlOrdering: return cssValuePool().createIdentifierValue(style->rtlOrdering() ? CSSValueVisual : CSSValueLogical); case CSSPropertyWebkitTapHighlightColor: return currentColorOrValidColor(*style, style->tapHighlightColor()); case CSSPropertyWebkitUserSelect: return cssValuePool().createValue(style->userSelect()); case CSSPropertyBorderBottomLeftRadius: return valueForBorderRadiusCorner(style->borderBottomLeftRadius(), *style); case CSSPropertyBorderBottomRightRadius: return valueForBorderRadiusCorner(style->borderBottomRightRadius(), *style); case CSSPropertyBorderTopLeftRadius: return valueForBorderRadiusCorner(style->borderTopLeftRadius(), *style); case CSSPropertyBorderTopRightRadius: return valueForBorderRadiusCorner(style->borderTopRightRadius(), *style); case CSSPropertyTransform: case CSSPropertyWebkitTransform: return computedTransform(renderer, *style); case CSSPropertyTransformOrigin: case CSSPropertyWebkitTransformOrigin: { RefPtr list = CSSValueList::createSpaceSeparated(); if (renderer) { LayoutRect box; if (renderer->isBox()) box = toRenderBox(renderer)->borderBoxRect(); list->append(pixelValue(minimumValueForLength(style->transformOriginX(), box.width()), *style)); list->append(pixelValue(minimumValueForLength(style->transformOriginY(), box.height()), *style)); if (style->transformOriginZ() != 0) list->append(pixelValue(style->transformOriginZ(), *style)); } else { list->append(pixelValueForLength(style->transformOriginX(), *style)); list->append(pixelValueForLength(style->transformOriginY(), *style)); if (style->transformOriginZ() != 0) list->append(pixelValue(style->transformOriginZ(), *style)); } return list.release(); } case CSSPropertyTransformStyle: case CSSPropertyWebkitTransformStyle: return cssValuePool().createIdentifierValue((style->transformStyle3D() == TransformStyle3DPreserve3D) ? CSSValuePreserve3d : CSSValueFlat); case CSSPropertyPointerEvents: return cssValuePool().createValue(style->pointerEvents()); case CSSPropertyWebkitTextOrientation: return CSSPrimitiveValue::create(style->textOrientation()); case CSSPropertyWebkitLineBoxContain: return createLineBoxContainValue(style->lineBoxContain()); case CSSPropertyWebkitClipPath: if (ClipPathOperation* operation = style->clipPath()) { if (operation->type() == ClipPathOperation::SHAPE) return valueForBasicShape(*style, toShapeClipPathOperation(operation)->basicShape()); } return cssValuePool().createIdentifierValue(CSSValueNone); case CSSPropertyFilter: return valueForFilter(renderer, *style); case CSSPropertyBackground: return valuesForBackgroundShorthand(); case CSSPropertyBorder: { RefPtr value = getPropertyCSSValue(CSSPropertyBorderTop, DoNotUpdateLayout); const CSSPropertyID properties[3] = { CSSPropertyBorderRight, CSSPropertyBorderBottom, CSSPropertyBorderLeft }; for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) { if (!compareCSSValuePtr(value, getPropertyCSSValue(properties[i], DoNotUpdateLayout))) return nullptr; } return value.release(); } case CSSPropertyBorderBottom: return valuesForShorthandProperty(borderBottomShorthand()); case CSSPropertyBorderColor: return valuesForSidesShorthand(borderColorShorthand()); case CSSPropertyBorderLeft: return valuesForShorthandProperty(borderLeftShorthand()); case CSSPropertyBorderRadius: return valueForBorderRadiusShorthand(*style); case CSSPropertyBorderRight: return valuesForShorthandProperty(borderRightShorthand()); case CSSPropertyBorderStyle: return valuesForSidesShorthand(borderStyleShorthand()); case CSSPropertyBorderTop: return valuesForShorthandProperty(borderTopShorthand()); case CSSPropertyBorderWidth: return valuesForSidesShorthand(borderWidthShorthand()); case CSSPropertyMargin: return valuesForSidesShorthand(marginShorthand()); case CSSPropertyOutline: return valuesForShorthandProperty(outlineShorthand()); case CSSPropertyPadding: return valuesForSidesShorthand(paddingShorthand()); /* Individual properties not part of the spec */ case CSSPropertyBackgroundRepeatX: case CSSPropertyBackgroundRepeatY: break; /* Unimplemented CSS 3 properties (including CSS3 shorthand properties) */ case CSSPropertyWebkitTextEmphasis: break; /* Directional properties are resolved by resolveDirectionAwareProperty() before the switch. */ case CSSPropertyWebkitBorderEnd: case CSSPropertyWebkitBorderEndColor: case CSSPropertyWebkitBorderEndStyle: case CSSPropertyWebkitBorderEndWidth: case CSSPropertyWebkitBorderStart: case CSSPropertyWebkitBorderStartColor: case CSSPropertyWebkitBorderStartStyle: case CSSPropertyWebkitBorderStartWidth: case CSSPropertyWebkitBorderAfter: case CSSPropertyWebkitBorderAfterColor: case CSSPropertyWebkitBorderAfterStyle: case CSSPropertyWebkitBorderAfterWidth: case CSSPropertyWebkitBorderBefore: case CSSPropertyWebkitBorderBeforeColor: case CSSPropertyWebkitBorderBeforeStyle: case CSSPropertyWebkitBorderBeforeWidth: case CSSPropertyWebkitMarginEnd: case CSSPropertyWebkitMarginStart: case CSSPropertyWebkitMarginAfter: case CSSPropertyWebkitMarginBefore: case CSSPropertyWebkitPaddingEnd: case CSSPropertyWebkitPaddingStart: case CSSPropertyWebkitPaddingAfter: case CSSPropertyWebkitPaddingBefore: case CSSPropertyWebkitLogicalWidth: case CSSPropertyWebkitLogicalHeight: case CSSPropertyWebkitMinLogicalWidth: case CSSPropertyWebkitMinLogicalHeight: case CSSPropertyWebkitMaxLogicalWidth: case CSSPropertyWebkitMaxLogicalHeight: ASSERT_NOT_REACHED(); break; /* Unimplemented @font-face properties */ case CSSPropertySrc: case CSSPropertyUnicodeRange: break; /* Unimplemented -webkit- properties */ case CSSPropertyWebkitBorderRadius: case CSSPropertyWebkitPerspectiveOriginX: case CSSPropertyWebkitPerspectiveOriginY: case CSSPropertyWebkitTextStroke: case CSSPropertyWebkitTransformOriginX: case CSSPropertyWebkitTransformOriginY: case CSSPropertyWebkitTransformOriginZ: break; /* @viewport rule properties */ case CSSPropertyOrientation: break; } logUnimplementedPropertyID(propertyID); return nullptr; } String CSSComputedStyleDeclaration::getPropertyValue(CSSPropertyID propertyID) const { RefPtr value = getPropertyCSSValue(propertyID); if (value) return value->cssText(); return ""; } unsigned CSSComputedStyleDeclaration::length() const { Node* node = m_node.get(); if (!node) return 0; RenderStyle* style = node->computedStyle(); if (!style) return 0; return computableProperties().size(); } String CSSComputedStyleDeclaration::item(unsigned i) const { if (i >= length()) return ""; return getPropertyNameString(computableProperties()[i]); } bool CSSComputedStyleDeclaration::cssPropertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const { if (propertyID == CSSPropertyFontSize && propertyValue->isPrimitiveValue() && m_node) { m_node->document().updateLayout(); RenderStyle* style = m_node->computedStyle(); if (style && style->fontDescription().keywordSize()) { CSSValueID sizeValue = cssIdentifierForFontSizeKeyword(style->fontDescription().keywordSize()); const CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(propertyValue); if (primitiveValue->isValueID() && primitiveValue->getValueID() == sizeValue) return true; } } RefPtr value = getPropertyCSSValue(propertyID); return value && propertyValue && value->equals(*propertyValue); } PassRefPtr CSSComputedStyleDeclaration::copyProperties() const { return copyPropertiesInSet(computableProperties()); } PassRefPtr CSSComputedStyleDeclaration::valuesForShorthandProperty(const StylePropertyShorthand& shorthand) const { RefPtr list = CSSValueList::createSpaceSeparated(); for (size_t i = 0; i < shorthand.length(); ++i) { RefPtr value = getPropertyCSSValue(shorthand.properties()[i], DoNotUpdateLayout); list->append(value); } return list.release(); } PassRefPtr CSSComputedStyleDeclaration::valuesForSidesShorthand(const StylePropertyShorthand& shorthand) const { RefPtr list = CSSValueList::createSpaceSeparated(); // Assume the properties are in the usual order top, right, bottom, left. RefPtr topValue = getPropertyCSSValue(shorthand.properties()[0], DoNotUpdateLayout); RefPtr rightValue = getPropertyCSSValue(shorthand.properties()[1], DoNotUpdateLayout); RefPtr bottomValue = getPropertyCSSValue(shorthand.properties()[2], DoNotUpdateLayout); RefPtr leftValue = getPropertyCSSValue(shorthand.properties()[3], DoNotUpdateLayout); // All 4 properties must be specified. if (!topValue || !rightValue || !bottomValue || !leftValue) return nullptr; bool showLeft = !compareCSSValuePtr(rightValue, leftValue); bool showBottom = !compareCSSValuePtr(topValue, bottomValue) || showLeft; bool showRight = !compareCSSValuePtr(topValue, rightValue) || showBottom; list->append(topValue.release()); if (showRight) list->append(rightValue.release()); if (showBottom) list->append(bottomValue.release()); if (showLeft) list->append(leftValue.release()); return list.release(); } PassRefPtr CSSComputedStyleDeclaration::copyPropertiesInSet(const Vector& properties) const { Vector list; list.reserveInitialCapacity(properties.size()); for (unsigned i = 0; i < properties.size(); ++i) { RefPtr value = getPropertyCSSValue(properties[i]); if (value) list.append(CSSProperty(properties[i], value.release(), false)); } return MutableStylePropertySet::create(list.data(), list.size()); } PassRefPtr CSSComputedStyleDeclaration::getPropertyCSSValue(const String& propertyName) { CSSPropertyID propertyID = cssPropertyID(propertyName); if (!propertyID) return nullptr; RefPtr value = getPropertyCSSValue(propertyID); return value ? value->cloneForCSSOM() : nullptr; } String CSSComputedStyleDeclaration::getPropertyValue(const String& propertyName) { CSSPropertyID propertyID = cssPropertyID(propertyName); if (!propertyID) return String(); ASSERT(CSSPropertyMetadata::isEnabledProperty(propertyID)); return getPropertyValue(propertyID); } String CSSComputedStyleDeclaration::getPropertyShorthand(const String&) { return ""; } bool CSSComputedStyleDeclaration::isPropertyImplicit(const String&) { return false; } void CSSComputedStyleDeclaration::setProperty(const String& name, const String&, ExceptionState& exceptionState) { exceptionState.ThrowDOMException(NoModificationAllowedError, "These styles are computed, and therefore the '" + name + "' property is read-only."); } String CSSComputedStyleDeclaration::removeProperty(const String& name, ExceptionState& exceptionState) { exceptionState.ThrowDOMException(NoModificationAllowedError, "These styles are computed, and therefore the '" + name + "' property is read-only."); return String(); } PassRefPtr CSSComputedStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID) { return getPropertyCSSValue(propertyID); } String CSSComputedStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID) { return getPropertyValue(propertyID); } void CSSComputedStyleDeclaration::setPropertyInternal(CSSPropertyID id, const String&, ExceptionState& exceptionState) { exceptionState.ThrowDOMException(NoModificationAllowedError, "These styles are computed, and therefore the '" + getPropertyNameString(id) + "' property is read-only."); } PassRefPtr CSSComputedStyleDeclaration::valuesForBackgroundShorthand() const { static const CSSPropertyID propertiesBeforeSlashSeperator[5] = { CSSPropertyBackgroundColor, CSSPropertyBackgroundImage, CSSPropertyBackgroundRepeat, CSSPropertyBackgroundAttachment, CSSPropertyBackgroundPosition }; static const CSSPropertyID propertiesAfterSlashSeperator[3] = { CSSPropertyBackgroundSize, CSSPropertyBackgroundOrigin, CSSPropertyBackgroundClip }; RefPtr list = CSSValueList::createSlashSeparated(); list->append(valuesForShorthandProperty(StylePropertyShorthand(CSSPropertyBackground, propertiesBeforeSlashSeperator, WTF_ARRAY_LENGTH(propertiesBeforeSlashSeperator)))); list->append(valuesForShorthandProperty(StylePropertyShorthand(CSSPropertyBackground, propertiesAfterSlashSeperator, WTF_ARRAY_LENGTH(propertiesAfterSlashSeperator)))); return list.release(); } } // namespace blink