MESSAGES ======== A DOM messaging proposal. Use case: Consider a page that embeds two gadgets: a mail gadget and a calendar gadget. The mail gadget wants to provide other gadgets with a way to read the names of the people in its address book. The calendar gadget wants to be able to get a list of names of people. The page that embeds them wants to act as mediator. Analysis: There are various ways of doing this, including, for example, the main page simply acting as a proxy for all the messages. However, this requires that the three parties keep careful track of all the conversations they're in. An alternative would be for the calendar to ask the main page for access to a list-of-names API, passing with it one end of a pipe, the other end of which is associated with a function that just deals with getting a list-of-names API. Then, the main page looks around, asking its gadgets for someone who supports list-of-names APIs. Again, along with its message it sends a (different) pipe that just knows how to handle getting one of these APIs. When it gets one, it passes the original end point down the pipe that it receives, and the other end begins talking to the calendar directly. (An example for this is shown at the bottom.) PROPOSAL: interface Window { // ... EndPointPair getNewEndPoints(); // extend postMessage like this: void postMessage(in DOMString message, in EndPoint endPoint); void postMessage(in DOMString message, in EndPoint endPoint, in DOMString origin); } interface EndPointPair { readonly attribute EndPoint endPoint1; readonly attribute EndPoint endPoint2; } interface EndPoint { // also supports EventTarget void postMessage(in DOMString message); void postMessage(in DOMString message, in EndPoint endPoint); void close(); attribute EventListener onmessage; attribute EventListener onunload; readonly attribute boolean active; } interface EndPointMessageEvent : Event { // ... readonly attribute DOMString message; readonly attribute EndPoint endPoint; // if any was passed } Each EndPoint internally is always associated with a Window object, and can also be associated (entangled) with another EndPoint object. getNewEndPoints() returns a newly created EndPointPair object with two newly created EndPoint objects, both of which have active=true, each of which is entangled with the other, and both of which are associated with the Window object on which getNewEndPoints() was called. EndPoint.postMessage(...) does nothing if it is called while active=false. (Should it instead raise an INVALID_STATE_ERR exception?) EndPoint.postMessage(m, null) dispatches an EndPointMessage event on the other EndPoint of the pair, with message=m and endPoint=null. e1.postMessage(m, e3) dispatches an EndPointMessage event on the EndPoint e2 that e1 is entangled with, with message=m and endPoint=e4, where e4 is constructed as follows: If the argument e3 has active=false, raise an INVALID_STATE_ERR exception. Create a new EndPoint object, call it e4. Entangle e4 with the same EndPoint as the e3 argument is entangled with, and then unentangle e3. Set e3.active to false. Associate e4 with the same Window object as e1. window.postMessage(m, [e1], [o]) posts a message to another window like now, except that if the second argument is an EndPoint object, then the MessageEvent also has an endPoint member, e2, initialised as follows: If the argument e1 has active=false, raise an INVALID_STATE_ERR exception. Create a new EndPoint object, call it e2. Entangle e2 with the same EndPoint as the e1 argument is entangled with, and then unentangle e1. Set e1.active to false. Associate e2 with the Window object window. When an EndPoint object's close() method is called, both EndPoint objects in the pair must have their 'active' attribute set to false. When a Window that has EndPoints associated with it is navigated to a different document, the EndPoints get put in the History the same way as the Document, grouped with the document, and these EndPoints must each have their other EndPoint's active attribute set to false. If the Document is ever made active again, these EndPoints must each have their other EndPoint's active attribute set to true. When a Window is closed, the EndPoints associated with it must each have their other EndPoint's active attribute set to false. An EndPoint must prevent its entangled EndPoint from being garbage collected if that EndPoint has any event handlers attached, unless neither could be reached, in which case they can both be garbage collected. If an EndPoint with no event handlers attached is garbage collected, its entangled EndPoint must have its active attribute set to false. All calls to all postMessage()s that are associated with or include as their arguments an EndPoint, are asychronous. EXAMPLE: This is the above example. Calendar widget: : getListOfNamesAPI(callback); // callback is a function that gets the list of names : function getListOfNamesAPI(callback) { var pipe = getNewEndPoints(); pipe.endPoint1.onmessage = function (event) { // assuming all went well, event.endPoint is an API for us callback(event.message); // send the list of names back to the callback }; window.parent.postMessage('get list-of-names', pipe.endPoint2); } Main page: window.onmessage = function (event) { if (event.message == 'get list-of-names') { var gadgetWindow = getGadgetThatSupports('list-of-names'); // ... gadgetWindow.postMessage('get list-of-names', event.endPoint); } else { // unknown request } }; Mail widget: window.onmessage = function (event) { if (event.message == 'get list-of-names') { sendNames(event.endPoint); } else { // unknown request } };