From 113e4d201021a8c02af35f0c128c27df75d48a73 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Mon, 16 Apr 2018 14:08:20 +1000 Subject: [PATCH] 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 --- layout/style/GenerateServoCSSPropList.py | 110 +++++++++++++++++++++ layout/style/ServoCSSPropList.mako.py | 87 ++++++++++++++++ layout/style/moz.build | 19 ++++ layout/style/nsCSSProps.cpp | 92 +++++++++++++++++ servo/components/style/properties/build.py | 8 +- servo/components/style/properties/data.py | 1 + 6 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 layout/style/GenerateServoCSSPropList.py create mode 100644 layout/style/ServoCSSPropList.mako.py diff --git a/layout/style/GenerateServoCSSPropList.py b/layout/style/GenerateServoCSSPropList.py new file mode 100644 index 000000000000..19e5c374094a --- /dev/null +++ b/layout/style/GenerateServoCSSPropList.py @@ -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 +""") diff --git a/layout/style/ServoCSSPropList.mako.py b/layout/style/ServoCSSPropList.mako.py new file mode 100644 index 000000000000..925b03cfff3b --- /dev/null +++ b/layout/style/ServoCSSPropList.mako.py @@ -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 +] diff --git a/layout/style/moz.build b/layout/style/moz.build index 50eaa4fa0962..7f9060e02d0b 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -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 = [ diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 6f6d45043202..421921853e84 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -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; diff --git a/servo/components/style/properties/build.py b/servo/components/style/properties/build.py index 326859ea6d81..04418723ee45 100644 --- a/servo/components/style/properties/build.py +++ b/servo/components/style/properties/build.py @@ -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