John J Barton | 9 Feb 19:15

connection ceremony for iframe postMessage communications

Recently I've been working with iframe messaging. The postMessage
solution has a lot of advantages and good traction across iframes,
WebWorkers, and browser extensions, with lots of overlap with Web
Sockets.

However the technology has two significant problems.  First is the
"contentWindow that is not a window" confusion I discussed recently.
Second concerns the connection setup. I describe the second problem
here.

The basic communications solution is simple enough:
  window.addEventListener('message', handler, false);  // I'm listening!
  portToOtherWindow.postMessage(message);  // I'm talking to you!

However the solution has two significant problems:
  1. There is no way to know if portToOtherWindow is connected before
you issue postMessage()
  2. All iframes send messages to the same "handler".

The first problem arises because web apps are increasingly
asynchronous for load performance and other reasons.

This leads developers to look for events that will tell them about
'load' on iframes, and that leads them to try
iframe.contentWindow.addEventListener(). It works fine for same-domain
iframes, but fails for cross-domain.

The second problem arises because the handler is attached to the
window and not to an object related to the connection between the two
windows.
(Continue reading)

Boris Zbarsky | 9 Feb 20:49
Picon
Favicon

Re: connection ceremony for iframe postMessage communications

On 2/9/12 1:15 PM, John J Barton wrote:
> This leads developers to look for events that will tell them about
> 'load' on iframes, and that leads them to try
> iframe.contentWindow.addEventListener(). It works fine for same-domain
> iframes, but fails for cross-domain.

Adding a load listener to the iframe element itself should work for 
this, no?

That doesn't help with the second problem, of course....

-Boris

John J Barton | 9 Feb 21:17

Re: connection ceremony for iframe postMessage communications

On Thu, Feb 9, 2012 at 11:49 AM, Boris Zbarsky <bzbarsky <at> mit.edu> wrote:
> On 2/9/12 1:15 PM, John J Barton wrote:
>>
>> This leads developers to look for events that will tell them about
>> 'load' on iframes, and that leads them to try
>> iframe.contentWindow.addEventListener(). It works fine for same-domain
>> iframes, but fails for cross-domain.
>
>
> Adding a load listener to the iframe element itself should work for this,
> no?

I guess you mean: by issuing
  iframe.addEventListener('load', handler, false);
