diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 77771b5b19a6..2d29781dfee9 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -230,9 +230,9 @@ const SUSPENSE_START_DATA = '$'; const SUSPENSE_END_DATA = '/$'; const SUSPENSE_PENDING_START_DATA = '$?'; const SUSPENSE_FALLBACK_START_DATA = '$!'; -const PREAMBLE_CONTRIBUTION_HTML = 0b001; -const PREAMBLE_CONTRIBUTION_BODY = 0b010; -const PREAMBLE_CONTRIBUTION_HEAD = 0b100; +const PREAMBLE_CONTRIBUTION_HTML = 'html'; +const PREAMBLE_CONTRIBUTION_BODY = 'body'; +const PREAMBLE_CONTRIBUTION_HEAD = 'head'; const FORM_STATE_IS_MATCHING = 'F!'; const FORM_STATE_IS_NOT_MATCHING = 'F'; @@ -1054,7 +1054,6 @@ export function clearSuspenseBoundary( suspenseInstance: SuspenseInstance, ): void { let node: Node = suspenseInstance; - let possiblePreambleContribution: number = 0; // Delete all nodes within this suspense boundary. // There might be nested nodes so we need to keep track of how // deep we are and only break out when we're back on top. @@ -1065,36 +1064,6 @@ export function clearSuspenseBoundary( if (nextNode && nextNode.nodeType === COMMENT_NODE) { const data = ((nextNode: any).data: string); if (data === SUSPENSE_END_DATA) { - if ( - // represents 3 bits where at least one bit is set (1-7) - possiblePreambleContribution > 0 && - possiblePreambleContribution < 8 - ) { - const code = possiblePreambleContribution; - // It's not normally possible to insert a comment immediately preceding Suspense boundary - // closing comment marker so we can infer that if the comment preceding starts with "1" through "7" - // then it is in fact a preamble contribution marker comment. We do this value test to avoid the case - // where the Suspense boundary is empty and the preceding comment marker is the Suspense boundary - // opening marker or the closing marker of an inner boundary. In those cases the first character won't - // have the requisite value to be interpreted as a Preamble contribution - const ownerDocument = parentInstance.ownerDocument; - if (code & PREAMBLE_CONTRIBUTION_HTML) { - const documentElement: Element = - (ownerDocument.documentElement: any); - releaseSingletonInstance(documentElement); - } - if (code & PREAMBLE_CONTRIBUTION_BODY) { - const body: Element = (ownerDocument.body: any); - releaseSingletonInstance(body); - } - if (code & PREAMBLE_CONTRIBUTION_HEAD) { - const head: Element = (ownerDocument.head: any); - releaseSingletonInstance(head); - // We need to clear the head because this is the only singleton that can have children that - // were part of this boundary but are not inside this boundary. - clearHead(head); - } - } if (depth === 0) { parentInstance.removeChild(nextNode); // Retry if any event replaying was blocked on this. @@ -1109,11 +1078,24 @@ export function clearSuspenseBoundary( data === SUSPENSE_FALLBACK_START_DATA ) { depth++; - } else { - possiblePreambleContribution = data.charCodeAt(0) - 48; + } else if (data === PREAMBLE_CONTRIBUTION_HTML) { + // If a preamble contribution marker is found within the bounds of this boundary, + // then it contributed to the html tag and we need to reset it. + const ownerDocument = parentInstance.ownerDocument; + const documentElement: Element = (ownerDocument.documentElement: any); + releaseSingletonInstance(documentElement); + } else if (data === PREAMBLE_CONTRIBUTION_HEAD) { + const ownerDocument = parentInstance.ownerDocument; + const head: Element = (ownerDocument.head: any); + releaseSingletonInstance(head); + // We need to clear the head because this is the only singleton that can have children that + // were part of this boundary but are not inside this boundary. + clearHead(head); + } else if (data === PREAMBLE_CONTRIBUTION_BODY) { + const ownerDocument = parentInstance.ownerDocument; + const body: Element = (ownerDocument.body: any); + releaseSingletonInstance(body); } - } else { - possiblePreambleContribution = 0; } // $FlowFixMe[incompatible-type] we bail out when we get a null node = nextNode; diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js index 0022820465a4..abdbf1a4f1c2 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js @@ -692,23 +692,16 @@ export function completeResumableState(resumableState: ResumableState): void { resumableState.bootstrapModules = undefined; } -const NoContribution /* */ = 0b000; -const HTMLContribution /* */ = 0b001; -const BodyContribution /* */ = 0b010; -const HeadContribution /* */ = 0b100; - export type PreambleState = { htmlChunks: null | Array, headChunks: null | Array, bodyChunks: null | Array, - contribution: number, }; export function createPreambleState(): PreambleState { return { htmlChunks: null, headChunks: null, bodyChunks: null, - contribution: NoContribution, }; } @@ -3279,6 +3272,12 @@ function pushTitleImpl( return null; } +// These are used by the client if we clear a boundary and we find these, then we +// also clear the singleton as well. +const headPreambleContributionChunk = stringToPrecomputedChunk(''); +const bodyPreambleContributionChunk = stringToPrecomputedChunk(''); +const htmlPreambleContributionChunk = stringToPrecomputedChunk(''); + function pushStartHead( target: Array, props: Object, @@ -3293,6 +3292,12 @@ function pushStartHead( if (preamble.headChunks) { throw new Error(`The ${'``'} tag may only be rendered once.`); } + + // Insert a marker in the body where the contribution to the head was in case we need to clear it. + if (preambleState !== null) { + target.push(headPreambleContributionChunk); + } + preamble.headChunks = []; return pushStartSingletonElement(preamble.headChunks, props, 'head'); } else { @@ -3317,6 +3322,11 @@ function pushStartBody( throw new Error(`The ${'``'} tag may only be rendered once.`); } + // Insert a marker in the body where the contribution to the body tag was in case we need to clear it. + if (preambleState !== null) { + target.push(bodyPreambleContributionChunk); + } + preamble.bodyChunks = []; return pushStartSingletonElement(preamble.bodyChunks, props, 'body'); } else { @@ -3341,6 +3351,11 @@ function pushStartHtml( throw new Error(`The ${'``'} tag may only be rendered once.`); } + // Insert a marker in the body where the contribution to the head was in case we need to clear it. + if (preambleState !== null) { + target.push(htmlPreambleContributionChunk); + } + preamble.htmlChunks = [DOCTYPE]; return pushStartSingletonElement(preamble.htmlChunks, props, 'html'); } else { @@ -4013,15 +4028,12 @@ export function hoistPreambleState( const rootPreamble = renderState.preamble; if (rootPreamble.htmlChunks === null && preambleState.htmlChunks) { rootPreamble.htmlChunks = preambleState.htmlChunks; - preambleState.contribution |= HTMLContribution; } if (rootPreamble.headChunks === null && preambleState.headChunks) { rootPreamble.headChunks = preambleState.headChunks; - preambleState.contribution |= HeadContribution; } if (rootPreamble.bodyChunks === null && preambleState.bodyChunks) { rootPreamble.bodyChunks = preambleState.bodyChunks; - preambleState.contribution |= BodyContribution; } } @@ -4101,11 +4113,7 @@ export function pushStartActivityBoundary( export function pushEndActivityBoundary( target: Array, renderState: RenderState, - preambleState: null | PreambleState, ): void { - if (preambleState) { - pushPreambleContribution(target, preambleState); - } target.push(endActivityBoundary); } @@ -4220,11 +4228,7 @@ export function writeStartClientRenderedSuspenseBoundary( export function writeEndCompletedSuspenseBoundary( destination: Destination, renderState: RenderState, - preambleState: null | PreambleState, ): boolean { - if (preambleState) { - writePreambleContribution(destination, preambleState); - } return writeChunkAndReturn(destination, endSuspenseBoundary); } export function writeEndPendingSuspenseBoundary( @@ -4236,48 +4240,10 @@ export function writeEndPendingSuspenseBoundary( export function writeEndClientRenderedSuspenseBoundary( destination: Destination, renderState: RenderState, - preambleState: null | PreambleState, ): boolean { - if (preambleState) { - writePreambleContribution(destination, preambleState); - } return writeChunkAndReturn(destination, endSuspenseBoundary); } -const boundaryPreambleContributionChunkStart = stringToPrecomputedChunk(''); - -function pushPreambleContribution( - target: Array, - preambleState: PreambleState, -) { - // Same as writePreambleContribution but for the render phase. - const contribution = preambleState.contribution; - if (contribution !== NoContribution) { - target.push( - boundaryPreambleContributionChunkStart, - // This is a number type so we can do the fast path without coercion checking - // eslint-disable-next-line react-internal/safe-string-coercion - stringToChunk('' + contribution), - boundaryPreambleContributionChunkEnd, - ); - } -} - -function writePreambleContribution( - destination: Destination, - preambleState: PreambleState, -) { - const contribution = preambleState.contribution; - if (contribution !== NoContribution) { - writeChunk(destination, boundaryPreambleContributionChunkStart); - // This is a number type so we can do the fast path without coercion checking - // eslint-disable-next-line react-internal/safe-string-coercion - writeChunk(destination, stringToChunk('' + contribution)); - writeChunk(destination, boundaryPreambleContributionChunkEnd); - } -} - const startSegmentHTML = stringToPrecomputedChunk(''); diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js index 78615d806324..43a0545ba10f 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js @@ -224,12 +224,11 @@ export function pushStartActivityBoundary( export function pushEndActivityBoundary( target: Array, renderState: RenderState, - preambleState: null | PreambleState, ): void { if (renderState.generateStaticMarkup) { return; } - pushEndActivityBoundaryImpl(target, renderState, preambleState); + pushEndActivityBoundaryImpl(target, renderState); } export function writeStartCompletedSuspenseBoundary( @@ -269,30 +268,20 @@ export function writeStartClientRenderedSuspenseBoundary( export function writeEndCompletedSuspenseBoundary( destination: Destination, renderState: RenderState, - preambleState: null | PreambleState, ): boolean { if (renderState.generateStaticMarkup) { return true; } - return writeEndCompletedSuspenseBoundaryImpl( - destination, - renderState, - preambleState, - ); + return writeEndCompletedSuspenseBoundaryImpl(destination, renderState); } export function writeEndClientRenderedSuspenseBoundary( destination: Destination, renderState: RenderState, - preambleState: null | PreambleState, ): boolean { if (renderState.generateStaticMarkup) { return true; } - return writeEndClientRenderedSuspenseBoundaryImpl( - destination, - renderState, - preambleState, - ); + return writeEndClientRenderedSuspenseBoundaryImpl(destination, renderState); } export type TransitionStatus = FormStatus; diff --git a/packages/react-markup/src/ReactFizzConfigMarkup.js b/packages/react-markup/src/ReactFizzConfigMarkup.js index 7e95c0ce3904..3d08ed1ee64a 100644 --- a/packages/react-markup/src/ReactFizzConfigMarkup.js +++ b/packages/react-markup/src/ReactFizzConfigMarkup.js @@ -162,7 +162,6 @@ export function pushStartActivityBoundary( export function pushEndActivityBoundary( target: Array, renderState: RenderState, - preambleState: null | PreambleState, ): void { // Markup doesn't have any instructions. return; @@ -192,7 +191,6 @@ export function writeStartClientRenderedSuspenseBoundary( export function writeEndCompletedSuspenseBoundary( destination: Destination, renderState: RenderState, - preambleState: null | PreambleState, ): boolean { // Markup doesn't have any instructions. return true; @@ -200,7 +198,6 @@ export function writeEndCompletedSuspenseBoundary( export function writeEndClientRenderedSuspenseBoundary( destination: Destination, renderState: RenderState, - preambleState: null | PreambleState, ): boolean { // Markup doesn't have any instructions. return true; diff --git a/packages/react-noop-renderer/src/ReactNoopServer.js b/packages/react-noop-renderer/src/ReactNoopServer.js index 5af393f3317e..7d9597d95610 100644 --- a/packages/react-noop-renderer/src/ReactNoopServer.js +++ b/packages/react-noop-renderer/src/ReactNoopServer.js @@ -181,7 +181,6 @@ const ReactNoopServer = ReactFizzServer({ pushEndActivityBoundary( target: Array, renderState: RenderState, - preambleState: null | PreambleState, ): void { target.push(POP); }, diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index b89c4adc349d..0eda8bcb02e9 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -2243,11 +2243,7 @@ function renderActivity( renderNode(request, task, props.children, -1); task.keyPath = prevKeyPath; } - pushEndActivityBoundary( - segment.chunks, - request.renderState, - task.blockedPreamble, - ); + pushEndActivityBoundary(segment.chunks, request.renderState); segment.lastPushedText = false; } } @@ -4908,7 +4904,6 @@ function flushSegment( return writeEndClientRenderedSuspenseBoundary( destination, request.renderState, - boundary.fallbackPreamble, ); } else if (boundary.status !== COMPLETED) { if (boundary.status === PENDING) { @@ -4979,11 +4974,7 @@ function flushSegment( const contentSegment = completedSegments[0]; flushSegment(request, destination, contentSegment, hoistableState); - return writeEndCompletedSuspenseBoundary( - destination, - request.renderState, - boundary.contentPreamble, - ); + return writeEndCompletedSuspenseBoundary(destination, request.renderState); } }