| <!DOCTYPE html> | |
| <title>CSS Values and Units Test: attr</title> | |
| <meta name="assert" content="test attr values"> | |
| <link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-notations"> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| <div id="attr"></div> | |
| <div id="expected"></div> | |
| <script> | |
| function test_attr_cycle(property, propertyValue, attrValue) { | |
| var elem = document.getElementById("attr"); | |
| var expectedValue = window.getComputedStyle(elem).getPropertyValue(property); | |
| elem.setAttribute("data-foo", attrValue); | |
| elem.style.setProperty(property, propertyValue); | |
| test(() => { | |
| assert_equals(window.getComputedStyle(elem).getPropertyValue(property), expectedValue, | |
| "Setting property \'" + property + "\' to the value \'" + propertyValue + | |
| "\', where \'data-foo=" + attrValue + "\' should not change it's value."); | |
| }); | |
| elem.style.setProperty(property, null); | |
| } | |
| function test_attr_no_cycle(property, propertyValue, attrValue, expectedValue) { | |
| var elem = document.getElementById("attr"); | |
| elem.setAttribute("data-foo", attrValue); | |
| elem.style.setProperty(property, propertyValue); | |
| var expectedElem = document.getElementById("expected"); | |
| expectedElem.style.setProperty(property, expectedValue); | |
| test(() => { | |
| assert_equals(window.getComputedStyle(elem).getPropertyValue(property), | |
| window.getComputedStyle(expectedElem).getPropertyValue(property), | |
| "Value \'" + propertyValue + "\', where \'data-foo=" + attrValue + | |
| "\' should be valid for the property \'" + property + "\'."); | |
| }); | |
| elem.style.setProperty(property, null); | |
| expectedElem.style.setProperty(property, null); | |
| } | |
| /* Simple cycle */ | |
| test_attr_cycle('--x', 'attr(data-foo type(<ident>))', 'attr(data-foo)'); | |
| test_attr_cycle('--x', 'attr(data-foo type(<length>))', 'attr(data-foo type(<length>))'); | |
| test_attr_cycle('--y', 'attr(data-foo type(*))', 'attr(data-foo type(*))'); | |
| var attrElem = document.getElementById("attr"); | |
| attrElem.setAttribute('data-bar', 'attr(data-foo)'); | |
| test_attr_cycle('--y', 'attr(data-foo type(*))', 'attr(data-bar type(*))'); | |
| attrElem.removeAttribute('data-bar'); | |
| /* Cycle with attr() and var() */ | |
| attrElem.style.setProperty('--x', 'attr(data-foo type(*))'); | |
| test_attr_cycle('--y', 'attr(data-foo type(*))', 'var(--x)'); | |
| attrElem.style.setProperty('--x', null); | |
| attrElem.setAttribute('data-bar', 'var(--y)'); | |
| test_attr_cycle('--y', 'attr(data-foo type(<string>))', 'attr(data-bar type(<string>))'); | |
| attrElem.removeAttribute('data-bar'); | |
| attrElem.setAttribute('data-bar', 'var(--x)'); | |
| test_attr_cycle('--x', 'attr(data-foo type(*), attr(data-bar type(*)))', 'attr(data-foo type(*))'); | |
| attrElem.removeAttribute('data-bar'); | |
| attrElem.style.setProperty('--x', 'attr(data-foo type(*))'); | |
| attrElem.setAttribute('data-bar', 'var(--x)'); | |
| test_attr_cycle('--y', 'attr(data-foo type(*))', 'attr(data-bar type(*), 11) var(--x)'); | |
| attrElem.style.setProperty('--x', null); | |
| attrElem.removeAttribute('data-bar'); | |
| /* Cycle with fallback */ | |
| test_attr_cycle('--x', 'attr(data-foo type(<length>))', 'attr(data-foo type(<length>), 11px)'); | |
| test_attr_cycle('--x', 'attr(data-foo type(<length>), 3px)', 'var(--x)'); | |
| test_attr_cycle('--x', 'attr(data-foo type(<length>), 3px)', 'var(--x, 11px)'); | |
| attrElem.style.setProperty('--y', 'attr(data-foo)'); | |
| test_attr_no_cycle('--x', 'attr(data-foo type(<custom-ident>), 3px)', 'var(--y)', '3px'); | |
| attrElem.style.setProperty('--y', null); | |
| /* Cycle within attributes, so we should fallback. */ | |
| test_attr_no_cycle('--x', 'attr(data-foo type(<length>), 11px)', 'attr(data-foo type(<length>), 3px)', '11px'); | |
| test_attr_no_cycle('--x', 'attr(data-foo type(<length>), 11px)', 'attr(data-foo type(<length>))', '11px'); | |
| test_attr_no_cycle('--y', 'attr(data-foo type(*), 11px)', 'attr(data-foo type(*))', '11px'); | |
| attrElem.setAttribute('data-bar', '11px'); | |
| test_attr_no_cycle('--x', 'attr(data-foo type(<length>), attr(data-bar type(<length>)))', 'attr(data-foo type(*))', '11px'); | |
| attrElem.removeAttribute('data-bar'); | |
| attrElem.setAttribute('data-bar', 'abc'); | |
| test_attr_no_cycle('--y', 'attr(data-foo type(*), attr(data-bar))', 'attr(data-foo type(*))', '"abc"'); | |
| attrElem.removeAttribute('data-bar'); | |
| attrElem.setAttribute('data-bar', 'attr(data-baz type(*), 3px)'); | |
| attrElem.setAttribute('data-baz', 'attr(data-foo type(*), 4px)'); | |
| test_attr_no_cycle('--y', 'attr(data-foo type(*), 1px)', 'attr(data-bar type(*), 2px)', '1px'); | |
| attrElem.removeAttribute('data-bar'); | |
| attrElem.removeAttribute('data-baz'); | |
| /* Cycle with var() and fallback */ | |
| attrElem.style.setProperty('--x', 'var(--y)'); | |
| test_attr_cycle('--y', 'var(--x, 100)', 'var(--y)'); | |
| attrElem.style.setProperty('--x', null); | |
| attrElem.setAttribute('data-bar', 'var(--y)'); | |
| attrElem.style.setProperty('--x', 'attr(data-foo)'); | |
| test_attr_cycle('--y', 'attr(data-foo type(*))', 'attr(data-bar type(*), 11) var(--x, 3)'); | |
| attrElem.style.setProperty('--x', null); | |
| attrElem.removeAttribute('data-bar'); | |
| /* Cycle in unused fallbacks */ | |
| test_attr_no_cycle('--y', 'attr(data-foo type(*), var(--y))', '3', '3'); | |
| test_attr_no_cycle('--y', 'attr(data-foo type(*), attr(data-foo))', '3', '3'); | |
| attrElem.style.setProperty('--x', 'var(--y)'); | |
| test_attr_no_cycle('--y', 'attr(data-foo type(*), var(--x))', '3', '3'); | |
| attrElem.style.setProperty('--x', null); | |
| attrElem.setAttribute('data-bar', 'attr(data-foo type(*))'); | |
| test_attr_no_cycle('--y', 'attr(data-foo type(*), attr(data-bar type(*)))', '3', '3'); | |
| attrElem.removeAttribute('data-bar'); | |
| /* Cycle in fallback */ | |
| test_attr_cycle('--y', 'attr(data-unknown type(*), var(--y))', '3'); | |
| /* No cycle, use raw CSS string without substitution */ | |
| attrElem.setAttribute('data-bar', 'var(--y)'); | |
| test_attr_no_cycle('--y', 'attr(data-foo type(<string>))', 'attr(data-bar type(<string>))', ''); | |
| attrElem.removeAttribute('data-bar'); | |
| attrElem.style.setProperty('--x', 'attr(data-foo)'); | |
| attrElem.setAttribute('data-bar', 'var(--x)'); | |
| test_attr_no_cycle('--y', 'attr(data-foo type(*))', 'attr(data-bar, 11) var(--x, 3)', '"var(--x)" "attr(data-bar, 11) var(--x, 3)"'); | |
| attrElem.removeAttribute('data-bar'); | |
| attrElem.style.setProperty('--x', null); | |
| /* Same as above, but with the custom properties swapped, so that the property | |
| using the raw (bare) attr() and the one using type() are resolved in the | |
| opposite order. The result must not depend on resolution order. */ | |
| attrElem.style.setProperty('--y', 'attr(data-foo)'); | |
| attrElem.setAttribute('data-bar', 'var(--y)'); | |
| test_attr_no_cycle('--x', 'attr(data-foo type(*))', 'attr(data-bar, 11) var(--y, 3)', '"var(--y)" "attr(data-bar, 11) var(--y, 3)"'); | |
| attrElem.removeAttribute('data-bar'); | |
| attrElem.style.setProperty('--y', null); | |
| /* No cycle, wrong attr syntax in attribute */ | |
| test_attr_no_cycle('--x', 'attr(data-foo type(*), abc)', 'attr(data-foo', 'abc'); | |
| /* Multiple attributes cycling simultaneously in bare attr() form */ | |
| attrElem.setAttribute('data-bar', 'attr(data-foo) attr(data-bar)'); | |
| test_attr_no_cycle('--x', 'attr(data-foo type(*), abc)', 'attr(data-bar type(*), fallback)', 'abc'); | |
| attrElem.removeAttribute('data-bar'); | |
| /* Transitive cycle through var() */ | |
| attrElem.setAttribute('data-bar', 'attr(data-foo, 10px)'); | |
| attrElem.style.setProperty('--x', 'attr(data-bar type(<length>))'); | |
| test_attr_cycle('width', 'attr(data-foo type(<length>))', 'var(--x)'); | |
| attrElem.style.setProperty('--x', null); | |
| attrElem.removeAttribute('data-bar'); | |
| /* No cycle, --data-foo and data-foo are distinct identifiers */ | |
| attrElem.setAttribute('data-bar', '3'); | |
| test_attr_no_cycle('--data-foo', 'attr(data-foo type(*))', 'attr(data-bar number)', '3'); | |
| attrElem.setAttribute('data-bar', null); | |
| /* Cycle in non-custom property */ | |
| CSS.registerProperty({ | |
| name: '--data-baz', | |
| syntax: '<length>', | |
| inherits: false, | |
| initialValue: '4px', | |
| }); | |
| attrElem.style.setProperty('--data-baz', '3em'); | |
| test_attr_cycle('font-size', 'attr(data-foo type(*))', 'var(--data-baz)'); | |
| test_attr_cycle('font-size', 'attr(unknown type(*), var(--data-baz))', ''); | |
| attrElem.style.setProperty('--data-baz', null); | |
| /* No cycle in non-custom property */ | |
| attrElem.style.setProperty('--bar', '3em'); | |
| test_attr_no_cycle('font-size', 'attr(unknown, var(--bar))', '', '48px'); | |
| attrElem.style.setProperty('--bar', null); | |
| attrElem.setAttribute('data-bar', '3'); | |
| test_attr_no_cycle('font-size', 'attr(data-foo type(*))', 'attr(data-bar em)', '48px'); | |
| attrElem.setAttribute('data-bar', null); | |
| /* Cycle in unused fallback for non-custom property */ | |
| attrElem.style.setProperty('--data-baz', '3em'); | |
| test_attr_no_cycle('font-size', 'attr(data-foo type(<length>), var(--data-baz))', '5px', '5px'); | |
| attrElem.style.setProperty('--data-baz', null); | |
| </script> |