From 3255feaa623f63ac13f331c658dfb51f6b30c4e8 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Tue, 8 Jun 2021 08:11:49 +0200 Subject: [PATCH] fix: upstream conversion of value to DOM String Fixes https://github.com/jsdom/cssstyle/issues/129 --- lib/CSSStyleDeclaration.js | 24 +++++-- lib/parsers.js | 101 +++++++++------------------ lib/properties/backgroundPosition.js | 2 +- lib/properties/border.js | 2 +- lib/properties/borderColor.js | 3 - lib/properties/borderSpacing.js | 4 +- lib/properties/borderStyle.js | 2 +- lib/properties/borderWidth.js | 3 - lib/properties/clip.js | 5 +- lib/properties/flex.js | 4 +- lib/properties/flexBasis.js | 4 +- lib/properties/fontFamily.js | 2 +- lib/properties/fontSize.js | 6 +- lib/properties/height.js | 4 +- lib/properties/margin.js | 6 -- lib/properties/padding.js | 6 -- lib/properties/width.js | 4 +- 17 files changed, 67 insertions(+), 115 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index fe8dced8..b38e0f25 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -6,7 +6,7 @@ var CSSOM = require('cssom'); var allProperties = require('./allProperties'); var allExtraProperties = require('./allExtraProperties'); -const { camelToDashed, dashedToCamelCase } = require('./parsers'); +const { camelToDashed, dashedToCamelCase, toDOMString } = require('./parsers'); const fs = require('fs'); const path = require('path'); var getBasicPropertyDescriptor = require('./utils/getBasicPropertyDescriptor'); @@ -39,7 +39,7 @@ CSSStyleDeclaration.prototype = { if (!this._values.hasOwnProperty(name)) { return ''; } - return this._values[name].toString(); + return this._values[name]; }, /** @@ -50,10 +50,8 @@ CSSStyleDeclaration.prototype = { * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty */ setProperty: function(name, value, priority) { - if (value === undefined) { - return; - } - if (value === null || value === '') { + value = toDOMString(value); + if (value === '') { this.removeProperty(name); return; } @@ -74,7 +72,7 @@ CSSStyleDeclaration.prototype = { if (value === undefined) { return; } - if (value === null || value === '') { + if (value === '') { this.removeProperty(name); return; } @@ -240,12 +238,24 @@ Object.defineProperties(CSSStyleDeclaration.prototype, { }, }); +/** + * @param {function} setter + * + * This "higher order" setter converts value to a DOM String for all properties. + */ +function createSetter(setter) { + return function setProperty(v) { + return setter.call(this, toDOMString(v)); + }; +} + const implementedProperties = fs .readdirSync(path.resolve(__dirname, 'properties')) .reduce((props, filename) => { const { definition } = require(`./properties/${filename}`); const { name: camelCaseName } = path.parse(filename); const dashedCaseName = camelToDashed(camelCaseName); + definition.set = createSetter(definition.set); Object.defineProperty(CSSStyleDeclaration.prototype, camelCaseName, definition); Object.defineProperty(CSSStyleDeclaration.prototype, dashedCaseName, definition); return props.add(dashedCaseName); diff --git a/lib/parsers.js b/lib/parsers.js index 8ecdf5e3..276b1ba4 100644 --- a/lib/parsers.js +++ b/lib/parsers.js @@ -17,7 +17,6 @@ exports.TYPES = { STRING: 7, ANGLE: 8, KEYWORD: 9, - NULL_OR_EMPTY_STR: 10, CALC: 11, }; @@ -35,19 +34,18 @@ var calcRegEx = /^calc\(([^)]*)\)$/; var colorRegEx4 = /^hsla?\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*(,\s*(-?\d+|-?\d*.\d+)\s*)?\)/; var angleRegEx = /^([-+]?[0-9]*\.?[0-9]+)(deg|grad|rad)$/; -// This will return one of the above types based on the passed in string -exports.valueType = function valueType(val) { - if (val === '' || val === null) { - return exports.TYPES.NULL_OR_EMPTY_STR; - } - if (typeof val === 'number') { - val = val.toString(); +exports.toDOMString = function toDOMString(val) { + if (val === null) { + return ''; } - - if (typeof val !== 'string') { - return undefined; + if (typeof val === 'symbol') { + throw Error('Cannot convert symbol to string'); } + return `${val}`; +}; +// This will return one of the above types based on the passed in string +exports.valueType = function valueType(val) { if (integerRegEx.test(val)) { return exports.TYPES.INTEGER; } @@ -156,21 +154,20 @@ exports.valueType = function valueType(val) { }; exports.parseInteger = function parseInteger(val) { - var type = exports.valueType(val); - if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + if (val === '') { return val; } - if (type !== exports.TYPES.INTEGER) { + if (exports.valueType(val) !== exports.TYPES.INTEGER) { return undefined; } return String(parseInt(val, 10)); }; exports.parseNumber = function parseNumber(val) { - var type = exports.valueType(val); - if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + if (val === '') { return val; } + var type = exports.valueType(val); if (type !== exports.TYPES.NUMBER && type !== exports.TYPES.INTEGER) { return undefined; } @@ -178,28 +175,26 @@ exports.parseNumber = function parseNumber(val) { }; exports.parseLength = function parseLength(val) { - if (val === 0 || val === '0') { - return '0px'; - } - var type = exports.valueType(val); - if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + if (val === '') { return val; } - if (type !== exports.TYPES.LENGTH) { + if (val === '0') { + return '0px'; + } + if (exports.valueType(val) !== exports.TYPES.LENGTH) { return undefined; } return val; }; exports.parsePercent = function parsePercent(val) { - if (val === 0 || val === '0') { - return '0%'; - } - var type = exports.valueType(val); - if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + if (val === '') { return val; } - if (type !== exports.TYPES.PERCENT) { + if (val === '0') { + return '0%'; + } + if (exports.valueType(val) !== exports.TYPES.PERCENT) { return undefined; } return val; @@ -220,8 +215,7 @@ exports.parseMeasurement = function parseMeasurement(val) { }; exports.parseUrl = function parseUrl(val) { - var type = exports.valueType(val); - if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + if (val === '') { return val; } var res = urlRegEx.exec(val); @@ -259,13 +253,9 @@ exports.parseUrl = function parseUrl(val) { }; exports.parseString = function parseString(val) { - var type = exports.valueType(val); - if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + if (val === '') { return val; } - if (type !== exports.TYPES.STRING) { - return undefined; - } var i; for (i = 1; i < val.length - 1; i++) { switch (val[i]) { @@ -286,8 +276,7 @@ exports.parseString = function parseString(val) { }; exports.parseColor = function parseColor(val) { - var type = exports.valueType(val); - if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + if (val === '') { return val; } var red, @@ -398,18 +387,17 @@ exports.parseColor = function parseColor(val) { return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')'; } - if (type === exports.TYPES.COLOR) { + if (exports.valueType(val) === exports.TYPES.COLOR) { return val; } return undefined; }; exports.parseAngle = function parseAngle(val) { - var type = exports.valueType(val); - if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + if (val === '') { return val; } - if (type !== exports.TYPES.ANGLE) { + if (exports.valueType(val) !== exports.TYPES.ANGLE) { return undefined; } var res = angleRegEx.exec(val); @@ -430,14 +418,13 @@ exports.parseAngle = function parseAngle(val) { }; exports.parseKeyword = function parseKeyword(val, valid_keywords) { - var type = exports.valueType(val); - if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + if (val === '') { return val; } - if (type !== exports.TYPES.KEYWORD) { + if (exports.valueType(val) !== exports.TYPES.KEYWORD) { return undefined; } - val = val.toString().toLowerCase(); + val = val.toLowerCase(); var i; for (i = 0; i < valid_keywords.length; i++) { if (valid_keywords[i].toLowerCase() === val) { @@ -519,22 +506,12 @@ var getParts = function(str) { */ exports.shorthandParser = function parse(v, shorthand_for) { var obj = {}; - var type = exports.valueType(v); - if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + if (v === '') { Object.keys(shorthand_for).forEach(function(property) { obj[property] = ''; }); return obj; } - - if (typeof v === 'number') { - v = v.toString(); - } - - if (typeof v !== 'string') { - return undefined; - } - if (v.toLowerCase() === 'inherit') { return {}; } @@ -623,12 +600,6 @@ exports.implicitSetter = function(property_before, property_after, isValid, pars var part_names = ['top', 'right', 'bottom', 'left']; return function(v) { - if (typeof v === 'number') { - v = v.toString(); - } - if (typeof v !== 'string') { - return undefined; - } var parts; if (v.toLowerCase() === 'inherit' || v === '') { parts = [v]; @@ -679,12 +650,6 @@ exports.subImplicitSetter = function(prefix, part, isValid, parser) { var subparts = [prefix + '-top', prefix + '-right', prefix + '-bottom', prefix + '-left']; return function(v) { - if (typeof v === 'number') { - v = v.toString(); - } - if (typeof v !== 'string') { - return undefined; - } if (!isValid(v)) { return undefined; } diff --git a/lib/properties/backgroundPosition.js b/lib/properties/backgroundPosition.js index 4405fe6f..9ae73e36 100644 --- a/lib/properties/backgroundPosition.js +++ b/lib/properties/backgroundPosition.js @@ -5,7 +5,7 @@ var parsers = require('../parsers'); var valid_keywords = ['top', 'center', 'bottom', 'left', 'right']; var parse = function parse(v) { - if (v === '' || v === null) { + if (v === '') { return undefined; } var parts = v.split(/\s+/); diff --git a/lib/properties/border.js b/lib/properties/border.js index bf5b5d64..f27c89bd 100644 --- a/lib/properties/border.js +++ b/lib/properties/border.js @@ -14,7 +14,7 @@ var myShorthandGetter = shorthandGetter('border', shorthand_for); module.exports.definition = { set: function(v) { - if (v.toString().toLowerCase() === 'none') { + if (v.toLowerCase() === 'none') { v = ''; } myShorthandSetter.call(this, v); diff --git a/lib/properties/borderColor.js b/lib/properties/borderColor.js index 6605e071..676df257 100644 --- a/lib/properties/borderColor.js +++ b/lib/properties/borderColor.js @@ -4,9 +4,6 @@ var parsers = require('../parsers'); var implicitSetter = require('../parsers').implicitSetter; module.exports.isValid = function parse(v) { - if (typeof v !== 'string') { - return false; - } return ( v === '' || v.toLowerCase() === 'transparent' || parsers.valueType(v) === parsers.TYPES.COLOR ); diff --git a/lib/properties/borderSpacing.js b/lib/properties/borderSpacing.js index ff1ce882..ac6c3d64 100644 --- a/lib/properties/borderSpacing.js +++ b/lib/properties/borderSpacing.js @@ -7,10 +7,10 @@ var parsers = require('../parsers'); // if two, the first applies to the horizontal and the second applies to vertical spacing var parse = function parse(v) { - if (v === '' || v === null) { + if (v === '') { return undefined; } - if (v === 0) { + if (v === '0') { return '0px'; } if (v.toLowerCase() === 'inherit') { diff --git a/lib/properties/borderStyle.js b/lib/properties/borderStyle.js index 6e3e674f..135e46bf 100644 --- a/lib/properties/borderStyle.js +++ b/lib/properties/borderStyle.js @@ -17,7 +17,7 @@ var styles = [ ]; module.exports.isValid = function parse(v) { - return typeof v === 'string' && (v === '' || styles.indexOf(v) !== -1); + return v === '' || styles.indexOf(v) !== -1; }; var isValid = module.exports.isValid; diff --git a/lib/properties/borderWidth.js b/lib/properties/borderWidth.js index 2b6d8718..88b2aa82 100644 --- a/lib/properties/borderWidth.js +++ b/lib/properties/borderWidth.js @@ -11,9 +11,6 @@ module.exports.isValid = function parse(v) { if (length !== undefined) { return true; } - if (typeof v !== 'string') { - return false; - } if (v === '') { return true; } diff --git a/lib/properties/clip.js b/lib/properties/clip.js index 91ba675e..0f90c33c 100644 --- a/lib/properties/clip.js +++ b/lib/properties/clip.js @@ -5,12 +5,9 @@ var parseMeasurement = require('../parsers').parseMeasurement; var shape_regex = /^rect\((.*)\)$/i; var parse = function(val) { - if (val === '' || val === null) { + if (val === '') { return val; } - if (typeof val !== 'string') { - return undefined; - } val = val.toLowerCase(); if (val === 'auto' || val === 'inherit') { return val; diff --git a/lib/properties/flex.js b/lib/properties/flex.js index b56fd55d..e40d8bdc 100644 --- a/lib/properties/flex.js +++ b/lib/properties/flex.js @@ -18,9 +18,7 @@ module.exports.isValid = function isValid(v) { module.exports.definition = { set: function(v) { - var normalizedValue = String(v) - .trim() - .toLowerCase(); + var normalizedValue = v.trim().toLowerCase(); if (normalizedValue === 'none') { myShorthandSetter.call(this, '0 0 auto'); diff --git a/lib/properties/flexBasis.js b/lib/properties/flexBasis.js index 0c7cddf0..0dec04bc 100644 --- a/lib/properties/flexBasis.js +++ b/lib/properties/flexBasis.js @@ -3,10 +3,10 @@ var parseMeasurement = require('../parsers').parseMeasurement; function parse(v) { - if (String(v).toLowerCase() === 'auto') { + if (v.toLowerCase() === 'auto') { return 'auto'; } - if (String(v).toLowerCase() === 'inherit') { + if (v.toLowerCase() === 'inherit') { return 'inherit'; } return parseMeasurement(v); diff --git a/lib/properties/fontFamily.js b/lib/properties/fontFamily.js index 40bd1c13..b1156c0c 100644 --- a/lib/properties/fontFamily.js +++ b/lib/properties/fontFamily.js @@ -5,7 +5,7 @@ var valueType = require('../parsers').valueType; var partsRegEx = /\s*,\s*/; module.exports.isValid = function isValid(v) { - if (v === '' || v === null) { + if (v === '') { return true; } var parts = v.split(partsRegEx); diff --git a/lib/properties/fontSize.js b/lib/properties/fontSize.js index c956324b..91a0a2f2 100644 --- a/lib/properties/fontSize.js +++ b/lib/properties/fontSize.js @@ -18,12 +18,12 @@ module.exports.isValid = function(v) { }; function parse(v) { - const valueAsString = String(v).toLowerCase(); + const lowerCaseValue = v.toLowerCase(); const optionalArguments = absoluteSizes.concat(relativeSizes); const isOptionalArgument = optionalArguments.some( - stringValue => stringValue.toLowerCase() === valueAsString + stringValue => stringValue.toLowerCase() === lowerCaseValue ); - return isOptionalArgument ? valueAsString : parseMeasurement(v); + return isOptionalArgument ? lowerCaseValue : parseMeasurement(v); } module.exports.definition = { diff --git a/lib/properties/height.js b/lib/properties/height.js index 82543c05..d3f3ffcf 100644 --- a/lib/properties/height.js +++ b/lib/properties/height.js @@ -3,10 +3,10 @@ var parseMeasurement = require('../parsers').parseMeasurement; function parse(v) { - if (String(v).toLowerCase() === 'auto') { + if (v.toLowerCase() === 'auto') { return 'auto'; } - if (String(v).toLowerCase() === 'inherit') { + if (v.toLowerCase() === 'inherit') { return 'inherit'; } return parseMeasurement(v); diff --git a/lib/properties/margin.js b/lib/properties/margin.js index 2a8f972b..5e48c3b6 100644 --- a/lib/properties/margin.js +++ b/lib/properties/margin.js @@ -37,12 +37,6 @@ var myGlobal = parsers.implicitSetter( module.exports.definition = { set: function(v) { - if (typeof v === 'number') { - v = String(v); - } - if (typeof v !== 'string') { - return; - } var V = v.toLowerCase(); switch (V) { case 'inherit': diff --git a/lib/properties/padding.js b/lib/properties/padding.js index 1287b19e..43754282 100644 --- a/lib/properties/padding.js +++ b/lib/properties/padding.js @@ -30,12 +30,6 @@ var myGlobal = parsers.implicitSetter( module.exports.definition = { set: function(v) { - if (typeof v === 'number') { - v = String(v); - } - if (typeof v !== 'string') { - return; - } var V = v.toLowerCase(); switch (V) { case 'inherit': diff --git a/lib/properties/width.js b/lib/properties/width.js index a8c365da..e15c8230 100644 --- a/lib/properties/width.js +++ b/lib/properties/width.js @@ -3,10 +3,10 @@ var parseMeasurement = require('../parsers').parseMeasurement; function parse(v) { - if (String(v).toLowerCase() === 'auto') { + if (v.toLowerCase() === 'auto') { return 'auto'; } - if (String(v).toLowerCase() === 'inherit') { + if (v.toLowerCase() === 'inherit') { return 'inherit'; } return parseMeasurement(v);