This question already has answers here:
Chrome Extension Message passing: response not sent
(3 answers)
Closed 5 years ago.
The issue that I'm facing is quite interesting. I've set up the communication between my background and my popup, it works perfectly fine, until I'm trying to reach the data in the chrome.tabs.query callback.
Could you please advise what could have went wrong?
background.js
chrome.extension.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.greeting === 'getTabs') {
chrome.tabs.query({},function(tabs){
sendResponse(tabs);
});
}
if (request.greeting === 'test') {
sendResponse('test-string');
}
});
popup.js
$(document).ready(function(){
$('button').on('click', function(){
chrome.extension.sendMessage({greeting: 'getTabs'}, function(response){
console.log(response);
});
chrome.extension.sendMessage({greeting: 'test'}, function(response){
console.log(response);
});
});
});
I debugged the background.js part, and I get the arra of tabs, but for some reason it's not getting sent as the response, even though the sendResponse(tabs) is in the callback function. Further addition, if I set a static string, like 'apple', it will not send it either.
I found the answer to my question eventually, although I can't quite comprehend, why did it solve the problem.
background.js
chrome.extension.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.greeting === 'getTabs') {
chrome.tabs.query({},function(tabs){
sendResponse(tabs);
});
}
if (request.greeting === 'test') {
sendResponse('test-string');
}
return true;
});
I had to return true, else the port in the chrome messaging.js was null.
Related
I am finding the 'Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received' error in the background.js. My chrome extension works but the dev tools displays a lot of these errors on background.js
I recently migrated my chrome extension from Manifest V2 to Manifest V3 and since then I am finding the error.
This is the place from where the error seems to be arising :
const browser = chrome || browser;
// Listen for messages from content script
browser.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
var frameId = sender.frameId || 0;
if (request.type == "connect") {
connect(sender.tab, frameId);
sendResponse({ success: true });
} else if (request.type == "disconnect") {
disconnect(sender.tab, frameId);
sendResponse({ success: true });
} else if (request.type == "send") {
sendNativeMessage(request.data, sender.tab);
document.addEventListener('nativeMessage',
function handleNativeMessage(e) {
sendResponse(e.detail);
document.removeEventListener(e.type, handleNativeMessage);
}, false);
sendResponse({});
return true;
}
});
The sendresponse is processed in ContentScript :
const browser = chrome || browser;
self.addEventListener("message", function(event){
var request = event.data;
if(request != null && request.type == "SendMessage")
{
ProcessNativeMessage(request.data);
}
});
function ProcessNativeMessage(nativeMessageData) {
var request = new Object();
request.data = nativeMessageData;
browser.runtime.sendMessage(request,handleExtensionResponse);
}
function handleExtensionResponse(value)
{
//alert(value);
};
I played around by returning true, returning false, returning undefined, sending empty response and even disabling the other extensions but none of these changes worked.
I even tried changing the code with async/await as suggested here but it makes no difference.
Does anything change if you rewrite the code like this?
Anyway I can't reproduce an extension with native messaging, sorry.
P.S. Before any scientist decides to downvote me badly, I want to ensure that if the solution doesn't work I'm willing to cancel the answer.
const browser = chrome || browser;
var globSendResp, globReqTypeSend; //(*)
// Listen for messages from content script
browser.runtime.onMessage.addListener(function(request, sender, sendResponse) {
var frameId = sender.frameId || 0;
globReqTypeSend = false; //(*)
if (request.type == "connect") {
connect(sender.tab, frameId);
sendResponse({ success: true });
} else if (request.type == "disconnect") {
disconnect(sender.tab, frameId);
sendResponse({ success: true });
} else if (request.type == "send") {
globReqTypeSend = true; //(*)
globSendResp = sendResponse; //(*)
sendNativeMessage(request.data, sender.tab);
//sendResponse({}); //(*)
return true;
}
});
document.addEventListener('nativeMessage', function handleNativeMessage(e) {
if (globReqTypeSend)
globSendResp(e.detail)
}, false);
I'm building a chrome extension , I need to create a function to detect the URL changes for example youtube.com to youtube.com/watch?v={some video id}
I kept looking around for a way to do that on Content.js but apparently it's not possible and i need to use background.js in that particular case
First I've implanted this part
chrome.webNavigation.((EVENT)).addListener(function() {
alert("worked");
});
to test if chrome can detect the URL changes correctly , then i will need to connect that to content.js to Ajax the URL changes and return JSON object from the server
I saw on this page Chrome Extension - webNavigation multiple Events that i can use
Events
onBeforeNavigate
onCommitted
onDOMContentLoaded
onCompleted
onErrorOccurred
onCreatedNavigationTarget
onReferenceFragmentUpdated
onTabReplaced
onHistoryStateUpdated
sadly none of them works properly as they the Alert pops Multiple times ( sometimes 1-4 times ) when i visit ( reload or visit a single page )
I don't know what i should do or if there is any other way to handle a problem like this , I'm fairly new to chrome extension and JS.
background.js
chrome.storage.sync.get( "extensionSwitch", function(data){
if( data[ "extensionSwitch" ] == undefined ){
chrome.storage.sync.set( { "extensionSwitch" : "true" }, function() { } );
}
});
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
fetch(request.input, request.init).then(function(response) {
return response.text().then(function(text) {
sendResponse([{
body: text,
status: response.status,
statusText: response.statusText,
}, null]);
});
}, function(error) {
sendResponse([null, error]);
});
return true;
});
chrome.webNavigation.onHistoryStateUpdated.addListener(function() {
alert("worked");
});
I'm attemtping to send a message from the content script to the background script, and send a response in return.
This is what I've got in my background script:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("Message received");
if(request.activeStatusRequest == "disable"){
sendResponse({activeStatusUpdate: "disable"});
}else if(request.activeStatusRequest == "enable"){
sendResponse({activeStatusUpdate: "enable"});
}
return true;
});
This is what I've got in my content script:
printReviews.onclick = function(element) {
if(printReviews.classList.contains("activeButton")){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.executeScript(
tabs[0].id,
{code: `${injectionLiteralGETURL}`}
);
});
toggleClasses(printReviews, "activeButton", "inactiveButton");
chrome.runtime.sendMessage({activeStatusRequest: "disable"}, function(response){
console.log("response received");
console.log(response.activeStatusUpdate);
});
}
else {
toggleClasses(printReviews, "inactiveButton", "activeButton");
chrome.runtime.sendMessage({activeStatusRequest: "enable"}, function(response){
console.log("response received");
console.log(response.activeStatusUpdate);
});
}
};
The rest of the above code works as expected, except that the none of the console logs in the content script run. The console log in the background script does run, however.
Anyone got any ideas as to what I've done wrong?
If you want to try another method, you could try to add a listener in the content script
Content.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("Message received from background", response.type, response.activeStatusUpdate);
switch(request.type){
case 'SomeFirstAction'
break;
case 'SomeSecondAction'
break;
}
});
Background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("Message received");
if(request.activeStatusRequest == "disable"){
sendMessageToCurrentTab({type: 'SomeFirstAction', activeStatusUpdate: "disable"});
}else if(request.activeStatusRequest == "enable"){
sendMessageToCurrentTab({type: 'SomeSecondAction', activeStatusUpdate: "enable"});
}
// return true; -> not needed with that method
});
function sendMessageToCurrentTab(message){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, message);
}
}
To have played with callbacks between popup/background/content-script, I prefer just play with messages,because when using sendResponse, the manner it works, it open a port to communicate, but if something change (tab close, popup close), you will have an error because the port will be closed too.
Using the 'Full send message manner', the background need to clearly target the receiver tab.
I also recommand you to use a type or some unique action attribute, because all listener will listen to all action. It will identify the action and it's purpose.
Here I just used a switch in the content script, but the background could use the same logic.
Maybe it's not the greatest way of doing things, but it works well for me.
Hope this can help you
I made a button which openning new page in new tab for example https://google.com. And I'm sending a message to the background script to start taking the URL of new opened page. The problem is that I don't know how to stop the listener after retrieving the URL. So it's continue the execution even after I closed that tab.
Popup.js
$('#click').click(function(e)
{
chrome.tabs.create({url: 'https://google.com'});
chrome.runtime.sendMessage({greeting: "googleisopened"}, function(response) {
console.log(ok);
});
});
Background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.greeting == "googleisopened")
{
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
alert(changeInfo.url);
});
}
});
I think you're insterested in this function http://api.jquery.com/off/
It removes all of the events from the object so in your case you could just plug it anywhere in your click event like this:
$(this).off('click');
// Or like this if you want to be specific
$('#click').off('click');
Thanks to everyone who tried to help me. I found a solution by myself.
Instead of using listener I started to use tabs.getSelected to retrieve page URL only when needed (not in cycle as in onUpdated.addListener).
So the background script will be next:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.greeting === "googleisopened")
{
chrome.tabs.getSelected(null, function(tab)
{
chrome.tabs.onUpdated.addListener(mylistener);
function mylistener (tabId, changeInfo, tab)
{
alert(changeInfo.url);
chrome.tabs.onUpdated.removeListener(mylistener);
}
});
}
});
Developing a chrome extension using javascript is one of my university projects.
I don't know how to establish a communication link between content script and background page using messaging. I need some help in this establishing the connection
background.html
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {method: "getHTML"}, function(response) {
console.log(response.data);
});
});
content_script.js
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == "getHTML")
sendResponse({data: document.getElementById('header').innerHTML});
else sendResponse({});
});
A few major issues:
You're depending on some element on the page having the ID header. Such IDs are at the discretion of the site designer, so very few pages actually do have that (including Google). Maybe go for something a little more universal, like the title of the page (document.title).
What does "the extension button" mean? If it means a browser action, that's a part of your extension, so you're correct in wanted to send something from the background script. This is also an easier case, as it's probable that (aside from the issue above of no Google pages having an element of ID header), you're just not capturing the browser action click event. If it's some injected button, however, it's the other way around.
What you want (browser action version)
background.html (inline):
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, { method: "getHTML"}, function(response) {
console.log(response.data);
});
});
});
content_script.js
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method === "getHTML") {
sendResponse({data: document.title});
} else {
sendResponse({});
}
});
What you might want (injected button click version)
background.html:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method === "getHTML") {
console.log(request.data);
}
});
content_script.js:
function buttonClick() {
chrome.extension.sendRequest({method: "getHTML", data: document.title});
}
Code for response to comment below
Very important recommendation: Chrome's developer reference is probably one of the friendliest out there. If you want to know what parts of the chrome.* API are available, start there.
function getHtml(tabId) {
chrome.tabs.sendRequest(tabId, { method: "getHTML"}, function(response) {
console.log(response.data);
});
}
// Note that this will only work once a tab has loaded
chrome.tabs.onSelectionChanged.addListener(function(tabId) {
getHtml(tabId);
});
// This fires the first time a page is loaded
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
if (changeInfo.status === "complete") {
getHtml(tabId);
}
});
Code for second response to comment below
background.html
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method === "getHTML") {
console.log(request.data);
}
});
content_script.js
document.addEventListener("keypress", function(e) {
chrome.extension.sendRequest({method: "getHTML", data: e.which});
});