-
Notifications
You must be signed in to change notification settings - Fork 166
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
ShadyDOM breaks querySelector polyfills #480
Comments
This is not just breaking qSA polyfills; ShadyDom actually breaks support for |
@bicknellr What is the support and maintenance status of these polyfills? |
@sorvell would have a better idea of the status of Shady DOM specifically but he's out of office for a few days; I'll ping him when he's back. Shadow DOM and custom elements are supported in all modern browsers so we've generally been handling issues for those polyfills on an on-demand basis, depending on severity. A couple things that would be helpful to know: Are you seeing this issue in situations where the polyfill is expected to be disabled? (I.e. in a browser that supports shadow DOM and the polyfill is not being explicitly enabled.) Also, in what order does your repro install Shady DOM and the other polyfill? |
I see that the report that jQuery got was with @blackjackyau what's the reason you're forcing ShadyDOM to apply even in browsers with native Shadow DOM support? |
Thanks for the repro! It looks like this issue is happening because Shady DOM's wrappers for these methods walk the nodes in the relevant shadow root and call |
@mgol yes, shady is needed in our case as the library is built with the assumption of shady polyfill. And yes, it looks like shadydom is no longer needed ( deprecated ) as most of the modern browsers are now supported with native shadow dom support. |
@sorvell, @justinfagnani, and I met to discuss this issue and talked through some options. This is going to be a tradeoff between correctness and performance, so we want to get feedback from this thread to make sure that our proposed option would actually work for your use cases before diving in on the implementation. For context, Shady DOM emulates Shadow DOM by making the structure of the real tree match (to the extent that it can) what would be the flattened tree from the perspective of a user of the DOM APIs it wraps, which is effectively what is rendered by browsers with native Shadow DOM support. More specifically, this means that in the real tree (a) children of shadow root hosts are replaced by the children of their shadow roots, (b) slots in those shadow roots are replaced by the children of those shadow roots' hosts that would be assigned to them, and (c) elements which would not be assigned to a slot are removed from the tree. As you would expect, this has implications for selectors since the browser matches selectors against the tree as it is structured in its internal model and not against Shady DOM's user-facing model somehow. Open this for an example of how Shady DOM builds the real tree from the user-facing tree and how this affects selectors, if you're interested.Consider this tree: <style>
outer-element inner-element {
color: blue;
}
outer-element > inner-element {
color: red;
}
</style>
<outer-element>
<template shadowroot="open">
<div>
<slot></slot>
</div>
</template>
<inner-element>the text</inner-element>
</outer-element> In a browser with native Shadow DOM support, "the text" is red because the <style>
outer-element inner-element {
color: blue;
}
outer-element > inner-element {
color: red;
}
</style>
<outer-element>
<div>
<inner-element>the text</inner-element>
</div>
</outer-element> If we assume Shady CSS2 isn't being used to rewrite selectors in that Here's a summary of the options that we talked about:
Given the costs involved with the more correct options and that using Shady DOM already requires some awareness of the flat tree with regards to selectors because Shady CSS can't transform all of them perfectly, we think the best choice is to add a flag to Shady DOM that enables the last option (i.e. the wrappers just call into the native functions and filter). This means that the same rules for selectors passed to these functions in Shady DOM would apply (i.e. they're matched against the flat tree), but it would trade the ability to potentially select unassigned nodes for the ability to use context-element-dependent selectors since the given selector would now be matched using the actual node you called the function on as the scoping root. Footnotes
|
Can you give an example that would be incorrect? My knowledge of shadow dom is limited and I can not currently judge how impactful it is to be incorrect in these cases. |
@bicknellr Thanks for the comprehensive proposal. I'll state what I understood from the perspective of the jQuery issue, please correct me if I'm wrong. My understandingAs I understand, the proposed change requires folks using ShadyDOM in forced mode to enable this new flag to make it work again when updating jQuery to 4.0.0 or newer. I also understand that this flag would make the patched jQuery behaviorjQuery's In jQuery <4, the Sizzle selector engine runs a support test for In jQuery >=4, we plan to drop all support tests that would only fail in IE and instead to do brand checks via Potential changes to jQueryOptionally, we could create an escape hatch and still define the support test in jQuery 4, just define it via the brand check: jQuery.support.scope = !document.documentMode; and then use that test result internally instead of a direct brand check. The out-of-the box behavior would be the same but people using ShadyDOM in forced mode could add the following to their app:
after jQuery is loaded and all browsers would get the temporary ID treatment. I'd normally prefer to have an upstream fix instead of adding such escape hatches to jQuery as this can blow up in size if we start considering all cases, but ShadyDOM may potentially have a large enough impact that it might be acceptable, especially that there's no ideal solution in sight. Pinging the jQuery core team: @timmywil @gibson042 @dmethvin what do you think? Would that make the changes in ShadyDOM not necessary or are there other concerns that justify a change? P.S. jQuery
|
Sure, let's say we have a tree like this: <outer-element>
<template shadowroot="open">
<div>
<slot></slot>
</div>
</template>
<inner-element>the text</inner-element>
</outer-element> and we want to do this: const innerElement = outerElement.querySelector(':scope > inner-element'); and we expect that to get the <outer-element>
<div>
<inner-element>the text</inner-element>
</div>
</outer-element> Then we make the <outer-element class="very-unique-class">
<div>
<inner-element>the text</inner-element>
</div>
</outer-element> Then, it rewrites The current implementation as well as options 3, 4, and 5 all have this problem because they all use the browser to test selectors against the flat tree.1 However, unlike the current implementation (or option 2), those options also give up control over the set of candidate elements so that Shady DOM's (native) I'm finding it hard to keep track of all of this, so here's my current understanding in table form:
In that meeting the other day, our priorities seemed to roughly land here: "No CSS parser?" and "No new tree effects?" are important, "Finds unassigned elements?" is breaking but seems ok if opt-in, and "
That seems like a good assessment.
I'm confused. This makes it sound like jQuery 4 is intended to continue supporting browsers without native
Yes, I think removing Footnotes
|
(I messed up the table originally: the "3. Rewrite |
🤔 This sounds very brittle and there doesn't seem to be a path that is an obvious fix for the original issue. I need to test a few things on my end, but I might be able to apply all other This way ShadyDOM should be unable to break those polyfills. |
I need to test a few things on my end, but I might be able to apply all other qSA polyfills (:scope, :has()) first, before Shady DOM. This way ShadyDOM should be unable to break those polyfills. I have tried this and this doesn't work. If I force a correct loading order and I force a polyfill for But this is not really a fix, it is another bug :/
Can we make a list of selectors that are impacted by this?
A full CSS parser would not be needed by the way.
|
@blackjackyau Can you clarify how/why your library has a hard dependency on the ShadyDOM polyfill?
Ideally no one depends on a polyfill. In a prefect world the polyfill behaves exactly like the native feature and everyone depends only on the aspects of the native feature. With the extra information provided by @bicknellr I do not think it is doable to force ShadyDOM even in browsers with native support and also have full support for any additional selectors that have been (and will be) introduced (like |
Yes but just for features that don't work correctly in IE only. I know it seems go against the recommended industry practice of not detecting browsers but features (the recommendation that the jQuery team has been involved in advocating a lot as well) but there are some aspects that make it a bit special:
In this case, |
BTW, I think my characterization of option 3 in my last comment was wrong since it wouldn't have to give up control over the candidate list assuming the wrapper would still manually walk and call
I think I was also wrong here: after looking through https://drafts.csswg.org/selectors/, I haven't found anything other than
True, we don't actually need a full parser, just enough for selector lists, but we still would like to avoid this if we can since we can't really anticipate future selector syntax. @sorvell, WDYT?
Yeah, that seems like it would give jQuery users a simple workaround at least. |
It is correct that you can not anticipate future selector syntax and that one day this polyfill might cause incorrect results again if people keep applying it in modern browsers. I don't think this weighs up equally to the current state where this polyfill breaks As I understand it, the reported issues here are : browser does not support
browser supports
browser supports
|
We discussed this issue in the jQuery Team meeting today, for now we'd like to wait and see if this issue can get a resolution within ShadyDOM as that would work beyond jQuery.
EDIT: Questions migrated back to jquery/jquery#5032. |
Maybe better to split this issue? |
Makes sense; those issues were initially tied but now it seems they should be addressed separately. Let's follow-up on the jQuery aspect of it in the original jQuery issue, jquery/jquery#5032. I'll migrate my questions there. |
In a vacuum, I think this could just be considered a documentation issue. Both would ideally work but fixing the case where the
This is the core issue; specifically, when Shady DOM enables itself and breaks existing
In a vacuum again, I think that making sure their app works without Shady DOM and doesn't force-enable it is a more fundamental issue than We don't want anyone to unconditionally force the polyfills if possible because they lose the automatic perf improvement from using the built-in implementation when available and are prone to accidentally becoming reliant on bugs or non-standard behaviors they introduce. The best way to go about using them is to let the polyfills decide when to enable themselves and use only the subset of features that work in both. Given that, I don't think these three are really separate issues from the perspective of finding a fix for Shady DOM. I spent a little time looking at parsing selector lists yesterday and limiting the scope to just those doesn't really seem to cut down on the complexity that much. Lexing and parsing alone are quite involved. (I also just realized while looking through that page that CSS comments are permitted in selector lists given to One thing to consider is that you can detect if Shady DOM is enabled with |
This is a non-starter for me. Issues should be fixed upstream or not at all. You really do not need full lexing and parsing. You only need enough to differentiate between these selectors :
Comments are only a factor because they start a new token.
I've updated the snippet above to account for this. |
|
Using native
From experience I can tell that it is not realistic to place a polyfill in maintenance mode before every browser that needs it has died. Issues can be discovered years after initial release. Maybe best to clearly document that these polyfills are no longer actively maintained? It might also be interesting to find a project that is willing to adopt these polyfills. |
Option 6, or maybe 2b, would be to split complex selectors into simple/compound selectors, and walk the logical tree matching the elements against the simple selectors with |
I still need to make a release; I'll post again once that's done. |
Sorry, I hit a lot of snags while trying to import but I haven't forgotten about this. |
I've just pushed new releases generated at a75b94e (versions in commit message). Docs for the new polyfills/packages/shadydom/src/shadydom.js Lines 121 to 195 in a75b94e
|
@bicknellr Thank you for the update! I am only using I will try to take a closer look as soon as possible to give you more useful feedback. Tests are also still failing, but this might be expected. |
Whoa, that's a serious perf hit. If you're able to get a reduced repro, please send it along and I'll take a look. Maybe it's the |
If I have to guess I would say it is not your changes, or not your changes alone. I am guessing the technique you used is similar to what I am using for This works ok-isch when there is one such operation. If the performance hit is just that, then I can live with that. I don't think it is reasonable for users to expect this combo to work :
|
Without ShadyDOM:
With ShadyDOM:
These two tests appear to be the most stable and reliable indicators. The whole test suite and the increase from ±3 seconds to ±50 seconds seem to caused by this change but it is noisy. Garbage collection, browser freezes, test host all come into play. (i.e. we are trashing that test machine and sometimes it's better, sometimes it's worse) These can be rephrased as : With(out) The What works and fails is now also less clear cut. Now it seems that somethings work in some browsers but not in others.
I haven't done a full matrix of what works and what is broken in which browsers because that is very expensive. I stopped checking after noticing that it sometimes works and sometimes doesn't. |
The way that the polyfills/packages/shadydom/src/patches/ParentNode.js Lines 354 to 364 in a75b94e
Querying for polyfills/packages/shadydom/src/patches/Node.js Lines 553 to 556 in a75b94e
If the tests often query for
Just to be clear, you're specifically referring to a case where you've set |
I tried
Ha! Yeah, that won't ever be compatible with how the
These steps are a bit simplified and a bunch more logic was needed to support relative selector syntax We also make use of So each call to But as I said before, I think this is fine. I don't think developers should expect all these features to work together in very old browsers. If you need to target browsers so old that they don't support shadow dom, then maybe don't use We can add a warning on our end if we detect that both of these features are used in the same bundle and that it will have a performance impact. Please let me know if you want to try and improve the performance issues, but for me at least this is fine. I really wanted Thank you again for all the time and effort you poured into this! |
Description
querySelector
and friends require polyfills for features like:scope
or:has()
in older browsers.These polyfills break when also including
@webcomponents/shadydom/shadydom.min.js
.Not sure why as I am not familiar with the code base here.
Could be because an unpolyfilled
querySelector
is memoized or because it is overwritten without wrapping and extending. the originalExample
include :
run
someElement.querySelector(':scope')
This will not return the expected result.
Expected behavior
Expected ShadyDOM to leave
querySelector
as is or correctly wrap it.Actual behavior
ShadyDOM breaks
querySelector
polyfills.Version
"@webcomponents/shadydom": "^1.9.0"
Browsers affected
The text was updated successfully, but these errors were encountered: