Abstract

This specification defines two mechanisms for communicating between browsing contexts in HTML documents.

Status of This document

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the most recently formally published revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.

If you wish to make comments regarding this document, you can enter feedback using this form:

Feedback Comments

Please don't use section numbers as these tend to change rapidly and make your feedback harder to understand.

(Note: Your IP address and user agent will be publicly recorded for spam prevention purposes.)

You can also e-mail feedback to public-webapps@w3.org (subscribe, archives), or whatwg@whatwg.org (subscribe, archives). All feedback is welcome.

Implementors should be aware that this specification is not stable. Implementors who are not taking part in the discussions are likely to find the specification changing out from under them in incompatible ways. Vendors interested in implementing this specification before it eventually reaches the Candidate Recommendation stage should join the aforementioned mailing lists and take part in the discussions.

The latest stable version of the editor's draft of this specification is always available on the W3C CVS server and in the WHATWG Subversion repository. The latest editor's working copy (which may contain unfinished text in the process of being prepared) contains the latest draft text of this specification (amongst others). For more details, please see the WHATWG FAQ.

Notifications of changes to this specification are sent along with notifications of changes to related specifications using the following mechanisms:

E-mail notifications of changes
Commit-Watchers mailing list (complete source diffs): http://lists.whatwg.org/listinfo.cgi/commit-watchers-whatwg.org
Browsable version-control record of all changes:
CVSWeb interface with side-by-side diffs: http://dev.w3.org/cvsweb/html5/
Annotated summary with unified diffs: http://html5.org/tools/web-apps-tracker
Raw Subversion interface: svn checkout http://svn.whatwg.org/webapps/

The W3C Web Applications Working Group is the W3C working group responsible for this specification's progress along the W3C Recommendation track. This specification is the 14 May 2014 Editor's Draft.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

Table of Contents

  1. 1 Conformance requirements
    1. 1.1 Dependencies
  2. 2 Terminology
  3. 3 The MessageEvent interfaces
  4. 4 Cross-document messaging
    1. 4.1 Introduction
    2. 4.2 Security
      1. 4.2.1 Authors
      2. 4.2.2 User agents
    3. 4.3 Posting messages
  5. 5 Channel messaging
    1. 5.1 Introduction
      1. 5.1.1 Examples
      2. 5.1.2 Ports as the basis of an object-capability model on the Web
      3. 5.1.3 Ports as the basis of abstracting out service implementations
    2. 5.2 Message channels
    3. 5.3 Message ports
    4. 5.4 Broadcasting to many ports
    5. 5.5 Ports and garbage collection
  6. References
  7. Acknowledgements

1 Conformance requirements

All diagrams, examples, and notes in this specification are non-normative, as are all sections explicitly marked non-normative. Everything else in this specification is normative.

The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in the normative parts of this document are to be interpreted as described in RFC2119. For readability, these words do not appear in all uppercase letters in this specification. [RFC2119]

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Some conformance requirements are phrased as requirements on attributes, methods or objects. Such requirements are to be interpreted as requirements on user agents.

Conformance requirements phrased as algorithms or specific steps may be implemented in any manner, so long as the end result is equivalent. (In particular, the algorithms defined in this specification are intended to be easy to follow, and not intended to be performant.)

The only conformance class defined by this specification is user agents.

User agents may impose implementation-specific limits on otherwise unconstrained inputs, e.g. to prevent denial of service attacks, to guard against running out of memory, or to work around platform-specific limitations.

When support for a feature is disabled (e.g. as an emergency measure to mitigate a security problem, or to aid in development, or for performance reasons), user agents must act as if they had no support for the feature whatsoever, and as if the feature was not mentioned in this specification. For example, if a particular feature is accessed via an attribute in a Web IDL interface, the attribute itself would be omitted from the objects that implement that interface — leaving the attribute on the object but making it return null or throw an exception is insufficient.

1.1 Dependencies

This specification relies on several other underlying specifications.

HTML

Many fundamental concepts from HTML are used by this specification. [HTML]

WebIDL

The IDL blocks in this specification use the semantics of the WebIDL specification. [WEBIDL]

2 Terminology

The construction "a Foo object", where Foo is actually an interface, is sometimes used instead of the more accurate "an object implementing the interface Foo".

