chromium/third_party/blink/renderer/build/scripts/core/css/properties/templates/style_builder_functions.tmpl

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

{% macro apply_initial(property) %}
  {% set class_name = property.name.to_upper_camel_case() %}
  {% if property.style_builder_declare %}
    {% if property.style_builder_generate_initial %}
void {{class_name}}::ApplyInitial(StyleResolverState& state) const {
{{(caller(property) ~ '}')}}
    {% endif %}
  {% endif %}
{% endmacro %}

{% macro apply_inherit(property) %}
  {% set class_name = property.name.to_upper_camel_case() %}
  {% if property.style_builder_declare %}
    {% if property.style_builder_generate_inherit %}
void {{class_name}}::ApplyInherit(StyleResolverState& state) const {
      {% if property.affected_by_zoom %}
  if (state.GetDocument().StandardizedBrowserZoomEnabled()) {
    if (ApplyParentValueIfZoomChanged(state)) {
      return;
    }
  }
      {% endif %}
{{(caller(property) ~ '}')}}
    {% endif %}
  {% endif %}
{% endmacro %}

{% macro apply_value(property) %}
  {% set class_name = property.name.to_upper_camel_case() %}
  {% if property.style_builder_declare %}
    {% if property.style_builder_generate_value %}
void {{class_name}}::ApplyValue(StyleResolverState& state, const CSSValue& value, ValueMode) const {
{% if property.anchor_mode %}
  blink::AnchorScope anchor_scope(
      blink::AnchorScope::Mode::{{property.anchor_mode.to_enum_value()}},
      state.CssToLengthConversionData().GetAnchorEvaluator());
{% endif %}
{{(caller(property) ~ '}')}}
    {% endif %}
  {% endif %}
{% endmacro %}

{% macro style_access(property) %}
state.StyleBuilder().
{%- endmacro %}

{% macro set_value(property) %}
  {%- if property.font %}
state.GetFontBuilder().{{property.setter}}
  {%- else %}
{{style_access(property)}}{{property.setter}}
  {%- endif %}
{% endmacro %}

{% macro convert_and_set_value(property) %}
  {% if property.converter == 'CSSPrimitiveValue' %}
{{set_value(property)}}(To<CSSPrimitiveValue>(value).ConvertTo<{{property.type_name}}>(state.CssToLengthConversionData()));
  {%- elif property.converter == 'CSSIdentifierValue' %}
{{set_value(property)}}(To<CSSIdentifierValue>(value).ConvertTo<blink::{{property.type_name}}>());
  {%- elif property.converter %}
{{set_value(property)}}(StyleBuilderConverter::{{property.converter}}(state, value));
  {%- endif %}
{% endmacro %}

{% macro style_builder_functions(property) %}
  {% if not property.style_builder_template %}
    {% call(property) apply_initial(property) %}
      {% if property.font %}
  {{set_value(property)}}(FontBuilder::{{property.initial}}());
      {% else %}
  {{set_value(property)}}(ComputedStyleInitialValues::{{property.initial}}());
      {% endif %}
      {% if property.independent %}
  state.StyleBuilder().{{property.is_inherited_setter}}(false);
      {% endif %}
    {% endcall %}
    {% call(property) apply_inherit(property) %}
      {% if property.font %}
  {{set_value(property)}}(state.ParentFontDescription().{{property.getter}}());
      {% else %}
  {{set_value(property)}}(state.ParentStyle()->{{property.getter}}());
      {% endif %}
      {% if property.independent %}
  state.StyleBuilder().{{property.is_inherited_setter}}(true);
      {% endif %}
    {% endcall %}
    {% call(property) apply_value(property) %}
  {{convert_and_set_value(property)}}
        {% if property.independent %}
  state.StyleBuilder().{{property.is_inherited_setter}}(false);
        {% endif %}
    {% endcall %}
  {% elif property.style_builder_template == 'empty' %}
    {% call(property) apply_initial(property) %}
  // Intentionally empty.
    {% endcall %}
    {% call(property) apply_inherit(property) %}
  // Intentionally empty.
    {% endcall %}
    {% call(property) apply_value(property) %}
  // Intentionally empty.
    {% endcall %}
  {% elif property.style_builder_template == "auto" %}
    {% set auto_getter = property.style_builder_template_args['auto_getter'] or
                       'HasAuto' + property.name_for_methods %}
    {% set auto_setter = property.style_builder_template_args['auto_setter'] or
                       'SetHasAuto' + property.name_for_methods %}
    {% call(property) apply_initial(property) %}
  {{style_access(property)}}{{auto_setter}}();
    {% endcall %}
    {% call(property) apply_inherit(property) %}
  if (state.ParentStyle()->{{auto_getter}}())
    {{style_access(property)}}{{auto_setter}}();
  else
    {{set_value(property)}}(state.ParentStyle()->{{property.getter}}());
    {% endcall %}
    {% call(property) apply_value(property) %}
  auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
  if (identifier_value && identifier_value->GetValueID() == CSSValueID::kAuto)
    {{style_access(property)}}{{auto_setter}}();
  else
    {{convert_and_set_value(property)}}
    {% endcall %}
  {% elif property.style_builder_template in ['border_image', 'mask_box'] %}
    {% set is_mask_box = property.style_builder_template == 'mask_box' %}
    {% set modifier_type = property.style_builder_template_args['modifier_type'] %}
    {% set getter = 'MaskBoxImage' if is_mask_box else 'BorderImage' %}
    {% set setter = 'Set' + getter %}
    {% call(property) apply_initial(property) %}
  const NinePieceImage& current_image = state.StyleBuilder().{{getter}}();
  {# Check for equality in case we can bail out before creating a new NinePieceImage. #}
      {% if modifier_type == 'Outset' %}
  if (style_building_utils::BorderImageLengthMatchesAllSides(current_image.Outset(),
                                       BorderImageLength(0)))
    return;
      {% elif modifier_type == 'Repeat' %}
  if (current_image.HorizontalRule() == kStretchImageRule &&
      current_image.VerticalRule() == kStretchImageRule)
    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 (current_image.Fill() == true &&
      style_building_utils::LengthMatchesAllSides(current_image.ImageSlices(), Length::Fixed(0)))
    return;
      {% elif modifier_type == 'Slice' and not is_mask_box %}
  if (current_image.Fill() == false &&
      style_building_utils::LengthMatchesAllSides(current_image.ImageSlices(), Length::Percent(100)))
    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 (style_building_utils::BorderImageLengthMatchesAllSides(current_image.BorderSlices(),
                                       BorderImageLength(Length::Auto())))
    return;
      {% elif modifier_type == 'Width' and not is_mask_box %}
  if (style_building_utils::BorderImageLengthMatchesAllSides(current_image.BorderSlices(),
                                       BorderImageLength(1.0)))
    return;
      {% endif %}
  NinePieceImage image(current_image);
      {% if modifier_type == 'Outset' %}
  image.SetOutset(0);
      {% elif modifier_type == 'Repeat' %}
  image.SetHorizontalRule(kStretchImageRule);
  image.SetVerticalRule(kStretchImageRule);
      {% elif modifier_type == 'Slice' and is_mask_box %}
  image.SetImageSlices(LengthBox({{ (['Length::Fixed(0)']*4) | join(', ') }}));
  image.SetFill(true);
      {% elif modifier_type == 'Slice' and not is_mask_box %}
  image.SetImageSlices(LengthBox({{ (['Length::Percent(100)']*4) | join(', ') }}));
  image.SetFill(false);
      {% elif modifier_type == 'Width' %}
  image.SetBorderSlices({{ 'Length::Auto()' if is_mask_box else '1.0' }});
      {% endif %}
  state.StyleBuilder().{{setter}}(image);
    {% endcall %}
    {% call(property) apply_inherit(property) %}
  NinePieceImage image(state.StyleBuilder().{{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.StyleBuilder().{{setter}}(image);
    {% endcall %}
    {% call(property) apply_value(property) %}
  NinePieceImage image(state.StyleBuilder().{{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.StyleBuilder().{{setter}}(image);
    {% endcall %}
  {% elif property.style_builder_template in ['animation', 'transition'] %}
    {% set attribute = property.style_builder_template_args['attribute'] %}
    {% set animation = 'Animation' if property.style_builder_template == 'animation' else 'Transition' %}
    {% set vector = attribute + "List()" %}
    {% call(property) apply_initial(property) %}
  if (!state.StyleBuilder().{{animation}}s())
    return;
  CSS{{animation}}Data& data = state.StyleBuilder().Access{{animation}}s();
  data.{{vector}}.clear();
  data.{{vector}}.push_back(CSS{{animation}}Data::Initial{{attribute}}());
    {% endcall %}

    {% call(property) apply_inherit(property) %}
  const CSS{{animation}}Data* parent_data = state.ParentStyle()->{{animation}}s();
  if (!parent_data)
    ApplyInitial{{property_id}}(state);
  else
    state.StyleBuilder().Access{{animation}}s().{{vector}} = parent_data->{{vector}};
    {% endcall %}

    {% call(property) apply_value(property) %}
  const CSSValueList& list = To<CSSValueList>(value);
  CSS{{animation}}Data& data = state.StyleBuilder().Access{{animation}}s();
  data.{{vector}}.clear();
  data.{{vector}}.reserve(list.length());
  for (const CSSValue* list_value : list) {
    const auto& item = *list_value;
    data.{{vector}}.push_back(CSSToStyleMap::MapAnimation{{attribute}}(state, item));
  }
    {% endcall %}
  {% elif property.style_builder_template in ['background_layer', 'mask_layer'] %}
    {% set layer_type = 'Background' if property.style_builder_template == 'background_layer' else 'Mask' %}
    {% set fill_type = property.style_builder_template_args['fill_type'] %}
    {% set fill_type_getter = property.style_builder_template_args['fill_type_getter'] or fill_type %}
    {% call(property) apply_initial(property) %}
  FillLayer* curr_child = &state.StyleBuilder().Access{{layer_type}}Layers();
  curr_child->Set{{fill_type}}(FillLayer::InitialFill{{fill_type}}(EFillLayerType::k{{layer_type}}));
  for (curr_child = curr_child->Next(); curr_child; curr_child = curr_child->Next())
    curr_child->Clear{{fill_type}}();
    {% endcall %}

    {% call(property) apply_inherit(property) %}
  FillLayer* curr_child = &state.StyleBuilder().Access{{layer_type}}Layers();
  FillLayer* prev_child = 0;
  const FillLayer* curr_parent = &state.ParentStyle()->{{layer_type}}Layers();
  while (curr_parent && curr_parent->Is{{fill_type}}Set()) {
    if (!curr_child)
      curr_child = prev_child->EnsureNext();
    curr_child->Set{{fill_type}}(curr_parent->{{fill_type_getter}}());
      {% if fill_type == "PositionX" %}
    if (curr_parent->IsBackgroundXOriginSet())
      curr_child->SetBackgroundXOrigin(curr_parent->BackgroundXOrigin());
      {% endif %}
      {% if fill_type == "PositionY" %}
    if (curr_parent->IsBackgroundYOriginSet())
      curr_child->SetBackgroundYOrigin(curr_parent->BackgroundYOrigin());
      {% endif %}
    prev_child = curr_child;
    curr_child = prev_child->Next();
    curr_parent = curr_parent->Next();
  }

  while (curr_child) {
    // Reset any remaining layers to not have the property set.
    curr_child->Clear{{fill_type}}();
    curr_child = curr_child->Next();
  }
    {% endcall %}

    {% call(property) apply_value(property) %}
  FillLayer* curr_child = &state.StyleBuilder().Access{{layer_type}}Layers();
  FillLayer* prev_child = 0;
  const auto* value_list = DynamicTo<CSSValueList>(value);
  if (value_list && !value.IsImageSetValue()) {
    // Walk each value and put it into a layer, creating new layers as needed.
    for (unsigned int i = 0; i < value_list->length(); i++) {
      if (!curr_child)
        curr_child = prev_child->EnsureNext();
      CSSToStyleMap::MapFill{{fill_type}}(state, curr_child, value_list->Item(i));
      prev_child = curr_child;
      curr_child = curr_child->Next();
    }
  } else {
    CSSToStyleMap::MapFill{{fill_type}}(state, curr_child, value);
    curr_child = curr_child->Next();
  }
  while (curr_child) {
    // Reset all remaining layers to not have the property set.
    curr_child->Clear{{fill_type}}();
    curr_child = curr_child->Next();
  }
    {% endcall %}
  {% elif property.style_builder_template in ['color', 'visited_color'] %}
    {% set initial_color = property.style_builder_template_args['initial_color'] or 'StyleColor::CurrentColor' %}
    {% set is_visited = property.style_builder_template == 'visited_color' %}
    {% set main_getter = is_visited and property.unvisited_property.getter or property.getter %}
    {% call(property) apply_initial(property) %}
  {{set_value(property)}}({{initial_color}}());
    {% endcall %}

    {% call(property) apply_inherit(property) %}
  {{set_value(property)}}(state.ParentStyle()->{{main_getter}}());
    {% endcall %}

    {% call(property) apply_value(property) %}
    {% set visited_link = is_visited and 'true' or 'false' %}
  {{set_value(property)}}(StyleBuilderConverter::{{property.converter}}(state, value, {{visited_link}}));
    {% endcall %}
  {% elif property.style_builder_template == 'counter' %}
    {% set action = property.style_builder_template_args['action'] %}
    {% call(property) apply_initial(property) %}
  state.StyleBuilder().Clear{{action}}Directives();
    {% endcall %}

    {% call(property) apply_inherit(property) %}
  state.StyleBuilder().Clear{{action}}Directives();
  const CounterDirectiveMap* parent_map = state.ParentStyle()->GetCounterDirectives();
  if (!parent_map)
    return;

  CounterDirectiveMap& map = state.StyleBuilder().AccessCounterDirectives();
  DCHECK(!parent_map->empty());

  typedef CounterDirectiveMap::const_iterator Iterator;
  Iterator end = parent_map->end();
  for (Iterator it = parent_map->begin(); it != end; ++it) {
    CounterDirectives& directives = map.insert(it->key, CounterDirectives()).stored_value->value;
    directives.Inherit{{action}}(it->value);
  }
    {% endcall %}

    {% call(property) apply_value(property) %}
  state.StyleBuilder().Clear{{action}}Directives();

  const auto* list = DynamicTo<CSSValueList>(value);
  if (!list) {
    DCHECK_EQ(To<CSSIdentifierValue>(value).GetValueID(), CSSValueID::kNone);
    return;
  }

  CounterDirectiveMap& map = state.StyleBuilder().AccessCounterDirectives();

  for (const CSSValue* item : *list) {
    const auto& pair = To<CSSValuePair>(*item);
    AtomicString identifier(To<CSSCustomIdentValue>(pair.First()).Value());
    int counter_value = To<CSSPrimitiveValue>(pair.Second()).ComputeInteger(state.CssToLengthConversionData());
    CounterDirectives& directives =
    map.insert(identifier, CounterDirectives()).stored_value->value;
        {% if action == 'Reset' %}
    directives.SetResetValue(counter_value);
        {% elif action == 'Increment' %}
    directives.AddIncrementValue(counter_value);
        {% else %}
    directives.SetSetValue(counter_value);
        {% endif %}
  }
  DCHECK(!map.empty());
    {% endcall %}
  {% elif property.style_builder_template == 'grid' %}
    {% set type = property.style_builder_template_args['type'] %}
    {% call(property) apply_initial(property) %}
  {{style_access(property)}}Set{{type}}(ComputedStyleInitialValues::Initial{{type}}());
    {% endcall %}

    {% call(property) apply_inherit(property) %}
  {{style_access(property)}}Set{{type}}(state.ParentStyle()->{{type}}());
    {% endcall %}

    {% call(property) apply_value(property) %}
  ComputedGridTrackList computed_grid_track_list;
  StyleBuilderConverter::ConvertGridTrackList(value, computed_grid_track_list, state);
  {{style_access(property)}}Set{{type}}(computed_grid_track_list);
    {% endcall %}
  {% endif %}
{%- endmacro %}