Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2c9a17d
Fix group -> ticket test annotation
sirreal Dec 10, 2025
da53752
Add failing test for deep nesting
sirreal Dec 10, 2025
0337b13
Handle Exception thrown by bookmark_token()
sirreal Dec 10, 2025
4101235
Add failing test for virtual tokens
sirreal Dec 10, 2025
e7d649c
Handle insert_virtual_node exceptions
sirreal Dec 10, 2025
1cfeb85
Fix php 7.x compat
sirreal Dec 10, 2025
17bbca8
Update test ticket annotation
sirreal Dec 10, 2025
ba480a5
Increase virtual token HTML size
sirreal Dec 10, 2025
ff7657f
Use single-line comment
sirreal Dec 12, 2025
b02d679
Merge branch 'trunk' into html-api/ensure-deep-nesting-no-exception
sirreal Jan 29, 2026
9e042f3
Return false or throw unsupported exception where available
sirreal Jan 29, 2026
c7710aa
Remove general exception catching
sirreal Jan 29, 2026
1681fed
Avoid throwing unsupported exception when tokens exceeded
sirreal Jan 29, 2026
3f6bffd
Merge branch 'trunk' into html-api/ensure-deep-nesting-no-exception
sirreal Jan 29, 2026
aefc5bf
Remove unsupported union return type
sirreal Jan 29, 2026
6d1f860
Use arrow functions ✨
sirreal Jan 30, 2026
207f04f
Merge branch 'trunk' into html-api/ensure-deep-nesting-no-exception
sirreal Jan 30, 2026
cbab9c1
Add assertion message and use class constant
sirreal Jan 30, 2026
c578125
Merge branch 'trunk' into html-api/ensure-deep-nesting-no-exception
sirreal Feb 2, 2026
5e45206
Merge branch 'trunk' into html-api/ensure-deep-nesting-no-exception
sirreal Feb 26, 2026
c4476be
Restore exception-catching approach
sirreal Feb 26, 2026
b847559
Improve tests
sirreal Feb 26, 2026
aa06e5e
Merge branch 'trunk' into html-api/ensure-deep-nesting-no-exception
sirreal Feb 26, 2026
547d37b
Lints
sirreal Feb 26, 2026
b6df5f7
Merge branch 'trunk' into html-api/ensure-deep-nesting-no-exception
sirreal Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/wp-includes/html-api/class-wp-html-processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -1040,8 +1040,17 @@ public function step( $node_to_process = self::PROCESS_NEXT_NODE ): bool {
$token_name = $this->get_token_name();

if ( self::REPROCESS_CURRENT_NODE !== $node_to_process ) {
try {
$bookmark_name = $this->bookmark_token();
Comment thread
sirreal marked this conversation as resolved.
} catch ( Exception $e ) {
if ( self::ERROR_EXCEEDED_MAX_BOOKMARKS === $this->last_error ) {
return false;
Comment thread
sirreal marked this conversation as resolved.
}
throw $e;
}

$this->state->current_token = new WP_HTML_Token(
$this->bookmark_token(),
$bookmark_name,
$token_name,
$this->has_self_closing_flag(),
$this->release_internal_bookmark_on_destruct
Expand Down Expand Up @@ -1151,6 +1160,14 @@ public function step( $node_to_process = self::PROCESS_NEXT_NODE ): bool {
* otherwise might involve messier calling and return conventions.
*/
return false;
} catch ( Exception $e ) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exhausted bookmarks throw a generic Exception.

This block catches the exceptions thrown by insert_virtual_token().

if ( self::ERROR_EXCEEDED_MAX_BOOKMARKS === $this->last_error ) {
return false;
}
/*
* Rethrow any other exceptions for higher-level handling.
*/
Comment thread
sirreal marked this conversation as resolved.
Outdated
throw $e;
}
}

Expand Down Expand Up @@ -6287,6 +6304,8 @@ private function insert_foreign_element( WP_HTML_Token $token, bool $only_add_to
*
* @since 6.7.0
*
* @throws Exception When unable to allocate a bookmark for the next token in the input HTML document.
*
Comment on lines +6333 to +6334
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bookmark_token() actually throws, but it's not handled here. The annotation may not be appropriate.

* @param string $token_name Name of token to create and insert into the stack of open elements.
* @param string|null $bookmark_name Optional. Name to give bookmark for created virtual node.
* Defaults to auto-creating a bookmark name.
Expand Down
59 changes: 58 additions & 1 deletion tests/phpunit/tests/html-api/wpHtmlProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,7 @@ public function test_ensure_next_token_method_extensibility( $html, $expected_to
/**
* Ensure that lowercased tag_name query matches tags case-insensitively.
*
* @group 62427
* @ticket 62427
*/
public function test_next_tag_lowercase_tag_name() {
// The upper case <DIV> is irrelevant but illustrates the case-insentivity.
Expand All @@ -1079,4 +1079,61 @@ public function test_next_tag_lowercase_tag_name() {
$processor = WP_HTML_Processor::create_fragment( '<svg><RECT>' );
$this->assertTrue( $processor->next_tag( array( 'tag_name' => 'rect' ) ) );
}

/**
* Ensure that the processor does not throw errors in cases of extreme HTML nesting.
*
* @ticket 64394
*
* @expectedIncorrectUsage WP_HTML_Tag_Processor::set_bookmark
*/
public function test_deep_nesting_fails_process_without_error() {
$html = str_repeat( '<i>', WP_HTML_Processor::MAX_BOOKMARKS * 2 );
$processor = WP_HTML_Processor::create_fragment( $html );

// The fragment parser starts with a few context tokens already bookmarked.
$reached_tokens = ( function () {
return count( $this->bookmarks );
} )->call( $processor );
Comment thread
sirreal marked this conversation as resolved.
Outdated
while ( $processor->next_token() ) {
++$reached_tokens;
}
$this->assertSame( WP_HTML_Processor::MAX_BOOKMARKS, $reached_tokens );
}

/**
* @ticket 64394
*
* @expectedIncorrectUsage WP_HTML_Tag_Processor::set_bookmark
*/
public function test_deep_nesting_fails_processing_virtual_tokens_without_error() {
$html = str_repeat( '<table><td>', WP_HTML_Processor::MAX_BOOKMARKS );
Comment thread
sirreal marked this conversation as resolved.
Outdated
$processor = WP_HTML_Processor::create_fragment( $html );

// The fragment parser starts with a few context tokens already bookmarked.
$reached_tokens = ( function () {
return count( $this->bookmarks );
} )->call( $processor );
Comment thread
sirreal marked this conversation as resolved.
Outdated
while ( $processor->next_token() ) {
++$reached_tokens;
}

/*
* This test has some variability depending on how the virtual tokens align.
* It will produce 1 real, 2 virtual, 1 real.
*
* "<table><td><table><td>…" produces:
* └─TABLE
* └─TBODY (virtual)
* └─TR (virtual)
* └─TD
* └─TABLE
* └─TBODY (virtual)
* └─TR (virtual)
* └─TD
* └─…
*/
$this->assertGreaterThanOrEqual( WP_HTML_Processor::MAX_BOOKMARKS - 1, $reached_tokens );
$this->assertLessThanOrEqual( WP_HTML_Processor::MAX_BOOKMARKS + 1, $reached_tokens );
}
Comment thread
sirreal marked this conversation as resolved.
}
Loading