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

Infinite loop fixed but still getting error in selectors #83

Open
rocioimpa opened this issue Apr 13, 2023 · 25 comments
Open

Infinite loop fixed but still getting error in selectors #83

rocioimpa opened this issue Apr 13, 2023 · 25 comments

Comments

@rocioimpa
Copy link

Hi, the infinite loop issue has been fixed but still getting this test failure in tests that were passing prior to release 2.2.3:
SyntaxError: 'style):not(script):not(noscript):not(link):not([tabIndex="-1"]' is not a valid selector

at emit (node_modules/nwsapi/src/nwsapi/js:557:17)
at Object._matches [as match] (node_modules/nwsapi/src/nwsapi/js:1400:9)
at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi/js:760:17))
at collect (node_modules/nwsapi/src/nwsapi/js:1552:21)

@alexgwolff
Copy link

See: #82

workaround:

"overrides": {
    "nwsapi": "2.2.2"
  }

@dperini
Copy link
Owner

dperini commented Apr 13, 2023

@rocioimpa
to be able to help you debugging the issue you reported I need you to write
a small code example that can reproduce the error you are getting.
However I retested the selector and found it being parsed correctly.

Furthermore I received confirmation from other devs telling the fix resolved the issue.
So please double check everything and report back the results you get.
Thank you!

@cole-adams
Copy link

Hello,
I'm also experiencing tests failing in >=2.2.3 that were passing in 2.2.2.

SyntaxError: 'slot):not([inert]' is not a valid selector
      at emit (node_modules/nwsapi/src/nwsapi.js:557:17)
      at Object._matches [as match] (node_modules/nwsapi/src/nwsapi.js:1400:9)
      at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi.js:760:17), <anonymous>:3:105)
      at collect (node_modules/nwsapi/src/nwsapi.js:1552:21)
      at Object._querySelectorAll [as select] (node_modules/nwsapi/src/nwsapi.js:1509:36)

To reproduce:

it("should not throw", () => {
  expect(() =>
    document.querySelectorAll("[tabindex]:not(slot):not([inert])")
  ).not.toThrow();
});

@asamuzaK
Copy link

Same here with v2.2.4.

Code:

const nodes = document.querySelectorAll('.foo:not(.bar):not([data-id="1"]');

Result:

SyntaxError: '.foo):not(.bar):not([data-id="1"]' is not a valid selector
      at emit (node_modules\nwsapi\src\nwsapi.js:557:17)
      at Object._matches [as match] (node_modules\nwsapi\src\nwsapi.js:1400:9)
      at Array.Resolver (eval at compile (node_modules\nwsapi\src\nwsapi.js:760:17), <anonymous>:3:105)
      at collect (node_modules\nwsapi\src\nwsapi.js:1552:21)
      at Object._querySelectorAll [as select] (node_modules\nwsapi\src\nwsapi.js:1509:36)

@dperini
Copy link
Owner

dperini commented Apr 15, 2023

@asamuzaK & @cole-adams
both the following DOM queries are passing here on my machines testing what you reported in the console:

document.querySelectorAll("[tabindex]:not(slot):not([inert])")

document.querySelectorAll('.foo:not(.bar):not([data-id="1"]');

are parsed correctly in version 2.2.4 of "nwsapi", not throwing errors in the console.
Please ensure you are loading "nwsapi" from the npm repository and not from a secondary fork.
If you load "nwsapi.js" in the browser you can verify current version by the following command in console:

NW.Dom.Version

Also, other developers have confirmed that these bugs were fixed, both the infinite recursion and the multiple ":not()".
Try using "nwsapi" independently of other packages in browser console first.

@asamuzaK
Copy link

Failed log: Update libs asamuzaK/sidebarTabs@99768e5

I'm not using nwsapi directly, I'm using jsdom which depends on nwsapi.
In v21.1.1 of jsdom, the nwsapi version is ^2.2.2, so v2.2.4 should be installed when you run tests with GitHub actions.

In package.json, temporary added overrides field and fixed nwsapi version to 2.2.2.

  "overrides": {
    "jsdom": {
      ".": "$jsdom",
      "nwsapi": "2.2.2"
    }
  },

Running tests again, it passed.
Add overrides field asamuzaK/sidebarTabs@01b8bb2

So it looks like v2.2.4 still has a bug.

asamuzaK added a commit to asamuzaK/sidebarTabs that referenced this issue Apr 15, 2023
Additional test for dperini/nwsapi#83
@asamuzaK
Copy link

asamuzaK commented Apr 15, 2023

Updated nwsapi to v2.2.4, it failed.

  "overrides": {
    "jsdom": {
      ".": "$jsdom",
      "nwsapi": "2.2.4"
    }
  },

Update nwsapi · asamuzaK/sidebarTabs@37c2664

@asamuzaK
Copy link

It looks like the regexp in logicalsel is not correct.

Refer to https://github.com/dperini/nwsapi/blob/master/src/nwsapi.js#L1002

