fix(apps): promote inline srcDoc frames to their own layer (#8037) - #8076
Conversation
Mochi's WidgetFrame and the meetings AgentPanel render model-authored HTML in sandboxed frames whose documents are built inline and passed to srcDoc, so they never reach the gateway sandbox-doc mint that #7931 covered. Both frames carried no compositing promotion, leaving them exposed to the same measured failure mode: an engine lays the document out, runs its scripts, reports a correct height, and never rasterizes it -- a correctly sized, visible frame painting an empty box, silent by construction. Port #7931's exact remedy onto both sites: transform: translateZ(0) added to each iframe's inline style. Neither frame carried an existing transform, so the plain 3D form suffices; nothing about sandbox flags, CSP, or how the documents are built changes, matching the invariants #7931 kept. Tests mirror #7931's promotion assertions: a new mochi test renders WidgetFrame and pins the transform plus the survival of its existing inline sizing, and the AgentPanel html-output suite gains the same pin. The regression is invisible in Chromium and in a test DOM, so the style assertions are the whole guard. Closes #8037
UX Review (Fable 5) — ✅ PASSUX-level review of The diff is minimal: it adds UX-Verdict: PASS Invisible-by-design fix: promotes two srcDoc iframes to their own compositing layer so widgets stop rendering as silent empty boxes — pure UX win, zero surface change. [UX-REVIEWED] 305a8bc |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS A measured silent-blank failure, fixed with the codebase's established remedy, scoped exactly to the two frames the tracking issue names. Suggestions
[DESIGN-REVIEWED] 305a8bc |
First Principles Review (Fable 5) — ✅ PASSPremise-level review of The verification is complete: the four #7931-promoted sites exist ( First-Principles-Verdict: PASS A tracked defect's filed remainder, fixed by the codebase's own established remedy, with every rider absent and every unfixed sibling counted and declared. What this change shipsIntent: make model-generated HTML in the Mochi widget frame and the meetings sketch panel actually paint instead of silently rendering an empty box. This is a FIX.
Verified mechanically: no existing shared helper does this job — all four #7931 sites ( [FIRST-PRINCIPLES-REVIEWED] 305a8bc |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
iamwhatever
left a comment
There was a problem hiding this comment.
Tier 1 auto-approve: fix (4 files). Criteria: no conflict, no requested changes, security path denylist clean, design-doc gate clean, SAST annotations clean, security checklist all-NO, AI reviewers green. Category: two srcDoc-built sandbox frames (meetings AgentPanel, mochi WidgetFrame) were left out of the #7931 compositing promotion because they never reach the sandbox-doc mint; adds transform: translateZ(0) plus style-contract tests that also pin the unchanged sandbox=allow-scripts and srcdoc-wrapper invariants.
chenmingwei23
left a comment
There was a problem hiding this comment.
Tier 1 auto-approve: fix (4 files). Criteria: no conflict, no requested changes, security path denylist clean, design-doc gate clean, SAST annotations clean, security checklist all-NO, AI reviewers green. Category: fix - adds a CSS compositing-layer promotion (transform translateZ(0)) to two inline srcDoc iframes so they paint; sandbox attribute untouched, no behaviour change beyond rendering.
Problem / Motivation
Two model-authored HTML frames build their document inline and hand it straight to
srcDoc, never reaching the gateway sandbox-doc mint that #7931 covered:website/src/apps/mochi/src/renderer/WidgetFrame.tsx(~:101)transformat allwebsite/src/apps/meetings/components/AgentPanel.tsx(~:251,buildSketchSrcdoc~:260)transformat allWithout layer promotion an engine can lay the document out, run its scripts, report a correct height, and never rasterize it: a correctly sized, visible frame painting an empty box, silent by construction — nothing fails, so there is no error state and no retry affordance. This is exactly the mechanism measured and fixed for the four
useSandboxDocconsumers in #7931 (c222244), whose commit message filed these inline frames as the tracked remainder: #8037.Why it matters
Both frames are model-output surfaces: Mochi's
<mcwidget>renderer and the meetings sketch-artist panel. When the failure fires, the user's generated content silently doesn't exist — the worst kind of display bug, because the app looks healthy while showing nothing.What changed (motivation → approach → change)
Port #7931's exact remedy onto the two inline-srcDoc frames:
WidgetFrame.tsx: addtransform: 'translateZ(0)'to the iframe's inline style (composed into the existing style object; nothing dropped).AgentPanel.tsx: same one-line promotion on the html-output iframe.Neither frame carried an existing transform, so the plain 3D form suffices (the compose-onto-
scale()care #7931 needed on its gallery thumbnail does not apply here). Per #7931's invariants, nothing about sandbox flags, CSP, or how the documents are built changes —buildSrcdoc/buildSketchSrcdocand bothsandboxattributes are byte-identical.Scope. This PR fixes the two frames #8037 names — the model-authored surfaces #7931's commit message filed as the remainder. The review pass for this PR enumerated the rest of the un-promoted inline-
srcDocpopulation, which is deliberately out of scope here and filed as #8075:McpAppFrame.tsx(~:1018, the primary residual —allow-scripts+ script-reported height), bothpptx-maker/BoardFrame.tsxframes (2Dscale()only, needs the compose treatment), andFileRenderers.tsxHtmlViewer(:267). Keeping this PR at the issue's two frames mirrors #7931's own scope discipline.Tests
website/src/apps/mochi/test/mochiWidgetFramePromotion.test.tsx— first test to mount this component:iframe.style.transform === 'translateZ(0)'(the regression is invisible in Chromium and in a test DOM, so the style assertion is the whole guard, as fix(dashboard): promote the widget and remote artifact frames to their own layer #7931's tests state);display: block,width: 100%, border style survive) and that the wrapper'soverflow: hiddenclip is still present (the frame's bottom corners come from the rounded card);sandboxis exactlyallow-scripts, and the model HTML reaches the frame only throughbuildSrcdoc's minted document, never raw.website/src/test/MeetingsAgentPanel.test.tsx— the html-output suite gains the same promotion pin plus the sizing-survives assertion.Local gates, all green:
npx tsc -b; full website vitest suite (1783 files) exit 0;npx eslint src/ --max-warnings 597→ 0 errors, warnings at baseline;npx jscpd .→ 0 clones; brand/feature-map/focus-cue root gates pass. Backend gates unaffected (frontend-only diff; CI skips backend jobs for this class).Manual verification
Rendered both components in the test DOM and inspected the produced inline styles; verified both
sandboxattributes and both srcdoc builders are byte-unchanged against base (git diffshows style-object-only edits). The defect itself has no deterministic local reproduction (engine-dependent rasterization skip), which is why #7931 established source-level pins as the guard; this PR follows that precedent.Screenshots / video
Why no screenshot:
translateZ(0)is a non-rendering compositing hint — on a correctly-rasterizing engine the before/after pixels are identical, so a screenshot pair would show two identical images (same waiver #7931 used for the same change class).Related Issues
Closes #8037
Refs #7931
Refs #8075 (follow-up: remaining un-promoted srcDoc frames, enumerated during this PR's review)
Pattern harvest
The failure class is "sandboxed frame laid out but never rasterized"; the durable pattern is that every
srcDoc/sandbox-doc frame needstranslateZ(0)composed onto its transform, there is no enumerating test for the class, and each new frame must add its own pin. #8075 now carries the enumeration record for the remaining sites, so the next sweep starts from a list instead of a manual grep.Rule candidate: every sandboxed
srcDoc/sandbox-doc iframe must carrytranslateZ(0)composed onto itstransform(a 2D transform alone makes a stacking context but does not promote), plus a source-level style assertion in its own test — the regression is invisible in Chromium and in a test DOM, so the pin is the whole guard.Checklist
npx tsc -b, eslint ratchet, jscpd, full website suite green locallyContribution License Agreement
By submitting this pull request, I confirm that my contribution is made under the terms of the project license.