Ejemplo n.º 1
0
    def __init__(self, file_paths):
        assert len(file_paths) >= 3, \
            "CSSProperties at least needs both css_properties.json5, \
            computed_style_field_aliases.json5 and \
            runtime_enabled_features.json5 to function"

        # computed_style_field_aliases.json5. Used to expand out parameters used
        # in the various generators for ComputedStyle.
        self._field_alias_expander = FieldAliasExpander(file_paths[1])

        # _alias_offset must be a power of 2.
        self._alias_offset = 1024
        # 0: CSSPropertyID::kInvalid
        # 1: CSSPropertyID::kVariable
        self._first_enum_value = 2
        self._last_used_enum_value = self._first_enum_value
        self._last_high_priority_property = None

        self._properties_by_id = {}
        self._properties_by_name = {}
        self._aliases = []
        self._longhands = []
        self._shorthands = []
        self._properties_including_aliases = []

        # Add default data in css_properties.json5. This must be consistent
        # across instantiations of this class.
        css_properties_file = json5_generator.Json5File.load_from_files(
            [file_paths[0]])
        self._default_parameters = css_properties_file.parameters
        # Map of feature name -> origin trial feature name
        origin_trial_features = {}
        # TODO(crbug/1031309): Refactor OriginTrialsWriter to reuse logic here.
        origin_trials_writer = OriginTrialsWriter([file_paths[2]], "")
        for feature in origin_trials_writer.origin_trial_features:
            origin_trial_features[str(feature['name'])] = True

        self.add_properties(css_properties_file.name_dictionaries,
                            origin_trial_features)

        assert self._first_enum_value + len(self._properties_by_id) < \
            self._alias_offset, \
            'Property aliasing expects fewer than %d properties.' % \
            self._alias_offset
        self._last_unresolved_property_id = max(property_["enum_value"]
                                                for property_ in self._aliases)

        # Process extra files passed in.
        self._extra_fields = []
        for i in range(3, len(file_paths)):
            fields = json5_generator.Json5File.load_from_files(
                [file_paths[i]], default_parameters=self._default_parameters)
            self._extra_fields.extend(fields.name_dictionaries)
        for field in self._extra_fields:
            self.expand_parameters(field)
            validate_field(field)
Ejemplo n.º 2
0
    def __init__(self, file_paths):
        assert len(file_paths) >= 2, \
            "CSSProperties at least needs both css_properties.json5 and \
            computed_style_field_aliases.json5 to function"

        # computed_style_field_aliases.json5. Used to expand out parameters used
        # in the various generators for ComputedStyle.
        self._field_alias_expander = FieldAliasExpander(file_paths[1])

        # CSSPropertyValueMetadata assumes that there are at most 1024
        # properties + aliases.
        self._alias_offset = 512
        # 0: CSSPropertyID::kInvalid
        # 1: CSSPropertyID::kVariable
        self._first_enum_value = 2
        self._last_used_enum_value = self._first_enum_value

        self._properties_by_id = {}
        self._properties_by_name = {}
        self._aliases = []
        self._longhands = []
        self._shorthands = []
        self._properties_including_aliases = []

        # Add default data in css_properties.json5. This must be consistent
        # across instantiations of this class.
        css_properties_file = json5_generator.Json5File.load_from_files(
            [file_paths[0]])
        self._default_parameters = css_properties_file.parameters
        self.add_properties(css_properties_file.name_dictionaries)

        assert self._first_enum_value + len(self._properties_by_id) < \
            self._alias_offset, \
            'Property aliasing expects fewer than %d properties.' % \
            self._alias_offset
        self._last_unresolved_property_id = max(property_["enum_value"]
                                                for property_ in self._aliases)

        # Process extra files passed in.
        self._extra_fields = []
        for i in range(2, len(file_paths)):
            fields = json5_generator.Json5File.load_from_files(
                [file_paths[i]], default_parameters=self._default_parameters)
            self._extra_fields.extend(fields.name_dictionaries)
        for field in self._extra_fields:
            self.expand_parameters(field)
