From 53cf7244da2a2040333335c36e435b1c12efdff9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Micha=C5=82=20Go=C5=82=C4=99biowski-Owczarek?= Date: Wed, 20 Sep 2023 02:31:35 +0200 Subject: [PATCH] CSS:Selector: Align with 3.x, remove the outer `selector.js` wrapper Bring some changes from `3.x-stable`: * rename `rtrim` to `rtrimCSS` to distinguish from the previous `rtrim` regex used for `jQuery.trim` * backport one `id` selector test that avoids the selector engine path Other changes: * remove the inner function wrapper from `selector.js` by renaming the imported `document.js` value * use `jQuery.error` in `selectorError` * make Selector tests pass in all-modules runs by fixing a sinon mistake in Core tests - Core tests had a spy set up for `jQuery.error` that wasn't cleaned up, influencing Selector tests when all were run together Closes gh-5295 --- src/css/curCSS.js | 6 +++--- src/selector.js | 29 ++++++++++++++++------------- src/selector/rbuggyQSA.js | 2 ++ src/selector/selectorError.js | 4 +++- src/selector/tokenize.js | 4 ++-- src/var/{rtrim.js => rtrimCSS.js} | 2 +- test/unit/core.js | 2 +- test/unit/selector.js | 13 +++++++++++-- 8 files changed, 39 insertions(+), 23 deletions(-) rename src/var/{rtrim.js => rtrimCSS.js} (78%) diff --git a/src/css/curCSS.js b/src/css/curCSS.js index 16104d89..b0f0a755 100644 --- a/src/css/curCSS.js +++ b/src/css/curCSS.js @@ -2,7 +2,7 @@ import { jQuery } from "../core.js"; import { isAttached } from "../core/isAttached.js"; import { getStyles } from "./var/getStyles.js"; import { rcustomProp } from "./var/rcustomProp.js"; -import { rtrim } from "../var/rtrim.js"; +import { rtrimCSS } from "../var/rtrimCSS.js"; export function curCSS( elem, name, computed ) { var ret, @@ -41,12 +41,12 @@ export function curCSS( elem, name, computed ) { // allowing us to differentiate them without a performance penalty // and returning `undefined` aligns with older jQuery. // - // rtrim treats U+000D CARRIAGE RETURN and U+000C FORM FEED + // rtrimCSS treats U+000D CARRIAGE RETURN and U+000C FORM FEED // as whitespace while CSS does not, but this is not a problem // because CSS preprocessing replaces them with U+000A LINE FEED // (which *is* CSS whitespace) // https://www.w3.org/TR/css-syntax-3/#input-preprocessing - ret = ret.replace( rtrim, "$1" ) || undefined; + ret = ret.replace( rtrimCSS, "$1" ) || undefined; } if ( ret === "" && !isAttached( elem ) ) { diff --git a/src/selector.js b/src/selector.js index 92bfcfed..d71e65ad 100644 --- a/src/selector.js +++ b/src/selector.js @@ -1,12 +1,12 @@ import { jQuery } from "./core.js"; import { nodeName } from "./core/nodeName.js"; -import { document } from "./var/document.js"; +import { document as preferredDoc } from "./var/document.js"; import { indexOf } from "./var/indexOf.js"; import { pop } from "./var/pop.js"; import { push } from "./var/push.js"; import { whitespace } from "./var/whitespace.js"; import { rbuggyQSA } from "./selector/rbuggyQSA.js"; -import { rtrim } from "./var/rtrim.js"; +import { rtrimCSS } from "./var/rtrimCSS.js"; import { isIE } from "./var/isIE.js"; import { identifier } from "./selector/var/identifier.js"; import { booleans } from "./selector/var/booleans.js"; @@ -27,10 +27,6 @@ import { toSelector } from "./selector/toSelector.js"; import "./selector/escapeSelector.js"; import "./selector/uniqueSort.js"; -var preferredDoc = document; - -( function() { - var i, outermostContext, @@ -167,7 +163,11 @@ function find( selector, context, results, seed ) { // Outside of IE, if we're not changing the context we can // use :scope instead of an ID. - if ( newContext !== context || isIE ) { + // Support: IE 11+ + // IE sometimes throws a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( newContext != context || isIE ) { // Capture the context ID, setting it first if necessary if ( ( nid = context.getAttribute( "id" ) ) ) { @@ -204,7 +204,7 @@ function find( selector, context, results, seed ) { } // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); + return select( selector.replace( rtrimCSS, "$1" ), context, results, seed ); } /** @@ -632,7 +632,7 @@ jQuery.expr = { // spaces as combinators var input = [], results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); + matcher = compile( selector.replace( rtrimCSS, "$1" ) ); return matcher[ jQuery.expando ] ? markFunction( function( seed, matches, _context, xml ) { @@ -1070,7 +1070,12 @@ function matcherFromTokens( tokens ) { return indexOf.call( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + + // Support: IE 11+ + // IE sometimes throws a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + var ret = ( !leadingRelative && ( xml || context != outermostContext ) ) || ( ( checkContext = context ).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); @@ -1104,7 +1109,7 @@ function matcherFromTokens( tokens ) { // If the preceding token was a descendant combinator, insert an implicit any-element `*` tokens.slice( 0, i - 1 ) .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) - ).replace( rtrim, "$1" ), + ).replace( rtrimCSS, "$1" ), matcher, i < j && matcherFromTokens( tokens.slice( i, j ) ), j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), @@ -1369,6 +1374,4 @@ find.select = select; find.setDocument = setDocument; find.tokenize = tokenize; -} )(); - export { jQuery, jQuery as $ }; diff --git a/src/selector/rbuggyQSA.js b/src/selector/rbuggyQSA.js index 7e4632c7..b056ffe7 100644 --- a/src/selector/rbuggyQSA.js +++ b/src/selector/rbuggyQSA.js @@ -2,6 +2,8 @@ import { isIE } from "../var/isIE.js"; import { whitespace } from "../var/whitespace.js"; import { support } from "./support.js"; +// Build QSA regex. +// Regex strategy adopted from Diego Perini. export var rbuggyQSA = []; if ( isIE ) { diff --git a/src/selector/selectorError.js b/src/selector/selectorError.js index 69770ec6..da2b239f 100644 --- a/src/selector/selectorError.js +++ b/src/selector/selectorError.js @@ -1,3 +1,5 @@ +import { jQuery } from "../core.js"; + export function selectorError( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); + jQuery.error( "Syntax error, unrecognized expression: " + msg ); } diff --git a/src/selector/tokenize.js b/src/selector/tokenize.js index 3c1e06c4..fb25cb20 100644 --- a/src/selector/tokenize.js +++ b/src/selector/tokenize.js @@ -1,7 +1,7 @@ import { jQuery } from "../core.js"; import { rcomma } from "./var/rcomma.js"; import { rleadingCombinator } from "./var/rleadingCombinator.js"; -import { rtrim } from "../var/rtrim.js"; +import { rtrimCSS } from "../var/rtrimCSS.js"; import { createCache } from "./createCache.js"; import { selectorError } from "./selectorError.js"; import { filterMatchExpr } from "./filterMatchExpr.js"; @@ -42,7 +42,7 @@ export function tokenize( selector, parseOnly ) { value: matched, // Cast descendant combinators to space - type: match[ 0 ].replace( rtrim, " " ) + type: match[ 0 ].replace( rtrimCSS, " " ) } ); soFar = soFar.slice( matched.length ); } diff --git a/src/var/rtrim.js b/src/var/rtrimCSS.js similarity index 78% rename from src/var/rtrim.js rename to src/var/rtrimCSS.js index aa20c961..f35b8a1e 100644 --- a/src/var/rtrim.js +++ b/src/var/rtrimCSS.js @@ -1,6 +1,6 @@ import { whitespace } from "./whitespace.js"; -export var rtrim = new RegExp( +export var rtrimCSS = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ); diff --git a/test/unit/core.js b/test/unit/core.js index 666f0e82..aa1f637d 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -1460,7 +1460,7 @@ QUnit.testUnlessIE( "jQuery.parseXML - error reporting", function( assert ) { var errorArg, lineMatch, line, columnMatch, column; - sinon.stub( jQuery, "error" ); + this.sandbox.stub( jQuery, "error" ); jQuery.parseXML( "

Not a <well-formed xml string

" ); errorArg = jQuery.error.firstCall.lastArg.toLowerCase(); diff --git a/test/unit/selector.js b/test/unit/selector.js index cf36b1db..d6f28e25 100644 --- a/test/unit/selector.js +++ b/test/unit/selector.js @@ -226,9 +226,9 @@ QUnit.test( "broken selectors throw", function( assert ) { } ); QUnit.test( "id", function( assert ) { - assert.expect( 34 ); + assert.expect( 35 ); - var fiddle, a; + var fiddle, a, lengthtest; assert.t( "ID Selector", "#body", [ "body" ] ); assert.t( "ID Selector w/ Element", "body#body", [ "body" ] ); @@ -283,6 +283,15 @@ QUnit.test( "id", function( assert ) { assert.t( "ID Selector on Form with an input that has a name of 'id'", "#lengthtest", [ "lengthtest" ] ); + // Run the above test again but with `jQuery.find` directly to avoid the jQuery + // quick path that avoids running the selector engine. + lengthtest = jQuery.find( "#lengthtest" ); + assert.strictEqual( + lengthtest && lengthtest[ 0 ], + document.getElementById( "lengthtest" ), + "ID Selector on Form with an input that has a name of 'id' - no quick path (#lengthtest)" + ); + assert.t( "ID selector with non-existent ancestor", "#asdfasdf #foobar", [] ); // bug trac-986 assert.deepEqual( jQuery( "div#form", document.body ).get(), [], -- 2.11.4.GIT