This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 22628 - Define broadcast to same-origin globals API
Summary: Define broadcast to same-origin globals API
Status: RESOLVED FIXED
Alias: None
Product: WHATWG
Classification: Unclassified
Component: HTML (show other bugs)
Version: unspecified
Hardware: PC All
: P2 enhancement
Target Milestone: 2014 Q2
Assignee: Ian 'Hixie' Hickson
QA Contact: contributor
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-07-09 20:45 UTC by Anne
Modified: 2013-11-13 19:09 UTC (History)
10 users (show)

See Also:


Attachments

Comment 1 Ian 'Hixie' Hickson 2013-07-09 23:35:35 UTC
> http://blog.fastmail.fm/2012/11/26/inter-tab-communication-using-local-
> storage/

| Ideally, we would like to maintain a single push connection and share it
| between the tabs, but there’s no API [...]

SharedWorkers are exactly that. This use case isn't a use case for broadcasting, it's a perfect textbook use case for shared workers.


> http://wintellect.com/blogs/jprosise/using-html5-web-storage-for-
> interprocess-communication

Use case: draw on one page and have it appear on the page in another tab.


> http://bens.me.uk/2013/localstorage-inter-window-messaging

Use case: notify other open tabs that some state (e.g. user logged-in state) has changed and they should update themselves.


> http://balpha.de/2012/03/javascript-concurrency-and-locking-the-html5-
> localstorage/

The use case here is actually this one:

| http://meta.stackoverflow.com/questions/85034/multiple-chat-tabs/85045#85045

...which is the SharedWorker use case.



A broadcast message API seems reasonable for the middle two use cases (the third in particular; the second seems more like a toy).

If we did a broadcast API, you'd want to be able to send messages back, presumably using a MessagePort. It's not clear how to do this cleanly. I'd rather not introduce yet more messaging primitives... I guess broadcastMessage() could return an array of MessagePorts, each one of which is created specifically for the purpose of sending the message, and which is therefore set as the event.source on the other end, even though the message is delivered to the Window object? But do we really want to expose how many Windows it broadcast to? Seems like that'd have to be async at best. Maybe create those ports as described above, but instead of returning them, we return an object that has an onconnect method similar to global workers, and when someone tries to send a message on one of these ports, the corresponding one locally first gets vended though onconnect?
Comment 2 Jonas Sicking (Not reading bugmail) 2013-07-10 18:45:38 UTC
Something like:

port = window.getBroadcastPort("foopy");

might be enough. No new primitives needed.

Posting to this port would send a message to all ports in all windows that was returned from a call getBroadcastPort with the same string argument.

I'm not 100% sure if it should send a message to the port itself. Based on feedback that we have gotten from authors, the fact that localStorage doesn't fire an event on the window itself is bad. It can be worked around, but it's a pain in the ass more often than it helps.

So firing a message on *all* ports might be better. Just make it easy to filter out messages that were sent from the port itself.

OTOH, if someone want to get messages from all ports, including ports on the page itself, that can always be done by opening a new port. I.e:

sendPort = window.getBroadcastPort("foopy");
sendPort.postMessage(...);
listingPort = window.getBroadcastPort("foopy");
listingPort = function(e) {
  // Called for messages sent to all ports, including ones sent to sendPort
}
Comment 3 Jonas Sicking (Not reading bugmail) 2013-07-10 18:50:09 UTC
Typo in the example. Here's a better/fixed one:


sendPort = window.getBroadcastPort("foopy");
sendPort.postMessage(...);
sendPort.onmessage = function(e) {
  // Doesn't receive message sent above
}
listingPort = window.getBroadcastPort("foopy");
listingPort.onmessage = function(e) {
  // Called for messages sent to all ports, including ones sent to sendPort
}
Comment 4 Ian 'Hixie' Hickson 2013-08-05 21:21:01 UTC
That would be a new messaging primitive. :-)
Comment 5 Ian 'Hixie' Hickson 2013-08-05 21:27:44 UTC
It would be good to have more use cases, especially use cases that require a response from the broadcast message. If the only use case is a state change notification broadcast, then "onstorage" is probably enough.
Comment 6 Anne 2013-08-05 21:37:58 UTC
For the Notifications API you want to tell same-origin environments you created a new Notification object (at which point they can get a reference to the underlying notification as well using Notification.get()). The windows can do this independently, they would not necessarily communicate directly.

