1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this file,
3# You can obtain one at http://mozilla.org/MPL/2.0/.
4
5import sys
6import string
7import argparse
8import subprocess
9import buildconfig
10from mozbuild import shellutil
11
12def get_properties(preprocessorHeader):
13    cpp = list(buildconfig.substs['CPP'])
14    cpp += shellutil.split(buildconfig.substs['ACDEFINES'])
15    cpp.append(preprocessorHeader)
16    preprocessed = subprocess.check_output(cpp)
17    properties = [{"name":p[0], "prop":p[1], "id":p[2],
18                   "flags":p[3], "pref":p[4], "proptype":p[5]}
19                  for (i, p) in enumerate(eval(preprocessed))]
20
21    # Sort the list so that longhand and logical properties are intermingled
22    # first, shorthand properties follow, then aliases appear last.  This matches
23    # the order of the nsCSSPropertyID enum.
24
25    def property_compare(x, y):
26        property_order = {"longhand": 0, "logical": 0, "shorthand": 1, "alias": 2}
27        return property_order[x["proptype"]] - property_order[y["proptype"]]
28
29    properties = sorted(properties, cmp=property_compare)
30
31    for i, p in enumerate(properties):
32        p["index"] = i
33
34    # Record each property's IDL name.
35    for p in properties:
36        if "CSS_PROPERTY_INTERNAL" in p["flags"]:
37            p["idlname"] = None
38        else:
39            idl_name = p["prop"]
40            if not idl_name.startswith("Moz"):
41                idl_name = idl_name[0].lower() + idl_name[1:]
42            p["idlname"] = idl_name
43
44    return properties
45
46def generate_idl_names(properties):
47    names = []
48    for p in properties:
49        if p["proptype"] is "alias":
50            continue
51        if p["idlname"] is None:
52            names.append("  nullptr,  // %s" % p["name"])
53        else:
54            names.append('  "%s",' % p["idlname"])
55    return "\n".join(names)
56
57def generate_assertions(properties):
58    def enum(p):
59        if p["proptype"] is "alias":
60            return "eCSSPropertyAlias_%s" % p["prop"]
61        else:
62            return "eCSSProperty_%s" % p["id"]
63    msg = ('static_assert(%s == %d, "GenerateCSSPropsGenerated.py did not list '
64           'properties in nsCSSPropertyID order");')
65    return "\n".join(map(lambda p: msg % (enum(p), p["index"]), properties))
66
67def generate_idl_name_positions(properties):
68    # Skip aliases.
69    ps = filter(lambda p: p["proptype"] is not "alias", properties)
70
71    # Sort alphabetically by IDL name.
72    ps = sorted(ps, key=lambda p: p["idlname"])
73
74    # Annotate entries with the sorted position.
75    ps = [(p, position) for position, p in enumerate(ps)]
76
77    # Sort back to nsCSSPropertyID order.
78    ps = sorted(ps, key=lambda (p, position): p["index"])
79
80    return ",\n".join(map(lambda (p, position): "  %d" % position, ps))
81
82def generate(output, cppTemplate, preprocessorHeader):
83    cppFile = open(cppTemplate, "r")
84    cppTemplate = cppFile.read()
85    cppFile.close()
86
87    properties = get_properties(preprocessorHeader)
88    substitutions = {
89        "idl_names": generate_idl_names(properties),
90        "assertions": generate_assertions(properties),
91        "idl_name_positions": generate_idl_name_positions(properties),
92    }
93    output.write("/* THIS IS AN AUTOGENERATED FILE.  DO NOT EDIT */\n\n" +
94                 string.Template(cppTemplate).substitute(substitutions) + "\n")
95
96def main():
97    parser = argparse.ArgumentParser()
98    parser.add_argument('cppTemplate', help='CSS property file template')
99    parser.add_argument('preprocessorHeader', help='Header file to pass through the preprocessor')
100    args = parser.parse_args()
101    generate(sys.stdout, args.cppTemplate, args.preprocessorHeader)
102
103if __name__ == '__main__':
104    main()
105