When applying updates to HTML, one step was left out in [56941] which updated the position of the end of the current tag. This made it possible to create bookmarks with null or earlier end positions than their start position. This in turn broke the Directive Processor in Gutenberg during the backport of changes from Core into Gutenberg.
In this commit, after applying updates, the HTML document is now scanned fully to the end of the current tag, updating the internal pointer to its end, so that nothing else will be broken or misaligned.
Follow-up to [56941].
Props dmsnell.
Fixes#59643.
Built from https://develop.svn.wordpress.org/trunk@56953
git-svn-id: http://core.svn.wordpress.org/trunk@56464 1a063a9b-81f0-0310-95a4-ce76da25c4cd
After modifying tags in the HTML API, the Tag Processor backs up to before the tag being modified and then re-parses its attributes. This saves on the code complexity involved in applying updates, which have already been transformed to “lexical updates” by the time they are applied.
In order to do that, `::get_updated_html()` called `::next_tag()` to reuse its logic. However, as a public method, subclasses may change the behavior of that method, and the HTML Processor does just this. It maintains an HTML stack of open elements and when the Tag Processor calls this method to re-scan a tag and its attributes, it leads to a broken stack.
This commit replaces the call to `::next_tag()` with a more appropriate reapplication of its internal parsing logic to rescan the tag name and its attributes. Given the limited nature of what's occurring in `::get_updated_html()`, this should bring with it certain guarantees that no HTML structure is being changed (that structure will only be changed by subclasses like the HTML Processor).
Follow-up to [56274], [56702].
Props dmsnell, zieladam, nicolefurlan.
Fixes#59607.
Built from https://develop.svn.wordpress.org/trunk@56941
git-svn-id: http://core.svn.wordpress.org/trunk@56452 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This patch adds two new public methods to the HTML Tag Processor:
- `has_class()` indicates if a matched tag contains a given CSS class name.
- `class_list()` returns a generator to iterate over all the class names in a matched tag.
Included in this patch is a refactoring of the internal logic when matching
a tag to reuse the new `has_class()` function. Previously it was relying on
optimized code in the `matches()` function which performed byte-for-byte
class name comparison. With the change in this patch it will perform class
name matching on the decoded value, which might differ if a class attribute
contains character references.
These methods may be useful for running more complicated queries based
on the presence or absence of CSS class names. The use of these methods
avoids the need to manually decode the class attribute as reported by
`$process->get_attribute( 'class' )`.
Props dmsnell.
Fixes#59209.
Built from https://develop.svn.wordpress.org/trunk@56703
git-svn-id: http://core.svn.wordpress.org/trunk@56215 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Inside a `next_tag()` loop it can be challenging to use breadcrumbs because they are only exposed inside the call to `next_tag()` via the `$query` arg.
In this patch a new method, `matches_breadcrumbs()`, is exposed which allows for querying within the `next_tag()` loop for more complicated queries.
This method exposes a wildcard `*` operator to allow matching ''any HTML tag'' that the currently-matched tag is a child or descendant of.
Props dmsnell, westonruter, mukesh27.
Fixes#59400.
Built from https://develop.svn.wordpress.org/trunk@56702
git-svn-id: http://core.svn.wordpress.org/trunk@56214 1a063a9b-81f0-0310-95a4-ce76da25c4cd
When encountering an HTML tag with duplicate copies of an attribute the tag processor ignores the duplicate values, according to the specification. However, when removing an attribute it must remove all copies of that attribute lest one of the duplicates becomes the primary and it appears as if no attributes were removed.
In this patch we're adding tests that will be used to ensure that all attribute copies are removed from a tag when one is request to be removed.
**Before**
{{{#!php
<?php
$p = new WP_HTML_Tag_Processor( '<br id=one id="two" id='three' id>' );
$p->next_tag();
$p->remove_attribute( 'id' );
$p->get_updated_html();
// <br id="two" id='three' id>
}}}
**After**
{{{#!php
<?php
$p = new WP_HTML_Tag_Processor( '<br id=one id="two" id='three' id>' );
$p->next_tag();
$p->remove_attribute( 'id' );
$p->get_updated_html();
// <br>
}}}
Previously we have been overlooking duplicate attributes since they don't have an impact on what parses into the DOM. However, as one unit test affirmed (asserting the presence of the bug in the tag processor) when removing an attribute where duplicates exist this meant we ended up changing the value of an attribute instead of removing it.
In this patch we're tracking the text spans of the parsed duplicate attributes so that ''if'' we attempt to remove them then we'll have the appropriate information necessary to do so. When an attribute isn't removed we'll simply forget about the tracked duplicates. This involves some overhead for normal operation ''when'' in fact there are duplicate attributes on a tag, but that overhead is minimal in the form of integer pairs of indices for each duplicated attribute.
Props dmsnell, zieladam.
Fixes#58119.
Built from https://develop.svn.wordpress.org/trunk@56684
git-svn-id: http://core.svn.wordpress.org/trunk@56196 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This patch updates documentation and an internal variable name within the
HTML Processor class so that they are more helpful and complete to a reader.
There should be no functional or visual changes in this patch.
Props dmsnell, mukesh27.
Fixes#59267.
Built from https://develop.svn.wordpress.org/trunk@56565
git-svn-id: http://core.svn.wordpress.org/trunk@56077 1a063a9b-81f0-0310-95a4-ce76da25c4cd
When encountering elements that imply switching into the RAWTEXT parsing state,
the Tag Processor should skip processing until exiting the RAWTEXT state.
In this patch the Tag Processor does just that, except for the case of the
deprecated XMP element which implies further and more complicated rules.
There's an implicit assumption that the SCRIPT ENABLED flag in HTML parsing
is enabled so that the contents of NOSCRIPT can be skipped. Otherwise, it would
be required to parse the contents of that tag.
Props dmsnell.
Fixes#59292.
Built from https://develop.svn.wordpress.org/trunk@56563
git-svn-id: http://core.svn.wordpress.org/trunk@56075 1a063a9b-81f0-0310-95a4-ce76da25c4cd
The `$current_token` reference has been stored in the HTML Processor itself, but I suggested to move it into the externalized state so that it can be stored and replaced.
In this patch the reference is moved to that state variable and it should become more possible to save and load state, to resume execution after pausing.
Props dmsnell.
Fixes#59268.
Built from https://develop.svn.wordpress.org/trunk@56558
git-svn-id: http://core.svn.wordpress.org/trunk@56070 1a063a9b-81f0-0310-95a4-ce76da25c4cd
It was a design goal of the HTML Processor to abort processing its input document when encountering unsupported markup. Unfortunately there was no test for this and so-far, the HTML Processor has paused, but continued processing in these situations.
In this patch a new test ensures that the HTML Processor stops and refuses to move forward after encountering any unsupported markup. It also ensures that it doesn't report any current tag names since unsupported markup could imply that the read tag name is different than the parsed tag name.
Props dmsnell.
Fixes#59167.
Built from https://develop.svn.wordpress.org/trunk@56493
git-svn-id: http://core.svn.wordpress.org/trunk@56005 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This patch adds support to process the BUTTON element. This requires adding some additional semantic rules to handle situations where a BUTTON element is already in scope.
Also included is a fixup to enforce that `WP_HTML_Processor::next_tag()` never returns for a tag closer. This is useful with the Tag Processor, but not for the HTML Processor. There were tests relying on this behavior to assert that internal processes were working as they should, but those tests have been updated to use the semi-private `step()` function, which does stop on tag closers.
This patch is one in a series of changes to expand support within the HTML API, moving gradually to allow for more focused changes that are easier to review and test. The HTML Processor is a work in progress with a certain set of features slated to be ready and tested by 6.4.0, but it will only contain partial support of the HTML5 specification even after that. Whenever it cannot positively recognize and process its input it bails, and certain function stubs and logical stubs exist to structure future expansions of support.
Props dmsnell.
Fixes#58961.
Built from https://develop.svn.wordpress.org/trunk@56380
git-svn-id: http://core.svn.wordpress.org/trunk@55892 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Adjust the code style according to the rules that the linting process in Gutenberg requires.
There are only a couple code changes that should have no effect on the runtime:
- A missing check to verify that only `UTF-8` is supported has been added (brought up because it was identified as an undefined variable).
- A few `return false;` statements have been added to avoid having the linter complain that functions don't return a value despite indicating they return `bool`. The functions are stubs for coming support and currently `throw`, so the `return` statements are unreachable.
Props dmsnell, costdev, davidbaumwald, peterwilsoncc, SergeyBiryukov.
Fixes#58918.
Built from https://develop.svn.wordpress.org/trunk@56363
git-svn-id: http://core.svn.wordpress.org/trunk@55875 1a063a9b-81f0-0310-95a4-ce76da25c4cd
In this patch we're introducing support for the SPAN element, which is the first
in the class of "any other tag" in the "in body" insertion mode.
This patch introduces the mechanisms required to handle that class of tags but
only introduces SPAN to keep the change focused. With the tests and mechanisms
in place it will be possible to follow-up and add another limited set of tags.
It's important that this not use the default catch-all in the switch handling
`step_in_body` because that would catch tags that have specific rules in previous
case statements that aren't yet added. For example, we don't want to treat the
`TABLE` element as "any other tag".
Props dmsnell.
Fixes#58907.
Built from https://develop.svn.wordpress.org/trunk@56331
git-svn-id: http://core.svn.wordpress.org/trunk@55843 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This patch introduces the //first// of //many// iterations on the evolution of the HTML API, the HTML Processor, which is built in order to understand HTML structure including nesting, misnesting, and complicated semantic rules.
In the first iteration, the HTML Processor is arbitrarily limited to a minimal subset of functionality so that we can review it, ship it, test it, and collect feedback before moving forward. This means that this patch is more or less an extension to the Tag Processor query language, providing the ability not only to scan for a tag of a given name, but also to find an HTML element in a specific nesting path.
The HTML Processor also aborts any time it encounters:
- a tag that isn't a `P`, `DIV`, `FIGURE`, `FIGCAPTION`, `IMG`, `STRONG`, `B`, `EM`, `I`, `A`, `BIG`, `CODE`, `FONT`, `SMALL`, `STRIKE`, `TT`, or `U` tag. this limit exists because many HTML elements require specific rules and we are trying to limit the number of rules introduced at once. this work is targeted at existing work in places like the image block.
- certain misnesting constructs that evoke complicated resolution inside the HTML spec. where possible and where simple to do reliably, certain parse errors are handled. in most cases the HTML Processor aborts.
The structure of the HTML Processor is established in this patch. Further spec-compliance comes through filling out //more of the same// kind and nature of code as is found in this patch. Certain critical HTML algorithms are partially supported, and where support requires more than is present, the HTML Processor acknowledges this and refuses to operate.
In this patch are explorations for how to verify that new HTML support is fully added (instead of allowing for partial updates that leave some code paths non-compliant). Performance is hard to measure since support is so limited at the current time, but it should generally follow the performance of the Tag Processor somewhat close as the overhead is minimized as much as practical.
Props dmsnell, zieladam, costdev.
Fixes#58517.
Built from https://develop.svn.wordpress.org/trunk@56274
git-svn-id: http://core.svn.wordpress.org/trunk@55786 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This moves a reference link in `::get_attribute_names_with_prefix()` below the code example, so that it is correctly displayed in the Developer Resources.
Includes updating some other `@see` tags for consistency as per the documentation standards.
Additionally, the example code for `WP_HTML_Tag_Processor::get_tag()` is updated to show lowercase tag names in the input HTML, so that it does not convey the wrong impression that the uppercase output from `::get_tag()` depends on the case of the input HTML.
Follow-up to [55203].
Props dmsnell, johnbillion, audrasjb, SergeyBiryukov.
Fixes#58254.
Built from https://develop.svn.wordpress.org/trunk@55724
git-svn-id: http://core.svn.wordpress.org/trunk@55236 1a063a9b-81f0-0310-95a4-ce76da25c4cd
In [55718] the Unicode replacement character was mistakenly removed. The purpose of including the character was to communicate what it looks like and why the Tag Processor won't insert it into the document.
This changeset brings the character back and adds a small clue to fix the confusion that may lead to its removal.
Follow-up to [55718].
Props dmsnell.
Fixes#58256
See #57840.
Built from https://develop.svn.wordpress.org/trunk@55723
git-svn-id: http://core.svn.wordpress.org/trunk@55235 1a063a9b-81f0-0310-95a4-ce76da25c4cd
A bug was discovered where where the parser wasn't returning to the
start of the affected tag after making some updates.
In few words, the Tag Processor has not been treating its own internal
pointer `bytes_already_parsed` the same way it treats its bookmarks.
That is, when updates are applied to the input document and then
`get_updated_html()` is called, the internal pointer transfers to
the newly-updated content as if no updates had been applied since
the previous call to `get_updated_html()`.
In this patch we're creating a new "shift accumulator" to account for
all of the updates that accrue before calling `get_updated_html()`.
This accumulated shift will be applied when swapping the input document
with the output buffer, which should result in the pointer pointing to
the same logical spot in the document it did before the udpate.
In effect this patch adds a single workaround for treating the
internal pointer like a bookmark, plus a temporary pointer which points
to the beginning of the current tag when calling `get_updated_html()`.
This will preserve the assumption that updating a document doesn't
move that pointer, or shift which tag is currently matched.
Props dmsnell, zieladam.
Fixes#58179.
Built from https://develop.svn.wordpress.org/trunk@55706
git-svn-id: http://core.svn.wordpress.org/trunk@55218 1a063a9b-81f0-0310-95a4-ce76da25c4cd
- Comments created by means of a tag closer with an invalid tag name, e.g. `</3>`.
- Comments closed with the invalid `--!>` closer. (Comments should be closed by `-->` but if the `!` appears it will also close it, in error.)
- Empty tag name elements, which are technically skipped over and aren't comments, e.g. `</>`.
Props dmsnell, costdev.
Fixes#58007.
Built from https://develop.svn.wordpress.org/trunk@55667
git-svn-id: http://core.svn.wordpress.org/trunk@55179 1a063a9b-81f0-0310-95a4-ce76da25c4cd
When setting a new value for an attribute multiple times and providing
multiple case variations of the attribute name the Tag Processor has
been appending multiple copies of the attribute into the updated HTML.
This means that only the first attribute set determines the value in
the final output, plus the output will //appear// wrong.
In this patch we're adding a test to catch the situation and resolving it
by using the appropriate comparable attribute name as a key for storing
the updates as we go. Previously we stored updates to the attribute by
its given `$name`, but when a new update of the same name with a
case variant was queued, it would not override the previously-enqueued
value as it out to have.
Props dmsnell, zieladam.
Fixes#58146.
Built from https://develop.svn.wordpress.org/trunk@55659
git-svn-id: http://core.svn.wordpress.org/trunk@55171 1a063a9b-81f0-0310-95a4-ce76da25c4cd
In this patch we're adding `has_self_closing_flag()` to the HTML Tag Processor.
This exposes whether a currently-matched tag contains the self-closing flag `/`.
This information is critical for the evolution of the HTML API in order
to track and parse HTML structure, specifically, knowing whether an
HTML foreign element is self-closing or not.
Props dmsnell, zieladam.
Fixes#58009.
Built from https://develop.svn.wordpress.org/trunk@55619
git-svn-id: http://core.svn.wordpress.org/trunk@55131 1a063a9b-81f0-0310-95a4-ce76da25c4cd
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.
Built from https://develop.svn.wordpress.org/trunk@55555
git-svn-id: http://core.svn.wordpress.org/trunk@55067 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Documents the shorthand usage, i.e. `$this->next_tag( 'img' )`, of `WP_HTML_Tag_Processor::next_tag()`.
Also includes table alignments and formatting adjustments in the class docs.
Follow-up to [55203], [55206].
Props zieladam, poena, dmsnell, costdev, hellofromTonya.
Fixes#57863.
See #57575.
Built from https://develop.svn.wordpress.org/trunk@55477
git-svn-id: http://core.svn.wordpress.org/trunk@55010 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Setting a bookmark on a tag should set its "start" position before the opening "<", e.g.:
{{{
<div> Testing a <b>Bookmark</b>
----------------^
}}}
The previous calculation assumed this is always one byte to the left from `$tag_name_starts_at`.
However, in a closing tag that index points to a solidus symbol "/":
{{{
<div> Testing a <b>Bookmark</b>
----------------------------^
}}}
The bookmark should therefore start two bytes before the tag name:
{{{
<div> Testing a <b>Bookmark</b>
---------------------------^
}}}
This changeset achieves this by:
* Using the correct starting index for closing tag bookmarks.
* Adding `array( 'tag_closers' => 'visit' )` in `WP_HTML_Tag_Processor::seek()`.
Follow-up to [55203].
Props zieladam, dmsnell, flixos90.
Fixes#57787.
See #57575.
Built from https://develop.svn.wordpress.org/trunk@55407
git-svn-id: http://core.svn.wordpress.org/trunk@54940 1a063a9b-81f0-0310-95a4-ce76da25c4cd
When the HTML API was introduced a number of fields were switched from private visibility to protected so that Gutenberg and other systems could more easily enhance the behaviors through subclassing. The $this->html property was overlooked but important for systems using the Tag Processor to stich HTML, specifically performing operations on innerHTML and innerText.
Follow-up [55203].
Props dmsnell.
See #57575.
Built from https://develop.svn.wordpress.org/trunk@55402
git-svn-id: http://core.svn.wordpress.org/trunk@54935 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit pulls in the HTML Tag Processor from the Gutenbeg repository.
The Tag Processor attempts to be an HTML5-spec-compliant parser that provides the ability in PHP to find specific HTML tags and then add, remove, or update attributes on that tag. It provides a safe and reliable way to modify the attribute on HTML tags.
More information: https://github.com/WordPress/wordpress-develop/pull/3920.
Props: antonvlasenko, bernhard-reiter, costdev, dmsnell, felixarntz, gziolo, hellofromtonya, zieladam, flixos90, ntsekouras, peterwilsoncc, swissspidy, andrewserong, onemaggie, get_dave, aristath, scruffian, justlevine, andraganescu, noisysocks, dlh, soean, cbirdsong, revgeorge, azaozz.
Fixes#57575.
Built from https://develop.svn.wordpress.org/trunk@55203
git-svn-id: http://core.svn.wordpress.org/trunk@54736 1a063a9b-81f0-0310-95a4-ce76da25c4cd