1 {% from 'macros.tmpl' import license %}
3 This file is for property handlers which use the templating engine to
4 reduce (handwritten) code duplication.
6 The `properties' dict can be used to access a property's parameters in
7 jinja2 templates (i.e. setter, getter, initial, type_name)
10 #include "StyleBuilderFunctions.h"
12 #include "CSSValueKeywords.h"
13 #include "core/css/BasicShapeFunctions.h"
14 #include "core/css/CSSContentDistributionValue.h"
15 #include "core/css/CSSPrimitiveValueMappings.h"
16 #include "core/css/CSSValuePair.h"
17 #include "core/css/resolver/StyleResolverState.h"
19 {% macro declare_initial_function(property_id) %}
20 void StyleBuilderFunctions::applyInitial{{property_id}}(StyleResolverState& state)
22 {% macro declare_inherit_function(property_id) %}
23 void StyleBuilderFunctions::applyInherit{{property_id}}(StyleResolverState& state)
25 {% macro declare_value_function(property_id) %}
26 void StyleBuilderFunctions::applyValue{{property_id}}(StyleResolverState& state, CSSValue* value)
28 {% macro set_value(property) %}
30 state.style()->accessSVGStyle().{{property.setter}}
31 {%- elif property.font %}
32 state.fontBuilder().{{property.setter}}
34 state.style()->{{property.setter}}
37 {% macro convert_and_set_value(property) %}
38 {% if property.converter %}
39 {{set_value(property)}}(StyleBuilderConverter::{{property.converter}}(state, value));
41 {{set_value(property)}}(static_cast<{{property.type_name}}>(*toCSSPrimitiveValue(value)));
47 {% for property_id, property in properties.items() if property.should_declare_functions %}
48 {% set apply_type = property.apply_type %}
49 {% if not property.custom_initial %}
50 {{declare_initial_function(property_id)}}
53 {{set_value(property)}}(SVGComputedStyle::{{property.initial}}());
54 {% elif property.font %}
55 {{set_value(property)}}(FontBuilder::{{property.initial}}());
57 {{set_value(property)}}(ComputedStyle::{{property.initial}}());
62 {% if not property.custom_inherit %}
63 {{declare_inherit_function(property_id)}}
66 {{set_value(property)}}(state.parentStyle()->svgStyle().{{property.getter}}());
67 {% elif property.font %}
68 {{set_value(property)}}(state.parentFontDescription().{{property.getter}}());
70 {{set_value(property)}}(state.parentStyle()->{{property.getter}}());
75 {% if not property.custom_value %}
76 {{declare_value_function(property_id)}}
78 {{convert_and_set_value(property)}}
84 {% macro apply_animation(property_id, attribute, animation) %}
85 {% set vector = attribute|lower_first + "List()" %}
86 {{declare_initial_function(property_id)}}
88 CSS{{animation}}Data& data = state.style()->access{{animation}}s();
89 data.{{vector}}.clear();
90 data.{{vector}}.append(CSS{{animation}}Data::initial{{attribute}}());
93 {{declare_inherit_function(property_id)}}
95 const CSS{{animation}}Data* parentData = state.parentStyle()->{{animation|lower}}s();
97 applyInitial{{property_id}}(state);
99 state.style()->access{{animation}}s().{{vector}} = parentData->{{vector}};
102 {{declare_value_function(property_id)}}
104 CSS{{animation}}Data& data = state.style()->access{{animation}}s();
105 data.{{vector}}.clear();
106 for (auto& listValue : toCSSValueList(*value))
107 data.{{vector}}.append(CSSToStyleMap::mapAnimation{{attribute}}(listValue.get()));
110 {{apply_animation('CSSPropertyAnimationDelay', 'Delay', 'Animation')}}
111 {{apply_animation('CSSPropertyAnimationDirection', 'Direction', 'Animation')}}
112 {{apply_animation('CSSPropertyAnimationDuration', 'Duration', 'Animation')}}
113 {{apply_animation('CSSPropertyAnimationFillMode', 'FillMode', 'Animation')}}
114 {{apply_animation('CSSPropertyAnimationIterationCount', 'IterationCount', 'Animation')}}
115 {{apply_animation('CSSPropertyAnimationName', 'Name', 'Animation')}}
116 {{apply_animation('CSSPropertyAnimationPlayState', 'PlayState', 'Animation')}}
117 {{apply_animation('CSSPropertyAnimationTimingFunction', 'TimingFunction', 'Animation')}}
118 {{apply_animation('CSSPropertyTransitionDelay', 'Delay', 'Transition')}}
119 {{apply_animation('CSSPropertyTransitionDuration', 'Duration', 'Transition')}}
120 {{apply_animation('CSSPropertyTransitionProperty', 'Property', 'Transition')}}
121 {{apply_animation('CSSPropertyTransitionTimingFunction', 'TimingFunction', 'Transition')}}
123 {% macro apply_auto(property_id, auto_getter=none, auto_setter=none, auto_identity='CSSValueAuto') %}
124 {% set property = properties[property_id] %}
125 {% set auto_getter = auto_getter or 'hasAuto' + property.name_for_methods %}
126 {% set auto_setter = auto_setter or 'setHasAuto' + property.name_for_methods %}
127 {{declare_initial_function(property_id)}}
129 state.style()->{{auto_setter}}();
132 {{declare_inherit_function(property_id)}}
134 if (state.parentStyle()->{{auto_getter}}())
135 state.style()->{{auto_setter}}();
137 {{set_value(property)}}(state.parentStyle()->{{property.getter}}());
140 {{declare_value_function(property_id)}}
142 if (value->isPrimitiveValue() && toCSSPrimitiveValue(value)->getValueID() == {{auto_identity}})
143 state.style()->{{auto_setter}}();
145 {{convert_and_set_value(property)}}
148 {{apply_auto('CSSPropertyClip')}}
149 {{apply_auto('CSSPropertyOrphans')}}
150 {{apply_auto('CSSPropertyWebkitColumnCount')}}
151 {{apply_auto('CSSPropertyWebkitColumnGap', auto_getter='hasNormalColumnGap', auto_setter='setHasNormalColumnGap', auto_identity='CSSValueNormal')}}
152 {{apply_auto('CSSPropertyWebkitColumnWidth')}}
153 {{apply_auto('CSSPropertyWidows')}}
154 {{apply_auto('CSSPropertyZIndex')}}
156 static bool lengthMatchesAllSides(const LengthBox& lengthBox, const Length& length)
158 return (lengthBox.left() == length
159 && lengthBox.right() == length
160 && lengthBox.top() == length
161 && lengthBox.bottom() == length);
164 static bool borderImageLengthMatchesAllSides(const BorderImageLengthBox& borderImageLengthBox, const BorderImageLength& borderImageLength)
166 return (borderImageLengthBox.left() == borderImageLength
167 && borderImageLengthBox.right() == borderImageLength
168 && borderImageLengthBox.top() == borderImageLength
169 && borderImageLengthBox.bottom() == borderImageLength);
172 {% macro apply_border_image_modifier(property_id, modifier_type) %}
173 {% set is_mask_box = 'MaskBox' in property_id %}
174 {% set getter = 'maskBoxImage' if is_mask_box else 'borderImage' %}
175 {% set setter = 'setMaskBoxImage' if is_mask_box else 'setBorderImage' %}
176 {{ declare_initial_function(property_id) }}
178 const NinePieceImage& currentImage = state.style()->{{getter}}();
179 {# Check for equality in case we can bail out before creating a new NinePieceImage. #}
180 {% if modifier_type == 'Outset' %}
181 if (borderImageLengthMatchesAllSides(currentImage.outset(), BorderImageLength(Length(0, Fixed))))
183 {% elif modifier_type == 'Repeat' %}
184 if (currentImage.horizontalRule() == StretchImageRule && currentImage.verticalRule() == StretchImageRule)
186 {% elif modifier_type == 'Slice' and is_mask_box %}
187 // Masks have a different initial value for slices. Preserve the value of 0 for backwards compatibility.
188 if (currentImage.fill() == true && lengthMatchesAllSides(currentImage.imageSlices(), Length(0, Fixed)))
190 {% elif modifier_type == 'Slice' and not is_mask_box %}
191 if (currentImage.fill() == false && lengthMatchesAllSides(currentImage.imageSlices(), Length(100, Percent)))
193 {% elif modifier_type == 'Width' and is_mask_box %}
194 // Masks have a different initial value for widths. Preserve the value of 'auto' for backwards compatibility.
195 if (borderImageLengthMatchesAllSides(currentImage.borderSlices(), BorderImageLength(Length(Auto))))
197 {% elif modifier_type == 'Width' and not is_mask_box %}
198 if (borderImageLengthMatchesAllSides(currentImage.borderSlices(), BorderImageLength(1.0)))
202 NinePieceImage image(currentImage);
203 {% if modifier_type == 'Outset' %}
204 image.setOutset(Length(0, Fixed));
205 {% elif modifier_type == 'Repeat' %}
206 image.setHorizontalRule(StretchImageRule);
207 image.setVerticalRule(StretchImageRule);
208 {% elif modifier_type == 'Slice' and is_mask_box %}
209 image.setImageSlices(LengthBox({{ (['Length(0, Fixed)']*4) | join(', ') }}));
211 {% elif modifier_type == 'Slice' and not is_mask_box %}
212 image.setImageSlices(LengthBox({{ (['Length(100, Percent)']*4) | join(', ') }}));
213 image.setFill(false);
214 {% elif modifier_type == 'Width' %}
215 image.setBorderSlices({{ 'Length(Auto)' if is_mask_box else '1.0' }});
217 state.style()->{{setter}}(image);
220 {{declare_inherit_function(property_id)}}
222 NinePieceImage image(state.style()->{{getter}}());
223 {% if modifier_type == 'Outset' %}
224 image.copyOutsetFrom(state.parentStyle()->{{getter}}());
225 {% elif modifier_type == 'Repeat' %}
226 image.copyRepeatFrom(state.parentStyle()->{{getter}}());
227 {% elif modifier_type == 'Slice' %}
228 image.copyImageSlicesFrom(state.parentStyle()->{{getter}}());
229 {% elif modifier_type == 'Width' %}
230 image.copyBorderSlicesFrom(state.parentStyle()->{{getter}}());
232 state.style()->{{setter}}(image);
235 {{declare_value_function(property_id)}}
237 NinePieceImage image(state.style()->{{getter}}());
238 {% if modifier_type == 'Outset' %}
239 image.setOutset(CSSToStyleMap::mapNinePieceImageQuad(state, value));
240 {% elif modifier_type == 'Repeat' %}
241 CSSToStyleMap::mapNinePieceImageRepeat(state, value, image);
242 {% elif modifier_type == 'Slice' %}
243 CSSToStyleMap::mapNinePieceImageSlice(state, value, image);
244 {% elif modifier_type == 'Width' %}
245 image.setBorderSlices(CSSToStyleMap::mapNinePieceImageQuad(state, value));
247 state.style()->{{setter}}(image);
250 {{apply_border_image_modifier('CSSPropertyBorderImageOutset', 'Outset')}}
251 {{apply_border_image_modifier('CSSPropertyBorderImageRepeat', 'Repeat')}}
252 {{apply_border_image_modifier('CSSPropertyBorderImageSlice', 'Slice')}}
253 {{apply_border_image_modifier('CSSPropertyBorderImageWidth', 'Width')}}
254 {{apply_border_image_modifier('CSSPropertyWebkitMaskBoxImageOutset', 'Outset')}}
255 {{apply_border_image_modifier('CSSPropertyWebkitMaskBoxImageRepeat', 'Repeat')}}
256 {{apply_border_image_modifier('CSSPropertyWebkitMaskBoxImageSlice', 'Slice')}}
257 {{apply_border_image_modifier('CSSPropertyWebkitMaskBoxImageWidth', 'Width')}}
259 {% macro apply_value_border_image_source(property_id) %}
260 {{declare_value_function(property_id)}}
262 {% set property = properties[property_id] %}
263 {{set_value(property)}}(state.styleImage({{property_id}}, value));
266 {{apply_value_border_image_source('CSSPropertyBorderImageSource')}}
267 {{apply_value_border_image_source('CSSPropertyWebkitMaskBoxImageSource')}}
269 {% macro apply_color(property_id, initial_color='StyleColor::currentColor') %}
270 {% set property = properties[property_id] %}
271 {% set visited_link_setter = 'setVisitedLink' + property.name_for_methods %}
272 {{declare_initial_function(property_id)}}
274 StyleColor color = {{initial_color}}();
275 if (state.applyPropertyToRegularStyle())
276 {{set_value(property)}}(color);
277 if (state.applyPropertyToVisitedLinkStyle())
278 state.style()->{{visited_link_setter}}(color);
281 {{declare_inherit_function(property_id)}}
283 // Visited link style can never explicitly inherit from parent visited link style so no separate getters are needed.
284 StyleColor color = state.parentStyle()->{{property.getter}}();
285 if (state.applyPropertyToRegularStyle())
286 {{set_value(property)}}(color);
287 if (state.applyPropertyToVisitedLinkStyle())
288 state.style()->{{visited_link_setter}}(color);
291 {{declare_value_function(property_id)}}
293 if (state.applyPropertyToRegularStyle())
294 {{set_value(property)}}(StyleBuilderConverter::convertStyleColor(state, value));
295 if (state.applyPropertyToVisitedLinkStyle())
296 state.style()->{{visited_link_setter}}(StyleBuilderConverter::convertStyleColor(state, value, true));
299 {{apply_color('CSSPropertyBackgroundColor', initial_color='ComputedStyle::initialBackgroundColor') }}
300 {{apply_color('CSSPropertyBorderBottomColor')}}
301 {{apply_color('CSSPropertyBorderLeftColor')}}
302 {{apply_color('CSSPropertyBorderRightColor')}}
303 {{apply_color('CSSPropertyBorderTopColor')}}
304 {{apply_color('CSSPropertyOutlineColor')}}
305 {{apply_color('CSSPropertyTextDecorationColor')}}
306 {{apply_color('CSSPropertyWebkitColumnRuleColor')}}
307 {{apply_color('CSSPropertyWebkitTextEmphasisColor')}}
308 {{apply_color('CSSPropertyWebkitTextFillColor')}}
309 {{apply_color('CSSPropertyWebkitTextStrokeColor')}}
311 {% macro apply_counter(property_id, action) %}
312 {% set property = properties[property_id] %}
313 {{declare_initial_function(property_id)}} {
314 state.style()->clear{{action}}Directives();
317 {{declare_inherit_function(property_id)}}
319 const CounterDirectiveMap* parentMap = state.parentStyle()->counterDirectives();
323 CounterDirectiveMap& map = state.style()->accessCounterDirectives();
324 ASSERT(!parentMap->isEmpty());
326 typedef CounterDirectiveMap::const_iterator Iterator;
327 Iterator end = parentMap->end();
328 for (Iterator it = parentMap->begin(); it != end; ++it) {
329 CounterDirectives& directives = map.add(it->key, CounterDirectives()).storedValue->value;
330 directives.inherit{{action}}(it->value);
334 {{declare_value_function(property_id)}}
336 state.style()->clear{{action}}Directives();
338 if (!value->isValueList()) {
339 ASSERT(value->isPrimitiveValue() && toCSSPrimitiveValue(value)->getValueID() == CSSValueNone);
343 CounterDirectiveMap& map = state.style()->accessCounterDirectives();
345 CSSValueList* list = toCSSValueList(value);
347 int length = list ? list->length() : 0;
348 for (int i = 0; i < length; ++i) {
349 const CSSValuePair* pair = toCSSValuePair(list->item(i));
350 AtomicString identifier(toCSSPrimitiveValue(pair->first()).getStringValue());
351 int value = toCSSPrimitiveValue(pair->second()).getIntValue();
352 CounterDirectives& directives = map.add(identifier, CounterDirectives()).storedValue->value;
353 {% if action == 'Reset' %}
354 directives.setResetValue(value);
356 directives.addIncrementValue(value);
359 ASSERT(!map.isEmpty());
362 {{apply_counter('CSSPropertyCounterIncrement', 'Increment')}}
363 {{apply_counter('CSSPropertyCounterReset', 'Reset')}}
365 {% macro apply_fill_layer(property_id, fill_type) %}
366 {% set layer_type = 'Background' if 'Background' in property_id else 'Mask' %}
367 {% set fill_layer_type = layer_type + 'FillLayer' %}
368 {% set access_layers = 'access' + layer_type + 'Layers' %}
369 {% set map_fill = 'mapFill' + fill_type %}
370 {{declare_initial_function(property_id)}}
372 FillLayer* currChild = &state.style()->{{access_layers}}();
373 currChild->set{{fill_type}}(FillLayer::initialFill{{fill_type}}({{fill_layer_type}}));
374 for (currChild = currChild->next(); currChild; currChild = currChild->next())
375 currChild->clear{{fill_type}}();
378 {{declare_inherit_function(property_id)}}
380 FillLayer* currChild = &state.style()->{{access_layers}}();
381 FillLayer* prevChild = 0;
382 const FillLayer* currParent = &state.parentStyle()->{{layer_type|lower}}Layers();
383 while (currParent && currParent->is{{fill_type}}Set()) {
385 currChild = prevChild->ensureNext();
386 currChild->set{{fill_type}}(currParent->{{fill_type|lower_first}}());
387 prevChild = currChild;
388 currChild = prevChild->next();
389 currParent = currParent->next();
393 /* Reset any remaining layers to not have the property set. */
394 currChild->clear{{fill_type}}();
395 currChild = currChild->next();
399 {{declare_value_function(property_id)}}
401 FillLayer* currChild = &state.style()->{{access_layers}}();
402 FillLayer* prevChild = 0;
403 if (value->isValueList() && !value->isImageSetValue()) {
404 /* Walk each value and put it into a layer, creating new layers as needed. */
405 CSSValueList* valueList = toCSSValueList(value);
406 for (unsigned int i = 0; i < valueList->length(); i++) {
408 currChild = prevChild->ensureNext();
409 CSSToStyleMap::{{map_fill}}(state, currChild, valueList->item(i));
410 prevChild = currChild;
411 currChild = currChild->next();
414 CSSToStyleMap::{{map_fill}}(state, currChild, value);
415 currChild = currChild->next();
418 /* Reset all remaining layers to not have the property set. */
419 currChild->clear{{fill_type}}();
420 currChild = currChild->next();
424 {{apply_fill_layer('CSSPropertyBackgroundAttachment', 'Attachment')}}
425 {{apply_fill_layer('CSSPropertyBackgroundBlendMode', 'BlendMode')}}
426 {{apply_fill_layer('CSSPropertyBackgroundClip', 'Clip')}}
427 {{apply_fill_layer('CSSPropertyBackgroundImage', 'Image')}}
428 {{apply_fill_layer('CSSPropertyBackgroundOrigin', 'Origin')}}
429 {{apply_fill_layer('CSSPropertyBackgroundPositionX', 'XPosition')}}
430 {{apply_fill_layer('CSSPropertyBackgroundPositionY', 'YPosition')}}
431 {{apply_fill_layer('CSSPropertyBackgroundRepeatX', 'RepeatX')}}
432 {{apply_fill_layer('CSSPropertyBackgroundRepeatY', 'RepeatY')}}
433 {{apply_fill_layer('CSSPropertyBackgroundSize', 'Size')}}
434 {{apply_fill_layer('CSSPropertyMaskSourceType', 'MaskSourceType')}}
435 {{apply_fill_layer('CSSPropertyWebkitBackgroundComposite', 'Composite')}}
436 {{apply_fill_layer('CSSPropertyWebkitMaskClip', 'Clip')}}
437 {{apply_fill_layer('CSSPropertyWebkitMaskComposite', 'Composite')}}
438 {{apply_fill_layer('CSSPropertyWebkitMaskImage', 'Image')}}
439 {{apply_fill_layer('CSSPropertyWebkitMaskOrigin', 'Origin')}}
440 {{apply_fill_layer('CSSPropertyWebkitMaskPositionX', 'XPosition')}}
441 {{apply_fill_layer('CSSPropertyWebkitMaskPositionY', 'YPosition')}}
442 {{apply_fill_layer('CSSPropertyWebkitMaskRepeatX', 'RepeatX')}}
443 {{apply_fill_layer('CSSPropertyWebkitMaskRepeatY', 'RepeatY')}}
444 {{apply_fill_layer('CSSPropertyWebkitMaskSize', 'Size')}}
446 {% macro apply_grid_template(property_id, type) %}
447 {{declare_initial_function(property_id)}}
449 state.style()->setGridTemplate{{type}}s(ComputedStyle::initialGridTemplate{{type}}s());
450 state.style()->setNamedGrid{{type}}Lines(ComputedStyle::initialNamedGrid{{type}}Lines());
451 state.style()->setOrderedNamedGrid{{type}}Lines(ComputedStyle::initialOrderedNamedGrid{{type}}Lines());
454 {{declare_inherit_function(property_id)}}
456 state.style()->setGridTemplate{{type}}s(state.parentStyle()->gridTemplate{{type}}s());
457 state.style()->setNamedGrid{{type}}Lines(state.parentStyle()->namedGrid{{type}}Lines());
458 state.style()->setOrderedNamedGrid{{type}}Lines(state.parentStyle()->orderedNamedGrid{{type}}Lines());
461 {{declare_value_function(property_id)}}
463 Vector<GridTrackSize> trackSizes;
464 NamedGridLinesMap namedGridLines;
465 OrderedNamedGridLines orderedNamedGridLines;
466 StyleBuilderConverter::convertGridTrackList(value, trackSizes, namedGridLines, orderedNamedGridLines, state);
467 const NamedGridAreaMap& namedGridAreas = state.style()->namedGridArea();
468 if (!namedGridAreas.isEmpty())
469 StyleBuilderConverter::createImplicitNamedGridLinesFromGridArea(namedGridAreas, namedGridLines, For{{type}}s);
470 state.style()->setGridTemplate{{type}}s(trackSizes);
471 state.style()->setNamedGrid{{type}}Lines(namedGridLines);
472 state.style()->setOrderedNamedGrid{{type}}Lines(orderedNamedGridLines);
475 {{apply_grid_template('CSSPropertyGridTemplateColumns', 'Column')}}
476 {{apply_grid_template('CSSPropertyGridTemplateRows', 'Row')}}
478 {% macro apply_svg_paint(property_id, paint_type) %}
479 {% set property = properties[property_id] %}
480 {{declare_initial_function(property_id)}}
482 {{set_value(property)}}(
483 SVGComputedStyle::initial{{paint_type}}Type(),
484 SVGComputedStyle::initial{{paint_type}}Color(),
485 SVGComputedStyle::initial{{paint_type}}Uri(),
486 state.applyPropertyToRegularStyle(),
487 state.applyPropertyToVisitedLinkStyle());
490 {{declare_inherit_function(property_id)}}
492 const SVGComputedStyle& svgParentStyle = state.parentStyle()->svgStyle();
493 {{set_value(property)}}(
494 svgParentStyle.{{paint_type|lower_first}}Type(),
495 svgParentStyle.{{paint_type|lower_first}}Color(),
496 svgParentStyle.{{paint_type|lower_first}}Uri(),
497 state.applyPropertyToRegularStyle(),
498 state.applyPropertyToVisitedLinkStyle());
501 {{declare_value_function(property_id)}}
504 if (value->isValueList()) {
505 CSSValueList* list = toCSSValueList(value);
506 ASSERT(list->length() == 2);
507 url = toCSSPrimitiveValue(list->item(0))->getStringValue();
508 value = list->item(1);
511 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
513 SVGPaintType paintType = SVG_PAINTTYPE_RGBCOLOR;
514 if (primitiveValue->getValueID() == CSSValueNone) {
515 paintType = url.isEmpty() ? SVG_PAINTTYPE_NONE : SVG_PAINTTYPE_URI_NONE;
516 } else if (primitiveValue->isURI()) {
517 paintType = SVG_PAINTTYPE_URI;
518 url = primitiveValue->getStringValue();
519 } else if (primitiveValue->getValueID() == CSSValueCurrentcolor) {
520 color = state.style()->color();
521 paintType = url.isEmpty() ? SVG_PAINTTYPE_CURRENTCOLOR : SVG_PAINTTYPE_URI_CURRENTCOLOR;
523 color = StyleBuilderConverter::convertColor(state, primitiveValue);
524 paintType = url.isEmpty() ? SVG_PAINTTYPE_RGBCOLOR : SVG_PAINTTYPE_URI_RGBCOLOR;
526 {{set_value(property)}}(paintType, color, url,
527 state.applyPropertyToRegularStyle(),
528 state.applyPropertyToVisitedLinkStyle());
531 {{apply_svg_paint('CSSPropertyFill', 'FillPaint')}}
532 {{apply_svg_paint('CSSPropertyStroke', 'StrokePaint')}}