Bug 1560055 part 3. Switch various "width" and "height" attributes on HTML elements to mostly follow the spec for parsing the attribute. r=mccr8
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 28 Jun 2019 20:56:55 +0000
changeset 543446 f094b8e00c4c50160faad3330de66d912b1cc4c4
parent 543445 0a2e02d520c94d81c523dab7425d98f54bb3cc5c
child 543447 98d562c4a456194b340cf25181a7c26eb6805415
push id2131
push userffxbld-merge
push dateMon, 26 Aug 2019 18:30:20 +0000
treeherdermozilla-release@b19ffb3ca153 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1560055, 1561440
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1560055 part 3. Switch various "width" and "height" attributes on HTML elements to mostly follow the spec for parsing the attribute. r=mccr8 The spec allows non-integer values, but we don't have a good way to store them in nsAttrValue yet. See https://bugzilla.mozilla.org/show_bug.cgi?id=1561440 HTMLTableCellElement::MapAttributesIntoRule can now call MapImageSizeAttributesInto instead of manually mapping width and height, because 0 values (which it was excluding before) are now excluded at attribute parse time. For 'width' on HTMLTableElement I kept our old behavior for 0, which matches the spec but not Safari or Chrome. For 'height' on HTMLTableElement I kept our old behavior for 0, which matches Safari and Chrome but not the spec. https://github.com/whatwg/html/issues/4715 tracks a possible spec change. Same thing for 'height' on HTMLTableRowElement. Same thing for 'width' on HTMLTableColElement. The ParseImageAttribute call in HTMLMediaElement is not needed, because HTMLAudioElement does not map any of those to style and HTMLVideoElement only maps width/height, which it already parses. Differential Revision: https://phabricator.services.mozilla.com/D36127
dom/html/HTMLHRElement.cpp
dom/html/HTMLIFrameElement.cpp
dom/html/HTMLInputElement.cpp
dom/html/HTMLMarqueeElement.cpp
dom/html/HTMLMediaElement.cpp
dom/html/HTMLTableCellElement.cpp
dom/html/HTMLTableColElement.cpp
dom/html/HTMLTableElement.cpp
dom/html/HTMLTableRowElement.cpp
dom/html/HTMLTableSectionElement.cpp
dom/html/HTMLVideoElement.cpp
dom/html/nsGenericHTMLElement.cpp
testing/web-platform/meta/html/rendering/dimension-attributes.html.ini
testing/web-platform/tests/html/rendering/dimension-attributes.html
--- a/dom/html/HTMLHRElement.cpp
+++ b/dom/html/HTMLHRElement.cpp
@@ -29,17 +29,17 @@ bool HTMLHRElement::ParseAttribute(int32
   static const nsAttrValue::EnumTable kAlignTable[] = {
       {"left", NS_STYLE_TEXT_ALIGN_LEFT},
       {"right", NS_STYLE_TEXT_ALIGN_RIGHT},
       {"center", NS_STYLE_TEXT_ALIGN_CENTER},
       {nullptr, 0}};
 
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::width) {
-      return aResult.ParseSpecialIntValue(aValue);
+      return aResult.ParseHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::size) {
       return aResult.ParseIntWithBounds(aValue, 1, 1000);
     }
     if (aAttribute == nsGkAtoms::align) {
       return aResult.ParseEnumValue(aValue, kAlignTable, false);
     }
     if (aAttribute == nsGkAtoms::color) {
--- a/dom/html/HTMLIFrameElement.cpp
+++ b/dom/html/HTMLIFrameElement.cpp
@@ -85,20 +85,20 @@ bool HTMLIFrameElement::ParseAttribute(i
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::marginwidth) {
       return aResult.ParseNonNegativeIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::marginheight) {
       return aResult.ParseNonNegativeIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::width) {
-      return aResult.ParseSpecialIntValue(aValue);
+      return aResult.ParseHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::height) {
-      return aResult.ParseSpecialIntValue(aValue);
+      return aResult.ParseHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::frameborder) {
       return ParseFrameborderValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::scrolling) {
       return ParseScrollingValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::align) {
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -5180,20 +5180,20 @@ bool HTMLInputElement::ParseAttribute(in
         // "text" in it.
         aResult.ParseEnumValue(aValue, kInputDefaultType, false,
                                kInputDefaultType);
       }
 
       return true;
     }
     if (aAttribute == nsGkAtoms::width) {
-      return aResult.ParseSpecialIntValue(aValue);
+      return aResult.ParseHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::height) {
-      return aResult.ParseSpecialIntValue(aValue);
+      return aResult.ParseHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::maxlength) {
       return aResult.ParseNonNegativeIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::minlength) {
       return aResult.ParseNonNegativeIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::size) {
--- a/dom/html/HTMLMarqueeElement.cpp
+++ b/dom/html/HTMLMarqueeElement.cpp
@@ -79,17 +79,17 @@ void HTMLMarqueeElement::GetDirection(ns
 
 bool HTMLMarqueeElement::ParseAttribute(int32_t aNamespaceID,
                                         nsAtom* aAttribute,
                                         const nsAString& aValue,
                                         nsIPrincipal* aMaybeScriptedPrincipal,
                                         nsAttrValue& aResult) {
   if (aNamespaceID == kNameSpaceID_None) {
     if ((aAttribute == nsGkAtoms::width) || (aAttribute == nsGkAtoms::height)) {
-      return aResult.ParseSpecialIntValue(aValue);
+      return aResult.ParseHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::bgcolor) {
       return aResult.ParseColor(aValue);
     }
     if (aAttribute == nsGkAtoms::behavior) {
       return aResult.ParseEnumValue(aValue, kBehaviorTable, false,
                                     kDefaultBehavior);
     }
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3957,19 +3957,16 @@ bool HTMLMediaElement::ParseAttribute(in
   static const nsAttrValue::EnumTable kPreloadTable[] = {
       {"", HTMLMediaElement::PRELOAD_ATTR_EMPTY},
       {"none", HTMLMediaElement::PRELOAD_ATTR_NONE},
       {"metadata", HTMLMediaElement::PRELOAD_ATTR_METADATA},
       {"auto", HTMLMediaElement::PRELOAD_ATTR_AUTO},
       {nullptr, 0}};
 
   if (aNamespaceID == kNameSpaceID_None) {
-    if (ParseImageAttribute(aAttribute, aValue, aResult)) {
-      return true;
-    }
     if (aAttribute == nsGkAtoms::crossorigin) {
       ParseCORSValue(aValue, aResult);
       return true;
     }
     if (aAttribute == nsGkAtoms::preload) {
       return aResult.ParseEnumValue(aValue, kPreloadTable, false);
     }
   }
--- a/dom/html/HTMLTableCellElement.cpp
+++ b/dom/html/HTMLTableCellElement.cpp
@@ -131,20 +131,20 @@ bool HTMLTableCellElement::ParseAttribut
       aResult.ParseClampedNonNegativeInt(aValue, 1, 0, MAX_ROWSPAN);
       // quirks mode does not honor the special html 4 value of 0
       if (aResult.GetIntegerValue() == 0 && InNavQuirksMode(OwnerDoc())) {
         aResult.SetTo(1, &aValue);
       }
       return true;
     }
     if (aAttribute == nsGkAtoms::height) {
-      return aResult.ParseSpecialIntValue(aValue);
+      return aResult.ParseNonzeroHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::width) {
-      return aResult.ParseSpecialIntValue(aValue);
+      return aResult.ParseNonzeroHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::align) {
       return ParseTableCellHAlignValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::bgcolor) {
       return aResult.ParseColor(aValue);
     }
     if (aAttribute == nsGkAtoms::scope) {
@@ -158,44 +158,18 @@ bool HTMLTableCellElement::ParseAttribut
   return nsGenericHTMLElement::ParseBackgroundAttribute(
              aNamespaceID, aAttribute, aValue, aResult) ||
          nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aMaybeScriptedPrincipal, aResult);
 }
 
 void HTMLTableCellElement::MapAttributesIntoRule(
     const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
-  // width: value
-  if (!aDecls.PropertyIsSet(eCSSProperty_width)) {
-    const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
-    if (value && value->Type() == nsAttrValue::eInteger) {
-      if (value->GetIntegerValue() > 0)
-        aDecls.SetPixelValue(eCSSProperty_width,
-                             (float)value->GetIntegerValue());
-      // else 0 implies auto for compatibility.
-    } else if (value && value->Type() == nsAttrValue::ePercent) {
-      if (value->GetPercentValue() > 0.0f)
-        aDecls.SetPercentValue(eCSSProperty_width, value->GetPercentValue());
-      // else 0 implies auto for compatibility
-    }
-  }
-  // height: value
-  if (!aDecls.PropertyIsSet(eCSSProperty_height)) {
-    const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height);
-    if (value && value->Type() == nsAttrValue::eInteger) {
-      if (value->GetIntegerValue() > 0)
-        aDecls.SetPixelValue(eCSSProperty_height,
-                             (float)value->GetIntegerValue());
-      // else 0 implies auto for compatibility.
-    } else if (value && value->Type() == nsAttrValue::ePercent) {
-      if (value->GetPercentValue() > 0.0f)
-        aDecls.SetPercentValue(eCSSProperty_height, value->GetPercentValue());
-      // else 0 implies auto for compatibility
-    }
-  }
+  MapImageSizeAttributesInto(aAttributes, aDecls);
+
   if (!aDecls.PropertyIsSet(eCSSProperty_white_space)) {
     // nowrap: enum
     if (aAttributes->GetAttr(nsGkAtoms::nowrap)) {
       // See if our width is not a nonzero integer width.
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
       nsCompatibility mode = aDecls.Document()->GetCompatibilityMode();
       if (!value || value->Type() != nsAttrValue::eInteger ||
           value->GetIntegerValue() == 0 || eCompatibility_NavQuirks != mode) {
--- a/dom/html/HTMLTableColElement.cpp
+++ b/dom/html/HTMLTableColElement.cpp
@@ -39,17 +39,20 @@ bool HTMLTableColElement::ParseAttribute
       return aResult.ParseSpecialIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::span) {
       /* protection from unrealistic large colspan values */
       aResult.ParseClampedNonNegativeInt(aValue, 1, 1, MAX_COLSPAN);
       return true;
     }
     if (aAttribute == nsGkAtoms::width) {
-      return aResult.ParseSpecialIntValue(aValue);
+      // Spec says to use ParseNonzeroHTMLDimension, but Chrome and Safari both
+      // allow 0, and we did all along too, so keep that behavior.  See
+      // https://github.com/whatwg/html/issues/4717
+      return aResult.ParseHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::align) {
       return ParseTableCellHAlignValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::valign) {
       return ParseTableVAlignValue(aValue, aResult);
     }
   }
--- a/dom/html/HTMLTableElement.cpp
+++ b/dom/html/HTMLTableElement.cpp
@@ -811,28 +811,23 @@ bool HTMLTableElement::ParseAttribute(in
   /* ignore summary, just a string */
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::cellspacing ||
         aAttribute == nsGkAtoms::cellpadding ||
         aAttribute == nsGkAtoms::border) {
       return aResult.ParseNonNegativeIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::height) {
-      return aResult.ParseSpecialIntValue(aValue);
+      // Purposeful spec violation (spec says to use ParseNonzeroHTMLDimension)
+      // to stay compatible with our old behavior and other browsers.  See
+      // https://github.com/whatwg/html/issues/4715
+      return aResult.ParseHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::width) {
-      if (aResult.ParseSpecialIntValue(aValue)) {
-        // treat 0 width as auto
-        nsAttrValue::ValueType type = aResult.Type();
-        return !(
-            (type == nsAttrValue::eInteger && aResult.GetIntegerValue() == 0) ||
-            (type == nsAttrValue::ePercent &&
-             aResult.GetPercentValue() == 0.0f));
-      }
-      return false;
+      return aResult.ParseNonzeroHTMLDimension(aValue);
     }
 
     if (aAttribute == nsGkAtoms::align) {
       return ParseTableHAlignValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::bgcolor ||
         aAttribute == nsGkAtoms::bordercolor) {
       return aResult.ParseColor(aValue);
--- a/dom/html/HTMLTableRowElement.cpp
+++ b/dom/html/HTMLTableRowElement.cpp
@@ -201,17 +201,19 @@ bool HTMLTableRowElement::ParseAttribute
    * ch
    */
 
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::charoff) {
       return aResult.ParseIntWithBounds(aValue, 0);
     }
     if (aAttribute == nsGkAtoms::height) {
-      return aResult.ParseSpecialIntValue(aValue);
+      // Per spec should be ParseNonzeroHTMLDimension, but no browsers do that.
+      // See https://github.com/whatwg/html/issues/4716
+      return aResult.ParseHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::width) {
       return aResult.ParseSpecialIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::align) {
       return ParseTableCellHAlignValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::bgcolor) {
--- a/dom/html/HTMLTableSectionElement.cpp
+++ b/dom/html/HTMLTableSectionElement.cpp
@@ -120,17 +120,21 @@ bool HTMLTableSectionElement::ParseAttri
   if (aNamespaceID == kNameSpaceID_None) {
     /* ignore these attributes, stored simply as strings
        ch
     */
     if (aAttribute == nsGkAtoms::charoff) {
       return aResult.ParseIntWithBounds(aValue, 0);
     }
     if (aAttribute == nsGkAtoms::height) {
-      return aResult.ParseSpecialIntValue(aValue);
+      // Per HTML spec there should be nothing special here, but all browsers
+      // implement height mapping to style.  See
+      // <https://github.com/whatwg/html/issues/4718>.  All browsers allow 0, so
+      // keep doing that.
+      return aResult.ParseHTMLDimension(aValue);
     }
     if (aAttribute == nsGkAtoms::align) {
       return ParseTableCellHAlignValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::bgcolor) {
       return aResult.ParseColor(aValue);
     }
     if (aAttribute == nsGkAtoms::valign) {
--- a/dom/html/HTMLVideoElement.cpp
+++ b/dom/html/HTMLVideoElement.cpp
@@ -146,17 +146,17 @@ void HTMLVideoElement::Invalidate(bool a
   }
 }
 
 bool HTMLVideoElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
                                       const nsAString& aValue,
                                       nsIPrincipal* aMaybeScriptedPrincipal,
                                       nsAttrValue& aResult) {
   if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height) {
-    return aResult.ParseSpecialIntValue(aValue);
+    return aResult.ParseHTMLDimension(aValue);
   }
 
   return HTMLMediaElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                           aMaybeScriptedPrincipal, aResult);
 }
 
 void HTMLVideoElement::MapAttributesIntoRule(
     const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1034,17 +1034,17 @@ bool nsGenericHTMLElement::ParseDivAlign
                                               nsAttrValue& aResult) {
   return aResult.ParseEnumValue(aString, kDivAlignTable, false);
 }
 
 bool nsGenericHTMLElement::ParseImageAttribute(nsAtom* aAttribute,
                                                const nsAString& aString,
                                                nsAttrValue& aResult) {
   if ((aAttribute == nsGkAtoms::width) || (aAttribute == nsGkAtoms::height)) {
-    return aResult.ParseSpecialIntValue(aString);
+    return aResult.ParseHTMLDimension(aString);
   }
   if ((aAttribute == nsGkAtoms::hspace) || (aAttribute == nsGkAtoms::vspace) ||
       (aAttribute == nsGkAtoms::border)) {
     return aResult.ParseIntWithBounds(aString, 0);
   }
   return false;
 }
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/rendering/dimension-attributes.html.ini
@@ -0,0 +1,649 @@
+[dimension-attributes.html]
+  [<hr width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<hr width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<hr width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<hr width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<hr width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<hr width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<hr width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<iframe width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<input width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<marquee width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<video width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<object width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<embed width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<img width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<td width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<table width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<col width="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<col width="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<col width="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<col width="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<col width="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<col width="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<col width="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<col width="0"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4717
+
+  [<col width="+0"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4717
+
+  [<col width="0%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4717
+
+  [<col width="+0%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4717
+
+  [<col width="0px"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4717
+
+  [<iframe height="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe height="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe height="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe height="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe height="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<iframe height="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<iframe height="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input height="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input height="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input height="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input height="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input height="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<input height="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<input height="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee height="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee height="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee height="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee height="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee height="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<marquee height="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<marquee height="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video height="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video height="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video height="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video height="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video height="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<video height="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<video height="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object height="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object height="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object height="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object height="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object height="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<object height="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<object height="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed height="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed height="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed height="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed height="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed height="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<embed height="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<embed height="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img height="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img height="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img height="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img height="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img height="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<img height="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<img height="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td height="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td height="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td height="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td height="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td height="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<td height="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<td height="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table height="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table height="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table height="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table height="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table height="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<table height="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table height="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<table height="0"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4715
+
+  [<table height="+0"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4715
+
+  [<table height="0%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4715
+
+  [<table height="+0%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4715
+
+  [<table height="0px"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4715
+
+  [<tr height="200.25"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<tr height="200.25in"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<tr height="   +200.25in    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<tr height="200.25%"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<tr height="200.%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4736
+
+  [<tr height="   +200.25%    "> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<tr height="   +200.25%abc"> mapping]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1561440
+
+  [<tr height="0"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4716
+
+  [<tr height="+0"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4716
+
+  [<tr height="0%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4716
+
+  [<tr height="+0%"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4716
+
+  [<tr height="0px"> mapping]
+    expected: FAIL
+    bug: https://github.com/whatwg/html/issues/4716
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/dimension-attributes.html
@@ -0,0 +1,180 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test handling of attributes that map to dimension properties</title>
+<link rel="help"
+      href="https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-dimension-property">
+<link rel="help"
+      href="https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-dimension-property-(ignoring-zero)">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+<!-- We need a place to put our elements so they're bound to a document and
+     have computed style, but we don't want percentages resolved to lengths,
+     so need to make sure they have no CSS boxes -->
+<div id="container" style="display: none"></div>
+<script>
+ /*
+  * This test tests
+  *
+  * https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-dimension-property
+  * and
+  * https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-dimension-property-(ignoring-zero)
+  * for various elements and various values.
+  */
+
+ /*
+  * Array of input/output pairs.  The input is the string to use as the
+  * attribute value.  The output is the string expected as the computed style
+  * for the relevant CSS property.
+  */
+const valid_values = [
+  // Valid values
+  [ "200", "200px" ],
+  [ "1007", "1007px" ],
+  [ "   00523   ", "523px" ],
+  [ "200.25", "200.25px" ],
+  [ "200.", "200px" ],
+  [ "+200", "200px" ],
+  [ "200in", "200px" ],
+  [ "200.25in", "200.25px" ],
+  [ "   +200in    ", "200px" ],
+  [ "   +200.25in    ", "200.25px" ],
+  [ "200 %", "200px" ],
+  [ "200 abc", "200px" ],
+  [ "200%", "200%" ],
+  [ "200%abc", "200%" ],
+  [ "+200%", "200%" ],
+  [ "200.25%", "200.25%" ],
+  // https://github.com/whatwg/html/issues/4736 tracks the fact that "200.%"
+  // should probably be mapped as "200%", not "200px".
+  [ "200.%", "200px" ],
+  [ "   +200.25%    ", "200.25%" ],
+  [ "   +200.25%abc", "200.25%" ],
+];
+
+ /*
+  * Values that are only valid for the not-ignoring-zero case.
+  */
+const zero_values = [
+  [ "0", "0px" ],
+  [ "+0", "0px" ],
+  [ "0%", "0%" ],
+  [ "+0%", "0%" ],
+  [ "0px", "0px" ],
+];
+
+ /*
+  * Array of invalid values.  These should lead to the default value of the
+  * relevant CSS property.
+  */
+const invalid_values = [
+  "-0",
+  "-0%",
+  "-200",
+  "-200px",
+  "   -200",
+  "+-200",
+  "-+200",
+  "-200%"
+];
+
+const valid_values_with_0 =
+      valid_values.concat(zero_values);
+const invalid_values_with_0 =
+      invalid_values.concat(zero_values.map((v) => v[0]));
+
+function newElem(name) {
+  return () => document.createElement(name);
+}
+
+function newImageInput() {
+  return () => {
+    var elem = newElem("input")();
+    elem.type = "image";
+    return elem;
+  }
+}
+
+/*
+ * Array of tests.  Each test consists of the following information:
+ *
+ * 1) An element creation function.
+ * 2) The name of the attribute to set (and CSS property to get).
+ * 3) A boolean indicating whether 0 is a valid value for the dimension
+ *    attribute.
+ */
+const tests = [
+  [ newElem("hr"), "width", true ],
+  [ newElem("iframe"), "width", true ],
+  [ newElem("iframe"), "height", true ],
+  [ newImageInput(), "width", true ],
+  [ newImageInput(), "height", true ],
+  [ newElem("marquee"), "width", true ],
+  [ newElem("marquee"), "height", true ],
+  [ newElem("video"), "width", true ],
+  [ newElem("video"), "height", true ],
+  [ newElem("object"), "width", true ],
+  [ newElem("object"), "height", true ],
+  [ newElem("embed"), "width", true ],
+  [ newElem("embed"), "height", true ],
+  [ newElem("img"), "width", true ],
+  [ newElem("img"), "height", true ],
+  [ newElem("td"), "width", false ],
+  [ newElem("td"), "height", false ],
+  // https://github.com/whatwg/html/issues/4715 tracks the fact that for
+  // <table width> and <table height> the "0 is valid" boolean should probably
+  // be true.
+  [ newElem("table"), "width", false ],
+  [ newElem("table"), "height", false ],
+  // https://github.com/whatwg/html/issues/4716 tracks the fact that for the
+  // <tr height> case that "0 is valid" boolean should probably be true.
+  [ newElem("tr"), "height", false ],
+  // https://github.com/whatwg/html/issues/4717 tracks the fact that for the
+  // <col width> case that "0 is valid" boolean should probably be true.
+  [ newElem("col"), "width", false ],
+];
+
+
+function style(element) {
+  return element.ownerDocument.defaultView.getComputedStyle(element);
+}
+
+const container = document.getElementById("container");
+
+for (let [ctor, attr, zero_allowed] of tests) {
+  let valid, invalid;
+  if (zero_allowed) {
+    valid = valid_values_with_0;
+    invalid = invalid_values;
+  } else {
+    valid = valid_values;
+    invalid = invalid_values_with_0;
+  }
+  for (let [value, result] of valid) {
+    let elem = ctor();
+    test(function() {
+      this.add_cleanup(() => elem.remove());
+      elem.setAttribute(attr, value);
+      assert_equals(elem.getAttribute(attr), value);
+      container.appendChild(elem);
+      assert_equals(style(elem)[attr], result);
+    }, `<${elem.localName} ${attr}="${value}"> mapping`);
+  }
+
+  let default_elem = ctor();
+  container.appendChild(default_elem);
+  let defaultVal = style(default_elem)[attr];
+  default_elem.remove();
+  for (let value of invalid) {
+    let elem = ctor();
+    test(function() {
+      this.add_cleanup(() => elem.remove());
+      elem.setAttribute(attr, value);
+      assert_equals(elem.getAttribute(attr), value);
+      container.appendChild(elem);
+      assert_equals(style(elem)[attr], defaultVal);
+    }, `<${elem.localName} ${attr}="${value}"> mapping`);
+  }
+}
+</script>
+</body>