you get notified when the iframe load event has completed (but you
don't need to touch the contentWindow property).

This will work if the iframe ensures that it completes its connection
work before 'load'. This prevents the iframe from using async loading
for the scripts that create the connection and for any code that
handles messages from the parent. Which, in a typical iframe
component, would be all the code, since its main job is to provide for
the parent.

In addition this solution requires that the above addEventListener be
attached after the iframe is inserted (so the iframe exists) but
before the parent's 'load' event (which is after the iframe load and
thus too late.

So I'd say it does not solve the original problem and it's hard to use
(Continue reading)

John J Barton | 10 Feb 00:43

Re: connection ceremony for iframe postMessage communications

On Thu, Feb 9, 2012 at 11:49 AM, Boris Zbarsky <bzbarsky <at> mit.edu> wrote:
> That doesn't help with the second problem, of course....

Ok here are some ideas, riffing off the web messaging doc

1 To iframe element add:
    readonly attribute MessagePort port;

'message' events from the iframe to the containing window (via
window.parent.postMessage) will be delivered to any port listener (as
well as the global window handler).
   This solves the multiplexing part, the container listens to a
per-iframe object.

'connect' event would be raised and delivered (synchronously) as soon
as the iframe issues window.parent.addEventListener('message'...) and
vice versa.
   This solves the async start up part, each side waits for 'connect'
before issuing its first postMessage. The 'connect' for the
second-place racer triggers the first real message.

Pro: also solves the
cross-domain-iframes-don't-really-have-contentWindow problems I
discussed before.
       familiar addEventListener API, reuses MessagePort
       existing iframe code would just work

2. Have HTMLIFrameElement implement MessagePort.
  This is similar to #1 but the message port functions are attached to
the iframe element directly rather than to its port property.
(Continue reading)

Ian Hickson | 10 Feb 01:42
Picon

Re: connection ceremony for iframe postMessage communications

On Thu, 9 Feb 2012, John J Barton wrote:
> 
> However the solution has two significant problems:
>   1. There is no way to know if portToOtherWindow is connected before
> you issue postMessage()

Just have the target message you when it's ready.

>   2. All iframes send messages to the same "handler".

Pass a MessagePort to the target when you start a new conversation, and 
do the rest of the conversation on that.

--

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

John J Barton | 10 Feb 06:41

Re: connection ceremony for iframe postMessage communications

On Thu, Feb 9, 2012 at 4:42 PM, Ian Hickson <ian <at> hixie.ch> wrote:
> On Thu, 9 Feb 2012, John J Barton wrote:
>>
>> However the solution has two significant problems:
>>   1. There is no way to know if portToOtherWindow is connected before
>> you issue postMessage()
>
> Just have the target message you when it's ready.

Ah, ok, just to translate (in case anyone understood what I was
talking about before): there already exists an out-of-band
introduction system, the global postMessage(), which can be used to
set up the in-band channel by sending MessageChannel ports as
Transferables.

Let me see if I can understand this.
  Both sides create MessageChannel objects;
  both sides window.addEventListener('message', handler, false)
  both sides issue other.postMessage("...", "*", [channel.port2]);

The second-place finisher in the race succeeds in posting its port2 to
the first-place racer. The first-place racer knows it 'won' because it
gets the port. But how does the second-place racer know it should use
channel.port1 rather than continue waiting? I guess the first-place
racer can send an ACK.

If yes, then this ACK message needs to be standard for cross-domain iframes.

We also need the containing window's global introduction handler to
associate the given port with the correct iframe. The difficulty here
(Continue reading)

Ian Hickson | 10 Feb 08:53
Picon

Re: connection ceremony for iframe postMessage communications

On Thu, 9 Feb 2012, John J Barton wrote:
> On Thu, Feb 9, 2012 at 4:42 PM, Ian Hickson <ian <at> hixie.ch> wrote:
> > On Thu, 9 Feb 2012, John J Barton wrote:
> >>
> >> However the solution has two significant problems:
> >>   1. There is no way to know if portToOtherWindow is connected before
> >> you issue postMessage()
> >
> > Just have the target message you when it's ready.

What I meant was just to do this on the receiving side (inside the 
iframe), after the onmessage handler has been set up (which we are 
assuming happens after the 'load' event for some reason):

   parent.postMessage('load', '*');

That way you don't have to depend on the 'load' event, you can just wait 
for the message from the inner frame. Then when you get it, you know you 
can start sending..

And when you do send, you just send a message whose contents are just a 
single key saying what API endpoint you want, and a port, which you then 
use for all communication for that particular API call.

No races or anything.

--

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'
(Continue reading)

John J Barton | 10 Feb 18:00

Re: connection ceremony for iframe postMessage communications

On Thu, Feb 9, 2012 at 11:53 PM, Ian Hickson <ian <at> hixie.ch> wrote:
> On Thu, 9 Feb 2012, John J Barton wrote:
>> On Thu, Feb 9, 2012 at 4:42 PM, Ian Hickson <ian <at> hixie.ch> wrote:
>> > On Thu, 9 Feb 2012, John J Barton wrote:
>> >>
>> >> However the solution has two significant problems:
>> >>   1. There is no way to know if portToOtherWindow is connected before
>> >> you issue postMessage()
>> >
>> > Just have the target message you when it's ready.
>
> What I meant was just to do this on the receiving side (inside the
> iframe), after the onmessage handler has been set up (which we are
> assuming happens after the 'load' event for some reason):
>
>   parent.postMessage('load', '*');
>
> That way you don't have to depend on the 'load' event, you can just wait
> for the message from the inner frame. Then when you get it, you know you
> can start sending..

The problem here is that the iframe may issue
parent.postMessage('load', '*") before the parent onmessage handler
has been set up. Modern apps no longer use single-point
synchronization.  The parent window 'load' event has no time relation
to the onmessage handler setup, neither the iframe load event.

Instead we have multiple synchronizations based on the dependency
relationships. This started with script loading but eventually all
content will be loaded this way.
(Continue reading)

Ian Hickson | 10 Feb 19:58
Picon

Re: connection ceremony for iframe postMessage communications

On Fri, 10 Feb 2012, John J Barton wrote:
> >
> > What I meant was just to do this on the receiving side (inside the 
> > iframe), after the onmessage handler has been set up (which we are 
> > assuming happens after the 'load' event for some reason):
> >
> >   parent.postMessage('load', '*');
> >
> > That way you don't have to depend on the 'load' event, you can just 
> > wait for the message from the inner frame. Then when you get it, you 
> > know you can start sending..
> 
> The problem here is that the iframe may issue parent.postMessage('load', 
> '*") before the parent onmessage handler has been set up.

You can always guarantee that you've set up your handler before you create 
the iframe. But, suppose that's somehow not possible. Then you just define 
"ping" as a message you can send to the inner frame, which the inner frame 
then responds to with the aforementioned "load" message.

So now you have the following situations:

 - parent is set up first, then opens iframe:
    - iframe sends 'load' message when ready

 - parent opens iframe, then sets up communications, iframe is quicker:
    - iframe sends 'load' message when ready, but it gets missed
    - parent sends 'ping' message
    - iframe sends 'load' message in response

(Continue reading)

John J Barton | 10 Feb 20:33

Re: connection ceremony for iframe postMessage communications

On Fri, Feb 10, 2012 at 10:58 AM, Ian Hickson <ian <at> hixie.ch> wrote:
> On Fri, 10 Feb 2012, John J Barton wrote:
>>
>> Just to clarify, I want to see the layer you just outlined be standard
>> so we can design iframe components and apps to mix and match. This can
>> be two simple layers on the current messaging: 1) the connection
>> ceremony, 2) the key/API format.
>
> No reason for it to be standard, just define it as part of the protocol
> you are implementing over postMessage().

Ok, so I define it for my app. You write an iframe. You read my
definition and I can load your iframe. Yay!

Then Boris write an app. He defines it for his app. You change your
iframe to deal with my app and Boris' app. Ok.

Then Mark Zuckerberg and Larry Page define apps. Soon you are spending
all of your money hiring devs to deal with connection protocols.

Then maybe you will have a reason for a standard?

jjb

>
> --
> Ian Hickson               U+1047E                )\._.,--....,'``.    fL
> http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
> Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