Ejemplo n.º 3
0
class CSSProperties(object):
    def __init__(self, file_paths):
        assert len(file_paths) >= 3, \
            "CSSProperties at least needs both css_properties.json5, \
            computed_style_field_aliases.json5 and \
            runtime_enabled_features.json5 to function"

        # computed_style_field_aliases.json5. Used to expand out parameters used
        # in the various generators for ComputedStyle.
        self._field_alias_expander = FieldAliasExpander(file_paths[1])

        # _alias_offset must be a power of 2.
        self._alias_offset = 1024
        # 0: CSSPropertyID::kInvalid
        # 1: CSSPropertyID::kVariable
        self._first_enum_value = 2
        self._last_used_enum_value = self._first_enum_value
        self._last_high_priority_property = None

        self._properties_by_id = {}
        self._properties_by_name = {}
        self._aliases = []
        self._longhands = []
        self._shorthands = []
        self._properties_including_aliases = []

        # Add default data in css_properties.json5. This must be consistent
        # across instantiations of this class.
        css_properties_file = json5_generator.Json5File.load_from_files(
            [file_paths[0]])
        self._default_parameters = css_properties_file.parameters
        # Map of feature name -> origin trial feature name
        origin_trial_features = {}
        # TODO(crbug/1031309): Refactor OriginTrialsWriter to reuse logic here.
        origin_trials_writer = OriginTrialsWriter([file_paths[2]], "")
        for feature in origin_trials_writer.origin_trial_features:
            origin_trial_features[str(feature['name'])] = True

        self.add_properties(css_properties_file.name_dictionaries,
                            origin_trial_features)

        assert self._first_enum_value + len(self._properties_by_id) < \
            self._alias_offset, \
            'Property aliasing expects fewer than %d properties.' % \
            self._alias_offset
        self._last_unresolved_property_id = max(property_["enum_value"]
                                                for property_ in self._aliases)

        # Process extra files passed in.
        self._extra_fields = []
        for i in range(3, len(file_paths)):
            fields = json5_generator.Json5File.load_from_files(
                [file_paths[i]], default_parameters=self._default_parameters)
            self._extra_fields.extend(fields.name_dictionaries)
        for field in self._extra_fields:
            self.expand_parameters(field)
            validate_field(field)

    def add_properties(self, properties, origin_trial_features):
        for property_ in properties:
            self._properties_by_name[property_['name'].original] = property_

        for property_ in properties:
            property_['is_shorthand'] = \
                property_['is_property'] and bool(property_['longhands'])
            property_['is_longhand'] = \
                property_['is_property'] and not property_['is_shorthand']
            self.expand_visited(property_)
            property_['in_origin_trial'] = False
            self.expand_origin_trials(property_, origin_trial_features)
            self.expand_surrogate(property_)

        self._aliases = [
            property_ for property_ in properties if property_['alias_for']
        ]
        self._shorthands = [
            property_ for property_ in properties if property_['longhands']
        ]
        self._longhands = [
            property_ for property_ in properties
            if (not property_['alias_for'] and not property_['longhands'])
        ]

        # Sort the properties by priority, then alphabetically. Ensure that
        # the resulting order is deterministic.
        # Sort properties by priority, then alphabetically.
        for property_ in self._longhands + self._shorthands:
            self.expand_parameters(property_)
            validate_property(property_)
            priority_numbers = {'High': 0, 'Low': 1}
            priority = priority_numbers[property_['priority']]
            name_without_leading_dash = property_['name'].original
            if name_without_leading_dash.startswith('-'):
                name_without_leading_dash = name_without_leading_dash[1:]
            property_['sorting_key'] = (priority, name_without_leading_dash)

        sorting_keys = {}
        for property_ in self._longhands + self._shorthands:
            key = property_['sorting_key']
            assert key not in sorting_keys, \
                ('Collision detected - two properties have the same name and '
                 'priority, a potentially non-deterministic ordering can '
                 'occur: {}, {} and {}'.format(
                     key, property_['name'].original, sorting_keys[key]))
            sorting_keys[key] = property_['name'].original
        self._longhands.sort(key=lambda p: p['sorting_key'])
        self._shorthands.sort(key=lambda p: p['sorting_key'])

        # The sorted index becomes the CSSPropertyID enum value.
        for property_ in self._longhands + self._shorthands:
            property_['enum_value'] = self._last_used_enum_value
            self._last_used_enum_value += 1
            # Add the new property into the map of properties.
            assert property_['property_id'] not in self._properties_by_id, \
                ('property with ID {} appears more than once in the '
                 'properties list'.format(property_['property_id']))
            self._properties_by_id[property_['property_id']] = property_
            if property_['priority'] == 'High':
                self._last_high_priority_property = property_

        self.expand_aliases()
        self._properties_including_aliases = self._longhands + \
            self._shorthands + self._aliases

    def expand_origin_trials(self, property_, origin_trial_features):
        if not property_['runtime_flag']:
            return
        if property_['runtime_flag'] in origin_trial_features:
            property_['in_origin_trial'] = True

    def expand_visited(self, property_):
        if not property_['visited_property_for']:
            return
        visited_property_for = property_['visited_property_for']
        unvisited_property = self._properties_by_name[visited_property_for]
        property_['visited'] = True
        # The visited property needs a link to the unvisited counterpart.
        property_['unvisited_property'] = unvisited_property
        # The unvisited property needs a link to the visited counterpart.
        assert 'visited_property' not in unvisited_property, \
            'A property may not have multiple visited properties'
        unvisited_property['visited_property'] = property_

    def expand_surrogate(self, property_):
        if not property_['surrogate_for']:
            return
        assert property_['surrogate_for'] in self._properties_by_name, \
            'surrogate_for must name a property'
        # Upgrade 'surrogate_for' to property reference.
        property_['surrogate_for'] = self._properties_by_name[
            property_['surrogate_for']]

    def expand_aliases(self):
        for i, alias in enumerate(self._aliases):
            validate_alias(alias)
            aliased_property = self._properties_by_id[id_for_css_property(
                alias['alias_for'])]
            aliased_property.setdefault('aliases', [])
            aliased_property['aliases'].append(alias['name'].original)
            updated_alias = aliased_property.copy()
            updated_alias['name'] = alias['name']
            updated_alias['alias_for'] = alias['alias_for']
            updated_alias['aliased_property'] = aliased_property[
                'name'].to_upper_camel_case()
            updated_alias['property_id'] = id_for_css_property_alias(
                alias['name'])
            updated_alias['enum_key'] = enum_key_for_css_property_alias(
                alias['name'])
            updated_alias['enum_value'] = aliased_property['enum_value'] + \
                self._alias_offset * len(aliased_property['aliases'])
            updated_alias['superclass'] = 'CSSUnresolvedProperty'
            updated_alias['namespace_group'] = \
                'Shorthand' if aliased_property['longhands'] else 'Longhand'
            self._aliases[i] = updated_alias

    def expand_parameters(self, property_):
        def set_if_none(property_, key, value):
            if key not in property_ or property_[key] is None:
                property_[key] = value

        # Basic info.
        name = property_['name']
        property_['property_id'] = id_for_css_property(name)
        property_['enum_key'] = enum_key_for_css_property(name)
        property_['is_internal'] = name.original.startswith('-internal-')
        method_name = property_['name_for_methods']
        if not method_name:
            method_name = name.to_upper_camel_case().replace('Webkit', '')
        set_if_none(property_, 'inherited', False)

        # Initial function, Getters and Setters for ComputedStyle.
        set_if_none(property_, 'initial', 'Initial' + method_name)
        simple_type_name = str(property_['type_name']).split('::')[-1]
        set_if_none(property_, 'name_for_methods', method_name)
        set_if_none(property_, 'type_name', 'E' + method_name)
        set_if_none(
            property_, 'getter',
            method_name if simple_type_name != method_name else 'Get' +
            method_name)
        set_if_none(property_, 'setter', 'Set' + method_name)
        if property_['inherited']:
            property_['is_inherited_setter'] = ('Set' + method_name +
                                                'IsInherited')
        property_['is_animation_property'] = (
            property_['priority'] == 'Animation')

        # Figure out whether this property should have style builders at all.
        # E.g. shorthands do not get style builders.
        property_['style_builder_declare'] = (property_['is_property']
                                              and not property_['longhands'])

        # Figure out whether we should generate style builder implementations.
        for x in ['initial', 'inherit', 'value']:
            suppressed = x in property_['style_builder_custom_functions']
            declared = property_['style_builder_declare']
            property_['style_builder_generate_%s' % x] = (declared
                                                          and not suppressed)

        # Expand StyleBuilderConverter params where necessary.
        if property_['type_name'] in PRIMITIVE_TYPES:
            set_if_none(property_, 'converter', 'CSSPrimitiveValue')
        else:
            set_if_none(property_, 'converter', 'CSSIdentifierValue')

        assert not property_['alias_for'], \
            'Use expand_aliases to expand aliases'
        if not property_['longhands']:
            property_['superclass'] = 'Longhand'
            property_['namespace_group'] = 'Longhand'
        elif property_['longhands']:
            property_['superclass'] = 'Shorthand'
            property_['namespace_group'] = 'Shorthand'

        # Expand out field templates.
        if property_['field_template']:
            self._field_alias_expander.expand_field_alias(property_)

            type_name = property_['type_name']
            if (property_['field_template'] == 'keyword'
                    or property_['field_template'] == 'multi_keyword'):
                default_value = (type_name + '::' + NameStyleConverter(
                    property_['default_value']).to_enum_value())
            elif (property_['field_template'] == 'external'
                  or property_['field_template'] == 'primitive'
                  or property_['field_template'] == 'pointer'):
                default_value = property_['default_value']
            else:
                assert property_['field_template'] == 'monotonic_flag', \
                    "Please put a valid value for field_template; got " + \
                    str(property_['field_template'])
                property_['type_name'] = 'bool'
                default_value = 'false'
            property_['default_value'] = default_value

            property_['unwrapped_type_name'] = property_['type_name']
            if property_['wrapper_pointer_name']:
                assert property_['field_template'] in ['pointer', 'external']
                if property_['field_template'] == 'external':
                    property_['type_name'] = '{}<{}>'.format(
                        property_['wrapper_pointer_name'], type_name)

        # Default values for extra parameters in computed_style_extra_fields.json5.
        set_if_none(property_, 'custom_copy', False)
        set_if_none(property_, 'custom_compare', False)
        set_if_none(property_, 'mutable', False)

        if property_['logical_property_group']:
            group = property_['logical_property_group']
            assert 'name' in group, 'name option is required'
            assert 'resolver' in group, 'resolver option is required'
            logicals = {
                'block', 'inline', 'block-start', 'block-end', 'inline-start',
                'inline-end', 'start-start', 'start-end', 'end-start',
                'end-end'
            }
            physicals = {
                'vertical', 'horizontal', 'top', 'bottom', 'left', 'right',
                'top-left', 'top-right', 'bottom-right', 'bottom-left'
            }
            if group['resolver'] in logicals:
                group['is_logical'] = True
            elif group['resolver'] in physicals:
                group['is_logical'] = False
            else:
                assert 0, 'invalid resolver option'
            group['name'] = NameStyleConverter(group['name'])
            group['resolver_name'] = NameStyleConverter(group['resolver'])
            if not property_['style_builder_template'] and group['is_logical']:
                property_['style_builder_template'] = 'direction_aware'

    @property
    def default_parameters(self):
        return self._default_parameters

    @property
    def aliases(self):
        return self._aliases

    @property
    def shorthands(self):
        return self._shorthands

    @property
    def shorthands_including_aliases(self):
        return self._shorthands + [x for x in self._aliases if x['longhands']]

    @property
    def longhands(self):
        return self._longhands

    @property
    def longhands_including_aliases(self):
        return self._longhands + [
            x for x in self._aliases if not x['longhands']
        ]

    @property
    def properties_by_name(self):
        return self._properties_by_name

    @property
    def properties_by_id(self):
        return self._properties_by_id

    @property
    def properties_including_aliases(self):
        return self._properties_including_aliases

    @property
    def first_property_id(self):
        return self._first_enum_value

    @property
    def last_property_id(self):
        return self._first_enum_value + len(self._properties_by_id) - 1

    @property
    def last_unresolved_property_id(self):
        return self._last_unresolved_property_id

    @property
    def last_high_priority_property_id(self):
        return self._last_high_priority_property['enum_key']

    @property
    def property_id_bit_length(self):
        return int.bit_length(self._last_unresolved_property_id)

    @property
    def alias_offset(self):
        return self._alias_offset

    @property
    def extra_fields(self):
        return self._extra_fields
