diff --git a/packages/block-editor/src/components/iframe/get-compatibility-styles.js b/packages/block-editor/src/components/iframe/get-compatibility-styles.js new file mode 100644 index 00000000000000..ab80e0e8eb43ea --- /dev/null +++ b/packages/block-editor/src/components/iframe/get-compatibility-styles.js @@ -0,0 +1,120 @@ +let compatibilityStyles = null; + +/** + * Returns a list of stylesheets that target the editor canvas. A stylesheet is + * considered targetting the editor a canvas if it contains the + * `editor-styles-wrapper`, `wp-block`, or `wp-block-*` class selectors. + * + * Ideally, this hook should be removed in the future and styles should be added + * explicitly as editor styles. + */ +export function getCompatibilityStyles() { + if ( compatibilityStyles ) { + return compatibilityStyles; + } + + // Only memoize the result once on load, since these stylesheets should not + // change. + compatibilityStyles = Array.from( document.styleSheets ).reduce( + ( accumulator, styleSheet ) => { + try { + // May fail for external styles. + // eslint-disable-next-line no-unused-expressions + styleSheet.cssRules; + } catch ( e ) { + return accumulator; + } + + const { ownerNode, cssRules } = styleSheet; + + // Stylesheet is added by another stylesheet. See + // https://developer.mozilla.org/en-US/docs/Web/API/StyleSheet/ownerNode#notes. + if ( ownerNode === null ) { + return accumulator; + } + + if ( ! cssRules ) { + return accumulator; + } + + // Don't try to add the reset styles, which were removed as a dependency + // from `edit-blocks` for the iframe since we don't need to reset admin + // styles. + if ( ownerNode.id === 'wp-reset-editor-styles-css' ) { + return accumulator; + } + + // Don't try to add styles without ID. Styles enqueued via the WP dependency system will always have IDs. + if ( ! ownerNode.id ) { + return accumulator; + } + + function matchFromRules( _cssRules ) { + return Array.from( _cssRules ).find( + ( { + selectorText, + conditionText, + cssRules: __cssRules, + } ) => { + // If the rule is conditional then it will not have selector text. + // Recurse into child CSS ruleset to determine selector eligibility. + if ( conditionText ) { + return matchFromRules( __cssRules ); + } + + return ( + selectorText && + ( selectorText.includes( + '.editor-styles-wrapper' + ) || + selectorText.includes( '.wp-block' ) ) + ); + } + ); + } + + if ( matchFromRules( cssRules ) ) { + const isInline = ownerNode.tagName === 'STYLE'; + + if ( isInline ) { + // If the current target is inline, + // it could be a dependency of an existing stylesheet. + // Look for that dependency and add it BEFORE the current target. + const mainStylesCssId = ownerNode.id.replace( + '-inline-css', + '-css' + ); + const mainStylesElement = + document.getElementById( mainStylesCssId ); + if ( mainStylesElement ) { + accumulator.push( mainStylesElement.cloneNode( true ) ); + } + } + + accumulator.push( ownerNode.cloneNode( true ) ); + + if ( ! isInline ) { + // If the current target is not inline, + // we still look for inline styles that could be relevant for the current target. + // If they exist, add them AFTER the current target. + const inlineStylesCssId = ownerNode.id.replace( + '-css', + '-inline-css' + ); + const inlineStylesElement = + document.getElementById( inlineStylesCssId ); + if ( inlineStylesElement ) { + accumulator.push( + inlineStylesElement.cloneNode( true ) + ); + } + } + } + + return accumulator; + }, + [] + ); + + return compatibilityStyles; +} diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index 1939f75811c8c5..de482c5f059dc8 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -28,7 +28,7 @@ import { useSelect } from '@wordpress/data'; */ import { useBlockSelectionClearer } from '../block-selection-clearer'; import { useWritingFlow } from '../writing-flow'; -import { useCompatibilityStyles } from './use-compatibility-styles'; +import { getCompatibilityStyles } from './get-compatibility-styles'; import { store as blockEditorStore } from '../../store'; function bubbleEvent( event, Constructor, frame ) { @@ -121,7 +121,6 @@ function Iframe( { const { styles = '', scripts = '' } = resolvedAssets; const [ iframeDocument, setIframeDocument ] = useState(); const [ bodyClasses, setBodyClasses ] = useState( [] ); - const compatStyles = useCompatibilityStyles(); const clearerRef = useBlockSelectionClearer(); const [ before, writingFlowRef, after ] = useWritingFlow(); const [ contentResizeListener, { height: contentHeight } ] = @@ -156,7 +155,7 @@ function Iframe( { contentDocument.dir = ownerDocument.dir; - for ( const compatStyle of compatStyles ) { + for ( const compatStyle of getCompatibilityStyles() ) { if ( contentDocument.getElementById( compatStyle.id ) ) { continue; } diff --git a/packages/block-editor/src/components/iframe/use-compatibility-styles.js b/packages/block-editor/src/components/iframe/use-compatibility-styles.js deleted file mode 100644 index eb738c7ebefdfe..00000000000000 --- a/packages/block-editor/src/components/iframe/use-compatibility-styles.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * WordPress dependencies - */ -import { useMemo } from '@wordpress/element'; - -/** - * Returns a list of stylesheets that target the editor canvas. A stylesheet is - * considered targetting the editor a canvas if it contains the - * `editor-styles-wrapper`, `wp-block`, or `wp-block-*` class selectors. - * - * Ideally, this hook should be removed in the future and styles should be added - * explicitly as editor styles. - */ -export function useCompatibilityStyles() { - // Only memoize the result once on load, since these stylesheets should not - // change. - return useMemo( () => { - // Search the document for stylesheets targetting the editor canvas. - return Array.from( document.styleSheets ).reduce( - ( accumulator, styleSheet ) => { - try { - // May fail for external styles. - // eslint-disable-next-line no-unused-expressions - styleSheet.cssRules; - } catch ( e ) { - return accumulator; - } - - const { ownerNode, cssRules } = styleSheet; - - // Stylesheet is added by another stylesheet. See - // https://developer.mozilla.org/en-US/docs/Web/API/StyleSheet/ownerNode#notes. - if ( ownerNode === null ) { - return accumulator; - } - - if ( ! cssRules ) { - return accumulator; - } - - // Don't try to add the reset styles, which were removed as a dependency - // from `edit-blocks` for the iframe since we don't need to reset admin - // styles. - if ( ownerNode.id === 'wp-reset-editor-styles-css' ) { - return accumulator; - } - - // Don't try to add styles without ID. Styles enqueued via the WP dependency system will always have IDs. - if ( ! ownerNode.id ) { - return accumulator; - } - - function matchFromRules( _cssRules ) { - return Array.from( _cssRules ).find( - ( { - selectorText, - conditionText, - cssRules: __cssRules, - } ) => { - // If the rule is conditional then it will not have selector text. - // Recurse into child CSS ruleset to determine selector eligibility. - if ( conditionText ) { - return matchFromRules( __cssRules ); - } - - return ( - selectorText && - ( selectorText.includes( - '.editor-styles-wrapper' - ) || - selectorText.includes( '.wp-block' ) ) - ); - } - ); - } - - if ( matchFromRules( cssRules ) ) { - const isInline = ownerNode.tagName === 'STYLE'; - - if ( isInline ) { - // If the current target is inline, - // it could be a dependency of an existing stylesheet. - // Look for that dependency and add it BEFORE the current target. - const mainStylesCssId = ownerNode.id.replace( - '-inline-css', - '-css' - ); - const mainStylesElement = - document.getElementById( mainStylesCssId ); - if ( mainStylesElement ) { - accumulator.push( - mainStylesElement.cloneNode( true ) - ); - } - } - - accumulator.push( ownerNode.cloneNode( true ) ); - - if ( ! isInline ) { - // If the current target is not inline, - // we still look for inline styles that could be relevant for the current target. - // If they exist, add them AFTER the current target. - const inlineStylesCssId = ownerNode.id.replace( - '-css', - '-inline-css' - ); - const inlineStylesElement = - document.getElementById( inlineStylesCssId ); - if ( inlineStylesElement ) { - accumulator.push( - inlineStylesElement.cloneNode( true ) - ); - } - } - } - - return accumulator; - }, - [] - ); - }, [] ); -}