/* * Copyright (C) 2013 Google Inc. All rights reserved. * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. * Copyright (C) 2007 Alexey Proskuryakov * Copyright (C) 2007, 2008 Eric Seidel * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (c) 2011, Code Aurora Forum. All rights reserved. * Copyright (C) Research In Motion Limited 2011. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "core/CSSPropertyNames.h" #include "core/CSSValueKeywords.h" #include "core/StyleBuilderFunctions.h" #include "core/StylePropertyShorthand.h" #include "core/css/BasicShapeFunctions.h" #include "core/css/CSSAspectRatioValue.h" #include "core/css/CSSCursorImageValue.h" #include "core/css/CSSFontValue.h" #include "core/css/CSSGradientValue.h" #include "core/css/CSSGridTemplateAreasValue.h" #include "core/css/CSSHelper.h" #include "core/css/CSSImageSetValue.h" #include "core/css/CSSLineBoxContainValue.h" #include "core/css/parser/BisonCSSParser.h" #include "core/css/CSSPrimitiveValueMappings.h" #include "core/css/CSSPropertyMetadata.h" #include "core/css/Pair.h" #include "core/css/StylePropertySet.h" #include "core/css/StyleRule.h" #include "core/css/resolver/ElementStyleResources.h" #include "core/css/resolver/FilterOperationResolver.h" #include "core/css/resolver/FontBuilder.h" #include "core/css/resolver/StyleBuilder.h" #include "core/css/resolver/TransformBuilder.h" #include "core/frame/LocalFrame.h" #include "core/frame/Settings.h" #include "core/rendering/style/QuotesData.h" #include "core/rendering/style/RenderStyle.h" #include "core/rendering/style/RenderStyleConstants.h" #include "core/rendering/style/StyleGeneratedImage.h" #include "platform/fonts/FontDescription.h" #include "wtf/MathExtras.h" #include "wtf/StdLibExtras.h" #include "wtf/Vector.h" namespace blink { namespace { static inline bool isValidVisitedLinkProperty(CSSPropertyID id) { switch (id) { case CSSPropertyBackgroundColor: case CSSPropertyBorderLeftColor: case CSSPropertyBorderRightColor: case CSSPropertyBorderTopColor: case CSSPropertyBorderBottomColor: case CSSPropertyColor: case CSSPropertyOutlineColor: case CSSPropertyTextDecorationColor: case CSSPropertyWebkitTextEmphasisColor: case CSSPropertyWebkitTextFillColor: case CSSPropertyWebkitTextStrokeColor: return true; default: return false; } } } // namespace void StyleBuilder::applyProperty(CSSPropertyID id, StyleResolverState& state, CSSValue* value) { ASSERT_WITH_MESSAGE(!isExpandedShorthand(id), "Shorthand property id = %d wasn't expanded at parsing time", id); bool isInherit = state.parentNode() && value->isInheritedValue(); bool isInitial = value->isInitialValue() || (!state.parentNode() && value->isInheritedValue()); ASSERT(!isInherit || !isInitial); // isInherit -> !isInitial && isInitial -> !isInherit ASSERT(!isInherit || (state.parentNode() && state.parentStyle())); // isInherit -> (state.parentNode() && state.parentStyle()) if (!state.applyPropertyToRegularStyle() && (!state.applyPropertyToVisitedLinkStyle() || !isValidVisitedLinkProperty(id))) { // Limit the properties that can be applied to only the ones honored by :visited. return; } CSSPrimitiveValue* primitiveValue = value->isPrimitiveValue() ? toCSSPrimitiveValue(value) : 0; if (primitiveValue && primitiveValue->getValueID() == CSSValueCurrentcolor) state.style()->setHasCurrentColor(); if (isInherit && !state.parentStyle()->hasExplicitlyInheritedProperties() && !CSSPropertyMetadata::isInheritedProperty(id)) state.parentStyle()->setHasExplicitlyInheritedProperties(); StyleBuilder::applyProperty(id, state, value, isInitial, isInherit); } void StyleBuilderFunctions::applyInitialCSSPropertyColor(StyleResolverState& state) { Color color = RenderStyle::initialColor(); if (state.applyPropertyToRegularStyle()) state.style()->setColor(color); if (state.applyPropertyToVisitedLinkStyle()) state.style()->setVisitedLinkColor(color); } void StyleBuilderFunctions::applyInheritCSSPropertyColor(StyleResolverState& state) { Color color = state.parentStyle()->color(); if (state.applyPropertyToRegularStyle()) state.style()->setColor(color); if (state.applyPropertyToVisitedLinkStyle()) state.style()->setVisitedLinkColor(color); } void StyleBuilderFunctions::applyValueCSSPropertyColor(StyleResolverState& state, CSSValue* value) { CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); // As per the spec, 'color: currentColor' is treated as 'color: inherit' if (primitiveValue->getValueID() == CSSValueCurrentcolor) { applyInheritCSSPropertyColor(state); return; } if (state.applyPropertyToRegularStyle()) state.style()->setColor(StyleBuilderConverter::convertColor(state, value)); if (state.applyPropertyToVisitedLinkStyle()) state.style()->setVisitedLinkColor(StyleBuilderConverter::convertColor(state, value, true)); } void StyleBuilderFunctions::applyInitialCSSPropertyJustifyItems(StyleResolverState& state) { state.style()->setJustifyItems(RenderStyle::initialJustifyItems()); state.style()->setJustifyItemsOverflowAlignment(RenderStyle::initialJustifyItemsOverflowAlignment()); state.style()->setJustifyItemsPositionType(RenderStyle::initialJustifyItemsPositionType()); } void StyleBuilderFunctions::applyInheritCSSPropertyJustifyItems(StyleResolverState& state) { state.style()->setJustifyItems(state.parentStyle()->justifyItems()); state.style()->setJustifyItemsOverflowAlignment(state.parentStyle()->justifyItemsOverflowAlignment()); state.style()->setJustifyItemsPositionType(state.parentStyle()->justifyItemsPositionType()); } void StyleBuilderFunctions::applyValueCSSPropertyJustifyItems(StyleResolverState& state, CSSValue* value) { CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (Pair* pairValue = primitiveValue->getPairValue()) { if (pairValue->first()->getValueID() == CSSValueLegacy) { state.style()->setJustifyItemsPositionType(LegacyPosition); state.style()->setJustifyItems(*pairValue->second()); } else { state.style()->setJustifyItems(*pairValue->first()); state.style()->setJustifyItemsOverflowAlignment(*pairValue->second()); } } else { state.style()->setJustifyItems(*primitiveValue); } } void StyleBuilderFunctions::applyInitialCSSPropertyCursor(StyleResolverState& state) { state.style()->clearCursorList(); state.style()->setCursor(RenderStyle::initialCursor()); } void StyleBuilderFunctions::applyInheritCSSPropertyCursor(StyleResolverState& state) { state.style()->setCursor(state.parentStyle()->cursor()); state.style()->setCursorList(state.parentStyle()->cursors()); } void StyleBuilderFunctions::applyValueCSSPropertyCursor(StyleResolverState& state, CSSValue* value) { state.style()->clearCursorList(); if (value->isValueList()) { CSSValueList* list = toCSSValueList(value); int len = list->length(); state.style()->setCursor(CURSOR_AUTO); for (int i = 0; i < len; i++) { CSSValue* item = list->item(i); if (item->isCursorImageValue()) { CSSCursorImageValue* image = toCSSCursorImageValue(item); state.style()->addCursor(state.styleImage(CSSPropertyCursor, image), image->hotSpot()); } else { state.style()->setCursor(*toCSSPrimitiveValue(item)); } } } else { state.style()->setCursor(*toCSSPrimitiveValue(value)); } } void StyleBuilderFunctions::applyValueCSSPropertyDirection(StyleResolverState& state, CSSValue* value) { state.style()->setDirection(*toCSSPrimitiveValue(value)); Element* element = state.element(); if (element && element == element->document().documentElement()) element->document().setDirectionSetOnDocumentElement(true); } void StyleBuilderFunctions::applyInitialCSSPropertyFontFamily(StyleResolverState& state) { state.fontBuilder().setFontFamilyInitial(); } void StyleBuilderFunctions::applyInheritCSSPropertyFontFamily(StyleResolverState& state) { state.fontBuilder().setFontFamilyInherit(state.parentFontDescription()); } void StyleBuilderFunctions::applyValueCSSPropertyFontFamily(StyleResolverState& state, CSSValue* value) { state.fontBuilder().setFontFamilyValue(value); } void StyleBuilderFunctions::applyInitialCSSPropertyFontSize(StyleResolverState& state) { state.fontBuilder().setFontSizeInitial(); } void StyleBuilderFunctions::applyInheritCSSPropertyFontSize(StyleResolverState& state) { state.fontBuilder().setFontSizeInherit(state.parentFontDescription()); } void StyleBuilderFunctions::applyValueCSSPropertyFontSize(StyleResolverState& state, CSSValue* value) { state.fontBuilder().setFontSizeValue(value, state.parentStyle(), state.rootElementStyle()); } void StyleBuilderFunctions::applyInitialCSSPropertyGridTemplateAreas(StyleResolverState& state) { state.style()->setNamedGridArea(RenderStyle::initialNamedGridArea()); state.style()->setNamedGridAreaRowCount(RenderStyle::initialNamedGridAreaCount()); state.style()->setNamedGridAreaColumnCount(RenderStyle::initialNamedGridAreaCount()); } void StyleBuilderFunctions::applyInheritCSSPropertyGridTemplateAreas(StyleResolverState& state) { state.style()->setNamedGridArea(state.parentStyle()->namedGridArea()); state.style()->setNamedGridAreaRowCount(state.parentStyle()->namedGridAreaRowCount()); state.style()->setNamedGridAreaColumnCount(state.parentStyle()->namedGridAreaColumnCount()); } void StyleBuilderFunctions::applyValueCSSPropertyGridTemplateAreas(StyleResolverState& state, CSSValue* value) { if (value->isPrimitiveValue()) { // FIXME: Shouldn't we clear the grid-area values ASSERT(toCSSPrimitiveValue(value)->getValueID() == CSSValueNone); return; } CSSGridTemplateAreasValue* gridTemplateAreasValue = toCSSGridTemplateAreasValue(value); const NamedGridAreaMap& newNamedGridAreas = gridTemplateAreasValue->gridAreaMap(); NamedGridLinesMap namedGridColumnLines = state.style()->namedGridColumnLines(); NamedGridLinesMap namedGridRowLines = state.style()->namedGridRowLines(); StyleBuilderConverter::createImplicitNamedGridLinesFromGridArea(newNamedGridAreas, namedGridColumnLines, ForColumns); StyleBuilderConverter::createImplicitNamedGridLinesFromGridArea(newNamedGridAreas, namedGridRowLines, ForRows); state.style()->setNamedGridColumnLines(namedGridColumnLines); state.style()->setNamedGridRowLines(namedGridRowLines); state.style()->setNamedGridArea(newNamedGridAreas); state.style()->setNamedGridAreaRowCount(gridTemplateAreasValue->rowCount()); state.style()->setNamedGridAreaColumnCount(gridTemplateAreasValue->columnCount()); } void StyleBuilderFunctions::applyValueCSSPropertyLineHeight(StyleResolverState& state, CSSValue* value) { CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); Length lineHeight; if (primitiveValue->getValueID() == CSSValueNormal) { lineHeight = RenderStyle::initialLineHeight(); } else if (primitiveValue->isLength()) { float multiplier = state.style()->effectiveZoom(); if (LocalFrame* frame = state.document().frame()) multiplier *= frame->textZoomFactor(); lineHeight = primitiveValue->computeLength(state.cssToLengthConversionData().copyWithAdjustedZoom(multiplier)); } else if (primitiveValue->isPercentage()) { lineHeight = Length((state.style()->computedFontSize() * primitiveValue->getIntValue()) / 100.0, Fixed); } else if (primitiveValue->isNumber()) { lineHeight = Length(primitiveValue->getDoubleValue() * 100.0, Percent); } else if (primitiveValue->isCalculated()) { double multiplier = state.style()->effectiveZoom(); if (LocalFrame* frame = state.document().frame()) multiplier *= frame->textZoomFactor(); Length zoomedLength = Length(primitiveValue->cssCalcValue()->toCalcValue(state.cssToLengthConversionData().copyWithAdjustedZoom(multiplier))); lineHeight = Length(valueForLength(zoomedLength, state.style()->fontSize()), Fixed); } else { return; } state.style()->setLineHeight(lineHeight); } void StyleBuilderFunctions::applyValueCSSPropertyListStyleImage(StyleResolverState& state, CSSValue* value) { state.style()->setListStyleImage(state.styleImage(CSSPropertyListStyleImage, value)); } void StyleBuilderFunctions::applyInitialCSSPropertyOutlineStyle(StyleResolverState& state) { state.style()->setOutlineStyleIsAuto(RenderStyle::initialOutlineStyleIsAuto()); state.style()->setOutlineStyle(RenderStyle::initialBorderStyle()); } void StyleBuilderFunctions::applyInheritCSSPropertyOutlineStyle(StyleResolverState& state) { state.style()->setOutlineStyleIsAuto(state.parentStyle()->outlineStyleIsAuto()); state.style()->setOutlineStyle(state.parentStyle()->outlineStyle()); } void StyleBuilderFunctions::applyValueCSSPropertyOutlineStyle(StyleResolverState& state, CSSValue* value) { CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); state.style()->setOutlineStyleIsAuto(*primitiveValue); state.style()->setOutlineStyle(*primitiveValue); } void StyleBuilderFunctions::applyValueCSSPropertyResize(StyleResolverState& state, CSSValue* value) { CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); EResize r = RESIZE_NONE; if (primitiveValue->getValueID() == CSSValueAuto) { if (Settings* settings = state.document().settings()) r = settings->textAreasAreResizable() ? RESIZE_BOTH : RESIZE_NONE; } else { r = *primitiveValue; } state.style()->setResize(r); } static Length mmLength(double mm) { return Length(mm * cssPixelsPerMillimeter, Fixed); } static Length inchLength(double inch) { return Length(inch * cssPixelsPerInch, Fixed); } static bool getPageSizeFromName(CSSPrimitiveValue* pageSizeName, CSSPrimitiveValue* pageOrientation, Length& width, Length& height) { DEFINE_STATIC_LOCAL(Length, a5Width, (mmLength(148))); DEFINE_STATIC_LOCAL(Length, a5Height, (mmLength(210))); DEFINE_STATIC_LOCAL(Length, a4Width, (mmLength(210))); DEFINE_STATIC_LOCAL(Length, a4Height, (mmLength(297))); DEFINE_STATIC_LOCAL(Length, a3Width, (mmLength(297))); DEFINE_STATIC_LOCAL(Length, a3Height, (mmLength(420))); DEFINE_STATIC_LOCAL(Length, b5Width, (mmLength(176))); DEFINE_STATIC_LOCAL(Length, b5Height, (mmLength(250))); DEFINE_STATIC_LOCAL(Length, b4Width, (mmLength(250))); DEFINE_STATIC_LOCAL(Length, b4Height, (mmLength(353))); DEFINE_STATIC_LOCAL(Length, letterWidth, (inchLength(8.5))); DEFINE_STATIC_LOCAL(Length, letterHeight, (inchLength(11))); DEFINE_STATIC_LOCAL(Length, legalWidth, (inchLength(8.5))); DEFINE_STATIC_LOCAL(Length, legalHeight, (inchLength(14))); DEFINE_STATIC_LOCAL(Length, ledgerWidth, (inchLength(11))); DEFINE_STATIC_LOCAL(Length, ledgerHeight, (inchLength(17))); if (!pageSizeName) return false; switch (pageSizeName->getValueID()) { case CSSValueA5: width = a5Width; height = a5Height; break; case CSSValueA4: width = a4Width; height = a4Height; break; case CSSValueA3: width = a3Width; height = a3Height; break; case CSSValueB5: width = b5Width; height = b5Height; break; case CSSValueB4: width = b4Width; height = b4Height; break; case CSSValueLetter: width = letterWidth; height = letterHeight; break; case CSSValueLegal: width = legalWidth; height = legalHeight; break; case CSSValueLedger: width = ledgerWidth; height = ledgerHeight; break; default: return false; } if (pageOrientation) { switch (pageOrientation->getValueID()) { case CSSValueLandscape: std::swap(width, height); break; case CSSValuePortrait: // Nothing to do. break; default: return false; } } return true; } void StyleBuilderFunctions::applyInitialCSSPropertySize(StyleResolverState&) { } void StyleBuilderFunctions::applyInheritCSSPropertySize(StyleResolverState&) { } void StyleBuilderFunctions::applyValueCSSPropertySize(StyleResolverState& state, CSSValue* value) { state.style()->resetPageSizeType(); Length width; Length height; PageSizeType pageSizeType = PAGE_SIZE_AUTO; CSSValueListInspector inspector(value); switch (inspector.length()) { case 2: { // {2} | if (!inspector.first()->isPrimitiveValue() || !inspector.second()->isPrimitiveValue()) return; CSSPrimitiveValue* first = toCSSPrimitiveValue(inspector.first()); CSSPrimitiveValue* second = toCSSPrimitiveValue(inspector.second()); if (first->isLength()) { // {2} if (!second->isLength()) return; width = first->computeLength(state.cssToLengthConversionData().copyWithAdjustedZoom(1.0)); height = second->computeLength(state.cssToLengthConversionData().copyWithAdjustedZoom(1.0)); } else { // // The value order is guaranteed. See BisonCSSParser::parseSizeParameter. if (!getPageSizeFromName(first, second, width, height)) return; } pageSizeType = PAGE_SIZE_RESOLVED; break; } case 1: { // | auto | | [ portrait | landscape] if (!inspector.first()->isPrimitiveValue()) return; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(inspector.first()); if (primitiveValue->isLength()) { // pageSizeType = PAGE_SIZE_RESOLVED; width = height = primitiveValue->computeLength(state.cssToLengthConversionData().copyWithAdjustedZoom(1.0)); } else { switch (primitiveValue->getValueID()) { case 0: return; case CSSValueAuto: pageSizeType = PAGE_SIZE_AUTO; break; case CSSValuePortrait: pageSizeType = PAGE_SIZE_AUTO_PORTRAIT; break; case CSSValueLandscape: pageSizeType = PAGE_SIZE_AUTO_LANDSCAPE; break; default: // pageSizeType = PAGE_SIZE_RESOLVED; if (!getPageSizeFromName(primitiveValue, 0, width, height)) return; } } break; } default: return; } state.style()->setPageSizeType(pageSizeType); state.style()->setPageSize(LengthSize(width, height)); } void StyleBuilderFunctions::applyValueCSSPropertyTextAlign(StyleResolverState& state, CSSValue* value) { if (!value->isPrimitiveValue()) return; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); // FIXME : Per http://www.w3.org/TR/css3-text/#text-align0 can now take but this is not implemented in the // rendering code. if (primitiveValue->isString()) return; if (primitiveValue->isValueID() && primitiveValue->getValueID() != CSSValueWebkitMatchParent) state.style()->setTextAlign(*primitiveValue); else if (state.parentStyle()->textAlign() == TASTART) state.style()->setTextAlign(state.parentStyle()->isLeftToRightDirection() ? LEFT : RIGHT); else if (state.parentStyle()->textAlign() == TAEND) state.style()->setTextAlign(state.parentStyle()->isLeftToRightDirection() ? RIGHT : LEFT); else state.style()->setTextAlign(state.parentStyle()->textAlign()); } void StyleBuilderFunctions::applyInheritCSSPropertyTextIndent(StyleResolverState& state) { state.style()->setTextIndent(state.parentStyle()->textIndent()); state.style()->setTextIndentLine(state.parentStyle()->textIndentLine()); state.style()->setTextIndentType(state.parentStyle()->textIndentType()); } void StyleBuilderFunctions::applyInitialCSSPropertyTextIndent(StyleResolverState& state) { state.style()->setTextIndent(RenderStyle::initialTextIndent()); state.style()->setTextIndentLine(RenderStyle::initialTextIndentLine()); state.style()->setTextIndentType(RenderStyle::initialTextIndentType()); } void StyleBuilderFunctions::applyValueCSSPropertyTextIndent(StyleResolverState& state, CSSValue* value) { if (!value->isValueList()) return; Length lengthOrPercentageValue; TextIndentLine textIndentLineValue = RenderStyle::initialTextIndentLine(); TextIndentType textIndentTypeValue = RenderStyle::initialTextIndentType(); for (CSSValueListIterator i(value); i.hasMore(); i.advance()) { CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(i.value()); if (!primitiveValue->getValueID()) lengthOrPercentageValue = primitiveValue->convertToLength(state.cssToLengthConversionData()); else if (primitiveValue->getValueID() == CSSValueEachLine) textIndentLineValue = TextIndentEachLine; else if (primitiveValue->getValueID() == CSSValueHanging) textIndentTypeValue = TextIndentHanging; else ASSERT_NOT_REACHED(); } state.style()->setTextIndent(lengthOrPercentageValue); state.style()->setTextIndentLine(textIndentLineValue); state.style()->setTextIndentType(textIndentTypeValue); } void StyleBuilderFunctions::applyValueCSSPropertyTransform(StyleResolverState& state, CSSValue* value) { TransformOperations operations; TransformBuilder::createTransformOperations(value, state.cssToLengthConversionData(), operations); state.style()->setTransform(operations); } void StyleBuilderFunctions::applyInitialCSSPropertyTransformOrigin(StyleResolverState& state) { applyInitialCSSPropertyWebkitTransformOriginX(state); applyInitialCSSPropertyWebkitTransformOriginY(state); applyInitialCSSPropertyWebkitTransformOriginZ(state); } void StyleBuilderFunctions::applyInheritCSSPropertyTransformOrigin(StyleResolverState& state) { applyInheritCSSPropertyWebkitTransformOriginX(state); applyInheritCSSPropertyWebkitTransformOriginY(state); applyInheritCSSPropertyWebkitTransformOriginZ(state); } void StyleBuilderFunctions::applyValueCSSPropertyTransformOrigin(StyleResolverState& state, CSSValue* value) { CSSValueList* list = toCSSValueList(value); ASSERT(list->length() == 3); CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(list->item(0)); if (primitiveValue->isValueID()) { switch (primitiveValue->getValueID()) { case CSSValueLeft: state.style()->setTransformOriginX(Length(0, Percent)); break; case CSSValueRight: state.style()->setTransformOriginX(Length(100, Percent)); break; case CSSValueCenter: state.style()->setTransformOriginX(Length(50, Percent)); break; default: ASSERT_NOT_REACHED(); } } else { state.style()->setTransformOriginX(StyleBuilderConverter::convertLength(state, primitiveValue)); } primitiveValue = toCSSPrimitiveValue(list->item(1)); if (primitiveValue->isValueID()) { switch (primitiveValue->getValueID()) { case CSSValueTop: state.style()->setTransformOriginY(Length(0, Percent)); break; case CSSValueBottom: state.style()->setTransformOriginY(Length(100, Percent)); break; case CSSValueCenter: state.style()->setTransformOriginY(Length(50, Percent)); break; default: ASSERT_NOT_REACHED(); } } else { state.style()->setTransformOriginY(StyleBuilderConverter::convertLength(state, primitiveValue)); } primitiveValue = toCSSPrimitiveValue(list->item(2)); state.style()->setTransformOriginZ(StyleBuilderConverter::convertComputedLength(state, primitiveValue)); } void StyleBuilderFunctions::applyInitialCSSPropertyPerspectiveOrigin(StyleResolverState& state) { applyInitialCSSPropertyWebkitPerspectiveOriginX(state); applyInitialCSSPropertyWebkitPerspectiveOriginY(state); } void StyleBuilderFunctions::applyInheritCSSPropertyPerspectiveOrigin(StyleResolverState& state) { applyInheritCSSPropertyWebkitPerspectiveOriginX(state); applyInheritCSSPropertyWebkitPerspectiveOriginY(state); } void StyleBuilderFunctions::applyValueCSSPropertyPerspectiveOrigin(StyleResolverState& state, CSSValue* value) { CSSValueList* list = toCSSValueList(value); ASSERT(list->length() == 2); CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(list->item(0)); if (primitiveValue->isValueID()) { switch (primitiveValue->getValueID()) { case CSSValueLeft: state.style()->setPerspectiveOriginX(Length(0, Percent)); break; case CSSValueRight: state.style()->setPerspectiveOriginX(Length(100, Percent)); break; case CSSValueCenter: state.style()->setPerspectiveOriginX(Length(50, Percent)); break; default: ASSERT_NOT_REACHED(); } } else { state.style()->setPerspectiveOriginX(StyleBuilderConverter::convertLength(state, primitiveValue)); } primitiveValue = toCSSPrimitiveValue(list->item(1)); if (primitiveValue->isValueID()) { switch (primitiveValue->getValueID()) { case CSSValueTop: state.style()->setPerspectiveOriginY(Length(0, Percent)); break; case CSSValueBottom: state.style()->setPerspectiveOriginY(Length(100, Percent)); break; case CSSValueCenter: state.style()->setPerspectiveOriginY(Length(50, Percent)); break; default: ASSERT_NOT_REACHED(); } } else { state.style()->setPerspectiveOriginY(StyleBuilderConverter::convertLength(state, primitiveValue)); } } void StyleBuilderFunctions::applyInheritCSSPropertyVerticalAlign(StyleResolverState& state) { EVerticalAlign verticalAlign = state.parentStyle()->verticalAlign(); state.style()->setVerticalAlign(verticalAlign); if (verticalAlign == LENGTH) state.style()->setVerticalAlignLength(state.parentStyle()->verticalAlignLength()); } void StyleBuilderFunctions::applyValueCSSPropertyVerticalAlign(StyleResolverState& state, CSSValue* value) { if (!value->isPrimitiveValue()) return; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (primitiveValue->getValueID()) { state.style()->setVerticalAlign(*primitiveValue); return; } state.style()->setVerticalAlignLength(primitiveValue->convertToLength(state.cssToLengthConversionData())); } static void resetEffectiveZoom(StyleResolverState& state) { // Reset the zoom in effect. This allows the setZoom method to accurately compute a new zoom in effect. state.setEffectiveZoom(state.parentStyle() ? state.parentStyle()->effectiveZoom() : RenderStyle::initialZoom()); } void StyleBuilderFunctions::applyInitialCSSPropertyZoom(StyleResolverState& state) { resetEffectiveZoom(state); state.setZoom(RenderStyle::initialZoom()); } void StyleBuilderFunctions::applyInheritCSSPropertyZoom(StyleResolverState& state) { resetEffectiveZoom(state); state.setZoom(state.parentStyle()->zoom()); } void StyleBuilderFunctions::applyValueCSSPropertyZoom(StyleResolverState& state, CSSValue* value) { ASSERT_WITH_SECURITY_IMPLICATION(value->isPrimitiveValue()); CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (primitiveValue->getValueID() == CSSValueNormal) { resetEffectiveZoom(state); state.setZoom(RenderStyle::initialZoom()); } else if (primitiveValue->getValueID() == CSSValueReset) { state.setEffectiveZoom(RenderStyle::initialZoom()); state.setZoom(RenderStyle::initialZoom()); } else if (primitiveValue->getValueID() == CSSValueDocument) { float docZoom = state.rootElementStyle() ? state.rootElementStyle()->zoom() : RenderStyle::initialZoom(); state.setEffectiveZoom(docZoom); state.setZoom(docZoom); } else if (primitiveValue->isPercentage()) { resetEffectiveZoom(state); if (float percent = primitiveValue->getFloatValue()) state.setZoom(percent / 100.0f); } else if (primitiveValue->isNumber()) { resetEffectiveZoom(state); if (float number = primitiveValue->getFloatValue()) state.setZoom(number); } } void StyleBuilderFunctions::applyInitialCSSPropertyWebkitAspectRatio(StyleResolverState& state) { state.style()->setHasAspectRatio(RenderStyle::initialHasAspectRatio()); state.style()->setAspectRatioDenominator(RenderStyle::initialAspectRatioDenominator()); state.style()->setAspectRatioNumerator(RenderStyle::initialAspectRatioNumerator()); } void StyleBuilderFunctions::applyInheritCSSPropertyWebkitAspectRatio(StyleResolverState& state) { if (!state.parentStyle()->hasAspectRatio()) return; state.style()->setHasAspectRatio(true); state.style()->setAspectRatioDenominator(state.parentStyle()->aspectRatioDenominator()); state.style()->setAspectRatioNumerator(state.parentStyle()->aspectRatioNumerator()); } void StyleBuilderFunctions::applyValueCSSPropertyWebkitAspectRatio(StyleResolverState& state, CSSValue* value) { if (!value->isAspectRatioValue()) { state.style()->setHasAspectRatio(false); return; } CSSAspectRatioValue* aspectRatioValue = toCSSAspectRatioValue(value); state.style()->setHasAspectRatio(true); state.style()->setAspectRatioDenominator(aspectRatioValue->denominatorValue()); state.style()->setAspectRatioNumerator(aspectRatioValue->numeratorValue()); } void StyleBuilderFunctions::applyValueCSSPropertyWebkitBorderImage(StyleResolverState& state, CSSValue* value) { NinePieceImage image; state.styleMap().mapNinePieceImage(state.style(), CSSPropertyWebkitBorderImage, value, image); state.style()->setBorderImage(image); } void StyleBuilderFunctions::applyValueCSSPropertyWebkitClipPath(StyleResolverState& state, CSSValue* value) { if (value->isPrimitiveValue()) { CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (primitiveValue->getValueID() == CSSValueNone) { state.style()->setClipPath(nullptr); } else if (primitiveValue->isShape()) { state.style()->setClipPath(ShapeClipPathOperation::create(basicShapeForValue(state, primitiveValue->getShapeValue()))); } } } void StyleBuilderFunctions::applyValueCSSPropertyWebkitFilter(StyleResolverState& state, CSSValue* value) { FilterOperations operations; if (FilterOperationResolver::createFilterOperations(value, state.cssToLengthConversionData(), operations, state)) state.style()->setFilter(operations); } // FIXME: We should use the same system for this as the rest of the pseudo-shorthands (e.g. background-position) void StyleBuilderFunctions::applyInitialCSSPropertyWebkitPerspectiveOrigin(StyleResolverState& state) { applyInitialCSSPropertyWebkitPerspectiveOriginX(state); applyInitialCSSPropertyWebkitPerspectiveOriginY(state); } void StyleBuilderFunctions::applyInheritCSSPropertyWebkitPerspectiveOrigin(StyleResolverState& state) { applyInheritCSSPropertyWebkitPerspectiveOriginX(state); applyInheritCSSPropertyWebkitPerspectiveOriginY(state); } void StyleBuilderFunctions::applyValueCSSPropertyWebkitPerspectiveOrigin(StyleResolverState&, CSSValue* value) { // This is expanded in the parser ASSERT_NOT_REACHED(); } void StyleBuilderFunctions::applyInitialCSSPropertyWebkitTextEmphasisStyle(StyleResolverState& state) { state.style()->setTextEmphasisFill(RenderStyle::initialTextEmphasisFill()); state.style()->setTextEmphasisMark(RenderStyle::initialTextEmphasisMark()); state.style()->setTextEmphasisCustomMark(RenderStyle::initialTextEmphasisCustomMark()); } void StyleBuilderFunctions::applyInheritCSSPropertyWebkitTextEmphasisStyle(StyleResolverState& state) { state.style()->setTextEmphasisFill(state.parentStyle()->textEmphasisFill()); state.style()->setTextEmphasisMark(state.parentStyle()->textEmphasisMark()); state.style()->setTextEmphasisCustomMark(state.parentStyle()->textEmphasisCustomMark()); } void StyleBuilderFunctions::applyValueCSSPropertyWebkitTextEmphasisStyle(StyleResolverState& state, CSSValue* value) { if (value->isValueList()) { CSSValueList* list = toCSSValueList(value); ASSERT(list->length() == 2); if (list->length() != 2) return; for (unsigned i = 0; i < 2; ++i) { CSSValue* item = list->item(i); if (!item->isPrimitiveValue()) continue; CSSPrimitiveValue* value = toCSSPrimitiveValue(item); if (value->getValueID() == CSSValueFilled || value->getValueID() == CSSValueOpen) state.style()->setTextEmphasisFill(*value); else state.style()->setTextEmphasisMark(*value); } state.style()->setTextEmphasisCustomMark(nullAtom); return; } if (!value->isPrimitiveValue()) return; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (primitiveValue->isString()) { state.style()->setTextEmphasisFill(TextEmphasisFillFilled); state.style()->setTextEmphasisMark(TextEmphasisMarkCustom); state.style()->setTextEmphasisCustomMark(AtomicString(primitiveValue->getStringValue())); return; } state.style()->setTextEmphasisCustomMark(nullAtom); if (primitiveValue->getValueID() == CSSValueFilled || primitiveValue->getValueID() == CSSValueOpen) { state.style()->setTextEmphasisFill(*primitiveValue); state.style()->setTextEmphasisMark(TextEmphasisMarkAuto); } else { state.style()->setTextEmphasisFill(TextEmphasisFillFilled); state.style()->setTextEmphasisMark(*primitiveValue); } } void StyleBuilderFunctions::applyInitialCSSPropertyWillChange(StyleResolverState& state) { state.style()->setWillChangeContents(false); state.style()->setWillChangeScrollPosition(false); state.style()->setWillChangeProperties(Vector()); state.style()->setSubtreeWillChangeContents(state.parentStyle()->subtreeWillChangeContents()); } void StyleBuilderFunctions::applyInheritCSSPropertyWillChange(StyleResolverState& state) { state.style()->setWillChangeContents(state.parentStyle()->willChangeContents()); state.style()->setWillChangeScrollPosition(state.parentStyle()->willChangeScrollPosition()); state.style()->setWillChangeProperties(state.parentStyle()->willChangeProperties()); state.style()->setSubtreeWillChangeContents(state.parentStyle()->subtreeWillChangeContents()); } void StyleBuilderFunctions::applyValueCSSPropertyWillChange(StyleResolverState& state, CSSValue* value) { ASSERT(value->isValueList()); bool willChangeContents = false; bool willChangeScrollPosition = false; Vector willChangeProperties; for (CSSValueListIterator i(value); i.hasMore(); i.advance()) { CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(i.value()); if (CSSPropertyID propertyID = primitiveValue->getPropertyID()) willChangeProperties.append(propertyID); else if (primitiveValue->getValueID() == CSSValueContents) willChangeContents = true; else if (primitiveValue->getValueID() == CSSValueScrollPosition) willChangeScrollPosition = true; else ASSERT_NOT_REACHED(); } state.style()->setWillChangeContents(willChangeContents); state.style()->setWillChangeScrollPosition(willChangeScrollPosition); state.style()->setWillChangeProperties(willChangeProperties); state.style()->setSubtreeWillChangeContents(willChangeContents || state.parentStyle()->subtreeWillChangeContents()); } void StyleBuilderFunctions::applyInitialCSSPropertyContent(StyleResolverState& state) { state.style()->clearContent(); } void StyleBuilderFunctions::applyInheritCSSPropertyContent(StyleResolverState&) { // FIXME: In CSS3, it will be possible to inherit content. In CSS2 it is not. This // note is a reminder that eventually "inherit" needs to be supported. } void StyleBuilderFunctions::applyValueCSSPropertyContent(StyleResolverState& state, CSSValue* value) { // list of string, uri, counter, attr, i if (!value->isValueList()) return; bool didSet = false; for (CSSValueListIterator i = value; i.hasMore(); i.advance()) { CSSValue* item = i.value(); if (item->isImageGeneratorValue()) { if (item->isGradientValue()) state.style()->setContent(StyleGeneratedImage::create(toCSSGradientValue(item)->gradientWithStylesResolved(state.document().textLinkColors(), state.style()->color()).get()), didSet); else state.style()->setContent(StyleGeneratedImage::create(toCSSImageGeneratorValue(item)), didSet); didSet = true; } else if (item->isImageSetValue()) { state.style()->setContent(state.elementStyleResources().setOrPendingFromValue(CSSPropertyContent, toCSSImageSetValue(item)), didSet); didSet = true; } if (item->isImageValue()) { state.style()->setContent(state.elementStyleResources().cachedOrPendingFromValue(state.document(), CSSPropertyContent, toCSSImageValue(item)), didSet); didSet = true; continue; } if (!item->isPrimitiveValue()) continue; CSSPrimitiveValue* contentValue = toCSSPrimitiveValue(item); if (contentValue->isString()) { state.style()->setContent(contentValue->getStringValue().impl(), didSet); didSet = true; } else if (contentValue->isAttr()) { // FIXME: Can a namespace be specified for an attr(foo)? if (state.style()->styleType() == NOPSEUDO) state.style()->setUnique(); else state.parentStyle()->setUnique(); QualifiedName attr(AtomicString(contentValue->getStringValue())); const AtomicString& value = state.element()->getAttribute(attr); state.style()->setContent(value.isNull() ? emptyString() : value.string(), didSet); didSet = true; // register the fact that the attribute value affects the style state.contentAttrValues().append(attr.localName()); } else { switch (contentValue->getValueID()) { default: // normal and none do not have any effect. { } } } } if (!didSet) state.style()->clearContent(); } void StyleBuilderFunctions::applyInitialCSSPropertyFont(StyleResolverState&) { ASSERT_NOT_REACHED(); } void StyleBuilderFunctions::applyInheritCSSPropertyFont(StyleResolverState&) { ASSERT_NOT_REACHED(); } void StyleBuilderFunctions::applyValueCSSPropertyFont(StyleResolverState& state, CSSValue* value) { // Only System Font identifiers should come through this method // all other values should have been handled when the shorthand // was expanded by the parser. // FIXME: System Font identifiers should not hijack this // short-hand CSSProperty like this (crbug.com/353932) state.style()->setLineHeight(RenderStyle::initialLineHeight()); state.setLineHeightValue(0); state.fontBuilder().fromSystemFont(toCSSPrimitiveValue(value)->getValueID(), state.style()->effectiveZoom()); } void StyleBuilderFunctions::applyValueCSSPropertyWebkitLocale(StyleResolverState& state, CSSValue* value) { if (!value->isPrimitiveValue()) return; const CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (primitiveValue->getValueID() == CSSValueAuto) state.style()->setLocale(nullAtom); else state.style()->setLocale(AtomicString(primitiveValue->getStringValue())); state.fontBuilder().setScript(state.style()->locale()); } void StyleBuilderFunctions::applyInitialCSSPropertyWebkitPerspective(StyleResolverState& state) { applyInitialCSSPropertyPerspective(state); } void StyleBuilderFunctions::applyInheritCSSPropertyWebkitPerspective(StyleResolverState& state) { applyInheritCSSPropertyPerspective(state); } void StyleBuilderFunctions::applyValueCSSPropertyWebkitPerspective(StyleResolverState& state, CSSValue* value) { if (!value->isPrimitiveValue()) return; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (primitiveValue->isNumber()) { float perspectiveValue = CSSPrimitiveValue::create(primitiveValue->getDoubleValue(), CSSPrimitiveValue::CSS_PX)->computeLength(state.cssToLengthConversionData()); if (perspectiveValue >= 0.0f) state.style()->setPerspective(perspectiveValue); } else { applyValueCSSPropertyPerspective(state, value); } } void StyleBuilderFunctions::applyValueCSSPropertyPerspective(StyleResolverState& state, CSSValue* value) { if (!value->isPrimitiveValue()) return; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (primitiveValue->getValueID() == CSSValueNone) { state.style()->setPerspective(0); return; } if (!primitiveValue->isLength()) return; float perspectiveValue = primitiveValue->computeLength(state.cssToLengthConversionData()); if (perspectiveValue >= 0.0f) state.style()->setPerspective(perspectiveValue); } void StyleBuilderFunctions::applyValueCSSPropertyWebkitWritingMode(StyleResolverState& state, CSSValue* value) { if (value->isPrimitiveValue()) state.setWritingMode(*toCSSPrimitiveValue(value)); // FIXME: It is not ok to modify document state while applying style. if (state.element() && state.element() == state.document().documentElement()) state.document().setWritingModeSetOnDocumentElement(true); } void StyleBuilderFunctions::applyValueCSSPropertyWebkitTextOrientation(StyleResolverState& state, CSSValue* value) { if (value->isPrimitiveValue()) state.setTextOrientation(*toCSSPrimitiveValue(value)); } void StyleBuilderFunctions::applyValueCSSPropertyGridAutoFlow(StyleResolverState& state, CSSValue* value) { ASSERT(value->isValueList()); CSSValueList* list = toCSSValueList(value); CSSPrimitiveValue* first = list->length() >= 1 ? toCSSPrimitiveValue(list->item(0)) : nullptr; if (!first) { applyInitialCSSPropertyGridAutoFlow(state); return; } CSSPrimitiveValue* second = list->length() == 2 ? toCSSPrimitiveValue(list->item(1)) : nullptr; GridAutoFlow autoFlow = RenderStyle::initialGridAutoFlow(); switch (first->getValueID()) { case CSSValueRow: if (second) { if (second->getValueID() == CSSValueDense) autoFlow = AutoFlowRowDense; else autoFlow = AutoFlowStackRow; } else { autoFlow = AutoFlowRow; } break; case CSSValueColumn: if (second) { if (second->getValueID() == CSSValueDense) autoFlow = AutoFlowColumnDense; else autoFlow = AutoFlowStackColumn; } else { autoFlow = AutoFlowColumn; } break; case CSSValueDense: if (second && second->getValueID() == CSSValueColumn) autoFlow = AutoFlowColumnDense; else autoFlow = AutoFlowRowDense; break; case CSSValueStack: if (second && second->getValueID() == CSSValueColumn) autoFlow = AutoFlowStackColumn; else autoFlow = AutoFlowStackRow; break; default: ASSERT_NOT_REACHED(); break; } state.style()->setGridAutoFlow(autoFlow); } } // namespace blink