MessageEvent
interfacesMessageEvent
interfacesMessages in server-sent events, Web sockets, cross-document
messaging, channel messaging, and broadcast channels use the
MessageEvent
interface for their message
events:
[Constructor(DOMString type, optional MessageEventInit eventInitDict), Exposed=(Window,Worker)] interface MessageEvent : Event { readonly attribute any data; readonly attribute DOMString origin; readonly attribute DOMString lastEventId; readonly attribute (WindowProxy or MessagePort)? source; readonly attribute MessagePort[]? ports; void initMessageEvent(DOMString typeArg, boolean canBubbleArg, boolean cancelableArg, any dataArg, DOMString originArg, DOMString lastEventIdArg, (WindowProxy or MessagePort) sourceArg, sequence<MessagePort>? portsArg); }; dictionary MessageEventInit : EventInit { any data; DOMString origin; DOMString lastEventId; (WindowProxy or MessagePort)? source; sequence<MessagePort> ports; };
data
Returns the data of the message.
origin
Returns the origin of the message, for server-sent events and cross-document messaging.
lastEventId
Returns the last event ID string, for server-sent events.
source
Returns the WindowProxy
of the source window, for cross-document
messaging, and the MessagePort
being attached, in the connect
event fired at
SharedWorkerGlobalScope
objects.
ports
Returns the MessagePort
array sent with the message, for cross-document
messaging and channel messaging.
The data
attribute must return the value
it was initialised to. When the object is created, this attribute must be initialised to null. It
represents the message being sent.
The origin
attribute must return the
value it was initialised to. When the object is created, this attribute must be initialised to the
empty string. It represents, in server-sent events and cross-document
messaging, the origin of the document that sent the message (typically the
scheme, hostname, and port of the document, but not its path or fragment identifier).
The lastEventId
attribute must
return the value it was initialised to. When the object is created, this attribute must be
initialised to the empty string. It represents, in server-sent events, the last event ID string of the event source.
The source
attribute must return the
value it was initialised to. When the object is created, this attribute must be initialised to
null. It represents, in cross-document messaging, the WindowProxy
of the
browsing context of the Window
object from which the message came; and
in the connect
events used by shared workers, the newly connecting
MessagePort
.
The ports
attribute must return the
value it was initialised to. When the object is created, this attribute must be initialised to
null. It represents, in
cross-document messaging and channel messaging, the
MessagePort
array being sent, if any.
The initMessageEvent()
method must initialise the event in a manner analogous to the similarly-named initEvent()
method. [DOM]
This section is non-normative.
To enable servers to push data to Web pages over HTTP or using dedicated server-push protocols,
this specification introduces the EventSource
interface.
Using this API consists of creating an EventSource
object and registering an event
listener.
var source = new EventSource('updates.cgi'); source.onmessage = function (event) { alert(event.data); };
On the server-side, the script ("updates.cgi
" in this case) sends
messages in the following form, with the text/event-stream
MIME type:
data: This is the first message. data: This is the second message, it data: has two lines. data: This is the third message.
Authors can separate events by using different event types. Here is a stream that has two event types, "add" and "remove":
event: add data: 73857293 event: remove data: 2153 event: add data: 113411
The script to handle such a stream would look like this (where addHandler
and removeHandler
are functions that take one argument, the event):
var source = new EventSource('updates.cgi'); source.addEventListener('add', addHandler, false); source.addEventListener('remove', removeHandler, false);
The default event type is "message".
Event streams are always decoded as UTF-8. There is no way to specify another character encoding.
Event stream requests can be redirected using HTTP 301 and 307 redirects as with normal HTTP requests. Clients will reconnect if the connection is closed; a client can be told to stop reconnecting using the HTTP 204 No Content response code.
Using this API rather than emulating it using XMLHttpRequest
or an
iframe
allows the user agent to make better use of network resources in cases where
the user agent implementor and the network operator are able to coordinate in advance. Amongst
other benefits, this can result in significant savings in battery life on portable devices. This
is discussed further in the section below on connectionless
push.
EventSource
interface[Constructor(DOMString url, optional EventSourceInit eventSourceInitDict), Exposed=(Window,Worker)] interface EventSource : EventTarget { readonly attribute DOMString url; readonly attribute boolean withCredentials; // ready state const unsigned short CONNECTING = 0; const unsigned short OPEN = 1; const unsigned short CLOSED = 2; readonly attribute unsigned short readyState; // networking attribute EventHandler onopen; attribute EventHandler onmessage; attribute EventHandler onerror; void close(); }; dictionary EventSourceInit { boolean withCredentials = false; };
The EventSource()
constructor takes one or two
arguments. The first specifies the URL to which to connect. The second specifies the
settings, if any, in the form of an EventSourceInit
dictionary. When the EventSource()
constructor is invoked, the UA must run these
steps:
Resolve the URL specified in the first argument, relative to the API base URL specified by the entry settings object.
If the previous step failed, then throw a SyntaxError
exception and abort
these steps.
Create a new EventSource
object.
Let CORS mode be Anonymous.
If the second argument is present, and the withCredentials
dictionary member has the
value true, then set CORS mode to Use Credentials and initialise the new
EventSource
object's withCredentials
attribute to true.
Return the new EventSource
object, but continue these steps
in parallel.
Do a potentially CORS-enabled fetch of the resulting absolute URL using the referrer source specified by the entry settings object, with the mode being CORS mode, and the origin being the origin specified by the entry settings object, and process the resource obtained in this fashion, if any, as described below.
The definition of the fetching algorithm (which is used by CORS) is such that if the browser is already fetching the resource identified by the given absolute URL, that connection can be reused, instead of a new connection being established. All messages received up to this point are dispatched immediately, in this case.
The url
attribute must return the
absolute URL that resulted from resolving the
value that was passed to the constructor.
The withCredentials
attribute
must return the value to which it was last initialised. When the object is created, it must be
initialised to false.
The readyState
attribute represents
the state of the connection. It can have the following values:
CONNECTING
(numeric value 0)OPEN
(numeric value 1)CLOSED
(numeric value 2)close()
method was invoked.When the object is created its readyState
must
be set to CONNECTING
(0). The rules given below
for handling the connection define when the value changes.
The close()
method must abort any
instances of the fetch algorithm started for this EventSource
object,
and must set the readyState
attribute to CLOSED
.
The following are the event handlers (and their corresponding event handler event types) that must be supported, as event
handler IDL attributes, by all objects implementing the EventSource
interface:
Event handler | Event handler event type |
---|---|
onopen | open
|
onmessage | message
|
onerror | error
|
In addition to the above, each EventSource
object has the following associated
with it:
These values are not currently exposed on the interface.
The resource indicated in the argument to the EventSource
constructor is fetched when the constructor is run.
For HTTP connections, the Accept
header may be included; if
included, it must contain only formats of event framing that are supported by the user agent (one
of which must be text/event-stream
, as described below).
If the event source's last event ID
string is not the empty string, then a Last-Event-ID
HTTP header must be included with the request,
whose value is the value of the event source's last event ID string, encoded as UTF-8.
User agents should use the Cache-Control: no-cache
header in requests to bypass any caches for requests of event sources. (This header is not a custom request header, so the user agent will still use the
CORS simple cross-origin request mechanism.) User agents should ignore HTTP cache
headers in the response, never caching event sources.
As data is received, the tasks queued by the networking task source to handle the data must act as follows.
HTTP 200 OK responses with a Content-Type header specifying the type
text/event-stream
, ignoring any MIME type parameters, must be processed
line by line as described below.
When a successful response with a supported MIME type is received, such that the user agent begins parsing the contents of the stream, the user agent must announce the connection.
The task that the networking task source places
on the task queue once the fetching algorithm for such a
resource (with the correct MIME type) has completed must cause the user agent to
reestablish the connection in parallel. This applies whether the connection is
closed gracefully or unexpectedly (but does not apply when the fetch algorithm is
canceled by the user agent, e.g. in response to window.stop()
,
since in those cases the final task is actually discarded).
It doesn't apply for the error conditions listed below except
where explicitly specified.
HTTP 200 OK responses that have a Content-Type specifying an unsupported type, or that have no Content-Type at all, must cause the user agent to fail the connection.
HTTP 305 Use Proxy, 401 Unauthorized, and 407 Proxy Authentication Required should be treated transparently as for any other subresource.
HTTP 301 Moved Permanently, 302 Found, 303 See Other, and 307 Temporary Redirect responses are
handled by the fetching and CORS algorithms. In the case of 301
redirects, the user agent must also remember the new URL so that subsequent requests for this
resource for this EventSource
object start with the URL given for the last 301 seen
for requests for this object.
Network errors that prevents the connection from being established in the first place (e.g. DNS errors), must cause the user agent to reestablish the connection in parallel.
Any other HTTP response code not listed here, as well as the
cancelation of the fetch algorithm by the user agent (e.g. in response to window.stop()
or the user canceling the network connection
manually) must cause the user agent to fail the connection.
For non-HTTP protocols, UAs should act in equivalent ways.
When a user agent is to announce the connection, the user agent must queue a
task which, if the readyState
attribute is
set to a value other than CLOSED
, sets the readyState
attribute to OPEN
and fires a simple
event named open
at the EventSource
object.
When a user agent is to reestablish the connection, the user agent must run the following steps. These steps are run in parallel, not as part of a task. (The tasks that it queues, of course, are run like normal tasks and not themselves in parallel.)
Queue a task to run the following steps:
If the readyState
attribute is set to
CLOSED
, abort the task.
Set the readyState
attribute to CONNECTING
.
Fire a simple event named error
at the
EventSource
object.
Wait a delay equal to the reconnection time of the event source.
Optionally, wait some more. In particular, if the previous attempt failed, then user agents might introduce an exponential backoff delay to avoid overloading a potentially already overloaded server. Alternatively, if the operating system has reported that there is no network connectivity, user agents might wait for the operating system to announce that the network connection has returned before retrying.
Wait until the aforementioned task has run, if it has not yet run.
Queue a task to run the following steps:
If the readyState
attribute is not set
to CONNECTING
, abort these steps.
Perform a potentially CORS-enabled fetch of the absolute
URL of the event source resource, using the same referrer source, and with the
same mode and origin, as those
used in the original request triggered by the EventSource()
constructor, and process the resource obtained in
this fashion, if any, as described earlier in this section.
When a user agent is to fail the connection, the user agent must queue a
task which, if the readyState
attribute is
set to a value other than CLOSED
, sets the readyState
attribute to CLOSED
and fires a simple
event named error
at the EventSource
object.
Once the user agent has failed the connection, it
does not attempt to reconnect!
The task source for any tasks that are queued by EventSource
objects is the remote event
task source.
This event stream format's MIME type is text/event-stream
.
The event stream format is as described by the stream
production of the
following ABNF, the character set for which is Unicode. [ABNF]
stream = [ bom ] *event event = *( comment / field ) end-of-line comment = colon *any-char end-of-line field = 1*name-char [ colon [ space ] *any-char ] end-of-line end-of-line = ( cr lf / cr / lf ) ; characters lf = %x000A ; U+000A LINE FEED (LF) cr = %x000D ; U+000D CARRIAGE RETURN (CR) space = %x0020 ; U+0020 SPACE colon = %x003A ; U+003A COLON (:) bom = %xFEFF ; U+FEFF BYTE ORDER MARK name-char = %x0000-0009 / %x000B-000C / %x000E-0039 / %x003B-10FFFF ; a Unicode character other than U+000A LINE FEED (LF), U+000D CARRIAGE RETURN (CR), or U+003A COLON (:) any-char = %x0000-0009 / %x000B-000C / %x000E-10FFFF ; a Unicode character other than U+000A LINE FEED (LF) or U+000D CARRIAGE RETURN (CR)
Event streams in this format must always be encoded as UTF-8. [ENCODING]
Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair, a single U+000A LINE FEED (LF) character, or a single U+000D CARRIAGE RETURN (CR) character.
Since connections established to remote servers for such resources are expected to be long-lived, UAs should ensure that appropriate buffering is used. In particular, while line buffering with lines are defined to end with a single U+000A LINE FEED (LF) character is safe, block buffering or line buffering with different expected line endings can cause delays in event dispatch.
Streams must be decoded using the UTF-8 decode algorithm.
The UTF-8 decode algorithm strips one leading UTF-8 Byte Order Mark (BOM), if any.
The stream must then be parsed by reading everything line by line, with a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair, a single U+000A LINE FEED (LF) character not preceded by a U+000D CARRIAGE RETURN (CR) character, and a single U+000D CARRIAGE RETURN (CR) character not followed by a U+000A LINE FEED (LF) character being the ways in which a line can end.
When a stream is parsed, a data buffer, an event type buffer, and a last event ID buffer must be associated with it. They must be initialised to the empty string
Lines must be processed, in the order they are received, as follows:
Dispatch the event, as defined below.
Ignore the line.
Collect the characters on the line before the first U+003A COLON character (:), and let field be that string.
Collect the characters on the line after the first U+003A COLON character (:), and let value be that string. If value starts with a U+0020 SPACE character, remove it from value.
Process the field using the steps described below, using field as the field name and value as the field value.
Process the field using the steps described below, using the whole line as the field name, and the empty string as the field value.
Once the end of the file is reached, any pending data must be discarded. (If the file ends in the middle of an event, before the final empty line, the incomplete event is not dispatched.)
The steps to process the field given a field name and a field value depend on the field name, as given in the following list. Field names must be compared literally, with no case folding performed.
Set the event type buffer to field value.
Append the field value to the data buffer, then append a single U+000A LINE FEED (LF) character to the data buffer.
Set the last event ID buffer to the field value.
If the field value consists of only ASCII digits, then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer. Otherwise, ignore the field.
The field is ignored.
When the user agent is required to dispatch the event, the user agent must process the data buffer, the event type buffer, and the last event ID buffer using steps appropriate for the user agent.
For Web browsers, the appropriate steps to dispatch the event are as follows:
Set the last event ID string of the event source to the value of the last event ID buffer. The buffer does not get reset, so the last event ID string of the event source remains set to this value until the next time it is set by the server.
If the data buffer is an empty string, set the data buffer and the event type buffer to the empty string and abort these steps.
If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.
Create an event that uses the MessageEvent
interface, with the event type
message
, which does not bubble, is not cancelable, and has no
default action. The data
attribute must be
initialised to the value of the data buffer, the origin
attribute must be initialised to the Unicode serialisation of the
origin of the event stream's final URL (i.e. the URL after redirects), and the lastEventId
attribute must be initialised to the
last event ID string of the event
source. This event is not trusted.
If the event type buffer has a value other than the empty string, change the type of the newly created event to equal the value of the event type buffer.
Set the data buffer and the event type buffer to the empty string.
Queue a task which, if the readyState
attribute is set to a value other than CLOSED
, dispatches the newly created event at the
EventSource
object.
If an event doesn't have an "id" field, but an earlier event did set the event
source's last event ID string, then the
event's lastEventId
field will be set to the
value of whatever the last seen "id" field was.
For other user agents, the appropriate steps to dispatch the event are implementation dependent, but at a minimum they must set the data and event type buffers to the empty string before returning.
The following event stream, once followed by a blank line:
data: YHOO data: +2 data: 10
...would cause an event message
with the interface
MessageEvent
to be dispatched on the EventSource
object. The event's
data
attribute would contain the string "YHOO\n+2\n10
" (where "\n
" represents a newline).
This could be used as follows:
var stocks = new EventSource("http://stocks.example.com/ticker.php"); stocks.onmessage = function (event) { var data = event.data.split('\n'); updateStocks(data[0], data[1], data[2]); };
...where updateStocks()
is a function defined as:
function updateStocks(symbol, delta, value) { ... }
...or some such.
The following stream contains four blocks. The first block has just a comment, and will fire
nothing. The second block has two fields with names "data" and "id" respectively; an event will
be fired for this block, with the data "first event", and will then set the last event ID to "1"
so that if the connection died between this block and the next, the server would be sent a Last-Event-ID
header with the value "1". The third block fires
an event with data "second event", and also has an "id" field, this time with no value, which
resets the last event ID to the empty string (meaning no Last-Event-ID
header will now be sent in the event of a
reconnection being attempted). Finally, the last block just fires an event with the data
" third event" (with a single leading space character). Note that the last still has to
end with a blank line, the end of the stream is not enough to trigger the dispatch of the last
event.
: test stream data: first event id: 1 data:second event id data: third event
The following stream fires two events:
data data data data:
The first block fires events with the data set to the empty string, as would the last block if it was followed by a blank line. The middle block fires an event with the data set to a single newline character. The last block is discarded because it is not followed by a blank line.
The following stream fires two identical events:
data:test data: test
This is because the space after the colon is ignored if present.
Legacy proxy servers are known to, in certain cases, drop HTTP connections after a short timeout. To protect against such proxy servers, authors can include a comment line (one starting with a ':' character) every 15 seconds or so.
Authors wishing to relate event source connections to each other or to specific documents previously served might find that relying on IP addresses doesn't work, as individual clients can have multiple IP addresses (due to having multiple proxy servers) and individual IP addresses can have multiple clients (due to sharing a proxy server). It is better to include a unique identifier in the document when it is served and then pass that identifier as part of the URL when the connection is established.
Authors are also cautioned that HTTP chunking can have unexpected negative effects on the reliability of this protocol. Where possible, chunking should be disabled for serving event streams unless the rate of messages is high enough for this not to matter.
Clients that support HTTP's per-server connection limitation might run into trouble when
opening multiple pages from a site if each page has an EventSource
to the same
domain. Authors can avoid this using the relatively complex mechanism of using unique domain names
per connection, or by allowing the user to enable or disable the EventSource
functionality on a per-page basis, or by sharing a single EventSource
object using a
shared worker.
User agents running in controlled environments, e.g. browsers on mobile handsets tied to specific carriers, may offload the management of the connection to a proxy on the network. In such a situation, the user agent for the purposes of conformance is considered to include both the handset software and the network proxy.
For example, a browser on a mobile device, after having established a connection, might detect that it is on a supporting network and request that a proxy server on the network take over the management of the connection. The timeline for such a situation might be as follows:
EventSource
constructor.EventSource
constructor (possibly
including a Last-Event-ID
HTTP header, etc).This can reduce the total data usage, and can therefore result in considerable power savings.
As well as implementing the existing API and text/event-stream
wire format as
defined by this specification and in more distributed ways as described above, formats of event
framing defined by other applicable specifications may be supported. This
specification does not define how they are to be parsed or processed.
While an EventSource
object's readyState
is CONNECTING
, and the object has one or more event
listeners registered for open
, message
or error
events, there must
be a strong reference from the Window
or WorkerGlobalScope
object that
the EventSource
object's constructor was invoked from to the EventSource
object itself.
While an EventSource
object's readyState
is OPEN
, and the object has one or more event listeners
registered for message
or error
events, there must be a strong reference from the
Window
or WorkerGlobalScope
object that the EventSource
object's constructor was invoked from to the EventSource
object itself.
While there is a task queued by an EventSource
object on the remote event
task source, there must be a strong reference from the Window
or
WorkerGlobalScope
object that the EventSource
object's constructor was
invoked from to that EventSource
object.
If a user agent is to forcibly close an
EventSource
object (this happens when a Document
object goes away
permanently), the user agent must abort any instances of the fetch algorithm started
for this EventSource
object, and must set the readyState
attribute to CLOSED
.
If an EventSource
object is garbage collected while its connection is still open,
the user agent must abort any instance of the fetch algorithm opened by
this EventSource
.
It's possible for one active network connection to be shared by multiple
EventSource
objects and their fetch algorithms, which is why the above
is phrased in terms of aborting the fetch algorithm and not the actual underlying
download.
This section is non-normative.
User agents are strongly urged to provide detailed diagnostic information about
EventSource
objects and their related network connections in their development
consoles, to aid authors in debugging code using this API.
For example, a user agent could have a panel displaying all the EventSource
objects a page has created, each listing the constructor's arguments, whether there was a network
error, what the CORS status of the connection is and what headers were sent by the client and
received from the server to lead to that status, the messages that were received and how they were
parsed, and so forth.
Implementations are especially encouraged to report detailed information to their development
consoles whenever an error
event is fired, since little to no
information can be made available in the events themselves.
text/event-stream
This registration is for community review and will be submitted to the IESG for review, approval, and registration with IANA.
charset
The charset
parameter may be provided. The parameter's value must be
"utf-8
". This parameter serves no purpose; it is only allowed for
compatibility with legacy servers.
An event stream from an origin distinct from the origin of the content consuming the event stream can result in information leakage. To avoid this, user agents are required to apply CORS semantics. [FETCH]
Event streams can overwhelm a user agent; a user agent is expected to apply suitable restrictions to avoid depleting local resources because of an overabundance of information from an event stream.
Servers can be overwhelmed if a situation develops in which the server is causing clients to reconnect rapidly. Servers should use a 5xx status code to indicate capacity problems, as this will prevent conforming clients from reconnecting automatically.
Fragment identifiers have no meaning with
text/event-stream
resources.
Last-Event-ID
This section describes a header for registration in the Permanent Message Header Field Registry. [RFC3864]
This section is non-normative.
To enable Web applications to maintain bidirectional communications with server-side processes,
this specification introduces the WebSocket
interface.
This interface does not allow for raw access to the underlying network. For example, this interface could not be used to implement an IRC client without proxying messages through a custom server.
WebSocket
interfaceenum BinaryType { "blob", "arraybuffer" }; [Constructor(DOMString url, optional (DOMString or DOMString[]) protocols), Exposed=(Window,Worker)] interface WebSocket : EventTarget { readonly attribute DOMString url; // ready state const unsigned short CONNECTING = 0; const unsigned short OPEN = 1; const unsigned short CLOSING = 2; const unsigned short CLOSED = 3; readonly attribute unsigned short readyState; readonly attribute unsigned long bufferedAmount; // networking attribute EventHandler onopen; attribute EventHandler onerror; attribute EventHandler onclose; readonly attribute DOMString extensions; readonly attribute DOMString protocol; void close([Clamp] optional unsigned short code, optional USVString reason); // messaging attribute EventHandler onmessage; attribute BinaryType binaryType; void send(USVString data); void send(Blob data); void send(ArrayBuffer data); void send(ArrayBufferView data); };
The WebSocket(url,
protocols)
constructor takes one or two arguments. The first argument,
url, specifies the URL to which to connect. The second,
protocols, if present, is either a string or an array of strings. If it is a string, it
is equivalent to an array consisting of just that string; if it is omitted, it is equivalent to
the empty array. Each string in the array is a subprotocol name. The connection will only be
established if the server reports that it has selected one of these subprotocols. The subprotocol
names must all be strings that match the requirements for elements that comprise the value of
Sec-WebSocket-Protocol
fields as defined by the
WebSocket protocol specification. [WSP]
When the WebSocket()
constructor is invoked, the UA must
run these steps:
Parse a WebSocket URL's components from
the url argument, to obtain host, port, resource
name, and secure. If this fails, throw a SyntaxError
exception and
abort these steps. [WSP]
If secure is false but the origin specified by the entry
settings object has a scheme component that is itself a secure protocol, e.g. HTTPS, then
throw a SecurityError
exception and abort these steps.
If port is a port to which the user agent is configured to block access, then
throw a SecurityError
exception and abort these steps. (User agents typically block
access to well-known ports like SMTP.)
Access to ports 80 and 443 should not be blocked, including the unlikely cases when secure is false but port is 443 or secure is true but port is 80.
If protocols is absent, let protocols be an empty array.
Otherwise, if protocols is present and a string, let protocols instead be an array consisting of just that string.
If any of the values in protocols occur more than once or otherwise fail to
match the requirements for elements that comprise the value of Sec-WebSocket-Protocol
fields as defined by the
WebSocket protocol specification, then throw a SyntaxError
exception and abort these
steps. [WSP]
Let origin be the ASCII serialisation of the origin specified by the entry settings object, converted to ASCII lowercase.
Return a new WebSocket
object, but continue these steps
in parallel.
Let the new object's client-specified protocols be the values (if any) given in protocols.
Establish a WebSocket connection given the set
(host, port, resource name, secure), along with the
protocols list, an empty list for the extensions, and origin. The
headers to send appropriate cookies must be a Cookie
header whose value is the cookie-string computed from the user's cookie store and
the URL url; for these purposes this is not a "non-HTTP" API. [WSP]
[COOKIES]
When the user agent validates the server's response during the "establish a WebSocket connection" algorithm, if the status code received from the server is not 101 (e.g. it is a redirect), the user agent must fail the WebSocket connection.
Following HTTP procedures here could introduce serious security problems in a Web browser context. For example, consider a host with a WebSocket server at one path and an open HTTP redirector at another. Suddenly, any script that can be given a particular WebSocket URL can be tricked into communicating to (and potentially sharing secrets with) any host on the Internet, even if the script checks that the URL has the right hostname.
If the establish a WebSocket
connection algorithm fails, it triggers the fail the
WebSocket connection algorithm, which then invokes the close the WebSocket connection algorithm, which then
establishes that the WebSocket connection is closed,
which fires the close
event as
described below.
The url
attribute must return the result of
resolving the URL that was passed to the
constructor, with the URL character encoding set to UTF-8. (It doesn't matter what it is resolved
relative to, since we already know it is an absolute URL.)
The readyState
attribute represents
the state of the connection. It can have the following values:
CONNECTING
(numeric value 0)OPEN
(numeric value 1)CLOSING
(numeric value 2)close()
method has been invoked.CLOSED
(numeric value 3)When the object is created its readyState
must be
set to CONNECTING
(0).
The extensions
attribute must
initially return the empty string. After the WebSocket
connection is established, its value might change, as defined below.
The extensions
attribute returns
the extensions selected by the server, if any. (Currently this will only ever be the empty
string.)
The protocol
attribute must initially
return the empty string. After the WebSocket connection is established, its value might
change, as defined below.
The protocol
attribute returns the
subprotocol selected by the server, if any. It can be used in conjunction with the array form of
the constructor's second argument to perform subprotocol negotiation.
The close()
method must run the following
steps:
If the method's first argument is present but is neither an integer equal to 1000 nor an
integer in the range 3000 to 4999, throw an InvalidAccessError
exception and abort
these steps.
If the method's second argument is present, then run these substeps:
Let raw reason be the method's second argument.
Let reason be the result of encoding raw reason as UTF-8.
If reason is longer than 123 bytes, then throw a
SyntaxError
exception and abort these steps. [ENCODING]
Run the first matching steps from the following list:
readyState
attribute is in the CLOSING
(2) or CLOSED
(3) stateDo nothing.
The connection is already closing or is already closed. If it has not already,
a close
event will eventually fire as described below.
Fail the WebSocket connection and set the readyState
attribute's value to CLOSING
(2). [WSP]
The fail the WebSocket connection
algorithm invokes the close the WebSocket connection
algorithm, which then establishes that the WebSocket
connection is closed, which fires the close
event as described below.
Start the WebSocket closing
handshake and set the readyState
attribute's value to CLOSING
(2). [WSP]
If the first argument is present, then the status code to use in the WebSocket Close message must be the integer given by the first argument. [WSP]
If the second argument is also present, then reason must be provided in the Close message after the status code. [ENCODING] [WSP]
The start the WebSocket
closing handshake algorithm eventually invokes the close the WebSocket connection algorithm, which then
establishes that the WebSocket connection is closed,
which fires the close
event as
described below.
Set the readyState
attribute's value to CLOSING
(2).
The WebSocket closing
handshake is started, and will eventually invoke the close the WebSocket connection algorithm, which will
establish that the WebSocket connection is closed,
and thus the close
event will fire, as described below.
The close()
method does not discard
previously sent messages before starting the WebSocket closing handshake — even if, in
practice, the user agent is still busy sending those messages, the handshake will only start after
the messages are sent.
The bufferedAmount
attribute must
return the number of bytes of application data (UTF-8 text and binary data) that have been queued
using send()
but that, as of the last time the
event loop reached step 1, had not yet been transmitted to the network. (This thus
includes any text sent during the execution of the current task, regardless of whether the user
agent is able to transmit text in the background in parallel with script execution.) This does not include
framing overhead incurred by the protocol, or buffering done by the operating system or network
hardware. If the connection is closed, this attribute's value will only increase with each call to
the send()
method (the number does not reset to zero once
the connection closes).
In this simple example, the bufferedAmount
attribute is used to ensure that updates are sent either at the rate of one update every 50ms, if
the network can handle that rate, or at whatever rate the network can handle, if that is
too fast.
var socket = new WebSocket('ws://game.example.com:12010/updates'); socket.onopen = function () { setInterval(function() { if (socket.bufferedAmount == 0) socket.send(getUpdateData()); }, 50); };
The bufferedAmount
attribute can also be
used to saturate the network without sending the data at a higher rate than the network can
handle, though this requires more careful monitoring of the value of the attribute over time.
When a WebSocket
object is created, its binaryType
IDL attribute must be set to the string
"blob
". On getting, it must return the last value it was
set to. On setting, the user agent must set the IDL attribute to the new value.
This attribute allows authors to control how binary data is exposed to scripts. By
setting the attribute to "blob
", binary data
is returned in Blob
form; by setting it to "arraybuffer
", it is returned in
ArrayBuffer
form. User agents can use this as a hint for how to handle incoming
binary data: if the attribute is set to "blob
", it is
safe to spool it to disk, and if it is set to "arraybuffer
", it is likely more efficient to keep the
data in memory. Naturally, user agents are encouraged to use more subtle heuristics to decide
whether to keep incoming data in memory or not, e.g. based on how big the data is or how common it
is for a script to change the attribute at the last minute. This latter aspect is important in
particular because it is quite possible for the attribute to be changed after the user agent has
received the data but before the user agent has fired the event for it.
The send(data)
method transmits
data using the connection. If the readyState
attribute is CONNECTING
, it must throw an
InvalidStateError
exception. Otherwise, the user agent must run the appropriate set
of steps from the following list:
If the WebSocket connection is
established and the WebSocket closing
handshake has not yet started, then the user agent must send a WebSocket Message comprised of the data argument using
a text frame opcode; if the data cannot be sent, e.g. because it would need to be buffered but
the buffer is full, the user agent must flag the
WebSocket as full and then close the WebSocket
connection. Any invocation of this method with a string argument that does not throw an
exception must increase the bufferedAmount
attribute by the number of bytes needed to express the argument as UTF-8. [UNICODE]
[ENCODING] [WSP]
Blob
objectIf the WebSocket connection is established, and
the WebSocket closing handshake has not yet
started, then the user agent must send a WebSocket
Message comprised of data using a binary frame opcode; if the data cannot be
sent, e.g. because it would need to be buffered but the buffer is full, the user agent must
flag the WebSocket as full and then close the WebSocket connection. The data to be sent is the
raw data represented by the Blob
object. Any invocation of this method with a
Blob
argument that does not throw an exception must increase the bufferedAmount
attribute by the size of the
Blob
object's raw data, in bytes. [WSP] [FILEAPI]
ArrayBuffer
objectIf the WebSocket connection is established, and
the WebSocket closing handshake has not yet
started, then the user agent must send a WebSocket
Message comprised of data using a binary frame opcode; if the data cannot be
sent, e.g. because it would need to be buffered but the buffer is full, the user agent must
flag the WebSocket as full and then close the WebSocket connection. The data to be sent is the
data stored in the buffer described by the ArrayBuffer
object. Any invocation of
this method with an ArrayBuffer
argument that does not throw an exception must
increase the bufferedAmount
attribute by the
length of the ArrayBuffer
in bytes. [WSP] [ECMA262]
ArrayBufferView
type definitionIf the WebSocket connection is established, and
the WebSocket closing handshake has not yet
started, then the user agent must send a WebSocket
Message comprised of data using a binary frame opcode; if the data cannot be
sent, e.g. because it would need to be buffered but the buffer is full, the user agent must
flag the WebSocket as full and then close the WebSocket connection. The data to be sent is the
data stored in the section of the buffer described by the ArrayBuffer
object that
data references. Any invocation of this method with this kind of argument that does
not throw an exception must increase the bufferedAmount
attribute by the length of
data's buffer in bytes. [WSP] [ECMA262]
The following are the event handlers (and their corresponding event handler event types) that must be supported, as event
handler IDL attributes, by all objects implementing the WebSocket
interface:
Event handler | Event handler event type |
---|---|
onopen | open
|
onmessage | message
|
onerror | error
|
onclose | close
|
When the WebSocket connection is established, the user agent must queue a task to run these steps:
If the WebSocket
object's client-specified protocols was not an
empty list, but the subprotocol in use is the null
value, then fail the WebSocket connection, set the readyState
attribute's value to CLOSING
(2), and abort these steps. [WSP]
Change the readyState
attribute's value to
OPEN
(1).
Change the extensions
attribute's value to
the extensions in use, if is not the null
value. [WSP]
Change the protocol
attribute's value to the
subprotocol in use, if is not the null value. [WSP]
Act as if the user agent had received a
set-cookie-string consisting of the cookies set during the server's opening
handshake, for the URL url given to the WebSocket()
constructor. [COOKIES] [ENCODING]
[WSP]
Fire a simple event named open
at the
WebSocket
object.
When a WebSocket message has been received with type type and data data, the user agent must queue a task to follow these steps: [WSP]
If the readyState
attribute's value is not
OPEN
(1), then abort these steps.
Let event be a newly created trusted
event that uses the MessageEvent
interface, with the event type message
, which does not bubble, is not cancelable, and has no
default action.
Initialise event's origin
attribute to the Unicode serialisation
of the origin of the URL that was passed to the WebSocket
object's constructor.
If type indicates that the data is Text, then initialise event's data
attribute to data.
If type indicates that the data is Binary, and binaryType
is set to "blob
", then initialise event's data
attribute to a new Blob
object that
represents data as its raw data. [FILEAPI]
If type indicates that the data is Binary, and binaryType
is set to "arraybuffer
", then initialise event's
data
attribute to a new ArrayBuffer
object whose contents are data. [ECMA262]
User agents are encouraged to check if they can perform the above steps
efficiently before they run the task, picking tasks from other task
queues while they prepare the buffers if not. For example, if the binaryType
attribute was set to "blob
" when the data arrived, and the user agent spooled all
the data to disk, but just before running the above task for
this particular message the script switched binaryType
to "arraybuffer
", the user agent would want to page the
data back to RAM before running this task so as to avoid
stalling the main thread while it created the ArrayBuffer
object.
Here is an example of how to define a handler for the message
event in the case of text frames:
mysocket.onmessage = function (event) { if (event.data == 'on') { turnLampOn(); } else if (event.data == 'off') { turnLampOff(); } };
The protocol here is a trivial one, with the server just sending "on" or "off" messages.
When the WebSocket closing handshake is
started, the user agent must queue a task to change the readyState
attribute's value to CLOSING
(2). (If the close()
method was called, the readyState
attribute's value will already be set to CLOSING
(2) when this task runs.) [WSP]
When the WebSocket connection is closed, possibly cleanly, the user agent must queue a task to run the following substeps:
Change the readyState
attribute's value to
CLOSED
(3).
If the user agent was required to fail the WebSocket
connection, or if the the WebSocket connection was
closed after being flagged as full,
fire a simple event named error
at the WebSocket
object. [WSP]
Create a trusted event that uses the
CloseEvent
interface, with the event type close
,
which does not bubble, is not cancelable, has no default action, whose wasClean
attribute is initialised to true if the
connection closed cleanly and false otherwise, whose code
attribute is initialised to the WebSocket connection close code, and whose reason
attribute is initialised to the result of applying
the UTF-8 decoder to the WebSocket
connection close reason, and dispatch the event
at the WebSocket
object. [WSP]
User agents must not convey any failure information to scripts in a way that would allow a script to distinguish the following situations:
In all of these cases, the the WebSocket connection close code would be 1006, as required by the WebSocket Protocol specification. [WSP]
Allowing a script to distinguish these cases would allow a script to probe the user's local network in preparation for an attack.
In particular, this means the code 1015 is not used by the user agent (unless the server erroneously uses it in its close frame, of course).
The task source for all tasks queued in this section is the WebSocket task source.
The WebSocket protocol specification defines Ping and Pong frames that can be used for keep-alive, heart-beats, network status probing, latency instrumentation, and so forth. These are not currently exposed in the API.
User agents may send ping and unsolicited pong frames as desired, for example in an attempt to maintain local network NAT mappings, to detect failed connections, or to display latency metrics to the user. User agents must not use pings or unsolicited pongs to aid the server; it is assumed that servers will solicit pongs whenever appropriate for the server's needs.
The steps to parse a WebSocket URL's components from a string url are as follows. These steps return either a host, a port, a resource name, and a secure flag, or they fail.
If the url string is not an absolute URL, then fail this algorithm.
Resolve the url string, with the URL character encoding set to UTF-8. [ENCODING]
It doesn't matter what it is resolved relative to, since we already know it is an absolute URL at this point.
If the resulting parsed URL does not have a scheme component whose value is either "ws
" or "wss
", then fail this algorithm.
If the resulting parsed URL has a non-null fragment component, then fail this algorithm.
If the scheme component of the resulting
parsed URL is "ws
", set secure to false;
otherwise, the scheme component is "wss
", set secure to true.
Let host be the value of the resulting parsed URL's host component.
If the resulting parsed URL has a port component that is not the empty string, then let port be that component's value; otherwise, there is no explicit port.
If there is no explicit port, then: if secure is false, let port be 80, otherwise let port be 443.
Let resource name be the value of the resulting parsed URL's path component (which might be empty).
If resource name is the empty string, set it to a single character U+002F SOLIDUS (/).
If the resulting parsed URL has a non-null query component, then append a single U+003F QUESTION MARK character (?) to resource name, followed by the value of the query component.
Return host, port, resource name, and secure.
CloseEvent
interfaces[Constructor(DOMString type, optional CloseEventInit eventInitDict), Exposed=(Window,Worker)] interface CloseEvent : Event { readonly attribute boolean wasClean; readonly attribute unsigned short code; readonly attribute DOMString reason; }; dictionary CloseEventInit : EventInit { boolean wasClean; unsigned short code; DOMString reason; };
The wasClean
attribute must return the
value it was initialised to. When the object is created, this attribute must be initialised to
false. It represents whether the connection closed cleanly or not.
The code
attribute must return the value it
was initialised to. When the object is created, this attribute must be initialised to zero. It
represents the WebSocket connection close code provided by the server.
The reason
attribute must return the
value it was initialised to. When the object is created, this attribute must be initialised to
empty string. It represents the WebSocket connection close reason provided by the server.
A WebSocket
object whose readyState
attribute's value was set to CONNECTING
(0) as of
the last time the event loop reached step 1 must not be garbage collected if there
are any event listeners registered for open
events, message
events, error
events, or
close
events.
A WebSocket
object whose readyState
attribute's value was set to OPEN
(1) as of the last time
the event loop reached step 1 must not be garbage collected if there are any event
listeners registered for message
events, error
, or close
events.
A WebSocket
object whose readyState
attribute's value was set to CLOSING
(2) as of the
last time the event loop reached step 1 must not be garbage collected if there are
any event listeners registered for error
or close
events.
A WebSocket
object with an
established connection that has data queued to be transmitted to the network must not be
garbage collected. [WSP]
If a WebSocket
object is garbage collected while its connection is still open, the
user agent must start the WebSocket closing
handshake, with no status code for the Close message. [WSP]
If a user agent is to make disappear a WebSocket
object (this happens
when a Document
object goes away), the user agent must follow the first appropriate
set of steps from the following list:
Start the WebSocket closing handshake, with the status code to use in the WebSocket Close message being 1001. [WSP]
Do nothing.
Web browsers, for security and privacy reasons, prevent documents in different domains from affecting each other; that is, cross-site scripting is disallowed.
While this is an important security feature, it prevents pages from different domains from communicating even when those pages are not hostile. This section introduces a messaging system that allows documents to communicate with each other regardless of their source domain, in a way designed to not enable cross-site scripting attacks.
This API has some privacy implications that may not be immediately obvious.
The task source for the tasks in cross-document messaging is the posted message task source.
This section is non-normative.
For example, if document A contains an iframe
element that contains document B,
and script in document A calls postMessage()
on the
Window
object of document B, then a message event will be fired on that object,
marked as originating from the Window
of document A. The script in document A might
look like:
var o = document.getElementsByTagName('iframe')[0]; o.contentWindow.postMessage('Hello world', 'http://b.example.org/');
To register an event handler for incoming events, the script would use addEventListener()
(or similar mechanisms). For example, the script in document B
might look like:
window.addEventListener('message', receiver, false); function receiver(e) { if (e.origin == 'http://example.com') { if (e.data == 'Hello world') { e.source.postMessage('Hello', e.origin); } else { alert(e.data); } } }
This script first checks the domain is the expected domain, and then looks at the message, which it either displays to the user, or responds to by sending a message back to the document which sent the message in the first place.
Use of this API requires extra care to protect users from hostile entities abusing a site for their own purposes.
Authors should check the origin
attribute to
ensure that messages are only accepted from domains that they expect to receive messages from.
Otherwise, bugs in the author's message handling code could be exploited by hostile sites.
Furthermore, even after checking the origin
attribute, authors should also check that the data in question is of the expected format.
Otherwise, if the source of the event has been attacked using a cross-site scripting flaw, further
unchecked processing of information sent using the postMessage()
method could result in the attack being
propagated into the receiver.
Authors should not use the wildcard keyword (*) in the targetOrigin argument in messages that contain any confidential information, as otherwise there is no way to guarantee that the message is only delivered to the recipient to which it was intended.
Authors who accept messages from any origin are encouraged to consider the risks of a denial-of-service attack. An attacker could send a high volume of messages; if the receiving page performs expensive computation or causes network traffic to be sent for each such message, the attacker's message could be multplied into a denial-of-service attack. Authors are encouraged to employ rate limiting (only accepting a certain number of messages per minute) to make such attacks impractical.
The integrity of this API is based on the inability for scripts of one origin to
post arbitrary events (using dispatchEvent()
or otherwise) to objects in
other origins (those that are not the same).
Implementors are urged to take extra care in the implementation of this feature. It allows authors to transmit information from one domain to another domain, which is normally disallowed for security reasons. It also requires that UAs be careful to allow access to certain properties but not others.
User agents are also encouraged to consider rate-limiting message traffic between different origins, to protect naïve sites from denial-of-service attacks.
postMessage
(message, targetOrigin [, transfer ] )Posts a message to the given window. Messages can be structured objects, e.g. nested objects
and arrays, can contain JavaScript values (strings, numbers, Date
objects, etc), and can contain certain data objects such as File
Blob
,
FileList
, and ArrayBuffer
objects.
Objects listed in transfer are transferred, not just cloned, meaning that they are no longer usable on the sending side.
If the origin of the target window doesn't match the given origin, the message is discarded,
to avoid information leakage. To send the message to the target regardless of origin, set the
target origin to "*
". To restrict the message to same-origin targets only,
without needing to explicitly state the origin, set the target origin to "/
".
Throws a DataCloneError
exception if transfer array contains
duplicate objects or if message could not be cloned.
When posting a message to a Window
of a browsing context
that has just been navigated to a new Document
is likely to result in the message not
receiving its intended recipient: the scripts in the target browsing context have to
have had time to set up listeners for the messages. Thus, for instance, in situations where a
message is to be sent to the Window
of newly created child iframe
,
authors are advised to have the child Document
post a message to their parent
announcing their readiness to receive messages, and for the parent to wait for this message before
beginning posting messages.
When a script invokes the postMessage(message, targetOrigin, transfer)
method (with two or three arguments) on a
Window
object, the user agent must follow these steps:
If the value of the targetOrigin argument is neither a single U+002A
ASTERISK character (*), a single U+002F SOLIDUS character (/), nor an absolute URL,
then throw a SyntaxError
exception and abort the overall set of steps.
Let new ports be an empty array.
Let transfer map be an empty association list of
Transferable
objects to placeholder objects.
If the method was invoked with a third argument transfer, run these substeps:
If any object is listed in transfer more than once, or any of the
Transferable
objects listed in transfer are marked as neutered, then throw a
DataCloneError
exception and abort these steps.
For each object x in transfer in turn, add a
mapping from x to a new unique placeholder object created for x to transfer map, and if x is a
MessagePort
object, also append the placeholder object to the new
ports array.
Let message clone be the result of obtaining a structured clone of the message argument, with transfer map as the transfer map. If this throws an exception, then throw that exception and abort these steps.
If the method was invoked with a third argument transfer, run these substeps:
Let new owner be the environment settings object of the
Window
object on which the method was invoked.
For each object x in transfer in turn, obtain a new object y by transferring the object x to new owner, and replace the placeholder object that was created for the object x by the new object y wherever the placeholder exists (i.e. in message clone and in new ports).
Make new ports into a read only array.
Return from the postMessage()
method, but
in parallel continue running these steps.
If the targetOrigin argument is a single literal U+002F SOLIDUS
character (/), and the Document
of the Window
object on which the
method was invoked does not have the same origin as the responsible
document specified by the entry settings object, then abort these steps
silently.
Otherwise, if the targetOrigin argument is an absolute URL,
and the Document
of the Window
object on which the method was invoked
does not have the same origin as targetOrigin, then abort
these steps silently.
Otherwise, the targetOrigin argument is a single literal U+002A ASTERISK character (*), and no origin check is made.
Create a trusted event that uses the
MessageEvent
interface, with the event type message
, which does not bubble, is not cancelable, and has no
default action. The data
attribute must be
initialised to the value of message clone, the origin
attribute must be initialised to the Unicode serialisation of the
origin specified by the incumbent settings object, the source
attribute must be initialised to the
WindowProxy
object corresponding to the global object (a
Window
object) specified by the incumbent settings object, and the
ports
attribute must be initialised to the new
ports array.
Queue a task to dispatch the
event created in the previous step at the Window
object on which the method was
invoked. The task source for this task is the
posted message task source.
This section is non-normative.
To enable independent pieces of code (e.g. running in different browsing contexts) to communicate directly, authors can use channel messaging.
Communication channels in this mechanism are implemented as two-ways pipes, with a port at each end. Messages sent in one port are delivered at the other port, and vice-versa. Messages are delivered as DOM events, without interrupting or blocking running tasks.
To create a connection (two "entangled" ports), the MessageChannel()
constructor is called:
var channel = new MessageChannel();
One of the ports is kept as the local port, and the other port is sent to the remote code, e.g.
using postMessage()
:
otherWindow.postMessage('hello', 'http://example.com', [channel.port2]);
To send messages, the postMessage()
method on
the port is used:
channel.port1.postMessage('hello');
To receive messages, one listens to message
events:
channel.port1.onmessage = handleMessage; function handleMessage(event) { // message is in event.data // ... }
Data sent on a port can be structured data; for example here an array of strings is passed on a
MessagePort
:
port1.postMessage(['hello', 'world']);
This section is non-normative.
In this example, two JavaScript libraries are connected to each other using
MessagePort
s. This allows the libraries to later be hosted in different frames, or
in Worker
objects, without any change to the APIs.
<script src="contacts.js"></script> <!-- exposes a contacts object --> <script src="compose-mail.js"></script> <!-- exposes a composer object --> <script> var channel = new MessageChannel(); composer.addContactsProvider(channel.port1); contacts.registerConsumer(channel.port2); </script>
Here's what the "addContactsProvider()" function's implementation could look like:
function addContactsProvider(port) { port.onmessage = function (event) { switch (event.data.messageType) { 'search-result': handleSearchResult(event.data.results); break; 'search-done': handleSearchDone(); break; 'search-error': handleSearchError(event.data.message); break; // ... } }; };
Alternatively, it could be implemented as follows:
function addContactsProvider(port) { port.addEventListener('message', function (event) { if (event.data.messageType == 'search-result') handleSearchResult(event.data.results); }); port.addEventListener('message', function (event) { if (event.data.messageType == 'search-done') handleSearchDone(); }); port.addEventListener('message', function (event) { if (event.data.messageType == 'search-error') handleSearchError(event.data.message); }); // ... port.start(); };
The key difference is that when using addEventListener()
, the start()
method must also be invoked. When using onmessage
, the call to start()
is implied.
The start()
method, whether called explicitly or
implicitly (by setting onmessage
), starts the
flow of messages: messages posted on message ports are initially paused, so that they don't get
dropped on the floor before the script has had a chance to set up its handlers.
This section is non-normative.
Ports can be viewed as a way to expose limited capabilities (in the object-capability model sense) to other actors in the system. This can either be a weak capability system, where the ports are merely used as a convenient model within a particular origin, or as a strong capability model, where they are provided by one origin provider as the only mechanism by which another origin consumer can effect change in or obtain information from provider.
For example, consider a situation in which a social Web site embeds in one iframe
the user's e-mail contacts provider (an address book site, from a second origin), and in a second
iframe
a game (from a third origin). The outer social site and the game in the second
iframe
cannot access anything inside the first iframe
; together they can
only:
iframe
to a new URL, such as the same
URL but with a different fragment identifier, causing the Window
in the
iframe
to receive a hashchange
event.iframe
, causing the Window
in the iframe
to
receive a resize
event.message
event to the Window
in the
iframe
using the window.postMessage()
API.The contacts provider can use these methods, most particularly the third one, to provide an API
that can be accessed by other origins to manipulate the user's address book. For example, it could
respond to a message "add-contact Guillaume Tell
<tell@pomme.example.net>
" by adding the given person and e-mail address to the user's
address book.
To avoid any site on the Web being able to manipulate the user's contacts, the contacts provider might only allow certain trusted sites, such as the social site, to do this.
Now suppose the game wanted to add a contact to the user's address book, and that the social site was willing to allow it to do so on its behalf, essentially "sharing" the trust that the contacts provider had with the social site. There are several ways it could do this; most simply, it could just proxy messages between the game site and the contacts site. However, this solution has a number of difficulties: it requires the social site to either completely trust the game site not to abuse the privilege, or it requires that the social site verify each request to make sure it's not a request that it doesn't want to allow (such as adding multiple contacts, reading the contacts, or deleting them); it also requires some additional complexity if there's ever the possibility of multiple games simultaneously trying to interact with the contacts provider.
Using message channels and MessagePort
objects, however, all of these problems can
go away. When the game tells the social site that it wants to add a contact, the social site can
ask the contacts provider not for it to add a contact, but for the capability to add a
single contact. The contacts provider then creates a pair of MessagePort
objects, and
sends one of them back to the social site, who forwards it on to the game. The game and the
contacts provider then have a direct connection, and the contacts provider knows to only honor a
single "add contact" request, nothing else. In other words, the game has been granted the
capability to add a single contact.
This section is non-normative.
Continuing the example from the previous section, consider the contacts provider in particular.
While an initial implementation might have simply used XMLHttpRequest
objects in the
service's iframe
, an evolution of the service might instead want to use a shared worker with a single WebSocket
connection.
If the initial design used MessagePort
objects to grant capabilities, or even just
to allow multiple simultaneous independent sessions, the service implementation can switch from
the XMLHttpRequest
s-in-each-iframe
model to the
shared-WebSocket
model without changing the API at all: the ports on the service
provider side can all be forwarded to the shared worker without it affecting the users of the API
in the slightest.
[Constructor, Exposed=(Window,Worker)] interface MessageChannel { readonly attribute MessagePort port1; readonly attribute MessagePort port2; };
MessageChannel
()Returns a new MessageChannel
object with two new MessagePort
objects.
port1
Returns the first MessagePort
object.
port2
Returns the second MessagePort
object.
When the MessageChannel()
constructor is
called, it must run the following algorithm:
Create a new MessagePort
object whose owner is the incumbent settings object, and let
port1 be that object.
Create a new MessagePort
object whose owner is the incumbent settings object, and let
port2 be that object.
Entangle the port1 and port2 objects.
Instantiate a new MessageChannel
object, and let channel be that
object.
Let the port1
attribute of the
channel object be port1.
Let the port2
attribute of the
channel object be port2.
Return channel.
The port1
and port2
attributes must return the values they were
assigned when the MessageChannel
object was created.
Each channel has two message ports. Data sent through one port is received by the other port, and vice versa.
[Exposed=(Window,Worker)] interface MessagePort : EventTarget { void postMessage(any message, optional sequence<Transferable> transfer); void start(); void close(); // event handlers attribute EventHandler onmessage; }; // MessagePort implements Transferable;
postMessage
(message [, transfer] )Posts a message through the channel. Objects listed in transfer are transferred, not just cloned, meaning that they are no longer usable on the sending side.
Throws a DataCloneError
exception if transfer array contains
duplicate objects or the source or target ports, or if message could not be
cloned.
start
()Begins dispatching messages received on the port.
close
()Disconnects the port, so that it is no longer active.
Each MessagePort
object can be entangled with another (a symmetric relationship).
Each MessagePort
object also has a task source called the port
message queue, initially empty. A port message queue can be enabled or
disabled, and is initially disabled. Once enabled, a port can never be disabled again (though
messages in the queue can get moved to another queue or removed altogether, which has much the
same effect). A MessagePort
also has a has been shipped flag, which must
initially be false, and an owner, which is a settings
object set when the object is created, as described below.
When a port's port message queue is enabled, the event loop must use it as one of its task sources. When a port's owner specifies a responsible event loop that is a browsing context event loop, all tasks queued on its port message queue must be associated with the responsible document specified by the port's owner.
If the port's owner specifies a responsible document that is fully active, but the event listeners all have scripts whose settings objects specify responsible documents that are not fully active, then the messages will be lost.
Each event loop has a task source called the unshipped port
message queue. This is a virtual task source: it must act as if it contained
the tasks of each port message queue of each
MessagePort
whose has been shipped flag is false, whose port
message queue is enabled, and whose owner
specifies that event loop as the responsible event loop, in the order in
which they were added to their respective task source. When a task would be removed from the unshipped port message
queue, it must instead be removed from its port message queue.
When a MessagePort
's has been shipped flag is false, its port
message queue must be ignored for the purposes of the event loop. (The
unshipped port message queue is used instead.)
The has been shipped flag is set to true when a port, its twin, or
the object it was cloned from, is or has been transferred. When a MessagePort
's has been shipped flag
is true, its port message queue acts as a first-class task source,
unaffected to any unshipped port message queue.
When the user agent is to create a new MessagePort
object with a
particular settings object as its owner, it must instantiate a
new MessagePort
object, and let its owner be
owner.
When the user agent is to entangle two MessagePort
objects, it must run
the following steps:
If one of the ports is already entangled, then disentangle it and the port that it was entangled with.
If those two previously entangled ports were the two ports of a
MessageChannel
object, then that MessageChannel
object no longer
represents an actual channel: the two ports in that object are no longer entangled.
Associate the two ports to be entangled, so that they form the two parts of a new channel.
(There is no MessageChannel
object that represents this channel.)
Two ports A and B that have gone through this step are now said to be entangled; one is entangled to the other, and vice versa.
While this specification describes this process as instantaneous, implementations are more likely to implement it via message passing. As with all algorithms, the key is "merely" that the end result be indistinguishable, in a black-box sense, from the specification.
When the user agent is to clone a port original port, with the
clone being owned by owner, it must run the following steps, which return a
new MessagePort
object. These steps must be run atomically.
Set original port's has been shipped flag to true.
Create a new MessagePort
object whose owner is owner, and let new port be that object.
Set new port's has been shipped flag to true.
Move all the tasks that are to fire message
events in the port message queue of original port to the port message queue of new
port, if any, leaving the new port's port message queue
in its initial disabled state, and, if the new port's owner specifies a responsible event loop that is
a browsing context event loop, associating the moved tasks with the responsible document specified by new port's owner.
If the original port is entangled with another port, then run these substeps:
Let the remote port be the port with which the original port is entangled.
Set remote port's has been shipped flag to true.
Entangle the remote port and new port objects. The original port object will be disentangled by this process.
Return new port. It is the clone.
To transfer a
MessagePort
object old to a new owner owner,
a user agent must clone the old object with
the clone being owned by owner, thus obtaining new, must
neuter the old port, and
must finally return new.
The postMessage()
method, when
called on a port source port, must cause the user agent to run the following
steps:
Let target port be the port with which source port is entangled, if any.
Let doomed be false. It is set to true if a condition is detected that will make this message cause the port to be unusable; specifically, if the message contains target port as one of the objects being transferred. (This condition cannot necessarily be detected when the method is called.)
Let new ports be an empty array.
Let transfer map be an empty association list of
Transferable
objects to placeholder objects.
If the method was invoked with a second argument transfer, run these substeps:
If any object is listed in transfer more than once, or any of the
Transferable
objects listed in transfer are marked as neutered, then throw a
DataCloneError
exception and abort these steps.
If any of the objects in transfer are the source
port, then throw a DataCloneError
exception and abort these steps.
If any of the objects in transfer are the target port, if any, then let doomed be true, and optionally report to a developer console that the target port was posted to itself, causing the communication channel to be lost.
For each object x in transfer in turn, add a
mapping from x to a new unique placeholder object created for x to transfer map, and if x is a
MessagePort
object, also append the placeholder object to the new
ports array.
Let message clone be the result of obtaining a structured clone of the message argument, with transfer map as the transfer map. If this throws an exception, then throw that exception and abort these steps.
If the method was invoked with a second argument transfer, run these substeps:
Let new owner be the owner of
target port, if there is a target port and doomed is false, or else some arbitrary owner. (This new
owner is used when transferring objects below. If there is no target
port, or if the target port is one of the objects being transferred, the Transferable
objects given in the second argument, if any, are still transferred, but since they are then discarded, it doesn't matter where they
are transferred to.)
For each object x in transfer in turn, obtain a new object y by transferring the object x to new owner, and replace the placeholder object that was created for the object x by the new object y wherever the placeholder exists (i.e. in message clone and in new ports).
Make new ports into a read only array.
If there is no target port (i.e. if source port is not entangled), or if doomed is true, then abort these steps.
Create an event e that uses the MessageEvent
interface, with the name message
, which does not bubble, is not cancelable, and has no
default action.
Let the data
attribute of e be
initialised to the value of message clone.
Let the ports
attribute of e be
initialised to the new ports array.
Add a task that runs the following steps to the port message queue of target port:
Let target be the MessagePort
in whose port message
queue the event e now finds itself.
Dispatch e at target.
The start()
method must enable its port's
port message queue, if it is not already enabled.
The close()
method, when called on a port
local port that is entangled with another port, must cause the user agent to
disentangle the two ports. If the method is called on a port that is not entangled, then the
method must do nothing.
The following are the event handlers (and their corresponding event handler event types) that must be supported, as event
handler IDL attributes, by all objects implementing the MessagePort
interface:
Event handler | Event handler event type |
---|---|
onmessage | message
|
The first time a MessagePort
object's onmessage
IDL attribute is set, the port's port
message queue must be enabled, as if the start()
method had been called.
The API described in this section is controversial, as, in an attempt to solve an architectural memory leak, it instead exposes the details of Garbage Collection. This is a lose-lose scenario. A better solution is really needed here.
Broadcasting to many ports is in principle relatively simple: keep an array of
MessagePort
objects to send messages to, and iterate through the array to send a
message. However, this has one rather unfortunate effect: it prevents the ports from being garbage
collected, even if the other side has gone away.
To avoid this problem, the PortCollection
object can be used. It acts as an opaque
array of MessagePort
objects, thus allowing the objects to be garbage collected when
they stop being relevant, while still allowing scripts to iterate over the
MessagePort
objects.
[Constructor, Exposed=(Window,Worker)] interface PortCollection { void add(MessagePort port); void remove(MessagePort port); void clear(); void iterate(PortCollectionCallback callback); }; callback PortCollectionCallback = void (MessagePort port);
PortCollection
()Returns a new empty PortCollection
object.
add
(port)Adds port to the collection, if it isn't already present.
remove
(port)Removes port from the collection, if it is present.
clear
()Removes all ports from the collection.
iterate
(callback)Calls callback for each port in the collection.
A PortCollection
object has an initially empty list of ports. When a MessagePort
object in
a list of ports is garbage collected, it must be
silently removed from that list of ports. Objects
in a list of ports are ordered chronologically by
the time at which they were most recently added; the least-recently added MessagePort
object is the first in the list, and the most-recently added MessagePort
is the last
in the list.
The PortCollection()
constructor must return
a new PortCollection
object (with an empty list of ports).
The add()
method must add the
MessagePort
given by the argument to the PortCollection
object's list of ports, unless the MessagePort
is
already in the list of ports, in which case the
method does nothing. (Calling this method with a port already in the list does not move the port
to the end of the list.)
The remove()
method must remove the
MessagePort
given by the argument from the PortCollection
object's list of ports, unless the MessagePort
is
not in the list of ports, in which case the
method does nothing.
The clear()
method must remove all
MessagePort
objects from the PortCollection
object's list of ports, returning it to the initial empty state.
If the list of ports is already empty, the method
does nothing.
The iterate()
method must invoke its
PortCollectionCallback
argument once for each MessagePort
object in the
object's list of ports, in the order defined
above, with each invocation being passed the corresponding MessagePort
object as the
callback's sole argument.
When a MessagePort
object o is entangled, user agents must
either act as if o's entangled MessagePort
object has a strong
reference to o, or as if the global object specified by o's owner has a strong reference to o.
Thus, a message port can be received, given an event listener, and then forgotten, and so long as that event listener could receive a message, the channel will be maintained.
Of course, if this was to occur on both sides of the channel, then both ports could be garbage collected, since they would not be reachable from live code, despite having a strong reference to each other.
Furthermore, a MessagePort
object must not be garbage collected while there exists
an event referenced by a task in a task queue that is to be dispatched on that MessagePort
object, or while the MessagePort
object's port message queue is enabled
and not empty.
There are no strong references from a PortCollection
object to its
MessagePort
objects. (That is in fact the whole point of PortCollection
objects: they allow for MessagePort
objects to be referenced without preventing them
from being garbage collected.)
Authors are strongly encouraged to explicitly close MessagePort
objects to disentangle them, so that their resources can be recollected. Creating many
MessagePort
objects and discarding them without closing them can lead to high
transient memory usage since garbage collection is not necessarily performed promptly, especially
for MessagePort
s where garbage collection can involve cross-process coordination.
Pages on a single origin opened by the same user in the same user agent but in different unrelated browsing contexts sometimes need to send notifications to each other, for example "hey, the user logged in over here, check your credentials again".
For elaborate cases, e.g. to manage locking of shared state, to manage synchronisation of
resources between a server and multiple local clients, to share a WebSocket
connection with a remote host, and so forth, shared workers are
the most appropriate solution.
For simple cases, though, where a shared worker would be an unreasonable overhead, authors can use the simple channel-based broadcast mechanism described in this section.
[Constructor(DOMString channel), Exposed=(Window,Worker)] interface BroadcastChannel : EventTarget { readonly attribute DOMString name; void postMessage(any message); void close(); attribute EventHandler onmessage; };
BroadcastChannel
(channel)Returns a new BroadcastChannel
object via which messages for the given channel can be sent and received.
name
Returns the channel ID (as passed to the constructor).
postMessage
(message)Sends the given message to other BroadcastChannel
objects set up for this channel. Messages can be structured objects, e.g. nested objects and arrays.
close
()Closes the BroadcastChannel
object, opening it up to garbage collection.
A BroadcastChannel
object has a channel name, a
BroadcastChannel
settings object, and a closed flag.
The BroadcastChannel()
constructor, when
invoked, must create and return a BroadcastChannel
object whose channel
name is the constructor's first argument, whose BroadcastChannel
settings object is the incumbent settings object, and whose closed flag is false.
The name
attribute must return the
channel name.
The postMessage()
method,
when invoked on a BroadcastChannel
object source with an
argument message, must run the following steps:
Let source settings be source's
BroadcastChannel
settings object.
If source's closed flag is
true, then throw an InvalidStateError
exception and abort these steps.
Let source channel be source's channel name.
Let cloned message be a structured clone of the message argument. If this throws an exception, then rethrow that exception and abort these steps.
Let destinations be a list of BroadcastChannel
objects that
match the following criteria:
Their BroadcastChannel
settings object specifies either:
a global object that is a Window
object and a
responsible document that is fully active, or
a global object that is a WorkerGlobalScope
object whose
closing
flag is false and whose
worker is a suspendable worker.
Their BroadcastChannel
settings object specifies an
origin that is the same origin as the origin specified
by source settings.
Their channel name is a case-sensitive match for source channel.
Their closed flag is false.
Remove source from destinations.
Sort destinations such that all BroadcastChannel
objects
whose BroadcastChannel
settings
objects specify the same responsible event loop are sorted in creation
order, oldest first. (This does not define a complete ordering. Within this constraint, user
agents may sort the list in any user-agent defined manner.)
For each BroadcastChannel
object in destinations,
queue a task that runs the following steps:
Create an event that uses the MessageEvent
interface, with the event type
message
, which does not bubble, is not cancelable, and has
no default action. The data
attribute must be
initialised to a structured clone of cloned message, and the origin
attribute must be initialised
to the Unicode serialisation of the
origin specified by source settings. This event is not trusted.
Dispatch the event at the
BroadcastChannel
object.
The tasks must use the DOM
manipulation task source, and, for those where the event loop specified by the target BroadcastChannel
object's
BroadcastChannel
settings object is a browsing context event loop, must
be associated with the responsible
document specified by that target BroadcastChannel
object's
BroadcastChannel
settings object.
While a BroadcastChannel
object whose closed flag is false has an event listener
registered for message
events, there must be a strong
reference from global object specified by the BroadcastChannel
object's
BroadcastChannel
settings object to the BroadcastChannel
object itself.
The close()
method must set the
closed flag of the
BroadcastChannel
object on which it was invoked to true.
Authors are strongly encouraged to explicitly close BroadcastChannel
objects when they are no longer needed, so that they can be garbage collected. Creating many
BroadcastChannel
objects and discarding them while leaving them with an event
listener and without closing them can lead to an apparent memory leak, since the objects will
continue to live for as long as they have an event listener (or until their page or worker is
closed).
The following are the event handlers (and their corresponding event handler event types) that must be supported, as event
handler IDL attributes, by all objects implementing the BroadcastChannel
interface:
Event handler | Event handler event type |
---|---|
onmessage | message
|
Suppose a page wants to know when the user logs out, even when the user does so from another tab at the same site:
var authChannel = new BroadcastChannel('auth'); authChannel.onmessage = function (event) { if (event.data == 'logout') showLogout(); } function logoutRequested() { // called when the user asks us to log them out doLogout(); showLogout(); authChannel.postMessage('logout'); } function doLogout() { // actually log the user out (e.g. clearing cookies) // ... } function showLogout() { // update the UI to indicate we're logged out // ... }