blob: af9d877180588e5a2a6d850da8db5fcf4d04f951 [file] [log] [blame]
{% 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('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_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<GridTrackSize> 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