From af87cc0677e3891f4887e9ad42e9a89fce783657 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Thu, 4 Nov 2021 15:29:57 +1300
Subject: [PATCH 01/30] Update experimental flag to block sites with
 use_BalanceTags option enabled and aren't on WP 5.9 or higher

---
 lib/experiments-page.php                      |    2 +-
 .../block-library/src/gallery/deprecated.js   | 1402 ++++++++++-------
 2 files changed, 845 insertions(+), 559 deletions(-)

diff --git a/lib/experiments-page.php b/lib/experiments-page.php
index bd4ca968073f8..75d4b529ee225 100644
--- a/lib/experiments-page.php
+++ b/lib/experiments-page.php
@@ -101,7 +101,7 @@ function gutenberg_experiments_editor_settings( $settings ) {
 	// This bypass needs to remain in place until this is resolved and a patch released.
 	// https://core.trac.wordpress.org/ticket/54130.
 	$experiments_settings = array(
-		'__unstableGalleryWithImageBlocks' => (int) get_option( 'use_balanceTags' ) !== 1,
+		'__unstableGalleryWithImageBlocks' => (int) get_option( 'use_balanceTags' ) !== 1 || is_wp_version_compatible( '5.9' ),
 	);
 	return array_merge( $settings, $experiments_settings );
 }
diff --git a/packages/block-library/src/gallery/deprecated.js b/packages/block-library/src/gallery/deprecated.js
index bc89ebbcc9ab1..487e259faf964 100644
--- a/packages/block-library/src/gallery/deprecated.js
+++ b/packages/block-library/src/gallery/deprecated.js
@@ -7,7 +7,25 @@ import { map, some } from 'lodash';
 /**
  * WordPress dependencies
  */
-import { RichText } from '@wordpress/block-editor';
+import {
+	RichText,
+	store as blockEditorStore,
+	useBlockProps,
+} from '@wordpress/block-editor';
+import { select } from '@wordpress/data';
+import { createBlock } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import {
+	LINK_DESTINATION_ATTACHMENT,
+	LINK_DESTINATION_MEDIA,
+	LINK_DESTINATION_NONE,
+} from './constants';
+
+const DEPRECATED_LINK_DESTINATION_MEDIA = 'file';
+const DEPRECATED_LINK_DESTINATION_ATTACHMENT = 'post';
 
 /**
  * Original function to determine default number of columns from a block's
@@ -22,419 +40,199 @@ export function defaultColumnsNumberV1( attributes ) {
 	return Math.min( 3, attributes.images.length );
 }
 
-const deprecated = [
-	{
-		attributes: {
-			images: {
-				type: 'array',
-				default: [],
-				source: 'query',
-				selector: '.blocks-gallery-item',
-				query: {
-					url: {
-						type: 'string',
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'src',
-					},
-					fullUrl: {
-						type: 'string',
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-full-url',
-					},
-					link: {
-						type: 'string',
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-link',
-					},
-					alt: {
-						type: 'string',
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'alt',
-						default: '',
-					},
-					id: {
-						type: 'string',
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-id',
-					},
-					caption: {
-						type: 'string',
-						source: 'html',
-						selector: '.blocks-gallery-item__caption',
-					},
-				},
-			},
-			ids: {
-				type: 'array',
-				items: {
-					type: 'number',
-				},
-				default: [],
-			},
-			columns: {
-				type: 'number',
-				minimum: 1,
-				maximum: 8,
-			},
-			caption: {
-				type: 'string',
-				source: 'html',
-				selector: '.blocks-gallery-caption',
-			},
-			imageCrop: {
-				type: 'boolean',
-				default: true,
-			},
-			linkTo: {
-				type: 'string',
-				default: 'none',
-			},
-			sizeSlug: {
-				type: 'string',
-				default: 'large',
-			},
-		},
-		supports: {
-			align: true,
-		},
-		isEligible( { linkTo } ) {
-			return ! linkTo || linkTo === 'attachment' || linkTo === 'media';
-		},
-		migrate( attributes ) {
-			let linkTo = attributes.linkTo;
-			if ( ! attributes.linkTo ) {
-				linkTo = 'none';
-			} else if ( attributes.linkTo === 'attachment' ) {
-				linkTo = 'post';
-			} else if ( attributes.linkTo === 'media' ) {
-				linkTo = 'file';
-			}
+/**
+ * Original function to determine new href and linkDestination values for an image block from the
+ * supplied Gallery link destination.
+ *
+ * Used in deprecations: v1-6.
+ *
+ * @param {Object} image       Gallery image.
+ * @param {string} destination Gallery's selected link destination.
+ * @return {Object}            New attributes to assign to image block.
+ */
+export function getHrefAndDestination( image, destination ) {
+	// Need to determine the URL that the selected destination maps to.
+	// Gutenberg and WordPress use different constants so the new link
+	// destination also needs to be tweaked.
+	switch ( destination ) {
+		case DEPRECATED_LINK_DESTINATION_MEDIA:
 			return {
-				...attributes,
-				linkTo,
+				href: image?.source_url || image?.url, // eslint-disable-line camelcase
+				linkDestination: LINK_DESTINATION_MEDIA,
 			};
-		},
-		save( { attributes } ) {
-			const {
-				images,
-				columns = defaultColumnsNumberV1( attributes ),
-				imageCrop,
-				caption,
-				linkTo,
-			} = attributes;
-
-			return (
-				<figure
-					className={ `columns-${ columns } ${
-						imageCrop ? 'is-cropped' : ''
-					}` }
-				>
-					<ul className="blocks-gallery-grid">
-						{ images.map( ( image ) => {
-							let href;
-
-							switch ( linkTo ) {
-								case 'media':
-									href = image.fullUrl || image.url;
-									break;
-								case 'attachment':
-									href = image.link;
-									break;
-							}
+		case DEPRECATED_LINK_DESTINATION_ATTACHMENT:
+			return {
+				href: image?.link,
+				linkDestination: LINK_DESTINATION_ATTACHMENT,
+			};
+		case LINK_DESTINATION_MEDIA:
+			return {
+				href: image?.source_url || image?.url, // eslint-disable-line camelcase
+				linkDestination: LINK_DESTINATION_MEDIA,
+			};
+		case LINK_DESTINATION_ATTACHMENT:
+			return {
+				href: image?.link,
+				linkDestination: LINK_DESTINATION_ATTACHMENT,
+			};
+		case LINK_DESTINATION_NONE:
+			return {
+				href: undefined,
+				linkDestination: LINK_DESTINATION_NONE,
+			};
+	}
 
-							const img = (
-								<img
-									src={ image.url }
-									alt={ image.alt }
-									data-id={ image.id }
-									data-full-url={ image.fullUrl }
-									data-link={ image.link }
-									className={
-										image.id
-											? `wp-image-${ image.id }`
-											: null
-									}
-								/>
-							);
-
-							return (
-								<li
-									key={ image.id || image.url }
-									className="blocks-gallery-item"
-								>
-									<figure>
-										{ href ? (
-											<a href={ href }>{ img }</a>
-										) : (
-											img
-										) }
-										{ ! RichText.isEmpty(
-											image.caption
-										) && (
-											<RichText.Content
-												tagName="figcaption"
-												className="blocks-gallery-item__caption"
-												value={ image.caption }
-											/>
-										) }
-									</figure>
-								</li>
-							);
-						} ) }
-					</ul>
-					{ ! RichText.isEmpty( caption ) && (
-						<RichText.Content
-							tagName="figcaption"
-							className="blocks-gallery-caption"
-							value={ caption }
-						/>
-					) }
-				</figure>
-			);
+	return {};
+}
+
+function runV2Migration( attributes ) {
+	let linkTo = attributes.linkTo ? attributes.linkTo : 'none';
+
+	if ( linkTo === 'post' ) {
+		linkTo = 'attachment';
+	} else if ( linkTo === 'file' ) {
+		linkTo = 'media';
+	}
+
+	const imageBlocks = attributes.images.map( ( image ) => {
+		return getImageBlock( image, attributes.sizeSlug, linkTo );
+	} );
+
+	return [
+		{
+			caption: attributes.caption,
+			columns: attributes.columns,
+			imageCrop: attributes.imageCrop,
+			linkTo,
+			sizeSlug: attributes.sizeSlug,
+			allowResize: false,
 		},
-	},
-	{
-		attributes: {
-			images: {
-				type: 'array',
-				default: [],
-				source: 'query',
-				selector: '.blocks-gallery-item',
-				query: {
-					url: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'src',
-					},
-					fullUrl: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-full-url',
-					},
-					link: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-link',
-					},
-					alt: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'alt',
-						default: '',
-					},
-					id: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-id',
-					},
-					caption: {
-						type: 'string',
-						source: 'html',
-						selector: '.blocks-gallery-item__caption',
-					},
+		imageBlocks,
+	];
+}
+/**
+ * Gets an Image block from gallery image data
+ *
+ * Used to migrate Galleries to nested Image InnerBlocks.
+ *
+ * @param {Object} image    Image properties.
+ * @param {string} sizeSlug Gallery sizeSlug attribute.
+ * @param {string} linkTo   Gallery linkTo attribute.
+ * @return {Object}         Image block.
+ */
+export function getImageBlock( image, sizeSlug, linkTo ) {
+	return createBlock( 'core/image', {
+		...( image.id && { id: parseInt( image.id ) } ),
+		url: image.url,
+		alt: image.alt,
+		caption: image.caption,
+		sizeSlug,
+		...getHrefAndDestination( image, linkTo ),
+	} );
+}
+
+const v6 = {
+	attributes: {
+		images: {
+			type: 'array',
+			default: [],
+			source: 'query',
+			selector: '.blocks-gallery-item',
+			query: {
+				url: {
+					type: 'string',
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'src',
+				},
+				fullUrl: {
+					type: 'string',
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-full-url',
+				},
+				link: {
+					type: 'string',
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-link',
+				},
+				alt: {
+					type: 'string',
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'alt',
+					default: '',
+				},
+				id: {
+					type: 'string',
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-id',
+				},
+				caption: {
+					type: 'string',
+					source: 'html',
+					selector: '.blocks-gallery-item__caption',
 				},
 			},
-			ids: {
-				type: 'array',
-				default: [],
-			},
-			columns: {
+		},
+		ids: {
+			type: 'array',
+			items: {
 				type: 'number',
 			},
-			caption: {
-				type: 'string',
-				source: 'html',
-				selector: '.blocks-gallery-caption',
-			},
-			imageCrop: {
-				type: 'boolean',
-				default: true,
-			},
-			linkTo: {
-				type: 'string',
-				default: 'none',
-			},
+			default: [],
 		},
-		supports: {
-			align: true,
+		columns: {
+			type: 'number',
+			minimum: 1,
+			maximum: 8,
 		},
-		isEligible( { ids } ) {
-			return ids && ids.some( ( id ) => typeof id === 'string' );
+		caption: {
+			type: 'string',
+			source: 'html',
+			selector: '.blocks-gallery-caption',
 		},
-		migrate( attributes ) {
-			return {
-				...attributes,
-				ids: map( attributes.ids, ( id ) => {
-					const parsedId = parseInt( id, 10 );
-					return Number.isInteger( parsedId ) ? parsedId : null;
-				} ),
-			};
+		imageCrop: {
+			type: 'boolean',
+			default: true,
 		},
-		save( { attributes } ) {
-			const {
-				images,
-				columns = defaultColumnsNumberV1( attributes ),
-				imageCrop,
-				caption,
-				linkTo,
-			} = attributes;
-
-			return (
-				<figure
-					className={ `columns-${ columns } ${
-						imageCrop ? 'is-cropped' : ''
-					}` }
-				>
-					<ul className="blocks-gallery-grid">
-						{ images.map( ( image ) => {
-							let href;
-
-							switch ( linkTo ) {
-								case 'media':
-									href = image.fullUrl || image.url;
-									break;
-								case 'attachment':
-									href = image.link;
-									break;
-							}
-
-							const img = (
-								<img
-									src={ image.url }
-									alt={ image.alt }
-									data-id={ image.id }
-									data-full-url={ image.fullUrl }
-									data-link={ image.link }
-									className={
-										image.id
-											? `wp-image-${ image.id }`
-											: null
-									}
-								/>
-							);
-
-							return (
-								<li
-									key={ image.id || image.url }
-									className="blocks-gallery-item"
-								>
-									<figure>
-										{ href ? (
-											<a href={ href }>{ img }</a>
-										) : (
-											img
-										) }
-										{ ! RichText.isEmpty(
-											image.caption
-										) && (
-											<RichText.Content
-												tagName="figcaption"
-												className="blocks-gallery-item__caption"
-												value={ image.caption }
-											/>
-										) }
-									</figure>
-								</li>
-							);
-						} ) }
-					</ul>
-					{ ! RichText.isEmpty( caption ) && (
-						<RichText.Content
-							tagName="figcaption"
-							className="blocks-gallery-caption"
-							value={ caption }
-						/>
-					) }
-				</figure>
-			);
+		linkTo: {
+			type: 'string',
 		},
-	},
-	{
-		attributes: {
-			images: {
-				type: 'array',
-				default: [],
-				source: 'query',
-				selector: 'ul.wp-block-gallery .blocks-gallery-item',
-				query: {
-					url: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'src',
-					},
-					fullUrl: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-full-url',
-					},
-					alt: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'alt',
-						default: '',
-					},
-					id: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-id',
-					},
-					link: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-link',
-					},
-					caption: {
-						type: 'array',
-						source: 'children',
-						selector: 'figcaption',
-					},
-				},
-			},
-			ids: {
-				type: 'array',
-				default: [],
-			},
-			columns: {
-				type: 'number',
-			},
-			imageCrop: {
-				type: 'boolean',
-				default: true,
-			},
-			linkTo: {
-				type: 'string',
-				default: 'none',
-			},
+		sizeSlug: {
+			type: 'string',
+			default: 'large',
 		},
-		supports: {
-			align: true,
-		},
-		save( { attributes } ) {
-			const {
-				images,
-				columns = defaultColumnsNumberV1( attributes ),
-				imageCrop,
-				linkTo,
-			} = attributes;
-			return (
-				<ul
-					className={ `columns-${ columns } ${
-						imageCrop ? 'is-cropped' : ''
-					}` }
-				>
+	},
+	supports: {
+		anchor: true,
+		align: true,
+	},
+	save( { attributes } ) {
+		const {
+			images,
+			columns = defaultColumnsNumberV1( attributes ),
+			imageCrop,
+			caption,
+			linkTo,
+		} = attributes;
+		const className = `columns-${ columns } ${
+			imageCrop ? 'is-cropped' : ''
+		}`;
+
+		return (
+			<figure { ...useBlockProps.save( { className } ) }>
+				<ul className="blocks-gallery-grid">
 					{ images.map( ( image ) => {
 						let href;
 
 						switch ( linkTo ) {
-							case 'media':
+							case DEPRECATED_LINK_DESTINATION_MEDIA:
 								href = image.fullUrl || image.url;
 								break;
-							case 'attachment':
+							case DEPRECATED_LINK_DESTINATION_ATTACHMENT:
 								href = image.link;
 								break;
 						}
@@ -463,116 +261,163 @@ const deprecated = [
 									) : (
 										img
 									) }
-									{ image.caption &&
-										image.caption.length > 0 && (
-											<RichText.Content
-												tagName="figcaption"
-												value={ image.caption }
-											/>
-										) }
+									{ ! RichText.isEmpty( image.caption ) && (
+										<RichText.Content
+											tagName="figcaption"
+											className="blocks-gallery-item__caption"
+											value={ image.caption }
+										/>
+									) }
 								</figure>
 							</li>
 						);
 					} ) }
 				</ul>
-			);
-		},
+				{ ! RichText.isEmpty( caption ) && (
+					<RichText.Content
+						tagName="figcaption"
+						className="blocks-gallery-caption"
+						value={ caption }
+					/>
+				) }
+			</figure>
+		);
 	},
