Support in all current engines.
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
postMessage()
API
can
be
used
as
a
tracking
vector
.
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',
'https://b.example.org/');
To
register
an
event
handler
for
incoming
events,
the
script
would
use
addEventListener()
(or
similar
mechanisms).
For
example,
the
script
in
document
B
might
look
like:
window.addEventListener('message', receiver, false);
function receiver(e) {
if (e.origin == 'https://example.com') {
if (e.data == 'Hello world') {
e.source.postMessage('Hello', e.origin);
} else {
alert(e.data);
}
}
}
This script first checks the domain is the expected domain, and then looks at the message, which it either displays to the user, or responds to by sending a message back to the document which sent the message in the first place.
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 multiplied into a denial-of-service attack. Authors are encouraged to employ rate limiting (only accepting a certain number of messages per minute) to make such attacks impractical.
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.
window
.
postMessage
(
message
[,
options
])
Support in all current engines.
Posts
a
message
to
the
given
window.
Messages
can
be
structured
objects,
e.g.
nested
objects
and
arrays,
can
contain
JavaScript
values
(strings,
numbers,
Date
objects,
etc),
and
can
contain
certain
data
objects
such
as
File
Blob
,
FileList
,
and
ArrayBuffer
objects.
Objects
listed
in
the
transfer
member
of
options
are
transferred,
not
just
cloned,
meaning
that
they
are
no
longer
usable
on
the
sending
side.
A
target
origin
can
be
specified
using
the
targetOrigin
member
of
options
.
If
not
provided,
it
defaults
to
"
/
".
This
default
restricts
the
message
to
same-origin
targets
only.
If
the
origin
of
the
target
window
doesn't
match
the
given
target
origin,
the
message
is
discarded,
to
avoid
information
leakage.
To
send
the
message
to
the
target
regardless
of
origin,
set
the
target
origin
to
"
*
".
Throws
a
"
DataCloneError
"
DOMException
if
transfer
array
contains
duplicate
objects
or
if
message
could
not
be
cloned.
window
.
postMessage
(
message
,
targetOrigin
[,
transfer
])
This
is
an
alternate
version
of
postMessage()
where
the
target
origin
is
specified
as
a
parameter.
Calling
window.postMessage(message,
target,
transfer)
is
equivalent
to
window.postMessage(message,
{targetOrigin,
transfer})
.
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.
The window post message steps , given a targetWindow , message , and options , are as follows:
Let targetRealm be targetWindow 's Realm .
Let incumbentSettings be the incumbent settings object .
Let
targetOrigin
be
options
["
targetOrigin
"].
If targetOrigin is a single U+002F SOLIDUS character (/), then set targetOrigin to incumbentSettings 's origin .
Otherwise, if targetOrigin is not a single U+002A ASTERISK character (*), then:
Let parsedURL be the result of running the URL parser on targetOrigin .
If
parsedURL
is
failure,
then
throw
a
"
SyntaxError
"
DOMException
.
Set targetOrigin to parsedURL 's origin .
Let
transfer
be
options
["
transfer
"].
Let serializeWithTransferResult be StructuredSerializeWithTransfer ( message , transfer ). Rethrow any exceptions.
Queue a global task on the posted message task source given targetWindow to run the following steps:
If
the
targetOrigin
argument
is
not
a
single
literal
U+002A
ASTERISK
character
(*)
and
targetWindow
's
associated
Document
's
origin
is
not
same
origin
with
targetOrigin
,
then
return.
Let origin be the serialization of incumbentSettings 's origin .
Let
source
be
the
WindowProxy
object
corresponding
to
incumbentSettings
's
global
object
(a
Window
object).
Let deserializeRecord be StructuredDeserializeWithTransfer ( serializeWithTransferResult , targetRealm ).
If
this
throws
an
exception,
catch
it,
fire
an
event
named
messageerror
at
targetWindow
,
using
MessageEvent
,
with
the
origin
attribute
initialized
to
origin
and
the
source
attribute
initialized
to
source
,
and
then
return.
Let messageClone be deserializeRecord .[[Deserialized]].
Let
newPorts
be
a
new
frozen
array
consisting
of
all
MessagePort
objects
in
deserializeRecord
.[[TransferredValues]],
if
any,
maintaining
their
relative
order.
Fire
an
event
named
message
at
targetWindow
,
using
MessageEvent
,
with
the
origin
attribute
initialized
to
origin
,
the
source
attribute
initialized
to
source
,
the
data
attribute
initialized
to
messageClone
,
and
the
ports
attribute
initialized
to
newPorts
.
The
postMessage(
message
,
options
)
method,
when
invoked
on
a
Window
object,
must
run
the
following
steps:
Let
targetWindow
be
this
Window
object.
Run the window post message steps providing targetWindow , message , and options .
The
postMessage(
message
,
targetOrigin
,
transfer
)
method,
when
invoked
on
a
Window
object,
must
run
the
following
steps:
Let
targetWindow
be
this
Window
object.
Let
options
be
«[
"
targetOrigin
"
→
targetOrigin
,
"
transfer
"
→
transfer
]».
Run the window post message steps providing targetWindow , message , and options .
Support in all current engines.
Channel_Messaging_API/Using_channel_messaging
Support in all current engines.
This section is non-normative.
To enable independent pieces of code (e.g. running in different browsing contexts ) to communicate directly, authors can use channel messaging .
Communication channels in this mechanism are implemented as two-ways pipes, with a port at each end. Messages sent in one port are delivered at the other port, and vice-versa. Messages are delivered as DOM events, without interrupting or blocking running tasks .
To
create
a
connection
(two
"entangled"
ports),
the
MessageChannel()
constructor
is
called:
var
channel
=
new
MessageChannel();
One
of
the
ports
is
kept
as
the
local
port,
and
the
other
port
is
sent
to
the
remote
code,
e.g.
using
postMessage()
:
otherWindow.postMessage('hello',
'https://example.com',
[channel.port2]);
To
send
messages,
the
postMessage()
method
on
the
port
is
used:
channel.port1.postMessage('hello');
To
receive
messages,
one
listens
to
message
events:
channel.port1.onmessage = handleMessage;
function handleMessage(event) {
// message is in event.data
// ...
}
Data
sent
on
a
port
can
be
structured
data;
for
example
here
an
array
of
strings
is
passed
on
a
MessagePort
:
port1.postMessage(['hello',
'world']);
This section is non-normative.
In
this
example,
two
JavaScript
libraries
are
connected
to
each
other
using
MessagePort
s.
This
allows
the
libraries
to
later
be
hosted
in
different
frames,
or
in
Worker
objects,
without
any
change
to
the
APIs.
<script src="contacts.js"></script> <!-- exposes a contacts object -->
<script src="compose-mail.js"></script> <!-- exposes a composer object -->
<script>
var channel = new MessageChannel();
composer.addContactsProvider(channel.port1);
contacts.registerConsumer(channel.port2);
</script>
Here's what the "addContactsProvider()" function's implementation could look like:
function addContactsProvider(port) {
port.onmessage = function (event) {
switch (event.data.messageType) {
'search-result': handleSearchResult(event.data.results); break;
'search-done': handleSearchDone(); break;
'search-error': handleSearchError(event.data.message); break;
// ...
}
};
};
Alternatively, it could be implemented as follows:
function addContactsProvider(port) {
port.addEventListener('message', function (event) {
if (event.data.messageType == 'search-result')
handleSearchResult(event.data.results);
});
port.addEventListener('message', function (event) {
if (event.data.messageType == 'search-done')
handleSearchDone();
});
port.addEventListener('message', function (event) {
if (event.data.messageType == 'search-error')
handleSearchError(event.data.message);
});
// ...
port.start();
};
The
key
difference
is
that
when
using
addEventListener()
,
the
start()
method
must
also
be
invoked.
When
using
onmessage
,
the
call
to
start()
is
implied.
The
start()
method,
whether
called
explicitly
or
implicitly
(by
setting
onmessage
),
starts
the
flow
of
messages:
messages
posted
on
message
ports
are
initially
paused,
so
that
they
don't
get
dropped
on
the
floor
before
the
script
has
had
a
chance
to
set
up
its
handlers.
This section is non-normative.
Ports can be viewed as a way to expose limited capabilities (in the object-capability model sense) to other actors in the system. This can either be a weak capability system, where the ports are merely used as a convenient model within a particular origin, or as a strong capability model, where they are provided by one origin provider as the only mechanism by which another origin consumer can effect change in or obtain information from provider .
For
example,
consider
a
situation
in
which
a
social
web
site
embeds
in
one
iframe
the
user's
email
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
,
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
email
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.
Support in all current engines.
[Exposed=(Window,Worker)]
interface MessageChannel {
constructor();
readonly attribute MessagePort port1;
readonly attribute MessagePort port2;
};
channel
=
new
MessageChannel
()
Support in all current engines.
Returns
a
new
MessageChannel
object
with
two
new
MessagePort
objects.
channel
.
port1
Support in all current engines.
Returns
the
first
MessagePort
object.
channel
.
port2
Support in all current engines.
Returns
the
second
MessagePort
object.
A
MessageChannel
object
has
an
associated
port
1
and
an
associated
port
2
,
both
MessagePort
objects.
The
new
MessageChannel()
constructor
steps
are:
Set
this
's
port
1
to
a
new
MessagePort
in
this
's
relevant
Realm
.
Set
this
's
port
2
to
a
new
MessagePort
in
this
's
relevant
Realm
.
The
port1
getter
steps
are
to
return
this
's
port
1
.
The
port2
getter
steps
are
to
return
this
's
port
2
.
Support in all current engines.
Each channel has two message ports. Data sent through one port is received by the other port, and vice versa.
[Exposed=(Window,Worker,AudioWorklet), Transferable]
interface MessagePort : EventTarget {
undefined postMessage(any message, sequence<object> transfer);
undefined options = {});
undefined postMessage(any message, optional StructuredSerializeOptions options = {});
undefined start();
undefined close();
// event handlers
attribute EventHandler onmessage;
attribute EventHandler onmessageerror;
};
dictionary {
sequence< = [];
dictionary StructuredSerializeOptions {
sequence<object> transfer = [];
};
port
.
postMessage
(
message
[,
transfer
])
Support in all current engines.
port
.
postMessage
(
message
[,
{
transfer
}])
Posts a message through the channel. Objects listed in transfer are transferred, not just cloned, meaning that they are no longer usable on the sending side.
Throws
a
"
DataCloneError
"
DOMException
if
transfer
contains
duplicate
objects
or
port
,
or
if
message
could
not
be
cloned.
port
.
start
()
Support in all current engines.
Begins dispatching messages received on the port.
port
.
close
()
Support in all current engines.
Disconnects the port, so that it is no longer active.
Each
MessagePort
object
can
be
entangled
with
another
(a
symmetric
relationship).
Each
MessagePort
object
also
has
a
task
source
called
the
port
message
queue
,
initially
empty.
A
port
message
queue
can
be
enabled
or
disabled,
and
is
initially
disabled.
Once
enabled,
a
port
can
never
be
disabled
again
(though
messages
in
the
queue
can
get
moved
to
another
queue
or
removed
altogether,
which
has
much
the
same
effect).
A
MessagePort
also
has
a
has
been
shipped
flag,
which
must
initially
be
false.
When
a
port's
port
message
queue
is
enabled,
the
event
loop
must
use
it
as
one
of
its
task
sources
.
When
a
port's
relevant
global
object
is
a
Window
,
all
tasks
queued
on
its
port
message
queue
must
be
associated
with
the
port's
relevant
global
object
's
associated
Document
.
If the document is fully active , but the event listeners all have scripts whose settings objects specify responsible documents that are not fully active , then the messages will not be received unless and until the documents become fully active again.
Each
event
loop
has
a
task
source
called
the
unshipped
port
message
queue
.
This
is
a
virtual
task
source
:
it
must
act
as
if
it
contained
the
tasks
of
each
port
message
queue
of
each
MessagePort
whose
has
been
shipped
flag
is
false,
whose
port
message
queue
is
enabled,
and
whose
relevant
agent
's
event
loop
is
that
event
loop
,
in
the
order
in
which
they
were
added
to
their
respective
task
source
.
When
a
task
would
be
removed
from
the
unshipped
port
message
queue
,
it
must
instead
be
removed
from
its
port
message
queue
.
When
a
MessagePort
's
has
been
shipped
flag
is
false,
its
port
message
queue
must
be
ignored
for
the
purposes
of
the
event
loop
.
(The
unshipped
port
message
queue
is
used
instead.)
The
has
been
shipped
flag
is
set
to
true
when
a
port,
its
twin,
or
the
object
it
was
cloned
from,
is
or
has
been
transferred.
When
a
MessagePort
's
has
been
shipped
flag
is
true,
its
port
message
queue
acts
as
a
first-class
task
source
,
unaffected
to
any
unshipped
port
message
queue
.
When
the
user
agent
is
to
entangle
two
MessagePort
objects,
it
must
run
the
following
steps:
If one of the ports is already entangled, then disentangle it and the port that it was entangled with.
If
those
two
previously
entangled
ports
were
the
two
ports
of
a
MessageChannel
object,
then
that
MessageChannel
object
no
longer
represents
an
actual
channel:
the
two
ports
in
that
object
are
no
longer
entangled.
Associate
the
two
ports
to
be
entangled,
so
that
they
form
the
two
parts
of
a
new
channel.
(There
is
no
MessageChannel
object
that
represents
this
channel.)
Two ports A and B that have gone through this step are now said to be entangled; one is entangled to the other, and vice versa.
While this specification describes this process as instantaneous, implementations are more likely to implement it via message passing. As with all algorithms, the key is "merely" that the end result be indistinguishable, in a black-box sense, from the specification.
MessagePort
objects
are
transferable
objects
.
Their
transfer
steps
,
given
value
and
dataHolder
,
are:
Set value 's has been shipped flag to true.
Set dataHolder .[[PortMessageQueue]] to value 's port message queue .
If value is entangled with another port remotePort , then:
Set remotePort 's has been shipped flag to true.
Set dataHolder .[[RemotePort]] to remotePort .
Otherwise, set dataHolder .[[RemotePort]] to null.
Their transfer-receiving steps , given dataHolder and value , are:
Set value 's has been shipped flag to true.
Move
all
the
tasks
that
are
to
fire
message
events
in
dataHolder
.[[PortMessageQueue]]
to
the
port
message
queue
of
value
,
if
any,
leaving
value
's
port
message
queue
in
its
initial
disabled
state,
and,
if
value
's
relevant
global
object
is
a
Window
,
associating
the
moved
tasks
with
value
's
relevant
global
object
's
associated
Document
.
If dataHolder .[[RemotePort]] is not null, then entangle dataHolder .[[RemotePort]] and value . (This will disentangle dataHolder .[[RemotePort]] from the original port that was transferred.)
The message port post message steps , given a targetPort , message and options are as follows:
Let
transfer
be
options
["
transfer
"].
If
transfer
contains
this
MessagePort
,
then
throw
a
"
DataCloneError
"
DOMException
.
Let doomed be false.
If targetPort is not null and transfer contains targetPort , then set doomed to true and optionally report to a developer console that the target port was posted to itself, causing the communication channel to be lost.
Let serializeWithTransferResult be StructuredSerializeWithTransfer ( message , transfer ). Rethrow any exceptions.
If targetPort is null, or if doomed is true, then return.
Add a task that runs the following steps to the port message queue of targetPort :
Let
finalTargetPort
be
the
MessagePort
in
whose
port
message
queue
the
task
now
finds
itself.
This can be different from targetPort , if targetPort itself was transferred and thus all its tasks moved along with it.
Let targetRealm be finalTargetPort 's relevant Realm .
Let deserializeRecord be StructuredDeserializeWithTransfer ( serializeWithTransferResult , targetRealm ).
If
this
throws
an
exception,
catch
it,
fire
an
event
named
messageerror
at
finalTargetPort
,
using
MessageEvent
,
and
then
return.
Let messageClone be deserializeRecord .[[Deserialized]].
Let
newPorts
be
a
new
frozen
array
consisting
of
all
MessagePort
objects
in
deserializeRecord
.[[TransferredValues]],
if
any,
maintaining
their
relative
order.
Fire
an
event
named
message
at
finalTargetPort
,
using
MessageEvent
,
with
the
data
attribute
initialized
to
messageClone
and
the
ports
attribute
initialized
to
newPorts
.
The
postMessage(
message
,
options
)
method,
when
invoked
on
a
MessagePort
object
must
run
the
following
steps:
Let
targetPort
be
the
port
with
which
this
MessagePort
is
entangled,
if
any;
otherwise
let
it
be
null.
Run the message port post message steps providing targetPort , message and options .
The
postMessage(
message
,
transfer
)
method,
when
invoked
on
a
MessagePort
object
must
run
the
following
steps:
Let
targetPort
be
the
port
with
which
this
MessagePort
is
entangled,
if
any;
otherwise
let
it
be
null.
Let
options
be
«[
"
transfer
"
→
transfer
]».
Run the message port post message steps providing targetPort , message and options .
The
start()
method,
when
invoked,
must
enable
this
MessagePort
object's
port
message
queue
,
if
it
is
not
already
enabled.
The
close()
method,
when
invoked,
must
run
these
steps:
Set
this
MessagePort
object's
[[Detached]]
internal
slot
value
to
true.
If
this
MessagePort
object
is
entangled,
disentangle
it.
The
following
are
the
event
handlers
(and
their
corresponding
event
handler
event
types
)
that
must
be
supported,
as
event
handler
IDL
attributes
,
by
all
objects
implementing
the
MessagePort
interface:
Event handler | Event handler event type |
---|---|
onmessage
Support in all current engines.
Firefox
41+
Safari
5+
Chrome
4+
Opera 10.6+ Edge 79+ Edge (Legacy) 12+ Internet Explorer 10+ Firefox Android 41+ Safari iOS 4.2+ Chrome Android 18+ WebView Android 37+ Samsung Internet 1.0+ Opera Android 11+ |
message
|
onmessageerror
Firefox
57+
Safari
No
Chrome
60+
Opera 47+ Edge 79+ Edge (Legacy) 18 Internet Explorer No Firefox Android 57+ Safari iOS No Chrome Android 60+ WebView Android 60+ Samsung Internet 8.0+ Opera Android 44+ |
messageerror
|
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.
This section is non-normative.
Broadcasting
to
many
ports
is
in
principle
relatively
simple:
keep
an
array
of
MessagePort
objects
to
send
messages
to,
and
iterate
through
the
array
to
send
a
message.
However,
this
has
one
rather
unfortunate
effect:
it
prevents
the
ports
from
being
garbage
collected,
even
if
the
other
side
has
gone
away.
To
avoid
this
problem,
implement
a
simple
protocol
whereby
the
other
side
acknowledges
it
still
exists.
If
it
doesn't
do
so
after
a
certain
amount
of
time,
assume
it's
gone,
close
the
MessagePort
object,
and
let
it
be
garbage
collected.
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
relevant
global
object
has
a
strong
reference
to
o
.
Thus, a message port can be received, given an event listener, and then forgotten, and so long as that event listener could receive a message, the channel will be maintained.
Of course, if this was to occur on both sides of the channel, then both ports could be garbage collected, since they would not be reachable from live code, despite having a strong reference to each other.
Furthermore,
a
MessagePort
object
must
not
be
garbage
collected
while
there
exists
an
event
referenced
by
a
task
in
a
task
queue
that
is
to
be
dispatched
on
that
MessagePort
object,
or
while
the
MessagePort
object's
port
message
queue
is
enabled
and
not
empty.
Authors
are
strongly
encouraged
to
explicitly
close
MessagePort
objects
to
disentangle
them,
so
that
their
resources
can
be
recollected.
Creating
many
MessagePort
objects
and
discarding
them
without
closing
them
can
lead
to
high
transient
memory
usage
since
garbage
collection
is
not
necessarily
performed
promptly,
especially
for
MessagePort
s
where
garbage
collection
can
involve
cross-process
coordination.
Pages on a single origin opened by the same user in the same user agent but in different unrelated browsing contexts sometimes need to send notifications to each other, for example "hey, the user logged in over here, check your credentials again".
For
elaborate
cases,
e.g.
to
manage
locking
of
shared
state,
to
manage
synchronization
of
resources
between
a
server
and
multiple
local
clients,
to
share
a
WebSocket
connection
with
a
remote
host,
and
so
forth,
shared
workers
are
the
most
appropriate
solution.
For simple cases, though, where a shared worker would be an unreasonable overhead, authors can use the simple channel-based broadcast mechanism described in this section.
[Exposed=(Window,Worker)]
interface BroadcastChannel : EventTarget {
constructor(DOMString name);
readonly attribute DOMString name;
undefined postMessage(any message);
undefined close();
attribute EventHandler onmessage;
attribute EventHandler onmessageerror;
};
broadcastChannel
=
new
BroadcastChannel
(
name
)
BroadcastChannel/BroadcastChannel
Returns
a
new
BroadcastChannel
object
via
which
messages
for
the
given
channel
name
can
be
sent
and
received.
broadcastChannel
.
name
Returns the channel name (as passed to the constructor).
broadcastChannel
.
postMessage
(
message
)
Sends
the
given
message
to
other
BroadcastChannel
objects
set
up
for
this
channel.
Messages
can
be
structured
objects,
e.g.
nested
objects
and
arrays.
broadcastChannel
.
close
()
Closes
the
BroadcastChannel
object,
opening
it
up
to
garbage
collection.
A
BroadcastChannel
object
has
a
channel
name
and
a
closed
flag
.
The
new
BroadcastChannel(
name
)
constructor
steps
are:
Set this 's channel name to name .
Set this 's closed flag to false.
The
name
getter
steps
are
to
return
this
's
channel
name
.
The
postMessage(
message
)
method
steps
are:
If
this
's
closed
flag
is
true,
then
throw
an
"
InvalidStateError
"
DOMException
.
Let serialized be StructuredSerialize ( message ). Rethrow any exceptions.
Let sourceOrigin be this 's relevant settings object 's origin .
Let
destinations
be
a
list
of
BroadcastChannel
objects
that
match
the
following
criteria:
Their relevant global object is either:
a
Window
object
whose
associated
Document
is
fully
active
,
or
a
WorkerGlobalScope
object
whose
closing
flag
is
false
and
whose
worker
is
not
a
suspendable
worker
.
Their relevant settings object 's origin is same origin with sourceOrigin .
Their channel name is this 's channel name .
Remove source from destinations .
Sort
destinations
such
that
all
BroadcastChannel
objects
whose
relevant
agents
are
the
same
are
sorted
in
creation
order,
oldest
first.
(This
does
not
define
a
complete
ordering.
Within
this
constraint,
user
agents
may
sort
the
list
in
any
implementation-defined
manner.)
For each destination in destinations , queue a global task on the DOM manipulation task source given destination 's relevant global object to perform the following steps:
If destination 's closed flag is true, then abort these steps.
Let targetRealm be destination 's relevant Realm .
Let data be StructuredDeserialize ( serialized , targetRealm ).
If
this
throws
an
exception,
catch
it,
fire
an
event
named
messageerror
at
destination
,
using
MessageEvent
,
with
the
origin
attribute
initialized
to
the
serialization
of
sourceOrigin
,
and
then
abort
these
steps.
Fire
an
event
named
message
at
destination
,
using
MessageEvent
,
with
the
data
attribute
initialized
to
data
and
the
origin
attribute
initialized
to
the
serialization
of
sourceOrigin
.
While
a
BroadcastChannel
object
whose
closed
flag
is
false
has
an
event
listener
registered
for
message
or
messageerror
events,
there
must
be
a
strong
reference
from
the
BroadcastChannel
object's
relevant
global
object
to
the
BroadcastChannel
object
itself.
The
close()
method
steps
are
to
set
this
's
closed
flag
to
true.
Authors
are
strongly
encouraged
to
explicitly
close
BroadcastChannel
objects
when
they
are
no
longer
needed,
so
that
they
can
be
garbage
collected.
Creating
many
BroadcastChannel
objects
and
discarding
them
while
leaving
them
with
an
event
listener
and
without
closing
them
can
lead
to
an
apparent
memory
leak,
since
the
objects
will
continue
to
live
for
as
long
as
they
have
an
event
listener
(or
until
their
page
or
worker
is
closed).
The
following
are
the
event
handlers
(and
their
corresponding
event
handler
event
types
)
that
must
be
supported,
as
event
handler
IDL
attributes
,
by
all
objects
implementing
the
BroadcastChannel
interface:
Event handler | Event handler event type |
---|---|
onmessage
Firefox
38+
Safari
No
Chrome
54+
Opera 41+ Edge 79+ Edge (Legacy) No Internet Explorer No Firefox Android 38+ Safari iOS No Chrome Android 54+ WebView Android 54+ Samsung Internet 6.0+ Opera Android 41+ |
message
|
onmessageerror
BroadcastChannel/onmessageerror
Firefox
57+
Safari
No
Chrome
60+
Opera 47+ Edge 79+ Edge (Legacy) No Internet Explorer No Firefox Android 57+ Safari iOS No Chrome Android 60+ WebView Android 60+ Samsung Internet 8.0+ Opera Android 44+ |
messageerror
|
Suppose a page wants to know when the user logs out, even when the user does so from another tab at the same site:
var authChannel = new BroadcastChannel('auth');
authChannel.onmessage = function (event) {
if (event.data == 'logout')
showLogout();
}
function logoutRequested() {
// called when the user asks us to log them out
doLogout();
showLogout();
authChannel.postMessage('logout');
}
function doLogout() {
// actually log the user out (e.g. clearing cookies)
// ...
}
function showLogout() {
// update the UI to indicate we're logged out
// ...
}