Indeed, for complex objects, pre-serializing is beneficial because a string is much easier to encode/decode.
However, you can use web platform messaging to send messages directly without a relay by creating a secure iframe in the web page temporarily just to send a MessagePort to your sandboxed iframe in the offscreen document, then delete the iframe, and use the port directly from the content script. Use "
Web messaging (two-way MessagePort)" in
https://stackoverflow.com/a/68689866 and modify the background portion like this:
// iframe.js (web_accessible_resources) **********************************************************
let port;
window.onmessage = e => {
if (e.data === new URLSearchParams(location.search).get('secret')) {
window.onmessage = null;
navigator.serviceWorker.ready.then(swr => {
swr.active.postMessage('port', [e.ports[0]]);
});
}
};
// background.js **********************************************************
self.onmessage = async e => {
if (e.data === 'port') {
(await getOffscreenClient()).postMessage('port', [e.ports[0]]);
e.ports[0].onmessage = onContentMessage;
e.ports[0].postMessage(null); // resolve makeExtensionFramePort
}
}
function getOffscreenClient(e) {
const all = await clients.matchAll({includeUncontrolled: true, type: 'window'});
return all.find(a => a.url.includes('offscreen.html'))
|| createOffscreen().then(getOffscreenClient);
}
async function createOffscreen() {
try {
await chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: ['BLOBS'],
justification: '...',
});
} catch (err) {
if (!err.message.startsWith('Only a single offscreen')) throw err;
}
}
// offscreen.js **********************************************************
var iframeElem;
self.onmessage = e => {
if (e.data === 'port') {
iframeElem.contentWindow.postMessage('port', [e.ports[0]]);
}
}
// sandboxed-iframe.js **********************************************************
var contentPort;
self.onmessage = e => {
if (e.data === 'port') {
contentPort = e.ports[0];
contentPort.onmessage = onContentMessage;
contentPort.postMessage(null); // resolve makeExtensionFramePort
}
}
function onContentMessage(e) {
console.log('from content:', e.data);
port.postMessage('ok');
}