Merge mozilla-inbound to mozilla-central. a=merge
authorDaniel Varga <dvarga@mozilla.com>
Wed, 31 Jul 2019 00:52:26 +0300
changeset 485437 d497a1f58d27d75da3667e74a5a7efe5799119fd
parent 485436 e259d43073bc84dbf3aa547c3dd080d62c89f45f (current diff)
parent 485435 86e4fc4b9b1c0e315d7ea63eafc43cec6f0f7511 (diff)
child 485438 50a36bc2cddb6d44da495f1522caa5cec5eac58a
child 485475 1b63a509d89647f0ce61be7d0b6b351e2fd72c09
push id91298
push userdvarga@mozilla.com
push dateTue, 30 Jul 2019 22:02:34 +0000
treeherderautoland@50a36bc2cddb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone70.0a1
first release with
nightly linux32
d497a1f58d27 / 70.0a1 / 20190730215316 / files
nightly linux64
d497a1f58d27 / 70.0a1 / 20190730215316 / files
nightly mac
d497a1f58d27 / 70.0a1 / 20190730215316 / files
nightly win32
d497a1f58d27 / 70.0a1 / 20190730215316 / files
nightly win64
d497a1f58d27 / 70.0a1 / 20190730215316 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -3148,16 +3148,38 @@ exports.CSS_PROPERTIES = {
       "font-optical-sizing",
       "-moz-osx-font-smoothing",
       "visibility",
       "writing-mode",
       "text-orientation",
       "color-adjust",
       "image-rendering",
       "image-orientation",
+      "dominant-baseline",
+      "text-anchor",
+      "color-interpolation",
+      "color-interpolation-filters",
+      "fill",
+      "fill-opacity",
+      "fill-rule",
+      "shape-rendering",
+      "stroke",
+      "stroke-width",
+      "stroke-linecap",
+      "stroke-linejoin",
+      "stroke-miterlimit",
+      "stroke-opacity",
+      "stroke-dasharray",
+      "stroke-dashoffset",
+      "clip-rule",
+      "marker-start",
+      "marker-mid",
+      "marker-end",
+      "paint-order",
+      "-moz-context-properties",
       "border-collapse",
       "empty-cells",
       "caption-side",
       "border-spacing",
       "color",
       "line-height",
       "text-transform",
       "hyphens",
@@ -3189,38 +3211,16 @@ exports.CSS_PROPERTIES = {
       "text-decoration-skip-ink",
       "cursor",
       "pointer-events",
       "-moz-user-input",
       "-moz-user-modify",
       "-moz-user-focus",
       "caret-color",
       "scrollbar-color",
-      "dominant-baseline",
-      "text-anchor",
-      "color-interpolation",
-      "color-interpolation-filters",
-      "fill",
-      "fill-opacity",
-      "fill-rule",
-      "shape-rendering",
-      "stroke",
-      "stroke-width",
-      "stroke-linecap",
-      "stroke-linejoin",
-      "stroke-miterlimit",
-      "stroke-opacity",
-      "stroke-dasharray",
-      "stroke-dashoffset",
-      "clip-rule",
-      "marker-start",
-      "marker-mid",
-      "marker-end",
-      "paint-order",
-      "-moz-context-properties",
       "list-style-position",
       "list-style-type",
       "list-style-image",
       "quotes",
       "-moz-image-region",
       "margin-top",
       "margin-right",
       "margin-bottom",
@@ -3278,28 +3278,16 @@ exports.CSS_PROPERTIES = {
       "grid-column-start",
       "grid-column-end",
       "grid-auto-columns",
       "grid-template-columns",
       "grid-auto-flow",
       "grid-template-areas",
       "column-gap",
       "row-gap",
-      "table-layout",
-      "text-overflow",
-      "text-decoration-line",
-      "text-decoration-style",
-      "text-decoration-color",
-      "initial-letter",
-      "text-decoration-thickness",
-      "ime-mode",
-      "scrollbar-width",
-      "user-select",
-      "-moz-window-dragging",
-      "-moz-force-broken-image-icon",
       "vector-effect",
       "stop-color",
       "stop-opacity",
       "flood-color",
       "flood-opacity",
       "lighting-color",
       "mask-type",
       "clip-path",
@@ -3314,16 +3302,28 @@ exports.CSS_PROPERTIES = {
       "mask-image",
       "x",
       "y",
       "cx",
       "cy",
       "rx",
       "ry",
       "r",
+      "table-layout",
+      "text-overflow",
+      "text-decoration-line",
+      "text-decoration-style",
+      "text-decoration-color",
+      "initial-letter",
+      "text-decoration-thickness",
+      "ime-mode",
+      "scrollbar-width",
+      "user-select",
+      "-moz-window-dragging",
+      "-moz-force-broken-image-icon",
       "-moz-box-align",
       "-moz-box-direction",
       "-moz-box-flex",
       "-moz-box-orient",
       "-moz-box-pack",
       "-moz-stack-sizing",
       "-moz-box-ordinal-group"
     ],
--- a/servo/components/style/Cargo.toml
+++ b/servo/components/style/Cargo.toml
@@ -3,28 +3,30 @@ name = "style"
 version = "0.0.1"
 authors = ["The Servo Project Developers"]
 license = "MPL-2.0"
 publish = false
 
 build = "build.rs"
 
 # https://github.com/rust-lang/cargo/issues/3544
-links = "for some reason the links key is required to pass data around between build scripts"
+links = "servo_style_crate"
 
 [lib]
 name = "style"
 path = "lib.rs"
 doctest = false
 
 [features]
 gecko = ["nsstring", "style_traits/gecko", "fallible/known_system_malloc", "bindgen", "regex", "toml"]
 servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever",
          "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "arrayvec/use_union",
          "servo_url", "string_cache", "crossbeam-channel", "to_shmem/servo", "servo_arc/servo"]
+servo-layout-2013 = []
+servo-layout-2020 = []
 gecko_debug = []
 gecko_refcount_logging = []
 gecko_profiler = []
 
 [dependencies]
 app_units = "0.7"
 arrayvec = "0.4.6"
 atomic_refcell = "0.1"
--- a/servo/components/style/build.rs
+++ b/servo/components/style/build.rs
@@ -84,24 +84,29 @@ fn generate_properties() {
             },
             _ => {},
         }
     }
 
     let script = Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap())
         .join("properties")
         .join("build.py");
-    let product = if cfg!(feature = "gecko") {
-        "gecko"
-    } else {
-        "servo"
-    };
+
+    #[cfg(feature = "gecko")]
+    let engine = "gecko";
+
+    #[cfg(feature = "servo-layout-2013")]
+    let engine = "servo-2013";
+
+    #[cfg(feature = "servo-layout-2020")]
+    let engine = "servo-2020";
+
     let status = Command::new(&*PYTHON)
         .arg(&script)
-        .arg(product)
+        .arg(engine)
         .arg("style-crate")
         .status()
         .unwrap();
     if !status.success() {
         exit(1)
     }
 }
 
@@ -112,13 +117,16 @@ fn main() {
         panic!("The style crate requires enabling one of its 'servo' or 'gecko' feature flags");
     }
     if gecko && servo {
         panic!(
             "The style crate does not support enabling both its 'servo' or 'gecko' \
              feature flags at the same time."
         );
     }
+    if gecko && (cfg!(feature = "servo-layout-2013") || cfg!(feature = "servo-layout-2020")) {
+        panic!("The 'servo-layout-*' features can only be enabled together with 'servo'.");
+    }
     println!("cargo:rerun-if-changed=build.rs");
     println!("cargo:out_dir={}", env::var("OUT_DIR").unwrap());
     generate_properties();
     build_gecko::generate();
 }
--- a/servo/components/style/properties/build.py
+++ b/servo/components/style/properties/build.py
@@ -25,88 +25,113 @@ STYLE_STRUCT_LIST = [
     "background",
     "border",
     "box",
     "column",
     "counters",
     "effects",
     "font",
     "inherited_box",
+    "inherited_svg",
     "inherited_table",
     "inherited_text",
     "inherited_ui",
-    "inherited_svg",
     "list",
     "margin",
     "outline",
     "padding",
     "position",
+    "svg",
     "table",
     "text",
     "ui",
-    "svg",
     "xul",
 ]
 
 
 def main():
-    usage = ("Usage: %s [ servo | gecko ] [ style-crate | geckolib <template> | html ]" %
+    usage = ("Usage: %s [ servo-2013 | servo-2020 | gecko ] [ style-crate | geckolib <template> | html ]" %
              sys.argv[0])
     if len(sys.argv) < 3:
         abort(usage)
-    product = sys.argv[1]
+    engine = sys.argv[1]
     output = sys.argv[2]
 
-    if product not in ["servo", "gecko"] or output not in ["style-crate", "geckolib", "html"]:
+    if engine not in ["servo-2013", "servo-2020", "gecko"] \
+            or output not in ["style-crate", "geckolib", "html"]:
         abort(usage)
 
-    properties = data.PropertiesData(product=product)
+    properties = data.PropertiesData(engine=engine)
     files = {}
     for kind in ["longhands", "shorthands"]:
         files[kind] = {}
         for struct in STYLE_STRUCT_LIST:
             file_name = os.path.join(BASE, kind, "{}.mako.rs".format(struct))
             if kind == "shorthands" and not os.path.exists(file_name):
                 files[kind][struct] = ""
                 continue
             files[kind][struct] = render(
                 file_name,
-                product=product,
+                engine=engine,
                 data=properties,
             )
     properties_template = os.path.join(BASE, "properties.mako.rs")
     files["properties"] = render(
         properties_template,
-        product=product,
+        engine=engine,
         data=properties,
         __file__=properties_template,
         OUT_DIR=OUT_DIR,
     )
     if output == "style-crate":
         write(OUT_DIR, "properties.rs", files["properties"])
         for kind in ["longhands", "shorthands"]:
             for struct in files[kind]:
                 write(
                     os.path.join(OUT_DIR, kind),
                     "{}.rs".format(struct),
                     files[kind][struct],
                 )
 
-        if product == "gecko":
+        if engine == "gecko":
             template = os.path.join(BASE, "gecko.mako.rs")
             rust = render(template, data=properties)
             write(OUT_DIR, "gecko_properties.rs", rust)
+
+        if engine in ["servo-2013", "servo-2020"]:
+            if engine == "servo-2013":
+                pref_attr = "servo_2013_pref"
+            if engine == "servo-2020":
+                pref_attr = "servo_2020_pref"
+            properties_dict = {
+                kind: {
+                    p.name: {
+                        "pref": getattr(p, pref_attr)
+                    }
+                    for prop in properties_list
+                    if prop.enabled_in_content()
+                    for p in [prop] + prop.alias
+                }
+                for kind, properties_list in [
+                    ("longhands", properties.longhands),
+                    ("shorthands", properties.shorthands)
+                ]
+            }
+            as_html = render(os.path.join(BASE, "properties.html.mako"), properties=properties_dict)
+            as_json = json.dumps(properties_dict, indent=4, sort_keys=True)
+            doc_servo = os.path.join(BASE, "..", "..", "..", "target", "doc", "servo")
+            write(doc_servo, "css-properties.html", as_html)
+            write(doc_servo, "css-properties.json", as_json)
+            write(OUT_DIR, "css-properties.json", as_json)
     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)
 
 
 def abort(message):
     sys.stderr.write(message + b"\n")
     sys.exit(1)
 
 
 def render(filename, **context):
@@ -134,24 +159,10 @@ def write(directory, filename, content):
     full_path = os.path.join(directory, filename)
     open(full_path, "wb").write(content)
 
     python_addr = RE_PYTHON_ADDR.search(content)
     if python_addr:
         abort("Found \"{}\" in {} ({})".format(python_addr.group(0), filename, full_path))
 
 
-def write_html(properties):
-    properties = dict(
-        (p.name, {
-            "flag": p.servo_pref,
-            "shorthand": hasattr(p, "sub_properties")
-        })
-        for p in properties.longhands + properties.shorthands
-    )
-    doc_servo = os.path.join(BASE, "..", "..", "..", "target", "doc", "servo")
-    html = render(os.path.join(BASE, "properties.html.mako"), properties=properties)
-    write(doc_servo, "css-properties.html", html)
-    write(doc_servo, "css-properties.json", json.dumps(properties, indent=4))
-
-
 if __name__ == "__main__":
     main()
--- a/servo/components/style/properties/cascade.rs
+++ b/servo/components/style/properties/cascade.rs
@@ -634,23 +634,18 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
 
             if let Some(svg) = builder.get_svg_if_mutated() {
                 svg.fill_arrays();
             }
         }
 
         #[cfg(feature = "servo")]
         {
-            // TODO(emilio): Use get_font_if_mutated instead.
-            if self.seen.contains(LonghandId::FontStyle) ||
-                self.seen.contains(LonghandId::FontWeight) ||
-                self.seen.contains(LonghandId::FontStretch) ||
-                self.seen.contains(LonghandId::FontFamily)
-            {
-                builder.mutate_font().compute_font_hash();
+            if let Some(font) = builder.get_font_if_mutated() {
+                font.compute_font_hash();
             }
         }
     }
 
     fn try_to_use_cached_reset_properties(
         &mut self,
         cache: Option<&'b RuleCache>,
         guards: &StylesheetGuards,
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -24,18 +24,18 @@ SYSTEM_FONT_LONGHANDS = """font_family f
                            font_variant_position font_weight
                            font_size_adjust font_variant_alternates
                            font_variant_ligatures font_variant_east_asian
                            font_variant_numeric font_language_override
                            font_feature_settings font_variation_settings
                            font_optical_sizing""".split()
 
 
-def maybe_moz_logical_alias(product, side, prop):
-    if product == "gecko" and side[1]:
+def maybe_moz_logical_alias(engine, side, prop):
+    if engine == "gecko" and side[1]:
         axis, dir = side[0].split("-")
         if axis == "inline":
             return prop % dir
     return None
 
 
 def to_rust_ident(name):
     name = name.replace("-", "_")
@@ -68,64 +68,62 @@ def parse_aliases(value):
         [a, v] = pair.split("=")
         aliases[a] = v
     return aliases
 
 
 class Keyword(object):
     def __init__(self, name, values, gecko_constant_prefix=None,
                  gecko_enum_prefix=None, custom_consts=None,
-                 extra_gecko_values=None, extra_servo_values=None,
-                 aliases=None,
-                 extra_gecko_aliases=None,
+                 extra_gecko_values=None,
+                 extra_servo_2013_values=None,
+                 extra_servo_2020_values=None,
+                 gecko_aliases=None,
+                 servo_2013_aliases=None,
+                 servo_2020_aliases=None,
                  gecko_strip_moz_prefix=None,
                  gecko_inexhaustive=None):
         self.name = name
         self.values = values.split()
         if gecko_constant_prefix and gecko_enum_prefix:
             raise TypeError("Only one of gecko_constant_prefix and gecko_enum_prefix "
                             "can be specified")
         self.gecko_constant_prefix = gecko_constant_prefix or \
             "NS_STYLE_" + self.name.upper().replace("-", "_")
         self.gecko_enum_prefix = gecko_enum_prefix
         self.extra_gecko_values = (extra_gecko_values or "").split()
-        self.extra_servo_values = (extra_servo_values or "").split()
-        self.aliases = parse_aliases(aliases or "")
-        self.extra_gecko_aliases = parse_aliases(extra_gecko_aliases or "")
+        self.extra_servo_2013_values = (extra_servo_2013_values or "").split()
+        self.extra_servo_2020_values = (extra_servo_2020_values or "").split()
+        self.gecko_aliases = parse_aliases(gecko_aliases or "")
+        self.servo_2013_aliases = parse_aliases(servo_2013_aliases or "")
+        self.servo_2020_aliases = parse_aliases(servo_2020_aliases or "")
         self.consts_map = {} if custom_consts is None else custom_consts
         self.gecko_strip_moz_prefix = True \
             if gecko_strip_moz_prefix is None else gecko_strip_moz_prefix
         self.gecko_inexhaustive = gecko_inexhaustive or (gecko_enum_prefix is None)
 
-    def gecko_values(self):
-        return self.values + self.extra_gecko_values
-
-    def servo_values(self):
-        return self.values + self.extra_servo_values
-
-    def gecko_aliases(self):
-        aliases = self.aliases.copy()
-        aliases.update(self.extra_gecko_aliases)
-        return aliases
+    def values_for(self, engine):
+        if engine == "gecko":
+            return self.values + self.extra_gecko_values
+        elif engine == "servo-2013":
+            return self.values + self.extra_servo_2013_values
+        elif engine == "servo-2020":
+            return self.values + self.extra_servo_2020_values
+        else:
+            raise Exception("Bad engine: " + engine)
 
-    def values_for(self, product):
-        if product == "gecko":
-            return self.gecko_values()
-        elif product == "servo":
-            return self.servo_values()
+    def aliases_for(self, engine):
+        if engine == "gecko":
+            return self.gecko_aliases
+        elif engine == "servo-2013":
+            return self.servo_2013_aliases
+        elif engine == "servo-2020":
+            return self.servo_2020_aliases
         else:
-            raise Exception("Bad product: " + product)
-
-    def aliases_for(self, product):
-        if product == "gecko":
-            return self.gecko_aliases()
-        elif product == "servo":
-            return self.aliases
-        else:
-            raise Exception("Bad product: " + product)
+            raise Exception("Bad engine: " + engine)
 
     def gecko_constant(self, value):
         moz_stripped = (value.replace("-moz-", '')
                         if self.gecko_strip_moz_prefix else value.replace("-moz-", 'moz-'))
         mapped = self.consts_map.get(value)
         if self.gecko_enum_prefix:
             parts = moz_stripped.replace('-', '_').split('_')
             parts = mapped if mapped else [p.title() for p in parts]
@@ -167,17 +165,20 @@ def parse_property_aliases(alias_list):
 
 
 def to_phys(name, logical, physical):
     return name.replace(logical, physical).replace("inset-", "")
 
 
 class Longhand(object):
     def __init__(self, style_struct, name, spec=None, animation_value_type=None, keyword=None,
-                 predefined_type=None, servo_pref=None, gecko_pref=None,
+                 predefined_type=None,
+                 servo_2013_pref=None,
+                 servo_2020_pref=None,
+                 gecko_pref=None,
                  enabled_in="content", need_index=False,
                  gecko_ffi_name=None,
                  has_effect_on_gecko_scrollbars=None,
                  allowed_in_keyframe_block=True, cast_type='u8',
                  logical=False, logical_group=None, alias=None, extra_prefixes=None, boxed=False,
                  flags=None, allowed_in_page_rule=False, allow_quirks="No",
                  ignored_when_colors_disabled=False,
                  simple_vector_bindings=False,
@@ -186,17 +187,18 @@ class Longhand(object):
         if not spec:
             raise TypeError("Spec should be specified for %s" % name)
         self.spec = spec
         self.keyword = keyword
         self.predefined_type = predefined_type
         self.ident = to_rust_ident(name)
         self.camel_case = to_camel_case(self.ident)
         self.style_struct = style_struct
-        self.servo_pref = servo_pref
+        self.servo_2013_pref = servo_2013_pref
+        self.servo_2020_pref = servo_2020_pref
         self.gecko_pref = gecko_pref
         self.has_effect_on_gecko_scrollbars = has_effect_on_gecko_scrollbars
         assert (
             has_effect_on_gecko_scrollbars in [None, False, True] and
             not style_struct.inherited or
             (gecko_pref is None) == (has_effect_on_gecko_scrollbars is None)), (
             "Property " + name + ": has_effect_on_gecko_scrollbars must be " +
             "specified, and must have a value of True or False, iff a " +
@@ -265,35 +267,45 @@ class Longhand(object):
 
         physical = PHYSICAL_SIDES if logical_side in LOGICAL_SIDES \
             else PHYSICAL_SIZES if logical_side in LOGICAL_SIZES \
             else PHYSICAL_AXES if logical_side in LOGICAL_AXES \
             else LOGICAL_CORNERS
         return [to_phys(self.name, logical_side, physical_side)
                 for physical_side in physical]
 
-    def experimental(self, product):
-        if product == "gecko":
+    def experimental(self, engine):
+        if engine == "gecko":
             return bool(self.gecko_pref)
-        return bool(self.servo_pref)
+        elif engine == "servo-2013":
+            return bool(self.servo_2013_pref)
+        elif engine == "servo-2020":
+            return bool(self.servo_2020_pref)
+        else:
+            raise Exception("Bad engine: " + engine)
 
     # FIXME(emilio): Shorthand and Longhand should really share a base class.
     def explicitly_enabled_in_ua_sheets(self):
         return self.enabled_in in ["ua", "chrome"]
 
     def explicitly_enabled_in_chrome(self):
         return self.enabled_in == "chrome"
 
     def enabled_in_content(self):
         return self.enabled_in == "content"
 
-    def may_be_disabled_in(self, shorthand, product):
-        if product == "gecko":
+    def may_be_disabled_in(self, shorthand, engine):
+        if engine == "gecko":
             return self.gecko_pref and self.gecko_pref != shorthand.gecko_pref
-        return self.servo_pref and self.servo_pref != shorthand.servo_pref
+        elif engine == "servo-2013":
+            return self.servo_2013_pref and self.servo_2013_pref != shorthand.servo_2013_pref
+        elif engine == "servo-2020":
+            return self.servo_2020_pref and self.servo_2020_pref != shorthand.servo_2020_pref
+        else:
+            raise Exception("Bad engine: " + engine)
 
     def base_type(self):
         if self.predefined_type and not self.is_vector:
             return "crate::values::specified::{}".format(self.predefined_type)
         return "longhands::{}::SpecifiedValue".format(self.ident)
 
     def specified_type(self):
         if self.predefined_type and not self.is_vector:
@@ -389,27 +401,31 @@ class Longhand(object):
             return computed
         return "<{} as ToAnimatedValue>::AnimatedValue".format(computed)
 
     def nscsspropertyid(self):
         return "nsCSSPropertyID::eCSSProperty_%s" % self.ident
 
 
 class Shorthand(object):
-    def __init__(self, name, sub_properties, spec=None, servo_pref=None, gecko_pref=None,
+    def __init__(self, name, sub_properties, spec=None,
+                 servo_2013_pref=None,
+                 servo_2020_pref=None,
+                 gecko_pref=None,
                  enabled_in="content",
                  allowed_in_keyframe_block=True, alias=None, extra_prefixes=None,
                  allowed_in_page_rule=False, flags=None):
         self.name = name
         if not spec:
             raise TypeError("Spec should be specified for %s" % name)
         self.spec = spec
         self.ident = to_rust_ident(name)
         self.camel_case = to_camel_case(self.ident)
-        self.servo_pref = servo_pref
+        self.servo_2013_pref = servo_2013_pref
+        self.servo_2020_pref = servo_2020_pref
         self.gecko_pref = gecko_pref
         self.sub_properties = sub_properties
         assert enabled_in in ["", "ua", "chrome", "content"]
         self.enabled_in = enabled_in
         self.alias = parse_property_aliases(alias)
         self.extra_prefixes = parse_property_aliases(extra_prefixes)
         self.allowed_in_page_rule = arg_to_bool(allowed_in_page_rule)
         self.flags = flags.split() if flags else []
@@ -437,20 +453,25 @@ class Shorthand(object):
 
     animatable = property(get_animatable)
     transitionable = property(get_transitionable)
 
     @staticmethod
     def type():
         return "shorthand"
 
-    def experimental(self, product):
-        if product == "gecko":
+    def experimental(self, engine):
+        if engine == "gecko":
             return bool(self.gecko_pref)
-        return bool(self.servo_pref)
+        elif engine == "servo-2013":
+            return bool(self.servo_2013_pref)
+        elif engine == "servo-2020":
+            return bool(self.servo_2020_pref)
+        else:
+            raise Exception("Bad engine: " + engine)
 
     # FIXME(emilio): Shorthand and Longhand should really share a base class.
     def explicitly_enabled_in_ua_sheets(self):
         return self.enabled_in in ["ua", "chrome"]
 
     def explicitly_enabled_in_chrome(self):
         return self.enabled_in == "chrome"
 
@@ -463,31 +484,37 @@ class Shorthand(object):
 
 class Alias(object):
     def __init__(self, name, original, gecko_pref):
         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.animatable = original.animatable
+        self.servo_2013_pref = original.servo_2013_pref
+        self.servo_2020_pref = original.servo_2020_pref
+        self.gecko_pref = gecko_pref
         self.transitionable = original.transitionable
-        self.gecko_pref = gecko_pref
         self.allowed_in_page_rule = original.allowed_in_page_rule
         self.allowed_in_keyframe_block = original.allowed_in_keyframe_block
 
     @staticmethod
     def type():
         return "alias"
 
-    def experimental(self, product):
-        if product == "gecko":
+    def experimental(self, engine):
+        if engine == "gecko":
             return bool(self.gecko_pref)
-        return bool(self.servo_pref)
+        elif engine == "servo-2013":
+            return bool(self.servo_2013_pref)
+        elif engine == "servo-2020":
+            return bool(self.servo_2020_pref)
+        else:
+            raise Exception("Bad engine: " + engine)
 
     def explicitly_enabled_in_ua_sheets(self):
         return self.enabled_in in ["ua", "chrome"]
 
     def explicitly_enabled_in_chrome(self):
         return self.enabled_in == "chrome"
 
     def enabled_in_content(self):
@@ -531,18 +558,18 @@ class StyleStruct(object):
         self.longhands = []
         self.inherited = inherited
         self.gecko_name = gecko_name or name
         self.gecko_ffi_name = "nsStyle" + self.gecko_name
         self.additional_methods = additional_methods or []
 
 
 class PropertiesData(object):
-    def __init__(self, product):
-        self.product = product
+    def __init__(self, engine):
+        self.engine = engine
         self.style_structs = []
         self.current_style_struct = None
         self.longhands = []
         self.longhands_by_name = {}
         self.longhands_by_logical_group = {}
         self.longhand_aliases = []
         self.shorthands = []
         self.shorthands_by_name = {}
@@ -554,40 +581,40 @@ class PropertiesData(object):
         self.current_style_struct = style_struct
 
     def active_style_structs(self):
         return [s for s in self.style_structs if s.additional_methods or s.longhands]
 
     def add_prefixed_aliases(self, property):
         # FIXME Servo's DOM architecture doesn't support vendor-prefixed properties.
         #       See servo/servo#14941.
-        if self.product == "gecko":
+        if self.engine == "gecko":
             for (prefix, pref) in property.extra_prefixes:
                 property.alias.append(('-%s-%s' % (prefix, property.name), pref))
 
-    def declare_longhand(self, name, products="gecko servo", **kwargs):
-        products = products.split()
-        if self.product not in products:
+    def declare_longhand(self, name, engines=None, **kwargs):
+        engines = engines.split()
+        if self.engine not in engines:
             return
 
         longhand = Longhand(self.current_style_struct, name, **kwargs)
         self.add_prefixed_aliases(longhand)
         longhand.alias = list(map(lambda xp: Alias(xp[0], longhand, xp[1]), longhand.alias))
         self.longhand_aliases += longhand.alias
         self.current_style_struct.longhands.append(longhand)
         self.longhands.append(longhand)
         self.longhands_by_name[name] = longhand
         if longhand.logical_group:
             self.longhands_by_logical_group.setdefault(longhand.logical_group, []).append(longhand)
 
         return longhand
 
-    def declare_shorthand(self, name, sub_properties, products="gecko servo", *args, **kwargs):
-        products = products.split()
-        if self.product not in products:
+    def declare_shorthand(self, name, sub_properties, engines, *args, **kwargs):
+        engines = engines.split()
+        if self.engine not in engines:
             return
 
         sub_properties = [self.longhands_by_name[s] for s in sub_properties]
         shorthand = Shorthand(name, sub_properties, *args, **kwargs)
         self.add_prefixed_aliases(shorthand)
         shorthand.alias = list(map(lambda xp: Alias(xp[0], shorthand, xp[1]), shorthand.alias))
         self.shorthand_aliases += shorthand.alias
         self.shorthands.append(shorthand)
@@ -600,29 +627,29 @@ class PropertiesData(object):
     def all_aliases(self):
         return self.longhand_aliases + self.shorthand_aliases
 
 
 def _add_logical_props(data, props):
     groups = set()
     for prop in props:
         if prop not in data.longhands_by_name:
-            assert data.product == "servo"
+            assert data.engine in ["servo-2013", "servo-2020"]
             continue
         prop = data.longhands_by_name[prop]
         if prop.logical_group:
             groups.add(prop.logical_group)
     for group in groups:
         for prop in data.longhands_by_logical_group[group]:
             props.add(prop.name)
 
 
 # These are probably Gecko bugs and should be supported per spec.
-def _remove_common_first_line_and_first_letter_properties(props, product):
-    if product == "gecko":
+def _remove_common_first_line_and_first_letter_properties(props, engine):
+    if engine == "gecko":
         props.remove("-moz-tab-size")
         props.remove("hyphens")
         props.remove("line-break")
         props.remove("text-align-last")
         props.remove("text-emphasis-position")
         props.remove("text-emphasis-style")
         props.remove("text-emphasis-color")
         props.remove("text-decoration-skip-ink")
@@ -639,16 +666,18 @@ def _remove_common_first_line_and_first_
 
 class PropertyRestrictions:
     @staticmethod
     def logical_group(data, group):
         return map(lambda p: p.name, data.longhands_by_logical_group[group])
 
     @staticmethod
     def shorthand(data, shorthand):
+        if shorthand not in data.shorthands_by_name:
+            return []
         return map(lambda p: p.name, data.shorthands_by_name[shorthand].sub_properties)
 
     @staticmethod
     def spec(data, spec_path):
         return map(lambda p: p.name, filter(lambda p: spec_path in p.spec, data.longhands))
 
     # https://drafts.csswg.org/css-pseudo/#first-letter-styling
     @staticmethod
@@ -675,17 +704,17 @@ class PropertyRestrictions:
           + PropertyRestrictions.spec(data, "css-fonts")
           + PropertyRestrictions.spec(data, "css-backgrounds")
           + PropertyRestrictions.spec(data, "css-text")
           + PropertyRestrictions.spec(data, "css-shapes")
           + PropertyRestrictions.spec(data, "css-text-decor"))
 
         _add_logical_props(data, props)
 
-        _remove_common_first_line_and_first_letter_properties(props, data.product)
+        _remove_common_first_line_and_first_letter_properties(props, data.engine)
         return props
 
     # https://drafts.csswg.org/css-pseudo/#first-line-styling
     @staticmethod
     def first_line(data):
         props = set([
             # Per spec.
             "color",
@@ -709,17 +738,17 @@ class PropertyRestrictions:
 
         # These are probably Gecko bugs and should be supported per spec.
         for prop in PropertyRestrictions.shorthand(data, "border"):
             props.remove(prop)
         for prop in PropertyRestrictions.shorthand(data, "border-radius"):
             props.remove(prop)
         props.remove("box-shadow")
 
-        _remove_common_first_line_and_first_letter_properties(props, data.product)
+        _remove_common_first_line_and_first_letter_properties(props, data.engine)
         return props
 
     # https://drafts.csswg.org/css-pseudo/#placeholder
     #
     # The spec says that placeholder and first-line have the same restrictions,
     # but that's not true in Gecko and we also allow a handful other properties
     # for ::placeholder.
     @staticmethod
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1820,30 +1820,30 @@ fn static_assert() {
         debug_assert_ne!(v.len(), 0);
         let input_len = v.len();
         self.gecko.mAnimations.ensure_len(input_len);
 
         self.gecko.mAnimation${gecko_ffi_name}Count = input_len as u32;
 
         for (gecko, servo) in self.gecko.mAnimations.iter_mut().take(input_len as usize).zip(v) {
             let result = match servo {
-                % for value in keyword.gecko_values():
+                % for value in keyword.values_for("gecko"):
                     Keyword::${to_camel_case(value)} =>
                         structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},
                 % endfor
             };
             gecko.m${gecko_ffi_name} = result;
         }
     }
     #[allow(non_snake_case)]
     pub fn animation_${ident}_at(&self, index: usize)
         -> longhands::animation_${ident}::computed_value::SingleComputedValue {
         use crate::properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword;
         match self.gecko.mAnimations[index].m${gecko_ffi_name} ${keyword.maybe_cast("u32")} {
-            % for value in keyword.gecko_values():
+            % for value in keyword.values_for("gecko"):
                 structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
             % endfor
             % if keyword.gecko_inexhaustive:
             _ => panic!("Found unexpected value for animation-${ident}"),
             % endif
         }
     }
     ${impl_animation_count(ident, gecko_ffi_name)}
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -362,17 +362,17 @@
             let v = style_traits::${separator}::parse(input, |parser| {
                 single_value::parse(context, parser)
             })?;
             Ok(SpecifiedValue(v.into()))
         }
 
         pub use self::single_value::SpecifiedValue as SingleSpecifiedValue;
 
-        % if not simple_vector_bindings and product == "gecko":
+        % if not simple_vector_bindings and engine == "gecko":
         impl SpecifiedValue {
             fn compute_iter<'a, 'cx, 'cx_a>(
                 &'a self,
                 context: &'cx Context<'cx_a>,
             ) -> computed_value::Iter<'a, 'cx, 'cx_a> {
                 computed_value::Iter::new(context, &self.0)
             }
         }
@@ -481,28 +481,28 @@
                     return;
                 }
                 PropertyDeclaration::WithVariables(..) => {
                     panic!("variables should already have been substituted")
                 }
                 _ => panic!("entered the wrong cascade_property() implementation"),
             };
 
