// RangerHQ Tuner — Service Worker. // Job: open the offscreen audio document on demand and route messages // between the popup/newtab (UI) and the offscreen document (audio engine). // Also acts as the storage gateway for the offscreen doc — offscreen // docs don't reliably have chrome.storage, so they message us instead. // We hold no in-memory state here — Chrome will kill this worker at any time. import { TARGETS, TYPES } from '../lib/messages.js'; import { logTrack } from '../lib/history.js'; const OFFSCREEN_PATH = 'src/offscreen/offscreen.html'; async function ensureOffscreen() { // hasDocument is the official MV3 way to check. Optional-chained // for older Chrome safety (we still require 116+ via the manifest). const exists = await chrome.offscreen.hasDocument?.(); if (exists) return; await chrome.offscreen.createDocument({ url: OFFSCREEN_PATH, reasons: ['AUDIO_PLAYBACK'], justification: 'Persistent audio playback for internet radio streams.', }); } chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { // Only react to messages targeted at the service worker. if (msg.target !== TARGETS.SW) return; (async () => { try { // Storage-gateway path: write to chrome.storage on behalf of the // offscreen document, which can't always reach it directly. if (msg.type === TYPES.LOG_TRACK_REQUEST) { const appended = await logTrack(msg.entry || {}); if (appended) { chrome.runtime.sendMessage({ target: TARGETS.POPUP, type: TYPES.TRACK_LOGGED, }).catch(() => {}); // no UI listening is fine } sendResponse({ ok: true, appended }); return; } // Default: forward audio-control messages to the offscreen doc. await ensureOffscreen(); const response = await chrome.runtime.sendMessage({ ...msg, target: TARGETS.OFFSCREEN, }); sendResponse(response); } catch (err) { sendResponse({ ok: false, error: err.message }); } })(); return true; // keep the channel open for async sendResponse });