messaging between content script and background page in a chrome extension is not working as it is supposed to be
You send just one message with a direct callback so naturally Chrome can use this response callback just one time (it's a one-time connection to one entity, be it a page or an iframe).
Solution 1: send multiple messages to each iframe explicitly:
manifest.json, additional permissions:
"permissions": [ "webNavigation" ],
background.js
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { ............. // before Chrome 49 it was chrome.webNavigation.getAllFrames(tabId, ..... // starting with Chrome 49 tabId is passed inside an object chrome.webNavigation.getAllFrames({tabId: tabId}, function(details) { details.forEach(function(frame) { chrome.tabs.sendMessage( tabId, {text: 'hey_cs'}, {frameId: frame.frameId}, function(response) { console.log(response) } ); }); }); });
Solution 2: rework your background script logic so that the content script is the lead in communication and let it send the message once it's loaded.
content.js
chrome.runtime.sendMessage({text: "hey"}, function(response) { console.log("Response: ", response); });
background.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { console.log("Received %o from %o, frame", msg, sender.tab, sender.frameId); sendResponse("Gotcha!"); });
Instead of messaging, you can use executeScript
for your purposes. While the callback's argument is rarely used (and I don't think many know how it works), it's perfect here:
chrome.tabs.executeScript(tabId, {file: "script.js"}, function(results) {
// Whichever is returned by the last executed statement of script.js
// is considered a result.
// "results" is an Array of all results - collected from all frames
})
You can make sure, for instance, that the last executed statement is something like
// script.js
/* ... */
result = { someFrameIdentifier: ..., data: ...};
// Note: you shouldn't do a "return" statement - it'll be an error,
// since it's not a function call. It just needs to evaluate to what you want.
Make sure you make script.js
able to execute more than once on the same context.
For a frame identifier, you can devise your own algorithm. Perhaps a URL is enough, perhaps you can use the frame's position in the hierarchy.
Here is a clear way how to communicate bidirectionally in chrome extension [Content ⬄ Background ]:
Inside Content script:
Sends info to background:
chrome.extension.sendRequest({message: contentScriptMessage});
Receives info from background:
chrome.runtime.onMessage.addListener(function(request, sender) {
console.log(request.message);
});
Inside Background script:
Sends info to content script:
chrome.extension.onRequest.addListener(function(request, sender) {
console.log(request.message);
});
Receives info from content script:
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendMessage(tab.id, { message: "TEST" });
});