Chrome扩展使用executeScript注入脚本时出现document未定义错误
Chrome扩展注入脚本报错:ReferenceError: document is not defined
问题场景
开发需验证用户付费状态的Chrome扩展,后台脚本通过ExtensionPay API完成验证后,调用chrome.scripting.executeScript向标签页注入脚本,触发以下错误:
Error handling response: ReferenceError: document is not defined
相关代码
background.js
importScripts("ExtPay.js", "script.js", "monitorNetflix.js"); const extpay = ExtPay("censor-bad-words-on-netflix") extpay.startBackground(); const millisecondsInOneDay = 86400000; const authenticateUser = () => { chrome.storage.sync.set({authenticated: true}); chrome.tabs.query({}, function(tabs) { for (var i = 0; i < tabs.length; i++) { var tabId = tabs[i].id; chrome.scripting.executeScript({ target: {tabId: tabId}, func: script }, _=>{ let e = chrome.runtime.lastError; if(e !== undefined){ console.log(tabId, _, e); } }); chrome.scripting.executeScript({ target: {tabId: tabId}, func: monitorNetflix }, _=>{ let e = chrome.runtime.lastError; if(e !== undefined){ console.log(tabId, _, e); } }); } }); }; extpay.getUser().then(user => { if (user.paid) { console.log("用户付费时间: ", user.paidAt) console.log("已启用功能"); authenticateUser(); } else { if (!user.trialStartedAt) { console.log("启动3天试用"); extpay.openTrialPage("3-day"); } else if (new Date().getTime() - user.trialStartedAt.getTime() > millisecondsInOneDay * 0.01) { console.log("试用已过期"); extpay.openPaymentPage().catch((rej) => { console.log(rej, "网络出错"); }); } else { console.log("试用进行中"); } } }).catch((rej) => { console.log(rej, "网络出错"); }); extpay.onPaid.addListener(user => { console.log("用户完成付费"); authenticateUser(); }); extpay.onTrialStarted.addListener(user => { console.log("用户启动试用"); authenticateUser(); });
manifest.json
{ "manifest_version": 3, "name": "AppName", "description": "", "version": "0.1.0", "permissions": [ "storage", "scripting", "tabs" ], "background": { "service_worker": "background.js" }, "host_permissions": ["https://*/*"], "content_scripts": [ { "matches": ["<all_urls>"], "js": ["script.js"] }, { "matches": ["https://www.netflix.com/*"], "js": ["script.js", "monitorNetflix.js"] }, { "matches": ["https://extensionpay.com/*"], "js": ["ExtPay.js"], "run_at": "document_start" } ], "action": { "default_icon": "icon.png" }, "icons": { "128": "icon.png" } }
script.js
chrome.storage.sync.get(["authenticated"], function(result) { console.log('当前认证状态: ' + result.authenticated); if (result.authenticated) { monitorNetflix(); } }); const monitorNetflix = (function () { // 依赖document的逻辑... }
错误原因
- 后台Service Worker无DOM环境:
background.js通过importScripts导入了script.js和monitorNetflix.js,而后者包含依赖document的代码。Service Worker运行在无浏览器DOM的环境中,加载时直接触发document未定义的错误。 - 注入函数方式错误:
chrome.scripting.executeScript的func参数要求传入可序列化的独立函数,但你传入的script并不是一个定义好的函数(script.js是直接执行的逻辑),且后台环境中的monitorNetflix变量无法在页面上下文正常运行。 - 重复注入冲突:
manifest.json已配置内容脚本自动注入,后台手动注入会导致逻辑重复,引发上下文混乱。
修复方案
1. 移除后台对DOM脚本的导入
修改background.js,仅保留适配后台环境的ExtensionPay脚本:
importScripts("ExtPay.js"); const extpay = ExtPay("censor-bad-words-on-netflix") extpay.startBackground(); const millisecondsInOneDay = 86400000; const authenticateUser = () => { chrome.storage.sync.set({authenticated: true}); // 无需手动注入,由内容脚本读取storage状态执行逻辑 }; // 后续extpay相关逻辑保持不变
2. 调整内容脚本逻辑
修改script.js,通过监听storage状态触发逻辑,避免重复执行:
// 监听认证状态变化 chrome.storage.sync.onChanged.addListener((changes, areaName) => { if (areaName === 'sync' && changes.authenticated?.newValue) { runMonitor(); } }); // 初始化检查状态 chrome.storage.sync.get(["authenticated"], function(result) { console.log('当前认证状态: ' + result.authenticated); if (result.authenticated) { runMonitor(); } }); // 封装执行逻辑,避免重复 function runMonitor() { if (window._monitorRan) return; window._monitorRan = true; monitorNetflix(); }
3. 修正内容脚本配置
确保manifest.json中内容脚本在DOM就绪后执行:
{ // ...其他配置不变 "content_scripts": [ { "matches": ["<all_urls>"], "js": ["script.js"], "run_at": "document_idle" }, { "matches": ["https://www.netflix.com/*"], "js": ["script.js", "monitorNetflix.js"], "run_at": "document_idle" }, { "matches": ["https://extensionpay.com/*"], "js": ["ExtPay.js"], "run_at": "document_start" } ] }
4. 规范monitorNetflix.js定义
确保函数在页面上下文可正常执行:
const monitorNetflix = function() { // 这里编写依赖document的DOM操作逻辑 console.log("执行Netflix监控", document); };
关键提示
- Service Worker(后台脚本)永远不能访问
document、window等浏览器DOM对象,所有页面相关逻辑必须放在内容脚本或注入的页面脚本中。 - 若需手动注入脚本,优先使用
chrome.scripting.executeScript的files参数传入脚本文件,而非func参数传递后台函数。 - 尽量通过
chrome.storage传递状态,让内容脚本自行判断执行时机,这是Chrome扩展的标准架构模式。
内容的提问来源于stack exchange,提问作者user19164383
相关产品推荐
相关产品推荐

