| 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, enumerator_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 ] | 33 ] |
| 32 | 34 |
| 33 | 35 |
| 34 class Field(object): | 36 class Field(object): |
| 35 """ | 37 """ |
| 36 The generated ComputedStyle object is made up of a series of Fields. | 38 The generated ComputedStyle object is made up of a series of Fields. |
| 37 Each Field has a name, size, type, etc, and a bunch of attributes to | 39 Each Field has a name, size, type, etc, and a bunch of attributes to |
| 38 determine which methods it will be used in. | 40 determine which methods it will be used in. |
| 39 | 41 |
| 40 A Field also has enough information to use any storage type in C++, such as | 42 A Field also has enough information to use any storage type in C++, such as |
| 41 regular member variables, or more complex storage like vectors or hashmaps. | 43 regular member variables, or more complex storage like vectors or hashmaps. |
| 42 Almost all properties will have at least one Field, often more than one. | 44 Almost all properties will have at least one Field, often more than one. |
| 43 | 45 |
| 44 Fields also fall into various roles, which determine the logic that is | 46 Most attributes in this class correspond to parameters in CSSProperties.json
5. |
| 45 used to generate them. The available field roles are: | 47 See that file for a more detailed explanation of each attribute. |
| 46 - 'property', for fields that store CSS properties | 48 |
| 47 - 'inherited_flag', for single-bit flags that store whether a property is | 49 Attributes: |
| 48 inherited by this style or set explicitly | 50 field_role: The semantic role of the field. Can be: |
| 49 - 'nonproperty', for fields that are not CSS properties | 51 - 'property': for fields that store CSS properties |
| 52 - 'inherited_flag': for single-bit flags that store whether a proper
ty is |
| 53 inherited by this style or set explicitly |
| 54 - 'nonproperty': for fields that are not CSS properties |
| 55 name_for_methods: String used to form the names of getters and setters. |
| 56 Should be in upper camel case. |
| 57 property_name: Name of the property that the field is part of. |
| 58 type_name: Name of the C++ type exposed by the generated interface (e.g.
EClear, int). |
| 59 'field_template': Determines the interface generated for the field. Can
be one of: |
| 60 keyword, flag, or monotonic_flag. |
| 61 'size': Number of bits needed for storage. |
| 62 'default_value': Default value for this field when it is first initializ
ed. |
| 50 """ | 63 """ |
| 51 | 64 |
| 52 # List of required attributes for a field which need to be passed in by | 65 def __init__(self, field_role, name_for_methods, property_name, type_name, |
| 53 # keyword arguments. See CSSProperties.json5 for an explanation of each | 66 field_template, size, default_value, **kwargs): |
| 54 # attribute. | 67 """Creates a new field.""" |
| 55 REQUIRED_ATTRIBUTES = set([ | 68 self.name = class_member_name(name_for_methods) |
| 56 # Name of field | 69 self.property_name = property_name |
| 57 'name', | 70 self.type_name = type_name |
| 58 # Name of property field is for | 71 self.field_template = field_template |
| 59 'property_name', | 72 self.size = size |
| 60 # Name of the type (e.g. EClear, int) | 73 self.default_value = default_value |
| 61 'type_name', | |
| 62 # Affects how the field is generated (keyword, flag, monotonic_flag) | |
| 63 'field_template', | |
| 64 # Bits needed for storage | |
| 65 'size', | |
| 66 # Default value for field | |
| 67 'default_value', | |
| 68 # Method names | |
| 69 'getter_method_name', | |
| 70 'setter_method_name', | |
| 71 'initial_method_name', | |
| 72 'resetter_method_name', | |
| 73 ]) | |
| 74 | |
| 75 def __init__(self, field_role, **kwargs): | |
| 76 # Values common to all fields | |
| 77 # Set attributes from the keyword arguments | |
| 78 for attrib in Field.REQUIRED_ATTRIBUTES: | |
| 79 setattr(self, attrib, kwargs.pop(attrib)) | |
| 80 | 74 |
| 81 # Field role: one of these must be true | 75 # Field role: one of these must be true |
| 82 self.is_property = field_role == 'property' | 76 self.is_property = field_role == 'property' |
| 83 self.is_inherited_flag = field_role == 'inherited_flag' | 77 self.is_inherited_flag = field_role == 'inherited_flag' |
| 84 self.is_nonproperty = field_role == 'nonproperty' | 78 self.is_nonproperty = field_role == 'nonproperty' |
| 85 assert (self.is_property, self.is_inherited_flag, self.is_nonproperty).c
ount(True) == 1, \ | 79 assert (self.is_property, self.is_inherited_flag, self.is_nonproperty).c
ount(True) == 1, \ |
| 86 'Field role has to be exactly one of: property, inherited_flag, nonp
roperty' | 80 'Field role has to be exactly one of: property, inherited_flag, nonp
roperty' |
| 87 | 81 |
| 88 if self.is_property: | 82 if self.is_property: |
| 89 self.is_inherited = kwargs.pop('inherited') | 83 self.is_inherited = kwargs.pop('inherited') |
| 90 self.is_independent = kwargs.pop('independent') | 84 self.is_independent = kwargs.pop('independent') |
| 91 assert self.is_inherited or not self.is_independent, 'Only inherited
fields can be independent' | 85 assert self.is_inherited or not self.is_independent, 'Only inherited
fields can be independent' |
| 92 | 86 |
| 93 self.is_inherited_method_name = kwargs.pop('is_inherited_method_name
') | 87 self.is_inherited_method_name = method_name(name_for_methods + 'IsIn
herited') |
| 94 elif self.is_inherited_flag: | 88 |
| 95 # Inherited flag-only fields | 89 # Method names |
| 96 pass | 90 getter_prefix = 'Get' if name_for_methods == self.type_name else '' |
| 91 self.getter_method_name = method_name(getter_prefix + name_for_methods) |
| 92 self.setter_method_name = method_name('Set' + name_for_methods) |
| 93 self.initial_method_name = method_name('Initial' + name_for_methods) |
| 94 self.resetter_method_name = method_name('Reset' + name_for_methods) |
| 97 | 95 |
| 98 assert len(kwargs) == 0, 'Unexpected arguments provided to Field: ' + st
r(kwargs) | 96 assert len(kwargs) == 0, 'Unexpected arguments provided to Field: ' + st
r(kwargs) |
| 99 | 97 |
| 100 | 98 |
| 101 def _get_include_paths(properties): | 99 def _get_include_paths(properties): |
| 102 """ | 100 """ |
| 103 Get a list of paths that need to be included for ComputedStyleBase. | 101 Get a list of paths that need to be included for ComputedStyleBase. |
| 104 """ | 102 """ |
| 105 include_paths = set() | 103 include_paths = set() |
| 106 for property_ in properties: | 104 for property_ in properties: |
| 107 if property_['field_type_path'] is not None: | 105 if property_['field_type_path'] is not None: |
| 108 include_paths.add(property_['field_type_path'] + '.h') | 106 include_paths.add(property_['field_type_path'] + '.h') |
| 109 return list(sorted(include_paths)) | 107 return list(sorted(include_paths)) |
| 110 | 108 |
| 111 | 109 |
| 112 def _create_enums(properties): | 110 def _create_enums(properties): |
| 113 """ | 111 """ |
| 114 Returns a dictionary of enums to be generated, enum name -> [list of enum va
lues] | 112 Returns a dictionary of enums to be generated, enum name -> [list of enum va
lues] |
| 115 """ | 113 """ |
| 116 enums = {} | 114 enums = {} |
| 117 for property_ in properties: | 115 for property_ in properties: |
| 118 # Only generate enums for keyword properties that use the default field_
type_path. | 116 # Only generate enums for keyword properties that use the default field_
type_path. |
| 119 if property_['field_template'] == 'keyword' and property_['field_type_pa
th'] is None: | 117 if property_['field_template'] == 'keyword' and property_['field_type_pa
th'] is None: |
| 120 enum_name = property_['type_name'] | 118 enum_name = property_['type_name'] |
| 121 # From the Blink style guide: Enum members should use InterCaps with
an initial capital letter. [names-enum-members] | 119 enum_values = [enumerator_name(k) for k in property_['keywords']] |
| 122 enum_values = [('k' + camel_case(k)) for k in property_['keywords']] | |
| 123 | 120 |
| 124 if enum_name in enums: | 121 if enum_name in enums: |
| 125 # There's an enum with the same name, check if the enum values a
re the same | 122 # There's an enum with the same name, check if the enum values a
re the same |
| 126 assert set(enums[enum_name]) == set(enum_values), \ | 123 assert set(enums[enum_name]) == set(enum_values), \ |
| 127 ("'" + property_['name'] + "' can't have type_name '" + enum
_name + "' " | 124 ("'" + property_['name'] + "' can't have type_name '" + enum
_name + "' " |
| 128 "because it was used by a previous property, but with a dif
ferent set of keywords. " | 125 "because it was used by a previous property, but with a dif
ferent set of keywords. " |
| 129 "Either give it a different name or ensure the keywords are
the same.") | 126 "Either give it a different name or ensure the keywords are
the same.") |
| 130 | 127 |
| 131 enums[enum_name] = enum_values | 128 enums[enum_name] = enum_values |
| 132 | 129 |
| 133 return enums | 130 return enums |
| 134 | 131 |
| 135 | 132 |
| 136 def _create_property_field(property_): | 133 def _create_property_field(property_): |
| 137 """ | 134 """ |
| 138 Create a property field from a CSS property and return the Field object. | 135 Create a property field from a CSS property and return the Field object. |
| 139 """ | 136 """ |
| 140 property_name = property_['name_for_methods'] | 137 name_for_methods = property_['name_for_methods'] |
| 141 property_name_lower = lower_first(property_name) | |
| 142 | 138 |
| 143 # From the Blink style guide: Other data members should be prefixed by "m_".
[names-data-members] | |
| 144 field_name = 'm_' + property_name_lower | |
| 145 bits_needed = math.log(len(property_['keywords']), 2) # TODO: implement for
non-enums | 139 bits_needed = math.log(len(property_['keywords']), 2) # TODO: implement for
non-enums |
| 146 type_name = property_['type_name'] | 140 type_name = property_['type_name'] |
| 147 | 141 |
| 148 # For now, the getter name should match the field name. Later, getter names | |
| 149 # will start with an uppercase letter, so if they conflict with the type nam
e, | |
| 150 # add 'get' to the front. | |
| 151 getter_method_name = property_name_lower | |
| 152 if type_name == property_name: | |
| 153 getter_method_name = 'get' + property_name | |
| 154 | |
| 155 assert property_['initial_keyword'] is not None, \ | 142 assert property_['initial_keyword'] is not None, \ |
| 156 ('MakeComputedStyleBase requires an initial keyword for keyword fields,
none specified ' | 143 ('MakeComputedStyleBase requires an initial keyword for keyword fields,
none specified ' |
| 157 'for property ' + property_['name']) | 144 'for property ' + property_['name']) |
| 158 default_value = type_name + '::k' + camel_case(property_['initial_keyword']) | 145 default_value = type_name + '::' + enumerator_name(property_['initial_keywor
d']) |
| 159 | 146 |
| 160 return Field( | 147 return Field( |
| 161 'property', | 148 'property', |
| 162 name=field_name, | 149 name_for_methods, |
| 163 property_name=property_['name'], | 150 property_name=property_['name'], |
| 164 inherited=property_['inherited'], | 151 inherited=property_['inherited'], |
| 165 independent=property_['independent'], | 152 independent=property_['independent'], |
| 166 type_name=type_name, | 153 type_name=type_name, |
| 167 field_template=property_['field_template'], | 154 field_template=property_['field_template'], |
| 168 size=int(math.ceil(bits_needed)), | 155 size=int(math.ceil(bits_needed)), |
| 169 default_value=default_value, | 156 default_value=default_value, |
| 170 getter_method_name=getter_method_name, | |
| 171 setter_method_name='set' + property_name, | |
| 172 initial_method_name='initial' + property_name, | |
| 173 resetter_method_name='reset' + property_name, | |
| 174 is_inherited_method_name=property_name_lower + 'IsInherited', | |
| 175 ) | 157 ) |
| 176 | 158 |
| 177 | 159 |
| 178 def _create_inherited_flag_field(property_): | 160 def _create_inherited_flag_field(property_): |
| 179 """ | 161 """ |
| 180 Create the field used for an inheritance fast path from an independent CSS p
roperty, | 162 Create the field used for an inheritance fast path from an independent CSS p
roperty, |
| 181 and return the Field object. | 163 and return the Field object. |
| 182 """ | 164 """ |
| 183 property_name = property_['name_for_methods'] | |
| 184 property_name_lower = lower_first(property_name) | |
| 185 | |
| 186 field_name_suffix_upper = property_name + 'IsInherited' | |
| 187 field_name_suffix_lower = property_name_lower + 'IsInherited' | |
| 188 | |
| 189 return Field( | 165 return Field( |
| 190 'inherited_flag', | 166 'inherited_flag', |
| 191 name='m_' + field_name_suffix_lower, | 167 property_['name_for_methods'] + 'IsInherited', |
| 192 property_name=property_['name'], | 168 property_name=property_['name'], |
| 193 type_name='bool', | 169 type_name='bool', |
| 194 field_template='flag', | 170 field_template='flag', |
| 195 size=1, | 171 size=1, |
| 196 default_value='true', | 172 default_value='true', |
| 197 getter_method_name=field_name_suffix_lower, | |
| 198 setter_method_name='set' + field_name_suffix_upper, | |
| 199 initial_method_name='initial' + field_name_suffix_upper, | |
| 200 resetter_method_name='reset' + field_name_suffix_upper, | |
| 201 ) | 173 ) |
| 202 | 174 |
| 203 | 175 |
| 204 def _create_nonproperty_field(property_): | 176 def _create_nonproperty_field(property_): |
| 205 """ | 177 """ |
| 206 Create a nonproperty field from an entry in NONPROPERTY_FIELDS and return th
e Field object. | 178 Create a nonproperty field from an entry in NONPROPERTY_FIELDS and return th
e Field object. |
| 207 """ | 179 """ |
| 208 # TODO(shend): Make this work for nonflags | 180 # TODO(shend): Make this work for nonflags |
| 209 assert property_['field_template'] in ('flag', 'monotonic_flag'), \ | 181 assert property_['field_template'] in ('flag', 'monotonic_flag'), \ |
| 210 "Nonproperties with arbitrary templates are not yet supported" | 182 "Nonproperties with arbitrary templates are not yet supported" |
| 211 member_name = 'm_' + property_['name'] | |
| 212 field_name_upper = upper_first_letter(property_['name']) | |
| 213 | 183 |
| 214 return Field( | 184 return Field( |
| 215 'nonproperty', | 185 'nonproperty', |
| 216 name=member_name, | 186 property_['name'], |
| 217 property_name=property_['name'], | 187 property_name=property_['name'], |
| 218 type_name='bool', | 188 type_name='bool', |
| 219 field_template=property_['field_template'], | 189 field_template=property_['field_template'], |
| 220 size=1, | 190 size=1, |
| 221 default_value='false', | 191 default_value='false', |
| 222 getter_method_name=property_['name'], | |
| 223 setter_method_name='set' + field_name_upper, | |
| 224 initial_method_name='initial' + field_name_upper, | |
| 225 resetter_method_name='reset' + field_name_upper, | |
| 226 ) | 192 ) |
| 227 | 193 |
| 228 | 194 |
| 229 def _create_fields(properties): | 195 def _create_fields(properties): |
| 230 """ | 196 """ |
| 231 Create ComputedStyle fields from CSS properties and return a list of Field o
bjects. | 197 Create ComputedStyle fields from CSS properties and return a list of Field o
bjects. |
| 232 """ | 198 """ |
| 233 fields = [] | 199 fields = [] |
| 234 for property_ in properties: | 200 for property_ in properties: |
| 235 # Only generate properties that have a field template | 201 # Only generate properties that have a field template |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 365 'expected_total_field_bytes': self._expected_total_field_bytes, | 331 'expected_total_field_bytes': self._expected_total_field_bytes, |
| 366 } | 332 } |
| 367 | 333 |
| 368 @template_expander.use_jinja('CSSValueIDMappingsGenerated.h.tmpl') | 334 @template_expander.use_jinja('CSSValueIDMappingsGenerated.h.tmpl') |
| 369 def generate_css_value_mappings(self): | 335 def generate_css_value_mappings(self): |
| 370 mappings = {} | 336 mappings = {} |
| 371 | 337 |
| 372 for property_ in self._properties.values(): | 338 for property_ in self._properties.values(): |
| 373 if property_['field_template'] == 'keyword': | 339 if property_['field_template'] == 'keyword': |
| 374 mappings[property_['type_name']] = { | 340 mappings[property_['type_name']] = { |
| 375 'default_value': 'k' + camel_case(property_['initial_keyword
']), | 341 'default_value': enumerator_name(property_['initial_keyword'
]), |
| 376 'mapping': [('k' + camel_case(k), enum_for_css_keyword(k)) f
or k in property_['keywords']], | 342 'mapping': [(enumerator_name(k), enum_for_css_keyword(k)) fo
r k in property_['keywords']], |
| 377 } | 343 } |
| 378 | 344 |
| 379 return { | 345 return { |
| 380 'include_paths': self._include_paths, | 346 'include_paths': self._include_paths, |
| 381 'mappings': mappings, | 347 'mappings': mappings, |
| 382 } | 348 } |
| 383 | 349 |
| 384 if __name__ == '__main__': | 350 if __name__ == '__main__': |
| 385 json5_generator.Maker(ComputedStyleBaseWriter).main() | 351 json5_generator.Maker(ComputedStyleBaseWriter).main() |
| OLD | NEW |