Commit 95cd2d21 authored by 谢宇轩's avatar 谢宇轩

fix: edit chromium tab bug

parent 4685f05e
......@@ -269,9 +269,16 @@ function looksLikeDisplayFailure(log) {
// startup tab is an orphan that wastes a tab slot and inflates Target.getTargets
// counts. Close it via the HTTP /json/close/<id> endpoint (no WebSocket needed).
// Only closes about:blank / chrome://newtab — never touches real harvest tabs.
//
// IMPORTANT (windowed mode / headless=false): Chromium quits when its last tab
// is closed — this is standard browser behavior, not a bug. In headless mode
// the browser stays alive with zero tabs, but in windowed mode it does not.
// When headless=false, we must keep at least one tab open as a "keep-alive" tab
// so that harvest sessions closing their own tabs don't accidentally kill the
// browser. We keep the FIRST startup tab and only close the rest (if any).
const STARTUP_TAB_URLS = new Set(['about:blank', 'chrome://newtab/', 'chrome://newtab']);
function closeStartupTabs(port) {
function closeStartupTabs(port, keepAlive = false) {
return new Promise((resolve) => {
// agent: false → don't pool sockets in the global HTTP agent. Without this
// the keep-alive sockets linger and keep the Node event loop alive, hanging
......@@ -282,7 +289,10 @@ function closeStartupTabs(port) {
res.on('end', () => {
try {
const tabs = JSON.parse(data);
const orphans = tabs.filter(t => t.type === 'page' && STARTUP_TAB_URLS.has(t.url));
let orphans = tabs.filter(t => t.type === 'page' && STARTUP_TAB_URLS.has(t.url));
// In windowed mode, keep the first startup tab alive so the browser
// doesn't quit when harvest tabs close. Only close extras.
if (keepAlive && orphans.length > 0) orphans = orphans.slice(1);
if (orphans.length === 0) return resolve(0);
let remaining = orphans.length;
for (const tab of orphans) {
......@@ -306,7 +316,7 @@ async function ensureChromium(config, portOverride) {
// 1. Already up? (idempotent — safe to call every run)
try {
const v = await cdpVersion(opts.cdpPort, 1500);
await closeStartupTabs(opts.cdpPort); // clean orphan tabs from prior runs
await closeStartupTabs(opts.cdpPort, !opts.headless); // keep-alive tab in windowed mode
return { port: opts.cdpPort, alreadyRunning: true, browser: v.Browser };
} catch (e) { /* not up yet, fall through to launch */ }
......@@ -323,7 +333,7 @@ async function ensureChromium(config, portOverride) {
// 4. Wait for readiness
try {
const v = await waitForCdp(opts.cdpPort, 30000, 500);
await closeStartupTabs(opts.cdpPort); // close the initial about:blank tab
await closeStartupTabs(opts.cdpPort, !opts.headless); // keep-alive tab in windowed mode
return { port: opts.cdpPort, pid, browser: v.Browser, bin, logPath };
} catch (e) {
const logTail = tailFile(logPath);
......
......@@ -269,9 +269,16 @@ function looksLikeDisplayFailure(log) {
// startup tab is an orphan that wastes a tab slot and inflates Target.getTargets
// counts. Close it via the HTTP /json/close/<id> endpoint (no WebSocket needed).
// Only closes about:blank / chrome://newtab — never touches real harvest tabs.
//
// IMPORTANT (windowed mode / headless=false): Chromium quits when its last tab
// is closed — this is standard browser behavior, not a bug. In headless mode
// the browser stays alive with zero tabs, but in windowed mode it does not.
// When headless=false, we must keep at least one tab open as a "keep-alive" tab
// so that harvest sessions closing their own tabs don't accidentally kill the
// browser. We keep the FIRST startup tab and only close the rest (if any).
const STARTUP_TAB_URLS = new Set(['about:blank', 'chrome://newtab/', 'chrome://newtab']);
function closeStartupTabs(port) {
function closeStartupTabs(port, keepAlive = false) {
return new Promise((resolve) => {
// agent: false → don't pool sockets in the global HTTP agent. Without this
// the keep-alive sockets linger and keep the Node event loop alive, hanging
......@@ -282,7 +289,10 @@ function closeStartupTabs(port) {
res.on('end', () => {
try {
const tabs = JSON.parse(data);
const orphans = tabs.filter(t => t.type === 'page' && STARTUP_TAB_URLS.has(t.url));
let orphans = tabs.filter(t => t.type === 'page' && STARTUP_TAB_URLS.has(t.url));
// In windowed mode, keep the first startup tab alive so the browser
// doesn't quit when harvest tabs close. Only close extras.
if (keepAlive && orphans.length > 0) orphans = orphans.slice(1);
if (orphans.length === 0) return resolve(0);
let remaining = orphans.length;
for (const tab of orphans) {
......@@ -306,7 +316,7 @@ async function ensureChromium(config, portOverride) {
// 1. Already up? (idempotent — safe to call every run)
try {
const v = await cdpVersion(opts.cdpPort, 1500);
await closeStartupTabs(opts.cdpPort); // clean orphan tabs from prior runs
await closeStartupTabs(opts.cdpPort, !opts.headless); // keep-alive tab in windowed mode
return { port: opts.cdpPort, alreadyRunning: true, browser: v.Browser };
} catch (e) { /* not up yet, fall through to launch */ }
......@@ -323,7 +333,7 @@ async function ensureChromium(config, portOverride) {
// 4. Wait for readiness
try {
const v = await waitForCdp(opts.cdpPort, 30000, 500);
await closeStartupTabs(opts.cdpPort); // close the initial about:blank tab
await closeStartupTabs(opts.cdpPort, !opts.headless); // keep-alive tab in windowed mode
return { port: opts.cdpPort, pid, browser: v.Browser, bin, logPath };
} catch (e) {
const logTail = tailFile(logPath);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment