Skip to content

Commit 33b6e64

Browse files
authored
Iframe: calc compat styles once per page load (#57798)
1 parent 45eb9a5 commit 33b6e64

File tree

3 files changed

+122
-125
lines changed

3 files changed

+122
-125
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
let compatibilityStyles = null;
2+
3+
/**
4+
* Returns a list of stylesheets that target the editor canvas. A stylesheet is
5+
* considered targetting the editor a canvas if it contains the
6+
* `editor-styles-wrapper`, `wp-block`, or `wp-block-*` class selectors.
7+
*
8+
* Ideally, this hook should be removed in the future and styles should be added
9+
* explicitly as editor styles.
10+
*/
11+
export function getCompatibilityStyles() {
12+
if ( compatibilityStyles ) {
13+
return compatibilityStyles;
14+
}
15+
16+
// Only memoize the result once on load, since these stylesheets should not
17+
// change.
18+
compatibilityStyles = Array.from( document.styleSheets ).reduce(
19+
( accumulator, styleSheet ) => {
20+
try {
21+
// May fail for external styles.
22+
// eslint-disable-next-line no-unused-expressions
23+
styleSheet.cssRules;
24+
} catch ( e ) {
25+
return accumulator;
26+
}
27+
28+
const { ownerNode, cssRules } = styleSheet;
29+
30+
// Stylesheet is added by another stylesheet. See
31+
// https://developer.mozilla.org/en-US/docs/Web/API/StyleSheet/ownerNode#notes.
32+
if ( ownerNode === null ) {
33+
return accumulator;
34+
}
35+
36+
if ( ! cssRules ) {
37+
return accumulator;
38+
}
39+
40+
// Don't try to add the reset styles, which were removed as a dependency
41+
// from `edit-blocks` for the iframe since we don't need to reset admin
42+
// styles.
43+
if ( ownerNode.id === 'wp-reset-editor-styles-css' ) {
44+
return accumulator;
45+
}
46+
47+
// Don't try to add styles without ID. Styles enqueued via the WP dependency system will always have IDs.
48+
if ( ! ownerNode.id ) {
49+
return accumulator;
50+
}
51+
52+
function matchFromRules( _cssRules ) {
53+
return Array.from( _cssRules ).find(
54+
( {
55+
selectorText,
56+
conditionText,
57+
cssRules: __cssRules,
58+
} ) => {
59+
// If the rule is conditional then it will not have selector text.
60+
// Recurse into child CSS ruleset to determine selector eligibility.
61+
if ( conditionText ) {
62+
return matchFromRules( __cssRules );
63+
}
64+
65+
return (
66+
selectorText &&
67+
( selectorText.includes(
68+
'.editor-styles-wrapper'
69+
) ||
70+
selectorText.includes( '.wp-block' ) )
71+
);
72+
}
73+
);
74+
}
75+
76+
if ( matchFromRules( cssRules ) ) {
77+
const isInline = ownerNode.tagName === 'STYLE';
78+
79+
if ( isInline ) {
80+
// If the current target is inline,
81+
// it could be a dependency of an existing stylesheet.
82+
// Look for that dependency and add it BEFORE the current target.
83+
const mainStylesCssId = ownerNode.id.replace(
84+
'-inline-css',
85+
'-css'
86+
);
87+
const mainStylesElement =
88+
document.getElementById( mainStylesCssId );
89+
if ( mainStylesElement ) {
90+
accumulator.push( mainStylesElement.cloneNode( true ) );
91+
}
92+
}
93+
94+
accumulator.push( ownerNode.cloneNode( true ) );
95+
96+
if ( ! isInline ) {
97+
// If the current target is not inline,
98+
// we still look for inline styles that could be relevant for the current target.
99+
// If they exist, add them AFTER the current target.
100+
const inlineStylesCssId = ownerNode.id.replace(
101+
'-css',
102+
'-inline-css'
103+
);
104+
const inlineStylesElement =
105+
document.getElementById( inlineStylesCssId );
106+
if ( inlineStylesElement ) {
107+
accumulator.push(
108+
inlineStylesElement.cloneNode( true )
109+
);
110+
}
111+
}
112+
}
113+
114+
return accumulator;
115+
},
116+
[]
117+
);
118+
119+
return compatibilityStyles;
120+
}

packages/block-editor/src/components/iframe/index.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { useSelect } from '@wordpress/data';
2828
*/
2929
import { useBlockSelectionClearer } from '../block-selection-clearer';
3030
import { useWritingFlow } from '../writing-flow';
31-
import { useCompatibilityStyles } from './use-compatibility-styles';
31+
import { getCompatibilityStyles } from './get-compatibility-styles';
3232
import { store as blockEditorStore } from '../../store';
3333

3434
function bubbleEvent( event, Constructor, frame ) {
@@ -121,7 +121,6 @@ function Iframe( {
121121
const { styles = '', scripts = '' } = resolvedAssets;
122122
const [ iframeDocument, setIframeDocument ] = useState();
123123
const [ bodyClasses, setBodyClasses ] = useState( [] );
124-
const compatStyles = useCompatibilityStyles();
125124
const clearerRef = useBlockSelectionClearer();
126125
const [ before, writingFlowRef, after ] = useWritingFlow();
127126
const [ contentResizeListener, { height: contentHeight } ] =
@@ -156,7 +155,7 @@ function Iframe( {
156155

157156
contentDocument.dir = ownerDocument.dir;
158157

159-
for ( const compatStyle of compatStyles ) {
158+
for ( const compatStyle of getCompatibilityStyles() ) {
160159
if ( contentDocument.getElementById( compatStyle.id ) ) {
161160
continue;
162161
}

packages/block-editor/src/components/iframe/use-compatibility-styles.js

Lines changed: 0 additions & 122 deletions
This file was deleted.

0 commit comments

Comments
 (0)