The term DOM is used to refer to the API set made available to scripts in Web applications, and does not necessarily imply the existence of an actual Document object or of any other Node objects as defined in the DOM specifications. [DOM]

An IDL attribute is said to be getting when its value is being retrieved (e.g. by author script), and is said to be setting when a new value is assigned to it.

3 The MessageEvent interfaces

Messages in server-sent events, Web sockets, cross-document messaging, channel messaging, and broadcast channels use the MessageEvent interface for their message events:

[Constructor(DOMString type, optional MessageEventInit eventInitDict), Exposed=Window,Worker]
interface MessageEvent : Event {
  readonly attribute any data;
  readonly attribute DOMString origin;
  readonly attribute DOMString lastEventId;
  readonly attribute (WindowProxy or MessagePort)? source;
  readonly attribute MessagePort[]? ports;
};

dictionary MessageEventInit : EventInit {
  any data;
  DOMString origin;
  DOMString lastEventId;
  DOMString channel;
  (WindowProxy or MessagePort)? source;
  sequence<MessagePort> ports;
};
event . data

Returns the data of the message.

event . origin

Returns the origin of the message, for server-sent events and cross-document messaging.

event . lastEventId

Returns the last event ID string, for server-sent events.

event . source

Returns the WindowProxy of the source window, for cross-document messaging, and the MessagePort being attached, in the connect event fired at SharedWorkerGlobalScope objects.

event . ports

Returns the MessagePort array sent with the message, for cross-document messaging and channel messaging.

The data attribute must return the value it was initialised to. When the object is created, this attribute must be initialised to null. It represents the message being sent.

The origin attribute must return the value it was initialised to. When the object is created, this attribute must be initialised to the empty string. It represents, in server-sent events and cross-document messaging, the origin of the document that sent the message (typically the scheme, hostname, and port of the document, but not its path or fragment identifier).

The lastEventId attribute must return the value it was initialised to. When the object is created, this attribute must be initialised to the empty string. It represents, in server-sent events, the last event ID string of the event source.

The source attribute must return the value it was initialised to. When the object is created, this attribute must be initialised to null. It represents, in cross-document messaging, the WindowProxy of the browsing context of the Window object from which the message came; and in the connect events used by shared workers, the newly connecting MessagePort.

The ports attribute must return the value it was initialised to. When the object is created, this attribute must be initialised to null. It represents, in cross-document messaging and channel messaging, the MessagePort array being sent, if any.

4 Cross-document messaging

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.

4.1 Introduction

This section is non-normative.

For example, if document A contains an iframe element that contains document B, and script in document A 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.

4.2 Security

4.2.1 Authors

Use of this API requires extra care to protect users from hostile entities abusing a site for their own purposes.

Authors should check the origin attribute to ensure that messages are only accepted from domains that they expect to receive messages from. Otherwise, bugs in the author's message handling code could be exploited by hostile sites.

Furthermore, even after checking the origin attribute, authors should also check that the data in question is of the expected format. Otherwise, if the source of the event has been attacked using a cross-site scripting flaw, further unchecked processing of information sent using 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.

4.2.2 User agents

