fix(inspect): reset a released preview video in the next task

On WebKit (GStreamer) a video replaced while its pipeline was still
starting blocked the page: pause, src removal and load() in one task
never returned, so the next chooser selection hung (CI page workflows
webkit shard 2/2, "URLs and media end with their owning session").
The src is still dropped before the URL is revoked; only the load()
reset moves to the next task. A fresh-page regression replaces the
first video as soon as it shows, fifteen times.
This commit is contained in:
Vito Sansevero
2026-10-22 12:40:18 -07:00
parent 27ea042018
commit b7db4c35f6
3 changed files with 32 additions and 4 deletions
+4 -2
View File
@@ -1296,8 +1296,10 @@ mapped differences"):
`image/png,image/jpeg,image/webp` plus `video/*,.mp4,.mov,.m4v,.webm,.mkv,.avi,.wmv`. A `image/png,image/jpeg,image/webp` plus `video/*,.mp4,.mov,.m4v,.webm,.mkv,.avi,.wmv`. A
video previews in the same `#local-preview` slot as `video.local-file-preview` with video previews in the same `#local-preview` slot as `video.local-file-preview` with
native `controls`, `playsInline`, `preload="metadata"` and `muted`, never autoplays native `controls`, `playsInline`, `preload="metadata"` and `muted`, never autoplays
inline, and is paused, unloaded (`removeAttribute('src')`, `load()`) and removed on inline, and is paused, has its `src` removed and is removed from the page on replacement
replacement or dispose. or dispose, before its URL is revoked; its `load()` reset runs in the next task, because
WebKit (GStreamer) blocked the page when a still-starting video was reset in the same
task (`replacing a video that just started loading never blocks the page`).
- **D63** (status line): one `p.small.spaced-16` in `#local-preview`, for states the - **D63** (status line): one `p.small.spaced-16` in `#local-preview`, for states the
concept never shows: "Drop one file at a time.", "Folders cannot be inspected. Choose concept never shows: "Drop one file at a time.", "Folders cannot be inspected. Choose
one image or video file.", "This file type cannot be previewed or inspected here. one image or video file.", "This file type cannot be previewed or inspected here.
+22
View File
@@ -391,6 +391,28 @@ test('URLs and media end with their owning session', async ({ page }) => {
expect(state.revoked.filter(url => url === state.created[7])).toHaveLength(1); expect(state.revoked.filter(url => url === state.created[7])).toHaveLength(1);
}); });
test('replacing a video that just started loading never blocks the page', async ({ context, browserName }) => {
test.setTimeout(120000);
// WebKit (GStreamer) blocked the page when a video's pipeline was reset with load() in the same task that
// removed its src, while it was still starting: the next chooser selection never completed. Each fresh page
// replaces its first video as soon as it shows.
for (let attempt = 0; attempt < 15; attempt++) {
const page = await context.newPage();
try {
if (attempt === 0) await startCoverage(page, browserName);
await openInspect(page);
await choose(page, localFixture('core-tags.webm', 'video/webm'));
await expect(preview(page).locator('video.local-file-preview')).toBeVisible();
await chooser(page).setInputFiles(parityPng('f02.png'), { timeout: 8000 });
await expect(preview(page).locator('img.local-file-preview')).toBeVisible();
await expect(preview(page).locator('video')).toHaveCount(0);
if (attempt === 0) await saveCoverage(page, browserName);
} finally {
await page.close();
}
}
});
test('replacing a file mid-read never shows or sends the old record', async ({ page, context }) => { test('replacing a file mid-read never shows or sends the old record', async ({ page, context }) => {
const host = await bridgeHost(context); const host = await bridgeHost(context);
try { try {
+6 -2
View File
@@ -37,10 +37,14 @@ function element(tag, className, text) {
return node; return node;
} }
/** Stops a preview from holding its blob: URL: media is paused and unloaded before the session revokes the URL. */ /**
* Stops a preview from holding its blob: URL: media is paused and its src dropped before the session revokes the URL.
* The media resets with load() in the next task: WebKit (GStreamer) blocked the page when a starting video was reset in
* the same task that removed its src.
*/
function releaseMedia(node) { function releaseMedia(node) {
if (!node) return; if (!node) return;
if (node instanceof HTMLMediaElement) { node.pause(); node.removeAttribute('src'); node.load(); } if (node instanceof HTMLMediaElement) { node.pause(); node.removeAttribute('src'); setTimeout(() => node.load(), 0); }
else node.removeAttribute('src'); else node.removeAttribute('src');
node.remove(); node.remove();
} }