-            % if property.ident in SYSTEM_FONT_LONGHANDS and product == "gecko":
+            % if property.ident in SYSTEM_FONT_LONGHANDS and engine == "gecko":
                 if let Some(sf) = specified_value.get_system() {
                     longhands::system_font::resolve_system_font(sf, context);
                 }
             % endif
 
             % if not property.style_struct.inherited and property.logical:
                 context.rule_cache_conditions.borrow_mut()
                     .set_writing_mode_dependency(context.builder.writing_mode);
             % endif
 
-            % if property.is_vector and not property.simple_vector_bindings and product == "gecko":
+            % if property.is_vector and not property.simple_vector_bindings and engine == "gecko":
                 // In the case of a vector property we want to pass down an
                 // iterator so that this can be computed without allocation.
                 //
                 // However, computing requires a context, but the style struct
                 // being mutated is on the context. We temporarily remove it,
                 // mutate it, and then put it back. Vector longhands cannot
                 // touch their own style struct whilst computing, else this will
                 // panic.
@@ -538,19 +538,23 @@
                 .map(PropertyDeclaration::${property.camel_case})
         }
     }
 </%def>
 
 <%def name="single_keyword_system(name, values, **kwargs)">
     <%
         keyword_kwargs = {a: kwargs.pop(a, None) for a in [
-            'gecko_constant_prefix', 'gecko_enum_prefix',
-            'extra_gecko_values', 'extra_servo_values',
-            'custom_consts', 'gecko_inexhaustive',
+            'gecko_constant_prefix',
+            'gecko_enum_prefix',
+            'extra_gecko_values',
+            'extra_servo_2013_values',
+            'extra_servo_2020_values',
+            'custom_consts',
+            'gecko_inexhaustive',
         ]}
         keyword = keyword=Keyword(name, values, **keyword_kwargs)
     %>
     <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
         use crate::properties::longhands::system_font::SystemFont;
 
         pub mod computed_value {
             #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
@@ -564,22 +568,22 @@
                 Parse,
                 PartialEq,
                 SpecifiedValueInfo,
                 ToCss,
                 ToResolvedValue,
                 ToShmem,
             )]
             pub enum T {
-            % for value in keyword.values_for(product):
+            % for value in keyword.values_for(engine):
                 ${to_camel_case(value)},
             % endfor
             }
 
-            ${gecko_keyword_conversion(keyword, keyword.values_for(product), type="T", cast_to="i32")}
+            ${gecko_keyword_conversion(keyword, keyword.values_for(engine), type="T", cast_to="i32")}
         }
 
         #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
         #[derive(Clone, Copy, Debug, Eq, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
         pub enum SpecifiedValue {
             Keyword(computed_value::T),
             #[css(skip)]
             System(SystemFont),
@@ -589,23 +593,25 @@
             Ok(SpecifiedValue::Keyword(computed_value::T::parse(input)?))
         }
 
         impl ToComputedValue for SpecifiedValue {
             type ComputedValue = computed_value::T;
             fn to_computed_value(&self, _cx: &Context) -> Self::ComputedValue {
                 match *self {
                     SpecifiedValue::Keyword(v) => v,
-                    SpecifiedValue::System(_) => {
-                        % if product == "gecko":
+                    % if engine == "gecko":
+                        SpecifiedValue::System(_) => {
                             _cx.cached_system_font.as_ref().unwrap().${to_rust_ident(name)}
-                        % else:
-                            unreachable!()
-                        % endif
-                    }
+                        }
+                    % else:
+                        SpecifiedValue::System(system_font) => {
+                            match system_font {}
+                        }
+                    % endif
                 }
             }
             fn from_computed_value(other: &computed_value::T) -> Self {
                 SpecifiedValue::Keyword(*other)
             }
         }
 
         #[inline]
@@ -630,17 +636,17 @@
             }
         }
     </%call>
 </%def>
 
 <%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)">
     <%
         if not values:
-            values = keyword.values_for(product)
+            values = keyword.values_for(engine)
         maybe_cast = "as %s" % cast_to if cast_to else ""
         const_type = cast_to if cast_to else "u32"
     %>
     #[cfg(feature = "gecko")]
     impl ${type} {
         /// Obtain a specified value from a Gecko keyword value
         ///
         /// Intended for use with presentation attributes, not style structs
@@ -698,30 +704,37 @@
         }
     }
 </%def>
 
 <%def name="single_keyword(name, values, vector=False,
             extra_specified=None, needs_conversion=False, **kwargs)">
     <%
         keyword_kwargs = {a: kwargs.pop(a, None) for a in [
-            'gecko_constant_prefix', 'gecko_enum_prefix',
-            'extra_gecko_values', 'extra_servo_values',
-            'aliases', 'extra_gecko_aliases', 'custom_consts',
-            'gecko_inexhaustive', 'gecko_strip_moz_prefix',
+            'gecko_constant_prefix',
+            'gecko_enum_prefix',
+            'extra_gecko_values',
+            'extra_servo_2013_values',
+            'extra_servo_2020_values',
+            'gecko_aliases',
+            'servo_2013_aliases',
+            'servo_2020_aliases',
+            'custom_consts',
+            'gecko_inexhaustive',
+            'gecko_strip_moz_prefix',
         ]}
     %>
 
     <%def name="inner_body(keyword, extra_specified=None, needs_conversion=False)">
         <%def name="variants(variants, include_aliases)">
             % for variant in variants:
             % if include_aliases:
             <%
                 aliases = []
-                for alias, v in keyword.aliases_for(product).iteritems():
+                for alias, v in keyword.aliases_for(engine).iteritems():
                     if variant == v:
                         aliases.append(alias)
             %>
             % if aliases:
             #[parse(aliases = "${','.join(aliases)}")]
             % endif
             % endif
             ${to_camel_case(variant)},
@@ -737,29 +750,29 @@
                 MallocSizeOf,
                 Parse,
                 PartialEq,
                 SpecifiedValueInfo,
                 ToCss,
                 ToShmem,
             )]
             pub enum SpecifiedValue {
-                ${variants(keyword.values_for(product) + extra_specified.split(), bool(extra_specified))}
+                ${variants(keyword.values_for(engine) + extra_specified.split(), bool(extra_specified))}
             }
         % else:
             pub use self::computed_value::T as SpecifiedValue;
         % endif
         pub mod computed_value {
             #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
             #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
             % if not extra_specified:
             #[derive(Parse, SpecifiedValueInfo, ToComputedValue, ToShmem)]
             % endif
             pub enum T {
-                ${variants(data.longhands_by_name[name].keyword.values_for(product), not extra_specified)}
+                ${variants(data.longhands_by_name[name].keyword.values_for(engine), not extra_specified)}
             }
         }
         #[inline]
         pub fn get_initial_value() -> computed_value::T {
             computed_value::T::${to_camel_case(values.split()[0])}
         }
         #[inline]
         pub fn get_initial_specified_value() -> SpecifiedValue {
@@ -768,20 +781,20 @@
         #[inline]
         pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                              -> Result<SpecifiedValue, ParseError<'i>> {
             SpecifiedValue::parse(input)
         }
 
         % if needs_conversion:
             <%
-                conversion_values = keyword.values_for(product)
+                conversion_values = keyword.values_for(engine)
                 if extra_specified:
                     conversion_values += extra_specified.split()
-                conversion_values += keyword.aliases_for(product).keys()
+                conversion_values += keyword.aliases_for(engine).keys()
             %>
             ${gecko_keyword_conversion(keyword, values=conversion_values)}
         % endif
     </%def>
     % if vector:
         <%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
             ${inner_body(Keyword(name, values, **keyword_kwargs))}
             % if caller:
@@ -843,21 +856,21 @@
         /// Represents a serializable set of all of the longhand properties that
         /// correspond to a shorthand.
         % if derive_serialize:
         #[derive(ToCss)]
         % endif
         pub struct LonghandsToSerialize<'a> {
             % for sub_property in shorthand.sub_properties:
                 pub ${sub_property.ident}:
-                % if sub_property.may_be_disabled_in(shorthand, product):
+                % if sub_property.may_be_disabled_in(shorthand, engine):
                     Option<
                 % endif
                     &'a longhands::${sub_property.ident}::SpecifiedValue,
