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 ( | 13 from name_utilities import ( |
14 enum_for_css_keyword, enum_type_name, enum_value_name, class_member_name, me thod_name, | 14 enum_for_css_keyword, enum_type_name, enum_value_name, class_member_name, me thod_name, |
15 join_name | 15 class_name, join_name |
16 ) | 16 ) |
17 from collections import OrderedDict | 17 from collections import defaultdict, OrderedDict |
18 from itertools import chain | |
18 | 19 |
19 | 20 |
20 # Temporary hard-coded list of fields that are not CSS properties. | 21 # Temporary hard-coded list of fields that are not CSS properties. |
21 # TODO(shend): Put this into its own JSON5 file. | 22 # TODO(shend): Put this into its own JSON5 file. |
22 NONPROPERTIES = [ | 23 NONPROPERTIES = [ |
23 {'name': 'IsLink', 'field_template': 'monotonic_flag', | 24 {'name': 'IsLink', 'field_template': 'monotonic_flag', |
24 'inherited': False, 'independent': False, 'default_value': False}, | 25 'inherited': False, 'independent': False, 'default_value': False}, |
25 {'name': 'OriginalDisplay', 'field_template': 'keyword', 'default_value': 'i nline', | 26 {'name': 'OriginalDisplay', 'field_template': 'keyword', 'default_value': 'i nline', |
26 'type_name': 'EDisplay', 'inherited': False, 'independent': False, | 27 'type_name': 'EDisplay', 'inherited': False, 'independent': False, |
27 'keywords': [ | 28 'keywords': [ |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
62 {'name': 'HasSimpleUnderline', 'field_template': 'storage_only', 'field_size ': 1, 'default_value': 'false', | 63 {'name': 'HasSimpleUnderline', 'field_template': 'storage_only', 'field_size ': 1, 'default_value': 'false', |
63 'type_name': 'bool', 'inherited': True, 'independent': False}, | 64 'type_name': 'bool', 'inherited': True, 'independent': False}, |
64 # TODO(shend): vertical align is actually a CSS property, but since we don't support union fields | 65 # TODO(shend): vertical align is actually a CSS property, but since we don't support union fields |
65 # which can be either a keyword or Length, this is generated as a nonpropert y for now. Remove this | 66 # which can be either a keyword or Length, this is generated as a nonpropert y for now. Remove this |
66 # once we can support union fields and groups. | 67 # once we can support union fields and groups. |
67 {'name': 'VerticalAlign', 'field_template': 'storage_only', 'field_size': 4, 'default_value': 'EVerticalAlign::kBaseline', | 68 {'name': 'VerticalAlign', 'field_template': 'storage_only', 'field_size': 4, 'default_value': 'EVerticalAlign::kBaseline', |
68 'type_name': 'EVerticalAlign', 'inherited': False, 'independent': False}, | 69 'type_name': 'EVerticalAlign', 'inherited': False, 'independent': False}, |
69 ] | 70 ] |
70 | 71 |
71 | 72 |
73 def _flatten_list(x): | |
74 """Flattens a list of lists into a single list.""" | |
75 return list(chain.from_iterable(x)) | |
76 | |
77 | |
78 def _num_bit_field_buckets(bit_fields): | |
meade_UTC10
2017/04/06 03:29:57
would this be better as something like
_num_32_bi
shend
2017/04/06 04:39:39
Done.
| |
79 """Gets the number of 32 bit unsigned integers needed store a list of bit fi elds.""" | |
80 num_buckets, cur_bucket = 0, 0 | |
81 for field in bit_fields: | |
82 if field.size + cur_bucket > 32: | |
83 num_buckets += 1 | |
84 cur_bucket = 0 | |
85 cur_bucket += field.size | |
86 return num_buckets + (cur_bucket > 0) | |
87 | |
88 | |
89 class Group(object): | |
90 def __init__(self, name, subgroups, fields): | |
91 """Represents a group of fields, which may contain subgroups that are dy namically allocated.""" | |
meade_UTC10
2017/04/06 03:29:57
Could you please document the types of the paramet
shend
2017/04/06 04:39:39
Agreed, the documentation in this file could be si
| |
92 self.name = name | |
93 self.subgroups = subgroups | |
94 self.fields = fields | |
95 self.type_name = class_name(join_name('style', name, ' data')) | |
96 self.member_name = class_member_name(name) | |
97 self.num_bit_field_buckets = _num_bit_field_buckets(field for field in f ields if field.is_bit_field) | |
98 | |
99 # Recursively get all the fields in the subgroups as well | |
100 self.all_fields = _flatten_list(subgroup.all_fields for subgroup in subg roups) + fields | |
101 | |
102 | |
72 class Field(object): | 103 class Field(object): |
73 """ | 104 """ |
74 The generated ComputedStyle object is made up of a series of Fields. | 105 The generated ComputedStyle object is made up of a series of Fields. |
75 Each Field has a name, size, type, etc, and a bunch of attributes to | 106 Each Field has a name, size, type, etc, and a bunch of attributes to |
76 determine which methods it will be used in. | 107 determine which methods it will be used in. |
77 | 108 |
78 A Field also has enough information to use any storage type in C++, such as | 109 A Field also has enough information to use any storage type in C++, such as |
79 regular member variables, or more complex storage like vectors or hashmaps. | 110 regular member variables, or more complex storage like vectors or hashmaps. |
80 Almost all properties will have at least one Field, often more than one. | 111 Almost all properties will have at least one Field, often more than one. |
81 | 112 |
(...skipping 10 matching lines...) Expand all Loading... | |
92 Should be in upper camel case. | 123 Should be in upper camel case. |
93 property_name: Name of the property that the field is part of. | 124 property_name: Name of the property that the field is part of. |
94 type_name: Name of the C++ type exposed by the generated interface (e.g. EClear, int). | 125 type_name: Name of the C++ type exposed by the generated interface (e.g. EClear, int). |
95 field_template: Determines the interface generated for the field. Can be one of: | 126 field_template: Determines the interface generated for the field. Can be one of: |
96 keyword, flag, or monotonic_flag. | 127 keyword, flag, or monotonic_flag. |
97 size: Number of bits needed for storage. | 128 size: Number of bits needed for storage. |
98 default_value: Default value for this field when it is first initialized . | 129 default_value: Default value for this field when it is first initialized . |
99 """ | 130 """ |
100 | 131 |
101 def __init__(self, field_role, name_for_methods, property_name, type_name, | 132 def __init__(self, field_role, name_for_methods, property_name, type_name, |
102 field_template, size, default_value, **kwargs): | 133 field_template, field_group, size, default_value, **kwargs): |
103 """Creates a new field.""" | 134 """Creates a new field.""" |
104 self.name = class_member_name(name_for_methods) | 135 self.name = class_member_name(name_for_methods) |
105 self.property_name = property_name | 136 self.property_name = property_name |
106 self.type_name = type_name | 137 self.type_name = type_name |
107 self.field_template = field_template | 138 self.field_template = field_template |
139 self.field_group = field_group | |
108 self.size = size | 140 self.size = size |
109 self.default_value = default_value | 141 self.default_value = default_value |
110 | 142 |
111 # Field role: one of these must be true | 143 # Field role: one of these must be true |
112 self.is_property = field_role == 'property' | 144 self.is_property = field_role == 'property' |
113 self.is_inherited_flag = field_role == 'inherited_flag' | 145 self.is_inherited_flag = field_role == 'inherited_flag' |
114 self.is_nonproperty = field_role == 'nonproperty' | 146 self.is_nonproperty = field_role == 'nonproperty' |
115 assert (self.is_property, self.is_inherited_flag, self.is_nonproperty).c ount(True) == 1, \ | 147 assert (self.is_property, self.is_inherited_flag, self.is_nonproperty).c ount(True) == 1, \ |
116 'Field role has to be exactly one of: property, inherited_flag, nonp roperty' | 148 'Field role has to be exactly one of: property, inherited_flag, nonp roperty' |
117 | 149 |
(...skipping 21 matching lines...) Expand all Loading... | |
139 """ | 171 """ |
140 Get a list of paths that need to be included for ComputedStyleBase. | 172 Get a list of paths that need to be included for ComputedStyleBase. |
141 """ | 173 """ |
142 include_paths = set() | 174 include_paths = set() |
143 for property_ in properties: | 175 for property_ in properties: |
144 if property_['field_type_path'] is not None: | 176 if property_['field_type_path'] is not None: |
145 include_paths.add(property_['field_type_path'] + '.h') | 177 include_paths.add(property_['field_type_path'] + '.h') |
146 return list(sorted(include_paths)) | 178 return list(sorted(include_paths)) |
147 | 179 |
148 | 180 |
181 def _group_fields(fields): | |
182 """Groups a list of fields by their field_group and returns the root group." "" | |
183 groups = defaultdict(list) | |
184 for field in fields: | |
185 groups[field.field_group].append(field) | |
186 | |
187 no_group = groups.pop(None) | |
188 subgroups = [Group(field_group, [], _reorder_fields(fields)) for field_group , fields in groups.items()] | |
189 return Group('', subgroups=subgroups, fields=_reorder_fields(no_group)) | |
190 | |
191 | |
149 def _create_enums(properties): | 192 def _create_enums(properties): |
150 """ | 193 """ |
151 Returns an OrderedDict of enums to be generated, enum name -> [list of enum values] | 194 Returns an OrderedDict of enums to be generated, enum name -> [list of enum values] |
152 """ | 195 """ |
153 enums = {} | 196 enums = {} |
154 for property_ in properties: | 197 for property_ in properties: |
155 # Only generate enums for keyword properties that use the default field_ type_path. | 198 # Only generate enums for keyword properties that use the default field_ type_path. |
156 if property_['field_template'] == 'keyword' and property_['field_type_pa th'] is None: | 199 if property_['field_template'] == 'keyword' and property_['field_type_pa th'] is None: |
157 enum_name = property_['type_name'] | 200 enum_name = property_['type_name'] |
158 enum_values = [enum_value_name(k) for k in property_['keywords']] | 201 enum_values = [enum_value_name(k) for k in property_['keywords']] |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
202 size = 1 | 245 size = 1 |
203 | 246 |
204 return Field( | 247 return Field( |
205 field_role, | 248 field_role, |
206 name_for_methods, | 249 name_for_methods, |
207 property_name=property_['name'], | 250 property_name=property_['name'], |
208 inherited=property_['inherited'], | 251 inherited=property_['inherited'], |
209 independent=property_['independent'], | 252 independent=property_['independent'], |
210 type_name=type_name, | 253 type_name=type_name, |
211 field_template=property_['field_template'], | 254 field_template=property_['field_template'], |
255 field_group=property_['field_group'], | |
212 size=size, | 256 size=size, |
213 default_value=default_value, | 257 default_value=default_value, |
214 ) | 258 ) |
215 | 259 |
216 | 260 |
217 def _create_inherited_flag_field(property_): | 261 def _create_inherited_flag_field(property_): |
218 """ | 262 """ |
219 Create the field used for an inheritance fast path from an independent CSS p roperty, | 263 Create the field used for an inheritance fast path from an independent CSS p roperty, |
220 and return the Field object. | 264 and return the Field object. |
221 """ | 265 """ |
222 return Field( | 266 return Field( |
223 'inherited_flag', | 267 'inherited_flag', |
224 join_name(property_['name_for_methods'], 'is inherited'), | 268 join_name(property_['name_for_methods'], 'is inherited'), |
225 property_name=property_['name'], | 269 property_name=property_['name'], |
226 type_name='bool', | 270 type_name='bool', |
227 field_template='flag', | 271 field_template='flag', |
272 field_group=property_['field_group'], | |
228 size=1, | 273 size=1, |
229 default_value='true', | 274 default_value='true', |
230 ) | 275 ) |
231 | 276 |
232 | 277 |
233 def _create_fields(field_role, properties): | 278 def _create_fields(field_role, properties): |
234 """ | 279 """ |
235 Create ComputedStyle fields from properties or nonproperties and return a li st of Field objects. | 280 Create ComputedStyle fields from properties or nonproperties and return a li st of Field objects. |
236 """ | 281 """ |
237 fields = [] | 282 fields = [] |
238 for property_ in properties: | 283 for property_ in properties: |
239 # Only generate properties that have a field template | 284 # Only generate properties that have a field template |
240 if property_['field_template'] is not None: | 285 if property_['field_template'] is not None: |
241 # If the property is independent, add the single-bit sized isInherit ed flag | 286 # If the property is independent, add the single-bit sized isInherit ed flag |
242 # to the list of Fields as well. | 287 # to the list of Fields as well. |
243 if property_['independent']: | 288 if property_['independent']: |
244 fields.append(_create_inherited_flag_field(property_)) | 289 fields.append(_create_inherited_flag_field(property_)) |
245 | 290 |
246 fields.append(_create_field(field_role, property_)) | 291 fields.append(_create_field(field_role, property_)) |
247 | 292 |
248 return fields | 293 return fields |
249 | 294 |
250 | 295 |
251 def _pack_fields(fields): | 296 def _reorder_fields(fields): |
meade_UTC10
2017/04/06 03:29:57
You're still doing packing though? I don't think t
shend
2017/04/06 04:39:39
For me, packing means the compiler "packing" the s
| |
252 """ | 297 """ |
253 Group a list of fields into buckets to minimise padding. | 298 Returns a list of fields ordered to minimise padding. |
254 Returns a list of buckets, where each bucket is a list of Field objects. | |
255 """ | 299 """ |
300 # Separate out bit fields from normal fields | |
301 bit_fields = [field for field in fields if field.is_bit_field] | |
302 normal_fields = [field for field in fields if not field.is_bit_field] | |
303 | |
256 # Since fields cannot cross word boundaries, in order to minimize | 304 # Since fields cannot cross word boundaries, in order to minimize |
257 # padding, group fields into buckets so that as many buckets as possible | 305 # padding, group fields into buckets so that as many buckets as possible |
258 # are exactly 32 bits. Although this greedy approach may not always | 306 # are exactly 32 bits. Although this greedy approach may not always |
259 # produce the optimal solution, we add a static_assert to the code to | 307 # produce the optimal solution, we add a static_assert to the code to |
260 # ensure ComputedStyleBase results in the expected size. If that | 308 # ensure ComputedStyleBase results in the expected size. If that |
261 # static_assert fails, this code is falling into the small number of | 309 # static_assert fails, this code is falling into the small number of |
262 # cases that are suboptimal, and may need to be rethought. | 310 # cases that are suboptimal, and may need to be rethought. |
263 # For more details on packing bit fields to reduce padding, see: | 311 # For more details on packing bit fields to reduce padding, see: |
264 # http://www.catb.org/esr/structure-packing/#_bitfields | 312 # http://www.catb.org/esr/structure-packing/#_bitfields |
265 field_buckets = [] | 313 field_buckets = [] |
266 # Consider fields in descending order of size to reduce fragmentation | 314 # Consider fields in descending order of size to reduce fragmentation |
267 # when they are selected. Ties broken in alphabetical order by name. | 315 # when they are selected. Ties broken in alphabetical order by name. |
268 for field in sorted(fields, key=lambda f: (-f.size, f.name)): | 316 for field in sorted(bit_fields, key=lambda f: (-f.size, f.name)): |
269 added_to_bucket = False | 317 added_to_bucket = False |
270 # Go through each bucket and add this field if it will not increase | 318 # Go through each bucket and add this field if it will not increase |
271 # the bucket's size to larger than 32 bits. Otherwise, make a new | 319 # the bucket's size to larger than 32 bits. Otherwise, make a new |
272 # bucket containing only this field. | 320 # bucket containing only this field. |
273 for bucket in field_buckets: | 321 for bucket in field_buckets: |
274 if sum(f.size for f in bucket) + field.size <= 32: | 322 if sum(f.size for f in bucket) + field.size <= 32: |
275 bucket.append(field) | 323 bucket.append(field) |
276 added_to_bucket = True | 324 added_to_bucket = True |
277 break | 325 break |
278 if not added_to_bucket: | 326 if not added_to_bucket: |
279 field_buckets.append([field]) | 327 field_buckets.append([field]) |
280 | 328 |
281 return field_buckets | 329 # Normal fields go first, then the bit fields. |
330 return list(normal_fields) + _flatten_list(field_buckets) | |
282 | 331 |
283 | 332 |
284 class ComputedStyleBaseWriter(make_style_builder.StyleBuilderWriter): | 333 class ComputedStyleBaseWriter(make_style_builder.StyleBuilderWriter): |
285 def __init__(self, json5_file_path): | 334 def __init__(self, json5_file_path): |
286 super(ComputedStyleBaseWriter, self).__init__(json5_file_path) | 335 super(ComputedStyleBaseWriter, self).__init__(json5_file_path) |
287 self._outputs = { | 336 self._outputs = { |
288 'ComputedStyleBase.h': self.generate_base_computed_style_h, | 337 'ComputedStyleBase.h': self.generate_base_computed_style_h, |
289 'ComputedStyleBase.cpp': self.generate_base_computed_style_cpp, | 338 'ComputedStyleBase.cpp': self.generate_base_computed_style_cpp, |
290 'ComputedStyleBaseConstants.h': self.generate_base_computed_style_co nstants, | 339 'ComputedStyleBaseConstants.h': self.generate_base_computed_style_co nstants, |
291 'CSSValueIDMappingsGenerated.h': self.generate_css_value_mappings, | 340 'CSSValueIDMappingsGenerated.h': self.generate_css_value_mappings, |
292 } | 341 } |
293 | 342 |
294 # TODO(shend): Remove this once we move NONPROPERTIES to its own JSON fi le, | 343 # TODO(shend): Remove this once we move NONPROPERTIES to its own JSON fi le, |
295 # since the JSON5 reader will handle missing fields and defaults. | 344 # since the JSON5 reader will handle missing fields and defaults. |
296 for property_ in NONPROPERTIES: | 345 for property_ in NONPROPERTIES: |
297 property_['name_for_methods'] = property_['name'] | 346 property_['name_for_methods'] = property_['name'] |
298 if 'field_type_path' not in property_: | 347 if 'field_type_path' not in property_: |
299 property_['field_type_path'] = None | 348 property_['field_type_path'] = None |
300 if 'type_name' not in property_: | 349 if 'type_name' not in property_: |
301 property_['type_name'] = 'E' + enum_type_name(property_['name_fo r_methods']) | 350 property_['type_name'] = 'E' + enum_type_name(property_['name_fo r_methods']) |
351 if 'field_group' not in property_: | |
352 property_['field_group'] = None | |
302 | 353 |
303 property_values = self._properties.values() | 354 property_values = self._properties.values() |
304 | 355 |
305 # Override the type name when field_type_path is specified | 356 # Override the type name when field_type_path is specified |
306 for property_ in property_values: | 357 for property_ in property_values: |
307 if property_['field_type_path']: | 358 if property_['field_type_path']: |
308 property_['type_name'] = property_['field_type_path'].split('/') [-1] | 359 property_['type_name'] = property_['field_type_path'].split('/') [-1] |
309 | 360 |
310 self._generated_enums = _create_enums(property_values + NONPROPERTIES) | 361 self._generated_enums = _create_enums(property_values + NONPROPERTIES) |
311 | 362 |
312 all_fields = (_create_fields('property', property_values) + | 363 fields = (_create_fields('property', property_values) + |
313 _create_fields('nonproperty', NONPROPERTIES)) | 364 _create_fields('nonproperty', NONPROPERTIES)) |
314 | 365 |
315 # Separate the normal fields from the bit fields | 366 # Separate fields into groups |
316 bit_fields = [field for field in all_fields if field.is_bit_field] | 367 self._base_group = _group_fields(fields) |
317 normal_fields = [field for field in all_fields if not field.is_bit_field ] | |
318 | 368 |
319 # Pack bit fields into buckets | 369 # Get include paths |
meade_UTC10
2017/04/06 03:29:57
Is this really a helpful comment? :p
shend
2017/04/06 04:39:40
lol, removed.
| |
320 field_buckets = _pack_fields(bit_fields) | 370 self._include_paths = _get_include_paths(property_values) |
321 | |
322 # The expected size of ComputedStyleBase is equivalent to as many words | |
323 # as the total number of buckets. | |
324 self._expected_bit_field_bytes = len(field_buckets) | |
325 | |
326 # The most optimal size of ComputedStyleBase is the total sum of all the | |
327 # field sizes, rounded up to the nearest word. If this produces the | |
328 # incorrect value, either the packing algorithm is not optimal or there | |
329 # is no way to pack the fields such that excess padding space is not | |
330 # added. | |
331 # If this fails, increase extra_padding_bytes by 1, but be aware that | |
332 # this also increases ComputedStyleBase by 1 word. | |
333 # We should be able to bring extra_padding_bytes back to 0 from time to | |
334 # time. | |
335 extra_padding_bytes = 0 | |
336 optimal_bit_field_bytes = int(math.ceil(sum(f.size for f in bit_fields) / 32.0)) | |
337 real_bit_field_bytes = optimal_bit_field_bytes + extra_padding_bytes | |
338 assert self._expected_bit_field_bytes == real_bit_field_bytes, \ | |
339 ('The field packing algorithm produced %s bytes, optimal is %s bytes ' % | |
340 (self._expected_bit_field_bytes, real_bit_field_bytes)) | |
341 | |
342 # Normal fields go first, then the bit fields. | |
343 self._fields = list(normal_fields) | |
344 | |
345 # Order the fields so fields in each bucket are adjacent. | |
346 for bucket in field_buckets: | |
347 for field in bucket: | |
348 self._fields.append(field) | |
349 | |
350 self._include_paths = _get_include_paths(property_values + NONPROPERTIES ) | |
351 | |
352 | 371 |
353 @template_expander.use_jinja('ComputedStyleBase.h.tmpl') | 372 @template_expander.use_jinja('ComputedStyleBase.h.tmpl') |
354 def generate_base_computed_style_h(self): | 373 def generate_base_computed_style_h(self): |
355 return { | 374 return { |
356 'properties': self._properties, | 375 'properties': self._properties, |
357 'enums': self._generated_enums, | 376 'enums': self._generated_enums, |
358 'include_paths': self._include_paths, | 377 'include_paths': self._include_paths, |
359 'fields': self._fields, | 378 'computed_style': self._base_group, |
360 } | 379 } |
361 | 380 |
362 @template_expander.use_jinja('ComputedStyleBase.cpp.tmpl') | 381 @template_expander.use_jinja('ComputedStyleBase.cpp.tmpl') |
363 def generate_base_computed_style_cpp(self): | 382 def generate_base_computed_style_cpp(self): |
364 return { | 383 return { |
365 'properties': self._properties, | 384 'properties': self._properties, |
366 'enums': self._generated_enums, | 385 'enums': self._generated_enums, |
367 'fields': self._fields, | 386 'computed_style': self._base_group, |
368 'expected_bit_field_bytes': self._expected_bit_field_bytes, | |
369 } | 387 } |
370 | 388 |
371 @template_expander.use_jinja('ComputedStyleBaseConstants.h.tmpl') | 389 @template_expander.use_jinja('ComputedStyleBaseConstants.h.tmpl') |
372 def generate_base_computed_style_constants(self): | 390 def generate_base_computed_style_constants(self): |
373 return { | 391 return { |
374 'properties': self._properties, | 392 'properties': self._properties, |
375 'enums': self._generated_enums, | 393 'enums': self._generated_enums, |
376 'fields': self._fields, | |
377 } | 394 } |
378 | 395 |
379 @template_expander.use_jinja('CSSValueIDMappingsGenerated.h.tmpl') | 396 @template_expander.use_jinja('CSSValueIDMappingsGenerated.h.tmpl') |
380 def generate_css_value_mappings(self): | 397 def generate_css_value_mappings(self): |
381 mappings = {} | 398 mappings = {} |
382 | 399 |
383 for property_ in self._properties.values(): | 400 for property_ in self._properties.values(): |
384 if property_['field_template'] == 'keyword': | 401 if property_['field_template'] == 'keyword': |
385 mappings[property_['type_name']] = { | 402 mappings[property_['type_name']] = { |
386 'default_value': enum_value_name(property_['default_value']) , | 403 'default_value': enum_value_name(property_['default_value']) , |
387 'mapping': [(enum_value_name(k), enum_for_css_keyword(k)) fo r k in property_['keywords']], | 404 'mapping': [(enum_value_name(k), enum_for_css_keyword(k)) fo r k in property_['keywords']], |
388 } | 405 } |
389 | 406 |
390 return { | 407 return { |
391 'include_paths': self._include_paths, | 408 'include_paths': self._include_paths, |
392 'mappings': mappings, | 409 'mappings': mappings, |
393 } | 410 } |
394 | 411 |
395 if __name__ == '__main__': | 412 if __name__ == '__main__': |
396 json5_generator.Maker(ComputedStyleBaseWriter).main() | 413 json5_generator.Maker(ComputedStyleBaseWriter).main() |
OLD | NEW |