http://notifications.spec.whatwg.org/
Comment 7 Jonas Sicking (Not reading bugmail) 2013-08-05 23:38:11 UTC
As long as onstorage requires the use of localStorage then it is unfortunately not an acceptable workaround for anything. Any use of localStorage requires synchronous IO which comes with unacceptable performance penalties. I.e. we don't consider use of localStorage acceptable in web development for the development we are doing (unfortunately we can't yet stop others from using it, though we are working towards that).

Additionally the fact that onstorage require *any* IO means that there is a decent amount of overhead involved in it. I.e. having to write something to disk just to send a message to another party is a waste of CPU cycles.

onstorage also has the additional overhead in that it requires loading the previously stored data which if you are using onstorage as a signaling mechanism often time will be just a waste of memory and CPU.


The use case here is high-level state changes. We can and will add support for notifications similar to onstorage for other storage APIs too (like IDB). However it can be a lot more work to tease out which application level changes happened by looking at the low-level storage changes that were caused by it. There is also all the overhead mentioned above to deal with if the application level change didn't actually cause storage changes.
Comment 8 Ian 'Hixie' Hickson 2013-08-06 22:13:08 UTC
I agree that only relying on 'storage'-style events is not good. But that's why it'd be good to have more information on use cases.

If the current use cases are all there is, then we don't need to talk back, and the mechanism can be trivial, e.g.:

   navigator.broadcast(data...);

   navigator.onmessage = function (event) { alert(event.data); };

...where the navigator.broadcast() method sends a structured clone of the given data to all other Window objects that have a page in the same origin, and that message is delivered to the Navigator object. (Using Navigator here since it's very much an intra-UA feature, but of course we could put it elsewhere too.)
Comment 9 Jonas Sicking (Not reading bugmail) 2013-08-06 23:31:36 UTC
The main problem with that proposal is that it removes the ability to have multiple "channels".

I think having multiple channels is a requirement. Yes, in theory developers could invent their own "channel" system on top of a single message by letting some property on the sent object indicate which "channel" the message represents.

However feedback from developers has been that getting this type of coordination across JS libraries and frameworks has been hard. MessageChannels help enforce a way to do this type of coordination by formalizing it in spec and API. But using MessageChannel here obviously doesn't work since trasferring objects doesn't make sense over a broadcast channel.

Another problem with that proposal is that it doesn't make it very clear who can send messages to the navigator object. This is something that the window.postMessage/onmessage API suffers from in very unfortunate ways. To the point of window.postMessage/onmessage likely leading to security bugs in websites.

As an implementor, I would prefer the API in comment 2.
Comment 10 Ian 'Hixie' Hickson 2013-08-07 20:59:31 UTC
I don't really understand why providing a dedicated "channel" aspect here is important, but providing dedicated metadata for other things, e.g. priority, source, data type, etc, isn't.


> However feedback from developers has been that getting this type of
> coordination across JS libraries and frameworks has been hard.
> MessageChannels help enforce a way to do this type of coordination by
> formalizing it in spec and API.

I don't understand. Can you elaborate on this? How does MessageChannel enforce a channel concept? Or do you just mean the way you can use a dedicated port pair per conversation?


> Another problem with that proposal is that it doesn't make it very clear who
> can send messages to the navigator object. This is something that the
> window.postMessage/onmessage API suffers from in very unfortunate ways. To
> the point of window.postMessage/onmessage likely leading to security bugs in
> websites.

Can you elaborate on this? I'm familiar with some security disadvantages with the window.postMessage() model, but I don't see how they apply here.


> As an implementor, I would prefer the API in comment 2.

I think the proposal in comment 2 is more complicated than necessary, and I'm confused by the talk of "port"s there (MessagePorts aren't 1:many).