Ejemplo n.º 4
0
class CSSProperties(object):
    def __init__(self, file_paths):
        assert len(file_paths) >= 2, \
            "CSSProperties at least needs both CSSProperties.json5 and \
            ComputedStyleFieldAliases.json5 to function"

        # ComputedStyleFieldAliases.json5. Used to expand out parameters used
        # in the various generators for ComputedStyle.
        self._field_alias_expander = FieldAliasExpander(file_paths[1])

        # CSSPropertyValueMetadata assumes that there are at most 1024
        # properties + aliases.
        self._alias_offset = 512
        # 0: CSSPropertyInvalid
        # 1: CSSPropertyVariable
        self._first_enum_value = 2
        self._last_used_enum_value = self._first_enum_value

        self._properties_by_id = {}
        self._aliases = []
        self._longhands = []
        self._shorthands = []
        self._properties_including_aliases = []

        # Add default data in CSSProperties.json5. This must be consistent
        # across instantiations of this class.
        css_properties_file = json5_generator.Json5File.load_from_files(
            [file_paths[0]])
        self._default_parameters = css_properties_file.parameters
        self.add_properties(css_properties_file.name_dictionaries)

        assert self._first_enum_value + len(self._properties_by_id) < \
            self._alias_offset, \
            'Property aliasing expects fewer than %d properties.' % \
            self._alias_offset
        self._last_unresolved_property_id = max(property_["enum_value"]
                                                for property_ in self._aliases)

        # Process extra files passed in.
        self._extra_fields = []
        for i in range(2, len(file_paths)):
            fields = json5_generator.Json5File.load_from_files(
                [file_paths[i]], default_parameters=self._default_parameters)
            self._extra_fields.extend(fields.name_dictionaries)
        for field in self._extra_fields:
            self.expand_parameters(field)

    def add_properties(self, properties):
        self._aliases = [
            property_ for property_ in properties if property_['alias_for']
        ]
        self._shorthands = [
            property_ for property_ in properties if property_['longhands']
        ]
        self._longhands = [
            property_ for property_ in properties
            if (not property_['alias_for'] and not property_['longhands'])
        ]

        # Sort the properties by priority, then alphabetically. Ensure that
        # the resulting order is deterministic.
        # Sort properties by priority, then alphabetically.
        for property_ in self._longhands + self._shorthands:
            self.expand_parameters(property_)
            check_property_parameters(property_)
            # This order must match the order in CSSPropertyPriority.h.
            priority_numbers = {'Animation': 0, 'High': 1, 'Low': 2}
            priority = priority_numbers[property_['priority']]
            name_without_leading_dash = property_['name'].original
            if name_without_leading_dash.startswith('-'):
                name_without_leading_dash = name_without_leading_dash[1:]
            property_['sorting_key'] = (priority, name_without_leading_dash)

        sorting_keys = {}
        for property_ in self._longhands + self._shorthands:
            key = property_['sorting_key']
            assert key not in sorting_keys, \
                ('Collision detected - two properties have the same name and '
                 'priority, a potentially non-deterministic ordering can '
                 'occur: {}, {} and {}'.format(
                     key, property_['name'].original, sorting_keys[key]))
            sorting_keys[key] = property_['name'].original
        self._longhands.sort(key=lambda p: p['sorting_key'])
        self._shorthands.sort(key=lambda p: p['sorting_key'])

        # The sorted index becomes the CSSPropertyID enum value.
        for property_ in self._longhands + self._shorthands:
            property_['enum_value'] = self._last_used_enum_value
            self._last_used_enum_value += 1
            # Add the new property into the map of properties.
            assert property_['property_id'] not in self._properties_by_id, \
                ('property with ID {} appears more than once in the '
                 'properties list'.format(property_['property_id']))
            self._properties_by_id[property_['property_id']] = property_

        self.expand_aliases()
        self._properties_including_aliases = self._longhands + \
            self._shorthands + self._aliases

    def expand_aliases(self):
        for i, alias in enumerate(self._aliases):
            assert not alias['runtime_flag'], \
                "Property '{}' is an alias with a runtime_flag, "\
                "but runtime flags do not currently work for aliases.".format(
                    alias['name'])
            aliased_property = self._properties_by_id[enum_for_css_property(
                alias['alias_for'])]
            updated_alias = aliased_property.copy()
            updated_alias['name'] = alias['name']
            updated_alias['alias_for'] = alias['alias_for']
            updated_alias['aliased_property'] = aliased_property[
                'name'].to_upper_camel_case()
            updated_alias['property_id'] = enum_for_css_property_alias(
                alias['name'])
            updated_alias['enum_value'] = aliased_property['enum_value'] + \
                self._alias_offset
            self._aliases[i] = updated_alias

    def expand_parameters(self, property_):
        def set_if_none(property_, key, value):
            if key not in property_ or property_[key] is None:
                property_[key] = value

        # Basic info.
        name = property_['name']
        property_['property_id'] = enum_for_css_property(name)
        property_['is_internal'] = name.original.startswith('-internal-')
        method_name = property_['name_for_methods']
        if not method_name:
            method_name = name.to_upper_camel_case().replace('Webkit', '')
        set_if_none(property_, 'inherited', False)

        # Initial function, Getters and Setters for ComputedStyle.
        property_['initial'] = 'Initial' + method_name
        simple_type_name = str(property_['type_name']).split('::')[-1]
        set_if_none(property_, 'name_for_methods', method_name)
        set_if_none(property_, 'type_name', 'E' + method_name)
        set_if_none(
            property_, 'getter',
            method_name if simple_type_name != method_name else 'Get' +
            method_name)
        set_if_none(property_, 'setter', 'Set' + method_name)
        if property_['inherited']:
            property_[
                'is_inherited_setter'] = 'Set' + method_name + 'IsInherited'

        # Figure out whether we should generate style builder implementations.
        for x in ['initial', 'inherit', 'value']:
            suppressed = x in property_['style_builder_custom_functions']
            property_['style_builder_generate_%s' % x] = not suppressed

        # Expand StyleBuilderConverter params where necessary.
        if property_['type_name'] in PRIMITIVE_TYPES:
            set_if_none(property_, 'converter', 'CSSPrimitiveValue')
        else:
            set_if_none(property_, 'converter', 'CSSIdentifierValue')

        # Expand out field templates.
        if property_['field_template']:
            self._field_alias_expander.expand_field_alias(property_)

            type_name = property_['type_name']
            if (property_['field_template'] == 'keyword'
                    or property_['field_template'] == 'multi_keyword'):
                default_value = (type_name + '::' + NameStyleConverter(
                    property_['default_value']).to_enum_value())
            elif (property_['field_template'] == 'external'
                  or property_['field_template'] == 'primitive'
                  or property_['field_template'] == 'pointer'):
                default_value = property_['default_value']
            else:
                assert property_['field_template'] == 'monotonic_flag', \
                    "Please put a valid value for field_template; got " + \
                    str(property_['field_template'])
                property_['type_name'] = 'bool'
                default_value = 'false'
            property_['default_value'] = default_value

            property_['unwrapped_type_name'] = property_['type_name']
            if property_['wrapper_pointer_name']:
                assert property_['field_template'] in ['pointer', 'external']
                if property_['field_template'] == 'external':
                    property_['type_name'] = '{}<{}>'.format(
                        property_['wrapper_pointer_name'], type_name)

        # Default values for extra parameters in ComputedStyleExtraFields.json5.
        set_if_none(property_, 'custom_copy', False)
        set_if_none(property_, 'custom_compare', False)
        set_if_none(property_, 'mutable', False)

        if property_['direction_aware_options'] and not property_[
                'style_builder_template']:
            property_['style_builder_template'] = 'direction_aware'

    @property
    def default_parameters(self):
        return self._default_parameters

    @property
    def aliases(self):
        return self._aliases

    @property
    def shorthands(self):
        return self._shorthands

    @property
    def longhands(self):
        return self._longhands

    @property
    def properties_by_id(self):
        return self._properties_by_id

    @property
    def properties_including_aliases(self):
        return self._properties_including_aliases

    @property
    def first_property_id(self):
        return self._first_enum_value

    @property
    def last_property_id(self):
        return self._first_enum_value + len(self._properties_by_id) - 1

    @property
    def last_unresolved_property_id(self):
        return self._last_unresolved_property_id

    @property
    def alias_offset(self):
        return self._alias_offset

    @property
    def extra_fields(self):
        return self._extra_fields