{% 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_