-	{
-		attributes: {
-			images: {
-				type: 'array',
-				default: [],
-				source: 'query',
-				selector: 'ul.wp-block-gallery .blocks-gallery-item',
-				query: {
-					url: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'src',
-					},
-					alt: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'alt',
-						default: '',
-					},
-					id: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-id',
-					},
-					link: {
-						source: 'attribute',
-						selector: 'img',
-						attribute: 'data-link',
-					},
-					caption: {
-						type: 'array',
-						source: 'children',
-						selector: 'figcaption',
-					},
+	migrate( attributes ) {
+		const settings = select( blockEditorStore ).getSettings();
+
+		if ( settings.__unstableGalleryWithImageBlocks ) {
+			return runV2Migration( attributes );
+		}
+
+		return attributes;
+	},
+};
+const v5 = {
+	attributes: {
+		images: {
+			type: 'array',
+			default: [],
+			source: 'query',
+			selector: '.blocks-gallery-item',
+			query: {
+				url: {
+					type: 'string',
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'src',
+				},
+				fullUrl: {
+					type: 'string',
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-full-url',
+				},
+				link: {
+					type: 'string',
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-link',
+				},
+				alt: {
+					type: 'string',
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'alt',
+					default: '',
+				},
+				id: {
+					type: 'string',
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-id',
+				},
+				caption: {
+					type: 'string',
+					source: 'html',
+					selector: '.blocks-gallery-item__caption',
 				},
 			},
-			columns: {
+		},
+		ids: {
+			type: 'array',
+			items: {
 				type: 'number',
 			},
-			imageCrop: {
-				type: 'boolean',
-				default: true,
-			},
-			linkTo: {
-				type: 'string',
-				default: 'none',
-			},
+			default: [],
 		},
-		isEligible( { images, ids } ) {
-			return (
-				images &&
-				images.length > 0 &&
-				( ( ! ids && images ) ||
-					( ids && images && ids.length !== images.length ) ||
-					some( images, ( id, index ) => {
-						if ( ! id && ids[ index ] !== null ) {
-							return true;
-						}
-						return parseInt( id, 10 ) !== ids[ index ];
-					} ) )
-			);
+		columns: {
+			type: 'number',
+			minimum: 1,
+			maximum: 8,
 		},
-		migrate( attributes ) {
-			return {
-				...attributes,
-				ids: map( attributes.images, ( { id } ) => {
-					if ( ! id ) {
-						return null;
-					}
-					return parseInt( id, 10 );
-				} ),
-			};
+		caption: {
+			type: 'string',
+			source: 'html',
+			selector: '.blocks-gallery-caption',
 		},
-		supports: {
-			align: true,
-		},
-		save( { attributes } ) {
-			const {
-				images,
-				columns = defaultColumnsNumberV1( attributes ),
-				imageCrop,
-				linkTo,
-			} = attributes;
-			return (
-				<ul
-					className={ `columns-${ columns } ${
-						imageCrop ? 'is-cropped' : ''
-					}` }
-				>
+		imageCrop: {
+			type: 'boolean',
+			default: true,
+		},
+		linkTo: {
+			type: 'string',
+			default: 'none',
+		},
+		sizeSlug: {
+			type: 'string',
+			default: 'large',
+		},
+	},
+	supports: {
+		align: true,
+	},
+	isEligible( { linkTo } ) {
+		return ! linkTo || linkTo === 'attachment' || linkTo === 'media';
+	},
+	migrate( attributes ) {
+		const settings = select( blockEditorStore ).getSettings();
+
+		if ( settings.__unstableGalleryWithImageBlocks ) {
+			return runV2Migration( attributes );
+		}
+
+		let linkTo = attributes.linkTo;
+
+		if ( ! attributes.linkTo ) {
+			linkTo = 'none';
+		} else if ( attributes.linkTo === 'attachment' ) {
+			linkTo = 'post';
+		} else if ( attributes.linkTo === 'media' ) {
+			linkTo = 'file';
+		}
+		return {
+			...attributes,
+			linkTo,
+		};
+	},
+	save( { attributes } ) {
+		const {
+			images,
+			columns = defaultColumnsNumberV1( attributes ),
+			imageCrop,
+			caption,
+			linkTo,
+		} = attributes;
+
+		return (
+			<figure
+				className={ `columns-${ columns } ${
+					imageCrop ? 'is-cropped' : ''
+				}` }
+			>
+				<ul className="blocks-gallery-grid">
 					{ images.map( ( image ) => {
 						let href;
 
 						switch ( linkTo ) {
 							case 'media':
-								href = image.url;
+								href = image.fullUrl || image.url;
 								break;
 							case 'attachment':
 								href = image.link;
@@ -584,6 +429,7 @@ const deprecated = [
 								src={ image.url }
 								alt={ image.alt }
 								data-id={ image.id }
+								data-full-url={ image.fullUrl }
 								data-link={ image.link }
 								className={
 									image.id ? `wp-image-${ image.id }` : null
@@ -602,84 +448,135 @@ const deprecated = [
 									) : (
 										img
 									) }
-									{ image.caption &&
-										image.caption.length > 0 && (
-											<RichText.Content
-												tagName="figcaption"
-												value={ image.caption }
-											/>
-										) }
+									{ ! RichText.isEmpty( image.caption ) && (
+										<RichText.Content
+											tagName="figcaption"
+											className="blocks-gallery-item__caption"
+											value={ image.caption }
+										/>
+									) }
 								</figure>
 							</li>
 						);
 					} ) }
 				</ul>
-			);
-		},
+				{ ! RichText.isEmpty( caption ) && (
+					<RichText.Content
+						tagName="figcaption"
+						className="blocks-gallery-caption"
+						value={ caption }
+					/>
+				) }
+			</figure>
+		);
 	},
-	{
-		attributes: {
-			images: {
-				type: 'array',
-				default: [],
-				source: 'query',
-				selector:
-					'div.wp-block-gallery figure.blocks-gallery-image img',
-				query: {
-					url: {
-						source: 'attribute',
-						attribute: 'src',
-					},
-					alt: {
-						source: 'attribute',
-						attribute: 'alt',
-						default: '',
-					},
-					id: {
-						source: 'attribute',
-						attribute: 'data-id',
-					},
+};
+
+const v4 = {
+	attributes: {
+		images: {
+			type: 'array',
+			default: [],
+			source: 'query',
+			selector: '.blocks-gallery-item',
+			query: {
+				url: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'src',
+				},
+				fullUrl: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-full-url',
+				},
+				link: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-link',
+				},
+				alt: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'alt',
+					default: '',
+				},
+				id: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-id',
+				},
+				caption: {
+					type: 'string',
+					source: 'html',
+					selector: '.blocks-gallery-item__caption',
 				},
 			},
-			columns: {
-				type: 'number',
-			},
-			imageCrop: {
-				type: 'boolean',
-				default: true,
-			},
-			linkTo: {
-				type: 'string',
-				default: 'none',
-			},
-			align: {
-				type: 'string',
-				default: 'none',
-			},
 		},
-		supports: {
-			align: true,
-		},
-		save( { attributes } ) {
-			const {
-				images,
-				columns = defaultColumnsNumberV1( attributes ),
-				align,
-				imageCrop,
-				linkTo,
-			} = attributes;
-			const className = classnames( `columns-${ columns }`, {
-				alignnone: align === 'none',
-				'is-cropped': imageCrop,
-			} );
-			return (
-				<div className={ className }>
+		ids: {
+			type: 'array',
+			default: [],
+		},
+		columns: {
+			type: 'number',
+		},
+		caption: {
+			type: 'string',
+			source: 'html',
+			selector: '.blocks-gallery-caption',
+		},
+		imageCrop: {
+			type: 'boolean',
+			default: true,
+		},
+		linkTo: {
+			type: 'string',
+			default: 'none',
+		},
+	},
+	supports: {
+		align: true,
+	},
+	isEligible( { ids } ) {
+		return ids && ids.some( ( id ) => typeof id === 'string' );
+	},
+	migrate( attributes ) {
+		const settings = select( blockEditorStore ).getSettings();
+
+		if ( settings.__unstableGalleryWithImageBlocks ) {
+			return runV2Migration( attributes );
+		}
+
+		return {
+			...attributes,
+			ids: map( attributes.ids, ( id ) => {
+				const parsedId = parseInt( id, 10 );
+				return Number.isInteger( parsedId ) ? parsedId : null;
+			} ),
+		};
+	},
+	save( { attributes } ) {
+		const {
+			images,
+			columns = defaultColumnsNumberV1( attributes ),
+			imageCrop,
+			caption,
+			linkTo,
+		} = attributes;
+
+		return (
+			<figure
+				className={ `columns-${ columns } ${
+					imageCrop ? 'is-cropped' : ''
+				}` }
+			>
+				<ul className="blocks-gallery-grid">
 					{ images.map( ( image ) => {
 						let href;
 
 						switch ( linkTo ) {
 							case 'media':
-								href = image.url;
+								href = image.fullUrl || image.url;
 								break;
 							case 'attachment':
 								href = image.link;
@@ -691,22 +588,411 @@ const deprecated = [
 								src={ image.url }
 								alt={ image.alt }
 								data-id={ image.id }
+								data-full-url={ image.fullUrl }
+								data-link={ image.link }
+								className={
+									image.id ? `wp-image-${ image.id }` : null
+								}
 							/>
 						);
 
 						return (
-							<figure
+							<li
 								key={ image.id || image.url }
-								className="blocks-gallery-image"
+								className="blocks-gallery-item"
 							>
-								{ href ? <a href={ href }>{ img }</a> : img }
-							</figure>
+								<figure>
+									{ href ? (
+										<a href={ href }>{ img }</a>
+									) : (
+										img
+									) }
+									{ ! RichText.isEmpty( image.caption ) && (
+										<RichText.Content
+											tagName="figcaption"
+											className="blocks-gallery-item__caption"
+											value={ image.caption }
+										/>
+									) }
+								</figure>
+							</li>
 						);
 					} ) }
-				</div>
-			);
+				</ul>
+				{ ! RichText.isEmpty( caption ) && (
+					<RichText.Content
+						tagName="figcaption"
+						className="blocks-gallery-caption"
+						value={ caption }
+					/>
+				) }
+			</figure>
+		);
+	},
+};
+const v3 = {
+	attributes: {
+		images: {
+			type: 'array',
+			default: [],
+			source: 'query',
+			selector: 'ul.wp-block-gallery .blocks-gallery-item',
+			query: {
+				url: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'src',
+				},
+				fullUrl: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-full-url',
+				},
+				alt: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'alt',
+					default: '',
+				},
+				id: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-id',
+				},
+				link: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-link',
+				},
+				caption: {
+					type: 'array',
+					source: 'children',
+					selector: 'figcaption',
+				},
+			},
+		},
+		ids: {
+			type: 'array',
+			default: [],
+		},
+		columns: {
+			type: 'number',
+		},
+		imageCrop: {
+			type: 'boolean',
+			default: true,
+		},
+		linkTo: {
+			type: 'string',
+			default: 'none',
+		},
+	},
+	supports: {
+		align: true,
+	},
+	save( { attributes } ) {
+		const {
+			images,
+			columns = defaultColumnsNumberV1( attributes ),
+			imageCrop,
+			linkTo,
+		} = attributes;
+		return (
+			<ul
+				className={ `columns-${ columns } ${
+					imageCrop ? 'is-cropped' : ''
+				}` }
+			>
+				{ images.map( ( image ) => {
+					let href;
+
+					switch ( linkTo ) {
+						case 'media':
+							href = image.fullUrl || image.url;
+							break;
+						case 'attachment':
+							href = image.link;
+							break;
+					}
+
+					const img = (
+						<img
+							src={ image.url }
+							alt={ image.alt }
+							data-id={ image.id }
+							data-full-url={ image.fullUrl }
+							data-link={ image.link }
+							className={
+								image.id ? `wp-image-${ image.id }` : null
+							}
+						/>
+					);
+
+					return (
+						<li
+							key={ image.id || image.url }
+							className="blocks-gallery-item"
+						>
+							<figure>
+								{ href ? <a href={ href }>{ img }</a> : img }
+								{ image.caption && image.caption.length > 0 && (
+									<RichText.Content
+										tagName="figcaption"
+										value={ image.caption }
+									/>
+								) }
+							</figure>
+						</li>
+					);
+				} ) }
+			</ul>
+		);
+	},
+	migrate( attributes ) {
+		const settings = select( blockEditorStore ).getSettings();
+
+		if ( settings.__unstableGalleryWithImageBlocks ) {
+			return runV2Migration( attributes );
+		}
+	},
+};
+const v2 = {
+	attributes: {
+		images: {
+			type: 'array',
+			default: [],
+			source: 'query',
+			selector: 'ul.wp-block-gallery .blocks-gallery-item',
+			query: {
+				url: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'src',
+				},
+				alt: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'alt',
+					default: '',
+				},
+				id: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-id',
+				},
+				link: {
+					source: 'attribute',
+					selector: 'img',
+					attribute: 'data-link',
+				},
+				caption: {
+					type: 'array',
+					source: 'children',
+					selector: 'figcaption',
+				},
+			},
+		},
+		columns: {
+			type: 'number',
+		},
+		imageCrop: {
+			type: 'boolean',
+			default: true,
+		},
+		linkTo: {
+			type: 'string',
+			default: 'none',
+		},
+	},
+	isEligible( { images, ids } ) {
+		return (
+			images &&
+			images.length > 0 &&
+			( ( ! ids && images ) ||
+				( ids && images && ids.length !== images.length ) ||
+				some( images, ( id, index ) => {
+					if ( ! id && ids[ index ] !== null ) {
+						return true;
+					}
+					return parseInt( id, 10 ) !== ids[ index ];
+				} ) )
+		);
+	},
+	migrate( attributes ) {
+		const settings = select( blockEditorStore ).getSettings();
+
+		if ( settings.__unstableGalleryWithImageBlocks ) {
+			return runV2Migration( attributes );
+		}
+		return {
+			...attributes,
+			ids: map( attributes.images, ( { id } ) => {
+				if ( ! id ) {
+					return null;
+				}
+				return parseInt( id, 10 );
+			} ),
+		};
+	},
+	supports: {
+		align: true,
+	},
+	save( { attributes } ) {
+		const {
+			images,
+			columns = defaultColumnsNumberV1( attributes ),
+			imageCrop,
+			linkTo,
+		} = attributes;
+		return (
+			<ul
+				className={ `columns-${ columns } ${
+					imageCrop ? 'is-cropped' : ''
+				}` }
+			>
+				{ images.map( ( image ) => {
+					let href;
+
+					switch ( linkTo ) {
+						case 'media':
+							href = image.url;
+							break;
+						case 'attachment':
+							href = image.link;
+							break;
+					}
+
+					const img = (
+						<img
+							src={ image.url }
+							alt={ image.alt }
+							data-id={ image.id }
+							data-link={ image.link }
+							className={
+								image.id ? `wp-image-${ image.id }` : null
+							}
+						/>
+					);
+
+					return (
+						<li
+							key={ image.id || image.url }
+							className="blocks-gallery-item"
+						>
+							<figure>
+								{ href ? <a href={ href }>{ img }</a> : img }
+								{ image.caption && image.caption.length > 0 && (
+									<RichText.Content
+										tagName="figcaption"
+										value={ image.caption }
+									/>
+								) }
+							</figure>
+						</li>
+					);
+				} ) }
+			</ul>
+		);
+	},
+};
+
+const v1 = {
+	attributes: {
+		images: {
+			type: 'array',
+			default: [],
+			source: 'query',
+			selector: 'div.wp-block-gallery figure.blocks-gallery-image img',
+			query: {
+				url: {
+					source: 'attribute',
+					attribute: 'src',
+				},
+				alt: {
+					source: 'attribute',
+					attribute: 'alt',
+					default: '',
+				},
+				id: {
+					source: 'attribute',
+					attribute: 'data-id',
+				},
+			},
+		},
+		columns: {
+			type: 'number',
 		},
+		imageCrop: {
+			type: 'boolean',
+			default: true,
+		},
+		linkTo: {
+			type: 'string',
+			default: 'none',
+		},
+		align: {
+			type: 'string',
+			default: 'none',
+		},
+	},
+	supports: {
+		align: true,
+	},
+	save( { attributes } ) {
+		const {
+			images,
+			columns = defaultColumnsNumberV1( attributes ),
+			align,
+			imageCrop,
+			linkTo,
+		} = attributes;
+		const className = classnames( `columns-${ columns }`, {
+			alignnone: align === 'none',
+			'is-cropped': imageCrop,
+		} );
+		return (
+			<div className={ className }>
+				{ images.map( ( image ) => {
+					let href;
+
+					switch ( linkTo ) {
+						case 'media':
+							href = image.url;
+							break;
+						case 'attachment':
+							href = image.link;
+							break;
+					}
+
+					const img = (
+						<img
+							src={ image.url }
+							alt={ image.alt }
+							data-id={ image.id }
+						/>
+					);
+
+					return (
+						<figure
+							key={ image.id || image.url }
+							className="blocks-gallery-image"
+						>
+							{ href ? <a href={ href }>{ img }</a> : img }
+						</figure>
+					);
+				} ) }
+			</div>
+		);
+	},
+	migrate( attributes ) {
+		const settings = select( blockEditorStore ).getSettings();
+
+		if ( settings.__unstableGalleryWithImageBlocks ) {
+			return runV2Migration( attributes );
+		}
+
+		return attributes;
 	},
-];
+};
 
-export default deprecated;
+export default [ v6, v5, v4, v3, v2, v1 ];

From 09077a8e6ff22a0145889a9a844f25457693960b Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Mon, 29 Nov 2021 13:42:47 +1300
Subject: [PATCH 02/30] Remove update gallery modal

---
 packages/block-library/src/gallery/v1/edit.js | 29 +-----
 .../src/gallery/v1/update-gallery-modal.js    | 97 -------------------
 2 files changed, 1 insertion(+), 125 deletions(-)
 delete mode 100644 packages/block-library/src/gallery/v1/update-gallery-modal.js

