1# Copyright 2017 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from itertools import chain
6
7from blinkbuild.name_style_converter import NameStyleConverter
8
9
10def _flatten_list(x):
11    """Flattens a list of lists into a single list."""
12    return list(chain.from_iterable(x))
13
14
15def _num_32_bit_words_for_bit_fields(bit_fields):
16    """
17    Gets the number of 32 bit unsigned ints needed store a list of bit fields.
18    """
19    num_buckets, cur_bucket = 0, 0
20    for field in bit_fields:
21        if field.size + cur_bucket > 32:
22            num_buckets += 1
23            cur_bucket = 0
24        cur_bucket += field.size
25    return num_buckets + (cur_bucket > 0)
26
27
28class Group(object):
29    """Represents a group of fields stored together in a class.
30
31    Attributes:
32        name: The name of the group as a string, or None.
33        subgroups: List of Group instances that are stored as subgroups under
34            this group.
35        fields: List of Field instances stored directly under this group.
36        parent: The parent group, or None if this is the root group.
37    """
38
39    def __init__(self, name, subgroups, fields):
40        self.name = name
41        self.subgroups = subgroups
42        self.fields = fields
43        self.parent = None
44
45        converter = NameStyleConverter(name or '')
46        self.type_name = converter.to_class_name(prefix='style', suffix='data')
47        self.member_name = converter.to_class_data_member(suffix='data')
48        self.num_32_bit_words_for_bit_fields = _num_32_bit_words_for_bit_fields(
49            field for field in fields if field.is_bit_field)
50
51        # Recursively get all the fields in the subgroups as well
52        self.all_fields = _flatten_list(subgroup.all_fields
53                                        for subgroup in subgroups) + fields
54
55        # Ensure that all fields/subgroups on this group link to it
56        for field in fields:
57            field.group = self
58
59        for subgroup in subgroups:
60            subgroup.parent = self
61
62    def path_without_root(self):
63        """Return list of ancestor groups, excluding the root group.
64
65        The first item is the current group, second item is the parent, third
66        is the grandparent and so on.
67        """
68        group_path = []
69        current_group = self
70        while current_group.name:
71            group_path.insert(0, current_group)
72            current_group = current_group.parent
73        return group_path
74
75
76class Enum(object):
77    """Represents a generated enum in ComputedStyleBaseConstants."""
78
79    def __init__(self, type_name, keywords, is_set):
80        self.type_name = type_name
81        self.values = [
82            NameStyleConverter(keyword).to_enum_value() for keyword in keywords
83        ]
84        self.is_set = is_set
85
86
87class DiffGroup(object):
88    """Represents a group of expressions and subgroups that need to be diffed
89    for a function in ComputedStyle.
90
91    Attributes:
92        subgroups: List of DiffGroup instances that are stored as subgroups
93            under this group.
94        expressions: List of expression that are on this group that need to
95            be diffed.
96    """
97
98    def __init__(self, group):
99        self.group = group
100        self.subgroups = []
101        self.fields = []
102        self.expressions = []
103        self.predicates = []
104
105
106class Field(object):
107    """
108    The generated ComputedStyle object is made up of a series of Fields.
109    Each Field has a name, size, type, etc, and a bunch of attributes to
110    determine which methods it will be used in.
111
112    A Field also has enough information to use any storage type in C++, such as
113    regular member variables, or more complex storage like vectors or hashmaps.
114    Almost all properties will have at least one Field, often more than one.
115
116    Most attributes in this class correspond to parameters in
117    css_properties.json5. See that file for a more detailed explanation of
118    each attribute.
119
120    Attributes:
121        field_role: The semantic role of the field. Can be:
122            - 'property': for fields that store CSS properties
123            - 'inherited_flag': for single-bit flags that store whether a
124                                property is inherited by this style or
125                                set explicitly
126        name_for_methods: String used to form the names of getters and setters.
127            Should be in upper camel case.
128        property_name: Name of the property that the field is part of.
129        type_name: Name of the C++ type exposed by the generated interface
130            (e.g. EClear, int).
131        wrapper_pointer_name: Name of the pointer type that wraps this field
132            (e.g. RefPtr).
133        field_template: Determines the interface generated for the field. Can
134            be one of: keyword, flag, or monotonic_flag.
135        size: Number of bits needed for storage.
136        default_value: Default value for this field when it is first initialized
137    """
138
139    def __init__(self, field_role, name_for_methods, property_name, type_name,
140                 wrapper_pointer_name, field_template, size, default_value,
141                 custom_copy, custom_compare, mutable, getter_method_name,
142                 setter_method_name, initial_method_name,
143                 computed_style_custom_functions, **kwargs):
144        name_source = NameStyleConverter(name_for_methods)
145        self.name = name_source.to_class_data_member()
146        self.property_name = property_name
147        self.type_name = type_name
148        self.wrapper_pointer_name = wrapper_pointer_name
149        self.alignment_type = self.wrapper_pointer_name or self.type_name
150        self.field_template = field_template
151        self.size = size
152        self.default_value = default_value
153        self.custom_copy = custom_copy
154        self.custom_compare = custom_compare
155        self.mutable = mutable
156        self.group = None
157
158        # Method names
159        self.getter_method_name = getter_method_name
160        self.setter_method_name = setter_method_name
161        self.internal_getter_method_name = name_source.to_function_name(
162            suffix='internal')
163        self.internal_mutable_method_name = name_source.to_function_name(
164            prefix='mutable', suffix='internal')
165        self.internal_setter_method_name = NameStyleConverter(
166            setter_method_name).to_function_name(suffix='internal')
167        self.initial_method_name = initial_method_name
168        self.resetter_method_name = name_source.to_function_name(
169            prefix='reset')
170        self.computed_style_custom_functions = computed_style_custom_functions
171        # Only bitfields have sizes.
172        self.is_bit_field = self.size is not None
173
174        # Field role: one of these must be true
175        self.is_property = field_role == 'property'
176        self.is_inherited_flag = field_role == 'inherited_flag'
177        assert (self.is_property, self.is_inherited_flag).count(True) == 1, \
178            'Field role has to be exactly one of: property, inherited_flag'
179
180        if not self.is_inherited_flag:
181            self.is_inherited = kwargs.pop('inherited')
182            self.is_independent = kwargs.pop('independent')
183            assert self.is_inherited or not self.is_independent, \
184                'Only inherited fields can be independent'
185
186            self.is_inherited_method_name = name_source.to_function_name(
187                suffix=['is', 'inherited'])
188        assert len(kwargs) == 0, \
189            'Unexpected arguments provided to Field: ' + str(kwargs)
190