(Continue reading)

Ian Hickson | 10 Feb 22:37
Picon

Re: connection ceremony for iframe postMessage communications

On Fri, 10 Feb 2012, John J Barton wrote:
> On Fri, Feb 10, 2012 at 10:58 AM, Ian Hickson <ian <at> hixie.ch> wrote:
> > On Fri, 10 Feb 2012, John J Barton wrote:
> >>
> >> Just to clarify, I want to see the layer you just outlined be 
> >> standard so we can design iframe components and apps to mix and 
> >> match. This can be two simple layers on the current messaging: 1) the 
> >> connection ceremony, 2) the key/API format.
> >
> > No reason for it to be standard, just define it as part of the 
> > protocol you are implementing over postMessage().
> 
> Ok, so I define it for my app. You write an iframe. You read my 
> definition and I can load your iframe. Yay!
> 
> Then Boris write an app. He defines it for his app. You change your 
> iframe to deal with my app and Boris' app. Ok.
> 
> Then Mark Zuckerberg and Larry Page define apps. Soon you are spending 
> all of your money hiring devs to deal with connection protocols.
> 
> Then maybe you will have a reason for a standard?

Why would the connectivity part of this be the hard part? Each of these 
apps has an entire protocol you'll have to reimplement if you want to 
connect to it! The connectivity part is trivial in comparison.

I'm all for people standardising their postMessage() protocols, though, 
if they want to. Nothing is stopping people from doing so.

(Continue reading)

John J Barton | 11 Feb 00:57

Re: connection ceremony for iframe postMessage communications

On Fri, Feb 10, 2012 at 1:37 PM, Ian Hickson <ian <at> hixie.ch> wrote:
> On Fri, 10 Feb 2012, John J Barton wrote:
>> On Fri, Feb 10, 2012 at 10:58 AM, Ian Hickson <ian <at> hixie.ch> wrote:
>> > On Fri, 10 Feb 2012, John J Barton wrote:
>> >>
>> >> Just to clarify, I want to see the layer you just outlined be
>> >> standard so we can design iframe components and apps to mix and
>> >> match. This can be two simple layers on the current messaging: 1) the
>> >> connection ceremony, 2) the key/API format.
>> >
>> > No reason for it to be standard, just define it as part of the
>> > protocol you are implementing over postMessage().
>>
>> Ok, so I define it for my app. You write an iframe. You read my
>> definition and I can load your iframe. Yay!
>>
>> Then Boris write an app. He defines it for his app. You change your
>> iframe to deal with my app and Boris' app. Ok.
>>
>> Then Mark Zuckerberg and Larry Page define apps. Soon you are spending
>> all of your money hiring devs to deal with connection protocols.
>>
>> Then maybe you will have a reason for a standard?
>
> Why would the connectivity part of this be the hard part?

Because the existing information on cross-domain iframe communications
is incomplete and written in terms few Web app developers understand,
the browser implementations are new and the error messages they emit
are puzzling. Solutions for same-domain cases don't work for
(Continue reading)

Ian Hickson | 13 Feb 21:57
Picon

Re: connection ceremony for iframe postMessage communications

On Fri, 10 Feb 2012, John J Barton wrote:
> >
> > Why would the connectivity part of this be the hard part?
> 
> Because the existing information on cross-domain iframe communications 
> is incomplete and written in terms few Web app developers understand, 
> the browser implementations are new and the error messages they emit are 
> puzzling. Solutions for same-domain cases don't work for cross-domain. 
> Async communications is hard to debug.

I agree with your described problems, but I don't see the link between 
them and adding yet more features to the platform.

The solution to "existing information on cross-domain iframe 
communications is incomplete" is to add more information. The solution to 
"existing information on cross-domain iframe communications is written in 
terms few Web app developers understand" is to write new information. The 
solution to "the browser implementations are new" is to wait. The solution 
to "the error messages they emit are puzzling" is to file bugs on the 
browsers with suggestions for how to improve them. Etc.

--

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

John J Barton | 13 Feb 22:35

Re: connection ceremony for iframe postMessage communications

On Mon, Feb 13, 2012 at 12:57 PM, Ian Hickson <ian <at> hixie.ch> wrote:
> On Fri, 10 Feb 2012, John J Barton wrote:
>> >
>> > Why would the connectivity part of this be the hard part?
>>
>> Because the existing information on cross-domain iframe communications
>> is incomplete and written in terms few Web app developers understand,
>> the browser implementations are new and the error messages they emit are
>> puzzling. Solutions for same-domain cases don't work for cross-domain.
>> Async communications is hard to debug.
>
> I agree with your described problems, but I don't see the link between
> them and adding yet more features to the platform.

Oh, sorry, I answered the question you asked, rather than the one you
wanted to ask ;-)

"Why should the connectivity part be part of the platform?"

To simplify and thus encourage cross-domain application development as
a natural and powerful extension of the Web.

