Skip to content

A reload of the page keeps the buffer for 20 s, in RAM and on the disk - #605

Merged
Nawid3333 merged 1 commit into
mainfrom
feat/reload-keeps-buffer
Oct 28, 2026
Merged

Nawid3333 merged 1 commit into
mainfrom
feat/reload-keeps-buffer

Conversation

@Nawid3333

Copy link
Copy Markdown
Owner

Your wish from the upstream triage (#488/#487, reshaped): "if a buffer is loaded and the page reloads, the buffered stuff stays for like 20 seconds, and then gets flushed out of memory. And if the disk is used, the same applies."

What happens now

  1. The player goes away (reload, or the page is left). At its beforeunload it hands its finished video files to the background:
    • RAM pieces as their Blobs. These are shared, not copied: 256 MB were handed over in 1 ms.
    • Disk pieces as their OPFS file names.
  2. The background keeps the hand-over for 20 s, keyed by the page, the video, private-or-not and the container.
  3. The next player of that video on that page takes it back before it downloads anything:
    • the RAM pieces into its own RAM budget, at once;
    • the disk files moved into its own session, a few at a time while it starts, so a long video doesn't delay playback.
  4. Whatever nobody takes within 20 s is flushed: the RAM is let go, and the files are deleted.

It also works when the site signs every page load anew (vidmoly's ?t=&s=). The query parameters that differ between the old and the new link are the signature, and a piece matches with those left out. Parameters that didn't change (?n=5) still tell pieces apart. A page check keeps two videos on the same address (hls.php?v=1 / ?v=2) from ever taking each other's pieces.

FreshLink gets the same matching (DownloadManager.relink). A fresh link after a refusal no longer downloads everything again.

Measured in Firefox 159 (new ext-specs/reload-keeps-buffer.e2e.mjs)

Case Result
RAM, reload 42 of 42 taken back, 0 asked for again, plays on
Disk (RAM budget 0, files really on disk), reload 100 of 100 taken back, 0 asked for again, plays from them, old session deleted
Left the page, back after 22 s still kept at 5 s; at 22 s everything downloaded again, session deleted
FreshLink, link runs out mid-video (fresh-link.e2e) 19 downloaded under the old link, 0 asked for again

Found while building, measured each time

  • runtime.getBackgroundPage() doesn't exist in a player iframe inside a web page. So the hand-over goes through messaging. Messages and ports still go out at beforeunload and pagehide while that page reloads.
  • The page is already empty when the hand-over arrives. By then the tab's frame records are the next page's: an empty address, and a new frame object for the player. So the background tells the player its page in the BUFFER_TAKE answer, and the player names it in its hand-over.
  • The answer's Blobs come from another realm: not instanceof Blob in the player, so none was taken. The answer is now structuredCloned into the player's realm, which shares the data and doesn't copy it.
  • FileSystemFileHandle.move() works on the main thread for OPFS: 4 ms for 1 MB.
  • The background sees the iframe's OPFS (it isn't partitioned away), so it can delete an unclaimed session.

Tests

  • Unit:
    • LinkSignature (7)
    • BufferStash (5)
    • DownloadManagerHandOff (11), including a second reload in a row, twins under two signatures, and a disk file the player asked for before its move
    • backgroundBufferStash (4): keys, the page frame reset before the hand-over, a non-player sender
  • Unit updates: the FreshLink client test now expects keepStorageOnce + relink; the stand-in player in the "stayed" tests got handOffBuffer.
  • e2e: the reload spec (3 cases), and a FreshLink assertion added to fresh-link.e2e.
  • Green locally after the rebase on Every press in the player ends however it ends: hold-to-speed, long press and seven drags (PressTracker) #604: lint, tsc, all unit tests, ext specs reload-keeps-buffer, fresh-link and page-qualities, web specs press-end, ram-budget and storage. Earlier in the branch also embed-page-query, same-video-file, source-length, framed-player, audio-output, redirected-source, playback, save-fmp4, seek-preview and player-controls.
  • glm: a design check (8 real points, 1 false: "move() doesn't exist") and two reviews:
    • Tests review: 5 real findings, all fixed. The disk test now proves the files are on disk, the take-back is asserted, the 20 s window is checked at 5 s and at 22 s, FreshLink got its e2e check, and a second reload in a row has a unit test.
    • Code review: 1 real finding (twins), fixed, and 1 false. Its other 4 findings came from my diff against a stale base.

Docs

README ("RAM and disk") gets one paragraph; docs/notes/storage.md gets a dated section with every measurement above.

🤖 Generated with Claude Code

…he disk

His wish (2026-10-28): reloading a page must not throw away what was buffered; what
nobody takes back is let go of 20 s later, in RAM and on the disk alike.

- At the player's beforeunload, before destroy(), the client hands its finished video
  files to the background (BUFFER_STASH): RAM ones as their Blobs (shared, not copied:
  256 MB sent in 1 ms, measured), disk ones as their OPFS file names and the session
  directory. No playlist or manifest (old signatures, a live one moves on), no key, nothing
  in the Cache API or IndexedDB.
- chrome/background/BufferStash.mjs keeps a hand-off BufferKeepMs (20 s) under its page,
  source, private-or-not and container, then lets its Blobs go and deletes its directory.
  The page is the one BUFFER_TAKE told the player: by the hand-off the tab's frames are the
  next page's already (measured: an empty address, a new frame object).
- The next player of that video on that page takes it back in setSourceInternal, after the
  reset, before the player is built: the answer structuredClone()d into its realm (its Blobs
  were no instanceof Blob there, measured), RAM Blobs into its FSBlob (its budget), disk files
  moved into its own session a few at a time while it starts (FileSystemFileHandle.move,
  4 ms for 1 MB), then the old directory deleted.
- A new signed link (vidmoly's ?t=&s= at every load): LinkSignature names the parameters
  the two links differ in, and DownloadManager.stored finds a file under the new link with
  those left out (an alias: the data stays under its store id, entry.blobId). FreshLink uses
  the same (relink): a fresh link no longer downloads everything again.

Measured in Firefox 159 (ext-specs/reload-keeps-buffer.e2e.mjs): RAM 42 of 42 taken back,
0 asked for again; disk (RAM budget 0, files on the disk) 100 of 100 taken back, 0 asked
for again, played from them, the old directory deleted; not taken within 20 s: kept at 5 s,
everything downloaded again and the directory gone at 22 s. fresh-link.e2e: 19 downloaded
under the old link, 0 asked for again under the fresh one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Nawid3333
Nawid3333 merged commit c01161b into main Oct 28, 2026
23 checks passed
@Nawid3333
Nawid3333 deleted the feat/reload-keeps-buffer branch October 28, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant