Packaged builds load the renderer over `file:`, whose origin is opaque
("null"). YouTube rejects a null-origin embed, so every YouTube embed was
dead in a packaged app while working in dev (where the renderer is served
over http from the Vite dev server).
Serve the packaged renderer from a loopback HTTP origin instead, so embeds
get a real origin in packaged builds too. `rendererBaseUrl()` becomes the
single place that resolves the renderer location -- dev server, packaged
HTTP origin, or the file:// fallback -- and the main window, session
windows and the pet overlay all route through it.
Addresses both problems raised in review of #63492:
- Navigation guard: `will-navigate` previously admitted any `file:` URL when
packaged, and the earlier revision of this PR compared URL strings against
the base. Both are unsound. A string compare accepts
`http://127.0.0.1:47891@attacker.example/` -- everything before the `@` is
userinfo, so it shares our prefix while its real origin is
attacker.example -- which would load attacker content into a window
carrying the `hermesDesktop` preload bridge. Navigation is now checked by
PARSED ORIGIN in a pure, unit-tested helper (`renderer-url.ts`), with a
regression that asserts the old prefix check would have let the hostile URL
through. `file:` bases compare by resolved path, since their origins are
all "null".
- Port collision: the server bound a fixed 47891 and rejected on any bind
error, while `whenReady` awaits it before `createWindow()` -- so an
occupied port meant startup never reached window creation. It now falls
back to an OS-assigned ephemeral port, covered by a test that binds a
squatter on the requested port first. The fixed port is still preferred
because the renderer's origin keys its localStorage, so a port that
changed per launch would orphan persisted renderer state.
Session windows keep upstream's `rendererIndexPath` path for the file://
fallback: passing a file:// URL as `devServer` would have produced
`index.html/?win=secondary`.
Scoped to the embed fix only. The OAuth/boot-recovery changes that were
mixed into this branch are split out into a separate change.