The integrity of this API is based on the inability for scripts of one origin to post arbitrary events (using dispatchEvent() or otherwise) to objects in other origins (those that are not 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.

4.3 Posting messages

window . postMessage(message, targetOrigin [, transfer ] )

Posts a message to the given window. Messages can be structured objects, e.g. nested objects and arrays, can contain JavaScript values (strings, numbers, Dates, etc), and can contain certain data objects such as File Blob, FileList, and ArrayBuffer objects.

Objects listed in transfer are transferred, not just cloned, meaning that they are no longer usable on the sending side.

If the origin of the target window doesn't match the given origin, the message is discarded, to avoid information leakage. To send the message to the target regardless of origin, set the target origin to "*". To restrict the message to same-origin targets only, without needing to explicitly state the origin, set the target origin to "/".

Throws a DataCloneError exception if transfer array contains duplicate objects or if message could not be cloned.

When posting a message to a Window of a browsing context that has just been navigated to a new Document is likely to result in the message not receiving its intended recipient: the scripts in the target browsing context have to have had time to set up listeners for the messages. Thus, for instance, in situations where a message is to be sent to the Window of newly created child iframe, authors are advised to have the child Document post a message to their parent announcing their readiness to receive messages, and for the parent to wait for this message before beginning posting messages.

When a script invokes the postMessage(message, targetOrigin, transfer) method (with two or three arguments) on a Window object, the user agent must follow these steps:

  1. 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.

  2. Let new ports be an empty array.

  3. Let transfer map be an empty association list of Transferable objects to placeholder objects.

  4. If the method was invoked with a third argument transfer, run these substeps:

    1. 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.

    2. 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.

  5. 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.

  6. If the method was invoked with a third argument transfer, run these substeps:

    1. Let new owner be the script settings object of the Window object on which the method was invoked.

    2. 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).

  7. Make new ports into a read only array.

  8. Return from the postMessage() method, but asynchronously continue running these steps.

  9. If the targetOrigin argument is a single literal U+002F SOLIDUS character (/), and the Document of the Window object on which the method was invoked does not have the same origin as the responsible document specified by the entry settings object, then abort these steps silently.

    Otherwise, if the targetOrigin argument is an absolute URL, and the Document of the Window object on which the method was invoked does not have the same origin as targetOrigin, then abort these steps silently.

    Otherwise, the targetOrigin argument is a single literal U+002A ASTERISK character (*), and no origin check is made.

  10. Create a trusted event that uses the MessageEvent interface, with the event type message, which does not bubble, is not cancelable, and has no default action. The data attribute must be initialised to the value of message clone, the origin attribute must be initialised to the Unicode serialization of the origin specified by the incumbent settings object, the source attribute must be initialised to the WindowProxy object corresponding to the global object (a Window object) specified by the incumbent settings object, and the ports attribute must be initialised to the new ports array.

  11. 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.

5 Channel messaging

5.1 Introduction

This section is non-normative.

To enable independent pieces of code (e.g. running in different browsing contexts) to communicate directly, authors can use channel messaging.

Communication channels in this mechanism are implemented as two-ways pipes, with a port at each end. Messages sent in one port are delivered at the other port, and vice-versa. Messages are 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 on a MessagePort:

port1.postMessage(['hello', 'world']);

5.1.1 Examples

This section is non-normative.

In this example, two JavaScript libraries are connected to each other using MessagePorts. 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.

5.1.2 Ports as the basis of an object-capability model on the Web

This section is non-normative.

Ports can be viewed as a way to expose limited capabilities (in the object-capability model sense) to other actors in the system. This can either be a weak capability system, where the ports are merely used as a convenient model within a particular origin, or as a strong capability model, where they are provided by one origin provider as the only mechanism by which another 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:

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.

5.1.3 Ports as the basis of abstracting out service implementations

This section is non-normative.

Continuing the example from the previous section, consider the contacts provider in particular. While an initial implementation might have simply 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 XMLHttpRequests-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.

5.2 Message channels

[Constructor, Exposed=Window,Worker]
interface MessageChannel {
  readonly attribute MessagePort port1;
  readonly attribute MessagePort port2;
};
channel = new MessageChannel()

Returns a new MessageChannel object with two new MessagePort objects.

channel . port1

Returns the first MessagePort object.

channel . port2

Returns the second MessagePort object.

When the MessageChannel() constructor is called, it must run the following algorithm:

  1. Create a new MessagePort object whose owner is the incumbent settings object, and let port1 be that object.

  2. Create a new MessagePort object whose owner is the incumbent settings object, and let port2 be that object.

  3. Entangle the port1 and port2 objects.

  4. Instantiate a new MessageChannel object, and let channel be that object.

  5. Let the port1 attribute of the channel object be port1.

  6. Let the port2 attribute of the channel object be port2.

  7. Return channel.

The port1 and port2 attributes must return the values they were assigned when the MessageChannel object was created.

5.3 Message ports

Each channel has two message ports. Data sent through one port is received by the other port, and vice versa.

[Exposed=Window,Worker]
interface MessagePort : EventTarget {
  void postMessage(any message, optional sequence<Transferable> transfer);
  void start();
  void close();

  // event handlers
           attribute EventHandler onmessage;
};
// MessagePort implements Transferable;
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 exception if transfer array contains duplicate objects or the source or target ports, or if message could not be cloned.

port . start()

Begins dispatching messages received on the port.

port . close()

Disconnects the port, so that it is no longer active.

