Page Mods API proposal: Auto-wrapped content-to-addon calls

13 views
Skip to first unread message

Daniel

unread,
Jan 3, 2011, 2:24:10 PM1/3/11
to mozilla-labs-jetpack
To provide the most ergonomic API methods for Page Mods (and
potentially other APIs that employ a post-message style architecture)
I would like to float the idea of automatically wrapping and injecting
method handlers into the content page that would mirror the methods
developers create in the add-on sandbox. Here is a little pseudo code
to help visualize this: http://paste.mootools.net/f1236748

Essentially we would introduce a messageHandler parameter. This
parameter would be the place you declare your page mod's chrome-side
functions. Behind the scenes, the SDK would take your functions, wrap
them in the message protocol automatically, and allow you to then call
these functions in the content page by the name you declared in the
messageHandler param.

In talking to some of the developers they thought this was a really
nice bit of polish that conceptually connects the dots. It makes it
easier to see the parity between content and the chrome-side add-on
sandbox.

Let me know what you all think!

- Daniel

Brian Warner

unread,
Jan 4, 2011, 6:11:38 PM1/4/11
to mozilla-la...@googlegroups.com

On 1/3/11 11:24 AM, Daniel wrote:

> Essentially we would introduce a messageHandler parameter. This
> parameter would be the place you declare your page mod's chrome-side
> functions. Behind the scenes, the SDK would take your functions, wrap
> them in the message protocol automatically, and allow you to then call
> these functions in the content page by the name you declared in the
> messageHandler param.

var pageMod = require("page-mod");
pageMod.PageMod({
include: "*.org",
contentScriptWhen: 'ready',
contentScript: 'reportLocation(window.location.href);'
messageHandlers:{
reportLocation: function(location){
require("notifications").notify({
title: "Page Location Notification",
text: "You are on the following page: " + location
});
}
}
});

Neat! I kind of like it. My main concern is whether it might trick new
developers into believing that the content script gets synchronous
direct access to the main addon process, when in fact it is limited to
doing postMessage with JSON data. The current approach, in which you can
see "postMessage()" all over the content script, makes this fairly
obvious. Adding some sugar to abstract out the setup and dispatch code,
while making things a lot easier to read overall, would happen to lose
this educational feature. For example, a newcomer might try to do this:


contentScript: 'if (isItInsecure(window.location.href)) {'+
'document.body.innerHTML="OMG! INSECURE!";}',
messageHandlers:{
isItInsecure: function(location){
require("notifications").notify({
title: "Page Location Notification",
text: "Insure Page Loaded: " + location
});
if (location.match(/^http:/)) return true; else return false;
}
}

I'm not sure how to balance the two desires. Maybe just some clear docs
and counter-examples (for instance, the above code with a lot of "THIS
DOESN'T WORK (and why)" labels on it).


Just for an experiment, what if we abstracted out the receive-side
dispatcher but didn't try to wrap the sender? I think it'd then look
like this:

contentScript: 'postMessage("reportLocation", window.location.href);'
messageHandlers:{
reportLocation: function(location){
require("notifications").notify({
title: "Page Location Notification",
text: "You are on the following page: " + location
});
}
}

Another thought is to put code into the "messageHandlers"-dispatcher
that notices whenever a handler returns a value, since this is never
useful in the handler for a send-and-forget postMessage (there'll be
no-one around to see the response). The dispatcher should throw an
exception or something, with an error message to explain to the
developer what they're doing wrong.

One other thought is how to handle misspelled messages, or messages
coming from other raw postMessage() calls in the content script. I
suppose that trying to call e.g. reportPosition(), when messageHandlers
only contains "reportLocation", will cause an error in the content
script, because it's trying to call an undefined function, so that's
pretty direct feedback.

Should the messageHandlers dispatcher's onListen() callback ignore
messages that it doesn't recognize? Probably yes.. otherwise it could be
hard to let it coexist with other messaging schemes.

thoughts?
-Brian

Daniel

unread,
Jan 5, 2011, 1:56:25 AM1/5/11
to mozilla-la...@googlegroups.com
I believe that removing the sugary, syntactical wrapping from one side or the other of the message transaction really reduces the benefit of the mechanism.

I believe that clear documentation and simply using the API will make it quite clear that there is not an synchronous bridge from one context to another.

The misspelled message concern is not really an issue, because if I tried to call reportPosition() instead of reportLocation() an error would be thrown in the page when I test it because that function will not exist in the page's context.

I do agree with the onListen() callback stance you highlighted, that makes sense.

- Daniel


Irakli Gozalishvili

unread,
Jan 5, 2011, 7:07:13 AM1/5/11
to mozilla-la...@googlegroups.com
Hi,

I think keeping APIs minimalistic & keeping doors open for libs implement sugar on top as third party libs is way to go. This would allow lib devs to try out different alternatives like the one proposed here and others (I personally would love to do: http://wiki.ecmascript.org/doku.php?id=strawman:concurrency https://github.com/kriskowal/q-comm). That would allow us to pick a winner and include it into standard library in a next version of the sdk. Also such lib / libs may be promoted via links in the docs to make them more accessible.

Regards
--
Irakli Gozalishvili
Web: http://www.jeditoolkit.com/
Address: 29 Rue Saint-Georges, 75009 Paris, France





--
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.

Atul Varma

unread,
Jan 5, 2011, 11:03:49 AM1/5/11
to mozilla-la...@googlegroups.com, Irakli Gozalishvili
Is there a way we can provide a "stopgap" API now and deprecate it later? While it's cool that folks can use third-party APIs, it's also not great that things are so hard to use out-of-the-box for such common use cases.

At the very least, pointing to a stable third-party library that provide a nicer interface in the SDK docs might be helpful, since the step from "using the SDK by itself" to "using the SDK with third-party libraries" can be scary for beginners--or even seasoned veterans who are just concerned about the stability of third-party libraries.

- Atul

Alexandre poirot

unread,
Jan 5, 2011, 11:42:43 AM1/5/11
to mozilla-la...@googlegroups.com
There is a concurrent discussion in this thread, about postMessage:
http://groups.google.com/group/mozilla-labs-jetpack/browse_thread/thread/a5b33a1335f21904?hl=en

I suggested almost same things than Brian:
 - Add a "message name", this is a so common use case! You almost always have multiple messages type to send back. And "messageHandlers" syntax sugar proposed by Brian works very well with this!
 - I'm confused with the lack of communication capabilities between panel/widget document and scripts that create them. And that lead to misunderstanding between both postMessage from content script and panel/widget document.
(We can't use document postMessage as it's the HTML5 ones)

And again, I have the same point of view than Brian on adding functions in global pageworker scope.
It would be really harmfull for newcomers that won't read documentation and build jetpack mainly with copy/paste and some basic javascript knowledges.

One last remark. I find chrome approach pretty neat by using only one "chrome" global, which highlight cleary what is plain standard javascript and chrome specific stuff.
http://code.google.com/chrome/extensions/content_scripts.html#host-page-communication
In this example, we really now that postMessage is related to chrome content script.
And again note that there is `message` name attribute in their simplest example!



2011/1/5 Atul Varma <ava...@mozilla.com>

Irakli Gozalishvili

unread,
Jan 6, 2011, 7:33:13 AM1/6/11
to mozilla-la...@googlegroups.com
I think it's a good idea to get rid of global's in content script, but in that case I would prefer something like

require('chrome').postMessage

Instead of copying Google's approach, also there were people requesting ability to share code between add-on modules and content scripts. This would also allow extensions like this one

// content module
require('message-dispatcher).postMessage(topic, data);

// add-on module

require('mod-dispatcher').{

   include: "*.org",
   contentScriptWhen: 'ready',
   contentScript: 'reportLocation(window.location.href);'
   messageHandlers:{
       reportLocation: function(location){
           require("notifications").notify({
               title: "Page Location Notification",
               text: "You are on the following page: " + location
           });
       }
   }
});


Irakli Gozalishvili

unread,
Jan 6, 2011, 7:52:30 AM1/6/11
to Atul Varma, mozilla-la...@googlegroups.com
On Wed, Jan 5, 2011 at 17:03, Atul Varma <ava...@mozilla.com> wrote:
Is there a way we can provide a "stopgap" API now and deprecate it later? While it's cool that folks can use third-party APIs, it's also not great that things are so hard to use out-of-the-box for such common use cases.


I don't agree that it's that hard maybe some more examples in docs may help to clarify that. Here is a [gist](https://gist.github.com/767840) of proposed API and snippet of code that does the same with a current API. If you compare you'll notice that there is only two lines diff.

- contentScript: 'reportLocation(window.location.href);'
+ contentScript
: 'postMessage({ handler: "reportLocation", data: window.location.href });',
+ onMessage
: function({ handler, data }) { this.messageHandlers[handler](data); },

Also I think it's more explicit that all the communication is async. Probably it will be a good idea to add this example to the of page mod.
 

Daniel

unread,
Jan 10, 2011, 5:43:09 PM1/10/11
to mozilla-la...@googlegroups.com, Atul Varma
There are many side benefits to this proposal as well. I am willing to venture developers will devise various similar handling methods to make this easier for them. Some will choose to create separate functions in their add-on code for all the various actions they need to perform and pass the message data to each, others may create large switch statements in their message receiver functions and handle the logic cases in a more monolithic-code-block fashion.

The point is, message handling when you have more than a simple action or if/else style logic block becomes annoying. This would relieve that annoyance in a way that is backwards compatible with the current code base, and should cost us very little to implement.

I feel the idea of developers being unable to progress beyond some perplexed state induced by the wrapped API's ergonomics is a corner case at best.

- Daniel
Reply all
Reply to author
Forward
0 new messages