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

[Fiber] retain scripts on clearContainer and clearSingleton #26871

Merged
merged 1 commit into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -989,9 +989,23 @@ function clearContainerSparingly(container: Node) {
detachDeletedInstance(element);
continue;
}
// Script tags are retained to avoid an edge case bug. Normally scripts will execute if they
// are ever inserted into the DOM. However when streaming if a script tag is opened but not
// yet closed some browsers create and insert the script DOM Node but the script cannot execute
// yet until the closing tag is parsed. If something causes React to call clearContainer while
// this DOM node is in the document but not yet executable the DOM node will be removed from the
// document and when the script closing tag comes in the script will not end up running. This seems
// to happen in Chrome/Firefox but not Safari at the moment though this is not necessarily specified
// behavior so it could change in future versions of browsers. While leaving all scripts is broader
// than strictly necessary this is the least amount of additional code to avoid this breaking
// edge case.
//
// Style tags are retained because they may likely come from 3rd party scripts and extensions
case 'SCRIPT':
case 'STYLE': {
continue;
}
// Stylesheet tags are retained because tehy may likely come from 3rd party scripts and extensions
case 'LINK': {
if (((node: any): HTMLLinkElement).rel.toLowerCase() === 'stylesheet') {
continue;
Expand Down Expand Up @@ -1939,6 +1953,7 @@ export function clearSingleton(instance: Instance): void {
isMarkedHoistable(node) ||
nodeName === 'HEAD' ||
nodeName === 'BODY' ||
nodeName === 'SCRIPT' ||
nodeName === 'STYLE' ||
(nodeName === 'LINK' &&
((node: any): HTMLLinkElement).rel.toLowerCase() === 'stylesheet')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,14 @@ describe('ReactDOM HostSingleton', () => {
let node = element.firstChild;
while (node) {
if (node.nodeType === 1) {
const el: Element = (node: any);
if (
node.tagName !== 'SCRIPT' &&
node.tagName !== 'TEMPLATE' &&
node.tagName !== 'template' &&
!node.hasAttribute('hidden') &&
!node.hasAttribute('aria-hidden')
(el.tagName !== 'SCRIPT' &&
el.tagName !== 'TEMPLATE' &&
el.tagName !== 'template' &&
!el.hasAttribute('hidden') &&
!el.hasAttribute('aria-hidden')) ||
el.hasAttribute('data-meaningful')
) {
const props = {};
const attributes = node.attributes;
Expand Down Expand Up @@ -742,11 +744,13 @@ describe('ReactDOM HostSingleton', () => {
<link rel="stylesheet" href="headbefore" />
<title>this should be removed</title>
<link rel="stylesheet" href="headafter" />
<script data-meaningful="">true</script>
</head>
<body>
<link rel="stylesheet" href="bodybefore" />
<div>this should be removed</div>
<link rel="stylesheet" href="bodyafter" />
<script data-meaningful="">true</script>
</body>
</html>,
);
Expand All @@ -771,11 +775,13 @@ describe('ReactDOM HostSingleton', () => {
<head>
<link rel="stylesheet" href="headbefore" />
<link rel="stylesheet" href="headafter" />
<script data-meaningful="">true</script>
<title>something new</title>
</head>
<body>
<link rel="stylesheet" href="bodybefore" />
<link rel="stylesheet" href="bodyafter" />
<script data-meaningful="">true</script>
<div>something new</div>
</body>
</html>,
Expand Down