<!DOCTYPE html>
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#css-style-value-reification" />
<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>
<script src="./resources/utils.js"></script>
<style id=style>
    div {}
</style>
<div id=target></div>

<script>

// Cleans style rules used for testing between every test.
add_result_callback(function(){
    target.attributeStyleMap.clear();
    // Clears 'div' rule in #style:
    style.sheet.rules[0].styleMap.clear();
});

// In the following utility functions, the 'map' parameter (if present)
// can be any StylePropertyMap. (Not StylePropertyMapReadOnly).

// Verifies that get()/getAll() reifies the specified property to a
// CSSUnparsedValue, with a string serialization equal to 'value'.
function verify_map_get_unparsed(map, name, value) {
    map.set(name, value);

    let specifiedValue = map.get(name);
    assert_true(specifiedValue instanceof CSSUnparsedValue);
    assert_equals(specifiedValue.toString(), value);

    let allSpecifiedValues = map.getAll(name);
    assert_equals(allSpecifiedValues.length, 1);
    assert_true(allSpecifiedValues[0] instanceof CSSUnparsedValue);
    assert_equals(allSpecifiedValues[0].toString(), value);
}

// Verifies that the specified value is accepted by set().
function verify_map_set(map, name, value) {
    map.set(name, value);
    assert_equals(map.get(name).toString(), value.toString());
}

// Verifies that the specified value is NOT accepted by set().
function verify_map_not_set(map, name, value) {
    assert_throws_js(TypeError, () => {
        map.set(name, value);
    });
}

// Verifies that the specified value is NOT accepted by append().
function verify_map_no_append(map, name, value) {
    assert_throws_js(TypeError, () => {
        map.append(name, value);
    });
}

// Verifies that the property 'name' shows up on iteration, that it's reified
// as a CSSUnparsedValue, and that the string representation is equal to
// 'value'.
function verify_map_iteration_unparsed(map, name, value) {
    map.set(name, value);
    let result = Array.from(map).filter(e => e[0] == name)[0];
    assert_equals(result.length, 2);
    let iter_value = result[1];
    assert_equals(iter_value.length, 1);
    assert_true(iter_value[0] instanceof CSSUnparsedValue);
    assert_equals(iter_value[0].toString(), value);
}

// Verifies that CSSStyleValue.parse/parseAll results in a CSSStyleValue with
// the 'expected' type.
function verify_parsed_type(prop, value, expected) {
    let parse_value = CSSStyleValue.parse(prop, value);
    let parse_all_value = CSSStyleValue.parseAll(prop, value);

    assert_true(parse_value instanceof expected);
    assert_true(parse_all_value.every(x => x instanceof expected))
}

// 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 using the style attribute
// of the target element.
function verify_computed_type(name, value, expected) {
    if (expected == CSSUnparsedValue) {
        throw 'CSSUnparsedValue may not be used as expected type';
    }

    try {
        if (value != null) {
            target.style = `${name}: ${value}`;
        }

        let computedValue = target.computedStyleMap().get(name);

        assert_false(computedValue instanceof CSSUnparsedValue);
        assert_true(computedValue instanceof expected);
    } finally {
        if (value != null) {
            target.style = '';
        }
    }
}

// Verifies that the property 'name' shows up on iteration, that it's reified
// to the specified type, and that the string representation is equal to 'value'.
function verify_computed_iteration_type(name, value, type) {
    target.attributeStyleMap.set(name, value);
    let result = Array.from(target.computedStyleMap()).filter(e => e[0] == name)[0];
    assert_equals(result.length, 2);
    let iter_value = result[1];
    assert_equals(iter_value.length, 1);
    assert_true(iter_value[0] instanceof type);
    assert_equals(iter_value[0].toString(), value);
}

// Run the same test twice: once for each StylePropertyMap.
//
// https://drafts.css-houdini.org/css-typed-om-1/#stylepropertymap
function test_specified_maps(func, description) {
    test(function(){
        func(target.attributeStyleMap)
    }, description + ' [attributeStyleMap]');

    test(function(){
        let rule = style.sheet.rules[0];
        func(rule.styleMap)
    }, description + ' [styleMap]');
}

// StylePropertyMapReadOnly.get