Each MessagePort object can be entangled with another (a symmetric relationship). Each MessagePort object also has a task source called the port message queue, initially empty. A port message queue can be enabled or disabled, and is initially disabled. Once enabled, a port can never be disabled again (though messages in the queue can get moved to another queue or removed altogether, which has much the same effect). A MessagePort also has a has been shipped flag, which must initially be false, and an owner, which is a settings object set when the object is created, as described below.

When a port's port message queue is enabled, the event loop must use it as one of its task sources. When a port's owner specifies a responsible event loop that is a browsing context event loop, all tasks queued on its port message queue must be associated with the responsible document specified by the port's owner.

If the port's owner specifies a responsible document that is fully active, but the event listeners all have scripts whose settings objects specify responsible documents that are not fully active, then the messages will be lost.

Each event loop has a task source called the unshipped port message queue. This is a virtual task source: it must act as if it contained the tasks of each port message queue of each MessagePort whose has been shipped flag is false, whose port message queue is enabled, and whose owner specifies that event loop as the responsible event loop, in the order in which they were added to their respective task source. When a task would be removed from the unshipped port message queue, it must instead be removed from its port message queue.

When a MessagePort's has been shipped flag is false, its port message queue must be ignored for the purposes of the event loop. (The unshipped port message queue is used instead.)

The has been shipped flag is set to true when a port, its twin, or the object it was cloned from, is or has been transferred. When a MessagePort's has been shipped flag is true, its port message queue acts as a first-class task source, unaffected to any unshipped port message queue.

When the user agent is to create a new MessagePort object with a particular settings object as its owner, it must instantiate a new MessagePort object, and let its owner be owner.

When the user agent is to entangle two MessagePort objects, it must run the following steps:

  1. 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.

  2. Associate the two ports to be entangled, so that they form the two parts of a new channel. (There is no MessageChannel object that represents this channel.)

    Two ports A and B that have gone through this step are now said to be entangled; one is entangled to the other, and vice versa.

    While this specification describes this process as instantaneous, implementations are more likely to implement it via message passing. As with all algorithms, the key is "merely" that the end result be indistinguishable, in a black-box sense, from the specification.

When the user agent is to clone a port original port, with the clone being owned by owner, it must run the following steps, which return a new MessagePort object. These steps must be run atomically.

  1. Set original port's has been shipped flag to true.

  2. Create a new MessagePort object whose owner is owner, and let new port be that object.

  3. Set new port's has been shipped flag to true.

  4. Move all the tasks that are to fire message events in the port message queue of original port to the port message queue of new port, if any, leaving the new port's port message queue in its initial disabled state, and, if the new port's owner specifies a responsible event loop that is a browsing context event loop, associating the moved tasks with the responsible document specified by new port's owner.

  5. If the original port is entangled with another port, then run these substeps:

    1. Let the remote port be the port with which the original port is entangled.

    2. Set remote port's has been shipped flag to true.

    3. Entangle the remote port and new port objects. The original port object will be disentangled by this process.

  6. 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:

  1. Let target port be the port with which source port is entangled, if any.

  2. Let doomed be false. It is set to true if a condition is detected that will make this message cause the port to be unusable; specifically, if the message contains target port as one of the objects being transferred. (This condition cannot necessarily be detected synchronously.)

  3. Let new ports be an empty array.

  4. Let transfer map be an empty association list of Transferable objects to placeholder objects.

  5. If the method was invoked with a second argument transfer, run these substeps:

    1. 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.

    2. If any of the objects in transfer are the source port, then throw a DataCloneError exception and abort these steps.

    3. If any of the objects in transfer are the target port, if any, then let doomed be true, and optionally report to a developer console that the target port was posted to itself, causing the communication channel to be lost.

    4. 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.

  6. 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.

  7. If the method was invoked with a second argument transfer, run these substeps:

    1. Let new owner be the owner of target port, if there is a target port and doomed is false, or else some arbitrary owner. (This new owner is used when transferring objects below. If there is no target port, or if the target port is one of the objects being transferred, the Transferable objects given in the second argument, if any, are still transferred, but since they are then discarded, it doesn't matter where they are transferred to.)

    2. 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).

  8. Make new ports into a read only array.

  9. If there is no target port (i.e. if source port is not entangled), or if doomed is true, then abort these steps.

  10. Create an event that uses the MessageEvent interface, with the name message, which does not bubble, is not cancelable, and has no default action.

  11. Let the data attribute of the event be initialised to the value of message clone.

  12. Let the ports attribute of the event be initialised to the new ports array.

  13. 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.


