Skip to content

Interactivity API: Choose the data-wp-html placeholder marker in linear time. - #15541

Closed
luisherranz wants to merge 3 commits into
WordPress:trunkfrom
luisherranz:fix/interactivity-api-linear-html-marker
Closed

luisherranz wants to merge 3 commits into
WordPress:trunkfrom
luisherranz:fix/interactivity-api-linear-html-marker

Conversation

@luisherranz

@luisherranz luisherranz commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

What

Choose the data-wp-html placeholder marker in WP_Interactivity_API::process_directives() without searching the input, so this step no longer depends on what the input contains.

Why

The marker identifies the placeholders of one call, so it must not be something the input can forge. It was chosen by adding one underscore at a time and searching the whole input again after each one. That made the cost of this step depend on what the input contains, not only on its length. The step runs for every input that contains data-wp-.

How

As suggested in review, the marker is wp-interactivity-dangerous-html- followed by a UUID from wp_generate_uuid4(). The first data-wp-html host of a call that renders draws it, and the later hosts of the call reuse it. Nothing searches the input for it, and the placeholders keep a fixed length. The rendered output doesn't change.

A call in which no host renders draws nothing. Drawing the UUID on every call made the default theme's per-block calls a few microseconds slower than before, so the first host draws it.

test_substitution_cannot_be_forged now covers a lookalike of the new placeholder form that holds another UUID. test_inserted_html_is_not_searched_for_placeholders is removed: with a marker drawn per call, its token's HTML can no longer equal another host's placeholder.

Trac ticket: https://core.trac.wordpress.org/ticket/66498

Use of AI Tools

AI assistance: Yes
Tool(s): OpenCode
Model(s): Claude Opus 5.5, Claude Sonnet 5.5, GPT-6.1 Sol, GPT-6 Luna, GPT-6 Astra
Used for: 100% of the code, tests, and comments in this PR were autonomously generated by AI and reviewed by @luisherranz before the PR was opened.


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.

…near time.

`WP_Interactivity_API::process_directives()` holds back the trusted HTML of each `data-wp-html` host behind a placeholder comment, whose marker must occur nowhere in the input. The marker was chosen by appending one underscore at a time and searching the whole input again after each one, so input holding `wp-interactivity-dangerous-html` followed by a long run of underscores took quadratic time. The search runs for every input that holds `data-wp-`, whether or not a `data-wp-html` host renders.

The marker is now chosen in a single pass over the input. It is the base followed by one more underscore than the longest run of underscores that follows the base anywhere in the input, or the base alone when the input does not hold it. This is the marker the previous loop chose, so the output does not change.

Follow-up to [64386].

Props jonsurrell.
Fixes #66498.
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs.

Core Committers: Use this line as a base for the props when committing in SVN:

Props luisherranz, jonsurrell.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.


Props Bot commands

You can trigger Props Bot actions by commenting on this PR:

  • @propsbot add <username> to give props to a GitHub user. Requires write access.
  • @propsbot remove <username> to leave a user out of the props. Requires write access.
  • @propsbot refresh to refresh this list. Adding the props-bot label works too.

@luisherranz
luisherranz requested a review from sirreal November 5, 2026 21:37
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown

Test using WordPress Playground

The 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

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@sirreal sirreal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd recommend changing to a unique marker and dropping the loop.

…r call from a UUID.

`WP_Interactivity_API::process_directives()` holds back the trusted HTML of each `data-wp-html` host behind a placeholder comment, whose marker identifies the placeholders of that call. The previous commit chose the marker by scanning the input for the longest run of underscores that follows the base.

The marker is now the base followed by a UUID from `wp_generate_uuid4()`. The first host of a call that renders draws it, and the later hosts of the call reuse it. Nothing reads the input to choose it, and the placeholders keep a fixed length. A call in which no host renders draws no marker, so ordinary calls take no longer than before.

`test_substitution_cannot_be_forged` now also covers a lookalike of the new placeholder form that holds another UUID. `test_inserted_html_is_not_searched_for_placeholders` is removed: with a marker drawn per call, its token's HTML can no longer equal another host's placeholder.

Props jonsurrell.
See #66498.
Merge public trunk at e4cfb66.

Preserve lazy per-call UUID markers alongside nested scope restoration
and forward-only balanced-tag processing.
@github-actions

Copy link
Copy Markdown

A commit was made that fixes the Trac ticket referenced in the description of this pull request.

SVN changeset: 65371
GitHub commit: 71051fc

This PR will be closed, but please confirm the accuracy of this and reopen if there is more work to be done.

@github-actions github-actions Bot closed this Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants