I have been working on porting this manifest v2 Chrome extension to manifest V3 and while I did port the extension through changing the structure in the manifest file and replacing the background.js
with service-worker.js
. The structure of the directory of the Chrome extension seems acceptable and should work in manifest v3, but I have a persistent problem that I cannot get around. I get an error message that states…
"Error: Could not establish connection. Receiving end does not exist."
"Stack Trace"
"service-worker.js:14 (anonymous function)"
Here is my service-worker.js
code to put it into context. Note: source code is licensed under the Mozilla Public License, version 2.
// 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);
}
});
Also, here is my content-script.js
code for context.
// 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);
}
});
Lastly, here is my manifest.json
file.
{
"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": {}
}
I am so close to having the program work without any errors, but this issue is getting me stumped. I would greatly appreciate your help. Please and thank you.
I tried debugging with the Chrome DevTools, but I wasn't able to resolve my issue.
Here's one way to establish message passing to all tabs where the URL does not beging with "chrome"
.
We wait for a message from the content script before replying.
content-script.js
// Receive messages from your service worker
console.log("Content script");
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log(message);
});
chrome.runtime.sendMessage({ url: location.href, title: document.title });
Here we reload all tabs that do not begin with "chrome"
when we reload the extension.
service-worker.js
chrome.runtime.onInstalled.addListener(async (reason) => {
console.log(reason);
const tabs = await chrome.tabs.query({});
for (const tab of tabs) {
if (tab.url.startsWith("chrome")) {
continue;
}
await chrome.tabs.reload(tab.id);
}
});
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
// console.log("onUpdated", tabId, changeInfo, tab);
});
chrome.tabs.onCreated.addListener((tab) => {
// console.log("onCreated", tab);
});
chrome.runtime.onMessage.addListener(async (message, sender) => {
console.log(message);
await chrome.tabs.sendMessage(sender.tab.id, "Message from ServiceWorker");
});