-                % if sub_property.may_be_disabled_in(shorthand, product):
+                % if sub_property.may_be_disabled_in(shorthand, engine):
                     >,
                 % endif
             % endfor
         }
 
         impl<'a> LonghandsToSerialize<'a> {
             /// Tries to get a serializable set of longhands given a set of
             /// property declarations.
@@ -886,17 +899,17 @@
                 match (
                     % for sub_property in shorthand.sub_properties:
                         ${sub_property.ident},
                     % endfor
                 ) {
 
                     (
                     % for sub_property in shorthand.sub_properties:
-                        % if sub_property.may_be_disabled_in(shorthand, product):
+                        % if sub_property.may_be_disabled_in(shorthand, engine):
                         ${sub_property.ident},
                         % else:
                         Some(${sub_property.ident}),
                         % endif
                     % endfor
                     ) =>
                     Ok(LonghandsToSerialize {
                         % for sub_property in shorthand.sub_properties:
@@ -914,23 +927,23 @@
             declarations: &mut SourcePropertyDeclaration,
             context: &ParserContext,
             input: &mut Parser<'i, 't>,
         ) -> Result<(), ParseError<'i>> {
             #[allow(unused_imports)]
             use crate::properties::{NonCustomPropertyId, LonghandId};
             input.parse_entirely(|input| parse_value(context, input)).map(|longhands| {
                 % for sub_property in shorthand.sub_properties:
-                % if sub_property.may_be_disabled_in(shorthand, product):
+                % if sub_property.may_be_disabled_in(shorthand, engine):
                 if NonCustomPropertyId::from(LonghandId::${sub_property.camel_case}).allowed_in(context) {
                 % endif
                     declarations.push(PropertyDeclaration::${sub_property.camel_case}(
                         longhands.${sub_property.ident}
                     ));
-                % if sub_property.may_be_disabled_in(shorthand, product):
+                % if sub_property.may_be_disabled_in(shorthand, engine):
                 }
                 % endif
                 % endfor
             })
         }
 
         ${caller.body()}
     }
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -386,17 +386,17 @@ impl AnimationValue {
 
         <%
             keyfunc = lambda x: (
                 x.specified_type(),
                 x.animated_type(),
                 x.boxed,
                 not x.is_animatable_with_computed_value,
                 x.style_struct.inherited,
-                x.ident in SYSTEM_FONT_LONGHANDS and product == "gecko",
+                x.ident in SYSTEM_FONT_LONGHANDS and engine == "gecko",
             )
         %>
 
         let animatable = match *decl {
             % for (specified_ty, ty, boxed, to_animated, inherit, system), props in groupby(animated_with_logical, key=keyfunc):
             ${" |\n".join("PropertyDeclaration::{}(ref value)".format(prop.camel_case) for prop in props)} => {
                 let decl_repr = unsafe {
                     &*(decl as *const _ as *const PropertyDeclarationVariantRepr<${specified_ty}>)
@@ -846,17 +846,17 @@ impl Animate for AnimatedFilter {
                 Ok(Filter::${func}(this.animate(other, procedure)?))
             },
             % endfor
             % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']:
             (&Filter::${func}(this), &Filter::${func}(other)) => {
                 Ok(Filter::${func}(animate_multiplicative_factor(this, other, procedure)?))
             },
             % endfor
-            % if product == "gecko":
+            % if engine == "gecko":
             (&Filter::DropShadow(ref this), &Filter::DropShadow(ref other)) => {
                 Ok(Filter::DropShadow(this.animate(other, procedure)?))
             },
             % endif
             _ => Err(()),
         }
     }
 }
@@ -866,15 +866,15 @@ impl ToAnimatedZero for AnimatedFilter {
     fn to_animated_zero(&self) -> Result<Self, ()> {
         match *self {
             % for func in ['Blur', 'Grayscale', 'HueRotate', 'Invert', 'Sepia']:
             Filter::${func}(ref this) => Ok(Filter::${func}(this.to_animated_zero()?)),
             % endfor
             % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']:
             Filter::${func}(_) => Ok(Filter::${func}(1.)),
             % endfor
-            % if product == "gecko":
+            % if engine == "gecko":
             Filter::DropShadow(ref this) => Ok(Filter::DropShadow(this.to_animated_zero()?)),
             % endif
             _ => Err(()),
         }
     }
 }
--- a/servo/components/style/properties/longhands/background.mako.rs
+++ b/servo/components/style/properties/longhands/background.mako.rs
@@ -5,101 +5,112 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Background", inherited=False) %>
 
 ${helpers.predefined_type(
     "background-color",
     "Color",
     "computed::Color::transparent()",
+    engines="gecko servo-2013 servo-2020",
     initial_specified_value="SpecifiedValue::transparent()",
     spec="https://drafts.csswg.org/css-backgrounds/#background-color",
     animation_value_type="AnimatedColor",
     ignored_when_colors_disabled=True,
     allow_quirks="Yes",
     flags="CAN_ANIMATE_ON_COMPOSITOR",
 )}
 
 ${helpers.predefined_type(
     "background-image",
     "ImageLayer",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_value="computed::ImageLayer::none()",
     initial_specified_value="specified::ImageLayer::none()",
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
     vector="True",
     animation_value_type="discrete",
     ignored_when_colors_disabled="True",
 )}
 
 % for (axis, direction, initial) in [("x", "Horizontal", "left"), ("y", "Vertical", "top")]:
     ${helpers.predefined_type(
         "background-position-" + axis,
         "position::" + direction + "Position",
+        engines="gecko servo-2013",
         initial_value="computed::LengthPercentage::zero()",
         initial_specified_value="SpecifiedValue::initial_specified_value()",
         spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
         animation_value_type="ComputedValue",
         vector=True,
         vector_animation_type="repeatable_list",
     )}
 % endfor
 
 ${helpers.predefined_type(
     "background-repeat",
     "BackgroundRepeat",
     "computed::BackgroundRepeat::repeat()",
+    engines="gecko servo-2013",
     initial_specified_value="specified::BackgroundRepeat::repeat()",
     animation_value_type="discrete",
     vector=True,
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat",
 )}
 
 ${helpers.single_keyword(
     "background-attachment",
-    "scroll fixed" + (" local" if product == "gecko" else ""),
+    "scroll fixed" + (" local" if engine == "gecko" else ""),
+    engines="gecko servo-2013",
     vector=True,
     gecko_enum_prefix="StyleImageLayerAttachment",
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-attachment",
     animation_value_type="discrete",
 )}
 
 ${helpers.single_keyword(
     "background-clip",
     "border-box padding-box content-box",
+    engines="gecko servo-2013",
     extra_gecko_values="text",
     vector=True, extra_prefixes="webkit",
     gecko_enum_prefix="StyleGeometryBox",
     gecko_inexhaustive=True,
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-clip",
     animation_value_type="discrete",
 )}
 
 ${helpers.single_keyword(
     "background-origin",
     "padding-box border-box content-box",
+    engines="gecko servo-2013",
     vector=True, extra_prefixes="webkit",
     gecko_enum_prefix="StyleGeometryBox",
     gecko_inexhaustive=True,
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-origin",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "background-size",
     "BackgroundSize",
+    engines="gecko servo-2013",
     initial_value="computed::BackgroundSize::auto()",
     initial_specified_value="specified::BackgroundSize::auto()",
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",
     vector=True,
     vector_animation_type="repeatable_list",
     animation_value_type="BackgroundSizeList",
     extra_prefixes="webkit")}
 
 // https://drafts.fxtf.org/compositing/#background-blend-mode
 ${helpers.single_keyword(
     "background-blend-mode",
     """normal multiply screen overlay darken lighten color-dodge
     color-burn hard-light soft-light difference exclusion hue
     saturation color luminosity""",
     gecko_constant_prefix="NS_STYLE_BLEND",
-    vector=True, products="gecko", animation_value_type="discrete",
+    vector=True,
+    engines="gecko",
+    animation_value_type="discrete",
     spec="https://drafts.fxtf.org/compositing/#background-blend-mode",
 )}
--- a/servo/components/style/properties/longhands/border.mako.rs
+++ b/servo/components/style/properties/longhands/border.mako.rs
@@ -18,42 +18,47 @@
 % for side in ALL_SIDES:
     <%
         side_name = side[0]
         is_logical = side[1]
     %>
     ${helpers.predefined_type(
         "border-%s-color" % side_name, "Color",
         "computed_value::T::currentcolor()",
-        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
+        engines="gecko servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
+        alias=maybe_moz_logical_alias(engine, side, "-moz-border-%s-color"),
         spec=maybe_logical_spec(side, "color"),
         animation_value_type="AnimatedColor",
         logical=is_logical,
         logical_group="border-color",
         allow_quirks="No" if is_logical else "Yes",
         ignored_when_colors_disabled=True,
     )}
 
     ${helpers.predefined_type(
         "border-%s-style" % side_name, "BorderStyle",
         "specified::BorderStyle::None",
-        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
+        engines="gecko servo-2013 servo-2020",
+        alias=maybe_moz_logical_alias(engine, side, "-moz-border-%s-style"),
         spec=maybe_logical_spec(side, "style"),
         animation_value_type="discrete" if not is_logical else "none",
         logical=is_logical,
         logical_group="border-style",
         needs_context=False,
     )}
 
     ${helpers.predefined_type(
         "border-%s-width" % side_name,
         "BorderSideWidth",
         "crate::values::computed::NonNegativeLength::new(3.)",
+        engines="gecko servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
         computed_type="crate::values::computed::NonNegativeLength",
-        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-width"),
+        alias=maybe_moz_logical_alias(engine, side, "-moz-border-%s-width"),
         spec=maybe_logical_spec(side, "width"),
         animation_value_type="NonNegativeLength",
         logical=is_logical,
         logical_group="border-width",
         flags="GETCS_NEEDS_LAYOUT_FLUSH",
         allow_quirks="No" if is_logical else "Yes",
         servo_restyle_damage="reflow rebuild_and_reflow_inline"
     )}
@@ -68,86 +73,92 @@
         else:
             prefixes = "webkit"
     %>
     ${helpers.predefined_type(
         "border-%s-radius" % corner_name,
         "BorderCornerRadius",
         "computed::BorderCornerRadius::zero()",
         "parse",
+        engines="gecko servo-2013",
         extra_prefixes=prefixes,
         spec=maybe_logical_spec(corner, "radius"),
         boxed=True,
         animation_value_type="BorderCornerRadius",
         logical_group="border-radius",
         logical=is_logical,
     )}
 % endfor
 
 ${helpers.single_keyword(
     "box-decoration-break",
     "slice clone",
+    engines="gecko",
     gecko_enum_prefix="StyleBoxDecorationBreak",
     spec="https://drafts.csswg.org/css-break/#propdef-box-decoration-break",
-    products="gecko",
     animation_value_type="discrete",
 )}
 
 ${helpers.single_keyword(
     "-moz-float-edge",
     "content-box margin-box",
+    engines="gecko",
     gecko_ffi_name="mFloatEdge",
     gecko_enum_prefix="StyleFloatEdge",
-    products="gecko",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "border-image-source",
     "ImageLayer",
+    engines="gecko servo-2013",
     initial_value="computed::ImageLayer::none()",
     initial_specified_value="specified::ImageLayer::none()",
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
     vector=False,
     animation_value_type="discrete",
-    boxed=product == "servo",
+    boxed=engine == "servo-2013",
     ignored_when_colors_disabled=True
 )}
 
 ${helpers.predefined_type(
     "border-image-outset",
     "NonNegativeLengthOrNumberRect",
+    engines="gecko servo-2013",
     initial_value="generics::rect::Rect::all(computed::NonNegativeLengthOrNumber::zero())",
     initial_specified_value="generics::rect::Rect::all(specified::NonNegativeLengthOrNumber::zero())",
     spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
     animation_value_type="discrete",
     boxed=True,
 )}
 
 ${helpers.predefined_type(
     "border-image-repeat",
     "BorderImageRepeat",
     "computed::BorderImageRepeat::stretch()",
+    engines="gecko servo-2013",
     initial_specified_value="specified::BorderImageRepeat::stretch()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat",
 )}
 
 ${helpers.predefined_type(
     "border-image-width",
     "BorderImageWidth",
+    engines="gecko servo-2013",
     initial_value="computed::BorderImageWidth::all(computed::BorderImageSideWidth::one())",
     initial_specified_value="specified::BorderImageWidth::all(specified::BorderImageSideWidth::one())",
     spec="https://drafts.csswg.org/css-backgrounds/#border-image-width",
     animation_value_type="discrete",
     boxed=True,
 )}
 
 ${helpers.predefined_type(
     "border-image-slice",
     "BorderImageSlice",
+    engines="gecko servo-2013",
     initial_value="computed::BorderImageSlice::hundred_percent()",
     initial_specified_value="specified::BorderImageSlice::hundred_percent()",
     spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice",
     animation_value_type="discrete",
     boxed=True,
 )}
--- a/servo/components/style/properties/longhands/box.mako.rs
+++ b/servo/components/style/properties/longhands/box.mako.rs
@@ -8,646 +8,692 @@
 <% data.new_style_struct("Box",
                          inherited=False,
                          gecko_name="Display") %>
 
 ${helpers.predefined_type(
     "display",
     "Display",
     "computed::Display::inline()",
+    engines="gecko servo-2013 servo-2020",
     initial_specified_value="specified::Display::inline()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-display/#propdef-display",
     servo_restyle_damage="rebuild_and_reflow",
-    needs_context=product == "gecko"
 )}
 
 ${helpers.single_keyword(
     "-moz-top-layer",
     "none top",
+    engines="gecko",
     gecko_constant_prefix="NS_STYLE_TOP_LAYER",
     gecko_ffi_name="mTopLayer",
-    products="gecko",
     animation_value_type="none",
     enabled_in="ua",
     spec="Internal (not web-exposed)",
 )}
 
 // An internal-only property for elements in a top layer
 // https://fullscreen.spec.whatwg.org/#top-layer
 ${helpers.single_keyword(
     "-servo-top-layer",
     "none top",
-    products="servo",
+    engines="servo-2013 servo-2020",
     animation_value_type="none",
     enabled_in="ua",
     spec="Internal (not web-exposed)",
 )}
 
 ${helpers.single_keyword(
     "position",
-    "static absolute relative fixed sticky",
+    "static absolute relative fixed" + (" sticky" if engine in ["gecko", "servo-2013"] else ""),
+    engines="gecko servo-2013 servo-2020",
     animation_value_type="discrete",
     flags="CREATES_STACKING_CONTEXT ABSPOS_CB",
     spec="https://drafts.csswg.org/css-position/#position-property",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "float",
     "Float",
     "computed::Float::None",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::Float::None",
     spec="https://drafts.csswg.org/css-box/#propdef-float",
     animation_value_type="discrete",
     needs_context=False,
     servo_restyle_damage="rebuild_and_reflow",
     gecko_ffi_name="mFloat",
 )}
 
 ${helpers.predefined_type(
     "clear",
     "Clear",
     "computed::Clear::None",
+    engines="gecko servo-2013",
     animation_value_type="discrete",
     needs_context=False,
     gecko_ffi_name="mBreakType",
     spec="https://drafts.csswg.org/css-box/#propdef-clear",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "vertical-align",
     "VerticalAlign",
     "computed::VerticalAlign::baseline()",
+    engines="gecko servo-2013",
     animation_value_type="ComputedValue",
     spec="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align",
     servo_restyle_damage = "reflow",
 )}
 
 // CSS 2.1, Section 11 - Visual effects
 
-${helpers.single_keyword("-servo-overflow-clip-box", "padding-box content-box",
-    products="servo", animation_value_type="none", enabled_in="ua",
+${helpers.single_keyword(
+    "-servo-overflow-clip-box",
+    "padding-box content-box",
+    engines="servo-2013",
+    animation_value_type="none",
+    enabled_in="ua",
     spec="Internal, not web-exposed, \
-          may be standardized in the future (https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)")}
+          may be standardized in the future (https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)",
+)}
 
 % for direction in ["inline", "block"]:
     ${helpers.predefined_type(
         "overflow-clip-box-" + direction,
         "OverflowClipBox",
         "computed::OverflowClipBox::PaddingBox",
-        products="gecko",
+        engines="gecko",
         enabled_in="ua",
         needs_context=False,
         gecko_pref="layout.css.overflow-clip-box.enabled",
         animation_value_type="discrete",
         spec="Internal, may be standardized in the future: \
               https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box",
     )}
 % endfor
 
 % for (axis, logical) in ALL_AXES:
     <% full_name = "overflow-{}".format(axis) %>
     ${helpers.predefined_type(
         full_name,
         "Overflow",
         "computed::Overflow::Visible",
+        engines="gecko servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
         logical_group="overflow",
         logical=logical,
         animation_value_type="discrete",
         spec="https://drafts.csswg.org/css-overflow-3/#propdef-{}".format(full_name),
         needs_context=False,
         servo_restyle_damage = "reflow",
         gecko_pref="layout.css.overflow-logical.enabled" if logical else None,
     )}
 % endfor
 
 ${helpers.predefined_type(
     "overflow-anchor",
     "OverflowAnchor",
     "computed::OverflowAnchor::Auto",
+    engines="gecko",
     initial_specified_value="specified::OverflowAnchor::Auto",
-    products="gecko",
     needs_context=False,
     gecko_pref="layout.css.scroll-anchoring.enabled",
     spec="https://drafts.csswg.org/css-scroll-anchoring/#exclusion-api",
     animation_value_type="discrete",
 )}
 
 <% transition_extra_prefixes = "moz:layout.css.prefixes.transitions webkit" %>
 
 ${helpers.predefined_type(
     "transition-duration",
     "Time",
     "computed::Time::zero()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::Time::zero()",
     parse_method="parse_non_negative",
     vector=True,
     need_index=True,
     animation_value_type="none",
     extra_prefixes=transition_extra_prefixes,
     spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration",
 )}
 
 ${helpers.predefined_type(
     "transition-timing-function",
     "TimingFunction",
     "computed::TimingFunction::ease()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::TimingFunction::ease()",
     vector=True,
     need_index=True,
     animation_value_type="none",
     extra_prefixes=transition_extra_prefixes,
     spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function",
 )}
 
 ${helpers.predefined_type(
     "transition-property",
     "TransitionProperty",
     "computed::TransitionProperty::all()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::TransitionProperty::all()",
     vector=True,
     allow_empty="NotInitial",
     need_index=True,
     animation_value_type="none",
     extra_prefixes=transition_extra_prefixes,
     spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property",
 )}
 
 ${helpers.predefined_type(
     "transition-delay",
     "Time",
     "computed::Time::zero()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::Time::zero()",
     vector=True,
     need_index=True,
     animation_value_type="none",
     extra_prefixes=transition_extra_prefixes,
     spec="https://drafts.csswg.org/css-transitions/#propdef-transition-delay",
 )}
 
 <% animation_extra_prefixes = "moz:layout.css.prefixes.animations webkit" %>
 
 ${helpers.predefined_type(
     "animation-name",
     "AnimationName",
     "computed::AnimationName::none()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::AnimationName::none()",
     vector=True,
     need_index=True,
     animation_value_type="none",
     extra_prefixes=animation_extra_prefixes,
     allowed_in_keyframe_block=False,
     spec="https://drafts.csswg.org/css-animations/#propdef-animation-name",
 )}
 
 ${helpers.predefined_type(
     "animation-duration",
     "Time",
     "computed::Time::zero()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::Time::zero()",
     parse_method="parse_non_negative",
     vector=True,
     need_index=True,
     animation_value_type="none",
     extra_prefixes=animation_extra_prefixes,
     spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration",
 )}
 
 // animation-timing-function is the exception to the rule for allowed_in_keyframe_block:
 // https://drafts.csswg.org/css-animations/#keyframes
 ${helpers.predefined_type(
     "animation-timing-function",
     "TimingFunction",
     "computed::TimingFunction::ease()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::TimingFunction::ease()",
     vector=True,
     need_index=True,
     animation_value_type="none",
     extra_prefixes=animation_extra_prefixes,
     allowed_in_keyframe_block=True,
     spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function",
 )}
 
 ${helpers.predefined_type(
     "animation-iteration-count",
     "AnimationIterationCount",
     "computed::AnimationIterationCount::one()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::AnimationIterationCount::one()",
     vector=True,
     need_index=True,
     animation_value_type="none",
     extra_prefixes=animation_extra_prefixes,
     allowed_in_keyframe_block=False,
     spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count",
 )}
 
 <% animation_direction_custom_consts = { "alternate-reverse": "Alternate_reverse" } %>
 ${helpers.single_keyword(
     "animation-direction",
     "normal reverse alternate alternate-reverse",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     need_index=True,
     animation_value_type="none",
     vector=True,
     gecko_enum_prefix="PlaybackDirection",
     custom_consts=animation_direction_custom_consts,
     extra_prefixes=animation_extra_prefixes,
     gecko_inexhaustive=True,
     spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction",
     allowed_in_keyframe_block=False,
 )}
 
 ${helpers.single_keyword(
     "animation-play-state",
     "running paused",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     need_index=True,
     animation_value_type="none",
     vector=True,
     extra_prefixes=animation_extra_prefixes,
     gecko_enum_prefix="StyleAnimationPlayState",
     spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state",
     allowed_in_keyframe_block=False,
 )}
 
 ${helpers.single_keyword(
     "animation-fill-mode",
     "none forwards backwards both",
+    engines="gecko servo-2013",
     need_index=True,
     animation_value_type="none",
     vector=True,
     gecko_enum_prefix="FillMode",
     extra_prefixes=animation_extra_prefixes,
     gecko_inexhaustive=True,
     spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode",
     allowed_in_keyframe_block=False,
 )}
 
 ${helpers.predefined_type(
     "animation-delay",
     "Time",
     "computed::Time::zero()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::Time::zero()",
     vector=True,
     need_index=True,
     animation_value_type="none",
     extra_prefixes=animation_extra_prefixes,
     spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay",
     allowed_in_keyframe_block=False,
 )}
 
 <% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %>
 
 ${helpers.predefined_type(
     "transform",
     "Transform",
     "generics::transform::Transform::none()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     extra_prefixes=transform_extra_prefixes,
     animation_value_type="ComputedValue",
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB \
            GETCS_NEEDS_LAYOUT_FLUSH CAN_ANIMATE_ON_COMPOSITOR",
     spec="https://drafts.csswg.org/css-transforms/#propdef-transform",
     servo_restyle_damage="reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type(
     "rotate",
     "Rotate",
     "generics::transform::Rotate::None",
+    engines="gecko servo-2013",
     animation_value_type="ComputedValue",
     boxed=True,
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR",
     gecko_pref="layout.css.individual-transform.enabled",
     spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
     servo_restyle_damage = "reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type(
     "scale",
     "Scale",
     "generics::transform::Scale::None",
+    engines="gecko servo-2013",
     animation_value_type="ComputedValue",
     boxed=True,
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR",
     gecko_pref="layout.css.individual-transform.enabled",
     spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
     servo_restyle_damage = "reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type(
     "translate",
     "Translate",
     "generics::transform::Translate::None",
+    engines="gecko servo-2013",
     animation_value_type="ComputedValue",
     boxed=True,
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR",
     gecko_pref="layout.css.individual-transform.enabled",
     spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
     servo_restyle_damage="reflow_out_of_flow",
 )}
 
 // Motion Path Module Level 1
 ${helpers.predefined_type(
     "offset-path",
     "OffsetPath",
     "computed::OffsetPath::none()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     gecko_pref="layout.css.motion-path.enabled",
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     spec="https://drafts.fxtf.org/motion-1/#offset-path-property",
     servo_restyle_damage="reflow_out_of_flow"
 )}
 
 // Motion Path Module Level 1
 ${helpers.predefined_type(
     "offset-distance",
     "LengthPercentage",
     "computed::LengthPercentage::zero()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     gecko_pref="layout.css.motion-path.enabled",
     spec="https://drafts.fxtf.org/motion-1/#offset-distance-property",
     servo_restyle_damage="reflow_out_of_flow"
 )}
 
 // Motion Path Module Level 1
 ${helpers.predefined_type(
     "offset-rotate",
     "OffsetRotate",
     "computed::OffsetRotate::auto()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     gecko_pref="layout.css.motion-path.enabled",
     spec="https://drafts.fxtf.org/motion-1/#offset-rotate-property",
     servo_restyle_damage="reflow_out_of_flow"
 )}
 
 // CSSOM View Module
 // https://www.w3.org/TR/cssom-view-1/
 ${helpers.single_keyword(
     "scroll-behavior",
     "auto smooth",
-    products="gecko",
+    engines="gecko",
     spec="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "scroll-snap-align",
     "ScrollSnapAlign",
     "computed::ScrollSnapAlign::none()",
-    products="gecko",
+    engines="gecko",
     gecko_pref="layout.css.scroll-snap-v1.enabled",
     spec="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "scroll-snap-type",
     "ScrollSnapType",
     "computed::ScrollSnapType::none()",
-    products="gecko",
+    engines="gecko",
     spec="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type",
     animation_value_type="discrete",
 )}
 
 % for axis in ["x", "y"]:
     ${helpers.predefined_type(
         "overscroll-behavior-" + axis,
         "OverscrollBehavior",
         "computed::OverscrollBehavior::Auto",
-        products="gecko",
+        engines="gecko",
         needs_context=False,
         gecko_pref="layout.css.overscroll-behavior.enabled",
         spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties",
         animation_value_type="discrete",
     )}
 % endfor
 
 // Compositing and Blending Level 1
 // http://www.w3.org/TR/compositing-1/
 ${helpers.single_keyword(
     "isolation",
     "auto isolate",
-    products="gecko",
+    engines="gecko",
     spec="https://drafts.fxtf.org/compositing/#isolation",
     flags="CREATES_STACKING_CONTEXT",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "break-after",
     "BreakBetween",
     "computed::BreakBetween::Auto",
+    engines="gecko",
     needs_context=False,
-    products="gecko",
     spec="https://drafts.csswg.org/css-break/#propdef-break-after",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "break-before",
     "BreakBetween",
     "computed::BreakBetween::Auto",
+    engines="gecko",
     needs_context=False,
-    products="gecko",
     spec="https://drafts.csswg.org/css-break/#propdef-break-before",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "break-inside",
     "BreakWithin",
     "computed::BreakWithin::Auto",
+    engines="gecko",
     needs_context=False,
-    products="gecko",
     alias="page-break-inside",
     spec="https://drafts.csswg.org/css-break/#propdef-break-inside",
     animation_value_type="discrete",
 )}
 
 // CSS Basic User Interface Module Level 3
 // http://dev.w3.org/csswg/css-ui
 ${helpers.predefined_type(
     "resize",
     "Resize",
     "computed::Resize::None",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     needs_context=False,
     gecko_ffi_name="mResize",
     spec="https://drafts.csswg.org/css-ui/#propdef-resize",
 )}
 
 ${helpers.predefined_type(
     "perspective",
     "Perspective",
     "computed::Perspective::none()",
+    engines="gecko servo-2013",
     gecko_ffi_name="mChildPerspective",
     spec="https://drafts.csswg.org/css-transforms/#perspective",
     extra_prefixes=transform_extra_prefixes,
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     animation_value_type="AnimatedPerspective",
     servo_restyle_damage = "reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type(
     "perspective-origin",
     "Position",
     "computed::position::Position::center()",
+    engines="gecko servo-2013",
     boxed=True,
     extra_prefixes=transform_extra_prefixes,
     spec="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property",
     flags="GETCS_NEEDS_LAYOUT_FLUSH",
     animation_value_type="ComputedValue",
     servo_restyle_damage="reflow_out_of_flow"
 )}
 
 ${helpers.single_keyword(
     "backface-visibility",
     "visible hidden",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property",
     extra_prefixes=transform_extra_prefixes,
     animation_value_type="discrete",
 )}
 
 ${helpers.single_keyword(
     "transform-box",
     "border-box fill-box view-box",
+    engines="gecko",
     gecko_enum_prefix="StyleGeometryBox",
-    products="gecko",
     gecko_pref="svg.transform-box.enabled",
     spec="https://drafts.csswg.org/css-transforms/#transform-box",
     gecko_inexhaustive="True",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "transform-style",
     "TransformStyle",
-    "computed::TransformStyle::" + ("Auto" if product == "servo" else "Flat"),
+    "computed::TransformStyle::" + ("Flat" if engine == "gecko" else "Auto"),
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
     needs_context=False,
     extra_prefixes=transform_extra_prefixes,
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     animation_value_type="discrete",
     servo_restyle_damage = "reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type(
     "transform-origin",
     "TransformOrigin",
     "computed::TransformOrigin::initial_value()",
+    engines="gecko servo-2013",
     animation_value_type="ComputedValue",
     extra_prefixes=transform_extra_prefixes,
     gecko_ffi_name="mTransformOrigin",
     boxed=True,
     flags="GETCS_NEEDS_LAYOUT_FLUSH",
     spec="https://drafts.csswg.org/css-transforms/#transform-origin-property",
     servo_restyle_damage="reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type(
     "contain",
     "Contain",
     "specified::Contain::empty()",
+    engines="gecko",
     animation_value_type="none",
-    products="gecko",
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     gecko_pref="layout.css.contain.enabled",
     spec="https://drafts.csswg.org/css-contain/#contain-property",
     enabled_in="chrome",
 )}
 
 // Non-standard
 ${helpers.predefined_type(
     "-moz-appearance",
     "Appearance",
     "computed::Appearance::None",
-    products="gecko",
+    engines="gecko",
     alias="-webkit-appearance:layout.css.webkit-appearance.enabled",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
     animation_value_type="discrete",
     gecko_ffi_name="mAppearance",
 )}
 
 ${helpers.predefined_type(
     "-moz-binding",
     "url::UrlOrNone",
     "computed::url::UrlOrNone::none()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="none",
     gecko_ffi_name="mBinding",
     gecko_pref="layout.css.moz-binding.content.enabled",
     enabled_in="chrome",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)",
 )}
 
 ${helpers.single_keyword(
     "-moz-orient",
     "inline block horizontal vertical",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mOrient",
     gecko_enum_prefix="StyleOrient",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-orient)",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "will-change",
     "WillChange",
     "computed::WillChange::auto()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="none",
     spec="https://drafts.csswg.org/css-will-change/#will-change",
 )}
 
 // The spec issue for the parse_method: https://github.com/w3c/csswg-drafts/issues/4102.
 ${helpers.predefined_type(
     "shape-image-threshold",
     "Opacity",
     "0.0",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     spec="https://drafts.csswg.org/css-shapes/#shape-image-threshold-property",
 )}
 
 ${helpers.predefined_type(
     "shape-margin",
     "NonNegativeLengthPercentage",
     "computed::NonNegativeLengthPercentage::zero()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="NonNegativeLengthPercentage",
     spec="https://drafts.csswg.org/css-shapes/#shape-margin-property",
 )}
 
 ${helpers.predefined_type(
     "shape-outside",
     "basic_shape::FloatAreaShape",
     "generics::basic_shape::ShapeSource::None",
-    products="gecko",
+    engines="gecko",
     animation_value_type="basic_shape::FloatAreaShape",
     spec="https://drafts.csswg.org/css-shapes/#shape-outside-property",
 )}
 
 ${helpers.predefined_type(
     "touch-action",
     "TouchAction",
     "computed::TouchAction::auto()",
-    products="gecko",
+    engines="gecko",
     gecko_pref="layout.css.touch_action.enabled",
     animation_value_type="discrete",
     spec="https://compat.spec.whatwg.org/#touch-action",
 )}
 
 // Note that we only implement -webkit-line-clamp as a single, longhand
 // property for now, but the spec defines line-clamp as a shorthand for separate
 // max-lines, block-ellipsis, and continue properties.
 ${helpers.predefined_type(
     "-webkit-line-clamp",
     "PositiveIntegerOrNone",
     "Either::Second(None_)",
+    engines="gecko",
     gecko_pref="layout.css.webkit-line-clamp.enabled",
     animation_value_type="Integer",
-    products="gecko",
     spec="https://drafts.csswg.org/css-overflow-3/#line-clamp",
 )}
