No standardized Web Extension API to send or transfer TypedArrays or ArrayBuffers or
ReadableStreams or TransformStreams to and from MV3 Web Extensions from arbitrary Web pages.
Define "web_accessible_resources" in manifest.json; use iframe appended
to arbitrary Web page to establish a WindowClient in ServiceWorker context;
use Transferable Streams with postMessage(readable, "*", [readable]) to
transfer a ReadableStream from the Web page to the FetchEvent in
ServiceWorker which becomes a full-duplex stream due to Chromium's use of Mojo
between WindowClient and Client and fetch event to handle fetch().
Internally Chromium has full-duplex stream capabilities between WindowClient
and Client of ServiceWorker via Mojo. We use this to initialize a TransformStream
with writable side in the arbitrary Web page, and readable side sent to
ServiceWorker as a streaming request with fetch() and duplex: "half" RequestInit
option set.
When the writable side gets a WritableStreamDefaultWriter, data in the
form of Uint8Array written to write() is sent to the streaming request
and read in the ServiceWorker fetch event, where any data can be enqueued
into the TransformStreamDefaultController and sent to the WindowClient (iframe)
where we read the data sent from the ServiceWorker.
The ServiceWorker is persistent, remains active due to the fact we have a
live WindowClient and an indefinite fetch() request being handled by fetch
event handler.
Since we have a WindowClient we have an id for the iframe nested context,
so we can send messages to the arbitrary Web page that initiated a fetch()
request from the ServiceWorker by filtering client ids, without necessarily
waiting on messages from the client.
Something like
for (const [id, controller] of messageClients) {
try {
if (await clients.get(id)) {
controller.enqueue(new TextEncoder().encode(id));
}
} catch (e) {
console.log(e);
}
}If we add a query string to the URL used to request the Web extension
web accessible resources iframe, we add citeria to filter further,
if needed
const { readable, writable, transferableWindow } = await WebExtensionMessageStream(location.host) // optional;Multiple iframes can be appended to the same Web page, and multiple discrete Web pages.
Removing the iframe from the Web page closes the messaging session, and
results in a network type error because we have abruptly aborted the
fetch() request. We can probably handle that a little differently using AbortController,
if we wanted to.
We'll use a modern design for full-duplex asynchronous messaging in the form of a WHATWG TransformStream, similar to the design used by WebSocketStream,
WebTrasnport.
const encoder = new TextEncoder();
const { readable, writable, transferableWindow } = await WebExtensionMessageStream(location.host) // optional;
const writer = writable.getWriter();
readable.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write(message) {
console.log(message);
},
close() {
console.log("Stream close");
},
abort(reason) {
console.log(reason);
},
}),
).then(() => console.log("Done streaming"))
.catch((e) => {
console.log(e);
})
.finally(() => {
transferableWindow.remove();
console.log("WebExtensionMessageStream closed");
});
await writer.ready;
await writer.write(encoder.encode(`Message from ${document.title}`));
Later
await writer.close();
- https://issues.chromium.org/issues/40321352
- https://groups.google.com/a/chromium.org/g/blink-network-dev/c/9MKtF4fPtMA?pli=1
- https://docs.google.com/document/d/1_KuZzg5c3pncLJPFa8SuVm23AP4tft6mzPCl5at3I9M/edit?pli=1&tab=t.0#heading=h.ctj5hkqmripj
- https://issues.chromium.org/issues/40597904
- https://github.com/whatwg/streams/blob/dd4e1806f894eff6ba006fb3c96657b8b104263b/explainers/transferable-streams.md
- https://groups.google.com/a/chromium.org/g/chromium-dev/c/I_hfhGILbDc/m/OVlsxYjUBAAJ
- https://chromium.googlesource.com/chromium/src/+/main/mojo/README.md
- https://github.com/chromium/chromium/blob/5a06296706a275b6539c39acedb0dc09a18b8543/content/browser/service_worker/service_worker_fetch_dispatcher.cc
- https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests
Do What the Fuck You Want to Public License WTFPLv2