In this case, the error occurs due to the confusion of pages receiving and sending data.
To send data to the extension, we need to listen for an event to receive data in the file content.js (index.js in my case), and not send them. To get the data in the extension, we need to send the data to the landing page, and not listen to their reception.
It was logical to imagine it differently. We need to send data from the landing page to the extension - we send data from the landing page to the extension:
chrome.runtime.sendMessage()
The extension, on its part, listens to them and gets the result:
chrome.runtime.onMessage.addListener()
The problem is that everything happens exactly the opposite, and although the documentation says:
On the receiving end, you need to set up an runtime.onMessage event
listener to handle the message. This looks the same from a content
script or extension page.
because of this confusion, you are wondering - what is the receiving party?
To begin with, remove all the addition in the permissions field of the manifest file.json, since they won't be needed in these examples (they were added out of desperation) and rename index.js in content.js (it will be more informative this way):
{
"manifest_version": 3,
"name": "Name",
"version": "1.0",
"description": "Description",
"icons": {
"128": "128.png"
},
"action": {
"default_popup": "page.html"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"css": ["index.css"],
"run_at": "document_start"
}
],
"background": {
"service_worker": "background.js"
},
"permissions": []
}
Let's say we want to put the text we clicked on on the page in the extension. We need to use non-chrome.runtime.SendMessage in the content file.js, which is embedded on the landing page and receives DOM data, we should use chrome.runtime.onMessage, that is, listen to events:
content.js:
// we get the text of the element and save it in the local storage:
document.addEventListener('click', ({ target }) => {
localStorage.setItem('content', JSON.stringify(target?.textContent));
})
// listening to the response from the extension
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(request, sender, sendResponse);
// request - server response
// sender - extensions id and origin
// after the response from the extension is received, we send the data directly to extension
sendResponse(JSON.stringify({ content: localStorage.getItem('content') }));
});
background.js
// sending data to the current tab
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
console.log(document); // this document will be reference the DOM of your extension, an html file assigned as an action: { default_popup: page.html }
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
// response - this is our sendResponse(JSON.stringify({ content: localStorage.getItem('content') })); from the previous step. this is changing the content in the DOM extension
document.body.textContent = response;
// а {greeting: "hello"} - this is our request (response from server) from the previous step
});
});
Don't forget to connect the background.js to your page.html (html file of your extension)
In my case, you need to click on the extension icon and background.js trigger the SendMessage event and will send data from the extension to the landing page where content.js will listen for this event and forward the data back to the extension. And not the other way around, as it may seem.
Conversely, if we need to get data from an extension, we should not listen to them on the landing page, but send them:
content.js:
chrome.runtime.sendMessage('Data for extension', function (msg) {
console.log(msg); // data from extension
});
background.js:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(request, sender, sendResponse);
sendResponse(`From backend: ${request}`); // this will be sent to the landing page (content.js)
});
Keep in mind that in chrome.runtime.onMessage.addListener
from background.js file of the last example we'll be neither windows
nor document
. When you try to access then, you will get: Error in the event handler: Link error: Window is not defined. The extension has its own console. You need to click on the extension icon and right-click - explore. All the data that comes or logs in to the extension will be visible there.
I hate this API. I hope I was able to help someone with this post.
P.S. I didn't use polifill
P.S.S Docs, MDN