From 1cd0639d4b6379837c1ac63131e22551bd22d7db Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 5 Oct 2017 17:47:24 +0100 Subject: [PATCH 01/15] Add regression test for CR-insensitive hydration --- .../ReactDOMServerIntegration-test.js | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js index 642e99a0e06d..990320327624 100644 --- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js +++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js @@ -1117,6 +1117,52 @@ describe('ReactDOMServerIntegration', () => { expectTextNode(e.childNodes[1], 'bar'); } }); + + describe('with carriage returns', () => { + // HTML parsing normalizes CR and CRLF to LF. + // https://www.w3.org/TR/html5/single-page.html#preprocessing-the-input-stream + // If we have a mismatch, it might be caused by that (and should not be reported). + // We won't be patching up in this case as that matches our past behavior. + + itRenders('a div with one text CR child', async render => { + const e = await render(
{'foo\rbar\r\nbaz\nqux'}
); + if ( + render === serverRender || + render === clientRenderOnServerString || + render === streamRender + ) { + expect(e.childNodes.length).toBe(1); + // Both CR and CRLF are replaced with LF. + expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\nbar\nbaz\nqux'); + } else { + expect(e.childNodes.length).toBe(1); + // Client rendering leaves CRs as they are. + // TODO: verify that it doesn't cause any observable difference. + expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\rbar\r\nbaz\nqux'); + } + }); + + itRenders('a div with two text CR children', async render => { + const e = await render(
{'foo\rbar'}{'\r\nbaz\nqux'}
); + if ( + render === serverRender || + render === clientRenderOnServerString || + render === streamRender + ) { + // Both CR and CRLF are replaced with LF. + // We have three nodes because there is a comment between them. + expect(e.childNodes.length).toBe(3); + expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\nbar'); + expectNode(e.childNodes[2], TEXT_NODE_TYPE, '\nbaz\nqux'); + } else { + expect(e.childNodes.length).toBe(2); + // Client rendering leaves CRs as they are. + // TODO: verify that it doesn't cause any observable difference. + expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\rbar'); + expectNode(e.childNodes[1], TEXT_NODE_TYPE, '\r\nbaz\nqux'); + } + }); + }); }); describe('number children', function() { From 4bb8ec35c5c2cbabeb5b383f6fe23b6b0d959295 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 5 Oct 2017 17:51:28 +0100 Subject: [PATCH 02/15] Normalize CR when comparing server HTML to DOM --- .../dom/fiber/ReactDOMFiberComponent.js | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/renderers/dom/fiber/ReactDOMFiberComponent.js b/src/renderers/dom/fiber/ReactDOMFiberComponent.js index 41c01404f8ea..e9482c4ce09e 100644 --- a/src/renderers/dom/fiber/ReactDOMFiberComponent.js +++ b/src/renderers/dom/fiber/ReactDOMFiberComponent.js @@ -59,6 +59,29 @@ var HTML = '__html'; var {Namespaces: {html: HTML_NAMESPACE}, getIntrinsicNamespace} = DOMNamespaces; +var NORMALIZE_NEWLINES_REGEX = /\r\n?/g; +function isServerTextDifferentFromClientText( + serverText: string, + clientText: string, +): boolean { + if (serverText === clientText) { + return false; + } + // HTML parsing normalizes CR and CRLF to LF. + // https://www.w3.org/TR/html5/single-page.html#preprocessing-the-input-stream + // If we have a mismatch, it might be caused by that. + // We won't be patching up in this case as that matches our past behavior. + // TODO: verify that it doesn't have any observable difference. + const normalizedClientText = clientText.replace( + NORMALIZE_NEWLINES_REGEX, + '\n', + ); + if (normalizedClientText === serverText) { + return false; + } + return true; +} + if (__DEV__) { var warnedUnknownTags = { // Chrome is the only major browser not shipping