From dba11c38634bbf91d2a80be62a29dc83782ccae1 Mon Sep 17 00:00:00 2001 From: Brian Warsing Date: Tue, 18 Aug 2026 14:14:21 -0700 Subject: [PATCH] Apply Clusterize spacer heights via CSSOM so CSP cannot drop them Fixes the task-log flickering reported in Shopify/continuous-deployment#2208 (#gsd:48220): output streams fine, then flickers, then stops updating. `renderExtraTag` sets `tag.style.height` on a DETACHED node and returns `tag.outerHTML`. `insertToDOM` joins those strings and `html()` assigns them with `innerHTML`, so the height reaches the document as a style ATTRIBUTE parsed from markup. Under a `style-src` policy without 'unsafe-inline' the browser refuses to apply such an attribute. Shipit serves exactly that: $ curl -sI https://shipit.shopify.io/ content-security-policy: ... style-src 'self' https: The spacers therefore render at height 0. Total scroll height collapses to just the rendered cluster no matter how many rows exist, scrollTop is clamped into that range, getClusterNum() derives a different cluster from the clamped value, insertToDOM swaps in different rows, and the height changes again. Measured on Shipit's task page with the real assets under the verbatim production CSP, 10 seconds at 1 line/sec: without CSP with CSP with CSP + fix html() swaps 10 601 14 re-entrant swaps 0 145 0 repaints/sec ~8 83 ~8 scrollHeight at 800 rows 19316 4844 19316 The first visible line number goes from `236 237 238 239 ...` to `173 173 239 173 242 173 ...` - the viewport alternating between two regions of the log about eighty times a second. Below `rows_in_block` no spacers are emitted, which is why the page starts out fine and only degrades once the log grows. Note that the `only_bottom_offset_changed` fast path already uses `lastChild.style.height` on a LIVE node, which CSP permits; that asymmetry is why the failure presents as oscillation rather than a static break. CSSOM writes are not blocked by CSP, only markup-parsed style attributes and inline