From 20cdf4e7de60f515a7acf6c70228c52668301d9b Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Tue, 18 Jul 2017 15:40:41 -0400 Subject: [PATCH] Support: Properly check for IE9 absolute scrollbox mishandling Ref gh-3589 Fixes gh-3699 Fixes gh-3730 Closes gh-3729 --- src/css.js | 2 +- src/css/support.js | 44 +++++++++++----------- test/unit/dimensions.js | 82 ++++++++++++++++++++++++----------------- test/unit/support.js | 30 ++++++++++----- 4 files changed, 91 insertions(+), 67 deletions(-) diff --git a/src/css.js b/src/css.js index 79e06319c..87bf2481d 100644 --- a/src/css.js +++ b/src/css.js @@ -378,7 +378,7 @@ jQuery.each( [ "height", "width" ], function( i, dimension ) { // Account for unreliable border-box dimensions by comparing offset* to computed and // faking a content-box to get border and padding (gh-3699) - if ( isBorderBox && !support.borderBoxReliable() ) { + if ( isBorderBox && support.scrollboxSize() === styles.position ) { subtract -= Math.ceil( elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - parseFloat( styles[ dimension ] ) - diff --git a/src/css/support.js b/src/css/support.js index 7b7945fef..97f5aad70 100644 --- a/src/css/support.js +++ b/src/css/support.js @@ -18,29 +18,33 @@ define( [ return; } + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; div.style.cssText = - "box-sizing:border-box;" + - "position:relative;display:block;" + + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + "margin:auto;border:1px;padding:1px;" + - "top:1%;width:50%"; - div.innerHTML = ""; - documentElement.appendChild( container ); + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); var divStyle = window.getComputedStyle( div ); pixelPositionVal = divStyle.top !== "1%"; // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 - reliableMarginLeftVal = divStyle.marginLeft === "2px"; - boxSizingReliableVal = divStyle.width === "5px"; - - // Support: IE 9 only - // Detect misreporting of content dimensions for border-box elements (gh-3699) - borderBoxReliableVal = divStyle.width[ 0 ] === "5"; + reliableMarginLeftVal = divStyle.marginLeft === "12px"; // Support: Android 4.0 - 4.3 only // Some styles come back with percentage values, even though they shouldn't - div.style.marginRight = "50%"; - pixelMarginRightVal = divStyle.marginRight === "5px"; + div.style.marginRight = "60%"; + pixelMarginRightVal = divStyle.marginRight === "36px"; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = divStyle.width === "36px"; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + div.style.position = "absolute"; + scrollboxSizeVal = div.offsetWidth === 36 || "absolute"; documentElement.removeChild( container ); @@ -49,7 +53,7 @@ define( [ div = null; } - var pixelPositionVal, boxSizingReliableVal, borderBoxReliableVal, pixelMarginRightVal, + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelMarginRightVal, reliableMarginLeftVal, container = document.createElement( "div" ), div = document.createElement( "div" ); @@ -65,15 +69,7 @@ define( [ div.cloneNode( true ).style.backgroundClip = ""; support.clearCloneStyle = div.style.backgroundClip === "content-box"; - container.style.cssText = "border:0;width:10px;height:0;top:0;left:-9999px;" + - "padding:0;margin-top:1px;position:absolute"; - container.appendChild( div ); - jQuery.extend( support, { - borderBoxReliable: function() { - computeStyleTests(); - return borderBoxReliableVal; - }, boxSizingReliable: function() { computeStyleTests(); return boxSizingReliableVal; @@ -89,6 +85,10 @@ define( [ reliableMarginLeft: function() { computeStyleTests(); return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; } } ); } )(); diff --git a/test/unit/dimensions.js b/test/unit/dimensions.js index 5db465868..13cd9662b 100644 --- a/test/unit/dimensions.js +++ b/test/unit/dimensions.js @@ -545,7 +545,7 @@ QUnit.test( "width/height on an inline element with no explicitly-set dimensions } ); QUnit.test( "interaction with scrollbars (gh-3589)", function( assert ) { - assert.expect( 36 ); + assert.expect( 48 ); var i, suffix = "", @@ -561,31 +561,29 @@ QUnit.test( "interaction with scrollbars (gh-3589)", function( assert ) { borderWidth = 1, padding = 2, size = 100 + fraction, - scrollBox = { - position: "absolute", - overflow: "scroll", - width: size + "px", - height: size + "px" - }, - borderBox = { - border: borderWidth + "px solid blue", - padding: padding + "px" - }, - plainContentBox = jQuery( "
" ) - .css( scrollBox ) - .css( { "box-sizing": "content-box" } ) - .appendTo( parent ), - contentBox = jQuery( "
" ) - .css( scrollBox ) - .css( borderBox ) - .css( { "box-sizing": "content-box" } ) - .appendTo( parent ), - borderBox = jQuery( "
" ) - .css( scrollBox ) - .css( borderBox ) - .css( { "box-sizing": "border-box" } ) - .appendTo( parent ), - $boxes = jQuery( [ plainContentBox[ 0 ], contentBox[ 0 ], borderBox[ 0 ] ] ), + plainBox = jQuery( "
" ) + .css( { + "box-sizing": "content-box", + position: "absolute", + overflow: "scroll", + width: size + "px", + height: size + "px" + } ), + contentBox = plainBox + .clone() + .css( { + border: borderWidth + "px solid blue", + padding: padding + "px" + } ), + borderBox = contentBox + .clone() + .css( { "box-sizing": "border-box" } ), + relativeBorderBox = borderBox + .clone() + .css( { position: "relative" } ), + $boxes = jQuery( + [ plainBox[ 0 ], contentBox[ 0 ], borderBox[ 0 ], relativeBorderBox[ 0 ] ] + ).appendTo( parent ), // Support: IE 9 only // Computed width seems to report content width even with "box-sizing: border-box", and @@ -594,6 +592,13 @@ QUnit.test( "interaction with scrollbars (gh-3589)", function( assert ) { borderBox.clone().css( { overflow: "auto" } ).appendTo( parent )[ 0 ].offsetWidth - borderBox[ 0 ].offsetWidth; + if ( borderBoxLoss > 0 ) { + borderBox.css( { + width: ( size + borderBoxLoss ) + "px", + height: ( size + borderBoxLoss ) + "px" + } ); + } + for ( i = 0; i < 3; i++ ) { if ( i === 1 ) { suffix = " after increasing inner* by " + i; @@ -605,13 +610,13 @@ QUnit.test( "interaction with scrollbars (gh-3589)", function( assert ) { $boxes.outerWidth( updater( i ) ).outerHeight( updater( i ) ); } - assert.equal( plainContentBox.innerWidth(), size, + assert.equal( plainBox.innerWidth(), size, "plain content-box innerWidth includes scroll gutter" + suffix ); - assert.equal( plainContentBox.innerHeight(), size, + assert.equal( plainBox.innerHeight(), size, "plain content-box innerHeight includes scroll gutter" + suffix ); - assert.equal( plainContentBox.outerWidth(), size, + assert.equal( plainBox.outerWidth(), size, "plain content-box outerWidth includes scroll gutter" + suffix ); - assert.equal( plainContentBox.outerHeight(), size, + assert.equal( plainBox.outerHeight(), size, "plain content-box outerHeight includes scroll gutter" + suffix ); assert.equal( contentBox.innerWidth(), size + 2 * padding, @@ -623,14 +628,23 @@ QUnit.test( "interaction with scrollbars (gh-3589)", function( assert ) { assert.equal( contentBox.outerHeight(), size + 2 * padding + 2 * borderWidth, "content-box outerHeight includes scroll gutter" + suffix ); - assert.equal( borderBox.innerWidth(), size - borderBoxLoss - 2 * borderWidth, + assert.equal( borderBox.innerWidth(), size - 2 * borderWidth, "border-box innerWidth includes scroll gutter" + suffix ); - assert.equal( borderBox.innerHeight(), size - borderBoxLoss - 2 * borderWidth, + assert.equal( borderBox.innerHeight(), size - 2 * borderWidth, "border-box innerHeight includes scroll gutter" + suffix ); - assert.equal( borderBox.outerWidth(), size - borderBoxLoss, + assert.equal( borderBox.outerWidth(), size, "border-box outerWidth includes scroll gutter" + suffix ); - assert.equal( borderBox.outerHeight(), size - borderBoxLoss, + assert.equal( borderBox.outerHeight(), size, "border-box outerHeight includes scroll gutter" + suffix ); + + assert.equal( relativeBorderBox.innerWidth(), size - 2 * borderWidth, + "relative border-box innerWidth includes scroll gutter" + suffix ); + assert.equal( relativeBorderBox.innerHeight(), size - 2 * borderWidth, + "relative border-box innerHeight includes scroll gutter" + suffix ); + assert.equal( relativeBorderBox.outerWidth(), size, + "relative border-box outerWidth includes scroll gutter" + suffix ); + assert.equal( relativeBorderBox.outerHeight(), size, + "relative border-box outerHeight includes scroll gutter" + suffix ); } } ); diff --git a/test/unit/support.js b/test/unit/support.js index daa8dc664..f893886da 100644 --- a/test/unit/support.js +++ b/test/unit/support.js @@ -73,7 +73,8 @@ testIframe( "pixelMarginRight": true, "pixelPosition": true, "radioValue": true, - "reliableMarginLeft": true + "reliableMarginLeft": true, + "scrollboxSize": true }; } else if ( /(msie 10\.0|trident\/7\.0)/i.test( userAgent ) ) { expected = { @@ -90,7 +91,8 @@ testIframe( "pixelMarginRight": true, "pixelPosition": true, "radioValue": false, - "reliableMarginLeft": true + "reliableMarginLeft": true, + "scrollboxSize": true }; } else if ( /msie 9\.0/i.test( userAgent ) ) { expected = { @@ -107,7 +109,8 @@ testIframe( "pixelMarginRight": true, "pixelPosition": true, "radioValue": false, - "reliableMarginLeft": true + "reliableMarginLeft": true, + "scrollboxSize": "absolute" }; } else if ( /chrome/i.test( userAgent ) ) { @@ -127,7 +130,8 @@ testIframe( "pixelMarginRight": true, "pixelPosition": true, "radioValue": true, - "reliableMarginLeft": true + "reliableMarginLeft": true, + "scrollboxSize": true }; } else if ( /\b(?:9|10)\.\d(\.\d+)* safari/i.test( userAgent ) ) { expected = { @@ -144,7 +148,8 @@ testIframe( "pixelMarginRight": true, "pixelPosition": false, "radioValue": true, - "reliableMarginLeft": true + "reliableMarginLeft": true, + "scrollboxSize": true }; } else if ( /firefox/i.test( userAgent ) ) { expected = { @@ -161,7 +166,8 @@ testIframe( "pixelMarginRight": true, "pixelPosition": true, "radioValue": true, - "reliableMarginLeft": false + "reliableMarginLeft": false, + "scrollboxSize": true }; } else if ( /iphone os (?:9|10)_/i.test( userAgent ) ) { expected = { @@ -178,7 +184,8 @@ testIframe( "pixelMarginRight": true, "pixelPosition": false, "radioValue": true, - "reliableMarginLeft": true + "reliableMarginLeft": true, + "scrollboxSize": true }; } else if ( /iphone os 8_/i.test( userAgent ) ) { expected = { @@ -195,7 +202,8 @@ testIframe( "pixelMarginRight": true, "pixelPosition": false, "radioValue": true, - "reliableMarginLeft": true + "reliableMarginLeft": true, + "scrollboxSize": true }; } else if ( /iphone os 7_/i.test( userAgent ) ) { expected = { @@ -212,7 +220,8 @@ testIframe( "pixelMarginRight": true, "pixelPosition": false, "radioValue": true, - "reliableMarginLeft": true + "reliableMarginLeft": true, + "scrollboxSize": true }; } else if ( /android 4\.[0-3]/i.test( userAgent ) ) { expected = { @@ -229,7 +238,8 @@ testIframe( "pixelMarginRight": false, "pixelPosition": false, "radioValue": true, - "reliableMarginLeft": false + "reliableMarginLeft": false, + "scrollboxSize": true }; }