Communicating across all contentScripts in a Jetpack add-on

I can’t imagine building a Firefox 4 Jetpack add-on that doesn’t need to communicate with its various parts (the main.js file talking to a panel, the panel talking to a page-mod, etc). The docs are good on how to communicate, but fragmented so you don’t see the bigger picture – a flow of communication from a panel, to main.js, down to the page DOM, and all the way back. Thus, here are my lessons learned for communicating between different parts of my add-on.

Please note:

  • I am no expert, so if I have something wrong here, let me know and I’ll update
  • This was all built using the online add-on builder but should apply if you are doing cfx development
  • You can see this add-on source code or even copy it if you want to play around with it

For this demo, we are going to create an add-on that:

  • Has a clickable widget that will open a panel when clicked
  • The panel will allow you to turn the current page’s background color to green or red
  • When a color is selected, a message from the panel is routed up to the main.js file and then down to a page-mod script so that the actual website’s background can be changed (to my knowledge, the panel can not directly talk to the paged)
  • Once changed, the page-mod script will communicate back up to main.js to let the add-on know it’s done
  • The add-on (main.js) will let the panel know the page color has changed as well

Here’s a quick peak of what it will look like:

Sample

    At this point, you should be aware of panel and page-mod – namely, what they do and how to use them. However, you will get the gist of these just by following along.

    The basis of communicating between modules of your code is postMessage. postMessage allows us to send a message, and the receiver should be rigged up to listen to message events in order to handle them. A quick example can be illustrated as:

    https://gist.github.com/873329

    So when we run this, and we click on a different tab, we should see in the logs:

    info: msg from main.js: http://someurl.com
    info: msg from panel: thanks, we got the url http://someurl.com

    Now let’s get back to our example add-on.

    Below is the code for main.js, please read the comments:

    https://gist.github.com/873322

    The first thing you might notice is that I don’t pass a simple string as my message. I ran into this problem early on in my add-on project. My panel might tell the main.js to do one of MANY things: delete something, save something, etc. so I pass my messages as JSON, and each packet of JSON has a “type” key that will allow my message listener to call different functionality based on what’s sent. Our communication system can now handle a variety of messages that do different things.

    Page-mod works a bit differently from a panel when listening for messages. This was a little complicated to me at first, but is easy once you see it in action. Instead of a built-in callback for handling message (onMessage), we need to fiddle around with the onAttach callback. Through this we have access to “worker”. “worker” will get our postMessage calls from pagemod.js and acts as our listener and reference to the page_mod for later use.

    Looking at these two lines:

    https://gist.github.com/873341

    We forward those messages to a stand alone function called “handlePageModMessages”. We also assign our global variable “page_worker” the value of “worker” for reference later… remember view.postMessage(), now we have page_worker.postMessage().

    Our main.js file should be good to go. We now need to create 4 files in our data directory:

    • panel.html
    • panel.js
    • jquery.js
    • pagemod.js

    Let’s look at pagemod.js first. In that file, we have the following code:

    https://gist.github.com/873332

    All we do is listen for messages from main.js. If we get one, and it’s a “color_change” type of message, we do something with it: change the current pages background color to the color passed (data.color). Once we do this, we want to tell the panel the task is complete, so we need to post back to main.js, as illustrated above with the message that has a type of “color_change_complete”.

    Main will get the message and, since it’s a type of “color_change_complete” will forward it on to the panel with a view.postMessage(data);

    Now, let’s look at your panel files: jquery.js, panel.js and panel.html

    jquery.js is just jquery. We’re installing it into our panel for easy DOM manipulation.

    panel.html is simply:

    https://gist.github.com/873374

    All the magic happens in panel.js:

    https://gist.github.com/873335

    There are three things going on here. First, we have our onMessage handler to accept messages from main.

    Second, we have a function that is called when we click on the red or blue buttons that will post the selected color back up to main.js.

    Finally, we have some jquery to help us add click events to our buttons.

    Running the above add-on would have this flow:

    • widget is clicked
    • panel is opened
    • red or blue button is pressed
    • change_bg (in panel.js) is called and posts a message back up to it’s parent, main.js
    • the code we used to create the panel received a message through it’s onMessage handler
    • checking the msg.type, main.js handles the message accordingly, by posting the msg to the paged script
    • pagemod accepts the message through it’s onMessage handler
    • paged processes the request and responds back to main.js
    • main.js sends a message to view that the color change is complete.

    This is pretty much it. It’s confusing at first, but once you play around with it some, you will find it’s no big deal… just remember:

    • To send a message to a parent (ex: a panel sends a message to main.js), use postMessage(msg);
    • To send a message from parent to child (ex: main.js to a panel), you need a reference to the panel (ex: view = panel.Panel({….})) and you use that as such: view.postMessage(msg);
    • Any js in your panel, page_mod, etc needs to listen for message events with onMessage = function onMessage(msg){ do stuff }
    • Any api element (panel, page_mod, etc) will have it’s own way of listening and handling message, so adjust accordingly.

    Whew, ok, that was a lot, but was my biggest challenge when I first started working with Jetpack. Hopefully, this will get you up and running faster than it took me.

    Advertisements

    3 comments

    1. Hi!Congratulations for the post.Maybe you can help me.I am doing a addon where I have a panel, from that panel I open a new window. do you know how to save some values from the new window into local storage using simple-storage module?I know how to comunicate my main.js with my panel.html using a panel.js, but I don’t know how to send values from the new window to main.jsThanks??lvaro.

    2. Alvaro, you could attach a page mod to the new window and use that to communicate. Have you done any work with page mods?

    3. Hi Daniel,Finally is working, I am using tab.attach() function.Thanks and congratulations for the blog.


    Comments are closed.