@@ -547,29 +547,30 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false
547547 * @return string Filtered block content.
548548 */
549549function wp_render_layout_support_flag ( $ block_content , $ block ) {
550- $ block_type = WP_Block_Type_Registry::get_instance ()->get_registered ( $ block ['blockName ' ] );
551- $ support_layout = block_has_support ( $ block_type , 'layout ' , false ) || block_has_support ( $ block_type , '__experimentalLayout ' , false );
552- $ has_child_layout = isset ( $ block ['attrs ' ]['style ' ]['layout ' ]['selfStretch ' ] ) ;
550+ $ block_type = WP_Block_Type_Registry::get_instance ()->get_registered ( $ block ['blockName ' ] );
551+ $ block_supports_layout = block_has_support ( $ block_type , 'layout ' , false ) || block_has_support ( $ block_type , '__experimentalLayout ' , false );
552+ $ layout_from_parent = $ block ['attrs ' ]['style ' ]['layout ' ]['selfStretch ' ] ?? null ;
553553
554- if ( ! $ support_layout && ! $ has_child_layout ) {
554+ if ( ! $ block_supports_layout && ! $ layout_from_parent ) {
555555 return $ block_content ;
556556 }
557+
557558 $ outer_class_names = array ();
558559
559- if ( $ has_child_layout && ( 'fixed ' === $ block [ ' attrs ' ][ ' style ' ][ ' layout ' ][ ' selfStretch ' ] || 'fill ' === $ block [ ' attrs ' ][ ' style ' ][ ' layout ' ][ ' selfStretch ' ] ) ) {
560+ if ( 'fixed ' === $ layout_from_parent || 'fill ' === $ layout_from_parent ) {
560561 $ container_content_class = wp_unique_id ( 'wp-container-content- ' );
561562
562563 $ child_layout_styles = array ();
563564
564- if ( 'fixed ' === $ block [ ' attrs ' ][ ' style ' ][ ' layout ' ][ ' selfStretch ' ] && isset ( $ block ['attrs ' ]['style ' ]['layout ' ]['flexSize ' ] ) ) {
565+ if ( 'fixed ' === $ layout_from_parent && isset ( $ block ['attrs ' ]['style ' ]['layout ' ]['flexSize ' ] ) ) {
565566 $ child_layout_styles [] = array (
566567 'selector ' => ". $ container_content_class " ,
567568 'declarations ' => array (
568569 'flex-basis ' => $ block ['attrs ' ]['style ' ]['layout ' ]['flexSize ' ],
569570 'box-sizing ' => 'border-box ' ,
570571 ),
571572 );
572- } elseif ( 'fill ' === $ block [ ' attrs ' ][ ' style ' ][ ' layout ' ][ ' selfStretch ' ] ) {
573+ } elseif ( 'fill ' === $ layout_from_parent ) {
573574 $ child_layout_styles [] = array (
574575 'selector ' => ". $ container_content_class " ,
575576 'declarations ' => array (
@@ -589,12 +590,25 @@ function wp_render_layout_support_flag( $block_content, $block ) {
589590 $ outer_class_names [] = $ container_content_class ;
590591 }
591592
592- // Return early if only child layout exists.
593- if ( ! $ support_layout && ! empty ( $ outer_class_names ) ) {
594- $ content = new WP_HTML_Tag_Processor ( $ block_content );
595- $ content ->next_tag ();
596- $ content ->add_class ( implode ( ' ' , $ outer_class_names ) );
597- return (string ) $ content ;
593+ // Prep the processor for modifying the block output.
594+ $ processor = new WP_HTML_Tag_Processor ( $ block_content );
595+
596+ // Having no tags implies there are no tags onto which to add class names.
597+ if ( ! $ processor ->next_tag () ) {
598+ return $ block_content ;
599+ }
600+
601+ /*
602+ * A block may not support layout but still be affected by a parent block's layout.
603+ *
604+ * In these cases add the appropriate class names and then return early; there's
605+ * no need to investigate on this block whether additional layout constraints apply.
606+ */
607+ if ( ! $ block_supports_layout && ! empty ( $ outer_class_names ) ) {
608+ foreach ( $ outer_class_names as $ class_name ) {
609+ $ processor ->add_class ( $ class_name );
610+ }
611+ return $ processor ->get_updated_html ();
598612 }
599613
600614 $ global_settings = wp_get_global_settings ();
@@ -604,7 +618,6 @@ function wp_render_layout_support_flag( $block_content, $block ) {
604618 $ class_names = array ();
605619 $ layout_definitions = wp_get_layout_definitions ();
606620 $ container_class = wp_unique_id ( 'wp-container- ' );
607- $ layout_classname = '' ;
608621
609622 // Set the correct layout type for blocks using legacy content width.
610623 if ( isset ( $ used_layout ['inherit ' ] ) && $ used_layout ['inherit ' ] || isset ( $ used_layout ['contentSize ' ] ) && $ used_layout ['contentSize ' ] ) {
@@ -702,49 +715,100 @@ function wp_render_layout_support_flag( $block_content, $block ) {
702715 $ block_name = explode ( '/ ' , $ block ['blockName ' ] );
703716 $ class_names [] = 'wp-block- ' . end ( $ block_name ) . '- ' . $ layout_classname ;
704717
705- $ content_with_outer_classnames = '' ;
706-
718+ // Add classes to the outermost HTML tag if necessary.
707719 if ( ! empty ( $ outer_class_names ) ) {
708- $ content_with_outer_classnames = new WP_HTML_Tag_Processor ( $ block_content );
709- $ content_with_outer_classnames ->next_tag ();
710720 foreach ( $ outer_class_names as $ outer_class_name ) {
711- $ content_with_outer_classnames ->add_class ( $ outer_class_name );
721+ $ processor ->add_class ( $ outer_class_name );
712722 }
713-
714- $ content_with_outer_classnames = (string ) $ content_with_outer_classnames ;
715723 }
716724
717725 /**
718- * The first chunk of innerContent contains the block markup up until the inner blocks start.
719- * This targets the opening tag of the inner blocks wrapper, which is the last tag in that chunk.
720- */
721- $ inner_content_classnames = '' ;
722-
723- if ( isset ( $ block ['innerContent ' ][0 ] ) && 'string ' === gettype ( $ block ['innerContent ' ][0 ] ) && count ( $ block ['innerContent ' ] ) > 1 ) {
724- $ tags = new WP_HTML_Tag_Processor ( $ block ['innerContent ' ][0 ] );
725- $ last_classnames = '' ;
726- while ( $ tags ->next_tag () ) {
727- $ last_classnames = $ tags ->get_attribute ( 'class ' );
726+ * Attempts to refer to the inner-block wrapping element by its class attribute.
727+ *
728+ * When examining a block's inner content, if a block has inner blocks, then
729+ * the first content item will likely be a text (HTML) chunk immediately
730+ * preceding the inner blocks. The last HTML tag in that chunk would then be
731+ * an opening tag for an element that wraps the inner blocks.
732+ *
733+ * There's no reliable way to associate this wrapper in $block_content because
734+ * it may have changed during the rendering pipeline (as inner contents is
735+ * provided before rendering) and through previous filters. In many cases,
736+ * however, the `class` attribute will be a good-enough identifier, so this
737+ * code finds the last tag in that chunk and stores the `class` attribute
738+ * so that it can be used later when working through the rendered block output
739+ * to identify the wrapping element and add the remaining class names to it.
740+ *
741+ * It's also possible that no inner block wrapper even exists. If that's the
742+ * case this code could apply the class names to an invalid element.
743+ *
744+ * Example:
745+ *
746+ * $block['innerBlocks'] = array( $list_item );
747+ * $block['innerContent'] = array( '<ul class="list-wrapper is-unordered">', null, '</ul>' );
748+ *
749+ * // After rendering, the initial contents may have been modified by other renderers or filters.
750+ * $block_content = <<<HTML
751+ * <figure>
752+ * <ul class="annotated-list list-wrapper is-unordered">
753+ * <li>Code</li>
754+ * </ul><figcaption>It's a list!</figcaption>
755+ * </figure>
756+ * HTML;
757+ *
758+ * Although it is possible that the original block-wrapper classes are changed in $block_content
759+ * from how they appear in $block['innerContent'], it's likely that the original class attributes
760+ * are still present in the wrapper as they are in this example. Frequently, additional classes
761+ * will also be present; rarely should classes be removed.
762+ *
763+ * @TODO: Find a better way to match the first inner block. If it's possible to identify where the
764+ * first inner block starts, then it will be possible to find the last tag before it starts
765+ * and then that tag, if an opening tag, can be solidly identified as a wrapping element.
766+ * Can some unique value or class or ID be added to the inner blocks when they process
767+ * so that they can be extracted here safely without guessing? Can the block rendering function
768+ * return information about where the rendered inner blocks start?
769+ *
770+ * @var string|null
771+ */
772+ $ inner_block_wrapper_classes = null ;
773+ $ first_chunk = $ block ['innerContent ' ][0 ] ?? null ;
774+ if ( is_string ( $ first_chunk ) && count ( $ block ['innerContent ' ] ) > 1 ) {
775+ $ first_chunk_processor = new WP_HTML_Tag_Processor ( $ first_chunk );
776+ while ( $ first_chunk_processor ->next_tag () ) {
777+ $ class_attribute = $ first_chunk_processor ->get_attribute ( 'class ' );
778+ if ( is_string ( $ class_attribute ) && ! empty ( $ class_attribute ) ) {
779+ $ inner_block_wrapper_classes = $ class_attribute ;
780+ }
728781 }
729-
730- $ inner_content_classnames = (string ) $ last_classnames ;
731782 }
732783
733- $ content = $ content_with_outer_classnames ? new WP_HTML_Tag_Processor ( $ content_with_outer_classnames ) : new WP_HTML_Tag_Processor ( $ block_content );
734-
735- if ( $ inner_content_classnames ) {
736- $ content ->next_tag ( array ( 'class_name ' => $ inner_content_classnames ) );
737- foreach ( $ class_names as $ class_name ) {
738- $ content ->add_class ( $ class_name );
784+ /*
785+ * If necessary, advance to what is likely to be an inner block wrapper tag.
786+ *
787+ * This advances until it finds the first tag containing the original class
788+ * attribute from above. If none is found it will scan to the end of the block
789+ * and fail to add any class names.
790+ *
791+ * If there is no block wrapper it won't advance at all, in which case the
792+ * class names will be added to the first and outermost tag of the block.
793+ * For cases where this outermost tag is the only tag surrounding inner
794+ * blocks then the outer wrapper and inner wrapper are the same.
795+ */
796+ do {
797+ if ( ! $ inner_block_wrapper_classes ) {
798+ break ;
739799 }
740- } else {
741- $ content ->next_tag ();
742- foreach ( $ class_names as $ class_name ) {
743- $ content ->add_class ( $ class_name );
800+
801+ if ( false !== strpos ( $ processor ->get_attribute ( 'class ' ), $ inner_block_wrapper_classes ) ) {
802+ break ;
744803 }
804+ } while ( $ processor ->next_tag () );
805+
806+ // Add the remaining class names.
807+ foreach ( $ class_names as $ class_name ) {
808+ $ processor ->add_class ( $ class_name );
745809 }
746810
747- return ( string ) $ content ;
811+ return $ processor -> get_updated_html () ;
748812}
749813
750814// Register the block support.
0 commit comments