I am in the process of transitioning a manifest v2 Chrome extension to manifest v3 and while converting my background script background.js to a service worker, and I am getting error messages in Google Chrome. I am somewhat familiar with Manifest v3, however the concept of service workers is still very confusing to me. Here is the error message. Note: Keep in mind, despite the single error message, I believe there may be some logical errors rather than syntax errors in my source code that may present undesired or unexpected behavior in the browser extension.
Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.
To put this error into context, I have included the code of my service worker called service-worker.js
, content-script.js
, and manifest.json
. I think I did most of the “heavy lifting” porting my MV2 extension to MV3, but there seems to be some minor issues.
service-worker.js
"use strict";
function sendAudioMessage(actionType, audioFile) {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
tabs.forEach(tab => {
chrome.tabs.sendMessage(tab.id, { action: actionType });
});
});
}
chrome.webNavigation.onCreatedNavigationTarget.addListener(onNav);
chrome.webNavigation.onBeforeNavigate.addListener(onNav);
chrome.webNavigation.onReferenceFragmentUpdated.addListener(onNav);
chrome.webNavigation.onHistoryStateUpdated.addListener(onNav);
function onNav({frameId}) {
if(frameId > 0) return;
sendAudioMessage('playNavigationSound');
}
chrome.downloads.onChanged.addListener(delta => {
if(delta.state && delta.state.current === "complete") {
sendAudioMessage('playDownloadCompleteSound');
}
if(delta.error && delta.error.current) {
sendAudioMessage('playDownloadErrorSound');
}
});
chrome.tabs.onUpdated.addListener((tabId, {mutedInfo}) => {
if(mutedInfo && mutedInfo.reason === "user") {
sendAudioMessage('playMuteWarningSound');
}
});
function changeVolume(volumeLevel) {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
tabs.forEach(tab => {
chrome.tabs.sendMessage(tab.id, { action: 'changeVolume', volume: volumeLevel });
});
});
}
// *** Message Handler ***
chrome.runtime.onMessage.addListener((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) {
const audio = new Audio(chrome.runtime.getURL(audioFile)); // Use chrome.runtime.getURL for packaged extension resources
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) => {
switch (message.action) {
case 'playNavigationSound':
playAudio('nav.ogg');
break;
case 'playDownloadCompleteSound':
playAudio('done.ogg');
break;
case 'playDownloadErrorSound':
playAudio('error.ogg');
break;
case 'playMuteWarningSound':
playAudio('unlock.ogg');
break;
case 'changeVolme':
setAudioVolume(0.75);
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",
"default_locale": "en",
"offline_enabled": true,
"icons": {
"32": "icon_32.png",
"96": "icon_96.png",
"128": "icon_128.png"
},
"background": {
"service_worker": "service-worker.js"
},
"permissions": [
"webNavigation",
"downloads",
"tabs"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"],
"run_at": "document_start"
}
],
"content_security_policy": {}
}
Edit: While I no longer get the error message when the extension runs, I now get two error messages when audio is expected to be played from the browser extension.
Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.
Uncaught (in promise) NotAllowedError: play() can only be initiated by a user gesture.
I hope this helps.