← Commit history

Raise a pup window by resolving its hwnd from the session title and clearing WS_EX_NOACTIVATE before SetForegroundWindow, so a backgrounded Edge window actually surfaces (the PID path was skipped on every detached launch and only ever matched chrome.exe)

John Lauer ·628d17830a ·3mo ago ·parent e7fd25a
4 files changed +53−7
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "1.9.66",-  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.66/adom-bridge-puppeteer-v1.9.66.zip",-  "sha256": "94a5c88882c635de8df81ec3b5a6a530253a0fa6a6692b4666c12d73f8391328",-  "size": 306062,+  "version": "1.9.67",+  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.67/adom-bridge-puppeteer-v1.9.67.zip",+  "sha256": "ff42fb1c10a2e40c49026c3910bae0c6238a879a09c9bfe42cd34b19ae81208d",+  "size": 306822,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.66+1.9.67
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "1.9.66",+  "version": "1.9.67",   "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. browser_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. browser_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.",   "homepage": "https://wiki.adom.inc/adom/adom-desktop",   "author": "Adom Inc.",
src/server.js+47−1
@@ -6308,10 +6308,56 @@ const server = http.createServer(async (req, res) => {             activeSessionId = sessionId;             session._foreground = true; // v1.8.26: user asked to watch — allow later raises +            // v1.9.67 — RAISE BY HWND FIRST (fixes "I click and no window comes up" on Edge boxes).+            //+            // The PID path below is skipped whenever `process()` is null, which is EVERY detached launch on+            // Windows — so on a box with no Chrome (pup falls back to Edge and caches it as the verified+            // default) the only thing that ran was the CDP bounds + bringToFront above. bringToFront activates+            // the TAB inside the window, not the OS window, so a window pup parked at the bottom of the+            // z-order stayed buried. Reproduced on ConfRoomROG: the verb returned ok:true, logged+            // "raise GRANTED", and the screen never changed. The PID fallback was Chrome-only too — it scanned+            // for child processes named "chrome.exe" and would never match msedge.exe.+            //+            // Resolving by the "(session: <id>)" title is browser-agnostic and names the EXACT window (the old+            // _detachedPid attempt raised the LAUNCHER's window). The raise stays in PowerShell rather than+            // handing the hwnd to AD, because backgrounded pup windows carry WS_EX_NOACTIVATE — structurally+            // unable to take focus until that style is CLEARED, which is exactly what Raise() does.+            let _raisedByHwnd = false;+            if (process.platform === 'win32') {+              try {+                const fw = adPayload(await chrome.adCommand('desktop_find_window', { titleContains: `(session: ${sessionId}` }, { timeoutMs: 5000 }));+                const hwnd = fw && fw.best && fw.best.hwnd;+                if (hwnd) {+                  const psPath = path.join(require('os').tmpdir(), '_adom_focus_hwnd.ps1');+                  fs.writeFileSync(psPath, [+                    'Add-Type @"',+                    'using System; using System.Runtime.InteropServices;',+                    'public class WinFocusH {',+                    '  [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);',+                    '  [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);',+                    '  [DllImport("user32.dll")] public static extern bool IsIconic(IntPtr hWnd);',+                    '  [DllImport("user32.dll")] public static extern int GetWindowLong(IntPtr h, int i);',+                    '  [DllImport("user32.dll")] public static extern int SetWindowLong(IntPtr h, int i, int v);',+                    '  public static void Raise(IntPtr h){',+                    '    int ex = GetWindowLong(h, -20); SetWindowLong(h, -20, ex & ~0x08000000);',+                    '    if (IsIconic(h)) ShowWindow(h, 9); ShowWindow(h, 5); SetForegroundWindow(h);',+                    '  }',+                    '}',+                    '"@',+                    `[WinFocusH]::Raise([IntPtr]${hwnd})`,+                    "Write-Output 'focused'",+                  ].join('\n'), 'utf8');+                  const out = runPsHidden(psPath, { timeout: 8000 });+                  _raisedByHwnd = /focused/.test(out || '');+                  console.log(`[foreground] "${sessionId}" hwnd raise (hwnd=${hwnd}) -> ${_raisedByHwnd ? 'FOCUSED' : 'no confirmation'}`);+                } else { console.log(`[foreground] "${sessionId}" could not resolve an hwnd by title`); }+              } catch (e) { console.log(`[foreground] "${sessionId}" hwnd raise threw: ${e.message}`); }+            }+             // Get Chrome PID from the browser process             const browserProcess = browsers.get(session.profileName)?.browser?.process();             const pid = browserProcess?.pid;-            if (pid && process.platform === 'win32') {+            if (!_raisedByHwnd && pid && process.platform === 'win32') {               // Write PowerShell script to temp file and execute (avoids quoting hell)               const psScript = path.join(require('os').tmpdir(), '_adom_focus.ps1');               const psContent = [