diff options
Diffstat (limited to 'testing/web-platform/tests/docs/writing-tests/channels.md')
-rw-r--r-- | testing/web-platform/tests/docs/writing-tests/channels.md | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/testing/web-platform/tests/docs/writing-tests/channels.md b/testing/web-platform/tests/docs/writing-tests/channels.md new file mode 100644 index 0000000000..ccccd3c39a --- /dev/null +++ b/testing/web-platform/tests/docs/writing-tests/channels.md @@ -0,0 +1,159 @@ +# Message Channels + +```eval_rst + +.. 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 ## + +```html +<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. + +```eval_rst + +.. 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. + +```eval_rst + +.. 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. + +```eval_rst + +.. js:autoclass:: RemoteObject + :members: +``` + +#### Example #### + +test.html + +```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 + +```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. + +```eval_rst + +.. 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 on `SendChannel`. This causes a + server-initiated disconnect of the corresponding `RecvChannel` + 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 the + `RecvChannel` 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. Calling `closeAllChannelSockets()` right before navigating + will leave you in a state with no open websocket connections (unless + something happens to reopen one before the navigation starts). + +```eval_rst + +.. js:autofunction:: close_all_channel_sockets + :members: +``` |