Skip to content

Commit

Permalink
FSE: load content in iframe (#25775)
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix authored Jan 11, 2021
1 parent 8a26233 commit e54a9ec
Show file tree
Hide file tree
Showing 23 changed files with 535 additions and 128 deletions.
37 changes: 37 additions & 0 deletions lib/client-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -669,3 +669,40 @@ function gutenberg_extend_block_editor_settings_with_fse_theme_flag( $settings )
return $settings;
}
add_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_settings_with_fse_theme_flag' );

/**
* Sets the editor styles to be consumed by JS.
*/
function gutenberg_extend_block_editor_styles_html() {
$handles = array(
'wp-block-editor',
'wp-block-library',
'wp-edit-blocks',
);

$block_registry = WP_Block_Type_Registry::get_instance();

foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
if ( ! empty( $block_type->style ) ) {
$handles[] = $block_type->style;
}

if ( ! empty( $block_type->editor_style ) ) {
$handles[] = $block_type->editor_style;
}
}

$handles = array_unique( $handles );
$done = wp_styles()->done;

ob_start();

wp_styles()->done = array();
wp_styles()->do_items( $handles );
wp_styles()->done = $done;

$editor_styles = wp_json_encode( array( 'html' => ob_get_clean() ) );

echo "<script>window.__editorStyles = $editor_styles</script>";
}
add_action( 'admin_footer-toplevel_page_gutenberg-edit-site', 'gutenberg_extend_block_editor_styles_html' );
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"lodash": "^4.17.19",
"memize": "^1.1.0",
"react-autosize-textarea": "^7.1.0",
"react-merge-refs": "^1.0.0",
"react-spring": "^8.0.19",
"reakit": "1.3.4",
"redux-multi": "^0.1.12",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,13 @@ function BlockPopover( {
: 'top right left';
const stickyBoundaryElement = showEmptyBlockSideInserter
? undefined
: getScrollContainer( node ) || ownerDocument.body;
: // The sticky boundary element should be the boundary at which the
// the block toolbar becomes sticky when the block scolls out of view.
// In case of an iframe, this should be the iframe boundary, otherwise
// the scroll container.
ownerDocument.defaultView.frameElement ||
getScrollContainer( node ) ||
ownerDocument.body;

return (
<Popover
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ function BlockSelectionButton( { clientId, rootClientId, blockElement } ) {

if ( navigateDown ) {
nextTabbable = focus.tabbable.findNext( blockElement );

if ( ! nextTabbable ) {
nextTabbable =
blockElement.ownerDocument.defaultView.frameElement;
nextTabbable = focus.tabbable.findNext( nextTabbable );
}
} else {
nextTabbable = focus.tabbable.findPrevious( blockElement );
}
Expand Down
189 changes: 189 additions & 0 deletions packages/block-editor/src/components/iframe/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/**
* External dependencies
*/
import mergeRefs from 'react-merge-refs';

/**
* WordPress dependencies
*/
import {
useState,
createPortal,
useCallback,
forwardRef,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';

const BODY_CLASS_NAME = 'editor-styles-wrapper';
const BLOCK_PREFIX = 'wp-block';

/**
* Clones stylesheets targetting the editor canvas to the given document. 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.
*
* @param {Document} doc The document to append cloned stylesheets to.
*/
function styleSheetsCompat( doc ) {
// Search the document for stylesheets targetting the editor canvas.
Array.from( document.styleSheets ).forEach( ( styleSheet ) => {
try {
// May fail for external styles.
// eslint-disable-next-line no-unused-expressions
styleSheet.cssRules;
} catch ( e ) {
return;
}

const { ownerNode, cssRules } = styleSheet;

if ( ! cssRules ) {
return;
}

const isMatch = Array.from( cssRules ).find(
( { selectorText } ) =>
selectorText &&
( selectorText.includes( `.${ BODY_CLASS_NAME }` ) ||
selectorText.includes( `.${ BLOCK_PREFIX }` ) )
);

if ( isMatch && ! doc.getElementById( ownerNode.id ) ) {
doc.head.appendChild( ownerNode.cloneNode( true ) );
}
} );
}

/**
* Bubbles some event types (keydown, keypress, and dragover) to parent document
* document to ensure that the keyboard shortcuts and drag and drop work.
*
* Ideally, we should remove event bubbling in the future. Keyboard shortcuts
* should be context dependent, e.g. actions on blocks like Cmd+A should not
* work globally outside the block editor.
*
* @param {Document} doc Document to attach listeners to.
*/
function bubbleEvents( doc ) {
const { defaultView } = doc;
const { frameElement } = defaultView;

function bubbleEvent( event ) {
const prototype = Object.getPrototypeOf( event );
const constructorName = prototype.constructor.name;
const Constructor = window[ constructorName ];

const init = {};

for ( const key in event ) {
init[ key ] = event[ key ];
}

if ( event instanceof defaultView.MouseEvent ) {
const rect = frameElement.getBoundingClientRect();
init.clientX += rect.left;
init.clientY += rect.top;
}

const newEvent = new Constructor( event.type, init );
const cancelled = ! frameElement.dispatchEvent( newEvent );

if ( cancelled ) {
event.preventDefault();
}
}

const eventTypes = [ 'keydown', 'keypress', 'dragover' ];

for ( const name of eventTypes ) {
doc.addEventListener( name, bubbleEvent );
}
}

/**
* Sets the document direction.
*
* Sets the `editor-styles-wrapper` class name on the body.
*
* Copies the `admin-color-*` class name to the body so that the admin color
* scheme applies to components in the iframe.
*
* @param {Document} doc Document to add class name to.
*/
function setBodyClassName( doc ) {
doc.dir = document.dir;
doc.body.className = BODY_CLASS_NAME;

for ( const name of document.body.classList ) {
if ( name.startsWith( 'admin-color-' ) ) {
doc.body.classList.add( name );
}
}
}

/**
* Sets the document head and default styles.
*
* @param {Document} doc Document to set the head for.
* @param {string} head HTML to set as the head.
*/
function setHead( doc, head ) {
doc.head.innerHTML =
// Body margin must be overridable by themes.
'<style>body{margin:0}</style>' + head;
}

function Iframe( { contentRef, children, head, ...props }, ref ) {
const [ iframeDocument, setIframeDocument ] = useState();

const setRef = useCallback( ( node ) => {
if ( ! node ) {
return;
}

function setDocumentIfReady() {
const { contentDocument } = node;
const { readyState } = contentDocument;

if ( readyState !== 'interactive' && readyState !== 'complete' ) {
return false;
}

setIframeDocument( contentDocument );
setHead( contentDocument, head );
setBodyClassName( contentDocument );
styleSheetsCompat( contentDocument );
bubbleEvents( contentDocument );
setBodyClassName( contentDocument );
contentRef.current = contentDocument.body;

return true;
}

if ( setDocumentIfReady() ) {
return;
}

// Document is not immediately loaded in Firefox.
node.addEventListener( 'load', () => {
setDocumentIfReady();
} );
}, [] );

return (
<iframe
{ ...props }
ref={ useCallback( mergeRefs( [ ref, setRef ] ), [] ) }
tabIndex="0"
title={ __( 'Editor canvas' ) }
name="editor-canvas"
>
{ iframeDocument && createPortal( children, iframeDocument.body ) }
</iframe>
);
}

export default forwardRef( Iframe );
2 changes: 2 additions & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export { default as NavigableToolbar } from './navigable-toolbar';
export {
default as ObserveTyping,
useTypingObserver as __unstableUseTypingObserver,
useMouseMoveTypingReset as __unstableUseMouseMoveTypingReset,
} from './observe-typing';
export { default as PreserveScrollInReorder } from './preserve-scroll-in-reorder';
export { default as SkipToSelectedBlock } from './skip-to-selected-block';
Expand All @@ -119,6 +120,7 @@ export { default as Warning } from './warning';
export { default as WritingFlow } from './writing-flow';
export { useCanvasClickRedirect as __unstableUseCanvasClickRedirect } from './use-canvas-click-redirect';
export { default as useBlockDisplayInformation } from './use-block-display-information';
export { default as __unstableIframe } from './iframe';

/*
* State Related Components
Expand Down
Loading

0 comments on commit e54a9ec

Please sign in to comment.