I am having an issue of asynchronicity (I believe). sendResponse()
in contentscript.js does not wait for getThumbnails()
to return.
I am sending a message in popup.js:
chrome.tabs.sendMessage(tabs[0].id, {message: "get_thumbnails", tabUrl: tabs[0].url},
function (respThumbnails) {
const thumbUrl = respThumbnails.payload;
console.log("payload", thumbUrl)
}
);
Then, in contentscript.js I listen for this message:
chrome.runtime.onMessage.addListener(async function(request,sender,sendResponse) {
if(request.message === "get_thumbnails") {
const payload = await getThumbnails();
console.log("thumbPayload after function:", payload)
sendResponse({payload:payload});
}
});
async function getThumbnails() {
let tUrl = null;
var potentialLocations = [
{sel: "meta[property='og:image:secure_url']", attr: "content" },
{sel: "meta[property='og:image']", attr: "content" },
];
for(s of potentialLocations) {
if(tUrl) return
const el = document.querySelector(s.sel);
if(el) {
tUrl = el.getAttribute(s.attr) || null;
}
}
return tUrl;
};
But it is also possible that the problem is coming from my getThumnails()
function, because most of the times, payload is null and not undefined. So getThumbnails()
might return before it is completely executed.
If this is the case, I have no idea why...
I also tried this code for getThubnails()
:
async function getThumbnails() {
let x = await function() {
let tUrl = null;
var potentialLocations = [
{sel: "meta[property='og:image:secure_url']", attr: "content" },
{sel: "meta[property='og:image']", attr: "content" },
];
for(s of potentialLocations) {
if(tUrl) return
const el = document.querySelector(s.sel);
if(el) {
tUrl = el.getAttribute(s.attr) || null;
}
}
return tUrl;
}
return x;
};
But this does not work, it seems to break my code...
Chrome still doesn't support Promise in the returned value of onMessage listener both in ManifestV3 and V2. Click the star in https://crbug.com/1185241 to be notified of progress.
Since your listener is declared with async
keyword, it returns a Promise
, not a literal true
value as required by onMessage implementation, so the returned Promise with your value is just ignored, the port is immediately closed, and the caller receives undefined
in response.
Remove the async
keyword from before (request, sender, sendResponse)
Declare a separate async
function and call it from the onMessage listener:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.message === "get_thumbnails") {
processMessage(msg).then(sendResponse);
return true; // keep the messaging channel open for sendResponse
}
});
async function processMessage(msg) {
console.log('Processing message', msg);
// .................
return 'foo';
}
Add the following at the beginning of each script that uses chrome.runtime.onMessage
if ('crbug.com/1185241') { // replace with a check for Chrome version that fixes the bug
const {onMessage} = chrome.runtime, {addListener} = onMessage;
onMessage.addListener = fn => addListener.call(onMessage, (msg, sender, respond) => {
const res = fn(msg, sender, respond);
if (res instanceof Promise) return !!res.then(respond, console.error);
if (res !== undefined) respond(res);
});
}
...and then you can simply return
the value:
chrome.runtime.onMessage.addListener(async msg => {
if (msg === 'foo') {
const res = await fetch('https://foo/bar');
const payload = await res.text();
return {payload};
}
});