OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2016 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import math | 6 import math |
7 import sys | 7 import sys |
8 | 8 |
9 import json5_generator | 9 import json5_generator |
10 import template_expander | 10 import template_expander |
11 import make_style_builder | 11 import make_style_builder |
12 | 12 |
13 from name_utilities import camel_case, lower_first, upper_first_letter, enum_for
_css_keyword | 13 from name_utilities import ( |
| 14 enum_for_css_keyword, enum_value_name, class_member_name, method_name |
| 15 ) |
14 | 16 |
15 | 17 |
16 # Temporary hard-coded list of fields that are not CSS properties. | 18 # Temporary hard-coded list of fields that are not CSS properties. |
17 # Ideally these would be specified in a .json5 file. | 19 # Ideally these would be specified in a .json5 file. |
18 NONPROPERTY_FIELDS = [ | 20 NONPROPERTY_FIELDS = [ |
19 {'name': 'isLink', 'field_template': 'monotonic_flag'}, | 21 {'name': 'IsLink', 'field_template': 'monotonic_flag'}, |
20 # Style can not be shared. | 22 # Style can not be shared. |
21 {'name': 'unique', 'field_template': 'monotonic_flag'}, | 23 {'name': 'Unique', 'field_template': 'monotonic_flag'}, |
22 # Whether this style is affected by these pseudo-classes. | 24 # Whether this style is affected by these pseudo-classes. |
23 {'name': 'affectedByFocus', 'field_template': 'monotonic_flag'}, | 25 {'name': 'AffectedByFocus', 'field_template': 'monotonic_flag'}, |
24 {'name': 'affectedByHover', 'field_template': 'monotonic_flag'}, | 26 {'name': 'AffectedByHover', 'field_template': 'monotonic_flag'}, |
25 {'name': 'affectedByActive', 'field_template': 'monotonic_flag'}, | 27 {'name': 'AffectedByActive', 'field_template': 'monotonic_flag'}, |
26 {'name': 'affectedByDrag', 'field_template': 'monotonic_flag'}, | 28 {'name': 'AffectedByDrag', 'field_template': 'monotonic_flag'}, |
27 # A non-inherited property references a variable or @apply is used | 29 # A non-inherited property references a variable or @apply is used |
28 {'name': 'hasVariableReferenceFromNonInheritedProperty', 'field_template': '
monotonic_flag'}, | 30 {'name': 'HasVariableReferenceFromNonInheritedProperty', 'field_template': '
monotonic_flag'}, |
29 # Explicitly inherits a non-inherited property | 31 # Explicitly inherits a non-inherited property |
30 {'name': 'hasExplicitlyInheritedProperties', 'field_template': 'monotonic_fl
ag'}, | 32 {'name': 'HasExplicitlyInheritedProperties', 'field_template': 'monotonic_fl
ag'}, |
31 # These properties only have generated storage, and their methods are handwr
itten in ComputedStyle. | 33 # These properties only have generated storage, and their methods are handwr
itten in ComputedStyle. |
32 # TODO(shend): Remove these fields and delete the 'storage_only' template. | 34 # TODO(shend): Remove these fields and delete the 'storage_only' template. |
33 {'name': 'emptyState', 'field_template': 'storage_only', 'size': 1} | 35 {'name': 'EmptyState', 'field_template': 'storage_only', 'size': 1} |
34 ] | 36 ] |
35 | 37 |
36 | 38 |
37 class Field(object): | 39 class Field(object): |
38 """ | 40 """ |
39 The generated ComputedStyle object is made up of a series of Fields. | 41 The generated ComputedStyle object is made up of a series of Fields. |
40 Each Field has a name, size, type, etc, and a bunch of attributes to | 42 Each Field has a name, size, type, etc, and a bunch of attributes to |
41 determine which methods it will be used in. | 43 determine which methods it will be used in. |
42 | 44 |
43 A Field also has enough information to use any storage type in C++, such as | 45 A Field also has enough information to use any storage type in C++, such as |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
114 | 116 |
115 def _create_enums(properties): | 117 def _create_enums(properties): |
116 """ | 118 """ |
117 Returns a dictionary of enums to be generated, enum name -> [list of enum va
lues] | 119 Returns a dictionary of enums to be generated, enum name -> [list of enum va
lues] |
118 """ | 120 """ |
119 enums = {} | 121 enums = {} |
120 for property_ in properties: | 122 for property_ in properties: |
121 # Only generate enums for keyword properties that use the default field_
type_path. | 123 # Only generate enums for keyword properties that use the default field_
type_path. |
122 if property_['field_template'] == 'keyword' and property_['field_type_pa
th'] is None: | 124 if property_['field_template'] == 'keyword' and property_['field_type_pa
th'] is None: |
123 enum_name = property_['type_name'] | 125 enum_name = property_['type_name'] |
124 # From the Blink style guide: Enum members should use InterCaps with
an initial capital letter. [names-enum-members] | 126 enum_values = [enum_value_name(k) for k in property_['keywords']] |
125 enum_values = [('k' + camel_case(k)) for k in property_['keywords']] | |
126 | 127 |
127 if enum_name in enums: | 128 if enum_name in enums: |
128 # There's an enum with the same name, check if the enum values a
re the same | 129 # There's an enum with the same name, check if the enum values a
re the same |
129 assert set(enums[enum_name]) == set(enum_values), \ | 130 assert set(enums[enum_name]) == set(enum_values), \ |
130 ("'" + property_['name'] + "' can't have type_name '" + enum
_name + "' " | 131 ("'" + property_['name'] + "' can't have type_name '" + enum
_name + "' " |
131 "because it was used by a previous property, but with a dif
ferent set of keywords. " | 132 "because it was used by a previous property, but with a dif
ferent set of keywords. " |
132 "Either give it a different name or ensure the keywords are
the same.") | 133 "Either give it a different name or ensure the keywords are
the same.") |
133 | 134 |
134 enums[enum_name] = enum_values | 135 enums[enum_name] = enum_values |
135 | 136 |
136 return enums | 137 return enums |
137 | 138 |
138 | 139 |
139 def _create_property_field(property_): | 140 def _create_property_field(property_): |
140 """ | 141 """ |
141 Create a property field from a CSS property and return the Field object. | 142 Create a property field from a CSS property and return the Field object. |
142 """ | 143 """ |
143 property_name = property_['name_for_methods'] | 144 name_for_methods = property_['name_for_methods'] |
144 property_name_lower = lower_first(property_name) | |
145 | 145 |
146 # From the Blink style guide: Other data members should be prefixed by "m_".
[names-data-members] | |
147 field_name = 'm_' + property_name_lower | |
148 bits_needed = math.log(len(property_['keywords']), 2) # TODO: implement for
non-enums | 146 bits_needed = math.log(len(property_['keywords']), 2) # TODO: implement for
non-enums |
149 type_name = property_['type_name'] | 147 type_name = property_['type_name'] |
150 | 148 |
151 # For now, the getter name should match the field name. Later, getter names | 149 # For now, the getter name should match the field name. Later, getter names |
152 # will start with an uppercase letter, so if they conflict with the type nam
e, | 150 # will start with an uppercase letter, so if they conflict with the type nam
e, |
153 # add 'get' to the front. | 151 # add 'get' to the front. |
154 getter_method_name = property_name_lower | 152 if type_name != name_for_methods: |
155 if type_name == property_name: | 153 getter_method_name = method_name(name_for_methods) |
156 getter_method_name = 'get' + property_name | 154 else: |
| 155 getter_method_name = method_name('get-' + name_for_methods) |
157 | 156 |
158 assert property_['initial_keyword'] is not None, \ | 157 assert property_['initial_keyword'] is not None, \ |
159 ('MakeComputedStyleBase requires an initial keyword for keyword fields,
none specified ' | 158 ('MakeComputedStyleBase requires an initial keyword for keyword fields,
none specified ' |
160 'for property ' + property_['name']) | 159 'for property ' + property_['name']) |
161 default_value = type_name + '::k' + camel_case(property_['initial_keyword']) | 160 default_value = type_name + '::' + enum_value_name(property_['initial_keywor
d']) |
162 | 161 |
163 return Field( | 162 return Field( |
164 'property', | 163 'property', |
165 name=field_name, | 164 name=class_member_name(name_for_methods), |
166 property_name=property_['name'], | 165 property_name=property_['name'], |
167 inherited=property_['inherited'], | 166 inherited=property_['inherited'], |
168 independent=property_['independent'], | 167 independent=property_['independent'], |
169 type_name=type_name, | 168 type_name=type_name, |
170 field_template=property_['field_template'], | 169 field_template=property_['field_template'], |
171 size=int(math.ceil(bits_needed)), | 170 size=int(math.ceil(bits_needed)), |
172 default_value=default_value, | 171 default_value=default_value, |
173 getter_method_name=getter_method_name, | 172 getter_method_name=getter_method_name, |
174 setter_method_name='set' + property_name, | 173 setter_method_name=method_name('set-' + name_for_methods), |
175 initial_method_name='initial' + property_name, | 174 initial_method_name=method_name('initial-' + name_for_methods), |
176 resetter_method_name='reset' + property_name, | 175 resetter_method_name=method_name('reset-' + name_for_methods), |
177 is_inherited_method_name=property_name_lower + 'IsInherited', | 176 is_inherited_method_name=method_name(name_for_methods + '-IsInherited'), |
178 ) | 177 ) |
179 | 178 |
180 | 179 |
181 def _create_inherited_flag_field(property_): | 180 def _create_inherited_flag_field(property_): |
182 """ | 181 """ |
183 Create the field used for an inheritance fast path from an independent CSS p
roperty, | 182 Create the field used for an inheritance fast path from an independent CSS p
roperty, |
184 and return the Field object. | 183 and return the Field object. |
185 """ | 184 """ |
186 property_name = property_['name_for_methods'] | 185 name_for_methods = property_['name_for_methods'] |
187 property_name_lower = lower_first(property_name) | 186 name_for_methods_suffixed = name_for_methods + 'IsInherited' |
188 | |
189 field_name_suffix_upper = property_name + 'IsInherited' | |
190 field_name_suffix_lower = property_name_lower + 'IsInherited' | |
191 | 187 |
192 return Field( | 188 return Field( |
193 'inherited_flag', | 189 'inherited_flag', |
194 name='m_' + field_name_suffix_lower, | 190 name=class_member_name(name_for_methods_suffixed), |
195 property_name=property_['name'], | 191 property_name=property_['name'], |
196 type_name='bool', | 192 type_name='bool', |
197 field_template='flag', | 193 field_template='flag', |
198 size=1, | 194 size=1, |
199 default_value='true', | 195 default_value='true', |
200 getter_method_name=field_name_suffix_lower, | 196 getter_method_name=method_name(name_for_methods_suffixed), |
201 setter_method_name='set' + field_name_suffix_upper, | 197 setter_method_name=method_name('set-' + name_for_methods_suffixed), |
202 initial_method_name='initial' + field_name_suffix_upper, | 198 initial_method_name=method_name('initial-' + name_for_methods_suffixed), |
203 resetter_method_name='reset' + field_name_suffix_upper, | 199 resetter_method_name=method_name('reset-' + name_for_methods_suffixed), |
204 ) | 200 ) |
205 | 201 |
206 | 202 |
207 def _create_nonproperty_field(property_): | 203 def _create_nonproperty_field(property_): |
208 """ | 204 """ |
209 Create a nonproperty field from an entry in NONPROPERTY_FIELDS and return th
e Field object. | 205 Create a nonproperty field from an entry in NONPROPERTY_FIELDS and return th
e Field object. |
210 """ | 206 """ |
211 # TODO(shend): Make this work for nonflags | 207 # TODO(shend): Make this work for nonflags |
212 assert property_['field_template'] in ('flag', 'monotonic_flag', 'storage_on
ly'), \ | 208 assert property_['field_template'] in ('flag', 'monotonic_flag', 'storage_on
ly'), \ |
213 "Nonproperties with arbitrary templates are not yet supported" | 209 "Nonproperties with arbitrary templates are not yet supported" |
214 member_name = 'm_' + property_['name'] | 210 name_for_methods = property_['name_for_methods'] |
215 field_name_upper = upper_first_letter(property_['name']) | |
216 | 211 |
217 if property_['field_template'] == 'storage_only': | 212 if property_['field_template'] == 'storage_only': |
218 assert 'size' in property_, 'storage_only fields need to specify a size' | 213 assert 'size' in property_, 'storage_only fields need to specify a size' |
219 size = property_['size'] | 214 size = property_['size'] |
220 else: | 215 else: |
221 # Otherwise the field must be some type of flag. | 216 # Otherwise the field must be some type of flag. |
222 size = 1 | 217 size = 1 |
223 | 218 |
224 return Field( | 219 return Field( |
225 'nonproperty', | 220 'nonproperty', |
226 name=member_name, | 221 name=class_member_name(name_for_methods), |
227 property_name=property_['name'], | 222 property_name=name_for_methods, |
228 type_name='bool', | 223 type_name='bool', |
229 field_template=property_['field_template'], | 224 field_template=property_['field_template'], |
230 size=size, | 225 size=size, |
231 default_value='false', | 226 default_value='false', |
232 getter_method_name=property_['name'], | 227 getter_method_name=method_name(name_for_methods), |
233 setter_method_name='set' + field_name_upper, | 228 setter_method_name=method_name('set-' + name_for_methods), |
234 initial_method_name='initial' + field_name_upper, | 229 initial_method_name=method_name('initial-' + name_for_methods), |
235 resetter_method_name='reset' + field_name_upper, | 230 resetter_method_name=method_name('reset-' + name_for_methods), |
236 ) | 231 ) |
237 | 232 |
238 | 233 |
239 def _create_fields(properties): | 234 def _create_fields(properties): |
240 """ | 235 """ |
241 Create ComputedStyle fields from CSS properties and return a list of Field o
bjects. | 236 Create ComputedStyle fields from CSS properties and return a list of Field o
bjects. |
242 """ | 237 """ |
243 fields = [] | 238 fields = [] |
244 for property_ in properties: | 239 for property_ in properties: |
245 # Only generate properties that have a field template | 240 # Only generate properties that have a field template |
246 if property_['field_template'] is not None: | 241 if property_['field_template'] is not None: |
247 # If the property is independent, add the single-bit sized isInherit
ed flag | 242 # If the property is independent, add the single-bit sized isInherit
ed flag |
248 # to the list of Fields as well. | 243 # to the list of Fields as well. |
249 if property_['independent']: | 244 if property_['independent']: |
250 fields.append(_create_inherited_flag_field(property_)) | 245 fields.append(_create_inherited_flag_field(property_)) |
251 | 246 |
252 fields.append(_create_property_field(property_)) | 247 fields.append(_create_property_field(property_)) |
253 | 248 |
254 # TODO(shend): Merge NONPROPERTY_FIELDS with property_values so that propert
ies and | 249 # TODO(shend): Merge NONPROPERTY_FIELDS with property_values so that propert
ies and |
255 # nonproperties can be treated uniformly. | 250 # nonproperties can be treated uniformly. |
256 for property_ in NONPROPERTY_FIELDS: | 251 for property_ in NONPROPERTY_FIELDS: |
| 252 property_['name_for_methods'] = property_['name'] |
257 fields.append(_create_nonproperty_field(property_)) | 253 fields.append(_create_nonproperty_field(property_)) |
258 | 254 |
259 return fields | 255 return fields |
260 | 256 |
261 | 257 |
262 def _pack_fields(fields): | 258 def _pack_fields(fields): |
263 """ | 259 """ |
264 Group a list of fields into buckets to minimise padding. | 260 Group a list of fields into buckets to minimise padding. |
265 Returns a list of buckets, where each bucket is a list of Field objects. | 261 Returns a list of buckets, where each bucket is a list of Field objects. |
266 """ | 262 """ |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
375 'expected_total_field_bytes': self._expected_total_field_bytes, | 371 'expected_total_field_bytes': self._expected_total_field_bytes, |
376 } | 372 } |
377 | 373 |
378 @template_expander.use_jinja('CSSValueIDMappingsGenerated.h.tmpl') | 374 @template_expander.use_jinja('CSSValueIDMappingsGenerated.h.tmpl') |
379 def generate_css_value_mappings(self): | 375 def generate_css_value_mappings(self): |
380 mappings = {} | 376 mappings = {} |
381 | 377 |
382 for property_ in self._properties.values(): | 378 for property_ in self._properties.values(): |
383 if property_['field_template'] == 'keyword': | 379 if property_['field_template'] == 'keyword': |
384 mappings[property_['type_name']] = { | 380 mappings[property_['type_name']] = { |
385 'default_value': 'k' + camel_case(property_['initial_keyword
']), | 381 'default_value': enum_value_name(property_['initial_keyword'
]), |
386 'mapping': [('k' + camel_case(k), enum_for_css_keyword(k)) f
or k in property_['keywords']], | 382 'mapping': [(enum_value_name(k), enum_for_css_keyword(k)) fo
r k in property_['keywords']], |
387 } | 383 } |
388 | 384 |
389 return { | 385 return { |
390 'include_paths': self._include_paths, | 386 'include_paths': self._include_paths, |
391 'mappings': mappings, | 387 'mappings': mappings, |
392 } | 388 } |
393 | 389 |
394 if __name__ == '__main__': | 390 if __name__ == '__main__': |
395 json5_generator.Maker(ComputedStyleBaseWriter).main() | 391 json5_generator.Maker(ComputedStyleBaseWriter).main() |
OLD | NEW |