test(function(){
    let name = generate_property('*', '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(){
    verify_computed_type(generate_property('<angle>'), null, CSSUnitValue);
    verify_computed_type(generate_property('fail | <angle> '), '42deg', CSSUnitValue);
}, 'Computed <angle> is reified as CSSUnitValue');

test(function(){
    verify_computed_type(generate_property('<color>'), null, CSSStyleValue);
    verify_computed_type(generate_property('fail | <color> '), null, CSSStyleValue);
}, 'Computed <color> is reified as CSSStyleValue');

test(function(){
    verify_computed_type(generate_property('<custom-ident>'), null, CSSKeywordValue);
    verify_computed_type(generate_property('<custom-ident> | <length>'), 'none', CSSKeywordValue);
}, 'Computed <custom-ident> is reified as CSSKeywordValue');

test(function(){
    verify_computed_type(generate_property('<image>'), null, CSSImageValue);
    verify_computed_type(generate_property('fail | <image> '), 'url(thing.png)', CSSImageValue);
}, 'Computed <image> [url] is reified as CSSImageValue');

test(function(){
    verify_computed_type(generate_property('<integer>'), null, CSSUnitValue);
    verify_computed_type(generate_property('fail | <integer> '), '100', CSSUnitValue);
}, 'Computed <integer> is reified as CSSUnitValue');

test(function(){
    verify_computed_type(generate_property('<length-percentage>'), null, CSSUnitValue);
    verify_computed_type(generate_property('fail | <length-percentage> '), '10%', CSSUnitValue);
}, 'Computed <length-percentage> [%] is reified as CSSUnitValue');

test(function(){
    verify_computed_type(generate_property('<length-percentage>'), null, CSSUnitValue);
    verify_computed_type(generate_property('fail | <length-percentage> '), '10px', CSSUnitValue);
}, 'Computed <length-percentage> [px] is reified as CSSUnitValue');

test(function(){
    verify_computed_type(generate_property({syntax: '<length-percentage>', initialValue: 'calc(10% + 10px)'}), null, CSSMathSum);
    verify_computed_type(generate_property('fail | <length-percentage> '), 'calc(10px + 10%)', CSSMathSum);
}, 'Computed <length-percentage> [px + %] is reified as CSSMathSum');

test(function(){
    verify_computed_type(generate_property('<length>'), null, CSSUnitValue);
    verify_computed_type(generate_property('fail | <length> '), '10px', CSSUnitValue);
}, 'Computed <length> is reified as CSSUnitValue');

test(function(){
    verify_computed_type(generate_property('<number>'), null, CSSUnitValue);
    verify_computed_type(generate_property('fail | <number> '), '42', CSSUnitValue);
}, 'Computed <number> is reified as CSSUnitValue');

test(function(){
    verify_computed_type(generate_property('<percentage>'), null, CSSUnitValue);
    verify_computed_type(generate_property('fail | <percentage> '), '10%', CSSUnitValue);
}, 'Computed <percentage> is reified as CSSUnitValue');

test(function(){
    verify_computed_type(generate_property('<resolution>'), null, CSSUnitValue);
    verify_computed_type(generate_property('fail | <resolution> '), '300dpi', CSSUnitValue);
}, 'Computed <resolution> is reified as CSSUnitValue');

test(function(){
    verify_computed_type(generate_property('<time>'), null, CSSUnitValue);
    verify_computed_type(generate_property('fail | <time> '), '42s', CSSUnitValue);
}, 'Computed <time> is reified as CSSUnitValue');

test(function(){
    verify_computed_type(generate_property('<url>'), null, CSSStyleValue);
    verify_computed_type(generate_property('fail | <url> '), 'url(a)', CSSStyleValue);
}, 'Computed <url> is reified as CSSStyleValue');

test(function(){
    verify_computed_type(generate_property('thing1 | THING2'), null, CSSKeywordValue);
    verify_computed_type(generate_property('thing1 | THING2 | <url>'), 'THING2', CSSKeywordValue);
}, 'Computed ident is reified as CSSKeywordValue');

test(function(){
    verify_computed_type(generate_property('<length>+'), null, CSSUnitValue);
    verify_computed_type(generate_property('<length>+'), '10px 20px', CSSUnitValue);
}, 'First computed value correctly reified in space-separated list');

test(function(){
    verify_computed_type(generate_property('<length>#'), null, CSSUnitValue);
    verify_computed_type(generate_property('<length>#'), '10px, 20px', CSSUnitValue);
}, 'First computed value correctly reified in comma-separated list');

// StylePropertyMapReadOnly.getAll

test(function(){
    let name = generate_property({syntax: '<length>+', initialValue: '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 = generate_property({syntax: '<length>#', initialValue: '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');

// StylePropertyMap.get/All

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('*'), 'foo');
}, 'Specified * is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('foo'), 'foo');
}, 'Specified foo is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<angle>'), '10deg');
}, 'Specified <angle> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<color>'), 'green');
}, 'Specified <color> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<custom-ident>'), 'foo');
}, 'Specified <custom-ident> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<image>'), 'url("a")');
}, 'Specified <image> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<integer>'), '1');
}, 'Specified <integer> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<length-percentage>'), 'calc(10% + 10px)');
}, 'Specified <length-percentage> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<length>'), '10px');
}, 'Specified <length> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<number>'), '1');
}, 'Specified <number> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<percentage>'), '10%');
}, 'Specified <percentage> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<resolution>'), '10dpi');
}, 'Specified <resolution> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<time>'), '1s');
}, 'Specified <time> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Specified <transform-function> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Specified <transform-list> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<url>'), 'url("a")');
}, 'Specified <url> is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<length>+'), '10px 11px');
}, 'Specified <length>+ is reified as CSSUnparsedValue from get/getAll');

test_specified_maps(function(map){
    verify_map_get_unparsed(map, generate_property('<length>#'), '10px, 11px');
}, 'Specified <length># is reified as CSSUnparsedValue from get/getAll');

// StylePropertyMap.set

// The following strings are valid for the specified syntax, and should be
// accepted by set().

test_specified_maps(function(map){
    verify_map_set(map, generate_property('*'), 'foo');
}, 'Specified string "foo" accepted by set() for syntax *');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('foo'), 'foo');
}, 'Specified string "foo" accepted by set() for syntax foo');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<angle>'), '10deg');
}, 'Specified string "10deg" accepted by set() for syntax <angle>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<color>'), 'green');
}, 'Specified string "green" accepted by set() for syntax <color>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<custom-ident>'), 'foo');
}, 'Specified string "foo" accepted by set() for syntax <custom-ident>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<image>'), 'url("a")');
}, 'Specified string "url("a")" accepted by set() for syntax <image>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<integer>'), '1');
}, 'Specified string "1" accepted by set() for syntax <integer>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length-percentage>'), 'calc(10% + 10px)');
}, 'Specified string "calc(10% + 10px)" accepted by set() for syntax <length-percentage>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length>'), '10px');
}, 'Specified string "10px" accepted by set() for syntax <length>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<number>'), '1');
}, 'Specified string "1" accepted by set() for syntax <number>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<percentage>'), '10%');
}, 'Specified string "10%" accepted by set() for syntax <percentage>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<resolution>'), '10dpi');
}, 'Specified string "10dpi" accepted by set() for syntax <resolution>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<time>'), '1s');
}, 'Specified string "1s" accepted by set() for syntax <time>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Specified string "matrix(0, 0, 0, 0, 0, 0)" accepted by set() for syntax <transform-function>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Specified string "matrix(0, 0, 0, 0, 0, 0)" accepted by set() for syntax <transform-list>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<url>'), 'url("a")');
}, 'Specified string "url("a")" accepted by set() for syntax <url>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length>+'), '10px 11px');
}, 'Specified string "10px 11px" accepted by set() for syntax <length>+');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length>#'), '10px, 11px');
}, 'Specified string "10px, 11px" accepted by set() for syntax <length>#');

// The following strings are invalid for the specified syntax, but should
// should be accepted by set().

test_specified_maps(function(map){
    verify_map_set(map, generate_property('foo'), 'bar');
}, 'Specified string "bar" accepted by set() for syntax foo');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<angle>'), '10px');
}, 'Specified string "10px" accepted by set() for syntax <angle>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<color>'), '10px');
}, 'Specified string "10px" accepted by set() for syntax <color>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<custom-ident>'), '10px');
}, 'Specified string "10px" accepted by set() for syntax <custom-ident>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<image>'), 'a');
}, 'Specified string "a" accepted by set() for syntax <image>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<integer>'), 'float');
}, 'Specified string "float" accepted by set() for syntax <integer>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length-percentage>'), 'red');
}, 'Specified string "red" accepted by set() for syntax <length-percentage>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length>'), 'red');
}, 'Specified string "red" accepted by set() for syntax <length>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<number>'), 'red');
}, 'Specified string "red" accepted by set() for syntax <number>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<percentage>'), 'var(--x)');
}, 'Specified string "var(--x)" accepted by set() for syntax <percentage>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<resolution>'), 'blue');
}, 'Specified string "blue" accepted by set() for syntax <resolution>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<time>'), '1meter');
}, 'Specified string "1meter" accepted by set() for syntax <time>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<transform-function>'), 'foo(0)');
}, 'Specified string "foo(0)" accepted by set() for syntax <transform-function>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<transform-list>'), 'bar(1)');
}, 'Specified string "bar(1)" accepted by set() for syntax <transform-list>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<url>'), 'a');
}, 'Specified string "a" accepted by set() for syntax <url>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length>+'), 'a b');
}, 'Specified string "a b" accepted by set() for syntax <length>+');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length>#'), 'a, b');
}, 'Specified string "a, b" accepted by set() for syntax <length>#');

