{% from 'macros.tmpl' import license %} {# This file is for property handlers which use the templating engine to reduce (handwritten) code duplication. The `properties' dict can be used to access a property's parameters in jinja2 templates (i.e. setter, getter, initial, type_name) #} #include "StyleBuilderFunctions.h" #include "CSSValueKeywords.h" #include "core/css/BasicShapeFunctions.h" #include "core/css/CSSContentDistributionValue.h" #include "core/css/CSSCustomIdentValue.h" #include "core/css/CSSPrimitiveValueMappings.h" #include "core/css/CSSURIValue.h" #include "core/css/CSSValuePair.h" #include "core/css/resolver/StyleResolverState.h" {% macro declare_initial_function(property_id) %} void StyleBuilderFunctions::applyInitial{{property_id}}(StyleResolverState& state) {%- endmacro %} {% macro declare_inherit_function(property_id) %} void StyleBuilderFunctions::applyInherit{{property_id}}(StyleResolverState& state) {%- endmacro %} {% macro declare_value_function(property_id) %} void StyleBuilderFunctions::applyValue{{property_id}}(StyleResolverState& state, CSSValue* value) {%- endmacro %} {% macro set_value(property) %} {% if property.svg %} state.style()->accessSVGStyle().{{property.setter}} {%- elif property.font %} state.fontBuilder().{{property.setter}} {%- else %} state.style()->{{property.setter}} {%- endif %} {% endmacro %} {% macro convert_and_set_value(property) %} {% if property.converter %} {{set_value(property)}}(StyleBuilderConverter::{{property.converter}}(state, *value)); {%- else %} {{set_value(property)}}(toCSSPrimitiveValue(value)->convertTo<{{property.type_name}}>()); {%- endif %} {% endmacro %} namespace blink { {% for property_id, property in properties.items() if property.should_declare_functions %} {% set apply_type = property.apply_type %} {% if not property.custom_initial %} {{declare_initial_function(property_id)}} { {% if property.svg %} {{set_value(property)}}(SVGComputedStyle::{{property.initial}}()); {% elif property.font %} {{set_value(property)}}(FontBuilder::{{property.initial}}()); {% else %} {{set_value(property)}}(ComputedStyle::{{property.initial}}()); {% endif %} } {% endif %} {% if not property.custom_inherit %} {{declare_inherit_function(property_id)}} { {% if property.svg %} {{set_value(property)}}(state.parentStyle()->svgStyle().{{property.getter}}()); {% elif property.font %} {{set_value(property)}}(state.parentFontDescription().{{property.getter}}()); {% else %} {{set_value(property)}}(state.parentStyle()->{{property.getter}}()); {% endif %} } {% endif %} {% if not property.custom_value %} {{declare_value_function(property_id)}} { {{convert_and_set_value(property)}} } {% endif %} {% endfor %} {% macro apply_animation(property_id, attribute, animation) %} {% set vector = attribute|lower_first + "List()" %} {{declare_initial_function(property_id)}} { CSS{{animation}}Data& data = state.style()->access{{animation}}s(); data.{{vector}}.clear(); data.{{vector}}.append(CSS{{animation}}Data::initial{{attribute}}()); } {{declare_inherit_function(property_id)}} { const CSS{{animation}}Data* parentData = state.parentStyle()->{{animation|lower}}s(); if (!parentData) applyInitial{{property_id}}(state); else state.style()->access{{animation}}s().{{vector}} = parentData->{{vector}}; } {{declare_value_function(property_id)}} { CSS{{animation}}Data& data = state.style()->access{{animation}}s(); data.{{vector}}.clear(); for (auto& listValue : toCSSValueList(*value)) data.{{vector}}.append(CSSToStyleMap::mapAnimation{{attribute}}(*listValue)); } {% endmacro %} {{apply_animation('CSSPropertyAnimationDelay', 'Delay', 'Animation')}} {{apply_animation('CSSPropertyAnimationDirection', 'Direction', 'Animation')}} {{apply_animation('CSSPropertyAnimationDuration', 'Duration', 'Animation')}} {{apply_animation('CSSPropertyAnimationFillMode', 'FillMode', 'Animation')}} {{apply_animation('CSSPropertyAnimationIterationCount', 'IterationCount', 'Animation')}} {{apply_animation('CSSPropertyAnimationName', 'Name', 'Animation')}} {{apply_animation('CSSPropertyAnimationPlayState', 'PlayState', 'Animation')}} {{apply_animation('CSSPropertyAnimationTimingFunction', 'TimingFunction', 'Animation')}} {{apply_animation('CSSPropertyTransitionDelay', 'Delay', 'Transition')}} {{apply_animation('CSSPropertyTransitionDuration', 'Duration', 'Transition')}} {{apply_animation('CSSPropertyTransitionProperty', 'Property', 'Transition')}} {{apply_animation('CSSPropertyTransitionTimingFunction', 'TimingFunction', 'Transition')}} {% macro apply_auto(property_id, auto_getter=none, auto_setter=none, auto_identity='CSSValueAuto') %} {% set property = properties[property_id] %} {% set auto_getter = auto_getter or 'hasAuto' + property.name_for_methods %} {% set auto_setter = auto_setter or 'setHasAuto' + property.name_for_methods %} {{declare_initial_function(property_id)}} { state.style()->{{auto_setter}}(); } {{declare_inherit_function(property_id)}} { if (state.parentStyle()->{{auto_getter}}()) state.style()->{{auto_setter}}(); else {{set_value(property)}}(state.parentStyle()->{{property.getter}}()); } {{declare_value_function(property_id)}} { if (value->isPrimitiveValue() && toCSSPrimitiveValue(value)->getValueID() == {{auto_identity}}) state.style()->{{auto_setter}}(); else {{convert_and_set_value(property)}} } {% endmacro %} {{apply_auto('CSSPropertyClip')}} {{apply_auto('CSSPropertyOrphans')}} {{apply_auto('CSSPropertyColumnCount')}} {{apply_auto('CSSPropertyColumnGap', auto_getter='hasNormalColumnGap', auto_setter='setHasNormalColumnGap', auto_identity='CSSValueNormal')}} {{apply_auto('CSSPropertyColumnWidth')}} {{apply_auto('CSSPropertyWidows')}} {{apply_auto('CSSPropertyZIndex')}} static bool lengthMatchesAllSides(const LengthBox& lengthBox, const Length& length) { return (lengthBox.left() == length && lengthBox.right() == length && lengthBox.top() == length && lengthBox.bottom() == length); } static bool borderImageLengthMatchesAllSides(const BorderImageLengthBox& borderImageLengthBox, const BorderImageLength& borderImageLength) { return (borderImageLengthBox.left() == borderImageLength && borderImageLengthBox.right() == borderImageLength && borderImageLengthBox.top() == borderImageLength && borderImageLengthBox.bottom() == borderImageLength); } {% macro apply_border_image_modifier(property_id, modifier_type) %} {% set is_mask_box = 'MaskBox' in property_id %} {% set getter = 'maskBoxImage' if is_mask_box else 'borderImage' %} {% set setter = 'setMaskBoxImage' if is_mask_box else 'setBorderImage' %} {{ declare_initial_function(property_id) }} { const NinePieceImage& currentImage = state.style()->{{getter}}(); {# Check for equality in case we can bail out before creating a new NinePieceImage. #} {% if modifier_type == 'Outset' %} if (borderImageLengthMatchesAllSides(currentImage.outset(), BorderImageLength(Length(0, Fixed)))) return; {% elif modifier_type == 'Repeat' %} if (currentImage.horizontalRule() == StretchImageRule && currentImage.verticalRule() == StretchImageRule) return; {% elif modifier_type == 'Slice' and is_mask_box %} // Masks have a different initial value for slices. Preserve the value of 0 for backwards compatibility. if (currentImage.fill() == true && lengthMatchesAllSides(currentImage.imageSlices(), Length(0, Fixed))) return; {% elif modifier_type == 'Slice' and not is_mask_box %} if (currentImage.fill() == false && lengthMatchesAllSides(currentImage.imageSlices(), Length(100, Percent))) return; {% elif modifier_type == 'Width' and is_mask_box %} // Masks have a different initial value for widths. Preserve the value of 'auto' for backwards compatibility. if (borderImageLengthMatchesAllSides(currentImage.borderSlices(), BorderImageLength(Length(Auto)))) return; {% elif modifier_type == 'Width' and not is_mask_box %} if (borderImageLengthMatchesAllSides(currentImage.borderSlices(), BorderImageLength(1.0))) return; {% endif %} NinePieceImage image(currentImage); {% if modifier_type == 'Outset' %} image.setOutset(Length(0, Fixed)); {% elif modifier_type == 'Repeat' %} image.setHorizontalRule(StretchImageRule); image.setVerticalRule(StretchImageRule); {% elif modifier_type == 'Slice' and is_mask_box %} image.setImageSlices(LengthBox({{ (['Length(0, Fixed)']*4) | join(', ') }})); image.setFill(true); {% elif modifier_type == 'Slice' and not is_mask_box %} image.setImageSlices(LengthBox({{ (['Length(100, Percent)']*4) | join(', ') }})); image.setFill(false); {% elif modifier_type == 'Width' %} image.setBorderSlices({{ 'Length(Auto)' if is_mask_box else '1.0' }}); {% endif %} state.style()->{{setter}}(image); } {{declare_inherit_function(property_id)}} { NinePieceImage image(state.style()->{{getter}}()); {% if modifier_type == 'Outset' %} image.copyOutsetFrom(state.parentStyle()->{{getter}}()); {% elif modifier_type == 'Repeat' %} image.copyRepeatFrom(state.parentStyle()->{{getter}}()); {% elif modifier_type == 'Slice' %} image.copyImageSlicesFrom(state.parentStyle()->{{getter}}()); {% elif modifier_type == 'Width' %} image.copyBorderSlicesFrom(state.parentStyle()->{{getter}}()); {% endif %} state.style()->{{setter}}(image); } {{declare_value_function(property_id)}} { NinePieceImage image(state.style()->{{getter}}()); {% if modifier_type == 'Outset' %} image.setOutset(CSSToStyleMap::mapNinePieceImageQuad(state, *value)); {% elif modifier_type == 'Repeat' %} CSSToStyleMap::mapNinePieceImageRepeat(state, *value, image); {% elif modifier_type == 'Slice' %} CSSToStyleMap::mapNinePieceImageSlice(state, *value, image); {% elif modifier_type == 'Width' %} image.setBorderSlices(CSSToStyleMap::mapNinePieceImageQuad(state, *value)); {% endif %} state.style()->{{setter}}(image); } {% endmacro %} {{apply_border_image_modifier('CSSPropertyBorderImageOutset', 'Outset')}} {{apply_border_image_modifier('CSSPropertyBorderImageRepeat', 'Repeat')}} {{apply_border_image_modifier('CSSPropertyBorderImageSlice', 'Slice')}} {{apply_border_image_modifier('CSSPropertyBorderImageWidth', 'Width')}} {{apply_border_image_modifier('CSSPropertyWebkitMaskBoxImageOutset', 'Outset')}} {{apply_border_image_modifier('CSSPropertyWebkitMaskBoxImageRepeat', 'Repeat')}} {{apply_border_image_modifier('CSSPropertyWebkitMaskBoxImageSlice', 'Slice')}} {{apply_border_image_modifier('CSSPropertyWebkitMaskBoxImageWidth', 'Width')}} {% macro apply_value_border_image_source(property_id) %} {{declare_value_function(property_id)}} { {% set property = properties[property_id] %} {{set_value(property)}}(state.styleImage({{property_id}}, *value)); } {% endmacro %} {{apply_value_border_image_source('CSSPropertyBorderImageSource')}} {{apply_value_border_image_source('CSSPropertyWebkitMaskBoxImageSource')}} {% macro apply_color(property_id, initial_color='StyleColor::currentColor') %} {% set property = properties[property_id] %} {% set visited_link_setter = 'setVisitedLink' + property.name_for_methods %} {{declare_initial_function(property_id)}} { StyleColor color = {{initial_color}}(); if (state.applyPropertyToRegularStyle()) {{set_value(property)}}(color); if (state.applyPropertyToVisitedLinkStyle()) state.style()->{{visited_link_setter}}(color); } {{declare_inherit_function(property_id)}} { // Visited link style can never explicitly inherit from parent visited link style so no separate getters are needed. StyleColor color = state.parentStyle()->{{property.getter}}(); if (state.applyPropertyToRegularStyle()) {{set_value(property)}}(color); if (state.applyPropertyToVisitedLinkStyle()) state.style()->{{visited_link_setter}}(color); } {{declare_value_function(property_id)}} { if (state.applyPropertyToRegularStyle()) {{set_value(property)}}(StyleBuilderConverter::convertStyleColor(state, *value)); if (state.applyPropertyToVisitedLinkStyle()) state.style()->{{visited_link_setter}}(StyleBuilderConverter::convertStyleColor(state, *value, true)); } {% endmacro %} {{apply_color('CSSPropertyBackgroundColor', initial_color='ComputedStyle::initialBackgroundColor') }} {{apply_color('CSSPropertyBorderBottomColor')}} {{apply_color('CSSPropertyBorderLeftColor')}} {{apply_color('CSSPropertyBorderRightColor')}} {{apply_color('CSSPropertyBorderTopColor')}} {{apply_color('CSSPropertyOutlineColor')}} {{apply_color('CSSPropertyTextDecorationColor')}} {{apply_color('CSSPropertyColumnRuleColor')}} {{apply_color('CSSPropertyWebkitTextEmphasisColor')}} {{apply_color('CSSPropertyWebkitTextFillColor')}} {{apply_color('CSSPropertyWebkitTextStrokeColor')}} {% macro apply_counter(property_id, action) %} {% set property = properties[property_id] %} {{declare_initial_function(property_id)}} { state.style()->clear{{action}}Directives(); } {{declare_inherit_function(property_id)}} { const CounterDirectiveMap* parentMap = state.parentStyle()->counterDirectives(); if (!parentMap) return; CounterDirectiveMap& map = state.style()->accessCounterDirectives(); ASSERT(!parentMap->isEmpty()); typedef CounterDirectiveMap::const_iterator Iterator; Iterator end = parentMap->end(); for (Iterator it = parentMap->begin(); it != end; ++it) { CounterDirectives& directives = map.add(it->key, CounterDirectives()).storedValue->value; directives.inherit{{action}}(it->value); } } {{declare_value_function(property_id)}} { state.style()->clear{{action}}Directives(); if (!value->isValueList()) { ASSERT(value->isPrimitiveValue() && toCSSPrimitiveValue(value)->getValueID() == CSSValueNone); return; } CounterDirectiveMap& map = state.style()->accessCounterDirectives(); CSSValueList* list = toCSSValueList(value); int length = list ? list->length() : 0; for (int i = 0; i < length; ++i) { const CSSValuePair* pair = toCSSValuePair(list->item(i)); AtomicString identifier(toCSSCustomIdentValue(pair->first()).value()); int value = toCSSPrimitiveValue(pair->second()).getIntValue(); CounterDirectives& directives = map.add(identifier, CounterDirectives()).storedValue->value; {% if action == 'Reset' %} directives.setResetValue(value); {% else %} directives.addIncrementValue(value); {% endif %} } ASSERT(!map.isEmpty()); } {% endmacro %} {{apply_counter('CSSPropertyCounterIncrement', 'Increment')}} {{apply_counter('CSSPropertyCounterReset', 'Reset')}} {% macro apply_fill_layer(property_id, fill_type) %} {% set layer_type = 'Background' if 'Background' in property_id else 'Mask' %} {% set fill_layer_type = layer_type + 'FillLayer' %} {% set access_layers = 'access' + layer_type + 'Layers' %} {% set map_fill = 'mapFill' + fill_type %} {{declare_initial_function(property_id)}} { FillLayer* currChild = &state.style()->{{access_layers}}(); currChild->set{{fill_type}}(FillLayer::initialFill{{fill_type}}({{fill_layer_type}})); for (currChild = currChild->next(); currChild; currChild = currChild->next()) currChild->clear{{fill_type}}(); } {{declare_inherit_function(property_id)}} { FillLayer* currChild = &state.style()->{{access_layers}}(); FillLayer* prevChild = 0; const FillLayer* currParent = &state.parentStyle()->{{layer_type|lower}}Layers(); while (currParent && currParent->is{{fill_type}}Set()) { if (!currChild) currChild = prevChild->ensureNext(); currChild->set{{fill_type}}(currParent->{{fill_type|lower_first}}()); prevChild = currChild; currChild = prevChild->next(); currParent = currParent->next(); } while (currChild) { /* Reset any remaining layers to not have the property set. */ currChild->clear{{fill_type}}(); currChild = currChild->next(); } } {{declare_value_function(property_id)}} { FillLayer* currChild = &state.style()->{{access_layers}}(); FillLayer* prevChild = 0; if (value->isValueList() && !value->isImageSetValue()) { /* Walk each value and put it into a layer, creating new layers as needed. */ CSSValueList* valueList = toCSSValueList(value); for (unsigned int i = 0; i < valueList->length(); i++) { if (!currChild) currChild = prevChild->ensureNext(); CSSToStyleMap::{{map_fill}}(state, currChild, *valueList->item(i)); prevChild = currChild; currChild = currChild->next(); } } else { CSSToStyleMap::{{map_fill}}(state, currChild, *value); currChild = currChild->next(); } while (currChild) { /* Reset all remaining layers to not have the property set. */ currChild->clear{{fill_type}}(); currChild = currChild->next(); } } {% endmacro %} {{apply_fill_layer('CSSPropertyBackgroundAttachment', 'Attachment')}} {{apply_fill_layer('CSSPropertyBackgroundBlendMode', 'BlendMode')}} {{apply_fill_layer('CSSPropertyBackgroundClip', 'Clip')}} {{apply_fill_layer('CSSPropertyBackgroundImage', 'Image')}} {{apply_fill_layer('CSSPropertyBackgroundOrigin', 'Origin')}} {{apply_fill_layer('CSSPropertyBackgroundPositionX', 'XPosition')}} {{apply_fill_layer('CSSPropertyBackgroundPositionY', 'YPosition')}} {{apply_fill_layer('CSSPropertyBackgroundRepeatX', 'RepeatX')}} {{apply_fill_layer('CSSPropertyBackgroundRepeatY', 'RepeatY')}} {{apply_fill_layer('CSSPropertyBackgroundSize', 'Size')}} {{apply_fill_layer('CSSPropertyMaskSourceType', 'MaskSourceType')}} {{apply_fill_layer('CSSPropertyWebkitMaskClip', 'Clip')}} {{apply_fill_layer('CSSPropertyWebkitMaskComposite', 'Composite')}} {{apply_fill_layer('CSSPropertyWebkitMaskImage', 'Image')}} {{apply_fill_layer('CSSPropertyWebkitMaskOrigin', 'Origin')}} {{apply_fill_layer('CSSPropertyWebkitMaskPositionX', 'XPosition')}} {{apply_fill_layer('CSSPropertyWebkitMaskPositionY', 'YPosition')}} {{apply_fill_layer('CSSPropertyWebkitMaskRepeatX', 'RepeatX')}} {{apply_fill_layer('CSSPropertyWebkitMaskRepeatY', 'RepeatY')}} {{apply_fill_layer('CSSPropertyWebkitMaskSize', 'Size')}} {% macro apply_grid_template(property_id, type) %} {{declare_initial_function(property_id)}} { state.style()->setGridTemplate{{type}}s(ComputedStyle::initialGridTemplate{{type}}s()); state.style()->setNamedGrid{{type}}Lines(ComputedStyle::initialNamedGrid{{type}}Lines()); state.style()->setOrderedNamedGrid{{type}}Lines(ComputedStyle::initialOrderedNamedGrid{{type}}Lines()); } {{declare_inherit_function(property_id)}} { state.style()->setGridTemplate{{type}}s(state.parentStyle()->gridTemplate{{type}}s()); state.style()->setNamedGrid{{type}}Lines(state.parentStyle()->namedGrid{{type}}Lines()); state.style()->setOrderedNamedGrid{{type}}Lines(state.parentStyle()->orderedNamedGrid{{type}}Lines()); } {{declare_value_function(property_id)}} { Vector trackSizes; NamedGridLinesMap namedGridLines; OrderedNamedGridLines orderedNamedGridLines; StyleBuilderConverter::convertGridTrackList(*value, trackSizes, namedGridLines, orderedNamedGridLines, state); const NamedGridAreaMap& namedGridAreas = state.style()->namedGridArea(); if (!namedGridAreas.isEmpty()) StyleBuilderConverter::createImplicitNamedGridLinesFromGridArea(namedGridAreas, namedGridLines, For{{type}}s); state.style()->setGridTemplate{{type}}s(trackSizes); state.style()->setNamedGrid{{type}}Lines(namedGridLines); state.style()->setOrderedNamedGrid{{type}}Lines(orderedNamedGridLines); } {% endmacro %} {{apply_grid_template('CSSPropertyGridTemplateColumns', 'Column')}} {{apply_grid_template('CSSPropertyGridTemplateRows', 'Row')}} {% macro apply_svg_paint(property_id, paint_type) %} {% set property = properties[property_id] %} {{declare_initial_function(property_id)}} { {{set_value(property)}}( SVGComputedStyle::initial{{paint_type}}Type(), SVGComputedStyle::initial{{paint_type}}Color(), SVGComputedStyle::initial{{paint_type}}Uri(), state.applyPropertyToRegularStyle(), state.applyPropertyToVisitedLinkStyle()); } {{declare_inherit_function(property_id)}} { const SVGComputedStyle& svgParentStyle = state.parentStyle()->svgStyle(); {{set_value(property)}}( svgParentStyle.{{paint_type|lower_first}}Type(), svgParentStyle.{{paint_type|lower_first}}Color(), svgParentStyle.{{paint_type|lower_first}}Uri(), state.applyPropertyToRegularStyle(), state.applyPropertyToVisitedLinkStyle()); } {{declare_value_function(property_id)}} { String url; if (value->isValueList()) { CSSValueList* list = toCSSValueList(value); ASSERT(list->length() == 2); url = toCSSURIValue(list->item(0))->value(); value = list->item(1); } Color color; SVGPaintType paintType = SVG_PAINTTYPE_RGBCOLOR; if (value->isURIValue()) { paintType = SVG_PAINTTYPE_URI; url = toCSSURIValue(value)->value(); } else if (value->isPrimitiveValue() && toCSSPrimitiveValue(value)->getValueID() == CSSValueNone) { paintType = url.isEmpty() ? SVG_PAINTTYPE_NONE : SVG_PAINTTYPE_URI_NONE; } else if (value->isPrimitiveValue() && toCSSPrimitiveValue(value)->getValueID() == CSSValueCurrentcolor) { color = state.style()->color(); paintType = url.isEmpty() ? SVG_PAINTTYPE_CURRENTCOLOR : SVG_PAINTTYPE_URI_CURRENTCOLOR; } else { color = StyleBuilderConverter::convertColor(state, *value); paintType = url.isEmpty() ? SVG_PAINTTYPE_RGBCOLOR : SVG_PAINTTYPE_URI_RGBCOLOR; } {{set_value(property)}}(paintType, color, url, state.applyPropertyToRegularStyle(), state.applyPropertyToVisitedLinkStyle()); } {% endmacro %} {{apply_svg_paint('CSSPropertyFill', 'FillPaint')}} {{apply_svg_paint('CSSPropertyStroke', 'StrokePaint')}} } // namespace blink