fix(content): YouTube preview 153 — give the iframe a referrer (page is no-referrer)

ROOT CAUSE (hard evidence this time, from the response headers): the app sends
Referrer-Policy: no-referrer globally (helmet default). A raw YouTube iframe then reaches
youtube.com with NO Referer, so YouTube can't identify the embedding site and shows "Video
player configuration error" (153). Confirmed by the three facts: the same /embed URL plays in
a top-level tab (no embed check), plays in the device player (YT.Player loads iframe_api and
validates via an ORIGIN postMessage handshake, which doesn't need Referer), and fails only as
a raw iframe on a no-referrer page. The player's page is ALSO no-referrer, proving it's the
embed method that saves it, not the headers.

Fix: add referrerpolicy="strict-origin-when-cross-origin" to the preview iframe — overrides
the page's no-referrer for just this element so YouTube receives our origin and validates the
embed. Scoped (only the YouTube embed sends a referrer; only the origin, not the path), no JS
API machinery needed for a passive preview, page-level no-referrer untouched.

Supersedes the earlier enablejsapi/origin strip, which was inert (those params do nothing in
a raw iframe with no IFrame API). Frontend-only; suite 149 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
ScreenTinker 2026-06-14 20:12:57 -05:00
parent 7f7dc80a8c
commit 46e4bc8579

View file

@ -635,7 +635,7 @@ function showPreview(content) {
<button style="position:absolute;top:8px;right:8px;z-index:1;background:rgba(0,0,0,0.7);border:none;color:white;width:32px;height:32px;border-radius:50%;font-size:18px;cursor:pointer" id="closePreview">&times;</button>
<div style="max-width:80vw;max-height:80vh">
${isYoutube
? `<iframe src="${(() => { try { const u = new URL(src); u.searchParams.set('mute', '1'); /* #YT153: this is a PASSIVE preview - it never drives playback via JS, so it must NOT declare the JS IFrame API. enablejsapi=1 (baked into the stored URL) + origin tells YouTube to expect a postMessage handshake from a parent API that this page never loads -> "Video player configuration error" (153). The player works because it loads iframe_api + uses YT.Player; the preview just needs a plain /embed/ID. */ u.searchParams.delete('enablejsapi'); u.searchParams.delete('origin'); return u.toString(); } catch { return src; } })()}" style="width:80vw;height:45vw;max-height:80vh;display:block;border:none" allow="autoplay;encrypted-media" allowfullscreen></iframe>`
? `<iframe referrerpolicy="strict-origin-when-cross-origin" src="${(() => { /* #YT153 ROOT CAUSE: the dashboard sends Referrer-Policy: no-referrer (helmet default), so a raw YouTube iframe reaches youtube.com with NO Referer -> YouTube can't identify the embedding site -> "Video player configuration error" (153). referrerpolicy on THIS iframe overrides the page policy to send just our origin, which YouTube uses to validate the embed. (The device player dodges no-referrer differently: YT.Player's iframe_api origin postMessage handshake, which doesn't rely on Referer.) The enablejsapi/origin URL params are inert in a raw iframe (no API loaded), so they're dropped. */ try { const u = new URL(src); u.searchParams.set('mute', '1'); u.searchParams.delete('enablejsapi'); u.searchParams.delete('origin'); return u.toString(); } catch { return src; } })()}" style="width:80vw;height:45vw;max-height:80vh;display:block;border:none" allow="autoplay;encrypted-media" allowfullscreen></iframe>`
: isVideo
? `<video src="${esc(src)}" controls autoplay style="max-width:80vw;max-height:80vh;display:block"></video>`
: `<img src="${esc(src)}" style="max-width:80vw;max-height:80vh;display:block">`