4.4 KiB
Message Channels
.. contents:: Table of Contents
:depth: 3
:local:
:backlinks: none
Message channels provide a mechanism to communicate across globals, including in cases where there is no client-side mechanism to establish a communication channel (i.e. when the globals are in different browsing context groups).
Markup
<script src="/resources/channels.sub.js"></script>
Channels can be used in any global and are not specifically linked to
testharness.js
.
High Level API
The high level API provides a way to message another global, and to execute functions in that global and return the result.
Globals wanting to receive messages using the high level API have to
be loaded with a uuid
query parameter in their URL, with a value
that's a UUID. This will be used to identify the channel dedicated to
messages sent to that context.
The context must call either global_channel
or
start_global_channel
when it's ready to receive messages. This
returns a RecvChannel
that can be used to add message handlers.
.. js:autofunction:: global_channel
:short-name:
.. js:autofunction:: start_global_channel
:short-name:
.. js:autoclass:: RemoteGlobalCommandRecvChannel
:members:
Contexts wanting to communicate with the remote context do so using a
RemoteGlobal
object.
.. js:autoclass:: RemoteGlobal
:members:
Remote Objects
By default objects (e.g. script arguments) sent to the remote global
are cloned. In order to support referencing objects owned by the
originating global, there is a RemoteObject
type which can pass a
reference to an object across a channel.
.. js:autoclass:: RemoteObject
:members:
Example
test.html
<!doctype html>
<title>call example</title>
<script src="/resources/testharness.js">
<script src="/resources/testharnessreport.js">
<script src="/resources/channel.js">
<script>
promise_test(async t => {
let remote = new RemoteGlobal();
window.open(`child.html?uuid=${remote.uuid}`, "_blank", "noopener");
let result = await remote.call(id => {
return document.getElementById(id).textContent;
}, "test");
assert_equals("result", "PASS");
});
</script>
child.html
<script src="/resources/channel.js">
<p id="nottest">FAIL</p>
<p id="test">PASS</p>
<script>
start_global_channel();
</script>
Low Level API
The high level API is implemented in terms of a channel abstraction. Each channel is identified by a UUID, and corresponds to a message queue hosted by the server. Channels are multiple producer, single consumer, so there's only only entity responsible for processing messages sent to the channel. This is designed to discourage race conditions where multiple consumers try to process the same message.
On the client side, the read side of a channel is represented by a
RecvChannel
object, and the send side by SendChannel
. An initial
channel pair is created with the channel()
function.
.. js:autofunction:: channel
:members:
.. js:autoclass:: Channel
:members:
.. js:autoclass:: SendChannel
:members:
.. js:autoclass:: RecvChannel
:members:
Navigation and bfcache
For specific use cases around bfcache, it's important to be able to ensure that no network connections (including websockets) remain open at the time of navigation, otherwise the page will be excluded from bfcache. This is handled as follows:
-
A
disconnectReader
method onSendChannel
. This causes a server-initiated disconnect of the correspondingRecvChannel
websocket. The idea is to allow a page to send a command that will initiate a navigation, then without knowing when the navigation is done, send further commands that will be processed when theRecvChannel
reconnects. If the commands are sent before the navigation, but not processed, they can be buffered by the remote and then lost during navigation. -
A
close_all_channel_sockets()
function. This just closes all the open websockets associated with channels in the global in which it's called. Any channel then has to be reconnected to be used again. CallingcloseAllChannelSockets()
right before navigating will leave you in a state with no open websocket connections (unless something happens to reopen one before the navigation starts).
.. js:autofunction:: close_all_channel_sockets
:members: