When encountering inline SVG and MathML content in an HTML document, there are certain "integration points" which transition back into the HTML parsing ruleset. Previously, the HTML API was incorrectly switching into the namespace of the element transitioning into that ruleset.
In this patch, the correct transition is made, where all integration points refer to HTML rules, while non-integration points refer to the rules of the namespace corresponding to the token itself.
Developed in https://github.com/wordpress/wordpress-develop/pull/7425
Discussed in https://core.trac.wordpress.org/ticket/61576
Props dmsnell, jonsurrell.
See #61576.
Built from https://develop.svn.wordpress.org/trunk@59099
git-svn-id: http://core.svn.wordpress.org/trunk@58495 1a063a9b-81f0-0310-95a4-ce76da25c4cd
HTML often appears in ways that are unexpected. It may be missing implicit tags, may have unquoted, single-quoted, or double-quoted attributes, may contain duplicate attributes, may contain unescaped text content, or any number of other possible invalid constructions. The HTML API understands all fo these inputs, but downline parsers may not, and HTML snippets which are safe on their own may introduce problems when joined with other HTML snippets.
This patch introduces the `serialize()` method on the HTML Processor, which prints a fully-normative HTML output, eliminating invalid markup along the way. It produces a string which contains every missing tag, double-quoted attributes, and no duplicates. A `normalize()` static method on the HTML Processor provides a convenient wrapper for constructing a fragment parser and immediately serializing.
Subclasses relying on the `serialize_token()` method may perform structural HTML modifications with as much security as the upcoming `\Dom\HTMLDocument()` parser will, though these are not
able to provide the full safety that will eventually appear with `set_inner_html()`.
Further work may explore serializing to XML (which involves a number of other important transformations) and adding constraints to serialization (such as only allowing inline/flow/formatting elements and text).
Developed in https://github.com/wordpress/wordpress-develop/pull/7331
Discussed in https://core.trac.wordpress.org/ticket/62036
Props dmsnell, jonsurrell, westonruter.
Fixes#62036.
Built from https://develop.svn.wordpress.org/trunk@59076
git-svn-id: http://core.svn.wordpress.org/trunk@58472 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Previously, there were a few cases where the modifiable text read from an HTML comment differs slightly from the parsed value of its inner text in a browser. This is due to the specific way that invalid HTML syntax tokens become "bogus comments."
This patch introduces a new method to the Tag Processor to allow differentiating these specific cases, such as when copying or serializing HTML from one source to another. Similar code has already been in use in the html5lib tests, and this patch simplifies the test runner, evidencing the fact that this method was already needed.
Developed in https://github.com/wordpress/wordpress-develop/pull/7342
Discussed in https://core.trac.wordpress.org/ticket/62036
Props dmsnell, jonsurrell.
See #62036.
Built from https://develop.svn.wordpress.org/trunk@59075
git-svn-id: http://core.svn.wordpress.org/trunk@58471 1a063a9b-81f0-0310-95a4-ce76da25c4cd
PHP 8.4 deprecates implicitly nullable parameters, i.e. typed parameters with a `null` default value, which are not explicitly declared as nullable.
This commit the one instance of this in the `WP_HTML_Processor` class.
Fixed by adding the nullability operator to the type, which is supported since PHP 7.1, so we can use it now the minimum supported PHP version is PHP 7.2.
As this deprecation is thrown at compile time, it can be seen at the top of the test output when running on PHP 8.4 (which will be gone once this change has been committed). It is not possible to write a test to cover this.
Ref: https://wiki.php.net/rfc/deprecate-implicitly-nullable-types
Follow-up to [58867], [58769], [58304], [58192].
Props jrf.
See #62061.
Built from https://develop.svn.wordpress.org/trunk@59053
git-svn-id: http://core.svn.wordpress.org/trunk@58449 1a063a9b-81f0-0310-95a4-ce76da25c4cd
There are places in the HTML API code where some tools get confused and flag invalid types for the return of a function because they are unable to detect that the end of the function is unreachable.
Since PHP doesn't provide a way to encode total matching in the source code, this patch adds a few extra lines in those unreachable locations to satisfy any tooling which isn't able to fully analyze the code.
Additionally this serves as extra guarding in case someone changes these functions in a way which would break them and the existing test suite doesn't catch those breakages.
Developed in https://github.com/WordPress/wordpress-develop/pull/7315
Discussed in https://core.trac.wordpress.org/ticket/62018
Props dlh, dmsnell.
Fixes#62018.
Built from https://develop.svn.wordpress.org/trunk@59001
git-svn-id: http://core.svn.wordpress.org/trunk@58397 1a063a9b-81f0-0310-95a4-ce76da25c4cd
The `pop_until( $tag_name )` method in the stack of open elements should only be examining HTML elements, but it has only been checking the tag name. This has led to closing the wrong tags when run from inside foreign content. A very specific situation where this may arise is when a `TEMPLATE` closer is found inside foreign content, inside another template.
{{{
HTML:template SVG:template HTML:/template
<template><svg><template><foreignObject><div></template><div>
╰──< this outer TEMPLATE is closed by this one >───╯
}}}
This patch constains the method to checking for elements matching the tag name which are in the HTML namespace so that the proper detection occurs.
Developed in https://github.com/WordPress/wordpress-develop/pull/7286
Discussed in https://core.trac.wordpress.org/ticket/61576
Follow-up to [58867].
Props dmsnell, jonsurrell.
See #61576.
Built from https://develop.svn.wordpress.org/trunk@58992
git-svn-id: http://core.svn.wordpress.org/trunk@58388 1a063a9b-81f0-0310-95a4-ce76da25c4cd
The HTML API has been behaving as if CSS class name selectors matched class names in an ASCII case-insensitive manner. This is only true if the document in question is set to quirks mode. Unfortunately most documents processed will be set to no-quirks mode, meaning that some CSS behaviors have been matching incorrectly when provided with case variants of class names.
In this patch, the CSS methods have been audited and updated to adhere to the rules governing ASCII case sensitivity when matching classes. This includes `add_class()`, `remove_class()`, `has_class()`, and `class_list()`. Now, it is assumed that a document is in no-quirks mode unless a full HTML parser infers quirks mode, and these methods will treat class names in a byte-for-byte manner. Otherwise, when a document is in quirks mode, the methods will compare the provided class names against existing class names for the tag in an ASCII case insensitive way, while `class_list()` will return a lower-cased version of the existing class names.
The lower-casing in `class_list()` is performed for consistency, since it's possible that multiple case variants of the same comparable class name exists on a tag in the input HTML.
Developed in https://github.com/WordPress/wordpress-develop/pull/7169
Discussed in https://core.trac.wordpress.org/ticket/61531
Props dmsnell, jonsurrell.
See #61531.
Built from https://develop.svn.wordpress.org/trunk@58985
git-svn-id: http://core.svn.wordpress.org/trunk@58381 1a063a9b-81f0-0310-95a4-ce76da25c4cd
HTML parsing rules at times differentiate character tokens that are all null bytes, all whitespace, or other content. This patch introduces a new function which may be used to classify text node sub-regions and lead to more efficient application of these parsing rules.
Further, when classified in this way, application code may skip some rules and decoding entirely, improving performance. For example, this can be used to ease the implementation of skipping inter-element whitespace, which is usually not rendered.
Developed in https://github.com/WordPress/wordpress-develop/pull/7236
Discussed in https://core.trac.wordpress.org/ticket/61974
Props dmsnell, jonsurrell.
Fixes#61974.
Built from https://develop.svn.wordpress.org/trunk@58970
git-svn-id: http://core.svn.wordpress.org/trunk@58366 1a063a9b-81f0-0310-95a4-ce76da25c4cd
A mistake in the original code handling opening A elements in the HTML Processor led to mistakes in parsing where the Processor would bail in situations when it could have proceeded. While this was errant behavior, it didn't violate the public contract since it would bail in these situations.
This patch fixes the mistake, which was to only break out of the innermost loop instead of breaking from the containing loop, which resolves the issue.
Developed in https://github.com/WordPress/wordpress-develop/pull/7281
Discussed in https://core.trac.wordpress.org/ticket/61576
Follow-up to [56274].
Props jonsurrell.
See #61576.
Built from https://develop.svn.wordpress.org/trunk@58966
git-svn-id: http://core.svn.wordpress.org/trunk@58362 1a063a9b-81f0-0310-95a4-ce76da25c4cd
When support was added for foreign content, an ambiguity in the HTML specification led to code that followed the wrong path when encountering a self-closing SCRIPT element in the SVG namespace. Further, a fallthrough was discovered during manual testing.
This patch adds a new test to assert the proper behaviors and fixes these issues. In the case of the SCRIPT element, the outcome was the same with the wrong code path, making the defect benign. In the case of the fallthrough, the wrong behavior would occur.
The updates in this patch also resolves a todo relating to the spec ambiguity.
Developed in https://github.com/wordpress/wordpress-develop/pull/7164
Discussed in https://core.trac.wordpress.org/ticket/61576
Follow-up to [58868].
Props: dmsnell, jonsurrell.
See #61576.
Built from https://develop.svn.wordpress.org/trunk@58871
git-svn-id: http://core.svn.wordpress.org/trunk@58267 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Previously, `WP_HTML_Processor::expects_closer()` would report `true` for self-closing foreign elements when called without supplying a node in question, but it should have been reporting `true` just as it does for HTML elements.
This patch adds a test case demonstrating the issue and a bugfix.
The `html5lib` test runner was relying on the incorrect behavior, accidentally working. This is also corrected and the `html5lib` test now relies on the correct behavior of `expects_closer()`.
Developed in https://github.com/wordpress/wordpress-develop/pull/7162
Discussed in https://core.trac.wordpress.org/ticket/61576
Follow-up to [58868].
Props: dmsnell.
See #61576.
Built from https://develop.svn.wordpress.org/trunk@58870
git-svn-id: http://core.svn.wordpress.org/trunk@58266 1a063a9b-81f0-0310-95a4-ce76da25c4cd
When `set_modifiable_text()` was added to the Tag Processor, it was considered that the same information could be queried after setting its value and before proceeding to the next token, but unfortunately overlooked that if the starting modifiable text length was zero, then the read in `get_modifiable_text()` would ignore enqueued updates.
In this patch, `get_modifiable_text()` will read any enqueued values before reading from the input HTML document to ensure consistency.
Follow-up to [58829].
Props dmsnell, jonsurrell, ramonopoly.
Fixes#61617.
Built from https://develop.svn.wordpress.org/trunk@58866
git-svn-id: http://core.svn.wordpress.org/trunk@58262 1a063a9b-81f0-0310-95a4-ce76da25c4cd
When the Tag Processor (or HTML Processor) attempts to parse certain
incomplete script tags, the parser enters an infinite loop and will
hang indefinitely. The conditions to reach this situation are:
- Input HTML ends with an open script tag.
- The final character of input is `-` or `<`.
The infinite loop was caused by the parser-advancing increment not being
called when two `||` OR conditions short-circuited. If the first
condition was true, the `$at++` code was never reached.
This path resolves the issue.
Developed in https://github.com/wordpress/wordpress-develop/pull/7128
Discussed in https://core.trac.wordpress.org/ticket/61810
Follow-up to [55203].
Props: dmsnell, jonsurrell.
Fixes#61810.
Built from https://develop.svn.wordpress.org/trunk@58845
git-svn-id: http://core.svn.wordpress.org/trunk@58241 1a063a9b-81f0-0310-95a4-ce76da25c4cd
When setting an an attribute value in the HTML API, WordPress may reject
an update based on rules in `kses`. In these cases, the return value from
an escaping function will be an empty string, and the HTML API should
reject the update. Unfortunately, it currently reports that it updates the
attribute but sets an empty string value, which is misleading.
In this patch, the HTML API will refuse the attribute update and return
false to indicate as much when WordPress rejects the updates.
Developed in https://github.com/wordpress/wordpress-develop/pull/7114
Discussed in https://core.trac.wordpress.org/ticket/61719
Follow-up to [58472].
Props: amitraj2203, dmsnell, mukesh27.
Fixes#61719.
Built from https://develop.svn.wordpress.org/trunk@58844
git-svn-id: http://core.svn.wordpress.org/trunk@58240 1a063a9b-81f0-0310-95a4-ce76da25c4cd
The HTML Processor has only supported a specific kind of parsing mode
called _the fragment parsing mode_, where it behaves in the same way
that `node.innerHTML = html` does in the DOM. This mode assumes a
context node and doesn't support parsing an entire document.
As part of work to add more spec support to the HTML API, this patch
introduces a full parsing mode, which can parse a full HTML document
from start to end, including the doctype declaration and head tags.
Developed in https://github.com/wordpress/wordpress-develop/pull/6977
Discussed in https://core.trac.wordpress.org/ticket/61576
Props: dmsnell, jonsurrell.
See #61576.
Built from https://develop.svn.wordpress.org/trunk@58836
git-svn-id: http://core.svn.wordpress.org/trunk@58232 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Insertion modes in an HTML parser may include instructions like "process
the token in the IN HEAD insertion mode." The rules do not change the
insertion mode of the parser, but the errors are triggered outside of the
rules for the current insertion mode. These will be misleading when
bailing on these instructions, because it will point someone to the wrong
place in the code to find the source of the error.
In this patch all of the bail-points due to lacking insertion mode support
are hard-coded to better orient someone to the section of the code lacking
support for handling the input HTML.
Developed in https://github.com/wordpress/wordpress-develop/pull/7043
Discussed in https://core.trac.wordpress.org/ticket/61576
Follow-up to [58679].
Props: dmsnell, jonsurrell.
See #61576.
Built from https://develop.svn.wordpress.org/trunk@58781
git-svn-id: http://core.svn.wordpress.org/trunk@58183 1a063a9b-81f0-0310-95a4-ce76da25c4cd
In part of a larger review of CSS semantics and behaviors, this patch
takes the opportunity to remove leading whitespace in an updated class
attribute after the first class in the attribute has been removed.
Previously, if the first class name had been removed, the whitespace
that formerly followed it would remain in the class attribute. This
stood in contrast to removing other class names, which removed their
associated whitespace.
There should be no semantic or functional changes in this patch, only
a slightly-large diff for modified HTML documents that looks prettier
when removing the first class name in a class attribute.
Developed in https://github.com/WordPress/wordpress-develop/pull/6933
Discussed in https://core.trac.wordpress.org/ticket/61531
Props dmsnell, jonsurrell.
See #61531.
Built from https://develop.svn.wordpress.org/trunk@58740
git-svn-id: http://core.svn.wordpress.org/trunk@58142 1a063a9b-81f0-0310-95a4-ce76da25c4cd
The HTML Processor internally throws an exception when it reaches HTML
that it knows it cannot process, but this exception is not made
available to calling code. It can be useful to extract more knowledge
about why it gave up, especially for debugging purposes.
In this patch, more context is added to the WP_HTML_Unsupported_Exception
and the last exception is made available to calling code through a new
method, `get_unsupported_exception()`.
Developed in https://github.com/WordPress/wordpress-develop/pull/6985
Discussed in https://core.trac.wordpress.org/ticket/61646
Props bernhard-reiter, dmsnell, jonsurrell.
See #61646.
Built from https://develop.svn.wordpress.org/trunk@58714
git-svn-id: http://core.svn.wordpress.org/trunk@58116 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Since the HTML Processor started visiting all nodes in a document, both
real and virtual, the breadcrumb accounting became a bit complicated
and it's not entirely clear that it is fully reliable.
In this patch the breadcrumbs are rebuilt separately from the stack of
open elements in order to eliminate the problem of the stateful stack
interactions and the post-hoc event queue.
Breadcrumbs are greatly simplified as a result, and more verifiably
correct, in this construction.
Developed in https://github.com/WordPress/wordpress-develop/pull/6981
Discussed in https://core.trac.wordpress.org/ticket/61576
Follow-up to [58590].
Props bernhard-reiter, dmsnell.
See #61576.
Built from https://develop.svn.wordpress.org/trunk@58713
git-svn-id: http://core.svn.wordpress.org/trunk@58115 1a063a9b-81f0-0310-95a4-ce76da25c4cd