postMessage JSON: encoding / decoding

5,880 views
Skip to first unread message

pd

unread,
Mar 15, 2011, 10:28:03 PM3/15/11
to mozilla-labs-jetpack
Hi All

A quick scan of the documentation did not reveal an encoding/decoding
JSON utility method. Now that I've got postMessage working between a
contentScript and my main.js, everything sent through postMessage
needs to be encoded in JSON yeah? Is there some standard JavaScript
JSON encoding/decoding functionality? In your opinion, where is the
best reference and example for how to do this?

I know this is a bit of a newb question but I've only played with JSON
at a very minor level when passing very small data structures via
jQuery's .getJSON() method.

Thanks
pd

Atul Varma

unread,
Mar 15, 2011, 10:53:05 PM3/15/11
to mozilla-la...@googlegroups.com, pd
No worries! I believe that JSON.parse() and JSON.stringify() are
actually part of JavaScript, rather than being part of the browser, and
as a result they're undocumented in Jetpack--though they ought to be
documented, since it's very hard for most people to know where
JavaScript ends and Web-specific APIs begin.

Here's the documentation on it:

https://developer.mozilla.org/En/Using_JSON_in_Firefox

- Atul

Daniel Goodwin

unread,
Mar 15, 2011, 11:06:17 PM3/15/11
to mozilla-la...@googlegroups.com
Typically, when posting a message, I use:

postMessage(JSON.stringify({
      "type": "delete",
      … other json data…
}));

When receiving a message, I use:

