CSS: Avoid unit-conversion interference from CSS upper bounds

Fixes gh-2144
Closes gh-3745
This commit is contained in:
Richard Gibson 2017-08-28 13:23:04 -04:00 committed by GitHub
parent b1b949d35e
commit 692f9d4db3
3 changed files with 37 additions and 18 deletions

View File

@ -6,8 +6,7 @@ define( [
"use strict"; "use strict";
function adjustCSS( elem, prop, valueParts, tween ) { function adjustCSS( elem, prop, valueParts, tween ) {
var adjusted, var adjusted, scale,
scale = 1,
maxIterations = 20, maxIterations = 20,
currentValue = tween ? currentValue = tween ?
function() { function() {
@ -25,30 +24,33 @@ function adjustCSS( elem, prop, valueParts, tween ) {
if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
// Support: Firefox <=54
// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
initial = initial / 2;
// Trust units reported by jQuery.css // Trust units reported by jQuery.css
unit = unit || initialInUnit[ 3 ]; unit = unit || initialInUnit[ 3 ];
// Make sure we update the tween properties later on
valueParts = valueParts || [];
// Iteratively approximate from a nonzero starting point // Iteratively approximate from a nonzero starting point
initialInUnit = +initial || 1; initialInUnit = +initial || 1;
do { while ( maxIterations-- ) {
// If previous iteration zeroed out, double until we get *something*. // Evaluate and update our best guess (doubling guesses that zero out).
// Use string for doubling so we don't accidentally see scale as unchanged below // Finish if the scale equals or crosses 1 (making the old*new product non-positive).
scale = scale || ".5";
// Adjust and apply
initialInUnit = initialInUnit / scale;
jQuery.style( elem, prop, initialInUnit + unit ); jQuery.style( elem, prop, initialInUnit + unit );
if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
maxIterations = 0;
}
initialInUnit = initialInUnit / scale;
// Update scale, tolerating zero or NaN from tween.cur() }
// Break the loop if scale is unchanged or perfect, or if we've just had enough.
} while ( initialInUnit = initialInUnit * 2;
scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations jQuery.style( elem, prop, initialInUnit + unit );
);
// Make sure we update the tween properties later on
valueParts = valueParts || [];
} }
if ( valueParts ) { if ( valueParts ) {

View File

@ -271,6 +271,22 @@ QUnit.test( "css() non-px relative values (gh-1711)", function( assert ) {
add( "lineHeight", 50, "%" ); add( "lineHeight", 50, "%" );
} ); } );
QUnit.test( "css() mismatched relative values with bounded styles (gh-2144)", function( assert ) {
assert.expect( 1 );
var right,
$container = jQuery( "<div/>" )
.css( { position: "absolute", width: "400px", fontSize: "4px" } )
.appendTo( "#qunit-fixture" ),
$el = jQuery( "<div/>" )
.css( { position: "absolute", left: "50%", right: "50%" } )
.appendTo( $container );
$el.css( "right", "-=25em" );
assert.equal( Math.round( parseFloat( $el.css( "right" ) ) ), 100,
"Constraints do not interfere with unit conversion" );
} );
QUnit.test( "css(String, Object)", function( assert ) { QUnit.test( "css(String, Object)", function( assert ) {
assert.expect( 19 ); assert.expect( 19 );
var j, div, display, ret, success; var j, div, display, ret, success;

View File

@ -1807,7 +1807,8 @@ QUnit.test( "animate does not change start value for non-px animation (#7109)",
} }
} ).queue( function( next ) { } ).queue( function( next ) {
var ratio = computed[ 0 ] / actual; var ratio = computed[ 0 ] / actual;
assert.ok( ratio > 0.9 && ratio < 1.1, "Starting width was close enough" ); assert.ok( ratio > 0.9 && ratio < 1.1,
"Starting width was close enough (" + computed[ 0 ] + " approximates " + actual + ")" );
next(); next();
parent.remove(); parent.remove();
} ); } );