HTML
Living Standard — Last Updated 14 February 2017
← 8 Web application APIs —Table of Contents —10 Web workers →
- 9 Communication
9 Communication
9.1 The MessageEvent
interfaces
Messages in server-sent events, Web sockets, cross-document messaging, channel messaging, and broadcast channels use the MessageEvent
interface for theirmessage
events:
[Constructor(DOMString type, optional MessageEventInit eventInitDict), Exposed=(Window,Worker)]
interface MessageEvent : Event {
readonly attribute any data;
readonly attribute USVString origin;
readonly attribute DOMString lastEventId;
readonly attribute MessageEventSource? source;
readonly attribute FrozenArray<MessagePort> ports;
void initMessageEvent(DOMString type, boolean bubbles, boolean cancelable, any data, USVString origin, DOMString lastEventId, MessageEventSource? source, sequence<MessagePort> ports);
};
dictionary MessageEventInit : EventInit {
any data = null;
USVString origin = "";
DOMString lastEventId = "";
MessageEventSource? source = null;
sequence<MessagePort> ports = [];
};
typedef (WindowProxy or MessagePort or ServiceWorker) MessageEventSource;
MessageEventInit eventInitDict), Exposed=(Window,Worker)]
interface MessageEvent : Event {
readonly attribute any data;
readonly attribute USVString origin;
readonly attribute DOMString lastEventId;
readonly attribute MessageEventSource? source;
readonly attribute FrozenArray<MessagePort> ports;
void initMessageEvent(DOMString type, boolean bubbles, boolean cancelable, any data, USVString origin, DOMString lastEventId, MessageEventSource? source, sequence<MessagePort> ports);
};
dictionary MessageEventInit : EventInit {
any data = null;
USVString origin = "";
DOMString lastEventId = "";
MessageEventSource? source = null;
sequence<MessagePort> ports = [];
};
typedef (WindowProxy or MessagePort or ServiceWorker) MessageEventSource;
event . data
Returns the data of the message.
event . origin
Returns the origin of the message, for server-sent events and cross-document messaging.
event . lastEventId
Returns the last event ID string, for server-sent events.
event . source
Returns the WindowProxy
of the source window, forcross-document messaging, and the MessagePort
being attached, in theconnect
event fired atSharedWorkerGlobalScope
objects.
event . ports
Returns the MessagePort
array sent with the message, forcross-document messaging and channel messaging.
The data
attribute must return the value it was initialized to. It represents the message being sent.
The origin
attribute must return the value it was initialized to. It represents, inserver-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 orfragment).
The lastEventId
attribute must return the value it was initialized to. It represents, inserver-sent events, the last event ID string of the event source.
The source
attribute must return the value it was initialized to. It represents, incross-document messaging, the WindowProxy
of thebrowsing context of the Window
object from which the message came; and in theconnect
events used byshared workers, the newly connecting MessagePort
.
The ports
attribute must return the value it was initialized to. It represents, incross-document messaging and channel messaging, the MessagePort
array being sent.
The initMessageEvent()
method must initialize the event in a manner analogous to the similarly-namedinitEvent()
method.[DOM]
Various APIs (e.g., WebSocket
,EventSource
) use theMessageEvent
interface for theirmessage
event without using theMessagePort
API.
9.2 Server-sent events
Support: eventsourceChrome for Android55+Chrome 6+iOS Safari4.0+UC Browser for Android11+Firefox 6+IENoneSamsung Internet 4+Opera Mini NoneAndroid Browser4.4+Safari 5+EdgeNoneOpera 11+
Source: caniuse.com
9.2.1 Introduction
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 theEventSource
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 thetext/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
andremoveHandler
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 aniframe
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 onconnectionless push.
9.2.2 The EventSource
interface
[Constructor(USVString url, optional EventSourceInit eventSourceInitDict), Exposed=(Window,Worker)]
interface EventSource : EventTarget {
readonly attribute USVString 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;
};
Constructor(USVString url, optional EventSourceInit eventSourceInitDict), Exposed=(Window,Worker)]
interface EventSource : EventTarget {
readonly attribute USVString 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;
};
Each EventSource
object has the following associated with it:
-
A url (a URL record). Set during construction.
-
A request. This must initially be null.
-
A reconnection time, in milliseconds. This must initially be a user-agent-defined value, probably in the region of a few seconds.
-
A last event ID string. This must initially be the empty string.
Apart from url these are not currently exposed on the EventSource
object.
The EventSource(url, eventSourceInitDict)
constructor, when invoked, must run these steps:
-
Let ev be a new
EventSource
object. -
Let settings be ev's relevant settings object.
-
Let urlRecord be the result of parsing url with settings's API base URL and settings's API URL character encoding.
-
If urlRecord is failure, then throw a "
SyntaxError
"DOMException
. -
Set ev's url to urlRecord.
-
Let corsAttributeState be Anonymous.
-
If the value of eventSourceInitDict's
withCredentials
member is true, then setcorsAttributeState to Use Credentials and set ev'swithCredentials
attribute to true. -
Let request be the result of creating a potential-CORS request given urlRecord, the empty string, andcorsAttributeState, and with the same-origin fallback flag set.
-
Set request's client to settings.
-
User agents may set `
Accept
`/`text/event-stream
` in request's header list. -
Set request's cache mode to "
no-store
". -
Set ev's request to request.
-
Return ev, but continue these steps in parallel.
-
Fetchrequest.
The url
attribute's getter must return theserialization of this EventSource
object'surl.
The withCredentials
attribute must return the value to which it was last initialized. When the object is created, it must be initialized to false.
The readyState
attribute represents the state of the connection. It can have the following values:
CONNECTING
(numeric value 0)
The connection has not yet been established, or it was closed and the user agent is reconnecting.
OPEN
(numeric value 1)
The user agent has an open connection and is dispatching events as it receives them.
CLOSED
(numeric value 2)
The connection is not open, and the user agent is not trying to reconnect. Either there was a fatal error or theclose()
method was invoked.
When the object is created its readyState
must be set toCONNECTING
(0). The rules given below for handling the connection define when the value changes.
The close()
method must abort any instances of thefetch algorithm started for this EventSource
object, and must set thereadyState
attribute toCLOSED
.
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 |
9.2.3 Processing model
The resource indicated in the argument to the EventSource
constructor is fetched when the constructor is run.
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 mustannounce the connection.
The task that the networking task source places on the task queue once fetching 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 fetching is canceled by the user agent, e.g., in response towindow.stop()
, since in those cases the finaltask 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.
Network errors that prevents the connection from being established in the first place (e.g. DNS errors), should cause the user agent toreestablish the connection in parallel, unless the user agent knows that to be futile, in which case the user agent mayfail the connection.
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 towindow.stop()
or the user canceling the network connection manually) must cause the user agent tofail the connection.
When a user agent is to announce the connection, the user agent mustqueue a task which, if the readyState
attribute is set to a value other thanCLOSED
, sets thereadyState
attribute toOPEN
andfires an event named open
at theEventSource
object.
When a user agent is to reestablish the connection, the user agent must run the following steps. These steps are runin parallel, not as part of a task. (The tasks that it queues, of course, are run like normal tasks and not themselvesin parallel.)
-
Queue a task to run the following steps:
-
If the
readyState
attribute is set toCLOSED
, abort the task. -
Set the
readyState
attribute toCONNECTING
. -
Fire an event named
error
at theEventSource
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
EventSource
object'sreadyState
attribute is not set toCONNECTING
, abort these steps. -
Let request be the
EventSource
object'srequest. -
If the
EventSource
object'slast event ID string is not the empty string, set `Last-Event-ID
`/last event ID string, encoded as UTF-8, in request's header list. -
Fetchrequest and process the response obtained in this fashion, if any, as described earlier in this section.
-
When a user agent is to fail the connection, the user agent mustqueue a task which, if the readyState
attribute is set to a value other thanCLOSED
, sets thereadyState
attribute toCLOSED
andfires an event named error
at theEventSource
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 theremote event task source.
9.2.4 Parsing an event stream
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.
9.2.5 Interpreting an event stream
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 alast event ID buffer must be associated with it. They must be initialized to the empty string
Lines must be processed, in the order they are received, as follows:
If the line is empty (a blank line)
Dispatch the event, as defined below.
If the line starts with a U+003A COLON character (:)
Ignore the line.
If the line contains a U+003A COLON character (:)
Collect the characters on the line before the first U+003A COLON character (:), and letfield be that string.
Collect the characters on the line after the first U+003A COLON character (:), and letvalue be that string. If value starts with a U+0020 SPACE character, remove it fromvalue.
Process the field using the steps described below, usingfield as the field name and value as the field value.
Otherwise, the string is not empty but does not contain a U+003A COLON character (:)
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.
If the field name is "event"
Set the event type buffer to field value.
If the field name is "data"
Append the field value to the data buffer, then append a single U+000A LINE FEED (LF) character to thedata buffer.
If the field name is "id"
Set the last event ID buffer to the field value.
If the field name is "retry"
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'sreconnection time to that integer. Otherwise, ignore the field.
Otherwise
The field is ignored.
When the user agent is required to dispatch the event, the user agent must process thedata 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 thelast 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 theevent 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 thedata buffer.
-
Let event be the result of creating an event using
MessageEvent
, in therelevant Realm of theEventSource
object. -
Initialize event's
type
attribute tomessage
, itsdata
attribute todata, itsorigin
attribute to theUnicode serialization of the origin of the event stream's final URL (i.e. the URL after redirects), and itslastEventId
attribute to thelast event ID string of the event source. -
If the event type buffer has a value other than the empty string, change thetype 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 thanCLOSED
,dispatches the newly created event at theEventSource
object.
If an event doesn't have an "id" field, but an earlier event did set the event source'slast 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 thedata 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 interfaceMessageEvent
to be dispatched on theEventSource
object. The event'sdata
attribute would contain the string "YHOO\n+2\n10
" (where "\n
" represents a newline).
This could be used as follows:
var stocks = new EventSource("https://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.
9.2.6 Authoring notes
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, in particular if the chunking is done by a different layer unaware of the timing requirements. If this is a problem, chunking can be disabled for serving event streams.
Clients that support HTTP's per-server connection limitation might run into trouble when opening multiple pages from a site if each page has anEventSource
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 singleEventSource
object using ashared worker.
9.2.7 Connectionless push and other features
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:
- Browser connects to a remote HTTP server and requests the resource specified by the author in the
EventSource
constructor. - The server sends occasional messages.
- In between two messages, the browser detects that it is idle except for the network activity involved in keeping the TCP connection alive, and decides to switch to sleep mode to save power.
- The browser disconnects from the server.
- The browser contacts a service on the network, and requests that the service, a "push proxy", maintain the connection instead.
- The "push proxy" service contacts the remote HTTP server and requests the resource specified by the author in the
EventSource
constructor (possibly including a `Last-Event-ID
` HTTP header, etc). - The browser allows the mobile device to go to sleep.
- The server sends another message.
- The "push proxy" service uses a technology such as OMA push to convey the event to the mobile device, which wakes only enough to process the event and then returns to sleep.
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 byother applicable specifications may be supported. This specification does not define how they are to be parsed or processed.
9.2.8 Garbage collection
While an EventSource
object'sreadyState
isCONNECTING
, and the object has one or more event listeners registered foropen
,message
orerror
events, there must be a strong reference from theWindow
orWorkerGlobalScope
object that theEventSource
object's constructor was invoked from to theEventSource
object itself.
While an EventSource
object'sreadyState
isOPEN
, and the object has one or more event listeners registered formessage
orerror
events, there must be a strong reference from theWindow
orWorkerGlobalScope
object that theEventSource
object's constructor was invoked from to theEventSource
object itself.
While there is a task queued by an EventSource
object on theremote event task source, there must be a strong reference from the Window
orWorkerGlobalScope
object that theEventSource
object's constructor was invoked from to thatEventSource
object.
If a user agent is to forcibly close anEventSource
object (this happens when aDocument
object goes away permanently), the user agent must abort any instances of thefetch algorithm started for this EventSource
object, and must set thereadyState
attribute toCLOSED
.
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
.
9.2.9 Implementation advice
This section is non-normative.
User agents are strongly urged to provide detailed diagnostic information aboutEventSource
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 anerror
event is fired, since little to no information can be made available in the events themselves.
9.3 Web sockets
Support: websocketsChrome for Android55+Chrome 16+iOS Safari6.0+UC Browser for Android11+Firefox 11+IE10+Samsung Internet 4+Opera Mini NoneAndroid Browser4.4+Safari 7+Edge12+Opera 12.1+
Source: caniuse.com
9.3.1 Introduction
This section is non-normative.
To enable Web applications to maintain bidirectional communications with server-side processes, this specification introduces theWebSocket
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.
9.3.2 The WebSocket
interface
enum BinaryType { "blob", "arraybuffer" };
[Constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols = []), Exposed=(Window,Worker)]
interface WebSocket : EventTarget {
readonly attribute USVString 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 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);
};
BinaryType { "blob", "arraybuffer" };
[Constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols = []), Exposed=(Window,Worker)]
interface WebSocket : EventTarget {
readonly attribute USVString 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 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);
};
Each WebSocket
object has an associatedurl (a URL record).
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]
The WebSocket(url,protocols)
constructor, when invoked, must run these steps:
-
Let urlRecord be the result of applying the URL parser to url.
-
If urlRecord is failure, then throw a "
SyntaxError
"DOMException
. -
If urlRecord's scheme is not "
ws
" or "wss
", then throw a "SyntaxError
"DOMException
. -
If urlRecord's fragment is non-null, then throw a "
SyntaxError
"DOMException
. -
If protocols is a string, set protocols to a sequence 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
"DOMException
and abort these steps.[WSP] -
Return a new
WebSocket
object whoseurl is urlRecord, but continue these steps in parallel. -
Establish a WebSocket connection givenurlRecord, protocols, and the entry settings object. [FETCH]
If the establish a WebSocket connection algorithm fails, it triggers the fail the WebSocket connection algorithm, which then invokes theclose the WebSocket connection algorithm, which then establishes thatthe WebSocket connection is closed, which fires the
close
eventas described below.
The url
attribute's getter must return thisWebSocket
object'surl, serialized.
The readyState
attribute represents the state of the connection. It can have the following values:
CONNECTING
(numeric value 0)
The connection has not yet been established.
OPEN
(numeric value 1)
The WebSocket connection is established and communication is possible.
CLOSING
(numeric value 2)
The connection is going through the closing handshake, or the close()
method has been invoked.
CLOSED
(numeric value 3)
The connection has been closed or could not be opened.
When the object is created its readyState
must be set toCONNECTING
(0).
The extensions
attribute must initially return the empty string. Afterthe WebSocket connection is established, its value might change, as defined below.
The extensions
attribute returns the extensions selected by the server, if any.
The protocol
attribute must initially return the empty string. Afterthe 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(code, reason)
method, when invoked, must run these steps:
-
If code is present, but is neither an integer equal to 1000 nor an integer in the range 3000 to 4999, inclusive, throw an"
InvalidAccessError
"DOMException
. -
If reason is present, then run these substeps:
-
Let reasonBytes be the result of encoding reason.
-
If reasonBytes is longer than 123 bytes, then throw a "
SyntaxError
"DOMException
.
-
-
Run the first matching steps from the following list:
If the
readyState
attribute is in theCLOSING
(2) orCLOSED
(3) stateDo nothing.
The connection is already closing or is already closed. If it has not already, a
close
event will eventually fireas described below.If the WebSocket connection is not yet established[WSP]
Fail the WebSocket connection and set the
readyState
attribute's value toCLOSING
(2).[WSP]The fail the WebSocket connection algorithm invokes theclose the WebSocket connection algorithm, which then establishes thatthe WebSocket connection is closed, which fires the
close
eventas described below.If the WebSocket closing handshake has not yet been started[WSP]
Start the WebSocket closing handshake and set the
readyState
attribute's value toCLOSING
(2).[WSP]If neither code nor reason is present, the WebSocket Close message must not have a body.
The WebSocket Protocol specification erroneously states that the status code is required for thestart the WebSocket closing handshake algorithm.
If code is present, then the status code to use in the WebSocket Close message must be the integer given byclose. [WSP]
If reason is also present, then reasonBytes must be provided in the Close message after the status code.[WSP]
The start the WebSocket closing handshake algorithm eventually invokes theclose the WebSocket connection algorithm, which then establishes thatthe WebSocket connection is closed, which fires the
close
eventas described below.Otherwise
Set the
readyState
attribute's value toCLOSING
(2).The WebSocket closing handshake is started, and will eventually invoke the close the WebSocket connection algorithm, which will establish thatthe 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 usingsend()
but that, as of the last time theevent 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 backgroundin 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, itsbinaryType
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 inBlob
form; by setting it to "arraybuffer
", it is returned inArrayBuffer
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 thereadyState
attribute isCONNECTING
, it must throw an"InvalidStateError
" DOMException
. Otherwise, the user agent must run the appropriate set of steps from the following list:
If the argument is a string
If the WebSocket connection is established andthe WebSocket closing handshake has not yet started, then the user agent must send a WebSocket Message comprised of thedata 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 mustflag 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 thebufferedAmount
attribute by the number of bytes needed to express the argument as UTF-8. [UNICODE] [ENCODING] [WSP]
If the argument is a Blob
object
If the WebSocket connection is established, andthe WebSocket closing handshake has not yet started, then the user agent must send a WebSocket Message comprised ofdata 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 mustflag the WebSocket as full and then close the WebSocket connection. The data to be sent is the raw data represented by theBlob
object. Any invocation of this method with aBlob
argument that does not throw an exception must increase thebufferedAmount
attribute by the size of theBlob
object's raw data, in bytes.[WSP] [FILEAPI]
If the argument is an ArrayBuffer
object
If the WebSocket connection is established, andthe WebSocket closing handshake has not yet started, then the user agent must send a WebSocket Message comprised ofdata 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 mustflag the WebSocket as full and then close the WebSocket connection. The data to be sent is the data stored in the buffer described by theArrayBuffer
object. Any invocation of this method with anArrayBuffer
argument that does not throw an exception must increase thebufferedAmount
attribute by the length of theArrayBuffer
in bytes.[WSP]
If the argument is an object that matches the ArrayBufferView
type definition
If the WebSocket connection is established, andthe WebSocket closing handshake has not yet started, then the user agent must send a WebSocket Message comprised ofdata 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 mustflag 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 theArrayBuffer
object thatdata references. Any invocation of this method with this kind of argument that does not throw an exception must increase thebufferedAmount
attribute by the length ofdata's buffer in bytes. [WSP]
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 |
9.3.3 Feedback from the protocol
When the WebSocket connection is established, the user agent mustqueue a task to run these steps:
-
Change the
readyState
attribute's value toOPEN
(1). -
Change the
extensions
attribute's value to theextensions in use, if is not the null value.[WSP] -
Change the
protocol
attribute's value to thesubprotocol in use, if is not the null value.[WSP] -
Fire an event named
open
at theWebSocket
object.
Since the algorithm above is queued as a task, there is no race condition between the WebSocket connection being established and the script setting up an event listener for theopen
event.
When a WebSocket message has been received with typetype and data data, the user agent must queue a task to follow these steps: [WSP]
-
If the
readyState
attribute's value is notOPEN
(1), then abort these steps. -
Let dataForEvent be determined by switching on type and
binaryType
:type indicates that the data is Text
a new
DOMString
containingdatatype indicates that the data is Binary and
binaryType
is "blob
"a new
Blob
object, created in therelevant Realm of theWebSocket
object, that representsdata as its raw data [FILEAPI]type indicates that the data is Binary and
binaryType
is "arraybuffer
"a new
ArrayBuffer
object, created in therelevant Realm of theWebSocket
object, whose contents aredata -
Fire an event named
message
at theWebSocket
object, usingMessageEvent
, with theorigin
attribute initialized to theUnicode serialization of theWebSocket
object'surl's origin, and thedata
attribute initialized todataForEvent.
User agents are encouraged to check if they can perform the above steps efficiently before they run the task, picking tasks from othertask 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 abovetask 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 toCLOSING
(2). (If theclose()
method was called, thereadyState
attribute's value will already be set toCLOSING
(2) when this task runs.)[WSP]
When the WebSocket connection is closed, possiblycleanly, the user agent must queue a task to run the following substeps:
-
Change the
readyState
attribute's value toCLOSED
(3). -
If the user agent was required to fail the WebSocket connection, or if thethe WebSocket connection was closed after beingflagged as full, fire an event named
error
at theWebSocket
object.[WSP] -
Fire an event named
close
at theWebSocket
object, usingCloseEvent
, with thewasClean
attribute initialized to true if the connection closedcleanly and false otherwise, thecode
attribute initialized tothe WebSocket connection close code, and thereason
attribute initialized to the result of applyingUTF-8 decode without BOM to the WebSocket connection close reason.[WSP]
User agents must not convey any failure information to scripts in a way that would allow a script to distinguish the following situations:
- A server whose host name could not be resolved.
- A server to which packets could not successfully be routed.
- A server that refused the connection on the specified port.
- A server that failed to correctly perform a TLS handshake (e.g., the server certificate can't be verified).
- A server that did not complete the opening handshake (e.g. because it was not a WebSocket server).
- A WebSocket server that sent a correct opening handshake, but that specified options that caused the client to drop the connection (e.g. the server specified a subprotocol that the client did not offer).
- A WebSocket server that abruptly closed the connection after successfully completing the opening handshake.
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.
9.3.4 Ping and Pong frames
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.
9.3.5 The 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 USVString reason;
};
dictionary CloseEventInit : EventInit {
boolean wasClean = false;
unsigned short code = 0;
USVString reason = "";
};
CloseEventInit eventInitDict), Exposed=(Window,Worker)]
interface CloseEvent : Event {
readonly attribute boolean wasClean;
readonly attribute unsigned short code;
readonly attribute USVString reason;
};
dictionary CloseEventInit : EventInit {
boolean wasClean = false;
unsigned short code = 0;
USVString reason = "";
};
The wasClean
attribute must return the value it was initialized to. It represents whether the connection closed cleanly or not.
The code
attribute must return the value it was initialized to. It represents the WebSocket connection close code provided by the server.
The reason
attribute must return the value it was initialized to. It represents the WebSocket connection close reason provided by the server.
9.3.6 Garbage collection
A WebSocket
object whosereadyState
attribute's value was set toCONNECTING
(0) as of the last time theevent loop reached step 1 must not be garbage collected if there are any event listeners registered foropen
events,message
events,error
events, orclose
events.
A WebSocket
object whosereadyState
attribute's value was set toOPEN
(1) as of the last time theevent loop reached step 1 must not be garbage collected if there are any event listeners registered formessage
events,error
, orclose
events.
A WebSocket
object whosereadyState
attribute's value was set toCLOSING
(2) as of the last time theevent loop reached step 1 must not be garbage collected if there are any event listeners registered forerror
orclose
events.
A WebSocket
object withan 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 muststart 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 aDocument
object goes away), the user agent must follow the first appropriate set of steps from the following list:
If the WebSocket connection is not yet established[WSP]
Fail the WebSocket connection.[WSP]
If the WebSocket closing handshake has not yet been started[WSP]
Start the WebSocket closing handshake, with the status code to use in the WebSocket Close message being 1001. [WSP]
Otherwise
Do nothing.
9.4 Cross-document messaging
Support: x-doc-messagingChrome for Android55+Chrome 4+iOS Safari3.2+UC Browser for Android11+Firefox 3+IE (limited)8+Samsung Internet 4+Opera Mini all+Android Browser2.1+Safari 4+Edge12+Opera 9.5+
Source: caniuse.com
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.
9.4.1 Introduction
This section is non-normative.
For example, if document A contains an iframe
element that contains document B, and script in document A callspostMessage()
on theWindow
object of document B, then a message event will be fired on that object, marked as originating from theWindow
of document A. The script in document A might look like:
var o = document.getElementsByTagName('iframe')[0];
o.contentWindow.postMessage('Hello world', 'https://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 == 'https://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.
9.4.2 Security
9.4.2.1 Authors
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 thepostMessage()
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 multiplied 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.
9.4.2.2 User agents
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 thesame).
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 differentorigins, to protect naïve sites from denial-of-service attacks.
9.4.3 Posting messages
window . 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 asFile
Blob
,FileList
, andArrayBuffer
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
" DOMException
iftransfer array contains duplicate objects or if message could not be cloned.
When posting a message to a Window
of abrowsing 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 targetbrowsing 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 theWindow
of newly created childiframe
, authors are advised to have the childDocument
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.
The postMessage(message, targetOrigin, transfer)
method, when invoked on a Window
object must run the following steps:
-
Let targetWindow be this
Window
object. -
Let targetRealm be targetWindow's Realm.
-
Let incumbentSettings be the incumbent settings object.
-
If targetOrigin is a single U+002F SOLIDUS character (/), then set targetOrigin to incumbentSettings's origin.
-
Otherwise, if targetOrigin is not a single U+002A ASTERISK character (*), then:
-
Let parsedURL be the result of running the URL parser on targetOrigin.
-
If parsedURL is failure, then throw a "
SyntaxError
"DOMException
. -
Set targetOrigin to parsedURL's origin.
-
-
Let cloneRecord be StructuredCloneWithTransfer(message, transfer, targetRealm). Rethrow any exceptions.
-
Let messageClone be cloneRecord.[[Clone]].
-
Let newPorts be a new frozen array consisting of all
MessagePort
objects incloneRecord.[[TransferList]], if any, maintaining their relative order. -
Return, but continue running these steps in in parallel.
-
If the targetOrigin argument is not a single literal U+002A ASTERISK character (*) andtargetWindow's associated
Document
's origin is not same origin with targetOrigin, then abort these steps. -
Queue a task on theposted message task source to fire an event named
message
attargetWindow, usingMessageEvent
, with thedata
attribute initialized tomessageClone, theorigin
attribute initialized to theUnicode serialization of incumbentSettings's origin, thesource
attribute initialized to theWindowProxy
object's correspondingincumbentSettings's global object (aWindow
object), and theports
attribute initialized tonewPorts.
9.5 Channel messaging
Support: channel-messagingChrome for Android55+Chrome 4+iOS Safari5.0+UC Browser for Android11+Firefox 41+IE10+Samsung Internet 4+Opera Mini NoneAndroid Browser4.4+Safari 5+Edge12+Opera 10.6+
Source: caniuse.com
9.5.1 Introduction
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 runningtasks.
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. usingpostMessage()
:
otherWindow.postMessage('hello', 'https://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 aMessagePort
:
port1.postMessage(['hello', 'world']);
9.5.1.1 Examples
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 inWorker
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()
, thestart()
method must also be invoked. When usingonmessage
, the call tostart()
is implied.
The start()
method, whether called explicitly or implicitly (by settingonmessage
), 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.
9.5.1.2 Ports as the basis of an object-capability model on the Web
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 originconsumer 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 secondiframe
a game (from a third origin). The outer social site and the game in the secondiframe
cannot access anything inside the firstiframe
; together they can only:
- Navigate the
iframe
to a newURL, such as the same URL but with a different fragment, causing theWindow
in theiframe
to receive ahashchange
event. - Resize the
iframe
, causing theWindow
in theiframe
to receive aresize
event. - Send a
message
event to theWindow
in theiframe
using thewindow.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 ofMessagePort
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.
9.5.1.3 Ports as the basis of abstracting out service implementations
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 usedXMLHttpRequest
objects in the service'siframe
, an evolution of the service might instead want to use ashared 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 theXMLHttpRequest
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.
9.5.2 Message channels
[Constructor, Exposed=(Window,Worker)]
interface MessageChannel {
readonly attribute MessagePort port1;
readonly attribute MessagePort port2;
};
Constructor, Exposed=(Window,Worker)]
interface MessageChannel {
readonly attribute MessagePort port1;
readonly attribute MessagePort port2;
};
channel = new MessageChannel
()
Returns a new MessageChannel
object with two newMessagePort
objects.
channel . port1
Returns the first MessagePort
object.
channel . 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 theport1 and port2 objects.
-
Instantiate a new
MessageChannel
object, and letchannel be that object. -
Let the
port1
attribute of thechannel object be port1. -
Let the
port2
attribute of thechannel object be port2. -
Return channel.
The port1
and port2
attributes must return the values they were assigned when theMessageChannel
object was created.
9.5.3 Message ports
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<object> transfer = []);
void start();
void close();
// event handlers
attribute EventHandler onmessage;
};
MessagePort : EventTarget {
void postMessage(any message, optional sequence<object> transfer = []);
void start();
void close();
// event handlers
attribute EventHandler onmessage;
};
port . 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
" DOMException
iftransfer array contains duplicate objects or the source or target ports, or ifmessage could not be cloned.
port . start
()
Begins dispatching messages received on the port.
port . close
()
Disconnects the port, so that it is no longer active.
Each MessagePort
object can be entangled with another (a symmetric relationship). EachMessagePort
object also has atask source called the port message queue, initially empty. Aport 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). AMessagePort
also has ahas been shipped flag, which must initially be false, and anowner, 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 virtualtask source: it must act as if it contained the tasks of each port message queue of each MessagePort
whosehas 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 respectivetask 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
'shas 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 aMessagePort
'shas 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 newMessagePort
object with a particular environment settings object as its owner, it must instantiate a newMessagePort
object, and let itsowner 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 thatMessageChannel
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.
MessagePort
objects aretransferable objects.
Each MessagePort
object's[[Transfer]](targetRealm) internal method must run these steps:
-
Set this's has been shipped flag to true.
-
Let new be a new
MessagePort
object in targetRealm whose owner is targetRealm's settings object. -
Set new's has been shipped flag to true.
-
Move all the tasks that are to fire
message
events in theport message queue of this to the port message queue of new, if any, leaving new's port message queue in its initial disabled state, and, if new'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's owner. -
If this is entangled with another port, then run these substeps:
-
Let remote port be the port with which this is entangled.
-
Set remote port's has been shipped flag to true.
-
Entangleremote port and new. this will be disentangled by this process.
-
-
Set this's [[Detached]] internal slot value to true.
-
Return new. It is the clone.
The postMessage(message,transfer)
method, when invoked on a MessagePort
object, must run the following steps:
-
Let targetPort be the port with which this
MessagePort
is entangled, if any; otherwise let it be null. -
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 containstarget port as one of the objects being transferred. (This condition cannot necessarily be detected when the method is called.)
-
If any of the objects in transfer are this
MessagePort
, then throw a"DataCloneError
"DOMException
and abort these steps. -
If targetPort is not null and any of the objects in transfer aretargetPort, then set doomed to true, and optionally report to a developer console that the target port was posted to itself, causing the communication channel to be lost.
-
Let targetRealm be targetPort's owner's Realm, if targetPort is non-null and doomed is false; otherwise lettargetRealm be some arbitrary Realm.
targetRealm is used when cloning and transferring objects below. If there is no target port, or if thetargetPort 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.)
-
Let cloneRecord be StructuredCloneWithTransfer(message, transfer, targetRealm). Rethrow any exceptions.
-
Let messageClone be cloneRecord.[[Clone]].
-
Let newPorts be a new frozen array consisting of all
MessagePort
objects incloneRecord.[[TransferList]], if any, maintaining their relative order. -
If there is no targetPort (i.e. if this
MessagePort
is not entangled), or ifdoomed is true, then abort these steps. -
Let e be the result of creating an event using
MessageEvent
. -
Initialize e's
type
attribute tomessage
, itsdata
attribute tomessageClone, and itsports
attribute tonewPorts. -
Add a task that runs the following steps to the port message queue of targetPort:
-
Let target be the
MessagePort
in whoseport message queue the event e now finds itself. -
Dispatche at target.
-
The start()
method must enable its port'sport message queue, if it is not already enabled.
The close()
method, when called on a portlocal 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'sonmessage
IDL attribute is set, the port'sport message queue must be enabled, as if the start()
method had been called.
9.5.4 Broadcasting to many ports
This section is non-normative.
Broadcasting to many ports is in principle relatively simple: keep an array ofMessagePort
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, implement a simple protocol whereby the other side acknowledges it still exists. If it doesn't do so after a certain amount of time, assume it's gone, close the MessagePort
object, and let it be garbage collected.
9.5.5 Ports and garbage collection
When a MessagePort
objecto is entangled, user agents must either act as if o's entangledMessagePort
object has a strong reference too, 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 theMessagePort
object'sport message queue is enabled and not empty.
Authors are strongly encouraged to explicitly close MessagePort
objects to disentangle them, so that their resources can be recollected. Creating manyMessagePort
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.
9.6 Broadcasting to other browsing contexts
Support: broadcastchannelChrome for Android55+Chrome 54+iOS SafariNoneUC Browser for AndroidNoneFirefox 38+IENoneSamsung Internet NoneOpera Mini NoneAndroid BrowserNoneSafari NoneEdgeNoneOpera 41+
Source: caniuse.com
Pages on a single origin opened by the same user in the same user agent but in different unrelatedbrowsing 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 synchronization of resources between a server and multiple local clients, to share aWebSocket
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 name), Exposed=(Window,Worker)]
interface BroadcastChannel : EventTarget {
readonly attribute DOMString name;
void postMessage(any message);
void close();
attribute EventHandler onmessage;
};
Constructor(DOMString name), Exposed=(Window,Worker)]
interface BroadcastChannel : EventTarget {
readonly attribute DOMString name;
void postMessage(any message);
void close();
attribute EventHandler onmessage;
};
broadcastChannel = new BroadcastChannel
(name)
Returns a new BroadcastChannel
object via which messages for the given channel name can be sent and received.
broadcastChannel . name
Returns the channel name (as passed to the constructor).
broadcastChannel . 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.
broadcastChannel . close
()
Closes the BroadcastChannel
object, opening it up to garbage collection.
A BroadcastChannel
object has achannel name, a BroadcastChannel
settings object, and a closed flag.
The BroadcastChannel()
constructor, when invoked, must create and return aBroadcastChannel
object whosechannel 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 thechannel name.
The postMessage(message)
method, when invoked on aBroadcastChannel
object, must run the following steps:
-
Let source be this
BroadcastChannel
. -
Let sourceSettings be source's
BroadcastChannel
settings object. -
If source's closed flag is true, then throw an "
InvalidStateError
"DOMException
and abort these steps. -
Let sourceChannel be source's channel name.
-
Let targetRealm be a user-agent defined Realm.
-
Let clonedMessage be StructuredClone(message, targetRealm). Rethrow any exceptions.
-
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 aresponsible document that is fully active, or -
a global object that is a
WorkerGlobalScope
object whoseclosing flag is false and whose worker is a suspendable worker.
-
-
Their
BroadcastChannel
settings object's origin is same origin with sourceSettings's origin. -
Their channel name is a case-sensitive match for sourceChannel.
-
Their closed flag is false.
-
-
Remove source from destinations.
-
Sort destinations such that all
BroadcastChannel
objects whoseBroadcastChannel
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
objectdestination in destinations, queue a task that runs the following steps:-
Let targetRealm be destination's relevant settings object's Realm.
-
Fire an event named
message
atdestination, usingMessageEvent
, with thedata
attribute initialized toStructuredClone(clonedMessage, targetRealm) and theorigin
attribute initialized to theUnicode serialization of sourceSettings's origin.
The tasks must use the DOM manipulation task source, and, for those where the event loop specified by the target
BroadcastChannel
object'sBroadcastChannel
settings object is a browsing context event loop, must be associated with the responsible document specified by that targetBroadcastChannel
object'sBroadcastChannel
settings object. -
While a BroadcastChannel
object whoseclosed flag is false has an event listener registered for message
events, there must be a strong reference fromglobal object specified by the BroadcastChannel
object'sBroadcastChannel
settings object to the BroadcastChannel
object itself.
The close()
method must set theclosed 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 manyBroadcastChannel
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
// ...