{% 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 "config.h" #include "StyleBuilderFunctions.h" #include "CSSValueKeywords.h" #include "core/css/BasicShapeFunctions.h" #include "core/css/CSSPrimitiveValueMappings.h" #include "core/css/Pair.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.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)}}(static_cast<{{property.type_name}}>(*toCSSPrimitiveValue(value))); {%- 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.font %} {{set_value(property)}}(FontBuilder::{{property.initial}}()); {% else %} {{set_value(property)}}(RenderStyle::{{property.initial}}()); {% endif %} } {% endif %} {% if not property.custom_inherit %} {{declare_inherit_function(property_id)}} { {% if 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 (CSSValueListIterator i = value; i.hasMore(); i.advance()) data.{{vector}}.append(state.styleMap().mapAnimation{{attribute}}(i.value())); } {% 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()) return; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (primitiveValue->getValueID() == {{auto_identity}}) state.style()->{{auto_setter}}(); else {{convert_and_set_value(property)}} } {% endmacro %} {{apply_auto('CSSPropertyClip')}} {{apply_auto('CSSPropertyZIndex')}} static bool lengthTypeAndValueMatch(const Length& length, LengthType type, float value) { return length.type() == type && length.value() == value; } static bool lengthTypeAndValueMatch(const LengthBox& lengthBox, LengthType type, float value) { return (lengthTypeAndValueMatch(lengthBox.left(), type, value) && lengthTypeAndValueMatch(lengthBox.right(), type, value) && lengthTypeAndValueMatch(lengthBox.top(), type, value) && lengthTypeAndValueMatch(lengthBox.bottom(), type, value)); } static bool lengthTypeAndValueMatch(const BorderImageLength& borderImageLength, LengthType type, float value) { return borderImageLength.isLength() && lengthTypeAndValueMatch(borderImageLength.length(), type, value); } static bool lengthTypeAndValueMatch(const BorderImageLengthBox& borderImageLengthBox, LengthType type, float value) { return (lengthTypeAndValueMatch(borderImageLengthBox.left(), type, value) && lengthTypeAndValueMatch(borderImageLengthBox.right(), type, value) && lengthTypeAndValueMatch(borderImageLengthBox.top(), type, value) && lengthTypeAndValueMatch(borderImageLengthBox.bottom(), type, value)); } {% macro apply_border_image_modifier(property_id, modifier_type) %} {{ declare_initial_function(property_id) }} { const NinePieceImage& currentImage = state.style()->borderImage(); {# Check for equality in case we can bail out before creating a new NinePieceImage. #} {% if modifier_type == 'Outset' %} if (lengthTypeAndValueMatch(currentImage.outset(), Fixed, 0)) return; {% elif modifier_type == 'Repeat' %} if (currentImage.horizontalRule() == StretchImageRule && currentImage.verticalRule() == StretchImageRule) return; {% elif modifier_type == 'Slice' %} if (currentImage.fill() == false && lengthTypeAndValueMatch(currentImage.imageSlices(), Percent, 100)) return; {% elif modifier_type == 'Width' %} if (lengthTypeAndValueMatch(currentImage.borderSlices(), Fixed, 1)) 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' %} image.setImageSlices(LengthBox({{ (['Length(100, Percent)']*4) | join(', ') }})); image.setFill(false); {% elif modifier_type == 'Width' %} image.setBorderSlices(1.0); {% endif %} state.style()->setBorderImage(image); } {{declare_inherit_function(property_id)}} { NinePieceImage image(state.style()->borderImage()); {% if modifier_type == 'Outset' %} image.copyOutsetFrom(state.parentStyle()->borderImage()); {% elif modifier_type == 'Repeat' %} image.copyRepeatFrom(state.parentStyle()->borderImage()); {% elif modifier_type == 'Slice' %} image.copyImageSlicesFrom(state.parentStyle()->borderImage()); {% elif modifier_type == 'Width' %} image.copyBorderSlicesFrom(state.parentStyle()->borderImage()); {% endif %} state.style()->setBorderImage(image); } {{declare_value_function(property_id)}} { NinePieceImage image(state.style()->borderImage()); {% if modifier_type == 'Outset' %} image.setOutset(state.styleMap().mapNinePieceImageQuad(value)); {% elif modifier_type == 'Repeat' %} state.styleMap().mapNinePieceImageRepeat(value, image); {% elif modifier_type == 'Slice' %} state.styleMap().mapNinePieceImageSlice(value, image); {% elif modifier_type == 'Width' %} image.setBorderSlices(state.styleMap().mapNinePieceImageQuad(value)); {% endif %} state.style()->setBorderImage(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')}} {% 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')}} {% macro apply_color(property_id, initial_color='StyleColor::currentColor') %} {% set property = properties[property_id] %} {{declare_initial_function(property_id)}} { StyleColor color = {{initial_color}}(); {{set_value(property)}}(color); } {{declare_inherit_function(property_id)}} { StyleColor color = state.parentStyle()->{{property.getter}}(); Color resolvedColor = color.resolve(state.parentStyle()->color()); {{set_value(property)}}(resolvedColor); } {{declare_value_function(property_id)}} { {{set_value(property)}}(StyleBuilderConverter::convertColor(state, value)); } {% endmacro %} {{apply_color('CSSPropertyBackgroundColor', initial_color='RenderStyle::initialBackgroundColor') }} {{apply_color('CSSPropertyBorderBottomColor')}} {{apply_color('CSSPropertyBorderLeftColor')}} {{apply_color('CSSPropertyBorderRightColor')}} {{apply_color('CSSPropertyBorderTopColor')}} {{apply_color('CSSPropertyOutlineColor')}} {{apply_color('CSSPropertyTextDecorationColor')}} {{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)}} { } {{declare_inherit_function(property_id)}} { CounterDirectiveMap& map = state.style()->accessCounterDirectives(); CounterDirectiveMap& parentMap = state.parentStyle()->accessCounterDirectives(); typedef CounterDirectiveMap::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)}} { CounterDirectiveMap& map = state.style()->accessCounterDirectives(); typedef CounterDirectiveMap::iterator Iterator; Iterator end = map.end(); for (Iterator it = map.begin(); it != end; ++it) it->value.clear{{action}}(); if (!value->isValueList()) { ASSERT(value->isPrimitiveValue() && toCSSPrimitiveValue(value)->getValueID() == CSSValueNone); return; } CSSValueList* list = toCSSValueList(value); int length = list ? list->length() : 0; for (int i = 0; i < length; ++i) { CSSValue* currValue = list->item(i); if (!currValue->isPrimitiveValue()) continue; Pair* pair = toCSSPrimitiveValue(currValue)->getPairValue(); if (!pair || !pair->first() || !pair->second()) continue; AtomicString identifier(pair->first()->getStringValue()); int value = pair->second()->getIntValue(); CounterDirectives& directives = map.add(identifier, CounterDirectives()).storedValue->value; {% if action == 'Reset' %} directives.setResetValue(value); {% else %} directives.addIncrementValue(value); {% endif %} } } {% endmacro %} {% macro apply_fill_layer(property_id, fill_type) %} {% set access_layers = 'accessBackgroundLayers' %} {% 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}}(BackgroundFillLayer)); 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()->backgroundLayers(); 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(); state.styleMap().{{map_fill}}(currChild, valueList->item(i)); prevChild = currChild; currChild = currChild->next(); } } else { state.styleMap().{{map_fill}}(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('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('CSSPropertyWebkitBackgroundComposite', 'Composite')}} {% macro apply_value_number(property_id, id_for_minus_one) %} {{declare_value_function(property_id)}} { {% set property = properties[property_id] %} if (!value->isPrimitiveValue()) return; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (primitiveValue->getValueID() == {{id_for_minus_one}}) {{set_value(property)}}(-1); else {{set_value(property)}}(primitiveValue->getValue<{{property.type_name}}>(CSSPrimitiveValue::CSS_NUMBER)); } {% endmacro %} {% macro apply_alignment(property_id, alignment_type) %} {% set property = properties[property_id] %} {{declare_initial_function(property_id)}} { state.style()->set{{alignment_type}}(RenderStyle::initial{{alignment_type}}()); state.style()->set{{alignment_type}}OverflowAlignment(RenderStyle::initial{{alignment_type}}OverflowAlignment()); } {{declare_inherit_function(property_id)}} { state.style()->set{{alignment_type}}(state.parentStyle()->{{property.getter}}()); state.style()->set{{alignment_type}}OverflowAlignment(state.parentStyle()->{{property.getter}}OverflowAlignment()); } {{declare_value_function(property_id)}} { CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (Pair* pairValue = primitiveValue->getPairValue()) { state.style()->set{{alignment_type}}(*pairValue->first()); state.style()->set{{alignment_type}}OverflowAlignment(*pairValue->second()); } else { state.style()->set{{alignment_type}}(*primitiveValue); } } {% endmacro %} {{apply_alignment('CSSPropertyAlignItems', 'AlignItems')}} {{apply_alignment('CSSPropertyAlignSelf', 'AlignSelf')}} } // namespace blink