--- a/servo/components/style/properties/longhands/column.mako.rs
+++ b/servo/components/style/properties/longhands/column.mako.rs
@@ -5,87 +5,91 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Column", inherited=False) %>
 
 ${helpers.predefined_type(
     "column-width",
     "length::NonNegativeLengthOrAuto",
     "computed::length::NonNegativeLengthOrAuto::auto()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::length::NonNegativeLengthOrAuto::auto()",
     extra_prefixes="moz",
     animation_value_type="NonNegativeLengthOrAuto",
-    servo_pref="layout.columns.enabled",
+    servo_2013_pref="layout.columns.enabled",
     spec="https://drafts.csswg.org/css-multicol/#propdef-column-width",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "column-count",
     "ColumnCount",
     "computed::ColumnCount::auto()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::ColumnCount::auto()",
-    servo_pref="layout.columns.enabled",
+    servo_2013_pref="layout.columns.enabled",
     animation_value_type="AnimatedColumnCount",
     extra_prefixes="moz",
     spec="https://drafts.csswg.org/css-multicol/#propdef-column-count",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.single_keyword(
     "column-fill",
     "balance auto",
+    engines="gecko",
     extra_prefixes="moz",
-    products="gecko",
     animation_value_type="discrete",
     gecko_enum_prefix="StyleColumnFill",
     spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill",
 )}
 
 ${helpers.predefined_type(
     "column-rule-width",
     "BorderSideWidth",
     "crate::values::computed::NonNegativeLength::new(3.)",
+    engines="gecko",
     initial_specified_value="specified::BorderSideWidth::Medium",
     computed_type="crate::values::computed::NonNegativeLength",
-    products="gecko",
     spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
     animation_value_type="NonNegativeLength",
     extra_prefixes="moz",
 )}
 
 // https://drafts.csswg.org/css-multicol-1/#crc
 ${helpers.predefined_type(
     "column-rule-color",
     "Color",
     "computed_value::T::currentcolor()",
+    engines="gecko",
     initial_specified_value="specified::Color::currentcolor()",
-    products="gecko",
     animation_value_type="AnimatedColor",
     extra_prefixes="moz",
     ignored_when_colors_disabled=True,
     spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
 )}
 
 // FIXME: Remove enabled_in="ua" once column-span is enabled on nightly (bug 1423383).
 ${helpers.single_keyword(
     "column-span",
     "none all",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     gecko_enum_prefix="StyleColumnSpan",
     gecko_pref="layout.css.column-span.enabled",
     enabled_in="ua",
     spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
     extra_prefixes="moz:layout.css.column-span.enabled",
 )}
 
 ${helpers.predefined_type(
     "column-rule-style",
     "BorderStyle",
     "computed::BorderStyle::None",
+    engines="gecko",
     needs_context=False,
     initial_specified_value="specified::BorderStyle::None",
-    products="gecko",
     extra_prefixes="moz",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style",
 )}
--- a/servo/components/style/properties/longhands/counters.mako.rs
+++ b/servo/components/style/properties/longhands/counters.mako.rs
@@ -5,41 +5,45 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Counters", inherited=False, gecko_name="Content") %>
 
 ${helpers.predefined_type(
     "content",
     "Content",
     "computed::Content::normal()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::Content::normal()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-content/#propdef-content",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "counter-increment",
     "CounterIncrement",
+    engines="gecko servo-2013",
     initial_value="Default::default()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "counter-reset",
     "CounterSetOrReset",
+    engines="gecko servo-2013",
     initial_value="Default::default()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "counter-set",
     "CounterSetOrReset",
+    engines="gecko",
     initial_value="Default::default()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-set",
     servo_restyle_damage="rebuild_and_reflow",
-    products="gecko",
 )}
--- a/servo/components/style/properties/longhands/effects.mako.rs
+++ b/servo/components/style/properties/longhands/effects.mako.rs
@@ -6,78 +6,88 @@
 
 // Box-shadow, etc.
 <% data.new_style_struct("Effects", inherited=False) %>
 
 ${helpers.predefined_type(
     "opacity",
     "Opacity",
     "1.0",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     animation_value_type="ComputedValue",
     flags="CREATES_STACKING_CONTEXT CAN_ANIMATE_ON_COMPOSITOR",
     spec="https://drafts.csswg.org/css-color/#transparency",
     servo_restyle_damage = "reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type(
     "box-shadow",
     "BoxShadow",
     None,
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     vector=True,
     simple_vector_bindings=True,
     animation_value_type="AnimatedBoxShadowList",
     vector_animation_type="with_zero",
     extra_prefixes="webkit",
     ignored_when_colors_disabled=True,
     spec="https://drafts.csswg.org/css-backgrounds/#box-shadow",
 )}
 
 ${helpers.predefined_type(
     "clip",
     "ClipRectOrAuto",
     "computed::ClipRectOrAuto::auto()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     animation_value_type="ComputedValue",
     boxed=True,
     allow_quirks="Yes",
     spec="https://drafts.fxtf.org/css-masking/#clip-property",
 )}
 
 ${helpers.predefined_type(
     "filter",
     "Filter",
     None,
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     vector=True,
     simple_vector_bindings=True,
     gecko_ffi_name="mFilters",
     separator="Space",
     animation_value_type="AnimatedFilterList",
     vector_animation_type="with_zero",
     extra_prefixes="webkit",
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     spec="https://drafts.fxtf.org/filters/#propdef-filter",
 )}
 
 ${helpers.predefined_type(
     "backdrop-filter",
     "Filter",
     None,
+    engines="gecko",
     vector=True,
     simple_vector_bindings=True,
     gecko_ffi_name="mBackdropFilters",
     separator="Space",
     animation_value_type="AnimatedFilterList",
     vector_animation_type="with_zero",
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     gecko_pref="layout.css.backdrop-filter.enabled",
     spec="https://drafts.fxtf.org/filter-effects-2/#propdef-backdrop-filter",
-    products="gecko",
 )}
 
 ${helpers.single_keyword(
     "mix-blend-mode",
     """normal multiply screen overlay darken lighten color-dodge
     color-burn hard-light soft-light difference exclusion hue
     saturation color luminosity""",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     gecko_constant_prefix="NS_STYLE_BLEND",
     animation_value_type="discrete",
     flags="CREATES_STACKING_CONTEXT",
     spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode",
 )}
--- a/servo/components/style/properties/longhands/font.mako.rs
+++ b/servo/components/style/properties/longhands/font.mako.rs
@@ -5,286 +5,297 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import Method, to_camel_case, to_rust_ident, to_camel_case_lower, SYSTEM_FONT_LONGHANDS %>
 
 <% data.new_style_struct("Font", inherited=True) %>
 
 ${helpers.predefined_type(
     "font-family",
     "FontFamily",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_value="computed::FontFamily::serif()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-family",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "font-style",
     "FontStyle",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_value="computed::FontStyle::normal()",
     initial_specified_value="specified::FontStyle::normal()",
     animation_value_type="FontStyle",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-style",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 <% font_variant_caps_custom_consts= { "small-caps": "SMALLCAPS",
                                       "all-small-caps": "ALLSMALL",
                                       "petite-caps": "PETITECAPS",
                                       "all-petite-caps": "ALLPETITE",
                                       "titling-caps": "TITLING" } %>
 
 ${helpers.single_keyword_system(
     "font-variant-caps",
     "normal small-caps",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     extra_gecko_values="all-small-caps petite-caps all-petite-caps unicase titling-caps",
     gecko_constant_prefix="NS_FONT_VARIANT_CAPS",
     gecko_ffi_name="mFont.variantCaps",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-caps",
     custom_consts=font_variant_caps_custom_consts,
     animation_value_type="discrete",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "font-weight",
     "FontWeight",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_value="computed::FontWeight::normal()",
     initial_specified_value="specified::FontWeight::normal()",
     animation_value_type="Number",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "font-size",
     "FontSize",
+    engines="gecko servo-2013 servo-2020",
     initial_value="computed::FontSize::medium()",
     initial_specified_value="specified::FontSize::medium()",
     animation_value_type="NonNegativeLength",
     allow_quirks="Yes",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-size",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "font-size-adjust",
     "FontSizeAdjust",
-    products="gecko",
+    engines="gecko",
     initial_value="computed::FontSizeAdjust::none()",
     initial_specified_value="specified::FontSizeAdjust::none()",
     animation_value_type="ComputedValue",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust",
 )}
 
 ${helpers.predefined_type(
     "font-synthesis",
     "FontSynthesis",
-    products="gecko",
+    engines="gecko",
     initial_value="specified::FontSynthesis::get_initial_value()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis",
 )}
 
 ${helpers.predefined_type(
     "font-stretch",
     "FontStretch",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_value="computed::FontStretch::hundred()",
     initial_specified_value="specified::FontStretch::normal()",
     animation_value_type="Percentage",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.single_keyword_system(
     "font-kerning",
     "auto none normal",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mFont.kerning",
     gecko_constant_prefix="NS_FONT_KERNING",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-kerning",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "font-variant-alternates",
     "FontVariantAlternates",
-    products="gecko",
+    engines="gecko",
     initial_value="computed::FontVariantAlternates::get_initial_value()",
     initial_specified_value="specified::FontVariantAlternates::get_initial_specified_value()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates",
 )}
 
 ${helpers.predefined_type(
     "font-variant-east-asian",
     "FontVariantEastAsian",
-    products="gecko",
+    engines="gecko",
     initial_value="computed::FontVariantEastAsian::empty()",
     initial_specified_value="specified::FontVariantEastAsian::empty()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian",
 )}
 
 ${helpers.predefined_type(
     "font-variant-ligatures",
     "FontVariantLigatures",
-    products="gecko",
+    engines="gecko",
     initial_value="computed::FontVariantLigatures::empty()",
     initial_specified_value="specified::FontVariantLigatures::empty()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures",
 )}
 
 ${helpers.predefined_type(
     "font-variant-numeric",
     "FontVariantNumeric",
-    products="gecko",
+    engines="gecko",
     initial_value="computed::FontVariantNumeric::empty()",
     initial_specified_value="specified::FontVariantNumeric::empty()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric",
 )}
 
 ${helpers.single_keyword_system(
     "font-variant-position",
     "normal sub super",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mFont.variantPosition",
     gecko_constant_prefix="NS_FONT_VARIANT_POSITION",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-position",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "font-feature-settings",
     "FontFeatureSettings",
-    products="gecko",
+    engines="gecko",
     initial_value="computed::FontFeatureSettings::normal()",
     initial_specified_value="specified::FontFeatureSettings::normal()",
     extra_prefixes="moz:layout.css.prefixes.font-features",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings",
 )}
 
 ${helpers.predefined_type(
     "font-variation-settings",
     "FontVariationSettings",
-    products="gecko",
+    engines="gecko",
     gecko_pref="layout.css.font-variations.enabled",
     has_effect_on_gecko_scrollbars=False,
     initial_value="computed::FontVariationSettings::normal()",
     initial_specified_value="specified::FontVariationSettings::normal()",
     animation_value_type="ComputedValue",
     spec="https://drafts.csswg.org/css-fonts-4/#propdef-font-variation-settings"
 )}
 
 ${helpers.predefined_type(
     "font-language-override",
     "FontLanguageOverride",
-    products="gecko",
+    engines="gecko",
     initial_value="computed::FontLanguageOverride::zero()",
     initial_specified_value="specified::FontLanguageOverride::normal()",
     animation_value_type="discrete",
     extra_prefixes="moz:layout.css.prefixes.font-features",
     spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override",
 )}
 
 ${helpers.single_keyword_system(
     "font-optical-sizing",
     "auto none",
-    products="gecko",
+    engines="gecko",
     gecko_pref="layout.css.font-variations.enabled",
     has_effect_on_gecko_scrollbars=False,
     gecko_ffi_name="mFont.opticalSizing",
     gecko_constant_prefix="NS_FONT_OPTICAL_SIZING",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/css-fonts-4/#font-optical-sizing-def",
 )}
 
 ${helpers.predefined_type(
     "-x-lang",
     "XLang",
-    products="gecko",
+    engines="gecko",
     initial_value="computed::XLang::get_initial_value()",
     animation_value_type="none",
     enabled_in="",
     spec="Internal (not web-exposed)",
 )}
 
 ${helpers.predefined_type(
     "-moz-script-size-multiplier",
     "MozScriptSizeMultiplier",
-    products="gecko",
+    engines="gecko",
     initial_value="computed::MozScriptSizeMultiplier::get_initial_value()",
     animation_value_type="none",
     gecko_ffi_name="mScriptSizeMultiplier",
     enabled_in="",
     spec="Internal (not web-exposed)",
 )}
 
 ${helpers.predefined_type(
     "-moz-script-level",
     "MozScriptLevel",
     0,
+    engines="gecko",
     animation_value_type="none",
-    products="gecko",
     enabled_in="ua",
     gecko_ffi_name="mScriptLevel",
     spec="Internal (not web-exposed)",
 )}
 
 ${helpers.single_keyword(
     "-moz-math-display",
     "inline block",
+    engines="gecko",
     gecko_constant_prefix="NS_MATHML_DISPLAYSTYLE",
     gecko_ffi_name="mMathDisplay",
-    products="gecko",
     enabled_in="ua",
     spec="Internal (not web-exposed)",
     animation_value_type="none",
 )}
 
 ${helpers.single_keyword(
     "-moz-math-variant",
     """none normal bold italic bold-italic script bold-script
     fraktur double-struck bold-fraktur sans-serif
     bold-sans-serif sans-serif-italic sans-serif-bold-italic
     monospace initial tailed looped stretched""",
+    engines="gecko",
     gecko_constant_prefix="NS_MATHML_MATHVARIANT",
     gecko_ffi_name="mMathVariant",
-    products="gecko",
     spec="Internal (not web-exposed)",
     animation_value_type="none",
     enabled_in="",
     needs_conversion=True,
 )}
 
 ${helpers.predefined_type(
     "-moz-script-min-size",
     "MozScriptMinSize",
     "specified::MozScriptMinSize::get_initial_value()",
+    engines="gecko",
     animation_value_type="none",
-    products="gecko",
     enabled_in="",
     gecko_ffi_name="mScriptMinSize",
     spec="Internal (not web-exposed)",
 )}
 
 ${helpers.predefined_type(
     "-x-text-zoom",
     "XTextZoom",
     "computed::XTextZoom(true)",
+    engines="gecko",
     animation_value_type="none",
-    products="gecko",
     enabled_in="",
     spec="Internal (not web-exposed)",
 )}
 
-% if product == "gecko":
+% if engine == "gecko":
     pub mod system_font {
         //! We deal with system fonts here
         //!
         //! System fonts can only be set as a group via the font shorthand.
         //! They resolve at compute time (not parse time -- this lets the
         //! browser respond to changes to the OS font settings).
         //!
         //! While Gecko handles these as a separate property and keyword
@@ -465,54 +476,54 @@
             }
         }
     }
 % else:
     pub mod system_font {
         use cssparser::Parser;
 
         // We don't parse system fonts, but in the interest of not littering
-        // a lot of code with `if product == gecko` conditionals, we have a
+        // a lot of code with `if engine == "gecko"` conditionals, we have a
         // dummy system font module that does nothing
 
         #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
         /// void enum for system font, can never exist
         pub enum SystemFont {}
         impl SystemFont {
             pub fn parse(_: &mut Parser) -> Result<Self, ()> {
                 Err(())
             }
         }
     }
 % endif
 
 ${helpers.single_keyword(
     "-moz-osx-font-smoothing",
     "auto grayscale",
+    engines="gecko",
     gecko_constant_prefix="NS_FONT_SMOOTHING",
     gecko_ffi_name="mFont.smoothing",
     gecko_pref="layout.css.osx-font-smoothing.enabled",
     has_effect_on_gecko_scrollbars=False,
-    products="gecko",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth)",
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type(
     "-moz-font-smoothing-background-color",
     "color::MozFontSmoothingBackgroundColor",
     "computed::color::MozFontSmoothingBackgroundColor::transparent()",
+    engines="gecko",
     animation_value_type="none",
-    products="gecko",
     gecko_ffi_name="mFont.fontSmoothingBackgroundColor",
     enabled_in="chrome",
     spec="None (Nonstandard internal property)",
 )}
 
 ${helpers.predefined_type(
     "-moz-min-font-size-ratio",
     "Percentage",
     "computed::Percentage::hundred()",
+    engines="gecko",
     animation_value_type="none",
-    products="gecko",
     enabled_in="ua",
     spec="Nonstandard (Internal-only)",
 )}
--- a/servo/components/style/properties/longhands/inherited_box.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_box.mako.rs
@@ -5,77 +5,83 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("InheritedBox", inherited=True, gecko_name="Visibility") %>
 
 // TODO: collapse. Well, do tables first.
 ${helpers.single_keyword(
     "visibility",
     "visible hidden",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     extra_gecko_values="collapse",
     gecko_ffi_name="mVisible",
     animation_value_type="ComputedValue",
     spec="https://drafts.csswg.org/css-box/#propdef-visibility",
 )}
 
 // CSS Writing Modes Level 3
 // https://drafts.csswg.org/css-writing-modes-3
 ${helpers.single_keyword(
     "writing-mode",
     "horizontal-tb vertical-rl vertical-lr",
+    engines="gecko servo-2013 servo-2020",
     extra_gecko_values="sideways-rl sideways-lr",
-    extra_gecko_aliases="lr=horizontal-tb lr-tb=horizontal-tb \
+    gecko_aliases="lr=horizontal-tb lr-tb=horizontal-tb \
                          rl=horizontal-tb rl-tb=horizontal-tb \
                          tb=vertical-rl   tb-rl=vertical-rl",
-    servo_pref="layout.writing-mode.enabled",
+    servo_2013_pref="layout.writing-mode.enabled",
     animation_value_type="none",
     spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.single_keyword(
     "direction",
     "ltr rtl",
+    engines="gecko servo-2013 servo-2020",
     animation_value_type="none",
     spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction",
     needs_conversion=True,
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.single_keyword(
     "text-orientation",
     "mixed upright sideways",
-    extra_gecko_aliases="sideways-right=sideways",
-    products="gecko",
+    engines="gecko",
+    gecko_aliases="sideways-right=sideways",
     animation_value_type="none",
     spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation",
 )}
 
 // CSS Color Module Level 4
 // https://drafts.csswg.org/css-color/
 ${helpers.single_keyword(
     "color-adjust",
-    "economy exact", products="gecko",
+    "economy exact",
+    engines="gecko",
     gecko_enum_prefix="StyleColorAdjust",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-color/#propdef-color-adjust",
 )}
 
 // According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
 // And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
 ${helpers.single_keyword(
     "image-rendering",
     "auto crisp-edges",
+    engines="gecko servo-2013",
     extra_gecko_values="optimizespeed optimizequality",
-    extra_servo_values="pixelated",
-    extra_gecko_aliases="-moz-crisp-edges=crisp-edges",
+    extra_servo_2013_values="pixelated",
+    gecko_aliases="-moz-crisp-edges=crisp-edges",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-images/#propdef-image-rendering",
 )}
 
 ${helpers.single_keyword(
     "image-orientation",
     "none from-image",
-    products="gecko",
+    engines="gecko",
     gecko_enum_prefix="StyleImageOrientation",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-images/#propdef-image-orientation",
 )}
--- a/servo/components/style/properties/longhands/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_svg.mako.rs
@@ -9,202 +9,202 @@
 <% data.new_style_struct("InheritedSVG", inherited=True, gecko_name="SVG") %>
 
 // Section 10 - Text
 
 ${helpers.single_keyword(
     "dominant-baseline",
     """auto ideographic alphabetic hanging mathematical central middle
        text-after-edge text-before-edge""",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/css-inline-3/#propdef-dominant-baseline",
 )}
 
 ${helpers.single_keyword(
     "text-anchor",
     "start middle end",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty",
 )}
 
 // Section 11 - Painting: Filling, Stroking and Marker Symbols
 ${helpers.single_keyword(
     "color-interpolation",
     "srgb auto linearrgb",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty",
 )}
 
 ${helpers.single_keyword(
     "color-interpolation-filters",
     "linearrgb auto srgb",
-    products="gecko",
+    engines="gecko",
     gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty",
 )}
 
 ${helpers.predefined_type(
     "fill",
     "SVGPaint",
     "crate::values::computed::SVGPaint::black()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="IntermediateSVGPaint",
     boxed=True,
     spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint",
 )}
 
 ${helpers.predefined_type(
     "fill-opacity",
     "SVGOpacity",
     "Default::default()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     spec="https://svgwg.org/svg2-draft/painting.html#FillOpacity",
 )}
 
 ${helpers.predefined_type(
     "fill-rule",
     "FillRule",
     "Default::default()",
+    engines="gecko",
     needs_context=False,
-    products="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty",
 )}
 
 ${helpers.single_keyword(
     "shape-rendering",
     "auto optimizespeed crispedges geometricprecision",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty",
 )}
 
 ${helpers.predefined_type(
     "stroke",
     "SVGPaint",
     "Default::default()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="IntermediateSVGPaint",
     boxed=True,
     spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint",
 )}
 
 ${helpers.predefined_type(
     "stroke-width",
     "SVGWidth",
     "computed::SVGWidth::one()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="crate::values::computed::SVGWidth",
     spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth",
 )}
 
 ${helpers.single_keyword(
     "stroke-linecap",
     "butt round square",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty",
 )}
 
 ${helpers.single_keyword(
     "stroke-linejoin",
     "miter round bevel",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty",
 )}
 
 ${helpers.predefined_type(
     "stroke-miterlimit",
     "NonNegativeNumber",
     "From::from(4.0)",
-    products="gecko",
+    engines="gecko",
     animation_value_type="crate::values::computed::NonNegativeNumber",
     spec="https://www.w3.org/TR/SVG2/painting.html#StrokeMiterlimitProperty",
 )}
 
 ${helpers.predefined_type(
     "stroke-opacity",
     "SVGOpacity",
     "Default::default()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     spec="https://svgwg.org/svg2-draft/painting.html#StrokeOpacity",
 )}
 
 ${helpers.predefined_type(
     "stroke-dasharray",
     "SVGStrokeDashArray",
     "Default::default()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="crate::values::computed::SVGStrokeDashArray",
     spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
 )}
 
 ${helpers.predefined_type(
     "stroke-dashoffset",
     "SVGLength",
     "computed::SVGLength::zero()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
 )}
 
 // Section 14 - Clipping, Masking and Compositing
 ${helpers.predefined_type(
     "clip-rule",
     "FillRule",
     "Default::default()",
+    engines="gecko",
     needs_context=False,
-    products="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty",
 )}
 
 ${helpers.predefined_type(
     "marker-start",
     "url::UrlOrNone",
     "computed::url::UrlOrNone::none()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties",
 )}
 
 ${helpers.predefined_type(
     "marker-mid",
     "url::UrlOrNone",
     "computed::url::UrlOrNone::none()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties",
 )}
 
 ${helpers.predefined_type(
     "marker-end",
     "url::UrlOrNone",
     "computed::url::UrlOrNone::none()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties",
 )}
 
 ${helpers.predefined_type(
     "paint-order",
     "SVGPaintOrder",
     "computed::SVGPaintOrder::normal()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder",
 )}
 
 ${helpers.predefined_type(
     "-moz-context-properties",
     "MozContextProperties",
     "computed::MozContextProperties::default()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="none",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)",
 )}
--- a/servo/components/style/properties/longhands/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_table.mako.rs
@@ -4,42 +4,47 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("InheritedTable", inherited=True, gecko_name="TableBorder") %>
 
 ${helpers.single_keyword(
     "border-collapse",
     "separate collapse",
+    engines="gecko servo-2013",
     gecko_enum_prefix="StyleBorderCollapse",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-tables/#propdef-border-collapse",
     servo_restyle_damage = "reflow",
 )}
 
 ${helpers.single_keyword(
     "empty-cells",
     "show hide",
+    engines="gecko servo-2013",
     gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.single_keyword(
     "caption-side",
     "top bottom",
+    engines="gecko servo-2013",
     extra_gecko_values="right left top-outside bottom-outside",
     needs_conversion="True",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-tables/#propdef-caption-side",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "border-spacing",
     "BorderSpacing",
     "computed::BorderSpacing::zero()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     animation_value_type="BorderSpacing",
     boxed=True,
     spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing",
     servo_restyle_damage = "reflow",
 )}
--- a/servo/components/style/properties/longhands/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_text.mako.rs
@@ -5,110 +5,123 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import Keyword %>
 <% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
 
 ${helpers.predefined_type(
     "color",
     "ColorPropertyValue",
     "::cssparser::RGBA::new(0, 0, 0, 255)",
+    engines="gecko servo-2013 servo-2020",
     animation_value_type="AnimatedRGBA",
     ignored_when_colors_disabled="True",
     spec="https://drafts.csswg.org/css-color/#color",
 )}
 
 ${helpers.predefined_type(
     "line-height",
     "LineHeight",
     "computed::LineHeight::normal()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     animation_value_type="LineHeight",
     flags="GETCS_NEEDS_LAYOUT_FLUSH",
     spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
     servo_restyle_damage="reflow"
 )}
 
 // CSS Text Module Level 3
 
 ${helpers.predefined_type(
     "text-transform",
     "TextTransform",
     "computed::TextTransform::none()",
+    engines="gecko servo-2013",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text/#propdef-text-transform",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.single_keyword(
     "hyphens",
     "manual none auto",
+    engines="gecko",
     gecko_enum_prefix="StyleHyphens",
-    products="gecko",
     animation_value_type="discrete",
     extra_prefixes="moz",
     spec="https://drafts.csswg.org/css-text/#propdef-hyphens",
 )}
 
 // TODO: Support <percentage>
 ${helpers.single_keyword(
     "-moz-text-size-adjust",
     "auto none",
+    engines="gecko",
     gecko_constant_prefix="NS_STYLE_TEXT_SIZE_ADJUST",
     gecko_ffi_name="mTextSizeAdjust",
-    products="gecko", animation_value_type="discrete",
+    animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control",
     alias="-webkit-text-size-adjust",
 )}
 
 ${helpers.predefined_type(
     "text-indent",
     "LengthPercentage",
     "computed::LengthPercentage::zero()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     animation_value_type="ComputedValue",
     spec="https://drafts.csswg.org/css-text/#propdef-text-indent",
     allow_quirks="Yes",
     servo_restyle_damage = "reflow",
 )}
 
 // Also known as "word-wrap" (which is more popular because of IE), but this is
 // the preferred name per CSS-TEXT 6.2.
 ${helpers.predefined_type(
     "overflow-wrap",
     "OverflowWrap",
     "computed::OverflowWrap::Normal",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
     alias="word-wrap",
     needs_context=False,
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "word-break",
     "WordBreak",
     "computed::WordBreak::Normal",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text/#propdef-word-break",
     needs_context=False,
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 // TODO(pcwalton): Support `text-justify: distribute`.
 <%helpers:single_keyword
     name="text-justify"
     values="auto none inter-word"
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     extra_gecko_values="inter-character"
-    extra_specified="${'distribute' if product == 'gecko' else ''}"
+    extra_specified="${'distribute' if engine == 'gecko' else ''}"
     gecko_enum_prefix="StyleTextJustify"
     animation_value_type="discrete"
     gecko_pref="layout.css.text-justify.enabled"
     has_effect_on_gecko_scrollbars="False"
     spec="https://drafts.csswg.org/css-text/#propdef-text-justify"
     servo_restyle_damage="rebuild_and_reflow"
 >
-    % if product == 'gecko':
+    % if engine == 'gecko':
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, _: &Context) -> computed_value::T {
             match *self {
                 % for value in "Auto None InterCharacter InterWord".split():
                 SpecifiedValue::${value} => computed_value::T::${value},
@@ -128,61 +141,67 @@
         }
     }
     % endif
 </%helpers:single_keyword>
 
 ${helpers.single_keyword(
     "text-align-last",
     "auto start end left right center justify",
-    products="gecko",
+    engines="gecko",
     gecko_constant_prefix="NS_STYLE_TEXT_ALIGN",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
 )}
 
 // TODO make this a shorthand and implement text-align-last/text-align-all
 ${helpers.predefined_type(
     "text-align",
     "TextAlign",
     "computed::TextAlign::Start",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text/#propdef-text-align",
     servo_restyle_damage = "reflow",
 )}
 
 ${helpers.predefined_type(
     "letter-spacing",
     "LetterSpacing",
     "computed::LetterSpacing::normal()",
+    engines="gecko servo-2013",
     animation_value_type="ComputedValue",
     spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "word-spacing",
     "WordSpacing",
     "computed::WordSpacing::zero()",
+    engines="gecko servo-2013",
     animation_value_type="ComputedValue",
     spec="https://drafts.csswg.org/css-text/#propdef-word-spacing",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 <%helpers:single_keyword
     name="white-space"
     values="normal pre nowrap pre-wrap pre-line"
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     extra_gecko_values="break-spaces -moz-pre-space"
     gecko_enum_prefix="StyleWhiteSpace"
     needs_conversion="True"
     animation_value_type="discrete"
     spec="https://drafts.csswg.org/css-text/#propdef-white-space"
     servo_restyle_damage="rebuild_and_reflow"
 >
-    % if product != "gecko":
+    % if engine == "servo-2013":
     impl SpecifiedValue {
         pub fn allow_wrap(&self) -> bool {
             match *self {
                 SpecifiedValue::Nowrap |
                 SpecifiedValue::Pre => false,
                 SpecifiedValue::Normal |
                 SpecifiedValue::PreWrap |
                 SpecifiedValue::PreLine => true,
@@ -211,176 +230,178 @@
     }
     % endif
 </%helpers:single_keyword>
 
 ${helpers.predefined_type(
     "text-shadow",
     "SimpleShadow",
     None,
+    engines="gecko servo-2013",
     vector=True,
     vector_animation_type="with_zero",
     animation_value_type="AnimatedTextShadowList",
     ignored_when_colors_disabled=True,
     simple_vector_bindings=True,
     spec="https://drafts.csswg.org/css-text-decor-3/#text-shadow-property",
 )}
 
 ${helpers.predefined_type(
     "text-emphasis-style",
     "TextEmphasisStyle",
     None,
+    engines="gecko",
     initial_specified_value="SpecifiedValue::None",
-    products="gecko",
     boxed=True,
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style",
 )}
 
 ${helpers.predefined_type(
     "text-emphasis-position",
     "TextEmphasisPosition",
     "computed::TextEmphasisPosition::over_right()",
+    engines="gecko",
     initial_specified_value="specified::TextEmphasisPosition::over_right()",
-    products="gecko",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position",
 )}
 
 ${helpers.predefined_type(
     "text-emphasis-color",
     "Color",
     "computed_value::T::currentcolor()",
+    engines="gecko",
     initial_specified_value="specified::Color::currentcolor()",
-    products="gecko",
     animation_value_type="AnimatedColor",
     ignored_when_colors_disabled=True,
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color",
 )}
 
 ${helpers.predefined_type(
     "-moz-tab-size",
     "NonNegativeLengthOrNumber",
     "generics::length::LengthOrNumber::Number(From::from(8.0))",
-    products="gecko",
+    engines="gecko",
     animation_value_type="LengthOrNumber",
     spec="https://drafts.csswg.org/css-text-3/#tab-size-property",
 )}
 
 ${helpers.predefined_type(
     "line-break",
     "LineBreak",
     "computed::LineBreak::Auto",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text-3/#line-break-property",
     needs_context=False,
 )}
 
 // CSS Compatibility
 // https://compat.spec.whatwg.org
 ${helpers.predefined_type(
     "-webkit-text-fill-color",
     "Color",
     "computed_value::T::currentcolor()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="AnimatedColor",
     ignored_when_colors_disabled=True,
     spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color",
 )}
 
 ${helpers.predefined_type(
     "-webkit-text-stroke-color",
     "Color",
     "computed_value::T::currentcolor()",
     initial_specified_value="specified::Color::currentcolor()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="AnimatedColor",
     ignored_when_colors_disabled=True,
     spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color",
 )}
 
 ${helpers.predefined_type(
     "-webkit-text-stroke-width",
     "BorderSideWidth",
     "crate::values::computed::NonNegativeLength::new(0.)",
+    engines="gecko",
     initial_specified_value="specified::BorderSideWidth::zero()",
     computed_type="crate::values::computed::NonNegativeLength",
-    products="gecko",
     spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width",
     animation_value_type="discrete",
 )}
 
 // CSS Ruby Layout Module Level 1
 // https://drafts.csswg.org/css-ruby/
 ${helpers.single_keyword(
     "ruby-align",
     "space-around start center space-between",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-ruby/#ruby-align-property",
 )}
 
 ${helpers.single_keyword(
     "ruby-position",
     "over under",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-ruby/#ruby-position-property",
 )}
 
 // CSS Writing Modes Module Level 3
 // https://drafts.csswg.org/css-writing-modes-3/
 
 ${helpers.single_keyword(
     "text-combine-upright",
     "none all",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright",
 )}
 
 // SVG 1.1: Section 11 - Painting: Filling, Stroking and Marker Symbols
 ${helpers.single_keyword(
     "text-rendering",
     "auto optimizespeed optimizelegibility geometricprecision",
+    engines="gecko servo-2013",
     gecko_enum_prefix="StyleTextRendering",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 // FIXME Firefox expects the initial value of this property to change depending
 // on the value of the layout.css.control-characters.visible pref.
 ${helpers.single_keyword(
     "-moz-control-character-visibility",
     "hidden visible",
+    engines="gecko",
     gecko_constant_prefix="NS_STYLE_CONTROL_CHARACTER_VISIBILITY",
     animation_value_type="none",
     gecko_ffi_name="mControlCharacterVisibility",
-    products="gecko",
     spec="Nonstandard",
 )}
 
 // text underline offset
 ${helpers.predefined_type(
     "text-underline-offset",
     "LengthOrAuto",
     "computed::LengthOrAuto::auto()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     gecko_pref="layout.css.text-underline-offset.enabled",
     has_effect_on_gecko_scrollbars=False,
     spec="https://drafts.csswg.org/css-text-decor-4/#underline-offset",
 )}
 
 // text decoration skip ink
 ${helpers.predefined_type(
     "text-decoration-skip-ink",
     "TextDecorationSkipInk",
     "computed::TextDecorationSkipInk::Auto",
-    products="gecko",
+    engines="gecko",
     needs_context=False,
     animation_value_type="discrete",
     gecko_pref="layout.css.text-decoration-skip-ink.enabled",
     has_effect_on_gecko_scrollbars=False,
     spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property",
 )}
--- a/servo/components/style/properties/longhands/inherited_ui.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_ui.mako.rs
@@ -5,81 +5,84 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("InheritedUI", inherited=True, gecko_name="UI") %>
 
 ${helpers.predefined_type(
     "cursor",
     "Cursor",
     "computed::Cursor::auto()",
+    engines="gecko servo-2013",
     initial_specified_value="specified::Cursor::auto()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-ui/#cursor",
 )}
 
 // NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact)
 // is nonstandard, slated for CSS4-UI.
 // TODO(pcwalton): SVG-only values.
 ${helpers.single_keyword(
     "pointer-events",
     "auto none",
+    engines="gecko servo-2013",
     animation_value_type="discrete",
     extra_gecko_values="visiblepainted visiblefill visiblestroke visible painted fill stroke all",
     spec="https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty",
 )}
 
 ${helpers.single_keyword(
     "-moz-user-input",
     "auto none",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mUserInput",
     gecko_enum_prefix="StyleUserInput",
     animation_value_type="discrete",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-input)",
 )}
 
 ${helpers.single_keyword(
     "-moz-user-modify",
     "read-only read-write write-only",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mUserModify",
     gecko_enum_prefix="StyleUserModify",
     needs_conversion=True,
     animation_value_type="discrete",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-modify)",
 )}
 
 ${helpers.single_keyword(
     "-moz-user-focus",
     "none ignore normal select-after select-before select-menu select-same select-all",
-    products="gecko", gecko_ffi_name="mUserFocus",
+    engines="gecko",
+    gecko_ffi_name="mUserFocus",
     gecko_enum_prefix="StyleUserFocus",
     animation_value_type="discrete",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)",
 )}
 
 ${helpers.predefined_type(
     "caret-color",
     "ColorOrAuto",
     "generics::color::ColorOrAuto::Auto",
+    engines="gecko",
     spec="https://drafts.csswg.org/css-ui/#caret-color",
     animation_value_type="AnimatedCaretColor",
     boxed=True,
     ignored_when_colors_disabled=True,
-    products="gecko",
 )}
 
 ${helpers.predefined_type(
     "scrollbar-color",
     "ui::ScrollbarColor",
     "Default::default()",
+    engines="gecko",
     spec="https://drafts.csswg.org/css-scrollbars-1/#scrollbar-color",
     gecko_pref="layout.css.scrollbar-color.enabled",
     # Surprisingly, yes the computed value of scrollbar-color has no effect on
     # Gecko scrollbar elements, since the value only matters on the scrollable
     # element itself.
     has_effect_on_gecko_scrollbars=False,
     animation_value_type="ScrollbarColor",
     boxed=True,
     ignored_when_colors_disabled=True,
     enabled_in="chrome",
-    products="gecko",
 )}
--- a/servo/components/style/properties/longhands/list.mako.rs
+++ b/servo/components/style/properties/longhands/list.mako.rs
@@ -1,82 +1,94 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("List", inherited=True) %>
 
-${helpers.single_keyword("list-style-position", "outside inside", animation_value_type="discrete",
-                         spec="https://drafts.csswg.org/css-lists/#propdef-list-style-position",
-                         servo_restyle_damage="rebuild_and_reflow")}
+${helpers.single_keyword(
+    "list-style-position",
+    "outside inside",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
+    animation_value_type="discrete",
+    spec="https://drafts.csswg.org/css-lists/#propdef-list-style-position",
+    servo_restyle_damage="rebuild_and_reflow",
+)}
 
 // TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1:
 //
 //     decimal-leading-zero, armenian, upper-armenian, lower-armenian, georgian, lower-roman,
 //     upper-roman
 //
 // [1]: http://dev.w3.org/csswg/css-counter-styles/
-% if product == "servo":
+% if engine in ["servo-2013", "servo-2020"]:
     ${helpers.single_keyword(
         "list-style-type",
         """disc none circle square decimal disclosure-open disclosure-closed lower-alpha upper-alpha
         arabic-indic bengali cambodian cjk-decimal devanagari gujarati gurmukhi kannada khmer lao
         malayalam mongolian myanmar oriya persian telugu thai tibetan cjk-earthly-branch
         cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana katakana-iroha""",
+        engines="servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
         animation_value_type="discrete",
         spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type",
         servo_restyle_damage="rebuild_and_reflow",
     )}
-% else:
+% endif
+% if engine == "gecko":
     ${helpers.predefined_type(
         "list-style-type",
         "ListStyleType",
         "computed::ListStyleType::disc()",
+        engines="gecko",
         initial_specified_value="specified::ListStyleType::disc()",
         animation_value_type="discrete",
         boxed=True,
         spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type",
         servo_restyle_damage="rebuild_and_reflow",
     )}
 % endif
 
 ${helpers.predefined_type(
     "list-style-image",
     "url::ImageUrlOrNone",
+    engines="gecko servo-2013",
     initial_value="computed::url::ImageUrlOrNone::none()",
     initial_specified_value="specified::url::ImageUrlOrNone::none()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "quotes",
     "Quotes",
     "computed::Quotes::get_initial_value()",
+    engines="gecko servo-2013",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-content/#propdef-quotes",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "-moz-image-region",
     "ClipRectOrAuto",
     "computed::ClipRectOrAuto::auto()",
+    engines="gecko",
     animation_value_type="ComputedValue",
-    products="gecko",
     boxed=True,
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-image-region)",
 )}
 
 ${helpers.predefined_type(
     "-moz-list-reversed",
     "MozListReversed",
     "computed::MozListReversed::False",
+    engines="gecko",
     animation_value_type="discrete",
-    products="gecko",
     enabled_in="ua",
     needs_context=False,
     spec="Internal implementation detail for <ol reversed>",
     servo_restyle_damage="rebuild_and_reflow",
 )}
--- a/servo/components/style/properties/longhands/margin.mako.rs
+++ b/servo/components/style/properties/longhands/margin.mako.rs
@@ -11,33 +11,35 @@
         spec = "https://drafts.csswg.org/css-box/#propdef-margin-%s" % side[0]
         if side[1]:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-margin-%s" % side[1]
     %>
     ${helpers.predefined_type(
         "margin-%s" % side[0],
         "LengthPercentageOrAuto",
         "computed::LengthPercentageOrAuto::zero()",
-        alias=maybe_moz_logical_alias(product, side, "-moz-margin-%s"),
+        engines="gecko servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
+        alias=maybe_moz_logical_alias(engine, side, "-moz-margin-%s"),
         allow_quirks="No" if side[1] else "Yes",
         animation_value_type="ComputedValue",
         logical=side[1],
         logical_group="margin",
         spec=spec,
         flags="GETCS_NEEDS_LAYOUT_FLUSH",
         allowed_in_page_rule=True,
         servo_restyle_damage="reflow"
     )}
 % endfor
 
 % for side in ALL_SIDES:
     ${helpers.predefined_type(
         "scroll-margin-%s" % side[0],
         "Length",
         "computed::Length::zero()",
-        products="gecko",
+        engines="gecko",
         gecko_pref="layout.css.scroll-snap-v1.enabled",
         logical=side[1],
         logical_group="scroll-margin",
         spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-%s" % side[0],
         animation_value_type="ComputedValue",
     )}
 % endfor
--- a/servo/components/style/properties/longhands/outline.mako.rs
+++ b/servo/components/style/properties/longhands/outline.mako.rs
@@ -9,54 +9,59 @@
                          inherited=False,
                          additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
 
 // TODO(pcwalton): `invert`
 ${helpers.predefined_type(
     "outline-color",
     "Color",
     "computed_value::T::currentcolor()",
+    engines="gecko servo-2013",
     initial_specified_value="specified::Color::currentcolor()",
     animation_value_type="AnimatedColor",
     ignored_when_colors_disabled=True,
     spec="https://drafts.csswg.org/css-ui/#propdef-outline-color",
 )}
 
 ${helpers.predefined_type(
     "outline-style",
     "OutlineStyle",
     "computed::OutlineStyle::none()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::OutlineStyle::none()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-ui/#propdef-outline-style",
 )}
 
 ${helpers.predefined_type(
     "outline-width",
     "BorderSideWidth",
     "crate::values::computed::NonNegativeLength::new(3.)",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::BorderSideWidth::Medium",
     computed_type="crate::values::computed::NonNegativeLength",
     animation_value_type="NonNegativeLength",
     spec="https://drafts.csswg.org/css-ui/#propdef-outline-width",
 )}
 
 // The -moz-outline-radius-* properties are non-standard and not on a standards track.
 % for corner in ["topleft", "topright", "bottomright", "bottomleft"]:
     ${helpers.predefined_type(
         "-moz-outline-radius-" + corner,
         "BorderCornerRadius",
         "computed::BorderCornerRadius::zero()",
-        products="gecko",
+        engines="gecko",
         boxed=True,
         animation_value_type="BorderCornerRadius",
         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)",
     )}
 % endfor
 
 ${helpers.predefined_type(
     "outline-offset",
     "Length",
     "crate::values::computed::Length::new(0.)",
-    products="servo gecko",
+    engines="gecko servo-2013",
     animation_value_type="ComputedValue",
     spec="https://drafts.csswg.org/css-ui/#propdef-outline-offset",
 )}
--- a/servo/components/style/properties/longhands/padding.mako.rs
+++ b/servo/components/style/properties/longhands/padding.mako.rs
@@ -11,32 +11,34 @@
         spec = "https://drafts.csswg.org/css-box/#propdef-padding-%s" % side[0]
         if side[1]:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-padding-%s" % side[1]
     %>
     ${helpers.predefined_type(
         "padding-%s" % side[0],
         "NonNegativeLengthPercentage",
         "computed::NonNegativeLengthPercentage::zero()",
-        alias=maybe_moz_logical_alias(product, side, "-moz-padding-%s"),
+        engines="gecko servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
+        alias=maybe_moz_logical_alias(engine, side, "-moz-padding-%s"),
         animation_value_type="NonNegativeLengthPercentage",
         logical=side[1],
         logical_group="padding",
         spec=spec,
         flags="GETCS_NEEDS_LAYOUT_FLUSH",
         allow_quirks="No" if side[1] else "Yes",
         servo_restyle_damage="reflow rebuild_and_reflow_inline"
     )}
 % endfor
 
 % for side in ALL_SIDES:
     ${helpers.predefined_type(
         "scroll-padding-%s" % side[0],
         "NonNegativeLengthPercentageOrAuto",
         "computed::NonNegativeLengthPercentageOrAuto::auto()",
-        products="gecko",
+        engines="gecko",
         gecko_pref="layout.css.scroll-snap-v1.enabled",
         logical=side[1],
         logical_group="scroll-padding",
         spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-%s" % side[0],
         animation_value_type="NonNegativeLengthPercentageOrAuto",
     )}
 % endfor
--- a/servo/components/style/properties/longhands/position.mako.rs
+++ b/servo/components/style/properties/longhands/position.mako.rs
@@ -9,30 +9,34 @@
 <% data.new_style_struct("Position", inherited=False) %>
 
 // "top" / "left" / "bottom" / "right"
 % for side in PHYSICAL_SIDES:
     ${helpers.predefined_type(
         side,
         "LengthPercentageOrAuto",
         "computed::LengthPercentageOrAuto::auto()",
+        engines="gecko servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
         spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
         flags="GETCS_NEEDS_LAYOUT_FLUSH",
         animation_value_type="ComputedValue",
         allow_quirks="Yes",
         servo_restyle_damage="reflow_out_of_flow",
         logical_group="inset",
     )}
 % endfor
 // inset-* logical properties, map to "top" / "left" / "bottom" / "right"
 % for side in LOGICAL_SIDES:
     ${helpers.predefined_type(
         "inset-%s" % side,
         "LengthPercentageOrAuto",
         "computed::LengthPercentageOrAuto::auto()",
+        engines="gecko servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
         spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side,
         flags="GETCS_NEEDS_LAYOUT_FLUSH",
         alias="offset-%s:layout.css.offset-logical-properties.enabled" % side,
         animation_value_type="ComputedValue",
         logical=True,
         logical_group="inset",
     )}
 % endfor
@@ -54,190 +58,213 @@ macro_rules! impl_align_conversions {
         }
     };
 }
 
 ${helpers.predefined_type(
     "z-index",
     "ZIndex",
     "computed::ZIndex::auto()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     spec="https://www.w3.org/TR/CSS2/visuren.html#z-index",
     flags="CREATES_STACKING_CONTEXT",
     animation_value_type="ComputedValue",
 )}
 
 // CSS Flexible Box Layout Module Level 1
 // http://www.w3.org/TR/css3-flexbox/
 
 // Flex container properties
 ${helpers.single_keyword(
     "flex-direction",
     "row row-reverse column column-reverse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
     extra_prefixes="webkit",
     animation_value_type="discrete",
     servo_restyle_damage = "reflow",
     gecko_enum_prefix = "StyleFlexDirection",
 )}
 
 ${helpers.single_keyword(
     "flex-wrap",
     "nowrap wrap wrap-reverse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property",
     extra_prefixes="webkit",
     animation_value_type="discrete",
     servo_restyle_damage = "reflow",
 )}
 
-% if product == "servo":
+% if engine == "servo-2013":
     // FIXME: Update Servo to support the same Syntax as Gecko.
     ${helpers.single_keyword(
         "justify-content",
         "flex-start stretch flex-end center space-between space-around",
+        engines="servo-2013",
         extra_prefixes="webkit",
         spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
         animation_value_type="discrete",
         servo_restyle_damage = "reflow",
     )}
-% else:
+% endif
+% if engine == "gecko":
     ${helpers.predefined_type(
         "justify-content",
         "JustifyContent",
         "specified::JustifyContent(specified::ContentDistribution::normal())",
+        engines="gecko",
         spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
         extra_prefixes="webkit",
         animation_value_type="discrete",
         servo_restyle_damage="reflow",
     )}
 % endif
 
-% if product == "servo":
+% if engine in ["servo-2013", "servo-2020"]:
     // FIXME: Update Servo to support the same Syntax as Gecko.
     ${helpers.single_keyword(
         "align-content",
         "stretch flex-start flex-end center space-between space-around",
+        engines="servo-2013",
         extra_prefixes="webkit",
         spec="https://drafts.csswg.org/css-align/#propdef-align-content",
         animation_value_type="discrete",
         servo_restyle_damage="reflow",
     )}
 
     ${helpers.single_keyword(
         "align-items",
         "stretch flex-start flex-end center baseline",
+        engines="servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
         extra_prefixes="webkit",
         spec="https://drafts.csswg.org/css-flexbox/#align-items-property",
         animation_value_type="discrete",
         servo_restyle_damage="reflow",
     )}
-% else:
+% endif
+% if engine == "gecko":
     ${helpers.predefined_type(
         "align-content",
         "AlignContent",
         "specified::AlignContent(specified::ContentDistribution::normal())",
+        engines="gecko",
         spec="https://drafts.csswg.org/css-align/#propdef-align-content",
         extra_prefixes="webkit",
         animation_value_type="discrete",
         servo_restyle_damage="reflow",
     )}
 
     ${helpers.predefined_type(
         "align-items",
         "AlignItems",
         "specified::AlignItems::normal()",
+        engines="gecko",
         spec="https://drafts.csswg.org/css-align/#propdef-align-items",
         extra_prefixes="webkit",
         animation_value_type="discrete",
         servo_restyle_damage="reflow",
     )}
 
     #[cfg(feature = "gecko")]
     impl_align_conversions!(crate::values::specified::align::AlignItems);
 
     ${helpers.predefined_type(
         "justify-items",
         "JustifyItems",
         "computed::JustifyItems::legacy()",
+        engines="gecko",
         spec="https://drafts.csswg.org/css-align/#propdef-justify-items",
         animation_value_type="discrete",
     )}
 
     #[cfg(feature = "gecko")]
     impl_align_conversions!(crate::values::specified::align::JustifyItems);
 % endif
 
 // Flex item properties
 ${helpers.predefined_type(
     "flex-grow",
     "NonNegativeNumber",
     "From::from(0.0)",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property",
     extra_prefixes="webkit",
     animation_value_type="NonNegativeNumber",
     servo_restyle_damage="reflow",
 )}
 
 ${helpers.predefined_type(
     "flex-shrink",
     "NonNegativeNumber",
     "From::from(1.0)",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property",
     extra_prefixes="webkit",
     animation_value_type="NonNegativeNumber",
     servo_restyle_damage = "reflow",
 )}
 
 // https://drafts.csswg.org/css-align/#align-self-property
-% if product == "servo":
+% if engine in ["servo-2013", "servo-2020"]:
     // FIXME: Update Servo to support the same syntax as Gecko.
     ${helpers.single_keyword(
         "align-self",
         "auto stretch flex-start flex-end center baseline",
+        engines="servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
         extra_prefixes="webkit",
         spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self",
         animation_value_type="discrete",
         servo_restyle_damage = "reflow",
     )}
-% else:
+% endif
+% if engine == "gecko":
     ${helpers.predefined_type(
         "align-self",
         "AlignSelf",
         "specified::AlignSelf(specified::SelfAlignment::auto())",
+        engines="gecko",
         spec="https://drafts.csswg.org/css-align/#align-self-property",
         extra_prefixes="webkit",
         animation_value_type="discrete",
     )}
 
     ${helpers.predefined_type(
         "justify-self",
         "JustifySelf",
         "specified::JustifySelf(specified::SelfAlignment::auto())",
+        engines="gecko",
         spec="https://drafts.csswg.org/css-align/#justify-self-property",
         animation_value_type="discrete",
     )}
 
     #[cfg(feature = "gecko")]
     impl_align_conversions!(crate::values::specified::align::SelfAlignment);
 % endif
 
 // https://drafts.csswg.org/css-flexbox/#propdef-order
 ${helpers.predefined_type(
     "order",
     "Integer",
     "0",
+    engines="gecko servo-2013",
     extra_prefixes="webkit",
     animation_value_type="ComputedValue",
     spec="https://drafts.csswg.org/css-flexbox/#order-property",
     servo_restyle_damage="reflow",
 )}
 
 ${helpers.predefined_type(
     "flex-basis",
     "FlexBasis",
     "computed::FlexBasis::auto()",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
     extra_prefixes="webkit",
     animation_value_type="FlexBasis",
     servo_restyle_damage="reflow",
     boxed=True,
 )}
 
 % for (size, logical) in ALL_SIZES:
@@ -246,162 +273,172 @@ macro_rules! impl_align_conversions {
         if logical:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
     %>
     // width, height, block-size, inline-size
     ${helpers.predefined_type(
         size,
         "Size",
         "computed::Size::auto()",
+        engines="gecko servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
         logical=logical,
         logical_group="size",
         allow_quirks="No" if logical else "Yes",
         spec=spec % size,
         animation_value_type="Size",
         flags="GETCS_NEEDS_LAYOUT_FLUSH",
         servo_restyle_damage="reflow",
     )}
     // min-width, min-height, min-block-size, min-inline-size
     ${helpers.predefined_type(
         "min-%s" % size,
         "Size",
         "computed::Size::auto()",
+        engines="gecko servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
         logical=logical,
         logical_group="min-size",
         allow_quirks="No" if logical else "Yes",
         spec=spec % size,
         animation_value_type="Size",
         servo_restyle_damage="reflow",
     )}
     ${helpers.predefined_type(
         "max-%s" % size,
         "MaxSize",
         "computed::MaxSize::none()",
+        engines="gecko servo-2013 servo-2020",
+        servo_2020_pref="layout.2020.unimplemented",
         logical=logical,
         logical_group="max-size",
         allow_quirks="No" if logical else "Yes",
         spec=spec % size,
         animation_value_type="MaxSize",
         servo_restyle_damage="reflow",
     )}
 % endfor
 
 ${helpers.single_keyword(
     "box-sizing",
     "content-box border-box",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     extra_prefixes="moz:layout.css.prefixes.box-sizing webkit",
     spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing",
     gecko_enum_prefix="StyleBoxSizing",
     custom_consts={ "content-box": "Content", "border-box": "Border" },
     animation_value_type="discrete",
     servo_restyle_damage = "reflow",
 )}
 
 ${helpers.single_keyword(
     "object-fit",
     "fill contain cover none scale-down",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-images/#propdef-object-fit",
 )}
 
 ${helpers.predefined_type(
     "object-position",
     "Position",
     "computed::Position::zero()",
-    products="gecko",
+    engines="gecko",
     boxed=True,
     spec="https://drafts.csswg.org/css-images-3/#the-object-position",
     animation_value_type="ComputedValue",
 )}
 
 % for kind in ["row", "column"]:
     % for range in ["start", "end"]:
         ${helpers.predefined_type(
             "grid-%s-%s" % (kind, range),
             "GridLine",
             "Default::default()",
+            engines="gecko",
             animation_value_type="discrete",
             spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range),
-            products="gecko",
         )}
     % endfor
 
     // NOTE: According to the spec, this should handle multiple values of `<track-size>`,
     // but gecko supports only a single value
     ${helpers.predefined_type(
         "grid-auto-%ss" % kind,
         "TrackSize",
         "Default::default()",
+        engines="gecko",
         animation_value_type="discrete",
         spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
-        products="gecko",
         boxed=True,
     )}
 
     ${helpers.predefined_type(
         "grid-template-%ss" % kind,
         "GridTemplateComponent",
         "specified::GenericGridTemplateComponent::None",
-        products="gecko",
+        engines="gecko",
         spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-%ss" % kind,
         boxed=True,
         flags="GETCS_NEEDS_LAYOUT_FLUSH",
         animation_value_type="ComputedValue",
     )}
 
 % endfor
 
 ${helpers.predefined_type(
     "grid-auto-flow",
     "GridAutoFlow",
     "computed::GridAutoFlow::row()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow",
 )}
 
 ${helpers.predefined_type(
     "grid-template-areas",
     "GridTemplateAreas",
     "computed::GridTemplateAreas::none()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-areas",
 )}
 
 ${helpers.predefined_type(
     "column-gap",
     "length::NonNegativeLengthPercentageOrNormal",
     "computed::length::NonNegativeLengthPercentageOrNormal::normal()",
-    alias="grid-column-gap" if product == "gecko" else "",
+    engines="gecko servo-2013",
+    alias="grid-column-gap" if engine == "gecko" else "",
     extra_prefixes="moz",
-    servo_pref="layout.columns.enabled",
+    servo_2013_pref="layout.columns.enabled",
     spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
     animation_value_type="NonNegativeLengthPercentageOrNormal",
     servo_restyle_damage="reflow",
 )}
 
 // no need for -moz- prefixed alias for this property
 ${helpers.predefined_type(
     "row-gap",
     "length::NonNegativeLengthPercentageOrNormal",
     "computed::length::NonNegativeLengthPercentageOrNormal::normal()",
+    engines="gecko",
     alias="grid-row-gap",
-    products="gecko",
     spec="https://drafts.csswg.org/css-align-3/#propdef-row-gap",
     animation_value_type="NonNegativeLengthPercentageOrNormal",
     servo_restyle_damage="reflow",
 )}
 
 // NOTE(emilio): Before exposing this property to content, we probably need to
 // change syntax and such, and make it apply to more elements.
 //
 // For now, it's used only for mapped attributes.
 ${helpers.predefined_type(
     "aspect-ratio",
     "Number",
     "computed::Number::zero()",
+    engines="gecko servo-2013",
     animation_value_type="ComputedValue",
     spec="Internal, for now",
     enabled_in="",
     servo_restyle_damage="reflow",
 )}
--- a/servo/components/style/properties/longhands/svg.mako.rs
+++ b/servo/components/style/properties/longhands/svg.mako.rs
@@ -4,244 +4,244 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>
 
 ${helpers.single_keyword(
     "vector-effect",
     "none non-scaling-stroke",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty",
 )}
 
 // Section 13 - Gradients and Patterns
 
 ${helpers.predefined_type(
     "stop-color",
     "Color",
     "RGBA::new(0, 0, 0, 255).into()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="AnimatedRGBA",
     spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
 )}
 
 ${helpers.predefined_type(
     "stop-opacity",
     "Opacity",
     "1.0",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     spec="https://svgwg.org/svg2-draft/pservers.html#StopOpacityProperty",
 )}
 
 // Section 15 - Filter Effects
 
 ${helpers.predefined_type(
     "flood-color",
     "Color",
     "RGBA::new(0, 0, 0, 255).into()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="AnimatedColor",
     spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
 )}
 
 ${helpers.predefined_type(
     "flood-opacity",
     "Opacity",
     "1.0",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     spec="https://drafts.fxtf.org/filter-effects/#FloodOpacityProperty",
 )}
 
 ${helpers.predefined_type(
     "lighting-color",
     "Color",
     "RGBA::new(255, 255, 255, 255).into()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="AnimatedColor",
     spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
 )}
 
 // CSS Masking Module Level 1
 // https://drafts.fxtf.org/css-masking
 ${helpers.single_keyword(
     "mask-type",
     "luminance alpha",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type",
 )}
 
 ${helpers.predefined_type(
     "clip-path",
     "basic_shape::ClippingShape",
     "generics::basic_shape::ShapeSource::None",
-    products="gecko",
+    engines="gecko",
     animation_value_type="basic_shape::ClippingShape",
     flags="CREATES_STACKING_CONTEXT",
     spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
 )}
 
 ${helpers.single_keyword(
     "mask-mode",
     "match-source alpha luminance",
+    engines="gecko",
     gecko_enum_prefix="StyleMaskMode",
     vector=True,
-    products="gecko",
     animation_value_type="discrete",
     spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode",
 )}
 
 ${helpers.predefined_type(
     "mask-repeat",
     "BackgroundRepeat",
     "computed::BackgroundRepeat::repeat()",
+    engines="gecko",
     initial_specified_value="specified::BackgroundRepeat::repeat()",
-    products="gecko",
     extra_prefixes="webkit",
     animation_value_type="discrete",
     spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat",
     vector=True,
 )}
 
 % for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
     ${helpers.predefined_type(
         "mask-position-" + axis,
         "position::" + direction + "Position",
         "computed::LengthPercentage::zero()",
-        products="gecko",
+        engines="gecko",
         extra_prefixes="webkit",
         initial_specified_value="specified::PositionComponent::Center",
         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
         animation_value_type="ComputedValue",
         vector_animation_type="repeatable_list",
         vector=True,
     )}
 % endfor
 
 ${helpers.single_keyword(
     "mask-clip",
     "border-box content-box padding-box",
+    engines="gecko",
     extra_gecko_values="fill-box stroke-box view-box no-clip",
     vector=True,
-    products="gecko",
     extra_prefixes="webkit",
     gecko_enum_prefix="StyleGeometryBox",
     gecko_inexhaustive=True,
     animation_value_type="discrete",
     spec="https://drafts.fxtf.org/css-masking/#propdef-mask-clip",
 )}
 
 ${helpers.single_keyword(
     "mask-origin",
     "border-box content-box padding-box",
+    engines="gecko",
     extra_gecko_values="fill-box stroke-box view-box",
     vector=True,
-    products="gecko",
     extra_prefixes="webkit",
     gecko_enum_prefix="StyleGeometryBox",
     gecko_inexhaustive=True,
     animation_value_type="discrete",
     spec="https://drafts.fxtf.org/css-masking/#propdef-mask-origin",
 )}
 
 ${helpers.predefined_type(
     "mask-size",
     "background::BackgroundSize",
     "computed::BackgroundSize::auto()",
+    engines="gecko",
     initial_specified_value="specified::BackgroundSize::auto()",
-    products="gecko",
     extra_prefixes="webkit",
     spec="https://drafts.fxtf.org/css-masking/#propdef-mask-size",
     animation_value_type="MaskSizeList",
     vector=True,
     vector_animation_type="repeatable_list",
 )}
 
 ${helpers.single_keyword(
     "mask-composite",
     "add subtract intersect exclude",
+    engines="gecko",
     vector=True,
-    products="gecko",
     extra_prefixes="webkit",
     animation_value_type="discrete",
     spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite",
 )}
 
 ${helpers.predefined_type(
     "mask-image",
     "ImageLayer",
+    engines="gecko",
     initial_value="computed::ImageLayer::none()",
     initial_specified_value="specified::ImageLayer::none()",
     parse_method="parse_with_cors_anonymous",
     spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
     vector=True,
-    products="gecko",
     extra_prefixes="webkit",
     animation_value_type="discrete",
     flags="CREATES_STACKING_CONTEXT",
 )}
 
 ${helpers.predefined_type(
     "x",
     "LengthPercentage",
     "computed::LengthPercentage::zero()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     spec="https://svgwg.org/svg2-draft/geometry.html#X",
 )}
 
 ${helpers.predefined_type(
     "y",
     "LengthPercentage",
     "computed::LengthPercentage::zero()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     spec="https://svgwg.org/svg2-draft/geometry.html#Y",
 )}
 
 ${helpers.predefined_type(
     "cx",
     "LengthPercentage",
     "computed::LengthPercentage::zero()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     spec="https://svgwg.org/svg2-draft/geometry.html#CX",
 )}
 
 ${helpers.predefined_type(
     "cy",
     "LengthPercentage",
     "computed::LengthPercentage::zero()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="ComputedValue",
     spec="https://svgwg.org/svg2-draft/geometry.html#CY",
 )}
 
 ${helpers.predefined_type(
     "rx",
     "NonNegativeLengthPercentageOrAuto",
     "computed::NonNegativeLengthPercentageOrAuto::auto()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="LengthPercentageOrAuto",
     spec="https://svgwg.org/svg2-draft/geometry.html#RX",
 )}
 
 ${helpers.predefined_type(
     "ry",
     "NonNegativeLengthPercentageOrAuto",
     "computed::NonNegativeLengthPercentageOrAuto::auto()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="LengthPercentageOrAuto",
     spec="https://svgwg.org/svg2-draft/geometry.html#RY",
 )}
 
 ${helpers.predefined_type(
     "r",
     "NonNegativeLengthPercentage",
     "computed::NonNegativeLengthPercentage::zero()",
-    products="gecko",
+    engines="gecko",
     animation_value_type="LengthPercentage",
     spec="https://svgwg.org/svg2-draft/geometry.html#R",
 )}
--- a/servo/components/style/properties/longhands/table.mako.rs
+++ b/servo/components/style/properties/longhands/table.mako.rs
@@ -4,23 +4,24 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Table", inherited=False) %>
 
 ${helpers.single_keyword(
     "table-layout",
     "auto fixed",
+    engines="gecko servo-2013",
     gecko_ffi_name="mLayoutStrategy",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-tables/#propdef-table-layout",
     servo_restyle_damage="reflow",
 )}
 
 ${helpers.predefined_type(
     "-x-span",
     "XSpan",
     "computed::XSpan(1)",
-    products="gecko",
+    engines="gecko",
     spec="Internal-only (for `<col span>` pres attr)",
     animation_value_type="none",
     enabled_in="",
 )}
--- a/servo/components/style/properties/longhands/text.mako.rs
+++ b/servo/components/style/properties/longhands/text.mako.rs
@@ -6,71 +6,75 @@
 <% from data import Method %>
 
 <% data.new_style_struct("Text", inherited=False, gecko_name="TextReset") %>
 
 ${helpers.predefined_type(
     "text-overflow",
     "TextOverflow",
     "computed::TextOverflow::get_initial_value()",
+    engines="gecko servo-2013",
     animation_value_type="discrete",
     boxed=True,
     spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.single_keyword(
     "unicode-bidi",
     "normal embed isolate bidi-override isolate-override plaintext",
+    engines="gecko servo-2013",
     animation_value_type="none",
     spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "text-decoration-line",
     "TextDecorationLine",
     "specified::TextDecorationLine::none()",
+    engines="gecko servo-2013 servo-2020",
+    servo_2020_pref="layout.2020.unimplemented",
     initial_specified_value="specified::TextDecorationLine::none()",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.single_keyword(
     "text-decoration-style",
     "solid double dotted dashed wavy -moz-none",
-    products="gecko",
+    engines="gecko",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style",
 )}
 
 ${helpers.predefined_type(
     "text-decoration-color",
     "Color",
     "computed_value::T::currentcolor()",
+    engines="gecko",
     initial_specified_value="specified::Color::currentcolor()",
-    products="gecko",
     animation_value_type="AnimatedColor",
     ignored_when_colors_disabled=True,
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color",
 )}
 
 ${helpers.predefined_type(
     "initial-letter",
     "InitialLetter",
     "computed::InitialLetter::normal()",
+    engines="gecko",
     initial_specified_value="specified::InitialLetter::normal()",
     animation_value_type="discrete",
-    products="gecko",
     gecko_pref="layout.css.initial-letter.enabled",
     spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials",
 )}
 
 ${helpers.predefined_type(
    "text-decoration-thickness",
    "LengthOrAuto",
    "computed::LengthOrAuto::auto()",
-   products="gecko",
+   engines="gecko",
    animation_value_type="ComputedValue",
    gecko_pref="layout.css.text-decoration-thickness.enabled",
    spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-width-property"
 )}
--- a/servo/components/style/properties/longhands/ui.mako.rs
+++ b/servo/components/style/properties/longhands/ui.mako.rs
@@ -9,102 +9,102 @@
 // https://drafts.csswg.org/css-ui-3/
 <% data.new_style_struct("UI", inherited=False, gecko_name="UIReset") %>
 
 // TODO spec says that UAs should not support this
 // we should probably remove from gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=1328331)
 ${helpers.single_keyword(
     "ime-mode",
     "auto normal active disabled inactive",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mIMEMode",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-ui/#input-method-editor",
 )}
 
 ${helpers.single_keyword(
     "scrollbar-width",
     "auto thin none",
-    products="gecko",
+    engines="gecko",
     gecko_enum_prefix="StyleScrollbarWidth",
     animation_value_type="discrete",
     gecko_pref="layout.css.scrollbar-width.enabled",
     enabled_in="chrome",
     spec="https://drafts.csswg.org/css-scrollbars-1/#scrollbar-width"
 )}
 
 ${helpers.predefined_type(
     "user-select",
     "UserSelect",
     "computed::UserSelect::Auto",
-    products="gecko",
+    engines="gecko",
     extra_prefixes="moz webkit",
     animation_value_type="discrete",
     needs_context=False,
     spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select",
 )}
 
 // TODO(emilio): This probably should be hidden from content.
 ${helpers.single_keyword(
     "-moz-window-dragging",
     "default drag no-drag",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mWindowDragging",
     gecko_enum_prefix="StyleWindowDragging",
     animation_value_type="discrete",
     spec="None (Nonstandard Firefox-only property)",
 )}
 
 ${helpers.single_keyword(
     "-moz-window-shadow",
     "none default menu tooltip sheet",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mWindowShadow",
     gecko_constant_prefix="NS_STYLE_WINDOW_SHADOW",
     animation_value_type="discrete",
     enabled_in="chrome",
     spec="None (Nonstandard internal property)",
 )}
 
 ${helpers.predefined_type(
     "-moz-window-opacity",
     "Opacity",
     "1.0",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mWindowOpacity",
     animation_value_type="ComputedValue",
     spec="None (Nonstandard internal property)",
     enabled_in="chrome",
 )}
 
 ${helpers.predefined_type(
     "-moz-window-transform",
     "Transform",
     "generics::transform::Transform::none()",
-    products="gecko",
+    engines="gecko",
     flags="GETCS_NEEDS_LAYOUT_FLUSH",
     animation_value_type="ComputedValue",
     spec="None (Nonstandard internal property)",
     enabled_in="chrome",
 )}
 
 ${helpers.predefined_type(
     "-moz-window-transform-origin",
     "TransformOrigin",
     "computed::TransformOrigin::initial_value()",
+    engines="gecko",
     animation_value_type="ComputedValue",
     gecko_ffi_name="mWindowTransformOrigin",
-    products="gecko",
     boxed=True,
     flags="GETCS_NEEDS_LAYOUT_FLUSH",
     spec="None (Nonstandard internal property)",
     enabled_in="chrome",
 )}
 
 // TODO(emilio): Probably also should be hidden from content.
 ${helpers.predefined_type(
     "-moz-force-broken-image-icon",
     "MozForceBrokenImageIcon",
     "computed::MozForceBrokenImageIcon::false_value()",
+    engines="gecko",
     animation_value_type="discrete",
-    products="gecko",
     spec="None (Nonstandard Firefox-only property)",
 )}
--- a/servo/components/style/properties/longhands/xul.mako.rs
+++ b/servo/components/style/properties/longhands/xul.mako.rs
@@ -6,81 +6,82 @@
 <% from data import Method %>
 
 // Non-standard properties that Gecko uses for XUL elements.
 <% data.new_style_struct("XUL", inherited=False) %>
 
 ${helpers.single_keyword(
     "-moz-box-align",
     "stretch start center baseline end",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mBoxAlign",
     gecko_enum_prefix="StyleBoxAlign",
     animation_value_type="discrete",
     alias="-webkit-box-align",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)",
 )}
 
 ${helpers.single_keyword(
     "-moz-box-direction",
     "normal reverse",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mBoxDirection",
     gecko_enum_prefix="StyleBoxDirection",
     animation_value_type="discrete",
     alias="-webkit-box-direction",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)",
 )}
 
 ${helpers.predefined_type(
     "-moz-box-flex",
     "NonNegativeNumber",
     "From::from(0.)",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mBoxFlex",
     animation_value_type="NonNegativeNumber",
     alias="-webkit-box-flex",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)",
 )}
 
 ${helpers.single_keyword(
     "-moz-box-orient",
     "horizontal vertical",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mBoxOrient",
-    extra_gecko_aliases="inline-axis=horizontal block-axis=vertical",
+    gecko_aliases="inline-axis=horizontal block-axis=vertical",
     gecko_enum_prefix="StyleBoxOrient",
     animation_value_type="discrete",
     alias="-webkit-box-orient",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)",
 )}
 
 ${helpers.single_keyword(
     "-moz-box-pack",
     "start center end justify",
-    products="gecko", gecko_ffi_name="mBoxPack",
+    engines="gecko",
+    gecko_ffi_name="mBoxPack",
     gecko_enum_prefix="StyleBoxPack",
     animation_value_type="discrete",
     alias="-webkit-box-pack",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)",
 )}
 
 ${helpers.single_keyword(
     "-moz-stack-sizing",
     "stretch-to-fit ignore ignore-horizontal ignore-vertical",
-    products="gecko",
+    engines="gecko",
     gecko_ffi_name="mStackSizing",
     gecko_enum_prefix="StyleStackSizing",
     animation_value_type="discrete",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-stack-sizing)",
 )}
 
 ${helpers.predefined_type(
     "-moz-box-ordinal-group",
     "Integer",
     "0",
+    engines="gecko",
     parse_method="parse_non_negative",
-    products="gecko",
     alias="-webkit-box-ordinal-group",
     gecko_ffi_name="mBoxOrdinal",
     animation_value_type="discrete",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)",
 )}
