← Commit history

The annotate capture toast's preview thumbnail rendered as a broken image on CSP-strict sites, the same disease as the cursors: it was an img loading a data: URL, and image loads from a page's DOM obey the page's CSP img-src no matter who inserted the element. The preview now ships as raw RGBA pixels and is PAINTED into a canvas via putImageData, a pure pixel write no site's CSP can veto. The annotate surface now loads zero external resources: cursors are inline SVG sprites, the preview is painted pixels.

John Lauer ·694aa4e93c ·2mo ago ·parent 094bdc1
4 files changed +31−8
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "2.0.134",-  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.134/adom-bridge-puppeteer-v2.0.134.zip",-  "sha256": "1a62e26143c761a18a9bcb979debdef5782d7bbc9a531d5513baf92c539baa38",-  "size": 888411,+  "version": "2.0.135",+  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.135/adom-bridge-puppeteer-v2.0.135.zip",+  "sha256": "58fdedc245f3a0cdd09f90d1766509f14d4d6efe7ba6539f9b24ec8141896073",+  "size": 889015,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-2.0.134+2.0.135
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "2.0.134",+  "version": "2.0.135",   "description": "Drives a browser ALREADY on the machine \u2014 installed Chrome, else Microsoft Edge (every Windows PC ships with Edge) \u2014 launched with a FRESH isolated profile, so the common case has NO ~150 MB Chrome-for-Testing download and it never touches the user's real tabs/logins. Binds its local server to LOOPBACK (127.0.0.1) only, so no Windows Firewall 'allow Node through the firewall' prompt ever pops. Chrome for Testing is fetched only as a LAST RESORT if the machine has no Chromium browser at all. SPAWN-VERIFIED fallback: pup tries each detected browser (Chrome \u2192 Edge \u2192 CfT) and actually launches it, falling through to the next if one won't start (and handling browsers like Edge that relaunch onto another PID), so a corrupt/partial install never hard-fails an open. The browser that launches is cached as the default (persisted across restarts) so it isn't re-probed each time. On spawn the bridge SELF-WARMS: detects browsers + disk, and only if the box has no Chrome/Edge and no cached CfT does it background-fetch CfT so the first open is instant. pup_use pins a specific browser (chrome | edge | cft | auto); {browser:'chrome',install:true} downloads & installs REAL Google Chrome (user-scoped, no admin) on an Edge-only box. pup_readiness (READ-ONLY) reports which browser will be used + the full candidate list + the default + free disk. Every failure returns a specific errorCode + _hint (chrome_install_no_disk, chrome_download_network, chrome_install_permission, chrome_stable_not_detected, \u2026). Launches with FULL browser permissions by default for full debug (strictPermissions:true opts out). Headful automation: open/close windows+tabs, navigate, screenshot, eval JS, record. pup is a GENERIC, non-profile browser only \u2014 every session is a fresh isolated Chrome/Edge process, never the user's real signed-in browser; profile defaults to sessionId. To drive the user's REAL Chrome/Edge (their profile, cookies, SSO), use the Adom browser EXTENSION's nbrowser_* verbs \u2014 a separate bridge, not pup's job. macOS (v1.9.74+): full parity with mac-native mechanics \u2014 background-by-default is the app-level hide (windows stay drivable/screenshottable while hidden), raise/lower map to unhide+activate/hide, pup_alert_window posts a native toast whose click focuses the window, pup_use {browser:'chrome',install:true} installs Chrome from the official dmg, and taskbar identity/badging remains Windows-only.",   "homepage": "https://wiki.adom.inc/adom/adom-bridge",   "author": "Adom Inc.",
src/server.js+25−2
@@ -5847,7 +5847,20 @@ const ANNOTATE_OVERLAY_JS = `window.__pupStartAnnotate = function (initTool) {       t.style.cssText = 'position:fixed;left:50%;transform:translateX(-50%);bottom:18px;z-index:2147483647;background:#21262df2;border:1px solid #30363d;border-radius:12px;padding:10px;box-shadow:0 8px 30px #000c;font:12px Satoshi,system-ui,sans-serif;color:#c9d1d9;max-width:340px;cursor:pointer;';       var cap = document.createElement('div'); cap.textContent = line1 || 'Captured'; cap.style.cssText = 'margin-bottom:7px;color:#00d4cb;font-weight:500;';       t.appendChild(cap);-      if (previewUrl) { var im = document.createElement('img'); im.src = previewUrl; im.style.cssText = 'display:block;max-width:320px;max-height:200px;border-radius:8px;border:1px solid #30363d;'; t.appendChild(im); }+      // v2.0.135: the preview arrives as RAW RGBA pixels and is PAINTED (putImageData), never+      // LOADED — an <img src="data:..."> is a resource load the page's CSP img-src can veto+      // (Hacker News does), which showed the user a broken-image icon. Pixel writes to a canvas+      // are not loads; no site's CSP can break this. (String form kept for mixed-version overlap.)+      if (previewUrl && previewUrl.b64 && previewUrl.w) {+        try {+          var pc = document.createElement('canvas'); pc.width = previewUrl.w; pc.height = previewUrl.h;+          pc.style.cssText = 'display:block;max-width:320px;max-height:200px;border-radius:8px;border:1px solid #30363d;';+          var bin = atob(previewUrl.b64), n = bin.length, arr = new Uint8ClampedArray(n);+          for (var i2 = 0; i2 < n; i2++) arr[i2] = bin.charCodeAt(i2);+          pc.getContext('2d').putImageData(new ImageData(arr, previewUrl.w, previewUrl.h), 0, 0);+          t.appendChild(pc);+        } catch (e2) {}+      } else if (previewUrl && typeof previewUrl === 'string') { var im = document.createElement('img'); im.src = previewUrl; im.style.cssText = 'display:block;max-width:320px;max-height:200px;border-radius:8px;border:1px solid #30363d;'; t.appendChild(im); }       t.onclick = function () { t.remove(); };       document.documentElement.appendChild(t);       setTimeout(function () { try { t.remove(); } catch (e) {} }, 4500);@@ -6258,8 +6271,18 @@ async function processAnnotateCapture(session, sessionId, pg, res) {   }   console.log(`[annotate] "${sessionId}" capture: screenshot ok (${buf.length}B)`);   // instant feedback: flash + toast (preview resize is fast, everything else waits)+  // v2.0.135 (John: "the image you grabbed is broken in this page?"): the preview was a+  // data:image/png URL in an <img>, and image loads — data: included — are governed by the+  // PAGE'S CSP img-src. CSP-strict sites (Hacker News: img-src 'self' ...) blocked it, so the+  // toast showed a broken-image icon. Same disease as the annotate cursors. Fix: ship RAW RGBA+  // pixels and let the toast PAINT them via canvas putImageData — pure pixel writes, no image+  // load, so no CSP anywhere can break the preview.   let preview = null;-  try { const sharp = require('sharp'); const small = await sharp(buf).resize({ width: 320, withoutEnlargement: true }).png().toBuffer(); preview = 'data:image/png;base64,' + small.toString('base64'); } catch (e) {}+  try {+    const sharp = require('sharp');+    const { data, info } = await sharp(buf).resize({ width: 320, withoutEnlargement: true }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });+    preview = { b64: data.toString('base64'), w: info.width, h: info.height };+  } catch (e) {}   const line1 = `${isSnip ? 'Snippet' : 'Annotation'} copied to your clipboard`;   pg.evaluate((rg, pv, l1) => { if (window.__pupCaptureDone) window.__pupCaptureDone(rg, pv, l1); }, clipRect || null, preview, line1).catch(() => {});   // slow legs, off the perceived path