We _could_ do something like:

   navigator.send(channel, data...);
   navigator.addEventListener(channel, (event) { alert(event.data); });

...or maybe (if the concern with using "navigator" is that we might have other things sending messages there one day):

   var s = navigator.sameOriginBroadcastSystem; // or some other name
   s.send(channel, data...);
   s.addEventListener(channel, (event) { alert(event.data); });

...but is even that level of complexity needed?
Comment 11 Jonas Sicking (Not reading bugmail) 2013-08-08 00:26:16 UTC
(In reply to comment #10)
> I don't really understand why providing a dedicated "channel" aspect here is
> important, but providing dedicated metadata for other things, e.g. priority,
> source, data type, etc, isn't.

Once you have a dedicated channel for each actor, it's easy for that actor to coordinate with himself how to send priority, source, data type, etc. It's the coordination between different actors that is a problem.

An actor here is things like JS-libraries, subcomponents within a page developed by different departments at the developer company etc.

This is the feedback that we have gotten from web developers is important to support. Specifically, the fact that Gecko still doesn't support MessageChannel is making it much for authors to coordinate the messages they are sending through window.postMessage.

> > However feedback from developers has been that getting this type of
> > coordination across JS libraries and frameworks has been hard.
> > MessageChannels help enforce a way to do this type of coordination by
> > formalizing it in spec and API.
> 
> I don't understand. Can you elaborate on this? How does MessageChannel
> enforce a channel concept? Or do you just mean the way you can use a
> dedicated port pair per conversation?

Yes. The independent port pair is what constitutes a message channel. I had always assumed that that was why you named the interface "MessageChannel".

> > Another problem with that proposal is that it doesn't make it very clear who
> > can send messages to the navigator object. This is something that the
> > window.postMessage/onmessage API suffers from in very unfortunate ways. To
> > the point of window.postMessage/onmessage likely leading to security bugs in
> > websites.
> 
> Can you elaborate on this? I'm familiar with some security disadvantages
> with the window.postMessage() model, but I don't see how they apply here.

I don't think there are particularly security issues here. It's just a confusing API in that nothing about "navigator.onmessage" or "navigator.postMessage()" says that this is a broadcasting system between pages from the same origin. It sounds a lot more like sending and receiving messages to the UA itself.

> > As an implementor, I would prefer the API in comment 2.
> 
> I think the proposal in comment 2 is more complicated than necessary, and
> I'm confused by the talk of "port"s there (MessagePorts aren't 1:many).
> 
> We _could_ do something like:
> 
>    navigator.send(channel, data...);
>    navigator.addEventListener(channel, (event) { alert(event.data); });
> 
> ...or maybe (if the concern with using "navigator" is that we might have
> other things sending messages there one day):
> 
>    var s = navigator.sameOriginBroadcastSystem; // or some other name
>    s.send(channel, data...);
>    s.addEventListener(channel, (event) { alert(event.data); });
> 
> ...but is even that level of complexity needed?

Both of these are ok in that they have the right feature set. However everywhere else in the platform the names of the events fired by the platform carries semantic meaning, rather than being page-provided author data. This enables us to always supply an .on* property corresponding to that event without ever having to deal with dynamic unbounded namespaces for such properties.

Also, I believe that doing something more similar to a MessagePort has the advantage of reusing existing API. And yes, this isn't exactly a message port, but it is extremely similar to one.

