| 1 | // Measure Worker-owned listeners independently of DOM listeners. A leased |
| 2 | // Markdown parser may legitimately exist or be absent after navigation; its |
| 3 | // single worker is a bounded resource, not an accumulating DOM subscription. |
| 4 | export async function workerListeners(cdp) { |
| 5 | const objectGroup = "app-memory-worker-evidence"; |
| 6 | try { |
| 7 | const { result: prototype } = await cdp.send("Runtime.evaluate", { expression: "Worker.prototype", objectGroup }); |
| 8 | const { objects } = await cdp.send("Runtime.queryObjects", { prototypeObjectId: prototype.objectId, objectGroup }); |
| 9 | const { result } = await cdp.send("Runtime.getProperties", { objectId: objects.objectId, ownProperties: true }); |
| 10 | const workers = result.filter(property => /^\d+$/.test(property.name)); |
| 11 | return await Promise.all(workers.map(async ({ value }) => { |
| 12 | const { listeners } = await cdp.send("DOMDebugger.getEventListeners", { objectId: value.objectId }); |
| 13 | return listeners.map(listener => listener.type).sort(); |
| 14 | })); |
| 15 | } finally { |
| 16 | await cdp.send("Runtime.releaseObjectGroup", { objectGroup }); |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | export function boundedParserWorker(sample) { |
| 21 | // Older evidence did not attribute workers: leave its raw counters intact. |
| 22 | if (sample.workers === undefined) return true; |
| 23 | return Array.isArray(sample.workers) && sample.workers.length <= 1 && sample.workers.every(events => |
| 24 | Array.isArray(events) && events.filter(type => type === "message").length <= 1 |
| 25 | && events.filter(type => type === "error").length <= 2 |
| 26 | && events.every(type => type === "message" || type === "error")); |
| 27 | } |
| 28 | |
| 29 | export function domListenerCount(sample) { |
| 30 | const owned = boundedParserWorker(sample) ? (sample.workers ?? []).flat().length : 0; |
| 31 | return sample.dom?.jsEventListeners - owned; |
| 32 | } |
| 33 |