SCAL-337722 Add preRenderConfig.inFlow to mount the pre-render in-flow - #664
sastaachar wants to merge 1 commit into
Conversation
showPreRender() parks the wrapper outside the host's layout flow and keeps it aligned in JS, so position, size, stacking, clipping and scrolling are emulated rather than owned by the browser. With inFlow set, Element.moveBefore() reparents the frame into the host element without reloading it, and the placeholder, syncPreRenderStyle, the ResizeObserver and the container scroll listener are all skipped. hidePreRender() moves it back to the parking container. Feature-detected and guarded: where moveBefore is missing (Safari) or the move is rejected, it falls back to the existing overlay path.
There was a problem hiding this comment.
Code Review
This pull request introduces an inFlow option for pre-rendering, allowing the pre-rendered frame to be reparented into the host element using Element.moveBefore() for native browser layout and scrolling. Feedback includes removing a redundant nullish coalescing operator in hidePreRender and correcting a spelling typo ('behaviour' to 'behavior') in the JSDoc to adhere to the en-US style guide.
| return; | ||
| } | ||
| if (this.isInFlow) { | ||
| this.movePreRenderWrapperTo(this.preRenderContainerEl ?? document.body); |
There was a problem hiding this comment.
The preRenderContainerEl property is initialized with document.body and is always an HTMLElement, so it will never be null or undefined. The nullish coalescing operator ?? document.body is redundant and can be removed for simplicity.
| this.movePreRenderWrapperTo(this.preRenderContainerEl ?? document.body); | |
| this.movePreRenderWrapperTo(this.preRenderContainerEl); |
| * | ||
| * Requires `Element.moveBefore()`, which preserves the frame's state across the | ||
| * move. Where it is unavailable (notably Safari) or the move is rejected, this | ||
| * silently falls back to the overlay behaviour, so it is always safe to set. |
There was a problem hiding this comment.
Per the repository style guide (Rule 9), please use American English (en-US) spelling. The word behaviour should be behavior.
| * silently falls back to the overlay behaviour, so it is always safe to set. | |
| * silently falls back to the overlay behavior, so it is always safe to set. |
References
- The style guide (Rule 9) mandates the use of American English (en-US) format for all written work, including code comments. This includes using 'behavior' instead of 'behaviour'. (link)
commit: |
What
Adds
preRenderConfig.inFlow. When set,showPreRender()moves the pre-rendered frame into the host element withElement.moveBefore()instead of overlaying it, so the browser lays it out like a normal embed.SCAL-337722
Why
Today the wrapper is parked on
document.body, absolutely positioned, and aligned to an in-flow placeholder bysyncPreRenderStyle()+ aResizeObserver+ a container scroll listener. Because it sits outside the host's layout flow, the SDK emulates what the browser does for free — position, size, stacking, clipping, scrolling.That emulation is the source of a recurring class of defects: SCAL-235696, SCAL-270176 (P0), SCAL-287671, SCAL-307017, SCAL-325118, SCAL-331221, and SCAL-265112 which is still open — six sizing/stacking bugs in 21 months, each from a different host layout, none predicted by the last.
moveBefore()is a state-preserving atomic move: the frame keeps its browsing context, so it can be reparented without reloading. That removes the reason the wrapper had to live out of flow.How
showPreRender()— move the wrapper intohostElement, clearposition/top/left, size it fromframeParams. The placeholder,syncPreRenderStyle(), theResizeObserverand the scroll listener are all skipped.hidePreRender()— move it back to the parking container before restoring the hidden styles.syncPreRenderStyle()is a no-op while in-flow, so a host calling it directly cannot fight the browser.Guarded end to end: off unless opted in, feature-detected on
Element.prototype.moveBefore, andtry/catcharound the move. Any of those failing takes the existing overlay path, so it is safe to set unconditionally.Not in scope
navigateToLiveboard.moveBeforepreserves state by design, so these are unaffected.fullHeight— the height negotiation over postMessage is untouched. This removes the wrapper sync, not the protocol.Browser support
Chrome 133+, Firefox 144+. Safari has not shipped
moveBefore, so WebKit keeps the overlay path and both placement models remain supported.Testing
tsc --noEmitclean. Fullts-embed.spec.tsgreen: 1257 passed, nothing existing changed.Five new cases under
preRenderConfig.inFlow:inFlow+moveBeforeavailableposition/top/leftclearedinFlow,moveBeforemissinginFlow,moveBeforethrowsinFlownot setWorth noting there is no e2e harness that can park and show a pre-render separately (see SCAL-336321's notes on the embed test bed), so real-browser verification across host layouts — ancestor
transform, clippingoverflow: hidden, inner scroll container, sticky header, highz-indexsibling, two co-located embeds — is still manual.