From 2d557d55778c112eb2e1568b6858b6966477d961 Mon Sep 17 00:00:00 2001 From: Tomasz Tunik Date: Wed, 16 Feb 2022 12:46:36 +0100 Subject: [PATCH 1/7] fix Block Editor Iframe component to render in standards mode Iframe document doctype is not set by default and not having doctype will cause browser to render iframe contents in quirks mode. Appending doctype to existing document won't change iframe's rendering mode. Document.write will overwrite the document with a new one containing a correct doctype and will correctly render in stadard mode. Fixes #38854 --- .../block-editor/src/components/iframe/index.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index b4e1e44f6568b..db9905d0b8427 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -174,12 +174,24 @@ function Iframe( const setRef = useRefEffect( ( node ) => { function setDocumentIfReady() { const { contentDocument, ownerDocument } = node; - const { readyState, documentElement } = contentDocument; + const { readyState } = contentDocument; if ( readyState !== 'interactive' && readyState !== 'complete' ) { return false; } + // Iframe document doctype is not set by default and not having doctype + // will cause browser to render iframe contents in quirks mode. Appending + // doctype to existing document won't change iframe's rendering mode. + // Document.write will overwrite the document with a new one containing + // a correct doctype and will correctly render in stadards mode. + contentDocument.write( + '' + contentDocument.documentElement.outerHTML + ); + contentDocument.close(); + + const { documentElement } = contentDocument; + bubbleEvents( contentDocument ); setIframeDocument( contentDocument ); clearerRef( documentElement ); From c74edb619e5bafa5f7e99d6394d7f81f3710807e Mon Sep 17 00:00:00 2001 From: Tomasz Tunik Date: Thu, 17 Feb 2022 19:03:27 +0100 Subject: [PATCH 2/7] Tests validating site editor's iframe rendering mode it will visit the editor and ensure that iframe is rendering in standards CSS1Compat rendering mode. If it had BackCompat set it would render in quirks mode. --- .../site-editor/iframe-rendering-mode.test.js | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 packages/e2e-tests/specs/site-editor/iframe-rendering-mode.test.js diff --git a/packages/e2e-tests/specs/site-editor/iframe-rendering-mode.test.js b/packages/e2e-tests/specs/site-editor/iframe-rendering-mode.test.js new file mode 100644 index 0000000000000..d7eb0a22ae2da --- /dev/null +++ b/packages/e2e-tests/specs/site-editor/iframe-rendering-mode.test.js @@ -0,0 +1,31 @@ +/** + * WordPress dependencies + */ +import { activateTheme, visitSiteEditor } from '@wordpress/e2e-test-utils'; + +describe( 'Site editor iframe rendering mode', () => { + beforeAll( async () => { + await activateTheme( 'emptytheme' ); + } ); + + afterAll( async () => { + await activateTheme( 'twentytwentyone' ); + } ); + + it( 'Should render editor in standards mode.', async () => { + await visitSiteEditor( { + postId: 'emptytheme//index', + postType: 'wp_template', + } ); + + const compatMode = await page.evaluate( + () => + document.querySelector( `iframe[name='editor-canvas']` ) + .contentDocument.compatMode + ); + + // CSS1Compat = expected standards mode. + // BackCompat = quirks mode. + expect( compatMode ).toBe( 'CSS1Compat' ); + } ); +} ); From 512fa24f37e87abdfa2a04b130aa775cf25f52e2 Mon Sep 17 00:00:00 2001 From: Tomasz Tunik Date: Fri, 25 Feb 2022 15:33:24 +0100 Subject: [PATCH 3/7] restart CI after flaky test failure From d953032ffbd5f3a488ec9a55afd2b6561f1d8231 Mon Sep 17 00:00:00 2001 From: Tomasz Tunik Date: Tue, 8 Mar 2022 08:57:20 +0100 Subject: [PATCH 4/7] Solve the issue with srcDoc rather than document.write --- .../src/components/iframe/index.js | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index db9905d0b8427..7a10531c4bd89 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -174,24 +174,19 @@ function Iframe( const setRef = useRefEffect( ( node ) => { function setDocumentIfReady() { const { contentDocument, ownerDocument } = node; - const { readyState } = contentDocument; - - if ( readyState !== 'interactive' && readyState !== 'complete' ) { + const { readyState, documentElement, compatMode } = contentDocument; + + // As srcDoc loads contents asynchronously this will cause the iframe to + // load documents twice. We need to hook react to the correct contentDocument + // so we need to skip the initial document and wait for the srcDoc with + // correct compatMode to load. + if ( + compatMode !== 'CSS1Compat' || + ( readyState !== 'complete' && readyState !== 'interactive' ) + ) { return false; } - // Iframe document doctype is not set by default and not having doctype - // will cause browser to render iframe contents in quirks mode. Appending - // doctype to existing document won't change iframe's rendering mode. - // Document.write will overwrite the document with a new one containing - // a correct doctype and will correctly render in stadards mode. - contentDocument.write( - '' + contentDocument.documentElement.outerHTML - ); - contentDocument.close(); - - const { documentElement } = contentDocument; - bubbleEvents( contentDocument ); setIframeDocument( contentDocument ); clearerRef( documentElement ); @@ -276,6 +271,8 @@ function Iframe( { ...props } ref={ useMergeRefs( [ ref, setRef ] ) } tabIndex={ tabIndex } + // Correct doctype is required to enable rendering in standards mode + srcDoc="yyyxxx" title={ __( 'Editor canvas' ) } > { iframeDocument && From 271485d78da05c096faab84a54872fa3c9ac6e1d Mon Sep 17 00:00:00 2001 From: Tomasz Tunik Date: Tue, 8 Mar 2022 10:58:49 +0100 Subject: [PATCH 5/7] Clean up the iframe srcDoc --- packages/block-editor/src/components/iframe/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index 7a10531c4bd89..9ad1f84cd9fd6 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -272,7 +272,7 @@ function Iframe( ref={ useMergeRefs( [ ref, setRef ] ) } tabIndex={ tabIndex } // Correct doctype is required to enable rendering in standards mode - srcDoc="yyyxxx" + srcDoc="" title={ __( 'Editor canvas' ) } > { iframeDocument && From 552ac833f7310d15fdec1bc6493cc3bcd7263872 Mon Sep 17 00:00:00 2001 From: Tomasz Tunik Date: Tue, 8 Mar 2022 17:50:03 +0100 Subject: [PATCH 6/7] Simplify the code based on findings --- .../src/components/iframe/index.js | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index 9ad1f84cd9fd6..83a9216ccd2f5 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -174,16 +174,9 @@ function Iframe( const setRef = useRefEffect( ( node ) => { function setDocumentIfReady() { const { contentDocument, ownerDocument } = node; - const { readyState, documentElement, compatMode } = contentDocument; - - // As srcDoc loads contents asynchronously this will cause the iframe to - // load documents twice. We need to hook react to the correct contentDocument - // so we need to skip the initial document and wait for the srcDoc with - // correct compatMode to load. - if ( - compatMode !== 'CSS1Compat' || - ( readyState !== 'complete' && readyState !== 'interactive' ) - ) { + const { readyState, documentElement } = contentDocument; + + if ( readyState !== 'complete' && readyState !== 'interactive' ) { return false; } @@ -210,11 +203,7 @@ function Iframe( return true; } - if ( setDocumentIfReady() ) { - return; - } - - // Document is not immediately loaded in Firefox. + // Document set with srcDoc is not immediately ready. node.addEventListener( 'load', () => { setDocumentIfReady(); } ); From 1473350e1ef29ccb03bf458ebaad0b8d2ec1275d Mon Sep 17 00:00:00 2001 From: Tomasz Tunik Date: Wed, 9 Mar 2022 11:19:43 +0100 Subject: [PATCH 7/7] cleanup + ensure we unbind the event --- packages/block-editor/src/components/iframe/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index 83a9216ccd2f5..acbfae766e3d4 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -176,7 +176,7 @@ function Iframe( const { contentDocument, ownerDocument } = node; const { readyState, documentElement } = contentDocument; - if ( readyState !== 'complete' && readyState !== 'interactive' ) { + if ( readyState !== 'interactive' && readyState !== 'complete' ) { return false; } @@ -204,9 +204,9 @@ function Iframe( } // Document set with srcDoc is not immediately ready. - node.addEventListener( 'load', () => { - setDocumentIfReady(); - } ); + node.addEventListener( 'load', setDocumentIfReady ); + + return () => node.removeEventListener( 'load', setDocumentIfReady ); }, [] ); const headRef = useRefEffect( ( element ) => { scripts