Skip to content

Commit de06d0b

Browse files
committed
Merge branch 'html-api/add-spawn-fragment-parser-method' into html-api/js-add-set-inner-html
2 parents 2aadea5 + 9ac142f commit de06d0b

File tree

2 files changed

+126
-9
lines changed

2 files changed

+126
-9
lines changed

src/wp-includes/html-api/class-wp-html-processor.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,61 @@ function ( WP_HTML_Token $token ): void {
517517
};
518518
}
519519

520+
/**
521+
* Creates a fragment processor with the current node as its context element.
522+
*
523+
* @see https://html.spec.whatwg.org/multipage/parsing.html#html-fragment-parsing-algorithm
524+
*
525+
* @param string $html Input HTML fragment to process.
526+
* @return static|null The created processor if successful, otherwise null.
527+
*/
528+
public function spawn_fragment_parser( string $html ): ?self {
529+
if ( $this->get_token_type() !== '#tag' ) {
530+
return null;
531+
}
532+
533+
$namespace = $this->get_namespace();
534+
535+
/*
536+
* Prevent creating fragments at "self-contained" nodes.
537+
*
538+
* @see https://github.com/WordPress/wordpress-develop/pull/7141
539+
* @see https://github.com/WordPress/wordpress-develop/pull/7198
540+
*/
541+
if (
542+
'html' === $namespace &&
543+
in_array( $this->get_tag(), array( 'IFRAME', 'NOEMBED', 'NOFRAMES', 'SCRIPT', 'STYLE', 'TEXTAREA', 'TITLE', 'XMP' ), true )
544+
) {
545+
return null;
546+
}
547+
548+
$fragment_processor = self::create_fragment( $html );
549+
$fragment_processor->compat_mode = $this->compat_mode;
550+
551+
$fragment_processor->context_node = clone $this->state->current_token;
552+
$fragment_processor->context_node->bookmark_name = 'context-node';
553+
$fragment_processor->context_node->on_destroy = null;
554+
555+
$context_element = array( $fragment_processor->context_node->node_name, array() );
556+
foreach ( $this->get_attribute_names_with_prefix( '' ) as $name => $value ) {
557+
$context_element[1][ $name ] = $value;
558+
}
559+
560+
$fragment_processor->breadcrumbs = array();
561+
562+
if ( 'TEMPLATE' === $context_element[0] ) {
563+
$fragment_processor->state->stack_of_template_insertion_modes[] = WP_HTML_Processor_State::INSERTION_MODE_IN_TEMPLATE;
564+
}
565+
566+
$fragment_processor->reset_insertion_mode_appropriately();
567+
568+
// @todo Set the parser's form element pointer.
569+
570+
$fragment_processor->state->encoding_confidence = 'irrelevant';
571+
572+
return $fragment_processor;
573+
}
574+
520575
/**
521576
* Stops the parser and terminates its execution when encountering unsupported markup.
522577
*

tests/phpunit/tests/html-api/wpHtmlProcessorHtml5lib.php

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,6 @@ public function data_external_html5lib_tests() {
138138
* @return bool True if the test case should be skipped. False otherwise.
139139
*/
140140
private static function should_skip_test( ?string $test_context_element, string $test_name ): bool {
141-
if ( null !== $test_context_element && 'body' !== $test_context_element ) {
142-
return true;
143-
}
144-
145141
if ( array_key_exists( $test_name, self::SKIP_TESTS ) ) {
146142
return true;
147143
}
@@ -157,11 +153,77 @@ private static function should_skip_test( ?string $test_context_element, string
157153
* @return string|null Tree structure of parsed HTML, if supported, else null.
158154
*/
159155
private static function build_tree_representation( ?string $fragment_context, string $html ) {
160-
$processor = $fragment_context
161-
? WP_HTML_Processor::create_fragment( $html, "<{$fragment_context}>" )
162-
: WP_HTML_Processor::create_full_parser( $html );
163-
if ( null === $processor ) {
164-
throw new WP_HTML_Unsupported_Exception( "Could not create a parser with the given fragment context: {$fragment_context}.", '', 0, '', array(), array() );
156+
$processor = null;
157+
if ( $fragment_context ) {
158+
if ( 'body' === $fragment_context ) {
159+
$processor = WP_HTML_Processor::create_fragment( $html );
160+
} else {
161+
162+
/*
163+
* If the string of characters starts with "svg ", the context
164+
* element is in the SVG namespace and the substring after
165+
* "svg " is the local name. If the string of characters starts
166+
* with "math ", the context element is in the MathML namespace
167+
* and the substring after "math " is the local name.
168+
* Otherwise, the context element is in the HTML namespace and
169+
* the string is the local name.
170+
*/
171+
if ( str_starts_with( $fragment_context, 'svg ' ) ) {
172+
$tag_name = substr( $fragment_context, 4 );
173+
if ( 'svg' === $tag_name ) {
174+
$parent_processor = WP_HTML_Processor::create_full_parser( '<!DOCTYPE html><svg>' );
175+
} else {
176+
$parent_processor = WP_HTML_Processor::create_full_parser( "<!DOCTYPE html><svg><{$tag_name}>" );
177+
}
178+
$parent_processor->next_tag( $tag_name );
179+
} elseif ( str_starts_with( $fragment_context, 'math ' ) ) {
180+
$tag_name = substr( $fragment_context, 5 );
181+
if ( 'math' === $tag_name ) {
182+
$parent_processor = WP_HTML_Processor::create_full_parser( '<!DOCTYPE html><math>' );
183+
} else {
184+
$parent_processor = WP_HTML_Processor::create_full_parser( "<!DOCTYPE html><math><{$tag_name}>" );
185+
}
186+
$parent_processor->next_tag( $tag_name );
187+
} else {
188+
if ( in_array(
189+
$fragment_context,
190+
array(
191+
'caption',
192+
'col',
193+
'colgroup',
194+
'tbody',
195+
'td',
196+
'tfoot',
197+
'th',
198+
'thead',
199+
'tr',
200+
),
201+
true
202+
) ) {
203+
$parent_processor = WP_HTML_Processor::create_full_parser( "<!DOCTYPE html><table><{$fragment_context}>" );
204+
$parent_processor->next_tag();
205+
} else {
206+
$parent_processor = WP_HTML_Processor::create_full_parser( "<!DOCTYPE html><{$fragment_context}>" );
207+
}
208+
$parent_processor->next_tag( $fragment_context );
209+
}
210+
if ( null !== $parent_processor->get_unsupported_exception() ) {
211+
throw $parent_processor->get_unsupported_exception();
212+
}
213+
if ( null !== $parent_processor->get_last_error() ) {
214+
throw new Exception( $parent_processor->get_last_error() );
215+
}
216+
$processor = $parent_processor->spawn_fragment_parser( $html );
217+
}
218+
219+
if ( null === $processor ) {
220+
throw new WP_HTML_Unsupported_Exception( "Could not create a parser with the given fragment context: {$fragment_context}.", '', 0, '', array(), array() );
221+
}
222+
} else {
223+
$processor = WP_HTML_Processor::create_full_parser( $html );
224+
if ( null === $processor ) {
225+
throw new Exception( 'Could not create a full parser.' );
226+
}
165227
}
166228

167229
/*

0 commit comments

Comments
 (0)