From 8bea1dec18da3f3a02751dc226d51b9d0546b49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski-Owczarek?= Date: Mon, 3 Oct 2022 18:10:42 +0200 Subject: [PATCH] CSS: Return `undefined` for whitespace-only CSS variable values (#5120) The spec requires that CSS variable values are trimmed. In browsers that do this - mainly, Safari, but also Firefox if the value only has leading whitespace - we currently return undefined; in other browsers, we return an empty string as the logic to fall back to undefined happens before trimming. This commit adds another explicit callback to `undefined` to have it consistent across browsers. Also, more explicit comments about behaviors we need to work around in various browsers have been added. Closes gh-5120 Ref gh-5106 (cherry picked from commit 7eb0019640a5856c42b451551eb7f995d913eba9) --- src/css/curCSS.js | 28 ++++++++++++++++++++++++---- test/unit/css.js | 4 +++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/css/curCSS.js b/src/css/curCSS.js index eed7f633e..cde40b6d4 100644 --- a/src/css/curCSS.js +++ b/src/css/curCSS.js @@ -28,17 +28,37 @@ function curCSS( elem, name, computed ) { // .css('filter') (IE 9 only, trac-12537) // .css('--customProperty) (gh-3144) if ( computed ) { + + // Support: IE <=9 - 11+ + // IE only supports `"float"` in `getPropertyValue`; in computed styles + // it's only available as `"cssFloat"`. We no longer modify properties + // sent to `.css()` apart from camelCasing, so we need to check both. + // Normally, this would create difference in behavior: if + // `getPropertyValue` returns an empty string, the value returned + // by `.css()` would be `undefined`. This is usually the case for + // disconnected elements. However, in IE even disconnected elements + // with no styles return `"none"` for `getPropertyValue( "float" )` ret = computed.getPropertyValue( name ) || computed[ name ]; - // trim whitespace for custom property (issue gh-4926) - if ( isCustomProp && ret !== undefined ) { + if ( isCustomProp && ret ) { - // rtrim treats U+000D CARRIAGE RETURN and U+000C FORM FEED + // Support: Firefox 105+, Chrome <=105+ + // Spec requires trimming whitespace for custom properties (gh-4926). + // Firefox only trims leading whitespace. Chrome just collapses + // both leading & trailing whitespace to a single space. + // + // Fall back to `undefined` if empty string returned. + // This collapses a missing definition with property defined + // and set to an empty string but there's no standard API + // allowing us to differentiate them without a performance penalty + // and returning `undefined` aligns with older jQuery. + // + // rtrimCSS treats U+000D CARRIAGE RETURN and U+000C FORM FEED // as whitespace while CSS does not, but this is not a problem // because CSS preprocessing replaces them with U+000A LINE FEED // (which *is* CSS whitespace) // https://www.w3.org/TR/css-syntax-3/#input-preprocessing - ret = ret.replace( rtrimCSS, "$1" ); + ret = ret.replace( rtrimCSS, "$1" ) || undefined; } if ( ret === "" && !isAttached( elem ) ) { diff --git a/test/unit/css.js b/test/unit/css.js index fc7d4c3d4..801dfe81b 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -1760,6 +1760,7 @@ QUnit.test( "Do not throw on frame elements from css method (trac-15098)", funct " --prop10:\f\r\n\t val10 \f\r\n\t;\n" + " --prop11:\u000C\u000D\u000A\u0009\u0020val11\u0020\u0009\u000A\u000D\u000C;\n" + " --prop12:\u000Bval12\u000B;\n" + + " --space: ;\n" + " --empty:;\n" + " }\n" + "" @@ -1769,7 +1770,7 @@ QUnit.test( "Do not throw on frame elements from css method (trac-15098)", funct $elem = jQuery( "
" ).addClass( "test__customProperties" ) .appendTo( "#qunit-fixture" ), webkitOrBlink = /\bsafari\b/i.test( navigator.userAgent ), - expected = 19; + expected = 20; if ( webkitOrBlink ) { expected -= 2; @@ -1815,6 +1816,7 @@ QUnit.test( "Do not throw on frame elements from css method (trac-15098)", funct assert.equal( $elem.css( "--prop10" ), "val10", "Multiple preceding and following escaped unicode whitespace trimmed" ); assert.equal( $elem.css( "--prop11" ), "val11", "Multiple preceding and following unicode whitespace trimmed" ); assert.equal( $elem.css( "--prop12" ), "\u000Bval12\u000B", "Multiple preceding and following non-CSS whitespace reserved" ); + assert.equal( $elem.css( "--space" ), undefined ); assert.equal( $elem.css( "--empty" ), undefined ); assert.equal( $elem.css( "--nonexistent" ), undefined ); } );