console.log(':not(.foo):not(.bar):not([data-baz="1"])'.match(Patterns.logicalsel));

/*
[
  ':not(.foo):not(.bar):not([data-baz="1"])',
  'not',
  '.foo):not(.bar):not([data-baz="1"]',
  '',
  index: 0,
  input: ':not(.foo):not(.bar):not([data-baz="1"])',
  groups: undefined
]
*/
console.log(':not(.foo):not(.bar, [data-baz="1"])'.match(Patterns.logicalsel));

/*
[
  ':not(.foo):not(.bar, [data-baz="1"])',
  'not',
  '.foo):not(.bar, [data-baz="1"]',
  '',
  index: 0,
  input: ':not(.foo):not(.bar, [data-baz="1"])',
  groups: undefined
]
*/
console.log(':not(.foo):not(.bar[data-baz="1"])'.match(Patterns.logicalsel));

/*
[
  ':not(.foo):not(.bar[data-baz="1"])',
  'not',
  '.foo):not(.bar[data-baz="1"]',
  '',
  index: 0,
  input: ':not(.foo):not(.bar[data-baz="1"])',
  groups: undefined
]
*/

If there is an attribute selector at the end of the last :not(), it does not match correctly.

@spuente
Copy link

spuente commented Apr 16, 2023

Hi, the infinite loop issue has been fixed but still getting this test failure in tests that were passing prior to release 2.2.3: SyntaxError: 'style):not(script):not(noscript):not(link):not([tabIndex="-1"]' is not a valid selector

at emit (node_modules/nwsapi/src/nwsapi/js:557:17) at Object._matches [as match] (node_modules/nwsapi/src/nwsapi/js:1400:9) at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi/js:760:17)) at collect (node_modules/nwsapi/src/nwsapi/js:1552:21)

Hi @rocioimpa, I was facing a similar issue. The error seems to occur when there's an attribute selector (like [tabIndex="-1"]) inside of a :not and after a selector that is not an attribute (like link, script, .my-class, etc) inside of a :not.
I'm not very familiar with the internals of this library, but it seems like the fix for this issue (which was not occurring on v2.2.2) would probably involve some reverts, code updates and generation of a new patch version.

As of now and using v2.2.4, I've found a couple of workarounds that could help:

  • Switch the order of your selectors to have :not([tabIndex="-1"] before the rest of :not selectors:
:not([tabIndex="-1"]):not(style):not(script):not(noscript):not(link)
:not([tabIndex="-1"], style, script, noscript, link)
:not(style, script, noscript, link, [tabIndex="-1"])

@spuente
Copy link

spuente commented Apr 16, 2023

Hello, I'm also experiencing tests failing in >=2.2.3 that were passing in 2.2.2.

SyntaxError: 'slot):not([inert]' is not a valid selector
      at emit (node_modules/nwsapi/src/nwsapi.js:557:17)
      at Object._matches [as match] (node_modules/nwsapi/src/nwsapi.js:1400:9)
      at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi.js:760:17), <anonymous>:3:105)
      at collect (node_modules/nwsapi/src/nwsapi.js:1552:21)
      at Object._querySelectorAll [as select] (node_modules/nwsapi/src/nwsapi.js:1509:36)

To reproduce:

it("should not throw", () => {
  expect(() =>
    document.querySelectorAll("[tabindex]:not(slot):not([inert])")
  ).not.toThrow();
});

Hi @cole-adams, you could work around this issue by changing the order of the :not selectors to set :not([inert]) before :not(slot):

document.querySelectorAll("[tabindex]:not([inert]):not(slot)")

