Files
zn-ai/src/main/scripts/common/tabs.js
2026-03-25 22:03:51 +08:00

139 lines
3.5 KiB
JavaScript

const normalizeUrl = (value) => {
const raw = String(value || '').trim();
if (!raw) return null;
try {
return new URL(raw);
} catch {
return null;
}
};
const isSameTarget = (currentUrl, targetUrl) => {
if (!currentUrl || !targetUrl) return false;
if (currentUrl.startsWith(targetUrl)) return true;
const current = normalizeUrl(currentUrl);
const target = normalizeUrl(targetUrl);
if (!current || !target) return false;
if (current.origin !== target.origin) return false;
if (!current.pathname.startsWith(target.pathname)) return false;
return true;
};
const isSameOrigin = (currentUrl, targetUrl) => {
const current = normalizeUrl(currentUrl);
const target = normalizeUrl(targetUrl);
if (!current || !target) return false;
return current.origin === target.origin;
};
const isBlankLikePage = (url) => {
const u = String(url || '').trim().toLowerCase();
if (!u) return true;
if (u === 'about:blank' || u.startsWith('about:blank#') || u.startsWith('about:blank?')) return true;
if (u === 'chrome://newtab/' || u.startsWith('chrome://newtab')) return true;
if (u.startsWith('chrome://new-tab-page')) return true;
return false;
};
const preparePage = async (
chromium,
{
tabIndex = Number(process.env.TAB_INDEX),
endpoint = process.env.CDP_ENDPOINT || 'http://127.0.0.1:9222',
targetUrl,
} = {},
) => {
const browser = await chromium.connectOverCDP(endpoint);
const context = browser.contexts()[0];
if (!context) {
throw new Error('No browser context available');
}
await context.addInitScript(() => {
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
});
let pages = await context.pages();
let page = null;
if (targetUrl) {
// 1. Try exact/path match
for (let i = 0; i < pages.length; i++) {
const p = pages[i];
if (isSameTarget(p.url(), targetUrl)) {
page = p;
break;
}
}
// 2. Try origin match
if (!page) {
for (let i = 0; i < pages.length; i++) {
const p = pages[i];
if (isSameOrigin(p.url(), targetUrl)) {
page = p;
break;
}
}
}
}
if (!page) {
const isTabIndexSet = Number.isFinite(tabIndex) && tabIndex >= 0;
// If targetUrl provided and NO specific tabIndex requested, avoid hijacking tab 0
if (targetUrl && !isTabIndexSet) {
// Try blank page
for (let i = 0; i < pages.length; i++) {
if (isBlankLikePage(pages[i].url())) {
page = pages[i];
break;
}
}
// Else new page
if (!page) {
page = await context.newPage();
}
} else {
// Legacy/Default behavior: use tabIndex (default 0)
const idx = isTabIndexSet ? Math.floor(tabIndex) : 0;
while (pages.length <= idx) {
await context.newPage();
pages = await context.pages();
}
page = pages[idx];
}
}
await page.bringToFront();
if (targetUrl) {
const currentUrl = page.url();
if (!currentUrl || !isSameTarget(currentUrl, targetUrl)) {
await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
}
}
return { browser, context, page };
};
const safeDisconnectBrowser = async (browser) => {
if (!browser) return;
try {
if (typeof browser.disconnect === 'function') {
await browser.disconnect();
} else {
await browser.close();
}
} catch {}
};
module.exports = {
preparePage,
safeDisconnectBrowser,
};