Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(platform-browser): unencapsulated styles leaking out of shadow dom host #42112

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

crisbeto
Copy link
Member

Currently when a component with ViewEncapsulation.None is inserted, it always adds its styles to document.head. This can be problematic when the component is inside the shadow DOM, because it can cause styles to leak out of it.

These changes resolve the issue by adding some extra logic to the SharedStylesHost that allows for multiple "insertion nodes" to be associated with a component host node. Then we use this new functionality to associate the unencapsulated component styles with the closest shadow root.

Note that in most cases we'll have one insertion node per host node, but we have to support multiple for the case where several unencapsulated components are inserted inside a shadow DOM host.

Fixes #35039.

@google-cla google-cla bot added the cla: yes label May 16, 2021
@crisbeto crisbeto force-pushed the 35039/shadow-dom-style-leak branch from 61e188e to 4ac9d7f Compare May 16, 2021 10:26
@crisbeto crisbeto added action: review The PR is still awaiting reviews from at least one requested reviewer target: patch This PR is targeted for the next patch release labels May 16, 2021
@crisbeto crisbeto marked this pull request as ready for review May 16, 2021 10:45
@pullapprove pullapprove bot requested a review from atscott May 16, 2021 10:45
// Maps all registered host nodes to a list of style nodes that have been added to the host node.
private _hostNodes = new Map<Node, Node[]>();
// Mapping of component host nodes to the nodes in which their styles are inserted.
private _hostNodes = new Map<Node, Map<Node, Node[]>>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there really a need to store a 1:n mapping from host node to insertion node? Isn't there always a single insertion node per host node?

IIUC, there are now multiple host nodes associated with a single insertion node (either the shadow root or the document head) but this mapping stores the reverse relation; from host node to their insertion point, which I think is singular.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is, but how would we distinguish which styles belong to which host node? I was trying to solve the issue where multiple host nodes point to a single insertion node and then one of the hosts is removed. In this case we don't want to clear the entire insertion node.

Copy link
Member

@JoostK JoostK May 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be sufficient:

private _hostNodes = new Map<Node, { insertionPoint: Node; styles: Node[] }>();

Then removing a host node can remove the corresponding styles from its insertion point. But I realize that the above is still insufficient when there's multiple host nodes that share styles that they have attached into the same insertion point; that is a bit harder to track.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think that we could do it by changing SharedStylesHost._stylesSet to a Map<string, Set<Node>> where the keys are the inserted styles and the set is the insertion nodes that have received the styles. My concern is that this will slow down cleanup, because we'd have to look through all the styles in order to remove the insertion node reference.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a change that should simplify this and resolve the issue where the same styles are inserted multiple times. It uses the insertion node as the key and keeps a counter of host nodes associated with the insertion node. The only downside is that we'll only clear the styles once every host node has been destroyed, but that doesn't seem like it would be too much of an issue?

// styles leak out into the document.
const rootNode = element.getRootNode?.();
if (typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot) {
return new ShadowDomRenderer(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that ShadowDomRenderer is doing style flattening in its constructor for every instantiation, instead of doing it only once and caching the result somewhere. This includes the regex replace for the %COMP% pattern, but that won't even be present as that is only applied by the compiler for components that use emulated style encapsulation. We can probably optimize this a bit, which has become more relevant now that the shadow DOM renderer is used more often (also because this can now trigger usage of ShadowDomRenderer even if not used in the Angular app itself, but bootstrapped into a shadow tree).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Similar flattening is done in the EmulatedEncapsulationDomRenderer2 but there it is less of an issue as rendered instances are cached per component.id. I wonder why the "flattening" isn't done in the compiler, TBH.

Also "flatten" is confusing right now as it does both flattening and %COMP% replacement - we should probably split those 2 different responsibilities.

Having said this I think that it is not strictly related to this PR - we can do those improvements in a separate PR (I'm tempted to do it so it is clearer what is going on....)

@crisbeto crisbeto force-pushed the 35039/shadow-dom-style-leak branch 2 times, most recently from fabe899 to 8456b8b Compare May 16, 2021 13:01
@zarend zarend added the area: core Issues related to the framework runtime label May 19, 2021
@ngbot ngbot bot added this to the Backlog milestone May 19, 2021
@crisbeto crisbeto force-pushed the 35039/shadow-dom-style-leak branch from 8456b8b to 5aaf89a Compare June 26, 2021 09:43
…m host

Currently when a component with `ViewEncapsulation.None` is inserted, it always adds its styles to `document.head`. This can be problematic when the component is inside the shadow DOM, because it can cause styles to leak out of it.

These changes resolve the issue by adding some extra logic to the `SharedStylesHost` that allows for multiple "insertion nodes" to be associated with a component host node. Then we use this new functionality to associate the unencapsulated component styles with the closest shadow root.

Note that in most cases we'll have one insertion node per host node, but we have to support multiple for the case where several unencapsulated components are inserted inside a shadow DOM host.

Fixes angular#35039.
@crisbeto crisbeto force-pushed the 35039/shadow-dom-style-leak branch 2 times, most recently from d980eb4 to 4448e75 Compare June 26, 2021 12:06
@jessicajaniuk
Copy link
Contributor

@crisbeto Did you still want to land this PR?

@crisbeto
Copy link
Member Author

Yes, I think that all the comments were addressed but the discussion has stalled.

@Yqnn
Copy link

Yqnn commented Jun 30, 2022

Any news ?
Having this PR merged would be great to get rid of workarounds based on ɵDomSharedStylesHost private API.

@jesusreal
Copy link
Contributor

Also waiting for this to be merged. It would be great to have this soon in Angular. Are there any news on it?

@mangei
Copy link

mangei commented Feb 22, 2023

Hi, any news on this? We ran into the same issue, when providing our Angular webcomponents which led to break the style of the host-application. (both applications used Angular Material, but in different versions). Thank you!

@ghost
Copy link

ghost commented Nov 20, 2024

shouldn't this be closed because of #58482

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
action: review The PR is still awaiting reviews from at least one requested reviewer area: core Issues related to the framework runtime cla: yes target: patch This PR is targeted for the next patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Styles from external components are included outside the scope of a shadow DOM component
8 participants