ITADN

[🐞] v2: Inline qwikloader is emitted inside an open `<svg>` (foreign content), so the script never executes and `q:container` stays `"paused"`

#8637Closed46ki75 创建于 2026-05-17
bugruntimeSSR
4
46ki75commented
### Which component is affected? Qwik Runtime ### Describe the bug In Qwik v2 dev SSR, `SSRContainer.openElement` inlines the `<script id="qwikloader">` tag as soon as the buffered response crosses 30 KB. The check does not consider the current element-frame stack, so when the threshold is crossed while the writer is inside an open `<svg>` (or `<math>`) subtree, the script is written into that subtree. The browser then parses it in HTML5 foreign-content mode, where XML element rules apply inside `<svg>`/`<math>`. The qwikloader source contains substrings like `for(let e=0;e<t.length;e++)`, and the tokenizer treats `<t.length…` as the opening of an XML element. The script element ends up in the SVG namespace and its body is truncated at the first such pseudo-tag. **Observable symptoms on the client:** - `<script id="qwikloader">.namespaceURI` is `"http://www.w3.org/2000/svg"` (not HTML) — so the browser never executes it as a script. - Its `textContent.length` is ~3788 chars; the original source is ~4900+ — the rest was eaten by the XML tokenizer. - `document.documentElement.getAttribute('q:container')` stays `"paused"` indefinitely. - No event handlers wire up. No `useVisibleTask$` ever fires (even with `{ strategy: "document-ready" }`). No data is fetched. The page appears interactive (it is SSR-rendered) but is fully inert. **Offending site:** `node_modules/@qwik.dev/core/dist/server.mjs` (Qwik 2.0.0-beta.35) at about **line 2042**: ```js if ( this.$noScriptHere$ === 0 && this.size > 30 * 1024 && elementName !== "body" ) { this.emitQwikLoaderInline(); } ``` There is no check that walks `this.currentElementFrame.parent` looking for an `svg` / `math` ancestor before emitting the inline script. **Suggested fix direction:** either short-circuit `emitQwikLoaderInline` when an SVG/MathML frame is on the stack (deferring the inline emit until the writer returns to HTML content), or constrain the inline emit to known-safe insertion points (e.g. only when the element about to be opened is itself in the HTML namespace). I'd be happy to send a PR if a maintainer can confirm the approach (walking the frame chain seems lowest-risk). ### Reproduction https://github.com/46ki75/qwik-v2-svg-qwikloader-bug ### Steps to reproduce ```bash pnpm install pnpm dev # open http://localhost:5173/ ``` The `/` page renders a heading that says: > WAITING — if this text is still here a few seconds after the page > loads, qwikloader DID NOT execute (BUG REPRODUCED). That heading is updated to **"RESUMED OK"** by a `useVisibleTask$(..., { strategy: "document-ready" })`. With the bug present, that task never fires and the heading stays on "WAITING". ### Direct evidence in DevTools After the page loads, in the console: ```js const s = document.getElementById("qwikloader"); ({ namespaceURI: s.namespaceURI, // → "http://www.w3.org/2000/svg" (BUG) parentTag: s.parentNode.nodeName, // → "svg" scriptTextLen: s.textContent.length, // → 3788 (real source is ~4900 chars) }); document.documentElement.getAttribute("q:container"); // → "paused" ``` ### Byte-position scan of the SSR response ```shell total bytes : 321735 <svg> opens at : [2094, 313291] ← second is Qwik's preloader icon </svg> closes at : [287612, 313794] qwikloader open at : 30994 ← inside the first <svg>! depth = 1 ``` ### System Info ```shell System: OS: Linux 6.6 Ubuntu 24.04.4 LTS (Noble Numbat) (WSL2) CPU: (16) x64 Intel(R) Core(TM) Ultra 7 255H Memory: 3.76 GB / 15.31 GB Shell: 5.2.21 - /bin/bash Binaries: Node: 24.15.0 npm: 11.12.1 pnpm: 10.33.0 Browsers: Chrome: 148.0.7778.167 npmPackages: @qwik.dev/core: 2.0.0-beta.35 => 2.0.0-beta.35 @qwik.dev/router: 2.0.0-beta.35 => 2.0.0-beta.35 typescript: 5.8 => 5.8.3 undici: * => 8.3.0 vite: 7.3.2 => 7.3.2 ``` ### Additional Information This is not a contrived case — it bit a real project where a text input component had a trailing trash-can SVG icon. Whenever the surrounding page content was large enough that the 30 KB threshold landed mid-icon, the entire page silently stopped resuming: no console error, no network error, just `q:container="paused"` forever and every `useVisibleTask$` / `routeLoader$`-driven fetch never running. It also reproduces for `<math>` (any HTML5 foreign-content subtree), not just `<svg>`.
关闭于 2026-05-19 1 条评论