So again, as an implementor, I would prefer the approach in comment 2. Happy to mess around with the names there though, or hanging the API off of navigator.
Comment 12 Anne 2013-08-08 10:09:49 UTC
FWIW, EventSource uses user-defined event names, with onmessage as fallback.
Comment 13 Ian 'Hixie' Hickson 2013-08-08 22:05:51 UTC
(In reply to comment #11)
> 
> This is the feedback that we have gotten from web developers is important to
> support. Specifically, the fact that Gecko still doesn't support
> MessageChannel is making it much for authors to coordinate the messages they
> are sending through window.postMessage.

Ok, good to know, thanks. What you describe makes sense.


> > you just mean the way you can use a dedicated port pair per conversation?
> 
> Yes. The independent port pair is what constitutes a message channel. I had
> always assumed that that was why you named the interface "MessageChannel".

I was using it more in the sense of a river channel, as in, a path where messages flow, than the sense of a named channel, but yeah.



> > > To the point of window.postMessage/onmessage likely leading to security 
> > > bugs in websites.
> > 
> > Can you elaborate on this? I'm familiar with some security disadvantages
> > with the window.postMessage() model, but I don't see how they apply here.
> 
> I don't think there are particularly security issues here.

I'm confused as to what you mean by "likely leading to security bugs in websites" then. Can you clarify?


> [...] It sounds a lot more like sending and receiving messages to the UA [...]

Yeah, that makes sense.


My problems with the comment 2 proposal are that it overloads the term "port" in a somewhat confusing fashion, and it involves more new objects than I'd like for such a simple mechanism (one per "named channel"). The first of these also leads to my feeling like the way it's reusing the same object for both sending and receiving is a bit weird, but I think that's just because of the associations I have with the term "port". Also, the whole mechanism really feels like a new primitive — in all the other messaging mechanisms, there's one place where _all_ the messages go, and you filter the "channels" yourself on the other side. Here, you're proposing vending a new messaging primitive object for each channel. Given that this is "more lightweight" (one-way 1:many without Transferable support), in principle, than window.postMessage(), it feels weird that it is so much heavier (one object per channel) than that mechanism. Less serious, but still something that I think is notable, is that it doesn't provide a "wildcard" mechanism, so there's no way to write a debugging page that just watches for all the messages on that origin.

But still, we should be able to find something that addresses your concerns as well as mine.

In the proposals below, suppose "ua" is either the window object or window.navigator (we can figure out where it should belong later), channel is an arbitrary string, and data is an arbitrary object. In each case, data is sent on channel, and when a message is received on channel, it's alerted. Anywhere the examples use onfoo it's implicit that addEventListener('foo', ...) is the same.

The simplest system that tries to remain lightweight while providing an explicit channel mechanism and a dedicated object would be this:

   ua.broadcast.postMessage(channel, data);
   ua.broadcast.addEventListener('message', function (event) {
     if (event.channel = channel) {
       alert(event.data)
     }
   });

(Here, event.channel would be a new member on MessageEvent.)

This doesn't provide UA-assisted dispatch, though, so it is slightly harder to coordinate amongst different players than it could be.

We could go the route of magically providing channels, similar to your proposal, but with a named creator rather than a method to get the subobjects:

   ua.broadcast[channel].postMessage(data);
   ua.broadcast[channel].onmessage = function (event) { alert(event.data) };
   ua.broadcast.onmessage = function (event) {
     // all messages also go here, for catch-all
     if (event.channel = channel) {
       alert(event.data)
     }
   };

...but this suffers from many of the things I was complaining about earlier.

We could use callbacks rather than events, though there we're again using new messaging primitives:

   ua.broadcast.postMessage(channel, data);
   ua.broadcast.registerMessageCallback(channel, function (data) {
     alert(data);
   });

   // maybe this also fires an event, for catch-all:
   ua.broadcast.onmessage = function (event) {
     // all messages also go here, for catch-all
     if (event.channel = channel) {
       alert(event.data)
     }
   };

We could move away from having a broadcast object:

   ua.broadcastMessage(channel, data);
   ua.onbroadcastmessage = function (event) {
     // all messages also go here, for catch-all
     if (event.channel = channel) {
       alert(event.data)
     }
   };

...but we've lost UA-mediated dispatch again.

It's hard to see a good mechanism that has both UA-mediated dispatch and a global listener without either using null, '' or '*' as a magical channel name, or providing two duplicate listening mechanisms (which isn't satisfying).

I dunno. None of these are great...
Comment 14 Jonas Sicking (Not reading bugmail) 2013-08-08 23:10:57 UTC
(In reply to comment #13)
> (In reply to comment #11)
> > > > To the point of window.postMessage/onmessage likely leading to security 
> > > > bugs in websites.
> > > 
> > > Can you elaborate on this? I'm familiar with some security disadvantages
> > > with the window.postMessage() model, but I don't see how they apply here.
> > 
> > I don't think there are particularly security issues here.
> 
> I'm confused as to what you mean by "likely leading to security bugs in
> websites" then. Can you clarify?

What I was saying was that the current window.postMessage/window.onmessage API makes it very unclear where messages can come from. For window.postMessage that can lead to security bugs.

navigator.postMessage/onmessage makes it similarly unclear where messages can come from. This can similarly lead to bugs. Probably these bugs are less likely to lead to security bugs (which is why I mentioned security specifically in relation to window.postMessage and not navigator.postMessage), but bugs nonetheless.

> My problems with the comment 2 proposal are that it overloads the term
> "port" in a somewhat confusing fashion, and it involves more new objects
> than I'd like for such a simple mechanism (one per "named channel"). The
> first of these also leads to my feeling like the way it's reusing the same
> object for both sending and receiving is a bit weird, but I think that's
> just because of the associations I have with the term "port". Also, the
> whole mechanism really feels like a new primitive — in all the other
> messaging mechanisms, there's one place where _all_ the messages go, and you
> filter the "channels" yourself on the other side. Here, you're proposing
> vending a new messaging primitive object for each channel. Given that this
> is "more lightweight" (one-way 1:many without Transferable support), in
> principle, than window.postMessage(), it feels weird that it is so much
> heavier (one object per channel) than that mechanism. Less serious, but
> still something that I think is notable, is that it doesn't provide a
> "wildcard" mechanism, so there's no way to write a debugging page that just
> watches for all the messages on that origin.

I don't think optimizing for number of objects that are created is the wrong thing to optimize for. Especially since it's unlikely getBroadcastChannel needs to be called in a tight loop.

I would argue that my proposal is less of a "new primitive" than the proposals in comment 10. This because it reuses the MessagePort primitive which is an API already known by developers.

Arguably it is bad to create something that is MessagePort-like if it is not 100% MessagePort compatible. And since this new thing doesn't support transferrable objects it isn't 100% compatible. I'd love to have author feedback about which way to go here. Either way the new object wouldn't inherit MessagePort, so instanceof etc would test false.

> The simplest system that tries to remain lightweight while providing an
> explicit channel mechanism and a dedicated object would be this:
> 
>    ua.broadcast.postMessage(channel, data);
>    ua.broadcast.addEventListener('message', function (event) {
>      if (event.channel = channel) {
>        alert(event.data)
>      }
>    });
> 
> (Here, event.channel would be a new member on MessageEvent.)
> 
> This doesn't provide UA-assisted dispatch, though, so it is slightly harder
> to coordinate amongst different players than it could be.

Indeed. The channel-check seems easy to forget leading to bugs once a website introduces code using a second channel.

> We could go the route of magically providing channels, similar to your
> proposal, but with a named creator rather than a method to get the
> subobjects:
> 
>    ua.broadcast[channel].postMessage(data);
>    ua.broadcast[channel].onmessage = function (event) { alert(event.data) };
>    ua.broadcast.onmessage = function (event) {
>      // all messages also go here, for catch-all
>      if (event.channel = channel) {
>        alert(event.data)
>      }
>    };
> 
> ...but this suffers from many of the things I was complaining about earlier.

Yeah, I don't really see what the advantage is with this proposal. But I have a natural bias against proxy-based APIs.

> We could use callbacks rather than events, though there we're again using
> new messaging primitives:
> 
>    ua.broadcast.postMessage(channel, data);
>    ua.broadcast.registerMessageCallback(channel, function (data) {
>      alert(data);
>    });
> 
>    // maybe this also fires an event, for catch-all:
>    ua.broadcast.onmessage = function (event) {
>      // all messages also go here, for catch-all
>      if (event.channel = channel) {
>        alert(event.data)
>      }
>    };

This seems ok, though means that it's harder to filter out messages that you yourself sent.

> We could move away from having a broadcast object:
> 
>    ua.broadcastMessage(channel, data);
>    ua.onbroadcastmessage = function (event) {
>      // all messages also go here, for catch-all
>      if (event.channel = channel) {
>        alert(event.data)
>      }
>    };
> 
> ...but we've lost UA-mediated dispatch again.

Yup. I have the same concern about it being too easy to forget to do the channel check.

I do sort of like calling the function broadcast-something rather than post-something though.

> It's hard to see a good mechanism that has both UA-mediated dispatch and a
> global listener without either using null, '' or '*' as a magical channel
> name, or providing two duplicate listening mechanisms (which isn't
> satisfying).

I don't think that having a global listener is a requirement. You can always send a message on multiple channels if needed.

> I dunno. None of these are great...

If the only problem you are trying to solve is to avoid creating an object instance because you feel that that is "too heavy", then I think you are using premature optimizations.

I'll send post an updated proposal in a separate message for linkability.
Comment 15 Jonas Sicking (Not reading bugmail) 2013-08-08 23:15:26 UTC
So here's my updated proposal.

If we think using something similar to existing message passing APIs:

sendPort = new BroadcastPort("foopy");
sendPort.postMessage(...);
sendPort.onmessage = function(e) {
  // Doesn't receive message sent above
}
listingPort = new BroadcastPort("foopy");
listingPort.onmessage = function(e) {
  // Called for messages sent to all ports, including ones sent to sendPort
}


If there is concern about having something called "postMessage" but that doesn't support transferrables, then something like the following could work instead:

sendPort = new BroadcastPort("foopy");
sendPort.broadcast(...);
sendPort.onbroadcast = function(e) {
  // Doesn't receive message sent above
}
listingPort = new BroadcastPort("foopy");
listingPort.onbroadcast = function(e) {
  // Called for messages sent to all ports, including ones sent to sendPort
}
Comment 16 David Bruant 2013-08-09 09:56:29 UTC
(In reply to comment #15)
> So here's my updated proposal.
A part is missing: what is in the event object?
 
> sendPort = new BroadcastPort("foopy");
> sendPort.broadcast(...);
> sendPort.onbroadcast = function(e) {
>   // Doesn't receive message sent above
> }
I hate on* properties. We have addEventListener and a pending proposal for better syntax [1], let's use that.

Why is the message not received? Because the send happens before the .onbroadcast in time or because a message isn't broadcasted to the window that emitted it?

> listingPort = new BroadcastPort("foopy");
listening? (or I may be misunderstanding part of your proposal)

> listingPort.onbroadcast = function(e) {
>   // Called for messages sent to all ports, including ones sent to sendPort
> }


On enabling 1:1 communication, the event could contain a port to answer to the sender and the sender could listen to specific responses.

// in one tab
var sendPort = new BroadcastPort("foopy");
sendPort.addEventListener('broadcast', e => {
  
})
sendPort.addEventListener('reply' e => {
  var p = e.port; // direct port to the replier

  // e.port.postMessage
  // e.port.addEventListener  

});
sendPort.broadcast('whaddup!')

// in another tab
var listeningPort = new BroadcastPort("foopy");
listeningPort.addEventListener('broadcast', e => {
  e.senderPort.postMessage('yo!') // direct port to sender to send replies

  // e.senderPort.postMessage
  // e.senderPort.addEventListener  
})

MessageChannels that carries the 2 ports are created internally and never exposed. They can be created lazily on first property access.

[1] https://www.w3.org/Bugs/Public/show_bug.cgi?id=16491
Comment 17 Ian 'Hixie' Hickson 2013-08-09 19:57:32 UTC
(In reply to comment #14)
> 
> What I was saying was that the current window.postMessage/window.onmessage
> API makes it very unclear where messages can come from. For
> window.postMessage that can lead to security bugs.

Ah, ok. Yeah, that I agree with.


> I don't think optimizing for number of objects that are created is the [right]
> thing to optimize for. Especially since it's unlikely getBroadcastChannel
> needs to be called in a tight loop.

It's not a performance issue that makes me concerned about it, it's more a cognitive load issue. All we're doing is broadcasting a structured-cloneable message with a channel name, it intuitively feels like it should be much lighter, in API surface, than the existing two-way, Transferable-supporting, multi-thread supporting, capability-model-supporting, mechanisms.

In practice I think pretty much all the proposals here would work, I'm just not convinced we've yet found the nicest API for it.


> I would argue that my proposal is less of a "new primitive" than the
> proposals in comment 10. This because it reuses the MessagePort primitive
> which is an API already known by developers.

This is probably a linguistic rat hole, but for what it's worth: as I see it, the "primitives" we have so far for comms are Events, callbacks, MessagePort objects, and window.postMessage(). The communication model in Workers is an example of reusing a primitive: it's literally defined in terms of MessagePorts, and the API is just a subset exposed in such a way that you can't actually get to the port to send it down another port and break the Worker communication model. What I would term a _new_ primitive is something that creates a new object that has its own semantics, which might be very similar, but are not exactly subset of or equal to the existing primitives'.

Most of the proposals here introduce new primitives. It may be that the nicest solution does too; maybe we have to. As a general rule, though, I try to avoid introducing new primitives when we don't have to. (Obviously when we _do_ introduce new primitives, it's often good to reuse the existing ideas of preexisting semantics, as your proposals do; MessagePort, for instance, reuses Events as part of its design.)


> I don't think that having a global listener is a requirement. You can always
> send a message on multiple channels if needed.

I don't think it's a hard requirement either, but I do think that if we can achieve it, it has great value. It is _really_ useful, when debugging, to be able to listen to everything and just log it all, for instance. Not being able to do this makes typos in channel names massively harder to detect — you go from "oh I typed it wrong" to "is it even sending? is the origin right?" etc.


> If the only problem you are trying to solve is to avoid creating an object
> instance because you feel that that is "too heavy", then I think you are
> using premature optimizations.

It's not the only problem.



Re comment 15, I really think we should avoid the word "port" for things that aren't 1:1, and I miss the ability to do global listening. But aside from those it is a good candidate. I have no trouble with using "postMessage()" here, even though we don't support Transferable. The various postMessage() methods already have different signatures based on their context.


(In reply to comment #16)
>
> I hate on* properties.

They're part of the Web. It would be far worse for us to stop providing them arbitrarily than to continue to be consistent. (As a general rule, consistency is really important in a platform. More important than whether or not we like or dislike a particular paradigm.)

addEventListener() would still be available, anyway. All event targets inherit from EventTarget which defines addEventListener().


> On enabling 1:1 communication, the event could contain a port to answer to
> the sender and the sender could listen to specific responses.

Please see comment 8. The use cases we have so far don't need us to provide a MessagePort per target. If there are use cases that do need that, then that will definitely inform the API design, as it would likely look quite different.




Proposal (based on comment 15's):

We add a new constructible BroadcastChannel object, whose constructor takes the channel name. It has a postMessage() method that sends a message to all other BroadcastChannel objects with the same origin and channel name. Messages are sent in the form of 'message' events with the interface MessageEvent. In addition, all broadcast messages are also sent to all Windows in that origin other than the Window of the source BroadcastChannel object, with the event name 'broadcastmessage', also using MessageEvent. We add a 'channel' attribute to MessageEvent that is only used for these two events (it's otherwise just "").

So:

   var c1 = new BroadcastChannel('test'); 
   var c2 = new BroadcaatChannel('test');

   c1.onmessage = function (e) { alert('1:' + e.data) };
   c2.onmessage = function (e) { alert('2:' + e.data) };
   onbroadcastmessage = function (e) { alert(e.channel + ' ' + e.data) }
   c1.postMessage('foo'); // alerts "2: foo"

   // another window broadcasts "bar" to channel "test" on our origin
   // three alerts fire: "test bar", "1: bar", "2: bar".

How badly do you hate the global listener thing? I don't particularly like the way it's shoe-horned into this, I must admit. It's a pretty neat design if we just drop it, but it _would_ be more annoying to debug...
Comment 19 Jonas Sicking (Not reading bugmail) 2013-08-22 08:02:27 UTC
(In reply to comment #17)
> How badly do you hate the global listener thing? I don't particularly like
> the way it's shoe-horned into this, I must admit. It's a pretty neat design
> if we just drop it, but it _would_ be more annoying to debug...

Given how awkward it looks, I'd say leave it out for now. Debugging might be solvable using debugger tools (which are internal and so has access to all channels anyway). Or it might not be a big problem since you'll pretty quickly discover if you mistype a channel name since you won't get *any* messages.

Lets attempt to solve the problem once/if it's asked for.
Comment 20 Jonas Sicking (Not reading bugmail) 2013-08-22 08:05:59 UTC
A bit problem is that the current proposals (other than the one in comment 16) has no way of enabling setting up a point-to-point channel with a particular actor. So while you can broadcast a message to all open windows, there is no way to find a particular window and start sending messages to just that one.

You could arguably always broadcast messages, even when intended to send messages to just one window. But that means that you can't ever support transferrables, even when intending to just talk to a particular window.


To support that I think we need something like a "channel coordinator". This acts much like a lock which can only be owned by one Broadcasting-port at a time.

var c1 = new BroadcastChannel("foopy");
var c2 = new BroadcastChannel("foopy");
c1.requestOwner().then(function(messageReceiver) {
  messageReceiver.onmessage = function(e) {
    ...
  }
});
c2.postMessageToOwner(data, transferables);


So here any BroadcastChannel instance can request to be the "owner" of the channel. Once granted, it will receive all messages sent to the owner. Such messages so can support transferrables, which provides an ability to set up 1:1 connections.

At any time the channel coordinator can close his port, in which case the ownership is transferred to the next BroadcastChannel instance that has requested to be owner. When ownership is transferred this way, the implementation MUST ensure that no messages as lost.

Even if the channel goes without an owner for some time, messages that are sent to the owner must be queued until an owner is established.


This approach has the advantage over comment 16 in that it is less prone to races. Comment 16 does allow setting up the same situation using application-level logic. However there's a risk that two instances attempt to claim ownership at the same time and that their messages cross paths. In that case both instances has to use random backoff methods in order to establish an owner, which is not trivial to realize it is needed, nor trivial to implement.


This solution could even be further generalized such that rather than having a single owner, a channel can have any number of "named actors". So any BroadcastChannel instance can request to be the actor with a particular name. And then anyone can send a message to a particularly named actor.

But I'm not sure that that complexity is needed. And it can always be implemented using application-level logic by letting the owner be responsible for setting up the other actors. Or you can use separate broadcast channels for separate actors.
Comment 21 Ian 'Hixie' Hickson 2013-08-22 18:04:14 UTC
I intentionally haven't been trying to enable setting up point-to-point comms, since none of the use cases need it. If we need to support that, then we should indeed go back to the drawing board. What are the use cases for it?

If we don't need to support that, then comment 17 without the global stuff seems reasonable (in particular, it's what Adam independently suggested on the wiki, apparently, which probably says something).
Comment 22 Jonas Sicking (Not reading bugmail) 2013-10-11 18:51:26 UTC
Yeah, after reaching out directly to people that have asked for point-to-point communication, I believe that it's orthogonal to broadcasting.

Likewise I think we should leave out the global listener thing and try to solve that using better development tools for now.
Comment 23 contributor 2013-11-13 19:09:44 UTC
Checked in as WHATWG revision r8274.
Check-in comment: New BroadcastChannel feature to send messages to other tabs in the same origin, so you don't have to abuse the onstorage event, and don't need to use a heavy shared worker for something otherwise trivial.
http://html5.org/tools/web-apps-tracker?from=8273&to=8274