// background.js const blobs = new Set(); function isExtensionUrl(url) { return url.startsWith(browser.runtime.getURL('')); } function viewerUrlFor(originalUrl) { return browser.runtime.getURL('viewer.html') + '?src=' + encodeURIComponent(originalUrl); } // Redirect obvious .pdf URLs (but skip extension pages) browser.webRequest.onBeforeRequest.addListener( details => { const url = details.url; if (isExtensionUrl(url)) return {}; const path = url.split('?')[0].toLowerCase(); if (path.endsWith('.pdf')) { return { redirectUrl: viewerUrlFor(details.url) }; } return {}; }, { urls: [""], types: ["main_frame"] }, ["blocking"] ); // Redirect when response headers indicate PDF browser.webRequest.onHeadersReceived.addListener( details => { const url = details.url; if (isExtensionUrl(url)) return {}; const headers = details.responseHeaders || []; for (let h of headers) { if (!h.name || !h.value) continue; const n = h.name.toLowerCase(); const v = h.value.toLowerCase(); if (n === "content-type" && v.includes("application/pdf")) { return { redirectUrl: viewerUrlFor(details.url) }; } // some servers force download with content-disposition if (n === "content-disposition" && v.includes("attachment") && v.includes(".pdf")) { return { redirectUrl: viewerUrlFor(details.url) }; } } return {}; }, { urls: [""], types: ["main_frame"] }, ["blocking", "responseHeaders"] ); // Message API: fetch PDF and return a blob: URL (no large binary through messaging) browser.runtime.onMessage.addListener(async (msg) => { if (!msg || !msg.type) return; if (msg.type === "fetch-pdf" && msg.url) { try { // Ensure you have host permission for the target (manifest had "") const res = await fetch(msg.url, { credentials: "include" }); if (!res.ok) { return { error: `HTTP ${res.status} ${res.statusText}` }; } const arrayBuffer = await res.arrayBuffer(); const contentType = res.headers.get("content-type") || "application/pdf"; const blob = new Blob([arrayBuffer], { type: contentType }); const blobUrl = URL.createObjectURL(blob); blobs.add(blobUrl); return { blobUrl, contentType }; } catch (err) { return { error: String(err) }; } } // allow viewer to tell us to free the blob URL when it's done if (msg.type === "revoke-blob" && msg.blobUrl) { if (blobs.has(msg.blobUrl)) { blobs.delete(msg.blobUrl); try { URL.revokeObjectURL(msg.blobUrl); } catch(e) {} return { revoked: true }; } return { revoked: false }; } });