From 359566131d3aba372a75091f66719711502f05ec Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Sat, 11 Apr 2026 02:01:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E4=B8=8E=E5=90=8E=E5=8F=B0=E9=80=9A=E4=BF=A1?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=20PING=20=E5=93=8D=E5=BA=94?= =?UTF-8?q?=E5=92=8C=E7=A1=AE=E4=BF=9D=E8=84=9A=E6=9C=AC=E5=B0=B1=E7=BB=AA?= =?UTF-8?q?=E7=9A=84=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- background.js | 89 +++++++++++++++++++++++++++++++++----------- content/utils.js | 10 ++++- content/vps-panel.js | 44 +++++++++++++--------- 3 files changed, 102 insertions(+), 41 deletions(-) diff --git a/background.js b/background.js index 6599841..64ae061 100644 --- a/background.js +++ b/background.js @@ -330,6 +330,55 @@ async function closeTabsByUrlPrefix(prefix, options = {}) { return matchedIds.length; } +async function pingContentScriptOnTab(tabId) { + if (!Number.isInteger(tabId)) return null; + + try { + return await chrome.tabs.sendMessage(tabId, { + type: 'PING', + source: 'background', + payload: {}, + }); + } catch { + return null; + } +} + +async function ensureContentScriptReadyOnTab(source, tabId, options = {}) { + const { inject = null, injectSource = null } = options; + const pong = await pingContentScriptOnTab(tabId); + + if (pong?.ok && (!pong.source || pong.source === source)) { + await registerTab(source, tabId); + return; + } + + if (!inject || !inject.length) { + throw new Error(`${getSourceLabel(source)} 内容脚本未就绪,且未提供可用的注入文件。`); + } + + const registry = await getTabRegistry(); + if (registry[source]) { + registry[source].ready = false; + await setState({ tabRegistry: registry }); + } + + if (injectSource) { + await chrome.scripting.executeScript({ + target: { tabId }, + func: (injectedSource) => { + window.__MULTIPAGE_SOURCE = injectedSource; + }, + args: [injectSource], + }); + } + + await chrome.scripting.executeScript({ + target: { tabId }, + files: inject, + }); +} + // ============================================================ // Command Queue (for content scripts not yet ready) // ============================================================ @@ -2302,8 +2351,9 @@ async function executeStep8(state) { resolved = true; cleanupListener(); clearTimeout(timeout); - completeStepFromBackground(8, { localhostUrl: details.url }).then(() => { - addLog(`步骤 8:已捕获 localhost 地址:${details.url}`, 'ok'); + addLog(`步骤 8:已捕获 localhost 地址:${details.url}`, 'ok').then(() => { + return completeStepFromBackground(8, { localhostUrl: details.url }); + }).then(() => { resolve(); }).catch((err) => { reject(err); @@ -2367,23 +2417,14 @@ async function executeStep9(state) { await addLog('步骤 9:正在打开 CPA 面板...'); + const injectFiles = ['content/utils.js', 'content/vps-panel.js']; let tabId = await getTabId('vps-panel'); const alive = tabId && await isTabAlive('vps-panel'); if (!alive) { - await closeConflictingTabsForSource('vps-panel', state.vpsUrl); - // Create new tab - const tab = await chrome.tabs.create({ url: state.vpsUrl, active: true }); - tabId = tab.id; - await rememberSourceLastUrl('vps-panel', state.vpsUrl); - await new Promise(resolve => { - const listener = (tid, info) => { - if (tid === tabId && info.status === 'complete') { - chrome.tabs.onUpdated.removeListener(listener); - resolve(); - } - }; - chrome.tabs.onUpdated.addListener(listener); + tabId = await reuseOrCreateTab('vps-panel', state.vpsUrl, { + inject: injectFiles, + reloadIfSameUrl: true, }); } else { await closeConflictingTabsForSource('vps-panel', state.vpsUrl, { excludeTabIds: [tabId] }); @@ -2391,21 +2432,25 @@ async function executeStep9(state) { await rememberSourceLastUrl('vps-panel', state.vpsUrl); } - // Inject scripts directly and wait for them to be ready - await chrome.scripting.executeScript({ - target: { tabId }, - files: ['content/utils.js', 'content/vps-panel.js'], + await ensureContentScriptReadyOnTab('vps-panel', tabId, { + inject: injectFiles, }); - await new Promise(r => setTimeout(r, 1000)); - // Send command directly — bypass queue/ready mechanism await addLog('步骤 9:正在填写回调地址...'); - await chrome.tabs.sendMessage(tabId, { + const result = await sendToContentScriptResilient('vps-panel', { type: 'EXECUTE_STEP', step: 9, source: 'background', payload: { localhostUrl: state.localhostUrl, vpsPassword: state.vpsPassword }, + }, { + timeoutMs: 30000, + retryDelayMs: 700, + logMessage: '步骤 9:CPA 面板通信未就绪,正在等待页面恢复...', }); + + if (result?.error) { + throw new Error(result.error); + } } // ============================================================ diff --git a/content/utils.js b/content/utils.js index 224c774..a4aa52d 100644 --- a/content/utils.js +++ b/content/utils.js @@ -17,10 +17,18 @@ const LOG_PREFIX = `[MultiPage:${SCRIPT_SOURCE}]`; const STOP_ERROR_MESSAGE = '流程已被用户停止。'; let flowStopped = false; -chrome.runtime.onMessage.addListener((message) => { +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === 'STOP_FLOW') { flowStopped = true; console.warn(LOG_PREFIX, STOP_ERROR_MESSAGE); + return; + } + + if (message.type === 'PING') { + sendResponse({ + ok: true, + source: SCRIPT_SOURCE, + }); } }); diff --git a/content/vps-panel.js b/content/vps-panel.js index fb7ae54..85c8fcb 100644 --- a/content/vps-panel.js +++ b/content/vps-panel.js @@ -25,24 +25,32 @@ console.log('[MultiPage:vps-panel] Content script loaded on', location.href); -// Listen for commands from Background -chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - if (message.type === 'EXECUTE_STEP') { - resetStopState(); - handleStep(message.step, message.payload).then(() => { - sendResponse({ ok: true }); - }).catch(err => { - if (isStopError(err)) { - log(`步骤 ${message.step}:已被用户停止。`, 'warn'); - sendResponse({ stopped: true, error: err.message }); - return; - } - reportError(message.step, err.message); - sendResponse({ error: err.message }); - }); - return true; - } -}); +const VPS_PANEL_LISTENER_SENTINEL = 'data-multipage-vps-panel-listener'; + +if (document.documentElement.getAttribute(VPS_PANEL_LISTENER_SENTINEL) !== '1') { + document.documentElement.setAttribute(VPS_PANEL_LISTENER_SENTINEL, '1'); + + // Listen for commands from Background + chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.type === 'EXECUTE_STEP') { + resetStopState(); + handleStep(message.step, message.payload).then(() => { + sendResponse({ ok: true }); + }).catch(err => { + if (isStopError(err)) { + log(`步骤 ${message.step}:已被用户停止。`, 'warn'); + sendResponse({ stopped: true, error: err.message }); + return; + } + reportError(message.step, err.message); + sendResponse({ error: err.message }); + }); + return true; + } + }); +} else { + console.log('[MultiPage:vps-panel] 消息监听已存在,跳过重复注册'); +} async function handleStep(step, payload) { switch (step) {