Introduction
With the help of extensions you can create and add new impressive functionality to the Opera browser. As mentioned in other articles, Opera extensions contain a background script, an injected script, and sometimes a pop-up window. In this article we will look at how to ensure the exchange of data between these three components.
Before proceeding, you must download three extensions and install them into Opera 11 alpha . We will consider the topic using the code of these extensions.
- An example of an extension that demonstrates the exchange of data between background and injected scripts.
- An example of an extension that demonstrates the exchange of data between a background script and a pop-up window.
- An example of an extension that demonstrates the exchange of data between a pop-up window and an injected script (with a little help from a background script)
Data exchange between background and embedded scripts
Opera uses the postMessage () method to send a message. If you want to send data from the background script to the script being injected, then you need to write the following code in the background script:
opera.extension.broadcastMessage("Hello there");
After you send a message, the injected script must accept it, which can be done as follows:
var thecatch; opera.extension.onmessage = function(event) { thecatch = event.data;
That's how simple it is. In the above code, the background script sends the data to the injected script using the broadcastMessage method, and the injected script accepts it. After receiving the data, it saves them in thecatch variable. It must be remembered that using the broadcastMessage method, you send a message to all embedded scripts and pop-up windows, so you need to use this method sparingly. Usually, you need to contact only one pop-up window or an embedded script (one that is contained in your extension) using the postMessage method or message channels.
')
We demonstrate this with our first test case.
The background script has a load event hook, which calls the setupConnection function. You can watch the output of the opera.postError method in the Error Console (Tools> Advanced> Error Console).
window.addEventListener("load", setupConnection, false); function setupConnection() {
Pay attention to the following code:
opera.extension.onconnect = function(event) { event.source.postMessage("something"); opera.postError("sent message to injected script"); }
The onconnect method runs when the injected extension script is activated in the document, connecting it with the background script. That is, as soon as the embedded script loads, the onconnect method is executed.
The onconnect handler in the background script receives a link back to the script being injected through the event.source object. This message port can be used to directly link with the embedded script, as demonstrated by sending the “something” line using the event.source.postMessage () method. Along the way, we send a small notification to the Error Console to track what is happening.
Let's leave the background script for a while and turn our attention to the script being injected:
opera.extension.onmessage = function(event){
The onmessage method is called when an embedded script receives a message. We store the message content in the message variable. Then we use the opera.postError method to send a notification to the Error Console.
To confirm the receipt of the message, we send a reverse response to the background script using the event.source object. Remember: event.source always points to the source of the message.
So now we have seen how messages are received by the injected script and how the response can be sent to the background script.
The next step is receiving the message with a background script and processing it. Let's go back to the background script and look at another part of the code:
Here, at the time of receiving the message, the script sends to the Error Console a notification of its receipt, along with the message content.
So that's what happens in the extension:
- The background script sends a message to the injected script.
- The injected script receives the message and sends a notification with the message contents in the Error Console
- The injected script sends the reverse answer.
- The background script receives the response and sends a notification with the contents of the response in the Error Console.
If you installed the extension (refresh the page so that the injected script is applied), then go to the Error Console. You should see the messages shown in Fig. one:
Figure 1 : Error Console and notifications sent to it from the extension.
We looked at how background and injected scripts can receive and send messages. Now let's look at how this can be done with a pop-up window.
Messaging between popup window and background script
We will use the second extension example for this chapter. Please note that there is no includes folder and the script being injected is only a background script, html files, config.xml and an icon.
From the previous articles you already know how
to create a UI element, for example a button , so that we will not focus our attention on this. Let's look at the following code in background.js:
opera.extension.onconnect = function(event){ event.source.postMessage("sending something"); opera.postError("sent message to popup"); }
As we know from the previous example, this code will be executed when something is associated with a background script, in this case this popup window. The function sends the message “sending something” to the pop-up window and the notification “Sent message to popup” in the Error Console.
Now look at the popup page:
<script> window.addEventListener("load", function(){ opera.extension.onmessage = function(event){ event.source.postMessage("do whatever you want with this message"); opera.postError("sent from popup to background script"); } }, false); </script>
Here we intercept the incoming message and send a reverse response to the source. We also, as usual, send a notification to the Error Console. Now the popup window accepts the message and sends the response to the background script. It remains only to get this answer in the background script.
Go back to background.js and see the code:
opera.extension.onmessage = function(event){ opera.postError("This is what I got from injected script: " + event.data); }
Here, the background script intercepts the message and sends a notification with the message content to the Error Console. As you can see, we can use the same code as for the implemented script.
So that's what happens in the extension:
- The background script sends a message to a popup window.
- The pop-up window receives a message and sends a notification with the contents of the message in the Error Console
- A popup window sends a reverse answer
- The background script receives the response and sends a notification with the contents of the response in the Error Console.
Everything happens exactly the same as in the previous example, only with the difference that we do not have an injected script, and we associate with a pop-up window.
If you installed the extension, click on its button on the browser panel to start the message exchange and open the Error Console to see the one shown in Figure. 2:
Figure 2 : Error Console and notifications sent to it from the extension.
Messaging between popup window and injected script
Now let's look at how to exchange data between the pop-up window and the injected script. The background script will be used only to initialize the connection, then the pop-up window and the injected script will communicate directly.
We can achieve this by creating a communication channel. You can learn more about the communication channels
in the HTML5 cross-document messaging specification on which messaging in extensions is based.
We will look at this using the third example extension, so run your text editor and look at the code. First of all, take a look at the background script. You will notice familiar elements, such as adding a button and a pop-up window. Drop down to a more interesting site.
First, we define the global variable port, which we will use later. Let's look at the onconnect handler:
opera.extension.onconnect = function( event ){ if( port ) event.source.postMessage( "Respond to the port", [port] ); }
At the time of connecting to the background script, it will send a message 'Respond to the port'. We specify the port to send the message, but since the port variable does not matter yet, the message will be sent as it is.
Now let's look at the injected script:
var channel = new MessageChannel(); opera.extension.postMessage( "Respond to this immediately", [channel.port2] );
Here we create a new message channel. The message channel will have two ports, and data can be sent and received through these two ports. Here we send data through the second port and receive through the first. Then we send the message using the postMessage () method via the second port of the message channel.
Let's go back to the background script:
opera.extension.onmessage = function( event ) { if (event.ports) port = event.ports[0]; }
The background script accepts a message from the script being injected. Now the link to the second port of the channel is accessible via the event.ports [0] object and stored in the port variable. Now the background script is ready to connect the popup. When this happens, the onconnect method is called again:
opera.extension.onconnect = function( event ){ if( port ){ event.source.postMessage( "Respond to the port", [port] ); } }
Now, the value of the port variable is not empty. It contains a link to the port of the message channel.
Look at the popup file:
opera.extension.onmessage = function( event ){ if( event.ports ){ opera.postError( "Responding to port" ); event.ports[0].postMessage( "Hi from popups side!" ); } }
The pop-up window receives the message and if it contains a link to the port, then it sends a notification to the Error Console, and more importantly, sends a response through the specified port. Now it only remains to receive this message in the embedded script. In the implemented script we have the following code:
channel.port1.onmessage = function( event ){ opera.postError("Here is what i got in the injected script for port1: " + event.data); }
The injected script listens to the message channel port1. When a message is received on it, it sends a notification to the Error Console with the contents of the message received from the pop-up window.
So that's what happens in the extension:
- The injected script is being loaded.
- The onconnect method runs in a background script and sends a message to the embedded script.
- The injected script receives the message and creates a message channel.
- The injected script sends a message to the background script along with a link to the second port.
- The background script accepts the message and saves the link to the port.
- The popup window is connected to the background script.
- The onconnect method is executed again, but this time it sends a return message along with a link to the port of the embedded script.
- A pop-up window receives a message, checks for a link to the port. If there is one, it sends a return message to this port (this is port2 of the communication channel).
- The injected script receives the message. After that, it sends a notification to the Error Console with the text received from the pop-up window.
In this scenario, the background script acts as a matchmaker. First, she contacts the first participant (the script injected). The participant gives his number (second port) to the matchmaker, which the matchmaker saves. Another participant (pop-up window) is associated with the matchmaker. As soon as this happens, the matchmaker sends the number of the first participant (the script being injected) to the second participant (pop-up window). Now all further communication takes place directly between the two participants, removing the matchmaker from the equation.
Summing up
Data exchange between different parts of an extension is fairly simple after you learn how to use the postMessage method with onconnect and onmessage handlers. You can also use message channels to transfer data, and this approach is necessary when data is transferred between the pop-up window and the embedded script. This article sheds some light on data transfer in three scenarios: between the background and injected scripts, between the background script and the pop-up window, between the pop-up window and the injected script.
API links
Methods related to the
background scriptMethods related to the
embedded scriptMethods related to
pop-ups