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

[selectors] The forgiving nature of :has breaks jQuery when used with a complex :has selector #7676

Closed
mgol opened this issue Sep 2, 2022 · 44 comments

Comments

@mgol
Copy link

mgol commented Sep 2, 2022

The fact that the native :has pseudo-class:
https://drafts.csswg.org/selectors/#relational
takes forgiving-relative-selector-list as an argument:
https://drafts.csswg.org/selectors/#forgiving-selector
means the contents are not validated.

jQuery has supported the :has pseudo-class for ages. However, its support is more powerful; in particular, it allows for jQuery extensions like :contains to appear within :has.

The way the jQuery selector engine works (and have worked for a long time), the selector is tried against querySelectorAll with minor modifications and if it throws, it goes through the internal selector engine. It's either-or.

Since selectors like ul:has(li:contains('Item')) no longer throw in Chrome, jQuery defers to querySelectorAll and such a selector returns 0 results even if the jQuery path should match something.

We can of course try to patch it in jQuery, perhaps defaulting selectors containing :has to use the jQuery selector engine. But it won't help countless apps using older jQuery versions. So I wanted to bring it for consideration for the standards committee. I understand if the decision is going to be "no changes in the spec are planned" but it'd be good to have this discussion and the decision made knowing the consequences.

As you can see, it didn't take long after the Chrome 105 release for us to get a bug report about this breakage:
jquery/jquery#5098
Implementing :has according to the spec makes the browser break the jQuery test suite right now.

@mgol
Copy link
Author

mgol commented Sep 2, 2022

It's worth noting this affects all jQuery versions from 1.3 released in January, 2009 to the current 3.6.1 released a few days ago.

@lilles
Copy link
Member

lilles commented Sep 2, 2022

This did not break with Safari shipping, because WebKit did not implement using forgiving selector list per spec: https://bugs.webkit.org/show_bug.cgi?id=244708

@lilles
Copy link
Member

lilles commented Sep 2, 2022

@byung-woo @anttijk @emilio

@byung-woo
Copy link
Member

byung-woo commented Sep 2, 2022

Even if we make the :has() unforgiving, it seems that there can be still same issues in :has(:is(.a:has(.b), .c)) since :is() is forgiving. (It seems that @lilles already asked about this here : jquery/jquery#5098 (comment))

@anttijk
Copy link

anttijk commented Sep 2, 2022

The current WebKit behavior (not by design) is to use forgiving parsing but fail the selector if the selector list ends up empty. That is

:has(:foo, bar) { }

is valid and parses to

:has(bar) { }

but

:has(:foo) is invalid.

Perhaps this is actually the best option considering the jQuery issue.

@emilio
Copy link
Collaborator

emilio commented Sep 2, 2022

The way the jQuery selector engine works (and have worked for a long time), the selector is tried against querySelectorAll with minor modifications and if it fails, it goes through the internal selector engine. It's either-or.

Shouldn't jQuery check CSS.supports("selector(:has(....))"), which should be unforgiving per spec? See #7280

@byung-woo
Copy link
Member

byung-woo commented Sep 2, 2022

Shouldn't jQuery check CSS.supports("selector(:has(....))"), which should be unforgiving per spec? See #7280

I agree that this is the correct way to validate :has() usage, but it's sad that even if JQuery applies this to the new version, there may be unexpected regressions when someone uses an older JQuery version. :(

Now I'm trying to merge a fix to Chrome to avoid existing JQuery conflict by following the WebKit's behavior. But I think that we need a way to handle this situation properly.

The one way I can imagine is to rename the pseudo class name in the spec so that there will not be any conflict with existing JQuery implementation (:has() -> :some-other-good-name())

@emilio
Copy link
Collaborator

emilio commented Sep 2, 2022

Is there any real-world site broken by the change? How frequent do we expect it to be to use jQuery-specific selectors inside :has() to begin with?

@emilio
Copy link
Collaborator

emilio commented Sep 2, 2022

Also, is this a "recent version of jQuery" kinda thing? Or would this change cause issues on older jQuery versions?

@emilio
Copy link
Collaborator

emilio commented Sep 2, 2022

If there's no real-world breakage, it might be worth just getting jQuery fixed by using CSS.supports(), rather than introduce a weird quirk in how :has() works vs. other modern selectors...

@mgol
Copy link
Author

mgol commented Sep 2, 2022

@emilio

Shouldn't jQuery check CSS.supports("selector(:has(....))"), which should be unforgiving per spec? See #7280

In Chrome 105, CSS.supports('selector(:has(div:contains(div)))') returns true. I'd gladly use this in jQuery if this was working as advertised.

Also, is this a "recent version of jQuery" kinda thing? Or would this change cause issues on older jQuery versions?

As I wrote in #7676 (comment), this affects virtually all jQuery versions used in the wild.

Is there any real-world site broken by the change? How frequent do we expect it to be to use jQuery-specific selectors inside :has() to begin with?

I am unable to answer this question. But it did both break our test suite and get us a report from a user at jquery/jquery#5098. @Rinzwind did you encounter this issue just in manual testing or did it actually break anything for you on a live site?

@mgol
Copy link
Author

mgol commented Sep 2, 2022

@byung-woo

Now I'm trying to merge a fix to Chrome to avoid existing JQuery conflict by following the WebKit's behavior. But I think that we need a way to handle this situation properly.

Note that the WebKit behavior still breaks some jQuery selectors. True, it doesn't break:

div:has(span:contains('Item'))

but it still breaks:

div:has(div, span:contains('Item'))

Maybe that latter kind of selectors is not used as often in the wild.

aarongable pushed a commit to chromium/chromium that referenced this issue Sep 2, 2022
Workaround to avoid JQuery :has() issue:
- w3c/csswg-drafts#7676

This CL follows the current WebKit behavior: Parse :has() as forgiving,
but make it invalid when all the arguments are dropped.
- csswg-drafts/issues/7676#issuecomment-1235450787

Bug: 1358953
Change-Id: Ib6bf08a6c3320585eb80e1761e852e530c369bd2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3868781
Commit-Queue: Byungwoo Lee <blee@igalia.com>
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1042628}
@lilles
Copy link
Member

