Chrome扩展:背景脚本无法向内容脚本发送executeAction消息
问题诊断与修复方案
核心问题分析
你的代码存在几个关键错误,导致消息无法正常发送:
actionCompleted回调中tabId未定义
当内容脚本发送actionCompleted消息时,代码尝试调用triggerAction(tabId),但此处的tabId从未被声明或赋值,会抛出ReferenceError,直接终止后续执行。triggerAction函数参数不匹配
在chrome.tabs.onUpdated的回调中,你调用triggerAction(tabId),但triggerAction函数定义时接收的是完整 tab 对象(而非数字类型的 tabId)。这会导致函数内部访问newTab.id时得到undefined,进而使脚本注入和消息发送因无效 tabId 失败。缺少 API 错误检查
Chrome 扩展 API 的回调中未检查chrome.runtime.lastError,无法及时发现脚本注入或消息发送失败的具体原因。
修复后的代码
background.js
console.log('Background script running'); let actionStep = 0; let currentTabId = null; // 存储当前操作的 tabId chrome.runtime.onMessage.addListener((message, _sender, _sendResponse) => { if (message.action === 'actionCompleted') { actionStep++; if (currentTabId !== null) { triggerAction(currentTabId); console.log('Action completed'); } } else if (message.action === 'triggerAction') { chrome.tabs.create({ url: 'https://www.amazon.com' }, (newTab) => { console.log('New tab created:', newTab); currentTabId = newTab.id; // 记录当前 tabId chrome.tabs.onUpdated.addListener(function listener(tabId, changeInfo) { if (tabId === newTab.id && changeInfo.status === 'complete') { chrome.tabs.onUpdated.removeListener(listener); console.log('ABOUT TO EXECUTE SCRIPT'); chrome.scripting.executeScript({ target: { tabId: newTab.id }, files: ['content.js'] }, () => { if (chrome.runtime.lastError) { console.error('Script injection failed:', chrome.runtime.lastError); return; } console.log('Content script injected'); triggerAction(newTab.id); // 传入 tabId 而非 tab 对象 }); } }); }); } }); chrome.runtime.onInstalled.addListener(() => { console.log('Extension installed or updated'); }); // 修改 triggerAction 为接收 tabId 参数 function triggerAction(tabId) { console.log('Triggering action number: ', actionStep); // 无需重复注入内容脚本,已在 tab 加载完成时注入过 chrome.tabs.sendMessage(tabId, { action: 'executeAction', actionStep: actionStep }, (response) => { if (chrome.runtime.lastError) { console.error('Message sending failed:', chrome.runtime.lastError); return; } console.log('Message sent to content script'); }); }
content.js
console.log('Content script loaded'); async function waitForSelector(selector) { console.log(`Waiting for selector: ${selector}`); while (document.querySelector(selector) === null) { await new Promise(resolve => setTimeout(resolve, 100)); } } async function performAction(action) { console.log('Performing action:', action); try { console.log(`Attempting to perform action: ${action.type} on selector: ${action.selector}`); switch (action.type) { case 'type': await waitForSelector(action.selector); const inputElement = document.querySelector(action.selector); inputElement.value = action.value; inputElement.dispatchEvent(new Event('input', { bubbles: true })); inputElement.dispatchEvent(new Event('change', { bubbles: true })); console.log('Type action performed.'); break; case 'click': await waitForSelector(action.selector); document.querySelector(action.selector).click(); console.log('Click action performed.'); break; case 'scroll': await waitForSelector(action.selector); document.querySelector(action.selector).scrollIntoView(); console.log('Scroll action performed.'); break; case 'delay': await new Promise(resolve => setTimeout(resolve, action.value)); console.log(`Delay for ${action.value}ms`); break; default: console.error(`Unsupported action type: ${action.type}`); break; } } catch (error) { console.error('Error performing action:', error); } } const actions = [ { type: 'type', selector: 'input#twotabsearchtextbox', value: 'The Pragmatic Programmer' }, { type: 'click', selector: 'input.nav-input[type=submit]' }, { type: 'delay', value: 5000 }, { type: 'click', selector: 'a.a-link-normal.s-underline-text.s-underline-link-text.s-link-style.a-text-normal' } ]; async function executeAction(actionStep) { console.log('Executing action:', actionStep); if (actionStep >= actions.length) { console.log('All actions completed'); return; } await performAction(actions[actionStep]); // 等待动作完成再发送消息 chrome.runtime.sendMessage({ action: 'actionCompleted' }); console.log('Action step completed, waiting for next'); } chrome.runtime.onMessage.addListener((message, _sender, _sendResponse) => { console.log('Received message'); if (message.action === 'executeAction') { console.log('Received message to execute action: ', message.actionStep); executeAction(message.actionStep); } });
额外优化点
- 移除重复的内容脚本注入,避免不必要的资源消耗
- 在
executeAction中添加动作边界检查,防止索引越界 - 等待
performAction完成后再发送actionCompleted消息,确保动作执行顺序正确 - 增加 API 调用的错误检查,便于调试问题
内容的提问来源于stack exchange,提问作者Anatole Martin
相关产品推荐
相关产品推荐