The close() method, when called on a port local port that is entangled with another port, must cause the user agent to disentangle the two ports. If the method is called on a port that is not entangled, then the method must do nothing.


The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by all objects implementing the MessagePort interface:

Event handler Event handler event type
onmessage message

The first time a MessagePort object's onmessage IDL attribute is set, the port's port message queue must be enabled, as if the start() method had been called.

5.4 Broadcasting to many ports

The API described in this section is controversial, as, in an attempt to solve an architectural memory leak, it instead exposes the details of Garbage Collection. This is a lose-lose scenario. A better solution is really needed here.

Broadcasting to many ports is in principle relatively simple: keep an array of MessagePort objects to send messages to, and iterate through the array to send a message. However, this has one rather unfortunate effect: it prevents the ports from being garbage collected, even if the other side has gone away.

To avoid this problem, the PortCollection object can be used. It acts as an opaque array of MessagePort objects, thus allowing the objects to be garbage collected when they stop being relevant, while still allowing scripts to iterate over the MessagePort objects.

[Constructor, Exposed=Window,Worker]
interface PortCollection {
  void add(MessagePort port);
  void remove(MessagePort port);
  void clear();
  void iterate(PortCollectionCallback callback);
};

callback PortCollectionCallback = void (MessagePort port);
portCollection = new PortCollection()

Returns a new empty PortCollection object.

portCollection . add(port)

Adds port to the collection, if it isn't already present.

portCollection . remove(port)

Removes port from the collection, if it is present.

portCollection . clear()

Removes all ports from the collection.

portCollection . iterate(callback)

Calls callback for each port in the collection.

A PortCollection object has an initially empty list of ports. When a MessagePort object in a list of ports is garbage collected, it must be silently removed from that list of ports. Objects in a list of ports are ordered chronologically by the time at which they were most recently added; the least-recently added MessagePort object is the first in the list, and the most-recently added MessagePort is the last in the list.

The PortCollection() constructor must return a new PortCollection object (with an empty list of ports).

The add() method must add the MessagePort given by the argument to the PortCollection object's list of ports, unless the MessagePort is already in the list of ports, in which case the method does nothing. (Calling this method with a port already in the list does not move the port to the end of the list.)

The remove() method must remove the MessagePort given by the argument from the PortCollection object's list of ports, unless the MessagePort is not in the list of ports, in which case the method does nothing.

The clear() method must remove all MessagePort objects from the PortCollection object's list of ports, returning it to the initial empty state. If the list of ports is already empty, the method does nothing.

The iterate() method must invoke its PortCollectionCallback argument once for each MessagePort object in the object's list of ports, in the order defined above, with each invocation being passed the corresponding MessagePort object as the callback's sole argument.

5.5 Ports and garbage collection

When a MessagePort object o is entangled, user agents must either act as if o's entangled MessagePort object has a strong reference to o, or as if the global object specified by o's owner has a strong reference to o.

Thus, a message port can be received, given an event listener, and then forgotten, and so long as that event listener could receive a message, the channel will be maintained.

Of course, if this was to occur on both sides of the channel, then both ports could be garbage collected, since they would not be reachable from live code, despite having a strong reference to each other.

Furthermore, a MessagePort object must not be garbage collected while there exists an event 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 there exists a message event in that queue.

There are no strong references from a PortCollection object to its MessagePort objects. (That is in fact the whole point of PortCollection objects: they allow for MessagePort objects to be referenced without preventing them from being garbage collected.)

Authors are strongly encouraged to explicitly close MessagePort objects to disentangle them, so that their resources can be recollected. Creating many MessagePort objects and discarding them without closing them can lead to high transient memory usage since garbage collection is not necessarily performed promptly, especially for MessagePorts where garbage collection can involve cross-process coordination.

References

All references are normative unless marked "Non-normative".

[DOM]
DOM, A. van Kesteren, A. Gregor, Ms2ger. WHATWG.
[HTML]
HTML, I. Hickson. WHATWG.
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels, S. Bradner. IETF.
[WEBIDL]
Web IDL, C. McCormack. W3C.

Acknowledgements

For a full list of acknowledgements, please see the WHATWG HTML standard.