I am trying to create a Google Chrome extension web scraper, and the idea is a user clicks on the icon and the extension scrapes the current active tab and prints some processed data onto a popup.

To do this, I decided to set in my manifest a default popup.html, and have the popup.js sendMessage to a content.js file, which contains the function that scrapes the webpage. However, during my implementation, I received the following error on popup.html:0 -> Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.

I tried looking up my exact issue online, and I found an already existing stack overflow page: Chrome Extension message passing: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist

However, I tried implementing this and the error still persists. I've attached my manifest.JSON, popup.html, popup.js, content.js, and background.js files below:

Manifest.JSON

{
    "manifest_version": 3,
    "name": "HTML Scraper",
    "version": "1.0",
    "description": "Scrapes the HTML of the current webpage and prints it to console.",
    "permissions": ["storage", "activeTab", "scripting"],
    "background": {
      "service_worker": "background.js",
      "type": "module"
    },
    "action": {
      "default_popup": "popup.html",
      "default_icon": "icons/icon16.png"
    },
    "icons": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  }  

popup.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Popup</title>
    <!-- Include popup.js -->
    <script src="popup.js"></script>
</head>
<body>
    <h1>Hi!</h1>
</body>
</html>

popup.js

if (document.readyState != 'loading') {
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
        chrome.tabs.sendMessage(tabs[0].id, {action: 'scrapeHTML'}, function(response) {
            console.log('Scrape command sent to content script');
        });
    });
} else {
    document.addEventListener('DOMContentLoaded', function() {
        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
            chrome.tabs.sendMessage(tabs[0].id, {action: 'scrapeHTML'}, function(response) {
                console.log('Scrape command sent to content script');
            });
            console.log("tester");
        });
    });
}

content.js

function ScrapeHTML() {
    const htmlcontent = document.body.innerText;

    const url = document.location.href;
    
    const containsAmazon = url.includes("amazon.com/");

    chrome.runtime.sendMessage({action: 'sentLoad'},{
        html: htmlcontent,
        containsAmazon: containsAmazon
    });
}

document.addEventListener('DOMContentLoaded', function() {
    chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
        if (message.action == 'scrapeHTML') {
            ScrapeHTML();
        } else {
            console.log('Unknown message received from popup script:', message);
        }
    });
});

background.js

// background.js
import {amazonMessage} from './helpers.js';

  chrome.runtime.onMessage.addListener((message) => {
    if (message.action === 'sentLoad') {
        const containsAmazon = message.containsAmazon;
        const text = message.html;

        if (containsAmazon) {
            console.log(amazonMessage(text, "Sponsored", "Products related to this item"));
        } else {
            console.log(text);
        }

        chrome.storage.local.set({counter: text}, function() {
            console.log("Message saved to local storage.");
        });
    }
});
1

There are 1 best solutions below

0
wOxxOm On

You need to declare content_scripts in manifest.json, then re-inject content_scripts after reloading/installing the extension in Chrome, but a much simpler solution is to use programmatic injection via chrome.scripting.executeScript instead of content_scripts+messaging, see these examples.

popup.html: add defer to load the script at DOMContentLoaded:

<script src="popup.js" defer></script>

popup.js:

(async () => {
  const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
  const [{result}] = await chrome.scripting.executeScript({
    target: {tabId: tab.id},
    func: () => document.body.innerText,
  });
  document.body.textContent = new URL(tab.url).hostname + ': ' +
    result.slice(0, 100) + '...';
})();

No need for the background script or a separate content script for such simple scraping.