| 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 |