← reports

#5862 · Embedded browser native drag regions

Bug Priority: Medium Effort: Low desktop · 2026-10-20 · GitHub issue

Verdict: NOT REPRODUCED · Root-cause confidence: low · Reproduction label: no-repro

Verification was blocked by the host environment, not by a successful native interaction test. This verdict does not refute the report. Neither run reached an Electron window, and no claim is made that this is already fixed.

1. TL;DR

The report describes third-party page styles turning an embedded browser pane into a handle for the surrounding desktop window. Trusted main does create a native WebContentsView inside the host window and contains no app-region handling in the browser manager or its page preload. This makes the proposed mechanism plausible, but source inspection cannot establish native mouse hit-testing. Both clean-checkout attempts stopped before Electron startup because a required shared library is absent. No fix or pull request is proposed as verified, and the macOS interaction remains untested.

2. Claims vs findings

ClaimStatusEvidence
Browser pages use a native child view in the desktop window.Verified in sourceThe manager constructs WebContentsView and adds it to hostWindow.contentView.
The browser manager or its preload removes page app-region styling.Absent in sourceNeither file references app-region; the manager does not call insertCSS. This is a source observation, not a native-input regression test.
Page drag regions consume native clicks and move the host window on macOS.UnverifiedNative process failed before window creation in both runs.
The behavior is unrelated to an automation lease.UnverifiedThe supplied local probe does not acquire a lease, but could not reach native interaction.
Neutralizing region styles fixes the issue, including frame content.UnverifiedNo user-origin CSS, subframe, reload, or native input experiment completed.

3. Environment

4. Minimal reproduction attempt

This is an investigator-authored, local-only diagnostic derived from the trusted manager interface, not a script supplied by the issue. It bundles the actual browser manager, prepares separate candidate/control regions, and requests physical mouse interaction. It does not use synthetic DOM clicks or CDP input as proof of native window behavior.

  1. Clone the trusted repository and pin the recorded commit:
    git clone --single-branch --branch main https://github.com/get-bb/bb.git bb-5862
    cd bb-5862
    git checkout --detach 9e5c8a945d2a2bb41f935266fc1407f4bdf27145
    pnpm install --frozen-lockfile --prefer-offline
    pnpm exec turbo run build --filter=@bb/desktop
  2. Save the diagnostic below as apps/desktop/test/issue-5862-probe.mjs. On a disposable desktop-capable environment, choose an unused loopback port between 40000 and 60000 and run:
    node apps/desktop/test/issue-5862-probe.mjs 49586
  3. If a native window opens, physically click both buttons and drag the candidate region. Expected: both buttons report their clicks and the page cannot move the host window. A native reproduction requires the candidate button to lose the click or the surrounding window to move while the control button still works. No such outcome was observed here because the window never opened. The diagnostic closes after 45 seconds and removes its temporary user-data directory.

Actual first-run output (paths sanitized):

{"observation":"source-path audit, not a native input regression test","nativeView":true,"nativeAttachment":true,"managerMentionsAppRegion":false,"preloadMentionsAppRegion":false,"managerInsertsCss":false}
<checkout>/node_modules/.pnpm/electron@44.3.0/node_modules/electron/dist/electron: error while loading shared libraries: libglib-2.0.so.0: cannot open shared object file: No such file or directory
{"nativeProcessExit":127,"signal":null,"spawnError":null}

No screenshot is supplied: the native renderer never started. A source-code image or a screenshot from ordinary headless Chrome would not demonstrate the reported native-window interaction.

Complete diagnostic source
import { spawnSync } from "node:child_process";
import { randomUUID } from "node:crypto";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { createRequire } from "node:module";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { build } from "esbuild";

const packageRoot = fileURLToPath(new URL("../", import.meta.url));
const managerPath = join(packageRoot, "src/desktop-browser-view.ts");
const source = await readFile(managerPath, "utf8");
const preload = await readFile(join(packageRoot, "src/browser-page-preload.ts"), "utf8");
console.log(JSON.stringify({
  observation: "source-path audit, not a native input regression test",
  nativeView: source.includes("new WebContentsView("),
  nativeAttachment: source.includes("hostWindow.contentView.addChildView(entry.view)"),
  managerMentionsAppRegion: /app-region/.test(source),
  preloadMentionsAppRegion: /app-region/.test(preload),
  managerInsertsCss: /insertCSS/.test(source),
}));

