Skip to content

Commit 86a3490

Browse files
committed
fix: convert value to DOMString
1 parent b527ed7 commit 86a3490

File tree

3 files changed

+132
-22
lines changed

3 files changed

+132
-22
lines changed

lib/CSSStyleDeclaration.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ var CSSOM = require('cssom');
77
var allProperties = require('./allProperties');
88
var allExtraProperties = require('./allExtraProperties');
99
var implementedProperties = require('./implementedProperties');
10-
var { dashedToCamelCase } = require('./parsers');
10+
var { dashedToCamelCase, toDOMString } = require('./parsers');
1111
var getBasicPropertyDescriptor = require('./utils/getBasicPropertyDescriptor');
1212

1313
/**
@@ -49,10 +49,11 @@ CSSStyleDeclaration.prototype = {
4949
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty
5050
*/
5151
setProperty: function(name, value, priority) {
52+
value = toDOMString(value);
5253
if (value === undefined) {
5354
return;
5455
}
55-
if (value === null || value === '') {
56+
if (value === '') {
5657
this.removeProperty(name);
5758
return;
5859
}
@@ -70,10 +71,11 @@ CSSStyleDeclaration.prototype = {
7071
this._importants[lowercaseName] = priority;
7172
},
7273
_setProperty: function(name, value, priority) {
74+
value = toDOMString(value);
7375
if (value === undefined) {
7476
return;
7577
}
76-
if (value === null || value === '') {
78+
if (value === '') {
7779
this.removeProperty(name);
7880
return;
7981
}

lib/CSSStyleDeclaration.test.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ var allowedProperties = dashedProperties.map(parsers.dashedToCamelCase);
1212
implementedProperties = Array.from(implementedProperties).map(parsers.dashedToCamelCase);
1313
var invalidProperties = implementedProperties.filter(prop => !allowedProperties.includes(prop));
1414

15+
var BigInt = BigInt || Number;
16+
1517
describe('CSSStyleDeclaration', () => {
1618
test('has only valid properties implemented', () => {
1719
expect(invalidProperties.length).toEqual(0);
@@ -352,6 +354,85 @@ describe('CSSStyleDeclaration', () => {
352354
expect(style.fillOpacity).toEqual('0');
353355
});
354356

357+
test('setting a property with a value that can not be converted to string', () => {
358+
const style = new CSSStyleDeclaration();
359+
360+
style.setProperty('--custom', '1');
361+
expect(style.getPropertyValue('--custom')).toBe('1');
362+
363+
style.setProperty('--custom', undefined);
364+
expect(style.getPropertyValue('--custom')).toBe('1');
365+
366+
expect(() => {
367+
style.setProperty('--custom', Symbol('0'));
368+
}).toThrow('Cannot convert symbol to string');
369+
370+
style.setProperty('--custom', {
371+
toString() {
372+
return null;
373+
},
374+
});
375+
expect(style.getPropertyValue('--custom')).toBe('1');
376+
377+
expect(() => {
378+
style.setProperty('--custom', {
379+
toString() {
380+
return [0];
381+
},
382+
});
383+
}).toThrow('Cannot convert value to string');
384+
});
385+
386+
test('setting a property with a value that can be converted to string', () => {
387+
const style = new CSSStyleDeclaration();
388+
389+
style.setProperty('--custom', '1');
390+
expect(style.getPropertyValue('--custom')).toBe('1');
391+
392+
style.setProperty('--custom', null);
393+
expect(style.getPropertyValue('--custom')).toBe('');
394+
style.setProperty('--custom', '1');
395+
396+
style.setProperty('--custom', false);
397+
expect(style.getPropertyValue('--custom')).toBe('false');
398+
399+
style.setProperty('--custom', 0);
400+
expect(style.getPropertyValue('--custom')).toBe('0');
401+
style.setProperty('--custom', '1');
402+
403+
style.setProperty('--custom', BigInt(0));
404+
expect(style.getPropertyValue('--custom')).toBe('0');
405+
style.setProperty('--custom', '1');
406+
407+
style.setProperty('--custom', [0]);
408+
expect(style.getPropertyValue('--custom')).toEqual('0');
409+
style.setProperty('--custom', '1');
410+
411+
style.setProperty('--custom', {
412+
toString: function() {
413+
return '0';
414+
},
415+
});
416+
expect(style.getPropertyValue('--custom')).toEqual('0');
417+
style.setProperty('--custom', '1');
418+
419+
style.setProperty('--custom', {
420+
toString: function() {
421+
return 0;
422+
},
423+
});
424+
expect(style.getPropertyValue('--custom')).toEqual('0');
425+
style.setProperty('--custom', '1');
426+
427+
style.setProperty('--custom', {
428+
toString: function() {
429+
return BigInt(0);
430+
},
431+
});
432+
expect(style.getPropertyValue('--custom')).toEqual('0');
433+
style.setProperty('--custom', '1');
434+
});
435+
355436
test('onchange callback should be called when the csstext changes', () => {
356437
var style = new CSSStyleDeclaration(function(cssText) {
357438
expect(cssText).toEqual('opacity: 0;');

lib/parsers.js

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ exports.TYPES = {
1717
STRING: 7,
1818
ANGLE: 8,
1919
KEYWORD: 9,
20-
NULL_OR_EMPTY_STR: 10,
20+
EMPTY: 10,
2121
CALC: 11,
2222
};
2323

@@ -35,19 +35,46 @@ var calcRegEx = /^calc\(([^)]*)\)$/;
3535
var colorRegEx4 = /^hsla?\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*(,\s*(-?\d+|-?\d*.\d+)\s*)?\)/;
3636
var angleRegEx = /^([-+]?[0-9]*\.?[0-9]+)(deg|grad|rad)$/;
3737

38-
// This will return one of the above types based on the passed in string
39-
exports.valueType = function valueType(val) {
40-
if (val === '' || val === null) {
41-
return exports.TYPES.NULL_OR_EMPTY_STR;
38+
// https://heycam.github.io/webidl/#es-DOMString
39+
exports.toDOMString = function toDOMString(val) {
40+
if (val === null) {
41+
return '';
42+
}
43+
if (val === undefined) {
44+
return val;
45+
}
46+
if (typeof val === 'string') {
47+
return val;
48+
}
49+
if (typeof val === 'symbol') {
50+
throw Error('Cannot convert symbol to string');
4251
}
43-
if (typeof val === 'number') {
52+
if (typeof val.toString === 'function') {
4453
val = val.toString();
54+
if (val === null) {
55+
return undefined;
56+
}
57+
if (typeof val !== 'string') {
58+
if (typeof val === 'number' || typeof val === 'bigint') {
59+
val = val.toString();
60+
} else {
61+
throw Error('Cannot convert value to string');
62+
}
63+
}
4564
}
65+
return val.toString();
66+
};
4667

47-
if (typeof val !== 'string') {
48-
return undefined;
49-
}
68+
// This will return one of the above types based on the passed in string
69+
exports.valueType = function valueType(val) {
70+
val = exports.toDOMString(val);
5071

72+
if (val === undefined) {
73+
return val;
74+
}
75+
if (val === '') {
76+
return exports.TYPES.EMPTY;
77+
}
5178
if (integerRegEx.test(val)) {
5279
return exports.TYPES.INTEGER;
5380
}
@@ -157,7 +184,7 @@ exports.valueType = function valueType(val) {
157184

158185
exports.parseInteger = function parseInteger(val) {
159186
var type = exports.valueType(val);
160-
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
187+
if (type === exports.TYPES.EMPTY) {
161188
return val;
162189
}
163190
if (type !== exports.TYPES.INTEGER) {
@@ -168,7 +195,7 @@ exports.parseInteger = function parseInteger(val) {
168195

169196
exports.parseNumber = function parseNumber(val) {
170197
var type = exports.valueType(val);
171-
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
198+
if (type === exports.TYPES.EMPTY) {
172199
return val;
173200
}
174201
if (type !== exports.TYPES.NUMBER && type !== exports.TYPES.INTEGER) {
@@ -182,7 +209,7 @@ exports.parseLength = function parseLength(val) {
182209
return '0px';
183210
}
184211
var type = exports.valueType(val);
185-
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
212+
if (type === exports.TYPES.EMPTY) {
186213
return val;
187214
}
188215
if (type !== exports.TYPES.LENGTH) {
@@ -196,7 +223,7 @@ exports.parsePercent = function parsePercent(val) {
196223
return '0%';
197224
}
198225
var type = exports.valueType(val);
199-
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
226+
if (type === exports.TYPES.EMPTY) {
200227
return val;
201228
}
202229
if (type !== exports.TYPES.PERCENT) {
@@ -221,7 +248,7 @@ exports.parseMeasurement = function parseMeasurement(val) {
221248

222249
exports.parseUrl = function parseUrl(val) {
223250
var type = exports.valueType(val);
224-
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
251+
if (type === exports.TYPES.EMPTY) {
225252
return val;
226253
}
227254
var res = urlRegEx.exec(val);
@@ -260,7 +287,7 @@ exports.parseUrl = function parseUrl(val) {
260287

261288
exports.parseString = function parseString(val) {
262289
var type = exports.valueType(val);
263-
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
290+
if (type === exports.TYPES.EMPTY) {
264291
return val;
265292
}
266293
if (type !== exports.TYPES.STRING) {
@@ -287,7 +314,7 @@ exports.parseString = function parseString(val) {
287314

288315
exports.parseColor = function parseColor(val) {
289316
var type = exports.valueType(val);
290-
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
317+
if (type === exports.TYPES.EMPTY) {
291318
return val;
292319
}
293320
var red,
@@ -406,7 +433,7 @@ exports.parseColor = function parseColor(val) {
406433

407434
exports.parseAngle = function parseAngle(val) {
408435
var type = exports.valueType(val);
409-
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
436+
if (type === exports.TYPES.EMPTY) {
410437
return val;
411438
}
412439
if (type !== exports.TYPES.ANGLE) {
@@ -431,7 +458,7 @@ exports.parseAngle = function parseAngle(val) {
431458

432459
exports.parseKeyword = function parseKeyword(val, valid_keywords) {
433460
var type = exports.valueType(val);
434-
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
461+
if (type === exports.TYPES.EMPTY) {
435462
return val;
436463
}
437464
if (type !== exports.TYPES.KEYWORD) {
@@ -520,7 +547,7 @@ var getParts = function(str) {
520547
exports.shorthandParser = function parse(v, shorthand_for) {
521548
var obj = {};
522549
var type = exports.valueType(v);
523-
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
550+
if (type === exports.TYPES.EMPTY) {
524551
Object.keys(shorthand_for).forEach(function(property) {
525552
obj[property] = '';
526553
});

0 commit comments

Comments
 (0)