shell bypass 403
import { useState, useEffect, useRef, useCallback } from '@wordpress/element';
import { debounce } from 'lodash';
import { useGlobalsStore } from '@library/state/global';
import { requiredCSSVars } from '@library/util/css';
import { hasCSSVar } from '@library/util/dom';
import { useIsMounted } from './useIsMounted';
const originalHeights = new WeakMap();
export const usePreviewIframe = ({
container,
onIFrameLoaded,
ready,
loadDelay,
}) => {
const isMounted = useIsMounted();
const [waitForIframe, setWaitForIframe] = useState(0);
const [iFrame, setIFrame] = useState(null);
const [maybeOk, setMaybeOk] = useState(false);
const isUpdating = useRef(false);
const { addMissingCSSVar } = useGlobalsStore();
const addMissingThemeVars = useCallback(
(frame) => {
if (!frame?.contentDocument) return;
const styles = getComputedStyle(frame.contentDocument.documentElement);
const styleSheets = frame.contentDocument.styleSheets;
for (let key in requiredCSSVars) {
// CSS variable was found applied somewhere
if (styles.getPropertyValue(key)) continue;
const varUsed = Array.from(styleSheets)
.filter((sheet) => {
try {
return sheet.cssRules;
} catch (error) {
return false;
}
})
.some((sheet) => hasCSSVar(key, sheet.cssRules));
// CSS variable was found in a stylesheet somewhere
if (varUsed) continue;
// Add the missing variable to the global state to be imported later
addMissingCSSVar(key);
// Dive into iframe and add the variables to at the :root scope
frame.contentDocument.documentElement.style.setProperty(
key,
requiredCSSVars[key],
);
}
},
[addMissingCSSVar],
);
const updateCoverBlocks = useCallback(async (frame, cntnr) => {
const ft = frame.getBoundingClientRect().top;
const ct = cntnr.getBoundingClientRect().top;
// If they have scrolled, don't mess with it
if (ft < ct) return;
isUpdating.current = true;
let scale = cntnr
.querySelector('[style*="scale"]')
?.style?.transform?.match(/scale\((.*?)\)/)?.[1];
scale = scale ? parseFloat(scale) : null;
const cntnrHScaled = cntnr.offsetHeight / (scale ?? 1);
frame.style.setProperty('max-height', `${cntnrHScaled}px`, 'important');
const coverBlocks =
frame.contentDocument.querySelectorAll('.wp-block-cover');
for (const el of coverBlocks) {
if (!originalHeights.has(el)) {
// Cache the original 'vh' value
originalHeights.set(el, el.style.minHeight);
}
// Reapply the original 'vh' value so it can be used in computations
el.style.minHeight = originalHeights.get(el);
}
cntnr.offsetHeight; // Force a reflow
// Give the browser time to paint
await new Promise((resolve) => requestAnimationFrame(resolve));
await new Promise((resolve) => requestAnimationFrame(resolve));
for (const el of coverBlocks) {
if (!frame.contentDocument?.defaultView) return;
// Get the computed height in px and use it for your calculation
const computedHeight = parseFloat(
frame.contentDocument.defaultView.getComputedStyle(el).height,
);
el.offsetHeight; // Force a reflow
el.style.minHeight =
computedHeight > 500 ? '500px' : computedHeight + 'px';
}
frame.style.setProperty('max-height', 'none', 'important');
isUpdating.current = false;
}, []);
useEffect(() => {
if (!ready) return;
// continuously check for iframe
const interval = setTimeout(() => {
if (iFrame) return;
const frame = container?.querySelector('iframe[title]');
// If not found, retry by updating state
if (!frame) return setWaitForIframe((prev) => prev + 1);
setIFrame(frame);
requestAnimationFrame(() => onIFrameLoaded(frame, container));
}, 100);
return () => clearTimeout(interval);
}, [iFrame, ready, waitForIframe, container, onIFrameLoaded]);
useEffect(() => {
setMaybeOk(false);
// After the iFrame is found, wait for it to load
// Note: using load event is not reliable
if (!iFrame?.contentDocument) return;
const config = {
attributes: false,
childList: true,
subtree: true,
};
// Run once in case the iframe is already loaded
requestAnimationFrame(() => loaded(iFrame, container));
// Continuously check for changes
const loaded = debounce(async () => {
if (!isMounted.current || isUpdating.current) return;
m.disconnect();
await updateCoverBlocks(iFrame, container);
if (window.extSharedData.themeSlug !== 'extendable') {
await addMissingThemeVars(iFrame, container);
}
// Reconnect observer after making changes
setTimeout(() => setMaybeOk(true), loadDelay);
if (isMounted.current) m.observe(iFrame.contentDocument, config);
}, 300);
const m = new MutationObserver(loaded);
m.observe(iFrame.contentDocument, config);
return () => {
loaded.cancel();
m?.disconnect();
};
}, [
iFrame,
container,
isMounted,
ready,
updateCoverBlocks,
addMissingThemeVars,
loadDelay,
]);
return { loading: !iFrame, ready: maybeOk };
};