// CSSUnparsedValue should always be accepted by any custom property,
// regardless of registation status.

const unparsed = CSSStyleValue.parse('--x', 'foo bar thing');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('*'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax *');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('foo'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax foo');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<angle>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <angle>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<color>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <color>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<custom-ident>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <custom-ident>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<image>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <image>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<integer>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <integer>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length-percentage>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <length-percentage>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <length>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<number>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <number>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<percentage>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <percentage>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<resolution>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <resolution>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<time>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <time>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<transform-function>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <transform-function>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<transform-list>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <transform-list>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<url>'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <url>');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length>+'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <length>+');

test_specified_maps(function(map){
    verify_map_set(map, generate_property('<length>#'), unparsed);
}, 'CSSUnparsedValue is accepted via set() for syntax <length>#');

// CSSStyleValues which aren't CSSUnparsedValues aren't accepted by set(),
// even if they're a value which is compatible with the syntax.
//
// https://drafts.css-houdini.org/css-properties-values-api-1/#cssom
const zero_matrix = CSSStyleValue.parse('transform', 'matrix(0, 0, 0, 0, 0, 0)');
const image_value = CSSStyleValue.parse('background-image', 'url(a)');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('*'), new CSSKeywordValue('foo'));
}, 'CSSKeywordValue rejected by set() for syntax *');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('foo'), new CSSKeywordValue('foo'));
}, 'CSSKeywordValue rejected by set() for syntax foo');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<angle>'), CSS.deg(10));
}, 'CSSUnitValue rejected by set() for syntax <angle>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<custom-ident>'), new CSSKeywordValue('foo'));
}, 'CSSKeywordValue rejected by set() for syntax <custom-ident>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<image>'), image_value);
}, 'CSSImageValue rejected by set() for syntax <image>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<integer>'), CSS.number(1));
}, 'CSSUnitValue rejected by set() for syntax <integer>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<length-percentage>'), CSS.px(10));
}, 'CSSUnitValue rejected by set() for syntax <length-percentage>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<length>'), CSS.px(10));
}, 'CSSUnitValue rejected by set() for syntax <length>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<number>'), CSS.number(10));
}, 'CSSUnitValue rejected by set() for syntax <number>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<percentage>'), CSS.percent(10));
}, 'CSSUnitValue rejected by set() for syntax <percentage>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<resolution>'), CSS.dpi(10));
}, 'CSSUnitValue rejected by set() for syntax <resolution>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<time>'), CSS.s(10));
}, 'CSSUnitValue rejected by set() for syntax <time>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<transform-list>'), zero_matrix);
}, 'CSSTransformValue rejected by set() for syntax <transform-list>');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<length>+'), CSS.px(10), CSS.px(10));
}, 'CSSUnitValue rejected by set() for syntax <length>+');

test_specified_maps(function(map){
    verify_map_not_set(map, generate_property('<length>#'), CSS.px(10), CSS.px(10));
}, 'CSSUnitValue rejected by set() for syntax <length>#');

// <color> has no CSSStyleValue subclass yet.
// <url> has no CSSStyleValue subclass yet.
// <transform-function> has no CSSStyleValue subclass yet.

// StylePropertyMap.append

