chromium/third_party/blink/renderer/build/scripts/core/style/templates/computed_style_base.h.tmpl

{% from 'templates/macros.tmpl' import license, print_if, source_files_for_generated_file %}
{% from 'templates/fields/field.tmpl' import encode, getter_expression, setter_expression, declare_storage, fieldwise_compare, fieldwise_diff, fieldwise_pointer_compare_inherited, field_group_access_bit_name %}
{% from 'templates/fields/group.tmpl' import declare_field_group_class %}
{{license()}}

{{source_files_for_generated_file(template_file, input_files)}}

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_COMPUTED_STYLE_BASE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_COMPUTED_STYLE_BASE_H_

#include "base/memory/values_equivalent.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/core/style/member_copy.h"
#include "third_party/blink/renderer/core/style/computed_style_initial_values.h"
#include "third_party/blink/renderer/core/style/style_cached_data.h"
{% for path in include_paths %}
#include "{{path}}"
{% endfor %}

{# Each field template has macros that we can call to generate specific
   aspects of the field (e.g. getters, setters).
#}
{% import 'templates/fields/keyword.tmpl' as keyword %}
{% import 'templates/fields/multi_keyword.tmpl' as multi_keyword %}
{% import 'templates/fields/bitset_keyword.tmpl' as bitset_keyword %}
{% import 'templates/fields/primitive.tmpl' as primitive %}
{% import 'templates/fields/derived_flag.tmpl' as derived_flag %}
{% import 'templates/fields/monotonic_flag.tmpl' as monotonic_flag %}
{% import 'templates/fields/external.tmpl' as external %}
{% import 'templates/fields/pointer.tmpl' as pointer %}
{% from 'templates/fields/field.tmpl' import encode %}
{% set field_templates = {
     'keyword': keyword,
     'multi_keyword': multi_keyword,
     'bitset_keyword': bitset_keyword,
     'primitive': primitive,
     'derived_flag': derived_flag,
     'monotonic_flag': monotonic_flag,
     'external': external,
     'pointer': pointer
   } %}

namespace blink {

// Forward declaration for diff functions.
class ComputedStyle;

// Forward declaration for constructor.
class ComputedStyleBuilderBase;

// Forward declaration of friends:
{% for property in longhands %}
{% if property.computed_style_custom_functions or property.computed_style_protected_functions %}
namespace {{property.namespace}} { class {{property.classname}}; }
{% endif %}
{% endfor %}

// The generated portion of ComputedStyle. For more info, see the header comment
// in computed_style.h.
//
// ComputedStyleBase is a generated class that stores data members or 'fields'
// used in ComputedStyle. These fields can represent CSS properties or internal
// style information.
//
// STORAGE:
//
// Fields are organised in a tree structure, where a node (called a 'group')
// stores a set of fields and a set of pointers to child nodes (called
// 'subgroups'). We can visualise the tree structure with ComputedStyleBase as
// the root node:
//
// ComputedStyleBase (fields: display, vertical-align, ...)
//  |- StyleSurroundData (fields: border-color, left/right/top/bottom, ...)
//  |- StyleBoxData (fields: width, height, padding, ...)
//  |- ...
//  |- StyleRareNonInheritedData (fields: box-shadow, text-overflow, ...)
//      |- StyleFlexibleBoxData (fields: flex-direction, flex-wrap, ...)
//      |- ...
//
// This design saves memory by allowing multiple ComputedStyleBases to share the
// same instance of a subgroup. For example, if a page never uses flex box
// properties, then every ComputedStyleBase can share the same instance of
// StyleFlexibleBoxData. Without this sharing, we would need to allocate a copy
// of all the flex box fields for every ComputedStyleBase. Similarly, when an
// element inherits from its parent, its ComputedStyleBase can simply share all
// of its subgroups with the parent's.
//
// Most of these groupings are done manually, although there have been some
// adjustments based on statistics.
//
// INTERFACE:
//
// The functions generated for a field is determined by its 'template'. For
// example, a field with the 'keyword' template has only one setter, whereas an
// 'external' field has an extra setter that takes an rvalue reference. A list
// of the available templates can be found in css_properties.json5.
class ComputedStyleBase : public GarbageCollected<ComputedStyleBase> {
  // Properties with protected accessors must be friends because
  // Longhand::Apply* functions typically need the "raw" computed value:
  {% for property in longhands %}
  {% if property.computed_style_custom_functions or property.computed_style_protected_functions %}
  friend class {{property.namespace}}::{{property.classname}};
  {% endif %}
  {% endfor %}

 public:
  inline bool IndependentInheritedEqual(const ComputedStyleBase& o) const {
    return (
        {{fieldwise_compare(computed_style, computed_style.all_fields
            |selectattr("is_property")
            |selectattr("is_inherited")
            |selectattr("is_independent")
            |rejectattr("is_semi_independent_variable")
            |list
          )|indent(8)}}
    );
  }

  inline bool NonIndependentInheritedEqual(const ComputedStyleBase& o) const {
    return (
        {{fieldwise_compare(computed_style, computed_style.all_fields
            |selectattr("is_property")
            |selectattr("is_inherited")
            |rejectattr("is_independent")
            |rejectattr("is_semi_independent_variable")
            |list
          )|indent(8)}}
    );
  }

  inline bool InheritedVariablesEqual(const ComputedStyleBase& o) const {
    return (
        {{fieldwise_compare(computed_style, computed_style.all_fields
            |selectattr("is_property")
            |selectattr("is_inherited")
            |rejectattr("is_independent")
            |selectattr("is_semi_independent_variable")
            |list
          )|indent(8)}}
    );
  }

  inline bool InheritedEqual(const ComputedStyleBase& o) const {
    return IndependentInheritedEqual(o) && NonIndependentInheritedEqual(o);
  }

  inline bool NonInheritedEqual(const ComputedStyleBase& o) const {
    return (
        {{fieldwise_compare(computed_style, computed_style.all_fields
            |selectattr("is_property")
            |rejectattr("is_inherited")
            |list
          )|indent(8)}}
    );
  }

  inline bool InheritedDataShared(const ComputedStyleBase& o) const {
    return (
        {{fieldwise_pointer_compare_inherited(computed_style)|indent(8)}}
    );
  }

  // Fields.
  // TODO(sashab): Remove initialFoo() static methods and update callers to
  // use resetFoo(), which can be more efficient.

  {% for field in computed_style.all_fields|sort(attribute='name') %}
  // {{field.property_name}}
  {{field_templates[field.field_template].decl_public_methods(field)|indent(2)}}

  {% endfor %}

#if DCHECK_IS_ON()
  enum class DebugField {
    {% for field in computed_style.all_fields|sort(attribute='name') %}
    {{field.name}},
    {% endfor %}
  };
  struct DebugDiff {
    DebugField field;
    std::string correct;
    std::string actual;
  };

  CORE_EXPORT static String DebugFieldToString(DebugField);

  // Returns a list of all fields that differ between |this| and |o|.
  CORE_EXPORT Vector<DebugDiff> DebugDiffFields(const ComputedStyleBase& o) const;
#endif // DCHECK_IS_ON()

  // Find a list of which subgroups listed have changed between this and the
  // other ComputedStyle(Base); output a list of the changed groups and their
  // sizes in bytes. This is meant for more precise memory tracking than just
  // looking at the Oilpan statistics, or for finding out (empirically)
  // which groups are affected by setting a specific property. It is used
  // only in the style perftest, not in the normal rendering engine.
  //
  // Note that if you change something deep in a subgroup like e.g. a->b->c,
  // both a->b and a will also be recorded, as they must be modified to get
  // the pointer in place. The fixed 4- or 8-byte Oilpan header overhead
  // is not included.
  Vector<std::pair<String, size_t>>
  FindChangedGroups(const ComputedStyleBase &other_style) const;

  CORE_EXPORT void Trace(Visitor* visitor) const;
  void TraceAfterDispatch(Visitor* visitor) const {
    {% for subgroup in computed_style.subgroups %}
    visitor->Trace({{subgroup.member_name}});
    {% endfor %}
    {% for field in computed_style.fields %}
    {% if field.wrapper_pointer_name == 'Member' %}
    visitor->Trace({{field.name}});
    {% endif %}
    {% endfor %}
  }

 private:
  {% for subgroup in computed_style.subgroups %}
  {{declare_field_group_class(subgroup)|indent(2)}}

  {% endfor %}

 protected:
  // Constructor and destructor are protected so that only the parent class ComputedStyle
  // can instantiate this class.
  ComputedStyleBase();
  ComputedStyleBase(const ComputedStyleBase &initial_style) = default;
  explicit ComputedStyleBase(const ComputedStyleBuilderBase&);

  {% for field in computed_style.all_fields|sort(attribute='name') %}
  {% if field.field_template not in ('pointer',) %}
  // {{field.property_name}}
  {{field_templates[field.field_template].decl_protected_methods(field)|indent(2)}}

  {% endif %}
  {% endfor %}

  struct Data {
    {% for field in computed_style.fields|rejectattr('requires_tracing') %}
    {{declare_storage(field)}}
    {% endfor %}
  };

  enum FieldDifference : uint64_t {
  {% for value in diff_enum %}
    {{value}} = static_cast<uint64_t>(1) << {{loop.index0}},
  {% endfor %}
  };

  static uint64_t FieldInvalidationDiff(const ComputedStyle& a, const ComputedStyle& b);

 private:
  friend class ComputedStyleBuilder;
  friend class ComputedStyleBuilderBase;

  // Storage.
  {% for subgroup in computed_style.subgroups %}
  Member<{{subgroup.type_name}}> {{subgroup.member_name}};
  {% endfor %}

  {# Members #}
  {% for field in computed_style.fields|selectattr('requires_tracing') %}
  {{declare_storage(field)}}
  {% endfor %}

  Data data_;
};

class ComputedStyleBuilderBase {
  STACK_ALLOCATED();
 public:

  enum IsAtShadowBoundary {
    kAtShadowBoundary,
    kNotAtShadowBoundary,
  };

  {% for field in computed_style_builder.all_fields|rejectattr('mutable')|sort(attribute='name') %}
  // {{field.property_name}}
  {{field_templates[field.field_template].decl_public_methods(field)|indent(2)}}

  {% endfor %}

 protected:
  ComputedStyleBuilderBase() = delete;

  explicit ComputedStyleBuilderBase(const ComputedStyleBase&);

  ComputedStyleBuilderBase(const ComputedStyleBase &source_for_noninherited,
                           const ComputedStyleBase &parent_style);

  void PropagateIndependentInheritedProperties(const ComputedStyleBase& parent_style);

  {% for field in computed_style_builder.all_fields|rejectattr('mutable')|sort(attribute='name') %}
  // {{field.property_name}}
  {{field_templates[field.field_template].decl_protected_methods(field)|indent(2)}}

  {% endfor %}

  void ResetAccess() const {
    memset(&access_, 0, sizeof(access_));
  }

 private:
  friend class ComputedStyleBase;

  // Access flags. These are passed to Access to ensure that we have
  // a private copy of the relevant group before mutation.
  mutable struct {
  {% for group in computed_style.all_subgroups|sort(attribute='name') %}
    bool {{group.member_name}} = false;
  {% endfor %}
  } access_;

  template <typename T>
  static T* Access(T*& data, bool& access_flag) {
    if (!access_flag) {
      access_flag = true;
      data = data->Copy();
    }
    return data;
  }

  template <typename T>
  static T* Access(Member<T>& data, bool& access_flag) {
    if (!access_flag) {
      access_flag = true;
      data = data->Copy();
    }
    return data.Get();
  }

  {% for subgroup in computed_style.subgroups %}
  using {{subgroup.type_name}} = ComputedStyleBase::{{subgroup.type_name}};
  {% endfor %}

  // Storage.
  {% for subgroup in computed_style.subgroups %}
  {{subgroup.type_name}}* {{subgroup.member_name}};
  {% endfor %}

  {# Members #}
  {% for field in computed_style.fields|selectattr('requires_tracing') %}
  {{field.type_name}}* {{field.name}};
  {% endfor %}

  ComputedStyleBase::Data data_;
};

}  // namespace blink

namespace blink {
{% for group in computed_style.all_subgroups|sort(attribute='name') %}
template <typename T>
struct ThreadingTrait<
    T,
    std::enable_if_t<std::is_base_of<blink::ComputedStyleBase::{{group.type_name}}, T>::value>> {
  static constexpr ThreadAffinity kAffinity = kMainThreadOnly;
};
{% endfor %}
template <typename T>
struct ThreadingTrait<
    T,
    std::enable_if_t<std::is_base_of<blink::ComputedStyleBase, T>::value>> {
  static constexpr ThreadAffinity kAffinity = kMainThreadOnly;
};
}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_COMPUTED_STYLE_BASE_H_