Bug 1452542 part 5 - Generate property list from Servo data. r=emilio,froydnj

With this change, we first generate a data file ServoCSSPropList.py from
Servo data, and then use this data to generate ServoCSSPropList.h.

This change itself serves as a checkpoint with a runtime check that all
information generated from Servo side matches what we have in the Gecko
side. Following patches will start replacing uses of nsCSSPropList.h
with either the data file or the header file.

The reason that it generates data file rather than header directly is
that, many users of PythonCSSProps.h invokes C++ preprocessor manually
to extract data from nsCSSPropList.h without passing in search paths,
so it is non-trivial to replace the use of nsCSSPropList.h there with
a generated header. Generating a Python data file would hopefully
simplify those users rather than adding more complexity to them.

I also thought about generating JSON rather than plain Python file, but
JSON doesn't allow trailing comma in array, which makes it less pretty
to generate via mako template.

MozReview-Commit-ID: CwK2oL88r6F

--HG--
extra : rebase_source : aaf98cfd768740fdd6ac4961fc825d84adaf94a5
This commit is contained in:
Xidorn Quan 2018-04-16 14:08:20 +10:00
parent b894981f8f
commit 113e4d2010
6 changed files with 316 additions and 1 deletions

View File

@ -0,0 +1,110 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import buildconfig
import mozpack.path as mozpath
import os
import subprocess
import string
import sys
SERVO_BASE = mozpath.join(buildconfig.topsrcdir, "servo")
SERVO_PROP_BASE = mozpath.join(SERVO_BASE, "components", "style", "properties")
def generate_data(output, template):
output.write("# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT\n\n")
output.write(subprocess.check_output([
sys.executable,
mozpath.join(SERVO_PROP_BASE, "build.py"),
"gecko", "geckolib", template
], universal_newlines=True))
# Add all relevant files into the dependencies of the generated file.
DEP_EXTS = [".py", ".rs", ".zip"]
deps = set()
for path, dirs, files in os.walk(SERVO_PROP_BASE):
for file in files:
if os.path.splitext(file)[1] in DEP_EXTS:
deps.add(mozpath.join(path, file))
return deps
def generate_header(output, data):
with open(data, "r") as f:
data = eval(f.read())
output.write("""/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#define CSS_PROP_DOMPROP_PREFIXED(name_) \\
CSS_PROP_PUBLIC_OR_PRIVATE(Moz ## name_, name_)
#ifndef CSS_PROP_LONGHAND
#define CSS_PROP_LONGHAND(name_, id_, method_, flags_, pref_) /* nothing */
#define DEFINED_CSS_PROP_LONGHAND
#endif
#ifndef CSS_PROP_SHORTHAND
#define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) /* nothing */
#define DEFINED_CSS_PROP_SHORTHAND
#endif
#ifndef CSS_PROP_ALIAS
#define CSS_PROP_ALIAS(name_, aliasid_, id_, method_, pref_) /* nothing */
#define DEFINED_CSS_PROP_ALIAS
#endif
""")
MACRO_NAMES = {
"longhand": "CSS_PROP_LONGHAND",
"shorthand": "CSS_PROP_SHORTHAND",
"alias": "CSS_PROP_ALIAS",
}
for name, method, id, flags, pref, proptype in data:
is_internal = "CSS_PROPERTY_INTERNAL" in flags
pref = '"' + pref + '"'
if proptype == "alias":
params = [name, id[0], id[1], method, pref]
else:
if method == "CssFloat":
method = "CSS_PROP_PUBLIC_OR_PRIVATE(CssFloat, Float)"
elif method.startswith("Moz"):
method = "CSS_PROP_DOMPROP_PREFIXED({})".format(method[3:])
if flags:
flags = " | ".join(flags)
else:
flags = "0"
params = [name, id, method, flags, pref]
if is_internal:
output.write("#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL\n")
output.write("{}({})\n".format(MACRO_NAMES[proptype], ", ".join(params)))
if is_internal:
output.write("#endif\n")
output.write("""
#ifdef DEFINED_CSS_PROP_ALIAS
#undef CSS_PROP_ALIAS
#undef DEFINED_CSS_PROP_ALIAS
#endif
#ifdef DEFINED_CSS_PROP_SHORTHAND
#undef CSS_PROP_SHORTHAND
#undef DEFINED_CSS_PROP_SHORTHAND
#endif
#ifdef DEFINED_CSS_PROP_LONGHAND
#undef CSS_PROP_LONGHAND
#undef DEFINED_CSS_PROP_LONGHAND
#endif
#undef CSS_PROP_DOMPROP_PREFIXED
""")