--- a/servo/components/style/properties/properties.html.mako
+++ b/servo/components/style/properties/properties.html.mako
@@ -1,40 +1,31 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <meta name="generator" content="rustdoc">
-    <meta name="description" content="API documentation for the Rust `servo` crate.">
-    <meta name="keywords" content="rust, rustlang, rust-lang, servo">
-    <title>Supported CSS properties - servo - Rust</title>
+    <title>Supported CSS properties in Servo</title>
+    <link rel="stylesheet" type="text/css" href="../normalize.css">
     <link rel="stylesheet" type="text/css" href="../rustdoc.css">
-    <link rel="stylesheet" type="text/css" href="../main.css">
+    <link rel="stylesheet" type="text/css" href="../light.css">
 </head>
 <body class="rustdoc">
-    <!--[if lte IE 8]>
-    <div class="warning">
-        This old browser is unsupported and will most likely display funky
-        things.
-    </div>
-    <![endif]-->
     <section id='main' class="content mod">
-      <h1 class='fqn'><span class='in-band'>CSS properties currently supported in <a class='mod' href=''>Servo</a></span></h1>
-      <div id='properties' class='docblock'>
-        <table>
+      <h1 class='fqn'><span class='in-band'>CSS properties currently supported in Servo</span></h1>
+      % for kind, props in sorted(properties.items()):
+      <h2>${kind.capitalize()}</h2>
+      <table>
+        <tr>
+          <th>Name</th>
+          <th>Pref</th>
+        </tr>
+        % for name, data in sorted(props.items()):
           <tr>
