{% from "macros.tmpl" import lower_first -%} {# 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" #include "core/platform/animation/CSSAnimationDataList.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 %} // FIXME: This is duplicated in StyleBuilder.cpp.tmpl, but we'll move the // function definitions there over to here later. {%- macro set_value(property) %} {%- if property.svg -%} state.style()->accessSVGStyle()->{{property.setter}} {%- else -%} state.style()->{{property.setter}} {%- endif -%} {%- endmacro %} namespace WebCore { {%- macro apply_animation(property_id, attribute, animation) %} {{ declare_initial_function(property_id) }} { CSSAnimationDataList* list = state.style()->access{{animation}}(); if (list->isEmpty()) list->append(CSSAnimationData::create()); list->animation(0)->set{{attribute}}(CSSAnimationData::initialAnimation{{attribute}}()); {%- if property_id == "CSSPropertyWebkitTransitionProperty" %} list->animation(0)->setAnimationMode(CSSAnimationData::AnimateAll); {%- endif %} for (size_t i = 1; i < list->size(); ++i) list->animation(i)->clear{{attribute}}(); } {{ declare_inherit_function(property_id) }} { CSSAnimationDataList* list = state.style()->access{{animation}}(); const CSSAnimationDataList* parentList = state.parentStyle()->{{animation|lower}}(); size_t i = 0, parentSize = parentList ? parentList->size() : 0; for ( ; i < parentSize && parentList->animation(i)->is{{attribute}}Set(); ++i) { ASSERT(list->size() >= i); if (list->size() == i) list->append(CSSAnimationData::create()); list->animation(i)->set{{attribute}}(parentList->animation(i)->{{lower_first(attribute)}}()); {%- if property_id == "CSSPropertyWebkitTransitionProperty" %} list->animation(i)->setAnimationMode(parentList->animation(i)->animationMode()); {%- endif %} } // Reset any remaining animations to not have the property set. for ( ; i < list->size(); ++i) list->animation(i)->clear{{attribute}}(); } {{ declare_value_function(property_id) }} { CSSAnimationDataList* list = state.style()->access{{animation}}(); size_t childIndex = 0; if (value->isValueList()) { // Walk each value and put it into an animation, creating new animations as needed. for (CSSValueListIterator i = value; i.hasMore(); i.advance()) { ASSERT(list->size() >= childIndex); if (list->size() == childIndex) list->append(CSSAnimationData::create()); state.styleMap().mapAnimation{{attribute}}(list->animation(childIndex), i.value()); ++childIndex; } } else { if (list->isEmpty()) list->append(CSSAnimationData::create()); state.styleMap().mapAnimation{{attribute}}(list->animation(childIndex), value); childIndex = 1; } for ( ; childIndex < list->size(); ++childIndex) { // Reset all remaining animations to not have the property set. list->animation(childIndex)->clear{{attribute}}(); } } {%- endmacro %} {{ apply_animation("CSSPropertyWebkitAnimationDelay", "Delay", "Animations") }} {{ apply_animation("CSSPropertyWebkitAnimationDirection", "Direction", "Animations") }} {{ apply_animation("CSSPropertyWebkitAnimationDuration", "Duration", "Animations") }} {{ apply_animation("CSSPropertyWebkitAnimationFillMode", "FillMode", "Animations") }} {{ apply_animation("CSSPropertyWebkitAnimationIterationCount", "IterationCount", "Animations") }} {{ apply_animation("CSSPropertyWebkitAnimationName", "Name", "Animations") }} {{ apply_animation("CSSPropertyWebkitAnimationPlayState", "PlayState", "Animations") }} {{ apply_animation("CSSPropertyWebkitAnimationTimingFunction", "TimingFunction", "Animations") }} {{ apply_animation("CSSPropertyWebkitTransitionDelay", "Delay", "Transitions") }} {{ apply_animation("CSSPropertyWebkitTransitionDuration", "Duration", "Transitions") }} {{ apply_animation("CSSPropertyWebkitTransitionProperty", "Property", "Transitions") }} {{ apply_animation("CSSPropertyWebkitTransitionTimingFunction", "TimingFunction", "Transitions") }} {%- macro apply_auto(property_id, auto_getter=none, auto_setter=none, auto_identity="CSSValueAuto", compute_length=false) %} {%- set property = properties[property_id] %} {%- set auto_getter = auto_getter or "hasAuto" + property.camel_case_name %} {%- set auto_setter = auto_setter or "setHasAuto" + property.camel_case_name %} {{ 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 {%- if compute_length %} {{ set_value(property) }}(primitiveValue->computeLength<{{property.type_name}}>(state.cssToLengthConversionData())); {%- else %} {{ set_value(property) }}(*primitiveValue); {%- endif %} } {%- endmacro %} {{ apply_auto("CSSPropertyOrphans") }} {{ apply_auto("CSSPropertyWebkitColumnCount") }} {{ apply_auto("CSSPropertyWebkitColumnGap", auto_getter="hasNormalColumnGap", auto_setter="setHasNormalColumnGap", auto_identity="CSSValueNormal", compute_length=true) }} {{ apply_auto("CSSPropertyWebkitColumnWidth", compute_length=true) }} {{ apply_auto("CSSPropertyWidows") }} {{ apply_auto("CSSPropertyZIndex") }} {%- 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) }} { NinePieceImage image(state.style()->{{getter}}()); {%- 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 %} // Masks have a different initial value for slices. Preserve the value of 0 for backwards compatibility. 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" %} // Masks have a different initial value for widths. Preserve the value of 'auto' for backwards compatibility. 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(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()->{{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, default_getter="color", initial_color=none, inherit_color=false) %} {%- set property = properties[property_id] %} {%- set visited_link_setter = "setVisitedLink" + property.camel_case_name %} {{ declare_initial_function(property_id) }} { Color color = {{ initial_color or "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. Color color = state.parentStyle()->{{property.getter}}(); if (!color.isValid()) color = state.parentStyle()->{{default_getter}}(); if (state.applyPropertyToRegularStyle()) {{ set_value(property) }}(color); if (state.applyPropertyToVisitedLinkStyle()) state.style()->{{visited_link_setter}}(color); } {{ declare_value_function(property_id) }} { if (!value->isPrimitiveValue()) return; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); {%- if inherit_color %} if (primitiveValue->getValueID() == CSSValueCurrentcolor) { applyInherit{{property_id}}(state); return; } {%- endif %} if (state.applyPropertyToRegularStyle()) {{ set_value(property) }}(state.document().textLinkColors().colorFromPrimitiveValue(primitiveValue, state.style()->color())); if (state.applyPropertyToVisitedLinkStyle()) state.style()->{{visited_link_setter}}(state.document().textLinkColors().colorFromPrimitiveValue(primitiveValue, state.style()->color(), state.element()->isLink() /* forVisitedLink */)); } {%- endmacro %} {{ apply_color("CSSPropertyBackgroundColor", default_getter="invalidColor") }} {{ apply_color("CSSPropertyBorderBottomColor") }} {{ apply_color("CSSPropertyBorderLeftColor") }} {{ apply_color("CSSPropertyBorderRightColor") }} {{ apply_color("CSSPropertyBorderTopColor") }} {{ apply_color("CSSPropertyColor", inherit_color=true, default_getter="invalidColor", initial_color="RenderStyle::initialColor") }} {{ apply_color("CSSPropertyOutlineColor") }} {{ apply_color("CSSPropertyTextDecorationColor") }} {{ apply_color("CSSPropertyWebkitColumnRuleColor") }} {{ 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()).iterator->value; directives.inherit{{action}}(it->value); } } {{ declare_value_function(property_id) }} { if (!value->isValueList()) return; CSSValueList* list = toCSSValueList(value); 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}}(); int length = list ? list->length() : 0; for (int i = 0; i < length; ++i) { CSSValue* currValue = list->itemWithoutBoundsCheck(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()).iterator->value; {%- if action == "Reset" %} directives.setResetValue(value); {%- else %} directives.addIncrementValue(value); {%- endif %} } } {%- 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) { /* Need to make a new layer.*/ currChild = new FillLayer({{fill_layer_type}}); prevChild->setNext(currChild); } currChild->set{{fill_type}}(currParent->{{lower_first(fill_type)}}()); 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) { /* Need to make a new layer to hold this value */ currChild = new FillLayer({{fill_layer_type}}); prevChild->setNext(currChild); } state.styleMap().{{map_fill}}({{property_id}}, currChild, valueList->itemWithoutBoundsCheck(i)); prevChild = currChild; currChild = currChild->next(); } } else { state.styleMap().{{map_fill}}({{property_id}}, 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("CSSPropertyWebkitBackgroundComposite", "Composite") }} {{ 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_font(property_id, name_for_methods, initial, type_name) %} {#- We specify the getters/setters here since they are on FontDescription and not RenderStyle #} {%- set getter = lower_first(name_for_methods) %} {%- set setter = "set" + name_for_methods %} {{ declare_initial_function(property_id) }} { state.fontBuilder().{{setter}}({{initial}}); } {{ declare_inherit_function(property_id) }} { state.fontBuilder().{{setter}}(state.parentFontDescription().{{getter}}()); } {{ declare_value_function(property_id) }} { if (!value->isPrimitiveValue()) return; CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); state.fontBuilder().{{setter}}(static_cast<{{type_name}}>(*primitiveValue)); } {%- endmacro %} {{ apply_font("CSSPropertyFontStyle", "Italic", "FontItalicOff", "FontItalic") }} {{ apply_font("CSSPropertyFontVariant", "SmallCaps", "FontSmallCapsOff", "FontSmallCaps") }} {{ apply_font("CSSPropertyTextRendering", "TextRenderingMode", "AutoTextRendering", "TextRenderingMode") }} {{ apply_font("CSSPropertyFontKerning", "Kerning", "FontDescription::AutoKerning", "FontDescription::Kerning") }} {{ apply_font("CSSPropertyWebkitFontSmoothing", "FontSmoothing", "AutoSmoothing", "FontSmoothingMode") }} {%- 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 %} {{ apply_value_number("CSSPropertyInternalMarqueeRepetition", "CSSValueInfinite") }} {%- macro apply_value_shape(property_id) %} {{ declare_value_function(property_id) }} { {%- set property = properties[property_id] %} if (value->isPrimitiveValue()) { CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); if (primitiveValue->getValueID() == CSSValueAuto) {{ set_value(property) }}(0); else if (primitiveValue->getValueID() == CSSValueMarginBox || primitiveValue->getValueID() == CSSValueBorderBox || primitiveValue->getValueID() == CSSValuePaddingBox || primitiveValue->getValueID() == CSSValueContentBox) {{ set_value(property) }}(ShapeValue::createLayoutBoxValue(LayoutBox(*primitiveValue))); else if (primitiveValue->getValueID() == CSSValueOutsideShape) {{ set_value(property) }}(ShapeValue::createOutsideValue()); else if (primitiveValue->isShape()) { {{ set_value(property) }}(ShapeValue::createShapeValue(basicShapeForValue(state, primitiveValue->getShapeValue()))); } } else if (value->isImageValue()) { {{ set_value(property) }}(ShapeValue::createImageValue(state.styleImage({{property_id}}, value))); } } {%- endmacro %} {{ apply_value_shape("CSSPropertyShapeInside") }} {{ apply_value_shape("CSSPropertyShapeOutside") }} } // namespace WebCore