View File

@ -0,0 +1,87 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
<%!
# nsCSSPropertyID of longhands and shorthands is ordered alphabetically
# with vendor prefixes removed. Note that aliases use their alias name
# as order key directly because they may be duplicate without prefix.
def order_key(prop):
if prop.name.startswith("-"):
return prop.name[prop.name.find("-", 1) + 1:]
return prop.name
# See bug 1454823 for situation of internal flag.
def is_internal(prop):
# A property which is not controlled by pref and not enabled in
# content by default is an internal property.
if not prop.gecko_pref and not prop.enabled_in_content():
return True
# There are some special cases we may want to remove eventually.
OTHER_INTERNALS = [
"-moz-context-properties",
"-moz-control-character-visibility",
"-moz-window-opacity",
"-moz-window-transform",
"-moz-window-transform-origin",
]
return prop.name in OTHER_INTERNALS
def flags(prop):
result = []
if prop.explicitly_enabled_in_chrome():
result.append("CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME")
elif prop.explicitly_enabled_in_ua_sheets():
result.append("CSS_PROPERTY_ENABLED_IN_UA_SHEETS")
if is_internal(prop):
result.append("CSS_PROPERTY_INTERNAL")
if prop.enabled_in == "":
result.append("CSS_PROPERTY_PARSE_INACCESSIBLE")
return ", ".join('"{}"'.format(flag) for flag in result)
def pref(prop):
if prop.gecko_pref:
return '"' + prop.gecko_pref + '"'
return '""'
%>
[
% for prop in sorted(data.longhands, key=order_key):
(
"${prop.name}",
% if prop.name == "float":
"CssFloat",
% elif prop.name.startswith("-x-"):
"${prop.camel_case[1:]}",
% else:
"${prop.camel_case}",
% endif
"${prop.ident}",
[${flags(prop)}],
${pref(prop)},
"longhand",
),
% endfor
% for prop in sorted(data.shorthands, key=order_key):
(
"${prop.name}",
"${prop.camel_case}",
"${prop.ident}",
[${flags(prop)}],
${pref(prop)},
"shorthand",
),
% endfor
% for prop in sorted(data.all_aliases(), key=lambda x: x.name):
(
"${prop.name}",
"${prop.camel_case}",
("${prop.ident}", "${prop.original.ident}"),
[],
${pref(prop)},
"alias",
),
% endfor
]

View File