diff --git a/packages/block-library/src/gallery/v1/edit.js b/packages/block-library/src/gallery/v1/edit.js
index cfa1755445d8a..ca57494142feb 100644
--- a/packages/block-library/src/gallery/v1/edit.js
+++ b/packages/block-library/src/gallery/v1/edit.js
@@ -24,10 +24,8 @@ import {
 	ToggleControl,
 	withNotices,
 	RangeControl,
-	ToolbarButton,
 } from '@wordpress/components';
 import {
-	BlockControls,
 	MediaPlaceholder,
 	InspectorControls,
 	useBlockProps,
@@ -53,7 +51,6 @@ import {
 	LINK_DESTINATION_MEDIA,
 	LINK_DESTINATION_NONE,
 } from './constants';
-import UpdateGalleryModal from './update-gallery-modal';
 
 const MAX_COLUMNS = 8;
 const linkOptions = [
@@ -102,13 +99,10 @@ function GalleryEdit( props ) {
 		mediaUpload,
 		getMedia,
 		wasBlockJustInserted,
-		__unstableGalleryWithImageBlocks,
 	} = useSelect( ( select ) => {
 		const settings = select( blockEditorStore ).getSettings();
 
 		return {
-			__unstableGalleryWithImageBlocks:
-				settings.__unstableGalleryWithImageBlocks,
 			imageSizes: settings.imageSizes,
 			mediaUpload: settings.mediaUpload,
 			getMedia: select( coreStore ).getMedia,
@@ -414,10 +408,6 @@ function GalleryEdit( props ) {
 		/>
 	);
 
-	const [ isUpdateOpen, setUpdateOpen ] = useState( false );
-	const openUpdateModal = () => setUpdateOpen( true );
-	const closeUpdateModal = () => setUpdateOpen( false );
-
 	const blockProps = useBlockProps();
 
 	if ( ! hasImages ) {
@@ -466,24 +456,7 @@ function GalleryEdit( props ) {
 					) }
 				</PanelBody>
 			</InspectorControls>
-			{ /* TODO: Remove platform condition when native conversion is ready */ }
-			{ Platform.isWeb && __unstableGalleryWithImageBlocks && (
-				<BlockControls group="other">
-					<ToolbarButton
-						onClick={ openUpdateModal }
-						title={ __( 'Update' ) }
-						label={ __( 'Update to the new gallery format' ) }
-					>
-						{ __( 'Update' ) }
-					</ToolbarButton>
-				</BlockControls>
-			) }
-			{ Platform.isWeb && isUpdateOpen && (
-				<UpdateGalleryModal
-					onClose={ closeUpdateModal }
-					clientId={ clientId }
-				/>
-			) }
+
 			{ noticeUI }
 			<Gallery
 				{ ...props }
diff --git a/packages/block-library/src/gallery/v1/update-gallery-modal.js b/packages/block-library/src/gallery/v1/update-gallery-modal.js
deleted file mode 100644
index 86510ade47850..0000000000000
--- a/packages/block-library/src/gallery/v1/update-gallery-modal.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { Button, Modal } from '@wordpress/components';
-import { __ } from '@wordpress/i18n';
-import { useDispatch, useSelect } from '@wordpress/data';
-import { createBlock } from '@wordpress/blocks';
-import { store as blockEditorStore } from '@wordpress/block-editor';
-
-/**
- * Internal dependencies
- */
-import {
-	LINK_DESTINATION_ATTACHMENT,
-	LINK_DESTINATION_NONE,
-	LINK_DESTINATION_MEDIA,
-} from './constants';
-
-export const updateGallery = ( {
-	clientId,
-	getBlock,
-	replaceBlocks,
-} ) => () => {
-	let link;
-	const {
-		attributes: { sizeSlug, linkTo, images, caption },
-	} = getBlock( clientId );
-
-	switch ( linkTo ) {
-		case 'post':
-			link = LINK_DESTINATION_ATTACHMENT;
-			break;
-		case 'file':
-			link = LINK_DESTINATION_MEDIA;
-			break;
-		default:
-			link = LINK_DESTINATION_NONE;
-			break;
-	}
-	const innerBlocks = images.map( ( image ) =>
-		createBlock( 'core/image', {
-			id: image.id ? parseInt( image.id, 10 ) : null,
-			url: image.url,
-			alt: image.alt,
-			caption: image.caption,
-			linkDestination: link,
-		} )
-	);
-
-	replaceBlocks(
-		clientId,
-		createBlock(
-			'core/gallery',
-			{ sizeSlug, linkTo: link, caption },
-			innerBlocks
-		)
-	);
-};
-
-export default function UpdateGalleryModal( { onClose, clientId } ) {
-	const { getBlock } = useSelect( blockEditorStore );
-	const { replaceBlocks } = useDispatch( blockEditorStore );
-	return (
-		<Modal
-			closeLabel={ __( 'Close' ) }
-			onRequestClose={ onClose }
-			title={ __( 'Update gallery' ) }
-			className={ 'wp-block-update-gallery-modal' }
-			aria={ {
-				describedby: 'wp-block-update-gallery-modal__description',
-			} }
-		>
-			<p id={ 'wp-block-update-gallery-modal__description' }>
-				{ __(
-					'Updating to the new format adds the ability to use custom links or styles on individual images in the gallery, and makes it easier to add or move them around.'
-				) }
-			</p>
-
-			<div className="wp-block-update-gallery-modal-buttons">
-				<Button isTertiary onClick={ onClose }>
-					{ __( 'Cancel' ) }
-				</Button>
-				<Button
-					isPrimary
-					onClick={ updateGallery( {
-						replaceBlocks,
-						getBlock,
-						clientId,
-						createBlock,
-					} ) }
-				>
-					{ __( 'Update' ) }
-				</Button>
-			</div>
-		</Modal>
-	);
-}

From 69b0899cc1697436183968c8c5eb6ce67194ce94 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Fri, 5 Nov 2021 14:01:09 +1300
Subject: [PATCH 03/30] Add check for gallery v2 compat to editor init so it is
 available when block deprecation pipeline runs

---
 lib/compat.php                                | 24 ++++++++++++++
 .../block-library/src/gallery/deprecated.js   | 32 +++++--------------
 packages/block-library/src/gallery/save.js    |  2 +-
 3 files changed, 33 insertions(+), 25 deletions(-)

diff --git a/lib/compat.php b/lib/compat.php
index 8d1f908161870..80bae7f52551d 100644
--- a/lib/compat.php
+++ b/lib/compat.php
@@ -191,3 +191,27 @@ function gutenberg_safe_style_attrs( $attrs ) {
 	return $attrs;
 }
 add_filter( 'safe_style_css', 'gutenberg_safe_style_attrs' );
+
+/**
+ * The new gallery block format is not compatible with the use_BalanceTags option
+ * in WP versions <= 5.8 https://core.trac.wordpress.org/ticket/54130.
+ * This method adds a variable to the wp namespace to indicate if the new gallery block
+ * format can be enabled or not. It needs to be added this early and to the wp namespace
+ * as it needs to be available when the intial block parsing runs on editor load, and most of
+ * the editor store and standard flags are not loaded yet at that point
+ *
+ * Note: This should be removed when the minimum required WP version is >= 5.9.
+ *
+ * @return void.
+ */
+function gutenberg_check_gallery_block_v2_compatibility() {
+	$use_balance_tags   = (int) get_option( 'use_balanceTags' );
+	$v2_gallery_enabled = boolval( 1 !== $use_balance_tags || is_wp_version_compatible( '5.9' ) ) ? 'true' : 'false';
+
+	wp_add_inline_script(
+		'wp-blocks',
+		'wp.galleryBlockV2Enabled = ' . $v2_gallery_enabled . ';',
+		'after'
+	);
+}
+add_action( 'init', 'gutenberg_check_gallery_block_v2_compatibility' );
diff --git a/packages/block-library/src/gallery/deprecated.js b/packages/block-library/src/gallery/deprecated.js
index 487e259faf964..2367546ff131a 100644
--- a/packages/block-library/src/gallery/deprecated.js
+++ b/packages/block-library/src/gallery/deprecated.js
@@ -7,12 +7,8 @@ import { map, some } from 'lodash';
 /**
  * WordPress dependencies
  */
-import {
-	RichText,
-	store as blockEditorStore,
-	useBlockProps,
-} from '@wordpress/block-editor';
-import { select } from '@wordpress/data';
+import { RichText, useBlockProps } from '@wordpress/block-editor';
+
 import { createBlock } from '@wordpress/blocks';
 
 /**
@@ -284,9 +280,7 @@ const v6 = {
 		);
 	},
 	migrate( attributes ) {
-		const settings = select( blockEditorStore ).getSettings();
-
-		if ( settings.__unstableGalleryWithImageBlocks ) {
+		if ( window.wp.galleryBlockV2Enabled ) {
 			return runV2Migration( attributes );
 		}
 
@@ -376,9 +370,7 @@ const v5 = {
 		return ! linkTo || linkTo === 'attachment' || linkTo === 'media';
 	},
 	migrate( attributes ) {
-		const settings = select( blockEditorStore ).getSettings();
-
-		if ( settings.__unstableGalleryWithImageBlocks ) {
+		if ( window.wp.galleryBlockV2Enabled ) {
 			return runV2Migration( attributes );
 		}
 
@@ -541,9 +533,7 @@ const v4 = {
 		return ids && ids.some( ( id ) => typeof id === 'string' );
 	},
 	migrate( attributes ) {
-		const settings = select( blockEditorStore ).getSettings();
-
-		if ( settings.__unstableGalleryWithImageBlocks ) {
+		if ( window.wp.galleryBlockV2Enabled ) {
 			return runV2Migration( attributes );
 		}
 
@@ -749,9 +739,7 @@ const v3 = {
 		);
 	},
 	migrate( attributes ) {
-		const settings = select( blockEditorStore ).getSettings();
-
-		if ( settings.__unstableGalleryWithImageBlocks ) {
+		if ( window.wp.galleryBlockV2Enabled ) {
 			return runV2Migration( attributes );
 		}
 	},
@@ -819,9 +807,7 @@ const v2 = {
 		);
 	},
 	migrate( attributes ) {
-		const settings = select( blockEditorStore ).getSettings();
-
-		if ( settings.__unstableGalleryWithImageBlocks ) {
+		if ( window.wp.galleryBlockV2Enabled ) {
 			return runV2Migration( attributes );
 		}
 		return {
@@ -985,9 +971,7 @@ const v1 = {
 		);
 	},
 	migrate( attributes ) {
-		const settings = select( blockEditorStore ).getSettings();
-
-		if ( settings.__unstableGalleryWithImageBlocks ) {
+		if ( window.wp.galleryBlockV2Enabled ) {
 			return runV2Migration( attributes );
 		}
 
diff --git a/packages/block-library/src/gallery/save.js b/packages/block-library/src/gallery/save.js
index 781b96d2aedbe..711a6abca48a3 100644
--- a/packages/block-library/src/gallery/save.js
+++ b/packages/block-library/src/gallery/save.js
@@ -18,7 +18,7 @@ import {
 import saveWithoutInnerBlocks from './v1/save';
 
 export default function saveWithInnerBlocks( { attributes } ) {
-	if ( attributes?.ids?.length > 0 || attributes?.images?.length > 0 ) {
+	if ( ! window.wp.galleryBlockV2Enabled ) {
 		return saveWithoutInnerBlocks( { attributes } );
 	}
 

From ea46fd49a016488ccc3478cab7150a2572cadb55 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Fri, 5 Nov 2021 16:50:26 +1300
Subject: [PATCH 04/30] Remove references to gallery experimental flag

---
 lib/experiments-page.php                      |  4 +--
 packages/block-editor/README.md               |  1 -
 packages/block-editor/src/store/defaults.js   |  2 --
 .../block-library/src/gallery/edit-wrapper.js |  7 +---
 .../block-library/src/gallery/transforms.js   | 32 +++++--------------
 .../provider/use-block-editor-settings.js     |  1 -
 6 files changed, 10 insertions(+), 37 deletions(-)

diff --git a/lib/experiments-page.php b/lib/experiments-page.php
index 75d4b529ee225..de0b9f5a8e994 100644
--- a/lib/experiments-page.php
+++ b/lib/experiments-page.php
@@ -100,9 +100,7 @@ function gutenberg_experiments_editor_settings( $settings ) {
 	// The refactored gallery currently can't be run on sites with use_balanceTags option set.
 	// This bypass needs to remain in place until this is resolved and a patch released.
 	// https://core.trac.wordpress.org/ticket/54130.
-	$experiments_settings = array(
-		'__unstableGalleryWithImageBlocks' => (int) get_option( 'use_balanceTags' ) !== 1 || is_wp_version_compatible( '5.9' ),
-	);
+	$experiments_settings = array();
 	return array_merge( $settings, $experiments_settings );
 }
 // This can be removed when plugin support requires WordPress 5.8.0+.
diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md
index 94a227890243b..8b6a5453acbb3 100644
--- a/packages/block-editor/README.md
+++ b/packages/block-editor/README.md
@@ -569,7 +569,6 @@ _Properties_
 -   _\_\_experimentalBlockDirectory_ `boolean`: Whether the user has enabled the Block Directory
 -   _\_\_experimentalBlockPatterns_ `Array`: Array of objects representing the block patterns
 -   _\_\_experimentalBlockPatternCategories_ `Array`: Array of objects representing the block pattern categories
--   _\_\_unstableGalleryWithImageBlocks_ `boolean`: Whether the user has enabled the refactored gallery block which uses InnerBlocks
 
 ### SkipToSelectedBlock
 
diff --git a/packages/block-editor/src/store/defaults.js b/packages/block-editor/src/store/defaults.js
index 33165125325e1..49035943cb4a5 100644
--- a/packages/block-editor/src/store/defaults.js
+++ b/packages/block-editor/src/store/defaults.js
@@ -28,7 +28,6 @@ export const PREFERENCES_DEFAULTS = {
  * @property {boolean}       __experimentalBlockDirectory           Whether the user has enabled the Block Directory
  * @property {Array}         __experimentalBlockPatterns            Array of objects representing the block patterns
  * @property {Array}         __experimentalBlockPatternCategories   Array of objects representing the block pattern categories
- * @property {boolean}       __unstableGalleryWithImageBlocks       Whether the user has enabled the refactored gallery block which uses InnerBlocks
  */
 export const SETTINGS_DEFAULTS = {
 	alignWide: false,
@@ -156,7 +155,6 @@ export const SETTINGS_DEFAULTS = {
 	__experimentalBlockPatterns: [],
 	__experimentalBlockPatternCategories: [],
 	__experimentalSpotlightEntityBlocks: [],
-	__unstableGalleryWithImageBlocks: false,
 
 	// gradients setting is not used anymore now defaults are passed from theme.json on the server and core has its own defaults.
 	// The setting is only kept for backward compatibility purposes.
diff --git a/packages/block-library/src/gallery/edit-wrapper.js b/packages/block-library/src/gallery/edit-wrapper.js
index 0e101dfa85e21..2a0c83e26bfc5 100644
--- a/packages/block-library/src/gallery/edit-wrapper.js
+++ b/packages/block-library/src/gallery/edit-wrapper.js
@@ -25,11 +25,6 @@ export default function GalleryEditWrapper( props ) {
 		[ clientId ]
 	);
 
-	const __unstableGalleryWithImageBlocks = useSelect( ( select ) => {
-		const settings = select( blockEditorStore ).getSettings();
-		return settings.__unstableGalleryWithImageBlocks;
-	}, [] );
-
 	// This logic is used to infer version information from content with higher
 	// precedence than the flag. New galleries (and existing empty galleries) will
 	// honor the flag.
@@ -38,7 +33,7 @@ export default function GalleryEditWrapper( props ) {
 		0 < attributes?.ids?.length || 0 < attributes?.images?.length;
 	if (
 		hasOldVersionContent ||
-		( ! hasNewVersionContent && ! __unstableGalleryWithImageBlocks )
+		( ! hasNewVersionContent && ! window.wp.galleryBlockV2Enabled )
 	) {
 		return <EditWithoutInnerBlocks { ...props } />;
 	}
diff --git a/packages/block-library/src/gallery/transforms.js b/packages/block-library/src/gallery/transforms.js
index f5fe6b341cd9e..f1aa7697cd477 100644
--- a/packages/block-library/src/gallery/transforms.js
+++ b/packages/block-library/src/gallery/transforms.js
@@ -49,9 +49,8 @@ const parseShortcodeIds = ( ids ) => {
  * @return   {Block}                 The transformed block.
  */
 function updateThirdPartyTransformToGallery( block ) {
-	const settings = select( blockEditorStore ).getSettings();
 	if (
-		settings.__unstableGalleryWithImageBlocks &&
+		window.wp.galleryBlockV2Enabled &&
 		block.name === 'core/gallery' &&
 		block.attributes?.images.length > 0
 	) {
@@ -145,8 +144,7 @@ const transforms = {
 
 				const validImages = filter( attributes, ( { url } ) => url );
 
-				const settings = select( blockEditorStore ).getSettings();
-				if ( settings.__unstableGalleryWithImageBlocks ) {
+				if ( window.wp.galleryBlockV2Enabled ) {
 					const innerBlocks = validImages.map( ( image ) => {
 						return createBlock( 'core/image', image );
 					} );
@@ -184,10 +182,7 @@ const transforms = {
 				images: {
 					type: 'array',
 					shortcode: ( { named: { ids } } ) => {
-						const settings = select(
-							blockEditorStore
-						).getSettings();
-						if ( ! settings.__unstableGalleryWithImageBlocks ) {
+						if ( ! window.wp.galleryBlockV2Enabled ) {
 							return parseShortcodeIds( ids ).map( ( id ) => ( {
 								id: toString( id ),
 							} ) );
@@ -197,10 +192,7 @@ const transforms = {
 				ids: {
 					type: 'array',
 					shortcode: ( { named: { ids } } ) => {
-						const settings = select(
-							blockEditorStore
-						).getSettings();
-						if ( ! settings.__unstableGalleryWithImageBlocks ) {
+						if ( ! window.wp.galleryBlockV2Enabled ) {
 							return parseShortcodeIds( ids );
 						}
 					},
@@ -208,10 +200,7 @@ const transforms = {
 				shortCodeTransforms: {
 					type: 'array',
 					shortcode: ( { named: { ids } } ) => {
-						const settings = select(
-							blockEditorStore
-						).getSettings();
-						if ( settings.__unstableGalleryWithImageBlocks ) {
+						if ( window.wp.galleryBlockV2Enabled ) {
 							return parseShortcodeIds( ids ).map( ( id ) => ( {
 								id: parseInt( id ),
 							} ) );
@@ -227,10 +216,7 @@ const transforms = {
 				linkTo: {
 					type: 'string',
 					shortcode: ( { named: { link } } ) => {
-						const settings = select(
-							blockEditorStore
-						).getSettings();
-						if ( ! settings.__unstableGalleryWithImageBlocks ) {
+						if ( ! window.wp.galleryBlockV2Enabled ) {
 							switch ( link ) {
 								case 'post':
 									return DEPRECATED_LINK_DESTINATION_ATTACHMENT;
@@ -273,8 +259,7 @@ const transforms = {
 				);
 			},
 			transform( files ) {
-				const settings = select( blockEditorStore ).getSettings();
-				if ( settings.__unstableGalleryWithImageBlocks ) {
+				if ( window.wp.galleryBlockV2Enabled ) {
 					const innerBlocks = files.map( ( file ) =>
 						createBlock( 'core/image', {
 							url: createBlobURL( file ),
@@ -299,8 +284,7 @@ const transforms = {
 			type: 'block',
 			blocks: [ 'core/image' ],
 			transform: ( { align, images, ids, sizeSlug }, innerBlocks ) => {
-				const settings = select( blockEditorStore ).getSettings();
-				if ( settings.__unstableGalleryWithImageBlocks ) {
+				if ( window.wp.galleryBlockV2Enabled ) {
 					if ( innerBlocks.length > 0 ) {
 						return innerBlocks.map(
 							( {
diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js
index aaed50728fb37..09481b66114e6 100644
--- a/packages/editor/src/components/provider/use-block-editor-settings.js
+++ b/packages/editor/src/components/provider/use-block-editor-settings.js
@@ -96,7 +96,6 @@ function useBlockEditorSettings( settings, hasTemplate ) {
 				'__experimentalFeatures',
 				'__experimentalPreferredStyleVariations',
 				'__experimentalSetIsInserterOpened',
-				'__unstableGalleryWithImageBlocks',
 				'alignWide',
 				'allowedBlockTypes',
 				'bodyPlaceholder',

From d1743b617533e391eab5a7dd5107f444874108a2 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Tue, 9 Nov 2021 15:23:40 +1300
Subject: [PATCH 05/30] Revert "Remove references to gallery experimental flag"

This reverts commit b988d24cde235e131224123b98b8d93b1ffd8940.
---
 lib/experiments-page.php                      |  4 ++-
 packages/block-editor/README.md               |  1 +
 packages/block-editor/src/store/defaults.js   |  2 ++
 .../block-library/src/gallery/edit-wrapper.js |  7 +++-
 .../block-library/src/gallery/transforms.js   | 32 ++++++++++++++-----
 .../provider/use-block-editor-settings.js     |  1 +
 6 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/lib/experiments-page.php b/lib/experiments-page.php
index de0b9f5a8e994..75d4b529ee225 100644
--- a/lib/experiments-page.php
+++ b/lib/experiments-page.php
@@ -100,7 +100,9 @@ function gutenberg_experiments_editor_settings( $settings ) {
 	// The refactored gallery currently can't be run on sites with use_balanceTags option set.
 	// This bypass needs to remain in place until this is resolved and a patch released.
 	// https://core.trac.wordpress.org/ticket/54130.
-	$experiments_settings = array();
+	$experiments_settings = array(
+		'__unstableGalleryWithImageBlocks' => (int) get_option( 'use_balanceTags' ) !== 1 || is_wp_version_compatible( '5.9' ),
+	);
 	return array_merge( $settings, $experiments_settings );
 }
 // This can be removed when plugin support requires WordPress 5.8.0+.
diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md
index 8b6a5453acbb3..94a227890243b 100644
--- a/packages/block-editor/README.md
+++ b/packages/block-editor/README.md
@@ -569,6 +569,7 @@ _Properties_
 -   _\_\_experimentalBlockDirectory_ `boolean`: Whether the user has enabled the Block Directory
 -   _\_\_experimentalBlockPatterns_ `Array`: Array of objects representing the block patterns
 -   _\_\_experimentalBlockPatternCategories_ `Array`: Array of objects representing the block pattern categories
+-   _\_\_unstableGalleryWithImageBlocks_ `boolean`: Whether the user has enabled the refactored gallery block which uses InnerBlocks
 
 ### SkipToSelectedBlock
 
diff --git a/packages/block-editor/src/store/defaults.js b/packages/block-editor/src/store/defaults.js
index 49035943cb4a5..33165125325e1 100644
--- a/packages/block-editor/src/store/defaults.js
+++ b/packages/block-editor/src/store/defaults.js
@@ -28,6 +28,7 @@ export const PREFERENCES_DEFAULTS = {
  * @property {boolean}       __experimentalBlockDirectory           Whether the user has enabled the Block Directory
  * @property {Array}         __experimentalBlockPatterns            Array of objects representing the block patterns
  * @property {Array}         __experimentalBlockPatternCategories   Array of objects representing the block pattern categories
+ * @property {boolean}       __unstableGalleryWithImageBlocks       Whether the user has enabled the refactored gallery block which uses InnerBlocks
  */
 export const SETTINGS_DEFAULTS = {
 	alignWide: false,
@@ -155,6 +156,7 @@ export const SETTINGS_DEFAULTS = {
 	__experimentalBlockPatterns: [],
 	__experimentalBlockPatternCategories: [],
 	__experimentalSpotlightEntityBlocks: [],
+	__unstableGalleryWithImageBlocks: false,
 
 	// gradients setting is not used anymore now defaults are passed from theme.json on the server and core has its own defaults.
 	// The setting is only kept for backward compatibility purposes.
diff --git a/packages/block-library/src/gallery/edit-wrapper.js b/packages/block-library/src/gallery/edit-wrapper.js
index 2a0c83e26bfc5..0e101dfa85e21 100644
--- a/packages/block-library/src/gallery/edit-wrapper.js
+++ b/packages/block-library/src/gallery/edit-wrapper.js
@@ -25,6 +25,11 @@ export default function GalleryEditWrapper( props ) {
 		[ clientId ]
 	);
 
+	const __unstableGalleryWithImageBlocks = useSelect( ( select ) => {
+		const settings = select( blockEditorStore ).getSettings();
+		return settings.__unstableGalleryWithImageBlocks;
+	}, [] );
+
 	// This logic is used to infer version information from content with higher
 	// precedence than the flag. New galleries (and existing empty galleries) will
 	// honor the flag.
@@ -33,7 +38,7 @@ export default function GalleryEditWrapper( props ) {
 		0 < attributes?.ids?.length || 0 < attributes?.images?.length;
 	if (
 		hasOldVersionContent ||
-		( ! hasNewVersionContent && ! window.wp.galleryBlockV2Enabled )
+		( ! hasNewVersionContent && ! __unstableGalleryWithImageBlocks )
 	) {
 		return <EditWithoutInnerBlocks { ...props } />;
 	}
diff --git a/packages/block-library/src/gallery/transforms.js b/packages/block-library/src/gallery/transforms.js
index f1aa7697cd477..f5fe6b341cd9e 100644
--- a/packages/block-library/src/gallery/transforms.js
+++ b/packages/block-library/src/gallery/transforms.js
@@ -49,8 +49,9 @@ const parseShortcodeIds = ( ids ) => {
  * @return   {Block}                 The transformed block.
  */
 function updateThirdPartyTransformToGallery( block ) {
+	const settings = select( blockEditorStore ).getSettings();
 	if (
-		window.wp.galleryBlockV2Enabled &&
+		settings.__unstableGalleryWithImageBlocks &&
 		block.name === 'core/gallery' &&
 		block.attributes?.images.length > 0
 	) {
@@ -144,7 +145,8 @@ const transforms = {
 
 				const validImages = filter( attributes, ( { url } ) => url );
 
-				if ( window.wp.galleryBlockV2Enabled ) {
+				const settings = select( blockEditorStore ).getSettings();
+				if ( settings.__unstableGalleryWithImageBlocks ) {
 					const innerBlocks = validImages.map( ( image ) => {
 						return createBlock( 'core/image', image );
 					} );
@@ -182,7 +184,10 @@ const transforms = {
 				images: {
 					type: 'array',
 					shortcode: ( { named: { ids } } ) => {
-						if ( ! window.wp.galleryBlockV2Enabled ) {
+						const settings = select(
+							blockEditorStore
+						).getSettings();
+						if ( ! settings.__unstableGalleryWithImageBlocks ) {
 							return parseShortcodeIds( ids ).map( ( id ) => ( {
 								id: toString( id ),
 							} ) );
@@ -192,7 +197,10 @@ const transforms = {
 				ids: {
 					type: 'array',
 					shortcode: ( { named: { ids } } ) => {
-						if ( ! window.wp.galleryBlockV2Enabled ) {
+						const settings = select(
+							blockEditorStore
+						).getSettings();
+						if ( ! settings.__unstableGalleryWithImageBlocks ) {
 							return parseShortcodeIds( ids );
 						}
 					},
@@ -200,7 +208,10 @@ const transforms = {
 				shortCodeTransforms: {
 					type: 'array',
 					shortcode: ( { named: { ids } } ) => {
-						if ( window.wp.galleryBlockV2Enabled ) {
+						const settings = select(
+							blockEditorStore
+						).getSettings();
+						if ( settings.__unstableGalleryWithImageBlocks ) {
 							return parseShortcodeIds( ids ).map( ( id ) => ( {
 								id: parseInt( id ),
 							} ) );
@@ -216,7 +227,10 @@ const transforms = {
 				linkTo: {
 					type: 'string',
 					shortcode: ( { named: { link } } ) => {
-						if ( ! window.wp.galleryBlockV2Enabled ) {
+						const settings = select(
+							blockEditorStore
+						).getSettings();
+						if ( ! settings.__unstableGalleryWithImageBlocks ) {
 							switch ( link ) {
 								case 'post':
 									return DEPRECATED_LINK_DESTINATION_ATTACHMENT;
@@ -259,7 +273,8 @@ const transforms = {
 				);
 			},
 			transform( files ) {
-				if ( window.wp.galleryBlockV2Enabled ) {
+				const settings = select( blockEditorStore ).getSettings();
+				if ( settings.__unstableGalleryWithImageBlocks ) {
 					const innerBlocks = files.map( ( file ) =>
 						createBlock( 'core/image', {
 							url: createBlobURL( file ),
@@ -284,7 +299,8 @@ const transforms = {
 			type: 'block',
 			blocks: [ 'core/image' ],
 			transform: ( { align, images, ids, sizeSlug }, innerBlocks ) => {
-				if ( window.wp.galleryBlockV2Enabled ) {
+				const settings = select( blockEditorStore ).getSettings();
+				if ( settings.__unstableGalleryWithImageBlocks ) {
 					if ( innerBlocks.length > 0 ) {
 						return innerBlocks.map(
 							( {
diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js
index 09481b66114e6..aaed50728fb37 100644
--- a/packages/editor/src/components/provider/use-block-editor-settings.js
+++ b/packages/editor/src/components/provider/use-block-editor-settings.js
@@ -96,6 +96,7 @@ function useBlockEditorSettings( settings, hasTemplate ) {
 				'__experimentalFeatures',
 				'__experimentalPreferredStyleVariations',
 				'__experimentalSetIsInserterOpened',
+				'__unstableGalleryWithImageBlocks',
 				'alignWide',
 				'allowedBlockTypes',
 				'bodyPlaceholder',

From cba6433b14ee57019aaa7cc8f7654a9c9ab3079e Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Thu, 11 Nov 2021 11:28:34 +1300
Subject: [PATCH 06/30] Abstract out the check for v2 gallery support and use
 the existing experimental flag for mobile

---
 .../block-library/src/gallery/deprecated.js   | 13 +++++----
 .../block-library/src/gallery/edit-wrapper.js |  8 ++----
 packages/block-library/src/gallery/save.js    |  3 +-
 packages/block-library/src/gallery/shared.js  | 20 +++++++++++++
 .../block-library/src/gallery/transforms.js   | 28 +++++--------------
 5 files changed, 38 insertions(+), 34 deletions(-)

diff --git a/packages/block-library/src/gallery/deprecated.js b/packages/block-library/src/gallery/deprecated.js
index 2367546ff131a..fde1ae5960e0c 100644
--- a/packages/block-library/src/gallery/deprecated.js
+++ b/packages/block-library/src/gallery/deprecated.js
@@ -19,6 +19,7 @@ import {
 	LINK_DESTINATION_MEDIA,
 	LINK_DESTINATION_NONE,
 } from './constants';
+import { isGalleryV2Enabled } from './shared';
 
 const DEPRECATED_LINK_DESTINATION_MEDIA = 'file';
 const DEPRECATED_LINK_DESTINATION_ATTACHMENT = 'post';
@@ -280,7 +281,7 @@ const v6 = {
 		);
 	},
 	migrate( attributes ) {
-		if ( window.wp.galleryBlockV2Enabled ) {
+		if ( isGalleryV2Enabled() ) {
 			return runV2Migration( attributes );
 		}
 
@@ -370,7 +371,7 @@ const v5 = {
 		return ! linkTo || linkTo === 'attachment' || linkTo === 'media';
 	},
 	migrate( attributes ) {
-		if ( window.wp.galleryBlockV2Enabled ) {
+		if ( isGalleryV2Enabled() ) {
 			return runV2Migration( attributes );
 		}
 
@@ -533,7 +534,7 @@ const v4 = {
 		return ids && ids.some( ( id ) => typeof id === 'string' );
 	},
 	migrate( attributes ) {
-		if ( window.wp.galleryBlockV2Enabled ) {
+		if ( isGalleryV2Enabled() ) {
 			return runV2Migration( attributes );
 		}
 
@@ -739,7 +740,7 @@ const v3 = {
 		);
 	},
 	migrate( attributes ) {
-		if ( window.wp.galleryBlockV2Enabled ) {
+		if ( isGalleryV2Enabled() ) {
 			return runV2Migration( attributes );
 		}
 	},
@@ -807,7 +808,7 @@ const v2 = {
 		);
 	},
 	migrate( attributes ) {
-		if ( window.wp.galleryBlockV2Enabled ) {
+		if ( isGalleryV2Enabled() ) {
 			return runV2Migration( attributes );
 		}
 		return {
@@ -971,7 +972,7 @@ const v1 = {
 		);
 	},
 	migrate( attributes ) {
-		if ( window.wp.galleryBlockV2Enabled ) {
+		if ( isGalleryV2Enabled() ) {
 			return runV2Migration( attributes );
 		}
 
diff --git a/packages/block-library/src/gallery/edit-wrapper.js b/packages/block-library/src/gallery/edit-wrapper.js
index 0e101dfa85e21..2f3638f7fe58c 100644
--- a/packages/block-library/src/gallery/edit-wrapper.js
+++ b/packages/block-library/src/gallery/edit-wrapper.js
@@ -9,6 +9,7 @@ import { useSelect } from '@wordpress/data';
  */
 import EditWithInnerBlocks from './edit';
 import EditWithoutInnerBlocks from './v1/edit';
+import { isGalleryV2Enabled } from './shared';
 
 /*
  * Using a wrapper around the logic to load the edit for v1 of Gallery block
@@ -25,11 +26,6 @@ export default function GalleryEditWrapper( props ) {
 		[ clientId ]
 	);
 
-	const __unstableGalleryWithImageBlocks = useSelect( ( select ) => {
-		const settings = select( blockEditorStore ).getSettings();
-		return settings.__unstableGalleryWithImageBlocks;
-	}, [] );
-
 	// This logic is used to infer version information from content with higher
 	// precedence than the flag. New galleries (and existing empty galleries) will
 	// honor the flag.
@@ -38,7 +34,7 @@ export default function GalleryEditWrapper( props ) {
 		0 < attributes?.ids?.length || 0 < attributes?.images?.length;
 	if (
 		hasOldVersionContent ||
-		( ! hasNewVersionContent && ! __unstableGalleryWithImageBlocks )
+		( ! hasNewVersionContent && ! isGalleryV2Enabled() )
 	) {
 		return <EditWithoutInnerBlocks { ...props } />;
 	}
diff --git a/packages/block-library/src/gallery/save.js b/packages/block-library/src/gallery/save.js
index 711a6abca48a3..655a1823020fc 100644
--- a/packages/block-library/src/gallery/save.js
+++ b/packages/block-library/src/gallery/save.js
@@ -16,9 +16,10 @@ import {
  * Internal dependencies
  */
 import saveWithoutInnerBlocks from './v1/save';
+import { isGalleryV2Enabled } from './shared';
 
 export default function saveWithInnerBlocks( { attributes } ) {
-	if ( ! window.wp.galleryBlockV2Enabled ) {
+	if ( ! isGalleryV2Enabled() ) {
 		return saveWithoutInnerBlocks( { attributes } );
 	}
 
diff --git a/packages/block-library/src/gallery/shared.js b/packages/block-library/src/gallery/shared.js
index 0de4e8586c33a..f9fdd7748ace7 100644
--- a/packages/block-library/src/gallery/shared.js
+++ b/packages/block-library/src/gallery/shared.js
@@ -3,6 +3,13 @@
  */
 import { get, pick } from 'lodash';
 
+/**
+ * WordPress dependencies
+ */
+import { Platform } from '@wordpress/element';
+import { select } from '@wordpress/data';
+import { store as blockEditorStore } from '@wordpress/block-editor';
+
 export function defaultColumnsNumber( imageCount ) {
 	return imageCount ? Math.min( 3, imageCount ) : 3;
 }
@@ -21,3 +28,16 @@ export const pickRelevantMediaFiles = ( image, sizeSlug = 'large' ) => {
 	}
 	return imageProps;
 };
+
+export function isGalleryV2Enabled() {
+	if ( Platform.isWeb && window.wp.galleryBlockV2Enabled ) {
+		return true;
+	}
+
+	const settings = select( blockEditorStore ).getSettings();
+	if ( settings.__unstableGalleryWithImageBlocks ) {
+		return true;
+	}
+
+	return false;
+}
diff --git a/packages/block-library/src/gallery/transforms.js b/packages/block-library/src/gallery/transforms.js
index f5fe6b341cd9e..5843b452b0017 100644
--- a/packages/block-library/src/gallery/transforms.js
+++ b/packages/block-library/src/gallery/transforms.js
@@ -24,7 +24,7 @@ import {
 	LINK_DESTINATION_ATTACHMENT as DEPRECATED_LINK_DESTINATION_ATTACHMENT,
 	LINK_DESTINATION_MEDIA as DEPRECATED_LINK_DESTINATION_MEDIA,
 } from './v1/constants';
-import { pickRelevantMediaFiles } from './shared';
+import { pickRelevantMediaFiles, isGalleryV2Enabled } from './shared';
 
 const parseShortcodeIds = ( ids ) => {
 	if ( ! ids ) {
@@ -49,9 +49,8 @@ const parseShortcodeIds = ( ids ) => {
  * @return   {Block}                 The transformed block.
  */
 function updateThirdPartyTransformToGallery( block ) {
-	const settings = select( blockEditorStore ).getSettings();
 	if (
-		settings.__unstableGalleryWithImageBlocks &&
+		isGalleryV2Enabled() &&
 		block.name === 'core/gallery' &&
 		block.attributes?.images.length > 0
 	) {
@@ -145,8 +144,7 @@ const transforms = {
 
 				const validImages = filter( attributes, ( { url } ) => url );
 
-				const settings = select( blockEditorStore ).getSettings();
-				if ( settings.__unstableGalleryWithImageBlocks ) {
+				if ( isGalleryV2Enabled() ) {
 					const innerBlocks = validImages.map( ( image ) => {
 						return createBlock( 'core/image', image );
 					} );
@@ -184,10 +182,7 @@ const transforms = {
 				images: {
 					type: 'array',
 					shortcode: ( { named: { ids } } ) => {
-						const settings = select(
-							blockEditorStore
-						).getSettings();
-						if ( ! settings.__unstableGalleryWithImageBlocks ) {
+						if ( ! isGalleryV2Enabled() ) {
 							return parseShortcodeIds( ids ).map( ( id ) => ( {
 								id: toString( id ),
 							} ) );
@@ -197,10 +192,7 @@ const transforms = {
 				ids: {
 					type: 'array',
 					shortcode: ( { named: { ids } } ) => {
-						const settings = select(
-							blockEditorStore
-						).getSettings();
-						if ( ! settings.__unstableGalleryWithImageBlocks ) {
+						if ( ! isGalleryV2Enabled() ) {
 							return parseShortcodeIds( ids );
 						}
 					},
@@ -208,10 +200,7 @@ const transforms = {
 				shortCodeTransforms: {
 					type: 'array',
 					shortcode: ( { named: { ids } } ) => {
-						const settings = select(
-							blockEditorStore
-						).getSettings();
-						if ( settings.__unstableGalleryWithImageBlocks ) {
+						if ( isGalleryV2Enabled() ) {
 							return parseShortcodeIds( ids ).map( ( id ) => ( {
 								id: parseInt( id ),
 							} ) );
@@ -227,10 +216,7 @@ const transforms = {
 				linkTo: {
 					type: 'string',
 					shortcode: ( { named: { link } } ) => {
-						const settings = select(
-							blockEditorStore
-						).getSettings();
-						if ( ! settings.__unstableGalleryWithImageBlocks ) {
+						if ( ! isGalleryV2Enabled() ) {
 							switch ( link ) {
 								case 'post':
 									return DEPRECATED_LINK_DESTINATION_ATTACHMENT;

From 6e6e643c26a87b73251c25c492d0f6a449e68892 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Thu, 11 Nov 2021 11:51:20 +1300
Subject: [PATCH 07/30] Add missing transformation updates

---
 packages/block-library/src/gallery/transforms.js | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/packages/block-library/src/gallery/transforms.js b/packages/block-library/src/gallery/transforms.js
index 5843b452b0017..36106b4bb0664 100644
--- a/packages/block-library/src/gallery/transforms.js
+++ b/packages/block-library/src/gallery/transforms.js
@@ -8,8 +8,6 @@ import { filter, every, toString } from 'lodash';
  */
 import { createBlock } from '@wordpress/blocks';
 import { createBlobURL } from '@wordpress/blob';
-import { select } from '@wordpress/data';
-import { store as blockEditorStore } from '@wordpress/block-editor';
 import { addFilter } from '@wordpress/hooks';
 
 /**
@@ -259,8 +257,7 @@ const transforms = {
 				);
 			},
 			transform( files ) {
-				const settings = select( blockEditorStore ).getSettings();
-				if ( settings.__unstableGalleryWithImageBlocks ) {
+				if ( isGalleryV2Enabled() ) {
 					const innerBlocks = files.map( ( file ) =>
 						createBlock( 'core/image', {
 							url: createBlobURL( file ),
@@ -285,8 +282,7 @@ const transforms = {
 			type: 'block',
 			blocks: [ 'core/image' ],
 			transform: ( { align, images, ids, sizeSlug }, innerBlocks ) => {
-				const settings = select( blockEditorStore ).getSettings();
-				if ( settings.__unstableGalleryWithImageBlocks ) {
+				if ( isGalleryV2Enabled() ) {
 					if ( innerBlocks.length > 0 ) {
 						return innerBlocks.map(
 							( {

From 5e8db7601702b99a67b0199afe000f40850d67cd Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Thu, 11 Nov 2021 12:33:59 +1300
Subject: [PATCH 08/30] Update fixture to be compat with v2 of gallery block

---
 packages/block-library/src/gallery/shared.js  |  4 ++
 .../blocks/core__gallery-with-caption.html    | 39 +++++++----
 .../blocks/core__gallery-with-caption.json    | 58 ++++++++++-----
 .../core__gallery-with-caption.parsed.json    | 49 +++++++++++--
 ...core__gallery-with-caption.serialized.html | 10 ++-
 .../fixtures/blocks/core__gallery.html        | 39 +++++++----
 .../fixtures/blocks/core__gallery.json        | 56 ++++++++++-----
 .../fixtures/blocks/core__gallery.parsed.json | 40 +++++++++--
 .../blocks/core__gallery.serialized.html      | 10 ++-
 .../blocks/core__gallery__columns.html        | 37 ++++++----
 .../blocks/core__gallery__columns.json        | 55 ++++++++++-----
 .../blocks/core__gallery__columns.parsed.json | 51 ++++++++++++--
 .../core__gallery__columns.serialized.html    | 10 ++-
 .../blocks/core__gallery__deprecated-1.json   | 37 ++++++----
 ...ore__gallery__deprecated-1.serialized.html | 10 ++-
 .../blocks/core__gallery__deprecated-2.json   | 44 +++++++-----
 ...ore__gallery__deprecated-2.serialized.html | 10 ++-
 .../blocks/core__gallery__deprecated-3.json   | 38 ++++++----
 ...ore__gallery__deprecated-3.serialized.html | 10 ++-
 .../blocks/core__gallery__deprecated-4.json   | 57 ++++++++++-----
 ...ore__gallery__deprecated-4.serialized.html | 14 +++-
 .../blocks/core__gallery__deprecated-5.json   | 70 ++++++++++++-------
 ...ore__gallery__deprecated-5.serialized.html | 14 +++-
 .../blocks/core__gallery__deprecated-6.html   | 54 ++++++++++++++
 .../blocks/core__gallery__deprecated-6.json   | 62 ++++++++++++++++
 .../core__gallery__deprecated-6.parsed.json   | 14 ++++
 ...ore__gallery__deprecated-6.serialized.html | 13 ++++
 27 files changed, 697 insertions(+), 208 deletions(-)
 create mode 100644 test/integration/fixtures/blocks/core__gallery__deprecated-6.html
 create mode 100644 test/integration/fixtures/blocks/core__gallery__deprecated-6.json
 create mode 100644 test/integration/fixtures/blocks/core__gallery__deprecated-6.parsed.json
 create mode 100644 test/integration/fixtures/blocks/core__gallery__deprecated-6.serialized.html

diff --git a/packages/block-library/src/gallery/shared.js b/packages/block-library/src/gallery/shared.js
index f9fdd7748ace7..a6a767e0c9407 100644
--- a/packages/block-library/src/gallery/shared.js
+++ b/packages/block-library/src/gallery/shared.js
@@ -30,6 +30,10 @@ export const pickRelevantMediaFiles = ( image, sizeSlug = 'large' ) => {
 };
 
 export function isGalleryV2Enabled() {
+	if ( typeof process !== 'undefined' && process.env?.NODE_ENV === 'test' ) {
+		return true;
+	}
+
 	if ( Platform.isWeb && window.wp.galleryBlockV2Enabled ) {
 		return true;
 	}
diff --git a/test/integration/fixtures/blocks/core__gallery-with-caption.html b/test/integration/fixtures/blocks/core__gallery-with-caption.html
index ca644b223d725..0ec3003092d8b 100644
--- a/test/integration/fixtures/blocks/core__gallery-with-caption.html
+++ b/test/integration/fixtures/blocks/core__gallery-with-caption.html
@@ -1,17 +1,26 @@
-<!-- wp:core/gallery {"ids":[null,null]} -->
-<figure class="wp-block-gallery columns-2 is-cropped">
-	<ul class="blocks-gallery-grid">
-		<li class="blocks-gallery-item">
-			<figure>
-				<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title" />
-			</figure>
-		</li>
-		<li class="blocks-gallery-item">
-			<figure>
-				<img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title" />
-			</figure>
-		</li>
-	</ul>
-	<figcaption class="blocks-gallery-caption">Gallery caption.</figcaption>
+<!-- wp:gallery {"linkTo":"none"} -->
+<figure
+	class="wp-block-gallery has-nested-images columns-default is-cropped columns-2"
+>
+	<!-- wp:image {"id":1421,"sizeSlug":"large","linkDestination":"none","inheritedAttributes":{"linkDestination":true,"linkTarget":true,"sizeSlug":true}} -->
+	<figure class="wp-block-image size-large">
+		<img
+			src="https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190"
+			alt="Image gallery image"
+			class="wp-image-1421"
+		/>
+	</figure>
+	<!-- /wp:image -->
+
+	<!-- wp:image {"id":1440,"sizeSlug":"large","linkDestination":"none","inheritedAttributes":{"linkDestination":true,"linkTarget":true,"sizeSlug":true}} -->
+	<figure class="wp-block-image size-large">
+		<img
+			src="https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580"
+			alt="Image gallery image"
+			class="wp-image-1440"
+		/>
+	</figure>
+	<!-- /wp:image -->
+	<figcaption class="blocks-gallery-caption">Gallery Caption</figcaption>
 </figure>
 <!-- /wp:core/gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery-with-caption.json b/test/integration/fixtures/blocks/core__gallery-with-caption.json
index 9780feb5bce79..4c6f79c7c0ea5 100644
--- a/test/integration/fixtures/blocks/core__gallery-with-caption.json
+++ b/test/integration/fixtures/blocks/core__gallery-with-caption.json
@@ -4,25 +4,49 @@
 		"name": "core/gallery",
 		"isValid": true,
 		"attributes": {
-			"images": [
-				{
-					"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",
-					"alt": "title",
-					"caption": ""
-				},
-				{
-					"url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
-					"alt": "title",
-					"caption": ""
-				}
-			],
-			"ids": [ null, null ],
-			"caption": "Gallery caption.",
+			"images": [],
+			"ids": [],
+			"shortCodeTransforms": [],
+			"caption": "Gallery Caption",
 			"imageCrop": true,
+			"fixedHeight": true,
 			"linkTo": "none",
-			"sizeSlug": "large"
+			"sizeSlug": "large",
+			"allowResize": false,
+			"className": "columns-2"
 		},
-		"innerBlocks": [],
-		"originalContent": "<figure class=\"wp-block-gallery columns-2 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n\t<figcaption class=\"blocks-gallery-caption\">Gallery caption.</figcaption>\n</figure>"
+		"innerBlocks": [
+			{
+				"clientId": "_clientId_0",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190",
+					"alt": "Image gallery image",
+					"caption": "",
+					"id": 1421,
+					"sizeSlug": "large",
+					"linkDestination": "none"
+				},
+				"innerBlocks": [],
+				"originalContent": "<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1421\"\n\t\t/>\n\t</figure>"
+			},
+			{
+				"clientId": "_clientId_1",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580",
+					"alt": "Image gallery image",
+					"caption": "",
+					"id": 1440,
+					"sizeSlug": "large",
+					"linkDestination": "none"
+				},
+				"innerBlocks": [],
+				"originalContent": "<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1440\"\n\t\t/>\n\t</figure>"
+			}
+		],
+		"originalContent": "<figure\n\tclass=\"wp-block-gallery has-nested-images columns-default is-cropped columns-2\"\n>\n\t\n\n\t\n\t<figcaption class=\"blocks-gallery-caption\">Gallery Caption</figcaption>\n</figure>"
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery-with-caption.parsed.json b/test/integration/fixtures/blocks/core__gallery-with-caption.parsed.json
index 8060435a4fdc6..bb43e7abcd7f8 100644
--- a/test/integration/fixtures/blocks/core__gallery-with-caption.parsed.json
+++ b/test/integration/fixtures/blocks/core__gallery-with-caption.parsed.json
@@ -2,12 +2,53 @@
 	{
 		"blockName": "core/gallery",
 		"attrs": {
-			"ids": [ null, null ]
+			"linkTo": "none"
 		},
-		"innerBlocks": [],
-		"innerHTML": "\n<figure class=\"wp-block-gallery columns-2 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n\t<figcaption class=\"blocks-gallery-caption\">Gallery caption.</figcaption>\n</figure>\n",
+		"innerBlocks": [
+			{
+				"blockName": "core/image",
+				"attrs": {
+					"id": 1421,
+					"sizeSlug": "large",
+					"linkDestination": "none",
+					"inheritedAttributes": {
+						"linkDestination": true,
+						"linkTarget": true,
+						"sizeSlug": true
+					}
+				},
+				"innerBlocks": [],
+				"innerHTML": "\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1421\"\n\t\t/>\n\t</figure>\n\t",
+				"innerContent": [
+					"\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1421\"\n\t\t/>\n\t</figure>\n\t"
+				]
+			},
+			{
+				"blockName": "core/image",
+				"attrs": {
+					"id": 1440,
+					"sizeSlug": "large",
+					"linkDestination": "none",
+					"inheritedAttributes": {
+						"linkDestination": true,
+						"linkTarget": true,
+						"sizeSlug": true
+					}
+				},
+				"innerBlocks": [],
+				"innerHTML": "\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1440\"\n\t\t/>\n\t</figure>\n\t",
+				"innerContent": [
+					"\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1440\"\n\t\t/>\n\t</figure>\n\t"
+				]
+			}
+		],
+		"innerHTML": "\n<figure\n\tclass=\"wp-block-gallery has-nested-images columns-default is-cropped columns-2\"\n>\n\t\n\n\t\n\t<figcaption class=\"blocks-gallery-caption\">Gallery Caption</figcaption>\n</figure>\n",
 		"innerContent": [
-			"\n<figure class=\"wp-block-gallery columns-2 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n\t<figcaption class=\"blocks-gallery-caption\">Gallery caption.</figcaption>\n</figure>\n"
+			"\n<figure\n\tclass=\"wp-block-gallery has-nested-images columns-default is-cropped columns-2\"\n>\n\t",
+			null,
+			"\n\n\t",
+			null,
+			"\n\t<figcaption class=\"blocks-gallery-caption\">Gallery Caption</figcaption>\n</figure>\n"
 		]
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery-with-caption.serialized.html b/test/integration/fixtures/blocks/core__gallery-with-caption.serialized.html
index 39a9da57a48fa..f67c8682af23e 100644
--- a/test/integration/fixtures/blocks/core__gallery-with-caption.serialized.html
+++ b/test/integration/fixtures/blocks/core__gallery-with-caption.serialized.html
@@ -1,3 +1,9 @@
-<!-- wp:gallery {"ids":[null,null],"linkTo":"none"} -->
-<figure class="wp-block-gallery columns-2 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title"/></figure></li><li class="blocks-gallery-item"><figure><img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title"/></figure></li></ul><figcaption class="blocks-gallery-caption">Gallery caption.</figcaption></figure>
+<!-- wp:gallery {"linkTo":"none","className":"columns-2"} -->
+<figure class="wp-block-gallery has-nested-images columns-default is-cropped columns-2"><!-- wp:image {"id":1421,"sizeSlug":"large","linkDestination":"none"} -->
+<figure class="wp-block-image size-large"><img src="https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190" alt="Image gallery image" class="wp-image-1421"/></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"id":1440,"sizeSlug":"large","linkDestination":"none"} -->
+<figure class="wp-block-image size-large"><img src="https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580" alt="Image gallery image" class="wp-image-1440"/></figure>
+<!-- /wp:image --><figcaption class="blocks-gallery-caption">Gallery Caption</figcaption></figure>
 <!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery.html b/test/integration/fixtures/blocks/core__gallery.html
index a0f62e68fc527..3db587c8d72d5 100644
--- a/test/integration/fixtures/blocks/core__gallery.html
+++ b/test/integration/fixtures/blocks/core__gallery.html
@@ -1,16 +1,25 @@
-<!-- wp:core/gallery {"ids":[null,null]} -->
-<figure class="wp-block-gallery columns-2 is-cropped">
-	<ul class="blocks-gallery-grid">
-		<li class="blocks-gallery-item">
-			<figure>
-				<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title" />
-			</figure>
-		</li>
-		<li class="blocks-gallery-item">
-			<figure>
-				<img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title" />
-			</figure>
-		</li>
-	</ul>
+<!-- wp:gallery {"linkTo":"none","className":"columns-2"} -->
+<figure
+	class="wp-block-gallery has-nested-images columns-default is-cropped columns-2"
+>
+	<!-- wp:image {"id":1421,"sizeSlug":"large","linkDestination":"none"} -->
+	<figure class="wp-block-image size-large">
+		<img
+			src="https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190"
+			alt="Image gallery image"
+			class="wp-image-1421"
+		/>
+	</figure>
+	<!-- /wp:image -->
+
+	<!-- wp:image {"id":1440,"sizeSlug":"large","linkDestination":"none"} -->
+	<figure class="wp-block-image size-large">
+		<img
+			src="https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580"
+			alt="Image gallery image"
+			class="wp-image-1440"
+		/>
+	</figure>
+	<!-- /wp:image -->
 </figure>
-<!-- /wp:core/gallery -->
+<!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery.json b/test/integration/fixtures/blocks/core__gallery.json
index 35cade362ceb9..4935952c27d15 100644
--- a/test/integration/fixtures/blocks/core__gallery.json
+++ b/test/integration/fixtures/blocks/core__gallery.json
@@ -4,25 +4,49 @@
 		"name": "core/gallery",
 		"isValid": true,
 		"attributes": {
-			"images": [
-				{
-					"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",
-					"alt": "title",
-					"caption": ""
-				},
-				{
-					"url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
-					"alt": "title",
-					"caption": ""
-				}
-			],
-			"ids": [ null, null ],
+			"images": [],
+			"ids": [],
+			"shortCodeTransforms": [],
 			"caption": "",
 			"imageCrop": true,
+			"fixedHeight": true,
 			"linkTo": "none",
-			"sizeSlug": "large"
+			"sizeSlug": "large",
+			"allowResize": false,
+			"className": "columns-2"
 		},
-		"innerBlocks": [],
-		"originalContent": "<figure class=\"wp-block-gallery columns-2 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>"
+		"innerBlocks": [
+			{
+				"clientId": "_clientId_0",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190",
+					"alt": "Image gallery image",
+					"caption": "",
+					"id": 1421,
+					"sizeSlug": "large",
+					"linkDestination": "none"
+				},
+				"innerBlocks": [],
+				"originalContent": "<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1421\"\n\t\t/>\n\t</figure>"
+			},
+			{
+				"clientId": "_clientId_1",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580",
+					"alt": "Image gallery image",
+					"caption": "",
+					"id": 1440,
+					"sizeSlug": "large",
+					"linkDestination": "none"
+				},
+				"innerBlocks": [],
+				"originalContent": "<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1440\"\n\t\t/>\n\t</figure>"
+			}
+		],
+		"originalContent": "<figure\n\tclass=\"wp-block-gallery has-nested-images columns-default is-cropped columns-2\"\n>\n\t\n\n\t\n</figure>"
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery.parsed.json b/test/integration/fixtures/blocks/core__gallery.parsed.json
index c75387eef15d5..6ab1b02f4ba33 100644
--- a/test/integration/fixtures/blocks/core__gallery.parsed.json
+++ b/test/integration/fixtures/blocks/core__gallery.parsed.json
@@ -2,12 +2,44 @@
 	{
 		"blockName": "core/gallery",
 		"attrs": {
-			"ids": [ null, null ]
+			"linkTo": "none",
+			"className": "columns-2"
 		},
-		"innerBlocks": [],
-		"innerHTML": "\n<figure class=\"wp-block-gallery columns-2 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>\n",
+		"innerBlocks": [
+			{
+				"blockName": "core/image",
+				"attrs": {
+					"id": 1421,
+					"sizeSlug": "large",
+					"linkDestination": "none"
+				},
+				"innerBlocks": [],
+				"innerHTML": "\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1421\"\n\t\t/>\n\t</figure>\n\t",
+				"innerContent": [
+					"\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1421\"\n\t\t/>\n\t</figure>\n\t"
+				]
+			},
+			{
+				"blockName": "core/image",
+				"attrs": {
+					"id": 1440,
+					"sizeSlug": "large",
+					"linkDestination": "none"
+				},
+				"innerBlocks": [],
+				"innerHTML": "\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1440\"\n\t\t/>\n\t</figure>\n\t",
+				"innerContent": [
+					"\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1440\"\n\t\t/>\n\t</figure>\n\t"
+				]
+			}
+		],
+		"innerHTML": "\n<figure\n\tclass=\"wp-block-gallery has-nested-images columns-default is-cropped columns-2\"\n>\n\t\n\n\t\n</figure>\n",
 		"innerContent": [
-			"\n<figure class=\"wp-block-gallery columns-2 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>\n"
+			"\n<figure\n\tclass=\"wp-block-gallery has-nested-images columns-default is-cropped columns-2\"\n>\n\t",
+			null,
+			"\n\n\t",
+			null,
+			"\n</figure>\n"
 		]
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery.serialized.html b/test/integration/fixtures/blocks/core__gallery.serialized.html
index 62c72702eb57f..6ab87b7c6031d 100644
--- a/test/integration/fixtures/blocks/core__gallery.serialized.html
+++ b/test/integration/fixtures/blocks/core__gallery.serialized.html
@@ -1,3 +1,9 @@
-<!-- wp:gallery {"ids":[null,null],"linkTo":"none"} -->
-<figure class="wp-block-gallery columns-2 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title"/></figure></li><li class="blocks-gallery-item"><figure><img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title"/></figure></li></ul></figure>
+<!-- wp:gallery {"linkTo":"none","className":"columns-2"} -->
+<figure class="wp-block-gallery has-nested-images columns-default is-cropped columns-2"><!-- wp:image {"id":1421,"sizeSlug":"large","linkDestination":"none"} -->
+<figure class="wp-block-image size-large"><img src="https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190" alt="Image gallery image" class="wp-image-1421"/></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"id":1440,"sizeSlug":"large","linkDestination":"none"} -->
+<figure class="wp-block-image size-large"><img src="https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580" alt="Image gallery image" class="wp-image-1440"/></figure>
+<!-- /wp:image --></figure>
 <!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery__columns.html b/test/integration/fixtures/blocks/core__gallery__columns.html
index 6ab9083784937..b70ac2b6f06a8 100644
--- a/test/integration/fixtures/blocks/core__gallery__columns.html
+++ b/test/integration/fixtures/blocks/core__gallery__columns.html
@@ -1,16 +1,23 @@
-<!-- wp:core/gallery {"ids":[null,null],"columns":1} -->
-<figure class="wp-block-gallery columns-1 is-cropped">
-	<ul class="blocks-gallery-grid">
-		<li class="blocks-gallery-item">
-			<figure>
-				<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title" />
-			</figure>
-		</li>
-		<li class="blocks-gallery-item">
-			<figure>
-				<img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title" />
-			</figure>
-		</li>
-	</ul>
+<!-- wp:gallery {"columns":1,"linkTo":"none"} -->
+<figure class="wp-block-gallery has-nested-images columns-1 is-cropped">
+	<!-- wp:image {"id":1421,"sizeSlug":"large","linkDestination":"none","inheritedAttributes":{"linkDestination":true,"linkTarget":true,"sizeSlug":true}} -->
+	<figure class="wp-block-image size-large">
+		<img
+			src="https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190"
+			alt="Image gallery image"
+			class="wp-image-1421"
+		/>
+	</figure>
+	<!-- /wp:image -->
+
+	<!-- wp:image {"id":1440,"sizeSlug":"large","linkDestination":"none","inheritedAttributes":{"linkDestination":true,"linkTarget":true,"sizeSlug":true}} -->
+	<figure class="wp-block-image size-large">
+		<img
+			src="https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580"
+			alt="Image gallery image"
+			class="wp-image-1440"
+		/>
+	</figure>
+	<!-- /wp:image -->
 </figure>
-<!-- /wp:core/gallery -->
+<!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery__columns.json b/test/integration/fixtures/blocks/core__gallery__columns.json
index f1c3a654adf9d..67d2a6e553ef6 100644
--- a/test/integration/fixtures/blocks/core__gallery__columns.json
+++ b/test/integration/fixtures/blocks/core__gallery__columns.json
@@ -4,26 +4,49 @@
 		"name": "core/gallery",
 		"isValid": true,
 		"attributes": {
-			"images": [
-				{
-					"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",
-					"alt": "title",
-					"caption": ""
-				},
-				{
-					"url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
-					"alt": "title",
-					"caption": ""
-				}
-			],
-			"ids": [ null, null ],
+			"images": [],
+			"ids": [],
+			"shortCodeTransforms": [],
 			"columns": 1,
 			"caption": "",
 			"imageCrop": true,
+			"fixedHeight": true,
 			"linkTo": "none",
-			"sizeSlug": "large"
+			"sizeSlug": "large",
+			"allowResize": false
 		},
-		"innerBlocks": [],
-		"originalContent": "<figure class=\"wp-block-gallery columns-1 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>"
+		"innerBlocks": [
+			{
+				"clientId": "_clientId_0",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190",
+					"alt": "Image gallery image",
+					"caption": "",
+					"id": 1421,
+					"sizeSlug": "large",
+					"linkDestination": "none"
+				},
+				"innerBlocks": [],
+				"originalContent": "<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1421\"\n\t\t/>\n\t</figure>"
+			},
+			{
+				"clientId": "_clientId_1",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580",
+					"alt": "Image gallery image",
+					"caption": "",
+					"id": 1440,
+					"sizeSlug": "large",
+					"linkDestination": "none"
+				},
+				"innerBlocks": [],
+				"originalContent": "<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1440\"\n\t\t/>\n\t</figure>"
+			}
+		],
+		"originalContent": "<figure class=\"wp-block-gallery has-nested-images columns-1 is-cropped\">\n\t\n\n\t\n</figure>"
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery__columns.parsed.json b/test/integration/fixtures/blocks/core__gallery__columns.parsed.json
index 933c6f6f11275..f2b53946598ba 100644
--- a/test/integration/fixtures/blocks/core__gallery__columns.parsed.json
+++ b/test/integration/fixtures/blocks/core__gallery__columns.parsed.json
@@ -2,13 +2,54 @@
 	{
 		"blockName": "core/gallery",
 		"attrs": {
-			"ids": [ null, null ],
-			"columns": 1
+			"columns": 1,
+			"linkTo": "none"
 		},
-		"innerBlocks": [],
-		"innerHTML": "\n<figure class=\"wp-block-gallery columns-1 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>\n",
+		"innerBlocks": [
+			{
+				"blockName": "core/image",
+				"attrs": {
+					"id": 1421,
+					"sizeSlug": "large",
+					"linkDestination": "none",
+					"inheritedAttributes": {
+						"linkDestination": true,
+						"linkTarget": true,
+						"sizeSlug": true
+					}
+				},
+				"innerBlocks": [],
+				"innerHTML": "\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1421\"\n\t\t/>\n\t</figure>\n\t",
+				"innerContent": [
+					"\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1421\"\n\t\t/>\n\t</figure>\n\t"
+				]
+			},
+			{
+				"blockName": "core/image",
+				"attrs": {
+					"id": 1440,
+					"sizeSlug": "large",
+					"linkDestination": "none",
+					"inheritedAttributes": {
+						"linkDestination": true,
+						"linkTarget": true,
+						"sizeSlug": true
+					}
+				},
+				"innerBlocks": [],
+				"innerHTML": "\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1440\"\n\t\t/>\n\t</figure>\n\t",
+				"innerContent": [
+					"\n\t<figure class=\"wp-block-image size-large\">\n\t\t<img\n\t\t\tsrc=\"https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580\"\n\t\t\talt=\"Image gallery image\"\n\t\t\tclass=\"wp-image-1440\"\n\t\t/>\n\t</figure>\n\t"
+				]
+			}
+		],
+		"innerHTML": "\n<figure class=\"wp-block-gallery has-nested-images columns-1 is-cropped\">\n\t\n\n\t\n</figure>\n",
 		"innerContent": [
-			"\n<figure class=\"wp-block-gallery columns-1 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>\n"
+			"\n<figure class=\"wp-block-gallery has-nested-images columns-1 is-cropped\">\n\t",
+			null,
+			"\n\n\t",
+			null,
+			"\n</figure>\n"
 		]
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery__columns.serialized.html b/test/integration/fixtures/blocks/core__gallery__columns.serialized.html
index 022a6a9cb7396..a1398677951cf 100644
--- a/test/integration/fixtures/blocks/core__gallery__columns.serialized.html
+++ b/test/integration/fixtures/blocks/core__gallery__columns.serialized.html
@@ -1,3 +1,9 @@
-<!-- wp:gallery {"ids":[null,null],"columns":1,"linkTo":"none"} -->
-<figure class="wp-block-gallery columns-1 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title"/></figure></li><li class="blocks-gallery-item"><figure><img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title"/></figure></li></ul></figure>
+<!-- wp:gallery {"columns":1,"linkTo":"none"} -->
+<figure class="wp-block-gallery has-nested-images columns-1 is-cropped"><!-- wp:image {"id":1421,"sizeSlug":"large","linkDestination":"none"} -->
+<figure class="wp-block-image size-large"><img src="https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190" alt="Image gallery image" class="wp-image-1421"/></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"id":1440,"sizeSlug":"large","linkDestination":"none"} -->
+<figure class="wp-block-image size-large"><img src="https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580" alt="Image gallery image" class="wp-image-1440"/></figure>
+<!-- /wp:image --></figure>
 <!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-1.json b/test/integration/fixtures/blocks/core__gallery__deprecated-1.json
index 3bb14e3d991c0..d274a72a649ef 100644
--- a/test/integration/fixtures/blocks/core__gallery__deprecated-1.json
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-1.json
@@ -4,22 +4,35 @@
 		"name": "core/gallery",
 		"isValid": true,
 		"attributes": {
-			"images": [
-				{
-					"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",
-					"alt": "title"
-				},
-				{
-					"url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
-					"alt": "title"
-				}
-			],
 			"columns": 2,
 			"imageCrop": true,
 			"linkTo": "none",
-			"align": "wide"
+			"allowResize": false
 		},
-		"innerBlocks": [],
+		"innerBlocks": [
+			{
+				"clientId": "_clientId_0",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",
+					"alt": "title",
+					"linkDestination": "none"
+				},
+				"innerBlocks": []
+			},
+			{
+				"clientId": "_clientId_1",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
+					"alt": "title",
+					"linkDestination": "none"
+				},
+				"innerBlocks": []
+			}
+		],
 		"originalContent": "<div class=\"wp-block-gallery columns-2 is-cropped alignwide\">\n\t<figure class=\"blocks-gallery-image\">\n\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t</figure>\n\t<figure class=\"blocks-gallery-image\">\n\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t</figure>\n</div>"
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-1.serialized.html b/test/integration/fixtures/blocks/core__gallery__deprecated-1.serialized.html
index dd51c3ac37cac..45a1ef9a67371 100644
--- a/test/integration/fixtures/blocks/core__gallery__deprecated-1.serialized.html
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-1.serialized.html
@@ -1,3 +1,9 @@
-<!-- wp:gallery {"columns":2,"linkTo":"none","align":"wide"} -->
-<figure class="wp-block-gallery alignwide columns-2 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title"/></figure></li><li class="blocks-gallery-item"><figure><img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title"/></figure></li></ul></figure>
+<!-- wp:gallery {"columns":2,"linkTo":"none"} -->
+<figure class="wp-block-gallery has-nested-images columns-2 is-cropped"><!-- wp:image {"linkDestination":"none"} -->
+<figure class="wp-block-image"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title"/></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"linkDestination":"none"} -->
+<figure class="wp-block-image"><img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title"/></figure>
+<!-- /wp:image --></figure>
 <!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-2.json b/test/integration/fixtures/blocks/core__gallery__deprecated-2.json
index 264c25f18ce1d..7dfc535aeee19 100644
--- a/test/integration/fixtures/blocks/core__gallery__deprecated-2.json
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-2.json
@@ -4,27 +4,39 @@
 		"name": "core/gallery",
 		"isValid": true,
 		"attributes": {
-			"images": [
-				{
+			"columns": 2,
+			"imageCrop": true,
+			"linkTo": "none",
+			"allowResize": false
+		},
+		"innerBlocks": [
+			{
+				"clientId": "_clientId_0",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
 					"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",
 					"alt": "title",
-					"id": "1",
-					"caption": []
+					"caption": [],
+					"id": 1,
+					"linkDestination": "none"
 				},
-				{
+				"innerBlocks": []
+			},
+			{
+				"clientId": "_clientId_1",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
 					"url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
 					"alt": "title",
-					"id": "2",
-					"caption": []
-				}
-			],
-			"ids": [],
-			"columns": 2,
-			"imageCrop": true,
-			"linkTo": "none",
-			"align": "wide"
-		},
-		"innerBlocks": [],
+					"caption": [],
+					"id": 2,
+					"linkDestination": "none"
+				},
+				"innerBlocks": []
+			}
+		],
 		"originalContent": "<ul class=\"wp-block-gallery columns-2 is-cropped alignwide\">\n\t<li class=\"blocks-gallery-item\">\n\t\t<figure>\n\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" data-id=\"1\" alt=\"title\" class=\"wp-image-1\" />\n\t\t</figure>\n\t</li>\n\t<li class=\"blocks-gallery-item\">\n\t\t<figure>\n\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" data-id=\"2\" alt=\"title\" class=\"wp-image-2\" />\n\t\t</figure>\n\t</li>\n</ul>"
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-2.serialized.html b/test/integration/fixtures/blocks/core__gallery__deprecated-2.serialized.html
index bd177026c8c5b..71cbb58536140 100644
--- a/test/integration/fixtures/blocks/core__gallery__deprecated-2.serialized.html
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-2.serialized.html
@@ -1,3 +1,9 @@
-<!-- wp:gallery {"ids":[],"columns":2,"linkTo":"none","align":"wide"} -->
-<figure class="wp-block-gallery alignwide columns-2 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title" data-id="1" class="wp-image-1"/></figure></li><li class="blocks-gallery-item"><figure><img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title" data-id="2" class="wp-image-2"/></figure></li></ul></figure>
+<!-- wp:gallery {"columns":2,"linkTo":"none"} -->
+<figure class="wp-block-gallery has-nested-images columns-2 is-cropped"><!-- wp:image {"id":1,"linkDestination":"none"} -->
+<figure class="wp-block-image"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title" class="wp-image-1"/></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"id":2,"linkDestination":"none"} -->
+<figure class="wp-block-image"><img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title" class="wp-image-2"/></figure>
+<!-- /wp:image --></figure>
 <!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-3.json b/test/integration/fixtures/blocks/core__gallery__deprecated-3.json
index 81357c8b33aa0..ffca81e1f45e3 100644
--- a/test/integration/fixtures/blocks/core__gallery__deprecated-3.json
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-3.json
@@ -4,24 +4,36 @@
 		"name": "core/gallery",
 		"isValid": true,
 		"attributes": {
-			"images": [
-				{
+			"imageCrop": true,
+			"linkTo": "none",
+			"allowResize": false
+		},
+		"innerBlocks": [
+			{
+				"clientId": "_clientId_0",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
 					"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",
 					"alt": "title",
-					"caption": []
+					"caption": [],
+					"linkDestination": "none"
 				},
-				{
+				"innerBlocks": []
+			},
+			{
+				"clientId": "_clientId_1",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
 					"url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
 					"alt": "title",
-					"caption": []
-				}
-			],
-			"ids": [ null, null ],
-			"imageCrop": true,
-			"linkTo": "none",
-			"align": "wide"
-		},
-		"innerBlocks": [],
+					"caption": [],
+					"linkDestination": "none"
+				},
+				"innerBlocks": []
+			}
+		],
 		"originalContent": "<ul class=\"wp-block-gallery columns-2 is-cropped alignwide\">\n\t<li class=\"blocks-gallery-item\">\n\t\t<figure>\n\t\t\t<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==\" alt=\"title\" />\n\t\t</figure>\n\t</li>\n\t<li class=\"blocks-gallery-item\">\n\t\t<figure>\n\t\t\t<img src=\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\" alt=\"title\" />\n\t\t</figure>\n\t</li>\n</ul>"
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-3.serialized.html b/test/integration/fixtures/blocks/core__gallery__deprecated-3.serialized.html
index 0fc203739e6db..3be73e557dbda 100644
--- a/test/integration/fixtures/blocks/core__gallery__deprecated-3.serialized.html
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-3.serialized.html
@@ -1,3 +1,9 @@
-<!-- wp:gallery {"ids":[null,null],"linkTo":"none","align":"wide"} -->
-<figure class="wp-block-gallery alignwide columns-2 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title"/></figure></li><li class="blocks-gallery-item"><figure><img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title"/></figure></li></ul></figure>
+<!-- wp:gallery {"linkTo":"none"} -->
+<figure class="wp-block-gallery has-nested-images columns-default is-cropped"><!-- wp:image {"linkDestination":"none"} -->
+<figure class="wp-block-image"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" alt="title"/></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"linkDestination":"none"} -->
+<figure class="wp-block-image"><img src="data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" alt="title"/></figure>
+<!-- /wp:image --></figure>
 <!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-4.json b/test/integration/fixtures/blocks/core__gallery__deprecated-4.json
index d62d125dc2ea6..bb566f74f6e00 100644
--- a/test/integration/fixtures/blocks/core__gallery__deprecated-4.json
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-4.json
@@ -4,33 +4,52 @@
 		"name": "core/gallery",
 		"isValid": true,
 		"attributes": {
-			"images": [
-				{
+			"caption": "",
+			"imageCrop": true,
+			"linkTo": "none",
+			"allowResize": false
+		},
+		"innerBlocks": [
+			{
+				"clientId": "_clientId_0",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
 					"url": "https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190",
 					"alt": "",
-					"id": "1421",
-					"caption": ""
+					"caption": "",
+					"id": 1421,
+					"linkDestination": "none"
 				},
-				{
+				"innerBlocks": []
+			},
+			{
+				"clientId": "_clientId_1",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
 					"url": "https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580",
 					"alt": "",
-					"id": "1440",
-					"caption": ""
+					"caption": "",
+					"id": 1440,
+					"linkDestination": "none"
 				},
-				{
+				"innerBlocks": []
+			},
+			{
+				"clientId": "_clientId_2",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
 					"url": "https://sergioestevaofolio.files.wordpress.com/2017/05/cropped-l1005945-2-2.jpg?w=580",
 					"alt": "",
-					"id": "1362",
-					"caption": ""
-				}
-			],
-			"ids": [ 1421, 1440, 1362 ],
-			"caption": "",
-			"imageCrop": true,
-			"linkTo": "none",
-			"align": "wide"
-		},
-		"innerBlocks": [],
+					"caption": "",
+					"id": 1362,
+					"linkDestination": "none"
+				},
+				"innerBlocks": []
+			}
+		],
 		"originalContent": "<figure class=\"wp-block-gallery columns-3 is-cropped alignwide\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190\" alt=\"\" data-id=\"1421\" class=\"wp-image-1421\"/>\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580\" alt=\"\" data-id=\"1440\" class=\"wp-image-1440\"/>\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<img src=\"https://sergioestevaofolio.files.wordpress.com/2017/05/cropped-l1005945-2-2.jpg?w=580\" alt=\"\" data-id=\"1362\" class=\"wp-image-1362\"/>\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>"
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-4.serialized.html b/test/integration/fixtures/blocks/core__gallery__deprecated-4.serialized.html
index e9bf8294c2f7e..5357a43951dd4 100644
--- a/test/integration/fixtures/blocks/core__gallery__deprecated-4.serialized.html
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-4.serialized.html
@@ -1,3 +1,13 @@
-<!-- wp:gallery {"ids":[1421,1440,1362],"linkTo":"none","align":"wide"} -->
-<figure class="wp-block-gallery alignwide columns-3 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img src="https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190" alt="" data-id="1421" class="wp-image-1421"/></figure></li><li class="blocks-gallery-item"><figure><img src="https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580" alt="" data-id="1440" class="wp-image-1440"/></figure></li><li class="blocks-gallery-item"><figure><img src="https://sergioestevaofolio.files.wordpress.com/2017/05/cropped-l1005945-2-2.jpg?w=580" alt="" data-id="1362" class="wp-image-1362"/></figure></li></ul></figure>
+<!-- wp:gallery {"linkTo":"none"} -->
+<figure class="wp-block-gallery has-nested-images columns-default is-cropped"><!-- wp:image {"id":1421,"linkDestination":"none"} -->
+<figure class="wp-block-image"><img src="https://sergioestevaofolio.files.wordpress.com/2016/09/cropped-img_9054-1.jpg?w=190" alt="" class="wp-image-1421"/></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"id":1440,"linkDestination":"none"} -->
+<figure class="wp-block-image"><img src="https://sergioestevaofolio.files.wordpress.com/2017/09/cropped-l1001498-1.jpg?w=580" alt="" class="wp-image-1440"/></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"id":1362,"linkDestination":"none"} -->
+<figure class="wp-block-image"><img src="https://sergioestevaofolio.files.wordpress.com/2017/05/cropped-l1005945-2-2.jpg?w=580" alt="" class="wp-image-1362"/></figure>
+<!-- /wp:image --></figure>
 <!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-5.json b/test/integration/fixtures/blocks/core__gallery__deprecated-5.json
index 66d6c1d8ac021..7a1021f6c8f59 100644
--- a/test/integration/fixtures/blocks/core__gallery__deprecated-5.json
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-5.json
@@ -4,39 +4,59 @@
 		"name": "core/gallery",
 		"isValid": true,
 		"attributes": {
-			"images": [
-				{
+			"caption": "",
+			"imageCrop": true,
+			"linkTo": "media",
+			"sizeSlug": "large",
+			"allowResize": false
+		},
+		"innerBlocks": [
+			{
+				"clientId": "_clientId_0",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
 					"url": "http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg",
-					"fullUrl": "http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg",
-					"link": "http://wptest.local/classic/test-image-3/",
 					"alt": "",
-					"id": "705",
-					"caption": ""
+					"caption": "",
+					"href": "http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg",
+					"id": 705,
+					"sizeSlug": "large",
+					"linkDestination": "media"
 				},
-				{
+				"innerBlocks": []
+			},
+			{
+				"clientId": "_clientId_1",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
 					"url": "http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg",
-					"fullUrl": "http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg",
-					"link": "http://wptest.local/test-image-2/",
 					"alt": "",
-					"id": "704",
-					"caption": ""
+					"caption": "",
+					"href": "http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg",
+					"id": 704,
+					"sizeSlug": "large",
+					"linkDestination": "media"
 				},
-				{
+				"innerBlocks": []
+			},
+			{
+				"clientId": "_clientId_2",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
 					"url": "http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg",
-					"fullUrl": "http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg",
-					"link": "http://wptest.local/test-image/",
 					"alt": "",
-					"id": "703",
-					"caption": ""
-				}
-			],
-			"ids": [ 705, 704, 703 ],
-			"caption": "",
-			"imageCrop": true,
-			"linkTo": "file",
-			"sizeSlug": "large"
-		},
-		"innerBlocks": [],
+					"caption": "",
+					"href": "http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg",
+					"id": 703,
+					"sizeSlug": "large",
+					"linkDestination": "media"
+				},
+				"innerBlocks": []
+			}
+		],
 		"originalContent": "<figure class=\"wp-block-gallery columns-3 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a href=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg\" >\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"705\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/classic/test-image-3/\"\n\t\t\t\t\t\tclass=\"wp-image-705\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a href=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg\" >\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"704\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/test-image-2/\"\n\t\t\t\t\t\tclass=\"wp-image-704\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a href=\"http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg\" >\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"703\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/test-image/\"\n\t\t\t\t\t\tclass=\"wp-image-703\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>"
 	}
 ]
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-5.serialized.html b/test/integration/fixtures/blocks/core__gallery__deprecated-5.serialized.html
index f3afe00b34a31..74fb0a124f8d7 100644
--- a/test/integration/fixtures/blocks/core__gallery__deprecated-5.serialized.html
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-5.serialized.html
@@ -1,3 +1,13 @@
-<!-- wp:gallery {"ids":[705,704,703],"linkTo":"file"} -->
-<figure class="wp-block-gallery columns-3 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><a href="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg"><img src="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg" alt="" data-id="705" data-full-url="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg" data-link="http://wptest.local/classic/test-image-3/" class="wp-image-705"/></a></figure></li><li class="blocks-gallery-item"><figure><a href="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg"><img src="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg" alt="" data-id="704" data-full-url="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg" data-link="http://wptest.local/test-image-2/" class="wp-image-704"/></a></figure></li><li class="blocks-gallery-item"><figure><a href="http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg"><img src="http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg" alt="" data-id="703" data-full-url="http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg" data-link="http://wptest.local/test-image/" class="wp-image-703"/></a></figure></li></ul></figure>
+<!-- wp:gallery {"linkTo":"media"} -->
+<figure class="wp-block-gallery has-nested-images columns-default is-cropped"><!-- wp:image {"id":705,"sizeSlug":"large","linkDestination":"media"} -->
+<figure class="wp-block-image size-large"><a href="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg"><img src="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg" alt="" class="wp-image-705"/></a></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"id":704,"sizeSlug":"large","linkDestination":"media"} -->
+<figure class="wp-block-image size-large"><a href="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg"><img src="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg" alt="" class="wp-image-704"/></a></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"id":703,"sizeSlug":"large","linkDestination":"media"} -->
+<figure class="wp-block-image size-large"><a href="http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg"><img src="http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg" alt="" class="wp-image-703"/></a></figure>
+<!-- /wp:image --></figure>
 <!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-6.html b/test/integration/fixtures/blocks/core__gallery__deprecated-6.html
new file mode 100644
index 0000000000000..0dcf604233435
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-6.html
@@ -0,0 +1,54 @@
+<!-- wp:gallery {"ids":[705,704,703],"linkTo":"media"} -->
+<figure class="wp-block-gallery columns-3 is-cropped">
+	<ul class="blocks-gallery-grid">
+		<li class="blocks-gallery-item">
+			<figure>
+				<a
+					href="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg"
+				>
+					<img
+						src="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg"
+						alt=""
+						data-id="705"
+						data-full-url="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg"
+						data-link="http://wptest.local/classic/test-image-3/"
+						class="wp-image-705"
+					/>
+				</a>
+			</figure>
+		</li>
+		<li class="blocks-gallery-item">
+			<figure>
+				<a
+					href="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg"
+				>
+					<img
+						src="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg"
+						alt=""
+						data-id="704"
+						data-full-url="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg"
+						data-link="http://wptest.local/test-image-2/"
+						class="wp-image-704"
+					/>
+				</a>
+			</figure>
+		</li>
+		<li class="blocks-gallery-item">
+			<figure>
+				<a
+					href="http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg"
+				>
+					<img
+						src="http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg"
+						alt=""
+						data-id="703"
+						data-full-url="http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg"
+						data-link="http://wptest.local/test-image/"
+						class="wp-image-703"
+					/>
+				</a>
+			</figure>
+		</li>
+	</ul>
+</figure>
+<!-- /wp:gallery -->
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-6.json b/test/integration/fixtures/blocks/core__gallery__deprecated-6.json
new file mode 100644
index 0000000000000..a9d2a3d393a96
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-6.json
@@ -0,0 +1,62 @@
+[
+	{
+		"clientId": "_clientId_0",
+		"name": "core/gallery",
+		"isValid": true,
+		"attributes": {
+			"caption": "",
+			"imageCrop": true,
+			"linkTo": "media",
+			"sizeSlug": "large",
+			"allowResize": false
+		},
+		"innerBlocks": [
+			{
+				"clientId": "_clientId_0",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg",
+					"alt": "",
+					"caption": "",
+					"href": "http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg",
+					"id": 705,
+					"sizeSlug": "large",
+					"linkDestination": "media"
+				},
+				"innerBlocks": []
+			},
+			{
+				"clientId": "_clientId_1",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg",
+					"alt": "",
+					"caption": "",
+					"href": "http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg",
+					"id": 704,
+					"sizeSlug": "large",
+					"linkDestination": "media"
+				},
+				"innerBlocks": []
+			},
+			{
+				"clientId": "_clientId_2",
+				"name": "core/image",
+				"isValid": true,
+				"attributes": {
+					"url": "http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg",
+					"alt": "",
+					"caption": "",
+					"href": "http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg",
+					"id": 703,
+					"sizeSlug": "large",
+					"linkDestination": "media"
+				},
+				"innerBlocks": []
+			}
+		],
+		"originalContent": "<figure class=\"wp-block-gallery columns-3 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a\n\t\t\t\t\thref=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg\"\n\t\t\t\t>\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"705\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/classic/test-image-3/\"\n\t\t\t\t\t\tclass=\"wp-image-705\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a\n\t\t\t\t\thref=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg\"\n\t\t\t\t>\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"704\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/test-image-2/\"\n\t\t\t\t\t\tclass=\"wp-image-704\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a\n\t\t\t\t\thref=\"http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg\"\n\t\t\t\t>\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"703\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/test-image/\"\n\t\t\t\t\t\tclass=\"wp-image-703\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>"
+	}
+]
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-6.parsed.json b/test/integration/fixtures/blocks/core__gallery__deprecated-6.parsed.json
new file mode 100644
index 0000000000000..72b400f283adf
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-6.parsed.json
@@ -0,0 +1,14 @@
+[
+	{
+		"blockName": "core/gallery",
+		"attrs": {
+			"ids": [ 705, 704, 703 ],
+			"linkTo": "media"
+		},
+		"innerBlocks": [],
+		"innerHTML": "\n<figure class=\"wp-block-gallery columns-3 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a\n\t\t\t\t\thref=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg\"\n\t\t\t\t>\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"705\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/classic/test-image-3/\"\n\t\t\t\t\t\tclass=\"wp-image-705\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a\n\t\t\t\t\thref=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg\"\n\t\t\t\t>\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"704\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/test-image-2/\"\n\t\t\t\t\t\tclass=\"wp-image-704\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a\n\t\t\t\t\thref=\"http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg\"\n\t\t\t\t>\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"703\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/test-image/\"\n\t\t\t\t\t\tclass=\"wp-image-703\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>\n",
+		"innerContent": [
+			"\n<figure class=\"wp-block-gallery columns-3 is-cropped\">\n\t<ul class=\"blocks-gallery-grid\">\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a\n\t\t\t\t\thref=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg\"\n\t\t\t\t>\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"705\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/classic/test-image-3/\"\n\t\t\t\t\t\tclass=\"wp-image-705\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a\n\t\t\t\t\thref=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg\"\n\t\t\t\t>\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"704\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/09/test-image-edited-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/test-image-2/\"\n\t\t\t\t\t\tclass=\"wp-image-704\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t\t<li class=\"blocks-gallery-item\">\n\t\t\t<figure>\n\t\t\t\t<a\n\t\t\t\t\thref=\"http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg\"\n\t\t\t\t>\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc=\"http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg\"\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tdata-id=\"703\"\n\t\t\t\t\t\tdata-full-url=\"http://wptest.local/wp-content/uploads/2020/04/test-image-scaled.jpg\"\n\t\t\t\t\t\tdata-link=\"http://wptest.local/test-image/\"\n\t\t\t\t\t\tclass=\"wp-image-703\"\n\t\t\t\t\t/>\n\t\t\t\t</a>\n\t\t\t</figure>\n\t\t</li>\n\t</ul>\n</figure>\n"
+		]
+	}
+]
diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-6.serialized.html b/test/integration/fixtures/blocks/core__gallery__deprecated-6.serialized.html
new file mode 100644
index 0000000000000..74fb0a124f8d7
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__gallery__deprecated-6.serialized.html
@@ -0,0 +1,13 @@
+<!-- wp:gallery {"linkTo":"media"} -->
+<figure class="wp-block-gallery has-nested-images columns-default is-cropped"><!-- wp:image {"id":705,"sizeSlug":"large","linkDestination":"media"} -->
+<figure class="wp-block-image size-large"><a href="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg"><img src="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1-682x1024.jpg" alt="" class="wp-image-705"/></a></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"id":704,"sizeSlug":"large","linkDestination":"media"} -->
+<figure class="wp-block-image size-large"><a href="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg"><img src="http://wptest.local/wp-content/uploads/2020/09/test-image-edited-1024x682.jpg" alt="" class="wp-image-704"/></a></figure>
+<!-- /wp:image -->
+
+<!-- wp:image {"id":703,"sizeSlug":"large","linkDestination":"media"} -->
+<figure class="wp-block-image size-large"><a href="http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg"><img src="http://wptest.local/wp-content/uploads/2020/04/test-image-1024x683.jpg" alt="" class="wp-image-703"/></a></figure>
+<!-- /wp:image --></figure>
+<!-- /wp:gallery -->

From 40af59fd7a16ad84b20139093408b15c4fc997f2 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Thu, 11 Nov 2021 12:49:18 +1300
Subject: [PATCH 09/30] Fix unit tests

---
 .../__snapshots__/blocks-raw-handling.test.js.snap            | 4 ++--
 .../fixtures/documents/shortcode-matching-out.html            | 4 ++--
 test/integration/fixtures/documents/wordpress-out.html        | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/test/integration/__snapshots__/blocks-raw-handling.test.js.snap b/test/integration/__snapshots__/blocks-raw-handling.test.js.snap
index 2a4ddbf8f5efa..6e3a6b8a84795 100644
--- a/test/integration/__snapshots__/blocks-raw-handling.test.js.snap
+++ b/test/integration/__snapshots__/blocks-raw-handling.test.js.snap
@@ -57,8 +57,8 @@ exports[`rawHandler should convert HTML post to blocks with minimal content chan
 <h3>Shortcode</h3>
 <!-- /wp:heading -->
 
-<!-- wp:gallery {\\"ids\\":[1],\\"columns\\":3,\\"linkTo\\":\\"post\\"} -->
-<figure class=\\"wp-block-gallery columns-3 is-cropped\\"><ul class=\\"blocks-gallery-grid\\"><li class=\\"blocks-gallery-item\\"><figure><img data-id=\\"1\\" class=\\"wp-image-1\\"/></figure></li></ul></figure>
+<!-- wp:gallery {\\"shortCodeTransforms\\":[{\\"id\\":1}],\\"columns\\":3,\\"linkTo\\":\\"none\\"} -->
+<figure class=\\"wp-block-gallery has-nested-images columns-3 is-cropped\\"></figure>
 <!-- /wp:gallery -->
 
 <!-- wp:html -->
diff --git a/test/integration/fixtures/documents/shortcode-matching-out.html b/test/integration/fixtures/documents/shortcode-matching-out.html
index 30a0e0679656b..3930f3ff885fe 100644
--- a/test/integration/fixtures/documents/shortcode-matching-out.html
+++ b/test/integration/fixtures/documents/shortcode-matching-out.html
@@ -1,7 +1,7 @@
 <!-- wp:test/gallery {"ids":[40,41,42]} /-->
 
-<!-- wp:gallery {"ids":[1000],"columns":3,"linkTo":"post"} -->
-<figure class="wp-block-gallery columns-3 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img data-id="1000" class="wp-image-1000"/></figure></li></ul></figure>
+<!-- wp:gallery {"shortCodeTransforms":[{"id":1000}],"columns":3,"linkTo":"none"} -->
+<figure class="wp-block-gallery has-nested-images columns-3 is-cropped"></figure>
 <!-- /wp:gallery -->
 
 <!-- wp:test/gallery {"ids":[42]} /-->
diff --git a/test/integration/fixtures/documents/wordpress-out.html b/test/integration/fixtures/documents/wordpress-out.html
index 159244b27659d..42087da595b2a 100644
--- a/test/integration/fixtures/documents/wordpress-out.html
+++ b/test/integration/fixtures/documents/wordpress-out.html
@@ -18,8 +18,8 @@ <h3>More tag</h3>
 <h3>Shortcode</h3>
 <!-- /wp:heading -->
 
-<!-- wp:gallery {"ids":[1],"columns":3,"linkTo":"post"} -->
-<figure class="wp-block-gallery columns-3 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img data-id="1" class="wp-image-1"/></figure></li></ul></figure>
+<!-- wp:gallery {"shortCodeTransforms":[{"id":1}],"columns":3,"linkTo":"none"} -->
+<figure class="wp-block-gallery has-nested-images columns-3 is-cropped"></figure>
 <!-- /wp:gallery -->
 
 <!-- wp:image {"id":5114} -->

From f5bed906407f9411fcd4574c5854acd30002a77b Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Thu, 11 Nov 2021 16:02:15 +1300
Subject: [PATCH 10/30] Fix issue with link destination not being set when
 block migrated

---
 packages/block-library/src/gallery/edit.js | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js
index e7cbe537c4922..492275995ca26 100644
--- a/packages/block-library/src/gallery/edit.js
+++ b/packages/block-library/src/gallery/edit.js
@@ -154,7 +154,7 @@ function GalleryEdit( props ) {
 	useEffect( () => {
 		newImages?.forEach( ( newImage ) => {
 			updateBlockAttributes( newImage.clientId, {
-				...buildImageAttributes( false, newImage.attributes ),
+				...buildImageAttributes( newImage.attributes ),
 				id: newImage.id,
 				align: undefined,
 			} );
@@ -186,26 +186,24 @@ function GalleryEdit( props ) {
 	 * it already existed in the gallery. If the image is in fact new, we need
 	 * to apply the gallery's current settings to the image.
 	 *
-	 * @param {Object} existingBlock Existing Image block that still exists after gallery update.
-	 * @param {Object} image         Media object for the actual image.
-	 * @return {Object}               Attributes to set on the new image block.
+	 * @param {Object} imageAttributes Media object for the actual image.
+	 * @return {Object}                Attributes to set on the new image block.
 	 */
-	function buildImageAttributes( existingBlock, image ) {
-		if ( existingBlock ) {
-			return existingBlock.attributes;
-		}
+	function buildImageAttributes( imageAttributes ) {
+		const image = imageAttributes.id
+			? find( imageData, { id: imageAttributes.id } )
+			: null;
 
 		let newClassName;
-		if ( image.className && image.className !== '' ) {
-			newClassName = image.className;
+		if ( imageAttributes.className && imageAttributes.className !== '' ) {
+			newClassName = imageAttributes.className;
 		} else {
 			newClassName = preferredStyle
 				? `is-style-${ preferredStyle }`
 				: undefined;
 		}
-
 		return {
-			...pickRelevantMediaFiles( image, sizeSlug ),
+			...pickRelevantMediaFiles( imageAttributes, sizeSlug ),
 			...getHrefAndDestination( image, linkTo ),
 			...getUpdatedLinkTargetSettings( linkTarget, attributes ),
 			className: newClassName,

From 6cbc1f78971fb1d71c68b90d8edaf68881fe8308 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Tue, 16 Nov 2021 14:32:00 +1300
Subject: [PATCH 11/30] Check that we have a v1 gallery block before we send
 content through the v1 save function

---
 packages/block-library/src/gallery/save.js   | 5 ++++-
 packages/block-library/src/gallery/shared.js | 5 +----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/packages/block-library/src/gallery/save.js b/packages/block-library/src/gallery/save.js
index 655a1823020fc..a07b024fb9787 100644
--- a/packages/block-library/src/gallery/save.js
+++ b/packages/block-library/src/gallery/save.js
@@ -19,7 +19,10 @@ import saveWithoutInnerBlocks from './v1/save';
 import { isGalleryV2Enabled } from './shared';
 
 export default function saveWithInnerBlocks( { attributes } ) {
-	if ( ! isGalleryV2Enabled() ) {
+	if (
+		! isGalleryV2Enabled() &&
+		( attributes?.ids?.length > 0 || attributes?.images?.length > 0 )
+	) {
 		return saveWithoutInnerBlocks( { attributes } );
 	}
 
diff --git a/packages/block-library/src/gallery/shared.js b/packages/block-library/src/gallery/shared.js
index a6a767e0c9407..ad110b3c9c074 100644
--- a/packages/block-library/src/gallery/shared.js
+++ b/packages/block-library/src/gallery/shared.js
@@ -39,9 +39,6 @@ export function isGalleryV2Enabled() {
 	}
 
 	const settings = select( blockEditorStore ).getSettings();
-	if ( settings.__unstableGalleryWithImageBlocks ) {
-		return true;
-	}
 
-	return false;
+	return !! settings.__unstableGalleryWithImageBlocks;
 }

From e79cdbef66fefbb818e3a77ae9fc2770c00bf962 Mon Sep 17 00:00:00 2001
From: Matthew Kevins <mmkevins@yahoo.com>
Date: Tue, 16 Nov 2021 12:26:21 +1000
Subject: [PATCH 12/30] Use `window.wp` global instead of store for gallery
 flag

To resolve a race with the deprecations and asynchronous store updates
for the gallery flag, we can use a property on the global wp object
instead.
---
 packages/block-library/src/gallery/shared.js  | 15 +--------------
 .../src/components/provider/index.native.js   | 19 ++++++++++---------
 2 files changed, 11 insertions(+), 23 deletions(-)

diff --git a/packages/block-library/src/gallery/shared.js b/packages/block-library/src/gallery/shared.js
index ad110b3c9c074..8c23232bb8699 100644
--- a/packages/block-library/src/gallery/shared.js
+++ b/packages/block-library/src/gallery/shared.js
@@ -3,13 +3,6 @@
  */
 import { get, pick } from 'lodash';
 
-/**
- * WordPress dependencies
- */
-import { Platform } from '@wordpress/element';
-import { select } from '@wordpress/data';
-import { store as blockEditorStore } from '@wordpress/block-editor';
-
 export function defaultColumnsNumber( imageCount ) {
 	return imageCount ? Math.min( 3, imageCount ) : 3;
 }
@@ -34,11 +27,5 @@ export function isGalleryV2Enabled() {
 		return true;
 	}
 
-	if ( Platform.isWeb && window.wp.galleryBlockV2Enabled ) {
-		return true;
-	}
-
-	const settings = select( blockEditorStore ).getSettings();
-
-	return !! settings.__unstableGalleryWithImageBlocks;
+	return window.wp.galleryBlockV2Enabled;
 }
diff --git a/packages/editor/src/components/provider/index.native.js b/packages/editor/src/components/provider/index.native.js
index 059b98eb45937..4fa47df6a58b5 100644
--- a/packages/editor/src/components/provider/index.native.js
+++ b/packages/editor/src/components/provider/index.native.js
@@ -64,7 +64,7 @@ import { EditorHelpTopics } from '@wordpress/editor';
 import EditorProvider from './index.js';
 
 class NativeEditorProvider extends Component {
-	constructor() {
+	constructor( props ) {
 		super( ...arguments );
 
 		// Keep a local reference to `post` to detect changes
@@ -75,6 +75,10 @@ class NativeEditorProvider extends Component {
 			this.post.type,
 			this.post
 		);
+
+		// need to set this globally to avoid race with deprecations
+		window.wp.galleryBlockV2Enabled = props.galleryWithImageBlocks;
+
 		this.getEditorSettings = memize(
 			( settings, capabilities ) => ( {
 				...settings,
@@ -97,9 +101,11 @@ class NativeEditorProvider extends Component {
 			galleryWithImageBlocks,
 		} = this.props;
 
+		// TODO: remove this as unnecessary since we are setting in constructor?
+		window.wp.galleryBlockV2Enabled = galleryWithImageBlocks;
+
 		updateSettings( {
 			...capabilities,
-			...{ __unstableGalleryWithImageBlocks: galleryWithImageBlocks },
 			...this.getThemeColors( this.props ),
 			locale,
 		} );
@@ -150,13 +156,8 @@ class NativeEditorProvider extends Component {
 
 		this.subscriptionParentUpdateEditorSettings = subscribeUpdateEditorSettings(
 			( editorSettings ) => {
-				updateSettings( {
-					...{
-						__unstableGalleryWithImageBlocks:
-							editorSettings.galleryWithImageBlocks,
-					},
-					...this.getThemeColors( editorSettings ),
-				} );
+				window.wp.galleryBlockV2Enabled = galleryWithImageBlocks;
+				updateSettings( { ...this.getThemeColors( editorSettings ) } );
 			}
 		);
 

From 857d01bdb1315fc6cf207accb3dd5a659f804d8f Mon Sep 17 00:00:00 2001
From: Matthew Kevins <mmkevins@yahoo.com>
Date: Tue, 16 Nov 2021 12:39:36 +1000
Subject: [PATCH 13/30] Remove unnecessary destructuring

This isn't necessary, since `getThemeColros` already returns a new
object.
---
 packages/editor/src/components/provider/index.native.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/editor/src/components/provider/index.native.js b/packages/editor/src/components/provider/index.native.js
index 4fa47df6a58b5..52d4d8ab3555e 100644
--- a/packages/editor/src/components/provider/index.native.js
+++ b/packages/editor/src/components/provider/index.native.js
@@ -157,7 +157,7 @@ class NativeEditorProvider extends Component {
 		this.subscriptionParentUpdateEditorSettings = subscribeUpdateEditorSettings(
 			( editorSettings ) => {
 				window.wp.galleryBlockV2Enabled = galleryWithImageBlocks;
-				updateSettings( { ...this.getThemeColors( editorSettings ) } );
+				updateSettings( this.getThemeColors( editorSettings ) );
 			}
 		);
 

From 970f717af87d23ac856e2a5e0b3e71a0f7cd8583 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Tue, 16 Nov 2021 16:20:41 +1300
Subject: [PATCH 14/30] Move the setting of wp.galleryBlockV2Enabled to
 directly after wp-dom-ready to be sure it is set well ahead of the block
 deprecations running

---
 lib/compat.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/compat.php b/lib/compat.php
index 80bae7f52551d..8f8d527020227 100644
--- a/lib/compat.php
+++ b/lib/compat.php
@@ -209,7 +209,7 @@ function gutenberg_check_gallery_block_v2_compatibility() {
 	$v2_gallery_enabled = boolval( 1 !== $use_balance_tags || is_wp_version_compatible( '5.9' ) ) ? 'true' : 'false';
 
 	wp_add_inline_script(
-		'wp-blocks',
+		'wp-dom-ready',
 		'wp.galleryBlockV2Enabled = ' . $v2_gallery_enabled . ';',
 		'after'
 	);

From 2054a3191ebfe1eee003468479e53057b9b5d261 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Wed, 17 Nov 2021 13:19:22 +1300
Subject: [PATCH 15/30] Add optional chaining operator to prevent error if
 images attribute not defined

---
 packages/block-library/src/gallery/deprecated.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/block-library/src/gallery/deprecated.js b/packages/block-library/src/gallery/deprecated.js
index fde1ae5960e0c..6c4150d9419b5 100644
--- a/packages/block-library/src/gallery/deprecated.js
+++ b/packages/block-library/src/gallery/deprecated.js
@@ -34,7 +34,7 @@ const DEPRECATED_LINK_DESTINATION_ATTACHMENT = 'post';
  * @return {number}           Default number of columns for the gallery.
  */
 export function defaultColumnsNumberV1( attributes ) {
-	return Math.min( 3, attributes.images.length );
+	return Math.min( 3, attributes?.images?.length );
 }
 
 /**

From 3b416e8f8f3fbde43f9afb01337b1dff69ad8a82 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Wed, 17 Nov 2021 15:00:06 +1300
Subject: [PATCH 16/30] Simplify deprecations, etc. by always using using v1
 methods if use_balanceTags is on regardless of content format

---
 .../block-library/src/gallery/edit-wrapper.js | 28 ++++---------------
 packages/block-library/src/gallery/save.js    |  5 +---
 2 files changed, 7 insertions(+), 26 deletions(-)

diff --git a/packages/block-library/src/gallery/edit-wrapper.js b/packages/block-library/src/gallery/edit-wrapper.js
index 2f3638f7fe58c..2c81271902d31 100644
--- a/packages/block-library/src/gallery/edit-wrapper.js
+++ b/packages/block-library/src/gallery/edit-wrapper.js
@@ -1,8 +1,8 @@
 /**
  * WordPress dependencies
  */
-import { store as blockEditorStore } from '@wordpress/block-editor';
-import { useSelect } from '@wordpress/data';
+import { compose } from '@wordpress/compose';
+import { withNotices } from '@wordpress/components';
 
 /**
  * Internal dependencies
@@ -16,28 +16,12 @@ import { isGalleryV2Enabled } from './shared';
  * or the refactored version with InnerBlocks. This is to prevent conditional
  * use of hooks lint errors if adding this logic to the top of the edit component.
  */
-export default function GalleryEditWrapper( props ) {
-	const { attributes, clientId } = props;
-
-	const innerBlockImages = useSelect(
-		( select ) => {
-			return select( blockEditorStore ).getBlock( clientId )?.innerBlocks;
-		},
-		[ clientId ]
-	);
-
-	// This logic is used to infer version information from content with higher
-	// precedence than the flag. New galleries (and existing empty galleries) will
-	// honor the flag.
-	const hasNewVersionContent = !! innerBlockImages?.length;
-	const hasOldVersionContent =
-		0 < attributes?.ids?.length || 0 < attributes?.images?.length;
-	if (
-		hasOldVersionContent ||
-		( ! hasNewVersionContent && ! isGalleryV2Enabled() )
-	) {
+function GalleryEditWrapper( props ) {
+	if ( ! isGalleryV2Enabled() ) {
 		return <EditWithoutInnerBlocks { ...props } />;
 	}
 
 	return <EditWithInnerBlocks { ...props } />;
 }
+
+export default compose( [ withNotices ] )( GalleryEditWrapper );
diff --git a/packages/block-library/src/gallery/save.js b/packages/block-library/src/gallery/save.js
index a07b024fb9787..655a1823020fc 100644
--- a/packages/block-library/src/gallery/save.js
+++ b/packages/block-library/src/gallery/save.js
@@ -19,10 +19,7 @@ import saveWithoutInnerBlocks from './v1/save';
 import { isGalleryV2Enabled } from './shared';
 
 export default function saveWithInnerBlocks( { attributes } ) {
-	if (
-		! isGalleryV2Enabled() &&
-		( attributes?.ids?.length > 0 || attributes?.images?.length > 0 )
-	) {
+	if ( ! isGalleryV2Enabled() ) {
 		return saveWithoutInnerBlocks( { attributes } );
 	}
 

From d191bcbed14b57f22c33cbe3797a99ab6c8bb784 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Wed, 17 Nov 2021 15:03:32 +1300
Subject: [PATCH 17/30] Fix potential issue with attributes not being returned
 from v6 deprecation

---
 packages/block-library/src/gallery/deprecated.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/block-library/src/gallery/deprecated.js b/packages/block-library/src/gallery/deprecated.js
index 6c4150d9419b5..001bbba8ea836 100644
--- a/packages/block-library/src/gallery/deprecated.js
+++ b/packages/block-library/src/gallery/deprecated.js
@@ -743,6 +743,7 @@ const v3 = {
 		if ( isGalleryV2Enabled() ) {
 			return runV2Migration( attributes );
 		}
+		return attributes;
 	},
 };
 const v2 = {

From 7700d5c26f9c30c0663c35bec5ac238254bc336b Mon Sep 17 00:00:00 2001
From: Matthew Kevins <mmkevins@yahoo.com>
Date: Fri, 19 Nov 2021 14:53:03 +1000
Subject: [PATCH 18/30] Move assignment of gallery global flag to native Editor

This moves the setting of the global flag higher in the view hierarchy,
since the provider's constructor is still too late (parsing code invokes
the deprecations before even the provider is instantiated).
---
 packages/edit-post/src/editor.native.js                 | 3 +++
 packages/editor/src/components/provider/index.native.js | 8 +-------
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/packages/edit-post/src/editor.native.js b/packages/edit-post/src/editor.native.js
index ea9dbf9eeaab0..b045724a0459d 100644
--- a/packages/edit-post/src/editor.native.js
+++ b/packages/edit-post/src/editor.native.js
@@ -30,6 +30,9 @@ class Editor extends Component {
 	constructor( props ) {
 		super( ...arguments );
 
+		// need to set this globally to avoid race with deprecations
+		window.wp.galleryBlockV2Enabled = props.galleryWithImageBlocks;
+
 		if ( props.initialHtmlModeEnabled && props.mode === 'visual' ) {
 			// enable html mode if the initial mode the parent wants it but we're not already in it
 			this.props.switchEditorMode( 'text' );
diff --git a/packages/editor/src/components/provider/index.native.js b/packages/editor/src/components/provider/index.native.js
index 52d4d8ab3555e..1a5eda37a07aa 100644
--- a/packages/editor/src/components/provider/index.native.js
+++ b/packages/editor/src/components/provider/index.native.js
@@ -64,7 +64,7 @@ import { EditorHelpTopics } from '@wordpress/editor';
 import EditorProvider from './index.js';
 
 class NativeEditorProvider extends Component {
-	constructor( props ) {
+	constructor() {
 		super( ...arguments );
 
 		// Keep a local reference to `post` to detect changes
@@ -76,9 +76,6 @@ class NativeEditorProvider extends Component {
 			this.post
 		);
 
-		// need to set this globally to avoid race with deprecations
-		window.wp.galleryBlockV2Enabled = props.galleryWithImageBlocks;
-
 		this.getEditorSettings = memize(
 			( settings, capabilities ) => ( {
 				...settings,
@@ -101,9 +98,6 @@ class NativeEditorProvider extends Component {
 			galleryWithImageBlocks,
 		} = this.props;
 
-		// TODO: remove this as unnecessary since we are setting in constructor?
-		window.wp.galleryBlockV2Enabled = galleryWithImageBlocks;
-
 		updateSettings( {
 			...capabilities,
 			...this.getThemeColors( this.props ),

From 9f06d775f5505415c2c241e1eebefcceba607f8b Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Mon, 22 Nov 2021 10:10:12 +1300
Subject: [PATCH 19/30] Move mocking of the galleryBlockV2Enabled flag to the
 jest globals file

---
 packages/block-library/src/gallery/shared.js |  4 ----
 test/unit/config/global-mocks.js             | 13 +++++++++++++
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/packages/block-library/src/gallery/shared.js b/packages/block-library/src/gallery/shared.js
index 8c23232bb8699..f7d87e46a01aa 100644
--- a/packages/block-library/src/gallery/shared.js
+++ b/packages/block-library/src/gallery/shared.js
@@ -23,9 +23,5 @@ export const pickRelevantMediaFiles = ( image, sizeSlug = 'large' ) => {
 };
 
 export function isGalleryV2Enabled() {
-	if ( typeof process !== 'undefined' && process.env?.NODE_ENV === 'test' ) {
-		return true;
-	}
-
 	return window.wp.galleryBlockV2Enabled;
 }
diff --git a/test/unit/config/global-mocks.js b/test/unit/config/global-mocks.js
index 0398af83c3a0b..29ca4ce94d4a6 100644
--- a/test/unit/config/global-mocks.js
+++ b/test/unit/config/global-mocks.js
@@ -9,3 +9,16 @@ jest.mock( '@wordpress/compose', () => {
 		] ),
 	};
 } );
+
+/**
+ * The new gallery block format is not compatible with the use_BalanceTags option
+ * so a flag is set in lib/compat.php to allow disabling the new block in this instance.
+ * This flag needs to be mocked here to ensure tests and fixtures run with the v2
+ * version of the Gallery block enabled.
+ *
+ * Note: This should be removed when the minimum required WP version is >= 5.9.
+ *
+ */
+if ( ! window.wp?.galleryBlockV2Enabled ) {
+	window.wp = { ...window.wp, galleryBlockV2Enabled: true };
+}

From 977435566da6ad950843535b4117e2d35a82a35c Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Mon, 22 Nov 2021 11:42:50 +1300
Subject: [PATCH 20/30] Explicitly check for galleryBlockV2Enabled being false
 and default to true in other instances

---
 packages/block-library/src/gallery/shared.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/block-library/src/gallery/shared.js b/packages/block-library/src/gallery/shared.js
index f7d87e46a01aa..06050ab65d892 100644
--- a/packages/block-library/src/gallery/shared.js
+++ b/packages/block-library/src/gallery/shared.js
@@ -23,5 +23,5 @@ export const pickRelevantMediaFiles = ( image, sizeSlug = 'large' ) => {
 };
 
 export function isGalleryV2Enabled() {
-	return window.wp.galleryBlockV2Enabled;
+	return window.wp?.galleryBlockV2Enabled === false ? false : true;
 }

From 5bc1aa2d9aad6b9a2cbb527cc539537afdacdc39 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Mon, 22 Nov 2021 13:19:56 +1300
Subject: [PATCH 21/30] Fail early if window.wp.galleryBlockV2Enabled  is not a
 boolean

---
 packages/block-library/src/gallery/shared.js | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/packages/block-library/src/gallery/shared.js b/packages/block-library/src/gallery/shared.js
index 06050ab65d892..e2c2e717d0f70 100644
--- a/packages/block-library/src/gallery/shared.js
+++ b/packages/block-library/src/gallery/shared.js
@@ -22,6 +22,18 @@ export const pickRelevantMediaFiles = ( image, sizeSlug = 'large' ) => {
 	return imageProps;
 };
 
+/**
+ * The new gallery block format is not compatible with the use_BalanceTags option
+ * in WP versions <= 5.8 https://core.trac.wordpress.org/ticket/54130. The
+ * window.wp.galleryBlockV2Enabled flag is set in lib/compat.php. This method
+ * can be removed when minimum supported WP version >=5.9.
+ */
 export function isGalleryV2Enabled() {
-	return window.wp?.galleryBlockV2Enabled === false ? false : true;
+	// We want to fail early here, at least during beta testing phase, to ensure
+	// there aren't instances where undefined values cause false negatives.
+	if ( ! window.wp || typeof window.wp.galleryBlockV2Enabled !== 'boolean' ) {
+		throw 'window.wp.galleryBlockV2Enabled is not defined';
+	}
+
+	return window.wp.galleryBlockV2Enabled;
 }

From de9d18752b7f6ebd13ba32fa8fb9a48d84b83dd3 Mon Sep 17 00:00:00 2001
From: Matthew Kevins <mmkevins@yahoo.com>
Date: Tue, 23 Nov 2021 11:23:08 +1000
Subject: [PATCH 22/30] Default to `true` for gallery flag when not yet cached

This makes sure the global value for the flag is established when the
editor is initialized, since some users may not have the value cached
locally yet. This is important to avoid incorrectly branching in the
parsing step, resulting in an incompatible block format.
---
 packages/edit-post/src/editor.native.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/packages/edit-post/src/editor.native.js b/packages/edit-post/src/editor.native.js
index b045724a0459d..7f71d7f68ad27 100644
--- a/packages/edit-post/src/editor.native.js
+++ b/packages/edit-post/src/editor.native.js
@@ -31,7 +31,9 @@ class Editor extends Component {
 		super( ...arguments );
 
 		// need to set this globally to avoid race with deprecations
-		window.wp.galleryBlockV2Enabled = props.galleryWithImageBlocks;
+		// defaulting to true to avoid issues with a not-yet-cached value
+		const { galleryWithImageBlocks = true } = props;
+		window.wp.galleryBlockV2Enabled = galleryWithImageBlocks;
 
 		if ( props.initialHtmlModeEnabled && props.mode === 'visual' ) {
 			// enable html mode if the initial mode the parent wants it but we're not already in it

From 9504e11ac540d75998ab98f5308146c05ba93d69 Mon Sep 17 00:00:00 2001
From: Matthew Kevins <mmkevins@yahoo.com>
Date: Wed, 24 Nov 2021 17:29:18 +1000
Subject: [PATCH 23/30] Only update gallery flag if fetch result is explicitly
 boolean

This adds the flag to the destructuring of the fetch callback (so it is
not binding to the stale closure value), and also conditionally updates
the global only if the type is explicitly boolean. This avoids a
scenario in which updates to other editor settings which omit the
gallery flag property result in the global value being overwritten with
`undefined`.
---
 .../editor/src/components/provider/index.native.js  | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/packages/editor/src/components/provider/index.native.js b/packages/editor/src/components/provider/index.native.js
index 1a5eda37a07aa..ace8b1f01da13 100644
--- a/packages/editor/src/components/provider/index.native.js
+++ b/packages/editor/src/components/provider/index.native.js
@@ -91,12 +91,7 @@ class NativeEditorProvider extends Component {
 	}
 
 	componentDidMount() {
-		const {
-			capabilities,
-			locale,
-			updateSettings,
-			galleryWithImageBlocks,
-		} = this.props;
+		const { capabilities, locale, updateSettings } = this.props;
 
 		updateSettings( {
 			...capabilities,
@@ -149,8 +144,10 @@ class NativeEditorProvider extends Component {
 		);
 
 		this.subscriptionParentUpdateEditorSettings = subscribeUpdateEditorSettings(
-			( editorSettings ) => {
-				window.wp.galleryBlockV2Enabled = galleryWithImageBlocks;
+			( { galleryWithImageBlocks, ...editorSettings } ) => {
+				if ( typeof galleryWithImageBlocks === 'boolean' ) {
+					window.wp.galleryBlockV2Enabled = galleryWithImageBlocks;
+				}
 				updateSettings( this.getThemeColors( editorSettings ) );
 			}
 		);

From a6bfee5eac3cad42ba8a3136e7875289f165e9b3 Mon Sep 17 00:00:00 2001
From: Matthew Kevins <mmkevins@yahoo.com>
Date: Wed, 24 Nov 2021 17:40:42 +1000
Subject: [PATCH 24/30] Update editor settings response to the editor to
 include gallery flag

This adds logic to ensure we report the gallery flag when the editor
settings are fetched.
---
 .../RNReactNativeGutenbergBridgeModule.java          | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java
index bdbe240267198..860a68419dcb3 100644
--- a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java
+++ b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java
@@ -60,6 +60,7 @@ public class RNReactNativeGutenbergBridgeModule extends ReactContextBaseJavaModu
     private static final String MAP_KEY_THEME_UPDATE_COLORS = "colors";
     private static final String MAP_KEY_THEME_UPDATE_GRADIENTS = "gradients";
     private static final String MAP_KEY_THEME_UPDATE_RAW_STYLES = "rawStyles";
+    private static final String MAP_KEY_GALLERY_WITH_IMAGE_BLOCKS = "galleryWithImageBlocks";
     public static final String MAP_KEY_MEDIA_FINAL_SAVE_RESULT_SUCCESS_VALUE = "success";
 
     private static final String MAP_KEY_IS_PREFERRED_COLOR_SCHEME_DARK = "isPreferredColorSchemeDark";
@@ -149,6 +150,13 @@ public void updateTheme(@Nullable Bundle editorTheme) {
         Serializable gradients = editorTheme.getSerializable(MAP_KEY_THEME_UPDATE_GRADIENTS);
         Serializable rawStyles = editorTheme.getSerializable(MAP_KEY_THEME_UPDATE_RAW_STYLES);
 
+        // We must assign null here to distinguish between a missing value and false
+        Boolean galleryWithImageBlocks = null;
+        if (editorTheme.containsKey(MAP_KEY_GALLERY_WITH_IMAGE_BLOCKS)) {
+            galleryWithImageBlocks = editorTheme.getBoolean(MAP_KEY_GALLERY_WITH_IMAGE_BLOCKS);
+        }
+
+
         if (colors != null) {
             writableMap.putArray(MAP_KEY_THEME_UPDATE_COLORS, Arguments.fromList((ArrayList)colors));
         }
@@ -161,6 +169,10 @@ public void updateTheme(@Nullable Bundle editorTheme) {
             writableMap.putString(MAP_KEY_THEME_UPDATE_RAW_STYLES, rawStyles.toString());
         }
 
+        if (galleryWithImageBlocks != null) {
+            writableMap.putBoolean(MAP_KEY_GALLERY_WITH_IMAGE_BLOCKS, galleryWithImageBlocks);
+        }
+
         emitToJS(EVENT_NAME_UPDATE_EDITOR_SETTINGS, writableMap);
     }
 

From f2df281fefba18191bc0fe8acc58388a73a11de8 Mon Sep 17 00:00:00 2001
From: Matthew Kevins <mmkevins@yahoo.com>
Date: Thu, 25 Nov 2021 18:07:19 +1000
Subject: [PATCH 25/30] Leave gallery flag `undefined` when not cached

This allows the value to be set to default `true` on the JS side,
instead of defaulting to a value of `false` from iOS native code.
---
 packages/react-native-bridge/ios/Gutenberg.swift | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/packages/react-native-bridge/ios/Gutenberg.swift b/packages/react-native-bridge/ios/Gutenberg.swift
index b926c36cb3bf3..318a9796a2e9b 100644
--- a/packages/react-native-bridge/ios/Gutenberg.swift
+++ b/packages/react-native-bridge/ios/Gutenberg.swift
@@ -199,7 +199,10 @@ public class Gutenberg: NSObject {
     private func properties(from editorSettings: GutenbergEditorSettings?) -> [String : Any] {
         var settingsUpdates = [String : Any]()
         settingsUpdates["isFSETheme"] = editorSettings?.isFSETheme ?? false
-        settingsUpdates["galleryWithImageBlocks"] = editorSettings?.galleryWithImageBlocks ?? false
+        
+        if let galleryWithImageBlocks = editorSettings?.galleryWithImageBlocks {
+            settingsUpdates["galleryWithImageBlocks"] = galleryWithImageBlocks
+        }
 
         if let rawStyles = editorSettings?.rawStyles {
             settingsUpdates["rawStyles"] = rawStyles

From 2226e57266ce099d2a0e08dcd6e387c6f122eec3 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Mon, 29 Nov 2021 11:42:34 +1300
Subject: [PATCH 26/30] Update the wording of the mobile warning

---
 packages/block-library/src/gallery/use-mobile-warning.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/block-library/src/gallery/use-mobile-warning.js b/packages/block-library/src/gallery/use-mobile-warning.js
index 6567d8f73349b..95d0f4c86432f 100644
--- a/packages/block-library/src/gallery/use-mobile-warning.js
+++ b/packages/block-library/src/gallery/use-mobile-warning.js
@@ -20,7 +20,7 @@ export default function useMobileWarning( newImages ) {
 
 	createWarningNotice(
 		__(
-			'Editing this Gallery in the WordPress mobile app requires version 18.2 or higher.'
+			'If you want to edit the gallery you just added in the mobile app, to avoid losing any data please make sure you use version 18.2 of the app or above.'
 		),
 		{ type: 'snackbar', explicitDismiss: true }
 	);

From 06b540cdca5359ca0dfdabfa2372036728a05a51 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Mon, 29 Nov 2021 13:26:52 +1300
Subject: [PATCH 27/30] Add filter to prevent use_balanceTags being enabled on
 WP < 5.9

---
 lib/compat.php | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/lib/compat.php b/lib/compat.php
index 8f8d527020227..ac2f1cecfcb50 100644
--- a/lib/compat.php
+++ b/lib/compat.php
@@ -200,7 +200,8 @@ function gutenberg_safe_style_attrs( $attrs ) {
  * as it needs to be available when the intial block parsing runs on editor load, and most of
  * the editor store and standard flags are not loaded yet at that point
  *
- * Note: This should be removed when the minimum required WP version is >= 5.9.
+ * @since 12.1.0
+ * @todo This should be removed when the minimum required WP version is >= 5.9.
  *
  * @return void.
  */
@@ -215,3 +216,28 @@ function gutenberg_check_gallery_block_v2_compatibility() {
 	);
 }
 add_action( 'init', 'gutenberg_check_gallery_block_v2_compatibility' );
+
+/**
+ * Prevent use_balanceTags being enabled on WordPress 5.8 or earlier as it breaks
+ * the layout of the new Gallery block.
+ *
+ * @since 12.1.0
+ * @todo This should be removed when the minimum required WP version is >= 5.9.
+ *
+ * @param int $new_value The new value for use_balanceTags.
+ */
+function gutenberg_use_balancetags_check( $new_value ) {
+	global $wp_version;
+
+	if ( 1 === (int) $new_value && version_compare( $wp_version, '5.9', '<' ) ) {
+		echo '<div class="error"><p>';
+		/* translators: %s: Minimum required version */
+		echo esc_html( printf( __( 'Gutenberg requires WordPress %s or later to function properly with <code>use_balanceTags</code> enabled. Please upgrade WordPress before enabling <code>use_balanceTags</code>.', 'gutenberg' ), '5.9' ) );
+		echo '</p></div>';
+
+		return 0;
+	}
+
+	return $new_value;
+}
+add_filter( 'pre_update_option_use_balanceTags', 'gutenberg_use_balancetags_check' );

From ad7ec30054cad0edc4804a0071d3bb90eeb45e96 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Mon, 29 Nov 2021 16:55:16 +1300
Subject: [PATCH 28/30] Switch to using settings_error for use_balanceTags
 notice

---
 lib/compat.php | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/lib/compat.php b/lib/compat.php
index ac2f1cecfcb50..c6524dd54ccfd 100644
--- a/lib/compat.php
+++ b/lib/compat.php
@@ -230,11 +230,9 @@ function gutenberg_use_balancetags_check( $new_value ) {
 	global $wp_version;
 
 	if ( 1 === (int) $new_value && version_compare( $wp_version, '5.9', '<' ) ) {
-		echo '<div class="error"><p>';
 		/* translators: %s: Minimum required version */
-		echo esc_html( printf( __( 'Gutenberg requires WordPress %s or later to function properly with <code>use_balanceTags</code> enabled. Please upgrade WordPress before enabling <code>use_balanceTags</code>.', 'gutenberg' ), '5.9' ) );
-		echo '</p></div>';
-
+		$message = sprintf( __( 'Gutenberg requires WordPress %s or later in order to enable the correct invalidly nested XHTML automatically option. Please upgrade WordPress before enabling.', 'gutenberg' ), '5.9' );
+		add_settings_error('gutenberg_use_balancetags_check', 'gutenberg_use_balancetags_check', $message, 'error' );
 		return 0;
 	}
 

From cff2300918b35d62732506cbca3ac1e90c0b2b31 Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Tue, 30 Nov 2021 09:59:08 +1300
Subject: [PATCH 29/30] Add a CLI error message

---
 lib/compat.php | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/lib/compat.php b/lib/compat.php
index c6524dd54ccfd..a667868b60580 100644
--- a/lib/compat.php
+++ b/lib/compat.php
@@ -231,8 +231,11 @@ function gutenberg_use_balancetags_check( $new_value ) {
 
 	if ( 1 === (int) $new_value && version_compare( $wp_version, '5.9', '<' ) ) {
 		/* translators: %s: Minimum required version */
-		$message = sprintf( __( 'Gutenberg requires WordPress %s or later in order to enable the correct invalidly nested XHTML automatically option. Please upgrade WordPress before enabling.', 'gutenberg' ), '5.9' );
+		$message = sprintf( __( 'Gutenberg requires WordPress %s or later in order to enable the &#8220;Correct invalidly nested XHTML automatically&#8221; option. Please upgrade WordPress before enabling.', 'gutenberg' ), '5.9' );
 		add_settings_error('gutenberg_use_balancetags_check', 'gutenberg_use_balancetags_check', $message, 'error' );
+		if ( class_exists( 'WP_CLI' ) ) {
+			WP_CLI::error( $message );
+		}
 		return 0;
 	}
 

From c752d648b5b58d4290015d17bdf3d2710bbbd8fd Mon Sep 17 00:00:00 2001
From: Glen Davies <glen.davies@a8c.com>
Date: Tue, 30 Nov 2021 10:15:35 +1300
Subject: [PATCH 30/30] Fix linting error

---
 lib/compat.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/compat.php b/lib/compat.php
index a667868b60580..f328928085efa 100644
--- a/lib/compat.php
+++ b/lib/compat.php
@@ -232,7 +232,7 @@ function gutenberg_use_balancetags_check( $new_value ) {
 	if ( 1 === (int) $new_value && version_compare( $wp_version, '5.9', '<' ) ) {
 		/* translators: %s: Minimum required version */
 		$message = sprintf( __( 'Gutenberg requires WordPress %s or later in order to enable the &#8220;Correct invalidly nested XHTML automatically&#8221; option. Please upgrade WordPress before enabling.', 'gutenberg' ), '5.9' );
-		add_settings_error('gutenberg_use_balancetags_check', 'gutenberg_use_balancetags_check', $message, 'error' );
+		add_settings_error( 'gutenberg_use_balancetags_check', 'gutenberg_use_balancetags_check', $message, 'error' );
 		if ( class_exists( 'WP_CLI' ) ) {
 			WP_CLI::error( $message );
 		}