From 42ea7468250f1b612684a71b8643ae25367ff469 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Mon, 11 May 2015 23:23:09 +0200 Subject: [PATCH] CSS: Don't cache unrecognized CSS property names This prevents jQuery from caching a prefixed property name if provided directly by the user, e.g. the following code: elem.css( "msTransform", "translate(5px, 2px)" ); should not prevent one from from later setting the transition directly: elem.css( "transform", "translate(5px, 2px)" ); on a browser not understanding the unprefixed version which is the case for Safari 8 & transform. (cherry-picked from d471842b3e3af83c9a1be06b5d16f75bfa96af8c) Fixes gh-2015 Closes gh-2298 --- src/css.js | 16 +++++----- test/unit/css.js | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 9 deletions(-) diff --git a/src/css.js b/src/css.js index bce0a6ce7..9a25816a4 100644 --- a/src/css.js +++ b/src/css.js @@ -39,32 +39,30 @@ var fontWeight: "400" }, - cssPrefixes = [ "Webkit", "Moz", "ms" ]; + cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style; // BuildExclude curCSS = curCSS.curCSS; // return a css property mapped to a potentially vendor prefixed property -function vendorPropName( style, name ) { +function vendorPropName( name ) { // shortcut for names that are not vendor prefixed - if ( name in style ) { + if ( name in emptyStyle ) { return name; } // check for vendor prefixed names var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), - origName = name, i = cssPrefixes.length; while ( i-- ) { name = cssPrefixes[ i ] + capName; - if ( name in style ) { + if ( name in emptyStyle ) { return name; } } - - return origName; } function setPositiveNumber( elem, value, subtract ) { @@ -209,7 +207,7 @@ jQuery.extend({ style = elem.style; name = jQuery.cssProps[ origName ] || - ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName ); // gets hook for the prefixed version // followed by the unprefixed version @@ -273,7 +271,7 @@ jQuery.extend({ // Make sure that we're working with the right name name = jQuery.cssProps[ origName ] || - ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName ); // gets hook for the prefixed version // followed by the unprefixed version diff --git a/test/unit/css.js b/test/unit/css.js index 5a9849c01..34620d05e 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -1187,4 +1187,84 @@ test( "get upper case alpha opacity in IE8", 1, function() { fixture.removeChild( div ); }); + +( function() { + var vendorPrefixes = [ "Webkit", "Moz", "ms" ]; + + function resetCssPropsFor( name ) { + delete jQuery.cssProps[ name ]; + jQuery.each( vendorPrefixes, function( index, prefix ) { + delete jQuery.cssProps[ prefix + name[ 0 ].toUpperCase() + name.slice( 1 ) ]; + } ); + } + + test( "Don't default to a cached previously used wrong prefixed name (gh-2015)", function() { + // Note: this test needs a property we know is only supported in a prefixed version + // by at least one of our main supported browsers. This may get out of date so let's + // use -(webkit|moz)-appearance as well as those two are not on a standards track. + var appearanceName, transformName, elem, elemStyle, + transformVal = "translate(5px, 2px)", + emptyStyle = document.createElement( "div" ).style; + + if ( "appearance" in emptyStyle ) { + appearanceName = "appearance"; + } else { + jQuery.each( vendorPrefixes, function( index, prefix ) { + var prefixedProp = prefix + "Appearance"; + if ( prefixedProp in emptyStyle ) { + appearanceName = prefixedProp; + } + } ); + } + + if ( "transform" in emptyStyle ) { + transformName = "transform"; + } else { + jQuery.each( vendorPrefixes, function( index, prefix ) { + var prefixedProp = prefix + "Transform"; + if ( prefixedProp in emptyStyle ) { + transformName = prefixedProp; + } + } ); + } + + expect( !!appearanceName + !!transformName + 1 ); + + resetCssPropsFor( "appearance" ); + resetCssPropsFor( "transform" ); + + elem = jQuery( "
" ) + .css( { + msAppearance: "none", + appearance: "none", + + // Only the ms prefix is used to make sure we haven't e.g. set + // webkitTransform ourselves in the test. + msTransform: transformVal, + transform: transformVal + } ); + elemStyle = elem[ 0 ].style; + + if ( appearanceName ) { + equal( elemStyle[ appearanceName ], "none", "setting properly-prefixed appearance" ); + } + if ( transformName ) { + equal( elemStyle[ transformName ], transformVal, "setting properly-prefixed transform" ); + } + equal( elemStyle[ "undefined" ], undefined, "Nothing writes to node.style.undefined" ); + } ); + + test( "Don't detect fake set properties on a node when caching the prefixed version", function() { + expect( 1 ); + + var elem = jQuery( "
" ), + style = elem[ 0 ].style; + style.MozFakeProperty = "old value"; + elem.css( "fakeProperty", "new value" ); + + equal( style.MozFakeProperty, "old value", "Fake prefixed property is not cached" ); + } ); + +} )(); + } -- 2.39.5