From 692f9d4db30c9c6c4f6bc76005cf153586202fa6 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Mon, 28 Aug 2017 13:23:04 -0400 Subject: [PATCH] CSS: Avoid unit-conversion interference from CSS upper bounds Fixes gh-2144 Closes gh-3745 --- src/css/adjustCSS.js | 36 +++++++++++++++++++----------------- test/unit/css.js | 16 ++++++++++++++++ test/unit/effects.js | 3 ++- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/css/adjustCSS.js b/src/css/adjustCSS.js index ded3a1666..626ec7475 100644 --- a/src/css/adjustCSS.js +++ b/src/css/adjustCSS.js @@ -6,8 +6,7 @@ define( [ "use strict"; function adjustCSS( elem, prop, valueParts, tween ) { - var adjusted, - scale = 1, + var adjusted, scale, maxIterations = 20, currentValue = tween ? function() { @@ -25,30 +24,33 @@ function adjustCSS( elem, prop, valueParts, tween ) { 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 unit = unit || initialInUnit[ 3 ]; - // Make sure we update the tween properties later on - valueParts = valueParts || []; - // Iteratively approximate from a nonzero starting point initialInUnit = +initial || 1; - do { - - // If previous iteration zeroed out, double until we get *something*. - // Use string for doubling so we don't accidentally see scale as unchanged below - scale = scale || ".5"; + while ( maxIterations-- ) { - // Adjust and apply - initialInUnit = initialInUnit / scale; + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). 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 ( - scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations - ); + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; } if ( valueParts ) { diff --git a/test/unit/css.js b/test/unit/css.js index 4533e4692..79cce0549 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -271,6 +271,22 @@ QUnit.test( "css() non-px relative values (gh-1711)", function( assert ) { add( "lineHeight", 50, "%" ); } ); +QUnit.test( "css() mismatched relative values with bounded styles (gh-2144)", function( assert ) { + assert.expect( 1 ); + + var right, + $container = jQuery( "
" ) + .css( { position: "absolute", width: "400px", fontSize: "4px" } ) + .appendTo( "#qunit-fixture" ), + $el = jQuery( "
" ) + .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 ) { assert.expect( 19 ); var j, div, display, ret, success; diff --git a/test/unit/effects.js b/test/unit/effects.js index 54c7f7995..ec1669f54 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -1807,7 +1807,8 @@ QUnit.test( "animate does not change start value for non-px animation (#7109)", } } ).queue( function( next ) { 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(); parent.remove(); } ); -- 2.39.5