Skip to content

fix: preserve siblings around raw HTML updates - #466

Merged
Mohamed Mansour (mohamedmansour) merged 4 commits into
microsoft:mainfrom
mohamedmansour:mohamedmansour-harden-raw-html
Aug 23, 2026
Merged

fix: preserve siblings around raw HTML updates#466
Mohamed Mansour (mohamedmansour) merged 4 commits into
microsoft:mainfrom
mohamedmansour:mohamedmansour-harden-raw-html

Conversation

@mohamedmansour

@mohamedmansour Mohamed Mansour (mohamedmansour) commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Raw HTML updates currently replace their entire parent, which can erase static siblings and structural anchors. Client-created bindings also use a wrapper that diverges from SSR structure.

This change gives each raw binding an indexed comment-bounded sibling range. Updates parse in the live parent context with an ephemeral DOM Range, preserve adjacent content, and keep instance node ownership synchronized for keyed movement, removal, reconnects, shadow DOM, light DOM, and table rows.

The parser and protocol carry raw-text context separately from brace escaping so <style> and other HTML text-only contexts remain marker-free. HandlerPlugin::on_binding_start and on_binding_end now receive the owned-range boolean directly; the coordinated build/render contract makes this intentional API change safe, and all built-in plugins were updated together.

Coverage includes multi-node/empty transitions, adjacent raw ranges, escaped text alignment, marker-like comments, contextual <tbody> parsing, client-created instances, reconnects, keyed identity/removal, light DOM, and raw CSS hydration.

Performance impact:

  • Raw SSR ranges add 19 bytes per single-digit binding index; ordinary and raw-text signals add no markers.
  • The minified framework bundle grows by 2,440 bytes (962 bytes gzip, 853 bytes Brotli).
  • No live DOM Range is retained; a Range is allocated only when raw HTML actually changes.

Validation:

  • Focused raw Playwright matrix: 11 passed
  • Full framework: 253 unit passed; 248 E2E passed, 2 skipped
  • Handler tests: 403 passed
  • cargo xtask check: passed
  • Docs build: passed
  • Read-only specialist review: no high-confidence findings

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0807f5af-f9d9-45ad-b7d2-907084c53ea0
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0807f5af-f9d9-45ad-b7d2-907084c53ea0

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the WebUI end-to-end raw HTML contract (parser → protocol → handler → client runtime) so reactive triple-brace updates replace only the intended sibling range instead of replacing a whole parent node, preserving static/conditional siblings and aligning client-created DOM structure with SSR.

Changes:

  • Introduce indexed comment-bounded ownership ranges (<!--wN--> / <!--/wN-->) for raw HTML bindings and update the framework hydration/update logic to patch within the bounded range.
  • Extend protocol/spec to carry raw_text_context for signals in HTML text-only/raw-text contexts, and propagate the binding “owns range” boolean to handler plugins.
  • Add/expand Playwright and unit coverage for sibling preservation, table-context parsing, adjacent raw ranges, keyed identity/movement/removal, reconnects, and raw-text-context behavior.

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/webui-framework/tests/fixtures/raw-html-light/webui.config.json Adds Light DOM fixture configuration.
packages/webui-framework/tests/fixtures/raw-html-light/state.json Adds initial state for Light DOM raw HTML fixture.
packages/webui-framework/tests/fixtures/raw-html-light/src/test-raw-html-light/test-raw-html-light.html Fixture template covering sibling preservation + table row context.
packages/webui-framework/tests/fixtures/raw-html-light/src/index.html Fixture page entry for the Light DOM raw HTML fixture.
packages/webui-framework/tests/fixtures/raw-html-light/raw-html-light.spec.ts Playwright coverage for Light DOM sibling preservation + <tbody> contextual parsing.
packages/webui-framework/tests/fixtures/raw-html-light/element.ts Fixture element with observables used by Light DOM tests.
packages/webui-framework/tests/fixtures/raw-html-conditional/state.json Expands fixture state for additional raw scenarios (adjacent, inline, table, keyed).
packages/webui-framework/tests/fixtures/raw-html-conditional/src/test-raw-html/test-raw-html.html Extends fixture template to exercise multiple raw/text adjacency and table context.
packages/webui-framework/tests/fixtures/raw-html-conditional/src/test-raw-html-keyed-repeat/test-raw-html-keyed-repeat.html Adds keyed repeat fixture for raw range identity/movement/removal.
packages/webui-framework/tests/fixtures/raw-html-conditional/src/index.html Registers additional fixture component on the test page.
packages/webui-framework/tests/fixtures/raw-html-conditional/raw-html-conditional.spec.ts Major Playwright expansion covering raw range ordering, reconnect, adjacency, raw CSS, client-created instances, keyed behavior.
packages/webui-framework/tests/fixtures/raw-html-conditional/element.ts Adds new fixture component and methods for keyed repeat manipulation.
packages/webui-framework/src/template-element.ts Core runtime changes: raw ownership anchors, SSR hydration raw range collection, bounded Range-based patching, instance node ownership updates.
packages/webui-framework/src/hydration-mismatch.test.ts Updates mismatch logic tests to reflect raw ranges being non-text-owned data.
packages/webui-framework/src/element/types.ts Updates TextBinding to support comment anchors + cached raw value + ownership metadata.
packages/webui-framework/src/element/markers.ts Adds raw marker helpers and SSR index support for raw ranges; skip logic updated accordingly.
packages/webui-framework/src/element/markers.test.ts Adds tests for raw range collection and legacy-looking comments within raw ranges.
packages/webui-framework/README.md Documents raw HTML update costs/constraints and SSR resolution behavior changes.
docs/guide/concepts/plugins/index.md Updates HandlerPlugin docs to include the new raw: bool parameter.
DESIGN.md Updates spec for raw_text_context, tx encoding, SSR marker set, and hydration/update semantics.
crates/webui-test-utils/src/lib.rs Extends fragment matchers and formatting for raw-text-context signals.
crates/webui-protocol/src/lib.rs Adds raw_text_signal(...) constructor and tests; sets defaults.
crates/webui-protocol/src/gen_webui.rs Regenerates protobuf struct with raw_text_context field.
crates/webui-protocol/proto/webui.proto Adds raw_text_context field to WebUIFragmentSignal schema.
crates/webui-parser/src/plugin/webui.rs Updates compilation of text runs for raw binding separation/adjacency and CSS signal comment handling.
crates/webui-parser/src/lib.rs Emits raw_text_signal for appropriate contexts and CSS comment signals; adds raw-text-context tests.
crates/webui-handler/src/plugin/webui.rs Emits <!--wN--> / <!--/wN--> markers for raw bindings using the new hook signature.
crates/webui-handler/src/plugin/mod.rs Updates HandlerPlugin trait signature and related tests/docs.
crates/webui-handler/src/plugin/fast.rs Updates plugin test to match new hook signature.
crates/webui-handler/src/plugin/fast_v3.rs Updates plugin implementation/tests to accept the new raw argument.
crates/webui-handler/src/plugin/fast_v2.rs Updates plugin implementation/tests to accept the new raw argument.
crates/webui-handler/src/lib.rs Threads owns_html_range into plugin hooks and uses raw_text_context to suppress range ownership for raw-text contexts.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread crates/webui-parser/src/lib.rs
Comment thread crates/webui-handler/src/lib.rs
Replace the deny-list check with an explicit match over the known
raw-text/RCDATA scope strings returned by boundary_parent_scope, so a
future scope value must be deliberately classified instead of
silently defaulting to text-only.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0807f5af-f9d9-45ad-b7d2-907084c53ea0
Document that raw_text_context only governs HTML sibling-marker
ownership and never changes escaping. signal.raw keeps its usual
meaning (HTML-encode vs. verbatim) uniformly across contexts,
including inside HTML raw-text elements (<script>, <style>, <xmp>)
which never decode character references, unlike RCDATA elements
(<title>, <textarea>) which do. Callers binding values that may
contain HTML-significant characters inside raw-text elements must
use the raw ({{{value}}}) form.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0807f5af-f9d9-45ad-b7d2-907084c53ea0
@mohamedmansour
Mohamed Mansour (mohamedmansour) merged commit 53d8c24 into microsoft:main Aug 23, 2026
24 checks passed
@mohamedmansour
Mohamed Mansour (mohamedmansour) deleted the mohamedmansour-harden-raw-html branch August 23, 2026 15:52
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.

3 participants