Chrome Manifest V3扩展content.js中chrome.offscreen未定义问题求助
Chrome扩展Manifest V3中content.js无法访问chrome.offscreen的解决方法
问题重现
开发Chrome扩展时,尝试在content.js中调用chrome.offscreen.createDocument播放音频,控制台抛出错误:
Uncaught TypeError: Cannot read properties of undefined (reading 'createDocument') at checkForVideoElement (content.js:28:30) at MutationObserver.<anonymous> (VM304 content.js:7:21)
排查后确认chrome.offscreen对象未定义,即使已在manifest.json中声明offscreen权限。
核心原因
Content Script运行在网页的隔离上下文里,Manifest V3明确规定:chrome.offscreen API仅支持在Service Worker(background)、Popup、Options页面这类扩展自身的上下文环境中调用,Content Script没有权限访问该API。
解决方案
调整逻辑,通过"Content Script → Background Service Worker → Offscreen页面"的消息传递流程实现音频播放:
- Content.js检测到视频元素后,向Background发送播放请求
- Background负责创建Offscreen文档,并向Offscreen发送播放指令
- Offscreen页面执行音频播放逻辑
修改后的代码
1. content.js(修改消息发送逻辑)
// Try to find the video element immediately if (!checkForVideoElement()) { // If not found, use a mutation observer to watch for changes const observer = new MutationObserver((mutationsList, observer) => { for (const mutation of mutationsList) { if (mutation.type === 'childList') { if (checkForVideoElement()) { // Stop observing once the video element is found observer.disconnect(); break; } } } }); // Start observing the document body for added nodes observer.observe(document.body, { childList: true, subtree: true }); } function checkForVideoElement() { var video = document.querySelector("video"); if (video) { console.log(video); if (video.currentTime <= 60) { console.log("Video found less than 60s"); // 改为向Background发送播放请求 chrome.runtime.sendMessage({action: "requestPlaySound"}); } return true; } return false; }
2. background.js(新增Offscreen创建与消息转发逻辑)
// 监听来自Content Script的消息 chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => { if (message.action === "requestPlaySound") { await createOffscreenDocument(); // 向Offscreen页面发送播放指令 chrome.runtime.sendMessage({action: "playSound"}); } }); // 封装Offscreen文档创建逻辑,避免重复创建 async function createOffscreenDocument() { if (await chrome.offscreen.hasDocument()) { return; } await chrome.offscreen.createDocument({ url: chrome.runtime.getURL("offscreen.html"), reasons: ["AUDIO_PLAYBACK"], justification: "Playing Custom Sound for Netflix video" }); }
3. offscreen.js(保持原有逻辑即可)
console.log("Offscreen script loaded"); chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { console.log("Offscreen script received message:", message); if (message.action === "playSound") { playSound(); } }); function playSound() { chrome.storage.local.get("Sound", function (result) { let audio = result.Sound; if (audio) { console.log("Audio found"); // Convert the data URI to a Blob fetch(audio) .then(response => response.blob()) .then(blob => { const audioUrl = URL.createObjectURL(blob); const audioElement = new Audio(audioUrl); audioElement.play().then(() => { console.log("Audio played successfully"); }).catch(error => { console.error("Error playing audio:", error); }); }) .catch(error => { console.error("Error converting audio data:", error); }); } else { console.log("Audio not found"); } }); }
注意事项
- 确保offscreen.html的路径正确,无需在manifest中额外声明,只要能通过
chrome.runtime.getURL访问即可 - 修改代码后需重新加载扩展,确保manifest权限和脚本变更生效
- 重复创建Offscreen文档会报错,因此在Background中加入
hasDocument检查
内容的提问来源于stack exchange,提问作者Rajveer Sodhi
相关产品推荐
相关产品推荐

