Chrome Extension "Receiving end does not exist." Error - javascript

I'm working on a Chrome Extension but lately I've noticed I've been getting the following error (pointing to the first line of popup.html):
Unchecked runtime.lastError: Could not establish connection. Receiving
end does not exist.
I've found a similar question here. But the error there is caused by the background property which I haven't declared on my manifest.
I'm using chrome.extension.onMessage.addListener on the contents.js script to listen for events and chrome.tabs.sendMessage on the popup.js script to send the events. Most of the time everything works fine, but sometimes I get the above error and none of the requests do anything.
The manifest.json is of the following format:
{
"manifest_version": 2,
"name": "APP_NAME",
"description": "APP_DESCRIPTION",
"version": "APP_VERSION",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"storage",
"clipboardRead",
"clipboardWrite"
],
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"content.js"
],
"css": [
"content.css"
]
}
]
}
Message Listener Example:
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if (request.action === "this") console.log({
dom: doThis()
});
if (request.action === "that") sendResponse({
dom: doThat()
});
else if (request.action === "other") doOther();
else sendResponse({});
});
Message Sender Example:
function getSelectedTab() {
return new Promise(function(resolve) {
chrome.tabs.getSelected(null, resolve);
});
}
function sendRequest(data) {
data = data || {
action: undefined
};
return new Promise(function(resolve) {
getSelectedTab().then(function(tab) {
chrome.tabs.sendMessage(tab.id, data, resolve);
});
});
}
Send Request Invocation Example:
document.querySelector("#this").addEventListener("click", function() {
sendRequest({
action: "this"
}).then(function(res) {
console.log(res);
});
});
document.querySelector("#that").addEventListener("hover", function() {
sendRequest({
action: "that"
});
});
addEventListener("blur", function() {
sendRequest({
action: "other"
});
});

I'm not sure if my answer good for given case, but if you reading it, you faced this kind of problem, and probably my answer will help you.
I spent a lot of time, trying to understand why it sometimes throws this error, while I'm working at dev version, and doesn't do it for released version of my extension. Then I understood, that after every code save, it updates at chrome, and creates new content version of script. So if you don't reload page, where you used previous version of your code to create context.js and trying it again with updated version, it throws this error.
I kinda wasted about one full day to figure it out, it's simply, but there a lot of answers in stackoverflow about this case, so you used to try them, and not think with your brain. Don't be like me:)

Related

Host permission issue in a created window - Chrome Extension [duplicate]

I am struggling to get this simple f-ty working... My scenario is:
get current URL
modify it
navigate/redirect to it
execute custom JS code there
The most problems I have is with 4)
manifest.json
{
"name": "Hello, World!",
"description": "Navigate and execute custom js script",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"tabs",
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {}
}
background.js
function myCustomScript() {
alert('myCustomScript test ok!');
console.log('myCustomScript test ok!');
}
chrome.action.onClicked.addListener((tab) => {
chrome.tabs.update({url: "https://example.com"}, myCustomScript);
});
The page got redirected but my js function is not executed! Do you know why and how to fix it?
P.S: this is my first time I am creating my chrome extension, maybe I am doing something wrong...
To execute custom code, use chrome.scripting API. For this scenario you'll need:
"scripting" added to "permissions", which you already have,
"https://example.com/" added to "host_permissions" in manifest.json.
Note that activeTab permission won't apply to the tab after it's navigated to a URL with a different origin because this permission only applies to the currently shown origin.
Due to a bug in Chrome, you need to wait for the URL to be set before executing the script.
The bug is fixed in Chrome 100.
chrome.action.onClicked.addListener(async tab => {
await chrome.tabs.update(tab.id, {url: "https://example.com"});
// Creating a tab needs the same workaround
// tab = await chrome.tabs.create({url: "https://example.com"});
await onTabUrlUpdated(tab.id);
const results = await chrome.scripting.executeScript({
target: {tabId: tab.id},
files: ['content.js'],
});
// do something with results
});
function onTabUrlUpdated(tabId) {
return new Promise((resolve, reject) => {
const onUpdated = (id, info) => id === tabId && info.url && done(true);
const onRemoved = id => id === tabId && done(false);
chrome.tabs.onUpdated.addListener(onUpdated);
chrome.tabs.onRemoved.addListener(onRemoved);
function done(ok) {
chrome.tabs.onUpdated.removeListener(onUpdated);
chrome.tabs.onRemoved.removeListener(onRemoved);
(ok ? resolve : reject)();
}
});
}
P.S. alert can't be used in a service worker. Instead, you should look at devtools console of the background script or use chrome.notifications API.

chrome.tabs.onUpdated.addListener won't send a message if I check the value of changeInfo.url

I want to send a message to content.js from background.js when the user navigates on the page or reloads. Therefore I am using chrome.tabs.onUpdated.addListener function.
To check if my user is navigating on my page I use changeInfo.url and check if he is on that page. The problem is that whenever I use it in an if statement, the sendMessage function is not called and therefore, the listener in the content.js is not calling the function to update the DOM (updateTweets).
background.js
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
chrome.storage.sync.get(["nickname", "stringInNickname", "username", "keyWords"], function (data) {
if (changeInfo.url !== undefined && changeInfo.url.includes("somewebsite.com/page")) {
chrome.tabs.sendMessage(tabId, data);
}
});
});
content.js
function setEventListeners() {
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
data.unwantedNicknames = request.nickname.split(",").filter((val) => val != "");
data.unwantedStringInNickname = request.stringInNickname.split(",").filter((val) => val != "");
data.unwantedUsernames = request.username.split(",").filter((val) => val != "");
data.unwantedKeyWords = request.keyWords.split(",").filter((val) => val != "");
updateTweets();
});
}
manifest.js
{
"manifest_version": 2,
"name": "extension",
"version": "1.0",
"content_scripts": [
{
"matches": ["*://*.somewebsite/page/*"],
"js": ["./content.js"]
}
],
"permissions": ["storage", "tabs"],
"browser_action": {
"default_icon": "popup16.png",
"default_popup": "panel/panel.html",
"default_title": "My popup!"
},
"background": {
"scripts": ["background.js"]
}
}
I tried working around the problem and setting a timeout function for both updateTweets() and sendMessage() and it worked. I don't understand why the sendMessage is not being called. Previously it worked without any if statements, I even tried with changeInfo.status === "complete" and it worked. Does it have to do something with the callbacks?
Even if I just do if(changeInfo.url), something that I saw in another stack questions, the sendMessage is not called. But if I have some conosle logs in the if statement where sendMessage is they get logged in the console but sendMessage isn't called.
Also, chageInfo doesn't always return url in the object, maybe that's the problem, but I use the if statement to check if it is defined.
I could solve it with changeInfo.status and make a few unneeded calls of the updateTweets function, but I wan't to make it as right as possible. I'm pretty much struggling with this one the whole day.
I don't know why it is not working as expected
For me, setting a timeout time from 1 to 1000 works. This is very strange.
I check the value of changeInfo.status === "complete."
If the status is completed, then I use chrome.tabs.sendMessage() to send a message to contentScript.js.
This won't work unless I wrap the chrome.tabs.sendMessage() code inside setTimeout(() => {}, time) just like your only working solution.

Chrome Extension strange behaviour of onMessage

I have the following code in my background.js:
chrome.webRequest.onHeadersReceived.addListener(function(details){
if(isfileTypeXYZ(details))
{
chrome.tabs.sendMessage(details.tabId, {isFileXYZ: true});
return { //Stop rendering of frame...
responseHeaders: [{
name: 'X-Content-Type-Options',
value: 'nosniff'
}, {
name: 'X-Frame-Options',
value: 'deny'
}]
};
}
}, {
urls: ['*://*/*'],
types: ['main_frame']
}, ['blocking', 'responseHeaders']);
And in my contentscript I have the following code:
var toLoadXYZ = 0;
chrome.runtime.onMessage.addListener(function(msg, _, sendMessage){
if(msg.isFileXYZ)
{
toLoadXYZ = 1;
}
});
$(document).ready(function(){
alert(toLoadXYZ);
});
What I want to do is to detect if a particular file type is being opened and then load an image from a server running on the system itself and display it. I will have to load the image using xhr but I need to get this detection thing working well first. There are two problems that I am facing:
The onMessage is not triggered when the url is first loaded - it is after that (refreshing using F5).
The value shown in the alert(toLoadXYZ) is 0 not 1 even though the debugger shows that the onMessage is triggered (after first load that is - after first load i am refreshing the page using F5).
Here is the manifest file if you want to refer:
{
"manifest_version": 2,
"name": "my Extension",
"version": "1.0",
"background": {
"scripts": ["background.js"],
"persistent": true
},
"permissions": [
"webRequest",
"<all_urls>",
"webRequestBlocking",
"tabs",
"webNavigation"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["jquery-2.1.4.min","renderXYZ.js"]
}
],
"web_accessible_resources": [ "http:/*", "https:/*", "ftp:/*", "file:/*", "chrome-extension:/*"]
}
What is the cause for this problem? How to fix it? I searched a lot didn't get anything useful please help!!
UPDATE
function findContentType(responseHeaders)
{
for(var i = 0; i < responseHeaders.length; i++)
{
var header = responseHeaders[i];
if(header.name.toLowerCase() === "content-type")
return header.value.toLowerCase();
}
return "";
}
function isfileTypeXYZ(details)
{
var contentType = findContentType(details.responseHeaders);
if(contentType === "some-mime-type-here")
{
return true;
}
return false;
}
chrome.runtime.onMessage is beeing called, but it's being called on the content script of the previous request. Since you're refreshing the page, tabId doesn't change so you send the message to the content script of the previous page, right before the new body gets loaded, that's why you're seeing this behaviour. Maybe clearer like this:
First URL call.
onHeadersReceived gets called
You call chrome.tabs.sendMessage but it doesn't trigger anything since the content script is not loaded yet.
The tab loads the body of the request and the content scripts. Document ready gets called.
You refresh the tab. onHeadersReceived gets called for this new request but the contents of the tab are STILL the ones of the previous request. This is where you see your alert and why the toLoadXYZ is 0.

Message callback returns a value infrequently - Chrome Extension