Multiple incompatible connection sequences exist and will naturally
arise. One may eventually dominate. The process will be long and
painful; the advantages of competition seem low. By pursuing a
standard connection we accelerate the process.

A different push-back would make sense to me: it's not yet time. By
having this discussion we put the possibility on the table.  Already
I've learned how to use the MessageChannel solution and that
(Continue reading)

John J Barton | 11 Feb 00:44

Re: connection ceremony for iframe postMessage communications

On Fri, Feb 10, 2012 at 10:58 AM, Ian Hickson <ian <at> hixie.ch> wrote:
> On Fri, 10 Feb 2012, John J Barton wrote:
>> >
>> > What I meant was just to do this on the receiving side (inside the
>> > iframe), after the onmessage handler has been set up (which we are
>> > assuming happens after the 'load' event for some reason):
>> >
>> >   parent.postMessage('load', '*');
>> >
>> > That way you don't have to depend on the 'load' event, you can just
>> > wait for the message from the inner frame. Then when you get it, you
>> > know you can start sending..
>>
>> The problem here is that the iframe may issue parent.postMessage('load',
>> '*") before the parent onmessage handler has been set up.
>
> You can always guarantee that you've set up your handler before you create
> the iframe. But, suppose that's somehow not possible. Then you just define
> "ping" as a message you can send to the inner frame, which the inner frame
> then responds to with the aforementioned "load" message.
>
> So now you have the following situations:
>
>  - parent is set up first, then opens iframe:
>    - iframe sends 'load' message when ready
>
>  - parent opens iframe, then sets up communications, iframe is quicker:
>    - iframe sends 'load' message when ready, but it gets missed
>    - parent sends 'ping' message
>    - iframe sends 'load' message in response
(Continue reading)

Ian Hickson | 11 Feb 00:56
Picon

Re: connection ceremony for iframe postMessage communications

On Fri, 10 Feb 2012, John J Barton wrote:
> 
> I think there are two more cases. Because the messages are all async,
> the 'it gets missed case" can be "it gets delayed". That causes
> additional messages.
> 
>   - parent opens iframe, then sets up communications, iframe is quicker:
>    -  iframe sends 'load' message when ready, but it gets delayed
>    - parent sends 'ping' message
>    - parent get first 'load' message, responds with port
>    - iframe sends 'load' message in response to 'ping'

Yeah, after receiving the first 'load', the parent should stop listening 
for more 'load's.

>  - parent opens iframe, then sets up communications, parent is quicker:
>    - parent sends 'ping' message, but it gets delayed
>    - iframe sends 'load' message when ready
>    - iframe gets 'ping' message, sends 'load' message in response
>    - parent gets 'load' message, responds with port

That's equivalent to the case of the parent not hooking things up until 
after the iframe is ready.

> This doe not change your conclusion, "the first 'load' message indicates 
> ready", but I believe it does mean that a third message (sending the 
> port) is needed.

Well, probably far more than three messages -- each time you want to start 
a conversation you would send a port for that conversation. (By 
(Continue reading)

Charles Pritchard | 11 Feb 01:58

Re: connection ceremony for iframe postMessage communications

On 2/10/2012 3:44 PM, John J Barton wrote:
> Thanks. As a hint for the next person, it seems like the asymmetric
> messages (parent 'ping', iframe 'load') is easier than symmetric
> ('hello'/'ack')
>
> I think there are two more cases. Because the messages are all async,
> the 'it gets missed case" can be "it gets delayed". That causes
> additional messages.

I've been pursuing something like this for transferring content between 
frames. That's part of why I was excited to see Web Intents taking off.
I also went with a ping mechanism for messaging.

I recommend taking this thread over to the Web Intents list and 
examining it there. I see every reason to want a loose idea of what Web 
Intents over postMessage would look like.
They have a shim mechanism for demo purposes and backwards 
compatibility, but they don't have the same focus you do (or I do), on 
iframe postMessage.

-Charles


Gmane