aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/downloads.js111
1 files changed, 42 insertions, 69 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/downloads.js b/packages/meshbay-hub/src/meshbay_hub/static/downloads.js
index 6ee7cf0..ce1901c 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/downloads.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/downloads.js
@@ -380,8 +380,8 @@ async function _claimController(budgetMs) {
*/
export function primeServiceWorker() {
if (!STREAMS_VIA_SW) return;
- _priming = serviceWorker()
- .then((worker) => worker && _repairIfBypassed(worker))
+ _priming = _repairIfBypassed()
+ .then(() => serviceWorker())
.catch(() => {});
}
@@ -391,85 +391,58 @@ export function primeServiceWorker() {
// path that is about to be repaired.
let _priming = null;
-// How long to wait for the worker to answer the one-byte self-test below.
-// Milliseconds when it works; a page that cannot stream at all is worth four
-// seconds to find out about, once, at boot.
-const SELF_TEST_BUDGET_MS = 4000;
-// Set for the life of this tab, so the repair below can happen at most once and
+// Set for the life of this tab, so the repair below happens at most once and
// can never become a reload loop.
const REPAIRED_KEY = 'meshbay.sw-repaired';
/**
- * Can this document actually have a download served, or only talk to the worker?
+ * Was this document loaded with the service worker bypassed?
*
- * Being controlled is not the same thing, and the gap between them is a real
- * failure people hit. A document fetched by a **hard** reload — Ctrl+F5,
- * Ctrl+Shift+R — is loaded with the service worker bypassed. It can still be
- * claimed afterwards, so `navigator.serviceWorker.controller` comes back and
- * every check in `_claimController` passes; but the navigations that document
- * starts keep missing the worker, and the hidden iframe a streamed download
- * needs *is* a navigation. Every download then fails with "the worker did not
- * answer", for the life of that page — on Firefox and Safari, the only path
- * there is for a file too large to hold in memory.
+ * A document fetched by a **hard** reload — Ctrl+F5, Ctrl+Shift+R — is loaded
+ * with the worker bypassed. It can still be claimed afterwards, so
+ * `navigator.serviceWorker.controller` comes back and every control check
+ * passes; but the navigations it starts keep missing the worker, and the hidden
+ * iframe a streamed download needs *is* a navigation. On Firefox and Safari
+ * that is the only way to write a file too large to hold in memory, so every
+ * download fails for the life of that page.
*
- * Reported after an operator was told, by this author, to hard-reload after
- * each deployment: four downloads out of four worked on a freshly started
- * browser, and the first attempt after a Ctrl+F5 failed, every time.
+ * Measured on Chrome, at document start, before anything registers:
*
- * This asks the question directly rather than inferring it: a four-byte stream
- * and a hidden iframe, exactly as a real download would.
- */
-async function _canServeDownloads(worker) {
- const id = `selftest-${Math.random().toString(36).slice(2, 10)}`;
- let readable, writable;
- try {
- ({ readable, writable } = new TransformStream());
- } catch {
- return true; // No transferable streams: a different failure, not this one.
- }
- const chan = new MessageChannel();
- const serving = new Promise((resolve) => {
- chan.port1.onmessage = (e) => {
- if (e.data && e.data.type === 'mbdl-serving') resolve(true);
- };
- });
- try {
- worker.postMessage({ type: 'mbdl', id, filename: 'meshbay-selftest.bin',
- size: 4, readable, port: chan.port2 },
- [readable, chan.port2]);
- } catch {
- return true; // Same: not the bypass this is looking for.
- }
- const frame = document.createElement('iframe');
- frame.hidden = true;
- frame.src = `${PREFIX_PATH}${id}`;
- document.body.appendChild(frame);
- const served = await Promise.race([
- serving,
- new Promise((r) => setTimeout(() => r(false), SELF_TEST_BUDGET_MS)),
- ]);
- frame.remove();
- try { chan.port1.close(); } catch { /* already gone */ }
- // Never completed, so the browser has nothing to save and no file appears.
- try { await writable.abort('self-test'); } catch { /* already gone */ }
- return served;
-}
-
-/**
- * An ordinary reload puts the document back under the worker, so do that once.
+ * first visit controller false, registration false
+ * ordinary reload controller true, registration true
+ * hard reload controller false, registration true
+ *
+ * So being uncontrolled while an active registration already exists names the
+ * case exactly, and costs nothing to ask.
*
- * Only at boot, where nothing is in flight and the reload costs a flicker.
- * Guarded by a session flag rather than a variable: the point is to survive the
- * reload it triggers, and to stop rather than loop if reloading does not help.
+ * The first version of this asked by *performing a download* — a four-byte
+ * stream through a hidden iframe — which was both unreliable and expensive:
+ * Chrome rations downloads a page starts without a user gesture to about three,
+ * so the test competed with the person's real downloads for that budget and its
+ * answer depended on how many had been spent. It reloaded a healthy page on
+ * every first visit, taking the group's WebRTC session down with it.
*/
-async function _repairIfBypassed(worker) {
+const _controlledAtLoad = STREAMS_VIA_SW
+ && Boolean(navigator.serviceWorker.controller);
+// Started here, at module load, because `register()` would make the answer
+// true whatever it was.
+const _registeredAtLoad = STREAMS_VIA_SW
+ ? navigator.serviceWorker.getRegistration('/')
+ .then((reg) => Boolean(reg && reg.active)).catch(() => false)
+ : Promise.resolve(false);
+
+/** An ordinary reload puts the document back under the worker, so do that once. */
+async function _repairIfBypassed() {
+ if (_controlledAtLoad) return;
+ // Uncontrolled with nothing registered is a first visit, not a bypass: the
+ // worker is being installed right now and the page is fine after it claims.
+ if (!await _registeredAtLoad) return;
let repaired = false;
try { repaired = sessionStorage.getItem(REPAIRED_KEY) === '1'; } catch { /* blocked */ }
if (repaired) return;
- if (await _canServeDownloads(worker)) return;
- console.warn('[MeshBay] this page cannot be served by the download worker — '
- + 'reloading once to put it back under the worker\u2019s control '
- + '(a hard reload leaves a page in this state)');
+ console.warn('[MeshBay] this page was loaded with the download worker '
+ + 'bypassed (a hard reload does that) — reloading once to put it '
+ + 'back under the worker\u2019s control');
try { sessionStorage.setItem(REPAIRED_KEY, '1'); } catch { /* blocked */ }
location.reload();
}