diff --git a/reviewboard/diffviewer/opcode_generator.py b/reviewboard/diffviewer/opcode_generator.py
index ea7cddd151f890d997874798972e642631464fb6..f65ec23b5c9168644b2d77ef2def027042e7d419 100644
--- a/reviewboard/diffviewer/opcode_generator.py
+++ b/reviewboard/diffviewer/opcode_generator.py
@@ -317,6 +317,7 @@ class DiffOpcodeGenerator(object):
         i_move_cur = ij1
         i_move_range = MoveRange(i_move_cur, i_move_cur)
         r_move_ranges = {}  # key -> (start, end, group)
+        r_move_indexes_used = set()
         move_key = None
 
         is_replace = (itag == 'replace')
@@ -347,7 +348,13 @@ class DiffOpcodeGenerator(object):
                 #
                 # If there isn't any move information for this line, we'll
                 # simply add it to the move ranges.
-                for ri, rgroup, rgroup_index in self.removes.get(iline, []):
+                for ri, rgroup, rgroup_index in self.removes[iline]:
+                    # Ignore any lines that have already been processed as
+                    # part of a move, so we don't end up with incorrect blocks
+                    # of lines being matched.
+                    if ri in r_move_indexes_used:
+                        continue
+
                     r_move_range = r_move_ranges.get(move_key)
 
                     if not r_move_range or ri != r_move_range.end + 1:
@@ -481,6 +488,14 @@ class DiffOpcodeGenerator(object):
                         imeta.setdefault('moved-from', {}).update(
                             dict(zip(i_range, r_range)))
 
+                        # Record each of the positions in the removed range
+                        # as used, so that they're not factored in again when
+                        # determining possible ranges for future moves.
+                        #
+                        # We'll use the r_range above, but normalize back to
+                        # 0-based indexes.
+                        r_move_indexes_used.update(r - 1 for r in r_range)
+
                 # Reset the state for the next range.
                 move_key = None
                 i_move_range = MoveRange(i_move_cur, i_move_cur)
diff --git a/reviewboard/diffviewer/testdata/move_detection/bug-4371-new.js b/reviewboard/diffviewer/testdata/move_detection/bug-4371-new.js
new file mode 100644
index 0000000000000000000000000000000000000000..8365cf586a0ce5bd5e77bb81ac1af9a76ea38137
--- /dev/null
+++ b/reviewboard/diffviewer/testdata/move_detection/bug-4371-new.js
@@ -0,0 +1,2678 @@
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "url(#mysym)" ],
+    invalid_values: []
+  },
+  "marker-start": {
+    domProp: "markerStart",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "url(#mysym)" ],
+    invalid_values: []
+  },
+  "shape-rendering": {
+    domProp: "shapeRendering",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: [ "optimizeSpeed", "crispEdges", "geometricPrecision" ],
+    invalid_values: []
+  },
+  "stop-color": {
+    domProp: "stopColor",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    prerequisites: { "color": "blue" },
+    initial_values: [ "black", "#000", "#000000", "rgb(0,0,0)", "rgba(0,0,0,1)" ],
+    other_values: [ "green", "#fc3", "currentColor" ],
+    invalid_values: [ "url('#myserver')", "url(foo.svg#myserver)", 'url("#myserver") green', "000000", "ff00ff" ]
+  },
+  "stop-opacity": {
+    domProp: "stopOpacity",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "1", "2.8", "1.000" ],
+    other_values: [ "0", "0.3", "-7.3" ],
+    invalid_values: []
+  },
+  "stroke": {
+    domProp: "stroke",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "black", "#000", "#000000", "rgb(0,0,0)", "rgba(0,0,0,1)", "green", "#fc3", "url('#myserver')", "url(foo.svg#myserver)", 'url("#myserver") green', "currentColor", "context-fill", "context-stroke" ],
+    invalid_values: [ "000000", "ff00ff" ]
+  },
+  "stroke-dasharray": {
+    domProp: "strokeDasharray",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none", "context-value" ],
+    other_values: [ "5px,3px,2px", "5px 3px 2px", "  5px ,3px\t, 2px ", "1px", "5%", "3em" ],
+    invalid_values: [ "-5px,3px,2px", "5px,3px,-2px" ]
+  },
+  "stroke-dashoffset": {
+    domProp: "strokeDashoffset",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0", "-0px", "0em", "context-value" ],
+    other_values: [ "3px", "3%", "1em" ],
+    invalid_values: []
+  },
+  "stroke-linecap": {
+    domProp: "strokeLinecap",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "butt" ],
+    other_values: [ "round", "square" ],
+    invalid_values: []
+  },
+  "stroke-linejoin": {
+    domProp: "strokeLinejoin",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "miter" ],
+    other_values: [ "round", "bevel" ],
+    invalid_values: []
+  },
+  "stroke-miterlimit": {
+    domProp: "strokeMiterlimit",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "4" ],
+    other_values: [ "1", "7", "5000", "1.1" ],
+    invalid_values: [ "0.9", "0", "-1", "3px", "-0.3" ]
+  },
+  "stroke-opacity": {
+    domProp: "strokeOpacity",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "1", "2.8", "1.000", "context-fill-opacity", "context-stroke-opacity" ],
+    other_values: [ "0", "0.3", "-7.3" ],
+    invalid_values: []
+  },
+  "stroke-width": {
+    domProp: "strokeWidth",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "1px", "context-value" ],
+    other_values: [ "0", "0px", "-0em", "17px", "0.2em" ],
+    invalid_values: [ "-0.1px", "-3px" ]
+  },
+  "text-anchor": {
+    domProp: "textAnchor",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "start" ],
+    other_values: [ "middle", "end" ],
+    invalid_values: []
+  },
+  "text-rendering": {
+    domProp: "textRendering",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: [ "optimizeSpeed", "optimizeLegibility", "geometricPrecision" ],
+    invalid_values: []
+  },
+  "vector-effect": {
+    domProp: "vectorEffect",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "non-scaling-stroke" ],
+    invalid_values: []
+  },
+  "-moz-window-dragging": {
+    domProp: "MozWindowDragging",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "default" ],
+    other_values: [ "drag", "no-drag" ],
+    invalid_values: [ "none" ]
+  },
+  "align-content": {
+    domProp: "alignContent",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: [ "start", "end", "flex-start", "flex-end", "center", "left",
+                    "right", "space-between", "space-around", "space-evenly",
+                    "baseline", "last-baseline", "stretch", "start safe",
+                    "unsafe end", "unsafe end stretch", "end safe space-evenly" ],
+    invalid_values: [ "none", "5", "self-end", "safe", "normal unsafe", "unsafe safe",
+                      "safe baseline", "baseline unsafe", "baseline end", "end normal",
+                      "safe end unsafe start", "safe end unsafe", "normal safe start",
+                      "unsafe end start", "end start safe", "space-between unsafe",
+                      "stretch safe", "auto" ]
+  },
+  "align-items": {
+    domProp: "alignItems",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    // Can't test 'left'/'right' here since that computes to 'start' for blocks.
+    other_values: [ "end", "flex-start", "flex-end", "self-start", "self-end",
+                    "center", "stretch", "baseline", "unsafe left", "start",
+                    "center unsafe", "safe right", "center safe" ],
+    invalid_values: [ "space-between", "abc", "5%", "legacy", "legacy end",
+                      "end legacy", "unsafe", "unsafe baseline", "normal unsafe",
+                      "safe left unsafe", "safe stretch", "end end", "auto" ]
+  },
+  "align-self": {
+    domProp: "alignSelf",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    // (Assuming defaults on the parent, 'auto' will compute to 'normal'.)
+    initial_values: [ "auto", "normal" ],
+    other_values: [ "start", "flex-start", "flex-end", "center", "stretch",
+                    "baseline", "last-baseline", "right safe", "unsafe center",
+                    "self-start", "self-end safe" ],
+    invalid_values: [ "space-between", "abc", "30px", "stretch safe", "safe" ]
+  },
+  "justify-content": {
+    domProp: "justifyContent",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: [ "start", "end", "flex-start", "flex-end", "center", "left",
+                    "right", "space-between", "space-around", "space-evenly",
+                    "baseline", "last-baseline", "stretch", "start safe",
+                    "unsafe end", "unsafe end stretch", "end safe space-evenly" ],
+    invalid_values: [ "30px", "5%", "self-end", "safe", "normal unsafe", "unsafe safe",
+                      "safe baseline", "baseline unsafe", "baseline end", "normal end",
+                      "safe end unsafe start", "safe end unsafe", "normal safe start",
+                      "unsafe end start", "end start safe", "space-around unsafe",
+                      "safe stretch", "auto" ]
+  },
+  "justify-items": {
+    domProp: "justifyItems",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto", "normal" ],
+    other_values: [ "end", "flex-start", "flex-end", "self-start", "self-end",
+                    "center", "left", "right", "baseline", "stretch", "start",
+                    "legacy left", "right legacy", "legacy center",
+                    "unsafe right", "left unsafe", "safe right", "center safe" ],
+    invalid_values: [ "space-between", "abc", "30px", "legacy", "legacy start",
+                      "end legacy", "legacy baseline", "legacy legacy", "unsafe",
+                      "safe legacy left", "legacy left safe", "legacy safe left",
+                      "safe left legacy", "legacy left legacy", "baseline unsafe",
+                      "safe unsafe", "safe left unsafe", "safe stretch" ]
+  },
+  "justify-self": {
+    domProp: "justifySelf",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto", "normal" ],
+    other_values: [ "start", "end", "flex-start", "flex-end", "self-start",
+                    "self-end", "center", "left", "right", "baseline",
+                    "last-baseline", "stretch", "left unsafe", "unsafe right",
+                    "safe right", "center safe" ],
+    invalid_values: [ "space-between", "abc", "30px", "none",
+                      "legacy left", "right legacy" ]
+  },
+  "flex": {
+    domProp: "flex",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "flex-grow",
+      "flex-shrink",
+      "flex-basis"
+    ],
+    initial_values: [ "0 1 auto", "auto 0 1", "0 auto", "auto 0" ],
+    other_values: [
+      "none",
+      "1",
+      "0",
+      "0 1",
+      "0.5",
+      "1.2 3.4",
+      "0 0 0",
+      "0 0 0px",
+      "0px 0 0",
+      "5px 0 0",
+      "2 auto",
+      "auto 4",
+      "auto 5.6 7.8",
+      "-moz-max-content",
+      "1 -moz-max-content",
+      "1 2 -moz-max-content",
+      "-moz-max-content 1",
+      "-moz-max-content 1 2",
+      "-0"
+    ],
+    invalid_values: [
+      "1 2px 3",
+      "1 auto 3",
+      "1px 2 3px",
+      "1px 2 3 4px",
+      "-1",
+      "1 -1",
+      "0 1 calc(0px + rubbish)",
+    ]
+  },
+  "flex-basis": {
+    domProp: "flexBasis",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ " auto" ],
+        // NOTE: This is cribbed directly from the "width" chunk, since this
+        // property takes the exact same values as width (albeit with
+        // different semantics on 'auto').
+        // XXXdholbert (Maybe these should get separated out into
+        // a reusable array defined at the top of this file?)
+    other_values: [ "15px", "3em", "15%", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available",
+      // valid calc() values
+      "calc(-2px)",
+      "calc(2px)",
+      "calc(50%)",
+      "calc(50% + 2px)",
+      "calc( 50% + 2px)",
+      "calc(50% + 2px )",
+      "calc( 50% + 2px )",
+      "calc(50% - -2px)",
+      "calc(2px - -50%)",
+      "calc(3*25px)",
+      "calc(3 *25px)",
+      "calc(3 * 25px)",
+      "calc(3* 25px)",
+      "calc(25px*3)",
+      "calc(25px *3)",
+      "calc(25px* 3)",
+      "calc(25px * 3)",
+      "calc(3*25px + 50%)",
+      "calc(50% - 3em + 2px)",
+      "calc(50% - (3em + 2px))",
+      "calc((50% - 3em) + 2px)",
+      "calc(2em)",
+      "calc(50%)",
+      "calc(50px/2)",
+      "calc(50px/(2 - 1))"
+    ],
+    invalid_values: [ "none", "-2px",
+      // invalid calc() values
+      "calc(50%+ 2px)",
+      "calc(50% +2px)",
+      "calc(50%+2px)",
+      "-moz-min()",
+      "calc(min())",
+      "-moz-max()",
+      "calc(max())",
+      "-moz-min(5px)",
+      "calc(min(5px))",
+      "-moz-max(5px)",
+      "calc(max(5px))",
+      "-moz-min(5px,2em)",
+      "calc(min(5px,2em))",
+      "-moz-max(5px,2em)",
+      "calc(max(5px,2em))",
+      "calc(50px/(2 - 2))",
+      // If we ever support division by values, which is
+      // complicated for the reasons described in
+      // http://lists.w3.org/Archives/Public/www-style/2010Jan/0007.html
+      // , we should support all 4 of these as described in
+      // http://lists.w3.org/Archives/Public/www-style/2009Dec/0296.html
+      "calc((3em / 100%) * 3em)",
+      "calc(3em / 100% * 3em)",
+      "calc(3em * (3em / 100%))",
+      "calc(3em * 3em / 100%)"
+    ]
+  },
+  "flex-direction": {
+    domProp: "flexDirection",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "row" ],
+    other_values: [ "row-reverse", "column", "column-reverse" ],
+    invalid_values: [ "10px", "30%", "justify", "column wrap" ]
+  },
+  "flex-flow": {
+    domProp: "flexFlow",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "flex-direction",
+      "flex-wrap"
+    ],
+    initial_values: [ "row nowrap", "nowrap row", "row", "nowrap" ],
+    other_values: [
+      // only specifying one property:
+      "column",
+      "wrap",
+      "wrap-reverse",
+      // specifying both properties, 'flex-direction' first:
+      "row wrap",
+      "row wrap-reverse",
+      "column wrap",
+      "column wrap-reverse",
+      // specifying both properties, 'flex-wrap' first:
+      "wrap row",
+      "wrap column",
+      "wrap-reverse row",
+      "wrap-reverse column",
+    ],
+    invalid_values: [
+      // specifying flex-direction twice (invalid):
+      "row column",
+      "row column nowrap",
+      "row nowrap column",
+      "nowrap row column",
+      // specifying flex-wrap twice (invalid):
+      "nowrap wrap-reverse",
+      "nowrap wrap-reverse row",
+      "nowrap row wrap-reverse",
+      "row nowrap wrap-reverse",
+      // Invalid data-type / invalid keyword type:
+      "1px", "5%", "justify", "none"
+    ]
+  },
+  "flex-grow": {
+    domProp: "flexGrow",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0" ],
+    other_values: [ "3", "1", "1.0", "2.5", "123" ],
+    invalid_values: [ "0px", "-5", "1%", "3em", "stretch", "auto" ]
+  },
+  "flex-shrink": {
+    domProp: "flexShrink",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "1" ],
+    other_values: [ "3", "0", "0.0", "2.5", "123" ],
+    invalid_values: [ "0px", "-5", "1%", "3em", "stretch", "auto" ]
+  },
+  "flex-wrap": {
+    domProp: "flexWrap",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "nowrap" ],
+    other_values: [ "wrap", "wrap-reverse" ],
+    invalid_values: [ "10px", "30%", "justify", "column wrap", "auto" ]
+  },
+  "order": {
+    domProp: "order",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0" ],
+    other_values: [ "1", "99999", "-1", "-50" ],
+    invalid_values: [ "0px", "1.0", "1.", "1%", "0.2", "3em", "stretch" ]
+  },
+
+  // Aliases
+  "-moz-transform": {
+    domProp: "MozTransform",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transform",
+    subproperties: [ "transform" ],
+    // NOTE: We specify other_values & invalid_values explicitly here (instead
+    // of deferring to "transform") because we accept some legacy syntax as
+    // valid for "-moz-transform" but not for "transform".
+    other_values: [ "translatex(1px)", "translatex(4em)",
+      "translatex(-4px)", "translatex(3px)",
+      "translatex(0px) translatex(1px) translatex(2px) translatex(3px) translatex(4px)",
+      "translatey(4em)", "translate(3px)", "translate(10px, -3px)",
+      "rotate(45deg)", "rotate(45grad)", "rotate(45rad)",
+      "rotate(0.25turn)", "rotate(0)", "scalex(10)", "scaley(10)",
+      "scale(10)", "scale(10, 20)", "skewx(30deg)", "skewx(0)",
+      "skewy(0)", "skewx(30grad)", "skewx(30rad)", "skewx(0.08turn)",
+      "skewy(30deg)", "skewy(30grad)", "skewy(30rad)", "skewy(0.08turn)",
+      "rotate(45deg) scale(2, 1)", "skewx(45deg) skewx(-50grad)",
+      "translate(0, 0) scale(1, 1) skewx(0) skewy(0) matrix(1, 0, 0, 1, 0, 0)",
+      "translatex(50%)", "translatey(50%)", "translate(50%)",
+      "translate(3%, 5px)", "translate(5px, 3%)",
+      "matrix(1, 2, 3, 4, 5, 6)",
+      /* valid calc() values */
+      "translatex(calc(5px + 10%))",
+      "translatey(calc(0.25 * 5px + 10% / 3))",
+      "translate(calc(5px - 10% * 3))",
+      "translate(calc(5px - 3 * 10%), 50px)",
+      "translate(-50px, calc(5px - 10% * 3))",
+      /* valid only when prefixed */
+      "matrix(1, 2, 3, 4, 5px, 6%)",
+      "matrix(1, 2, 3, 4, 5%, 6px)",
+      "matrix(1, 2, 3, 4, 5%, 6%)",
+      "matrix(1, 2, 3, 4, 5px, 6em)",
+      "matrix(1, 0, 0, 1, calc(5px * 3), calc(10% - 3px))",
+      "translatez(1px)", "translatez(4em)", "translatez(-4px)",
+      "translatez(0px)", "translatez(2px) translatez(5px)",
+      "translate3d(3px, 4px, 5px)", "translate3d(2em, 3px, 1em)",
+      "translatex(2px) translate3d(4px, 5px, 6px) translatey(1px)",
+      "scale3d(4, 4, 4)", "scale3d(-2, 3, -7)", "scalez(4)",
+      "scalez(-6)", "rotate3d(2, 3, 4, 45deg)",
+      "rotate3d(-3, 7, 0, 12rad)", "rotatex(15deg)", "rotatey(-12grad)",
+      "rotatez(72rad)", "rotatex(0.125turn)", "perspective(1000px)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)",
+      /* valid only when prefixed */
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13px, 14em, 15px, 16)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20%, 10%, 15, 16)",
+    ],
+    invalid_values: ["1px", "#0000ff", "red", "auto",
+      "translatex(1)", "translatey(1)", "translate(2)",
+      "translate(-3, -4)",
+      "translatex(1px 1px)", "translatex(translatex(1px))",
+      "translatex(#0000ff)", "translatex(red)", "translatey()",
+      "matrix(1px, 2px, 3px, 4px, 5px, 6px)", "scale(150%)",
+      "skewx(red)", "matrix(1%, 0, 0, 0, 0px, 0px)",
+      "matrix(0, 1%, 2, 3, 4px,5px)", "matrix(0, 1, 2%, 3, 4px, 5px)",
+      "matrix(0, 1, 2, 3%, 4%, 5%)",
+      /* invalid calc() values */
+      "translatey(-moz-min(5px,10%))",
+      "translatex(-moz-max(5px,10%))",
+      "translate(10px, calc(min(5px,10%)))",
+      "translate(calc(max(5px,10%)), 10%)",
+      "matrix(1, 0, 0, 1, max(5px * 3), calc(10% - 3px))",
+      "perspective(0px)", "perspective(-10px)", "matrix3d(dinosaur)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15%, 16)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16px)",
+      "rotatey(words)", "rotatex(7)", "translate3d(3px, 4px, 1px, 7px)",
+    ],
+  },
+  "-moz-transform-origin": {
+    domProp: "MozTransformOrigin",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transform-origin",
+    subproperties: [ "transform-origin" ],
+  },
+  "-moz-perspective-origin": {
+    domProp: "MozPerspectiveOrigin",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "perspective-origin",
+    subproperties: [ "perspective-origin" ],
+  },
+  "-moz-perspective": {
+    domProp: "MozPerspective",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "perspective",
+    subproperties: [ "perspective" ],
+  },
+  "-moz-backface-visibility": {
+    domProp: "MozBackfaceVisibility",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "backface-visibility",
+    subproperties: [ "backface-visibility" ],
+  },
+  "-moz-transform-style": {
+    domProp: "MozTransformStyle",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transform-style",
+    subproperties: [ "transform-style" ],
+  },
+  "-moz-border-image": {
+    domProp: "MozBorderImage",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    alias_for: "border-image",
+    subproperties: [ "border-image-source", "border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat" ],
+  },
+  "-moz-transition": {
+    domProp: "MozTransition",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    alias_for: "transition",
+    subproperties: [ "transition-property", "transition-duration", "transition-timing-function", "transition-delay" ],
+  },
+  "-moz-transition-delay": {
+    domProp: "MozTransitionDelay",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transition-delay",
+    subproperties: [ "transition-delay" ],
+  },
+  "-moz-transition-duration": {
+    domProp: "MozTransitionDuration",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transition-duration",
+    subproperties: [ "transition-duration" ],
+  },
+  "-moz-transition-property": {
+    domProp: "MozTransitionProperty",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transition-property",
+    subproperties: [ "transition-property" ],
+  },
+  "-moz-transition-timing-function": {
+    domProp: "MozTransitionTimingFunction",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transition-timing-function",
+    subproperties: [ "transition-timing-function" ],
+  },
+  "-moz-animation": {
+    domProp: "MozAnimation",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    alias_for: "animation",
+    subproperties: [ "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-direction", "animation-fill-mode", "animation-iteration-count", "animation-play-state" ],
+  },
+  "-moz-animation-delay": {
+    domProp: "MozAnimationDelay",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-delay",
+    subproperties: [ "animation-delay" ],
+  },
+  "-moz-animation-direction": {
+    domProp: "MozAnimationDirection",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-direction",
+    subproperties: [ "animation-direction" ],
+  },
+  "-moz-animation-duration": {
+    domProp: "MozAnimationDuration",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-duration",
+    subproperties: [ "animation-duration" ],
+  },
+  "-moz-animation-fill-mode": {
+    domProp: "MozAnimationFillMode",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-fill-mode",
+    subproperties: [ "animation-fill-mode" ],
+  },
+  "-moz-animation-iteration-count": {
+    domProp: "MozAnimationIterationCount",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-iteration-count",
+    subproperties: [ "animation-iteration-count" ],
+  },
+  "-moz-animation-name": {
+    domProp: "MozAnimationName",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-name",
+    subproperties: [ "animation-name" ],
+  },
+  "-moz-animation-play-state": {
+    domProp: "MozAnimationPlayState",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-play-state",
+    subproperties: [ "animation-play-state" ],
+  },
+  "-moz-animation-timing-function": {
+    domProp: "MozAnimationTimingFunction",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-timing-function",
+    subproperties: [ "animation-timing-function" ],
+  },
+  "-moz-font-feature-settings": {
+    domProp: "MozFontFeatureSettings",
+    inherited: true,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "font-feature-settings",
+    subproperties: [ "font-feature-settings" ],
+  },
+  "-moz-font-language-override": {
+    domProp: "MozFontLanguageOverride",
+    inherited: true,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "font-language-override",
+    subproperties: [ "font-language-override" ],
+  },
+  "-moz-hyphens": {
+    domProp: "MozHyphens",
+    inherited: true,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "hyphens",
+    subproperties: [ "hyphens" ],
+  }
+}
+
+function logical_axis_prop_get_computed(cs, property)
+{
+  // Use defaults for these two properties in case the vertical text
+  // pref (which they live behind) is turned off.
+  var writingMode = cs.getPropertyValue("writing-mode") || "horizontal-tb";
+  var orientation = writingMode.substring(0, writingMode.indexOf("-"));
+
+  var mappings = {
+    "block-size":      { horizontal: "height",
+                         vertical:   "width",
+                         sideways:   "width"      },
+    "inline-size":     { horizontal: "width",
+                         vertical:   "height",
+                         sideways:   "height"     },
+    "max-block-size":  { horizontal: "max-height",
+                         vertical:   "max-width",
+                         sideways:   "max-width"  },
+    "max-inline-size": { horizontal: "max-width",
+                         vertical:   "max-height",
+                         sideways:   "max-height" },
+    "min-block-size":  { horizontal: "min-height",
+                         vertical:   "min-width",
+                         sideways:   "min-width"  },
+    "min-inline-size": { horizontal: "min-width",
+                         vertical:   "min-height",
+                         sideways:   "min-height" },
+  };
+
+  if (!mappings[property]) {
+    throw "unexpected property " + property;
+  }
+
+  var prop = mappings[property][orientation];
+  if (!prop) {
+    throw "unexpected writing mode " + writingMode;
+  }
+
+  return cs.getPropertyValue(prop);
+}
+
+function logical_box_prop_get_computed(cs, property)
+{
+  // http://dev.w3.org/csswg/css-writing-modes-3/#logical-to-physical
+
+  // Use default for writing-mode in case the vertical text
+  // pref (which it lives behind) is turned off.
+  var writingMode = cs.getPropertyValue("writing-mode") || "horizontal-tb";
+
+  var direction = cs.getPropertyValue("direction");
+
+  // keys in blockMappings are writing-mode values
+  var blockMappings = {
+    "horizontal-tb": { "start": "top",   "end": "bottom" },
+    "vertical-rl":   { "start": "right", "end": "left"   },
+    "vertical-lr":   { "start": "left",  "end": "right"  },
+    "sideways-rl":   { "start": "right", "end": "left"   },
+    "sideways-lr":   { "start": "left",  "end": "right"  },
+  };
+
+  // keys in inlineMappings are regular expressions that match against
+  // a {writing-mode,direction} pair as a space-separated string
+  var inlineMappings = {
+    "horizontal-tb ltr": { "start": "left",   "end": "right"  },
+    "horizontal-tb rtl": { "start": "right",  "end": "left"   },
+    "vertical-.. ltr":   { "start": "bottom", "end": "top"    },
+    "vertical-.. rtl":   { "start": "top",    "end": "bottom" },
+    "vertical-.. ltr":   { "start": "top",    "end": "bottom" },
+    "vertical-.. rtl":   { "start": "bottom", "end": "top"    },
+    "sideways-lr ltr":   { "start": "bottom", "end": "top"    },
+    "sideways-lr rtl":   { "start": "top",    "end": "bottom" },
+    "sideways-rl ltr":   { "start": "top",    "end": "bottom" },
+    "sideways-rl rtl":   { "start": "bottom", "end": "top"    },
+  };
+
+  var blockMapping = blockMappings[writingMode];
+  var inlineMapping;
+
+  // test each regular expression in inlineMappings against the
+  // {writing-mode,direction} pair
+  var key = `${writingMode} ${direction}`;
+  for (var k in inlineMappings) {
+    if (new RegExp(k).test(key)) {
+      inlineMapping = inlineMappings[k];
+      break;
+    }
+  }
+
+  if (!blockMapping || !inlineMapping) {
+    throw "Unexpected writing mode property values";
+  }
+
+  function physicalize(aProperty, aMapping, aLogicalPrefix) {
+    for (var logicalSide in aMapping) {
+      var physicalSide = aMapping[logicalSide];
+      logicalSide = aLogicalPrefix + logicalSide;
+      aProperty = aProperty.replace(logicalSide, physicalSide);
+    }
+    return aProperty;
+  }
+
+  if (/^-moz-/.test(property)) {
+    property = physicalize(property.substring(5), inlineMapping, "");
+  } else if (/^offset-(block|inline)-(start|end)/.test(property)) {
+    property = property.substring(7);  // we want "top" not "offset-top", e.g.
+    property = physicalize(property, blockMapping, "block-");
+    property = physicalize(property, inlineMapping, "inline-");
+  } else if (/-(block|inline)-(start|end)/.test(property)) {
+    property = physicalize(property, blockMapping, "block-");
+    property = physicalize(property, inlineMapping, "inline-");
+  } else {
+    throw "Unexpected property";
+  }
+  return cs.getPropertyValue(property);
+}
+
+// Helper to get computed style of "-webkit-box-orient" from "flex-direction"
+// and the "writing-mode".
+function webkit_orient_get_computed(cs, property)
+{
+  var writingMode = cs.getPropertyValue("writing-mode") || "horizontal-tb";
+
+  var mapping; // map from flex-direction values to -webkit-box-orient values.
+  if (writingMode == "horizontal-tb") {
+    // Horizontal writing-mode
+    mapping = { "row" : "horizontal", "column" : "vertical"};
+  } else {
+    // Vertical writing-mode
+    mapping = { "row" : "vertical",   "column" : "horizontal"};
+  }
+
+  var flexDirection = cs.getPropertyValue("flex-direction");
+  return mapping[flexDirection];
+}
+
+// Get the computed value for a property.  For shorthands, return the
+// computed values of all the subproperties, delimited by " ; ".
+function get_computed_value(cs, property)
+{
+  var info = gCSSProperties[property];
+  if (info.type == CSS_TYPE_TRUE_SHORTHAND ||
+      (info.type == CSS_TYPE_SHORTHAND_AND_LONGHAND &&
+        (property == "text-decoration" || property == "mask"))) {
+    var results = [];
+    for (var idx in info.subproperties) {
+      var subprop = info.subproperties[idx];
+      results.push(get_computed_value(cs, subprop));
+    }
+    return results.join(" ; ");
+  }
+  if (info.get_computed)
+    return info.get_computed(cs, property);
+  return cs.getPropertyValue(property);
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.touch_action.enabled")) {
+    gCSSProperties["touch-action"] = {
+        domProp: "touchAction",
+        inherited: false,
+        type: CSS_TYPE_LONGHAND,
+        initial_values: ["auto"],
+        other_values: ["none", "pan-x", "pan-y", "pan-x pan-y", "pan-y pan-x", "manipulation"],
+        invalid_values: ["zoom", "pinch", "tap", "10px", "2", "auto pan-x", "pan-x auto", "none pan-x", "pan-x none",
+                 "auto pan-y", "pan-y auto", "none pan-y", "pan-y none", "pan-x pan-x", "pan-y pan-y",
+                 "pan-x pan-y none", "pan-x none pan-y", "none pan-x pan-y", "pan-y pan-x none", "pan-y none pan-x", "none pan-y pan-x",
+                 "pan-x pan-y auto", "pan-x auto pan-y", "auto pan-x pan-y", "pan-y pan-x auto", "pan-y auto pan-x", "auto pan-y pan-x",
+                 "pan-x pan-y zoom", "pan-x zoom pan-y", "zoom pan-x pan-y", "pan-y pan-x zoom", "pan-y zoom pan-x", "zoom pan-y pan-x",
+                 "pan-x pan-y pan-x", "pan-x pan-x pan-y", "pan-y pan-x pan-x", "pan-y pan-x pan-y", "pan-y pan-y pan-x", "pan-x pan-y pan-y",
+                 "manipulation none", "none manipulation", "manipulation auto", "auto manipulation", "manipulation zoom", "zoom manipulation",
+                 "manipulation manipulation", "manipulation pan-x", "pan-x manipulation", "manipulation pan-y", "pan-y manipulation",
+                 "manipulation pan-x pan-y", "pan-x manipulation pan-y", "pan-x pan-y manipulation",
+                 "manipulation pan-y pan-x", "pan-y manipulation pan-x", "pan-y pan-x manipulation"]
+    };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.vertical-text.enabled")) {
+  var verticalTextProperties = {
+    "writing-mode": {
+      domProp: "writingMode",
+      inherited: true,
+      type: CSS_TYPE_LONGHAND,
+      initial_values: [ "horizontal-tb", "lr", "lr-tb", "rl", "rl-tb" ],
+      other_values: [ "vertical-lr", "vertical-rl", "sideways-rl", "sideways-lr", "tb", "tb-rl" ],
+      invalid_values: [ "10px", "30%", "justify", "auto", "1em" ]
+    },
+    "text-orientation": {
+      domProp: "textOrientation",
+      inherited: true,
+      type: CSS_TYPE_LONGHAND,
+      initial_values: [ "mixed" ],
+      other_values: [ "upright", "sideways", "sideways-right" ], /* sideways-right alias for backward compatibility */
+      invalid_values: [ "none", "3em", "sideways-left" ] /* sideways-left removed from CSS Writing Modes */
+    },
+    "border-block-end": {
+      domProp: "borderBlockEnd",
+      inherited: false,
+      type: CSS_TYPE_TRUE_SHORTHAND,
+      subproperties: [ "border-block-end-color", "border-block-end-style", "border-block-end-width" ],
+      initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
+      other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
+      invalid_values: [ "5%", "5", "5 solid green" ]
+    },
+    "block-size": {
+      domProp: "blockSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      /* XXX testing auto has prerequisites */
+      initial_values: [ "auto" ],
+      prerequisites: { "display": "block" },
+      other_values: [ "15px", "3em", "15%",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "none", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ],
+    },
+    "border-block-end-color": {
+      domProp: "borderBlockEndColor",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      initial_values: [ "currentColor" ],
+      other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
+      invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000" ]
+    },
+    "border-block-end-style": {
+      domProp: "borderBlockEndStyle",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* XXX hidden is sometimes the same as initial */
+      initial_values: [ "none" ],
+      other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ],
+      invalid_values: []
+    },
+    "border-block-end-width": {
+      domProp: "borderBlockEndWidth",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      prerequisites: { "border-block-end-style": "solid" },
+      initial_values: [ "medium", "3px", "calc(4px - 1px)" ],
+      other_values: [ "thin", "thick", "1px", "2em",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(0em)",
+        "calc(0px)",
+        "calc(5em)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 5em)",
+      ],
+      invalid_values: [ "5%", "5" ]
+    },
+    "border-block-start": {
+      domProp: "borderBlockStart",
+      inherited: false,
+      type: CSS_TYPE_TRUE_SHORTHAND,
+      subproperties: [ "border-block-start-color", "border-block-start-style", "border-block-start-width" ],
+      initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
+      other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
+      invalid_values: [ "5%", "5", "5 solid green" ]
+    },
+    "border-block-start-color": {
+      domProp: "borderBlockStartColor",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      initial_values: [ "currentColor" ],
+      other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
+      invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000" ]
+    },
+    "border-block-start-style": {
+      domProp: "borderBlockStartStyle",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* XXX hidden is sometimes the same as initial */
+      initial_values: [ "none" ],
+      other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ],
+      invalid_values: []
+    },
+    "border-block-start-width": {
+      domProp: "borderBlockStartWidth",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      prerequisites: { "border-block-start-style": "solid" },
+      initial_values: [ "medium", "3px", "calc(4px - 1px)" ],
+      other_values: [ "thin", "thick", "1px", "2em",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(0em)",
+        "calc(0px)",
+        "calc(5em)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 5em)",
+      ],
+      invalid_values: [ "5%", "5" ]
+    },
+    "-moz-border-end": {
+      domProp: "MozBorderEnd",
+      inherited: false,
+      type: CSS_TYPE_TRUE_SHORTHAND,
+      alias_for: "border-inline-end",
+      subproperties: [ "-moz-border-end-color", "-moz-border-end-style", "-moz-border-end-width" ],
+    },
+    "-moz-border-end-color": {
+      domProp: "MozBorderEndColor",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-end-color",
+      subproperties: [ "border-inline-end-color" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-border-end-style": {
+      domProp: "MozBorderEndStyle",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-end-style",
+      subproperties: [ "border-inline-end-style" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-border-end-width": {
+      domProp: "MozBorderEndWidth",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-end-width",
+      subproperties: [ "border-inline-end-width" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-border-start": {
+      domProp: "MozBorderStart",
+      inherited: false,
+      type: CSS_TYPE_TRUE_SHORTHAND,
+      alias_for: "border-inline-start",
+      subproperties: [ "-moz-border-start-color", "-moz-border-start-style", "-moz-border-start-width" ],
+    },
+    "-moz-border-start-color": {
+      domProp: "MozBorderStartColor",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-start-color",
+      subproperties: [ "border-inline-start-color" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-border-start-style": {
+      domProp: "MozBorderStartStyle",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-start-style",
+      subproperties: [ "border-inline-start-style" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-border-start-width": {
+      domProp: "MozBorderStartWidth",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-start-width",
+      subproperties: [ "border-inline-start-width" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "inline-size": {
+      domProp: "inlineSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      /* XXX testing auto has prerequisites */
+      initial_values: [ "auto" ],
+      prerequisites: { "display": "block" },
+      other_values: [ "15px", "3em", "15%",
+        // these three keywords compute to the initial value only when the
+        // writing mode is vertical, and we're testing with a horizontal
+        // writing mode
+        "-moz-max-content", "-moz-min-content", "-moz-fit-content",
+        // whether -moz-available computes to the initial value depends on
+        // the container size, and for the container size we're testing
+        // with, it does
+        // "-moz-available",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "none" ],
+    },
+    "margin-block-end": {
+      domProp: "marginBlockEnd",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* XXX testing auto has prerequisites */
+      initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ],
+      other_values: [ "1px", "2em", "5%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ],
+    },
+    "margin-block-start": {
+      domProp: "marginBlockStart",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* XXX testing auto has prerequisites */
+      initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ],
+      other_values: [ "1px", "2em", "5%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ],
+    },
+    "-moz-margin-end": {
+      domProp: "MozMarginEnd",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "margin-inline-end",
+      subproperties: [ "margin-inline-end" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-margin-start": {
+      domProp: "MozMarginStart",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "margin-inline-start",
+      subproperties: [ "margin-inline-start" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "max-block-size": {
+      domProp: "maxBlockSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      prerequisites: { "display": "block" },
+      initial_values: [ "none" ],
+      other_values: [ "30px", "50%",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "auto", "5", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ]
+    },
+    "max-inline-size": {
+      domProp: "maxInlineSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      prerequisites: { "display": "block" },
+      initial_values: [ "none" ],
+      other_values: [ "30px", "50%",
+        // these four keywords compute to the initial value only when the
+        // writing mode is vertical, and we're testing with a horizontal
+        // writing mode
+        "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "auto", "5" ]
+    },
+    "min-block-size": {
+      domProp: "minBlockSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      prerequisites: { "display": "block" },
+      initial_values: [ "auto", "0", "calc(0em)", "calc(-2px)", "calc(-1%)" ],
+      other_values: [ "30px", "50%",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "none", "5", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ]
+    },
+    "min-inline-size": {
+      domProp: "minInlineSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      prerequisites: { "display": "block" },
+      initial_values: [ "auto", "0", "calc(0em)", "calc(-2px)", "calc(-1%)" ],
+      other_values: [ "30px", "50%",
+        // these four keywords compute to the initial value only when the
+        // writing mode is vertical, and we're testing with a horizontal
+        // writing mode
+        "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "none", "5" ]
+    },
+    "offset-block-end": {
+      domProp: "offsetBlockEnd",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* FIXME: run tests with multiple prerequisites */
+      prerequisites: { "position": "relative" },
+      /* XXX 0 may or may not be equal to auto */
+      initial_values: [ "auto" ],
+      other_values: [ "32px", "-3em", "12%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: []
+    },
+    "offset-block-start": {
+      domProp: "offsetBlockStart",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* FIXME: run tests with multiple prerequisites */
+      prerequisites: { "position": "relative" },
+      /* XXX 0 may or may not be equal to auto */
+      initial_values: [ "auto" ],
+      other_values: [ "32px", "-3em", "12%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: []
+    },
+    "offset-inline-end": {
+      domProp: "offsetInlineEnd",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* FIXME: run tests with multiple prerequisites */
+      prerequisites: { "position": "relative" },
+      /* XXX 0 may or may not be equal to auto */
+      initial_values: [ "auto" ],
+      other_values: [ "32px", "-3em", "12%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: []
+    },
+    "offset-inline-start": {
+      domProp: "offsetInlineStart",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* FIXME: run tests with multiple prerequisites */
+      prerequisites: { "position": "relative" },
+      /* XXX 0 may or may not be equal to auto */
+      initial_values: [ "auto" ],
+      other_values: [ "32px", "-3em", "12%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: []
+    },
+    "padding-block-end": {
+      domProp: "paddingBlockEnd",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ],
+      other_values: [ "1px", "2em", "5%",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ ],
+    },
+    "padding-block-start": {
+      domProp: "paddingBlockStart",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ],
+      other_values: [ "1px", "2em", "5%",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ ],
+    },
+    "-moz-padding-end": {
+      domProp: "MozPaddingEnd",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "padding-inline-end",
+      subproperties: [ "padding-inline-end" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-padding-start": {
+      domProp: "MozPaddingStart",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "padding-inline-start",
+      subproperties: [ "padding-inline-start" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+  };
+  for (var prop in verticalTextProperties) {
+    gCSSProperties[prop] = verticalTextProperties[prop];
+  }
+  /*
+   * Vertical vs horizontal writing-mode can affect line-height
+   * because font metrics may not be symmetrical,
+   * so we require writing-mode:initial to ensure consistency
+   * in font shorthand and line-height tests.
+   */
+  ["font", "line-height"].forEach(function(prop) {
+    var p = gCSSProperties[prop];
+    if (p.prerequisites === undefined) {
+      p.prerequisites = {};
+    }
+    p.prerequisites["writing-mode"] = "initial";
+  });
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.text-combine-upright.enabled")) {
+  gCSSProperties["text-combine-upright"] = {
+    domProp: "textCombineUpright",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "all", "digits", "digits 2", "digits 3", "digits 4", "digits     3" ],
+    invalid_values: [ "auto", "all 2", "none all", "digits -3", "digits 0",
+                      "digits 12", "none 3", "digits 3.1415", "digits3", "digits 1",
+                      "digits 3 all", "digits foo", "digits all", "digits 3.0" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.masking.enabled")) {
+  gCSSProperties["mask-type"] = {
+    domProp: "maskType",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "luminance" ],
+    other_values: [ "alpha" ],
+    invalid_values: []
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("svg.paint-order.enabled")) {
+  gCSSProperties["paint-order"] = {
+    domProp: "paintOrder",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: [ "fill", "fill stroke", "fill stroke markers", "stroke markers fill" ],
+    invalid_values: [ "fill stroke markers fill", "fill normal" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("svg.transform-box.enabled")) {
+  gCSSProperties["transform-box"] = {
+    domProp: "transformBox",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "border-box" ],
+    other_values: [ "fill-box", "view-box" ],
+    invalid_values: []
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.clip-path-shapes.enabled")) {
+  gCSSProperties["clip-path"] = {
+    domProp: "clipPath",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      // SVG reference clip-path
+      "url(#my-clip-path)",
+
+      "polygon(20px 20px)",
+      "polygon(20px 20%)",
+      "polygon(20% 20%)",
+      "polygon(20rem 20em)",
+      "polygon(20cm 20mm)",
+      "polygon(20px 20px, 30px 30px)",
+      "polygon(20px 20px, 30% 30%, 30px 30px)",
+      "polygon(nonzero, 20px 20px, 30% 30%, 30px 30px)",
+      "polygon(evenodd, 20px 20px, 30% 30%, 30px 30px)",
+
+      "content-box",
+      "padding-box",
+      "border-box",
+      "margin-box",
+      "fill-box",
+      "stroke-box",
+      "view-box",
+
+      "polygon(0 0) content-box",
+      "border-box polygon(0 0)",
+      "padding-box    polygon(   0  20px ,  30px    20% )  ",
+      "polygon(evenodd, 20% 20em) content-box",
+      "polygon(evenodd, 20vh 20em) padding-box",
+      "polygon(evenodd, 20vh calc(20% + 20em)) border-box",
+      "polygon(evenodd, 20vh 20vw) margin-box",
+      "polygon(evenodd, 20pt 20cm) fill-box",
+      "polygon(evenodd, 20ex 20pc) stroke-box",
+      "polygon(evenodd, 20rem 20in) view-box",
+
+      "circle()",
+      "circle(at center)",
+      "circle(at top left 20px)",
+      "circle(at bottom right)",
+      "circle(20%)",
+      "circle(300px)",
+      "circle(calc(20px + 30px))",
+      "circle(farthest-side)",
+      "circle(closest-side)",
+      "circle(closest-side at center)",
+      "circle(farthest-side at top)",
+      "circle(20px at top right)",
+      "circle(40% at 50% 100%)",
+      "circle(calc(20% + 20%) at right bottom)",
+      "circle() padding-box",
+
+      "ellipse()",
+      "ellipse(at center)",
+      "ellipse(at top left 20px)",
+      "ellipse(at bottom right)",
+      "ellipse(20% 20%)",
+      "ellipse(300px 50%)",
+      "ellipse(calc(20px + 30px) 10%)",
+      "ellipse(farthest-side closest-side)",
+      "ellipse(closest-side farthest-side)",
+      "ellipse(farthest-side farthest-side)",
+      "ellipse(closest-side closest-side)",
+      "ellipse(closest-side closest-side at center)",
+      "ellipse(20% farthest-side at top)",
+      "ellipse(20px 50% at top right)",
+      "ellipse(closest-side 40% at 50% 100%)",
+      "ellipse(calc(20% + 20%) calc(20px + 20cm) at right bottom)",
+
+      "inset(1px)",
+      "inset(20% -20px)",
+      "inset(20em 4rem calc(20% + 20px))",
+      "inset(20vh 20vw 20pt 3%)",
+      "inset(5px round 3px)",
+      "inset(1px 2px round 3px / 3px)",
+      "inset(1px 2px 3px round 3px 2em / 20%)",
+      "inset(1px 2px 3px 4px round 3px 2vw 20% / 20px 3em 2vh 20%)",
+    ],
+    invalid_values: [
+      "url(#test) url(#tes2)",
+      "polygon (0 0)",
+      "polygon(20px, 40px)",
+      "border-box content-box",
+      "polygon(0 0) polygon(0 0)",
+      "polygon(nonzero 0 0)",
+      "polygon(evenodd 20px 20px)",
+      "polygon(20px 20px, evenodd)",
+      "polygon(20px 20px, nonzero)",
+      "polygon(0 0) conten-box content-box",
+      "content-box polygon(0 0) conten-box",
+      "padding-box polygon(0 0) conten-box",
+      "polygon(0 0) polygon(0 0) content-box",
+      "polygon(0 0) content-box polygon(0 0)",
+      "polygon(0 0), content-box",
+      "polygon(0 0), polygon(0 0)",
+      "content-box polygon(0 0) polygon(0 0)",
+      "content-box polygon(0 0) none",
+      "none content-box polygon(0 0)",
+      "inherit content-box polygon(0 0)",
+      "initial polygon(0 0)",
+      "polygon(0 0) farthest-side",
+      "farthest-corner polygon(0 0)",
+      "polygon(0 0) farthest-corner",
+      "polygon(0 0) conten-box",
+      "polygon(0 0) polygon(0 0) farthest-corner",
+      "polygon(0 0) polygon(0 0) polygon(0 0)",
+      "border-box polygon(0, 0)",
+      "border-box padding-box",
+      "margin-box farthest-side",
+      "nonsense() border-box",
+      "border-box nonsense()",
+
+      "circle(at)",
+      "circle(at 20% 20% 30%)",
+      "circle(20px 2px at center)",
+      "circle(2at center)",
+      "circle(closest-corner)",
+      "circle(at center top closest-side)",
+      "circle(-20px)",
+      "circle(farthest-side closest-side)",
+      "circle(20% 20%)",
+      "circle(at farthest-side)",
+      "circle(calc(20px + rubbish))",
+
+      "ellipse(at)",
+      "ellipse(at 20% 20% 30%)",
+      "ellipse(20px at center)",
+      "ellipse(-20px 20px)",
+      "ellipse(closest-corner farthest-corner)",
+      "ellipse(20px -20px)",
+      "ellipse(-20px -20px)",
+      "ellipse(farthest-side)",
+      "ellipse(20%)",
+      "ellipse(at farthest-side farthest-side)",
+      "ellipse(at top left calc(20px + rubbish))",
+
+      "polygon(at)",
+      "polygon(at 20% 20% 30%)",
+      "polygon(20px at center)",
+      "polygon(2px 2at center)",
+      "polygon(closest-corner farthest-corner)",
+      "polygon(at center top closest-side closest-side)",
+      "polygon(40% at 50% 100%)",
+      "polygon(40% farthest-side 20px at 50% 100%)",
+
+      "inset()",
+      "inset(round)",
+      "inset(round 3px)",
+      "inset(1px round 1px 2px 3px 4px 5px)",
+      "inset(1px 2px 3px 4px 5px)",
+      "inset(1px, round 3px)",
+      "inset(1px, 2px)",
+      "inset(1px 2px, 3px)",
+      "inset(1px at 3px)",
+      "inset(1px round 1px // 2px)",
+      "inset(1px round)",
+      "inset(1px calc(2px + rubbish))",
+      "inset(1px round 2px calc(3px + rubbish))",
+    ],
+    unbalanced_values: [
+      "polygon(30% 30%",
+      "polygon(nonzero, 20% 20px",
+      "polygon(evenodd, 20px 20px",
+
+      "circle(",
+      "circle(40% at 50% 100%",
+      "ellipse(",
+      "ellipse(40% at 50% 100%",
+
+      "inset(1px",
+      "inset(1px 2px",
+      "inset(1px 2px 3px",
+      "inset(1px 2px 3px 4px",
+      "inset(1px 2px 3px 4px round 5px",
+      "inset(1px 2px 3px 4px round 5px / 6px",
+    ]
+  };
+}
+
+
+if (IsCSSPropertyPrefEnabled("layout.css.filters.enabled")) {
+  gCSSProperties["filter"] = {
+    domProp: "filter",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      // SVG reference filters
+      "url(#my-filter)",
+      "url(#my-filter-1) url(#my-filter-2)",
+
+      // Filter functions
+      "opacity(50%) saturate(1.0)",
+      "invert(50%) sepia(0.1) brightness(90%)",
+
+      // Mixed SVG reference filters and filter functions
+      "grayscale(1) url(#my-filter-1)",
+      "url(#my-filter-1) brightness(50%) contrast(0.9)",
+
+      // Bad URLs
+      "url('badscheme:badurl')",
+      "blur(3px) url('badscheme:badurl') grayscale(50%)",
+
+      "blur(0)",
+      "blur(0px)",
+      "blur(0.5px)",
+      "blur(3px)",
+      "blur(100px)",
+      "blur(0.1em)",
+      "blur(calc(-1px))", // Parses and becomes blur(0px).
+      "blur(calc(0px))",
+      "blur(calc(5px))",
+      "blur(calc(2 * 5px))",
+
+      "brightness(0)",
+      "brightness(50%)",
+      "brightness(1)",
+      "brightness(1.0)",
+      "brightness(2)",
+      "brightness(350%)",
+      "brightness(4.567)",
+
+      "contrast(0)",
+      "contrast(50%)",
+      "contrast(1)",
+      "contrast(1.0)",
+      "contrast(2)",
+      "contrast(350%)",
+      "contrast(4.567)",
+
+      "drop-shadow(2px 2px)",
+      "drop-shadow(2px 2px 1px)",
+      "drop-shadow(2px 2px green)",
+      "drop-shadow(2px 2px 1px green)",
+      "drop-shadow(green 2px 2px)",
+      "drop-shadow(green 2px 2px 1px)",
+      "drop-shadow(currentColor 3px 3px)",
+      "drop-shadow(2px 2px calc(-5px))", /* clamped */
+      "drop-shadow(calc(3em - 2px) 2px green)",
+      "drop-shadow(green calc(3em - 2px) 2px)",
+      "drop-shadow(2px calc(2px + 0.2em))",
+      "drop-shadow(blue 2px calc(2px + 0.2em))",
+      "drop-shadow(2px calc(2px + 0.2em) blue)",
+      "drop-shadow(calc(-2px) calc(-2px))",
+      "drop-shadow(-2px -2px)",
+      "drop-shadow(calc(2px) calc(2px))",
+      "drop-shadow(calc(2px) calc(2px) calc(2px))",
+
+      "grayscale(0)",
+      "grayscale(50%)",
+      "grayscale(1)",
+      "grayscale(1.0)",
+      "grayscale(2)",
+      "grayscale(350%)",
+      "grayscale(4.567)",
+
+      "hue-rotate(0deg)",
+      "hue-rotate(90deg)",
+      "hue-rotate(540deg)",
+      "hue-rotate(-90deg)",
+      "hue-rotate(10grad)",
+      "hue-rotate(1.6rad)",
+      "hue-rotate(-1.6rad)",
+      "hue-rotate(0.5turn)",
+      "hue-rotate(-2turn)",
+
+      "invert(0)",
+      "invert(50%)",
+      "invert(1)",
+      "invert(1.0)",
+      "invert(2)",
+      "invert(350%)",
+      "invert(4.567)",
+
+      "opacity(0)",
+      "opacity(50%)",
+      "opacity(1)",
+      "opacity(1.0)",
+      "opacity(2)",
+      "opacity(350%)",
+      "opacity(4.567)",
+
+      "saturate(0)",
+      "saturate(50%)",
+      "saturate(1)",
+      "saturate(1.0)",
+      "saturate(2)",
+      "saturate(350%)",
+      "saturate(4.567)",
+
+      "sepia(0)",
+      "sepia(50%)",
+      "sepia(1)",
+      "sepia(1.0)",
+      "sepia(2)",
+      "sepia(350%)",
+      "sepia(4.567)",
+    ],
+    invalid_values: [
+      // none
+      "none none",
+      "url(#my-filter) none",
+      "none url(#my-filter)",
+      "blur(2px) none url(#my-filter)",
+
+      // Nested filters
+      "grayscale(invert(1.0))",
+
+      // Comma delimited filters
+      "url(#my-filter),",
+      "invert(50%), url(#my-filter), brightness(90%)",
+
+      // Test the following situations for each filter function:
+      // - Invalid number of arguments
+      // - Comma delimited arguments
+      // - Wrong argument type
+      // - Argument value out of range
+      "blur()",
+      "blur(3px 5px)",
+      "blur(3px,)",
+      "blur(3px, 5px)",
+      "blur(#my-filter)",
+      "blur(0.5)",
+      "blur(50%)",
+      "blur(calc(0))", // Unitless zero in calc is not a valid length.
+      "blur(calc(0.1))",
+      "blur(calc(10%))",
+      "blur(calc(20px - 5%))",
+      "blur(-3px)",
+
+      "brightness()",
+      "brightness(0.5 0.5)",
+      "brightness(0.5,)",
+      "brightness(0.5, 0.5)",
+      "brightness(#my-filter)",
+      "brightness(10px)",
+      "brightness(-1)",
+
+      "contrast()",
+      "contrast(0.5 0.5)",
+      "contrast(0.5,)",
+      "contrast(0.5, 0.5)",
+      "contrast(#my-filter)",
+      "contrast(10px)",
+      "contrast(-1)",
+
+      "drop-shadow()",
+      "drop-shadow(3% 3%)",
+      "drop-shadow(2px 2px -5px)",
+      "drop-shadow(2px 2px 2px 2px)",
+      "drop-shadow(2px 2px, none)",
+      "drop-shadow(none, 2px 2px)",
+      "drop-shadow(inherit, 2px 2px)",
+      "drop-shadow(2px 2px, inherit)",
+      "drop-shadow(2 2px)",
+      "drop-shadow(2px 2)",
+      "drop-shadow(2px 2px 2)",
+      "drop-shadow(2px 2px 2px 2)",
+      "drop-shadow(calc(2px) calc(2px) calc(2px) calc(2px))",
+      "drop-shadow(green 2px 2px, blue 1px 3px 4px)",
+      "drop-shadow(blue 2px 2px, currentColor 1px 2px)",
+
+      "grayscale()",
+      "grayscale(0.5 0.5)",
+      "grayscale(0.5,)",
+      "grayscale(0.5, 0.5)",
+      "grayscale(#my-filter)",
+      "grayscale(10px)",
+      "grayscale(-1)",
+
+      "hue-rotate()",
+      "hue-rotate(0)",
+      "hue-rotate(0.5 0.5)",
+      "hue-rotate(0.5,)",
+      "hue-rotate(0.5, 0.5)",
+      "hue-rotate(#my-filter)",
+      "hue-rotate(10px)",
+      "hue-rotate(-1)",
+      "hue-rotate(45deg,)",
+
+      "invert()",
+      "invert(0.5 0.5)",
+      "invert(0.5,)",
+      "invert(0.5, 0.5)",
+      "invert(#my-filter)",
+      "invert(10px)",
+      "invert(-1)",
+
+      "opacity()",
+      "opacity(0.5 0.5)",
+      "opacity(0.5,)",
+      "opacity(0.5, 0.5)",
+      "opacity(#my-filter)",
+      "opacity(10px)",
+      "opacity(-1)",
+
+      "saturate()",
+      "saturate(0.5 0.5)",
+      "saturate(0.5,)",
+      "saturate(0.5, 0.5)",
+      "saturate(#my-filter)",
+      "saturate(10px)",
+      "saturate(-1)",
+
+      "sepia()",
+      "sepia(0.5 0.5)",
+      "sepia(0.5,)",
+      "sepia(0.5, 0.5)",
+      "sepia(#my-filter)",
+      "sepia(10px)",
+      "sepia(-1)",
+    ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.grid.enabled")) {
+  var isGridTemplateSubgridValueEnabled =
+    IsCSSPropertyPrefEnabled("layout.css.grid-template-subgrid-value.enabled");
+
+  gCSSProperties["display"].other_values.push("grid", "inline-grid");
+  gCSSProperties["grid-auto-flow"] = {
+    domProp: "gridAutoFlow",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "row" ],
+    other_values: [
+      "column",
+      "column dense",
+      "row dense",
+      "dense column",
+      "dense row",
+      "dense",
+    ],
+    invalid_values: [
+      "",
+      "auto",
+      "none",
+      "10px",
+      "column row",
+      "dense row dense",
+    ]
+  };
+
+  gCSSProperties["grid-auto-columns"] = {
+    domProp: "gridAutoColumns",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: [
+      "40px",
+      "2em",
+      "2.5fr",
+      "12%",
+      "min-content",
+      "max-content",
+      "calc(20px + 10%)",
+      "minmax(20px, max-content)",
+      "minmax(min-content, auto)",
+      "minmax(auto, max-content)",
+      "m\\69nmax(20px, 4Fr)",
+      "MinMax(min-content, calc(20px + 10%))",
+    ],
+    invalid_values: [
+      "",
+      "normal",
+      "40ms",
+      "-40px",
+      "-12%",
+      "-2em",
+      "-2.5fr",
+      "minmax()",
+      "minmax(20px)",
+      "mİnmax(20px, 100px)",
+      "minmax(20px, 100px, 200px)",
+      "maxmin(100px, 20px)",
+      "minmax(min-content, minmax(30px, max-content))",
+    ]
+  };
+  gCSSProperties["grid-auto-rows"] = {
+    domProp: "gridAutoRows",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: gCSSProperties["grid-auto-columns"].initial_values,
+    other_values: gCSSProperties["grid-auto-columns"].other_values,
+    invalid_values: gCSSProperties["grid-auto-columns"].invalid_values
+  };
+
+  gCSSProperties["grid-template-columns"] = {
+    domProp: "gridTemplateColumns",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      "auto",
+      "40px",
+      "2.5fr",
+      "[normal] 40px [] auto [ ] 12%",
+      "[foo] 40px min-content [ bar ] calc(20px + 10%) max-content",
+      "40px min-content calc(20px + 10%) max-content",
+      "minmax(min-content, auto)",
+      "minmax(auto, max-content)",
+      "m\\69nmax(20px, 4Fr)",
+      "40px MinMax(min-content, calc(20px + 10%)) max-content",
+      "40px 2em",
+      "[] 40px [-foo] 2em [bar baz This\ is\ one\ ident]",
+      // TODO bug 978478: "[a] repeat(3, [b] 20px [c] 40px [d]) [e]",
+      "repeat(1, 20px)",
+      "repeat(1, [a] 20px)",
+      "[a] Repeat(4, [a] 20px [] auto [b c]) [d]",
+      "[a] 2.5fr Repeat(4, [a] 20px [] auto [b c]) [d]",
+      "[a] 2.5fr [z] Repeat(4, [a] 20px [] auto [b c]) [d]",
+      "[a] 2.5fr [z] Repeat(4, [a] 20px [] auto) [d]",
+      "[a] 2.5fr [z] Repeat(4, 20px [b c] auto [b c]) [d]",
+      "[a] 2.5fr [z] Repeat(4, 20px auto) [d]",
+      "repeat(auto-fill, 0)",
+      "[a] repeat( Auto-fill,1%)",
+      "[a] repeat(Auto-fit, 0)",
+      "repeat(Auto-fit,[] 1%)",
+      "repeat(auto-fit, [a] 1em) auto",
+      "[a] repeat( auto-fit,[a b] minmax(0,0) )",
+      "[a] 40px repeat(auto-fit,[a b] minmax(1px, 0) [])",
+      "[a] auto [b] repeat(auto-fit,[a b] minmax(1mm, 1%) [c]) [c] auto",
+    ],
+    invalid_values: [
+      "",
+      "normal",
+      "40ms",
+      "-40px",
+      "-12%",
+      "-2fr",
+      "(foo)",
+      "(inherit) 40px",
+      "(initial) 40px",
+      "(unset) 40px",
+      "(default) 40px",
+      "(6%) 40px",
+      "(5th) 40px",
+      "(foo() bar) 40px",
+      "(foo)) 40px",
+      "(foo) 40px",
+      "(foo) (bar) 40px",
+      "40px (foo) (bar)",
+      "minmax()",
+      "minmax(20px)",
+      "mİnmax(20px, 100px)",
+      "minmax(20px, 100px, 200px)",
+      "maxmin(100px, 20px)",
+      "minmax(min-content, minmax(30px, max-content))",
+      "repeat(0, 20px)",
+      "repeat(-3, 20px)",
+      "rêpeat(1, 20px)",
+      "repeat(1)",
+      "repeat(1, )",
+      "repeat(3px, 20px)",
+      "repeat(2.0, 20px)",
+      "repeat(2.5, 20px)",
+      "repeat(2, (foo))",
+      "repeat(2, foo)",
+      "40px calc(0px + rubbish)",
+      "repeat(1, repeat(1, 20px))",
+      "repeat(auto-fill, auto)",
+      "repeat(auto-fit,auto)",
+      "repeat(auto-fit,[])",
+      "repeat(auto-fill, 0) repeat(auto-fit, 0) ",
+      "repeat(auto-fit, 0) repeat(auto-fill, 0) ",
+      "[a] repeat(auto-fit, 0) repeat(auto-fit, 0) ",
+      "[a] repeat(auto-fill, 0) [a] repeat(auto-fill, 0) ",
+      "repeat(auto-fill, 0 0)",
+      "repeat(auto-fill, 0 [] 0)",
+      "repeat(auto-fill, min-content)",
+      "repeat(auto-fit,max-content)",
+      "repeat(auto-fit,minmax(auto,auto))",
+      "repeat(auto-fit,[] minmax(1px, min-content))",
+      "repeat(auto-fit,[a] minmax(1%, auto) [])",
+    ],
+    unbalanced_values: [
+      "(foo] 40px",
+    ]
+  };
+  if (isGridTemplateSubgridValueEnabled) {
+    gCSSProperties["grid-template-columns"].other_values.push(
+      // See https://bugzilla.mozilla.org/show_bug.cgi?id=981300
+      "[none auto subgrid min-content max-content foo] 40px",
+
+      "subgrid",
+      "subgrid [] [foo bar]",
+      "subgrid repeat(1, [])",
+      "subgrid Repeat(4, [a] [b c] [] [d])",
+      "subgrid repeat(auto-fill, [])",
+      "subgrid [x] repeat( Auto-fill, [a b c]) []",
+      "subgrid [x] repeat(auto-fill, []) [y z]"
+    );
+    gCSSProperties["grid-template-columns"].invalid_values.push(
+      "subgrid (foo) 40px",
+      "subgrid (foo 40px)",
+      "(foo) subgrid",
+      "subgrid rêpeat(1, ())",
+      "subgrid repeat(0, ())",
+      "subgrid repeat(-3, ())",
+      "subgrid repeat(2.0, ())",
+      "subgrid repeat(2.5, ())",
+      "subgrid repeat(3px, ())",
+      "subgrid repeat(1)",
+      "subgrid repeat(1, )",
+      "subgrid repeat(2, (40px))",
+      "subgrid repeat(2, foo)",
+      "subgrid repeat(1, repeat(1, []))",
+      "subgrid repeat(auto-fit,[])",
+      "subgrid [] repeat(auto-fit,[])",
+      "subgrid [a] repeat(auto-fit,[])",
+      "subgrid repeat(auto-fill, 1px)",
+      "subgrid repeat(auto-fill, 1px [])",
+      "subgrid repeat(Auto-fill, [a] [b c] [] [d])",
+      "subgrid repeat(auto-fill, []) repeat(auto-fill, [])"
+    );
+  }
+  gCSSProperties["grid-template-rows"] = {
+    domProp: "gridTemplateRows",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: gCSSProperties["grid-template-columns"].initial_values,
+    other_values: gCSSProperties["grid-template-columns"].other_values,
+    invalid_values: gCSSProperties["grid-template-columns"].invalid_values
+  };
+  gCSSProperties["grid-template-areas"] = {
+    domProp: "gridTemplateAreas",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      "''",
+      "'' ''",
+      "'1a-é_ .' \"b .\"",
+      "' Z\t\\aZ' 'Z Z'",
+      " '. . a b'  '. .a b' ",
+      "'a.b' '. . .'",
+      "'.' '..'",
+      "'...' '.'",
+      "'...-blah' '. .'",
+      "'.. ..' '.. ...'",
+    ],
+    invalid_values: [
+      "'a b' 'a/b'",
+      "'a . a'",
+      "'. a a' 'a a a'",
+      "'a a .' 'a a a'",
+      "'a a' 'a .'",
+      "'a a'\n'..'\n'a a'",
+    ]
+  };
+
+  gCSSProperties["grid-template"] = {
+    domProp: "gridTemplate",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "grid-template-areas",
+      "grid-template-rows",
+      "grid-template-columns",
+    ],
+    initial_values: [
+      "none",
+      "none / none",
+    ],
+    other_values: [
+      // <'grid-template-rows'> / <'grid-template-columns'>
+      "40px / 100px",
+      "[foo] 40px [bar] / [baz] 100px [fizz]",
+      " none/100px",
+      "40px/none",
+      // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
+      "'fizz'",
+      "[bar] 'fizz'",
+      "'fizz' / [foo] 40px",
+      "[bar] 'fizz' / [foo] 40px",
+      "'fizz' 100px / [foo] 40px",
+      "[bar] 'fizz' 100px / [foo] 40px",
+      "[bar] 'fizz' 100px [buzz] / [foo] 40px",
+      "[bar] 'fizz' 100px [buzz] \n [a] '.' 200px [b] / [foo] 40px",
+    ],
+    invalid_values: [
+      "[foo] [bar] 40px / 100px",
+      "[fizz] [buzz] 100px / 40px",
+      "[fizz] [buzz] 'foo' / 40px",
+      "'foo' / none"
+    ]
+  };
+  if (isGridTemplateSubgridValueEnabled) {
+    gCSSProperties["grid-template"].other_values.push(
+      "subgrid",
+      "subgrid/40px 20px",
+      "subgrid [foo] [] [bar baz] / 40px 20px",
+      "40px 20px/subgrid",
+      "40px 20px/subgrid  [foo] [] repeat(3, [a] [b]) [bar baz]",
+      "subgrid/subgrid",
+      "subgrid [foo] [] [bar baz]/subgrid [foo] [] [bar baz]"
+    );
+    gCSSProperties["grid-template"].invalid_values.push(
+      "subgrid []",
+      "subgrid [] / 'fizz'",
+      "subgrid / 'fizz'"
+    );
+  }
+
+  gCSSProperties["grid"] = {
+    domProp: "grid",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "grid-template-areas",
+      "grid-template-rows",
+      "grid-template-columns",
+      "grid-auto-flow",
+      "grid-auto-rows",
+      "grid-auto-columns",
+      "grid-column-gap",
+      "grid-row-gap",
+    ],
+    initial_values: [
+      "none",
+      "none / none",
+    ],
+    other_values: [
+      "column 40px",
+      "column dense auto",
+      "dense row minmax(min-content, 2fr)",
+      "row 40px / 100px",
+    ].concat(
+      gCSSProperties["grid-template"].other_values,
+      gCSSProperties["grid-auto-flow"].other_values
+    ),
+    invalid_values: [
+      "row column 40px",
+      "row -20px",
+      "row 200ms",
+      "row 40px 100px",
+    ].concat(
+      gCSSProperties["grid-template"].invalid_values,
+      gCSSProperties["grid-auto-flow"].invalid_values
+        .filter((v) => v != 'none')
+    )
+  };
+
+  var gridLineOtherValues = [
+    "foo",
+    "2",
+    "2 foo",
+    "foo 2",
+    "-3",
+    "-3 bar",
+    "bar -3",
+    "span 2",
+    "2 span",
+    "span foo",
+    "foo span",
+    "span 2 foo",
+    "span foo 2",
+    "2 foo span",
+    "foo 2 span",
+  ];
+  var gridLineInvalidValues = [
+    "",
+    "4th",
+    "span",
+    "inherit 2",
+    "2 inherit",
+    "20px",
+    "2 3",
+    "2.5",
+    "2.0",
+    "0",
+    "0 foo",
+    "span 0",
+    "2 foo 3",
+    "foo 2 foo",
+    "2 span foo",
+    "foo span 2",
+    "span -3",
+    "span -3 bar",
+    "span 2 span",
+    "span foo span",
+    "span 2 foo span",
+  ];
+
+  gCSSProperties["grid-column-start"] = {
+    domProp: "gridColumnStart",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: gridLineOtherValues,
+    invalid_values: gridLineInvalidValues
+  };
+  gCSSProperties["grid-column-end"] = {
+    domProp: "gridColumnEnd",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: gridLineOtherValues,
+    invalid_values: gridLineInvalidValues
+  };
+  gCSSProperties["grid-row-start"] = {
+    domProp: "gridRowStart",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: gridLineOtherValues,
+    invalid_values: gridLineInvalidValues
+  };
+  gCSSProperties["grid-row-end"] = {
+    domProp: "gridRowEnd",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: gridLineOtherValues,
+    invalid_values: gridLineInvalidValues
+  };
+
+  // The grid-column and grid-row shorthands take values of the form
+  //   <grid-line> [ / <grid-line> ]?
+  var gridColumnRowOtherValues = [].concat(gridLineOtherValues);
+  gridLineOtherValues.concat([ "auto" ]).forEach(function(val) {
+    gridColumnRowOtherValues.push(" foo / " + val);
+    gridColumnRowOtherValues.push(val + "/2");
+  });
+  var gridColumnRowInvalidValues = [
+    "foo, bar",
+    "foo / bar / baz",
+  ].concat(gridLineInvalidValues);
+  gridLineInvalidValues.forEach(function(val) {
+    gridColumnRowInvalidValues.push("span 3 / " + val);
+    gridColumnRowInvalidValues.push(val + " / foo");
+  });
+  gCSSProperties["grid-column"] = {
+    domProp: "gridColumn",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "grid-column-start",
+      "grid-column-end"
+    ],
+    initial_values: [ "auto", "auto / auto" ],
+    other_values: gridColumnRowOtherValues,
+    invalid_values: gridColumnRowInvalidValues
+  };
+  gCSSProperties["grid-row"] = {
+    domProp: "gridRow",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "grid-row-start",
+      "grid-row-end"
+    ],
+    initial_values: [ "auto", "auto / auto" ],
+    other_values: gridColumnRowOtherValues,
+    invalid_values: gridColumnRowInvalidValues
+  };
+
+  var gridAreaOtherValues = gridLineOtherValues.slice();
+  gridLineOtherValues.forEach(function(val) {
+    gridAreaOtherValues.push("foo / " + val);
+    gridAreaOtherValues.push(val + "/2/3");
+    gridAreaOtherValues.push("foo / bar / " + val + " / baz");
+  });
+  var gridAreaInvalidValues = [
+    "foo, bar",
+    "foo / bar / baz / fizz / buzz",
+    "default / foo / bar / baz",
+    "foo / initial / bar / baz",
+    "foo / bar / inherit / baz",
+    "foo / bar / baz / unset",
+  ].concat(gridLineInvalidValues);
+  gridLineInvalidValues.forEach(function(val) {
+    gridAreaInvalidValues.push("foo / " + val);
+    gridAreaInvalidValues.push("foo / bar / " + val);
+    gridAreaInvalidValues.push("foo / 4 / bar / " + val);
+  });
+
+  gCSSProperties["grid-area"] = {
+    domProp: "gridArea",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "grid-row-start",
+      "grid-column-start",
+      "grid-row-end",
+      "grid-column-end"
+    ],
+    initial_values: [
+      "auto",
+      "auto / auto",
+      "auto / auto / auto",
+      "auto / auto / auto / auto"
+    ],
+    other_values: gridAreaOtherValues,
+    invalid_values: gridAreaInvalidValues
+  };
+
+  gCSSProperties["grid-column-gap"] = {
+    domProp: "gridColumnGap",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0" ],
+    other_values: [ "2px", "1em", "calc(1px + 1em)" ],
+    invalid_values: [ "-1px", "2%", "auto", "none", "1px 1px", "calc(1%)" ],
+  };
+  gCSSProperties["grid-row-gap"] = {
+    domProp: "gridRowGap",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0" ],
+    other_values: [ "2px", "1em", "calc(1px + 1em)" ],
+    invalid_values: [ "-1px", "2%", "auto", "none", "1px 1px", "calc(1%)" ],
+  };
+  gCSSProperties["grid-gap"] = {
+    domProp: "gridGap",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "grid-column-gap", "grid-row-gap" ],
+    initial_values: [ "0", "0 0" ],
+    other_values: [ "1ch 0", "1em 1px", "calc(1px + 1ch)" ],
+    invalid_values: [ "-1px", "1px -1px", "1px 1px 1px", "inherit 1px",
+                      "1px 1%", "1px auto" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.display-contents.enabled")) {
+  gCSSProperties["display"].other_values.push("contents");
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.contain.enabled")) {
+  gCSSProperties["contain"] = {
+    domProp: "contain",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      "strict",
+      "layout",
+      "style",
+      "layout style",
+      "style layout",
+      "paint",
+      "layout paint",
+      "paint layout",
+      "style paint",
+      "paint style",
+      "layout style paint",
+      "layout paint style",
+      "style paint layout",
+      "paint style layout",
+    ],
+    invalid_values: [
+      "none strict",
+      "strict layout",
+      "strict layout style",
+      "layout strict",
+      "layout style strict",
+      "layout style paint strict",
+      "paint strict",
+      "style strict",
+      "paint paint",
+      "strict strict",
+      "auto",
+      "10px",
+      "0",
+    ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.image-orientation.enabled")) {
+  gCSSProperties["image-orientation"] = {
+    domProp: "imageOrientation",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [
+      "0deg",
+      "0grad",
+      "0rad",
+      "0turn",
+
+      // Rounded initial values.
+      "-90deg",
+      "15deg",
+      "360deg",
+    ],
+    other_values: [
+      "0deg flip",
+      "90deg",
+      "90deg flip",
+      "180deg",
+      "180deg flip",
+      "270deg",
+      "270deg flip",
+      "flip",
+      "from-image",
+
+      // Grad units.
+      "0grad flip",
+      "100grad",
+      "100grad flip",
+      "200grad",
+      "200grad flip",
+      "300grad",
+      "300grad flip",
+
+      // Radian units.
+      "0rad flip",
+      "1.57079633rad",
+      "1.57079633rad flip",
+      "3.14159265rad",
+      "3.14159265rad flip",
+      "4.71238898rad",
+      "4.71238898rad flip",
+
+      // Turn units.
+      "0turn flip",
+      "0.25turn",
+      "0.25turn flip",
+      "0.5turn",
+      "0.5turn flip",
+      "0.75turn",
+      "0.75turn flip",
+
+      // Rounded values.
+      "-45deg flip",
+      "65deg flip",
+      "400deg flip",
+    ],
+    invalid_values: [
+      "none",
+      "0deg none",
+      "flip 0deg",
+      "flip 0deg",
+      "0",
+      "0 flip",
+      "flip 0",
+      "0deg from-image",
+      "from-image 0deg",
+      "flip from-image",
+      "from-image flip",
+    ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.osx-font-smoothing.enabled")) {
+  gCSSProperties["-moz-osx-font-smoothing"] = {
+    domProp: "MozOsxFontSmoothing",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: [ "grayscale" ],
+    invalid_values: [ "none", "subpixel-antialiased", "antialiased" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.sticky.enabled")) {
+  gCSSProperties["position"].other_values.push("sticky");
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.mix-blend-mode.enabled")) {
+  gCSSProperties["mix-blend-mode"] = {
+    domProp: "mixBlendMode",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: ["multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn",
+        "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"],
+    invalid_values: []
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.isolation.enabled")) {
+  gCSSProperties["isolation"] = {
+    domProp: "isolation",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: ["isolate"],
+    invalid_values: []
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.background-blend-mode.enabled")) {
+  gCSSProperties["background-blend-mode"] = {
+    domProp: "backgroundBlendMode",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: [ "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn",
+      "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity" ],
+    invalid_values: ["none", "10px", "multiply multiply"]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.object-fit-and-position.enabled")) {
+  gCSSProperties["object-fit"] = {
+    domProp: "objectFit",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "fill" ],
+    other_values: [ "contain", "cover", "none", "scale-down" ],
+    invalid_values: [ "auto", "5px", "100%" ]
+  };
+  gCSSProperties["object-position"] = {
+    domProp: "objectPosition",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "50% 50%", "50%", "center", "center center" ],
+    other_values: [
+      "calc(20px)",
+      "calc(20px) 10px",
+      "10px calc(20px)",
+      "calc(20px) 25%",
+      "25% calc(20px)",
+      "calc(20px) calc(20px)",
+      "calc(20px + 1em) calc(20px / 2)",
+      "calc(20px + 50%) calc(50% - 10px)",
+      "calc(-20px) calc(-50%)",
+      "calc(-20%) calc(-50%)",
+      "0px 0px",
+      "right 20px top 60px",
+      "right 20px bottom 60px",
+      "left 20px top 60px",
+      "left 20px bottom 60px",
+      "right -50px top -50px",
+      "left -50px bottom -50px",
+      "right 20px top -50px",
+      "right -20px top 50px",
+      "right 3em bottom 10px",
+      "bottom 3em right 10px",
+      "top 3em right 10px",
+      "left 15px",
+      "10px top",
+      "left top 15px",
+      "left 10px top",
+      "left 20%",
+      "right 20%"
+    ],
+    invalid_values: [ "center 10px center 4px", "center 10px center",
+                      "top 20%", "bottom 20%", "50% left", "top 50%",
+                      "50% bottom 10%", "right 10% 50%", "left right",
+                      "top bottom", "left 10% right",
+                      "top 20px bottom 20px", "left left", "20 20" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.overflow-clip-box.enabled")) {
+  gCSSProperties["overflow-clip-box"] = {
+    domProp: "overflowClipBox",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "padding-box" ],
+    other_values: [ "content-box" ],
+    invalid_values: [ "none", "auto", "border-box", "0" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.box-decoration-break.enabled")) {
+  gCSSProperties["box-decoration-break"] = {
+    domProp: "boxDecorationBreak",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "slice" ],
+    other_values: [ "clone" ],
+    invalid_values: [ "auto",  "none",  "1px" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.scroll-behavior.property-enabled")) {
+  gCSSProperties["scroll-behavior"] = {
+    domProp: "scrollBehavior",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: [ "smooth" ],
+    invalid_values: [ "none",  "1px" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.scroll-snap.enabled")) {
+  gCSSProperties["scroll-snap-coordinate"] = {
+    domProp: "scrollSnapCoordinate",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "25% 25%", "top", "0px 100px, 10em 50%",
+                    "top left, top right, bottom left, bottom right, center",
+                    "calc(2px)",
+                    "calc(50%)",
+                    "calc(3*25px)",
+                    "calc(3*25px) 5px",
+                    "5px calc(3*25px)",
+                    "calc(20%) calc(3*25px)",
+                    "calc(25px*3)",
+                    "calc(3*25px + 50%)",
+                    "calc(20%) calc(3*25px), center"],
+    invalid_values: [ "auto", "default" ]
+  }
+  gCSSProperties["scroll-snap-destination"] = {
+    domProp: "scrollSnapDestination",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0px 0px" ],
+    other_values: [ "25% 25%", "6px 5px", "20% 3em", "0in 1in",
+                    "top", "right", "top left", "top right", "center",
+                    "calc(2px)",
+                    "calc(50%)",
+                    "calc(3*25px)",
+                    "calc(3*25px) 5px",
+                    "5px calc(3*25px)",
+                    "calc(20%) calc(3*25px)",
+                    "calc(25px*3)",
+                    "calc(3*25px + 50%)"],
+    invalid_values: [ "auto", "none", "default" ]
+  }
+  gCSSProperties["scroll-snap-points-x"] = {
+    domProp: "scrollSnapPointsX",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "repeat(100%)", "repeat(120px)", "repeat(calc(3*25px))" ],
+    invalid_values: [ "auto", "1px", "left", "rgb(1,2,3)" ]
+  }
+  gCSSProperties["scroll-snap-points-y"] = {
+    domProp: "scrollSnapPointsY",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "repeat(100%)", "repeat(120px)", "repeat(calc(3*25px))" ],
+    invalid_values: [ "auto", "1px", "top", "rgb(1,2,3)" ]
+  }
+  gCSSProperties["scroll-snap-type"] = {
+    domProp: "scrollSnapType",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "scroll-snap-type-x", "scroll-snap-type-y" ],
+    initial_values: [ "none" ],
+    other_values: [ "mandatory", "proximity" ],
+    invalid_values: [ "auto",  "1px" ]
+  };
+  gCSSProperties["scroll-snap-type-x"] = {
+    domProp: "scrollSnapTypeX",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: ["mandatory", "proximity"],
+    invalid_values: [ "auto",  "1px" ]
+  };
+  gCSSProperties["scroll-snap-type-y"] = {
+    domProp: "scrollSnapTypeY",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: ["mandatory", "proximity"],
+    invalid_values: [ "auto",  "1px" ]
+  };
+}
+
+function SupportsMaskShorthand() {
+  return "maskImage" in document.documentElement.style;
+}
+
+if (SupportsMaskShorthand()) {
+  gCSSProperties["mask"] = {
+    domProp: "mask",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    /* FIXME: All mask-border-* should be added when we implement them. */
+    subproperties: ["mask-clip", "mask-image", "mask-mode", "mask-origin", "mask-position", "mask-repeat", "mask-size" , "mask-composite"],
+    initial_values: [ "match-source", "none", "repeat", "add", "0% 0%", "top left", "left top", "0% 0% / auto", "top left / auto", "left top / auto", "0% 0% / auto auto",
+      "top left none", "left top none", "none left top", "none top left", "none 0% 0%", "left top / auto none", "left top / auto auto none",
+      "match-source none repeat add top left", "left top repeat none add", "none repeat add top left / auto", "left top / auto repeat none add match-source", "none repeat add 0% 0% / auto auto match-source" ],
+    other_values: [
+                      "repeat-x repeat-y",
+                      "repeat repeat-x",
+                      "repeat repeat-y",
+                      "repeat-x repeat",
+                      "repeat-y repeat" ]
+  };
+  gCSSProperties["mask-size"] = {
+    domProp: "maskSize",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto", "auto auto" ],
+    other_values: [ "contain", "cover", "100px auto", "auto 100px", "100% auto", "auto 100%", "25% 50px", "3em 40%",
+      "calc(20px)",
+      "calc(20px) 10px",
+      "10px calc(20px)",
+      "calc(20px) 25%",
+      "25% calc(20px)",
+      "calc(20px) calc(20px)",
+      "calc(20px + 1em) calc(20px / 2)",
+      "calc(20px + 50%) calc(50% - 10px)",
+      "calc(-20px) calc(-50%)",
+      "calc(-20%) calc(-50%)"
+    ],
+    invalid_values: [ "contain contain", "cover cover", "cover auto", "auto cover", "contain cover", "cover contain", "-5px 3px", "3px -5px", "auto -5px", "-5px auto", "5 3", "10px calc(10px + rubbish)" ]
+  };
+} else {
+  gCSSProperties["mask"] = {
+    domProp: "mask",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "url(#mymask)" ],
+    invalid_values: []
+  };
+}
+
+}
diff --git a/reviewboard/diffviewer/testdata/move_detection/bug-4371-old.js b/reviewboard/diffviewer/testdata/move_detection/bug-4371-old.js
new file mode 100644
index 0000000000000000000000000000000000000000..51b58088b94f68758a3eeb9d49f35cc1a20c7f2c
--- /dev/null
+++ b/reviewboard/diffviewer/testdata/move_detection/bug-4371-old.js
@@ -0,0 +1,2661 @@
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "url(#mysym)" ],
+    invalid_values: []
+  },
+  "marker-start": {
+    domProp: "markerStart",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "url(#mysym)" ],
+    invalid_values: []
+  },
+  "mask": {
+    domProp: "mask",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    /* FIXME: All mask-border-* should be added when we implement them. */
+    subproperties: ["mask-clip", "mask-image", "mask-mode", "mask-origin", "mask-position", "mask-repeat", "mask-size" , "mask-composite"],
+    initial_values: [ "match-source", "none", "repeat", "add", "0% 0%", "top left", "left top", "0% 0% / auto", "top left / auto", "left top / auto", "0% 0% / auto auto",
+      "top left none", "left top none", "none left top", "none top left", "none 0% 0%", "left top / auto none", "left top / auto auto none",
+      "match-source none repeat add top left", "left top repeat none add", "none repeat add top left / auto", "left top / auto repeat none add match-source", "none repeat add 0% 0% / auto auto match-source" ],
+                      "repeat-x repeat-y",
+                      "repeat repeat-x",
+                      "repeat repeat-y",
+                      "repeat-x repeat",
+                      "repeat-y repeat" ]
+  },
+  "mask-size": {
+    domProp: "maskSize",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto", "auto auto" ],
+    other_values: [ "contain", "cover", "100px auto", "auto 100px", "100% auto", "auto 100%", "25% 50px", "3em 40%",
+      "calc(20px)",
+      "calc(20px) 10px",
+      "10px calc(20px)",
+      "calc(20px) 25%",
+      "25% calc(20px)",
+      "calc(20px) calc(20px)",
+      "calc(20px + 1em) calc(20px / 2)",
+      "calc(20px + 50%) calc(50% - 10px)",
+      "calc(-20px) calc(-50%)",
+      "calc(-20%) calc(-50%)"
+    ],
+    invalid_values: [ "contain contain", "cover cover", "cover auto", "auto cover", "contain cover", "cover contain", "-5px 3px", "3px -5px", "auto -5px", "-5px auto", "5 3", "10px calc(10px + rubbish)" ]
+  },
+  "shape-rendering": {
+    domProp: "shapeRendering",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: [ "optimizeSpeed", "crispEdges", "geometricPrecision" ],
+    invalid_values: []
+  },
+  "stop-color": {
+    domProp: "stopColor",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    prerequisites: { "color": "blue" },
+    initial_values: [ "black", "#000", "#000000", "rgb(0,0,0)", "rgba(0,0,0,1)" ],
+    other_values: [ "green", "#fc3", "currentColor" ],
+    invalid_values: [ "url('#myserver')", "url(foo.svg#myserver)", 'url("#myserver") green', "000000", "ff00ff" ]
+  },
+  "stop-opacity": {
+    domProp: "stopOpacity",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "1", "2.8", "1.000" ],
+    other_values: [ "0", "0.3", "-7.3" ],
+    invalid_values: []
+  },
+  "stroke": {
+    domProp: "stroke",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "black", "#000", "#000000", "rgb(0,0,0)", "rgba(0,0,0,1)", "green", "#fc3", "url('#myserver')", "url(foo.svg#myserver)", 'url("#myserver") green', "currentColor", "context-fill", "context-stroke" ],
+    invalid_values: [ "000000", "ff00ff" ]
+  },
+  "stroke-dasharray": {
+    domProp: "strokeDasharray",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none", "context-value" ],
+    other_values: [ "5px,3px,2px", "5px 3px 2px", "  5px ,3px\t, 2px ", "1px", "5%", "3em" ],
+    invalid_values: [ "-5px,3px,2px", "5px,3px,-2px" ]
+  },
+  "stroke-dashoffset": {
+    domProp: "strokeDashoffset",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0", "-0px", "0em", "context-value" ],
+    other_values: [ "3px", "3%", "1em" ],
+    invalid_values: []
+  },
+  "stroke-linecap": {
+    domProp: "strokeLinecap",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "butt" ],
+    other_values: [ "round", "square" ],
+    invalid_values: []
+  },
+  "stroke-linejoin": {
+    domProp: "strokeLinejoin",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "miter" ],
+    other_values: [ "round", "bevel" ],
+    invalid_values: []
+  },
+  "stroke-miterlimit": {
+    domProp: "strokeMiterlimit",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "4" ],
+    other_values: [ "1", "7", "5000", "1.1" ],
+    invalid_values: [ "0.9", "0", "-1", "3px", "-0.3" ]
+  },
+  "stroke-opacity": {
+    domProp: "strokeOpacity",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "1", "2.8", "1.000", "context-fill-opacity", "context-stroke-opacity" ],
+    other_values: [ "0", "0.3", "-7.3" ],
+    invalid_values: []
+  },
+  "stroke-width": {
+    domProp: "strokeWidth",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "1px", "context-value" ],
+    other_values: [ "0", "0px", "-0em", "17px", "0.2em" ],
+    invalid_values: [ "-0.1px", "-3px" ]
+  },
+  "text-anchor": {
+    domProp: "textAnchor",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "start" ],
+    other_values: [ "middle", "end" ],
+    invalid_values: []
+  },
+  "text-rendering": {
+    domProp: "textRendering",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: [ "optimizeSpeed", "optimizeLegibility", "geometricPrecision" ],
+    invalid_values: []
+  },
+  "vector-effect": {
+    domProp: "vectorEffect",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "non-scaling-stroke" ],
+    invalid_values: []
+  },
+  "-moz-window-dragging": {
+    domProp: "MozWindowDragging",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "default" ],
+    other_values: [ "drag", "no-drag" ],
+    invalid_values: [ "none" ]
+  },
+  "align-content": {
+    domProp: "alignContent",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: [ "start", "end", "flex-start", "flex-end", "center", "left",
+                    "right", "space-between", "space-around", "space-evenly",
+                    "baseline", "last-baseline", "stretch", "start safe",
+                    "unsafe end", "unsafe end stretch", "end safe space-evenly" ],
+    invalid_values: [ "none", "5", "self-end", "safe", "normal unsafe", "unsafe safe",
+                      "safe baseline", "baseline unsafe", "baseline end", "end normal",
+                      "safe end unsafe start", "safe end unsafe", "normal safe start",
+                      "unsafe end start", "end start safe", "space-between unsafe",
+                      "stretch safe", "auto" ]
+  },
+  "align-items": {
+    domProp: "alignItems",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    // Can't test 'left'/'right' here since that computes to 'start' for blocks.
+    other_values: [ "end", "flex-start", "flex-end", "self-start", "self-end",
+                    "center", "stretch", "baseline", "unsafe left", "start",
+                    "center unsafe", "safe right", "center safe" ],
+    invalid_values: [ "space-between", "abc", "5%", "legacy", "legacy end",
+                      "end legacy", "unsafe", "unsafe baseline", "normal unsafe",
+                      "safe left unsafe", "safe stretch", "end end", "auto" ]
+  },
+  "align-self": {
+    domProp: "alignSelf",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    // (Assuming defaults on the parent, 'auto' will compute to 'normal'.)
+    initial_values: [ "auto", "normal" ],
+    other_values: [ "start", "flex-start", "flex-end", "center", "stretch",
+                    "baseline", "last-baseline", "right safe", "unsafe center",
+                    "self-start", "self-end safe" ],
+    invalid_values: [ "space-between", "abc", "30px", "stretch safe", "safe" ]
+  },
+  "justify-content": {
+    domProp: "justifyContent",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: [ "start", "end", "flex-start", "flex-end", "center", "left",
+                    "right", "space-between", "space-around", "space-evenly",
+                    "baseline", "last-baseline", "stretch", "start safe",
+                    "unsafe end", "unsafe end stretch", "end safe space-evenly" ],
+    invalid_values: [ "30px", "5%", "self-end", "safe", "normal unsafe", "unsafe safe",
+                      "safe baseline", "baseline unsafe", "baseline end", "normal end",
+                      "safe end unsafe start", "safe end unsafe", "normal safe start",
+                      "unsafe end start", "end start safe", "space-around unsafe",
+                      "safe stretch", "auto" ]
+  },
+  "justify-items": {
+    domProp: "justifyItems",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto", "normal" ],
+    other_values: [ "end", "flex-start", "flex-end", "self-start", "self-end",
+                    "center", "left", "right", "baseline", "stretch", "start",
+                    "legacy left", "right legacy", "legacy center",
+                    "unsafe right", "left unsafe", "safe right", "center safe" ],
+    invalid_values: [ "space-between", "abc", "30px", "legacy", "legacy start",
+                      "end legacy", "legacy baseline", "legacy legacy", "unsafe",
+                      "safe legacy left", "legacy left safe", "legacy safe left",
+                      "safe left legacy", "legacy left legacy", "baseline unsafe",
+                      "safe unsafe", "safe left unsafe", "safe stretch" ]
+  },
+  "justify-self": {
+    domProp: "justifySelf",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto", "normal" ],
+    other_values: [ "start", "end", "flex-start", "flex-end", "self-start",
+                    "self-end", "center", "left", "right", "baseline",
+                    "last-baseline", "stretch", "left unsafe", "unsafe right",
+                    "safe right", "center safe" ],
+    invalid_values: [ "space-between", "abc", "30px", "none",
+                      "legacy left", "right legacy" ]
+  },
+  "flex": {
+    domProp: "flex",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "flex-grow",
+      "flex-shrink",
+      "flex-basis"
+    ],
+    initial_values: [ "0 1 auto", "auto 0 1", "0 auto", "auto 0" ],
+    other_values: [
+      "none",
+      "1",
+      "0",
+      "0 1",
+      "0.5",
+      "1.2 3.4",
+      "0 0 0",
+      "0 0 0px",
+      "0px 0 0",
+      "5px 0 0",
+      "2 auto",
+      "auto 4",
+      "auto 5.6 7.8",
+      "-moz-max-content",
+      "1 -moz-max-content",
+      "1 2 -moz-max-content",
+      "-moz-max-content 1",
+      "-moz-max-content 1 2",
+      "-0"
+    ],
+    invalid_values: [
+      "1 2px 3",
+      "1 auto 3",
+      "1px 2 3px",
+      "1px 2 3 4px",
+      "-1",
+      "1 -1",
+      "0 1 calc(0px + rubbish)",
+    ]
+  },
+  "flex-basis": {
+    domProp: "flexBasis",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ " auto" ],
+        // NOTE: This is cribbed directly from the "width" chunk, since this
+        // property takes the exact same values as width (albeit with
+        // different semantics on 'auto').
+        // XXXdholbert (Maybe these should get separated out into
+        // a reusable array defined at the top of this file?)
+    other_values: [ "15px", "3em", "15%", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available",
+      // valid calc() values
+      "calc(-2px)",
+      "calc(2px)",
+      "calc(50%)",
+      "calc(50% + 2px)",
+      "calc( 50% + 2px)",
+      "calc(50% + 2px )",
+      "calc( 50% + 2px )",
+      "calc(50% - -2px)",
+      "calc(2px - -50%)",
+      "calc(3*25px)",
+      "calc(3 *25px)",
+      "calc(3 * 25px)",
+      "calc(3* 25px)",
+      "calc(25px*3)",
+      "calc(25px *3)",
+      "calc(25px* 3)",
+      "calc(25px * 3)",
+      "calc(3*25px + 50%)",
+      "calc(50% - 3em + 2px)",
+      "calc(50% - (3em + 2px))",
+      "calc((50% - 3em) + 2px)",
+      "calc(2em)",
+      "calc(50%)",
+      "calc(50px/2)",
+      "calc(50px/(2 - 1))"
+    ],
+    invalid_values: [ "none", "-2px",
+      // invalid calc() values
+      "calc(50%+ 2px)",
+      "calc(50% +2px)",
+      "calc(50%+2px)",
+      "-moz-min()",
+      "calc(min())",
+      "-moz-max()",
+      "calc(max())",
+      "-moz-min(5px)",
+      "calc(min(5px))",
+      "-moz-max(5px)",
+      "calc(max(5px))",
+      "-moz-min(5px,2em)",
+      "calc(min(5px,2em))",
+      "-moz-max(5px,2em)",
+      "calc(max(5px,2em))",
+      "calc(50px/(2 - 2))",
+      // If we ever support division by values, which is
+      // complicated for the reasons described in
+      // http://lists.w3.org/Archives/Public/www-style/2010Jan/0007.html
+      // , we should support all 4 of these as described in
+      // http://lists.w3.org/Archives/Public/www-style/2009Dec/0296.html
+      "calc((3em / 100%) * 3em)",
+      "calc(3em / 100% * 3em)",
+      "calc(3em * (3em / 100%))",
+      "calc(3em * 3em / 100%)"
+    ]
+  },
+  "flex-direction": {
+    domProp: "flexDirection",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "row" ],
+    other_values: [ "row-reverse", "column", "column-reverse" ],
+    invalid_values: [ "10px", "30%", "justify", "column wrap" ]
+  },
+  "flex-flow": {
+    domProp: "flexFlow",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "flex-direction",
+      "flex-wrap"
+    ],
+    initial_values: [ "row nowrap", "nowrap row", "row", "nowrap" ],
+    other_values: [
+      // only specifying one property:
+      "column",
+      "wrap",
+      "wrap-reverse",
+      // specifying both properties, 'flex-direction' first:
+      "row wrap",
+      "row wrap-reverse",
+      "column wrap",
+      "column wrap-reverse",
+      // specifying both properties, 'flex-wrap' first:
+      "wrap row",
+      "wrap column",
+      "wrap-reverse row",
+      "wrap-reverse column",
+    ],
+    invalid_values: [
+      // specifying flex-direction twice (invalid):
+      "row column",
+      "row column nowrap",
+      "row nowrap column",
+      "nowrap row column",
+      // specifying flex-wrap twice (invalid):
+      "nowrap wrap-reverse",
+      "nowrap wrap-reverse row",
+      "nowrap row wrap-reverse",
+      "row nowrap wrap-reverse",
+      // Invalid data-type / invalid keyword type:
+      "1px", "5%", "justify", "none"
+    ]
+  },
+  "flex-grow": {
+    domProp: "flexGrow",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0" ],
+    other_values: [ "3", "1", "1.0", "2.5", "123" ],
+    invalid_values: [ "0px", "-5", "1%", "3em", "stretch", "auto" ]
+  },
+  "flex-shrink": {
+    domProp: "flexShrink",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "1" ],
+    other_values: [ "3", "0", "0.0", "2.5", "123" ],
+    invalid_values: [ "0px", "-5", "1%", "3em", "stretch", "auto" ]
+  },
+  "flex-wrap": {
+    domProp: "flexWrap",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "nowrap" ],
+    other_values: [ "wrap", "wrap-reverse" ],
+    invalid_values: [ "10px", "30%", "justify", "column wrap", "auto" ]
+  },
+  "order": {
+    domProp: "order",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0" ],
+    other_values: [ "1", "99999", "-1", "-50" ],
+    invalid_values: [ "0px", "1.0", "1.", "1%", "0.2", "3em", "stretch" ]
+  },
+
+  // Aliases
+  "-moz-transform": {
+    domProp: "MozTransform",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transform",
+    subproperties: [ "transform" ],
+    // NOTE: We specify other_values & invalid_values explicitly here (instead
+    // of deferring to "transform") because we accept some legacy syntax as
+    // valid for "-moz-transform" but not for "transform".
+    other_values: [ "translatex(1px)", "translatex(4em)",
+      "translatex(-4px)", "translatex(3px)",
+      "translatex(0px) translatex(1px) translatex(2px) translatex(3px) translatex(4px)",
+      "translatey(4em)", "translate(3px)", "translate(10px, -3px)",
+      "rotate(45deg)", "rotate(45grad)", "rotate(45rad)",
+      "rotate(0.25turn)", "rotate(0)", "scalex(10)", "scaley(10)",
+      "scale(10)", "scale(10, 20)", "skewx(30deg)", "skewx(0)",
+      "skewy(0)", "skewx(30grad)", "skewx(30rad)", "skewx(0.08turn)",
+      "skewy(30deg)", "skewy(30grad)", "skewy(30rad)", "skewy(0.08turn)",
+      "rotate(45deg) scale(2, 1)", "skewx(45deg) skewx(-50grad)",
+      "translate(0, 0) scale(1, 1) skewx(0) skewy(0) matrix(1, 0, 0, 1, 0, 0)",
+      "translatex(50%)", "translatey(50%)", "translate(50%)",
+      "translate(3%, 5px)", "translate(5px, 3%)",
+      "matrix(1, 2, 3, 4, 5, 6)",
+      /* valid calc() values */
+      "translatex(calc(5px + 10%))",
+      "translatey(calc(0.25 * 5px + 10% / 3))",
+      "translate(calc(5px - 10% * 3))",
+      "translate(calc(5px - 3 * 10%), 50px)",
+      "translate(-50px, calc(5px - 10% * 3))",
+      /* valid only when prefixed */
+      "matrix(1, 2, 3, 4, 5px, 6%)",
+      "matrix(1, 2, 3, 4, 5%, 6px)",
+      "matrix(1, 2, 3, 4, 5%, 6%)",
+      "matrix(1, 2, 3, 4, 5px, 6em)",
+      "matrix(1, 0, 0, 1, calc(5px * 3), calc(10% - 3px))",
+      "translatez(1px)", "translatez(4em)", "translatez(-4px)",
+      "translatez(0px)", "translatez(2px) translatez(5px)",
+      "translate3d(3px, 4px, 5px)", "translate3d(2em, 3px, 1em)",
+      "translatex(2px) translate3d(4px, 5px, 6px) translatey(1px)",
+      "scale3d(4, 4, 4)", "scale3d(-2, 3, -7)", "scalez(4)",
+      "scalez(-6)", "rotate3d(2, 3, 4, 45deg)",
+      "rotate3d(-3, 7, 0, 12rad)", "rotatex(15deg)", "rotatey(-12grad)",
+      "rotatez(72rad)", "rotatex(0.125turn)", "perspective(1000px)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)",
+      /* valid only when prefixed */
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13px, 14em, 15px, 16)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20%, 10%, 15, 16)",
+    ],
+    invalid_values: ["1px", "#0000ff", "red", "auto",
+      "translatex(1)", "translatey(1)", "translate(2)",
+      "translate(-3, -4)",
+      "translatex(1px 1px)", "translatex(translatex(1px))",
+      "translatex(#0000ff)", "translatex(red)", "translatey()",
+      "matrix(1px, 2px, 3px, 4px, 5px, 6px)", "scale(150%)",
+      "skewx(red)", "matrix(1%, 0, 0, 0, 0px, 0px)",
+      "matrix(0, 1%, 2, 3, 4px,5px)", "matrix(0, 1, 2%, 3, 4px, 5px)",
+      "matrix(0, 1, 2, 3%, 4%, 5%)",
+      /* invalid calc() values */
+      "translatey(-moz-min(5px,10%))",
+      "translatex(-moz-max(5px,10%))",
+      "translate(10px, calc(min(5px,10%)))",
+      "translate(calc(max(5px,10%)), 10%)",
+      "matrix(1, 0, 0, 1, max(5px * 3), calc(10% - 3px))",
+      "perspective(0px)", "perspective(-10px)", "matrix3d(dinosaur)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15%, 16)",
+      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16px)",
+      "rotatey(words)", "rotatex(7)", "translate3d(3px, 4px, 1px, 7px)",
+    ],
+  },
+  "-moz-transform-origin": {
+    domProp: "MozTransformOrigin",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transform-origin",
+    subproperties: [ "transform-origin" ],
+  },
+  "-moz-perspective-origin": {
+    domProp: "MozPerspectiveOrigin",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "perspective-origin",
+    subproperties: [ "perspective-origin" ],
+  },
+  "-moz-perspective": {
+    domProp: "MozPerspective",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "perspective",
+    subproperties: [ "perspective" ],
+  },
+  "-moz-backface-visibility": {
+    domProp: "MozBackfaceVisibility",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "backface-visibility",
+    subproperties: [ "backface-visibility" ],
+  },
+  "-moz-transform-style": {
+    domProp: "MozTransformStyle",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transform-style",
+    subproperties: [ "transform-style" ],
+  },
+  "-moz-border-image": {
+    domProp: "MozBorderImage",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    alias_for: "border-image",
+    subproperties: [ "border-image-source", "border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat" ],
+  },
+  "-moz-transition": {
+    domProp: "MozTransition",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    alias_for: "transition",
+    subproperties: [ "transition-property", "transition-duration", "transition-timing-function", "transition-delay" ],
+  },
+  "-moz-transition-delay": {
+    domProp: "MozTransitionDelay",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transition-delay",
+    subproperties: [ "transition-delay" ],
+  },
+  "-moz-transition-duration": {
+    domProp: "MozTransitionDuration",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transition-duration",
+    subproperties: [ "transition-duration" ],
+  },
+  "-moz-transition-property": {
+    domProp: "MozTransitionProperty",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transition-property",
+    subproperties: [ "transition-property" ],
+  },
+  "-moz-transition-timing-function": {
+    domProp: "MozTransitionTimingFunction",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "transition-timing-function",
+    subproperties: [ "transition-timing-function" ],
+  },
+  "-moz-animation": {
+    domProp: "MozAnimation",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    alias_for: "animation",
+    subproperties: [ "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-direction", "animation-fill-mode", "animation-iteration-count", "animation-play-state" ],
+  },
+  "-moz-animation-delay": {
+    domProp: "MozAnimationDelay",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-delay",
+    subproperties: [ "animation-delay" ],
+  },
+  "-moz-animation-direction": {
+    domProp: "MozAnimationDirection",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-direction",
+    subproperties: [ "animation-direction" ],
+  },
+  "-moz-animation-duration": {
+    domProp: "MozAnimationDuration",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-duration",
+    subproperties: [ "animation-duration" ],
+  },
+  "-moz-animation-fill-mode": {
+    domProp: "MozAnimationFillMode",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-fill-mode",
+    subproperties: [ "animation-fill-mode" ],
+  },
+  "-moz-animation-iteration-count": {
+    domProp: "MozAnimationIterationCount",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-iteration-count",
+    subproperties: [ "animation-iteration-count" ],
+  },
+  "-moz-animation-name": {
+    domProp: "MozAnimationName",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-name",
+    subproperties: [ "animation-name" ],
+  },
+  "-moz-animation-play-state": {
+    domProp: "MozAnimationPlayState",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-play-state",
+    subproperties: [ "animation-play-state" ],
+  },
+  "-moz-animation-timing-function": {
+    domProp: "MozAnimationTimingFunction",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "animation-timing-function",
+    subproperties: [ "animation-timing-function" ],
+  },
+  "-moz-font-feature-settings": {
+    domProp: "MozFontFeatureSettings",
+    inherited: true,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "font-feature-settings",
+    subproperties: [ "font-feature-settings" ],
+  },
+  "-moz-font-language-override": {
+    domProp: "MozFontLanguageOverride",
+    inherited: true,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "font-language-override",
+    subproperties: [ "font-language-override" ],
+  },
+  "-moz-hyphens": {
+    domProp: "MozHyphens",
+    inherited: true,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+    alias_for: "hyphens",
+    subproperties: [ "hyphens" ],
+  }
+}
+
+function logical_axis_prop_get_computed(cs, property)
+{
+  // Use defaults for these two properties in case the vertical text
+  // pref (which they live behind) is turned off.
+  var writingMode = cs.getPropertyValue("writing-mode") || "horizontal-tb";
+  var orientation = writingMode.substring(0, writingMode.indexOf("-"));
+
+  var mappings = {
+    "block-size":      { horizontal: "height",
+                         vertical:   "width",
+                         sideways:   "width"      },
+    "inline-size":     { horizontal: "width",
+                         vertical:   "height",
+                         sideways:   "height"     },
+    "max-block-size":  { horizontal: "max-height",
+                         vertical:   "max-width",
+                         sideways:   "max-width"  },
+    "max-inline-size": { horizontal: "max-width",
+                         vertical:   "max-height",
+                         sideways:   "max-height" },
+    "min-block-size":  { horizontal: "min-height",
+                         vertical:   "min-width",
+                         sideways:   "min-width"  },
+    "min-inline-size": { horizontal: "min-width",
+                         vertical:   "min-height",
+                         sideways:   "min-height" },
+  };
+
+  if (!mappings[property]) {
+    throw "unexpected property " + property;
+  }
+
+  var prop = mappings[property][orientation];
+  if (!prop) {
+    throw "unexpected writing mode " + writingMode;
+  }
+
+  return cs.getPropertyValue(prop);
+}
+
+function logical_box_prop_get_computed(cs, property)
+{
+  // http://dev.w3.org/csswg/css-writing-modes-3/#logical-to-physical
+
+  // Use default for writing-mode in case the vertical text
+  // pref (which it lives behind) is turned off.
+  var writingMode = cs.getPropertyValue("writing-mode") || "horizontal-tb";
+
+  var direction = cs.getPropertyValue("direction");
+
+  // keys in blockMappings are writing-mode values
+  var blockMappings = {
+    "horizontal-tb": { "start": "top",   "end": "bottom" },
+    "vertical-rl":   { "start": "right", "end": "left"   },
+    "vertical-lr":   { "start": "left",  "end": "right"  },
+    "sideways-rl":   { "start": "right", "end": "left"   },
+    "sideways-lr":   { "start": "left",  "end": "right"  },
+  };
+
+  // keys in inlineMappings are regular expressions that match against
+  // a {writing-mode,direction} pair as a space-separated string
+  var inlineMappings = {
+    "horizontal-tb ltr": { "start": "left",   "end": "right"  },
+    "horizontal-tb rtl": { "start": "right",  "end": "left"   },
+    "vertical-.. ltr":   { "start": "bottom", "end": "top"    },
+    "vertical-.. rtl":   { "start": "top",    "end": "bottom" },
+    "vertical-.. ltr":   { "start": "top",    "end": "bottom" },
+    "vertical-.. rtl":   { "start": "bottom", "end": "top"    },
+    "sideways-lr ltr":   { "start": "bottom", "end": "top"    },
+    "sideways-lr rtl":   { "start": "top",    "end": "bottom" },
+    "sideways-rl ltr":   { "start": "top",    "end": "bottom" },
+    "sideways-rl rtl":   { "start": "bottom", "end": "top"    },
+  };
+
+  var blockMapping = blockMappings[writingMode];
+  var inlineMapping;
+
+  // test each regular expression in inlineMappings against the
+  // {writing-mode,direction} pair
+  var key = `${writingMode} ${direction}`;
+  for (var k in inlineMappings) {
+    if (new RegExp(k).test(key)) {
+      inlineMapping = inlineMappings[k];
+      break;
+    }
+  }
+
+  if (!blockMapping || !inlineMapping) {
+    throw "Unexpected writing mode property values";
+  }
+
+  function physicalize(aProperty, aMapping, aLogicalPrefix) {
+    for (var logicalSide in aMapping) {
+      var physicalSide = aMapping[logicalSide];
+      logicalSide = aLogicalPrefix + logicalSide;
+      aProperty = aProperty.replace(logicalSide, physicalSide);
+    }
+    return aProperty;
+  }
+
+  if (/^-moz-/.test(property)) {
+    property = physicalize(property.substring(5), inlineMapping, "");
+  } else if (/^offset-(block|inline)-(start|end)/.test(property)) {
+    property = property.substring(7);  // we want "top" not "offset-top", e.g.
+    property = physicalize(property, blockMapping, "block-");
+    property = physicalize(property, inlineMapping, "inline-");
+  } else if (/-(block|inline)-(start|end)/.test(property)) {
+    property = physicalize(property, blockMapping, "block-");
+    property = physicalize(property, inlineMapping, "inline-");
+  } else {
+    throw "Unexpected property";
+  }
+  return cs.getPropertyValue(property);
+}
+
+// Helper to get computed style of "-webkit-box-orient" from "flex-direction"
+// and the "writing-mode".
+function webkit_orient_get_computed(cs, property)
+{
+  var writingMode = cs.getPropertyValue("writing-mode") || "horizontal-tb";
+
+  var mapping; // map from flex-direction values to -webkit-box-orient values.
+  if (writingMode == "horizontal-tb") {
+    // Horizontal writing-mode
+    mapping = { "row" : "horizontal", "column" : "vertical"};
+  } else {
+    // Vertical writing-mode
+    mapping = { "row" : "vertical",   "column" : "horizontal"};
+  }
+
+  var flexDirection = cs.getPropertyValue("flex-direction");
+  return mapping[flexDirection];
+}
+
+// Get the computed value for a property.  For shorthands, return the
+// computed values of all the subproperties, delimited by " ; ".
+function get_computed_value(cs, property)
+{
+  var info = gCSSProperties[property];
+  if (info.type == CSS_TYPE_TRUE_SHORTHAND ||
+      (info.type == CSS_TYPE_SHORTHAND_AND_LONGHAND &&
+        (property == "text-decoration" || property == "mask"))) {
+    var results = [];
+    for (var idx in info.subproperties) {
+      var subprop = info.subproperties[idx];
+      results.push(get_computed_value(cs, subprop));
+    }
+    return results.join(" ; ");
+  }
+  if (info.get_computed)
+    return info.get_computed(cs, property);
+  return cs.getPropertyValue(property);
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.touch_action.enabled")) {
+    gCSSProperties["touch-action"] = {
+        domProp: "touchAction",
+        inherited: false,
+        type: CSS_TYPE_LONGHAND,
+        initial_values: ["auto"],
+        other_values: ["none", "pan-x", "pan-y", "pan-x pan-y", "pan-y pan-x", "manipulation"],
+        invalid_values: ["zoom", "pinch", "tap", "10px", "2", "auto pan-x", "pan-x auto", "none pan-x", "pan-x none",
+                 "auto pan-y", "pan-y auto", "none pan-y", "pan-y none", "pan-x pan-x", "pan-y pan-y",
+                 "pan-x pan-y none", "pan-x none pan-y", "none pan-x pan-y", "pan-y pan-x none", "pan-y none pan-x", "none pan-y pan-x",
+                 "pan-x pan-y auto", "pan-x auto pan-y", "auto pan-x pan-y", "pan-y pan-x auto", "pan-y auto pan-x", "auto pan-y pan-x",
+                 "pan-x pan-y zoom", "pan-x zoom pan-y", "zoom pan-x pan-y", "pan-y pan-x zoom", "pan-y zoom pan-x", "zoom pan-y pan-x",
+                 "pan-x pan-y pan-x", "pan-x pan-x pan-y", "pan-y pan-x pan-x", "pan-y pan-x pan-y", "pan-y pan-y pan-x", "pan-x pan-y pan-y",
+                 "manipulation none", "none manipulation", "manipulation auto", "auto manipulation", "manipulation zoom", "zoom manipulation",
+                 "manipulation manipulation", "manipulation pan-x", "pan-x manipulation", "manipulation pan-y", "pan-y manipulation",
+                 "manipulation pan-x pan-y", "pan-x manipulation pan-y", "pan-x pan-y manipulation",
+                 "manipulation pan-y pan-x", "pan-y manipulation pan-x", "pan-y pan-x manipulation"]
+    };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.vertical-text.enabled")) {
+  var verticalTextProperties = {
+    "writing-mode": {
+      domProp: "writingMode",
+      inherited: true,
+      type: CSS_TYPE_LONGHAND,
+      initial_values: [ "horizontal-tb", "lr", "lr-tb", "rl", "rl-tb" ],
+      other_values: [ "vertical-lr", "vertical-rl", "sideways-rl", "sideways-lr", "tb", "tb-rl" ],
+      invalid_values: [ "10px", "30%", "justify", "auto", "1em" ]
+    },
+    "text-orientation": {
+      domProp: "textOrientation",
+      inherited: true,
+      type: CSS_TYPE_LONGHAND,
+      initial_values: [ "mixed" ],
+      other_values: [ "upright", "sideways", "sideways-right" ], /* sideways-right alias for backward compatibility */
+      invalid_values: [ "none", "3em", "sideways-left" ] /* sideways-left removed from CSS Writing Modes */
+    },
+    "border-block-end": {
+      domProp: "borderBlockEnd",
+      inherited: false,
+      type: CSS_TYPE_TRUE_SHORTHAND,
+      subproperties: [ "border-block-end-color", "border-block-end-style", "border-block-end-width" ],
+      initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
+      other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
+      invalid_values: [ "5%", "5", "5 solid green" ]
+    },
+    "block-size": {
+      domProp: "blockSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      /* XXX testing auto has prerequisites */
+      initial_values: [ "auto" ],
+      prerequisites: { "display": "block" },
+      other_values: [ "15px", "3em", "15%",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "none", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ],
+    },
+    "border-block-end-color": {
+      domProp: "borderBlockEndColor",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      initial_values: [ "currentColor" ],
+      other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
+      invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000" ]
+    },
+    "border-block-end-style": {
+      domProp: "borderBlockEndStyle",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* XXX hidden is sometimes the same as initial */
+      initial_values: [ "none" ],
+      other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ],
+      invalid_values: []
+    },
+    "border-block-end-width": {
+      domProp: "borderBlockEndWidth",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      prerequisites: { "border-block-end-style": "solid" },
+      initial_values: [ "medium", "3px", "calc(4px - 1px)" ],
+      other_values: [ "thin", "thick", "1px", "2em",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(0em)",
+        "calc(0px)",
+        "calc(5em)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 5em)",
+      ],
+      invalid_values: [ "5%", "5" ]
+    },
+    "border-block-start": {
+      domProp: "borderBlockStart",
+      inherited: false,
+      type: CSS_TYPE_TRUE_SHORTHAND,
+      subproperties: [ "border-block-start-color", "border-block-start-style", "border-block-start-width" ],
+      initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
+      other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
+      invalid_values: [ "5%", "5", "5 solid green" ]
+    },
+    "border-block-start-color": {
+      domProp: "borderBlockStartColor",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      initial_values: [ "currentColor" ],
+      other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
+      invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000" ]
+    },
+    "border-block-start-style": {
+      domProp: "borderBlockStartStyle",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* XXX hidden is sometimes the same as initial */
+      initial_values: [ "none" ],
+      other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ],
+      invalid_values: []
+    },
+    "border-block-start-width": {
+      domProp: "borderBlockStartWidth",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      prerequisites: { "border-block-start-style": "solid" },
+      initial_values: [ "medium", "3px", "calc(4px - 1px)" ],
+      other_values: [ "thin", "thick", "1px", "2em",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(0em)",
+        "calc(0px)",
+        "calc(5em)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 5em)",
+      ],
+      invalid_values: [ "5%", "5" ]
+    },
+    "-moz-border-end": {
+      domProp: "MozBorderEnd",
+      inherited: false,
+      type: CSS_TYPE_TRUE_SHORTHAND,
+      alias_for: "border-inline-end",
+      subproperties: [ "-moz-border-end-color", "-moz-border-end-style", "-moz-border-end-width" ],
+    },
+    "-moz-border-end-color": {
+      domProp: "MozBorderEndColor",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-end-color",
+      subproperties: [ "border-inline-end-color" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-border-end-style": {
+      domProp: "MozBorderEndStyle",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-end-style",
+      subproperties: [ "border-inline-end-style" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-border-end-width": {
+      domProp: "MozBorderEndWidth",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-end-width",
+      subproperties: [ "border-inline-end-width" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-border-start": {
+      domProp: "MozBorderStart",
+      inherited: false,
+      type: CSS_TYPE_TRUE_SHORTHAND,
+      alias_for: "border-inline-start",
+      subproperties: [ "-moz-border-start-color", "-moz-border-start-style", "-moz-border-start-width" ],
+    },
+    "-moz-border-start-color": {
+      domProp: "MozBorderStartColor",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-start-color",
+      subproperties: [ "border-inline-start-color" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-border-start-style": {
+      domProp: "MozBorderStartStyle",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-start-style",
+      subproperties: [ "border-inline-start-style" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-border-start-width": {
+      domProp: "MozBorderStartWidth",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "border-inline-start-width",
+      subproperties: [ "border-inline-start-width" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "inline-size": {
+      domProp: "inlineSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      /* XXX testing auto has prerequisites */
+      initial_values: [ "auto" ],
+      prerequisites: { "display": "block" },
+      other_values: [ "15px", "3em", "15%",
+        // these three keywords compute to the initial value only when the
+        // writing mode is vertical, and we're testing with a horizontal
+        // writing mode
+        "-moz-max-content", "-moz-min-content", "-moz-fit-content",
+        // whether -moz-available computes to the initial value depends on
+        // the container size, and for the container size we're testing
+        // with, it does
+        // "-moz-available",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "none" ],
+    },
+    "margin-block-end": {
+      domProp: "marginBlockEnd",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* XXX testing auto has prerequisites */
+      initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ],
+      other_values: [ "1px", "2em", "5%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ],
+    },
+    "margin-block-start": {
+      domProp: "marginBlockStart",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* XXX testing auto has prerequisites */
+      initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ],
+      other_values: [ "1px", "2em", "5%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ],
+    },
+    "-moz-margin-end": {
+      domProp: "MozMarginEnd",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "margin-inline-end",
+      subproperties: [ "margin-inline-end" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-margin-start": {
+      domProp: "MozMarginStart",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "margin-inline-start",
+      subproperties: [ "margin-inline-start" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "max-block-size": {
+      domProp: "maxBlockSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      prerequisites: { "display": "block" },
+      initial_values: [ "none" ],
+      other_values: [ "30px", "50%",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "auto", "5", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ]
+    },
+    "max-inline-size": {
+      domProp: "maxInlineSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      prerequisites: { "display": "block" },
+      initial_values: [ "none" ],
+      other_values: [ "30px", "50%",
+        // these four keywords compute to the initial value only when the
+        // writing mode is vertical, and we're testing with a horizontal
+        // writing mode
+        "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "auto", "5" ]
+    },
+    "min-block-size": {
+      domProp: "minBlockSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      prerequisites: { "display": "block" },
+      initial_values: [ "auto", "0", "calc(0em)", "calc(-2px)", "calc(-1%)" ],
+      other_values: [ "30px", "50%",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "none", "5", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ]
+    },
+    "min-inline-size": {
+      domProp: "minInlineSize",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      axis: true,
+      get_computed: logical_axis_prop_get_computed,
+      prerequisites: { "display": "block" },
+      initial_values: [ "auto", "0", "calc(0em)", "calc(-2px)", "calc(-1%)" ],
+      other_values: [ "30px", "50%",
+        // these four keywords compute to the initial value only when the
+        // writing mode is vertical, and we're testing with a horizontal
+        // writing mode
+        "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ "none", "5" ]
+    },
+    "offset-block-end": {
+      domProp: "offsetBlockEnd",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* FIXME: run tests with multiple prerequisites */
+      prerequisites: { "position": "relative" },
+      /* XXX 0 may or may not be equal to auto */
+      initial_values: [ "auto" ],
+      other_values: [ "32px", "-3em", "12%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: []
+    },
+    "offset-block-start": {
+      domProp: "offsetBlockStart",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* FIXME: run tests with multiple prerequisites */
+      prerequisites: { "position": "relative" },
+      /* XXX 0 may or may not be equal to auto */
+      initial_values: [ "auto" ],
+      other_values: [ "32px", "-3em", "12%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: []
+    },
+    "offset-inline-end": {
+      domProp: "offsetInlineEnd",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* FIXME: run tests with multiple prerequisites */
+      prerequisites: { "position": "relative" },
+      /* XXX 0 may or may not be equal to auto */
+      initial_values: [ "auto" ],
+      other_values: [ "32px", "-3em", "12%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: []
+    },
+    "offset-inline-start": {
+      domProp: "offsetInlineStart",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      /* FIXME: run tests with multiple prerequisites */
+      prerequisites: { "position": "relative" },
+      /* XXX 0 may or may not be equal to auto */
+      initial_values: [ "auto" ],
+      other_values: [ "32px", "-3em", "12%",
+        "calc(2px)",
+        "calc(-2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: []
+    },
+    "padding-block-end": {
+      domProp: "paddingBlockEnd",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ],
+      other_values: [ "1px", "2em", "5%",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ ],
+    },
+    "padding-block-start": {
+      domProp: "paddingBlockStart",
+      inherited: false,
+      type: CSS_TYPE_LONGHAND,
+      logical: true,
+      get_computed: logical_box_prop_get_computed,
+      initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ],
+      other_values: [ "1px", "2em", "5%",
+        "calc(2px)",
+        "calc(50%)",
+        "calc(3*25px)",
+        "calc(25px*3)",
+        "calc(3*25px + 50%)",
+      ],
+      invalid_values: [ ],
+    },
+    "-moz-padding-end": {
+      domProp: "MozPaddingEnd",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "padding-inline-end",
+      subproperties: [ "padding-inline-end" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+    "-moz-padding-start": {
+      domProp: "MozPaddingStart",
+      inherited: false,
+      type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
+      alias_for: "padding-inline-start",
+      subproperties: [ "padding-inline-start" ],
+      get_computed: logical_box_prop_get_computed,
+    },
+  };
+  for (var prop in verticalTextProperties) {
+    gCSSProperties[prop] = verticalTextProperties[prop];
+  }
+  /*
+   * Vertical vs horizontal writing-mode can affect line-height
+   * because font metrics may not be symmetrical,
+   * so we require writing-mode:initial to ensure consistency
+   * in font shorthand and line-height tests.
+   */
+  ["font", "line-height"].forEach(function(prop) {
+    var p = gCSSProperties[prop];
+    if (p.prerequisites === undefined) {
+      p.prerequisites = {};
+    }
+    p.prerequisites["writing-mode"] = "initial";
+  });
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.text-combine-upright.enabled")) {
+  gCSSProperties["text-combine-upright"] = {
+    domProp: "textCombineUpright",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "all", "digits", "digits 2", "digits 3", "digits 4", "digits     3" ],
+    invalid_values: [ "auto", "all 2", "none all", "digits -3", "digits 0",
+                      "digits 12", "none 3", "digits 3.1415", "digits3", "digits 1",
+                      "digits 3 all", "digits foo", "digits all", "digits 3.0" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.masking.enabled")) {
+  gCSSProperties["mask-type"] = {
+    domProp: "maskType",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "luminance" ],
+    other_values: [ "alpha" ],
+    invalid_values: []
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("svg.paint-order.enabled")) {
+  gCSSProperties["paint-order"] = {
+    domProp: "paintOrder",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: [ "fill", "fill stroke", "fill stroke markers", "stroke markers fill" ],
+    invalid_values: [ "fill stroke markers fill", "fill normal" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("svg.transform-box.enabled")) {
+  gCSSProperties["transform-box"] = {
+    domProp: "transformBox",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "border-box" ],
+    other_values: [ "fill-box", "view-box" ],
+    invalid_values: []
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.clip-path-shapes.enabled")) {
+  gCSSProperties["clip-path"] = {
+    domProp: "clipPath",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      // SVG reference clip-path
+      "url(#my-clip-path)",
+
+      "polygon(20px 20px)",
+      "polygon(20px 20%)",
+      "polygon(20% 20%)",
+      "polygon(20rem 20em)",
+      "polygon(20cm 20mm)",
+      "polygon(20px 20px, 30px 30px)",
+      "polygon(20px 20px, 30% 30%, 30px 30px)",
+      "polygon(nonzero, 20px 20px, 30% 30%, 30px 30px)",
+      "polygon(evenodd, 20px 20px, 30% 30%, 30px 30px)",
+
+      "content-box",
+      "padding-box",
+      "border-box",
+      "margin-box",
+      "fill-box",
+      "stroke-box",
+      "view-box",
+
+      "polygon(0 0) content-box",
+      "border-box polygon(0 0)",
+      "padding-box    polygon(   0  20px ,  30px    20% )  ",
+      "polygon(evenodd, 20% 20em) content-box",
+      "polygon(evenodd, 20vh 20em) padding-box",
+      "polygon(evenodd, 20vh calc(20% + 20em)) border-box",
+      "polygon(evenodd, 20vh 20vw) margin-box",
+      "polygon(evenodd, 20pt 20cm) fill-box",
+      "polygon(evenodd, 20ex 20pc) stroke-box",
+      "polygon(evenodd, 20rem 20in) view-box",
+
+      "circle()",
+      "circle(at center)",
+      "circle(at top left 20px)",
+      "circle(at bottom right)",
+      "circle(20%)",
+      "circle(300px)",
+      "circle(calc(20px + 30px))",
+      "circle(farthest-side)",
+      "circle(closest-side)",
+      "circle(closest-side at center)",
+      "circle(farthest-side at top)",
+      "circle(20px at top right)",
+      "circle(40% at 50% 100%)",
+      "circle(calc(20% + 20%) at right bottom)",
+      "circle() padding-box",
+
+      "ellipse()",
+      "ellipse(at center)",
+      "ellipse(at top left 20px)",
+      "ellipse(at bottom right)",
+      "ellipse(20% 20%)",
+      "ellipse(300px 50%)",
+      "ellipse(calc(20px + 30px) 10%)",
+      "ellipse(farthest-side closest-side)",
+      "ellipse(closest-side farthest-side)",
+      "ellipse(farthest-side farthest-side)",
+      "ellipse(closest-side closest-side)",
+      "ellipse(closest-side closest-side at center)",
+      "ellipse(20% farthest-side at top)",
+      "ellipse(20px 50% at top right)",
+      "ellipse(closest-side 40% at 50% 100%)",
+      "ellipse(calc(20% + 20%) calc(20px + 20cm) at right bottom)",
+
+      "inset(1px)",
+      "inset(20% -20px)",
+      "inset(20em 4rem calc(20% + 20px))",
+      "inset(20vh 20vw 20pt 3%)",
+      "inset(5px round 3px)",
+      "inset(1px 2px round 3px / 3px)",
+      "inset(1px 2px 3px round 3px 2em / 20%)",
+      "inset(1px 2px 3px 4px round 3px 2vw 20% / 20px 3em 2vh 20%)",
+    ],
+    invalid_values: [
+      "url(#test) url(#tes2)",
+      "polygon (0 0)",
+      "polygon(20px, 40px)",
+      "border-box content-box",
+      "polygon(0 0) polygon(0 0)",
+      "polygon(nonzero 0 0)",
+      "polygon(evenodd 20px 20px)",
+      "polygon(20px 20px, evenodd)",
+      "polygon(20px 20px, nonzero)",
+      "polygon(0 0) conten-box content-box",
+      "content-box polygon(0 0) conten-box",
+      "padding-box polygon(0 0) conten-box",
+      "polygon(0 0) polygon(0 0) content-box",
+      "polygon(0 0) content-box polygon(0 0)",
+      "polygon(0 0), content-box",
+      "polygon(0 0), polygon(0 0)",
+      "content-box polygon(0 0) polygon(0 0)",
+      "content-box polygon(0 0) none",
+      "none content-box polygon(0 0)",
+      "inherit content-box polygon(0 0)",
+      "initial polygon(0 0)",
+      "polygon(0 0) farthest-side",
+      "farthest-corner polygon(0 0)",
+      "polygon(0 0) farthest-corner",
+      "polygon(0 0) conten-box",
+      "polygon(0 0) polygon(0 0) farthest-corner",
+      "polygon(0 0) polygon(0 0) polygon(0 0)",
+      "border-box polygon(0, 0)",
+      "border-box padding-box",
+      "margin-box farthest-side",
+      "nonsense() border-box",
+      "border-box nonsense()",
+
+      "circle(at)",
+      "circle(at 20% 20% 30%)",
+      "circle(20px 2px at center)",
+      "circle(2at center)",
+      "circle(closest-corner)",
+      "circle(at center top closest-side)",
+      "circle(-20px)",
+      "circle(farthest-side closest-side)",
+      "circle(20% 20%)",
+      "circle(at farthest-side)",
+      "circle(calc(20px + rubbish))",
+
+      "ellipse(at)",
+      "ellipse(at 20% 20% 30%)",
+      "ellipse(20px at center)",
+      "ellipse(-20px 20px)",
+      "ellipse(closest-corner farthest-corner)",
+      "ellipse(20px -20px)",
+      "ellipse(-20px -20px)",
+      "ellipse(farthest-side)",
+      "ellipse(20%)",
+      "ellipse(at farthest-side farthest-side)",
+      "ellipse(at top left calc(20px + rubbish))",
+
+      "polygon(at)",
+      "polygon(at 20% 20% 30%)",
+      "polygon(20px at center)",
+      "polygon(2px 2at center)",
+      "polygon(closest-corner farthest-corner)",
+      "polygon(at center top closest-side closest-side)",
+      "polygon(40% at 50% 100%)",
+      "polygon(40% farthest-side 20px at 50% 100%)",
+
+      "inset()",
+      "inset(round)",
+      "inset(round 3px)",
+      "inset(1px round 1px 2px 3px 4px 5px)",
+      "inset(1px 2px 3px 4px 5px)",
+      "inset(1px, round 3px)",
+      "inset(1px, 2px)",
+      "inset(1px 2px, 3px)",
+      "inset(1px at 3px)",
+      "inset(1px round 1px // 2px)",
+      "inset(1px round)",
+      "inset(1px calc(2px + rubbish))",
+      "inset(1px round 2px calc(3px + rubbish))",
+    ],
+    unbalanced_values: [
+      "polygon(30% 30%",
+      "polygon(nonzero, 20% 20px",
+      "polygon(evenodd, 20px 20px",
+
+      "circle(",
+      "circle(40% at 50% 100%",
+      "ellipse(",
+      "ellipse(40% at 50% 100%",
+
+      "inset(1px",
+      "inset(1px 2px",
+      "inset(1px 2px 3px",
+      "inset(1px 2px 3px 4px",
+      "inset(1px 2px 3px 4px round 5px",
+      "inset(1px 2px 3px 4px round 5px / 6px",
+    ]
+  };
+}
+
+
+if (IsCSSPropertyPrefEnabled("layout.css.filters.enabled")) {
+  gCSSProperties["filter"] = {
+    domProp: "filter",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      // SVG reference filters
+      "url(#my-filter)",
+      "url(#my-filter-1) url(#my-filter-2)",
+
+      // Filter functions
+      "opacity(50%) saturate(1.0)",
+      "invert(50%) sepia(0.1) brightness(90%)",
+
+      // Mixed SVG reference filters and filter functions
+      "grayscale(1) url(#my-filter-1)",
+      "url(#my-filter-1) brightness(50%) contrast(0.9)",
+
+      // Bad URLs
+      "url('badscheme:badurl')",
+      "blur(3px) url('badscheme:badurl') grayscale(50%)",
+
+      "blur(0)",
+      "blur(0px)",
+      "blur(0.5px)",
+      "blur(3px)",
+      "blur(100px)",
+      "blur(0.1em)",
+      "blur(calc(-1px))", // Parses and becomes blur(0px).
+      "blur(calc(0px))",
+      "blur(calc(5px))",
+      "blur(calc(2 * 5px))",
+
+      "brightness(0)",
+      "brightness(50%)",
+      "brightness(1)",
+      "brightness(1.0)",
+      "brightness(2)",
+      "brightness(350%)",
+      "brightness(4.567)",
+
+      "contrast(0)",
+      "contrast(50%)",
+      "contrast(1)",
+      "contrast(1.0)",
+      "contrast(2)",
+      "contrast(350%)",
+      "contrast(4.567)",
+
+      "drop-shadow(2px 2px)",
+      "drop-shadow(2px 2px 1px)",
+      "drop-shadow(2px 2px green)",
+      "drop-shadow(2px 2px 1px green)",
+      "drop-shadow(green 2px 2px)",
+      "drop-shadow(green 2px 2px 1px)",
+      "drop-shadow(currentColor 3px 3px)",
+      "drop-shadow(2px 2px calc(-5px))", /* clamped */
+      "drop-shadow(calc(3em - 2px) 2px green)",
+      "drop-shadow(green calc(3em - 2px) 2px)",
+      "drop-shadow(2px calc(2px + 0.2em))",
+      "drop-shadow(blue 2px calc(2px + 0.2em))",
+      "drop-shadow(2px calc(2px + 0.2em) blue)",
+      "drop-shadow(calc(-2px) calc(-2px))",
+      "drop-shadow(-2px -2px)",
+      "drop-shadow(calc(2px) calc(2px))",
+      "drop-shadow(calc(2px) calc(2px) calc(2px))",
+
+      "grayscale(0)",
+      "grayscale(50%)",
+      "grayscale(1)",
+      "grayscale(1.0)",
+      "grayscale(2)",
+      "grayscale(350%)",
+      "grayscale(4.567)",
+
+      "hue-rotate(0deg)",
+      "hue-rotate(90deg)",
+      "hue-rotate(540deg)",
+      "hue-rotate(-90deg)",
+      "hue-rotate(10grad)",
+      "hue-rotate(1.6rad)",
+      "hue-rotate(-1.6rad)",
+      "hue-rotate(0.5turn)",
+      "hue-rotate(-2turn)",
+
+      "invert(0)",
+      "invert(50%)",
+      "invert(1)",
+      "invert(1.0)",
+      "invert(2)",
+      "invert(350%)",
+      "invert(4.567)",
+
+      "opacity(0)",
+      "opacity(50%)",
+      "opacity(1)",
+      "opacity(1.0)",
+      "opacity(2)",
+      "opacity(350%)",
+      "opacity(4.567)",
+
+      "saturate(0)",
+      "saturate(50%)",
+      "saturate(1)",
+      "saturate(1.0)",
+      "saturate(2)",
+      "saturate(350%)",
+      "saturate(4.567)",
+
+      "sepia(0)",
+      "sepia(50%)",
+      "sepia(1)",
+      "sepia(1.0)",
+      "sepia(2)",
+      "sepia(350%)",
+      "sepia(4.567)",
+    ],
+    invalid_values: [
+      // none
+      "none none",
+      "url(#my-filter) none",
+      "none url(#my-filter)",
+      "blur(2px) none url(#my-filter)",
+
+      // Nested filters
+      "grayscale(invert(1.0))",
+
+      // Comma delimited filters
+      "url(#my-filter),",
+      "invert(50%), url(#my-filter), brightness(90%)",
+
+      // Test the following situations for each filter function:
+      // - Invalid number of arguments
+      // - Comma delimited arguments
+      // - Wrong argument type
+      // - Argument value out of range
+      "blur()",
+      "blur(3px 5px)",
+      "blur(3px,)",
+      "blur(3px, 5px)",
+      "blur(#my-filter)",
+      "blur(0.5)",
+      "blur(50%)",
+      "blur(calc(0))", // Unitless zero in calc is not a valid length.
+      "blur(calc(0.1))",
+      "blur(calc(10%))",
+      "blur(calc(20px - 5%))",
+      "blur(-3px)",
+
+      "brightness()",
+      "brightness(0.5 0.5)",
+      "brightness(0.5,)",
+      "brightness(0.5, 0.5)",
+      "brightness(#my-filter)",
+      "brightness(10px)",
+      "brightness(-1)",
+
+      "contrast()",
+      "contrast(0.5 0.5)",
+      "contrast(0.5,)",
+      "contrast(0.5, 0.5)",
+      "contrast(#my-filter)",
+      "contrast(10px)",
+      "contrast(-1)",
+
+      "drop-shadow()",
+      "drop-shadow(3% 3%)",
+      "drop-shadow(2px 2px -5px)",
+      "drop-shadow(2px 2px 2px 2px)",
+      "drop-shadow(2px 2px, none)",
+      "drop-shadow(none, 2px 2px)",
+      "drop-shadow(inherit, 2px 2px)",
+      "drop-shadow(2px 2px, inherit)",
+      "drop-shadow(2 2px)",
+      "drop-shadow(2px 2)",
+      "drop-shadow(2px 2px 2)",
+      "drop-shadow(2px 2px 2px 2)",
+      "drop-shadow(calc(2px) calc(2px) calc(2px) calc(2px))",
+      "drop-shadow(green 2px 2px, blue 1px 3px 4px)",
+      "drop-shadow(blue 2px 2px, currentColor 1px 2px)",
+
+      "grayscale()",
+      "grayscale(0.5 0.5)",
+      "grayscale(0.5,)",
+      "grayscale(0.5, 0.5)",
+      "grayscale(#my-filter)",
+      "grayscale(10px)",
+      "grayscale(-1)",
+
+      "hue-rotate()",
+      "hue-rotate(0)",
+      "hue-rotate(0.5 0.5)",
+      "hue-rotate(0.5,)",
+      "hue-rotate(0.5, 0.5)",
+      "hue-rotate(#my-filter)",
+      "hue-rotate(10px)",
+      "hue-rotate(-1)",
+      "hue-rotate(45deg,)",
+
+      "invert()",
+      "invert(0.5 0.5)",
+      "invert(0.5,)",
+      "invert(0.5, 0.5)",
+      "invert(#my-filter)",
+      "invert(10px)",
+      "invert(-1)",
+
+      "opacity()",
+      "opacity(0.5 0.5)",
+      "opacity(0.5,)",
+      "opacity(0.5, 0.5)",
+      "opacity(#my-filter)",
+      "opacity(10px)",
+      "opacity(-1)",
+
+      "saturate()",
+      "saturate(0.5 0.5)",
+      "saturate(0.5,)",
+      "saturate(0.5, 0.5)",
+      "saturate(#my-filter)",
+      "saturate(10px)",
+      "saturate(-1)",
+
+      "sepia()",
+      "sepia(0.5 0.5)",
+      "sepia(0.5,)",
+      "sepia(0.5, 0.5)",
+      "sepia(#my-filter)",
+      "sepia(10px)",
+      "sepia(-1)",
+    ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.grid.enabled")) {
+  var isGridTemplateSubgridValueEnabled =
+    IsCSSPropertyPrefEnabled("layout.css.grid-template-subgrid-value.enabled");
+
+  gCSSProperties["display"].other_values.push("grid", "inline-grid");
+  gCSSProperties["grid-auto-flow"] = {
+    domProp: "gridAutoFlow",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "row" ],
+    other_values: [
+      "column",
+      "column dense",
+      "row dense",
+      "dense column",
+      "dense row",
+      "dense",
+    ],
+    invalid_values: [
+      "",
+      "auto",
+      "none",
+      "10px",
+      "column row",
+      "dense row dense",
+    ]
+  };
+
+  gCSSProperties["grid-auto-columns"] = {
+    domProp: "gridAutoColumns",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: [
+      "40px",
+      "2em",
+      "2.5fr",
+      "12%",
+      "min-content",
+      "max-content",
+      "calc(20px + 10%)",
+      "minmax(20px, max-content)",
+      "minmax(min-content, auto)",
+      "minmax(auto, max-content)",
+      "m\\69nmax(20px, 4Fr)",
+      "MinMax(min-content, calc(20px + 10%))",
+    ],
+    invalid_values: [
+      "",
+      "normal",
+      "40ms",
+      "-40px",
+      "-12%",
+      "-2em",
+      "-2.5fr",
+      "minmax()",
+      "minmax(20px)",
+      "mİnmax(20px, 100px)",
+      "minmax(20px, 100px, 200px)",
+      "maxmin(100px, 20px)",
+      "minmax(min-content, minmax(30px, max-content))",
+    ]
+  };
+  gCSSProperties["grid-auto-rows"] = {
+    domProp: "gridAutoRows",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: gCSSProperties["grid-auto-columns"].initial_values,
+    other_values: gCSSProperties["grid-auto-columns"].other_values,
+    invalid_values: gCSSProperties["grid-auto-columns"].invalid_values
+  };
+
+  gCSSProperties["grid-template-columns"] = {
+    domProp: "gridTemplateColumns",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      "auto",
+      "40px",
+      "2.5fr",
+      "[normal] 40px [] auto [ ] 12%",
+      "[foo] 40px min-content [ bar ] calc(20px + 10%) max-content",
+      "40px min-content calc(20px + 10%) max-content",
+      "minmax(min-content, auto)",
+      "minmax(auto, max-content)",
+      "m\\69nmax(20px, 4Fr)",
+      "40px MinMax(min-content, calc(20px + 10%)) max-content",
+      "40px 2em",
+      "[] 40px [-foo] 2em [bar baz This\ is\ one\ ident]",
+      // TODO bug 978478: "[a] repeat(3, [b] 20px [c] 40px [d]) [e]",
+      "repeat(1, 20px)",
+      "repeat(1, [a] 20px)",
+      "[a] Repeat(4, [a] 20px [] auto [b c]) [d]",
+      "[a] 2.5fr Repeat(4, [a] 20px [] auto [b c]) [d]",
+      "[a] 2.5fr [z] Repeat(4, [a] 20px [] auto [b c]) [d]",
+      "[a] 2.5fr [z] Repeat(4, [a] 20px [] auto) [d]",
+      "[a] 2.5fr [z] Repeat(4, 20px [b c] auto [b c]) [d]",
+      "[a] 2.5fr [z] Repeat(4, 20px auto) [d]",
+      "repeat(auto-fill, 0)",
+      "[a] repeat( Auto-fill,1%)",
+      "[a] repeat(Auto-fit, 0)",
+      "repeat(Auto-fit,[] 1%)",
+      "repeat(auto-fit, [a] 1em) auto",
+      "[a] repeat( auto-fit,[a b] minmax(0,0) )",
+      "[a] 40px repeat(auto-fit,[a b] minmax(1px, 0) [])",
+      "[a] auto [b] repeat(auto-fit,[a b] minmax(1mm, 1%) [c]) [c] auto",
+    ],
+    invalid_values: [
+      "",
+      "normal",
+      "40ms",
+      "-40px",
+      "-12%",
+      "-2fr",
+      "(foo)",
+      "(inherit) 40px",
+      "(initial) 40px",
+      "(unset) 40px",
+      "(default) 40px",
+      "(6%) 40px",
+      "(5th) 40px",
+      "(foo() bar) 40px",
+      "(foo)) 40px",
+      "(foo) 40px",
+      "(foo) (bar) 40px",
+      "40px (foo) (bar)",
+      "minmax()",
+      "minmax(20px)",
+      "mİnmax(20px, 100px)",
+      "minmax(20px, 100px, 200px)",
+      "maxmin(100px, 20px)",
+      "minmax(min-content, minmax(30px, max-content))",
+      "repeat(0, 20px)",
+      "repeat(-3, 20px)",
+      "rêpeat(1, 20px)",
+      "repeat(1)",
+      "repeat(1, )",
+      "repeat(3px, 20px)",
+      "repeat(2.0, 20px)",
+      "repeat(2.5, 20px)",
+      "repeat(2, (foo))",
+      "repeat(2, foo)",
+      "40px calc(0px + rubbish)",
+      "repeat(1, repeat(1, 20px))",
+      "repeat(auto-fill, auto)",
+      "repeat(auto-fit,auto)",
+      "repeat(auto-fit,[])",
+      "repeat(auto-fill, 0) repeat(auto-fit, 0) ",
+      "repeat(auto-fit, 0) repeat(auto-fill, 0) ",
+      "[a] repeat(auto-fit, 0) repeat(auto-fit, 0) ",
+      "[a] repeat(auto-fill, 0) [a] repeat(auto-fill, 0) ",
+      "repeat(auto-fill, 0 0)",
+      "repeat(auto-fill, 0 [] 0)",
+      "repeat(auto-fill, min-content)",
+      "repeat(auto-fit,max-content)",
+      "repeat(auto-fit,minmax(auto,auto))",
+      "repeat(auto-fit,[] minmax(1px, min-content))",
+      "repeat(auto-fit,[a] minmax(1%, auto) [])",
+    ],
+    unbalanced_values: [
+      "(foo] 40px",
+    ]
+  };
+  if (isGridTemplateSubgridValueEnabled) {
+    gCSSProperties["grid-template-columns"].other_values.push(
+      // See https://bugzilla.mozilla.org/show_bug.cgi?id=981300
+      "[none auto subgrid min-content max-content foo] 40px",
+
+      "subgrid",
+      "subgrid [] [foo bar]",
+      "subgrid repeat(1, [])",
+      "subgrid Repeat(4, [a] [b c] [] [d])",
+      "subgrid repeat(auto-fill, [])",
+      "subgrid [x] repeat( Auto-fill, [a b c]) []",
+      "subgrid [x] repeat(auto-fill, []) [y z]"
+    );
+    gCSSProperties["grid-template-columns"].invalid_values.push(
+      "subgrid (foo) 40px",
+      "subgrid (foo 40px)",
+      "(foo) subgrid",
+      "subgrid rêpeat(1, ())",
+      "subgrid repeat(0, ())",
+      "subgrid repeat(-3, ())",
+      "subgrid repeat(2.0, ())",
+      "subgrid repeat(2.5, ())",
+      "subgrid repeat(3px, ())",
+      "subgrid repeat(1)",
+      "subgrid repeat(1, )",
+      "subgrid repeat(2, (40px))",
+      "subgrid repeat(2, foo)",
+      "subgrid repeat(1, repeat(1, []))",
+      "subgrid repeat(auto-fit,[])",
+      "subgrid [] repeat(auto-fit,[])",
+      "subgrid [a] repeat(auto-fit,[])",
+      "subgrid repeat(auto-fill, 1px)",
+      "subgrid repeat(auto-fill, 1px [])",
+      "subgrid repeat(Auto-fill, [a] [b c] [] [d])",
+      "subgrid repeat(auto-fill, []) repeat(auto-fill, [])"
+    );
+  }
+  gCSSProperties["grid-template-rows"] = {
+    domProp: "gridTemplateRows",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: gCSSProperties["grid-template-columns"].initial_values,
+    other_values: gCSSProperties["grid-template-columns"].other_values,
+    invalid_values: gCSSProperties["grid-template-columns"].invalid_values
+  };
+  gCSSProperties["grid-template-areas"] = {
+    domProp: "gridTemplateAreas",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      "''",
+      "'' ''",
+      "'1a-é_ .' \"b .\"",
+      "' Z\t\\aZ' 'Z Z'",
+      " '. . a b'  '. .a b' ",
+      "'a.b' '. . .'",
+      "'.' '..'",
+      "'...' '.'",
+      "'...-blah' '. .'",
+      "'.. ..' '.. ...'",
+    ],
+    invalid_values: [
+      "'a b' 'a/b'",
+      "'a . a'",
+      "'. a a' 'a a a'",
+      "'a a .' 'a a a'",
+      "'a a' 'a .'",
+      "'a a'\n'..'\n'a a'",
+    ]
+  };
+
+  gCSSProperties["grid-template"] = {
+    domProp: "gridTemplate",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "grid-template-areas",
+      "grid-template-rows",
+      "grid-template-columns",
+    ],
+    initial_values: [
+      "none",
+      "none / none",
+    ],
+    other_values: [
+      // <'grid-template-rows'> / <'grid-template-columns'>
+      "40px / 100px",
+      "[foo] 40px [bar] / [baz] 100px [fizz]",
+      " none/100px",
+      "40px/none",
+      // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
+      "'fizz'",
+      "[bar] 'fizz'",
+      "'fizz' / [foo] 40px",
+      "[bar] 'fizz' / [foo] 40px",
+      "'fizz' 100px / [foo] 40px",
+      "[bar] 'fizz' 100px / [foo] 40px",
+      "[bar] 'fizz' 100px [buzz] / [foo] 40px",
+      "[bar] 'fizz' 100px [buzz] \n [a] '.' 200px [b] / [foo] 40px",
+    ],
+    invalid_values: [
+      "[foo] [bar] 40px / 100px",
+      "[fizz] [buzz] 100px / 40px",
+      "[fizz] [buzz] 'foo' / 40px",
+      "'foo' / none"
+    ]
+  };
+  if (isGridTemplateSubgridValueEnabled) {
+    gCSSProperties["grid-template"].other_values.push(
+      "subgrid",
+      "subgrid/40px 20px",
+      "subgrid [foo] [] [bar baz] / 40px 20px",
+      "40px 20px/subgrid",
+      "40px 20px/subgrid  [foo] [] repeat(3, [a] [b]) [bar baz]",
+      "subgrid/subgrid",
+      "subgrid [foo] [] [bar baz]/subgrid [foo] [] [bar baz]"
+    );
+    gCSSProperties["grid-template"].invalid_values.push(
+      "subgrid []",
+      "subgrid [] / 'fizz'",
+      "subgrid / 'fizz'"
+    );
+  }
+
+  gCSSProperties["grid"] = {
+    domProp: "grid",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "grid-template-areas",
+      "grid-template-rows",
+      "grid-template-columns",
+      "grid-auto-flow",
+      "grid-auto-rows",
+      "grid-auto-columns",
+      "grid-column-gap",
+      "grid-row-gap",
+    ],
+    initial_values: [
+      "none",
+      "none / none",
+    ],
+    other_values: [
+      "column 40px",
+      "column dense auto",
+      "dense row minmax(min-content, 2fr)",
+      "row 40px / 100px",
+    ].concat(
+      gCSSProperties["grid-template"].other_values,
+      gCSSProperties["grid-auto-flow"].other_values
+    ),
+    invalid_values: [
+      "row column 40px",
+      "row -20px",
+      "row 200ms",
+      "row 40px 100px",
+    ].concat(
+      gCSSProperties["grid-template"].invalid_values,
+      gCSSProperties["grid-auto-flow"].invalid_values
+        .filter((v) => v != 'none')
+    )
+  };
+
+  var gridLineOtherValues = [
+    "foo",
+    "2",
+    "2 foo",
+    "foo 2",
+    "-3",
+    "-3 bar",
+    "bar -3",
+    "span 2",
+    "2 span",
+    "span foo",
+    "foo span",
+    "span 2 foo",
+    "span foo 2",
+    "2 foo span",
+    "foo 2 span",
+  ];
+  var gridLineInvalidValues = [
+    "",
+    "4th",
+    "span",
+    "inherit 2",
+    "2 inherit",
+    "20px",
+    "2 3",
+    "2.5",
+    "2.0",
+    "0",
+    "0 foo",
+    "span 0",
+    "2 foo 3",
+    "foo 2 foo",
+    "2 span foo",
+    "foo span 2",
+    "span -3",
+    "span -3 bar",
+    "span 2 span",
+    "span foo span",
+    "span 2 foo span",
+  ];
+
+  gCSSProperties["grid-column-start"] = {
+    domProp: "gridColumnStart",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: gridLineOtherValues,
+    invalid_values: gridLineInvalidValues
+  };
+  gCSSProperties["grid-column-end"] = {
+    domProp: "gridColumnEnd",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: gridLineOtherValues,
+    invalid_values: gridLineInvalidValues
+  };
+  gCSSProperties["grid-row-start"] = {
+    domProp: "gridRowStart",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: gridLineOtherValues,
+    invalid_values: gridLineInvalidValues
+  };
+  gCSSProperties["grid-row-end"] = {
+    domProp: "gridRowEnd",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: gridLineOtherValues,
+    invalid_values: gridLineInvalidValues
+  };
+
+  // The grid-column and grid-row shorthands take values of the form
+  //   <grid-line> [ / <grid-line> ]?
+  var gridColumnRowOtherValues = [].concat(gridLineOtherValues);
+  gridLineOtherValues.concat([ "auto" ]).forEach(function(val) {
+    gridColumnRowOtherValues.push(" foo / " + val);
+    gridColumnRowOtherValues.push(val + "/2");
+  });
+  var gridColumnRowInvalidValues = [
+    "foo, bar",
+    "foo / bar / baz",
+  ].concat(gridLineInvalidValues);
+  gridLineInvalidValues.forEach(function(val) {
+    gridColumnRowInvalidValues.push("span 3 / " + val);
+    gridColumnRowInvalidValues.push(val + " / foo");
+  });
+  gCSSProperties["grid-column"] = {
+    domProp: "gridColumn",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "grid-column-start",
+      "grid-column-end"
+    ],
+    initial_values: [ "auto", "auto / auto" ],
+    other_values: gridColumnRowOtherValues,
+    invalid_values: gridColumnRowInvalidValues
+  };
+  gCSSProperties["grid-row"] = {
+    domProp: "gridRow",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "grid-row-start",
+      "grid-row-end"
+    ],
+    initial_values: [ "auto", "auto / auto" ],
+    other_values: gridColumnRowOtherValues,
+    invalid_values: gridColumnRowInvalidValues
+  };
+
+  var gridAreaOtherValues = gridLineOtherValues.slice();
+  gridLineOtherValues.forEach(function(val) {
+    gridAreaOtherValues.push("foo / " + val);
+    gridAreaOtherValues.push(val + "/2/3");
+    gridAreaOtherValues.push("foo / bar / " + val + " / baz");
+  });
+  var gridAreaInvalidValues = [
+    "foo, bar",
+    "foo / bar / baz / fizz / buzz",
+    "default / foo / bar / baz",
+    "foo / initial / bar / baz",
+    "foo / bar / inherit / baz",
+    "foo / bar / baz / unset",
+  ].concat(gridLineInvalidValues);
+  gridLineInvalidValues.forEach(function(val) {
+    gridAreaInvalidValues.push("foo / " + val);
+    gridAreaInvalidValues.push("foo / bar / " + val);
+    gridAreaInvalidValues.push("foo / 4 / bar / " + val);
+  });
+
+  gCSSProperties["grid-area"] = {
+    domProp: "gridArea",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [
+      "grid-row-start",
+      "grid-column-start",
+      "grid-row-end",
+      "grid-column-end"
+    ],
+    initial_values: [
+      "auto",
+      "auto / auto",
+      "auto / auto / auto",
+      "auto / auto / auto / auto"
+    ],
+    other_values: gridAreaOtherValues,
+    invalid_values: gridAreaInvalidValues
+  };
+
+  gCSSProperties["grid-column-gap"] = {
+    domProp: "gridColumnGap",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0" ],
+    other_values: [ "2px", "1em", "calc(1px + 1em)" ],
+    invalid_values: [ "-1px", "2%", "auto", "none", "1px 1px", "calc(1%)" ],
+  };
+  gCSSProperties["grid-row-gap"] = {
+    domProp: "gridRowGap",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0" ],
+    other_values: [ "2px", "1em", "calc(1px + 1em)" ],
+    invalid_values: [ "-1px", "2%", "auto", "none", "1px 1px", "calc(1%)" ],
+  };
+  gCSSProperties["grid-gap"] = {
+    domProp: "gridGap",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "grid-column-gap", "grid-row-gap" ],
+    initial_values: [ "0", "0 0" ],
+    other_values: [ "1ch 0", "1em 1px", "calc(1px + 1ch)" ],
+    invalid_values: [ "-1px", "1px -1px", "1px 1px 1px", "inherit 1px",
+                      "1px 1%", "1px auto" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.display-contents.enabled")) {
+  gCSSProperties["display"].other_values.push("contents");
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.contain.enabled")) {
+  gCSSProperties["contain"] = {
+    domProp: "contain",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      "strict",
+      "layout",
+      "style",
+      "layout style",
+      "style layout",
+      "paint",
+      "layout paint",
+      "paint layout",
+      "style paint",
+      "paint style",
+      "layout style paint",
+      "layout paint style",
+      "style paint layout",
+      "paint style layout",
+    ],
+    invalid_values: [
+      "none strict",
+      "strict layout",
+      "strict layout style",
+      "layout strict",
+      "layout style strict",
+      "layout style paint strict",
+      "paint strict",
+      "style strict",
+      "paint paint",
+      "strict strict",
+      "auto",
+      "10px",
+      "0",
+    ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.image-orientation.enabled")) {
+  gCSSProperties["image-orientation"] = {
+    domProp: "imageOrientation",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [
+      "0deg",
+      "0grad",
+      "0rad",
+      "0turn",
+
+      // Rounded initial values.
+      "-90deg",
+      "15deg",
+      "360deg",
+    ],
+    other_values: [
+      "0deg flip",
+      "90deg",
+      "90deg flip",
+      "180deg",
+      "180deg flip",
+      "270deg",
+      "270deg flip",
+      "flip",
+      "from-image",
+
+      // Grad units.
+      "0grad flip",
+      "100grad",
+      "100grad flip",
+      "200grad",
+      "200grad flip",
+      "300grad",
+      "300grad flip",
+
+      // Radian units.
+      "0rad flip",
+      "1.57079633rad",
+      "1.57079633rad flip",
+      "3.14159265rad",
+      "3.14159265rad flip",
+      "4.71238898rad",
+      "4.71238898rad flip",
+
+      // Turn units.
+      "0turn flip",
+      "0.25turn",
+      "0.25turn flip",
+      "0.5turn",
+      "0.5turn flip",
+      "0.75turn",
+      "0.75turn flip",
+
+      // Rounded values.
+      "-45deg flip",
+      "65deg flip",
+      "400deg flip",
+    ],
+    invalid_values: [
+      "none",
+      "0deg none",
+      "flip 0deg",
+      "flip 0deg",
+      "0",
+      "0 flip",
+      "flip 0",
+      "0deg from-image",
+      "from-image 0deg",
+      "flip from-image",
+      "from-image flip",
+    ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.osx-font-smoothing.enabled")) {
+  gCSSProperties["-moz-osx-font-smoothing"] = {
+    domProp: "MozOsxFontSmoothing",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: [ "grayscale" ],
+    invalid_values: [ "none", "subpixel-antialiased", "antialiased" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.sticky.enabled")) {
+  gCSSProperties["position"].other_values.push("sticky");
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.mix-blend-mode.enabled")) {
+  gCSSProperties["mix-blend-mode"] = {
+    domProp: "mixBlendMode",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: ["multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn",
+        "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"],
+    invalid_values: []
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.isolation.enabled")) {
+  gCSSProperties["isolation"] = {
+    domProp: "isolation",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: ["isolate"],
+    invalid_values: []
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.background-blend-mode.enabled")) {
+  gCSSProperties["background-blend-mode"] = {
+    domProp: "backgroundBlendMode",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: [ "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn",
+      "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity" ],
+    invalid_values: ["none", "10px", "multiply multiply"]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.object-fit-and-position.enabled")) {
+  gCSSProperties["object-fit"] = {
+    domProp: "objectFit",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "fill" ],
+    other_values: [ "contain", "cover", "none", "scale-down" ],
+    invalid_values: [ "auto", "5px", "100%" ]
+  };
+  gCSSProperties["object-position"] = {
+    domProp: "objectPosition",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "50% 50%", "50%", "center", "center center" ],
+    other_values: [
+      "calc(20px)",
+      "calc(20px) 10px",
+      "10px calc(20px)",
+      "calc(20px) 25%",
+      "25% calc(20px)",
+      "calc(20px) calc(20px)",
+      "calc(20px + 1em) calc(20px / 2)",
+      "calc(20px + 50%) calc(50% - 10px)",
+      "calc(-20px) calc(-50%)",
+      "calc(-20%) calc(-50%)",
+      "0px 0px",
+      "right 20px top 60px",
+      "right 20px bottom 60px",
+      "left 20px top 60px",
+      "left 20px bottom 60px",
+      "right -50px top -50px",
+      "left -50px bottom -50px",
+      "right 20px top -50px",
+      "right -20px top 50px",
+      "right 3em bottom 10px",
+      "bottom 3em right 10px",
+      "top 3em right 10px",
+      "left 15px",
+      "10px top",
+      "left top 15px",
+      "left 10px top",
+      "left 20%",
+      "right 20%"
+    ],
+    invalid_values: [ "center 10px center 4px", "center 10px center",
+                      "top 20%", "bottom 20%", "50% left", "top 50%",
+                      "50% bottom 10%", "right 10% 50%", "left right",
+                      "top bottom", "left 10% right",
+                      "top 20px bottom 20px", "left left", "20 20" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.overflow-clip-box.enabled")) {
+  gCSSProperties["overflow-clip-box"] = {
+    domProp: "overflowClipBox",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "padding-box" ],
+    other_values: [ "content-box" ],
+    invalid_values: [ "none", "auto", "border-box", "0" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.box-decoration-break.enabled")) {
+  gCSSProperties["box-decoration-break"] = {
+    domProp: "boxDecorationBreak",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "slice" ],
+    other_values: [ "clone" ],
+    invalid_values: [ "auto",  "none",  "1px" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.scroll-behavior.property-enabled")) {
+  gCSSProperties["scroll-behavior"] = {
+    domProp: "scrollBehavior",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "auto" ],
+    other_values: [ "smooth" ],
+    invalid_values: [ "none",  "1px" ]
+  };
+}
+
+if (IsCSSPropertyPrefEnabled("layout.css.scroll-snap.enabled")) {
+  gCSSProperties["scroll-snap-coordinate"] = {
+    domProp: "scrollSnapCoordinate",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "25% 25%", "top", "0px 100px, 10em 50%",
+                    "top left, top right, bottom left, bottom right, center",
+                    "calc(2px)",
+                    "calc(50%)",
+                    "calc(3*25px)",
+                    "calc(3*25px) 5px",
+                    "5px calc(3*25px)",
+                    "calc(20%) calc(3*25px)",
+                    "calc(25px*3)",
+                    "calc(3*25px + 50%)",
+                    "calc(20%) calc(3*25px), center"],
+    invalid_values: [ "auto", "default" ]
+  }
+  gCSSProperties["scroll-snap-destination"] = {
+    domProp: "scrollSnapDestination",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0px 0px" ],
+    other_values: [ "25% 25%", "6px 5px", "20% 3em", "0in 1in",
+                    "top", "right", "top left", "top right", "center",
+                    "calc(2px)",
+                    "calc(50%)",
+                    "calc(3*25px)",
+                    "calc(3*25px) 5px",
+                    "5px calc(3*25px)",
+                    "calc(20%) calc(3*25px)",
+                    "calc(25px*3)",
+                    "calc(3*25px + 50%)"],
+    invalid_values: [ "auto", "none", "default" ]
+  }
+  gCSSProperties["scroll-snap-points-x"] = {
+    domProp: "scrollSnapPointsX",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "repeat(100%)", "repeat(120px)", "repeat(calc(3*25px))" ],
+    invalid_values: [ "auto", "1px", "left", "rgb(1,2,3)" ]
+  }
+  gCSSProperties["scroll-snap-points-y"] = {
+    domProp: "scrollSnapPointsY",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "repeat(100%)", "repeat(120px)", "repeat(calc(3*25px))" ],
+    invalid_values: [ "auto", "1px", "top", "rgb(1,2,3)" ]
+  }
+  gCSSProperties["scroll-snap-type"] = {
+    domProp: "scrollSnapType",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "scroll-snap-type-x", "scroll-snap-type-y" ],
+    initial_values: [ "none" ],
+    other_values: [ "mandatory", "proximity" ],
+    invalid_values: [ "auto",  "1px" ]
+  };
+  gCSSProperties["scroll-snap-type-x"] = {
+    domProp: "scrollSnapTypeX",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: ["mandatory", "proximity"],
+    invalid_values: [ "auto",  "1px" ]
+  };
+  gCSSProperties["scroll-snap-type-y"] = {
+    domProp: "scrollSnapTypeY",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: ["mandatory", "proximity"],
+    invalid_values: [ "auto",  "1px" ]
+  };
+}
+
+}
diff --git a/reviewboard/diffviewer/tests.py b/reviewboard/diffviewer/tests.py
index 9cbfcc1a8810df8fdab85ef946d6268051aa5dfb..4c1163feff47d5d871850e7f532ac5acb859a115 100644
--- a/reviewboard/diffviewer/tests.py
+++ b/reviewboard/diffviewer/tests.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 
 import bz2
+import os
 
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.http import HttpResponse
@@ -927,6 +928,90 @@ class DiffParserTest(TestCase):
             ]
         )
 
+    def test_move_detection_similar_blocks(self):
+        """Testing diff viewer move detection with multiple blocks of similar
+        moved lines
+        """
+        # See https://hellosplat.com/s/beanbag/tickets/4371/ for a description
+        # of the bug.
+        testdata_path = os.path.abspath(
+            os.path.join(__file__, '..', 'testdata', 'move_detection'))
+
+        with open(os.path.join(testdata_path, 'bug-4371-old.js'), 'r') as fp:
+            old = fp.readlines()
+
+        with open(os.path.join(testdata_path, 'bug-4371-new.js'), 'r') as fp:
+            new = fp.readlines()
+
+        self._test_move_detection(
+            old,
+            new,
+            [{
+                2633: 16,
+                2634: 17,
+                2635: 18,
+                2636: 19,
+                2637: 20,
+                2638: 21,
+                2639: 22,
+                2640: 23,
+                2642: 24,
+                2643: 25,
+                2644: 26,
+                2645: 27,
+                2646: 28,
+                2649: 31,
+                2650: 32,
+                2651: 33,
+                2652: 34,
+                2653: 35,
+                2654: 36,
+                2655: 37,
+                2656: 38,
+                2657: 39,
+                2658: 40,
+                2659: 41,
+                2660: 42,
+                2661: 43,
+                2662: 44,
+                2663: 45,
+                2664: 46,
+                2665: 47,
+            }],
+            [{
+                16: 2633,
+                17: 2634,
+                18: 2635,
+                19: 2636,
+                20: 2637,
+                21: 2638,
+                22: 2639,
+                23: 2640,
+                24: 2642,
+                25: 2643,
+                26: 2644,
+                27: 2645,
+                28: 2646,
+                31: 2649,
+                32: 2650,
+                33: 2651,
+                34: 2652,
+                35: 2653,
+                36: 2654,
+                37: 2655,
+                38: 2656,
+                39: 2657,
+                40: 2658,
+                41: 2659,
+                42: 2660,
+                43: 2661,
+                44: 2662,
+                45: 2663,
+                46: 2664,
+                47: 2665,
+            }]
+        )
+
     def test_line_counts(self):
         """Testing DiffParser with insert/delete line counts"""
         diff = (
