Index: trunk/src/wp-includes/class-wp-block.php
===================================================================
--- trunk/src/wp-includes/class-wp-block.php	(revision 58298)
+++ trunk/src/wp-includes/class-wp-block.php	(revision 58398)
@@ -334,25 +334,66 @@
 			case 'html':
 			case 'rich-text':
-				// Hardcode the selectors and processing until the HTML API is able to read CSS selectors and replace inner HTML.
-				// TODO: Use the HTML API instead.
-				if ( 'core/paragraph' === $this->name && 'content' === $attribute_name ) {
-					$selector = 'p';
-				}
-				if ( 'core/heading' === $this->name && 'content' === $attribute_name ) {
-					$selector = 'h[1-6]';
-				}
-				if ( 'core/button' === $this->name && 'text' === $attribute_name ) {
-					// Check if it is a <button> or <a> tag.
-					if ( preg_match( '/<button[^>]*>.*?<\/button>/', $block_content ) ) {
-						$selector = 'button';
-					} else {
-						$selector = 'a';
+				$block_reader = new WP_HTML_Tag_Processor( $block_content );
+
+				// TODO: Support for CSS selectors whenever they are ready in the HTML API.
+				// In the meantime, support comma-separated selectors by exploding them into an array.
+				$selectors = explode( ',', $block_type->attributes[ $attribute_name ]['selector'] );
+				// Add a bookmark to the first tag to be able to iterate over the selectors.
+				$block_reader->next_tag();
+				$block_reader->set_bookmark( 'iterate-selectors' );
+
+				// TODO: This shouldn't be needed when the `set_inner_html` function is ready.
+				// Store the parent tag and its attributes to be able to restore them later in the button.
+				// The button block has a wrapper while the paragraph and heading blocks don't.
+				if ( 'core/button' === $this->name ) {
+					$button_wrapper                 = $block_reader->get_tag();
+					$button_wrapper_attribute_names = $block_reader->get_attribute_names_with_prefix( '' );
+					$button_wrapper_attrs           = array();
+					foreach ( $button_wrapper_attribute_names as $name ) {
+						$button_wrapper_attrs[ $name ] = $block_reader->get_attribute( $name );
 					}
 				}
-				if ( empty( $selector ) ) {
-					return $block_content;
+
+				foreach ( $selectors as $selector ) {
+					// If the parent tag, or any of its children, matches the selector, replace the HTML.
+					if ( strcasecmp( $block_reader->get_tag( $selector ), $selector ) === 0 || $block_reader->next_tag(
+						array(
+							'tag_name' => $selector,
+						)
+					) ) {
+						$block_reader->release_bookmark( 'iterate-selectors' );
+
+						// TODO: Use `set_inner_html` method whenever it's ready in the HTML API.
+						// Until then, it is hardcoded for the paragraph, heading, and button blocks.
+						// Store the tag and its attributes to be able to restore them later.
+						$selector_attribute_names = $block_reader->get_attribute_names_with_prefix( '' );
+						$selector_attrs           = array();
+						foreach ( $selector_attribute_names as $name ) {
+							$selector_attrs[ $name ] = $block_reader->get_attribute( $name );
+						}
+						$selector_markup = "<$selector>" . wp_kses_post( $source_value ) . "</$selector>";
+						$amended_content = new WP_HTML_Tag_Processor( $selector_markup );
+						$amended_content->next_tag();
+						foreach ( $selector_attrs as $attribute_key => $attribute_value ) {
+							$amended_content->set_attribute( $attribute_key, $attribute_value );
+						}
+						if ( 'core/paragraph' === $this->name || 'core/heading' === $this->name ) {
+							return $amended_content->get_updated_html();
+						}
+						if ( 'core/button' === $this->name ) {
+							$button_markup  = "<$button_wrapper>{$amended_content->get_updated_html()}</$button_wrapper>";
+							$amended_button = new WP_HTML_Tag_Processor( $button_markup );
+							$amended_button->next_tag();
+							foreach ( $button_wrapper_attrs as $attribute_key => $attribute_value ) {
+								$amended_button->set_attribute( $attribute_key, $attribute_value );
+							}
+							return $amended_button->get_updated_html();
+						}
+					} else {
+						$block_reader->seek( 'iterate-selectors' );
+					}
 				}
-				$pattern = '/(<' . $selector . '[^>]*>).*?(<\/' . $selector . '>)/i';
-				return preg_replace( $pattern, '$1' . wp_kses_post( $source_value ) . '$2', $block_content );
+				$block_reader->release_bookmark( 'iterate-selectors' );
+				return $block_content;
 
 			case 'attribute':