lilles commented Sep 2, 2022

Opened issue for CSS.supports()/@supports: https://bugs.chromium.org/p/chromium/issues/detail?id=1359396

@lilles
Copy link
Member

lilles commented Sep 2, 2022

There is a real-world breakage reported here: https://crbug.com/1359246

@Rinzwind
Copy link

Rinzwind commented Sep 2, 2022

@Rinzwind did you encounter this issue just in manual testing or did it actually break anything for you on a live site?

In our case, none of the selectors used in the application itself seem to be affected by this problem, only the ones in our automated tests (see: jquery/jquery#5098 (comment)).

@tabatkins
Copy link
Member

Importantly, this is not specific to :has(), and so any change to :has() won't stop the breakage. This breakage is due to the nature of forgiving-selector-list in general, so any place that uses it (like :is(), as Byungwoo notes), will trigger the exact same problem - jQuery users putting jQuery-specific selectors into an :is() will similarly bypass the current check and stick with the browser's selector behavior. The :has()-specific part is just that :has() already exists as a jQuery-custom selector, while :is() doesn't, so older content using :has() will break. That said, new content using :is() with older jQuery versions will also continue to get the bad behavior.

So our options are:

  1. Rename :has() to not overlap with the jQuery name, so older content using older jQuery versions will continue to trigger the custom code path rather than use built-ins.
  2. Give up on forgiving-selector-list entirely, switching :is() and :has() to the unforgiving behavior, and never using it again.
  3. Accept that older versions of jQuery can cause page breakage, if you use custom jQuery selectors inside of :has() (or nest :has(), which is allowed in jQuery)

All three options suck! Unless the breakage gets fairly bad, tho, I do lean toward 3. The :has() name is very nice and already widely socialized, and I'd be extremely loathe to give up on forgiving-selector-list.

@mgol
Copy link
Author

mgol commented Sep 2, 2022

BTW, re:

Now I'm trying to merge a fix to Chrome to avoid existing JQuery conflict by following the WebKit's behavior. But I think that we need a way to handle this situation properly.

What use cases exist that make it useful to have :has forgiving that do not also require not following the WebKit behavior of throwing if none of the :has contents use only supported selectors?

The fact that in WebKit qSA('div:has(:foo)') throws while qSA('div:has(:foo, span)') does not is more confusing to me than either not throwing in both cases or throwing in both. It'd be good to learn if there are any arguments for this split to be good for the Web before Chrome jumps in to copy WebKit here and that behavior has to be standardized.

@mgol
Copy link
Author

mgol commented Sep 2, 2022

@tabatkins

jQuery users putting jQuery-specific selectors into an :is() will similarly bypass the current check and stick with the browser's selector behavior

Because the jQuery selector engine currently works via a binary switch - first, check qSA; if it throws, use the jQuery custom traversal - and because jQuery never implemented :is(), the behavior change is only that before :is() got implemented a selector like :is(div:contains("Foo")) would throw and once :is() got implemented it started returning 0 results. So there's a change but one unlikely to cause a breakage, contrary to the :has() situation.

Does Google have any way to collect metrics of how often :has is used with jQuery-specific pseudos like :contains? That would help a lot.

@Loirooriol Loirooriol added the selectors-4 Current Work label Sep 5, 2022
@andruud
Copy link
Member

andruud commented Sep 6, 2022

Does Google have any way to collect metrics of how often :has is used with jQuery-specific pseudos like :contains? That would help a lot.

@mgol We could query HTTP Archive. What are we looking for?

  • :has(.*:contains.*)
  • What else?

@mgol
Copy link
Author

mgol commented Sep 6, 2022

@andruud

We could query HTTP Archive. What are we looking for?

* `:has(.*:contains.*)`

* What else?

Any jQuery-specific pseudo inside of :has(), including:

  • positional pseudos: :first, :last, :eq, :even, :odd, :lt, :gt, and :nth
  • other jQuery-specific ones: :contains, :parent, :header, :input, :button, :text, :radio, :checkbox, :file, :password, :image, :submit, :reset, :animated - I think those are not available natively
  • native ones but not available in :has natively: :has (yes, nested)

There are other native ones also implemented by jQuery but those are probably fine? :not, :lang, :target, :root, :focus, :enabled, :disabled, :checked, :selected, :empty

EDIT: Updated based on the comment from @SelenIT

@SelenIT
Copy link
Collaborator

SelenIT commented Sep 6, 2022

@mgol isn't :not available in :has? The spec apparently suggests it's allowed (see Example 15). Are there any extra implementation-specific limitations?

@andruud
Copy link
Member

andruud commented Sep 6, 2022

  1. Give up on forgiving-selector-list entirely, switching :is() and :has() to the unforgiving behavior, and never using it again.

@tabatkins What about the following?

2b. Don't give up on forgiving-selector-list entirely, but spec the current Safari behavior. I.e. make :has() invalid if all the arguments are invalid.

... provided that it avoids enough problems to be worth it. (As I understand it doesn't fully solve the issue).

@emilio
Copy link
Collaborator

emilio commented Sep 6, 2022

That seems rather unfortunate fwiw, I'd much rather make them behave consistently regardless of the number of valid arguments...

If we had something like that I think I'd prefer making it unforgiving iff the number of provided arguments is exactly one, rather than making it depend on the number of valid arguments. But still unfortunate IMHO

@lilles
Copy link
Member

lilles commented Sep 7, 2022

So making:

div.class:has(:invalid, :invalid) {}

invalid instead of never matching wouldn't be too bad, but the consequence of dropping other fully valid selectors from a list, which would happen with main.class in:

main.class, div.class:has(:invalid, :invalid) {}

is more problematic.

@mgol
Copy link
Author

mgol commented Sep 7, 2022

@SelenIT

@mgol isn't :not available in :has? The spec apparently suggests it's allowed (see Example 15). Are there any extra implementation-specific limitations?

You are right. What's not supported natively is nested :has(), :not() in :has() seems fine.

I updated the comment.

tabatkins added a commit to web-platform-tests/wpt that referenced this issue Dec 9, 2022
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Dec 12, 2022
Automatic update from web-platform-tests
Make :has() unforgiving

Apply the issue resolution of making :has() unforgiving:
w3c/csswg-drafts#7676 (comment)

Bug: 1399744
Change-Id: Ibb499e251ecce7ba22bd454ea94b2c8c8b1d8afb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4090967
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Byungwoo Lee <blee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1081452}

--

wpt-commits: 37e131cadfd27444fa4223b826edbd6bed29c356
wpt-pr: 37423
BruceDai pushed a commit to BruceDai/wpt that referenced this issue Dec 13, 2022
Apply the issue resolution of making :has() unforgiving:
w3c/csswg-drafts#7676 (comment)

Bug: 1399744
Change-Id: Ibb499e251ecce7ba22bd454ea94b2c8c8b1d8afb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4090967
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Byungwoo Lee <blee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1081452}
mgol added a commit to mgol/jquery that referenced this issue Dec 13, 2022
jQuery has followed the following logic for selector handling for ages:
1. Modify the selector to adhere to scoping rules jQuery mandates.
2. Try `qSA` on the modified selector. If it succeeds, use the results.
3. If `qSA` threw an error, run the jQuery custom traversal instead.

It worked fine so far but now CSS has a concept of forgiving selector lists that
some selectors like `:is()` & `:has()` use. That means providing unrecognized
selectors as parameters to `:is()` & `:has()` no longer throws an error, it will
just return no results. That made browsers with native `:has()` support break
selectors using jQuery extensions inside, e.g. `:has(:contains("Item"))`.

Detecting support for selectors can also be done via:

```js
CSS.supports( "selector(SELECTOR_TO_BE_TESTED)" )
```
which returns a boolean. There was a recent spec change requiring this API to
always use non-forgiving parsing:
w3c/csswg-drafts#7280 (comment)
However, no browsers have implemented this change so far.

To solve this, two changes are being made:
1. In browsers supports the new spec change to `CSS.supports( "selector()" )`,
   use it before trying `qSA`.
2. Otherwise, add `:has` to the buggy selectors list.

Ref jquerygh-5098
Ref jquerygh-5107
Ref jquery/sizzle#486
Ref w3c/csswg-drafts#7676
jamienicol pushed a commit to jamienicol/gecko that referenced this issue Dec 14, 2022
Automatic update from web-platform-tests
Make :has() unforgiving

Apply the issue resolution of making :has() unforgiving:
w3c/csswg-drafts#7676 (comment)

Bug: 1399744
Change-Id: Ibb499e251ecce7ba22bd454ea94b2c8c8b1d8afb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4090967
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Byungwoo Lee <blee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1081452}

