Commit efd690f5 authored by Darren Shen's avatar Darren Shen Committed by Commit Bot

[css-typed-om] Implement declared style property map.

Implements style property map for interacting with CSS rules.

The tests are copied from the inline style property map tests,
with the only change being createInlineStyle -> createDeclaredStyle.

Spec: https://drafts.css-houdini.org/css-typed-om-1/#declared-stylepropertymap-objects

Bug: 785132
Change-Id: I63aedf6d02ccacc506e7a1873c17fecafe466779
Reviewed-on: https://chromium-review.googlesource.com/823508
Commit-Queue: Darren Shen <shend@chromium.org>
Reviewed-by: default avatarmeade_UTC10 <meade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#526886}
parent 3a29249f
...@@ -114,6 +114,18 @@ function createComputedStyleMap(test, cssText) { ...@@ -114,6 +114,18 @@ function createComputedStyleMap(test, cssText) {
return createDivWithStyle(test, cssText).computedStyleMap(); return createDivWithStyle(test, cssText).computedStyleMap();
} }
// Creates a new style element with a rule |cssText| and returns
// its declared style property map.
function createDeclaredStyleMap(test, cssText) {
const style = document.createElement('style');
document.head.appendChild(style);
const rule = style.sheet.cssRules[style.sheet.insertRule('#test { ' + cssText + '}')];
test.add_cleanup(() => {
style.remove();
});
return rule.attributeStyleMap;
}
// Creates a new element with background image set to |imageValue| // Creates a new element with background image set to |imageValue|
// and returns a new Image element that can be used to attach // and returns a new Image element that can be used to attach
// event listeners regarding the image. // event listeners regarding the image.
......
<!doctype html>
<meta charset="utf-8">
<title>StylePropertyMap.append tests</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#append-to-a-stylepropertymap">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<body>
<script>
'use strict';
const gInvalidTestCases = [
{ property: 'lemon', values: ['ade'], desc: 'an unsupported property name' },
{ property: null, values: ['foo'], desc: 'an null property name' },
{ property: 'width', values: ['10px'], desc: 'a property that is not list valued' },
{ property: 'transition-duration', values: [CSS.px(10)], desc: 'an invalid CSSStyleValue' },
{ property: 'transition-duration', values: ['10px'], desc: 'an invalid String value' },
{ property: 'transition-duration', values: [CSS.s(1), '10px', CSS.px(10)], desc: 'a mix of valid and invalid values' },
];
for (const {property, values, desc} of gInvalidTestCases) {
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
assert_throws(new TypeError(), () => styleMap.append(property, ...values));
}, 'Calling StylePropertyMap.append with ' + desc + ' throws TypeError');
}
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
styleMap.append('transition-duration', CSS.s(1), '2s');
assert_style_value_array_equals(styleMap.getAll('transition-duration'),
[CSS.s(1), CSS.s(2)]);
styleMap.append('transition-duration', '3s', CSS.s(4));
assert_style_value_array_equals(styleMap.getAll('transition-duration'),
[CSS.s(1), CSS.s(2), CSS.s(3), CSS.s(4)]);
}, 'Appending a list-valued property with CSSStyleValue or String updates its values');
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
styleMap.append('transition-duration', '1s, 2s');
assert_style_value_array_equals(styleMap.getAll('transition-duration'),
[CSS.s(1), CSS.s(2)]);
styleMap.append('transition-duration', '3s, 4s');
assert_style_value_array_equals(styleMap.getAll('transition-duration'),
[CSS.s(1), CSS.s(2), CSS.s(3), CSS.s(4)]);
}, 'Appending a list-valued property with list-valued string updates its values');
test(t => {
let styleMap = createDeclaredStyleMap(t, 'transition-duration: 5s, 10s');
styleMap.append('tRaNsItIoN-dUrAtIoN', '1s', CSS.s(2));
const result = styleMap.getAll('transition-duration');
assert_style_value_array_equals(result,
[CSS.s(5), CSS.s(10), CSS.s(1), CSS.s(2)]);
}, 'StylePropertyMap.append is case-insensitive');
</script>
<!doctype html>
<meta charset="utf-8">
<title>Declared StylePropertyMap tests</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#declared-stylepropertymap-objects">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<style>
div {
height: 10px;
width: 50%;
width: 'lemon';
--foo: auto;
transition-duration: 1s, 2s;
color: 10;
}
#target {
height: 20px;
--foo: 1s;
width: 10%;
}
</style>
<div style="width: 50px">
<div id="target" style="top: 5px; --bar: auto;"></div>
</div>
<script>
'use strict';
const target = document.getElementById('target');
const styleMap = document.styleSheets[0].rules[0].attributeStyleMap;
test(() => {
const properties = styleMap.getProperties();
assert_array_equals(properties, ['height', 'transition-duration', 'width', '--foo']);
}, 'Declared StylePropertyMap only contains properties in the style rule');
test(() => {
assert_style_value_equals(styleMap.get('height'), CSS.px(10));
}, 'Declared StylePropertyMap contains CSS property declarations in style rules');
test(() => {
assert_equals(styleMap.get('top'), null);
assert_equals(styleMap.get('--bar'), null);
}, 'Declared StylePropertyMap does not contain inline styles');
test(() => {
assert_style_value_equals(styleMap.get('--foo'), new CSSUnparsedValue(' auto'));
}, 'Declared StylePropertyMap contains custom property declarations');
test(() => {
assert_equals(styleMap.get('color'), null);
}, 'Declared StylePropertyMap does not contain properties with invalid values');
test(() => {
assert_style_value_equals(styleMap.get('width'), CSS.percent(50));
}, 'Declared StylePropertyMap contains properties with their last valid value');
test(() => {
const style = document.createElement('style');
document.head.appendChild(style);
style.sheet.insertRule('.test { width: 10px; }');
let rule = style.sheet.rules[0];
let styleMap = rule.attributeStyleMap;
assert_style_value_equals(styleMap.get('width'), CSS.px(10));
rule.style.width = '20px';
assert_style_value_equals(styleMap.get('width'), CSS.px(20));
styleMap.set('width', CSS.px(30));
assert_equals(rule.cssText, '.test { width: 30px; }');
}, 'Declared StylePropertyMap is live');
</script>
This is a testharness.js-based test.
PASS Calling StylePropertyMap.delete with an unsupported property name throws a TypeError
PASS Deleting a property not in a StylePropertyMap is a no-op
PASS Deleting a property in the property model removes it from the property model
FAIL Deleting a custom property in the property model removes it from the property model Failed to execute 'delete' on 'StylePropertyMap': Invalid propertyName: --foo
PASS Deleting a list-valued property in the property model removes it from the property model
PASS Deleting a mixed-case property in the property model removes it from the property model
Harness: the test ran to completion.
<!doctype html>
<meta charset="utf-8">
<title>StylePropertyMap.get tests</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#get-a-value-from-a-stylepropertymap">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<body>
<script>
'use strict';
test(t => {
const styleMap = createDeclaredStyleMap(t);
assert_throws(new TypeError(), () => styleMap.get('lemon'));
}, 'Calling StylePropertyMap.get with an unsupported property throws a TypeError');
test(t => {
const styleMap = createDeclaredStyleMap(t);
assert_equals(styleMap.get('height'), null);
}, 'Calling StylePropertyMap.get with a property not in the property model returns null');
test(t => {
const styleMap = createDeclaredStyleMap(t, '--foo: auto');
assert_equals(styleMap.get('--Foo'), null);
}, 'Calling StylePropertyMap.get with a custom property not in the property model returns null');
test(t => {
const styleMap = createDeclaredStyleMap(t, 'width: 10px; height: 20px');
assert_style_value_equals(styleMap.get('width'), CSS.px(10));
}, 'Calling StylePropertyMap.get with a valid property returns the correct entry');
test(t => {
const styleMap = createDeclaredStyleMap(t, 'height: 20px; width: 10px;');
assert_style_value_equals(styleMap.get('wIdTh'), CSS.px(10));
}, 'StylePropertyMap.get with a valid property in mixed case returns the correct entry');
test(t => {
const styleMap = createDeclaredStyleMap(t, '--foo: auto; --bar: 10px');
assert_style_value_equals(styleMap.get('--foo'), new CSSUnparsedValue(' auto'));
}, 'Calling StylePropertyMap.get with a valid custom property returns the correct entry');
test(t => {
const styleMap = createDeclaredStyleMap(t, 'width: 10px; transition-duration: 1s, 2s; height: 10px;');
assert_style_value_equals(styleMap.get('transition-duration'), CSS.s(1));
}, 'Calling StylePropertyMap.get with a list-valued property returns only the first value');
</script>
<!doctype html>
<meta charset="utf-8">
<title>StylePropertyMap.getAll tests</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#stylepropertymap">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<body>
<script>
'use strict';
test(t => {
const styleMap = createDeclaredStyleMap(t);
assert_throws(new TypeError(), () => styleMap.getAll('lemon'));
}, 'Calling StylePropertyMap.getAll with an unsupported property throws a TypeError');
test(t => {
const styleMap = createDeclaredStyleMap(t);
assert_style_value_array_equals(styleMap.getAll('height'), []);
}, 'Calling StylePropertyMap.getAll with a property not in the property model returns an empty list');
test(t => {
const styleMap = createDeclaredStyleMap(t, '--foo: auto');
assert_style_value_array_equals(styleMap.getAll('--Foo'), []);
}, 'Calling StylePropertyMap.getAll with a custom property not in the property model returns an empty list');
test(t => {
const styleMap = createDeclaredStyleMap(t, 'width: 10px; height: 20px');
assert_style_value_array_equals(styleMap.getAll('width'), [CSS.px(10)]);
}, 'Calling StylePropertyMap.getAll with a valid property returns a single element list with the correct entry');
test(t => {
const styleMap = createDeclaredStyleMap(t, 'height: 20px; width: 10px');
assert_style_value_array_equals(styleMap.getAll('wIdTh'), [CSS.px(10)]);
}, 'StylePropertyMap.getAll is case-insensitive');
test(t => {
const styleMap = createDeclaredStyleMap(t, '--foo: auto; --bar: 10px');
assert_style_value_array_equals(styleMap.getAll('--foo'), [new CSSUnparsedValue(' auto')]);
}, 'Calling StylePropertyMap.getAll with a valid custom property returns a single element list with the correct entry');
test(t => {
const styleMap = createDeclaredStyleMap(t, 'width: 10px; transition-duration: 1s, 2s; height: 20px');
assert_style_value_array_equals(styleMap.getAll('transition-duration'), [CSS.s(1), CSS.s(2)]);
}, 'Calling StylePropertyMap.getAll with a list-valued property returns all the values');
</script>
<!doctype html>
<meta charset="utf-8">
<title>StylePropertyMap.getProperties tests</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-getproperties">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<body>
<script>
'use strict';
test(t => {
const styleMap = createDeclaredStyleMap(t, '');
assert_array_equals(styleMap.getProperties(), []);
}, 'Calling StylePropertyMap.getProperties on an empty property model returns a zero-length array');
test(t => {
const styleMap = createDeclaredStyleMap(t, '--A: A; width: 0px; --C: C; transition-duration: 1s, 2s; color: red; --B: B;');
assert_array_equals(styleMap.getProperties(),
['color', 'transition-duration', 'width', '--A', '--B', '--C']);
}, 'StylePropertyMap.getProperties returns CSS properties in alphabetical order then custom properties by codepoint');
</script>
<!doctype html>
<meta charset="utf-8">
<title>StylePropertyMap.has tests</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#check-if-stylepropertymap-has-a-property">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<body>
<script>
'use strict';
test(t => {
const styleMap = createDeclaredStyleMap(t);
assert_throws(new TypeError(), () => styleMap.has('lemon'));
}, 'Calling StylePropertyMap.has with an unsupported property throws a TypeError');
const gTestCases = [
{ property: 'height', expected: false, desc: 'a property not in the property model' },
{ property: '--Foo', expected: false, desc: 'a custom property not in the property model' },
{ property: 'width', expected: true, desc: 'a valid property' },
{ property: 'wIdTh', expected: true, desc: 'a valid property in mixed case' },
{ property: '--foo', expected: true, desc: 'a valid custom property' },
{ property: 'transition-duration', expected: true, desc: 'a valid list-valued property' },
];
for (const {property, expected, desc} of gTestCases) {
test(t => {
const styleMap = createDeclaredStyleMap(t, 'width: 10px; --foo: auto; transition-duration: 1s, 2s');
assert_equals(styleMap.has(property), expected);
}, 'Calling StylePropertyMap.has with ' + desc + ' returns ' + expected);
}
</script>
<!doctype html>
<meta charset="utf-8">
<title>StylePropertyMap iterable tests</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#the-stylepropertymap">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<body>
<script>
'use strict';
test(t => {
const styleMap = createDeclaredStyleMap(t, '');
assert_array_equals([...styleMap.entries()], []);
}, 'Iterating over an empty StylePropertyMap gives a zero-length array');
test(t => {
const styleMap = createDeclaredStyleMap(t, '--A: A; width: 10px; --C: C; transition-duration: 1s, 2s; color: red; --B: B;');
assert_array_equals([...styleMap.keys()],
['color', 'transition-duration', 'width', '--A', '--B', '--C']);
}, 'StylePropertyMap iterates properties in correct order');
test(t => {
const styleMap = createDeclaredStyleMap(t, 'height: 5px; width: 10px;');
const keys = [...styleMap.keys()], values = [...styleMap.values()];
assert_array_equals(keys, ['height', 'width']);
assert_style_value_array_equals(values, [CSS.px(5), CSS.px(10)]);
}, 'StylePropertyMap iterator returns CSS properties with the correct CSSStyleValue');
test(t => {
const styleMap = createDeclaredStyleMap(t, 'transition-duration: 1s, 2s');
const keys = [...styleMap.keys()], values = [...styleMap.values()];
assert_array_equals(keys, ['transition-duration']);
assert_style_value_array_equals(values[0], [CSS.s(1), CSS.s(2)]);
}, 'StylePropertyMap iterator returns list-valued properties with the correct CSSStyleValue');
test(t => {
const styleMap = createDeclaredStyleMap(t, '--A: A; --B: B; --C: C');
const keys = [...styleMap.keys()], values = [...styleMap.values()];
assert_array_equals(keys, ['--A', '--B', '--C']);
assert_style_value_array_equals(values, [
new CSSUnparsedValue(' A'),
new CSSUnparsedValue(' B'),
new CSSUnparsedValue(' C'),
])
}, 'StylePropertyMap iterator returns custom properties with the correct CSSStyleValue');
</script>
This is a testharness.js-based test.
PASS Setting a StylePropertyMap with an unsupported property name throws TypeError
PASS Setting a StylePropertyMap with an null property name throws TypeError
PASS Setting a StylePropertyMap with an invalid CSSStyleValue throws TypeError
PASS Setting a StylePropertyMap with an invalid String throws TypeError
PASS Setting a non list-valued property with multiple arguments throws TypeError
PASS Setting a non list-valued property with list-valued string throws TypeError
PASS Setting a property with CSSStyleValue or String updates its value
PASS Setting a list-valued property with CSSStyleValue or String updates its values
PASS Setting a list-valued property with a list-valued string updates its value
FAIL Setting a custom property with CSSStyleValue or String updates its value Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: --foo
PASS StylePropertyMap.set is case-insensitive
Harness: the test ran to completion.
<!doctype html>
<meta charset="utf-8">
<title>StylePropertyMap.set</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#set-a-value-on-a-stylepropertymap">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<body>
<script>
'use strict';
const gInvalidTestCases = [
{ property: 'lemon', values: ['ade'], desc: 'an unsupported property name' },
{ property: null, values: ['foo'], desc: 'an null property name' },
{ property: 'width', values: [CSS.deg(0)], desc: 'an invalid CSSStyleValue' },
{ property: 'width', values: ['10s'], desc: 'an invalid String' },
];
for (const {property, values, desc} of gInvalidTestCases) {
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
assert_throws(new TypeError(), () => styleMap.set(property, ...values));
}, 'Setting a StylePropertyMap with ' + desc + ' throws TypeError');
}
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
assert_throws(new TypeError(), () => styleMap.set('width', CSS.px(10), CSS.px(10)));
}, 'Setting a non list-valued property with multiple arguments throws TypeError');
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
assert_throws(new TypeError(), () => styleMap.set('width', '1s, 2s'));
}, 'Setting a non list-valued property with list-valued string throws TypeError');
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
styleMap.set('width', CSS.px(10));
assert_style_value_array_equals(styleMap.get('width'), CSS.px(10));
styleMap.set('width', '20px');
assert_style_value_array_equals(styleMap.get('width'), CSS.px(20));
}, 'Setting a property with CSSStyleValue or String updates its value');
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
styleMap.set('transition-duration', CSS.s(1), '2s');
assert_style_value_array_equals(styleMap.getAll('transition-duration'), [CSS.s(1), CSS.s(2)]);
styleMap.set('transition-duration', '3s', CSS.s(4));
assert_style_value_array_equals(styleMap.getAll('transition-duration'), [CSS.s(3), CSS.s(4)]);
}, 'Setting a list-valued property with CSSStyleValue or String updates its values');
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
styleMap.set('transition-duration', '1s, 2s');
assert_style_value_array_equals(styleMap.getAll('transition-duration'), [CSS.s(1), CSS.s(2)]);
}, 'Setting a list-valued property with a list-valued string updates its value');
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
styleMap.set('--foo', CSS.px(10));
assert_style_value_array_equals(styleMap.get('--foo'), CSS.px(10));
styleMap.set('--foo', '20px');
assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue('20px'));
}, 'Setting a custom property with CSSStyleValue or String updates its value');
test(t => {
let styleMap = createDeclaredStyleMap(t, 'transition-duration: 5s, 10s');
styleMap.set('tRaNsItIoN-dUrAtIoN', '1s', CSS.s(2));
const result = styleMap.getAll('transition-duration');
assert_style_value_array_equals(result, [CSS.s(1), CSS.s(2)]);
}, 'StylePropertyMap.set is case-insensitive');
</script>
<!doctype html>
<meta charset="utf-8">
<title>Declared StylePropertyMap tests</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#declared-stylepropertymap-objects">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<style>
#target { width: 100px; height: 100px; background: red; }
</style>
<body>
<div id="target">
<script>
'use strict';
test(() => {
let rule = document.styleSheets[0].rules[0];
rule.attributeStyleMap.set('width', CSS.px(200));
assert_equals(getComputedStyle(target).width, '200px')
rule.attributeStyleMap.set('width', CSS.px(150));
assert_equals(getComputedStyle(target).width, '150px')
});
</script>
</body>
This is a testharness.js-based test.
PASS Updating a StylePropertyMap with an unsupported property name throws TypeError
PASS Updating a StylePropertyMap with an null property name throws TypeError
PASS Updating a StylePropertyMap with an invalid CSSStyleValue throws TypeError
PASS Updating a StylePropertyMap with an invalid String throws TypeError
PASS Updating a property with CSSStyleValue updates its value
PASS Updating a list-valued property with CSSStyleValue updates its value
FAIL Updating a custom property with CSSStyleValue updates its value Failed to execute 'update' on 'StylePropertyMap': Invalid propertyName: --foo
PASS Calling StylePropertyMap.update on an empty property model calls update function with null
PASS Calling StylePropertyMap.update on an existing property calls update function with old value
PASS Calling StylePropertyMap.update on an existing list-valued property calls update function with first value
PASS StylePropertyMap.update is case-insensitive
Harness: the test ran to completion.
<!doctype html>
<meta charset="utf-8">
<title>StylePropertyMap.update tests</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#update-a-value-in-a-stylepropertymap">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<body>
<script>
'use strict';
const gInvalidTestCases = [
{ property: 'lemon', value: 'ade', desc: 'an unsupported property name' },
{ property: null, value: 'foo', desc: 'an null property name' },
{ property: 'width', value: CSS.deg(0), desc: 'an invalid CSSStyleValue' },
{ property: 'width', value: '10s', desc: 'an invalid String' },
];
for (const {property, value, desc} of gInvalidTestCases) {
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
assert_throws(new TypeError(), () => styleMap.update(property, () => value));
}, 'Updating a StylePropertyMap with ' + desc + ' throws TypeError');
}
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
styleMap.update('width', () => CSS.px(10));
assert_style_value_array_equals(styleMap.get('width'), CSS.px(10));
styleMap.update('width', () => CSS.px(20));
assert_style_value_array_equals(styleMap.get('width'), CSS.px(20));
}, 'Updating a property with CSSStyleValue updates its value');
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
styleMap.update('transition-duration', () => CSS.s(1));
assert_style_value_array_equals(styleMap.getAll('transition-duration'), [CSS.s(1)]);
styleMap.update('transition-duration', () => CSS.s(2));
assert_style_value_array_equals(styleMap.getAll('transition-duration'), [CSS.s(2)]);
}, 'Updating a list-valued property with CSSStyleValue updates its value');
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
styleMap.update('--foo', () => CSS.px(10));
assert_style_value_array_equals(styleMap.get('--foo'), CSS.px(10));
styleMap.update('--foo', () => CSS.px(20));
assert_style_value_array_equals(styleMap.get('--foo'), CSS.px(20));
}, 'Updating a custom property with CSSStyleValue updates its value');
test(t => {
let styleMap = createDeclaredStyleMap(t, '');
styleMap.update('width', oldValue => {
assert_equals(oldValue, null);
return CSS.px(10);
});
}, 'Calling StylePropertyMap.update on an empty property model calls update function with null');
test(t => {
let styleMap = createDeclaredStyleMap(t, 'width: 10px');
styleMap.update('width', oldValue => {
assert_style_value_equals(oldValue, CSS.px(10));
return CSS.px(20);
});
}, 'Calling StylePropertyMap.update on an existing property calls update function with old value');
test(t => {
let styleMap = createDeclaredStyleMap(t, 'transition-duration: 1s, 2s');
styleMap.update('transition-duration', oldValue => {
assert_style_value_equals(oldValue, CSS.s(1));
return CSS.s(2);
});
}, 'Calling StylePropertyMap.update on an existing list-valued property calls update function with first value');
test(t => {
let styleMap = createDeclaredStyleMap(t, 'width: 10px');
styleMap.update('wIdTh', () => CSS.px(20));
const result = styleMap.get('width');
assert_style_value_equals(result, CSS.px(20));
}, 'StylePropertyMap.update is case-insensitive');
</script>
...@@ -798,6 +798,7 @@ interface CSSStyleDeclaration ...@@ -798,6 +798,7 @@ interface CSSStyleDeclaration
setter cssText setter cssText
interface CSSStyleRule : CSSRule interface CSSStyleRule : CSSRule
attribute @@toStringTag attribute @@toStringTag
getter attributeStyleMap
getter selectorText getter selectorText
getter style getter style
method constructor method constructor
......
...@@ -361,6 +361,8 @@ blink_core_sources("css") { ...@@ -361,6 +361,8 @@ blink_core_sources("css") {
"cssom/CSSUnsupportedStyleValue.h", "cssom/CSSUnsupportedStyleValue.h",
"cssom/ComputedStylePropertyMap.cpp", "cssom/ComputedStylePropertyMap.cpp",
"cssom/ComputedStylePropertyMap.h", "cssom/ComputedStylePropertyMap.h",
"cssom/DeclaredStylePropertyMap.cpp",
"cssom/DeclaredStylePropertyMap.h",
"cssom/ElementComputedStyleMap.h", "cssom/ElementComputedStyleMap.h",
"cssom/FilteredComputedStylePropertyMap.cpp", "cssom/FilteredComputedStylePropertyMap.cpp",
"cssom/FilteredComputedStylePropertyMap.h", "cssom/FilteredComputedStylePropertyMap.h",
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "core/css/CSSStyleSheet.h" #include "core/css/CSSStyleSheet.h"
#include "core/css/StyleRule.h" #include "core/css/StyleRule.h"
#include "core/css/StyleRuleCSSStyleDeclaration.h" #include "core/css/StyleRuleCSSStyleDeclaration.h"
#include "core/css/cssom/DeclaredStylePropertyMap.h"
#include "core/css/parser/CSSParser.h" #include "core/css/parser/CSSParser.h"
#include "core/dom/ExecutionContext.h" #include "core/dom/ExecutionContext.h"
#include "platform/wtf/text/StringBuilder.h" #include "platform/wtf/text/StringBuilder.h"
...@@ -41,7 +42,9 @@ static SelectorTextCache& GetSelectorTextCache() { ...@@ -41,7 +42,9 @@ static SelectorTextCache& GetSelectorTextCache() {
} }
CSSStyleRule::CSSStyleRule(StyleRule* style_rule, CSSStyleSheet* parent) CSSStyleRule::CSSStyleRule(StyleRule* style_rule, CSSStyleSheet* parent)
: CSSRule(parent), style_rule_(style_rule) {} : CSSRule(parent),
style_rule_(style_rule),
attribute_style_map_(new DeclaredStylePropertyMap(this)) {}
CSSStyleRule::~CSSStyleRule() = default; CSSStyleRule::~CSSStyleRule() = default;
...@@ -108,6 +111,7 @@ void CSSStyleRule::Reattach(StyleRuleBase* rule) { ...@@ -108,6 +111,7 @@ void CSSStyleRule::Reattach(StyleRuleBase* rule) {
void CSSStyleRule::Trace(blink::Visitor* visitor) { void CSSStyleRule::Trace(blink::Visitor* visitor) {
visitor->Trace(style_rule_); visitor->Trace(style_rule_);
visitor->Trace(properties_cssom_wrapper_); visitor->Trace(properties_cssom_wrapper_);
visitor->Trace(attribute_style_map_);
CSSRule::Trace(visitor); CSSRule::Trace(visitor);
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#define CSSStyleRule_h #define CSSStyleRule_h
#include "core/css/CSSRule.h" #include "core/css/CSSRule.h"
#include "core/css/cssom/StylePropertyMap.h"
#include "platform/heap/Handle.h" #include "platform/heap/Handle.h"
namespace blink { namespace blink {
...@@ -50,6 +51,10 @@ class CORE_EXPORT CSSStyleRule final : public CSSRule { ...@@ -50,6 +51,10 @@ class CORE_EXPORT CSSStyleRule final : public CSSRule {
CSSStyleDeclaration* style() const; CSSStyleDeclaration* style() const;
StylePropertyMap* attributeStyleMap() const {
return attribute_style_map_.Get();
}
// FIXME: Not CSSOM. Remove. // FIXME: Not CSSOM. Remove.
StyleRule* GetStyleRule() const { return style_rule_.Get(); } StyleRule* GetStyleRule() const { return style_rule_.Get(); }
...@@ -62,6 +67,7 @@ class CORE_EXPORT CSSStyleRule final : public CSSRule { ...@@ -62,6 +67,7 @@ class CORE_EXPORT CSSStyleRule final : public CSSRule {
Member<StyleRule> style_rule_; Member<StyleRule> style_rule_;
mutable Member<StyleRuleCSSStyleDeclaration> properties_cssom_wrapper_; mutable Member<StyleRuleCSSStyleDeclaration> properties_cssom_wrapper_;
Member<StylePropertyMap> attribute_style_map_;
}; };
DEFINE_CSS_RULE_TYPE_CASTS(CSSStyleRule, kStyleRule); DEFINE_CSS_RULE_TYPE_CASTS(CSSStyleRule, kStyleRule);
......
...@@ -23,4 +23,5 @@ ...@@ -23,4 +23,5 @@
interface CSSStyleRule : CSSRule { interface CSSStyleRule : CSSRule {
[SetterCallWith=ExecutionContext] attribute DOMString selectorText; [SetterCallWith=ExecutionContext] attribute DOMString selectorText;
[SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style;
[SameObject, RuntimeEnabled=CSSTypedOM] readonly attribute StylePropertyMap attributeStyleMap;
}; };
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/css/cssom/DeclaredStylePropertyMap.h"
#include "core/css/CSSCustomPropertyDeclaration.h"
#include "core/css/CSSPropertyValueSet.h"
#include "core/css/CSSStyleRule.h"
#include "core/css/CSSStyleSheet.h"
#include "core/css/StyleRule.h"
namespace blink {
DeclaredStylePropertyMap::DeclaredStylePropertyMap(CSSStyleRule* owner_rule)
: StylePropertyMap(), owner_rule_(owner_rule) {}
const CSSValue* DeclaredStylePropertyMap::GetProperty(
CSSPropertyID property_id) {
if (!GetStyleRule())
return nullptr;
return GetStyleRule()->Properties().GetPropertyCSSValue(property_id);
}
const CSSValue* DeclaredStylePropertyMap::GetCustomProperty(
AtomicString property_name) {
if (!GetStyleRule())
return nullptr;
return GetStyleRule()->Properties().GetPropertyCSSValue(property_name);
}
void DeclaredStylePropertyMap::SetProperty(CSSPropertyID property_id,
const CSSValue& value) {
if (!GetStyleRule())
return;
CSSStyleSheet::RuleMutationScope mutation_scope(owner_rule_);
GetStyleRule()->MutableProperties().SetProperty(property_id, value);
}
void DeclaredStylePropertyMap::RemoveProperty(CSSPropertyID property_id) {
if (!GetStyleRule())
return;
CSSStyleSheet::RuleMutationScope mutation_scope(owner_rule_);
GetStyleRule()->MutableProperties().RemoveProperty(property_id);
}
void DeclaredStylePropertyMap::RemoveCustomProperty(
const AtomicString& property_name) {
if (!GetStyleRule())
return;
CSSStyleSheet::RuleMutationScope mutation_scope(owner_rule_);
GetStyleRule()->MutableProperties().RemoveProperty(property_name);
}
void DeclaredStylePropertyMap::ForEachProperty(
const IterationCallback& callback) {
const CSSPropertyValueSet& declared_style_set = GetStyleRule()->Properties();
for (unsigned i = 0; i < declared_style_set.PropertyCount(); i++) {
const auto& property_reference = declared_style_set.PropertyAt(i);
if (property_reference.Id() == CSSPropertyVariable) {
const auto& decl =
ToCSSCustomPropertyDeclaration(property_reference.Value());
callback(decl.GetName(), property_reference.Value());
} else {
const CSSProperty& property = CSSProperty::Get(property_reference.Id());
callback(property.GetPropertyNameAtomicString(),
property_reference.Value());
}
}
}
StyleRule* DeclaredStylePropertyMap::GetStyleRule() const {
if (!owner_rule_)
return nullptr;
return owner_rule_->GetStyleRule();
}
} // namespace blink
// Copyright 2017 the Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DeclaredStylePropertyMap_h
#define DeclaredStylePropertyMap_h
#include "core/css/cssom/StylePropertyMap.h"
namespace blink {
class CSSStyleRule;
class StyleRule;
// This class implements declared StylePropertMap in the Typed CSSOM
// API. The specification is here:
// https://drafts.css-houdini.org/css-typed-om-1/#declared-stylepropertymap-objects
//
// The declared StylePropertyMap retrieves styles specified by a CSS style rule
// and returns them as CSSStyleValues. The IDL for this class is in
// StylePropertyMap.idl. The declared StylePropertyMap for an element is
// accessed via CSSStyleRule.attributeStyleMap (see CSSStyleRule.idl)
class CORE_EXPORT DeclaredStylePropertyMap final : public StylePropertyMap {
WTF_MAKE_NONCOPYABLE(DeclaredStylePropertyMap);
public:
explicit DeclaredStylePropertyMap(CSSStyleRule* owner_rule);
virtual void Trace(blink::Visitor* visitor) {
visitor->Trace(owner_rule_);
StylePropertyMap::Trace(visitor);
}
protected:
const CSSValue* GetProperty(CSSPropertyID) override;
const CSSValue* GetCustomProperty(AtomicString) override;
void ForEachProperty(const IterationCallback&) override;
void SetProperty(CSSPropertyID, const CSSValue&) override;
void RemoveProperty(CSSPropertyID) override;
void RemoveCustomProperty(const AtomicString&) override;
private:
StyleRule* GetStyleRule() const;
WeakMember<CSSStyleRule> owner_rule_;
};
} // namespace blink
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment