Search code examples
javascriptgoogle-chrome-extensionfirefox-addon-webextensionsweb-extension

My service worker is not connecting to my content script in my Chrome extension. How do I fix this?


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.


Solution

  • 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");
    });