I'm building a chrome extension which communicates with a nodejs server through websockets. The point of it is to track browsing history with content. It all seems to work, but occasionally (30% of the time) the callback in a function passed to onMessage.addListener doesn't fire correctly. Let me show you the code:
background.js
var socket = io('http://localhost:3000/');
var tabLoad = function (tab) {
socket.emit('page load', tab);
};
var tabUpdate = function (tabid, changeinfo, tab) {
var url = tab.url;
if (url !== undefined && changeinfo.status == "complete") {
tab.user_agent = navigator.userAgent;
tab.description = '';
tab.content = '';
socket.emit('insert', tab);
}
};
socket.on('inserted', function(page){
socket.emit('event', 'Requesting page content\n');
//page = {tab: page, id: docs._id};
chrome.tabs.sendMessage(page.tab_id, {requested: "content", page: page}, function(data) {
socket.emit('content', data);
});
});
try {
chrome.tabs.onCreated.addListener(tabLoad);
chrome.tabs.onUpdated.addListener(tabUpdate);
} catch(e) {
alert('Error in background.js: ' + e.message);
}
content script - public.js
var messageHandler = function(request, sender, sendContent) {
if (request.requested == "content") {
var html = document.getElementsByTagName('html')[0].innerHTML;
var data = {
content: html,
page: request.page
};
sendContent(data);
return true;
}
};
chrome.extension.onMessage.addListener(messageHandler);
The problem is that sometimes data in sendContent is undefined, while sometimes it is alright. Any ideas how to debug this or what i'm doing wrong?
I've tried replacing document.getElementsByTagName('html')[0].innerHTML with a hardcoded 'test' string, but that didn't help.
Pages like youtube/wikipedia seem to never work, while facebook/google works.
Edit: The sendContent callback does fire 100% of the time it's just that the data passed to it is undefined.
Edit: Here's the manifest file
{
"manifest_version": 2,
"name": "Socket test",
"description": "sockets are cool",
"version": "1.0",
"permissions": [
"http://st-api.localhost/",
"http://localhost:3000/",
"tabs",
"background",
"history",
"idle",
"notifications"
],
"content_scripts": [{
"matches": ["*://*/"],
"js": ["public/public.js"]
//"run_at": "document_start"
}],
//"browser_action": {
// "default_icon": "logo.png",
// "default_popup": "index.html"
//},
"background": {
//"page" : "background.html",
"scripts": ["socket-io.js", "background.js"],
"persistent": true
}
}
First off, your understanding that sendContent is executed 100% of the time is wrong.
As established in the comments, the sendMessage callback also gets executed when there was an error; and this error is, in your case, "Receiving end does not exist"
The error lies in your manifest declaration of the content script. A match pattern "*://*/" will only match top-level pages on http and https URIs. I.e. http://example.com/ will match, while http://example.com/test will not.
The easiest fix is "*://*/*", but I would recommend the universal match pattern "<all_urls>".
With that fixed, there are still a couple of improvements to your code.
Replace chrome.extension.onMessage (which is deprecated) and use chrome.runtime.onMessage
Modify the sendMessage part to be more resilient, by checking for chrome.runtime.lastError. Despite the wide permission, Chrome still won't inject any content scripts into some pages (e.g. chrome:// pages, Chrome Web Store)
Make sure you use "run_at" : "document_start" in your content script, to make sure onUpdated with "complete" is not fired before your script is ready.

Content Script and Background Communication

I am looking to create an extension for a particular site to provide additional formatting and sharing options that they don't currently have.
I am having issues getting things to communicate properly and there doesn't seem to be a clearly laid out example.
Manifest:
{
"name": "Test",
"description": "Testing.",
"version": "1.0",
"background_page": "background.html",
"permissions": [
"tabs", "http://www.sitedomain.com/*"
],
"content_scripts": [
{
"matches": ["*://*.sitedomain.com/*"],
"js": ["jquery.min.js", "test.js"],
"css": ["test.css"]
}
]
}
Content Script:
$(document).ready(function () {
alert('test js fired');
$("#ColumnContainer div.item").each(function () {
$(this).css("background-color", "skyBlue");
var itemId = $(this).children("a.itemImage").attr("href");
$(this).children(".details").append("Goto Item");
});
});
chrome.extension.onRequest.addListener(function (request, sender, sendResponse) {
alert('listener request');
alert(request);
});
JavaScript of Background HTML:
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status == "complete") {
if (tab.url.indexOf("sitedomain.com") > -1) {
chrome.tabs.executeScript(null, {file: "test.js"});
}
}
});
chrome.tabs.sendRequest(tabId, request, responseCallback);
function responseCallback() {
alert('response callback');
}
function gotoItem(itemId) {
alert('goto Item - ' + itemId);
}
The above code does append the link and change the styling on the client page when the sitedomain.com is loaded. However, I haven't had any luck getting the gotoItem method to fire, Chrome Dev Tools shows undefined. I have tried various combinations, but just can't quite grasp the listeners and requests yet.
I would really like to see a clean sample that just shows how to call a method from each site.
I see two issues with your code. 1) the gotoItem function is defined in the background page and content_scripts can't access functions there. 2) content_scripts and javascript on pages they are injected into can not interact so your onclick can't be part of the links html.
To fix #1 is as simple as moving the gotoItem function to be in the content_script.
To fix #2 something like the following should work.
$("#ColumnContainer div.item").each(function(){
$(this).css("background-color","skyBlue");
var itemId = $(this).children("a.itemImage").attr("href");
var $link = $('Goto Item');
$link.click(function() {
gotoItem(itemId);
}
$(this).children(".details").append($link);
});
You may have to modify how itemId gets passed.

Categories