// It is not allowed to append CSSStyleValues to custom properties, even
// when the string matches the syntax of the custom property.

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('*'), 'a');
}, 'Appending a string to * is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('foo+'), 'foo');
}, 'Appending a string to foo+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<angle>+'), '10deg');
}, 'Appending a string to <angle>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<color>+'), 'red');
}, 'Appending a string to <color>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<custom-ident>+'), 'foo');
}, 'Appending a string to <custom-ident>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<image>+'), 'url(a)');
}, 'Appending a string to <image>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<integer>+'), 'a');
}, 'Appending a string to <integer>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<length-percentage>+'), 'calc(10*% + 10px)');
}, 'Appending a string to <length-percentage>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<length>+'), '10px');
}, 'Appending a string to <length>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<number>+'), '1.3');
}, 'Appending a string to <number>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<percentage>+'), '10%');
}, 'Appending a string to <percentage>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<resolution>+'), '10dpi');
}, 'Appending a string to <resolution>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<time>+'), '1s');
}, 'Appending a string to <time>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<transform-function>+'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Appending a string to <transform-function>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Appending a string to <transform-list> is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<url>+'), 'url(a)');
}, 'Appending a string to <url>+ is not allowed');

test_specified_maps(function(map){
    verify_map_no_append(map, generate_property('<length>#'), '10px');
}, 'Appending a string to <length># is not allowed');

// It is not allowed to append CSSStyleValues to custom properties, even
// when the CSSStyleValue matches the syntax of the custom property.

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('*'), new CSSKeywordValue('foo'));
}, 'Appending a CSSKeywordValue to * is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('foo+'), new CSSKeywordValue('foo'));
}, 'Appending a CSSKeywordValue to foo+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<angle>+'), CSS.deg(10));
}, 'Appending a CSSUnitValue to <angle>+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<custom-ident>+'), new CSSKeywordValue('foo'));
}, 'Appending a CSSKeywordValue to <custom-ident>+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<image>+'), image_value);
}, 'Appending a CSSImageValue to <image>+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<integer>+'), CSS.number(1));
}, 'Appending a CSSUnitValue to <integer>+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<length-percentage>+'), CSS.px(10));
}, 'Appending a CSSUnitValue to <length-percentage>+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<length>+'), CSS.px(10));
}, 'Appending a CSSUnitValue to <length>+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<number>+'), CSS.number(10));
}, 'Appending a CSSUnitValue to <number>+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<percentage>+'), CSS.percent(10));
}, 'Appending a CSSUnitValue to <percentage>+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<resolution>+'), CSS.dpi(10));
}, 'Appending a CSSUnitValue to <resolution>+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<time>+'), CSS.s(10));
}, 'Appending a CSSUnitValue to <time>+ is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<transform-list>'), zero_matrix);
}, 'Appending a CSSKeywordValue to <transform-list> is not allowed');

test_specified_maps(function(map) {
    verify_map_no_append(map, generate_property('<length>#'), CSS.px(10));
}, 'Appending a CSSUnitValue to <length># is not allowed');

// <color> has no CSSStyleValue subclass yet.
// <url> has no CSSStyleValue subclass yet.
// <transform-function> has no CSSStyleValue subclass yet.

// CSSStyleValue.parse/parseAll