Or you could group the selectors inside a single :not(...) (See https://github.com/dperini/nwsapi/wiki/CSS-supported-selectors#logical-combination-pseudo-classes-selectors). In this case, the order won't matter, so both of the following snippets should work:

document.querySelectorAll("[tabindex]:not([inert], slot)")
document.querySelectorAll("[tabindex]:not(slot, [inert])")

I'm not very familiar with the internals of this library, so the above suggestions are just meant to work around the issue as a consumer of the library. I hope someone with more context of this codebase can find a definite fix, as this issue was not present on v2.2.2.

@asamuzaK
Copy link

:is() takes <forgiving-selector-list> as an argument, while :not() takes <complex-real-selector-list>.
IMHO, you should use different regexp for :is() and :not().

@stefcameron
Copy link

Or you could group the selectors inside a single :not(...) (See https://github.com/dperini/nwsapi/wiki/CSS-supported-selectors#logical-combination-pseudo-classes-selectors). In this case, the order won't matter, so both of the following snippets should work:

document.querySelectorAll("[tabindex]:not([inert], slot)")
document.querySelectorAll("[tabindex]:not(slot, [inert])")

I'm not very familiar with the internals of this library, so the above suggestions are just meant to work around the issue as a consumer of the library. I hope someone with more context of this codebase can find a definite fix, as this issue was not present on v2.2.2.

This suggestion involves using :not() with multiple arguments, which is not widely-enough supported yet across browsers.

We could change the order of the selectors as suggested here in https://github.com/focus-trap/tabbable/blob/master/src/index.js#L12

Hi @cole-adams, you could work around this issue by changing the order of the :not selectors to set :not([inert]) before :not(slot):

document.querySelectorAll("[tabindex]:not([inert]):not(slot)")

but since the order should not matter, it seems like not the right thing to do (or at least not something that should be necessary to do). I think nswapi should fix the bug instead of tabbable (and who knows how many other libraries) changing the order of their selectors just to make it work, aside from trying to mitigate this issue until it's fixed.

I might see if I can apply that v2.2.2 override to tabbable's package.json instead and then wait for the fix here.

@stefcameron
Copy link

tabbable@v6.1.2 has been published which temporarily overrides nwsapi and pins it to v2.2.2 which does not cause the issue. I hope that helps folks while we wait for a fix here.

@dperini
Copy link
Owner

dperini commented May 4, 2023

@stefcameron
I am really sorry for the troubles this gives you and the other users, however I am working on a fix for the multiple :not() selectors and specifically when one of them contains an attribute. Also suggestions are welcome and appreciated.

@stefcameron
Copy link

@dperini Thank you for working to fix the issue! Hopefully my fix takes a bit of pressure off for now. 😄

@dperini
Copy link
Owner

dperini commented May 4, 2023

I want to share the regular expresssion that I am currently refining to solve the first part of the problem.
The objective is to split the multiple :not() selectors at the right position in the various possible cases.

https://regex101.com/r/QZFy85/1

by adding a ^ (caret sign) at the start of the RE (hooking the RE to the start of the input selector),
or adding a $ (dollar sign) at the end of the RE (hooking the RE to the end of the input selector),

I am able to pop out the first or the last :not() selector from the string at the correct place.
These selectors will then be handed one by one to Javascript which will resolve the selector series recursively.

Please, if anybody see a missing or failing case or can spot any inconsistency with this strategy report or suggest.

Thank you !

@dperini
Copy link
Owner

dperini commented May 25, 2023

@ALL
please test if the new changes to the Regular Expression works in your cases.
This will be in 2.2.5 as soon as everybody confirm their case is resolved by this change.

@stefcameron
Copy link

@dperini I don't see a PR open with a fix against this issue. What line of code in nwsapi would you be updating to this new regex you're thinking of using in #83 (comment) ?

@dperini
Copy link
Owner

dperini commented Jun 19, 2023

@stefcameron
forgive me, I missed this question which is now 3 weeks old, but anyway ...
The regex I was talking about is the one in L #81 named "logicalsel",
the one in charge of resolving :is(), :not() and :has() pseudo-classes.
I finally changed it yesterday in commit 01216f8

@stefcameron
Copy link

@dperini Better late than never! 😉 Thank you for the commit and the updated code. I'll try to give this a try locally later this week when I have time for focus-trap and will report back here with my findings.

@stefcameron
Copy link

@dperini I installed nwsapi v2.2.5, replaced that one line with your updated one, and ran our tabbable tests. All passed! Though I'll be honest, they also passed with v2.2.5 and the old Regex as-is -- while they did not when this issue first surfaced. I don't understand why. But all passed with your changes.

@asamuzaK
Copy link

https://regex101.com/r/QZFy85/1
:not(:is([foo=bar], .baz[qux]))
only matches :is([foo=bar], .baz[qux])

@dperini
Copy link
Owner

dperini commented Jun 28, 2023

@asamuzaK
cannot reproduce currently published copy on nwsapi GitHub repository works for me.
Both the followed selector queries:

    :not(:is([foo=bar], .baz[qux]))
    :is(:not([foo=bar], .baz[qux]))

return the same results using "nwsapi" or "native" methods.
Please setup a minimal test that shows the problem, maybe I misunderstood where you are testing.
On RegEx101 all the test input lines return correct results.
PS: there were a few commit to "nwsapi" recently.

@asamuzaK
Copy link

The previous comment is the result of testing with https://regex101.com/r/QZFy85/1

  1. go to https://regex101.com/r/QZFy85/1
  2. clear test string
  3. input :not(:is([foo=bar], .baz[qux]))

Expected:
:not(:is([foo=bar], .baz[qux])) is highlighted

Actual:
:is([foo=bar], .baz[qux]) is highlighted

@dperini
Copy link
Owner

dperini commented Jun 29, 2023

@asamuzaK
I got it now ... sorry for the misunderstanding but 1 month has gone from when I published that message.
I should have explained more about that RE. It should apply only to the expression contained in the first logical selector.
The matched logical selector (let's say the :not() selector as in this case) is removed before the expression is evaluated and the compound expression is then handed back to Javascript for the recursion to work.

Have a look at the included RE.txt document which I quickly created trying to explain this process:

RE.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants