I think chrome extensions are overall quite simple and very powerful, but one thing that always confuses me is trying to communicate between the various script that code may run in. There's the code that runs when referenced from the "default_popup" page of the browser action, the code in the "scripts" property of "background" and the content scripts.
In what context are scripts in these categories run, and how can each communicate with the others?
It's been some time since I have had to deal with chrome's extensions. I remember it was quite a struggle before I got how things worked. In order for your extension to communicate with the browser it's easy you use your javascript/background file, and to communicate with the webpage you need to use
chrome.tabs.executeScript
but that's really tricky and can be a real pain in the butt. I suggest you take google's tour on extensions and give their api a really good overlook, everything's in there! I wish you good luck and I hope this answer has helped you! :PThe Google documentation has everything, but it's hard to pull all the information together. There are two main types of scripts:
1. Background scripts have full access to the Chrome api's, but can't interact with the target webpage.
2. Content scripts can interact with each other and with the webpage's DOM (but not its scripts, from which it's isolated), but have only limited access to the Chrome api's.
Both run whenever you load a new page (unless you've used "matches" to restrict where the content script runs).
You can communicate between the two via message passing. This is more easily done from content scripts than from background scripts because you need to know the tab's id for the latter.
Other scripts (
browserAction.js
,pageAction.js
,optionsPage.js
) only run when their corresponding html page is opened (as if you are opening the webpage in your browser window, which is what you're really doing). They are similar to background scripts in restrictions and abilities.Try to avoid the need to interact with a page's scripts. The best way I know of is to interact through the shared DOM (literally writing javascript code inside html comments). But the target of your extension isn't designed for this, so you'll have to include you're own script that does this into the webpage. Use a content script to write the script element into the document (its
src
ischrome.extension.getURL("myscript.js")
,and you'll need to have
"web_accessible_resources": ["myscript.js"]
in your manifest.
Three different contexts
As a Chrome extension developer, you can distinguish three different environments.
Note that
<iframe src="chrome-extension://EXTENSIONID/page.htm">
in non-extension pages used to be treated like case 2 (content scripts), because the frame was loaded in an unprivileged tab process. Since out-of-process iframes was launched for extensions in Chrome 56, these pages are handled by the extension process, and therefore they may use the same full set of extension APIs. This change in behavior (allowing extension frames to use privileged extension APIs) is intentional.Accessing the
window
object within an extension processBecause all extension code runs in the same process, they can access each other global
window
object. This feature is not well-known, but allows one to directly manipulate JavaScript and DOM objects within the same extension process. It's generally better to not use this method, but use the message passing APIs instead.To keep this section short, I have intentionally limited the code example to a demonstration of accessing other global
window
objects. You could use these methods to define a global method, set a global variable, call a global function, etc.... provided that the page is open. Someone thought that the popup's
window
is always available. This is not true, when the popup is closed, the global object is disposed!Communication by message passing
A message channel always has two ends: The sender and a receiver.
To become a receiver, bind an event listener using the
chrome.runtime.onMessage.addListener
method. This can be done by extension code and content scripts.To pass messages within the extension, use
chrome.runtime.sendMessage
. If you want to send a message to another tab, callchrome.tabs.sendMessage
. The target tab is specified by including an integer (tabId
) as its first argument. Note that a background page can only send a message to one tab. To reach all tabs, the method has to be called for every tab. For instance:Content scripts can only call
chrome.runtime.sendMessage
to send a message to extension code. If you want to send a message from a content script to another content script, a background / event page should is needed, which takes a message and sends it to the desired tab. See this answer for an example.The
sendMessage
methods accept an optional function, which is received as a third argument to theonMessage
event.The previous example shows obvious behaviour. Things get trickier when you want to send a response asynchronously, for instance if you want to perform an AJAX request to fetch some data. When the
onMessage
function returns without having calledsendResponse
, Chrome will immediately invokesendResponse
. SincesendResponse
can be called only once, you will receive the following error:Do as the error suggest, add
return true;
inside your onMessage event listener:I've explained the practical application of simple one-time message passing in this section. If you want to know more about long-lived message channels or cross-extension messaging, read the tutorial from the official documentation.
The message passing API has undergone several name changes. Keep this in mind if you read old examples. The history and compatibility notes can be found here.
Communication between a content script and the page
It's possible to communicate with the page. Apsillers has created an excellent answer which explains how to set up a communication channel between a (non-extension) page and a content script. Read his answer at Can a site invoke a browser extension?.
The advantage of apsiller's method over the one from the documentation is that a custom event is used. The documentation uses
window.postMessage
to send a message to the page, but this could cause conflict with badly coded pages which do not expect the message events.