onMessage = function onMessage(msg){
    msg = JSON.parse(msg);
    if(msg.type == "delete"){
        … do something...
    }else if(msg.type == "save"){
    …etc…

So JSON.stringify when posting, JSON.parse when receiving.

Also, what has helped me out in the past is passing some kind of message identifier, illustrated in the "type" key above. Chances are, I'll be sending various messages back and forth and will need a way to identify each one to process differently.


Drew Willcoxon

unread,
Mar 15, 2011, 11:07:31 PM3/15/11
to mozilla-la...@googlegroups.com
> contentScript and my main.js, everything sent through postMessage
> needs to be encoded in JSON yeah?

postMessage automatically encodes whatever you pass it, including
objects, and on the other side onMessage automatically decodes the
message. Your message only has to be JSONable, e.g., it can't be a
function, and if your object includes a method, the method won't be encoded.

You can encode your message yourself, no harm, but there shouldn't be
any need. It'll end up being double encoded, and you'll have to decode
it yourself in onMessage.

Drew

On 3/15/11 7:28 PM, pd wrote:

pd

unread,
Mar 16, 2011, 1:15:11 AM3/16/11
to mozilla-labs-jetpack
Thanks everyone. Phew I'm glad this topic wasn't as silly as I feared.

@Atul: Your point about people not knowing where web (or document, aka
DOM?) APIs begin and JS ends or vice-versa is very important I think.
I realise the SDK doc has reason to be concise and limited to just the
API however as I mentioned in my 'Patterns' post, I wonder if there is
not a need for more of a hacks.mozilla.org style how-to do stuff in
Jetpack site? Maybe now that developers apparently are able to spend
more time on Jetpack since Fx4 is nearing a release, someone might
have more time for this?

pd

unread,
Mar 17, 2011, 11:16:08 AM3/17/11
to mozilla-labs-jetpack
Hi All

I thought I had this sorted but as usual one step forward, two back
for me.

Regardless of whether the data is JSON encoded by the postMessage /
onMessage pair, or double encoded with JSON.stringify and JSON.parse,
I am having trouble getting any content to pass a message back to the
main.js on user interaction.

Here's the open tab function run at load time in main.js

function open_tab () {

const data = require("self").data;

var JP_TABS = require("tabs");

JP_TABS.open({

url : data.url("empty.html"),
onReady : function (tab) {

tab.attach({

contentScriptFile : data.url("content.js"),

onMessage : function (data) {

console.log(data);

}

});

}

});

}

Here's content.js



document.body.innerHTML = "<p>A content.js paragraph</p>";

postMessage("a postMessage from content.js");



Now both those lines work but obviously all the innerHTML line proves
is that I can modify the DOM of the empty.html file loaded in the tab.
The postMessage line proves I can get a simple double quoted string to
output via postMessage / onMessage to the cmd line console output.

I've tried using a and button tags in the innetHTML and giving them
onclick attributes like this:

document.body.innerHTML = "<a href='#' onclick=postMessage('a simple
string')>A content.js paragraph</p>";

and that is of course fun due to the single and double quoting.
However at best an error is registered when I click on it, nothing
else is posted back to the main.js


How do create a HTML element that will be able to send a postMessage
back to main.js when clicked?


Thanks
pd


On Mar 16, 2:06 pm, Daniel Goodwin <das...@gmail.com> wrote:
> Typically, when posting a message, I use:
>
> postMessage(*JSON.stringify(*{
>       "type": "delete",
>       … other json data…
>
> }*)*);
>
> When receiving a message, I use:
>
> onMessage = function onMessage(msg){
>     msg = *JSON.parse(*msg*)*;

Daniel Goodwin

unread,
Mar 17, 2011, 11:47:52 AM3/17/11
to mozilla-la...@googlegroups.com
pd,

I've just posted a detailed article on communication throughout your add-on (from main.js to a panel back to main.js down to a page-mod and all the way back to the panel again) based on my experiences. Maybe this will help you:

http://dsg.posterous.com/communicating-across-all-contentscripts-in-a

Additionally, I've made available the bare-bones add-on from the article so you can poke around and copy it if needed.

https://builder.addons.mozilla.org/addon/1001184/latest/

I hope this helps... but, let me know if you still have issues after reading over that so I can (A) make the article clearer, and (B) help you get through your issue.



wbam...@mozilla.com

unread,
Mar 17, 2011, 12:42:45 PM3/17/11
to mozilla-la...@googlegroups.com
Hey pd

> How do create a HTML element that will be able to send a postMessage
> back to main.js when clicked?
>

I'm not sure if this answers your question, but you could also take a
look at the annotator example code, for example under
annotator/data/editor, which includes HTML and a content script for a
panel that hosts a textarea and sends the contents of the textarea to
the add-on when the user presses <return>.

Will


Atul Varma

unread,
Mar 17, 2011, 2:20:07 PM3/17/11
to mozilla-la...@googlegroups.com, pd
Yeah, that makes sense, it'd be great if we could devote more resources
to that, or perhaps come up with an aggregated "Planet Jetpack"-style
feed of blog posts from around the Web that provide how-tos.

Regarding JSON and other kinds of APIs, the Chrome Extensions
documentation has a nice page on "Other APIs" that provide a broad
overview of what else is available, along with pointers to more
documentation:

http://code.google.com/chrome/extensions/api_other.html

Might be useful to integrate similar things into our docs, although I
believe we've already got some of it mentioned on the Globals section.

- Atul

Irakli Gozalishvili

unread,
Mar 17, 2011, 6:43:39 PM3/17/11
to mozilla-la...@googlegroups.com
Hi pd,

This won't work since postMessage is called by a page not a content
script. You should use addEventListener in which case postMessage will
bee called from content script

> --
> You received this message because you are subscribed to the Google Groups "mozilla-labs-jetpack" group.
> To post to this group, send email to mozilla-la...@googlegroups.com.
> To unsubscribe from this group, send email to mozilla-labs-jet...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/mozilla-labs-jetpack?hl=en.
>
>

--
Regards
--
Irakli Gozalishvili
Web: http://www.jeditoolkit.com/
Address: 29 Rue Saint-Georges, 75009 Paris, France <http://goo.gl/maps/3CHu>

pd

unread,
Mar 19, 2011, 4:45:58 AM3/19/11
to mozilla-labs-jetpack
Daniel

Huge effort, that's excellent. Unfortunately since it is structured as
a three-way example I'm a bit confused. It has helped me with some
basics like getting jquery attached to the page contents. I can now
also trigger actions in main.js from tab content html via tab
content.js

My problem now is being able to pass data back to the content from
main.js

Also another problem I found with your example is that I'm not
strictly using a pageMod but what seems to be (though I'm not sure) a
kinda 'quasi' pageMod created when opening a tab with my HTML as the
content.

Since my code is getting a little big, the problem of how to present
it for help with debugging is a bit of a mongrel. I tried the Add-On
Builder but it would not let me upload the jQuery lib into the data
folder! "Forbidden". Also it's a little slow on my machine. Maybe
github but I'm not using git locally ATM as it seems overboard for a
four-file (so far) addon.

Any thoughts? I can always paste into this group though it cuts code
to 80 columns on the web at least :(

pd

pd

unread,
Mar 19, 2011, 4:50:49 AM3/19/11
to mozilla-labs-jetpack
Will

I'd temporarily forgotten about the annotator example, I'll see what I
can get from it though I expect it will be like Daniels in that it's
going to use widgets and panels that I do not use.

What I really need is a simplified example of how to communicate from
a contentScript attached to a tab up to main.js and then back again.

Editing the DOM is easy now with the magnificent jQuery and Irakli's
excellent point about only doing postMessage from the JS, not the
HTML. I'm quite familiar with this from following the unobtrusive JS
theory of webdev.

pd

On Mar 18, 3:42 am, "wbamb...@mozilla.com" <wbamb...@mozilla.com>
wrote:

pd

unread,
Mar 19, 2011, 4:59:23 AM3/19/11
to mozilla-labs-jetpack
Irakli

Thanks. It took me a while to understand what you were saying but when
I did, the bell rang true!

With your tip and having got jQuery running through some hints from
Daniel's post, I can HTML elements in a HTML file I load into a tab to
trigger a file listing in the cmd line console.

Unfortunately I thought sending that file listing back to the HTML
file and displaying it with jQuery.HTML() would be easy but I'm not
sure where to start with that.

I haven't got, AFAIK, a worker so far. Maybe that's what I am
missing?

I would love everyone to be able to hlep me figure out what is
happening but am not sure where to post my code.

pd
> > For more options, visit this group athttp://groups.google.com/group/mozilla-labs-jetpack?hl=en.

Atul Varma

unread,
Mar 19, 2011, 8:53:21 AM3/19/11
to mozilla-la...@googlegroups.com, pd
On 3/19/11 1:59 AM, pd wrote:
> I would love everyone to be able to hlep me figure out what is
> happening but am not sure where to post my code.
Github has pretty nice facilities for this... If it's just a snippet of
code, you can post a "gist":

https://gist.github.com/

If it's the whole directory structure, you're better off creating a
github account and making your project into a git repository, which will
allow other folks to post comments on your code. They've got a great
"github bootcamp" series to help you get started:

http://help.github.com/

Hope that helps.

- Atul

Daniel Goodwin

unread,
Mar 21, 2011, 4:34:00 PM3/21/11
to mozilla-la...@googlegroups.com, pd
pd,

If I understand your goal correctly, I think this barebones example may help you (please see inline comments for explanation):

Create a new addon. In main.js, add this:

// load in our helpers
var tabs = require("tabs");
var data = require("self").data;

var current_tab;

// when someone goes to a tab:
tabs.on('activate', function(tab){
    tab.attach({
        // attach the tab.js file
        contentScriptFile: data.url("tab.js"),
        // and handle messages coming FROM tab.js
        onMessage: function(data){
            if(data.type == "click"){
                // do some stuff, then post back to the tab
                // the thing you might be missing here is using "this" to refer back to your tab/contentScript
                this.postMessage({
                    "type": "click_complete",
                    "msg": "thanks for clicking the button!"
                });
            }else{
                console.log("unknown message from TAB: " + data.toSource());
            }
        }
    });
});

Now, create a file called tab.js in your data folder, add this:

var btn_id = "my_addon_btn";

// create a button and attach to DOM:
// but only if it isnt there already...
if(!document.getElementById(btn_id)){
    var btn = document.createElement("button");
    btn.id = btn_id;
    btn.innerHTML = "Click Me";
    // add an event listener that will call the btnClick func
    btn.addEventListener("click", btnClick, false);
    // add to the tab's DOM
    document.body.appendChild(btn);
}

// handle the click event listener on the button
function btnClick(){
    // The button was click so tell main.js
    postMessage({
        "type": "click",
        "msg": "You click on the button!"
    });
}

// handle messages from main.js to here....
onMessage = function onMessage(data) {
    if(data.type == "click_complete"){
         console.log("you clicked a button in the dom");
         console.log("we told main.js about it");
         console.log("and this is main.js's response:");
         console.log(" - " + data.msg);
    }else{
        console.log("unknown message type: " + data.toSource());
    }
};

Run that, bounce around some tabs, click the appended button, and watch the console. I'm hoping this is what you need.

Some notes:
  • May need to wait for the dom to be loaded before adding the button
  • Not sure if the way I have the content script attaching to the tab will load multiple copies of tab.js into the tab...

The sample addon can be found here for testing:

https://builder.addons.mozilla.org/addon/1001314/latest/

HTH

pd

unread,
Mar 31, 2011, 11:10:14 AM3/31/11
to mozilla-labs-jetpack
Thanks heaps Daniel. My development has stalled of late due to Firefox
4 video acceleration BSoD fun amongst other things. I did get a chance
to notice a post about 1.0b4 though and hopefully you can the
highlighted section here:

http://diigo.com/0gdfx

but just in case:

"New tab property added to the Worker API, which returns the tab
associated with that worker. This is primarily useful to fetch the tab
associated with a page-mod's content scripts."

Is that relevant to this topic? It sounds like it is but I'm not sure.
>    - May need to wait for the dom to be loaded before adding the button
>    - Not sure if the way I have the content script attaching to the tab will

Daniel Goodwin

unread,
Apr 4, 2011, 12:40:09 PM4/4/11
to mozilla-la...@googlegroups.com
I havent had a chance to look into that change, but based on this example in the docs:

const workers = require("content/worker");
let worker =  workers.Worker({
  window: require("window-utils").activeWindow,
  contentScript: "onMessage = function(data) { " +
                 "  postMessage(window.location + ': Hi ' + data.name); " +
                 "};",
  onMessage: function(msg) {
    console.log(msg);
  }
});
worker.postMessage({ name: 'worker'});

it looks like worker will refer to the contentScript of which ever tab is currently active. That's cool.
Reply all
Reply to author
Forward
0 new messages