Messages in server-sent events, Web
sockets, cross-document messaging, and
channel messaging use the message
event.
The following interface is defined for this event:
[Constructor(DOMString type, optional MessageEventInit eventInitDict)] 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; }; dictionary MessageEventInit : EventInit { any data; DOMString origin; DOMString lastEventId; WindowProxy? source; 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 initialized to. When the
object is created, this attribute must be initialized to null. It
represents the message being sent.
The origin
attribute
must return the value it was initialized to. When the object is
created, this attribute must be initialized 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 initialized to. When the
object is created, this attribute must be initialized 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 initialized to. When the object is
created, this attribute must be initialized 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 initialized to. When the
object is created, this attribute must be initialized to null.
It represents, in cross-document messaging and
channel messaging the MessagePort
array
being sent, if any.
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 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)] 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 entry script's base URL.
If the previous step failed, then throw a
SyntaxError
exception.
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 initialize the new EventSource
object's withCredentials
attribute to true.
Return the new EventSource
object, and continue
these steps in the background (without blocking scripts).
Do a potentially CORS-enabled fetch of the resulting absolute URL, with the mode being CORS mode, and the origin being the entry script's origin, 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.
This constructor must be visible when the script's global
object is either a Window
object or an object
implementing the WorkerUtils
interface.
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 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)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 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 asynchronously reestablish the connection. This applies whether the connection is closed gracefully or unexpectedly. It doesn't apply for the error conditions listed below.
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.
Any other HTTP response code not listed here, and any network error that prevents the HTTP connection from being established in the first place (e.g. DNS errors), 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 asynchronously, not as part of a task. (The tasks that it queues, of course, are run like normal tasks and not asynchronously.)
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.
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, with
the mode and
the origin being the same 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. [RFC3629]
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 as UTF-8, with error handling.
One leading U+FEFF BYTE ORDER MARK character must be ignored if any are present.
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 initialized 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 characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), 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, then the user agent must act as follows:
Set the last event ID string of the event source to 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
initialized to the value of the data buffer,
the origin
attribute
must be initialized to the Unicode serialization of the origin of
the event stream's final URL (i.e. the URL after redirects), and
the lastEventId
attribute must be initialized to the last event ID
string of the event source.
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.
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 WorkerUtils
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 WorkerUtils
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
WorkerUtils
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.
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. [CORS]
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 field 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
interface[Constructor(DOMString url, optional (DOMString or DOMString[]) protocols)] 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 DOMString reason); // messaging attribute EventHandler onmessage; attribute DOMString binaryType; void send(DOMString 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
header 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 of the entry script has a scheme
component that is itself a secure protocol, e.g. HTTPS, then throw
a SecurityError
exception.
If port is a port to which the user agent
is configured to block access, then throw a
SecurityError
exception. (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
header fields as defined by the WebSocket protocol specification,
then throw a SyntaxError
exception and abort these
steps. [WSP]
Let origin be the ASCII serialization of the origin of the entry script, converted to ASCII lowercase.
Return a new WebSocket
object, and continue
these steps in the background (without blocking scripts).
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.
This constructor must be visible when the script's global
object is either a Window
object or an object
implementing the WorkerUtils
interface.
The url
attribute must return the result of resolving the URL that was passed to the
constructor. (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)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 not an
integer equal to 1000 or 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 Unicode reason be the result of converting raw reason to a sequence of Unicode characters.
Let reason be the result of encoding Unicode reason as UTF-8.
If reason is longer than 123 bytes,
then throw a SyntaxError
exception and abort these
steps. [RFC3629]
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. [RFC3629] [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 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 started executing a task, 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 asynchronously 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, if
the new value is either the string "blob
" or
the string "arraybuffer
", then set the IDL
attribute to this new value. Otherwise, throw a
SyntaxError
exception.
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:
Let data be the result of converting the data argument to a
sequence of Unicode characters. 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 data 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
close the WebSocket connection with prejudice. 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] [RFC3629] [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 close the WebSocket
connection with
prejudice. 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 close the WebSocket
connection with
prejudice. 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] [TYPEDARRAY]
ArrayBufferView
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 close the WebSocket
connection with
prejudice. The data to be sent is the data stored in the
section of the buffer described by the ArrayBuffer
object that the ArrayBufferView
object references.
Any invocation
of this method with an ArrayBufferView
argument that
does not throw an exception must increase the bufferedAmount
attribute by the length of the ArrayBufferView
in
bytes. [WSP] [TYPEDARRAY]
The following are the event handlers (and their
corresponding event handler
event types) that must be supported, as 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:
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] [RFC3629] [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 an event that uses the
MessageEvent
interface, with the event type message
, which does not bubble, is
not cancelable, and has no default action.
Initialize event's origin
attribute to the
Unicode
serialization of the origin of the
URL that was passed to the WebSocket
object's constructor.
If type indicates that the data is Text,
then initialize event's data
attribute to data.
If type indicates that the data is Binary,
and binaryType
is
set to "blob
", then initialize 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 initialize event's data
attribute to a new
read-only ArrayBuffer
object whose contents are data. [TYPEDARRAY]
Dispatch event at the
WebSocket
object.
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 the WebSocket connection is closed with prejudice,
fire a simple event named error
at the WebSocket
object. [WSP]
Create an 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 initialized to
true if the connection closed cleanly and false
otherwise, whose code
attribute is initialized to the WebSocket connection close code, and
whose reason
attribute
is initialized to the WebSocket connection close reason
decoded as UTF-8, with error handling, 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. [RFC3629]
It doesn't matter what it is resolved relative to, since we already know it is an absolute URL at this point.
If url does not have a <scheme> component whose value,
when converted to ASCII lowercase, is either "ws
" or "wss
", then fail this
algorithm.
If url has a <fragment> component, then fail this algorithm.
If the <scheme>
component of url is "ws
",
set secure to false; otherwise, the <scheme> component is "wss
", set secure to
true.
Let host be the value of the <host> component of url, converted to ASCII lowercase.
If url has a <port> component, 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 <path> component (which might be empty) of url.
If resource name is the empty string, set it to a single character U+002F SOLIDUS (/).
If url has a <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.
[Constructor(DOMString type, optional CloseEventInit eventInitDict)] 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 initialized to. When the
object is created, this attribute must be initialized to false. It
represents whether the connection closed cleanly or not.
The code
attribute must return the value it was initialized to. When the
object is created, this attribute must be initialized to zero. It
represents the WebSocket connection close code provided by the
server.
The reason
attribute must return the value it was initialized to. When the
object is created, this attribute must be initialized 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 started
executing a task 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 started executing a task 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 started executing a
task 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.
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
s, 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
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 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
asynchronously 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 entry script's document, 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 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
initialized to the value of message clone, the
origin
attribute must
be initialized to the Unicode serialization of the origin of
the script that invoked the method, the source
attribute must be
initialized to the script's global object's
WindowProxy
object, and the ports
attribute must be
initialized 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 asynchronous, and delivered as DOM events.
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:
port1.postMessage(['hello', 'world'], 'http://example.com');
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] 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
owned by the script's global object, and let port1 be that object.
Create a new MessagePort
object
owned by the script's global 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.
This constructor must be visible when the script's global
object is either a Window
object or an object
implementing the WorkerUtils
interface.
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.
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
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).
When the user agent is to create a new
MessagePort
object owned by a script's
global object object 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.)
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.
Create a new MessagePort
object
owned by owner, and let new
port be that object.
Move all the 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.
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.
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 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
either the source port or the target port (if any), 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 second argument transfer, run these substeps:
Let new owner be the owner of target port, if there is a target
port, or else some arbitrary owner. (This new owner is used when transfering objects below.
If there is no target port, the
Transferable
objects given in the second argument,
if any, are still transfered, but since they are then discarded, it
doesn't matter where they are transfered 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), then abort these steps.
Create an event 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 the event be initialized to the value of message clone.
Let the ports
attribute of the event be initialized to the new
ports array.
Add the event to the port message queue of target port.
The start()
method must enable its port's port message queue, if it
is not already enabled.
When a port's port message queue is enabled, the event loop must use it as one of its task sources.
If the Document
of the port's event
listeners' global object
is not fully active, then the messages are lost.
The close()
method, when called on a port local port that is
entangled with another port, must cause the user agents 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 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.
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 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 a message in a task
queue that is to be dispatched on that
MessagePort
object, or while the
MessagePort
object's port message queue is
open and there exists a message
event in that queue.
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
memory usage.
This section is non-normative.
This specification introduces two related mechanisms, similar to HTTP session cookies, for storing structured data on the client side. [COOKIES]
The first is designed for scenarios where the user is carrying out a single transaction, but could be carrying out multiple transactions in different windows at the same time.
Cookies don't really handle this case well. For example, a user could be buying plane tickets in two different windows, using the same site. If the site used cookies to keep track of which ticket the user was buying, then as the user clicked from page to page in both windows, the ticket currently being purchased would "leak" from one window to the other, potentially causing the user to buy two tickets for the same flight without really noticing.
To address this, this specification introduces the sessionStorage
IDL attribute.
Sites can add data to the session storage, and it will be accessible
to any page from the same site opened in that window.
For example, a page could have a checkbox that the user ticks to indicate that he wants insurance:
<label> <input type="checkbox" onchange="sessionStorage.insurance = checked ? 'true' : ''"> I want insurance on this trip. </label>
A later page could then check, from script, whether the user had checked the checkbox or not:
if (sessionStorage.insurance) { ... }
If the user had multiple windows opened on the site, each one would have its own individual copy of the session storage object.
The second storage mechanism is designed for storage that spans multiple windows, and lasts beyond the current session. In particular, Web applications may wish to store megabytes of user data, such as entire user-authored documents or a user's mailbox, on the client side for performance reasons.
Again, cookies do not handle this case well, because they are transmitted with every request.
The localStorage
IDL
attribute is used to access a page's local storage area.
The site at example.com can display a count of how many times the user has loaded its page by putting the following at the bottom of its page:
<p> You have viewed this page <span id="count">an untold number of</span> time(s). </p> <script> if (!localStorage.pageLoadCount) localStorage.pageLoadCount = 0; localStorage.pageLoadCount = parseInt(localStorage.pageLoadCount) + 1; document.getElementById('count').textContent = localStorage.pageLoadCount; </script>
Each site has its own separate storage area.
Storage
interfaceinterface Storage { readonly attribute unsigned long length; DOMString? key(unsigned long index); getter DOMString getItem(DOMString key); setter creator void setItem(DOMString key, DOMString value); deleter void removeItem(DOMString key); void clear(); };
Each Storage
object provides access to a list of
key/value pairs, which are sometimes called items. Keys are
strings. Any string (including the empty string) is a valid
key. Values are similarly strings.
Each Storage
object is associated with a list of
key/value pairs when it is created, as defined in the sections on
the sessionStorage
and localStorage
attributes. Multiple
separate objects implementing the Storage
interface can
all be associated with the same list of key/value pairs
simultaneously.
The length
attribute must return the number of key/value pairs currently
present in the list associated with the object.
The key(n)
method must return the name of the
nth key in the list. The order of keys is
user-agent defined, but must be consistent within an object so long
as the number of keys doesn't change. (Thus, adding or removing a key may change the
order of the keys, but merely changing the value of an existing key
must not.) If n is greater than or equal to the number of key/value pairs
in the object, then this method must return null.
The supported property names on a
Storage
object are the keys of each key/value pair
currently present in the list associated with the object.
The getItem(key)
method must return
the current value associated with
the given key. If the given key does not exist in the list associated with the
object then this method must return null.
The setItem(key, value)
method
must first check if a key/value pair
with the given key already exists in the list
associated with the object.
If it does not, then a new key/value pair must be added to the list, with the given key and with its value set to value.
If the given key does exist in the list, then it must have its value updated to value.
If it couldn't set the new value, the method must throw an
QuotaExceededError
exception. (Setting could fail if,
e.g., the user has disabled storage for the site, or if the quota
has been exceeded.)
The removeItem(key)
method must cause the key/value
pair with the given key to be removed from the
list associated with the object, if it exists. If no item with that
key exists, the method must do nothing.
The setItem()
and removeItem()
methods must be
atomic with respect to failure. In the case of failure, the method
does nothing. That is, changes to the data storage area must either
be successful, or the data storage area must not be changed at
all.
The clear()
method must atomically cause the list associated with the object to
be emptied of all key/value pairs, if there are any. If there are
none, then the method must do nothing.
When the setItem()
, removeItem()
, and clear()
methods are invoked, events
are fired on other Document
objects that can access the
newly stored or removed data, as defined in the sections on the
sessionStorage
and localStorage
attributes.
This specification does not require that the above methods wait until the data has been physically written to disk. Only consistency in what different scripts accessing the same underlying list of key/value pairs see is required.
sessionStorage
attribute[NoInterfaceObject] interface WindowSessionStorage { readonly attribute Storage sessionStorage; }; Window implements WindowSessionStorage;
The sessionStorage
attribute represents the set of storage areas specific to the
current top-level browsing context.
Each top-level browsing context has a unique set of session storage areas, one for each origin.
User agents should not expire data from a browsing context's session storage areas, but may do so when the user requests that such data be deleted, or when the UA detects that it has limited storage space, or for security reasons. User agents should always avoid deleting data while a script that could access that data is running. When a top-level browsing context is destroyed (and therefore permanently inaccessible to the user) the data stored in its session storage areas can be discarded with it, as the API described in this specification provides no way for that data to ever be subsequently retrieved.
The lifetime of a browsing context can be unrelated to the lifetime of the actual user agent process itself, as the user agent may support resuming sessions after a restart.
When a new Document
is created in a browsing
context which has a top-level browsing context,
the user agent must check to see if that top-level browsing
context has a session storage area for that document's
origin. If it does, then that is the
Document
's assigned session storage area. If it does
not, a new storage area for that document's origin must
be created, and then that is the Document
's
assigned session storage area. A Document
's assigned
storage area does not change during the lifetime of a
Document
.
In the case of an iframe
being moved to
another Document
, the nested browsing context is
destroyed and a new one created.
The sessionStorage
attribute must return a Storage
object associated with
the Document
's assigned session storage area, if any,
or null if there isn't one. Each Document
object must
have a separate object for its Window
's sessionStorage
attribute.
When a new top-level browsing context is created by cloning an existing browsing context, the new browsing context must start with the same session storage areas as the original, but the two sets must from that point on be considered separate, not affecting each other in any way.
When a new top-level browsing context is created by
a script in an existing
browsing context, or by the user following a link in an
existing browsing context, or in some other way related to a
specific Document
, then the session storage area of the
origin of that Document
must be copied
into the new browsing context when it is created. From that point
on, however, the two session storage areas must be considered
separate, not affecting each other in any way.
When the setItem()
, removeItem()
, and clear()
methods are called on a
Storage
object x that is associated
with a session storage area, if the methods did something, then in
every Document
object whose Window
object's sessionStorage
attribute's Storage
object is associated with the same
storage area, other than x, a storage
event must be fired, as described below.
localStorage
attribute[NoInterfaceObject] interface WindowLocalStorage { readonly attribute Storage localStorage; }; Window implements WindowLocalStorage;
The localStorage
object provides a Storage
object for an
origin.
User agents must have a set of local storage areas, one for each origin.
User agents should expire data from the local storage areas only for security reasons or when requested to do so by the user. User agents should always avoid deleting data while a script that could access that data is running.
When the localStorage
attribute is accessed, the user agent must run the following steps,
which are known as the Storage
object
initialization steps:
The user agent may throw a SecurityError
exception instead of returning a Storage
object if the
request violates a policy decision (e.g. if the user agent is
configured to not allow the page to persist data).
If the Document
's origin is not a
scheme/host/port tuple, then throw a SecurityError
exception and abort these steps.
Check to see if the user agent has allocated a local storage
area for the origin of the Document
of
the Window
object on which the attribute was accessed.
If it has not, create a new storage area for that
origin.
Return the Storage
object associated with that
origin's local storage area. Each Document
object must
have a separate object for its Window
's localStorage
attribute.
When the setItem()
, removeItem()
, and clear()
methods are called on a
Storage
object x that is associated
with a local storage area, if the methods did something, then in
every Document
object whose Window
object's localStorage
attribute's Storage
object is associated with the same
storage area, other than x, a storage
event must be fired, as described below.
Whenever the properties of a localStorage
attribute's
Storage
object are to be examined, returned, set, or
deleted, whether as part of a direct property access, when checking
for the presence of a property, during property enumeration, when
determining the number of properties present, or as part of the
execution of any of the methods or attributes defined on the
Storage
interface, the user agent must first
obtain the storage mutex.
User agents must throw a SecurityError
exception
whenever any of the members of a Storage
object
originally returned by the localStorage
attribute are accessed
by scripts whose effective script origin is not the
same as the origin of
the Document
of the Window
object on which
the localStorage
attribute was
accessed.
This means Storage
objects are neutered
when the document.domain
attribute is used.
storage
eventThe storage
event
is fired when a storage area changes, as described in the previous
two sections (for session
storage, for local
storage).
When this happens, the user agent must queue a task
to fire an event with the name storage
, which does not
bubble and is not cancelable, and which uses the
StorageEvent
interface, at each Window
object whose Document
object has a Storage
object that is affected.
This includes Document
objects that are
not fully active, but events fired on those are ignored
by the event loop until the Document
becomes fully active again.
The task source for this task is the DOM manipulation task source.
If the event is being fired due to an invocation of the setItem()
or removeItem()
methods, the
event must have its key
attribute initialized to the name of the key in question, its oldValue
attribute initialized to
the old value of the key in question, or null if the key is newly
added, and its newValue
attribute initialized to
the new value of the key in question, or null if the key was
removed.
Otherwise, if the event is being fired due to an invocation of
the clear()
method, the event
must have its key
, oldValue
, and newValue
attributes
initialized to null.
In addition, the event must have its url
attribute initialized to
the address of the
document whose Storage
object was affected; and
its storageArea
attribute initialized to the Storage
object from the
Window
object of the target Document
that
represents the same kind of Storage
area as was
affected (i.e. session or local).
[Constructor(DOMString type, optional StorageEventInit eventInitDict)] interface StorageEvent : Event { readonly attribute DOMString? key; readonly attribute DOMString? oldValue; readonly attribute DOMString? newValue; readonly attribute DOMString url; readonly attribute Storage? storageArea; }; dictionary StorageEventInit : EventInit { DOMString? key; DOMString? oldValue; DOMString? newValue; DOMString url; Storage? storageArea; };
The key
attribute must return the value it was initialized to. When the
object is created, this attribute must be initialized to null. It
represents the key being changed.
The oldValue
attribute must return the value it was initialized to. When the
object is created, this attribute must be initialized to null. It
represents the old value of the key being changed.
The newValue
attribute must return the value it was initialized to. When the
object is created, this attribute must be initialized to null. It
represents the new value of the key being changed.
The url
attribute must return the value it was initialized to. When the
object is created, this attribute must be initialized to the empty
string. It represents the address of the document whose key
changed.
The storageArea
attribute must return the value it was initialized to. When the
object is created, this attribute must be initialized to null. It
represents the Storage
object that was affected.
Because of the use of the storage mutex, multiple browsing contexts will be able to access the local storage areas simultaneously in such a manner that scripts cannot detect any concurrent script execution.
Thus, the length
attribute of a Storage
object, and the value of the
various properties of that object, cannot change while a script is
executing, other than in a way that is predictable by the script
itself.
User agents should limit the total amount of space allowed for storage areas.
User agents should guard against sites storing data under their origin's other affiliated sites, e.g. storing up to the limit in a1.example.com, a2.example.com, a3.example.com, etc, circumventing the main example.com storage limit.
User agents may prompt the user when quotas are reached, allowing the user to grant a site more space. This enables sites to store many user-created documents on the user's computer, for instance.
User agents should allow users to see how much space each domain is using.
A mostly arbitrary limit of five megabytes per origin is recommended. Implementation feedback is welcome and will be used to update this suggestion in the future.
A third-party advertiser (or any entity capable of getting content distributed to multiple sites) could use a unique identifier stored in its local storage area to track a user across multiple sessions, building a profile of the user's interests to allow for highly targeted advertising. In conjunction with a site that is aware of the user's real identity (for example an e-commerce site that requires authenticated credentials), this could allow oppressive groups to target individuals with greater accuracy than in a world with purely anonymous Web usage.
There are a number of techniques that can be used to mitigate the risk of user tracking:
User agents may restrict access to the localStorage
objects to scripts
originating at the domain of the top-level document of the
browsing context, for instance denying access to the
API for pages from other domains running in
iframe
s.
User agents may, if so configured by the user, automatically delete stored data after a period of time.
For example, a user agent could be configured to treat third-party local storage areas as session-only storage, deleting the data once the user had closed all the browsing contexts that could access it.
This can restrict the ability of a site to track a user, as the site would then only be able to track the user across multiple sessions when he authenticates with the site itself (e.g. by making a purchase or logging in to a service).
However, this also reduces the usefulness of the API as a long-term storage mechanism. It can also put the user's data at risk, if the user does not fully understand the implications of data expiration.
If users attempt to protect their privacy by clearing cookies without also clearing data stored in the local storage area, sites can defeat those attempts by using the two features as redundant backup for each other. User agents should present the interfaces for clearing these in a way that helps users to understand this possibility and enables them to delete data in all persistent storage features simultaneously. [COOKIES]
User agents may allow sites to access session storage areas in an unrestricted manner, but require the user to authorize access to local storage areas.
User agents may record the origins of sites that contained content from third-party origins that caused data to be stored.
If this information is then used to present the view of data currently in persistent storage, it would allow the user to make informed decisions about which parts of the persistent storage to prune. Combined with a blacklist ("delete this data and prevent this domain from ever storing data again"), the user can restrict the use of persistent storage to sites that he trusts.
User agents may allow users to share their persistent storage domain blacklists.
This would allow communities to act together to protect their privacy.
While these suggestions prevent trivial use of this API for user tracking, they do not block it altogether. Within a single domain, a site can continue to track the user during a session, and can then pass all this information to the third party along with any identifying information (names, credit card numbers, addresses) obtained by the site. If a third party cooperates with multiple sites to obtain such information, a profile can still be created.
However, user tracking is to some extent possible even with no cooperation from the user agent whatsoever, for instance by using session identifiers in URLs, a technique already commonly used for innocuous purposes but easily repurposed for user tracking (even retroactively). This information can then be shared with other sites, using using visitors' IP addresses and other user-specific data (e.g. user-agent headers and configuration settings) to combine separate sessions into coherent user profiles.
User agents should treat persistently stored data as potentially sensitive; it's quite possible for e-mails, calendar appointments, health records, or other confidential documents to be stored in this mechanism.
To this end, user agents should ensure that when deleting data, it is promptly deleted from the underlying storage.
Because of the potential for DNS spoofing attacks, one cannot guarantee that a host claiming to be in a certain domain really is from that domain. To mitigate this, pages can use TLS. Pages using TLS can be sure that only the user, software working on behalf of the user, and other pages using TLS that have certificates identifying them as being from the same domain, can access their storage areas.
Different authors sharing one host name, for example users
hosting content on geocities.com
, all share one local
storage object. There is no feature to restrict the access by
pathname. Authors on shared hosts are therefore recommended to avoid
using these features, as it would be trivial for other authors to
read the data and overwrite it.
Even if a path-restriction feature was made available, the usual DOM scripting security model would make it trivial to bypass this protection and access the data from any path.
The two primary risks when implementing these persistent storage features are letting hostile sites read information from other domains, and letting hostile sites write information that is then read from other domains.
Letting third-party sites read data that is not supposed to be read from their domain causes information leakage, For example, a user's shopping wishlist on one domain could be used by another domain for targeted advertising; or a user's work-in-progress confidential documents stored by a word-processing site could be examined by the site of a competing company.
Letting third-party sites write data to the persistent storage of other domains can result in information spoofing, which is equally dangerous. For example, a hostile site could add items to a user's wishlist; or a hostile site could set a user's session identifier to a known ID that the hostile site can then use to track the user's actions on the victim site.
Thus, strictly following the origin model described in this specification is important for user security.