| <!doctype html> | |
| <meta charset=utf-8> | |
| <title>Test handling of attributes that map to dimension properties</title> | |
| <meta name="timeout" content="long"> | |
| <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.7", "200.7px" ], | |
| [ "200.", "200px" ], | |
| [ "200in", "200px" ], | |
| [ "200.25in", "200.25px" ], | |
| [ "200 %", "200px" ], | |
| [ "200 abc", "200px" ], | |
| [ "200%", "200%" ], | |
| [ "200%abc", "200%" ], | |
| [ "200.25%", "200.25%" ], | |
| [ "200.%", "200%" ], | |
| [ "20.25e2", "20.25px" ], | |
| [ "20.25E2", "20.25px" ], | |
| ]; | |
| /* | |
| * Values that are only valid for the not-ignoring-zero case. | |
| */ | |
| const zero_values = [ | |
| [ "0", "0px" ], | |
| [ "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%", | |
| "+200", | |
| " +200in ", | |
| " +200.25in ", | |
| "+200%", | |
| " +200.25% ", | |
| " +200.25%abc", | |
| "+0", | |
| "+0%", | |
| ".", | |
| ".%", | |
| ".x", | |
| ".5", | |
| ".5%" | |
| ]; | |
| 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; | |
| } | |
| } | |
| function newImgSource() { | |
| return () => { | |
| var elem = newElem("source")(); | |
| elem.setAttribute("srcset", "/images/green-100x50.png"); | |
| 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 | |
| * 3) The name of the CSS property to get. | |
| * 4) A boolean indicating whether 0 is a valid value for the dimension | |
| * attribute. | |
| */ | |
| const tests = [ | |
| [ newElem("hr"), "width", "width", true ], | |
| [ newElem("iframe"), "width", "width", true ], | |
| [ newElem("iframe"), "height", "height", true ], | |
| [ newImageInput(), "width", "width", true ], | |
| [ newImageInput(), "height", "height", true ], | |
| [ newElem("marquee"), "width", "width", true ], | |
| [ newElem("marquee"), "height", "height", true ], | |
| [ newElem("video"), "width", "width", true ], | |
| [ newElem("video"), "height", "height", true ], | |
| [ newElem("object"), "width", "width", true ], | |
| [ newElem("object"), "height", "height", true ], | |
| [ newElem("embed"), "width", "width", true ], | |
| [ newElem("embed"), "height", "height", true ], | |
| [ newElem("img"), "width", "width", true ], | |
| [ newElem("img"), "height", "height", true ], | |
| [ newElem("td"), "width", "width", false ], | |
| [ newElem("td"), "height", "height", false ], | |
| [ newElem("table"), "width", "width", false ], | |
| [ newElem("table"), "height", "height", true ], | |
| [ newElem("tr"), "height", "height", true ], | |
| [ newElem("col"), "width", "width", true ], | |
| [ newElem("embed"), "hspace", "marginLeft", true ], | |
| [ newElem("embed"), "hspace", "marginRight", true ], | |
| [ newElem("embed"), "vspace", "marginTop", true ], | |
| [ newElem("embed"), "vspace", "marginBottom", true ], | |
| [ newElem("img"), "hspace", "marginLeft", true ], | |
| [ newElem("img"), "hspace", "marginRight", true ], | |
| [ newElem("img"), "vspace", "marginTop", true ], | |
| [ newElem("img"), "vspace", "marginBottom", true ], | |
| [ newElem("object"), "hspace", "marginLeft", true ], | |
| [ newElem("object"), "hspace", "marginRight", true ], | |
| [ newElem("object"), "vspace", "marginTop", true ], | |
| [ newElem("object"), "vspace", "marginBottom", true ], | |
| [ newImageInput(), "hspace", "marginLeft", true ], | |
| [ newImageInput(), "hspace", "marginRight", true ], | |
| [ newImageInput(), "vspace", "marginTop", true ], | |
| [ newImageInput(), "vspace", "marginBottom", true ], | |
| [ newElem("marquee"), "hspace", "marginLeft", true ], | |
| [ newElem("marquee"), "hspace", "marginRight", true ], | |
| [ newElem("marquee"), "vspace", "marginTop", true ], | |
| [ newElem("marquee"), "vspace", "marginBottom", true ], | |
| // <source width> is mapped to <img> width if both are in <picture>. | |
| [ newImgSource(), "width", "width", true, newElem("img"), newElem("picture") ], | |
| // <source height> is mapped to <img> height if both are in <picture>. | |
| [ newImgSource(), "height", "height", true, newElem("img"), newElem("picture") ], | |
| ]; | |
| function style(element) { | |
| return element.ownerDocument.defaultView.getComputedStyle(element); | |
| } | |
| const container = document.getElementById("container"); | |
| for (let [ctor, attr, prop, zero_allowed, mappedElemCtor, containerCtor] 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; | |
| } | |
| let elemContainer = null; | |
| if (!!containerCtor) { | |
| elemContainer = containerCtor(); | |
| container.appendChild(elemContainer); | |
| } else { | |
| elemContainer = container; | |
| } | |
| let runTest = (value, expected) => { | |
| let elem = ctor(); | |
| let mappedElem = !!mappedElemCtor ? mappedElemCtor() : elem; | |
| test(function() { | |
| this.add_cleanup(() => { | |
| elem.remove(); | |
| if (!!mappedElemCtor) { | |
| mappedElem.remove(); | |
| } | |
| }); | |
| elem.setAttribute(attr, value); | |
| assert_equals(elem.getAttribute(attr), value); | |
| elemContainer.appendChild(elem); | |
| if (!!mappedElemCtor) { | |
| elemContainer.appendChild(mappedElem); | |
| } | |
| assert_equals(style(mappedElem)[prop], expected); | |
| }, `<${elem.localName} ${attr}="${value}"> mapping to ` + | |
| `<${mappedElem.localName}> ${prop} property`); | |
| } | |
| for (let [value, result] of valid) { | |
| runTest(value, result); | |
| } | |
| let default_elem = !!mappedElemCtor ? mappedElemCtor() : ctor(); | |
| elemContainer.appendChild(default_elem); | |
| let defaultVal = style(default_elem)[prop]; | |
| default_elem.remove(); | |
| for (let value of invalid) { | |
| runTest(value, defaultVal); | |
| } | |
| if (!!containerCtor) { | |
| elemContainer.remove(); | |
| } | |
| } | |
| </script> | |
| </body> |