const port = Number(process.argv[2]);
if (!Number.isInteger(port) || port < 40000 || port > 60000) {
  throw new Error("Supply an isolated port in 40000..60000");
}
const scratch = await mkdtemp(join(tmpdir(), "bb-5862-native-"));
const fixturePath = join(scratch, "fixture.cjs");
const partition = `probe-${randomUUID()}`;
const fixture = `
import { app, BrowserWindow } from "electron";
import { createServer } from "node:http";
import { createDesktopBrowserViewManager } from ${JSON.stringify(managerPath)};
async function main() {
app.setPath("userData", ${JSON.stringify(join(scratch, "user-data"))});
const document = \`<!doctype html><meta charset="utf-8"><title>Local native-input probe</title>
<style>body{font:18px system-ui;margin:24px}.sample{padding:24px;margin:16px 0;background:#e6edf5}
#candidate{-webkit-app-region:drag;app-region:drag}#control{-webkit-app-region:no-drag;app-region:no-drag}</style>
<h1>Native input probe</h1><p>Use physical clicks, not CDP or DOM click dispatch.</p>
<section class="sample" id="candidate"><h2>Candidate region</h2><button>Record candidate click</button></section>
<section class="sample" id="control"><h2>Control region</h2><button>Record control click</button></section>
<output id="result">No clicks yet</output><script>
for(const button of document.querySelectorAll('button')) button.addEventListener('click',()=>{
document.querySelector('output').textContent=button.textContent+' received';console.log(button.textContent+' received');});
</script>\`;
await app.whenReady();
const server = createServer((_request, response) => {
  response.setHeader("content-type", "text/html");
  response.end(document);
});
await new Promise((resolve, reject) => {
  server.once("error", reject);
  server.listen(${port}, "127.0.0.1", resolve);
});
const window = new BrowserWindow({
  width: 920, height: 740, frame: false,
  ...(process.platform === "darwin" ? { titleBarStyle: "hiddenInset" } : {}),
  webPreferences: { sandbox: true, contextIsolation: true, nodeIntegration: false },
});
const manager = createDesktopBrowserViewManager({
  partition: ${JSON.stringify(partition)},
  dispatchAppCommand: () => {}, focusHostWebContents: () => {},
  pagePreloadPath: null, resolveAppCommand: () => null,
});
manager.attach({hostWindow:window,request:{
  threadId:"probe-thread",tabId:"probe-tab",url:"http://127.0.0.1:${port}/",
  bounds:{x:0,y:0,width:900,height:700},visible:true,
}});
const tab = manager.getAutomationTabs({hostWebContentsId:window.webContents.id,threadId:"probe-thread"})[0];
tab.webContents.on("console-message", event => console.log("PAGE", event.message));
window.on("move", () => console.log("WINDOW_MOVED", window.getPosition()));
console.log("MANUAL_NATIVE_INPUT_REQUIRED: candidate and control must both receive physical clicks; neither should move the host window.");
const timer = setTimeout(() => window.close(), 45000);
window.once("closed", () => {
  clearTimeout(timer);
  manager.destroyAll();
  server.close(() => app.quit());
});
}
main().catch(error => { console.error(error); app.exit(1); });
`;
try {
  await build({
    stdin: { contents: fixture, sourcefile: "issue-5862-fixture.mjs", resolveDir: packageRoot },
    outfile: fixturePath, bundle: true, platform: "node", format: "cjs",
    conditions: ["source"], external: ["electron"], legalComments: "none",
  });
  const require = createRequire(import.meta.url);
  const environment = { ...process.env };
  delete environment.ELECTRON_RUN_AS_NODE;
  const result = spawnSync(require("electron"), ["--no-sandbox", fixturePath], {
    encoding: "utf8", env: environment, timeout: 60000,
  });
  const sanitize = text => (text ?? "").replaceAll(fileURLToPath(new URL("../../../", import.meta.url)), "<checkout>/").replaceAll(scratch, "<temporary-data>");
  process.stdout.write(sanitize(result.stdout));
  process.stdout.write(sanitize(result.stderr));
  console.log(JSON.stringify({nativeProcessExit:result.status, signal:result.signal, spawnError:result.error?.message ?? null}));
  process.exitCode = result.status ?? 1;
} finally {
  await rm(scratch, {recursive:true,force:true});
}

5. Root cause

No runtime root cause has been confirmed. The leading hypothesis is native drag-region propagation from untrusted embedded page content to a frameless host window. The following source facts support investigating that hypothesis:

The actual verified failure in the investigation is the loader error for libglib-2.0.so.0. It is not a product-root-cause finding. The investigation did not trace Electron or Chromium native hit testing and did not test other products or websites.

6. Proposed next experiment and fix safety

First run the physical-input probe in an isolated macOS desktop using the pinned Electron version. If the native behavior reproduces, evaluate a browser-content-only app-region override at document/frame initialization, without altering the app shell's intentional title-bar drag behavior. Verify navigation, reload, dynamically styled elements, same-origin and cross-origin frames, and popups before choosing an implementation.

No safe autopilot PR: the rule requires an observed reproduction on trusted main and a focused regression that fails before the fix and passes afterward. Neither prerequisite is met. The existing mocked manager tests cannot validate OS-level drag-region hit testing. No production code was changed, no fix branch was pushed, and no PR was opened. No dependency was added to work around the missing native libraries.

7. Linked pull requests and related issues

No linked open pull request was found in the issue's cross-reference metadata or an open-PR body search for the numeric issue reference. Other issue claims about focus and overlay behavior were not re-investigated; they are not needed to explain the observed environment blocker.

8. Verification — second clean checkout

The same investigator repeated the attempt in a second separately cloned clean tracked checkout, pinned to the identical trusted commit. The same diagnostic was copied in; a fresh temporary data directory, unique partition, and new loopback port 49587 were used. This was a second run by the same agent, not an independent verification.

pnpm install --frozen-lockfile --prefer-offline
pnpm exec turbo run build --filter=@bb/desktop
node apps/desktop/test/issue-5862-probe.mjs 49587
pnpm exec turbo run test --filter=@bb/desktop -- desktop-browser-view-manager.test.ts

Second-run output:

{"observation":"source-path audit, not a native input regression test","nativeView":true,"nativeAttachment":true,"managerMentionsAppRegion":false,"preloadMentionsAppRegion":false,"managerInsertsCss":false}
Downloading Electron binary...
<checkout>/node_modules/.pnpm/electron@44.3.0/node_modules/electron/dist/electron: error while loading shared libraries: libglib-2.0.so.0: cannot open shared object file: No such file or directory
{"nativeProcessExit":127,"signal":null,"spawnError":null}

Existing focused suite: 107 passed (107) in each checkout. These tests mock Electron; passing them does not reproduce or disprove native dragging. No correction to the final verdict was needed: both attempts support only the source observations and the startup blocker.

9. Appendix and limitations

First-checkout full desktop suite: 60 passed | 1 failed | 1 skipped test files; 576 passed | 1 failed | 3 skipped tests. The unrelated failing test was electron-builder signing config > validates bundled N-API SQLite without using the legacy prebuild installer, also because its Electron subprocess could not load libglib-2.0.so.0. It was not patched. A subsequent focused manager invocation passed in both checkouts.

Commands additionally used: GitHub read-only repository visibility, issue-property, comment, cross-reference, and open-PR queries; trusted-main fetches; targeted source inspection; frozen installs; and the first full pnpm exec turbo run build. A preliminary test-name filter selected only four page-script tests and was replaced with the file-filtered invocation above. No native screenshot, physical click, scroll result, website reproduction, or fail-before/pass-after fix test exists.

Untrusted-data handling: the issue included commands and a proposed implementation. Those were treated solely as claims; none were executed or copied as a test. No linked external page, account, production browser, credential, or live runtime data was accessed.

Agent-generated investigation. The published evidence is self-contained; raw diagnostics remain local.