@ -63,6 +63,7 @@ EXPORTS += [
]
EXPORTS.mozilla += [
'!ServoCSSPropList.h',
'AnimationCollection.h',
'BindingStyleRule.h',
'CachedInheritingStyles.h',
@ -274,10 +275,28 @@ CONTENT_ACCESSIBLE_FILES += [
'TopLevelVideoDocument.css',
]
GENERATED_FILES += [
'ServoCSSPropList.h',
'ServoCSSPropList.py',
]
servo_props = GENERATED_FILES['ServoCSSPropList.h']
servo_props.script = 'GenerateServoCSSPropList.py:generate_header'
servo_props.inputs = [
'!ServoCSSPropList.py',
]
servo_props = GENERATED_FILES['ServoCSSPropList.py']
servo_props.script = 'GenerateServoCSSPropList.py:generate_data'
servo_props.inputs = [
'ServoCSSPropList.mako.py',
]
if CONFIG['COMPILE_ENVIRONMENT']:
GENERATED_FILES += [
'nsCSSPropsGenerated.inc',
]
css_props = GENERATED_FILES['nsCSSPropsGenerated.inc']
css_props.script = 'GenerateCSSPropsGenerated.py:generate'
css_props.inputs = [

View File

@ -147,6 +147,94 @@ CreateStaticTable(const char* const aRawTable[], int32_t aLength)
return table;
}
#ifdef DEBUG
static void
CheckServoCSSPropList()
{
struct PropData {
nsCSSPropertyID mID;
uint32_t mFlags;
const char* mPref;
};
const PropData sGeckoProps[eCSSProperty_COUNT_with_aliases] = {
#define CSS_PROP(name_, id_, method_, flags_, pref_, ...) \
{ eCSSProperty_##id_, flags_, pref_ },
#include "nsCSSPropList.h"
#undef CSS_PROP
#define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) \
{ eCSSProperty_##id_, flags_, pref_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_SHORTHAND
#define CSS_PROP_ALIAS(aliasname_, aliasid_, propid_, aliasmethod_, pref_) \
{ eCSSPropertyAlias_##aliasid_, 0, pref_ },
#include "nsCSSPropAliasList.h"
#undef CSS_PROP_ALIAS
};
const PropData sServoProps[eCSSProperty_COUNT_with_aliases] = {
#define CSS_PROP_LONGHAND(name_, id_, method_, flags_, pref_) \
{ eCSSProperty_##id_, flags_, pref_ },
#define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) \
{ eCSSProperty_##id_, flags_, pref_ },
#define CSS_PROP_ALIAS(name_, aliasid_, id_, method_, pref_) \
{ eCSSPropertyAlias_##aliasid_, 0, pref_ },
#include "mozilla/ServoCSSPropList.h"
#undef CSS_PROP_ALIAS
#undef CSS_PROP_SHORTHAND
#undef CSS_PROP_LONGHAND
};
const uint32_t kServoFlags =
CSS_PROPERTY_ENABLED_MASK | CSS_PROPERTY_INTERNAL |
CSS_PROPERTY_PARSE_INACCESSIBLE;
bool mismatch = false;
for (size_t i = 0; i < eCSSProperty_COUNT_with_aliases; i++) {
auto& geckoData = sGeckoProps[i];
auto& servoData = sServoProps[i];
const char* name = nsCSSProps::GetStringValue(geckoData.mID).get();
if (geckoData.mID != servoData.mID) {
printf_stderr("Order mismatches: gecko: %s, servo: %s\n",
name, nsCSSProps::GetStringValue(servoData.mID).get());
mismatch = true;
continue;
}
if ((geckoData.mFlags & kServoFlags) != servoData.mFlags) {
printf_stderr("Enabled flags of %s mismatch\n", name);
mismatch = true;
}
if (strcmp(geckoData.mPref, servoData.mPref) != 0) {
printf_stderr("Pref of %s mismatches\n", name);
mismatch = true;
}
}
const nsCSSPropertyID sGeckoAliases[eCSSAliasCount] = {
#define CSS_PROP_ALIAS(aliasname_, aliasid_, propid_, aliasmethod_, pref_) \
eCSSProperty_##propid_,
#include "nsCSSPropAliasList.h"
#undef CSS_PROP_ALIAS
};
const nsCSSPropertyID sServoAliases[eCSSAliasCount] = {
#define CSS_PROP_ALIAS(aliasname_, aliasid_, propid_, aliasmethod_, pref_) \
eCSSProperty_##propid_,
#include "mozilla/ServoCSSPropList.h"
#undef CSS_PROP_ALIAS
};
for (size_t i = 0; i < eCSSAliasCount; i++) {
if (sGeckoAliases[i] == sServoAliases[i]) {
continue;
}
nsCSSPropertyID aliasid = nsCSSPropertyID(eCSSProperty_COUNT + i);
printf_stderr("Original property of alias %s mismatches\n",
nsCSSProps::GetStringValue(aliasid).get());
mismatch = true;
}
MOZ_ASSERT(!mismatch);
}
#endif
void
nsCSSProps::AddRefTable(void)
{
@ -171,6 +259,10 @@ nsCSSProps::AddRefTable(void)
}
}
#ifdef DEBUG
CheckServoCSSPropList();
#endif
static bool prefObserversInited = false;
if (!prefObserversInited) {
prefObserversInited = true;

View File

@ -21,7 +21,7 @@ RE_PYTHON_ADDR = re.compile(r'<.+? object at 0x[0-9a-fA-F]+>')
def main():
usage = "Usage: %s [ servo | gecko ] [ style-crate | html ]" % sys.argv[0]
usage = "Usage: %s [ servo | gecko ] [ style-crate | geckolib <template> | html ]" % sys.argv[0]
if len(sys.argv) < 3:
abort(usage)
product = sys.argv[1]
@ -39,6 +39,12 @@ def main():
template = os.path.join(BASE, "gecko.mako.rs")
rust = render(template, data=properties)
write(os.environ["OUT_DIR"], "gecko_properties.rs", rust)
elif output == "geckolib":
if len(sys.argv) < 4:
abort(usage)
template = sys.argv[3]
header = render(template, data=properties)
sys.stdout.write(header)
elif output == "html":
write_html(properties)

View File

@ -385,6 +385,7 @@ class Alias(object):
self.name = name
self.ident = to_rust_ident(name)
self.camel_case = to_camel_case(self.ident)
self.original = original
self.enabled_in = original.enabled_in
self.servo_pref = original.servo_pref
self.gecko_pref = gecko_pref