[whatwg] onclose events for MessagePort

Jonas Sicking jonas at sicking.cc
Fri Dec 13 23:06:07 PST 2013

On Fri, Dec 13, 2013 at 3:29 PM, Ian Hickson <ian at hixie.ch> wrote:
> On Wed, 11 Dec 2013, Jonas Sicking wrote:
>> No sync IPC needed. When a port is pinned, you send an async message to
>> the process which contains the page for the "other side". When that
>> process receives the message you check if the page is currently being
>> displayed.
>> If the page has been completely torn down then you send a message back
>> saying that the page is dead and that the promise created during pinning
>> should be rejected.
>> If the page is sitting in the bfcache, you remove it from the bfcache
>> and send a message back saying that the page is dead and that the
>> promise created during pinning should be rejected.
>> If the page is displayed, then you add a flag indicating that if the
>> page is navigated away from, it should not go into the bfcache and that
>> we should send a signal to reject the promise.
>> Obviously if the process had crashed before we were able to process the
>> event, you send a message back to reject the promise.
>> The same thing is done when unpinning. You send a message to the other
>> side saying that it's getting unpinned.
> This means that it's possible to get a lock, have the other side navigate
> then go back, then have the other side receive the notification for the
> lock. It's this that you need blocking IPC to prevent. But I guess we
> could live with that just being possible.

Indeed. The idea with bfcache is that going back/forward should be
largely transparent to the page itself. So I think it's fine that it's
transparent also to the page that's talking to it in this instance.

Kicking the page out of bfcache isn't a goal in and of itself. The
goal is to prevent other pages from waiting unduly long for a message.

> If we do want to address use cases 3 and 4 above, then I agree that the
> event in the spec today isn't sufficient.
> I'm not sure a promise makes a lot of sense as an alternative, though. To
> make that work, you'd need to have the UA reject the promise but the
> author resolve it, which seems kind of unusual for promise-based APIs. You
> also end up, as author, being simultaneously the consumer of the promise
> and the resolver, which is a bit weird too.

I think this is a matter of taste in how to use promises. I'm happy to
confer with people that have more experiences with APIs that use
promises to see if we're abusing the Promise API here.

> Also, I'm not sure it really solves all the use cases. As an author, one
> of the ways in which I use channels (in any environment, so not just those
> that use MessagePorts, but also e.g. linux pipes, WebSockets, etc) is as a
> notification mechanism. As David Barrett-Kahn implied earlier, it's no
> good if you're listening to notifications, and then they stop, but you
> never get told that they've stopped. The pinning mechanism would basically
> require authors who are consuming such streams from other tabs to pin when
> they opened the channel, and leave it pinned forever. To detect when the
> channel is dead, they'd have to listen for rejection on the promise, an
> object separate from the port. This seems unintuitive.

I don't see how it doesn't solve the use case? Whether the use of
Promises here is awkward or not I agree is a matter of taste.

> But in such a scenario, would you even _want_ to block bfcaching? You'd
> want to know when it was _never_ coming back, as in worker.terminate().
> But if it went into the bfcache, don't you really just want to receive a
> message saying "I'm actually not around right now"?

We could allow bfcaching here by introducing the discussed separate
features which allow the page to signal that it's fine with being
bfcached even though the error was signaled. That way if the page is
revived from bfcache it can send a message on the channel saying "I'm
back now" at which point they can resume communicating.

> Maybe, and I'm just brainstorming here, we should deal with the bfcache
> differently than termination/oom/crash. How about we provide an API that
> defines an "autoresponder" message that gets sent when a port receives a
> message but isn't around to handle it? Or maybe an API that defines a
> message to send when the worker is suspended / tab is navigated away from,
> and another that sends a message when the worker/port is resumed. Or maybe
> instead of an API that defines a prerecorded message, we just fire a
> different event. So you'd have:
>    port.onmessage - received a message
>    port.onerror - other side crashed or was terminate()d
>    port.onsuspend - other side went to sleep
>    port.onresume - other side came back

Wouldn't this expose GC behavior? If an unreferenced channel is GCed
before the page navigates then port.onsuspend is not fired. If the
same unreferenced channel is not GCed before the page navigates then
port.onsuspend is fired.

Basically you'd have to prevent GCing of a channel where either side
has a onsuspend listener.

Additionally as the only implementation of a bfcaching browser this is
a risky API. In all other browsers, once onsuspend has fired the
channel is dead and no events will fire.

However in Gecko once the page is revived from bfcache it would now
start sending messages to the other side again.

I'm happy to enable this behavior, but it's something that I'd want
pages to opt in to very explicitly.

/ Jonas

More information about the whatwg mailing list