
Question:
I'm developing an extension for Chrome, and here's the workflow I'm trying to achieve:
popup sends message -> content script 1 listens -> content script 1 sends message -> content script 2 listens -> content script 2 performs action
In concept it's fine and dandy; what I've done is set up 2 listeners: one in each content script:
Popup:
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
console.log('popup send request');
chrome.tabs.sendMessage(tabs[0].id, obj);
});
Content script 1:
chrome.runtime.onMessage.addListener((function (request, sender) {
this.log('wg got request', 'request', request, 'sender', sender);
if (request.action == 'options-updated') {
this.updateOptions(request, (function() {
var obj = {action: 'refresh', WG: window.WG};
this.log('wg forwarded request');
chrome.runtime.sendMessage(obj); // attempting to forward another request
return true;
}).bind(this));
}
return true;
}).bind(window.WG));
Content script 2:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log('content script got request', 'request', request, 'sender', sender);
if (request.WG) {
request.WG.log('message', request.action);
if (request.action == 'refresh') {
WGRefresh(request.WG, request.options);
}
}
return true;
});
Problem is, content script 2 only receives the first message. So the output I'm getting is:
popup send request content script got request (first, to ignore) wg got request (same, first, don't ignore here) wg forward request
And then nothing. The content script should have fired it again, and in the request I always send "action", which I check for in the listener, but for the logs I don't differentiate (it should ALWAYS log, so this means the request never gets there).
I've tried returning true in all the listeners, according to the documentation it will keep the chain running and not stop after the first hit, but even so it's not working. What am I doing wrong?!
Answer1:There are 2 sendMessage
functions in Chrome API.
chrome.runtime.sendMessage
</a> sends a message to <em>all</em> open <em>extension pages</em> (i.e. background, popup, etc.)</li>
<li><a href="https://developer.chrome.com/extensions/tabs#method-sendMessage" rel="nofollow">chrome.tabs.sendMessage
</a> sends a message to <em>all</em> content scripts from the extension <em>in a given tab</em></li>
</ul>So the call to chrome.runtime.sendMessage()
in your first content script can't reach any other content script.
What's more, you can't call chrome.tabs
directly from a content script.
To do what you want, you need to set up a background script that will act like a proxy between CS1 and CS2. Technically, you could use the popup, but it's unreliable, as the popup may be closed and then nobody would be listening. The background page (or better yet, an <a href="https://developer.chrome.com/extensions/event_pages" rel="nofollow">event page</a>) is designed specifically for that purpose.
So the scheme becomes: popup -(tabs.sendMessage
)-> CS1 -(runtime.sendMessage
)-> background -(tabs.sendMessage
)-> CS2
Do note that background page will need to know the tab ID to send the message to. If it's the same tab for some reason, e.g. you're trying to message across frames, you can use the sender
parameter in the callback.
See <a href="https://developer.chrome.com/extensions/messaging" rel="nofollow">Messaging docs</a> for more details.