cmux-next browser: a web .mp4 sent as a generic file plays in the tab instead of downloading
Ready: an independent Codex review says SHIP at the head, and the fleet tests pass at the head and fail at the test-only commit (see the receipt comment).
Leo-ask: ask-76f83a7020
"this watch is kind aass also id like to be able to watch .mp4s direclty in cmux next somehow (chromium?)"
What
This PR handles the ".mp4 in cmux" half of the ask. The link to the What's New reel (Watch what changed in nightly <sha> โ) points at raw.githubusercontent.com/.../reel.mp4. GitHub sends every .mp4 there as application/octet-stream with nosniff, so a cmux browser tab downloaded the file instead of playing it. Any other web .mp4 served the same way did too.
Now a WebKit tab plays such a video at the video's own address, in a plain <video controls autoplay> page (black background, fitted to the tab), when a main-frame response meets all of these:
- WebKit cannot show its type;
- it is not
Content-Disposition: attachment; - its http(s) path ends in mp4, m4v, mov, webm or ogv;
- the navigation was a GET and not Back or Forward.
The page loads with loadSimulatedRequest, so it is a normal back-list entry: Back returns to the page the video was opened from.
These cases still download, as before:
- attachments;
- every other file type;
- a POST, because the player could not fetch the video again;
- a Back or Forward to an entry WebKit had evicted, because that must not add a new back-list entry.
The link's look ("this watch is kind aass") is the What's New page, which the whats-new lane owns. I've sent them that half of the ask, with the screenshot.
Known limitation
A page that calls location.replace() on such a video gets a new back-list entry instead of replacing its own, because WebKit doesn't tell the app that a navigation is a replace. This is rare, and the player still plays.
Chromium tabs don't take this path. CEF downloads octet-stream responses in the shim, and changing that means a shim rebuild. This PR keeps to the WebKit change.
Tests
Tests/CmuxNextBrowserTests/RemoteVideoPageTests.swift runs a real WebKit tab against a local server. The server sends a generated H.264 .mp4 the way GitHub does: application/octet-stream, nosniff, with byte ranges. The test:
- goes from page a to page b, then follows b's link to the video;
- waits for the video's
currentTimeto pass 0; - checks there was no download and that the back list is exactly
a, b, reel.mp4after Back and Forward; - checks that a form POST for the same video still downloads.
Observed / Not run
- Red first: on the fleet, the test-only commit cf8e824 fails. The tab downloads the video, so it never plays.
- Green: on the fleet,
RemoteVideoPageTestspasses at the head (5ff010c). - The prepush gate passed with no new violations.
- Not run: a click on the real What's New link in a built app.
Changelog
- Fixed: a web .mp4 that its server sends as a generic file, such as the nightly What's New reel, plays in a cmux browser tab instead of downloading.
No capture: the player is the system <video> control on black. The test proves playback.
Agent session: cosmic-gray
๐ค Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Plays web videos whose servers send them as generic files in a cmux WebKit tab instead of downloading them โ the nightly What's New reel among them, since raw.githubusercontent.com serves every .mp4 as application/octet-stream with nosniff. The video now opens in a plain player page (system controls, black background) at its own address, and takes a normal spot in the back list: Back returns to the page the video was opened from.
Only a main-frame GET for an http(s) URL ending in mp4, m4v, mov, webm, or ogv becomes the player. The tab records the video URL when its newest own main-frame navigation asks for it, and only a response for that URL plays โ a target=_blank click, an older held GET, or an in-flight reload cannot turn a POST's response into it. Attachments, other file types, POSTs, and Back or Forward navigations still download as before, as do Chromium tabs.
Known limitation
- A page calling
location.replace()on such a video adds a new back-list entry instead of replacing its own; the player still plays.
Tests
RemoteVideoPageTestsplays a generated H.264 .mp4 served like GitHub does (octet-stream, nosniff, byte ranges) and checks playback, the back-list order, that a form POST still downloads, and that only the matching response becomes the player.