chromium/third_party/blink/renderer/build/scripts/core/css/make_style_shorthands.py

#!/usr/bin/env python
# Copyright (C) 2013 Intel Corporation. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#     * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
#     * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from core.css import css_properties
from collections import defaultdict
import json5_generator
from name_utilities import enum_key_for_css_property
import template_expander


def collect_runtime_flags(properties):
    """Returns a list of unique runtime flags used by the properties"""
    flags = {p.runtime_flag for p in properties if p.runtime_flag}
    return sorted(flags)


class Expansion(object):
    """A specific (longhand) expansion of a shorthand.

    A shorthand may have multiple expansions, because some of the longhands
    might be behind runtime flags.

    The enabled_mask represents which flags are enabled/disabled for this
    specific expansion. For example, if flags contains three elements,
    and enabled_mask is 0b100, then flags[0] is disabled, flags[1] is disabled,
    and flags[2] is enabled. This information is used to produce the correct
    list of longhands corresponding to the runtime flags that are enabled/
    disabled.
    """

    def __init__(self, longhands, flags, enabled_mask):
        super(Expansion, self).__init__()
        self._longhands = longhands
        self._flags = flags
        self._enabled_mask = enabled_mask

    def is_enabled(self, flag):
        return (1 << self._flags.index(flag)) & self._enabled_mask

    @property
    def is_empty(self):
        return len(self.enabled_longhands) == 0

    @property
    def enabled_longhands(self):
        include = lambda longhand: not longhand.runtime_flag or self.is_enabled(
            longhand.runtime_flag)
        return list(filter(include, self._longhands))

    @property
    def index(self):
        return self._enabled_mask

    @property
    def flags(self):
        return [
            dict(name=flag, enabled=self.is_enabled(flag))
            for flag in self._flags
        ]


def create_expansions(longhands):
    flags = collect_runtime_flags(longhands)
    expansions = list(
        map(lambda mask: Expansion(longhands, flags, mask),
            range(1 << len(flags))))
    assert len(expansions) > 0
    # We generate 2^N expansions for N flags, so enforce some limit.
    assert len(flags) <= 4, 'Too many runtime flags for a single shorthand'
    return expansions


class StylePropertyShorthandWriter(json5_generator.Writer):
    class_name = 'StylePropertyShorthand'
    _FILE_BASENAME = 'style_property_shorthand'

    def __init__(self, json5_file_paths, output_dir):
        super(StylePropertyShorthandWriter, self).__init__([], output_dir)
        self._input_files = json5_file_paths
        self._outputs = {
            (self._FILE_BASENAME + '.cc'):
            self.generate_style_property_shorthand_cpp,
            (self._FILE_BASENAME + '.h'):
            self.generate_style_property_shorthand_h
        }

        json5_properties = css_properties.CSSProperties(json5_file_paths)
        self._shorthands = json5_properties.shorthands

        self._longhand_dictionary = defaultdict(list)
        for property_ in json5_properties.shorthands:
            longhand_enum_keys = list(
                map(enum_key_for_css_property, property_.longhands))

            longhands = list(
                map(lambda name: json5_properties.properties_by_name[name],
                    property_.longhands))
            property_.expansions = create_expansions(longhands)
            for longhand_enum_key in longhand_enum_keys:
                self._longhand_dictionary[longhand_enum_key].append(property_)

        for longhands in self._longhand_dictionary.values():
            # Sort first by number of longhands in decreasing order, then
            # alphabetically
            longhands.sort(key=lambda property_: (-len(property_.longhands),
                                                  property_.name.original))

    @template_expander.use_jinja(
        'core/css/templates/style_property_shorthand.cc.tmpl')
    def generate_style_property_shorthand_cpp(self):
        return {
            'input_files': self._input_files,
            'properties': self._shorthands,
            'longhands_dictionary': self._longhand_dictionary,
        }

    @template_expander.use_jinja(
        'core/css/templates/style_property_shorthand.h.tmpl')
    def generate_style_property_shorthand_h(self):
        return {
            'input_files':
            self._input_files,
            'properties':
            self._shorthands,
            'header_guard':
            self.make_header_guard(self._relative_output_dir +
                                   self._FILE_BASENAME + '.h')
        }


if __name__ == '__main__':
    json5_generator.Maker(StylePropertyShorthandWriter).main()