HTML API: Add bookmark invalidation logic.

While `WP_HTML_Tag_Processor` currently only supports changing a given tag's attributes, the plan is to provide methods to make broader changes (possibly through a subclass of `WP_HTML_Tag_Processor`). The API will have the potential of replacing a tag that a bookmark points to. To prepare, this changeset makes sure that all bookmarks affected by a HTML replacement are invalidated (i.e. released).

Changes:
* Extends the existing loop in `WP_HTML_Tag_Processor::apply_attributes_updates()` that adjusts bookmarks' start and end positions upon HTML changes to check if the entire bookmark is within a portion of the HTML that has been replaced.
* Adds `WP_HTML_Tag_Processor::has_bookmark() to check whether the given bookmark name exists.

References:
* [https://github.com/WordPress/gutenberg/pull/47559 Gutenberg PR 47559]
* [https://github.com/WordPress/gutenberg/releases/tag/v15.3.0 Released in Gutenberg 15.3.0]

Follow-up to [55203].

Props bernhard-reiter, dmsnell, zieladam.
Fixes #57788.

git-svn-id: https://develop.svn.wordpress.org/trunk@55555 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Tonya Mork
2023-03-16 13:09:55 +00:00
parent 6222a118c9
commit f2028e108e
2 changed files with 57 additions and 7 deletions

View File

@@ -1404,6 +1404,7 @@ class WP_HTML_Tag_Processor {
* Applies attribute updates to HTML document.
*
* @since 6.2.0
* @since 6.3.0 Invalidate any bookmarks whose targets are overwritten.
*
* @return void
*/
@@ -1434,7 +1435,7 @@ class WP_HTML_Tag_Processor {
* Adjust bookmark locations to account for how the text
* replacements adjust offsets in the input document.
*/
foreach ( $this->bookmarks as $bookmark ) {
foreach ( $this->bookmarks as $bookmark_name => $bookmark ) {
/*
* Each lexical update which appears before the bookmark's endpoints
* might shift the offsets for those endpoints. Loop through each change
@@ -1445,20 +1446,22 @@ class WP_HTML_Tag_Processor {
$tail_delta = 0;
foreach ( $this->lexical_updates as $diff ) {
$update_head = $bookmark->start >= $diff->start;
$update_tail = $bookmark->end >= $diff->start;
if ( ! $update_head && ! $update_tail ) {
if ( $bookmark->start < $diff->start && $bookmark->end < $diff->start ) {
break;
}
if ( $bookmark->start >= $diff->start && $bookmark->end < $diff->end ) {
$this->release_bookmark( $bookmark_name );
continue 2;
}
$delta = strlen( $diff->text ) - ( $diff->end - $diff->start );
if ( $update_head ) {
if ( $bookmark->start >= $diff->start ) {
$head_delta += $delta;
}
if ( $update_tail ) {
if ( $bookmark->end >= $diff->end ) {
$tail_delta += $delta;
}
}
@@ -1470,6 +1473,18 @@ class WP_HTML_Tag_Processor {
$this->lexical_updates = array();
}
/**
* Checks whether a bookmark with the given name exists.
*
* @since 6.3.0
*
* @param string $bookmark_name Name to identify a bookmark that potentially exists.
* @return bool Whether that bookmark exists.
*/
public function has_bookmark( $bookmark_name ) {
return array_key_exists( $bookmark_name, $this->bookmarks );
}
/**
* Move the internal cursor in the Tag Processor to a given bookmark's location.
*

View File

@@ -40,6 +40,41 @@ class Tests_HtmlApi_wpHtmlTagProcessor_Bookmark extends WP_UnitTestCase {
$this->assertTrue( $p->release_bookmark( 'first li' ), 'Could not release a bookmark' );
}
/**
* @ticket 57788
*
* @covers WP_HTML_Tag_Processor::has_bookmark
*/
public function test_has_bookmark_returns_false_if_bookmark_does_not_exist() {
$p = new WP_HTML_Tag_Processor( '<div>Test</div>' );
$this->assertFalse( $p->has_bookmark( 'my-bookmark' ) );
}
/**
* @ticket 57788
*
* @covers WP_HTML_Tag_Processor::has_bookmark
*/
public function test_has_bookmark_returns_true_if_bookmark_exists() {
$p = new WP_HTML_Tag_Processor( '<div>Test</div>' );
$p->next_tag();
$p->set_bookmark( 'my-bookmark' );
$this->assertTrue( $p->has_bookmark( 'my-bookmark' ) );
}
/**
* @ticket 57788
*
* @covers WP_HTML_Tag_Processor::has_bookmark
*/
public function test_has_bookmark_returns_false_if_bookmark_has_been_released() {
$p = new WP_HTML_Tag_Processor( '<div>Test</div>' );
$p->next_tag();
$p->set_bookmark( 'my-bookmark' );
$p->release_bookmark( 'my-bookmark' );
$this->assertFalse( $p->has_bookmark( 'my-bookmark' ) );
}
/**
* @ticket 56299
*