Skip to content

OPFS never stores a playback fragment: every DownloadManager identifier contains "/" and is an invalid OPFS file name #132

Closed

Description

@Nawid3333

Severity: high

Where: chrome/player/network/DownloadManager.mjs (getIdentifier, archiveEntryData), chrome/player/network/opfs-worker.mjs (setFile, getFile, deleteFile), chrome/player/network/OPFSManager.mjs (getSavedFile), chrome/player/modules/FSBlob.mjs (saveBlobInOPFSAsync)

What happens

DownloadManager.getIdentifier builds the key of every downloaded fragment, playlist and manifest as

return url + '::' + rangeStart + '-' + rangeEnd + '::' + responseType;

and archiveEntryData passes that string unchanged as the OPFS file name:

const identifier = this.getIdentifier(entry);
await this.blobStore.saveBlobAsync(entry.data, identifier);

The worker then calls sessionDir.getFileHandle(identifier, {create: true}). The File System standard forbids / in a file name, and Firefox's IsValidName (dom/fs/shared/FileSystemHelpers.cpp) rejects any name containing / with a TypeError: Invalid filename. Every identifier of a real fragment is https://host/path/seg.ts::undefined-undefined::arraybuffer, so every write fails. saveBlobInOPFSAsync catches it, logs OPFS write failed for this blob, keeping it in memory once per fragment, and keeps the Blob in RAM.

So the backend FSBlob prefers on Firefox has never held a single playback fragment. "Offloaded fragments leave RAM" only ever applied to the blob0-style names the save path uses. With "download entire video" (shouldDownloadAll()) a multi-GB video is buffered entirely in process memory, exactly what OPFS was added to avoid. deleteBlob -> deleteFile -> removeEntry(identifier) fails the same way (silently caught).

Why the tests did not see it

tests/e2e/specs/storage.e2e.mjs calls saveBlobAsync(new Blob([payload])) with no identifier, so it round-trips blob0, and no spec asserts that a real fragment's entry.data() is a File once offloaded.

Suggested fix

  • Map identifiers to OPFS-safe names inside OPFSManager (a Map<identifier, 'f' + counter> on the main thread, or a hex SHA-1/SHA-256 of the identifier) and use the mapped name in setFile, getFile/getSavedFile and deleteFile.
  • Add to storage.e2e.mjs a round trip with an identifier of the real shape ('https://a/b.ts::0-100::arraybuffer'), and a playback spec asserting downloadManager.getEntry(ctx).data() returns a File once offloaded.
  • Report "OPFS write failed" once per session rather than per fragment.

Confidence: high (the name rule is in the standard and in Gecko's source; the identifier shape is in the code).


From a line-by-line code review of the tree at 41363fd (2026-10-02). Each claim above was checked against the source before filing; the reproduction steps and line references are as of that commit.


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    code-reviewFound by the 2026-10-02 line-by-line review
    on Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcode-reviewFound by the 2026-10-02 line-by-line review

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions