You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 06:00:05