-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interactivity API: Skip instead of bail out if HTML contains SVG or MATH #6091
Interactivity API: Skip instead of bail out if HTML contains SVG or MATH #6091
Conversation
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN:
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been something that @sirreal and I have been working with lately.
it's complicated, and I might recommend creating something marginally more robust in the meantime, because the rules for the self-closing flag change within SVG and MathML.
that is, non-void elements which contain the self-closing flag are treated as empty elements.
there are escapes from SVG and MathML that I think we can practically overlook for now because the logic for detecting them is complicated, but a new method $p->skip_foreign_content()
might serve us better.
this method would be like next_balanced_tag()
except it will only pay attention to the current tag.
private function skip_foreign_content() {
$tag_name = $this->get_tag_name();
$depth = 1;
while ( $depth > && $this->next_tag( $tag_name ) ) {
if ( $this->has_self_closing_flag() ) {
continue;
}
$depth += $this->is_tag_closer() ? -1 : 1;
}
return 0 === $depth;
}
I created the function as suggested, with small tweaks to make it work as expected. I also added some tests in a pair programming session with @michalczaplinski I would feel much more confident with a second review, thanks @dmsnell! |
if ( $this->has_self_closing_flag() ) { | ||
continue; | ||
} | ||
$depth += $this->is_tag_closer() ? -1 : 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dmsnell What about a title
tag that appears inside of an SVG? Since it's in XML parsing mode, would it still be processed as RCDATA here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@westonruter this is a great question, and you're right. it's not RCDATA when found inside SVG. this is all the more reason to focus only on SVG and MathML tags and ignore the others.
in #6006 what I'm looking to do is have the HTML Processor tell the Tag Processor that it's inside foreign content, and while that flag is active, we'll disable the special handling of tokens like TITLE and the others.
it's complicated and we explored trying to put the foreign-content handling code inside the Tag Processor, but that ended up demanding that we re-implement much of the HTML Processor's functionality there, so it will probably be the case that you need the HTML Processor to adequately parse inside SVG and MathML.
in the meantime, if one overlooks the HTML elements which break out into the nearest "integration point," then I think that skipping from SVG tag to SVG tag will be reliable enough for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aha I misread your statement @westonruter concerning TITLE
. in this case it shouldn't matter that we're checking is_tag_closer()
because we're not visiting the TITLE
tag - we're skipping straight to whichever tag (SVG
or MATH
) opened the foreign content.
Sorry @westonruter for re-requesting your review again, but the code has change quite a bit 😅 |
); | ||
$html = ' | ||
<header> | ||
<svg height="100"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a title
tag here just to make sure it works as expected.
<svg height="100"> | |
<svg height="100"> | |
<title>Red Circle</title> |
<header> | ||
<svg height="100"> | ||
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" /> | ||
<div data-wp-bind--id="myPlugin::state.id"></div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not an SVG element?
<div data-wp-bind--id="myPlugin::state.id"></div> | |
<g data-wp-bind--id="myPlugin::state.id" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a good example of a bad example anyway 🙃 as the div
here would close the open SVG
element 🤦♂️
the full set of HTML tags which break out to the nearest integration point are:
"b", "big", "blockquote", "body", "br", "center", "code", "dd", "div", "dl", "dt", "em", "embed", "h1", "h2", "h3", "h4", "h5", "h6", "head", "hr", "i", "img", "li", "listing", "menu", "meta", "nobr", "ol", "p", "pre", "ruby", "s", "small", "span", "strong", "strike", "sub", "sup", "table", "tt", "u", "ul", "var"
and some FONT
start tags containing specific attributes. also some end tags.
https://html.spec.whatwg.org/#parsing-main-inforeign
the "integration point" thing is a bit more complicated than it appears at a glance. there can be a stack of open elements that zigzags between HTML and foreign content, and some SVG/MathML tags themselves create new integration points, so a DIV
inside one of those regions won't close out the SVG
- it will only close out everything up until that element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was to use an element that we know the server directive processing would process. As far as I know, it should skip SVG tags and its inner accepted elements by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@c4rl0sbr4v0 my point was mostly that in an HTML document, the DIV
in this example closes out the SVG
, and so the skip_foreign_content()
function would fail here.
if you wanted to use an element that's neutral on this, it would be fine to use something like <aside>
or <data>
or <abbr>
, as these and any other elements not listed above do not affect the foreign content stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they are edge cases, maybe is better to just use what should be inside a SVG tag.
if you wanted to use an element that's neutral on this, it would be fine to use something like
or or , as these and any other elements not listed above do not affect the foreign content stack.
You are becoming a huge expert in HTML processing @dmsnell 😃
$html = ' | ||
<header> | ||
<math data-wp-bind--id="myPlugin::state.math"> | ||
<div data-wp-bind--id="myPlugin::state.id"></div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably better to use actual MathML, right?
<div data-wp-bind--id="myPlugin::state.id"></div> | |
<mrow data-wp-bind--id="myPlugin::state.id" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was to use an element that we know the server directive processing would process. As far as I know, it should skip SVG tags and its inner accepted elements by default.
Similar here but with MathML
$content = '<div><span>Not closed</div>'; | ||
$p = new WP_Interactivity_API_Directives_Processor( $content ); | ||
$p->next_tag(); | ||
$this->assertTrue( $p->skip_foreign_content() ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's actually no foreign content here. Should a better name be chosen than skip_foreign_content()
like jump_to_tag_closer()
?
I addressed the requested changes in 23f25c1 |
* | ||
* @return bool Whether the foreign content was successfully skipped. | ||
*/ | ||
public function jump_to_tag_closer(): bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just realized that skip
is the terminology used in the tag processor and also in the description so it would probably be better here.
public function jump_to_tag_closer(): bool { | |
public function skip_to_tag_closer(): bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true.
$html = ' | ||
<header> | ||
<math data-wp-bind--id="myPlugin::state.math"> | ||
<mrow data-wp-bind--id="myPlugin::state.id" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indentation nit.
<mrow data-wp-bind--id="myPlugin::state.id" /> | |
<mrow data-wp-bind--id="myPlugin::state.id" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple nits left, so I'm pre-approving.
Done in 8ea4a69 Ready to commit then! |
@dmsnell Is this one good to go in your opinion as well? |
I'm afraid Dennis is AFK until the 26th of February. Not sure if he will answer on time 😅 |
Alright. Committed in https://core.trac.wordpress.org/changeset/57649. We can always commit follow-ups if needed. |
@sirreal has a lot of experience with HTML API, he could take a look at it too |
👍 The changes as committed here look good to me. |
All good from my end too. Of course we could make this more robust, but doing so inches us towards the HTML Processor and isn't worth recreating. I think we're going to start exploring using the HTML Processor for the Interactivity API, so in time this will be a non-issue. |
When there is a SVG icon in the navigation menu. The Interactivity API is not server-side processing the directives. This is causing a flash effect when the runtime process the JavaScript part.
Trac ticket: https://core.trac.wordpress.org/ticket/60517
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.