1#!/usr/bin/env python
2
3import os.path
4import re
5import subprocess
6import sys
7
8from in_file import InFile
9import in_generator
10import license
11
12
13HEADER_TEMPLATE = """
14%(license)s
15
16#ifndef %(class_name)s_h
17#define %(class_name)s_h
18
19#include "core/css/CSSParserMode.h"
20#include "wtf/HashFunctions.h"
21#include "wtf/HashTraits.h"
22#include <string.h>
23
24namespace WTF {
25class AtomicString;
26class String;
27}
28
29namespace WebCore {
30
31enum CSSPropertyID {
32    CSSPropertyInvalid = 0,
33%(property_enums)s
34};
35
36const int firstCSSProperty = %(first_property_id)s;
37const int numCSSProperties = %(properties_count)s;
38const int lastCSSProperty = %(last_property_id)d;
39const size_t maxCSSPropertyNameLength = %(max_name_length)d;
40
41const char* getPropertyName(CSSPropertyID);
42const WTF::AtomicString& getPropertyNameAtomicString(CSSPropertyID);
43WTF::String getPropertyNameString(CSSPropertyID);
44WTF::String getJSPropertyName(CSSPropertyID);
45bool isInternalProperty(CSSPropertyID id);
46
47inline CSSPropertyID convertToCSSPropertyID(int value)
48{
49    ASSERT((value >= firstCSSProperty && value <= lastCSSProperty) || value == CSSPropertyInvalid);
50    return static_cast<CSSPropertyID>(value);
51}
52
53} // namespace WebCore
54
55namespace WTF {
56template<> struct DefaultHash<WebCore::CSSPropertyID> { typedef IntHash<unsigned> Hash; };
57template<> struct HashTraits<WebCore::CSSPropertyID> : GenericHashTraits<WebCore::CSSPropertyID> {
58    static const bool emptyValueIsZero = true;
59    static const bool needsDestruction = false;
60    static void constructDeletedValue(WebCore::CSSPropertyID& slot) { slot = static_cast<WebCore::CSSPropertyID>(WebCore::lastCSSProperty + 1); }
61    static bool isDeletedValue(WebCore::CSSPropertyID value) { return value == (WebCore::lastCSSProperty + 1); }
62};
63}
64
65#endif // %(class_name)s_h
66"""
67
68GPERF_TEMPLATE = """
69%%{
70%(license)s
71
72#include "config.h"
73#include "%(class_name)s.h"
74#include "core/css/HashTools.h"
75#include <string.h>
76
77#include "wtf/ASCIICType.h"
78#include "wtf/text/AtomicString.h"
79#include "wtf/text/WTFString.h"
80
81namespace WebCore {
82static const char propertyNameStringsPool[] = {
83%(property_name_strings)s
84};
85
86static const unsigned short propertyNameStringsOffsets[] = {
87%(property_name_offsets)s
88};
89
90%%}
91%%struct-type
92struct Property;
93%%omit-struct-type
94%%language=C++
95%%readonly-tables
96%%global-table
97%%compare-strncmp
98%%define class-name %(class_name)sHash
99%%define lookup-function-name findPropertyImpl
100%%define hash-function-name property_hash_function
101%%define slot-name nameOffset
102%%define word-array-name property_word_list
103%%enum
104%%%%
105%(property_to_enum_map)s
106%%%%
107const Property* findProperty(register const char* str, register unsigned int len)
108{
109    return %(class_name)sHash::findPropertyImpl(str, len);
110}
111
112const char* getPropertyName(CSSPropertyID id)
113{
114    if (id < firstCSSProperty)
115        return 0;
116    int index = id - firstCSSProperty;
117    if (index >= numCSSProperties)
118        return 0;
119    return propertyNameStringsPool + propertyNameStringsOffsets[index];
120}
121
122const AtomicString& getPropertyNameAtomicString(CSSPropertyID id)
123{
124    if (id < firstCSSProperty)
125        return nullAtom;
126    int index = id - firstCSSProperty;
127    if (index >= numCSSProperties)
128        return nullAtom;
129
130    static AtomicString* propertyStrings = new AtomicString[numCSSProperties]; // Intentionally never destroyed.
131    AtomicString& propertyString = propertyStrings[index];
132    if (propertyString.isNull()) {
133        const char* propertyName = propertyNameStringsPool + propertyNameStringsOffsets[index];
134        propertyString = AtomicString(propertyName, strlen(propertyName), AtomicString::ConstructFromLiteral);
135    }
136    return propertyString;
137}
138
139String getPropertyNameString(CSSPropertyID id)
140{
141    // We share the StringImpl with the AtomicStrings.
142    return getPropertyNameAtomicString(id).string();
143}
144
145String getJSPropertyName(CSSPropertyID id)
146{
147    char result[maxCSSPropertyNameLength + 1];
148    const char* cssPropertyName = getPropertyName(id);
149    const char* propertyNamePointer = cssPropertyName;
150    if (!propertyNamePointer)
151        return emptyString();
152
153    char* resultPointer = result;
154    while (char character = *propertyNamePointer++) {
155        if (character == '-') {
156            char nextCharacter = *propertyNamePointer++;
157            if (!nextCharacter)
158                break;
159            character = (propertyNamePointer - 2 != cssPropertyName) ? toASCIIUpper(nextCharacter) : nextCharacter;
160        }
161        *resultPointer++ = character;
162    }
163    *resultPointer = '\\0';
164    return String(result);
165}
166
167bool isInternalProperty(CSSPropertyID id)
168{
169    switch (id) {
170        %(internal_properties)s
171            return true;
172        default:
173            return false;
174    }
175}
176
177} // namespace WebCore
178"""
179
180
181class CSSPropertiesWriter(in_generator.Writer):
182    class_name = "CSSPropertyNames"
183    defaults = {
184        'alias_for': None,
185        'is_internal': False,
186    }
187
188    def __init__(self, file_paths):
189        in_generator.Writer.__init__(self, file_paths)
190        self._outputs = {(self.class_name + ".h"): self.generate_header,
191                         (self.class_name + ".cpp"): self.generate_implementation,
192                        }
193
194        self._aliases = filter(lambda property: property['alias_for'], self.in_file.name_dictionaries)
195        for offset, property in enumerate(self._aliases):
196            # Aliases use the enum_name that they are an alias for.
197            property['enum_name'] = self._enum_name_from_property_name(property['alias_for'])
198            # Aliases do not get an enum_value.
199
200        self._properties = filter(lambda property: not property['alias_for'], self.in_file.name_dictionaries)
201        if len(self._properties) > 1024:
202            print "ERROR : There is more than 1024 CSS Properties, you need to update CSSProperty.h/StylePropertyMetadata m_propertyID accordingly."
203            exit(1)
204        self._first_property_id = 1  # We start after CSSPropertyInvalid.
205        property_id = self._first_property_id
206        for offset, property in enumerate(self._properties):
207            property['enum_name'] = self._enum_name_from_property_name(property['name'])
208            property['enum_value'] = self._first_property_id + offset
209            if property['name'].startswith('-internal-'):
210                property['is_internal'] = True
211
212    def _enum_name_from_property_name(self, property_name):
213        return "CSSProperty" + re.sub(r'(^[^-])|-(.)', lambda match: (match.group(1) or match.group(2)).upper(), property_name)
214
215    def _enum_declaration(self, property):
216        return "    %(enum_name)s = %(enum_value)s," % property
217
218    def generate_header(self):
219        return HEADER_TEMPLATE % {
220            'license': license.license_for_generated_cpp(),
221            'class_name': self.class_name,
222            'property_enums': "\n".join(map(self._enum_declaration, self._properties)),
223            'first_property_id': self._first_property_id,
224            'properties_count': len(self._properties),
225            'last_property_id': self._first_property_id + len(self._properties) - 1,
226            'max_name_length': reduce(max, map(len, map(lambda property: property['name'], self._properties))),
227        }
228
229    def _case_properties(self, property):
230        return "case %(enum_name)s:" % property
231
232    def generate_implementation(self):
233        property_offsets = []
234        current_offset = 0
235        for property in self._properties:
236            property_offsets.append(current_offset)
237            current_offset += len(property["name"]) + 1
238
239        gperf_input = GPERF_TEMPLATE % {
240            'license': license.license_for_generated_cpp(),
241            'class_name': self.class_name,
242            'property_name_strings': '\n'.join(map(lambda property: '    "%(name)s\\0"' % property, self._properties)),
243            'property_name_offsets': '\n'.join(map(lambda offset: '    %d,' % offset, property_offsets)),
244            'property_to_enum_map': '\n'.join(map(lambda property: '%(name)s, %(enum_name)s' % property, self._properties + self._aliases)),
245            'internal_properties': '\n'.join(map(self._case_properties, filter(lambda property: property['is_internal'], self._properties))),
246        }
247        # FIXME: If we could depend on Python 2.7, we would use subprocess.check_output
248        gperf_args = [self.gperf_path, '--key-positions=*', '-P', '-n']
249        gperf_args.extend(['-m', '50'])  # Pick best of 50 attempts.
250        gperf_args.append('-D')  # Allow duplicate hashes -> More compact code.
251        gperf = subprocess.Popen(gperf_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
252        return gperf.communicate(gperf_input)[0]
253
254
255if __name__ == "__main__":
256    in_generator.Maker(CSSPropertiesWriter).main(sys.argv)
257