--

wpt-commits: 37e131cadfd27444fa4223b826edbd6bed29c356
wpt-pr: 37423
webkit-early-warning-system pushed a commit to nt1m/WebKit that referenced this issue Jan 10, 2023
https://bugs.webkit.org/show_bug.cgi?id=249914
rdar://103733208

Reviewed by Antti Koivisto.

Following CSSWG resolution: w3c/csswg-drafts#7676 (comment)

In order to unbreak jQuery.

* LayoutTests/imported/w3c/web-platform-tests/css/selectors/parsing/parse-has-expected.txt:
* Source/WebCore/css/parser/CSSSelectorParser.cpp:
(WebCore::CSSSelectorParser::consumeRelativeSelectorList):
(WebCore::CSSSelectorParser::consumePseudo):
(WebCore::CSSSelectorParser::consumeForgivingRelativeSelectorList): Deleted.
* Source/WebCore/css/parser/CSSSelectorParser.h:

Canonical link: https://commits.webkit.org/258712@main
jakearchibald pushed a commit to jakearchibald/csswg-drafts that referenced this issue Jan 16, 2023
aarongable pushed a commit to chromium/chromium that referenced this issue Feb 7, 2023
…ngParsing

Enable these two features by default.

CSSAtSupportsAlwaysNonForgivingParsing:
- Apply the csswg issue resolution: change '@supports' selector parsing.
- w3c/csswg-drafts#7280 (comment)

CSSPseudoHasNonForgivingParsing:
- Apply the csswg issue resolution: make ':has()' non-forgiving.
- w3c/csswg-drafts#7676 (comment)

Intent-to-ship: https://groups.google.com/u/1/a/chromium.org/g/blink-dev/c/RJrIxJA9LYw

Bug: 1359396, 1399744
Change-Id: I179f22074f5cf6c9c8d56e36a4e3360bfe892256
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4224863
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Byungwoo Lee <blee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1102139}
@mgol
Copy link
Author

mgol commented Feb 8, 2023

I have some doubts about the generic recommendation of wrapping with :is() or :where() to get forgiveness; I submitted an issue about this: #8430.

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 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 to jquery/jquery 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 <richard.gibson@gmail.com>
mgol added a commit to jquery/jquery 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 added a commit to jquery/sizzle 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.

Fixes jquery/jquery#5194
Closes gh-493
Ref jquery/jquery#5098
Ref jquery/jquery#5206
Ref jquery/jquery#5207
Ref gh-486
Ref w3c/csswg-drafts#7676
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests