Skip to content

Commit ebb312e

Browse files
committed
Selector: Allow whitespace after hex escapes in CSS identifiers
Ref jquery#4424 Ref jquery/sizzle#450
1 parent 577015e commit ebb312e

File tree

2 files changed

+33
-20
lines changed

2 files changed

+33
-20
lines changed

src/selector.js

+15-12
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ var i,
5656
// http://www.w3.org/TR/css3-selectors/#whitespace
5757
whitespace = "[\\x20\\t\\r\\n\\f]",
5858

59-
// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
60-
identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+",
59+
// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
60+
identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
61+
"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",
6162

6263
// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
6364
attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
@@ -122,21 +123,23 @@ var i,
122123

123124
// CSS escapes
124125
// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
125-
runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,
126-
funescape = function( _, escaped ) {
127-
var high = "0x" + escaped - 0x10000;
126+
runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace +
127+
"?|\\\\([^\\r\\n\\f])", "g" ),
128+
funescape = function( escape, nonHex ) {
129+
var high = "0x" + escape.slice( 1 ) - 0x10000;
128130

129-
// NaN means non-codepoint
130-
if ( high !== high ) {
131-
return escaped;
131+
if ( nonHex ) {
132+
133+
// Strip the backslash prefix from a non-hex escape sequence
134+
return nonHex;
132135
}
133136

137+
// Replace a hexadecimal escape sequence with the encoded Unicode code point
138+
// Support: IE <=11+
139+
// For values outside the Basic Multilingual Plane (BMP), manually construct a
140+
// surrogate pair
134141
return high < 0 ?
135-
136-
// BMP codepoint
137142
String.fromCharCode( high + 0x10000 ) :
138-
139-
// Supplemental Plane codepoint (surrogate pair)
140143
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
141144
},
142145

test/unit/selector.js

+18-8
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,11 @@ QUnit.test( "XML Document Selectors", function( assert ) {
179179
} );
180180

181181
QUnit.test( "broken selectors throw", function( assert ) {
182-
assert.expect( 31 );
182+
assert.expect( 33 );
183183

184184
function broken( name, selector ) {
185185
assert.throws( function() {
186-
jQuery.find( selector );
186+
jQuery( selector );
187187
}, name + ": " + selector );
188188
}
189189

@@ -197,6 +197,8 @@ QUnit.test( "broken selectors throw", function( assert ) {
197197
broken( "Broken Selector", "," );
198198
broken( "Broken Selector", ",a" );
199199
broken( "Broken Selector", "a," );
200+
broken( "Post-comma invalid selector", "*,:x" );
201+
broken( "Identifier with bad escape", "foo\\\fbaz" );
200202
broken( "Broken Selector", "[id=012345678901234567890123456789" );
201203
broken( "Doesn't exist", ":visble" );
202204
broken( "Nth-child", ":nth-child" );
@@ -214,14 +216,14 @@ QUnit.test( "broken selectors throw", function( assert ) {
214216
broken( "Last-last-child", ":last-last-child" );
215217
broken( "Only-last-child", ":only-last-child" );
216218

217-
// Make sure attribute value quoting works correctly. See: #6093
219+
// Make sure attribute value quoting works correctly. See: trac-6093
218220
jQuery( "<input type='hidden' value='2' name='foo.baz' id='attrbad1'/>" +
219221
"<input type='hidden' value='2' name='foo[baz]' id='attrbad2'/>" )
220222
.appendTo( "#qunit-fixture" );
221223

222224
broken( "Attribute equals non-value", "input[name=]" );
223-
broken( "Attribute equals unquoted non-identifer", "input[name=foo.baz]" );
224-
broken( "Attribute equals unquoted non-identifer", "input[name=foo[baz]]" );
225+
broken( "Attribute equals unquoted non-identifier", "input[name=foo.baz]" );
226+
broken( "Attribute equals unquoted non-identifier", "input[name=foo[baz]]" );
225227
broken( "Attribute equals bad string", "input[name=''double-quoted'']" );
226228
broken( "Attribute equals bad string", "input[name='apostrophe'd']" );
227229
} );
@@ -637,14 +639,14 @@ QUnit.test( "attributes - hyphen-prefix matches", function( assert ) {
637639
} );
638640

639641
QUnit.test( "attributes - special characters", function( assert ) {
640-
assert.expect( 14 );
642+
assert.expect( 16 );
641643

642644
var attrbad;
643645
var div = document.createElement( "div" );
644646

645-
// trac-3279
647+
// trac-3729
646648
div.innerHTML = "<div id='foo' xml:test='something'></div>";
647-
assert.deepEqual( jQuery.find( "[xml\\:test]", div ),
649+
assert.deepEqual( jQuery( "[xml\\:test]", div ).get(),
648650
[ div.firstChild ],
649651
"attribute name containing colon" );
650652

@@ -655,6 +657,7 @@ QUnit.test( "attributes - special characters", function( assert ) {
655657
"<input type='hidden' id='attrbad_space' name='foo bar'/>" +
656658
"<input type='hidden' id='attrbad_dot' value='2' name='foo.baz'/>" +
657659
"<input type='hidden' id='attrbad_brackets' value='2' name='foo[baz]'/>" +
660+
"<input type='hidden' id='attrbad_leading_digits' name='agent' value='007'/>" +
658661
"<input type='hidden' id='attrbad_injection' data-attr='foo_baz&#39;]'/>" +
659662
"<input type='hidden' id='attrbad_quote' data-attr='&#39;'/>" +
660663
"<input type='hidden' id='attrbad_backslash' data-attr='&#92;'/>" +
@@ -677,6 +680,13 @@ QUnit.test( "attributes - special characters", function( assert ) {
677680
q( "attrbad_injection" ),
678681
"string containing quote and right bracket" );
679682

683+
assert.deepEqual( jQuery.find( "input[value=\\30 \\30\\37 ]", null, null, attrbad ),
684+
q( "attrbad_leading_digits" ),
685+
"identifier containing escaped leading digits with whitespace termination" );
686+
assert.deepEqual( jQuery.find( "input[value=\\00003007]", null, null, attrbad ),
687+
q( "attrbad_leading_digits" ),
688+
"identifier containing escaped leading digits without whitespace termination" );
689+
680690
assert.deepEqual( jQuery.find( "input[data-attr='\\'']", null, null, attrbad ),
681691
q( "attrbad_quote" ),
682692
"string containing quote" );

0 commit comments

Comments
 (0)