Skip to content

Commit

Permalink
CSS: Don't cache unrecognized CSS property names
Browse files Browse the repository at this point in the history
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.

Fixes gh-2015
Closes gh-2298
  • Loading branch information
mgol committed Jun 1, 2015
1 parent 6df669f commit d471842
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 11 deletions.
21 changes: 10 additions & 11 deletions src/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ define([
"./var/pnum",
"./core/access",
"./css/var/rmargin",
"./var/document",
"./var/rcssNum",
"./css/var/rnumnonpx",
"./css/var/cssExpand",
Expand All @@ -18,8 +19,8 @@ define([
"./core/init",
"./core/ready",
"./selector" // contains
], function( jQuery, pnum, access, rmargin, rcssNum, rnumnonpx, cssExpand, isHidden,
getStyles, swap, curCSS, adjustCSS, addGetHookIf, support, showHide ) {
], function( jQuery, pnum, access, rmargin, document, rcssNum, rnumnonpx, cssExpand,
isHidden, getStyles, swap, curCSS, adjustCSS, addGetHookIf, support, showHide ) {

var
// Swappable if display is none or starts with table
Expand All @@ -34,29 +35,27 @@ var
fontWeight: "400"
},

cssPrefixes = [ "Webkit", "Moz", "ms" ];
cssPrefixes = [ "Webkit", "Moz", "ms" ],
emptyStyle = document.createElement( "div" ).style;

// 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 ) {
Expand Down Expand Up @@ -203,7 +202,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, then unprefixed version
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
Expand Down Expand Up @@ -261,7 +260,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 );

// Try prefixed name followed by the unprefixed name
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
Expand Down
80 changes: 80 additions & 0 deletions test/unit/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -1116,4 +1116,84 @@ test( "Do not throw on frame elements from css method (#15098)", 1, function() {
ok( false, "It did throw" );
}
});

( 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( "<div/>" )
.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( "<div/>" ),
style = elem[ 0 ].style;
style.MozFakeProperty = "old value";
elem.css( "fakeProperty", "new value" );

equal( style.MozFakeProperty, "old value", "Fake prefixed property is not cached" );
} );

} )();

}

0 comments on commit d471842

Please sign in to comment.