Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jQuery 3.6.3 throws on some invalid selectors accepted in <=3.6.1 in Firefox (it works in Chrome & Safari) #5194

Closed
mgol opened this issue Jan 13, 2023 · 13 comments · Fixed by #5206 or jquery/sizzle#493

Comments

@mgol
Copy link
Member

mgol commented Jan 13, 2023

Originally posted by @khetaramsb in #5177 (comment)

After upgrading jQuery from 3.6.0 to3.6.3 pseudo selector :before does not work in selector list with Firefox. Everything works with Chrome.

This worked in 3.6.0
jQuery(".inline-dialog-content:before").css({'left': 40});

In 3.6.3 it gives following error:

Uncaught Error: Syntax error, unrecognized expression: unsupported pseudo: before

@mgol mgol added Selector Discuss in Meeting Reserved for Issues and PRs that anyone would like to discuss in the weekly meeting. labels Jan 13, 2023
@mgol
Copy link
Member Author

mgol commented Jan 13, 2023

@khetaramsb despite using a single colon here, :before (and ::before) are really pseudo-elements, not pseudo-classes. They can neither be selected via jQuery nor via querySelectorAll.

I guess there's a behavior difference here because while a selector like .inline-dialog-content:before doesn't match anything, it also doesn't throw an error with querySelectorAll; it just returns an empty collection. The code you pasted was previously effectively a noop and now it throws. The reason for that is CSS.supports('selector(:before') returns true but CSS.supports('selector(:is(:before)') returns false as :is() doesn't support pseudo-elements inside.

That said, this is invalid input so I don't think we're going to change anything here; I don't see an easy way anyway. I've added it to the items to discuss with the team, though.

@mgol
Copy link
Member Author

mgol commented Jan 23, 2023

@timmywil @gibson042 I found what's up with this :before handling that we discussed at the team meeting. For simplicity, I'm talking exclusively about the non-qSA path as that's what's being triggered in Firefox for this invalid selector in jQuery 3.6.3.

So, $('.foo:before,bar') always throws but $('.foo:before') throws only if $('.foo') doesn't match anything. The difference is caused by an optimization in:

if ( match.length === 1 ) {

where 1-elem selector lists are treated differently. Now, .foo:before looks like a class + a pseudo-class (just because of those legacy pseudo-elements allowing a single colon as well) and since .foo doesn't match anything, there's a bail out at:

jquery/src/selector.js

Lines 1615 to 1621 in c66d470

// If seed is empty or no tokens remain, we can return early
tokens.splice( i, 1 );
selector = seed.length && toSelector( tokens );
if ( !selector ) {
push.apply( results, seed );
return results;
}

On the other hand, .foo::before (with double colons) fails during tokenizing so $('.foo:before') does throw regardless of whether $('.foo') would match anything or not.

@noraj
Copy link

noraj commented Jan 27, 2023

After upgrading jQuery from 3.6.0 to 3.6.3, find() method does not work properly with some CSS selectors with Firefox. Everything works with Chromium.

This worked in 3.6.0:

newline.find('label[for=mylabel').prop('for', function() { return $(this).attr('for') + '-' + rand } );

In 3.6.3 it gives following error with Firefox 109.0 but works with Chromium 109.0.5414.119:

Uncaught Error: Syntax error, unrecognized expression: label[for=input-project-codes
error
tokenize
select
se
find

It seems the invalid label[for=mylabel was accepted before and is no longer, I'll try with the missing closing square bracket.

@mgol
Copy link
Member Author

mgol commented Jan 27, 2023

@noraj the selector is invalid, it misses a closing attribute bracket. Try newline.find('label[for=mylabel]').

@noraj
Copy link

noraj commented Jan 27, 2023

@noraj the selector is invalid, it misses a closing attribute bracket. Try newline.find('label[for=mylabel]').

Yes I was updating my comment, weird it was accepted in 3.6.0 tho.

Update: it works with the closing bracket. But the fact the erroneous syntax was working in 3.6.30 and not in 3.6.3 is may be linked with this issue.

@timmywil
Copy link
Member

timmywil commented Jan 27, 2023

You are correct. It’s another selector that the is technically invalid but the qsa parser forgives. CSS.supports seems to not have the same forgiveness in its parser.

@timmywil timmywil removed the Discuss in Meeting Reserved for Issues and PRs that anyone would like to discuss in the weekly meeting. label Jan 30, 2023
@timmywil
Copy link
Member

We've discussed the options, and jQuery is now as unforgiving as CSS.supports. It used to be as forgiving as qSA, but now that the spec has introduced what's ironically called "forgiving parsing", I'm not sure we have a choice anymore. CSS.supports is still the right way to go. It's just unfortunate that we can't really pick and choose which selectors we want to be more forgiving with.

@timmywil
Copy link
Member

And...we discussed this further in the same meeting and completely reversed. Now that :has is going to be unforgiving in the spec, it seems unlikely for that situation to arise again. We're considering going back to the old way now.

@mgol
Copy link
Member Author

mgol commented Jan 30, 2023

I submitted an issue to CSSWG asking for some assurances about the plans for forgiving parsing: w3c/csswg-drafts#8378.

@mgol mgol changed the title After upgrading jQuery from 3.6.0 to3.6.3 pseudo selector :before does not work in selector list with Firefox. Everything works with Chrome. After upgrading jQuery from 3.6.0 to 3.6.3 pseudo selector :before does not work in selector list with Firefox. Everything works with Chrome. Jan 30, 2023
@mgol mgol changed the title After upgrading jQuery from 3.6.0 to 3.6.3 pseudo selector :before does not work in selector list with Firefox. Everything works with Chrome. jQuery 3.6.3 throws on some invalid selectors accepted in <=3.6.1 in Firefox (it works in Chrome & Safari) Jan 30, 2023
mgol added a commit to mgol/jquery that referenced this issue Feb 9, 2023
`CSS.supports( "selector(...)" )` has different semantics than selectors passed
to `querySelectorAll`. Apart from the fact that the former returns `false` for
unrecognized selectors and the latter throws, `qSA` is more forgiving and
accepts some invalid selectors, auto-correcting them where needed - for
example, mismatched brackers are auto-closed. This behavior difference is
breaking for many users.

To add to that, a recent CSSWG resolution made `:is()` & `:where()` the only
pseudos with forgiving parsing; browsers are in the process of making `:has()`
parsing unforgiving.

Taking all that into account, we go back to our previous try-catch approach
without relying on `CSS.supports( "selector(...)" )`. The only difference
is we detect forgiving parsing in `:has()` and mark the selector as buggy.

The PR also updates `playwright-webkit` so that we test against a version
of WebKit that already has non-forgiving `:has()`.

Fixes jquerygh-5194
Ref jquerygh-5098
Ref jquerygh-5107
Ref w3c/csswg-drafts#7676
mgol added a commit to mgol/jquery that referenced this issue Feb 9, 2023
`CSS.supports( "selector(...)" )` has different semantics than selectors passed
to `querySelectorAll`. Apart from the fact that the former returns `false` for
unrecognized selectors and the latter throws, `qSA` is more forgiving and
accepts some invalid selectors, auto-correcting them where needed - for
example, mismatched brackers are auto-closed. This behavior difference is
breaking for many users.

To add to that, a recent CSSWG resolution made `:is()` & `:where()` the only
pseudos with forgiving parsing; browsers are in the process of making `:has()`
parsing unforgiving.

Taking all that into account, we go back to our previous try-catch approach
without relying on `CSS.supports( "selector(...)" )`. The only difference
is we detect forgiving parsing in `:has()` and mark the selector as buggy.

The PR also updates `playwright-webkit` so that we test against a version
of WebKit that already has non-forgiving `:has()`.

Fixes jquerygh-5194
Ref jquerygh-5098
Ref jquerygh-5107
Ref w3c/csswg-drafts#7676
mgol added a commit to mgol/jquery that referenced this issue Feb 9, 2023
`CSS.supports( "selector(...)" )` has different semantics than selectors passed
to `querySelectorAll`. Apart from the fact that the former returns `false` for
unrecognized selectors and the latter throws, `qSA` is more forgiving and
accepts some invalid selectors, auto-correcting them where needed - for
example, mismatched brackers are auto-closed. This behavior difference is
breaking for many users.

To add to that, a recent CSSWG resolution made `:is()` & `:where()` the only
pseudos with forgiving parsing; browsers are in the process of making `:has()`
parsing unforgiving.

Taking all that into account, we go back to our previous try-catch approach
without relying on `CSS.supports( "selector(...)" )`. The only difference
is we detect forgiving parsing in `:has()` and mark the selector as buggy.

The PR also updates `playwright-webkit` so that we test against a version
of WebKit that already has non-forgiving `:has()`.

Fixes jquerygh-5194
Ref jquerygh-5098
Ref jquerygh-5107
Ref w3c/csswg-drafts#7676
mgol added a commit to mgol/sizzle that referenced this issue Feb 10, 2023
`CSS.supports( "selector(...)" )` has different semantics than selectors passed
to `querySelectorAll`. Apart from the fact that the former returns `false` for
unrecognized selectors and the latter throws, `qSA` is more forgiving and
accepts some invalid selectors, auto-correcting them where needed - for
example, mismatched brackers are auto-closed. This behavior difference is
breaking for many users.

To add to that, a recent CSSWG resolution made `:is()` & `:where()` the only
pseudos with forgiving parsing; browsers are in the process of making `:has()`
parsing unforgiving.

Taking all that into account, we go back to our previous try-catch approach
without relying on `CSS.supports( "selector(...)" )`. The only difference
is we detect forgiving parsing in `:has()` and mark the selector as buggy.

Fixes jquery/jquery#5194
Ref jquery/jquery#5098
Ref jquerygh-486
Ref w3c/csswg-drafts#7676
@mgol
Copy link
Member Author

mgol commented Feb 10, 2023

PRs:

We'll also need a new Sizzle release and a 4th PR against jQuery 3.6-stable bumping Sizzle after that.

mgol added a commit to mgol/jquery that referenced this issue Feb 13, 2023
`CSS.supports( "selector(...)" )` has different semantics than selectors passed
to `querySelectorAll`. Apart from the fact that the former returns `false` for
unrecognized selectors and the latter throws, `qSA` is more forgiving and
accepts some invalid selectors, auto-correcting them where needed - for
example, mismatched brackers are auto-closed. This behavior difference is
breaking for many users.

To add to that, a recent CSSWG resolution made `:is()` & `:where()` the only
pseudos with forgiving parsing; browsers are in the process of making `:has()`
parsing unforgiving.

Taking all that into account, we go back to our previous try-catch approach
without relying on `CSS.supports( "selector(...)" )`. The only difference
is we detect forgiving parsing in `:has()` and mark the selector as buggy.

The PR also updates `playwright-webkit` so that we test against a version
of WebKit that already has non-forgiving `:has()`.

Fixes jquerygh-5194
Ref jquerygh-5098
Ref jquerygh-5107
Ref w3c/csswg-drafts#7676
mgol added a commit to mgol/jquery that referenced this issue Feb 13, 2023
`CSS.supports( "selector(...)" )` has different semantics than selectors passed
to `querySelectorAll`. Apart from the fact that the former returns `false` for
unrecognized selectors and the latter throws, `qSA` is more forgiving and
accepts some invalid selectors, auto-correcting them where needed - for
example, mismatched brackers are auto-closed. This behavior difference is
breaking for many users.

To add to that, a recent CSSWG resolution made `:is()` & `:where()` the only
pseudos with forgiving parsing; browsers are in the process of making `:has()`
parsing unforgiving.

Taking all that into account, we go back to our previous try-catch approach
without relying on `CSS.supports( "selector(...)" )`. The only difference
is we detect forgiving parsing in `:has()` and mark the selector as buggy.

The PR also updates `playwright-webkit` so that we test against a version
of WebKit that already has non-forgiving `:has()`.

Fixes jquerygh-5194
Ref jquerygh-5098
Ref jquerygh-5107
Ref w3c/csswg-drafts#7676
mgol added a commit to mgol/sizzle that referenced this issue Feb 13, 2023
`CSS.supports( "selector(...)" )` has different semantics than selectors passed
to `querySelectorAll`. Apart from the fact that the former returns `false` for
unrecognized selectors and the latter throws, `qSA` is more forgiving and
accepts some invalid selectors, auto-correcting them where needed - for
example, mismatched brackers are auto-closed. This behavior difference is
breaking for many users.

To add to that, a recent CSSWG resolution made `:is()` & `:where()` the only
pseudos with forgiving parsing; browsers are in the process of making `:has()`
parsing unforgiving.

Taking all that into account, we go back to our previous try-catch approach
without relying on `CSS.supports( "selector(...)" )`. The only difference
is we detect forgiving parsing in `:has()` and mark the selector as buggy.

Fixes jquery/jquery#5194
Ref jquery/jquery#5098
Ref jquerygh-486
Ref w3c/csswg-drafts#7676
mgol added a commit that referenced this issue Feb 14, 2023
`CSS.supports( "selector(...)" )` has different semantics than selectors passed
to `querySelectorAll`. Apart from the fact that the former returns `false` for
unrecognized selectors and the latter throws, `qSA` is more forgiving and
accepts some invalid selectors, auto-correcting them where needed - for
example, mismatched brackers are auto-closed. This behavior difference is
breaking for many users.

To add to that, a recent CSSWG resolution made `:is()` & `:where()` the only
pseudos with forgiving parsing; browsers are in the process of making `:has()`
parsing unforgiving.

Taking all that into account, we go back to our previous try-catch approach
without relying on `CSS.supports( "selector(...)" )`. The only difference
is we detect forgiving parsing in `:has()` and mark the selector as buggy.

The PR also updates `playwright-webkit` so that we test against a version
of WebKit that already has non-forgiving `:has()`.

Fixes gh-5194
Closes gh-5206
Ref gh-5098
Ref gh-5107
Ref w3c/csswg-drafts#7676

Co-authored-by: Richard Gibson <[email protected]>
@mgol mgol reopened this Feb 14, 2023
mgol added a commit that referenced this issue Feb 14, 2023
`CSS.supports( "selector(...)" )` has different semantics than selectors passed
to `querySelectorAll`. Apart from the fact that the former returns `false` for
unrecognized selectors and the latter throws, `qSA` is more forgiving and
accepts some invalid selectors, auto-correcting them where needed - for
example, mismatched brackers are auto-closed. This behavior difference is
breaking for many users.

To add to that, a recent CSSWG resolution made `:is()` & `:where()` the only
pseudos with forgiving parsing; browsers are in the process of making `:has()`
parsing unforgiving.

Taking all that into account, we go back to our previous try-catch approach
without relying on `CSS.supports( "selector(...)" )`. The only difference
is we detect forgiving parsing in `:has()` and mark the selector as buggy.

The PR also updates `playwright-webkit` so that we test against a version
of WebKit that already has non-forgiving `:has()`.

Fixes gh-5194
Closes gh-5207
Ref gh-5206
Ref gh-5098
Ref gh-5107
Ref w3c/csswg-drafts#7676
@mgol mgol reopened this Feb 14, 2023
@mgol mgol reopened this Feb 14, 2023
mgol added a commit to mgol/jquery that referenced this issue Feb 14, 2023
@mgol
Copy link
Member Author

mgol commented Feb 14, 2023

Sizzle 2.3.10 has been released and here's a PR updating jQuery's 3.6-stable branch to that Sizzle version: #5209.

mgol added a commit that referenced this issue Feb 14, 2023
@mgol
Copy link
Member Author

mgol commented Feb 14, 2023

All PRs have landed; the issue is fixed.

@mgol mgol closed this as completed Feb 14, 2023
@mgol
Copy link
Member Author

mgol commented Feb 14, 2023

The TestSwarm run for 3.6-stable after landing #5209 looks good: https://swarm.jquery.org/job/13100.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment