Reputation: 163
For future reference: I solved this by switching the onMessage/sendMessage to the background script and the content script, respectively. I'm not sure why this worked, but it did.
I've been trying to debug this for the past three hours--I'm building a chrome extension and I've been getting "undefined" as the response to a message I send. I'm trying to get the text of the tab the user is on and use it to do some analysis.
Content Script:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.method == "collectText") {
sendResponse({data: document.body.innerText, method: "collectText"});
}
return true;
}
);
Popup Script:
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {method: "collectText"}, function(response) {
if (response) {
txt = response.data;
console.log("response arrived");
console.log(txt);
} else {
console.log("No response.");
}
});
});
The console.logs are currently there so that I know if the data is coming back (it's not--"No response" is logged every time the scripts run, and txt
ends up undefined). I'm not sure why this isn't working (& I've tried many variations on the same scripts to no avail). I'm not getting any errors in the console of either the extension or the page I'm on. Should I be using a background script instead of writing everything inside popup.js?
Thank you!
Upvotes: 10
Views: 13055
Reputation: 815
For me the problem was that the listener function was async
. I just wanted to use await
but this causes the listener to return a Promise
instead of true
. You need to wrap your handler in an async function.
Before (incorrect):
chrome.runtime.onMessage.addListener(async function (request: any, sender, sendResponse) {
debug('Got message', request);
if (request.type === 'updateUser') {
const responseUser = await getUser();
user = responseUser;
await chrome.storage.local.set({ user });
sendResponse({ user });
}
return true;
})
After:
const handleUpdateUser = async (sendResponse: (response: any) => void) => {
const responseUser = await getUser();
user = responseUser;
await chrome.storage.local.set({ user });
sendResponse({ user });
};
chrome.runtime.onMessage.addListener(function (request: any, sender, sendResponse) {
debug('Got message', request);
if (request.type === 'updateUser') {
handleUpdateUser(sendResponse);
}
return true;
})
Upvotes: 23
Reputation: 149
The solution to this problem is to move the content of the scripts so that the listener is in the background script, and the message is sent from the content script.
Popup communicating with content script won't achieve what you want.
Visually:
Before, not working:
Popup script ----(message)---> content script
After, working:
Content script ----(message)---> background script
I see a comment on OP's own answer saying "I wish I knew what you meant" with 2 upvotes! So I post my own answer, to both visually represent the solution to OP's problem, and to answer commentator's and the 2 up-voters question in one answer. How is this an answer you vote to delete 'as it doesn't answer the question'? I have tested this on my end and it works.
Upvotes: 0
Reputation: 2352
This type of errors can happen if you return an answer asynchronously. (Although that could not happen on the example you have given)
// sender
runtime.sendMessage(msg, response => {
if (!response)
console.error("This was a fiasco :", runtime.lastError.message);
});
// receiver
runtime.onMessage.addListener(async (request, sender, sendResponse) => {
var response = await answer_something(request);
sendResponse(response);
});
// same problem for this receiver
runtime.onMessage.addListener((request, sender, sendResponse) => {
promise_something(request).then(response => {
sendResponse(response);
});
});
In such case you would see the following error : Unchecked runtime.lastError: The message port closed before a response was received
To fix it, you must return true
from an asynchronous onMessage
listener. Note that returning a promise will NOT work on chrome (docs for chrome, docs for firefox).
Upvotes: 18
Reputation: 163
For future reference: I solved this by switching the onMessage/sendMessage to the background script and the content script, respectively. I'm not sure why this worked, but it did.
Upvotes: 0
Reputation: 2684
You probably get an error, because of the code + comment:
// According to the documentation of chrome.runtime.sendMessage, the
// callback is invoked without any arguments when an error occurs
You need to check an lastError
property to get info about an error and fix it.
Hope, that will help.
Upvotes: 3