test(function(){
    verify_parsed_type(generate_property('*'), 'while(){}', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax *', CSSUnparsedValue);

test(function(){
    verify_parsed_type(generate_property('<angle>'), '42deg', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <angle>');

test(function(){
    verify_parsed_type(generate_property('<color>'), '#fefefe', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <color>');

test(function(){
    verify_parsed_type(generate_property('<custom-ident> | <length>'), 'none', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <custom-ident> | <length>');

test(function(){
    verify_parsed_type(generate_property('<image>'), 'url(thing.png)', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <image>');

test(function(){
    verify_parsed_type(generate_property('<integer>'), '100', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <integer>');

test(function(){
    verify_parsed_type(generate_property('<length-percentage>'), '10%', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length-percentage> (10%)');

test(function(){
    verify_parsed_type(generate_property('<length-percentage>'), '10px', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length-percentage> (10px)');

test(function(){
    verify_parsed_type(generate_property('<length-percentage>'), 'calc(10px + 10%)', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length-percentage> (calc(10px + 10%))');

test(function(){
    verify_parsed_type(generate_property('<length>'), '10px', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length>');

test(function(){
    verify_parsed_type(generate_property('<number>'), '42', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <number>');

test(function(){
    verify_parsed_type(generate_property('<percentage>'), '10%', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <percentage>');

test(function(){
    verify_parsed_type(generate_property('<resolution>'), '300dpi', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <resolution>');

test(function(){
    verify_parsed_type(generate_property('<time>'), '42s', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <time>');

test(function(){
    verify_parsed_type(generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <transform-function>');

test(function(){
    verify_parsed_type(generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <transform-list>');

test(function(){
    verify_parsed_type(generate_property('<url>'), 'url(a)', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <url>');

test(function(){
    verify_parsed_type(generate_property('thing1 | THING2 | <url>'), 'THING2', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax thing1 | THING2 | <url>');

test(function(){
    verify_parsed_type(generate_property('<length>+'), '10px 20px', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length>+');

test(function(){
    verify_parsed_type(generate_property('<length>#'), '10px, 20px', CSSUnparsedValue);
}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length>#');

// Direct CSSStyleValue objects:

test_specified_maps(function(map){
    for (let syntax of all_syntaxes()) {
        let name = generate_property(syntax);

        let initialValue = target.computedStyleMap().get(name);

        // We only care about direct CSSStyleValue instances in this test.
        // Ultimately, in some future version of CSS TypedOM, we may have no
        // direct CSSStyleValue instances at all, which is fine.
        if (initialValue.constructor !== CSSStyleValue) {
            continue;
        }

        // Verify that direct CSSStyleValues are rejected by set(). Two things
        // should prevent this: 1) direct CSSStyleValues are not
        // CSSUnparsedValues, and 2) direct CSSStyleValues are only valid for
        // the property they were reified from.
        verify_map_not_set(map, generate_property(syntax), initialValue);
    }
}, 'Direct CSSStyleValue may not be set');

// StylePropertyMap iteration

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('*'), 'foo');
}, 'Specified * is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('foo'), 'foo');
}, 'Specified foo is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<angle>'), '10deg');
}, 'Specified <angle> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<color>'), 'green');
}, 'Specified <color> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<custom-ident>'), 'foo');
}, 'Specified <custom-ident> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<image>'), 'url("a")');
}, 'Specified <image> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<integer>'), '1');
}, 'Specified <integer> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<length-percentage>'), 'calc(10% + 10px)');
}, 'Specified <length-percentage> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<length>'), '10px');
}, 'Specified <length> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<number>'), '1');
}, 'Specified <number> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<percentage>'), '10%');
}, 'Specified <percentage> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<resolution>'), '10dpi');
}, 'Specified <resolution> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<time>'), '1s');
}, 'Specified <time> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Specified <transform-function> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)');
}, 'Specified <transform-list> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<url>'), 'url("a")');
}, 'Specified <url> is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<length>+'), '10px 11px');
}, 'Specified <length>+ is reified CSSUnparsedValue by iterator');

test_specified_maps(function(map){
    verify_map_iteration_unparsed(map, generate_property('<length>#'), '10px, 11px');
}, 'Specified <length># is reified CSSUnparsedValue by iterator');

// StylePropertyMapReadOnly iteration

test(function(){
    let name = generate_property({syntax: '<length>', initialValue: '10px'});
    let result = Array.from(target.computedStyleMap()).filter(e => e[0] == name)[0];
    assert_true(typeof(result) !== 'undefined');
}, 'Registered property with initial value show up on iteration of computedStyleMap');

test(function(){
    verify_computed_iteration_type(generate_property('*'), 'thing', CSSUnparsedValue);
}, 'Computed * is reified as CSSUnparsedValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('<angle>'), '42deg', CSSUnitValue);
}, 'Computed <angle> is reified as CSSUnitValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('<custom-ident>'), 'thing', CSSKeywordValue);
}, 'Computed <custom-ident> is reified as CSSKeywordValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('<image>'), 'url(\"a\")', CSSImageValue);
}, 'Computed <image> is reified as CSSImageValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('<integer>'), '100', CSSUnitValue);
}, 'Computed <integer> is reified as CSSUnitValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('<length>'), '10px', CSSUnitValue);
}, 'Computed <length> is reified as CSSUnitValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('<number>'), '42', CSSUnitValue);
}, 'Computed <number> is reified as CSSUnitValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('<percentage>'), '10%', CSSUnitValue);
}, 'Computed <percentage> is reified as CSSUnitValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('<resolution>'), '300dppx', CSSUnitValue);
}, 'Computed <resolution> is reified as CSSUnitValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('<time>'), '10s', CSSUnitValue);
}, 'Computed <time> is reified as CSSUnitValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('none | thing | THING'), 'THING', CSSKeywordValue);
}, 'Computed none | thing | THING is reified as CSSKeywordValue by iterator');

test(function(){
    verify_computed_iteration_type(generate_property('<angle> | <length>'), '10px', CSSUnitValue);
}, 'Computed <angle> | <length> is reified as CSSUnitValue by iterator');

</script>
