| <!DOCTYPE html> |
| <link rel="author" title="Anders Hartvoll Ruud" href="andruud@chromium.org"> |
| <!-- TODO(andruud): Add Typed OM details to spec and link to it here. --> |
| <link rel="help" href="https://github.com/w3c/css-houdini-drafts/pull/783" /> |
| <meta name="assert" content="Verifies that registered custom properties interact correctly with CSS Typed OM" /> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <style id=style> |
| div {} |
| </style> |
| <div id=target></div> |
| |
| <script> |
| |
| // Properties are generated on demand, as `--prop-${g_counter}`. |
| let g_counter = 1; |
| |
| // Generate a new property name. |
| function gen_name() { |
| let name = `--prop-${g_counter}`; |
| g_counter++; |
| return name; |
| } |
| |
| // Generate a property and return its name. |
| function gen_prop(syntax, initialValue) { |
| let name = gen_name(); |
| CSS.registerProperty({ |
| name: name, |
| syntax: syntax, |
| initialValue: initialValue, |
| inherits: false |
| }); |
| return name; |
| } |
| |
| // On the target element, verify that computed value of 'name' is an instance |
| // of 'expected' and not an instance of CSSUnparsedValue. |
| // |
| // If 'value' is non-null, that value is first set on the attributeStyleMap |
| // of the target. |
| function assert_computed_type(name, value, expected) { |
| if (expected == CSSUnparsedValue) { |
| throw 'CSSUnparsedValue may not be used as expected type'; |
| } |
| |
| if (value != null) { |
| target.style = `${name}: ${value}`; |
| } |
| |
| let computedValue = target.computedStyleMap().get(name); |
| |
| assert_false(computedValue instanceof CSSUnparsedValue); |
| assert_true(computedValue instanceof expected); |
| |
| if (value != null) { |
| target.style = ''; |
| } |
| } |
| |
| function assert_attribute_get_type(syntax, value, expected) { |
| let name = gen_name(); |
| target.style = `${name}: ${value}`; |
| |
| assert_true(target.attributeStyleMap.get(name) instanceof CSSUnparsedValue); |
| |
| CSS.registerProperty({ |
| name: name, |
| syntax: syntax, |
| initialValue: value, |
| inherits: false |
| }); |
| |
| if (expected == CSSStyleValue) { |
| assert_false(target.attributeStyleMap.get(name) instanceof CSSUnparsedValue); |
| } |
| |
| assert_true(target.attributeStyleMap.get(name) instanceof expected); |
| } |
| |
| // computedStyleMap |
| |
| test(function(){ |
| let name = gen_prop('*', 'if(){}'); |
| assert_true(target.computedStyleMap().get(name) instanceof CSSUnparsedValue); |
| |
| target.attributeStyleMap.set(name, 'as{}df'); |
| assert_true(target.computedStyleMap().get(name) instanceof CSSUnparsedValue); |
| target.attributeStyleMap.delete(name); |
| }, 'Computed * is reified as CSSUnparsedValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<angle>', '42deg'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<angle> | fail', 'fail'), '42deg', CSSUnitValue); |
| }, 'Computed <angle> is reified as CSSUnitValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<color>', '#fefefe'), null, CSSStyleValue); |
| assert_computed_type(gen_prop('<color> | fail', 'fail'), null, CSSStyleValue); |
| }, 'Computed <color> is reified as CSSStyleValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<custom-ident>', 'none'), null, CSSKeywordValue); |
| assert_computed_type(gen_prop('<custom-ident> | <length>', '10px'), 'none', CSSKeywordValue); |
| }, 'Computed <custom-ident> is reified as CSSKeywordValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<image>', 'url(thing.png)'), null, CSSImageValue); |
| assert_computed_type(gen_prop('<image> | fail', 'fail'), 'url(thing.png)', CSSImageValue); |
| }, 'Computed <image> [url] is reified as CSSImageValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<integer>', '100'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<integer> | fail', 'fail'), '100', CSSUnitValue); |
| }, 'Computed <integer> is reified as CSSUnitValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<length-percentage>', '10%'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<length-percentage> | fail', 'fail'), '10%', CSSUnitValue); |
| }, 'Computed <length-percentage> [%] is reified as CSSUnitValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<length-percentage>', '10px'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<length-percentage> | fail', 'fail'), '10px', CSSUnitValue); |
| }, 'Computed <length-percentage> [px] is reified as CSSUnitValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<length-percentage>', 'calc(10px + 10%)'), null, CSSMathSum); |
| assert_computed_type(gen_prop('<length-percentage> | fail', 'fail'), 'calc(10px + 10%)', CSSMathSum); |
| }, 'Computed <length-percentage> [px + %] is reified as CSSMathSum'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<length>', '10px'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<length> | fail', 'fail'), '10px', CSSUnitValue); |
| }, 'Computed <length> is reified as CSSUnitValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<number>', '42'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<number> | fail', 'fail'), '42', CSSUnitValue); |
| }, 'Computed <number> is reified as CSSUnitValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<percentage>', '10%'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<percentage> | fail', 'fail'), '10%', CSSUnitValue); |
| }, 'Computed <percentage> is reified as CSSUnitValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<resolution>', '300dpi'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<resolution> | fail', 'fail'), '300dpi', CSSUnitValue); |
| }, 'Computed <resolution> is reified as CSSUnitValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<time>', '42s'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<time> | fail', 'fail'), '42s', CSSUnitValue); |
| }, 'Computed <time> is reified as CSSUnitValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<url>', 'url(a)'), null, CSSStyleValue); |
| assert_computed_type(gen_prop('<url> | fail', 'fail'), 'url(a)', CSSStyleValue); |
| }, 'Computed <url> is reified as CSSStyleValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('thing1 | THING2', 'thing1'), null, CSSKeywordValue); |
| assert_computed_type(gen_prop('thing1 | THING2 | <url>', 'url(fail)'), 'THING2', CSSKeywordValue); |
| }, 'Computed ident is reified as CSSKeywordValue'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<length>+', '10px 20px'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<length>+', '0px 0px'), '10px 20px', CSSUnitValue); |
| }, 'First computed value correctly reified in space-separated list'); |
| |
| test(function(){ |
| assert_computed_type(gen_prop('<length>#', '10px, 20px'), null, CSSUnitValue); |
| assert_computed_type(gen_prop('<length>#', '0px, 0px'), '10px, 20px', CSSUnitValue); |
| }, 'First computed value correctly reified in comma-separated list'); |
| |
| test(function(){ |
| let name = gen_prop('<length>+', '10px 20px'); |
| assert_equals(target.computedStyleMap().getAll(name).length, 2); |
| assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); |
| |
| target.style = `${name}: 10px 20px 30px`; |
| assert_equals(target.computedStyleMap().getAll(name).length, 3); |
| assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); |
| }, 'All computed values correctly reified in space-separated list'); |
| |
| test(function(){ |
| let name = gen_prop('<length>#', '10px, 20px'); |
| assert_equals(target.computedStyleMap().getAll(name).length, 2); |
| assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); |
| |
| target.style = `${name}: 10px, 20px, 30px`; |
| assert_equals(target.computedStyleMap().getAll(name).length, 3); |
| assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); |
| }, 'All computed values correctly reified in comma-separated list'); |
| |
| // attributeStyleMap.get |
| |
| test(function(){ |
| let name1 = gen_prop('<length>', '100px'); |
| let name2 = gen_prop('<length>', '0px'); |
| target.style = `${name2}: var(${name1})`; |
| assert_true(target.attributeStyleMap.get(name2) instanceof CSSUnparsedValue); |
| }, 'attributeStyleMap.get returns CSSUnparsedValue for value with var references'); |
| |
| test(function(){ |
| let name1 = gen_prop('<length>', '100px'); |
| let name2 = gen_prop('<length>#', '0px'); |
| target.style = `${name2}: 1px, var(${name1}), 3px`; |
| assert_true(target.attributeStyleMap.get(name2) instanceof CSSUnparsedValue); |
| }, 'attributeStyleMap.get returns CSSUnparsedValue for value with var reference in list'); |
| |
| test(function(){ |
| assert_attribute_get_type('*', 'if(){}', CSSUnparsedValue); |
| }, 'attributeStyleMap.get returns CSSUnparsedValue for *'); |
| |
| test(function(){ |
| assert_attribute_get_type('<angle>', '42deg', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <angle>'); |
| |
| test(function(){ |
| assert_attribute_get_type('<color>', '#fefefe', CSSStyleValue); |
| }, 'attributeStyleMap.get returns CSSStyleValue for <color>'); |
| |
| test(function(){ |
| assert_attribute_get_type('<custom-ident>', 'none', CSSKeywordValue); |
| }, 'attributeStyleMap.get returns CSSKeywordValue for <custom-ident>'); |
| |
| test(function(){ |
| assert_attribute_get_type('<image>', 'url(thing.png)', CSSImageValue); |
| }, 'attributeStyleMap.get returns CSSImageValue for <image>'); |
| |
| test(function(){ |
| assert_attribute_get_type('<integer>', '100', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <integer>'); |
| |
| test(function(){ |
| assert_attribute_get_type('<length-percentage>', '10%', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <length-percentage> [10%]'); |
| |
| test(function(){ |
| assert_attribute_get_type('<length-percentage>', '10px', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <length-percentage> [10px]'); |
| |
| test(function(){ |
| assert_attribute_get_type('<length-percentage>', 'calc(10px + 10%)', CSSMathSum); |
| }, 'attributeStyleMap.get returns CSSMathSum for <length-percentage> [calc(10px + 10%)]'); |
| |
| test(function(){ |
| assert_attribute_get_type('<length>', '10px', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <length>'); |
| |
| test(function(){ |
| assert_attribute_get_type('<number>', '42', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <number>'); |
| |
| test(function(){ |
| assert_attribute_get_type('<percentage>', '10%', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <percentage>'); |
| |
| test(function(){ |
| assert_attribute_get_type('<resolution>', '300dpi', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <resolution>'); |
| |
| test(function(){ |
| assert_attribute_get_type('<time>', '42s', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <time>'); |
| |
| test(function(){ |
| assert_attribute_get_type('<url>', 'url(a)', CSSStyleValue); |
| }, 'attributeStyleMap.get returns CSSStyleValue for <url>'); |
| |
| test(function(){ |
| assert_attribute_get_type('thing1 | THING2', 'thing1', CSSKeywordValue); |
| }, 'attributeStyleMap.get returns CSSKeywordValue for thing1 | THING2'); |
| |
| test(function(){ |
| assert_attribute_get_type('<length>+', '10px 20px', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <length>+'); |
| |
| test(function(){ |
| assert_attribute_get_type('<length>#', '10px 20px', CSSUnitValue); |
| }, 'attributeStyleMap.get returns CSSUnitValue for <length>#'); |
| |
| // attributeStyleMap.getAll |
| |
| test(function(){ |
| let name = gen_prop('<length>+', '0px'); |
| target.attributeStyleMap.clear(); |
| target.style = `${name}: 10px 20px 30px`; |
| assert_equals(target.attributeStyleMap.getAll(name).length, 3); |
| assert_true(target.attributeStyleMap.getAll(name).every(x => x instanceof CSSUnitValue)); |
| }, 'attributeStyleMap.getAll returns a list of CSSUnitValues for <length>+'); |
| |
| test(function(){ |
| let name = gen_prop('<length>#', '0px'); |
| target.attributeStyleMap.clear(); |
| target.style = `${name}: 10px, 20px, 30px`; |
| assert_equals(target.attributeStyleMap.getAll(name).length, 3); |
| assert_true(target.attributeStyleMap.getAll(name).every(x => x instanceof CSSUnitValue)); |
| }, 'attributeStyleMap.getAll returns a list of CSSUnitValues for <length>#'); |
| |
| // StylePropertyMap.set |
| |
| function test_style_property_map_set_using_property_map(propertyMapName, propertyMap, options) { |
| test(function(){ |
| let name = gen_prop(options.syntax, options.initialValue); |
| propertyMap.clear(); |
| |
| for (let value of options.shouldAccept) |
| propertyMap.set(name, value); |
| |
| for (let value of options.shouldReject) { |
| assert_throws(new TypeError(), () => propertyMap.set(name, value)); |
| } |
| }, `${propertyMapName}.set accepts correct CSSUnitValues for ${options.syntax}`); |
| } |
| |
| // Verify that the correct CSSStyleValues are accepted/rejected for a registered |
| // property with the specified syntax. |
| // |
| // The same test is performed twice: once for attributeStyleMap, and once |
| // for styleMap. |
| function test_style_property_map_set(options) { |
| test_style_property_map_set_using_property_map('attributeStyleMap', target.attributeStyleMap, options); |
| test_style_property_map_set_using_property_map('styleMap', style.sheet.rules[0].styleMap, options); |
| } |
| |
| let unparsed = x => new CSSUnparsedValue([x]); |
| let keyword = x => new CSSKeywordValue(x); |
| let sum = (a, b) => new CSSMathSum(a, b); |
| let url_image = x => CSSStyleValue.parse('background-image', x); |
| |
| test_style_property_map_set({ |
| syntax: '*', |
| initialValue: 'none', |
| shouldAccept: [unparsed('thing')], |
| shouldReject: [CSS.px(15), keyword('none')], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<angle>', |
| initialValue: '0deg', |
| shouldAccept: [CSS.deg(42), CSS.turn(2)], |
| shouldReject: [unparsed('42deg'), CSS.px(15)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<custom-ident>', |
| initialValue: 'none', |
| shouldAccept: [keyword('foo')], |
| shouldReject: [unparsed('foo'), CSS.px(15)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<image>', |
| initialValue: 'url(a)', |
| shouldAccept: [url_image('url(b)')], |
| shouldReject: [unparsed('url(b)'), CSS.px(100)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<integer>', |
| initialValue: '0', |
| shouldAccept: [CSS.number(1), CSS.number(-42)], |
| shouldReject: [unparsed('42'), CSS.px(100)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<length-percentage>', |
| initialValue: '0px', |
| shouldAccept: [CSS.percent(10), CSS.px(1), CSS.em(1)], |
| shouldReject: [unparsed('10%'), unparsed('10px'), CSS.dpi(1)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<length>', |
| initialValue: '0px', |
| shouldAccept: [CSS.px(10), CSS.em(10), CSS.vh(200), sum(CSS.px(10), CSS.em(20))], |
| shouldReject: [unparsed('10px'), CSS.percent(1)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<number>', |
| initialValue: '0', |
| shouldAccept: [CSS.number(1337), CSS.number(-42.5)], |
| shouldReject: [unparsed('42'), CSS.px(15)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<percentage>', |
| initialValue: '0%', |
| shouldAccept: [CSS.percent(10)], |
| shouldReject: [unparsed('10%'), CSS.px(1)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<resolution>', |
| initialValue: '0dpi', |
| shouldAccept: [CSS.dpi(100), CSS.dpcm(10), CSS.dppx(50)], |
| shouldReject: [unparsed('42'), CSS.px(15)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<time>', |
| initialValue: '0s', |
| shouldAccept: [CSS.s(42), CSS.ms(16)], |
| shouldReject: [unparsed('42s'), CSS.px(15)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<url>', |
| initialValue: 'url(a)', |
| shouldAccept: [url_image('url(b)')], |
| shouldReject: [unparsed('url(b)'), CSS.px(100)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<transform-list>', |
| initialValue: 'translateX(0px)', |
| shouldAccept: [CSSStyleValue.parse('transform', 'translateX(10px)')], |
| shouldReject: [unparsed('transformX(10px'), CSS.px(100)], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: 'none | thing | THING', |
| initialValue: 'none', |
| shouldAccept: [keyword('thing'), keyword('THING')], |
| shouldReject: [unparsed('thing'), CSS.px(15), keyword('notathing')], |
| }); |
| |
| test_style_property_map_set({ |
| syntax: '<angle> | <length>', |
| initialValue: '0deg', |
| shouldAccept: [CSS.deg(42), CSS.turn(2), CSS.px(10), CSS.em(10)], |
| shouldReject: [unparsed('42deg'), unparsed('20px'), CSS.s(1)], |
| }); |
| |
| </script> |