Chrome扩展Manifest V3:Service Worker无法连接Content Script求助
Manifest V3扩展迁移:修复"无法建立连接,接收端不存在"错误
已将Manifest V2的Chrome扩展迁移至V3,调整了manifest文件结构、将background.js替换为service-worker.js,目录符合V3规范,但出现报错:
Error: Could not establish connection. Receiving end does not exist.
栈信息指向service-worker.js第14行,以下是相关代码:
service-worker.js
// Listen for the extension being reloaded or re-installed chrome.runtime.onInstalled.addListener(() => { // Query for all open tabs chrome.tabs.query({}, (tabs) => { tabs.forEach((tab) => { // Inject the content script into each open tab chrome.tabs.registerContentScripts(tab.id, { file: 'content-script.js' }); }); }); }); function sendAudioMessage(actionType, audioFile) { chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { tabs.forEach(async (tab) => { console.log(tab); if (tab.url.startsWith("chrome")) return false; try { await chrome.tabs.sendMessage(tab.id, { action: actionType }); } catch (e) { console.error(e); console.trace(); } }); }); } // *** Message Handler *** chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { console.log(message, sender, sendResponse); switch (message.action) { case 'playNavigationSound': sendAudioMessage('playNavigationSound'); break; case 'playDownloadCompleteSound': sendAudioMessage('playDownloadCompleteSound'); break; case 'playDownloadErrorSound': sendAudioMessage('playDownloadErrorSound'); break; case 'playMuteWarningSound': sendAudioMessage('playMuteWarningSound'); break; case 'changeVolume': changeVolume(0.75); break; default: console.log('Unknown message action:', message.action); } });
content-script.js
// Helper function to play audio function playAudio(audioFile) { console.log(audioFile, chrome.runtime.getURL(audioFile)); const audio = new Audio(chrome.runtime.getURL(audioFile)); // Play the audio only when the EventListener for click is triggered audio.addEventListener('click', () => { audio.play(); }); } // Helper function for setting volume function setAudioVolume(volumeLevel) { const audioElements = document.querySelectorAll('audio'); audioElements.forEach(audio => { audio.volume = volumeLevel; }); } // Receive messages from your service worker chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { console.log(message, sender, sendResponse); switch (message.action) { case 'playNavigationSound': playAudio('nav.ogg'); break; case 'playDownloadCompleteSound': playAudio('complete.ogg'); break; case 'playDownloadErrorSound': playAudio('error.ogg'); break; case 'playMuteWarningSound': playAudio('unlock.ogg'); break; case 'changeVolume': setAudioVolume(message.volume); break; default: console.log('Unknown message action:', message.action); } });
manifest.json
{ "manifest_version": 3, "name": "PopupSound", "description": "Plays a click sound when you click on a link. Also plays a trumpet sound when you finish downloading a file.", "author": "Michael Gunter", "version": "3.0", "offline_enabled": true, "default_locale": "en", "icons": { "46": "icon_46.png", "96": "icon_96.png", "128": "icon_128.png" }, "background": { "service_worker": "service-worker.js" }, "content_scripts": [{ "matches": ["<all_urls>"], "js": ["content-script.js"], "run_at": "document_end" }], "web_accessible_resources": [{ "resources": [ "*.ogg" ], "matches": [ "<all_urls>" ], "extensions": [] }], "permissions": [ "webNavigation", "downloads", "tabs", "activeTab" ], "host_permissions": ["<all_urls>"], "content_security_policy": {} }
问题分析与修复方案
1. 修复内容脚本注入逻辑(核心问题)
service-worker.js中chrome.tabs.registerContentScripts用法错误,该API用于注册静态内容脚本规则,而非向已打开标签页注入脚本。正确做法是使用chrome.scripting.executeScript,同时需要在manifest中添加scripting权限:
修改manifest.json的
permissions数组:"permissions": [ "webNavigation", "downloads", "tabs", "activeTab", "scripting" ]替换service-worker.js中
onInstalled的逻辑:chrome.runtime.onInstalled.addListener(async () => { const tabs = await chrome.tabs.query({}); for (const tab of tabs) { if (tab.url.startsWith("chrome")) continue; try { await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['content-script.js'] }); } catch (e) { console.log(`无法注入标签页 ${tab.id}:`, e); } } });
2. 完善sendAudioMessage函数的异步处理
原函数混合了回调和async/await,调整为统一的异步写法,避免逻辑混乱:
async function sendAudioMessage(actionType) { const tabs = await chrome.tabs.query({ active: true, currentWindow: true }); for (const tab of tabs) { if (tab.url.startsWith("chrome")) continue; try { await chrome.tabs.sendMessage(tab.id, { action: actionType }); } catch (e) { console.error(`发送消息到标签页 ${tab.id} 失败:`, e); } } }
3. 修复playAudio函数的播放逻辑
原函数中给audio元素绑定click事件,但消息触发时无需等待点击,直接播放即可:
function playAudio(audioFile) { console.log(audioFile, chrome.runtime.getURL(audioFile)); const audio = new Audio(chrome.runtime.getURL(audioFile)); audio.play().catch(e => console.error('播放音频失败:', e)); }
4. 修复changeVolume未定义问题
service-worker.js中changeVolume函数未实现,补充实现逻辑:
function changeVolume(level) { chrome.tabs.query({}, async (tabs) => { for (const tab of tabs) { if (tab.url.startsWith("chrome")) continue; try { await chrome.tabs.sendMessage(tab.id, { action: 'changeVolume', volume: level }); } catch (e) { console.log(`调整标签页 ${tab.id} 音量失败:`, e); } } }); }
内容的提问来源于stack exchange,提问作者Michael Gunter
相关产品推荐
相关产品推荐

