Following on from a bout of attempting (and finally succeeding) to get the content and background scripts in a Chrome extension to talk to one another, this seemed like a perfect opportunity to write down a couple of the things that I’ve learnt.
Looking at the Chrome API documents for messaging, it seems like this should be an easy task. And it is, of course, once you know what you’re doing. Which is the most frustrating kind of easy task.
Background
We have several different script files in our Chrome extensions. Most commonly, we have the content and background scripts (and the popup script, but the same principles apply).
The content script is the script that is injected into the actual tab that is being viewed. This script has access to the page’s DOM, which allows us to retrieve information about what is being viewed, but does not have access to various pieces of the Chrome API (such as storage, for example).
The background script has access to the Chrome API, but does not have access to the DOM. Between the two of them we have everything we could need, but we have to move the information in between the two somehow.
The functionality to do this is built in of course – let’s have a look at how it works.
Messaging
We can send messages in between all of the different scripts. We can even send messages between different extensions, and from websites to extensions.
Sending a message is very simple, although handled slightly differently depending on if we’re sending a message to a background script, or to a script running on a specific tab.
Content to Background
First off, let’s send a message to the background script from the content script.
1 |
chrome.runtime.sendMessage({"message": "Hi background!"}); |
This version of the command is extremely simple – we just pass in an object that we wish to send to the script. In order for the background script to read this message we set up a message listener for it.
1 2 |
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { } |
This listener will get called whenever a message is passed in. Obviously we want to deal with various messages in specific ways, so inside the listener we test to see what kind of message we’ve received. The object that we originally passed into the sendMessage function is passed through directly, so we can examine it to work out which message it is.
1 2 3 4 5 |
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { if (request.message === "Hi background!") { // we received a hi message } } |
Sending a message is great but, given that most of the time the reason why we send the message is to get information back, it would be convenient to be able to send a response.
Of course, this is allowed for by the API. We can include a callback in our sendMessage request that will be run when a response is received and we can use the sendResponse() method to send that response. First off, let’s look at the callback.
1 2 3 |
chrome.runtime.sendMessage({"message": "Hi background!"}, function(response) { console.log("Background responded saying: " + response.message); }); |
The callback takes in one object, which is the response object sent back by the answering listener. Note that only one response is received – once a listener has called sendResponse() , the callback will not be invoked again.
There are two changes that we need in order to be able to send a response – the most obvious one is to call sendResponse() , and the other is to ensure that our listener returns true . This indicates that we’ll be doing an asynchronous response – note that if you do not do this, your response call back will not be invoked even if you call sendResponse() .
Chrome extension development involves a lot of callbacks that can get confusing, so if your responses are apparently not being received check that return true; returns from the main listener function and not from a callback.
1 2 3 4 5 6 7 |
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { if (request.message === "Hi background!") { // we received a hi message sendResponse({"message":"Hey content!"}); return true; // important } } |
That’s the complete cycle from content to background and back again. Next up, let’s see how we’d go about doing the same thing but starting from the background script.
Background to Content
The procedure for sending a message from the background script to the content script is very similar. The listeners are set up in exactly the same way, as are the responses.
The difference is that we need to specify which tab we wish to send the message to. Most of the time we just want to send it to the active tab, which we can find using the Chrome API. The chrome.tabs.query function takes in the tab parameters and invokes a callback with the matched tabs.
1 2 3 |
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { // do something with it }); |
Once we have it, we use chrome.tabs.sendMessage to send the message, similar to the way we used chrome.runtime.sendMessage earlier. Here I’ve wrapped the block of code in a helper method.
1 2 3 4 5 6 |
function sendMessage(type, message) { message.type = type; chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { chrome.tabs.sendMessage(tabs[0].id, message); }); } |
Conclusion
Messaging in Chrome is fairly easy to use once you’ve gotten it set up and allows for the use of all of the available features regardless of where in the code your entry point is.
That’s everything you need to know to send messages in between content and background scripts – for more details be sure to check out the official documentation, and feel free to post comments if you have any questions.
Leave a Reply