-            <th>Property</th>
-            <th>Flag</th>
-            <th>Shorthand</th>
+            <td><code>${name}</code></td>
+            <td><code>${data['pref'] or ''}</code></td>
           </tr>
-          % for prop in properties:
-            <tr>
-              <td>${prop}</td>
-              <td>${properties[prop]['flag']}</td>
-              <td>${properties[prop]['shorthand']}</td>
-            </tr>
-          % endfor
-        </table>
-      </div>
+        % endfor
+      </table>
+      % endfor
     </section>
 </body>
 </html>
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -162,26 +162,27 @@ pub mod shorthands {
     // when there is one, whatever that is.
     <%
         logical_longhands = []
         other_longhands = []
 
         for p in data.longhands:
             if p.name in ['direction', 'unicode-bidi']:
                 continue;
-            if not p.enabled_in_content() and not p.experimental(product):
+            if not p.enabled_in_content() and not p.experimental(engine):
                 continue;
             if p.logical:
                 logical_longhands.append(p.name)
             else:
                 other_longhands.append(p.name)
 
         data.declare_shorthand(
             "all",
             logical_longhands + other_longhands,
+            engines="gecko servo-2013 servo-2020",
             spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
         )
     %>
 
     /// The max amount of longhands that the `all` shorthand will ever contain.
     pub const ALL_SHORTHAND_MAX_LEN: usize = ${len(logical_longhands + other_longhands)};
 }
 
@@ -421,17 +422,17 @@ pub mod animated_properties {
 /// A longhand or shorthand property.
 #[derive(Clone, Copy, Debug)]
 pub struct NonCustomPropertyId(usize);
 
 /// The length of all the non-custom properties.
 pub const NON_CUSTOM_PROPERTY_ID_COUNT: usize =
     ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())};
 
-% if product == "gecko":
+% if engine == "gecko":
 #[allow(dead_code)]
 unsafe fn static_assert_nscsspropertyid() {
     % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
     std::mem::transmute::<[u8; ${i}], [u8; ${property.nscsspropertyid()} as usize]>([0; ${i}]); // ${property.name}
     % endfor
 }
 % endif
 
@@ -487,43 +488,47 @@ impl NonCustomPropertyId {
         ${static_non_custom_property_id_set("ANIMATABLE", lambda p: p.animatable)}
         ANIMATABLE.contains(self)
     }
 
     #[inline]
     fn enabled_for_all_content(self) -> bool {
         ${static_non_custom_property_id_set(
             "EXPERIMENTAL",
-            lambda p: p.experimental(product)
+            lambda p: p.experimental(engine)
         )}
 
         ${static_non_custom_property_id_set(
             "ALWAYS_ENABLED",
-            lambda p: (not p.experimental(product)) and p.enabled_in_content()
+            lambda p: (not p.experimental(engine)) and p.enabled_in_content()
         )}
 
         let passes_pref_check = || {
-            % if product == "servo":
+            % if engine == "gecko":
+                unsafe { structs::nsCSSProps_gPropertyEnabled[self.0] }
+            % else:
                 static PREF_NAME: [Option< &str>; ${len(data.longhands) + len(data.shorthands)}] = [
                     % for property in data.longhands + data.shorthands:
-                        % if property.servo_pref:
-                            Some("${property.servo_pref}"),
+                        <%
+                            attrs = {"servo-2013": "servo_2013_pref", "servo-2020": "servo_2020_pref"}
+                            pref = getattr(property, attrs[engine])
+                        %>
+                        % if pref:
+                            Some("${pref}"),
                         % else:
                             None,
                         % endif
                     % endfor
                 ];
                 let pref = match PREF_NAME[self.0] {
                     None => return true,
                     Some(pref) => pref,
                 };
 
                 prefs::pref_map().get(pref).as_bool().unwrap_or(false)
-            % else:
-                unsafe { structs::nsCSSProps_gPropertyEnabled[self.0] }
             % endif
         };
 
         if ALWAYS_ENABLED.contains(self) {
             return true
         }
 
         if EXPERIMENTAL.contains(self) && passes_pref_check() {
@@ -1237,33 +1242,35 @@ impl LonghandId {
         PropertyFlags::from_bits_truncate(FLAGS[self as usize])
     }
 
     /// Only a few properties are allowed to depend on the visited state of
     /// links. When cascading visited styles, we can save time by only
     /// processing these properties.
     fn is_visited_dependent(&self) -> bool {
         matches!(*self,
-            % if product == "gecko":
+            % if engine == "gecko":
             LonghandId::ColumnRuleColor |
             LonghandId::TextEmphasisColor |
             LonghandId::WebkitTextFillColor |
             LonghandId::WebkitTextStrokeColor |
             LonghandId::TextDecorationColor |
             LonghandId::Fill |
             LonghandId::Stroke |
             LonghandId::CaretColor |
             % endif
-            LonghandId::Color |
+            % if engine in ["gecko", "servo-2013"]:
             LonghandId::BackgroundColor |
             LonghandId::BorderTopColor |
             LonghandId::BorderRightColor |
             LonghandId::BorderBottomColor |
             LonghandId::BorderLeftColor |
-            LonghandId::OutlineColor
+            LonghandId::OutlineColor |
+            % endif
+            LonghandId::Color
         )
     }
 
     /// Returns true if the property is one that is ignored when document
     /// colors are disabled.
     #[inline]
     fn ignored_when_document_colors_disabled(self) -> bool {
         LonghandIdSet::ignored_when_colors_disabled().contains(self)
@@ -1274,17 +1281,17 @@ impl LonghandId {
     ///
     /// So we classify properties into "early" and "other", such that the only
     /// dependencies can be from "other" to "early".
     ///
     /// Unfortunately, it’s not easy to check that this classification is
     /// correct.
     fn is_early_property(&self) -> bool {
         matches!(*self,
-            % if product == 'gecko':
+            % if engine == "gecko":
 
             // Needed to properly compute the writing mode, to resolve logical
             // properties, and similar stuff. In this block instead of along
             // `WritingMode` and `Direction` just for convenience, since it's
             // Gecko-only (for now at least).
             //
             // see WritingMode::new.
             LonghandId::TextOrientation |
@@ -1300,22 +1307,24 @@ impl LonghandId {
             // Needed for ruby to respect language-dependent min-font-size
             // preferences properly, see bug 1165538.
             LonghandId::MozMinFontSizeRatio |
 
             // Needed to do font-size for MathML. :(
             LonghandId::MozScriptLevel |
             % endif
 
+            % if engine in ["gecko", "servo-2013"]:
             // Needed to compute the first available font, in order to
             // compute font-relative units correctly.
             LonghandId::FontSize |
             LonghandId::FontWeight |
             LonghandId::FontStretch |
             LonghandId::FontStyle |
+            % endif
             LonghandId::FontFamily |
 
             // Needed to properly compute the writing mode, to resolve logical
             // properties, and similar stuff.
             LonghandId::WritingMode |
             LonghandId::Direction
         )
     }
@@ -2146,17 +2155,17 @@ impl PropertyDeclaration {
             },
             _ => None,
         }
     }
 
     /// Returns whether or not the property is set by a system font
     pub fn get_system(&self) -> Option<SystemFont> {
         match *self {
-            % if product == "gecko":
+            % if engine == "gecko":
             % for prop in SYSTEM_FONT_LONGHANDS:
                 PropertyDeclaration::${to_camel_case(prop)}(ref prop) => {
                     prop.get_system()
                 }
             % endfor
             % endif
             _ => None,
         }
@@ -2350,17 +2359,18 @@ impl PropertyDeclaration {
         if let Some(use_counters) = context.use_counters {
             use_counters.non_custom_properties.record(non_custom_id.unwrap());
         }
         Ok(())
     }
 }
 
 type SubpropertiesArray<T> =
-    [T; ${max(len(s.sub_properties) for s in data.shorthands_except_all())}];
+    [T; ${max(len(s.sub_properties) for s in data.shorthands_except_all()) \
+          if data.shorthands_except_all() else 0}];
 
 type SubpropertiesVec<T> = ArrayVec<SubpropertiesArray<T>>;
 
 /// A stack-allocated vector of `PropertyDeclaration`
 /// large enough to parse one CSS `key: value` declaration.
 /// (Shorthands expand to multiple `PropertyDeclaration`s.)
 pub struct SourcePropertyDeclaration {
     declarations: SubpropertiesVec<PropertyDeclaration>,
@@ -3176,17 +3186,17 @@ impl ComputedValuesInner {
             }
         }
 
         // Neither perspective nor transform present
         false
     }
 }
 
-% if product == "gecko":
+% if engine == "gecko":
     pub use crate::servo_arc::RawOffsetArc as BuilderArc;
     /// Clone an arc, returning a regular arc
     fn clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T> {
         Arc::from_raw_offset(x.clone())
     }
 % else:
     pub use crate::servo_arc::Arc as BuilderArc;
     /// Clone an arc, returning a regular arc
@@ -3489,17 +3499,17 @@ impl<'a> StyleBuilder<'a> {
                 reset_struct,
                 % if property.logical:
                 self.writing_mode,
                 % endif
             );
     }
     % endif
 
-    % if not property.is_vector or property.simple_vector_bindings or product != "gecko":
+    % if not property.is_vector or property.simple_vector_bindings or engine in ["servo-2013", "servo-2020"]:
     /// Set the `${property.ident}` to the computed value `value`.
     #[allow(non_snake_case)]
     pub fn set_${property.ident}(
         &mut self,
         value: longhands::${property.ident}::computed_value::T
     ) {
         % if not property.style_struct.inherited:
         self.modified_reset = true;
@@ -3877,17 +3887,17 @@ macro_rules! longhand_properties_idents 
         $macro_name! {
             % for property in data.longhands:
                 { ${property.ident}, ${"true" if property.boxed else "false"} }
             % endfor
         }
     }
 }
 
-% if product == "servo":
+% if engine in ["servo-2013", "servo-2020"]:
 % for effect_name in ["repaint", "reflow_out_of_flow", "reflow", "rebuild_and_reflow_inline", "rebuild_and_reflow"]:
     macro_rules! restyle_damage_${effect_name} {
         ($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ]) => ({
             if
                 % for style_struct in data.active_style_structs():
                     % for longhand in style_struct.longhands:
                         % if effect_name in longhand.servo_restyle_damage.split() and not longhand.logical:
                             $old.get_${style_struct.name_lower}().${longhand.ident} !=
--- a/servo/components/style/properties/shorthands/background.mako.rs
+++ b/servo/components/style/properties/shorthands/background.mako.rs
@@ -1,16 +1,17 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 // TODO: other background-* properties
 <%helpers:shorthand name="background"
+                    engines="gecko servo-2013"
                     sub_properties="background-color background-position-x background-position-y background-repeat
                                     background-attachment background-image background-size background-origin
                                     background-clip"
                     spec="https://drafts.csswg.org/css-backgrounds/#the-background">
     use crate::properties::longhands::{background_position_x, background_position_y, background_repeat};
     use crate::properties::longhands::{background_attachment, background_image, background_size, background_origin};
     use crate::properties::longhands::background_clip;
     use crate::properties::longhands::background_clip::single_value::computed_value::T as Clip;
@@ -188,16 +189,17 @@
             }
 
             Ok(())
         }
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand name="background-position"
+                    engines="gecko servo-2013"
                     flags="SHORTHAND_IN_GETCS"
                     sub_properties="background-position-x background-position-y"
                     spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position">
     use crate::properties::longhands::{background_position_x, background_position_y};
     use crate::values::specified::AllowQuirks;
     use crate::values::specified::position::Position;
 
     pub fn parse_value<'i, 't>(
--- a/servo/components/style/properties/shorthands/border.mako.rs
+++ b/servo/components/style/properties/shorthands/border.mako.rs
@@ -1,28 +1,37 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import to_rust_ident, ALL_SIDES, PHYSICAL_SIDES, maybe_moz_logical_alias %>
 
-${helpers.four_sides_shorthand("border-color", "border-%s-color", "specified::Color::parse",
-                               spec="https://drafts.csswg.org/css-backgrounds/#border-color",
-                               allow_quirks="Yes")}
+${helpers.four_sides_shorthand(
+    "border-color",
+    "border-%s-color",
+    "specified::Color::parse",
+    engines="gecko servo-2013",
+    spec="https://drafts.csswg.org/css-backgrounds/#border-color",
+    allow_quirks="Yes",
+)}
 
 ${helpers.four_sides_shorthand(
     "border-style",
     "border-%s-style",
     "specified::BorderStyle::parse",
+    engines="gecko servo-2013",
     needs_context=False,
     spec="https://drafts.csswg.org/css-backgrounds/#border-style",
 )}
 
-<%helpers:shorthand name="border-width" sub_properties="${
+<%helpers:shorthand
+    name="border-width"
+    engines="gecko servo-2013"
+    sub_properties="${
         ' '.join('border-%s-width' % side
                  for side in PHYSICAL_SIDES)}"
     spec="https://drafts.csswg.org/css-backgrounds/#border-width">
     use crate::values::generics::rect::Rect;
     use crate::values::specified::{AllowQuirks, BorderSideWidth};
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
@@ -96,21 +105,23 @@ pub fn parse_border<'i, 't>(
 % for side, logical in ALL_SIDES:
     <%
         spec = "https://drafts.csswg.org/css-backgrounds/#border-%s" % side
         if logical:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-%s" % side
     %>
     <%helpers:shorthand
         name="border-${side}"
+        engines="gecko servo-2013 servo-2020"
+        servo_2020_pref="layout.2020.unimplemented"
         sub_properties="${' '.join(
             'border-%s-%s' % (side, prop)
             for prop in ['color', 'style', 'width']
         )}"
-        alias="${maybe_moz_logical_alias(product, (side, logical), '-moz-border-%s')}"
+        alias="${maybe_moz_logical_alias(engine, (side, logical), '-moz-border-%s')}"
         spec="${spec}">
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         let (color, style, width) = super::parse_border(context, input)?;
         Ok(expanded! {
@@ -130,16 +141,17 @@ pub fn parse_border<'i, 't>(
             )
         }
     }
 
     </%helpers:shorthand>
 % endfor
 
 <%helpers:shorthand name="border"
+    engines="gecko servo-2013"
     sub_properties="${' '.join('border-%s-%s' % (side, prop)
         for side in PHYSICAL_SIDES
         for prop in ['color', 'style', 'width'])}
         ${' '.join('border-image-%s' % name
         for name in ['outset', 'repeat', 'slice', 'source', 'width'])}"
     derive_value_info="False"
     spec="https://drafts.csswg.org/css-backgrounds/#border">
 
@@ -211,20 +223,26 @@ pub fn parse_border<'i, 't>(
     impl SpecifiedValueInfo for Longhands {
         const SUPPORTED_TYPES: u8 = ${border_left}::SUPPORTED_TYPES;
         fn collect_completion_keywords(f: KeywordsCollectFn) {
             ${border_left}::collect_completion_keywords(f);
         }
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="border-radius" sub_properties="${' '.join(
-    'border-%s-radius' % (corner)
-     for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']
-)}" extra_prefixes="webkit" spec="https://drafts.csswg.org/css-backgrounds/#border-radius">
+<%helpers:shorthand
+    name="border-radius"
+    engines="gecko servo-2013"
+    sub_properties="${' '.join(
+        'border-%s-radius' % (corner)
+         for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']
+    )}"
+    extra_prefixes="webkit"
+    spec="https://drafts.csswg.org/css-backgrounds/#border-radius"
+>
     use crate::values::generics::rect::Rect;
     use crate::values::generics::border::BorderCornerRadius;
     use crate::values::specified::border::BorderRadius;
     use crate::parser::Parse;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
@@ -251,20 +269,24 @@ pub fn parse_border<'i, 't>(
             let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width());
             let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height());
 
             BorderRadius::serialize_rects(widths, heights, dest)
         }
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="border-image" sub_properties="border-image-outset
-    border-image-repeat border-image-slice border-image-source border-image-width"
+<%helpers:shorthand
+    name="border-image"
+    engines="gecko servo-2013"
+    sub_properties="border-image-outset
+        border-image-repeat border-image-slice border-image-source border-image-width"
     extra_prefixes="moz:layout.css.prefixes.border-image webkit"
-    spec="https://drafts.csswg.org/css-backgrounds-3/#border-image">
+    spec="https://drafts.csswg.org/css-backgrounds-3/#border-image"
+>
     use crate::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
     use crate::properties::longhands::{border_image_source, border_image_width};
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         % for name in "outset repeat slice source width".split():
@@ -357,16 +379,17 @@ pub fn parse_border<'i, 't>(
 </%helpers:shorthand>
 
 % for axis in ["block", "inline"]:
     % for prop in ["width", "style", "color"]:
         <%
             spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s-%s" % (axis, prop)
         %>
         <%helpers:shorthand
+            engines="gecko servo-2013"
             name="border-${axis}-${prop}"
             sub_properties="${' '.join(
                 'border-%s-%s-%s' % (axis, side, prop)
                 for side in ['start', 'end']
             )}"
             spec="${spec}">
 
             use crate::properties::longhands::border_${axis}_start_${prop};
@@ -402,16 +425,17 @@ pub fn parse_border<'i, 't>(
 % endfor
 
 % for axis in ["block", "inline"]:
     <%
         spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s" % (axis)
     %>
     <%helpers:shorthand
         name="border-${axis}"
+        engines="gecko servo-2013"
         sub_properties="${' '.join(
             'border-%s-%s-width' % (axis, side)
             for side in ['start', 'end']
         )} ${' '.join(
             'border-%s-%s-style' % (axis, side)
             for side in ['start', 'end']
         )} ${' '.join(
             'border-%s-%s-color' % (axis, side)
--- a/servo/components/style/properties/shorthands/box.mako.rs
+++ b/servo/components/style/properties/shorthands/box.mako.rs
@@ -4,48 +4,51 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 ${helpers.two_properties_shorthand(
     "overflow",
     "overflow-x",
     "overflow-y",
     "specified::Overflow::parse",
+    engines="gecko servo-2013",
     flags="SHORTHAND_IN_GETCS",
     needs_context=False,
     spec="https://drafts.csswg.org/css-overflow/#propdef-overflow",
 )}
 
 ${helpers.two_properties_shorthand(
     "overflow-clip-box",
     "overflow-clip-box-block",
     "overflow-clip-box-inline",
     "specified::OverflowClipBox::parse",
+    engines="gecko",
     enabled_in="ua",
     needs_context=False,
     gecko_pref="layout.css.overflow-clip-box.enabled",
     spec="Internal, may be standardized in the future "
          "(https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)",
-    products="gecko",
 )}
 
+#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
 macro_rules! try_parse_one {
     ($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
         if $var.is_none() {
             if let Ok(value) = $input.try(|i| {
                 $prop_module::single_value::parse($context, i)
             }) {
                 $var = Some(value);
                 continue;
             }
         }
     };
 }
 
 <%helpers:shorthand name="transition"
+                    engines="gecko servo-2013"
                     extra_prefixes="moz:layout.css.prefixes.transitions webkit"
                     sub_properties="transition-property transition-duration
                                     transition-timing-function
                                     transition-delay"
                     spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
     use crate::parser::Parse;
     % for prop in "delay duration property timing_function".split():
     use crate::properties::longhands::transition_${prop};
@@ -181,16 +184,17 @@ macro_rules! try_parse_one {
                 % endfor
             }
             Ok(())
         }
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand name="animation"
+                    engines="gecko servo-2013"
                     extra_prefixes="moz:layout.css.prefixes.animations webkit"
                     sub_properties="animation-name animation-duration
                                     animation-timing-function animation-delay
                                     animation-iteration-count animation-direction
                                     animation-fill-mode animation-play-state"
                     allowed_in_keyframe_block="False"
                     spec="https://drafts.csswg.org/css-animations/#propdef-animation">
     <%
@@ -303,25 +307,25 @@ macro_rules! try_parse_one {
     }
 </%helpers:shorthand>
 
 ${helpers.two_properties_shorthand(
     "overscroll-behavior",
     "overscroll-behavior-x",
     "overscroll-behavior-y",
     "specified::OverscrollBehavior::parse",
+    engines="gecko",
     needs_context=False,
-    products="gecko",
     gecko_pref="layout.css.overscroll-behavior.enabled",
     spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties",
 )}
 
 <%helpers:shorthand
+    engines="gecko"
     name="page-break-before"
-    products="gecko"
     flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
     sub_properties="break-before"
     spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before"
 >
     pub fn parse_value<'i>(
         _: &ParserContext,
         input: &mut Parser<'i, '_>,
     ) -> Result<Longhands, ParseError<'i>> {
@@ -334,18 +338,18 @@ macro_rules! try_parse_one {
     impl<'a> ToCss for LonghandsToSerialize<'a> {
         fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
             self.break_before.to_css_legacy(dest)
         }
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand
+    engines="gecko"
     name="page-break-after"
-    products="gecko"
     flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
     sub_properties="break-after"
     spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after"
 >
     pub fn parse_value<'i>(
         _: &ParserContext,
         input: &mut Parser<'i, '_>,
     ) -> Result<Longhands, ParseError<'i>> {
--- a/servo/components/style/properties/shorthands/column.mako.rs
+++ b/servo/components/style/properties/shorthands/column.mako.rs
@@ -1,17 +1,18 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <%helpers:shorthand name="columns"
+                    engines="gecko servo-2013"
                     sub_properties="column-width column-count"
-                    servo_pref="layout.columns.enabled",
+                    servo_2013_pref="layout.columns.enabled",
                     derive_serialize="True"
                     extra_prefixes="moz" spec="https://drafts.csswg.org/css-multicol/#propdef-columns">
     use crate::properties::longhands::{column_count, column_width};
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
@@ -50,20 +51,24 @@
             Ok(expanded! {
                 column_count: unwrap_or_initial!(column_count),
                 column_width: unwrap_or_initial!(column_width),
             })
         }
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="column-rule" products="gecko" extra_prefixes="moz"
+<%helpers:shorthand
+    name="column-rule"
+    engines="gecko"
+    extra_prefixes="moz"
     sub_properties="column-rule-width column-rule-style column-rule-color"
     derive_serialize="True"
-    spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule">
+    spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule"
+>
     use crate::properties::longhands::{column_rule_width, column_rule_style};
     use crate::properties::longhands::column_rule_color;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         % for name in "width style color".split():
--- a/servo/components/style/properties/shorthands/font.mako.rs
+++ b/servo/components/style/properties/shorthands/font.mako.rs
@@ -1,65 +1,76 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import SYSTEM_FONT_LONGHANDS %>
 
-<%helpers:shorthand name="font"
-                    sub_properties="font-style font-variant-caps font-weight font-stretch
-                                    font-size line-height font-family
-                                    ${'font-size-adjust' if product == 'gecko' else ''}
-                                    ${'font-kerning' if product == 'gecko' else ''}
-                                    ${'font-optical-sizing' if product == 'gecko' else ''}
-                                    ${'font-variant-alternates' if product == 'gecko' else ''}
-                                    ${'font-variant-east-asian' if product == 'gecko' else ''}
-                                    ${'font-variant-ligatures' if product == 'gecko' else ''}
-                                    ${'font-variant-numeric' if product == 'gecko' else ''}
-                                    ${'font-variant-position' if product == 'gecko' else ''}
-                                    ${'font-language-override' if product == 'gecko' else ''}
-                                    ${'font-feature-settings' if product == 'gecko' else ''}
-                                    ${'font-variation-settings' if product == 'gecko' else ''}"
-                    derive_value_info="False"
-                    spec="https://drafts.csswg.org/css-fonts-3/#propdef-font">
+<%helpers:shorthand
+    name="font"
+    engines="gecko servo-2013 servo-2020"
+    servo_2020_pref="layout.2020.unimplemented"
+    sub_properties="
+        font-style
+        font-variant-caps
+        font-weight
+        font-stretch
+        font-size
+        line-height
+        font-family
+        ${'font-size-adjust' if engine == 'gecko' else ''}
+        ${'font-kerning' if engine == 'gecko' else ''}
+        ${'font-optical-sizing' if engine == 'gecko' else ''}
+        ${'font-variant-alternates' if engine == 'gecko' else ''}
+        ${'font-variant-east-asian' if engine == 'gecko' else ''}
+        ${'font-variant-ligatures' if engine == 'gecko' else ''}
+        ${'font-variant-numeric' if engine == 'gecko' else ''}
+        ${'font-variant-position' if engine == 'gecko' else ''}
+        ${'font-language-override' if engine == 'gecko' else ''}
+        ${'font-feature-settings' if engine == 'gecko' else ''}
+        ${'font-variation-settings' if engine == 'gecko' else ''}
+    "
+    derive_value_info="False"
+    spec="https://drafts.csswg.org/css-fonts-3/#propdef-font"
+>
     use crate::parser::Parse;
     use crate::properties::longhands::{font_family, font_style, font_weight, font_stretch};
     use crate::properties::longhands::font_variant_caps;
     #[cfg(feature = "gecko")]
     use crate::properties::longhands::system_font::SystemFont;
     use crate::values::specified::text::LineHeight;
     use crate::values::specified::FontSize;
     use crate::values::specified::font::{FontStretch, FontStretchKeyword};
 
     <%
         gecko_sub_properties = "kerning language_override size_adjust \
                                 variant_alternates variant_east_asian \
                                 variant_ligatures variant_numeric \
                                 variant_position feature_settings \
                                 variation_settings optical_sizing".split()
     %>
-    % if product == "gecko":
+    % if engine == "gecko":
         % for prop in gecko_sub_properties:
             use crate::properties::longhands::font_${prop};
         % endfor
     % endif
     use self::font_family::SpecifiedValue as FontFamily;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         let mut nb_normals = 0;
         let mut style = None;
         let mut variant_caps = None;
         let mut weight = None;
         let mut stretch = None;
         let size;
-        % if product == "gecko":
+        % if engine == "gecko":
             if let Ok(sys) = input.try(SystemFont::parse) {
                 return Ok(expanded! {
                      % for name in SYSTEM_FONT_LONGHANDS:
                          % if name == "font_size":
                              ${name}: FontSize::system_font(sys),
                          % else:
                              ${name}: ${name}::SpecifiedValue::system_font(sys),
                          % endif
@@ -130,43 +141,43 @@
         let family = FontFamily::parse_specified(input)?;
         Ok(expanded! {
             % for name in "style weight stretch variant_caps".split():
                 font_${name}: unwrap_or_initial!(font_${name}, ${name}),
             % endfor
             font_size: size,
             line_height: line_height.unwrap_or(LineHeight::normal()),
             font_family: family,
-            % if product == "gecko":
+            % if engine == "gecko":
                 % for name in gecko_sub_properties:
                     font_${name}: font_${name}::get_initial_specified_value(),
                 % endfor
             % endif
         })
     }
 
-    % if product == "gecko":
+    % if engine == "gecko":
         enum CheckSystemResult {
             AllSystem(SystemFont),
             SomeSystem,
             None
         }
     % endif
 
     impl<'a> ToCss for LonghandsToSerialize<'a> {
         fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
-            % if product == "gecko":
+            % if engine == "gecko":
                 match self.check_system() {
                     CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),
                     CheckSystemResult::SomeSystem => return Ok(()),
                     CheckSystemResult::None => {}
                 }
             % endif
 
-            % if product == "gecko":
+            % if engine == "gecko":
             if let Some(v) = self.font_optical_sizing {
                 if v != &font_optical_sizing::get_initial_specified_value() {
                     return Ok(());
                 }
             }
             if let Some(v) = self.font_variation_settings {
                 if v != &font_variation_settings::get_initial_specified_value() {
                     return Ok(());
@@ -217,17 +228,17 @@
             dest.write_str(" ")?;
             self.font_family.to_css(dest)?;
 
             Ok(())
         }
     }
 
     impl<'a> LonghandsToSerialize<'a> {
-        % if product == "gecko":
+        % if engine == "gecko":
         /// Check if some or all members are system fonts
         fn check_system(&self) -> CheckSystemResult {
             let mut sys = None;
             let mut all = true;
 
             % for prop in SYSTEM_FONT_LONGHANDS:
             % if prop == "font_optical_sizing" or prop == "font_variation_settings":
             if let Some(value) = self.${prop} {
@@ -280,28 +291,29 @@
             ${p}::collect_completion_keywords(f);
             % endfor
             <longhands::system_font::SystemFont as SpecifiedValueInfo>::collect_completion_keywords(f);
         }
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand name="font-variant"
+                    engines="gecko servo-2013"
                     flags="SHORTHAND_IN_GETCS"
                     sub_properties="font-variant-caps
-                                    ${'font-variant-alternates' if product == 'gecko' else ''}
-                                    ${'font-variant-east-asian' if product == 'gecko' else ''}
-                                    ${'font-variant-ligatures' if product == 'gecko' else ''}
-                                    ${'font-variant-numeric' if product == 'gecko' else ''}
-                                    ${'font-variant-position' if product == 'gecko' else ''}"
+                                    ${'font-variant-alternates' if engine == 'gecko' else ''}
+                                    ${'font-variant-east-asian' if engine == 'gecko' else ''}
+                                    ${'font-variant-ligatures' if engine == 'gecko' else ''}
+                                    ${'font-variant-numeric' if engine == 'gecko' else ''}
+                                    ${'font-variant-position' if engine == 'gecko' else ''}"
                     spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-variant">
     <% gecko_sub_properties = "alternates east_asian ligatures numeric position".split() %>
     <%
         sub_properties = ["caps"]
-        if product == "gecko":
+        if engine == "gecko":
             sub_properties += gecko_sub_properties
     %>
 
 % for prop in sub_properties:
     use crate::properties::longhands::font_variant_${prop};
 % endfor
     #[allow(unused_imports)]
     use crate::values::specified::FontVariantLigatures;
@@ -314,17 +326,17 @@
         let mut ${prop} = None;
     % endfor
 
         if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
             // Leave the values to None, 'normal' is the initial value for all the sub properties.
         } else if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             // The 'none' value sets 'font-variant-ligatures' to 'none' and resets all other sub properties
             // to their initial value.
-        % if product == "gecko":
+        % if engine == "gecko":
             ligatures = Some(FontVariantLigatures::none());
         % endif
         } else {
             let mut has_custom_value: bool = false;
             loop {
                 if input.try(|input| input.expect_ident_matching("normal")).is_ok() ||
                    input.try(|input| input.expect_ident_matching("none")).is_ok() {
                     return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
@@ -354,17 +366,17 @@
         })
     }
 
     impl<'a> ToCss for LonghandsToSerialize<'a>  {
         #[allow(unused_assignments)]
         fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
 
             let has_none_ligatures =
-            % if product == "gecko":
+            % if engine == "gecko":
                 self.font_variant_ligatures == &FontVariantLigatures::none();
             % else:
                 false;
             % endif
 
             const TOTAL_SUBPROPS: usize = ${len(sub_properties)};
             let mut nb_normals = 0;
         % for prop in sub_properties:
--- a/servo/components/style/properties/shorthands/inherited_svg.mako.rs
+++ b/servo/components/style/properties/shorthands/inherited_svg.mako.rs
@@ -1,17 +1,20 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
-<%helpers:shorthand name="marker" products="gecko"
+<%helpers:shorthand
+    name="marker"
+    engines="gecko"
     sub_properties="marker-start marker-end marker-mid"
-    spec="https://www.w3.org/TR/SVG2/painting.html#MarkerShorthand">
+    spec="https://www.w3.org/TR/SVG2/painting.html#MarkerShorthand"
+>
     use crate::values::specified::url::UrlOrNone;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         use crate::parser::Parse;
         let url = UrlOrNone::parse(context, input)?;
--- a/servo/components/style/properties/shorthands/inherited_text.mako.rs
+++ b/servo/components/style/properties/shorthands/inherited_text.mako.rs
@@ -1,18 +1,21 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
-<%helpers:shorthand name="text-emphasis" products="gecko"
+<%helpers:shorthand
+    name="text-emphasis"
+    engines="gecko"
     sub_properties="text-emphasis-style text-emphasis-color"
     derive_serialize="True"
-    spec="https://drafts.csswg.org/css-text-decor-3/#text-emphasis-property">
+    spec="https://drafts.csswg.org/css-text-decor-3/#text-emphasis-property"
+>
     use crate::properties::longhands::{text_emphasis_color, text_emphasis_style};
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         let mut color = None;
         let mut style = None;
@@ -41,19 +44,19 @@
             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 </%helpers:shorthand>
 
 // CSS Compatibility
 // https://compat.spec.whatwg.org/
 <%helpers:shorthand name="-webkit-text-stroke"
+                    engines="gecko"
                     sub_properties="-webkit-text-stroke-width
                                     -webkit-text-stroke-color"
-                    products="gecko"
                     derive_serialize="True"
                     spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke">
     use crate::properties::longhands::{_webkit_text_stroke_color, _webkit_text_stroke_width};
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
--- a/servo/components/style/properties/shorthands/list.mako.rs
+++ b/servo/components/style/properties/shorthands/list.mako.rs
@@ -1,15 +1,16 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <%helpers:shorthand name="list-style"
+                    engines="gecko servo-2013"
                     sub_properties="list-style-position list-style-image list-style-type"
                     derive_serialize="True"
                     spec="https://drafts.csswg.org/css-lists/#propdef-list-style">
     use crate::properties::longhands::{list_style_image, list_style_position, list_style_type};
     use crate::values::specified::url::ImageUrlOrNone;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
@@ -56,21 +57,21 @@
                 }
             }
             break
         }
 
         let position = unwrap_or_initial!(list_style_position, position);
 
         fn list_style_type_none() -> list_style_type::SpecifiedValue {
-            % if product == "servo":
-            list_style_type::SpecifiedValue::None
+            % if engine == "gecko":
+                use crate::values::generics::CounterStyleOrNone;
+                list_style_type::SpecifiedValue::CounterStyle(CounterStyleOrNone::None)
             % else:
-            use crate::values::generics::CounterStyleOrNone;
-            list_style_type::SpecifiedValue::CounterStyle(CounterStyleOrNone::None)
+                list_style_type::SpecifiedValue::None
             % endif
         }
 
         // If there are two `none`s, then we can't have a type or image; if there is one `none`,
         // then we can't have both a type *and* an image; if there is no `none` then we're fine as
         // long as we parsed something.
         match (any, nones, list_style_type, image) {
             (true, 2, None, None) => {
--- a/servo/components/style/properties/shorthands/margin.mako.rs
+++ b/servo/components/style/properties/shorthands/margin.mako.rs
@@ -3,57 +3,60 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 ${helpers.four_sides_shorthand(
     "margin",
     "margin-%s",
     "specified::LengthPercentageOrAuto::parse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-box/#propdef-margin",
     allowed_in_page_rule=True,
     allow_quirks="Yes",
 )}
 
 ${helpers.two_properties_shorthand(
     "margin-block",
     "margin-block-start",
     "margin-block-end",
     "specified::LengthPercentageOrAuto::parse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-logical/#propdef-margin-block"
 )}
 
 ${helpers.two_properties_shorthand(
     "margin-inline",
     "margin-inline-start",
     "margin-inline-end",
     "specified::LengthPercentageOrAuto::parse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-logical/#propdef-margin-inline"
 )}
 
 ${helpers.four_sides_shorthand(
     "scroll-margin",
     "scroll-margin-%s",
     "specified::Length::parse",
+    engines="gecko",
     spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin",
-    products="gecko",
     gecko_pref="layout.css.scroll-snap-v1.enabled",
 )}
 
 ${helpers.two_properties_shorthand(
     "scroll-margin-block",
     "scroll-margin-block-start",
     "scroll-margin-block-end",
     "specified::Length::parse",
+    engines="gecko",
     spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-block",
-    products="gecko",
     gecko_pref="layout.css.scroll-snap-v1.enabled",
 )}
 
 ${helpers.two_properties_shorthand(
     "scroll-margin-inline",
     "scroll-margin-inline-start",
     "scroll-margin-inline-end",
     "specified::Length::parse",
+    engines="gecko",
     spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-inline",
-    products="gecko",
     gecko_pref="layout.css.scroll-snap-v1.enabled",
 )}
--- a/servo/components/style/properties/shorthands/outline.mako.rs
+++ b/servo/components/style/properties/shorthands/outline.mako.rs
@@ -1,15 +1,16 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <%helpers:shorthand name="outline"
+                    engines="gecko servo-2013"
                     sub_properties="outline-color outline-style outline-width"
                     derive_serialize="True"
                     spec="https://drafts.csswg.org/css-ui/#propdef-outline">
     use crate::properties::longhands::{outline_color, outline_width, outline_style};
     use crate::values::specified;
     use crate::parser::Parse;
 
     pub fn parse_value<'i, 't>(
@@ -53,20 +54,25 @@
             })
         } else {
             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 </%helpers:shorthand>
 
 // The -moz-outline-radius shorthand is non-standard and not on a standards track.
-<%helpers:shorthand name="-moz-outline-radius" sub_properties="${' '.join(
-    '-moz-outline-radius-%s' % corner
-    for corner in ['topleft', 'topright', 'bottomright', 'bottomleft']
-)}" products="gecko" spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)">
+<%helpers:shorthand
+    name="-moz-outline-radius"
+    engines="gecko"
+    sub_properties="${' '.join(
+        '-moz-outline-radius-%s' % corner
+        for corner in ['topleft', 'topright', 'bottomright', 'bottomleft']
+    )}"
+    spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)"
+>
     use crate::values::generics::rect::Rect;
     use crate::values::specified::border::BorderRadius;
     use crate::parser::Parse;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
--- a/servo/components/style/properties/shorthands/padding.mako.rs
+++ b/servo/components/style/properties/shorthands/padding.mako.rs
@@ -3,57 +3,60 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 ${helpers.four_sides_shorthand(
     "padding",
     "padding-%s",
     "specified::NonNegativeLengthPercentage::parse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-box-3/#propdef-padding",
     allow_quirks="Yes",
 )}
 
 ${helpers.two_properties_shorthand(
     "padding-block",
     "padding-block-start",
     "padding-block-end",
     "specified::NonNegativeLengthPercentage::parse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-logical/#propdef-padding-block"
 )}
 
 ${helpers.two_properties_shorthand(
     "padding-inline",
     "padding-inline-start",
     "padding-inline-end",
     "specified::NonNegativeLengthPercentage::parse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-logical/#propdef-padding-inline"
 )}
 
 ${helpers.four_sides_shorthand(
     "scroll-padding",
     "scroll-padding-%s",
     "specified::NonNegativeLengthPercentageOrAuto::parse",
-    products="gecko",
+    engines="gecko",
     gecko_pref="layout.css.scroll-snap-v1.enabled",
     spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding"
 )}
 
 ${helpers.two_properties_shorthand(
     "scroll-padding-block",
     "scroll-padding-block-start",
     "scroll-padding-block-end",
     "specified::NonNegativeLengthPercentageOrAuto::parse",
-    products="gecko",
+    engines="gecko",
     gecko_pref="layout.css.scroll-snap-v1.enabled",
     spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-block"
 )}
 
 ${helpers.two_properties_shorthand(
     "scroll-padding-inline",
     "scroll-padding-inline-start",
     "scroll-padding-inline-end",
     "specified::NonNegativeLengthPercentageOrAuto::parse",
-    products="gecko",
+    engines="gecko",
     gecko_pref="layout.css.scroll-snap-v1.enabled",
     spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-inline"
 )}
 
--- a/servo/components/style/properties/shorthands/position.mako.rs
+++ b/servo/components/style/properties/shorthands/position.mako.rs
@@ -1,15 +1,16 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <%helpers:shorthand name="flex-flow"
+                    engines="gecko servo-2013"
                     sub_properties="flex-direction flex-wrap"
                     extra_prefixes="webkit"
                     derive_serialize="True"
                     spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property">
     use crate::properties::longhands::{flex_direction, flex_wrap};
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
@@ -39,16 +40,17 @@
         Ok(expanded! {
             flex_direction: unwrap_or_initial!(flex_direction, direction),
             flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
         })
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand name="flex"
+                    engines="gecko servo-2013"
                     sub_properties="flex-grow flex-shrink flex-basis"
                     extra_prefixes="webkit"
                     derive_serialize="True"
                     spec="https://drafts.csswg.org/css-flexbox/#flex-property">
     use crate::parser::Parse;
     use crate::values::specified::NonNegativeNumber;
     use crate::properties::longhands::flex_basis::SpecifiedValue as FlexBasis;
 
@@ -103,19 +105,23 @@
             // browsers currently agree on using `0%`. This is a spec
             // change which hasn't been adopted by browsers:
             // https://github.com/w3c/csswg-drafts/commit/2c446befdf0f686217905bdd7c92409f6bd3921b
             flex_basis: basis.unwrap_or(FlexBasis::zero_percent()),
         })
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="gap" alias="grid-gap" sub_properties="row-gap column-gap"
-                    spec="https://drafts.csswg.org/css-align-3/#gap-shorthand"
-                    products="gecko">
+<%helpers:shorthand
+    name="gap"
+    engines="gecko"
+    alias="grid-gap"
+    sub_properties="row-gap column-gap"
+    spec="https://drafts.csswg.org/css-align-3/#gap-shorthand"
+>
   use crate::properties::longhands::{row_gap, column_gap};
 
   pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                              -> Result<Longhands, ParseError<'i>> {
       let r_gap = row_gap::parse(context, input)?;
       let c_gap = input.try(|input| column_gap::parse(context, input)).unwrap_or(r_gap.clone());
 
       Ok(expanded! {
@@ -134,19 +140,22 @@
             self.column_gap.to_css(dest)
           }
       }
   }
 
 </%helpers:shorthand>
 
 % for kind in ["row", "column"]:
-<%helpers:shorthand name="grid-${kind}" sub_properties="grid-${kind}-start grid-${kind}-end"
-                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-${kind}"
-                    products="gecko">
+<%helpers:shorthand
+    name="grid-${kind}"
+    sub_properties="grid-${kind}-start grid-${kind}-end"
+    engines="gecko",
+    spec="https://drafts.csswg.org/css-grid/#propdef-grid-${kind}"
+>
     use crate::values::specified::GridLine;
     use crate::parser::Parse;
     use crate::Zero;
 
     // NOTE: Since both the shorthands have the same code, we should (re-)use code from one to implement
     // the other. This might not be a big deal for now, but we should consider looking into this in the future
     // to limit the amount of code generated.
     pub fn parse_value<'i, 't>(
@@ -176,20 +185,22 @@
             self.grid_${kind}_start.to_css(dest)?;
             dest.write_str(" / ")?;
             self.grid_${kind}_end.to_css(dest)
         }
     }
 </%helpers:shorthand>
 % endfor
 
-<%helpers:shorthand name="grid-area"
-                    sub_properties="grid-row-start grid-row-end grid-column-start grid-column-end"
-                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-area"
-                    products="gecko">
+<%helpers:shorthand
+    name="grid-area"
+    engines="gecko"
+    sub_properties="grid-row-start grid-row-end grid-column-start grid-column-end"
+    spec="https://drafts.csswg.org/css-grid/#propdef-grid-area"
+>
     use crate::values::specified::GridLine;
     use crate::parser::Parse;
     use crate::Zero;
 
     // The code is the same as `grid-{row,column}` except that this can have four values at most.
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
@@ -244,20 +255,22 @@
                 value.to_css(dest)?;
             }
 
             Ok(())
         }
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="grid-template"
-                    sub_properties="grid-template-rows grid-template-columns grid-template-areas"
-                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-template"
-                    products="gecko">
+<%helpers:shorthand
+    name="grid-template"
+    engines="gecko"
+    sub_properties="grid-template-rows grid-template-columns grid-template-areas"
+    spec="https://drafts.csswg.org/css-grid/#propdef-grid-template"
+>
     use crate::parser::Parse;
     use servo_arc::Arc;
     use crate::values::generics::grid::{TrackSize, TrackList, TrackListType};
     use crate::values::generics::grid::{TrackListValue, concat_serialize_idents};
     use crate::values::specified::{GridTemplateComponent, GenericGridTemplateComponent};
     use crate::values::specified::grid::parse_line_names;
     use crate::values::specified::position::{GridTemplateAreas, TemplateAreas, TemplateAreasArc};
 
@@ -471,21 +484,23 @@
                 self.grid_template_columns,
                 self.grid_template_areas,
                 dest
             )
         }
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="grid"
-                    sub_properties="grid-template-rows grid-template-columns grid-template-areas
-                                    grid-auto-rows grid-auto-columns grid-auto-flow"
-                    spec="https://drafts.csswg.org/css-grid/#propdef-grid"
-                    products="gecko">
+<%helpers:shorthand
+    name="grid"
+    engines="gecko"
+    sub_properties="grid-template-rows grid-template-columns grid-template-areas
+                    grid-auto-rows grid-auto-columns grid-auto-flow"
+    spec="https://drafts.csswg.org/css-grid/#propdef-grid"
+>
     use crate::parser::Parse;
     use crate::properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow};
     use crate::values::generics::grid::{GridTemplateComponent, TrackListType};
     use crate::values::specified::{GenericGridTemplateComponent, TrackSize};
     use crate::values::specified::position::{AutoFlow, GridAutoFlow, GridTemplateAreas};
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
@@ -624,19 +639,22 @@
                 dest.write_str(" / ")?;
                 self.grid_template_columns.to_css(dest)?;
             }
             Ok(())
         }
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="place-content" sub_properties="align-content justify-content"
-                    spec="https://drafts.csswg.org/css-align/#propdef-place-content"
-                    products="gecko">
+<%helpers:shorthand
+    name="place-content"
+    engines="gecko"
+    sub_properties="align-content justify-content"
+    spec="https://drafts.csswg.org/css-align/#propdef-place-content"
+>
     use crate::values::specified::align::{AlignContent, JustifyContent, ContentDistribution, AxisDirection};
 
     pub fn parse_value<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         let align_content =
             ContentDistribution::parse(input, AxisDirection::Block)?;
@@ -676,19 +694,22 @@
                 dest.write_str(" ")?;
                 self.justify_content.to_css(dest)?;
             }
             Ok(())
         }
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="place-self" sub_properties="align-self justify-self"
-                    spec="https://drafts.csswg.org/css-align/#place-self-property"
-                    products="gecko">
+<%helpers:shorthand
+    name="place-self"
+    engines="gecko"
+    sub_properties="align-self justify-self"
+    spec="https://drafts.csswg.org/css-align/#place-self-property"
+>
     use crate::values::specified::align::{AlignSelf, JustifySelf, SelfAlignment, AxisDirection};
 
     pub fn parse_value<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         let align = SelfAlignment::parse(input, AxisDirection::Block)?;
         let justify = input.try(|input| SelfAlignment::parse(input, AxisDirection::Inline));
@@ -714,19 +735,22 @@
                 dest.write_str(" ")?;
                 self.justify_self.to_css(dest)?;
             }
             Ok(())
         }
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="place-items" sub_properties="align-items justify-items"
-                    spec="https://drafts.csswg.org/css-align/#place-items-property"
-                    products="gecko">
+<%helpers:shorthand
+    name="place-items"
+    engines="gecko"
+    sub_properties="align-items justify-items"
+    spec="https://drafts.csswg.org/css-align/#place-items-property"
+>
     use crate::values::specified::align::{AlignItems, JustifyItems};
     use crate::parser::Parse;
 
     impl From<AlignItems> for JustifyItems {
         fn from(align: AlignItems) -> JustifyItems {
             JustifyItems(align.0)
         }
     }
@@ -759,27 +783,30 @@
     }
 </%helpers:shorthand>
 
 // See https://github.com/w3c/csswg-drafts/issues/3525 for the quirks stuff.
 ${helpers.four_sides_shorthand(
     "inset",
     "%s",
     "specified::LengthPercentageOrAuto::parse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-logical/#propdef-inset",
     allow_quirks="No",
 )}
 
 ${helpers.two_properties_shorthand(
     "inset-block",
     "inset-block-start",
     "inset-block-end",
     "specified::LengthPercentageOrAuto::parse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-logical/#propdef-inset-block"
 )}
 
 ${helpers.two_properties_shorthand(
     "inset-inline",
     "inset-inline-start",
     "inset-inline-end",
     "specified::LengthPercentageOrAuto::parse",
+    engines="gecko servo-2013",
     spec="https://drafts.csswg.org/css-logical/#propdef-inset-inline"
 )}
--- a/servo/components/style/properties/shorthands/svg.mako.rs
+++ b/servo/components/style/properties/shorthands/svg.mako.rs
@@ -1,15 +1,15 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
-<%helpers:shorthand name="mask" products="gecko" extra_prefixes="webkit"
+<%helpers:shorthand name="mask" engines="gecko" extra_prefixes="webkit"
                     flags="SHORTHAND_IN_GETCS"
                     sub_properties="mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x
                                     mask-position-y mask-size mask-image"
                     spec="https://drafts.fxtf.org/css-masking/#propdef-mask">
     use crate::properties::longhands::{mask_mode, mask_repeat, mask_clip, mask_origin, mask_composite, mask_position_x,
                                 mask_position_y};
     use crate::properties::longhands::{mask_size, mask_image};
     use crate::values::specified::{Position, PositionComponent};
@@ -20,17 +20,17 @@
         fn from(origin: mask_origin::single_value::SpecifiedValue) -> mask_clip::single_value::SpecifiedValue {
             match origin {
                 mask_origin::single_value::SpecifiedValue::ContentBox =>
                     mask_clip::single_value::SpecifiedValue::ContentBox,
                 mask_origin::single_value::SpecifiedValue::PaddingBox =>
                     mask_clip::single_value::SpecifiedValue::PaddingBox ,
                 mask_origin::single_value::SpecifiedValue::BorderBox =>
                     mask_clip::single_value::SpecifiedValue::BorderBox,
-                % if product == "gecko":
+                % if engine == "gecko":
                 mask_origin::single_value::SpecifiedValue::FillBox =>
                     mask_clip::single_value::SpecifiedValue::FillBox ,
                 mask_origin::single_value::SpecifiedValue::StrokeBox =>
                     mask_clip::single_value::SpecifiedValue::StrokeBox,
                 mask_origin::single_value::SpecifiedValue::ViewBox=>
                     mask_clip::single_value::SpecifiedValue::ViewBox,
                 % endif
             }
@@ -190,17 +190,17 @@
                 }
             }
 
             Ok(())
         }
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="mask-position" products="gecko" extra_prefixes="webkit"
+<%helpers:shorthand name="mask-position" engines="gecko" extra_prefixes="webkit"
                     flags="SHORTHAND_IN_GETCS"
                     sub_properties="mask-position-x mask-position-y"
                     spec="https://drafts.csswg.org/css-masks-4/#the-mask-position">
     use crate::properties::longhands::{mask_position_x,mask_position_y};
     use crate::values::specified::position::Position;
     use crate::parser::Parse;
 
     pub fn parse_value<'i, 't>(
--- a/servo/components/style/properties/shorthands/text.mako.rs
+++ b/servo/components/style/properties/shorthands/text.mako.rs
@@ -1,32 +1,32 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <%helpers:shorthand name="text-decoration"
+                    engines="gecko servo-2013"
                     flags="SHORTHAND_IN_GETCS"
                     sub_properties="text-decoration-line
-                    ${' text-decoration-style text-decoration-color' if product == 'gecko' else ''}"
+                    ${' text-decoration-style text-decoration-color' if engine == 'gecko' else ''}"
                     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration">
 
-    % if product == "gecko":
+    % if engine == "gecko":
         use crate::values::specified;
-        use crate::properties::longhands::{text_decoration_line, text_decoration_style, text_decoration_color};
-    % else:
-        use crate::properties::longhands::text_decoration_line;
+        use crate::properties::longhands::{text_decoration_style, text_decoration_color};
     % endif
+    use crate::properties::longhands::text_decoration_line;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
-        % if product == "gecko":
+        % if engine == "gecko":
             let (mut line, mut style, mut color, mut any) = (None, None, None, false);
         % else:
             let (mut line, mut any) = (None, false);
         % endif
 
         loop {
             macro_rules! parse_component {
                 ($value:ident, $module:ident) => (
@@ -37,43 +37,43 @@
                             continue;
                         }
                     }
                 )
             }
 
             parse_component!(line, text_decoration_line);
 
-            % if product == "gecko":
+            % if engine == "gecko":
                 parse_component!(style, text_decoration_style);
                 parse_component!(color, text_decoration_color);
             % endif
 
             break;
         }
 
         if !any {
             return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
 
         Ok(expanded! {
             text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
 
-            % if product == "gecko":
+            % if engine == "gecko":
                 text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
                 text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
             % endif
         })
     }
 
     impl<'a> ToCss for LonghandsToSerialize<'a>  {
         fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
             self.text_decoration_line.to_css(dest)?;
 
-            % if product == "gecko":
+            % if engine == "gecko":
                 if *self.text_decoration_style != text_decoration_style::SpecifiedValue::Solid {
                     dest.write_str(" ")?;
                     self.text_decoration_style.to_css(dest)?;
                 }
 
                 if *self.text_decoration_color != specified::Color::CurrentColor {
                     dest.write_str(" ")?;
                     self.text_decoration_color.to_css(dest)?;
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -238,17 +238,17 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
                 .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
         }
 
         if self.style.is_pseudo_element() {
             self.style
                 .add_flags(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
         }
 
-        #[cfg(feature = "servo")]
+        #[cfg(feature = "servo-layout-2013")]
         {
             if self.style.get_parent_column().is_multicol() {
                 self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED);
             }
         }
     }
 
     /// Adjust the style for text style.
--- a/servo/components/style/values/computed/text.rs
+++ b/servo/components/style/values/computed/text.rs
@@ -167,27 +167,26 @@ pub struct TextDecorationsInEffect {
     /// Whether a line-through style is in effect.
     pub line_through: bool,
 }
 
 impl TextDecorationsInEffect {
     /// Computes the text-decorations in effect for a given style.
     #[cfg(feature = "servo")]
     pub fn from_style(style: &StyleBuilder) -> Self {
-        use crate::values::computed::Display;
-
         // Start with no declarations if this is an atomic inline-level box;
         // otherwise, start with the declarations in effect and add in the text
         // decorations that this block specifies.
-        let mut result = match style.get_box().clone_display() {
-            Display::InlineBlock | Display::InlineTable => Self::default(),
-            _ => style
+        let mut result = if style.get_box().clone_display().is_atomic_inline_level() {
+            Self::default()
+        } else {
+            style
                 .get_parent_inherited_text()
                 .text_decorations_in_effect
-                .clone(),
+                .clone()
         };
 
         let line = style.get_text().clone_text_decoration_line();
 
         result.underline |= line.contains(TextDecorationLine::UNDERLINE);
         result.overline |= line.contains(TextDecorationLine::OVERLINE);
         result.line_through |= line.contains(TextDecorationLine::LINE_THROUGH);
 
--- a/servo/components/style/values/specified/border.rs
+++ b/servo/components/style/values/specified/border.rs
@@ -42,23 +42,30 @@ use style_traits::{CssWriter, ParseError
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
 #[repr(u8)]
 pub enum BorderStyle {
     Hidden,
     None,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     Inset,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     Groove,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     Outset,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     Ridge,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     Dotted,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     Dashed,
     Solid,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     Double,
 }
 
 impl BorderStyle {
     /// Whether this border style is either none or hidden.
     #[inline]
     pub fn none_or_hidden(&self) -> bool {
         matches!(*self, BorderStyle::None | BorderStyle::Hidden)
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -32,16 +32,29 @@ fn moz_display_values_enabled(context: &
 fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
     use crate::gecko_bindings::structs;
     context.in_ua_or_chrome_sheet() ||
         unsafe {
             structs::StaticPrefs::sVarCache_layout_css_xul_box_display_values_content_enabled
         }
 }
 
+#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
+fn parse_unimplemented_in_servo_2020(_context: &ParserContext) -> bool {
+    true
+}
+
+#[cfg(feature = "servo-layout-2020")]
+fn parse_unimplemented_in_servo_2020(_context: &ParserContext) -> bool {
+    servo_config::prefs::pref_map()
+        .get("layout.2020.unimplemented")
+        .as_bool()
+        .unwrap_or(false)
+}
+
 /// Defines an element’s display type, which consists of
 /// the two basic qualities of how an element generates boxes
 /// <https://drafts.csswg.org/css-display/#propdef-display>
 ///
 ///
 /// NOTE(emilio): Order is important in Gecko!
 ///
 /// If you change it, make sure to take a look at the
@@ -67,30 +80,44 @@ fn moz_box_display_values_enabled(contex
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[repr(u8)]
 pub enum Display {
     None = 0,
     Block,
     #[cfg(feature = "gecko")]
     FlowRoot,
     Inline,
+    #[parse(condition = "parse_unimplemented_in_servo_2020")]
     InlineBlock,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     ListItem,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     Table,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     InlineTable,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     TableRowGroup,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     TableColumn,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     TableColumnGroup,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     TableHeaderGroup,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     TableFooterGroup,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     TableRow,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     TableCell,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     TableCaption,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     #[parse(aliases = "-webkit-flex")]
     Flex,
+    #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
     #[parse(aliases = "-webkit-inline-flex")]
     InlineFlex,
     #[cfg(feature = "gecko")]
     Grid,
     #[cfg(feature = "gecko")]
     InlineGrid,
     #[cfg(feature = "gecko")]
     Ruby,
@@ -145,22 +172,35 @@ pub enum Display {
 
 impl Display {
     /// The initial display value.
     #[inline]
     pub fn inline() -> Self {
         Display::Inline
     }
 
+    /// <https://drafts.csswg.org/css2/visuren.html#x13>
+    #[cfg(feature = "servo")]
+    #[inline]
+    pub fn is_atomic_inline_level(&self) -> bool {
+        match *self {
+            Display::InlineBlock => true,
+            #[cfg(feature = "servo-layout-2013")]
+            Display::InlineFlex | Display::InlineTable => true,
+            _ => false,
+        }
+    }
+
     /// Returns whether this "display" value is the display of a flex or
     /// grid container.
     ///
     /// This is used to implement various style fixups.
     pub fn is_item_container(&self) -> bool {
         match *self {
+            #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
             Display::Flex | Display::InlineFlex => true,
             #[cfg(feature = "gecko")]
             Display::Grid | Display::InlineGrid => true,
             _ => false,
         }
     }
 
     /// Returns whether an element with this display type is a line
@@ -199,33 +239,35 @@ impl Display {
     }
 
     /// Convert this display into an equivalent block display.
     ///
     /// Also used for style adjustments.
     pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self {
         match *self {
             // Values that have a corresponding block-outside version.
+            #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
             Display::InlineTable => Display::Table,
+            #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
             Display::InlineFlex => Display::Flex,
 
             #[cfg(feature = "gecko")]
             Display::InlineGrid => Display::Grid,
             #[cfg(feature = "gecko")]
             Display::WebkitInlineBox => Display::WebkitBox,
 
             // Special handling for contents and list-item on the root
             // element for Gecko.
             #[cfg(feature = "gecko")]
             Display::Contents | Display::ListItem if _is_root_element => Display::Block,
 
             // These are not changed by blockification.
-            Display::None | Display::Block | Display::Flex | Display::ListItem | Display::Table => {
-                *self
-            },
+            Display::None | Display::Block => *self,
+            #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
+            Display::Flex | Display::ListItem | Display::Table => *self,
 
             #[cfg(feature = "gecko")]
             Display::Contents | Display::FlowRoot | Display::Grid | Display::WebkitBox => *self,
 
             // Everything else becomes block.
             _ => Display::Block,
         }
     }