/* global chrome, pako */ function log(...args) { console.log.apply(null, ["[webrtc-internal-exporter:background]", ...args]); } log("loaded"); import "/assets/pako.min.js"; const DEFAULT_OPTIONS = { url: "http://localhost:9092", username: "", password: "", updateInterval: 2, gzip: false, job: "webrtc-internals-exporter", enabledOrigins: { }, enabledStats: ["data-channel", "local-candidate", "remote-candidate"] }; const options = {}; // Handle install/update. chrome.runtime.onInstalled.addListener(async ({ reason }) => { log("onInstalled", reason); if (reason === "install") { await chrome.storage.sync.set(DEFAULT_OPTIONS); } else if (reason === "update") { const options = await chrome.storage.sync.get(); await chrome.storage.sync.set({ ...DEFAULT_OPTIONS, ...options, }); } }); async function updateTabInfo(tab) { const tabId = tab.id; const origin = new URL(tab.url || tab.pendingUrl).origin; if (options.enabledOrigins) { const { peerConnectionsPerOrigin } = await chrome.storage.local.get( "peerConnectionsPerOrigin", ); const peerConnections = (peerConnectionsPerOrigin && peerConnectionsPerOrigin[origin]) || 0; chrome.action.setTitle({ title: `WebRTC Internals Exporter\nActive Peer Connections: ${peerConnections}`, tabId, }); chrome.action.setBadgeText({ text: `${peerConnections}`, tabId }); chrome.action.setBadgeBackgroundColor({ color: "rgb(63, 81, 181)", tabId }); } else { chrome.action.setTitle({ title: `WebRTC Internals Exporter (disabled)`, tabId, }); chrome.action.setBadgeText({ text: "", tabId }); } } async function optionsUpdated() { const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true, }); await updateTabInfo(tab); } chrome.storage.sync.get().then((ret) => { Object.assign(options, ret); log("options loaded"); optionsUpdated(); }); chrome.storage.onChanged.addListener((changes, areaName) => { if (areaName !== "sync") return; for (let [key, { newValue }] of Object.entries(changes)) { options[key] = newValue; } log("options changed"); optionsUpdated(); }); chrome.tabs.onActivated.addListener(async ({ tabId }) => { try { const tab = await chrome.tabs.get(tabId); await updateTabInfo(tab); } catch (err) { log(`get tab error: ${err.message}`); } }); chrome.tabs.onUpdated.addListener(async (tabId, changeInfo) => { if (!changeInfo.url) return; await updateTabInfo({ id: tabId, url: changeInfo.url }); }); // Send data to POST handler. async function sendJsonData(method, data) { const { url, username, password, gzip, job } = options; const headers = { "Content-Type": "application/json", }; if (username && password) { headers.Authorization = "Basic " + btoa(`${username}:${password}`); } if (data && gzip) { headers["Content-Encoding"] = "gzip"; data = await pako.gzip(data); } log(`sendJsonData: ${data} \n ${data.length} bytes (gzip: ${gzip}) url: ${url} job: ${job}`); const start = Date.now(); const response = await fetch( `${url}/${job}`, { method, headers, body: method === "POST" ? data : undefined, }, ); const stats = await chrome.storage.local.get([ "messagesSent", "bytesSent", "totalTime", "errors", ]); if (data) { stats.messagesSent = (stats.messagesSent || 0) + 1; stats.bytesSent = (stats.bytesSent || 0) + data.length; stats.totalTime = (stats.totalTime || 0) + Date.now() - start; } if (!response.ok) { stats.errors = (stats.errors || 0) + 1; } await chrome.storage.local.set(stats); if (!response.ok) { const text = await response.text(); throw new Error(`Response status: ${response.status} error: ${text}`); } return response.text(); } chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.event === "peer-connection-stats") { sendJsonData("POST", JSON.stringify(message.data)) .then(() => { sendResponse({}); }) .catch((err) => { sendResponse({ error: err.message }); }); } else if (message.event === "peer-connections-stats") { sendJsonData("POST", JSON.stringify(message.data)) .then(() => { sendResponse({}); }) .catch((err) => { sendResponse({ error: err.message }); }); } else { sendResponse({ error: "unknown event" }); } return true; });